From c8fda6132be097763b56138c164c7bd34ac70de8 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Wed, 23 Sep 2026 22:30:55 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20Reflect=20=E6=97=8F=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E9=94=AE=E8=BD=AC=E6=8D=A2=E5=BC=82=E5=B8=B8=E5=8E=9F=E5=80=BC?= =?UTF-8?q?=E4=BC=A0=E6=92=AD=E6=8E=A5=E4=BC=A0=E6=92=AD=E5=8F=98=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/object.rs | 12 ++++- crates/oxide_builtins/src/reflect.rs | 30 ++++++++++-- crates/oxide_vm/src/vm/vmhost.rs | 7 +-- .../oxide_vm/tests/builtins_reflect_tests.rs | 46 +++++++++++++++++++ 4 files changed, 86 insertions(+), 9 deletions(-) diff --git a/crates/oxide_builtins/src/object.rs b/crates/oxide_builtins/src/object.rs index 40de67e4..4a309dd2 100644 --- a/crates/oxide_builtins/src/object.rs +++ b/crates/oxide_builtins/src/object.rs @@ -1115,7 +1115,17 @@ pub fn object_get_own_property_descriptor(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), diff --git a/crates/oxide_builtins/src/reflect.rs b/crates/oxide_builtins/src/reflect.rs index a5cf16e3..6ba4036b 100644 --- a/crates/oxide_builtins/src/reflect.rs +++ b/crates/oxide_builtins/src/reflect.rs @@ -88,12 +88,16 @@ pub fn reflect_define_property(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(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(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(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(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 }; diff --git a/crates/oxide_vm/src/vm/vmhost.rs b/crates/oxide_vm/src/vm/vmhost.rs index e8fb2913..9651fec4 100644 --- a/crates/oxide_vm/src/vm/vmhost.rs +++ b/crates/oxide_vm/src/vm/vmhost.rs @@ -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) } diff --git a/crates/oxide_vm/tests/builtins_reflect_tests.rs b/crates/oxide_vm/tests/builtins_reflect_tests.rs index 8daa5f31..64ad9711 100644 --- a/crates/oxide_vm/tests/builtins_reflect_tests.rs +++ b/crates/oxide_vm/tests/builtins_reflect_tests.rs @@ -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() {