Commit 50304f87 authored by Leszek Swirski's avatar Leszek Swirski Committed by V8 LUCI CQ

[csa] Add "Iterate" helper to IteratorBuiltinsAssembler

"Iterate" takes an iterable and a function of TNode<Object>, and
implements the iterator protocol to iterate the iterable, applying the
function to each element.

It handles exceptions thrown during iteration and closes the iterator as
appropriate -- the hope is that if the body of the iteration has no
exception-throwing nodes, TurboFan can dead-code eliminate this close.

In the future, we may want to add an array fast-path to this method;
centralising the implementation means that this fast-path will then be
used by all callers of Iterate.

Change-Id: I9fe2f862b78619fe21ea7cb6469ed7ba93f14a30
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3581770Reviewed-by: 's avatarFrank Tang <ftang@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80075}
parent 056f7e52
......@@ -3,13 +3,14 @@
// found in the LICENSE file.
#include "src/builtins/builtins-iterator-gen.h"
#include "src/builtins/growable-fixed-array-gen.h"
#include "src/builtins/builtins-collections-gen.h"
#include "src/builtins/builtins-string-gen.h"
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
#include "src/builtins/growable-fixed-array-gen.h"
#include "src/codegen/code-stub-assembler.h"
#include "src/compiler/code-assembler.h"
#include "src/heap/factory-inl.h"
namespace v8 {
......@@ -131,6 +132,55 @@ TNode<Object> IteratorBuiltinsAssembler::IteratorValue(
return var_value.value();
}
void IteratorBuiltinsAssembler::Iterate(
TNode<Context> context, TNode<Object> iterable,
std::function<void(TNode<Object>)> func,
std::initializer_list<compiler::CodeAssemblerVariable*> merged_variables) {
Iterate(context, iterable, GetIteratorMethod(context, iterable), func,
merged_variables);
}
void IteratorBuiltinsAssembler::Iterate(
TNode<Context> context, TNode<Object> iterable, TNode<Object> iterable_fn,
std::function<void(TNode<Object>)> func,
std::initializer_list<compiler::CodeAssemblerVariable*> merged_variables) {
Label done(this);
IteratorRecord iterator_record = GetIterator(context, iterable, iterable_fn);
Label if_exception(this, Label::kDeferred);
TVARIABLE(Object, var_exception);
Label loop_start(this, merged_variables);
Goto(&loop_start);
BIND(&loop_start);
{
TNode<JSReceiver> next = IteratorStep(context, iterator_record, &done);
TNode<Object> next_value = IteratorValue(context, next);
{
compiler::ScopedExceptionHandler handler(this, &if_exception,
&var_exception);
func(next_value);
}
Goto(&loop_start);
}
BIND(&if_exception);
{
TNode<HeapObject> message = GetPendingMessage();
SetPendingMessage(TheHoleConstant());
IteratorCloseOnException(context, iterator_record);
CallRuntime(Runtime::kReThrowWithMessage, context, var_exception.value(),
message);
Unreachable();
}
BIND(&done);
}
TNode<JSArray> IteratorBuiltinsAssembler::IterableToList(
TNode<Context> context, TNode<Object> iterable, TNode<Object> iterator_fn) {
GrowableFixedArray values(state());
......@@ -149,33 +199,26 @@ TNode<FixedArray> IteratorBuiltinsAssembler::IterableToFixedArray(
void IteratorBuiltinsAssembler::FillFixedArrayFromIterable(
TNode<Context> context, TNode<Object> iterable, TNode<Object> iterator_fn,
GrowableFixedArray* values) {
// 1. Let iteratorRecord be ? GetIterator(items, method).
IteratorRecord iterator_record = GetIterator(context, iterable, iterator_fn);
// 1. Let iteratorRecord be ? GetIterator(items, method) (handled by Iterate).
// 2. Let values be a new empty List.
// The GrowableFixedArray has already been created. It's ok if we do this step
// out of order, since creating an empty List is not observable.
Label loop_start(this, {values->var_array(), values->var_length(),
values->var_capacity()}),
done(this);
Goto(&loop_start);
// 3. Let next be true.
// 4. Repeat, while next is not false
BIND(&loop_start);
{
// a. Set next to ? IteratorStep(iteratorRecord).
TNode<JSReceiver> next = IteratorStep(context, iterator_record, &done);
// b. If next is not false, then
// i. Let nextValue be ? IteratorValue(next).
TNode<Object> next_value = IteratorValue(context, next);
// ii. Append nextValue to the end of the List values.
values->Push(next_value);
Goto(&loop_start);
}
BIND(&done);
// 3. Let next be true. (handled by Iterate)
// 4. Repeat, while next is not false (handled by Iterate)
Iterate(context, iterable, iterator_fn,
[&values](TNode<Object> value) {
// Handled by Iterate:
// a. Set next to ? IteratorStep(iteratorRecord).
// b. If next is not false, then
// i. Let nextValue be ? IteratorValue(next).
// ii. Append nextValue to the end of the List values.
values->Push(value);
},
{values->var_array(), values->var_capacity(), values->var_length()});
}
TF_BUILTIN(IterableToList, IteratorBuiltinsAssembler) {
......@@ -226,56 +269,48 @@ TNode<FixedArray> IteratorBuiltinsAssembler::StringListFromIterable(
// a. Return a new empty List.
GotoIf(IsUndefined(iterable), &done);
// 2. Let iteratorRecord be ? GetIterator(items).
IteratorRecord iterator_record = GetIterator(context, iterable);
// 2. Let iteratorRecord be ? GetIterator(items) (handled by Iterate).
// 3. Let list be a new empty List.
Label loop_start(this,
{list.var_array(), list.var_length(), list.var_capacity()});
Goto(&loop_start);
// 4. Let next be true.
// 5. Repeat, while next is not false
Label if_isnotstringtype(this, Label::kDeferred),
if_exception(this, Label::kDeferred);
BIND(&loop_start);
{
// a. Set next to ? IteratorStep(iteratorRecord).
TNode<JSReceiver> next = IteratorStep(context, iterator_record, &done);
// b. If next is not false, then
// i. Let nextValue be ? IteratorValue(next).
TNode<Object> next_value = IteratorValue(context, next);
// ii. If Type(nextValue) is not String, then
GotoIf(TaggedIsSmi(next_value), &if_isnotstringtype);
TNode<Uint16T> next_value_type = LoadInstanceType(CAST(next_value));
GotoIfNot(IsStringInstanceType(next_value_type), &if_isnotstringtype);
// iii. Append nextValue to the end of the List list.
list.Push(next_value);
Goto(&loop_start);
// 5.b.ii
BIND(&if_isnotstringtype);
{
// 1. Let error be ThrowCompletion(a newly created TypeError object).
TVARIABLE(Object, var_exception);
{
compiler::ScopedExceptionHandler handler(this, &if_exception,
&var_exception);
CallRuntime(Runtime::kThrowTypeError, context,
SmiConstant(MessageTemplate::kIterableYieldedNonString),
next_value);
}
Unreachable();
// 2. Return ? IteratorClose(iteratorRecord, error).
BIND(&if_exception);
TNode<HeapObject> message = GetPendingMessage();
SetPendingMessage(TheHoleConstant());
IteratorCloseOnException(context, iterator_record);
CallRuntime(Runtime::kReThrowWithMessage, context, var_exception.value(),
message);
Unreachable();
}
}
// 4. Let next be true (handled by Iterate).
// 5. Repeat, while next is not false (handled by Iterate).
Iterate(
context, iterable,
[&](TNode<Object> next_value) {
// Handled by Iterate:
// a. Set next to ? IteratorStep(iteratorRecord).
// b. If next is not false, then
// i. Let nextValue be ? IteratorValue(next).
// ii. If Type(nextValue) is not String, then
Label if_isnotstringtype(this, Label::kDeferred), loop_body_end(this);
GotoIf(TaggedIsSmi(next_value), &if_isnotstringtype);
TNode<Uint16T> next_value_type = LoadInstanceType(CAST(next_value));
GotoIfNot(IsStringInstanceType(next_value_type), &if_isnotstringtype);
// iii. Append nextValue to the end of the List list.
list.Push(next_value);
Goto(&loop_body_end);
// 5.b.ii
BIND(&if_isnotstringtype);
{
// 1. Let error be ThrowCompletion(a newly created TypeError object).
CallRuntime(Runtime::kThrowTypeError, context,
SmiConstant(MessageTemplate::kIterableYieldedNonString),
next_value);
// 2. Return ? IteratorClose(iteratorRecord, error). (handled by
// Iterate).
Unreachable();
}
BIND(&loop_body_end);
},
{list.var_array(), list.var_length(), list.var_capacity()});
Goto(&done);
BIND(&done);
// 6. Return list.
......
......@@ -50,6 +50,16 @@ class IteratorBuiltinsAssembler : public CodeStubAssembler {
TNode<Context> context, TNode<JSReceiver> result,
base::Optional<TNode<Map>> fast_iterator_result_map = base::nullopt);
void Iterate(TNode<Context> context, TNode<Object> iterable,
std::function<void(TNode<Object>)> func,
std::initializer_list<compiler::CodeAssemblerVariable*>
merged_variables = {});
void Iterate(TNode<Context> context, TNode<Object> iterable,
TNode<Object> iterable_fn,
std::function<void(TNode<Object>)> func,
std::initializer_list<compiler::CodeAssemblerVariable*>
merged_variables = {});
// #sec-iterabletolist
// Build a JSArray by iterating over {iterable} using {iterator_fn},
// following the ECMAscript operation with the same name.
......
......@@ -33,55 +33,52 @@ TemporalBuiltinsAssembler::TemporalInstantFixedArrayFromIterable(
// a. Return a new empty List.
GotoIf(IsUndefined(iterable), &done);
// 2. Let iteratorRecord be ? GetIterator(items).
IteratorRecord iterator_record = GetIterator(context, iterable);
// 2. Let iteratorRecord be ? GetIterator(items) (handled by Iterate).
// 3. Let list be a new empty List.
Label loop_start(this,
{list.var_array(), list.var_length(), list.var_capacity()});
Goto(&loop_start);
// 4. Let next be true.
// 5. Repeat, while next is not false
Label if_isnottemporalinstant(this, Label::kDeferred),
if_exception(this, Label::kDeferred);
BIND(&loop_start);
{
// a. Set next to ? IteratorStep(iteratorRecord).
TNode<JSReceiver> next = IteratorStep(context, iterator_record, &done);
// b. If next is not false, then
// i. Let nextValue be ? IteratorValue(next).
TNode<Object> next_value = IteratorValue(context, next);
// ii. If Type(nextValue) is not Object or nextValue does not have an
// [[InitializedTemporalInstant]] internal slot
GotoIf(TaggedIsSmi(next_value), &if_isnottemporalinstant);
TNode<Uint16T> next_value_type = LoadInstanceType(CAST(next_value));
GotoIfNot(IsTemporalInstantInstanceType(next_value_type),
&if_isnottemporalinstant);
// iii. Append nextValue to the end of the List list.
list.Push(next_value);
Goto(&loop_start);
// 5.b.ii
BIND(&if_isnottemporalinstant);
{
// 1. Let error be ThrowCompletion(a newly created TypeError object).
TVARIABLE(Object, var_exception);
{
compiler::ScopedExceptionHandler handler(this, &if_exception,
&var_exception);
CallRuntime(Runtime::kThrowTypeError, context,
SmiConstant(MessageTemplate::kIterableYieldedNonString),
next_value);
}
Unreachable();
// 2. Return ? IteratorClose(iteratorRecord, error).
BIND(&if_exception);
IteratorCloseOnException(context, iterator_record);
CallRuntime(Runtime::kReThrow, context, var_exception.value());
Unreachable();
}
}
// 3. Let next be true. (handled by Iterate).
// 4. Repeat, while next is not false (handled by Iterate).
Iterate(context, iterable,
[&](TNode<Object> next_value) {
// Handled by Iterate:
// a. Set next to ? IteratorStep(iteratorRecord).
// b. If next is not false, then
// i. Let nextValue be ? IteratorValue(next).
// ii. If Type(nextValue) is not Object or nextValue does not have
// an [[InitializedTemporalInstant]] internal slot
Label if_isnottemporalinstant(this, Label::kDeferred),
loop_body_end(this);
GotoIf(TaggedIsSmi(next_value), &if_isnottemporalinstant);
TNode<Uint16T> next_value_type = LoadInstanceType(CAST(next_value));
GotoIfNot(IsTemporalInstantInstanceType(next_value_type),
&if_isnottemporalinstant);
// iii. Append nextValue to the end of the List list.
list.Push(next_value);
Goto(&loop_body_end);
// 5.b.ii
BIND(&if_isnottemporalinstant);
{
// 1. Let error be ThrowCompletion(a newly created TypeError
// object).
CallRuntime(
Runtime::kThrowTypeError, context,
SmiConstant(MessageTemplate::kIterableYieldedNonString),
next_value);
// 2. Return ? IteratorClose(iteratorRecord, error). (handled by
// Iterate).
Unreachable();
}
BIND(&loop_body_end);
},
{list.var_array(), list.var_length(), list.var_capacity()});
Goto(&done);
BIND(&done);
return list.ToFixedArray();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment