mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
docs: 补齐虚拟机运行时与调用绑定注释
This commit is contained in:
@@ -104,6 +104,14 @@ macro_rules! bind_constructor {
|
||||
}};
|
||||
}
|
||||
|
||||
/// 给既有构造器对象安装 native 函数项指针、形参个数与构造器类型标签。
|
||||
///
|
||||
/// 绑定模块构造器注册的公共步骤:只更新既有对象的函数元数据,不新建对象、不写属性表。
|
||||
/// 函数项指针经 `NativeFnPtr::from_raw` 存入。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - `native_fn` 须为调用方转成 `*const ()` 的合法 `NativeFn` 函数项指针。
|
||||
/// - 不设置 `length` 属性:需要 Function.length 的调用方自行补写。
|
||||
pub(crate) fn configure_native_constructor(ctor: &mut JsObject, native_fn: *const (), arg_count: u8) {
|
||||
// SAFETY: native_fn 始终是调用方转成 *const () 的合法 NativeFn 函数项指针。
|
||||
ctor.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(native_fn) }));
|
||||
@@ -117,6 +125,15 @@ fn configure_existing_ctor(ctor: &P<JsObject>, native_fn: *const (), arg_count:
|
||||
configure_native_constructor(ctor, native_fn, arg_count);
|
||||
}
|
||||
|
||||
/// 把 `(name, native_fn_ptr, nargs)` 绑定表逐条安装为目标对象的命名方法。
|
||||
///
|
||||
/// 每条经 `world.bind_method` 在 `target` 上建/复用方法 wrapper 并写命名属性;shape 与
|
||||
/// 字符串 interner 由 `core` 借出,循环内共享。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 表中所有函数项指针须为合法的 `NativeFn`(转成 `*const ()`),此处经
|
||||
/// `NativeFnPtr::from_raw` 转换。
|
||||
/// - `bind_method` 的返回值被丢弃:单条绑定失败不中断后续条目。
|
||||
pub(crate) fn apply_binding_table(
|
||||
world: &BuiltinWorld, target: &mut JsObject, core: &Arc<KernelCore>, bindings: &[(&'static str, *const (), u8)],
|
||||
) {
|
||||
|
||||
@@ -9,6 +9,15 @@ use oxide_types::object::{Cell, JsObject, PropAttributes};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
impl Vm {
|
||||
/// 把连续实参寄存器段与 this 寄存器打包成 native 调用下标表。
|
||||
///
|
||||
/// 返回 `([u8; 257], len)`:`buf[0]` 为 this 寄存器下标,其后依次为 `first_arg_reg`
|
||||
/// 起连续递增的实参寄存器下标。数组由调用方在栈上持有,供 native 函数按下标直读
|
||||
/// 共享寄存器窗口。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - `arg_count` 截断为 256,超出部分不进入下标表(`len ≤ 257`)。
|
||||
/// - 实参寄存器下标按 `u8` 回绕,调用方须保证窗口内连续可用。
|
||||
#[inline(always)]
|
||||
pub(crate) fn build_native_args(first_arg_reg: u8, arg_count: usize, this_reg: u8) -> ([u8; 257], usize) {
|
||||
let mut args_buf = [0u8; 257];
|
||||
@@ -20,6 +29,28 @@ impl Vm {
|
||||
(args_buf, n + 1)
|
||||
}
|
||||
|
||||
/// native 函数调用收口:校验调用深度、打包实参、快照 callee 槽后执行实现并按三态回写结果。
|
||||
///
|
||||
/// native 与调用方共享扁平寄存器文件,故实现执行前把当前 callee 放进 `regs[254]`
|
||||
/// 供分发型 builtin(bind/call/apply)读取「当前 callee」,执行后恢复调用方 `this`。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 调用深度达上限抛 RangeError。
|
||||
/// 2. `build_native_args` 打包实参下标表。
|
||||
/// 3. 经 `native_fn_ptr_to_fn` 取得函数项并执行;执行前后快照/恢复 `regs[254]`。
|
||||
/// 4. 按 `NativeResult` 三态回写:`Ok` 写 `regs[0]`;`Err` 以原始值走 `unwind`;
|
||||
/// `TailCall` 目标为 native 时同步调用,否则压字节码帧。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 调用方须保证 `obj` 已是 native 函数:`native_fn()` 为空时此处 unwrap 失败。
|
||||
/// - `Err` 保留异常原值:用户回调 throw 的原始值经 catch 原样收到,不包装成 Error。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 修改 `native_call_depth`、`regs[0]`、`regs[254]`,可能压帧或触发异常展开。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - `native_fn_ptr_to_fn` 是 NativeFnPtr → NativeFn 的唯一强制转换点,SAFETY 前提
|
||||
/// 为 `native_fn` 由 `set_native_fn` 以合法函数项指针设置。
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_native_call(
|
||||
&mut self, obj: &JsObject, callee: JsValue, this_reg: u8, first_arg_reg: u8, arg_count: usize,
|
||||
@@ -197,6 +228,23 @@ impl Vm {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 初始化(或复用)当前帧调用环境的 cell:按 a 槽下标写入 `cell_stack`,b 槽决定初始化标志。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 取 a 槽为 cell 下标,按需扩容 `cell_stack` 至可容纳该下标。
|
||||
/// 2. b 槽为 0 表示已初始化,非 0 为 TDZ(读写抛 ReferenceError,用于 for-in/for-of
|
||||
/// 词法头环境建模)。
|
||||
/// 3. 槽位为空时新建 cell;已有 `CREATE_CLOSURE` 建的占位 cell 则原位更新值与标志,
|
||||
/// 使闭包 upvalue 指向的同一 cell 跟随初始化。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - `cell_stack` 为空时 `last_mut()` unwrap 失败;调用前须已压入当前帧环境层。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 写入/更新堆上 `Cell`(可能触发分配),改动闭包 upvalue 可见值。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 手写 IR 路径保留的指令,常规编译不发射,故标 `#[allow(dead_code)]`。
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn dispatch_make_cell(&mut self, rd: usize, instr: u32) -> Result<(), String> {
|
||||
// a 槽为 cell 索引;b 槽承载未初始化标志(1 = 绑定永不被初始化,
|
||||
@@ -240,6 +288,18 @@ impl Vm {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 读取当前帧 cell:空槽惰性建 cell,未初始化(TDZ)抛 ReferenceError,否则写 `regs[rd]`。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 取 b 槽为 cell 下标,按需扩容 `cell_stack`。
|
||||
/// 2. 槽位为空时以 `regs[a]` 为初值新建已初始化 cell(`CREATE_CLOSURE` 占位缺失路径)。
|
||||
/// 3. cell 未初始化抛 ReferenceError;否则值写入 `regs[rd]`。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - cell 下标越界按需扩容,不报错。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 手写 IR 保留指令(同 `dispatch_make_cell`)。
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn dispatch_cell_get(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
let cell_idx = b;
|
||||
@@ -259,6 +319,22 @@ impl Vm {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 写入当前帧 cell:越界或空槽静默 no-op,TDZ 写检查后写值并置已初始化。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. b 槽为 cell 下标;下标越界直接返回(不建槽)。
|
||||
/// 2. 槽位为空(尚无 `CREATE_CLOSURE`/`MAKE_CELL`)静默返回,不改状态。
|
||||
/// 3. cell 未初始化(声明点前)抛 ReferenceError。
|
||||
/// 4. 写入 `regs[a]` 并置 initialized。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 空槽/越界均为 no-op,与读路径的惰性建 cell 不对称:写方无可用源值建槽。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 修改堆上 `Cell` 值与初始化标志。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 手写 IR 保留指令(同 `dispatch_make_cell`)。
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn dispatch_cell_set(&mut self, a: usize, b: usize) -> Result<(), String> {
|
||||
let cell_idx = b;
|
||||
@@ -289,6 +365,19 @@ impl Vm {
|
||||
self.frames.last().map(|f| f.callee).or(self.inline_callee)
|
||||
}
|
||||
|
||||
/// 读取当前闭包的 upvalue:命中 cell 判初始化后取值,未命中委托惰性建 cell。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 取 imm16 为 upvalue 下标,从当前 callee 的 `upvalues` 命中 cell。
|
||||
/// 2. 命中且非空:未初始化(TDZ)抛 ReferenceError,否则值写入 `regs[rd]`。
|
||||
/// 3. 未命中/空槽(`CREATE_CLOSURE` 早于 `MAKE_CELL`):委托
|
||||
/// `lazy_create_upvalue_cell` 建 cell。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - `uv_idx` 越界或当前无 callee 时同样走惰性路径,最终回退 undefined。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 手写 IR 保留指令(同 `dispatch_make_cell`)。
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn dispatch_load_upvalue(&mut self, rd: usize, instr: u32) -> Result<(), String> {
|
||||
let uv_idx = opcode::imm16(instr) as usize;
|
||||
@@ -315,6 +404,11 @@ impl Vm {
|
||||
self.lazy_create_upvalue_cell(rd, uv_idx)
|
||||
}
|
||||
|
||||
/// 为当前闭包惰性创建 upvalue cell:初值优先取调用方 cell 表(`cell_stack` 倒数
|
||||
/// 第二层)同下标 cell 的值,缺失时取 `regs[rd]`。
|
||||
///
|
||||
/// 服务于 `CREATE_CLOSURE` 早于 `MAKE_CELL` 的 hoisting 顺序。cell 建好后写回闭包
|
||||
/// `upvalues[uv_idx]` 并把值读回 `regs[rd]`;无 callee 或下标越界时 `regs[rd]` 置 undefined。
|
||||
fn lazy_create_upvalue_cell(&mut self, rd: usize, uv_idx: usize) -> Result<(), String> {
|
||||
if let Some(callee) = self.current_callee() {
|
||||
if callee.is_object() {
|
||||
@@ -342,6 +436,19 @@ impl Vm {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 写入当前闭包的 upvalue:空槽惰性建 cell,TDZ/const 检查后写值置已初始化。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. b 槽为 upvalue 下标,源值为 `regs[a]`;`rd` 复用为 const 标志。
|
||||
/// 2. 命中闭包 `upvalues` 且非空:未初始化(TDZ)抛 ReferenceError,const 标志非 0
|
||||
/// 抛 TypeError,否则写值置 initialized。
|
||||
/// 3. 槽位为空(尚未建 cell)以源值新建已初始化 cell;下标越界或无 callee 静默返回。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - const 检查在 TDZ 检查之后,两者均先于写值。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 手写 IR 保留指令(同 `dispatch_make_cell`)。
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn dispatch_store_upvalue(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
let const_flag = rd;
|
||||
|
||||
@@ -123,7 +123,7 @@ impl Vm {
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
// 目标为普通 JS 函数(如用户覆盖了 builtin 名):回退到通用调用路径,
|
||||
// 复用 dispatch_call 的 JS 函数调用逻辑而非强制走 native。
|
||||
// pc 此刻指向 ext 字(与 CALL 指令一致),dispatch_call 直接读取。
|
||||
// pc 此刻指向指令的扩展操作数字(ext 字,与 CALL 指令一致),dispatch_call 直接读取。
|
||||
if obj.is_function() && obj.native_fn().is_none() {
|
||||
return self.dispatch_call(rd, a, b).map(|_| ());
|
||||
}
|
||||
|
||||
@@ -370,6 +370,29 @@ impl Vm {
|
||||
)
|
||||
}
|
||||
|
||||
/// 内联同步调用字节码函数:不起新 CallFrame,直接在调用方寄存器窗口内执行被调模块。
|
||||
///
|
||||
/// 平表按被调对象创建期 `table_gen` 解析(跨 run 调用仍命中定义模块所在原表);
|
||||
/// 生成器/异步/异步生成器函数不执行函数体,分别返回对应迭代器或 promise 对象。
|
||||
/// 普通函数经 `save_inline_state` 保存窗口后在调用方寄存器文件上执行子模块,结束
|
||||
/// 时 `restore_inline_state` 回拷窗口。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. `sub_module_index == 0` 为 native 哨兵(accessor 不可调用),直接返回 TypeError。
|
||||
/// 2. 按创建期代际取平表并校验下标;调用深度达上限抛 RangeError。
|
||||
/// 3. 生成器/异步/异步生成器分流到对应对象构造,不进入 dispatch。
|
||||
/// 4. 保存窗口与 pc,零填 callee 读区,装载字节码/常量/平表 id,实参写入 spill 栈实参区。
|
||||
/// 5. 绑定 `regs[254]`:箭头函数用创建期捕获的 `this`,sloppy 普通函数在 nullish
|
||||
/// receiver 上替换全局对象,严格模式与显式 receiver 原样保留;`regs[255]` 置 undefined。
|
||||
/// 6. 执行 dispatch 后恢复调用方状态并返回结果。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 表代际未注册或 `sub_module_index` 越界返回 `Err`,不 panic。
|
||||
/// - 实参超出 `n_args` 的部分忽略;缺位参数填 undefined。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 改写全局寄存器文件、pc、字节码/常量/平表 id、spill 栈与 `cell_stack`;
|
||||
/// 结束前经 `restore_inline_state` 全部还原。
|
||||
pub(crate) fn call_bytecode_function_inline(
|
||||
&mut self, callee: JsValue, callee_obj: &JsObject, receiver: JsValue, args: &[JsValue],
|
||||
) -> Result<JsValue, String> {
|
||||
@@ -480,6 +503,18 @@ impl Vm {
|
||||
result
|
||||
}
|
||||
|
||||
/// 从调用帧恢复调用方执行状态:弹回字节码/平表 id/表代际/常量四组保存栈,
|
||||
/// 回拷调用方寄存器窗口并按 `return_addr` 回写 pc。
|
||||
///
|
||||
/// 帧恢复只负责执行核心状态;被调函数的返回值传递由调用路径另行处理。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 保存栈为空时对应字段保持原值(不 panic);`saved_reg_offset` 与
|
||||
/// `caller_reg_limit` 由压帧时按调用方活动上界写入,须与其匹配。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 改写 pc、字节码、活动平表 id/代际、活动常量、寄存器窗口与 `regs[254]/[255]`、
|
||||
/// save_stack/spill_stack、`active_reg_limit`,并重载调用方 builtin 镜像槽。
|
||||
pub(crate) fn restore_frame(&mut self, frame: CallFrame) {
|
||||
vm_trace!(
|
||||
"restore_frame: return_addr={} caller_reg_limit={}",
|
||||
@@ -628,6 +663,28 @@ impl Vm {
|
||||
result
|
||||
}
|
||||
|
||||
/// 异常展开:沿 `try_stack` 查找最近可接管的 catch/finally,无则逐帧回退并返回未捕获错误。
|
||||
///
|
||||
/// 与 `TRY_FINALLY_END` 的完成穿越逻辑对应:finally 体在途异常经 `pending_exception`
|
||||
/// 悬挂,`finally_active` 标记其已进入,防止同一 finally 重复执行;处理器作用域内被
|
||||
/// 中断的 for-of 循环先经 `close_for_of_above` 执行 IteratorClose。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 逐 handler 展开:所属帧已返回(`frame_depth` 超出当前帧数)的残留 handler 跳过。
|
||||
/// 2. 弹帧至 handler 深度,逐帧弹 `cell_stack` 并 `restore_frame` 回写调用方状态。
|
||||
/// 3. 关闭该作用域内被中断的 for-of 迭代器。
|
||||
/// 4. 有 finally:未进入则挂起在途异常并转入 finally 体;已进入(finally 体内新异常)
|
||||
/// 则丢弃在途异常继续向外展开,不重入本 finally。
|
||||
/// 5. 有 catch:在途异常写入 `regs[0]`,pc 转向 catch。
|
||||
/// 6. 无可用处理器:关闭全部迭代器、回退所有帧,记 `last_uncaught_value` 并返回 `Err`。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - `exception_value` 为空时按 undefined 处理;`try_stack` 为空直接走未捕获路径。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 弹出 `try_stack`/`frames`/`cell_stack`,改写 pc 与寄存器窗口;未捕获时把逃逸
|
||||
/// 异常值存入 `last_uncaught_value`(for-of 的 next() 抛出经此重抛原值而非展平
|
||||
/// 字符串),并消费 `pending_error_kind`。
|
||||
pub(crate) fn unwind(&mut self) -> Result<(), String> {
|
||||
vm_debug!("unwind: {} try handlers on stack", self.try_stack.len());
|
||||
while let Some(mut handler) = self.try_stack.pop() {
|
||||
|
||||
Reference in New Issue
Block a user