mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-27 04:01:11 +08:00
fix: RegExp source getter EscapeRegExpPattern + groups 对象构建
This commit is contained in:
@@ -4,7 +4,26 @@ use oxide_types::value::JsValue;
|
|||||||
|
|
||||||
use oxide_runtime_api::{NativeResult, VmHost};
|
use oxide_runtime_api::{NativeResult, VmHost};
|
||||||
|
|
||||||
use crate::string::{make_units_array, OwnedText};
|
use crate::string::{make_units_array, MatchText, OwnedText};
|
||||||
|
|
||||||
|
/// 构建 RegExp match result 的 `groups` 对象:键为命名捕获组名,值为匹配字符串或 undefined。
|
||||||
|
pub(crate) fn build_groups_object<H: VmHost>(vm: &mut H, m: ®ress::Match, text: &MatchText) -> JsValue {
|
||||||
|
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||||
|
let groups_obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
|
||||||
|
let groups_ptr = unsafe { &mut *groups_obj };
|
||||||
|
|
||||||
|
// 遍历所有命名捕获组,按名称设置属性(重复名称按规范取最后一次匹配)。
|
||||||
|
for (name, range) in m.named_groups() {
|
||||||
|
let name_si = vm.kernel_core().perm_interner().intern(name).0;
|
||||||
|
let value = match range {
|
||||||
|
Some(r) => vm.new_string_units_owned(text.slice(r.start, r.end).into_owned()),
|
||||||
|
None => JsValue::undefined(),
|
||||||
|
};
|
||||||
|
vm.set_or_create_prop_value(groups_ptr, name_si, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
JsValue::from_js_object(groups_obj)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_regexp_ptr<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<*mut JsObject, JsValue> {
|
fn get_regexp_ptr<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<*mut JsObject, JsValue> {
|
||||||
let this_val = vm.reg(args[0]);
|
let this_val = vm.reg(args[0]);
|
||||||
@@ -584,8 +603,9 @@ pub fn regexp_exec<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|||||||
let input_val = haystack.to_value(vm);
|
let input_val = haystack.to_value(vm);
|
||||||
let input_si = vm.kernel_core().perm_interner().intern("input").0;
|
let input_si = vm.kernel_core().perm_interner().intern("input").0;
|
||||||
vm.set_or_create_prop_value(unsafe { &mut *arr }, input_si, input_val);
|
vm.set_or_create_prop_value(unsafe { &mut *arr }, input_si, input_val);
|
||||||
|
let groups_val = build_groups_object(vm, &m, &text);
|
||||||
let groups_si = vm.kernel_core().perm_interner().intern("groups").0;
|
let groups_si = vm.kernel_core().perm_interner().intern("groups").0;
|
||||||
vm.set_or_create_prop_value(unsafe { &mut *arr }, groups_si, JsValue::undefined());
|
vm.set_or_create_prop_value(unsafe { &mut *arr }, groups_si, groups_val);
|
||||||
|
|
||||||
// 成功:lastIndex 推进到匹配末尾(仅 global/sticky),Set 语义。
|
// 成功:lastIndex 推进到匹配末尾(仅 global/sticky),Set 语义。
|
||||||
if tracks_last_index {
|
if tracks_last_index {
|
||||||
@@ -814,12 +834,12 @@ pub fn regexp_get_flags<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|||||||
/// `RegExp.prototype.source` getter:返回实例 source 串。
|
/// `RegExp.prototype.source` getter:返回实例 source 串。
|
||||||
pub fn regexp_get_source<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
pub fn regexp_get_source<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
let this_val = vm.reg(args[0]);
|
let this_val = vm.reg(args[0]);
|
||||||
// proto 恒等判定:规范对 prototype 本身返回 undefined。
|
// proto 恒等判定:规范对 prototype 本身返回 "(?:)"(无 [[OriginalFlags]] 槽时)。
|
||||||
if this_val.is_object() {
|
if this_val.is_object() {
|
||||||
let this_ptr = this_val.as_js_object_ptr();
|
let this_ptr = this_val.as_js_object_ptr();
|
||||||
let proto_ptr = vm.session().builtin_world().regexp_proto.as_ptr() as *mut JsObject;
|
let proto_ptr = vm.session().builtin_world().regexp_proto.as_ptr() as *mut JsObject;
|
||||||
if !this_ptr.is_null() && this_ptr == proto_ptr {
|
if !this_ptr.is_null() && this_ptr == proto_ptr {
|
||||||
return NativeResult::Ok(JsValue::undefined());
|
return NativeResult::Ok(vm.new_string_units_owned(vec!['(' as u16, '?' as u16, ':' as u16, ')' as u16]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let re_ptr = match get_regexp_ptr(vm, args) {
|
let re_ptr = match get_regexp_ptr(vm, args) {
|
||||||
@@ -827,7 +847,54 @@ pub fn regexp_get_source<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|||||||
Err(err) => return NativeResult::Err(err),
|
Err(err) => return NativeResult::Err(err),
|
||||||
};
|
};
|
||||||
let re = unsafe { &*re_ptr };
|
let re = unsafe { &*re_ptr };
|
||||||
NativeResult::Ok(re.get_regexp_source())
|
let source = re.get_regexp_source();
|
||||||
|
let flags = re.get_regexp_flags();
|
||||||
|
let source_str = vm.lookup_str(source).unwrap_or_default();
|
||||||
|
let flags_str = vm.lookup_str(flags).unwrap_or_default();
|
||||||
|
NativeResult::Ok(vm.new_string_units_owned(escape_regexp_pattern(&source_str, &flags_str)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `EscapeRegExpPattern(pattern, flags)`:按规范转义 `\`、`/`(非 u/v 旗时)
|
||||||
|
/// 与行终止符,使结果可安全嵌入 `/pattern/flags` 字面量。
|
||||||
|
fn escape_regexp_pattern(pattern: &str, flags: &str) -> Vec<u16> {
|
||||||
|
let has_uv = flags.contains('u') || flags.contains('v');
|
||||||
|
let mut out: Vec<u16> = Vec::with_capacity(pattern.len() + 4);
|
||||||
|
for ch in pattern.chars() {
|
||||||
|
match ch {
|
||||||
|
'/' | '\\' if !has_uv => {
|
||||||
|
out.push('\\' as u16);
|
||||||
|
out.push(ch as u16);
|
||||||
|
}
|
||||||
|
'\n' => {
|
||||||
|
out.push('\\' as u16);
|
||||||
|
out.push('n' as u16);
|
||||||
|
}
|
||||||
|
'\r' => {
|
||||||
|
out.push('\\' as u16);
|
||||||
|
out.push('r' as u16);
|
||||||
|
}
|
||||||
|
'\u{2028}' => {
|
||||||
|
out.push('\\' as u16);
|
||||||
|
out.push('u' as u16);
|
||||||
|
out.push('2' as u16);
|
||||||
|
out.push('0' as u16);
|
||||||
|
out.push('2' as u16);
|
||||||
|
out.push('8' as u16);
|
||||||
|
}
|
||||||
|
'\u{2029}' => {
|
||||||
|
out.push('\\' as u16);
|
||||||
|
out.push('u' as u16);
|
||||||
|
out.push('2' as u16);
|
||||||
|
out.push('0' as u16);
|
||||||
|
out.push('2' as u16);
|
||||||
|
out.push('9' as u16);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
out.push(ch as u16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 取匹配文本参数,按 ToString 语义完整转换(对象经 toString/valueOf);
|
/// 取匹配文本参数,按 ToString 语义完整转换(对象经 toString/valueOf);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ pub(crate) enum MatchText<'a> {
|
|||||||
impl<'a> MatchText<'a> {
|
impl<'a> MatchText<'a> {
|
||||||
/// 从字符串值按载荷形态借用:Flat → Str 零拷贝,其余 → Units
|
/// 从字符串值按载荷形态借用:Flat → Str 零拷贝,其余 → Units
|
||||||
/// (FlatU16 直接借用、Cons 经扁平化缓存)。
|
/// (FlatU16 直接借用、Cons 经扁平化缓存)。
|
||||||
fn from_value(val: JsValue) -> Self {
|
pub(crate) fn from_value(val: JsValue) -> Self {
|
||||||
debug_assert!(val.is_string());
|
debug_assert!(val.is_string());
|
||||||
// SAFETY: val 为字符串值,借用期存活由调用方借用纪律保证。
|
// SAFETY: val 为字符串值,借用期存活由调用方借用纪律保证。
|
||||||
let sp = unsafe { &*val.as_string_ptr() };
|
let sp = unsafe { &*val.as_string_ptr() };
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ use oxide_runtime_api::{NativeResult, VmHost};
|
|||||||
|
|
||||||
use crate::builtins_debug;
|
use crate::builtins_debug;
|
||||||
use crate::builtins_error;
|
use crate::builtins_error;
|
||||||
|
use crate::regexp::build_groups_object;
|
||||||
|
|
||||||
use super::common::try_string;
|
use super::common::{try_string, MatchText};
|
||||||
use super::{
|
use super::{
|
||||||
as_units, byte_to_unit, find_units, make_string_array_values, make_units_array, map_well_formed_segments,
|
as_units, byte_to_unit, find_units, make_string_array_values, make_units_array, map_well_formed_segments,
|
||||||
this_text, this_units, unit_to_byte,
|
this_text, this_units, unit_to_byte,
|
||||||
@@ -1050,7 +1051,7 @@ pub fn string_match_all_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
|||||||
if idx > total_units {
|
if idx > total_units {
|
||||||
return make_match_done_result(vm, JsValue::undefined());
|
return make_match_done_result(vm, JsValue::undefined());
|
||||||
}
|
}
|
||||||
let (match_start, next_idx, parts): (usize, usize, Vec<Vec<u16>>) = if sp.is_flat() {
|
let (match_start, next_idx, parts, m_opt): (usize, usize, Vec<Vec<u16>>, Option<regress::Match>) = if sp.is_flat() {
|
||||||
let s = sp.as_str();
|
let s = sp.as_str();
|
||||||
match regex.find_from(s, unit_to_byte(s, idx)).next() {
|
match regex.find_from(s, unit_to_byte(s, idx)).next() {
|
||||||
Some(m) => {
|
Some(m) => {
|
||||||
@@ -1068,9 +1069,9 @@ pub fn string_match_all_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
|||||||
let start_units = byte_to_unit(s, range.start);
|
let start_units = byte_to_unit(s, range.start);
|
||||||
let end_units = byte_to_unit(s, range.end);
|
let end_units = byte_to_unit(s, range.end);
|
||||||
let next_idx = if range.end > range.start { end_units } else { end_units + 1 };
|
let next_idx = if range.end > range.start { end_units } else { end_units + 1 };
|
||||||
(start_units, next_idx, parts)
|
(start_units, next_idx, parts, Some(m))
|
||||||
}
|
}
|
||||||
None => (0, 0, Vec::new()),
|
None => (0, 0, Vec::new(), None),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let u = sp.units();
|
let u = sp.units();
|
||||||
@@ -1086,9 +1087,9 @@ pub fn string_match_all_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let next_idx = if range.end > range.start { range.end } else { range.end + 1 };
|
let next_idx = if range.end > range.start { range.end } else { range.end + 1 };
|
||||||
(range.start, next_idx, parts)
|
(range.start, next_idx, parts, Some(m))
|
||||||
}
|
}
|
||||||
None => (0, 0, Vec::new()),
|
None => (0, 0, Vec::new(), None),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1097,12 +1098,20 @@ pub fn string_match_all_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
|||||||
}
|
}
|
||||||
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int(next_idx as i32));
|
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int(next_idx as i32));
|
||||||
// 构建结果数组:按元素物化字符串值 + 挂 index/input/groups 属性。
|
// 构建结果数组:按元素物化字符串值 + 挂 index/input/groups 属性。
|
||||||
let value = make_match_result_array(vm, parts, match_start as i32, input_val);
|
let match_text = MatchText::from_value(input_val);
|
||||||
|
let value = make_match_result_array(vm, parts, match_start as i32, input_val, m_opt.as_ref(), &match_text);
|
||||||
make_match_done_result(vm, value)
|
make_match_done_result(vm, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 构建 matchAll 结果数组:元素为匹配字符串值,附带 index/input/groups 属性。
|
/// 构建 matchAll 结果数组:元素为匹配字符串值,附带 index/input/groups 属性。
|
||||||
fn make_match_result_array<H: VmHost>(vm: &mut H, parts: Vec<Vec<u16>>, match_index: i32, input_val: JsValue) -> JsValue {
|
fn make_match_result_array<H: VmHost>(
|
||||||
|
vm: &mut H,
|
||||||
|
parts: Vec<Vec<u16>>,
|
||||||
|
match_index: i32,
|
||||||
|
input_val: JsValue,
|
||||||
|
m: Option<®ress::Match>,
|
||||||
|
text: &MatchText,
|
||||||
|
) -> JsValue {
|
||||||
let values: Vec<JsValue> = parts.into_iter().map(|u| vm.new_string_units_owned(u)).collect();
|
let values: Vec<JsValue> = parts.into_iter().map(|u| vm.new_string_units_owned(u)).collect();
|
||||||
let arr = make_string_array_values(vm, values);
|
let arr = make_string_array_values(vm, values);
|
||||||
let arr_ptr = arr.as_js_object_ptr();
|
let arr_ptr = arr.as_js_object_ptr();
|
||||||
@@ -1111,8 +1120,12 @@ fn make_match_result_array<H: VmHost>(vm: &mut H, parts: Vec<Vec<u16>>, match_in
|
|||||||
vm.set_or_create_prop_value(arr_obj, index_si, JsValue::int(match_index));
|
vm.set_or_create_prop_value(arr_obj, index_si, JsValue::int(match_index));
|
||||||
let input_si = vm.kernel_core().perm_interner().intern("input").0;
|
let input_si = vm.kernel_core().perm_interner().intern("input").0;
|
||||||
vm.set_or_create_prop_value(arr_obj, input_si, input_val);
|
vm.set_or_create_prop_value(arr_obj, input_si, input_val);
|
||||||
|
let groups_val = match m {
|
||||||
|
Some(m) => build_groups_object(vm, m, text),
|
||||||
|
None => JsValue::undefined(),
|
||||||
|
};
|
||||||
let groups_si = vm.kernel_core().perm_interner().intern("groups").0;
|
let groups_si = vm.kernel_core().perm_interner().intern("groups").0;
|
||||||
vm.set_or_create_prop_value(arr_obj, groups_si, JsValue::undefined());
|
vm.set_or_create_prop_value(arr_obj, groups_si, groups_val);
|
||||||
arr
|
arr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user