use crate::atom::{Atom, AtomTable};
use crate::native::{
BifRegistryImpl, Capability, ExceptionClass, NativeFn, NativeRegistrationError, ProcessContext,
};
use crate::term::Term;
type ExceptionBif = (&'static str, u8, Capability, NativeFn);
const EXCEPTION_BIFS: &[ExceptionBif] = &[("raise", 3, Capability::Pure, bif_raise_3)];
pub fn register_exception_bifs(
registry: &BifRegistryImpl,
atom_table: &AtomTable,
) -> Result<(), NativeRegistrationError> {
let erlang = atom_table.intern("erlang");
for &(function_name, arity, capability, native_function) in EXCEPTION_BIFS {
let function = atom_table.intern(function_name);
registry.register(erlang, function, arity, native_function, capability)?;
}
Ok(())
}
pub fn bif_raise_3(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
let [class, reason, stacktrace] = args else {
return Err(badarg());
};
let exception_class = term_to_exception_class(*class).ok_or_else(badarg)?;
context.set_exception_class(exception_class);
context.set_exception_stacktrace(*stacktrace);
Err(*reason)
}
fn term_to_exception_class(class: Term) -> Option<ExceptionClass> {
if class == Term::atom(Atom::ERROR) {
Some(ExceptionClass::Error)
} else if class == Term::atom(Atom::THROW) {
Some(ExceptionClass::Throw)
} else if class == Term::atom(Atom::EXIT_CLASS) {
Some(ExceptionClass::Exit)
} else {
None
}
}
fn badarg() -> Term {
Term::atom(Atom::BADARG)
}
#[cfg(test)]
mod tests {
use super::{bif_raise_3, register_exception_bifs};
use crate::atom::{Atom, AtomTable};
use crate::native::{BifRegistryImpl, ExceptionClass, ProcessContext};
use crate::term::Term;
#[test]
fn bif_raise_3_sets_exception_metadata_and_returns_reason_error() {
let mut context = ProcessContext::new();
let class = Term::atom(Atom::THROW);
let reason = Term::atom(Atom::BADMATCH);
let stacktrace = Term::small_int(123);
assert_eq!(
bif_raise_3(&[class, reason, stacktrace], &mut context),
Err(reason)
);
assert_eq!(context.take_exception_class(), ExceptionClass::Throw);
assert_eq!(context.take_exception_stacktrace(), stacktrace);
}
#[test]
fn bif_raise_3_rejects_invalid_class() {
let mut context = ProcessContext::new();
assert_eq!(
bif_raise_3(
&[
Term::atom(Atom::OK),
Term::atom(Atom::BADARG),
Term::small_int(456)
],
&mut context
),
Err(Term::atom(Atom::BADARG))
);
assert_eq!(context.take_exception_class(), ExceptionClass::Error);
assert_eq!(context.take_exception_stacktrace(), Term::NIL);
}
#[test]
fn register_exception_bifs_registers_raise_3() {
let registry = BifRegistryImpl::new();
let atom_table = AtomTable::new();
register_exception_bifs(®istry, &atom_table).expect("exception BIF registration");
let erlang = atom_table.intern("erlang");
let raise = atom_table.intern("raise");
assert!(registry.lookup(erlang, raise, 3).is_some());
}
}