feat: DisposableStack 内置对象(状态盒 + use/adopt/defer/dispose/move + GC 六处接线 + 绑定三时机)
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||
use oxide_types::object::{JsObject, PropAttributes};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use crate::bindings::{
|
||||
apply_binding_table, bind_accessor_getter, bind_global_value, bind_well_known_data_property,
|
||||
bind_well_known_method_alias, configure_native_constructor,
|
||||
};
|
||||
|
||||
/// 在 `DisposableStack.prototype` 上安装方法/别名/访问器/@@toStringTag。
|
||||
///
|
||||
/// 幂等:proto 上已有 `use` 方法(全新 proto 或已完整绑定)时整组跳过,
|
||||
/// 保留原型经 dirty reset 重复经过时安全。`@@dispose` 别名须在 `dispose`
|
||||
/// 绑定之后调用(读源槽 lookup_position,顺序敏感)。
|
||||
pub fn bind_disposable_stack_protos(core: &Arc<KernelCore>, session: &KernelSession) {
|
||||
let world = session.builtin_world();
|
||||
let proto_ptr = world.disposable_stack_proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &mut *proto_ptr };
|
||||
let si_use = core.perm_interner().intern("use").0;
|
||||
if core.shape_forge().lookup_position(proto.shape_id(), si_use).is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
apply_binding_table(
|
||||
world,
|
||||
proto,
|
||||
core,
|
||||
&[
|
||||
("use", oxide_builtins::disposable_stack::disposable_stack_use::<crate::vm::Vm> as *const (), 1),
|
||||
("adopt", oxide_builtins::disposable_stack::disposable_stack_adopt::<crate::vm::Vm> as *const (), 2),
|
||||
("defer", oxide_builtins::disposable_stack::disposable_stack_defer::<crate::vm::Vm> as *const (), 1),
|
||||
("dispose", oxide_builtins::disposable_stack::disposable_stack_dispose::<crate::vm::Vm> as *const (), 0),
|
||||
("move", oxide_builtins::disposable_stack::disposable_stack_move::<crate::vm::Vm> as *const (), 0),
|
||||
],
|
||||
);
|
||||
bind_accessor_getter(
|
||||
core,
|
||||
session,
|
||||
proto,
|
||||
"disposed",
|
||||
oxide_builtins::disposable_stack::disposable_stack_disposed_getter::<crate::vm::Vm> as *const (),
|
||||
);
|
||||
// @@dispose 与 dispose 为同一函数对象(先绑 dispose 再取源槽)。
|
||||
bind_well_known_method_alias(core, proto, "dispose", 12);
|
||||
let sf = core.perm_interner().as_ref();
|
||||
let tag = JsValue::perm_string(sf.string_ptr(sf.intern("DisposableStack").0));
|
||||
bind_well_known_data_property(core, proto, 9, tag, PropAttributes::new(false, false, true));
|
||||
}
|
||||
|
||||
/// 在 proto 上写 `constructor` 数据属性(非枚举,描述符 {t,f,t})。
|
||||
fn write_proto_constructor(core: &Arc<KernelCore>, proto: &mut JsObject, ctor_val: JsValue) {
|
||||
let sf = core.perm_interner().as_ref();
|
||||
let sh = core.shape_forge().as_ref();
|
||||
let si_constructor = sf.intern("constructor").0;
|
||||
if sh.lookup_position(proto.shape_id(), si_constructor).is_some() {
|
||||
return;
|
||||
}
|
||||
let new_shape = sh.make_shape(proto.shape_id(), si_constructor);
|
||||
proto.set_shape_id(new_shape);
|
||||
let pos = proto.push_prop(ctor_val);
|
||||
proto.set_data_meta(pos, PropAttributes::new(true, false, true));
|
||||
proto.bump_generation();
|
||||
}
|
||||
|
||||
/// 把 `DisposableStack` 构造器绑定到 global(init 与 full_reset global 重建共用)。
|
||||
///
|
||||
/// proto.constructor 已有则复用(dirty reset 保留路径);否则 Box 自建构造器
|
||||
/// (proto=Function.prototype,prototype 槽=DisposableStack.prototype,描述符按规范)。
|
||||
/// 幂等:protos 安装与 global 槽写入均自带重复跳过。
|
||||
pub fn bind_disposable_stack(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||
let world = session.builtin_world();
|
||||
let proto_ptr = world.disposable_stack_proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &mut *proto_ptr };
|
||||
let si_constructor = core.perm_interner().intern("constructor").0;
|
||||
let ctor_val = match core.shape_forge().lookup_position(proto.shape_id(), si_constructor) {
|
||||
Some(pos) => {
|
||||
let existing = proto.get_prop_at(pos);
|
||||
if existing.is_object() {
|
||||
existing
|
||||
} else {
|
||||
JsValue::undefined()
|
||||
}
|
||||
}
|
||||
None => JsValue::undefined(),
|
||||
};
|
||||
let ctor_val = if ctor_val.is_object() {
|
||||
ctor_val
|
||||
} else {
|
||||
// Box 自建:[[Prototype]] 指向 Function.prototype,prototype/name 描述符按规范。
|
||||
let function_proto = world.function_proto.as_ptr() as *mut JsObject;
|
||||
let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto)));
|
||||
ctor.set_function(true);
|
||||
configure_native_constructor(
|
||||
&mut ctor,
|
||||
oxide_builtins::disposable_stack::disposable_stack_constructor::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
);
|
||||
let sf = core.perm_interner().as_ref();
|
||||
let sh = core.shape_forge().as_ref();
|
||||
let si_prototype = sf.intern("prototype").0;
|
||||
let si_name = sf.intern("name").0;
|
||||
let si_length = sf.intern("length").0;
|
||||
let ctor_shape1 = sh.make_shape(EMPTY_SHAPE_ID, si_prototype);
|
||||
let ctor_shape2 = sh.make_shape(ctor_shape1, si_name);
|
||||
let ctor_shape3 = sh.make_shape(ctor_shape2, si_length);
|
||||
ctor.set_shape_id(ctor_shape3);
|
||||
ctor.ensure_hash_props()
|
||||
.push(JsValue::from_js_object(world.disposable_stack_proto.as_ptr() as *mut JsObject));
|
||||
ctor.ensure_hash_props().push(JsValue::perm_string(sf.string_ptr(sf.intern("DisposableStack").0)));
|
||||
ctor.ensure_hash_props().push(JsValue::int(0));
|
||||
ctor.set_data_meta(0u32, PropAttributes::new(false, false, false));
|
||||
ctor.set_data_meta(1u32, PropAttributes::new(false, false, true));
|
||||
ctor.set_data_meta(2u32, PropAttributes::new(false, false, true));
|
||||
JsValue::from_js_object(Box::into_raw(ctor))
|
||||
};
|
||||
|
||||
write_proto_constructor(core, proto, ctor_val);
|
||||
bind_disposable_stack_protos(core, session);
|
||||
bind_global_value(core, global, "DisposableStack", ctor_val);
|
||||
}
|
||||
|
||||
/// 对齐保留 global 上 `DisposableStack` 函数对象的 `prototype` 属性:object 家族
|
||||
/// 重建会产生新的 `DisposableStack.prototype`,保留的构造器须指向新原型,
|
||||
/// 否则 reset 后 `instanceof` 沿旧原型链恒 false。
|
||||
///
|
||||
/// global 同时重建(global dirty)或初始化时无 `DisposableStack` 属性,直接跳过
|
||||
/// ——彼时由 `bind_disposable_stack` 以新原型创建函数对象。
|
||||
pub fn sync_disposable_stack_ctor(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||
let si_stack = core.perm_interner().intern("DisposableStack").0;
|
||||
let Some(pos) = core.shape_forge().lookup_position(global.shape_id(), si_stack) else {
|
||||
return;
|
||||
};
|
||||
let stack_val = global.get_prop_at(pos);
|
||||
if !stack_val.is_object() {
|
||||
return;
|
||||
}
|
||||
let ctor = unsafe { &mut *stack_val.as_js_object_ptr() };
|
||||
let si_prototype = core.perm_interner().intern("prototype").0;
|
||||
let Some(proto_pos) = core.shape_forge().lookup_position(ctor.shape_id(), si_prototype) else {
|
||||
return;
|
||||
};
|
||||
let new_proto = JsValue::from_js_object(session.builtin_world().disposable_stack_proto.as_ptr() as *mut JsObject);
|
||||
ctor.set_prop_at(proto_pos, new_proto);
|
||||
|
||||
let proto_ptr = session.builtin_world().disposable_stack_proto.as_ptr() as *mut JsObject;
|
||||
write_proto_constructor(core, unsafe { &mut *proto_ptr }, stack_val);
|
||||
}
|
||||
@@ -14,6 +14,8 @@ pub mod bind_boolean;
|
||||
pub mod bind_data_view;
|
||||
/// Date 构造器与原型的 native 方法绑定。
|
||||
pub mod bind_date;
|
||||
/// DisposableStack 构造器与原型的 native 方法绑定(含 dirty reset 同步)。
|
||||
pub mod bind_disposable_stack;
|
||||
/// Error 家族构造器与原型的 native 方法绑定(含各子类型构造器创建)。
|
||||
pub mod bind_error;
|
||||
/// Function 构造器与原型的 native 方法绑定。
|
||||
@@ -972,6 +974,7 @@ pub fn bind_global_builtin_slots(core: &Arc<KernelCore>, session: &KernelSession
|
||||
|
||||
bind_reflect_global(core, session, global);
|
||||
bind_iterator_global(core, session, global);
|
||||
bind_disposable_stack::bind_disposable_stack(core, session, global);
|
||||
bind_stub_globals(core, session, global);
|
||||
bind_bigint::bind_bigint(core, session, global);
|
||||
bind_global_functions(core, session, global);
|
||||
@@ -991,10 +994,12 @@ pub fn rebind_dirty_builtins(core: &Arc<KernelCore>, session: &mut KernelSession
|
||||
|
||||
if dirty.map_or(true, |d| d.object) {
|
||||
bind_object::bind_object(core, session, global);
|
||||
// object 家族重建连带重建 6 个迭代器原型(其链到新 Object.prototype),
|
||||
// 须同步安装原型方法并让保留 global 上的 Iterator 指向新 %IteratorPrototype%。
|
||||
// object 家族重建连带重建 6 个迭代器原型与两资源栈原型(其链到新
|
||||
// Object.prototype),须同步安装原型方法并让保留的 global 构造器指向新原型。
|
||||
bind_iterator_protos(core, session);
|
||||
sync_iterator_function_prototype(core, session, global);
|
||||
bind_disposable_stack::bind_disposable_stack_protos(core, session);
|
||||
bind_disposable_stack::sync_disposable_stack_ctor(core, session, global);
|
||||
}
|
||||
if dirty.map_or(true, |d| d.array) {
|
||||
bind_array::bind_array(core, session, global);
|
||||
@@ -1061,6 +1066,7 @@ pub fn init_kernel_builtins(core: &Arc<KernelCore>, session: &mut KernelSession)
|
||||
let global_ptr = session.global_object().as_ptr() as *mut oxide_types::object::JsObject;
|
||||
let global = unsafe { &mut *global_ptr };
|
||||
bind_iterator::bind_iterator(core, session, global);
|
||||
bind_disposable_stack::bind_disposable_stack(core, session, global);
|
||||
bind_reflect::bind_reflect(core, session, global);
|
||||
bind_global::bind_global(core, session, global);
|
||||
bind_global_value(core, global, "globalThis", JsValue::from_js_object(global_ptr));
|
||||
|
||||
@@ -6,7 +6,7 @@ use oxide_types::value::JsValue;
|
||||
use rustc_hash::FxBuildHasher;
|
||||
|
||||
use crate::vm::Vm;
|
||||
use oxide_builtins::{array_buffer, data_view, map, regexp, set, typed_array};
|
||||
use oxide_builtins::{array_buffer, data_view, disposable_stack, map, regexp, set, typed_array};
|
||||
|
||||
impl Vm {
|
||||
pub(crate) fn is_session_escape_root_ptr(&self, target_ptr: *mut JsObject) -> bool {
|
||||
@@ -57,6 +57,11 @@ impl Vm {
|
||||
set::clone_set_native_with_rewrite(src_ref, dst_ref, |value| {
|
||||
self.promote_value_if_epoch_object(value, forwarding)
|
||||
});
|
||||
} else if src_ref.is_disposable_stack_obj() || src_ref.is_async_disposable_stack_obj() {
|
||||
// 资源栈状态盒深拷贝到新对象:源盒随 epoch 释放,互不共享。
|
||||
disposable_stack::clone_dispose_native_with_rewrite(src_ref, dst_ref, |value| {
|
||||
self.promote_value_if_epoch_object(value, forwarding)
|
||||
});
|
||||
} else if src_ref.is_typed_array_obj() {
|
||||
typed_array::clone_typed_array_native_with_rewrite(src_ref, dst_ref, |value| {
|
||||
self.promote_value_if_epoch_object(value, forwarding)
|
||||
|
||||
@@ -8,7 +8,7 @@ use oxide_types::value::JsValue;
|
||||
use rustc_hash::FxBuildHasher;
|
||||
|
||||
use crate::vm::Vm;
|
||||
use oxide_builtins::{array_buffer, data_view, map, regexp, set, typed_array};
|
||||
use oxide_builtins::{array_buffer, data_view, disposable_stack, map, regexp, set, typed_array};
|
||||
|
||||
/// session 级 mark-sweep GC 的状态与统计。
|
||||
///
|
||||
@@ -96,6 +96,9 @@ impl SessionGc {
|
||||
if obj.is_set() {
|
||||
edges.extend(set::set_native_edges(obj));
|
||||
}
|
||||
if obj.is_disposable_stack_obj() || obj.is_async_disposable_stack_obj() {
|
||||
edges.extend(disposable_stack::dispose_edges(obj));
|
||||
}
|
||||
if obj.is_typed_array_obj() {
|
||||
edges.extend(typed_array::typed_array_native_edges(obj));
|
||||
}
|
||||
@@ -223,6 +226,13 @@ impl SessionGc {
|
||||
}
|
||||
}
|
||||
}
|
||||
if obj.is_disposable_stack_obj() || obj.is_async_disposable_stack_obj() {
|
||||
for value in disposable_stack::dispose_edges(obj) {
|
||||
if value.is_string() {
|
||||
Self::mark_string_live(live, value.as_string_ptr_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
if obj.is_generator_obj() {
|
||||
for ptr in crate::generator::generator_native_string_edges(obj) {
|
||||
Self::mark_string_live(live, ptr);
|
||||
@@ -363,6 +373,7 @@ impl SessionGc {
|
||||
|
||||
freed_bytes += map::drop_map_native(obj);
|
||||
freed_bytes += set::drop_set_native(obj);
|
||||
freed_bytes += disposable_stack::drop_dispose_native(obj);
|
||||
freed_bytes += array_buffer::drop_array_buffer_native(obj);
|
||||
freed_bytes += regexp::drop_regexp_native(obj);
|
||||
freed_bytes += typed_array::drop_typed_array_native(obj);
|
||||
@@ -422,6 +433,8 @@ impl SessionGc {
|
||||
map::clone_map_native_with_rewrite(old_ref, new_ref, |value| value);
|
||||
} else if old_ref.is_set() {
|
||||
set::clone_set_native_with_rewrite(old_ref, new_ref, |value| value);
|
||||
} else if old_ref.is_disposable_stack_obj() || old_ref.is_async_disposable_stack_obj() {
|
||||
disposable_stack::clone_dispose_native_with_rewrite(old_ref, new_ref, |value| value);
|
||||
} else if old_ref.is_array_buffer_obj() {
|
||||
array_buffer::clone_array_buffer_native(old_ref, new_ref);
|
||||
} else if old_ref.is_typed_array_obj() {
|
||||
@@ -464,6 +477,8 @@ impl SessionGc {
|
||||
map::rewrite_map_native(obj, |value| rewrite_forwarded_value(value, &forwarding));
|
||||
} else if obj.is_set() {
|
||||
set::rewrite_set_native(obj, |value| rewrite_forwarded_value(value, &forwarding));
|
||||
} else if obj.is_disposable_stack_obj() || obj.is_async_disposable_stack_obj() {
|
||||
disposable_stack::rewrite_dispose_native(obj, |value| rewrite_forwarded_value(value, &forwarding));
|
||||
} else if obj.is_typed_array_obj() {
|
||||
typed_array::rewrite_typed_array_native(obj, |value| rewrite_forwarded_value(value, &forwarding));
|
||||
} else if obj.is_data_view_obj() {
|
||||
|
||||
Reference in New Issue
Block a user