mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-24 18:02:20 +00:00
LibJS: Allow advancing built-in iterators without result object creation
Expose a method on built-in iterators that allows retrieving the next iteration result without allocating a JS::Object. This change is a preparation for optimizing for..of and for..in loops.
This commit is contained in:
parent
c72d5943e6
commit
ab52d86a69
Notes:
github-actions[bot]
2025-04-30 18:52:44 +00:00
Author: https://github.com/kalenikaliaksandr
Commit: ab52d86a69
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4533
15 changed files with 234 additions and 169 deletions
|
@ -158,6 +158,7 @@ class Agent;
|
||||||
struct AsyncGeneratorRequest;
|
struct AsyncGeneratorRequest;
|
||||||
class BigInt;
|
class BigInt;
|
||||||
class BoundFunction;
|
class BoundFunction;
|
||||||
|
class BuiltinIterator;
|
||||||
struct CachedSourceRange;
|
struct CachedSourceRange;
|
||||||
class Cell;
|
class Cell;
|
||||||
class ClassExpression;
|
class ClassExpression;
|
||||||
|
|
|
@ -4,8 +4,9 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <LibJS/Runtime/Array.h>
|
||||||
#include <LibJS/Runtime/ArrayIterator.h>
|
#include <LibJS/Runtime/ArrayIterator.h>
|
||||||
#include <LibJS/Runtime/GlobalObject.h>
|
#include <LibJS/Runtime/TypedArray.h>
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
|
||||||
|
@ -35,4 +36,108 @@ void ArrayIterator::visit_edges(Cell::Visitor& visitor)
|
||||||
visitor.visit(m_array);
|
visitor.visit(m_array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,13 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibJS/Runtime/Iterator.h>
|
||||||
#include <LibJS/Runtime/Object.h>
|
#include <LibJS/Runtime/Object.h>
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
|
||||||
class ArrayIterator final : public Object {
|
class ArrayIterator final : public Object
|
||||||
|
, public BuiltinIterator {
|
||||||
JS_OBJECT(ArrayIterator, Object);
|
JS_OBJECT(ArrayIterator, Object);
|
||||||
GC_DECLARE_ALLOCATOR(ArrayIterator);
|
GC_DECLARE_ALLOCATOR(ArrayIterator);
|
||||||
|
|
||||||
|
@ -19,13 +21,8 @@ public:
|
||||||
|
|
||||||
virtual ~ArrayIterator() override = default;
|
virtual ~ArrayIterator() override = default;
|
||||||
|
|
||||||
Value array() const { return m_array; }
|
BuiltinIterator* as_builtin_iterator() override { return this; }
|
||||||
void set_array(Value array) { m_array = array; }
|
ThrowCompletionOr<void> next(VM&, bool& done, Value& value) override;
|
||||||
|
|
||||||
Object::PropertyKind iteration_kind() const { return m_iteration_kind; }
|
|
||||||
|
|
||||||
size_t index() const { return m_index; }
|
|
||||||
void set_index(size_t index) { m_index = index; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ArrayIterator(Value array, Object::PropertyKind iteration_kind, Object& prototype);
|
ArrayIterator(Value array, Object::PropertyKind iteration_kind, Object& prototype);
|
||||||
|
|
|
@ -35,103 +35,13 @@ void ArrayIteratorPrototype::initialize(Realm& realm)
|
||||||
// 23.1.5.2.1 %ArrayIteratorPrototype%.next ( ), https://tc39.es/ecma262/#sec-%arrayiteratorprototype%.next
|
// 23.1.5.2.1 %ArrayIteratorPrototype%.next ( ), https://tc39.es/ecma262/#sec-%arrayiteratorprototype%.next
|
||||||
JS_DEFINE_NATIVE_FUNCTION(ArrayIteratorPrototype::next)
|
JS_DEFINE_NATIVE_FUNCTION(ArrayIteratorPrototype::next)
|
||||||
{
|
{
|
||||||
auto& realm = *vm.current_realm();
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
auto iterator = TRY(typed_this_value(vm));
|
auto iterator = TRY(typed_this_value(vm));
|
||||||
|
|
||||||
// 4. Let array be O.[[IteratedArrayLike]].
|
Value value;
|
||||||
auto target_array = iterator->array();
|
bool done = false;
|
||||||
|
TRY(iterator->next(vm, done, value));
|
||||||
|
|
||||||
// 5. If array is undefined, return CreateIteratorResultObject(undefined, true).
|
return create_iterator_result_object(vm, value, done);
|
||||||
if (target_array.is_undefined())
|
|
||||||
return create_iterator_result_object(vm, js_undefined(), true);
|
|
||||||
|
|
||||||
VERIFY(target_array.is_object());
|
|
||||||
auto& array = target_array.as_object();
|
|
||||||
|
|
||||||
// 6. Let index be O.[[ArrayLikeNextIndex]].
|
|
||||||
auto index = iterator->index();
|
|
||||||
|
|
||||||
// 7. Let kind be O.[[ArrayLikeIterationKind]].
|
|
||||||
auto kind = iterator->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.
|
|
||||||
iterator->set_array(js_undefined());
|
|
||||||
|
|
||||||
// b. Return CreateIteratorResultObject(undefined, true).
|
|
||||||
return create_iterator_result_object(vm, js_undefined(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 11. Set O.[[ArrayLikeNextIndex]] to index + 1.
|
|
||||||
iterator->set_index(index + 1);
|
|
||||||
|
|
||||||
// 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(realm, { Value(static_cast<i32>(index)), element_value });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 15. Return CreateIteratorResultObject(result, false).
|
|
||||||
return create_iterator_result_object(vm, result, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,12 @@ enum class PrimitiveHandling {
|
||||||
RejectPrimitives,
|
RejectPrimitives,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class BuiltinIterator {
|
||||||
|
public:
|
||||||
|
virtual ~BuiltinIterator() = default;
|
||||||
|
virtual ThrowCompletionOr<void> next(VM&, bool& done, Value& value) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
// 7.4.12 IfAbruptCloseIterator ( value, iteratorRecord ), https://tc39.es/ecma262/#sec-ifabruptcloseiterator
|
// 7.4.12 IfAbruptCloseIterator ( value, iteratorRecord ), https://tc39.es/ecma262/#sec-ifabruptcloseiterator
|
||||||
#define TRY_OR_CLOSE_ITERATOR(vm, iterator_record, expression) \
|
#define TRY_OR_CLOSE_ITERATOR(vm, iterator_record, expression) \
|
||||||
({ \
|
({ \
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <LibJS/Runtime/GlobalObject.h>
|
#include <LibJS/Runtime/Array.h>
|
||||||
#include <LibJS/Runtime/MapIterator.h>
|
#include <LibJS/Runtime/MapIterator.h>
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
@ -30,4 +30,34 @@ void MapIterator::visit_edges(Cell::Visitor& visitor)
|
||||||
visitor.visit(m_map);
|
visitor.visit(m_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThrowCompletionOr<void> MapIterator::next(VM& vm, bool& done, Value& value)
|
||||||
|
{
|
||||||
|
if (m_done) {
|
||||||
|
done = true;
|
||||||
|
value = js_undefined();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_iterator.is_end()) {
|
||||||
|
m_done = true;
|
||||||
|
done = true;
|
||||||
|
value = js_undefined();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto entry = *m_iterator;
|
||||||
|
++m_iterator;
|
||||||
|
if (m_iteration_kind == Object::PropertyKind::Key) {
|
||||||
|
value = entry.key;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (m_iteration_kind == Object::PropertyKind::Value) {
|
||||||
|
value = entry.value;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
value = Array::create_from(*vm.current_realm(), { entry.key, entry.value });
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,14 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibJS/Runtime/Iterator.h>
|
||||||
#include <LibJS/Runtime/Map.h>
|
#include <LibJS/Runtime/Map.h>
|
||||||
#include <LibJS/Runtime/Object.h>
|
#include <LibJS/Runtime/Object.h>
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
|
||||||
class MapIterator final : public Object {
|
class MapIterator final : public Object
|
||||||
|
, public BuiltinIterator {
|
||||||
JS_OBJECT(MapIterator, Object);
|
JS_OBJECT(MapIterator, Object);
|
||||||
GC_DECLARE_ALLOCATOR(MapIterator);
|
GC_DECLARE_ALLOCATOR(MapIterator);
|
||||||
|
|
||||||
|
@ -20,9 +22,8 @@ public:
|
||||||
|
|
||||||
virtual ~MapIterator() override = default;
|
virtual ~MapIterator() override = default;
|
||||||
|
|
||||||
Map& map() const { return m_map; }
|
BuiltinIterator* as_builtin_iterator() override { return this; }
|
||||||
bool done() const { return m_done; }
|
ThrowCompletionOr<void> next(VM&, bool& done, Value& value) override;
|
||||||
Object::PropertyKind iteration_kind() const { return m_iteration_kind; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class MapIteratorPrototype;
|
friend class MapIteratorPrototype;
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
|
|
||||||
#include <AK/TypeCasts.h>
|
#include <AK/TypeCasts.h>
|
||||||
#include <LibJS/Runtime/Array.h>
|
#include <LibJS/Runtime/Array.h>
|
||||||
#include <LibJS/Runtime/Error.h>
|
|
||||||
#include <LibJS/Runtime/GlobalObject.h>
|
|
||||||
#include <LibJS/Runtime/Iterator.h>
|
#include <LibJS/Runtime/Iterator.h>
|
||||||
#include <LibJS/Runtime/MapIteratorPrototype.h>
|
#include <LibJS/Runtime/MapIteratorPrototype.h>
|
||||||
|
|
||||||
|
@ -32,27 +30,13 @@ void MapIteratorPrototype::initialize(Realm& realm)
|
||||||
// 24.1.5.2.1 %MapIteratorPrototype%.next ( ), https://tc39.es/ecma262/#sec-%mapiteratorprototype%.next
|
// 24.1.5.2.1 %MapIteratorPrototype%.next ( ), https://tc39.es/ecma262/#sec-%mapiteratorprototype%.next
|
||||||
JS_DEFINE_NATIVE_FUNCTION(MapIteratorPrototype::next)
|
JS_DEFINE_NATIVE_FUNCTION(MapIteratorPrototype::next)
|
||||||
{
|
{
|
||||||
auto& realm = *vm.current_realm();
|
auto iterator = TRY(typed_this_value(vm));
|
||||||
|
|
||||||
auto map_iterator = TRY(typed_this_value(vm));
|
Value value;
|
||||||
if (map_iterator->done())
|
bool done = false;
|
||||||
return create_iterator_result_object(vm, js_undefined(), true);
|
TRY(iterator->next(vm, done, value));
|
||||||
|
|
||||||
if (map_iterator->m_iterator.is_end()) {
|
return create_iterator_result_object(vm, value, done);
|
||||||
map_iterator->m_done = true;
|
|
||||||
return create_iterator_result_object(vm, js_undefined(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto iteration_kind = map_iterator->iteration_kind();
|
|
||||||
|
|
||||||
auto entry = *map_iterator->m_iterator;
|
|
||||||
++map_iterator->m_iterator;
|
|
||||||
if (iteration_kind == Object::PropertyKind::Key)
|
|
||||||
return create_iterator_result_object(vm, entry.key, false);
|
|
||||||
if (iteration_kind == Object::PropertyKind::Value)
|
|
||||||
return create_iterator_result_object(vm, entry.value, false);
|
|
||||||
|
|
||||||
return create_iterator_result_object(vm, Array::create_from(realm, { entry.key, entry.value }), false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -212,6 +212,8 @@ public:
|
||||||
virtual bool is_array_iterator() const { return false; }
|
virtual bool is_array_iterator() const { return false; }
|
||||||
virtual bool is_raw_json_object() const { return false; }
|
virtual bool is_raw_json_object() const { return false; }
|
||||||
|
|
||||||
|
virtual BuiltinIterator* as_builtin_iterator() { return nullptr; }
|
||||||
|
|
||||||
// B.3.7 The [[IsHTMLDDA]] Internal Slot, https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot
|
// B.3.7 The [[IsHTMLDDA]] Internal Slot, https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot
|
||||||
virtual bool is_htmldda() const { return false; }
|
virtual bool is_htmldda() const { return false; }
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <LibJS/Runtime/GlobalObject.h>
|
#include <LibJS/Runtime/Array.h>
|
||||||
#include <LibJS/Runtime/SetIterator.h>
|
#include <LibJS/Runtime/SetIterator.h>
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
@ -30,4 +30,31 @@ void SetIterator::visit_edges(Cell::Visitor& visitor)
|
||||||
visitor.visit(m_set);
|
visitor.visit(m_set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThrowCompletionOr<void> SetIterator::next(VM& vm, bool& done, Value& value)
|
||||||
|
{
|
||||||
|
if (m_done) {
|
||||||
|
done = true;
|
||||||
|
value = js_undefined();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_iterator == m_set->end()) {
|
||||||
|
m_done = true;
|
||||||
|
done = true;
|
||||||
|
value = js_undefined();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
VERIFY(m_iteration_kind != Object::PropertyKind::Key);
|
||||||
|
|
||||||
|
value = (*m_iterator).key;
|
||||||
|
++m_iterator;
|
||||||
|
if (m_iteration_kind == Object::PropertyKind::Value) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
value = Array::create_from(*vm.current_realm(), { value, value });
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,14 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/HashTable.h>
|
#include <LibJS/Runtime/Iterator.h>
|
||||||
#include <LibJS/Runtime/Object.h>
|
#include <LibJS/Runtime/Object.h>
|
||||||
#include <LibJS/Runtime/Set.h>
|
#include <LibJS/Runtime/Set.h>
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
|
||||||
class SetIterator final : public Object {
|
class SetIterator final : public Object
|
||||||
|
, public BuiltinIterator {
|
||||||
JS_OBJECT(SetIterator, Object);
|
JS_OBJECT(SetIterator, Object);
|
||||||
GC_DECLARE_ALLOCATOR(SetIterator);
|
GC_DECLARE_ALLOCATOR(SetIterator);
|
||||||
|
|
||||||
|
@ -21,9 +22,8 @@ public:
|
||||||
|
|
||||||
virtual ~SetIterator() override = default;
|
virtual ~SetIterator() override = default;
|
||||||
|
|
||||||
Set& set() const { return m_set; }
|
BuiltinIterator* as_builtin_iterator() override { return this; }
|
||||||
bool done() const { return m_done; }
|
ThrowCompletionOr<void> next(VM&, bool& done, Value& value) override;
|
||||||
Object::PropertyKind iteration_kind() const { return m_iteration_kind; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class SetIteratorPrototype;
|
friend class SetIteratorPrototype;
|
||||||
|
|
|
@ -34,27 +34,13 @@ void SetIteratorPrototype::initialize(Realm& realm)
|
||||||
// 24.2.5.2.1 %SetIteratorPrototype%.next ( ), https://tc39.es/ecma262/#sec-%setiteratorprototype%.next
|
// 24.2.5.2.1 %SetIteratorPrototype%.next ( ), https://tc39.es/ecma262/#sec-%setiteratorprototype%.next
|
||||||
JS_DEFINE_NATIVE_FUNCTION(SetIteratorPrototype::next)
|
JS_DEFINE_NATIVE_FUNCTION(SetIteratorPrototype::next)
|
||||||
{
|
{
|
||||||
auto& realm = *vm.current_realm();
|
auto iterator = TRY(typed_this_value(vm));
|
||||||
|
|
||||||
auto set_iterator = TRY(typed_this_value(vm));
|
Value value;
|
||||||
if (set_iterator->done())
|
bool done = false;
|
||||||
return create_iterator_result_object(vm, js_undefined(), true);
|
TRY(iterator->next(vm, done, value));
|
||||||
|
|
||||||
auto& set = set_iterator->set();
|
return create_iterator_result_object(vm, value, done);
|
||||||
if (set_iterator->m_iterator == set.end()) {
|
|
||||||
set_iterator->m_done = true;
|
|
||||||
return create_iterator_result_object(vm, js_undefined(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto iteration_kind = set_iterator->iteration_kind();
|
|
||||||
VERIFY(iteration_kind != Object::PropertyKind::Key);
|
|
||||||
|
|
||||||
auto value = (*set_iterator->m_iterator).key;
|
|
||||||
++set_iterator->m_iterator;
|
|
||||||
if (iteration_kind == Object::PropertyKind::Value)
|
|
||||||
return create_iterator_result_object(vm, value, false);
|
|
||||||
|
|
||||||
return create_iterator_result_object(vm, Array::create_from(realm, { value, value }), false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,4 +24,26 @@ StringIterator::StringIterator(String string, Object& prototype)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThrowCompletionOr<void> StringIterator::next(VM& vm, bool& done, Value& value)
|
||||||
|
{
|
||||||
|
if (m_done) {
|
||||||
|
done = true;
|
||||||
|
value = js_undefined();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_iterator.done()) {
|
||||||
|
m_done = true;
|
||||||
|
done = true;
|
||||||
|
value = js_undefined();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto code_point = String::from_code_point(*m_iterator);
|
||||||
|
++m_iterator;
|
||||||
|
|
||||||
|
value = PrimitiveString::create(vm, move(code_point));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,13 @@
|
||||||
|
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/Utf8View.h>
|
#include <AK/Utf8View.h>
|
||||||
|
#include <LibJS/Runtime/Iterator.h>
|
||||||
#include <LibJS/Runtime/Object.h>
|
#include <LibJS/Runtime/Object.h>
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
|
||||||
class StringIterator final : public Object {
|
class StringIterator final : public Object
|
||||||
|
, public BuiltinIterator {
|
||||||
JS_OBJECT(StringIterator, Object);
|
JS_OBJECT(StringIterator, Object);
|
||||||
GC_DECLARE_ALLOCATOR(StringIterator);
|
GC_DECLARE_ALLOCATOR(StringIterator);
|
||||||
|
|
||||||
|
@ -21,8 +23,8 @@ public:
|
||||||
|
|
||||||
virtual ~StringIterator() override = default;
|
virtual ~StringIterator() override = default;
|
||||||
|
|
||||||
Utf8CodePointIterator& iterator() { return m_iterator; }
|
BuiltinIterator* as_builtin_iterator() override { return this; }
|
||||||
bool done() const { return m_done; }
|
ThrowCompletionOr<void> next(VM&, bool& done, Value& value) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit StringIterator(String string, Object& prototype);
|
explicit StringIterator(String string, Object& prototype);
|
||||||
|
|
|
@ -33,20 +33,12 @@ void StringIteratorPrototype::initialize(Realm& realm)
|
||||||
JS_DEFINE_NATIVE_FUNCTION(StringIteratorPrototype::next)
|
JS_DEFINE_NATIVE_FUNCTION(StringIteratorPrototype::next)
|
||||||
{
|
{
|
||||||
auto iterator = TRY(typed_this_value(vm));
|
auto iterator = TRY(typed_this_value(vm));
|
||||||
if (iterator->done())
|
|
||||||
return create_iterator_result_object(vm, js_undefined(), true);
|
|
||||||
|
|
||||||
auto& utf8_iterator = iterator->iterator();
|
Value value;
|
||||||
|
bool done = false;
|
||||||
|
TRY(iterator->next(vm, done, value));
|
||||||
|
|
||||||
if (utf8_iterator.done()) {
|
return create_iterator_result_object(vm, value, done);
|
||||||
iterator->m_done = true;
|
|
||||||
return create_iterator_result_object(vm, js_undefined(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto code_point = String::from_code_point(*utf8_iterator);
|
|
||||||
++utf8_iterator;
|
|
||||||
|
|
||||||
return create_iterator_result_object(vm, PrimitiveString::create(vm, move(code_point)), false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue