fix: 子模块表代际注册表——跨 run 存活函数按创建期表代解析子模块,索引悬空与静默错模块闭合

This commit is contained in:
2026-09-12 19:27:22 +00:00
parent 473f73a1f6
commit d9c469e9d0
18 changed files with 450 additions and 211 deletions
+1 -1
View File
@@ -336,7 +336,7 @@ pub fn function_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
.unwrap_or_else(|| { .unwrap_or_else(|| {
let sub_idx = func.sub_module_index(); let sub_idx = func.sub_module_index();
if sub_idx > 0 { if sub_idx > 0 {
vm.sub_module_function_name((sub_idx - 1) as u16) vm.sub_module_function_name(func.table_gen(), (sub_idx - 1) as u16)
} else { } else {
String::new() String::new()
} }
+1 -1
View File
@@ -258,7 +258,7 @@ impl KernelCore {
/// 不自动重建——重建须由宿主在"无存活 VM"边界驱动:归还全部 `VmGuard` /// 不自动重建——重建须由宿主在"无存活 VM"边界驱动:归还全部 `VmGuard`
/// (池排空)→ 旧 `Arc<KernelCore>` 归零整体释放(PermInterner/ShapeForge/ /// (池排空)→ 旧 `Arc<KernelCore>` 归零整体释放(PermInterner/ShapeForge/
/// CodeForge/PropForge,含全部泄漏键文本与物化串)→ 新建 kernel + 新池/ /// CodeForge/PropForge,含全部泄漏键文本与物化串)→ 新建 kernel + 新池/
/// VM。存活 VM 的 `immutables_cache` 与 P 对象持有旧 kernel 的物化串裸 /// VM。存活 VM 的代际表注册表(`immutables`)与 P 对象持有旧 kernel 的物化串裸
/// 指针与 shape id,VM 存活期间重建会使其悬垂。三个预设默认 None = 永不 /// 指针与 shape id,VM 存活期间重建会使其悬垂。三个预设默认 None = 永不
/// 触发(有意裁定:CLI eval/run/REPL/bench/test262 宿主均未接重建边界, /// 触发(有意裁定:CLI eval/run/REPL/bench/test262 宿主均未接重建边界,
/// None 保证零行为漂移)。 /// None 保证零行为漂移)。
+1 -1
View File
@@ -191,7 +191,7 @@ pub trait VmHost {
fn promote_if_needed_for_write_ptr(&mut self, target_ptr: *mut JsObject, value: JsValue) -> JsValue; fn promote_if_needed_for_write_ptr(&mut self, target_ptr: *mut JsObject, value: JsValue) -> JsValue;
fn step_rng(&mut self); fn step_rng(&mut self);
fn math_rng_value(&self) -> f64; fn math_rng_value(&self) -> f64;
fn sub_module_function_name(&self, sub_idx: u16) -> String; fn sub_module_function_name(&self, gen: u32, sub_idx: u16) -> String;
/// 动态编译一个函数体(`Function` 构造器用):把参数列表与函数体编译为可调用 /// 动态编译一个函数体(`Function` 构造器用):把参数列表与函数体编译为可调用
/// 函数对象。编译或解析失败返回 `Err`,由调用方转为 `SyntaxError`。 /// 函数对象。编译或解析失败返回 `Err`,由调用方转为 `SyntaxError`。
fn create_dynamic_function(&mut self, params: &[String], body: &str) -> Result<JsValue, String>; fn create_dynamic_function(&mut self, params: &[String], body: &str) -> Result<JsValue, String>;
+20 -6
View File
@@ -509,7 +509,8 @@ impl PropIndex for i32 {
/// native_fn: Option\<NativeFnPtr\> (16 字节 — 裸 `*const ()` 无法利用 Option\<NonNull\> /// native_fn: Option\<NativeFnPtr\> (16 字节 — 裸 `*const ()` 无法利用 Option\<NonNull\>
/// 优化;因 repr(Rust) 布局规则存为包装 8 字节指针的 Option,带 8 字节 /// 优化;因 repr(Rust) 布局规则存为包装 8 字节指针的 Option,带 8 字节
/// 判别式填充) /// 判别式填充)
/// sub_module_index: u32 (4 字节 + 4 填充,索引 CompiledModule.sub_modules) /// sub_module_index: u32 (4 字节,子模块平表下标,与 table_gen 配对解析)
/// table_gen: u32 (4 字节,创建期所属子模块平表的表代际)
/// captured_this: JsValue (8 字节,箭头函数的词法 this) /// captured_this: JsValue (8 字节,箭头函数的词法 this)
/// home_object: JsValue (8 字节,\[\[HomeObject\]\],供 super 查找) /// home_object: JsValue (8 字节,\[\[HomeObject\]\],供 super 查找)
/// upvalues: *mut u8 (8 字节,指向闭包的 Box<Vec<*mut Cell>>) /// upvalues: *mut u8 (8 字节,指向闭包的 Box<Vec<*mut Cell>>)
@@ -598,7 +599,9 @@ pub struct JsObject {
array_len_override: u32, array_len_override: u32,
native_fn: Option<NativeFnPtr>, native_fn: Option<NativeFnPtr>,
sub_module_index: u32, sub_module_index: u32,
_pad3: [u8; 4], /// 子模块平表的表代际:函数对象创建时所属的平表由该代际唯一定位,
/// 调用期与 sub_module_index 配对解析(跨 run 换表后按创建期代际仍命中原表)。
table_gen: u32,
captured_this: JsValue, captured_this: JsValue,
home_object: JsValue, home_object: JsValue,
pub upvalues: *mut u8, pub upvalues: *mut u8,
@@ -821,7 +824,7 @@ impl JsObject {
array_len_override: 0, array_len_override: 0,
native_fn: None, native_fn: None,
sub_module_index: 0, sub_module_index: 0,
_pad3: [0; 4], table_gen: 0,
captured_this: JsValue::undefined(), captured_this: JsValue::undefined(),
home_object: JsValue::undefined(), home_object: JsValue::undefined(),
upvalues: std::ptr::null_mut(), upvalues: std::ptr::null_mut(),
@@ -847,7 +850,7 @@ impl JsObject {
array_len_override: 0, array_len_override: 0,
native_fn: None, native_fn: None,
sub_module_index: 0, sub_module_index: 0,
_pad3: [0; 4], table_gen: 0,
captured_this: JsValue::undefined(), captured_this: JsValue::undefined(),
home_object: JsValue::undefined(), home_object: JsValue::undefined(),
upvalues: std::ptr::null_mut(), upvalues: std::ptr::null_mut(),
@@ -946,7 +949,7 @@ impl JsObject {
array_len_override: self.array_len_override, array_len_override: self.array_len_override,
native_fn: self.native_fn, native_fn: self.native_fn,
sub_module_index: self.sub_module_index, sub_module_index: self.sub_module_index,
_pad3: self._pad3, table_gen: self.table_gen,
captured_this: self.captured_this, captured_this: self.captured_this,
home_object: self.home_object, home_object: self.home_object,
upvalues: self.upvalues, upvalues: self.upvalues,
@@ -1810,7 +1813,8 @@ impl JsObject {
self.native_arg_count = n; self.native_arg_count = n;
} }
/// 子模块下标(函数对象对应的 `CompiledModule` 索引)。 /// 子模块下标(函数对象对应的 `CompiledModule` 在创建期平表中的下标;
/// 0 = 原生函数哨兵)。
pub fn sub_module_index(&self) -> u32 { pub fn sub_module_index(&self) -> u32 {
self.sub_module_index self.sub_module_index
} }
@@ -1820,6 +1824,16 @@ impl JsObject {
self.sub_module_index = idx; self.sub_module_index = idx;
} }
/// 创建期所属子模块平表的表代际(与 sub_module_index 配对解析出唯一模块)。
pub fn table_gen(&self) -> u32 {
self.table_gen
}
/// 设置创建期表代际。
pub fn set_table_gen(&mut self, gen: u32) {
self.table_gen = gen;
}
/// 箭头函数标志(header bit 28)。 /// 箭头函数标志(header bit 28)。
/// 为 true 时,CALL 分发捕获创建时的词法 `this`。 /// 为 true 时,CALL 分发捕获创建时的词法 `this`。
pub fn is_arrow(&self) -> bool { pub fn is_arrow(&self) -> bool {
+2 -5
View File
@@ -8,8 +8,6 @@
//! spill/save/try/cell 栈段搬运。`AWAIT` 经 `Vm::async_suspended` 信号让内嵌 //! spill/save/try/cell 栈段搬运。`AWAIT` 经 `Vm::async_suspended` 信号让内嵌
//! dispatch 返回,恢复方据此快照挂起状态。 //! dispatch 返回,恢复方据此快照挂起状态。
use std::sync::Arc;
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_runtime_api::{to_boolean, NativeResult}; use oxide_runtime_api::{to_boolean, NativeResult};
use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes}; use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes};
@@ -197,10 +195,9 @@ impl Vm {
{ {
let state = unsafe { &mut *state_ptr }; let state = unsafe { &mut *state_ptr };
let subs = Arc::clone(&self.sub_modules); let restore_res = state.suspended.restore_into(self, state.callee);
let restore_res = state.suspended.restore_into(self, &subs);
if restore_res.is_err() { if restore_res.is_err() {
// 挂起状态跨 run:sub_modules 已重建,无法恢复(与生成器同限制)。 // callee 记录的表代际已被回收(跨 run 且无存活函数引用),无法恢复。
self.restore_async_flags(prev_ctx, prev_dispatch, prev_gen_ctx, prev_gen_dispatch); self.restore_async_flags(prev_ctx, prev_dispatch, prev_gen_ctx, prev_gen_dispatch);
self.native_call_depth -= 1; self.native_call_depth -= 1;
self.restore_inline_state(saved); self.restore_inline_state(saved);
+2 -4
View File
@@ -13,7 +13,6 @@
//! 快照挂起状态;`YIELD` 复用 `Vm::generator_suspended` 信号。 //! 快照挂起状态;`YIELD` 复用 `Vm::generator_suspended` 信号。
use std::collections::VecDeque; use std::collections::VecDeque;
use std::sync::Arc;
use oxide_builtins::iterator::make_iter_result; use oxide_builtins::iterator::make_iter_result;
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
@@ -304,10 +303,9 @@ impl Vm {
{ {
let state = unsafe { &mut *state_ptr }; let state = unsafe { &mut *state_ptr };
let subs = Arc::clone(&self.sub_modules); let restore_res = state.suspended.restore_into(self, state.callee);
let restore_res = state.suspended.restore_into(self, &subs);
if restore_res.is_err() { if restore_res.is_err() {
// 挂起状态跨 run:sub_modules 已重建,无法恢复(与生成器同限制)。 // callee 记录的表代际已被回收(跨 run 且无存活函数引用),无法恢复。
self.async_gen_dispatch = prev_agd; self.async_gen_dispatch = prev_agd;
self.async_gen_context = prev_gen_ctx; self.async_gen_context = prev_gen_ctx;
self.generator_dispatch = prev_gd; self.generator_dispatch = prev_gd;
+18 -19
View File
@@ -105,16 +105,18 @@ impl Vm {
pub(crate) fn dispatch_create_closure(&mut self, rd: usize, instr: u32) -> Result<(), String> { pub(crate) fn dispatch_create_closure(&mut self, rd: usize, instr: u32) -> Result<(), String> {
let sub_idx = opcode::imm16(instr) as u32; let sub_idx = opcode::imm16(instr) as u32;
vm_trace!("CREATE_CLOSURE rd={} sub_idx={}", rd, sub_idx); vm_trace!("CREATE_CLOSURE rd={} sub_idx={}", rd, sub_idx);
if sub_idx == 0 || (sub_idx as usize) >= self.sub_modules.len() { // 闭包按当前 run 的平表解析(CREATE_CLOSURE 只在本 run 装载的模块内发射)。
let table = self.current_table();
if sub_idx == 0 || (sub_idx as usize) >= table.modules.len() {
// 逃逸闭包(函数对象在定义模块之外被创建)时 sub_idx 相对定义模块, // 逃逸闭包(函数对象在定义模块之外被创建)时 sub_idx 相对定义模块,
// 超出当前 sub_modules 上下文——按运行时错误处理而非索引越界 panic。 // 超出当前平表上下文——按运行时错误处理而非索引越界 panic。
return Err(format!( return Err(format!(
"CREATE_CLOSURE: sub_module_index {} out of bounds (max {})", "CREATE_CLOSURE: sub_module_index {} out of bounds (max {})",
sub_idx, sub_idx,
self.sub_modules.len() table.modules.len()
)); ));
} }
let sub = &self.sub_modules[sub_idx as usize]; let sub = &table.modules[sub_idx as usize];
let is_arrow = sub.is_arrow; let is_arrow = sub.is_arrow;
let is_class_constructor = sub.is_class_constructor; let is_class_constructor = sub.is_class_constructor;
let is_derived_constructor = sub.is_derived_constructor; let is_derived_constructor = sub.is_derived_constructor;
@@ -504,11 +506,11 @@ impl Vm {
} }
} else if super_obj.sub_module_index() > 0 { } else if super_obj.sub_module_index() > 0 {
let sub_idx = super_obj.sub_module_index() as usize; let sub_idx = super_obj.sub_module_index() as usize;
if sub_idx >= self.sub_modules.len() { if self.callee_module(super_obj).is_none() {
return Err(format!( return Err(format!(
"SUPER_CALL: sub_module_index {} out of bounds (max {})", "SUPER_CALL: sub_module_index {} out of bounds (max {})",
sub_idx, sub_idx,
self.sub_modules.len() self.current_table().modules.len()
)); ));
} }
// 收敛到统一压帧入口:this = derived_this(super() 把实例交予父构造器), // 收敛到统一压帧入口:this = derived_this(super() 把实例交予父构造器),
@@ -748,12 +750,11 @@ impl Vm {
} }
} }
} else if obj.sub_module_index() > 0 { } else if obj.sub_module_index() > 0 {
let sub_idx = obj.sub_module_index() as usize; // 按函数对象自身记录的表代际解析:表缺失(已回收)时落
// 普通压帧路径,由 push_bytecode_frame 按自身口径报错。
let sub = self.callee_module(obj);
// 异步生成器函数调用返回异步生成器迭代器对象。 // 异步生成器函数调用返回异步生成器迭代器对象。
if sub_idx < self.sub_modules.len() if matches!(sub, Some(m) if m.is_generator && m.is_async) {
&& self.sub_modules[sub_idx].is_generator
&& self.sub_modules[sub_idx].is_async
{
let gen_pc = self.pc; let gen_pc = self.pc;
let gen = self.create_async_generator_object(callee, this_value, &args)?; let gen = self.create_async_generator_object(callee, this_value, &args)?;
// 参数初始化抛错已就地展开(unwind 改写 pc 至 catch):异常值已 // 参数初始化抛错已就地展开(unwind 改写 pc 至 catch):异常值已
@@ -765,7 +766,7 @@ impl Vm {
return Ok(false); return Ok(false);
} }
// 生成器函数调用返回迭代器对象,不执行函数体。 // 生成器函数调用返回迭代器对象,不执行函数体。
if sub_idx < self.sub_modules.len() && self.sub_modules[sub_idx].is_generator { if matches!(sub, Some(m) if m.is_generator) {
let gen_pc = self.pc; let gen_pc = self.pc;
let gen = self.create_generator_object(callee, this_value, &args)?; let gen = self.create_generator_object(callee, this_value, &args)?;
// 参数初始化抛错已就地展开时不得覆盖 catch 参数(同上)。 // 参数初始化抛错已就地展开时不得覆盖 catch 参数(同上)。
@@ -775,7 +776,7 @@ impl Vm {
return Ok(false); return Ok(false);
} }
// 异步函数调用返回 capability promise,立即同步执行 body 到首个 await。 // 异步函数调用返回 capability promise,立即同步执行 body 到首个 await。
if sub_idx < self.sub_modules.len() && self.sub_modules[sub_idx].is_async { if matches!(sub, Some(m) if m.is_async) {
let promise = self.create_async_object(callee, this_value, &args)?; let promise = self.create_async_object(callee, this_value, &args)?;
self.regs[0] = promise; self.regs[0] = promise;
return Ok(false); return Ok(false);
@@ -880,16 +881,14 @@ impl Vm {
} }
} }
} else if ctor_obj.sub_module_index() > 0 { } else if ctor_obj.sub_module_index() > 0 {
// 按构造器自身记录的表代际解析。
let sub = self.callee_module(ctor_obj);
// 生成器函数不是构造器:`new g()` 抛 TypeError。 // 生成器函数不是构造器:`new g()` 抛 TypeError。
if (ctor_obj.sub_module_index() as usize) < self.sub_modules.len() if matches!(sub, Some(m) if m.is_generator) {
&& self.sub_modules[ctor_obj.sub_module_index() as usize].is_generator
{
return self.raise_type_error("g is not a constructor").map(|_| true); return self.raise_type_error("g is not a constructor").map(|_| true);
} }
// 异步函数不是构造器:`new f()` 抛 TypeError。 // 异步函数不是构造器:`new f()` 抛 TypeError。
if (ctor_obj.sub_module_index() as usize) < self.sub_modules.len() if matches!(sub, Some(m) if m.is_async) {
&& self.sub_modules[ctor_obj.sub_module_index() as usize].is_async
{
return self.raise_type_error("g is not a constructor").map(|_| true); return self.raise_type_error("g is not a constructor").map(|_| true);
} }
let this_value = if ctor_obj.is_derived_constructor() { let this_value = if ctor_obj.is_derived_constructor() {
+2 -5
View File
@@ -6,8 +6,6 @@
//! 循环中继续执行到下一个 `YIELD` 或 `RETURN`。`YIELD` 通过 //! 循环中继续执行到下一个 `YIELD` 或 `RETURN`。`YIELD` 通过
//! `Vm::generator_suspended` 信号让内嵌 dispatch 返回,恢复方据此快照挂起状态。 //! `Vm::generator_suspended` 信号让内嵌 dispatch 返回,恢复方据此快照挂起状态。
use std::sync::Arc;
use oxide_builtins::iterator::{is_callable, make_iter_result}; use oxide_builtins::iterator::{is_callable, make_iter_result};
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_runtime_api::{to_boolean, NativeResult}; use oxide_runtime_api::{to_boolean, NativeResult};
@@ -276,10 +274,9 @@ impl Vm {
unsafe { (*state_ptr).phase = GeneratorPhase::Running }; unsafe { (*state_ptr).phase = GeneratorPhase::Running };
} else { } else {
let state = unsafe { &mut *state_ptr }; let state = unsafe { &mut *state_ptr };
let subs = Arc::clone(&self.sub_modules); let restore_res = state.suspended.restore_into(self, state.callee);
let restore_res = state.suspended.restore_into(self, &subs);
if restore_res.is_err() { if restore_res.is_err() {
// 挂起状态跨 run:sub_modules 已重建,无法恢复(与动态函数同限制)。 // 挂起状态引用的子模块表代际已回收:无法恢复。
self.restore_inline_state(saved); self.restore_inline_state(saved);
self.native_call_depth -= 1; self.native_call_depth -= 1;
return Err("generator suspended across runs is no longer valid".into()); return Err("generator suspended across runs is no longer valid".into());
+13 -7
View File
@@ -238,14 +238,19 @@ impl Vm {
fn call_constructor_bytecode_inline( fn call_constructor_bytecode_inline(
&mut self, ctor: JsValue, ctor_obj: &JsObject, args: &[JsValue], &mut self, ctor: JsValue, ctor_obj: &JsObject, args: &[JsValue],
) -> Result<JsValue, JsValue> { ) -> Result<JsValue, JsValue> {
let sub_idx = ctor_obj.sub_module_index() as usize; // 按构造器自身记录的表代际解析:代际表缺失(已回收)或下标越界与
if sub_idx == 0 || sub_idx >= self.sub_modules.len() { // 原生函数哨兵同口径报 TypeError。
return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor")); let callee_module = match self.callee_module(ctor_obj) {
} Some(m) => m,
// 生成器/异步函数不是构造器。 None => {
if self.sub_modules[sub_idx].is_generator || self.sub_modules[sub_idx].is_async { return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor"));
}
};
if callee_module.is_generator || callee_module.is_async {
return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor")); return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor"));
} }
// 先拷出寄存器数,释放对代际表的借用后再进入可变借用区。
let callee_reg_count = callee_module.n_registers;
let new_obj_ptr = self.alloc_ctor_this(ctor_obj)?; let new_obj_ptr = self.alloc_ctor_this(ctor_obj)?;
let new_obj_val = JsValue::from_js_object(new_obj_ptr); let new_obj_val = JsValue::from_js_object(new_obj_ptr);
// derived 构造器 super() 前 this 为 undefined,基类 this = 新对象。 // derived 构造器 super() 前 this 为 undefined,基类 this = 新对象。
@@ -254,7 +259,8 @@ impl Vm {
} else { } else {
new_obj_val new_obj_val
}; };
let window = self.active_reg_limit.max(self.sub_modules[sub_idx].n_registers).max(1) as usize; // 入口已解析代际表:读 n_registers 定保存窗口。
let window = self.active_reg_limit.max(callee_reg_count).max(1) as usize;
let saved = self.save_inline_state(window); let saved = self.save_inline_state(window);
let prev_construct = self.construct_dispatch; let prev_construct = self.construct_dispatch;
self.construct_dispatch = true; self.construct_dispatch = true;
+22 -13
View File
@@ -7,7 +7,6 @@
use std::sync::Arc; use std::sync::Arc;
use oxide_bytecode::module::CompiledModule;
use oxide_bytecode::opcode; use oxide_bytecode::opcode;
use oxide_types::object::Cell; use oxide_types::object::Cell;
use oxide_types::value::JsValue; use oxide_types::value::JsValue;
@@ -20,7 +19,8 @@ pub(crate) struct SuspendedFrame {
pub regs: Box<[JsValue; 256]>, pub regs: Box<[JsValue; 256]>,
pub pc: usize, pub pc: usize,
pub bytecode: Arc<[opcode::Instr]>, pub bytecode: Arc<[opcode::Instr]>,
/// 模块 flat_id(恢复时经 sub_modules 重激活 immutables;跨 run 失效据此报错)。 /// 模块 flat_id(callee 表代际平表中的下标;恢复时按 callee 记录的代际
/// 解析平表并重激活 immutables)。
pub sub_idx: u32, pub sub_idx: u32,
pub active_reg_limit: u8, pub active_reg_limit: u8,
pub root_reg_limit: u8, pub root_reg_limit: u8,
@@ -106,20 +106,30 @@ impl SuspendedFrame {
Ok(()) Ok(())
} }
/// 恢复进 VM:写回各栈段、在途异常/完成,按 sub_idx 重激活 immutables。 /// 恢复进 VM:写回各栈段、在途异常/完成,按 callee 记录的表代际重激活
/// immutables。
/// ///
/// # 边界 /// # 边界
/// `sub_idx >= sub_modules.len()`(挂起状态跨 run)返回 Err,调用方须按各自 /// - `callee` 须为挂起函数对象值(状态盒 `callee` 槽):代际按对象创建期
/// 路径回滚并报错(保持现有三处行为)。 /// 记录解析,存活函数对象按代际保活其表,跨 run 恢复命中同一张表。
pub fn restore_into(&mut self, vm: &mut Vm, sub_modules: &Arc<Vec<Arc<CompiledModule>>>) -> Result<(), String> { /// - callee 非对象(gen 0 哨兵)、代际表已被回收或下标越界返回 Err,
/// 调用方须按各自路径回滚并报错(保持现有三处行为)。
pub fn restore_into(&mut self, vm: &mut Vm, callee: JsValue) -> Result<(), String> {
vm.regs = *self.regs; vm.regs = *self.regs;
vm.pc = self.pc; vm.pc = self.pc;
vm.bytecode = std::mem::take(&mut self.bytecode); vm.bytecode = std::mem::take(&mut self.bytecode);
let subs = Arc::clone(sub_modules); let gen = if callee.is_object() {
if (self.sub_idx as usize) < subs.len() { // SAFETY: callee 为执行核心产出的函数对象值,session 生命周期内指针有效。
vm.activate_immutables(self.sub_idx as usize, &subs[self.sub_idx as usize].constants); unsafe { (*callee.as_js_object_ptr()).table_gen() }
} else { } else {
return Err("suspended state across runs is no longer valid".into()); vm.current_gen
};
let table = vm.tables.get(&gen);
let module = table.and_then(|t| t.modules.get(self.sub_idx as usize));
let constants = module.map(|m| m.constants.clone());
match constants {
Some(c) => vm.activate_immutables(gen, self.sub_idx as usize, &c),
None => return Err("suspended state module table is no longer available".into()),
} }
vm.active_reg_limit = self.active_reg_limit; vm.active_reg_limit = self.active_reg_limit;
vm.root_reg_limit = self.root_reg_limit; vm.root_reg_limit = self.root_reg_limit;
@@ -463,12 +473,11 @@ mod tests {
Some(Completion::Return { value, remaining_finally: 1, .. }) if value == JsValue::float(46.0) Some(Completion::Return { value, remaining_finally: 1, .. }) if value == JsValue::float(46.0)
)); ));
// sub_modules 为空表:恢复应报"跨 run"错(保持现有三处行为)。 // gen 0 空表占位 + sub_idx = 0 越界:恢复应报错(保持现有三处行为)。
let mut machine2 = vm(); let mut machine2 = vm();
let subs = std::sync::Arc::new(vec![]);
let mut frame2 = SuspendedFrame::new_empty(); let mut frame2 = SuspendedFrame::new_empty();
frame2.sub_idx = 0; frame2.sub_idx = 0;
let err = frame2.restore_into(&mut machine2, &subs); let err = frame2.restore_into(&mut machine2, JsValue::undefined());
assert!(err.is_err()); assert!(err.is_err());
} }
} }
+161 -80
View File
@@ -392,6 +392,24 @@ pub(crate) struct InlineSyncState {
pub(crate) inline_args_count: u16, pub(crate) inline_args_count: u16,
pub(crate) accessor_frame_target_reg: Option<u8>, pub(crate) accessor_frame_target_reg: Option<u8>,
pub(crate) active_flat_id: u32, pub(crate) active_flat_id: u32,
pub(crate) active_table_gen: u32,
}
/// 单代子模块平表:模块平表 + 每模块不可变常量缓存,注册进 `Vm::tables` 按代际管理。
///
/// 以 `Box` 承载(HashMap 扩容搬 Box 指针不搬被指物):`active_immutables` 与
/// `saved_immutables_stack` 是伸入 immutables 内部常量 Vec 的胖指针,表地址须
/// 在其整个生命周期内稳定。动态扩表(`create_dynamic_function` /
/// `create_dynamic_script`)对 modules 外层 Arc 做 `make_mut`:彼时该 Arc 的
/// 强引用唯一持有者是本表(函数对象只持 u32 下标不持 Arc),原地扩展不分叉。
pub(crate) struct TableGen {
/// 全局扁平模块表:下标 = 模块 `flat_id`(顶层 0,子模块 flatten 后全局唯一)。
/// 函数对象 `sub_module_index` 即该平表下标,逃逸函数也能自足解析。
pub(crate) modules: Arc<Vec<Arc<CompiledModule>>>,
/// 每次 run 转换一次的不可变常量缓存:下标 0 = 顶层模块,sub_idx = modules[sub_idx]。
/// 每个 `OnceLock` 保存该模块常量本次运行中只转换一次的 `JsValue` 结果。
/// 不可变常量是标量 + perm 字符串,只读,GC 根收集按表代际遍历。
pub(crate) immutables: Vec<OnceLock<Vec<JsValue>>>,
} }
/// 基于寄存器的 JS 虚拟机:持有执行状态、寄存器文件、调用栈与 session 内存。 /// 基于寄存器的 JS 虚拟机:持有执行状态、寄存器文件、调用栈与 session 内存。
@@ -403,15 +421,11 @@ pub struct Vm {
pub(crate) regs: [JsValue; 256], pub(crate) regs: [JsValue; 256],
pub(crate) pc: usize, pub(crate) pc: usize,
/// 当前活动字节码。以 `Arc<[Instr]>` 共享:函数调用经 `Arc::clone` 换帧(O(1)), /// 当前活动字节码。以 `Arc<[Instr]>` 共享:函数调用经 `Arc::clone` 换帧(O(1)),
/// 不再逐帧深拷贝。与 sub_modules 源共享同一缓冲,IC 写回经 `bytecode_mut` 的 /// 不再逐帧深拷贝。与子模块平表源共享同一缓冲,IC 写回经 `bytecode_mut` 的
/// `Arc::make_mut` 写时复制,保证独占后才改写(miss 时才深拷贝,频率低)。 /// `Arc::make_mut` 写时复制,保证独占后才改写(miss 时才深拷贝,频率低)。
pub(crate) bytecode: Arc<[opcode::Instr]>, pub(crate) bytecode: Arc<[opcode::Instr]>,
/// 每次 run 转换一次的不可变常量缓存。下标 0 = 顶层模块,sub_idx+1 = sub_modules[sub_idx]。 /// 当前活动模块已转换不可变常量的只读视图(指向当前表代际的 immutables 内部)。
/// 每个 `OnceLock` 保存该模块常量本次运行中只转换一次的 `JsValue` 结果,每次 `run()` 重建。 /// 用胖 `*const`:常量 Vec 归表代际所有且 OnceLock 只填一次(堆分配地址稳定)。
/// 不可变常量是标量 + perm 字符串,只读,不作为 GC 根。
pub(crate) immutables_cache: Vec<OnceLock<Vec<JsValue>>>,
/// 当前活动模块已转换不可变常量的只读视图(指向 immutables_cache 内部)。
/// 用胖 `*const`:缓存 Vec 归 VM 所有且本次运行稳定(OnceLock 只填一次)。
pub(crate) active_immutables: *const [JsValue], pub(crate) active_immutables: *const [JsValue],
pub(crate) frames: SmallVec<[CallFrame; 16]>, pub(crate) frames: SmallVec<[CallFrame; 16]>,
pub(crate) kernel_core: Arc<KernelCore>, pub(crate) kernel_core: Arc<KernelCore>,
@@ -444,11 +458,16 @@ pub struct Vm {
/// 微任务队列(Promise reactions / thenable 委托),`run()` 末尾 FIFO drain。 /// 微任务队列(Promise reactions / thenable 委托),`run()` 末尾 FIFO drain。
pub(crate) job_queue: VecDeque<crate::promise::Microtask>, pub(crate) job_queue: VecDeque<crate::promise::Microtask>,
pub math_rng_state: u64, pub math_rng_state: u64,
/// 全局扁平模块表:下标 = 模块 `flat_id`(顶层 0,子模块 flatten 后全局唯一)。 /// 子模块平表的表代际注册表:键 = 表代际(`current_gen` 为当前 run 正在
/// 闭包 `sub_module_index` 即 flat_id,逃逸闭包也能自足解析。 /// 装载的代际)。函数对象创建时记录自身所属代际(`JsObject::table_gen`),
/// 调用期按创建期代际解析平表——跨 run 换表后存活函数仍命中原表。
/// 条目为 `Arc<CompiledModule>`,与调用方模块树共享:顶层条目即调用方模块 /// 条目为 `Arc<CompiledModule>`,与调用方模块树共享:顶层条目即调用方模块
/// Arc 同一实例,`run()` 全程只做 Arc::clone,无深拷贝。 /// Arc 同一实例,`run()` 全程只做 Arc::clone,无深拷贝。run 边界回收无
pub(crate) sub_modules: Arc<Vec<Arc<CompiledModule>>>, /// 存活函数对象引用的代际(`reclaim_unreferenced_tables`),内存有界于
/// 存活函数对象数,不随 run 数单调增。
pub(crate) tables: HashMap<u32, Box<TableGen>>,
/// 当前 run 正在装载的表代际(构造器预登记 gen 0 空表占位,run() 换表时 +1)。
pub(crate) current_gen: u32,
/// 帧切换时暂存调用方字节码的 Arc 栈(与 `bytecode` 同共享语义)。 /// 帧切换时暂存调用方字节码的 Arc 栈(与 `bytecode` 同共享语义)。
pub(crate) saved_bytecode_stack: Vec<Arc<[opcode::Instr]>>, pub(crate) saved_bytecode_stack: Vec<Arc<[opcode::Instr]>>,
pub(crate) saved_immutables_stack: Vec<*const [JsValue]>, pub(crate) saved_immutables_stack: Vec<*const [JsValue]>,
@@ -558,15 +577,22 @@ pub struct Vm {
/// 分组保存 inline cache 与指令计数器。 /// 分组保存 inline cache 与指令计数器。
pub(crate) profiling: ProfilingState, pub(crate) profiling: ProfilingState,
pub(crate) cell_stack: Vec<Vec<*mut Cell>>, pub(crate) cell_stack: Vec<Vec<*mut Cell>>,
/// 标签模板对象缓存(GetTemplateObject):键 = (模块 flat_id, site 序号)。 /// 标签模板对象缓存(GetTemplateObject):键 = (表代际, 模块 flat_id, site 序号)。
/// 同一编译树同 site 恒返回同一对象;每次 `run()` 清空(flat_id 复用防误命中)。 /// 同一代际同 flat_id 同 site 恒返回同一对象;每次 `run()` 清空(缓存只留
/// 值为 GC 根(for_each_value/rewrite_values 遍历)。 /// 本次 run 的条目,规模有界)。键含代际维度后,跨 run 调用旧代模块的
pub(crate) template_objects: HashMap<(u32, u32), JsValue>, /// flat_id 重编号不再误命中。值为 GC 根(for_each_value/rewrite_values 遍历)。
pub(crate) template_objects: HashMap<(u32, u32, u32), JsValue>,
/// 当前活动字节码所属模块的 flat_id(顶层 0;帧切换时随 bytecode 换)。 /// 当前活动字节码所属模块的 flat_id(顶层 0;帧切换时随 bytecode 换)。
/// GET_TEMPLATE_OBJECT 据其区分不同编译树(eval 每次编译独立 site)。 /// GET_TEMPLATE_OBJECT 据其区分不同编译树(eval 每次编译独立 site)。
pub(crate) active_flat_id: u32, pub(crate) active_flat_id: u32,
/// 当前活动字节码所属子模块平表的表代际(帧切换时随 flat_id 同步换)。
/// 模板缓存键含该代际:跨 run 调用的旧代模块与当前 run 模块 flat_id
/// 重编号互不碰撞。
pub(crate) active_table_gen: u32,
/// 帧切换时暂存调用方 flat_id 的栈(与 saved_bytecode_stack 同步 push/pop)。 /// 帧切换时暂存调用方 flat_id 的栈(与 saved_bytecode_stack 同步 push/pop)。
pub(crate) saved_flat_id_stack: Vec<u32>, pub(crate) saved_flat_id_stack: Vec<u32>,
/// 帧切换时暂存调用方表代际的栈(与 saved_flat_id_stack 同步 push/pop)。
pub(crate) saved_table_gen_stack: Vec<u32>,
} }
impl Drop for Vm { impl Drop for Vm {
@@ -763,25 +789,59 @@ impl Vm {
if self.active_immutables.is_null() { if self.active_immutables.is_null() {
&[] &[]
} else { } else {
// SAFETY: active_immutables 指向 VM 拥有的 immutables_cache 内的 OnceLock<Vec<JsValue>>, // SAFETY: active_immutables 指向某表代际 immutables 内 OnceLock 的常量
// Vec 只填充一次,本次运行期间不再重分配。 // Vec,Vec 只填充一次且堆地址稳定;所指代际表在注册表内存活(当前
// 代际恒在,旧代际由存活函数对象引用保活)。
unsafe { &*self.active_immutables } unsafe { &*self.active_immutables }
} }
} }
/// 激活模块 `cache_idx` 的不可变常量(0 = 顶层,sub_idx+1 = sub_modules[sub_idx]), /// 当前代际的子模块平表(动态扩表与测试注入的目标)。
/// 只转换一次存入 `immutables_cache[cache_idx]`,并把 `active_immutables` 指向该 Vec。 pub(crate) fn current_table(&self) -> &TableGen {
self.tables.get(&self.current_gen).expect("当前代际表构造期已预登记")
}
/// 当前代际的子模块平表(可变)。
pub(crate) fn current_table_mut(&mut self) -> &mut TableGen {
self.tables.get_mut(&self.current_gen).expect("当前代际表构造期已预登记")
}
/// 函数对象 `sub_module_index` 指向的子模块条目:按对象自身记录的表代际
/// 解析平表后按下标定位。
///
/// # 边界与前提
/// - 原生函数哨兵 `sub_module_index() == 0` 不经本方法(调用方先行分派);
/// - 代际表已被回收或下标越界返回 None(调用方按各自越界口径报错)。
pub(crate) fn callee_module<'a>(&'a self, obj: &'a JsObject) -> Option<&'a CompiledModule> {
self.tables
.get(&obj.table_gen())
.and_then(|t| t.modules.get(obj.sub_module_index() as usize))
.map(|m| m.as_ref())
}
/// 子模块表注册表的当前代际条目总数(run 边界回收行为的测试钉)。
pub fn table_gen_count(&self) -> usize {
self.tables.len()
}
/// 激活代际 `gen` 平表中模块 `cache_idx` 的不可变常量,只转换一次存入该代际
/// `immutables[cache_idx]`,并把 `active_immutables` 指向该 Vec。
/// `constants` 由调用方传入(它已持有 `&module.constants`)。 /// `constants` 由调用方传入(它已持有 `&module.constants`)。
pub(crate) fn activate_immutables(&mut self, cache_idx: usize, constants: &[Constant]) { ///
// 用裸指针访问缓存槽,避免 get_or_init(借用 immutables_cache)与 &self 的 /// # 边界与前提
/// - `gen` 的表须已在注册表中(当前代际由 run() 登记,旧代际由存活函数
/// 对象引用保活);`cache_idx` 须小于该代际平表长度。
pub(crate) fn activate_immutables(&mut self, gen: u32, cache_idx: usize, constants: &[Constant]) {
// 用裸指针访问缓存槽,避免 get_or_init(借用表 immutables)与 &self 的
// convert_immutables 闭包和随后对 active_immutables 的写入发生借用冲突。 // convert_immutables 闭包和随后对 active_immutables 的写入发生借用冲突。
// 成立前提:immutables_cache 归 VM 所有、只读、本次运行稳定。 // 成立前提:代际表归注册表所有、只读、Box 承载地址稳定。
let slot: *const OnceLock<Vec<JsValue>> = &self.immutables_cache[cache_idx]; let table = self.tables.get_mut(&gen).expect("激活的代际表须在注册表中");
let slot: *const OnceLock<Vec<JsValue>> = &table.immutables[cache_idx];
let vec = unsafe { &*slot }.get_or_init(|| self.convert_immutables(constants)); let vec = unsafe { &*slot }.get_or_init(|| self.convert_immutables(constants));
self.active_immutables = vec.as_slice() as *const [JsValue]; self.active_immutables = vec.as_slice() as *const [JsValue];
} }
/// 当前活动字节码的可变访问入口。bytecode 以 `Arc<[Instr]>` 与 sub_modules 源共享, /// 当前活动字节码的可变访问入口。bytecode 以 `Arc<[Instr]>` 与代际表 modules 源共享,
/// IC 写回经 `Arc::make_mut` 保证独占:独占时零拷贝原地写,共享时先深拷贝再写 /// IC 写回经 `Arc::make_mut` 保证独占:独占时零拷贝原地写,共享时先深拷贝再写
/// (IC miss 才触发,频率低)。所有写操作必须经此方法,防止共享缓冲被多实例污染。 /// (IC miss 才触发,频率低)。所有写操作必须经此方法,防止共享缓冲被多实例污染。
pub(crate) fn bytecode_mut(&mut self) -> &mut [opcode::Instr] { pub(crate) fn bytecode_mut(&mut self) -> &mut [opcode::Instr] {
@@ -796,12 +856,14 @@ impl Vm {
for value in &self.regs { for value in &self.regs {
f(*value); f(*value);
} }
// immutables 缓存含 session BigInt(new_bigint 分配),未入根则被 sweep 释放 // 各代际 immutables 缓存含 session BigInt(new_bigint 分配),未入根则被
// → 常量池加载时悬垂。perm 字符串无害(不在 session 集合中)。 // sweep 释放 → 常量池加载时悬垂。perm 字符串无害(不在 session 集合中)。
for once_lock in &self.immutables_cache { for table in self.tables.values() {
if let Some(immutable_vec) = once_lock.get() { for once_lock in &table.immutables {
for &value in immutable_vec.iter() { if let Some(immutable_vec) = once_lock.get() {
f(value); for &value in immutable_vec.iter() {
f(value);
}
} }
} }
} }
@@ -1534,23 +1596,27 @@ impl Vm {
return Err(self.error_message_text("TypeError", "CALL target is not callable")); return Err(self.error_message_text("TypeError", "CALL target is not callable"));
} }
let sub_idx = obj.sub_module_index() as usize; let sub_idx = obj.sub_module_index() as usize;
if sub_idx >= self.sub_modules.len() { let gen = obj.table_gen();
return Err(format!( let table = match self.tables.get(&gen) {
"CALL: sub_module_index {} out of bounds (max {})", Some(t) => t,
sub_idx, None => return Err(format!("CALL: module table gen {} not available", gen)),
self.sub_modules.len() };
)); if sub_idx >= table.modules.len() {
return Err(format!("CALL: sub_module_index {} out of bounds (max {})", sub_idx, table.modules.len()));
} }
if self.frames.len() >= self.kernel_core.config.max_call_depth { if self.frames.len() >= self.kernel_core.config.max_call_depth {
return self.raise_error_kind("RangeError", "Maximum call stack size exceeded"); return self.raise_error_kind("RangeError", "Maximum call stack size exceeded");
} }
let sub_bytecode = Arc::clone(&self.sub_modules[sub_idx].bytecode); // 被调模块取 Arc 克隆(与调用方共享同一编译产物):后续 activate_immutables
let sub_n_args = self.sub_modules[sub_idx].n_args as usize; // 等 &mut self 调用期间不持有对注册表的借用。
let sub_n_registers = self.sub_modules[sub_idx].n_registers; let sub = Arc::clone(&table.modules[sub_idx]);
let sub_param_base = self.sub_modules[sub_idx].param_base as usize; let sub_bytecode = Arc::clone(&sub.bytecode);
let sub_is_arrow = self.sub_modules[sub_idx].is_arrow; let sub_n_args = sub.n_args as usize;
let sub_is_strict = self.sub_modules[sub_idx].is_strict; let sub_n_registers = sub.n_registers;
let sub_param_base = sub.param_base as usize;
let sub_is_arrow = sub.is_arrow;
let sub_is_strict = sub.is_strict;
// 窗口 = min(调用方活动寄存器, 存活上界);call_window=0 表示调用方全量 // 窗口 = min(调用方活动寄存器, 存活上界);call_window=0 表示调用方全量
// (运行时发起路径 / 未编码的旧模块)。恢复按窗口回拷,active_reg_limit // (运行时发起路径 / 未编码的旧模块)。恢复按窗口回拷,active_reg_limit
// 仍还原为调用方真实值(caller_active_reg_limit)。 // 仍还原为调用方真实值(caller_active_reg_limit)。
@@ -1598,11 +1664,14 @@ impl Vm {
self.saved_bytecode_stack.push(std::mem::take(&mut self.bytecode)); self.saved_bytecode_stack.push(std::mem::take(&mut self.bytecode));
self.saved_immutables_stack.push(self.active_immutables); self.saved_immutables_stack.push(self.active_immutables);
// 记录调用方 flat_id,进入被调模块(标签模板 site 缓存按模块隔离)。 // 记录调用方 flat_id 与表代际,进入被调模块(标签模板 site 缓存按
// (代际, flat_id) 隔离)。
self.saved_flat_id_stack.push(self.active_flat_id); self.saved_flat_id_stack.push(self.active_flat_id);
self.saved_table_gen_stack.push(self.active_table_gen);
self.active_flat_id = sub_idx as u32; self.active_flat_id = sub_idx as u32;
self.active_table_gen = gen;
let function_name = self.sub_modules[sub_idx] let function_name = sub
.function_name .function_name
.as_deref() .as_deref()
.map(|name| self.kernel_core.perm_interner().intern(name).0) .map(|name| self.kernel_core.perm_interner().intern(name).0)
@@ -1630,10 +1699,9 @@ impl Vm {
self.pc = 0; self.pc = 0;
self.bytecode = sub_bytecode; self.bytecode = sub_bytecode;
let subs = Arc::clone(&self.sub_modules); self.activate_immutables(gen, sub_idx, &sub.constants);
self.activate_immutables(sub_idx, &subs[sub_idx].constants); self.cell_stack.push(Vec::with_capacity(sub.cells_needed as usize));
self.cell_stack.push(Vec::with_capacity(subs[sub_idx].cells_needed as usize)); for (name, reg) in &sub.builtin_reg_map.clone() {
for (name, reg) in &self.sub_modules[sub_idx].builtin_reg_map.clone() {
let si = self.kernel_core.perm_interner().intern(name.as_str()).0; let si = self.kernel_core.perm_interner().intern(name.as_str()).0;
let global = self.session.global_object(); let global = self.session.global_object();
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) { if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) {
@@ -2527,9 +2595,10 @@ impl oxide_runtime_api::VmHost for Vm {
fn math_rng_value(&self) -> f64 { fn math_rng_value(&self) -> f64 {
self.math_rng_value() self.math_rng_value()
} }
fn sub_module_function_name(&self, sub_idx: u16) -> String { fn sub_module_function_name(&self, gen: u32, sub_idx: u16) -> String {
self.sub_modules self.tables
.get(sub_idx as usize) .get(&gen)
.and_then(|t| t.modules.get(sub_idx as usize))
.and_then(|m| m.function_name.clone()) .and_then(|m| m.function_name.clone())
.unwrap_or_default() .unwrap_or_default()
} }
@@ -2564,16 +2633,17 @@ impl Vm {
/// 1. wrap 源码 `function anonymous(p...) { body }`,parse + compile。 /// 1. wrap 源码 `function anonymous(p...) { body }`,parse + compile。
/// 2. 取 `sub_modules[0]`(匿名函数模块),把其子树 flat_id 重编号到平表末尾 /// 2. 取 `sub_modules[0]`(匿名函数模块),把其子树 flat_id 重编号到平表末尾
/// 并重写子树内每条 `CREATE_CLOSURE` 的 imm16。 /// 并重写子树内每条 `CREATE_CLOSURE` 的 imm16。
/// 3. 同步 resize `immutables_cache`,建函数对象并设置 name/length。 /// 3. 同步扩容当前代际的常量缓存,建函数对象并设置 name/length。
/// ///
/// # 边界与前提 /// # 边界与前提
/// - 编译或解析失败返回 `Err`(由 builtin 层转 SyntaxError)。 /// - 编译或解析失败返回 `Err`(由 builtin 层转 SyntaxError)。
/// - 追加的子树原 flat_id 自 1 连续(flatten 后 1=匿名体,2…=其嵌套函数); /// - 追加的子树原 flat_id 自 1 连续(flatten 后 1=匿名体,2…=其嵌套函数);
/// 新 id = 平表长度 + (old - 1)。 /// 新 id = 平表长度 + (old - 1)。
/// - 动态函数只在本次 `run()` 内有效:下次 run 重建平表,跨 run 引用会越界。 /// - 动态函数在函数对象存活期间跨 run 有效:扩展落当前代际平表,存活函数
/// 对象按代际引用保活该表;full_reset 重建 session 后失效。
/// ///
/// # 副作用 /// # 副作用
/// - 修改 `self.sub_modules` 与 `self.immutables_cache`。 /// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。
pub fn create_dynamic_function(&mut self, params: &[String], body: &str) -> Result<JsValue, String> { pub fn create_dynamic_function(&mut self, params: &[String], body: &str) -> Result<JsValue, String> {
// wrap 源码:末尾换行防止 body 以行注释结尾吞掉右花括号。 // wrap 源码:末尾换行防止 body 以行注释结尾吞掉右花括号。
let params_str = params.join(", "); let params_str = params.join(", ");
@@ -2588,14 +2658,15 @@ impl Vm {
// (ES 动态函数把非末位实参以逗号连接成参数串再解析)。 // (ES 动态函数把非末位实参以逗号连接成参数串再解析)。
let formal_count = anonymous.n_args as i32; let formal_count = anonymous.n_args as i32;
// 子树重编号 + 追加:base = 当前平表长度,DFS 前序压入,push 序即新 flat_id。 // 子树重编号 + 追加:base = 当前代际平表长度,DFS 前序压入,push 序即新
let base = self.sub_modules.len() as u32; // flat_id。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。
let table = self.current_table_mut();
let base = table.modules.len() as u32;
let mut added = Vec::new(); let mut added = Vec::new();
rehome_subtree(&anonymous, base, &mut added); rehome_subtree(&anonymous, base, &mut added);
Arc::make_mut(&mut self.sub_modules).extend(added); Arc::make_mut(&mut table.modules).extend(added);
// 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。 // 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。
self.immutables_cache table.immutables.resize(table.modules.len(), OnceLock::new());
.extend((0..self.sub_modules.len().saturating_sub(self.immutables_cache.len())).map(|_| OnceLock::new()));
let func_val = self.create_function_object(base, false, false, false, false); let func_val = self.create_function_object(base, false, false, false, false);
let func_obj = unsafe { &mut *func_val.as_js_object_ptr() }; let func_obj = unsafe { &mut *func_val.as_js_object_ptr() };
@@ -2618,15 +2689,15 @@ impl Vm {
/// Compiler 置 is_eval_script=true)。 /// Compiler 置 is_eval_script=true)。
/// 2. 整棵模块树(根 flat_id=0 + 嵌套函数)追加进平表:`rehome_subtree(&module, base+1)`, /// 2. 整棵模块树(根 flat_id=0 + 嵌套函数)追加进平表:`rehome_subtree(&module, base+1)`,
/// 使根落 base、子函数 old→base+old,CREATE_CLOSURE imm16 同步重写。 /// 使根落 base、子函数 old→base+old,CREATE_CLOSURE imm16 同步重写。
/// 3. 扩容 immutables_cache,建函数对象(sub_module_index = base)返回。 /// 3. 扩容当前代际的常量缓存,建函数对象(sub_module_index = base)返回。
/// ///
/// # 边界与前提 /// # 边界与前提
/// - 顶层 return 不报 SyntaxError(emit 无此检查,与 CLI 脚本路径一致)——已知偏差。 /// - 顶层 return 不报 SyntaxError(emit 无此检查,与 CLI 脚本路径一致)——已知偏差。
/// - 动态模块只在本次 run() 内有效(同 create_dynamic_function)。 /// - 动态模块在函数对象存活期间跨 run 有效(同 create_dynamic_function)。
/// - 返回函数对象仅供内部同步调用,不设 name/length(用户不可见)。 /// - 返回函数对象仅供内部同步调用,不设 name/length(用户不可见)。
/// ///
/// # 副作用 /// # 副作用
/// - 修改 `self.sub_modules` 与 `self.immutables_cache`。 /// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。
pub fn create_dynamic_script(&mut self, code: &str) -> Result<JsValue, String> { pub fn create_dynamic_script(&mut self, code: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, code) let program = oxide_parser::parse(&allocator, code)
@@ -2635,14 +2706,15 @@ impl Vm {
let module = oxide_compiler::compiler::Compiler::new() let module = oxide_compiler::compiler::Compiler::new()
.with_eval_script(true) .with_eval_script(true)
.compile(&program)?; .compile(&program)?;
let base = self.sub_modules.len() as u32; // 根模块 flat_id=0 传 base+1,重编号后落 base(避开 sub_module_index()==0
// 守卫)。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。
let table = self.current_table_mut();
let base = table.modules.len() as u32;
let mut added = Vec::new(); let mut added = Vec::new();
// 根模块 flat_id=0 传 base+1,重编号后落 base(避开 sub_module_index()==0 守卫)。
rehome_subtree(&module, base + 1, &mut added); rehome_subtree(&module, base + 1, &mut added);
Arc::make_mut(&mut self.sub_modules).extend(added); Arc::make_mut(&mut table.modules).extend(added);
// 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。 // 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。
self.immutables_cache table.immutables.resize(table.modules.len(), OnceLock::new());
.extend((0..self.sub_modules.len().saturating_sub(self.immutables_cache.len())).map(|_| OnceLock::new()));
Ok(self.create_function_object(base, false, false, false, false)) Ok(self.create_function_object(base, false, false, false, false))
} }
} }
@@ -2674,6 +2746,17 @@ fn rehome_subtree(module: &CompiledModule, base: u32, out: &mut Vec<Arc<Compiled
} }
} }
#[cfg(test)]
impl Vm {
/// 测试用:整表替换当前代际的子模块平表并同步常量缓存槽位(绕过 run() 装载,
/// 直接注入模块表后压帧/内联调用)。
pub(crate) fn install_module_table_for_test(&mut self, modules: Arc<Vec<Arc<CompiledModule>>>) {
let table = self.current_table_mut();
table.modules = modules;
table.immutables.resize(table.modules.len(), OnceLock::new());
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{opcode, ForOfEntry, JsValue, TryHandler, Vm}; use super::{opcode, ForOfEntry, JsValue, TryHandler, Vm};
@@ -2681,7 +2764,7 @@ mod tests {
use oxide_runtime_api::{NativeResult, VmHost}; use oxide_runtime_api::{NativeResult, VmHost};
use oxide_types::object::NativeFnPtr; use oxide_types::object::NativeFnPtr;
use oxide_types::object::{JsObject, PropAttributes}; use oxide_types::object::{JsObject, PropAttributes};
use std::sync::{Arc, OnceLock}; use std::sync::Arc;
fn native_return_7(_vm: &mut Vm, _args: &[u8]) -> NativeResult { fn native_return_7(_vm: &mut Vm, _args: &[u8]) -> NativeResult {
NativeResult::Ok(JsValue::int(7)) NativeResult::Ok(JsValue::int(7))
@@ -3111,9 +3194,10 @@ mod tests {
// 且调用方 active_reg_limit = 254(regs[253] 为活动值)时,调用后 // 且调用方 active_reg_limit = 254(regs[253] 为活动值)时,调用后
// regs[253] 必须恢复为调用方值,不得被 callee 写值覆盖。 // regs[253] 必须恢复为调用方值,不得被 callee 写值覆盖。
let mut vm = Vm::new(); let mut vm = Vm::new();
vm.sub_modules = Arc::new(vec![Arc::new(CompiledModule::new()), Arc::new(sub_module_254_with_r253_write())]); vm.install_module_table_for_test(Arc::new(vec![
vm.immutables_cache Arc::new(CompiledModule::new()),
.extend((0..vm.sub_modules.len().saturating_sub(vm.immutables_cache.len())).map(|_| OnceLock::new())); Arc::new(sub_module_254_with_r253_write()),
]));
vm.active_reg_limit = 254; vm.active_reg_limit = 254;
vm.regs[253] = JsValue::int(42); vm.regs[253] = JsValue::int(42);
@@ -3133,9 +3217,10 @@ mod tests {
// 嵌套回归防线:native 回调体(receiver 落 regs[253])内嵌 n_registers=254 // 嵌套回归防线:native 回调体(receiver 落 regs[253])内嵌 n_registers=254
// 的 inline 字节码回调时,内层写 regs[253] 不得污染外层 receiver 槽。 // 的 inline 字节码回调时,内层写 regs[253] 不得污染外层 receiver 槽。
let mut vm = Vm::new(); let mut vm = Vm::new();
vm.sub_modules = Arc::new(vec![Arc::new(CompiledModule::new()), Arc::new(sub_module_254_with_r253_write())]); vm.install_module_table_for_test(Arc::new(vec![
vm.immutables_cache Arc::new(CompiledModule::new()),
.extend((0..vm.sub_modules.len().saturating_sub(vm.immutables_cache.len())).map(|_| OnceLock::new())); Arc::new(sub_module_254_with_r253_write()),
]));
vm.active_reg_limit = 254; vm.active_reg_limit = 254;
vm.regs[253] = JsValue::int(7); vm.regs[253] = JsValue::int(7);
vm.regs[254] = JsValue::int(8); vm.regs[254] = JsValue::int(8);
@@ -3163,9 +3248,7 @@ mod tests {
callee_mod.param_base = 2; callee_mod.param_base = 2;
callee_mod.n_registers = 5; callee_mod.n_registers = 5;
callee_mod.bytecode = Arc::from(vec![opcode::encode(opcode::OpCode::RETURN, 0, 0, 0)]); callee_mod.bytecode = Arc::from(vec![opcode::encode(opcode::OpCode::RETURN, 0, 0, 0)]);
vm.sub_modules = Arc::new(vec![Arc::new(CompiledModule::new()), Arc::new(callee_mod)]); vm.install_module_table_for_test(Arc::new(vec![Arc::new(CompiledModule::new()), Arc::new(callee_mod)]));
vm.immutables_cache
.extend((0..vm.sub_modules.len().saturating_sub(vm.immutables_cache.len())).map(|_| OnceLock::new()));
vm.active_reg_limit = 8; vm.active_reg_limit = 8;
// 调用方实参区 regs[1..3):arg0=10, arg1=20;regs[2] 同时是 callee 形参槽(param_base=2) // 调用方实参区 regs[1..3):arg0=10, arg1=20;regs[2] 同时是 callee 形参槽(param_base=2)
vm.regs[1] = JsValue::int(10); vm.regs[1] = JsValue::int(10);
@@ -3205,9 +3288,7 @@ mod tests {
ctor_mod.param_base = 2; ctor_mod.param_base = 2;
ctor_mod.n_registers = 5; ctor_mod.n_registers = 5;
ctor_mod.bytecode = Arc::from(vec![opcode::encode(opcode::OpCode::RETURN, 0, 0, 0)]); ctor_mod.bytecode = Arc::from(vec![opcode::encode(opcode::OpCode::RETURN, 0, 0, 0)]);
vm.sub_modules = Arc::new(vec![Arc::new(CompiledModule::new()), Arc::new(ctor_mod)]); vm.install_module_table_for_test(Arc::new(vec![Arc::new(CompiledModule::new()), Arc::new(ctor_mod)]));
vm.immutables_cache
.extend((0..vm.sub_modules.len().saturating_sub(vm.immutables_cache.len())).map(|_| OnceLock::new()));
vm.active_reg_limit = 8; vm.active_reg_limit = 8;
// NEW_EXPRESSION 指令:ext 低 8 位 = 实参个数 2,高 8 位 = 窗口 0(全量) // NEW_EXPRESSION 指令:ext 低 8 位 = 实参个数 2,高 8 位 = 窗口 0(全量)
vm.bytecode = Arc::from(vec![opcode::encode(opcode::OpCode::NEW_EXPRESSION, 0, 0, 0), 2]); vm.bytecode = Arc::from(vec![opcode::encode(opcode::OpCode::NEW_EXPRESSION, 0, 0, 0), 2]);
+5 -3
View File
@@ -33,10 +33,12 @@ impl Vm {
self.dispatch_native_call(obj, callee, this_reg, first_arg_reg, arg_count)?; self.dispatch_native_call(obj, callee, this_reg, first_arg_reg, arg_count)?;
return Ok(true); return Ok(true);
} else if obj.sub_module_index() > 0 { } else if obj.sub_module_index() > 0 {
let sub_idx = obj.sub_module_index() as usize;
let this_value = self.regs[this_reg as usize]; let this_value = self.regs[this_reg as usize];
let is_generator = sub_idx < self.sub_modules.len() && self.sub_modules[sub_idx].is_generator; // 按函数对象自身记录的表代际解析(跨 run 调用旧代模块时
let is_async = sub_idx < self.sub_modules.len() && self.sub_modules[sub_idx].is_async; // 当前 run 平表不指其下标)。
let sub = self.callee_module(obj);
let is_generator = sub.map(|m| m.is_generator).unwrap_or(false);
let is_async = sub.map(|m| m.is_async).unwrap_or(false);
// 生成器/异步路径需把实参物化存进状态盒;普通字节码调用直接 // 生成器/异步路径需把实参物化存进状态盒;普通字节码调用直接
// 引用寄存器连续区间,免临时堆 Vec(每次调用省 1 次分配)。 // 引用寄存器连续区间,免临时堆 Vec(每次调用省 1 次分配)。
if is_generator || is_async { if is_generator || is_async {
+19 -17
View File
@@ -102,19 +102,22 @@ impl Vm {
} }
} else if ctor_obj.sub_module_index() > 0 { } else if ctor_obj.sub_module_index() > 0 {
let sub_idx = ctor_obj.sub_module_index() as usize; let sub_idx = ctor_obj.sub_module_index() as usize;
if sub_idx >= self.sub_modules.len() { let sub = match self.callee_module(ctor_obj) {
return Err(format!( Some(m) => m,
"NEW_EXPRESSION: sub_module_index {} out of bounds (max {})", None => {
sub_idx, return Err(format!(
self.sub_modules.len() "NEW_EXPRESSION: sub_module_index {} out of bounds (max {})",
)); sub_idx,
} self.current_table().modules.len()
))
}
};
// 生成器函数不是构造器:`new g()` 抛 TypeError。 // 生成器函数不是构造器:`new g()` 抛 TypeError。
if self.sub_modules[sub_idx].is_generator { if sub.is_generator {
return self.raise_type_error("g is not a constructor").map(|_| true); return self.raise_type_error("g is not a constructor").map(|_| true);
} }
// 异步函数不是构造器:`new f()` 抛 TypeError。 // 异步函数不是构造器:`new f()` 抛 TypeError。
if self.sub_modules[sub_idx].is_async { if sub.is_async {
return self.raise_type_error("g is not a constructor").map(|_| true); return self.raise_type_error("g is not a constructor").map(|_| true);
} }
@@ -199,12 +202,9 @@ impl Vm {
if target_obj.native_fn().is_some() && target_obj.type_tag != JsObject::OBJ_TYPE_CONSTRUCTOR { if target_obj.native_fn().is_some() && target_obj.type_tag != JsObject::OBJ_TYPE_CONSTRUCTOR {
return self.raise_type_error("object is not a constructor").map(|_| true); return self.raise_type_error("object is not a constructor").map(|_| true);
} }
let sub_idx = target_obj.sub_module_index() as usize; // 按 target 自身记录的表代际解析:生成器/异步函数不是构造器。
if sub_idx > 0 && sub_idx < self.sub_modules.len() { if matches!(self.callee_module(target_obj), Some(sub) if sub.is_generator || sub.is_async) {
let sub = &self.sub_modules[sub_idx]; return self.raise_type_error("object is not a constructor").map(|_| true);
if sub.is_generator || sub.is_async {
return self.raise_type_error("object is not a constructor").map(|_| true);
}
} }
// 新对象原型取最内层 target 的 prototype(bound 包装自身无 prototype)。 // 新对象原型取最内层 target 的 prototype(bound 包装自身无 prototype)。
@@ -235,7 +235,7 @@ impl Vm {
self.unwind().map(|_| true) self.unwind().map(|_| true)
} }
} }
} else if sub_idx > 0 { } else if target_obj.sub_module_index() > 0 {
// 字节码构造器:this = 新对象(派生 target 为 undefined,super() 装配), // 字节码构造器:this = 新对象(派生 target 为 undefined,super() 装配),
// new.target = 最内层 target。 // new.target = 最内层 target。
let this_value = if target_obj.is_derived_constructor() { let this_value = if target_obj.is_derived_constructor() {
@@ -1043,7 +1043,9 @@ impl Vm {
let site_no = self.bytecode[self.pc]; let site_no = self.bytecode[self.pc];
self.pc += 1; self.pc += 1;
let key = (self.active_flat_id, site_no); // 键含表代际:跨 run 调用的旧代模块与当前 run 模块 flat_id 重编号,
// 无代际维度会误命中他代同 (flat_id, site) 的模板对象。
let key = (self.active_table_gen, self.active_flat_id, site_no);
if let Some(&cached) = self.template_objects.get(&key) { if let Some(&cached) = self.template_objects.get(&key) {
self.regs[rd] = cached; self.regs[rd] = cached;
return Ok(()); return Ok(());
+76 -17
View File
@@ -2,7 +2,7 @@ use std::sync::{Arc, OnceLock};
use oxide_bytecode::module::CompiledModule; use oxide_bytecode::module::CompiledModule;
use crate::vm::{CallFrame, FrameArgs, FrameContinuation, InlineSyncState, Vm}; use crate::vm::{CallFrame, FrameArgs, FrameContinuation, InlineSyncState, TableGen, Vm};
use crate::{vm_debug, vm_info, vm_trace, vm_warn}; use crate::{vm_debug, vm_info, vm_trace, vm_warn};
use oxide_types::object::JsObject; use oxide_types::object::JsObject;
use oxide_types::value::JsValue; use oxide_types::value::JsValue;
@@ -134,6 +134,9 @@ macro_rules! inline_save_field {
($recv:ident, $window_regs:ident, active_flat_id, copy) => { ($recv:ident, $window_regs:ident, active_flat_id, copy) => {
$recv.active_flat_id $recv.active_flat_id
}; };
($recv:ident, $window_regs:ident, active_table_gen, copy) => {
$recv.active_table_gen
};
} }
/// restore 方向的字段写回语句。`regs` 只回拷窗口并把缓冲归还池。 /// restore 方向的字段写回语句。`regs` 只回拷窗口并把缓冲归还池。
@@ -232,6 +235,9 @@ macro_rules! inline_restore_field {
($recv:ident, $saved:ident, active_flat_id, copy) => { ($recv:ident, $saved:ident, active_flat_id, copy) => {
$recv.active_flat_id = $saved.active_flat_id $recv.active_flat_id = $saved.active_flat_id
}; };
($recv:ident, $saved:ident, active_table_gen, copy) => {
$recv.active_table_gen = $saved.active_table_gen
};
} }
/// 内联同步调用可搬移执行核心字段的单一登记表。save/restore 双向由本宏展开; /// 内联同步调用可搬移执行核心字段的单一登记表。save/restore 双向由本宏展开;
@@ -274,6 +280,7 @@ macro_rules! inline_core_fields {
(inline_args_count, copy), // M (inline_args_count, copy), // M
(accessor_frame_target_reg, copy), // M (accessor_frame_target_reg, copy), // M
(active_flat_id, copy), // M (active_flat_id, copy), // M
(active_table_gen, copy), // M
) )
}; };
} }
@@ -367,17 +374,22 @@ impl Vm {
return Err(self.error_message_text("TypeError", "accessor is not callable")); return Err(self.error_message_text("TypeError", "accessor is not callable"));
} }
let sub_idx = callee_obj.sub_module_index() as usize; let sub_idx = callee_obj.sub_module_index() as usize;
let gen = callee_obj.table_gen();
vm_debug!( vm_debug!(
"call_bytecode_function_inline: sub_idx={}, args={}, depth={}", "call_bytecode_function_inline: sub_idx={}, args={}, depth={}",
sub_idx, sub_idx,
args.len(), args.len(),
self.frames.len() self.frames.len()
); );
if sub_idx >= self.sub_modules.len() { let table = match self.tables.get(&gen) {
Some(t) => t,
None => return Err(format!("accessor: module table gen {} not available", gen)),
};
if sub_idx >= table.modules.len() {
return Err(format!( return Err(format!(
"accessor sub_module_index {} out of bounds (max {})", "accessor sub_module_index {} out of bounds (max {})",
sub_idx, sub_idx,
self.sub_modules.len() table.modules.len()
)); ));
} }
if self.frames.len() >= self.kernel_core.config.max_call_depth { if self.frames.len() >= self.kernel_core.config.max_call_depth {
@@ -388,23 +400,23 @@ impl Vm {
self.raise_error_kind("RangeError", "Maximum call stack size exceeded")?; self.raise_error_kind("RangeError", "Maximum call stack size exceeded")?;
return Ok(JsValue::undefined()); return Ok(JsValue::undefined());
} }
// 被调模块取 Arc 克隆(与调用方共享同一编译产物):后续 &mut self
// 调用期间不持有对注册表的借用。
let sub = Arc::clone(&table.modules[sub_idx]);
// 异步生成器函数调用返回异步生成器迭代器对象,next/return/throw 返回 Promise。 // 异步生成器函数调用返回异步生成器迭代器对象,next/return/throw 返回 Promise。
if self.sub_modules[sub_idx].is_generator && self.sub_modules[sub_idx].is_async { if sub.is_generator && sub.is_async {
return self.create_async_generator_object(callee, receiver, args); return self.create_async_generator_object(callee, receiver, args);
} }
// 生成器函数调用返回迭代器对象,不执行函数体。 // 生成器函数调用返回迭代器对象,不执行函数体。
if self.sub_modules[sub_idx].is_generator { if sub.is_generator {
return self.create_generator_object(callee, receiver, args); return self.create_generator_object(callee, receiver, args);
} }
// 异步函数调用返回 capability promise,立即同步执行 body 到首个 await。 // 异步函数调用返回 capability promise,立即同步执行 body 到首个 await。
if self.sub_modules[sub_idx].is_async { if sub.is_async {
return self.create_async_object(callee, receiver, args); return self.create_async_object(callee, receiver, args);
} }
self.native_call_depth += 1; self.native_call_depth += 1;
let subs = Arc::clone(&self.sub_modules);
let sub = &subs[sub_idx];
vm_trace!("call_bytecode: saving state pc={} depth={}", self.pc, self.frames.len()); vm_trace!("call_bytecode: saving state pc={} depth={}", self.pc, self.frames.len());
// 窗口 = max(调用方活跃寄存器, callee 寄存器数):restore 只回拷窗口,正好 // 窗口 = max(调用方活跃寄存器, callee 寄存器数):restore 只回拷窗口,正好
// 覆盖 callee 写入区(regs[0..n_registers] + 254/255)与调用方活动区。 // 覆盖 callee 写入区(regs[0..n_registers] + 254/255)与调用方活动区。
@@ -418,8 +430,9 @@ impl Vm {
} }
self.pc = 0; self.pc = 0;
self.bytecode = Arc::clone(&sub.bytecode); self.bytecode = Arc::clone(&sub.bytecode);
self.activate_immutables(sub_idx, &sub.constants); self.activate_immutables(gen, sub_idx, &sub.constants);
self.active_flat_id = sub_idx as u32; self.active_flat_id = sub_idx as u32;
self.active_table_gen = gen;
self.active_reg_limit = sub.n_registers.max(1); self.active_reg_limit = sub.n_registers.max(1);
self.root_reg_limit = self.active_reg_limit; self.root_reg_limit = self.active_reg_limit;
self.cell_stack.push(Vec::with_capacity(sub.cells_needed as usize)); self.cell_stack.push(Vec::with_capacity(sub.cells_needed as usize));
@@ -482,6 +495,9 @@ impl Vm {
if let Some(saved_flat) = self.saved_flat_id_stack.pop() { if let Some(saved_flat) = self.saved_flat_id_stack.pop() {
self.active_flat_id = saved_flat; self.active_flat_id = saved_flat;
} }
if let Some(saved_gen) = self.saved_table_gen_stack.pop() {
self.active_table_gen = saved_gen;
}
vm_debug!( vm_debug!(
"restore_frame: return_addr={} bc_len={} saved_stack={} fn={:?}", "restore_frame: return_addr={} bc_len={} saved_stack={} fn={:?}",
frame.return_addr, frame.return_addr,
@@ -517,6 +533,34 @@ impl Vm {
self.dispatch() self.dispatch()
} }
/// run 边界回收无存活函数对象引用的子模块表代际:扫 session 对象表收
/// 存活函数对象引用的表代际集,注册表中其余代际 drop。表内存由此有界于
/// 存活函数对象数,不随 run 数单调增;存活函数按创建期代际仍命中原表。
///
/// # 注意事项
/// - 须在换表注册新代际**之前**调用(此刻 current_gen 仍指上一 run 的表,
/// 新表未注册天然不被回收)。
/// - 执行外安全点调用(dispatch 未重入、无在途 builtin 局部裸指针),与
/// `promote_session_epoch_refs` 同前提:session 对象表此刻全部有效。
pub(crate) fn reclaim_unreferenced_tables(&mut self) {
// 存活代际集:函数对象只经创建点直落 session 对象表登记(或晋升克隆
// 随行),扫全表即完备;native 函数哨兵 sub_module_index == 0 不计数。
let mut live_gens: Vec<u32> = Vec::new();
for &ptr in &self.gc_state.session_object_ptrs {
if ptr.is_null() {
continue;
}
// SAFETY: session 对象表此刻未清空,指针指向 session arena 内合法对象。
let obj = unsafe { &*ptr };
if obj.is_function() && obj.sub_module_index() != 0 {
live_gens.push(obj.table_gen());
}
}
// 当前代际恒保留(本 run 执行仍在用,即便尚无函数引用)。
live_gens.push(self.current_gen);
self.tables.retain(|gen, _| live_gens.contains(gen));
}
/// 加载并执行一个已编译模块,返回模块顶层执行结果或未捕获异常消息。 /// 加载并执行一个已编译模块,返回模块顶层执行结果或未捕获异常消息。
/// ///
/// 模块以 `Arc` 与调用方共享(如 CodeForge 缓存条目):平表装载只做 /// 模块以 `Arc` 与调用方共享(如 CodeForge 缓存条目):平表装载只做
@@ -525,19 +569,34 @@ impl Vm {
pub fn run(&mut self, module: &Arc<CompiledModule>) -> Result<JsValue, String> { pub fn run(&mut self, module: &Arc<CompiledModule>) -> Result<JsValue, String> {
vm_debug!("run: starting bytecode execution, {} instructions", module.bytecode.len()); vm_debug!("run: starting bytecode execution, {} instructions", module.bytecode.len());
self.clear_execution_state(); self.clear_execution_state();
// 模板对象缓存按 run 清空:flat_id 每次 run 从 0 重新分配,跨 run 复用会 // 模板对象缓存按 run 清空:缓存只留本次 run 的条目,规模有界(键含代际
// 让不同编译树的 site 误命中(缓存键 = (flat_id, site_no))。 // 维度后跨 run 不再误命中,清空仅为规模约束)。
self.template_objects.clear(); self.template_objects.clear();
self.saved_flat_id_stack.clear(); self.saved_flat_id_stack.clear();
self.saved_table_gen_stack.clear();
self.active_flat_id = 0; self.active_flat_id = 0;
self.cell_stack.clear(); self.cell_stack.clear();
self.cell_stack.push(Vec::new()); self.cell_stack.push(Vec::new());
self.sub_modules = Arc::new(collect_flat_modules(module)); // run 边界换表前先回收无存活函数对象引用的表代际(此刻 current_gen 仍
self.immutables_cache = (0..self.sub_modules.len()).map(|_| OnceLock::new()).collect(); // 指上一 run 的表,存活代际集由 session 对象表扫描得出)。
self.active_immutables = std::ptr::slice_from_raw_parts(std::ptr::null(), 0);
self.reclaim_unreferenced_tables();
// 新表注册于 current_gen + 1:本 run 创建的函数对象记录该代际,跨 run
// 调用按创建期代际解析。
self.current_gen = self.current_gen.wrapping_add(1);
let modules = Arc::new(collect_flat_modules(module));
self.tables.insert(
self.current_gen,
Box::new(TableGen {
immutables: (0..modules.len()).map(|_| OnceLock::new()).collect(),
modules,
}),
);
self.active_table_gen = self.current_gen;
// 顶层脚本严格模式:无帧且无 inline 时写路径的 strict/sloppy 判定来源。 // 顶层脚本严格模式:无帧且无 inline 时写路径的 strict/sloppy 判定来源。
self.top_level_strict = module.is_strict; self.top_level_strict = module.is_strict;
self.bytecode = Arc::clone(&module.bytecode); self.bytecode = Arc::clone(&module.bytecode);
self.activate_immutables(0, &module.constants); self.activate_immutables(self.current_gen, 0, &module.constants);
self.root_reg_limit = module.n_registers.max(1); self.root_reg_limit = module.n_registers.max(1);
self.active_reg_limit = self.root_reg_limit; self.active_reg_limit = self.root_reg_limit;
@@ -650,10 +709,10 @@ mod tests {
let mut vm = Vm::new(); let mut vm = Vm::new();
let module = Arc::new(compile("var o = { a: 1 }; o.a + o.a")); let module = Arc::new(compile("var o = { a: 1 }; o.a + o.a"));
vm.run(&module).expect("run1"); vm.run(&module).expect("run1");
assert!(Arc::ptr_eq(&vm.sub_modules[0], &module), "顶层条目须与宿主 Arc 同一实例"); assert!(Arc::ptr_eq(&vm.current_table().modules[0], &module), "顶层条目须与宿主 Arc 同一实例");
vm.full_reset(); vm.full_reset();
vm.run(&module).expect("run2"); vm.run(&module).expect("run2");
assert!(Arc::ptr_eq(&vm.sub_modules[0], &module), "full_reset 后二次 run 仍共享"); assert!(Arc::ptr_eq(&vm.current_table().modules[0], &module), "full_reset 后二次 run 仍共享");
} }
#[test] #[test]
+56 -17
View File
@@ -6,7 +6,7 @@ use std::sync::Arc;
use oxide_bytecode::module::Constant; use oxide_bytecode::module::Constant;
use crate::bindings; use crate::bindings;
use crate::vm::Vm; use crate::vm::{TableGen, Vm};
use crate::vm_info; use crate::vm_info;
use crate::vm_state::{GcState, IterState, ProfilingState, SymbolState}; use crate::vm_state::{GcState, IterState, ProfilingState, SymbolState};
use oxide_kernel::kernel::{KernelConfig, KernelCore, KernelSession}; use oxide_kernel::kernel::{KernelConfig, KernelCore, KernelSession};
@@ -48,7 +48,6 @@ impl Vm {
regs: [JsValue::undefined(); 256], regs: [JsValue::undefined(); 256],
pc: 0, pc: 0,
bytecode: Arc::default(), bytecode: Arc::default(),
immutables_cache: Vec::new(),
active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0), active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0),
frames: smallvec::SmallVec::new(), frames: smallvec::SmallVec::new(),
kernel_core: core, kernel_core: core,
@@ -67,7 +66,16 @@ impl Vm {
async_generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), async_generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
job_queue: VecDeque::new(), job_queue: VecDeque::new(),
math_rng_state: 0, math_rng_state: 0,
sub_modules: Arc::new(Vec::new()), // gen 0 预登记空表占位:函数对象恒在 run 内创建(彼时 current_gen
// ≥ 1),gen 0 表只是首 run 前路径的占位,首 run 边界即被回收。
tables: std::collections::HashMap::from([(
0u32,
Box::new(TableGen {
modules: Arc::new(Vec::new()),
immutables: Vec::new(),
}),
)]),
current_gen: 0,
saved_bytecode_stack: Vec::new(), saved_bytecode_stack: Vec::new(),
saved_immutables_stack: Vec::new(), saved_immutables_stack: Vec::new(),
save_stack: Vec::new(), save_stack: Vec::new(),
@@ -138,7 +146,9 @@ impl Vm {
cell_stack: Vec::new(), cell_stack: Vec::new(),
template_objects: std::collections::HashMap::new(), template_objects: std::collections::HashMap::new(),
active_flat_id: 0, active_flat_id: 0,
active_table_gen: 0,
saved_flat_id_stack: Vec::new(), saved_flat_id_stack: Vec::new(),
saved_table_gen_stack: Vec::new(),
}; };
vm.init_generator_intrinsics(); vm.init_generator_intrinsics();
vm.init_promise_intrinsics(); vm.init_promise_intrinsics();
@@ -166,7 +176,6 @@ impl Vm {
regs: [JsValue::undefined(); 256], regs: [JsValue::undefined(); 256],
pc: 0, pc: 0,
bytecode: Arc::default(), bytecode: Arc::default(),
immutables_cache: Vec::new(),
active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0), active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0),
frames: smallvec::SmallVec::new(), frames: smallvec::SmallVec::new(),
kernel_core: core, kernel_core: core,
@@ -185,7 +194,16 @@ impl Vm {
async_generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), async_generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
job_queue: VecDeque::new(), job_queue: VecDeque::new(),
math_rng_state: 0, math_rng_state: 0,
sub_modules: Arc::new(Vec::new()), // gen 0 预登记空表占位:函数对象恒在 run 内创建(彼时 current_gen
// ≥ 1),gen 0 表只是首 run 前路径的占位,首 run 边界即被回收。
tables: std::collections::HashMap::from([(
0u32,
Box::new(TableGen {
modules: Arc::new(Vec::new()),
immutables: Vec::new(),
}),
)]),
current_gen: 0,
saved_bytecode_stack: Vec::new(), saved_bytecode_stack: Vec::new(),
saved_immutables_stack: Vec::new(), saved_immutables_stack: Vec::new(),
save_stack: Vec::new(), save_stack: Vec::new(),
@@ -256,7 +274,9 @@ impl Vm {
cell_stack: Vec::new(), cell_stack: Vec::new(),
template_objects: std::collections::HashMap::new(), template_objects: std::collections::HashMap::new(),
active_flat_id: 0, active_flat_id: 0,
active_table_gen: 0,
saved_flat_id_stack: Vec::new(), saved_flat_id_stack: Vec::new(),
saved_table_gen_stack: Vec::new(),
}; };
vm.init_generator_intrinsics(); vm.init_generator_intrinsics();
vm.init_promise_intrinsics(); vm.init_promise_intrinsics();
@@ -344,9 +364,21 @@ impl Vm {
// 必须随 session 一并清空。 // 必须随 session 一并清空。
self.template_objects.clear(); self.template_objects.clear();
self.saved_flat_id_stack.clear(); self.saved_flat_id_stack.clear();
self.saved_table_gen_stack.clear();
self.active_flat_id = 0; self.active_flat_id = 0;
self.active_table_gen = 0;
self.bytecode = Arc::default(); self.bytecode = Arc::default();
self.immutables_cache.clear(); // 注册表随 session 重建整体清空:session 对象(唯一按代际保活表者)全部
// 消失,残留表无引用源。回到构造期同态:gen 0 空表占位 + current_gen = 0。
self.tables.clear();
self.current_gen = 0;
self.tables.insert(
0,
Box::new(TableGen {
modules: Arc::new(Vec::new()),
immutables: Vec::new(),
}),
);
self.active_immutables = std::ptr::slice_from_raw_parts(std::ptr::null(), 0); self.active_immutables = std::ptr::slice_from_raw_parts(std::ptr::null(), 0);
self.teardown_session_heap_data(); self.teardown_session_heap_data();
self.epoch.reset(); self.epoch.reset();
@@ -508,7 +540,9 @@ impl Vm {
// 重置前把 epoch 子引用原地克隆晋升进 session,避免悬垂指针。 // 重置前把 epoch 子引用原地克隆晋升进 session,避免悬垂指针。
self.promote_session_epoch_refs(); self.promote_session_epoch_refs();
self.bytecode = Arc::default(); self.bytecode = Arc::default();
self.immutables_cache.clear(); // 表代际注册表不动:存活函数对象(含挂起帧 callee)按创建期代际仍须
// 命中原表,跨 run 调用与恢复靠它成立。active_immutables 指向的旧表
// 指针作废,下次 run 重装。
self.active_immutables = std::ptr::slice_from_raw_parts(std::ptr::null(), 0); self.active_immutables = std::ptr::slice_from_raw_parts(std::ptr::null(), 0);
self.free_epoch_dead_upvalues(); self.free_epoch_dead_upvalues();
self.free_epoch_object_heap_data(); self.free_epoch_object_heap_data();
@@ -659,8 +693,11 @@ impl Vm {
) -> JsValue { ) -> JsValue {
// 生成器函数对象:原型为 %GeneratorFunction.prototype%(constructor 链解析到 // 生成器函数对象:原型为 %GeneratorFunction.prototype%(constructor 链解析到
// "GeneratorFunction"),且不像普通函数那样拥有 `prototype` 属性。 // "GeneratorFunction"),且不像普通函数那样拥有 `prototype` 属性。
let is_generator = self.sub_modules.get(sub_idx as usize).map(|m| m.is_generator).unwrap_or(false); // 按当前代际平表解析:函数对象恒在本次 run 内创建(CREATE_CLOSURE /
let is_async = self.sub_modules.get(sub_idx as usize).map(|m| m.is_async).unwrap_or(false); // 动态构造),sub_idx 口径即当前代际表。
let table = self.current_table();
let is_generator = table.modules.get(sub_idx as usize).map(|m| m.is_generator).unwrap_or(false);
let is_async = table.modules.get(sub_idx as usize).map(|m| m.is_async).unwrap_or(false);
// 异步生成器(`async function*`)函数对象:原型为 %AsyncGeneratorFunction.prototype%。 // 异步生成器(`async function*`)函数对象:原型为 %AsyncGeneratorFunction.prototype%。
let is_async_generator = is_generator && is_async; let is_async_generator = is_generator && is_async;
let proto_val = if is_async_generator { let proto_val = if is_async_generator {
@@ -675,6 +712,9 @@ impl Vm {
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto_val); let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto_val);
obj.set_function(true); obj.set_function(true);
obj.set_sub_module_index(sub_idx); obj.set_sub_module_index(sub_idx);
// 记录创建期表代际:调用点按 (table_gen, sub_module_index) 解析子模块
// 平表,跨 run 换表后存活函数仍命中原表(注册表按代际保活)。
obj.set_table_gen(self.current_gen);
obj.set_class_constructor(is_class_constructor); obj.set_class_constructor(is_class_constructor);
obj.set_derived_constructor(is_derived_constructor); obj.set_derived_constructor(is_derived_constructor);
let _ = needs_home_object; let _ = needs_home_object;
@@ -1032,8 +1072,8 @@ mod tests {
assert!(err.contains("memory limit"), "unexpected error: {err}"); assert!(err.contains("memory limit"), "unexpected error: {err}");
} }
/// 直接恢复生成器一步:等价 `it.next()`,但避免跨 run(sub_modules 重建会使 /// 直接恢复生成器一步:等价 `it.next()`——回归聚焦 GC 搬移后的状态盒
/// 挂起帧失效)——回归聚焦 GC 搬移后的状态盒有效性。 /// 有效性(同 run 内恢复,不经 run 边界换表)。
fn resume_one_step(vm: &mut Vm, gen: JsValue) -> JsValue { fn resume_one_step(vm: &mut Vm, gen: JsValue) -> JsValue {
match vm.resume_generator(gen, crate::generator::GeneratorResumeMode::Next(JsValue::undefined())) { match vm.resume_generator(gen, crate::generator::GeneratorResumeMode::Next(JsValue::undefined())) {
Ok(crate::generator::GeneratorStep::Suspended { value }) => value, Ok(crate::generator::GeneratorStep::Suspended { value }) => value,
@@ -1056,11 +1096,11 @@ mod tests {
let _ = run_source(&mut vm, "function* g(){ yield 1; yield 2; } globalThis.it = g(); globalThis.it.next(); 0"); let _ = run_source(&mut vm, "function* g(){ yield 1; yield 2; } globalThis.it = g(); globalThis.it.next(); 0");
// 直接触发完整收集(保留执行上下文):存活生成器克隆进新 arena, // 直接触发完整收集(保留执行上下文):存活生成器克隆进新 arena,
// 状态盒深拷贝为新 Box(reset 会清空模块表使恢复不可行,走收集入口等价验证)。 // 状态盒深拷贝为新 Box(走收集入口而非 run 边界:聚焦 GC 搬移本身)。
vm.maybe_collect_session_gc(); vm.maybe_collect_session_gc();
assert!(vm.session_gc_stats().total_collections > 0, "应触发对象收集"); assert!(vm.session_gc_stats().total_collections > 0, "应触发对象收集");
// 从 global 取 sweep 重写后的生成器(sub_modules/immutables 未重建,可恢复)。 // 从 global 取 sweep 重写后的生成器(同 run,模块表未换发,可恢复)。
let it = global_prop(&vm, "it"); let it = global_prop(&vm, "it");
assert_eq!(resume_one_step(&mut vm, it), JsValue::int(2), "sweep 后应恢复第二次 yield"); assert_eq!(resume_one_step(&mut vm, it), JsValue::int(2), "sweep 后应恢复第二次 yield");
} }
@@ -1317,8 +1357,7 @@ mod tests {
/// 标签模板 cooked/raw 数组直 session 分配计入 session 堆账目: /// 标签模板 cooked/raw 数组直 session 分配计入 session 堆账目:
/// 含模板的 run 账目增量须超出同等函数对象口径(两数组各含对象头 + 元素区)。 /// 含模板的 run 账目增量须超出同等函数对象口径(两数组各含对象头 + 元素区)。
/// 单 run 内完成(标签函数定义 + 模板调用):跨 run 调用异模块函数受 /// 单 run 内完成(标签函数定义 + 模板调用):账目口径与函数对象钉同 run 对齐。
/// sub_module_index 缺口限制,不在本钉面。
#[test] #[test]
fn tagged_template_object_counted_in_session_bytes() { fn tagged_template_object_counted_in_session_bytes() {
let mut vm = Vm::new(); let mut vm = Vm::new();
@@ -1337,14 +1376,14 @@ mod tests {
} }
#[test] #[test]
fn immutables_cache_filled_once_per_module() { fn immutables_filled_once_per_module() {
let mut vm = Vm::new(); let mut vm = Vm::new();
// `f` 递归(同一子模块进入 4 次),其不可变常量经 OnceLock 只转换一次。 // `f` 递归(同一子模块进入 4 次),其不可变常量经 OnceLock 只转换一次。
let result = run_source(&mut vm, "function f(n){ if(n<=0){ return 'done'; } return f(n-1); } f(3)"); let result = run_source(&mut vm, "function f(n){ if(n<=0){ return 'done'; } return f(n-1); } f(3)");
assert!(result.is_string()); assert!(result.is_string());
assert_eq!(vm.lookup_str(result).as_deref(), Some("done")); assert_eq!(vm.lookup_str(result).as_deref(), Some("done"));
// 缓存 = 顶层模块 + 1 个子模块(f);子模块槽由这些调用初始化。 // 缓存 = 顶层模块 + 1 个子模块(f);子模块槽由这些调用初始化。
assert_eq!(vm.immutables_cache.len(), 2); assert_eq!(vm.current_table().immutables.len(), 2);
// 子模块常量改走 temp_immutables(避免缓存下标冲突) // 子模块常量改走 temp_immutables(避免缓存下标冲突)
} }
+24 -6
View File
@@ -225,8 +225,8 @@ fn full_reset_clears_session_after_runtime_gc() {
} }
/// 低阈值下 reset 触发完整对象 sweep(移动式搬移 + 重标):写全局的函数对象 /// 低阈值下 reset 触发完整对象 sweep(移动式搬移 + 重标):写全局的函数对象
/// 不被当死对象释放,run2 仍为可引用的函数值(跨 run 调用受独立的 /// 不被当死对象释放,run2 直接调用该闭包——upvalue 读回创建期值,子模块按
/// sub_module_index 缺口限制,不在此断言)。 /// 创建期表代际解析。
#[test] #[test]
fn reset_sweep_preserves_global_function_object() { fn reset_sweep_preserves_global_function_object() {
let mut vm = vm_with_threshold(1024); let mut vm = vm_with_threshold(1024);
@@ -240,7 +240,7 @@ fn reset_sweep_preserves_global_function_object() {
assert!(vm.session_gc_stats().total_collections > 0, "低阈值应触发执行期收集"); assert!(vm.session_gc_stats().total_collections > 0, "低阈值应触发执行期收集");
vm.reset(); vm.reset();
let second = compile("typeof globalThis.fn === 'function'"); let second = compile("globalThis.fn() === 9");
let result = vm.run(&Arc::new(second)).expect("run2"); let result = vm.run(&Arc::new(second)).expect("run2");
assert!(result.is_bool() && result.as_bool()); assert!(result.is_bool() && result.as_bool());
} }
@@ -289,9 +289,9 @@ fn dead_closure_upvalues_freed_by_sweep_without_double_free() {
vm.collect_session_gc(); vm.collect_session_gc();
assert!(vm.session_gc_stats().last_collection_objects_dead >= 1, "死闭包应经 sweep 死分支"); assert!(vm.session_gc_stats().last_collection_objects_dead >= 1, "死闭包应经 sweep 死分支");
// 跨 run 存活核(只读属性;跨 run 调用受 sub_module_index 缺口限制): // 跨 run 存活核:sweep 后存活闭包克隆仍挂在 global 上,直接调用读回
// sweep 后存活闭包克隆仍挂在 global 上。 // upvalue 和(子模块按创建期表代际解析)。
let third = compile("typeof globalThis.live === 'function'"); let third = compile("globalThis.live() === 6");
let result = vm.run(&Arc::new(third)).expect("run3"); let result = vm.run(&Arc::new(third)).expect("run3");
assert!(result.is_bool() && result.as_bool()); assert!(result.is_bool() && result.as_bool());
@@ -336,6 +336,24 @@ fn dead_closure_upvalues_freed_by_sweep_without_double_free() {
); );
} }
/// run 边界回收无引用的表代际:不产生存活函数对象的 run 不使注册表条目数
/// 随 run 数单调增;每 run 逃逸的函数对象钉住其创建期代际,条目数相应增长。
#[test]
fn unreferenced_table_gens_do_not_grow_with_runs() {
let mut vm = Vm::new();
for _ in 0..12 {
vm.run(&Arc::new(compile("0"))).expect("run");
}
let baseline = vm.table_gen_count();
assert!(baseline <= 2, "无存活函数对象跨 run,注册表条目应有界(≤2),实际 {baseline}");
for i in 0..5 {
let src = format!("globalThis.keep{i} = function() {{ return {i}; }}; 0");
vm.run(&Arc::new(compile(&src))).expect("run");
}
let pinned = vm.table_gen_count();
assert!(pinned > baseline, "每 run 函数对象钉住创建期代际,条目数应增长,实际 {pinned}");
}
// ── 原生盒内字符串/BigInt 边存活(mark 边收集去对象预过滤) ───────────────── // ── 原生盒内字符串/BigInt 边存活(mark 边收集去对象预过滤) ─────────────────
/// 盒持唯一引用 session 串(Promise 结算值):churn 窗口内执行期收集 /// 盒持唯一引用 session 串(Promise 结算值):churn 窗口内执行期收集
@@ -212,24 +212,44 @@ fn same_function_written_to_two_globals_is_identical() {
} }
/// 跨 run(reset)契约:函数对象直落 session,其属性持有的 epoch 对象 /// 跨 run(reset)契约:函数对象直落 session,其属性持有的 epoch 对象
/// (prototype 槽)在 epoch 清空前被晋升,run2 经全局别名可读其属性。 /// (prototype 槽)在 epoch 清空前被晋升;子模块按创建期表代际解析,run2
/// 不跨 run 调用存活函数:sub_module_index 指向上一执行期的子模块表, /// 直接调用存活函数并读其属性。
/// 跨执行调用是独立缺口(基线即失败,非本次修复范围)。
#[test] #[test]
fn global_function_survives_reset_with_promoted_property() { fn global_function_survives_reset_with_promoted_property() {
assert!(eval_two_phases( assert!(eval_two_phases(
"var f = function(){}; globalThis.f = f; f.prototype = {}; f.prototype.q = 7; 0", "var f = function(){}; globalThis.f = f; f.prototype = {}; f.prototype.q = 7; 0",
"typeof globalThis.f === 'function' && globalThis.f.prototype.q === 7", "typeof globalThis.f === 'function' && globalThis.f() === undefined && globalThis.f.prototype.q === 7",
)); ));
} }
/// 类构造器跨 run:prototype 子对象(运行时 epoch 新建)经 reset 边界晋升, /// 类构造器跨 run:prototype 子对象(运行时 epoch 新建)经 reset 边界晋升,
/// run2 经全局别名读到方法属性。 /// run2 经全局别名读到方法属性并直接调用。
#[test] #[test]
fn class_prototype_link_survives_reset() { fn class_prototype_link_survives_reset() {
assert!(eval_two_phases( assert!(eval_two_phases(
"var cc = class { m(){ return 3; } }; globalThis.cc = cc; 0", "var cc = class { m(){ return 3; } }; globalThis.cc = cc; 0",
"typeof globalThis.cc === 'function' && typeof globalThis.cc.prototype.m === 'function'", "typeof globalThis.cc === 'function' && typeof globalThis.cc.prototype.m === 'function' && new globalThis.cc().m() === 3",
));
}
/// 跨 run 调用值:run1 建的函数(含闭包捕获)与 prototype 子对象跨 reset
/// 存活,run2 经全局别名调用——子模块按创建期代际解析,调用值与捕获值正确。
#[test]
fn cross_run_call_resolves_creation_generation() {
assert!(eval_two_phases(
"function make() { var v = 41; return function() { return v + 1; }; } \
globalThis.f = make(); globalThis.f.prototype = {}; globalThis.f.prototype.q = 7; 0",
"globalThis.f() === 42 && globalThis.f.prototype.q === 7",
));
}
/// 动态函数跨 run 调用:run1 `new Function` 创建的函数对象属 run1 代际,
/// run2 按自身代际解析执行,调用值正确。
#[test]
fn dynamic_function_survives_reset_and_calls() {
assert!(eval_two_phases(
"globalThis.d = new Function('a', 'return a * 2'); 0",
"globalThis.d(21) === 42",
)); ));
} }
+1 -3
View File
@@ -160,8 +160,6 @@ fn thenable_chain_after_already_resolved_noop() {
#[test] #[test]
fn subclass_then_derives_subclass_instance() { fn subclass_then_derives_subclass_instance() {
// 子类 then 派生:class P extends Promise {},p.then 返回 P 实例(经子类构造器)。 // 子类 then 派生:class P extends Promise {},p.then 返回 P 实例(经子类构造器)。
// 单次 eval 完成断言:跨 eval 时旧 module 的 bytecode 函数对象随 sub_modules
// 替换失效(引擎既有局限),故不拆分执行。
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = eval( let result = eval(
&mut vm, &mut vm,
@@ -174,7 +172,7 @@ fn subclass_then_derives_subclass_instance() {
#[test] #[test]
fn subclass_catch_finally_derive_subclass() { fn subclass_catch_finally_derive_subclass() {
// catch/finally 经 this.then 调用,自动获得子类派生语义。 // catch/finally 经 this.then 调用,自动获得子类派生语义。
// 每次 eval 独立定义 P:跨 eval 复用旧 module 函数对象会随 sub_modules 替换失效。 // 每次 eval 独立定义 P,两条断言互不干扰。
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = eval( let result = eval(
&mut vm, &mut vm,