From 985310d545e5ace0b761ca7a24dbe91f93681b45 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Fri, 16 Oct 2026 19:57:30 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20Proxy=20=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1=E6=A0=87=E7=AD=BE=E4=B8=8E=E7=8A=B6=E6=80=81=E7=9B=92?= =?UTF-8?q?=E7=9A=84=E5=9E=83=E5=9C=BE=E5=9B=9E=E6=94=B6=E6=8E=A5=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 42 +++---- Cargo.toml | 2 +- crates/oxide_types/src/object.rs | 8 ++ crates/oxide_vm/src/lib.rs | 2 + crates/oxide_vm/src/native_box_dispatch.rs | 20 ++- crates/oxide_vm/src/proxy.rs | 135 +++++++++++++++++++++ crates/oxide_vm/src/session_gc.rs | 2 + 7 files changed, 184 insertions(+), 27 deletions(-) create mode 100644 crates/oxide_vm/src/proxy.rs diff --git a/Cargo.lock b/Cargo.lock index 9628767f..bda11a50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.20.3" +version = "0.20.4" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.20.3" +version = "0.20.4" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.20.3" +version = "0.20.4" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.20.3" +version = "0.20.4" dependencies = [ "ansi_term", "bumpalo", @@ -1228,7 +1228,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.20.3" +version = "0.20.4" dependencies = [ "lru", "oxide_bytecode", @@ -1237,7 +1237,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.20.3" +version = "0.20.4" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1258,7 +1258,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.20.3" +version = "0.20.4" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1272,7 +1272,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.20.3" +version = "0.20.4" dependencies = [ "num-bigint", "oxc_allocator", @@ -1288,7 +1288,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.20.3" +version = "0.20.4" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1300,7 +1300,7 @@ dependencies = [ [[package]] name = "oxide_fold" -version = "0.20.3" +version = "0.20.4" dependencies = [ "num-bigint", "oxide_bytecode", @@ -1312,7 +1312,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.20.3" +version = "0.20.4" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.20.3" +version = "0.20.4" dependencies = [ "bumpalo", "dashmap", @@ -1335,7 +1335,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.20.3" +version = "0.20.4" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1347,7 +1347,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.20.3" +version = "0.20.4" dependencies = [ "tracing", "tracing-appender", @@ -1356,11 +1356,11 @@ dependencies = [ [[package]] name = "oxide_net" -version = "0.20.3" +version = "0.20.4" [[package]] name = "oxide_parser" -version = "0.20.3" +version = "0.20.4" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1375,7 +1375,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.20.3" +version = "0.20.4" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1391,7 +1391,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.20.3" +version = "0.20.4" dependencies = [ "num-bigint", "num-traits", @@ -1404,7 +1404,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.20.3" +version = "0.20.4" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1424,7 +1424,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.20.3" +version = "0.20.4" dependencies = [ "bumpalo", "dashmap", @@ -1437,7 +1437,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.20.3" +version = "0.20.4" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 61e9b7b4..9afb7a58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ members = [ ] [workspace.package] -version = "0.20.3" +version = "0.20.4" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_types/src/object.rs b/crates/oxide_types/src/object.rs index f7c4f7fc..c0f169f9 100644 --- a/crates/oxide_types/src/object.rs +++ b/crates/oxide_types/src/object.rs @@ -240,6 +240,9 @@ impl JsObject { pub const OBJ_TYPE_ERROR_EVENT: u8 = 37; /// CustomEvent 对象:事件派生类载荷盒(detail 边)存于 `native_fn` 槽。 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 标记位。 pub const SESSION_EPOCH_BIT: u8 = 0x01; /// `is_session_epoch` 字段中的 GC 标记位。 @@ -332,6 +335,11 @@ impl JsObject { pub fn is_event_obj(&self) -> bool { 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 对象。 #[inline] pub fn is_data_view_obj(&self) -> bool { diff --git a/crates/oxide_vm/src/lib.rs b/crates/oxide_vm/src/lib.rs index 66a8bb97..5b837b29 100644 --- a/crates/oxide_vm/src/lib.rs +++ b/crates/oxide_vm/src/lib.rs @@ -27,6 +27,8 @@ pub mod native; mod native_box_dispatch; /// Promise 运行时(状态盒 + 微任务队列 + 构造器/方法实现)。 pub mod promise; +/// Proxy 状态盒(native_data 载荷盒)与 session GC 三类接线。 +mod proxy; /// 每 VM 的 realm 组合:内核会话、session GC 簿记与 10 个内建原型槽。 mod realm; /// session 级 mark-sweep GC:标记-清扫 session arena 对象与 session 字符串。 diff --git a/crates/oxide_vm/src/native_box_dispatch.rs b/crates/oxide_vm/src/native_box_dispatch.rs index 863ba0d1..b37d12e5 100644 --- a/crates/oxide_vm/src/native_box_dispatch.rs +++ b/crates/oxide_vm/src/native_box_dispatch.rs @@ -1,7 +1,7 @@ //! native 载荷家族的单点分类与每家族边函数引用(家族表)。 //! //! 分类是复合谓词:header 位(map/set/module_ns)优先于 `type_tag`, -//! 两维互斥、一个对象至多归入一个家族。tag 维显式枚举全部 0..=38, +//! 两维互斥、一个对象至多归入一个家族。tag 维显式枚举全部 0..=39, //! 未登记 tag panic。 //! //! 关键约定: @@ -16,7 +16,7 @@ use oxide_types::value::JsValue; /// native 载荷家族。 /// -/// 21 个 native 家族 + 空家族;空家族是无 native 盒类型的显式归类结果。 +/// 22 个 native 家族 + 空家族;空家族是无 native 盒类型的显式归类结果。 #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum NativeBoxFamily { /// 无 native 载荷盒。 @@ -63,6 +63,8 @@ pub(crate) enum NativeBoxFamily { CustomEvent, /// mapped arguments 对象(tag 20,同步状态盒存 `native_data`,无引用边)。 Arguments, + /// Proxy 对象(tag 39,target 与 handler 载荷盒存 `native_data`)。 + Proxy, } /// 一个家族的边操作集(每家族函数引用,单一注册面)。 @@ -95,7 +97,7 @@ pub(crate) fn classify(obj: &JsObject) -> NativeBoxFamily { /// 按类型 tag 分类 native 载荷家族。 /// /// # 边界与前提 -/// - 0..=38 全 tag 显式枚举;未登记 tag(39 及以上)panic。 +/// - 0..=39 全 tag 显式枚举;未登记 tag(40 及以上)panic。 pub(crate) fn classify_by_tag(tag: u8) -> NativeBoxFamily { match tag { 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_ERROR_EVENT => NativeBoxFamily::ErrorEvent, JsObject::OBJ_TYPE_CUSTOM_EVENT => NativeBoxFamily::CustomEvent, + JsObject::OBJ_TYPE_PROXY => NativeBoxFamily::Proxy, _ => panic!("unregistered type tag: {tag}"), } } @@ -269,6 +272,12 @@ pub(crate) fn ops_for(family: NativeBoxFamily) -> NativeBoxOps { string_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_CUSTOM_EVENT => NativeBoxFamily::CustomEvent, JsObject::OBJ_TYPE_ARGUMENTS => NativeBoxFamily::Arguments, + JsObject::OBJ_TYPE_PROXY => NativeBoxFamily::Proxy, _ => NativeBoxFamily::None, } } #[test] 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 family = classify(&obj); assert_eq!(family, expected_family_for_tag(tag), "tag {tag} 家族不一致"); @@ -345,7 +355,7 @@ mod tests { #[test] #[should_panic(expected = "unregistered type tag")] fn unregistered_tag_panics() { - let obj = obj_with_tag(39); + let obj = obj_with_tag(40); classify(&obj); } } diff --git a/crates/oxide_vm/src/proxy.rs b/crates/oxide_vm/src/proxy.rs new file mode 100644 index 00000000..54277c57 --- /dev/null +++ b/crates/oxide_vm/src/proxy.rs @@ -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 { + 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,此处只读不释放。 + std::mem::size_of::() 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::() 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); + } +} diff --git a/crates/oxide_vm/src/session_gc.rs b/crates/oxide_vm/src/session_gc.rs index 721b2298..f8c3b1e7 100644 --- a/crates/oxide_vm/src/session_gc.rs +++ b/crates/oxide_vm/src/session_gc.rs @@ -137,6 +137,7 @@ impl SessionGc { bytes += event::error_event_native_size(obj); bytes += event::custom_event_native_size(obj); bytes += crate::arguments_gc::arguments_native_size(obj); + bytes += crate::proxy::proxy_native_size(obj); bytes } @@ -599,6 +600,7 @@ impl SessionGc { freed_bytes += event::drop_error_event_native(obj); freed_bytes += event::drop_custom_event_native(obj); freed_bytes += crate::arguments_gc::drop_arguments_native(obj); + freed_bytes += crate::proxy::drop_proxy_native(obj); freed_bytes }