mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
refactor: realm 与 Vm 生命周期解耦,Vm 持 Arc<Realm>
This commit is contained in:
@@ -67,12 +67,16 @@ pub fn iterator_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
/// 重建内置对象族,换新的 `Iterator` 函数对象;缓存旧指针会指向已释放对象。
|
||||
/// lookup 失败(极端:global 无 `Iterator`)返回 undefined,不 panic。
|
||||
pub fn iterator_constructor_getter<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||
let global = vm.session().global_object();
|
||||
let (shape_id, global_ptr) = {
|
||||
let session = vm.session();
|
||||
let global = session.global_object();
|
||||
(global.shape_id(), global.as_ptr() as *mut JsObject)
|
||||
};
|
||||
let si = vm.kernel_core().perm_interner().intern("Iterator").0;
|
||||
let Some(pos) = vm.kernel_core().shape_forge().lookup_position(global.shape_id(), si) else {
|
||||
let Some(pos) = vm.kernel_core().shape_forge().lookup_position(shape_id, si) else {
|
||||
return NativeResult::Ok(JsValue::undefined());
|
||||
};
|
||||
NativeResult::Ok(global.get_prop_at(pos))
|
||||
NativeResult::Ok(unsafe { &*global_ptr }.get_prop_at(pos))
|
||||
}
|
||||
|
||||
/// `%IteratorPrototype%` 上 `Symbol.toStringTag` 访问器的 getter:返回 `"Iterator"`。
|
||||
@@ -1486,15 +1490,18 @@ fn builtin_iterator_default_intact<H: VmHost>(vm: &mut H, value: JsValue, method
|
||||
if !method.is_object() {
|
||||
return false;
|
||||
}
|
||||
let world = vm.session().builtin_world();
|
||||
let (anchor_ptr, anchor_name) = if is_array_value(value) {
|
||||
(world.array_proto.as_ptr() as *mut JsObject, "values")
|
||||
} else if is_typed_array_value(value) {
|
||||
(world.typed_array_proto.as_ptr() as *mut JsObject, "values")
|
||||
} else if is_map_value(value) {
|
||||
(world.map_proto.as_ptr() as *mut JsObject, "entries")
|
||||
} else {
|
||||
(world.set_proto.as_ptr() as *mut JsObject, "values")
|
||||
let (anchor_ptr, anchor_name) = {
|
||||
let session = vm.session();
|
||||
let world = session.builtin_world();
|
||||
if is_array_value(value) {
|
||||
(world.array_proto.as_ptr() as *mut JsObject, "values")
|
||||
} else if is_typed_array_value(value) {
|
||||
(world.typed_array_proto.as_ptr() as *mut JsObject, "values")
|
||||
} else if is_map_value(value) {
|
||||
(world.map_proto.as_ptr() as *mut JsObject, "entries")
|
||||
} else {
|
||||
(world.set_proto.as_ptr() as *mut JsObject, "values")
|
||||
}
|
||||
};
|
||||
let anchor_obj = unsafe { &*anchor_ptr };
|
||||
let anchor_si = vm.kernel_core().perm_interner().intern(anchor_name).0;
|
||||
|
||||
@@ -455,10 +455,11 @@ pub fn map_size<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
pub fn map_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let _inner = native_try!(get_map_inner(vm, this_val));
|
||||
let proto_ptr = vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject;
|
||||
NativeResult::Ok(crate::iterator::make_collection_iterator(
|
||||
vm,
|
||||
this_val,
|
||||
JsValue::from_js_object(vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject),
|
||||
JsValue::from_js_object(proto_ptr),
|
||||
crate::iterator::MapSetMode::MapEntries,
|
||||
))
|
||||
}
|
||||
@@ -467,10 +468,11 @@ pub fn map_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
pub fn map_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let _inner = native_try!(get_map_inner(vm, this_val));
|
||||
let proto_ptr = vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject;
|
||||
NativeResult::Ok(crate::iterator::make_collection_iterator(
|
||||
vm,
|
||||
this_val,
|
||||
JsValue::from_js_object(vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject),
|
||||
JsValue::from_js_object(proto_ptr),
|
||||
crate::iterator::MapSetMode::MapValues,
|
||||
))
|
||||
}
|
||||
@@ -479,10 +481,11 @@ pub fn map_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
pub fn map_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let _inner = native_try!(get_map_inner(vm, this_val));
|
||||
let proto_ptr = vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject;
|
||||
NativeResult::Ok(crate::iterator::make_collection_iterator(
|
||||
vm,
|
||||
this_val,
|
||||
JsValue::from_js_object(vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject),
|
||||
JsValue::from_js_object(proto_ptr),
|
||||
crate::iterator::MapSetMode::MapKeys,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1568,10 +1568,8 @@ pub fn object_from_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if entries_ptr.is_null() {
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "Object.fromEntries: argument must be iterable"));
|
||||
}
|
||||
let obj = vm.alloc_object(JsObject::new_empty(
|
||||
EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject),
|
||||
));
|
||||
let proto_ptr = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr)));
|
||||
let target_val = JsValue::from_js_object(obj);
|
||||
// entry 数组的元素存元素区(元素区之外才是命名属性区),按元素区长度迭代。
|
||||
let n: usize = unsafe { (*entries_ptr).array_prop_count } as usize;
|
||||
|
||||
@@ -315,10 +315,11 @@ pub fn set_size<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
pub fn set_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let _inner = native_try!(get_set_inner(vm, this_val));
|
||||
let proto_ptr = vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject;
|
||||
NativeResult::Ok(crate::iterator::make_collection_iterator(
|
||||
vm,
|
||||
this_val,
|
||||
JsValue::from_js_object(vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject),
|
||||
JsValue::from_js_object(proto_ptr),
|
||||
crate::iterator::MapSetMode::SetEntries,
|
||||
))
|
||||
}
|
||||
@@ -327,10 +328,11 @@ pub fn set_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
pub fn set_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let _inner = native_try!(get_set_inner(vm, this_val));
|
||||
let proto_ptr = vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject;
|
||||
NativeResult::Ok(crate::iterator::make_collection_iterator(
|
||||
vm,
|
||||
this_val,
|
||||
JsValue::from_js_object(vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject),
|
||||
JsValue::from_js_object(proto_ptr),
|
||||
crate::iterator::MapSetMode::SetValues,
|
||||
))
|
||||
}
|
||||
@@ -340,10 +342,11 @@ pub fn set_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let _inner = native_try!(get_set_inner(vm, this_val));
|
||||
// Set 的 keys() 是 values() 的别名——返回同样的逐元素迭代器。
|
||||
let proto_ptr = vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject;
|
||||
NativeResult::Ok(crate::iterator::make_collection_iterator(
|
||||
vm,
|
||||
this_val,
|
||||
JsValue::from_js_object(vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject),
|
||||
JsValue::from_js_object(proto_ptr),
|
||||
crate::iterator::MapSetMode::SetValues,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -931,10 +931,8 @@ fn match_all_construct_invoke<H: VmHost>(vm: &mut H, this_val: JsValue, pattern_
|
||||
|
||||
// regexp = RegExpCreate(pattern, "g")。
|
||||
let g_str = vm.new_string("g");
|
||||
let rx_val = match vm.construct_ctor(
|
||||
JsValue::from_js_object(vm.session().builtin_world().regexp_constructor.as_ptr() as *mut JsObject),
|
||||
&[pattern_val, g_str],
|
||||
) {
|
||||
let ctor_ptr = vm.session().builtin_world().regexp_constructor.as_ptr() as *mut JsObject;
|
||||
let rx_val = match vm.construct_ctor(JsValue::from_js_object(ctor_ptr), &[pattern_val, g_str]) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return NativeResult::Err(e),
|
||||
};
|
||||
|
||||
@@ -58,19 +58,22 @@ fn to_index<H: VmHost>(vm: &mut H, value: JsValue, msg: &str) -> Result<usize, J
|
||||
}
|
||||
|
||||
fn typed_array_proto_ptr<H: VmHost>(vm: &mut H, kind: TypedArrayKind) -> *mut JsObject {
|
||||
let world = vm.session().builtin_world();
|
||||
match kind {
|
||||
TypedArrayKind::Int8 => world.int8array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::Uint8 => world.uint8array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::Uint8Clamped => world.uint8clampedarray_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::Int16 => world.int16array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::Uint16 => world.uint16array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::Int32 => world.int32array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::Uint32 => world.uint32array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::Float32 => world.float32array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::Float64 => world.float64array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::BigInt64 => world.bigint64array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::BigUint64 => world.biguint64array_proto.as_ptr() as *mut JsObject,
|
||||
{
|
||||
let session = vm.session();
|
||||
let world = session.builtin_world();
|
||||
match kind {
|
||||
TypedArrayKind::Int8 => world.int8array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::Uint8 => world.uint8array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::Uint8Clamped => world.uint8clampedarray_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::Int16 => world.int16array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::Uint16 => world.uint16array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::Int32 => world.int32array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::Uint32 => world.uint32array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::Float32 => world.float32array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::Float64 => world.float64array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::BigInt64 => world.bigint64array_proto.as_ptr() as *mut JsObject,
|
||||
TypedArrayKind::BigUint64 => world.biguint64array_proto.as_ptr() as *mut JsObject,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,19 +103,22 @@ fn ta_gpf_proto<H: VmHost>(vm: &mut H, kind: TypedArrayKind) -> Result<JsValue,
|
||||
}
|
||||
|
||||
fn typed_array_ctor_value<H: VmHost>(vm: &mut H, kind: TypedArrayKind) -> JsValue {
|
||||
let world = vm.session().builtin_world();
|
||||
let ctor = match kind {
|
||||
TypedArrayKind::Int8 => world.int8array_constructor.clone(),
|
||||
TypedArrayKind::Uint8 => world.uint8array_constructor.clone(),
|
||||
TypedArrayKind::Uint8Clamped => world.uint8clampedarray_constructor.clone(),
|
||||
TypedArrayKind::Int16 => world.int16array_constructor.clone(),
|
||||
TypedArrayKind::Uint16 => world.uint16array_constructor.clone(),
|
||||
TypedArrayKind::Int32 => world.int32array_constructor.clone(),
|
||||
TypedArrayKind::Uint32 => world.uint32array_constructor.clone(),
|
||||
TypedArrayKind::Float32 => world.float32array_constructor.clone(),
|
||||
TypedArrayKind::Float64 => world.float64array_constructor.clone(),
|
||||
TypedArrayKind::BigInt64 => world.bigint64array_constructor.clone(),
|
||||
TypedArrayKind::BigUint64 => world.biguint64array_constructor.clone(),
|
||||
let ctor = {
|
||||
let session = vm.session();
|
||||
let world = session.builtin_world();
|
||||
match kind {
|
||||
TypedArrayKind::Int8 => world.int8array_constructor.clone(),
|
||||
TypedArrayKind::Uint8 => world.uint8array_constructor.clone(),
|
||||
TypedArrayKind::Uint8Clamped => world.uint8clampedarray_constructor.clone(),
|
||||
TypedArrayKind::Int16 => world.int16array_constructor.clone(),
|
||||
TypedArrayKind::Uint16 => world.uint16array_constructor.clone(),
|
||||
TypedArrayKind::Int32 => world.int32array_constructor.clone(),
|
||||
TypedArrayKind::Uint32 => world.uint32array_constructor.clone(),
|
||||
TypedArrayKind::Float32 => world.float32array_constructor.clone(),
|
||||
TypedArrayKind::Float64 => world.float64array_constructor.clone(),
|
||||
TypedArrayKind::BigInt64 => world.bigint64array_constructor.clone(),
|
||||
TypedArrayKind::BigUint64 => world.biguint64array_constructor.clone(),
|
||||
}
|
||||
};
|
||||
JsValue::from_js_object(ctor.as_ptr() as *mut JsObject)
|
||||
}
|
||||
|
||||
@@ -107,6 +107,8 @@ pub fn run_js_stress_bench(config: &BenchConfig, kernel: &Arc<KernelCore>, pool:
|
||||
let gc_bytes_freed = gc_stats.last_collection_bytes_freed;
|
||||
let gc_objects_scanned = gc_stats.last_collection_objects_scanned;
|
||||
let gc_collection_us = gc_stats.last_collection_duration_us;
|
||||
// 统计已读尽:释放读守卫,使下方强制 GC 可借 vm。
|
||||
drop(gc_stats);
|
||||
let session_objects = vm.session_object_count().saturating_sub(pre_session);
|
||||
let session_bytes = vm.session_bytes_allocated();
|
||||
let epoch_objects = vm.epoch_object_count().saturating_sub(pre_epoch);
|
||||
|
||||
@@ -803,19 +803,22 @@ pub fn to_object<H: VmHost>(val: JsValue, host: &mut H) -> Result<JsValue, Strin
|
||||
if val.is_null() || val.is_undefined() {
|
||||
return Err(host.error_message_text("TypeError", "Cannot convert null or undefined to object"));
|
||||
}
|
||||
let world = host.session().builtin_world();
|
||||
let (proto_ptr, type_tag) = if val.is_string() {
|
||||
(P::as_ptr(&world.string_proto) as *mut JsObject, JsObject::OBJ_TYPE_STRING_OBJ)
|
||||
} else if val.is_int() || val.is_double() {
|
||||
(P::as_ptr(&world.number_proto) as *mut JsObject, JsObject::OBJ_TYPE_NUMBER_OBJ)
|
||||
} else if val.is_bool() {
|
||||
(P::as_ptr(&world.boolean_proto) as *mut JsObject, JsObject::OBJ_TYPE_BOOLEAN_OBJ)
|
||||
} else if val.is_bigint() {
|
||||
(P::as_ptr(&world.bigint_proto) as *mut JsObject, JsObject::OBJ_TYPE_PLAIN)
|
||||
} else if val.is_symbol() {
|
||||
(P::as_ptr(&world.symbol_proto) as *mut JsObject, JsObject::OBJ_TYPE_SYMBOL_OBJ)
|
||||
} else {
|
||||
(P::as_ptr(&world.object_proto) as *mut JsObject, JsObject::OBJ_TYPE_PLAIN)
|
||||
let (proto_ptr, type_tag) = {
|
||||
let session = host.session();
|
||||
let world = session.builtin_world();
|
||||
if val.is_string() {
|
||||
(P::as_ptr(&world.string_proto) as *mut JsObject, JsObject::OBJ_TYPE_STRING_OBJ)
|
||||
} else if val.is_int() || val.is_double() {
|
||||
(P::as_ptr(&world.number_proto) as *mut JsObject, JsObject::OBJ_TYPE_NUMBER_OBJ)
|
||||
} else if val.is_bool() {
|
||||
(P::as_ptr(&world.boolean_proto) as *mut JsObject, JsObject::OBJ_TYPE_BOOLEAN_OBJ)
|
||||
} else if val.is_bigint() {
|
||||
(P::as_ptr(&world.bigint_proto) as *mut JsObject, JsObject::OBJ_TYPE_PLAIN)
|
||||
} else if val.is_symbol() {
|
||||
(P::as_ptr(&world.symbol_proto) as *mut JsObject, JsObject::OBJ_TYPE_SYMBOL_OBJ)
|
||||
} else {
|
||||
(P::as_ptr(&world.object_proto) as *mut JsObject, JsObject::OBJ_TYPE_PLAIN)
|
||||
}
|
||||
};
|
||||
let proto_val = JsValue::from_js_object(proto_ptr);
|
||||
let obj = host.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
||||
|
||||
@@ -103,7 +103,11 @@ pub trait VmHost {
|
||||
|
||||
// 内核访问器
|
||||
fn kernel_core(&self) -> &Arc<KernelCore>;
|
||||
fn session(&self) -> &KernelSession;
|
||||
/// 只读访问当前 session(builtin world 与 global object)。
|
||||
///
|
||||
/// 返回 `Ref` 守卫(session 入 `RefCell` 后无法再给稳定 `&`):调用方
|
||||
/// 在单表达式内消费,不跨 `borrow_mut` 长存。
|
||||
fn session(&self) -> std::cell::Ref<'_, KernelSession>;
|
||||
|
||||
// 属性解析
|
||||
fn property_key_si(&mut self, val: JsValue) -> u32;
|
||||
|
||||
@@ -200,7 +200,8 @@ fn run_test_inner(
|
||||
/// 读取异步测试捕获的 `$DONE` 输出字符串。
|
||||
pub(crate) fn read_async_output(vm: &Vm) -> String {
|
||||
let si = vm.kernel_core().perm_interner().intern("$__test262_async_result").0;
|
||||
let global = vm.session().global_object();
|
||||
let session = vm.session();
|
||||
let global = session.global_object();
|
||||
let val = vm
|
||||
.kernel_core()
|
||||
.shape_forge()
|
||||
|
||||
@@ -83,7 +83,7 @@ pub(crate) fn dispose_async(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
fn make_dispose_continue_fn(
|
||||
vm: &mut Vm, stack: JsValue, resolve: JsValue, reject: JsValue, reject_role: bool, state_from: Option<&JsObject>,
|
||||
) -> JsValue {
|
||||
let fn_proto = vm.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = vm.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: dispose_async_continue 是 NativeFn 函数项。
|
||||
|
||||
@@ -81,7 +81,7 @@ impl Vm {
|
||||
let return_fn = make_wrapper_fn(self, async_from_sync_return as *const ());
|
||||
let throw_fn = make_wrapper_fn(self, async_from_sync_throw as *const ());
|
||||
let self_fn = make_wrapper_fn(self, async_from_sync_symbol_async_iterator as *const ());
|
||||
let object_proto = self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let object_proto = self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
|
||||
let obj = unsafe { &mut *ptr };
|
||||
let inner_si = self.kernel_core.perm_interner().intern(INNER_PROP).0;
|
||||
@@ -100,7 +100,7 @@ impl Vm {
|
||||
|
||||
/// 构造包装器方法 native 函数对象(name/length 属性由 `add_fn_name_length` 补全)。
|
||||
fn make_wrapper_fn(vm: &mut Vm, native_fn: *const ()) -> JsValue {
|
||||
let fn_proto = vm.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = vm.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: native_fn 来自 NativeFn 函数项。
|
||||
@@ -235,7 +235,7 @@ fn close_sync_iterator(vm: &mut Vm, inner: JsValue) {
|
||||
|
||||
/// 构造 valueWrapper 拒绝时关闭迭代器的闭包:携带目标 reject 与内层同步迭代器。
|
||||
fn make_reject_close_fn(vm: &mut Vm, reject: JsValue, inner: JsValue) -> JsValue {
|
||||
let fn_proto = vm.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = vm.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: async_from_sync_reject_close 是 NativeFn 函数项。
|
||||
@@ -271,7 +271,7 @@ fn async_from_sync_reject_close(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
/// 构造 AsyncFromSyncIteratorContinuation 的 value unwrap 闭包:携带目标 resolve 与
|
||||
/// done 标志,valueWrapper settle 后把解开值包成 `{value, done}` 结算。
|
||||
fn make_unwrap_fn(vm: &mut Vm, resolve: JsValue, done: bool) -> JsValue {
|
||||
let fn_proto = vm.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = vm.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: async_from_sync_unwrap 是 NativeFn 函数项。
|
||||
|
||||
@@ -88,7 +88,7 @@ impl Vm {
|
||||
&mut self, callee: JsValue, this_value: JsValue, args: &[JsValue], promise: JsValue, resolve: JsValue,
|
||||
reject: JsValue,
|
||||
) -> JsValue {
|
||||
let proto_val = JsValue::from_js_object(self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||
let proto_val = JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
||||
let obj = unsafe { &mut *ptr };
|
||||
obj.type_tag = JsObject::OBJ_TYPE_ASYNC;
|
||||
@@ -342,7 +342,7 @@ impl Vm {
|
||||
|
||||
/// 构造 await 恢复闭包:携带目标异步上下文对象,区分 fulfill/reject 角色。
|
||||
fn make_async_await_resume_fn(&mut self, ctx: JsValue, reject_role: bool) -> JsValue {
|
||||
let fn_proto = self.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: async_await_resume_closure 是 NativeFn 函数项。
|
||||
@@ -361,7 +361,7 @@ impl Vm {
|
||||
/// 构造逃出关闭结算闭包:携带目标异步上下文对象与 return() promise,区分
|
||||
/// fulfill/reject 角色。
|
||||
pub(crate) fn make_async_escape_close_fn(&mut self, ctx: JsValue, promise: JsValue, reject_role: bool) -> JsValue {
|
||||
let fn_proto = self.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: async_escape_close_closure 是 NativeFn 函数项。
|
||||
@@ -596,7 +596,7 @@ fn async_escape_close_closure(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
pub(crate) fn init_async_intrinsics(vm: &mut Vm) {
|
||||
let sf = vm.kernel_core.perm_interner().as_ref();
|
||||
let sh = vm.kernel_core.shape_forge().as_ref();
|
||||
let fn_proto_val = vm.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto_val = vm.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
|
||||
// %AsyncFunction% 构造器:proto = Function.prototype,动态编译异步函数。
|
||||
// 选择性重建复用:前轮构造器按键迁移(prototype 槽在下方 P 槽换入后重指
|
||||
@@ -607,13 +607,13 @@ pub(crate) fn init_async_intrinsics(vm: &mut Vm) {
|
||||
let af_ctor_fn_ptr =
|
||||
unsafe { NativeFnPtr::from_raw(oxide_builtins::function::async_function_constructor::<Vm> as *const ()) };
|
||||
let (af_ctor_ptr, af_ctor_is_new) =
|
||||
match vm.realm.session.builtin_world().find_fn_wrapper(af_reuse_key, af_ctor_fn_ptr, 1) {
|
||||
match vm.realm.session.borrow().builtin_world().find_fn_wrapper(af_reuse_key, af_ctor_fn_ptr, 1) {
|
||||
Some(ptr) => (ptr, false),
|
||||
None => {
|
||||
// [[Prototype]] = Function 构造器本体(与 Function 构造器同链,
|
||||
// `Object.getPrototypeOf(AsyncFunction) === Function` 语义)。
|
||||
let fn_ctor_val =
|
||||
JsValue::from_js_object(vm.realm.session.builtin_world().function_constructor.as_ptr() as *mut JsObject);
|
||||
JsValue::from_js_object(vm.realm.session.borrow().builtin_world().function_constructor.as_ptr() as *mut JsObject);
|
||||
let mut af_ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, fn_ctor_val));
|
||||
af_ctor.set_function(true);
|
||||
af_ctor.set_native_arg_count(1);
|
||||
@@ -637,7 +637,7 @@ pub(crate) fn init_async_intrinsics(vm: &mut Vm) {
|
||||
af_ctor.set_shape_id(tag_shape);
|
||||
let af_ctor_ptr = Box::into_raw(af_ctor);
|
||||
// 登记进 world 释放表(带复用键):session 收尾统一释放构造器本体与属性区。
|
||||
vm.realm.session.builtin_world().track_fn_wrapper(af_ctor_ptr, af_reuse_key);
|
||||
vm.realm.session.borrow().builtin_world().track_fn_wrapper(af_ctor_ptr, af_reuse_key);
|
||||
(af_ctor_ptr, true)
|
||||
}
|
||||
};
|
||||
@@ -663,11 +663,11 @@ pub(crate) fn init_async_intrinsics(vm: &mut Vm) {
|
||||
// 使其与动态异步函数使用的 [[Prototype]] 同一对象,保证
|
||||
// `AsyncFunction.prototype === (async () => {}).__proto__`。
|
||||
// 复用构造器 prototype 槽原位改指新 P 原型(旧原型已随 P 换出释放)。
|
||||
Vm::swap_intrinsic_proto(&mut vm.realm.async_function_proto, *af_proto);
|
||||
Vm::swap_intrinsic_proto(&mut vm.realm.async_function_proto.borrow_mut(), *af_proto);
|
||||
// SAFETY: af_ctor_ptr 为 Box 原分配(已登记释放表、生命周期覆盖 session),本 Vm 独占。
|
||||
unsafe {
|
||||
let ctor_mut = &mut *af_ctor_ptr;
|
||||
let proto_val = JsValue::from_js_object(vm.realm.async_function_proto.as_ptr() as *mut JsObject);
|
||||
let proto_val = JsValue::from_js_object(vm.realm.async_function_proto.borrow().as_ptr() as *mut JsObject);
|
||||
let name_val = JsValue::perm_string(sf.string_ptr(sf.intern("AsyncFunction").0));
|
||||
if af_ctor_is_new {
|
||||
let lpos = ctor_mut.push_prop(JsValue::int(1));
|
||||
|
||||
@@ -103,7 +103,7 @@ impl Vm {
|
||||
pub(crate) fn create_async_generator_object(
|
||||
&mut self, callee: JsValue, this_value: JsValue, args: &[JsValue],
|
||||
) -> Result<JsValue, String> {
|
||||
let gen_proto_val = JsValue::from_js_object(self.realm.async_generator_proto.as_ptr() as *mut JsObject);
|
||||
let gen_proto_val = JsValue::from_js_object(self.realm.async_generator_proto.borrow().as_ptr() as *mut JsObject);
|
||||
let obj = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, gen_proto_val));
|
||||
let obj_ref = unsafe { &mut *obj };
|
||||
obj_ref.type_tag = JsObject::OBJ_TYPE_ASYNC_GENERATOR;
|
||||
@@ -681,7 +681,7 @@ impl Vm {
|
||||
|
||||
/// 构造 await 恢复闭包:携带目标异步生成器上下文对象,区分 fulfill/reject 角色。
|
||||
pub(crate) fn make_async_gen_await_resume_fn(&mut self, ctx: JsValue, reject_role: bool) -> JsValue {
|
||||
let fn_proto = self.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: async_gen_await_resume_closure 是 NativeFn 函数项。
|
||||
@@ -702,7 +702,7 @@ impl Vm {
|
||||
fn make_async_gen_yield_unwrap_fn(
|
||||
&mut self, ctx: JsValue, promise: JsValue, raw: bool, reject_role: bool,
|
||||
) -> JsValue {
|
||||
let fn_proto = self.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: async_gen_yield_unwrap_closure 是 NativeFn 函数项。
|
||||
@@ -726,7 +726,7 @@ impl Vm {
|
||||
pub(crate) fn make_delegate_step_closure(
|
||||
&mut self, ctx: JsValue, step: DelegateStep, reject_role: bool,
|
||||
) -> JsValue {
|
||||
let fn_proto = self.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: async_gen_delegate_step_closure 是 NativeFn 函数项。
|
||||
@@ -747,7 +747,7 @@ impl Vm {
|
||||
/// 构造委托 return 请求值 Await 恢复闭包:携带目标异步生成器上下文与
|
||||
/// fulfill/reject 角色,结算后以展开值完成生成器(拒绝时以拒绝原因终止)。
|
||||
fn make_delegate_return_await_closure(&mut self, ctx: JsValue, reject_role: bool) -> JsValue {
|
||||
let fn_proto = self.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: async_gen_delegate_return_await_closure 是 NativeFn 函数项。
|
||||
@@ -770,7 +770,7 @@ impl Vm {
|
||||
pub(crate) fn make_async_gen_escape_close_fn(
|
||||
&mut self, ctx: JsValue, promise: JsValue, reject_role: bool,
|
||||
) -> JsValue {
|
||||
let fn_proto = self.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: async_gen_escape_closure 是 NativeFn 函数项。
|
||||
@@ -1469,7 +1469,7 @@ fn async_dispose_settle_closure(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
impl Vm {
|
||||
/// 构造 `@@asyncDispose` 的 unwrap 闭包(无捕获状态,恒返回 undefined)。
|
||||
fn make_async_dispose_unwrap_fn(&mut self) -> JsValue {
|
||||
let fn_proto = self.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: async_dispose_unwrap_closure 是 NativeFn 函数项。
|
||||
@@ -1483,7 +1483,7 @@ impl Vm {
|
||||
/// 构造 `@@asyncDispose` 结算闭包:捕获能力 promise 与结算角色,
|
||||
/// 反应触发时以反应值结算能力 promise。
|
||||
fn make_async_dispose_settle_fn(&mut self, promise: JsValue, reject_role: bool) -> JsValue {
|
||||
let fn_proto = self.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: async_dispose_settle_closure 是 NativeFn 函数项。
|
||||
@@ -1507,8 +1507,9 @@ impl Vm {
|
||||
pub(crate) fn init_async_generator_intrinsics(vm: &mut Vm) {
|
||||
let sf = vm.kernel_core.perm_interner().as_ref();
|
||||
let sh = vm.kernel_core.shape_forge().as_ref();
|
||||
let fn_proto_val = vm.realm.session.builtin_world().fn_proto_val();
|
||||
let world = vm.realm.session.builtin_world();
|
||||
let fn_proto_val = vm.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let session = vm.realm.session.borrow();
|
||||
let world = session.builtin_world();
|
||||
// %AsyncGeneratorPrototype% 链到 %AsyncIteratorPrototype%(规范原型链),
|
||||
// 方法 next/return/throw 挂其自身。
|
||||
let async_iterator_proto_val = JsValue::from_js_object(world.async_iterator_proto.as_ptr() as *mut JsObject);
|
||||
@@ -1547,7 +1548,7 @@ pub(crate) fn init_async_generator_intrinsics(vm: &mut Vm) {
|
||||
world,
|
||||
ag_label,
|
||||
);
|
||||
Vm::swap_intrinsic_proto(&mut vm.realm.async_generator_proto, *ag_proto);
|
||||
Vm::swap_intrinsic_proto(&mut vm.realm.async_generator_proto.borrow_mut(), *ag_proto);
|
||||
|
||||
// %AsyncGeneratorFunction.prototype%:proto = Function.prototype,
|
||||
// constructor = %AsyncGeneratorFunction%。占位构造器动态创建未实现,
|
||||
@@ -1600,7 +1601,7 @@ pub(crate) fn init_async_generator_intrinsics(vm: &mut Vm) {
|
||||
let proto2_si = sf.intern("prototype").0;
|
||||
let proto2_shape = sh.make_shape(agf_proto.shape_id(), proto2_si);
|
||||
agf_proto.set_shape_id(proto2_shape);
|
||||
let ppos2 = agf_proto.push_prop(JsValue::from_js_object(vm.realm.async_generator_proto.as_ptr() as *mut JsObject));
|
||||
let ppos2 = agf_proto.push_prop(JsValue::from_js_object(vm.realm.async_generator_proto.borrow().as_ptr() as *mut JsObject));
|
||||
agf_proto.set_data_meta(ppos2, PropAttributes::new(false, false, true));
|
||||
// agf_proto[Symbol.toStringTag] = "AsyncGeneratorFunction"(数据属性,w/e/c = false/false/true)。
|
||||
let tag2_key =
|
||||
@@ -1614,11 +1615,11 @@ pub(crate) fn init_async_generator_intrinsics(vm: &mut Vm) {
|
||||
// 构造器 length/name/prototype/@@toStringTag 属性(按形状序),prototype 指向 P 槽实例,
|
||||
// 使其与动态异步生成器函数使用的 [[Prototype]] 同一对象。复用构造器槽位已填充,
|
||||
// prototype 原位改指新 P 原型(旧原型已随 P 换出释放)。
|
||||
Vm::swap_intrinsic_proto(&mut vm.realm.async_generator_function_proto, *agf_proto);
|
||||
Vm::swap_intrinsic_proto(&mut vm.realm.async_generator_function_proto.borrow_mut(), *agf_proto);
|
||||
// SAFETY: agf_ctor_ptr 为 Box 原分配(已登记释放表、生命周期覆盖 session),本 Vm 独占。
|
||||
unsafe {
|
||||
let ctor_mut = &mut *agf_ctor_ptr;
|
||||
let proto_val = JsValue::from_js_object(vm.realm.async_generator_function_proto.as_ptr() as *mut JsObject);
|
||||
let proto_val = JsValue::from_js_object(vm.realm.async_generator_function_proto.borrow().as_ptr() as *mut JsObject);
|
||||
let name_val = JsValue::perm_string(sf.string_ptr(sf.intern("AsyncGeneratorFunction").0));
|
||||
if agf_ctor_is_new {
|
||||
let lpos = ctor_mut.push_prop(JsValue::int(1));
|
||||
@@ -1644,7 +1645,7 @@ pub(crate) fn init_async_generator_intrinsics(vm: &mut Vm) {
|
||||
|
||||
// 绑定 global:AsyncGeneratorFunction 槽已存在则原位更新(full_reset 未重建
|
||||
// global 时旧槽指向已弃 ctor),不存在则开新槽。
|
||||
let global_ptr = vm.realm.session.global_object().as_ptr() as *mut JsObject;
|
||||
let global_ptr = vm.realm.session.borrow().global_object().as_ptr() as *mut JsObject;
|
||||
// SAFETY: global 由 session 持有存活整个 session;本函数内只改其 shape/属性区,
|
||||
// 期间无 reset 或对象搬移。
|
||||
let global = unsafe { &mut *global_ptr };
|
||||
|
||||
@@ -230,7 +230,7 @@ impl Vm {
|
||||
if current_cells[cell_idx].is_null() {
|
||||
// 占位 Cell 保持未初始化(TDZ)直到 MAKE_CELL 置位;
|
||||
// 若绑定为 var 则其初始化 MAKE_CELL 在函数序言先于任何读取执行。
|
||||
current_cells[cell_idx] = self.realm.gc.alloc_cell(JsValue::undefined(), false);
|
||||
current_cells[cell_idx] = self.realm.gc.borrow().alloc_cell(JsValue::undefined(), false);
|
||||
}
|
||||
current_cells[cell_idx]
|
||||
};
|
||||
@@ -275,14 +275,14 @@ impl Vm {
|
||||
}
|
||||
if current[cell_idx].is_null() {
|
||||
// 无占位 cell:新建。
|
||||
current[cell_idx] = self.realm.gc.alloc_cell(value, initialized);
|
||||
current[cell_idx] = self.realm.gc.borrow().alloc_cell(value, initialized);
|
||||
} else {
|
||||
let existing_initialized = unsafe { &*current[cell_idx] }.is_initialized();
|
||||
if !initialized && existing_initialized {
|
||||
// TDZ 占位而槽内是已初始化 cell:新建替换。循环体每迭代重执行
|
||||
// 的 TDZ 占位不得原位翻新迭代 cell(已被本迭代前闭包捕获);
|
||||
// 已初始化 cell 保持身份,闭包 upvalue 保值。
|
||||
current[cell_idx] = self.realm.gc.alloc_cell(value, initialized);
|
||||
current[cell_idx] = self.realm.gc.borrow().alloc_cell(value, initialized);
|
||||
} else {
|
||||
// 更新占位 cell(CREATE_CLOSURE 已建),使闭包 upvalue 指向的
|
||||
// cell 值跟随初始化;初始化标志随指令 b 槽。
|
||||
@@ -309,7 +309,7 @@ impl Vm {
|
||||
while current.len() <= cell_idx {
|
||||
current.push(std::ptr::null_mut());
|
||||
}
|
||||
current[cell_idx] = self.realm.gc.alloc_cell(value, true);
|
||||
current[cell_idx] = self.realm.gc.borrow().alloc_cell(value, true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ impl Vm {
|
||||
}
|
||||
if current[cell_idx].is_null() {
|
||||
let val = self.regs[a];
|
||||
current[cell_idx] = self.realm.gc.alloc_cell(val, true);
|
||||
current[cell_idx] = self.realm.gc.borrow().alloc_cell(val, true);
|
||||
}
|
||||
let c = unsafe { &*current[cell_idx] };
|
||||
if !c.is_initialized() {
|
||||
@@ -478,7 +478,7 @@ impl Vm {
|
||||
} else {
|
||||
self.regs[rd]
|
||||
};
|
||||
upvals[uv_idx] = self.realm.gc.alloc_cell(val, true);
|
||||
upvals[uv_idx] = self.realm.gc.borrow().alloc_cell(val, true);
|
||||
self.regs[rd] = unsafe { (*upvals[uv_idx]).value };
|
||||
return Ok(());
|
||||
}
|
||||
@@ -532,7 +532,7 @@ impl Vm {
|
||||
}
|
||||
vm_debug!("STORE_UPVALUE len={} wrote existing", upvals.len());
|
||||
} else {
|
||||
upvals[uv_idx] = self.realm.gc.alloc_cell(src_val, true);
|
||||
upvals[uv_idx] = self.realm.gc.borrow().alloc_cell(src_val, true);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -544,7 +544,7 @@ impl Vm {
|
||||
vm_trace!("CREATE_REGEXP rd={}", rd);
|
||||
let pat_val = self.regs[a];
|
||||
let flags_val = self.regs[b];
|
||||
let ctor_ptr = self.realm.session.builtin_world().regexp_constructor.as_ptr() as *mut JsObject;
|
||||
let ctor_ptr = self.realm.session.borrow().builtin_world().regexp_constructor.as_ptr() as *mut JsObject;
|
||||
let ctor = unsafe { &*ctor_ptr };
|
||||
let Some(native_fn) = ctor.native_fn() else {
|
||||
self.raise_error_kind("TypeError", "RegExp constructor unavailable")?;
|
||||
@@ -1004,7 +1004,7 @@ impl Vm {
|
||||
|
||||
// bound 包装:解包链后转发到最内层 target([[Construct]] 语义)。
|
||||
if ctor_obj.type_tag == oxide_types::object::JsObject::OBJ_TYPE_BOUND {
|
||||
let proto_ptr = &*self.realm.object_prototype as *const JsObject as *mut JsObject;
|
||||
let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject;
|
||||
let new_obj = self.alloc_object(JsObject::new_empty(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto_ptr),
|
||||
@@ -1012,7 +1012,7 @@ impl Vm {
|
||||
return self.dispatch_new_bound(rd, constructor, new_obj, args, 0);
|
||||
}
|
||||
|
||||
let proto_ptr = &*self.realm.object_prototype as *const JsObject as *mut JsObject;
|
||||
let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject;
|
||||
let new_obj = self.alloc_object(JsObject::new_empty(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto_ptr),
|
||||
|
||||
@@ -39,8 +39,11 @@ impl Vm {
|
||||
return Err(format!("LOAD_GLOBAL constant index {idx} is not a string key"));
|
||||
}
|
||||
let si = self.property_key_si(key_val)?;
|
||||
let global = self.realm.session.global_object();
|
||||
if let Some(val) = self.resolve_property(global, si) {
|
||||
let global = {
|
||||
let session = self.realm.session.borrow();
|
||||
session.global_object().clone()
|
||||
};
|
||||
if let Some(val) = self.resolve_property(&global, si) {
|
||||
self.regs[rd] = val;
|
||||
Ok(false)
|
||||
} else {
|
||||
@@ -67,7 +70,8 @@ impl Vm {
|
||||
return Err(format!("LOAD_GLOBAL_TYPEOF constant index {idx} is not a string key"));
|
||||
}
|
||||
let si = self.property_key_si(key_val)?;
|
||||
let global = self.realm.session.global_object();
|
||||
let session = self.realm.session.borrow();
|
||||
let global = session.global_object();
|
||||
self.regs[rd] = self.resolve_property(global, si).unwrap_or(JsValue::undefined());
|
||||
Ok(())
|
||||
}
|
||||
@@ -215,7 +219,7 @@ impl Vm {
|
||||
pub(crate) fn dispatch_new_object(&mut self, rd: usize, instr: u32) -> Result<(), String> {
|
||||
let nprops = opcode::a(instr) as usize;
|
||||
vm_trace!("NEW_OBJECT rd={} nprops={}", rd, nprops);
|
||||
let proto_ptr = &*self.realm.object_prototype as *const JsObject as *mut JsObject;
|
||||
let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject;
|
||||
let mut shape_id = EMPTY_SHAPE_ID;
|
||||
if nprops > 0 {
|
||||
let key_idxs: Vec<u32> = self.bytecode[self.pc..self.pc + nprops].to_vec();
|
||||
@@ -258,7 +262,7 @@ impl Vm {
|
||||
/// - 登记 session 对象表并计入堆账目;回收由 session GC mark/sweep 承担。
|
||||
pub(crate) fn dispatch_new_session_object(&mut self, rd: usize) -> Result<(), String> {
|
||||
vm_trace!("NEW_SESSION_OBJECT rd={}", rd);
|
||||
let proto_ptr = &*self.realm.object_prototype as *const JsObject as *mut JsObject;
|
||||
let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject;
|
||||
let obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr));
|
||||
let obj_ptr = self.alloc_session_object(obj);
|
||||
self.regs[rd] = JsValue::object(obj_ptr as *mut u8);
|
||||
@@ -278,7 +282,7 @@ impl Vm {
|
||||
Some(frame) => (frame.arguments_base, frame.arguments_count),
|
||||
None => (self.inline_args_base, self.inline_args_count),
|
||||
};
|
||||
let proto_ptr = &*self.realm.object_prototype as *const JsObject as *mut JsObject;
|
||||
let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject;
|
||||
let obj_ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr)));
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
obj.type_tag = JsObject::OBJ_TYPE_ARGUMENTS;
|
||||
@@ -312,7 +316,7 @@ impl Vm {
|
||||
let simple = self.active_module().is_some_and(|m| m.has_simple_params);
|
||||
if strict || !simple {
|
||||
// SAFETY: 指针由绑定层在 session 构造期写入,session 存活期内有效。
|
||||
let thrower_ptr = self.realm.session.builtin_world().throw_type_error.get();
|
||||
let thrower_ptr = self.realm.session.borrow().builtin_world().throw_type_error.get();
|
||||
if thrower_ptr.is_null() {
|
||||
// 绑定层尚未写入共享对象(正常路径不出现):退化为数据属性保调用可用。
|
||||
if let Err(msg) =
|
||||
@@ -367,7 +371,7 @@ impl Vm {
|
||||
let sym_iter_si = make_well_known_symbol_key(0);
|
||||
// SAFETY: array_proto 是 perm 层内置对象(BuiltinWorld 持有),地址稳定且
|
||||
// 跨 epoch/session 存活,本调用期间无 GC 搬移。
|
||||
let array_proto = self.realm.session.builtin_world().array_proto.as_ptr();
|
||||
let array_proto = self.realm.session.borrow().builtin_world().array_proto.as_ptr();
|
||||
let iter_val = match self.ordinary_get(unsafe { &*array_proto }, sym_iter_si, JsValue::undefined()) {
|
||||
Ok(v) => v,
|
||||
Err(msg) => return self.raise_error_kind("TypeError", &msg),
|
||||
@@ -397,7 +401,7 @@ impl Vm {
|
||||
};
|
||||
let n = count as usize;
|
||||
let rest_len = n.saturating_sub(fixed_count);
|
||||
let proto_ptr = self.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let proto_ptr = self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let obj_ptr =
|
||||
self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr), rest_len));
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
@@ -417,7 +421,7 @@ impl Vm {
|
||||
pub(crate) fn dispatch_new_array(&mut self, rd: usize, instr: u32) {
|
||||
let n = opcode::imm16(instr) as usize;
|
||||
vm_trace!("NEW_ARRAY rd={} n={}", rd, n);
|
||||
let proto_ptr = self.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let proto_ptr = self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let obj = self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr), n));
|
||||
self.regs[rd] = JsValue::object(obj as *mut u8);
|
||||
}
|
||||
|
||||
@@ -88,27 +88,27 @@ impl Vm {
|
||||
}
|
||||
return Ok(Some(JsValue::undefined()));
|
||||
}
|
||||
let proto_ptr = self.realm.session.builtin_world().string_proto.as_ptr() as *mut JsObject;
|
||||
let proto_ptr = self.realm.session.borrow().builtin_world().string_proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &*proto_ptr };
|
||||
return self.ordinary_get(proto, prop_name_si, val).map(Some);
|
||||
}
|
||||
if val.is_int() || val.is_double() {
|
||||
let proto_ptr = self.realm.session.builtin_world().number_proto.as_ptr() as *mut JsObject;
|
||||
let proto_ptr = self.realm.session.borrow().builtin_world().number_proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &*proto_ptr };
|
||||
return self.ordinary_get(proto, prop_name_si, val).map(Some);
|
||||
}
|
||||
if val.is_bool() {
|
||||
let proto_ptr = self.realm.session.builtin_world().boolean_proto.as_ptr() as *mut JsObject;
|
||||
let proto_ptr = self.realm.session.borrow().builtin_world().boolean_proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &*proto_ptr };
|
||||
return self.ordinary_get(proto, prop_name_si, val).map(Some);
|
||||
}
|
||||
if val.is_bigint() {
|
||||
let proto_ptr = self.realm.session.builtin_world().bigint_proto.as_ptr() as *mut JsObject;
|
||||
let proto_ptr = self.realm.session.borrow().builtin_world().bigint_proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &*proto_ptr };
|
||||
return self.ordinary_get(proto, prop_name_si, val).map(Some);
|
||||
}
|
||||
if val.is_symbol() {
|
||||
let proto_ptr = self.realm.session.builtin_world().symbol_proto.as_ptr() as *mut JsObject;
|
||||
let proto_ptr = self.realm.session.borrow().builtin_world().symbol_proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &*proto_ptr };
|
||||
return self.ordinary_get(proto, prop_name_si, val).map(Some);
|
||||
}
|
||||
@@ -790,7 +790,7 @@ impl Vm {
|
||||
return Err(format!("DELETE_GLOBAL_PROP_C constant index {idx} is not a string key"));
|
||||
}
|
||||
let si = self.property_key_si(key_val)?;
|
||||
let global_ptr = self.realm.session.global_object().as_ptr() as *mut JsObject;
|
||||
let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject;
|
||||
// SAFETY: 全局对象钉在 session 永久区,指针在 VM 生命周期内有效。
|
||||
let obj = unsafe { &mut *global_ptr };
|
||||
// 统一走共享删除逻辑(与 Reflect.deleteProperty 同源);不可配置返回
|
||||
|
||||
@@ -102,7 +102,7 @@ impl Vm {
|
||||
pub(crate) fn create_generator_object(
|
||||
&mut self, callee: JsValue, this_value: JsValue, args: &[JsValue],
|
||||
) -> Result<JsValue, String> {
|
||||
let gen_proto_val = JsValue::from_js_object(self.realm.generator_proto.as_ptr() as *mut JsObject);
|
||||
let gen_proto_val = JsValue::from_js_object(self.realm.generator_proto.borrow().as_ptr() as *mut JsObject);
|
||||
let obj = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, gen_proto_val));
|
||||
let obj_ref = unsafe { &mut *obj };
|
||||
obj_ref.type_tag = JsObject::OBJ_TYPE_GENERATOR;
|
||||
@@ -797,12 +797,13 @@ fn generator_step_result(vm: &mut Vm, step: Result<GeneratorStep, String>) -> Na
|
||||
pub(crate) fn init_generator_intrinsics(vm: &mut Vm) {
|
||||
let sf = vm.kernel_core.perm_interner().as_ref();
|
||||
let sh = vm.kernel_core.shape_forge().as_ref();
|
||||
let fn_proto_val = vm.realm.session.builtin_world().fn_proto_val();
|
||||
let world = vm.realm.session.builtin_world();
|
||||
let fn_proto_val = vm.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let session = vm.realm.session.borrow();
|
||||
let world = session.builtin_world();
|
||||
// %GeneratorPrototype%:proto = %IteratorPrototype%(生成器是迭代器,继承
|
||||
// @@iterator 与 Iterator helper 方法),方法 next/return/throw。
|
||||
let iterator_proto_val =
|
||||
JsValue::from_js_object(vm.realm.session.builtin_world().iterator_proto.as_ptr() as *mut JsObject);
|
||||
JsValue::from_js_object(vm.realm.session.borrow().builtin_world().iterator_proto.as_ptr() as *mut JsObject);
|
||||
let mut gen_proto = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, iterator_proto_val));
|
||||
// 站点标签:与 AsyncGenerator 原型的同名方法槽(next/return/throw)区分复用键。
|
||||
let gen_label = sf.intern("GeneratorPrototype").0;
|
||||
@@ -836,7 +837,7 @@ pub(crate) fn init_generator_intrinsics(vm: &mut Vm) {
|
||||
world,
|
||||
gen_label,
|
||||
);
|
||||
Vm::swap_intrinsic_proto(&mut vm.realm.generator_proto, *gen_proto);
|
||||
Vm::swap_intrinsic_proto(&mut vm.realm.generator_proto.borrow_mut(), *gen_proto);
|
||||
|
||||
// %GeneratorFunction.prototype%:proto = Function.prototype,constructor = %GeneratorFunction%。
|
||||
let mut gf_proto = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto_val));
|
||||
@@ -888,7 +889,7 @@ pub(crate) fn init_generator_intrinsics(vm: &mut Vm) {
|
||||
let proto2_si = sf.intern("prototype").0;
|
||||
let proto2_shape = sh.make_shape(gf_proto.shape_id(), proto2_si);
|
||||
gf_proto.set_shape_id(proto2_shape);
|
||||
let ppos2 = gf_proto.push_prop(JsValue::from_js_object(vm.realm.generator_proto.as_ptr() as *mut JsObject));
|
||||
let ppos2 = gf_proto.push_prop(JsValue::from_js_object(vm.realm.generator_proto.borrow().as_ptr() as *mut JsObject));
|
||||
gf_proto.set_data_meta(ppos2, oxide_types::object::PropAttributes::new(false, false, true));
|
||||
// gf_proto[Symbol.toStringTag] = "GeneratorFunction"(数据属性,w/e/c = false/false/true)。
|
||||
let tag2_key =
|
||||
@@ -902,11 +903,11 @@ pub(crate) fn init_generator_intrinsics(vm: &mut Vm) {
|
||||
// 构造器 length/name/prototype/@@toStringTag 属性(按形状序),prototype 指向
|
||||
// P 槽实例,使其与动态生成器函数使用的 [[Prototype]] 同一对象。复用构造器槽位
|
||||
// 已填充,prototype 原位改指新 P 原型(旧原型已随 P 换出释放)。
|
||||
Vm::swap_intrinsic_proto(&mut vm.realm.generator_function_proto, *gf_proto);
|
||||
Vm::swap_intrinsic_proto(&mut vm.realm.generator_function_proto.borrow_mut(), *gf_proto);
|
||||
// SAFETY: gf_ctor_ptr 为 Box 原分配(已登记释放表、生命周期覆盖 session),本 Vm 独占。
|
||||
unsafe {
|
||||
let ctor_mut = &mut *gf_ctor_ptr;
|
||||
let proto_val = JsValue::from_js_object(vm.realm.generator_function_proto.as_ptr() as *mut JsObject);
|
||||
let proto_val = JsValue::from_js_object(vm.realm.generator_function_proto.borrow().as_ptr() as *mut JsObject);
|
||||
let name_val = JsValue::perm_string(sf.string_ptr(sf.intern("GeneratorFunction").0));
|
||||
if gf_ctor_is_new {
|
||||
let lpos = ctor_mut.push_prop(JsValue::int(1));
|
||||
@@ -932,7 +933,7 @@ pub(crate) fn init_generator_intrinsics(vm: &mut Vm) {
|
||||
|
||||
// 绑定 global:GeneratorFunction 槽已存在则原位更新(full_reset 未重建 global
|
||||
// 时旧槽指向已弃 ctor),不存在则开新槽。
|
||||
let global_ptr = vm.realm.session.global_object().as_ptr() as *mut JsObject;
|
||||
let global_ptr = vm.realm.session.borrow().global_object().as_ptr() as *mut JsObject;
|
||||
// SAFETY: global 由 session 持有存活整个 session;本函数内只改其 shape/属性区,
|
||||
// 期间无 reset 或对象搬移。
|
||||
let global = unsafe { &mut *global_ptr };
|
||||
|
||||
@@ -28,7 +28,7 @@ impl Vm {
|
||||
/// 创建聚合静态方法(all/race/allSettled/any)的共享记录对象:剩余计数
|
||||
/// 初始 1,结果数组(race 无)与能力 resolve/reject 闭包全部存为自身属性。
|
||||
fn make_agg_record(&mut self, values: JsValue, resolve: JsValue, reject: JsValue) -> JsValue {
|
||||
let proto_val = JsValue::from_js_object(self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||
let proto_val = JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
||||
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;本次 native 调用内不搬移,借出期间无别名。
|
||||
let obj = unsafe { &mut *ptr };
|
||||
@@ -46,7 +46,7 @@ impl Vm {
|
||||
/// 创建 keyed 聚合静态方法的共享记录对象:剩余计数(初始 1)、结果平行数组、
|
||||
/// 键平行数组与能力 resolve/reject 闭包存为自身属性。
|
||||
fn make_agg_record_keyed(&mut self, values: JsValue, keys: JsValue, resolve: JsValue, reject: JsValue) -> JsValue {
|
||||
let proto_val = JsValue::from_js_object(self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||
let proto_val = JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
||||
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;本次 native 调用内不搬移,借出期间无别名。
|
||||
let obj = unsafe { &mut *ptr };
|
||||
@@ -67,7 +67,7 @@ impl Vm {
|
||||
/// 初始 false。内部状态属性非枚举且追加在 length/name 之后,保持内建函数
|
||||
/// "length 先于 name" 的属性序。
|
||||
fn make_agg_element_fn(&mut self, native_fn: NativeFn, record: JsValue, index: i32) -> JsValue {
|
||||
let fn_proto = self.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: native_fn 是 NativeFn 函数项。
|
||||
@@ -99,7 +99,7 @@ impl Vm {
|
||||
|
||||
/// 构造 allSettled 的结算记录 `{status, <value_field>: value}` 普通对象。
|
||||
fn make_settled_record(&mut self, status: &str, value_field: &str, value: JsValue) -> JsValue {
|
||||
let proto_val = JsValue::from_js_object(self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||
let proto_val = JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
||||
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;写 status/value 属性(含 new_string 分配)期间不搬移,借出期间无别名。
|
||||
let obj = unsafe { &mut *ptr };
|
||||
@@ -114,7 +114,7 @@ impl Vm {
|
||||
/// 构造 AggregateError 实例(proto = %AggregateError.prototype%):message 非
|
||||
/// undefined 时 ToString 建自身属性,errors 存为数据属性。
|
||||
fn make_aggregate_error(&mut self, errors: JsValue, message: JsValue) -> JsValue {
|
||||
let proto_val = JsValue::from_js_object(self.realm.aggregate_error_proto.as_ptr() as *mut JsObject);
|
||||
let proto_val = JsValue::from_js_object(self.realm.aggregate_error_proto.borrow().as_ptr() as *mut JsObject);
|
||||
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
||||
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;写 message/errors 数据属性期间不搬移,借出期间无别名。
|
||||
let obj = unsafe { &mut *ptr };
|
||||
@@ -135,7 +135,7 @@ impl Vm {
|
||||
|
||||
/// 把 errors 可迭代值收集为新数组(IterableToList)。不可迭代抛 TypeError。
|
||||
fn aggregate_errors_to_list(&mut self, errors: JsValue) -> Result<JsValue, JsValue> {
|
||||
let array_proto = JsValue::from_js_object(self.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject);
|
||||
let array_proto = JsValue::from_js_object(self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject);
|
||||
let ptr = self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, array_proto, 0));
|
||||
let list = JsValue::from_js_object(ptr);
|
||||
let mut index = 0usize;
|
||||
@@ -151,7 +151,7 @@ impl Vm {
|
||||
|
||||
/// 建普通数组对象并依次写入元素(Promise.try 转发实参的承载)。
|
||||
fn make_plain_array(&mut self, elements: Vec<JsValue>) -> JsValue {
|
||||
let array_proto = JsValue::from_js_object(self.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject);
|
||||
let array_proto = JsValue::from_js_object(self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject);
|
||||
let ptr = self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, array_proto, 0));
|
||||
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;元素写入不搬移对象。
|
||||
let obj = unsafe { &mut *ptr };
|
||||
@@ -165,7 +165,7 @@ impl Vm {
|
||||
/// 自身属性(GC 边走属性区,免 native_data 接线);W 被构造器以
|
||||
/// (resolve, reject) 实参调用。
|
||||
fn make_try_wrapper(&mut self, executor: JsValue, call_args: Vec<JsValue>) -> JsValue {
|
||||
let fn_proto = self.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: promise_try_wrapper 是 NativeFn 函数项。
|
||||
@@ -192,7 +192,7 @@ impl Vm {
|
||||
let sf = self.kernel_core.perm_interner().as_ref();
|
||||
let sh = self.kernel_core.shape_forge().as_ref();
|
||||
let error_proto_val =
|
||||
JsValue::from_js_object(self.realm.session.builtin_world().error_proto.as_ptr() as *mut JsObject);
|
||||
JsValue::from_js_object(self.realm.session.borrow().builtin_world().error_proto.as_ptr() as *mut JsObject);
|
||||
|
||||
// %AggregateError.prototype%:proto = %Error.prototype%,constructor/name/message 数据属性。
|
||||
let mut proto = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val));
|
||||
@@ -214,7 +214,7 @@ impl Vm {
|
||||
|
||||
// %AggregateError% 构造器:proto = %Error%(NativeError 构造器继承 Error 构造器),length 2。
|
||||
let error_ctor_val =
|
||||
JsValue::from_js_object(self.realm.session.builtin_world().error_constructor.as_ptr() as *mut JsObject);
|
||||
JsValue::from_js_object(self.realm.session.borrow().builtin_world().error_constructor.as_ptr() as *mut JsObject);
|
||||
let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_ctor_val));
|
||||
ctor.set_function(true);
|
||||
ctor.type_tag = JsObject::OBJ_TYPE_CONSTRUCTOR;
|
||||
@@ -238,22 +238,22 @@ impl Vm {
|
||||
ctor.set_data_meta(2u32, PropAttributes::new(false, false, false));
|
||||
|
||||
// 固定地址后互相接线:proto.constructor ↔ ctor.prototype。
|
||||
Self::swap_intrinsic_proto(&mut self.realm.aggregate_error_proto, *proto);
|
||||
Self::swap_intrinsic_proto(&mut self.realm.aggregate_error_constructor, *ctor);
|
||||
Self::swap_intrinsic_proto(&mut self.realm.aggregate_error_proto.borrow_mut(), *proto);
|
||||
Self::swap_intrinsic_proto(&mut self.realm.aggregate_error_constructor.borrow_mut(), *ctor);
|
||||
// SAFETY: aggregate_error_proto 为堆址固定的 P<JsObject>(Arc 透明包装),本行前刚经 swap_intrinsic_proto 落地;与 ctor_mut 分属不同对象,无别名。
|
||||
let proto_mut = unsafe { &mut *self.realm.aggregate_error_proto.as_mut_ptr() };
|
||||
let proto_mut = unsafe { &mut *self.realm.aggregate_error_proto.borrow().as_mut_ptr() };
|
||||
proto_mut
|
||||
.set_prop_at(0u32, JsValue::from_js_object(self.realm.aggregate_error_constructor.as_ptr() as *mut JsObject));
|
||||
.set_prop_at(0u32, JsValue::from_js_object(self.realm.aggregate_error_constructor.borrow().as_ptr() as *mut JsObject));
|
||||
// SAFETY: aggregate_error_constructor 同为堆址固定的 P<JsObject>(Arc 透明包装);此处写 prototype 槽位(下标 2),与 proto_mut 分属不同对象,无别名。
|
||||
let ctor_mut = unsafe { &mut *self.realm.aggregate_error_constructor.as_mut_ptr() };
|
||||
ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.realm.aggregate_error_proto.as_ptr() as *mut JsObject));
|
||||
let ctor_mut = unsafe { &mut *self.realm.aggregate_error_constructor.borrow().as_mut_ptr() };
|
||||
ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.realm.aggregate_error_proto.borrow().as_ptr() as *mut JsObject));
|
||||
|
||||
// 绑定 global(槽已存在则更新)。
|
||||
let global_ptr = self.realm.session.global_object().as_ptr() as *mut JsObject;
|
||||
let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject;
|
||||
// SAFETY: global 对象由 session 持有,存活整个 session;本函数内只改其 shape/属性区,期间无 reset 或对象搬移。
|
||||
let global = unsafe { &mut *global_ptr };
|
||||
let si = self.kernel_core.perm_interner().intern("AggregateError").0;
|
||||
let ctor_val = JsValue::from_js_object(self.realm.aggregate_error_constructor.as_ptr() as *mut JsObject);
|
||||
let ctor_val = JsValue::from_js_object(self.realm.aggregate_error_constructor.borrow().as_ptr() as *mut JsObject);
|
||||
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) {
|
||||
global.set_prop_at(pos, ctor_val);
|
||||
} else {
|
||||
@@ -356,7 +356,7 @@ pub(super) fn promise_static_with_resolvers(vm: &mut Vm, args: &[u8]) -> NativeR
|
||||
Ok(t) => t,
|
||||
Err(err) => return NativeResult::Err(err),
|
||||
};
|
||||
let object_proto = JsValue::from_js_object(vm.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||
let object_proto = JsValue::from_js_object(vm.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||
let ptr = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, object_proto));
|
||||
// SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;写 promise/resolve/reject 属性期间不搬移,借出期间无别名。
|
||||
let obj = unsafe { &mut *ptr };
|
||||
@@ -481,7 +481,7 @@ fn perform_promise_combine(
|
||||
let values = if kind == AggregateKind::Race {
|
||||
JsValue::undefined()
|
||||
} else {
|
||||
let array_proto = JsValue::from_js_object(vm.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject);
|
||||
let array_proto = JsValue::from_js_object(vm.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject);
|
||||
let ptr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, array_proto, 0));
|
||||
JsValue::from_js_object(ptr)
|
||||
};
|
||||
@@ -1049,7 +1049,7 @@ fn aggregate_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let this = if this_val.is_object() && new_target.is_object() {
|
||||
this_val.as_js_object_ptr()
|
||||
} else {
|
||||
let proto_val = JsValue::from_js_object(vm.realm.aggregate_error_proto.as_ptr() as *mut JsObject);
|
||||
let proto_val = JsValue::from_js_object(vm.realm.aggregate_error_proto.borrow().as_ptr() as *mut JsObject);
|
||||
vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val))
|
||||
};
|
||||
// SAFETY: this 来自构造路径预分配或 alloc_object 新建,均存活且本段无别名。
|
||||
|
||||
@@ -22,7 +22,7 @@ use super::{is_constructor_value, PromiseState, PromiseStateKind};
|
||||
impl Vm {
|
||||
/// 创建空 Promise 对象(proto = `%Promise.prototype%`),状态盒为 Pending。
|
||||
pub(super) fn create_promise_object(&mut self) -> JsValue {
|
||||
let proto_val = JsValue::from_js_object(self.realm.promise_proto.as_ptr() as *mut JsObject);
|
||||
let proto_val = JsValue::from_js_object(self.realm.promise_proto.borrow().as_ptr() as *mut JsObject);
|
||||
let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
||||
// SAFETY: `alloc_object` 返回非空 arena 指针;此处写 type_tag 与 native_data
|
||||
// (Box<PromiseState> 由 Box::into_raw 分配,随对象释放),无别名。
|
||||
@@ -114,11 +114,11 @@ impl Vm {
|
||||
// 的 receiver 原型链判定通过。
|
||||
let is_data_view = std::ptr::eq(
|
||||
ctor.as_js_object_ptr(),
|
||||
self.realm.session.builtin_world().data_view_constructor.as_ptr() as *mut JsObject,
|
||||
self.realm.session.borrow().builtin_world().data_view_constructor.as_ptr() as *mut JsObject,
|
||||
);
|
||||
let this_val = if is_data_view {
|
||||
let data_view_proto_val =
|
||||
JsValue::from_js_object(self.realm.session.builtin_world().data_view_proto.as_ptr() as *mut JsObject);
|
||||
JsValue::from_js_object(self.realm.session.borrow().builtin_world().data_view_proto.as_ptr() as *mut JsObject);
|
||||
JsValue::from_js_object(self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, data_view_proto_val)))
|
||||
} else {
|
||||
let this_ptr = self.alloc_ctor_this(nt_obj, ctor_obj, new_target)?;
|
||||
@@ -156,7 +156,7 @@ impl Vm {
|
||||
&mut self, nt_obj: &JsObject, ctor_obj: &JsObject, nt_val: JsValue,
|
||||
) -> Result<*mut JsObject, JsValue> {
|
||||
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
|
||||
let object_proto = JsValue::from_js_object(self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||
let object_proto = JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||
let is_native_ctor = ctor_obj.native_fn().is_some() && ctor_obj.type_tag == JsObject::OBJ_TYPE_CONSTRUCTOR;
|
||||
let proto_val = if is_native_ctor {
|
||||
match self.resolve_property(nt_obj, proto_si) {
|
||||
@@ -257,10 +257,11 @@ impl Vm {
|
||||
pub(crate) fn init_promise_intrinsics(&mut self) {
|
||||
let sf = self.kernel_core.perm_interner().as_ref();
|
||||
let sh = self.kernel_core.shape_forge().as_ref();
|
||||
let fn_proto_val = self.realm.session.builtin_world().fn_proto_val();
|
||||
let world = self.realm.session.builtin_world();
|
||||
let fn_proto_val = self.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let session = self.realm.session.borrow();
|
||||
let world = session.builtin_world();
|
||||
let object_proto_val =
|
||||
JsValue::from_js_object(self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||
JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||
|
||||
// %Promise.prototype%:proto = Object.prototype,方法 then/catch/finally + @@toStringTag。
|
||||
let mut proto = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, object_proto_val));
|
||||
@@ -325,25 +326,28 @@ impl Vm {
|
||||
("withResolvers", promise_static_with_resolvers as *const (), 0),
|
||||
("try", promise_static_try as *const (), 1),
|
||||
);
|
||||
// world 的最后一处使用在上:显式释放 session 读守卫,
|
||||
// 使后续 &mut self 的初始化调用可借 self。
|
||||
drop(session);
|
||||
|
||||
// 固定地址后互相接线:proto.constructor ↔ ctor.prototype。
|
||||
Self::swap_intrinsic_proto(&mut self.realm.promise_proto, *proto);
|
||||
Self::swap_intrinsic_proto(&mut self.realm.promise_constructor, *ctor);
|
||||
Self::swap_intrinsic_proto(&mut self.realm.promise_proto.borrow_mut(), *proto);
|
||||
Self::swap_intrinsic_proto(&mut self.realm.promise_constructor.borrow_mut(), *ctor);
|
||||
// SAFETY: `promise_proto` 为 `P<JsObject>`(Arc 透明包装),堆址固定且本行前刚经
|
||||
// swap_intrinsic_proto 落地;与下一处 ctor_mut 指向不同对象,无别名。
|
||||
let proto_mut = unsafe { &mut *self.realm.promise_proto.as_mut_ptr() };
|
||||
proto_mut.set_prop_at(0u32, JsValue::from_js_object(self.realm.promise_constructor.as_ptr() as *mut JsObject));
|
||||
let proto_mut = unsafe { &mut *self.realm.promise_proto.borrow().as_mut_ptr() };
|
||||
proto_mut.set_prop_at(0u32, JsValue::from_js_object(self.realm.promise_constructor.borrow().as_ptr() as *mut JsObject));
|
||||
// SAFETY: `promise_constructor` 同为堆址固定的存活 `P<JsObject>`(Arc 透明包装);
|
||||
// 此处写 prototype 槽位(下标 2),与 proto_mut 分属不同对象,无别名。
|
||||
let ctor_mut = unsafe { &mut *self.realm.promise_constructor.as_mut_ptr() };
|
||||
let ctor_mut = unsafe { &mut *self.realm.promise_constructor.borrow().as_mut_ptr() };
|
||||
// prototype 槽位在 length/name 之后(下标 2)。
|
||||
ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.realm.promise_proto.as_ptr() as *mut JsObject));
|
||||
ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.realm.promise_proto.borrow().as_ptr() as *mut JsObject));
|
||||
|
||||
// Promise[Symbol.species] 访问器:getter 返回 receiver,派生类沿静态原型链
|
||||
// 解析 @@species 得自身构造器。ctor 经本函数每次调用全新构造,同键槽不累积。
|
||||
crate::bindings::bind_accessor_getter_key(
|
||||
&self.kernel_core,
|
||||
&self.realm.session,
|
||||
&self.realm.session.borrow(),
|
||||
ctor_mut,
|
||||
oxide_types::private_key::make_well_known_symbol_key(oxide_types::private_key::WELL_KNOWN_SYMBOL_SPECIES),
|
||||
"get [Symbol.species]",
|
||||
@@ -351,12 +355,12 @@ impl Vm {
|
||||
);
|
||||
|
||||
// 绑定 global:槽已存在则更新(full_reset 未重建 global 时旧槽指向已弃 ctor)。
|
||||
let global_ptr = self.realm.session.global_object().as_ptr() as *mut JsObject;
|
||||
let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject;
|
||||
// SAFETY: global 对象由 session 持有,存活整个 session;本函数内只改其
|
||||
// shape/属性区,期间无 reset 或对象搬移。
|
||||
let global = unsafe { &mut *global_ptr };
|
||||
let si = self.kernel_core.perm_interner().intern("Promise").0;
|
||||
let ctor_val = JsValue::from_js_object(self.realm.promise_constructor.as_ptr() as *mut JsObject);
|
||||
let ctor_val = JsValue::from_js_object(self.realm.promise_constructor.borrow().as_ptr() as *mut JsObject);
|
||||
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) {
|
||||
global.set_prop_at(pos, ctor_val);
|
||||
} else {
|
||||
@@ -392,7 +396,7 @@ fn promise_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "Promise executor is not a function"));
|
||||
}
|
||||
// GetPrototypeFromConstructor:异常原值上抛;非对象结果回落内建原型。
|
||||
let promise_proto_val = JsValue::from_js_object(vm.realm.promise_proto.as_ptr() as *mut JsObject);
|
||||
let promise_proto_val = JsValue::from_js_object(vm.realm.promise_proto.borrow().as_ptr() as *mut JsObject);
|
||||
let new_target = vm.reg(255);
|
||||
let proto = if new_target.is_object() {
|
||||
let nt_ptr = new_target.as_js_object_ptr();
|
||||
|
||||
@@ -145,7 +145,7 @@ impl Vm {
|
||||
}
|
||||
};
|
||||
if ctor.is_undefined() {
|
||||
return Ok(JsValue::from_js_object(self.realm.promise_constructor.as_ptr() as *mut JsObject));
|
||||
return Ok(JsValue::from_js_object(self.realm.promise_constructor.borrow().as_ptr() as *mut JsObject));
|
||||
}
|
||||
if !ctor.is_object() {
|
||||
return Err(oxide_builtins::error::create_type_error(
|
||||
@@ -287,7 +287,7 @@ impl Vm {
|
||||
/// 调 onFinally 后经 PromiseResolve(ctor, 返回值) 解包,再以直通 thunk
|
||||
/// 调 Invoke(p, "then")——fulfill 角色透传原值,reject 角色重抛原拒绝原因。
|
||||
fn make_finally_handler(&mut self, on_finally: JsValue, ctor: JsValue, reject_role: bool) -> JsValue {
|
||||
let fn_proto = self.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: promise_finally_handler 是 NativeFn 函数项。
|
||||
@@ -309,7 +309,7 @@ impl Vm {
|
||||
/// 构造 finally 直通 thunk(CreateBuiltinFunction(closure, 0, "")):
|
||||
/// fulfill 角色返回携带值,reject 角色抛携带的拒绝原因。
|
||||
fn make_finally_thunk(&mut self, reject_role: bool, value: JsValue) -> JsValue {
|
||||
let fn_proto = self.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: finally_thunk 是 NativeFn 函数项。
|
||||
@@ -360,7 +360,7 @@ fn promise_finally_handler(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let c_si = vm.kernel_core.perm_interner().intern(FINALLY_CTOR_PROP).0;
|
||||
let ctor = vm
|
||||
.resolve_property(callee_obj, c_si)
|
||||
.unwrap_or_else(|| JsValue::from_js_object(vm.realm.promise_constructor.as_ptr() as *mut JsObject));
|
||||
.unwrap_or_else(|| JsValue::from_js_object(vm.realm.promise_constructor.borrow().as_ptr() as *mut JsObject));
|
||||
let rj_si = vm.kernel_core.perm_interner().intern(FINALLY_REJECT_PROP).0;
|
||||
let is_reject = vm
|
||||
.resolve_property(callee_obj, rj_si)
|
||||
|
||||
@@ -36,7 +36,7 @@ impl Vm {
|
||||
pub(super) fn new_promise_capability_with_ctor(
|
||||
&mut self, ctor: JsValue,
|
||||
) -> Result<(JsValue, JsValue, JsValue), JsValue> {
|
||||
let intrinsic = JsValue::from_js_object(self.realm.promise_constructor.as_ptr() as *mut JsObject);
|
||||
let intrinsic = JsValue::from_js_object(self.realm.promise_constructor.borrow().as_ptr() as *mut JsObject);
|
||||
if oxide_runtime_api::same_value(ctor, intrinsic) {
|
||||
return Ok(self.new_promise_capability());
|
||||
}
|
||||
@@ -71,7 +71,7 @@ impl Vm {
|
||||
|
||||
/// 构造 GetCapabilitiesExecutor:调用时把 resolve/reject 存入自身 prop。
|
||||
fn make_capability_executor(&mut self) -> JsValue {
|
||||
let fn_proto = self.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: capability_executor 是 NativeFn 函数项。
|
||||
@@ -90,7 +90,7 @@ impl Vm {
|
||||
/// 仍阻止二次结算。
|
||||
pub(super) fn make_resolve_reject_fn(&mut self, promise: JsValue, reject_role: bool, delegated: bool) -> JsValue {
|
||||
let native_fn: NativeFn = if reject_role { promise_reject_closure } else { promise_resolve_closure };
|
||||
let fn_proto = self.realm.session.builtin_world().fn_proto_val();
|
||||
let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val();
|
||||
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
|
||||
func.set_function(true);
|
||||
// SAFETY: native_fn 是 NativeFn 函数项。
|
||||
@@ -314,7 +314,7 @@ impl Vm {
|
||||
return Err(exc);
|
||||
}
|
||||
};
|
||||
let intrinsic = JsValue::from_js_object(self.realm.promise_constructor.as_ptr() as *mut JsObject);
|
||||
let intrinsic = JsValue::from_js_object(self.realm.promise_constructor.borrow().as_ptr() as *mut JsObject);
|
||||
if oxide_runtime_api::same_value(ctor, intrinsic) {
|
||||
return Ok(value);
|
||||
}
|
||||
|
||||
+127
-15
@@ -1,44 +1,156 @@
|
||||
//! 每 VM 的 realm 组合:内核会话(builtin world 与 global 对象)、
|
||||
//! session GC 簿记与 10 个内建原型槽。
|
||||
//!
|
||||
//! `Vm` 以单个 `realm: Realm` 字段持有本组合。`kernel_core`
|
||||
//! 留在 `Vm` 上不进 Realm(`active_vms` 边界守卫计数是 per-VM 语义,
|
||||
//! `KernelCore` 是跨 realm 共享结构,多 realm 共享同一 kernel)。
|
||||
//! `Vm` 以单个 `realm: Arc<Realm>` 字段持有本组合。可变组(session、gc、
|
||||
//! 10 个 P 字段)经 `RefCell` 内部可变性,使重置路径(full_reset / reset /
|
||||
//! 收尾)在 `&Arc<Realm>` 下改写;读路径经 `borrow()` 透传。Arc 计数归零时
|
||||
//! `Drop for Realm` 执行收尾(per-realm 消亡:多 Vm 共享同一 Realm 时最后
|
||||
//! 一个 Vm 析构不触发收尾,计数归零才触发)。
|
||||
//!
|
||||
//! `kernel_core` 留在 `Vm` 上不进 Realm(`active_vms` 边界守卫计数是
|
||||
//! per-VM 语义,`KernelCore` 是跨 realm 共享结构,多 realm 共享同一
|
||||
//! kernel)。`iters` 同样留在 `Vm` 上:活跃 for-in / for-of 迭代器是
|
||||
//! per-VM 执行态,两个共享 Realm 的 Vm 须有独立迭代器栈。
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
use oxide_kernel::kernel::KernelSession;
|
||||
use oxide_types::mem::P;
|
||||
use oxide_types::object::JsObject;
|
||||
|
||||
use crate::session_gc::SessionGc;
|
||||
use crate::vm_state::GcState;
|
||||
|
||||
/// 每 VM 的 realm 组合:内核会话(builtin world 与 global 对象)、
|
||||
/// session GC 簿记与 10 个内建原型槽。
|
||||
///
|
||||
/// 可变组经 `RefCell` 内部可变性:重置路径(selective_reset / rebind /
|
||||
/// record_snapshot / 整体替换、水位复位 / 表排空 / 账目清零、
|
||||
/// object_prototype 重赋)在 `&Arc<Realm>` 下改写,读路径经 `borrow()`
|
||||
/// 透传。
|
||||
///
|
||||
/// 字段顺序按原 `Vm` 声明顺序(session 先、10 个 P 字段次之、gc 最后),
|
||||
/// 保 Drop 相对顺序。
|
||||
pub(crate) struct Realm {
|
||||
pub(crate) session: KernelSession,
|
||||
pub(crate) session: RefCell<KernelSession>,
|
||||
/// `%Object.prototype%`:session 的 Object 原型(global 的 `[[Prototype]]` 挂它)。
|
||||
pub(crate) object_prototype: P<JsObject>,
|
||||
pub(crate) object_prototype: RefCell<P<JsObject>>,
|
||||
/// `%GeneratorPrototype%`:生成器实例的原型(next/return/throw 方法挂此)。
|
||||
pub(crate) generator_proto: P<JsObject>,
|
||||
pub(crate) generator_proto: RefCell<P<JsObject>>,
|
||||
/// `%GeneratorFunction.prototype%`:生成器函数对象的原型(`constructor` 指向
|
||||
/// `%GeneratorFunction%`,使 `g.constructor.name` 解析为 "GeneratorFunction")。
|
||||
pub(crate) generator_function_proto: P<JsObject>,
|
||||
pub(crate) generator_function_proto: RefCell<P<JsObject>>,
|
||||
/// `%Promise%` 构造器(resolve/reject 静态方法挂此,global 的 Promise 槽指向它)。
|
||||
pub(crate) promise_constructor: P<JsObject>,
|
||||
pub(crate) promise_constructor: RefCell<P<JsObject>>,
|
||||
/// `%Promise.prototype%`:Promise 实例的原型(then/catch/finally 方法挂此)。
|
||||
pub(crate) promise_proto: P<JsObject>,
|
||||
pub(crate) promise_proto: RefCell<P<JsObject>>,
|
||||
/// `%AggregateError%` 构造器(Promise.any 拒绝时构造 AggregateError 用)。
|
||||
pub(crate) aggregate_error_constructor: P<JsObject>,
|
||||
pub(crate) aggregate_error_constructor: RefCell<P<JsObject>>,
|
||||
/// `%AggregateError.prototype%`(proto = %Error.prototype%)。
|
||||
pub(crate) aggregate_error_proto: P<JsObject>,
|
||||
pub(crate) aggregate_error_proto: RefCell<P<JsObject>>,
|
||||
/// `%AsyncFunction.prototype%`:异步函数对象的原型(`constructor` 指向 `%AsyncFunction%`)。
|
||||
pub(crate) async_function_proto: P<JsObject>,
|
||||
pub(crate) async_function_proto: RefCell<P<JsObject>>,
|
||||
/// `%AsyncGeneratorPrototype%`:异步生成器实例的原型(next/return/throw/@@asyncIterator)。
|
||||
pub(crate) async_generator_proto: P<JsObject>,
|
||||
pub(crate) async_generator_proto: RefCell<P<JsObject>>,
|
||||
/// `%AsyncGeneratorFunction.prototype%`:异步生成器函数对象的原型。
|
||||
pub(crate) async_generator_function_proto: P<JsObject>,
|
||||
pub(crate) async_generator_function_proto: RefCell<P<JsObject>>,
|
||||
/// session 堆与 GC 簿记(对象/字符串/BigInt/cell 四表 + 水位与账目)。
|
||||
pub(crate) gc: GcState,
|
||||
pub(crate) gc: RefCell<GcState>,
|
||||
}
|
||||
|
||||
impl Drop for Realm {
|
||||
fn drop(&mut self) {
|
||||
// 直接 drop(test262 每测试新建即弃)不经 reset / full_reset 路径:
|
||||
// 统一收尾释放全部 session 堆数据与内建原型属性区,防逐测试累积泄漏。
|
||||
// per-realm 消亡:Arc 计数归零时恰好执行一次,多 Vm 共享同一 Realm
|
||||
// 时最后一个 Vm 析构不触发收尾(计数未归零),归零才触发。
|
||||
self.teardown_intrinsic_protos();
|
||||
self.teardown_session_heap_data();
|
||||
// session 字段随后按声明顺序 drop → KernelSession::drop →
|
||||
// teardown_builtins(幂等),与上两组释放集合不相交(oxide_kernel
|
||||
// 不变量:登记表与 retire_replaced 对象集不相交、恰好一次)。
|
||||
}
|
||||
}
|
||||
|
||||
impl Realm {
|
||||
/// 释放本 realm 内建原型 P 对象(生成器/Promise/异步族与 Object 原型)的
|
||||
/// 堆外属性区。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// 对象本体随 Arc 引用归零释放(字段 drop);属性区须在此显式释放一次
|
||||
/// (`JsObject` 无 Drop 口径)。仅释放本 realm 独占(强引用计数为 1)的
|
||||
/// 副本:`object_prototype` 与 session world 共享 Arc,session 存活期
|
||||
/// 下一测试仍经 world 引用同一对象,其属性区归 session 收尾
|
||||
/// (`teardown_builtins`)释放。
|
||||
pub(crate) fn teardown_intrinsic_protos(&self) {
|
||||
for p in [
|
||||
&self.object_prototype,
|
||||
&self.generator_proto,
|
||||
&self.generator_function_proto,
|
||||
&self.promise_constructor,
|
||||
&self.promise_proto,
|
||||
&self.aggregate_error_constructor,
|
||||
&self.aggregate_error_proto,
|
||||
&self.async_function_proto,
|
||||
&self.async_generator_proto,
|
||||
&self.async_generator_function_proto,
|
||||
] {
|
||||
let p = p.borrow();
|
||||
if p.strong_count() != 1 {
|
||||
continue;
|
||||
}
|
||||
// SAFETY: 独占副本的属性区仅此一处释放并置空(幂等)。
|
||||
unsafe {
|
||||
(&mut *p.as_mut_ptr()).release_raw_heap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 释放全部 session 堆数据:session 对象(本体 + 堆数据 + upvalue 列表)
|
||||
/// + session 串 + BigInt box + upvalue cell box。
|
||||
///
|
||||
/// 供 `full_reset` 与 `Drop` 共用——统一入口路径上对象本体全部为堆载体
|
||||
/// (`Box::from_raw` 恰好释放一次),堆数据与 upvalue 列表各恰好释放一次。
|
||||
/// 独占所有权免去重:死对象已出表不重复枚举。
|
||||
pub(crate) fn teardown_session_heap_data(&self) {
|
||||
// 对象逐条独占释放:本体 + 堆数据 + upvalue 列表,顺序由
|
||||
// drop_dead_session_object 收口。
|
||||
for ptr in self.gc.borrow_mut().session_object_ptrs.drain(..) {
|
||||
if ptr.is_null() {
|
||||
continue;
|
||||
}
|
||||
// SAFETY: ptr 来自 session 对象表登记,收尾时仍指向合法对象;
|
||||
// 表独占持有,无其他释放点。
|
||||
SessionGc::drop_dead_session_object(ptr);
|
||||
}
|
||||
self.free_session_string_heap_data();
|
||||
self.free_session_bigint_heap_data();
|
||||
self.gc.borrow_mut().free_cells();
|
||||
}
|
||||
|
||||
/// 释放全部 session 堆 `JsString`。仅在完全隔离重置
|
||||
/// (`full_reset` / `clear_full_reset_state`)时调用,此时没有存活的
|
||||
/// session 对象会引用它们。较轻量的 `reset()` 刻意保留它们,与 session
|
||||
/// 串跨 eval 存活一致。
|
||||
fn free_session_string_heap_data(&self) {
|
||||
for ptr in self.gc.borrow_mut().session_string_ptrs.drain(..) {
|
||||
// SAFETY: 每个指针来自 new_string/new_cons_string 的 Box::into_raw,
|
||||
// 且只在这里(或 sweep)恰好释放一次;内部连带释放 rope 扁平化产物。
|
||||
unsafe {
|
||||
SessionGc::drop_session_string_box(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 释放全部 session 堆 BigInt box。仅在完全隔离重置(`full_reset`)时
|
||||
/// 调用,此时没有存活的 session 对象/寄存器会引用它们。
|
||||
fn free_session_bigint_heap_data(&self) {
|
||||
for ptr in self.gc.borrow_mut().session_bigint_ptrs.borrow_mut().drain(..) {
|
||||
// SAFETY: 每个指针来自 new_bigint 的 Box::into_raw(Box::new(BigInt)),
|
||||
// 且只在这里恰好释放一次。
|
||||
unsafe {
|
||||
drop(Box::from_raw(ptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ impl SessionGc {
|
||||
/// 清空 session 对象表的 GC mark 位。置位不随表清位消失,残留位会让下一次
|
||||
/// `mark` 的 DFS 在已标对象处短路,漏扫其新增引用边。
|
||||
pub(crate) fn clear_all_marks(&mut self, vm: &mut Vm) {
|
||||
vm_debug!("[GC] clear_all_marks: {} session objects", vm.realm.gc.session_object_ptrs.len());
|
||||
vm_debug!("[GC] clear_all_marks: {} session objects", vm.realm.gc.borrow().session_object_ptrs.len());
|
||||
self.clear_session_marks(vm);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ impl SessionGc {
|
||||
/// 与收集前历史残留——残留 true 会让下一次 `mark` 的 DFS 在已标对象处
|
||||
/// 短路,漏扫其新增引用边。
|
||||
pub(crate) fn clear_session_marks(&mut self, vm: &mut Vm) {
|
||||
for &ptr in &vm.realm.gc.session_object_ptrs {
|
||||
for &ptr in &vm.realm.gc.borrow().session_object_ptrs {
|
||||
if ptr.is_null() {
|
||||
continue;
|
||||
}
|
||||
@@ -650,7 +650,7 @@ impl SessionGc {
|
||||
/// `sweep`(完整收集)与 `collect_in_run`(执行期收集)共用,保证两条
|
||||
/// 收集路径的弱键定夺口径一致。返回 (存活数, 死数, 释放字节)。
|
||||
fn sweep_in_place(&mut self, vm: &mut Vm) -> (u64, u64, u64) {
|
||||
let old_ptrs = std::mem::take(&mut vm.realm.gc.session_object_ptrs);
|
||||
let old_ptrs = std::mem::take(&mut vm.realm.gc.borrow_mut().session_object_ptrs);
|
||||
let mut survivors = Vec::with_capacity(old_ptrs.len());
|
||||
|
||||
// 弱键定夺先于死对象释放:键对象(含死键)此刻全部仍分配,
|
||||
@@ -689,12 +689,15 @@ impl SessionGc {
|
||||
}
|
||||
}
|
||||
let live = survivors.len() as u64;
|
||||
vm.realm.gc.session_object_ptrs = survivors;
|
||||
vm.realm.gc.borrow_mut().session_object_ptrs = survivors;
|
||||
|
||||
// 对象口径重算:存活对象头 + 堆数据求和(串/BigInt/cell 分量不在此口径,
|
||||
// 由各清扫路径按存活补回,与完整收集的最终账目一致)。
|
||||
vm.realm.gc.session_bytes_allocated = vm
|
||||
.realm.gc
|
||||
// 由各清扫路径按存活补回,与完整收集的最终账目一致)。读入局部变量后
|
||||
// 释放借用再写回,不跨读写持借。
|
||||
let new_bytes: usize = vm
|
||||
.realm
|
||||
.gc
|
||||
.borrow()
|
||||
.session_object_ptrs
|
||||
.iter()
|
||||
.filter(|&&ptr| !ptr.is_null())
|
||||
@@ -704,6 +707,7 @@ impl SessionGc {
|
||||
size_of::<JsObject>() as u64 + Self::object_heap_data_bytes(obj)
|
||||
})
|
||||
.sum::<u64>() as usize;
|
||||
vm.realm.gc.borrow_mut().session_bytes_allocated = new_bytes;
|
||||
|
||||
// 统计按对象口径累计(串/BigInt/cell 清扫在其上累加释放字节)。
|
||||
self.total_bytes_freed = self.total_bytes_freed.saturating_add(freed_bytes);
|
||||
@@ -737,8 +741,8 @@ impl SessionGc {
|
||||
/// `session_bytes_allocated` 重置为仅对象),再补回存活字符串字节。
|
||||
/// 返回释放的字节数。
|
||||
pub(crate) fn sweep_session_strings(&mut self, vm: &mut Vm) -> u64 {
|
||||
vm_debug!("[GC] sweep strings: {} string ptrs", vm.realm.gc.session_string_ptrs.len());
|
||||
let old = std::mem::take(&mut vm.realm.gc.session_string_ptrs);
|
||||
vm_debug!("[GC] sweep strings: {} string ptrs", vm.realm.gc.borrow().session_string_ptrs.len());
|
||||
let old = std::mem::take(&mut vm.realm.gc.borrow_mut().session_string_ptrs);
|
||||
let mut freed = 0u64;
|
||||
let mut live_bytes = 0usize;
|
||||
let mut live = Vec::with_capacity(old.len());
|
||||
@@ -756,8 +760,9 @@ impl SessionGc {
|
||||
freed += unsafe { Self::drop_dead_session_string(ptr) };
|
||||
}
|
||||
}
|
||||
vm.realm.gc.session_string_ptrs = live;
|
||||
vm.realm.gc.session_bytes_allocated = vm.realm.gc.session_bytes_allocated.saturating_add(live_bytes);
|
||||
vm.realm.gc.borrow_mut().session_string_ptrs = live;
|
||||
let new_bytes = vm.realm.gc.borrow().session_bytes_allocated.saturating_add(live_bytes);
|
||||
vm.realm.gc.borrow_mut().session_bytes_allocated = new_bytes;
|
||||
|
||||
self.total_bytes_freed = self.total_bytes_freed.saturating_add(freed);
|
||||
self.last_collection_bytes_freed = self.last_collection_bytes_freed.saturating_add(freed);
|
||||
@@ -769,7 +774,7 @@ impl SessionGc {
|
||||
/// forwarding 表与根指针重写。在字符串清扫之后运行。
|
||||
/// 返回释放的字节数。
|
||||
pub(crate) fn sweep_session_bigints(&mut self, vm: &mut Vm) -> u64 {
|
||||
let old = vm.realm.gc.session_bigint_ptrs.borrow_mut().drain(..).collect::<Vec<_>>();
|
||||
let old = vm.realm.gc.borrow_mut().session_bigint_ptrs.borrow_mut().drain(..).collect::<Vec<_>>();
|
||||
let mut freed = 0u64;
|
||||
let mut live = Vec::with_capacity(old.len());
|
||||
for ptr in old {
|
||||
@@ -788,7 +793,7 @@ impl SessionGc {
|
||||
}
|
||||
}
|
||||
}
|
||||
*vm.realm.gc.session_bigint_ptrs.borrow_mut() = live;
|
||||
*vm.realm.gc.borrow_mut().session_bigint_ptrs.borrow_mut() = live;
|
||||
|
||||
self.total_bytes_freed = self.total_bytes_freed.saturating_add(freed);
|
||||
self.last_collection_bytes_freed = self.last_collection_bytes_freed.saturating_add(freed);
|
||||
@@ -804,7 +809,7 @@ impl SessionGc {
|
||||
/// 统计、不重算存活账目。
|
||||
/// 返回释放的字节数。
|
||||
pub(crate) fn sweep_session_cells(&mut self, vm: &mut Vm) -> u64 {
|
||||
let old = vm.realm.gc.session_cell_ptrs.borrow_mut().drain(..).collect::<Vec<_>>();
|
||||
let old = vm.realm.gc.borrow_mut().session_cell_ptrs.borrow_mut().drain(..).collect::<Vec<_>>();
|
||||
let mut freed = 0u64;
|
||||
let mut live = Vec::with_capacity(old.len());
|
||||
for ptr in old {
|
||||
@@ -822,7 +827,7 @@ impl SessionGc {
|
||||
}
|
||||
}
|
||||
}
|
||||
*vm.realm.gc.session_cell_ptrs.borrow_mut() = live;
|
||||
*vm.realm.gc.borrow_mut().session_cell_ptrs.borrow_mut() = live;
|
||||
|
||||
self.total_bytes_freed = self.total_bytes_freed.saturating_add(freed);
|
||||
self.last_collection_bytes_freed = self.last_collection_bytes_freed.saturating_add(freed);
|
||||
@@ -831,10 +836,11 @@ impl SessionGc {
|
||||
|
||||
/// 完整收集的触发判断:对象、字符串或 BigInt 表非空,且字节账目已达阈值。
|
||||
pub(crate) fn should_collect(&self, vm: &Vm) -> bool {
|
||||
(!vm.realm.gc.session_object_ptrs.is_empty()
|
||||
|| !vm.realm.gc.session_string_ptrs.is_empty()
|
||||
|| !vm.realm.gc.session_bigint_ptrs.borrow().is_empty())
|
||||
&& vm.realm.gc.session_bytes_allocated >= vm.kernel_core().config().session_gc_threshold
|
||||
let gc = vm.realm.gc.borrow();
|
||||
(!gc.session_object_ptrs.is_empty()
|
||||
|| !gc.session_string_ptrs.is_empty()
|
||||
|| !gc.session_bigint_ptrs.borrow().is_empty())
|
||||
&& gc.session_bytes_allocated >= vm.kernel_core().config().session_gc_threshold
|
||||
}
|
||||
|
||||
/// 执行期字符串回收的触发判断:账目须超过水位(上次收集后的存活字节 +
|
||||
@@ -842,10 +848,11 @@ impl SessionGc {
|
||||
/// 保证死对象超阈值即被 reset 回收;执行期走增量水位,活串超阈值时不每指令
|
||||
/// 重复触发无死串可回收的白跑。
|
||||
pub(crate) fn should_collect_strings(&self, vm: &Vm) -> bool {
|
||||
(!vm.realm.gc.session_object_ptrs.is_empty()
|
||||
|| !vm.realm.gc.session_string_ptrs.is_empty()
|
||||
|| !vm.realm.gc.session_bigint_ptrs.borrow().is_empty())
|
||||
&& vm.realm.gc.session_bytes_allocated >= vm.realm.gc.string_gc_watermark
|
||||
let gc = vm.realm.gc.borrow();
|
||||
(!gc.session_object_ptrs.is_empty()
|
||||
|| !gc.session_string_ptrs.is_empty()
|
||||
|| !gc.session_bigint_ptrs.borrow().is_empty())
|
||||
&& gc.session_bytes_allocated >= gc.string_gc_watermark
|
||||
}
|
||||
|
||||
/// debug 兜底:mark 之后断言所有存活对象持有的字符串边都已登记进
|
||||
@@ -853,7 +860,7 @@ impl SessionGc {
|
||||
#[cfg(debug_assertions)]
|
||||
fn debug_assert_marked_object_strings_live(&self, vm: &Vm) {
|
||||
let mut live = HashSet::with_hasher(FxBuildHasher);
|
||||
for &ptr in &vm.realm.gc.session_object_ptrs {
|
||||
for &ptr in &vm.realm.gc.borrow().session_object_ptrs {
|
||||
if ptr.is_null() {
|
||||
continue;
|
||||
}
|
||||
@@ -942,7 +949,9 @@ impl SessionGc {
|
||||
|
||||
// 扣减字符串账目(保留对象账目),再补回存活串字节。
|
||||
let object_bytes: usize = vm
|
||||
.realm.gc
|
||||
.realm
|
||||
.gc
|
||||
.borrow()
|
||||
.session_object_ptrs
|
||||
.iter()
|
||||
.filter(|&&ptr| !ptr.is_null())
|
||||
@@ -951,7 +960,7 @@ impl SessionGc {
|
||||
size_of::<JsObject>() as u64 + Self::object_heap_data_bytes(obj)
|
||||
})
|
||||
.sum::<u64>() as usize;
|
||||
vm.realm.gc.session_bytes_allocated = object_bytes;
|
||||
vm.realm.gc.borrow_mut().session_bytes_allocated = object_bytes;
|
||||
let mut freed_bytes = self.sweep_session_strings(vm);
|
||||
freed_bytes += self.sweep_session_bigints(vm);
|
||||
freed_bytes += self.sweep_session_cells(vm);
|
||||
@@ -962,9 +971,10 @@ impl SessionGc {
|
||||
self.clear_all_marks(vm);
|
||||
|
||||
// 抬高下次触发水位:本次存活字节 + 阈值增量,避免活串超阈值时每指令重复
|
||||
// 触发无死串可回收的完整 mark + 串清扫。
|
||||
// 触发无死串可回收的完整 mark + 串清扫。读入局部变量后释放借用再写回。
|
||||
let threshold = vm.kernel_core().config().session_gc_threshold;
|
||||
vm.realm.gc.string_gc_watermark = vm.realm.gc.session_bytes_allocated.saturating_add(threshold);
|
||||
let new_watermark = vm.realm.gc.borrow().session_bytes_allocated.saturating_add(threshold);
|
||||
vm.realm.gc.borrow_mut().string_gc_watermark = new_watermark;
|
||||
|
||||
let elapsed = start.elapsed();
|
||||
self.total_collections += 1;
|
||||
@@ -1034,18 +1044,21 @@ impl SessionGc {
|
||||
// 串,串表整体计入,与 strings-only 口径一致;BigInt/cell 不在手工
|
||||
// 账目内,由 `run_alloc_bytes` 公式按表长单列)。
|
||||
let mut string_bytes: usize = 0;
|
||||
for &ptr in &vm.realm.gc.session_string_ptrs {
|
||||
for &ptr in &vm.realm.gc.borrow().session_string_ptrs {
|
||||
if ptr.is_null() {
|
||||
continue;
|
||||
}
|
||||
// SAFETY: ptr 在字符串表登记,收尾前有效。
|
||||
string_bytes += size_of::<JsString>() + unsafe { (*ptr).payload_bytes() };
|
||||
}
|
||||
vm.realm.gc.session_bytes_allocated = vm.realm.gc.session_bytes_allocated.saturating_add(string_bytes);
|
||||
let new_bytes = vm.realm.gc.borrow().session_bytes_allocated.saturating_add(string_bytes);
|
||||
vm.realm.gc.borrow_mut().session_bytes_allocated = new_bytes;
|
||||
|
||||
// 抬高下次触发水位:当前分配包络 + 阈值增量——存活包络超阈值时不每指令
|
||||
// 重复触发无死对象可回收的白跑。
|
||||
vm.realm.gc.gc_watermark = vm.run_alloc_bytes().saturating_add(vm.realm.gc.gc_threshold_cached);
|
||||
// 重复触发无死对象可回收的白跑。读入局部变量后释放借用再写回。
|
||||
let alloc = vm.run_alloc_bytes();
|
||||
let threshold = vm.realm.gc.borrow().gc_threshold_cached;
|
||||
vm.realm.gc.borrow_mut().gc_watermark = alloc.saturating_add(threshold);
|
||||
|
||||
let elapsed = start.elapsed();
|
||||
self.total_collections += 1;
|
||||
|
||||
@@ -14,7 +14,7 @@ use oxide_emit::module::{ModuleKind, ModuleSourceLoader, ResolvedModule};
|
||||
use oxide_runtime_api::NativeResult;
|
||||
|
||||
fn plain_object(vm: &mut Vm) -> *mut JsObject {
|
||||
let proto_ptr = vm.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let proto_ptr = vm.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
vm.alloc_object(JsObject::new_empty(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto_ptr),
|
||||
@@ -127,7 +127,7 @@ fn gc_roots_contains_registers_frames_and_root_roots() {
|
||||
assert!(has_ptr(&roots, frame_session));
|
||||
assert!(has_ptr(&roots, this_session));
|
||||
assert!(has_ptr(&roots, child_session));
|
||||
assert!(has_ptr(&roots, vm.realm.session.global_object().as_ptr() as *mut JsObject));
|
||||
assert!(has_ptr(&roots, vm.realm.session.borrow().global_object().as_ptr() as *mut JsObject));
|
||||
assert!(!roots.is_empty());
|
||||
assert!(roots.contains(&JsValue::from_js_object(root_session)));
|
||||
assert_eq!(vm.exception_value, Some(JsValue::from_js_object(root_session)));
|
||||
@@ -149,9 +149,9 @@ fn mark_phase_reaches_cycles_and_unreachable_are_unmarked() {
|
||||
let unreachable_session = unreachable;
|
||||
|
||||
vm.regs[0] = JsValue::from_js_object(root_session);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.session_gc);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc);
|
||||
gc.mark(&vm);
|
||||
vm.realm.gc.session_gc = gc;
|
||||
vm.realm.gc.borrow_mut().session_gc = gc;
|
||||
|
||||
assert!(unsafe { (*root_session).is_gc_marked() });
|
||||
assert!(unsafe { (*reachable_session).is_gc_marked() });
|
||||
@@ -173,17 +173,19 @@ fn sweep_preserves_cycle_and_collects_unreachable() {
|
||||
let root_session = root;
|
||||
vm.regs[0] = JsValue::from_js_object(root_session);
|
||||
let dead_session = dead;
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.session_gc);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc);
|
||||
gc.mark(&vm);
|
||||
let _ = gc.sweep(&mut vm);
|
||||
vm.realm.gc.session_gc = gc;
|
||||
vm.realm.gc.borrow_mut().session_gc = gc;
|
||||
|
||||
assert_eq!(vm.realm.gc.session_object_ptrs.len(), 2);
|
||||
assert_eq!(vm.realm.gc.borrow().session_object_ptrs.len(), 2);
|
||||
// 原地 sweep 不搬移存活对象:根与子保持原址(克隆体即原对象),死对象出表。
|
||||
assert!(vm.realm.gc.session_object_ptrs.contains(&root_session));
|
||||
assert!(!vm.realm.gc.session_object_ptrs.contains(&dead_session));
|
||||
assert!(vm.realm.gc.borrow().session_object_ptrs.contains(&root_session));
|
||||
assert!(!vm.realm.gc.borrow().session_object_ptrs.contains(&dead_session));
|
||||
assert!(!vm
|
||||
.realm.gc
|
||||
.realm
|
||||
.gc
|
||||
.borrow()
|
||||
.session_object_ptrs
|
||||
.iter()
|
||||
.any(|ptr| unsafe { (*(*ptr)).is_gc_marked() }));
|
||||
@@ -192,7 +194,7 @@ fn sweep_preserves_cycle_and_collects_unreachable() {
|
||||
#[test]
|
||||
fn sweep_preserves_array_elements_and_collects_dead_element_object() {
|
||||
let mut vm = Vm::new();
|
||||
let array_proto = vm.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let array_proto = vm.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let arr = vm.alloc_object(JsObject::new_array(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(array_proto),
|
||||
@@ -212,10 +214,10 @@ fn sweep_preserves_array_elements_and_collects_dead_element_object() {
|
||||
}
|
||||
vm.regs[0] = JsValue::from_js_object(arr_session);
|
||||
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.session_gc);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc);
|
||||
gc.mark(&vm);
|
||||
let _ = gc.sweep(&mut vm);
|
||||
vm.realm.gc.session_gc = gc;
|
||||
vm.realm.gc.borrow_mut().session_gc = gc;
|
||||
|
||||
// 原地 sweep 不搬移存活对象:数组保持原址、根指针不变,死元素对象
|
||||
// 原地释放并出表,存活集合只剩数组与元素 0 的对象。
|
||||
@@ -227,7 +229,7 @@ fn sweep_preserves_array_elements_and_collects_dead_element_object() {
|
||||
assert_eq!(unsafe { (*arr_addr).get_prop_at(0).as_js_object_ptr() }, live_session);
|
||||
// 元素 1 引用已断开:死元素对象被回收。
|
||||
assert_eq!(unsafe { (*arr_addr).get_prop_at(1) }, JsValue::undefined());
|
||||
assert_eq!(vm.realm.gc.session_object_ptrs.len(), 2);
|
||||
assert_eq!(vm.realm.gc.borrow().session_object_ptrs.len(), 2);
|
||||
}
|
||||
|
||||
fn vm_with_low_threshold() -> Vm {
|
||||
@@ -247,7 +249,7 @@ fn native_ok(result: NativeResult) -> JsValue {
|
||||
|
||||
/// 分配一个原型指向 Map.prototype 的占位对象并写入寄存器,作为构造器调用的 `this`。
|
||||
fn map_this(vm: &mut Vm, reg: u8) -> JsValue {
|
||||
let proto = vm.realm.session.builtin_world().map_proto.as_ptr() as *mut JsObject;
|
||||
let proto = vm.realm.session.borrow().builtin_world().map_proto.as_ptr() as *mut JsObject;
|
||||
let obj = vm.alloc_object(JsObject::new_empty(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto),
|
||||
@@ -259,7 +261,7 @@ fn map_this(vm: &mut Vm, reg: u8) -> JsValue {
|
||||
|
||||
/// 分配一个原型指向 DataView.prototype 的占位对象并写入寄存器,作为构造器调用的 `this`。
|
||||
fn data_view_this(vm: &mut Vm, reg: u8) -> JsValue {
|
||||
let proto = vm.realm.session.builtin_world().data_view_proto.as_ptr() as *mut JsObject;
|
||||
let proto = vm.realm.session.borrow().builtin_world().data_view_proto.as_ptr() as *mut JsObject;
|
||||
let obj = vm.alloc_object(JsObject::new_empty(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto),
|
||||
@@ -271,7 +273,7 @@ fn data_view_this(vm: &mut Vm, reg: u8) -> JsValue {
|
||||
|
||||
/// 分配一个原型指向 Set.prototype 的占位对象并写入寄存器,作为构造器调用的 `this`。
|
||||
fn set_this(vm: &mut Vm, reg: u8) -> JsValue {
|
||||
let proto = vm.realm.session.builtin_world().set_proto.as_ptr() as *mut JsObject;
|
||||
let proto = vm.realm.session.borrow().builtin_world().set_proto.as_ptr() as *mut JsObject;
|
||||
let obj = vm.alloc_object(JsObject::new_empty(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto),
|
||||
@@ -284,7 +286,7 @@ fn set_this(vm: &mut Vm, reg: u8) -> JsValue {
|
||||
/// 分配一个原型指向 DisposableStack.prototype 的占位对象并写入寄存器,
|
||||
/// 作为构造器调用的 `this`。
|
||||
fn dispose_stack_this(vm: &mut Vm, reg: u8) -> JsValue {
|
||||
let proto = vm.realm.session.builtin_world().disposable_stack_proto.as_ptr() as *mut JsObject;
|
||||
let proto = vm.realm.session.borrow().builtin_world().disposable_stack_proto.as_ptr() as *mut JsObject;
|
||||
let obj = vm.alloc_object(JsObject::new_empty(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto),
|
||||
@@ -297,7 +299,7 @@ fn dispose_stack_this(vm: &mut Vm, reg: u8) -> JsValue {
|
||||
/// 置函数位标志的占位对象(通过 `is_callable` 判定),供 adopt 的
|
||||
/// onDispose 槽使用;测试不调用它。
|
||||
fn function_placeholder(vm: &mut Vm) -> JsValue {
|
||||
let proto = vm.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let proto = vm.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let obj = vm.alloc_object(JsObject::new_empty(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto),
|
||||
@@ -312,16 +314,16 @@ fn function_placeholder(vm: &mut Vm) -> JsValue {
|
||||
fn reset_maybe_collect_collects_after_threshold() {
|
||||
let mut vm = vm_with_low_threshold();
|
||||
plain_object(&mut vm);
|
||||
assert!(!vm.realm.gc.session_object_ptrs.is_empty());
|
||||
let tracked_before = vm.realm.gc.session_object_ptrs.len();
|
||||
assert!(!vm.realm.gc.borrow().session_object_ptrs.is_empty());
|
||||
let tracked_before = vm.realm.gc.borrow().session_object_ptrs.len();
|
||||
|
||||
vm.regs[0] = JsValue::undefined();
|
||||
vm.regs[1] = JsValue::undefined();
|
||||
vm.reset();
|
||||
|
||||
assert!(vm.realm.gc.session_object_ptrs.len() <= tracked_before);
|
||||
assert_eq!(vm.realm.gc.session_object_ptrs.len(), 0);
|
||||
assert_eq!(vm.realm.gc.session_bytes_allocated, 0);
|
||||
assert!(vm.realm.gc.borrow().session_object_ptrs.len() <= tracked_before);
|
||||
assert_eq!(vm.realm.gc.borrow().session_object_ptrs.len(), 0);
|
||||
assert_eq!(vm.realm.gc.borrow().session_bytes_allocated, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -330,7 +332,7 @@ fn gc_stats_summary_includes_collection() {
|
||||
plain_object(&mut vm);
|
||||
vm.regs[0] = JsValue::undefined();
|
||||
vm.maybe_collect_session_gc();
|
||||
let summary = vm.realm.gc.session_gc.stats_summary();
|
||||
let summary = vm.realm.gc.borrow().session_gc.stats_summary();
|
||||
assert!(summary.contains("[GC] collection"));
|
||||
}
|
||||
|
||||
@@ -343,7 +345,7 @@ fn full_collect_keeps_global_root_edges_in_place() {
|
||||
}
|
||||
let old_ptr = obj;
|
||||
let key = vm.kernel_core.perm_interner().intern("gcRoot").0;
|
||||
let global_ptr = vm.realm.session.global_object().as_ptr() as *mut JsObject;
|
||||
let global_ptr = vm.realm.session.borrow().global_object().as_ptr() as *mut JsObject;
|
||||
unsafe {
|
||||
let global = &mut *global_ptr;
|
||||
vm.set_or_create_prop_value(global, key, JsValue::from_js_object(old_ptr));
|
||||
@@ -351,7 +353,8 @@ fn full_collect_keeps_global_root_edges_in_place() {
|
||||
|
||||
vm.maybe_collect_session_gc();
|
||||
|
||||
let global = vm.realm.session.global_object();
|
||||
let session = vm.realm.session.borrow();
|
||||
let global = session.global_object();
|
||||
let pos = vm
|
||||
.kernel_core
|
||||
.shape_forge()
|
||||
@@ -401,15 +404,15 @@ fn session_gc_traces_map_object_key_and_value() {
|
||||
let map_session = map_value.as_js_object_ptr();
|
||||
vm.regs.fill(JsValue::undefined());
|
||||
vm.regs[0] = JsValue::from_js_object(map_session);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.session_gc);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc);
|
||||
gc.collect(&mut vm);
|
||||
vm.realm.gc.session_gc = gc;
|
||||
vm.realm.gc.borrow_mut().session_gc = gc;
|
||||
|
||||
let live_map = unsafe { &*vm.regs[0].as_js_object_ptr() };
|
||||
let edges = map::map_native_edges(live_map);
|
||||
assert_eq!(edges.len(), 2);
|
||||
assert!(edges.iter().all(|value| vm.is_session_ptr(value.as_js_object_ptr())));
|
||||
assert_eq!(vm.realm.gc.session_object_ptrs.len(), 3);
|
||||
assert_eq!(vm.realm.gc.borrow().session_object_ptrs.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -425,15 +428,15 @@ fn session_gc_traces_set_object_key() {
|
||||
let set_session = set_value.as_js_object_ptr();
|
||||
vm.regs.fill(JsValue::undefined());
|
||||
vm.regs[0] = JsValue::from_js_object(set_session);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.session_gc);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc);
|
||||
gc.collect(&mut vm);
|
||||
vm.realm.gc.session_gc = gc;
|
||||
vm.realm.gc.borrow_mut().session_gc = gc;
|
||||
|
||||
let live_set = unsafe { &*vm.regs[0].as_js_object_ptr() };
|
||||
let edges = set::set_native_edges(live_set);
|
||||
assert_eq!(edges.len(), 1);
|
||||
assert!(vm.is_session_ptr(edges[0].as_js_object_ptr()));
|
||||
assert_eq!(vm.realm.gc.session_object_ptrs.len(), 2);
|
||||
assert_eq!(vm.realm.gc.borrow().session_object_ptrs.len(), 2);
|
||||
}
|
||||
|
||||
/// 造带条目盒的 WeakMap 形 session 对象(构造体落地前的单测代用形态)。
|
||||
@@ -470,9 +473,9 @@ fn weak_map_entry_survives_promotion_with_live_key() {
|
||||
let value_root =
|
||||
oxide_builtins::weak_map::weak_map_probe_get(unsafe { &*wm_session }, JsValue::from_js_object(key));
|
||||
vm.regs[2] = value_root;
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.session_gc);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc);
|
||||
gc.collect(&mut vm);
|
||||
vm.realm.gc.session_gc = gc;
|
||||
vm.realm.gc.borrow_mut().session_gc = gc;
|
||||
|
||||
let live_wm = unsafe { &*vm.regs[0].as_js_object_ptr() };
|
||||
assert_eq!(oxide_builtins::weak_map::weak_map_entry_count(live_wm), 1, "强可达键的条目须在收集后存活");
|
||||
@@ -503,9 +506,9 @@ fn weak_map_entry_dropped_when_key_unrooted() {
|
||||
vm.regs.fill(JsValue::undefined());
|
||||
vm.regs[0] = JsValue::from_js_object(wm_session);
|
||||
vm.regs[2] = JsValue::from_js_object(value);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.session_gc);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc);
|
||||
gc.collect(&mut vm);
|
||||
vm.realm.gc.session_gc = gc;
|
||||
vm.realm.gc.borrow_mut().session_gc = gc;
|
||||
|
||||
let live_wm = unsafe { &*vm.regs[0].as_js_object_ptr() };
|
||||
assert_eq!(oxide_builtins::weak_map::weak_map_entry_count(live_wm), 0, "死键条目须在收集时丢弃");
|
||||
@@ -535,9 +538,9 @@ fn weak_map_value_edge_keeps_value_alive() {
|
||||
vm.regs.fill(JsValue::undefined());
|
||||
vm.regs[0] = JsValue::from_js_object(wm_session);
|
||||
vm.regs[1] = JsValue::from_js_object(key);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.session_gc);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc);
|
||||
gc.collect(&mut vm);
|
||||
vm.realm.gc.session_gc = gc;
|
||||
vm.realm.gc.borrow_mut().session_gc = gc;
|
||||
|
||||
let live_wm = unsafe { &*vm.regs[0].as_js_object_ptr() };
|
||||
// 原地 sweep 不搬移对象:键与值边原地保留,按原键读回同一对象。
|
||||
@@ -547,7 +550,7 @@ fn weak_map_value_edge_keeps_value_alive() {
|
||||
assert!(vm.is_session_ptr(stored.as_js_object_ptr()));
|
||||
// key、value、wm 三对象均存活(key/value 经寄存器根、wm 经寄存器根),
|
||||
// 原地保留原址。
|
||||
assert_eq!(vm.realm.gc.session_object_ptrs.len(), 3);
|
||||
assert_eq!(vm.realm.gc.borrow().session_object_ptrs.len(), 3);
|
||||
}
|
||||
|
||||
/// in-run 原地 sweep·死键面:无强根的 session 键在原地 sweep 按 mark 位判死,
|
||||
@@ -560,7 +563,7 @@ fn weak_map_dead_key_freed_by_in_run_sweep() {
|
||||
// 键无强根:仅表内弱边引用。
|
||||
let key = plain_object(&mut vm);
|
||||
// P 键(builtin 原型,不可死):对照条目须保留。
|
||||
let p_key = JsValue::from_js_object(vm.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||
let p_key = JsValue::from_js_object(vm.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject);
|
||||
unsafe {
|
||||
oxide_builtins::weak_map::weak_map_insert(
|
||||
&mut *wm,
|
||||
@@ -574,7 +577,7 @@ fn weak_map_dead_key_freed_by_in_run_sweep() {
|
||||
vm.maybe_collect_in_run();
|
||||
|
||||
assert_eq!(vm.session_gc_stats().total_collections, 1, "in-run 收集应跑一轮");
|
||||
assert!(!vm.realm.gc.session_object_ptrs.contains(&key), "死键对象须出表");
|
||||
assert!(!vm.realm.gc.borrow().session_object_ptrs.contains(&key), "死键对象须出表");
|
||||
let live_wm = unsafe { &*vm.regs[0].as_js_object_ptr() };
|
||||
assert_eq!(
|
||||
oxide_builtins::weak_map::weak_map_entry_count(live_wm),
|
||||
@@ -645,8 +648,8 @@ fn session_gc_traces_map_string_and_bigint_value() {
|
||||
collect(&mut vm);
|
||||
|
||||
// 只断言表成员——sweep 已释放的指针解引用即 UB。
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&str_ptr));
|
||||
assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&str_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_bigint_ptrs.borrow().contains(&bi_ptr));
|
||||
}
|
||||
|
||||
/// 盒持唯一引用的 session 串与 BigInt 经 Set native 边进入存活集:
|
||||
@@ -672,8 +675,8 @@ fn session_gc_traces_set_string_and_bigint_value() {
|
||||
collect(&mut vm);
|
||||
|
||||
// 只断言表成员——sweep 已释放的指针解引用即 UB。
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&str_ptr));
|
||||
assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&str_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_bigint_ptrs.borrow().contains(&bi_ptr));
|
||||
}
|
||||
|
||||
/// 盒持唯一引用的 session 串与 BigInt 经资源栈条目边进入存活集:
|
||||
@@ -702,8 +705,8 @@ fn session_gc_traces_dispose_stack_string_and_bigint_value() {
|
||||
collect(&mut vm);
|
||||
|
||||
// 只断言表成员——sweep 已释放的指针解引用即 UB。
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&str_ptr));
|
||||
assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&str_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_bigint_ptrs.borrow().contains(&bi_ptr));
|
||||
}
|
||||
|
||||
/// 挂起帧内期望值 BigInt 查找:挂起点唯一持有期望值(帧内死槽残留的
|
||||
@@ -740,8 +743,8 @@ fn session_gc_traces_promise_string_and_bigint_result() {
|
||||
collect(&mut vm);
|
||||
|
||||
// 只断言表成员——sweep 已释放的指针解引用即 UB。
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&str_ptr));
|
||||
assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&str_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_bigint_ptrs.borrow().contains(&bi_ptr));
|
||||
}
|
||||
|
||||
/// 挂起生成器帧寄存器持唯一引用 session BigInt:yield 挂起时 b 存活
|
||||
@@ -771,7 +774,7 @@ fn session_gc_traces_suspended_generator_bigint_value() {
|
||||
collect(&mut vm);
|
||||
|
||||
// 只断言表成员——sweep 已释放的指针解引用即 UB。
|
||||
assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_bigint_ptrs.borrow().contains(&bi_ptr));
|
||||
}
|
||||
|
||||
/// 挂起异步函数帧持唯一引用 session BigInt:body await 永不结算的 promise,
|
||||
@@ -815,7 +818,7 @@ fn session_gc_traces_suspended_async_function_bigint_value() {
|
||||
collect(&mut vm);
|
||||
|
||||
// 只断言表成员——sweep 已释放的指针解引用即 UB。
|
||||
assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_bigint_ptrs.borrow().contains(&bi_ptr));
|
||||
}
|
||||
|
||||
/// 挂起异步生成器帧持唯一引用 session BigInt:首次 next() 挂起于 yield,
|
||||
@@ -844,7 +847,7 @@ fn session_gc_traces_suspended_async_generator_bigint_value() {
|
||||
collect(&mut vm);
|
||||
|
||||
// 只断言表成员——sweep 已释放的指针解引用即 UB。
|
||||
assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_bigint_ptrs.borrow().contains(&bi_ptr));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -883,9 +886,9 @@ fn session_gc_keeps_shared_array_buffer_alive_through_view_native_edges() {
|
||||
let root_session = root;
|
||||
vm.regs.fill(JsValue::undefined());
|
||||
vm.regs[0] = JsValue::from_js_object(root_session);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.session_gc);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc);
|
||||
gc.collect(&mut vm);
|
||||
vm.realm.gc.session_gc = gc;
|
||||
vm.realm.gc.borrow_mut().session_gc = gc;
|
||||
|
||||
let live_root = unsafe { &*vm.regs[0].as_js_object_ptr() };
|
||||
let live_typed = live_root.get_prop_at(0);
|
||||
@@ -936,16 +939,16 @@ fn session_gc_rewrites_buffer_retained_only_by_data_view_native_edge() {
|
||||
let view_session = view.as_js_object_ptr();
|
||||
vm.regs.fill(JsValue::undefined());
|
||||
vm.regs[0] = JsValue::from_js_object(view_session);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.session_gc);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc);
|
||||
gc.collect(&mut vm);
|
||||
vm.realm.gc.session_gc = gc;
|
||||
vm.realm.gc.borrow_mut().session_gc = gc;
|
||||
|
||||
let live_view = vm.regs[0];
|
||||
let live_view_obj = unsafe { &*live_view.as_js_object_ptr() };
|
||||
let edges = data_view::data_view_native_edges(live_view_obj);
|
||||
assert_eq!(edges.len(), 1);
|
||||
assert!(vm.is_session_ptr(edges[0].as_js_object_ptr()));
|
||||
assert_eq!(vm.realm.gc.session_object_ptrs.len(), 2);
|
||||
assert_eq!(vm.realm.gc.borrow().session_object_ptrs.len(), 2);
|
||||
|
||||
vm.regs[0] = live_view;
|
||||
vm.regs[1] = JsValue::int(0);
|
||||
@@ -953,9 +956,9 @@ fn session_gc_rewrites_buffer_retained_only_by_data_view_native_edge() {
|
||||
}
|
||||
|
||||
fn collect(vm: &mut Vm) {
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.session_gc);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc);
|
||||
gc.collect(vm);
|
||||
vm.realm.gc.session_gc = gc;
|
||||
vm.realm.gc.borrow_mut().session_gc = gc;
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -963,12 +966,12 @@ fn session_string_collected_when_dead() {
|
||||
let mut vm = Vm::new();
|
||||
let dead = vm.new_string("dead-string");
|
||||
let dead_ptr = dead.as_string_ptr_mut();
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&dead_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&dead_ptr));
|
||||
|
||||
// 没有根引用 `dead`(它只作为值包装存在于 Rust 栈上)。
|
||||
collect(&mut vm);
|
||||
|
||||
assert!(!vm.realm.gc.session_string_ptrs.contains(&dead_ptr));
|
||||
assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&dead_ptr));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -980,7 +983,7 @@ fn session_string_survives_when_in_register() {
|
||||
|
||||
collect(&mut vm);
|
||||
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&live_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&live_ptr));
|
||||
// 存活字符串永不搬移——寄存器仍指向同一 box。
|
||||
assert_eq!(vm.regs[0].as_string_ptr_mut(), live_ptr);
|
||||
assert_eq!(unsafe { (*live_ptr).as_str() }, "live-in-reg");
|
||||
@@ -1003,7 +1006,7 @@ fn session_string_survives_via_object_property() {
|
||||
|
||||
collect(&mut vm);
|
||||
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&s_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&s_ptr));
|
||||
assert_eq!(unsafe { (*s_ptr).as_str() }, "prop-string");
|
||||
}
|
||||
|
||||
@@ -1013,13 +1016,13 @@ fn permanent_string_untouched_by_sweep() {
|
||||
let perm = vm.perm_string("perm");
|
||||
let perm_ptr = perm.as_string_ptr_mut();
|
||||
// 永久字符串位于 PermInterner,绝不在 session 集合中。
|
||||
assert!(!vm.realm.gc.session_string_ptrs.contains(&perm_ptr));
|
||||
assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&perm_ptr));
|
||||
vm.regs[0] = perm;
|
||||
|
||||
collect(&mut vm);
|
||||
|
||||
// 绝不被 session 清扫释放(仍可读,仍不在 session 集合中)。
|
||||
assert!(!vm.realm.gc.session_string_ptrs.contains(&perm_ptr));
|
||||
assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&perm_ptr));
|
||||
assert_eq!(unsafe { (*perm_ptr).as_str() }, "perm");
|
||||
}
|
||||
|
||||
@@ -1030,11 +1033,11 @@ fn string_sweep_byte_accounting() {
|
||||
let _ = dead.as_string_ptr_mut();
|
||||
let expected = (size_of::<JsString>() + "0123456789".len()) as u64;
|
||||
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.session_gc);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc);
|
||||
let before = gc.total_bytes_freed;
|
||||
gc.collect(&mut vm);
|
||||
let after = gc.total_bytes_freed;
|
||||
vm.realm.gc.session_gc = gc;
|
||||
vm.realm.gc.borrow_mut().session_gc = gc;
|
||||
|
||||
assert!(after >= before + expected);
|
||||
}
|
||||
@@ -1055,7 +1058,8 @@ fn compile(source: &str) -> oxide_bytecode::module::CompiledModule {
|
||||
}
|
||||
|
||||
fn global_prop_opt(vm: &Vm, name: &str) -> Option<JsValue> {
|
||||
let global = vm.realm.session.global_object();
|
||||
let session = vm.realm.session.borrow();
|
||||
let global = session.global_object();
|
||||
let si = vm.kernel_core().perm_interner().intern(name).0;
|
||||
vm.resolve_property(global, si)
|
||||
}
|
||||
@@ -1071,7 +1075,7 @@ fn closure_cell_survives_object_sweep() {
|
||||
"function outer() { var counter = 0; function inc() { return ++counter; } globalThis.inc = inc; return 0; } outer(); 0",
|
||||
)))
|
||||
.expect("run1");
|
||||
assert!(!vm.realm.gc.session_cell_ptrs.borrow().is_empty(), "run1 应分配 upvalue cell");
|
||||
assert!(!vm.realm.gc.borrow().session_cell_ptrs.borrow().is_empty(), "run1 应分配 upvalue cell");
|
||||
|
||||
// reset 触发对象 sweep:存活对象原地保留,死对象原地释放。
|
||||
vm.reset();
|
||||
@@ -1126,10 +1130,10 @@ fn cells_freed_by_full_reset_and_reallocatable() {
|
||||
"function outer() { var x = 1; function f() { return x; } globalThis.f = f; return f(); } outer()",
|
||||
)))
|
||||
.expect("run1");
|
||||
assert!(!vm.realm.gc.session_cell_ptrs.borrow().is_empty());
|
||||
assert!(!vm.realm.gc.borrow().session_cell_ptrs.borrow().is_empty());
|
||||
|
||||
vm.full_reset();
|
||||
assert!(vm.realm.gc.session_cell_ptrs.borrow().is_empty(), "full_reset 应释放全部 cell");
|
||||
assert!(vm.realm.gc.borrow().session_cell_ptrs.borrow().is_empty(), "full_reset 应释放全部 cell");
|
||||
|
||||
let result = vm
|
||||
.run(&Arc::new(compile(
|
||||
@@ -1186,13 +1190,13 @@ fn live_closure_cell_survives_full_collect() {
|
||||
let cells = obj.upvalues_slice();
|
||||
assert_eq!(cells.len(), 1, "inc 应捕获 counter 一个 cell");
|
||||
let cell_ptr = cells[0];
|
||||
assert!(vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr), "活 cell 应登记在表");
|
||||
assert!(vm.realm.gc.borrow().session_cell_ptrs.borrow().contains(&cell_ptr), "活 cell 应登记在表");
|
||||
|
||||
vm.regs.fill(JsValue::undefined());
|
||||
collect(&mut vm);
|
||||
|
||||
// 只断言表成员——sweep 已释放的指针解引用即 UB。
|
||||
assert!(vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr), "活 cell 应跨完整收集存活");
|
||||
assert!(vm.realm.gc.borrow().session_cell_ptrs.borrow().contains(&cell_ptr), "活 cell 应跨完整收集存活");
|
||||
}
|
||||
|
||||
/// 活跃帧 cell_stack 根:内层函数 upvalue cell 在活跃帧 cell_stack 上(低阈值使
|
||||
@@ -1245,14 +1249,14 @@ fn suspended_state_box_cell_root_survives_collect() {
|
||||
.copied()
|
||||
.find(|p| !p.is_null())
|
||||
.expect("挂起帧应持有 x cell");
|
||||
assert!(vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr), "cell 应登记在表");
|
||||
assert!(vm.realm.gc.borrow().session_cell_ptrs.borrow().contains(&cell_ptr), "cell 应登记在表");
|
||||
|
||||
vm.regs.fill(JsValue::undefined());
|
||||
vm.regs[0] = JsValue::from_js_object(g_session);
|
||||
collect(&mut vm);
|
||||
|
||||
// 只断言表成员——sweep 已释放的指针解引用即 UB。
|
||||
assert!(vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr), "挂起帧 cell 应跨完整收集存活");
|
||||
assert!(vm.realm.gc.borrow().session_cell_ptrs.borrow().contains(&cell_ptr), "挂起帧 cell 应跨完整收集存活");
|
||||
|
||||
// 恢复后读值:cell 未被误释放。
|
||||
let read = vm.run(&Arc::new(compile("globalThis.g.next().value"))).expect("read");
|
||||
@@ -1312,7 +1316,7 @@ fn module_ns_cell_root_survives_collect() {
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("依赖 ns 应持有 x 的 cell");
|
||||
assert!(vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr), "cell 应登记在表");
|
||||
assert!(vm.realm.gc.borrow().session_cell_ptrs.borrow().contains(&cell_ptr), "cell 应登记在表");
|
||||
|
||||
vm.regs.fill(JsValue::undefined());
|
||||
vm.regs[0] = ns_val;
|
||||
@@ -1320,7 +1324,7 @@ fn module_ns_cell_root_survives_collect() {
|
||||
|
||||
// 只断言表成员——sweep 已释放的指针解引用即 UB。
|
||||
assert!(
|
||||
vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr),
|
||||
vm.realm.gc.borrow().session_cell_ptrs.borrow().contains(&cell_ptr),
|
||||
"模块 ns cell 应跨完整收集存活"
|
||||
);
|
||||
|
||||
@@ -1346,16 +1350,16 @@ fn allocation_does_not_trigger_gc_before_safe_point() {
|
||||
let seed_ptr = seed.as_string_ptr_mut();
|
||||
let returned = vm.new_string_owned("returned".repeat(16));
|
||||
|
||||
assert_eq!(vm.realm.gc.session_gc.total_collections, 0);
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&seed_ptr));
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&returned.as_string_ptr_mut()));
|
||||
assert_eq!(vm.realm.gc.borrow().session_gc.total_collections, 0);
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&seed_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&returned.as_string_ptr_mut()));
|
||||
|
||||
// 显式触发后:无根引用的 seed 被回收,入根的 returned 存活(安全点语义)。
|
||||
vm.regs[0] = returned;
|
||||
vm.maybe_collect_session_strings();
|
||||
assert!(vm.realm.gc.session_gc.total_collections >= 1);
|
||||
assert!(!vm.realm.gc.session_string_ptrs.contains(&seed_ptr));
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&returned.as_string_ptr_mut()));
|
||||
assert!(vm.realm.gc.borrow().session_gc.total_collections >= 1);
|
||||
assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&seed_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&returned.as_string_ptr_mut()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1388,10 +1392,10 @@ fn strings_only_collection_preserves_all_root_kinds() {
|
||||
|
||||
vm.maybe_collect_session_strings();
|
||||
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(®_str.as_string_ptr_mut()));
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&prop_str.as_string_ptr_mut()));
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&epoch_str.as_string_ptr_mut()));
|
||||
assert!(!vm.realm.gc.session_string_ptrs.contains(&dead_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(®_str.as_string_ptr_mut()));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&prop_str.as_string_ptr_mut()));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&epoch_str.as_string_ptr_mut()));
|
||||
assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&dead_ptr));
|
||||
// 存活串内容可读,地址稳定。
|
||||
assert_eq!(unsafe { (*reg_str.as_string_ptr_mut()).as_str() }, "reg-root".repeat(16));
|
||||
assert_eq!(unsafe { (*prop_str.as_string_ptr_mut()).as_str() }, "prop-root".repeat(16));
|
||||
@@ -1404,7 +1408,7 @@ fn strings_only_collection_does_not_move_objects() {
|
||||
let obj = plain_object(&mut vm);
|
||||
let obj_session = obj;
|
||||
vm.regs[0] = JsValue::from_js_object(obj_session);
|
||||
let before: Vec<_> = vm.realm.gc.session_object_ptrs.clone();
|
||||
let before: Vec<_> = vm.realm.gc.borrow().session_object_ptrs.clone();
|
||||
|
||||
let s = vm.new_string_owned("x".repeat(64));
|
||||
vm.regs[1] = s;
|
||||
@@ -1412,7 +1416,7 @@ fn strings_only_collection_does_not_move_objects() {
|
||||
vm.maybe_collect_session_strings();
|
||||
|
||||
// 对象指针逐一相同:不搬移、不重写根。
|
||||
assert_eq!(vm.realm.gc.session_object_ptrs, before);
|
||||
assert_eq!(vm.realm.gc.borrow().session_object_ptrs, before);
|
||||
assert_eq!(vm.regs[0].as_js_object_ptr(), obj_session);
|
||||
assert_eq!(vm.session_object_count(), 1);
|
||||
}
|
||||
@@ -1435,10 +1439,10 @@ fn multiple_strings_only_cycles_keep_objects_alive() {
|
||||
}
|
||||
// 分配不自动触发,每轮显式触发一次回收。
|
||||
vm.maybe_collect_session_strings();
|
||||
assert!(vm.realm.gc.session_object_ptrs.contains(&obj_session));
|
||||
assert!(vm.realm.gc.borrow().session_object_ptrs.contains(&obj_session));
|
||||
assert_eq!(unsafe { (*obj_session).get_prop_at(0) }, live);
|
||||
}
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&live.as_string_ptr_mut()));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&live.as_string_ptr_mut()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1458,15 +1462,15 @@ fn strings_only_then_full_collect_keeps_object_strings_live() {
|
||||
// 第一轮 strings-only:对象被 mark 置位。收尾清残留,否则该位残留至完整
|
||||
// 收集会使 mark DFS 在对象处短路、漏标其属性中的串;随后校验串跨完整收集存活。
|
||||
vm.maybe_collect_session_strings();
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&s_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&s_ptr));
|
||||
|
||||
// 完整收集(reset 的 maybe_collect 路径):对象搬移、字符串地址稳定。
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.session_gc);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc);
|
||||
gc.collect(&mut vm);
|
||||
vm.realm.gc.session_gc = gc;
|
||||
vm.realm.gc.borrow_mut().session_gc = gc;
|
||||
|
||||
// 存活对象经重写仍可达其串:串未被误释放,内容可读。
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&s_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&s_ptr));
|
||||
assert_eq!(unsafe { (*s_ptr).as_str() }, "kept".repeat(8));
|
||||
assert_eq!(unsafe { (*vm.regs[0].as_js_object_ptr()).get_prop_at(0) }, s);
|
||||
}
|
||||
@@ -1487,8 +1491,8 @@ fn strings_only_collection_byte_accounting_matches_survivors() {
|
||||
|
||||
// 账目 = 对象头 + 存活串(size_of::<JsString>() + len),死串不再计入。
|
||||
let expected = size_of::<JsObject>() + (size_of::<JsString>() + unsafe { (*live_ptr).payload_bytes() });
|
||||
assert_eq!(vm.realm.gc.session_bytes_allocated, expected);
|
||||
assert!(!vm.realm.gc.session_string_ptrs.contains(&dead.as_string_ptr_mut()));
|
||||
assert_eq!(vm.realm.gc.borrow().session_bytes_allocated, expected);
|
||||
assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&dead.as_string_ptr_mut()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1498,9 +1502,10 @@ fn run_alloc_formula_lists_bigint_and_cell_via_tables() {
|
||||
vm.regs[0] = JsValue::from_js_object(obj);
|
||||
// 带堆区对象:元素向量非空(账目含头 + 向量容量),深层等式在双计形态
|
||||
// 下不成立,钉向量面互不相交。
|
||||
let array_proto_ptr = vm.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let arr = vm.alloc_object(JsObject::new_array(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(vm.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject),
|
||||
JsValue::from_js_object(array_proto_ptr),
|
||||
2,
|
||||
));
|
||||
vm.regs[3] = JsValue::from_js_object(arr);
|
||||
@@ -1509,30 +1514,30 @@ fn run_alloc_formula_lists_bigint_and_cell_via_tables() {
|
||||
let b = vm.new_bigint(num_bigint::BigInt::from(7_i128));
|
||||
vm.regs[2] = b;
|
||||
// 未根 cell:收集后不可达,随 sweep 出表。
|
||||
vm.realm.gc.alloc_cell(JsValue::int(9), true);
|
||||
vm.realm.gc.borrow().alloc_cell(JsValue::int(9), true);
|
||||
|
||||
// 公式 = 手工账目 + BigInt 表长 × 尺寸 + cell 表长 × 尺寸,三分量两两不相交。
|
||||
assert_eq!(
|
||||
vm.run_alloc_bytes(),
|
||||
vm.realm.gc.session_bytes_allocated
|
||||
+ vm.realm.gc.session_bigint_ptrs.borrow().len() * size_of::<num_bigint::BigInt>()
|
||||
+ vm.realm.gc.session_cell_ptrs.borrow().len() * size_of::<Cell>()
|
||||
vm.realm.gc.borrow().session_bytes_allocated
|
||||
+ vm.realm.gc.borrow().session_bigint_ptrs.borrow().len() * size_of::<num_bigint::BigInt>()
|
||||
+ vm.realm.gc.borrow().session_cell_ptrs.borrow().len() * size_of::<Cell>()
|
||||
);
|
||||
// BigInt 不在手工账目内:账目 = 两对象(头 + 堆数据)+ 串,数组带元素向量。
|
||||
let string_bytes = size_of::<JsString>() + unsafe { (*s.as_string_ptr_mut()).payload_bytes() };
|
||||
let arr_heap_bytes = SessionGc::object_heap_data_bytes(unsafe { &*arr });
|
||||
assert!(arr_heap_bytes > 0, "数组元素向量应计入对象堆数据");
|
||||
assert_eq!(
|
||||
vm.realm.gc.session_bytes_allocated,
|
||||
vm.realm.gc.borrow().session_bytes_allocated,
|
||||
2 * size_of::<JsObject>() + arr_heap_bytes as usize + string_bytes
|
||||
);
|
||||
|
||||
// 完整收集后:死 cell 出表、存活 BigInt 留表,账目保持对象 + 串口径。
|
||||
vm.collect_session_gc();
|
||||
assert_eq!(vm.realm.gc.session_cell_ptrs.borrow().len(), 0);
|
||||
assert_eq!(vm.realm.gc.session_bigint_ptrs.borrow().len(), 1);
|
||||
assert_eq!(vm.realm.gc.borrow().session_cell_ptrs.borrow().len(), 0);
|
||||
assert_eq!(vm.realm.gc.borrow().session_bigint_ptrs.borrow().len(), 1);
|
||||
assert_eq!(
|
||||
vm.realm.gc.session_bytes_allocated,
|
||||
vm.realm.gc.borrow().session_bytes_allocated,
|
||||
2 * size_of::<JsObject>() + arr_heap_bytes as usize + string_bytes
|
||||
);
|
||||
// 深采样全量重算与轻层公式零增长下逐位相等:存活对象带堆区(元素向量),
|
||||
@@ -1546,8 +1551,8 @@ fn runtime_collection_below_threshold_is_noop() {
|
||||
let s = vm.new_string_owned("small".repeat(4));
|
||||
vm.regs[0] = s;
|
||||
|
||||
assert_eq!(vm.realm.gc.session_gc.total_collections, 0);
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&s.as_string_ptr_mut()));
|
||||
assert_eq!(vm.realm.gc.borrow().session_gc.total_collections, 0);
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&s.as_string_ptr_mut()));
|
||||
}
|
||||
|
||||
// ── rope(Cons)GC 传播闭包 ──
|
||||
@@ -1570,9 +1575,9 @@ fn rope_survives_collection_with_children_propagated() {
|
||||
|
||||
// 父(寄存器根)+ 子节点(经传播闭包)全部存活,内容可读。
|
||||
let parent_ptr = parent.as_string_ptr_mut();
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&parent_ptr));
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&l_ptr));
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&r_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&parent_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&l_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&r_ptr));
|
||||
assert_eq!(unsafe { (*parent_ptr).as_lossy_str() }, "left-partright-part");
|
||||
}
|
||||
|
||||
@@ -1581,14 +1586,14 @@ fn rope_children_swept_when_parent_dead() {
|
||||
let mut vm = Vm::new();
|
||||
let (parent, l_ptr, r_ptr) = make_cons_pair(&mut vm, "left", "right");
|
||||
let parent_ptr = parent.as_string_ptr_mut();
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&parent_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&parent_ptr));
|
||||
|
||||
// 父与子均无根引用 → 整树回收。
|
||||
collect(&mut vm);
|
||||
|
||||
assert!(!vm.realm.gc.session_string_ptrs.contains(&parent_ptr));
|
||||
assert!(!vm.realm.gc.session_string_ptrs.contains(&l_ptr));
|
||||
assert!(!vm.realm.gc.session_string_ptrs.contains(&r_ptr));
|
||||
assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&parent_ptr));
|
||||
assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&l_ptr));
|
||||
assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&r_ptr));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1605,8 +1610,8 @@ fn rope_product_freed_with_parent() {
|
||||
|
||||
// 父死 → 连带释放产物(不 double-free、不泄漏;产物从不进 session 表)。
|
||||
let flat_mut = flat_ptr as *mut JsString;
|
||||
assert!(!vm.realm.gc.session_string_ptrs.contains(&parent_ptr));
|
||||
assert!(!vm.realm.gc.session_string_ptrs.contains(&flat_mut));
|
||||
assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&parent_ptr));
|
||||
assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&flat_mut));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1623,7 +1628,7 @@ fn rope_deep_chain_mark_iterative() {
|
||||
|
||||
collect(&mut vm);
|
||||
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&chain_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&chain_ptr));
|
||||
assert_eq!(unsafe { (*chain_ptr).as_lossy_str() }, format!("root{}", "x".repeat(1024)));
|
||||
}
|
||||
|
||||
@@ -1640,8 +1645,8 @@ fn rope_perm_child_untouched_by_sweep() {
|
||||
collect(&mut vm);
|
||||
|
||||
// perm 子节点永不释放(不在 session 表);session 子节点随父存活。
|
||||
assert!(!vm.realm.gc.session_string_ptrs.contains(&perm_ptr));
|
||||
assert!(vm.realm.gc.session_string_ptrs.contains(&session_ptr));
|
||||
assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&perm_ptr));
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&session_ptr));
|
||||
assert_eq!(unsafe { (*parent.as_string_ptr_mut()).as_lossy_str() }, "perm-leafsession-leaf");
|
||||
}
|
||||
|
||||
@@ -1655,7 +1660,7 @@ fn full_reset_frees_rope_and_product() {
|
||||
|
||||
// full_reset 清空全部 session 字符串(连带产物):无泄漏、无 double-free。
|
||||
vm.full_reset();
|
||||
assert!(vm.realm.gc.session_string_ptrs.is_empty());
|
||||
assert!(vm.realm.gc.borrow().session_string_ptrs.is_empty());
|
||||
}
|
||||
|
||||
// ── 执行期两档收集 ──────────────────────────────────────────────────────
|
||||
@@ -1708,7 +1713,7 @@ fn active_and_suspended_for_in_do_not_block_in_run_collection() {
|
||||
|
||||
vm.maybe_collect_in_run();
|
||||
assert_eq!(vm.session_gc_stats().total_collections, 1, "无门控:活跃 for-in 存在时收集照常执行");
|
||||
assert!(!vm.realm.gc.session_object_ptrs.contains(&dead), "死 session 对象应被原地释放并出表");
|
||||
assert!(!vm.realm.gc.borrow().session_object_ptrs.contains(&dead), "死 session 对象应被原地释放并出表");
|
||||
|
||||
// 挂起形:生成器在 for-in 内 yield 后 run 结束,迭代器经状态盒持有
|
||||
// (vm.iters 已空)——键经状态盒边入根收集标活,收集照常执行。
|
||||
@@ -1725,7 +1730,9 @@ fn active_and_suspended_for_in_do_not_block_in_run_collection() {
|
||||
assert_eq!(vm.session_gc_stats().total_collections, 1, "挂起 for-in:收集照常执行");
|
||||
// 挂起生成器对象保持原址(未释放):状态盒与迭代器一体存活。
|
||||
let gen_in_session = vm
|
||||
.realm.gc
|
||||
.realm
|
||||
.gc
|
||||
.borrow()
|
||||
.session_object_ptrs
|
||||
.iter()
|
||||
.any(|&p| !p.is_null() && unsafe { (*p).type_tag == JsObject::OBJ_TYPE_GENERATOR });
|
||||
@@ -1900,11 +1907,11 @@ fn promise_promote_migrates_reject_reactions_to_clone() {
|
||||
fn mark_counts_roots_by_group() {
|
||||
let mut vm = Vm::new();
|
||||
vm.regs[0] = JsValue::from_js_object(plain_object(&mut vm));
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.session_gc);
|
||||
let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc);
|
||||
gc.mark(&vm);
|
||||
vm.realm.gc.session_gc = gc;
|
||||
vm.realm.gc.borrow_mut().session_gc = gc;
|
||||
|
||||
let counts = &vm.realm.gc.session_gc.root_counts;
|
||||
let counts = &vm.realm.gc.borrow().session_gc.root_counts;
|
||||
assert_eq!(counts[RootGroup::Regs as usize], 256, "寄存器组应计满 256 槽");
|
||||
assert_eq!(counts[RootGroup::Global as usize], 1, "global 组应计 global 对象");
|
||||
// 9 个单槽侧通道:空 VM 时各经 unwrap_or(undefined) 产出一个未定义根值。
|
||||
|
||||
@@ -47,7 +47,7 @@ pub fn eval_script(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let code = oxide_kernel::string_forge::source_escape(&units);
|
||||
match vm.create_plain_dynamic_script(&code) {
|
||||
Ok(func) => {
|
||||
let global = JsValue::from_js_object(vm.realm.session.global_object().as_ptr() as *mut JsObject);
|
||||
let global = JsValue::from_js_object(vm.realm.session.borrow().global_object().as_ptr() as *mut JsObject);
|
||||
match vm.call_function_sync(func, global, &[]) {
|
||||
Ok(val) => NativeResult::Ok(val),
|
||||
Err(e) => NativeResult::Err(oxide_builtins::error::create_from_text(vm, &e)),
|
||||
@@ -82,7 +82,7 @@ pub fn create_realm(vm: &mut Vm, _args: &[u8]) -> NativeResult {
|
||||
/// - 置强制收集旗标;下一个顶层指令边界执行 mark + 移动式 sweep +
|
||||
/// 字符串/BigInt 清扫,重写 session 对象指针。
|
||||
pub fn gc(vm: &mut Vm, _args: &[u8]) -> NativeResult {
|
||||
vm.realm.gc.pending_forced_collect = true;
|
||||
vm.realm.gc.borrow_mut().pending_forced_collect = true;
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
|
||||
@@ -47,10 +47,13 @@ impl Vm {
|
||||
obj.set_session_epoch(true);
|
||||
obj.set_heap_alloc(true);
|
||||
let ptr = Box::into_raw(Box::new(obj));
|
||||
self.realm.gc.session_object_ptrs.push(ptr);
|
||||
// 直 session 分配计入堆账目(与 promote 同式:对象头 + 对象堆数据)。
|
||||
self.realm.gc.session_bytes_allocated += std::mem::size_of::<JsObject>()
|
||||
// 直 session 分配计入堆账目(与 promote 同式:对象头 + 对象堆数据):
|
||||
// 表登记与账目更新合并进同一次借用。
|
||||
let bytes = std::mem::size_of::<JsObject>()
|
||||
+ crate::session_gc::SessionGc::object_heap_data_bytes(unsafe { &*ptr }) as usize;
|
||||
let gc = &mut self.realm.gc.borrow_mut();
|
||||
gc.session_object_ptrs.push(ptr);
|
||||
gc.session_bytes_allocated += bytes;
|
||||
ptr
|
||||
}
|
||||
|
||||
@@ -296,7 +299,7 @@ impl Vm {
|
||||
self.regs[254] = if sub_is_arrow {
|
||||
obj.captured_this()
|
||||
} else if !sub_is_strict && this_value.is_nullish() {
|
||||
JsValue::from_js_object(self.realm.session.global_object().as_ptr() as *mut JsObject)
|
||||
JsValue::from_js_object(self.realm.session.borrow().global_object().as_ptr() as *mut JsObject)
|
||||
} else if sub_is_strict {
|
||||
this_value
|
||||
} else {
|
||||
@@ -402,7 +405,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn native_function(vm: &mut Vm, f: crate::native::NativeFn) -> JsValue {
|
||||
let proto = vm.realm.session.builtin_world().function_proto.as_ptr() as *mut JsObject;
|
||||
let proto = vm.realm.session.borrow().builtin_world().function_proto.as_ptr() as *mut JsObject;
|
||||
let mut obj = JsObject::new_empty(oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto));
|
||||
obj.set_function(true);
|
||||
// SAFETY: f 是 NativeFn 函数项,可作为 NativeFnPtr 存储。
|
||||
|
||||
@@ -128,29 +128,31 @@ impl Vm {
|
||||
// 宿主 `$262.gc()` 置位的强制收集与压力模式收集在此边界消费:
|
||||
// 完整收集唯一安全点(无在途 dispatch 局部值),与正常触发路径
|
||||
// 同一入口。压力模式开启后每个顶层边界做一次完整收集。
|
||||
if self.realm.gc.pending_forced_collect || self.realm.gc.gc_pressure_mode {
|
||||
self.realm.gc.pending_forced_collect = false;
|
||||
// 各字段读入局部变量(临时借用)后释放借用再调收集,
|
||||
// 不跨 collect 调用持借。
|
||||
if self.realm.gc.borrow().pending_forced_collect || self.realm.gc.borrow().gc_pressure_mode {
|
||||
self.realm.gc.borrow_mut().pending_forced_collect = false;
|
||||
self.collect_session_gc();
|
||||
}
|
||||
// 峰值高水位:同一边界采样 session 堆账目上界
|
||||
let bytes = self.realm.gc.session_bytes_allocated;
|
||||
if bytes > self.realm.gc.session_bytes_peak {
|
||||
self.realm.gc.session_bytes_peak = bytes;
|
||||
let bytes = self.realm.gc.borrow().session_bytes_allocated;
|
||||
if bytes > self.realm.gc.borrow().session_bytes_peak {
|
||||
self.realm.gc.borrow_mut().session_bytes_peak = bytes;
|
||||
}
|
||||
// 单 run 分配包络高水位:O(1) 读(手工账目 + BigInt/cell 表长)。
|
||||
let alloc = self.run_alloc_bytes();
|
||||
if alloc > self.realm.gc.run_alloc_peak {
|
||||
self.realm.gc.run_alloc_peak = alloc;
|
||||
if alloc > self.realm.gc.borrow().run_alloc_peak {
|
||||
self.realm.gc.borrow_mut().run_alloc_peak = alloc;
|
||||
}
|
||||
// 压力模式开启时跳过两档水位收集:完整收集已涵盖其全部工作,
|
||||
// 同边界双触发只会多一轮空转 mark。
|
||||
if bytes >= self.realm.gc.string_gc_watermark && !self.realm.gc.gc_pressure_mode {
|
||||
if bytes >= self.realm.gc.borrow().string_gc_watermark && !self.realm.gc.borrow().gc_pressure_mode {
|
||||
self.maybe_collect_session_strings();
|
||||
}
|
||||
// 执行期原地 sweep 收集:O(1) 包络超触发水位 → 收集(无门控:
|
||||
// 迭代器键引用经根收集标活,原地释放不搬移存活对象;
|
||||
// 边界契约见 maybe_collect_in_run)。
|
||||
if alloc >= self.realm.gc.gc_watermark && !self.realm.gc.gc_pressure_mode {
|
||||
if alloc >= self.realm.gc.borrow().gc_watermark && !self.realm.gc.borrow().gc_pressure_mode {
|
||||
self.maybe_collect_in_run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
}
|
||||
f(RootGroup::Global, JsValue::from_js_object(self.realm.session.global_object().as_ptr() as *mut JsObject));
|
||||
f(RootGroup::Global, JsValue::from_js_object(self.realm.session.borrow().global_object().as_ptr() as *mut JsObject));
|
||||
}
|
||||
|
||||
/// GC 根统一枚举入口:遍历的字段清单与 `for_each_value` 相同。
|
||||
@@ -163,17 +163,17 @@ impl Vm {
|
||||
/// `session_gc` 经 `mem::take` 借出后再放回:收集需要 `&mut Vm`,而
|
||||
/// `SessionGc` 是 `Vm` 的内部字段,借出以避开借用冲突。
|
||||
pub(crate) fn maybe_collect_session_gc(&mut self) {
|
||||
let mut session_gc = std::mem::take(&mut self.realm.gc.session_gc);
|
||||
let mut session_gc = std::mem::take(&mut self.realm.gc.borrow_mut().session_gc);
|
||||
session_gc.maybe_collect(self);
|
||||
self.realm.gc.session_gc = session_gc;
|
||||
self.realm.gc.borrow_mut().session_gc = session_gc;
|
||||
}
|
||||
|
||||
/// 执行期字符串阈值回收:仅回收 session 字符串(跳过对象搬移)。热路径只在
|
||||
/// 超阈值后进入,`mem::take` 不承担每次分配的开销。
|
||||
pub(crate) fn maybe_collect_session_strings(&mut self) {
|
||||
let mut session_gc = std::mem::take(&mut self.realm.gc.session_gc);
|
||||
let mut session_gc = std::mem::take(&mut self.realm.gc.borrow_mut().session_gc);
|
||||
session_gc.maybe_collect_strings_only(self);
|
||||
self.realm.gc.session_gc = session_gc;
|
||||
self.realm.gc.borrow_mut().session_gc = session_gc;
|
||||
}
|
||||
|
||||
/// 执行期原地 sweep 收集的 dispatch 安全点入口:仅在循环顶
|
||||
@@ -187,35 +187,38 @@ impl Vm {
|
||||
/// - 无对象可回收时仍跑一轮(mark + 原地清扫),水位同点抬高,
|
||||
/// 触发间距由包络增量控制。
|
||||
pub(crate) fn maybe_collect_in_run(&mut self) {
|
||||
let mut session_gc = std::mem::take(&mut self.realm.gc.session_gc);
|
||||
let mut session_gc = std::mem::take(&mut self.realm.gc.borrow_mut().session_gc);
|
||||
session_gc.collect_in_run(self);
|
||||
self.realm.gc.session_gc = session_gc;
|
||||
self.realm.gc.borrow_mut().session_gc = session_gc;
|
||||
}
|
||||
|
||||
/// 只读访问 session GC 的统计(回收次数、存活/死亡对象数、释放字节等)。
|
||||
pub fn session_gc_stats(&self) -> &SessionGc {
|
||||
&self.realm.gc.session_gc
|
||||
///
|
||||
/// 返回 `Ref` 守卫(`session_gc` 入 `RefCell` 后无法再给稳定 `&`):
|
||||
/// 调用方在单表达式内消费,不跨 `borrow_mut` 长存。
|
||||
pub fn session_gc_stats(&self) -> std::cell::Ref<'_, SessionGc> {
|
||||
std::cell::Ref::map(self.realm.gc.borrow(), |gc| &gc.session_gc)
|
||||
}
|
||||
|
||||
/// 当前 session arena 中存活(已晋升)的对象数量。
|
||||
pub fn session_object_count(&self) -> usize {
|
||||
self.realm.gc.session_object_ptrs.len()
|
||||
self.realm.gc.borrow().session_object_ptrs.len()
|
||||
}
|
||||
|
||||
/// session 当前分配的字节数(对象 + 存活字符串)。
|
||||
pub fn session_bytes_allocated(&self) -> usize {
|
||||
self.realm.gc.session_bytes_allocated
|
||||
self.realm.gc.borrow().session_bytes_allocated
|
||||
}
|
||||
|
||||
/// 执行期 session 堆账目的峰值高水位(顶层指令边界采样,全量重置清零)。
|
||||
pub fn session_bytes_peak(&self) -> usize {
|
||||
self.realm.gc.session_bytes_peak
|
||||
self.realm.gc.borrow().session_bytes_peak
|
||||
}
|
||||
|
||||
/// 本 run 累计分配字节的高水位:`run_alloc_bytes` 的顶层指令边界
|
||||
/// 采样上界,run 边界(reset/full_reset)重起算。留存内存观测锚。
|
||||
pub fn run_alloc_peak(&self) -> usize {
|
||||
self.realm.gc.run_alloc_peak
|
||||
self.realm.gc.borrow().run_alloc_peak
|
||||
}
|
||||
|
||||
/// 本 run 累计分配字节:session 手工堆账目(session 对象及其属性向量 +
|
||||
@@ -228,9 +231,9 @@ impl Vm {
|
||||
/// 属性区(元素/属性向量扩容)增长对其不可见——上限判定须配合
|
||||
/// [`Self::run_alloc_bytes_full`] 的深采样层。
|
||||
pub(crate) fn run_alloc_bytes(&self) -> usize {
|
||||
self.realm.gc.session_bytes_allocated
|
||||
+ self.realm.gc.session_bigint_ptrs.borrow().len() * std::mem::size_of::<num_bigint::BigInt>()
|
||||
+ self.realm.gc.session_cell_ptrs.borrow().len() * std::mem::size_of::<Cell>()
|
||||
self.realm.gc.borrow().session_bytes_allocated
|
||||
+ self.realm.gc.borrow().session_bigint_ptrs.borrow().len() * std::mem::size_of::<num_bigint::BigInt>()
|
||||
+ self.realm.gc.borrow().session_cell_ptrs.borrow().len() * std::mem::size_of::<Cell>()
|
||||
}
|
||||
|
||||
/// 本 run 累计分配字节(O(1) 读)。
|
||||
@@ -248,7 +251,7 @@ impl Vm {
|
||||
/// 与 [`Self::run_alloc_bytes`] 的轻层公式逐位相等。
|
||||
pub(crate) fn run_alloc_bytes_full(&self) -> u64 {
|
||||
let mut bytes = 0u64;
|
||||
for &ptr in self.realm.gc.session_object_ptrs.iter() {
|
||||
for &ptr in self.realm.gc.borrow().session_object_ptrs.iter() {
|
||||
if ptr.is_null() {
|
||||
continue;
|
||||
}
|
||||
@@ -256,15 +259,15 @@ impl Vm {
|
||||
let obj = unsafe { &*ptr };
|
||||
bytes += std::mem::size_of::<JsObject>() as u64 + SessionGc::object_heap_data_bytes(obj);
|
||||
}
|
||||
for &ptr in &self.realm.gc.session_string_ptrs {
|
||||
for &ptr in &self.realm.gc.borrow().session_string_ptrs {
|
||||
if ptr.is_null() {
|
||||
continue;
|
||||
}
|
||||
// SAFETY: ptr 在字符串表登记,收尾前有效。
|
||||
bytes += (std::mem::size_of::<JsString>() + unsafe { (*ptr).payload_bytes() }) as u64;
|
||||
}
|
||||
bytes += (self.realm.gc.session_bigint_ptrs.borrow().len() * std::mem::size_of::<num_bigint::BigInt>()) as u64;
|
||||
bytes += (self.realm.gc.session_cell_ptrs.borrow().len() * std::mem::size_of::<Cell>()) as u64;
|
||||
bytes += (self.realm.gc.borrow().session_bigint_ptrs.borrow().len() * std::mem::size_of::<num_bigint::BigInt>()) as u64;
|
||||
bytes += (self.realm.gc.borrow().session_cell_ptrs.borrow().len() * std::mem::size_of::<Cell>()) as u64;
|
||||
bytes
|
||||
}
|
||||
|
||||
@@ -278,9 +281,9 @@ impl Vm {
|
||||
/// - 须在执行外的安全点调用(无在途 builtin 局部裸指针、dispatch 未重入);
|
||||
/// 执行期触发仍走水位路径,本入口供事后观测(如基准测 workload 后留存堆)。
|
||||
pub fn collect_session_gc(&mut self) {
|
||||
let mut session_gc = std::mem::take(&mut self.realm.gc.session_gc);
|
||||
let mut session_gc = std::mem::take(&mut self.realm.gc.borrow_mut().session_gc);
|
||||
session_gc.collect(self);
|
||||
self.realm.gc.session_gc = session_gc;
|
||||
self.realm.gc.borrow_mut().session_gc = session_gc;
|
||||
}
|
||||
|
||||
/// 当前 epoch 中已分配并跟踪的对象数量。对象分配统一入口 Box 化后
|
||||
|
||||
@@ -176,8 +176,10 @@ pub struct Vm {
|
||||
pub(crate) frames: SmallVec<[CallFrame; 16]>,
|
||||
pub(crate) kernel_core: Arc<KernelCore>,
|
||||
/// 每 VM 的 realm 组合:内核会话(builtin world 与 global 对象)、
|
||||
/// session GC 簿记与 10 个内建原型槽(见 `realm` 模块)。
|
||||
pub(crate) realm: Realm,
|
||||
/// session GC 簿记与 10 个内建原型槽(见 `realm` 模块)。以 `Arc` 持有:
|
||||
/// 可变组经 `RefCell` 内部可变性在 `&Arc<Realm>` 下改写,teardown 归
|
||||
/// `Drop for Realm`(per-realm 消亡:Arc 计数归零时收尾)。
|
||||
pub(crate) realm: Arc<Realm>,
|
||||
/// `"length"` 属性键的 intern id 缓存:进程内稳定(PermInterner append-only、
|
||||
/// KernelCore 不重建),属性 get/set 热路径免每次 intern(hash64 + DashMap +
|
||||
/// RwLock 读锁)。
|
||||
@@ -382,10 +384,11 @@ impl Drop for Vm {
|
||||
fn drop(&mut self) {
|
||||
// 边界守卫计数:与构造器登记恰好配对(Rust 所有权保证恰好一次)。
|
||||
self.kernel_core.note_vm_ended();
|
||||
// 直接 drop(test262 每测试新建即弃)不经 reset/full_reset 路径:
|
||||
// 统一收尾释放全部 session 堆数据与内建原型属性区,防逐测试累积泄漏。
|
||||
self.teardown_intrinsic_protos();
|
||||
self.teardown_session_heap_data();
|
||||
// 活跃 for-in / for-of 迭代器是 per-VM 执行态:收尾路径逐条释放,
|
||||
// 防 Vm 直接 drop 时表内残留体泄漏。
|
||||
self.iters.reset();
|
||||
// realm 收尾归 `Drop for Realm`(per-realm 消亡:Arc 计数归零时触发,
|
||||
// 直接 drop 时恰好一次)。
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,13 +422,16 @@ impl Vm {
|
||||
}
|
||||
|
||||
/// 只读访问当前 session(builtin world 与 global object)。
|
||||
pub fn session(&self) -> &KernelSession {
|
||||
&self.realm.session
|
||||
///
|
||||
/// 返回 `Ref` 守卫(session 入 `RefCell` 后无法再给稳定 `&`):调用方
|
||||
/// 在单表达式内消费,不跨 `borrow_mut` 长存。
|
||||
pub fn session(&self) -> std::cell::Ref<'_, KernelSession> {
|
||||
self.realm.session.borrow()
|
||||
}
|
||||
|
||||
/// 判定裸指针是否指向当前 session 的 `%Object.prototype%`。
|
||||
pub(crate) fn is_object_prototype(&self, ptr: *const JsObject) -> bool {
|
||||
let proto_ptr = self.realm.session.builtin_world().object_proto.as_ptr();
|
||||
let proto_ptr = self.realm.session.borrow().builtin_world().object_proto.as_ptr();
|
||||
std::ptr::eq(ptr, proto_ptr)
|
||||
}
|
||||
|
||||
|
||||
@@ -45,8 +45,8 @@ impl oxide_runtime_api::VmHost for Vm {
|
||||
fn kernel_core(&self) -> &Arc<KernelCore> {
|
||||
self.kernel_core()
|
||||
}
|
||||
fn session(&self) -> &KernelSession {
|
||||
&self.realm.session
|
||||
fn session(&self) -> std::cell::Ref<'_, KernelSession> {
|
||||
self.session()
|
||||
}
|
||||
fn pc(&self) -> usize {
|
||||
self.pc
|
||||
@@ -189,9 +189,9 @@ impl oxide_runtime_api::VmHost for Vm {
|
||||
return false;
|
||||
}
|
||||
let p = proto.as_js_object_ptr() as *const JsObject;
|
||||
std::ptr::eq(p, self.realm.generator_function_proto.as_ptr())
|
||||
|| std::ptr::eq(p, self.realm.async_function_proto.as_ptr())
|
||||
|| std::ptr::eq(p, self.realm.async_generator_function_proto.as_ptr())
|
||||
std::ptr::eq(p, self.realm.generator_function_proto.borrow().as_ptr())
|
||||
|| std::ptr::eq(p, self.realm.async_function_proto.borrow().as_ptr())
|
||||
|| std::ptr::eq(p, self.realm.async_generator_function_proto.borrow().as_ptr())
|
||||
}
|
||||
fn step_rng(&mut self) {
|
||||
self.step_rng()
|
||||
|
||||
@@ -387,7 +387,7 @@ impl Vm {
|
||||
}
|
||||
let si = self.property_key_si(key_val)?;
|
||||
let value = self.regs[a];
|
||||
let global_ptr = self.realm.session.global_object().as_ptr() as *mut JsObject;
|
||||
let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject;
|
||||
// SAFETY: 全局对象钉在 session 永久区,指针在 VM 生命周期内有效。
|
||||
let obj = unsafe { &mut *global_ptr };
|
||||
// 属性已存在:accessor 形走 Set 语义(有 setter 调用之、无 setter
|
||||
@@ -483,7 +483,7 @@ impl Vm {
|
||||
}
|
||||
let si = self.property_key_si(key_val)?;
|
||||
let value = self.regs[a];
|
||||
let global_ptr = self.realm.session.global_object().as_ptr() as *mut JsObject;
|
||||
let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject;
|
||||
// SAFETY: 全局对象钉在 session 永久区,指针在 VM 生命周期内有效。
|
||||
let obj = unsafe { &mut *global_ptr };
|
||||
// 既有属性:CreateGlobalVarBinding 零动作。
|
||||
|
||||
@@ -55,7 +55,7 @@ impl Vm {
|
||||
for i in 0..arg_count.min(256) {
|
||||
args.push(self.regs[first_arg_reg.wrapping_add(i as u8) as usize]);
|
||||
}
|
||||
let proto_ptr = &*self.realm.object_prototype as *const JsObject as *mut JsObject;
|
||||
let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject;
|
||||
let new_obj = self.alloc_object(JsObject::new_empty(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto_ptr),
|
||||
@@ -63,7 +63,7 @@ impl Vm {
|
||||
return self.dispatch_new_bound(rd, constructor, new_obj, args, call_window);
|
||||
}
|
||||
|
||||
let proto_ptr = &*self.realm.object_prototype as *const JsObject as *mut JsObject;
|
||||
let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject;
|
||||
let new_obj = self.alloc_object(JsObject::new_empty(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto_ptr),
|
||||
@@ -998,7 +998,7 @@ impl Vm {
|
||||
if src.is_null() || src.is_undefined() {
|
||||
return self.raise_type_error("Cannot destructure property of null/undefined");
|
||||
}
|
||||
let proto_ptr = self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let proto_ptr = self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let rest_ptr = self.alloc_object(JsObject::new_empty(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto_ptr),
|
||||
@@ -1088,7 +1088,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
// 提交到 rest 对象。
|
||||
let proto_ptr = self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let proto_ptr = self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let rest_ptr = self.alloc_object(JsObject::new_empty(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto_ptr),
|
||||
@@ -1210,7 +1210,7 @@ impl Vm {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let proto_ptr = self.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let proto_ptr = self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let proto_val = JsValue::from_js_object(proto_ptr);
|
||||
// 模板对象直接分配为 session 对象:写入 session 根(存进 global/数组)
|
||||
// 时对象地址稳定,缓存指针不失效——分配档不同则首次写入触发搬移,
|
||||
|
||||
@@ -239,7 +239,7 @@ impl Vm {
|
||||
/// - 只可挂在真实 A 侧写发生之后;写失败路径(只读 no-op / 抛错)不得
|
||||
/// 调用,否则污染镜像槽。
|
||||
pub(crate) fn sync_global_builtin_mirror(&mut self, obj: &JsObject, key_si: u32, val: JsValue) {
|
||||
if !std::ptr::eq(obj as *const JsObject, self.realm.session.global_object().as_ptr()) {
|
||||
if !std::ptr::eq(obj as *const JsObject, self.realm.session.borrow().global_object().as_ptr()) {
|
||||
return;
|
||||
}
|
||||
let Some(module) = self.active_module() else {
|
||||
@@ -264,7 +264,7 @@ impl Vm {
|
||||
pub(crate) fn sync_global_builtin_mirror_slot(
|
||||
&mut self, obj: &JsObject, shape_id: u32, slot: u32, depth: u8, val: JsValue,
|
||||
) {
|
||||
if !std::ptr::eq(obj as *const JsObject, self.realm.session.global_object().as_ptr()) {
|
||||
if !std::ptr::eq(obj as *const JsObject, self.realm.session.borrow().global_object().as_ptr()) {
|
||||
return;
|
||||
}
|
||||
if depth != 0 || shape_id != obj.shape_id() {
|
||||
@@ -305,7 +305,8 @@ impl Vm {
|
||||
if map.is_empty() {
|
||||
return;
|
||||
}
|
||||
let global = self.realm.session.global_object();
|
||||
let session = self.realm.session.borrow();
|
||||
let global = session.global_object();
|
||||
for (name, reg) in map {
|
||||
if (*reg as usize) < pack_end {
|
||||
continue;
|
||||
@@ -1543,7 +1544,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn native_function(vm: &mut Vm, f: crate::native::NativeFn) -> JsValue {
|
||||
let proto = vm.realm.session.builtin_world().function_proto.as_ptr() as *mut JsObject;
|
||||
let proto = vm.realm.session.borrow().builtin_world().function_proto.as_ptr() as *mut JsObject;
|
||||
let mut obj = JsObject::new_empty(oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto));
|
||||
obj.set_function(true);
|
||||
// SAFETY: f 是 NativeFn 函数项,可作为 NativeFnPtr 存储。
|
||||
@@ -1552,7 +1553,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn plain_object(vm: &mut Vm) -> JsValue {
|
||||
let proto = vm.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let proto = vm.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let obj = JsObject::new_empty(oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto));
|
||||
JsValue::object(vm.alloc_object(obj) as *mut u8)
|
||||
}
|
||||
@@ -1681,7 +1682,7 @@ mod tests {
|
||||
// ===== 大索引(越稠密上限)写降级命名属性的行为钉 =====
|
||||
|
||||
fn new_array_val(vm: &mut Vm, n: u32) -> JsValue {
|
||||
let proto = vm.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let proto = vm.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let arr =
|
||||
JsObject::new_array(oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto), n as usize);
|
||||
JsValue::object(vm.alloc_object(arr) as *mut u8)
|
||||
|
||||
@@ -502,7 +502,7 @@ impl Vm {
|
||||
self.regs[254] = if sub.is_arrow {
|
||||
callee_obj.captured_this()
|
||||
} else if !sub.is_strict && receiver.is_nullish() {
|
||||
JsValue::from_js_object(self.realm.session.global_object().as_ptr() as *mut JsObject)
|
||||
JsValue::from_js_object(self.realm.session.borrow().global_object().as_ptr() as *mut JsObject)
|
||||
} else if sub.is_strict {
|
||||
receiver
|
||||
} else {
|
||||
@@ -632,7 +632,7 @@ impl Vm {
|
||||
// 存活代际集:函数对象只经创建点直落 session 对象表登记(或晋升克隆
|
||||
// 随行),扫全表即完备;native 函数哨兵 sub_module_index == 0 不计数。
|
||||
let mut live_gens: Vec<u32> = Vec::new();
|
||||
for &ptr in &self.realm.gc.session_object_ptrs {
|
||||
for &ptr in &self.realm.gc.borrow().session_object_ptrs {
|
||||
if ptr.is_null() {
|
||||
continue;
|
||||
}
|
||||
@@ -692,11 +692,11 @@ impl Vm {
|
||||
// 顶层 this:脚本为全局对象(ECMA-262 全局执行上下文);
|
||||
// ES module 顶层环境 GetThisBinding 返回 undefined。记录到
|
||||
// top_level_this:rerun 清空寄存器文件后按此恢复顶层 this。
|
||||
let global = self.realm.session.global_object();
|
||||
let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject;
|
||||
let this_val = if module.is_es_module {
|
||||
JsValue::undefined()
|
||||
} else {
|
||||
JsValue::from_js_object(global.as_ptr() as *mut JsObject)
|
||||
JsValue::from_js_object(global_ptr)
|
||||
};
|
||||
self.top_level_this = this_val;
|
||||
self.regs[254] = this_val;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#![allow(clippy::arc_with_non_send_sync)]
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -64,24 +65,24 @@ impl Vm {
|
||||
active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0),
|
||||
frames: smallvec::SmallVec::new(),
|
||||
kernel_core: core,
|
||||
realm: Realm {
|
||||
session,
|
||||
object_prototype: obj_proto,
|
||||
generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
promise_constructor: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
promise_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
aggregate_error_constructor: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
aggregate_error_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
async_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
async_generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
async_generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
gc: GcState {
|
||||
realm: Arc::new(Realm {
|
||||
session: RefCell::new(session),
|
||||
object_prototype: RefCell::new(obj_proto),
|
||||
generator_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
generator_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
promise_constructor: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
promise_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
aggregate_error_constructor: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
aggregate_error_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
async_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
async_generator_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
async_generator_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
gc: RefCell::new(GcState {
|
||||
session_gc: crate::session_gc::SessionGc::new(),
|
||||
session_object_ptrs: Vec::new(),
|
||||
session_string_ptrs: Vec::new(),
|
||||
session_bigint_ptrs: std::cell::RefCell::new(Vec::new()),
|
||||
session_cell_ptrs: std::cell::RefCell::new(Vec::new()),
|
||||
session_bigint_ptrs: RefCell::new(Vec::new()),
|
||||
session_cell_ptrs: RefCell::new(Vec::new()),
|
||||
session_bytes_allocated: 0,
|
||||
session_bytes_peak: 0,
|
||||
run_alloc_peak: 0,
|
||||
@@ -90,8 +91,8 @@ impl Vm {
|
||||
gc_watermark: gc_threshold,
|
||||
pending_forced_collect: false,
|
||||
gc_pressure_mode: gc_pressure_mode_from_env(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
}),
|
||||
length_si,
|
||||
length_perm_ptr,
|
||||
number_to_string_cache_keys: [0u64; 16],
|
||||
@@ -186,7 +187,7 @@ impl Vm {
|
||||
vm.init_async_intrinsics();
|
||||
vm.init_async_generator_intrinsics();
|
||||
// Promise 全局绑定发生在快照采集之后,重录快照避免首次 full_reset 误判脏。
|
||||
vm.realm.session.record_snapshot();
|
||||
vm.realm.session.borrow_mut().record_snapshot();
|
||||
// 边界守卫计数:VM 完整构造后登记,与 `Drop for Vm` 的注销恰好配对。
|
||||
vm.kernel_core.note_vm_started();
|
||||
vm_info!("Vm created");
|
||||
@@ -211,24 +212,24 @@ impl Vm {
|
||||
active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0),
|
||||
frames: smallvec::SmallVec::new(),
|
||||
kernel_core: core,
|
||||
realm: Realm {
|
||||
session,
|
||||
object_prototype: obj_proto,
|
||||
generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
promise_constructor: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
promise_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
aggregate_error_constructor: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
aggregate_error_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
async_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
async_generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
async_generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
gc: GcState {
|
||||
realm: Arc::new(Realm {
|
||||
session: RefCell::new(session),
|
||||
object_prototype: RefCell::new(obj_proto),
|
||||
generator_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
generator_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
promise_constructor: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
promise_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
aggregate_error_constructor: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
aggregate_error_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
async_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
async_generator_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
async_generator_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))),
|
||||
gc: RefCell::new(GcState {
|
||||
session_gc: crate::session_gc::SessionGc::new(),
|
||||
session_object_ptrs: Vec::new(),
|
||||
session_string_ptrs: Vec::new(),
|
||||
session_bigint_ptrs: std::cell::RefCell::new(Vec::new()),
|
||||
session_cell_ptrs: std::cell::RefCell::new(Vec::new()),
|
||||
session_bigint_ptrs: RefCell::new(Vec::new()),
|
||||
session_cell_ptrs: RefCell::new(Vec::new()),
|
||||
session_bytes_allocated: 0,
|
||||
session_bytes_peak: 0,
|
||||
run_alloc_peak: 0,
|
||||
@@ -237,8 +238,8 @@ impl Vm {
|
||||
gc_watermark: gc_threshold,
|
||||
pending_forced_collect: false,
|
||||
gc_pressure_mode: gc_pressure_mode_from_env(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
}),
|
||||
length_si,
|
||||
length_perm_ptr,
|
||||
number_to_string_cache_keys: [0u64; 16],
|
||||
@@ -333,7 +334,7 @@ impl Vm {
|
||||
vm.init_async_intrinsics();
|
||||
vm.init_async_generator_intrinsics();
|
||||
// Promise 全局绑定发生在快照采集之后,重录快照避免首次 full_reset 误判脏。
|
||||
vm.realm.session.record_snapshot();
|
||||
vm.realm.session.borrow_mut().record_snapshot();
|
||||
// 边界守卫计数:VM 完整构造后登记,与 `Drop for Vm` 的注销恰好配对。
|
||||
vm.kernel_core.note_vm_started();
|
||||
vm_info!("Vm created (pool)");
|
||||
@@ -370,26 +371,28 @@ impl Vm {
|
||||
// 防御兜底:属性值写原语已推进 generation,常规覆盖写由快照对比发现;
|
||||
// 若未来出现绕过属性写原语的裸属性区改写,session 对象会被 session GC 回收,
|
||||
// 保留 global 将持悬垂指针,故带 session 对象时强制 bump 保证 global 重建。
|
||||
if !self.realm.gc.session_object_ptrs.is_empty() {
|
||||
let global_ptr = self.realm.session.global_object().as_ptr() as *mut JsObject;
|
||||
if !self.realm.gc.borrow().session_object_ptrs.is_empty() {
|
||||
let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject;
|
||||
unsafe { &mut *global_ptr }.bump_generation();
|
||||
}
|
||||
let dirty = self.realm.session.selective_reset(&self.kernel_core);
|
||||
let dirty = self.realm.session.borrow_mut().selective_reset(&self.kernel_core);
|
||||
if dirty.any_builtin_dirty() {
|
||||
bindings::rebind_dirty_builtins(&self.kernel_core, &mut self.realm.session, Some(&dirty));
|
||||
bindings::rebind_dirty_builtins(&self.kernel_core, &mut self.realm.session.borrow_mut(), Some(&dirty));
|
||||
}
|
||||
if dirty.global {
|
||||
let global_ptr = self.realm.session.global_object().as_ptr() as *mut JsObject;
|
||||
let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject;
|
||||
let global = unsafe { &mut *global_ptr };
|
||||
bindings::bind_global_builtin_slots(&self.kernel_core, &self.realm.session, global);
|
||||
bindings::bind_global_builtin_slots(&self.kernel_core, &self.realm.session.borrow_mut(), global);
|
||||
}
|
||||
self.realm.object_prototype = P::clone(&self.realm.session.builtin_world().object_proto);
|
||||
*self.realm
|
||||
.object_prototype
|
||||
.borrow_mut() = P::clone(&self.realm.session.borrow().builtin_world().object_proto);
|
||||
self.init_generator_intrinsics();
|
||||
self.init_promise_intrinsics();
|
||||
self.init_async_intrinsics();
|
||||
self.init_async_generator_intrinsics();
|
||||
// 快照须在 Promise 全局绑定之后采集:绑定会修改 global 世代。
|
||||
self.realm.session.record_snapshot();
|
||||
self.realm.session.borrow_mut().record_snapshot();
|
||||
self.clear_full_reset_state();
|
||||
vm_info!("full_reset completed");
|
||||
}
|
||||
@@ -397,14 +400,16 @@ impl Vm {
|
||||
/// benchmark 专用重置路径:总是丢弃并重建整个 session 与内置对象。
|
||||
#[doc(hidden)]
|
||||
pub fn full_reset_legacy_for_bench(&mut self) {
|
||||
self.realm.session = KernelSession::new(&self.kernel_core);
|
||||
bindings::init_kernel_builtins(&self.kernel_core, &mut self.realm.session);
|
||||
self.realm.object_prototype = P::clone(&self.realm.session.builtin_world().object_proto);
|
||||
*self.realm.session.borrow_mut() = KernelSession::new(&self.kernel_core);
|
||||
bindings::init_kernel_builtins(&self.kernel_core, &mut self.realm.session.borrow_mut());
|
||||
*self.realm
|
||||
.object_prototype
|
||||
.borrow_mut() = P::clone(&self.realm.session.borrow().builtin_world().object_proto);
|
||||
self.init_generator_intrinsics();
|
||||
self.init_promise_intrinsics();
|
||||
self.init_async_intrinsics();
|
||||
self.init_async_generator_intrinsics();
|
||||
self.realm.session.record_snapshot();
|
||||
self.realm.session.borrow_mut().record_snapshot();
|
||||
self.clear_full_reset_state();
|
||||
}
|
||||
|
||||
@@ -435,15 +440,16 @@ impl Vm {
|
||||
// 否则下次命中返回悬垂指针。
|
||||
self.number_to_string_cache_keys = [0u64; 16];
|
||||
self.number_to_string_cache_vals = [JsValue::undefined(); 16];
|
||||
self.teardown_session_heap_data();
|
||||
self.realm.gc.session_bytes_allocated = 0;
|
||||
self.realm.gc.session_bytes_peak = 0;
|
||||
self.realm.gc.run_alloc_peak = 0;
|
||||
self.realm.gc.string_gc_watermark = self.kernel_core.config().session_gc_threshold;
|
||||
self.realm.teardown_session_heap_data();
|
||||
self.realm.gc.borrow_mut().session_bytes_allocated = 0;
|
||||
self.realm.gc.borrow_mut().session_bytes_peak = 0;
|
||||
self.realm.gc.borrow_mut().run_alloc_peak = 0;
|
||||
self.realm.gc.borrow_mut().string_gc_watermark = self.kernel_core.config().session_gc_threshold;
|
||||
// 执行期收集水位同点复位(同式:阈值增量起算):旧 run 的存活包络
|
||||
// 不延续到新 run 的触发判定。
|
||||
self.realm.gc.gc_watermark = self.realm.gc.gc_threshold_cached;
|
||||
self.realm.gc.session_gc = crate::session_gc::SessionGc::new();
|
||||
let threshold = self.realm.gc.borrow().gc_threshold_cached;
|
||||
self.realm.gc.borrow_mut().gc_watermark = threshold;
|
||||
self.realm.gc.borrow_mut().session_gc = crate::session_gc::SessionGc::new();
|
||||
self.symbols.reset();
|
||||
self.root_reg_limit = 0;
|
||||
self.active_reg_limit = 0;
|
||||
@@ -452,61 +458,6 @@ impl Vm {
|
||||
self.max_steps_override = None;
|
||||
}
|
||||
|
||||
/// 释放全部 session 堆数据:session 对象(本体 + 堆数据 + upvalue 列表)
|
||||
/// + session 串 + BigInt box + upvalue cell box。
|
||||
///
|
||||
/// 供 `full_reset` 与 `Drop` 共用——统一入口路径上对象本体全部为堆载体
|
||||
/// (`Box::from_raw` 恰好释放一次),堆数据与 upvalue 列表各恰好释放一次。
|
||||
/// 独占所有权免去重:死对象已出表不重复枚举。
|
||||
pub(crate) fn teardown_session_heap_data(&mut self) {
|
||||
// 迭代器体是堆上 Box:收尾路径(full_reset 与 Drop 共用)逐条释放,
|
||||
// 防 Vm 直接 drop 时表内残留体泄漏。
|
||||
self.iters.reset();
|
||||
// 对象逐条独占释放:本体 + 堆数据 + upvalue 列表,顺序由
|
||||
// drop_dead_session_object 收口。
|
||||
for ptr in self.realm.gc.session_object_ptrs.drain(..) {
|
||||
if ptr.is_null() {
|
||||
continue;
|
||||
}
|
||||
// SAFETY: ptr 来自 session 对象表登记,收尾时仍指向合法对象;
|
||||
// 表独占持有,无其他释放点。
|
||||
crate::session_gc::SessionGc::drop_dead_session_object(ptr);
|
||||
}
|
||||
self.free_session_string_heap_data();
|
||||
self.free_session_bigint_heap_data();
|
||||
self.realm.gc.free_cells();
|
||||
}
|
||||
|
||||
/// 释放 VM 内建原型 P 对象(生成器/Promise/异步族与 Object 原型)的堆外属性区。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// 对象本体随 Arc 引用归零释放(字段 drop);属性区须在此显式释放一次
|
||||
/// (`JsObject` 无 Drop 口径)。仅释放本 VM 独占(强引用计数为 1)的副本:
|
||||
/// `object_prototype` 与 session world 共享 Arc,session 存活期下一测试仍经
|
||||
/// world 引用同一对象,其属性区归 session 收尾(`teardown_builtins`)释放。
|
||||
pub(crate) fn teardown_intrinsic_protos(&mut self) {
|
||||
for p in [
|
||||
&self.realm.object_prototype,
|
||||
&self.realm.generator_proto,
|
||||
&self.realm.generator_function_proto,
|
||||
&self.realm.promise_constructor,
|
||||
&self.realm.promise_proto,
|
||||
&self.realm.aggregate_error_constructor,
|
||||
&self.realm.aggregate_error_proto,
|
||||
&self.realm.async_function_proto,
|
||||
&self.realm.async_generator_proto,
|
||||
&self.realm.async_generator_function_proto,
|
||||
] {
|
||||
if p.strong_count() != 1 {
|
||||
continue;
|
||||
}
|
||||
// SAFETY: 独占副本的属性区仅此一处释放并置空(幂等)。
|
||||
unsafe {
|
||||
(&mut *p.as_mut_ptr()).release_raw_heap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn clear_execution_state(&mut self) {
|
||||
// 重置契约:
|
||||
// - 清空寄存器文件、pc、帧/迭代器栈、保存的执行栈、try 处理器、
|
||||
@@ -570,7 +521,7 @@ impl Vm {
|
||||
self.sampling.clear_records();
|
||||
// GC 统计同属执行期状态:池化 Vm 跨 run 复用,不清零则 per-run
|
||||
// 指标(gc_trigger_count)跨文件累积,报告失真。
|
||||
let gc = &mut self.realm.gc.session_gc;
|
||||
let gc = &mut self.realm.gc.borrow_mut().session_gc;
|
||||
gc.total_collections = 0;
|
||||
gc.total_bytes_freed = 0;
|
||||
gc.total_objects_scanned = 0;
|
||||
@@ -595,9 +546,10 @@ impl Vm {
|
||||
// 指针作废,下次 run 重装。
|
||||
self.active_immutables = std::ptr::slice_from_raw_parts(std::ptr::null(), 0);
|
||||
// 单 run 分配包络按 run 边界重起算(与 run_alloc_bytes 起算口径同源)。
|
||||
self.realm.gc.run_alloc_peak = 0;
|
||||
self.realm.gc.borrow_mut().run_alloc_peak = 0;
|
||||
// 执行期收集水位与包络同起算:旧 run 的存活包络不延续到新 run 的触发判定。
|
||||
self.realm.gc.gc_watermark = self.realm.gc.gc_threshold_cached;
|
||||
let threshold = self.realm.gc.borrow().gc_threshold_cached;
|
||||
self.realm.gc.borrow_mut().gc_watermark = threshold;
|
||||
self.root_reg_limit = 0;
|
||||
self.active_reg_limit = 0;
|
||||
}
|
||||
@@ -669,8 +621,8 @@ impl Vm {
|
||||
/// 水位触发(此时 builtin 局部值已落地为执行根,分配点触发会误释放
|
||||
/// 仅存于局部/构造中的活串)。
|
||||
fn register_session_string(&mut self, ptr: *mut JsString, bytes: usize) -> JsValue {
|
||||
self.realm.gc.session_string_ptrs.push(ptr);
|
||||
self.realm.gc.session_bytes_allocated += std::mem::size_of::<JsString>() + bytes;
|
||||
self.realm.gc.borrow_mut().session_string_ptrs.push(ptr);
|
||||
self.realm.gc.borrow_mut().session_bytes_allocated += std::mem::size_of::<JsString>() + bytes;
|
||||
JsValue::string(ptr)
|
||||
}
|
||||
|
||||
@@ -738,19 +690,6 @@ impl Vm {
|
||||
JsValue::perm_string(self.kernel_core.perm_interner().string_ptr(id))
|
||||
}
|
||||
|
||||
/// 释放全部 session 堆 `JsString`。仅在完全隔离重置
|
||||
/// (`full_reset` / `clear_full_reset_state`)时调用,此时没有存活的 session 对象
|
||||
/// 会引用它们。较轻量的 `reset()` 刻意保留它们,与 session 对象跨 eval 存活一致。
|
||||
fn free_session_string_heap_data(&mut self) {
|
||||
for ptr in self.realm.gc.session_string_ptrs.drain(..) {
|
||||
// SAFETY: 每个指针来自 new_string/new_cons_string 的 Box::into_raw,
|
||||
// 且只在这里(或 sweep)恰好释放一次;内部连带释放 rope 扁平化产物。
|
||||
unsafe {
|
||||
crate::session_gc::SessionGc::drop_session_string_box(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 为 BytecodeFunc 常量创建函数 JsObject。
|
||||
/// 当 `is_arrow` 为 true 时,捕获当前 `this`(regs[254]),供调用时词法 this 绑定。
|
||||
///
|
||||
@@ -772,13 +711,13 @@ impl Vm {
|
||||
// 异步生成器(`async function*`)函数对象:原型为 %AsyncGeneratorFunction.prototype%。
|
||||
let is_async_generator = is_generator && is_async;
|
||||
let proto_val = if is_async_generator {
|
||||
JsValue::from_js_object(self.realm.async_generator_function_proto.as_ptr() as *mut JsObject)
|
||||
JsValue::from_js_object(self.realm.async_generator_function_proto.borrow().as_ptr() as *mut JsObject)
|
||||
} else if is_generator {
|
||||
JsValue::from_js_object(self.realm.generator_function_proto.as_ptr() as *mut JsObject)
|
||||
JsValue::from_js_object(self.realm.generator_function_proto.borrow().as_ptr() as *mut JsObject)
|
||||
} else if is_async {
|
||||
JsValue::from_js_object(self.realm.async_function_proto.as_ptr() as *mut JsObject)
|
||||
JsValue::from_js_object(self.realm.async_function_proto.borrow().as_ptr() as *mut JsObject)
|
||||
} else {
|
||||
JsValue::from_js_object(self.realm.session.builtin_world().function_proto.as_ptr() as *mut JsObject)
|
||||
JsValue::from_js_object(self.realm.session.borrow().builtin_world().function_proto.as_ptr() as *mut JsObject)
|
||||
};
|
||||
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto_val);
|
||||
obj.set_function(true);
|
||||
@@ -803,11 +742,11 @@ impl Vm {
|
||||
// 原型对象自身的 [[Prototype]]:生成器为 %GeneratorPrototype%,普通函数为 Object.prototype。
|
||||
// 纯异步函数(非生成器)无 `prototype` 属性:规范不为其建 prototype 子对象。
|
||||
let proto_of_proto = if is_async_generator {
|
||||
JsValue::from_js_object(self.realm.async_generator_proto.as_ptr() as *mut JsObject)
|
||||
JsValue::from_js_object(self.realm.async_generator_proto.borrow().as_ptr() as *mut JsObject)
|
||||
} else if is_generator {
|
||||
JsValue::from_js_object(self.realm.generator_proto.as_ptr() as *mut JsObject)
|
||||
JsValue::from_js_object(self.realm.generator_proto.borrow().as_ptr() as *mut JsObject)
|
||||
} else {
|
||||
JsValue::from_js_object(self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject)
|
||||
JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject)
|
||||
};
|
||||
// prototype 子对象与函数本体同走 session 分配:`f.prototype ===
|
||||
// globalThis.f.prototype` 要求两侧同一对象,逃逸写不会克隆出第二份。
|
||||
@@ -873,7 +812,7 @@ impl Vm {
|
||||
/// `&self` 使 `convert_immutables`(常量池 → JsValue)也能分配。
|
||||
pub fn new_bigint(&self, v: num_bigint::BigInt) -> JsValue {
|
||||
let ptr = Box::into_raw(Box::new(v));
|
||||
self.realm.gc.session_bigint_ptrs.borrow_mut().push(ptr);
|
||||
self.realm.gc.borrow_mut().session_bigint_ptrs.borrow_mut().push(ptr);
|
||||
JsValue::bigint(ptr)
|
||||
}
|
||||
|
||||
@@ -883,18 +822,6 @@ impl Vm {
|
||||
unsafe { &*val.as_bigint_ptr() }
|
||||
}
|
||||
|
||||
/// 释放全部 session 堆 BigInt box。仅在完全隔离重置(`full_reset`)时调用,
|
||||
/// 此时没有存活的 session 对象/寄存器会引用它们。
|
||||
fn free_session_bigint_heap_data(&mut self) {
|
||||
for ptr in self.realm.gc.session_bigint_ptrs.borrow_mut().drain(..) {
|
||||
// SAFETY: 每个指针来自 new_bigint 的 Box::into_raw(Box::new(BigInt)),
|
||||
// 且只在这里恰好释放一次。
|
||||
unsafe {
|
||||
drop(Box::from_raw(ptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_constant(&self, constant: &Constant) -> JsValue {
|
||||
match constant {
|
||||
Constant::Number(v) => JsValue::float(*v),
|
||||
|
||||
@@ -6,7 +6,8 @@ fn global_prop(vm: &Vm, name: &str) -> JsValue {
|
||||
}
|
||||
|
||||
fn global_prop_opt(vm: &Vm, name: &str) -> Option<JsValue> {
|
||||
let global = vm.realm.session.global_object();
|
||||
let session = vm.realm.session.borrow();
|
||||
let global = session.global_object();
|
||||
let si = vm.kernel_core.perm_interner().intern(name).0;
|
||||
vm.kernel_core
|
||||
.shape_forge()
|
||||
@@ -68,17 +69,17 @@ fn full_reset_with_session_objects_forces_global_rebuild() {
|
||||
unsafe { (&*written.as_js_object_ptr()).is_session_epoch() },
|
||||
"覆盖值应为 session 对象(统一入口 Box 化)"
|
||||
);
|
||||
let old_global = vm.realm.session.global_object.as_ptr();
|
||||
let old_global = vm.realm.session.borrow().global_object.as_ptr();
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
// global 必须重建:旧 global 与其 session 子引用随 session 释放,Array 恢复内置构造器。
|
||||
assert!(!std::ptr::eq(old_global, vm.realm.session.global_object.as_ptr()));
|
||||
assert!(!std::ptr::eq(old_global, vm.realm.session.borrow().global_object.as_ptr()));
|
||||
assert!(std::ptr::eq(
|
||||
global_prop(&vm, "Array").as_js_object_ptr(),
|
||||
vm.realm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject
|
||||
vm.realm.session.borrow().builtin_world().array_constructor.as_ptr() as *mut JsObject
|
||||
));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
/// P 原型上方法槽的 wrapper 对象原始指针(rebuild 跨轮复用验证用)。
|
||||
@@ -98,7 +99,7 @@ fn method_wrapper_ptr(vm: &Vm, proto_ptr: *const JsObject, name: &str) -> *const
|
||||
|
||||
/// global 属性槽数(槽位原位更新跨轮不追加验证用)。
|
||||
fn global_slot_count(vm: &Vm) -> usize {
|
||||
let g = vm.realm.session.global_object.as_ptr() as *mut JsObject;
|
||||
let g = vm.realm.session.borrow().global_object.as_ptr() as *mut JsObject;
|
||||
// SAFETY: global 是本 session 对象,安全点内无并发读者。
|
||||
unsafe { (*g).prop_vec_len() }
|
||||
}
|
||||
@@ -110,9 +111,9 @@ fn global_slot_count(vm: &Vm) -> usize {
|
||||
fn full_reset_rebuild_reuses_method_wrappers_across_rounds() {
|
||||
let mut vm = Vm::new();
|
||||
let _ = run_source(&mut vm, "0");
|
||||
let push_before = method_wrapper_ptr(&vm, vm.realm.session.builtin_world().array_proto.as_ptr(), "push");
|
||||
let push_before = method_wrapper_ptr(&vm, vm.realm.session.borrow().builtin_world().array_proto.as_ptr(), "push");
|
||||
let push_shape_before = unsafe { &*push_before }.shape_id();
|
||||
let registry_before = vm.realm.session.builtin_world().leaked_object_count();
|
||||
let registry_before = vm.realm.session.borrow().builtin_world().leaked_object_count();
|
||||
let slots_before = global_slot_count(&vm);
|
||||
|
||||
for i in 0..3u32 {
|
||||
@@ -126,10 +127,10 @@ fn full_reset_rebuild_reuses_method_wrappers_across_rounds() {
|
||||
vm.full_reset();
|
||||
}
|
||||
|
||||
let push_after = method_wrapper_ptr(&vm, vm.realm.session.builtin_world().array_proto.as_ptr(), "push");
|
||||
let push_after = method_wrapper_ptr(&vm, vm.realm.session.borrow().builtin_world().array_proto.as_ptr(), "push");
|
||||
assert!(std::ptr::eq(push_before, push_after), "存活方法 wrapper 应复用(同 raw 指针)");
|
||||
assert_eq!(push_shape_before, unsafe { &*push_after }.shape_id(), "wrapper shape 应稳定");
|
||||
let registry_after = vm.realm.session.builtin_world().leaked_object_count();
|
||||
let registry_after = vm.realm.session.borrow().builtin_world().leaked_object_count();
|
||||
assert!(
|
||||
registry_after <= registry_before,
|
||||
"释放表计数跨轮不应增长: {registry_before} -> {registry_after}"
|
||||
@@ -161,7 +162,7 @@ fn full_reset_rebuild_keeps_global_slot_count_flat() {
|
||||
// Error 槽应指向本轮重建的构造器(非滞留旧指针)。
|
||||
assert!(std::ptr::eq(
|
||||
global_prop(&vm, "Error").as_js_object_ptr(),
|
||||
vm.realm.session.builtin_world().error_constructor.as_ptr() as *mut JsObject
|
||||
vm.realm.session.borrow().builtin_world().error_constructor.as_ptr() as *mut JsObject
|
||||
));
|
||||
assert!(global_prop(&vm, "TypeError").is_object());
|
||||
assert_eq!(run_source(&mut vm, "new TypeError('x') instanceof TypeError"), JsValue::bool(true));
|
||||
@@ -279,74 +280,75 @@ fn generator_captured_upvalue_survives_sweep() {
|
||||
#[test]
|
||||
fn full_reset_clean_keeps_session_objects() {
|
||||
let mut vm = Vm::new();
|
||||
let world_ptr = Arc::as_ptr(&vm.realm.session.builtin_world);
|
||||
let global_ptr = vm.realm.session.global_object.as_ptr();
|
||||
let object_proto_ptr = vm.realm.session.builtin_world().object_proto.as_ptr();
|
||||
let world_ptr = Arc::as_ptr(&vm.realm.session.borrow().builtin_world);
|
||||
let global_ptr = vm.realm.session.borrow().global_object.as_ptr();
|
||||
let object_proto_ptr = vm.realm.session.borrow().builtin_world().object_proto.as_ptr();
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.builtin_world)));
|
||||
assert!(std::ptr::eq(global_ptr, vm.realm.session.global_object.as_ptr()));
|
||||
assert!(std::ptr::eq(object_proto_ptr, vm.realm.session.builtin_world().object_proto.as_ptr()));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.borrow().builtin_world)));
|
||||
assert!(std::ptr::eq(global_ptr, vm.realm.session.borrow().global_object.as_ptr()));
|
||||
assert!(std::ptr::eq(object_proto_ptr, vm.realm.session.borrow().builtin_world().object_proto.as_ptr()));
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_reset_global_dirty_rebuilds_global_and_restores_slots() {
|
||||
let mut vm = Vm::new();
|
||||
let world_ptr = Arc::as_ptr(&vm.realm.session.builtin_world);
|
||||
let global_ptr = vm.realm.session.global_object.as_ptr();
|
||||
let global = unsafe { &mut *(vm.realm.session.global_object.as_ptr() as *mut JsObject) };
|
||||
let world_ptr = Arc::as_ptr(&vm.realm.session.borrow().builtin_world);
|
||||
let global_ptr = vm.realm.session.borrow().global_object.as_ptr();
|
||||
let global = unsafe { &mut *(vm.realm.session.borrow().global_object.as_ptr() as *mut JsObject) };
|
||||
bindings::bind_global_value(&vm.kernel_core, global, "userGlobal", JsValue::int(99));
|
||||
unsafe { &mut *(vm.realm.session.global_object.as_ptr() as *mut JsObject) }.bump_generation();
|
||||
unsafe { &mut *(vm.realm.session.borrow().global_object.as_ptr() as *mut JsObject) }.bump_generation();
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.builtin_world)));
|
||||
assert!(!std::ptr::eq(global_ptr, vm.realm.session.global_object.as_ptr()));
|
||||
assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.borrow().builtin_world)));
|
||||
assert!(!std::ptr::eq(global_ptr, vm.realm.session.borrow().global_object.as_ptr()));
|
||||
assert!(global_prop_opt(&vm, "userGlobal").is_none());
|
||||
assert!(std::ptr::eq(
|
||||
global_prop(&vm, "Array").as_js_object_ptr(),
|
||||
vm.realm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject
|
||||
vm.realm.session.borrow().builtin_world().array_constructor.as_ptr() as *mut JsObject
|
||||
));
|
||||
assert!(std::ptr::eq(
|
||||
global_prop(&vm, "globalThis").as_js_object_ptr(),
|
||||
vm.realm.session.global_object.as_ptr() as *mut JsObject
|
||||
vm.realm.session.borrow().global_object.as_ptr() as *mut JsObject
|
||||
));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_reset_dirty_builtin_rebinds_global_slot() {
|
||||
let mut vm = Vm::new();
|
||||
let old_object_proto = vm.realm.session.builtin_world().object_proto.as_ptr();
|
||||
let old_array_proto = vm.realm.session.builtin_world().array_proto.as_ptr();
|
||||
let old_object_proto = vm.realm.session.borrow().builtin_world().object_proto.as_ptr();
|
||||
let old_array_proto = vm.realm.session.borrow().builtin_world().array_proto.as_ptr();
|
||||
unsafe { &mut *(old_array_proto as *mut JsObject) }.bump_generation();
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
assert!(std::ptr::eq(old_object_proto, vm.realm.session.builtin_world().object_proto.as_ptr()));
|
||||
assert!(!std::ptr::eq(old_array_proto, vm.realm.session.builtin_world().array_proto.as_ptr()));
|
||||
assert!(std::ptr::eq(old_object_proto, vm.realm.session.borrow().builtin_world().object_proto.as_ptr()));
|
||||
assert!(!std::ptr::eq(old_array_proto, vm.realm.session.borrow().builtin_world().array_proto.as_ptr()));
|
||||
assert!(std::ptr::eq(
|
||||
global_prop(&vm, "Array").as_js_object_ptr(),
|
||||
vm.realm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject
|
||||
vm.realm.session.borrow().builtin_world().array_constructor.as_ptr() as *mut JsObject
|
||||
));
|
||||
let constructor_si = vm.kernel_core.perm_interner().intern("constructor").0;
|
||||
let array_proto = &*vm.realm.session.builtin_world().array_proto;
|
||||
let session = vm.realm.session.borrow();
|
||||
let array_proto = &*session.builtin_world().array_proto;
|
||||
let constructor = vm
|
||||
.resolve_property(array_proto, constructor_si)
|
||||
.expect("Array.prototype.constructor");
|
||||
assert!(std::ptr::eq(
|
||||
constructor.as_js_object_ptr(),
|
||||
vm.realm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject
|
||||
vm.realm.session.borrow().builtin_world().array_constructor.as_ptr() as *mut JsObject
|
||||
));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_reset_dirty_function_keeps_call_working() {
|
||||
let mut vm = Vm::new();
|
||||
let function_proto = vm.realm.session.builtin_world().function_proto.as_ptr();
|
||||
let function_proto = vm.realm.session.borrow().builtin_world().function_proto.as_ptr();
|
||||
unsafe { &mut *(function_proto as *mut JsObject) }.bump_generation();
|
||||
|
||||
vm.full_reset();
|
||||
@@ -366,7 +368,7 @@ fn full_reset_dirty_function_keeps_call_working() {
|
||||
assert_eq!(in_map, JsValue::bool(true));
|
||||
let mapped = run_source(&mut vm, "Array.prototype.map.call([1, 2], function(x){ return x + 1; }).join(',')");
|
||||
assert_eq!(vm.lookup_str(mapped).as_deref(), Some("2,3"));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
/// Function 家族脏重建:未重建家族的方法 wrapper 是跨重置存活对象,其 proto
|
||||
@@ -374,9 +376,9 @@ fn full_reset_dirty_function_keeps_call_working() {
|
||||
#[test]
|
||||
fn full_reset_dirty_function_repoints_retained_wrapper_proto() {
|
||||
let mut vm = Vm::new();
|
||||
let old_fn_proto = vm.realm.session.builtin_world().function_proto.as_ptr() as *mut JsObject;
|
||||
let old_fn_proto = vm.realm.session.borrow().builtin_world().function_proto.as_ptr() as *mut JsObject;
|
||||
// 保留方法 wrapper:array 家族不重建,wrapper 对象与其 proto 槽跨重置存活。
|
||||
let array_proto = vm.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let array_proto = vm.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let push_si = vm.kernel_core.perm_interner().intern("push").0;
|
||||
let push = vm
|
||||
.resolve_property(unsafe { &*array_proto }, push_si)
|
||||
@@ -388,12 +390,12 @@ fn full_reset_dirty_function_repoints_retained_wrapper_proto() {
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
let new_fn_proto = vm.realm.session.builtin_world().function_proto.as_ptr() as *mut JsObject;
|
||||
let new_fn_proto = vm.realm.session.borrow().builtin_world().function_proto.as_ptr() as *mut JsObject;
|
||||
assert!(!std::ptr::eq(new_fn_proto, old_fn_proto));
|
||||
// 保留 wrapper proto 槽已重指新 fn_proto,call 链走新原型。
|
||||
assert!(std::ptr::eq(unsafe { (*push_ptr).proto().as_js_object_ptr() }, new_fn_proto));
|
||||
assert_eq!(run_source(&mut vm, "Array.prototype.push.call([1], 2)"), JsValue::int(2));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
/// 新键写脏 object/array/string/function 四家族(S2 脏源形态):full_reset 后
|
||||
@@ -406,7 +408,7 @@ fn full_reset_dirty_four_families_cross_family_reads() {
|
||||
&mut vm,
|
||||
"Object.prototype['d'] = 1; Array.prototype['d'] = 2; String.prototype['d'] = 3; Function.prototype['d'] = 4;",
|
||||
);
|
||||
assert!(vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
@@ -420,7 +422,7 @@ fn full_reset_dirty_four_families_cross_family_reads() {
|
||||
JsValue::bool(true)
|
||||
);
|
||||
assert_eq!(run_source(&mut vm, "Array.prototype.constructor === Array"), JsValue::bool(true));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
/// 未捕获异常侧通道是执行期状态:原生错误后可能残留原值,reset/full_reset
|
||||
@@ -592,32 +594,32 @@ fn dynamic_function_rethrows_to_string_exception() {
|
||||
#[test]
|
||||
fn full_reset_refreshes_object_prototype_after_object_dirty() {
|
||||
let mut vm = Vm::new();
|
||||
let old_object_proto = vm.realm.session.builtin_world().object_proto.as_ptr();
|
||||
let old_object_proto = vm.realm.session.borrow().builtin_world().object_proto.as_ptr();
|
||||
unsafe { &mut *(old_object_proto as *mut JsObject) }.bump_generation();
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
assert!(!std::ptr::eq(old_object_proto, vm.realm.session.builtin_world().object_proto.as_ptr()));
|
||||
assert!(!std::ptr::eq(old_object_proto, vm.realm.session.borrow().builtin_world().object_proto.as_ptr()));
|
||||
assert!(std::ptr::eq(
|
||||
vm.realm.object_prototype.as_ptr(),
|
||||
vm.realm.session.builtin_world().object_proto.as_ptr()
|
||||
vm.realm.object_prototype.borrow().as_ptr(),
|
||||
vm.realm.session.borrow().builtin_world().object_proto.as_ptr()
|
||||
));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_reset_object_dirty_rebinds_iterator_family() {
|
||||
let mut vm = Vm::new();
|
||||
let old_object_proto = vm.realm.session.builtin_world().object_proto.as_ptr();
|
||||
let old_iterator_proto = vm.realm.session.builtin_world().iterator_proto.as_ptr();
|
||||
let old_object_proto = vm.realm.session.borrow().builtin_world().object_proto.as_ptr();
|
||||
let old_iterator_proto = vm.realm.session.borrow().builtin_world().iterator_proto.as_ptr();
|
||||
// 用户修改 Object.prototype:object 家族世代递增,global 未动。
|
||||
unsafe { &mut *(old_object_proto as *mut JsObject) }.bump_generation();
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
// object 家族与迭代器原型全部重建(新原型链到新 Object.prototype)。
|
||||
assert!(!std::ptr::eq(old_object_proto, vm.realm.session.builtin_world().object_proto.as_ptr()));
|
||||
assert!(!std::ptr::eq(old_iterator_proto, vm.realm.session.builtin_world().iterator_proto.as_ptr()));
|
||||
assert!(!std::ptr::eq(old_object_proto, vm.realm.session.borrow().builtin_world().object_proto.as_ptr()));
|
||||
assert!(!std::ptr::eq(old_iterator_proto, vm.realm.session.borrow().builtin_world().iterator_proto.as_ptr()));
|
||||
// global 保留(dirty.global=false):其 Iterator 函数对象的 prototype
|
||||
// 属性须对齐到重建后的 %IteratorPrototype%。
|
||||
let iter_val = global_prop(&vm, "Iterator");
|
||||
@@ -630,10 +632,10 @@ fn full_reset_object_dirty_rebinds_iterator_family() {
|
||||
.expect("Iterator should have prototype slot");
|
||||
assert!(std::ptr::eq(
|
||||
iter_obj.get_prop_at(proto_pos).as_js_object_ptr(),
|
||||
vm.realm.session.builtin_world().iterator_proto.as_ptr() as *mut JsObject
|
||||
vm.realm.session.borrow().builtin_world().iterator_proto.as_ptr() as *mut JsObject
|
||||
));
|
||||
// full_reset 后 session 干净;此后 run_source 执行才重新累积世代变化。
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
// 迭代器家族功能完整:原型 next 就位,for-of/spread/Array.from/Iterator.from/
|
||||
// Map/Set/String/yield* 全部可用,原型链与 Iterator.prototype 一致。
|
||||
let r = run_source(&mut vm, "[...[1,2,3]].join(',')");
|
||||
@@ -662,19 +664,19 @@ fn full_reset_object_dirty_rebinds_iterator_family() {
|
||||
#[test]
|
||||
fn full_reset_global_dirty_keeps_iterator_proto_slots_stable() {
|
||||
let mut vm = Vm::new();
|
||||
let arr_iter_proto = vm.realm.session.builtin_world().array_iterator_proto.as_ptr() as *mut JsObject;
|
||||
let arr_iter_proto = vm.realm.session.borrow().builtin_world().array_iterator_proto.as_ptr() as *mut JsObject;
|
||||
let slots_before = unsafe { &*arr_iter_proto }.hash_props_vec().map_or(0, |v| v.len());
|
||||
// global 世代递增(用户写 global),builtin 家族未动。
|
||||
unsafe { &mut *(vm.realm.session.global_object.as_ptr() as *mut JsObject) }.bump_generation();
|
||||
unsafe { &mut *(vm.realm.session.borrow().global_object.as_ptr() as *mut JsObject) }.bump_generation();
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
// builtin 未脏 → 迭代器原型保留原对象且属性槽不膨胀(重复 full_reset 不再追加)。
|
||||
let arr_iter_proto_after = vm.realm.session.builtin_world().array_iterator_proto.as_ptr() as *mut JsObject;
|
||||
let arr_iter_proto_after = vm.realm.session.borrow().builtin_world().array_iterator_proto.as_ptr() as *mut JsObject;
|
||||
assert!(std::ptr::eq(arr_iter_proto, arr_iter_proto_after));
|
||||
let slots_after = unsafe { &*arr_iter_proto_after }.hash_props_vec().map_or(0, |v| v.len());
|
||||
assert_eq!(slots_before, slots_after);
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
// 迭代器功能经保留原型仍完整(run_source 起再次累积世代变化)。
|
||||
let r = run_source(&mut vm, "[...[1,2,3]].join(',')");
|
||||
assert_eq!(vm.lookup_str(r).as_deref(), Some("1,2,3"));
|
||||
@@ -862,19 +864,19 @@ fn inline_for_in_escape_restores_snapshot_and_releases_residual_body() {
|
||||
fn full_reset_value_overwrite_on_builtin_proto_rebuilds_family() {
|
||||
let mut vm = Vm::new();
|
||||
let _ = run_source(&mut vm, "0");
|
||||
let old_array_proto = vm.realm.session.builtin_world().array_proto.as_ptr();
|
||||
let old_array_proto = vm.realm.session.borrow().builtin_world().array_proto.as_ptr();
|
||||
|
||||
assert_eq!(run_source(&mut vm, "Array.prototype.push = 9"), JsValue::int(9));
|
||||
assert_eq!(run_source(&mut vm, "Array.prototype.push"), JsValue::int(9));
|
||||
assert!(vm.realm.session.is_dirty_since_snapshot(), "值覆盖应推进世代被脏检测捕获");
|
||||
assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "值覆盖应推进世代被脏检测捕获");
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
assert!(!std::ptr::eq(old_array_proto, vm.realm.session.builtin_world().array_proto.as_ptr()));
|
||||
assert!(!std::ptr::eq(old_array_proto, vm.realm.session.borrow().builtin_world().array_proto.as_ptr()));
|
||||
let t = run_source(&mut vm, "typeof Array.prototype.push");
|
||||
assert_eq!(vm.lookup_str(t).as_deref(), Some("function"));
|
||||
assert_eq!(run_source(&mut vm, "[1, 2].push(3)"), JsValue::int(3));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
/// 对象值形态的既有槽覆盖同样须被发现:epoch 对象写入 builtin P 对象不被晋升,
|
||||
@@ -883,17 +885,17 @@ fn full_reset_value_overwrite_on_builtin_proto_rebuilds_family() {
|
||||
fn full_reset_value_overwrite_with_object_value_drops_stale_pointer() {
|
||||
let mut vm = Vm::new();
|
||||
let _ = run_source(&mut vm, "0");
|
||||
let old_array_proto = vm.realm.session.builtin_world().array_proto.as_ptr();
|
||||
let old_array_proto = vm.realm.session.borrow().builtin_world().array_proto.as_ptr();
|
||||
|
||||
let _ = run_source(&mut vm, "Array.prototype.push = {}; 0");
|
||||
assert!(vm.realm.session.is_dirty_since_snapshot(), "对象值覆盖应被脏检测捕获");
|
||||
assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "对象值覆盖应被脏检测捕获");
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
assert!(!std::ptr::eq(old_array_proto, vm.realm.session.builtin_world().array_proto.as_ptr()));
|
||||
assert!(!std::ptr::eq(old_array_proto, vm.realm.session.borrow().builtin_world().array_proto.as_ptr()));
|
||||
let t = run_source(&mut vm, "typeof Array.prototype.push");
|
||||
assert_eq!(vm.lookup_str(t).as_deref(), Some("function"));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
/// 零 session 对象的全局内置值覆盖:`globalThis.Array = 9` 不触发 promote,
|
||||
@@ -905,17 +907,17 @@ fn full_reset_global_builtin_value_overwrite_restores_constructor() {
|
||||
let _ = run_source(&mut vm, "0");
|
||||
|
||||
let _ = run_source(&mut vm, "globalThis.Array = 9; 0");
|
||||
assert!(vm.realm.gc.session_object_ptrs.is_empty(), "原始值覆盖不产生 session 对象");
|
||||
assert!(vm.realm.session.is_dirty_since_snapshot(), "全局值覆盖应推进世代");
|
||||
assert!(vm.realm.gc.borrow().session_object_ptrs.is_empty(), "原始值覆盖不产生 session 对象");
|
||||
assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "全局值覆盖应推进世代");
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
assert!(std::ptr::eq(
|
||||
global_prop(&vm, "Array").as_js_object_ptr(),
|
||||
vm.realm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject
|
||||
vm.realm.session.borrow().builtin_world().array_constructor.as_ptr() as *mut JsObject
|
||||
));
|
||||
assert_eq!(run_source(&mut vm, "[1].push(2)"), JsValue::int(2));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
/// IC 二次写(miss 落既有槽 + 命中直写)与复合赋值(set_member_prop 既有槽)
|
||||
@@ -927,19 +929,19 @@ fn full_reset_value_overwrite_via_ic_and_compound_marks_dirty() {
|
||||
|
||||
// IC 首写 miss 落既有槽、二次写经缓存命中,两次都须 bump。
|
||||
let _ = run_source(&mut vm, "Array.prototype.push = 9; Array.prototype.push = 8; 0");
|
||||
assert!(vm.realm.session.is_dirty_since_snapshot(), "IC 二次写应被脏检测捕获");
|
||||
assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "IC 二次写应被脏检测捕获");
|
||||
vm.full_reset();
|
||||
let t = run_source(&mut vm, "typeof Array.prototype.push");
|
||||
assert_eq!(vm.lookup_str(t).as_deref(), Some("function"));
|
||||
|
||||
// 复合赋值读改写走 set_member_prop 既有槽路径,同样须 bump。
|
||||
let _ = run_source(&mut vm, "Array.prototype.push = 9; Array.prototype.push += 1; 0");
|
||||
assert!(vm.realm.session.is_dirty_since_snapshot(), "复合赋值应被脏检测捕获");
|
||||
assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "复合赋值应被脏检测捕获");
|
||||
vm.full_reset();
|
||||
let t = run_source(&mut vm, "typeof Array.prototype.push");
|
||||
assert_eq!(vm.lookup_str(t).as_deref(), Some("function"));
|
||||
assert_eq!(run_source(&mut vm, "[1, 2, 3].push(4)"), JsValue::int(4));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
/// wrapper 本体新增 own 属性:写入的是方法 wrapper 自身而非所属 P 对象,
|
||||
@@ -949,20 +951,20 @@ fn full_reset_value_overwrite_via_ic_and_compound_marks_dirty() {
|
||||
fn full_reset_wrapper_own_property_does_not_survive() {
|
||||
let mut vm = Vm::new();
|
||||
let _ = run_source(&mut vm, "0");
|
||||
let push_before = method_wrapper_ptr(&vm, vm.realm.session.builtin_world().array_proto.as_ptr(), "push");
|
||||
let push_before = method_wrapper_ptr(&vm, vm.realm.session.borrow().builtin_world().array_proto.as_ptr(), "push");
|
||||
|
||||
let _ = run_source(&mut vm, "Array.prototype.push.custom = 1; 0");
|
||||
assert!(vm.realm.session.is_dirty_since_snapshot(), "wrapper 本体写应被脏检测捕获");
|
||||
assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "wrapper 本体写应被脏检测捕获");
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
assert_eq!(run_source(&mut vm, "Array.prototype.push.custom"), JsValue::undefined());
|
||||
assert_eq!(run_source(&mut vm, "'custom' in Array.prototype.push"), JsValue::bool(false));
|
||||
assert_eq!(run_source(&mut vm, "typeof Array.prototype.push === 'function'"), JsValue::bool(true));
|
||||
let push_after = method_wrapper_ptr(&vm, vm.realm.session.builtin_world().array_proto.as_ptr(), "push");
|
||||
let push_after = method_wrapper_ptr(&vm, vm.realm.session.borrow().builtin_world().array_proto.as_ptr(), "push");
|
||||
assert!(!std::ptr::eq(push_before, push_after), "被写的 wrapper 不应被复用");
|
||||
assert_eq!(run_source(&mut vm, "[1, 2].push(3)"), JsValue::int(3));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
/// wrapper 本体写 epoch 对象值:epoch 对象不晋升、不登记,若 wrapper 跨轮
|
||||
@@ -973,13 +975,13 @@ fn full_reset_wrapper_object_property_does_not_dangle() {
|
||||
let _ = run_source(&mut vm, "0");
|
||||
|
||||
let _ = run_source(&mut vm, "Array.prototype.push.custom = {}; 0");
|
||||
assert!(vm.realm.session.is_dirty_since_snapshot(), "wrapper 对象值写应被脏检测捕获");
|
||||
assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "wrapper 对象值写应被脏检测捕获");
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
assert_eq!(run_source(&mut vm, "Array.prototype.push.custom"), JsValue::undefined());
|
||||
assert_eq!(run_source(&mut vm, "[1, 2].push(3)"), JsValue::int(3));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
/// wrapper 既有 own 槽的值覆盖:首次写已建槽后重录快照,第二次写走
|
||||
@@ -989,16 +991,16 @@ fn full_reset_wrapper_existing_slot_overwrite_marks_dirty() {
|
||||
let mut vm = Vm::new();
|
||||
let _ = run_source(&mut vm, "0");
|
||||
let _ = run_source(&mut vm, "Array.prototype.push.custom = 1; 0");
|
||||
vm.realm.session.record_snapshot();
|
||||
vm.realm.session.borrow_mut().record_snapshot();
|
||||
|
||||
let _ = run_source(&mut vm, "Array.prototype.push.custom = 2; 0");
|
||||
assert!(vm.realm.session.is_dirty_since_snapshot(), "wrapper 既有槽覆盖应被脏检测捕获");
|
||||
assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "wrapper 既有槽覆盖应被脏检测捕获");
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
assert_eq!(run_source(&mut vm, "Array.prototype.push.custom"), JsValue::undefined());
|
||||
assert_eq!(run_source(&mut vm, "typeof Array.prototype.push === 'function'"), JsValue::bool(true));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
/// 不可复用宿主对象(`Reflect`)的属性写:登记面无复用键,脏时靠 global
|
||||
@@ -1009,13 +1011,13 @@ fn full_reset_keyless_leaked_object_property_does_not_survive() {
|
||||
let _ = run_source(&mut vm, "0");
|
||||
|
||||
let _ = run_source(&mut vm, "Reflect.custom = 1; 0");
|
||||
assert!(vm.realm.session.is_dirty_since_snapshot(), "keyless 宿主对象写应被脏检测捕获");
|
||||
assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "keyless 宿主对象写应被脏检测捕获");
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
assert_eq!(run_source(&mut vm, "'custom' in Reflect"), JsValue::bool(false));
|
||||
assert_eq!(run_source(&mut vm, "typeof Reflect.apply === 'function'"), JsValue::bool(true));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
/// global 函数 wrapper(`parseInt`)的属性写:global 函数 wrapper 无 P 家族
|
||||
@@ -1026,13 +1028,13 @@ fn full_reset_global_function_wrapper_property_does_not_survive() {
|
||||
let _ = run_source(&mut vm, "0");
|
||||
|
||||
let _ = run_source(&mut vm, "parseInt.custom = 1; 0");
|
||||
assert!(vm.realm.session.is_dirty_since_snapshot(), "global 函数 wrapper 写应被脏检测捕获");
|
||||
assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "global 函数 wrapper 写应被脏检测捕获");
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
assert_eq!(run_source(&mut vm, "'custom' in parseInt"), JsValue::bool(false));
|
||||
assert_eq!(run_source(&mut vm, "parseInt('42')"), JsValue::int(42));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
/// wrapper 写重建后基线已刷新:后续仅 P 对象脏写的轮次复用新 wrapper、
|
||||
@@ -1044,8 +1046,8 @@ fn wrapper_write_rebuild_then_p_writes_reuse_fresh_wrapper() {
|
||||
let _ = run_source(&mut vm, "Array.prototype.push.custom = 1; 0");
|
||||
vm.full_reset();
|
||||
|
||||
let push_after = method_wrapper_ptr(&vm, vm.realm.session.builtin_world().array_proto.as_ptr(), "push");
|
||||
let registry_after = vm.realm.session.builtin_world().leaked_object_count();
|
||||
let push_after = method_wrapper_ptr(&vm, vm.realm.session.borrow().builtin_world().array_proto.as_ptr(), "push");
|
||||
let registry_after = vm.realm.session.borrow().builtin_world().leaked_object_count();
|
||||
|
||||
for i in 0..3u32 {
|
||||
let source = format!("Array.prototype['w{i}'] = 1; Object.prototype['w{i}'] = 2; 0");
|
||||
@@ -1053,15 +1055,15 @@ fn wrapper_write_rebuild_then_p_writes_reuse_fresh_wrapper() {
|
||||
vm.full_reset();
|
||||
}
|
||||
|
||||
let push_final = method_wrapper_ptr(&vm, vm.realm.session.builtin_world().array_proto.as_ptr(), "push");
|
||||
let push_final = method_wrapper_ptr(&vm, vm.realm.session.borrow().builtin_world().array_proto.as_ptr(), "push");
|
||||
assert!(std::ptr::eq(push_after, push_final), "P 写轮应复用 wrapper,不应每轮失效重建");
|
||||
assert_eq!(
|
||||
vm.realm.session.builtin_world().leaked_object_count(),
|
||||
vm.realm.session.borrow().builtin_world().leaked_object_count(),
|
||||
registry_after,
|
||||
"P 写轮释放登记表不应增长"
|
||||
);
|
||||
assert_eq!(run_source(&mut vm, "[1, 2].push(3)"), JsValue::int(3));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
/// 假阳性护栏:只写用户对象的良性运行不得误判 wrapper 脏——wrapper 世代
|
||||
@@ -1070,16 +1072,16 @@ fn wrapper_write_rebuild_then_p_writes_reuse_fresh_wrapper() {
|
||||
fn benign_user_object_writes_do_not_dirty_leaked_objects() {
|
||||
let mut vm = Vm::new();
|
||||
let _ = run_source(&mut vm, "0");
|
||||
let world_ptr = Arc::as_ptr(&vm.realm.session.builtin_world);
|
||||
let world_ptr = Arc::as_ptr(&vm.realm.session.borrow().builtin_world);
|
||||
|
||||
let result = run_source(&mut vm, "(function () { var a = {}; a.x = 1; a.x = 2; return a.x; })()");
|
||||
assert_eq!(result, JsValue::int(2));
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot(), "只写用户对象不应误判 wrapper 脏");
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot(), "只写用户对象不应误判 wrapper 脏");
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.builtin_world)), "无污染不应重建 world");
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.borrow().builtin_world)), "无污染不应重建 world");
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
/// 假阳性护栏:只写用户对象的良性运行不得误判 builtin 脏。
|
||||
@@ -1091,16 +1093,16 @@ fn benign_user_object_writes_do_not_dirty_leaked_objects() {
|
||||
fn benign_run_does_not_falsely_dirty_builtins() {
|
||||
let mut vm = Vm::new();
|
||||
let _ = run_source(&mut vm, "0");
|
||||
let world_ptr = Arc::as_ptr(&vm.realm.session.builtin_world);
|
||||
let world_ptr = Arc::as_ptr(&vm.realm.session.borrow().builtin_world);
|
||||
|
||||
run_source(&mut vm, "({ x: 1 }).x + [1, 2].length");
|
||||
assert_eq!(vm.session_object_count(), 2, "良性表达式应产生两枚临时对象(对象字面量 + 数组字面量)");
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot(), "良性运行不应误判 builtin 脏");
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot(), "良性运行不应误判 builtin 脏");
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.builtin_world)), "builtin world 不应重建");
|
||||
assert!(!vm.realm.session.is_dirty_since_snapshot());
|
||||
assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.borrow().builtin_world)), "builtin world 不应重建");
|
||||
assert!(!vm.realm.session.borrow().is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -160,7 +160,8 @@ fn error_subtype_constructors_produce_named_objects() {
|
||||
#[test]
|
||||
fn error_prototype_has_name() {
|
||||
let vm = make_vm();
|
||||
let bw = vm.session().builtin_world();
|
||||
let session = vm.session();
|
||||
let bw = session.builtin_world();
|
||||
let name_si = vm.kernel_core().perm_interner().intern("name").0;
|
||||
let proto_ptr = P::as_ptr(&bw.error_proto) as *mut oxide_types::object::JsObject;
|
||||
let si = vm
|
||||
@@ -173,7 +174,8 @@ fn error_prototype_has_name() {
|
||||
#[test]
|
||||
fn error_prototype_has_message() {
|
||||
let vm = make_vm();
|
||||
let bw = vm.session().builtin_world();
|
||||
let session = vm.session();
|
||||
let bw = session.builtin_world();
|
||||
let msg_si = vm.kernel_core().perm_interner().intern("message").0;
|
||||
let proto_ptr = P::as_ptr(&bw.error_proto) as *mut oxide_types::object::JsObject;
|
||||
let si = vm
|
||||
@@ -186,7 +188,8 @@ fn error_prototype_has_message() {
|
||||
#[test]
|
||||
fn type_error_prototype_name() {
|
||||
let vm = make_vm();
|
||||
let bw = vm.session().builtin_world();
|
||||
let session = vm.session();
|
||||
let bw = session.builtin_world();
|
||||
let name_si = vm.kernel_core().perm_interner().intern("name").0;
|
||||
let proto_ptr = P::as_ptr(&bw.type_error_proto) as *mut oxide_types::object::JsObject;
|
||||
let si = vm
|
||||
@@ -199,7 +202,8 @@ fn type_error_prototype_name() {
|
||||
#[test]
|
||||
fn reference_error_prototype_name() {
|
||||
let vm = make_vm();
|
||||
let bw = vm.session().builtin_world();
|
||||
let session = vm.session();
|
||||
let bw = session.builtin_world();
|
||||
let name_si = vm.kernel_core().perm_interner().intern("name").0;
|
||||
let proto_ptr = P::as_ptr(&bw.reference_error_proto) as *mut oxide_types::object::JsObject;
|
||||
let si = vm
|
||||
@@ -212,7 +216,8 @@ fn reference_error_prototype_name() {
|
||||
#[test]
|
||||
fn range_error_prototype_name() {
|
||||
let vm = make_vm();
|
||||
let bw = vm.session().builtin_world();
|
||||
let session = vm.session();
|
||||
let bw = session.builtin_world();
|
||||
let name_si = vm.kernel_core().perm_interner().intern("name").0;
|
||||
let proto_ptr = P::as_ptr(&bw.range_error_proto) as *mut oxide_types::object::JsObject;
|
||||
let si = vm
|
||||
@@ -225,7 +230,8 @@ fn range_error_prototype_name() {
|
||||
#[test]
|
||||
fn syntax_error_prototype_name() {
|
||||
let vm = make_vm();
|
||||
let bw = vm.session().builtin_world();
|
||||
let session = vm.session();
|
||||
let bw = session.builtin_world();
|
||||
let name_si = vm.kernel_core().perm_interner().intern("name").0;
|
||||
let proto_ptr = P::as_ptr(&bw.syntax_error_proto) as *mut oxide_types::object::JsObject;
|
||||
let si = vm
|
||||
@@ -238,7 +244,8 @@ fn syntax_error_prototype_name() {
|
||||
#[test]
|
||||
fn uri_error_prototype_name() {
|
||||
let vm = make_vm();
|
||||
let bw = vm.session().builtin_world();
|
||||
let session = vm.session();
|
||||
let bw = session.builtin_world();
|
||||
let name_si = vm.kernel_core().perm_interner().intern("name").0;
|
||||
let proto_ptr = P::as_ptr(&bw.uri_error_proto) as *mut oxide_types::object::JsObject;
|
||||
let si = vm
|
||||
@@ -251,7 +258,8 @@ fn uri_error_prototype_name() {
|
||||
#[test]
|
||||
fn eval_error_prototype_name() {
|
||||
let vm = make_vm();
|
||||
let bw = vm.session().builtin_world();
|
||||
let session = vm.session();
|
||||
let bw = session.builtin_world();
|
||||
let name_si = vm.kernel_core().perm_interner().intern("name").0;
|
||||
let proto_ptr = P::as_ptr(&bw.eval_error_proto) as *mut oxide_types::object::JsObject;
|
||||
let si = vm
|
||||
|
||||
@@ -15,7 +15,8 @@ fn run_source(vm: &mut Vm, source: &str) -> JsValue {
|
||||
|
||||
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 session = vm.session();
|
||||
let global = session.global_object();
|
||||
let pos = vm
|
||||
.kernel_core()
|
||||
.shape_forge()
|
||||
|
||||
Reference in New Issue
Block a user