This repository has been archived on 2026-09-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
OxideJS/crates/oxide_vm/tests/session_arena_tests.rs
T

135 lines
4.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use oxide_compiler::compiler::Compiler;
use oxide_parser::Allocator;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
fn run_source(vm: &mut Vm, source: &str) -> JsValue {
let allocator = Allocator::default();
let program = oxide_parser::parse(&allocator, source).expect("parse failed");
let module = Compiler::new().compile(&program).expect("compile failed");
vm.run(&module).expect("vm run failed")
}
fn global_prop(vm: &Vm, name: &str) -> JsValue {
let si = vm.kernel_core().perm_interner().intern(name).0;
let global = vm.session().global_object();
let pos = vm
.kernel_core()
.shape_forge()
.lookup_position(global.shape_id(), si)
.expect("global property slot");
global.get_prop_at(pos)
}
#[test]
fn test_basic_object_escape() {
let mut vm = Vm::new();
run_source(&mut vm, "globalThis.x = {}; globalThis.x.y = 1");
vm.reset();
let result = run_source(&mut vm, "globalThis.x.y");
assert_eq!(result, JsValue::int(1));
}
#[test]
fn test_array_escape() {
let mut vm = Vm::new();
run_source(&mut vm, "globalThis.a = []; globalThis.a.push({ v: 2 })");
vm.reset();
let result = run_source(
&mut vm,
"var i = 0; while (i < 1000) { var tmp = { v: i }; i = i + 1; } globalThis.a[0].v",
);
assert_eq!(result, JsValue::int(2));
}
#[test]
fn test_closure_captured_this_escape() {
let mut vm = Vm::new();
run_source(&mut vm, "globalThis.marker = { v: 9 }; globalThis.f = () => this.marker.v");
vm.reset();
let function = run_source(&mut vm, "globalThis.f");
let result = run_source(&mut vm, "globalThis.marker.v");
let function_obj = unsafe { &*function.as_js_object_ptr() };
assert!(function.is_object());
assert!(function_obj.is_session_epoch());
assert_eq!(global_prop(&vm, "f"), function);
assert_eq!(result, JsValue::int(9));
}
#[test]
fn test_transitive_escape() {
let mut vm = Vm::new();
run_source(&mut vm, "globalThis.root = {}");
vm.reset();
run_source(&mut vm, "globalThis.root.child = { v: 3 }");
vm.reset();
let result = run_source(&mut vm, "globalThis.root.child.v");
assert_eq!(result, JsValue::int(3));
}
#[test]
fn test_full_reset_clears_session_state() {
let mut vm = Vm::new();
run_source(&mut vm, "globalThis.x = {}; globalThis.x.y = 1");
vm.reset();
assert_eq!(run_source(&mut vm, "globalThis.x.y"), JsValue::int(1));
vm.full_reset();
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 后逃逸对象不可再访问"
);
}