mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
fix: $262.gc() 补 mark 收口为顶层边界强制完整收集
This commit is contained in:
@@ -69,14 +69,20 @@ pub fn create_realm(vm: &mut Vm, _args: &[u8]) -> NativeResult {
|
||||
not_supported(vm, "createRealm")
|
||||
}
|
||||
|
||||
/// `$262.gc()`:请求一次 session 全量回收(不计返回值)。
|
||||
/// `$262.gc()`:请求一次 session 全量强制收集,返回 undefined。
|
||||
///
|
||||
/// 收集延迟到下一个顶层指令边界执行,不在 native 重入点就地跑:重入期间
|
||||
/// dispatch 调用链上的调用方寄存器窗口副本与 callee 引用是 Rust 局部值
|
||||
/// (非 GC 根),移动式 sweep 就地搬移会使它们悬垂,返回后恢复动作还会
|
||||
/// 把陈旧指针写回 `regs[254]`。顶层指令边界是无在途局部值的安全点,
|
||||
/// 收集经既有完整收集入口(与引擎正常触发路径同一函数)执行。
|
||||
/// 对 JS 可观察语义等价于同步:收集在下一条指令求值前完成。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 执行 mark-sweep 搬移存活对象并释放不可达对象,会重写 session 对象指针。
|
||||
/// - 置强制收集旗标;下一个顶层指令边界执行 mark + 移动式 sweep +
|
||||
/// 字符串/BigInt 清扫,重写 session 对象指针。
|
||||
pub fn gc(vm: &mut Vm, _args: &[u8]) -> NativeResult {
|
||||
let mut session_gc = std::mem::take(&mut vm.gc_state.session_gc);
|
||||
session_gc.sweep(vm);
|
||||
vm.gc_state.session_gc = session_gc;
|
||||
vm.gc_state.pending_forced_collect = true;
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
|
||||
@@ -121,6 +121,12 @@ impl Vm {
|
||||
// 返回顶层后检查恢复,存活值此时已回拷为执行根。账目未超水位时仅
|
||||
// 少量字段比较。
|
||||
if self.native_call_depth == 0 {
|
||||
// 宿主 `$262.gc()` 置位的强制收集在此边界消费:移动式 sweep
|
||||
// 唯一安全点(无在途 dispatch 局部值),与正常触发路径同一入口。
|
||||
if self.gc_state.pending_forced_collect {
|
||||
self.gc_state.pending_forced_collect = false;
|
||||
self.collect_session_gc();
|
||||
}
|
||||
// 峰值高水位:同一边界采样 session 堆账目上界
|
||||
let bytes = self.gc_state.session_bytes_allocated;
|
||||
if bytes > self.gc_state.session_bytes_peak {
|
||||
|
||||
@@ -58,6 +58,9 @@ pub(crate) struct GcState {
|
||||
/// 门控未过(挂起态持有 for-in)后的重扫锚:包络再增长一个阈值才重扫
|
||||
/// 三型状态盒(O(对象数) 扫描),避免持挂起 for-in 的 run 每指令边界重扫。
|
||||
pub(crate) gc_gate_retry_alloc: usize,
|
||||
/// 宿主强制收集旗标(`$262.gc()`):native 重入中置位,由下一个顶层
|
||||
/// 指令边界(移动式 sweep 的唯一安全点)消费并执行完整收集。
|
||||
pub(crate) pending_forced_collect: bool,
|
||||
pub(crate) forwarding: HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>,
|
||||
}
|
||||
|
||||
|
||||
@@ -134,6 +134,7 @@ impl Vm {
|
||||
gc_threshold_cached: gc_threshold,
|
||||
gc_watermark: gc_threshold,
|
||||
gc_gate_retry_alloc: 0,
|
||||
pending_forced_collect: false,
|
||||
forwarding: std::collections::HashMap::with_hasher(rustc_hash::FxBuildHasher),
|
||||
},
|
||||
symbols: SymbolState {
|
||||
@@ -267,6 +268,7 @@ impl Vm {
|
||||
gc_threshold_cached: gc_threshold,
|
||||
gc_watermark: gc_threshold,
|
||||
gc_gate_retry_alloc: 0,
|
||||
pending_forced_collect: false,
|
||||
forwarding: std::collections::HashMap::with_hasher(rustc_hash::FxBuildHasher),
|
||||
},
|
||||
symbols: SymbolState {
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_kernel::kernel::{KernelConfig, KernelCore};
|
||||
use oxide_parser::Allocator;
|
||||
use oxide_types::value::JsValue;
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
/// 单测辅助:源串 → 结果串(字符串值解包实际内容,运行错误以 `vm error: ...` 呈现)。
|
||||
@@ -25,6 +27,14 @@ fn eval(source: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// 单测辅助:在调用方提供的 VM 上执行源串,返回原始结果值(低阈值配置钉用)。
|
||||
fn eval_on(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||
let allocator = Allocator::default();
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("parse error: {}", e[0].message))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("compile error: {e}"))?;
|
||||
vm.run(&Arc::new(module))
|
||||
}
|
||||
|
||||
/// `$262` 作为编译期已知全局绑定:typeof 为 object,`global` 即 globalThis。
|
||||
#[test]
|
||||
fn host_object_exists_and_exposes_global() {
|
||||
@@ -85,3 +95,25 @@ fn detach_array_buffer_detaches() {
|
||||
fn gc_runs_without_error() {
|
||||
assert_eq!(eval("$262.gc(); 'ok'"), "ok");
|
||||
}
|
||||
|
||||
/// `$262.gc()` 存活钉(低阈值形态):小阈值长循环反复触发执行期收集,
|
||||
/// 全局可达的 session 对象经强制收集后身份与属性读回保持完整——
|
||||
/// 收集前的 mark 必须覆盖全部可达对象,漏标即误回收、读回悬垂。
|
||||
#[test]
|
||||
fn gc_keeps_reachable_session_objects_alive() {
|
||||
let mut config = KernelConfig::minimal();
|
||||
config.set_session_gc_threshold(4096);
|
||||
let mut vm = Vm::with_kernel_core(KernelCore::new(config));
|
||||
let result = eval_on(
|
||||
&mut vm,
|
||||
"(function () { \
|
||||
globalThis.o = { x: 41, s: 'keep' }; \
|
||||
globalThis.o.x = 42; \
|
||||
for (var i = 0; i < 500000; i++) { var t = 'str' + i; } \
|
||||
$262.gc(); \
|
||||
return globalThis.o.x + globalThis.o.s.length === 46 \
|
||||
&& $262.global.o === globalThis.o; })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user