fix: 排序默认比较改 UTF-16 码元序

This commit is contained in:
2026-10-05 09:55:41 +00:00
parent 88107f45c5
commit a1cbb82fb6
5 changed files with 44 additions and 23 deletions
Generated
+18 -18
View File
@@ -1095,7 +1095,7 @@ dependencies = [
[[package]]
name = "oxide_builtins"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"chrono",
"fast-float",
@@ -1120,7 +1120,7 @@ dependencies = [
[[package]]
name = "oxide_bytecode"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"num-bigint",
"oxide_log",
@@ -1128,7 +1128,7 @@ dependencies = [
[[package]]
name = "oxide_cfg"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"oxide_bytecode",
"oxide_emit",
@@ -1139,7 +1139,7 @@ dependencies = [
[[package]]
name = "oxide_cli"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"ansi_term",
"bumpalo",
@@ -1159,7 +1159,7 @@ dependencies = [
[[package]]
name = "oxide_code_cache"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"lru",
"oxide_bytecode",
@@ -1168,7 +1168,7 @@ dependencies = [
[[package]]
name = "oxide_compiler"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1187,7 +1187,7 @@ dependencies = [
[[package]]
name = "oxide_dce"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1201,7 +1201,7 @@ dependencies = [
[[package]]
name = "oxide_emit"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"num-bigint",
"oxc_allocator",
@@ -1216,7 +1216,7 @@ dependencies = [
[[package]]
name = "oxide_ir"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"oxide_bytecode",
"oxide_log",
@@ -1225,7 +1225,7 @@ dependencies = [
[[package]]
name = "oxide_kernel"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"bumpalo",
"dashmap",
@@ -1240,7 +1240,7 @@ dependencies = [
[[package]]
name = "oxide_liveness"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1252,7 +1252,7 @@ dependencies = [
[[package]]
name = "oxide_log"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"tracing",
"tracing-appender",
@@ -1261,7 +1261,7 @@ dependencies = [
[[package]]
name = "oxide_parser"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1276,7 +1276,7 @@ dependencies = [
[[package]]
name = "oxide_regalloc"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1292,7 +1292,7 @@ dependencies = [
[[package]]
name = "oxide_runtime_api"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"num-bigint",
"num-traits",
@@ -1304,7 +1304,7 @@ dependencies = [
[[package]]
name = "oxide_test262"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1321,7 +1321,7 @@ dependencies = [
[[package]]
name = "oxide_types"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"bumpalo",
"hashbrown 0.14.5",
@@ -1333,7 +1333,7 @@ dependencies = [
[[package]]
name = "oxide_vm"
version = "0.8.5"
version = "0.8.6"
dependencies = [
"bumpalo",
"chrono",
+1 -1
View File
@@ -22,7 +22,7 @@ members = [
]
[workspace.package]
version = "0.8.5"
version = "0.8.6"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+1 -1
View File
@@ -75,7 +75,7 @@ pub fn array_with<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
/// # 边界与前提
/// - 先校验 compareFn 可调用(非 undefined 且不可调用则抛 TypeError),再读取
/// this/length;
/// - 未提供比较回调时按 ToString 结果的字符串字典序排序;底层 `sort_by` 为稳定
/// - 未提供比较回调时按 ToString 结果的 UTF-16 码元字典序排序;底层 `sort_by` 为稳定
/// 排序,比较结果相等的元素保持原有相对次序。
pub fn array_to_sorted<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
builtins_debug!("Array.prototype.toSorted called with {} args", args.len());
@@ -16,7 +16,7 @@ use super::common::{
/// 对元素向量执行 `Array.prototype.sort` 的比较语义(原地排序)。
///
/// 默认按 ToString 结果的字符串字典序比较;提供比较回调时按其返回值
/// 默认按 ToString 结果的 UTF-16 码元字典序比较;提供比较回调时按其返回值
///(<0/=0/>0)决定次序,回调抛错则中止并把异常原样返回。
pub(crate) fn sort_values_inner<H: VmHost>(
vm: &mut H, vals: &mut [JsValue], comparator: Option<JsValue>,
@@ -48,9 +48,12 @@ pub(crate) fn sort_values_inner<H: VmHost>(
}
}
} else {
// 规范 CompareArrayElements 对 ToString 结果做关系比较,字符串按
// UTF-16 码元字典序;码元序列比较与码点序在补充平面字符处分歧,
// 此处以码元序为准。
let sa = oxide_runtime_api::to_string(*a);
let sb = oxide_runtime_api::to_string(*b);
sa.cmp(&sb)
sa.encode_utf16().cmp(sb.encode_utf16())
}
});
if let Some(err) = sort_error {
@@ -72,7 +75,7 @@ pub(crate) fn parse_sort_comparator<H: VmHost>(vm: &mut H, candidate: JsValue) -
/// 原地排序数组元素并返回原数组(`Array.prototype.sort`)。
///
/// 未提供比较回调时按 ToString 结果的字符串字典序排序;底层 `sort_by` 为稳定
/// 未提供比较回调时按 ToString 结果的 UTF-16 码元字典序排序;底层 `sort_by` 为稳定
/// 排序,比较结果相等(含 NaN)的元素保持原有相对次序。洞位不参与比较:
/// present 值稳定排序后自索引 0 紧凑写回,尾部空位转为洞。
///
@@ -351,6 +351,24 @@ fn array_sort_propagates_comparator_exception() {
assert_eq!(to_str(&vm, result), "cmp");
}
#[test]
fn array_sort_default_orders_by_utf16_code_units() {
// 默认排序按 UTF-16 码元字典序(规范 CompareArrayElements):补充平面字符
// 以高代理(U+D800 及以上)参与比较,小于 U+E000..U+FFFF,与码点序分歧。
// 前三例为判别例(码点序下结果相反),后两例为边界钉(两口径一致)。
let cases = [
("['\\u{10000}','\\uFFFF'].sort().join('')", "\u{10000}\u{FFFF}"),
("['\\uFFFF','\\u{10000}'].toSorted().join('')", "\u{10000}\u{FFFF}"),
("['\\u{10000}','A','\\uFFFF'].sort().join('')", "A\u{10000}\u{FFFF}"),
("['\\uD7FF','\\u{10000}'].sort().join('')", "\u{D7FF}\u{10000}"),
("['\\u{12345}','\\u{10000}'].sort().join('')", "\u{10000}\u{12345}"),
];
for (source, expected) in cases {
let (vm, result) = eval(source).unwrap();
assert_eq!(to_str(&vm, result), expected, "source: {source}");
}
}
#[test]
fn array_callback_type_errors_are_not_sentinels() {
let (vm, result) = eval("try { [1].map(null) } catch (e) { e.name }").unwrap();