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();