//! 给源协议返回的对象列表提供受控封装。列表内容通过托管引用被追踪,读方法只暴露检查后的条目。 //! 用于儿童组成、布局结果和几何输入等需要保留对象身份的场景;不是窗口线程共享的可变容器。 //! //! Managed indexed host results; values and the at method are traced edges. use endor_core::{ ContextNativeFunction, NativeCallable, NativeContext, NativeResult, ObjectPayload, ObjectRef, Value, VmError, }; #[derive(Debug)] /// 托管对象序列,trace 负责拥有其中的对象引用;源代码通过受检查方法读取。 struct ObjectList(Vec); impl ObjectPayload for ObjectList { /// 声明此 payload 的赋值遵循引用身份语义,避免把内部宿主状态当作可复制值。 fn reference_assignment_only(&self) -> bool { true } /// 提供受检查的 Any 下转型入口,让宿主按具体 payload 类型读取数据。 fn as_any(&self) -> &dyn std::any::Any { self } /// 返回运行时诊断/类型检查使用的稳定 payload 名称。 fn kind_name(&self) -> &'static str { "tessera-object-list" } /// 声明此 payload 是否允许使用托管属性存储,具体字段仍由类型与调用检查约束。 fn allows_attributes(&self) -> bool { true } /// 枚举本对象真正拥有的托管强引用,供 Aster 引用追踪和循环回收使用。 fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) { for mount in &self.0 { visit(*mount); } } } #[derive(Debug)] /// 绑定到某个列表对象的下标访问适配器。 struct At(ObjectRef); impl NativeCallable for At { /// 给出参数与返回值的托管类型契约;实际操作在 invoke 中执行。 fn signature(&self) -> Option { Some(endor_core::CallSignature::exact( vec![endor_core::AnnotationType::Number], endor_core::AnnotationType::Any, endor_core::CallEffect::ReadOnly, )) } /// 声明宿主可调用对象接受的参数个数,供 VM 在执行前检查。 fn arity(&self) -> Option { Some(endor_core::CallArity::exact(1)) } /// VM 调入本宿主能力的入口;按所属操作解码参数并返回托管值或后续调用。 fn invoke(&self, cx: &mut NativeContext<'_>, args: &[Value]) -> Result { let [Value::Number(index)] = args else { return Err(VmError::InvalidArgument("object list at expects an index")); }; let index = index .as_i64() .and_then(|v| usize::try_from(v).ok()) .ok_or(VmError::InvalidArgument("invalid list index"))?; let mount = *cx .store() .payload::(self.0, "Object list")? .0 .get(index) .ok_or(VmError::InvalidArgument("list index out of range"))?; Ok(NativeResult::Return(Value::Object(mount))) } } /// 将已有对象引用包装为新的 ObjectList,并安装默认名义类型及下标查询方法。 pub fn collection( cx: &mut NativeContext<'_>, mounts: Vec, desktop: ObjectRef, ) -> Result { build(cx, mounts, desktop, None) } /// 根据当前输入构造本模块的派生结果,具体的验证与资源边界见函数内步骤。 fn build( cx: &mut NativeContext<'_>, mounts: Vec, desktop: ObjectRef, definition: Option, ) -> Result { if mounts.len() > 10_000 { return Err(VmError::InvalidArgument( "object list exceeds 10000 members", )); } let first = mounts .first() .copied() .map(Value::Object) .unwrap_or(Value::Nil); let count = mounts.len(); let set = cx.allocate(ObjectList(mounts))?; if let Some(definition) = definition { cx.attach_type(set, definition)?; } else { crate::ui_bridge::attach_desktop_type(cx, set, "ObjectList", desktop)?; } cx.set_attr(set, "first", first)?; cx.set_attr(set, "count", Value::Number((count as i64).into()))?; let at = cx.allocate(ContextNativeFunction::new("at", At(set), vec![set]))?; crate::ui_bridge::attach_desktop_type(cx, at, "Function", desktop)?; cx.set_attr(set, "at", Value::Object(at))?; Ok(set) } /// 借用受检查列表的条目;返回值的有效期仍依赖原托管对象存活。 pub fn items(store: &endor_core::ObjectStore, list: ObjectRef) -> Result<&[ObjectRef], VmError> { Ok(&store.payload::(list, "ObjectList")?.0) } #[derive(Debug)] /// ObjectList 原生类型的可调用构造器,负责检查传入子对象。 pub struct Constructor { pub signature: Option, pub desktop: ObjectRef, } impl NativeCallable for Constructor { /// 给出参数与返回值的托管类型契约;实际操作在 invoke 中执行。 fn signature(&self) -> Option { self.signature.clone() } /// 声明宿主可调用对象接受的参数个数,供 VM 在执行前检查。 fn arity(&self) -> Option { Some(endor_core::CallArity::range(1, usize::MAX)) } /// VM 调入本宿主能力的入口;按所属操作解码参数并返回托管值或后续调用。 fn invoke(&self, cx: &mut NativeContext<'_>, args: &[Value]) -> Result { let [Value::Object(definition), values @ ..] = args else { return Err(VmError::InvalidArgument("ObjectList type argument")); }; let fields = cx .store() .payload::(*definition, "Type")? .fields() .to_vec(); if fields .iter() .any(|field| matches!(field.name.as_str(), "count" | "first" | "at")) { return Err(VmError::InvalidArgument("reserved ObjectList field")); } let count = values .len() .checked_sub(fields.len()) .ok_or(VmError::InvalidArgument( "missing ObjectList subclass fields", ))?; cx.store() .check_class_field_arguments(*definition, &values[count..])?; let members = values[..count] .iter() .map(|value| { if let Value::Object(object) = value { Ok(*object) } else { Err(VmError::InvalidArgument( "ObjectList members must be objects", )) } }) .collect::, _>>()?; let list = build(cx, members, self.desktop, Some(*definition))?; for (field, value) in fields.iter().zip(&values[count..]) { cx.set_attr(list, &field.name, value.clone())?; } Ok(NativeResult::Return(Value::Object(list))) } }