Add explicit managed traits and inherited method arity contracts
This commit is contained in:
@@ -4,19 +4,19 @@
|
||||
|
||||
| 文件 | 职责 |
|
||||
| --- | --- |
|
||||
| [lib.rs](lib.rs) | 包边界、对象类型/ObjectStore 公开导出与独立存储 rustdoc 示例 |
|
||||
| [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 引导、继承查询、受管方法表/接收者绑定、类字段与原生构造委托 |
|
||||
| [native.rs](native.rs) | NativeContext、上下文原生函数、作用域语言函数工厂、根与非递归委托调用 |
|
||||
| [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、引用事务、迭代释放、循环回收、只读变更屏障/全调用及嵌套子调用读取跟踪及私有边界测试 |
|
||||
| [heap.rs](heap.rs) | 私有 Heap、不可伪造 ObjectRef、稳定 Box、引用事务、迭代释放、循环回收、后代契约更新的私有借用枚举、只读变更屏障/全调用及嵌套子调用读取跟踪及私有边界测试 |
|
||||
| [store.rs](store.rs) | ObjectStore、空堆创建、对象操作、retain/release 和回收入口 |
|
||||
| [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,避免反向依赖 core |
|
||||
| [error.rs](error.rs) | 对象、代码与执行共用的 VmError(含能力/方法定位的TraitViolation),避免反向依赖 core |
|
||||
|
||||
对象操作变更同步 [对象模型](../../../docs/runtime/objects.md),内置载荷同步 [基本对象](../../../docs/runtime/builtins.md)。新增操作码需要同时修改本包 instruction.rs/code.rs 和 core 的 eval.rs 分派,见 [字节码 VM](../../../docs/runtime/bytecode.md)。
|
||||
|
||||
|
||||
@@ -13,6 +13,11 @@ pub enum VmError {
|
||||
UnsupportedOperation(&'static str),
|
||||
InvalidArgument(&'static str),
|
||||
ObjectKindMismatch(&'static str),
|
||||
TraitViolation {
|
||||
capability: String,
|
||||
method: String,
|
||||
reason: &'static str,
|
||||
},
|
||||
EmptyCell,
|
||||
InvalidBytecode {
|
||||
offset: usize,
|
||||
@@ -43,6 +48,11 @@ impl fmt::Display for VmError {
|
||||
}
|
||||
Self::InvalidArgument(message) => f.write_str(message),
|
||||
Self::ObjectKindMismatch(expected) => write!(f, "expected {expected} object"),
|
||||
Self::TraitViolation {
|
||||
capability,
|
||||
method,
|
||||
reason,
|
||||
} => write!(f, "trait {capability} method {method}: {reason}"),
|
||||
Self::EmptyCell => f.write_str("cell is empty"),
|
||||
Self::InvalidBytecode { offset, reason } => {
|
||||
write!(f, "invalid bytecode at code unit {offset}: {reason}")
|
||||
|
||||
@@ -69,6 +69,16 @@ impl Heap {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn live_references(&self) -> impl Iterator<Item = ObjectRef> + '_ {
|
||||
self.slots.iter().enumerate().filter_map(|(index, slot)| {
|
||||
slot.object.as_ref().map(|_| ObjectRef {
|
||||
heap_id: self.id,
|
||||
index,
|
||||
generation: slot.generation,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn begin_read_only(&mut self) -> usize {
|
||||
let protected = self
|
||||
.slots
|
||||
|
||||
@@ -44,4 +44,6 @@ pub use object::{
|
||||
ObjectHeader, ObjectPayload, Record, Value,
|
||||
};
|
||||
pub use store::ObjectStore;
|
||||
pub use types::{ClassField, EndorTypeObject, RootTypes, TypeConstructor};
|
||||
pub use types::{
|
||||
ClassField, EndorTraitObject, EndorTypeObject, MethodRequirement, RootTypes, TypeConstructor,
|
||||
};
|
||||
|
||||
@@ -44,6 +44,14 @@ impl<'a> NativeContext<'a> {
|
||||
self.store.attach_type(object, type_object)
|
||||
}
|
||||
|
||||
pub fn implement_trait(
|
||||
&mut self,
|
||||
definition: ObjectRef,
|
||||
capability: ObjectRef,
|
||||
) -> Result<(), VmError> {
|
||||
self.store.implement_trait(definition, capability)
|
||||
}
|
||||
|
||||
pub fn store(&self) -> &ObjectStore {
|
||||
self.store
|
||||
}
|
||||
|
||||
@@ -77,6 +77,16 @@ impl ObjectStore {
|
||||
"replace immutable type definition",
|
||||
));
|
||||
}
|
||||
if self
|
||||
.object(reference)?
|
||||
.payload()
|
||||
.as_any()
|
||||
.is::<crate::EndorTraitObject>()
|
||||
{
|
||||
return Err(VmError::UnsupportedOperation(
|
||||
"replace immutable trait definition",
|
||||
));
|
||||
}
|
||||
self.validate_builtin(&payload)?;
|
||||
self.heap.replace_payload(reference, payload)
|
||||
}
|
||||
|
||||
+225
-2
@@ -28,13 +28,14 @@ pub struct ClassField {
|
||||
/// Nominal definition with immutable identity/base and a managed method table.
|
||||
/// Its base, constructor and metatype are traced
|
||||
/// edges; no registry outside the heap keeps unused definitions alive.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EndorTypeObject {
|
||||
name: String,
|
||||
base: Option<ObjectRef>,
|
||||
constructor: TypeConstructor,
|
||||
methods: BTreeMap<String, ObjectRef>,
|
||||
fields: Vec<ClassField>,
|
||||
traits: Vec<ObjectRef>,
|
||||
}
|
||||
|
||||
impl EndorTypeObject {
|
||||
@@ -68,6 +69,9 @@ impl ObjectPayload for EndorTypeObject {
|
||||
{
|
||||
visit(function);
|
||||
}
|
||||
for capability in &self.traits {
|
||||
visit(*capability);
|
||||
}
|
||||
for method in self.methods.values() {
|
||||
visit(*method);
|
||||
}
|
||||
@@ -145,6 +149,7 @@ impl ObjectStore {
|
||||
constructor: TypeConstructor::Record,
|
||||
methods: BTreeMap::new(),
|
||||
fields: Vec::new(),
|
||||
traits: Vec::new(),
|
||||
})?;
|
||||
let mut allocated_type = None;
|
||||
let result = (|| {
|
||||
@@ -154,6 +159,7 @@ impl ObjectStore {
|
||||
constructor: TypeConstructor::Abstract,
|
||||
methods: BTreeMap::new(),
|
||||
fields: Vec::new(),
|
||||
traits: Vec::new(),
|
||||
})?;
|
||||
allocated_type = Some(type_type);
|
||||
self.attach_type(object, type_type)?;
|
||||
@@ -194,6 +200,7 @@ impl ObjectStore {
|
||||
constructor,
|
||||
methods: BTreeMap::new(),
|
||||
fields,
|
||||
traits: Vec::new(),
|
||||
})?;
|
||||
if let Err(error) = self.attach_type(definition, metatype) {
|
||||
self.release(definition)?;
|
||||
@@ -269,6 +276,7 @@ impl ObjectStore {
|
||||
));
|
||||
}
|
||||
let mut methods = definition.methods.clone();
|
||||
self.validate_method_update(type_object, &name, function)?;
|
||||
methods.insert(name, function);
|
||||
let updated = EndorTypeObject {
|
||||
name: definition.name.clone(),
|
||||
@@ -276,6 +284,7 @@ impl ObjectStore {
|
||||
constructor: definition.constructor,
|
||||
methods,
|
||||
fields: definition.fields.clone(),
|
||||
traits: definition.traits.clone(),
|
||||
};
|
||||
self.heap.replace_payload(type_object, updated)
|
||||
}
|
||||
@@ -331,8 +340,12 @@ impl ObjectStore {
|
||||
constructor,
|
||||
methods,
|
||||
fields,
|
||||
traits: Vec::new(),
|
||||
})?;
|
||||
if let Err(error) = self.attach_type(class, metatype) {
|
||||
if let Err(error) = self
|
||||
.validate_inherited_traits(class)
|
||||
.and_then(|()| self.attach_type(class, metatype))
|
||||
{
|
||||
self.release(class)?;
|
||||
return Err(error);
|
||||
}
|
||||
@@ -417,6 +430,11 @@ impl ObjectStore {
|
||||
{
|
||||
return Ok(AttributeLookup::Value(Value::Object(type_object)));
|
||||
}
|
||||
if name == "name"
|
||||
&& let Some(capability) = object.payload().as_any().downcast_ref::<EndorTraitObject>()
|
||||
{
|
||||
return Ok(AttributeLookup::Value(Value::Text(capability.name.clone())));
|
||||
}
|
||||
if let Some(definition) = object.payload().as_any().downcast_ref::<EndorTypeObject>() {
|
||||
let metadata = match name {
|
||||
"name" => Some(Value::Text(definition.name.clone())),
|
||||
@@ -503,3 +521,208 @@ impl NativeCallable for CallBoundMethod {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Minimal explicit capability contract. Arity excludes the bound receiver.
|
||||
/// Parameter/return types, default implementations and syntax are host concerns.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MethodRequirement {
|
||||
pub name: String,
|
||||
pub parameters: usize,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct EndorTraitObject {
|
||||
name: String,
|
||||
requirements: Vec<MethodRequirement>,
|
||||
}
|
||||
impl EndorTraitObject {
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
pub fn requirements(&self) -> &[MethodRequirement] {
|
||||
&self.requirements
|
||||
}
|
||||
}
|
||||
impl ObjectPayload for EndorTraitObject {
|
||||
fn reference_assignment_only(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn kind_name(&self) -> &'static str {
|
||||
"trait"
|
||||
}
|
||||
fn repr(&self, _: ObjectRef) -> Result<String, VmError> {
|
||||
Ok(format!("<trait {}>", self.name))
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectStore {
|
||||
pub fn new_trait(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
requirements: Vec<MethodRequirement>,
|
||||
) -> Result<ObjectRef, VmError> {
|
||||
let name = name.into();
|
||||
let mut names = std::collections::BTreeSet::new();
|
||||
if name.is_empty() {
|
||||
return Err(VmError::InvalidArgument("trait name must not be empty"));
|
||||
}
|
||||
for requirement in &requirements {
|
||||
if requirement.name.is_empty()
|
||||
|| requirement.name == "type"
|
||||
|| requirement.parameters == usize::MAX
|
||||
|| !names.insert(&requirement.name)
|
||||
{
|
||||
return Err(VmError::InvalidArgument(
|
||||
"invalid or duplicate trait requirement",
|
||||
));
|
||||
}
|
||||
}
|
||||
self.allocate(EndorTraitObject { name, requirements })
|
||||
}
|
||||
|
||||
fn inherited_traits(&self, definition: ObjectRef) -> Result<Vec<ObjectRef>, VmError> {
|
||||
let mut found = std::collections::BTreeSet::new();
|
||||
let mut current = Some(definition);
|
||||
while let Some(definition) = current {
|
||||
let definition = self.payload::<EndorTypeObject>(definition, "Type")?;
|
||||
found.extend(definition.traits.iter().copied());
|
||||
current = definition.base;
|
||||
}
|
||||
Ok(found.into_iter().collect())
|
||||
}
|
||||
fn validate_requirement(
|
||||
&self,
|
||||
capability: ObjectRef,
|
||||
requirement: &MethodRequirement,
|
||||
function: Option<ObjectRef>,
|
||||
) -> Result<(), VmError> {
|
||||
let invalid = |reason| VmError::TraitViolation {
|
||||
capability: self
|
||||
.payload::<EndorTraitObject>(capability, "Trait")
|
||||
.expect("validated trait")
|
||||
.name
|
||||
.clone(),
|
||||
method: requirement.name.clone(),
|
||||
reason,
|
||||
};
|
||||
let function = function.ok_or_else(|| invalid("required method is missing"))?;
|
||||
if let Some(count) = self.positional_parameter_count(function)? {
|
||||
if count != requirement.parameters + 1 {
|
||||
return Err(invalid(
|
||||
"positional parameter count does not match (including self)",
|
||||
));
|
||||
}
|
||||
} else if self.object(function)?.payload().native_callable().is_none() {
|
||||
return Err(invalid("required member is not callable"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn validate_trait(&self, definition: ObjectRef, capability: ObjectRef) -> Result<(), VmError> {
|
||||
let contract = self.payload::<EndorTraitObject>(capability, "Trait")?;
|
||||
for requirement in &contract.requirements {
|
||||
self.validate_requirement(
|
||||
capability,
|
||||
requirement,
|
||||
self.lookup_method(definition, &requirement.name)?,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn validate_inherited_traits(&self, definition: ObjectRef) -> Result<(), VmError> {
|
||||
for capability in self.inherited_traits(definition)? {
|
||||
self.validate_trait(definition, capability)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
// Enumerate borrowed heap handles only while validating a definition update;
|
||||
// no global type registry or owning descendant index is introduced.
|
||||
fn descendant_types(&self, base: ObjectRef) -> Result<Vec<ObjectRef>, VmError> {
|
||||
self.payload::<EndorTypeObject>(base, "Type")?;
|
||||
let mut found = vec![];
|
||||
for reference in self.heap.live_references() {
|
||||
if self
|
||||
.object(reference)?
|
||||
.payload()
|
||||
.as_any()
|
||||
.is::<EndorTypeObject>()
|
||||
&& self.is_subtype(reference, base)?
|
||||
{
|
||||
found.push(reference);
|
||||
}
|
||||
}
|
||||
Ok(found)
|
||||
}
|
||||
fn validate_method_update(
|
||||
&self,
|
||||
changed: ObjectRef,
|
||||
name: &str,
|
||||
function: ObjectRef,
|
||||
) -> Result<(), VmError> {
|
||||
for descendant in self.descendant_types(changed)? {
|
||||
let mut current = Some(descendant);
|
||||
let mut affected = false;
|
||||
while let Some(definition) = current {
|
||||
if definition == changed {
|
||||
affected = true;
|
||||
break;
|
||||
}
|
||||
let definition = self.payload::<EndorTypeObject>(definition, "Type")?;
|
||||
if definition.methods.contains_key(name) {
|
||||
break;
|
||||
}
|
||||
current = definition.base;
|
||||
}
|
||||
if !affected {
|
||||
continue;
|
||||
}
|
||||
for capability in self.inherited_traits(descendant)? {
|
||||
for requirement in &self
|
||||
.payload::<EndorTraitObject>(capability, "Trait")?
|
||||
.requirements
|
||||
{
|
||||
if requirement.name == name {
|
||||
self.validate_requirement(capability, requirement, Some(function))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn implement_trait(
|
||||
&mut self,
|
||||
definition: ObjectRef,
|
||||
capability: ObjectRef,
|
||||
) -> Result<(), VmError> {
|
||||
self.payload::<EndorTraitObject>(capability, "Trait")?;
|
||||
for descendant in self.descendant_types(definition)? {
|
||||
self.validate_trait(descendant, capability)?;
|
||||
}
|
||||
let mut updated = self.payload::<EndorTypeObject>(definition, "Type")?.clone();
|
||||
if !updated.traits.contains(&capability) {
|
||||
updated.traits.push(capability);
|
||||
self.heap.replace_payload(definition, updated)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn implements_trait(
|
||||
&self,
|
||||
definition: ObjectRef,
|
||||
capability: ObjectRef,
|
||||
) -> Result<bool, VmError> {
|
||||
self.payload::<EndorTraitObject>(capability, "Trait")?;
|
||||
let declared = self.inherited_traits(definition)?.contains(&capability);
|
||||
if declared {
|
||||
self.validate_trait(definition, capability)?;
|
||||
}
|
||||
Ok(declared)
|
||||
}
|
||||
pub fn supports(&self, object: ObjectRef, capability: ObjectRef) -> Result<bool, VmError> {
|
||||
self.payload::<EndorTraitObject>(capability, "Trait")?;
|
||||
match self.type_of(object)? {
|
||||
Some(definition) => self.implements_trait(definition, capability),
|
||||
None => Ok(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user