use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::JsObject; use oxide_types::value::JsValue; use oxide_runtime_api::{NativeResult, VmHost}; use crate::builtins_debug; use crate::builtins_error; use crate::regexp::build_groups_object; use super::common::{try_string, MatchText}; use super::{ as_units, find_units, make_string_array_values, make_units_array, map_well_formed_segments, split_limit_to_uint32, this_text, this_units, }; // ── 正则替换(单元口径) ──────────────────────────────────────────────── /// 调用函数 replacer 并把返回值转为单元序列;调用抛出的异常原样恢复。 fn call_replacer(vm: &mut H, replacer: JsValue, cb_args: &[JsValue]) -> Result, JsValue> { match vm.call_function_sync(replacer, JsValue::undefined(), cb_args) { Ok(result) => match oxide_runtime_api::to_units_full(result, vm) { Ok(units) => Ok(units), Err(_) => { // 转换失败(Symbol 等)或回调异常均须原样传播。 if let Some(exc) = vm.take_uncaught_value() { return Err(exc); } Err(crate::error::create_type_error(vm, "Cannot convert a Symbol value to a string")) } }, Err(err) => Err(vm .take_uncaught_value() .unwrap_or_else(|| crate::error::create_type_error(vm, &format!("replace replacer: {}", err)))), } } /// 判断值是否为 RegExp 对象:非对象、空指针、原型非对象均返回 false, /// 原型指针与 `%RegExpPrototype%` 恒等比较。 fn is_regexp_obj(val: JsValue, vm: &H) -> bool { if !val.is_object() { return false; } let ptr = val.as_js_object_ptr(); if ptr.is_null() { return false; } let obj = unsafe { &*ptr }; let proto = obj.proto(); if !proto.is_object() { return false; } let proto_ptr = proto.as_js_object_ptr(); if proto_ptr.is_null() { return false; } let rp = vm.session().builtin_world().regexp_proto.as_ptr() as *mut JsObject; std::ptr::eq(proto_ptr, rp) } /// IsRegExp 口径判定:非对象 false;对象一律 Get(@@match) 后 ToBoolean /// (真 RegExp 亦读,getter 副作用可观测;getter 抛错恢复原异常上抛)。 /// 供正则匹配族与 includes/startsWith/endsWith 的 IsRegExp 前置判共用。 pub(crate) fn is_regexp_live(vm: &mut H, val: JsValue) -> Result { if !val.is_object() { return Ok(false); } let match_key = oxide_types::private_key::make_well_known_symbol_key(1); let ptr = val.as_js_object_ptr(); // SAFETY: val 已校验为非空对象值。 let obj = unsafe { &*ptr }; let b = match vm.ordinary_get(obj, match_key, val) { Ok(v) => v, Err(_) => { if let Some(exc) = vm.take_uncaught_value() { return Err(exc); } return Err(crate::error::create_type_error(vm, "cannot read @@match")); } }; Ok(oxide_runtime_api::to_boolean(b)) } /// GetMethod(searchValue, @@replace):undefined/null 返回 None,不可调用抛 /// TypeError,属性读抛错恢复原异常值。 fn rx_get_replace_method( vm: &mut H, rx: *mut JsObject, this_val: JsValue, ) -> Result, JsValue> { let replace_key = oxide_types::private_key::make_well_known_symbol_key(2); // SAFETY: this_val 为对象值,rx 与其同址。 let func = match vm.ordinary_get(unsafe { &*rx }, replace_key, this_val) { Ok(v) => v, Err(_) => { if let Some(exc) = vm.take_uncaught_value() { return Err(exc); } return Err(crate::error::create_type_error(vm, "cannot read @@replace")); } }; if func.is_undefined() || func.is_null() { return Ok(None); } if !crate::iterator::is_callable(func) { return Err(crate::error::create_type_error(vm, "Symbol.replace is not callable")); } Ok(Some(func)) } /// 字符串臂的替换形态:功能替换器(回调值)或非功能替换串单元序列, /// 恰好其一在场。 enum ArmReplacer<'a> { Fn(JsValue), Text(&'a [u16]), } /// 字符串臂:子串定位 + 逐命中替换。功能替换器经 /// Call(replaceValue, undefined, «matched, position, string»)+ToString;非功能 /// 走 GetSubstitution(捕获表空、namedCaptures undefined)。空模式在每个码元 /// 边界命中(replaceAll 含串尾,replace 仅串首)。 fn string_arm_replace( vm: &mut H, text: &[u16], search: &[u16], replacer: &ArmReplacer<'_>, all: bool, s_val: JsValue, ) -> NativeResult { let search_length = search.len(); let mut positions: Vec = Vec::new(); if search_length == 0 { positions.push(0); if all { for i in 1..=text.len() { positions.push(i); } } } else { let mut start = 0; while let Some(p) = find_units(text, search, start) { positions.push(p); if !all { break; } start = p + search_length; } } let mut out = Vec::new(); let mut last_end = 0; for p in positions { out.extend_from_slice(&text[last_end..p]); let matched = &text[p..p + search_length]; let repl = match replacer { ArmReplacer::Fn(f) => { let cb_args = [vm.new_string_units(matched), JsValue::int(p as i32), s_val]; try_string!(call_replacer(vm, *f, &cb_args)) } ArmReplacer::Text(r) => match crate::regexp::get_substitution_units(vm, matched, text, p, &[], None, r) { Ok(u) => u, Err(e) => return NativeResult::Err(e), }, }; out.extend_from_slice(&repl); last_end = p + search_length; } out.extend_from_slice(&text[last_end..]); NativeResult::Ok(vm.new_string_units_owned(out)) } // ── 拆分 / 正则匹配 ───────────────────────────────────────────────────── /// GetMethod(sep, @@split):undefined/null 返回 None(落字符串臂),不可调用 /// 抛 TypeError,属性读抛错恢复原异常值。 fn rx_get_split_method(vm: &mut H, sep: JsValue) -> Result, JsValue> { let split_key = oxide_types::private_key::make_well_known_symbol_key(4); let ptr = sep.as_js_object_ptr(); // SAFETY: 调用方已校验 sep 为非空对象值。 let func = match vm.ordinary_get(unsafe { &*ptr }, split_key, sep) { Ok(v) => v, Err(_) => { if let Some(exc) = vm.take_uncaught_value() { return Err(exc); } return Err(crate::error::create_type_error(vm, "cannot read @@split")); } }; if func.is_undefined() || func.is_null() { return Ok(None); } if !crate::iterator::is_callable(func) { return Err(crate::error::create_type_error(vm, "Symbol.split is not callable")); } Ok(Some(func)) } /// `String.prototype.split(separator, limit)`:按分隔符拆分为字符串数组; /// 分隔符可为 RegExp(含捕获组)或字符串。空分隔按单元逐个产出(规格口径, /// 孤立 surrogate 为 1 单元元素)。 /// /// # 步骤 /// 1. receiver 前置校验(RequireObjectCoercible,纯 is_* 读取,先于一切分叉)。 /// 2. separator 为对象时:GetMethod(separator, @@split),可调用则 /// Call(splitter, separator, «thisValue, limit»)——传原始 this/limit /// 寄存器不预转换,结果原值返回(RegExp 分隔经此委托 Symbol.split)。 /// 3. string = ToString(this)(转换异常传播)。 /// 4. lim:limit 缺省或 undefined 为 2^32-1,否则 ToUint32(mod 2^32 回绕)。 /// 5. separatorString = ToString(separator)(转换异常传播;先于 lim = 0 判定)。 /// 6. lim = 0 → 空数组(先于 sep-undefined 判定)。 /// 7. separator 为 undefined → [string]。 /// 8. 空分隔逐码元产出(clamp(lim, 0, 串长));非空分隔搜索循环,尾段恒推。 /// /// # 边界与前提 /// - 规范序位:GetMethod 派发先于 ToString(this),ToUint32(limit) 先于 /// ToString(separator),ToString(separator) 先于 lim = 0 判定, /// lim = 0 判定先于 sep-undefined 判定。 /// - 各转换(this/limit/separator)抛出的原生异常原样传播。 pub fn string_split(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.split called with {} args", args.len()); let this_val = vm.reg(args[0]); // receiver 前置校验:null/undefined 抛 TypeError(RequireObjectCoercible, // 纯 is_* 读取无用户代码,先于一切分叉);symbol 的 ToString 必败,提前抛。 if this_val.is_null() || this_val.is_undefined() { return NativeResult::Err(crate::error::create_type_error( vm, "String.prototype method called on null or undefined", )); } if this_val.is_symbol() { return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert a Symbol value to a string")); } let sep_val = if args.len() >= 2 { vm.reg(args[1]) } else { JsValue::undefined() }; let limit_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; // 对象分隔符经 GetMethod 派发:调用定义则 Call 后原值返回,须先于 // ToString(this) 短路(实参为原始寄存器,不预转换)。 if sep_val.is_object() { let splitter = try_string!(rx_get_split_method(vm, sep_val)); if let Some(splitter) = splitter { return match vm.call_function_sync(splitter, sep_val, &[this_val, limit_val]) { Ok(r) => NativeResult::Ok(r), Err(_) => { if let Some(exc) = vm.take_uncaught_value() { return NativeResult::Err(exc); } NativeResult::Err(crate::error::create_type_error(vm, "split matcher call failed")) } }; } } // string = ToString(this)。 let s: Vec = try_string!(this_units(vm, args)).into_owned(); // lim = ToUint32(limit):undefined 为 2^32-1,对象经完整 ToNumber(异常传播)。 let lim = try_string!(split_limit_to_uint32(vm, limit_val)); // separatorString = ToString(separator)(先于 lim = 0 判定:sep 转换 // 抛错时 lim = 0 的早退不生效)。 let sep_units: Vec = try_string!(as_units(vm, sep_val)).into_owned(); if lim == 0 { return NativeResult::Ok(make_units_array(vm, Vec::new())); } // separator 为 undefined 时返回单元素 [string]。 if sep_val.is_undefined() { return NativeResult::Ok(make_units_array(vm, vec![s.to_vec()])); } if sep_units.is_empty() { // 每单元一个元素(ASCII 走单字符缓存零分配,其余 1 单元串创建)。 let mut values = Vec::with_capacity(s.len().min(lim)); for &u in s.iter().take(lim) { values.push(match vm.single_unit(u) { Some(v) => v, None => vm.new_string_units(&[u]), }); } return NativeResult::Ok(make_string_array_values(vm, values)); } // 非空字符串分隔:手动查找循环 + limit。 let mut parts: Vec> = Vec::new(); let mut start = 0; loop { if parts.len() >= lim { break; } match find_units(&s, &sep_units, start) { Some(p) => { parts.push(s[start..p].to_vec()); start = p + sep_units.len(); } None => { parts.push(s[start..].to_vec()); break; } } } NativeResult::Ok(make_units_array(vm, parts)) } /// `replace`/`replaceAll` 共用的分叉实现: /// /// # 步骤 /// 1. receiver 前置校验(RequireObjectCoercible,纯 is_* 读取)。 /// 2. searchValue 非 null/undefined 时:IsRegExp(真 RegExp 原型恒等;其余对象 /// Get @@match 后 ToBoolean,getter 抛错传播);replaceAll 命中正则时 live /// Get "flags" + RequireObjectCoercible + ToString 须含 "g"。 /// 3. GetMethod(searchValue, @@replace) 定义(可调用)则 /// Call(matcher, searchValue, «this, replaceValue»),结果原值返回(不 /// ToString);真 RegExp 默认经此委托 regexp_symbol_replace(参数槽位与 /// 直接派发一致)。 /// 4. 字符串臂:string = ToString(this)、searchString = ToString(searchValue) /// (异常传播),子串定位后逐命中替换(string_arm_replace)。 /// /// # 边界与前提 /// - 缺省 searchValue/replaceValue 均按 ToString(undefined)="undefined" 处理 /// (`s.replace()` 全缺省等价于把 "undefined" 替换为 "undefined",结果与 /// 原串一致;`s.replace("b")` 得到 "aundefinedc" 而非 "ac")。 /// - searchValue 为 null/undefined 时跳过整段第 2 步(无 IsRegExp/GetMethod)。 /// - searchValue 的 @@replace 为 undefined/null(含真 RegExp 自置 undefined) /// 回退字符串臂,searchString 取 ToString(searchValue)。 fn string_replace_impl(vm: &mut H, args: &[u8], all: bool) -> NativeResult { let this_val = vm.reg(args[0]); // receiver 前置校验:null/undefined/symbol 抛 TypeError(规范第 1 步 // RequireObjectCoercible,纯 is_* 读取无用户代码,先于一切分叉)。 if this_val.is_null() || this_val.is_undefined() { return NativeResult::Err(crate::error::create_type_error( vm, "String.prototype method called on null or undefined", )); } if this_val.is_symbol() { return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert a Symbol value to a string")); } let pattern_val = if args.len() >= 2 { vm.reg(args[1]) } else { JsValue::undefined() }; let replacement_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; // 第 2 步:searchValue 非 null/undefined 的对象走 IsRegExp + flags + // GetMethod;matcher 定义则 Call 后原值返回。 if !pattern_val.is_null() && !pattern_val.is_undefined() && pattern_val.is_object() { let is_regexp = match is_regexp_live(vm, pattern_val) { Ok(b) => b, Err(e) => return NativeResult::Err(e), }; if all && is_regexp { // 规范序:Get "flags"(单读,getter 异常传播)→ // RequireObjectCoercible(undefined/null 抛 TypeError)→ // ToString 须含 "g"(转换异常传播)。 let re_ptr = pattern_val.as_js_object_ptr(); // SAFETY: pattern_val 已校验为非空对象值。 let flags_val = match vm.ordinary_get( unsafe { &*re_ptr }, vm.kernel_core().perm_interner().intern("flags").0, pattern_val, ) { Ok(v) => v, Err(_) => { if let Some(exc) = vm.take_uncaught_value() { return NativeResult::Err(exc); } return NativeResult::Err(crate::error::create_type_error(vm, "cannot read flags")); } }; if flags_val.is_undefined() || flags_val.is_null() { return NativeResult::Err(crate::error::create_type_error( vm, "String.prototype.replaceAll called on a regex without the global flag", )); } let flags = match oxide_runtime_api::to_string_value_full(flags_val, vm) { Ok(v) => v, Err(_) => { if let Some(exc) = vm.take_uncaught_value() { return NativeResult::Err(exc); } return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a string")); } }; let flags_str = String::from_utf16_lossy(&vm.string_units(flags)); if !flags_str.contains('g') { return NativeResult::Err(crate::error::create_type_error( vm, "String.prototype.replaceAll called on a regex without the global flag", )); } } let re_ptr = pattern_val.as_js_object_ptr(); let matcher = match rx_get_replace_method(vm, re_ptr, pattern_val) { Ok(m) => m, Err(e) => return NativeResult::Err(e), }; if let Some(matcher) = matcher { // Call(matcher, searchValue, « this, replaceValue »)。 return match vm.call_function_sync(matcher, pattern_val, &[this_val, replacement_val]) { Ok(r) => NativeResult::Ok(r), Err(_) => { if let Some(exc) = vm.take_uncaught_value() { return NativeResult::Err(exc); } NativeResult::Err(crate::error::create_type_error(vm, "replace matcher call failed")) } }; } } // 字符串臂:string/searchString 完整 ToString(对象 toString/valueOf 抛错 // 原样传播);replacer 功能判定 IsCallable,非功能替换串先 ToString // (Symbol 抛 TypeError,对象转换抛错传播)。 let (s_units, s_val) = if this_val.is_string() { // SAFETY: this_val 为字符串值,借用即时消费。 let sp = unsafe { &*this_val.as_string_ptr() }; (sp.units().into_owned(), this_val) } else { let v = match oxide_runtime_api::to_string_value_full(this_val, vm) { Ok(v) => v, Err(_) => { if let Some(exc) = vm.take_uncaught_value() { return NativeResult::Err(exc); } return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a string")); } }; // SAFETY: v 为字符串值,借用即时消费。 let sp = unsafe { &*v.as_string_ptr() }; (sp.units().into_owned(), v) }; let search_units = if pattern_val.is_string() { // SAFETY: pattern_val 为字符串值,借用即时消费。 let sp = unsafe { &*pattern_val.as_string_ptr() }; sp.units().into_owned() } else if pattern_val.is_undefined() { "undefined".encode_utf16().collect() } else { match oxide_runtime_api::to_units_full(pattern_val, vm) { Ok(u) => u, Err(_) => { if let Some(exc) = vm.take_uncaught_value() { return NativeResult::Err(exc); } return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a string")); } } }; let functional = crate::iterator::is_callable(replacement_val); let replacement_units: Vec = if functional { Vec::new() } else { match crate::regexp::replacement_units(vm, replacement_val) { Ok(u) => u, Err(e) => return NativeResult::Err(e), } }; let replacer = if functional { ArmReplacer::Fn(replacement_val) } else { ArmReplacer::Text(&replacement_units) }; string_arm_replace(vm, &s_units, &search_units, &replacer, all, s_val) } /// `String.prototype.replace(pattern, replacement)`:替换首个匹配; /// 支持 RegExp(global 全替换)、字符串以及函数替换器。 pub fn string_replace(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.replace called with {} args", args.len()); string_replace_impl(vm, args, false) } /// `String.prototype.match(pattern)`:按 RegExp 匹配;global 返回全部匹配数组, /// 否则返回首个匹配及捕获组,无匹配返回 null。 pub fn string_match_fn(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.match called with {} args", args.len()); // 正则判定与参数字符串转换先行(&mut 路径),后借 this 匹配扫描。 let pattern_val = if args.len() >= 2 { Some(vm.reg(args[1])) } else { None }; let is_re = pattern_val.map(|v| is_regexp_obj(v, vm)).unwrap_or(false); // global 标志前置读:flags 串共享借用与下方 text 转换的 &mut 借用不可重叠。 let is_global = if is_re { let re_val = match pattern_val { Some(v) => v, None => return NativeResult::Err(crate::error::create_type_error(vm, "expected regexp")), }; let re_ptr = re_val.as_js_object_ptr(); // SAFETY: is_re 已保证 pattern_val 为非空对象且 proto 恒等 RegExp.prototype。 let re = unsafe { &*re_ptr }; re.native_fn().is_some() && crate::regexp::regexp_has_flag(vm, re, 'g') } else { false }; let text = try_string!(this_text(vm, args)); // 缺参按 undefined 模式(规范:构造空模式正则,串头命中)。 let pattern_val = match pattern_val { Some(v) => v, None => JsValue::undefined(), }; if is_re { let re_ptr = pattern_val.as_js_object_ptr(); if is_global { let re = unsafe { &*re_ptr }; let fn_ptr = match re.native_fn() { Some(p) => p, None => return NativeResult::Ok(JsValue::null()), }; // SAFETY: fn_ptr 持有 regexp_constructor 存放的 `Box` 指针。 let regex = unsafe { &*(fn_ptr.as_ptr() as *const regress::Regex) }; let mut matches: Vec> = Vec::new(); text.for_each_match(regex, |m| { let range = m.range(); matches.push(text.slice(range.start, range.end).into_owned()); }); if matches.is_empty() { return NativeResult::Ok(JsValue::null()); } return NativeResult::Ok(make_units_array(vm, matches)); } // 非 global:GetMethod(rx, @@match) + Invoke,结果原值返回 // (默认 @@match 非 global 臂交付 exec 结果本体,index/input/groups/ // indices 与 exec 同源;影子 @@match 可替换)。 let s_units = text.units().into_owned(); let s_val = vm.new_string_units_owned(s_units); let matcher = match rx_get_match_method(vm, re_ptr, pattern_val) { Ok(v) => v, Err(e) => return NativeResult::Err(e), }; return match vm.call_function_sync(matcher, pattern_val, &[s_val]) { Ok(r) => NativeResult::Ok(r), Err(e) => NativeResult::Err(crate::iterator::engine_error(vm, &e)), }; } // string 值物化一次(text 借用至此结束),供下方两臂共用。 let s_units = text.units().into_owned(); let s_val = vm.new_string_units_owned(s_units); // 对象模式:GetMethod(pattern, @@match) 先于构造(规范序);matcher 非 // undefined 则 Call(matcher, pattern, «string») 原值返回(自定义对象与 // 自置 @@match 的载体均走此臂)。 if pattern_val.is_object() { let matcher = match rx_get_match_method_opt(vm, pattern_val.as_js_object_ptr(), pattern_val) { Ok(m) => m, Err(e) => return NativeResult::Err(e), }; if let Some(matcher) = matcher { return match vm.call_function_sync(matcher, pattern_val, &[s_val]) { Ok(r) => NativeResult::Ok(r), Err(e) => NativeResult::Err(crate::iterator::engine_error(vm, &e)), }; } } // 非对象 / matcher undefined:Construct %RegExp%(undefined → 空模式、其余 // 经 ToString),再 GetMethod(rx, @@match) + Invoke;转换抛错(含 Symbol) // 原样传播。 let regexp_ctor = vm.session().builtin_world().regexp_constructor.as_ptr() as *mut JsObject; let rx_val = match vm.construct_ctor(JsValue::from_js_object(regexp_ctor), &[pattern_val]) { Ok(v) => v, Err(e) => return NativeResult::Err(e), }; if !rx_val.is_object() { return NativeResult::Err(crate::error::create_type_error( vm, "match pattern constructor must return an object", )); } let matcher = match rx_get_match_method(vm, rx_val.as_js_object_ptr(), rx_val) { Ok(v) => v, Err(e) => return NativeResult::Err(e), }; match vm.call_function_sync(matcher, rx_val, &[s_val]) { Ok(r) => NativeResult::Ok(r), Err(e) => NativeResult::Err(crate::iterator::engine_error(vm, &e)), } } /// GetMethod(rx, @@match):可调用返回之,null/undefined/不可调用抛 TypeError; /// 属性读抛错恢复原异常值。 fn rx_get_match_method(vm: &mut H, rx: *mut JsObject, this_val: JsValue) -> Result { let match_key = oxide_types::private_key::make_well_known_symbol_key(1); let matcher = match vm.ordinary_get(unsafe { &*rx }, match_key, this_val) { Ok(v) => v, Err(e) => return Err(crate::iterator::engine_error(vm, &e)), }; if !crate::iterator::is_callable(matcher) { return Err(crate::error::create_type_error(vm, "RegExp @@match is not callable")); } Ok(matcher) } /// GetMethod(rx, @@match) 的 Option 形态:undefined/null 返回 None(落构造 /// 路径),不可调用抛 TypeError,属性读抛错恢复原异常值。 fn rx_get_match_method_opt( vm: &mut H, rx: *mut JsObject, this_val: JsValue, ) -> Result, JsValue> { let match_key = oxide_types::private_key::make_well_known_symbol_key(1); let func = match vm.ordinary_get(unsafe { &*rx }, match_key, this_val) { Ok(v) => v, Err(e) => return Err(crate::iterator::engine_error(vm, &e)), }; if func.is_undefined() || func.is_null() { return Ok(None); } if !crate::iterator::is_callable(func) { return Err(crate::error::create_type_error(vm, "Symbol.match is not callable")); } Ok(Some(func)) } /// GetMethod(rx, @@search):undefined/null 返回 None(落构造路径),不可调用 /// 抛 TypeError,属性读抛错恢复原异常值。 fn rx_get_search_method( vm: &mut H, rx: *mut JsObject, this_val: JsValue, ) -> Result, JsValue> { let search_key = oxide_types::private_key::make_well_known_symbol_key(3); let func = match vm.ordinary_get(unsafe { &*rx }, search_key, this_val) { Ok(v) => v, Err(e) => return Err(crate::iterator::engine_error(vm, &e)), }; if func.is_undefined() || func.is_null() { return Ok(None); } if !crate::iterator::is_callable(func) { return Err(crate::error::create_type_error(vm, "Symbol.search is not callable")); } Ok(Some(func)) } /// `String.prototype.search(pattern)`:返回首个匹配位置(码元),无匹配返回 -1。 /// /// # 步骤 /// 1. string = ToString(this)(转换异常传播)。 /// 2. pattern 为对象时:GetMethod(pattern, @@search),searcher 非 undefined 则 /// Call(searcher, pattern, «string») 原值返回(真 RegExp 默认 @@search 与 /// 自定义对象均走此臂)。 /// 3. Construct %RegExp%(null/undefined 映射对应文本,其余经 ToString)→ /// GetMethod(rx, @@search) + Invoke(rx, «string»)。 /// /// # 边界与前提 /// - 无参 / null 模式走构造路径:空模式命中串头返回 0。 /// - 构造路径不预转换 pattern:构造器内部完成 ToString(异常原样传播)。 pub fn string_search(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.search called with {} args", args.len()); let pattern_val = if args.len() >= 2 { vm.reg(args[1]) } else { JsValue::undefined() }; let text = try_string!(this_text(vm, args)); // string 值物化(text 借用结束),供下方两臂共用。 let s_units = text.units().into_owned(); let s_val = vm.new_string_units_owned(s_units); // 对象模式:GetMethod(pattern, @@search) 先于构造(规范序);searcher 非 // undefined 则 Call(searcher, pattern, «string») 原值返回。 if pattern_val.is_object() { let searcher = match rx_get_search_method(vm, pattern_val.as_js_object_ptr(), pattern_val) { Ok(m) => m, Err(e) => return NativeResult::Err(e), }; if let Some(searcher) = searcher { return match vm.call_function_sync(searcher, pattern_val, &[s_val]) { Ok(r) => NativeResult::Ok(r), Err(e) => NativeResult::Err(crate::iterator::engine_error(vm, &e)), }; } } // 非对象 / searcher undefined:Construct %RegExp% + GetMethod(rx, @@search) // + Invoke(rx, «string»);转换抛错(含 Symbol)原样传播。 let regexp_ctor = vm.session().builtin_world().regexp_constructor.as_ptr() as *mut JsObject; let rx_val = match vm.construct_ctor(JsValue::from_js_object(regexp_ctor), &[pattern_val]) { Ok(v) => v, Err(e) => return NativeResult::Err(e), }; if !rx_val.is_object() { return NativeResult::Err(crate::error::create_type_error( vm, "search pattern constructor must return an object", )); } let searcher = match rx_get_search_method(vm, rx_val.as_js_object_ptr(), rx_val) { Ok(m) => m, Err(e) => return NativeResult::Err(e), }; match searcher { Some(searcher) => match vm.call_function_sync(searcher, rx_val, &[s_val]) { Ok(r) => NativeResult::Ok(r), Err(e) => NativeResult::Err(crate::iterator::engine_error(vm, &e)), }, None => NativeResult::Err(crate::error::create_type_error(vm, "RegExp @@search is not callable")), } } /// `String.prototype.isWellFormed()`:字符串无孤立 surrogate(每个码元要么是 /// 合法字符,要么与相邻码元成代理对)时返回 true。 pub fn string_is_well_formed(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.isWellFormed called with {} args", args.len()); let s = try_string!(this_units(vm, args)); let mut i = 0; while i < s.len() { let u = s[i]; if (0xDC00..=0xDFFF).contains(&u) { return NativeResult::Ok(JsValue::bool(false)); } if (0xD800..=0xDBFF).contains(&u) { if !(i + 1 < s.len() && (0xDC00..=0xDFFF).contains(&s[i + 1])) { return NativeResult::Ok(JsValue::bool(false)); } i += 2; } else { i += 1; } } NativeResult::Ok(JsValue::bool(true)) } /// `String.prototype.toWellFormed()`:把孤立 surrogate 替换为 U+FFFD 返回新字符串。 pub fn string_to_well_formed(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.toWellFormed called with {} args", args.len()); let s = try_string!(this_units(vm, args)); let mut out = Vec::with_capacity(s.len()); let mut i = 0; while i < s.len() { let u = s[i]; if (0xD800..=0xDBFF).contains(&u) { if i + 1 < s.len() && (0xDC00..=0xDFFF).contains(&s[i + 1]) { out.push(u); out.push(s[i + 1]); i += 2; continue; } out.push(0xFFFD); } else if (0xDC00..=0xDFFF).contains(&u) { out.push(0xFFFD); } else { out.push(u); } i += 1; } NativeResult::Ok(vm.new_string_units_owned(out)) } /// `String.prototype.normalize(form)`:按 NFC/NFD/NFKC/NFKD 规范化为 Unicode /// 规范形式(良形段映射,孤立 surrogate 单元原样保留)。 pub fn string_normalize(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.normalize called with {} args", args.len()); use unicode_normalization::UnicodeNormalization; // form 缺省或 undefined 按 "NFC" 处理(规范第 3 步),其余经 ToString。 let form_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; let form = if form_val.is_undefined() { "NFC".encode_utf16().collect() } else { try_string!(as_units(vm, form_val)).into_owned() }; // 规范化形式名按良形文本比对(NFC 等名无孤立 surrogate)。 let form_name = String::from_utf16_lossy(&form); // this 转换先于 form 校验(规范第 2 步先于第 4 步)。 let s = try_string!(this_units(vm, args)); // form 非四合法值抛 RangeError。 match form_name.as_str() { "NFC" | "NFD" | "NFKC" | "NFKD" => {} _ => return NativeResult::Err(crate::error::create_range_error(vm, "normalize form is not valid")), } let out = map_well_formed_segments(&s, |seg| match form_name.as_str() { "NFD" => seg.nfd().collect(), "NFKC" => seg.nfkc().collect(), "NFKD" => seg.nfkd().collect(), _ => seg.nfc().collect(), }); NativeResult::Ok(vm.new_string_units_owned(out)) } pub(crate) const MALL_INPUT: &str = "__mal_input__"; pub(crate) const MALL_RE: &str = "__mal_re__"; pub(crate) const MALL_DONE: &str = "__mal_done__"; pub(crate) const MALL_GLOBAL: &str = "__mal_global__"; pub(crate) const MALL_UNICODE: &str = "__mal_unicode__"; /// `String.prototype.matchAll(pattern)`:返回带 `next` 的迭代器,逐步产出全部匹配 /// (要求 RegExp 带 global 标志;普通字符串会被转义成等效正则)。包装器 input /// 属性存原始字符串值(单元保真),游标归匹配器 lastIndex。 /// /// # 步骤 /// 1. receiver 前置校验(RequireObjectCoercible,纯 is_* 读取)。 /// 2. pattern 为 null/undefined:跳过对象分支,直接构造路径。 /// 3. pattern 为对象:真 RegExp 先 flags 单读 g 判定;GetMethod 全链单读, /// undefined/null 落构造路径,非 callable 非空值抛 TypeError,可调用则 /// Call(matcher, pattern, « thisValue ») 原值返回。 /// 4. 非对象 pattern:跳过对象分支,按字面文本编译载体正则。 /// /// # 边界与前提 /// - ToString(thisValue) 延迟到构造路径实际消费(matcher 直调臂不触发)。 /// - 构造路径(RegExpCreate(pattern, "g") + Invoke)复用 /// `match_all_construct_invoke`,无 callable @@matchAll 时抛 TypeError。 pub fn string_match_all(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.matchAll called with {} args", args.len()); let this_val = vm.reg(args[0]); // receiver 前置校验:null/undefined/symbol 抛 TypeError(规范第 1-2 步 // RequireObjectCoercible,纯 is_* 读取无用户代码,先于一切分叉)。 if this_val.is_null() || this_val.is_undefined() { return NativeResult::Err(crate::error::create_type_error( vm, "String.prototype method called on null or undefined", )); } if this_val.is_symbol() { return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert a Symbol value to a string")); } if args.len() < 2 { builtins_error!("String.prototype.matchAll: invalid receiver"); return NativeResult::Err(JsValue::undefined()); } let pattern_val = vm.reg(args[1]); // 对象分支条件(IsObject)为假:null/undefined pattern 跳过 flags/GetMethod // 全部步骤,直接落构造路径。 if pattern_val.is_null() || pattern_val.is_undefined() { return match_all_construct_invoke(vm, this_val, pattern_val); } if pattern_val.is_object() { // 真 RegExp:flags 单读原始值(getter 副作用/抛错只观察一次)→ // RequireObjectCoercible → ToString 含 "g" 判定;g 判定先于 GetMethod。 if is_regexp_obj(pattern_val, vm) { let re_ptr = pattern_val.as_js_object_ptr(); // SAFETY: pattern_val 已校验为非空对象值。 let re_obj = unsafe { &*re_ptr }; let flags_si = vm.kernel_core().perm_interner().intern("flags").0; let flags_val = match vm.ordinary_get(re_obj, flags_si, pattern_val) { Ok(v) => v, Err(e) => return NativeResult::err(crate::iterator::engine_error(vm, &e)), }; if flags_val.is_undefined() || flags_val.is_null() { return NativeResult::Err(crate::error::create_type_error( vm, "String.prototype.matchAll: regex must have global flag", )); } let flags_sv = match oxide_runtime_api::to_string_value_full(flags_val, vm) { Ok(v) => v, Err(_) => { if let Some(exc) = vm.take_uncaught_value() { return NativeResult::Err(exc); } return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a string")); } }; if !String::from_utf16_lossy(&vm.string_units(flags_sv)).contains('g') { return NativeResult::Err(crate::error::create_type_error( vm, "String.prototype.matchAll: regex must have global flag", )); } } // GetMethod(pattern, @@matchAll) 全链单次 Get:own 命中 undefined/null // 即停(Get 语义),不回退原型链。 let match_all_key = oxide_types::private_key::make_well_known_symbol_key(7); let matcher = match vm.ordinary_get(unsafe { &*pattern_val.as_js_object_ptr() }, match_all_key, pattern_val) { Ok(v) => v, Err(e) => return NativeResult::err(crate::iterator::engine_error(vm, &e)), }; // GetMethod 返 undefined/null:落构造路径(ToString(thisValue) → // RegExpCreate(pattern, "g") → Invoke 产物 @@matchAll)。 if matcher.is_undefined() || matcher.is_null() { return match_all_construct_invoke(vm, this_val, pattern_val); } if !crate::iterator::is_callable(matcher) { return NativeResult::Err(crate::error::create_type_error( vm, "String.prototype.matchAll: matcher is not a function", )); } // Call(matcher, pattern, « thisValue »):实参为原始 this 值。 return match vm.call_function_sync(matcher, pattern_val, &[this_val]) { Ok(v) => NativeResult::Ok(v), Err(e) => NativeResult::err(crate::iterator::engine_error(vm, &e)), }; } // 非对象 pattern:对象分支整体跳过,按文本编译为 stub(非捕获组,免额外 // capture)。 { let pattern_units = try_string!(as_units(vm, pattern_val)).into_owned(); let pattern_str = String::from_utf16_lossy(&pattern_units); let escaped = regress::escape(&pattern_str); let rx_str = if escaped.is_empty() { String::from("(?:)") } else { format!("(?:{})", escaped) }; let compiled = match regress::Regex::new(&rx_str) { Ok(rx) => rx, Err(e) => { return NativeResult::Err(crate::error::create_syntax_error(vm, &format!("Invalid regex: {}", e))); } }; let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject; let mut stub = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)); // 载体专型标签:native_fn 槽的 Box 经 RegExp 同一守卫释放/深拷贝, // 避免每次 matchAll 泄漏一个已编译正则。 stub.type_tag = oxide_types::object::JsObject::OBJ_TYPE_REGEX_STUB; // 载体语义即 RegExpCreate(pattern, "g"):flags 字段物化 "g",使 // g/y/d 判定(regexp_has_flag 读实例字段)与真 RegExp 同径。 stub.set_regexp_flags(vm.new_string("g")); let boxed = Box::new(compiled); let raw = Box::into_raw(boxed) as *const u8; stub.set_native_fn(Some(unsafe { oxide_types::object::NativeFnPtr::from_raw(raw as *const ()) })); let stub_ptr = vm.alloc_object(stub); // string = ToString(thisValue):构造路径消费点,仅此臂实际转换。 let input_val = match oxide_runtime_api::to_string_value_full(this_val, vm) { Ok(v) => v, Err(_) => { if let Some(exc) = vm.take_uncaught_value() { return NativeResult::Err(exc); } return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a string")); } }; builder_wrapper(vm, input_val, JsValue::from_js_object(stub_ptr)) } } /// 构造路径(规范步骤 4-6):string = ToString(thisValue) → /// regexp = RegExpCreate(pattern, "g") → Invoke(regexp, @@matchAll, « string »)。 /// /// # 边界与前提 /// - pattern 为 null/undefined 时构造器内部映射(null → "null" 文本、 /// undefined → 空模式),不在此处重复转换 /// - 构造产物无 callable @@matchAll(含原型链删除)时 Invoke 抛 TypeError /// - 对象 toString/valueOf 抛出的原始异常原样传播 fn match_all_construct_invoke(vm: &mut H, this_val: JsValue, pattern_val: JsValue) -> NativeResult { // string = ToString(thisValue)。 let input_val = match oxide_runtime_api::to_string_value_full(this_val, vm) { Ok(v) => v, Err(_) => { if let Some(exc) = vm.take_uncaught_value() { return NativeResult::Err(exc); } return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a string")); } }; // regexp = RegExpCreate(pattern, "g")。 let g_str = vm.new_string("g"); let rx_val = match vm.construct_ctor( JsValue::from_js_object(vm.session().builtin_world().regexp_constructor.as_ptr() as *mut JsObject), &[pattern_val, g_str], ) { Ok(v) => v, Err(e) => return NativeResult::Err(e), }; if !rx_val.is_object() { return NativeResult::Err(crate::error::create_type_error( vm, "match pattern constructor must return an object", )); } // Invoke(regexp, @@matchAll, « string »):产物全链 Get,无 callable 抛 TypeError。 let match_all_key = oxide_types::private_key::make_well_known_symbol_key(7); let rx_ptr = rx_val.as_js_object_ptr(); let rx_this = JsValue::from_js_object(rx_ptr); let matcher = match vm.ordinary_get(unsafe { &*rx_ptr }, match_all_key, rx_this) { Ok(v) => v, Err(e) => return NativeResult::err(crate::iterator::engine_error(vm, &e)), }; if matcher.is_undefined() || matcher.is_null() || !crate::iterator::is_callable(matcher) { return NativeResult::Err(crate::error::create_type_error( vm, "RegExp.prototype[Symbol.matchAll] is not a function", )); } match vm.call_function_sync(matcher, rx_this, &[input_val]) { Ok(v) => NativeResult::Ok(v), Err(e) => NativeResult::err(crate::iterator::engine_error(vm, &e)), } } /// 构造 matchAll 包装对象:原型挂 `%RegExpStringIteratorPrototype%`, /// own 属性为 input/re/done/global/unicode 五槽(内部槽物化形态), /// next 由原型提供。 /// /// # 边界与前提 /// - 字符串 pattern 臂调用方:global 恒真(载体语义 = RegExpCreate(pattern, "g"))、 /// unicode 恒假(无 u/v 标志);游标归匹配器 lastIndex,包装器不存游标。 fn builder_wrapper(vm: &mut H, input_val: JsValue, re_obj: JsValue) -> NativeResult { // matchAll 迭代器挂 %RegExpStringIteratorPrototype%(链到 %IteratorPrototype%), // next 由原型提供(不挂实例 own)。 let regexp_iter_proto = vm.session().builtin_world().regexp_string_iterator_proto.as_ptr() as *mut JsObject; let wrapper = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(regexp_iter_proto))); let wrapper_obj = unsafe { &mut *wrapper }; let input_si = vm.kernel_core().perm_interner().intern(MALL_INPUT).0; let re_si = vm.kernel_core().perm_interner().intern(MALL_RE).0; let done_si = vm.kernel_core().perm_interner().intern(MALL_DONE).0; let global_si = vm.kernel_core().perm_interner().intern(MALL_GLOBAL).0; let unicode_si = vm.kernel_core().perm_interner().intern(MALL_UNICODE).0; vm.set_or_create_prop_value(wrapper_obj, input_si, input_val); vm.set_or_create_prop_value(wrapper_obj, re_si, re_obj); vm.set_or_create_prop_value(wrapper_obj, done_si, JsValue::bool(false)); vm.set_or_create_prop_value(wrapper_obj, global_si, JsValue::bool(true)); vm.set_or_create_prop_value(wrapper_obj, unicode_si, JsValue::bool(false)); NativeResult::Ok(JsValue::from_js_object(wrapper)) } /// `String.prototype[Symbol.iterator]()`:返回按码元迭代字符的迭代器。 /// /// # 步骤 /// 1. null/undefined 抛 TypeError(RequireObjectCoercible) /// 2. this 经 ToString 完整转换(对象取 toString 结果,Symbol 抛 TypeError) /// 3. 包成统一迭代器包装(next 逐码元产出,耗尽后 done) /// /// # 边界与前提 /// - 对象 toString 抛出的原始异常原样传播 /// - 包装器挂 `%StringIteratorPrototype%` → `%IteratorPrototype%` 链 pub fn string_symbol_iterator(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if this_val.is_null() || this_val.is_undefined() { return NativeResult::Err(crate::error::create_type_error( vm, "String.prototype[Symbol.iterator] called on null or undefined", )); } let s_val = match oxide_runtime_api::to_string_value_full(this_val, vm) { Ok(v) => v, Err(_) => { // ToString 触发对象 toString/valueOf 抛出的原生异常须原样传播。 if let Some(exc) = vm.take_uncaught_value() { return NativeResult::Err(exc); } return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a string")); } }; // 直构 String 迭代器:内层即 ToString 结果,不再回读 @@iterator。本函数语义 // 只由 this 决定,属性表被删除/置 null/被覆盖均不影响已保存引用的直调。 // 迭代器挂 %StringIteratorPrototype%(链到 %IteratorPrototype%);串内层不暴露 return。 let string_iter_proto = vm.session().builtin_world().string_iterator_proto.as_ptr() as *mut JsObject; NativeResult::Ok(crate::iterator::build_iterator_wrapper(vm, s_val, None, true, Some(string_iter_proto))) } /// `matchAll` 迭代器的 `next`:返回 `{value: 匹配, done: false}`,耗尽后 /// done 为 true。 /// /// # 步骤 /// 1. this 非对象抛 TypeError;五内部槽缺一即抛 TypeError(RequireInternalSlot)。 /// 2. `[[Done]]` 为 true → done 结果。 /// 3. `match = RegExpExec(regexp, string)`:`exec` 属性可调用 → /// Call(exec, regexp, «string»),结果非对象非 null 抛 TypeError;不可调用 → /// regexp 持编译正则时落内置搜索核(RegExpBuiltinExec 口径),否则 /// TypeError。 /// 4. match 为 null → 写 `[[Done]]` = true,done 结果。 /// 5. `[[Global]]` 为 false → 写 `[[Done]]` = true,返回单匹配(非 global /// 匹配器只产一枚)。 /// 6. 空匹配(ToString(match["0"]) 为空串)→ lastIndex 读-推进-写: /// thisIndex = ToLength(Get(regexp, "lastIndex")),nextIndex = /// AdvanceStringIndex(u/v 口径跨代理对 2 单元),Set(regexp, "lastIndex")。 /// 7. 返回 `{value: match, done: false}`。 /// /// # 边界与前提 /// - 各读/调/写(exec getter 与调用、match."0" 读与 ToString、lastIndex /// Get/Set/ToLength)的原异常原值传播。 /// /// # 副作用 /// - 写包装器 `[[Done]]` 槽;空匹配臂写匹配器 lastIndex。 pub fn string_match_all_next(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if !this_val.is_object() { return NativeResult::Err(crate::error::create_type_error(vm, "matchAll next called on non-object")); } let wrapper = unsafe { &mut *this_val.as_js_object_ptr() }; let input_si = vm.kernel_core().perm_interner().intern(MALL_INPUT).0; let re_si = vm.kernel_core().perm_interner().intern(MALL_RE).0; let done_si = vm.kernel_core().perm_interner().intern(MALL_DONE).0; let global_si = vm.kernel_core().perm_interner().intern(MALL_GLOBAL).0; let unicode_si = vm.kernel_core().perm_interner().intern(MALL_UNICODE).0; // 内部槽守卫:五槽同批写入,查一槽即全查;缺失即 TypeError。 if vm.get_own_property_slot(wrapper, done_si).is_none() { return NativeResult::Err(crate::error::create_type_error( vm, "matchAll next called on object without internal slots", )); } // [[Done]] 为 true → done 结果。 let done_val = match vm.ordinary_get(wrapper, done_si, this_val) { Ok(v) => v, Err(_) => return NativeResult::Err(crate::iterator::engine_error(vm, "cannot read done slot")), }; if oxide_runtime_api::to_boolean(done_val) { return make_match_done_result(vm, JsValue::undefined()); } // 读余下四槽:input / re / global / unicode。 let input_val = match vm.ordinary_get(wrapper, input_si, this_val) { Ok(v) => v, Err(_) => return NativeResult::Err(crate::iterator::engine_error(vm, "cannot read input slot")), }; let re_val = match vm.ordinary_get(wrapper, re_si, this_val) { Ok(v) if v.is_object() => v, _ => { return NativeResult::Err(crate::error::create_type_error(vm, "matchAll next called on non-object regexp")) } }; let is_global = match vm.ordinary_get(wrapper, global_si, this_val) { Ok(v) => oxide_runtime_api::to_boolean(v), Err(_) => return NativeResult::Err(crate::iterator::engine_error(vm, "cannot read global slot")), }; let is_unicode = match vm.ordinary_get(wrapper, unicode_si, this_val) { Ok(v) => oxide_runtime_api::to_boolean(v), Err(_) => return NativeResult::Err(crate::iterator::engine_error(vm, "cannot read unicode slot")), }; let re_ptr = re_val.as_js_object_ptr(); let re_obj = unsafe { &*re_ptr }; // ── RegExpExec(R, S) ── // Get(R, "exec"):getter 抛错原值传播。 let exec_val = match crate::regexp::rx_get_prop(vm, re_ptr, "exec", re_val) { Ok(v) => v, Err(err) => return NativeResult::Err(err), }; let match_val = if crate::iterator::is_callable(exec_val) { // Call(exec, R, «S»):调用抛错原值传播;结果非对象非 null 抛 TypeError。 let result = match vm.call_function_sync(exec_val, re_val, &[input_val]) { Ok(r) => r, Err(_) => return NativeResult::Err(crate::iterator::engine_error(vm, "RegExp exec call failed")), }; if !(result.is_null() || result.is_object()) { return NativeResult::Err(crate::error::create_type_error( vm, "RegExp exec result must be an object or null", )); } result } else { // 不可调用回落:仅 regexp 持编译正则(真 RegExp 或字符串 pattern 载体) // 可走内置搜索核,否则 RequireInternalSlot 失败。 let fn_ptr = match re_obj.native_fn() { Some(p) => p, None => return NativeResult::Err(crate::error::create_type_error(vm, "RegExp exec is not callable")), }; // SAFETY: fn_ptr 持有 regexp_constructor 存放的 `Box` 指针。 let regex = unsafe { &*(fn_ptr.as_ptr() as *const regress::Regex) }; let text = MatchText::from_value(input_val); // g/y 标志决定 lastIndex 追踪(与 exec/test 同口径)。 let tracks = crate::regexp::regexp_has_flag(vm, re_obj, 'g') || crate::regexp::regexp_has_flag(vm, re_obj, 'y'); let sticky = crate::regexp::regexp_has_flag(vm, re_obj, 'y'); let m = match crate::regexp::rx_search(vm, re_ptr, re_val, regex, &text, tracks, sticky) { Ok(opt) => opt, Err(err) => return NativeResult::Err(err), }; match m { None => JsValue::null(), Some(m) => { // d 标志门控 indices 产出(与 exec 同口径)。 let has_indices = crate::regexp::regexp_has_flag(vm, re_obj, 'd'); let parts = match_parts(vm, &text, &m); make_match_result_array(vm, parts, m.range().start as i32, input_val, Some(&m), &text, has_indices) } } }; // match 为 null → 写 [[Done]] = true,done 结果。 if match_val.is_null() { vm.set_or_create_prop_value(wrapper, done_si, JsValue::bool(true)); return make_match_done_result(vm, JsValue::undefined()); } // [[Global]] 为 false → 写 [[Done]] = true,返回单匹配。 if !is_global { vm.set_or_create_prop_value(wrapper, done_si, JsValue::bool(true)); return make_match_done_result(vm, match_val); } // matchString = ToString(Get(match, "0")):下标经整数键换算(数组元素区 // 按整数键存储),getter 与 ToString 抛错原值传播。 let m0 = match crate::regexp::rx_get_index(vm, match_val.as_js_object_ptr(), 0, match_val) { Ok(v) => v, Err(err) => return NativeResult::Err(err), }; let m0_str = match oxide_runtime_api::to_string_value_full(m0, vm) { Ok(v) => v, Err(_) => { if let Some(exc) = vm.take_uncaught_value() { return NativeResult::Err(exc); } return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a string")); } }; // 空匹配 → lastIndex 读-推进-写(内置臂 lastIndex 为 rx_search 写入的 // 匹配末尾,自定义 exec 臂为用户代码已设值)。 if vm.string_units(m0_str).is_empty() { let li_val = match crate::regexp::rx_get_prop(vm, re_ptr, "lastIndex", re_val) { Ok(v) => v, Err(err) => return NativeResult::Err(err), }; let this_index = match crate::regexp::to_length_value(vm, li_val) { Ok(n) => n, Err(err) => return NativeResult::Err(err), }; let units = vm.string_units(input_val); let next_index = crate::regexp::advance_string_index(&units, this_index, is_unicode); if let Err(err) = crate::regexp::rx_set_prop(vm, re_ptr, "lastIndex", JsValue::int(next_index as i32), re_val) { return NativeResult::Err(err); } } make_match_done_result(vm, match_val) } /// 由匹配结果构建结果数组元素列表:完整匹配字符串值在前,其后逐捕获组 /// (未参与匹配为 undefined),片段经 MatchText 臂的单元口径物化。 fn match_parts(vm: &mut H, text: &MatchText, m: ®ress::Match) -> Vec { let range = m.range(); let mut parts = Vec::with_capacity(m.captures.len() + 1); parts.push(vm.new_string_units_owned(text.slice(range.start, range.end).into_owned())); for i in 1..=m.captures.len() { match m.group(i) { Some(g) => parts.push(vm.new_string_units_owned(text.slice(g.start, g.end).into_owned())), None => parts.push(JsValue::undefined()), } } parts } /// 构建 matchAll 结果数组:元素为匹配字符串值(未匹配捕获组为 undefined), /// 附带 index/input/groups 属性(d 标志下另挂 indices,与 exec 结果同面)。 fn make_match_result_array( vm: &mut H, parts: Vec, match_index: i32, input_val: JsValue, m: Option<®ress::Match>, text: &MatchText, has_indices: bool, ) -> JsValue { let arr = make_string_array_values(vm, parts); let arr_ptr = arr.as_js_object_ptr(); let arr_obj = unsafe { &mut *arr_ptr }; let index_si = vm.kernel_core().perm_interner().intern("index").0; 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; 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; vm.set_or_create_prop_value(arr_obj, groups_si, groups_val); // d 标志:挂 indices 属性(与 exec 结果同口径的码元对数组族)。 if has_indices { if let Some(m) = m { let indices_val = crate::regexp::build_indices_array(vm, m, text); let indices_si = vm.kernel_core().perm_interner().intern("indices").0; vm.set_or_create_prop_value(arr_obj, indices_si, indices_val); } } arr } /// 构造迭代器结果对象 `{value, done}`(done 仅在 value 为 undefined 时为 true)。 fn make_match_done_result(vm: &mut H, value: JsValue) -> NativeResult { let done = value.is_undefined(); let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject; let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto))); let value_si = vm.kernel_core().perm_interner().intern("value").0; let done_si = vm.kernel_core().perm_interner().intern("done").0; let obj_ref = unsafe { &mut *obj }; vm.set_or_create_prop_value(obj_ref, value_si, value); vm.set_or_create_prop_value(obj_ref, done_si, JsValue::bool(done)); NativeResult::Ok(JsValue::from_js_object(obj)) } /// `String.prototype.replaceAll(pattern, replacement)`:替换全部匹配 /// (RegExp 或字符串模式)。 pub fn string_replace_all(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.replaceAll called with {} args", args.len()); string_replace_impl(vm, args, true) } /// `String.raw(template, ...substitutions)`:从模板对象的 `raw` 数组元素与 /// substitutions 交错拼接,返回原始字符串(单元口径,lone surrogate 保真)。 /// /// # 步骤 /// 1. 取 template 的 `raw` 属性(必须为对象,否则抛 TypeError) /// 2. 取 raw 的 `length` 属性(ToLength) /// 3. 逐下标读 raw[i] 拼接;仅在段间(i + 1 < length)追加 substitutions[i] /// /// # 边界与前提 /// - raw[i] 缺失时视为 undefined(ToString 为 "undefined") /// - substitutions 缺失时段间追加空串,末段后不追加任何内容 pub fn string_raw(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.raw called with {} args", args.len()); // Step 1: 取 template 参数(args[1] 为 this 区域后的首个实参)。 if args.len() < 2 { return NativeResult::Err(crate::error::create_type_error(vm, "String.raw: template is required")); } let template_val = vm.reg(args[1]); if !template_val.is_object() || template_val.as_js_object_ptr().is_null() { return NativeResult::Err(crate::error::create_type_error(vm, "String.raw: template must be an object")); } let template_ptr = template_val.as_js_object_ptr(); // Step 2: 取 template.raw 属性。 let raw_si = vm.kernel_core().perm_interner().intern("raw").0; let template_obj = unsafe { &*template_ptr }; let raw_val = match vm.ordinary_get(template_obj, raw_si, template_val) { Ok(v) => v, Err(e) => { let exc = vm .take_uncaught_value() .unwrap_or_else(|| crate::error::create_type_error(vm, &e)); return NativeResult::Err(exc); } }; if !raw_val.is_object() || raw_val.as_js_object_ptr().is_null() { return NativeResult::Err(crate::error::create_type_error(vm, "String.raw: template.raw must be an object")); } let raw_ptr = raw_val.as_js_object_ptr(); // Step 3: 取 raw.length(ToLength,Symbol 抛 TypeError)。 let length_si = vm.kernel_core().perm_interner().intern("length").0; let raw_obj = unsafe { &*raw_ptr }; let raw_len = match vm.ordinary_get(raw_obj, length_si, raw_val) { Ok(v) => { // ToLength:经 to_number_full(Symbol 抛 TypeError / 对象 ToPrimitive 异常 // 原样传播),再按 ToLength 归一并截断(防超大 raw 数组导致 OOM)。 match oxide_runtime_api::to_number_full(v, vm) { Ok(n) => { let len = if n.is_nan() || n <= 0.0 { 0.0 } else { n.min(9_007_199_254_740_991.0) }; (len.trunc() as u64).min(0x1FFFFF) as usize } Err(msg) => { let exc = vm .take_uncaught_value() .unwrap_or_else(|| crate::error::create_type_error(vm, &msg)); return NativeResult::Err(exc); } } } Err(e) => { let exc = vm .take_uncaught_value() .unwrap_or_else(|| crate::error::create_type_error(vm, &e)); return NativeResult::Err(exc); } }; // Step 4: 逐下标拼接 raw[i] + substitutions[i]。 let mut result: Vec = Vec::new(); let raw_obj_ref = unsafe { &*raw_ptr }; for i in 0..raw_len { // 读 raw[i]:用 property_key_si 把整数 i 映射为整数键 si, // 再经 ordinary_get 沿原型链查找(触发 getter)。 let index_key = vm.property_key_si(JsValue::int(i as i32)); let raw_elem = vm.ordinary_get(raw_obj_ref, index_key, raw_val); let raw_units = match raw_elem { // ToString 完整路径:对象经 ToPrimitive(异常原样传播),Symbol 抛 TypeError。 Ok(v) => match oxide_runtime_api::to_string_value_full(v, vm) { Ok(sv) => vm.string_units(sv).into_owned(), Err(msg) => { let exc = vm .take_uncaught_value() .unwrap_or_else(|| crate::error::create_type_error(vm, &msg)); return NativeResult::Err(exc); } }, Err(_) => { if let Some(exc) = vm.take_uncaught_value() { return NativeResult::Err(exc); } "undefined".encode_utf16().collect() } }; result.extend(raw_units); // 段间(i + 1 < length)才追加 substitutions[i];substitutions 缺失时 // 追加空串,末段后不再追加任何内容。 if i + 1 < raw_len { let sub_idx = i + 2; if sub_idx < args.len() { let sub_val = vm.reg(args[sub_idx]); // ToString 完整路径:对象 ToPrimitive 异常原样传播,Symbol 抛 TypeError。 let sub_units = match oxide_runtime_api::to_string_value_full(sub_val, vm) { Ok(sv) => vm.string_units(sv).into_owned(), Err(msg) => { let exc = vm .take_uncaught_value() .unwrap_or_else(|| crate::error::create_type_error(vm, &msg)); return NativeResult::Err(exc); } }; result.extend(sub_units); } } } NativeResult::Ok(vm.new_string_units_owned(result)) }