Files

274 lines
11 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,
))
}
/// 绑定列表接收者已捕获,只接受一个显式索引。
fn arity(&self) -> Option<endor_core::CallArity> {
Some(endor_core::CallArity::exact(1))
}
/// 要求索引可精确转为非负平台整数,检查列表范围后返回对应托管成员,不复制成员对象。
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"));
};
// 先要求可精确表示为 i64 的整数,再转为 usize,拒绝小数、负数及目标平台越界值。
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)))
}
}
#[derive(Debug)]
struct Edit {
owner: ObjectRef,
desktop: ObjectRef,
remove: bool,
persistent: bool,
extend: bool,
}
impl NativeCallable for Edit {
fn arity(&self) -> Option<endor_core::CallArity> {
Some(endor_core::CallArity::exact(1))
}
fn invoke(&self, cx: &mut NativeContext<'_>, args: &[Value]) -> Result<NativeResult, VmError> {
let mut members = items(cx.store(), self.owner)?.to_vec();
if self.extend {
let [Value::Object(other)] = args else {
return Err(VmError::InvalidArgument("extend expects ObjectList"));
};
let incoming = items(cx.store(), *other)?;
if members.len() + incoming.len() > 10_000 {
return Err(VmError::InvalidArgument(
"object list exceeds 10000 members",
));
}
members.extend_from_slice(incoming);
} else if self.remove {
let [Value::Number(index)] = args else {
return Err(VmError::InvalidArgument("remove expects index"));
};
let index = index
.as_i64()
.and_then(|v| usize::try_from(v).ok())
.filter(|&i| i < members.len())
.ok_or(VmError::InvalidArgument("list index out of range"))?;
members.remove(index);
} else {
let [Value::Object(item)] = args else {
return Err(VmError::InvalidArgument("append expects object"));
};
if members.len() >= 10_000 {
return Err(VmError::InvalidArgument(
"object list exceeds 10000 members",
));
}
members.push(*item);
}
if self.persistent {
return Ok(NativeResult::Return(Value::Object(collection(
cx,
members,
self.desktop,
)?)));
}
let count = members.len();
let first = members
.first()
.copied()
.map(Value::Object)
.unwrap_or(Value::Nil);
cx.replace_payload(self.owner, ObjectList(members))?;
cx.set_attr(self.owner, "count", Value::Number((count as i64).into()))?;
cx.set_attr(self.owner, "first", first)?;
Ok(NativeResult::Return(Value::Nil))
}
}
/// 将已有对象引用包装为新的 ObjectList,并安装默认名义类型及下标查询方法。
pub fn collection(
cx: &mut NativeContext<'_>,
mounts: Vec<ObjectRef>,
desktop: ObjectRef,
) -> Result<ObjectRef, VmError> {
build(cx, mounts, desktop, None)
}
/// 建立列表 payload 和 first/count/at 属性;definition 指定子类时保留该名义类型。
/// 最多接受 10000 个成员;空列表的 first 为 Nil,at 越界仍报错而不返回 Nil。
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",
));
}
// 在移动 Vec 之前提取元信息;成员对象的拥有边由 ObjectList::trace 声明。
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()))?;
// at 捕获列表,列表属性又指向 at,形成托管环;显式 captures 让 GC 能追踪和回收此环。
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))?;
for (name, remove, persistent, extend) in [
("append", false, false, false),
("remove", true, false, false),
("added", false, true, false),
("extend", false, false, true),
] {
let method = cx.allocate(ContextNativeFunction::new(
name,
Edit {
owner: set,
desktop,
remove,
persistent,
extend,
},
vec![set, desktop],
))?;
crate::ui_bridge::attach_desktop_type(cx, method, "Function", desktop)?;
cx.set_attr(set, name, Value::Object(method))?;
}
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()
}
/// 至少包含请求 Type,之后为可变成员对象与源子类字段,具体分段在 invoke 校验。
fn arity(&self) -> Option<endor_core::CallArity> {
Some(endor_core::CallArity::range(1, usize::MAX))
}
/// 按请求类型字段数切分成员/字段,禁止覆盖列表协议保留属性,验证后分配列表并初始化源字段。
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" | "append" | "remove" | "added" | "extend"
)
}) {
return Err(VmError::InvalidArgument("reserved ObjectList field"));
}
// 字段按类型定义顺序占据最后 N 项;checked_sub 在实参不足时返回错误,避免下标下溢。
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)))
}
}