From 53b16537bcd4c64796b399c455592606fb88c625 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Fri, 25 Sep 2026 16:52:25 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=AD=97=E7=AC=A6=E4=B8=B2=E5=8E=9F?= =?UTF-8?q?=E5=A7=8B=E5=80=BC=E6=95=B4=E6=95=B0=E7=B4=A2=E5=BC=95=E8=AF=BB?= =?UTF-8?q?=E8=BF=94=E7=A0=81=E5=85=83=E5=AD=90=E4=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_vm/src/dispatch/property.rs | 15 +++++++ .../tests/property_array_fastpath_tests.rs | 40 +++++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/crates/oxide_vm/src/dispatch/property.rs b/crates/oxide_vm/src/dispatch/property.rs index 3564a547..d46bd8d0 100644 --- a/crates/oxide_vm/src/dispatch/property.rs +++ b/crates/oxide_vm/src/dispatch/property.rs @@ -73,6 +73,21 @@ impl Vm { let len = unsafe { (*val.as_string_ptr()).utf16_len() }; return Ok(Some(JsValue::int(len as i32))); } + // 整数索引键:规范 String exotic [[Get]] 界内返码元子串(码元口径非码点), + // 越界返 undefined——必须 Some(undefined) 而非 None,None 会被调用点当 + // "非原始值属性"回落对象臂抛 TypeError。 + if is_int_key(prop_name_si) { + // SAFETY: val 是字符串值。 + let s = unsafe { &*val.as_string_ptr() }; + let len = s.utf16_len(); + let idx = int_key_value(prop_name_si); + if idx < len { + // 码元读取:ASCII 命中共享 perm 串零分配,非 ASCII 物化 1 单元会话串。 + let units = s.units(); + return Ok(Some(self.unit_char_value(units[idx as usize]))); + } + return Ok(Some(JsValue::undefined())); + } let proto_ptr = self.session.builtin_world().string_proto.as_ptr() as *mut JsObject; let proto = unsafe { &*proto_ptr }; return self.ordinary_get(proto, prop_name_si, val).map(Some); diff --git a/crates/oxide_vm/tests/property_array_fastpath_tests.rs b/crates/oxide_vm/tests/property_array_fastpath_tests.rs index c0499682..6e90dfc0 100644 --- a/crates/oxide_vm/tests/property_array_fastpath_tests.rs +++ b/crates/oxide_vm/tests/property_array_fastpath_tests.rs @@ -132,11 +132,43 @@ fn plain_object_int_key_unaffected() { assert_eq!(eval_str("var o={}; o[0]=\"y\"; o[0]").unwrap(), "y"); } -// 非对象 receiver(`"abc"[0]`):is_object 快检先行,不 raise、返回 undefined。 +// 字符串原始值 receiver 的整数索引读:界内返码元子串(规范 String exotic [[Get]])。 #[test] -fn primitive_string_index_does_not_raise() { - assert_eq!(eval_str("\"abc\"[0]").unwrap(), "undefined"); - assert_eq!(eval_str("var a=\"abc\"; a[0]").unwrap(), "undefined"); +fn primitive_string_index_returns_code_unit() { + assert_eq!(eval_str("\"abc\"[0]").unwrap(), "a"); + assert_eq!(eval_str("\"abc\"[1]").unwrap(), "b"); + assert_eq!(eval_str("\"abc\"[2]").unwrap(), "c"); + assert_eq!(eval_str("var a=\"abc\"; a[0]").unwrap(), "a"); +} + +// 越界 / 负索引 / 非整数键读字符串原始值返 undefined(不 raise)。 +#[test] +fn primitive_string_index_oob_and_non_integral_undefined() { + assert_eq!(eval_str("\"abc\"[3]").unwrap(), "undefined"); + assert_eq!(eval_str("\"abc\"[-1]").unwrap(), "undefined"); + assert_eq!(eval_str("\"abc\"[1.5]").unwrap(), "undefined"); +} + +// 非 ASCII 码元口径:界内整数索引返单码元子串(码元口径非码点), +// 孤立 surrogate 按 1 单元交付(lossy 显示为 U+FFFD)。 +#[test] +fn primitive_string_index_non_ascii_code_unit() { + assert_eq!(eval_str("\"é\"[0]").unwrap(), "é"); + assert_eq!(eval_str("\"😀a\"[0]").unwrap().chars().count(), 1, "孤立 surrogate 应恰为 1 单元"); + assert_eq!(eval_str("\"😀a\"[2]").unwrap(), "a"); + assert_eq!(eval_str("\"😀a\"[3]").unwrap(), "undefined"); +} + +// 链式读:界内索引返字符串后 .length 可继续(不 raise)。 +#[test] +fn primitive_string_index_chained_length() { + assert_eq!(eval_str("\"abc\"[0].length").unwrap(), "1"); +} + +// 写面不变:sloppy 下 s[0]="x" 装箱即弃 no-op。 +#[test] +fn primitive_string_index_write_noop() { + assert_eq!(eval_str("var s=\"abc\"; s[0]=\"x\"; s").unwrap(), "abc"); } // push 构造的数组 fast path 读写与 length 一致。