types/kernel/vm/builtins: JsString UTF-16 载荷层(FlatU16 变体 + 单元入口)——原 UTF-8 载荷只存 well-formed 串、孤立 surrogate lossy 转 U+FFFD 回写,FromCodePoint/属性转义与字符串面码点语义皆破;现载荷直存 [u64;3](Flat well-formed / FlatU16 单元序列 / Cons 拼接),utf16_len 独立 32 位字,32B 布局锚保持;from_units 智能路由(含孤立→FlatU16),as_str 仅限 Flat,units/units_borrowed 单元借用,as_lossy_str 宽容读;string_forge 编解码 surrogate 感知(孤立 surrogate 逃逸 hex4 编码、解码展开 astral 代理对、string_ptr 由单元物化);VmHost 单元物化/分配/查询入口 + runtime_api 单元 API;字符串内置全法单元化(charAt/slice/substr/split/replace/search/match/matchAll 单元定位;空模式边界:search→0、match→[""]、replace 插 0 位、matchAll 游标耗尽守卫;接收者 ToString 错误原样传播);正则内置单元化(exec/test/search/symbol_* 单元文本,替换串 \u{...} 与孤立 surrogate 文本处理,lastIndex 与 matchAll 游标单元化);字符串迭代器码点化(高代理+后续低代理成对产出,astral 值回真实码点);String(Symbol) 抛 TypeError;回归钉更新五处(charAt/split/slice/substr 单元精确语义 + dispose 钉改 TypeError 断言);四 crate 全绿(oxide_vm 98 套件 2403 测)+ clippy/fmt 零新增

This commit is contained in:
2026-09-13 22:42:10 +00:00
parent 8a35dcd06e
commit 3acac721b0
17 changed files with 2177 additions and 1186 deletions
+64 -11
View File
@@ -1487,6 +1487,30 @@ fn get_iterator<H: VmHost>(vm: &mut H, value: JsValue) -> Result<Option<(JsValue
Ok(None)
}
/// 字符串迭代的一个产出元素:单个码元,或整个合法代理对(超平面字符,
/// 按 Unicode 标量产出单元素)。
#[derive(Clone, Copy)]
enum StrIterElem {
One(u16),
Pair(u16, u16),
}
/// 单元序列的字符串迭代步进(Unicode 标量口径):合法代理对整体产出一个
/// 2 单元元素(游标推进 2),否则产出单单元元素(推进 1);越界产出 None。
fn str_iter_step(units: &[u16], at: usize) -> (usize, Option<StrIterElem>) {
let Some(&u) = units.get(at) else {
return (0, None);
};
if (0xD800..=0xDBFF).contains(&u) {
if let Some(lo) = units.get(at + 1) {
if (0xDC00..=0xDFFF).contains(lo) {
return (at + 2, Some(StrIterElem::Pair(u, *lo)));
}
}
}
(at + 1, Some(StrIterElem::One(u)))
}
fn next_array_like<H: VmHost>(
vm: &mut H, wrapper: &mut JsObject, inner: JsValue, index_si: u32,
) -> Result<Option<JsValue>, JsValue> {
@@ -1513,18 +1537,47 @@ fn next_array_like<H: VmHost>(
}
if inner.is_string() {
// index 槽在字符串分支存字节游标(每步推进一个 char 的 UTF-8 宽度)而非
// 元素序号:该槽只被 next 内部读写、无外部消费者,包装器创建后类型固定
// 不跨类型复用,故可安全借用语义(字符串=字节偏移)。
let byteoff = current_index(vm, wrapper, index_si);
// 源串裸指针借用压缩到单个表达式:ch 是 Copy 的 char,不携带借用,
// index 槽在字符串分支存游标(按载荷形态钉死):单元形态(FlatU16/Cons)
// 存单元偏移;Flat 形态存 字节偏移×2(与单元口径对齐,每字符步长按其
// UTF-16 单元数)。游标只被 next 内部读写、无外部消费者,包装器创建后
// 类型固定不跨类型复用。
//
// 产出按 Unicode 标量(规格口径):合法代理对整体产出一个 2 单元元素,
// 孤立 surrogate 各产出 1 单元元素。
let cursor = current_index(vm, wrapper, index_si);
// 源串裸指针借用压缩到单个表达式:产出元素为 Copy 值,不携带借用,
// 之后对 VM 状态的可变访问不再与源串借用共存。
let ch = unsafe { &*inner.as_string_ptr() }.as_str()[byteoff..].chars().next();
if let Some(ch) = ch {
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int((byteoff + ch.len_utf8()) as i32));
let value = match vm.single_char(ch) {
Some(v) => v,
None => vm.new_string(&ch.to_string()),
let (next_cursor, elem): (usize, Option<StrIterElem>) = unsafe {
let sp = &*inner.as_string_ptr();
if sp.is_flat() {
let byteoff = cursor / 2;
match sp.as_str().get(byteoff..).unwrap_or("").chars().next() {
// 超平面字符:出整个代理对(单元素),游标跳越字符。
Some(c) if c.len_utf8() == 4 => {
let v = c as u32;
let hi = (0xD800 + (((v - 0x10000) >> 10) & 0x3FF)) as u16;
let lo = (0xDC00 + ((v - 0x10000) & 0x3FF)) as u16;
((byteoff + 4) * 2, Some(StrIterElem::Pair(hi, lo)))
}
Some(c) => ((byteoff + c.len_utf8()) * 2, Some(StrIterElem::One(c as u16))),
None => (0, None),
}
} else if let Some(units) = sp.units_borrowed() {
str_iter_step(units, cursor)
} else {
let units = sp.units();
str_iter_step(&units, cursor)
}
};
if let Some(elem) = elem {
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int(next_cursor as i32));
let value = match elem {
// ASCII 单元走单字符缓存零分配,其余(含孤立 surrogate)落串创建。
StrIterElem::One(u) => match vm.single_unit(u) {
Some(v) => v,
None => vm.new_string_units(&[u]),
},
StrIterElem::Pair(hi, lo) => vm.new_string_units(&[hi, lo]),
};
return Ok(Some(make_iter_result(vm, value, false)));
}