mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
perf: 收紧字符串借用语义消除跨分配点 UB
This commit is contained in:
@@ -250,7 +250,7 @@ fn to_bigint<H: VmHost>(vm: &mut H, val: JsValue) -> Result<JsValue, JsValue> {
|
||||
return Err(crate::error::create_type_error(vm, "Cannot convert a Number value to a BigInt"));
|
||||
}
|
||||
if val.is_string() {
|
||||
let s = unsafe { oxide_runtime_api::string_data(val) }.to_string();
|
||||
let s = oxide_runtime_api::to_string(val);
|
||||
return match string_to_bigint(vm, &s) {
|
||||
Ok(v) => Ok(v),
|
||||
Err(e) => Err(e),
|
||||
|
||||
@@ -308,9 +308,10 @@ fn next_array_like<H: VmHost>(
|
||||
let index = current_index(vm, wrapper, index_si);
|
||||
let byteoff_si = vm.kernel_core().perm_interner().intern(BYTEOFF_PROP).0;
|
||||
let byteoff = current_index(vm, wrapper, byteoff_si);
|
||||
let source = unsafe { &*inner.as_string_ptr() }.as_str();
|
||||
let rest = &source[byteoff..];
|
||||
if let Some(ch) = rest.chars().next() {
|
||||
// 源串裸指针借用压缩到单个表达式:ch 是 Copy 的 char,不携带借用,
|
||||
// 之后对 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, byteoff_si, JsValue::int((byteoff + ch.len_utf8()) as i32));
|
||||
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int((index + 1) as i32));
|
||||
let value = vm.new_string(&ch.to_string());
|
||||
|
||||
@@ -192,7 +192,7 @@ pub fn module_data<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if !kind_val.is_string() || !content_val.is_string() {
|
||||
return type_error(vm, "__moduleData: kind/content must be strings");
|
||||
}
|
||||
let kind = unsafe { oxide_runtime_api::string_data(kind_val) }.to_string();
|
||||
let kind = oxide_runtime_api::to_string(kind_val);
|
||||
let default_val = match kind.as_str() {
|
||||
"json" => {
|
||||
// 复用 JSON.parse 的 serde 管线:args[1] 即内容字符串。
|
||||
@@ -204,8 +204,9 @@ pub fn module_data<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
"text" => {
|
||||
let text = unsafe { oxide_runtime_api::string_data(content_val) };
|
||||
vm.new_string(text)
|
||||
// 经借用路径拷贝出 owned String 后再入堆,避免借用跨过 new_string 分配点。
|
||||
let text = vm.string_ref(content_val).to_string();
|
||||
vm.new_string_owned(text)
|
||||
}
|
||||
_ => return type_error(vm, "__moduleData: unsupported data kind"),
|
||||
};
|
||||
|
||||
@@ -77,6 +77,13 @@ pub trait VmHost {
|
||||
fn new_string(&mut self, s: &str) -> JsValue;
|
||||
/// move 接收 `String` 创建会话字符串,避免一次整串克隆。
|
||||
fn new_string_owned(&mut self, s: String) -> JsValue;
|
||||
/// 借出字符串值的文本内容,生命周期绑定到 `&self` 借用。
|
||||
///
|
||||
/// perm 字符串由内核持有、永不释放;session 字符串只在 `&mut self` 路径
|
||||
/// (`new_string`/`new_string_owned`/`maybe_collect_session_gc`)释放,`&self`
|
||||
/// 借用与 `&mut` 互斥由编译器强制,借用期内该字符串不会回收。实现内部
|
||||
/// `unsafe` 解引用,调用方须保证 `val` 为字符串值。
|
||||
fn string_ref(&self, val: JsValue) -> &str;
|
||||
/// 分配 BigInt 值(num_bigint::BigInt box 登记到 VM,返回携带指针的 `JsValue`)。
|
||||
fn new_bigint(&mut self, v: num_bigint::BigInt) -> JsValue;
|
||||
/// 读取 BigInt 值;调用方须保证 `val.is_bigint()`。
|
||||
@@ -149,12 +156,15 @@ pub fn format_error_message(name: &str, msg: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// 借出字符串值的文本内容。
|
||||
/// 借出字符串值的文本内容,生命周期为 `'static`。仅限同一函数内即时消费。
|
||||
///
|
||||
/// # Safety
|
||||
/// `val` 必须是字符串 `JsValue`,且其 `JsString` 指针存活。
|
||||
/// 调用方必须保证:从取用返回值到最后一次使用之间,不发生任何可能释放该字符串
|
||||
/// 的操作(分配、GC、`full_reset`)——session 字符串在执行期 GC 或 reset 时会被
|
||||
/// 释放,跨分配点持有引用即悬垂。需要跨分配点消费的场景改用 [`VmHost::string_ref`]
|
||||
/// 或公开的 [`to_string`] owned 路径。
|
||||
#[inline]
|
||||
pub unsafe fn string_data(val: JsValue) -> &'static str {
|
||||
pub(crate) unsafe fn string_data(val: JsValue) -> &'static str {
|
||||
(*val.as_string_ptr()).as_str()
|
||||
}
|
||||
|
||||
|
||||
@@ -1808,6 +1808,12 @@ impl oxide_runtime_api::VmHost for Vm {
|
||||
fn new_string_owned(&mut self, s: String) -> JsValue {
|
||||
Vm::new_string_owned(self, s)
|
||||
}
|
||||
fn string_ref(&self, val: JsValue) -> &str {
|
||||
// SAFETY: 调用方保证 val 为字符串值。perm 串由内核持有永不释放;session
|
||||
// 串仅经 &mut self 路径(new_string/new_string_owned/GC)释放,此处 &self
|
||||
// 借用期间编译器强制不存在 &mut 存续,字符串不会在借用期内回收。
|
||||
unsafe { (*val.as_string_ptr()).as_str() }
|
||||
}
|
||||
fn new_bigint(&mut self, v: num_bigint::BigInt) -> JsValue {
|
||||
Vm::new_bigint(self, v)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ fn run(module: &CompiledModule) -> JsValue {
|
||||
fn eval_str(source: &str) -> String {
|
||||
let v = eval(source);
|
||||
if v.is_string() {
|
||||
unsafe { oxide_runtime_api::string_data(v) }.to_string()
|
||||
oxide_runtime_api::to_string(v)
|
||||
} else {
|
||||
format!("{v}")
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ fn eval_int(source: &str) -> i32 {
|
||||
fn eval_str(source: &str) -> String {
|
||||
let v = eval(source);
|
||||
if v.is_string() {
|
||||
unsafe { oxide_runtime_api::string_data(v) }.to_string()
|
||||
oxide_runtime_api::to_string(v)
|
||||
} else {
|
||||
format!("{v}")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user