From 56df4e5b35decd15c601bb3246018e38af0f22d7 Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Sun, 15 Nov 2026 17:53:34 +0800 Subject: [PATCH] docs(core): explain native dispatch and delegated return contracts --- crates/core/src/eval.rs | 46 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/crates/core/src/eval.rs b/crates/core/src/eval.rs index 992ad83..b8af5be 100644 --- a/crates/core/src/eval.rs +++ b/crates/core/src/eval.rs @@ -858,15 +858,19 @@ impl Vm { Ok(frame) } + /// prepare_call 分派非源码载荷:上下文回调交 NativeContext,旧式回调自行验证并保留返回根。 fn invoke_native( &mut self, function: ObjectRef, arguments: &[Value], ) -> Result { + // 先验证当前函数句柄,不让失效/异堆身份进入原生协议。 self.object(function)?; + // 参数起初是借用,仅验证有效性;此循环本身不取得拥有根。 for argument in arguments { self.validate_value(argument)?; } + // 对象首参可能是方法接收者,普通覆盖和显式能力检查不可只靠原生数量约束。 if let Some(Value::Object(receiver)) = arguments.first() { self.objects.check_method_contract_call( function, @@ -874,6 +878,7 @@ impl Vm { self.source_type_checks, )?; } + // 已发布的数量区间必须满足,缺少元数据不在此处猜测数量。 if let Some(arity) = self.objects.call_arity(function)? && !arity.accepts(arguments.len()) { @@ -884,22 +889,30 @@ impl Vm { provided: arguments.len(), }); } + // 克隆到的分派器 Arc 不借用载荷,可在 NativeContext 中安全可变访问 Store。 if let Some(callable) = self.object(function)?.payload().native_callable() { return NativeContext::execute(&mut self.objects, function, arguments, callable); } + // 无上下文旧式协议读取完整签名;有签名才检查参数/效果及结果。 let signature = self.objects.native_signature(function)?; + // 复制诊断类别名,不在执行回调后依赖之前的载荷借用。 let name = self.object(function)?.payload().kind_name().to_owned(); + // 完整签名存在才执行相应参数或返回检查,缺失时维持旧协议行为。 if let Some(signature) = &signature { signature.check_arguments(&self.objects, &name, arguments)?; } + // 旧式回调返回借用值,不能立即作为拥有结果交给宿主。 let result = self.object(function)?.payload().call(arguments)?; + // 完整签名存在才执行相应参数或返回检查,缺失时维持旧协议行为。 if let Some(signature) = &signature { signature.check_return(&self.objects, &name, &result)?; } + // 验证并取得结果根,即使回调原样返回某个参数,也必须给结果独立所有权。 self.retain_value(&result)?; Ok(NativeResult::Return(result)) } + /// 同步调用、只读续执行、协程及 Call 指令共用准备循环,原生委托不递归进入求值器。 fn prepare_call( &mut self, mut function: ObjectRef, @@ -907,42 +920,59 @@ impl Vm { executed: &mut usize, limits: ExecutionLimits, ) -> Result { + // 最初函数/实参借用;第一次 Invoke 后才接管 NativeContext 移交的函数/参数根。 let mut owned = false; + // 累计原生/能力返回约束,后续委托每一层完成都仍须满足这些要求。 let mut returns = Vec::new(); + // 闭包把委托循环的提前错误汇总,外层统一清理尚未移交的约束与输入根。 let result = (|| loop { + // 源码载荷交给 prepare_frame,不走原生调用协议或 Rust 递归执行。 if self .object(function)? .payload() .as_any() .is::() { + // 源码帧取得自己的函数/参数根,后面归还委托输入根不会使帧失效。 let mut frame = self.prepare_frame(function, &arguments)?; + // 暂取帧本地能力返回约束,将外层原生委托约束放在前面。 let mut local = std::mem::take(&mut frame.native_returns); + // 把累计约束所有权移入源码帧,外层 returns 变空,避免统一清理重复归还。 frame.native_returns = std::mem::take(&mut returns); + // 本地约束追加在外层之后,最终返回时按反序检查。 frame.native_returns.append(&mut local); return Ok(PreparedCall::Frame(frame)); } + // 每次原生/委托分派前检查累计硬预算,避免无限原生委托链绕过指令限制。 if *executed >= limits.max_instructions { return Err(VmError::ExecutionLimit("instructions")); } *executed += 1; + // 非 Any 返回约束需要跨委托保留,不能只校验中间包装器直接返回值。 if let Some(signature) = self.objects.native_signature(function)? && !signature.returns.is_any() { + // 诊断名与解析后的返回类型一并保存,不在最终返回时重查可能被重绑定的全局。 let check = NativeReturn { name: self.object(function)?.payload().kind_name().into(), expected: signature.returns, }; self.push_native_return(&mut returns, check)?; } + // 对象首参可能是方法接收者,普通覆盖和显式能力检查不可只靠原生数量约束。 if let Some(Value::Object(receiver)) = arguments.first() { + // 有效接收者的全部能力返回约束同样跨委托保留。 for (name, expected) in self.objects.method_trait_returns(function, *receiver)? { self.push_native_return(&mut returns, NativeReturn { name, expected })?; } } + // 结果已经拥有:Return 的结果根或 Invoke 的函数/各参数根由本循环接管。 match self.invoke_native(function, &arguments)? { + // 最终直接结果要通过委托链所有返回检查后才可交付。 NativeResult::Return(value) => { + // 从最内层到外层校验,每项约束都必须满足。 for check in returns.iter().rev() { + // 检查失败先归还本次结果根,再让外层清理链上剩余资源。 if let Err(error) = check.check(&self.objects, &value) { self.drop_root(value); return Err(error); @@ -954,22 +984,29 @@ impl Vm { function: next, arguments: values, } => { + // 只有已接管 Invoke 输入时才归还当前函数/实参,初始借用输入不能释放。 if owned { self.release(function)?; + // 先清空旧拥有参数列表并逐项归还,随后安装新委托输出。 for value in arguments.drain(..) { self.release_value(value)?; } } + // 新委托函数/参数根已经由 NativeContext 保留,赋值只转交清理责任。 function = next; arguments = values; + // 从此循环退出时须统一归还当前委托输入;返回结果或源码帧各自已保活。 owned = true; } } })(); + // 直接完成/错误清理仍在本地的约束,已移入源码帧的那部分不再在此表中。 self.clear_native_returns(returns); + // 只有已接管 Invoke 输入时才归还当前函数/实参,初始借用输入不能释放。 if owned { self.release(function) .expect("delegated call owns its function"); + // 循环结束时归还最后一批拥有参数,重复对象参数逐项计数。 for value in arguments { self.drop_root(value); } @@ -977,14 +1014,19 @@ impl Vm { result } + /// 委托链保留返回约束定义根,全部 retain 成功后才追加检查项,中途失败回滚已保留部分。 fn push_native_return( &mut self, returns: &mut Vec, check: NativeReturn, ) -> Result<(), VmError> { + // 先记成功 retain 的定义身份,未全部成功前不提交检查项。 let mut retained = Vec::new(); + // 枚举联合返回类型内全部定义引用,重复引用不能去重。 for reference in check.expected.references() { + // 后续定义根失败时,归还此前成功取得的根,不留下半安装约束。 if let Err(error) = self.retain(reference) { + // 回滚只处理本次取得的根,不触碰原签名载荷拥有边。 for reference in retained { self.release(reference)?; } @@ -992,11 +1034,15 @@ impl Vm { } retained.push(reference); } + // 全部定义根已取得,追加检查项后由返回链/帧负责清理。 returns.push(check); Ok(()) } + /// 委托结束/帧销毁统一归还返回约束定义根,每个引用按拥有次数独立 release。 fn clear_native_returns(&mut self, returns: Vec) { + // 销毁每份检查项持有的定义根,Rust CallType 的 drop 不会自动 release。 for check in returns { + // 枚举联合返回类型内全部定义引用,重复引用不能去重。 for reference in check.expected.references() { self.release(reference) .expect("native return check owns its type");