1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-08 05:27:14 +09:00
ladybird/Libraries/LibJS/Runtime/ArrayIterator.cpp
Aliaksandr Kalenik 285bc005cb LibJS: Do more comprehensive check if next() fast path is possible
Before this change each built-in iterator object has a boolean
`m_next_method_was_redefined`. If user code later changed the iterator’s
prototype (e.g. `Object.setPrototypeOf()`), we still believed the
built-in fast-path was safe and skipped the user supplied override,
producing wrong results.

With this change
`BuiltinIterator::as_builtin_iterator_if_next_is_not_redefined()` looks
up the current `next` property and verifies that it is still the
built-in native function.
2025-06-02 00:15:36 +02:00

157 lines
5.4 KiB
C++
Raw Permalink 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(IteratorRecord const& iterator_record)
{
if (iterator_record.next_method.is_object()) {
auto const& next_function = iterator_record.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_properties().has_index(index)) {
if (auto value = array.indexed_properties().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 {};
}
}