From f8722ca91d2d956cb0ec1ad1d75088dd7774029c Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 27 Sep 2026 00:49:22 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20sloppy=20=E5=87=BD=E6=95=B0=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E5=8E=9F=E5=A7=8B=E5=80=BC=20this=20=E6=8C=89?= =?UTF-8?q?=E8=A7=84=E8=8C=83=E7=9B=92=E8=A3=85=E4=B8=BA=E5=8C=85=E8=A3=85?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_vm/src/vm/call.rs | 7 +++++-- crates/oxide_vm/src/vm_runtime.rs | 7 +++++-- crates/oxide_vm/tests/this_binding_tests.rs | 20 +++++++++++++++++++- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/crates/oxide_vm/src/vm/call.rs b/crates/oxide_vm/src/vm/call.rs index e7bce928..b8b94d30 100644 --- a/crates/oxide_vm/src/vm/call.rs +++ b/crates/oxide_vm/src/vm/call.rs @@ -273,13 +273,16 @@ impl Vm { self.regs[sub_param_base + i] = v; } // this 绑定:箭头函数恒用词法捕获;sloppy 普通函数 this 为 null/undefined 时 - // 替换为全局对象(ECMA-262 10.4.3);严格模式与显式方法/构造 this 原样保留。 + // 替换为全局对象,其余原始值经 ToObject 盒装(ECMA-262 10.4.3);严格模式 + // this 原样保留。 self.regs[254] = if sub_is_arrow { obj.captured_this() } else if !sub_is_strict && this_value.is_nullish() { JsValue::from_js_object(self.session.global_object().as_ptr() as *mut JsObject) - } else { + } else if sub_is_strict { this_value + } else { + oxide_runtime_api::to_object(this_value, self)? }; self.regs[255] = new_target; diff --git a/crates/oxide_vm/src/vm_runtime.rs b/crates/oxide_vm/src/vm_runtime.rs index 76ad7d6f..4cf75499 100644 --- a/crates/oxide_vm/src/vm_runtime.rs +++ b/crates/oxide_vm/src/vm_runtime.rs @@ -485,13 +485,16 @@ impl Vm { self.regs[sub.param_base as usize + i] = args.get(i).copied().unwrap_or(JsValue::undefined()); } // this 绑定:箭头函数恒用词法捕获;sloppy 普通函数 this 为 null/undefined 时 - // 替换为全局对象(ECMA-262 10.4.3);严格模式与显式 receiver 原样保留。 + // 替换为全局对象,其余原始值经 ToObject 盒装(ECMA-262 10.4.3);严格模式 + // this 原样保留。 self.regs[254] = if sub.is_arrow { callee_obj.captured_this() } else if !sub.is_strict && receiver.is_nullish() { JsValue::from_js_object(self.session.global_object().as_ptr() as *mut JsObject) - } else { + } else if sub.is_strict { receiver + } else { + oxide_runtime_api::to_object(receiver, self)? }; self.regs[255] = JsValue::undefined(); self.reload_builtin_mirror_slots(&sub.builtin_reg_map, 0); diff --git a/crates/oxide_vm/tests/this_binding_tests.rs b/crates/oxide_vm/tests/this_binding_tests.rs index 573eb07c..42339fab 100644 --- a/crates/oxide_vm/tests/this_binding_tests.rs +++ b/crates/oxide_vm/tests/this_binding_tests.rs @@ -1,5 +1,6 @@ //! sloppy 普通函数 `this` 绑定语义测试:非箭头 && 非 strict && this 为 null/undefined -//! 时替换为全局对象;箭头/严格/method/构造/显式 receiver 均不替换。 +//! 时替换为全局对象,其余原始值经 ToObject 盒装;箭头/严格/method/构造/显式 +//! receiver 均不替换。 //! 覆盖普通调用、call/apply/bind、builtin 回调、构造、生成器、async、derived 构造。 use std::sync::Arc; @@ -119,6 +120,23 @@ fn strict_call_with_nullish_keeps_undefined() { assert_true(&mut vm, "function f(){ 'use strict'; return this === undefined; } f.call(undefined)"); } +#[test] +fn call_with_primitive_this_boxes_object() { + // sloppy 普通函数 call 原始值 this 经 ToObject 盒装为对应包装对象。 + let mut vm = Vm::new(); + assert_true(&mut vm, "function f(){ return typeof this === 'object'; } f.call('str')"); + assert_true(&mut vm, "function f(){ return typeof this === 'object'; } f.call(1)"); + assert_true(&mut vm, "function f(){ return typeof this === 'object'; } f.call(true)"); + assert_true(&mut vm, "function f(){ return typeof this === 'object'; } f.call(1n)"); +} + +#[test] +fn strict_call_with_primitive_this_keeps_primitive() { + // strict 函数 call 原始值 this 原样保留,不盒装。 + let mut vm = Vm::new(); + assert_true(&mut vm, "function f(){ 'use strict'; return typeof this === 'string'; } f.call('str')"); +} + // ── builtin 回调 / 生成器 / async ── #[test]