mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: 原型面四方法补不变量并改四入口路由
This commit is contained in:
Generated
+21
-21
@@ -1160,7 +1160,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_builtins"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fast-float",
|
||||
@@ -1185,7 +1185,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_bytecode"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_log",
|
||||
@@ -1193,7 +1193,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cfg"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_emit",
|
||||
@@ -1204,7 +1204,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cli"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"bumpalo",
|
||||
@@ -1228,7 +1228,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_code_cache"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"lru",
|
||||
"oxide_bytecode",
|
||||
@@ -1237,7 +1237,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_compiler"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1258,7 +1258,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_dce"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1272,7 +1272,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_emit"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxc_allocator",
|
||||
@@ -1288,7 +1288,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_engine"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1300,7 +1300,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_fold"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_bytecode",
|
||||
@@ -1312,7 +1312,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_ir"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_log",
|
||||
@@ -1321,7 +1321,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_kernel"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1335,7 +1335,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_liveness"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1347,7 +1347,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_log"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
@@ -1356,11 +1356,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_net"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
|
||||
[[package]]
|
||||
name = "oxide_parser"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
@@ -1375,7 +1375,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_regalloc"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1391,7 +1391,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_runtime_api"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
@@ -1404,7 +1404,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_test262"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1424,7 +1424,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_types"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1437,7 +1437,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_vm"
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"chrono",
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.20.14"
|
||||
version = "0.20.15"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -1579,7 +1579,7 @@ fn desc_field_bool<H: VmHost>(vm: &mut H, desc: JsValue, prop_si: u32) -> Result
|
||||
/// [[IsExtensible]](代理感知):代理经 isExtensible trap 路由(实参 target,
|
||||
/// 结果经 ToBoolean 投影,trap 报告须与 target 实际可扩展状态一致,不一致抛
|
||||
/// TypeError),trap 缺失落穿 target;普通对象读自身标志。
|
||||
fn proxy_is_extensible<H: VmHost>(vm: &mut H, obj: &JsObject) -> Result<bool, String> {
|
||||
pub fn proxy_is_extensible<H: VmHost>(vm: &mut H, obj: &JsObject) -> Result<bool, String> {
|
||||
if !obj.is_proxy_obj() {
|
||||
return Ok(obj.is_extensible());
|
||||
}
|
||||
@@ -1943,9 +1943,10 @@ fn seal_own_prop_meta(obj: &mut JsObject, store: u32) {
|
||||
}
|
||||
|
||||
/// 代理 [[PreventExtensions]](规范 ProxyPreventExtensions):preventExtensions
|
||||
/// trap 以(target)调用,结果经 ToBoolean 投影后须与 target 实际可扩展状态
|
||||
/// 一致(不一致抛 TypeError);trap 缺失落穿 target 的普通 preventExtensions
|
||||
/// (置不可扩展标志,返 true)。
|
||||
/// trap 以(target)调用,结果经 ToBoolean 投影;trap 报 true 而 target 可扩展
|
||||
/// 时抛 TypeError(报 false 无论 target 状态均原样返回);trap 缺失落穿
|
||||
/// target 的 [[PreventExtensions]](target 自身为代理时递归再路由,普通对象
|
||||
/// 置不可扩展标志)。
|
||||
pub fn proxy_prevent_extensions<H: VmHost>(vm: &mut H, obj: &JsObject) -> Result<bool, JsValue> {
|
||||
let target = crate::proxy::proxy_state_ref(obj).expect("状态盒").target;
|
||||
// SAFETY: target 由构造器校验为非空对象指针。
|
||||
@@ -1955,32 +1956,45 @@ pub fn proxy_prevent_extensions<H: VmHost>(vm: &mut H, obj: &JsObject) -> Result
|
||||
Err(msg) => return Err(own_keys_trap_exc(vm, msg)),
|
||||
};
|
||||
let Some(r) = trap_result else {
|
||||
// OrdinaryPreventExtensions(target):置不可扩展标志。
|
||||
// 落穿 target 的 [[PreventExtensions]]:target 自身为代理时递归再路由
|
||||
// (其 trap 与落穿语义对 target 生效);普通对象置不可扩展标志。
|
||||
if target_obj.is_proxy_obj() {
|
||||
return proxy_prevent_extensions(vm, target_obj);
|
||||
}
|
||||
// SAFETY: target 由构造器校验为非空对象指针。
|
||||
unsafe { &mut *target.as_js_object_ptr() }.set_extensible(false);
|
||||
return Ok(true);
|
||||
};
|
||||
let result = oxide_runtime_api::to_boolean(r);
|
||||
if result {
|
||||
let target_extensible =
|
||||
proxy_is_extensible(vm, target_obj).map_err(|msg| crate::error::create_from_text(vm, &msg))?;
|
||||
if result != target_extensible {
|
||||
if target_extensible {
|
||||
return Err(crate::error::create_type_error(
|
||||
vm,
|
||||
"Proxy preventExtensions trap result is inconsistent with target",
|
||||
"Proxy preventExtensions trap returned true for an extensible target",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// 代理完整性级别(规范 SetIntegrityLevel):[[PreventExtensions]](代理感知)
|
||||
/// 后 [[OwnPropertyKeys]](代理感知),逐键 DefinePropertyOrThrow(经
|
||||
/// defineProperty trap 路由,trap 缺失落穿 target)。seal 仅置 configurable:
|
||||
/// false;freeze 对数据属性额外置 writable: false(规范步 7b 先读 currentDesc
|
||||
/// 区分数据/访问器,访问器不写 writable 字段)。
|
||||
/// 代理完整性级别(规范 SetIntegrityLevel):[[PreventExtensions]](代理感知,
|
||||
/// 报 false 时按规范抛 TypeError)后 [[OwnPropertyKeys]](代理感知),逐键
|
||||
/// DefinePropertyOrThrow(经 defineProperty trap 路由,trap 缺失落穿 target)。
|
||||
/// seal 仅置 configurable: false;freeze 对数据属性额外置 writable: false
|
||||
/// (规范步 7b 先读 currentDesc 区分数据/访问器,访问器不写 writable 字段)。
|
||||
fn proxy_set_integrity_level<H: VmHost>(vm: &mut H, obj_ptr: *mut JsObject, frozen: bool) -> Result<(), JsValue> {
|
||||
// SAFETY: obj_ptr 为存活对象指针。
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
proxy_prevent_extensions(vm, obj)?;
|
||||
// SetIntegrityLevel 步 3:[[PreventExtensions]] 报 false 时完整性级别
|
||||
// 返回 false,Object.freeze/seal 按规范抛 TypeError。
|
||||
if !proxy_prevent_extensions(vm, obj)? {
|
||||
return Err(crate::error::create_type_error(
|
||||
vm,
|
||||
"SetIntegrityLevel: [[PreventExtensions]] returned false",
|
||||
));
|
||||
}
|
||||
let (keys, _storage) = own_keys_proxy_aware(vm, obj)?;
|
||||
let cfg_si = vm.perm_intern("configurable");
|
||||
let writable_si = vm.perm_intern("writable");
|
||||
@@ -2138,6 +2152,10 @@ pub fn object_seal<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
|
||||
/// `Object.preventExtensions(obj)`:禁止添加新属性,返回原对象。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 代理经 preventExtensions trap 路由(缺失落穿 target);`[[PreventExtensions]]`
|
||||
/// 报 false 时按规范抛 TypeError。
|
||||
pub fn object_prevent_extensions<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "Object.preventExtensions called on non-object"));
|
||||
@@ -2147,10 +2165,16 @@ pub fn object_prevent_extensions<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeRe
|
||||
return NativeResult::Ok(val);
|
||||
}
|
||||
let obj_ptr = val.as_js_object_ptr();
|
||||
unsafe {
|
||||
(*obj_ptr).set_extensible(false);
|
||||
match vm.prevent_extensions(unsafe { &mut *obj_ptr }) {
|
||||
Ok(true) => NativeResult::Ok(val),
|
||||
// [[PreventExtensions]] 报 false:规范 20.1.2.20 步 3 抛 TypeError。
|
||||
Ok(false) => NativeResult::Err(crate::error::create_type_error(
|
||||
vm,
|
||||
"Object.preventExtensions: [[PreventExtensions]] returned false",
|
||||
)),
|
||||
// 代理 trap 异常原值传播(ReturnIfAbrupt)。
|
||||
Err(err) => NativeResult::Err(crate::array::from_engine_error(vm, &err)),
|
||||
}
|
||||
NativeResult::Ok(val)
|
||||
}
|
||||
|
||||
/// `Object.isFrozen(obj)`:对象是否冻结(检查 extensible 及全部属性 writable/configurable)。
|
||||
@@ -2236,6 +2260,10 @@ pub fn object_is_sealed<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
|
||||
/// `Object.isExtensible(obj)`:对象是否可扩展。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 代理经 isExtensible trap 路由(缺失落穿 target,报告与 target 状态
|
||||
/// 不一致抛 TypeError);普通对象读自身标志。
|
||||
pub fn object_is_extensible<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "Object.isExtensible called on non-object"));
|
||||
@@ -2248,8 +2276,11 @@ pub fn object_is_extensible<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
if obj_ptr.is_null() {
|
||||
return NativeResult::Ok(JsValue::bool(false));
|
||||
}
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
NativeResult::Ok(JsValue::bool(obj.is_extensible()))
|
||||
match vm.is_extensible(unsafe { &*obj_ptr }) {
|
||||
Ok(extensible) => NativeResult::Ok(JsValue::bool(extensible)),
|
||||
// 代理 trap 异常原值传播(ReturnIfAbrupt)。
|
||||
Err(err) => NativeResult::Err(crate::array::from_engine_error(vm, &err)),
|
||||
}
|
||||
}
|
||||
|
||||
/// `Object.getOwnPropertyNames(obj)`:返回全部自身属性名(含不可枚举)。
|
||||
|
||||
@@ -70,54 +70,17 @@ fn check_target_and_handler(target: JsValue, handler: JsValue) -> Result<(), &'s
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 不变量检查(规范 10.2.4 步骤 1、4-5):target 可调用时须可扩展,handler
|
||||
/// 的 isExtensible 与 setPrototypeOf trap 若存在,其布尔值须与 target 实际
|
||||
/// 状态一致,否则抛 TypeError。
|
||||
/// 不变量检查(规范 10.5.15 ProxyCreate 步 1-2):target 可调用且不可扩展时
|
||||
/// 抛 TypeError。构造器不读 handler 的 trap 属性(现代规范无构造期 trap 一致性
|
||||
/// 检查,不变量在内部方法调用期执行,getter 抛错不得在构造期触发)。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 仅当 handler 自有对应 trap 属性时检查(继承属性不触发)。
|
||||
/// - trap 读为传播读,getter 抛错原值上抛。
|
||||
fn proxy_create_invariants<H: VmHost>(
|
||||
vm: &mut H, target: JsValue, target_obj: &JsObject, handler_obj: &JsObject, handler: JsValue,
|
||||
) -> Result<(), JsValue> {
|
||||
/// - 仅当 target 可调用时检查可扩展性。
|
||||
fn proxy_create_invariants<H: VmHost>(vm: &mut H, target: JsValue, target_obj: &JsObject) -> Result<(), JsValue> {
|
||||
// target 可调用且不可扩展:函数代理的不变量无法满足,抛 TypeError。
|
||||
if crate::iterator::is_callable(target) && !target_obj.is_extensible() {
|
||||
return Err(crate::error::create_type_error(vm, "Proxy target must be extensible if it is callable"));
|
||||
}
|
||||
// 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(())
|
||||
}
|
||||
|
||||
@@ -146,7 +109,7 @@ fn create_proxy_object<H: VmHost>(vm: &mut H, target: JsValue, handler: JsValue)
|
||||
/// # 步骤
|
||||
/// 1. NewTarget 校验:非构造形态调用抛 TypeError。
|
||||
/// 2. 参数校验:target 与 handler 均须对象。
|
||||
/// 3. 不变量检查:handler 的 isExtensible / setPrototypeOf trap 与 target 一致性。
|
||||
/// 3. 不变量检查:target 可调用时须可扩展(构造器不读 handler trap 属性)。
|
||||
/// 4. 创建代理对象(tag 39 + 状态盒 + Object.prototype 原型链)。
|
||||
///
|
||||
/// # 边界与前提
|
||||
@@ -164,8 +127,7 @@ pub fn proxy_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
// 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, target_obj, handler_obj, handler) {
|
||||
if let Err(exc) = proxy_create_invariants(vm, target, target_obj) {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
NativeResult::Ok(create_proxy_object(vm, target, handler))
|
||||
@@ -191,8 +153,7 @@ pub fn proxy_revocable<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
// 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, target_obj, handler_obj, handler) {
|
||||
if let Err(exc) = proxy_create_invariants(vm, target, target_obj) {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
let proxy_val = create_proxy_object(vm, target, handler);
|
||||
|
||||
@@ -233,12 +233,20 @@ pub fn reflect_has<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
|
||||
/// `Reflect.isExtensible(target)`:对象是否可扩展。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 代理经 isExtensible trap 路由(缺失落穿 target,报告与 target 状态
|
||||
/// 不一致抛 TypeError);普通对象读自身标志。
|
||||
pub fn reflect_is_extensible<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let target_val = arg(vm, args, 1);
|
||||
let Some(target_ptr) = object_ptr(target_val) else {
|
||||
return type_error(vm, "Reflect.isExtensible target is not an object");
|
||||
};
|
||||
NativeResult::Ok(JsValue::bool(unsafe { &*target_ptr }.is_extensible()))
|
||||
match vm.is_extensible(unsafe { &*target_ptr }) {
|
||||
Ok(extensible) => NativeResult::Ok(JsValue::bool(extensible)),
|
||||
// 代理 trap 异常原值传播(ReturnIfAbrupt)。
|
||||
Err(msg) => NativeResult::Err(from_engine_error(vm, &msg)),
|
||||
}
|
||||
}
|
||||
|
||||
/// `Reflect.ownKeys(target)`:返回对象全部自身属性键,字符串键在前、Symbol 键在后。
|
||||
@@ -297,14 +305,22 @@ pub fn reflect_own_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::from_js_object(arr))
|
||||
}
|
||||
|
||||
/// `Reflect.preventExtensions(target)`:阻止扩展,返回 true。
|
||||
/// `Reflect.preventExtensions(target)`:阻止扩展,返回 `[[PreventExtensions]]`
|
||||
/// 的布尔结果。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 代理经 preventExtensions trap 路由(缺失落穿 target);结果按规范
|
||||
/// 原样返回(非恒 true)。
|
||||
pub fn reflect_prevent_extensions<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let target_val = arg(vm, args, 1);
|
||||
let Some(target_ptr) = object_ptr(target_val) else {
|
||||
return type_error(vm, "Reflect.preventExtensions target is not an object");
|
||||
};
|
||||
unsafe { &mut *target_ptr }.set_extensible(false);
|
||||
NativeResult::Ok(JsValue::bool(true))
|
||||
match vm.prevent_extensions(unsafe { &mut *target_ptr }) {
|
||||
Ok(status) => NativeResult::Ok(JsValue::bool(status)),
|
||||
// 代理 trap 异常原值传播(ReturnIfAbrupt)。
|
||||
Err(msg) => NativeResult::Err(from_engine_error(vm, &msg)),
|
||||
}
|
||||
}
|
||||
|
||||
/// `Reflect.set(target, key, value, receiver)`:按 OrdinarySetWithReceiver 语义写入属性。
|
||||
|
||||
@@ -330,7 +330,8 @@ pub trait VmHost {
|
||||
// 原型与扩展性面(代理感知路由:代理经 trap 路由、非代理委托固有语义)
|
||||
/// `[[GetPrototypeOf]]`:返回对象的 prototype(对象或 null);代理经
|
||||
/// getPrototypeOf trap 路由——缺失落穿 target 的 `[[GetPrototypeOf]]`,
|
||||
/// trap 返值须为对象或 null,否则抛 TypeError。
|
||||
/// trap 返值须为对象或 null,否则抛 TypeError;target 不可扩展时 trap
|
||||
/// 结果须与 target 原型 SameValue,否则抛 TypeError。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - `obj` 须为存活对象;代理的 target 由构造器校验为非空对象指针。
|
||||
@@ -352,18 +353,24 @@ pub trait VmHost {
|
||||
/// - 可能执行用户 trap 代码(含分配与 GC);revoked 抛 TypeError。
|
||||
fn set_prototype_of(&mut self, obj: &mut JsObject, proto: JsValue) -> Result<bool, String>;
|
||||
/// `[[IsExtensible]]`:返回可扩展标志;代理经 isExtensible trap 路由
|
||||
/// (结果经布尔投影),trap 缺失落穿 target。
|
||||
/// (结果经布尔投影,报告须与 target 实际可扩展状态一致,不一致抛
|
||||
/// TypeError),trap 缺失落穿 target。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 可能执行用户 trap 代码(含分配与 GC);revoked 抛 TypeError。
|
||||
fn is_extensible(&mut self, obj: &JsObject) -> Result<bool, String>;
|
||||
/// `[[PreventExtensions]]`:置不可扩展标志;代理经 preventExtensions
|
||||
/// trap 路由(结果经布尔投影,报告须与 target 实际可扩展状态一致),
|
||||
/// trap 缺失落穿 target 的普通 preventExtensions。
|
||||
/// `[[PreventExtensions]]`:置不可扩展标志并返回状态(普通对象恒 true;
|
||||
/// 代理经 preventExtensions trap 路由,结果经布尔投影,trap 报 true 而
|
||||
/// target 可扩展时抛 TypeError),trap 缺失落穿 target 的普通
|
||||
/// preventExtensions。
|
||||
///
|
||||
/// # 返回值
|
||||
/// - `Ok(true)` 成功置不可扩展;`Ok(false)` 代理 trap 报 false(未置位);
|
||||
/// `Err` 为 TypeError 或 trap 异常。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 可能执行用户 trap 代码(含分配与 GC);revoked 抛 TypeError。
|
||||
fn prevent_extensions(&mut self, obj: &mut JsObject) -> Result<(), String>;
|
||||
fn prevent_extensions(&mut self, obj: &mut JsObject) -> Result<bool, String>;
|
||||
/// `[[OwnPropertyKeys]]`:返回自身属性键列表(JS 可见键值,整数键升序
|
||||
/// 在前、字符串键插入序、Symbol 键插入序);代理经 ownKeys trap 路由
|
||||
/// (缺失落穿 target 自身键,重复键与不可配置键做不变量检查)。
|
||||
|
||||
+146
-31
@@ -704,12 +704,41 @@ mod tests {
|
||||
"isExtensible trap 须命中"
|
||||
);
|
||||
|
||||
// preventExtensions:trap 报 true(与 target 可扩展状态一致)。
|
||||
// ownKeys:trap 返数组,原样透传。
|
||||
let keys = vm.own_keys(proxy_ref).expect("own_keys 应成功");
|
||||
assert_eq!(keys.len(), 1, "ownKeys trap 返值须为单元素数组");
|
||||
assert_eq!(vm.lookup_str(keys[0]).as_deref(), Some("k"), "ownKeys trap 返值须原样透传");
|
||||
assert_eq!(vm.resolve_property(handler_ref, hits_si).unwrap(), JsValue::int(3), "ownKeys trap 须命中");
|
||||
|
||||
// getOwnPropertyDescriptor:trap 返描述符对象,原样透传。
|
||||
let desc = vm
|
||||
.get_own_property_desc(proxy_ref, key_si)
|
||||
.expect("get_own_property_desc 应成功");
|
||||
assert!(desc.is_some(), "getOwnPropertyDescriptor trap 须返描述符");
|
||||
assert_eq!(
|
||||
vm.resolve_property(handler_ref, hits_si).unwrap(),
|
||||
JsValue::int(4),
|
||||
"getOwnPropertyDescriptor trap 须命中"
|
||||
);
|
||||
|
||||
// deleteProperty:trap 报 true,布尔投影为 true。
|
||||
assert!(
|
||||
vm.delete_own(unsafe { &mut *proxy }, key_si).expect("delete_own 应成功"),
|
||||
"deleteProperty trap 报 true 须投影为 true"
|
||||
);
|
||||
assert_eq!(
|
||||
vm.resolve_property(handler_ref, hits_si).unwrap(),
|
||||
JsValue::int(5),
|
||||
"deleteProperty trap 须命中"
|
||||
);
|
||||
|
||||
// preventExtensions:trap 报 true(与 target 不可扩展状态一致)。
|
||||
unsafe { (*target).set_extensible(false) };
|
||||
vm.prevent_extensions(unsafe { &mut *proxy })
|
||||
.expect("prevent_extensions 应成功");
|
||||
assert_eq!(
|
||||
vm.resolve_property(handler_ref, hits_si).unwrap(),
|
||||
JsValue::int(3),
|
||||
JsValue::int(6),
|
||||
"preventExtensions trap 须命中"
|
||||
);
|
||||
|
||||
@@ -719,38 +748,10 @@ mod tests {
|
||||
.expect("set_prototype_of 应成功"),
|
||||
"setPrototypeOf trap 报 true 须投影为 true"
|
||||
);
|
||||
assert_eq!(
|
||||
vm.resolve_property(handler_ref, hits_si).unwrap(),
|
||||
JsValue::int(4),
|
||||
"setPrototypeOf trap 须命中"
|
||||
);
|
||||
|
||||
// ownKeys:trap 返数组,原样透传。
|
||||
let keys = vm.own_keys(proxy_ref).expect("own_keys 应成功");
|
||||
assert_eq!(keys.len(), 1, "ownKeys trap 返值须为单元素数组");
|
||||
assert_eq!(vm.lookup_str(keys[0]).as_deref(), Some("k"), "ownKeys trap 返值须原样透传");
|
||||
assert_eq!(vm.resolve_property(handler_ref, hits_si).unwrap(), JsValue::int(5), "ownKeys trap 须命中");
|
||||
|
||||
// getOwnPropertyDescriptor:trap 返描述符对象,原样透传。
|
||||
let desc = vm
|
||||
.get_own_property_desc(proxy_ref, key_si)
|
||||
.expect("get_own_property_desc 应成功");
|
||||
assert!(desc.is_some(), "getOwnPropertyDescriptor trap 须返描述符");
|
||||
assert_eq!(
|
||||
vm.resolve_property(handler_ref, hits_si).unwrap(),
|
||||
JsValue::int(6),
|
||||
"getOwnPropertyDescriptor trap 须命中"
|
||||
);
|
||||
|
||||
// deleteProperty:trap 报 true,布尔投影为 true。
|
||||
assert!(
|
||||
vm.delete_own(unsafe { &mut *proxy }, key_si).expect("delete_own 应成功"),
|
||||
"deleteProperty trap 报 true 须投影为 true"
|
||||
);
|
||||
assert_eq!(
|
||||
vm.resolve_property(handler_ref, hits_si).unwrap(),
|
||||
JsValue::int(7),
|
||||
"deleteProperty trap 须命中"
|
||||
"setPrototypeOf trap 须命中"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -812,6 +813,120 @@ mod tests {
|
||||
assert!(err.contains("TypeError"), "须抛 TypeError:{err}");
|
||||
}
|
||||
|
||||
/// setPrototypeOf 不变量(规范 10.5.2 步 10-12):trap 报 true 且 target
|
||||
/// 不可扩展时,新 proto 与 target 原型非 SameValue 抛 TypeError;target
|
||||
/// 可扩展时直接成功(不读 target 原型)。
|
||||
#[test]
|
||||
fn set_prototype_of_invariant_by_extensibility() {
|
||||
let mut vm = Vm::new();
|
||||
let target = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
|
||||
unsafe { (*target).set_proto(JsValue::null()).expect("set_proto 应成功") };
|
||||
unsafe { (*target).set_extensible(false) };
|
||||
let handler = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
|
||||
let trap_fn = native_fn_obj(&mut vm, counting_trap);
|
||||
vm.set_or_create_prop_value(
|
||||
unsafe { &mut *handler },
|
||||
vm.perm_intern("setPrototypeOf"),
|
||||
JsValue::from_js_object(trap_fn),
|
||||
);
|
||||
let proxy = proxy_with_state(&mut vm, JsValue::from_js_object(target), JsValue::from_js_object(handler));
|
||||
// 异 proto:不可扩展 target 下 trap 报 true 须抛 TypeError。
|
||||
let other = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
|
||||
let err = vm
|
||||
.set_prototype_of(unsafe { &mut *proxy }, JsValue::from_js_object(other))
|
||||
.expect_err("须抛 TypeError");
|
||||
assert!(err.contains("TypeError"), "须抛 TypeError:{err}");
|
||||
// 同 proto(null):不变量通过,返 true。
|
||||
assert!(
|
||||
vm.set_prototype_of(unsafe { &mut *proxy }, JsValue::null())
|
||||
.expect("set_prototype_of 应成功"),
|
||||
"同 proto 须返 true"
|
||||
);
|
||||
}
|
||||
|
||||
/// setPrototypeOf 可扩展早返(规范 10.5.2 步 10-11):target 可扩展时
|
||||
/// trap 报 true 直接成功,不触 target 的 [[GetPrototypeOf]](其 trap 抛
|
||||
/// 异常值验证未调用)。
|
||||
#[test]
|
||||
fn set_prototype_of_extensible_target_skips_target_proto_read() {
|
||||
let mut vm = Vm::new();
|
||||
// target 为代理且可扩展:其 getPrototypeOf trap 抛错。
|
||||
fn throwing_get_prototype_trap(vm: &mut Vm, _args: &[u8]) -> NativeResult {
|
||||
let exc = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
|
||||
NativeResult::Err(JsValue::from_js_object(exc))
|
||||
}
|
||||
let inner_target = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
|
||||
let inner_handler = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
|
||||
let throw_fn = native_fn_obj(&mut vm, throwing_get_prototype_trap);
|
||||
vm.set_or_create_prop_value(
|
||||
unsafe { &mut *inner_handler },
|
||||
vm.perm_intern("getPrototypeOf"),
|
||||
JsValue::from_js_object(throw_fn),
|
||||
);
|
||||
let target =
|
||||
proxy_with_state(&mut vm, JsValue::from_js_object(inner_target), JsValue::from_js_object(inner_handler));
|
||||
let handler = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
|
||||
let trap_fn = native_fn_obj(&mut vm, counting_trap);
|
||||
vm.set_or_create_prop_value(
|
||||
unsafe { &mut *handler },
|
||||
vm.perm_intern("setPrototypeOf"),
|
||||
JsValue::from_js_object(trap_fn),
|
||||
);
|
||||
let proxy = proxy_with_state(&mut vm, JsValue::from_js_object(target), JsValue::from_js_object(handler));
|
||||
let new_proto = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
|
||||
assert!(
|
||||
vm.set_prototype_of(unsafe { &mut *proxy }, JsValue::from_js_object(new_proto))
|
||||
.expect("可扩展 target 下 trap 报 true 须成功"),
|
||||
"须返 true"
|
||||
);
|
||||
}
|
||||
|
||||
/// preventExtensions 不变量(规范 10.5.4 步 9):trap 报 true 而 target
|
||||
/// 可扩展抛 TypeError;target 不可扩展时返 true。
|
||||
#[test]
|
||||
fn prevent_extensions_invariant_by_extensibility() {
|
||||
let mut vm = Vm::new();
|
||||
let target = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
|
||||
let handler = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
|
||||
let trap_fn = native_fn_obj(&mut vm, counting_trap);
|
||||
vm.set_or_create_prop_value(
|
||||
unsafe { &mut *handler },
|
||||
vm.perm_intern("preventExtensions"),
|
||||
JsValue::from_js_object(trap_fn),
|
||||
);
|
||||
let proxy = proxy_with_state(&mut vm, JsValue::from_js_object(target), JsValue::from_js_object(handler));
|
||||
// 可扩展 target + trap 报 true:抛 TypeError。
|
||||
let err = vm.prevent_extensions(unsafe { &mut *proxy }).expect_err("须抛 TypeError");
|
||||
assert!(err.contains("TypeError"), "须抛 TypeError:{err}");
|
||||
// 不可扩展 target + trap 报 true:返 true。
|
||||
unsafe { (*target).set_extensible(false) };
|
||||
assert!(
|
||||
vm.prevent_extensions(unsafe { &mut *proxy })
|
||||
.expect("prevent_extensions 应成功"),
|
||||
"target 不可扩展时 trap 报 true 须返 true"
|
||||
);
|
||||
}
|
||||
|
||||
/// getPrototypeOf 不变量(规范 10.5.1 步 9-12):target 不可扩展且 trap
|
||||
/// 结果与 target 原型非 SameValue 抛 TypeError。
|
||||
#[test]
|
||||
fn get_prototype_of_invariant_on_non_extensible_target() {
|
||||
let mut vm = Vm::new();
|
||||
let target = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
|
||||
unsafe { (*target).set_proto(JsValue::null()).expect("set_proto 应成功") };
|
||||
unsafe { (*target).set_extensible(false) };
|
||||
let handler = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()));
|
||||
let trap_fn = native_fn_obj(&mut vm, counting_get_prototype_trap);
|
||||
vm.set_or_create_prop_value(
|
||||
unsafe { &mut *handler },
|
||||
vm.perm_intern("getPrototypeOf"),
|
||||
JsValue::from_js_object(trap_fn),
|
||||
);
|
||||
let proxy = proxy_with_state(&mut vm, JsValue::from_js_object(target), JsValue::from_js_object(handler));
|
||||
let err = vm.get_prototype_of(unsafe { &*proxy }).expect_err("须抛 TypeError");
|
||||
assert!(err.contains("TypeError"), "须抛 TypeError:{err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn proxy_native_edges_return_target_and_handler() {
|
||||
let target = plain_obj_value();
|
||||
|
||||
@@ -278,6 +278,18 @@ impl oxide_runtime_api::VmHost for Vm {
|
||||
"a [[GetPrototypeOf]] trap returned a non-object and non-null value",
|
||||
));
|
||||
}
|
||||
// 不变量:target 不可扩展时,trap 结果须与 target 的
|
||||
// [[GetPrototypeOf]] 结果 SameValue(规范 10.5.1 步 9-12)。
|
||||
if !self.is_extensible(unsafe { &*target.as_js_object_ptr() })? {
|
||||
// SAFETY: target 由构造器校验为非空对象指针。
|
||||
let target_proto = self.get_prototype_of(unsafe { &*target.as_js_object_ptr() })?;
|
||||
if !oxide_runtime_api::same_value(result, target_proto) {
|
||||
return Err(oxide_runtime_api::format_error_message(
|
||||
"TypeError",
|
||||
"a [[GetPrototypeOf]] trap returned a value different from the non-extensible target's prototype",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
// SAFETY: target 由构造器校验为非空对象指针。
|
||||
@@ -310,39 +322,38 @@ impl oxide_runtime_api::VmHost for Vm {
|
||||
if !crate::proxy::trap_bool_result(trap_result, "setPrototypeOf")? {
|
||||
return Ok(false);
|
||||
}
|
||||
// 不变量检查:trap 报 true 时 target 的 prototype 须为对象或 null。
|
||||
// 不变量:trap 报 true 且 target 不可扩展时,proto 须与 target 的
|
||||
// [[GetPrototypeOf]] 结果 SameValue(规范 10.5.2 步 10-12);target
|
||||
// 可扩展时直接成功,不读 target 原型。
|
||||
if !self.is_extensible(unsafe { &*target.as_js_object_ptr() })? {
|
||||
// SAFETY: target 由构造器校验为非空对象指针。
|
||||
let target_proto = self.get_prototype_of(unsafe { &*target.as_js_object_ptr() })?;
|
||||
if !target_proto.is_object() && !target_proto.is_null() {
|
||||
if !oxide_runtime_api::same_value(proto, target_proto) {
|
||||
return Err(oxide_runtime_api::format_error_message(
|
||||
"TypeError",
|
||||
"a [[SetPrototypeOf]] trap returned true for a non-object and non-null target prototype",
|
||||
"a [[SetPrototypeOf]] trap returned true for a non-extensible target with a different prototype",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
fn is_extensible(&mut self, obj: &JsObject) -> Result<bool, String> {
|
||||
if !obj.is_proxy_obj() {
|
||||
return Ok(obj.is_extensible());
|
||||
}
|
||||
// 代理路径:isExtensible trap(实参 target),结果经布尔投影,缺失
|
||||
// 落穿 target(target 自身为代理时经递归再路由)。
|
||||
let target = oxide_builtins::proxy::proxy_state_ref(obj).expect("状态盒").target;
|
||||
match crate::proxy::call_trap(self, obj, self.perm_intern("isExtensible"), &[target])? {
|
||||
Some(result) => Ok(oxide_runtime_api::to_boolean(result)),
|
||||
// SAFETY: target 由构造器校验为非空对象指针。
|
||||
None => self.is_extensible(unsafe { &*target.as_js_object_ptr() }),
|
||||
// 代理路径:isExtensible trap 路由(实参 target,结果经布尔投影,
|
||||
// 报告须与 target 实际可扩展状态一致),缺失落穿 target。
|
||||
oxide_builtins::object::proxy_is_extensible(self, obj)
|
||||
}
|
||||
}
|
||||
fn prevent_extensions(&mut self, obj: &mut JsObject) -> Result<(), String> {
|
||||
fn prevent_extensions(&mut self, obj: &mut JsObject) -> Result<bool, String> {
|
||||
if !obj.is_proxy_obj() {
|
||||
obj.set_extensible(false);
|
||||
return Ok(());
|
||||
return Ok(true);
|
||||
}
|
||||
// 代理路径:preventExtensions trap 路由(含报告与 target 可扩展状态
|
||||
// 一致性检查),缺失落穿 target 的普通 preventExtensions。
|
||||
match oxide_builtins::object::proxy_prevent_extensions(self, obj) {
|
||||
Ok(_) => Ok(()),
|
||||
Ok(status) => Ok(status),
|
||||
// 还原原始异常值入 uncaught 侧通道(保身份)。
|
||||
Err(exc) => {
|
||||
self.last_uncaught_value = Some(exc);
|
||||
|
||||
Reference in New Issue
Block a user