mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: 数组 length 赋值 ArraySetLength 语义与超长逻辑长度(a.length 读取/收缩校验/空洞同步)
This commit is contained in:
@@ -399,6 +399,10 @@ pub struct JsObject {
|
||||
/// 数组元素数(数组对象)。普通对象恒 0。命名属性存储在 `hash_props`
|
||||
/// (与元素区分,JS 数组命名属性不影响 length)。
|
||||
pub array_prop_count: u32,
|
||||
/// 数组逻辑长度覆盖:仅当 length 超出 dense 存储上限 `MAX_DENSE_PROPS` 时非 0。
|
||||
/// `arr.length = 4294967295` 等超大长度时,元素区仍以 `MAX_DENSE_PROPS` 封顶,
|
||||
/// 超出部分视为稀疏空洞,逻辑长度单独记录供 `a.length` 读取。
|
||||
array_len_override: u32,
|
||||
native_fn: Option<NativeFnPtr>,
|
||||
sub_module_index: u32,
|
||||
_pad3: [u8; 4],
|
||||
@@ -576,6 +580,7 @@ impl JsObject {
|
||||
proto,
|
||||
generation: 1,
|
||||
array_prop_count: 0,
|
||||
array_len_override: 0,
|
||||
native_fn: None,
|
||||
sub_module_index: 0,
|
||||
_pad3: [0; 4],
|
||||
@@ -601,6 +606,7 @@ impl JsObject {
|
||||
proto,
|
||||
generation: 1,
|
||||
array_prop_count: 0,
|
||||
array_len_override: 0,
|
||||
native_fn: None,
|
||||
sub_module_index: 0,
|
||||
_pad3: [0; 4],
|
||||
@@ -611,6 +617,8 @@ impl JsObject {
|
||||
let vec = Box::new(vec![JsValue::undefined(); n_elements.min(MAX_DENSE_PROPS)]);
|
||||
obj.array_elements = Box::into_raw(vec) as *mut u8;
|
||||
obj.array_prop_count = n_elements.min(MAX_DENSE_PROPS) as u32;
|
||||
// 逻辑长度单独记录(超过 dense 上限时),供 a.length 读取。
|
||||
obj.array_len_override = if n_elements > MAX_DENSE_PROPS { n_elements as u32 } else { 0 };
|
||||
obj
|
||||
}
|
||||
|
||||
@@ -681,6 +689,7 @@ impl JsObject {
|
||||
proto: self.proto,
|
||||
generation: self.generation,
|
||||
array_prop_count: self.array_prop_count,
|
||||
array_len_override: self.array_len_override,
|
||||
native_fn: self.native_fn,
|
||||
sub_module_index: self.sub_module_index,
|
||||
_pad3: self._pad3,
|
||||
@@ -837,6 +846,36 @@ impl JsObject {
|
||||
}
|
||||
}
|
||||
|
||||
/// 数组逻辑长度:`a.length` 读取语义(含超出 dense 上限的覆盖值)。
|
||||
#[inline]
|
||||
pub fn logical_len(&self) -> u32 {
|
||||
if self.array_len_override != 0 {
|
||||
self.array_len_override
|
||||
} else {
|
||||
self.array_prop_count
|
||||
}
|
||||
}
|
||||
|
||||
/// 设置数组逻辑长度覆盖(仅在 length > `MAX_DENSE_PROPS` 时使用)。
|
||||
pub fn set_array_len_override(&mut self, len: u32) {
|
||||
self.array_len_override = len;
|
||||
}
|
||||
|
||||
/// 清除数组逻辑长度覆盖,恢复以 `array_prop_count` 为 length。
|
||||
pub fn clear_array_len_override(&mut self) {
|
||||
self.array_len_override = 0;
|
||||
}
|
||||
|
||||
/// 数组 length 属性值(`a.length` 读取结果)。
|
||||
pub fn logical_len_value(&self) -> JsValue {
|
||||
let l = self.logical_len();
|
||||
if l <= i32::MAX as u32 {
|
||||
JsValue::int(l as i32)
|
||||
} else {
|
||||
JsValue::float(l as f64)
|
||||
}
|
||||
}
|
||||
|
||||
/// 设置数组元素数 / 命名属性向量长度。截断或补 undefined 扩展。
|
||||
/// 数组对象只调整独立元素区(`array_elements` + `array_prop_count`),命名属性区
|
||||
/// (`hash_props`)零搬移——push 为摊销 O(1) 的 `Vec::push`。
|
||||
|
||||
@@ -934,7 +934,7 @@ impl Vm {
|
||||
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;
|
||||
if obj.is_array() && prop_name_si == length_si {
|
||||
return Some(JsValue::int(obj.prop_count() as i32));
|
||||
return Some(obj.logical_len_value());
|
||||
}
|
||||
if obj.is_array() {
|
||||
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
|
||||
|
||||
@@ -26,7 +26,7 @@ impl Vm {
|
||||
let mut depth = 0usize;
|
||||
while let Some(obj) = current {
|
||||
if obj.is_array() && prop_name_si == length_si {
|
||||
return Ok(JsValue::int(obj.prop_count() as i32));
|
||||
return Ok(obj.logical_len_value());
|
||||
}
|
||||
if obj.is_array() {
|
||||
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
|
||||
@@ -177,6 +177,44 @@ impl Vm {
|
||||
return oxide_builtins::typed_array::typed_array_element_set(self, obj, index, val);
|
||||
}
|
||||
}
|
||||
// 数组 length 赋值:ArraySetLength 语义(ToUint32 + 调整元素区)。
|
||||
// 旧行为会把 length 存成影子命名属性,导致 `arr.length = N` 后
|
||||
// prop_count/迭代/内置方法看到的长度不一致。
|
||||
let length_si = self.kernel_core.perm_interner().intern("length").0;
|
||||
if obj.is_array() && prop_name_si == length_si {
|
||||
let number_len = self.coerce_number_bounded(val)?;
|
||||
let raw_new_len = if number_len == 0.0 || !number_len.is_finite() {
|
||||
0
|
||||
} else {
|
||||
number_len.trunc().rem_euclid(4_294_967_296.0) as u32 as usize
|
||||
};
|
||||
// ToUint32 != ToNumber (e.g. 1.5 / NaN / Infinity / negative / 2**32) -> RangeError.
|
||||
if raw_new_len as f64 != number_len {
|
||||
return self.raise_error_kind("RangeError", "Invalid array length");
|
||||
}
|
||||
let old_count = obj.array_prop_count as usize;
|
||||
// Dense storage caps at MAX_DENSE_PROPS; larger lengths stay at the cap.
|
||||
let new_len_u = raw_new_len.min(oxide_types::object::MAX_DENSE_PROPS);
|
||||
obj.set_prop_count(new_len_u);
|
||||
// Grown slots are sparse holes: HasProperty / prototype reads must treat
|
||||
// them as absent.
|
||||
for idx in old_count..new_len_u {
|
||||
obj.mark_hole_at(idx);
|
||||
}
|
||||
// Logical length override: lengths beyond the dense cap are recorded
|
||||
// separately so `a.length` reads return the real value.
|
||||
if raw_new_len > oxide_types::object::MAX_DENSE_PROPS {
|
||||
obj.set_array_len_override(raw_new_len as u32);
|
||||
} else {
|
||||
obj.clear_array_len_override();
|
||||
}
|
||||
// 同步旧版可能残留的影子 length 属性(shape 槽),保证 IC 快路径读到新值。
|
||||
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), length_si) {
|
||||
let store_idx = obj.array_prop_count as usize + pos as usize;
|
||||
obj.set_prop_storage(store_idx, obj.logical_len_value());
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(pos) = self.get_own_property_slot(obj, prop_name_si) {
|
||||
if let Some(meta) = obj.prop_meta_at(pos) {
|
||||
if meta.is_accessor {
|
||||
|
||||
Reference in New Issue
Block a user