diff --git a/crates/compiler/tests/README.md b/crates/compiler/tests/README.md index 19209ef..d87e599 100644 --- a/crates/compiler/tests/README.md +++ b/crates/compiler/tests/README.md @@ -1,6 +1,6 @@ # 编译链路检查 -[classes.rs](classes.rs) 覆盖源码 class、继承字段构造、动态方法覆盖、绑定接收者保活/回收、绑定后参数数量和重复成员/缺少 self 的预执行诊断。只编译,不在本地运行。 +[classes.rs](classes.rs) 覆盖源码 class、NativeTyped 实际原生载荷继承和多层字段构造、继承字段构造、动态方法覆盖、绑定接收者保活/回收、绑定后参数数量和重复成员/缺少 self 的预执行诊断。只编译,不在本地运行。 [pipeline.rs](pipeline.rs) 将真实 Endor 示例交给 reader/parser/名称分析/代码生成/VM,检查函数值、前向引用、局部遮蔽、精确数字常量、语义诊断、宿主全局与属性回调、对象结果保活、跨 VM 模板装载、反复调用、错误帧清理、递归限额、共享交互命名空间及超过 255 的操作数。 diff --git a/crates/compiler/tests/classes.rs b/crates/compiler/tests/classes.rs index a62c288..bc035b6 100644 --- a/crates/compiler/tests/classes.rs +++ b/crates/compiler/tests/classes.rs @@ -1,7 +1,115 @@ use compiler::{CompileOptions, compile_source}; +use endor_core::{ + ContextNativeFunction, EndorTypeObject, NativeCallable, NativeContext, NativeResult, + ObjectPayload, TypeConstructor, VmError, +}; use endor_core::{ObjectRef, Value, Vm}; use frontend::{EndorReader, ParseMode}; +#[derive(Debug)] +struct NativeWidget(String); +impl ObjectPayload for NativeWidget { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn kind_name(&self) -> &'static str { + "widget" + } + fn allows_attributes(&self) -> bool { + true + } +} + +#[derive(Debug)] +struct WidgetFactory; +impl NativeCallable for WidgetFactory { + fn invoke(&self, cx: &mut NativeContext<'_>, args: &[Value]) -> Result { + let [ + Value::Object(requested), + Value::Text(native_label), + values @ .., + ] = args + else { + return Err(VmError::InvalidArgument("widget constructor arguments")); + }; + let fields = cx + .store() + .payload::(*requested, "Type")? + .fields() + .to_vec(); + if values.len() != fields.len() { + return Err(VmError::InvalidArgument("widget field count")); + } + let instance = cx.allocate(NativeWidget(native_label.clone()))?; + cx.attach_type(instance, *requested)?; + for (field, value) in fields.iter().zip(values) { + cx.set_attr(instance, &field.name, value.clone())?; + } + Ok(NativeResult::Return(Value::Object(instance))) + } +} + +#[test] +fn source_subclasses_preserve_native_allocation_fields_and_nominal_identity() { + let mut vm = Vm::new().unwrap(); + let globals = vm.new_dict().unwrap(); + let roots = vm.bootstrap_types().unwrap(); + let factory = vm + .allocate(ContextNativeFunction::new("widget", WidgetFactory, vec![])) + .unwrap(); + let widget = vm + .new_type( + "Widget", + roots.object, + TypeConstructor::NativeTyped(factory), + ) + .unwrap(); + for (name, object) in [ + ("Object", roots.object), + ("Type", roots.type_type), + ("Widget", widget), + ] { + vm.dict_set(globals, Value::Text(name.into()), Value::Object(object)) + .unwrap(); + vm.release(object).unwrap(); + } + vm.release(factory).unwrap(); + let source = "class Child: Widget { value: Number }\nclass Grand: Child { label: String }"; + assert_eq!( + execute(&mut vm, globals, source, ParseMode::Module), + Value::Nil + ); + let value = execute( + &mut vm, + globals, + "Grand(\"native\", 9, \"extra\")", + ParseMode::Expression, + ); + let Value::Object(instance) = value else { + panic!("native instance") + }; + assert_eq!( + vm.payload::(instance, "widget").unwrap().0, + "native" + ); + assert!(vm.is_instance(instance, widget).unwrap()); + assert_eq!( + vm.get_attr(instance, "value").unwrap(), + Value::Number(9_i64.into()) + ); + assert_eq!( + vm.get_attr(instance, "label").unwrap(), + Value::Text("extra".into()) + ); + vm.dict_clear(globals).unwrap(); + vm.release(globals).unwrap(); + vm.collect_cycles(); + assert!(vm.is_instance(instance, widget).unwrap()); + vm.release(instance).unwrap(); + vm.collect_cycles(); + assert_eq!(vm.object_count(), 0); +} + fn execute(vm: &mut Vm, globals: ObjectRef, source: &str, mode: ParseMode) -> Value { let names = vm .dict_items(globals) diff --git a/crates/core/README.md b/crates/core/README.md index 893978f..36b1c44 100644 --- a/crates/core/README.md +++ b/crates/core/README.md @@ -10,6 +10,6 @@ Endor VM 包,仅负责 Vm、执行帧、调用协议和 wordcode 求值循环 Vm::new 创建独立 ObjectStore 和空执行状态;代码对象、全局字典和函数仍按宿主需要显式创建,再 execute/call。EndorObject、Number、ObjectRef、内置载荷、代码对象/指令定义与 VmError 在 objects 中实现;endor_core 保留这些类型的兼容重导出,既有 Vm 对象方法委托到 ObjectStore。 -宿主可显式引导托管 Object/Type 和名义继承,见[类型机制](../../docs/runtime/types.md);普通源码 class 和继承方法分派已经接入,trait、原生类分配继承与完整元类尚未接入。网络、数据库和进程环境属于宿主,源码阅读器与 lexer/parser 属于 [frontend](../frontend/README.md),AST 名称分析与字节码生成属于独立 [compiler](../compiler/README.md),见 [执行与版本模型](../../docs/architecture/execution-and-versions.md)。 +宿主可显式引导托管 Object/Type 和名义继承,见[类型机制](../../docs/runtime/types.md);普通源码 class 和继承方法分派已经接入,显式 NativeTyped 原生分配继承已接入,trait 与完整元类尚未接入。网络、数据库和进程环境属于宿主,源码阅读器与 lexer/parser 属于 [frontend](../frontend/README.md),AST 名称分析与字节码生成属于独立 [compiler](../compiler/README.md),见 [执行与版本模型](../../docs/architecture/execution-and-versions.md)。 返回值所有权见 [生命周期](../../docs/runtime/lifecycle.md),执行契约见 [字节码 VM](../../docs/runtime/bytecode.md)。本地不构建或测试,见 [验证](../../docs/operations/validation.md)。返回 [工作区](../README.md)。 diff --git a/crates/objects/README.md b/crates/objects/README.md index 003eaf1..b61c347 100644 --- a/crates/objects/README.md +++ b/crates/objects/README.md @@ -1,6 +1,6 @@ # objects -Endor 核心对象与对象存储包,零外部依赖,不依赖 core。包含 EndorObject、ObjectPayload、Value、ObjectRef、Number、字符串/字典/Cell/函数/异常载荷,以及代码对象与 wordcode 数据定义、编解码和静态验证。提供显式引导的 Object/Type 名义类型,已接入普通源码 class 和继承方法分派,trait、原生分配继承与完整元类行为仍待实现。 +Endor 核心对象与对象存储包,零外部依赖,不依赖 core。包含 EndorObject、ObjectPayload、Value、ObjectRef、Number、字符串/字典/Cell/函数/异常载荷,以及代码对象与 wordcode 数据定义、编解码和静态验证。提供显式引导的 Object/Type 名义类型,已接入普通源码 class 和继承方法分派,已支持显式 NativeTyped 分配继承和可转移字段存储;trait 与完整元类行为仍待实现。 | 内容 | 职责 | | --- | --- | diff --git a/crates/objects/src/object.rs b/crates/objects/src/object.rs index 88b48f3..955079b 100644 --- a/crates/objects/src/object.rs +++ b/crates/objects/src/object.rs @@ -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, 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. diff --git a/crates/objects/src/store.rs b/crates/objects/src/store.rs index 485fb62..14aa597 100644 --- a/crates/objects/src/store.rs +++ b/crates/objects/src/store.rs @@ -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, 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. diff --git a/crates/objects/src/types.rs b/crates/objects/src/types.rs index d40155b..f75db14 100644 --- a/crates/objects/src/types.rs +++ b/crates/objects/src/types.rs @@ -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 { 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 { - self.payload::(base, "Type")?; + let fields = self + .payload::(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, @@ -268,11 +288,12 @@ impl ObjectStore { methods: BTreeMap, ) -> Result { let definition = self.payload::(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) } } diff --git a/crates/objects/tests/README.md b/crates/objects/tests/README.md index 167ccbd..1d055e8 100644 --- a/crates/objects/tests/README.md +++ b/crates/objects/tests/README.md @@ -4,6 +4,7 @@ | 文件 | 覆盖 | | --- | --- | +| [attribute_storage.rs](attribute_storage.rs) | 可转移字段存储、原类型身份、替换释放、属性写删和非法路径拒绝 | | [store.rs](store.rs) | 稳定地址、跨存储句柄拒绝与更新原子性、Dict/Cell 循环回收、函数校验失败无泄漏、共享代码与常量生命周期 | 非法句柄索引、槽位代数与计数溢出仍在 src/heap.rs 的私有单元测试内,不开放句柄构造器。VM 委托 API 与求值行为继续由 [core/tests](../../core/tests/README.md) 检查。随工作区在指定远端运行,本地不测试;现有测试源码不代表已通过,见 [验证](../../../docs/operations/validation.md)。返回 [objects](../README.md)。 diff --git a/crates/objects/tests/attribute_storage.rs b/crates/objects/tests/attribute_storage.rs new file mode 100644 index 0000000..435eebd --- /dev/null +++ b/crates/objects/tests/attribute_storage.rs @@ -0,0 +1,78 @@ +use objects::{ObjectPayload, ObjectRef, ObjectStore, TypeConstructor, Value, VmError}; + +#[derive(Debug)] +struct Proxy(ObjectRef); +impl ObjectPayload for Proxy { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn kind_name(&self) -> &'static str { + "proxy" + } + fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) { + visit(self.0); + } + fn attribute_target(&self, _: &ObjectStore) -> Result, VmError> { + Ok(Some(self.0)) + } +} + +#[test] +fn storage_retargeting_preserves_type_identity_and_releases_old_state() { + let mut store = ObjectStore::new().unwrap(); + let roots = store.bootstrap_types().unwrap(); + let class = store + .new_type("Proxy", roots.object, TypeConstructor::Record) + .unwrap(); + let storage = store.new_object().unwrap(); + let old_value = store.new_object().unwrap(); + let weak_value = store.downgrade(old_value).unwrap(); + store + .set_attr(storage, "value", Value::Object(old_value)) + .unwrap(); + store.release(old_value).unwrap(); + let proxy = store.allocate(Proxy(storage)).unwrap(); + store.attach_type(proxy, class).unwrap(); + store.release(storage).unwrap(); + assert_eq!(store.attribute_path(proxy).unwrap(), vec![proxy, storage]); + assert_eq!(store.get_attr(proxy, "type").unwrap(), Value::Object(class)); + assert_eq!( + store.get_attr(proxy, "value").unwrap(), + Value::Object(old_value) + ); + let next = store.new_object().unwrap(); + store.replace_payload(proxy, Proxy(next)).unwrap(); + store.release(next).unwrap(); + assert!(store.upgrade(weak_value).unwrap().is_none()); + store + .set_attr(proxy, "value", Value::Number(9_i64.into())) + .unwrap(); + assert_eq!( + store.get_attr(next, "value").unwrap(), + Value::Number(9_i64.into()) + ); + store.del_attr(proxy, "value").unwrap(); + assert!(store.get_attr(next, "value").is_err()); + for object in [proxy, class, roots.object, roots.type_type] { + store.release(object).unwrap(); + } + store.collect_cycles(); + assert_eq!(store.object_count(), 0); +} + +#[test] +fn cyclic_and_foreign_attribute_storage_fail_without_mutating_fields() { + let mut store = ObjectStore::new().unwrap(); + let storage = store.new_object().unwrap(); + let proxy = store.allocate(Proxy(storage)).unwrap(); + store.replace_payload(proxy, Proxy(proxy)).unwrap(); + let version = store.change_version(proxy).unwrap(); + assert!(store.get_attr(proxy, "field").is_err()); + assert!(store.set_attr(proxy, "field", Value::Nil).is_err()); + assert_eq!(store.change_version(proxy).unwrap(), version); + let other = ObjectStore::new().unwrap(); + assert_eq!(other.attribute_path(proxy), Err(VmError::ForeignHandle)); + store.release(storage).unwrap(); + store.release(proxy).unwrap(); + assert_eq!(store.collect_cycles(), 1); +} diff --git a/docs/README.md b/docs/README.md index 452cbff..4fbbf05 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,7 +6,7 @@ | 解释执行、资源修订与引擎升级 | [执行与版本](architecture/execution-and-versions.md) | frontend/compiler/core | | EndorObject、句柄、默认操作 | [对象模型](runtime/objects.md) | objects/src | | 原生可变上下文、委托、弱引用与变更通知 | [原生调用](runtime/native-calls.md) | objects/src/native.rs、changes.rs、core/src/eval.rs | -| 托管类型、继承关系与构造 | [名义类型](runtime/types.md) | objects/src/types.rs | +| 托管类型、原生分配继承与字段存储 | [名义类型](runtime/types.md) | objects/src/types.rs | | Number、String、Dict、Cell、Function | [基本对象](runtime/builtins.md) | objects/src | | 源码读取与来源 | [Reader](runtime/reader.md) | frontend/src/reader.rs | | 词法、语法、裸赋值和诊断 | [Frontend](runtime/frontend.md) | frontend/src/lexer.peg/grammar.peg | diff --git a/docs/contributing/handoff.md b/docs/contributing/handoff.md index b714453..b2697d5 100644 --- a/docs/contributing/handoff.md +++ b/docs/contributing/handoff.md @@ -10,7 +10,7 @@ Aster 是可嵌入引擎,只保留 objects、core、frontend、compiler。Obje ## 已实现 -- EndorObject、稳定 Box、代际句柄、显式强引用及手动循环回收;VM 从空堆启动,宿主可显式引导托管 Object/Type 和名义继承;普通源码 class、字段构造和继承方法分派已接入,trait 和原生类分配继承仍待完成,见[类型机制](../runtime/types.md)。 +- EndorObject、稳定 Box、代际句柄、显式强引用及手动循环回收;VM 从空堆启动,宿主可显式引导托管 Object/Type 和名义继承;普通源码 class、字段构造和继承方法分派已接入,显式 NativeTyped 原生分配继承已接入,trait 仍待完成,见[类型机制](../runtime/types.md)。 - Function、NativeFunction、Dict、Cell、异常数据、UTF-8 String 和无损表示优先的 Number。 - 小型 wordcode、共享代码对象、操作数/控制流/栈校验、显式执行帧及 execute/call;MakeFunction 可在执行中创建带默认参数与共享闭包 Cell 的函数。对象返回值持有根,用后 release_value;借用读取不自动 retain。 - EndorReader 在 lexer 前提供字符串/流/文件/宿主 URI provider 输入,产出不可变 SourceUnit,保存来源与不透明修订标签;带来源的词法/语法错误保留行列,Lyra parse 支持文件和 stdin。没有平台 URI 后端或版本锁实现,见 [阅读器](../runtime/reader.md)。 diff --git a/docs/runtime/objects.md b/docs/runtime/objects.md index bb126d7..dccda9e 100644 --- a/docs/runtime/objects.md +++ b/docs/runtime/objects.md @@ -1,6 +1,6 @@ # EndorObject 代码说明 -当前实现的是 Endor 的 VM 运行时基础对象,角色类似 CPython 的公共对象结构。宿主可显式引导托管 Object/Type 与名义继承,见[类型机制](types.md);普通源码类和继承方法分派已实现,trait 和原生类分配继承尚待接入。标量仍然可以直接保存在 Value 中,不要求一切装箱。 +当前实现的是 Endor 的 VM 运行时基础对象,角色类似 CPython 的公共对象结构。宿主可显式引导托管 Object/Type 与名义继承,见[类型机制](types.md);普通源码类和继承方法分派已实现,显式 NativeTyped 原生分配继承已接入,trait 尚待接入。标量仍然可以直接保存在 Value 中,不要求一切装箱。 基本对象及统一数字表示已在此基础上加入,详见 [基本对象与 Number](builtins.md)。`builtins.rs` 定义字符串、字典、Cell、函数和异常数据;`number.rs` 定义无损数字表示;`store_builtins.rs` 提供容器操作及关联对象校验。 @@ -102,6 +102,6 @@ compare 的反向规则是 Lt 对应 Gt、Le 对应 Ge,Eq/Ne 保持不变。 已实现:统一对象结构、稳定地址、代际句柄、身份、显式引用计数、托管属性、原生操作覆盖、比较与哈希、原生调用、资源清理和显式循环回收。另有受限原生可变上下文、安全弱引用、托管 Cell 赋值及有界对象变更记录,见 [原生调用](native-calls.md)。 -尚未实现:Python type 兼容、trait、原生类分配继承和描述符、任意 VM 重入调用上下文、自动/分代/增量 GC、语言终结器/复活、完整资源限额和 VM 快照;独立 frontend 已提供阅读器、lexer/parser 与 AST,基础源码执行由独立 [Compiler](compiler.md) 接入,见 [源码前端](frontend.md);执行器已有帧数和指令数限额。数据库仅为未来特殊类提供按需连接,普通对象或宿主基础信息不自动持久化,详见 [数据库特殊类](https://gitea.entroia.com/Zayden-Jung/endorphin-hestia-server/src/branch/main/docs/runtime/database-objects.md)。 +尚未实现:Python type 兼容、trait 和描述符、任意 VM 重入调用上下文、自动/分代/增量 GC、语言终结器/复活、完整资源限额和 VM 快照;独立 frontend 已提供阅读器、lexer/parser 与 AST,基础源码执行由独立 [Compiler](compiler.md) 接入,见 [源码前端](frontend.md);执行器已有帧数和指令数限额。数据库仅为未来特殊类提供按需连接,普通对象或宿主基础信息不自动持久化,详见 [数据库特殊类](https://gitea.entroia.com/Zayden-Jung/endorphin-hestia-server/src/branch/main/docs/runtime/database-objects.md)。 验证范围与执行约定见 [验证](../operations/validation.md)。 diff --git a/docs/runtime/types.md b/docs/runtime/types.md index 6ac696e..946af24 100644 --- a/docs/runtime/types.md +++ b/docs/runtime/types.md @@ -1,6 +1,6 @@ # 托管名义类型 -类型与实例保存在同一个 ObjectStore 中。此处是已实现的运行时机制;源码普通 `class`、字段构造和继承方法分派已经接入;trait、原生类分配继承和完整初始化协议尚未接入,不把 Rust 的 ObjectPayload 当成 Endor 的类定义。 +类型与实例保存在同一个 ObjectStore 中。此处是已实现的运行时机制;源码普通 `class`、字段构造和继承方法分派以及显式选择支持继承的原生分配器已经接入;trait 和完整初始化协议尚未接入,不把 Rust 的 ObjectPayload 当成 Endor 的类定义。 ## 创建与关系 @@ -19,11 +19,12 @@ ObjectStore/Vm 仍从空堆启动。宿主显式调用 `bootstrap_types()` 得 带类型的对象通过 `get_attr(object, "type")` 暴露类型对象,不能写入或删除。Type 对象额外提供只读的 `name`、`base`;根 Object 的 base 为 Nil。源码前端已有的成员读取和调用表达式可以直接使用这些对象。类型名称由宿主注册到全局字典,Aster 不硬编码 Block 或 Page。 -TypeConstructor 三种入口: +TypeConstructor 四种入口: - Record:按声明字段接收位置参数,创建带此类型的普通 Record 实例;无字段时不接收参数。 - Native(callable):通过现有显式 VM 调用循环委托给托管可调用对象;宿主构造器负责返回该类型的实例。 -- Abstract:拒绝直接构造。 +- NativeTyped(callable):显式支持源码继承,调用参数前增加请求构造的 Type;分配器须创建具有该类型的真实原生实例,并负责按类型 fields 元数据初始化字段。 +- Abstract:拒绝直接构造;其源码子类可以定义,但继续保持抽象。 原生 Type 调用通过 NativeContext::callee 取得当前定义,委托计入现有指令限额,不递归调用 Rust 求值器。尚不提供构造后初始化 continuation。 @@ -53,4 +54,11 @@ class DetailedCounter: Counter { 方法表本身是类型对象拥有的托管边。宿主 define_method 可显式替换条目,不改变类型身份、基类或已有绑定方法选中的函数。bind_attr 返回一份拥有式根;get_attr 返回借用值和未绑定方法,宿主不能把两者的调用/释放协议混用。positional_parameter_count 会扣除绑定的接收者,供宿主判断事件回调的剩余源码参数。 -这是一份实现子集说明:构造器只按字段赋值,没有默认值、命名字段构造或隐式 init;字段/参数/返回注解保留名称,尚未实施类型检查。普通类当前只能继承普通 Record 分配的类,继承原生或抽象分配器明确报错,不能伪造带 Block 名义身份的普通 Record。trait、原生视觉子类和用户自定义测量/绘制协议仍待实现。正式语言文档的候选语法不因这一实现被自动确认为规范。 +这是一份实现子集说明:构造器只按字段赋值,没有默认值、命名字段构造或隐式 init;字段/参数/返回注解保留名称,尚未实施类型检查。源码类可继承 Record 或 NativeTyped 分配器;未选择支持继承的旧 Native 分配器仍明确报错。继承会保留分配器和父类字段,不能伪造带原生名义身份的普通 Record。字段构造参数如何与原生参数组合由宿主分配器负责;Tessera 使用原生参数在前、继承及本类字段在后的协议。trait 和用户自定义测量/绘制协议仍待实现。正式语言文档的候选语法不因这一实现被自动确认为规范。 + + +## 可转移的字段存储 + +ObjectPayload 可选择实现 `attribute_target(store)`,把实例字段放在另一个受托管强边保活的对象上。`attribute_path` 验证完整路径,拒绝跨堆句柄及循环;get_attr/set_attr/del_attr 读取或修改最终存储对象。名义类型、方法选择及绑定接收者仍取原对象,字段存储不代替类型身份。默认返回 None,普通对象行为不变。 + +反应式宿主订阅路径中各中间对象的 Contents,以及最终对象的 Attribute(field);重定向字段存储时必须发布 Contents 变化。否则仅监听外壳字段会漏掉实际存储更新。Tessera 的 Block 把字段存在当前内容的托管 Record 中,共享替换后别名会跟随新存储,旧字段及其引用可正常回收。实现回归见 [attribute_storage.rs](../../crates/objects/tests/attribute_storage.rs);本地仅编译。