//! 托管事件与订阅的传播实现。Connection 持有回调引用,Event 保存目标、阶段与停止传播状态。 //! 分发沿出现关系执行 capture → target → bubble,记录默认行为阻止状态;只有同一 VM 中的回调可以接触托管对象。 //! //! bootstrap 调用 install 安装 Event/Subscription 控制方法;ui_bridge::calls 和 graphics/scene3d 指针适配调用 dispatch。 //! dispatch 只快照路线并生成驱动,真正回调执行由 VM 调用 Delivery,再通过 Invoke 接回源处理器。 //! vm_sequence::build 使整次传播共用同一执行预算;每次具体 Mount 是独立传播上下文,不从共享业务树猜父链。 use crate::{block, mount::Mount, ui_bridge::attach_desktop_type}; use endor_core::{ ContextNativeFunction, NativeCallable, NativeContext, NativeResult, ObjectPayload, ObjectRef, Value, Vm, VmError, WeakObjectRef, }; // 一次分发最多安排 10000 个处理器,另由共享 VM 驱动约束全部回调的总执行预算。 const MAX_DELIVERIES: usize = 10_000; /// 订阅检查、事件构造与传播分支共用的参数错误构造器,源诊断位置由外层 VM 调用栈提供。 fn invalid(message: &'static str) -> VmError { VmError::InvalidArgument(message) } /// ui_bridge::calls 安装 on/onClick 时调用,检查零参或单 Event 回调能力,不在此执行用户函数。 /// 这里只检查 Function 身份和可接受参数数量;Event 的名义参数注解及实际返回/效果由 VM 真正调用时继续检查。 pub fn validate_handler( store: &endor_core::ObjectStore, function: ObjectRef, ) -> Result<(), VmError> { if !store.object(function)?.payload().is_function() { // 普通 Object 即使有 call 属性也不能作为这里的 Function 注册,避免实际分派才暴露类型错误。 return Err(invalid("event handler must be a Function")); } // 元数据必须明确存在;不能把未知 arity 当作“随便传一个 Event 总能工作”。 let arity = store .call_arity(function)? .ok_or(invalid("event handler requires positional metadata"))?; // 允许无参便捷回调或单 Event 回调;实际递送时优先传 Event(若接受一参),否则零参调用。 if !arity.accepts(0) && !arity.accepts(1) { return Err(invalid( "event handler must accept zero arguments or one Event", )); } Ok(()) } #[derive(Debug, Clone, Default)] /// 本次传播的托管事件状态,包含目标、当前阶段和阻止默认/停止传播控制。 pub struct Event { // 弱引用本轮 guard,检测同一 Event 是否正在分发;驱动释放后 guard 消失,事件可再次使用。 dispatch: Option, // 当前传播组,一般对应路径中的一个出现/阶段;目标捕获与目标非捕获共用一组。 group: usize, // stopPropagation 记下当前组,只阻止后续其他组;同组余下处理器仍执行。 stopped_at: Option, // stopImmediatePropagation 全局跳过后续 Delivery,连当前组剩余处理器也停止。 immediate: bool, // preventDefault 仅记录默认行为是否被阻止;本模块不在这里实现具体的系统默认动作。 prevented: bool, } impl ObjectPayload for Event { /// 声明此 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-event" } /// 声明此 payload 是否允许使用托管属性存储,具体字段仍由类型与调用检查约束。 fn allows_attributes(&self) -> bool { true } } #[derive(Debug, Clone)] /// 实际拥有回调引用的托管连接,挂在目标的订阅关系中参与 GC。 pub struct Connection { // Block 的 subscriptions 强持 Connection,Connection 再强持 handler;取消时将这一强边置空。 pub handler: Option, // 与 Event.kind 比较的精确事件字符串,不对事件名称做前缀或真值匹配。 pub kind: String, // 区分捕获和非捕获队列;目标阶段两种队列仍共享一个传播组。 pub capture: bool, } impl ObjectPayload for Connection { /// 声明此 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-event-connection" } /// 枚举本对象真正拥有的托管强引用,供 Aster 引用追踪和循环回收使用。 fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) { if let Some(handler) = self.handler { // Aster GC 访问者只收到当前仍连接的处理器;None 不再保活旧回调及它捕获的业务对象。 visit(handler); } } } #[derive(Debug)] /// 源代码可保留的订阅能力包装,用于取消对应连接。 pub struct Subscription { // 事件 Connection 或属性 Binding 的弱引用;用户长期保留 token 不应反向保活目标和计算闭包。 pub connection: WeakObjectRef, } impl ObjectPayload for Subscription { /// 声明此 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-subscription" } } #[derive(Debug, Clone, Copy)] /// install 的名义方法表选用的操作标签;TypedMethod 提供类型契约,Method 只执行对应状态变化/查询。 enum Method { Stop, StopImmediate, Prevent, Prevented, Disconnect, Connected, } impl NativeCallable for Method { /// 事件与订阅查询/控制方法无显式参数,只接受隐式 Event 或 Subscription 接收者。 fn arity(&self) -> Option { Some(endor_core::CallArity::exact(1)) } /// TypedMethod::invoke 转交,按 receiver payload 查询/取消订阅,或更新当前事件传播控制标志。 fn invoke(&self, cx: &mut NativeContext<'_>, args: &[Value]) -> Result { // 方法接收者仍在 args[0];不像 UiBridge 的 TypeMethod,这个包装没有提前把 owner 剥出来。 let [Value::Object(owner)] = args else { return Err(invalid("event method receiver")); }; // 取消/查询订阅和传播控制使用不同 payload,先按方法标签分流再进行受检查的向下转型。 let result = match self { Self::Disconnect | Self::Connected => { // Subscription 不直接拥有 Connection,读弱身份后在当前 NativeContext 中临时升级。 let weak = cx .store() .payload::(*owner, "Subscription")? .connection; // 已死亡连接正常得到 None,isConnected=false/disconnect 无动作,而不是新建连接。 let connection = cx.upgrade(weak)?; // Subscription 同时包装事件 Connection 和属性 Binding;按 payload 类型清 handler 或 compute。 let connected = if let Some(connection) = connection { if cx .store() .object(connection)? .payload() .as_any() .is::() { // 属性绑定和事件订阅复用同一种取消令牌,但 Binding 的真实强边字段是 compute。 let mut data = cx .store() .payload::(connection, "Binding")? .clone(); // 返回查询的是操作前连接状态;disconnect 自己最后返回 Unit,不暴露该布尔。 let connected = data.compute.is_some(); // 取消切断真实强拥有边,外部仍可保留 token,但 token 弱引用本身不保活回调或目标。 if matches!(self, Self::Disconnect) { data.compute = None; cx.replace_payload(connection, data)?; } connected } else { // 其余合法令牌包装事件 Connection,不用 Binding 的字段布局读取它。 let mut data = cx .store() .payload::(connection, "Connection")? .clone(); // handler=None 就是已断开,保留 Connection 对象本身不代表回调仍可执行。 let connected = data.handler.is_some(); if matches!(self, Self::Disconnect) { data.handler = None; cx.replace_payload(connection, data)?; } connected } } else { // 弱目标已经死亡与主动断开都属于不再连接,查询不因此报过期对象错误。 false }; if matches!(self, Self::Disconnect) { // API 约定取消返回 Unit,只有 isConnected 返回前面采样到的布尔。 Value::Nil } else { Value::Bool(connected) } } _ => { // 传播标志属于 Event payload,复制后经 replace_payload 写回以保持 store 的写屏障。 let mut event = cx.store().payload::(*owner, "Event")?.clone(); if matches!(self, Self::Prevented) { // 只读查询不替换 payload,不制造一次虚假的事件变更。 Value::Bool(event.prevented) } else { match self { Self::Stop | Self::StopImmediate => { // Stop 保留当前组的其余回调,Immediate 额外跳过同组后续 Delivery。 event.stopped_at = Some(event.group); event.immediate |= matches!(self, Self::StopImmediate); } // 只记录阻止默认,不在本模块额外调用或取消某个 OS 默认动作。 Self::Prevent => event.prevented = true, _ => unreachable!(), } cx.replace_payload(*owner, event)?; Value::Nil } } }; Ok(NativeResult::Return(result)) } } #[derive(Debug)] /// 安装到托管类型上的原生构造调用适配器。 pub struct Constructor { // bootstrap 在 Event 类型/签名都建立后补入,VM 据此先检查 Type/kind/字段外壳。 pub signature: Option, } impl NativeCallable for Constructor { /// 给出参数与返回值的托管类型契约;实际操作在 invoke 中执行。 fn signature(&self) -> Option { self.signature.clone() } /// 至少接收隐式请求 Type 和事件 kind,额外参数留给源子类声明字段。 fn arity(&self) -> Option { Some(endor_core::CallArity::range(2, usize::MAX)) } /// VM 执行 Event/源子类 NativeTyped 构造器时调用,验证字段前不先分配业务 Event,避免非法字段副作用。 fn invoke(&self, cx: &mut NativeContext<'_>, args: &[Value]) -> Result { // definition 是实际请求子类,kind 是唯一原生构造参数,其余后缀对应声明字段。 let [Value::Object(definition), Value::Text(kind), values @ ..] = args else { return Err(invalid("Event requires an event kind")); }; // 事件子类构造先验证额外字段参数数量与注解,再创建默认事件并逐字段初始化。 let fields = cx .store() .payload::(*definition, "Type")? .fields() .to_vec(); if fields.len() != values.len() { // 不用 zip 静默截断少/多实参,完整类型字段数量必须与输入后缀一致。 return Err(invalid("incorrect Event field arguments")); } cx.store() .check_class_field_arguments(*definition, values)?; // 数量和类型都检查后才创建真实子类 Event;NativeContext 会清理未输出的临时根。 let event = new_event(cx, *definition, kind)?; for (field, value) in fields.iter().zip(values) { // 用普通属性写入口初始化字段,保持对象引用和依赖记录受 VM 统一管理。 cx.set_attr(event, &field.name, value.clone())?; } Ok(NativeResult::Return(Value::Object(event))) } } /// Event 构造器、click 和图形/三维指针适配调用,建立尚未分派的带名义身份事件;目标由 dispatch 填写。 pub fn new_event( cx: &mut NativeContext<'_>, definition: ObjectRef, kind: &str, ) -> Result { if kind.is_empty() || kind.len() > 128 { return Err(invalid("event kind must contain 1..128 UTF-8 bytes")); } // payload 默认标志与普通属性分开:停止/默认阻止在 payload,目标/阶段信息通过托管属性公开。 let event = cx.allocate(Event::default())?; cx.attach_type(event, definition)?; cx.set_attr(event, "kind", Value::Text(kind.into()))?; // 新事件尚未递送,目标、出现与 hit 都为空;kind 保留输入种类,phase 初始 none。 for name in ["target", "mount", "currentTarget", "currentMount", "hit"] { // 初始化明确的 nil,处理器读取未分派事件时不会因字段缺失而失败。 cx.set_attr(event, name, Value::Nil)?; } cx.set_attr(event, "phase", Value::Text("none".into()))?; Ok(event) } #[derive(Debug)] /// install 挂到 Event/Subscription 类型上的签名包装;实例 receiver 由 VM 方法绑定机制传入。 struct TypedMethod { // 无状态操作标签,实际事件/订阅状态都在本次传入 receiver 的托管 payload 中。 method: Method, signature: endor_core::CallSignature, } impl NativeCallable for TypedMethod { /// 给出参数与返回值的托管类型契约;实际操作在 invoke 中执行。 fn signature(&self) -> Option { Some(self.signature.clone()) } /// 把已声明签名的调用交给内部 Method,复用事件/订阅控制实现,不创建第二套状态。 fn invoke(&self, cx: &mut NativeContext<'_>, args: &[Value]) -> Result { self.method.invoke(cx, args) } } /// ui_bridge::bootstrap 完成 Event/Subscription/Function 类型建立后调用,为两类对象安装控制与查询方法。 pub fn install( vm: &mut Vm, roots: &mut Vec, event_type: ObjectRef, token_type: ObjectRef, function_type: ObjectRef, ) -> Result<(), VmError> { for (definition, name, method) in [ (event_type, "stopPropagation", Method::Stop), ( event_type, "stopImmediatePropagation", Method::StopImmediate, ), (event_type, "preventDefault", Method::Prevent), (event_type, "isDefaultPrevented", Method::Prevented), (token_type, "disconnect", Method::Disconnect), (token_type, "isConnected", Method::Connected), ] { // isDefaultPrevented/isConnected 返回 Bool 且只读;控制传播和取消方法返回 Unit 并声明 Mutating。 let query = matches!(method, Method::Prevented | Method::Connected); // callable 接一个隐式名义 receiver,查询返回 Bool/ReadOnly,修改返回 Unit/Mutating。 let function = vm.allocate(ContextNativeFunction::new( name, TypedMethod { method, signature: endor_core::CallSignature::exact( vec![endor_core::AnnotationType::Nominal(definition)], if query { // isDefaultPrevented/isConnected 的返回类型是布尔,不把“无连接”写成 nil。 endor_core::AnnotationType::Bool } else { // 控制/取消操作没有业务返回值,VM 用 Unit 检查而非任意 Any。 endor_core::AnnotationType::Unit }, // 同一个标签同时决定调用效果,不能让断开连接冒充只读查询。 if query { endor_core::CallEffect::ReadOnly } else { endor_core::CallEffect::Mutating }, ), }, vec![], ))?; roots.push(function); vm.attach_type(function, function_type)?; vm.define_method(definition, name, function)?; } Ok(()) } #[derive(Debug)] /// 本次递送选择的对象方法或显式订阅处理器。 enum Handler { // 捕获 Connection 对象而非函数快照,前面回调 disconnect 后本项会重新观察 handler=None。 Token(ObjectRef), // onClick 的旧式单处理器是本轮函数快照,不具有独立 Connection 令牌。 Legacy(ObjectRef), // onEvent 从实际类型的方法表在轮到本项时解析,以保留继承/源覆盖。 ObjectMethod, } #[derive(Debug)] /// 事件传播过程中将要调用的一项处理器及其阶段/目标上下文。 struct Delivery { // 本轮事件由 captures 显式保活;payload 中的数值句柄本身不构成 VM 强边。 event: ObjectRef, // 当前路径项的业务对象,不一定是最终 target;祖先捕获/冒泡也用相同适配器。 owner: ObjectRef, // 该路径项的具体出现,页面上下文无 Mount 时为 None。 mount: Option, // stopPropagation 的边界编号;目标捕获与非捕获共享组,其他出现/阶段递增。 group: usize, // 对外公开的 capture/target/bubble 字符串,与 group 的内部停止规则分别保存。 phase: &'static str, // 处理器的解析策略,决定是否需要临时取连接或补 receiver。 handler: Handler, } impl NativeCallable for Delivery { /// 单次递送步骤的 Event/handler/阶段已保存在适配器中,执行时不再接收参数。 fn arity(&self) -> Option { Some(endor_core::CallArity::exact(0)) } /// vm_sequence 驱动轮到本项时调用,重查停止/连接状态,再用 Invoke 交给同预算的真实处理器。 fn invoke(&self, cx: &mut NativeContext<'_>, _: &[Value]) -> Result { // 不是快照整个传播开始时的标志,前一个回调产生的 stop 必须立刻影响当前项。 let mut event = cx.store().payload::(self.event, "Event")?.clone(); // 真正轮到处理器时重新读取传播状态,之前回调的 stop 操作立即影响后续项,无需重建序列。 if event.immediate || event.stopped_at.is_some_and(|group| group != self.group) { // 跳过此项仍返回正常 Nil,让驱动继续推进到其他跳过项和最终 Finish 清理。 return Ok(NativeResult::Return(Value::Nil)); } // 函数与参数前缀一起解析:对象协议需 receiver,独立处理器最初没有显式参数。 let (function, mut arguments) = match self.handler { // 序列只捕获 Connection,不快照 handler;前面处理器取消它后,这里看到 None 并跳过。 Handler::Token(token) => { let Some(handler) = cx .store() .payload::(token, "Connection")? .handler else { // 已断开连接不再调用旧 handler,即使驱动已经为它分配过 Delivery。 return Ok(NativeResult::Return(Value::Nil)); }; (handler, vec![]) } Handler::Legacy(function) => (function, vec![]), // 对象协议在执行到该项时解析实际类型的 onEvent;显式 receiver 先放入实参向量。 Handler::ObjectMethod => { // 动态源子类的 onEvent 可以不同于基类默认空实现,不能在安装时固定函数。 let Some(definition) = cx.store().type_of(self.owner)? else { return Err(invalid("untyped event target")); }; // 按类型继承查方法,缺少协议方法是契约错误,不降级忽略对象事件能力。 let function = cx .store() .lookup_method(definition, "onEvent")? .ok_or(invalid("Block has no onEvent method"))?; (function, vec![Value::Object(self.owner)]) } }; // 调用前更新 currentTarget/currentMount/phase,处理器观察到的是当前路径项,不会误用最终目标。 event.group = self.group; cx.replace_payload(self.event, event)?; cx.set_attr(self.event, "phase", Value::Text(self.phase.into()))?; cx.set_attr(self.event, "currentTarget", Value::Object(self.owner))?; cx.set_attr( self.event, "currentMount", self.mount.map(Value::Object).unwrap_or(Value::Nil), )?; // 对象方法总是携带 Event;显式订阅/旧式回调按 arity 决定是否传 Event。 if matches!(self.handler, Handler::ObjectMethod) || cx .store() .call_arity(function)? .is_some_and(|arity| arity.accepts(1)) { // 单 Event 回调优先;仅真正不接受一参但接受零参的便捷处理器才不追加事件。 arguments.push(Value::Object(self.event)); } Ok(NativeResult::Invoke { function, arguments, }) } } #[derive(Debug)] /// 正常传播结束后清理当前目标、阶段和 guard 标记,返回仍保留最终控制状态的 Event。 struct Finish(ObjectRef); impl NativeCallable for Finish { /// 派发结束步骤已持有 Event 引用,无需显式输入。 fn arity(&self) -> Option { Some(endor_core::CallArity::exact(0)) } /// vm_sequence 在所有 Delivery 之后调用,清理当前阶段/守卫并返回最终 Event,供调用方读取默认阻止状态。 fn invoke(&self, cx: &mut NativeContext<'_>, _: &[Value]) -> Result { // 保留事件最终 prevented 等标志,不用 Event::default 抹掉这一轮处理结果。 // 仅正常走到序列末尾才进入这里;回调报错会中断驱动,届时靠弱 guard 失效解除重入限制,不保证 phase/currentTarget 已清空。 let mut event = cx.store().payload::(self.0, "Event")?.clone(); // 正常序列结束清递送标记与当前上下文,保留原始 target/mount 和 prevented 供调用方读取。 event.dispatch = None; cx.replace_payload(self.0, event)?; cx.set_attr(self.0, "phase", Value::Text("none".into()))?; cx.set_attr(self.0, "currentTarget", Value::Nil)?; cx.set_attr(self.0, "currentMount", Value::Nil)?; Ok(NativeResult::Return(Value::Object(self.0))) } } /// ui_bridge::calls 的 click/dispatch 及指针适配调用,构造本轮路线/处理顺序,再返回 VM 驱动续调。 /// 无 Mount 的代码事件只访问 owner,不猜测共享对象位于哪个窗口;新订阅不加入本轮,取消可立即跳过旧项。 pub fn dispatch( cx: &mut NativeContext<'_>, owner: ObjectRef, event_ref: ObjectRef, mount: Option, desktop: ObjectRef, ) -> Result { // 先检查真实 Event payload,复制是为了稍后重置传播标志并经写屏障写回。 let mut event = cx.store().payload::(event_ref, "Event")?.clone(); // 只有 guard 仍存活才禁止重入;此前分发失败但 guard 已释放时,不让旧弱身份永久锁住 Event。 if let Some(weak) = event.dispatch && cx.upgrade(weak)?.is_some() { // 同 Event 不可重入分发;若上轮驱动已因失败释放,弱 guard 失效则允许重新使用。 return Err(invalid("Event is already being dispatched")); } // kind 可以是源程序改过的普通属性,所以每次 dispatch 都重新验证实际字符串和长度。 let Value::Text(kind) = cx.store().get_attr(event_ref, "kind")? else { return Err(invalid("Event kind must be text")); }; if kind.is_empty() || kind.len() > 128 { return Err(invalid("invalid event kind")); } // 路线由目标向外保存。无 Mount 的代码事件只访问目标对象,不猜测共享 Block 属于哪个出现。 // kind 和候选连接在构建驱动时选定;后续回调改 event.kind 不会重选本轮列表,Connection.handler 是否已取消则由 Delivery 临时重查。 let mut route = vec![(owner, mount)]; if let Some(mount) = mount { // 克隆出现 payload 后沿其 parent/page 弱边构建路径,和 Block.children 的业务组成分开。 let target = cx.store().payload::(mount, "Mount")?.clone(); // 传入 Mount 必须仍挂接且确实展示 owner;同一 Block 的另一个出现不能替代该上下文。 if !target.mounted || target.block != owner { return Err(invalid("event context does not belong to this Block")); } // parent 是下一祖先弱身份,目标先放进 seen,防止父链回到自身。 let mut parent = target.parent; // 沿弱 parent 链构建本轮路径快照,检查死亡祖先、脱离与环;不通过 Block 业务孩子关系反推祖先。 let mut seen = std::collections::HashSet::from([mount]); while let Some(weak) = parent { // NativeContext 跟踪升级根,驱动 captures 将保留真正需要的祖先直到本轮执行结束。 let ancestor = cx .upgrade(weak)? .ok_or(invalid("event route has expired"))?; if !seen.insert(ancestor) { // 循环父链不能让宿主构造无限驱动,遇重复身份立即拒绝。 return Err(invalid("cyclic event route")); } // 祖先存在还不够,必须仍挂载,不能把历史出现关系当成当前传播路径。 let data = cx.store().payload::(ancestor, "Mount")?; if !data.mounted { return Err(invalid("event route is detached")); } route.push((data.block, Some(ancestor))); parent = data.parent; } // 出现祖先之外再加入 Page 上下文,页面本身无 currentMount;弱页已失效则拒绝该旧路线。 if let Some(weak) = target.page { // 页面属于传播最外层语义目标,但没有它自己的内容 Mount,所以 route 中配 None。 let page = cx.upgrade(weak)?.ok_or(invalid("event Page has expired"))?; route.push((page, None)); } } // 所有递送和 Finish 显式捕获 guard,Event 只弱引用它;驱动完成/失败释放后 guard 自动结束本轮资格。 let guard = cx.new_object()?; // 按最终递送顺序存临时 callable,vm_sequence::build 随后让它们成为驱动常量强边。 let mut calls = Vec::new(); // 每个出现/阶段有自己的停止边界;同业务对象多次出现也不能共用一个 group。 let mut group = 0; // 先从最外层向目标递送捕获,再从目标向外递送非捕获;目标两次遍历都标记 phase=target。 let traversal = route .iter() .enumerate() .rev() .map(|(index, entry)| (index, entry, true)) .chain( route .iter() .enumerate() .map(|(index, entry)| (index, entry, false)), ); for (index, &(block, current_mount), capture) in traversal { // block 可能是共享转发别名,解析到实际 Data 后读取本轮回调/订阅列表。 let (_, data) = block::resolve(cx.store(), block)?; // 此路径项的候选处理顺序,稍后逐项包装成独立 Delivery。 let mut handlers = Vec::new(); // 本轮先快照符合事件 kind 与 capture 的订阅顺序;回调期间新注册的连接不进入当前分发。 for token in &data.subscriptions { // subscriptions 保存的是 Connection 而非用户持有的 Subscription 令牌。 let token_data = cx.store().payload::(*token, "Connection")?; if token_data.handler.is_some() && token_data.kind == kind && token_data.capture == capture { // 这里只快照连接身份,handler 的存活在真正执行 Delivery 时再次读取。 handlers.push(Handler::Token(*token)); } } if !capture { // 非捕获阶段 onEvent 先执行,再按订阅顺序,最后兼容旧 click 回调。 handlers.insert(0, Handler::ObjectMethod); if kind == "click" && let Some(handler) = data.click { // 旧式 onClick 仅匹配 click 且只在非捕获阶段执行,不变成所有事件通用监听器。 handlers.push(Handler::Legacy(handler)); } } for handler in handlers { if calls.len() >= MAX_DELIVERIES { // 总量包括所有祖先和阶段,不能让每个节点各用 10000 额度放大一次分发。 return Err(invalid("event delivery budget exceeded")); } // 每一项显式保活本轮 Event、guard、目标和可选 Mount/连接;不能仅靠 Rust ObjectRef 值当 GC 根。 let mut captures = vec![event_ref, guard, block]; if let Some(mount) = current_mount { // captures 保留这一具体出现,不能只保留业务 Block 丢掉几何/祖先上下文。 captures.push(mount); } match handler { // Connection 或旧函数必须有真实托管强边,Rust 枚举里的句柄数值不保活它们。 Handler::Token(token) | Handler::Legacy(token) => captures.push(token), // 对象方法在轮到时查找,owner 已在 captures,不提前强持某个可能被覆盖的函数。 Handler::ObjectMethod => {} } // 分配一个零参步骤;真实用户函数的 receiver/Event 参数由 Delivery::invoke 再决定。 let function = cx.allocate(ContextNativeFunction::new( "event delivery", Delivery { event: event_ref, owner: block, mount: current_mount, group, phase: if index == 0 { // 路线 index=0 始终是最终目标,目标捕获/非捕获都对外显示 target。 "target" } else if capture { // 从外层向目标的祖先遍历显示 capture。 "capture" } else { // 从目标向外层的祖先遍历显示 bubble。 "bubble" }, handler, }, captures, ))?; attach_desktop_type(cx, function, "Function", desktop)?; calls.push(function); } // 目标捕获后不递增 group,使 stopPropagation 仍允许目标非捕获处理器;其他路径/阶段则隔离为新组。 if index != 0 || !capture { group += 1; } } // Finish 也捕获 guard,保证即使没有任何用户监听器,整次驱动仍维持活跃分发资格到结束。 let finish = cx.allocate(ContextNativeFunction::new( "event finish", Finish(event_ref), vec![event_ref, guard], ))?; // 停止传播只让 Delivery 跳过处理器,Finish 仍作为序列最后一项执行;若处理器抛错则整段序列失败,不保证进入 Finish。 calls.push(finish); // 驱动常量池拥有完整顺序,全部处理器共用调用者 VM 的预算,不能逐个 vm.call 重置额度。 let driver = crate::vm_sequence::build(cx, calls, "", desktop)?; event.dispatch = Some(cx.store().downgrade(guard)?); event.group = 0; // 再次使用 Event 时清上一轮停止和默认阻止标志;目标与 Mount 属性也被本轮参数覆盖。 event.stopped_at = None; event.immediate = false; event.prevented = false; cx.replace_payload(event_ref, event)?; cx.set_attr(event_ref, "target", Value::Object(owner))?; cx.set_attr( event_ref, "mount", mount.map(Value::Object).unwrap_or(Value::Nil), )?; // 仅委托驱动,不在本 Rust 调用栈递归执行用户回调;VM 后续负责返回最终 Event。 Ok(NativeResult::Invoke { function: driver, arguments: vec![], }) }