From b860fdf5ee6d8716c667d25a9520983225c6200c Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sat, 15 Aug 2026 04:28:01 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20string.search=20=E6=97=A0=E5=8F=82?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E8=B6=8A=E7=95=8C=20panic=EF=BC=8C=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E8=AF=BB=E5=8F=96=E5=AE=88=E5=8D=AB=E5=89=8D=E7=BD=AE?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=20-1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/string.rs | 4 +- .../oxide_vm/tests/builtins_string_tests.rs | 41 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/crates/oxide_builtins/src/string.rs b/crates/oxide_builtins/src/string.rs index e80bbfc..a3e19f0 100644 --- a/crates/oxide_builtins/src/string.rs +++ b/crates/oxide_builtins/src/string.rs @@ -1245,7 +1245,9 @@ pub fn string_match_fn(vm: &mut H, args: &[u8]) -> NativeResult { /// `String.prototype.search(pattern)`:返回首个匹配位置,无匹配返回 -1。 pub fn string_search(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.search called with {} args", args.len()); - let pattern_val = vm.reg(args[1]); + // 参数读取守卫先行:无参调用缺省 searchString 为 undefined(args 仅含 this 槽)。 + let pattern_val = if args.len() >= 2 { vm.reg(args[1]) } else { JsValue::undefined() }; + // 正则判定与参数转换先行(&mut 路径),后借 this 扫描。 let is_re = args.len() >= 2 && is_regexp_obj(pattern_val, vm); let pattern = if args.len() >= 2 && !is_re { diff --git a/crates/oxide_vm/tests/builtins_string_tests.rs b/crates/oxide_vm/tests/builtins_string_tests.rs index b4d038b..67515eb 100644 --- a/crates/oxide_vm/tests/builtins_string_tests.rs +++ b/crates/oxide_vm/tests/builtins_string_tests.rs @@ -196,6 +196,47 @@ fn string_search_not_found() { assert_eq!(result.as_int(), -1); } +#[test] +fn string_search_no_args_returns_minus_one() { + // 无参调用缺省 searchString:args 仅含 this 槽,守卫返回 -1,不越界 panic。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "'abc'.search()").unwrap(); + assert_eq!(result.as_int(), -1); +} + +#[test] +fn string_search_undefined_arg_returns_minus_one() { + // 显式 undefined 参数:ToString(undefined)="undefined",在 "abc" 中无匹配返回 -1。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "'abc'.search(undefined)").unwrap(); + assert_eq!(result.as_int(), -1); +} + +#[test] +fn string_search_empty_pattern_returns_zero() { + // 空串 pattern:find("") 命中串头,返回 0。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "'abc'.search('')").unwrap(); + assert_eq!(result.as_int(), 0); +} + +#[test] +fn string_no_arg_guard_family_consistent() { + // 家族无参守卫对照:search/indexOf 返回 -1,includes/startsWith/endsWith 返回 false, + // charAt 取首字符——search 与同批方法守卫位置一致、行为家族化。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "'abc'.search()").unwrap(); + assert_eq!(result.as_int(), -1); + let result = eval(&mut vm, "'abc'.indexOf()").unwrap(); + assert_eq!(result.as_int(), -1); + let result = eval(&mut vm, "'abc'.includes()").unwrap(); + assert!(!result.as_bool()); + let result = eval(&mut vm, "'abc'.startsWith()").unwrap(); + assert!(!result.as_bool()); + let result = eval(&mut vm, "'abc'.endsWith()").unwrap(); + assert!(!result.as_bool()); +} + #[test] fn string_trim_start() { let mut vm = Vm::new();