diff --git a/crates/oxide_vm/src/vm_dispatch_misc.rs b/crates/oxide_vm/src/vm_dispatch_misc.rs index 7b4b4c4..47edd11 100644 --- a/crates/oxide_vm/src/vm_dispatch_misc.rs +++ b/crates/oxide_vm/src/vm_dispatch_misc.rs @@ -1,7 +1,6 @@ -use crate::native::NativeFn; -use crate::vm::{native_fn_ptr_to_fn, ForInIter, FrameArgs, FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH}; +use crate::vm::{ForInIter, FrameArgs, FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH}; use crate::vm_trace; -use oxide_runtime_api::{push_to_string, to_boolean, to_string_full, NativeResult, VmHost}; +use oxide_runtime_api::{push_to_string, to_boolean, to_string_full, VmHost}; use oxide_types::object::{JsObject, PropAttributes}; use oxide_types::private_key::{int_key_value, is_int_key, is_private_name_key, is_symbol_key, make_int_key}; use oxide_types::value::JsValue; @@ -78,30 +77,28 @@ impl Vm { if ctor_obj.native_fn().is_some() { let new_obj_val = JsValue::object(new_obj as *mut u8); - self.regs[255] = new_obj_val; - - let mut args_buf = [0u8; 257]; - args_buf[0] = 255u8; + let mut args = Vec::with_capacity(arg_count); for i in 0..arg_count.min(256) { - args_buf[i + 1] = first_arg_reg.wrapping_add(i as u8); + args.push(self.regs[first_arg_reg.wrapping_add(i as u8) as usize]); } - let args_slice = &args_buf[..arg_count + 1]; - - let func: NativeFn = unsafe { native_fn_ptr_to_fn(ctor_obj.native_fn().unwrap()) }; - self.regs[254] = constructor; - match func(self, args_slice) { - NativeResult::Ok(val) => { - self.regs[rd] = if val.is_object() { val } else { new_obj_val }; + // native 构造器:收口到 call_function_sync(与 spread / bound 变体一致)。 + // 该入口统一保存/恢复调用窗口与 253/254 槽且不触碰 255,调用方 + // new.target 与 this 跨 native 构造不被污染;receiver 经 arg0 打包, + // native 侧经 reg(args[0]) 读取,构造语义不变。 + match self.call_function_sync(constructor, new_obj_val, &args) { + Ok(v) => { + self.regs[rd] = if v.is_object() { v } else { new_obj_val }; Ok(false) } - NativeResult::Err(err_val) => { - self.exception_value = Some(err_val); - self.pending_error_kind = Some(self.thrown_error_kind(err_val)); + Err(_) => { + let exc = self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(self, "constructor call failed")); + self.exception_value = Some(exc); + self.pending_error_kind = Some(self.thrown_error_kind(exc)); self.unwind().map(|_| true) } - NativeResult::TailCall { .. } => { - self.raise_type_error("constructor tail call not supported").map(|_| true) - } } } else if ctor_obj.sub_module_index() > 0 { let sub_idx = ctor_obj.sub_module_index() as usize; diff --git a/crates/oxide_vm/tests/new_target_tests.rs b/crates/oxide_vm/tests/new_target_tests.rs index fddf3f9..198cdaa 100644 --- a/crates/oxide_vm/tests/new_target_tests.rs +++ b/crates/oxide_vm/tests/new_target_tests.rs @@ -102,3 +102,44 @@ fn import_meta_is_explicit_error() { }; assert!(err.contains("import.meta not yet supported"), "unexpected compile error: {err}"); } + +#[test] +fn native_construct_does_not_pollute_new_target() { + // native 构造(非 spread)不污染调用方 new.target:`new Date()` 后构造器 + // 返回 `new.target` 应仍为外层构造目标 f(255 槽随调用恢复,返回 f 自身)。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "function f(){ new Date(); return new.target; } (new f()) === f").unwrap(); + assert_bool(result, true); +} + +#[test] +fn native_construct_does_not_pollute_this() { + // native 构造后同帧 this 保持外层构造的新对象:receiver 槽随调用保存/恢复(254 同修)。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "function f(){ new Date(); this.t = this instanceof f; } new f().t").unwrap(); + assert_bool(result, true); +} + +#[test] +fn native_construct_spread_does_not_pollute_new_target() { + // spread 构造变体与普通构造一致:`new Date(...[])` 后 new.target 不被污染。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "function f(){ new Date(...[]); return new.target; } (new f()) === f").unwrap(); + assert_bool(result, true); +} + +#[test] +fn native_construct_then_plain_call_new_target_is_undefined() { + // 普通调用帧内 native 构造后,new.target 仍为 undefined(帧语义不受构造调用干扰)。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "function f(){ new Date(); return new.target === undefined; } f()").unwrap(); + assert_bool(result, true); +} + +#[test] +fn native_construct_keeps_instance_semantics() { + // 收口 call_function_sync 后 native 构造语义不变:返回真实实例且原型链正确。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "(function(){ var d = new Date(0); return d instanceof Date; })()").unwrap(); + assert_bool(result, true); +}