Enforce native type signatures and effects across delegated calls
This commit is contained in:
@@ -4,10 +4,10 @@
|
||||
|
||||
| 文件 | 职责 |
|
||||
| --- | --- |
|
||||
| [lib.rs](lib.rs) | VM 类型导出、objects 类型兼容重导出(含 ReadAccess)、宿主调用与字节码 rustdoc 示例 |
|
||||
| [lib.rs](lib.rs) | VM 类型导出、objects 类型兼容重导出(含 ReadAccess/CallSignature/CallEffect)、宿主调用与字节码 rustdoc 示例 |
|
||||
| [vm.rs](vm.rs) | Vm 拥有 ObjectStore 与错误位置记录;对象操作和宿主 new_class 和显式Trait注册/关系查询显式委托给 objects;函数/字段检查统一开关、统一参数区间查询与函数类型配置 |
|
||||
| [frame.rs](frame.rs) | EndorFrame、栈条目、返回类型执行根、执行位置元数据、ExecutionLimits |
|
||||
| [eval.rs](eval.rs) | execute/call、只读调用与依赖结果、参数绑定、原生实现前数量检查、可选注解检查、源码类字段环境绑定、帧切换、取指分派、MakeFunction、结果根转移与失败清理 |
|
||||
| [frame.rs](frame.rs) | EndorFrame、栈条目、源码与原生委托返回契约执行根、执行位置元数据、ExecutionLimits |
|
||||
| [eval.rs](eval.rs) | execute/call、只读调用与依赖结果、参数绑定、原生实现前数量/类型/效果检查和委托返回检查、可选注解检查、源码类字段环境绑定、帧切换、取指分派、MakeFunction、结果根转移与失败清理 |
|
||||
|
||||
## 常见修改入口
|
||||
|
||||
|
||||
+49
-5
@@ -1,4 +1,4 @@
|
||||
use crate::frame::StackEntry;
|
||||
use crate::frame::{NativeReturn, StackEntry};
|
||||
use crate::{
|
||||
CompareOp, EndorCellObject, EndorCodeObject, EndorFrame, EndorFunctionObject,
|
||||
EndorStringObject, ExecutionLimits, ExecutionLocation, Instruction, ObjectRef, Opcode, Value,
|
||||
@@ -125,6 +125,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn clear_frame(&mut self, frame: EndorFrame) {
|
||||
self.clear_native_returns(frame.native_returns);
|
||||
for entry in frame.stack {
|
||||
if let StackEntry::Value(value) = entry {
|
||||
self.drop_root(value);
|
||||
@@ -204,6 +205,7 @@ impl Vm {
|
||||
}
|
||||
let mut frame = EndorFrame {
|
||||
return_type,
|
||||
native_returns: Vec::new(),
|
||||
function,
|
||||
previous: None,
|
||||
code: data.code(),
|
||||
@@ -273,7 +275,15 @@ impl Vm {
|
||||
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))
|
||||
}
|
||||
@@ -286,6 +296,7 @@ impl Vm {
|
||||
limits: ExecutionLimits,
|
||||
) -> Result<PreparedCall, VmError> {
|
||||
let mut owned = false;
|
||||
let mut returns = Vec::new();
|
||||
let result = (|| loop {
|
||||
if self
|
||||
.object(function)?
|
||||
@@ -293,16 +304,36 @@ impl Vm {
|
||||
.as_any()
|
||||
.is::<EndorFunctionObject>()
|
||||
{
|
||||
return self
|
||||
.prepare_frame(function, &arguments)
|
||||
.map(PreparedCall::Frame);
|
||||
let mut frame = self.prepare_frame(function, &arguments)?;
|
||||
frame.native_returns = std::mem::take(&mut returns);
|
||||
return Ok(PreparedCall::Frame(frame));
|
||||
}
|
||||
if *executed >= limits.max_instructions {
|
||||
return Err(VmError::ExecutionLimit("instructions"));
|
||||
}
|
||||
*executed += 1;
|
||||
if let Some(signature) = self.objects.native_signature(function)?
|
||||
&& !matches!(signature.returns, crate::AnnotationType::Any)
|
||||
{
|
||||
let check = NativeReturn {
|
||||
name: self.object(function)?.payload().kind_name().into(),
|
||||
expected: signature.returns,
|
||||
};
|
||||
if let Some(reference) = check.expected.reference() {
|
||||
self.retain(reference)?;
|
||||
}
|
||||
returns.push(check);
|
||||
}
|
||||
match self.invoke_native(function, &arguments)? {
|
||||
NativeResult::Return(value) => return Ok(PreparedCall::Value(value)),
|
||||
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);
|
||||
}
|
||||
}
|
||||
return Ok(PreparedCall::Value(value));
|
||||
}
|
||||
NativeResult::Invoke {
|
||||
function: next,
|
||||
arguments: values,
|
||||
@@ -319,6 +350,7 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
})();
|
||||
self.clear_native_returns(returns);
|
||||
if owned {
|
||||
self.release(function)
|
||||
.expect("delegated call owns its function");
|
||||
@@ -329,6 +361,15 @@ impl Vm {
|
||||
result
|
||||
}
|
||||
|
||||
fn clear_native_returns(&mut self, returns: Vec<NativeReturn>) {
|
||||
for check in returns {
|
||||
if let Some(reference) = check.expected.reference() {
|
||||
self.release(reference)
|
||||
.expect("native return check owns its type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn stack_value(frame: &EndorFrame, index: usize) -> Result<Value, VmError> {
|
||||
match frame.stack.get(index) {
|
||||
Some(StackEntry::Value(value)) => Ok(value.clone()),
|
||||
@@ -681,6 +722,9 @@ impl Vm {
|
||||
actual: self.objects.value_type_name(&result)?,
|
||||
});
|
||||
}
|
||||
for check in frame.native_returns.iter().rev() {
|
||||
check.check(&self.objects, &result)?;
|
||||
}
|
||||
frame.stack.pop();
|
||||
self.clear_frame(frames.pop().unwrap());
|
||||
if let Some(caller) = frames.last_mut() {
|
||||
|
||||
@@ -10,12 +10,32 @@ pub(crate) enum StackEntry {
|
||||
Value(Value),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct NativeReturn {
|
||||
pub name: String,
|
||||
pub expected: crate::AnnotationType,
|
||||
}
|
||||
impl NativeReturn {
|
||||
pub fn check(&self, store: &objects::ObjectStore, value: &Value) -> Result<(), crate::VmError> {
|
||||
if !store.matches_annotation(value, self.expected)? {
|
||||
return Err(crate::VmError::TypeMismatch {
|
||||
function: self.name.clone(),
|
||||
position: "return".into(),
|
||||
expected: store.annotation_type_name(self.expected)?,
|
||||
actual: store.value_type_name(value)?,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal activation record, analogous to _PyInterpreterFrame. It has no
|
||||
/// EndorObject header; introspection objects can be added independently later.
|
||||
#[derive(Debug)]
|
||||
pub struct EndorFrame {
|
||||
pub(crate) function: ObjectRef,
|
||||
pub(crate) return_type: Option<crate::AnnotationType>,
|
||||
pub(crate) native_returns: Vec<NativeReturn>,
|
||||
/// Index of the caller in the execution's frame vector, never a pointer.
|
||||
pub(crate) previous: Option<usize>,
|
||||
pub(crate) code: ObjectRef,
|
||||
|
||||
@@ -50,12 +50,13 @@ pub use objects::{
|
||||
TypeConstructor,
|
||||
};
|
||||
pub use objects::{
|
||||
CallArity, 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, ReadAccess, Record, SourceSignature, Value, VmError, WeakObjectRef,
|
||||
CallArity, CallEffect, CallSignature, 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, ReadAccess, Record, SourceSignature, Value, VmError,
|
||||
WeakObjectRef,
|
||||
};
|
||||
pub use vm::Vm;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
| [builtins.rs](builtins.rs) | 无损 Number、Unicode 字符串、Dict 顺序与键、Cell、函数环境/共享代码、异常数据 |
|
||||
| [execution.rs](execution.rs) | wordcode 与校验、控制流、参数/默认值、嵌套帧、字节码函数创建/默认值/环境捕获、闭包、返回值存活、失败清理、限额 |
|
||||
| [types.rs](types.rs) | 名义身份、继承与实例关系、类型边保活/循环回收、只读类型元数据与外来句柄、显式Trait身份/继承/参数约束及后代更新原子性;统一函数分类、原生数量元数据/副作用前拒绝/能力更新/默认参数与返回类型错误根清理;仅远端执行 |
|
||||
| [native_context.rs](native_context.rs) | 原生作用域根、分配失败清理、委托、只读结果分配/写入拒绝/依赖/校验与普通读取区分及失败恢复、弱引用过期及有界变更;仅远端执行 |
|
||||
| [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)。
|
||||
|
||||
|
||||
@@ -268,3 +268,352 @@ fn annotation_reads_remain_dependencies_without_becoming_program_content_reads()
|
||||
store.release(object).unwrap();
|
||||
assert_eq!(store.object_count(), 0);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TypedNative {
|
||||
signature: endor_core::CallSignature,
|
||||
calls: std::sync::Arc<std::sync::atomic::AtomicUsize>,
|
||||
delegate: Option<ObjectRef>,
|
||||
allocate_result: bool,
|
||||
}
|
||||
impl NativeCallable for TypedNative {
|
||||
fn signature(&self) -> Option<endor_core::CallSignature> {
|
||||
Some(self.signature.clone())
|
||||
}
|
||||
fn invoke(&self, cx: &mut NativeContext<'_>, args: &[Value]) -> Result<NativeResult, VmError> {
|
||||
self.calls.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
if let Some(function) = self.delegate {
|
||||
return Ok(NativeResult::Invoke {
|
||||
function,
|
||||
arguments: args.to_vec(),
|
||||
});
|
||||
}
|
||||
Ok(NativeResult::Return(if self.allocate_result {
|
||||
Value::Object(cx.new_object()?)
|
||||
} else {
|
||||
args.last().cloned().unwrap_or(Value::Nil)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_types_preflight_arguments_and_clean_wrong_result_roots() {
|
||||
use endor_core::{AnnotationType as T, CallEffect, CallSignature};
|
||||
use std::sync::{
|
||||
Arc,
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
let mut vm = Vm::new().unwrap();
|
||||
let calls = Arc::new(AtomicUsize::new(0));
|
||||
let function = vm
|
||||
.allocate(ContextNativeFunction::new(
|
||||
"typed",
|
||||
TypedNative {
|
||||
signature: CallSignature::exact(vec![T::Number], T::Number, CallEffect::ReadOnly),
|
||||
calls: calls.clone(),
|
||||
delegate: None,
|
||||
allocate_result: true,
|
||||
},
|
||||
vec![],
|
||||
))
|
||||
.unwrap();
|
||||
assert!(
|
||||
matches!(vm.call(function, &[Value::Text("bad".into())]), Err(VmError::TypeMismatch { position, .. }) if position == "parameter 1")
|
||||
);
|
||||
assert_eq!(calls.load(Ordering::SeqCst), 0);
|
||||
assert!(
|
||||
matches!(vm.call(function, &[Value::Number(7.into())]), Err(VmError::TypeMismatch { position, .. }) if position == "return")
|
||||
);
|
||||
assert_eq!(calls.load(Ordering::SeqCst), 1);
|
||||
assert_eq!(vm.object_count(), 1);
|
||||
vm.release(function).unwrap();
|
||||
assert_eq!(vm.object_count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delegated_source_results_obey_every_native_contract_without_extra_frames() {
|
||||
use endor_core::{
|
||||
AnnotationType as T, CallEffect, CallSignature, CodeLayout, EndorCode, Instruction, Opcode,
|
||||
};
|
||||
use std::sync::{Arc, atomic::AtomicUsize};
|
||||
let mut vm = Vm::new().unwrap();
|
||||
let code = EndorCode::from_instructions(
|
||||
&[
|
||||
Instruction::new(Opcode::LoadConst, 0),
|
||||
Instruction::simple(Opcode::ReturnValue),
|
||||
],
|
||||
vec![Value::Text("source result".into())],
|
||||
CodeLayout::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let code = vm.new_code(code).unwrap();
|
||||
let globals = vm.new_dict().unwrap();
|
||||
let source = vm
|
||||
.new_function("source", code, globals, vec![], vec![])
|
||||
.unwrap();
|
||||
let calls = Arc::new(AtomicUsize::new(0));
|
||||
let inner = vm
|
||||
.allocate(ContextNativeFunction::new(
|
||||
"inner",
|
||||
TypedNative {
|
||||
signature: CallSignature::exact(vec![], T::String, CallEffect::ReadOnly),
|
||||
calls: calls.clone(),
|
||||
delegate: Some(source),
|
||||
allocate_result: false,
|
||||
},
|
||||
vec![source],
|
||||
))
|
||||
.unwrap();
|
||||
let outer = vm
|
||||
.allocate(ContextNativeFunction::new(
|
||||
"outer",
|
||||
TypedNative {
|
||||
signature: CallSignature::exact(vec![], T::Number, CallEffect::ReadOnly),
|
||||
calls,
|
||||
delegate: Some(inner),
|
||||
allocate_result: false,
|
||||
},
|
||||
vec![inner],
|
||||
))
|
||||
.unwrap();
|
||||
let limits = ExecutionLimits {
|
||||
max_frames: 1,
|
||||
max_instructions: 100,
|
||||
};
|
||||
assert_eq!(
|
||||
vm.call_with_limits(inner, &[], limits).unwrap(),
|
||||
Value::Text("source result".into())
|
||||
);
|
||||
assert!(
|
||||
matches!(vm.call_with_limits(outer, &[], limits), Err(VmError::TypeMismatch { function, position, .. }) if function == "outer" && position == "return")
|
||||
);
|
||||
assert!(matches!(
|
||||
vm.call_with_limits(
|
||||
outer,
|
||||
&[],
|
||||
ExecutionLimits {
|
||||
max_frames: 0,
|
||||
..limits
|
||||
}
|
||||
),
|
||||
Err(VmError::ExecutionLimit("frames"))
|
||||
));
|
||||
for object in [outer, inner, source, globals, code] {
|
||||
vm.release(object).unwrap();
|
||||
}
|
||||
vm.collect_cycles();
|
||||
assert_eq!(vm.object_count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signatures_trace_nominal_types_and_bound_receivers_use_the_actual_contract() {
|
||||
use endor_core::{AnnotationType as T, CallEffect, CallSignature, TypeConstructor};
|
||||
use std::sync::{
|
||||
Arc,
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
let mut vm = Vm::new().unwrap();
|
||||
let roots = vm.bootstrap_types().unwrap();
|
||||
let definition = vm
|
||||
.new_type("Receiver", roots.object, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
let calls = Arc::new(AtomicUsize::new(0));
|
||||
let function = vm
|
||||
.allocate(ContextNativeFunction::new(
|
||||
"echo",
|
||||
TypedNative {
|
||||
signature: CallSignature::exact(
|
||||
vec![T::Nominal(definition), T::Number],
|
||||
T::Number,
|
||||
CallEffect::ReadOnly,
|
||||
),
|
||||
calls: calls.clone(),
|
||||
delegate: None,
|
||||
allocate_result: false,
|
||||
},
|
||||
vec![],
|
||||
))
|
||||
.unwrap(); // Metadata alone traces the definition.
|
||||
let weak = vm.downgrade(definition).unwrap();
|
||||
vm.define_method(definition, "echo", function).unwrap();
|
||||
let receiver = vm.new_object().unwrap();
|
||||
vm.attach_type(receiver, definition).unwrap();
|
||||
vm.release(definition).unwrap();
|
||||
let Value::Object(bound) = vm.bind_attr(receiver, "echo").unwrap() else {
|
||||
panic!("bound method")
|
||||
};
|
||||
assert_eq!(
|
||||
vm.call_arity(bound).unwrap(),
|
||||
Some(endor_core::CallArity::exact(1))
|
||||
);
|
||||
assert!(vm.call(bound, &[Value::Bool(true)]).is_err());
|
||||
assert_eq!(calls.load(Ordering::SeqCst), 0);
|
||||
assert_eq!(
|
||||
vm.call(bound, &[Value::Number(9.into())]).unwrap(),
|
||||
Value::Number(9.into())
|
||||
);
|
||||
for object in [bound, receiver, function, roots.object, roots.type_type] {
|
||||
vm.release(object).unwrap();
|
||||
}
|
||||
vm.collect_cycles();
|
||||
assert!(vm.upgrade(weak).unwrap().is_none());
|
||||
assert_eq!(vm.object_count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declared_host_effects_are_rejected_before_read_only_dispatch() {
|
||||
use endor_core::{AnnotationType as T, CallEffect, CallSignature};
|
||||
use std::sync::{
|
||||
Arc,
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
for effect in [CallEffect::Mutating, CallEffect::External] {
|
||||
let mut vm = Vm::new().unwrap();
|
||||
let calls = Arc::new(AtomicUsize::new(0));
|
||||
let function = vm
|
||||
.allocate(ContextNativeFunction::new(
|
||||
"effect",
|
||||
TypedNative {
|
||||
signature: CallSignature::exact(vec![], T::Unit, effect),
|
||||
calls: calls.clone(),
|
||||
delegate: None,
|
||||
allocate_result: false,
|
||||
},
|
||||
vec![],
|
||||
))
|
||||
.unwrap();
|
||||
assert!(
|
||||
vm.call_read_only_with_limits(function, &[], ExecutionLimits::default())
|
||||
.is_err()
|
||||
);
|
||||
assert_eq!(calls.load(Ordering::SeqCst), 0);
|
||||
assert_eq!(vm.call(function, &[]).unwrap(), Value::Nil);
|
||||
vm.release(function).unwrap();
|
||||
assert_eq!(vm.object_count(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_native_signatures_check_optional_variadic_types_and_metadata() {
|
||||
use endor_core::{AnnotationType as T, CallArity, CallEffect, CallSignature, NativeFunction};
|
||||
fn echo(args: &[Value]) -> Result<Value, VmError> {
|
||||
Ok(args.last().cloned().unwrap_or(Value::Nil))
|
||||
}
|
||||
let mut vm = Vm::new().unwrap();
|
||||
let function = vm
|
||||
.allocate(
|
||||
NativeFunction::new("legacy", echo).with_signature(CallSignature {
|
||||
arity: CallArity::range(0, usize::MAX),
|
||||
parameters: vec![],
|
||||
variadic: Some(T::Number),
|
||||
returns: T::Number,
|
||||
effect: CallEffect::ReadOnly,
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
vm.call(
|
||||
function,
|
||||
&[Value::Number(1.into()), Value::Number(2.into())]
|
||||
)
|
||||
.unwrap(),
|
||||
Value::Number(2.into())
|
||||
);
|
||||
assert!(vm.call(function, &[Value::Bool(true)]).is_err());
|
||||
assert!(
|
||||
matches!(vm.call(function, &[]), Err(VmError::TypeMismatch { position, .. }) if position == "return")
|
||||
);
|
||||
let invalid = vm
|
||||
.allocate(
|
||||
NativeFunction::new("invalid", echo)
|
||||
.with_signature(CallSignature::exact(
|
||||
vec![T::Number],
|
||||
T::Number,
|
||||
CallEffect::ReadOnly,
|
||||
))
|
||||
.with_arity(CallArity::exact(0)),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(vm.call_arity(invalid).is_err());
|
||||
assert!(vm.call(invalid, &[]).is_err());
|
||||
vm.release(invalid).unwrap();
|
||||
vm.release(function).unwrap();
|
||||
assert_eq!(vm.object_count(), 0);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct EraseThenDelegate {
|
||||
signature: endor_core::CallSignature,
|
||||
target: ObjectRef,
|
||||
}
|
||||
impl NativeCallable for EraseThenDelegate {
|
||||
fn signature(&self) -> Option<endor_core::CallSignature> {
|
||||
Some(self.signature.clone())
|
||||
}
|
||||
fn invoke(&self, cx: &mut NativeContext<'_>, _: &[Value]) -> Result<NativeResult, VmError> {
|
||||
cx.replace_payload(cx.callee(), endor_core::Record)?;
|
||||
Ok(NativeResult::Invoke {
|
||||
function: self.target,
|
||||
arguments: vec![],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delegated_return_type_survives_removal_of_the_original_signature_edges() {
|
||||
use endor_core::{
|
||||
AnnotationType as T, CallEffect, CallSignature, CodeLayout, EndorCode, Instruction, Opcode,
|
||||
TypeConstructor,
|
||||
};
|
||||
let mut vm = Vm::new().unwrap();
|
||||
let roots = vm.bootstrap_types().unwrap();
|
||||
let definition = vm
|
||||
.new_type("Transient", roots.object, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
let weak = vm.downgrade(definition).unwrap();
|
||||
let code = EndorCode::from_instructions(
|
||||
&[
|
||||
Instruction::new(Opcode::LoadConst, 0),
|
||||
Instruction::simple(Opcode::ReturnValue),
|
||||
],
|
||||
vec![Value::Nil],
|
||||
CodeLayout::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let code = vm.new_code(code).unwrap();
|
||||
let globals = vm.new_dict().unwrap();
|
||||
let source = vm
|
||||
.new_function("source", code, globals, vec![], vec![])
|
||||
.unwrap();
|
||||
let wrapper = vm
|
||||
.allocate(ContextNativeFunction::new(
|
||||
"erased",
|
||||
EraseThenDelegate {
|
||||
signature: CallSignature::exact(
|
||||
vec![],
|
||||
T::Nominal(definition),
|
||||
CallEffect::Mutating,
|
||||
),
|
||||
target: source,
|
||||
},
|
||||
vec![source],
|
||||
))
|
||||
.unwrap();
|
||||
vm.release(definition).unwrap();
|
||||
assert!(
|
||||
matches!(vm.call(wrapper, &[]), Err(VmError::TypeMismatch { function, expected, .. }) if function == "erased" && expected == "Transient")
|
||||
);
|
||||
assert!(vm.upgrade(weak).unwrap().is_none());
|
||||
for object in [
|
||||
wrapper,
|
||||
source,
|
||||
code,
|
||||
globals,
|
||||
roots.object,
|
||||
roots.type_type,
|
||||
] {
|
||||
vm.release(object).unwrap();
|
||||
}
|
||||
vm.collect_cycles();
|
||||
assert_eq!(vm.object_count(), 0);
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
| 文件 | 职责 |
|
||||
| --- | --- |
|
||||
| [lib.rs](lib.rs) | 包边界、对象类型/ObjectStore 公开导出(含Trait/MethodRequirement/SourceSignature/AnnotationType/CallArity/ReadAccess)与独立存储 rustdoc 示例 |
|
||||
| [lib.rs](lib.rs) | 包边界、对象类型/ObjectStore 公开导出(含Trait/MethodRequirement/SourceSignature/AnnotationType/CallArity/CallSignature/CallEffect/ReadAccess)与独立存储 rustdoc 示例 |
|
||||
| [object.rs](object.rs) | Value、CompareOp、ObjectPayload(含显式函数标记)、ObjectHeader、EndorObject、Record、可发布数量元数据的 NativeFunction |
|
||||
| [types.rs](types.rs) | 托管名义类型、显式 Object/Type 引导、继承查询、受管方法表/接收者绑定、类字段、显式Trait定义/声明/方法参数约束、原生构造委托、统一源码/原生/绑定方法参数区间查询与能力验证、声明环境托管边、字段构造/写入检查与标记为校验读取的注解解析/匹配 |
|
||||
| [native.rs](native.rs) | NativeContext、CallArity 元数据与调用前数量检查、上下文原生函数、作用域语言函数工厂、显式能力声明入口、函数标记、根与非递归委托调用 |
|
||||
| [native.rs](native.rs) | NativeContext、CallArity/CallSignature 类型与效果元数据、调用前检查、签名类型强边、上下文原生函数、作用域语言函数工厂、显式能力声明入口、函数标记、根与非递归委托调用 |
|
||||
| [changes.rs](changes.rs) | 合并变更批次、变更键与有界通知额度 |
|
||||
| [number.rs](number.rs) | Integer/Decimal/Rational/BinaryFloat/Complex 的无损表示和装箱载荷 |
|
||||
| [builtins.rs](builtins.rs) | String、Dict、Cell、Function、BaseException 载荷与 trace 边、语言函数标记 |
|
||||
|
||||
@@ -35,7 +35,10 @@ pub use code::{CodeLayout, EndorCode, EndorCodeObject, SourceSignature};
|
||||
pub use error::VmError;
|
||||
pub use heap::{ObjectRef, ReadAccess, WeakObjectRef};
|
||||
pub use instruction::{DecodedInstruction, Instruction, Opcode};
|
||||
pub use native::{CallArity, ContextNativeFunction, NativeCallable, NativeContext, NativeResult};
|
||||
pub use native::{
|
||||
CallArity, CallEffect, CallSignature, ContextNativeFunction, NativeCallable, NativeContext,
|
||||
NativeResult,
|
||||
};
|
||||
pub use number::{
|
||||
EndorDecimal, EndorInteger, EndorNumber, EndorNumberObject, EndorRational, EndorReal,
|
||||
};
|
||||
|
||||
@@ -50,10 +50,141 @@ impl CallArity {
|
||||
}
|
||||
}
|
||||
|
||||
/// Declared host effect. ReadOnly may allocate results but must not mutate
|
||||
/// existing objects or perform external I/O. Unknown legacy calls remain unknown.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CallEffect {
|
||||
ReadOnly,
|
||||
Mutating,
|
||||
External,
|
||||
}
|
||||
|
||||
/// Immutable native contract, including an explicit receiver where applicable.
|
||||
/// Missing fixed slots are covered by variadic; optionality comes from arity.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CallSignature {
|
||||
pub arity: CallArity,
|
||||
pub parameters: Vec<crate::AnnotationType>,
|
||||
pub variadic: Option<crate::AnnotationType>,
|
||||
pub returns: crate::AnnotationType,
|
||||
pub effect: CallEffect,
|
||||
}
|
||||
impl CallSignature {
|
||||
pub fn exact(
|
||||
parameters: Vec<crate::AnnotationType>,
|
||||
returns: crate::AnnotationType,
|
||||
effect: CallEffect,
|
||||
) -> Self {
|
||||
Self {
|
||||
arity: CallArity::exact(parameters.len()),
|
||||
parameters,
|
||||
variadic: None,
|
||||
returns,
|
||||
effect,
|
||||
}
|
||||
}
|
||||
pub fn references(&self) -> impl Iterator<Item = ObjectRef> + '_ {
|
||||
self.parameters
|
||||
.iter()
|
||||
.copied()
|
||||
.chain(self.variadic)
|
||||
.chain([self.returns])
|
||||
.filter_map(crate::AnnotationType::reference)
|
||||
}
|
||||
pub fn validate(&self, store: &ObjectStore) -> Result<(), VmError> {
|
||||
self.arity.validate()?;
|
||||
if self.parameters.len() > self.arity.maximum
|
||||
|| (self.variadic.is_none() && self.parameters.len() != self.arity.maximum)
|
||||
{
|
||||
return Err(VmError::InvalidArgument("incomplete native signature"));
|
||||
}
|
||||
for expected in self
|
||||
.parameters
|
||||
.iter()
|
||||
.copied()
|
||||
.chain(self.variadic)
|
||||
.chain([self.returns])
|
||||
{
|
||||
match expected {
|
||||
crate::AnnotationType::Nominal(reference) => {
|
||||
store.payload::<crate::EndorTypeObject>(reference, "Type")?;
|
||||
}
|
||||
crate::AnnotationType::Capability(reference) => {
|
||||
store.payload::<crate::EndorTraitObject>(reference, "Trait")?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn check_arguments(
|
||||
&self,
|
||||
store: &ObjectStore,
|
||||
name: &str,
|
||||
arguments: &[Value],
|
||||
) -> Result<(), VmError> {
|
||||
self.validate(store)?;
|
||||
self.arity.check(name.into(), arguments.len())?;
|
||||
if store.is_read_only() && self.effect != CallEffect::ReadOnly {
|
||||
return Err(VmError::InvalidArgument(
|
||||
"native effect is forbidden during read-only execution",
|
||||
));
|
||||
}
|
||||
for (index, value) in arguments.iter().enumerate() {
|
||||
let expected = self
|
||||
.parameters
|
||||
.get(index)
|
||||
.copied()
|
||||
.or(self.variadic)
|
||||
.ok_or(VmError::InvalidArgument("incomplete native signature"))?;
|
||||
self.check_value(
|
||||
store,
|
||||
name,
|
||||
format!("parameter {}", index + 1),
|
||||
value,
|
||||
expected,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn check_return(
|
||||
&self,
|
||||
store: &ObjectStore,
|
||||
name: &str,
|
||||
value: &Value,
|
||||
) -> Result<(), VmError> {
|
||||
self.check_value(store, name, "return".into(), value, self.returns)
|
||||
}
|
||||
fn check_value(
|
||||
&self,
|
||||
store: &ObjectStore,
|
||||
name: &str,
|
||||
position: String,
|
||||
value: &Value,
|
||||
expected: crate::AnnotationType,
|
||||
) -> Result<(), VmError> {
|
||||
if !store.matches_annotation(value, expected)? {
|
||||
return Err(VmError::TypeMismatch {
|
||||
function: name.into(),
|
||||
position,
|
||||
expected: store.annotation_type_name(expected)?,
|
||||
actual: store.value_type_name(value)?,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NativeCallable: fmt::Debug + Send + Sync + 'static {
|
||||
/// Immutable resolved types; referenced definitions must be traced by the
|
||||
/// payload. ContextNativeFunction snapshots and traces them automatically.
|
||||
fn signature(&self) -> Option<CallSignature> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Optional positional contract. Missing metadata remains an explicit legacy gap.
|
||||
fn arity(&self) -> Option<CallArity> {
|
||||
None
|
||||
self.signature().map(|signature| signature.arity)
|
||||
}
|
||||
|
||||
fn invoke(
|
||||
@@ -197,16 +328,29 @@ impl<'a> NativeContext<'a> {
|
||||
arguments.len(),
|
||||
)?;
|
||||
}
|
||||
let signature = store.native_signature(function)?;
|
||||
let name = store.object(function)?.payload().kind_name().to_owned();
|
||||
if let Some(signature) = &signature {
|
||||
signature.check_arguments(store, &name, arguments)?;
|
||||
}
|
||||
let mut context = Self {
|
||||
store,
|
||||
roots: Vec::new(),
|
||||
callee: function,
|
||||
};
|
||||
context.protect(&Value::Object(function))?;
|
||||
if let Some(signature) = &signature {
|
||||
for reference in signature.references() {
|
||||
context.protect(&Value::Object(reference))?;
|
||||
}
|
||||
}
|
||||
for argument in arguments {
|
||||
context.protect(argument)?;
|
||||
}
|
||||
let result = callable.invoke(&mut context, arguments)?;
|
||||
if let (Some(signature), NativeResult::Return(value)) = (&signature, &result) {
|
||||
signature.check_return(context.store(), &name, value)?;
|
||||
}
|
||||
let transferred_from = context.roots.len();
|
||||
match &result {
|
||||
NativeResult::Return(value) => context.protect(value)?,
|
||||
@@ -242,6 +386,7 @@ pub struct ContextNativeFunction {
|
||||
name: &'static str,
|
||||
callable: Arc<dyn NativeCallable>,
|
||||
captures: Vec<ObjectRef>,
|
||||
signature: Option<CallSignature>,
|
||||
}
|
||||
|
||||
impl ContextNativeFunction {
|
||||
@@ -252,6 +397,7 @@ impl ContextNativeFunction {
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
signature: callable.signature(),
|
||||
callable: Arc::new(callable),
|
||||
captures,
|
||||
}
|
||||
@@ -272,10 +418,18 @@ impl ObjectPayload for ContextNativeFunction {
|
||||
self.name
|
||||
}
|
||||
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
|
||||
if let Some(signature) = &self.signature {
|
||||
for reference in signature.references() {
|
||||
visit(reference);
|
||||
}
|
||||
}
|
||||
for capture in &self.captures {
|
||||
visit(*capture);
|
||||
}
|
||||
}
|
||||
fn call_signature(&self) -> Option<CallSignature> {
|
||||
self.signature.clone()
|
||||
}
|
||||
fn native_callable(&self) -> Option<Arc<dyn NativeCallable>> {
|
||||
Some(self.callable.clone())
|
||||
}
|
||||
|
||||
@@ -118,6 +118,11 @@ pub trait ObjectPayload: Any + fmt::Debug + Send + Sync {
|
||||
/// validates/retains the result before exposing it as an owned call result.
|
||||
/// Language functions are dispatched by Vm's frame evaluator instead.
|
||||
/// Positional metadata for native call protocols; source code is queried by the store.
|
||||
fn call_signature(&self) -> Option<crate::CallSignature> {
|
||||
self.native_callable()
|
||||
.and_then(|callable| callable.signature())
|
||||
}
|
||||
|
||||
fn call_arity(&self) -> Option<crate::CallArity> {
|
||||
self.native_callable().and_then(|callable| callable.arity())
|
||||
}
|
||||
@@ -182,6 +187,7 @@ pub struct EndorNativeFunctionObject {
|
||||
name: String,
|
||||
function: NativeCall,
|
||||
arity: Option<crate::CallArity>,
|
||||
signature: Option<crate::CallSignature>,
|
||||
}
|
||||
|
||||
impl EndorNativeFunctionObject {
|
||||
@@ -190,12 +196,18 @@ impl EndorNativeFunctionObject {
|
||||
name: name.into(),
|
||||
function,
|
||||
arity: None,
|
||||
signature: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
pub fn with_signature(mut self, signature: crate::CallSignature) -> Self {
|
||||
self.arity = Some(signature.arity);
|
||||
self.signature = Some(signature);
|
||||
self
|
||||
}
|
||||
pub fn with_arity(mut self, arity: crate::CallArity) -> Self {
|
||||
self.arity = Some(arity);
|
||||
self
|
||||
@@ -224,6 +236,16 @@ impl ObjectPayload for EndorNativeFunctionObject {
|
||||
Ok(format!("<native-function {}>", self.name))
|
||||
}
|
||||
|
||||
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
|
||||
if let Some(signature) = &self.signature {
|
||||
for reference in signature.references() {
|
||||
visit(reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn call_signature(&self) -> Option<crate::CallSignature> {
|
||||
self.signature.clone()
|
||||
}
|
||||
fn call_arity(&self) -> Option<crate::CallArity> {
|
||||
self.arity
|
||||
}
|
||||
|
||||
@@ -434,6 +434,26 @@ impl ObjectStore {
|
||||
}
|
||||
}
|
||||
|
||||
/// Validated direct native metadata. Bound methods delegate to their actual
|
||||
/// target, so receiver slots remain explicit here. Returned handles are borrowed.
|
||||
pub fn native_signature(
|
||||
&self,
|
||||
function: ObjectRef,
|
||||
) -> Result<Option<crate::CallSignature>, VmError> {
|
||||
let payload = self.object(function)?.payload();
|
||||
let signature = payload.call_signature();
|
||||
if let Some(signature) = &signature {
|
||||
signature.validate(self)?;
|
||||
if payload
|
||||
.call_arity()
|
||||
.is_some_and(|arity| arity != signature.arity)
|
||||
{
|
||||
return Err(VmError::InvalidArgument("native signature arity mismatch"));
|
||||
}
|
||||
}
|
||||
Ok(signature)
|
||||
}
|
||||
|
||||
/// Unified source/native positional interval, after any bound receivers.
|
||||
/// None means no published native metadata (including generic Type construction).
|
||||
pub fn call_arity(&self, function: ObjectRef) -> Result<Option<crate::CallArity>, VmError> {
|
||||
@@ -462,7 +482,9 @@ impl ObjectStore {
|
||||
maximum,
|
||||
))
|
||||
} else {
|
||||
payload.call_arity()
|
||||
self.native_signature(current)?
|
||||
.map(|signature| signature.arity)
|
||||
.or_else(|| payload.call_arity())
|
||||
};
|
||||
return arity
|
||||
.map(|arity| {
|
||||
@@ -1040,6 +1062,26 @@ impl ObjectStore {
|
||||
},
|
||||
})
|
||||
}
|
||||
pub fn annotation_type_name(&self, expected: AnnotationType) -> Result<String, VmError> {
|
||||
Ok(match expected {
|
||||
AnnotationType::Any => "Object".into(),
|
||||
AnnotationType::Unit => "Unit".into(),
|
||||
AnnotationType::Number => "Number".into(),
|
||||
AnnotationType::String => "String".into(),
|
||||
AnnotationType::Bool => "Bool".into(),
|
||||
AnnotationType::Function => "Function".into(),
|
||||
AnnotationType::Type => "Type".into(),
|
||||
AnnotationType::Trait => "Trait".into(),
|
||||
AnnotationType::Nominal(reference) => self
|
||||
.payload::<EndorTypeObject>(reference, "Type")?
|
||||
.name
|
||||
.clone(),
|
||||
AnnotationType::Capability(reference) => self
|
||||
.payload::<EndorTraitObject>(reference, "Trait")?
|
||||
.name
|
||||
.clone(),
|
||||
})
|
||||
}
|
||||
pub fn value_type_name(&self, value: &Value) -> Result<String, VmError> {
|
||||
Ok(match value {
|
||||
Value::Nil => "Unit".into(),
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
| 引擎与宿主边界 | [架构](architecture/README.md) | [crates](../crates/README.md) |
|
||||
| 解释执行、资源修订与引擎升级 | [执行与版本](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/native-calls.md) | objects/src/native.rs、changes.rs、core/src/eval.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 |
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
| --- | --- |
|
||||
| [types.md](types.md) | 托管 Object/Type、名义继承、声明字段/函数注解检查、构造与类型边生命周期 |
|
||||
| [objects.md](objects.md) | 对象、句柄与默认操作 |
|
||||
| [native-calls.md](native-calls.md) | 原生上下文、读取分类、统一参数数量、非递归委托、弱引用、托管赋值与变更批次 |
|
||||
| [native-calls.md](native-calls.md) | 原生上下文、读取分类、统一参数数量/类型/效果、非递归委托返回、弱引用、托管赋值与变更批次 |
|
||||
| [builtins.md](builtins.md) | Number、String、Dict、Cell、Function、异常数据 |
|
||||
| [reader.md](reader.md) | 输入、来源和修订标签 |
|
||||
| [frontend.md](frontend.md) | 词法、语法和诊断 |
|
||||
|
||||
@@ -70,7 +70,7 @@ take_changes 按对象与变更键合并同一批写入,返回最终版本。
|
||||
|
||||
## 统一位置参数数量
|
||||
|
||||
`CallArity { required, maximum }` 表示接受的位置参数区间,包含未绑定的显式接收者;`exact(n)` 与 `range(min,max)` 提供构造方式,`accepts(n)` 查询接受性。`usize::MAX` 表示开放上界。公开字段不能绕过使用时校验:required 大于 maximum 时,查询/调用报告 InvalidArgument,不能进入实现。此元数据只描述数量,不描述参数类型、返回类型或效果。
|
||||
`CallArity { required, maximum }` 表示接受的位置参数区间,包含未绑定的显式接收者;`exact(n)` 与 `range(min,max)` 提供构造方式,`accepts(n)` 查询接受性。`usize::MAX` 表示开放上界。公开字段不能绕过使用时校验:required 大于 maximum 时,查询/调用报告 InvalidArgument,不能进入实现。CallArity 本身只描述数量;可选的 CallSignature 在此基础上声明类型与宿主效果。
|
||||
|
||||
上下文原生实现通过 `NativeCallable::arity()` 提供 `Some(CallArity)`;默认 None 明确表示旧实现没有元数据。ObjectPayload::call_arity 默认委托该入口,也供直接 call 协议发布元数据;传统 NativeFunction 可使用 `with_arity(...)`。VM 在进入原生实现前检查数量,NativeContext::execute 也检查其 NativeCallable 元数据,失败不调用业务实现。传统无元数据的普通原生调用仍保留原协议,不伪造精确签名。
|
||||
|
||||
@@ -86,3 +86,20 @@ Tessera 为原生方法、回调和内部视觉 continuation 发布数量元数
|
||||
只读子区域新增 `ObjectStore::end_read_tracking_details(token) -> Vec<ReadAccess>`,每项仍是借用 ObjectRef,content_read 表示是否存在普通内容读取。matches_annotation 内部的名义/基础值检查标为校验读取;同一区域随后发生普通读取时,content_read 合并为 true。嵌套区域均保留两类读取,不增加对象根。原 end_read_tracking 与完整 end_read_only 仍返回两类读取的完整并集,没有删除依赖或放宽只读写屏障。
|
||||
|
||||
校验分类按线程记录嵌套深度,作用域退出、错误和展开时通过 RAII 恢复,不让另一线程继承标签。公开 ReadAccess 仅提供信息,不保证调用者可忽略任何对象;动态类型/能力/全局定义等校验依赖仍须参与失效。Tessera 独立验证每次活动 Mount/归属/方法后,仅跳过该 Mount 的纯参数类型校验读取;程序实际读取 Mount 内容仍禁止复用测量缓存。其他读取继续记录版本。详见 core/tests/native_context.rs 的合并、完整依赖并集与错误恢复回归源码,仅编译未本地执行。
|
||||
|
||||
|
||||
## 原生参数、返回类型与宿主效果
|
||||
|
||||
CallSignature 保存 CallArity、固定位置的 AnnotationType 列表、可选 variadic 类型、returns 与 CallEffect。exact 提供无默认参数的便捷构造;可选参数仍通过 arity.required/maximum 表示,列表覆盖所有固定位置。开放上界必须有 variadic,有限上界未被固定列表覆盖的部分也必须有 variadic;错误区间、不完整列表、非 Type/Trait 的名义/能力引用和独立 arity 与签名不一致在调用前拒绝。名义类型引用是实际定义,不从字符串或调用者全局环境重新解析。
|
||||
|
||||
NativeCallable::signature 默认 None,明确表示尚未发布类型/效果契约;默认 arity 从签名取得。ObjectPayload::call_signature 供自定义直接调用协议发布。ContextNativeFunction 在创建时保存不可变签名并在 trace 中自动登记每个类型/能力引用;其他捕获仍须显式传 captures。NativeFunction::with_signature 同样追踪签名边并设置数量。自定义载荷须在 trace 中登记自己的签名引用,元数据与其他载荷边一样禁止原地改变;更换使用 replace_payload。ObjectStore::native_signature 校验并返回借用描述,不创建隐藏强注册表。
|
||||
|
||||
已发布的原生签名总是检查,不依赖默认关闭的源码注解开关。所有实参在进入原生实现前匹配类型,失败不产生该实现的业务副作用;可选的未提供参数不检查,实际提供的重复参数逐个匹配。Return 在交付前匹配返回类型,错误释放本次作用域分配和结果根,但不回滚已经执行的业务写入。传统直接 call 结果仍按借用协议处理。NativeContext 的独立执行桥同样执行参数、效果与直接返回检查。
|
||||
|
||||
NativeResult::Invoke 仍是尾委托,不递归进入 Rust/VM。VM 为每层已声明的返回契约保留检查项;委托原生立即返回或 Endor 帧返回时,按由内到外顺序验证全部契约。返回定义作为执行根保留,原生载荷替换、释放原委托者或源码全局重绑定不会使检查丢失。准备失败、帧限额、指令限额、错误返回与正常完成都释放这些执行根。委托不会为检查额外创建 VM 帧;仍共享每次原生分派和 Endor 指令预算。NativeContext::execute 单独返回 Invoke 时,调用者须使用具有等效最终返回检查的求值器;通常直接使用 Vm::call。
|
||||
|
||||
CallEffect::ReadOnly 声明只读现存对象,可分配临时结果;Mutating 与 External 在 ObjectStore 只读作用域内、进入实现前拒绝。普通执行允许这些效果。ReadOnly 声明不绕过已有写屏障,也不证明可信 Rust 不会进行隐藏 I/O;未知旧原生函数仍沿原调用/宿主检查,不能把 None 理解为纯函数。这不是静态效果推断、完整 I/O 沙箱或对阻塞原生代码的抢占。
|
||||
|
||||
Tessera 的 measure/arrange/paint/hitTest、Point 几何查询、Window 方法及可表达的 Block/Container/Page 方法发布签名。需要 nullable/union 参数的 Page.open/window 与 Block.on 尚保留无类型元数据;构造器、其他管理 API 和完整源码覆写方差仍未全部接入。能力注册目前继续检查参数数量,不声称已经比较签名类型或效果。
|
||||
|
||||
回归源码 native_context.rs 覆盖参数副作用前拒绝、错误结果清理、跨原生/源码委托检查、单帧限制、名义签名边与绑定方法循环回收、只读效果拒绝、传统直接函数及重复参数和元数据错误。只编译,不本地执行。
|
||||
|
||||
Reference in New Issue
Block a user