From 3fb3ebc3cc6eec4c1fb1def9ac4aa7fee2960c3c Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 20 Sep 2026 15:13:18 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20Reflect.construct=20=E6=8D=A2=E7=9C=9F?= =?UTF-8?q?=E6=9E=84=E9=80=A0=E5=B8=A7=E5=B9=B6=E6=94=AF=E6=8C=81=20newTar?= =?UTF-8?q?get=20=E8=A6=86=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/reflect.rs | 72 ++++--- crates/oxide_runtime_api/src/host.rs | 13 ++ crates/oxide_vm/src/promise/constructor.rs | 34 +++- crates/oxide_vm/src/vm/vmhost.rs | 3 + .../oxide_vm/tests/builtins_reflect_tests.rs | 188 ++++++++++++++++++ 5 files changed, 269 insertions(+), 41 deletions(-) diff --git a/crates/oxide_builtins/src/reflect.rs b/crates/oxide_builtins/src/reflect.rs index 12449fbf..835e9108 100644 --- a/crates/oxide_builtins/src/reflect.rs +++ b/crates/oxide_builtins/src/reflect.rs @@ -1,11 +1,11 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; -use oxide_types::mem::P; use oxide_types::object::JsObject; use oxide_types::value::JsValue; +use crate::array::{arraylike_get, from_engine_error, is_constructor_value}; use crate::object::{delete_own_property, key_si_to_js_value, own_symbol_key_values, walk_own_keys}; -use oxide_runtime_api::{NativeResult, VmHost}; +use oxide_runtime_api::{to_length, NativeResult, VmHost}; /// `Reflect.apply(target, thisArgument, argumentsList)`:以指定 this 与参数数组调用函数。 pub fn reflect_apply(vm: &mut H, args: &[u8]) -> NativeResult { @@ -24,47 +24,53 @@ pub fn reflect_apply(vm: &mut H, args: &[u8]) -> NativeResult { } } -/// `Reflect.construct(target, argumentsList, newTarget)`:以 newTarget 的 prototype -/// 分配 this 后调用 target;返回值非对象时回退到新建的 this。 +/// `Reflect.construct(target, argumentsList, newTarget)`:真 `[[Construct]]`—— +/// 按 newTarget 的 prototype 分配 this、new.target = newTarget 压构造帧执行; +/// 返回值非对象时回退到新建的 this。 +/// +/// # 步骤 +/// 1. IsConstructor(target) 为 false → TypeError。 +/// 2. newTarget 缺省 = target;IsConstructor 为 false → TypeError。 +/// 3. argumentsList 非对象 → TypeError;CreateListFromArrayLike:规范读 length +/// (访问器异常原值传播)、ToLength、逐索引规范 Get。 +/// +/// # 边界与前提 +/// - 构造失败(derived 未调 super 等)的 `Err` 携带原始异常值,原值直传重抛。 pub fn reflect_construct(vm: &mut H, args: &[u8]) -> NativeResult { let target = arg(vm, args, 1); let arg_list = arg(vm, args, 2); let new_target = if args.len() > 3 { arg(vm, args, 3) } else { target }; - if !is_callable(target) { - return type_error(vm, "Reflect.construct target is not callable"); + if !is_constructor_value(target) { + return type_error(vm, "Reflect.construct target is not a constructor"); } - let new_target_ptr = if new_target.is_object() { - new_target.as_js_object_ptr() - } else { - std::ptr::null_mut() - }; - if new_target_ptr.is_null() || !unsafe { &*new_target_ptr }.is_function() { + if !is_constructor_value(new_target) { return type_error(vm, "Reflect.construct newTarget is not a constructor"); } - // 不可构造:箭头函数、native 方法(非构造器,OBJ_TYPE_CONSTRUCTOR 标记的除外)。 - let nt = unsafe { &*new_target_ptr }; - if nt.is_arrow() || (nt.native_fn().is_some() && nt.type_tag != oxide_types::object::JsObject::OBJ_TYPE_CONSTRUCTOR) - { - return type_error(vm, "Reflect.construct newTarget is not a constructor"); + let Some(arg_list_ptr) = object_ptr(arg_list) else { + return type_error(vm, "Reflect.construct argumentsList is not an object"); + }; + // SAFETY: object_ptr 保证指针非空且指向存活对象。 + let arg_list_obj = unsafe { &*arg_list_ptr }; + + // CreateListFromArrayLike:规范读 length(访问器异常原值传播),ToLength 纯函数不抛。 + let length_si = vm.kernel_core().perm_interner().intern("length").0; + let len_val = match vm.ordinary_get(arg_list_obj, length_si, arg_list) { + Ok(v) => v, + Err(msg) => return NativeResult::Err(from_engine_error(vm, &msg)), + }; + let len = to_length(len_val) as usize; + let mut call_args = Vec::with_capacity(len); + for i in 0..len { + match arraylike_get(vm, arg_list_ptr, i) { + Ok(v) => call_args.push(v), + Err(exc) => return NativeResult::Err(exc), + } } - let proto_si = vm.kernel_core().perm_interner().intern("prototype").0; - let proto_val = match vm.resolve_property(unsafe { &*new_target_ptr }, proto_si) { - Some(p) if p.is_object() => p, - _ => JsValue::from_js_object(P::as_ptr(&vm.session().builtin_world().object_proto) as *mut JsObject), - }; - let this_ptr = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)); - let this_val = JsValue::from_js_object(this_ptr); - - let call_args = array_like_elements(arg_list).unwrap_or_default(); - // ponytail: construct by binding the freshly allocated `this` to a plain call; - // new.target is not propagated into target. Upgrade path: expose the VM's - // [[Construct]] frame (constructed_this/new_target in vm.rs) through VmHost. - match vm.call_function_sync(target, this_val, &call_args) { - Ok(ret) if ret.is_object() => NativeResult::Ok(ret), - Ok(_) => NativeResult::Ok(this_val), - Err(err) => NativeResult::Err(crate::error::create_type_error(vm, &err)), + match vm.construct_ctor_nt(target, new_target, &call_args) { + Ok(value) => NativeResult::Ok(value), + Err(exc) => NativeResult::Err(exc), } } diff --git a/crates/oxide_runtime_api/src/host.rs b/crates/oxide_runtime_api/src/host.rs index c45b47c5..b947950d 100644 --- a/crates/oxide_runtime_api/src/host.rs +++ b/crates/oxide_runtime_api/src/host.rs @@ -155,6 +155,19 @@ pub trait VmHost { /// - 失败时已消费 `last_uncaught_value`,`Err` 直接携带原始抛出值 /// (保原值身份,不做文本降级);调用方不得再取槽。 fn construct_ctor(&mut self, ctor: JsValue, args: &[JsValue]) -> Result; + /// 带 newTarget 覆写的构造调用(Construct(C, args, newTarget)):this 与 + /// new.target 均按 `new_target` 推导;不覆写时调用方传构造器本身(与 + /// `construct_ctor` 逐位等价)。 + /// + /// # 边界与前提 + /// - `ctor` 必须为可构造值(入口拒绝,返回 `Err`);`new_target` 的可构造性 + /// 由调用方入口校验;`args` 为完整实参列表。 + /// + /// # 副作用 + /// - 同 `construct_ctor`:bytecode 构造器压帧内嵌 dispatch,失败时消费 + /// `last_uncaught_value`,`Err` 直接携带原始抛出值(保原值身份,不做 + /// 文本降级);调用方不得再取槽。 + fn construct_ctor_nt(&mut self, ctor: JsValue, new_target: JsValue, args: &[JsValue]) -> Result; /// 取回在 String 展平调用边界上保留下来的原始抛出 JsValue, /// 使迭代器包装器能重新抛出原错误而非二次包装。 fn take_uncaught_value(&mut self) -> Option; diff --git a/crates/oxide_vm/src/promise/constructor.rs b/crates/oxide_vm/src/promise/constructor.rs index e0a15fce..b288d318 100644 --- a/crates/oxide_vm/src/promise/constructor.rs +++ b/crates/oxide_vm/src/promise/constructor.rs @@ -47,6 +47,21 @@ impl Vm { /// - `ctor` 非可构造值(箭头 / 非构造 native / 普通值)返回 TypeError 的 `Err`; /// 消费 `last_uncaught_value`,调用方不得再取槽。 pub(crate) fn construct_ctor(&mut self, ctor: JsValue, args: &[JsValue]) -> Result { + self.construct_with(ctor, ctor, args) + } + + /// 带 newTarget 覆写的构造调用(Construct(C, args, newTarget)):this 与 + /// new.target 均按 `new_target` 推导——this = OrdinaryCreateFromConstructor(newTarget), + /// 构造帧 new.target 槽 = newTarget。Reflect.construct 面的入口;newTarget 与 + /// 构造器本身同值时与 `construct_ctor` 逐位等价。 + /// + /// # 边界与前提 + /// - `ctor` 非可构造值返回 TypeError 的 `Err`;`new_target` 的可构造性由调用方 + /// 入口校验。失败时消费 `last_uncaught_value`,`Err` 携带原始抛出值, + /// 调用方不得再取槽。 + pub(crate) fn construct_with( + &mut self, ctor: JsValue, new_target: JsValue, args: &[JsValue], + ) -> Result { // IsConstructor 校验:arrow / 非构造 native / 普通值拒绝。 if !is_constructor_value(ctor) { return Err(oxide_builtins::error::create_type_error(self, "constructor is not a constructor")); @@ -54,9 +69,12 @@ impl Vm { // SAFETY: `is_constructor_value` 已含对象校验,指针非空且指向存活对象; // 此处只读 native_fn() 判定分支,不跨 GC/reset。 let ctor_obj = unsafe { &*ctor.as_js_object_ptr() }; - // native 构造器:receiver 为新对象,值传递调用(%Promise% 主路径)。 + // SAFETY: 调用方入口已对 new_target 做过同谓词 IsConstructor 校验, + // 指针非空且指向存活对象。 + let nt_obj = unsafe { &*new_target.as_js_object_ptr() }; + // native 构造器:receiver 为新对象(this 取 newTarget.prototype),值传递调用。 if ctor_obj.native_fn().is_some() { - let this_ptr = self.alloc_ctor_this(ctor_obj)?; + let this_ptr = self.alloc_ctor_this(nt_obj)?; let this_val = JsValue::from_js_object(this_ptr); return match self.call_function_sync(ctor, this_val, args) { Ok(ret) if ret.is_object() => Ok(ret), @@ -67,7 +85,7 @@ impl Vm { .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e))), }; } - self.call_constructor_bytecode_inline(ctor, ctor_obj, args) + self.call_constructor_bytecode_inline(ctor, ctor_obj, nt_obj, new_target, args) } /// 分配构造 this:proto = ctor.prototype(缺省 Object.prototype)。 @@ -81,14 +99,14 @@ impl Vm { } /// bytecode 构造器构造调用:压构造帧内嵌 dispatch 执行(derived 构造器 - /// super() 前 this = undefined,new.target = ctor),结果经 `do_return` 交付 - /// regs[0](非对象回退构造 this)。 + /// super() 前 this = undefined,new.target = newTarget),this 与构造 this + /// 均按 newTarget 分配,结果经 `do_return` 交付 regs[0](非对象回退构造 this)。 /// /// # 副作用 /// - 经 `save_inline_state` / `restore_inline_state` 保存恢复调用方执行状态。 /// - `construct_dispatch` 标志令构造帧弹出时交付结果而非继续执行。 fn call_constructor_bytecode_inline( - &mut self, ctor: JsValue, ctor_obj: &JsObject, args: &[JsValue], + &mut self, ctor: JsValue, ctor_obj: &JsObject, nt_obj: &JsObject, new_target: JsValue, args: &[JsValue], ) -> Result { // 按构造器自身记录的表代际解析:代际表缺失(已回收)或下标越界与 // 原生函数哨兵同口径报 TypeError。 @@ -103,7 +121,7 @@ impl Vm { } // 先拷出寄存器数,释放对代际表的借用后再进入可变借用区。 let callee_reg_count = callee_module.n_registers; - let new_obj_ptr = self.alloc_ctor_this(ctor_obj)?; + let new_obj_ptr = self.alloc_ctor_this(nt_obj)?; let new_obj_val = JsValue::from_js_object(new_obj_ptr); // derived 构造器 super() 前 this 为 undefined,基类 this = 新对象。 let this_value = if ctor_obj.is_derived_constructor() { @@ -123,7 +141,7 @@ impl Vm { FrameArgs::Slice(args), Some(0), Some(new_obj_val), - ctor, + new_target, FrameContinuation::None, 0, ) diff --git a/crates/oxide_vm/src/vm/vmhost.rs b/crates/oxide_vm/src/vm/vmhost.rs index ef147d9d..304538f4 100644 --- a/crates/oxide_vm/src/vm/vmhost.rs +++ b/crates/oxide_vm/src/vm/vmhost.rs @@ -133,6 +133,9 @@ impl oxide_runtime_api::VmHost for Vm { fn construct_ctor(&mut self, ctor: JsValue, args: &[JsValue]) -> Result { Vm::construct_ctor(self, ctor, args) } + fn construct_ctor_nt(&mut self, ctor: JsValue, new_target: JsValue, args: &[JsValue]) -> Result { + Vm::construct_with(self, ctor, new_target, args) + } fn checked_object_ptr(&mut self, val: JsValue, error_msg: &str) -> Result, String> { self.checked_object_ptr(val, error_msg) } diff --git a/crates/oxide_vm/tests/builtins_reflect_tests.rs b/crates/oxide_vm/tests/builtins_reflect_tests.rs index 37393e68..8daa5f31 100644 --- a/crates/oxide_vm/tests/builtins_reflect_tests.rs +++ b/crates/oxide_vm/tests/builtins_reflect_tests.rs @@ -133,3 +133,191 @@ fn reflect_non_object_target_throws_type_error() { let result = eval(&mut vm, "try { Reflect.get(1, 'x') } catch (e) { e instanceof TypeError }").unwrap(); assert!(result.as_bool()); } + +// 派生类(extends Array)经 Reflect.construct 产出真实例:isArray 与 length 就位。 +#[test] +fn reflect_construct_derived_class_instance() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class B extends Array { constructor(x) { super(x); } } \ + var inst = Reflect.construct(B, [2]); \ + inst instanceof B && Array.isArray(inst) && inst.length === 2", + ) + .unwrap(); + assert!(result.as_bool()); +} + +// 派生类经 Reflect.construct 正常走 super 链构造,实例值判别。 +#[test] +fn reflect_construct_derived_extends_base_class() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class Base { constructor(x) { this.x = x; } } \ + class D extends Base { constructor(x) { super(x); } } \ + var inst = Reflect.construct(D, [9]); \ + inst instanceof D && inst.x === 9", + ) + .unwrap(); + assert!(result.as_bool()); +} + +// 无 newTarget 时构造帧内 new.target = target。 +#[test] +fn reflect_construct_new_target_defaults_to_target() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var newTarget = null; function f() { newTarget = new.target; } \ + Reflect.construct(f, []); newTarget === f", + ) + .unwrap(); + assert!(result.as_bool()); +} + +// newTarget 覆写臂:构造帧内 new.target = newTarget,实例 proto 取 newTarget.prototype。 +#[test] +fn reflect_construct_new_target_override() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var newTarget = null; function f() { newTarget = new.target; } \ + var NT = function() {}; \ + Reflect.construct(f, [], NT); \ + newTarget === NT && Object.getPrototypeOf(Reflect.construct(f, [], NT)) === NT.prototype", + ) + .unwrap(); + assert!(result.as_bool()); +} + +// 派生类 + newTarget 覆写全形:super 实参逐位、new.target = NT、this = 实例。 +#[test] +fn reflect_construct_derived_with_new_target() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var expectedNewTarget = function() {}; \ + var thisValue, instance, args, actualNewTarget; \ + function Parent() { thisValue = this; args = arguments; actualNewTarget = new.target; } \ + class Child extends Parent { constructor() { super(1, 2, 3); } } \ + instance = Reflect.construct(Child, [4, 5, 6], expectedNewTarget); \ + thisValue === instance && args.length === 3 && args[0] === 1 && args[1] === 2 && args[2] === 3 \ + && actualNewTarget === expectedNewTarget \ + && Object.getPrototypeOf(instance) === expectedNewTarget.prototype", + ) + .unwrap(); + assert!(result.as_bool()); +} + +// 基类 + newTarget 覆写:实例 proto 取 NT.prototype,new.target = NT。 +#[test] +fn reflect_construct_base_class_with_new_target() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class CBase { constructor(x) { this.x = x; this.nt = new.target; } } \ + var NT = function() {}; \ + var inst = Reflect.construct(CBase, [5], NT); \ + Object.getPrototypeOf(inst) === NT.prototype && inst.nt === NT && inst.x === 5", + ) + .unwrap(); + assert!(result.as_bool()); +} + +// 入参面:target 非构造器(Date.now / 箭头 / 基元)均 TypeError。 +#[test] +fn reflect_construct_non_constructor_target_throws() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var t1 = false, t2 = false, t3 = false; \ + try { Reflect.construct(Date.now, [], Array); } catch (e) { t1 = e instanceof TypeError; } \ + try { Reflect.construct((a) => a, []); } catch (e) { t2 = e instanceof TypeError; } \ + try { Reflect.construct(1, []); } catch (e) { t3 = e instanceof TypeError; } \ + t1 && t2 && t3", + ) + .unwrap(); + assert!(result.as_bool()); +} + +// 入参面:newTarget 非构造器与 argumentsList 非对象均 TypeError。 +#[test] +fn reflect_construct_invalid_new_target_or_arguments_list_throws() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var t1 = false, t2 = false; \ + try { Reflect.construct(function(){}, [], 1); } catch (e) { t1 = e instanceof TypeError; } \ + try { Reflect.construct(function(){}, 1); } catch (e) { t2 = e instanceof TypeError; } \ + t1 && t2", + ) + .unwrap(); + assert!(result.as_bool()); +} + +// length 访问器抛出的异常原值传播(不降级、不吞没)。 +#[test] +fn reflect_construct_length_getter_exception_propagates() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var o = {}; \ + Object.defineProperty(o, 'length', { get: function() { throw new RangeError('len'); } }); \ + try { Reflect.construct(function(a){ return a; }, o); false } catch (e) { e instanceof RangeError }", + ) + .unwrap(); + assert!(result.as_bool()); +} + +// array-like 对象按规范 Get 逐位取值(非位序直读),值与实参个数就位。 +#[test] +fn reflect_construct_array_like_elements_read_by_get() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var captured; \ + function C(a, b) { captured = [typeof a, arguments.length]; } \ + Reflect.construct(C, { length: 2, 0: 'a', 1: 'b' }); \ + captured[0] === 'string' && captured[1] === 2", + ) + .unwrap(); + assert!(result.as_bool()); +} + +// 守卫面:native 构造器(Array 填槽)、构造帧 arguments 完整、显式返回对象 +// 优先、绑定元数据(isConstructor(Reflect.construct) === false)。 +#[test] +fn reflect_construct_guard_face() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var arr = Reflect.construct(Array, [3]); \ + function fn() { this.args = arguments; } \ + var result = Reflect.construct(fn, [42, 'Mike', 'Leo']); \ + var retObj = Reflect.construct(function() { return { z: 9 }; }, []); \ + var meta = false; \ + try { Reflect.construct(Reflect.construct, []); } catch (e) { meta = e instanceof TypeError; } \ + arr instanceof Array && arr.length === 3 \ + && result.args.length === 3 && result.args[0] === 42 && result.args[1] === 'Mike' && result.args[2] === 'Leo' \ + && retObj.z === 9 && !(retObj instanceof Function) && meta", + ) + .unwrap(); + assert!(result.as_bool()); +} + +// 227.1 回归守卫:隐式 super 面经 `new` 直接构造不回归。 +#[test] +fn construct_direct_new_derived_array_guard() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class B extends Array { constructor(x) { super(x); } } \ + var sub = new B(2); \ + class Sub extends Array { constructor(x) { super(x); } } \ + var s7 = new Sub(7); \ + sub instanceof B && sub.length === 2 && s7.length === 7", + ) + .unwrap(); + assert!(result.as_bool()); +}