perf: 模板字面量字符串段借用直写并消除末尾克隆

This commit is contained in:
2026-08-15 15:10:23 +08:00
parent f9c639426d
commit 3c9e33f227
2 changed files with 38 additions and 11 deletions
+17 -11
View File
@@ -1,7 +1,7 @@
use crate::native::NativeFn; use crate::native::NativeFn;
use crate::vm::{native_fn_ptr_to_fn, ForInIter, FrameArgs, FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH}; use crate::vm::{native_fn_ptr_to_fn, ForInIter, FrameArgs, FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH};
use crate::vm_trace; use crate::vm_trace;
use oxide_runtime_api::{to_boolean, to_string_full, NativeResult, VmHost}; use oxide_runtime_api::{push_to_string, to_boolean, to_string_full, NativeResult, VmHost};
use oxide_types::object::{JsObject, PropAttributes}; use oxide_types::object::{JsObject, PropAttributes};
use oxide_types::private_key::{int_key_value, is_int_key, is_private_name_key, is_symbol_key, make_int_key}; use oxide_types::private_key::{int_key_value, is_int_key, is_private_name_key, is_symbol_key, make_int_key};
use oxide_types::value::JsValue; use oxide_types::value::JsValue;
@@ -149,27 +149,33 @@ impl Vm {
if (seg >> 31) == 1 { if (seg >> 31) == 1 {
let reg = (seg & 0x7FFF_FFFF) as usize; let reg = (seg & 0x7FFF_FFFF) as usize;
let val = self.regs[reg]; let val = self.regs[reg];
let s = if val.is_string() { if val.is_string() {
// SAFETY: val 是字符串值。 // SAFETY: val 是字符串值;借用仅在本次 push 内消费,不跨分配点。
unsafe { (*val.as_string_ptr()).to_owned_string() } let s = unsafe { (*val.as_string_ptr()).as_str() };
result.push_str(s);
} else if val.is_object() || val.is_symbol() {
// 对象走完整 ToString(ToPrimitive 副作用顺序);Symbol 由
// to_string_full 抛 TypeError(push_to_string 无 symbol 分支)。
let s = to_string_full(val, self)?;
result.push_str(&s);
} else { } else {
to_string_full(val, self)? // 原始值直写结果缓冲,免中间 String。
}; push_to_string(val, &mut result);
result.push_str(&s); }
} else { } else {
let const_idx = (seg & 0x7FFF_FFFF) as usize; let const_idx = (seg & 0x7FFF_FFFF) as usize;
let imm = self.immutables(); let imm = self.immutables();
if const_idx < imm.len() { if const_idx < imm.len() {
let val = imm[const_idx]; let val = imm[const_idx];
if val.is_string() { if val.is_string() {
// SAFETY: val 是字符串值。 // SAFETY: val 是字符串值;借用仅在本次 push 内消费,不跨分配点。
let s = unsafe { (*val.as_string_ptr()).to_owned_string() }; let s = unsafe { (*val.as_string_ptr()).as_str() };
result.push_str(&s); result.push_str(s);
} }
} }
} }
} }
self.regs[rd] = self.new_string(&result); self.regs[rd] = self.new_string_owned(result);
Ok(()) Ok(())
} }
+21
View File
@@ -92,6 +92,27 @@ fn template_numeric_expression() {
assert_eq!(to_str(&vm, result.unwrap()), "2"); assert_eq!(to_str(&vm, result.unwrap()), "2");
} }
#[test]
fn template_with_double_bigint_and_bool() {
// 原始值段走直写路径,输出与 ToString 一致。
let (vm, result) = eval_val("`a${1.5}b${123n}c${true}d`");
assert_eq!(to_str(&vm, result.unwrap()), "a1.5b123ctrued");
}
#[test]
fn template_with_object_uses_tostring() {
// 对象段保留完整 ToString(ToPrimitive 触发用户 toString)。
let (vm, result) = eval_val("var o = { toString: function () { return 'T'; } }; `x${o}y`");
assert_eq!(to_str(&vm, result.unwrap()), "xTy");
}
#[test]
fn template_with_symbol_throws_type_error() {
// Symbol 段经 to_string_full 抛 TypeError,不静默输出空段。
let result = eval("`${Symbol('x')}`");
assert!(result.contains("TypeError"), "模板串中的 Symbol 应抛 TypeError,got: {result}");
}
#[test] #[test]
fn template_expression_reads_physical_register_above_127() { fn template_expression_reads_physical_register_above_127() {
let mut ir = IRFunction::new(); let mut ir = IRFunction::new();