[parser] Track labels in the parser-base rather than parser+ast

Bug: v8:8088
Change-Id: Ie92499a43e2286e9bb1c64b0d553a515d74d5aa2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2059989
Reviewed-by: Georg Neis <neis@chromium.org>
Reviewed-by: Simon Zünd <szuend@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66313}
This commit is contained in:
Toon Verwaest 2020-02-18 16:07:23 +01:00 committed by Commit Bot
parent b9e40f7c01
commit f5592da673
13 changed files with 264 additions and 510 deletions

View File

@ -1103,20 +1103,5 @@ const char* CallRuntime::debug_name() {
#endif // DEBUG
}
#define RETURN_LABELS(NodeType) \
case k##NodeType: \
return static_cast<const NodeType*>(this)->labels();
ZonePtrList<const AstRawString>* BreakableStatement::labels() const {
switch (node_type()) {
BREAKABLE_NODE_LIST(RETURN_LABELS)
ITERATION_NODE_LIST(RETURN_LABELS)
default:
UNREACHABLE();
}
}
#undef RETURN_LABELS
} // namespace internal
} // namespace v8

View File

@ -295,62 +295,28 @@ class FailureExpression : public Expression {
// 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.
// statement.
//
// Since we don't want to attach a list of labels to all kinds of statements, we
// Since we don't want to track a list of labels for 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.
//
// TODO(verwaest): Reconsider this optimization now that the tracking of labels
// is done at runtime.
class BreakableStatement : public Statement {
public:
enum BreakableType {
TARGET_FOR_ANONYMOUS,
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.
bool is_target_for_anonymous() const {
return BreakableTypeField::decode(bit_field_) == TARGET_FOR_ANONYMOUS;
}
private:
using BreakableTypeField = Statement::NextBitField<BreakableType, 1>;
protected:
BreakableStatement(BreakableType breakable_type, int position, NodeType type)
: Statement(position, type) {
bit_field_ |= BreakableTypeField::encode(breakable_type);
}
template <class T, int size>
using NextBitField = BreakableTypeField::Next<T, size>;
BreakableStatement(int position, NodeType type) : Statement(position, type) {}
};
class Block : public BreakableStatement {
class Block final : public BreakableStatement {
public:
ZonePtrList<Statement>* statements() { return &statements_; }
bool ignore_completion_value() const {
return IgnoreCompletionField::decode(bit_field_);
}
inline ZonePtrList<const AstRawString>* labels() const;
bool is_breakable() const { return IsBreakableField::decode(bit_field_); }
Scope* scope() const { return scope_; }
void set_scope(Scope* scope) { scope_ = scope; }
@ -368,49 +334,22 @@ class Block : public BreakableStatement {
Scope* scope_;
using IgnoreCompletionField = BreakableStatement::NextBitField<bool, 1>;
using IsLabeledField = IgnoreCompletionField::Next<bool, 1>;
using IsBreakableField = IgnoreCompletionField::Next<bool, 1>;
protected:
Block(Zone* zone, ZonePtrList<const AstRawString>* labels, int capacity,
bool ignore_completion_value)
: BreakableStatement(TARGET_FOR_NAMED_ONLY, kNoSourcePosition, kBlock),
Block(Zone* zone, int capacity, bool ignore_completion_value,
bool is_breakable)
: BreakableStatement(kNoSourcePosition, kBlock),
statements_(capacity, zone),
scope_(nullptr) {
bit_field_ |= IgnoreCompletionField::encode(ignore_completion_value) |
IsLabeledField::encode(labels != nullptr);
IsBreakableField::encode(is_breakable);
}
Block(ZonePtrList<const AstRawString>* labels, bool ignore_completion_value)
: Block(nullptr, labels, 0, ignore_completion_value) {}
Block(bool ignore_completion_value, bool is_breakable)
: Block(nullptr, 0, ignore_completion_value, is_breakable) {}
};
class LabeledBlock final : public Block {
private:
friend class AstNodeFactory;
friend class Block;
LabeledBlock(Zone* zone, ZonePtrList<const AstRawString>* labels,
int capacity, bool ignore_completion_value)
: Block(zone, labels, capacity, ignore_completion_value),
labels_(labels) {
DCHECK_NOT_NULL(labels);
DCHECK_GT(labels->length(), 0);
}
LabeledBlock(ZonePtrList<const AstRawString>* labels,
bool ignore_completion_value)
: LabeledBlock(nullptr, labels, 0, ignore_completion_value) {}
ZonePtrList<const AstRawString>* labels_;
};
inline ZonePtrList<const AstRawString>* Block::labels() const {
if (IsLabeledField::decode(bit_field_)) {
return static_cast<const LabeledBlock*>(this)->labels_;
}
return nullptr;
}
class Declaration : public AstNode {
public:
using List = base::ThreadedList<Declaration>;
@ -491,31 +430,12 @@ class IterationStatement : public BreakableStatement {
Statement* body() const { return body_; }
void set_body(Statement* s) { body_ = s; }
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,
ZonePtrList<const AstRawString>* own_labels, int pos,
NodeType type)
: BreakableStatement(TARGET_FOR_ANONYMOUS, pos, type),
labels_(labels),
own_labels_(own_labels),
body_(nullptr) {}
IterationStatement(int pos, NodeType type)
: BreakableStatement(pos, type), body_(nullptr) {}
void Initialize(Statement* body) { body_ = body; }
private:
ZonePtrList<const AstRawString>* labels_;
ZonePtrList<const AstRawString>* own_labels_;
Statement* body_;
};
@ -532,10 +452,8 @@ class DoWhileStatement final : public IterationStatement {
private:
friend class AstNodeFactory;
DoWhileStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos)
: IterationStatement(labels, own_labels, pos, kDoWhileStatement),
cond_(nullptr) {}
explicit DoWhileStatement(int pos)
: IterationStatement(pos, kDoWhileStatement), cond_(nullptr) {}
Expression* cond_;
};
@ -553,10 +471,8 @@ class WhileStatement final : public IterationStatement {
private:
friend class AstNodeFactory;
WhileStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos)
: IterationStatement(labels, own_labels, pos, kWhileStatement),
cond_(nullptr) {}
explicit WhileStatement(int pos)
: IterationStatement(pos, kWhileStatement), cond_(nullptr) {}
Expression* cond_;
};
@ -579,9 +495,8 @@ class ForStatement final : public IterationStatement {
private:
friend class AstNodeFactory;
ForStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos)
: IterationStatement(labels, own_labels, pos, kForStatement),
explicit ForStatement(int pos)
: IterationStatement(pos, kForStatement),
init_(nullptr),
cond_(nullptr),
next_(nullptr) {}
@ -617,12 +532,8 @@ class ForEachStatement : public IterationStatement {
protected:
friend class AstNodeFactory;
ForEachStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos,
NodeType type)
: IterationStatement(labels, own_labels, pos, type),
each_(nullptr),
subject_(nullptr) {}
ForEachStatement(int pos, NodeType type)
: IterationStatement(pos, type), each_(nullptr), subject_(nullptr) {}
Expression* each_;
Expression* subject_;
@ -632,9 +543,7 @@ class ForInStatement final : public ForEachStatement {
private:
friend class AstNodeFactory;
ForInStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos)
: ForEachStatement(labels, own_labels, pos, kForInStatement) {}
explicit ForInStatement(int pos) : ForEachStatement(pos, kForInStatement) {}
};
enum class IteratorType { kNormal, kAsync };
@ -645,11 +554,8 @@ class ForOfStatement final : public ForEachStatement {
private:
friend class AstNodeFactory;
ForOfStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos,
IteratorType type)
: ForEachStatement(labels, own_labels, pos, kForOfStatement),
type_(type) {}
ForOfStatement(int pos, IteratorType type)
: ForEachStatement(pos, kForOfStatement), type_(type) {}
IteratorType type_;
};
@ -777,8 +683,6 @@ class CaseClause final : public ZoneObject {
class SwitchStatement final : public BreakableStatement {
public:
ZonePtrList<const AstRawString>* labels() const { return labels_; }
Expression* tag() const { return tag_; }
void set_tag(Expression* t) { tag_ = t; }
@ -787,14 +691,9 @@ class SwitchStatement final : public BreakableStatement {
private:
friend class AstNodeFactory;
SwitchStatement(Zone* zone, ZonePtrList<const AstRawString>* labels,
Expression* tag, int pos)
: BreakableStatement(TARGET_FOR_ANONYMOUS, pos, kSwitchStatement),
labels_(labels),
tag_(tag),
cases_(4, zone) {}
SwitchStatement(Zone* zone, Expression* tag, int pos)
: BreakableStatement(pos, kSwitchStatement), tag_(tag), cases_(4, zone) {}
ZonePtrList<const AstRawString>* labels_;
Expression* tag_;
ZonePtrList<CaseClause> cases_;
};
@ -2824,59 +2723,46 @@ class AstNodeFactory final {
}
Block* NewBlock(int capacity, bool ignore_completion_value) {
return new (zone_) Block(zone_, nullptr, capacity, ignore_completion_value);
return new (zone_) Block(zone_, capacity, ignore_completion_value, false);
}
Block* NewBlock(bool ignore_completion_value,
ZonePtrList<const AstRawString>* labels) {
return labels != nullptr
? new (zone_) LabeledBlock(labels, ignore_completion_value)
: new (zone_) Block(labels, ignore_completion_value);
Block* NewBlock(bool ignore_completion_value, bool is_breakable) {
return new (zone_) Block(ignore_completion_value, is_breakable);
}
Block* NewBlock(bool ignore_completion_value,
const ScopedPtrList<Statement>& statements) {
Block* result = NewBlock(ignore_completion_value, nullptr);
Block* result = NewBlock(ignore_completion_value, false);
result->InitializeStatements(statements, zone_);
return result;
}
#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)
STATEMENT_WITH_LABELS(ForStatement)
#undef STATEMENT_WITH_LABELS
#define STATEMENT_WITH_POSITION(NodeType) \
NodeType* New##NodeType(int pos) { return new (zone_) NodeType(pos); }
STATEMENT_WITH_POSITION(DoWhileStatement)
STATEMENT_WITH_POSITION(WhileStatement)
STATEMENT_WITH_POSITION(ForStatement)
#undef STATEMENT_WITH_POSITION
SwitchStatement* NewSwitchStatement(ZonePtrList<const AstRawString>* labels,
Expression* tag, int pos) {
return new (zone_) SwitchStatement(zone_, labels, tag, pos);
SwitchStatement* NewSwitchStatement(Expression* tag, int pos) {
return new (zone_) SwitchStatement(zone_, tag, pos);
}
ForEachStatement* NewForEachStatement(
ForEachStatement::VisitMode visit_mode,
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos) {
ForEachStatement* NewForEachStatement(ForEachStatement::VisitMode visit_mode,
int pos) {
switch (visit_mode) {
case ForEachStatement::ENUMERATE: {
return new (zone_) ForInStatement(labels, own_labels, pos);
return new (zone_) ForInStatement(pos);
}
case ForEachStatement::ITERATE: {
return new (zone_)
ForOfStatement(labels, own_labels, pos, IteratorType::kNormal);
return new (zone_) ForOfStatement(pos, IteratorType::kNormal);
}
}
UNREACHABLE();
}
ForOfStatement* NewForOfStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
int pos, IteratorType type) {
return new (zone_) ForOfStatement(labels, own_labels, pos, type);
ForOfStatement* NewForOfStatement(int pos, IteratorType type) {
return new (zone_) ForOfStatement(pos, type);
}
ExpressionStatement* NewExpressionStatement(Expression* expression, int pos) {

View File

@ -659,15 +659,6 @@ void AstPrinter::Print(const char* format, ...) {
}
}
void AstPrinter::PrintLabels(ZonePtrList<const AstRawString>* labels) {
if (labels != nullptr) {
for (int i = 0; i < labels->length(); i++) {
PrintLiteral(labels->at(i), false);
Print(": ");
}
}
}
void AstPrinter::PrintLiteral(Literal* literal, bool quote) {
switch (literal->type()) {
case Literal::kString:
@ -819,16 +810,6 @@ void AstPrinter::PrintLiteralWithModeIndented(const char* info, Variable* var,
}
}
void AstPrinter::PrintLabelsIndented(ZonePtrList<const AstRawString>* labels,
const char* prefix) {
if (labels == nullptr || labels->length() == 0) return;
PrintIndented(prefix);
Print("LABELS ");
PrintLabels(labels);
Print("\n");
}
void AstPrinter::PrintIndentedVisit(const char* s, AstNode* node) {
if (node != nullptr) {
IndentedScope indent(this, s, node->position());
@ -902,7 +883,6 @@ void AstPrinter::VisitBlock(Block* node) {
const char* block_txt =
node->ignore_completion_value() ? "BLOCK NOCOMPLETIONS" : "BLOCK";
IndentedScope indent(this, block_txt, node->position());
PrintLabelsIndented(node->labels());
PrintStatements(node->statements());
}
@ -953,13 +933,11 @@ void AstPrinter::VisitIfStatement(IfStatement* node) {
void AstPrinter::VisitContinueStatement(ContinueStatement* node) {
IndentedScope indent(this, "CONTINUE", node->position());
PrintLabelsIndented(node->target()->labels());
}
void AstPrinter::VisitBreakStatement(BreakStatement* node) {
IndentedScope indent(this, "BREAK", node->position());
PrintLabelsIndented(node->target()->labels());
}
@ -978,7 +956,6 @@ void AstPrinter::VisitWithStatement(WithStatement* node) {
void AstPrinter::VisitSwitchStatement(SwitchStatement* node) {
IndentedScope indent(this, "SWITCH", node->position());
PrintLabelsIndented(node->labels());
PrintIndentedVisit("TAG", node->tag());
for (CaseClause* clause : *node->cases()) {
if (clause->is_default()) {
@ -995,8 +972,6 @@ 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());
}
@ -1004,8 +979,6 @@ 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());
}
@ -1013,8 +986,6 @@ 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());
@ -1024,8 +995,6 @@ 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->subject());
PrintIndentedVisit("BODY", node->body());
@ -1034,8 +1003,6 @@ 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 ");
const char* for_type;
switch (node->type()) {
case IteratorType::kNormal:

View File

@ -232,8 +232,6 @@ class ParserBase {
using FuncNameInferrerState = typename Types::FuncNameInferrer::State;
using SourceRange = typename Types::SourceRange;
using SourceRangeScope = typename Types::SourceRangeScope;
using TargetT = typename Types::Target;
using TargetScopeT = typename Types::TargetScope;
// All implementation-specific methods must be called through this.
Impl* impl() { return static_cast<Impl*>(this); }
@ -320,7 +318,6 @@ class ParserBase {
int loop_nesting_depth() const {
return function_state_->loop_nesting_depth();
}
int GetNextFunctionLiteralId() { return ++function_literal_id_; }
int GetLastFunctionLiteralId() const { return function_literal_id_; }
@ -370,6 +367,93 @@ class ParserBase {
Scope* const outer_scope_;
};
// ---------------------------------------------------------------------------
// Target is a support class to facilitate manipulation of the
// Parser's target_stack_ (the stack of potential 'break' and
// 'continue' statement targets). Upon construction, a new target is
// added; it is removed upon destruction.
// |labels| is a list of all labels that can be used as a target for break.
// |own_labels| is a list of all labels that an 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.
class Target {
public:
enum TargetType { TARGET_FOR_ANONYMOUS, TARGET_FOR_NAMED_ONLY };
Target(ParserBase* parser, BreakableStatementT statement,
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, TargetType target_type)
: stack_(parser->function_state_->target_stack_address()),
statement_(statement),
labels_(labels),
own_labels_(own_labels),
target_type_(target_type),
previous_(*stack_) {
DCHECK_IMPLIES(Impl::IsIterationStatement(statement_),
target_type == Target::TARGET_FOR_ANONYMOUS);
DCHECK_IMPLIES(!Impl::IsIterationStatement(statement_),
own_labels == nullptr);
*stack_ = this;
}
~Target() { *stack_ = previous_; }
const Target* previous() const { return previous_; }
const BreakableStatementT statement() const { return statement_; }
const ZonePtrList<const AstRawString>* labels() const { return labels_; }
const ZonePtrList<const AstRawString>* own_labels() const {
return own_labels_;
}
bool is_iteration() const { return Impl::IsIterationStatement(statement_); }
bool is_target_for_anonymous() const {
return target_type_ == TARGET_FOR_ANONYMOUS;
}
private:
Target** const stack_;
const BreakableStatementT statement_;
const ZonePtrList<const AstRawString>* const labels_;
const ZonePtrList<const AstRawString>* const own_labels_;
const TargetType target_type_;
Target* const previous_;
};
Target* target_stack() { return *function_state_->target_stack_address(); }
BreakableStatementT LookupBreakTarget(IdentifierT label) {
bool anonymous = impl()->IsNull(label);
for (const Target* t = target_stack(); t != nullptr; t = t->previous()) {
if ((anonymous && t->is_target_for_anonymous()) ||
(!anonymous &&
ContainsLabel(t->labels(),
impl()->GetRawNameFromIdentifier(label)))) {
return t->statement();
}
}
return impl()->NullStatement();
}
IterationStatementT LookupContinueTarget(IdentifierT label) {
bool anonymous = impl()->IsNull(label);
for (const Target* t = target_stack(); t != nullptr; t = t->previous()) {
if (!t->is_iteration()) continue;
DCHECK(t->is_target_for_anonymous());
if (anonymous || ContainsLabel(t->own_labels(),
impl()->GetRawNameFromIdentifier(label))) {
return impl()->AsIterationStatement(t->statement());
}
}
return impl()->NullStatement();
}
class FunctionState final : public BlockState {
public:
FunctionState(FunctionState** function_state_stack, Scope** scope_stack,
@ -427,7 +511,7 @@ class ParserBase {
PointerWithPayload<FunctionState, bool, 1> state_and_prev_value_;
};
class LoopScope {
class LoopScope final {
public:
explicit LoopScope(FunctionState* function_state)
: function_state_(function_state) {
@ -442,6 +526,8 @@ class ParserBase {
int loop_nesting_depth() const { return loop_nesting_depth_; }
Target** target_stack_address() { return &target_stack_; }
private:
// Properties count estimation.
int expected_property_count_;
@ -455,6 +541,7 @@ class ParserBase {
FunctionState** function_state_stack_;
FunctionState* outer_function_state_;
DeclarationScope* scope_;
Target* target_stack_ = nullptr; // for break, continue statements
// A reason, if any, why this function should not be optimized.
BailoutReason dont_optimize_reason_;
@ -623,6 +710,46 @@ class ParserBase {
bool is_rest;
};
void DeclareLabel(ZonePtrList<const AstRawString>** labels,
ZonePtrList<const AstRawString>** own_labels,
const AstRawString* label) {
if (ContainsLabel(*labels, label) || TargetStackContainsLabel(label)) {
ReportMessage(MessageTemplate::kLabelRedeclaration, label);
return;
}
// 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());
(*own_labels)->Add(label, zone());
}
bool ContainsLabel(const ZonePtrList<const AstRawString>* labels,
const AstRawString* label) {
DCHECK_NOT_NULL(label);
if (labels != nullptr) {
for (int i = labels->length(); i-- > 0;) {
if (labels->at(i) == label) return true;
}
}
return false;
}
bool TargetStackContainsLabel(const AstRawString* label) {
for (const Target* t = target_stack(); t != nullptr; t = t->previous()) {
if (ContainsLabel(t->labels(), label)) return true;
}
return false;
}
ClassLiteralProperty::Kind ClassPropertyKindFor(ParsePropertyKind kind) {
switch (kind) {
case ParsePropertyKind::kAccessorGetter:
@ -4809,10 +4936,6 @@ void ParserBase<Impl>::ParseStatementList(StatementListT* body,
}
}
// Allocate a target stack to use for this set of source elements. This way,
// all scripts and functions get their own target stack thus avoiding illegal
// breaks and continues across functions.
TargetScopeT target_scope(this);
while (peek() != end_token) {
StatementT stat = ParseStatementListItem();
if (impl()->IsNull(stat)) return;
@ -4932,8 +5055,9 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatement(
// put the labels there.
if (labels == nullptr) return ParseTryStatement();
StatementListT statements(pointer_buffer());
BlockT result = factory()->NewBlock(false, labels);
TargetT target(this, result);
BlockT result = factory()->NewBlock(false, true);
Target target(this, result, labels, nullptr,
Target::TARGET_FOR_NAMED_ONLY);
StatementT statement = ParseTryStatement();
statements.Add(statement);
result->InitializeStatements(statements, zone());
@ -4981,7 +5105,7 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseBlock(
// '{' StatementList '}'
// Parse the statements and collect escaping labels.
BlockT body = factory()->NewBlock(false, labels);
BlockT body = factory()->NewBlock(false, labels != nullptr);
StatementListT statements(pointer_buffer());
CheckStackOverflow();
@ -4989,7 +5113,7 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseBlock(
{
BlockState block_state(zone(), &scope_);
scope()->set_start_position(peek_position());
TargetT target(this, body);
Target target(this, body, labels, nullptr, Target::TARGET_FOR_NAMED_ONLY);
Expect(Token::LBRACE);
@ -5214,11 +5338,11 @@ ParserBase<Impl>::ParseContinueStatement() {
// ECMA allows "eval" or "arguments" as labels even in strict mode.
label = ParseIdentifier();
}
IterationStatementT target = impl()->LookupContinueTarget(label);
IterationStatementT target = LookupContinueTarget(label);
if (impl()->IsNull(target)) {
// Illegal continue statement.
MessageTemplate message = MessageTemplate::kIllegalContinue;
BreakableStatementT breakable_target = impl()->LookupBreakTarget(label);
BreakableStatementT breakable_target = LookupBreakTarget(label);
if (impl()->IsNull(label)) {
message = MessageTemplate::kNoIterationStatement;
} else if (impl()->IsNull(breakable_target)) {
@ -5250,11 +5374,12 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseBreakStatement(
}
// Parse labeled break statements that target themselves into
// empty statements, e.g. 'l1: l2: l3: break l2;'
if (!impl()->IsNull(label) && impl()->ContainsLabel(labels, label)) {
if (!impl()->IsNull(label) &&
impl()->ContainsLabel(labels, impl()->GetRawNameFromIdentifier(label))) {
ExpectSemicolon();
return factory()->EmptyStatement();
}
BreakableStatementT target = impl()->LookupBreakTarget(label);
BreakableStatementT target = LookupBreakTarget(label);
if (impl()->IsNull(target)) {
// Illegal break statement.
MessageTemplate message = MessageTemplate::kIllegalBreak;
@ -5349,9 +5474,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDoWhileStatement(
// 'do' Statement 'while' '(' Expression ')' ';'
typename FunctionState::LoopScope loop_scope(function_state_);
auto loop =
factory()->NewDoWhileStatement(labels, own_labels, peek_position());
TargetT target(this, loop);
auto loop = factory()->NewDoWhileStatement(peek_position());
Target target(this, loop, labels, own_labels, Target::TARGET_FOR_ANONYMOUS);
SourceRange body_range;
StatementT body = impl()->NullStatement();
@ -5389,8 +5513,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWhileStatement(
// 'while' '(' Expression ')' Statement
typename FunctionState::LoopScope loop_scope(function_state_);
auto loop = factory()->NewWhileStatement(labels, own_labels, peek_position());
TargetT target(this, loop);
auto loop = factory()->NewWhileStatement(peek_position());
Target target(this, loop, labels, own_labels, Target::TARGET_FOR_ANONYMOUS);
SourceRange body_range;
StatementT body = impl()->NullStatement();
@ -5438,7 +5562,6 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement(
// CaseClause ::
// 'case' Expression ':' StatementList
// 'default' ':' StatementList
int switch_pos = peek_position();
Consume(Token::SWITCH);
@ -5446,14 +5569,14 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement(
ExpressionT tag = ParseExpression();
Expect(Token::RPAREN);
auto switch_statement =
factory()->NewSwitchStatement(labels, tag, switch_pos);
auto switch_statement = factory()->NewSwitchStatement(tag, switch_pos);
{
BlockState cases_block_state(zone(), &scope_);
scope()->set_start_position(switch_pos);
scope()->SetNonlinear();
TargetT target(this, switch_statement);
Target target(this, switch_statement, labels, nullptr,
Target::TARGET_FOR_ANONYMOUS);
bool default_seen = false;
Expect(Token::LBRACE);
@ -5788,9 +5911,8 @@ ParserBase<Impl>::ParseForEachStatementWithDeclarations(
BlockT init_block = impl()->RewriteForVarInLegacy(*for_info);
auto loop = factory()->NewForEachStatement(for_info->mode, labels, own_labels,
stmt_pos);
TargetT target(this, loop);
auto loop = factory()->NewForEachStatement(for_info->mode, stmt_pos);
Target target(this, loop, labels, own_labels, Target::TARGET_FOR_ANONYMOUS);
ExpressionT enumerable = impl()->NullExpression();
if (for_info->mode == ForEachStatement::ITERATE) {
@ -5852,9 +5974,8 @@ ParserBase<Impl>::ParseForEachStatementWithoutDeclarations(
int stmt_pos, ExpressionT expression, int lhs_beg_pos, int lhs_end_pos,
ForInfo* for_info, ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels) {
auto loop = factory()->NewForEachStatement(for_info->mode, labels, own_labels,
stmt_pos);
TargetT target(this, loop);
auto loop = factory()->NewForEachStatement(for_info->mode, stmt_pos);
Target target(this, loop, labels, own_labels, Target::TARGET_FOR_ANONYMOUS);
ExpressionT enumerable = impl()->NullExpression();
if (for_info->mode == ForEachStatement::ITERATE) {
@ -5943,8 +6064,8 @@ typename ParserBase<Impl>::ForStatementT ParserBase<Impl>::ParseStandardForLoop(
ZonePtrList<const AstRawString>* own_labels, ExpressionT* cond,
StatementT* next, StatementT* body) {
CheckStackOverflow();
ForStatementT loop = factory()->NewForStatement(labels, own_labels, stmt_pos);
TargetT target(this, loop);
ForStatementT loop = factory()->NewForStatement(stmt_pos);
Target target(this, loop, labels, own_labels, Target::TARGET_FOR_ANONYMOUS);
if (peek() != Token::SEMICOLON) {
*cond = ParseExpression();
@ -5988,13 +6109,12 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement(
scope()->set_start_position(scanner()->location().beg_pos);
scope()->set_is_hidden();
auto loop = factory()->NewForOfStatement(labels, own_labels, stmt_pos,
IteratorType::kAsync);
auto loop = factory()->NewForOfStatement(stmt_pos, IteratorType::kAsync);
// Two suspends: one for next() and one for return()
function_state_->AddSuspend();
function_state_->AddSuspend();
TargetT target(this, loop);
Target target(this, loop, labels, own_labels, Target::TARGET_FOR_ANONYMOUS);
ExpressionT each_variable = impl()->NullExpression();

View File

@ -427,7 +427,6 @@ Parser::Parser(ParseInfo* info)
reusable_preparser_(nullptr),
mode_(PARSE_EAGERLY), // Lazy mode must be set explicitly.
source_range_map_(info->source_range_map()),
target_stack_(nullptr),
total_preparse_skipped_(0),
consumed_preparse_data_(info->consumed_preparse_data()),
preparse_data_buffer_(),
@ -566,7 +565,6 @@ FunctionLiteral* Parser::DoParseProgram(Isolate* isolate, ParseInfo* info) {
// isolate will be nullptr.
DCHECK_EQ(parsing_on_main_thread_, isolate != nullptr);
DCHECK_NULL(scope_);
DCHECK_NULL(target_stack_);
ParsingModeScope mode(this, allow_lazy_ ? PARSE_LAZILY : PARSE_EAGERLY);
ResetFunctionLiteralId();
@ -681,9 +679,6 @@ FunctionLiteral* Parser::DoParseProgram(Isolate* isolate, ParseInfo* info) {
info->set_max_function_literal_id(GetLastFunctionLiteralId());
// Make sure the target stack is empty.
DCHECK_NULL(target_stack_);
if (has_error()) return nullptr;
RecordFunctionLiteralSourceRange(result);
@ -848,7 +843,6 @@ FunctionLiteral* Parser::DoParseFunction(Isolate* isolate, ParseInfo* info,
DCHECK_EQ(parsing_on_main_thread_, isolate != nullptr);
DCHECK_NOT_NULL(raw_name);
DCHECK_NULL(scope_);
DCHECK_NULL(target_stack_);
DCHECK(ast_value_factory());
fni_.PushEnclosingName(raw_name);
@ -972,8 +966,6 @@ FunctionLiteral* Parser::DoParseFunction(Isolate* isolate, ParseInfo* info,
}
}
// Make sure the target stack is empty.
DCHECK_NULL(target_stack_);
DCHECK_IMPLIES(result,
info->function_literal_id() == result->function_literal_id());
return result;
@ -1601,43 +1593,6 @@ Statement* Parser::DeclareNative(const AstRawString* name, int pos) {
pos);
}
void Parser::DeclareLabel(ZonePtrList<const AstRawString>** labels,
ZonePtrList<const AstRawString>** own_labels,
const AstRawString* label) {
// 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)) {
ReportMessage(MessageTemplate::kLabelRedeclaration, label);
return;
}
// 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());
(*own_labels)->Add(label, zone());
}
bool Parser::ContainsLabel(ZonePtrList<const AstRawString>* labels,
const AstRawString* label) {
DCHECK_NOT_NULL(label);
if (labels != nullptr) {
for (int i = labels->length(); i-- > 0;) {
if (labels->at(i) == label) return true;
}
}
return false;
}
Block* Parser::IgnoreCompletion(Statement* statement) {
Block* block = factory()->NewBlock(1, true);
block->statements()->Add(statement, zone());
@ -2073,8 +2028,7 @@ Statement* Parser::DesugarLexicalBindingsInForStatement(
// explicit break target, instead handing it directly to those nodes that
// 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, nullptr, kNoSourcePosition);
ForStatement* outer_loop = factory()->NewForStatement(kNoSourcePosition);
outer_block->statements()->Add(outer_loop, zone());
outer_block->set_scope(scope());
@ -3065,40 +3019,6 @@ void Parser::InsertSloppyBlockFunctionVarBindings(DeclarationScope* scope) {
// ----------------------------------------------------------------------------
// Parser support
bool Parser::TargetStackContainsLabel(const AstRawString* label) {
for (ParserTarget* t = target_stack_; t != nullptr; t = t->previous()) {
if (ContainsLabel(t->statement()->labels(), label)) return true;
}
return false;
}
BreakableStatement* Parser::LookupBreakTarget(const AstRawString* label) {
bool anonymous = label == nullptr;
for (ParserTarget* t = target_stack_; t != nullptr; t = t->previous()) {
BreakableStatement* stat = t->statement();
if ((anonymous && stat->is_target_for_anonymous()) ||
(!anonymous && ContainsLabel(stat->labels(), label))) {
return stat;
}
}
return nullptr;
}
IterationStatement* Parser::LookupContinueTarget(const AstRawString* label) {
bool anonymous = label == nullptr;
for (ParserTarget* t = target_stack_; t != nullptr; t = t->previous()) {
IterationStatement* stat = t->statement()->AsIterationStatement();
if (stat == nullptr) continue;
DCHECK(stat->is_target_for_anonymous());
if (anonymous || ContainsLabel(stat->own_labels(), label)) {
return stat;
}
if (ContainsLabel(stat->labels(), label)) break;
}
return nullptr;
}
void Parser::HandleSourceURLComments(Isolate* isolate, Handle<Script> script) {
Handle<String> source_url = scanner_.SourceUrl(isolate);
if (!source_url.is_null()) {

View File

@ -122,8 +122,6 @@ struct ParserTypes<Parser> {
using FuncNameInferrer = v8::internal::FuncNameInferrer;
using SourceRange = v8::internal::SourceRange;
using SourceRangeScope = v8::internal::SourceRangeScope;
using Target = ParserTarget;
using TargetScope = ParserTargetScope;
};
class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
@ -279,11 +277,6 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
};
ZonePtrList<const NamedImport>* ParseNamedImports(int pos);
Statement* BuildInitializationBlock(DeclarationParsingResult* parsing_result);
void DeclareLabel(ZonePtrList<const AstRawString>** labels,
ZonePtrList<const AstRawString>** own_labels,
const AstRawString* label);
bool ContainsLabel(ZonePtrList<const AstRawString>* labels,
const AstRawString* label);
Expression* RewriteReturn(Expression* return_value, int pos);
Statement* RewriteSwitchStatement(SwitchStatement* switch_statement,
Scope* scope);
@ -407,10 +400,6 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
Scope* declaration_scope, bool* was_added, int var_begin_pos,
int var_end_pos = kNoSourcePosition);
bool TargetStackContainsLabel(const AstRawString* label);
BreakableStatement* LookupBreakTarget(const AstRawString* label);
IterationStatement* LookupContinueTarget(const AstRawString* label);
// Factory methods.
FunctionLiteral* DefaultConstructor(const AstRawString* name, bool call_super,
int pos, int end_pos);
@ -727,6 +716,10 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
return arg;
}
IterationStatement* AsIterationStatement(BreakableStatement* s) {
return s->AsIterationStatement();
}
void ReportUnexpectedTokenAt(
Scanner::Location location, Token::Value token,
MessageTemplate message = MessageTemplate::kUnexpectedToken);
@ -750,6 +743,10 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
return subject == nullptr;
}
V8_INLINE static bool IsIterationStatement(Statement* subject) {
return subject->AsIterationStatement() != nullptr;
}
// Non-null empty string.
V8_INLINE const AstRawString* EmptyIdentifierString() const {
return ast_value_factory()->empty_string();
@ -1047,9 +1044,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
SourceRangeMap* source_range_map_ = nullptr;
friend class ParserTarget;
friend class ParserTargetScope;
ParserTarget* target_stack_; // for break, continue statements
ScriptCompiler::CompileOptions compile_options_;
@ -1072,47 +1067,6 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
int parameters_end_pos_;
};
// ----------------------------------------------------------------------------
// Target is a support class to facilitate manipulation of the
// Parser's target_stack_ (the stack of potential 'break' and
// 'continue' statement targets). Upon construction, a new target is
// added; it is removed upon destruction.
class ParserTarget {
public:
ParserTarget(ParserBase<Parser>* parser, BreakableStatement* statement)
: variable_(&parser->impl()->target_stack_),
statement_(statement),
previous_(parser->impl()->target_stack_) {
parser->impl()->target_stack_ = this;
}
~ParserTarget() { *variable_ = previous_; }
ParserTarget* previous() { return previous_; }
BreakableStatement* statement() { return statement_; }
private:
ParserTarget** variable_;
BreakableStatement* statement_;
ParserTarget* previous_;
};
class ParserTargetScope {
public:
explicit ParserTargetScope(ParserBase<Parser>* parser)
: variable_(&parser->impl()->target_stack_),
previous_(parser->impl()->target_stack_) {
parser->impl()->target_stack_ = nullptr;
}
~ParserTargetScope() { *variable_ = previous_; }
private:
ParserTarget** variable_;
ParserTarget* previous_;
};
} // namespace internal
} // namespace v8

View File

@ -416,6 +416,10 @@ class PreParserStatement {
return PreParserStatement(kUnknownStatement);
}
static PreParserStatement Iteration() {
return PreParserStatement(kIterationStatement);
}
static PreParserStatement Null() {
return PreParserStatement(kNullStatement);
}
@ -450,6 +454,8 @@ class PreParserStatement {
bool IsNull() { return code_ == kNullStatement; }
bool IsIterationStatement() { return code_ == kIterationStatement; }
bool IsEmptyStatement() {
DCHECK(!IsNull());
return code_ == kEmptyStatement;
@ -478,6 +484,7 @@ class PreParserStatement {
kEmptyStatement,
kUnknownStatement,
kJumpStatement,
kIterationStatement,
kStringLiteralExpressionStatement,
};
@ -691,8 +698,7 @@ class PreParserFactory {
return PreParserBlock::Default();
}
PreParserBlock NewBlock(bool ignore_completion_value,
ZonePtrList<const AstRawString>* labels) {
PreParserBlock NewBlock(bool ignore_completion_value, bool is_breakable) {
return PreParserBlock::Default();
}
@ -737,20 +743,15 @@ class PreParserFactory {
return PreParserStatement::Default();
}
PreParserStatement NewDoWhileStatement(
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos) {
return PreParserStatement::Default();
PreParserStatement NewDoWhileStatement(int pos) {
return PreParserStatement::Iteration();
}
PreParserStatement NewWhileStatement(
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos) {
return PreParserStatement::Default();
PreParserStatement NewWhileStatement(int pos) {
return PreParserStatement::Iteration();
}
PreParserStatement NewSwitchStatement(ZonePtrList<const AstRawString>* labels,
const PreParserExpression& tag,
PreParserStatement NewSwitchStatement(const PreParserExpression& tag,
int pos) {
return PreParserStatement::Default();
}
@ -761,23 +762,17 @@ class PreParserFactory {
return PreParserStatement::Default();
}
PreParserStatement NewForStatement(
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos) {
return PreParserStatement::Default();
PreParserStatement NewForStatement(int pos) {
return PreParserStatement::Iteration();
}
PreParserStatement NewForEachStatement(
ForEachStatement::VisitMode visit_mode,
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos) {
return PreParserStatement::Default();
PreParserStatement NewForEachStatement(ForEachStatement::VisitMode visit_mode,
int pos) {
return PreParserStatement::Iteration();
}
PreParserStatement NewForOfStatement(
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, int pos, IteratorType type) {
return PreParserStatement::Default();
PreParserStatement NewForOfStatement(int pos, IteratorType type) {
return PreParserStatement::Iteration();
}
PreParserExpression NewCallRuntime(
@ -819,17 +814,6 @@ class PreParserFormalParameters : public FormalParametersBase {
bool strict_parameter_error_ = false;
};
class PreParserTarget {
public:
PreParserTarget(ParserBase<PreParser>* preparser,
PreParserStatement statement) {}
};
class PreParserTargetScope {
public:
explicit PreParserTargetScope(ParserBase<PreParser>* preparser) {}
};
class PreParserFuncNameInferrer {
public:
explicit PreParserFuncNameInferrer(AstValueFactory* avf) {}
@ -904,8 +888,6 @@ struct ParserTypes<PreParser> {
using FuncNameInferrer = PreParserFuncNameInferrer;
using SourceRange = PreParserSourceRange;
using SourceRangeScope = PreParserSourceRangeScope;
using Target = PreParserTarget;
using TargetScope = PreParserTargetScope;
};
@ -1070,18 +1052,6 @@ class PreParser : public ParserBase<PreParser> {
const PreParserScopedStatementList* body, PreParserStatement block,
const PreParserExpression& return_value) {}
V8_INLINE void DeclareLabel(ZonePtrList<const AstRawString>** labels,
ZonePtrList<const AstRawString>** own_labels,
const AstRawString* label) {
DCHECK(!parsing_module_ || !label->IsOneByteEqualTo("await"));
}
// TODO(nikolaos): The preparser currently does not keep track of labels.
V8_INLINE bool ContainsLabel(ZonePtrList<const AstRawString>* labels,
const PreParserIdentifier& label) {
return false;
}
V8_INLINE PreParserExpression
RewriteReturn(const PreParserExpression& return_value, int pos) {
return return_value;
@ -1186,17 +1156,6 @@ class PreParser : public ParserBase<PreParser> {
bool IdentifierEquals(const PreParserIdentifier& identifier,
const AstRawString* other);
// TODO(nikolaos): The preparser currently does not keep track of labels
// and targets.
V8_INLINE PreParserStatement
LookupBreakTarget(const PreParserIdentifier& label) {
return PreParserStatement::Default();
}
V8_INLINE PreParserStatement
LookupContinueTarget(const PreParserIdentifier& label) {
return PreParserStatement::Default();
}
V8_INLINE PreParserStatement DeclareFunction(
const PreParserIdentifier& variable_name,
const PreParserExpression& function, VariableMode mode, VariableKind kind,
@ -1498,7 +1457,7 @@ class PreParser : public ParserBase<PreParser> {
V8_INLINE void ReportMessageAt(Scanner::Location source_location,
MessageTemplate message,
const PreParserIdentifier& arg) {
UNREACHABLE();
ReportMessageAt(source_location, message, arg.string_);
}
void ReportMessageAt(Scanner::Location source_location,
@ -1512,6 +1471,8 @@ class PreParser : public ParserBase<PreParser> {
return arg.string_;
}
PreParserStatement AsIterationStatement(PreParserStatement s) { return s; }
// "null" return type creators.
V8_INLINE static PreParserIdentifier NullIdentifier() {
return PreParserIdentifier::Null();
@ -1538,6 +1499,10 @@ class PreParser : public ParserBase<PreParser> {
return subject.IsNull();
}
V8_INLINE static bool IsIterationStatement(PreParserStatement subject) {
return subject.IsIterationStatement();
}
V8_INLINE PreParserIdentifier EmptyIdentifierString() const {
PreParserIdentifier result = PreParserIdentifier::Default();
result.string_ = ast_value_factory()->empty_string();

View File

@ -146,7 +146,7 @@ void Processor::VisitBlock(Block* node) {
// returns 'undefined'. To obtain the same behavior with v8, we need
// to prevent rewriting in that case.
if (!node->ignore_completion_value()) {
BreakableScope scope(this, node->labels() != nullptr);
BreakableScope scope(this, node->is_breakable());
Process(node->statements());
}
replacement_ = node;

View File

@ -3258,25 +3258,6 @@ TEST(FuncNameInferrerEscaped) {
}
TEST(RegressionLazyFunctionWithErrorWithArg) {
// Test only applies when lazy parsing.
if (!i::FLAG_lazy) return;
// The bug occurred when a lazy function had an error which requires a
// parameter (such as "unknown label" here). The error message was processed
// before the AstValueFactory containing the error message string was
// internalized.
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
LocalContext env;
i::FLAG_lazy = true;
CompileRun("function this_is_lazy() {\n"
" break p;\n"
"}\n"
"this_is_lazy();\n");
}
TEST(SerializationOfMaybeAssignmentFlag) {
i::Isolate* isolate = CcTest::i_isolate();
i::Factory* factory = isolate->factory();

View File

@ -1,32 +1,14 @@
getPossibleBreakpoints should not crash during lazy compilation (crbug.com/715334)
{
method : Debugger.scriptParsed
params : {
endColumn : 21
endLine : 2
executionContextId : <executionContextId>
hasSourceURL : true
hash : 124cb0278e3aa9f250651d433cdefeb5618c7202
isLiveEdit : false
isModule : false
length : 52
scriptId : <scriptId>
sourceMapURL :
startColumn : 0
startLine : 0
url : test.js
}
}
{
method : Debugger.scriptFailedToParse
params : {
endColumn : 21
endColumn : 23
endLine : 2
executionContextId : <executionContextId>
hasSourceURL : true
hash : 124cb0278e3aa9f250651d433cdefeb5618c7202
hash : 1bce5d0c4da4d13a3ea6e6f35ea0f34705c26ba4
isModule : false
length : 52
length : 56
scriptId : <scriptId>
sourceMapURL :
startColumn : 0
@ -34,7 +16,6 @@ getPossibleBreakpoints should not crash during lazy compilation (crbug.com/71533
url : test.js
}
}
One script is reported twice
{
id : <messageId>
result : {

View File

@ -3,26 +3,16 @@
// found in the LICENSE file.
let {session, contextGroup, Protocol} = InspectorTest.start('getPossibleBreakpoints should not crash during lazy compilation (crbug.com/715334)');
contextGroup.addScript(`
function test() { continue; }
//# sourceURL=test.js`);
(async function test() {
Protocol.Debugger.enable();
let script = await Protocol.Debugger.onceScriptParsed();
InspectorTest.logMessage(script);
let scriptId = script.params.scriptId;
Protocol.Debugger.onScriptFailedToParse(msg => {
InspectorTest.logMessage(msg);
if (msg.params.scriptId !== script.params.scriptId) {
InspectorTest.log('Failed script to parse event has different scriptId');
} else {
InspectorTest.log('One script is reported twice');
}
});
let response = await Protocol.Debugger.getPossibleBreakpoints({
start: {scriptId, lineNumber: 0, columnNumber: 0}});
InspectorTest.logMessage(response);
InspectorTest.completeTest();
(async function test() {
Protocol.Debugger.enable();
Protocol.Debugger.onScriptFailedToParse(async msg => {
InspectorTest.logMessage(msg);
const response = await Protocol.Debugger.getPossibleBreakpoints({
start: {scriptId: msg.params.scriptId, lineNumber: 0, columnNumber: 0}});
InspectorTest.logMessage(response);
InspectorTest.completeTest();
});
contextGroup.addScript(`
function test() { continue; }
//# sourceURL=test.js`);
})();

View File

@ -7,10 +7,7 @@
function outer() {
"use asm";
function inner() {
switch (1) {
case 0:
break foo;
}
/f(/
}
}
outer();

View File

@ -0,0 +1,8 @@
// Copyright 2020 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("function f() { break }", SyntaxError);
assertThrows("function f() { break a }", SyntaxError);
assertThrows("function f() { continue }", SyntaxError);
assertThrows("function f() { continue a }", SyntaxError);