From ff5ab6222241d906aa281bd903d7b40e265c3025 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Tue, 6 Oct 2026 10:51:26 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=A1=B6=E5=B1=82=20var=20=E6=92=9E?= =?UTF-8?q?=E6=97=A2=E6=9C=89=E5=B1=9E=E6=80=A7=E5=86=99=E9=9D=A2=E5=AF=B9?= =?UTF-8?q?=E9=BD=90=20node=20=E9=94=9A=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 36 +++++----- Cargo.toml | 2 +- crates/oxide_vm/src/vm_dispatch_ctrl.rs | 66 ++++++++++++------- .../tests/global_var_binding_tests.rs | 40 +++++++++++ 4 files changed, 100 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53c0b2c6..ff8e69a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1095,7 +1095,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.8.21" +version = "0.8.22" dependencies = [ "chrono", "fast-float", @@ -1120,7 +1120,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.8.21" +version = "0.8.22" dependencies = [ "num-bigint", "oxide_log", @@ -1128,7 +1128,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.8.21" +version = "0.8.22" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1139,7 +1139,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.8.21" +version = "0.8.22" dependencies = [ "ansi_term", "bumpalo", @@ -1159,7 +1159,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.8.21" +version = "0.8.22" dependencies = [ "lru", "oxide_bytecode", @@ -1168,7 +1168,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.8.21" +version = "0.8.22" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1187,7 +1187,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.8.21" +version = "0.8.22" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1201,7 +1201,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.8.21" +version = "0.8.22" dependencies = [ "num-bigint", "oxc_allocator", @@ -1216,7 +1216,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.8.21" +version = "0.8.22" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1225,7 +1225,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.8.21" +version = "0.8.22" dependencies = [ "bumpalo", "dashmap", @@ -1240,7 +1240,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.8.21" +version = "0.8.22" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1252,7 +1252,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.8.21" +version = "0.8.22" dependencies = [ "tracing", "tracing-appender", @@ -1261,7 +1261,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.8.21" +version = "0.8.22" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1276,7 +1276,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.8.21" +version = "0.8.22" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1292,7 +1292,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.8.21" +version = "0.8.22" dependencies = [ "num-bigint", "num-traits", @@ -1304,7 +1304,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.8.21" +version = "0.8.22" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.8.21" +version = "0.8.22" dependencies = [ "bumpalo", "hashbrown 0.14.5", @@ -1333,7 +1333,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.8.21" +version = "0.8.22" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index dd82a3c1..4c517c50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ members = [ ] [workspace.package] -version = "0.8.21" +version = "0.8.22" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_vm/src/vm_dispatch_ctrl.rs b/crates/oxide_vm/src/vm_dispatch_ctrl.rs index 8976980a..423880a3 100644 --- a/crates/oxide_vm/src/vm_dispatch_ctrl.rs +++ b/crates/oxide_vm/src/vm_dispatch_ctrl.rs @@ -303,13 +303,14 @@ impl Vm { /// 定义全局 var 绑定数据属性:rd=全局对象,a=值,b=键。 /// - /// 描述符语义(CreateGlobalVarBinding): - /// - 已有可配置数据属性且不可写:no-op 返回——规范不更新既有数据描述符 - /// (writable/enumerable/configurable 与值均保持原样)。 - /// - 已有可配置属性且可写(数据或 accessor):保 enumerable/configurable、 - /// 更新值;accessor 形重定义为数据属性(既有行为,不扩面)。 - /// - 已有不可配置属性:走定义不变量检查(末尾统一路径)——描述符冲突或 - /// 不可写属性值变更时静默 no-op,否则仅更新值、描述符不变。 + /// 描述符语义(CreateGlobalVarBinding 写点,对照 node 锚定): + /// - 已有 accessor 属性(可配置或不可配置):零修改返回——var 声明写点 + /// 不覆写访问器(两模式均不抛,值与描述符保持原样)。 + /// - 已有数据属性且不可写(可配置或不可配置):strict 抛 TypeError、 + /// sloppy 静默 no-op(规范不更新既有数据描述符,writable/enumerable/ + /// configurable 与值均保持原样)。 + /// - 已有数据属性且可写(可配置或不可配置):保 enumerable/configurable、 + /// 仅更新值。 /// - 属性缺失:新建可写/可枚举/不可配置数据属性。 /// /// # 边界与前提 @@ -323,24 +324,32 @@ impl Vm { let prop_name_si = self.property_key_si(self.regs[b])?; let value = self.regs[a]; let obj = unsafe { &mut *obj_val.as_js_object_ptr() }; - // 已有可配置属性:CreateGlobalVarBinding 不改既有数据描述符;可写数据 - // 属性仅更新值(保 e/c),accessor 形重定义为数据属性。 + // 已有属性:CreateGlobalVarBinding 不改既有描述符——accessor 形零修改 + // (不覆写、不抛);数据属性不可写时 strict 抛 TypeError、sloppy 静默 + // no-op;可写数据属性保 enumerable/configurable 仅更新值。 if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) { if let Some(current) = obj.prop_meta_at(pos) { - if current.attributes.configurable() { - // 既有数据属性不可写:不更新描述符也不更新值(规范零修改)。 - if !current.is_accessor && !current.attributes.writable() { - return Ok(()); - } - let attrs = - PropAttributes::new(true, current.attributes.enumerable(), current.attributes.configurable()); - let _ = self.define_data_property(obj, prop_name_si, value, attrs); + // accessor 形:var 声明写点不覆写访问器,两模式均零修改。 + if current.is_accessor { return Ok(()); } + // 既有数据属性不可写(可配置与不可配置同口径):strict 抛 + // TypeError、sloppy 静默 no-op。 + if !current.attributes.writable() { + return if self.current_strict() { + self.raise_error_kind("TypeError", "cannot assign to read-only property") + } else { + Ok(()) + }; + } + // 可写数据属性:保 enumerable/configurable、仅更新值。 + let attrs = + PropAttributes::new(true, current.attributes.enumerable(), current.attributes.configurable()); + let _ = self.define_data_property(obj, prop_name_si, value, attrs); + return Ok(()); } } - // 属性缺失或不可配置既有属性:统一走定义,不可配置时由定义侧不变量 - // 检查决定更新值或静默 no-op。 + // 属性缺失:新建可写/可枚举/不可配置数据属性。 let _ = self.define_data_property(obj, prop_name_si, value, PropAttributes::new(true, true, false)); Ok(()) } @@ -351,8 +360,9 @@ impl Vm { /// eval 脚本 var/函数声明的属性描述符)。 /// /// # 步骤 - /// 1. 属性已存在:CreateGlobalVarBinding 不改既有描述符——保持原属性只更新 - /// 值(不可写数据/访问器:strict 抛 TypeError、sloppy 静默 no-op)。 + /// 1. 属性已存在:CreateGlobalVarBinding 不改既有描述符——accessor 形零修改 + /// (不覆写、不抛);数据属性不可写时 strict 抛 TypeError、sloppy 静默 + /// no-op;可写数据属性保原描述符仅更新值。 /// 2. 属性缺失:新建可写/可枚举/可配置属性;全局对象不可扩展 → TypeError /// (两模式均抛,CreateGlobalVarBinding 语义)。 /// @@ -370,18 +380,24 @@ impl Vm { let global_ptr = self.session.global_object().as_ptr() as *mut JsObject; // SAFETY: 全局对象钉在 session 永久区,指针在 VM 生命周期内有效。 let obj = unsafe { &mut *global_ptr }; - // 属性已存在:保持原描述符只更新值,不升级 configurable(规范: - // CreateGlobalVarBinding 对既有属性不改描述符)。 + // 属性已存在:CreateGlobalVarBinding 不改既有描述符——accessor 形零修改 + // (不覆写、不抛);数据属性不可写时 strict 抛 TypeError、sloppy 静默 + // no-op;可写数据属性保原描述符仅更新值。 if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), si) { if let Some(current) = obj.prop_meta_at(pos) { - let writable = !current.is_accessor && current.attributes.writable(); - if !writable { + // accessor 形:var 声明写点不覆写访问器,两模式均零修改。 + if current.is_accessor { + return Ok(()); + } + // 既有数据属性不可写:strict 抛 TypeError、sloppy 静默 no-op。 + if !current.attributes.writable() { return if self.current_strict() { self.raise_error_kind("TypeError", "cannot assign to read-only property") } else { Ok(()) }; } + // 可写数据属性:保原描述符仅更新值。 return self.define_data_property(obj, si, value, current.attributes); } } diff --git a/crates/oxide_vm/tests/global_var_binding_tests.rs b/crates/oxide_vm/tests/global_var_binding_tests.rs index abf21900..471724f3 100644 --- a/crates/oxide_vm/tests/global_var_binding_tests.rs +++ b/crates/oxide_vm/tests/global_var_binding_tests.rs @@ -616,3 +616,43 @@ fn nonextensible_global_var_existing_property_no_throw() { "var ex; ex === 1 && globalThis.ex === 1", ); } + +// ── 顶层 var 撞 builtin 名与 accessor 形:写面对照 node 锚定(零覆写 / 可写仅更值)── + +#[test] +fn var_init_on_builtin_math_updates_value() { + // var Math = 1:Math 是既有可写数据属性(c:true、e:false),PutValue 更新 + // 值、保 enumerable;裸读与反射见同一新值。 + eval_truthy("var Math = 1; Math === 1 && globalThis.Math === 1 && typeof Math === 'number'"); + eval_truthy( + "var Math = 1; var d = Object.getOwnPropertyDescriptor(globalThis, 'Math'); \ + d.value === 1 && d.writable === true && d.enumerable === false && d.configurable === true", + ); +} + +#[test] +fn strict_var_init_on_builtin_math_updates_value() { + // strict 同形:可写 builtin 名更新值不抛。 + eval_truthy("'use strict'; var Math = 1; Math === 1 && globalThis.Math === 1"); +} + +#[test] +fn var_init_on_accessor_property_does_not_overwrite() { + // accessor 形:var 声明写点不覆写访问器,两模式均零修改(描述符保持原样)。 + // 既有 accessor 须先于 GDI 存在,分两阶段。写面锚定:globalThis 反射见 + // 访问器原值(getter 返回 1),描述符保持 accessor 形(get 在、无 value 面)。 + assert_two_phases_truthy( + "Object.defineProperty(globalThis, 'acc', {get() { return 1; }, configurable: true})", + "var acc = 2; globalThis.acc === 1", + ); + assert_two_phases_truthy( + "Object.defineProperty(globalThis, 'acc2', {get() { return 1; }, configurable: true})", + "var acc2 = 2; var d = Object.getOwnPropertyDescriptor(globalThis, 'acc2'); \ + typeof d.get === 'function' && d.value === undefined", + ); + // strict 同形:accessor 零修改不抛。 + assert_two_phases_truthy( + "Object.defineProperty(globalThis, 'acc3', {get() { return 1; }, configurable: true})", + "'use strict'; var acc3 = 2; globalThis.acc3 === 1", + ); +}