feat: 新增 Proxy 对象标签与状态盒的垃圾回收接线

This commit is contained in:
2026-10-16 19:57:30 +00:00
parent a05e5e6a6e
commit 985310d545
7 changed files with 184 additions and 27 deletions
Generated
+21 -21
View File
@@ -1160,7 +1160,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_builtins" name = "oxide_builtins"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"chrono", "chrono",
"fast-float", "fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_bytecode" name = "oxide_bytecode"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"num-bigint", "num-bigint",
"oxide_log", "oxide_log",
@@ -1193,7 +1193,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_cfg" name = "oxide_cfg"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_emit", "oxide_emit",
@@ -1204,7 +1204,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_cli" name = "oxide_cli"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"ansi_term", "ansi_term",
"bumpalo", "bumpalo",
@@ -1228,7 +1228,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_code_cache" name = "oxide_code_cache"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"lru", "lru",
"oxide_bytecode", "oxide_bytecode",
@@ -1237,7 +1237,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_compiler" name = "oxide_compiler"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1258,7 +1258,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_dce" name = "oxide_dce"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1272,7 +1272,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_emit" name = "oxide_emit"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"num-bigint", "num-bigint",
"oxc_allocator", "oxc_allocator",
@@ -1288,7 +1288,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_engine" name = "oxide_engine"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_compiler", "oxide_compiler",
@@ -1300,7 +1300,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_fold" name = "oxide_fold"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"num-bigint", "num-bigint",
"oxide_bytecode", "oxide_bytecode",
@@ -1312,7 +1312,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_ir" name = "oxide_ir"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_log", "oxide_log",
@@ -1321,7 +1321,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_kernel" name = "oxide_kernel"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"dashmap", "dashmap",
@@ -1335,7 +1335,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_liveness" name = "oxide_liveness"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1347,7 +1347,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_log" name = "oxide_log"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"tracing", "tracing",
"tracing-appender", "tracing-appender",
@@ -1356,11 +1356,11 @@ dependencies = [
[[package]] [[package]]
name = "oxide_net" name = "oxide_net"
version = "0.20.3" version = "0.20.4"
[[package]] [[package]]
name = "oxide_parser" name = "oxide_parser"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"oxc_allocator", "oxc_allocator",
"oxc_ast", "oxc_ast",
@@ -1375,7 +1375,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_regalloc" name = "oxide_regalloc"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1391,7 +1391,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_runtime_api" name = "oxide_runtime_api"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"num-bigint", "num-bigint",
"num-traits", "num-traits",
@@ -1404,7 +1404,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_test262" name = "oxide_test262"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_compiler", "oxide_compiler",
@@ -1424,7 +1424,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_types" name = "oxide_types"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"dashmap", "dashmap",
@@ -1437,7 +1437,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_vm" name = "oxide_vm"
version = "0.20.3" version = "0.20.4"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"chrono", "chrono",
+1 -1
View File
@@ -25,7 +25,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.20.3" version = "0.20.4"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
rust-version = "1.80" rust-version = "1.80"
+8
View File
@@ -240,6 +240,9 @@ impl JsObject {
pub const OBJ_TYPE_ERROR_EVENT: u8 = 37; pub const OBJ_TYPE_ERROR_EVENT: u8 = 37;
/// CustomEvent 对象:事件派生类载荷盒(detail 边)存于 `native_fn` 槽。 /// CustomEvent 对象:事件派生类载荷盒(detail 边)存于 `native_fn` 槽。
pub const OBJ_TYPE_CUSTOM_EVENT: u8 = 38; pub const OBJ_TYPE_CUSTOM_EVENT: u8 = 38;
/// Proxy 对象:target 与 handler 存于 `native_data` 载荷盒(`oxide_vm` 的
/// ProxyState),全部 trap 经 handler 路由;revoked 位记入同一载荷盒。
pub const OBJ_TYPE_PROXY: u8 = 39;
/// `is_session_epoch` 字段中的 session 标记位。 /// `is_session_epoch` 字段中的 session 标记位。
pub const SESSION_EPOCH_BIT: u8 = 0x01; pub const SESSION_EPOCH_BIT: u8 = 0x01;
/// `is_session_epoch` 字段中的 GC 标记位。 /// `is_session_epoch` 字段中的 GC 标记位。
@@ -332,6 +335,11 @@ impl JsObject {
pub fn is_event_obj(&self) -> bool { pub fn is_event_obj(&self) -> bool {
self.type_tag == Self::OBJ_TYPE_EVENT self.type_tag == Self::OBJ_TYPE_EVENT
} }
/// 是否 Proxy 对象(`native_data` 槽存 target 与 handler 的载荷盒)。
#[inline]
pub fn is_proxy_obj(&self) -> bool {
self.type_tag == Self::OBJ_TYPE_PROXY
}
/// 是否 DataView 对象。 /// 是否 DataView 对象。
#[inline] #[inline]
pub fn is_data_view_obj(&self) -> bool { pub fn is_data_view_obj(&self) -> bool {
+2
View File
@@ -27,6 +27,8 @@ pub mod native;
mod native_box_dispatch; mod native_box_dispatch;
/// Promise 运行时(状态盒 + 微任务队列 + 构造器/方法实现)。 /// Promise 运行时(状态盒 + 微任务队列 + 构造器/方法实现)。
pub mod promise; pub mod promise;
/// Proxy 状态盒(native_data 载荷盒)与 session GC 三类接线。
mod proxy;
/// 每 VM 的 realm 组合:内核会话、session GC 簿记与 10 个内建原型槽。 /// 每 VM 的 realm 组合:内核会话、session GC 簿记与 10 个内建原型槽。
mod realm; mod realm;
/// session 级 mark-sweep GC:标记-清扫 session arena 对象与 session 字符串。 /// session 级 mark-sweep GC:标记-清扫 session arena 对象与 session 字符串。
+15 -5
View File
@@ -1,7 +1,7 @@
//! native 载荷家族的单点分类与每家族边函数引用(家族表)。 //! native 载荷家族的单点分类与每家族边函数引用(家族表)。
//! //!
//! 分类是复合谓词:header 位(map/set/module_ns)优先于 `type_tag`, //! 分类是复合谓词:header 位(map/set/module_ns)优先于 `type_tag`,
//! 两维互斥、一个对象至多归入一个家族。tag 维显式枚举全部 0..=38, //! 两维互斥、一个对象至多归入一个家族。tag 维显式枚举全部 0..=39,
//! 未登记 tag panic。 //! 未登记 tag panic。
//! //!
//! 关键约定: //! 关键约定:
@@ -16,7 +16,7 @@ use oxide_types::value::JsValue;
/// native 载荷家族。 /// native 载荷家族。
/// ///
/// 21 个 native 家族 + 空家族;空家族是无 native 盒类型的显式归类结果。 /// 22 个 native 家族 + 空家族;空家族是无 native 盒类型的显式归类结果。
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum NativeBoxFamily { pub(crate) enum NativeBoxFamily {
/// 无 native 载荷盒。 /// 无 native 载荷盒。
@@ -63,6 +63,8 @@ pub(crate) enum NativeBoxFamily {
CustomEvent, CustomEvent,
/// mapped arguments 对象(tag 20,同步状态盒存 `native_data`,无引用边)。 /// mapped arguments 对象(tag 20,同步状态盒存 `native_data`,无引用边)。
Arguments, Arguments,
/// Proxy 对象(tag 39,target 与 handler 载荷盒存 `native_data`)。
Proxy,
} }
/// 一个家族的边操作集(每家族函数引用,单一注册面)。 /// 一个家族的边操作集(每家族函数引用,单一注册面)。
@@ -95,7 +97,7 @@ pub(crate) fn classify(obj: &JsObject) -> NativeBoxFamily {
/// 按类型 tag 分类 native 载荷家族。 /// 按类型 tag 分类 native 载荷家族。
/// ///
/// # 边界与前提 /// # 边界与前提
/// - 0..=38 全 tag 显式枚举;未登记 tag(39 及以上)panic。 /// - 0..=39 全 tag 显式枚举;未登记 tag(40 及以上)panic。
pub(crate) fn classify_by_tag(tag: u8) -> NativeBoxFamily { pub(crate) fn classify_by_tag(tag: u8) -> NativeBoxFamily {
match tag { match tag {
JsObject::OBJ_TYPE_ARRAY_BUFFER => NativeBoxFamily::ArrayBuffer, JsObject::OBJ_TYPE_ARRAY_BUFFER => NativeBoxFamily::ArrayBuffer,
@@ -138,6 +140,7 @@ pub(crate) fn classify_by_tag(tag: u8) -> NativeBoxFamily {
JsObject::OBJ_TYPE_MESSAGE_EVENT => NativeBoxFamily::MessageEvent, JsObject::OBJ_TYPE_MESSAGE_EVENT => NativeBoxFamily::MessageEvent,
JsObject::OBJ_TYPE_ERROR_EVENT => NativeBoxFamily::ErrorEvent, JsObject::OBJ_TYPE_ERROR_EVENT => NativeBoxFamily::ErrorEvent,
JsObject::OBJ_TYPE_CUSTOM_EVENT => NativeBoxFamily::CustomEvent, JsObject::OBJ_TYPE_CUSTOM_EVENT => NativeBoxFamily::CustomEvent,
JsObject::OBJ_TYPE_PROXY => NativeBoxFamily::Proxy,
_ => panic!("unregistered type tag: {tag}"), _ => panic!("unregistered type tag: {tag}"),
} }
} }
@@ -269,6 +272,12 @@ pub(crate) fn ops_for(family: NativeBoxFamily) -> NativeBoxOps {
string_edges: None, string_edges: None,
cell_edges: None, cell_edges: None,
}, },
// Proxy 载荷盒:target 与 handler 两条对象边(mark 期经家族表自动传播)。
NativeBoxFamily::Proxy => NativeBoxOps {
object_edges: Some(crate::proxy::proxy_native_edges),
string_edges: None,
cell_edges: None,
},
} }
} }
@@ -305,13 +314,14 @@ mod tests {
JsObject::OBJ_TYPE_ERROR_EVENT => NativeBoxFamily::ErrorEvent, JsObject::OBJ_TYPE_ERROR_EVENT => NativeBoxFamily::ErrorEvent,
JsObject::OBJ_TYPE_CUSTOM_EVENT => NativeBoxFamily::CustomEvent, JsObject::OBJ_TYPE_CUSTOM_EVENT => NativeBoxFamily::CustomEvent,
JsObject::OBJ_TYPE_ARGUMENTS => NativeBoxFamily::Arguments, JsObject::OBJ_TYPE_ARGUMENTS => NativeBoxFamily::Arguments,
JsObject::OBJ_TYPE_PROXY => NativeBoxFamily::Proxy,
_ => NativeBoxFamily::None, _ => NativeBoxFamily::None,
} }
} }
#[test] #[test]
fn tag_dimension_every_tag_hits_exactly_one_family() { fn tag_dimension_every_tag_hits_exactly_one_family() {
for tag in 0..=38u8 { for tag in 0..=39u8 {
let obj = obj_with_tag(tag); let obj = obj_with_tag(tag);
let family = classify(&obj); let family = classify(&obj);
assert_eq!(family, expected_family_for_tag(tag), "tag {tag} 家族不一致"); assert_eq!(family, expected_family_for_tag(tag), "tag {tag} 家族不一致");
@@ -345,7 +355,7 @@ mod tests {
#[test] #[test]
#[should_panic(expected = "unregistered type tag")] #[should_panic(expected = "unregistered type tag")]
fn unregistered_tag_panics() { fn unregistered_tag_panics() {
let obj = obj_with_tag(39); let obj = obj_with_tag(40);
classify(&obj); classify(&obj);
} }
} }
+135
View File
@@ -0,0 +1,135 @@
//! Proxy 状态盒:Proxy 对象 `native_data` 槽的载荷盒(target、handler、revoked
//! 位)与 session GC 三类接线——值边、字节核算与释放。
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
/// Proxy 状态盒:存于 Proxy 对象 `native_data`,target 与 handler 是 GC 边,
/// revoked 位标记已撤销状态(撤销后全部 trap 抛 TypeError)。
///
/// 生命周期与 Proxy 对象一致:构造期经 `Box::into_raw` 分配,GC drop 链释放,
/// 此后不替换。
pub(crate) struct ProxyState {
/// 目标对象(GC 边)。
pub(crate) target: JsValue,
/// 处理器对象(GC 边)。
pub(crate) handler: JsValue,
/// 撤销位:置位后所有 trap 派发抛 TypeError。
#[allow(dead_code)] // 读方为 trap 派发路径,随该路径接线启用
pub(crate) revoked: bool,
}
/// 读 Proxy 状态盒(盒未建立时 None)。
pub(crate) fn proxy_state_ref(obj: &JsObject) -> Option<&ProxyState> {
let ptr = obj.native_data() as *const ProxyState;
if ptr.is_null() {
None
} else {
// SAFETY: native_data 由 Box::into_raw 分配,生命周期与 Proxy 对象一致。
Some(unsafe { &*ptr })
}
}
/// 可变访问 Proxy 状态盒(写 revoked 位)。
#[allow(dead_code)] // 写方为构造器与 revocable 路径,随该路径接线启用
pub(crate) fn proxy_state_mut(obj: &mut JsObject) -> Option<&mut ProxyState> {
let ptr = obj.native_data() as *mut ProxyState;
if ptr.is_null() {
None
} else {
// SAFETY: native_data 由 Box::into_raw 分配,生命周期与 Proxy 对象一致。
Some(unsafe { &mut *ptr })
}
}
/// Proxy 状态盒内全部值边(GC mark 边):target 与 handler。
pub(crate) fn proxy_native_edges(obj: &JsObject) -> Vec<JsValue> {
let Some(state) = proxy_state_ref(obj) else {
return Vec::new();
};
vec![state.target, state.handler]
}
/// 只读核算 Proxy 状态盒字节(不释放),供 GC 账目核算。
pub(crate) fn proxy_native_size(obj: &JsObject) -> u64 {
if !obj.is_proxy_obj() {
return 0;
}
let ptr = obj.native_data() as *mut ProxyState;
if ptr.is_null() {
return 0;
}
// SAFETY: 非 Proxy 与空指针已早退,ptr 非空且指向存活 Box<ProxyState>,此处只读不释放。
std::mem::size_of::<ProxyState>() as u64
}
/// 释放 Proxy 状态盒(对象被回收时),返回释放字节数。
pub(crate) fn drop_proxy_native(obj: &JsObject) -> u64 {
let bytes = proxy_native_size(obj);
if bytes == 0 {
return 0;
}
let ptr = obj.native_data() as *mut ProxyState;
// SAFETY: ptr 非空(proxy_native_size 已验证),Box::from_raw 恰好释放一次。
let state = unsafe { Box::from_raw(ptr) };
drop(state);
bytes
}
#[cfg(test)]
mod tests {
use super::*;
use oxide_types::shape::EMPTY_SHAPE_ID;
/// 建一个带状态盒的 Proxy 对象(测试构造路径,不经 Vm 分配)。
fn proxy_obj(target: JsValue, handler: JsValue) -> JsObject {
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined());
obj.type_tag = JsObject::OBJ_TYPE_PROXY;
let state = Box::new(ProxyState {
target,
handler,
revoked: false,
});
obj.set_native_data(Box::into_raw(state) as *mut u8);
obj
}
/// 堆分配一个普通对象值,作 GC 边占位。
fn plain_obj_value() -> JsValue {
let obj = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
JsValue::from_js_object(Box::into_raw(obj))
}
#[test]
fn proxy_native_edges_return_target_and_handler() {
let target = plain_obj_value();
let handler = plain_obj_value();
let obj = proxy_obj(target, handler);
assert_eq!(proxy_native_edges(&obj), vec![target, handler]);
}
#[test]
fn proxy_native_size_and_drop_account_box_bytes() {
let obj = proxy_obj(plain_obj_value(), plain_obj_value());
let bytes = proxy_native_size(&obj);
assert_eq!(bytes, std::mem::size_of::<ProxyState>() as u64);
assert_eq!(drop_proxy_native(&obj), bytes);
}
#[test]
fn proxy_native_size_is_zero_for_non_proxy() {
let obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined());
assert_eq!(proxy_native_size(&obj), 0);
assert_eq!(drop_proxy_native(&obj), 0);
}
#[test]
fn proxy_state_revoked_bit_roundtrip() {
let mut obj = proxy_obj(plain_obj_value(), plain_obj_value());
let state = proxy_state_ref(&obj).expect("状态盒");
assert!(!state.revoked);
let state = proxy_state_mut(&mut obj).expect("状态盒");
state.revoked = true;
assert!(proxy_state_ref(&obj).expect("状态盒").revoked);
}
}
+2
View File
@@ -137,6 +137,7 @@ impl SessionGc {
bytes += event::error_event_native_size(obj); bytes += event::error_event_native_size(obj);
bytes += event::custom_event_native_size(obj); bytes += event::custom_event_native_size(obj);
bytes += crate::arguments_gc::arguments_native_size(obj); bytes += crate::arguments_gc::arguments_native_size(obj);
bytes += crate::proxy::proxy_native_size(obj);
bytes bytes
} }
@@ -599,6 +600,7 @@ impl SessionGc {
freed_bytes += event::drop_error_event_native(obj); freed_bytes += event::drop_error_event_native(obj);
freed_bytes += event::drop_custom_event_native(obj); freed_bytes += event::drop_custom_event_native(obj);
freed_bytes += crate::arguments_gc::drop_arguments_native(obj); freed_bytes += crate::arguments_gc::drop_arguments_native(obj);
freed_bytes += crate::proxy::drop_proxy_native(obj);
freed_bytes freed_bytes
} }