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;
|
// 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.
|
// 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()) };
|
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.regs[254] = callee;
|
||||||
self.native_call_depth += 1;
|
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) => {
|
NativeResult::Ok(val) => {
|
||||||
self.native_call_depth -= 1;
|
|
||||||
builtins_trace!("native_call ok depth={}", self.native_call_depth);
|
builtins_trace!("native_call ok depth={}", self.native_call_depth);
|
||||||
self.regs[0] = val;
|
self.regs[0] = val;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
NativeResult::Err(err_val) => {
|
NativeResult::Err(err_val) => {
|
||||||
self.native_call_depth -= 1;
|
|
||||||
let (error, kind) = if err_val.is_object() {
|
let (error, kind) = if err_val.is_object() {
|
||||||
(err_val, self.thrown_error_kind(err_val))
|
(err_val, self.thrown_error_kind(err_val))
|
||||||
} else {
|
} else {
|
||||||
@@ -62,7 +69,6 @@ impl Vm {
|
|||||||
self.unwind()
|
self.unwind()
|
||||||
}
|
}
|
||||||
NativeResult::TailCall { callee, this, args } => {
|
NativeResult::TailCall { callee, this, args } => {
|
||||||
self.native_call_depth -= 1;
|
|
||||||
if callee.is_object() {
|
if callee.is_object() {
|
||||||
let obj = unsafe { &*callee.as_js_object_ptr() };
|
let obj = unsafe { &*callee.as_js_object_ptr() };
|
||||||
if obj.native_fn().is_some() {
|
if obj.native_fn().is_some() {
|
||||||
|
|||||||
@@ -341,3 +341,14 @@ fn private_class_same_name_different_classes_do_not_share_brand() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(result.is_bool() && result.as_bool());
|
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