fix: Reflect.construct 换真构造帧并支持 newTarget 覆写

This commit is contained in:
2026-09-20 15:13:18 +00:00
parent 299708e729
commit 3fb3ebc3cc
5 changed files with 269 additions and 41 deletions
+26 -8
View File
@@ -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<JsValue, JsValue> {
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<JsValue, JsValue> {
// 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<JsValue, JsValue> {
// 按构造器自身记录的表代际解析:代际表缺失(已回收)或下标越界与
// 原生函数哨兵同口径报 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,
)
+3
View File
@@ -133,6 +133,9 @@ impl oxide_runtime_api::VmHost for Vm {
fn construct_ctor(&mut self, ctor: JsValue, args: &[JsValue]) -> Result<JsValue, JsValue> {
Vm::construct_ctor(self, ctor, args)
}
fn construct_ctor_nt(&mut self, ctor: JsValue, new_target: JsValue, args: &[JsValue]) -> Result<JsValue, JsValue> {
Vm::construct_with(self, ctor, new_target, args)
}
fn checked_object_ptr(&mut self, val: JsValue, error_msg: &str) -> Result<Option<*mut JsObject>, String> {
self.checked_object_ptr(val, error_msg)
}
@@ -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());
}