perf: 数组元素访问 dispatch 内联 fast path + length_si 缓存
GET_PROP_DYNAMIC/SET_PROP_DYNAMIC 开头内联数组元素 fast path:整数键 + 真数组 + 无元素 meta + 界内时直读/直写元素区,免 primitive_property_get / ordinary_get/set 调用链与 __proto__ 每次 RwLock 查询;越界与有 meta 场景回退原路径(CreateDataProperty + length 扩展 / accessor/hole 协议)。 GET 侧 is_object() 快检先于 checked_object_ptr,保持原始值 receiver 不抛错。length_si 缓存为 Vm 字段(builtin 绑定后取 id,稳定非 0), 普通属性路径 5 处 hot spot 改读缓存,消除每次 intern 的共享锁开销。
This commit is contained in:
@@ -131,7 +131,7 @@ impl Vm {
|
||||
);
|
||||
// 函数名推断:emit 端在变量声明/对象属性赋值点设置 function_name。
|
||||
let func_obj = unsafe { &mut *result.as_js_object_ptr() };
|
||||
let length_si = self.kernel_core.perm_interner().intern("length").0;
|
||||
let length_si = self.length_si;
|
||||
let name_si = self.kernel_core.perm_interner().intern("name").0;
|
||||
// length/name 描述符均为不可写、不可枚举、可配置(SetFunctionLength /
|
||||
// SetFunctionName 语义);length 先于 name 定义保证属性序 [prototype, length, name]。
|
||||
|
||||
@@ -216,7 +216,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
// length:实参个数,可写、不可枚举、可配置。
|
||||
let length_si = self.kernel_core.perm_interner().intern("length").0;
|
||||
let length_si = self.length_si;
|
||||
if let Err(msg) = self.define_data_property(
|
||||
obj,
|
||||
length_si,
|
||||
|
||||
@@ -3,7 +3,7 @@ use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_kernel::prop_forge::PropTemplate;
|
||||
use oxide_runtime_api as coercion;
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::private_key::make_private_name_id;
|
||||
use oxide_types::private_key::{int_key_value, is_int_key, make_private_name_id};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use crate::ic_helper::{self, ic_get_hit, ic_set_hit};
|
||||
@@ -46,7 +46,7 @@ impl Vm {
|
||||
|
||||
fn primitive_property_get(&mut self, val: JsValue, prop_name_si: u32) -> Result<Option<JsValue>, String> {
|
||||
if val.is_string() {
|
||||
let length_si = self.kernel_core.perm_interner().intern("length").0;
|
||||
let length_si = self.length_si;
|
||||
if prop_name_si == length_si {
|
||||
// SAFETY: val 是字符串值。utf16_len 构造时缓存,O(1) 读取。
|
||||
let len = unsafe { (*val.as_string_ptr()).utf16_len() };
|
||||
@@ -522,15 +522,33 @@ impl Vm {
|
||||
fn dispatch_get_prop_dynamic(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("GET_PROP_DYNAMIC rd={} a={} b={}", rd, a, b);
|
||||
let prop_name_si = self.property_key_si(self.regs[a])?;
|
||||
if let Some(value) = self.primitive_property_get(self.regs[rd], prop_name_si)? {
|
||||
let receiver = self.regs[rd];
|
||||
// 数组元素 fast path:整数键 + 真数组 + 无元素 meta(无 hole/accessor/描述符)
|
||||
// + 界内 → 直读元素区,免 primitive_property_get / checked_object_ptr /
|
||||
// ordinary_get 调用链。判定条件与 ordinary_get_inner 数组分支严格等价。
|
||||
// 非对象 receiver(如 `"abc"[0]`)须先 is_object 快检再 checked_object_ptr,
|
||||
// 否则该检查会误抛 TypeError(原路径对原始值走 primitive_property_get 不抛)。
|
||||
if is_int_key(prop_name_si) && receiver.is_object() {
|
||||
if let Some(obj_ptr) = self.checked_object_ptr(receiver, "GET_PROP_DYNAMIC on non-object")? {
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
if obj.is_array() && obj.array_elements_meta_vec().is_none() {
|
||||
let idx = int_key_value(prop_name_si);
|
||||
if idx < obj.array_prop_count {
|
||||
self.regs[b] = obj.get_prop_at(idx);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(value) = self.primitive_property_get(receiver, prop_name_si)? {
|
||||
self.regs[b] = value;
|
||||
return Ok(());
|
||||
}
|
||||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "GET_PROP_DYNAMIC on non-object")? else {
|
||||
let Some(obj_ptr) = self.checked_object_ptr(receiver, "GET_PROP_DYNAMIC on non-object")? else {
|
||||
return Ok(());
|
||||
};
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
let val = self.ordinary_get_with_target(obj, prop_name_si, self.regs[rd], b as u8)?;
|
||||
let val = self.ordinary_get_with_target(obj, prop_name_si, receiver, b as u8)?;
|
||||
if self.accessor_frame_target_reg.take().is_none() {
|
||||
self.regs[b] = val;
|
||||
}
|
||||
@@ -543,7 +561,28 @@ impl Vm {
|
||||
return Ok(());
|
||||
};
|
||||
let prop_name_si = self.property_key_si(self.regs[a])?;
|
||||
if self.kernel_core.perm_interner().lookup(prop_name_si) == Some("__proto__") {
|
||||
// 数组元素 fast path:整数键 + 真数组 + 无 meta + 界内 → 直写元素区,
|
||||
// 免 __proto__ 查询 / ordinary_set_dispatch / get_own_property_slot 调用链。
|
||||
// 与 ordinary_set_inner 数组分支等价(界内写不触发 length 扩展、无 shadow
|
||||
// 同步)。越界写必须走原路径(CreateDataProperty + length 扩展),绝不可
|
||||
// 直写——set_prop_storage 对越界下标会落到 hash_props 属性区。
|
||||
if is_int_key(prop_name_si) {
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
if obj.is_array() && obj.array_elements_meta_vec().is_none() {
|
||||
let idx = int_key_value(prop_name_si);
|
||||
if idx < obj.array_prop_count {
|
||||
// promote 必须保留:obj 是 session 根、写入值可能是 epoch 对象,
|
||||
// 不 promote 下轮 GC 会悬垂。
|
||||
let value = self.promote_if_needed_for_write_ptr(obj_ptr, self.regs[b]);
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
obj.set_prop_storage(idx as usize, value);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 整数键恒不可能等于 "__proto__"(字符串 intern id 远小于整数键区间),
|
||||
// 短路免去每次 set 的 RwLock 查询。
|
||||
if !is_int_key(prop_name_si) && self.kernel_core.perm_interner().lookup(prop_name_si) == Some("__proto__") {
|
||||
let proto_value = self.promote_if_needed_for_write_ptr(obj_ptr, self.regs[b]);
|
||||
if self.is_object_prototype(obj_ptr) && !proto_value.is_null() {
|
||||
self.raise_type_error("Object.prototype.__proto__ is immutable")?;
|
||||
|
||||
@@ -308,6 +308,10 @@ pub struct Vm {
|
||||
pub(crate) frames: SmallVec<[CallFrame; 16]>,
|
||||
pub(crate) kernel_core: Arc<KernelCore>,
|
||||
pub(crate) session: KernelSession,
|
||||
/// `"length"` 属性键的 intern id 缓存:进程内稳定(PermInterner append-only、
|
||||
/// KernelCore 不重建),属性 get/set 热路径免每次 intern(hash64 + DashMap +
|
||||
/// RwLock 读锁)。
|
||||
pub(crate) length_si: u32,
|
||||
pub epoch: Epoch,
|
||||
pub object_prototype: P<JsObject>,
|
||||
/// `%GeneratorPrototype%`:生成器实例的原型(next/return/throw 方法挂此)。
|
||||
@@ -1003,7 +1007,7 @@ impl Vm {
|
||||
|
||||
pub(crate) fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option<JsValue> {
|
||||
vm_trace!("resolve_property: shape_id={} prop_name_si={}", obj.shape_id(), prop_name_si);
|
||||
let length_si = self.kernel_core.perm_interner().intern("length").0;
|
||||
let length_si = self.length_si;
|
||||
if obj.is_array() && prop_name_si == length_si {
|
||||
return Some(obj.logical_len_value());
|
||||
}
|
||||
@@ -1042,7 +1046,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
pub(crate) fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option<u32> {
|
||||
let length_si = self.kernel_core.perm_interner().intern("length").0;
|
||||
let length_si = self.length_si;
|
||||
if obj.is_array() && prop_name_si == length_si {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ impl Vm {
|
||||
&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue, target_reg: Option<u8>,
|
||||
) -> Result<JsValue, String> {
|
||||
vm_trace!("ordinary_get_inner: shape={} prop_si={}", obj.shape_id(), prop_name_si);
|
||||
let length_si = self.kernel_core.perm_interner().intern("length").0;
|
||||
let length_si = self.length_si;
|
||||
let mut current = Some(obj);
|
||||
let mut depth = 0usize;
|
||||
while let Some(obj) = current {
|
||||
@@ -186,7 +186,7 @@ impl Vm {
|
||||
// 数组 length 赋值:ArraySetLength 语义(ToUint32 + 调整元素区)。
|
||||
// 旧行为会把 length 存成影子命名属性,导致 `arr.length = N` 后
|
||||
// prop_count/迭代/内置方法看到的长度不一致。
|
||||
let length_si = self.kernel_core.perm_interner().intern("length").0;
|
||||
let length_si = self.length_si;
|
||||
if obj.is_array() && prop_name_si == length_si {
|
||||
let pc_before = self.pc;
|
||||
let number_len = self.coerce_number_bounded(val)?;
|
||||
|
||||
@@ -21,6 +21,9 @@ impl Vm {
|
||||
let core = KernelCore::new(KernelConfig::minimal());
|
||||
let mut session = KernelSession::new(&core);
|
||||
bindings::init_kernel_builtins(&core, &mut session);
|
||||
// 在 builtin 绑定之后取 id:绑定过程已 intern "length",此处命中缓存得到
|
||||
// 非 0 的稳定 id(保持枚举层"id 0 哨兵"的既有约定,见 walk_own_keys)。
|
||||
let length_si = core.perm_interner().intern("length").0;
|
||||
let obj_proto = P::clone(&session.builtin_world().object_proto);
|
||||
let mut vm = Self {
|
||||
regs: [JsValue::undefined(); 256],
|
||||
@@ -31,6 +34,7 @@ impl Vm {
|
||||
frames: smallvec::SmallVec::new(),
|
||||
kernel_core: core,
|
||||
session,
|
||||
length_si,
|
||||
epoch: Epoch::new(),
|
||||
object_prototype: obj_proto,
|
||||
generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
@@ -115,6 +119,9 @@ impl Vm {
|
||||
pub fn with_kernel_core(core: Arc<KernelCore>) -> Self {
|
||||
let mut session = KernelSession::new(&core);
|
||||
bindings::init_kernel_builtins(&core, &mut session);
|
||||
// 在 builtin 绑定之后取 id:绑定过程已 intern "length",此处命中缓存得到
|
||||
// 非 0 的稳定 id(保持枚举层"id 0 哨兵"的既有约定,见 walk_own_keys)。
|
||||
let length_si = core.perm_interner().intern("length").0;
|
||||
let obj_proto = P::clone(&session.builtin_world().object_proto);
|
||||
let mut vm = Self {
|
||||
regs: [JsValue::undefined(); 256],
|
||||
@@ -125,6 +132,7 @@ impl Vm {
|
||||
frames: smallvec::SmallVec::new(),
|
||||
kernel_core: core,
|
||||
session,
|
||||
length_si,
|
||||
epoch: Epoch::new(),
|
||||
object_prototype: obj_proto,
|
||||
generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
@@ -637,10 +645,7 @@ mod tests {
|
||||
// wrapper 是跨重置存活对象,其原型链必须仍能解析出 call/apply/bind:
|
||||
// 任何一条路径失效都说明 wrapper 原型指向了已释放的旧 Function 原型。
|
||||
assert_eq!(run_source(&mut vm, "Array.prototype.push.call([1], 2)"), JsValue::int(2));
|
||||
assert_eq!(
|
||||
run_source(&mut vm, "Array.prototype.push.apply([], [1, 2, 3])"),
|
||||
JsValue::int(3)
|
||||
);
|
||||
assert_eq!(run_source(&mut vm, "Array.prototype.push.apply([], [1, 2, 3])"), JsValue::int(3));
|
||||
let replaced = run_source(&mut vm, "String.prototype.replace.call('a', 'a', 'b')");
|
||||
assert_eq!(vm.lookup_str(replaced).as_deref(), Some("b"));
|
||||
let has = run_source(&mut vm, "Object.prototype.hasOwnProperty.call({x: 1}, 'x')");
|
||||
|
||||
Reference in New Issue
Block a user