diff --git a/crates/oxide_builtins/src/regexp.rs b/crates/oxide_builtins/src/regexp.rs index febdd9a..be8bcb3 100644 --- a/crates/oxide_builtins/src/regexp.rs +++ b/crates/oxide_builtins/src/regexp.rs @@ -234,8 +234,13 @@ pub fn regexp_exec(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)); } diff --git a/crates/oxide_emit/src/expr/literal.rs b/crates/oxide_emit/src/expr/literal.rs index 7d00f47..a997280 100644 --- a/crates/oxide_emit/src/expr/literal.rs +++ b/crates/oxide_emit/src/expr/literal.rs @@ -36,7 +36,6 @@ impl Emitter { fn emit_string_literal_expression( &self, s: &oxide_parser::StringLiteral, ctx: &mut CompileCtx, ) -> Result { - 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)); diff --git a/crates/oxide_types/src/object.rs b/crates/oxide_types/src/object.rs index b279147..bef854c 100644 --- a/crates/oxide_types/src/object.rs +++ b/crates/oxide_types/src/object.rs @@ -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, 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) }; - 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()); } } + diff --git a/crates/oxide_vm/src/dispatch/property.rs b/crates/oxide_vm/src/dispatch/property.rs index 6090c6b..3dc4608 100644 --- a/crates/oxide_vm/src/dispatch/property.rs +++ b/crates/oxide_vm/src/dispatch/property.rs @@ -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) } } + diff --git a/crates/oxide_vm/src/ic_helper.rs b/crates/oxide_vm/src/ic_helper.rs index 977a9f3..62aafcb 100644 --- a/crates/oxide_vm/src/ic_helper.rs +++ b/crates/oxide_vm/src/ic_helper.rs @@ -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 } } + diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 0db54b9..d011c37 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -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)); } } + diff --git a/crates/oxide_vm/src/vm_props.rs b/crates/oxide_vm/src/vm_props.rs index b36b5f4..2478381 100644 --- a/crates/oxide_vm/src/vm_props.rs +++ b/crates/oxide_vm/src/vm_props.rs @@ -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() {