perf: replace 字符串 pattern 走共享借用零拷贝并修 replace 语义

This commit is contained in:
2026-08-14 12:54:27 +08:00
parent e1747ca560
commit bd9b981362
2 changed files with 296 additions and 100 deletions
@@ -305,6 +305,113 @@ fn string_replace_string_pattern() {
assert_eq!(to_str(&vm, s), "heLlo");
}
#[test]
fn string_replace_string_pattern_only_first() {
// 字符串 pattern 只替换首个匹配。
let mut vm = Vm::new();
let s = eval(&mut vm, "'aba'.replace('a', 'c')").unwrap();
assert_eq!(to_str(&vm, s), "cba");
}
#[test]
fn string_replace_regex_global_all() {
// 正则 global 全替换,非 global 只替换首个。
let mut vm = Vm::new();
let s = eval(&mut vm, "'hello'.replace(/l/g, 'L')").unwrap();
assert_eq!(to_str(&vm, s), "heLLo");
let s = eval(&mut vm, "'hello'.replace(/l/, 'L')").unwrap();
assert_eq!(to_str(&vm, s), "heLlo");
}
#[test]
fn string_replace_function_replacer_string_pattern() {
// 函数 replacer + 字符串 pattern:回调参数 (match, position, string)。
let mut vm = Vm::new();
let s = eval(&mut vm, "'ab'.replace('a', function(m){ return m.toUpperCase() })").unwrap();
assert_eq!(to_str(&vm, s), "Ab");
let s = eval(&mut vm, "'abc'.replace('b', function(m, o){ return o })").unwrap();
assert_eq!(to_str(&vm, s), "a1c");
}
#[test]
fn string_replace_missing_replacement_is_undefined_string() {
// 缺失 replaceValue 按 ToString(undefined)="undefined" 替换(非空串)。
let mut vm = Vm::new();
let s = eval(&mut vm, "'abc'.replace('b')").unwrap();
assert_eq!(to_str(&vm, s), "aundefinedc");
let s = eval(&mut vm, "'abc'.replaceAll('b')").unwrap();
assert_eq!(to_str(&vm, s), "aundefinedc");
let s = eval(&mut vm, "'abc'.replace('b', undefined)").unwrap();
assert_eq!(to_str(&vm, s), "aundefinedc");
}
#[test]
fn string_replace_missing_args_replace_undefined_pattern() {
// 缺失 searchValue 按 "undefined" 文本替换(找不到则原串不变)。
let mut vm = Vm::new();
let s = eval(&mut vm, "'xundefinedy'.replace()").unwrap();
assert_eq!(to_str(&vm, s), "xundefinedy");
let s = eval(&mut vm, "'xundefinedy'.replaceAll()").unwrap();
assert_eq!(to_str(&vm, s), "xundefinedy");
let s = eval(&mut vm, "'abc'.replace()").unwrap();
assert_eq!(to_str(&vm, s), "abc");
}
#[test]
fn string_replace_all_nonglobal_regex_throws() {
// replaceAll 遇非 global 正则抛 TypeError。
let mut vm = Vm::new();
for src in [
"'abc'.replaceAll(/b/, 'X')",
"'abc'.replaceAll(/b/, function(){ return 'X' })",
"String.prototype.replaceAll.call('abc', /b/)",
] {
let err = eval(&mut vm, src).unwrap_err();
assert!(err.contains("TypeError"), "{src} 应抛 TypeError,实际: {err}");
}
let s = eval(&mut vm, "'abc'.replaceAll(/b/g, 'X')").unwrap();
assert_eq!(to_str(&vm, s), "aXc");
}
#[test]
fn string_replace_regex_proto_subclass_fallback() {
// 类正则对象(proto 恒等 RegExp.prototype 但无编译正则):
// replace 走字符串文本路径,replaceAll 返回原串。
let mut vm = Vm::new();
let s = eval(&mut vm, "var sp = Object.create(RegExp.prototype); 'aXb'.replace(sp, 'Y')").unwrap();
assert_eq!(to_str(&vm, s), "aXb");
let s = eval(&mut vm, "var sp = Object.create(RegExp.prototype); 'aXb'.replaceAll(sp, 'Y')").unwrap();
assert_eq!(to_str(&vm, s), "aXb");
let s = eval(
&mut vm,
"var sp = Object.create(RegExp.prototype); 'aXb'.replace(sp, function(m){ return 'Z' })",
)
.unwrap();
assert_eq!(to_str(&vm, s), "aXb");
}
#[test]
fn string_replace_boxed_receiver_and_replacement() {
// boxed receiver 走对象 ToString 路径,boxed replacement 走 ToString 转换。
let mut vm = Vm::new();
let s = eval(&mut vm, "new String('abc').replace('b', 'X')").unwrap();
assert_eq!(to_str(&vm, s), "aXc");
let s = eval(&mut vm, "'abc'.replace('b', new String('X'))").unwrap();
assert_eq!(to_str(&vm, s), "aXc");
}
#[test]
fn string_replace_astral_and_empty_pattern() {
// astral 串走零拷贝快路径不破坏代理对;空 pattern 语义保持。
let mut vm = Vm::new();
let s = eval(&mut vm, "'\\u{1F600}x\\u{1F600}'.replace('x', 'Y')").unwrap();
assert_eq!(to_str(&vm, s), "\u{1F600}Y\u{1F600}");
let s = eval(&mut vm, "'abc'.replace('', '-')").unwrap();
assert_eq!(to_str(&vm, s), "-abc");
let s = eval(&mut vm, "'abc'.replaceAll('', '-')").unwrap();
assert_eq!(to_str(&vm, s), "-a-b-c-");
}
// ── split 测试 ──
#[test]