Commit 3062af70 authored by neis's avatar neis Committed by Commit bot

Implement iterator finalization in array destructuring.

We must close the iterator whenever the destructuring didn't exhaust it, unless an iterator operation (eg. next) threw.  We do this by wrapping the iterator use in a try-catch-finally similar to the desugaring of for-of.

This is behind --harmony-iterator-close.

R=adamk@chromium.org
BUG=v8:3566
LOG=Y

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

Cr-Commit-Position: refs/heads/master@{#34654}
parent 9bf7730d
......@@ -6528,9 +6528,119 @@ void ParserTraits::BuildIteratorClose(ZoneList<Statement*>* statements,
statements->Add(validate_output, zone);
}
void ParserTraits::FinalizeIteratorUse(Variable* completion,
Expression* condition, Variable* iter,
Block* iterator_use, Block* target) {
if (!FLAG_harmony_iterator_close) return;
// Runtime encoding of different completion modes.
enum ForOfLoopBodyCompletion { BODY_COMPLETED, BODY_ABORTED, BODY_THREW };
//
// This function adds two statements to [target], corresponding to the
// following code:
//
// completion = kNormalCompletion;
// try {
// try {
// iterator_use
// } catch(e) {
// if (completion === kAbruptCompletion) completion = kThrowCompletion;
// throw e;
// }
// } finally {
// if (condition) {
// #BuildIteratorCloseForCompletion(iter, completion)
// }
// }
//
const int nopos = RelocInfo::kNoPosition;
auto factory = parser_->factory();
auto avfactory = parser_->ast_value_factory();
auto scope = parser_->scope_;
auto zone = parser_->zone();
// completion = kNormalCompletion;
Statement* initialize_completion;
{
Expression* proxy = factory->NewVariableProxy(completion);
Expression* assignment = factory->NewAssignment(
Token::ASSIGN, proxy,
factory->NewSmiLiteral(Parser::kNormalCompletion, nopos), nopos);
initialize_completion = factory->NewExpressionStatement(assignment, nopos);
}
// if (completion === kAbruptCompletion) completion = kThrowCompletion;
Statement* set_completion_throw;
{
Expression* condition = factory->NewCompareOperation(
Token::EQ_STRICT, factory->NewVariableProxy(completion),
factory->NewSmiLiteral(Parser::kAbruptCompletion, nopos), nopos);
Expression* proxy = factory->NewVariableProxy(completion);
Expression* assignment = factory->NewAssignment(
Token::ASSIGN, proxy,
factory->NewSmiLiteral(Parser::kThrowCompletion, nopos), nopos);
Statement* statement = factory->NewExpressionStatement(assignment, nopos);
set_completion_throw = factory->NewIfStatement(
condition, statement, factory->NewEmptyStatement(nopos), nopos);
}
// if (condition) {
// #BuildIteratorCloseForCompletion(iter, completion)
// }
Block* maybe_close;
{
Block* block = factory->NewBlock(nullptr, 2, true, nopos);
parser_->BuildIteratorCloseForCompletion(block->statements(), iter,
completion);
DCHECK(block->statements()->length() == 2);
maybe_close = factory->NewBlock(nullptr, 1, true, nopos);
maybe_close->statements()->Add(
factory->NewIfStatement(condition, block,
factory->NewEmptyStatement(nopos), nopos),
zone);
}
// try { #try_block }
// catch(e) {
// #set_completion_throw;
// throw e;
// }
Statement* try_catch;
{
Scope* catch_scope = parser_->NewScope(scope, CATCH_SCOPE);
Variable* catch_variable =
catch_scope->DeclareLocal(avfactory->dot_catch_string(), VAR,
kCreatedInitialized, Variable::NORMAL);
Statement* rethrow;
{
Expression* proxy = factory->NewVariableProxy(catch_variable);
rethrow = factory->NewExpressionStatement(factory->NewThrow(proxy, nopos),
nopos);
}
Block* catch_block = factory->NewBlock(nullptr, 2, false, nopos);
catch_block->statements()->Add(set_completion_throw, zone);
catch_block->statements()->Add(rethrow, zone);
try_catch = factory->NewTryCatchStatement(
iterator_use, catch_scope, catch_variable, catch_block, nopos);
}
// try { #try_catch } finally { #maybe_close }
Statement* try_finally;
{
Block* try_block = factory->NewBlock(nullptr, 1, false, nopos);
try_block->statements()->Add(try_catch, zone);
try_finally =
factory->NewTryFinallyStatement(try_block, maybe_close, nopos);
}
target->statements()->Add(initialize_completion, zone);
target->statements()->Add(try_finally, zone);
}
void ParserTraits::BuildIteratorCloseForCompletion(
ZoneList<Statement*>* statements, Variable* iterator,
......@@ -6541,7 +6651,7 @@ void ParserTraits::BuildIteratorCloseForCompletion(
//
// let iteratorReturn = iterator.return;
// if (!IS_NULL_OR_UNDEFINED(iteratorReturn)) {
// if (completion === BODY_THREW) {
// if (completion === kThrowCompletion) {
// if (!IS_CALLABLE(iteratorReturn)) {
// throw MakeTypeError(kReturnMethodNotCallable);
// }
......@@ -6659,7 +6769,7 @@ void ParserTraits::BuildIteratorCloseForCompletion(
validate_return->statements()->Add(check_return, zone);
}
// if (completion === BODY_THREW) {
// if (completion === kThrowCompletion) {
// #check_return_callable;
// #try_call_return;
// } else {
......@@ -6669,7 +6779,7 @@ void ParserTraits::BuildIteratorCloseForCompletion(
{
Expression* condition = factory->NewCompareOperation(
Token::EQ_STRICT, factory->NewVariableProxy(completion),
factory->NewSmiLiteral(BODY_THREW, nopos), nopos);
factory->NewSmiLiteral(Parser::kThrowCompletion, nopos), nopos);
Block* then_block = factory->NewBlock(nullptr, 2, false, nopos);
then_block->statements()->Add(check_return_callable, zone);
......@@ -6703,15 +6813,17 @@ Statement* ParserTraits::FinalizeForOfStatement(ForOfStatement* loop, int pos) {
//
// This function replaces the loop with the following wrapping:
//
// let completion = BODY_COMPLETED;
// let each;
// let completion = kNormalCompletion;
// try {
// #loop;
// } catch(e) {
// if (completion === BODY_ABORTED) completion = BODY_THREW;
// throw e;
// try {
// #loop;
// } catch(e) {
// if (completion === kAbruptCompletion) completion = kThrowCompletion;
// throw e;
// }
// } finally {
// if (!(completion === BODY_COMPLETED || IS_UNDEFINED(#iterator))) {
// if (!(completion === kNormalCompletion || IS_UNDEFINED(#iterator))) {
// #BuildIteratorCloseForCompletion(#iterator, completion)
// }
// }
......@@ -6720,15 +6832,16 @@ Statement* ParserTraits::FinalizeForOfStatement(ForOfStatement* loop, int pos) {
//
// {
// #loop-body
// {{completion = BODY_COMPLETED;}}
// {{completion = kNormalCompletion;}}
// }
//
// and assign_each is wrapped as follows
// and the loop's assign_each is wrapped as follows
//
// do {
// {{completion = BODY_ABORTED;}}
// {{completion = kAbruptCompletion;}}
// #assign-each
// } into each
// }
//
const int nopos = RelocInfo::kNoPosition;
auto factory = parser_->factory();
......@@ -6736,17 +6849,7 @@ Statement* ParserTraits::FinalizeForOfStatement(ForOfStatement* loop, int pos) {
auto scope = parser_->scope_;
auto zone = parser_->zone();
// let completion = BODY_COMPLETED;
Variable* var_completion = scope->NewTemporary(avfactory->empty_string());
Statement* initialize_completion;
{
Expression* proxy = factory->NewVariableProxy(var_completion);
Expression* assignment = factory->NewAssignment(
Token::ASSIGN, proxy,
factory->NewSmiLiteral(BODY_COMPLETED, nopos), nopos);
initialize_completion =
factory->NewExpressionStatement(assignment, nopos);
}
// let each;
Variable* var_each = scope->NewTemporary(avfactory->empty_string());
......@@ -6760,105 +6863,27 @@ Statement* ParserTraits::FinalizeForOfStatement(ForOfStatement* loop, int pos) {
factory->NewExpressionStatement(assignment, nopos);
}
// if (completion === BODY_ABORTED) completion = BODY_THREW;
Statement* set_completion_throw;
// !(completion === kNormalCompletion || IS_UNDEFINED(#iterator))
Expression* closing_condition;
{
Expression* condition = factory->NewCompareOperation(
Expression* lhs = factory->NewCompareOperation(
Token::EQ_STRICT, factory->NewVariableProxy(var_completion),
factory->NewSmiLiteral(BODY_ABORTED, nopos), nopos);
Expression* proxy = factory->NewVariableProxy(var_completion);
Expression* assignment = factory->NewAssignment(
Token::ASSIGN, proxy, factory->NewSmiLiteral(BODY_THREW, nopos),
nopos);
Statement* statement = factory->NewExpressionStatement(assignment, nopos);
set_completion_throw = factory->NewIfStatement(
condition, statement, factory->NewEmptyStatement(nopos), nopos);
}
// if (!(completion === BODY_COMPLETED || IS_UNDEFINED(#iterator))) {
// #BuildIteratorCloseForCompletion(#iterator, completion)
// }
Block* maybe_close;
{
Expression* condition1 = factory->NewCompareOperation(
Token::EQ_STRICT, factory->NewVariableProxy(var_completion),
factory->NewSmiLiteral(BODY_COMPLETED, nopos), nopos);
Expression* condition2 = factory->NewCompareOperation(
factory->NewSmiLiteral(Parser::kNormalCompletion, nopos), nopos);
Expression* rhs = factory->NewCompareOperation(
Token::EQ_STRICT, factory->NewVariableProxy(loop->iterator()),
factory->NewUndefinedLiteral(nopos), nopos);
Expression* condition = factory->NewBinaryOperation(
Token::OR, condition1, condition2, nopos);
Block* block = factory->NewBlock(nullptr, 2, false, nopos);
BuildIteratorCloseForCompletion(
block->statements(), loop->iterator(), var_completion);
DCHECK(block->statements()->length() == 2);
maybe_close = factory->NewBlock(nullptr, 1, false, nopos);
maybe_close->statements()->Add(factory->NewIfStatement(
condition, factory->NewEmptyStatement(nopos), block, nopos), zone);
}
// try { #try_block }
// catch(e) {
// #set_completion_throw;
// throw e;
// }
Statement* try_catch;
{
Scope* catch_scope = NewScope(scope, CATCH_SCOPE);
Variable* catch_variable = catch_scope->DeclareLocal(
avfactory->dot_catch_string(), VAR, kCreatedInitialized,
Variable::NORMAL);
Statement* rethrow;
{
Expression* proxy = factory->NewVariableProxy(catch_variable);
rethrow = factory->NewExpressionStatement(
factory->NewThrow(proxy, nopos), nopos);
}
Block* try_block = factory->NewBlock(nullptr, 1, false, nopos);
try_block->statements()->Add(loop, zone);
Block* catch_block = factory->NewBlock(nullptr, 2, false, nopos);
catch_block->statements()->Add(set_completion_throw, zone);
catch_block->statements()->Add(rethrow, zone);
try_catch = factory->NewTryCatchStatement(
try_block, catch_scope, catch_variable, catch_block, nopos);
}
// try { #try_catch } finally { #maybe_close }
Statement* try_finally;
{
Block* try_block = factory->NewBlock(nullptr, 1, false, nopos);
try_block->statements()->Add(try_catch, zone);
try_finally =
factory->NewTryFinallyStatement(try_block, maybe_close, nopos);
}
// #initialize_completion;
// #initialize_each;
// #try_finally;
Statement* final_loop;
{
Block* block = factory->NewBlock(nullptr, 2, false, nopos);
block->statements()->Add(initialize_completion, zone);
block->statements()->Add(initialize_each, zone);
block->statements()->Add(try_finally, zone);
final_loop = block;
closing_condition = factory->NewUnaryOperation(
Token::NOT, factory->NewBinaryOperation(Token::OR, lhs, rhs, nopos),
nopos);
}
// {{completion = BODY_COMPLETED;}}
// {{completion = kNormalCompletion;}}
Statement* set_completion_normal;
{
Expression* proxy = factory->NewVariableProxy(var_completion);
Expression* assignment = factory->NewAssignment(
Token::ASSIGN, proxy, factory->NewSmiLiteral(BODY_COMPLETED, nopos),
nopos);
Token::ASSIGN, proxy,
factory->NewSmiLiteral(Parser::kNormalCompletion, nopos), nopos);
Block* block = factory->NewBlock(nullptr, 1, true, nopos);
block->statements()->Add(
......@@ -6866,36 +6891,53 @@ Statement* ParserTraits::FinalizeForOfStatement(ForOfStatement* loop, int pos) {
set_completion_normal = block;
}
// { #loop-body; #set_completion_normal }
Block* new_body = factory->NewBlock(nullptr, 2, false, nopos);
new_body->statements()->Add(loop->body(), zone);
new_body->statements()->Add(set_completion_normal, zone);
loop->set_body(new_body);
// {{completion = BODY_ABORTED;}}
Statement* set_completion_break;
// {{completion = kAbruptCompletion;}}
Statement* set_completion_abrupt;
{
Expression* proxy = factory->NewVariableProxy(var_completion);
Expression* assignment = factory->NewAssignment(
Token::ASSIGN, proxy, factory->NewSmiLiteral(BODY_ABORTED, nopos),
nopos);
Token::ASSIGN, proxy,
factory->NewSmiLiteral(Parser::kAbruptCompletion, nopos), nopos);
Block* block = factory->NewBlock(nullptr, 1, true, nopos);
block->statements()->Add(factory->NewExpressionStatement(assignment, nopos),
zone);
set_completion_break = block;
set_completion_abrupt = block;
}
// { #set_completion_break; #assign-each }
// { #loop-body; #set_completion_normal }
Block* new_body = factory->NewBlock(nullptr, 2, false, nopos);
{
new_body->statements()->Add(loop->body(), zone);
new_body->statements()->Add(set_completion_normal, zone);
}
// { #set_completion_abrupt; #assign-each }
Block* new_assign_each = factory->NewBlock(nullptr, 2, false, nopos);
new_assign_each->statements()->Add(set_completion_break, zone);
new_assign_each->statements()->Add(
factory->NewExpressionStatement(loop->assign_each(), nopos), zone);
{
new_assign_each->statements()->Add(set_completion_abrupt, zone);
new_assign_each->statements()->Add(
factory->NewExpressionStatement(loop->assign_each(), nopos), zone);
}
Expression* do_each =
factory->NewDoExpression(new_assign_each, var_each, nopos);
loop->set_assign_each(do_each);
// Now put things together.
loop->set_body(new_body);
loop->set_assign_each(
factory->NewDoExpression(new_assign_each, var_each, nopos));
Statement* final_loop;
{
Block* target = factory->NewBlock(nullptr, 3, false, nopos);
target->statements()->Add(initialize_each, zone);
Block* try_block = factory->NewBlock(nullptr, 1, false, nopos);
try_block->statements()->Add(loop, zone);
FinalizeIteratorUse(var_completion, closing_condition, loop->iterator(),
try_block, target);
final_loop = target;
}
return final_loop;
}
......
......@@ -456,6 +456,9 @@ class ParserTraits {
MessageTemplate::Template message,
const AstRawString* arg, int pos);
void FinalizeIteratorUse(Variable* completion, Expression* condition,
Variable* iter, Block* iterator_use, Block* result);
Statement* FinalizeForOfStatement(ForOfStatement* loop, int pos);
// Reporting errors.
......@@ -696,6 +699,13 @@ class Parser : public ParserBase<ParserTraits> {
private:
friend class ParserTraits;
// Runtime encoding of different completion modes.
enum CompletionKind {
kNormalCompletion,
kThrowCompletion,
kAbruptCompletion
};
// Limit the allowed number of local variables in a function. The hard limit
// is that offsets computed by FullCodeGenerator::StackOperand and similar
// functions are ints, and they should not overflow. In addition, accessing
......
......@@ -409,16 +409,27 @@ void Parser::PatternRewriter::VisitObjectLiteral(ObjectLiteral* node) {
void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node,
Variable** temp_var) {
auto temp = *temp_var = CreateTempVar(current_value_);
block_->statements()->Add(parser_->BuildAssertIsCoercible(temp), zone());
DCHECK(block_->ignore_completion_value());
auto temp = *temp_var = CreateTempVar(current_value_);
auto iterator = CreateTempVar(parser_->GetIterator(
factory()->NewVariableProxy(temp), factory(), RelocInfo::kNoPosition));
auto done = CreateTempVar(
factory()->NewBooleanLiteral(false, RelocInfo::kNoPosition));
auto result = CreateTempVar();
auto v = CreateTempVar();
auto completion = CreateTempVar();
auto nopos = RelocInfo::kNoPosition;
// For the purpose of iterator finalization, we temporarily set block_ to a
// new block. In the main body of this function, we write to block_ (both
// explicitly and implicitly via recursion). At the end of the function, we
// wrap this new block in a try-finally statement, restore block_ to its
// original value, and add the try-finally statement to block_.
auto target = block_;
if (FLAG_harmony_iterator_close) {
block_ = factory()->NewBlock(nullptr, 8, true, nopos);
}
Spread* spread = nullptr;
for (Expression* value : *node->values()) {
......@@ -428,60 +439,101 @@ void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node,
}
PatternContext context = SetInitializerContextIfNeeded(value);
// if (!done) {
// done = true; // If .next, .done or .value throws, don't close.
// result = IteratorNext(iterator);
// v = (done = result.done) ? undefined : result.value;
// }
auto next_block =
factory()->NewBlock(nullptr, 2, true, RelocInfo::kNoPosition);
next_block->statements()->Add(factory()->NewExpressionStatement(
parser_->BuildIteratorNextResult(
factory()->NewVariableProxy(iterator),
result, RelocInfo::kNoPosition),
RelocInfo::kNoPosition),
zone());
auto assign_to_done = factory()->NewAssignment(
Token::ASSIGN, factory()->NewVariableProxy(done),
factory()->NewProperty(
factory()->NewVariableProxy(result),
factory()->NewStringLiteral(ast_value_factory()->done_string(),
RelocInfo::kNoPosition),
RelocInfo::kNoPosition),
RelocInfo::kNoPosition);
auto next_value = factory()->NewConditional(
assign_to_done, factory()->NewUndefinedLiteral(RelocInfo::kNoPosition),
factory()->NewProperty(
factory()->NewVariableProxy(result),
factory()->NewStringLiteral(ast_value_factory()->value_string(),
RelocInfo::kNoPosition),
RelocInfo::kNoPosition),
RelocInfo::kNoPosition);
next_block->statements()->Add(
factory()->NewExpressionStatement(
factory()->NewAssignment(Token::ASSIGN,
factory()->NewVariableProxy(v), next_value,
RelocInfo::kNoPosition),
RelocInfo::kNoPosition),
zone());
Statement* if_statement;
{
auto next_block =
factory()->NewBlock(nullptr, 3, true, RelocInfo::kNoPosition);
next_block->statements()->Add(
factory()->NewExpressionStatement(
factory()->NewAssignment(
Token::ASSIGN, factory()->NewVariableProxy(done),
factory()->NewBooleanLiteral(true, nopos), nopos),
nopos),
zone());
auto if_statement = factory()->NewIfStatement(
factory()->NewUnaryOperation(Token::NOT,
factory()->NewVariableProxy(done),
RelocInfo::kNoPosition),
next_block, factory()->NewEmptyStatement(RelocInfo::kNoPosition),
RelocInfo::kNoPosition);
next_block->statements()->Add(
factory()->NewExpressionStatement(
parser_->BuildIteratorNextResult(
factory()->NewVariableProxy(iterator), result,
RelocInfo::kNoPosition),
RelocInfo::kNoPosition),
zone());
auto assign_to_done = factory()->NewAssignment(
Token::ASSIGN, factory()->NewVariableProxy(done),
factory()->NewProperty(
factory()->NewVariableProxy(result),
factory()->NewStringLiteral(ast_value_factory()->done_string(),
RelocInfo::kNoPosition),
RelocInfo::kNoPosition),
RelocInfo::kNoPosition);
auto next_value = factory()->NewConditional(
assign_to_done,
factory()->NewUndefinedLiteral(RelocInfo::kNoPosition),
factory()->NewProperty(
factory()->NewVariableProxy(result),
factory()->NewStringLiteral(ast_value_factory()->value_string(),
RelocInfo::kNoPosition),
RelocInfo::kNoPosition),
RelocInfo::kNoPosition);
next_block->statements()->Add(
factory()->NewExpressionStatement(
factory()->NewAssignment(Token::ASSIGN,
factory()->NewVariableProxy(v),
next_value, RelocInfo::kNoPosition),
RelocInfo::kNoPosition),
zone());
if_statement = factory()->NewIfStatement(
factory()->NewUnaryOperation(Token::NOT,
factory()->NewVariableProxy(done),
RelocInfo::kNoPosition),
next_block, factory()->NewEmptyStatement(RelocInfo::kNoPosition),
RelocInfo::kNoPosition);
}
block_->statements()->Add(if_statement, zone());
if (!(value->IsLiteral() && value->AsLiteral()->raw_value()->IsTheHole())) {
if (FLAG_harmony_iterator_close) {
// completion = kAbruptCompletion;
Expression* proxy = factory()->NewVariableProxy(completion);
Expression* assignment = factory()->NewAssignment(
Token::ASSIGN, proxy,
factory()->NewSmiLiteral(kAbruptCompletion, nopos), nopos);
block_->statements()->Add(
factory()->NewExpressionStatement(assignment, nopos), zone());
}
RecurseIntoSubpattern(value, factory()->NewVariableProxy(v));
if (FLAG_harmony_iterator_close) {
// completion = kNormalCompletion;
Expression* proxy = factory()->NewVariableProxy(completion);
Expression* assignment = factory()->NewAssignment(
Token::ASSIGN, proxy,
factory()->NewSmiLiteral(kNormalCompletion, nopos), nopos);
block_->statements()->Add(
factory()->NewExpressionStatement(assignment, nopos), zone());
}
}
set_context(context);
}
if (spread != nullptr) {
// array = [];
// A spread can only occur as the last component. It is not handled by
// RecurseIntoSubpattern above.
// let array = [];
// if (!done) %concat_iterable_to_array(array, iterator);
// done = true;
auto empty_exprs = new (zone()) ZoneList<Expression*>(0, zone());
auto array = CreateTempVar(factory()->NewArrayLiteral(
empty_exprs,
......@@ -506,9 +558,23 @@ void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node,
RelocInfo::kNoPosition);
block_->statements()->Add(if_statement, zone());
auto set_done = factory()->NewAssignment(
Token::ASSIGN, factory()->NewVariableProxy(done),
factory()->NewBooleanLiteral(true, nopos), nopos);
block_->statements()->Add(
factory()->NewExpressionStatement(set_done, nopos), zone());
RecurseIntoSubpattern(spread->expression(),
factory()->NewVariableProxy(array));
}
if (FLAG_harmony_iterator_close) {
Expression* closing_condition = factory()->NewUnaryOperation(
Token::NOT, factory()->NewVariableProxy(done), nopos);
parser_->FinalizeIteratorUse(completion, closing_condition, iterator,
block_, target);
block_ = target;
}
}
......
......@@ -13,13 +13,13 @@ snippet: "
"
frame size: 16
parameter count: 1
bytecode array length: 345
bytecode array length: 344
bytecodes: [
B(StackCheck),
B(LdaZero),
B(Star), R(3),
B(LdaUndefined),
B(Star), R(4),
B(LdaZero),
B(Star), R(3),
B(Mov), R(context), R(11),
B(Mov), R(context), R(12),
B(CreateArrayLiteral), U8(0), U8(0), U8(3),
......@@ -46,7 +46,7 @@ bytecodes: [
B(Star), R(13),
B(LoadIC), R(13), U8(3), U8(9),
B(JumpIfToBooleanTrue), U8(28),
B(LdaSmi8), U8(1),
B(LdaSmi8), U8(2),
B(Star), R(3),
B(Ldar), R(2),
B(Star), R(13),
......@@ -72,10 +72,10 @@ bytecodes: [
B(PushContext), R(8),
B(Ldar), R(3),
B(Star), R(13),
B(LdaSmi8), U8(1),
B(LdaSmi8), U8(2),
B(TestEqualStrict), R(13),
B(JumpIfFalse), U8(6),
B(LdaSmi8), U8(2),
B(LdaSmi8), U8(1),
B(Star), R(3),
B(LdaContextSlot), R(context), U8(4),
B(Throw),
......@@ -96,8 +96,8 @@ bytecodes: [
B(Star), R(12),
B(LdaUndefined),
B(TestEqualStrict), R(12),
B(JumpIfToBooleanFalse), U8(4),
B(JumpConstant), U8(10),
B(LogicalNot),
B(JumpIfFalseConstant), U8(10),
B(Ldar), R(1),
B(Star), R(12),
B(LoadIC), R(12), U8(6), U8(13),
......@@ -109,7 +109,7 @@ bytecodes: [
B(Jump), U8(122),
B(Ldar), R(3),
B(Star), R(12),
B(LdaSmi8), U8(2),
B(LdaSmi8), U8(1),
B(TestEqualStrict), R(12),
B(JumpIfFalse), U8(77),
B(Ldar), R(5),
......@@ -183,7 +183,7 @@ constant pool: [
handlers: [
[10, 151, 157],
[13, 105, 107],
[248, 261, 263],
[247, 260, 262],
]
---
......@@ -193,15 +193,15 @@ snippet: "
"
frame size: 17
parameter count: 1
bytecode array length: 361
bytecode array length: 360
bytecodes: [
B(StackCheck),
B(LdaConstant), U8(0),
B(Star), R(7),
B(LdaZero),
B(Star), R(3),
B(LdaUndefined),
B(Star), R(4),
B(LdaZero),
B(Star), R(3),
B(Mov), R(context), R(12),
B(Mov), R(context), R(13),
B(Ldar), R(7),
......@@ -228,7 +228,7 @@ bytecodes: [
B(Star), R(14),
B(LoadIC), R(14), U8(3), U8(9),
B(JumpIfToBooleanTrue), U8(32),
B(LdaSmi8), U8(1),
B(LdaSmi8), U8(2),
B(Star), R(3),
B(Ldar), R(2),
B(Star), R(14),
......@@ -256,10 +256,10 @@ bytecodes: [
B(PushContext), R(9),
B(Ldar), R(3),
B(Star), R(14),
B(LdaSmi8), U8(1),
B(LdaSmi8), U8(2),
B(TestEqualStrict), R(14),
B(JumpIfFalse), U8(6),
B(LdaSmi8), U8(2),
B(LdaSmi8), U8(1),
B(Star), R(3),
B(LdaContextSlot), R(context), U8(4),
B(Throw),
......@@ -280,8 +280,8 @@ bytecodes: [
B(Star), R(13),
B(LdaUndefined),
B(TestEqualStrict), R(13),
B(JumpIfToBooleanFalse), U8(4),
B(JumpConstant), U8(10),
B(LogicalNot),
B(JumpIfFalseConstant), U8(10),
B(Ldar), R(1),
B(Star), R(13),
B(LoadIC), R(13), U8(6), U8(13),
......@@ -293,7 +293,7 @@ bytecodes: [
B(Jump), U8(122),
B(Ldar), R(3),
B(Star), R(13),
B(LdaSmi8), U8(2),
B(LdaSmi8), U8(1),
B(TestEqualStrict), R(13),
B(JumpIfFalse), U8(77),
B(Ldar), R(5),
......@@ -372,7 +372,7 @@ constant pool: [
handlers: [
[14, 157, 163],
[17, 111, 113],
[255, 268, 270],
[254, 267, 269],
]
---
......@@ -384,13 +384,13 @@ snippet: "
"
frame size: 16
parameter count: 1
bytecode array length: 367
bytecode array length: 366
bytecodes: [
B(StackCheck),
B(LdaZero),
B(Star), R(3),
B(LdaUndefined),
B(Star), R(4),
B(LdaZero),
B(Star), R(3),
B(Mov), R(context), R(11),
B(Mov), R(context), R(12),
B(CreateArrayLiteral), U8(0), U8(0), U8(3),
......@@ -417,7 +417,7 @@ bytecodes: [
B(Star), R(13),
B(LoadIC), R(13), U8(3), U8(9),
B(JumpIfToBooleanTrue), U8(50),
B(LdaSmi8), U8(1),
B(LdaSmi8), U8(2),
B(Star), R(3),
B(Ldar), R(2),
B(Star), R(13),
......@@ -454,10 +454,10 @@ bytecodes: [
B(PushContext), R(8),
B(Ldar), R(3),
B(Star), R(13),
B(LdaSmi8), U8(1),
B(LdaSmi8), U8(2),
B(TestEqualStrict), R(13),
B(JumpIfFalse), U8(6),
B(LdaSmi8), U8(2),
B(LdaSmi8), U8(1),
B(Star), R(3),
B(LdaContextSlot), R(context), U8(4),
B(Throw),
......@@ -478,8 +478,8 @@ bytecodes: [
B(Star), R(12),
B(LdaUndefined),
B(TestEqualStrict), R(12),
B(JumpIfToBooleanFalse), U8(4),
B(JumpConstant), U8(10),
B(LogicalNot),
B(JumpIfFalseConstant), U8(10),
B(Ldar), R(1),
B(Star), R(12),
B(LoadIC), R(12), U8(6), U8(13),
......@@ -491,7 +491,7 @@ bytecodes: [
B(Jump), U8(122),
B(Ldar), R(3),
B(Star), R(12),
B(LdaSmi8), U8(2),
B(LdaSmi8), U8(1),
B(TestEqualStrict), R(12),
B(JumpIfFalse), U8(77),
B(Ldar), R(5),
......@@ -565,7 +565,7 @@ constant pool: [
handlers: [
[10, 173, 179],
[13, 127, 129],
[270, 283, 285],
[269, 282, 284],
]
---
......@@ -575,16 +575,16 @@ snippet: "
"
frame size: 15
parameter count: 1
bytecode array length: 377
bytecode array length: 376
bytecodes: [
B(StackCheck),
B(CreateObjectLiteral), U8(0), U8(0), U8(5),
B(Star), R(8),
B(Star), R(6),
B(LdaZero),
B(Star), R(2),
B(LdaUndefined),
B(Star), R(3),
B(LdaZero),
B(Star), R(2),
B(Mov), R(context), R(10),
B(Mov), R(context), R(11),
B(CreateArrayLiteral), U8(1), U8(1), U8(3),
......@@ -611,7 +611,7 @@ bytecodes: [
B(Star), R(12),
B(LoadIC), R(12), U8(4), U8(9),
B(JumpIfToBooleanTrue), U8(42),
B(LdaSmi8), U8(1),
B(LdaSmi8), U8(2),
B(Star), R(2),
B(Ldar), R(6),
B(Star), R(12),
......@@ -642,10 +642,10 @@ bytecodes: [
B(PushContext), R(7),
B(Ldar), R(2),
B(Star), R(12),
B(LdaSmi8), U8(1),
B(LdaSmi8), U8(2),
B(TestEqualStrict), R(12),
B(JumpIfFalse), U8(6),
B(LdaSmi8), U8(2),
B(LdaSmi8), U8(1),
B(Star), R(2),
B(LdaContextSlot), R(context), U8(4),
B(Throw),
......@@ -666,8 +666,8 @@ bytecodes: [
B(Star), R(11),
B(LdaUndefined),
B(TestEqualStrict), R(11),
B(JumpIfToBooleanFalse), U8(4),
B(JumpConstant), U8(12),
B(LogicalNot),
B(JumpIfFalseConstant), U8(12),
B(Ldar), R(0),
B(Star), R(11),
B(LoadIC), R(11), U8(8), U8(17),
......@@ -679,7 +679,7 @@ bytecodes: [
B(Jump), U8(122),
B(Ldar), R(2),
B(Star), R(11),
B(LdaSmi8), U8(2),
B(LdaSmi8), U8(1),
B(TestEqualStrict), R(11),
B(JumpIfFalse), U8(77),
B(Ldar), R(4),
......@@ -760,6 +760,6 @@ constant pool: [
handlers: [
[18, 173, 179],
[21, 127, 129],
[271, 284, 286],
[270, 283, 285],
]
......@@ -4,6 +4,7 @@
// Flags: --harmony-iterator-close
function* g() { yield 42; return 88 };
......@@ -11,33 +12,86 @@ function* g() { yield 42; return 88 };
{
g.prototype.return = null;
assertEquals(undefined, (() => {
for (var x of g()) { break; }
})());
assertEquals(undefined, (() => {
for (let x of g()) { break; }
})());
assertEquals(undefined, (() => {
for (const x of g()) { break; }
})());
assertEquals(undefined, (() => {
for (x of g()) { break; }
})());
assertThrowsEquals(() => {
for (var x of g()) { throw 42; }
}, 42);
assertThrowsEquals(() => {
for (let x of g()) { throw 42; }
}, 42);
assertThrowsEquals(() => {
for (const x of g()) { throw 42; }
}, 42);
assertThrowsEquals(() => {
for (x of g()) { throw 42; }
}, 42);
assertEquals(42, (() => {
for (var x of g()) { return 42; }
})());
assertEquals(42, (() => {
for (let x of g()) { return 42; }
})());
assertEquals(42, (() => {
for (const 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 (var x of g()) { x; }'));
assertEquals(42, eval('for (let x of g()) { x; }'));
assertEquals(42, eval('for (const x of g()) { x; }'));
assertEquals(42, eval('for (x of g()) { x; }'));
assertEquals(42, (() => {
var [x] = g(); return x;
})());
assertEquals(42, (() => {
let [x] = g(); return x;
})());
assertEquals(42, (() => {
const [x] = g(); return x;
})());
assertEquals(42, (() => {
[x] = g(); return x;
})());
assertEquals(42,
(([x]) => x)(g())
);
}
......@@ -45,33 +99,86 @@ function* g() { yield 42; return 88 };
{
g.prototype.return = 666;
assertThrows(() => {
for (var x of g()) { break; }
}, TypeError);
assertThrows(() => {
for (let x of g()) { break; }
}, TypeError);
assertThrows(() => {
for (const x of g()) { break; }
}, TypeError);
assertThrows(() => {
for (x of g()) { break; }
}, TypeError);
assertThrows(() => {
for (var x of g()) { throw 666; }
}, TypeError);
assertThrows(() => {
for (let x of g()) { throw 666; }
}, TypeError);
assertThrows(() => {
for (const x of g()) { throw 666; }
}, TypeError);
assertThrows(() => {
for (x of g()) { throw 666; }
}, TypeError);
assertThrows(() => {
for (var x of g()) { return 666; }
}, TypeError);
assertThrows(() => {
for (let x of g()) { return 666; }
}, TypeError);
assertThrows(() => {
for (const 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 (var x of g()) { x; }'));
assertEquals(42, eval('for (let x of g()) { x; }'));
assertEquals(42, eval('for (const x of g()) { x; }'));
assertEquals(42, eval('for (x of g()) { x; }'));
assertThrows(() => {
var [x] = g(); return x;
}, TypeError);
assertThrows(() => {
let [x] = g(); return x;
}, TypeError);
assertThrows(() => {
const [x] = g(); return x;
}, TypeError);
assertThrows(() => {
[x] = g(); return x;
}, TypeError);
assertThrows(() => {
(([x]) => x)(g());
}, TypeError);
}
......@@ -79,35 +186,89 @@ function* g() { yield 42; return 88 };
{
g.prototype.return = () => 666;
assertThrows(() => {
for (var x of g()) { break; }
}, TypeError);
assertThrows(() => {
for (let x of g()) { break; }
}, TypeError);
assertThrows(() => {
for (const x of g()) { break; }
}, TypeError);
assertThrows(() => {
for (x of g()) { break; }
}, TypeError);
// Throw from the body of a for loop 'wins' vs throw
// originating from a bad 'return' value.
assertThrowsEquals(() => {
for (var x of g()) { throw 666; }
}, 666);
assertThrowsEquals(() => {
for (let x of g()) { throw 666; }
}, 666);
assertThrowsEquals(() => {
for (const x of g()) { throw 666; }
}, 666);
assertThrowsEquals(() => {
for (x of g()) { throw 666; }
}, 666);
assertThrows(() => {
for (var x of g()) { return 666; }
}, TypeError);
assertThrows(() => {
for (let x of g()) { return 666; }
}, TypeError);
assertThrows(() => {
for (const x of g()) { return 666; }
}, TypeError);
assertThrows(() => {
for (x of g()) { return 666; }
}, TypeError);
assertEquals(42, eval('for (var x of g()) { x; }'));
assertEquals(42, eval('for (let x of g()) { x; }'));
assertEquals(42, eval('for (const x of g()) { x; }'));
assertEquals(42, eval('for (x of g()) { x; }'));
assertThrows(() => {
var [x] = g(); return x;
}, TypeError);
assertThrows(() => {
let [x] = g(); return x;
}, TypeError);
assertThrows(() => {
const [x] = g(); return x;
}, TypeError);
assertThrows(() => {
[x] = g(); return x;
}, TypeError);
assertThrows(() => {
(([x]) => x)(g());
}, TypeError);
}
......@@ -116,52 +277,562 @@ function* g() { yield 42; return 88 };
let log = [];
g.prototype.return = (...args) => { log.push(args); return {} };
log = [];
for (var x of g()) { break; }
assertEquals([[]], log);
log = [];
for (let x of g()) { break; }
assertEquals([[]], log);
log = [];
for (const x of g()) { break; }
assertEquals([[]], log);
log = [];
for (x of g()) { break; }
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (var x of g()) { throw 42; }
}, 42);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (let x of g()) { throw 42; }
}, 42);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (const x of g()) { throw 42; }
}, 42);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (x of g()) { throw 42; }
}, 42);
assertEquals([[]], log);
log = [];
assertEquals(42, (() => {
for (var x of g()) { return 42; }
})());
assertEquals([[]], log);
log = [];
assertEquals(42, (() => {
for (let x of g()) { return 42; }
})());
assertEquals([[]], log);
log = [];
assertEquals(42, (() => {
for (const x of g()) { return 42; }
})());
assertEquals([[]], log);
log = [];
assertEquals(42, (() => {
for (x of g()) { return 42; }
})());
assertEquals([[]], log);
log = [];
assertEquals(42, eval('for (var x of g()) { x; }'));
assertEquals([], log);
log = [];
assertEquals(42, eval('for (let x of g()) { x; }'));
assertEquals([], log);
log = [];
assertEquals(42, eval('for (const x of g()) { x; }'));
assertEquals([], log);
log = [];
assertEquals(42, eval('for (x of g()) { x; }'));
assertEquals([], log);
// Even if doing the assignment throws, still call return
log = [];
x = { set attr(_) { throw 1234; } };
assertThrowsEquals(() => {
for (x.attr of g()) { throw 456; }
}, 1234);
assertEquals([[]], log);
log = [];
assertEquals(42, (() => {
var [x] = g(); return x;
})());
assertEquals([[]], log);
log = [];
assertEquals(42, (() => {
let [x] = g(); return x;
})());
assertEquals([[]], log);
log = [];
assertEquals(42, (() => {
const [x] = g(); return x;
})());
assertEquals([[]], log);
log = [];
assertEquals(42, (() => {
[x] = g(); return x;
})());
assertEquals([[]], log);
log = []
assertEquals(42,
(([x]) => x)(g())
);
assertEquals([[]], log);
log = [];
assertEquals(42, (() => {
var [x,] = g(); return x;
})());
assertEquals([[]], log);
log = [];
assertEquals(42, (() => {
let [x,] = g(); return x;
})());
assertEquals([[]], log);
log = [];
assertEquals(42, (() => {
const [x,] = g(); return x;
})());
assertEquals([[]], log);
log = [];
assertEquals(42, (() => {
[x,] = g(); return x;
})());
assertEquals([[]], log);
log = []
assertEquals(42,
(([x,]) => x)(g())
);
assertEquals([[]], log);
log = [];
assertEquals(42, (() => {
var [x,,] = g(); return x;
})());
assertEquals([], log);
log = [];
assertEquals(42, (() => {
let [x,,] = g(); return x;
})());
assertEquals([], log);
log = [];
assertEquals(42, (() => {
const [x,,] = g(); return x;
})());
assertEquals([], log);
log = [];
assertEquals(42, (() => {
[x,,] = g(); return x;
})());
assertEquals([], log);
log = []
assertEquals(42,
(([x,,]) => x)(g())
);
assertEquals([], log);
log = [];
assertEquals([42, undefined], (() => {
var [x, y] = g(); return [x, y];
})());
assertEquals([], log);
log = [];
assertEquals([42, undefined], (() => {
let [x, y] = g(); return [x, y];
})());
assertEquals([], log);
log = [];
assertEquals([42, undefined], (() => {
const [x, y] = g(); return [x, y];
})());
assertEquals([], log);
log = [];
assertEquals([42, undefined], (() => {
[x, y] = g(); return [x, y];
})());
assertEquals([], log);
log = []
assertEquals([42, undefined],
(([x, y]) => [x, y])(g())
);
assertEquals([], log);
log = [];
assertEquals([42], (() => {
var [...x] = g(); return x;
})());
assertEquals([], log);
log = [];
assertEquals([42], (() => {
let [...x] = g(); return x;
})());
assertEquals([], log);
log = [];
assertEquals([42], (() => {
const [...x] = g(); return x;
})());
assertEquals([], log);
log = [];
assertEquals([42], (() => {
[...x] = g(); return x;
})());
assertEquals([], log);
log = []
assertEquals([42],
(([...x]) => x)(g())
);
assertEquals([], log);
log = [];
assertEquals([42, []], (() => {
var [x, ...y] = g(); return [x, y];
})());
assertEquals([], log);
log = [];
assertEquals([42, []], (() => {
let [x, ...y] = g(); return [x, y];
})());
assertEquals([], log);
log = [];
assertEquals([42, []], (() => {
const [x, ...y] = g(); return [x, y];
})());
assertEquals([], log);
log = [];
assertEquals([42, []], (() => {
[x, ...y] = g(); return [x, y];
})());
assertEquals([], log);
log = []
assertEquals([42, []],
(([x, ...y]) => [x, y])(g())
);
assertEquals([], log);
log = [];
assertEquals([], (() => {
var [] = g(); return [];
})());
assertEquals([[]], log);
log = [];
assertEquals([], (() => {
let [] = g(); return [];
})());
assertEquals([[]], log);
log = [];
assertEquals([], (() => {
const [] = g(); return [];
})());
assertEquals([[]], log);
log = [];
assertEquals([], (() => {
[] = g(); return [];
})());
assertEquals([[]], log);
log = []
assertEquals([],
(([]) => [])(g())
);
assertEquals([[]], log);
log = [];
assertEquals([], (() => {
var [...[]] = g(); return [];
})());
assertEquals([], log);
log = [];
assertEquals([], (() => {
let [...[]] = g(); return [];
})());
assertEquals([], log);
log = [];
assertEquals([], (() => {
const [...[]] = g(); return [];
})());
assertEquals([], log);
log = [];
assertEquals([], (() => {
[...[]] = g(); return [];
})());
assertEquals([], log);
log = []
assertEquals([],
(([...[]]) => [])(g())
);
assertEquals([], log);
log = [];
assertEquals([42], (() => {
var [...[x]] = g(); return [x];
})());
assertEquals([], log);
log = [];
assertEquals([42], (() => {
let [...[x]] = g(); return [x];
})());
assertEquals([], log);
log = [];
assertEquals([42], (() => {
const [...[x]] = g(); return [x];
})());
assertEquals([], log);
log = [];
assertEquals([42], (() => {
[...[x]] = g(); return [x];
})());
assertEquals([], log);
log = []
assertEquals([42],
(([...[x]]) => [x])(g())
);
assertEquals([], log);
log = [];
assertEquals([42, undefined], (() => {
var [...[x, y]] = g(); return [x, y];
})());
assertEquals([], log);
log = [];
assertEquals([42, undefined], (() => {
let [...[x, y]] = g(); return [x, y];
})());
assertEquals([], log);
log = [];
assertEquals([42, undefined], (() => {
const [...[x, y]] = g(); return [x, y];
})());
assertEquals([], log);
log = [];
assertEquals([42, undefined], (() => {
[...[x, y]] = g(); return [x, y];
})());
assertEquals([], log);
log = []
assertEquals([42, undefined],
(([...[x, y]]) => [x, y])(g())
);
assertEquals([], log);
log = []
assertThrowsEquals(() => {
let x = { set foo(_) { throw 666; } };
[x.foo] = g();
}, 666);
assertEquals([[]], log);
log = []
assertThrows(() => {
var [[]] = g();
}, TypeError);
assertEquals([[]], log);
log = []
assertThrows(() => {
let [[]] = g();
}, TypeError);
assertEquals([[]], log);
log = []
assertThrows(() => {
const [[]] = g();
}, TypeError);
assertEquals([[]], log);
log = []
assertThrows(() => {
[[]] = g();
}, TypeError);
assertEquals([[]], log);
log = []
assertThrows(() => {
(([[]]) => 0)(g());
}, TypeError);
assertEquals([[]], log);
log = []
assertThrows(() => {
var [...[[]]] = g();
}, TypeError);
assertEquals([], log);
log = []
assertThrows(() => {
let [...[[]]] = g();
}, TypeError);
assertEquals([], log);
log = []
assertThrows(() => {
const [...[[]]] = g();
}, TypeError);
assertEquals([], log);
log = []
assertThrows(() => {
[...[[]]] = g();
}, TypeError);
assertEquals([], log);
log = []
assertThrows(() => {
(([...[[]]]) => 0)(g());
}, TypeError);
assertEquals([], log);
{
let backup = Array.prototype[Symbol.iterator];
Array.prototype[Symbol.iterator] = () => g();
log = [];
assertDoesNotThrow(() => {
var [x, ...[y]] = [1, 2, 3]
});
assertEquals(log, [[]]);
log = [];
assertDoesNotThrow(() => {
let [x, ...[y]] = [1, 2, 3];
});
assertEquals(log, [[]]);
log = [];
assertDoesNotThrow(() => {
const [x, ...[y]] = [1, 2, 3];
});
assertEquals(log, [[]]);
log = [];
assertDoesNotThrow(() => {
(([x, ...[y]]) => {})([1, 2, 3]);
});
assertEquals(log, [[]]);
log = [];
assertThrows(() => {
var [x, ...[[]]] = [1, 2, 3];
}, TypeError);
assertEquals(log, [[]]);
log = [];
assertThrows(() => {
let [x, ...[[]]] = [1, 2, 3];
}, TypeError);
assertEquals(log, [[]]);
log = [];
assertThrows(() => {
const [x, ...[[]]] = [1, 2, 3];
}, TypeError);
assertEquals(log, [[]]);
log = [];
assertThrows(() => {
(([x, ...[[]]]) => {})([1, 2, 3]);
}, TypeError);
assertEquals(log, [[]]);
log = [];
assertDoesNotThrow(() => {
var [x, ...[...y]] = [1, 2, 3];
});
assertEquals(log, []);
log = [];
assertDoesNotThrow(() => {
let [x, ...[...y]] = [1, 2, 3];
});
assertEquals(log, []);
log = [];
assertDoesNotThrow(() => {
const [x, ...[...y]] = [1, 2, 3];
});
assertEquals(log, []);
log = [];
assertDoesNotThrow(() => {
(([x, ...[...y]]) => {})([1, 2, 3]);
});
assertEquals(log, []);
Array.prototype[Symbol.iterator] = backup;
}
}
......@@ -170,49 +841,128 @@ function* g() { yield 42; return 88 };
let log = [];
g.prototype.return = (...args) => { log.push(args); throw 23 };
log = [];
assertThrowsEquals(() => {
for (var x of g()) { break; }
}, 23);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (let x of g()) { break; }
}, 23);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (const x of g()) { break; }
}, 23);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (x of g()) { break; }
}, 23);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (var x of g()) { throw 42; }
}, 42);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (let x of g()) { throw 42; }
}, 42);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (const x of g()) { throw 42; }
}, 42);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (x of g()) { throw 42; }
}, 42);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (var x of g()) { return 42; }
}, 23);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (let x of g()) { return 42; }
}, 23);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (const x of g()) { return 42; }
}, 23);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
for (x of g()) { return 42; }
}, 23);
assertEquals([[]], log);
log = [];
assertEquals(42, eval('for (var x of g()) { x; }'));
assertEquals([], log);
log = [];
assertEquals(42, eval('for (let x of g()) { x; }'));
assertEquals([], log);
log = [];
assertEquals(42, eval('for (const x of g()) { x; }'));
assertEquals([], log);
log = [];
assertEquals(42, eval('for (x of g()) { x; }'));
assertEquals([], log);
log = [];
assertThrowsEquals(() => {
var [x] = g(); return x;
}, 23);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
let [x] = g(); return x;
}, 23);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
const [x] = g(); return x;
}, 23);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
[x] = g(); return x;
}, 23);
assertEquals([[]], log);
log = [];
assertThrowsEquals(() => {
(([x]) => x)(g())
}, 23);
assertEquals([[]], log);
}
......@@ -221,13 +971,42 @@ function* g() { yield 42; return 88 };
g.prototype.next = () => { throw 666; };
g.prototype.return = () => { assertUnreachable() };
assertThrowsEquals(() => {
for (var x of g()) {}
}, 666);
assertThrowsEquals(() => {
for (let x of g()) {}
}, 666);
assertThrowsEquals(() => {
for (const x of g()) {}
}, 666);
assertThrowsEquals(() => {
for (x of g()) {}
}, 666);
assertThrowsEquals(() => {
var [x] = g();
}, 666);
assertThrowsEquals(() => {
let [x] = g();
}, 666);
assertThrowsEquals(() => {
const [x] = g();
}, 666);
assertThrowsEquals(() => {
[x] = g();
}, 666);
assertThrowsEquals(() => {
(([x]) => x)(g());
}, 666);
}
......
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