mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 09:04:59 +00:00
LibJS: Stop converting between Object <-> IteratorRecord all the time
This patch makes IteratorRecord an Object. Although it's not exposed to author code, this does allow us to store it in a VM register. Now that we can store it in a VM register, we don't need to convert it back and forth between IteratorRecord and Object when accessing it from bytecode. The big win here is avoiding 3 [[Get]] accesses on every iteration step of for..of loops. There are also a bunch of smaller efficiencies gained. 20% speed-up on this microbenchmark: function go(a) { for (const p of a) { } } const a = []; a.length = 1_000_000; go(a);
This commit is contained in:
parent
4966c083df
commit
4699c81fc1
23 changed files with 226 additions and 144 deletions
|
@ -1749,16 +1749,11 @@ Bytecode::CodeGenerationErrorOr<void> 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<Bytecode::Op::Store>(iterator_register);
|
||||
generator.emit<Bytecode::Op::GetObjectFromIteratorRecord>(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<Bytecode::Op::Load>(iterator_record_register);
|
||||
generator.emit_get_by_id(next_method_identifier);
|
||||
generator.emit<Bytecode::Op::Store>(next_method_register);
|
||||
generator.emit<Bytecode::Op::GetNextMethodFromIteratorRecord>(next_method_register, iterator_record_register);
|
||||
|
||||
// 6. Let received be NormalCompletion(undefined).
|
||||
// See get_received_completion_type_and_value above.
|
||||
|
|
|
@ -654,31 +654,9 @@ ThrowCompletionOr<NonnullGCPtr<Object>> 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<NonnullGCPtr<Array>> 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<IteratorRecord>(iterator.as_object());
|
||||
|
||||
auto array = MUST(Array::create(*vm.current_realm(), 0));
|
||||
size_t index = 0;
|
||||
|
@ -832,47 +810,42 @@ ThrowCompletionOr<Object*> 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<Value> {
|
||||
auto& realm = *vm.current_realm();
|
||||
auto iterated_object_value = vm.this_value();
|
||||
if (!iterated_object_value.is_object())
|
||||
return vm.throw_completion<InternalError>("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<Value> {
|
||||
auto& realm = *vm.current_realm();
|
||||
auto iterated_object_value = vm.this_value();
|
||||
if (!iterated_object_value.is_object())
|
||||
return vm.throw_completion<InternalError>("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<IteratorRecord>(realm, realm, object, callback, false).ptr();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -35,8 +35,6 @@ MarkedVector<Value> argument_list_evaluation(VM&, Value arguments);
|
|||
ThrowCompletionOr<void> create_variable(VM&, DeprecatedFlyString const& name, Op::EnvironmentMode, bool is_global, bool is_immutable, bool is_strict);
|
||||
ThrowCompletionOr<ECMAScriptFunctionObject*> new_class(VM&, Value super_class, ClassExpression const&, Optional<IdentifierTableIndex> const& lhs_name);
|
||||
ThrowCompletionOr<NonnullGCPtr<Object>> 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<NonnullGCPtr<Array>> iterator_to_array(VM&, Value iterator);
|
||||
ThrowCompletionOr<void> append(VM& vm, Value lhs, Value rhs, bool is_spread);
|
||||
ThrowCompletionOr<Value> delete_by_id(Bytecode::Interpreter&, Value base, IdentifierTableIndex identifier);
|
||||
|
|
|
@ -46,8 +46,10 @@
|
|||
O(GetByValueWithThis) \
|
||||
O(GetCalleeAndThisFromEnvironment) \
|
||||
O(GetIterator) \
|
||||
O(GetObjectFromIteratorRecord) \
|
||||
O(GetMethod) \
|
||||
O(GetNewTarget) \
|
||||
O(GetNextMethodFromIteratorRecord) \
|
||||
O(GetImportMeta) \
|
||||
O(GetObjectPropertyIterator) \
|
||||
O(GetPrivateById) \
|
||||
|
|
|
@ -1225,8 +1225,21 @@ ThrowCompletionOr<void> DeleteByValueWithThis::execute_impl(Bytecode::Interprete
|
|||
ThrowCompletionOr<void> 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<void> GetObjectFromIteratorRecord::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& iterator_record = verify_cast<IteratorRecord>(interpreter.reg(m_iterator_record).as_object());
|
||||
interpreter.reg(m_object) = iterator_record.iterator;
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> GetNextMethodFromIteratorRecord::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& iterator_record = verify_cast<IteratorRecord>(interpreter.reg(m_iterator_record).as_object());
|
||||
interpreter.reg(m_next_method) = iterator_record.next_method;
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -1248,8 +1261,7 @@ ThrowCompletionOr<void> GetObjectPropertyIterator::execute_impl(Bytecode::Interp
|
|||
ThrowCompletionOr<void> 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<IteratorRecord>(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<void> IteratorClose::execute_impl(Bytecode::Interpreter& inter
|
|||
ThrowCompletionOr<void> 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<IteratorRecord>(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<void> AsyncIteratorClose::execute_impl(Bytecode::Interpreter&
|
|||
ThrowCompletionOr<void> 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<IteratorRecord>(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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<void> 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<void> 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 {
|
||||
|
|
|
@ -187,7 +187,7 @@ struct ImportEntry;
|
|||
class ImportStatement;
|
||||
class Identifier;
|
||||
class Intrinsics;
|
||||
struct IteratorRecord;
|
||||
class IteratorRecord;
|
||||
class MemberExpression;
|
||||
class MetaProperty;
|
||||
class Module;
|
||||
|
|
|
@ -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<IteratorRecord>(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<IteratorRecord>(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<IteratorRecord>(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<Value> 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<IteratorRecord>(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<Value> 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<IteratorRecord>(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, {} }));
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -338,7 +338,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async)
|
|||
}
|
||||
|
||||
// e. Let iteratorRecord be undefined.
|
||||
Optional<IteratorRecord> iterator_record;
|
||||
GCPtr<IteratorRecord> 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<Object> array;
|
||||
|
||||
// i. If IsConstructor(C) is true, then
|
||||
|
@ -377,7 +377,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async)
|
|||
auto error = vm.throw_completion<TypeError>(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.
|
||||
}
|
||||
|
|
|
@ -13,12 +13,12 @@ namespace JS {
|
|||
|
||||
JS_DEFINE_ALLOCATOR(AsyncFromSyncIterator);
|
||||
|
||||
NonnullGCPtr<AsyncFromSyncIterator> AsyncFromSyncIterator::create(Realm& realm, IteratorRecord sync_iterator_record)
|
||||
NonnullGCPtr<AsyncFromSyncIterator> AsyncFromSyncIterator::create(Realm& realm, NonnullGCPtr<IteratorRecord> sync_iterator_record)
|
||||
{
|
||||
return realm.heap().allocate<AsyncFromSyncIterator>(realm, realm, sync_iterator_record);
|
||||
}
|
||||
|
||||
AsyncFromSyncIterator::AsyncFromSyncIterator(Realm& realm, IteratorRecord sync_iterator_record)
|
||||
AsyncFromSyncIterator::AsyncFromSyncIterator(Realm& realm, NonnullGCPtr<IteratorRecord> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ class AsyncFromSyncIterator final : public Object {
|
|||
JS_DECLARE_ALLOCATOR(AsyncFromSyncIterator);
|
||||
|
||||
public:
|
||||
static NonnullGCPtr<AsyncFromSyncIterator> create(Realm&, IteratorRecord sync_iterator_record);
|
||||
static NonnullGCPtr<AsyncFromSyncIterator> create(Realm&, NonnullGCPtr<IteratorRecord> 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<IteratorRecord> sync_iterator_record);
|
||||
|
||||
IteratorRecord m_sync_iterator_record; // [[SyncIteratorRecord]]
|
||||
NonnullGCPtr<IteratorRecord> m_sync_iterator_record; // [[SyncIteratorRecord]]
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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<IteratorRecord> create_async_from_sync_iterator(VM& vm, NonnullGCPtr<IteratorRecord> 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<IteratorRecord>(realm, realm, async_iterator, next_method, false);
|
||||
|
||||
// 5. Return iteratorRecord.
|
||||
return iterator_record;
|
||||
|
|
|
@ -31,6 +31,6 @@ private:
|
|||
JS_DECLARE_NATIVE_FUNCTION(throw_);
|
||||
};
|
||||
|
||||
IteratorRecord create_async_from_sync_iterator(VM&, IteratorRecord sync_iterator);
|
||||
NonnullGCPtr<IteratorRecord> create_async_from_sync_iterator(VM&, NonnullGCPtr<IteratorRecord> sync_iterator);
|
||||
|
||||
}
|
||||
|
|
|
@ -17,25 +17,26 @@
|
|||
namespace JS {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(Iterator);
|
||||
JS_DEFINE_ALLOCATOR(IteratorRecord);
|
||||
|
||||
NonnullGCPtr<Iterator> Iterator::create(Realm& realm, Object& prototype, IteratorRecord iterated)
|
||||
NonnullGCPtr<Iterator> Iterator::create(Realm& realm, Object& prototype, NonnullGCPtr<IteratorRecord> iterated)
|
||||
{
|
||||
return realm.heap().allocate<Iterator>(realm, prototype, move(iterated));
|
||||
}
|
||||
|
||||
Iterator::Iterator(Object& prototype, IteratorRecord iterated)
|
||||
Iterator::Iterator(Object& prototype, NonnullGCPtr<IteratorRecord> iterated)
|
||||
: Object(ConstructWithPrototypeTag::Tag, prototype)
|
||||
, m_iterated(move(iterated))
|
||||
{
|
||||
}
|
||||
|
||||
Iterator::Iterator(Object& prototype)
|
||||
: Iterator(prototype, {})
|
||||
: Iterator(prototype, prototype.heap().allocate<IteratorRecord>(prototype.shape().realm(), prototype.shape().realm(), nullptr, js_undefined(), false))
|
||||
{
|
||||
}
|
||||
|
||||
// 7.4.2 GetIteratorFromMethod ( obj, method ), https://tc39.es/ecma262/#sec-getiteratorfrommethod
|
||||
ThrowCompletionOr<IteratorRecord> get_iterator_from_method(VM& vm, Value object, NonnullGCPtr<FunctionObject> method)
|
||||
ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator_from_method(VM& vm, Value object, NonnullGCPtr<FunctionObject> method)
|
||||
{
|
||||
// 1. Let iterator be ? Call(method, obj).
|
||||
auto iterator = TRY(call(vm, *method, object));
|
||||
|
@ -48,14 +49,15 @@ ThrowCompletionOr<IteratorRecord> 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<IteratorRecord>(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<IteratorRecord> get_iterator(VM& vm, Value object, IteratorHint kind)
|
||||
ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> get_iterator(VM& vm, Value object, IteratorHint kind)
|
||||
{
|
||||
JS::GCPtr<FunctionObject> method;
|
||||
|
||||
|
@ -95,20 +97,19 @@ ThrowCompletionOr<IteratorRecord> get_iterator(VM& vm, Value object, IteratorHin
|
|||
}
|
||||
|
||||
// 2.1.1 GetIteratorDirect ( obj ), https://tc39.es/proposal-iterator-helpers/#sec-getiteratorflattenable
|
||||
ThrowCompletionOr<IteratorRecord> get_iterator_direct(VM& vm, Object& object)
|
||||
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 }.
|
||||
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<IteratorRecord>(realm, realm, object, next_method, false);
|
||||
}
|
||||
|
||||
// 2.1.2 GetIteratorFlattenable ( obj, stringHandling ), https://tc39.es/proposal-iterator-helpers/#sec-getiteratorflattenable
|
||||
ThrowCompletionOr<IteratorRecord> get_iterator_flattenable(VM& vm, Value object, StringHandling string_handling)
|
||||
ThrowCompletionOr<NonnullGCPtr<IteratorRecord>> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Object> iterator, Value next_method, bool done)
|
||||
: Object(ConstructWithoutPrototypeTag::Tag, realm)
|
||||
, iterator(iterator)
|
||||
, next_method(next_method)
|
||||
, done(done)
|
||||
{
|
||||
}
|
||||
|
||||
GCPtr<Object> 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<IteratorRecord>() const { return is_iterator_record(); }
|
||||
|
||||
class Iterator : public Object {
|
||||
JS_OBJECT(Iterator, Object);
|
||||
JS_DECLARE_ALLOCATOR(Iterator);
|
||||
|
||||
public:
|
||||
static NonnullGCPtr<Iterator> create(Realm&, Object& prototype, IteratorRecord iterated);
|
||||
static NonnullGCPtr<Iterator> create(Realm&, Object& prototype, NonnullGCPtr<IteratorRecord> iterated);
|
||||
|
||||
IteratorRecord const& iterated() const { return m_iterated; }
|
||||
|
||||
private:
|
||||
Iterator(Object& prototype, IteratorRecord iterated);
|
||||
Iterator(Object& prototype, NonnullGCPtr<IteratorRecord> iterated);
|
||||
explicit Iterator(Object& prototype);
|
||||
|
||||
IteratorRecord m_iterated; // [[Iterated]]
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
NonnullGCPtr<IteratorRecord> m_iterated; // [[Iterated]]
|
||||
};
|
||||
|
||||
enum class IteratorHint {
|
||||
|
@ -49,10 +70,10 @@ enum class StringHandling {
|
|||
RejectStrings,
|
||||
};
|
||||
|
||||
ThrowCompletionOr<IteratorRecord> get_iterator_from_method(VM&, Value, NonnullGCPtr<FunctionObject>);
|
||||
ThrowCompletionOr<IteratorRecord> get_iterator(VM&, Value, IteratorHint);
|
||||
ThrowCompletionOr<IteratorRecord> get_iterator_direct(VM&, Object&);
|
||||
ThrowCompletionOr<IteratorRecord> get_iterator_flattenable(VM&, Value, StringHandling);
|
||||
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<Object>> iterator_next(VM&, IteratorRecord const&, Optional<Value> = {});
|
||||
ThrowCompletionOr<GCPtr<Object>> iterator_step(VM&, IteratorRecord const&);
|
||||
ThrowCompletionOr<bool> iterator_complete(VM&, Object& iterator_result);
|
||||
|
|
|
@ -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]] »).
|
||||
|
|
|
@ -13,12 +13,12 @@ namespace JS {
|
|||
|
||||
JS_DEFINE_ALLOCATOR(IteratorHelper);
|
||||
|
||||
ThrowCompletionOr<NonnullGCPtr<IteratorHelper>> IteratorHelper::create(Realm& realm, IteratorRecord underlying_iterator, Closure closure, Optional<AbruptClosure> abrupt_closure)
|
||||
ThrowCompletionOr<NonnullGCPtr<IteratorHelper>> IteratorHelper::create(Realm& realm, NonnullGCPtr<IteratorRecord> underlying_iterator, Closure closure, Optional<AbruptClosure> abrupt_closure)
|
||||
{
|
||||
return realm.heap().allocate<IteratorHelper>(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<AbruptClosure> abrupt_closure)
|
||||
IteratorHelper::IteratorHelper(Realm& realm, Object& prototype, NonnullGCPtr<IteratorRecord> underlying_iterator, Closure closure, Optional<AbruptClosure> 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)
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
using Closure = JS::SafeFunction<ThrowCompletionOr<Value>(VM&, IteratorHelper&)>;
|
||||
using AbruptClosure = JS::SafeFunction<ThrowCompletionOr<Value>(VM&, IteratorHelper&, Completion const&)>;
|
||||
|
||||
static ThrowCompletionOr<NonnullGCPtr<IteratorHelper>> create(Realm&, IteratorRecord, Closure, Optional<AbruptClosure> = {});
|
||||
static ThrowCompletionOr<NonnullGCPtr<IteratorHelper>> create(Realm&, NonnullGCPtr<IteratorRecord>, Closure, Optional<AbruptClosure> = {});
|
||||
|
||||
IteratorRecord const& underlying_iterator() const { return m_underlying_iterator; }
|
||||
|
||||
|
@ -33,12 +33,12 @@ public:
|
|||
ThrowCompletionOr<Value> close_result(VM&, Completion);
|
||||
|
||||
private:
|
||||
IteratorHelper(Realm&, Object& prototype, IteratorRecord, Closure, Optional<AbruptClosure>);
|
||||
IteratorHelper(Realm&, Object& prototype, NonnullGCPtr<IteratorRecord>, Closure, Optional<AbruptClosure>);
|
||||
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
virtual ThrowCompletionOr<Value> execute(VM&, JS::Completion const& completion) override;
|
||||
|
||||
IteratorRecord m_underlying_iterator; // [[UnderlyingIterator]]
|
||||
NonnullGCPtr<IteratorRecord> m_underlying_iterator; // [[UnderlyingIterator]]
|
||||
Closure m_closure;
|
||||
Optional<AbruptClosure> m_abrupt_closure;
|
||||
|
||||
|
|
|
@ -318,7 +318,7 @@ class FlatMapIterator : public Cell {
|
|||
public:
|
||||
ThrowCompletionOr<Value> 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<Value> 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<Value> next_outer_iterator(VM& vm, IteratorRecord const& iterated, IteratorHelper& iterator, FunctionObject& mapper)
|
||||
|
@ -391,7 +389,7 @@ private:
|
|||
|
||||
ThrowCompletionOr<Value> 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<IteratorRecord> m_inner_iterator;
|
||||
GCPtr<IteratorRecord> m_inner_iterator;
|
||||
};
|
||||
|
||||
JS_DEFINE_ALLOCATOR(FlatMapIterator);
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -226,7 +226,7 @@ ThrowCompletionOr<void> VM::binding_initialization(NonnullRefPtr<BindingPattern
|
|||
auto result = iterator_binding_initialization(*target, iterator_record, environment);
|
||||
|
||||
// 3. If iteratorRecord.[[Done]] is false, return ? IteratorClose(iteratorRecord, result).
|
||||
if (!iterator_record.done) {
|
||||
if (!iterator_record->done) {
|
||||
// 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({});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue