178 lines
6.9 KiB
Rust
178 lines
6.9 KiB
Rust
//! 给源协议返回的对象列表提供受控封装。列表内容通过托管引用被追踪,读方法只暴露检查后的条目。
|
|
//! 用于儿童组成、布局结果和几何输入等需要保留对象身份的场景;不是窗口线程共享的可变容器。
|
|
//!
|
|
//! 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<ObjectRef>);
|
|
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<endor_core::CallSignature> {
|
|
Some(endor_core::CallSignature::exact(
|
|
vec![endor_core::AnnotationType::Number],
|
|
endor_core::AnnotationType::Any,
|
|
endor_core::CallEffect::ReadOnly,
|
|
))
|
|
}
|
|
/// 声明宿主可调用对象接受的参数个数,供 VM 在执行前检查。
|
|
fn arity(&self) -> Option<endor_core::CallArity> {
|
|
Some(endor_core::CallArity::exact(1))
|
|
}
|
|
|
|
/// VM 调入本宿主能力的入口;按所属操作解码参数并返回托管值或后续调用。
|
|
fn invoke(&self, cx: &mut NativeContext<'_>, args: &[Value]) -> Result<NativeResult, VmError> {
|
|
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::<ObjectList>(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<ObjectRef>,
|
|
desktop: ObjectRef,
|
|
) -> Result<ObjectRef, VmError> {
|
|
build(cx, mounts, desktop, None)
|
|
}
|
|
|
|
/// 根据当前输入构造本模块的派生结果,具体的验证与资源边界见函数内步骤。
|
|
fn build(
|
|
cx: &mut NativeContext<'_>,
|
|
mounts: Vec<ObjectRef>,
|
|
desktop: ObjectRef,
|
|
definition: Option<ObjectRef>,
|
|
) -> Result<ObjectRef, VmError> {
|
|
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::<ObjectList>(list, "ObjectList")?.0)
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
/// ObjectList 原生类型的可调用构造器,负责检查传入子对象。
|
|
pub struct Constructor {
|
|
pub signature: Option<endor_core::CallSignature>,
|
|
pub desktop: ObjectRef,
|
|
}
|
|
impl NativeCallable for Constructor {
|
|
/// 给出参数与返回值的托管类型契约;实际操作在 invoke 中执行。
|
|
fn signature(&self) -> Option<endor_core::CallSignature> {
|
|
self.signature.clone()
|
|
}
|
|
/// 声明宿主可调用对象接受的参数个数,供 VM 在执行前检查。
|
|
fn arity(&self) -> Option<endor_core::CallArity> {
|
|
Some(endor_core::CallArity::range(1, usize::MAX))
|
|
}
|
|
|
|
/// VM 调入本宿主能力的入口;按所属操作解码参数并返回托管值或后续调用。
|
|
fn invoke(&self, cx: &mut NativeContext<'_>, args: &[Value]) -> Result<NativeResult, VmError> {
|
|
let [Value::Object(definition), values @ ..] = args else {
|
|
return Err(VmError::InvalidArgument("ObjectList type argument"));
|
|
};
|
|
let fields = cx
|
|
.store()
|
|
.payload::<endor_core::EndorTypeObject>(*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::<Result<Vec<_>, _>>()?;
|
|
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)))
|
|
}
|
|
}
|