fix: NEW_OBJECT 走统一分配入口登记 epoch 对象消除属性向量泄漏

This commit is contained in:
2026-08-16 02:16:34 +08:00
parent 5de959e2bd
commit 355b0233f0
2 changed files with 46 additions and 3 deletions
+1 -3
View File
@@ -227,9 +227,7 @@ impl Vm {
shape_id = self.kernel_core.shape_forge().make_shape(shape_id, si);
}
}
let obj = self
.epoch
.alloc(JsObject::new_empty(shape_id, JsValue::from_js_object(proto_ptr)));
let obj = self.alloc_object(JsObject::new_empty(shape_id, JsValue::from_js_object(proto_ptr)));
// 预分配数据槽并同步 generation:批量一次性 +nprops,与逐属性路径的终值一致。
let obj_ref = unsafe { &mut *obj };
for _ in 0..nprops {
@@ -87,3 +87,48 @@ fn test_full_reset_clears_session_state() {
let result = run_source(&mut vm, "globalThis.x");
assert!(result.is_undefined());
}
/// NEW_OBJECT 字面量必须登记进 epoch 追踪表:`{}` 写入超内联容量的属性会分配
/// hash_props 堆 Box,reset 时经追踪表统一释放;不登记则每个字面量泄漏一个 Box。
#[test]
fn new_object_literal_tracked_and_freed_on_reset() {
let mut vm = Vm::new();
let result = run_source(
&mut vm,
"var o = {}; for (var i = 0; i < 40; i++) { o['k' + i] = i; } \
delete o.k7; o.k8",
);
assert_eq!(format!("{}", result), "8", "属性写入/删除后读取应正常");
assert!(vm.epoch_object_count() > 0, "NEW_OBJECT 应登记 epoch 追踪表");
let freed_before = vm.session_gc_stats().total_bytes_freed;
vm.reset();
assert!(
vm.session_gc_stats().total_bytes_freed > freed_before,
"reset 应释放字面量对象的属性向量堆数据"
);
assert_eq!(vm.epoch_object_count(), 0, "reset 后追踪表应清空");
}
/// full_reset 同样释放 NEW_OBJECT 字面量:带属性写的 epoch 源对象经 promote 深拷贝
/// 到 session 后,完全重置同时释放源对象与克隆的堆数据,不得双重释放。
#[test]
fn new_object_literal_freed_by_full_reset() {
let mut vm = Vm::new();
let result = run_source(
&mut vm,
"var o = {}; for (var i = 0; i < 40; i++) { o['k' + i] = i; } \
globalThis.keep = o; o.k7",
);
assert_eq!(format!("{}", result), "7", "逃逸后属性读取应正常");
assert!(vm.epoch_object_count() > 0, "字面量源对象在 promote 后仍登记于 epoch 追踪表");
assert!(vm.session_object_count() > 0, "逃逸克隆应登记于 session 追踪表");
vm.full_reset();
assert_eq!(vm.epoch_object_count(), 0, "full_reset 后 epoch 追踪表应清空");
assert_eq!(vm.session_object_count(), 0, "full_reset 后 session 追踪表应清空");
assert!(
run_source(&mut vm, "globalThis.keep").is_undefined(),
"full_reset 后逃逸对象不可再访问"
);
}