Commit bf4cc9ee authored by Caitlin Potter's avatar Caitlin Potter Committed by Commit Bot

[esnext] load `iterator.next` only once at beginning of iteration

https://github.com/tc39/ecma262/pull/988 gained concensus during the
september 2017 TC39 meetings. This moves the load of the "next" method
to the very beginning of the iteration protocol, rather than during
each iteration step.

This impacts:

- yield*
- for-of loops
- spread arguments
- array spreads

In the v8 implementation, this also affects async iteration versions of
these things (the sole exception being the Async-From-Sync iterator,
which requires a few more changes to work with this, likely done in a
followup patch).

This change introduces a new AST node, ResolvedProperty, which can be used
as a callee by Call nodes to produce the same bytecode as Property calls,
without observably re-loading the property. This is used in several
AST-desugarings involving the iteration protocol.

BUG=v8:6861, v8:5699
R=rmcilroy@chromium.org, neis@chromium.org, adamk@chromium.org

Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: Ib81106a0182687fc5efea0bc32302ad06376773b
Reviewed-on: https://chromium-review.googlesource.com/687997
Commit-Queue: Caitlin Potter <caitp@igalia.com>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50452}
parent ae14edca
...@@ -195,6 +195,11 @@ void AstNumberingVisitor::VisitProperty(Property* node) { ...@@ -195,6 +195,11 @@ void AstNumberingVisitor::VisitProperty(Property* node) {
Visit(node->obj()); Visit(node->obj());
} }
void AstNumberingVisitor::VisitResolvedProperty(ResolvedProperty* node) {
Visit(node->object());
Visit(node->property());
}
void AstNumberingVisitor::VisitAssignment(Assignment* node) { void AstNumberingVisitor::VisitAssignment(Assignment* node) {
Visit(node->target()); Visit(node->target());
Visit(node->value()); Visit(node->value());
...@@ -251,6 +256,7 @@ void AstNumberingVisitor::VisitForInStatement(ForInStatement* node) { ...@@ -251,6 +256,7 @@ void AstNumberingVisitor::VisitForInStatement(ForInStatement* node) {
void AstNumberingVisitor::VisitForOfStatement(ForOfStatement* node) { void AstNumberingVisitor::VisitForOfStatement(ForOfStatement* node) {
Visit(node->assign_iterator()); // Not part of loop. Visit(node->assign_iterator()); // Not part of loop.
Visit(node->assign_next());
node->set_first_suspend_id(suspend_count_); node->set_first_suspend_id(suspend_count_);
Visit(node->next_result()); Visit(node->next_result());
Visit(node->result_done()); Visit(node->result_done());
......
...@@ -392,6 +392,14 @@ void AstTraversalVisitor<Subclass>::VisitProperty(Property* expr) { ...@@ -392,6 +392,14 @@ void AstTraversalVisitor<Subclass>::VisitProperty(Property* expr) {
RECURSE_EXPRESSION(Visit(expr->key())); RECURSE_EXPRESSION(Visit(expr->key()));
} }
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitResolvedProperty(
ResolvedProperty* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(VisitVariableProxy(expr->object()));
RECURSE_EXPRESSION(VisitVariableProxy(expr->property()));
}
template <class Subclass> template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitCall(Call* expr) { void AstTraversalVisitor<Subclass>::VisitCall(Call* expr) {
PROCESS_EXPRESSION(expr); PROCESS_EXPRESSION(expr);
......
...@@ -805,6 +805,10 @@ Call::CallType Call::GetCallType() const { ...@@ -805,6 +805,10 @@ Call::CallType Call::GetCallType() const {
} }
} }
if (expression()->IsResolvedProperty()) {
return RESOLVED_PROPERTY_CALL;
}
return OTHER_CALL; return OTHER_CALL;
} }
......
...@@ -94,6 +94,7 @@ namespace internal { ...@@ -94,6 +94,7 @@ namespace internal {
V(Literal) \ V(Literal) \
V(NativeFunctionLiteral) \ V(NativeFunctionLiteral) \
V(Property) \ V(Property) \
V(ResolvedProperty) \
V(RewritableExpression) \ V(RewritableExpression) \
V(Spread) \ V(Spread) \
V(SuperCallReference) \ V(SuperCallReference) \
...@@ -590,11 +591,13 @@ class ForInStatement final : public ForEachStatement { ...@@ -590,11 +591,13 @@ class ForInStatement final : public ForEachStatement {
class ForOfStatement final : public ForEachStatement { class ForOfStatement final : public ForEachStatement {
public: public:
void Initialize(Statement* body, Variable* iterator, void Initialize(Statement* body, Variable* iterator,
Expression* assign_iterator, Expression* next_result, Expression* assign_iterator, Expression* assign_next,
Expression* result_done, Expression* assign_each) { Expression* next_result, Expression* result_done,
Expression* assign_each) {
ForEachStatement::Initialize(body); ForEachStatement::Initialize(body);
iterator_ = iterator; iterator_ = iterator;
assign_iterator_ = assign_iterator; assign_iterator_ = assign_iterator;
assign_next_ = assign_next;
next_result_ = next_result; next_result_ = next_result;
result_done_ = result_done; result_done_ = result_done;
assign_each_ = assign_each; assign_each_ = assign_each;
...@@ -609,6 +612,9 @@ class ForOfStatement final : public ForEachStatement { ...@@ -609,6 +612,9 @@ class ForOfStatement final : public ForEachStatement {
return assign_iterator_; return assign_iterator_;
} }
// iteratorRecord.next = iterator.next
Expression* assign_next() const { return assign_next_; }
// result = iterator.next() // with type check // result = iterator.next() // with type check
Expression* next_result() const { Expression* next_result() const {
return next_result_; return next_result_;
...@@ -624,6 +630,12 @@ class ForOfStatement final : public ForEachStatement { ...@@ -624,6 +630,12 @@ class ForOfStatement final : public ForEachStatement {
return assign_each_; return assign_each_;
} }
void set_assign_iterator(Expression* e) { assign_iterator_ = e; }
void set_assign_next(Expression* e) { assign_next_ = e; }
void set_next_result(Expression* e) { next_result_ = e; }
void set_result_done(Expression* e) { result_done_ = e; }
void set_assign_each(Expression* e) { assign_each_ = e; }
private: private:
friend class AstNodeFactory; friend class AstNodeFactory;
...@@ -637,6 +649,7 @@ class ForOfStatement final : public ForEachStatement { ...@@ -637,6 +649,7 @@ class ForOfStatement final : public ForEachStatement {
Variable* iterator_; Variable* iterator_;
Expression* assign_iterator_; Expression* assign_iterator_;
Expression* assign_next_;
Expression* next_result_; Expression* next_result_;
Expression* result_done_; Expression* result_done_;
Expression* assign_each_; Expression* assign_each_;
...@@ -1607,6 +1620,25 @@ class Property final : public Expression { ...@@ -1607,6 +1620,25 @@ class Property final : public Expression {
Expression* key_; Expression* key_;
}; };
// ResolvedProperty pairs a receiver field with a value field. It allows Call
// to support arbitrary receivers while still taking advantage of TypeFeedback.
class ResolvedProperty final : public Expression {
public:
VariableProxy* object() const { return object_; }
VariableProxy* property() const { return property_; }
void set_object(VariableProxy* e) { object_ = e; }
void set_property(VariableProxy* e) { property_ = e; }
private:
friend class AstNodeFactory;
ResolvedProperty(VariableProxy* obj, VariableProxy* property, int pos)
: Expression(pos, kResolvedProperty), object_(obj), property_(property) {}
VariableProxy* object_;
VariableProxy* property_;
};
class Call final : public Expression { class Call final : public Expression {
public: public:
...@@ -1633,6 +1665,7 @@ class Call final : public Expression { ...@@ -1633,6 +1665,7 @@ class Call final : public Expression {
NAMED_SUPER_PROPERTY_CALL, NAMED_SUPER_PROPERTY_CALL,
KEYED_SUPER_PROPERTY_CALL, KEYED_SUPER_PROPERTY_CALL,
SUPER_CALL, SUPER_CALL,
RESOLVED_PROPERTY_CALL,
OTHER_CALL OTHER_CALL
}; };
...@@ -2104,7 +2137,6 @@ class YieldStar final : public Suspend { ...@@ -2104,7 +2137,6 @@ class YieldStar final : public Suspend {
// - One for awaiting the iterator result yielded by the delegated iterator // - One for awaiting the iterator result yielded by the delegated iterator
// (await_delegated_iterator_output_suspend_id) // (await_delegated_iterator_output_suspend_id)
int await_iterator_close_suspend_id() const { int await_iterator_close_suspend_id() const {
DCHECK_NE(-1, await_iterator_close_suspend_id_);
return await_iterator_close_suspend_id_; return await_iterator_close_suspend_id_;
} }
void set_await_iterator_close_suspend_id(int id) { void set_await_iterator_close_suspend_id(int id) {
...@@ -2112,7 +2144,6 @@ class YieldStar final : public Suspend { ...@@ -2112,7 +2144,6 @@ class YieldStar final : public Suspend {
} }
int await_delegated_iterator_output_suspend_id() const { int await_delegated_iterator_output_suspend_id() const {
DCHECK_NE(-1, await_delegated_iterator_output_suspend_id_);
return await_delegated_iterator_output_suspend_id_; return await_delegated_iterator_output_suspend_id_;
} }
void set_await_delegated_iterator_output_suspend_id(int id) { void set_await_delegated_iterator_output_suspend_id(int id) {
...@@ -2997,6 +3028,12 @@ class AstNodeFactory final BASE_EMBEDDED { ...@@ -2997,6 +3028,12 @@ class AstNodeFactory final BASE_EMBEDDED {
return new (zone_) Property(obj, key, pos); return new (zone_) Property(obj, key, pos);
} }
ResolvedProperty* NewResolvedProperty(VariableProxy* obj,
VariableProxy* property,
int pos = kNoSourcePosition) {
return new (zone_) ResolvedProperty(obj, property, pos);
}
Call* NewCall(Expression* expression, ZoneList<Expression*>* arguments, Call* NewCall(Expression* expression, ZoneList<Expression*>* arguments,
int pos, Call::PossiblyEval possibly_eval = Call::NOT_EVAL) { int pos, Call::PossiblyEval possibly_eval = Call::NOT_EVAL) {
return new (zone_) Call(expression, arguments, pos, possibly_eval); return new (zone_) Call(expression, arguments, pos, possibly_eval);
......
...@@ -326,6 +326,7 @@ void CallPrinter::VisitProperty(Property* node) { ...@@ -326,6 +326,7 @@ void CallPrinter::VisitProperty(Property* node) {
} }
} }
void CallPrinter::VisitResolvedProperty(ResolvedProperty* node) {}
void CallPrinter::VisitCall(Call* node) { void CallPrinter::VisitCall(Call* node) {
bool was_found = false; bool was_found = false;
...@@ -1249,6 +1250,14 @@ void AstPrinter::VisitProperty(Property* node) { ...@@ -1249,6 +1250,14 @@ void AstPrinter::VisitProperty(Property* node) {
} }
} }
void AstPrinter::VisitResolvedProperty(ResolvedProperty* node) {
EmbeddedVector<char, 128> buf;
SNPrintF(buf, "RESOLVED-PROPERTY");
IndentedScope indent(this, buf.start(), node->position());
PrintIndentedVisit("RECEIVER", node->object());
PrintIndentedVisit("PROPERTY", node->property());
}
void AstPrinter::VisitCall(Call* node) { void AstPrinter::VisitCall(Call* node) {
EmbeddedVector<char, 128> buf; EmbeddedVector<char, 128> buf;
......
...@@ -232,10 +232,9 @@ void BaseCollectionsAssembler::AddConstructorEntriesFromIterable( ...@@ -232,10 +232,9 @@ void BaseCollectionsAssembler::AddConstructorEntriesFromIterable(
TNode<Object> add_func = GetAddFunction(variant, context, collection); TNode<Object> add_func = GetAddFunction(variant, context, collection);
IteratorBuiltinsAssembler iterator_assembler(this->state()); IteratorBuiltinsAssembler iterator_assembler(this->state());
TNode<Object> iterator = IteratorRecord iterator = iterator_assembler.GetIterator(context, iterable);
CAST(iterator_assembler.GetIterator(context, iterable));
CSA_ASSERT(this, Word32BinaryNot(IsUndefined(iterator))); CSA_ASSERT(this, Word32BinaryNot(IsUndefined(iterator.object)));
TNode<Object> fast_iterator_result_map = TNode<Object> fast_iterator_result_map =
LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX); LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
......
...@@ -11,9 +11,10 @@ namespace internal { ...@@ -11,9 +11,10 @@ namespace internal {
using compiler::Node; using compiler::Node;
Node* IteratorBuiltinsAssembler::GetIterator(Node* context, Node* object, IteratorRecord IteratorBuiltinsAssembler::GetIterator(Node* context,
Label* if_exception, Node* object,
Variable* exception) { Label* if_exception,
Variable* exception) {
Node* method = GetProperty(context, object, factory()->iterator_symbol()); Node* method = GetProperty(context, object, factory()->iterator_symbol());
GotoIfException(method, if_exception, exception); GotoIfException(method, if_exception, exception);
...@@ -21,9 +22,9 @@ Node* IteratorBuiltinsAssembler::GetIterator(Node* context, Node* object, ...@@ -21,9 +22,9 @@ Node* IteratorBuiltinsAssembler::GetIterator(Node* context, Node* object,
Node* iterator = CallJS(callable, context, method, object); Node* iterator = CallJS(callable, context, method, object);
GotoIfException(iterator, if_exception, exception); GotoIfException(iterator, if_exception, exception);
Label done(this), if_notobject(this, Label::kDeferred); Label get_next(this), if_notobject(this, Label::kDeferred);
GotoIf(TaggedIsSmi(iterator), &if_notobject); GotoIf(TaggedIsSmi(iterator), &if_notobject);
Branch(IsJSReceiver(iterator), &done, &if_notobject); Branch(IsJSReceiver(iterator), &get_next, &if_notobject);
BIND(&if_notobject); BIND(&if_notobject);
{ {
...@@ -34,24 +35,21 @@ Node* IteratorBuiltinsAssembler::GetIterator(Node* context, Node* object, ...@@ -34,24 +35,21 @@ Node* IteratorBuiltinsAssembler::GetIterator(Node* context, Node* object,
Unreachable(); Unreachable();
} }
BIND(&done); BIND(&get_next);
return iterator; Node* const next = GetProperty(context, iterator, factory()->next_string());
GotoIfException(next, if_exception, exception);
return IteratorRecord{TNode<JSReceiver>::UncheckedCast(iterator),
TNode<Object>::UncheckedCast(next)};
} }
Node* IteratorBuiltinsAssembler::IteratorStep(Node* context, Node* iterator, Node* IteratorBuiltinsAssembler::IteratorStep(
Label* if_done, Node* context, const IteratorRecord& iterator, Label* if_done,
Node* fast_iterator_result_map, Node* fast_iterator_result_map, Label* if_exception, Variable* exception) {
Label* if_exception,
Variable* exception) {
DCHECK_NOT_NULL(if_done); DCHECK_NOT_NULL(if_done);
// IteratorNext
Node* next_method = GetProperty(context, iterator, factory()->next_string());
GotoIfException(next_method, if_exception, exception);
// 1. a. Let result be ? Invoke(iterator, "next", « »). // 1. a. Let result be ? Invoke(iterator, "next", « »).
Callable callable = CodeFactory::Call(isolate()); Callable callable = CodeFactory::Call(isolate());
Node* result = CallJS(callable, context, next_method, iterator); Node* result = CallJS(callable, context, iterator.next, iterator.object);
GotoIfException(result, if_exception, exception); GotoIfException(result, if_exception, exception);
// 3. If Type(result) is not Object, throw a TypeError exception. // 3. If Type(result) is not Object, throw a TypeError exception.
...@@ -129,20 +127,20 @@ Node* IteratorBuiltinsAssembler::IteratorValue(Node* context, Node* result, ...@@ -129,20 +127,20 @@ Node* IteratorBuiltinsAssembler::IteratorValue(Node* context, Node* result,
return var_value.value(); return var_value.value();
} }
void IteratorBuiltinsAssembler::IteratorCloseOnException(Node* context, void IteratorBuiltinsAssembler::IteratorCloseOnException(
Node* iterator, Node* context, const IteratorRecord& iterator, Label* if_exception,
Label* if_exception, Variable* exception) {
Variable* exception) {
// Perform ES #sec-iteratorclose when an exception occurs. This simpler // Perform ES #sec-iteratorclose when an exception occurs. This simpler
// algorithm does not include redundant steps which are never reachable from // algorithm does not include redundant steps which are never reachable from
// the spec IteratorClose algorithm. // the spec IteratorClose algorithm.
DCHECK_NOT_NULL(if_exception); DCHECK_NOT_NULL(if_exception);
DCHECK_NOT_NULL(exception); DCHECK_NOT_NULL(exception);
CSA_ASSERT(this, IsNotTheHole(exception->value())); CSA_ASSERT(this, IsNotTheHole(exception->value()));
CSA_ASSERT(this, IsJSReceiver(iterator)); CSA_ASSERT(this, IsJSReceiver(iterator.object));
// Let return be ? GetMethod(iterator, "return"). // Let return be ? GetMethod(iterator, "return").
Node* method = GetProperty(context, iterator, factory()->return_string()); Node* method =
GetProperty(context, iterator.object, factory()->return_string());
GotoIfException(method, if_exception, exception); GotoIfException(method, if_exception, exception);
// If return is undefined, return Completion(completion). // If return is undefined, return Completion(completion).
...@@ -152,7 +150,7 @@ void IteratorBuiltinsAssembler::IteratorCloseOnException(Node* context, ...@@ -152,7 +150,7 @@ void IteratorBuiltinsAssembler::IteratorCloseOnException(Node* context,
// Let innerResult be Call(return, iterator, « »). // Let innerResult be Call(return, iterator, « »).
// If an exception occurs, the original exception remains bound // If an exception occurs, the original exception remains bound
Node* inner_result = Node* inner_result =
CallJS(CodeFactory::Call(isolate()), context, method, iterator); CallJS(CodeFactory::Call(isolate()), context, method, iterator.object);
GotoIfException(inner_result, if_exception, nullptr); GotoIfException(inner_result, if_exception, nullptr);
// (If completion.[[Type]] is throw) return Completion(completion). // (If completion.[[Type]] is throw) return Completion(completion).
...@@ -160,9 +158,8 @@ void IteratorBuiltinsAssembler::IteratorCloseOnException(Node* context, ...@@ -160,9 +158,8 @@ void IteratorBuiltinsAssembler::IteratorCloseOnException(Node* context,
} }
} }
void IteratorBuiltinsAssembler::IteratorCloseOnException(Node* context, void IteratorBuiltinsAssembler::IteratorCloseOnException(
Node* iterator, Node* context, const IteratorRecord& iterator, Variable* exception) {
Variable* exception) {
Label rethrow(this, Label::kDeferred); Label rethrow(this, Label::kDeferred);
IteratorCloseOnException(context, iterator, &rethrow, exception); IteratorCloseOnException(context, iterator, &rethrow, exception);
......
...@@ -19,16 +19,17 @@ class IteratorBuiltinsAssembler : public CodeStubAssembler { ...@@ -19,16 +19,17 @@ class IteratorBuiltinsAssembler : public CodeStubAssembler {
// https://tc39.github.io/ecma262/#sec-getiterator --- never used for // https://tc39.github.io/ecma262/#sec-getiterator --- never used for
// @@asyncIterator. // @@asyncIterator.
Node* GetIterator(Node* context, Node* object, Label* if_exception = nullptr, IteratorRecord GetIterator(Node* context, Node* object,
Variable* exception = nullptr); Label* if_exception = nullptr,
Variable* exception = nullptr);
// https://tc39.github.io/ecma262/#sec-iteratorstep // https://tc39.github.io/ecma262/#sec-iteratorstep
// Returns `false` if the iterator is done, otherwise returns an // Returns `false` if the iterator is done, otherwise returns an
// iterator result. // iterator result.
// `fast_iterator_result_map` refers to the map for the JSIteratorResult // `fast_iterator_result_map` refers to the map for the JSIteratorResult
// object, loaded from the native context. // object, loaded from the native context.
Node* IteratorStep(Node* context, Node* iterator, Label* if_done, Node* IteratorStep(Node* context, const IteratorRecord& iterator,
Node* fast_iterator_result_map = nullptr, Label* if_done, Node* fast_iterator_result_map = nullptr,
Label* if_exception = nullptr, Label* if_exception = nullptr,
Variable* exception = nullptr); Variable* exception = nullptr);
...@@ -42,9 +43,9 @@ class IteratorBuiltinsAssembler : public CodeStubAssembler { ...@@ -42,9 +43,9 @@ class IteratorBuiltinsAssembler : public CodeStubAssembler {
Variable* exception = nullptr); Variable* exception = nullptr);
// https://tc39.github.io/ecma262/#sec-iteratorclose // https://tc39.github.io/ecma262/#sec-iteratorclose
void IteratorCloseOnException(Node* context, Node* iterator, void IteratorCloseOnException(Node* context, const IteratorRecord& iterator,
Label* if_exception, Variable* exception); Label* if_exception, Variable* exception);
void IteratorCloseOnException(Node* context, Node* iterator, void IteratorCloseOnException(Node* context, const IteratorRecord& iterator,
Variable* exception); Variable* exception);
}; };
......
...@@ -1807,8 +1807,9 @@ TF_BUILTIN(PerformNativePromiseThen, PromiseBuiltinsAssembler) { ...@@ -1807,8 +1807,9 @@ TF_BUILTIN(PerformNativePromiseThen, PromiseBuiltinsAssembler) {
} }
Node* PromiseBuiltinsAssembler::PerformPromiseAll( Node* PromiseBuiltinsAssembler::PerformPromiseAll(
Node* context, Node* constructor, Node* capability, Node* iterator, Node* context, Node* constructor, Node* capability,
Label* if_exception, Variable* var_exception) { const IteratorRecord& iterator, Label* if_exception,
Variable* var_exception) {
IteratorBuiltinsAssembler iter_assembler(state()); IteratorBuiltinsAssembler iter_assembler(state());
Label close_iterator(this); Label close_iterator(this);
...@@ -2014,7 +2015,7 @@ TF_BUILTIN(PromiseAll, PromiseBuiltinsAssembler) { ...@@ -2014,7 +2015,7 @@ TF_BUILTIN(PromiseAll, PromiseBuiltinsAssembler) {
// Let iterator be GetIterator(iterable). // Let iterator be GetIterator(iterable).
// IfAbruptRejectPromise(iterator, promiseCapability). // IfAbruptRejectPromise(iterator, promiseCapability).
Node* const iterable = Parameter(Descriptor::kIterable); Node* const iterable = Parameter(Descriptor::kIterable);
Node* const iterator = iter_assembler.GetIterator( IteratorRecord iterator = iter_assembler.GetIterator(
context, iterable, &reject_promise, &var_exception); context, iterable, &reject_promise, &var_exception);
// Let result be PerformPromiseAll(iteratorRecord, C, promiseCapability). // Let result be PerformPromiseAll(iteratorRecord, C, promiseCapability).
...@@ -2151,7 +2152,7 @@ TF_BUILTIN(PromiseRace, PromiseBuiltinsAssembler) { ...@@ -2151,7 +2152,7 @@ TF_BUILTIN(PromiseRace, PromiseBuiltinsAssembler) {
// Let iterator be GetIterator(iterable). // Let iterator be GetIterator(iterable).
// IfAbruptRejectPromise(iterator, promiseCapability). // IfAbruptRejectPromise(iterator, promiseCapability).
Node* const iterable = Parameter(Descriptor::kIterable); Node* const iterable = Parameter(Descriptor::kIterable);
Node* const iterator = iter_assembler.GetIterator( IteratorRecord iterator = iter_assembler.GetIterator(
context, iterable, &reject_promise, &var_exception); context, iterable, &reject_promise, &var_exception);
// Let result be PerformPromiseRace(iteratorRecord, C, promiseCapability). // Let result be PerformPromiseRace(iteratorRecord, C, promiseCapability).
......
...@@ -157,7 +157,7 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler { ...@@ -157,7 +157,7 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
Node* CreateThrowerFunction(Node* reason, Node* native_context); Node* CreateThrowerFunction(Node* reason, Node* native_context);
Node* PerformPromiseAll(Node* context, Node* constructor, Node* capability, Node* PerformPromiseAll(Node* context, Node* constructor, Node* capability,
Node* iterator, Label* if_exception, const IteratorRecord& record, Label* if_exception,
Variable* var_exception); Variable* var_exception);
Node* IncrementSmiCell(Node* cell, Label* if_overflow = nullptr); Node* IncrementSmiCell(Node* cell, Label* if_overflow = nullptr);
......
...@@ -71,6 +71,17 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol }; ...@@ -71,6 +71,17 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
promise_default_resolve_handler_symbol, \ promise_default_resolve_handler_symbol, \
PromiseDefaultResolveHandlerSymbol) PromiseDefaultResolveHandlerSymbol)
// Returned from IteratorBuiltinsAssembler::GetIterator(). Struct is declared
// here to simplify use in other generated builtins.
struct IteratorRecord {
public:
// iteratorRecord.[[Iterator]]
compiler::TNode<JSReceiver> object;
// iteratorRecord.[[NextMethod]]
compiler::TNode<Object> next;
};
// Provides JavaScript-specific "macro-assembler" functionality on top of the // Provides JavaScript-specific "macro-assembler" functionality on top of the
// CodeAssembler. By factoring the JavaScript-isms out of the CodeAssembler, // CodeAssembler. By factoring the JavaScript-isms out of the CodeAssembler,
// it's possible to add JavaScript-specific useful CodeAssembler "macros" // it's possible to add JavaScript-specific useful CodeAssembler "macros"
......
This diff is collapsed.
...@@ -151,9 +151,24 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> { ...@@ -151,9 +151,24 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void BuildAwait(int suspend_id); void BuildAwait(int suspend_id);
void BuildGetIterator(Expression* iterable, IteratorType hint); void BuildGetIterator(Expression* iterable, IteratorType hint);
// Create an IteratorRecord with pre-allocated registers holding the next
// method and iterator object.
IteratorRecord BuildGetIteratorRecord(Expression* iterable,
Register iterator_next,
Register iterator_object,
IteratorType hint);
// Create an IteratorRecord allocating new registers to hold the next method
// and iterator object.
IteratorRecord BuildGetIteratorRecord(Expression* iterable, IteratorRecord BuildGetIteratorRecord(Expression* iterable,
IteratorType hint); IteratorType hint);
void BuildIteratorNext(const IteratorRecord& iterator, Register next_result); void BuildIteratorNext(const IteratorRecord& iterator, Register next_result);
void BuildIteratorClose(const IteratorRecord& iterator, int suspend_id = -1);
void BuildCallIteratorMethod(Register iterator, const AstRawString* method,
RegisterList receiver_and_args,
BytecodeLabel* if_called,
BytecodeLabels* if_notcalled);
void BuildArrayLiteralSpread(Spread* spread, Register array); void BuildArrayLiteralSpread(Spread* spread, Register array);
......
...@@ -1889,13 +1889,11 @@ void Parser::DeclareFunctionNameVar(const AstRawString* function_name, ...@@ -1889,13 +1889,11 @@ void Parser::DeclareFunctionNameVar(const AstRawString* function_name,
// !%_IsJSReceiver(result = Await(iterator.next())) && // !%_IsJSReceiver(result = Await(iterator.next())) &&
// %ThrowIteratorResultNotAnObject(result) // %ThrowIteratorResultNotAnObject(result)
// [endif] // [endif]
Expression* Parser::BuildIteratorNextResult(Expression* iterator, Expression* Parser::BuildIteratorNextResult(VariableProxy* iterator,
VariableProxy* next,
Variable* result, IteratorType type, Variable* result, IteratorType type,
int pos) { int pos) {
Expression* next_literal = factory()->NewStringLiteral( Expression* next_property = factory()->NewResolvedProperty(iterator, next);
ast_value_factory()->next_string(), kNoSourcePosition);
Expression* next_property =
factory()->NewProperty(iterator, next_literal, kNoSourcePosition);
ZoneList<Expression*>* next_arguments = ZoneList<Expression*>* next_arguments =
new (zone()) ZoneList<Expression*>(0, zone()); new (zone()) ZoneList<Expression*>(0, zone());
Expression* next_call = Expression* next_call =
...@@ -2098,6 +2096,7 @@ Statement* Parser::InitializeForOfStatement( ...@@ -2098,6 +2096,7 @@ Statement* Parser::InitializeForOfStatement(
auto avfactory = ast_value_factory(); auto avfactory = ast_value_factory();
Variable* iterator = NewTemporary(avfactory->dot_iterator_string()); Variable* iterator = NewTemporary(avfactory->dot_iterator_string());
Variable* next = NewTemporary(avfactory->empty_string());
Variable* result = NewTemporary(avfactory->dot_result_string()); Variable* result = NewTemporary(avfactory->dot_result_string());
Variable* completion = NewTemporary(avfactory->empty_string()); Variable* completion = NewTemporary(avfactory->empty_string());
...@@ -2110,6 +2109,17 @@ Statement* Parser::InitializeForOfStatement( ...@@ -2110,6 +2109,17 @@ Statement* Parser::InitializeForOfStatement(
iterable->position()); iterable->position());
} }
Expression* assign_next;
{
assign_next = factory()->NewAssignment(
Token::ASSIGN, factory()->NewVariableProxy(next),
factory()->NewProperty(factory()->NewVariableProxy(iterator),
factory()->NewStringLiteral(
avfactory->next_string(), kNoSourcePosition),
kNoSourcePosition),
kNoSourcePosition);
}
// [if (IteratorType == kNormal)] // [if (IteratorType == kNormal)]
// !%_IsJSReceiver(result = iterator.next()) && // !%_IsJSReceiver(result = iterator.next()) &&
// %ThrowIteratorResultNotAnObject(result) // %ThrowIteratorResultNotAnObject(result)
...@@ -2119,9 +2129,10 @@ Statement* Parser::InitializeForOfStatement( ...@@ -2119,9 +2129,10 @@ Statement* Parser::InitializeForOfStatement(
// [endif] // [endif]
Expression* next_result; Expression* next_result;
{ {
Expression* iterator_proxy = factory()->NewVariableProxy(iterator); VariableProxy* iterator_proxy = factory()->NewVariableProxy(iterator);
next_result = VariableProxy* next_proxy = factory()->NewVariableProxy(next);
BuildIteratorNextResult(iterator_proxy, result, type, next_result_pos); next_result = BuildIteratorNextResult(iterator_proxy, next_proxy, result,
type, next_result_pos);
} }
// result.done // result.done
...@@ -2191,8 +2202,8 @@ Statement* Parser::InitializeForOfStatement( ...@@ -2191,8 +2202,8 @@ Statement* Parser::InitializeForOfStatement(
body = block; body = block;
} }
for_of->Initialize(body, iterator, assign_iterator, next_result, result_done, for_of->Initialize(body, iterator, assign_iterator, assign_next, next_result,
assign_each); result_done, assign_each);
return finalize ? FinalizeForOfStatement(for_of, completion, type, nopos) return finalize ? FinalizeForOfStatement(for_of, completion, type, nopos)
: for_of; : for_of;
} }
......
...@@ -402,13 +402,14 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { ...@@ -402,13 +402,14 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
Expression* RewriteDestructuringAssignment(Assignment* assignment); Expression* RewriteDestructuringAssignment(Assignment* assignment);
// [if (IteratorType == kAsync)] // [if (IteratorType == kAsync)]
// !%_IsJSReceiver(result = Await(iterator.next()) && // !%_IsJSReceiver(result = Await(next.[[Call]](iterator, « »)) &&
// %ThrowIteratorResultNotAnObject(result) // %ThrowIteratorResultNotAnObject(result)
// [else] // [else]
// !%_IsJSReceiver(result = iterator.next()) && // !%_IsJSReceiver(result = next.[[Call]](iterator, « »)) &&
// %ThrowIteratorResultNotAnObject(result) // %ThrowIteratorResultNotAnObject(result)
// [endif] // [endif]
Expression* BuildIteratorNextResult(Expression* iterator, Variable* result, Expression* BuildIteratorNextResult(VariableProxy* iterator,
VariableProxy* next, Variable* result,
IteratorType type, int pos); IteratorType type, int pos);
// Initialize the components of a for-in / for-of statement. // Initialize the components of a for-in / for-of statement.
......
...@@ -445,6 +445,11 @@ void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node, ...@@ -445,6 +445,11 @@ void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node,
auto iterator = CreateTempVar(factory()->NewGetIterator( auto iterator = CreateTempVar(factory()->NewGetIterator(
factory()->NewVariableProxy(temp), current_value_, IteratorType::kNormal, factory()->NewVariableProxy(temp), current_value_, IteratorType::kNormal,
current_value_->position())); current_value_->position()));
auto next = CreateTempVar(factory()->NewProperty(
factory()->NewVariableProxy(iterator),
factory()->NewStringLiteral(ast_value_factory()->next_string(),
kNoSourcePosition),
kNoSourcePosition));
auto done = auto done =
CreateTempVar(factory()->NewBooleanLiteral(false, kNoSourcePosition)); CreateTempVar(factory()->NewBooleanLiteral(false, kNoSourcePosition));
auto result = CreateTempVar(); auto result = CreateTempVar();
...@@ -525,7 +530,8 @@ void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node, ...@@ -525,7 +530,8 @@ void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node,
next_block->statements()->Add( next_block->statements()->Add(
factory()->NewExpressionStatement( factory()->NewExpressionStatement(
parser_->BuildIteratorNextResult( parser_->BuildIteratorNextResult(
factory()->NewVariableProxy(iterator), result, factory()->NewVariableProxy(iterator),
factory()->NewVariableProxy(next), result,
IteratorType::kNormal, kNoSourcePosition), IteratorType::kNormal, kNoSourcePosition),
kNoSourcePosition), kNoSourcePosition),
zone()); zone());
...@@ -599,6 +605,7 @@ void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node, ...@@ -599,6 +605,7 @@ void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node,
// result = IteratorNext(iterator); // result = IteratorNext(iterator);
Statement* get_next = factory()->NewExpressionStatement( Statement* get_next = factory()->NewExpressionStatement(
parser_->BuildIteratorNextResult(factory()->NewVariableProxy(iterator), parser_->BuildIteratorNextResult(factory()->NewVariableProxy(iterator),
factory()->NewVariableProxy(next),
result, IteratorType::kNormal, nopos), result, IteratorType::kNormal, nopos),
nopos); nopos);
...@@ -756,6 +763,7 @@ NOT_A_PATTERN(ImportCallExpression) ...@@ -756,6 +763,7 @@ NOT_A_PATTERN(ImportCallExpression)
NOT_A_PATTERN(Literal) NOT_A_PATTERN(Literal)
NOT_A_PATTERN(NativeFunctionLiteral) NOT_A_PATTERN(NativeFunctionLiteral)
NOT_A_PATTERN(RegExpLiteral) NOT_A_PATTERN(RegExpLiteral)
NOT_A_PATTERN(ResolvedProperty)
NOT_A_PATTERN(ReturnStatement) NOT_A_PATTERN(ReturnStatement)
NOT_A_PATTERN(SloppyBlockFunctionStatement) NOT_A_PATTERN(SloppyBlockFunctionStatement)
NOT_A_PATTERN(Spread) NOT_A_PATTERN(Spread)
......
...@@ -9,10 +9,10 @@ function testFunction() { ...@@ -9,10 +9,10 @@ function testFunction() {
var arr = |_|[1]; var arr = |_|[1];
var all = |_|[]; var all = |_|[];
for (var |_|k in |_|arr) { all.|C|push(k); } for (var |_|k in |_|arr) { all.|C|push(k); }
for (var |_|k of |_|arr) { all.|C|push(k); } for (var |C|k of |_|arr) { all.|C|push(k); }
for (var |_|k in |_|obj) { all.|C|push(k); } for (var |_|k in |_|obj) { all.|C|push(k); }
for (let |_|k in |_|arr) { all.|C|push(k); } for (let |_|k in |_|arr) { all.|C|push(k); }
for (let |_|k of |_|arr) { all.|C|push(k); } for (let |C|k of |_|arr) { all.|C|push(k); }
for (let |_|k in |_|obj) { all.|C|push(k); } for (let |_|k in |_|obj) { all.|C|push(k); }
var iterable = |_|{ var iterable = |_|{
...@@ -28,9 +28,9 @@ function testFunction() { ...@@ -28,9 +28,9 @@ function testFunction() {
};|R| };|R|
} }
}; };
for (var |_|k of |_|iterable) { all.|C|push(k); } for (var |C|k of |_|iterable) { all.|C|push(k); }
|_|iterable.i = 0; |_|iterable.i = 0;
for (let |_|k of |_|iterable) { all.|C|push(k); } for (let |C|k of |_|iterable) { all.|C|push(k); }
|R|} |R|}
(anonymous) (expr.js:0:0) (anonymous) (expr.js:0:0)
......
...@@ -95,10 +95,10 @@ function testForLoop() { ...@@ -95,10 +95,10 @@ function testForLoop() {
|R|} |R|}
function testForOfLoop() { function testForOfLoop() {
for (var |_|k of |_|[]) {} for (var |C|k of |_|[]) {}
for (var |_|k of |_|[1]) |_|k; for (var |C|k of |_|[1]) |_|k;
var a = |_|[]; var a = |_|[];
for (var |_|k of |_|a) {} for (var |C|k of |_|a) {}
|R|} |R|}
function testForInLoop() { function testForInLoop() {
......
...@@ -37,6 +37,8 @@ ...@@ -37,6 +37,8 @@
var r2 = testMax(1, 2); var r2 = testMax(1, 2);
assertEquals(3, called); // .next() is only loaded once during the iteration prologue (see
// https://github.com/tc39/ecma262/pull/988/ and v8:6861)
assertEquals(1, called);
assertEquals(2, r2); assertEquals(2, r2);
})(); })();
...@@ -220,13 +220,11 @@ assertThrows('fold(sum, 0, unreachable({}))', TypeError); ...@@ -220,13 +220,11 @@ assertThrows('fold(sum, 0, unreachable({}))', TypeError);
assertThrows('fold(sum, 0, unreachable(false))', TypeError); assertThrows('fold(sum, 0, unreachable(false))', TypeError);
assertThrows('fold(sum, 0, unreachable(37))', TypeError); assertThrows('fold(sum, 0, unreachable(37))', TypeError);
// "next" is looked up each time. // "next" is looked up only once during the iteration prologue (see
assertThrows('fold(sum, 0, remove_next_after(integers_until(10), 5))', // https://github.com/tc39/ecma262/pull/988)
TypeError); assertEquals(45, fold(sum, 0, remove_next_after(integers_until(10), 5)));
// It is not called at any other time.
assertEquals(45, assertEquals(45,
fold(sum, 0, remove_next_after(integers_until(10), 10))); fold(sum, 0, remove_next_after(integers_until(10), 10)));
// It is not looked up too many times.
assertEquals(45, assertEquals(45,
fold(sum, 0, poison_next_after(integers_until(10), 10))); fold(sum, 0, poison_next_after(integers_until(10), 10)));
......
...@@ -376,6 +376,11 @@ testSpreadCallsStrict(); ...@@ -376,6 +376,11 @@ testSpreadCallsStrict();
a[3] = 4; a[3] = 4;
var called = 0; var called = 0;
// .next method is only accessed during iteration prologue (see
// https://github.com/tc39/ecma262/pull/988)
let ArrayIteratorPrototype = Array.prototype[Symbol.iterator]().__proto__;
let ArrayIteratorPrototypeNextDescriptor =
Object.getOwnPropertyDescriptor(ArrayIteratorPrototype, 'next');
Object.defineProperty(Array.prototype, 2, { Object.defineProperty(Array.prototype, 2, {
get: function() { get: function() {
var ai = a[Symbol.iterator](); var ai = a[Symbol.iterator]();
...@@ -384,7 +389,8 @@ testSpreadCallsStrict(); ...@@ -384,7 +389,8 @@ testSpreadCallsStrict();
get: function() { get: function() {
called++; called++;
return original_next; return original_next;
} },
configurable: true
}); });
return 3; return 3;
}, },
...@@ -392,8 +398,10 @@ testSpreadCallsStrict(); ...@@ -392,8 +398,10 @@ testSpreadCallsStrict();
}); });
assertEquals(10, sum(...a)); assertEquals(10, sum(...a));
assertEquals(2, called); assertEquals(0, called);
Object.defineProperty(ArrayIteratorPrototype, 'next',
ArrayIteratorPrototypeNextDescriptor);
Object.defineProperty(Array.prototype, 2, {}); Object.defineProperty(Array.prototype, 2, {});
})(); })();
...@@ -430,9 +438,9 @@ testSpreadCallsStrict(); ...@@ -430,9 +438,9 @@ testSpreadCallsStrict();
countArgs(...a); countArgs(...a);
// should be called 4 times; 3 for the values, 1 for the final // .next method is only accessed during iteration prologue (see
// {value: undefined, done: true} pair // https://github.com/tc39/ecma262/pull/988)
assertEquals(4, called); assertEquals(1, called);
})(); })();
(function testArrayIteratorPrototypeModified() { (function testArrayIteratorPrototypeModified() {
......
...@@ -48,7 +48,9 @@ ...@@ -48,7 +48,9 @@
var r2 = testArgumentsPoint(1, 2); var r2 = testArgumentsPoint(1, 2);
assertEquals(3, called); // .next() is only loaded once during the iteration prologue (see
// https://github.com/tc39/ecma262/pull/988/ and v8:6861)
assertEquals(1, called);
assertInstanceof(r2, ArgumentsPoint); assertInstanceof(r2, ArgumentsPoint);
assertInstanceof(r2, Point); assertInstanceof(r2, Point);
assertEquals(r2.x, 1); assertEquals(r2.x, 1);
......
...@@ -341,16 +341,30 @@ function TestTypedArray(constr, elementSize, typicalElement) { ...@@ -341,16 +341,30 @@ function TestTypedArray(constr, elementSize, typicalElement) {
// Modified %ArrayIteratorPrototype%.next() method is honoured (v8:5699) // Modified %ArrayIteratorPrototype%.next() method is honoured (v8:5699)
const ArrayIteratorPrototype = Object.getPrototypeOf([][Symbol.iterator]()); const ArrayIteratorPrototype = Object.getPrototypeOf([][Symbol.iterator]());
const ArrayIteratorPrototypeNextDescriptor =
Object.getOwnPropertyDescriptor(ArrayIteratorPrototype, 'next');
const ArrayIteratorPrototypeNext = ArrayIteratorPrototype.next; const ArrayIteratorPrototypeNext = ArrayIteratorPrototype.next;
ArrayIteratorPrototype.next = function() { ArrayIteratorPrototype.next = function() {
return { done: true }; return { done: true };
}; };
genArr = new constr([1, 2, 3]); genArr = new constr([1, 2, 3]);
assertEquals(0, genArr.length); assertEquals(0, genArr.length);
ArrayIteratorPrototype.next = ArrayIteratorPrototypeNext; ArrayIteratorPrototype.next = ArrayIteratorPrototypeNext;
// Modified %ArrayIteratorPrototype%.next() during iteration is honoured as // Modified %ArrayIteratorPrototype%.next() is only loaded during the iterator
// well. // prologue.
let nextMethod = ArrayIteratorPrototypeNext;
let getNextCount = 0;
Object.defineProperty(ArrayIteratorPrototype, 'next', {
get() {
getNextCount++;
return nextMethod;
},
set(v) { nextMethod = v; },
configurable: true
});
genArr = new constr(Object.defineProperty([1, , 3], 1, { genArr = new constr(Object.defineProperty([1, , 3], 1, {
get() { get() {
ArrayIteratorPrototype.next = function() { ArrayIteratorPrototype.next = function() {
...@@ -359,9 +373,13 @@ function TestTypedArray(constr, elementSize, typicalElement) { ...@@ -359,9 +373,13 @@ function TestTypedArray(constr, elementSize, typicalElement) {
return 2; return 2;
} }
})); }));
assertEquals(2, genArr.length); Object.defineProperty(ArrayIteratorPrototype, 'next',
ArrayIteratorPrototypeNextDescriptor);
assertEquals(1, getNextCount);
assertEquals(3, genArr.length);
assertEquals(1, genArr[0]); assertEquals(1, genArr[0]);
assertEquals(2, genArr[1]); assertEquals(2, genArr[1]);
assertEquals(3, genArr[2]);
ArrayIteratorPrototype.next = ArrayIteratorPrototypeNext; ArrayIteratorPrototype.next = ArrayIteratorPrototypeNext;
} }
......
...@@ -423,19 +423,8 @@ ...@@ -423,19 +423,8 @@
'built-ins/Proxy/ownKeys/return-duplicate-symbol-entries-throws': [FAIL], 'built-ins/Proxy/ownKeys/return-duplicate-symbol-entries-throws': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=6861 # https://bugs.chromium.org/p/v8/issues/detail?id=6861
'language/statements/for-of/iterator-next-reference': [FAIL],
'language/expressions/async-generator/named-yield-star-async-next': [FAIL],
'language/expressions/async-generator/yield-star-async-next': [FAIL],
'language/expressions/class/async-gen-method-yield-star-async-next': [FAIL],
'language/expressions/class/async-gen-method-static-yield-star-async-next': [FAIL],
'language/expressions/object/method-definition/async-gen-yield-star-async-next': [FAIL],
'language/statements/async-generator/yield-star-async-next': [FAIL],
'language/statements/class/async-gen-method-yield-star-async-next': [FAIL],
'language/statements/class/async-gen-method-static-yield-star-async-next': [FAIL],
'language/expressions/object/method-definition/async-gen-yield-star-sync-next': [FAIL], 'language/expressions/object/method-definition/async-gen-yield-star-sync-next': [FAIL],
'language/expressions/class/async-gen-method-static-yield-star-sync-next': [FAIL], 'language/expressions/class/async-gen-method-static-yield-star-sync-next': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=6861
'language/expressions/async-generator/yield-star-sync-next': [FAIL], 'language/expressions/async-generator/yield-star-sync-next': [FAIL],
'language/statements/class/async-gen-method-static-yield-star-sync-next': [FAIL], 'language/statements/class/async-gen-method-static-yield-star-sync-next': [FAIL],
'language/expressions/async-generator/named-yield-star-sync-next': [FAIL], 'language/expressions/async-generator/named-yield-star-sync-next': [FAIL],
......
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