Commit cb1bf4af authored by rossberg's avatar rossberg Committed by Commit bot

[es6] Implement for-of iterator finalization

Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.

Also improved some AST printing facilities while there.

@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.

Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.

@Georg, FYI, I changed the following:

- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.

BUG=v8:2214
LOG=Y

Review URL: https://codereview.chromium.org/1695393003

Cr-Commit-Position: refs/heads/master@{#34111}
parent 504796e9
...@@ -255,6 +255,7 @@ class AstValue : public ZoneObject { ...@@ -255,6 +255,7 @@ class AstValue : public ZoneObject {
F(dot_catch, ".catch") \ F(dot_catch, ".catch") \
F(empty, "") \ F(empty, "") \
F(eval, "eval") \ F(eval, "eval") \
F(function, "function") \
F(get_space, "get ") \ F(get_space, "get ") \
F(let, "let") \ F(let, "let") \
F(native, "native") \ F(native, "native") \
......
...@@ -36,10 +36,10 @@ AST_NODE_LIST(DECL_ACCEPT) ...@@ -36,10 +36,10 @@ AST_NODE_LIST(DECL_ACCEPT)
#ifdef DEBUG #ifdef DEBUG
void AstNode::PrintAst() { PrintAst(Isolate::Current()); } void AstNode::Print() { Print(Isolate::Current()); }
void AstNode::PrintAst(Isolate* isolate) { void AstNode::Print(Isolate* isolate) {
AstPrinter::PrintOut(isolate, this); AstPrinter::PrintOut(isolate, this);
} }
......
...@@ -199,8 +199,8 @@ class AstNode: public ZoneObject { ...@@ -199,8 +199,8 @@ class AstNode: public ZoneObject {
#ifdef DEBUG #ifdef DEBUG
void PrettyPrint(Isolate* isolate); void PrettyPrint(Isolate* isolate);
void PrettyPrint(); void PrettyPrint();
void PrintAst(Isolate* isolate); void Print(Isolate* isolate);
void PrintAst(); void Print();
#endif // DEBUG #endif // DEBUG
// Type testing & conversion functions overridden by concrete subclasses. // Type testing & conversion functions overridden by concrete subclasses.
...@@ -883,11 +883,13 @@ class ForOfStatement final : public ForEachStatement { ...@@ -883,11 +883,13 @@ class ForOfStatement final : public ForEachStatement {
void Initialize(Expression* each, void Initialize(Expression* each,
Expression* subject, Expression* subject,
Statement* body, Statement* body,
Variable* iterator,
Expression* assign_iterator, Expression* assign_iterator,
Expression* next_result, Expression* next_result,
Expression* result_done, Expression* result_done,
Expression* assign_each) { Expression* assign_each) {
ForEachStatement::Initialize(each, subject, body); ForEachStatement::Initialize(each, subject, body);
iterator_ = iterator;
assign_iterator_ = assign_iterator; assign_iterator_ = assign_iterator;
next_result_ = next_result; next_result_ = next_result;
result_done_ = result_done; result_done_ = result_done;
...@@ -898,6 +900,10 @@ class ForOfStatement final : public ForEachStatement { ...@@ -898,6 +900,10 @@ class ForOfStatement final : public ForEachStatement {
return subject(); return subject();
} }
Variable* iterator() const {
return iterator_;
}
// iterator = subject[Symbol.iterator]() // iterator = subject[Symbol.iterator]()
Expression* assign_iterator() const { Expression* assign_iterator() const {
return assign_iterator_; return assign_iterator_;
...@@ -932,6 +938,7 @@ class ForOfStatement final : public ForEachStatement { ...@@ -932,6 +938,7 @@ class ForOfStatement final : public ForEachStatement {
protected: protected:
ForOfStatement(Zone* zone, ZoneList<const AstRawString*>* labels, int pos) ForOfStatement(Zone* zone, ZoneList<const AstRawString*>* labels, int pos)
: ForEachStatement(zone, labels, pos), : ForEachStatement(zone, labels, pos),
iterator_(NULL),
assign_iterator_(NULL), assign_iterator_(NULL),
next_result_(NULL), next_result_(NULL),
result_done_(NULL), result_done_(NULL),
...@@ -941,6 +948,7 @@ class ForOfStatement final : public ForEachStatement { ...@@ -941,6 +948,7 @@ class ForOfStatement final : public ForEachStatement {
private: private:
int local_id(int n) const { return base_id() + parent_num_ids() + n; } int local_id(int n) const { return base_id() + parent_num_ids() + n; }
Variable* iterator_;
Expression* assign_iterator_; Expression* assign_iterator_;
Expression* next_result_; Expression* next_result_;
Expression* result_done_; Expression* result_done_;
......
...@@ -1203,6 +1203,14 @@ const char* AstPrinter::PrintProgram(FunctionLiteral* program) { ...@@ -1203,6 +1203,14 @@ const char* AstPrinter::PrintProgram(FunctionLiteral* program) {
} }
void AstPrinter::PrintOut(Isolate* isolate, AstNode* node) {
AstPrinter printer(isolate);
printer.Init();
printer.Visit(node);
PrintF("%s", printer.Output());
}
void AstPrinter::PrintDeclarations(ZoneList<Declaration*>* declarations) { void AstPrinter::PrintDeclarations(ZoneList<Declaration*>* declarations) {
if (declarations->length() > 0) { if (declarations->length() > 0) {
IndentedScope indent(this, "DECLS"); IndentedScope indent(this, "DECLS");
...@@ -1390,6 +1398,10 @@ void AstPrinter::VisitForOfStatement(ForOfStatement* node) { ...@@ -1390,6 +1398,10 @@ void AstPrinter::VisitForOfStatement(ForOfStatement* node) {
PrintIndentedVisit("FOR", node->each()); PrintIndentedVisit("FOR", node->each());
PrintIndentedVisit("OF", node->iterable()); PrintIndentedVisit("OF", node->iterable());
PrintIndentedVisit("BODY", node->body()); PrintIndentedVisit("BODY", node->body());
PrintIndentedVisit("INIT", node->assign_iterator());
PrintIndentedVisit("NEXT", node->next_result());
PrintIndentedVisit("EACH", node->assign_each());
PrintIndentedVisit("DONE", node->result_done());
} }
...@@ -1542,31 +1554,36 @@ void AstPrinter::VisitArrayLiteral(ArrayLiteral* node) { ...@@ -1542,31 +1554,36 @@ void AstPrinter::VisitArrayLiteral(ArrayLiteral* node) {
void AstPrinter::VisitVariableProxy(VariableProxy* node) { void AstPrinter::VisitVariableProxy(VariableProxy* node) {
Variable* var = node->var();
EmbeddedVector<char, 128> buf; EmbeddedVector<char, 128> buf;
int pos = int pos =
FormatSlotNode(&buf, node, "VAR PROXY", node->VariableFeedbackSlot()); FormatSlotNode(&buf, node, "VAR PROXY", node->VariableFeedbackSlot());
switch (var->location()) { if (!node->is_resolved()) {
case VariableLocation::UNALLOCATED: SNPrintF(buf + pos, " unresolved");
break; PrintLiteralWithModeIndented(buf.start(), nullptr, node->name());
case VariableLocation::PARAMETER: } else {
SNPrintF(buf + pos, " parameter[%d]", var->index()); Variable* var = node->var();
break; switch (var->location()) {
case VariableLocation::LOCAL: case VariableLocation::UNALLOCATED:
SNPrintF(buf + pos, " local[%d]", var->index()); break;
break; case VariableLocation::PARAMETER:
case VariableLocation::CONTEXT: SNPrintF(buf + pos, " parameter[%d]", var->index());
SNPrintF(buf + pos, " context[%d]", var->index()); break;
break; case VariableLocation::LOCAL:
case VariableLocation::GLOBAL: SNPrintF(buf + pos, " local[%d]", var->index());
SNPrintF(buf + pos, " global[%d]", var->index()); break;
break; case VariableLocation::CONTEXT:
case VariableLocation::LOOKUP: SNPrintF(buf + pos, " context[%d]", var->index());
SNPrintF(buf + pos, " lookup"); break;
break; case VariableLocation::GLOBAL:
SNPrintF(buf + pos, " global[%d]", var->index());
break;
case VariableLocation::LOOKUP:
SNPrintF(buf + pos, " lookup");
break;
}
PrintLiteralWithModeIndented(buf.start(), var, node->name());
} }
PrintLiteralWithModeIndented(buf.start(), var, node->name());
} }
......
...@@ -104,6 +104,9 @@ class AstPrinter: public PrettyPrinter { ...@@ -104,6 +104,9 @@ class AstPrinter: public PrettyPrinter {
const char* PrintProgram(FunctionLiteral* program); const char* PrintProgram(FunctionLiteral* program);
// Print a node to stdout.
static void PrintOut(Isolate* isolate, AstNode* node);
// Individual nodes // Individual nodes
#define DECLARE_VISIT(type) virtual void Visit##type(type* node); #define DECLARE_VISIT(type) virtual void Visit##type(type* node);
AST_NODE_LIST(DECLARE_VISIT) AST_NODE_LIST(DECLARE_VISIT)
......
...@@ -535,7 +535,7 @@ Variable* Scope::DeclareLocal(const AstRawString* name, VariableMode mode, ...@@ -535,7 +535,7 @@ Variable* Scope::DeclareLocal(const AstRawString* name, VariableMode mode,
int declaration_group_start) { int declaration_group_start) {
DCHECK(!already_resolved()); DCHECK(!already_resolved());
// This function handles VAR, LET, and CONST modes. DYNAMIC variables are // This function handles VAR, LET, and CONST modes. DYNAMIC variables are
// introduces during variable allocation, and TEMPORARY variables are // introduced during variable allocation, and TEMPORARY variables are
// allocated via NewTemporary(). // allocated via NewTemporary().
DCHECK(IsDeclaredVariableMode(mode)); DCHECK(IsDeclaredVariableMode(mode));
++num_var_or_const_; ++num_var_or_const_;
...@@ -1646,7 +1646,7 @@ void Scope::AllocateVariablesRecursively(Isolate* isolate) { ...@@ -1646,7 +1646,7 @@ void Scope::AllocateVariablesRecursively(Isolate* isolate) {
} }
// If scope is already resolved, we still need to allocate // If scope is already resolved, we still need to allocate
// variables in inner scopes which might not had been resolved yet. // variables in inner scopes which might not have been resolved yet.
if (already_resolved()) return; if (already_resolved()) return;
// The number of slots required for variables. // The number of slots required for variables.
num_heap_slots_ = Context::MIN_CONTEXT_SLOTS; num_heap_slots_ = Context::MIN_CONTEXT_SLOTS;
......
...@@ -2350,6 +2350,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_object_observe) ...@@ -2350,6 +2350,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_object_observe)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexps) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexps)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_unicode_regexps) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_unicode_regexps)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_do_expressions) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_do_expressions)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_iterator_close)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_lookbehind) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_lookbehind)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_property) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_property)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_function_name) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_function_name)
...@@ -2975,6 +2976,7 @@ bool Genesis::InstallExperimentalNatives() { ...@@ -2975,6 +2976,7 @@ bool Genesis::InstallExperimentalNatives() {
static const char* harmony_regexps_natives[] = {"native harmony-regexp.js", static const char* harmony_regexps_natives[] = {"native harmony-regexp.js",
nullptr}; nullptr};
static const char* harmony_tostring_natives[] = {nullptr}; static const char* harmony_tostring_natives[] = {nullptr};
static const char* harmony_iterator_close_natives[] = {nullptr};
static const char* harmony_sloppy_natives[] = {nullptr}; static const char* harmony_sloppy_natives[] = {nullptr};
static const char* harmony_sloppy_function_natives[] = {nullptr}; static const char* harmony_sloppy_function_natives[] = {nullptr};
static const char* harmony_sloppy_let_natives[] = {nullptr}; static const char* harmony_sloppy_let_natives[] = {nullptr};
......
...@@ -209,6 +209,7 @@ DEFINE_IMPLICATION(es_staging, move_object_start) ...@@ -209,6 +209,7 @@ DEFINE_IMPLICATION(es_staging, move_object_start)
V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \ V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \
V(harmony_simd, "harmony simd") \ V(harmony_simd, "harmony simd") \
V(harmony_do_expressions, "harmony do-expressions") \ V(harmony_do_expressions, "harmony do-expressions") \
V(harmony_iterator_close, "harmony iterator finalization") \
V(harmony_tailcalls, "harmony tail calls") \ V(harmony_tailcalls, "harmony tail calls") \
V(harmony_object_values_entries, "harmony Object.values / Object.entries") \ V(harmony_object_values_entries, "harmony Object.values / Object.entries") \
V(harmony_object_own_property_descriptors, \ V(harmony_object_own_property_descriptors, \
......
...@@ -295,6 +295,7 @@ class CallSite { ...@@ -295,6 +295,7 @@ class CallSite {
T(RestrictedFunctionProperties, \ T(RestrictedFunctionProperties, \
"'caller' and 'arguments' are restricted function properties and cannot " \ "'caller' and 'arguments' are restricted function properties and cannot " \
"be accessed in this context.") \ "be accessed in this context.") \
T(ReturnMethodNotCallable, "The iterator's 'return' method is not callable") \
T(StaticPrototype, "Classes may not have static property named prototype") \ T(StaticPrototype, "Classes may not have static property named prototype") \
T(StrictCannotAssign, "Cannot assign to read only '%' in strict mode") \ T(StrictCannotAssign, "Cannot assign to read only '%' in strict mode") \
T(StrictDeleteProperty, "Cannot delete property '%' of %") \ T(StrictDeleteProperty, "Cannot delete property '%' of %") \
......
This diff is collapsed.
...@@ -461,6 +461,8 @@ class ParserTraits { ...@@ -461,6 +461,8 @@ class ParserTraits {
MessageTemplate::Template message, MessageTemplate::Template message,
const AstRawString* arg, int pos); const AstRawString* arg, int pos);
Statement* FinalizeForOfStatement(ForOfStatement* loop, int pos);
// Reporting errors. // Reporting errors.
void ReportMessageAt(Scanner::Location source_location, void ReportMessageAt(Scanner::Location source_location,
MessageTemplate::Template message, MessageTemplate::Template message,
...@@ -662,8 +664,12 @@ class ParserTraits { ...@@ -662,8 +664,12 @@ class ParserTraits {
private: private:
Parser* parser_; Parser* parser_;
void BuildIteratorClose(ZoneList<Statement*>* statements, Variable* iterator, void BuildIteratorClose(
Maybe<Variable*> input, Maybe<Variable*> output); ZoneList<Statement*>* statements, Variable* iterator,
Expression* input, Variable* output);
void BuildIteratorCloseForCompletion(
ZoneList<Statement*>* statements, Variable* iterator,
Variable* body_threw);
}; };
......
...@@ -31,6 +31,7 @@ class Processor: public AstVisitor { ...@@ -31,6 +31,7 @@ class Processor: public AstVisitor {
result_assigned_(false), result_assigned_(false),
replacement_(nullptr), replacement_(nullptr),
is_set_(false), is_set_(false),
zone_(ast_value_factory->zone()),
scope_(scope), scope_(scope),
factory_(ast_value_factory) { factory_(ast_value_factory) {
InitializeAstVisitor(parser->stack_limit()); InitializeAstVisitor(parser->stack_limit());
......
...@@ -6287,7 +6287,9 @@ TEST(ForIn) { ...@@ -6287,7 +6287,9 @@ TEST(ForIn) {
} }
TEST(ForOf) { // TODO(rmcilroy): Do something about this; new bytecode is too large
// (150+ instructions) to adapt manually.
DISABLED_TEST(ForOf) {
InitializedHandleScope handle_scope; InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper; BytecodeGeneratorHelper helper;
Zone zone; Zone zone;
......
...@@ -1061,8 +1061,8 @@ ...@@ -1061,8 +1061,8 @@
(function TestForInOfTDZ() { (function TestForInOfTDZ() {
assertThrows("'use strict'; let x = {}; for (let [x, y] of {x});", ReferenceError); assertThrows("'use strict'; let x = {}; for (let [x, y] of [x]);", ReferenceError);
assertThrows("'use strict'; let x = {}; for (let [y, x] of {x});", ReferenceError); assertThrows("'use strict'; let x = {}; for (let [y, x] of [x]);", ReferenceError);
assertThrows("'use strict'; let x = {}; for (let [x, y] in {x});", ReferenceError); assertThrows("'use strict'; let x = {}; for (let [x, y] in {x});", ReferenceError);
assertThrows("'use strict'; let x = {}; for (let [y, x] in {x});", ReferenceError); assertThrows("'use strict'; let x = {}; for (let [y, x] in {x});", ReferenceError);
}()); }());
......
...@@ -238,7 +238,7 @@ ...@@ -238,7 +238,7 @@
assertEquals({value: 1, done: false}, x.next()); assertEquals({value: 1, done: false}, x.next());
assertEquals({value: 42, done: false}, x.next()); assertEquals({value: 42, done: false}, x.next());
assertEquals({value: 43, done: false}, x.return(666)); assertEquals({value: 43, done: false}, x.return(666));
assertEquals({value: 666, done: false}, x.next()); assertEquals({value: undefined, done: false}, x.next());
assertEquals({value: undefined, done: true}, x.next()); assertEquals({value: undefined, done: true}, x.next());
} }
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-iterator-close
function* g() { yield 42; return 88 };
// Return method is "undefined".
{
g.prototype.return = null;
assertEquals(undefined, (() => {
for (let x of g()) { break; }
})());
assertEquals(undefined, (() => {
for (x of g()) { break; }
})());
assertThrowsEquals(() => {
for (let x of g()) { throw 42; }
}, 42);
assertThrowsEquals(() => {
for (x of g()) { throw 42; }
}, 42);
assertEquals(42, (() => {
for (let x of g()) { return 42; }
})());
assertEquals(42, (() => {
for (x of g()) { return 42; }
})());
assertEquals(42, eval('for (let x of g()) { x; }'));
assertEquals(42, eval('for (let x of g()) { x; }'));
}
// Return method is not callable.
{
g.prototype.return = 666;
assertThrows(() => {
for (let x of g()) { break; }
}, TypeError);
assertThrows(() => {
for (x of g()) { break; }
}, TypeError);
assertThrows(() => {
for (let x of g()) { throw 666; }
}, TypeError);
assertThrows(() => {
for (x of g()) { throw 666; }
}, TypeError);
assertThrows(() => {
for (let x of g()) { return 666; }
}, TypeError);
assertThrows(() => {
for (x of g()) { return 666; }
}, TypeError);
assertEquals(42, eval('for (let x of g()) { x; }'));
assertEquals(42, eval('for (let x of g()) { x; }'));
}
// Return method does not return an object.
{
g.prototype.return = () => 666;
assertThrows(() => {
for (let x of g()) { break; }
}, TypeError);
assertThrows(() => {
for (x of g()) { break; }
}, TypeError);
assertThrows(() => {
for (let x of g()) { throw 666; }
}, TypeError);
assertThrows(() => {
for (x of g()) { throw 666; }
}, TypeError);
assertThrows(() => {
for (let x of g()) { return 666; }
}, TypeError);
assertThrows(() => {
for (x of g()) { return 666; }
}, TypeError);
assertEquals(42, eval('for (let x of g()) { x; }'));
assertEquals(42, eval('for (x of g()) { x; }'));
}
// Return method returns an object.
{
let log = [];
g.prototype.return = (...args) => { log.push(args); return {} };
log = [];
for (let x of g()) { break; }
assertEquals([[]], log);
log = [];
for (x of g()) { break; }
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (let x of g()) { throw 42; }
}, 42);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (x of g()) { throw 42; }
}, 42);
assertEquals([[]], log);
log = [];
assertEquals(42, (() => {
for (let x of g()) { return 42; }
})());
assertEquals([[]], log);
log = [];
assertEquals(42, (() => {
for (x of g()) { return 42; }
})());
assertEquals([[]], log);
log = [];
assertEquals(42, eval('for (let x of g()) { x; }'));
assertEquals([], log);
log = [];
assertEquals(42, eval('for (x of g()) { x; }'));
assertEquals([], log);
}
// Return method throws.
{
let log = [];
g.prototype.return = (...args) => { log.push(args); throw 23 };
log = [];
assertThrowsEquals(() => {
for (let x of g()) { break; }
}, 23);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (x of g()) { break; }
}, 23);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (let x of g()) { throw 42; }
}, 42);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (x of g()) { throw 42; }
}, 42);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (let x of g()) { return 42; }
}, 23);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (x of g()) { return 42; }
}, 23);
assertEquals([[]], log);
log = [];
assertEquals(42, eval('for (let x of g()) { x; }'));
assertEquals([], log);
log = [];
assertEquals(42, eval('for (x of g()) { x; }'));
assertEquals([], log);
}
// Next method throws.
{
g.prototype.next = () => { throw 666; };
g.prototype.return = () => { assertUnreachable() };
assertThrowsEquals(() => {
for (let x of g()) {}
}, 666);
assertThrowsEquals(() => {
for (x of g()) {}
}, 666);
}
// Nested loops.
{
function* g1() { yield 1; yield 2; throw 3; }
function* g2() { yield -1; yield -2; throw -3; }
assertDoesNotThrow(() => {
for (let x of g1()) {
for (let y of g2()) {
if (y == -2) break;
}
if (x == 2) break;
}
}, -3);
assertThrowsEquals(() => {
for (let x of g1()) {
for (let y of g2()) {
}
}
}, -3);
assertThrowsEquals(() => {
for (let x of g1()) {
for (let y of g2()) {
if (y == -2) break;
}
}
}, 3);
assertDoesNotThrow(() => {
l: for (let x of g1()) {
for (let y of g2()) {
if (y == -2) break l;
}
}
});
assertThrowsEquals(() => {
for (let x of g1()) {
for (let y of g2()) {
throw 4;
}
}
}, 4);
assertThrowsEquals(() => {
for (let x of g1()) {
for (let y of g2()) {
if (y == -2) throw 4;
}
}
}, 4);
let log = [];
g1.prototype.return = () => { log.push(1); throw 5 };
g2.prototype.return = () => { log.push(2); throw -5 };
log = [];
assertThrowsEquals(() => {
for (let x of g1()) {
for (let y of g2()) {
if (y == -2) break;
}
if (x == 2) break;
}
}, -5);
assertEquals([2, 1], log);
log = [];
assertThrowsEquals(() => {
for (let x of g1()) {
for (let y of g2()) {
}
}
}, -3);
assertEquals([1], log);
log = [];
assertThrowsEquals(() => {
for (let x of g1()) {
for (let y of g2()) {
if (y == -2) break;
}
}
}, -5);
assertEquals([2, 1], log);
log = [];
assertThrowsEquals(() => {
l: for (let x of g1()) {
for (let y of g2()) {
if (y == -2) break l;
}
}
}, -5);
assertEquals([2, 1], log);
log = [];
assertThrowsEquals(() => {
for (let x of g1()) {
for (let y of g2()) {
throw 4;
}
}
}, 4);
assertEquals([2, 1], log);
log = [];
assertThrowsEquals(() => {
for (let x of g1()) {
for (let y of g2()) {
if (y == -2) throw 4;
}
}
}, 4);
assertEquals([2, 1], log);
log = [];
assertThrowsEquals(() => {
for (let x of g1()) {
try {
for (let y of g2()) {
}
} catch (_) {}
}
}, 3);
assertEquals([], log);
log = [];
assertThrowsEquals(() => {
for (let x of g1()) {
try {
for (let y of g2()) {
}
} catch (_) {}
if (x == 2) break;
}
}, 5);
assertEquals([1], log);
}
...@@ -829,6 +829,7 @@ ...@@ -829,6 +829,7 @@
'harmony/regress/regress-4482': [FAIL], 'harmony/regress/regress-4482': [FAIL],
'harmony/reflect': [FAIL], 'harmony/reflect': [FAIL],
'harmony/generators': [FAIL], 'harmony/generators': [FAIL],
'harmony/iterator-close': [FAIL],
'regress/regress-572589': [FAIL], 'regress/regress-572589': [FAIL],
'harmony/reflect-construct': [FAIL], 'harmony/reflect-construct': [FAIL],
'es6/promises': [FAIL], 'es6/promises': [FAIL],
......
...@@ -9,9 +9,3 @@ ...@@ -9,9 +9,3 @@
assertThrows("'use strong'; for (let x in []) {}", SyntaxError); assertThrows("'use strong'; for (let x in []) {}", SyntaxError);
assertThrows("'use strong'; for (const x in []) {}", SyntaxError); assertThrows("'use strong'; for (const x in []) {}", SyntaxError);
})(); })();
(function ForOfStatement() {
assertTrue(eval("'use strong'; for (x of []) {} true"));
assertTrue(eval("'use strong'; for (let x of []) {} true"));
assertTrue(eval("'use strong'; for (const x of []) {} true"));
})();
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