diff --git a/crates/oxide_builtins/src/array.rs b/crates/oxide_builtins/src/array.rs index 8ca688d..3730875 100644 --- a/crates/oxide_builtins/src/array.rs +++ b/crates/oxide_builtins/src/array.rs @@ -245,7 +245,7 @@ pub fn array_pop(vm: &mut H, args: &[u8]) -> NativeResult { return NativeResult::Ok(JsValue::undefined()); } let last = arr.get_prop_at(len - 1); - arr.set_prop_count(len - 1); + arr.set_prop_count_fast(len - 1); NativeResult::Ok(last) } @@ -335,7 +335,7 @@ pub fn array_splice(vm: &mut H, args: &[u8]) -> NativeResult { } let new_len = n + insert_count - delete_count; - arr.set_prop_count(new_len); + arr.set_prop_count_fast(new_len); let removed_arr = create_new_array(vm, removed.len()); unsafe { @@ -899,7 +899,7 @@ pub fn array_shift(vm: &mut H, args: &[u8]) -> NativeResult { let v = arr.get_prop_at(i); arr.set_prop_at(i - 1, v); } - arr.set_prop_count(len - 1); + arr.set_prop_count_fast(len - 1); NativeResult::Ok(first) } @@ -917,7 +917,7 @@ pub fn array_unshift(vm: &mut H, args: &[u8]) -> NativeResult { arr.set_prop_at(j, vm.reg(arg_reg)); } let new_len = len as usize + n_items; - arr.set_prop_count(new_len); + arr.set_prop_count_fast(new_len); NativeResult::Ok(JsValue::int(new_len as i32)) } diff --git a/crates/oxide_types/src/object.rs b/crates/oxide_types/src/object.rs index 0c324d3..e2845b6 100644 --- a/crates/oxide_types/src/object.rs +++ b/crates/oxide_types/src/object.rs @@ -571,6 +571,33 @@ impl JsObject { !self.prop_meta.is_null() } + /// Like `set_prop_count` but assumes `hash_props` is already allocated. + /// Caller must guarantee the object has at least one element (or was + /// created via `new_array`). Used in hot array builtins to skip the + /// redundant `ensure_hash_props` null check on each mutation. + #[inline] + pub fn set_prop_count_fast(&mut self, count: impl PropIndex) { + let target = count.to_u32() as usize; + // SAFETY: caller guarantees hash_props is non-null. + 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); + } + } + } + } + pub fn ensure_prop_meta(&mut self) -> &mut Vec> { if self.prop_meta.is_null() { let len = self.prop_vec_len();