From a12daebea60a0a326f2a0862d25422962dd20f3d Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Tue, 22 Sep 2026 01:09:52 +0000 Subject: [PATCH] =?UTF-8?q?test:=20=E9=92=89=20exec/match=20=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=20groups/indices=20=E9=9D=A2=E4=B8=8E=E6=9E=84?= =?UTF-8?q?=E9=80=A0=E5=99=A8=E7=A9=BA=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oxide_vm/tests/builtins_regexp_tests.rs | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/crates/oxide_vm/tests/builtins_regexp_tests.rs b/crates/oxide_vm/tests/builtins_regexp_tests.rs index 84f1bff7..3425768e 100644 --- a/crates/oxide_vm/tests/builtins_regexp_tests.rs +++ b/crates/oxide_vm/tests/builtins_regexp_tests.rs @@ -822,3 +822,137 @@ fn regexp_constructor_regexp_instance_form() { let result = eval(&mut vm, "new RegExp(/ab/g).flags").unwrap(); assert_eq!(to_str(&vm, result), "g"); } + +// --- exec/match 结果面:groups 对象(null 原型、重名键序)与 indices(d 标志)--- + +#[test] +fn regexp_exec_duplicate_named_groups_first_source_order() { + // groups 键序按首现源序(y 先于 x);各分支命中各自命名组取值。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(() => { var m = /(?a)(?a)|(?b)(?b)/.exec('aa'); return Object.keys(m.groups).join(',') + '|' + m.groups.y + m.groups.x; })()", + ) + .unwrap(); + assert_eq!(to_str(&vm, result), "y,x|aa"); + let result = eval( + &mut vm, + "(() => { var m = /(?a)(?a)|(?b)(?b)/.exec('bb'); return Object.keys(m.groups).join(',') + '|' + m.groups.y + m.groups.x; })()", + ) + .unwrap(); + assert_eq!(to_str(&vm, result), "y,x|bb"); +} + +#[test] +fn regexp_exec_groups_undefined_own_property() { + // 无命名组时 groups 是自身属性 undefined(非删除),对象不构建。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(() => { var m = /a/.exec('a'); return (m.groups === undefined) + ':' + m.hasOwnProperty('groups') + ':' + (/(a)/.exec('a').groups === undefined); })()", + ) + .unwrap(); + assert_eq!(to_str(&vm, result), "true:true:true"); +} + +#[test] +fn regexp_exec_groups_null_prototype() { + // groups 对象原型为 null(ObjectCreate(null)),不挂 Object.prototype。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "Object.getPrototypeOf(/a(?b)/.exec('ab').groups) === null").unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn regexp_constructor_undefined_pattern_is_empty() { + // undefined 模式按空串编译(非 "undefined"):串头空命中,index 0。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(() => { var m = new RegExp(undefined).exec('xyz'); return new RegExp(undefined).source + ':' + m[0] + ':' + m.index; })()", + ) + .unwrap(); + assert_eq!(to_str(&vm, result), "::0"); +} + +#[test] +fn regexp_exec_indices_basic_pairs() { + // d 标志:indices[0] 为完整匹配 [start, end] 码元对,逐捕获组同形; + // 无 d 标志时无 indices 属性。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(() => { var m = /(?a)b/d.exec('cab'); return m.indices[0][0] + ',' + m.indices[0][1] + '|' + m.indices[1][0] + ',' + m.indices[1][1]; })()", + ) + .unwrap(); + assert_eq!(to_str(&vm, result), "1,3|1,2"); + let result = eval(&mut vm, "/(a)/.exec('a').indices === undefined").unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn regexp_exec_indices_unmatched_and_groups() { + // 未匹配捕获组在 indices 中为 undefined;indices.groups 原型 null、值为码元对。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(() => { var m = /a(?b)?/d.exec('a'); return m.indices[1] === undefined ? 'u' : 'n'; })()", + ) + .unwrap(); + assert_eq!(to_str(&vm, result), "u"); + let result = eval( + &mut vm, + "(() => { var m = /(?a)b/d.exec('cab'); return (Object.getPrototypeOf(m.indices.groups) === null) + ':' + m.indices.groups.g[0] + ',' + m.indices.groups.g[1]; })()", + ) + .unwrap(); + assert_eq!(to_str(&vm, result), "true:1,2"); +} + +#[test] +fn string_match_delegates_exec_result() { + // 非 global match 交付 exec 结果本体(index/input/未匹配捕获 undefined/ + // groups null 原型);缺参按空模式串头命中。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(() => { var m = 'x0'.match(/(0)(1)?/); return m.index + ':' + m.input + ':' + (m[2] === undefined ? 'u' : 'n') + ':' + (m.groups === undefined ? 'u' : 'n'); })()", + ) + .unwrap(); + assert_eq!(to_str(&vm, result), "1:x0:u:u"); + let result = eval( + &mut vm, + "(() => { var m = 'x'.match(); return m.length + ':' + m.index + ':' + m[0] + ':' + m.input; })()", + ) + .unwrap(); + assert_eq!(to_str(&vm, result), "1:0::x"); +} + +#[test] +fn string_split_unmatched_capture_undefined() { + // split 捕获组未匹配时元素为 undefined(非空串)。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(() => { var p = 'xa(xb)'.split(/x(a)(b)?/); return p.length + ':' + (p[2] === undefined ? 'u' : 'n') + ':' + p[3]; })()", + ) + .unwrap(); + assert_eq!(to_str(&vm, result), "4:u:(xb)"); +} + +#[test] +fn string_match_all_result_indices_and_unmatched() { + // d 标志下 matchAll 结果挂 indices(与 exec 同面);未匹配捕获组 undefined。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(() => { var it = 'aab'.matchAll(/a(?a)?/dg); var r = it.next().value; return JSON.stringify(r.indices) + ':' + JSON.stringify(r.groups) + ':' + r.index; })()", + ) + .unwrap(); + assert_eq!(to_str(&vm, result), "[[0,2],[1,2]]:{\"g\":\"a\"}:0"); + let result = eval( + &mut vm, + "(() => { var it = 'x0'.matchAll(/(0)(1)?/g); var r = it.next().value; return r[2] === undefined ? 'u' : 'n'; })()", + ) + .unwrap(); + assert_eq!(to_str(&vm, result), "u"); +}