From 5e9cd271e1ef70e2e7ba2a47c1f55d7a62b50469 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Thu, 17 Sep 2026 13:22:18 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=94=B9=E5=86=99=E8=99=9A=E6=8B=9F?= =?UTF-8?q?=E6=9C=BA=E6=A0=B8=E5=BF=83=E5=9F=9F=E6=B3=A8=E9=87=8A=E5=B9=B6?= =?UTF-8?q?=E6=B8=85=E9=99=A4=E8=8B=B1=E6=96=87=E6=AE=8B=E7=95=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - error.rs:补 coerce_primitive_bounded / coerce_number/int32/uint32_bounded / checked_object_ptr / raise_error_kind / raise_js_error / raise_type_error / error_message_text / thrown_error_kind 共 10 项文档;@@toPrimitive 英文块译中。 - dispatch_loop.rs:补 dispatch 主循环五段式文档。 - vmhost.rs:property_key_si trait 臂英文块译中(Reflect/Object 不传播键转换异常)。 --- crates/oxide_vm/src/vm/dispatch_loop.rs | 28 +++++++++++++ crates/oxide_vm/src/vm/error.rs | 53 +++++++++++++++++++++++-- crates/oxide_vm/src/vm/vmhost.rs | 6 +-- 3 files changed, 81 insertions(+), 6 deletions(-) diff --git a/crates/oxide_vm/src/vm/dispatch_loop.rs b/crates/oxide_vm/src/vm/dispatch_loop.rs index 7421948b..f0a19d17 100644 --- a/crates/oxide_vm/src/vm/dispatch_loop.rs +++ b/crates/oxide_vm/src/vm/dispatch_loop.rs @@ -55,6 +55,34 @@ macro_rules! binary_arith { } impl Vm { + /// 主 dispatch 循环:逐指令解码并分派到各 `dispatch_*` 处理器,直到返回、 + /// 异常逃出或触发步数/分配上限。 + /// + /// # 约定 + /// - 完成值:HALT 与 RETURN 类指令把结果写回寄存器(顶层承载于 `regs[0]`), + /// 循环以 `Ok(value)` 返回。 + /// - 异常:可捕获异常经 `unwind` 改写 pc/寄存器后就地继续;无 handler 时 + /// 返回 `Err`,异常值由 `last_uncaught_value` 上交调用方。 + /// - 寄存器镜像槽允许裸读:热点路径直接索引 `self.regs` 取模块 builtin 名集 + /// 槽,槽值仅在 run/帧入口重载,进入循环前须保证已重载。 + /// + /// # 步骤 + /// 1. 循环前缓存 `max_steps`/`max_alloc_bytes`,嵌套重入按 hop 计数兜底采样。 + /// 2. 指令边界采执行期 GC 安全点(仅顶层 `native_call_depth == 0`)。 + /// 3. 检查步数上限、单 run 分配上限与 pc 越界,超限按错误返回。 + /// 4. 解码当前指令、递增 pc,按 OpCode 分派到处理器。 + /// + /// # 边界与前提 + /// - 嵌套 dispatch(原生 builtin 重入执行 JS)不推进顶层 `steps`,靠重入 + /// hop 计数每 64 跳强制一次轻采样兜底分配上限。 + /// - `pc` 越过 `bytecode.len()` 视为内部错误,返回 `Err`(不抛 JS 异常)。 + /// + /// # 副作用 + /// - 改写 `pc`、寄存器、帧栈与 GC 状态;可能触发字符串/两档 session 收集。 + /// + /// # 注意事项 + /// - GC 安全点不得下移到嵌套 dispatch:调用方寄存器窗口副本存于 inline + /// 状态、非 GC 根,此时回收会把调用方 regs 中的活值当死值释放。 pub(crate) fn dispatch(&mut self) -> Result { // config.max_steps / max_alloc_bytes 逐指令只读且循环内不变:提到循环外, // 免每次经 kernel_core Arc 指针追寻读取(热点内仅有的 config 访问)。 diff --git a/crates/oxide_vm/src/vm/error.rs b/crates/oxide_vm/src/vm/error.rs index 94991d90..756e640a 100644 --- a/crates/oxide_vm/src/vm/error.rs +++ b/crates/oxide_vm/src/vm/error.rs @@ -11,6 +11,30 @@ use super::{format_error_message, js_error_kind, js_error_kind_name, Vm}; use crate::vm_debug; impl Vm { + /// ToPrimitive 有界版:按规范序把对象转为原始值,失败统一抛 `TypeError`。 + /// + /// 对象自身的 `Symbol.toPrimitive` 优先;不存在时按 `prefer_string` 定序尝试 + /// `toString`/`valueOf`,等价于 OrdinaryToPrimitive。 + /// + /// # 步骤 + /// 1. 非对象或空指针原样返回。 + /// 2. 读 `@@toPrimitive`:存在则必须可调用(否则抛 `TypeError`),以 + /// `"string"`/`"number"` hint 调用;返回对象再抛 `TypeError`。 + /// 3. 回退方法族:按 hint 顺序试 `toString`/`valueOf`,不可调用者跳过, + /// 首个返回非对象的结果即采用。 + /// 4. 两条路径都不产出原始值时抛 `TypeError`。 + /// + /// # 边界与前提 + /// - `prefer_string` 为 `true` 时先试 `toString`,否则先试 `valueOf`。 + /// - 方法调用经 `call_function_sync`;调用错误转 `raise_call_error` 恢复 + /// 原始异常。 + /// + /// # 副作用 + /// - 执行用户代码(getter / 方法调用),可能抛异常并触发 GC 安全点。 + /// + /// # 注意事项 + /// - 主 dispatch 下就地展开异常;原生 builtin 内部只返回格式化 `Err`, + /// 由其调用边界恢复(见 `conversion_error`)。 pub(crate) fn coerce_primitive_bounded(&mut self, value: JsValue, prefer_string: bool) -> Result { if !value.is_object() { return Ok(value); @@ -21,9 +45,9 @@ impl Vm { return Ok(value); } - // ECMA-262 §7.1.1 step 1: an exotic obj[Symbol.toPrimitive] takes precedence - // over OrdinaryToPrimitive. well-known symbol 键经 property_key_si 映射为 - // 固定 Symbol 键,读键路径与写键路径一致。 + // ECMA-262 §7.1.1 step 1:exotic 对象自身的 Symbol.toPrimitive 优先于 + // OrdinaryToPrimitive。well-known symbol 键经 property_key_si 映射为固定 + // Symbol 键,读键路径与写键路径一致。 let sym_key = { let sym_ptr = self.session.builtin_world().sym_to_primitive.as_ptr() as *mut JsObject; JsValue::from_js_object(sym_ptr) @@ -124,11 +148,13 @@ impl Vm { Err(err.to_string()) } + /// ToNumber 有界版:先按 number hint 做 ToPrimitive,再转 `f64`。 pub(crate) fn coerce_number_bounded(&mut self, value: JsValue) -> Result { let primitive = self.coerce_primitive_bounded(value, false)?; Ok(coercion::to_number(primitive)) } + /// ToInt32 有界版:整数直通;其余先 ToNumber,再取模 2^32 后按符号回卷。 pub(crate) fn coerce_int32_bounded(&mut self, value: JsValue) -> Result { if value.is_int() { return Ok(value.as_int()); @@ -145,6 +171,7 @@ impl Vm { } } + /// ToUint32 有界版:整数直通;其余先 ToNumber,再取模 2^32。 pub(crate) fn coerce_uint32_bounded(&mut self, value: JsValue) -> Result { if value.is_int() { return Ok(value.as_int() as u32); @@ -156,6 +183,13 @@ impl Vm { Ok(n.trunc().rem_euclid(4_294_967_296.0) as u32) } + /// 校验 `JsValue` 为合法非空对象指针,成功时返回对象裸指针。 + /// + /// # 边界与前提 + /// - 非对象、空指针、地址低于 `0x10000` 或未按 `JsObject` 对齐:抛 + /// `TypeError`(消息由调用方给出)并返回 `Ok(None)`,调用方据此短路 + /// (错误已进异常通道,后续由 dispatch 继续展开)。 + /// - 本函数不做语义检查(可调用/可构造等能力由调用方判定)。 pub(crate) fn checked_object_ptr( &mut self, val: JsValue, error_msg: &str, ) -> Result, String> { @@ -172,10 +206,17 @@ impl Vm { Ok(Some(ptr)) } + /// 按 kind 名构造 `JsError` 并转 `raise_js_error` 抛出。 pub(crate) fn raise_error_kind(&mut self, kind: &'static str, msg: &str) -> Result<(), String> { self.raise_js_error(JsError::new(js_error_kind(kind), msg)) } + /// 构造错误对象、写入异常通道并展开到最近的 try 处理器。 + /// + /// - `exception_value` 存错误对象、`pending_error_kind` 存 kind 名,供外围 + /// try/catch 与错误通道查询。 + /// - 展开失败(无 handler)时 `unwind` 返回 `Err`,异常逃出 run 边界经 + /// `last_uncaught_value` 上交调用方。 pub(crate) fn raise_js_error(&mut self, err: JsError) -> Result<(), String> { let kind = js_error_kind_name(err.kind); vm_debug!("raise_js_error: {} \"{}\"", kind, err.message); @@ -185,14 +226,20 @@ impl Vm { self.unwind() } + /// 抛 `TypeError` 的便捷入口,等价于以 kind 名 `"TypeError"` 调 + /// `raise_error_kind`。 pub(crate) fn raise_type_error(&mut self, msg: &str) -> Result<(), String> { self.raise_error_kind("TypeError", msg) } + /// 格式化错误文本,供原生 builtin 调用边界恢复异常时使用。 pub(crate) fn error_message_text(&self, kind: &str, msg: &str) -> String { format_error_message(kind, msg) } + /// 从异常对象读 `name` 属性并映射为错误 kind 名。 + /// + /// 非对象、无 `name`、name 非字符串或未知名称一律回退 `"Error"`。 pub(crate) fn thrown_error_kind(&self, val: JsValue) -> &'static str { if !val.is_object() { return "Error"; diff --git a/crates/oxide_vm/src/vm/vmhost.rs b/crates/oxide_vm/src/vm/vmhost.rs index d612a588..fd8265b2 100644 --- a/crates/oxide_vm/src/vm/vmhost.rs +++ b/crates/oxide_vm/src/vm/vmhost.rs @@ -53,9 +53,9 @@ impl oxide_runtime_api::VmHost for Vm { self.last_uncaught_value = value; } fn property_key_si(&mut self, val: JsValue) -> u32 { - // Object-key conversion (to_string_full) failures degrade to the empty key here: - // this trait path is used by Reflect/Object builtins; computed property access - // uses the inherent Result-returning version to preserve the full exception. + // 对象键转换(to_string_full)失败时降级为空键:本 trait 路径供 + // Reflect/Object 内置使用,这些内置按规范不传播键转换异常;计算属性 + // 访问走返回 Result 的 inherent 版本,转换异常原样保留。 self.property_key_si(val) .unwrap_or_else(|_| self.kernel_core.perm_interner().intern("").0) }