fix: property_key_si 对象与兜底原始值键统一走字符串键规范化

对象经 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"] 键等价。
This commit is contained in:
2026-08-15 04:20:26 +08:00
parent 17ee852ced
commit 6c9b8419c7
2 changed files with 24 additions and 3 deletions
+6 -3
View File
@@ -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<u32> {
+18
View File
@@ -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");