diff --git a/crates/oxide_vm/src/vm/call.rs b/crates/oxide_vm/src/vm/call.rs index 1d35fb1c..67590f69 100644 --- a/crates/oxide_vm/src/vm/call.rs +++ b/crates/oxide_vm/src/vm/call.rs @@ -27,6 +27,7 @@ impl Vm { arg_regs } + /// 判定裸指针是否属于本 session 的 epoch:空指针恒 false。 pub(crate) fn is_session_ptr(&self, obj_ptr: *mut JsObject) -> bool { if obj_ptr.is_null() { return false; @@ -35,12 +36,26 @@ impl Vm { unsafe { (*obj_ptr).is_session_epoch() } } + /// 在当前 epoch arena 分配对象并登记 GC 追踪表,返回裸指针。 pub(crate) fn alloc_object(&mut self, obj: JsObject) -> *mut JsObject { let ptr = self.epoch.alloc(obj); self.gc_state.track_epoch_object(ptr); ptr } + /// native / 字节码函数的统一同步调用入口,返回结果或错误文本。 + /// + /// native 侧超过寄存器窗口(253)的实参转存 spill 溢出区(GC 根),字节码侧 + /// 交同 epoch 的内联执行以避免跨 epoch 指针悬垂。不可调用 callee 抛 + /// TypeError,调用深度达 `max_call_depth` 抛 RangeError。 + /// + /// # 步骤 + /// 1. 保存调用方窗口与溢出区描述,打包实参后调用 native 函数项。 + /// 2. 回拷窗口、截断溢出区并还原描述;`TailCall` 结果递归续走本入口。 + /// + /// # 副作用 + /// - 临时改写调用方寄存器窗口与 `spill_stack`,返回前还原。 + /// - `native_call_depth` 在 native 调用期间 ±1;错误对象写入异常通道。 pub(crate) fn call_function_sync( &mut self, callee: JsValue, receiver: JsValue, args: &[JsValue], ) -> Result { @@ -147,6 +162,24 @@ impl Vm { self.top_level_strict } + /// 字节码函数调用的统一压帧入口。 + /// + /// 按被调函数的表代际解析平表,实参拷入 spill 实参区后回写形参,保存调用方 + /// 窗口并切换到被调模块;非可调用、子模块索引越界或表代际缺失返回错误, + /// 调用深度达 `max_call_depth` 时抛 RangeError。 + /// + /// # 步骤 + /// 1. 校验 callee 可调用且带子模块索引,按表代际取表并检查下标。 + /// 2. 实参写入 spill 实参区(CREATE_ARGUMENTS 源),再从中回写形参。 + /// 3. 绑定 this/new_target,压帧并切换 bytecode / immutables / 表代际。 + /// 4. 按窗口恢复内置镜像槽,重置 pc 与活动寄存器上限。 + /// + /// # 边界与前提 + /// - 形参多于实参补 undefined;实参区与形参写入区重叠时先拷 spill 再回写。 + /// + /// # 副作用 + /// - 压入调用帧与各类保存栈,改写 pc / bytecode / 活动寄存器上限与镜像槽; + /// `continuation` 决定返回后的续行方式。 #[expect(clippy::too_many_arguments)] pub(crate) fn push_bytecode_frame( &mut self, callee: JsValue, this_value: JsValue, args: FrameArgs, construct_result_reg: Option, diff --git a/crates/oxide_vm/src/vm/gc_hooks.rs b/crates/oxide_vm/src/vm/gc_hooks.rs index 3f1f2f83..156576a8 100644 --- a/crates/oxide_vm/src/vm/gc_hooks.rs +++ b/crates/oxide_vm/src/vm/gc_hooks.rs @@ -168,11 +168,15 @@ impl Vm { } } + /// GC 根统一枚举入口:遍历的字段清单与 `rewrite_values` 一一对应。 pub(crate) fn for_each_root(&self, f: impl FnMut(JsValue)) { - // 统一遍历:根收集与指针重写共用同一字段清单。 self.for_each_value(f); } + /// 执行一次 session GC(是否真正回收由 `SessionGc` 的水位门控决定)。 + /// + /// `session_gc` 经 `mem::take` 借出后再放回:收集需要 `&mut Vm`,而 + /// `SessionGc` 是 `Vm` 的内部字段,借出以避开借用冲突。 pub(crate) fn maybe_collect_session_gc(&mut self) { let mut session_gc = std::mem::take(&mut self.gc_state.session_gc); session_gc.maybe_collect(self); diff --git a/crates/oxide_vm/src/vm/mod.rs b/crates/oxide_vm/src/vm/mod.rs index bf8e2d21..30757de1 100644 --- a/crates/oxide_vm/src/vm/mod.rs +++ b/crates/oxide_vm/src/vm/mod.rs @@ -1,3 +1,6 @@ +//! 基于寄存器的 JS 虚拟机:执行状态、寄存器文件、调用栈与 session 内存, +//! 以及同步调用、指令主循环、异常通道、GC 钩子、属性解析等子模块的装配。 + #![allow(clippy::arc_with_non_send_sync)] use std::collections::{HashMap, VecDeque}; @@ -38,6 +41,7 @@ pub use frames::{CallFrame, Completion, ForInIter, FrameContinuation, TryHandler pub(crate) use inline::InlineSyncState; pub(crate) use tables::TableGen; +/// 原型链解析深度上限:防超长或成环的原型链把属性查找拖入无界循环。 pub(crate) const MAX_PROTO_CHAIN_DEPTH: usize = 1024; /// 判定字符串是否为规范数组下标(无前导零的纯数字串),并反解其值。 @@ -129,6 +133,7 @@ fn js_error_kind_name(kind: JsErrorKind) -> &'static str { } } +/// 按 name/msg 拼接错误文本:两者皆非空时为 `name: msg`,任一为空则取另一个。 pub(crate) fn format_error_message(name: &str, msg: &str) -> String { if name.is_empty() { msg.to_string() @@ -371,6 +376,7 @@ impl Vm { &self.session } + /// 判定裸指针是否指向当前 session 的 `%Object.prototype%`。 pub(crate) fn is_object_prototype(&self, ptr: *const JsObject) -> bool { let proto_ptr = self.session.builtin_world().object_proto.as_ptr(); std::ptr::eq(ptr, proto_ptr) diff --git a/crates/oxide_vm/src/vm/props.rs b/crates/oxide_vm/src/vm/props.rs index 77fb9cc3..04ca1f89 100644 --- a/crates/oxide_vm/src/vm/props.rs +++ b/crates/oxide_vm/src/vm/props.rs @@ -13,7 +13,7 @@ use super::{canonical_index_of, canonical_index_units, Vm, MAX_PROTO_CHAIN_DEPTH use crate::vm_trace; impl Vm { - /// 把 `JsValue` 转为属性键 si(`u32`)。 + /// 把 `JsValue` 转为属性键 si(字符串 intern id,`u32`)。 /// /// 非负小整数(含整值 double)直接编码到整数键区间,免 to_string + intern; /// 字符串中形如数组下标的规范数字串(`"5"`)映射到同一整数键,保证 @@ -97,6 +97,7 @@ impl Vm { } } + /// 属性键反解数组下标:整数键直取,字符串键须无前导零且 `u32` 解析成功。 pub(crate) fn array_index_from_property_key(&self, prop_name_si: u32) -> Option { if is_int_key(prop_name_si) { return Some(int_key_value(prop_name_si)); @@ -144,6 +145,11 @@ impl Vm { Ok(false) } + /// 属性读值解析:按数组 length 虚拟属性、元素区、shape 槽、原型链的顺序 + /// 查找,命中即返回,全链 miss 返回 `None`。 + /// + /// 数组元素区的 hole(删除标记)视同不存在;原型链查找止于 + /// `MAX_PROTO_CHAIN_DEPTH` 深度上限。 pub(crate) fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option { vm_trace!("resolve_property: shape_id={} prop_name_si={}", obj.shape_id(), prop_name_si); let length_si = self.length_si; @@ -184,6 +190,9 @@ impl Vm { None } + /// 查找自身属性槽下标:数组 length 虚拟属性返回 `None`,元素区返回下标, + /// shape 槽按数组(元素区之后偏移)与普通对象(槽位即下标)各自定位; + /// 未命中的槽返回 `None`。 pub(crate) fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option { let length_si = self.length_si; if obj.is_array() && prop_name_si == length_si {