mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: Reflect.construct 换真构造帧并支持 newTarget 覆写
This commit is contained in:
@@ -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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
@@ -24,47 +24,53 @@ pub fn reflect_apply<H: VmHost>(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<H: VmHost>(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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -155,6 +155,19 @@ pub trait VmHost {
|
||||
/// - 失败时已消费 `last_uncaught_value`,`Err` 直接携带原始抛出值
|
||||
/// (保原值身份,不做文本降级);调用方不得再取槽。
|
||||
fn construct_ctor(&mut self, ctor: JsValue, args: &[JsValue]) -> Result<JsValue, JsValue>;
|
||||
/// 带 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<JsValue, JsValue>;
|
||||
/// 取回在 String 展平调用边界上保留下来的原始抛出 JsValue,
|
||||
/// 使迭代器包装器能重新抛出原错误而非二次包装。
|
||||
fn take_uncaught_value(&mut self) -> Option<JsValue>;
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user