use crate::vm::{Vm, VmBuiltinRegistration};
use std::cell::LazyCell;
thread_local! {
static REGISTRATION: LazyCell<VmBuiltinRegistration> = LazyCell::new(|| {
let mut vm = crate::new_vm();
crate::stdlib::register_stdlib_bindings(&mut vm);
vm.builtin_registration()
});
}
pub(crate) fn install_shared_stdlib_registration(vm: &mut Vm) -> bool {
vm.install_builtin_registration(|| REGISTRATION.with(|cached| (**cached).clone()))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::value::VmValue;
use std::sync::Arc;
#[tokio::test(flavor = "current_thread")]
async fn module_registration_selects_its_owner_and_reaches_the_handler() {
let module = "harn_vm::stdlib::math";
let definitions = crate::stdlib::macros::module_builtin_defs(module);
assert!(definitions.iter().any(|def| def.sig.name == "abs"));
assert!(definitions.iter().all(|def| def.module == module));
let mut vm = crate::new_vm();
crate::stdlib::macros::register_module_builtins(&mut vm, module);
assert!(matches!(
vm.call_named_builtin("abs", vec![VmValue::Int(-7)])
.await
.unwrap(),
VmValue::Int(7)
));
assert!(!vm.builtin_names().iter().any(|name| name == "channel"));
assert!(!vm.builtin_names().iter().any(|name| name == "http_get"));
}
#[test]
#[should_panic(expected = "no linked builtin definitions for module")]
fn missing_registration_owner_cannot_silently_install_nothing() {
crate::stdlib::macros::module_builtin_defs("harn_vm::stdlib::missing_owner");
}
#[tokio::test(flavor = "current_thread")]
async fn standard_library_shares_bindings_but_not_invocation_state() {
let mut first = crate::new_vm();
let mut sibling = crate::new_vm();
crate::stdlib::register_vm_stdlib(&mut first);
crate::stdlib::register_vm_stdlib(&mut sibling);
assert!(
first.builtin_names().len() > 100,
"empty registry is not sharing"
);
assert!(first.shares_builtin_tables_for_test(&sibling));
assert!(!Arc::ptr_eq(
first.synchronization(),
sibling.synchronization()
));
assert!(!Arc::ptr_eq(first.shared_state(), sibling.shared_state()));
assert!(!first
.harness()
.unwrap()
.shares_owner(sibling.harness().unwrap()));
let args = vec![VmValue::Int(-7)];
assert!(matches!(
first.call_named_builtin("abs", args.clone()).await.unwrap(),
VmValue::Int(7)
));
first.register_builtin("abs", |_, _| Ok(VmValue::Int(99)));
assert!(matches!(
first.call_named_builtin("abs", args.clone()).await.unwrap(),
VmValue::Int(99)
));
assert!(matches!(
sibling
.call_named_builtin("abs", args.clone())
.await
.unwrap(),
VmValue::Int(7)
));
let mut later = crate::new_vm();
crate::stdlib::register_vm_stdlib(&mut later);
assert!(matches!(
later.call_named_builtin("abs", args).await.unwrap(),
VmValue::Int(7)
));
first.set_global("pi", VmValue::Int(0));
assert!(matches!(sibling.global("pi"), Some(VmValue::Float(_))));
assert!(matches!(later.global("pi"), Some(VmValue::Float(_))));
}
#[tokio::test(flavor = "current_thread")]
async fn preinstalled_host_bindings_keep_ordered_registration() {
let mut vm = crate::new_vm();
vm.register_builtin("host_custom", |_, _| Ok(VmValue::Int(42)));
vm.set_global("host_fact", VmValue::Int(13));
assert!(!install_shared_stdlib_registration(&mut vm));
crate::stdlib::register_vm_stdlib(&mut vm);
assert!(matches!(
vm.call_named_builtin("host_custom", vec![]).await.unwrap(),
VmValue::Int(42)
));
assert!(matches!(
vm.call_named_builtin("abs", vec![VmValue::Int(-7)])
.await
.unwrap(),
VmValue::Int(7)
));
assert!(matches!(vm.global("host_fact"), Some(VmValue::Int(13))));
}
#[test]
fn namespaces_cannot_capture_execution_objects() {
let mut vm = crate::new_vm();
vm.set_global("constant", VmValue::Float(1.0));
let _ = vm.builtin_registration();
vm.set_global("authority", crate::Harness::null().into_vm_value());
assert!(std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
vm.builtin_registration()
}))
.is_err());
}
}