use std::fmt;
use std::sync::Arc;
use crate::atom::{Atom, AtomTable};
use crate::module::Module;
use crate::term::{
Term,
boxed::{Cons, Tuple},
format::format_term,
};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Monitor {
reference: u64,
watcher: u64,
target: u64,
}
impl Monitor {
#[must_use]
pub const fn new(reference: u64, watcher: u64, target: u64) -> Self {
Self {
reference,
watcher,
target,
}
}
#[must_use]
pub const fn reference(self) -> u64 {
self.reference
}
#[must_use]
pub const fn watcher(self) -> u64 {
self.watcher
}
#[must_use]
pub const fn target(self) -> u64 {
self.target
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct CodePosition {
pub module: Atom,
pub instruction_pointer: usize,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Register {
X(u16),
Y(u16),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum HandlerKind {
Try,
Catch,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ExceptionHandler {
pub kind: HandlerKind,
pub stack_depth: usize,
pub catch_position: CodePosition,
pub destination: Register,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Exception {
pub class: Term,
pub reason: Term,
pub stacktrace: Term,
}
impl Exception {
#[must_use]
pub fn format_with_atoms(&self, atom_table: &AtomTable) -> String {
let mut output = format!(
"{}: {}",
format_term(self.class, atom_table),
format_term(self.reason, atom_table)
);
if !self.stacktrace.is_nil() {
append_stacktrace(&mut output, self.stacktrace, atom_table);
}
output
}
}
fn append_stacktrace(output: &mut String, stacktrace: Term, atom_table: &AtomTable) {
let mut current = stacktrace;
let mut appended_frame = false;
loop {
if current.is_nil() {
return;
}
let Some(cons) = Cons::new(current) else {
if !appended_frame {
output.push_str("\n stacktrace: ");
output.push_str(&format_term(stacktrace, atom_table));
} else {
output.push_str("\n at ");
output.push_str(&format_term(current, atom_table));
}
return;
};
output.push_str("\n at ");
output.push_str(&format_stacktrace_frame(cons.head(), atom_table));
appended_frame = true;
current = cons.tail();
}
}
fn format_stacktrace_frame(frame: Term, atom_table: &AtomTable) -> String {
let Some(tuple) = Tuple::new(frame) else {
return format_term(frame, atom_table);
};
if tuple.arity() != 4 {
return format_term(frame, atom_table);
}
let module = tuple
.get(0)
.map(|term| format_term(term, atom_table))
.unwrap_or_else(|| "#<missing module>".to_owned());
let function = tuple
.get(1)
.map(|term| format_term(term, atom_table))
.unwrap_or_else(|| "#<missing function>".to_owned());
let arity = tuple
.get(2)
.and_then(Term::as_small_int)
.map(|value| value.to_string())
.unwrap_or_else(|| {
tuple
.get(2)
.map(|term| format_term(term, atom_table))
.unwrap_or_else(|| "#<missing arity>".to_owned())
});
let mut formatted = format!("{module}:{function}/{arity}");
if let Some(info) = tuple.get(3)
&& let Some(line) = stacktrace_line(info)
{
formatted.push(':');
formatted.push_str(&line.to_string());
}
formatted
}
fn stacktrace_line(info: Term) -> Option<i64> {
let mut current = info;
loop {
if current.is_nil() {
return None;
}
let cons = Cons::new(current)?;
let tuple = Tuple::new(cons.head())?;
if tuple.arity() == 2 && tuple.get(0).and_then(Term::as_atom) == Some(Atom::LINE) {
return tuple.get(1).and_then(Term::as_small_int);
}
current = cons.tail();
}
}
#[derive(Clone, Debug)]
pub struct RawStackEntry {
pub module: Arc<Module>,
pub ip: usize,
pub mfa: Option<(Atom, Atom, u8)>,
pub location_info: Term,
pub compiled: bool,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ReceiveTimeout {
pub timeout_position: CodePosition,
pub milliseconds: u64,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ExitReason {
Normal,
Kill,
Killed,
Error,
NoConnection,
}
impl ExitReason {
#[must_use]
pub const fn as_atom(self) -> Atom {
match self {
Self::Normal => Atom::NORMAL,
Self::Kill => Atom::KILL,
Self::Killed => Atom::KILLED,
Self::Error => Atom::ERROR,
Self::NoConnection => Atom::NOCONNECTION,
}
}
#[must_use]
pub const fn as_term(self) -> Term {
Term::atom(self.as_atom())
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct JitRuntimeContext {
pub module: *const Module,
pub registry: *const crate::module::ModuleRegistry,
pub jit_cache: *const crate::jit::JitCache,
}
impl JitRuntimeContext {
#[must_use]
pub const fn new(
module: *const Module,
registry: *const crate::module::ModuleRegistry,
jit_cache: *const crate::jit::JitCache,
) -> Self {
Self {
module,
registry,
jit_cache,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum JitStatus {
Yield,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct RemotePid {
pub node: Atom,
pub pid_number: u64,
pub serial: u64,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ProcessStatus {
New,
Running,
Yielded,
Waiting,
Suspended,
Exited(ExitReason),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
pub enum Priority {
Low,
#[default]
Normal,
High,
Max,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ProcessError {
InvalidStatusTransition {
from: ProcessStatus,
to: ProcessStatus,
},
InvalidFloatRegister {
index: u16,
},
}
impl fmt::Display for ProcessError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidStatusTransition { from, to } => {
write!(
f,
"invalid process status transition from {from:?} to {to:?}"
)
}
Self::InvalidFloatRegister { index } => {
write!(f, "invalid float register index {index}")
}
}
}
}
impl std::error::Error for ProcessError {}