From 6c9b8419c741438d576ca1cafa38debb01b7ff97 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sat, 15 Aug 2026 04:20:26 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20property=5Fkey=5Fsi=20=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E4=B8=8E=E5=85=9C=E5=BA=95=E5=8E=9F=E5=A7=8B=E5=80=BC=E9=94=AE?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E8=B5=B0=E5=AD=97=E7=AC=A6=E4=B8=B2=E9=94=AE?= =?UTF-8?q?=E8=A7=84=E8=8C=83=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 对象经 ToPrimitive 转字符串后直接 intern 绕过 canonical_index_of,产生 与整数键区间分裂的字符串键(o[{toString(){return "5"}}] 与 o[5] 不同 键);bigint 等兜底原始值键同病。统一经 string_key_si 规范化:规范数字 串映射整数键,其余 intern,与字符串分支及 03.1 各建键入口口径一致。 补回归:对象 ToPropertyKey 数字串键与 o[5]/o["5"] 键等价(读写双向)、 o[5n] 与 o[5]/o["5"] 键等价。 --- crates/oxide_vm/src/vm.rs | 9 ++++++--- crates/oxide_vm/tests/object_tests.rs | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 0bd157f..c978761 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -975,16 +975,19 @@ impl Vm { if let Some(id) = oxide_runtime_api::well_known_symbol_id(self, val.as_js_object_ptr()) { return Ok(make_well_known_symbol_key(id)); } - // ToPropertyKey:对象经 ToPrimitive(string hint),结果为 Symbol 时直接作键。 + // ToPropertyKey:对象经 ToPrimitive(string hint),结果为 Symbol 时直接作键; + // 其余字符串经规范化(规范数字串映射整数键)与字符串分支统一口径。 let prim = coercion::to_primitive(val, coercion::ToPrimitiveHint::String, self)?; if prim.is_symbol() { return Ok(make_symbol_key(prim.as_symbol_index())); } let key = coercion::to_string(prim); - return Ok(self.kernel_core.perm_interner().intern(&key).0); + return Ok(oxide_runtime_api::VmHost::string_key_si(self, &key)); } + // 其它原始值(BigInt 等):ToPropertyKey 一律转字符串并走规范化,避免与 + // 数字键区间分裂(`o[5n]` 与 `o["5"]`/`o[5]` 必须同键)。 let key = coercion::to_string(val); - Ok(self.kernel_core.perm_interner().intern(&key).0) + Ok(oxide_runtime_api::VmHost::string_key_si(self, &key)) } pub(crate) fn array_index_from_property_key(&self, prop_name_si: u32) -> Option { diff --git a/crates/oxide_vm/tests/object_tests.rs b/crates/oxide_vm/tests/object_tests.rs index 56b2780..5b6f761 100644 --- a/crates/oxide_vm/tests/object_tests.rs +++ b/crates/oxide_vm/tests/object_tests.rs @@ -205,6 +205,24 @@ fn eval_numeric_property_key_roundtrip() { assert_eq!(eval("var o={}; o[1]=42; o['1']"), "42"); } +#[test] +fn eval_object_primitive_hint_key_unifies_with_numeric_key() { + // 对象经 ToPropertyKey 的字符串结果必须走规范化:`{toString(){return "5"}}` + // 作为键与 `o[5]`/`o["5"]` 同键,不得 intern 出分裂的字符串键。 + assert_eq!(eval("var o={}; o[{toString(){return \"5\"}}]=7; o[\"5\"]"), "7"); + assert_eq!(eval("var o={}; o[{toString(){return \"5\"}}]=7; o[5]"), "7"); + assert_eq!(eval("var o={}; o[\"5\"]=7; o[{toString(){return \"5\"}}]"), "7"); + // 字符串值 Display 为 {string},键等价语义与数值哨兵一致。 + assert_eq!(eval("var o={}; o[{toString(){return \"5\"}}]='a'; o[\"5\"]"), "{string}"); +} + +#[test] +fn eval_bigint_key_unifies_with_numeric_key() { + // 兜底原始值键(BigInt)同样走规范化:`o[5n]` 与 `o["5"]`/`o[5]` 同键。 + assert_eq!(eval("var o={}; o[5n]=7; o[\"5\"]"), "7"); + assert_eq!(eval("var o={}; o[5n]=7; o[5]"), "7"); +} + #[test] fn eval_numeric_dynamic_property_key_roundtrip() { assert_eq!(eval("var o={}; var k=1; o[k]=42; o['1']"), "42");