From 6346041f779029a3279bc11492b8ccaeb5825734 Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Wed, 21 Oct 2026 22:38:22 +0800 Subject: [PATCH] Check source function annotations and unify managed function identity --- crates/compiler/src/README.md | 2 +- crates/compiler/src/codegen.rs | 27 +++++- crates/compiler/tests/README.md | 2 +- crates/compiler/tests/pipeline.rs | 36 ++++++++ crates/core/src/README.md | 6 +- crates/core/src/eval.rs | 56 ++++++++++++ crates/core/src/frame.rs | 1 + crates/core/src/lib.rs | 11 +-- crates/core/src/vm.rs | 15 ++++ crates/core/tests/README.md | 2 +- crates/core/tests/types.rs | 131 +++++++++++++++++++++++++++ crates/objects/src/README.md | 18 ++-- crates/objects/src/builtins.rs | 3 + crates/objects/src/code.rs | 30 +++++++ crates/objects/src/error.rs | 19 ++++ crates/objects/src/heap.rs | 3 + crates/objects/src/lib.rs | 5 +- crates/objects/src/native.rs | 3 + crates/objects/src/object.rs | 8 ++ crates/objects/src/store.rs | 41 ++++++++- crates/objects/src/types.rs | 141 ++++++++++++++++++++++++++++++ docs/README.md | 2 +- docs/contributing/handoff.md | 2 +- docs/runtime/builtins.md | 4 +- docs/runtime/bytecode.md | 4 +- docs/runtime/compiler.md | 8 +- docs/runtime/types.md | 28 +++++- 27 files changed, 568 insertions(+), 40 deletions(-) diff --git a/crates/compiler/src/README.md b/crates/compiler/src/README.md index c02822f..66acd2a 100644 --- a/crates/compiler/src/README.md +++ b/crates/compiler/src/README.md @@ -4,6 +4,6 @@ | --- | --- | | [lib.rs](lib.rs) | CompileOptions、公开分析/编译入口、AST 原文范围检查与带来源诊断 | | [analysis.rs](analysis.rs) | 模块/函数/类方法/表达式/单次交互作用域、局部槽、全局名称、逐引用绑定和初始化检查 | -| [codegen.rs](codegen.rs) | AST 到函数与类创建指令、嵌套代码模板、EndorCode 验证、显式 VM 装载与临时根释放 | +| [codegen.rs](codegen.rs) | AST 到函数与类创建指令、嵌套代码模板与源码签名保留、EndorCode 验证、显式 VM 装载与临时根释放 | 节点定义在 frontend/Endor.asdl;这里匹配生成的 AST,不复制节点类型。源码和调用方提供的 AST 都先分析后生成,不执行 AST。修改名称语义、代码布局或所有权时同步 [编译契约](../../../docs/runtime/compiler.md)、[检查](../tests/README.md) 与 [示例](../examples/README.md)。返回 [compiler](../README.md)。 diff --git a/crates/compiler/src/codegen.rs b/crates/compiler/src/codegen.rs index a8950d8..78e3ae5 100644 --- a/crates/compiler/src/codegen.rs +++ b/crates/compiler/src/codegen.rs @@ -25,6 +25,9 @@ impl CompiledCode { pub fn name(&self) -> &str { self.code.name() } + pub fn source_signature(&self) -> Option<&endor_core::SourceSignature> { + self.code.source_signature() + } pub fn filename(&self) -> &str { self.code.filename() } @@ -47,7 +50,7 @@ impl CompiledCode { roots.push(reference); constants[*index] = Value::Object(reference); } - let code = EndorCode::with_layout( + let mut code = EndorCode::with_layout( self.code.bytecode().to_vec(), constants, self.code.layout().clone(), @@ -57,6 +60,9 @@ impl CompiledCode { self.code.filename(), self.code.first_line(), ); + if let Some(signature) = self.code.source_signature() { + code = code.with_source_signature(signature.clone())?; + } vm.new_code(code) })(); for reference in roots { @@ -152,7 +158,7 @@ impl<'a> Emitter<'a> { fn declaration(&mut self, declaration: &Decl, symbols: &SymbolTable) -> Result<(), Diagnostic> { match &declaration.kind { DeclKind::Function(function) => { - self.function_value(function, symbols)?; + self.function_value(function, symbols, None)?; self.emit(Opcode::StoreGlobal, self.name_index(&function.name.text)) } DeclKind::Class(class) => { @@ -169,7 +175,7 @@ impl<'a> Emitter<'a> { self.emit(Opcode::BuildMap, class.fields.len())?; for method in &class.methods { self.constant(Value::Text(method.name.text.clone()))?; - self.function_value(method, symbols)?; + self.function_value(method, symbols, Some(&class.name.text))?; } self.emit(Opcode::BuildMap, class.methods.len())?; self.simple(Opcode::BuildClass); @@ -182,6 +188,7 @@ impl<'a> Emitter<'a> { &mut self, function: &frontend::ast::Function, symbols: &SymbolTable, + owner: Option<&str>, ) -> Result<(), Diagnostic> { let scope = symbols .functions @@ -203,7 +210,19 @@ impl<'a> Emitter<'a> { body.constant(Value::Nil)?; body.simple(Opcode::ReturnValue); } - let child = body.finish()?; + let mut child = body.finish()?; + child.code = child + .code + .with_source_signature(endor_core::SourceSignature { + parameters: function + .parameters + .iter() + .map(|p| p.type_name.text.clone()) + .collect(), + return_type: function.return_type.text.clone(), + method_owner: owner.map(str::to_owned), + }) + .map_err(|failure| error(function.span, failure.to_string()))?; let index = self.constants.len(); self.children.push((index, child)); self.constant(Value::Nil)?; // Replaced by a managed code object during load(). diff --git a/crates/compiler/tests/README.md b/crates/compiler/tests/README.md index d87e599..8a438f6 100644 --- a/crates/compiler/tests/README.md +++ b/crates/compiler/tests/README.md @@ -2,7 +2,7 @@ [classes.rs](classes.rs) 覆盖源码 class、NativeTyped 实际原生载荷继承和多层字段构造、继承字段构造、动态方法覆盖、绑定接收者保活/回收、绑定后参数数量和重复成员/缺少 self 的预执行诊断。只编译,不在本地运行。 -[pipeline.rs](pipeline.rs) 将真实 Endor 示例交给 reader/parser/名称分析/代码生成/VM,检查函数值、前向引用、局部遮蔽、精确数字常量、语义诊断、宿主全局与属性回调、对象结果保活、跨 VM 模板装载、反复调用、错误帧清理、递归限额、共享交互命名空间及超过 255 的操作数。 +[pipeline.rs](pipeline.rs) 将真实 Endor 示例交给 reader/parser/名称分析/代码生成/VM,检查函数值、前向引用、局部遮蔽、精确数字常量、语义诊断、宿主全局与属性回调、对象结果保活、跨 VM 模板装载、源码签名保留和参数/返回失败清理、反复调用、错误帧清理、递归限额、共享交互命名空间及超过 255 的操作数。 裸赋值检查覆盖跨交互输入创建/更新 globals、参数重赋值、局部槽复用及未初始化读取;执行结束检查空堆。 diff --git a/crates/compiler/tests/pipeline.rs b/crates/compiler/tests/pipeline.rs index ba5b67f..98d98c8 100644 --- a/crates/compiler/tests/pipeline.rs +++ b/crates/compiler/tests/pipeline.rs @@ -446,3 +446,39 @@ fn assignments_update_parameters_and_allocate_one_local_slot_per_target() { .is_err() ); } + +#[test] +fn source_signatures_survive_template_load_and_enable_runtime_checks() { + let compiled = compile( + "function identity(value: Number): Number { return value }\nfunction bad(): Number { return \"wrong\" }", + ParseMode::Module, + ); + let signature = compiled + .children() + .next() + .unwrap() + .source_signature() + .unwrap(); + assert_eq!(signature.parameters, ["Number"]); + assert_eq!(signature.return_type, "Number"); + let mut vm = Vm::new().unwrap(); + vm.set_source_type_checks(true).unwrap(); + let (code, globals) = module(&mut vm, &compiled); + let identity = function(&vm, globals, "identity"); + assert_eq!( + vm.call(identity, &[Value::Number(7.into())]).unwrap(), + Value::Number(7.into()) + ); + assert!(matches!( + vm.call(identity, &[Value::Text("wrong".into())]), + Err(VmError::TypeMismatch { .. }) + )); + let bad = function(&vm, globals, "bad"); + let baseline = vm.object_count(); + assert!(matches!( + vm.call(bad, &[]), + Err(VmError::TypeMismatch { .. }) + )); + assert_eq!(vm.object_count(), baseline); + cleanup(&mut vm, code, globals); +} diff --git a/crates/core/src/README.md b/crates/core/src/README.md index f0d9deb..2f1c8c2 100644 --- a/crates/core/src/README.md +++ b/crates/core/src/README.md @@ -5,9 +5,9 @@ | 文件 | 职责 | | --- | --- | | [lib.rs](lib.rs) | VM 类型导出、objects 类型兼容重导出、宿主调用与字节码 rustdoc 示例 | -| [vm.rs](vm.rs) | Vm 拥有 ObjectStore 与错误位置记录;对象操作和宿主 new_class 和显式Trait注册/关系查询显式委托给 objects | -| [frame.rs](frame.rs) | EndorFrame、栈条目、执行位置元数据、ExecutionLimits | -| [eval.rs](eval.rs) | execute/call、只读调用与依赖结果、参数绑定、帧切换、取指分派、MakeFunction、结果根转移与失败清理 | +| [vm.rs](vm.rs) | Vm 拥有 ObjectStore 与错误位置记录;对象操作和宿主 new_class 和显式Trait注册/关系查询显式委托给 objects;源码检查开关与函数类型配置 | +| [frame.rs](frame.rs) | EndorFrame、栈条目、返回类型执行根、执行位置元数据、ExecutionLimits | +| [eval.rs](eval.rs) | execute/call、只读调用与依赖结果、参数绑定、可选注解检查、帧切换、取指分派、MakeFunction、结果根转移与失败清理 | ## 常见修改入口 diff --git a/crates/core/src/eval.rs b/crates/core/src/eval.rs index d69857b..025f8e9 100644 --- a/crates/core/src/eval.rs +++ b/crates/core/src/eval.rs @@ -135,6 +135,10 @@ impl Vm { } self.release(frame.function) .expect("frame owns its function root"); + if let Some(reference) = frame.return_type.and_then(|kind| kind.reference()) { + self.release(reference) + .expect("frame owns its return type root"); + } } fn prepare_frame( @@ -161,8 +165,45 @@ impl Vm { for value in arguments { self.validate_value(value)?; } + let mut return_type = None; + if self.source_type_checks + && let Some(signature) = executable.source_signature() + { + let receiver = arguments.first().and_then(|value| match value { + Value::Object(object) => Some(*object), + _ => None, + }); + for (index, expected_name) in signature.parameters.iter().enumerate() { + let value = arguments + .get(index) + .unwrap_or_else(|| &data.defaults()[index - required]); + let expected = self + .objects + .annotation_type(function, expected_name, receiver)?; + if !self.objects.matches_annotation(value, expected)? { + return Err(VmError::TypeMismatch { + function: data.name().into(), + position: format!("parameter {}", executable.parameters()[index]), + expected: expected_name.clone(), + actual: self.objects.value_type_name(value)?, + }); + } + } + return_type = Some(self.objects.annotation_type( + function, + &signature.return_type, + receiver, + )?); + } self.retain(function)?; + if let Some(reference) = return_type.and_then(|kind| kind.reference()) + && let Err(error) = self.retain(reference) + { + self.release(function)?; + return Err(error); + } let mut frame = EndorFrame { + return_type, function, previous: None, code: data.code(), @@ -614,6 +655,21 @@ impl Vm { } Opcode::ReturnValue => { let result = Self::top_value(frame)?; + if let Some(expected) = frame.return_type + && !self.objects.matches_annotation(&result, expected)? + { + return Err(VmError::TypeMismatch { + function: frame.name.clone(), + position: "return".into(), + expected: frame + .executable + .source_signature() + .expect("checked source signature") + .return_type + .clone(), + actual: self.objects.value_type_name(&result)?, + }); + } frame.stack.pop(); self.clear_frame(frames.pop().unwrap()); if let Some(caller) = frames.last_mut() { diff --git a/crates/core/src/frame.rs b/crates/core/src/frame.rs index 5f62100..91afeec 100644 --- a/crates/core/src/frame.rs +++ b/crates/core/src/frame.rs @@ -15,6 +15,7 @@ pub(crate) enum StackEntry { #[derive(Debug)] pub struct EndorFrame { pub(crate) function: ObjectRef, + pub(crate) return_type: Option, /// Index of the caller in the execution's frame vector, never a pointer. pub(crate) previous: Option, pub(crate) code: ObjectRef, diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 837ba3c..1a19fc6 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -45,16 +45,17 @@ mod vm; pub use frame::{EndorFrame, ExecutionLimits, ExecutionLocation}; // Preserve existing embedding imports; objects owns these definitions. pub use objects::ObjectStore; +pub use objects::{ + AnnotationType, ClassField, EndorTraitObject, EndorTypeObject, MethodRequirement, RootTypes, + TypeConstructor, +}; pub use objects::{ ChangeBatch, ChangeKey, CodeLayout, CompareOp, ContextNativeFunction, DecodedInstruction, EndorBaseExceptionObject, EndorCellObject, EndorCode, EndorCodeObject, EndorDecimal, EndorDictObject, EndorFunctionObject, EndorInteger, EndorNativeCall, EndorNativeFunctionObject, EndorNumber, EndorNumberObject, EndorObject, EndorRational, EndorReal, EndorStringObject, EndorTracebackEntry, Instruction, NativeCall, NativeCallable, NativeContext, NativeFunction, - NativeResult, ObjectChange, ObjectHeader, ObjectPayload, ObjectRef, Opcode, Record, Value, - VmError, WeakObjectRef, -}; -pub use objects::{ - ClassField, EndorTraitObject, EndorTypeObject, MethodRequirement, RootTypes, TypeConstructor, + NativeResult, ObjectChange, ObjectHeader, ObjectPayload, ObjectRef, Opcode, Record, + SourceSignature, Value, VmError, WeakObjectRef, }; pub use vm::Vm; diff --git a/crates/core/src/vm.rs b/crates/core/src/vm.rs index 8e394fd..42d5870 100644 --- a/crates/core/src/vm.rs +++ b/crates/core/src/vm.rs @@ -11,9 +11,23 @@ use crate::{ pub struct Vm { pub(crate) objects: ObjectStore, pub(crate) last_traceback: Vec, + pub(crate) source_type_checks: bool, } impl Vm { + pub fn set_source_type_checks(&mut self, enabled: bool) -> Result<(), VmError> { + if self.objects.is_read_only() { + return Err(VmError::UnsupportedOperation( + "configure source type checks during read-only call", + )); + } + self.source_type_checks = enabled; + Ok(()) + } + pub fn set_function_type(&mut self, definition: ObjectRef) -> Result<(), VmError> { + self.objects.set_function_type(definition) + } + pub fn new_trait( &mut self, name: impl Into, @@ -134,6 +148,7 @@ impl Vm { Ok(Self { objects: ObjectStore::new()?, last_traceback: Vec::new(), + source_type_checks: false, }) } diff --git a/crates/core/tests/README.md b/crates/core/tests/README.md index 14365ae..916ece6 100644 --- a/crates/core/tests/README.md +++ b/crates/core/tests/README.md @@ -7,7 +7,7 @@ | [runtime.rs](runtime.rs) | 空堆启动、默认操作、引用/借用、属性原子性、稳定地址、长链/循环、原生回调、VM 销毁 | | [builtins.rs](builtins.rs) | 无损 Number、Unicode 字符串、Dict 顺序与键、Cell、函数环境/共享代码、异常数据 | | [execution.rs](execution.rs) | wordcode 与校验、控制流、参数/默认值、嵌套帧、字节码函数创建/默认值/环境捕获、闭包、返回值存活、失败清理、限额 | -| [types.rs](types.rs) | 名义身份、继承与实例关系、类型边保活/循环回收、只读类型元数据与外来句柄、显式Trait身份/继承/参数约束及后代更新原子性;仅远端执行 | +| [types.rs](types.rs) | 名义身份、继承与实例关系、类型边保活/循环回收、只读类型元数据与外来句柄、显式Trait身份/继承/参数约束及后代更新原子性;统一函数分类与返回类型错误根清理;仅远端执行 | | [native_context.rs](native_context.rs) | 原生作用域根、分配失败清理、委托、只读结果分配/写入拒绝/依赖与失败恢复、弱引用过期及有界变更;仅远端执行 | 核心对象现由 objects 定义,本目录保留既有 endor_core 导入与 Vm 方法的兼容性检查。独立对象存储检查见 [objects/tests](../../objects/tests/README.md)。堆内部伪造句柄与计数溢出检查在 objects 的 src/heap.rs,公开示例由 src/lib.rs rustdoc 提供。文件职责见 [src](../src/README.md),检查方式和结果边界见 [验证](../../../docs/operations/validation.md)。本地不执行测试;存在测试文件不代表某次提交已通过。返回 [core](../README.md)。 diff --git a/crates/core/tests/types.rs b/crates/core/tests/types.rs index 086572d..b05a32f 100644 --- a/crates/core/tests/types.rs +++ b/crates/core/tests/types.rs @@ -198,3 +198,134 @@ fn late_trait_registration_and_ancestor_updates_validate_existing_descendants() Err(VmError::ForeignHandle) ); } + +#[test] +fn configured_function_identity_covers_source_native_and_bound_methods() { + use endor_core::{NativeFunction, SourceSignature}; + let mut vm = Vm::new().unwrap(); + let roots = vm.bootstrap_types().unwrap(); + let function_type = vm + .new_type("Function", roots.object, TypeConstructor::Abstract) + .unwrap(); + vm.set_function_type(function_type).unwrap(); + let source = contract_method(&mut vm, 1); + let native = vm + .allocate(NativeFunction::new("native", |_| Ok(Value::Nil))) + .unwrap(); + assert!(vm.is_instance(source, function_type).unwrap()); + assert!(vm.is_instance(native, function_type).unwrap()); + vm.attach_type(source, function_type).unwrap(); + let class = vm + .new_class( + "Data", + roots.object, + vec![], + [("read".into(), source)].into(), + ) + .unwrap(); + let Value::Object(instance) = vm.call(class, &[]).unwrap() else { + panic!("instance required"); + }; + let Value::Object(bound) = vm.bind_attr(instance, "read").unwrap() else { + panic!("method required"); + }; + assert!(vm.is_instance(bound, function_type).unwrap()); + assert!(!vm.is_instance(class, function_type).unwrap()); + assert!(vm.set_function_type(class).is_err()); + let invalid = endor_core::EndorCode::from_instructions( + &[ + endor_core::Instruction::new(endor_core::Opcode::LoadConst, 0), + endor_core::Instruction::simple(endor_core::Opcode::ReturnValue), + ], + vec![Value::Nil], + endor_core::CodeLayout::default(), + ) + .unwrap() + .with_source_signature(SourceSignature { + parameters: vec!["Number".into()], + return_type: "Unit".into(), + method_owner: None, + }); + assert!(invalid.is_err()); + for object in [ + source, + native, + class, + instance, + bound, + function_type, + roots.object, + roots.type_type, + ] { + vm.release(object).unwrap(); + } + vm.collect_cycles(); + assert_eq!(vm.object_count(), 0); +} + +#[test] +fn failed_nominal_return_releases_value_and_captured_type_roots() { + use endor_core::{ + CodeLayout, EndorCode, EndorCodeObject, Instruction, Opcode, SourceSignature, + }; + let mut vm = Vm::new().unwrap(); + vm.set_source_type_checks(true).unwrap(); + let roots = vm.bootstrap_types().unwrap(); + let expected = vm + .new_type("Expected", roots.object, TypeConstructor::Record) + .unwrap(); + let other = vm + .new_type("Other", roots.object, TypeConstructor::Record) + .unwrap(); + let Value::Object(value) = vm.call(other, &[]).unwrap() else { + panic!("instance required"); + }; + let globals = vm.new_dict().unwrap(); + vm.dict_set( + globals, + Value::Text("Expected".into()), + Value::Object(expected), + ) + .unwrap(); + let code = EndorCode::from_instructions( + &[ + Instruction::new(Opcode::LoadConst, 0), + Instruction::simple(Opcode::ReturnValue), + ], + vec![Value::Object(value)], + CodeLayout::default(), + ) + .unwrap() + .with_source_signature(SourceSignature { + parameters: vec![], + return_type: "Expected".into(), + method_owner: None, + }) + .unwrap(); + let code = vm.allocate(EndorCodeObject::new(code)).unwrap(); + let function = vm + .new_function("bad return", code, globals, vec![], vec![]) + .unwrap(); + let before_type = vm.ref_count(expected).unwrap(); + let before_value = vm.ref_count(value).unwrap(); + assert!(matches!( + vm.call(function, &[]), + Err(VmError::TypeMismatch { .. }) + )); + assert_eq!(vm.ref_count(expected).unwrap(), before_type); + assert_eq!(vm.ref_count(value).unwrap(), before_value); + for object in [ + function, + code, + globals, + value, + expected, + other, + roots.object, + roots.type_type, + ] { + vm.release(object).unwrap(); + } + vm.collect_cycles(); + assert_eq!(vm.object_count(), 0); +} diff --git a/crates/objects/src/README.md b/crates/objects/src/README.md index 0cc1ec1..7c621b5 100644 --- a/crates/objects/src/README.md +++ b/crates/objects/src/README.md @@ -4,19 +4,19 @@ | 文件 | 职责 | | --- | --- | -| [lib.rs](lib.rs) | 包边界、对象类型/ObjectStore 公开导出(含Trait/MethodRequirement)与独立存储 rustdoc 示例 | -| [object.rs](object.rs) | Value、CompareOp、ObjectPayload、ObjectHeader、EndorObject、Record、NativeFunction | -| [types.rs](types.rs) | 托管名义类型、显式 Object/Type 引导、继承查询、受管方法表/接收者绑定、类字段、显式Trait定义/声明/方法参数约束、原生构造委托 | -| [native.rs](native.rs) | NativeContext、上下文原生函数、作用域语言函数工厂、显式能力声明入口、根与非递归委托调用 | +| [lib.rs](lib.rs) | 包边界、对象类型/ObjectStore 公开导出(含Trait/MethodRequirement/SourceSignature/AnnotationType)与独立存储 rustdoc 示例 | +| [object.rs](object.rs) | Value、CompareOp、ObjectPayload(含显式函数标记)、ObjectHeader、EndorObject、Record、NativeFunction | +| [types.rs](types.rs) | 托管名义类型、显式 Object/Type 引导、继承查询、受管方法表/接收者绑定、类字段、显式Trait定义/声明/方法参数约束、原生构造委托、注解解析/匹配 | +| [native.rs](native.rs) | NativeContext、上下文原生函数、作用域语言函数工厂、显式能力声明入口、函数标记、根与非递归委托调用 | | [changes.rs](changes.rs) | 合并变更批次、变更键与有界通知额度 | | [number.rs](number.rs) | Integer/Decimal/Rational/BinaryFloat/Complex 的无损表示和装箱载荷 | -| [builtins.rs](builtins.rs) | String、Dict、Cell、Function、BaseException 载荷与 trace 边 | -| [heap.rs](heap.rs) | 私有 Heap、不可伪造 ObjectRef、稳定 Box、引用事务、迭代释放、循环回收、后代契约更新的私有借用枚举、只读变更屏障/全调用及嵌套子调用读取跟踪及私有边界测试 | -| [store.rs](store.rs) | ObjectStore、空堆创建、对象操作、retain/release 和回收入口 | +| [builtins.rs](builtins.rs) | String、Dict、Cell、Function、BaseException 载荷与 trace 边、语言函数标记 | +| [heap.rs](heap.rs) | 私有 Heap、不可伪造 ObjectRef、稳定 Box、引用事务、迭代释放、循环回收、后代契约更新的私有借用枚举、同类型附加幂等、只读变更屏障/全调用及嵌套子调用读取跟踪及私有边界测试 | +| [store.rs](store.rs) | ObjectStore、空堆创建、对象操作、retain/release 和回收入口、弱 Function 类型配置与分配分类 | | [store_builtins.rs](store_builtins.rs) | 载荷校验、代码/函数工厂、Dict/Cell 操作及键语义 | | [instruction.rs](instruction.rs) | Opcode、Instruction、wordcode 编解码、MakeFunction/比较参数与跳转读取接口 | -| [code.rs](code.rs) | CodeLayout、EndorCode/EndorCodeObject、共享不可变代码和操作数/控制流/栈验证 | -| [error.rs](error.rs) | 对象、代码与执行共用的 VmError(含能力/方法定位的TraitViolation),避免反向依赖 core | +| [code.rs](code.rs) | CodeLayout、EndorCode/EndorCodeObject、可选源码签名、共享不可变代码和操作数/控制流/栈验证 | +| [error.rs](error.rs) | 对象、代码与执行共用的 VmError(含TraitViolation、UnknownAnnotation、TypeMismatch),避免反向依赖 core | 对象操作变更同步 [对象模型](../../../docs/runtime/objects.md),内置载荷同步 [基本对象](../../../docs/runtime/builtins.md)。新增操作码需要同时修改本包 instruction.rs/code.rs 和 core 的 eval.rs 分派,见 [字节码 VM](../../../docs/runtime/bytecode.md)。 diff --git a/crates/objects/src/builtins.rs b/crates/objects/src/builtins.rs index 525bb69..d7323d8 100644 --- a/crates/objects/src/builtins.rs +++ b/crates/objects/src/builtins.rs @@ -208,6 +208,9 @@ impl EndorFunctionObject { } impl ObjectPayload for EndorFunctionObject { + fn is_function(&self) -> bool { + true + } fn reference_assignment_only(&self) -> bool { true } diff --git a/crates/objects/src/code.rs b/crates/objects/src/code.rs index 63337b0..2fe18ee 100644 --- a/crates/objects/src/code.rs +++ b/crates/objects/src/code.rs @@ -17,6 +17,14 @@ pub struct CodeLayout { pub names: Vec, } +/// Source declaration metadata, separate from executable slot layout. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SourceSignature { + pub parameters: Vec, + pub return_type: String, + pub method_owner: Option, +} + #[derive(Debug, Clone)] pub struct EndorCode { bytecode: Vec, @@ -27,6 +35,7 @@ pub struct EndorCode { name: String, filename: String, first_line: usize, + signature: Option, } impl EndorCode { @@ -73,9 +82,30 @@ impl EndorCode { name: "".into(), filename: "".into(), first_line: 1, + signature: None, }) } + pub fn with_source_signature(mut self, signature: SourceSignature) -> Result { + if signature.parameters.len() != self.parameters().len() + || signature.return_type.is_empty() + || signature.parameters.iter().any(String::is_empty) + || signature + .method_owner + .as_ref() + .is_some_and(String::is_empty) + { + return Err(VmError::InvalidArgument( + "source signature does not match code parameters", + )); + } + self.signature = Some(signature); + Ok(self) + } + pub fn source_signature(&self) -> Option<&SourceSignature> { + self.signature.as_ref() + } + pub fn with_location( mut self, name: impl Into, diff --git a/crates/objects/src/error.rs b/crates/objects/src/error.rs index 85d137d..5b23d16 100644 --- a/crates/objects/src/error.rs +++ b/crates/objects/src/error.rs @@ -18,6 +18,13 @@ pub enum VmError { method: String, reason: &'static str, }, + UnknownAnnotation(String), + TypeMismatch { + function: String, + position: String, + expected: String, + actual: String, + }, EmptyCell, InvalidBytecode { offset: usize, @@ -53,6 +60,18 @@ impl fmt::Display for VmError { method, reason, } => write!(f, "trait {capability} method {method}: {reason}"), + Self::UnknownAnnotation(name) => { + write!(f, "unknown or invalid type annotation: {name}") + } + Self::TypeMismatch { + function, + position, + expected, + actual, + } => write!( + f, + "{function} {position} expects {expected}, received {actual}" + ), Self::EmptyCell => f.write_str("cell is empty"), Self::InvalidBytecode { offset, reason } => { write!(f, "invalid bytecode at code unit {offset}: {reason}") diff --git a/crates/objects/src/heap.rs b/crates/objects/src/heap.rs index 53308d2..42607aa 100644 --- a/crates/objects/src/heap.rs +++ b/crates/objects/src/heap.rs @@ -210,6 +210,9 @@ impl Heap { reference: ObjectRef, type_object: ObjectRef, ) -> Result<(), VmError> { + if self.get(reference)?.header.type_object == Some(type_object) { + return Ok(()); + } if self.get(reference)?.header.type_object.is_some() { return Err(VmError::InvalidArgument("object already has a type")); } diff --git a/crates/objects/src/lib.rs b/crates/objects/src/lib.rs index 79a1353..f31089c 100644 --- a/crates/objects/src/lib.rs +++ b/crates/objects/src/lib.rs @@ -31,7 +31,7 @@ pub use builtins::{ EndorStringObject, EndorTracebackEntry, }; pub use changes::{ChangeBatch, ChangeKey, ObjectChange}; -pub use code::{CodeLayout, EndorCode, EndorCodeObject}; +pub use code::{CodeLayout, EndorCode, EndorCodeObject, SourceSignature}; pub use error::VmError; pub use heap::{ObjectRef, WeakObjectRef}; pub use instruction::{DecodedInstruction, Instruction, Opcode}; @@ -45,5 +45,6 @@ pub use object::{ }; pub use store::ObjectStore; pub use types::{ - ClassField, EndorTraitObject, EndorTypeObject, MethodRequirement, RootTypes, TypeConstructor, + AnnotationType, ClassField, EndorTraitObject, EndorTypeObject, MethodRequirement, RootTypes, + TypeConstructor, }; diff --git a/crates/objects/src/native.rs b/crates/objects/src/native.rs index 9e6dee8..c5dfc89 100644 --- a/crates/objects/src/native.rs +++ b/crates/objects/src/native.rs @@ -211,6 +211,9 @@ impl ContextNativeFunction { } impl ObjectPayload for ContextNativeFunction { + fn is_function(&self) -> bool { + true + } fn reference_assignment_only(&self) -> bool { true } diff --git a/crates/objects/src/object.rs b/crates/objects/src/object.rs index 86f1883..0eec937 100644 --- a/crates/objects/src/object.rs +++ b/crates/objects/src/object.rs @@ -51,6 +51,11 @@ 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 { + /// Explicit function category; callable Type objects are not functions. + fn is_function(&self) -> bool { + false + } + /// 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> { @@ -190,6 +195,9 @@ impl EndorNativeFunctionObject { pub type NativeFunction = EndorNativeFunctionObject; impl ObjectPayload for EndorNativeFunctionObject { + fn is_function(&self) -> bool { + true + } fn reference_assignment_only(&self) -> bool { true } diff --git a/crates/objects/src/store.rs b/crates/objects/src/store.rs index 58aba78..192099c 100644 --- a/crates/objects/src/store.rs +++ b/crates/objects/src/store.rs @@ -10,6 +10,7 @@ use crate::{ #[derive(Debug)] pub struct ObjectStore { pub(crate) heap: Heap, + function_type: Option, } impl ObjectStore { @@ -125,7 +126,10 @@ impl ObjectStore { } pub fn new() -> Result { - Ok(Self { heap: Heap::new()? }) + Ok(Self { + heap: Heap::new()?, + function_type: None, + }) } pub fn object_count(&self) -> usize { @@ -136,7 +140,40 @@ impl ObjectStore { /// are borrowed; use retain/release to manage additional owned references. pub fn allocate(&mut self, payload: impl ObjectPayload) -> Result { self.validate_builtin(&payload)?; - self.heap.allocate(Box::new(payload)) + let function = payload.is_function(); + let object = self.heap.allocate(Box::new(payload))?; + if function && let Some(weak) = self.function_type { + let result = (|| { + let definition = self.upgrade(weak)?.ok_or(VmError::InvalidArgument( + "configured Function type has expired", + ))?; + let result = self.attach_type(object, definition); + self.release(definition)?; + result + })(); + if let Err(error) = result { + self.release(object)?; + return Err(error); + } + } + Ok(object) + } + + pub fn set_function_type(&mut self, definition: ObjectRef) -> Result<(), VmError> { + self.payload::(definition, "Type")?; + if self.is_read_only() { + return Err(VmError::UnsupportedOperation( + "configure Function type during read-only call", + )); + } + let weak = self.downgrade(definition)?; + if self.function_type.is_some_and(|old| old != weak) { + return Err(VmError::InvalidArgument( + "Function type is already configured", + )); + } + self.function_type = Some(weak); + Ok(()) } pub fn new_object(&mut self) -> Result { diff --git a/crates/objects/src/types.rs b/crates/objects/src/types.rs index f0c54e7..181f49d 100644 --- a/crates/objects/src/types.rs +++ b/crates/objects/src/types.rs @@ -483,6 +483,9 @@ struct BoundMethod { function: ObjectRef, } impl ObjectPayload for BoundMethod { + fn is_function(&self) -> bool { + true + } fn reference_assignment_only(&self) -> bool { true } @@ -726,3 +729,141 @@ impl ObjectStore { } } } + +/// Borrowed expected-type descriptor. Frames retain its managed reference +/// while validating the return, so rebinding globals cannot release it mid-call. +#[derive(Debug, Clone, Copy)] +pub enum AnnotationType { + Any, + Unit, + Number, + String, + Bool, + Function, + Type, + Trait, + Nominal(ObjectRef), + Capability(ObjectRef), +} +impl AnnotationType { + pub fn reference(self) -> Option { + match self { + Self::Nominal(reference) | Self::Capability(reference) => Some(reference), + _ => None, + } + } +} +impl ObjectStore { + pub fn annotation_type( + &self, + function: ObjectRef, + name: &str, + receiver: Option, + ) -> Result { + let data = self.payload::(function, "function")?; + let signature = self + .payload::(data.code(), "code")? + .code() + .source_signature(); + // Bind a method's own class to the definition that actually owns it, + // including inherited methods and old instances after REPL redefinition. + if signature.and_then(|s| s.method_owner.as_deref()) == Some(name) + && let Some(receiver) = receiver + { + let mut current = self.type_of(receiver)?; + while let Some(definition) = current { + let payload = self.payload::(definition, "Type")?; + if payload.name == name + && payload.methods.values().any(|method| *method == function) + { + return Ok(AnnotationType::Nominal(definition)); + } + current = payload.base; + } + } + let primitive = match name { + "Object" => Some(AnnotationType::Any), + "Unit" => Some(AnnotationType::Unit), + "Number" => Some(AnnotationType::Number), + "String" | "Text" => Some(AnnotationType::String), + "Bool" => Some(AnnotationType::Bool), + "Function" => Some(AnnotationType::Function), + "Type" => Some(AnnotationType::Type), + "Trait" => Some(AnnotationType::Trait), + _ => None, + }; + if let Some(primitive) = primitive { + return Ok(primitive); + } + let value = self + .dict_get(data.globals(), &Value::Text(name.into()))? + .or(match data.builtins() { + Some(builtins) => self.dict_get(builtins, &Value::Text(name.into()))?, + None => None, + }); + if let Some(Value::Object(definition)) = value { + let payload = self.object(definition)?.payload(); + if payload.as_any().is::() { + return Ok(AnnotationType::Nominal(definition)); + } + if payload.as_any().is::() { + return Ok(AnnotationType::Capability(definition)); + } + } + Err(VmError::UnknownAnnotation(name.into())) + } + pub fn matches_annotation( + &self, + value: &Value, + expected: AnnotationType, + ) -> Result { + if let Value::Object(object) = value { + self.object(*object)?; + } + Ok(match expected { + AnnotationType::Any => true, + AnnotationType::Unit => matches!(value, Value::Nil), + AnnotationType::Number => { + matches!(value, Value::Number(_)) + || matches!(value,Value::Object(object) if self.object(*object)?.payload().as_any().is::()) + } + AnnotationType::String => { + matches!(value, Value::Text(_)) + || matches!(value,Value::Object(object) if self.object(*object)?.payload().as_any().is::()) + } + AnnotationType::Bool => matches!(value, Value::Bool(_)), + AnnotationType::Function => { + matches!(value,Value::Object(object) if self.object(*object)?.payload().is_function()) + } + AnnotationType::Type => { + matches!(value,Value::Object(object) if self.object(*object)?.payload().as_any().is::()) + } + AnnotationType::Trait => { + matches!(value,Value::Object(object) if self.object(*object)?.payload().as_any().is::()) + } + AnnotationType::Nominal(definition) => match value { + Value::Object(object) => self.is_instance(*object, definition)?, + _ => false, + }, + AnnotationType::Capability(definition) => match value { + Value::Object(object) => self.supports(*object, definition)?, + _ => false, + }, + }) + } + pub fn value_type_name(&self, value: &Value) -> Result { + Ok(match value { + Value::Nil => "Unit".into(), + Value::Bool(_) => "Bool".into(), + Value::Number(_) => "Number".into(), + Value::Text(_) => "String".into(), + Value::Object(object) => match self.type_of(*object)? { + Some(definition) => self + .payload::(definition, "Type")? + .name + .clone(), + None => self.object(*object)?.payload().kind_name().into(), + }, + }) + } +} diff --git a/docs/README.md b/docs/README.md index aaeed01..1ba7ac9 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 0699b4c..0761366 100644 --- a/docs/contributing/handoff.md +++ b/docs/contributing/handoff.md @@ -21,7 +21,7 @@ Aster 是可嵌入引擎,只保留 objects、core、frontend、compiler。Obje ## 当前缺口 -平台资源版本/依赖锁、源码闭包、数值算术、完整异常传播、内存配额、可靠取消、不可信代码沙箱、自动 GC 和宿主 panic 后执行根恢复尚未完成。类型注解只保存语法,不引入 Python type/元类。UI 接入适配由 Tessera 实现。通用 ObjectPayload::assign_from 允许宿主对象选择共享引用赋值;协议和根计数归 Aster,UI 挂载、布局、绘制仍归 Tessera,见字节码 VM 的共享引用章节。 +平台资源版本/依赖锁、源码闭包、数值算术、完整异常传播、内存配额、可靠取消、不可信代码沙箱、自动 GC 和宿主 panic 后执行根恢复尚未完成。源码函数参数/返回注解已支持默认关闭的运行时检查,Tessera 显式启用;普通名称按调用时函数全局解析,字段/静态类型/效果与原生统一签名仍未完成,见[类型机制](../runtime/types.md#源码函数注解检查)。不引入 Python 元类。UI 接入适配由 Tessera 实现。通用 ObjectPayload::assign_from 允许宿主对象选择共享引用赋值;协议和根计数归 Aster,UI 挂载、布局、绘制仍归 Tessera,见字节码 VM 的共享引用章节。 ## 工作约定 diff --git a/docs/runtime/builtins.md b/docs/runtime/builtins.md index fdbaea6..ca8f8f3 100644 --- a/docs/runtime/builtins.md +++ b/docs/runtime/builtins.md @@ -1,6 +1,6 @@ # Endor 基本对象与统一 Number -这些对象统一定义在独立 [objects 包](../../crates/objects/README.md),属于实现层的数据结构与宿主 API,尚未自动注册到[托管名义类型](types.md),不定义解析器;语言函数已接入 [小型字节码执行器](bytecode.md)。它们通过 `ObjectPayload` 接入现有 `EndorObject`,共同使用对象头、稳定 Box、代际句柄、引用计数和循环回收。不是每种对象重新实现一套堆管理。 +这些对象统一定义在独立 [objects 包](../../crates/objects/README.md),属于实现层的数据结构与宿主 API,通常由宿主显式注册到[托管名义类型](types.md),其中函数支持配置后自动分类,不定义解析器;语言函数已接入 [小型字节码执行器](bytecode.md)。它们通过 `ObjectPayload` 接入现有 `EndorObject`,共同使用对象头、稳定 Box、代际句柄、引用计数和循环回收。不是每种对象重新实现一套堆管理。 ## 对应关系 @@ -45,6 +45,8 @@ fn main() -> Result<(), VmError> { Native 函数和语言函数均通过 `Vm::call(&mut self, ...)` 调用。语言函数用显式帧绑定位置参数、默认值和闭包,再由取指循环执行。Native 回调仍接收借用参数、返回借用对象值,不能在回调里修改当前 VM;VM 会将对象结果 retain 后返回,因此宿主需对 call/execute 的对象结果配对 release_value。 +函数名义分类可由宿主配置 `set_function_type`,源码、原生函数及绑定方法使用同一类型;可调用的类型定义仍保持 Type 身份。配置和源码注解检查的唯一契约见[类型机制](types.md#源码函数注解检查)。 + ## Number:优先表示,不先做重计算 数字统一表示为 `Value::Number(EndorNumber)`。宿主仍可直接保存数字值;需要对象身份时才使用 `vm.new_number(number)` 装箱成 `EndorNumberObject`。 diff --git a/docs/runtime/bytecode.md b/docs/runtime/bytecode.md index 96ca872..ef85929 100644 --- a/docs/runtime/bytecode.md +++ b/docs/runtime/bytecode.md @@ -45,7 +45,7 @@ Endor 是解释型语言,字节码是内部执行表示;从 AST 生成字节 ## 代码对象:保存静态信息 -`EndorCode` 保存 wordcode、解码结果、常量、名称表、局部变量布局、参数与自由变量信息、最大栈深度、代码名和源码位置元信息。它是不可变 Rust 数据。 +`EndorCode` 保存 wordcode、解码结果、常量、名称表、局部变量布局、参数与自由变量信息、最大栈深度、代码名和源码位置元信息。它是不可变 Rust 数据。可选 SourceSignature 还保存形参/返回注解名称和声明类名;Compiler 保留这些数据,宿主可启用[源码函数注解检查](types.md#源码函数注解检查)。 `vm.new_code(code)` 把数据放进 `EndorCodeObject`,分配一个受管理的堆对象。代码对象持有常量中的对象引用,并参与引用计数和循环回收;函数持有代码对象句柄,所以多个函数可共享同一代码对象,代码常量也只由这一对象统一持有。 @@ -116,7 +116,7 @@ ReturnValue ## 对象引用与返回值 -每个帧持有函数的一份执行根,函数继续保活代码、全局字典、内建字典、默认值及闭包。局部槽和求值栈中的每个对象值也各自持有执行根,重复出现分别计数。执行根复用现有外部强引用计数,所以循环回收能保留它们。 +每个帧持有函数的一份执行根,函数继续保活代码、全局字典、内建字典、默认值及闭包。启用源码注解时,帧还持有本次返回约束的 Type/Trait 根,成功或失败清理时释放。局部槽和求值栈中的每个对象值也各自持有执行根,重复出现分别计数。执行根复用现有外部强引用计数,所以循环回收能保留它们。 装载借用值时先 retain,弹栈或清空槽时 release;槽之间移动直接转移持有。调用先准备并保活被调函数的参数,再清理调用者的输入。返回值从当前帧移交给调用者栈或宿主,帧清理不会释放这一份结果。 diff --git a/docs/runtime/compiler.md b/docs/runtime/compiler.md index aeb050d..85e59d5 100644 --- a/docs/runtime/compiler.md +++ b/docs/runtime/compiler.md @@ -11,7 +11,7 @@ | compile_source(&SourceUnit, ParseMode, &CompileOptions) | 先调用 frontend 解析,再编译;错误携带原始文件/流/URI 与修订来源及一基行列 | | CompiledCode::load(&mut Vm) | 将模板及子模板装载为该 VM 的代码对象,返回一个拥有根的 ObjectRef | -解析错误沿用 Lexical/Syntax,名称错误为 Semantic,不支持的执行特性或代码验证错误为 Compilation。编译不修改 VM 或全局字典;词法动作也不执行语言代码。模板提供 instructions/layout/name/filename/first_line/children 只读检查入口。生成时使用已有 EndorCode 验证器检查槽位、操作数和栈平衡;装载再次验证真实常量。 +解析错误沿用 Lexical/Syntax,名称错误为 Semantic,不支持的执行特性或代码验证错误为 Compilation。编译不修改 VM 或全局字典;词法动作也不执行语言代码。模板提供 instructions/layout/name/source_signature/filename/first_line/children 只读检查入口。生成时使用已有 EndorCode 验证器检查槽位、操作数和栈平衡;装载再次验证真实常量。 ## 名称与作用域 @@ -19,7 +19,7 @@ 函数内先收集全部局部名称,再按语句顺序检查初始化。后面声明的 let 会遮蔽整个函数中的同名全局,但提前读取报错;let x = x 也会报未初始化。参数、局部绑定和同一模块的函数不允许在各自作用域内重复声明;参数或局部可以遮蔽模块函数。裸名称赋值 `x = expression` 可在交互全局创建或更新名字;函数内赋值复用已有参数/局部槽,不存在时分配局部槽。赋值右侧先求值再写入,失败不会覆盖该目标;函数内赋值目标仍遮蔽同名全局,首次初始化前读取报错。`let` 的重复声明检查不变。嵌套函数/闭包分析和块级作用域尚未实现。 -CompileOptions.globals 显式声明宿主会提供的 globals/builtins 名称,默认空。不认识的名称在编译时报错;已声明的宿主名称仍必须在执行时存在,否则 VM 返回 NameNotFound。编译器不自动注入 print、默认基础类或标准库,也不读取这些名字的值。类型注解只保留语法,不参与名称绑定、运行时求值或类型检查。 +CompileOptions.globals 显式声明宿主会提供的 globals/builtins 名称,默认空。不认识的名称在编译时报错;已声明的宿主名称仍必须在执行时存在,否则 VM 返回 NameNotFound。编译器不自动注入 print、默认基础类或标准库,也不读取这些名字的值。函数参数/返回注解保存为 SourceSignature,不参与静态名称分析;宿主可启用运行时检查,解析与所有权见[类型机制](types.md#源码函数注解检查)。字段注解仍只保存名称。 ## 指令与代码对象 @@ -38,7 +38,7 @@ CompileOptions.globals 显式声明宿主会提供的 globals/builtins 名称 源码 parser 接受绑定/名称及属性赋值/表达式语句与可选末尾带值 return;空 return 仍仅由 AST 调用方构造。模块初始化发布函数/类并返回 nil,不自动调用 main。Lyra 宿主选择入口属于宿主行为。 -二元算术、非字面量的一元负号和符号值 .name 目前产生明确 Compilation 诊断,不伪造运行结果。导入、类、Agent、条件/循环、关键字参数等仍未实现。函数参数个数和对象是否可调用由 VM 在运行时检查。 +二元算术、非字面量的一元负号和符号值 .name 目前产生明确 Compilation 诊断,不伪造运行结果。导入、Agent、条件/循环、关键字参数等仍未实现。函数参数个数和对象是否可调用由 VM 在运行时检查。 ## 装载、执行与所有权 @@ -57,4 +57,4 @@ lyra run [--entry name] 编译模块、执行初始化、调用无参 可执行文件见 [示例目录](../../crates/compiler/examples/README.md),API/所有权与错误路径见 [pipeline.rs](../../crates/compiler/tests/pipeline.rs),进程入口见 [CLI 检查](https://gitea.entroia.com/Zayden-Jung/endorphin-lyra-cli/src/branch/main/crates/cli/tests/README.md)。引擎和进程入口检查由独立 Lyra CI 分别显式执行;本地只做静态检查,不据此宣称实际执行通过,见 [验证约定](../operations/validation.md)。返回 [运行时索引](README.md)。 -类方法使用显式首参数 self;编译器检查本类重复字段/方法和缺少 self。方法代码仍是共享的普通代码对象,参数类型名目前不实施运行时类型检查。BuildClass 在 VM 中检查继承字段冲突和原生分配边界;构造失败不发布类型到目标全局名。默认基类 Object 须由宿主通过 CompileOptions 声明并在运行全局中提供,编译器不自行引导类型。普通类构造和继承分派见[类型机制](types.md)。 +类方法使用显式首参数 self;编译器检查本类重复字段/方法和缺少 self。方法代码仍是共享的普通代码对象,参数/返回注解和声明类名随代码保存,由宿主开关选择运行时检查。BuildClass 在 VM 中检查继承字段冲突和原生分配边界;构造失败不发布类型到目标全局名。默认基类 Object 须由宿主通过 CompileOptions 声明并在运行全局中提供,编译器不自行引导类型。普通类构造和继承分派见[类型机制](types.md)。 diff --git a/docs/runtime/types.md b/docs/runtime/types.md index b7f4112..3cb8c30 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 的类定义。 ## 创建与关系 @@ -13,7 +13,7 @@ ObjectStore/Vm 仍从空堆启动。宿主显式调用 `bootstrap_types()` 得 `Vm::new_class(name, base, fields, methods)` 为宿主公开与源码相同的类构建校验和所有权协议,返回拥有式定义根;宿主注册普通值类型无需生成源码。 -`new_type(name, base, constructor)` 创建一份新的名义定义,并沿 base 取得元类型。名字相同不代表类型相同。base 在创建时固定,不能通过替换载荷或修改属性改变,因此继承链不会因后续修改出现循环。`attach_type(instance, definition)` 只允许给尚未附加类型的对象设置一次类型,验证同堆、存活状态和 Type 载荷。已有 `type` 普通字段时拒绝附加,避免遮蔽数据。旧的原生对象可以继续保持未分类。 +`new_type(name, base, constructor)` 创建一份新的名义定义,并沿 base 取得元类型。名字相同不代表类型相同。base 在创建时固定,不能通过替换载荷或修改属性改变,因此继承链不会因后续修改出现循环。`attach_type(instance, definition)` 首次附加类型;重复附加同一定义为无操作,改成另一定义被拒绝。首次附加时验证同堆、存活状态和 Type 载荷。已有 `type` 普通字段时拒绝附加,避免遮蔽数据。旧的原生对象可以继续保持未分类。 `type_of` 返回借用式类型引用;`is_subtype(candidate, expected)` 检查名义父链,含自身;`is_instance(instance, expected)` 先取得实例类型,再检查父链。非法句柄和非 Type 参数报错,不返回假值来掩盖错误。这些接口目前面向堆对象,直接保存在 Value 中的标量尚未统一接入。 @@ -56,7 +56,7 @@ class DetailedCounter: Counter { 方法表本身是类型对象拥有的托管边。宿主 define_method 可显式替换条目,不改变类型身份、基类或已有绑定方法选中的函数。bind_attr 返回一份拥有式根;get_attr 返回借用值和未绑定方法,宿主不能把两者的调用/释放协议混用。positional_parameter_count 会扣除绑定的接收者,供宿主判断事件回调的剩余源码参数。 -这是一份实现子集说明:构造器只按字段赋值,没有默认值、命名字段构造或隐式 init;字段/参数/返回注解保留名称,尚未实施类型检查。源码类可继承 Record 或 NativeTyped 分配器;未选择支持继承的旧 Native 分配器仍明确报错。继承会保留分配器和父类字段,不能伪造带原生名义身份的普通 Record。字段构造参数如何与原生参数组合由宿主分配器负责;Tessera 使用原生参数在前、继承及本类字段在后的协议。trait 的正式源码语法、参数/返回类型约束和效果系统仍待实现;宿主显式能力子集见下文。正式语言文档的候选语法不因这一实现被自动确认为规范。 +这是一份实现子集说明:构造器只按字段赋值,没有默认值、命名字段构造或隐式 init;字段注解保留名称但尚未约束字段写入;源码参数/返回注解支持下述宿主可选的运行时检查。源码类可继承 Record 或 NativeTyped 分配器;未选择支持继承的旧 Native 分配器仍明确报错。继承会保留分配器和父类字段,不能伪造带原生名义身份的普通 Record。字段构造参数如何与原生参数组合由宿主分配器负责;Tessera 使用原生参数在前、继承及本类字段在后的协议。trait 的正式源码语法、完整类型兼容性和效果系统仍待实现;宿主显式能力子集见下文。正式语言文档的候选语法不因这一实现被自动确认为规范。 ## 可转移的字段存储 @@ -77,3 +77,25 @@ implement_trait(Type, Trait)明确建立能力关系并校验必需方法。只 建立声明前检查已有后代;构造源码子类检查继承的所有能力。define_method更新前检查受影响后代的显式/继承契约,被后代自己的覆盖遮蔽的方法不受该祖先更新影响。失败保留原方法表和关系。内部扫描只枚举当前借用句柄,不引入全局强类型注册表。声明更新通过普通堆写屏障与trace进行,既有类型的只读作用域内变更会被拒绝。NativeContext也可使用同一implement_trait入口,仍受该屏障约束。 相关回归加入core/tests/types.rs,仅编译,不在本地执行。Tessera提供的Visual/EventTarget/Children/Presentable以及运行时返回值校验属于宿主,Aster不内置GUI类型。 + + +## 源码函数注解检查 + +`Vm::set_source_type_checks(true)` 启用源码函数调用的运行时检查,默认关闭;Tessera 显式启用,其他宿主须自行选择。Compiler 把形参注解、返回注解及可选的声明类名保存为 `SourceSignature`,模板装载后仍保留。宿主也可通过 `EndorCode::with_source_signature` 添加经过形参数量/非空名称校验的元数据;没有该元数据的宿主生成代码继续沿原调用协议执行。 + +每次调用在进入函数体前检查所有实参与用到的默认参数,并解析本次返回约束;`ReturnValue` 在交出结果前检查返回值。错误分别是 `UnknownAnnotation` 或带函数、参数/return、预期及实际名称的 `TypeMismatch`。参数失败不进入函数体;返回失败不回滚此前完成的业务写入。执行帧持有返回约束中实际 Type/Trait 的一份根,直到成功或失败清理;全局名字在调用中改变也不会释放这一约束。约束查询不写函数或类型对象,适用于只读布局/绘制调用。 + +| 注解 | 当前匹配规则 | +| --- | --- | +| Object | 任意合法值,包括 nil | +| Unit | nil | +| Number | 直接 Number 或装箱 EndorNumberObject | +| String/Text | 直接文本或装箱 EndorStringObject | +| Bool | Bool | +| Function | 明确标记为函数的语言函数、原生函数及绑定方法;可调用 Type 不属于 Function | +| Type/Trait | 真实 Type/Trait 定义载荷 | +| 其他名称 | 从函数自己的 globals、其次创建时捕获的 builtins 找到实际 Type 或 Trait;前者检查名义实例/继承,后者检查显式能力 | + +普通名称每次调用重新解析,不是静态推断或定义时冻结的类型绑定。同名新定义不等于旧定义;自由函数可观察后续全局类型名重绑定。方法注解中指向自身声明类的名称,优先从实际接收者祖先中查找仍持有该函数的方法定义,因此 REPL 重定义全局类名后,原实例保存的绑定方法仍可调用。其他类型名仍遵循上述全局解析规则。此机制不提供重写方差、字段赋值检查、泛型、效果类型或原生函数的统一签名元数据。 + +源码与原生函数的统一名义分类由 `ObjectStore::set_function_type`/`Vm::set_function_type` 显式配置。配置验证 Type、拒绝只读修改,只保留弱引用且不可改成另一个定义;设置后新分配的显式 `ObjectPayload::is_function` 载荷自动附加该类型,已有对象不追溯改写。语言函数、两类原生函数及绑定方法提供此标记;仅有 call 能力不算函数。配置类型过期则函数分配报错并清理新对象,不引入隐藏 GC 根。宿主应在创建函数前配置,并通过正常对象边保活定义。