mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 10:11:11 +08:00
perf: 字符串 length 站点指针快判免 IC 读
This commit is contained in:
Generated
+18
-18
@@ -1095,7 +1095,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_builtins"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fast-float",
|
||||
@@ -1120,7 +1120,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_bytecode"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_log",
|
||||
@@ -1128,7 +1128,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cfg"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_emit",
|
||||
@@ -1139,7 +1139,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cli"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"bumpalo",
|
||||
@@ -1159,7 +1159,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_code_cache"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"lru",
|
||||
"oxide_bytecode",
|
||||
@@ -1168,7 +1168,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_compiler"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1187,7 +1187,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_dce"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1201,7 +1201,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_emit"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxc_allocator",
|
||||
@@ -1216,7 +1216,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_ir"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_log",
|
||||
@@ -1225,7 +1225,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_kernel"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1240,7 +1240,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_liveness"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1252,7 +1252,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_log"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
@@ -1261,7 +1261,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_parser"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
@@ -1276,7 +1276,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_regalloc"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1292,7 +1292,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_runtime_api"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
@@ -1304,7 +1304,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_test262"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1321,7 +1321,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_types"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"hashbrown 0.14.5",
|
||||
@@ -1333,7 +1333,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_vm"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"chrono",
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -334,6 +334,16 @@ impl Vm {
|
||||
let val = self.regs[a];
|
||||
let obj_ptr = val.as_object_ptr() as *mut JsObject;
|
||||
if obj_ptr.is_null() {
|
||||
// 入口快判:键寄存器恒为 perm 串,指针相等即键为 "length";
|
||||
// 字符串接收者直返码元长度,免键解析(hash64 + DashMap + RwLock)。
|
||||
let key_val = self.regs[b];
|
||||
if key_val.is_string() && key_val.as_string_ptr() == self.length_perm_ptr && val.is_string() {
|
||||
// SAFETY: val 是字符串值。
|
||||
let len = unsafe { (*val.as_string_ptr()).utf16_len() };
|
||||
self.regs[a] = JsValue::int(len as i32);
|
||||
self.pc += IC_EXT_WORDS;
|
||||
return Ok(());
|
||||
}
|
||||
let prop_name_si = self.property_key_si(self.regs[b])?;
|
||||
if let Some(resolved) = self.primitive_property_get(val, prop_name_si)? {
|
||||
self.regs[a] = resolved;
|
||||
@@ -350,6 +360,14 @@ impl Vm {
|
||||
}
|
||||
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
// 入口快判:键寄存器恒为 perm 串,指针相等即键为 "length";
|
||||
// 数组接收者直返逻辑长度,免 IC 读与键解析(与虚拟长度槽口径一致)。
|
||||
let key_val = self.regs[b];
|
||||
if key_val.is_string() && key_val.as_string_ptr() == self.length_perm_ptr && obj.is_array() {
|
||||
self.regs[a] = obj.logical_len_value();
|
||||
self.pc += IC_EXT_WORDS;
|
||||
return Ok(());
|
||||
}
|
||||
let (cached_shape_id, cached_slot, cached_depth) = ic_helper::read_ic_slot0(&self.bytecode, &mut self.pc);
|
||||
let ic_pc = self.pc;
|
||||
if obj.has_prop_meta() {
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::vm_state::{GcState, IterState, ProfilingState, SampleState, SymbolSta
|
||||
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
||||
use oxide_types::error::JsErrorKind;
|
||||
use oxide_types::mem::P;
|
||||
use oxide_types::object::{Cell, JsObject, NativeFnPtr};
|
||||
use oxide_types::object::{Cell, JsObject, JsString, NativeFnPtr};
|
||||
use oxide_types::private_key::INT_KEY_COUNT;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
@@ -179,6 +179,10 @@ pub struct Vm {
|
||||
/// KernelCore 不重建),属性 get/set 热路径免每次 intern(hash64 + DashMap +
|
||||
/// RwLock 读锁)。
|
||||
pub(crate) length_si: u32,
|
||||
/// `"length"` 属性键 perm 串的内部指针:IC 站点的键寄存器恒为 perm 串
|
||||
/// (发射层字符串常量统一经 perm_string 物化),指针相等即键为 "length",
|
||||
/// 入口快判只比指针免键解析。PermInterner 追加式、指针唯一,永不失效。
|
||||
pub(crate) length_perm_ptr: *const JsString,
|
||||
pub object_prototype: P<JsObject>,
|
||||
/// `%GeneratorPrototype%`:生成器实例的原型(next/return/throw 方法挂此)。
|
||||
pub generator_proto: P<JsObject>,
|
||||
|
||||
@@ -1864,4 +1864,80 @@ mod tests {
|
||||
assert_eq!(obj.prop_vec_len(), 1, "命名区恰一个槽,复写不重复建槽");
|
||||
assert_eq!(vm.ordinary_get(obj, si, a).expect("get"), JsValue::int(9));
|
||||
}
|
||||
|
||||
// ===== defineProperty 对 length 键的拦截语义钉 =====
|
||||
|
||||
#[test]
|
||||
fn define_property_array_length_applies_array_set_length() {
|
||||
let mut vm = Vm::new();
|
||||
let a = new_array_val(&mut vm, 3);
|
||||
unsafe {
|
||||
let arr = a.as_js_object_ptr();
|
||||
(*arr).set_prop_at(0, JsValue::int(1));
|
||||
(*arr).set_prop_at(1, JsValue::int(2));
|
||||
(*arr).set_prop_at(2, JsValue::int(3));
|
||||
}
|
||||
let length_si = vm.kernel_core.perm_interner().intern("length").0;
|
||||
let si0 = array_index_si(&mut vm, 0);
|
||||
let si1 = array_index_si(&mut vm, 1);
|
||||
// 数组 length 一律 ArraySetLength 拦截:截断到 1,元素区同步收缩。
|
||||
// writable 保持 true,允许后续重定义再扩。
|
||||
let attrs = PropAttributes::new(true, false, false);
|
||||
vm.define_data_property(unsafe { &mut *a.as_js_object_ptr() }, length_si, JsValue::int(1), attrs)
|
||||
.expect("define length");
|
||||
let obj = unsafe { &*a.as_js_object_ptr() };
|
||||
assert_eq!(obj.logical_len(), 1, "length 截断到 1");
|
||||
assert_eq!(vm.ordinary_get(obj, length_si, a).expect("get"), JsValue::int(1));
|
||||
assert_eq!(vm.ordinary_get(obj, si0, a).expect("get"), JsValue::int(1));
|
||||
assert_eq!(vm.ordinary_get(obj, si1, a).expect("get"), JsValue::undefined(), "元素 1 随截断删除");
|
||||
// 扩到 5:首元素保留,新增元素区补 undefined。
|
||||
vm.define_data_property(unsafe { &mut *a.as_js_object_ptr() }, length_si, JsValue::int(5), attrs)
|
||||
.expect("define length");
|
||||
let obj = unsafe { &*a.as_js_object_ptr() };
|
||||
assert_eq!(obj.logical_len(), 5, "length 扩到 5");
|
||||
assert_eq!(vm.ordinary_get(obj, length_si, a).expect("get"), JsValue::int(5));
|
||||
assert_eq!(vm.ordinary_get(obj, si0, a).expect("get"), JsValue::int(1), "首元素保留");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn define_property_array_length_accessor_rejected() {
|
||||
let mut vm = Vm::new();
|
||||
let a = new_array_val(&mut vm, 1);
|
||||
let getter = native_function(&mut vm, native_return_7);
|
||||
let length_si = vm.kernel_core.perm_interner().intern("length").0;
|
||||
// 数组 length 为不可配置数据属性,禁止转访问器。
|
||||
let err = vm
|
||||
.define_accessor_property(
|
||||
unsafe { &mut *a.as_js_object_ptr() },
|
||||
length_si,
|
||||
getter,
|
||||
JsValue::undefined(),
|
||||
PropAttributes::new(false, false, false),
|
||||
)
|
||||
.expect_err("数组 length 禁转访问器");
|
||||
assert!(err.contains("cannot redefine"), "实际: {err}");
|
||||
let obj = unsafe { &*a.as_js_object_ptr() };
|
||||
assert_eq!(obj.logical_len(), 1, "length 不变");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn define_property_string_length_rejected() {
|
||||
let mut vm = Vm::new();
|
||||
let str_si = vm.kernel_core.perm_interner().intern("abcd").0;
|
||||
let str_val = JsValue::perm_string(vm.kernel_core.perm_interner().string_ptr(str_si));
|
||||
let boxed = coercion::to_object(str_val, &mut vm).expect("box");
|
||||
let length_si = vm.kernel_core.perm_interner().intern("length").0;
|
||||
// 字符串对象 length 不可写不可配置,重定义校验拒绝。
|
||||
let err = vm
|
||||
.define_data_property(
|
||||
unsafe { &mut *boxed.as_js_object_ptr() },
|
||||
length_si,
|
||||
JsValue::int(5),
|
||||
PropAttributes::new(false, false, false),
|
||||
)
|
||||
.expect_err("字符串 length 不可重定义");
|
||||
assert!(err.contains("cannot redefine"), "实际: {err}");
|
||||
let obj = unsafe { &*boxed.as_js_object_ptr() };
|
||||
assert_eq!(vm.ordinary_get(obj, length_si, boxed).expect("get"), JsValue::int(4), "length 不变");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ impl Vm {
|
||||
// 在 builtin 绑定之后取 id:绑定过程已 intern "length",此处命中缓存得到
|
||||
// 稳定的 id,用于运行期常见属性(如函数 length 槽)的快路径。
|
||||
let length_si = core.perm_interner().intern("length").0;
|
||||
let length_perm_ptr = core.perm_interner().string_ptr(length_si);
|
||||
let obj_proto = P::clone(&session.builtin_world().object_proto);
|
||||
// 提前缓存执行期字符串 GC 初始水位(构造后 config 不再变化)。
|
||||
let gc_threshold = core.config().session_gc_threshold;
|
||||
@@ -55,6 +56,7 @@ impl Vm {
|
||||
kernel_core: core,
|
||||
session,
|
||||
length_si,
|
||||
length_perm_ptr,
|
||||
object_prototype: obj_proto,
|
||||
generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
@@ -182,6 +184,7 @@ impl Vm {
|
||||
// 在 builtin 绑定之后取 id:绑定过程已 intern "length",此处命中缓存得到
|
||||
// 稳定的 id,用于运行期常见属性(如函数 length 槽)的快路径。
|
||||
let length_si = core.perm_interner().intern("length").0;
|
||||
let length_perm_ptr = core.perm_interner().string_ptr(length_si);
|
||||
let obj_proto = P::clone(&session.builtin_world().object_proto);
|
||||
// 提前缓存执行期字符串 GC 初始水位(构造后 config 不再变化)。
|
||||
let gc_threshold = core.config().session_gc_threshold;
|
||||
@@ -194,6 +197,7 @@ impl Vm {
|
||||
kernel_core: core,
|
||||
session,
|
||||
length_si,
|
||||
length_perm_ptr,
|
||||
object_prototype: obj_proto,
|
||||
generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
var ITERATIONS = 3000;
|
||||
|
||||
var arr = [];
|
||||
for (var i = 0; i < 100; i++) {
|
||||
arr.push("item" + i);
|
||||
}
|
||||
var sum = 0;
|
||||
for (var j = 0; j < ITERATIONS; j++) {
|
||||
for (var k = 0; k < 100; k++) {
|
||||
sum += arr[k].length;
|
||||
}
|
||||
}
|
||||
sum
|
||||
Reference in New Issue
Block a user