feat: Reflect.construct 实现正确 construct 语义

This commit is contained in:
2026-06-30 15:47:05 +08:00
parent c720481ae5
commit 6dd58e471f
2 changed files with 30 additions and 12 deletions
+21 -10
View File
@@ -1,4 +1,5 @@
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::mem::P;
use oxide_types::object::{JsObject, PropAttributes, PropMetaEntry};
use oxide_types::value::JsValue;
@@ -30,20 +31,30 @@ pub fn reflect_construct<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if !is_callable(target) {
return type_error(vm, "Reflect.construct target is not callable");
}
// newTarget must be constructable — check if it's an object with function flag
if new_target.is_object() {
let nt_ptr = new_target.as_js_object_ptr();
if !nt_ptr.is_null() && unsafe { &*nt_ptr }.is_function() {
// Accept: newTarget is a valid constructor
} else {
return type_error(vm, "Reflect.construct newTarget 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() {
return type_error(vm, "Reflect.construct newTarget is not a constructor");
}
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();
match vm.call_function_sync(target, JsValue::undefined(), &call_args) {
Ok(value) => NativeResult::Ok(value),
// 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)),
}
}
@@ -92,16 +92,23 @@ fn reflect_apply_calls_function() {
}
#[test]
fn reflect_construct_throws_clear_type_error() {
fn reflect_construct_returns_new_instance() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"try { Reflect.construct(function(){}, []) } catch (e) { e instanceof TypeError }",
"var o = Reflect.construct(function(){ this.x = 5; }, []); typeof o === 'object' && o.x === 5",
)
.unwrap();
assert!(result.as_bool());
}
#[test]
fn reflect_construct_non_callable_target_throws_type_error() {
let mut vm = Vm::new();
let result = eval(&mut vm, "try { Reflect.construct(123, []) } catch (e) { e instanceof TypeError }").unwrap();
assert!(result.as_bool());
}
#[test]
fn reflect_non_object_target_throws_type_error() {
let mut vm = Vm::new();