mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
fix: 数组属性存储机制(array_prop_count 显式元素数 + shape 属性偏移 + IC/prop_forge 对齐)
This commit is contained in:
@@ -234,8 +234,13 @@ pub fn regexp_exec<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
(*arr).set_prop_count(n);
|
||||
}
|
||||
|
||||
// ponytail: 引擎数组的 shape 属性槽与元素 prop_vec 共用 hash_props 索引,
|
||||
// 设 index/input/groups 属性会与元素冲突/膨胀 length。暂只填元素。
|
||||
let index_si = vm.kernel_core().perm_interner().intern("index").0;
|
||||
vm.set_or_create_prop_value(unsafe { &mut *arr }, index_si, JsValue::int(range.start as i32));
|
||||
let input_si = vm.kernel_core().perm_interner().intern("input").0;
|
||||
let haystack_val = vm.new_string(&haystack);
|
||||
vm.set_or_create_prop_value(unsafe { &mut *arr }, input_si, haystack_val);
|
||||
let groups_si = vm.kernel_core().perm_interner().intern("groups").0;
|
||||
vm.set_or_create_prop_value(unsafe { &mut *arr }, groups_si, JsValue::undefined());
|
||||
if is_global {
|
||||
set_prop_at(re_ptr, 0, JsValue::int(range.end as i32));
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ impl Emitter {
|
||||
fn emit_string_literal_expression(
|
||||
&self, s: &oxide_parser::StringLiteral, ctx: &mut CompileCtx,
|
||||
) -> Result<u32, String> {
|
||||
eprintln!("[DBG] string literal value={:?}", s.value.as_str());
|
||||
let idx = ctx.add_constant(Constant::String(s.value.to_string()));
|
||||
let r = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(r), idx));
|
||||
|
||||
@@ -329,7 +329,9 @@ pub struct JsObject {
|
||||
native_data: *mut u8,
|
||||
proto: JsValue,
|
||||
generation: u32,
|
||||
_pad2: [u8; 4],
|
||||
/// 数组元素数(数组对象)。普通对象恒 0。属性(shape 槽位)存储偏移
|
||||
/// `array_prop_count + 槽位`,与元素区分(JS 数组属性不影响 length)。
|
||||
pub array_prop_count: u32,
|
||||
native_fn: Option<NativeFnPtr>,
|
||||
sub_module_index: u32,
|
||||
_pad3: [u8; 4],
|
||||
@@ -418,7 +420,7 @@ impl JsObject {
|
||||
native_data: std::ptr::null_mut(),
|
||||
proto,
|
||||
generation: 1,
|
||||
_pad2: [0; 4],
|
||||
array_prop_count: 0,
|
||||
native_fn: None,
|
||||
sub_module_index: 0,
|
||||
_pad3: [0; 4],
|
||||
@@ -441,7 +443,7 @@ impl JsObject {
|
||||
native_data: std::ptr::null_mut(),
|
||||
proto,
|
||||
generation: 1,
|
||||
_pad2: [0; 4],
|
||||
array_prop_count: 0,
|
||||
native_fn: None,
|
||||
sub_module_index: 0,
|
||||
_pad3: [0; 4],
|
||||
@@ -451,6 +453,7 @@ impl JsObject {
|
||||
};
|
||||
let vec = Box::new(vec![JsValue::undefined(); n_elements.min(MAX_DENSE_PROPS)]);
|
||||
obj.hash_props = Box::into_raw(vec) as *mut u8;
|
||||
obj.array_prop_count = n_elements as u32;
|
||||
obj
|
||||
}
|
||||
|
||||
@@ -510,7 +513,7 @@ impl JsObject {
|
||||
native_data: self.native_data,
|
||||
proto: self.proto,
|
||||
generation: self.generation,
|
||||
_pad2: self._pad2,
|
||||
array_prop_count: self.array_prop_count,
|
||||
native_fn: self.native_fn,
|
||||
sub_module_index: self.sub_module_index,
|
||||
_pad3: self._pad3,
|
||||
@@ -627,6 +630,9 @@ impl JsObject {
|
||||
/// 返回 hash_props vec 的长度作为属性数。
|
||||
/// hash_props 未分配时返回 0。
|
||||
pub fn prop_count(&self) -> u32 {
|
||||
if self.is_array() {
|
||||
return self.array_prop_count;
|
||||
}
|
||||
if self.hash_props.is_null() {
|
||||
0
|
||||
} else {
|
||||
@@ -638,9 +644,21 @@ impl JsObject {
|
||||
}
|
||||
|
||||
/// 设置 hash_props vec 的长度。截断或补 undefined 扩展。
|
||||
/// 数组对象只调整元素区(`array_prop_count`),属性区(尾部)整体搬移保持对齐。
|
||||
pub fn set_prop_count(&mut self, count: impl PropIndex) {
|
||||
let target = count.to_u32() as usize;
|
||||
{
|
||||
if self.is_array() {
|
||||
let old = self.array_prop_count as usize;
|
||||
let vec = self.ensure_hash_props();
|
||||
if target > old {
|
||||
for _ in old..target {
|
||||
vec.insert(old, JsValue::undefined());
|
||||
}
|
||||
} else if target < old {
|
||||
vec.drain(target..old);
|
||||
}
|
||||
self.array_prop_count = target as u32;
|
||||
} else {
|
||||
let vec = self.ensure_hash_props();
|
||||
if target < vec.len() {
|
||||
vec.truncate(target);
|
||||
@@ -671,25 +689,7 @@ impl JsObject {
|
||||
/// 用于热路径数组 builtin,跳过每次变更时冗余的 `ensure_hash_props` 空检查。
|
||||
#[inline]
|
||||
pub fn set_prop_count_fast(&mut self, count: impl PropIndex) {
|
||||
let target = count.to_u32() as usize;
|
||||
// SAFETY: 调用方保证 hash_props 非空。
|
||||
let vec = unsafe { &mut *(self.hash_props as *mut Vec<JsValue>) };
|
||||
if target < vec.len() {
|
||||
vec.truncate(target);
|
||||
} else {
|
||||
while vec.len() < target {
|
||||
vec.push(JsValue::undefined());
|
||||
}
|
||||
}
|
||||
if let Some(meta) = self.prop_meta_vec_mut() {
|
||||
if target < meta.len() {
|
||||
meta.truncate(target);
|
||||
} else {
|
||||
while meta.len() < target {
|
||||
meta.push(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.set_prop_count(count);
|
||||
}
|
||||
|
||||
/// 确保属性元数据向量已分配并返回可变引用。
|
||||
@@ -893,6 +893,7 @@ impl JsObject {
|
||||
}
|
||||
|
||||
/// 设置下标 position 处的属性值。vec 按需自动扩容。
|
||||
/// 数组对象元素写入会更新 `array_prop_count`(元素数随最高索引增长)。
|
||||
pub fn set_prop_at(&mut self, position: impl PropIndex, val: JsValue) {
|
||||
let pos = position.to_u32() as usize;
|
||||
if pos > MAX_DENSE_PROPS {
|
||||
@@ -914,6 +915,40 @@ impl JsObject {
|
||||
meta.push(None);
|
||||
}
|
||||
}
|
||||
if self.is_array() && pos >= self.array_prop_count as usize {
|
||||
self.array_prop_count = (pos + 1) as u32;
|
||||
}
|
||||
}
|
||||
|
||||
/// 数组对象(shape 槽位 → 存储索引 = `array_prop_count + 槽位`)的
|
||||
/// 属性写入;普通对象等价 `set_prop_at`。
|
||||
pub fn set_prop_shape(&mut self, shape_pos: u32, val: JsValue) {
|
||||
let idx = if self.is_array() {
|
||||
self.array_prop_count as usize + shape_pos as usize
|
||||
} else {
|
||||
shape_pos as usize
|
||||
};
|
||||
let vec = self.ensure_hash_props();
|
||||
while vec.len() <= idx {
|
||||
vec.push(JsValue::undefined());
|
||||
}
|
||||
vec[idx] = val;
|
||||
if let Some(meta) = self.prop_meta_vec_mut() {
|
||||
while meta.len() <= idx {
|
||||
meta.push(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 数组对象属性读取(shape 槽位 → 存储索引 = `array_prop_count + 槽位`);
|
||||
/// 普通对象等价 `get_prop_at`。越界返回 undefined。
|
||||
pub fn get_prop_shape(&self, shape_pos: u32) -> JsValue {
|
||||
let idx = if self.is_array() {
|
||||
self.array_prop_count as usize + shape_pos as usize
|
||||
} else {
|
||||
shape_pos as usize
|
||||
};
|
||||
self.get_prop_at(idx)
|
||||
}
|
||||
|
||||
/// 把值压入 hash_props vec,返回其下标。
|
||||
@@ -1265,3 +1300,4 @@ mod tests {
|
||||
assert_eq!(obj.prop_meta_vec().expect("meta").len(), obj.prop_vec_len());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -170,8 +170,7 @@ impl Vm {
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
let prop_name_si = self.property_key_si(self.regs[b]);
|
||||
let (cached_shape_id, cached_slot, cached_depth) = ic_helper::read_ic_entry(&self.bytecode, &mut self.pc);
|
||||
if obj.has_prop_meta() {
|
||||
let val = self.ordinary_get_with_target(obj, prop_name_si, val, a as u8)?;
|
||||
if obj.has_prop_meta() { let val = self.ordinary_get_with_target(obj, prop_name_si, val, a as u8)?;
|
||||
if self.accessor_frame_target_reg.take().is_none() {
|
||||
self.regs[a] = val;
|
||||
}
|
||||
@@ -194,7 +193,7 @@ impl Vm {
|
||||
prop_name_si,
|
||||
template.position
|
||||
);
|
||||
self.regs[a] = obj.get_prop_at(template.position);
|
||||
self.regs[a] = obj.get_prop_shape(template.position);
|
||||
} else {
|
||||
ic_debug!("IC_GET miss shape={} prop={}", obj.shape_id(), prop_name_si);
|
||||
self.regs[a] = self.ordinary_get(obj, prop_name_si, val)?;
|
||||
@@ -445,3 +444,4 @@ impl Vm {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,9 @@ pub(crate) fn ic_get_hit(obj: &JsObject, shape_id: u32, slot_index: u32, proto_d
|
||||
}
|
||||
if proto_depth == 0 {
|
||||
if obj.shape_id() == shape_id && slot_index < obj.prop_vec_len() as u32 {
|
||||
return Some(obj.get_prop_at(slot_index));
|
||||
let v = obj.get_prop_shape(slot_index);
|
||||
eprintln!("[DBG] ic_get_hit shape={} slot={} apc={} v={:?}", shape_id, slot_index, obj.array_prop_count, v);
|
||||
return Some(v);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
@@ -95,7 +97,7 @@ pub(crate) fn ic_get_hit(obj: &JsObject, shape_id: u32, slot_index: u32, proto_d
|
||||
}
|
||||
let target_ref = unsafe { &*target };
|
||||
if target_ref.shape_id() == shape_id && slot_index < target_ref.prop_vec_len() as u32 {
|
||||
Some(target_ref.get_prop_at(slot_index))
|
||||
Some(target_ref.get_prop_shape(slot_index))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -108,7 +110,7 @@ pub(crate) fn ic_set_hit(obj: &mut JsObject, shape_id: u32, slot_index: u32, pro
|
||||
}
|
||||
if proto_depth == 0 {
|
||||
if obj.shape_id() == shape_id && slot_index < obj.prop_vec_len() as u32 {
|
||||
obj.set_prop_at(slot_index, value);
|
||||
obj.set_prop_shape(slot_index, value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -119,9 +121,10 @@ pub(crate) fn ic_set_hit(obj: &mut JsObject, shape_id: u32, slot_index: u32, pro
|
||||
}
|
||||
let target_ref = unsafe { &*target };
|
||||
if target_ref.shape_id() == shape_id && slot_index < target_ref.prop_vec_len() as u32 {
|
||||
unsafe { &mut *(target as *mut JsObject) }.set_prop_at(slot_index, value);
|
||||
unsafe { &mut *(target as *mut JsObject) }.set_prop_shape(slot_index, value);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -700,11 +700,23 @@ impl Vm {
|
||||
.shape_forge()
|
||||
.lookup_position(obj.shape_id(), prop_name_si)
|
||||
.and_then(|pos| {
|
||||
let val = obj.get_prop_at(pos);
|
||||
if !val.is_undefined() || obj.prop_vec_len() > pos as usize {
|
||||
Some(pos)
|
||||
if obj.is_array() {
|
||||
// 数组属性存储索引 = array_prop_count + shape 槽位(与元素区分)。
|
||||
let idx = obj.array_prop_count as usize + pos as usize;
|
||||
let val = obj.get_prop_at(idx);
|
||||
eprintln!("[DBG] gos array prop pos={} apc={} idx={} val={:?}", pos, obj.array_prop_count, idx, val);
|
||||
if !val.is_undefined() {
|
||||
Some(idx as u32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
let val = obj.get_prop_at(pos);
|
||||
if !val.is_undefined() || obj.prop_vec_len() > pos as usize {
|
||||
Some(pos)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1805,3 +1817,4 @@ mod tests {
|
||||
assert_eq!(vm.reg(255), JsValue::int(13));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -309,10 +309,11 @@ impl Vm {
|
||||
pub(crate) fn set_or_create_prop_value(&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue) {
|
||||
vm_trace!("set_or_create_prop_value: shape_id={} prop_name_si={}", obj.shape_id(), prop_name_si);
|
||||
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
obj.set_prop_at(pos, val);
|
||||
obj.set_prop_shape(pos, val);
|
||||
} else {
|
||||
let new_shape_id = self.kernel_core.shape_forge().make_shape(obj.shape_id(), prop_name_si);
|
||||
obj.set_shape_id(new_shape_id);
|
||||
// 数组对象:属性追加到 hash_props 属性区(元素之后),array_prop_count 不变。
|
||||
obj.push_prop(val);
|
||||
obj.bump_generation();
|
||||
}
|
||||
@@ -324,11 +325,16 @@ impl Vm {
|
||||
vm_trace!("define_data_property: shape={} prop_si={}", obj.shape_id(), prop_name_si);
|
||||
let val = self.promote_if_needed_for_write_ptr(obj as *mut JsObject, val);
|
||||
let pos = if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
pos
|
||||
// shape 槽位 → 存储索引(数组属性在元素区之后)。
|
||||
if obj.is_array() {
|
||||
obj.array_prop_count as usize + pos as usize
|
||||
} else {
|
||||
pos as usize
|
||||
}
|
||||
} else {
|
||||
let new_shape_id = self.kernel_core.shape_forge().make_shape(obj.shape_id(), prop_name_si);
|
||||
obj.set_shape_id(new_shape_id);
|
||||
obj.push_prop(JsValue::undefined())
|
||||
obj.push_prop(JsValue::undefined()) as usize
|
||||
};
|
||||
if let Some(current) = obj.prop_meta_at(pos) {
|
||||
if !current.attributes.configurable() {
|
||||
|
||||
Reference in New Issue
Block a user