ladybird/Libraries/LibJS/Runtime/ArrayIterator.cpp
Andreas Kling 614713ed08 LibJS: Replace IndexedProperties with inline Packed/Holey/Dictionary
Replace the OwnPtr<IndexedPropertyStorage> indirection with inline
indexed element storage directly on Object. This eliminates virtual
dispatch and reduces indirection for indexed property access.

The new system uses three storage kinds tracked by IndexedStorageKind:

- Packed: Dense array, no holes. Elements stored in a malloced Value*
  array with capacity header (same layout as named properties).
- Holey: Dense array with possible holes marked by empty sentinel.
  Same physical layout as Packed.
- Dictionary: Sparse storage using GenericIndexedPropertyStorage,
  type-punned into the m_indexed_elements pointer.

Transitions: None->Packed->Holey->Dictionary (mostly monotonic).
Dictionary mode triggers on non-default attributes or sparse arrays.

Object keeps the same 48-byte size since m_indexed_elements (8 bytes)
replaces IndexedProperties (8 bytes), and the storage kind + array
size fit in existing padding alongside m_flags.

The asm interpreter benefits from one fewer indirection: it now reads
the element pointer and array size directly from Object fields instead
of chasing through OwnPtr -> IndexedPropertyStorage -> Vector.

Removes: IndexedProperties, SimpleIndexedPropertyStorage,
IndexedPropertyStorage, IndexedPropertyIterator.
Keeps: GenericIndexedPropertyStorage (for Dictionary mode).
2026-03-17 22:28:35 -05:00

157 lines
5.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/ArrayIterator.h>
#include <LibJS/Runtime/ArrayIteratorPrototype.h>
#include <LibJS/Runtime/TypedArray.h>
namespace JS {
GC_DEFINE_ALLOCATOR(ArrayIterator);
// 23.1.5.1 CreateArrayIterator ( array, kind ), https://tc39.es/ecma262/#sec-createarrayiterator
GC::Ref<ArrayIterator> ArrayIterator::create(Realm& realm, Value array, Object::PropertyKind iteration_kind)
{
// 1. Let iterator be OrdinaryObjectCreate(%ArrayIteratorPrototype%, « [[IteratedArrayLike]], [[ArrayLikeNextIndex]], [[ArrayLikeIterationKind]] »).
// 2. Set iterator.[[IteratedArrayLike]] to array.
// 3. Set iterator.[[ArrayLikeNextIndex]] to 0.
// 4. Set iterator.[[ArrayLikeIterationKind]] to kind.
// 5. Return iterator.
return realm.create<ArrayIterator>(array, iteration_kind, realm.intrinsics().array_iterator_prototype());
}
ArrayIterator::ArrayIterator(Value array, Object::PropertyKind iteration_kind, Object& prototype)
: Object(ConstructWithPrototypeTag::Tag, prototype)
, m_array(array)
, m_iteration_kind(iteration_kind)
{
}
void ArrayIterator::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_array);
}
BuiltinIterator* ArrayIterator::as_builtin_iterator_if_next_is_not_redefined(Value next_method)
{
if (next_method.is_object()) {
auto const& next_function = next_method.as_object();
if (next_function.is_native_function()) {
auto const& native_function = static_cast<NativeFunction const&>(next_function);
if (native_function.is_array_prototype_next_builtin())
return this;
}
}
return nullptr;
}
ThrowCompletionOr<void> ArrayIterator::next(VM& vm, bool& done, Value& value)
{
// 1. Let O be the this value.
// 2. If O is not an Object, throw a TypeError exception.
// 3. If O does not have all of the internal slots of an Array Iterator Instance (23.1.5.3), throw a TypeError exception.
// 4. Let array be O.[[IteratedArrayLike]].
auto target_array = m_array;
// 5. If array is undefined, return CreateIteratorResultObject(undefined, true).
if (target_array.is_undefined()) {
value = js_undefined();
done = true;
return {};
}
VERIFY(target_array.is_object());
auto& array = target_array.as_object();
// 6. Let index be O.[[ArrayLikeNextIndex]].
auto index = m_index;
// 7. Let kind be O.[[ArrayLikeIterationKind]].
auto kind = m_iteration_kind;
size_t length = 0;
// 8. If array has a [[TypedArrayName]] internal slot, then
if (array.is_typed_array()) {
auto& typed_array = static_cast<TypedArrayBase&>(array);
// a. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(array, SEQ-CST).
auto typed_array_record = make_typed_array_with_buffer_witness_record(typed_array, ArrayBuffer::SeqCst);
// b. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception.
if (is_typed_array_out_of_bounds(typed_array_record))
return vm.throw_completion<TypeError>(ErrorType::BufferOutOfBounds, "TypedArray"sv);
// c. Let len be TypedArrayLength(taRecord).
length = typed_array_length(typed_array_record);
}
// 9. Else,
else {
// a. Let len be ? LengthOfArrayLike(array).
length = TRY(length_of_array_like(vm, array));
}
// 10. If index ≥ len, then
if (index >= length) {
// a. Set O.[[IteratedArrayLike]] to undefined.
m_array = js_undefined();
// b. Return CreateIteratorResultObject(undefined, true).
value = js_undefined();
done = true;
return {};
}
// 11. Set O.[[ArrayLikeNextIndex]] to index + 1.
m_index++;
// 12. Let indexNumber be 𝔽(index).
Value result;
// 13. If kind is KEY, then
if (kind == PropertyKind::Key) {
// a. Let result be indexNumber.
result = Value { static_cast<i32>(index) };
}
// 14. Else,
else {
// a. Let elementKey be ! ToString(indexNumber).
// b. Let elementValue be ? Get(array, elementKey).
auto element_value = TRY([&]() -> ThrowCompletionOr<Value> {
// OPTIMIZATION: For objects that don't interfere with indexed property access, we try looking directly at storage.
if (!array.may_interfere_with_indexed_property_access() && array.indexed_has(index)) {
if (auto value = array.indexed_get(index)->value; !value.is_accessor())
return value;
}
return array.get(index);
}());
// c. If kind is VALUE, then
if (kind == PropertyKind::Value) {
// i. Let result be elementValue.
result = element_value;
}
// d. Else,
else {
// i. Assert: kind is KEY+VALUE.
VERIFY(kind == PropertyKind::KeyAndValue);
// ii. Let result be CreateArrayFromList(« indexNumber, elementValue »).
result = Array::create_from(*vm.current_realm(), { Value(static_cast<i32>(index)), element_value });
}
}
// 15. Return CreateIteratorResultObject(result, false).
value = result;
return {};
}
}