mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
refactor: 晋升族整文件退役加生产调用点清理
This commit is contained in:
@@ -108,12 +108,7 @@ pub fn run_js_stress_bench(config: &BenchConfig, kernel: &Arc<KernelCore>, pool:
|
|||||||
let ic_hits = vm.ic_hit_count();
|
let ic_hits = vm.ic_hit_count();
|
||||||
let ic_misses = vm.ic_miss_count();
|
let ic_misses = vm.ic_miss_count();
|
||||||
|
|
||||||
// 留存口径:先让存活集对 session 完全可见——根直接持有的 epoch 对象
|
// 留存口径:强制完整 GC 清掉不可达垃圾后账目即留存堆。
|
||||||
// (顶层 var 等)晋升进 session,session 对象持有的 epoch 子引用
|
|
||||||
// (闭包捕获、原生盒直插)同样晋升(均与 epoch 边界同机制);
|
|
||||||
// 再强制完整 GC 清掉不可达垃圾,清扫后账目即 workload 留存堆
|
|
||||||
vm.promote_rooted_epoch_objects();
|
|
||||||
vm.promote_session_epoch_refs();
|
|
||||||
vm.collect_session_gc();
|
vm.collect_session_gc();
|
||||||
let retained_bytes = vm.session_bytes_allocated();
|
let retained_bytes = vm.session_bytes_allocated();
|
||||||
let retained_objects = vm.session_object_count();
|
let retained_objects = vm.session_object_count();
|
||||||
|
|||||||
@@ -765,6 +765,7 @@ pub(crate) fn async_native_cell_edges(obj: &JsObject) -> Vec<*mut Cell> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 用转发函数重写状态快照中的所有 JsValue(session GC 移动式清扫 / promote 用)。
|
/// 用转发函数重写状态快照中的所有 JsValue(session GC 移动式清扫 / promote 用)。
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn rewrite_async_native(obj: &JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue) {
|
pub(crate) fn rewrite_async_native(obj: &JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue) {
|
||||||
let Some(state) = async_state_mut(obj) else {
|
let Some(state) = async_state_mut(obj) else {
|
||||||
return;
|
return;
|
||||||
@@ -781,6 +782,7 @@ pub(crate) fn rewrite_async_native(obj: &JsObject, mut rewrite: impl FnMut(JsVal
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 深拷贝状态盒到新对象(promote / sweep 用):新对象持独立 Box,源盒可安全释放。
|
/// 深拷贝状态盒到新对象(promote / sweep 用):新对象持独立 Box,源盒可安全释放。
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn clone_async_native_with_rewrite(
|
pub(crate) fn clone_async_native_with_rewrite(
|
||||||
old: &JsObject, new: &mut JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue,
|
old: &JsObject, new: &mut JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1753,6 +1753,7 @@ pub(crate) fn async_generator_native_cell_edges(obj: &JsObject) -> Vec<*mut Cell
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 用转发函数重写状态快照中的所有 JsValue(session GC 移动式清扫 / promote 用)。
|
/// 用转发函数重写状态快照中的所有 JsValue(session GC 移动式清扫 / promote 用)。
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn rewrite_async_generator_native(obj: &JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue) {
|
pub(crate) fn rewrite_async_generator_native(obj: &JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue) {
|
||||||
let Some(state) = async_gen_state_mut(obj) else {
|
let Some(state) = async_gen_state_mut(obj) else {
|
||||||
return;
|
return;
|
||||||
@@ -1776,6 +1777,7 @@ pub(crate) fn rewrite_async_generator_native(obj: &JsObject, mut rewrite: impl F
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 深拷贝状态盒到新对象(promote / sweep 用):新对象持独立 Box,源盒可安全释放。
|
/// 深拷贝状态盒到新对象(promote / sweep 用):新对象持独立 Box,源盒可安全释放。
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn clone_async_generator_native_with_rewrite(
|
pub(crate) fn clone_async_generator_native_with_rewrite(
|
||||||
old: &JsObject, new: &mut JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue,
|
old: &JsObject, new: &mut JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1023,6 +1023,7 @@ pub(crate) fn generator_native_cell_edges(obj: &JsObject) -> Vec<*mut Cell> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 用转发函数重写状态快照中的所有 JsValue(promote 用)。
|
/// 用转发函数重写状态快照中的所有 JsValue(promote 用)。
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn rewrite_generator_native(obj: &JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue) {
|
pub(crate) fn rewrite_generator_native(obj: &JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue) {
|
||||||
let Some(state) = generator_state_mut(obj) else {
|
let Some(state) = generator_state_mut(obj) else {
|
||||||
return;
|
return;
|
||||||
@@ -1036,6 +1037,7 @@ pub(crate) fn rewrite_generator_native(obj: &JsObject, mut rewrite: impl FnMut(J
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 深拷贝状态盒到新对象(promote / sweep 用):新对象持独立 Box,源盒可安全释放。
|
/// 深拷贝状态盒到新对象(promote / sweep 用):新对象持独立 Box,源盒可安全释放。
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn clone_generator_native_with_rewrite(
|
pub(crate) fn clone_generator_native_with_rewrite(
|
||||||
old: &JsObject, new: &mut JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue,
|
old: &JsObject, new: &mut JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ pub mod native;
|
|||||||
mod native_box_dispatch;
|
mod native_box_dispatch;
|
||||||
/// Promise 运行时(状态盒 + 微任务队列 + 构造器/方法实现)。
|
/// Promise 运行时(状态盒 + 微任务队列 + 构造器/方法实现)。
|
||||||
pub mod promise;
|
pub mod promise;
|
||||||
mod session_arena;
|
|
||||||
/// session 级 mark-sweep GC:标记-清扫 session arena 对象与 session 字符串。
|
/// session 级 mark-sweep GC:标记-清扫 session arena 对象与 session 字符串。
|
||||||
pub mod session_gc;
|
pub mod session_gc;
|
||||||
mod suspended;
|
mod suspended;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ pub(crate) fn promise_native_edges(obj: &JsObject) -> Vec<JsValue> {
|
|||||||
/// 结算链指针是裸指针边、不在本函数改写面:晋升克隆新分配于 session arena
|
/// 结算链指针是裸指针边、不在本函数改写面:晋升克隆新分配于 session arena
|
||||||
/// (原件地址不变),改写场景指针天然有效;结算链接链由
|
/// (原件地址不变),改写场景指针天然有效;结算链接链由
|
||||||
/// `migrate_settlement_to_newest_clone` 负责。
|
/// `migrate_settlement_to_newest_clone` 负责。
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn rewrite_promise_native(obj: &JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue) {
|
pub(crate) fn rewrite_promise_native(obj: &JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue) {
|
||||||
let Some(state) = promise_state_mut(obj) else {
|
let Some(state) = promise_state_mut(obj) else {
|
||||||
return;
|
return;
|
||||||
@@ -59,6 +60,7 @@ pub(crate) fn rewrite_promise_native(obj: &JsObject, mut rewrite: impl FnMut(JsV
|
|||||||
/// 永久丢失、原件侧消费者永不触发;每条反应的 JsValue 随状态盒其余字段同一
|
/// 永久丢失、原件侧消费者永不触发;每条反应的 JsValue 随状态盒其余字段同一
|
||||||
/// pass 晋升/改写。结算链指针按原值保留(接链由
|
/// pass 晋升/改写。结算链指针按原值保留(接链由
|
||||||
/// `migrate_settlement_to_newest_clone` 负责)。
|
/// `migrate_settlement_to_newest_clone` 负责)。
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn clone_promise_native_with_rewrite(
|
pub(crate) fn clone_promise_native_with_rewrite(
|
||||||
old: &JsObject, new: &mut JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue,
|
old: &JsObject, new: &mut JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue,
|
||||||
) {
|
) {
|
||||||
@@ -110,6 +112,7 @@ pub(crate) fn clone_promise_native_with_rewrite(
|
|||||||
/// `clone_promise_native_with_rewrite`);session 搬移(GC sweep)不改结算
|
/// `clone_promise_native_with_rewrite`);session 搬移(GC sweep)不改结算
|
||||||
/// 拓扑,不得调用。
|
/// 拓扑,不得调用。
|
||||||
/// - 源非 pending 时无事可做直接返回(已结算原件不再传导)。
|
/// - 源非 pending 时无事可做直接返回(已结算原件不再传导)。
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn migrate_settlement_to_newest_clone(
|
pub(crate) fn migrate_settlement_to_newest_clone(
|
||||||
old: &JsObject, new: &mut JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue,
|
old: &JsObject, new: &mut JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue,
|
||||||
) {
|
) {
|
||||||
@@ -146,6 +149,7 @@ pub(crate) fn migrate_settlement_to_newest_clone(
|
|||||||
|
|
||||||
/// 改写单条反应的四条 JsValue 引用(派生 promise 能力与处理器),源反应迁移
|
/// 改写单条反应的四条 JsValue 引用(派生 promise 能力与处理器),源反应迁移
|
||||||
/// 与再晋升合并共用。
|
/// 与再晋升合并共用。
|
||||||
|
#[allow(dead_code)]
|
||||||
fn rewrite_reaction<F: FnMut(JsValue) -> JsValue>(r: PromiseReaction, rewrite: &mut F) -> PromiseReaction {
|
fn rewrite_reaction<F: FnMut(JsValue) -> JsValue>(r: PromiseReaction, rewrite: &mut F) -> PromiseReaction {
|
||||||
PromiseReaction {
|
PromiseReaction {
|
||||||
promise: rewrite(r.promise),
|
promise: rewrite(r.promise),
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ pub(crate) fn for_each_job_value(job: &Microtask, mut f: impl FnMut(JsValue)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 供外部改写微任务队列中的 JsValue(session GC sweep 用)。
|
/// 供外部改写微任务队列中的 JsValue(session GC sweep 用)。
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn rewrite_job_values(job: &mut Microtask, mut rewrite: impl FnMut(JsValue) -> JsValue) {
|
pub(crate) fn rewrite_job_values(job: &mut Microtask, mut rewrite: impl FnMut(JsValue) -> JsValue) {
|
||||||
match job {
|
match job {
|
||||||
Microtask::Reaction {
|
Microtask::Reaction {
|
||||||
|
|||||||
@@ -17,10 +17,7 @@ mod jobs;
|
|||||||
mod reactions;
|
mod reactions;
|
||||||
mod settlement;
|
mod settlement;
|
||||||
|
|
||||||
pub(crate) use gc_edges::{
|
pub(crate) use gc_edges::{drop_promise_native, promise_native_edges, promise_native_size};
|
||||||
clone_promise_native_with_rewrite, drop_promise_native, migrate_settlement_to_newest_clone, promise_native_edges,
|
|
||||||
promise_native_size, rewrite_promise_native,
|
|
||||||
};
|
|
||||||
pub(crate) use jobs::{for_each_job_value, rewrite_job_values};
|
pub(crate) use jobs::{for_each_job_value, rewrite_job_values};
|
||||||
pub use reactions::promise_settled_value;
|
pub use reactions::promise_settled_value;
|
||||||
|
|
||||||
@@ -161,6 +158,7 @@ fn promise_state_ref(obj: &JsObject) -> Option<&PromiseState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::mut_from_ref)]
|
#[expect(clippy::mut_from_ref)]
|
||||||
|
#[allow(dead_code)]
|
||||||
fn promise_state_mut(obj: &JsObject) -> Option<&mut PromiseState> {
|
fn promise_state_mut(obj: &JsObject) -> Option<&mut PromiseState> {
|
||||||
let ptr = obj.native_data() as *mut PromiseState;
|
let ptr = obj.native_data() as *mut PromiseState;
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
|
|||||||
@@ -1,524 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::vm_debug;
|
|
||||||
use oxide_types::object::JsObject;
|
|
||||||
use oxide_types::value::JsValue;
|
|
||||||
use rustc_hash::FxBuildHasher;
|
|
||||||
|
|
||||||
use crate::vm::Vm;
|
|
||||||
use oxide_builtins::{array_buffer, data_view, disposable_stack, map, module, regexp, set, typed_array, weak_map};
|
|
||||||
|
|
||||||
impl Vm {
|
|
||||||
/// 单对象晋升测试入口:局部转发表下调 `promote_object_inner`。
|
|
||||||
/// 生产路径(边界修复/根晋升)各自持表管理,不经过此包装。
|
|
||||||
#[cfg(test)]
|
|
||||||
pub(crate) fn promote_object(&mut self, src: *mut JsObject) -> *mut JsObject {
|
|
||||||
vm_debug!("promote_object: src={:p}", src);
|
|
||||||
let mut forwarding = HashMap::with_hasher(FxBuildHasher);
|
|
||||||
let result = self.promote_object_inner(src, &mut forwarding);
|
|
||||||
// 局部表随作用域丢弃,晋升克隆的转发改写已在其上完成。
|
|
||||||
forwarding.clear();
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn promote_object_inner(
|
|
||||||
&mut self, src: *mut JsObject, forwarding: &mut HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>,
|
|
||||||
) -> *mut JsObject {
|
|
||||||
vm_debug!("promote_object_inner: src={:p}", src);
|
|
||||||
if src.is_null() {
|
|
||||||
return src;
|
|
||||||
}
|
|
||||||
let src_ref = unsafe { &*src };
|
|
||||||
if src_ref.is_session_epoch() || !src_ref.is_epoch() {
|
|
||||||
return src;
|
|
||||||
}
|
|
||||||
if let Some(dst) = forwarding.get(&src).copied() {
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
|
|
||||||
let clone = src_ref.clone_for_session_epoch();
|
|
||||||
let dst = self.gc_state.session_epoch.alloc(clone) as *mut JsObject;
|
|
||||||
forwarding.insert(src, dst);
|
|
||||||
self.gc_state.session_object_ptrs.push(dst);
|
|
||||||
|
|
||||||
let dst_ref = unsafe { &mut *dst };
|
|
||||||
dst_ref.rewrite_object_values(|value| self.promote_value_if_epoch_object(value, forwarding));
|
|
||||||
if src_ref.is_map() {
|
|
||||||
map::clone_map_native_with_rewrite(src_ref, dst_ref, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if src_ref.is_set() {
|
|
||||||
set::clone_set_native_with_rewrite(src_ref, dst_ref, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if src_ref.is_weak_map_obj() {
|
|
||||||
// 值边随克隆晋升(强边);弱键原样搬运,生死交晋升收敛后的
|
|
||||||
// 弱键定夺路径(现为原样保留)。
|
|
||||||
weak_map::clone_weak_map_native_with_rewrite(src_ref, dst_ref, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if src_ref.is_disposable_stack_obj() || src_ref.is_async_disposable_stack_obj() {
|
|
||||||
// 资源栈状态盒深拷贝到新对象:源盒随 epoch 释放,互不共享。
|
|
||||||
disposable_stack::clone_dispose_native_with_rewrite(src_ref, dst_ref, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if src_ref.is_typed_array_obj() {
|
|
||||||
typed_array::clone_typed_array_native_with_rewrite(src_ref, dst_ref, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if src_ref.is_data_view_obj() {
|
|
||||||
data_view::clone_data_view_native_with_rewrite(src_ref, dst_ref, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if src_ref.is_array_buffer_obj() {
|
|
||||||
// 字节缓冲深拷贝到新对象:源 Vec 随 epoch 释放,互不共享。
|
|
||||||
array_buffer::clone_array_buffer_native(src_ref, dst_ref);
|
|
||||||
} else if src_ref.is_shared_array_buffer_obj() {
|
|
||||||
// 字节缓冲深拷贝到新对象:源 Vec 随 epoch 释放,互不共享。
|
|
||||||
array_buffer::clone_shared_array_buffer_native(src_ref, dst_ref);
|
|
||||||
} else if src_ref.is_generator_obj() {
|
|
||||||
// 生成器状态盒深拷贝到新对象:源盒随 epoch 释放,互不共享。
|
|
||||||
crate::generator::clone_generator_native_with_rewrite(src_ref, dst_ref, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if src_ref.is_promise_obj() {
|
|
||||||
// Promise 状态盒深拷贝到新对象:源盒随 epoch 释放,互不共享;
|
|
||||||
// 反应迁入新克隆并接结算链(原件/旧克隆结算沿链传导到最新克隆)。
|
|
||||||
crate::promise::clone_promise_native_with_rewrite(src_ref, dst_ref, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
crate::promise::migrate_settlement_to_newest_clone(src_ref, dst_ref, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if src_ref.is_async_obj() {
|
|
||||||
// 异步状态盒深拷贝到新对象:源盒随 epoch 释放,互不共享。
|
|
||||||
crate::async_func::clone_async_native_with_rewrite(src_ref, dst_ref, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if src_ref.is_async_generator_obj() {
|
|
||||||
// 异步生成器状态盒深拷贝到新对象:源盒随 epoch 释放,互不共享。
|
|
||||||
crate::async_generator::clone_async_generator_native_with_rewrite(src_ref, dst_ref, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if src_ref.holds_compiled_regex() {
|
|
||||||
// 已编译正则是 Box 深拷贝到新对象:源盒随 epoch 释放,互不共享。
|
|
||||||
regexp::clone_regexp_native(src_ref, dst_ref);
|
|
||||||
} else if src_ref.is_module_namespace() {
|
|
||||||
// 条目表深拷贝到新对象并改写值边:`clone_for_session_epoch` 只浅拷贝
|
|
||||||
// native_data 指针,共享同一表会在释放时双放。
|
|
||||||
module::clone_module_ns_native_with_rewrite(src_ref, dst_ref, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// 账目计入:对象头 + 堆数据(属性/元素/meta Vec + native 状态盒),用 clone 后的 dst 核算。
|
|
||||||
self.gc_state.session_bytes_allocated +=
|
|
||||||
std::mem::size_of::<JsObject>() + crate::session_gc::SessionGc::object_heap_data_bytes(dst_ref) as usize;
|
|
||||||
dst
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 克隆改写与 reset 边界晋升共用的单引用改写:epoch 对象克隆晋升进
|
|
||||||
/// session(forwarding 去重共享与环);其余值原样保留。
|
|
||||||
pub(crate) fn promote_value_if_epoch_object(
|
|
||||||
&mut self, value: JsValue, forwarding: &mut HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>,
|
|
||||||
) -> JsValue {
|
|
||||||
if !value.is_object() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
let ptr = value.as_js_object_ptr();
|
|
||||||
if ptr.is_null() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
// SAFETY: 执行核心产出的对象值,指针在 session 生命周期内有效。
|
|
||||||
if unsafe { &*ptr }.is_epoch() {
|
|
||||||
return JsValue::from_js_object(self.promote_object_inner(ptr, forwarding));
|
|
||||||
}
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
/// epoch 边界前的 session 原地晋升:遍历全部 session 对象,把仍指向 epoch
|
|
||||||
/// 对象的引用克隆进 session(JS 边 + native 状态盒),消除 epoch 重置后的
|
|
||||||
/// 悬垂指针。
|
|
||||||
///
|
|
||||||
/// 覆盖三类绕过写屏障的引用来源:函数对象直接 session 分配后捕获的
|
|
||||||
/// `captured_this`/`home_object`/upvalue cell/属性值(SET_HOME_OBJECT 与
|
|
||||||
/// 函数目标豁免均直落 epoch 值);Map/Set 等原生盒按键值直插 epoch 值;
|
|
||||||
/// global 对象的属性值(逃逸写直通后 epoch 值直落 global 槽,global 不入
|
|
||||||
/// session 对象表,由 `rewrite_session_epoch_refs` 同趟改写)。
|
|
||||||
/// `rewrite_object_values` 覆盖元素/meta/属性/proto/captured_this/home_object/
|
|
||||||
/// cell 值;克隆子树经局部转发表去重,环与共享引用各克隆一次。
|
|
||||||
/// 统一入口 Box 化后生产路径不产 epoch 对象,本方法为晋升族空转保留面。
|
|
||||||
///
|
|
||||||
/// # 注意事项
|
|
||||||
/// - 须在执行外的安全点调用(无在途 builtin 局部裸指针、dispatch 未重入):
|
|
||||||
/// 本方法原地改写 session 对象的引用字段,执行期调用将使 builtin 局部
|
|
||||||
/// 裸指针失效(与 `collect_session_gc` 同前提)。
|
|
||||||
/// - session 对象表为空时 global 的 epoch 子引用仍须修复,不做空表早退。
|
|
||||||
pub fn promote_session_epoch_refs(&mut self) {
|
|
||||||
let objects = std::mem::take(&mut self.gc_state.session_object_ptrs);
|
|
||||||
let mut forwarding = HashMap::with_hasher(FxBuildHasher);
|
|
||||||
self.rewrite_session_epoch_refs(&objects, &mut forwarding);
|
|
||||||
// 弱键定夺退化为原样保留(定夺路径无克隆可重指,见
|
|
||||||
// resolve_weak_key_after_promotion);调用保留以收口族内流程。
|
|
||||||
self.rewrite_weak_map_keys_after_promotion(&objects, &mut forwarding);
|
|
||||||
// 局部转发表随作用域丢弃:晋升新克隆已推入对象表,旧对象按原序拼回。
|
|
||||||
self.gc_state.session_object_ptrs.extend(objects);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 按调用方给定的转发表把对象列表的 epoch 子引用就地改写:未在上游晋升过的
|
|
||||||
/// epoch 子对象经转发表克隆进 session(递归去重共享与环),已是 session/
|
|
||||||
/// 非 epoch 的值原样保留。JS 边与 native 状态盒走同一改写闭包。
|
|
||||||
///
|
|
||||||
/// global 对象不入 session 对象表(P 根,不在 arena 内):其 epoch 子引用
|
|
||||||
/// 与列表同趟改写,无 native 盒只覆盖 JS 边。
|
|
||||||
///
|
|
||||||
/// # 注意事项
|
|
||||||
/// - 调用方持有局部转发表期间,晋升新克隆推入的对象表由调用方决定何时
|
|
||||||
/// 拼回;本方法不改表。
|
|
||||||
/// - 死对象(不可达)的 epoch 子引用改写会把死对象一并克隆进 session——
|
|
||||||
/// 克隆体无根,下一轮收集按死对象出表,不泄漏也不悬垂。
|
|
||||||
pub(crate) fn rewrite_session_epoch_refs(
|
|
||||||
&mut self, objects: &[*mut JsObject], forwarding: &mut HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>,
|
|
||||||
) {
|
|
||||||
for &ptr in objects {
|
|
||||||
if ptr.is_null() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// SAFETY: ptr 来自 session_epoch.alloc,arena 存活期内有效。
|
|
||||||
unsafe {
|
|
||||||
let obj = &mut *ptr;
|
|
||||||
obj.rewrite_object_values(|value| self.promote_value_if_epoch_object(value, forwarding));
|
|
||||||
if obj.is_map() {
|
|
||||||
map::rewrite_map_native(obj, |value| self.promote_value_if_epoch_object(value, forwarding));
|
|
||||||
} else if obj.is_set() {
|
|
||||||
set::rewrite_set_native(obj, |value| self.promote_value_if_epoch_object(value, forwarding));
|
|
||||||
} else if obj.is_weak_map_obj() {
|
|
||||||
// 值边同强边改写;弱键留给转发表收敛后的定夺路径,
|
|
||||||
// 提前改写会引入晋升序依赖(键的强可达根尚未遍历完)。
|
|
||||||
weak_map::rewrite_weak_map_native_values(obj, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if obj.is_disposable_stack_obj() || obj.is_async_disposable_stack_obj() {
|
|
||||||
disposable_stack::rewrite_dispose_native(obj, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if obj.is_typed_array_obj() {
|
|
||||||
typed_array::rewrite_typed_array_native(obj, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if obj.is_data_view_obj() {
|
|
||||||
data_view::rewrite_data_view_native(obj, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if obj.is_generator_obj() {
|
|
||||||
crate::generator::rewrite_generator_native(obj, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if obj.is_promise_obj() {
|
|
||||||
crate::promise::rewrite_promise_native(obj, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if obj.is_async_obj() {
|
|
||||||
crate::async_func::rewrite_async_native(obj, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if obj.is_async_generator_obj() {
|
|
||||||
crate::async_generator::rewrite_async_generator_native(obj, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
} else if obj.is_module_namespace() {
|
|
||||||
module::rewrite_module_ns_native(obj, |value| {
|
|
||||||
self.promote_value_if_epoch_object(value, forwarding)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// global 对象不入 session 对象表:其 epoch 子引用(逃逸写直通留存、
|
|
||||||
// 函数目标豁免、native 盒直插)与列表同趟修复,免 epoch 释放后悬垂。
|
|
||||||
let global_ptr = self.session.global_object().as_ptr() as *mut JsObject;
|
|
||||||
// SAFETY: global 归本 session 所有,安全点内指针有效。
|
|
||||||
unsafe {
|
|
||||||
let obj = &mut *global_ptr;
|
|
||||||
obj.rewrite_object_values(|value| self.promote_value_if_epoch_object(value, forwarding));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 晋升收敛后的弱键定夺:对候选集(session 对象列表 + 已晋升克隆)中的
|
|
||||||
/// 每个弱表按定夺路径处理条目。统一入口 Box 化后定夺退化为原样保留
|
|
||||||
/// (无克隆可重指、reset 边界 mark 位无本轮可达性信息),本路径不改表项。
|
|
||||||
///
|
|
||||||
/// # 副作用
|
|
||||||
/// - 每个弱表原位重建条目表(整表重建口径,条目内容不变)。
|
|
||||||
///
|
|
||||||
/// # 边界与前提
|
|
||||||
/// - 须在主改写路径(JS 边 + 值边)对转发表收敛完成后调用;转发表届时
|
|
||||||
/// 即最终强可达集(统一入口树上恒空)。调用方负责其后的清表。
|
|
||||||
/// - global 上的弱表经其晋升克隆进入转发表值集,候选集无需单列 global。
|
|
||||||
pub(crate) fn rewrite_weak_map_keys_after_promotion(
|
|
||||||
&mut self, objects: &[*mut JsObject], forwarding: &mut HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>,
|
|
||||||
) {
|
|
||||||
for &ptr in objects {
|
|
||||||
if ptr.is_null() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// SAFETY: session 对象在 arena 存活期内有效。
|
|
||||||
unsafe {
|
|
||||||
let obj = &mut *ptr;
|
|
||||||
if obj.is_weak_map_obj() {
|
|
||||||
weak_map::rewrite_weak_map_native(
|
|
||||||
obj,
|
|
||||||
crate::session_gc::resolve_weak_key_after_promotion,
|
|
||||||
|value| value,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for &new_ptr in forwarding.values() {
|
|
||||||
// SAFETY: 晋升克隆分配于 session arena,存活期内有效。
|
|
||||||
unsafe {
|
|
||||||
let obj = &mut *new_ptr;
|
|
||||||
if obj.is_weak_map_obj() {
|
|
||||||
weak_map::rewrite_weak_map_native(
|
|
||||||
obj,
|
|
||||||
crate::session_gc::resolve_weak_key_after_promotion,
|
|
||||||
|value| value,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 把根直接持有的 epoch 对象(顶层 var 寄存器、挂起句柄等)晋升进 session,
|
|
||||||
/// 并把根引用改写到克隆体。统一入口 Box 化后生产路径无 epoch 对象,
|
|
||||||
/// 本方法为晋升族空转保留面(基准留存测量与图留存测试仍经此入口)。
|
|
||||||
///
|
|
||||||
/// # 副作用
|
|
||||||
/// - 每个根 epoch 对象深克隆进 session(含 native 状态盒,子引用递归晋升),
|
|
||||||
/// 账目计入克隆字节;根引用按局部转发表重写。
|
|
||||||
///
|
|
||||||
/// # 注意事项
|
|
||||||
/// - 须在执行外的安全点调用(无在途 builtin 局部裸指针、dispatch 未重入):
|
|
||||||
/// 本方法改写全部根寄存器槽,执行期调用将使 builtin 局部裸指针失效
|
|
||||||
/// (与 `collect_session_gc` 同前提)。
|
|
||||||
/// - 与 `promote_session_epoch_refs` 互补:本方法处理根直接持有的 epoch 对象,
|
|
||||||
/// 后者处理 session 对象持有的 epoch 子引用(闭包捕获等绕过写屏障的来源)。
|
|
||||||
pub fn promote_rooted_epoch_objects(&mut self) {
|
|
||||||
let mut epoch_roots = Vec::new();
|
|
||||||
self.for_each_root(|value| {
|
|
||||||
if value.is_object() {
|
|
||||||
let ptr = value.as_js_object_ptr();
|
|
||||||
if !ptr.is_null() {
|
|
||||||
// SAFETY: 根上的对象值均为 VM 自有存活 JsObject。
|
|
||||||
if unsafe { &*ptr }.is_epoch() {
|
|
||||||
epoch_roots.push(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// 局部转发表:晋升克隆去重共享与环,作用域结束即弃。
|
|
||||||
let mut forwarding = HashMap::with_hasher(FxBuildHasher);
|
|
||||||
for ptr in epoch_roots {
|
|
||||||
self.promote_object_inner(ptr, &mut forwarding);
|
|
||||||
}
|
|
||||||
crate::session_gc::rewrite_vm_roots(self, &forwarding);
|
|
||||||
// 弱表键定夺退化为原样保留(定夺路径无克隆可重指,见
|
|
||||||
// resolve_weak_key_after_promotion);调用保留以收口族内流程。
|
|
||||||
self.rewrite_weak_map_keys_after_promotion(&[], &mut forwarding);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 逃逸写屏障:写向全局/session 目标的对象值原样直通。
|
|
||||||
///
|
|
||||||
/// session 对象持有 epoch 子引用是 GC 设计的一等存活态:epoch 边界 reset
|
|
||||||
/// 经 `promote_session_epoch_refs` 统一克隆晋升(转发表去重共享与环)
|
|
||||||
/// 覆盖全部跨界持有面;builtin 调用期内 epoch 不重置,帧局部裸指针
|
|
||||||
/// 全程有效。此处若克隆,同一逻辑对象分裂为原件与克隆两份,经克隆的写
|
|
||||||
/// 对方经原件的读不可见(builtin 帧指针与逃逸目标双引用并存时即暴露)。
|
|
||||||
pub(crate) fn promote_if_needed_for_write_ptr(&mut self, _target_ptr: *mut JsObject, value: JsValue) -> JsValue {
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
|
||||||
use oxide_types::object::{PropAttributes, PropMetaEntry};
|
|
||||||
|
|
||||||
fn plain_object(vm: &mut Vm) -> *mut JsObject {
|
|
||||||
let proto = vm.session.builtin_world().object_proto.as_ptr() as *mut JsObject;
|
|
||||||
let ptr = vm
|
|
||||||
.epoch
|
|
||||||
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto)));
|
|
||||||
// 测试辅助函数绕过 alloc_object,需手动置位 EPOCH_BIT。
|
|
||||||
unsafe { (*ptr).set_is_epoch(true) };
|
|
||||||
ptr
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_epoch_object(_vm: &Vm, value: JsValue) -> bool {
|
|
||||||
value.is_object() && unsafe { (&*value.as_js_object_ptr()).is_epoch() }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn session_arena_promotion_preserves_cycle_identity() {
|
|
||||||
let mut vm = Vm::new();
|
|
||||||
let root = plain_object(&mut vm);
|
|
||||||
unsafe {
|
|
||||||
(*root).set_prop_at(0, JsValue::from_js_object(root));
|
|
||||||
}
|
|
||||||
|
|
||||||
let promoted = vm.promote_object(root);
|
|
||||||
let promoted_obj = unsafe { &*promoted };
|
|
||||||
|
|
||||||
assert!(promoted_obj.is_session_epoch());
|
|
||||||
assert!(std::ptr::eq(promoted_obj.get_prop_at(0).as_js_object_ptr(), promoted));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn session_arena_promotion_preserves_duplicate_child_reference() {
|
|
||||||
let mut vm = Vm::new();
|
|
||||||
let root = plain_object(&mut vm);
|
|
||||||
let child = plain_object(&mut vm);
|
|
||||||
unsafe {
|
|
||||||
(*root).set_prop_at(0, JsValue::from_js_object(child));
|
|
||||||
(*root).set_prop_at(1, JsValue::from_js_object(child));
|
|
||||||
}
|
|
||||||
|
|
||||||
let promoted = vm.promote_object(root);
|
|
||||||
let promoted_obj = unsafe { &*promoted };
|
|
||||||
let left = promoted_obj.get_prop_at(0).as_js_object_ptr();
|
|
||||||
let right = promoted_obj.get_prop_at(1).as_js_object_ptr();
|
|
||||||
|
|
||||||
assert!(std::ptr::eq(left, right));
|
|
||||||
assert!(!std::ptr::eq(left, child));
|
|
||||||
assert!(unsafe { (&*left).is_session_epoch() });
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn session_arena_captured_and_home_object_links_promote() {
|
|
||||||
let mut vm = Vm::new();
|
|
||||||
let root = plain_object(&mut vm);
|
|
||||||
let proto = plain_object(&mut vm);
|
|
||||||
let captured = plain_object(&mut vm);
|
|
||||||
let home = plain_object(&mut vm);
|
|
||||||
let getter = plain_object(&mut vm);
|
|
||||||
let setter = plain_object(&mut vm);
|
|
||||||
unsafe {
|
|
||||||
(*root).set_proto(JsValue::from_js_object(proto)).expect("proto");
|
|
||||||
(*root).set_captured_this(JsValue::from_js_object(captured));
|
|
||||||
(*root).set_home_object(JsValue::from_js_object(home));
|
|
||||||
(*root).set_prop_at(0, JsValue::undefined());
|
|
||||||
(*root).set_accessor_meta(
|
|
||||||
0,
|
|
||||||
JsValue::from_js_object(getter),
|
|
||||||
JsValue::from_js_object(setter),
|
|
||||||
PropAttributes::DEFAULT_DATA,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let promoted = vm.promote_object(root);
|
|
||||||
let promoted_obj = unsafe { &*promoted };
|
|
||||||
let meta = promoted_obj.prop_meta_at(0).expect("meta");
|
|
||||||
|
|
||||||
assert!(!is_epoch_object(&vm, promoted_obj.proto()));
|
|
||||||
assert!(!is_epoch_object(&vm, promoted_obj.captured_this()));
|
|
||||||
assert!(!is_epoch_object(&vm, promoted_obj.home_object()));
|
|
||||||
assert!(!is_epoch_object(&vm, meta.get));
|
|
||||||
assert!(!is_epoch_object(&vm, meta.set));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn session_arena_promotes_array_elements_and_element_meta() {
|
|
||||||
let mut vm = Vm::new();
|
|
||||||
let elem_child = plain_object(&mut vm);
|
|
||||||
let meta_getter = plain_object(&mut vm);
|
|
||||||
let array_proto = vm.session.builtin_world().array_proto.as_ptr() as *mut JsObject;
|
|
||||||
let arr = vm.epoch.alloc(JsObject::new_array(
|
|
||||||
EMPTY_SHAPE_ID,
|
|
||||||
JsValue::from_js_object(array_proto),
|
|
||||||
2,
|
|
||||||
vm.epoch.bump(),
|
|
||||||
));
|
|
||||||
// 测试辅助函数绕过 alloc_object,需手动置位 EPOCH_BIT。
|
|
||||||
unsafe { (*arr).set_is_epoch(true) };
|
|
||||||
unsafe {
|
|
||||||
(*arr).set_prop_at(0, JsValue::from_js_object(elem_child));
|
|
||||||
(*arr).set_accessor_meta(
|
|
||||||
1,
|
|
||||||
JsValue::from_js_object(meta_getter),
|
|
||||||
JsValue::undefined(),
|
|
||||||
PropAttributes::DEFAULT_DATA,
|
|
||||||
);
|
|
||||||
(*arr).set_prop_shape(0, JsValue::int(99));
|
|
||||||
}
|
|
||||||
|
|
||||||
let promoted = vm.promote_object(arr);
|
|
||||||
let promoted_arr = unsafe { &*promoted };
|
|
||||||
|
|
||||||
assert!(promoted_arr.is_session_epoch());
|
|
||||||
// 数组元素(含访问器 get)与命名属性随 clone + rewrite 迁移到 session。
|
|
||||||
assert!(!is_epoch_object(&vm, promoted_arr.get_prop_at(0)));
|
|
||||||
let meta = promoted_arr.prop_meta_at(1).expect("element accessor meta");
|
|
||||||
assert!(!is_epoch_object(&vm, meta.get));
|
|
||||||
assert_eq!(promoted_arr.get_prop_shape(0), JsValue::int(99));
|
|
||||||
// 元素计数与长度不因 promotion 改变。
|
|
||||||
assert_eq!(promoted_arr.prop_count(), 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn session_arena_barrier_global_root_write_returns_original() {
|
|
||||||
let mut vm = Vm::new();
|
|
||||||
let value = JsValue::from_js_object(plain_object(&mut vm));
|
|
||||||
let global_ptr = vm.session.global_object().as_ptr() as *mut JsObject;
|
|
||||||
|
|
||||||
let promoted = vm.promote_if_needed_for_write_ptr(global_ptr, value);
|
|
||||||
|
|
||||||
assert_eq!(promoted, value);
|
|
||||||
assert!(is_epoch_object(&vm, promoted), "epoch 子引用应直通留存,由边界/晋升档统一修复");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn session_arena_barrier_session_target_write_returns_original() {
|
|
||||||
let mut vm = Vm::new();
|
|
||||||
let target_epoch = plain_object(&mut vm);
|
|
||||||
let target = vm.promote_object(target_epoch);
|
|
||||||
let value = JsValue::from_js_object(plain_object(&mut vm));
|
|
||||||
|
|
||||||
let promoted = vm.promote_if_needed_for_write_ptr(target, value);
|
|
||||||
|
|
||||||
assert_eq!(promoted, value);
|
|
||||||
assert!(is_epoch_object(&vm, promoted), "session 目标上的 epoch 值应直通留存");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn session_arena_barrier_leaves_non_escape_target_write_unchanged() {
|
|
||||||
let mut vm = Vm::new();
|
|
||||||
let target = plain_object(&mut vm);
|
|
||||||
let value = JsValue::from_js_object(plain_object(&mut vm));
|
|
||||||
|
|
||||||
let unchanged = vm.promote_if_needed_for_write_ptr(target, value);
|
|
||||||
|
|
||||||
assert_eq!(unchanged, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn session_arena_promotion_rewrites_accessor_metadata_values() {
|
|
||||||
let mut vm = Vm::new();
|
|
||||||
let root = plain_object(&mut vm);
|
|
||||||
let getter = plain_object(&mut vm);
|
|
||||||
let setter = plain_object(&mut vm);
|
|
||||||
unsafe {
|
|
||||||
(*root).set_prop_at(0, JsValue::undefined());
|
|
||||||
(*root).set_accessor_meta(
|
|
||||||
0,
|
|
||||||
JsValue::from_js_object(getter),
|
|
||||||
JsValue::from_js_object(setter),
|
|
||||||
PropAttributes::DEFAULT_DATA,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let promoted = vm.promote_object(root);
|
|
||||||
let meta: PropMetaEntry = unsafe { &*promoted }.prop_meta_at(0).expect("meta");
|
|
||||||
|
|
||||||
assert!(!is_epoch_object(&vm, meta.get));
|
|
||||||
assert!(!is_epoch_object(&vm, meta.set));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -60,8 +60,8 @@ impl SessionGc {
|
|||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// SAFETY: session_object_ptrs 中的指针只来自 promote_object_inner 的
|
// SAFETY: session_object_ptrs 中的指针来自统一入口的 Box 化分配
|
||||||
// session_epoch.alloc,在 arena 存活期间有效。
|
// (alloc_object_box),对象堆内指针在释放前有效。
|
||||||
unsafe { (*ptr).set_gc_mark(false) };
|
unsafe { (*ptr).set_gc_mark(false) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1060,6 +1060,7 @@ impl Default for SessionGc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn rewrite_forwarded_value(
|
fn rewrite_forwarded_value(
|
||||||
value: JsValue, forwarding: &HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>,
|
value: JsValue, forwarding: &HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>,
|
||||||
) -> JsValue {
|
) -> JsValue {
|
||||||
@@ -1103,6 +1104,7 @@ pub(crate) fn resolve_weak_key_sweep(key: weak_map::WeakKey) -> Option<weak_map:
|
|||||||
/// # 边界与前提
|
/// # 边界与前提
|
||||||
/// - 不解引用任何对象本体(无位域读取),无定夺时序面;
|
/// - 不解引用任何对象本体(无位域读取),无定夺时序面;
|
||||||
/// - 晋升族整体为死代码保留面,弱表族本体退役时一并删除。
|
/// - 晋升族整体为死代码保留面,弱表族本体退役时一并删除。
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn resolve_weak_key_after_promotion(key: weak_map::WeakKey) -> Option<weak_map::WeakKey> {
|
pub(crate) fn resolve_weak_key_after_promotion(key: weak_map::WeakKey) -> Option<weak_map::WeakKey> {
|
||||||
Some(key)
|
Some(key)
|
||||||
}
|
}
|
||||||
@@ -1110,6 +1112,7 @@ pub(crate) fn resolve_weak_key_after_promotion(key: weak_map::WeakKey) -> Option
|
|||||||
/// 按 forwarding 表把全部 VM 根引用重写到搬移后的新地址。与 `for_each_value`
|
/// 按 forwarding 表把全部 VM 根引用重写到搬移后的新地址。与 `for_each_value`
|
||||||
/// 共用同一字段清单(经 `rewrite_values` 遍历)且须同步:遗漏字段会在搬移后
|
/// 共用同一字段清单(经 `rewrite_values` 遍历)且须同步:遗漏字段会在搬移后
|
||||||
/// 保留指向旧 arena 的悬垂指针。
|
/// 保留指向旧 arena 的悬垂指针。
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn rewrite_vm_roots(vm: &mut Vm, forwarding: &HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>) {
|
pub(crate) fn rewrite_vm_roots(vm: &mut Vm, forwarding: &HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>) {
|
||||||
vm_debug!("[GC] rewrite_vm_roots: {} forwarded objects", forwarding.len());
|
vm_debug!("[GC] rewrite_vm_roots: {} forwarded objects", forwarding.len());
|
||||||
// 统一遍历:与 for_each_value 共用同一字段清单。
|
// 统一遍历:与 for_each_value 共用同一字段清单。
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ fn has_ptr(roots: &[JsValue], ptr: *mut JsObject) -> bool {
|
|||||||
fn uncaught_value_is_gc_root() {
|
fn uncaught_value_is_gc_root() {
|
||||||
let mut vm = Vm::new();
|
let mut vm = Vm::new();
|
||||||
let obj = plain_object(&mut vm);
|
let obj = plain_object(&mut vm);
|
||||||
let session = vm.promote_object(obj);
|
let session = obj;
|
||||||
vm.last_uncaught_value = Some(JsValue::from_js_object(session));
|
vm.last_uncaught_value = Some(JsValue::from_js_object(session));
|
||||||
let mut roots = Vec::new();
|
let mut roots = Vec::new();
|
||||||
vm.for_each_root(|v| roots.push(v));
|
vm.for_each_root(|v| roots.push(v));
|
||||||
@@ -42,7 +42,7 @@ fn uncaught_value_is_gc_root() {
|
|||||||
fn pending_length_exception_is_gc_root() {
|
fn pending_length_exception_is_gc_root() {
|
||||||
let mut vm = Vm::new();
|
let mut vm = Vm::new();
|
||||||
let obj = plain_object(&mut vm);
|
let obj = plain_object(&mut vm);
|
||||||
let session = vm.promote_object(obj);
|
let session = obj;
|
||||||
vm.pending_length_exception = Some(JsValue::from_js_object(session));
|
vm.pending_length_exception = Some(JsValue::from_js_object(session));
|
||||||
let mut roots = Vec::new();
|
let mut roots = Vec::new();
|
||||||
vm.for_each_root(|v| roots.push(v));
|
vm.for_each_root(|v| roots.push(v));
|
||||||
@@ -55,9 +55,9 @@ fn suspended_signal_fields_are_roots() {
|
|||||||
let a = plain_object(&mut vm);
|
let a = plain_object(&mut vm);
|
||||||
let b = plain_object(&mut vm);
|
let b = plain_object(&mut vm);
|
||||||
let c = plain_object(&mut vm);
|
let c = plain_object(&mut vm);
|
||||||
let a_s = vm.promote_object(a);
|
let a_s = a;
|
||||||
let b_s = vm.promote_object(b);
|
let b_s = b;
|
||||||
let c_s = vm.promote_object(c);
|
let c_s = c;
|
||||||
vm.generator_suspended = Some(JsValue::from_js_object(a_s));
|
vm.generator_suspended = Some(JsValue::from_js_object(a_s));
|
||||||
vm.async_context = Some(JsValue::from_js_object(b_s));
|
vm.async_context = Some(JsValue::from_js_object(b_s));
|
||||||
vm.async_gen_context = Some(JsValue::from_js_object(c_s));
|
vm.async_gen_context = Some(JsValue::from_js_object(c_s));
|
||||||
@@ -80,10 +80,10 @@ fn gc_roots_contains_registers_frames_and_root_roots() {
|
|||||||
(*root).set_prop_at(0, JsValue::from_js_object(child));
|
(*root).set_prop_at(0, JsValue::from_js_object(child));
|
||||||
}
|
}
|
||||||
|
|
||||||
let root_session = vm.promote_object(root);
|
let root_session = root;
|
||||||
let frame_session = vm.promote_object(frame_obj);
|
let frame_session = frame_obj;
|
||||||
let this_session = vm.promote_object(saved_this);
|
let this_session = saved_this;
|
||||||
let child_session = vm.promote_object(child);
|
let child_session = child;
|
||||||
vm.regs[0] = JsValue::from_js_object(root_session);
|
vm.regs[0] = JsValue::from_js_object(root_session);
|
||||||
|
|
||||||
vm.frames.push(CallFrame {
|
vm.frames.push(CallFrame {
|
||||||
@@ -138,9 +138,9 @@ fn rewrite_matches_roots_coverage() {
|
|||||||
// 测试同口径,共同构成孪生清单验证)。
|
// 测试同口径,共同构成孪生清单验证)。
|
||||||
let mut vm = Vm::new();
|
let mut vm = Vm::new();
|
||||||
let obj = plain_object(&mut vm);
|
let obj = plain_object(&mut vm);
|
||||||
let session = vm.promote_object(obj);
|
let session = obj;
|
||||||
let marker = plain_object(&mut vm);
|
let marker = plain_object(&mut vm);
|
||||||
let marker_session = vm.promote_object(marker);
|
let marker_session = marker;
|
||||||
vm.regs[0] = JsValue::from_js_object(session);
|
vm.regs[0] = JsValue::from_js_object(session);
|
||||||
vm.last_uncaught_value = Some(JsValue::from_js_object(session));
|
vm.last_uncaught_value = Some(JsValue::from_js_object(session));
|
||||||
vm.generator_suspended = Some(JsValue::from_js_object(session));
|
vm.generator_suspended = Some(JsValue::from_js_object(session));
|
||||||
@@ -189,9 +189,9 @@ fn mark_phase_reaches_cycles_and_unreachable_are_unmarked() {
|
|||||||
(*reachable).set_prop_at(0, JsValue::from_js_object(root));
|
(*reachable).set_prop_at(0, JsValue::from_js_object(root));
|
||||||
}
|
}
|
||||||
|
|
||||||
let root_session = vm.promote_object(root);
|
let root_session = root;
|
||||||
let reachable_session = unsafe { (*root_session).get_prop_at(0).as_js_object_ptr() };
|
let reachable_session = unsafe { (*root_session).get_prop_at(0).as_js_object_ptr() };
|
||||||
let unreachable_session = vm.promote_object(unreachable);
|
let unreachable_session = unreachable;
|
||||||
|
|
||||||
vm.regs[0] = JsValue::from_js_object(root_session);
|
vm.regs[0] = JsValue::from_js_object(root_session);
|
||||||
let mut gc = std::mem::take(&mut vm.gc_state.session_gc);
|
let mut gc = std::mem::take(&mut vm.gc_state.session_gc);
|
||||||
@@ -215,9 +215,9 @@ fn sweep_preserves_cycle_and_collects_unreachable() {
|
|||||||
(*child).set_prop_at(0, JsValue::from_js_object(root));
|
(*child).set_prop_at(0, JsValue::from_js_object(root));
|
||||||
}
|
}
|
||||||
|
|
||||||
let root_session = vm.promote_object(root);
|
let root_session = root;
|
||||||
vm.regs[0] = JsValue::from_js_object(root_session);
|
vm.regs[0] = JsValue::from_js_object(root_session);
|
||||||
let dead_session = vm.promote_object(dead);
|
let dead_session = dead;
|
||||||
let mut gc = std::mem::take(&mut vm.gc_state.session_gc);
|
let mut gc = std::mem::take(&mut vm.gc_state.session_gc);
|
||||||
gc.mark(&vm);
|
gc.mark(&vm);
|
||||||
let _ = gc.sweep(&mut vm);
|
let _ = gc.sweep(&mut vm);
|
||||||
@@ -250,9 +250,9 @@ fn sweep_preserves_array_elements_and_collects_dead_element_object() {
|
|||||||
(*arr).set_prop_at(0, JsValue::from_js_object(live_elem));
|
(*arr).set_prop_at(0, JsValue::from_js_object(live_elem));
|
||||||
(*arr).set_prop_at(1, JsValue::from_js_object(dead_elem));
|
(*arr).set_prop_at(1, JsValue::from_js_object(dead_elem));
|
||||||
}
|
}
|
||||||
// 晋升数组会把元素对象一并带入 session;随后断开元素 1 的引用,使其成为
|
// 数组与其元素对象分配时同入 session 表;随后断开元素 1 的引用,使其成为
|
||||||
// mark 不可达的死对象,供 sweep 回收。
|
// mark 不可达的死对象,供 sweep 回收。
|
||||||
let arr_session = vm.promote_object(arr);
|
let arr_session = arr;
|
||||||
unsafe {
|
unsafe {
|
||||||
(*arr_session).set_prop_at(1, JsValue::undefined());
|
(*arr_session).set_prop_at(1, JsValue::undefined());
|
||||||
}
|
}
|
||||||
@@ -357,8 +357,7 @@ fn function_placeholder(vm: &mut Vm) -> JsValue {
|
|||||||
#[test]
|
#[test]
|
||||||
fn reset_maybe_collect_collects_after_threshold() {
|
fn reset_maybe_collect_collects_after_threshold() {
|
||||||
let mut vm = vm_with_low_threshold();
|
let mut vm = vm_with_low_threshold();
|
||||||
let obj = plain_object(&mut vm);
|
plain_object(&mut vm);
|
||||||
vm.promote_object(obj);
|
|
||||||
assert!(!vm.gc_state.session_object_ptrs.is_empty());
|
assert!(!vm.gc_state.session_object_ptrs.is_empty());
|
||||||
let tracked_before = vm.gc_state.session_object_ptrs.len();
|
let tracked_before = vm.gc_state.session_object_ptrs.len();
|
||||||
|
|
||||||
@@ -374,8 +373,7 @@ fn reset_maybe_collect_collects_after_threshold() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn gc_stats_summary_includes_collection() {
|
fn gc_stats_summary_includes_collection() {
|
||||||
let mut vm = vm_with_low_threshold();
|
let mut vm = vm_with_low_threshold();
|
||||||
let obj = plain_object(&mut vm);
|
plain_object(&mut vm);
|
||||||
vm.promote_object(obj);
|
|
||||||
vm.regs[0] = JsValue::undefined();
|
vm.regs[0] = JsValue::undefined();
|
||||||
vm.maybe_collect_session_gc();
|
vm.maybe_collect_session_gc();
|
||||||
let summary = vm.gc_state.session_gc.stats_summary();
|
let summary = vm.gc_state.session_gc.stats_summary();
|
||||||
@@ -389,7 +387,7 @@ fn full_collect_keeps_global_root_edges_in_place() {
|
|||||||
unsafe {
|
unsafe {
|
||||||
(*obj).set_prop_at(0, JsValue::int(42));
|
(*obj).set_prop_at(0, JsValue::int(42));
|
||||||
}
|
}
|
||||||
let old_ptr = vm.promote_object(obj);
|
let old_ptr = obj;
|
||||||
let key = vm.kernel_core.perm_interner().intern("gcRoot").0;
|
let key = vm.kernel_core.perm_interner().intern("gcRoot").0;
|
||||||
let global_ptr = vm.session.global_object().as_ptr() as *mut JsObject;
|
let global_ptr = vm.session.global_object().as_ptr() as *mut JsObject;
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -446,7 +444,7 @@ fn session_gc_traces_map_object_key_and_value() {
|
|||||||
vm.regs[2] = value;
|
vm.regs[2] = value;
|
||||||
native_ok(map::map_set(&mut vm, &[0, 1, 2]));
|
native_ok(map::map_set(&mut vm, &[0, 1, 2]));
|
||||||
|
|
||||||
let map_session = vm.promote_object(map_value.as_js_object_ptr());
|
let map_session = map_value.as_js_object_ptr();
|
||||||
vm.regs.fill(JsValue::undefined());
|
vm.regs.fill(JsValue::undefined());
|
||||||
vm.regs[0] = JsValue::from_js_object(map_session);
|
vm.regs[0] = JsValue::from_js_object(map_session);
|
||||||
let mut gc = std::mem::take(&mut vm.gc_state.session_gc);
|
let mut gc = std::mem::take(&mut vm.gc_state.session_gc);
|
||||||
@@ -470,7 +468,7 @@ fn session_gc_traces_set_object_key() {
|
|||||||
vm.regs[1] = key;
|
vm.regs[1] = key;
|
||||||
native_ok(set::set_add(&mut vm, &[0, 1]));
|
native_ok(set::set_add(&mut vm, &[0, 1]));
|
||||||
|
|
||||||
let set_session = vm.promote_object(set_value.as_js_object_ptr());
|
let set_session = set_value.as_js_object_ptr();
|
||||||
vm.regs.fill(JsValue::undefined());
|
vm.regs.fill(JsValue::undefined());
|
||||||
vm.regs[0] = JsValue::from_js_object(set_session);
|
vm.regs[0] = JsValue::from_js_object(set_session);
|
||||||
let mut gc = std::mem::take(&mut vm.gc_state.session_gc);
|
let mut gc = std::mem::take(&mut vm.gc_state.session_gc);
|
||||||
@@ -510,11 +508,11 @@ fn weak_map_entry_survives_promotion_with_live_key() {
|
|||||||
vm.regs[0] = JsValue::from_js_object(wm);
|
vm.regs[0] = JsValue::from_js_object(wm);
|
||||||
vm.regs[1] = JsValue::from_js_object(key);
|
vm.regs[1] = JsValue::from_js_object(key);
|
||||||
vm.regs[2] = JsValue::from_js_object(value);
|
vm.regs[2] = JsValue::from_js_object(value);
|
||||||
let wm_session = vm.promote_object(wm);
|
let wm_session = wm;
|
||||||
vm.regs.fill(JsValue::undefined());
|
vm.regs.fill(JsValue::undefined());
|
||||||
vm.regs[0] = JsValue::from_js_object(wm_session);
|
vm.regs[0] = JsValue::from_js_object(wm_session);
|
||||||
vm.regs[1] = JsValue::from_js_object(key);
|
vm.regs[1] = JsValue::from_js_object(key);
|
||||||
// 值的强根取表内读回(统一入口后晋升为原样返回,读回即原件)。
|
// 值的强根取表内读回(读回即原件)。
|
||||||
let value_root =
|
let value_root =
|
||||||
oxide_builtins::weak_map::weak_map_probe_get(unsafe { &*wm_session }, JsValue::from_js_object(key));
|
oxide_builtins::weak_map::weak_map_probe_get(unsafe { &*wm_session }, JsValue::from_js_object(key));
|
||||||
vm.regs[2] = value_root;
|
vm.regs[2] = value_root;
|
||||||
@@ -547,7 +545,7 @@ fn weak_map_entry_dropped_when_key_unrooted() {
|
|||||||
}
|
}
|
||||||
vm.regs[0] = JsValue::from_js_object(wm);
|
vm.regs[0] = JsValue::from_js_object(wm);
|
||||||
vm.regs[2] = JsValue::from_js_object(value);
|
vm.regs[2] = JsValue::from_js_object(value);
|
||||||
let wm_session = vm.promote_object(wm);
|
let wm_session = wm;
|
||||||
vm.regs.fill(JsValue::undefined());
|
vm.regs.fill(JsValue::undefined());
|
||||||
vm.regs[0] = JsValue::from_js_object(wm_session);
|
vm.regs[0] = JsValue::from_js_object(wm_session);
|
||||||
vm.regs[2] = JsValue::from_js_object(value);
|
vm.regs[2] = JsValue::from_js_object(value);
|
||||||
@@ -573,13 +571,13 @@ fn weak_map_value_edge_keeps_value_alive() {
|
|||||||
JsValue::from_js_object(value),
|
JsValue::from_js_object(value),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// 值的独立根随后撤销,存活仅靠弱表值边(统一入口后晋升为原样返回)。
|
// 值的独立根随后撤销,存活仅靠弱表值边。
|
||||||
vm.regs[2] = JsValue::from_js_object(value);
|
vm.regs[2] = JsValue::from_js_object(value);
|
||||||
let value_session = vm.promote_object(value);
|
let value_session = value;
|
||||||
vm.regs[2] = JsValue::from_js_object(value_session);
|
vm.regs[2] = JsValue::from_js_object(value_session);
|
||||||
vm.regs[0] = JsValue::from_js_object(wm);
|
vm.regs[0] = JsValue::from_js_object(wm);
|
||||||
vm.regs[1] = JsValue::from_js_object(key);
|
vm.regs[1] = JsValue::from_js_object(key);
|
||||||
let wm_session = vm.promote_object(wm);
|
let wm_session = wm;
|
||||||
vm.regs.fill(JsValue::undefined());
|
vm.regs.fill(JsValue::undefined());
|
||||||
vm.regs[0] = JsValue::from_js_object(wm_session);
|
vm.regs[0] = JsValue::from_js_object(wm_session);
|
||||||
vm.regs[1] = JsValue::from_js_object(key);
|
vm.regs[1] = JsValue::from_js_object(key);
|
||||||
@@ -687,7 +685,7 @@ fn session_gc_traces_map_string_and_bigint_value() {
|
|||||||
vm.regs[2] = bi;
|
vm.regs[2] = bi;
|
||||||
native_ok(map::map_set(&mut vm, &[0, 1, 2]));
|
native_ok(map::map_set(&mut vm, &[0, 1, 2]));
|
||||||
|
|
||||||
let map_session = vm.promote_object(map_value.as_js_object_ptr());
|
let map_session = map_value.as_js_object_ptr();
|
||||||
vm.regs.fill(JsValue::undefined());
|
vm.regs.fill(JsValue::undefined());
|
||||||
vm.regs[0] = JsValue::from_js_object(map_session);
|
vm.regs[0] = JsValue::from_js_object(map_session);
|
||||||
collect(&mut vm);
|
collect(&mut vm);
|
||||||
@@ -714,7 +712,7 @@ fn session_gc_traces_set_string_and_bigint_value() {
|
|||||||
vm.regs[1] = bi;
|
vm.regs[1] = bi;
|
||||||
native_ok(set::set_add(&mut vm, &[0, 1]));
|
native_ok(set::set_add(&mut vm, &[0, 1]));
|
||||||
|
|
||||||
let set_session = vm.promote_object(set_value.as_js_object_ptr());
|
let set_session = set_value.as_js_object_ptr();
|
||||||
vm.regs.fill(JsValue::undefined());
|
vm.regs.fill(JsValue::undefined());
|
||||||
vm.regs[0] = JsValue::from_js_object(set_session);
|
vm.regs[0] = JsValue::from_js_object(set_session);
|
||||||
collect(&mut vm);
|
collect(&mut vm);
|
||||||
@@ -744,7 +742,7 @@ fn session_gc_traces_dispose_stack_string_and_bigint_value() {
|
|||||||
vm.regs[1] = bi;
|
vm.regs[1] = bi;
|
||||||
native_ok(disposable_stack::disposable_stack_adopt(&mut vm, &[0, 1, 2]));
|
native_ok(disposable_stack::disposable_stack_adopt(&mut vm, &[0, 1, 2]));
|
||||||
|
|
||||||
let stack_session = vm.promote_object(stack_value.as_js_object_ptr());
|
let stack_session = stack_value.as_js_object_ptr();
|
||||||
vm.regs.fill(JsValue::undefined());
|
vm.regs.fill(JsValue::undefined());
|
||||||
vm.regs[0] = JsValue::from_js_object(stack_session);
|
vm.regs[0] = JsValue::from_js_object(stack_session);
|
||||||
collect(&mut vm);
|
collect(&mut vm);
|
||||||
@@ -780,8 +778,8 @@ fn session_gc_traces_promise_string_and_bigint_result() {
|
|||||||
let bi_ptr = bi.as_bigint_ptr() as *mut num_bigint::BigInt;
|
let bi_ptr = bi.as_bigint_ptr() as *mut num_bigint::BigInt;
|
||||||
vm.fulfill_promise(promise2, bi).expect("fulfill bigint");
|
vm.fulfill_promise(promise2, bi).expect("fulfill bigint");
|
||||||
|
|
||||||
let promise_session = vm.promote_object(promise.as_js_object_ptr());
|
let promise_session = promise.as_js_object_ptr();
|
||||||
let promise2_session = vm.promote_object(promise2.as_js_object_ptr());
|
let promise2_session = promise2.as_js_object_ptr();
|
||||||
vm.regs.fill(JsValue::undefined());
|
vm.regs.fill(JsValue::undefined());
|
||||||
vm.regs[0] = JsValue::from_js_object(promise_session);
|
vm.regs[0] = JsValue::from_js_object(promise_session);
|
||||||
vm.regs[1] = JsValue::from_js_object(promise2_session);
|
vm.regs[1] = JsValue::from_js_object(promise2_session);
|
||||||
@@ -806,7 +804,7 @@ fn session_gc_traces_suspended_generator_bigint_value() {
|
|||||||
.expect("run");
|
.expect("run");
|
||||||
|
|
||||||
let g_val = global_prop_opt(&vm, "g").expect("g 应挂在 global 上");
|
let g_val = global_prop_opt(&vm, "g").expect("g 应挂在 global 上");
|
||||||
let g_session = vm.promote_object(g_val.as_js_object_ptr());
|
let g_session = g_val.as_js_object_ptr();
|
||||||
let g_obj = unsafe { &*g_session };
|
let g_obj = unsafe { &*g_session };
|
||||||
// SAFETY: 生成器状态盒经 Box::into_raw 挂对象构造,生命周期与对象一致。
|
// SAFETY: 生成器状态盒经 Box::into_raw 挂对象构造,生命周期与对象一致。
|
||||||
let state = g_obj.native_data() as *mut crate::generator::GeneratorState;
|
let state = g_obj.native_data() as *mut crate::generator::GeneratorState;
|
||||||
@@ -879,7 +877,7 @@ fn session_gc_traces_suspended_async_generator_bigint_value() {
|
|||||||
.expect("run");
|
.expect("run");
|
||||||
|
|
||||||
let it_val = global_prop_opt(&vm, "it").expect("it 应挂在 global 上");
|
let it_val = global_prop_opt(&vm, "it").expect("it 应挂在 global 上");
|
||||||
let it_session = vm.promote_object(it_val.as_js_object_ptr());
|
let it_session = it_val.as_js_object_ptr();
|
||||||
let it_obj = unsafe { &*it_session };
|
let it_obj = unsafe { &*it_session };
|
||||||
// SAFETY: 异步生成器状态盒经 Box::into_raw 挂迭代器构造,生命周期与对象一致。
|
// SAFETY: 异步生成器状态盒经 Box::into_raw 挂迭代器构造,生命周期与对象一致。
|
||||||
let state = it_obj.native_data() as *mut crate::async_generator::AsyncGeneratorState;
|
let state = it_obj.native_data() as *mut crate::async_generator::AsyncGeneratorState;
|
||||||
@@ -928,7 +926,7 @@ fn session_gc_keeps_shared_array_buffer_alive_through_view_native_edges() {
|
|||||||
(*root).set_prop_at(1, view);
|
(*root).set_prop_at(1, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
let root_session = vm.promote_object(root);
|
let root_session = root;
|
||||||
vm.regs.fill(JsValue::undefined());
|
vm.regs.fill(JsValue::undefined());
|
||||||
vm.regs[0] = JsValue::from_js_object(root_session);
|
vm.regs[0] = JsValue::from_js_object(root_session);
|
||||||
let mut gc = std::mem::take(&mut vm.gc_state.session_gc);
|
let mut gc = std::mem::take(&mut vm.gc_state.session_gc);
|
||||||
@@ -981,7 +979,7 @@ fn session_gc_rewrites_buffer_retained_only_by_data_view_native_edge() {
|
|||||||
vm.regs[2] = JsValue::int(9);
|
vm.regs[2] = JsValue::int(9);
|
||||||
native_ok(data_view::data_view_set_int32(&mut vm, &[0, 1, 2]));
|
native_ok(data_view::data_view_set_int32(&mut vm, &[0, 1, 2]));
|
||||||
|
|
||||||
let view_session = vm.promote_object(view.as_js_object_ptr());
|
let view_session = view.as_js_object_ptr();
|
||||||
vm.regs.fill(JsValue::undefined());
|
vm.regs.fill(JsValue::undefined());
|
||||||
vm.regs[0] = JsValue::from_js_object(view_session);
|
vm.regs[0] = JsValue::from_js_object(view_session);
|
||||||
let mut gc = std::mem::take(&mut vm.gc_state.session_gc);
|
let mut gc = std::mem::take(&mut vm.gc_state.session_gc);
|
||||||
@@ -1043,7 +1041,7 @@ fn session_string_survives_via_object_property() {
|
|||||||
unsafe {
|
unsafe {
|
||||||
(*obj).set_prop_at(0, s);
|
(*obj).set_prop_at(0, s);
|
||||||
}
|
}
|
||||||
let obj_session = vm.promote_object(obj);
|
let obj_session = obj;
|
||||||
|
|
||||||
// 该字符串仅通过存活对象的属性可达,不经过任何寄存器。
|
// 该字符串仅通过存活对象的属性可达,不经过任何寄存器。
|
||||||
vm.regs.fill(JsValue::undefined());
|
vm.regs.fill(JsValue::undefined());
|
||||||
@@ -1282,7 +1280,7 @@ fn suspended_state_box_cell_root_survives_collect() {
|
|||||||
.expect("run");
|
.expect("run");
|
||||||
|
|
||||||
let g_val = global_prop_opt(&vm, "g").expect("g 应挂在 global 上");
|
let g_val = global_prop_opt(&vm, "g").expect("g 应挂在 global 上");
|
||||||
let g_session = vm.promote_object(g_val.as_js_object_ptr());
|
let g_session = g_val.as_js_object_ptr();
|
||||||
let g_obj = unsafe { &*g_session };
|
let g_obj = unsafe { &*g_session };
|
||||||
// SAFETY: 生成器状态盒经 Box::into_raw 挂对象构造,生命周期与对象一致。
|
// SAFETY: 生成器状态盒经 Box::into_raw 挂对象构造,生命周期与对象一致。
|
||||||
let state = g_obj.native_data() as *mut crate::generator::GeneratorState;
|
let state = g_obj.native_data() as *mut crate::generator::GeneratorState;
|
||||||
@@ -1413,23 +1411,23 @@ fn strings_only_collection_preserves_all_root_kinds() {
|
|||||||
// 寄存器根:直接持有 session 串。
|
// 寄存器根:直接持有 session 串。
|
||||||
let reg_str = vm.new_string_owned("reg-root".repeat(16));
|
let reg_str = vm.new_string_owned("reg-root".repeat(16));
|
||||||
vm.regs[0] = reg_str;
|
vm.regs[0] = reg_str;
|
||||||
// 存活对象属性根:session 对象经 promote 后持有 session 串(分配即触发回收,
|
// 存活对象属性根:session 对象持有 session 串(分配即触发回收,
|
||||||
// reg_str 仍在寄存器,prop_str 挂到对象后才被下次回收看到)。
|
// reg_str 仍在寄存器,prop_str 挂到对象后才被下次回收看到)。
|
||||||
let obj = plain_object(&mut vm);
|
let obj = plain_object(&mut vm);
|
||||||
let prop_str = vm.new_string_owned("prop-root".repeat(16));
|
let prop_str = vm.new_string_owned("prop-root".repeat(16));
|
||||||
unsafe {
|
unsafe {
|
||||||
(*obj).set_prop_at(0, prop_str);
|
(*obj).set_prop_at(0, prop_str);
|
||||||
}
|
}
|
||||||
let obj_session = vm.promote_object(obj);
|
let obj_session = obj;
|
||||||
vm.regs[1] = JsValue::from_js_object(obj_session);
|
vm.regs[1] = JsValue::from_js_object(obj_session);
|
||||||
// 非 session 根对象属性:epoch 根对象(在寄存器)持有 session 串,mark 走
|
// 寄存器根对象属性:根对象(在寄存器)持有 session 串,mark 走根扫描
|
||||||
// 非 session 根扫描路径保护。
|
// 路径保护。
|
||||||
let epoch_obj = plain_object(&mut vm);
|
let root_obj = plain_object(&mut vm);
|
||||||
let epoch_str = vm.new_string_owned("epoch-root".repeat(16));
|
let epoch_str = vm.new_string_owned("epoch-root".repeat(16));
|
||||||
unsafe {
|
unsafe {
|
||||||
(*epoch_obj).set_prop_at(0, epoch_str);
|
(*root_obj).set_prop_at(0, epoch_str);
|
||||||
}
|
}
|
||||||
vm.regs[2] = JsValue::from_js_object(epoch_obj);
|
vm.regs[2] = JsValue::from_js_object(root_obj);
|
||||||
// 死串:无任何根引用,最后手动触发一轮回收它。
|
// 死串:无任何根引用,最后手动触发一轮回收它。
|
||||||
let dead = vm.new_string_owned("dead".repeat(16));
|
let dead = vm.new_string_owned("dead".repeat(16));
|
||||||
let dead_ptr = dead.as_string_ptr_mut();
|
let dead_ptr = dead.as_string_ptr_mut();
|
||||||
@@ -1450,7 +1448,7 @@ fn strings_only_collection_preserves_all_root_kinds() {
|
|||||||
fn strings_only_collection_does_not_move_objects() {
|
fn strings_only_collection_does_not_move_objects() {
|
||||||
let mut vm = vm_with_threshold(1);
|
let mut vm = vm_with_threshold(1);
|
||||||
let obj = plain_object(&mut vm);
|
let obj = plain_object(&mut vm);
|
||||||
let obj_session = vm.promote_object(obj);
|
let obj_session = obj;
|
||||||
vm.regs[0] = JsValue::from_js_object(obj_session);
|
vm.regs[0] = JsValue::from_js_object(obj_session);
|
||||||
let before: Vec<_> = vm.gc_state.session_object_ptrs.clone();
|
let before: Vec<_> = vm.gc_state.session_object_ptrs.clone();
|
||||||
|
|
||||||
@@ -1469,7 +1467,7 @@ fn strings_only_collection_does_not_move_objects() {
|
|||||||
fn multiple_strings_only_cycles_keep_objects_alive() {
|
fn multiple_strings_only_cycles_keep_objects_alive() {
|
||||||
let mut vm = vm_with_threshold(1);
|
let mut vm = vm_with_threshold(1);
|
||||||
let obj = plain_object(&mut vm);
|
let obj = plain_object(&mut vm);
|
||||||
let obj_session = vm.promote_object(obj);
|
let obj_session = obj;
|
||||||
let live = vm.new_string_owned("keep".repeat(32));
|
let live = vm.new_string_owned("keep".repeat(32));
|
||||||
unsafe {
|
unsafe {
|
||||||
(*obj_session).set_prop_at(0, live);
|
(*obj_session).set_prop_at(0, live);
|
||||||
@@ -1500,7 +1498,7 @@ fn strings_only_then_full_collect_keeps_object_strings_live() {
|
|||||||
unsafe {
|
unsafe {
|
||||||
(*obj).set_prop_at(0, s);
|
(*obj).set_prop_at(0, s);
|
||||||
}
|
}
|
||||||
let obj_session = vm.promote_object(obj);
|
let obj_session = obj;
|
||||||
vm.regs[0] = JsValue::from_js_object(obj_session);
|
vm.regs[0] = JsValue::from_js_object(obj_session);
|
||||||
|
|
||||||
// 第一轮 strings-only:对象被 mark 置位。收尾清残留,否则该位残留至完整
|
// 第一轮 strings-only:对象被 mark 置位。收尾清残留,否则该位残留至完整
|
||||||
@@ -1523,7 +1521,7 @@ fn strings_only_then_full_collect_keeps_object_strings_live() {
|
|||||||
fn strings_only_collection_byte_accounting_matches_survivors() {
|
fn strings_only_collection_byte_accounting_matches_survivors() {
|
||||||
let mut vm = vm_with_threshold(1);
|
let mut vm = vm_with_threshold(1);
|
||||||
let obj = plain_object(&mut vm);
|
let obj = plain_object(&mut vm);
|
||||||
let obj_session = vm.promote_object(obj);
|
let obj_session = obj;
|
||||||
vm.regs[0] = JsValue::from_js_object(obj_session);
|
vm.regs[0] = JsValue::from_js_object(obj_session);
|
||||||
let live = vm.new_string_owned("live".repeat(8));
|
let live = vm.new_string_owned("live".repeat(8));
|
||||||
let live_ptr = live.as_string_ptr_mut();
|
let live_ptr = live.as_string_ptr_mut();
|
||||||
@@ -1710,7 +1708,7 @@ fn full_reset_frees_rope_and_product() {
|
|||||||
// ── 执行期两档收集 ──────────────────────────────────────────────────────
|
// ── 执行期两档收集 ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// 执行期收集有效性:死 epoch 对象随换新 Bump 回收、死 session 对象原地
|
/// 执行期收集有效性:死 epoch 对象随换新 Bump 回收、死 session 对象原地
|
||||||
/// 回收出表,活对象晋升 session 后值保持可读。
|
/// 回收出表,活对象值保持可读。
|
||||||
#[test]
|
#[test]
|
||||||
fn in_run_collection_reclaims_dead_and_keeps_live() {
|
fn in_run_collection_reclaims_dead_and_keeps_live() {
|
||||||
let mut vm = vm_with_threshold(65536);
|
let mut vm = vm_with_threshold(65536);
|
||||||
@@ -1807,12 +1805,12 @@ fn in_run_collection_keeps_live_addresses_stable() {
|
|||||||
vm.run(&Arc::new(compile("globalThis.o = { a: [1, 2, 3], f: function() { return 1; } }; 0")))
|
vm.run(&Arc::new(compile("globalThis.o = { a: [1, 2, 3], f: function() { return 1; } }; 0")))
|
||||||
.expect("run1");
|
.expect("run1");
|
||||||
|
|
||||||
// 第一轮执行期收集:o 及其 epoch 子引用晋升 session。
|
// 第一轮执行期收集:o 分配即入 session 表(统一入口)。
|
||||||
vm.maybe_collect_in_run();
|
vm.maybe_collect_in_run();
|
||||||
assert!(vm.session_gc_stats().total_collections >= 1, "第一轮收集应执行");
|
assert!(vm.session_gc_stats().total_collections >= 1, "第一轮收集应执行");
|
||||||
let o = global_prop_opt(&vm, "o").expect("o 应挂在 global");
|
let o = global_prop_opt(&vm, "o").expect("o 应挂在 global");
|
||||||
let o_ptr = o.as_js_object_ptr();
|
let o_ptr = o.as_js_object_ptr();
|
||||||
assert!(unsafe { (*o_ptr).is_session_epoch() }, "o 晋升后应为 session 对象");
|
assert!(unsafe { (*o_ptr).is_session_epoch() }, "o 应为 session 对象");
|
||||||
let a_ptr = vm
|
let a_ptr = vm
|
||||||
.resolve_property(unsafe { &*o_ptr }, vm.kernel_core().perm_interner().intern("a").0)
|
.resolve_property(unsafe { &*o_ptr }, vm.kernel_core().perm_interner().intern("a").0)
|
||||||
.expect("o.a")
|
.expect("o.a")
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ impl SuspendedFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// GC 指针重写:与 `for_each_value` 字段一一对应。
|
/// GC 指针重写:与 `for_each_value` 字段一一对应。
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn rewrite_values(&mut self, mut rewrite: impl FnMut(JsValue) -> JsValue) {
|
pub fn rewrite_values(&mut self, mut rewrite: impl FnMut(JsValue) -> JsValue) {
|
||||||
for v in self.regs.iter_mut() {
|
for v in self.regs.iter_mut() {
|
||||||
*v = rewrite(*v);
|
*v = rewrite(*v);
|
||||||
@@ -324,6 +325,7 @@ impl SuspendedFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 深拷贝(promote / sweep 搬移用,替代 async/asyncgen 各自的 clone_*_with_rewrite)。
|
/// 深拷贝(promote / sweep 搬移用,替代 async/asyncgen 各自的 clone_*_with_rewrite)。
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn clone_with_rewrite(&self, mut rewrite: impl FnMut(JsValue) -> JsValue) -> Self {
|
pub fn clone_with_rewrite(&self, mut rewrite: impl FnMut(JsValue) -> JsValue) -> Self {
|
||||||
SuspendedFrame {
|
SuspendedFrame {
|
||||||
regs: Box::new({
|
regs: Box::new({
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// GC 指针重写(session 搬移后调用)。与 `for_each_value` 字段一一对应。
|
/// GC 指针重写(session 搬移后调用)。与 `for_each_value` 字段一一对应。
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn rewrite_values(&mut self, mut rewrite: impl FnMut(JsValue) -> JsValue) {
|
pub(crate) fn rewrite_values(&mut self, mut rewrite: impl FnMut(JsValue) -> JsValue) {
|
||||||
for value in &mut self.regs {
|
for value in &mut self.regs {
|
||||||
*value = rewrite(*value);
|
*value = rewrite(*value);
|
||||||
|
|||||||
@@ -194,8 +194,8 @@ impl oxide_runtime_api::VmHost for Vm {
|
|||||||
|| std::ptr::eq(p, self.async_function_proto.as_ptr())
|
|| std::ptr::eq(p, self.async_function_proto.as_ptr())
|
||||||
|| std::ptr::eq(p, self.async_generator_function_proto.as_ptr())
|
|| std::ptr::eq(p, self.async_generator_function_proto.as_ptr())
|
||||||
}
|
}
|
||||||
fn promote_if_needed_for_write_ptr(&mut self, target_ptr: *mut JsObject, value: JsValue) -> JsValue {
|
fn promote_if_needed_for_write_ptr(&mut self, _target_ptr: *mut JsObject, value: JsValue) -> JsValue {
|
||||||
self.promote_if_needed_for_write_ptr(target_ptr, value)
|
value
|
||||||
}
|
}
|
||||||
fn step_rng(&mut self) {
|
fn step_rng(&mut self) {
|
||||||
self.step_rng()
|
self.step_rng()
|
||||||
|
|||||||
@@ -624,8 +624,8 @@ impl Vm {
|
|||||||
/// # 注意事项
|
/// # 注意事项
|
||||||
/// - 须在换表注册新代际**之前**调用(此刻 current_gen 仍指上一 run 的表,
|
/// - 须在换表注册新代际**之前**调用(此刻 current_gen 仍指上一 run 的表,
|
||||||
/// 新表未注册天然不被回收)。
|
/// 新表未注册天然不被回收)。
|
||||||
/// - 执行外安全点调用(dispatch 未重入、无在途 builtin 局部裸指针),与
|
/// - 执行外安全点调用(dispatch 未重入、无在途 builtin 局部裸指针),
|
||||||
/// `promote_session_epoch_refs` 同前提:session 对象表此刻全部有效。
|
/// session 对象表此刻全部有效。
|
||||||
pub(crate) fn reclaim_unreferenced_tables(&mut self) {
|
pub(crate) fn reclaim_unreferenced_tables(&mut self) {
|
||||||
// 存活代际集:函数对象只经创建点直落 session 对象表登记(或晋升克隆
|
// 存活代际集:函数对象只经创建点直落 session 对象表登记(或晋升克隆
|
||||||
// 随行),扫全表即完备;native 函数哨兵 sub_module_index == 0 不计数。
|
// 随行),扫全表即完备;native 函数哨兵 sub_module_index == 0 不计数。
|
||||||
|
|||||||
@@ -549,9 +549,6 @@ impl Vm {
|
|||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
self.clear_execution_state();
|
self.clear_execution_state();
|
||||||
self.maybe_collect_session_gc();
|
self.maybe_collect_session_gc();
|
||||||
// 晋升族空转调用保留:统一入口 Box 化后生产路径不产 epoch 对象,
|
|
||||||
// 本调用在统一表上恒无操作(随晋升族整体退役)。
|
|
||||||
self.promote_session_epoch_refs();
|
|
||||||
self.bytecode = Arc::default();
|
self.bytecode = Arc::default();
|
||||||
// 表代际注册表不动:存活函数对象(含挂起帧 callee)按创建期代际仍须
|
// 表代际注册表不动:存活函数对象(含挂起帧 callee)按创建期代际仍须
|
||||||
// 命中原表,跨 run 调用与恢复靠它成立。active_immutables 指向的旧表
|
// 命中原表,跨 run 调用与恢复靠它成立。active_immutables 指向的旧表
|
||||||
@@ -565,6 +562,11 @@ impl Vm {
|
|||||||
self.active_reg_limit = 0;
|
self.active_reg_limit = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 逃逸写屏障:写向全局/session 目标的对象值原样直通,不克隆。
|
||||||
|
pub(crate) fn promote_if_needed_for_write_ptr(&mut self, _target_ptr: *mut JsObject, value: JsValue) -> JsValue {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
/// 分配一个可被 session GC 回收的字符串 `JsValue`(session-heap 字符串)。
|
/// 分配一个可被 session GC 回收的字符串 `JsValue`(session-heap 字符串)。
|
||||||
pub fn new_string(&mut self, s: &str) -> JsValue {
|
pub fn new_string(&mut self, s: &str) -> JsValue {
|
||||||
self.new_string_owned(s.to_string())
|
self.new_string_owned(s.to_string())
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
//! mem_object_graph 留存计数回归锚:逃逸写直通 + 测量点 promote 语义下的
|
//! mem_object_graph 留存计数回归锚:逃逸写直通下的确定性账目。
|
||||||
//! 确定性账目。
|
|
||||||
//!
|
//!
|
||||||
//! 15 层满二叉树(32767 节点)在「写路径直通(不引克隆)+ 根 promote 深克隆」
|
//! 15 层满二叉树(32767 节点)的确定性计数。对象分配统一入口 Box 化后全部
|
||||||
//! 晋升语义下的确定性计数。对象分配统一入口 Box 化后全部对象入统一表,
|
//! 对象入统一表,run 末统一表含整树节点字面量 + 每次 JS 调用无条件创建的
|
||||||
//! run 末统一表含整树节点字面量 + 每次 JS 调用无条件创建的 arguments 对象 +
|
//! arguments 对象 + 直 session 分配(函数对象与 prototype 子对象)。根处理
|
||||||
//! 直 session 分配(函数对象与 prototype 子对象);promote_rooted 对 session
|
//! 改动后,若以下任一数值变化,说明 session 留存账目已变——须对照
|
||||||
//! 对象全空转(统一表后无 epoch 原件可晋升)。晋升语义或根处理改动后,若以下
|
//! tests/stress/mem_object_graph.js 的基线(benchmark_baseline.json)复核后
|
||||||
//! 任一数值变化,说明 session 留存账目已变——须对照 tests/stress/mem_object_graph.js
|
//! 再更新本锚。
|
||||||
//! 的基线(benchmark_baseline.json)复核后再更新本锚。
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -45,17 +43,14 @@ fn run_module(source: &str) -> Vm {
|
|||||||
|
|
||||||
/// 留存锚:对象分配统一入口 Box 化后 run 末统一表含全部对象——整树节点
|
/// 留存锚:对象分配统一入口 Box 化后 run 末统一表含全部对象——整树节点
|
||||||
/// 字面量 32767 + 每次 JS 调用无条件创建的 arguments 对象 32767 + 直 session
|
/// 字面量 32767 + 每次 JS 调用无条件创建的 arguments 对象 32767 + 直 session
|
||||||
/// 分配 2(makeNode 函数对象与其 prototype 子对象)= 65536。promote_rooted
|
/// 分配 2(makeNode 函数对象与其 prototype 子对象)= 65536;full GC 后逻辑
|
||||||
/// 对 session 对象全空转(统一表后无 epoch 原件可晋升);full GC 后逻辑存活集
|
/// 存活集(整树 + 函数 2)在 session 内恰一份、无双计。
|
||||||
/// (整树 + 函数 2)在 session 内恰一份、无双计。
|
|
||||||
#[test]
|
#[test]
|
||||||
fn object_graph_retention_anchor() {
|
fn object_graph_retention_anchor() {
|
||||||
let mut vm = run_module(OBJECT_GRAPH);
|
let mut vm = run_module(OBJECT_GRAPH);
|
||||||
|
|
||||||
assert_eq!(vm.session_object_count(), 65536, "run 末统一表计数(节点 + arguments + 直分配)");
|
assert_eq!(vm.session_object_count(), 65536, "run 末统一表计数(节点 + arguments + 直分配)");
|
||||||
|
|
||||||
vm.promote_rooted_epoch_objects();
|
|
||||||
vm.promote_session_epoch_refs();
|
|
||||||
vm.collect_session_gc();
|
vm.collect_session_gc();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vm.session_object_count(),
|
vm.session_object_count(),
|
||||||
|
|||||||
Reference in New Issue
Block a user