From a34c289d475d4b12d49705c33181ddc7bd211a54 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Tue, 30 Jun 2026 16:12:42 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20native=20=E8=B0=83=E7=94=A8=E4=BF=9D?= =?UTF-8?q?=E5=AD=98/=E6=81=A2=E5=A4=8D=20this=20=E5=AF=84=E5=AD=98?= =?UTF-8?q?=E5=99=A8=EF=BC=8C=E9=81=BF=E5=85=8D=E6=96=B9=E6=B3=95=E5=86=85?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E5=90=8E=20this=20=E8=A2=AB=20callee=20?= =?UTF-8?q?=E8=A6=86=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_vm/src/dispatch/call.rs | 14 ++++++++++---- crates/oxide_vm/tests/class_tests.rs | 11 +++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/crates/oxide_vm/src/dispatch/call.rs b/crates/oxide_vm/src/dispatch/call.rs index 9bedd289..f2dc4153 100644 --- a/crates/oxide_vm/src/dispatch/call.rs +++ b/crates/oxide_vm/src/dispatch/call.rs @@ -34,17 +34,24 @@ impl Vm { // SAFETY: native_fn was set via set_native_fn with a valid NativeFn pointer; // native_fn_ptr_to_fn is the single coercion point for NativeFnPtr → NativeFn. let func: NativeFn = unsafe { native_fn_ptr_to_fn(obj.native_fn().unwrap()) }; + // regs[254] is the caller's `this` register and doubles as the "current callee" + // slot that dispatcher builtins (Function.prototype.bind/call/apply) read. Native + // calls share the flat register file with the caller, so snapshot the caller's + // `this`, expose the callee for the native's duration, then restore it — otherwise + // every native call leaves its own function object in the caller's `this` register. + let saved_this = self.regs[254]; self.regs[254] = callee; self.native_call_depth += 1; - match func(self, args_slice) { + let result = func(self, args_slice); + self.native_call_depth -= 1; + self.regs[254] = saved_this; + match result { NativeResult::Ok(val) => { - self.native_call_depth -= 1; builtins_trace!("native_call ok depth={}", self.native_call_depth); self.regs[0] = val; Ok(()) } NativeResult::Err(err_val) => { - self.native_call_depth -= 1; let (error, kind) = if err_val.is_object() { (err_val, self.thrown_error_kind(err_val)) } else { @@ -62,7 +69,6 @@ impl Vm { self.unwind() } NativeResult::TailCall { callee, this, args } => { - self.native_call_depth -= 1; if callee.is_object() { let obj = unsafe { &*callee.as_js_object_ptr() }; if obj.native_fn().is_some() { diff --git a/crates/oxide_vm/tests/class_tests.rs b/crates/oxide_vm/tests/class_tests.rs index 6b19e817..f4553612 100644 --- a/crates/oxide_vm/tests/class_tests.rs +++ b/crates/oxide_vm/tests/class_tests.rs @@ -341,3 +341,14 @@ fn private_class_same_name_different_classes_do_not_share_brand() { .unwrap(); assert!(result.is_bool() && result.as_bool()); } + +#[test] +fn this_survives_native_call_inside_method() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class C { v=7; m(){ Object.keys(this); Object.getOwnPropertyNames(this); return this.v; } } new C().m()", + ) + .unwrap(); + assert_eq!(result.as_int(), 7); +}