Commit 260af115 authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

[parsing] Fix detection of invalid continue targets.

In order to know which labels are valid continue targets, we must
track the labels that immediately prefix an iteration statement.

Also document some things that I had to figure out.

Bug: v8:8033
Change-Id: Ia8288fd0e553a547aa0f9d1b4381bb103325bc3a
Reviewed-on: https://chromium-review.googlesource.com/1172292Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Commit-Queue: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55110}
parent 4c98815a
......@@ -249,7 +249,23 @@ class Expression : public AstNode {
static const uint8_t kNextBitFieldIndex = AstNode::kNextBitFieldIndex;
};
// V8's notion of BreakableStatement does not correspond to the notion of
// BreakableStatement in ECMAScript. In V8, the idea is that a
// BreakableStatement is a statement that can be the target of a break
// statement. The BreakableStatement AST node carries a list of labels, any of
// which can be used as an argument to the break statement in order to target
// it.
//
// Since we don't want to attach a list of labels to all kinds of statements, we
// only declare switchs, loops, and blocks as BreakableStatements. This means
// that we implement breaks targeting other statement forms as breaks targeting
// a substatement thereof. For instance, in "foo: if (b) { f(); break foo; }" we
// pretend that foo is the label of the inner block. That's okay because one
// can't observe the difference.
//
// This optimization makes it harder to detect invalid continue labels, see the
// need for own_labels in IterationStatement.
//
class BreakableStatement : public Statement {
public:
enum BreakableType {
......@@ -257,6 +273,13 @@ class BreakableStatement : public Statement {
TARGET_FOR_NAMED_ONLY
};
// A list of all labels declared on the path up to the previous
// BreakableStatement (if any).
//
// Example: "l1: for (;;) l2: l3: { l4: if (b) l5: { s } }"
// labels() of the ForStatement will be l1.
// labels() of the Block { l4: ... } will be l2, l3.
// labels() of the Block { s } will be l4, l5.
ZonePtrList<const AstRawString>* labels() const;
// Testers.
......@@ -441,11 +464,23 @@ class IterationStatement : public BreakableStatement {
ZonePtrList<const AstRawString>* labels() const { return labels_; }
// A list of all labels that the iteration statement is directly prefixed
// with, i.e. all the labels that a continue statement in the body can use to
// continue this iteration statement. This is always a subset of {labels}.
//
// Example: "l1: { l2: if (b) l3: l4: for (;;) s }"
// labels() of the Block will be l1.
// labels() of the ForStatement will be l2, l3, l4.
// own_labels() of the ForStatement will be l3, l4.
ZonePtrList<const AstRawString>* own_labels() const { return own_labels_; }
protected:
IterationStatement(ZonePtrList<const AstRawString>* labels, int pos,
IterationStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos,
NodeType type)
: BreakableStatement(TARGET_FOR_ANONYMOUS, pos, type),
labels_(labels),
own_labels_(own_labels),
body_(nullptr) {}
void Initialize(Statement* body) { body_ = body; }
......@@ -454,6 +489,7 @@ class IterationStatement : public BreakableStatement {
private:
ZonePtrList<const AstRawString>* labels_;
ZonePtrList<const AstRawString>* own_labels_;
Statement* body_;
};
......@@ -470,8 +506,10 @@ class DoWhileStatement final : public IterationStatement {
private:
friend class AstNodeFactory;
DoWhileStatement(ZonePtrList<const AstRawString>* labels, int pos)
: IterationStatement(labels, pos, kDoWhileStatement), cond_(nullptr) {}
DoWhileStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos)
: IterationStatement(labels, own_labels, pos, kDoWhileStatement),
cond_(nullptr) {}
Expression* cond_;
};
......@@ -489,8 +527,10 @@ class WhileStatement final : public IterationStatement {
private:
friend class AstNodeFactory;
WhileStatement(ZonePtrList<const AstRawString>* labels, int pos)
: IterationStatement(labels, pos, kWhileStatement), cond_(nullptr) {}
WhileStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos)
: IterationStatement(labels, own_labels, pos, kWhileStatement),
cond_(nullptr) {}
Expression* cond_;
};
......@@ -513,8 +553,9 @@ class ForStatement final : public IterationStatement {
private:
friend class AstNodeFactory;
ForStatement(ZonePtrList<const AstRawString>* labels, int pos)
: IterationStatement(labels, pos, kForStatement),
ForStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos)
: IterationStatement(labels, own_labels, pos, kForStatement),
init_(nullptr),
cond_(nullptr),
next_(nullptr) {}
......@@ -539,9 +580,10 @@ class ForEachStatement : public IterationStatement {
}
protected:
ForEachStatement(ZonePtrList<const AstRawString>* labels, int pos,
ForEachStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos,
NodeType type)
: IterationStatement(labels, pos, type) {}
: IterationStatement(labels, own_labels, pos, type) {}
};
......@@ -566,8 +608,9 @@ class ForInStatement final : public ForEachStatement {
private:
friend class AstNodeFactory;
ForInStatement(ZonePtrList<const AstRawString>* labels, int pos)
: ForEachStatement(labels, pos, kForInStatement),
ForInStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos)
: ForEachStatement(labels, own_labels, pos, kForInStatement),
each_(nullptr),
subject_(nullptr) {
bit_field_ = ForInTypeField::update(bit_field_, SLOW_FOR_IN);
......@@ -632,8 +675,9 @@ class ForOfStatement final : public ForEachStatement {
private:
friend class AstNodeFactory;
ForOfStatement(ZonePtrList<const AstRawString>* labels, int pos)
: ForEachStatement(labels, pos, kForOfStatement),
ForOfStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos)
: ForEachStatement(labels, own_labels, pos, kForOfStatement),
iterator_(nullptr),
assign_iterator_(nullptr),
next_result_(nullptr),
......@@ -2810,9 +2854,11 @@ class AstNodeFactory final BASE_EMBEDDED {
Block(zone_, labels, capacity, ignore_completion_value);
}
#define STATEMENT_WITH_LABELS(NodeType) \
NodeType* New##NodeType(ZonePtrList<const AstRawString>* labels, int pos) { \
return new (zone_) NodeType(labels, pos); \
#define STATEMENT_WITH_LABELS(NodeType) \
NodeType* New##NodeType(ZonePtrList<const AstRawString>* labels, \
ZonePtrList<const AstRawString>* own_labels, \
int pos) { \
return new (zone_) NodeType(labels, own_labels, pos); \
}
STATEMENT_WITH_LABELS(DoWhileStatement)
STATEMENT_WITH_LABELS(WhileStatement)
......@@ -2824,23 +2870,25 @@ class AstNodeFactory final BASE_EMBEDDED {
return new (zone_) SwitchStatement(zone_, labels, tag, pos);
}
ForEachStatement* NewForEachStatement(ForEachStatement::VisitMode visit_mode,
ZonePtrList<const AstRawString>* labels,
int pos) {
ForEachStatement* NewForEachStatement(
ForEachStatement::VisitMode visit_mode,
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos) {
switch (visit_mode) {
case ForEachStatement::ENUMERATE: {
return new (zone_) ForInStatement(labels, pos);
return new (zone_) ForInStatement(labels, own_labels, pos);
}
case ForEachStatement::ITERATE: {
return new (zone_) ForOfStatement(labels, pos);
return new (zone_) ForOfStatement(labels, own_labels, pos);
}
}
UNREACHABLE();
}
ForOfStatement* NewForOfStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
int pos) {
return new (zone_) ForOfStatement(labels, pos);
return new (zone_) ForOfStatement(labels, own_labels, pos);
}
ExpressionStatement* NewExpressionStatement(Expression* expression, int pos) {
......
......@@ -749,9 +749,11 @@ void AstPrinter::PrintLiteralWithModeIndented(const char* info, Variable* var,
}
}
void AstPrinter::PrintLabelsIndented(ZonePtrList<const AstRawString>* labels) {
void AstPrinter::PrintLabelsIndented(ZonePtrList<const AstRawString>* labels,
const char* prefix) {
if (labels == nullptr || labels->length() == 0) return;
PrintIndented("LABELS ");
PrintIndented(prefix);
Print("LABELS ");
PrintLabels(labels);
Print("\n");
}
......@@ -922,6 +924,7 @@ void AstPrinter::VisitSwitchStatement(SwitchStatement* node) {
void AstPrinter::VisitDoWhileStatement(DoWhileStatement* node) {
IndentedScope indent(this, "DO", node->position());
PrintLabelsIndented(node->labels());
PrintLabelsIndented(node->own_labels(), "OWN ");
PrintIndentedVisit("BODY", node->body());
PrintIndentedVisit("COND", node->cond());
}
......@@ -930,6 +933,7 @@ void AstPrinter::VisitDoWhileStatement(DoWhileStatement* node) {
void AstPrinter::VisitWhileStatement(WhileStatement* node) {
IndentedScope indent(this, "WHILE", node->position());
PrintLabelsIndented(node->labels());
PrintLabelsIndented(node->own_labels(), "OWN ");
PrintIndentedVisit("COND", node->cond());
PrintIndentedVisit("BODY", node->body());
}
......@@ -938,6 +942,7 @@ void AstPrinter::VisitWhileStatement(WhileStatement* node) {
void AstPrinter::VisitForStatement(ForStatement* node) {
IndentedScope indent(this, "FOR", node->position());
PrintLabelsIndented(node->labels());
PrintLabelsIndented(node->own_labels(), "OWN ");
if (node->init()) PrintIndentedVisit("INIT", node->init());
if (node->cond()) PrintIndentedVisit("COND", node->cond());
PrintIndentedVisit("BODY", node->body());
......@@ -947,6 +952,8 @@ void AstPrinter::VisitForStatement(ForStatement* node) {
void AstPrinter::VisitForInStatement(ForInStatement* node) {
IndentedScope indent(this, "FOR IN", node->position());
PrintLabelsIndented(node->labels());
PrintLabelsIndented(node->own_labels(), "OWN ");
PrintIndentedVisit("FOR", node->each());
PrintIndentedVisit("IN", node->enumerable());
PrintIndentedVisit("BODY", node->body());
......@@ -955,6 +962,8 @@ void AstPrinter::VisitForInStatement(ForInStatement* node) {
void AstPrinter::VisitForOfStatement(ForOfStatement* node) {
IndentedScope indent(this, "FOR OF", node->position());
PrintLabelsIndented(node->labels());
PrintLabelsIndented(node->own_labels(), "OWN ");
PrintIndentedVisit("INIT", node->assign_iterator());
PrintIndentedVisit("NEXT", node->next_result());
PrintIndentedVisit("DONE", node->result_done());
......
......@@ -110,7 +110,8 @@ class AstPrinter final : public AstVisitor<AstPrinter> {
bool quote);
void PrintLiteralWithModeIndented(const char* info, Variable* var,
const AstRawString* value);
void PrintLabelsIndented(ZonePtrList<const AstRawString>* labels);
void PrintLabelsIndented(ZonePtrList<const AstRawString>* labels,
const char* prefix = "");
void PrintObjectProperties(ZonePtrList<ObjectLiteral::Property>* properties);
void PrintClassProperties(ZonePtrList<ClassLiteral::Property>* properties);
......
......@@ -1235,10 +1235,15 @@ class ParserBase {
Token::Value end_token, bool may_abort,
bool* ok);
StatementT ParseStatementListItem(bool* ok);
StatementT ParseStatement(ZonePtrList<const AstRawString>* labels, bool* ok) {
return ParseStatement(labels, kDisallowLabelledFunctionStatement, ok);
StatementT ParseStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
bool* ok) {
return ParseStatement(labels, own_labels,
kDisallowLabelledFunctionStatement, ok);
}
StatementT ParseStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
AllowLabelledFunctionStatement allow_function,
bool* ok);
BlockT ParseBlock(ZonePtrList<const AstRawString>* labels, bool* ok);
......@@ -1262,6 +1267,7 @@ class ParserBase {
StatementT ParseExpressionOrLabelledStatement(
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
AllowLabelledFunctionStatement allow_function, bool* ok);
StatementT ParseIfStatement(ZonePtrList<const AstRawString>* labels,
bool* ok);
......@@ -1272,34 +1278,41 @@ class ParserBase {
StatementT ParseWithStatement(ZonePtrList<const AstRawString>* labels,
bool* ok);
StatementT ParseDoWhileStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
bool* ok);
StatementT ParseWhileStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
bool* ok);
StatementT ParseThrowStatement(bool* ok);
StatementT ParseSwitchStatement(ZonePtrList<const AstRawString>* labels,
bool* ok);
StatementT ParseTryStatement(bool* ok);
StatementT ParseForStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
bool* ok);
StatementT ParseForEachStatementWithDeclarations(
int stmt_pos, ForInfo* for_info, ZonePtrList<const AstRawString>* labels,
Scope* inner_block_scope, bool* ok);
ZonePtrList<const AstRawString>* own_labels, Scope* inner_block_scope,
bool* ok);
StatementT ParseForEachStatementWithoutDeclarations(
int stmt_pos, ExpressionT expression, int lhs_beg_pos, int lhs_end_pos,
ForInfo* for_info, ZonePtrList<const AstRawString>* labels, bool* ok);
ForInfo* for_info, ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, bool* ok);
// Parse a C-style for loop: 'for (<init>; <cond>; <next>) { ... }'
// "for (<init>;" is assumed to have been parser already.
ForStatementT ParseStandardForLoop(int stmt_pos,
ZonePtrList<const AstRawString>* labels,
ExpressionT* cond, StatementT* next,
StatementT* body, bool* ok);
ForStatementT ParseStandardForLoop(
int stmt_pos, ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, ExpressionT* cond,
StatementT* next, StatementT* body, bool* ok);
// Same as the above, but handles those cases where <init> is a
// lexical variable declaration.
StatementT ParseStandardForLoopWithLexicalDeclarations(
int stmt_pos, StatementT init, ForInfo* for_info,
ZonePtrList<const AstRawString>* labels, bool* ok);
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, bool* ok);
StatementT ParseForAwaitStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
bool* ok);
bool IsNextLetKeyword();
......@@ -4983,12 +4996,13 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatementListItem(
default:
break;
}
return ParseStatement(nullptr, kAllowLabelledFunctionStatement, ok);
return ParseStatement(nullptr, nullptr, kAllowLabelledFunctionStatement, ok);
}
template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatement(
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
AllowLabelledFunctionStatement allow_function, bool* ok) {
// Statement ::
// Block
......@@ -5007,6 +5021,9 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatement(
// TryStatement
// DebuggerStatement
// {own_labels} is always a subset of {labels}.
DCHECK_IMPLIES(labels == nullptr, own_labels == nullptr);
// Note: Since labels can only be used by 'break' and 'continue'
// statements, which themselves are only valid within blocks,
// iterations or 'switch' statements (i.e., BreakableStatements),
......@@ -5022,14 +5039,14 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatement(
case Token::IF:
return ParseIfStatement(labels, ok);
case Token::DO:
return ParseDoWhileStatement(labels, ok);
return ParseDoWhileStatement(labels, own_labels, ok);
case Token::WHILE:
return ParseWhileStatement(labels, ok);
return ParseWhileStatement(labels, own_labels, ok);
case Token::FOR:
if (V8_UNLIKELY(is_async_function() && PeekAhead() == Token::AWAIT)) {
return ParseForAwaitStatement(labels, ok);
return ParseForAwaitStatement(labels, own_labels, ok);
}
return ParseForStatement(labels, ok);
return ParseForStatement(labels, own_labels, ok);
case Token::CONTINUE:
return ParseContinueStatement(ok);
case Token::BREAK:
......@@ -5082,7 +5099,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatement(
}
V8_FALLTHROUGH;
default:
return ParseExpressionOrLabelledStatement(labels, allow_function, ok);
return ParseExpressionOrLabelledStatement(labels, own_labels,
allow_function, ok);
}
}
......@@ -5122,7 +5140,7 @@ template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseScopedStatement(
ZonePtrList<const AstRawString>* labels, bool* ok) {
if (is_strict(language_mode()) || peek() != Token::FUNCTION) {
return ParseStatement(labels, ok);
return ParseStatement(labels, nullptr, ok);
} else {
// Make a block around the statement for a lexical binding
// is introduced by a FunctionDeclaration.
......@@ -5182,6 +5200,7 @@ template <typename Impl>
typename ParserBase<Impl>::StatementT
ParserBase<Impl>::ParseExpressionOrLabelledStatement(
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
AllowLabelledFunctionStatement allow_function, bool* ok) {
// ExpressionStatement | LabelledStatement ::
// Expression ';'
......@@ -5225,15 +5244,15 @@ ParserBase<Impl>::ParseExpressionOrLabelledStatement(
impl()->IsIdentifier(expr)) {
// The whole expression was a single identifier, and not, e.g.,
// something starting with an identifier or a parenthesized identifier.
labels = impl()->DeclareLabel(labels, impl()->AsIdentifierExpression(expr),
CHECK_OK);
impl()->DeclareLabel(&labels, &own_labels,
impl()->AsIdentifierExpression(expr), CHECK_OK);
Consume(Token::COLON);
// ES#sec-labelled-function-declarations Labelled Function Declarations
if (peek() == Token::FUNCTION && is_sloppy(language_mode()) &&
allow_function == kAllowLabelledFunctionStatement) {
return ParseFunctionDeclaration(ok);
}
return ParseStatement(labels, allow_function, ok);
return ParseStatement(labels, own_labels, allow_function, ok);
}
// If we have an extension, we allow a native function declaration.
......@@ -5425,7 +5444,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWithStatement(
{
BlockState block_state(&scope_, with_scope);
with_scope->set_start_position(scanner()->peek_location().beg_pos);
body = ParseStatement(labels, CHECK_OK);
body = ParseStatement(labels, nullptr, CHECK_OK);
with_scope->set_end_position(scanner()->location().end_pos);
}
return factory()->NewWithStatement(with_scope, expr, body, pos);
......@@ -5433,11 +5452,13 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWithStatement(
template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDoWhileStatement(
ZonePtrList<const AstRawString>* labels, bool* ok) {
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, bool* ok) {
// DoStatement ::
// 'do' Statement 'while' '(' Expression ')' ';'
auto loop = factory()->NewDoWhileStatement(labels, peek_position());
auto loop =
factory()->NewDoWhileStatement(labels, own_labels, peek_position());
typename Types::Target target(this, loop);
SourceRange body_range;
......@@ -5446,7 +5467,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDoWhileStatement(
Expect(Token::DO, CHECK_OK);
{
SourceRangeScope range_scope(scanner(), &body_range);
body = ParseStatement(nullptr, CHECK_OK);
body = ParseStatement(nullptr, nullptr, CHECK_OK);
}
Expect(Token::WHILE, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK);
......@@ -5468,11 +5489,12 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDoWhileStatement(
template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWhileStatement(
ZonePtrList<const AstRawString>* labels, bool* ok) {
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, bool* ok) {
// WhileStatement ::
// 'while' '(' Expression ')' Statement
auto loop = factory()->NewWhileStatement(labels, peek_position());
auto loop = factory()->NewWhileStatement(labels, own_labels, peek_position());
typename Types::Target target(this, loop);
SourceRange body_range;
......@@ -5484,7 +5506,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWhileStatement(
Expect(Token::RPAREN, CHECK_OK);
{
SourceRangeScope range_scope(scanner(), &body_range);
body = ParseStatement(nullptr, CHECK_OK);
body = ParseStatement(nullptr, nullptr, CHECK_OK);
}
loop->Initialize(cond, body);
......@@ -5683,7 +5705,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement(
template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement(
ZonePtrList<const AstRawString>* labels, bool* ok) {
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, bool* ok) {
// Either a standard for loop
// for (<init>; <cond>; <next>) { ... }
// or a for-each loop
......@@ -5723,8 +5746,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement(
if (CheckInOrOf(&for_info.mode)) {
scope()->set_is_hidden();
return ParseForEachStatementWithDeclarations(stmt_pos, &for_info, labels,
inner_block_scope, ok);
return ParseForEachStatementWithDeclarations(
stmt_pos, &for_info, labels, own_labels, inner_block_scope, ok);
}
Expect(Token::SEMICOLON, CHECK_OK);
......@@ -5736,8 +5759,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement(
// No variable declarations will have been created in inner_block_scope.
DCHECK_NULL(finalized);
USE(finalized);
return ParseStandardForLoopWithLexicalDeclarations(stmt_pos, init,
&for_info, labels, ok);
return ParseStandardForLoopWithLexicalDeclarations(
stmt_pos, init, &for_info, labels, own_labels, ok);
}
StatementT init = impl()->NullStatement();
......@@ -5749,7 +5772,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement(
if (CheckInOrOf(&for_info.mode)) {
return ParseForEachStatementWithDeclarations(stmt_pos, &for_info, labels,
nullptr, ok);
own_labels, nullptr, ok);
}
init = impl()->BuildInitializationBlock(&for_info.parsing_result, nullptr,
......@@ -5772,9 +5795,9 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement(
}
if (is_for_each) {
return ParseForEachStatementWithoutDeclarations(stmt_pos, expression,
lhs_beg_pos, lhs_end_pos,
&for_info, labels, ok);
return ParseForEachStatementWithoutDeclarations(
stmt_pos, expression, lhs_beg_pos, lhs_end_pos, &for_info, labels,
own_labels, ok);
}
// Initializer is just an expression.
init = factory()->NewExpressionStatement(expression, lhs_beg_pos);
......@@ -5786,8 +5809,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement(
ExpressionT cond = impl()->NullExpression();
StatementT next = impl()->NullStatement();
StatementT body = impl()->NullStatement();
ForStatementT loop =
ParseStandardForLoop(stmt_pos, labels, &cond, &next, &body, CHECK_OK);
ForStatementT loop = ParseStandardForLoop(stmt_pos, labels, own_labels, &cond,
&next, &body, CHECK_OK);
loop->Initialize(init, cond, next, body);
return loop;
}
......@@ -5796,7 +5819,8 @@ template <typename Impl>
typename ParserBase<Impl>::StatementT
ParserBase<Impl>::ParseForEachStatementWithDeclarations(
int stmt_pos, ForInfo* for_info, ZonePtrList<const AstRawString>* labels,
Scope* inner_block_scope, bool* ok) {
ZonePtrList<const AstRawString>* own_labels, Scope* inner_block_scope,
bool* ok) {
// Just one declaration followed by in/of.
if (for_info->parsing_result.declarations.size() != 1) {
impl()->ReportMessageAt(for_info->parsing_result.bindings_loc,
......@@ -5824,7 +5848,8 @@ ParserBase<Impl>::ParseForEachStatementWithDeclarations(
BlockT init_block = impl()->RewriteForVarInLegacy(*for_info);
auto loop = factory()->NewForEachStatement(for_info->mode, labels, stmt_pos);
auto loop = factory()->NewForEachStatement(for_info->mode, labels, own_labels,
stmt_pos);
typename Types::Target target(this, loop);
ExpressionT enumerable = impl()->NullExpression();
......@@ -5854,7 +5879,7 @@ ParserBase<Impl>::ParseForEachStatementWithDeclarations(
SourceRange body_range;
SourceRangeScope range_scope(scanner(), &body_range);
StatementT body = ParseStatement(nullptr, CHECK_OK);
StatementT body = ParseStatement(nullptr, nullptr, CHECK_OK);
impl()->RecordIterationStatementSourceRange(loop, range_scope.Finalize());
impl()->DesugarBindingInForEachStatement(for_info, &body_block,
......@@ -5892,7 +5917,8 @@ template <typename Impl>
typename ParserBase<Impl>::StatementT
ParserBase<Impl>::ParseForEachStatementWithoutDeclarations(
int stmt_pos, ExpressionT expression, int lhs_beg_pos, int lhs_end_pos,
ForInfo* for_info, ZonePtrList<const AstRawString>* labels, bool* ok) {
ForInfo* for_info, ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, bool* ok) {
// Initializer is reference followed by in/of.
if (!expression->IsArrayLiteral() && !expression->IsObjectLiteral()) {
expression = CheckAndRewriteReferenceExpression(
......@@ -5900,7 +5926,8 @@ ParserBase<Impl>::ParseForEachStatementWithoutDeclarations(
kSyntaxError, CHECK_OK);
}
auto loop = factory()->NewForEachStatement(for_info->mode, labels, stmt_pos);
auto loop = factory()->NewForEachStatement(for_info->mode, labels, own_labels,
stmt_pos);
typename Types::Target target(this, loop);
ExpressionT enumerable = impl()->NullExpression();
......@@ -5919,7 +5946,7 @@ ParserBase<Impl>::ParseForEachStatementWithoutDeclarations(
SourceRange body_range;
SourceRangeScope range_scope(scanner(), &body_range);
body = ParseStatement(nullptr, CHECK_OK);
body = ParseStatement(nullptr, nullptr, CHECK_OK);
impl()->RecordIterationStatementSourceRange(loop, range_scope.Finalize());
}
return impl()->InitializeForEachStatement(loop, expression, enumerable, body);
......@@ -5929,7 +5956,8 @@ template <typename Impl>
typename ParserBase<Impl>::StatementT
ParserBase<Impl>::ParseStandardForLoopWithLexicalDeclarations(
int stmt_pos, StatementT init, ForInfo* for_info,
ZonePtrList<const AstRawString>* labels, bool* ok) {
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, bool* ok) {
// The condition and the next statement of the for loop must be parsed
// in a new scope.
Scope* inner_scope = NewScope(BLOCK_SCOPE);
......@@ -5940,8 +5968,8 @@ ParserBase<Impl>::ParseStandardForLoopWithLexicalDeclarations(
{
BlockState block_state(&scope_, inner_scope);
scope()->set_start_position(scanner()->location().beg_pos);
loop =
ParseStandardForLoop(stmt_pos, labels, &cond, &next, &body, CHECK_OK);
loop = ParseStandardForLoop(stmt_pos, labels, own_labels, &cond, &next,
&body, CHECK_OK);
scope()->set_end_position(scanner()->location().end_pos);
}
......@@ -5984,9 +6012,10 @@ ParserBase<Impl>::ParseStandardForLoopWithLexicalDeclarations(
template <typename Impl>
typename ParserBase<Impl>::ForStatementT ParserBase<Impl>::ParseStandardForLoop(
int stmt_pos, ZonePtrList<const AstRawString>* labels, ExpressionT* cond,
int stmt_pos, ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, ExpressionT* cond,
StatementT* next, StatementT* body, bool* ok) {
ForStatementT loop = factory()->NewForStatement(labels, stmt_pos);
ForStatementT loop = factory()->NewForStatement(labels, own_labels, stmt_pos);
typename Types::Target target(this, loop);
if (peek() != Token::SEMICOLON) {
......@@ -6003,7 +6032,7 @@ typename ParserBase<Impl>::ForStatementT ParserBase<Impl>::ParseStandardForLoop(
SourceRange body_range;
{
SourceRangeScope range_scope(scanner(), &body_range);
*body = ParseStatement(nullptr, CHECK_OK);
*body = ParseStatement(nullptr, nullptr, CHECK_OK);
}
impl()->RecordIterationStatementSourceRange(loop, body_range);
......@@ -6023,7 +6052,8 @@ void ParserBase<Impl>::MarkLoopVariableAsAssigned(
template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement(
ZonePtrList<const AstRawString>* labels, bool* ok) {
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, bool* ok) {
// for await '(' ForDeclaration of AssignmentExpression ')'
DCHECK(is_async_function());
......@@ -6040,7 +6070,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement(
scope()->set_start_position(scanner()->location().beg_pos);
scope()->set_is_hidden();
auto loop = factory()->NewForOfStatement(labels, stmt_pos);
auto loop = factory()->NewForOfStatement(labels, own_labels, stmt_pos);
typename Types::Target target(this, loop);
ExpressionT each_variable = impl()->NullExpression();
......@@ -6123,7 +6153,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement(
SourceRange body_range;
SourceRangeScope range_scope(scanner(), &body_range);
body = ParseStatement(nullptr, CHECK_OK);
body = ParseStatement(nullptr, nullptr, CHECK_OK);
scope()->set_end_position(scanner()->location().end_pos);
impl()->RecordIterationStatementSourceRange(loop, range_scope.Finalize());
......
......@@ -1470,29 +1470,40 @@ Statement* Parser::DeclareNative(const AstRawString* name, int pos, bool* ok) {
pos);
}
ZonePtrList<const AstRawString>* Parser::DeclareLabel(
ZonePtrList<const AstRawString>* labels, VariableProxy* var, bool* ok) {
void Parser::DeclareLabel(ZonePtrList<const AstRawString>** labels,
ZonePtrList<const AstRawString>** own_labels,
VariableProxy* var, bool* ok) {
DCHECK(IsIdentifier(var));
const AstRawString* label = var->raw_name();
// TODO(1240780): We don't check for redeclaration of labels
// during preparsing since keeping track of the set of active
// labels requires nontrivial changes to the way scopes are
// structured. However, these are probably changes we want to
// make later anyway so we should go back and fix this then.
if (ContainsLabel(labels, label) || TargetStackContainsLabel(label)) {
if (ContainsLabel(*labels, label) || TargetStackContainsLabel(label)) {
ReportMessage(MessageTemplate::kLabelRedeclaration, label);
*ok = false;
return nullptr;
return;
}
if (labels == nullptr) {
labels = new (zone()) ZonePtrList<const AstRawString>(1, zone());
// Add {label} to both {labels} and {own_labels}.
if (*labels == nullptr) {
DCHECK_NULL(*own_labels);
*labels = new (zone()) ZonePtrList<const AstRawString>(1, zone());
*own_labels = new (zone()) ZonePtrList<const AstRawString>(1, zone());
} else {
if (*own_labels == nullptr) {
*own_labels = new (zone()) ZonePtrList<const AstRawString>(1, zone());
}
}
labels->Add(label, zone());
(*labels)->Add(label, zone());
(*own_labels)->Add(label, zone());
// Remove the "ghost" variable that turned out to be a label
// from the top scope. This way, we don't try to resolve it
// during the scope processing.
scope()->RemoveUnresolved(var);
return labels;
}
bool Parser::ContainsLabel(ZonePtrList<const AstRawString>* labels,
......@@ -2191,7 +2202,7 @@ Statement* Parser::DesugarLexicalBindingsInForStatement(
// need to know about it. This should be safe because we don't run any code
// in this function that looks up break targets.
ForStatement* outer_loop =
factory()->NewForStatement(nullptr, kNoSourcePosition);
factory()->NewForStatement(nullptr, nullptr, kNoSourcePosition);
outer_block->statements()->Add(outer_loop, zone());
outer_block->set_scope(scope());
......@@ -3393,9 +3404,10 @@ IterationStatement* Parser::LookupContinueTarget(const AstRawString* label,
if (stat == nullptr) continue;
DCHECK(stat->is_target_for_anonymous());
if (anonymous || ContainsLabel(stat->labels(), label)) {
if (anonymous || ContainsLabel(stat->own_labels(), label)) {
return stat;
}
if (ContainsLabel(stat->labels(), label)) break;
}
return nullptr;
}
......
......@@ -284,8 +284,9 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
Block* BuildInitializationBlock(DeclarationParsingResult* parsing_result,
ZonePtrList<const AstRawString>* names,
bool* ok);
ZonePtrList<const AstRawString>* DeclareLabel(
ZonePtrList<const AstRawString>* labels, VariableProxy* expr, bool* ok);
void DeclareLabel(ZonePtrList<const AstRawString>** labels,
ZonePtrList<const AstRawString>** own_labels,
VariableProxy* expr, bool* ok);
bool ContainsLabel(ZonePtrList<const AstRawString>* labels,
const AstRawString* label);
Expression* RewriteReturn(Expression* return_value, int pos);
......
......@@ -671,7 +671,8 @@ void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node,
// #maybe_store_and_unset_done;
// #increment_index;
// }
WhileStatement* loop = factory()->NewWhileStatement(nullptr, nopos);
WhileStatement* loop =
factory()->NewWhileStatement(nullptr, nullptr, nopos);
{
Expression* condition = factory()->NewUnaryOperation(
Token::NOT, factory()->NewVariableProxy(done), nopos);
......
......@@ -788,12 +788,14 @@ class PreParserFactory {
}
PreParserStatement NewDoWhileStatement(
ZonePtrList<const AstRawString>* labels, int pos) {
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos) {
return PreParserStatement::Default();
}
PreParserStatement NewWhileStatement(ZonePtrList<const AstRawString>* labels,
int pos) {
PreParserStatement NewWhileStatement(
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos) {
return PreParserStatement::Default();
}
......@@ -808,19 +810,22 @@ class PreParserFactory {
return PreParserStatement::Default();
}
PreParserStatement NewForStatement(ZonePtrList<const AstRawString>* labels,
int pos) {
PreParserStatement NewForStatement(
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos) {
return PreParserStatement::Default();
}
PreParserStatement NewForEachStatement(
ForEachStatement::VisitMode visit_mode,
ZonePtrList<const AstRawString>* labels, int pos) {
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos) {
return PreParserStatement::Default();
}
PreParserStatement NewForOfStatement(ZonePtrList<const AstRawString>* labels,
int pos) {
PreParserStatement NewForOfStatement(
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos) {
return PreParserStatement::Default();
}
......@@ -1071,12 +1076,11 @@ class PreParser : public ParserBase<PreParser> {
const DeclarationParsingResult::Declaration* declaration,
ZonePtrList<const AstRawString>* names, bool* ok);
V8_INLINE ZonePtrList<const AstRawString>* DeclareLabel(
ZonePtrList<const AstRawString>* labels, const PreParserExpression& expr,
bool* ok) {
V8_INLINE void DeclareLabel(ZonePtrList<const AstRawString>** labels,
ZonePtrList<const AstRawString>** own_labels,
const PreParserExpression& expr, bool* ok) {
DCHECK(!parsing_module_ || !expr.AsIdentifier().IsAwait());
DCHECK(IsIdentifier(expr));
return labels;
}
// TODO(nikolaos): The preparser currently does not keep track of labels.
......
// Copyright 2018 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.
assertThrows("foo: if (true) do { continue foo } while (false)", SyntaxError);
assertThrows("foo: if (true) while (false) { continue foo }", SyntaxError);
assertThrows("foo: if (true) for (; false; ) { continue foo }", SyntaxError);
assertThrows("foo: if (true) for (let x of []) { continue foo }", SyntaxError);
assertThrows("foo: if (true) for (let x in []) { continue foo }", SyntaxError);
assertThrows("foo: if (true) { do { continue foo } while (false) }", SyntaxError);
assertThrows("foo: if (true) { while (false) { continue foo } }", SyntaxError);
assertThrows("foo: if (true) { for (; false; ) { continue foo } }", SyntaxError);
assertThrows("foo: if (true) { for (let x of []) { continue foo } }", SyntaxError);
assertThrows("foo: if (true) { for (let x in []) { continue foo } }", SyntaxError);
assertThrows("foo: goo: if (true) do { continue foo } while (false)", SyntaxError);
assertThrows("foo: goo: if (true) while (false) { continue foo }", SyntaxError);
assertThrows("foo: goo: if (true) for (; false; ) { continue foo }", SyntaxError);
assertThrows("foo: goo: if (true) for (let x of []) { continue foo }", SyntaxError);
assertThrows("foo: goo: if (true) for (let x in []) { continue foo }", SyntaxError);
assertThrows("foo: goo: if (true) { do { continue foo } while (false) }", SyntaxError);
assertThrows("foo: goo: if (true) { while (false) { continue foo } }", SyntaxError);
assertThrows("foo: goo: if (true) { for (; false; ) { continue foo } }", SyntaxError);
assertThrows("foo: goo: if (true) { for (let x of []) { continue foo } }", SyntaxError);
assertThrows("foo: goo: if (true) { for (let x in []) { continue foo } }", SyntaxError);
assertDoesNotThrow("if (true) foo: goo: do { continue foo } while (false)");
assertDoesNotThrow("if (true) foo: goo: while (false) { continue foo }");
assertDoesNotThrow("if (true) foo: goo: for (; false; ) { continue foo }");
assertDoesNotThrow("if (true) foo: goo: for (let x of []) { continue foo }");
assertDoesNotThrow("if (true) foo: goo: for (let x in []) { continue foo }");
assertThrows("if (true) foo: goo: { do { continue foo } while (false) }", SyntaxError);
assertThrows("if (true) foo: goo: { while (false) { continue foo } }", SyntaxError);
assertThrows("if (true) foo: goo: { for (; false; ) { continue foo } }", SyntaxError);
assertThrows("if (true) foo: goo: { for (let x of []) { continue foo } }", SyntaxError);
assertThrows("if (true) foo: goo: { for (let x in []) { continue foo } }", SyntaxError);
assertDoesNotThrow("if (true) { foo: goo: do { continue foo } while (false) }");
assertDoesNotThrow("if (true) { foo: goo: while (false) { continue foo } }");
assertDoesNotThrow("if (true) { foo: goo: for (; false; ) { continue foo } }");
assertDoesNotThrow("if (true) { foo: goo: for (let x of []) { continue foo } }");
assertDoesNotThrow("if (true) { foo: goo: for (let x in []) { continue foo } }");
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