From 84ad36de0692b8890a2aa7ab66ed4d679cf630c8 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Wed, 23 Oct 2024 14:34:35 -0400 Subject: [PATCH] LibJS: Update spec numbers for the Iterator Helpers proposal This proposal has reached stage 4 and was merged into the ECMA-262 spec. See: https://github.com/tc39/ecma262/commit/961f269 --- Userland/Libraries/LibJS/Runtime/Iterator.cpp | 71 +- Userland/Libraries/LibJS/Runtime/Iterator.h | 16 +- .../LibJS/Runtime/IteratorConstructor.cpp | 18 +- .../LibJS/Runtime/IteratorHelperPrototype.cpp | 9 +- .../LibJS/Runtime/IteratorPrototype.cpp | 665 +++++++++--------- .../LibJS/Runtime/IteratorPrototype.h | 18 +- .../Runtime/WrapForValidIteratorPrototype.cpp | 8 +- .../Tests/builtins/Iterator/Iterator.from.js | 2 +- 8 files changed, 408 insertions(+), 399 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/Iterator.cpp b/Userland/Libraries/LibJS/Runtime/Iterator.cpp index b37bee16748..63014028875 100644 --- a/Userland/Libraries/LibJS/Runtime/Iterator.cpp +++ b/Userland/Libraries/LibJS/Runtime/Iterator.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2020, Matthew Olsson * Copyright (c) 2022-2023, Linus Groh - * Copyright (c) 2023, Tim Flynn + * Copyright (c) 2023-2024, Tim Flynn * * 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> 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(realm, realm, object, next_method, false); +} + +// 7.4.3 GetIteratorFromMethod ( obj, method ), https://tc39.es/ecma262/#sec-getiteratorfrommethod ThrowCompletionOr> get_iterator_from_method(VM& vm, Value object, NonnullGCPtr method) { // 1. Let iterator be ? Call(method, obj). @@ -56,7 +68,7 @@ ThrowCompletionOr> 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> get_iterator(VM& vm, Value object, IteratorHint kind) { JS::GCPtr method; @@ -96,29 +108,24 @@ ThrowCompletionOr> 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> 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(realm, realm, object, next_method, false); -} - -// 2.1.2 GetIteratorFlattenable ( obj, stringHandling ), https://tc39.es/proposal-iterator-helpers/#sec-getiteratorflattenable -ThrowCompletionOr> get_iterator_flattenable(VM& vm, Value object, StringHandling string_handling) +// 7.4.5 GetIteratorFlattenable ( obj, primitiveHandling ), https://tc39.es/ecma262/#sec-getiteratorflattenable +ThrowCompletionOr> 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(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(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> 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> iterator_next(VM& vm, IteratorRecord& iterator_record, Optional value) { auto result = [&]() { @@ -183,21 +190,21 @@ ThrowCompletionOr> 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 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 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> iterator_step(VM& vm, IteratorRecord& iterator_record) { // 1. Let result be ? IteratorNext(iteratorRecord). @@ -231,7 +238,7 @@ ThrowCompletionOr> 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> iterator_step_value(VM& vm, IteratorRecord& iterator_record) { // 1. Let result be ? IteratorStep(iteratorRecord). @@ -256,8 +263,8 @@ ThrowCompletionOr> 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 create_iterator_result_object(VM& vm, Value value, bool done) { auto& realm = *vm.current_realm(); @@ -337,7 +344,7 @@ NonnullGCPtr 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> iterator_to_list(VM& vm, IteratorRecord& iterator_record) { // 1. Let values be a new empty List. @@ -359,7 +366,7 @@ ThrowCompletionOr> 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 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 diff --git a/Userland/Libraries/LibJS/Runtime/Iterator.h b/Userland/Libraries/LibJS/Runtime/Iterator.h index 593eeb6fc01..d409772e25d 100644 --- a/Userland/Libraries/LibJS/Runtime/Iterator.h +++ b/Userland/Libraries/LibJS/Runtime/Iterator.h @@ -1,7 +1,7 @@ /* * Copyright (c) 2020, Matthew Olsson * Copyright (c) 2022-2023, Linus Groh - * Copyright (c) 2023, Tim Flynn + * Copyright (c) 2023-2024, Tim Flynn * * 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> get_iterator_direct(VM&, Object&); ThrowCompletionOr> get_iterator_from_method(VM&, Value, NonnullGCPtr); ThrowCompletionOr> get_iterator(VM&, Value, IteratorHint); -ThrowCompletionOr> get_iterator_direct(VM&, Object&); -ThrowCompletionOr> get_iterator_flattenable(VM&, Value, StringHandling); +ThrowCompletionOr> get_iterator_flattenable(VM&, Value, PrimitiveHandling); ThrowCompletionOr> iterator_next(VM&, IteratorRecord&, Optional = {}); -ThrowCompletionOr> iterator_step(VM&, IteratorRecord&); -ThrowCompletionOr> iterator_step_value(VM&, IteratorRecord&); ThrowCompletionOr iterator_complete(VM&, Object& iterator_result); ThrowCompletionOr iterator_value(VM&, Object& iterator_result); +ThrowCompletionOr> iterator_step(VM&, IteratorRecord&); +ThrowCompletionOr> iterator_step_value(VM&, IteratorRecord&); Completion iterator_close(VM&, IteratorRecord const&, Completion); Completion async_iterator_close(VM&, IteratorRecord const&, Completion); NonnullGCPtr create_iterator_result_object(VM&, Value, bool done); diff --git a/Userland/Libraries/LibJS/Runtime/IteratorConstructor.cpp b/Userland/Libraries/LibJS/Runtime/IteratorConstructor.cpp index 9b30307e052..dcc70b6a050 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/IteratorConstructor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Tim Flynn + * Copyright (c) 2023-2024, Tim Flynn * * 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 IteratorConstructor::call() { auto& vm = this->vm(); @@ -46,7 +46,7 @@ ThrowCompletionOr IteratorConstructor::call() return vm.throw_completion(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> IteratorConstructor::construct(FunctionObject& new_target) { auto& vm = this->vm(); @@ -59,15 +59,15 @@ ThrowCompletionOr> IteratorConstructor::construct(FunctionO return TRY(ordinary_create_from_constructor(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; diff --git a/Userland/Libraries/LibJS/Runtime/IteratorHelperPrototype.cpp b/Userland/Libraries/LibJS/Runtime/IteratorHelperPrototype.cpp index 2ddba9b6fb4..1b932a2a97d 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorHelperPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/IteratorHelperPrototype.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Tim Flynn + * Copyright (c) 2023-2024, Tim Flynn * * 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. diff --git a/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp index 77f958e14b8..53bc565ba36 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Matthew Olsson + * Copyright (c) 2024, Tim Flynn * * 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(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 { - 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(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 { - 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(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(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 { - 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(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(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 { + 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(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(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(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 { + 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 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(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(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(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(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 { + 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(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 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(); diff --git a/Userland/Libraries/LibJS/Runtime/IteratorPrototype.h b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.h index 5382092e483..744422feb73 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.h @@ -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); }; diff --git a/Userland/Libraries/LibJS/Runtime/WrapForValidIteratorPrototype.cpp b/Userland/Libraries/LibJS/Runtime/WrapForValidIteratorPrototype.cpp index a6c3e8169f9..05b49c18320 100644 --- a/Userland/Libraries/LibJS/Runtime/WrapForValidIteratorPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/WrapForValidIteratorPrototype.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Tim Flynn + * Copyright (c) 2023-2024, Tim Flynn * * 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. diff --git a/Userland/Libraries/LibJS/Tests/builtins/Iterator/Iterator.from.js b/Userland/Libraries/LibJS/Tests/builtins/Iterator/Iterator.from.js index de45d8d2224..27a1ab5bc06 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Iterator/Iterator.from.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Iterator/Iterator.from.js @@ -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", () => {