From c6dfa3be799d92797b9142fe63af10b40f62fe55 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Fri, 11 Sep 2026 20:20:00 +0000 Subject: [PATCH] =?UTF-8?q?test:=20=E6=AD=BB=E9=97=AD=E5=8C=85=E5=8F=8C?= =?UTF-8?q?=E6=94=BE=E5=9B=9E=E5=BD=92=E5=8D=95=E6=B5=8B=E8=A1=A5=E5=AD=97?= =?UTF-8?q?=E8=8A=82=E8=B4=A6=E7=9B=AE=20A/B=20=E5=B7=AE=E5=88=86=E6=96=AD?= =?UTF-8?q?=E8=A8=80=E2=80=94=E2=80=94=E4=B8=A4=E6=9D=A1=E5=90=8C=E6=9E=84?= =?UTF-8?q?=20arrow=20=E4=BB=85=20upvalue=20=E6=95=B0=E4=B8=8D=E5=90=8C?= =?UTF-8?q?=EF=BC=883=20vs=200=EF=BC=89=EF=BC=8C=E4=B8=A4=E6=AC=A1?= =?UTF-8?q?=E6=94=B6=E9=9B=86=E8=B4=A6=E7=9B=AE=E5=B7=AE=E5=8D=B3=20upvalu?= =?UTF-8?q?e=20=E5=88=97=E8=A1=A8=20Box=20=E5=AD=97=E8=8A=82=E6=95=B0?= =?UTF-8?q?=EF=BC=8C=E9=80=80=E5=9B=9E=E6=AD=BB=E5=88=86=E6=94=AF=E4=B8=8D?= =?UTF-8?q?=E9=87=8A=E6=94=BE=EF=BC=88=E6=B3=84=E6=BC=8F=EF=BC=89=E6=97=A7?= =?UTF-8?q?=E8=A1=8C=E4=B8=BA=E5=B7=AE=E5=88=86=E4=B8=BA=200=20=E5=8D=B3?= =?UTF-8?q?=E8=BD=AC=E7=BA=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_vm/tests/gc_runtime_tests.rs | 27 ++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/crates/oxide_vm/tests/gc_runtime_tests.rs b/crates/oxide_vm/tests/gc_runtime_tests.rs index 7bc40b63..6e7b3f3a 100644 --- a/crates/oxide_vm/tests/gc_runtime_tests.rs +++ b/crates/oxide_vm/tests/gc_runtime_tests.rs @@ -298,8 +298,33 @@ fn dead_closure_upvalues_freed_by_sweep_without_double_free() { // 重置后引擎健康:重建新 3-upvalue 闭包,同 run 调用语义正确。 let fourth = compile( "var x = 10; var y = 20; var z = 30; \ - globalThis.f = function() { return x + y + z; }; globalThis.f()", + globalThis.f = function() { return x + y + z; }; globalThis.f()", ); let result = vm.run(&fourth).expect("run4"); assert_eq!(format!("{}", result), "60"); + + // 字节账目 A/B 差分样本:两条同构 arrow 仅 upvalue 数不同(3 vs 0), + // 各走独立 VM 的「创建 → 撤根 → 完整收集」。两次收集的字节账目差即死 + // arrow 的 upvalue 列表 Box 字节数——对象本体、length/name 属性区、函数 + // 名 session 串在 A/B 恒等,差分相消(各 1 个死对象 + 1 条死串)。 + // 死分支不释放 upvalue 列表(泄漏)时差分为 0,本断言转红。 + let collect_freed = |src: &str| -> u64 { + let mut v = vm_with_threshold(4096); + v.run(&compile(src)).expect("run create"); + v.run(&compile("globalThis.arrow = undefined; 0")).expect("run unroot"); + v.collect_session_gc(); + let stats = v.session_gc_stats(); + assert!(stats.last_collection_objects_dead >= 1, "arrow 闭包应经 sweep 死分支"); + stats.last_collection_bytes_freed + }; + let freed_with_captures = + collect_freed("var a = 1; var b = 2; var c = 3; globalThis.arrow = () => a + b + c; 0"); + let freed_no_captures = collect_freed("globalThis.arrow = () => 7; 0"); + let upvalue_box_min = (std::mem::size_of::>() + + 3 * std::mem::size_of::<*mut oxide_types::object::Cell>()) as u64; + assert!( + freed_with_captures >= freed_no_captures + upvalue_box_min, + "两次收集的字节账目差应为死闭包 upvalue 列表 Box(≥{upvalue_box_min} B),实际差 {}", + freed_with_captures - freed_no_captures + ); }