LibJS: Update spec numbers for the Iterator Helpers proposal

This proposal has reached stage 4 and was merged into the ECMA-262 spec.
See: 961f269
This commit is contained in:
Timothy Flynn 2024-10-23 14:34:35 -04:00 committed by Tim Flynn
commit 84ad36de06
Notes: github-actions[bot] 2024-10-23 21:27:24 +00:00
8 changed files with 408 additions and 399 deletions

View file

@ -1,7 +1,7 @@
/*
* Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
* Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -35,7 +35,19 @@ Iterator::Iterator(Object& prototype)
{
}
// 7.4.2 GetIteratorFromMethod ( obj, method ), https://tc39.es/ecma262/#sec-getiteratorfrommethod
// 7.4.2 GetIteratorDirect ( obj ), https://tc39.es/ecma262/#sec-getiteratordirect
ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator_direct(VM& vm, Object& object)
{
// 1. Let nextMethod be ? Get(obj, "next").
auto next_method = TRY(object.get(vm.names.next));
// 2. Let iteratorRecord be Record { [[Iterator]]: obj, [[NextMethod]]: nextMethod, [[Done]]: false }.
// 3. Return iteratorRecord.
auto& realm = *vm.current_realm();
return vm.heap().allocate<IteratorRecord>(realm, realm, object, next_method, false);
}
// 7.4.3 GetIteratorFromMethod ( obj, method ), https://tc39.es/ecma262/#sec-getiteratorfrommethod
ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator_from_method(VM& vm, Value object, NonnullGCPtr<FunctionObject> method)
{
// 1. Let iterator be ? Call(method, obj).
@ -56,7 +68,7 @@ ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator_from_method(VM& vm,
return iterator_record;
}
// 7.4.3 GetIterator ( obj, kind ), https://tc39.es/ecma262/#sec-getiterator
// 7.4.4 GetIterator ( obj, kind ), https://tc39.es/ecma262/#sec-getiterator
ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator(VM& vm, Value object, IteratorHint kind)
{
JS::GCPtr<FunctionObject> method;
@ -96,29 +108,24 @@ ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator(VM& vm, Value objec
return TRY(get_iterator_from_method(vm, object, *method));
}
// 2.1.1 GetIteratorDirect ( obj ), https://tc39.es/proposal-iterator-helpers/#sec-getiteratorflattenable
ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator_direct(VM& vm, Object& object)
{
// 1. Let nextMethod be ? Get(obj, "next").
auto next_method = TRY(object.get(vm.names.next));
// 2. Let iteratorRecord be Record { [[Iterator]]: obj, [[NextMethod]]: nextMethod, [[Done]]: false }.
// 3. Return iteratorRecord.
auto& realm = *vm.current_realm();
return vm.heap().allocate<IteratorRecord>(realm, realm, object, next_method, false);
}
// 2.1.2 GetIteratorFlattenable ( obj, stringHandling ), https://tc39.es/proposal-iterator-helpers/#sec-getiteratorflattenable
ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator_flattenable(VM& vm, Value object, StringHandling string_handling)
// 7.4.5 GetIteratorFlattenable ( obj, primitiveHandling ), https://tc39.es/ecma262/#sec-getiteratorflattenable
ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator_flattenable(VM& vm, Value object, PrimitiveHandling primitive_handling)
{
// 1. If obj is not an Object, then
if (!object.is_object()) {
// a. If stringHandling is reject-strings or obj is not a String, throw a TypeError exception.
if (string_handling == StringHandling::RejectStrings || !object.is_string())
// a. If primitiveHandling is reject-primitives, throw a TypeError exception.
if (primitive_handling == PrimitiveHandling::RejectPrimitives)
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, object.to_string_without_side_effects());
// b. Assert: primitiveHandling is iterate-string-primitives.
ASSERT(primitive_handling == PrimitiveHandling::IterateStringPrimitives);
// c. If obj is not a String, throw a TypeError exception.
if (!object.is_string())
return vm.throw_completion<TypeError>(ErrorType::NotAString, object.to_string_without_side_effects());
}
// 2. Let method be ? GetMethod(obj, @@iterator).
// 2. Let method be ? GetMethod(obj, %Symbol.iterator%).
auto method = TRY(object.get_method(vm, vm.well_known_symbol_iterator()));
Value iterator;
@ -142,7 +149,7 @@ ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator_flattenable(VM& vm,
return TRY(get_iterator_direct(vm, iterator.as_object()));
}
// 7.4.4 IteratorNext ( iteratorRecord [ , value ] ), https://tc39.es/ecma262/#sec-iteratornext
// 7.4.6 IteratorNext ( iteratorRecord [ , value ] ), https://tc39.es/ecma262/#sec-iteratornext
ThrowCompletionOr<NonnullGCPtr<Object>> iterator_next(VM& vm, IteratorRecord& iterator_record, Optional<Value> value)
{
auto result = [&]() {
@ -183,21 +190,21 @@ ThrowCompletionOr<NonnullGCPtr<Object>> iterator_next(VM& vm, IteratorRecord& it
return result_value.as_object();
}
// 7.4.5 IteratorComplete ( iterResult ), https://tc39.es/ecma262/#sec-iteratorcomplete
// 7.4.7 IteratorComplete ( iteratorResult ), https://tc39.es/ecma262/#sec-iteratorcomplete
ThrowCompletionOr<bool> iterator_complete(VM& vm, Object& iterator_result)
{
// 1. Return ToBoolean(? Get(iterResult, "done")).
return TRY(iterator_result.get(vm.names.done)).to_boolean();
}
// 7.4.6 IteratorValue ( iterResult ), https://tc39.es/ecma262/#sec-iteratorvalue
// 7.4.8 IteratorValue ( iteratorResult ), https://tc39.es/ecma262/#sec-iteratorvalue
ThrowCompletionOr<Value> iterator_value(VM& vm, Object& iterator_result)
{
// 1. Return ? Get(iterResult, "value").
return TRY(iterator_result.get(vm.names.value));
}
// 7.4.7 IteratorStep ( iteratorRecord ), https://tc39.es/ecma262/#sec-iteratorstep
// 7.4.9 IteratorStep ( iteratorRecord ), https://tc39.es/ecma262/#sec-iteratorstep
ThrowCompletionOr<GCPtr<Object>> iterator_step(VM& vm, IteratorRecord& iterator_record)
{
// 1. Let result be ? IteratorNext(iteratorRecord).
@ -231,7 +238,7 @@ ThrowCompletionOr<GCPtr<Object>> iterator_step(VM& vm, IteratorRecord& iterator_
return result;
}
// 7.4.8 IteratorStepValue ( iteratorRecord ), https://tc39.es/ecma262/#sec-iteratorstepvalue
// 7.4.10 IteratorStepValue ( iteratorRecord ), https://tc39.es/ecma262/#sec-iteratorstepvalue
ThrowCompletionOr<Optional<Value>> iterator_step_value(VM& vm, IteratorRecord& iterator_record)
{
// 1. Let result be ? IteratorStep(iteratorRecord).
@ -256,8 +263,8 @@ ThrowCompletionOr<Optional<Value>> iterator_step_value(VM& vm, IteratorRecord& i
return TRY(value);
}
// 7.4.9 IteratorClose ( iteratorRecord, completion ), https://tc39.es/ecma262/#sec-iteratorclose
// 7.4.11 AsyncIteratorClose ( iteratorRecord, completion ), https://tc39.es/ecma262/#sec-asynciteratorclose
// 7.4.11 IteratorClose ( iteratorRecord, completion , https://tc39.es/ecma262/#sec-iteratorclose
// 7.4.13 AsyncIteratorClose ( iteratorRecord, completion ), https://tc39.es/ecma262/#sec-asynciteratorclose
// NOTE: These only differ in that async awaits the inner value after the call.
static Completion iterator_close_impl(VM& vm, IteratorRecord const& iterator_record, Completion completion, IteratorHint iterator_hint)
{
@ -307,19 +314,19 @@ static Completion iterator_close_impl(VM& vm, IteratorRecord const& iterator_rec
return completion;
}
// 7.4.9 IteratorClose ( iteratorRecord, completion ), https://tc39.es/ecma262/#sec-iteratorclose
// 7.4.11 IteratorClose ( iteratorRecord, completion , https://tc39.es/ecma262/#sec-iteratorclose
Completion iterator_close(VM& vm, IteratorRecord const& iterator_record, Completion completion)
{
return iterator_close_impl(vm, iterator_record, move(completion), IteratorHint::Sync);
}
// 7.4.11 AsyncIteratorClose ( iteratorRecord, completion ), https://tc39.es/ecma262/#sec-asynciteratorclose
// 7.4.13 AsyncIteratorClose ( iteratorRecord, completion ), https://tc39.es/ecma262/#sec-asynciteratorclose
Completion async_iterator_close(VM& vm, IteratorRecord const& iterator_record, Completion completion)
{
return iterator_close_impl(vm, iterator_record, move(completion), IteratorHint::Async);
}
// 7.4.12 CreateIterResultObject ( value, done ), https://tc39.es/ecma262/#sec-createiterresultobject
// 7.4.14 CreateIteratorResultObject ( value, done ), https://tc39.es/ecma262/#sec-createiterresultobject
NonnullGCPtr<Object> create_iterator_result_object(VM& vm, Value value, bool done)
{
auto& realm = *vm.current_realm();
@ -337,7 +344,7 @@ NonnullGCPtr<Object> create_iterator_result_object(VM& vm, Value value, bool don
return object;
}
// 7.4.14 IteratorToList ( iteratorRecord ), https://tc39.es/ecma262/#sec-iteratortolist
// 7.4.16 IteratorToList ( iteratorRecord ), https://tc39.es/ecma262/#sec-iteratortolist
ThrowCompletionOr<MarkedVector<Value>> iterator_to_list(VM& vm, IteratorRecord& iterator_record)
{
// 1. Let values be a new empty List.
@ -359,7 +366,7 @@ ThrowCompletionOr<MarkedVector<Value>> iterator_to_list(VM& vm, IteratorRecord&
}
}
// 2.1 SetterThatIgnoresPrototypeProperties ( this, home, p, v ), https://tc39.es/proposal-iterator-helpers/#sec-SetterThatIgnoresPrototypeProperties
// 7.3.36 SetterThatIgnoresPrototypeProperties ( thisValue, home, p, v ), https://tc39.es/ecma262/#sec-SetterThatIgnoresPrototypeProperties
ThrowCompletionOr<void> setter_that_ignores_prototype_properties(VM& vm, Value this_, Object const& home, PropertyKey const& property, Value value)
{
// 1. If this is not an Object, then

View file

@ -1,7 +1,7 @@
/*
* Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
* Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -65,20 +65,20 @@ enum class IteratorHint {
Async,
};
enum class StringHandling {
IterateStrings,
RejectStrings,
enum class PrimitiveHandling {
IterateStringPrimitives,
RejectPrimitives,
};
ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator_direct(VM&, Object&);
ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator_from_method(VM&, Value, NonnullGCPtr<FunctionObject>);
ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator(VM&, Value, IteratorHint);
ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator_direct(VM&, Object&);
ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator_flattenable(VM&, Value, StringHandling);
ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator_flattenable(VM&, Value, PrimitiveHandling);
ThrowCompletionOr<NonnullGCPtr<Object>> iterator_next(VM&, IteratorRecord&, Optional<Value> = {});
ThrowCompletionOr<GCPtr<Object>> iterator_step(VM&, IteratorRecord&);
ThrowCompletionOr<Optional<Value>> iterator_step_value(VM&, IteratorRecord&);
ThrowCompletionOr<bool> iterator_complete(VM&, Object& iterator_result);
ThrowCompletionOr<Value> iterator_value(VM&, Object& iterator_result);
ThrowCompletionOr<GCPtr<Object>> iterator_step(VM&, IteratorRecord&);
ThrowCompletionOr<Optional<Value>> iterator_step_value(VM&, IteratorRecord&);
Completion iterator_close(VM&, IteratorRecord const&, Completion);
Completion async_iterator_close(VM&, IteratorRecord const&, Completion);
NonnullGCPtr<Object> create_iterator_result_object(VM&, Value, bool done);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -16,7 +16,7 @@ namespace JS {
JS_DEFINE_ALLOCATOR(IteratorConstructor);
// 3.1.1.1 The Iterator Constructor, https://tc39.es/proposal-iterator-helpers/#sec-iterator-constructor
// 27.1.3.1 The Iterator Constructor, https://tc39.es/ecma262/#sec-iterator-constructor
IteratorConstructor::IteratorConstructor(Realm& realm)
: Base(realm.vm().names.Iterator.as_string(), realm.intrinsics().function_prototype())
{
@ -28,7 +28,7 @@ void IteratorConstructor::initialize(Realm& realm)
auto& vm = this->vm();
// 3.1.1.2.1 Iterator.prototype, https://tc39.es/proposal-iterator-helpers/#sec-iterator.prototype
// 27.1.3.2.2 Iterator.prototype Iterator.prototype, https://tc39.es/ecma262/#sec-iterator.prototype
define_direct_property(vm.names.prototype, realm.intrinsics().iterator_prototype(), 0);
u8 attr = Attribute::Writable | Attribute::Configurable;
@ -37,7 +37,7 @@ void IteratorConstructor::initialize(Realm& realm)
define_direct_property(vm.names.length, Value(0), Attribute::Configurable);
}
// 3.1.1.1.1 Iterator ( ), https://tc39.es/proposal-iterator-helpers/#sec-iterator
// 27.1.3.1.1 Iterator ( ), https://tc39.es/ecma262/#sec-iterator
ThrowCompletionOr<Value> IteratorConstructor::call()
{
auto& vm = this->vm();
@ -46,7 +46,7 @@ ThrowCompletionOr<Value> IteratorConstructor::call()
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, "Iterator");
}
// 3.1.1.1.1 Iterator ( ), https://tc39.es/proposal-iterator-helpers/#sec-iterator
// 27.1.3.1.1 Iterator ( ), https://tc39.es/ecma262/#sec-iterator
ThrowCompletionOr<NonnullGCPtr<Object>> IteratorConstructor::construct(FunctionObject& new_target)
{
auto& vm = this->vm();
@ -59,15 +59,15 @@ ThrowCompletionOr<NonnullGCPtr<Object>> IteratorConstructor::construct(FunctionO
return TRY(ordinary_create_from_constructor<Iterator>(vm, new_target, &Intrinsics::iterator_prototype));
}
// 3.1.1.2.2 Iterator.from ( O ), https://tc39.es/proposal-iterator-helpers/#sec-iterator.from
// 27.1.3.2.1 Iterator.from ( O ), https://tc39.es/ecma262/#sec-iterator.from
JS_DEFINE_NATIVE_FUNCTION(IteratorConstructor::from)
{
auto& realm = *vm.current_realm();
auto object = vm.argument(0);
// 1. Let iteratorRecord be ? GetIteratorFlattenable(O, iterate-strings).
auto iterator_record = TRY(get_iterator_flattenable(vm, object, StringHandling::IterateStrings));
// 1. Let iteratorRecord be ? GetIteratorFlattenable(O, iterate-string-primitives).
auto iterator_record = TRY(get_iterator_flattenable(vm, object, PrimitiveHandling::IterateStringPrimitives));
// 2. Let hasInstance be ? OrdinaryHasInstance(%Iterator%, iteratorRecord.[[Iterator]]).
auto has_instance = TRY(ordinary_has_instance(vm, iterator_record->iterator, realm.intrinsics().iterator_constructor()));
@ -80,7 +80,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorConstructor::from)
// 4. Let wrapper be OrdinaryObjectCreate(%WrapForValidIteratorPrototype%, « [[Iterated]] »).
// 5. Set wrapper.[[Iterated]] to iteratorRecord.
auto wrapper = Iterator::create(realm, realm.intrinsics().wrap_for_valid_iterator_prototype(), move(iterator_record));
auto wrapper = Iterator::create(realm, realm.intrinsics().wrap_for_valid_iterator_prototype(), iterator_record);
// 6. Return wrapper.
return wrapper;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -12,6 +12,7 @@ namespace JS {
JS_DEFINE_ALLOCATOR(IteratorHelperPrototype);
// 27.1.2.1 The %IteratorHelperPrototype% Object, https://tc39.es/ecma262/#sec-%iteratorhelperprototype%-object
IteratorHelperPrototype::IteratorHelperPrototype(Realm& realm)
: PrototypeObject(realm.intrinsics().iterator_prototype())
{
@ -26,11 +27,11 @@ void IteratorHelperPrototype::initialize(Realm& realm)
define_native_function(realm, vm.names.next, next, 0, attr);
define_native_function(realm, vm.names.return_, return_, 0, attr);
// 3.1.2.1.3 %IteratorHelperPrototype% [ @@toStringTag ], https://tc39.es/proposal-iterator-helpers/#sec-%iteratorhelperprototype%-@@tostringtag
// 27.1.2.1.3 %IteratorHelperPrototype% [ %Symbol.toStringTag% ], https://tc39.es/ecma262/#sec-%iteratorhelperprototype%-%symbol.tostringtag%
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Iterator Helper"_string), Attribute::Configurable);
}
// 3.1.2.1.1 %IteratorHelperPrototype%.next ( ), https://tc39.es/proposal-iterator-helpers/#sec-%iteratorhelperprototype%.next
// 27.1.2.1.1 %IteratorHelperPrototype%.next ( ), https://tc39.es/ecma262/#sec-%iteratorhelperprototype%.next
JS_DEFINE_NATIVE_FUNCTION(IteratorHelperPrototype::next)
{
auto iterator = TRY(typed_this_object(vm));
@ -39,7 +40,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorHelperPrototype::next)
return iterator->resume(vm, js_undefined(), "Iterator Helper"sv);
}
// 3.1.2.1.2 %IteratorHelperPrototype%.return ( ), https://tc39.es/proposal-iterator-helpers/#sec-%iteratorhelperprototype%.return
// 27.1.2.1.2 %IteratorHelperPrototype%.return ( ), https://tc39.es/ecma262/#sec-%iteratorhelperprototype%.return
JS_DEFINE_NATIVE_FUNCTION(IteratorHelperPrototype::return_)
{
// 1. Let O be this value.

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -32,33 +33,26 @@ void IteratorPrototype::initialize(Realm& realm)
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.well_known_symbol_iterator(), symbol_iterator, 0, attr);
define_native_function(realm, vm.names.map, map, 1, attr);
define_native_function(realm, vm.names.filter, filter, 1, attr);
define_native_function(realm, vm.names.take, take, 1, attr);
define_native_function(realm, vm.names.drop, drop, 1, attr);
define_native_function(realm, vm.names.flatMap, flat_map, 1, attr);
define_native_function(realm, vm.names.reduce, reduce, 1, attr);
define_native_function(realm, vm.names.toArray, to_array, 0, attr);
define_native_function(realm, vm.names.forEach, for_each, 1, attr);
define_native_function(realm, vm.names.some, some, 1, attr);
define_native_function(realm, vm.names.every, every, 1, attr);
define_native_function(realm, vm.names.filter, filter, 1, attr);
define_native_function(realm, vm.names.find, find, 1, attr);
define_native_function(realm, vm.names.flatMap, flat_map, 1, attr);
define_native_function(realm, vm.names.forEach, for_each, 1, attr);
define_native_function(realm, vm.names.map, map, 1, attr);
define_native_function(realm, vm.names.reduce, reduce, 1, attr);
define_native_function(realm, vm.names.some, some, 1, attr);
define_native_function(realm, vm.names.take, take, 1, attr);
define_native_function(realm, vm.names.toArray, to_array, 0, attr);
// 3.1.3.1 Iterator.prototype.constructor, https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.constructor
// 27.1.4.1 Iterator.prototype.constructor, https://tc39.es/ecma262/#sec-iterator.prototype.constructor
define_native_accessor(realm, vm.names.constructor, constructor_getter, constructor_setter, Attribute::Configurable);
// 3.1.3.13 Iterator.prototype [ @@toStringTag ], https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype-@@tostringtag
// 27.1.4.14 Iterator.prototype [ %Symbol.toStringTag% ], https://tc39.es/ecma262/#sec-iterator.prototype-%symbol.tostringtag%
define_native_accessor(realm, vm.well_known_symbol_to_string_tag(), to_string_tag_getter, to_string_tag_setter, Attribute::Configurable);
}
// 27.1.2.1 %IteratorPrototype% [ @@iterator ] ( ), https://tc39.es/ecma262/#sec-%iteratorprototype%-@@iterator
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::symbol_iterator)
{
// 1. Return the this value.
return vm.this_value();
}
// 3.1.3.1.1 get Iterator.prototype.constructor, https://tc39.es/proposal-iterator-helpers/#sec-get-iteratorprototype-constructor
// 27.1.4.1.1 get Iterator.prototype.constructor, https://tc39.es/ecma262/#sec-get-iterator.prototype.constructor
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::constructor_getter)
{
auto& realm = *vm.current_realm();
@ -67,7 +61,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::constructor_getter)
return realm.intrinsics().iterator_constructor();
}
// 3.1.3.1.2 set Iterator.prototype.constructor, https://tc39.es/proposal-iterator-helpers/#sec-set-iteratorprototype-constructor
// 27.1.4.1.2 set Iterator.prototype.constructor, https://tc39.es/ecma262/#sec-set-iterator.prototype.constructor
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::constructor_setter)
{
auto& realm = *vm.current_realm();
@ -79,189 +73,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::constructor_setter)
return js_undefined();
}
// 3.1.3.2 Iterator.prototype.map ( mapper ), https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.map
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::map)
{
auto& realm = *vm.current_realm();
auto mapper = vm.argument(0);
// 1. Let O be the this value.
// 2. If O is not an Object, throw a TypeError exception.
auto object = TRY(this_object(vm));
// 3. If IsCallable(mapper) is false, throw a TypeError exception.
if (!mapper.is_function())
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, "mapper"sv);
// 4. Let iterated be ? GetIteratorDirect(O).
auto iterated = TRY(get_iterator_direct(vm, object));
// 5. Let closure be a new Abstract Closure with no parameters that captures iterated and mapper and performs the following steps when called:
auto closure = JS::create_heap_function(realm.heap(), [mapper = NonnullGCPtr { mapper.as_function() }](VM& vm, IteratorHelper& iterator) -> ThrowCompletionOr<Value> {
auto& iterated = iterator.underlying_iterator();
// a. Let counter be 0.
// b. Repeat,
// i. Let value be ? IteratorStepValue(iterated).
auto value = TRY(iterator_step_value(vm, iterated));
// ii. If value is done, return undefined.
if (!value.has_value())
return iterator.result(js_undefined());
// iii. Let mapped be Completion(Call(mapper, undefined, « value, 𝔽(counter) »)).
auto mapped = call(vm, *mapper, js_undefined(), *value, Value { iterator.counter() });
// iv. IfAbruptCloseIterator(mapped, iterated).
if (mapped.is_error())
return iterator.close_result(vm, mapped.release_error());
// vii. Set counter to counter + 1.
// NOTE: We do this step early to ensure it occurs before returning.
iterator.increment_counter();
// v. Let completion be Completion(Yield(mapped)).
// vi. IfAbruptCloseIterator(completion, iterated).
return iterator.result(mapped.release_value());
});
// 6. Let result be CreateIteratorFromClosure(closure, "Iterator Helper", %IteratorHelperPrototype%, « [[UnderlyingIterator]] »).
// 7. Set result.[[UnderlyingIterator]] to iterated.
auto result = TRY(IteratorHelper::create(realm, move(iterated), move(closure)));
// 8. Return result.
return result;
}
// 3.1.3.3 Iterator.prototype.filter ( predicate ), https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.filter
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::filter)
{
auto& realm = *vm.current_realm();
auto predicate = vm.argument(0);
// 1. Let O be the this value.
// 2. If O is not an Object, throw a TypeError exception.
auto object = TRY(this_object(vm));
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (!predicate.is_function())
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, "predicate"sv);
// 4. Let iterated be ? GetIteratorDirect(O).
auto iterated = TRY(get_iterator_direct(vm, object));
// 5. Let closure be a new Abstract Closure with no parameters that captures iterated and predicate and performs the following steps when called:
auto closure = JS::create_heap_function(realm.heap(), [predicate = NonnullGCPtr { predicate.as_function() }](VM& vm, IteratorHelper& iterator) -> ThrowCompletionOr<Value> {
auto& iterated = iterator.underlying_iterator();
// a. Let counter be 0.
// b. Repeat,
while (true) {
// i. Let value be ? IteratorStepValue(iterated).
auto value = TRY(iterator_step_value(vm, iterated));
// ii. If value is done, return undefined.
if (!value.has_value())
return iterator.result(js_undefined());
// iii. Let selected be Completion(Call(predicate, undefined, « value, 𝔽(counter) »)).
auto selected = call(vm, *predicate, js_undefined(), *value, Value { iterator.counter() });
// iv. IfAbruptCloseIterator(selected, iterated).
if (selected.is_error())
return iterator.close_result(vm, selected.release_error());
// vi. Set counter to counter + 1.
// NOTE: We do this step early to ensure it occurs before returning.
iterator.increment_counter();
// v. If ToBoolean(selected) is true, then
if (selected.value().to_boolean()) {
// 1. Let completion be Completion(Yield(value)).
// 2. IfAbruptCloseIterator(completion, iterated).
return iterator.result(*value);
}
}
});
// 6. Let result be CreateIteratorFromClosure(closure, "Iterator Helper", %IteratorHelperPrototype%, « [[UnderlyingIterator]] »).
// 7. Set result.[[UnderlyingIterator]] to iterated.
auto result = TRY(IteratorHelper::create(realm, move(iterated), move(closure)));
// 8. Return result.
return result;
}
// 3.1.3.4 Iterator.prototype.take ( limit ), https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.take
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::take)
{
auto& realm = *vm.current_realm();
auto limit = vm.argument(0);
// 1. Let O be the this value.
// 2. If O is not an Object, throw a TypeError exception.
auto object = TRY(this_object(vm));
// 3. Let numLimit be ? ToNumber(limit).
auto numeric_limit = TRY(limit.to_number(vm));
// 4. If numLimit is NaN, throw a RangeError exception.
if (numeric_limit.is_nan())
return vm.throw_completion<RangeError>(ErrorType::NumberIsNaN, "limit"sv);
// 5. Let integerLimit be ! ToIntegerOrInfinity(numLimit).
auto integer_limit = MUST(numeric_limit.to_integer_or_infinity(vm));
// 6. If integerLimit < 0, throw a RangeError exception.
if (integer_limit < 0)
return vm.throw_completion<RangeError>(ErrorType::NumberIsNegative, "limit"sv);
// 7. Let iterated be ? GetIteratorDirect(O).
auto iterated = TRY(get_iterator_direct(vm, object));
// 8. Let closure be a new Abstract Closure with no parameters that captures iterated and integerLimit and performs the following steps when called:
auto closure = JS::create_heap_function(realm.heap(), [integer_limit](VM& vm, IteratorHelper& iterator) -> ThrowCompletionOr<Value> {
auto& iterated = iterator.underlying_iterator();
// a. Let remaining be integerLimit.
// b. Repeat,
// i. If remaining is 0, then
if (iterator.counter() >= integer_limit) {
// 1. Return ? IteratorClose(iterated, NormalCompletion(undefined)).
return iterator.close_result(vm, normal_completion(js_undefined()));
}
// ii. If remaining is not +∞, then
// 1. Set remaining to remaining - 1.
iterator.increment_counter();
// iii. Let value be ? IteratorStepValue(iterated).
auto value = TRY(iterator_step_value(vm, iterated));
// iv. If value is done, return undefined.
if (!value.has_value())
return iterator.result(js_undefined());
// v. Let completion be Completion(Yield(value)).
// vi. IfAbruptCloseIterator(completion, iterated).
return iterator.result(*value);
});
// 9. Let result be CreateIteratorFromClosure(closure, "Iterator Helper", %IteratorHelperPrototype%, « [[UnderlyingIterator]] »).
// 10. Set result.[[UnderlyingIterator]] to iterated.
auto result = TRY(IteratorHelper::create(realm, move(iterated), move(closure)));
// 11. Return result.
return result;
}
// 3.1.3.5 Iterator.prototype.drop ( limit ), https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.drop
// 27.1.4.2 Iterator.prototype.drop ( limit ), https://tc39.es/ecma262/#sec-iterator.prototype.drop
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::drop)
{
auto& realm = *vm.current_realm();
@ -324,12 +136,161 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::drop)
// 9. Let result be CreateIteratorFromClosure(closure, "Iterator Helper", %IteratorHelperPrototype%, « [[UnderlyingIterator]] »).
// 10. Set result.[[UnderlyingIterator]] to iterated.
auto result = TRY(IteratorHelper::create(realm, move(iterated), move(closure)));
auto result = TRY(IteratorHelper::create(realm, iterated, closure));
// 11. Return result.
return result;
}
// 27.1.4.3 Iterator.prototype.every ( predicate ), https://tc39.es/ecma262/#sec-iterator.prototype.every
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::every)
{
auto predicate = vm.argument(0);
// 1. Let O be the this value.
// 2. If O is not an Object, throw a TypeError exception.
auto object = TRY(this_object(vm));
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (!predicate.is_function())
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, "predicate"sv);
// 4. Let iterated be ? GetIteratorDirect(O).
auto iterated = TRY(get_iterator_direct(vm, object));
// 5. Let counter be 0.
size_t counter = 0;
// 6. Repeat,
while (true) {
// a. Let value be ? IteratorStepValue(iterated).
auto value = TRY(iterator_step_value(vm, iterated));
// b. If value is done, return undefined.
if (!value.has_value())
return Value { true };
// c. Let result be Completion(Call(predicate, undefined, « value, 𝔽(counter) »)).
auto result = call(vm, predicate.as_function(), js_undefined(), *value, Value { counter });
// d. IfAbruptCloseIterator(result, iterated).
if (result.is_error())
return *TRY(iterator_close(vm, iterated, result.release_error()));
// e. If ToBoolean(result) is false, return ? IteratorClose(iterated, NormalCompletion(false)).
if (!result.value().to_boolean())
return *TRY(iterator_close(vm, iterated, normal_completion(Value { false })));
// f. Set counter to counter + 1.
++counter;
}
}
// 27.1.4.4 Iterator.prototype.filter ( predicate ), https://tc39.es/ecma262/#sec-iterator.prototype.filter
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::filter)
{
auto& realm = *vm.current_realm();
auto predicate = vm.argument(0);
// 1. Let O be the this value.
// 2. If O is not an Object, throw a TypeError exception.
auto object = TRY(this_object(vm));
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (!predicate.is_function())
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, "predicate"sv);
// 4. Let iterated be ? GetIteratorDirect(O).
auto iterated = TRY(get_iterator_direct(vm, object));
// 5. Let closure be a new Abstract Closure with no parameters that captures iterated and predicate and performs the following steps when called:
auto closure = JS::create_heap_function(realm.heap(), [predicate = NonnullGCPtr { predicate.as_function() }](VM& vm, IteratorHelper& iterator) -> ThrowCompletionOr<Value> {
auto& iterated = iterator.underlying_iterator();
// a. Let counter be 0.
// b. Repeat,
while (true) {
// i. Let value be ? IteratorStepValue(iterated).
auto value = TRY(iterator_step_value(vm, iterated));
// ii. If value is done, return undefined.
if (!value.has_value())
return iterator.result(js_undefined());
// iii. Let selected be Completion(Call(predicate, undefined, « value, 𝔽(counter) »)).
auto selected = call(vm, *predicate, js_undefined(), *value, Value { iterator.counter() });
// iv. IfAbruptCloseIterator(selected, iterated).
if (selected.is_error())
return iterator.close_result(vm, selected.release_error());
// vi. Set counter to counter + 1.
// NOTE: We do this step early to ensure it occurs before returning.
iterator.increment_counter();
// v. If ToBoolean(selected) is true, then
if (selected.value().to_boolean()) {
// 1. Let completion be Completion(Yield(value)).
// 2. IfAbruptCloseIterator(completion, iterated).
return iterator.result(*value);
}
}
});
// 6. Let result be CreateIteratorFromClosure(closure, "Iterator Helper", %IteratorHelperPrototype%, « [[UnderlyingIterator]] »).
// 7. Set result.[[UnderlyingIterator]] to iterated.
auto result = TRY(IteratorHelper::create(realm, iterated, closure));
// 8. Return result.
return result;
}
// 27.1.4.5 Iterator.prototype.find ( predicate ), https://tc39.es/ecma262/#sec-iterator.prototype.find
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::find)
{
auto predicate = vm.argument(0);
// 1. Let O be the this value.
// 2. If O is not an Object, throw a TypeError exception.
auto object = TRY(this_object(vm));
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (!predicate.is_function())
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, "predicate"sv);
// 4. Let iterated be ? GetIteratorDirect(O).
auto iterated = TRY(get_iterator_direct(vm, object));
// 5. Let counter be 0.
size_t counter = 0;
// 6. Repeat,
while (true) {
// a. Let value be ? IteratorStepValue(iterated).
auto value = TRY(iterator_step_value(vm, iterated));
// b. If value is done, return undefined.
if (!value.has_value())
return js_undefined();
// c. Let result be Completion(Call(predicate, undefined, « value, 𝔽(counter) »)).
auto result = call(vm, predicate.as_function(), js_undefined(), *value, Value { counter });
// d. IfAbruptCloseIterator(result, iterated).
if (result.is_error())
return *TRY(iterator_close(vm, iterated, result.release_error()));
// e. If ToBoolean(result) is true, return ? IteratorClose(iterated, NormalCompletion(value)).
if (result.value().to_boolean())
return *TRY(iterator_close(vm, iterated, normal_completion(value)));
// f. Set counter to counter + 1.
++counter;
}
}
class FlatMapIterator : public Cell {
JS_CELL(FlatMapIterator, Cell);
JS_DECLARE_ALLOCATOR(FlatMapIterator);
@ -385,8 +346,8 @@ private:
if (mapped.is_error())
return iterator.close_result(vm, mapped.release_error());
// v. Let innerIterator be Completion(GetIteratorFlattenable(mapped, reject-strings)).
auto inner_iterator = get_iterator_flattenable(vm, mapped.release_value(), StringHandling::RejectStrings);
// v. Let innerIterator be Completion(GetIteratorFlattenable(mapped, reject-primitives)).
auto inner_iterator = get_iterator_flattenable(vm, mapped.release_value(), PrimitiveHandling::RejectPrimitives);
// vi. IfAbruptCloseIterator(innerIterator, iterated).
if (inner_iterator.is_error())
@ -434,7 +395,7 @@ private:
JS_DEFINE_ALLOCATOR(FlatMapIterator);
// 3.1.3.6 Iterator.prototype.flatMap ( mapper ), https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.flatmap
// 27.1.4.6 Iterator.prototype.flatMap ( mapper ), https://tc39.es/ecma262/#sec-iterator.prototype.flatmap
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::flat_map)
{
auto& realm = *vm.current_realm();
@ -466,13 +427,109 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::flat_map)
// 6. Let result be CreateIteratorFromClosure(closure, "Iterator Helper", %IteratorHelperPrototype%, « [[UnderlyingIterator]] »).
// 7. Set result.[[UnderlyingIterator]] to iterated.
auto result = TRY(IteratorHelper::create(realm, move(iterated), move(closure), move(abrupt_closure)));
auto result = TRY(IteratorHelper::create(realm, iterated, closure, move(abrupt_closure)));
// 8. Return result.
return result;
}
// 3.1.3.7 Iterator.prototype.reduce ( reducer [ , initialValue ] ), https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.reduce
// 27.1.4.7 Iterator.prototype.forEach ( procedure ), https://tc39.es/ecma262/#sec-iterator.prototype.foreach
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::for_each)
{
auto function = vm.argument(0);
// 1. Let O be the this value.
// 2. If O is not an Object, throw a TypeError exception.
auto object = TRY(this_object(vm));
// 3. If IsCallable(fn) is false, throw a TypeError exception.
if (!function.is_function())
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, "fn"sv);
// 4. Let iterated be ? GetIteratorDirect(O).
auto iterated = TRY(get_iterator_direct(vm, object));
// 5. Let counter be 0.
size_t counter = 0;
// 6. Repeat,
while (true) {
// a. Let value be ? IteratorStepValue(iterated).
auto value = TRY(iterator_step_value(vm, iterated));
// b. If value is done, return undefined.
if (!value.has_value())
return js_undefined();
// c. Let result be Completion(Call(fn, undefined, « value, 𝔽(counter) »)).
auto result = call(vm, function.as_function(), js_undefined(), *value, Value { counter });
// d. IfAbruptCloseIterator(result, iterated).
if (result.is_error())
return *TRY(iterator_close(vm, iterated, result.release_error()));
// e. Set counter to counter + 1.
++counter;
}
}
// 27.1.4.8 Iterator.prototype.map ( mapper ), https://tc39.es/ecma262/#sec-iterator.prototype.map
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::map)
{
auto& realm = *vm.current_realm();
auto mapper = vm.argument(0);
// 1. Let O be the this value.
// 2. If O is not an Object, throw a TypeError exception.
auto object = TRY(this_object(vm));
// 3. If IsCallable(mapper) is false, throw a TypeError exception.
if (!mapper.is_function())
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, "mapper"sv);
// 4. Let iterated be ? GetIteratorDirect(O).
auto iterated = TRY(get_iterator_direct(vm, object));
// 5. Let closure be a new Abstract Closure with no parameters that captures iterated and mapper and performs the following steps when called:
auto closure = JS::create_heap_function(realm.heap(), [mapper = NonnullGCPtr { mapper.as_function() }](VM& vm, IteratorHelper& iterator) -> ThrowCompletionOr<Value> {
auto& iterated = iterator.underlying_iterator();
// a. Let counter be 0.
// b. Repeat,
// i. Let value be ? IteratorStepValue(iterated).
auto value = TRY(iterator_step_value(vm, iterated));
// ii. If value is done, return undefined.
if (!value.has_value())
return iterator.result(js_undefined());
// iii. Let mapped be Completion(Call(mapper, undefined, « value, 𝔽(counter) »)).
auto mapped = call(vm, *mapper, js_undefined(), *value, Value { iterator.counter() });
// iv. IfAbruptCloseIterator(mapped, iterated).
if (mapped.is_error())
return iterator.close_result(vm, mapped.release_error());
// vii. Set counter to counter + 1.
// NOTE: We do this step early to ensure it occurs before returning.
iterator.increment_counter();
// v. Let completion be Completion(Yield(mapped)).
// vi. IfAbruptCloseIterator(completion, iterated).
return iterator.result(mapped.release_value());
});
// 6. Let result be CreateIteratorFromClosure(closure, "Iterator Helper", %IteratorHelperPrototype%, « [[UnderlyingIterator]] »).
// 7. Set result.[[UnderlyingIterator]] to iterated.
auto result = TRY(IteratorHelper::create(realm, iterated, closure));
// 8. Return result.
return result;
}
// 27.1.4.9 Iterator.prototype.reduce ( reducer [ , initialValue ] ), https://tc39.es/ecma262/#sec-iterator.prototype.reduce
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::reduce)
{
auto reducer = vm.argument(0);
@ -538,76 +595,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::reduce)
}
}
// 3.1.3.8 Iterator.prototype.toArray ( ), https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.toarray
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::to_array)
{
auto& realm = *vm.current_realm();
// 1. Let O be the this value.
// 2. If O is not an Object, throw a TypeError exception.
auto object = TRY(this_object(vm));
// 3. Let iterated be ? GetIteratorDirect(O).
auto iterated = TRY(get_iterator_direct(vm, object));
// 4. Let items be a new empty List.
Vector<Value> items;
// 5. Repeat,
while (true) {
// a. Let value be ? IteratorStepValue(iterated).
auto value = TRY(iterator_step_value(vm, iterated));
// b. If value is done, return CreateArrayFromList(items).
if (!value.has_value())
return Array::create_from(realm, items);
// c. Append value to items.
TRY_OR_THROW_OOM(vm, items.try_append(*value));
}
}
// 3.1.3.9 Iterator.prototype.forEach ( fn ), https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.foreach
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::for_each)
{
auto function = vm.argument(0);
// 1. Let O be the this value.
// 2. If O is not an Object, throw a TypeError exception.
auto object = TRY(this_object(vm));
// 3. If IsCallable(fn) is false, throw a TypeError exception.
if (!function.is_function())
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, "fn"sv);
// 4. Let iterated be ? GetIteratorDirect(O).
auto iterated = TRY(get_iterator_direct(vm, object));
// 5. Let counter be 0.
size_t counter = 0;
// 6. Repeat,
while (true) {
// a. Let value be ? IteratorStepValue(iterated).
auto value = TRY(iterator_step_value(vm, iterated));
// b. If value is done, return undefined.
if (!value.has_value())
return js_undefined();
// c. Let result be Completion(Call(fn, undefined, « value, 𝔽(counter) »)).
auto result = call(vm, function.as_function(), js_undefined(), *value, Value { counter });
// d. IfAbruptCloseIterator(result, iterated).
if (result.is_error())
return *TRY(iterator_close(vm, iterated, result.release_error()));
// e. Set counter to counter + 1.
++counter;
}
}
// 3.1.3.10 Iterator.prototype.some ( predicate ), https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.some
// 27.1.4.10 Iterator.prototype.some ( predicate ), https://tc39.es/ecma262/#sec-iterator.prototype.some
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::some)
{
auto predicate = vm.argument(0);
@ -651,102 +639,115 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::some)
}
}
// 3.1.3.11 Iterator.prototype.every ( predicate ), https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.every
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::every)
// 27.1.4.11 Iterator.prototype.take ( limit ), https://tc39.es/ecma262/#sec-iterator.prototype.take
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::take)
{
auto predicate = vm.argument(0);
auto& realm = *vm.current_realm();
auto limit = vm.argument(0);
// 1. Let O be the this value.
// 2. If O is not an Object, throw a TypeError exception.
auto object = TRY(this_object(vm));
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (!predicate.is_function())
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, "predicate"sv);
// 3. Let numLimit be ? ToNumber(limit).
auto numeric_limit = TRY(limit.to_number(vm));
// 4. Let iterated be ? GetIteratorDirect(O).
// 4. If numLimit is NaN, throw a RangeError exception.
if (numeric_limit.is_nan())
return vm.throw_completion<RangeError>(ErrorType::NumberIsNaN, "limit"sv);
// 5. Let integerLimit be ! ToIntegerOrInfinity(numLimit).
auto integer_limit = MUST(numeric_limit.to_integer_or_infinity(vm));
// 6. If integerLimit < 0, throw a RangeError exception.
if (integer_limit < 0)
return vm.throw_completion<RangeError>(ErrorType::NumberIsNegative, "limit"sv);
// 7. Let iterated be ? GetIteratorDirect(O).
auto iterated = TRY(get_iterator_direct(vm, object));
// 5. Let counter be 0.
size_t counter = 0;
// 8. Let closure be a new Abstract Closure with no parameters that captures iterated and integerLimit and performs the following steps when called:
auto closure = JS::create_heap_function(realm.heap(), [integer_limit](VM& vm, IteratorHelper& iterator) -> ThrowCompletionOr<Value> {
auto& iterated = iterator.underlying_iterator();
// 6. Repeat,
while (true) {
// a. Let value be ? IteratorStepValue(iterated).
// a. Let remaining be integerLimit.
// b. Repeat,
// i. If remaining is 0, then
if (iterator.counter() >= integer_limit) {
// 1. Return ? IteratorClose(iterated, NormalCompletion(undefined)).
return iterator.close_result(vm, normal_completion(js_undefined()));
}
// ii. If remaining is not +∞, then
// 1. Set remaining to remaining - 1.
iterator.increment_counter();
// iii. Let value be ? IteratorStepValue(iterated).
auto value = TRY(iterator_step_value(vm, iterated));
// b. If value is done, return undefined.
// iv. If value is done, return undefined.
if (!value.has_value())
return Value { true };
return iterator.result(js_undefined());
// c. Let result be Completion(Call(predicate, undefined, « value, 𝔽(counter) »)).
auto result = call(vm, predicate.as_function(), js_undefined(), *value, Value { counter });
// v. Let completion be Completion(Yield(value)).
// vi. IfAbruptCloseIterator(completion, iterated).
return iterator.result(*value);
});
// d. IfAbruptCloseIterator(result, iterated).
if (result.is_error())
return *TRY(iterator_close(vm, iterated, result.release_error()));
// 9. Let result be CreateIteratorFromClosure(closure, "Iterator Helper", %IteratorHelperPrototype%, « [[UnderlyingIterator]] »).
// 10. Set result.[[UnderlyingIterator]] to iterated.
auto result = TRY(IteratorHelper::create(realm, iterated, closure));
// e. If ToBoolean(result) is false, return ? IteratorClose(iterated, NormalCompletion(false)).
if (!result.value().to_boolean())
return *TRY(iterator_close(vm, iterated, normal_completion(Value { false })));
// f. Set counter to counter + 1.
++counter;
}
// 11. Return result.
return result;
}
// 3.1.3.12 Iterator.prototype.find ( predicate ), https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.find
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::find)
// 27.1.4.12 Iterator.prototype.toArray ( ), https://tc39.es/ecma262/#sec-iterator.prototype.toarray
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::to_array)
{
auto predicate = vm.argument(0);
auto& realm = *vm.current_realm();
// 1. Let O be the this value.
// 2. If O is not an Object, throw a TypeError exception.
auto object = TRY(this_object(vm));
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (!predicate.is_function())
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, "predicate"sv);
// 4. Let iterated be ? GetIteratorDirect(O).
// 3. Let iterated be ? GetIteratorDirect(O).
auto iterated = TRY(get_iterator_direct(vm, object));
// 5. Let counter be 0.
size_t counter = 0;
// 4. Let items be a new empty List.
Vector<Value> items;
// 6. Repeat,
// 5. Repeat,
while (true) {
// a. Let value be ? IteratorStepValue(iterated).
auto value = TRY(iterator_step_value(vm, iterated));
// b. If value is done, return undefined.
// b. If value is done, return CreateArrayFromList(items).
if (!value.has_value())
return js_undefined();
return Array::create_from(realm, items);
// c. Let result be Completion(Call(predicate, undefined, « value, 𝔽(counter) »)).
auto result = call(vm, predicate.as_function(), js_undefined(), *value, Value { counter });
// d. IfAbruptCloseIterator(result, iterated).
if (result.is_error())
return *TRY(iterator_close(vm, iterated, result.release_error()));
// e. If ToBoolean(result) is true, return ? IteratorClose(iterated, NormalCompletion(value)).
if (result.value().to_boolean())
return *TRY(iterator_close(vm, iterated, normal_completion(value)));
// f. Set counter to counter + 1.
++counter;
// c. Append value to items.
TRY_OR_THROW_OOM(vm, items.try_append(*value));
}
}
// 3.1.3.13.1 get Iterator.prototype [ @@toStringTag ], https://tc39.es/proposal-iterator-helpers/#sec-get-iteratorprototype-@@tostringtag
// 27.1.4.13 Iterator.prototype [ %Symbol.iterator% ] ( ), https://tc39.es/ecma262/#sec-iterator.prototype-%symbol.iterator%
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::symbol_iterator)
{
// 1. Return the this value.
return vm.this_value();
}
// 27.1.4.14.1 get Iterator.prototype [ %Symbol.toStringTag% ], https://tc39.es/ecma262/#sec-get-iterator.prototype-%symbol.tostringtag%
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::to_string_tag_getter)
{
// 1. Return "Iterator".
return PrimitiveString::create(vm, vm.names.Iterator.as_string());
}
// 3.1.3.13.2 set Iterator.prototype [ @@toStringTag ], https://tc39.es/proposal-iterator-helpers/#sec-set-iteratorprototype-@@tostringtag
// 27.1.4.14.2 set Iterator.prototype [ %Symbol.toStringTag% ], https://tc39.es/ecma262/#sec-set-iterator.prototype-%symbol.tostringtag%
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::to_string_tag_setter)
{
auto& realm = *vm.current_realm();

View file

@ -25,19 +25,19 @@ private:
JS_DECLARE_NATIVE_FUNCTION(constructor_getter);
JS_DECLARE_NATIVE_FUNCTION(constructor_setter);
JS_DECLARE_NATIVE_FUNCTION(symbol_iterator);
JS_DECLARE_NATIVE_FUNCTION(map);
JS_DECLARE_NATIVE_FUNCTION(filter);
JS_DECLARE_NATIVE_FUNCTION(take);
JS_DECLARE_NATIVE_FUNCTION(drop);
JS_DECLARE_NATIVE_FUNCTION(flat_map);
JS_DECLARE_NATIVE_FUNCTION(reduce);
JS_DECLARE_NATIVE_FUNCTION(to_array);
JS_DECLARE_NATIVE_FUNCTION(for_each);
JS_DECLARE_NATIVE_FUNCTION(some);
JS_DECLARE_NATIVE_FUNCTION(every);
JS_DECLARE_NATIVE_FUNCTION(filter);
JS_DECLARE_NATIVE_FUNCTION(find);
JS_DECLARE_NATIVE_FUNCTION(flat_map);
JS_DECLARE_NATIVE_FUNCTION(for_each);
JS_DECLARE_NATIVE_FUNCTION(map);
JS_DECLARE_NATIVE_FUNCTION(reduce);
JS_DECLARE_NATIVE_FUNCTION(some);
JS_DECLARE_NATIVE_FUNCTION(take);
JS_DECLARE_NATIVE_FUNCTION(to_array);
JS_DECLARE_NATIVE_FUNCTION(symbol_iterator);
JS_DECLARE_NATIVE_FUNCTION(to_string_tag_getter);
JS_DECLARE_NATIVE_FUNCTION(to_string_tag_setter);
};

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -12,7 +12,7 @@ namespace JS {
JS_DEFINE_ALLOCATOR(WrapForValidIteratorPrototype);
// 3.1.1.2.2.1 The %WrapForValidIteratorPrototype% Object, https://tc39.es/proposal-iterator-helpers/#sec-wrapforvaliditeratorprototype-object
// 27.1.3.2.1.1 The %WrapForValidIteratorPrototype% Object, https://tc39.es/ecma262/#sec-%wrapforvaliditeratorprototype%-object
WrapForValidIteratorPrototype::WrapForValidIteratorPrototype(Realm& realm)
: PrototypeObject(realm.intrinsics().iterator_prototype())
{
@ -28,7 +28,7 @@ void WrapForValidIteratorPrototype::initialize(Realm& realm)
define_native_function(realm, vm.names.return_, return_, 0, attr);
}
// 3.1.1.2.2.1.1 %WrapForValidIteratorPrototype%.next ( ), https://tc39.es/proposal-iterator-helpers/#sec-wrapforvaliditeratorprototype.next
// 27.1.3.2.1.1.1 %WrapForValidIteratorPrototype%.next ( ), https://tc39.es/ecma262/#sec-%wrapforvaliditeratorprototype%.next
JS_DEFINE_NATIVE_FUNCTION(WrapForValidIteratorPrototype::next)
{
// 1. Let O be this value.
@ -42,7 +42,7 @@ JS_DEFINE_NATIVE_FUNCTION(WrapForValidIteratorPrototype::next)
return TRY(call(vm, iterator_record.next_method, iterator_record.iterator));
}
// 3.1.1.2.2.1.2 %WrapForValidIteratorPrototype%.return ( ), https://tc39.es/proposal-iterator-helpers/#sec-wrapforvaliditeratorprototype.return
// 27.1.3.2.1.1.2 %WrapForValidIteratorPrototype%.return ( ), https://tc39.es/ecma262/#sec-%wrapforvaliditeratorprototype%.return
JS_DEFINE_NATIVE_FUNCTION(WrapForValidIteratorPrototype::return_)
{
// 1. Let O be this value.

View file

@ -2,7 +2,7 @@ describe("errors", () => {
test("called with non-Object", () => {
expect(() => {
Iterator.from(Symbol.hasInstance);
}).toThrowWithMessage(TypeError, "Symbol(Symbol.hasInstance) is not an object");
}).toThrowWithMessage(TypeError, "Symbol(Symbol.hasInstance) is not a string");
});
test("@@iterator is not callable", () => {