diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 817e6926f3..dc164743d1 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -1749,16 +1749,11 @@ Bytecode::CodeGenerationErrorOr YieldExpression::generate_bytecode(Bytecod // 5. Let iterator be iteratorRecord.[[Iterator]]. auto iterator_register = generator.allocate_register(); - auto iterator_identifier = generator.intern_identifier("iterator"); - generator.emit_get_by_id(iterator_identifier); - generator.emit(iterator_register); + generator.emit(iterator_register, iterator_record_register); // Cache iteratorRecord.[[NextMethod]] for use in step 7.a.i. auto next_method_register = generator.allocate_register(); - auto next_method_identifier = generator.intern_identifier("next"); - generator.emit(iterator_record_register); - generator.emit_get_by_id(next_method_identifier); - generator.emit(next_method_register); + generator.emit(next_method_register, iterator_record_register); // 6. Let received be NormalCompletion(undefined). // See get_received_completion_type_and_value above. diff --git a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp index 31c6c41207..a62a4f9567 100644 --- a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp +++ b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp @@ -654,31 +654,9 @@ ThrowCompletionOr> super_call_with_argument_array(VM& vm, V return result; } -// FIXME: Since the accumulator is a Value, we store an object there and have to convert back and forth between that an Iterator records. Not great. -// Make sure to put this into the accumulator before the iterator object disappears from the stack to prevent the members from being GC'd. -Object* iterator_to_object(VM& vm, IteratorRecord iterator) -{ - auto& realm = *vm.current_realm(); - auto object = Object::create(realm, nullptr); - object->define_direct_property(vm.names.iterator, iterator.iterator, 0); - object->define_direct_property(vm.names.next, iterator.next_method, 0); - object->define_direct_property(vm.names.done, Value(iterator.done), 0); - return object; -} - -IteratorRecord object_to_iterator(VM& vm, Object& object) -{ - return IteratorRecord { - .iterator = &MUST(object.get(vm.names.iterator)).as_object(), - .next_method = MUST(object.get(vm.names.next)), - .done = MUST(object.get(vm.names.done)).as_bool() - }; -} - ThrowCompletionOr> iterator_to_array(VM& vm, Value iterator) { - auto iterator_object = TRY(iterator.to_object(vm)); - auto iterator_record = object_to_iterator(vm, iterator_object); + auto& iterator_record = verify_cast(iterator.as_object()); auto array = MUST(Array::create(*vm.current_realm(), 0)); size_t index = 0; @@ -832,47 +810,42 @@ ThrowCompletionOr get_object_property_iterator(VM& vm, Value value) properties.set(move(property_key)); } } - IteratorRecord iterator { - .iterator = object, - .next_method = NativeFunction::create( - *vm.current_realm(), - [items = move(properties)](VM& vm) mutable -> ThrowCompletionOr { - auto& realm = *vm.current_realm(); - auto iterated_object_value = vm.this_value(); - if (!iterated_object_value.is_object()) - return vm.throw_completion("Invalid state for GetObjectPropertyIterator.next"sv); - - auto& iterated_object = iterated_object_value.as_object(); - auto result_object = Object::create(realm, nullptr); - while (true) { - if (items.is_empty()) { - result_object->define_direct_property(vm.names.done, JS::Value(true), default_attributes); - return result_object; - } - - auto key = items.take_first(); - - // If the property is deleted, don't include it (invariant no. 2) - if (!TRY(iterated_object.has_property(key))) - continue; - - result_object->define_direct_property(vm.names.done, JS::Value(false), default_attributes); - - if (key.is_number()) - result_object->define_direct_property(vm.names.value, PrimitiveString::create(vm, TRY_OR_THROW_OOM(vm, String::number(key.as_number()))), default_attributes); - else if (key.is_string()) - result_object->define_direct_property(vm.names.value, PrimitiveString::create(vm, key.as_string()), default_attributes); - else - VERIFY_NOT_REACHED(); // We should not have non-string/number keys. + auto& realm = *vm.current_realm(); + auto callback = NativeFunction::create( + *vm.current_realm(), [items = move(properties)](VM& vm) mutable -> ThrowCompletionOr { + auto& realm = *vm.current_realm(); + auto iterated_object_value = vm.this_value(); + if (!iterated_object_value.is_object()) + return vm.throw_completion("Invalid state for GetObjectPropertyIterator.next"sv); + auto& iterated_object = iterated_object_value.as_object(); + auto result_object = Object::create(realm, nullptr); + while (true) { + if (items.is_empty()) { + result_object->define_direct_property(vm.names.done, JS::Value(true), default_attributes); return result_object; } - }, - 1, - vm.names.next), - .done = false, - }; - return iterator_to_object(vm, move(iterator)); + + auto key = items.take_first(); + + // If the property is deleted, don't include it (invariant no. 2) + if (!TRY(iterated_object.has_property(key))) + continue; + + result_object->define_direct_property(vm.names.done, JS::Value(false), default_attributes); + + if (key.is_number()) + result_object->define_direct_property(vm.names.value, PrimitiveString::create(vm, TRY_OR_THROW_OOM(vm, String::number(key.as_number()))), default_attributes); + else if (key.is_string()) + result_object->define_direct_property(vm.names.value, PrimitiveString::create(vm, key.as_string()), default_attributes); + else + VERIFY_NOT_REACHED(); // We should not have non-string/number keys. + + return result_object; + } + }, + 1, vm.names.next); + return vm.heap().allocate(realm, realm, object, callback, false).ptr(); } } diff --git a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h index 1b683de5d6..242d0dfa5e 100644 --- a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h +++ b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h @@ -35,8 +35,6 @@ MarkedVector argument_list_evaluation(VM&, Value arguments); ThrowCompletionOr create_variable(VM&, DeprecatedFlyString const& name, Op::EnvironmentMode, bool is_global, bool is_immutable, bool is_strict); ThrowCompletionOr new_class(VM&, Value super_class, ClassExpression const&, Optional const& lhs_name); ThrowCompletionOr> super_call_with_argument_array(VM&, Value argument_array, bool is_synthetic); -Object* iterator_to_object(VM&, IteratorRecord); -IteratorRecord object_to_iterator(VM&, Object&); ThrowCompletionOr> iterator_to_array(VM&, Value iterator); ThrowCompletionOr append(VM& vm, Value lhs, Value rhs, bool is_spread); ThrowCompletionOr delete_by_id(Bytecode::Interpreter&, Value base, IdentifierTableIndex identifier); diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index 2a0f6c5adc..2721fbd9fa 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -46,8 +46,10 @@ O(GetByValueWithThis) \ O(GetCalleeAndThisFromEnvironment) \ O(GetIterator) \ + O(GetObjectFromIteratorRecord) \ O(GetMethod) \ O(GetNewTarget) \ + O(GetNextMethodFromIteratorRecord) \ O(GetImportMeta) \ O(GetObjectPropertyIterator) \ O(GetPrivateById) \ diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index 4531bc5a74..9922c3d2be 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -1225,8 +1225,21 @@ ThrowCompletionOr DeleteByValueWithThis::execute_impl(Bytecode::Interprete ThrowCompletionOr GetIterator::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - auto iterator = TRY(get_iterator(vm, interpreter.accumulator(), m_hint)); - interpreter.accumulator() = iterator_to_object(vm, iterator); + interpreter.accumulator() = TRY(get_iterator(vm, interpreter.accumulator(), m_hint)); + return {}; +} + +ThrowCompletionOr GetObjectFromIteratorRecord::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& iterator_record = verify_cast(interpreter.reg(m_iterator_record).as_object()); + interpreter.reg(m_object) = iterator_record.iterator; + return {}; +} + +ThrowCompletionOr GetNextMethodFromIteratorRecord::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& iterator_record = verify_cast(interpreter.reg(m_iterator_record).as_object()); + interpreter.reg(m_next_method) = iterator_record.next_method; return {}; } @@ -1248,8 +1261,7 @@ ThrowCompletionOr GetObjectPropertyIterator::execute_impl(Bytecode::Interp ThrowCompletionOr IteratorClose::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - auto iterator_object = TRY(interpreter.accumulator().to_object(vm)); - auto iterator = object_to_iterator(vm, iterator_object); + auto& iterator = verify_cast(interpreter.accumulator().as_object()); // FIXME: Return the value of the resulting completion. (Note that m_completion_value can be empty!) TRY(iterator_close(vm, iterator, Completion { m_completion_type, m_completion_value, {} })); @@ -1259,8 +1271,7 @@ ThrowCompletionOr IteratorClose::execute_impl(Bytecode::Interpreter& inter ThrowCompletionOr AsyncIteratorClose::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - auto iterator_object = TRY(interpreter.accumulator().to_object(vm)); - auto iterator = object_to_iterator(vm, iterator_object); + auto& iterator = verify_cast(interpreter.accumulator().as_object()); // FIXME: Return the value of the resulting completion. (Note that m_completion_value can be empty!) TRY(async_iterator_close(vm, iterator, Completion { m_completion_type, m_completion_value, {} })); @@ -1270,8 +1281,7 @@ ThrowCompletionOr AsyncIteratorClose::execute_impl(Bytecode::Interpreter& ThrowCompletionOr IteratorNext::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); - auto iterator_object = TRY(interpreter.accumulator().to_object(vm)); - auto iterator = object_to_iterator(vm, iterator_object); + auto& iterator = verify_cast(interpreter.accumulator().as_object()); interpreter.accumulator() = TRY(iterator_next(vm, iterator)); return {}; @@ -1818,4 +1828,14 @@ DeprecatedString Catch::to_deprecated_string_impl(Bytecode::Executable const&) c return "Catch"sv; } +DeprecatedString GetObjectFromIteratorRecord::to_deprecated_string_impl(Bytecode::Executable const&) const +{ + return DeprecatedString::formatted("GetObjectFromIteratorRecord object:{} <- iterator_record:{}", m_object, m_iterator_record); +} + +DeprecatedString GetNextMethodFromIteratorRecord::to_deprecated_string_impl(Bytecode::Executable const&) const +{ + return DeprecatedString::formatted("GetNextMethodFromIteratorRecord next_method:{} <- iterator_record:{}", m_next_method, m_iterator_record); +} + } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index abee872568..6079d5dbdc 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -1373,6 +1373,46 @@ private: IteratorHint m_hint { IteratorHint::Sync }; }; +class GetObjectFromIteratorRecord final : public Instruction { +public: + GetObjectFromIteratorRecord(Register object, Register iterator_record) + : Instruction(Type::GetObjectFromIteratorRecord, sizeof(*this)) + , m_object(object) + , m_iterator_record(iterator_record) + { + } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; + + Register object() const { return m_object; } + Register iterator_record() const { return m_iterator_record; } + +private: + Register m_object; + Register m_iterator_record; +}; + +class GetNextMethodFromIteratorRecord final : public Instruction { +public: + GetNextMethodFromIteratorRecord(Register next_method, Register iterator_record) + : Instruction(Type::GetNextMethodFromIteratorRecord, sizeof(*this)) + , m_next_method(next_method) + , m_iterator_record(iterator_record) + { + } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; + + Register next_method() const { return m_next_method; } + Register iterator_record() const { return m_iterator_record; } + +private: + Register m_next_method; + Register m_iterator_record; +}; + class GetMethod final : public Instruction { public: GetMethod(IdentifierTableIndex property) @@ -1551,7 +1591,6 @@ public: private: size_t m_index; }; - } namespace JS::Bytecode { diff --git a/Userland/Libraries/LibJS/Forward.h b/Userland/Libraries/LibJS/Forward.h index 323fc8f155..c902f122c4 100644 --- a/Userland/Libraries/LibJS/Forward.h +++ b/Userland/Libraries/LibJS/Forward.h @@ -187,7 +187,7 @@ struct ImportEntry; class ImportStatement; class Identifier; class Intrinsics; -struct IteratorRecord; +class IteratorRecord; class MemberExpression; class MetaProperty; class Module; diff --git a/Userland/Libraries/LibJS/JIT/Compiler.cpp b/Userland/Libraries/LibJS/JIT/Compiler.cpp index 40d5bcc27d..ee85a2e0d3 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.cpp +++ b/Userland/Libraries/LibJS/JIT/Compiler.cpp @@ -3059,8 +3059,7 @@ void Compiler::compile_super_call_with_argument_array(Bytecode::Op::SuperCallWit static Value cxx_get_iterator(VM& vm, Value value, IteratorHint hint) { - auto iterator = TRY_OR_SET_EXCEPTION(get_iterator(vm, value, hint)); - return Bytecode::iterator_to_object(vm, iterator); + return TRY_OR_SET_EXCEPTION(get_iterator(vm, value, hint)); } void Compiler::compile_get_iterator(Bytecode::Op::GetIterator const& op) @@ -3074,10 +3073,33 @@ void Compiler::compile_get_iterator(Bytecode::Op::GetIterator const& op) check_exception(); } +static Value cxx_get_object_from_iterator_record(VM&, Value value) +{ + return verify_cast(value.as_object()).iterator; +} + +void Compiler::compile_get_object_from_iterator_record(Bytecode::Op::GetObjectFromIteratorRecord const& op) +{ + load_vm_register(ARG1, op.iterator_record()); + native_call((void*)cxx_get_object_from_iterator_record); + store_vm_register(op.object(), RET); +} + +static Value cxx_next_method_from_iterator_record(VM&, Value value) +{ + return verify_cast(value.as_object()).next_method; +} + +void Compiler::compile_get_next_method_from_iterator_record(Bytecode::Op::GetNextMethodFromIteratorRecord const& op) +{ + load_vm_register(ARG1, op.iterator_record()); + native_call((void*)cxx_next_method_from_iterator_record); + store_vm_register(op.next_method(), RET); +} + static Value cxx_iterator_next(VM& vm, Value iterator) { - auto iterator_object = TRY_OR_SET_EXCEPTION(iterator.to_object(vm)); - auto iterator_record = Bytecode::object_to_iterator(vm, iterator_object); + auto& iterator_record = verify_cast(iterator.as_object()); return TRY_OR_SET_EXCEPTION(iterator_next(vm, iterator_record)); } @@ -3147,8 +3169,7 @@ void Compiler::compile_iterator_result_value(Bytecode::Op::IteratorResultValue c static Value cxx_iterator_close(VM& vm, Value iterator, Completion::Type completion_type, Optional const& completion_value) { - auto iterator_object = TRY_OR_SET_EXCEPTION(iterator.to_object(vm)); - auto iterator_record = Bytecode::object_to_iterator(vm, iterator_object); + auto& iterator_record = verify_cast(iterator.as_object()); // FIXME: Return the value of the resulting completion. (Note that m_completion_value can be empty!) TRY_OR_SET_EXCEPTION(iterator_close(vm, iterator_record, Completion { completion_type, completion_value, {} })); @@ -3565,8 +3586,7 @@ void Compiler::compile_copy_object_excluding_properties(Bytecode::Op::CopyObject static Value cxx_async_iterator_close(VM& vm, Value iterator, Completion::Type completion_type, Optional const& completion_value) { - auto iterator_object = TRY_OR_SET_EXCEPTION(iterator.to_object(vm)); - auto iterator_record = Bytecode::object_to_iterator(vm, iterator_object); + auto& iterator_record = verify_cast(iterator.as_object()); // FIXME: Return the value of the resulting completion. (Note that completion_value can be empty!) TRY_OR_SET_EXCEPTION(async_iterator_close(vm, iterator_record, Completion { completion_type, completion_value, {} })); diff --git a/Userland/Libraries/LibJS/JIT/Compiler.h b/Userland/Libraries/LibJS/JIT/Compiler.h index 8dc785b77d..ac88fd2769 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.h +++ b/Userland/Libraries/LibJS/JIT/Compiler.h @@ -114,6 +114,8 @@ private: O(BlockDeclarationInstantiation, block_declaration_instantiation) \ O(SuperCallWithArgumentArray, super_call_with_argument_array) \ O(GetIterator, get_iterator) \ + O(GetObjectFromIteratorRecord, get_object_from_iterator_record) \ + O(GetNextMethodFromIteratorRecord, get_next_method_from_iterator_record) \ O(IteratorNext, iterator_next) \ O(IteratorResultDone, iterator_result_done) \ O(ThrowIfNotObject, throw_if_not_object) \ diff --git a/Userland/Libraries/LibJS/Runtime/ArrayConstructor.cpp b/Userland/Libraries/LibJS/Runtime/ArrayConstructor.cpp index 4eeba5b47f..87a1e36795 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/ArrayConstructor.cpp @@ -338,7 +338,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async) } // e. Let iteratorRecord be undefined. - Optional iterator_record; + GCPtr iterator_record; // f. If usingAsyncIterator is not undefined, then if (using_async_iterator) { @@ -354,7 +354,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async) } // h. If iteratorRecord is not undefined, then - if (iterator_record.has_value()) { + if (iterator_record) { GCPtr array; // i. If IsConstructor(C) is true, then @@ -377,7 +377,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async) auto error = vm.throw_completion(ErrorType::ArrayMaxSize); // b. Return ? AsyncIteratorClose(iteratorRecord, error). - return *TRY(async_iterator_close(vm, iterator_record.value(), move(error))); + return *TRY(async_iterator_close(vm, *iterator_record, move(error))); } // 2. Let Pk be ! ToString(𝔽(k)). @@ -433,7 +433,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async) // b. IfAbruptCloseAsyncIterator(mappedValue, iteratorRecord). if (mapped_value_or_error.is_error()) { - TRY(async_iterator_close(vm, iterator_record.value(), mapped_value_or_error)); + TRY(async_iterator_close(vm, *iterator_record, mapped_value_or_error)); return mapped_value_or_error; } @@ -442,7 +442,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async) // d. IfAbruptCloseAsyncIterator(mappedValue, iteratorRecord). if (mapped_value_or_error.is_error()) { - TRY(async_iterator_close(vm, iterator_record.value(), mapped_value_or_error)); + TRY(async_iterator_close(vm, *iterator_record, mapped_value_or_error)); return mapped_value_or_error; } @@ -458,7 +458,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async) // 9. If defineStatus is an abrupt completion, return ? AsyncIteratorClose(iteratorRecord, defineStatus). if (define_status.is_error()) - return *TRY(iterator_close(vm, iterator_record.value(), define_status.release_error())); + return *TRY(iterator_close(vm, *iterator_record, define_status.release_error())); // 10. Set k to k + 1. } diff --git a/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.cpp b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.cpp index d226c2baad..aaf49e6848 100644 --- a/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.cpp +++ b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.cpp @@ -13,12 +13,12 @@ namespace JS { JS_DEFINE_ALLOCATOR(AsyncFromSyncIterator); -NonnullGCPtr AsyncFromSyncIterator::create(Realm& realm, IteratorRecord sync_iterator_record) +NonnullGCPtr AsyncFromSyncIterator::create(Realm& realm, NonnullGCPtr sync_iterator_record) { return realm.heap().allocate(realm, realm, sync_iterator_record); } -AsyncFromSyncIterator::AsyncFromSyncIterator(Realm& realm, IteratorRecord sync_iterator_record) +AsyncFromSyncIterator::AsyncFromSyncIterator(Realm& realm, NonnullGCPtr sync_iterator_record) : Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().async_from_sync_iterator_prototype()) , m_sync_iterator_record(sync_iterator_record) { @@ -27,8 +27,7 @@ AsyncFromSyncIterator::AsyncFromSyncIterator(Realm& realm, IteratorRecord sync_i void AsyncFromSyncIterator::visit_edges(Cell::Visitor& visitor) { Base::visit_edges(visitor); - visitor.visit(m_sync_iterator_record.iterator); - visitor.visit(m_sync_iterator_record.next_method); + visitor.visit(m_sync_iterator_record); } } diff --git a/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.h b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.h index 4088fd07c7..747fedb61f 100644 --- a/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.h +++ b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIterator.h @@ -18,7 +18,7 @@ class AsyncFromSyncIterator final : public Object { JS_DECLARE_ALLOCATOR(AsyncFromSyncIterator); public: - static NonnullGCPtr create(Realm&, IteratorRecord sync_iterator_record); + static NonnullGCPtr create(Realm&, NonnullGCPtr sync_iterator_record); virtual ~AsyncFromSyncIterator() override = default; @@ -28,9 +28,9 @@ public: IteratorRecord const& sync_iterator_record() const { return m_sync_iterator_record; } private: - AsyncFromSyncIterator(Realm&, IteratorRecord sync_iterator_record); + AsyncFromSyncIterator(Realm&, NonnullGCPtr sync_iterator_record); - IteratorRecord m_sync_iterator_record; // [[SyncIteratorRecord]] + NonnullGCPtr m_sync_iterator_record; // [[SyncIteratorRecord]] }; } diff --git a/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.cpp b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.cpp index 0ce3388170..141c637a80 100644 --- a/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.cpp @@ -199,7 +199,7 @@ JS_DEFINE_NATIVE_FUNCTION(AsyncFromSyncIteratorPrototype::throw_) } // 27.1.4.1 CreateAsyncFromSyncIterator ( syncIteratorRecord ), https://tc39.es/ecma262/#sec-createasyncfromsynciterator -IteratorRecord create_async_from_sync_iterator(VM& vm, IteratorRecord sync_iterator_record) +NonnullGCPtr create_async_from_sync_iterator(VM& vm, NonnullGCPtr sync_iterator_record) { auto& realm = *vm.current_realm(); @@ -211,7 +211,7 @@ IteratorRecord create_async_from_sync_iterator(VM& vm, IteratorRecord sync_itera auto next_method = MUST(async_iterator->get(vm.names.next)); // 4. Let iteratorRecord be the Iterator Record { [[Iterator]]: asyncIterator, [[NextMethod]]: nextMethod, [[Done]]: false }. - auto iterator_record = IteratorRecord { .iterator = async_iterator, .next_method = next_method, .done = false }; + auto iterator_record = vm.heap().allocate(realm, realm, async_iterator, next_method, false); // 5. Return iteratorRecord. return iterator_record; diff --git a/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.h b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.h index 3eb60afe84..684fc90de2 100644 --- a/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.h @@ -31,6 +31,6 @@ private: JS_DECLARE_NATIVE_FUNCTION(throw_); }; -IteratorRecord create_async_from_sync_iterator(VM&, IteratorRecord sync_iterator); +NonnullGCPtr create_async_from_sync_iterator(VM&, NonnullGCPtr sync_iterator); } diff --git a/Userland/Libraries/LibJS/Runtime/Iterator.cpp b/Userland/Libraries/LibJS/Runtime/Iterator.cpp index 4eaf3d41a5..d6ba888d02 100644 --- a/Userland/Libraries/LibJS/Runtime/Iterator.cpp +++ b/Userland/Libraries/LibJS/Runtime/Iterator.cpp @@ -17,25 +17,26 @@ namespace JS { JS_DEFINE_ALLOCATOR(Iterator); +JS_DEFINE_ALLOCATOR(IteratorRecord); -NonnullGCPtr Iterator::create(Realm& realm, Object& prototype, IteratorRecord iterated) +NonnullGCPtr Iterator::create(Realm& realm, Object& prototype, NonnullGCPtr iterated) { return realm.heap().allocate(realm, prototype, move(iterated)); } -Iterator::Iterator(Object& prototype, IteratorRecord iterated) +Iterator::Iterator(Object& prototype, NonnullGCPtr iterated) : Object(ConstructWithPrototypeTag::Tag, prototype) , m_iterated(move(iterated)) { } Iterator::Iterator(Object& prototype) - : Iterator(prototype, {}) + : Iterator(prototype, prototype.heap().allocate(prototype.shape().realm(), prototype.shape().realm(), nullptr, js_undefined(), false)) { } // 7.4.2 GetIteratorFromMethod ( obj, method ), https://tc39.es/ecma262/#sec-getiteratorfrommethod -ThrowCompletionOr get_iterator_from_method(VM& vm, Value object, NonnullGCPtr method) +ThrowCompletionOr> get_iterator_from_method(VM& vm, Value object, NonnullGCPtr method) { // 1. Let iterator be ? Call(method, obj). auto iterator = TRY(call(vm, *method, object)); @@ -48,14 +49,15 @@ ThrowCompletionOr get_iterator_from_method(VM& vm, Value object, auto next_method = TRY(iterator.get(vm, vm.names.next)); // 4. Let iteratorRecord be the Iterator Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }. - auto iterator_record = IteratorRecord { .iterator = &iterator.as_object(), .next_method = next_method, .done = false }; + auto& realm = *vm.current_realm(); + auto iterator_record = vm.heap().allocate(realm, realm, iterator.as_object(), next_method, false); // 5. Return iteratorRecord. return iterator_record; } // 7.4.3 GetIterator ( obj, kind ), https://tc39.es/ecma262/#sec-getiterator -ThrowCompletionOr get_iterator(VM& vm, Value object, IteratorHint kind) +ThrowCompletionOr> get_iterator(VM& vm, Value object, IteratorHint kind) { JS::GCPtr method; @@ -95,20 +97,19 @@ ThrowCompletionOr get_iterator(VM& vm, Value object, IteratorHin } // 2.1.1 GetIteratorDirect ( obj ), https://tc39.es/proposal-iterator-helpers/#sec-getiteratorflattenable -ThrowCompletionOr get_iterator_direct(VM& vm, Object& object) +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 }. - IteratorRecord iterator_record { .iterator = object, .next_method = next_method, .done = false }; - // 3. Return iteratorRecord. - return iterator_record; + 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) +ThrowCompletionOr> get_iterator_flattenable(VM& vm, Value object, StringHandling string_handling) { // 1. If obj is not an Object, then if (!object.is_object()) { @@ -320,4 +321,17 @@ Completion get_iterator_values(VM& vm, Value iterable, IteratorValueCallback cal } } +void Iterator::visit_edges(Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_iterated); +} + +void IteratorRecord::visit_edges(Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(iterator); + visitor.visit(next_method); +} + } diff --git a/Userland/Libraries/LibJS/Runtime/Iterator.h b/Userland/Libraries/LibJS/Runtime/Iterator.h index 56b7afe974..1c4fd4f80e 100644 --- a/Userland/Libraries/LibJS/Runtime/Iterator.h +++ b/Userland/Libraries/LibJS/Runtime/Iterator.h @@ -17,26 +17,47 @@ namespace JS { // 7.4.1 Iterator Records, https://tc39.es/ecma262/#sec-iterator-records -struct IteratorRecord { +class IteratorRecord final : public Object { + JS_OBJECT(IteratorRecord, Object); + JS_DECLARE_ALLOCATOR(IteratorRecord); + +public: + IteratorRecord(Realm& realm, GCPtr iterator, Value next_method, bool done) + : Object(ConstructWithoutPrototypeTag::Tag, realm) + , iterator(iterator) + , next_method(next_method) + , done(done) + { + } + GCPtr iterator; // [[Iterator]] Value next_method; // [[NextMethod]] bool done { false }; // [[Done]] + +private: + virtual void visit_edges(Cell::Visitor&) override; + virtual bool is_iterator_record() const override { return true; } }; +template<> +inline bool Object::fast_is() const { return is_iterator_record(); } + class Iterator : public Object { JS_OBJECT(Iterator, Object); JS_DECLARE_ALLOCATOR(Iterator); public: - static NonnullGCPtr create(Realm&, Object& prototype, IteratorRecord iterated); + static NonnullGCPtr create(Realm&, Object& prototype, NonnullGCPtr iterated); IteratorRecord const& iterated() const { return m_iterated; } private: - Iterator(Object& prototype, IteratorRecord iterated); + Iterator(Object& prototype, NonnullGCPtr iterated); explicit Iterator(Object& prototype); - IteratorRecord m_iterated; // [[Iterated]] + virtual void visit_edges(Cell::Visitor&) override; + + NonnullGCPtr m_iterated; // [[Iterated]] }; enum class IteratorHint { @@ -49,10 +70,10 @@ enum class StringHandling { RejectStrings, }; -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_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> iterator_next(VM&, IteratorRecord const&, Optional = {}); ThrowCompletionOr> iterator_step(VM&, IteratorRecord const&); ThrowCompletionOr iterator_complete(VM&, Object& iterator_result); diff --git a/Userland/Libraries/LibJS/Runtime/IteratorConstructor.cpp b/Userland/Libraries/LibJS/Runtime/IteratorConstructor.cpp index 4fbda56e13..9b30307e05 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/IteratorConstructor.cpp @@ -70,12 +70,12 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorConstructor::from) auto iterator_record = TRY(get_iterator_flattenable(vm, object, StringHandling::IterateStrings)); // 2. Let hasInstance be ? OrdinaryHasInstance(%Iterator%, iteratorRecord.[[Iterator]]). - auto has_instance = TRY(ordinary_has_instance(vm, iterator_record.iterator, realm.intrinsics().iterator_constructor())); + auto has_instance = TRY(ordinary_has_instance(vm, iterator_record->iterator, realm.intrinsics().iterator_constructor())); // 3. If hasInstance is true, then if (has_instance.is_boolean() && has_instance.as_bool()) { // a. Return iteratorRecord.[[Iterator]]. - return iterator_record.iterator; + return iterator_record->iterator; } // 4. Let wrapper be OrdinaryObjectCreate(%WrapForValidIteratorPrototype%, « [[Iterated]] »). diff --git a/Userland/Libraries/LibJS/Runtime/IteratorHelper.cpp b/Userland/Libraries/LibJS/Runtime/IteratorHelper.cpp index 194c591909..06f40ff6fe 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorHelper.cpp +++ b/Userland/Libraries/LibJS/Runtime/IteratorHelper.cpp @@ -13,12 +13,12 @@ namespace JS { JS_DEFINE_ALLOCATOR(IteratorHelper); -ThrowCompletionOr> IteratorHelper::create(Realm& realm, IteratorRecord underlying_iterator, Closure closure, Optional abrupt_closure) +ThrowCompletionOr> IteratorHelper::create(Realm& realm, NonnullGCPtr underlying_iterator, Closure closure, Optional abrupt_closure) { return realm.heap().allocate(realm, realm, realm.intrinsics().iterator_helper_prototype(), move(underlying_iterator), move(closure), move(abrupt_closure)); } -IteratorHelper::IteratorHelper(Realm& realm, Object& prototype, IteratorRecord underlying_iterator, Closure closure, Optional abrupt_closure) +IteratorHelper::IteratorHelper(Realm& realm, Object& prototype, NonnullGCPtr underlying_iterator, Closure closure, Optional abrupt_closure) : GeneratorObject(realm, prototype, realm.vm().running_execution_context().copy(), "Iterator Helper"sv) , m_underlying_iterator(move(underlying_iterator)) , m_closure(move(closure)) @@ -29,7 +29,7 @@ IteratorHelper::IteratorHelper(Realm& realm, Object& prototype, IteratorRecord u void IteratorHelper::visit_edges(Visitor& visitor) { Base::visit_edges(visitor); - visitor.visit(m_underlying_iterator.iterator); + visitor.visit(m_underlying_iterator); } Value IteratorHelper::result(Value value) diff --git a/Userland/Libraries/LibJS/Runtime/IteratorHelper.h b/Userland/Libraries/LibJS/Runtime/IteratorHelper.h index b474fd0c1d..11f335436a 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorHelper.h +++ b/Userland/Libraries/LibJS/Runtime/IteratorHelper.h @@ -22,7 +22,7 @@ public: using Closure = JS::SafeFunction(VM&, IteratorHelper&)>; using AbruptClosure = JS::SafeFunction(VM&, IteratorHelper&, Completion const&)>; - static ThrowCompletionOr> create(Realm&, IteratorRecord, Closure, Optional = {}); + static ThrowCompletionOr> create(Realm&, NonnullGCPtr, Closure, Optional = {}); IteratorRecord const& underlying_iterator() const { return m_underlying_iterator; } @@ -33,12 +33,12 @@ public: ThrowCompletionOr close_result(VM&, Completion); private: - IteratorHelper(Realm&, Object& prototype, IteratorRecord, Closure, Optional); + IteratorHelper(Realm&, Object& prototype, NonnullGCPtr, Closure, Optional); virtual void visit_edges(Visitor&) override; virtual ThrowCompletionOr execute(VM&, JS::Completion const& completion) override; - IteratorRecord m_underlying_iterator; // [[UnderlyingIterator]] + NonnullGCPtr m_underlying_iterator; // [[UnderlyingIterator]] Closure m_closure; Optional m_abrupt_closure; diff --git a/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp index 243c311929..9f5ca879ce 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp @@ -318,7 +318,7 @@ class FlatMapIterator : public Cell { public: ThrowCompletionOr next(VM& vm, IteratorRecord const& iterated, IteratorHelper& iterator, FunctionObject& mapper) { - if (m_inner_iterator.has_value()) + if (m_inner_iterator) return next_inner_iterator(vm, iterated, iterator, mapper); return next_outer_iterator(vm, iterated, iterator, mapper); } @@ -326,7 +326,7 @@ public: // NOTE: This implements step 5.b.ix.4.d of Iterator.prototype.flatMap. ThrowCompletionOr on_abrupt_completion(VM& vm, IteratorHelper& iterator, Completion const& completion) { - VERIFY(m_inner_iterator.has_value()); + VERIFY(m_inner_iterator); // d. If completion is an abrupt completion, then @@ -347,9 +347,7 @@ private: virtual void visit_edges(Visitor& visitor) override { Base::visit_edges(visitor); - - if (m_inner_iterator.has_value()) - visitor.visit(m_inner_iterator->iterator); + visitor.visit(m_inner_iterator); } ThrowCompletionOr next_outer_iterator(VM& vm, IteratorRecord const& iterated, IteratorHelper& iterator, FunctionObject& mapper) @@ -391,7 +389,7 @@ private: ThrowCompletionOr next_inner_iterator(VM& vm, IteratorRecord const& iterated, IteratorHelper& iterator, FunctionObject& mapper) { - VERIFY(m_inner_iterator.has_value()); + VERIFY(m_inner_iterator); // 1. Let innerNext be Completion(IteratorStep(innerIterator)). auto inner_next = iterator_step(vm, *m_inner_iterator); @@ -403,7 +401,7 @@ private: // 3. If innerNext is false, then if (!inner_next.value()) { // a. Set innerAlive to false. - m_inner_iterator.clear(); + m_inner_iterator = nullptr; return next_outer_iterator(vm, iterated, iterator, mapper); } @@ -422,7 +420,7 @@ private: } } - Optional m_inner_iterator; + GCPtr m_inner_iterator; }; JS_DEFINE_ALLOCATOR(FlatMapIterator); diff --git a/Userland/Libraries/LibJS/Runtime/Object.h b/Userland/Libraries/LibJS/Runtime/Object.h index 33fd3d3b11..1525feee5f 100644 --- a/Userland/Libraries/LibJS/Runtime/Object.h +++ b/Userland/Libraries/LibJS/Runtime/Object.h @@ -189,6 +189,7 @@ public: virtual bool is_proxy_object() const { return false; } virtual bool is_native_function() const { return false; } virtual bool is_ecmascript_function_object() const { return false; } + virtual bool is_iterator_record() const { return false; } // B.3.7 The [[IsHTMLDDA]] Internal Slot, https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot virtual bool is_htmldda() const { return false; } diff --git a/Userland/Libraries/LibJS/Runtime/PromiseConstructor.cpp b/Userland/Libraries/LibJS/Runtime/PromiseConstructor.cpp index 7039c2457a..e24b9bcc08 100644 --- a/Userland/Libraries/LibJS/Runtime/PromiseConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/PromiseConstructor.cpp @@ -333,7 +333,7 @@ JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::all) // 8. If result is an abrupt completion, then if (result.is_error()) { // a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)). - if (!iterator_record.done) + if (!iterator_record->done) result = iterator_close(vm, iterator_record, result.release_error()); // b. IfAbruptRejectPromise(result, promiseCapability). @@ -367,7 +367,7 @@ JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::all_settled) // 8. If result is an abrupt completion, then if (result.is_error()) { // a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)). - if (!iterator_record.done) + if (!iterator_record->done) result = iterator_close(vm, iterator_record, result.release_error()); // b. IfAbruptRejectPromise(result, promiseCapability). @@ -401,7 +401,7 @@ JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::any) // 8. If result is an abrupt completion, then if (result.is_error()) { // a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)). - if (!iterator_record.done) + if (!iterator_record->done) result = iterator_close(vm, iterator_record, result.release_error()); // b. IfAbruptRejectPromise(result, promiseCapability). @@ -435,7 +435,7 @@ JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::race) // 8. If result is an abrupt completion, then if (result.is_error()) { // a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)). - if (!iterator_record.done) + if (!iterator_record->done) result = iterator_close(vm, iterator_record, result.release_error()); // b. IfAbruptRejectPromise(result, promiseCapability). diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp index 6f99bfdc93..3cea062a37 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.cpp +++ b/Userland/Libraries/LibJS/Runtime/VM.cpp @@ -226,7 +226,7 @@ ThrowCompletionOr VM::binding_initialization(NonnullRefPtrdone) { // iterator_close() always returns a Completion, which ThrowCompletionOr will interpret as a throw // completion. So only return the result of iterator_close() if it is indeed a throw completion. auto completion = result.is_throw_completion() ? result.release_error() : normal_completion({});