feat: 落地 Proxy 构造器与 revocable 及撤销函数

This commit is contained in:
2026-10-17 05:47:54 +00:00
parent 985310d545
commit 60170ac9be
11 changed files with 483 additions and 106 deletions
Generated
+21 -21
View File
@@ -1160,7 +1160,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_builtins" name = "oxide_builtins"
version = "0.20.4" version = "0.20.5"
dependencies = [ dependencies = [
"chrono", "chrono",
"fast-float", "fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_bytecode" name = "oxide_bytecode"
version = "0.20.4" version = "0.20.5"
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.4" version = "0.20.5"
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.4" version = "0.20.5"
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.4" version = "0.20.5"
dependencies = [ dependencies = [
"lru", "lru",
"oxide_bytecode", "oxide_bytecode",
@@ -1237,7 +1237,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_compiler" name = "oxide_compiler"
version = "0.20.4" version = "0.20.5"
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.4" version = "0.20.5"
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.4" version = "0.20.5"
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.4" version = "0.20.5"
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.4" version = "0.20.5"
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.4" version = "0.20.5"
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.4" version = "0.20.5"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"dashmap", "dashmap",
@@ -1335,7 +1335,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_liveness" name = "oxide_liveness"
version = "0.20.4" version = "0.20.5"
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.4" version = "0.20.5"
dependencies = [ dependencies = [
"tracing", "tracing",
"tracing-appender", "tracing-appender",
@@ -1356,11 +1356,11 @@ dependencies = [
[[package]] [[package]]
name = "oxide_net" name = "oxide_net"
version = "0.20.4" version = "0.20.5"
[[package]] [[package]]
name = "oxide_parser" name = "oxide_parser"
version = "0.20.4" version = "0.20.5"
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.4" version = "0.20.5"
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.4" version = "0.20.5"
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.4" version = "0.20.5"
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.4" version = "0.20.5"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"dashmap", "dashmap",
@@ -1437,7 +1437,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_vm" name = "oxide_vm"
version = "0.20.4" version = "0.20.5"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"chrono", "chrono",
+1 -1
View File
@@ -25,7 +25,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.20.4" version = "0.20.5"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
rust-version = "1.80" rust-version = "1.80"
+4 -2
View File
@@ -5,8 +5,8 @@
//! ArrayBuffer、DataView、Reflect、Function、Error 与全局函数(URI/escape 等)。 //! ArrayBuffer、DataView、Reflect、Function、Error 与全局函数(URI/escape 等)。
//! 每个内置方法以 `fn xxx<H: VmHost>(vm, args: &[u8]) -> NativeResult` 形式暴露, //! 每个内置方法以 `fn xxx<H: VmHost>(vm, args: &[u8]) -> NativeResult` 形式暴露,
//! 由上层(oxide_vm / oxide_api)注册为 JS 全局对象上的 native function。 //! 由上层(oxide_vm / oxide_api)注册为 JS 全局对象上的 native function。
//! 架构性延后的特性(Proxy、WeakMap、WeakSet、WeakRef、FinalizationRegistry) //! 架构性延后的特性(WeakSet、WeakRef、FinalizationRegistry)在 `stubs` 中
//! 在 `stubs` 中仅提供占位实现,调用时抛 TypeError。 //! 仅提供占位实现,调用时抛 TypeError;Proxy 与 WeakMap 已落地真构造器。
/// Array 内置对象实现(目录模块:common 共享助手、from 构造器与静态方法、element 元素变更、iterate 高阶迭代、sort_iterator 排序与迭代协议、immutable ES2023 不可变方法族)。 /// Array 内置对象实现(目录模块:common 共享助手、from 构造器与静态方法、element 元素变更、iterate 高阶迭代、sort_iterator 排序与迭代协议、immutable ES2023 不可变方法族)。
pub mod array; pub mod array;
@@ -64,6 +64,8 @@ pub mod module;
pub mod number; pub mod number;
/// Object 内置对象实现(keys/create/assign/defineProperty 等静态方法)。 /// Object 内置对象实现(keys/create/assign/defineProperty 等静态方法)。
pub mod object; pub mod object;
/// Proxy 内置对象实现(%Proxy% 构造器、参数与不变量校验、Proxy.revocable 与撤销函数)。
pub mod proxy;
/// Reflect 内置对象实现(属性操作镜像)。 /// Reflect 内置对象实现(属性操作镜像)。
pub mod reflect; pub mod reflect;
/// RegExp 内置对象实现(constructor/test/exec/toString)。 /// RegExp 内置对象实现(constructor/test/exec/toString)。
+320
View File
@@ -0,0 +1,320 @@
//! %Proxy% 构造器与 revocable:Proxy 状态盒(target、handler、revoked 位)、
//! 参数与不变量校验、函数代理 is_function 位、Proxy.revocable 的撤销函数。
//!
//! 状态盒经 `Box::into_raw` 挂 `JsObject.native_data`,GC 边函数族(mark 边)
//! 由 `oxide_vm` 的家族表消费;撤销函数以独立 tag 存被撤销代理指针(GC 边),
//! 保证仅持撤销函数时代理不被回收。
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_runtime_api::{NativeResult, ProtoKind, VmHost};
use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes};
use oxide_types::value::JsValue;
/// Proxy 状态盒:存于 Proxy 对象 `native_data`,target 与 handler 是 GC 边,
/// revoked 位标记已撤销状态(撤销后全部 trap 抛 TypeError)。
///
/// 生命周期与 Proxy 对象一致:构造期经 `Box::into_raw` 分配,GC drop 链释放,
/// 此后不替换。
pub struct ProxyState {
/// 目标对象(GC 边)。
pub target: JsValue,
/// 处理器对象(GC 边)。
pub handler: JsValue,
/// 撤销位:置位后所有 trap 派发抛 TypeError。
pub revoked: bool,
}
/// 读 Proxy 状态盒(盒未建立时 None)。
pub 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 位)。
pub 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 })
}
}
/// 撤销函数的对象边(GC mark 强边):`native_data` 存的被撤销代理指针。
pub fn proxy_revoker_native_edges(obj: &JsObject) -> Vec<JsValue> {
if !obj.is_proxy_revoker_obj() {
return Vec::new();
}
let ptr = obj.native_data() as *mut JsObject;
if ptr.is_null() {
return Vec::new();
}
// SAFETY: native_data 存存活代理指针(GC 边),mark 期对象存活。
vec![JsValue::from_js_object(ptr)]
}
/// 参数校验:target 与 handler 均须对象,否则抛 TypeError(规范 10.2.4 步骤 1-2)。
fn check_target_and_handler(target: JsValue, handler: JsValue) -> Result<(), &'static str> {
if !target.is_object() || target.as_js_object_ptr().is_null() {
return Err("Proxy target must be an object");
}
if !handler.is_object() || handler.as_js_object_ptr().is_null() {
return Err("Proxy handler must be an object");
}
Ok(())
}
/// 不变量检查(规范 10.2.4 步骤 4-5):handler 的 isExtensible 与 setPrototypeOf
/// trap 若存在,其布尔值须与 target 实际状态一致,否则抛 TypeError。
///
/// # 边界与前提
/// - 仅当 handler 自有对应 trap 属性时检查(继承属性不触发)。
/// - trap 读为传播读,getter 抛错原值上抛。
fn proxy_create_invariants<H: VmHost>(
vm: &mut H, target_obj: &JsObject, handler_obj: &JsObject, handler: JsValue,
) -> Result<(), JsValue> {
// isExtensible trap:handler 报 true 时 target 须可扩展,报 false 时须不可扩展。
let si_is_extensible = vm.perm_intern("isExtensible");
if vm.get_own_property_slot(handler_obj, si_is_extensible).is_some() {
let trap_val = vm
.ordinary_get(handler_obj, si_is_extensible, handler)
.map_err(|e| crate::iterator::engine_error(vm, &e))?;
let target_extensible = target_obj.is_extensible();
let trap_is_true = trap_val.is_bool() && trap_val.as_bool();
let trap_is_false = trap_val.is_bool() && !trap_val.as_bool();
if (trap_is_true && !target_extensible) || (trap_is_false && target_extensible) {
return Err(crate::error::create_type_error(
vm,
"Proxy invariant: isExtensible trap inconsistent with target",
));
}
}
// setPrototypeOf trap:handler 报 true 时 target 原型须为对象或 null,
// 报 false 时须为其它类型。
let si_set_proto = vm.perm_intern("setPrototypeOf");
if vm.get_own_property_slot(handler_obj, si_set_proto).is_some() {
let trap_val = vm
.ordinary_get(handler_obj, si_set_proto, handler)
.map_err(|e| crate::iterator::engine_error(vm, &e))?;
let target_proto = target_obj.proto();
let target_proto_is_obj_or_null = target_proto.is_object() || target_proto.is_null();
let trap_is_true = trap_val.is_bool() && trap_val.as_bool();
let trap_is_false = trap_val.is_bool() && !trap_val.as_bool();
if (trap_is_true && !target_proto_is_obj_or_null) || (trap_is_false && target_proto_is_obj_or_null) {
return Err(crate::error::create_type_error(
vm,
"Proxy invariant: setPrototypeOf trap inconsistent with target",
));
}
}
Ok(())
}
/// 创建代理对象本体:tag 39 + ProxyState 载荷盒 + Object.prototype 原型链;
/// target 可调用时置 is_function 位(typeof 经该位返回 function)。
fn create_proxy_object<H: VmHost>(vm: &mut H, target: JsValue, handler: JsValue) -> JsValue {
let object_proto = vm.builtin_proto(ProtoKind::ObjectProto);
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto));
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);
if crate::iterator::is_callable(target) {
obj.set_function(true);
}
let proxy_obj = vm.alloc_object(obj);
JsValue::from_js_object(proxy_obj)
}
/// `Proxy ( target, handler )` 构造器:NewTarget 校验(须经 new 调用)后按
/// 规范 10.2.4 建代理对象并返回。
///
/// # 步骤
/// 1. NewTarget 校验:非构造形态调用抛 TypeError。
/// 2. 参数校验:target 与 handler 均须对象。
/// 3. 不变量检查:handler 的 isExtensible / setPrototypeOf trap 与 target 一致性。
/// 4. 创建代理对象(tag 39 + 状态盒 + Object.prototype 原型链)。
///
/// # 边界与前提
/// - %Proxy% 无 prototype 属性(规范 25.1),返回的代理对象原型链落
/// Object.prototype。
/// - target 可调用时代理置 is_function 位(typeof 返 function)。
pub fn proxy_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if !vm.constructing_native() {
return NativeResult::Err(crate::error::create_type_error(vm, "Proxy constructor requires 'new'"));
}
let target = vm.reg(if args.len() > 1 { args[1] } else { 0 });
let handler = vm.reg(if args.len() > 2 { args[2] } else { 0 });
if let Err(msg) = check_target_and_handler(target, handler) {
return NativeResult::Err(crate::error::create_type_error(vm, msg));
}
// SAFETY: check_target_and_handler 已校验非空对象指针。
let target_obj = unsafe { &*target.as_js_object_ptr() };
let handler_obj = unsafe { &*handler.as_js_object_ptr() };
if let Err(exc) = proxy_create_invariants(vm, target_obj, handler_obj, handler) {
return NativeResult::Err(exc);
}
NativeResult::Ok(create_proxy_object(vm, target, handler))
}
/// `Proxy.revocable ( target, handler )`:建代理对象后返回 `{proxy, revoke}`
/// 结果对象;revoke 为撤销函数(调用后代理全部 trap 抛 TypeError)。
///
/// # 步骤
/// 1. 参数校验与不变量检查(同构造器)。
/// 2. 创建代理对象。
/// 3. 创建撤销函数(length 0、name 空串、非构造器,native_data 存代理指针)。
/// 4. 建结果对象并装 `proxy` / `revoke` 两数据属性。
///
/// # 边界与前提
/// - 撤销函数经独立 tag 存代理指针,GC 边保活代理(仅持撤销函数时代理不回收)。
/// - 撤销函数可重复调用,幂等置 revoked 位。
pub fn proxy_revocable<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let target = vm.reg(if args.len() > 1 { args[1] } else { 0 });
let handler = vm.reg(if args.len() > 2 { args[2] } else { 0 });
if let Err(msg) = check_target_and_handler(target, handler) {
return NativeResult::Err(crate::error::create_type_error(vm, msg));
}
// SAFETY: check_target_and_handler 已校验非空对象指针。
let target_obj = unsafe { &*target.as_js_object_ptr() };
let handler_obj = unsafe { &*handler.as_js_object_ptr() };
if let Err(exc) = proxy_create_invariants(vm, target_obj, handler_obj, handler) {
return NativeResult::Err(exc);
}
let proxy_val = create_proxy_object(vm, target, handler);
// 撤销函数:Function.prototype 原型链 + native 函数项 + length 0 / name 空串。
let function_proto = vm.builtin_proto(ProtoKind::FunctionProto);
let mut revoker = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto));
revoker.set_function(true);
revoker.type_tag = JsObject::OBJ_TYPE_PROXY_REVOKER;
// SAFETY: proxy_revoker_dispatcher 是合法 NativeFn 函数项。
revoker.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(proxy_revoker_dispatcher::<H> as *const ()) }));
revoker.set_native_arg_count(0);
let revoker_obj = vm.alloc_object(revoker);
let revoker_ref = unsafe { &mut *revoker_obj };
let length_si = vm.perm_intern("length");
if let Err(e) = vm.define_data_property(
revoker_ref,
length_si,
JsValue::int(0),
PropAttributes::new(false, false, true),
) {
return NativeResult::Err(crate::iterator::engine_error(vm, &e));
}
let name_si = vm.perm_intern("name");
let empty_name = vm.new_string("");
if let Err(e) = vm.define_data_property(
revoker_ref,
name_si,
empty_name,
PropAttributes::new(false, false, true),
) {
return NativeResult::Err(crate::iterator::engine_error(vm, &e));
}
// 代理指针存进撤销函数 native_data(GC 边,mark 期经家族表传播)。
unsafe {
(*revoker_obj).set_native_data(proxy_val.as_js_object_ptr() as *mut u8);
}
let revoker_val = JsValue::from_js_object(revoker_obj);
// 结果对象 {proxy, revoke}:两数据属性(可写、可枚举、可配置)。
let object_proto = vm.builtin_proto(ProtoKind::ObjectProto);
let result_obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
let result_ref = unsafe { &mut *result_obj };
let proxy_si = vm.perm_intern("proxy");
let revoke_si = vm.perm_intern("revoke");
vm.set_or_create_prop_value(result_ref, proxy_si, proxy_val);
vm.set_or_create_prop_value(result_ref, revoke_si, revoker_val);
NativeResult::Ok(JsValue::from_js_object(result_obj))
}
/// 撤销函数调用体:置被撤销代理的 revoked 位,幂等返回 undefined。
///
/// # 步骤
/// 1. 经 callee 槽(reg 254)读撤销函数本体。
/// 2. 读其 native_data 的被撤销代理指针。
/// 3. 置代理状态盒 revoked 位。
///
/// # 边界与前提
/// - 撤销函数本体缺失或代理指针为空时静默返 undefined(防御性)。
fn proxy_revoker_dispatcher<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
let revoker_val = vm.reg(254);
let revoker_ptr = revoker_val.as_js_object_ptr();
if revoker_ptr.is_null() {
return NativeResult::Ok(JsValue::undefined());
}
// SAFETY: revoker 为存活函数对象。
let revoker = unsafe { &*revoker_ptr };
let proxy_ptr = revoker.native_data() as *mut JsObject;
if proxy_ptr.is_null() {
return NativeResult::Ok(JsValue::undefined());
}
// SAFETY: proxy_ptr 为存活代理对象(经撤销函数 GC 边保活)。
let proxy = unsafe { &mut *proxy_ptr };
let Some(state) = proxy_state_mut(proxy) else {
return NativeResult::Ok(JsValue::undefined());
};
state.revoked = true;
NativeResult::Ok(JsValue::undefined())
}
#[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_state_ref_reads_target_and_handler() {
let target = plain_obj_value();
let handler = plain_obj_value();
let obj = proxy_obj(target, handler);
let state = proxy_state_ref(&obj).expect("状态盒");
assert_eq!(state.target, target);
assert_eq!(state.handler, handler);
assert!(!state.revoked);
}
#[test]
fn proxy_state_mut_toggles_revoked() {
let mut obj = proxy_obj(plain_obj_value(), plain_obj_value());
let state = proxy_state_mut(&mut obj).expect("状态盒");
state.revoked = true;
assert!(proxy_state_ref(&obj).expect("状态盒").revoked);
}
#[test]
fn proxy_revoker_edges_return_proxy_pointer() {
let proxy = plain_obj_value();
let mut revoker = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined());
revoker.type_tag = JsObject::OBJ_TYPE_PROXY_REVOKER;
revoker.set_native_data(proxy.as_js_object_ptr() as *mut u8);
assert_eq!(proxy_revoker_native_edges(&revoker), vec![proxy]);
}
#[test]
fn proxy_revoker_edges_empty_for_non_revoker() {
let obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined());
assert!(proxy_revoker_native_edges(&obj).is_empty());
}
}
-4
View File
@@ -5,10 +5,6 @@ fn stub_error<H: VmHost>(vm: &mut H, name: &str) -> NativeResult {
} }
// 架构性延后:这些特性按设计排除在受支持语言子集之外。 // 架构性延后:这些特性按设计排除在受支持语言子集之外。
/// `Proxy` 全局占位:始终抛 TypeError。Proxy 按设计延后实现。
pub fn proxy_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
stub_error(vm, "Proxy")
}
/// `WeakSet` 全局占位:始终抛 TypeError。WeakSet 按设计延后实现。 /// `WeakSet` 全局占位:始终抛 TypeError。WeakSet 按设计延后实现。
pub fn weakset_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult { pub fn weakset_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
stub_error(vm, "WeakSet") stub_error(vm, "WeakSet")
+10 -2
View File
@@ -240,9 +240,12 @@ 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` 的 /// Proxy 对象:target 与 handler 存于 `native_data` 载荷盒(`oxide_builtins`
/// ProxyState),全部 trap 经 handler 路由;revoked 位记入同一载荷盒。 /// 的 ProxyState),全部 trap 经 handler 路由;revoked 位记入同一载荷盒。
pub const OBJ_TYPE_PROXY: u8 = 39; pub const OBJ_TYPE_PROXY: u8 = 39;
/// Proxy 撤销函数(`Proxy.revocable` 的 revoke):被撤销代理指针存于
/// `native_data`(GC 边),保证仅持撤销函数时代理不被回收。
pub const OBJ_TYPE_PROXY_REVOKER: u8 = 40;
/// `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 标记位。
@@ -340,6 +343,11 @@ impl JsObject {
pub fn is_proxy_obj(&self) -> bool { pub fn is_proxy_obj(&self) -> bool {
self.type_tag == Self::OBJ_TYPE_PROXY self.type_tag == Self::OBJ_TYPE_PROXY
} }
/// 是否 Proxy 撤销函数(`native_data` 槽存被撤销代理指针,GC 边)。
#[inline]
pub fn is_proxy_revoker_obj(&self) -> bool {
self.type_tag == Self::OBJ_TYPE_PROXY_REVOKER
}
/// 是否 DataView 对象。 /// 是否 DataView 对象。
#[inline] #[inline]
pub fn is_data_view_obj(&self) -> bool { pub fn is_data_view_obj(&self) -> bool {
+12 -14
View File
@@ -11,12 +11,10 @@ use crate::bindings::{bind_global_value, configure_native_constructor};
/// 未实现内置的 stub 表:(全局名、native 函数项指针、形参个数、 /// 未实现内置的 stub 表:(全局名、native 函数项指针、形参个数、
/// 原型 @@toStringTag、是否安装原型对象)。 /// 原型 @@toStringTag、是否安装原型对象)。
/// ///
/// `tag` 为 `None` 的条目(Proxy)原型对象不设 @@toStringTag;弱族 stub /// 弱族 stub(WeakSet/WeakRef/FinalizationRegistry)各取族名作 @@toStringTag,
/// (WeakSet/WeakRef/FinalizationRegistry)各取族名。 /// 各装带 constructor 回指的原型对象。Proxy 已落地真构造器(`bind_proxy`),
/// 规范 25.1 的 %Proxy% 无 prototype 属性(`has_prototype` = false), /// 不在此表。
/// 弱族 stub 各装带 constructor 回指的原型对象。 const STUBS: [(&str, *const (), u8, Option<&str>, bool); 3] = [
const STUBS: [(&str, *const (), u8, Option<&str>, bool); 4] = [
("Proxy", oxide_builtins::stubs::proxy_stub::<crate::vm::Vm> as *const (), 2, None, false),
( (
"WeakSet", "WeakSet",
oxide_builtins::stubs::weakset_stub::<crate::vm::Vm> as *const (), oxide_builtins::stubs::weakset_stub::<crate::vm::Vm> as *const (),
@@ -50,16 +48,16 @@ fn set_data_property(core: &Arc<KernelCore>, obj: &mut JsObject, si: u32, value:
obj.bump_generation(); obj.bump_generation();
} }
/// 把未实现内置(Proxy/WeakSet/WeakRef/FinalizationRegistry)的 stub 构造器 /// 把未实现内置(WeakSet/WeakRef/FinalizationRegistry)的 stub 构造器
/// 绑定到 global,并登记到 `stub_objects` 供快照跟踪。WeakMap 已落地真构造器, /// 绑定到 global,并登记到 `stub_objects` 供快照跟踪。WeakMap 与 Proxy 已落地
/// 走 `bind_weak` 绑定面,不在此表。 /// 真构造器,走 `bind_weak` / `bind_proxy` 绑定面,不在此表。
/// ///
/// # 副作用 /// # 副作用
/// 每个 stub 按规范补装构造器面属性:`length`、`name`(四族全装);弱族 stub /// 每个 stub 按规范补装构造器面属性:`length`、`name`(三族全装);弱族 stub
/// 另装 `prototype`(新建 plain 对象,原型链落 Object.prototype,其上带指回 /// 另装 `prototype`(新建 plain 对象,原型链落 Object.prototype,其上带指回
/// stub 的 `constructor` 与 @@toStringTag)——规范 25.1 的 %Proxy% 无 /// stub 的 `constructor` 与 @@toStringTag);描述符均不可枚举,`prototype`
/// prototype 属性,不装;描述符均不可枚举,`prototype` 另不可写、不可配置。 /// 另不可写、不可配置。原型对象登记进 world 释放登记表,与 stub 对象同生死,
/// 原型对象登记进 world 释放登记表,与 stub 对象同生死,session 收尾统一释放。 /// session 收尾统一释放。
/// ///
/// `realm_id` 是目标所属 realm 的编号:`@@toStringTag` 符号键按 (realm 编号, /// `realm_id` 是目标所属 realm 的编号:`@@toStringTag` 符号键按 (realm 编号,
/// 局部下标) 编码,realm 编号为 0 时与旧编码逐字节一致。 /// 局部下标) 编码,realm 编号为 0 时与旧编码逐字节一致。
@@ -79,7 +77,7 @@ pub fn bind_stubs(core: &Arc<KernelCore>, session: &mut KernelSession, global: &
let stub = P::new(stub); let stub = P::new(stub);
let stub_ptr = stub.as_ptr() as *mut JsObject; let stub_ptr = stub.as_ptr() as *mut JsObject;
// 构造器面属性:length / name 四族全装;prototype 仅弱族 stub(%Proxy% 无)。 // 构造器面属性:length / name 三族全装;prototype 仅弱族 stub。
// SAFETY: stub 尚未发布到 global,局部 Arc 为唯一持有者,可变访问无读者冲突。 // SAFETY: stub 尚未发布到 global,局部 Arc 为唯一持有者,可变访问无读者冲突。
let stub_obj = unsafe { &mut *stub_ptr }; let stub_obj = unsafe { &mut *stub_ptr };
if has_prototype { if has_prototype {
+84 -4
View File
@@ -59,7 +59,7 @@ pub mod bind_set;
pub mod bind_shared_array_buffer; pub mod bind_shared_array_buffer;
/// String 构造器与原型的 native 方法绑定。 /// String 构造器与原型的 native 方法绑定。
pub mod bind_string; pub mod bind_string;
/// 未实现内置(Proxy/BigInt/WeakMap 等)的 stub 构造器绑定。 /// 未实现内置(WeakSet/WeakRef/FinalizationRegistry 等)的 stub 构造器绑定。
pub mod bind_stubs; pub mod bind_stubs;
/// Symbol 构造器与原型的 native 方法绑定。 /// Symbol 构造器与原型的 native 方法绑定。
pub mod bind_symbol; pub mod bind_symbol;
@@ -978,10 +978,9 @@ fn bind_iterator_global(env: BindEnv<'_>, global: &mut JsObject) {
} }
fn bind_stub_globals(env: BindEnv<'_>, global: &mut JsObject) { fn bind_stub_globals(env: BindEnv<'_>, global: &mut JsObject) {
// 索引 4 = WeakMap 真构造器(bind_weak 留存 stub_objects 末位),global // 索引 3 = WeakMap 真构造器、索引 4 = Proxy 真构造器(bind_weak / bind_proxy
// 重建时原位重绑;四 stub 族索引 0-3 紧随其后。 // 留存 stub_objects 末两位),global 重建时原位重绑;三 stub 族索引 0-2 紧随其后。
for (index, (name, native_fn, arg_count)) in [ for (index, (name, native_fn, arg_count)) in [
("Proxy", oxide_builtins::stubs::proxy_stub::<crate::vm::Vm> as *const (), 2),
("WeakSet", oxide_builtins::stubs::weakset_stub::<crate::vm::Vm> as *const (), 0), ("WeakSet", oxide_builtins::stubs::weakset_stub::<crate::vm::Vm> as *const (), 0),
("WeakRef", oxide_builtins::stubs::weakref_stub::<crate::vm::Vm> as *const (), 1), ("WeakRef", oxide_builtins::stubs::weakref_stub::<crate::vm::Vm> as *const (), 1),
( (
@@ -990,6 +989,7 @@ fn bind_stub_globals(env: BindEnv<'_>, global: &mut JsObject) {
1, 1,
), ),
("WeakMap", oxide_builtins::weak_map::weak_map_constructor::<crate::vm::Vm> as *const (), 0), ("WeakMap", oxide_builtins::weak_map::weak_map_constructor::<crate::vm::Vm> as *const (), 0),
("Proxy", oxide_builtins::proxy::proxy_constructor::<crate::vm::Vm> as *const (), 2),
] ]
.into_iter() .into_iter()
.enumerate() .enumerate()
@@ -1001,6 +1001,83 @@ fn bind_stub_globals(env: BindEnv<'_>, global: &mut JsObject) {
} }
} }
/// 把 %Proxy% 真构造器绑定到 global。
///
/// # 副作用
/// - 构造器经 `P` 留存 `stub_objects`(索引 4,紧随 WeakMap 真构造器),global
/// 重建时原位重绑;全局 `Proxy` 槽经 `bind_global_value` 安装。
/// - 构造器面按规范装 `length`(2)与 `name`("Proxy");规范 25.1 的 %Proxy%
/// 无 prototype 属性,不装。
/// - `revocable` 静态方法(非构造器函数,length 2、name "revocable",原型链
/// Function.prototype)经构造器属性边可达,另登记释放登记表,session 收尾
/// 统一释放。
fn bind_proxy(core: &Arc<KernelCore>, session: &mut KernelSession, global: &mut JsObject, _realm_id: u32) {
let world = Arc::get_mut(&mut session.builtin_world)
.expect("BuiltinWorld must be uniquely owned during init_kernel_builtins");
let function_proto_ptr = world.function_proto.as_ptr() as *mut JsObject;
let si_length = core.perm_interner().intern("length").0;
let si_name = core.perm_interner().intern("name").0;
// 构造器对象:Function 原型链 + native 函数项;length / name 按规范装
// (描述符 { f,f,t },length 先于 name 以保 own 属性序)。
let mut ctor = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto_ptr));
ctor.set_function(true);
configure_native_constructor(
&mut ctor,
oxide_builtins::proxy::proxy_constructor::<crate::vm::Vm> as *const (),
2,
);
let length_shape = core.shape_forge().make_shape(ctor.shape_id(), si_length);
ctor.set_shape_id(length_shape);
ctor.ensure_hash_props().push(JsValue::int(2));
let length_pos = ctor.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
ctor.set_data_meta(length_pos, PropAttributes::new(false, false, true));
let name_shape = core.shape_forge().make_shape(ctor.shape_id(), si_name);
ctor.set_shape_id(name_shape);
ctor.ensure_hash_props().push(JsValue::perm_string(
core.perm_interner().string_ptr(core.perm_interner().intern("Proxy").0),
));
let name_pos = ctor.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
ctor.set_data_meta(name_pos, PropAttributes::new(false, false, true));
// revocable 静态方法:非构造器函数(不设 CONSTRUCTOR 标签,new 调用按规范
// 抛 TypeError),length 2 / name "revocable",原型链 Function.prototype。
let mut revocable = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto_ptr));
revocable.set_function(true);
// SAFETY: proxy_revocable 是合法 NativeFn 函数项。
revocable.set_native_fn(Some(unsafe {
NativeFnPtr::from_raw(oxide_builtins::proxy::proxy_revocable::<crate::vm::Vm> as *const ())
}));
revocable.set_native_arg_count(2);
let rev_length_shape = core.shape_forge().make_shape(revocable.shape_id(), si_length);
revocable.set_shape_id(rev_length_shape);
revocable.ensure_hash_props().push(JsValue::int(2));
let rev_length_pos = revocable.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
revocable.set_data_meta(rev_length_pos, PropAttributes::new(false, false, true));
let rev_name_shape = core.shape_forge().make_shape(revocable.shape_id(), si_name);
revocable.set_shape_id(rev_name_shape);
revocable.ensure_hash_props().push(JsValue::perm_string(
core.perm_interner().string_ptr(core.perm_interner().intern("revocable").0),
));
let rev_name_pos = revocable.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
revocable.set_data_meta(rev_name_pos, PropAttributes::new(false, false, true));
let revocable_ptr = Box::into_raw(Box::new(revocable));
world.track_leaked_object(revocable_ptr);
// 构造器 revocable 属性({ f,f,t }),own 属性序 length、name、revocable。
let si_revocable = core.perm_interner().intern("revocable").0;
let rev_shape = core.shape_forge().make_shape(ctor.shape_id(), si_revocable);
ctor.set_shape_id(rev_shape);
ctor.ensure_hash_props().push(JsValue::from_js_object(revocable_ptr));
let rev_pos = ctor.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
ctor.set_data_meta(rev_pos, PropAttributes::new(false, false, true));
let stub = P::new(ctor);
let ctor_ptr = stub.as_ptr() as *mut JsObject;
world.stub_objects.push(stub);
bind_global_value(core, global, "Proxy", JsValue::from_js_object(ctor_ptr));
}
/// 把已有 builtin 构造器的 native 实现与 global 槽位一次性装配完整。 /// 把已有 builtin 构造器的 native 实现与 global 槽位一次性装配完整。
/// ///
/// 在 global 对象重建(dirty reset)后调用:重新配置构造器 native 函数并重绑 /// 在 global 对象重建(dirty reset)后调用:重新配置构造器 native 函数并重绑
@@ -1361,6 +1438,9 @@ pub fn rebind_dirty_builtins(
// WeakMap 真构造器面与 stub 族同脏位:原型对象经 leaked 登记表迁移, // WeakMap 真构造器面与 stub 族同脏位:原型对象经 leaked 登记表迁移,
// 重绑时全局槽原位更新,不占 BuiltinWorld 槽。 // 重绑时全局槽原位更新,不占 BuiltinWorld 槽。
bind_weak::bind_weak_map(core, session, global, realm_id); bind_weak::bind_weak_map(core, session, global, realm_id);
// Proxy 真构造器面与 WeakMap 同脏位:构造器经 stub_objects 留存,重绑时
// 全局槽原位更新,不占 BuiltinWorld 槽。
bind_proxy(core, session, global, realm_id);
} }
let env = BindEnv { let env = BindEnv {
core, core,
+17 -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..=39, //! 两维互斥、一个对象至多归入一个家族。tag 维显式枚举全部 0..=40,
//! 未登记 tag panic。 //! 未登记 tag panic。
//! //!
//! 关键约定: //! 关键约定:
@@ -10,7 +10,9 @@
//! - 注册面集中在 `ops_for` 的家族表,mark/size/drop/clone/rewrite 各链 //! - 注册面集中在 `ops_for` 的家族表,mark/size/drop/clone/rewrite 各链
//! 由同一张表驱动,新增家族只在此登记。 //! 由同一张表驱动,新增家族只在此登记。
use oxide_builtins::{data_view, disposable_stack, event, map, message_channel, module, set, typed_array, weak_map}; use oxide_builtins::{
data_view, disposable_stack, event, map, message_channel, module, proxy, set, typed_array, weak_map,
};
use oxide_types::object::{Cell, JsObject, JsString}; use oxide_types::object::{Cell, JsObject, JsString};
use oxide_types::value::JsValue; use oxide_types::value::JsValue;
@@ -65,6 +67,8 @@ pub(crate) enum NativeBoxFamily {
Arguments, Arguments,
/// Proxy 对象(tag 39,target 与 handler 载荷盒存 `native_data`)。 /// Proxy 对象(tag 39,target 与 handler 载荷盒存 `native_data`)。
Proxy, Proxy,
/// Proxy 撤销函数(tag 40,被撤销代理指针存 `native_data`,GC 边)。
ProxyRevoker,
} }
/// 一个家族的边操作集(每家族函数引用,单一注册面)。 /// 一个家族的边操作集(每家族函数引用,单一注册面)。
@@ -97,7 +101,7 @@ pub(crate) fn classify(obj: &JsObject) -> NativeBoxFamily {
/// 按类型 tag 分类 native 载荷家族。 /// 按类型 tag 分类 native 载荷家族。
/// ///
/// # 边界与前提 /// # 边界与前提
/// - 0..=39 全 tag 显式枚举;未登记 tag(40 及以上)panic。 /// - 0..=40 全 tag 显式枚举;未登记 tag(41 及以上)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,
@@ -141,6 +145,7 @@ pub(crate) fn classify_by_tag(tag: u8) -> NativeBoxFamily {
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, JsObject::OBJ_TYPE_PROXY => NativeBoxFamily::Proxy,
JsObject::OBJ_TYPE_PROXY_REVOKER => NativeBoxFamily::ProxyRevoker,
_ => panic!("unregistered type tag: {tag}"), _ => panic!("unregistered type tag: {tag}"),
} }
} }
@@ -278,6 +283,12 @@ pub(crate) fn ops_for(family: NativeBoxFamily) -> NativeBoxOps {
string_edges: None, string_edges: None,
cell_edges: None, cell_edges: None,
}, },
// Proxy 撤销函数:被撤销代理指针一条对象边(保活代理,仅持撤销函数时不回收)。
NativeBoxFamily::ProxyRevoker => NativeBoxOps {
object_edges: Some(proxy::proxy_revoker_native_edges),
string_edges: None,
cell_edges: None,
},
} }
} }
@@ -315,13 +326,14 @@ mod tests {
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, JsObject::OBJ_TYPE_PROXY => NativeBoxFamily::Proxy,
JsObject::OBJ_TYPE_PROXY_REVOKER => NativeBoxFamily::ProxyRevoker,
_ => 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..=39u8 { for tag in 0..=40u8 {
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} 家族不一致");
@@ -355,7 +367,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(40); let obj = obj_with_tag(41);
classify(&obj); classify(&obj);
} }
} }
+11 -49
View File
@@ -1,50 +1,16 @@
//! Proxy 状态盒:Proxy 对象 `native_data` 槽的载荷盒(target、handler、revoked //! Proxy 状态盒的 session GC 三类接线:值边、字节核算与释放。
//! 位)与 session GC 三类接线——值边、字节核算与释放。 //!
//! 状态盒本体(`ProxyState`)定义在 `oxide_builtins::proxy`(构造器侧分配),
//! 本模块只读其布局做 GC 接线;撤销函数(独立 tag)的边函数在
//! `oxide_builtins::proxy::proxy_revoker_native_edges`。
use oxide_builtins::proxy::ProxyState;
use oxide_types::object::JsObject; use oxide_types::object::JsObject;
use oxide_types::value::JsValue; 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。 /// Proxy 状态盒内全部值边(GC mark 边):target 与 handler。
pub(crate) fn proxy_native_edges(obj: &JsObject) -> Vec<JsValue> { pub(crate) fn proxy_native_edges(obj: &JsObject) -> Vec<JsValue> {
let Some(state) = proxy_state_ref(obj) else { let Some(state) = oxide_builtins::proxy::proxy_state_ref(obj) else {
return Vec::new(); return Vec::new();
}; };
vec![state.target, state.handler] vec![state.target, state.handler]
@@ -85,11 +51,7 @@ mod tests {
fn proxy_obj(target: JsValue, handler: JsValue) -> JsObject { fn proxy_obj(target: JsValue, handler: JsValue) -> JsObject {
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()); let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined());
obj.type_tag = JsObject::OBJ_TYPE_PROXY; obj.type_tag = JsObject::OBJ_TYPE_PROXY;
let state = Box::new(ProxyState { let state = Box::new(ProxyState { target, handler, revoked: false });
target,
handler,
revoked: false,
});
obj.set_native_data(Box::into_raw(state) as *mut u8); obj.set_native_data(Box::into_raw(state) as *mut u8);
obj obj
} }
@@ -126,10 +88,10 @@ mod tests {
#[test] #[test]
fn proxy_state_revoked_bit_roundtrip() { fn proxy_state_revoked_bit_roundtrip() {
let mut obj = proxy_obj(plain_obj_value(), plain_obj_value()); let mut obj = proxy_obj(plain_obj_value(), plain_obj_value());
let state = proxy_state_ref(&obj).expect("状态盒"); let state = oxide_builtins::proxy::proxy_state_ref(&obj).expect("状态盒");
assert!(!state.revoked); assert!(!state.revoked);
let state = proxy_state_mut(&mut obj).expect("状态盒"); let state = oxide_builtins::proxy::proxy_state_mut(&mut obj).expect("状态盒");
state.revoked = true; state.revoked = true;
assert!(proxy_state_ref(&obj).expect("状态盒").revoked); assert!(oxide_builtins::proxy::proxy_state_ref(&obj).expect("状态盒").revoked);
} }
} }
@@ -98,8 +98,8 @@ fn stub_proto_member_read_returns_undefined() {
assert!(result.as_bool()); assert!(result.as_bool());
} }
/// 构造体不回归钉:四 stub 构造调用仍抛 TypeError(消息含 "… is not /// 构造体不回归钉:三弱族 stub 构造调用仍抛 TypeError(消息含 "… is not
/// implemented");WeakMap 已落地真构造器,不在此列。 /// implemented");WeakMap 与 Proxy 已落地真构造器,不在此列。
#[test] #[test]
fn stub_ctor_call_still_throws_not_implemented() { fn stub_ctor_call_still_throws_not_implemented() {
let mut vm = Vm::new(); let mut vm = Vm::new();
@@ -113,8 +113,7 @@ fn stub_ctor_call_still_throws_not_implemented() {
} \ } \
return probe(WeakSet, [], 'WeakSet') \ return probe(WeakSet, [], 'WeakSet') \
&& probe(WeakRef, [{}], 'WeakRef') \ && probe(WeakRef, [{}], 'WeakRef') \
&& probe(FinalizationRegistry, [function () {}], 'FinalizationRegistry') \ && probe(FinalizationRegistry, [function () {}], 'FinalizationRegistry'); })()",
&& probe(Proxy, [ {}, {} ], 'Proxy'); })()",
) )
.unwrap(); .unwrap();
assert!(result.as_bool()); assert!(result.as_bool());