feat: inherit native allocators and forward managed field storage

This commit is contained in:
2026-10-21 02:05:52 +08:00
parent a970fd7707
commit be9a032647
13 changed files with 275 additions and 23 deletions
+6
View File
@@ -51,6 +51,12 @@ impl CompareOp {
/// Rust Drop releases host resources when the payload is destroyed.
/// Equal payloads must provide compatible hashes, or reject hashing explicitly.
pub trait ObjectPayload: Any + fmt::Debug + Send + Sync {
/// Ordinary field storage may be indirect, independently of type identity.
/// Targets must be traced; retargeting records Contents on the forwarder.
fn attribute_target(&self, _store: &crate::ObjectStore) -> Result<Option<ObjectRef>, VmError> {
Ok(None)
}
/// Shared-reference facades may expose the current referent's type. The
/// referent must also be held by traced managed edges. Ordinary objects
/// keep their immutable header type; this does not change inheritance.
+28 -2
View File
@@ -110,6 +110,24 @@ impl ObjectStore {
self.heap.get(reference)
}
/// Borrowed storage path: depend on Contents for intermediate objects and
/// Attribute(name) on the final storage object. Every handle is validated.
pub fn attribute_path(&self, reference: ObjectRef) -> Result<Vec<ObjectRef>, VmError> {
let mut path = Vec::new();
let mut seen = std::collections::BTreeSet::new();
let mut current = reference;
loop {
if !seen.insert(current) {
return Err(VmError::InvalidArgument("cyclic attribute storage"));
}
path.push(current);
match self.object(current)?.payload().attribute_target(self)? {
Some(target) => current = target,
None => return Ok(path),
}
}
}
pub fn retain(&mut self, reference: ObjectRef) -> Result<(), VmError> {
self.heap.retain(reference)
}
@@ -187,14 +205,22 @@ impl ObjectStore {
if name == "type" && self.type_of(reference)?.is_some() {
return Err(VmError::UnsupportedOperation("write object type"));
}
self.heap.set_attr(reference, name, value)
let storage = *self
.attribute_path(reference)?
.last()
.expect("nonempty attribute path");
self.heap.set_attr(storage, name, value)
}
pub fn del_attr(&mut self, reference: ObjectRef, name: &str) -> Result<(), VmError> {
if name == "type" && self.type_of(reference)?.is_some() {
return Err(VmError::UnsupportedOperation("delete object type"));
}
self.heap.del_attr(reference, name)
let storage = *self
.attribute_path(reference)?
.last()
.expect("nonempty attribute path");
self.heap.del_attr(storage, name)
}
/// Validate a borrowed object handle without taking an owned reference.
+35 -10
View File
@@ -12,6 +12,9 @@ pub enum TypeConstructor {
Record,
/// Delegate to a managed callable. It must return an instance of this type.
Native(ObjectRef),
/// Opt-in allocator inheritance. The requested nominal type is prepended
/// to the constructor arguments; the allocator must create that real type.
NativeTyped(ObjectRef),
Abstract,
}
@@ -41,6 +44,9 @@ impl EndorTypeObject {
pub fn base(&self) -> Option<ObjectRef> {
self.base
}
pub fn fields(&self) -> &[ClassField] {
&self.fields
}
}
impl ObjectPayload for EndorTypeObject {
@@ -54,7 +60,9 @@ impl ObjectPayload for EndorTypeObject {
if let Some(base) = self.base {
visit(base);
}
if let TypeConstructor::Native(function) = self.constructor {
if let TypeConstructor::Native(function) | TypeConstructor::NativeTyped(function) =
self.constructor
{
visit(function);
}
for method in self.methods.values() {
@@ -84,6 +92,15 @@ impl NativeCallable for Construct {
function,
arguments: arguments.to_vec(),
}),
TypeConstructor::NativeTyped(function) => {
let mut values = Vec::with_capacity(arguments.len() + 1);
values.push(Value::Object(type_object));
values.extend_from_slice(arguments);
Ok(NativeResult::Invoke {
function,
arguments: values,
})
}
TypeConstructor::Abstract => {
Err(VmError::UnsupportedOperation("abstract type construction"))
}
@@ -157,7 +174,10 @@ impl ObjectStore {
base: ObjectRef,
constructor: TypeConstructor,
) -> Result<ObjectRef, VmError> {
self.payload::<EndorTypeObject>(base, "Type")?;
let fields = self
.payload::<EndorTypeObject>(base, "Type")?
.fields
.clone();
let metatype = self
.type_of(base)?
.ok_or(VmError::InvalidArgument("base type has no metatype"))?;
@@ -170,7 +190,7 @@ impl ObjectStore {
base: Some(base),
constructor,
methods: BTreeMap::new(),
fields: Vec::new(),
fields,
})?;
if let Err(error) = self.attach_type(definition, metatype) {
self.release(definition)?;
@@ -258,8 +278,8 @@ impl ObjectStore {
}
/// Atomically build a source class, including inherited constructor fields.
/// Native allocation needs its own protocol; never forge a Record payload
/// claiming to be a native visual instance.
/// Only explicitly inheritable native allocators are reused. Abstract
/// subclasses remain abstract; no Record can forge a native payload.
pub fn new_class(
&mut self,
name: impl Into<String>,
@@ -268,11 +288,12 @@ impl ObjectStore {
methods: BTreeMap<String, ObjectRef>,
) -> Result<ObjectRef, VmError> {
let definition = self.payload::<EndorTypeObject>(base, "Type")?;
if !matches!(definition.constructor, TypeConstructor::Record) {
if matches!(definition.constructor, TypeConstructor::Native(_)) {
return Err(VmError::UnsupportedOperation(
"source subclass of native or abstract allocator",
"native allocator does not opt into source subclass construction",
));
}
let constructor = definition.constructor;
let mut fields = definition.fields.clone();
for field in own_fields {
if field.name.is_empty()
@@ -304,7 +325,7 @@ impl ObjectStore {
let class = self.allocate(EndorTypeObject {
name,
base: Some(base),
constructor: TypeConstructor::Record,
constructor,
methods,
fields,
})?;
@@ -406,7 +427,11 @@ impl ObjectStore {
return Ok(AttributeLookup::Value(Value::Object(function)));
}
}
match self.heap.get_attr(receiver, name) {
let storage = *self
.attribute_path(receiver)?
.last()
.expect("nonempty attribute path");
match self.heap.get_attr(storage, name) {
Ok(value) => return Ok(AttributeLookup::Value(value)),
Err(VmError::AttributeNotFound(_) | VmError::UnsupportedOperation("attributes")) => {}
Err(error) => return Err(error),
@@ -418,7 +443,7 @@ impl ObjectStore {
return Err(VmError::AttributeNotFound(name.into()));
}
self.heap
.get_attr(receiver, name)
.get_attr(storage, name)
.map(AttributeLookup::Value)
}
}