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) {
Visit(node->obj());
}
void AstNumberingVisitor::VisitResolvedProperty(ResolvedProperty* node) {
Visit(node->object());
Visit(node->property());
}
void AstNumberingVisitor::VisitAssignment(Assignment* node) {
Visit(node->target());
Visit(node->value());
......@@ -251,6 +256,7 @@ void AstNumberingVisitor::VisitForInStatement(ForInStatement* node) {
void AstNumberingVisitor::VisitForOfStatement(ForOfStatement* node) {
Visit(node->assign_iterator()); // Not part of loop.
Visit(node->assign_next());
node->set_first_suspend_id(suspend_count_);
Visit(node->next_result());
Visit(node->result_done());
......
......@@ -392,6 +392,14 @@ void AstTraversalVisitor<Subclass>::VisitProperty(Property* expr) {
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>
void AstTraversalVisitor<Subclass>::VisitCall(Call* expr) {
PROCESS_EXPRESSION(expr);
......
......@@ -805,6 +805,10 @@ Call::CallType Call::GetCallType() const {
}
}
if (expression()->IsResolvedProperty()) {
return RESOLVED_PROPERTY_CALL;
}
return OTHER_CALL;
}
......
......@@ -94,6 +94,7 @@ namespace internal {
V(Literal) \
V(NativeFunctionLiteral) \
V(Property) \
V(ResolvedProperty) \
V(RewritableExpression) \
V(Spread) \
V(SuperCallReference) \
......@@ -590,11 +591,13 @@ class ForInStatement final : public ForEachStatement {
class ForOfStatement final : public ForEachStatement {
public:
void Initialize(Statement* body, Variable* iterator,
Expression* assign_iterator, Expression* next_result,
Expression* result_done, Expression* assign_each) {
Expression* assign_iterator, Expression* assign_next,
Expression* next_result, Expression* result_done,
Expression* assign_each) {
ForEachStatement::Initialize(body);
iterator_ = iterator;
assign_iterator_ = assign_iterator;
assign_next_ = assign_next;
next_result_ = next_result;
result_done_ = result_done;
assign_each_ = assign_each;
......@@ -609,6 +612,9 @@ class ForOfStatement final : public ForEachStatement {
return assign_iterator_;
}
// iteratorRecord.next = iterator.next
Expression* assign_next() const { return assign_next_; }
// result = iterator.next() // with type check
Expression* next_result() const {
return next_result_;
......@@ -624,6 +630,12 @@ class ForOfStatement final : public ForEachStatement {
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:
friend class AstNodeFactory;
......@@ -637,6 +649,7 @@ class ForOfStatement final : public ForEachStatement {
Variable* iterator_;
Expression* assign_iterator_;
Expression* assign_next_;
Expression* next_result_;
Expression* result_done_;
Expression* assign_each_;
......@@ -1607,6 +1620,25 @@ class Property final : public Expression {
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 {
public:
......@@ -1633,6 +1665,7 @@ class Call final : public Expression {
NAMED_SUPER_PROPERTY_CALL,
KEYED_SUPER_PROPERTY_CALL,
SUPER_CALL,
RESOLVED_PROPERTY_CALL,
OTHER_CALL
};
......@@ -2104,7 +2137,6 @@ class YieldStar final : public Suspend {
// - One for awaiting the iterator result yielded by the delegated iterator
// (await_delegated_iterator_output_suspend_id)
int await_iterator_close_suspend_id() const {
DCHECK_NE(-1, await_iterator_close_suspend_id_);
return await_iterator_close_suspend_id_;
}
void set_await_iterator_close_suspend_id(int id) {
......@@ -2112,7 +2144,6 @@ class YieldStar final : public Suspend {
}
int await_delegated_iterator_output_suspend_id() const {
DCHECK_NE(-1, await_delegated_iterator_output_suspend_id_);
return await_delegated_iterator_output_suspend_id_;
}
void set_await_delegated_iterator_output_suspend_id(int id) {
......@@ -2997,6 +3028,12 @@ class AstNodeFactory final BASE_EMBEDDED {
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,
int pos, Call::PossiblyEval possibly_eval = Call::NOT_EVAL) {
return new (zone_) Call(expression, arguments, pos, possibly_eval);
......
......@@ -326,6 +326,7 @@ void CallPrinter::VisitProperty(Property* node) {
}
}
void CallPrinter::VisitResolvedProperty(ResolvedProperty* node) {}
void CallPrinter::VisitCall(Call* node) {
bool was_found = false;
......@@ -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) {
EmbeddedVector<char, 128> buf;
......
......@@ -232,10 +232,9 @@ void BaseCollectionsAssembler::AddConstructorEntriesFromIterable(
TNode<Object> add_func = GetAddFunction(variant, context, collection);
IteratorBuiltinsAssembler iterator_assembler(this->state());
TNode<Object> iterator =
CAST(iterator_assembler.GetIterator(context, iterable));
IteratorRecord iterator = iterator_assembler.GetIterator(context, iterable);
CSA_ASSERT(this, Word32BinaryNot(IsUndefined(iterator)));
CSA_ASSERT(this, Word32BinaryNot(IsUndefined(iterator.object)));
TNode<Object> fast_iterator_result_map =
LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
......
......@@ -11,7 +11,8 @@ namespace internal {
using compiler::Node;
Node* IteratorBuiltinsAssembler::GetIterator(Node* context, Node* object,
IteratorRecord IteratorBuiltinsAssembler::GetIterator(Node* context,
Node* object,
Label* if_exception,
Variable* exception) {
Node* method = GetProperty(context, object, factory()->iterator_symbol());
......@@ -21,9 +22,9 @@ Node* IteratorBuiltinsAssembler::GetIterator(Node* context, Node* object,
Node* iterator = CallJS(callable, context, method, object);
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);
Branch(IsJSReceiver(iterator), &done, &if_notobject);
Branch(IsJSReceiver(iterator), &get_next, &if_notobject);
BIND(&if_notobject);
{
......@@ -34,24 +35,21 @@ Node* IteratorBuiltinsAssembler::GetIterator(Node* context, Node* object,
Unreachable();
}
BIND(&done);
return iterator;
BIND(&get_next);
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,
Label* if_done,
Node* fast_iterator_result_map,
Label* if_exception,
Variable* exception) {
Node* IteratorBuiltinsAssembler::IteratorStep(
Node* context, const IteratorRecord& iterator, Label* if_done,
Node* fast_iterator_result_map, Label* if_exception, Variable* exception) {
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", « »).
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);
// 3. If Type(result) is not Object, throw a TypeError exception.
......@@ -129,9 +127,8 @@ Node* IteratorBuiltinsAssembler::IteratorValue(Node* context, Node* result,
return var_value.value();
}
void IteratorBuiltinsAssembler::IteratorCloseOnException(Node* context,
Node* iterator,
Label* if_exception,
void IteratorBuiltinsAssembler::IteratorCloseOnException(
Node* context, const IteratorRecord& iterator, Label* if_exception,
Variable* exception) {
// Perform ES #sec-iteratorclose when an exception occurs. This simpler
// algorithm does not include redundant steps which are never reachable from
......@@ -139,10 +136,11 @@ void IteratorBuiltinsAssembler::IteratorCloseOnException(Node* context,
DCHECK_NOT_NULL(if_exception);
DCHECK_NOT_NULL(exception);
CSA_ASSERT(this, IsNotTheHole(exception->value()));
CSA_ASSERT(this, IsJSReceiver(iterator));
CSA_ASSERT(this, IsJSReceiver(iterator.object));
// 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);
// If return is undefined, return Completion(completion).
......@@ -152,7 +150,7 @@ void IteratorBuiltinsAssembler::IteratorCloseOnException(Node* context,
// Let innerResult be Call(return, iterator, « »).
// If an exception occurs, the original exception remains bound
Node* inner_result =
CallJS(CodeFactory::Call(isolate()), context, method, iterator);
CallJS(CodeFactory::Call(isolate()), context, method, iterator.object);
GotoIfException(inner_result, if_exception, nullptr);
// (If completion.[[Type]] is throw) return Completion(completion).
......@@ -160,9 +158,8 @@ void IteratorBuiltinsAssembler::IteratorCloseOnException(Node* context,
}
}
void IteratorBuiltinsAssembler::IteratorCloseOnException(Node* context,
Node* iterator,
Variable* exception) {
void IteratorBuiltinsAssembler::IteratorCloseOnException(
Node* context, const IteratorRecord& iterator, Variable* exception) {
Label rethrow(this, Label::kDeferred);
IteratorCloseOnException(context, iterator, &rethrow, exception);
......
......@@ -19,7 +19,8 @@ class IteratorBuiltinsAssembler : public CodeStubAssembler {
// https://tc39.github.io/ecma262/#sec-getiterator --- never used for
// @@asyncIterator.
Node* GetIterator(Node* context, Node* object, Label* if_exception = nullptr,
IteratorRecord GetIterator(Node* context, Node* object,
Label* if_exception = nullptr,
Variable* exception = nullptr);
// https://tc39.github.io/ecma262/#sec-iteratorstep
......@@ -27,8 +28,8 @@ class IteratorBuiltinsAssembler : public CodeStubAssembler {
// iterator result.
// `fast_iterator_result_map` refers to the map for the JSIteratorResult
// object, loaded from the native context.
Node* IteratorStep(Node* context, Node* iterator, Label* if_done,
Node* fast_iterator_result_map = nullptr,
Node* IteratorStep(Node* context, const IteratorRecord& iterator,
Label* if_done, Node* fast_iterator_result_map = nullptr,
Label* if_exception = nullptr,
Variable* exception = nullptr);
......@@ -42,9 +43,9 @@ class IteratorBuiltinsAssembler : public CodeStubAssembler {
Variable* exception = nullptr);
// 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);
void IteratorCloseOnException(Node* context, Node* iterator,
void IteratorCloseOnException(Node* context, const IteratorRecord& iterator,
Variable* exception);
};
......
......@@ -1807,8 +1807,9 @@ TF_BUILTIN(PerformNativePromiseThen, PromiseBuiltinsAssembler) {
}
Node* PromiseBuiltinsAssembler::PerformPromiseAll(
Node* context, Node* constructor, Node* capability, Node* iterator,
Label* if_exception, Variable* var_exception) {
Node* context, Node* constructor, Node* capability,
const IteratorRecord& iterator, Label* if_exception,
Variable* var_exception) {
IteratorBuiltinsAssembler iter_assembler(state());
Label close_iterator(this);
......@@ -2014,7 +2015,7 @@ TF_BUILTIN(PromiseAll, PromiseBuiltinsAssembler) {
// Let iterator be GetIterator(iterable).
// IfAbruptRejectPromise(iterator, promiseCapability).
Node* const iterable = Parameter(Descriptor::kIterable);
Node* const iterator = iter_assembler.GetIterator(
IteratorRecord iterator = iter_assembler.GetIterator(
context, iterable, &reject_promise, &var_exception);
// Let result be PerformPromiseAll(iteratorRecord, C, promiseCapability).
......@@ -2151,7 +2152,7 @@ TF_BUILTIN(PromiseRace, PromiseBuiltinsAssembler) {
// Let iterator be GetIterator(iterable).
// IfAbruptRejectPromise(iterator, promiseCapability).
Node* const iterable = Parameter(Descriptor::kIterable);
Node* const iterator = iter_assembler.GetIterator(
IteratorRecord iterator = iter_assembler.GetIterator(
context, iterable, &reject_promise, &var_exception);
// Let result be PerformPromiseRace(iteratorRecord, C, promiseCapability).
......
......@@ -157,7 +157,7 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
Node* CreateThrowerFunction(Node* reason, Node* native_context);
Node* PerformPromiseAll(Node* context, Node* constructor, Node* capability,
Node* iterator, Label* if_exception,
const IteratorRecord& record, Label* if_exception,
Variable* var_exception);
Node* IncrementSmiCell(Node* cell, Label* if_overflow = nullptr);
......
......@@ -71,6 +71,17 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
promise_default_resolve_handler_symbol, \
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
// CodeAssembler. By factoring the JavaScript-isms out of the CodeAssembler,
// it's possible to add JavaScript-specific useful CodeAssembler "macros"
......
This diff is collapsed.
......@@ -151,9 +151,24 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void BuildAwait(int suspend_id);
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,
IteratorType hint);
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);
......
......@@ -1889,13 +1889,11 @@ void Parser::DeclareFunctionNameVar(const AstRawString* function_name,
// !%_IsJSReceiver(result = Await(iterator.next())) &&
// %ThrowIteratorResultNotAnObject(result)
// [endif]
Expression* Parser::BuildIteratorNextResult(Expression* iterator,
Expression* Parser::BuildIteratorNextResult(VariableProxy* iterator,
VariableProxy* next,
Variable* result, IteratorType type,
int pos) {
Expression* next_literal = factory()->NewStringLiteral(
ast_value_factory()->next_string(), kNoSourcePosition);
Expression* next_property =
factory()->NewProperty(iterator, next_literal, kNoSourcePosition);
Expression* next_property = factory()->NewResolvedProperty(iterator, next);
ZoneList<Expression*>* next_arguments =
new (zone()) ZoneList<Expression*>(0, zone());
Expression* next_call =
......@@ -2098,6 +2096,7 @@ Statement* Parser::InitializeForOfStatement(
auto avfactory = ast_value_factory();
Variable* iterator = NewTemporary(avfactory->dot_iterator_string());
Variable* next = NewTemporary(avfactory->empty_string());
Variable* result = NewTemporary(avfactory->dot_result_string());
Variable* completion = NewTemporary(avfactory->empty_string());
......@@ -2110,6 +2109,17 @@ Statement* Parser::InitializeForOfStatement(
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)]
// !%_IsJSReceiver(result = iterator.next()) &&
// %ThrowIteratorResultNotAnObject(result)
......@@ -2119,9 +2129,10 @@ Statement* Parser::InitializeForOfStatement(
// [endif]
Expression* next_result;
{
Expression* iterator_proxy = factory()->NewVariableProxy(iterator);
next_result =
BuildIteratorNextResult(iterator_proxy, result, type, next_result_pos);
VariableProxy* iterator_proxy = factory()->NewVariableProxy(iterator);
VariableProxy* next_proxy = factory()->NewVariableProxy(next);
next_result = BuildIteratorNextResult(iterator_proxy, next_proxy, result,
type, next_result_pos);
}
// result.done
......@@ -2191,8 +2202,8 @@ Statement* Parser::InitializeForOfStatement(
body = block;
}
for_of->Initialize(body, iterator, assign_iterator, next_result, result_done,
assign_each);
for_of->Initialize(body, iterator, assign_iterator, assign_next, next_result,
result_done, assign_each);
return finalize ? FinalizeForOfStatement(for_of, completion, type, nopos)
: for_of;
}
......
......@@ -402,13 +402,14 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
Expression* RewriteDestructuringAssignment(Assignment* assignment);
// [if (IteratorType == kAsync)]
// !%_IsJSReceiver(result = Await(iterator.next()) &&
// !%_IsJSReceiver(result = Await(next.[[Call]](iterator, « »)) &&
// %ThrowIteratorResultNotAnObject(result)
// [else]
// !%_IsJSReceiver(result = iterator.next()) &&
// !%_IsJSReceiver(result = next.[[Call]](iterator, « »)) &&
// %ThrowIteratorResultNotAnObject(result)
// [endif]
Expression* BuildIteratorNextResult(Expression* iterator, Variable* result,
Expression* BuildIteratorNextResult(VariableProxy* iterator,
VariableProxy* next, Variable* result,
IteratorType type, int pos);
// Initialize the components of a for-in / for-of statement.
......
......@@ -445,6 +445,11 @@ void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node,
auto iterator = CreateTempVar(factory()->NewGetIterator(
factory()->NewVariableProxy(temp), current_value_, IteratorType::kNormal,
current_value_->position()));
auto next = CreateTempVar(factory()->NewProperty(
factory()->NewVariableProxy(iterator),
factory()->NewStringLiteral(ast_value_factory()->next_string(),
kNoSourcePosition),
kNoSourcePosition));
auto done =
CreateTempVar(factory()->NewBooleanLiteral(false, kNoSourcePosition));
auto result = CreateTempVar();
......@@ -525,7 +530,8 @@ void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node,
next_block->statements()->Add(
factory()->NewExpressionStatement(
parser_->BuildIteratorNextResult(
factory()->NewVariableProxy(iterator), result,
factory()->NewVariableProxy(iterator),
factory()->NewVariableProxy(next), result,
IteratorType::kNormal, kNoSourcePosition),
kNoSourcePosition),
zone());
......@@ -599,6 +605,7 @@ void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node,
// result = IteratorNext(iterator);
Statement* get_next = factory()->NewExpressionStatement(
parser_->BuildIteratorNextResult(factory()->NewVariableProxy(iterator),
factory()->NewVariableProxy(next),
result, IteratorType::kNormal, nopos),
nopos);
......@@ -756,6 +763,7 @@ NOT_A_PATTERN(ImportCallExpression)
NOT_A_PATTERN(Literal)
NOT_A_PATTERN(NativeFunctionLiteral)
NOT_A_PATTERN(RegExpLiteral)
NOT_A_PATTERN(ResolvedProperty)
NOT_A_PATTERN(ReturnStatement)
NOT_A_PATTERN(SloppyBlockFunctionStatement)
NOT_A_PATTERN(Spread)
......
......@@ -9,10 +9,10 @@ function testFunction() {
var arr = |_|[1];
var all = |_|[];
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 (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); }
var iterable = |_|{
......@@ -28,9 +28,9 @@ function testFunction() {
};|R|
}
};
for (var |_|k of |_|iterable) { all.|C|push(k); }
for (var |C|k of |_|iterable) { all.|C|push(k); }
|_|iterable.i = 0;
for (let |_|k of |_|iterable) { all.|C|push(k); }
for (let |C|k of |_|iterable) { all.|C|push(k); }
|R|}
(anonymous) (expr.js:0:0)
......
......@@ -95,10 +95,10 @@ function testForLoop() {
|R|}
function testForOfLoop() {
for (var |_|k of |_|[]) {}
for (var |_|k of |_|[1]) |_|k;
for (var |C|k of |_|[]) {}
for (var |C|k of |_|[1]) |_|k;
var a = |_|[];
for (var |_|k of |_|a) {}
for (var |C|k of |_|a) {}
|R|}
function testForInLoop() {
......
......@@ -37,6 +37,8 @@
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);
})();
......@@ -220,13 +220,11 @@ assertThrows('fold(sum, 0, unreachable({}))', TypeError);
assertThrows('fold(sum, 0, unreachable(false))', TypeError);
assertThrows('fold(sum, 0, unreachable(37))', TypeError);
// "next" is looked up each time.
assertThrows('fold(sum, 0, remove_next_after(integers_until(10), 5))',
TypeError);
// It is not called at any other time.
// "next" is looked up only once during the iteration prologue (see
// https://github.com/tc39/ecma262/pull/988)
assertEquals(45, fold(sum, 0, remove_next_after(integers_until(10), 5)));
assertEquals(45,
fold(sum, 0, remove_next_after(integers_until(10), 10)));
// It is not looked up too many times.
assertEquals(45,
fold(sum, 0, poison_next_after(integers_until(10), 10)));
......
......@@ -376,6 +376,11 @@ testSpreadCallsStrict();
a[3] = 4;
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, {
get: function() {
var ai = a[Symbol.iterator]();
......@@ -384,7 +389,8 @@ testSpreadCallsStrict();
get: function() {
called++;
return original_next;
}
},
configurable: true
});
return 3;
},
......@@ -392,8 +398,10 @@ testSpreadCallsStrict();
});
assertEquals(10, sum(...a));
assertEquals(2, called);
assertEquals(0, called);
Object.defineProperty(ArrayIteratorPrototype, 'next',
ArrayIteratorPrototypeNextDescriptor);
Object.defineProperty(Array.prototype, 2, {});
})();
......@@ -430,9 +438,9 @@ testSpreadCallsStrict();
countArgs(...a);
// should be called 4 times; 3 for the values, 1 for the final
// {value: undefined, done: true} pair
assertEquals(4, called);
// .next method is only accessed during iteration prologue (see
// https://github.com/tc39/ecma262/pull/988)
assertEquals(1, called);
})();
(function testArrayIteratorPrototypeModified() {
......
......@@ -48,7 +48,9 @@
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, Point);
assertEquals(r2.x, 1);
......
......@@ -341,16 +341,30 @@ function TestTypedArray(constr, elementSize, typicalElement) {
// Modified %ArrayIteratorPrototype%.next() method is honoured (v8:5699)
const ArrayIteratorPrototype = Object.getPrototypeOf([][Symbol.iterator]());
const ArrayIteratorPrototypeNextDescriptor =
Object.getOwnPropertyDescriptor(ArrayIteratorPrototype, 'next');
const ArrayIteratorPrototypeNext = ArrayIteratorPrototype.next;
ArrayIteratorPrototype.next = function() {
return { done: true };
};
genArr = new constr([1, 2, 3]);
assertEquals(0, genArr.length);
ArrayIteratorPrototype.next = ArrayIteratorPrototypeNext;
// Modified %ArrayIteratorPrototype%.next() during iteration is honoured as
// well.
// Modified %ArrayIteratorPrototype%.next() is only loaded during the iterator
// 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, {
get() {
ArrayIteratorPrototype.next = function() {
......@@ -359,9 +373,13 @@ function TestTypedArray(constr, elementSize, typicalElement) {
return 2;
}
}));
assertEquals(2, genArr.length);
Object.defineProperty(ArrayIteratorPrototype, 'next',
ArrayIteratorPrototypeNextDescriptor);
assertEquals(1, getNextCount);
assertEquals(3, genArr.length);
assertEquals(1, genArr[0]);
assertEquals(2, genArr[1]);
assertEquals(3, genArr[2]);
ArrayIteratorPrototype.next = ArrayIteratorPrototypeNext;
}
......
......@@ -423,19 +423,8 @@
'built-ins/Proxy/ownKeys/return-duplicate-symbol-entries-throws': [FAIL],
# 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/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/statements/class/async-gen-method-static-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