fix: 数组 length 赋值 ArraySetLength 语义与超长逻辑长度(a.length 读取/收缩校验/空洞同步)

This commit is contained in:
2026-08-12 12:52:02 +08:00
parent e87515a27f
commit d4cd52ba90
3 changed files with 79 additions and 2 deletions
+1 -1
View File
@@ -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) {
+39 -1
View File
@@ -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 {