perf: typeof 改 js_type 单次提取并复用进程级静态字符串消除每次分配

This commit is contained in:
2026-08-15 16:09:19 +08:00
parent 31296ed16d
commit 3d1ece321b
8 changed files with 128 additions and 57 deletions
+20 -24
View File
@@ -4,7 +4,7 @@ use oxide_bytecode::opcode;
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::{JsObject, PropAttributes};
use oxide_types::private_key::make_int_key;
use oxide_types::value::JsValue;
use oxide_types::value::{JsType, JsValue};
impl Vm {
#[inline(always)]
@@ -25,31 +25,27 @@ impl Vm {
pub(crate) fn dispatch_typeof(&mut self, rd: usize, a: usize) {
vm_trace!("TYPEOF rd={} r{}={:?}", rd, a, self.regs[a]);
let val = self.regs[a];
let result = if val.is_undefined() {
"undefined"
} else if val.is_null() {
"object"
} else if val.is_bool() {
"boolean"
} else if val.is_int() || val.is_double() {
"number"
} else if val.is_string() {
"string"
} else if val.is_symbol() {
"symbol"
} else if val.is_bigint() {
"bigint"
} else if val.is_object() {
let obj = unsafe { &*val.as_js_object_ptr() };
if obj.is_function() {
"function"
} else {
"object"
let kind = match val.js_type() {
JsType::Undefined => 0,
JsType::Null => 1,
JsType::Bool => 2,
JsType::Int | JsType::Double => 3,
JsType::String => 4,
JsType::Symbol => 5,
JsType::BigInt => 6,
JsType::Object => {
// 函数对象归为 "function"(语言类型是 object,typeof 有专属分支)。
let obj = unsafe { &*val.as_js_object_ptr() };
if obj.is_function() {
7
} else {
1
}
}
} else {
"undefined"
};
self.regs[rd] = self.new_string(result);
// 结果字符串复用进程级静态表(零分配、无锁、跨 VM 共享),
// 避免每次 typeof 新建 session 字符串。
self.regs[rd] = JsValue::string(oxide_kernel::string_forge::typeof_string_ptr(kind));
}
/// ToObject:rd 原地转换。对象直接返回;null/undefined 抛 TypeError;
@@ -343,8 +343,12 @@ fn format_error_message_empty_name() {
#[test]
fn error_stack_is_string() {
let result = eval("typeof new Error().stack()").unwrap();
let vm = make_vm();
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, "typeof new Error().stack()").unwrap();
let module = Compiler::new().compile(&program).unwrap();
let mut vm = make_vm();
let result = vm.run(&module).unwrap();
// typeof 结果复用进程级静态串,同 VM 上读取(跨 VM 指针存活由实现保证,不做假设)。
let s = vm.lookup_str(result);
assert_eq!(s, Some("string".to_string()));
}
+34
View File
@@ -252,6 +252,40 @@ fn eval_signed_zero_object_is_and_array_methods() {
assert_eq!(eval("[NaN].includes(NaN)"), "true");
}
// typeof 结果断言需要真实字符串内容,单独用 (Vm, JsValue) 形态的辅助。
fn eval_ty(source: &str) -> Result<(Vm, JsValue), String> {
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
let result = vm.run(&module)?;
Ok((vm, result))
}
// typeof:js_type 分派 + perm_string 的结果与语言类型一一对应。
#[test]
fn eval_typeof_all_kinds() {
let cases: &[(&str, &str)] = &[
("typeof 1", "number"),
("typeof 1.5", "number"),
("typeof NaN", "number"),
("typeof 's'", "string"),
("typeof true", "boolean"),
("typeof null", "object"),
("typeof undefined", "undefined"),
("typeof function(){}", "function"),
("typeof Symbol('x')", "symbol"),
("typeof 1n", "bigint"),
("typeof {}", "object"),
("typeof []", "object"),
];
for (src, expected) in cases {
let (vm, result) = eval_ty(src).unwrap_or_else(|e| panic!("{src} failed: {e}"));
let actual = vm.lookup_str(result).unwrap_or_default();
assert_eq!(actual, *expected, "{src} 的 typeof 结果不符");
}
}
// 算术 +:对象操作数经 valueOf 强转。
#[test]
fn add_object_valueof_coercion() {
@@ -20,9 +20,14 @@ fn run(module: &CompiledModule) -> JsValue {
}
fn eval_str(source: &str) -> String {
let v = eval(source);
let allocator = Allocator::default();
let program = oxide_parser::parse(&allocator, source).expect("parse failed");
let module = Compiler::new().compile(&program).expect("compile failed");
let mut vm = Vm::new();
let v = vm.run(&module).expect("vm run failed");
// 字符串结果须在同一 VM 上读:perm 串指向 VM 私有内核,VM drop 后指针悬垂。
if v.is_string() {
oxide_runtime_api::to_string(v)
vm.lookup_str(v).unwrap_or_default()
} else {
format!("{v}")
}
@@ -16,9 +16,10 @@ fn eval(source: &str) -> Result<(Vm, JsValue), String> {
}
fn eval_str(source: &str) -> Result<String, String> {
let (_vm, v) = eval(source)?;
let (vm, v) = eval(source)?;
// 字符串结果须在同一 VM 上读:perm 串指向 VM 私有内核,VM drop 后指针悬垂。
if v.is_string() {
Ok(oxide_runtime_api::to_string(v))
Ok(vm.lookup_str(v).unwrap_or_default())
} else {
Ok(format!("{v}"))
}
+7 -2
View File
@@ -26,9 +26,14 @@ fn eval_int(source: &str) -> i32 {
}
fn eval_str(source: &str) -> String {
let v = eval(source);
let allocator = Allocator::default();
let program = oxide_parser::parse(&allocator, source).expect("parse failed");
let module = Compiler::new().compile(&program).expect("compile failed");
let mut vm = Vm::new();
let v = vm.run(&module).expect("vm run failed");
// 字符串结果须在同一 VM 上读:perm 串指向 VM 私有内核,VM drop 后指针悬垂。
if v.is_string() {
oxide_runtime_api::to_string(v)
vm.lookup_str(v).unwrap_or_default()
} else {
format!("{v}")
}