mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-27 12:11:12 +08:00
fix: 子模块表代际注册表——跨 run 存活函数按创建期表代解析子模块,索引悬空与静默错模块闭合
This commit is contained in:
@@ -225,8 +225,8 @@ fn full_reset_clears_session_after_runtime_gc() {
|
||||
}
|
||||
|
||||
/// 低阈值下 reset 触发完整对象 sweep(移动式搬移 + 重标):写全局的函数对象
|
||||
/// 不被当死对象释放,run2 仍为可引用的函数值(跨 run 调用受独立的
|
||||
/// sub_module_index 缺口限制,不在此断言)。
|
||||
/// 不被当死对象释放,run2 直接调用该闭包——upvalue 读回创建期值,子模块按
|
||||
/// 创建期表代际解析。
|
||||
#[test]
|
||||
fn reset_sweep_preserves_global_function_object() {
|
||||
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, "低阈值应触发执行期收集");
|
||||
vm.reset();
|
||||
|
||||
let second = compile("typeof globalThis.fn === 'function'");
|
||||
let second = compile("globalThis.fn() === 9");
|
||||
let result = vm.run(&Arc::new(second)).expect("run2");
|
||||
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();
|
||||
assert!(vm.session_gc_stats().last_collection_objects_dead >= 1, "死闭包应经 sweep 死分支");
|
||||
|
||||
// 跨 run 存活核(只读属性;跨 run 调用受 sub_module_index 缺口限制):
|
||||
// sweep 后存活闭包克隆仍挂在 global 上。
|
||||
let third = compile("typeof globalThis.live === 'function'");
|
||||
// 跨 run 存活核:sweep 后存活闭包克隆仍挂在 global 上,直接调用读回
|
||||
// upvalue 和(子模块按创建期表代际解析)。
|
||||
let third = compile("globalThis.live() === 6");
|
||||
let result = vm.run(&Arc::new(third)).expect("run3");
|
||||
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 边收集去对象预过滤) ─────────────────
|
||||
|
||||
/// 盒持唯一引用 session 串(Promise 结算值):churn 窗口内执行期收集
|
||||
|
||||
@@ -212,24 +212,44 @@ fn same_function_written_to_two_globals_is_identical() {
|
||||
}
|
||||
|
||||
/// 跨 run(reset)契约:函数对象直落 session,其属性持有的 epoch 对象
|
||||
/// (prototype 槽)在 epoch 清空前被晋升,run2 经全局别名可读其属性。
|
||||
/// 不跨 run 调用存活函数:sub_module_index 指向上一执行期的子模块表,
|
||||
/// 跨执行调用是独立缺口(基线即失败,非本次修复范围)。
|
||||
/// (prototype 槽)在 epoch 清空前被晋升;子模块按创建期表代际解析,run2
|
||||
/// 直接调用存活函数并读其属性。
|
||||
#[test]
|
||||
fn global_function_survives_reset_with_promoted_property() {
|
||||
assert!(eval_two_phases(
|
||||
"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 边界晋升,
|
||||
/// run2 经全局别名读到方法属性。
|
||||
/// run2 经全局别名读到方法属性并直接调用。
|
||||
#[test]
|
||||
fn class_prototype_link_survives_reset() {
|
||||
assert!(eval_two_phases(
|
||||
"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",
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -160,8 +160,6 @@ fn thenable_chain_after_already_resolved_noop() {
|
||||
#[test]
|
||||
fn subclass_then_derives_subclass_instance() {
|
||||
// 子类 then 派生:class P extends Promise {},p.then 返回 P 实例(经子类构造器)。
|
||||
// 单次 eval 完成断言:跨 eval 时旧 module 的 bytecode 函数对象随 sub_modules
|
||||
// 替换失效(引擎既有局限),故不拆分执行。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
@@ -174,7 +172,7 @@ fn subclass_then_derives_subclass_instance() {
|
||||
#[test]
|
||||
fn subclass_catch_finally_derive_subclass() {
|
||||
// catch/finally 经 this.then 调用,自动获得子类派生语义。
|
||||
// 每次 eval 独立定义 P:跨 eval 复用旧 module 函数对象会随 sub_modules 替换失效。
|
||||
// 每次 eval 独立定义 P,两条断言互不干扰。
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
|
||||
Reference in New Issue
Block a user