From eb2fb002496809e4b280e01d94c268e42a1286f9 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Thu, 17 Sep 2026 17:54:29 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=AD=97=E7=AC=A6=E4=B8=B2=E5=85=B3?= =?UTF-8?q?=E7=B3=BB=E6=AF=94=E8=BE=83=E7=BB=9F=E4=B8=80=E6=8C=89=20UTF-16?= =?UTF-8?q?=20=E7=A0=81=E5=85=83=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_runtime_api/src/coercion.rs | 10 ++++--- crates/oxide_vm/tests/comparison_tests.rs | 33 +++++++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/crates/oxide_runtime_api/src/coercion.rs b/crates/oxide_runtime_api/src/coercion.rs index 8a286a2d..9daf5feb 100644 --- a/crates/oxide_runtime_api/src/coercion.rs +++ b/crates/oxide_runtime_api/src/coercion.rs @@ -513,17 +513,19 @@ fn strict_double_eq(a: f64, b: f64) -> bool { /// Relational Comparison(`<`,ECMA-262 §7.2.13)的原始值版本。 /// -/// 双字符串按字典序;否则转数值比较,任一侧为 NaN 时返回 `None`(表示比较未定义,调用方据此处理 `<`/`>`)。 +/// 双字符串按 UTF-16 码元字典序比较,两条形态路径口径统一;否则转数值比较, +/// 任一侧为 NaN 时返回 `None`(表示比较未定义,调用方据此处理 `<`/`>`)。 pub fn relational_compare(lhs: JsValue, rhs: JsValue) -> Option { if lhs.is_string() && rhs.is_string() { // SAFETY: 两侧均为字符串值,借用即时消费。 let l = unsafe { &*lhs.as_string_ptr() }; let r = unsafe { &*rhs.as_string_ptr() }; if l.is_flat() && r.is_flat() { - // 双 Flat:直接按文本(UTF-8 码点序)字典序比较;任一侧非 Flat 时整体走单元序列比较。 - return Some(l.as_str() < r.as_str()); + // 双 Flat:流式按 UTF-16 码元字典序比较,补充平面字符拆为高低代理参与排序, + // 与下方单元通道相对 U+E000..U+FFFF 的 BMP 字符结果一致,免物化分配。 + return Some(l.as_str().encode_utf16().cmp(r.as_str().encode_utf16()) == Ordering::Less); } - // 单元口径:码元字典序即规范序,Flat 编码与 rope 扁平化统一走单元通道。 + // 任一侧非 Flat:走单元序列,同为 UTF-16 码元字典序(规范 §7.2.13 的 code unit 序)。 return Some(l.units() < r.units()); } if lhs.is_bigint() && rhs.is_bigint() { diff --git a/crates/oxide_vm/tests/comparison_tests.rs b/crates/oxide_vm/tests/comparison_tests.rs index acacf28d..5fdd1b05 100644 --- a/crates/oxide_vm/tests/comparison_tests.rs +++ b/crates/oxide_vm/tests/comparison_tests.rs @@ -124,3 +124,36 @@ fn eval_unary_plus_nan() { fn eval_unary_plus_false() { assert_eq!(eval("+false"), "0"); } + +#[test] +fn eval_string_relational_utf16_code_unit_order() { + // 字符串关系比较按 UTF-16 码元序:补充平面字符(首码元 D800..DBFF)小于 + // U+E000..U+FFFF 的 BMP 字符。覆盖双 Flat、单侧 rope、ASCII 与等值边界。 + let cases = [ + // 超平面 vs BMP 高区,四运算符双向。 + (r#""\u{10000}" < "\uFFFF""#, "true"), + (r#""\u{10000}" > "\uFFFF""#, "false"), + (r#""\u{10000}" <= "\uFFFF""#, "true"), + (r#""\u{10000}" >= "\uFFFF""#, "false"), + (r#""\uFFFF" < "\u{10000}""#, "false"), + (r#""\u{10000}" < "\uE000""#, "true"), + (r#""\uE000" < "\u{10000}""#, "false"), + // 等值边界:代理对写法与转义写法内容相同。 + (r#""\u{10000}" < "\u{10000}""#, "false"), + (r#""\u{10000}" <= "\u{10000}""#, "true"), + (r#""\u{10000}" >= "\uD800\uDC00""#, "true"), + // 单侧非 Flat:左侧 rope 首码元为 D800,兜底单元通道结果与双 Flat 一致。 + (r#""\u{10000}" + "a".repeat(200) < "\uFFFF""#, "true"), + (r#""\uFFFF" < "\u{10000}" + "a".repeat(200)"#, "false"), + // ASCII 与公共前缀:字节序与码元序一致。 + (r#""a" < "b""#, "true"), + (r#""abc" < "abd""#, "true"), + (r#""abc" < "abc""#, "false"), + (r#""abc" <= "abc""#, "true"), + (r#""b" > "a""#, "true"), + ("\"\" < \"a\"", "true"), + ]; + for (source, expected) in cases { + assert_eq!(eval(source), expected, "source: {source}"); + } +}