diff --git a/crates/oxide_vm/src/promise/constructor.rs b/crates/oxide_vm/src/promise/constructor.rs index 66877eb4..37531ee4 100644 --- a/crates/oxide_vm/src/promise/constructor.rs +++ b/crates/oxide_vm/src/promise/constructor.rs @@ -56,6 +56,9 @@ impl Vm { /// 构造帧 new.target 槽 = newTarget。Reflect.construct 面的入口;newTarget 与 /// 构造器本身同值时与 `construct_ctor` 逐位等价。 /// + /// bound 包装按 BoundFunctionExoticConstruct 逐层解包:绑定实参前置(内层先)、 + /// newTarget 逐层指针相等替换为包装体,最内层非构造器判定后递归进入对应臂。 + /// /// # 边界与前提 /// - `ctor` 非可构造值返回 TypeError 的 `Err`;`new_target` 的可构造性由调用方 /// 入口校验。失败时消费 `last_uncaught_value`,`Err` 携带原始抛出值, @@ -73,6 +76,37 @@ impl Vm { // SAFETY: 调用方入口已对 new_target 做过同谓词 IsConstructor 校验, // 指针非空且指向存活对象。 let nt_obj = unsafe { &*new_target.as_js_object_ptr() }; + // bound 包装:解包链(绑定实参前置、newTarget 逐层指针相等替换), + // 最内层非构造器判定后递归进入既有臂。 + if ctor_obj.type_tag == JsObject::OBJ_TYPE_BOUND { + let mut cur = ctor; + let mut cur_obj = ctor_obj; + let mut nt = new_target; + let mut combined = args.to_vec(); + while cur_obj.type_tag == JsObject::OBJ_TYPE_BOUND { + // 纯读无 GC 点:状态对象布局 [target, thisArg, ...boundArgs]。 + let state = oxide_builtins::function::bound_state_values(cur_obj); + let target = state.first().copied().unwrap_or(JsValue::undefined()); + if !target.is_object() { + // 非对象的最内层 target 不可构造。 + return Err(oxide_builtins::error::create_type_error(self, "object is not a constructor")); + } + for v in state.iter().skip(2) { + combined.insert(0, *v); + } + if cur == nt { + nt = target; + } + cur = target; + // SAFETY: is_object 守卫保证指针非空且指向存活对象;只读 type_tag + // 判定分支,不跨 GC/reset。 + cur_obj = unsafe { &*cur.as_js_object_ptr() }; + } + if !is_constructor_value(cur) { + return Err(oxide_builtins::error::create_type_error(self, "object is not a constructor")); + } + return self.construct_with(cur, nt, &combined); + } // native 构造器:receiver 为新对象(this 取 newTarget.prototype),值传递调用。 if ctor_obj.native_fn().is_some() { // DataView 构造器不预分配 this:其体内先做全部校验、后读 newTarget.prototype diff --git a/crates/oxide_vm/tests/builtins_function_tests.rs b/crates/oxide_vm/tests/builtins_function_tests.rs index b3cc0529..8e03dfd2 100644 --- a/crates/oxide_vm/tests/builtins_function_tests.rs +++ b/crates/oxide_vm/tests/builtins_function_tests.rs @@ -320,3 +320,25 @@ fn bound_function_construct_errors() { .unwrap(); assert_num(result, 9.0); } + +#[test] +fn bound_function_newtarget_pins() { + let mut vm = Vm::new(); + // Reflect.construct 以 bound 包装为构造器:new.target 逐层替换到最内层 + // target(SameValue(F, newTarget) → target),三种 newTarget 形态同值。 + for src in [ + "var nt; function A() { nt = new.target; } var B = A.bind(); var C = B.bind(); Reflect.construct(C, [], C); nt === A", + "var nt; function A() { nt = new.target; } var B = A.bind(); var C = B.bind(); Reflect.construct(C, [], A); nt === A", + "var nt; function A() { nt = new.target; } var B = A.bind(); var C = B.bind(); Reflect.construct(C, [], B); nt === A", + ] { + let result = eval(&mut vm, src).unwrap(); + assert!(result.as_bool(), "for {}", src); + } + // 构造实例原型链指向最内层 target 的 prototype。 + let result = eval( + &mut vm, + "function A() {} var B = A.bind(); var C = B.bind(); Object.getPrototypeOf(Reflect.construct(C, [], C)) === A.prototype", + ) + .unwrap(); + assert!(result.as_bool()); +}