From 3c691ef4d62f935389ba8efcd497d817d4bc149e Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Thu, 17 Sep 2026 10:41:26 +0000 Subject: [PATCH] =?UTF-8?q?refactor:=20string=20=E5=85=B1=E4=BA=AB?= =?UTF-8?q?=E5=B1=82=E6=8B=86=E5=87=BA=20common=20=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/string/common.rs | 381 +++++++++++++++++++++ crates/oxide_builtins/src/string/mod.rs | 375 +------------------- 2 files changed, 384 insertions(+), 372 deletions(-) create mode 100644 crates/oxide_builtins/src/string/common.rs diff --git a/crates/oxide_builtins/src/string/common.rs b/crates/oxide_builtins/src/string/common.rs new file mode 100644 index 00000000..f4f3a0db --- /dev/null +++ b/crates/oxide_builtins/src/string/common.rs @@ -0,0 +1,381 @@ +use std::borrow::Cow; + +use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; +use oxide_types::object::JsObject; +use oxide_types::value::JsValue; + +use oxide_runtime_api::VmHost; + +macro_rules! try_string { + ($e:expr) => { + match $e { + Ok(v) => v, + Err(e) => return NativeResult::Err(e), + } + }; +} +pub(crate) use try_string; + +// ── 文本通道 ───────────────────────────────────────────────────────────── +// +// 字符串值的载荷分形态:Flat(良形 UTF-8)、FlatU16(单元载荷,可含孤立 +// surrogate)、Cons(rope)。正则家族按文本通道分臂——Str 臂走良形文本 +// (字节口径,与旧代码路径一致),Units 臂走单元序列(regress utf16 接口, +// 匹配范围即码元);其余方法统一按单元序列处理(规格口径)。 + +/// 正则家族的匹配文本通道。 +pub(crate) enum MatchText<'a> { + Str(&'a str), + Units(Cow<'a, [u16]>), +} + +impl<'a> MatchText<'a> { + /// 从字符串值按载荷形态借用:Flat → Str 零拷贝,其余 → Units + /// (FlatU16 直接借用、Cons 经扁平化缓存)。 + fn from_value(val: JsValue) -> Self { + debug_assert!(val.is_string()); + // SAFETY: val 为字符串值,借用期存活由调用方借用纪律保证。 + let sp = unsafe { &*val.as_string_ptr() }; + if sp.is_flat() { + MatchText::Str(sp.as_str()) + } else { + MatchText::Units(sp.units()) + } + } + + /// 码元数。 + pub(crate) fn len_units(&self) -> usize { + match self { + MatchText::Str(s) => { + if s.is_ascii() { + s.len() + } else { + s.encode_utf16().count() + } + } + MatchText::Units(u) => u.len(), + } + } + + /// 臂原生长度(Str 臂字节、Units 臂码元)——`slice` 的端点口径。 + pub(crate) fn raw_len(&self) -> usize { + match self { + MatchText::Str(s) => s.len(), + MatchText::Units(u) => u.len(), + } + } + + /// 借出单元序列(Str 臂一次性编码为 owned;Units 臂借用/浅拷)。 + pub(crate) fn units(&self) -> Cow<'a, [u16]> { + match self { + MatchText::Str(s) => { + if s.is_ascii() { + Cow::Owned(s.as_bytes().iter().map(|&b| b as u16).collect()) + } else { + Cow::Owned(s.encode_utf16().collect()) + } + } + MatchText::Units(u) => u.clone(), + } + } + + /// 从码元位置开始的首个匹配(Str 臂内部换算为字节位置)。 + pub(crate) fn find_from_units(&self, regex: ®ress::Regex, start_units: usize) -> Option { + match self { + MatchText::Str(s) => regex.find_from(s, unit_to_byte(s, start_units)).next(), + MatchText::Units(u) => regex.find_from_utf16(u, start_units).next(), + } + } + + /// 按序消费全部非重叠匹配(自码元 0 起)。 + pub(crate) fn for_each_match(&self, regex: ®ress::Regex, mut f: impl FnMut(®ress::Match)) { + match self { + MatchText::Str(s) => { + for m in regex.find_iter(s) { + f(&m); + } + } + MatchText::Units(u) => { + for m in regex.find_from_utf16(u, 0) { + f(&m); + } + } + } + } + + /// 匹配片段为单元序列(start/end 取臂自身匹配范围口径:Str 臂字节、 + /// Units 臂码元——臂由文本通道钉死,调用方直接传 `m.range()`)。 + pub(crate) fn slice(&self, start: usize, end: usize) -> Cow<'a, [u16]> { + match self { + MatchText::Str(s) => { + let sub = &s[start..end]; + Cow::Owned(if sub.is_ascii() { + sub.as_bytes().iter().map(|&b| b as u16).collect() + } else { + sub.encode_utf16().collect() + }) + } + MatchText::Units(u) => Cow::Owned(u[start..end].to_vec()), + } + } + + /// 匹配范围端点换算到码元口径(Str 臂范围是字节口径,Units 臂即码元)。 + pub(crate) fn unit_pos(&self, pos: usize) -> usize { + match self { + MatchText::Str(s) => byte_to_unit(s, pos), + MatchText::Units(_) => pos, + } + } +} + +/// 良形文本的前置转换缓存:owned 匹配文本(regexp 模块的 haystack 载体)。 +pub(crate) enum OwnedText { + Str(String), + Units(Vec), +} + +impl OwnedText { + pub(crate) fn as_match_text(&self) -> MatchText<'_> { + match self { + OwnedText::Str(s) => MatchText::Str(s), + OwnedText::Units(u) => MatchText::Units(Cow::Borrowed(u)), + } + } + + /// 物化为字符串值(Str 臂文本 / Units 臂单元,载荷形态各自最小)。 + pub(crate) fn to_value(&self, vm: &mut H) -> JsValue { + match self { + OwnedText::Str(s) => vm.new_string(s), + OwnedText::Units(u) => vm.new_string_units_owned(u.clone()), + } + } +} + +// ── 单元基础工具 ───────────────────────────────────────────────────────── + +/// 单元序列首个匹配位置(自 from 起,返回 `hay` 内的绝对下标);needle 空 +/// 由调用方先行处理。 +pub(crate) fn find_units(hay: &[u16], needle: &[u16], from: usize) -> Option { + if from >= hay.len() { + return None; + } + let hay = &hay[from..]; + if needle.len() == 1 { + return hay.iter().position(|&u| u == needle[0]).map(|i| from + i); + } + hay.windows(needle.len()).position(|w| w == needle).map(|i| from + i) +} + +/// 单元序列末个匹配位置(限 end 前缀内起始)。 +pub(crate) fn rfind_units(hay: &[u16], needle: &[u16], end: usize) -> Option { + let hay = &hay[..end]; + if needle.len() == 1 { + return hay.iter().rposition(|&u| u == needle[0]); + } + hay.windows(needle.len()).rposition(|w| w == needle) +} + +/// 自 start 起的最长良形段终点(代理对整体归段,孤立 surrogate 截断段)。 +fn well_formed_segment_end(units: &[u16], start: usize) -> usize { + let mut i = start; + while i < units.len() { + let u = units[i]; + if (0xD800..=0xDBFF).contains(&u) { + if i + 1 < units.len() && (0xDC00..=0xDFFF).contains(&units[i + 1]) { + i += 2; + } else { + break; + } + } else if (0xDC00..=0xDFFF).contains(&u) { + break; + } else { + i += 1; + } + } + i +} + +/// 码点数(代理对计 1,孤立 surrogate 计 1)——pad 族的目标长度口径。 +pub(crate) fn code_point_count(units: &[u16]) -> usize { + let mut n = 0; + let mut i = 0; + while i < units.len() { + if (0xD800..=0xDBFF).contains(&units[i]) && i + 1 < units.len() && (0xDC00..=0xDFFF).contains(&units[i + 1]) { + i += 2; + } else { + i += 1; + } + n += 1; + } + n +} + +/// 取序列头 count 个码点对应的单元前缀。 +pub(crate) fn take_code_points(units: &[u16], count: usize) -> &[u16] { + let mut n = 0; + let mut i = 0; + while i < units.len() && n < count { + if (0xD800..=0xDBFF).contains(&units[i]) && i + 1 < units.len() && (0xDC00..=0xDFFF).contains(&units[i + 1]) { + i += 2; + } else { + i += 1; + } + n += 1; + } + &units[..i] +} + +/// ES 白空格判定的单元口径:Rust 空白表与 White_Space+LineTerminator 等价; +/// 孤立 surrogate 非白空格。 +pub(crate) fn is_trim_unit(u: u16) -> bool { + if u < 0x80 { + return matches!(u, 0x09..=0x0D | 0x20); + } + if let Some(c) = char::from_u32(u as u32) { + return c.is_whitespace(); + } + false +} + +/// 良形文本的字节偏移 → 码元数(Str 臂匹配范围/index 的码元换算入口)。 +pub(crate) fn byte_to_unit(s: &str, byte: usize) -> usize { + if s.is_ascii() { + byte + } else { + s[..byte].encode_utf16().count() + } +} + +/// 良形文本的码元位置 → 字节偏移(位置须落在字符边界:匹配游标恒对齐)。 +pub(crate) fn unit_to_byte(s: &str, unit: usize) -> usize { + if s.is_ascii() { + return unit.min(s.len()); + } + let mut acc = 0usize; + for (byte, ch) in s.char_indices() { + if unit == acc { + return byte; + } + acc += ch.len_utf16(); + } + s.len() +} + +/// 大小写/规范化按"良形段"分段映射:整段经 UTF-8 往返调用映射函数,孤立 +/// surrogate 单元原样透传(映射对码点定义,对孤立 surrogate 无定义)。 +pub(crate) fn map_well_formed_segments(units: &[u16], mut f: impl FnMut(&str) -> String) -> Vec { + let mut out = Vec::with_capacity(units.len()); + let mut i = 0; + while i < units.len() { + let seg_start = i; + i = well_formed_segment_end(units, i); + if i == seg_start { + out.push(units[i]); + i += 1; + continue; + } + let s = String::from_utf16(&units[seg_start..i]).expect("良形段 from_utf16 必成功"); + out.extend(f(&s).encode_utf16()); + } + out +} + +// ── this / 参数转换 ───────────────────────────────────────────────────── + +/// 取 this 的单元序列供只读扫描:原始字符串按载荷形态借用(Flat 惰性编码、 +/// FlatU16 直接借用、Cons 经扁平化缓存),对象经完整 ToString。 +/// null/undefined/symbol 抛 TypeError;对象 ToString 抛出的原生异常原样传播。 +/// +/// # 注意事项 +/// 返回借用绑定本次 `&mut` 借用,存活期内禁止任何 `&mut` 调用;需与参数并存 +/// 时调用方先 `into_owned` 落地。 +pub(crate) fn this_units<'a, H: VmHost>(vm: &'a mut H, args: &[u8]) -> Result, JsValue> { + let this_val = vm.reg(args[0]); + if this_val.is_null() || this_val.is_undefined() { + return Err(crate::error::create_type_error(vm, "String.prototype method called on null or undefined")); + } + if this_val.is_symbol() { + return Err(crate::error::create_type_error(vm, "Cannot convert a Symbol value to a string")); + } + if this_val.is_string() { + return Ok(vm.string_units(this_val)); + } + match oxide_runtime_api::to_string_value_full(this_val, vm) { + Ok(v) => Ok(vm.string_units(v)), + Err(_) => { + // ToString 触发对象 toString/valueOf 抛出的原生异常须原样传播, + // 否则会被展平为普通 Error 丢失原始异常对象。 + if let Some(exc) = vm.take_uncaught_value() { + return Err(exc); + } + Err(crate::error::create_type_error(vm, "Cannot convert this value to a string")) + } + } +} + +/// 取 this 的匹配文本通道:原始字符串按载荷形态零拷贝借用(Flat → Str、其余 +/// → Units),对象经完整 ToString(结果为字符串值,同形态借用)。 +/// null/undefined/symbol 抛 TypeError;对象 ToString 抛出的原生异常原样传播。 +pub(crate) fn this_text<'a, H: VmHost>(vm: &'a mut H, args: &[u8]) -> Result, JsValue> { + let this_val = vm.reg(args[0]); + if this_val.is_null() || this_val.is_undefined() { + return Err(crate::error::create_type_error(vm, "String.prototype method called on null or undefined")); + } + if this_val.is_symbol() { + return Err(crate::error::create_type_error(vm, "Cannot convert a Symbol value to a string")); + } + if this_val.is_string() { + return Ok(MatchText::from_value(this_val)); + } + match oxide_runtime_api::to_string_value_full(this_val, vm) { + Ok(v) => Ok(MatchText::from_value(v)), + Err(_) => { + if let Some(exc) = vm.take_uncaught_value() { + return Err(exc); + } + Err(crate::error::create_type_error(vm, "Cannot convert this value to a string")) + } + } +} + +/// 取参数字符串的单元序列:原始字符串按载荷形态借用,其余经完整 ToString。 +/// Symbol 与其余转换失败按规范抛 TypeError;对象 ToString 抛出的原生异常原样传播。 +pub(crate) fn as_units<'a, H: VmHost>(vm: &'a mut H, val: JsValue) -> Result, JsValue> { + if val.is_string() { + return Ok(vm.string_units(val)); + } + match oxide_runtime_api::to_string_value_full(val, vm) { + Ok(v) => Ok(vm.string_units(v)), + Err(_) => { + 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")) + } + } +} + +// ── 数组产出 ───────────────────────────────────────────────────────────── + +/// 以已构造的字符串值构建字符串数组(元素零拷贝落地),供逐单元产出路径 +/// (空分隔 split)复用,跳过 `Vec` 中间层。 +pub(crate) fn make_string_array_values(vm: &mut H, parts: Vec) -> JsValue { + let proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject; + let n = parts.len(); + let arr = + vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(proto), n, vm.epoch().bump())); + unsafe { + for (i, sv) in parts.into_iter().enumerate() { + (*arr).set_prop_at(i, sv); + } + (*arr).set_prop_count(n); + } + JsValue::from_js_object(arr) +} + +/// 以单元序列集合构建字符串数组(元素按智能路由物化为字符串值),供 +/// split/match 的片段产出路径复用。 +pub(crate) fn make_units_array(vm: &mut H, parts: Vec>) -> JsValue { + let values: Vec = parts.into_iter().map(|u| vm.new_string_units_owned(u)).collect(); + make_string_array_values(vm, values) +} diff --git a/crates/oxide_builtins/src/string/mod.rs b/crates/oxide_builtins/src/string/mod.rs index 455dcc81..62c842bb 100644 --- a/crates/oxide_builtins/src/string/mod.rs +++ b/crates/oxide_builtins/src/string/mod.rs @@ -9,378 +9,9 @@ use oxide_runtime_api::{NativeResult, VmHost}; use crate::builtins_debug; use crate::builtins_error; -macro_rules! try_string { - ($e:expr) => { - match $e { - Ok(v) => v, - Err(e) => return NativeResult::Err(e), - } - }; -} - -// ── 文本通道 ───────────────────────────────────────────────────────────── -// -// 字符串值的载荷分形态:Flat(良形 UTF-8)、FlatU16(单元载荷,可含孤立 -// surrogate)、Cons(rope)。正则家族按文本通道分臂——Str 臂走良形文本 -// (字节口径,与旧代码路径一致),Units 臂走单元序列(regress utf16 接口, -// 匹配范围即码元);其余方法统一按单元序列处理(规格口径)。 - -/// 正则家族的匹配文本通道。 -pub(crate) enum MatchText<'a> { - Str(&'a str), - Units(Cow<'a, [u16]>), -} - -impl<'a> MatchText<'a> { - /// 从字符串值按载荷形态借用:Flat → Str 零拷贝,其余 → Units - /// (FlatU16 直接借用、Cons 经扁平化缓存)。 - fn from_value(val: JsValue) -> Self { - debug_assert!(val.is_string()); - // SAFETY: val 为字符串值,借用期存活由调用方借用纪律保证。 - let sp = unsafe { &*val.as_string_ptr() }; - if sp.is_flat() { - MatchText::Str(sp.as_str()) - } else { - MatchText::Units(sp.units()) - } - } - - /// 码元数。 - pub(crate) fn len_units(&self) -> usize { - match self { - MatchText::Str(s) => { - if s.is_ascii() { - s.len() - } else { - s.encode_utf16().count() - } - } - MatchText::Units(u) => u.len(), - } - } - - /// 臂原生长度(Str 臂字节、Units 臂码元)——`slice` 的端点口径。 - pub(crate) fn raw_len(&self) -> usize { - match self { - MatchText::Str(s) => s.len(), - MatchText::Units(u) => u.len(), - } - } - - /// 借出单元序列(Str 臂一次性编码为 owned;Units 臂借用/浅拷)。 - pub(crate) fn units(&self) -> Cow<'a, [u16]> { - match self { - MatchText::Str(s) => { - if s.is_ascii() { - Cow::Owned(s.as_bytes().iter().map(|&b| b as u16).collect()) - } else { - Cow::Owned(s.encode_utf16().collect()) - } - } - MatchText::Units(u) => u.clone(), - } - } - - /// 从码元位置开始的首个匹配(Str 臂内部换算为字节位置)。 - pub(crate) fn find_from_units(&self, regex: ®ress::Regex, start_units: usize) -> Option { - match self { - MatchText::Str(s) => regex.find_from(s, unit_to_byte(s, start_units)).next(), - MatchText::Units(u) => regex.find_from_utf16(u, start_units).next(), - } - } - - /// 按序消费全部非重叠匹配(自码元 0 起)。 - pub(crate) fn for_each_match(&self, regex: ®ress::Regex, mut f: impl FnMut(®ress::Match)) { - match self { - MatchText::Str(s) => { - for m in regex.find_iter(s) { - f(&m); - } - } - MatchText::Units(u) => { - for m in regex.find_from_utf16(u, 0) { - f(&m); - } - } - } - } - - /// 匹配片段为单元序列(start/end 取臂自身匹配范围口径:Str 臂字节、 - /// Units 臂码元——臂由文本通道钉死,调用方直接传 `m.range()`)。 - pub(crate) fn slice(&self, start: usize, end: usize) -> Cow<'a, [u16]> { - match self { - MatchText::Str(s) => { - let sub = &s[start..end]; - Cow::Owned(if sub.is_ascii() { - sub.as_bytes().iter().map(|&b| b as u16).collect() - } else { - sub.encode_utf16().collect() - }) - } - MatchText::Units(u) => Cow::Owned(u[start..end].to_vec()), - } - } - - /// 匹配范围端点换算到码元口径(Str 臂范围是字节口径,Units 臂即码元)。 - pub(crate) fn unit_pos(&self, pos: usize) -> usize { - match self { - MatchText::Str(s) => byte_to_unit(s, pos), - MatchText::Units(_) => pos, - } - } -} - -/// 良形文本的前置转换缓存:owned 匹配文本(regexp 模块的 haystack 载体)。 -pub(crate) enum OwnedText { - Str(String), - Units(Vec), -} - -impl OwnedText { - pub(crate) fn as_match_text(&self) -> MatchText<'_> { - match self { - OwnedText::Str(s) => MatchText::Str(s), - OwnedText::Units(u) => MatchText::Units(Cow::Borrowed(u)), - } - } - - /// 物化为字符串值(Str 臂文本 / Units 臂单元,载荷形态各自最小)。 - pub(crate) fn to_value(&self, vm: &mut H) -> JsValue { - match self { - OwnedText::Str(s) => vm.new_string(s), - OwnedText::Units(u) => vm.new_string_units_owned(u.clone()), - } - } -} - -// ── 单元基础工具 ───────────────────────────────────────────────────────── - -/// 单元序列首个匹配位置(自 from 起,返回 `hay` 内的绝对下标);needle 空 -/// 由调用方先行处理。 -fn find_units(hay: &[u16], needle: &[u16], from: usize) -> Option { - if from >= hay.len() { - return None; - } - let hay = &hay[from..]; - if needle.len() == 1 { - return hay.iter().position(|&u| u == needle[0]).map(|i| from + i); - } - hay.windows(needle.len()).position(|w| w == needle).map(|i| from + i) -} - -/// 单元序列末个匹配位置(限 end 前缀内起始)。 -fn rfind_units(hay: &[u16], needle: &[u16], end: usize) -> Option { - let hay = &hay[..end]; - if needle.len() == 1 { - return hay.iter().rposition(|&u| u == needle[0]); - } - hay.windows(needle.len()).rposition(|w| w == needle) -} - -/// 自 start 起的最长良形段终点(代理对整体归段,孤立 surrogate 截断段)。 -fn well_formed_segment_end(units: &[u16], start: usize) -> usize { - let mut i = start; - while i < units.len() { - let u = units[i]; - if (0xD800..=0xDBFF).contains(&u) { - if i + 1 < units.len() && (0xDC00..=0xDFFF).contains(&units[i + 1]) { - i += 2; - } else { - break; - } - } else if (0xDC00..=0xDFFF).contains(&u) { - break; - } else { - i += 1; - } - } - i -} - -/// 码点数(代理对计 1,孤立 surrogate 计 1)——pad 族的目标长度口径。 -fn code_point_count(units: &[u16]) -> usize { - let mut n = 0; - let mut i = 0; - while i < units.len() { - if (0xD800..=0xDBFF).contains(&units[i]) && i + 1 < units.len() && (0xDC00..=0xDFFF).contains(&units[i + 1]) { - i += 2; - } else { - i += 1; - } - n += 1; - } - n -} - -/// 取序列头 count 个码点对应的单元前缀。 -fn take_code_points(units: &[u16], count: usize) -> &[u16] { - let mut n = 0; - let mut i = 0; - while i < units.len() && n < count { - if (0xD800..=0xDBFF).contains(&units[i]) && i + 1 < units.len() && (0xDC00..=0xDFFF).contains(&units[i + 1]) { - i += 2; - } else { - i += 1; - } - n += 1; - } - &units[..i] -} - -/// ES 白空格判定的单元口径:Rust 空白表与 White_Space+LineTerminator 等价; -/// 孤立 surrogate 非白空格。 -fn is_trim_unit(u: u16) -> bool { - if u < 0x80 { - return matches!(u, 0x09..=0x0D | 0x20); - } - if let Some(c) = char::from_u32(u as u32) { - return c.is_whitespace(); - } - false -} - -/// 良形文本的字节偏移 → 码元数(Str 臂匹配范围/index 的码元换算入口)。 -fn byte_to_unit(s: &str, byte: usize) -> usize { - if s.is_ascii() { - byte - } else { - s[..byte].encode_utf16().count() - } -} - -/// 良形文本的码元位置 → 字节偏移(位置须落在字符边界:匹配游标恒对齐)。 -fn unit_to_byte(s: &str, unit: usize) -> usize { - if s.is_ascii() { - return unit.min(s.len()); - } - let mut acc = 0usize; - for (byte, ch) in s.char_indices() { - if unit == acc { - return byte; - } - acc += ch.len_utf16(); - } - s.len() -} - -/// 大小写/规范化按"良形段"分段映射:整段经 UTF-8 往返调用映射函数,孤立 -/// surrogate 单元原样透传(映射对码点定义,对孤立 surrogate 无定义)。 -fn map_well_formed_segments(units: &[u16], mut f: impl FnMut(&str) -> String) -> Vec { - let mut out = Vec::with_capacity(units.len()); - let mut i = 0; - while i < units.len() { - let seg_start = i; - i = well_formed_segment_end(units, i); - if i == seg_start { - out.push(units[i]); - i += 1; - continue; - } - let s = String::from_utf16(&units[seg_start..i]).expect("良形段 from_utf16 必成功"); - out.extend(f(&s).encode_utf16()); - } - out -} - -// ── this / 参数转换 ───────────────────────────────────────────────────── - -/// 取 this 的单元序列供只读扫描:原始字符串按载荷形态借用(Flat 惰性编码、 -/// FlatU16 直接借用、Cons 经扁平化缓存),对象经完整 ToString。 -/// null/undefined/symbol 抛 TypeError;对象 ToString 抛出的原生异常原样传播。 -/// -/// # 注意事项 -/// 返回借用绑定本次 `&mut` 借用,存活期内禁止任何 `&mut` 调用;需与参数并存 -/// 时调用方先 `into_owned` 落地。 -fn this_units<'a, H: VmHost>(vm: &'a mut H, args: &[u8]) -> Result, JsValue> { - let this_val = vm.reg(args[0]); - if this_val.is_null() || this_val.is_undefined() { - return Err(crate::error::create_type_error(vm, "String.prototype method called on null or undefined")); - } - if this_val.is_symbol() { - return Err(crate::error::create_type_error(vm, "Cannot convert a Symbol value to a string")); - } - if this_val.is_string() { - return Ok(vm.string_units(this_val)); - } - match oxide_runtime_api::to_string_value_full(this_val, vm) { - Ok(v) => Ok(vm.string_units(v)), - Err(_) => { - // ToString 触发对象 toString/valueOf 抛出的原生异常须原样传播, - // 否则会被展平为普通 Error 丢失原始异常对象。 - if let Some(exc) = vm.take_uncaught_value() { - return Err(exc); - } - Err(crate::error::create_type_error(vm, "Cannot convert this value to a string")) - } - } -} - -/// 取 this 的匹配文本通道:原始字符串按载荷形态零拷贝借用(Flat → Str、其余 -/// → Units),对象经完整 ToString(结果为字符串值,同形态借用)。 -/// null/undefined/symbol 抛 TypeError;对象 ToString 抛出的原生异常原样传播。 -fn this_text<'a, H: VmHost>(vm: &'a mut H, args: &[u8]) -> Result, JsValue> { - let this_val = vm.reg(args[0]); - if this_val.is_null() || this_val.is_undefined() { - return Err(crate::error::create_type_error(vm, "String.prototype method called on null or undefined")); - } - if this_val.is_symbol() { - return Err(crate::error::create_type_error(vm, "Cannot convert a Symbol value to a string")); - } - if this_val.is_string() { - return Ok(MatchText::from_value(this_val)); - } - match oxide_runtime_api::to_string_value_full(this_val, vm) { - Ok(v) => Ok(MatchText::from_value(v)), - Err(_) => { - if let Some(exc) = vm.take_uncaught_value() { - return Err(exc); - } - Err(crate::error::create_type_error(vm, "Cannot convert this value to a string")) - } - } -} - -/// 取参数字符串的单元序列:原始字符串按载荷形态借用,其余经完整 ToString。 -/// Symbol 与其余转换失败按规范抛 TypeError;对象 ToString 抛出的原生异常原样传播。 -fn as_units<'a, H: VmHost>(vm: &'a mut H, val: JsValue) -> Result, JsValue> { - if val.is_string() { - return Ok(vm.string_units(val)); - } - match oxide_runtime_api::to_string_value_full(val, vm) { - Ok(v) => Ok(vm.string_units(v)), - Err(_) => { - 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")) - } - } -} - -// ── 数组产出 ───────────────────────────────────────────────────────────── - -/// 以已构造的字符串值构建字符串数组(元素零拷贝落地),供逐单元产出路径 -/// (空分隔 split)复用,跳过 `Vec` 中间层。 -pub(crate) fn make_string_array_values(vm: &mut H, parts: Vec) -> JsValue { - let proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject; - let n = parts.len(); - let arr = - vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(proto), n, vm.epoch().bump())); - unsafe { - for (i, sv) in parts.into_iter().enumerate() { - (*arr).set_prop_at(i, sv); - } - (*arr).set_prop_count(n); - } - JsValue::from_js_object(arr) -} - -/// 以单元序列集合构建字符串数组(元素按智能路由物化为字符串值),供 -/// split/match 的片段产出路径复用。 -pub(crate) fn make_units_array(vm: &mut H, parts: Vec>) -> JsValue { - let values: Vec = parts.into_iter().map(|u| vm.new_string_units_owned(u)).collect(); - make_string_array_values(vm, values) -} +mod common; +use common::try_string; +pub(crate) use common::*; // ── 正则替换(单元口径) ────────────────────────────────────────────────