Files
endorphin-aster-vm/crates/aster-core/tests/runtime.rs
T
codex-bot d394f07fc2
Verify databases and deploy Aster VM / verify-and-deploy (push) Successful in 14m16s
Unify heap objects with typed headers and bootstrap Type
2026-10-18 13:38:00 +08:00

173 lines
6.0 KiB
Rust

use aster_core::{ObjectData, Value, Vm, VmError};
#[test]
fn bootstrap_is_unique_and_root_has_no_parent() {
let mut vm = Vm::new().unwrap();
let root = vm.root_class();
assert_eq!(vm.bootstrap(), root);
assert_eq!(vm.bootstrap(), root);
assert_eq!(vm.object_count(), 2);
assert_eq!(vm.class(root).unwrap().name(), "Object");
assert_eq!(vm.class(root).unwrap().parent(), None);
}
#[test]
fn bootstrap_types_have_headers_and_distinct_inheritance_links() {
let vm = Vm::new().unwrap();
let root = vm.root_class();
let type_class = vm.type_class();
assert_ne!(root, type_class);
assert_eq!(vm.class(type_class).unwrap().name(), "Type");
assert_eq!(vm.class(type_class).unwrap().parent(), Some(root));
assert_eq!(vm.type_of(root.into()).unwrap(), type_class);
assert_eq!(vm.type_of(type_class.into()).unwrap(), type_class);
for class in [root, type_class] {
let object = vm.object(class.into()).unwrap();
assert_eq!(object.header().class(), type_class);
assert!(matches!(object.data(), ObjectData::Class(_)));
assert_eq!(vm.as_class(class.into()).unwrap(), class);
assert_eq!(vm.as_instance(class.into()), Err(VmError::ExpectedInstance));
}
}
#[test]
fn factories_preserve_class_links_and_distinct_identity() {
let mut vm = Vm::new().unwrap();
let root = vm.root_class();
let class = vm.create_class("Point", root).unwrap();
let child = vm.create_class("ColoredPoint", class).unwrap();
let first = vm.create_instance(class).unwrap();
let second = vm.create_instance(class).unwrap();
let plain = vm.create_instance(root).unwrap();
assert_ne!(first, second);
assert_eq!(vm.class(child).unwrap().parent(), Some(class));
assert_eq!(vm.type_of(class.into()).unwrap(), vm.type_class());
assert_eq!(vm.type_of(child.into()).unwrap(), vm.type_class());
assert_eq!(vm.type_of(first.into()).unwrap(), class);
assert_eq!(vm.type_of(plain.into()).unwrap(), root);
let object = vm.object(first.into()).unwrap();
assert_eq!(object.header().class(), class);
assert!(matches!(object.data(), ObjectData::Instance(_)));
vm.set_field(first, "x", Value::Integer(1)).unwrap();
vm.set_field(first, "next", Value::Object(second.into()))
.unwrap();
assert_eq!(
vm.instance(first).unwrap().field("x"),
Some(&Value::Integer(1))
);
assert_eq!(vm.instance(second).unwrap().field("x"), None);
assert_eq!(vm.as_class(class.into()).unwrap(), class);
assert_eq!(vm.as_instance(first.into()).unwrap(), first);
assert_eq!(vm.as_instance(class.into()), Err(VmError::ExpectedInstance));
assert_eq!(vm.as_class(first.into()), Err(VmError::ExpectedClass));
}
#[test]
fn foreign_handles_are_rejected_without_mutation() {
let mut vm = Vm::new().unwrap();
let mut other = Vm::new().unwrap();
let local = vm.create_instance(vm.root_class()).unwrap();
let foreign = other.create_instance(other.root_class()).unwrap();
let count = vm.object_count();
assert_eq!(
vm.create_class("Alien", other.root_class()),
Err(VmError::ForeignHandle)
);
assert_eq!(
vm.create_instance(other.root_class()),
Err(VmError::ForeignHandle)
);
assert!(matches!(
vm.object(foreign.into()),
Err(VmError::ForeignHandle)
));
assert!(matches!(vm.instance(foreign), Err(VmError::ForeignHandle)));
for reference in [
foreign.into(),
other.root_class().into(),
other.type_class().into(),
] {
assert_eq!(vm.type_of(reference), Err(VmError::ForeignHandle));
}
assert_eq!(
vm.set_field(foreign, "x", Value::Nil),
Err(VmError::ForeignHandle)
);
vm.set_field(local, "x", Value::Integer(7)).unwrap();
assert_eq!(
vm.set_field(local, "x", Value::Object(foreign.into())),
Err(VmError::ForeignHandle)
);
assert_eq!(
vm.instance(local).unwrap().field("x"),
Some(&Value::Integer(7))
);
assert_eq!(vm.object_count(), count);
}
#[test]
fn handles_outliving_a_vm_cannot_alias_a_new_vm() {
let old = {
let vm = Vm::new().unwrap();
vm.root_class()
};
let vm = Vm::new().unwrap();
assert_ne!(old, vm.root_class());
assert!(matches!(vm.class(old), Err(VmError::ForeignHandle)));
}
#[test]
fn invalid_names_do_not_allocate_and_names_are_not_a_namespace() {
let mut vm = Vm::new().unwrap();
assert_eq!(
vm.create_class(" ", vm.root_class()),
Err(VmError::EmptyClassName)
);
for name in ["Object", "Type"] {
assert_eq!(
vm.create_class(name, vm.root_class()),
Err(VmError::ReservedClassName)
);
}
assert_eq!(vm.object_count(), 2);
let a = vm.create_class("Label", vm.root_class()).unwrap();
let b = vm.create_class("Label", vm.root_class()).unwrap();
assert_ne!(a, b);
}
#[test]
fn type_cannot_be_used_to_bypass_the_class_factory() {
let mut vm = Vm::new().unwrap();
let type_class = vm.type_class();
let count = vm.object_count();
assert_eq!(
vm.create_instance(type_class),
Err(VmError::UnsupportedMetaclass)
);
assert_eq!(
vm.create_class("CustomType", type_class),
Err(VmError::UnsupportedMetaclass)
);
assert_eq!(vm.object_count(), count);
assert_eq!(vm.type_of(type_class.into()).unwrap(), type_class);
}
#[test]
fn handles_remain_valid_when_the_arena_grows() {
let mut vm = Vm::new().unwrap();
let root = vm.root_class();
let first = vm.create_instance(root).unwrap();
vm.set_field(first, "stable", Value::Bool(true)).unwrap();
for _ in 0..4096 {
vm.create_instance(root).unwrap();
}
assert_eq!(vm.type_of(first.into()).unwrap(), root);
assert_eq!(vm.type_of(root.into()).unwrap(), vm.type_class());
assert_eq!(vm.type_of(vm.type_class().into()).unwrap(), vm.type_class());
assert_eq!(
vm.instance(first).unwrap().field("stable"),
Some(&Value::Bool(true))
);
assert_eq!(vm.class(root).unwrap().name(), "Object");
}