mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
fix: native 调用保存/恢复 this 寄存器,避免方法内调用后 this 被 callee 覆盖
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user