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
+9 -9
View File
@@ -541,7 +541,7 @@ impl SessionGc {
/// `ptr` 必须是 `Box::into_raw(Box::new(JsString))` 产生的非空指针,仍存在
/// 于 `session_string_ptrs`,且恰好释放一次。
unsafe fn drop_dead_session_string(ptr: *mut JsString) -> u64 {
let bytes = (size_of::<JsString>() + (*ptr).len()) as u64;
let bytes = (size_of::<JsString>() + (*ptr).payload_bytes()) as u64;
Self::drop_session_string_box(ptr);
bytes
}
@@ -701,7 +701,7 @@ impl SessionGc {
if self.live_strings.contains(&ptr) {
// 存活——地址不变,无需重写。
// SAFETY: ptr 是仍归 VM 所有的存活 session 字符串 box。
live_bytes += unsafe { size_of::<JsString>() + (*ptr).len() };
live_bytes += unsafe { size_of::<JsString>() + (*ptr).payload_bytes() };
live.push(ptr);
} else {
// SAFETY: ptr 在 session_string_ptrs 中但不可达,恰好释放一次。
@@ -1032,7 +1032,7 @@ impl SessionGc {
continue;
}
// SAFETY: ptr 在字符串表登记,收尾前有效。
string_bytes += size_of::<JsString>() + unsafe { (*ptr).len() };
string_bytes += size_of::<JsString>() + unsafe { (*ptr).payload_bytes() };
}
let bigint_bytes = vm.gc_state.session_bigint_ptrs.borrow().len() * size_of::<num_bigint::BigInt>();
vm.gc_state.session_bytes_allocated = object_bytes + string_bytes + bigint_bytes;
@@ -2311,7 +2311,7 @@ mod tests {
vm.maybe_collect_session_strings();
// 账目 = 对象头 + 存活串(size_of::<JsString>() + len),死串不再计入。
let expected = size_of::<JsObject>() + (size_of::<JsString>() + unsafe { (*live_ptr).len() });
let expected = size_of::<JsObject>() + (size_of::<JsString>() + unsafe { (*live_ptr).payload_bytes() });
assert_eq!(vm.gc_state.session_bytes_allocated, expected);
assert!(!vm.gc_state.session_string_ptrs.contains(&dead.as_string_ptr_mut()));
}
@@ -2349,7 +2349,7 @@ mod tests {
assert!(vm.gc_state.session_string_ptrs.contains(&parent_ptr));
assert!(vm.gc_state.session_string_ptrs.contains(&l_ptr));
assert!(vm.gc_state.session_string_ptrs.contains(&r_ptr));
assert_eq!(unsafe { (*parent_ptr).as_str() }, "left-partright-part");
assert_eq!(unsafe { (*parent_ptr).as_lossy_str() }, "left-partright-part");
}
#[test]
@@ -2373,7 +2373,7 @@ mod tests {
let (parent, _, _) = make_cons_pair(&mut vm, "left-part", "right-part");
let parent_ptr = parent.as_string_ptr_mut();
// 触发扁平化:产物发布到 flat_cache。
assert_eq!(unsafe { (*parent_ptr).flat_str() }, "left-partright-part");
assert_eq!(unsafe { (*parent_ptr).as_lossy_str() }, "left-partright-part");
let flat_ptr = unsafe { (*parent_ptr).flat_cache_ptr() };
assert!(!flat_ptr.is_null());
@@ -2400,7 +2400,7 @@ mod tests {
collect(&mut vm);
assert!(vm.gc_state.session_string_ptrs.contains(&chain_ptr));
assert_eq!(unsafe { (*chain_ptr).as_str() }, format!("root{}", "x".repeat(1024)));
assert_eq!(unsafe { (*chain_ptr).as_lossy_str() }, format!("root{}", "x".repeat(1024)));
}
#[test]
@@ -2418,7 +2418,7 @@ mod tests {
// perm 子节点永不释放(不在 session 表);session 子节点随父存活。
assert!(!vm.gc_state.session_string_ptrs.contains(&perm_ptr));
assert!(vm.gc_state.session_string_ptrs.contains(&session_ptr));
assert_eq!(unsafe { (*parent.as_string_ptr_mut()).as_str() }, "perm-leafsession-leaf");
assert_eq!(unsafe { (*parent.as_string_ptr_mut()).as_lossy_str() }, "perm-leafsession-leaf");
}
#[test]
@@ -2426,7 +2426,7 @@ mod tests {
let mut vm = Vm::new();
let (parent, _, _) = make_cons_pair(&mut vm, "left-part", "right-part");
// 触发扁平化(产物挂在父上)。
assert_eq!(unsafe { (*parent.as_string_ptr_mut()).flat_str() }, "left-partright-part");
assert_eq!(unsafe { (*parent.as_string_ptr_mut()).as_lossy_str() }, "left-partright-part");
vm.regs[0] = parent;
// full_reset 清空全部 session 字符串(连带产物):无泄漏、无 double-free。