fix: Reflect 族属性键转换异常原值传播接传播变体

This commit is contained in:
2026-09-23 22:30:55 +00:00
parent 9a161290f3
commit c8fda6132b
4 changed files with 86 additions and 9 deletions
+11 -1
View File
@@ -1115,7 +1115,17 @@ pub fn object_get_own_property_descriptor<H: VmHost>(vm: &mut H, args: &[u8]) ->
Err(msg) => return NativeResult::Err(crate::error::create_type_error(vm, &msg)),
};
let obj_ptr = obj_val.as_js_object_ptr();
let key = vm.property_key_si(vm.reg(args[2]));
// 键转换异常原值传播(ToPropertyKey 步先于 GetOwnProperty):Reflect 委托面
// 与 Object 同核,须按规范传播。
let key = match vm.to_property_key_si(vm.reg(args[2])) {
Ok(si) => si,
Err(e) => {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &e));
return NativeResult::Err(exc);
}
};
let obj = unsafe { &*obj_ptr };
match own_descriptor_of(vm, obj, key) {
Ok(Some(desc)) => NativeResult::Ok(desc),
+25 -5
View File
@@ -88,12 +88,16 @@ pub fn reflect_define_property<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResu
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.defineProperty target is not an object");
};
// 规范步序:键转换(ToPropertyKey)先于 descriptor 类型检;键转换异常原值传播。
let key_si = match vm.to_property_key_si(arg(vm, args, 2)) {
Ok(si) => si,
Err(e) => return NativeResult::Err(from_engine_error(vm, &e)),
};
let desc_val = arg(vm, args, 3);
let Some(_) = object_ptr(desc_val) else {
return type_error(vm, "Reflect.defineProperty descriptor is not an object");
};
let key_si = vm.property_key_si(arg(vm, args, 2));
match crate::object::define_from_descriptor(vm, target_ptr, key_si, desc_val) {
Ok(()) => NativeResult::Ok(JsValue::bool(true)),
Err(msg) => {
@@ -117,7 +121,11 @@ pub fn reflect_delete_property<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResu
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.deleteProperty target is not an object");
};
let key_si = vm.property_key_si(arg(vm, args, 2));
// 键转换异常原值传播(ToPropertyKey 步先于 DeleteOwnProperty)。
let key_si = match vm.to_property_key_si(arg(vm, args, 2)) {
Ok(si) => si,
Err(e) => return NativeResult::Err(from_engine_error(vm, &e)),
};
let target = unsafe { &mut *target_ptr };
NativeResult::Ok(JsValue::bool(delete_own_property(vm, target, key_si)))
}
@@ -128,7 +136,11 @@ pub fn reflect_get<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.get target is not an object");
};
let key_si = vm.property_key_si(arg(vm, args, 2));
// 键转换异常原值传播(ToPropertyKey 步先于 Get)。
let key_si = match vm.to_property_key_si(arg(vm, args, 2)) {
Ok(si) => si,
Err(e) => return NativeResult::Err(from_engine_error(vm, &e)),
};
let receiver = if args.len() > 3 { vm.reg(args[3]) } else { target_val };
match vm.ordinary_get(unsafe { &*target_ptr }, key_si, receiver) {
Ok(value) => NativeResult::Ok(value),
@@ -165,7 +177,11 @@ pub fn reflect_has<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.has target is not an object");
};
let key_si = vm.property_key_si(arg(vm, args, 2));
// 键转换异常原值传播(ToPropertyKey 步先于 HasProperty)。
let key_si = match vm.to_property_key_si(arg(vm, args, 2)) {
Ok(si) => si,
Err(e) => return NativeResult::Err(from_engine_error(vm, &e)),
};
NativeResult::Ok(JsValue::bool(vm.has_property(unsafe { &*target_ptr }, key_si)))
}
@@ -241,7 +257,11 @@ pub fn reflect_set<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.set target is not an object");
};
let key_si = vm.property_key_si(arg(vm, args, 2));
// 键转换异常原值传播(ToPropertyKey 步先于 Set)。
let key_si = match vm.to_property_key_si(arg(vm, args, 2)) {
Ok(si) => si,
Err(e) => return NativeResult::Err(from_engine_error(vm, &e)),
};
let value = arg(vm, args, 3);
let receiver = if args.len() > 4 { vm.reg(args[4]) } else { target_val };
let target = unsafe { &mut *target_ptr };
+4 -3
View File
@@ -68,9 +68,10 @@ impl oxide_runtime_api::VmHost for Vm {
self.pending_length_exception = self.last_uncaught_value.take();
}
fn property_key_si(&mut self, val: JsValue) -> u32 {
// 对象键转换(to_string_full)失败时降级为空键:本 trait 路径供
// Reflect/Object 内置使用,这些内置按规范不传播键转换异常;计算属性
// 访问走返回 Result 的 inherent 版本,转换异常原样保留。
// 对象键转换(to_string_full)失败时降级为空键:本 trait 路径供不传播
// 键转换异常的内置站点(Object defineProperty/fromEntries/hasOwn、
// 数值串内部键等)使用;按规范须传播异常的站点用返回 Result 的
// `to_property_key_si`,转换异常原样保留。
self.property_key_si(val)
.unwrap_or_else(|_| self.kernel_core.perm_interner().intern("").0)
}
@@ -306,6 +306,52 @@ fn reflect_construct_guard_face() {
assert!(result.as_bool());
}
// 键转换异常原值传播:Reflect.has 的键 ToPropertyKey 抛 "X" → 捕获值 === "X"。
#[test]
fn reflect_has_propagates_key_conversion_original_value() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var key = { toString: function() { throw 'X'; } }; \
var caught; \
try { Reflect.has({}, key); caught = 'none'; } catch (e) { caught = e; } \
caught === 'X'",
)
.unwrap();
assert!(result.as_bool());
}
// 同族探针:Reflect.get 的键转换异常同式原值传播。
#[test]
fn reflect_get_propagates_key_conversion_original_value() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var key = { toString: function() { throw 'X'; } }; \
var caught; \
try { Reflect.get({}, key); caught = 'none'; } catch (e) { caught = e; } \
caught === 'X'",
)
.unwrap();
assert!(result.as_bool());
}
// 守卫序钉:defineProperty 键转换先于 descriptor 类型检——descriptor 缺省且
// 键抛 "X" 时捕获的必须是键异常原值,不是 descriptor TypeError。
#[test]
fn reflect_define_property_key_evaluates_before_descriptor_check() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var key = { toString: function() { throw 'X'; } }; \
var caught; \
try { Reflect.defineProperty({}, key, undefined); caught = 'none'; } catch (e) { caught = e; } \
caught === 'X'",
)
.unwrap();
assert!(result.as_bool());
}
// 227.1 回归守卫:隐式 super 面经 `new` 直接构造不回归。
#[test]
fn construct_direct_new_derived_array_guard() {