docs: 改写虚拟机核心域注释并清除英文残留

- 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 不传播键转换异常)。
This commit is contained in:
2026-09-17 13:22:18 +00:00
parent fa7a3bd2f3
commit 5e9cd271e1
3 changed files with 81 additions and 6 deletions
+28
View File
@@ -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<JsValue, String> {
// config.max_steps / max_alloc_bytes 逐指令只读且循环内不变:提到循环外,
// 免每次经 kernel_core Arc 指针追寻读取(热点内仅有的 config 访问)。
+50 -3
View File
@@ -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<JsValue, String> {
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<f64, String> {
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<i32, String> {
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<u32, String> {
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<Option<*mut JsObject>, 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";
+3 -3
View File
@@ -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)
}