fix: replaceAll 类正则对象统一走 ToString 文本路径与 replace 对齐
This commit is contained in:
@@ -1013,8 +1013,8 @@ pub fn string_split<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|||||||
/// (`s.replace()` 全缺省等价于把 "undefined" 替换为 "undefined",结果与
|
/// (`s.replace()` 全缺省等价于把 "undefined" 替换为 "undefined",结果与
|
||||||
/// 原串一致;`s.replace("b")` 得到 "aundefinedc" 而非 "ac")。
|
/// 原串一致;`s.replace("b")` 得到 "aundefinedc" 而非 "ac")。
|
||||||
/// - replaceAll 遇非 global 正则抛 TypeError;类正则对象(proto 恒等
|
/// - replaceAll 遇非 global 正则抛 TypeError;类正则对象(proto 恒等
|
||||||
/// RegExp.prototype 但无编译正则)replaceAll 返回原串、replace 按 ToString
|
/// RegExp.prototype 但无编译正则)replace/replaceAll 统一按 ToString 文本走
|
||||||
/// 文本走字符串路径。
|
/// 字符串路径。
|
||||||
///
|
///
|
||||||
/// # 注意事项
|
/// # 注意事项
|
||||||
/// - 分支 B 的 `&H` 共享借用与 `&mut` 互斥由编译器强制,三 `&str` 同源可共存
|
/// - 分支 B 的 `&H` 共享借用与 `&mut` 互斥由编译器强制,三 `&str` 同源可共存
|
||||||
@@ -1066,13 +1066,8 @@ fn string_replace_impl<H: VmHost>(vm: &mut H, args: &[u8], all: bool) -> NativeR
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// 类正则对象无编译正则:replaceAll 按现状返回原串(receiver 转换先行保错误次序)。
|
|
||||||
if all && is_re && !has_native_re {
|
|
||||||
let s = try_string!(this_string(vm, args)).into_owned();
|
|
||||||
return NativeResult::Ok(vm.new_string_owned(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
// replaceAll 要求正则带 global:非 global 正则直接抛 TypeError(规范 flags 检查)。
|
// replaceAll 要求正则带 global:非 global 正则直接抛 TypeError(规范 flags 检查)。
|
||||||
|
// 类正则对象(无编译正则)无 flags 概念,统一走 as_string 文本路径,不在此检查。
|
||||||
if all && has_native_re && !is_global {
|
if all && has_native_re && !is_global {
|
||||||
return NativeResult::Err(crate::error::create_type_error(
|
return NativeResult::Err(crate::error::create_type_error(
|
||||||
vm,
|
vm,
|
||||||
|
|||||||
@@ -398,7 +398,9 @@ fn string_replace_all_nonglobal_regex_throws() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn string_replace_regex_proto_subclass_fallback() {
|
fn string_replace_regex_proto_subclass_fallback() {
|
||||||
// 类正则对象(proto 恒等 RegExp.prototype 但无编译正则):
|
// 类正则对象(proto 恒等 RegExp.prototype 但无编译正则):
|
||||||
// replace 走字符串文本路径,replaceAll 返回原串。
|
// replace/replaceAll 统一按 ToString 文本走字符串路径;默认 toString
|
||||||
|
// 不命中源串,故此处断言结果与原串一致(判别性用例见
|
||||||
|
// string_replace_regex_proto_subclass_custom_tostring)。
|
||||||
let mut vm = Vm::new();
|
let mut vm = Vm::new();
|
||||||
let s = eval(&mut vm, "var sp = Object.create(RegExp.prototype); 'aXb'.replace(sp, 'Y')").unwrap();
|
let s = eval(&mut vm, "var sp = Object.create(RegExp.prototype); 'aXb'.replace(sp, 'Y')").unwrap();
|
||||||
assert_eq!(to_str(&vm, s), "aXb");
|
assert_eq!(to_str(&vm, s), "aXb");
|
||||||
@@ -412,6 +414,37 @@ fn string_replace_regex_proto_subclass_fallback() {
|
|||||||
assert_eq!(to_str(&vm, s), "aXb");
|
assert_eq!(to_str(&vm, s), "aXb");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_replace_regex_proto_subclass_custom_tostring() {
|
||||||
|
// 判别性回归:自定义 toString 命中源串的类正则对象,replace/replaceAll
|
||||||
|
// 均按 ToString 文本替换(不返回原串、不抛 TypeError),两入口对称。
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let s = eval(
|
||||||
|
&mut vm,
|
||||||
|
"var sp = Object.create(RegExp.prototype); sp.toString = function(){ return 'X' }; 'aXb'.replace(sp, 'Y')",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(to_str(&vm, s), "aYb");
|
||||||
|
let s = eval(
|
||||||
|
&mut vm,
|
||||||
|
"var sp = Object.create(RegExp.prototype); sp.toString = function(){ return 'X' }; 'aXb'.replaceAll(sp, 'Y')",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(to_str(&vm, s), "aYb");
|
||||||
|
let s = eval(
|
||||||
|
&mut vm,
|
||||||
|
"var sp = Object.create(RegExp.prototype); sp.toString = function(){ return 'X' }; 'aXb'.replace(sp, function(){ return 'Z' })",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(to_str(&vm, s), "aZb");
|
||||||
|
let s = eval(
|
||||||
|
&mut vm,
|
||||||
|
"var sp = Object.create(RegExp.prototype); sp.toString = function(){ return 'X' }; 'aXb'.replaceAll(sp, function(){ return 'Z' })",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(to_str(&vm, s), "aZb");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn string_replace_boxed_receiver_and_replacement() {
|
fn string_replace_boxed_receiver_and_replacement() {
|
||||||
// boxed receiver 走对象 ToString 路径,boxed replacement 走 ToString 转换。
|
// boxed receiver 走对象 ToString 路径,boxed replacement 走 ToString 转换。
|
||||||
@@ -434,6 +467,34 @@ fn string_replace_astral_and_empty_pattern() {
|
|||||||
assert_eq!(to_str(&vm, s), "-a-b-c-");
|
assert_eq!(to_str(&vm, s), "-a-b-c-");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_replace_numeric_pattern() {
|
||||||
|
// 数字 pattern 经 ToString 文本替换("1"),replace/replaceAll 均走字符串路径。
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let s = eval(&mut vm, "'a1b'.replace(1, 'X')").unwrap();
|
||||||
|
assert_eq!(to_str(&vm, s), "aXb");
|
||||||
|
let s = eval(&mut vm, "'a1b1c'.replaceAll(1, 'X')").unwrap();
|
||||||
|
assert_eq!(to_str(&vm, s), "aXbXc");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_replace_all_boxed_string_pattern() {
|
||||||
|
// boxed String pattern 经 ToString 文本替换。
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let s = eval(&mut vm, "'abc'.replaceAll(new String('b'), 'X')").unwrap();
|
||||||
|
assert_eq!(to_str(&vm, s), "aXc");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_replace_empty_receiver() {
|
||||||
|
// 空串 receiver + 空 pattern:仅位置 0 一次匹配。
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let s = eval(&mut vm, "''.replace('', 'x')").unwrap();
|
||||||
|
assert_eq!(to_str(&vm, s), "x");
|
||||||
|
let s = eval(&mut vm, "''.replaceAll('', 'x')").unwrap();
|
||||||
|
assert_eq!(to_str(&vm, s), "x");
|
||||||
|
}
|
||||||
|
|
||||||
// ── split 测试 ──
|
// ── split 测试 ──
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user