From 79dcc1d553927759e5496c4519841bc68abd8704 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sat, 26 Sep 2026 23:17:53 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20RegExp=20flags=20getter=20=E5=AF=B9?= =?UTF-8?q?=E6=97=A0=E6=A7=BD=E5=AF=B9=E8=B1=A1=E6=8C=89=E8=A7=84=E8=8C=83?= =?UTF-8?q?=E8=AF=BB=E6=97=97=E8=AE=BF=E9=97=AE=E5=99=A8=E7=BB=84=E4=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/regexp.rs | 50 +++++++++++++++---- .../oxide_vm/tests/builtins_regexp_tests.rs | 21 ++++++++ 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/crates/oxide_builtins/src/regexp.rs b/crates/oxide_builtins/src/regexp.rs index 3048dbe4..664f1577 100644 --- a/crates/oxide_builtins/src/regexp.rs +++ b/crates/oxide_builtins/src/regexp.rs @@ -999,22 +999,50 @@ pub fn regexp_get_unicode_sets(vm: &mut H, args: &[u8]) -> NativeResu } /// `RegExp.prototype.flags` getter:返回实例 flags 串。 +/// +/// 非对象 this 抛 TypeError;持编译正则槽的对象直接返槽串;其余对象 +/// (含 prototype 本身)无槽可返,按规范顺序读 8 个旗属性并以 ToBoolean +/// 判位组串。 pub fn regexp_get_flags(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(args[0]); - // proto 恒等判定:规范对 prototype 本身返回 undefined。 - if this_val.is_object() { + if !this_val.is_object() { + return NativeResult::Err(crate::error::create_type_error(vm, "RegExp.prototype method called on non-object")); + } + let this_ptr = this_val.as_js_object_ptr(); + if this_ptr.is_null() { + return NativeResult::Err(crate::error::create_type_error(vm, "null object")); + } + // 持编译正则槽的对象直接返槽串。 + // SAFETY: this_ptr 已确认非空对象值,借用即时消费。 + if unsafe { &*this_ptr }.is_regexp_obj() { + // SAFETY: 同上,槽串由对象持有。 + return NativeResult::Ok(unsafe { &*this_ptr }.get_regexp_flags()); + } + // 无槽对象:按规范序(d,g,i,m,s,u,v,y)读 8 个旗属性,ToBoolean 判位 + // 组串。每次读都可能触发用户 getter 重入(session GC 可搬移对象), + // 对象指针须每次读前从 this 寄存器(GC 根)重取。 + let mut out: Vec = Vec::with_capacity(8); + for (name, unit) in [ + ("hasIndices", 'd'), + ("global", 'g'), + ("ignoreCase", 'i'), + ("multiline", 'm'), + ("dotAll", 's'), + ("unicode", 'u'), + ("unicodeSets", 'v'), + ("sticky", 'y'), + ] { + let this_val = vm.reg(args[0]); let this_ptr = this_val.as_js_object_ptr(); - let proto_ptr = vm.session().builtin_world().regexp_proto.as_ptr() as *mut JsObject; - if !this_ptr.is_null() && this_ptr == proto_ptr { - return NativeResult::Ok(JsValue::undefined()); + let v = match rx_get_bool_prop(vm, this_ptr, name, this_val) { + Ok(v) => v, + Err(err) => return NativeResult::Err(err), + }; + if v { + out.push(unit as u16); } } - let re_ptr = match get_regexp_ptr(vm, args) { - Ok(ptr) => ptr, - Err(err) => return NativeResult::Err(err), - }; - let re = unsafe { &*re_ptr }; - NativeResult::Ok(re.get_regexp_flags()) + NativeResult::Ok(vm.new_string_units(&out)) } /// `RegExp.prototype.source` getter:返回实例 source 串。 diff --git a/crates/oxide_vm/tests/builtins_regexp_tests.rs b/crates/oxide_vm/tests/builtins_regexp_tests.rs index bb3c026c..7369912c 100644 --- a/crates/oxide_vm/tests/builtins_regexp_tests.rs +++ b/crates/oxide_vm/tests/builtins_regexp_tests.rs @@ -1188,3 +1188,24 @@ fn regexp_v_flag_zero_width_surrogate_pair_match_returns() { let result = eval(&mut vm, "var m = '𠮷a'.match(/(?:)/g); m.length + ':' + m.join('|')").unwrap(); assert_eq!(to_str(&vm, result), "4:|||"); } + +// --- RegExp.prototype.flags getter --- + +#[test] +fn flags_getter_plain_object_reads_accessor_props() { + // 无 [[RegExp]] 槽的对象 this:按规范序读 8 个旗访问器(hasIndices/ + // global/ignoreCase/multiline/dotAll/unicode/unicodeSets/sticky)并以 + // ToBoolean 判位组串;prototype 本身读得全 falsy 返空串。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "var get = Object.getOwnPropertyDescriptor(RegExp.prototype, 'flags').get; get.call({ global: 'string', ignoreCase: 1 })").unwrap(); + assert_eq!(to_str(&vm, result), "gi"); + let result = eval( + &mut vm, + "var get = Object.getOwnPropertyDescriptor(RegExp.prototype, 'flags').get; get.call(RegExp.prototype)", + ) + .unwrap(); + assert_eq!(to_str(&vm, result), ""); + // 非对象 this 抛 TypeError。 + let result = eval(&mut vm, "var get = Object.getOwnPropertyDescriptor(RegExp.prototype, 'flags').get; try { get.call(4); 'no-throw' } catch (e) { e instanceof TypeError }").unwrap(); + assert!(result.as_bool()); +}