docs: 改写虚拟机调度与属性域注释

vm/mod.rs:补模块头,说明基于寄存器的执行状态、寄存器文件、调用栈
与 session 内存及子模块职责;为 MAX_PROTO_CHAIN_DEPTH、
format_error_message、is_object_prototype 补文档。
vm/call.rs:为 is_session_ptr、alloc_object 补文档;为
call_function_sync、push_bytecode_frame 补五段式文档,写清 native/字节码
统一同步调用与统一压帧的步骤、边界和副作用。
vm/gc_hooks.rs:为 for_each_root、maybe_collect_session_gc 补文档。
vm/props.rs:property_key_si 首现展开字符串 intern id;为
array_index_from_property_key、resolve_property、get_own_property_slot
补文档。
This commit is contained in:
2026-09-17 13:29:22 +00:00
parent 5e9cd271e1
commit 38d710cf04
4 changed files with 54 additions and 2 deletions
+33
View File
@@ -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<JsValue, String> {
@@ -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<u8>,
+5 -1
View File
@@ -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);
+6
View File
@@ -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)
+10 -1
View File
@@ -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<u32> {
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<JsValue> {
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<u32> {
let length_si = self.length_si;
if obj.is_array() && prop_name_si == length_si {