Commit 480b182f authored by jgruber's avatar jgruber Committed by Commit Bot

[coverage] Support switch statements

Switch statements generate a counter for each clause plus a continuation
counter.

Bug: v8:6000
Change-Id: Ic55a7efda54de1152bd5283d753119aa2764afbd
Reviewed-on: https://chromium-review.googlesource.com/558249Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46550}
parent 3ca64085
...@@ -1068,8 +1068,11 @@ Call::CallType Call::GetCallType() const { ...@@ -1068,8 +1068,11 @@ Call::CallType Call::GetCallType() const {
} }
CaseClause::CaseClause(Expression* label, ZoneList<Statement*>* statements, CaseClause::CaseClause(Expression* label, ZoneList<Statement*>* statements,
int pos) int pos, const SourceRange& clause_range)
: Expression(pos, kCaseClause), label_(label), statements_(statements) {} : Expression(pos, kCaseClause),
label_(label),
statements_(statements),
clause_range_(clause_range) {}
void CaseClause::AssignFeedbackSlots(FeedbackVectorSpec* spec, void CaseClause::AssignFeedbackSlots(FeedbackVectorSpec* spec,
LanguageMode language_mode, LanguageMode language_mode,
......
...@@ -188,9 +188,12 @@ struct SourceRange { ...@@ -188,9 +188,12 @@ struct SourceRange {
SourceRange() : SourceRange(kNoSourcePosition, kNoSourcePosition) {} SourceRange() : SourceRange(kNoSourcePosition, kNoSourcePosition) {}
SourceRange(int start, int end) : start(start), end(end) {} SourceRange(int start, int end) : start(start), end(end) {}
bool IsEmpty() const { return start == kNoSourcePosition; } bool IsEmpty() const { return start == kNoSourcePosition; }
static SourceRange Empty() { return SourceRange(); }
static SourceRange OpenEnded(int32_t start) {
return SourceRange(start, kNoSourcePosition);
}
static SourceRange ContinuationOf(const SourceRange& that) { static SourceRange ContinuationOf(const SourceRange& that) {
return that.IsEmpty() ? SourceRange() return that.IsEmpty() ? Empty() : OpenEnded(that.end);
: SourceRange(that.end, kNoSourcePosition);
} }
int32_t start, end; int32_t start, end;
}; };
...@@ -500,6 +503,8 @@ class IterationStatement : public BreakableStatement { ...@@ -500,6 +503,8 @@ class IterationStatement : public BreakableStatement {
void set_body(Statement* s) { body_ = s; } void set_body(Statement* s) { body_ = s; }
SourceRange body_range() const { return body_range_; } SourceRange body_range() const { return body_range_; }
// The range starting after the iteration body, used for block coverage.
SourceRange continuation_range() const { SourceRange continuation_range() const {
return SourceRange::ContinuationOf(body_range_); return SourceRange::ContinuationOf(body_range_);
} }
...@@ -771,7 +776,10 @@ class JumpStatement : public Statement { ...@@ -771,7 +776,10 @@ class JumpStatement : public Statement {
public: public:
bool IsJump() const { return true; } bool IsJump() const { return true; }
int32_t continuation_pos() const { return continuation_pos_; } // The range starting after the jump statement, used for block coverage.
SourceRange continuation_range() const {
return SourceRange::OpenEnded(continuation_pos_);
}
protected: protected:
JumpStatement(int pos, NodeType type, int32_t continuation_pos) JumpStatement(int pos, NodeType type, int32_t continuation_pos)
...@@ -872,6 +880,8 @@ class CaseClause final : public Expression { ...@@ -872,6 +880,8 @@ class CaseClause final : public Expression {
Label* body_target() { return &body_target_; } Label* body_target() { return &body_target_; }
ZoneList<Statement*>* statements() const { return statements_; } ZoneList<Statement*>* statements() const { return statements_; }
SourceRange clause_range() const { return clause_range_; }
void AssignFeedbackSlots(FeedbackVectorSpec* spec, LanguageMode language_mode, void AssignFeedbackSlots(FeedbackVectorSpec* spec, LanguageMode language_mode,
FeedbackSlotCache* cache); FeedbackSlotCache* cache);
...@@ -880,20 +890,24 @@ class CaseClause final : public Expression { ...@@ -880,20 +890,24 @@ class CaseClause final : public Expression {
private: private:
friend class AstNodeFactory; friend class AstNodeFactory;
CaseClause(Expression* label, ZoneList<Statement*>* statements, int pos); CaseClause(Expression* label, ZoneList<Statement*>* statements, int pos,
const SourceRange& clause_range);
FeedbackSlot feedback_slot_; FeedbackSlot feedback_slot_;
Expression* label_; Expression* label_;
Label body_target_; Label body_target_;
ZoneList<Statement*>* statements_; ZoneList<Statement*>* statements_;
SourceRange clause_range_;
}; };
class SwitchStatement final : public BreakableStatement { class SwitchStatement final : public BreakableStatement {
public: public:
void Initialize(Expression* tag, ZoneList<CaseClause*>* cases) { void Initialize(Expression* tag, ZoneList<CaseClause*>* cases,
int32_t continuation_pos) {
tag_ = tag; tag_ = tag;
cases_ = cases; cases_ = cases;
continuation_pos_ = continuation_pos;
} }
Expression* tag() const { return tag_; } Expression* tag() const { return tag_; }
...@@ -901,16 +915,23 @@ class SwitchStatement final : public BreakableStatement { ...@@ -901,16 +915,23 @@ class SwitchStatement final : public BreakableStatement {
void set_tag(Expression* t) { tag_ = t; } void set_tag(Expression* t) { tag_ = t; }
// The range starting after the switch body, used for block coverage.
SourceRange continuation_range() const {
return SourceRange::OpenEnded(continuation_pos_);
}
private: private:
friend class AstNodeFactory; friend class AstNodeFactory;
SwitchStatement(ZoneList<const AstRawString*>* labels, int pos) SwitchStatement(ZoneList<const AstRawString*>* labels, int pos)
: BreakableStatement(labels, TARGET_FOR_ANONYMOUS, pos, kSwitchStatement), : BreakableStatement(labels, TARGET_FOR_ANONYMOUS, pos, kSwitchStatement),
tag_(NULL), tag_(NULL),
cases_(NULL) {} cases_(NULL),
continuation_pos_(kNoSourcePosition) {}
Expression* tag_; Expression* tag_;
ZoneList<CaseClause*>* cases_; ZoneList<CaseClause*>* cases_;
int32_t continuation_pos_;
}; };
...@@ -930,6 +951,8 @@ class IfStatement final : public Statement { ...@@ -930,6 +951,8 @@ class IfStatement final : public Statement {
SourceRange then_range() const { return then_range_; } SourceRange then_range() const { return then_range_; }
SourceRange else_range() const { return else_range_; } SourceRange else_range() const { return else_range_; }
// The range starting after the if body, used for block coverage.
SourceRange continuation_range() const { SourceRange continuation_range() const {
SourceRange trailing_range = SourceRange trailing_range =
HasElseStatement() ? else_range() : then_range(); HasElseStatement() ? else_range() : then_range();
...@@ -2431,9 +2454,11 @@ class Throw final : public Expression { ...@@ -2431,9 +2454,11 @@ class Throw final : public Expression {
public: public:
Expression* exception() const { return exception_; } Expression* exception() const { return exception_; }
void set_exception(Expression* e) { exception_ = e; } void set_exception(Expression* e) { exception_ = e; }
// The first source position past the end of the throw statement, used by
// block coverage. // The range starting after the throw statement, used for block coverage.
int32_t continuation_pos() const { return continuation_pos_; } SourceRange continuation_range() const {
return SourceRange::OpenEnded(continuation_pos_);
}
private: private:
friend class AstNodeFactory; friend class AstNodeFactory;
...@@ -3272,9 +3297,9 @@ class AstNodeFactory final BASE_EMBEDDED { ...@@ -3272,9 +3297,9 @@ class AstNodeFactory final BASE_EMBEDDED {
SloppyBlockFunctionStatement(NewEmptyStatement(kNoSourcePosition)); SloppyBlockFunctionStatement(NewEmptyStatement(kNoSourcePosition));
} }
CaseClause* NewCaseClause( CaseClause* NewCaseClause(Expression* label, ZoneList<Statement*>* statements,
Expression* label, ZoneList<Statement*>* statements, int pos) { int pos, const SourceRange& clause_range = {}) {
return new (zone_) CaseClause(label, statements, pos); return new (zone_) CaseClause(label, statements, pos, clause_range);
} }
Literal* NewStringLiteral(const AstRawString* string, int pos) { Literal* NewStringLiteral(const AstRawString* string, int pos) {
......
...@@ -1275,22 +1275,19 @@ void BytecodeGenerator::VisitSloppyBlockFunctionStatement( ...@@ -1275,22 +1275,19 @@ void BytecodeGenerator::VisitSloppyBlockFunctionStatement(
} }
void BytecodeGenerator::VisitContinueStatement(ContinueStatement* stmt) { void BytecodeGenerator::VisitContinueStatement(ContinueStatement* stmt) {
AllocateBlockCoverageSlotIfEnabled( AllocateBlockCoverageSlotIfEnabled(stmt->continuation_range());
{stmt->continuation_pos(), kNoSourcePosition});
builder()->SetStatementPosition(stmt); builder()->SetStatementPosition(stmt);
execution_control()->Continue(stmt->target()); execution_control()->Continue(stmt->target());
} }
void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) { void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
AllocateBlockCoverageSlotIfEnabled( AllocateBlockCoverageSlotIfEnabled(stmt->continuation_range());
{stmt->continuation_pos(), kNoSourcePosition});
builder()->SetStatementPosition(stmt); builder()->SetStatementPosition(stmt);
execution_control()->Break(stmt->target()); execution_control()->Break(stmt->target());
} }
void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) { void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
AllocateBlockCoverageSlotIfEnabled( AllocateBlockCoverageSlotIfEnabled(stmt->continuation_range());
{stmt->continuation_pos(), kNoSourcePosition});
builder()->SetStatementPosition(stmt); builder()->SetStatementPosition(stmt);
VisitForAccumulatorValue(stmt->expression()); VisitForAccumulatorValue(stmt->expression());
if (stmt->is_async_return()) { if (stmt->is_async_return()) {
...@@ -1351,9 +1348,11 @@ void BytecodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { ...@@ -1351,9 +1348,11 @@ void BytecodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
for (int i = 0; i < clauses->length(); i++) { for (int i = 0; i < clauses->length(); i++) {
CaseClause* clause = clauses->at(i); CaseClause* clause = clauses->at(i);
switch_builder.SetCaseTarget(i); switch_builder.SetCaseTarget(i);
BuildIncrementBlockCoverageCounterIfEnabled(clause->clause_range());
VisitStatements(clause->statements()); VisitStatements(clause->statements());
} }
switch_builder.BindBreakTarget(); switch_builder.BindBreakTarget();
BuildIncrementBlockCoverageCounterIfEnabled(stmt->continuation_range());
} }
void BytecodeGenerator::VisitCaseClause(CaseClause* clause) { void BytecodeGenerator::VisitCaseClause(CaseClause* clause) {
...@@ -2952,8 +2951,7 @@ void BytecodeGenerator::VisitYieldStar(YieldStar* expr) { ...@@ -2952,8 +2951,7 @@ void BytecodeGenerator::VisitYieldStar(YieldStar* expr) {
} }
void BytecodeGenerator::VisitThrow(Throw* expr) { void BytecodeGenerator::VisitThrow(Throw* expr) {
AllocateBlockCoverageSlotIfEnabled( AllocateBlockCoverageSlotIfEnabled(expr->continuation_range());
{expr->continuation_pos(), kNoSourcePosition});
VisitForAccumulatorValue(expr->exception()); VisitForAccumulatorValue(expr->exception());
builder()->SetExpressionPosition(expr); builder()->SetExpressionPosition(expr);
builder()->Throw(); builder()->Throw();
......
...@@ -99,7 +99,11 @@ class SourceRangeScope final { ...@@ -99,7 +99,11 @@ class SourceRangeScope final {
DCHECK_NE(range_->start, kNoSourcePosition); DCHECK_NE(range_->start, kNoSourcePosition);
} }
~SourceRangeScope() { ~SourceRangeScope() { Finalize(); }
void Finalize() {
if (is_finalized_) return;
is_finalized_ = true;
range_->end = GetPosition(post_kind_); range_->end = GetPosition(post_kind_);
DCHECK_NE(range_->end, kNoSourcePosition); DCHECK_NE(range_->end, kNoSourcePosition);
} }
...@@ -123,6 +127,7 @@ class SourceRangeScope final { ...@@ -123,6 +127,7 @@ class SourceRangeScope final {
Scanner* scanner_; Scanner* scanner_;
SourceRange* range_; SourceRange* range_;
PositionKind post_kind_; PositionKind post_kind_;
bool is_finalized_ = false;
DISALLOW_IMPLICIT_CONSTRUCTORS(SourceRangeScope); DISALLOW_IMPLICIT_CONSTRUCTORS(SourceRangeScope);
}; };
...@@ -5466,6 +5471,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement( ...@@ -5466,6 +5471,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement(
while (peek() != Token::RBRACE) { while (peek() != Token::RBRACE) {
// An empty label indicates the default case. // An empty label indicates the default case.
ExpressionT label = impl()->EmptyExpression(); ExpressionT label = impl()->EmptyExpression();
SourceRange clause_range;
SourceRangeScope range_scope(scanner(), &clause_range);
if (Check(Token::CASE)) { if (Check(Token::CASE)) {
label = ParseExpression(true, CHECK_OK); label = ParseExpression(true, CHECK_OK);
} else { } else {
...@@ -5485,14 +5492,19 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement( ...@@ -5485,14 +5492,19 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement(
StatementT stat = ParseStatementListItem(CHECK_OK); StatementT stat = ParseStatementListItem(CHECK_OK);
statements->Add(stat, zone()); statements->Add(stat, zone());
} }
auto clause = factory()->NewCaseClause(label, statements, clause_pos); range_scope.Finalize();
auto clause =
factory()->NewCaseClause(label, statements, clause_pos, clause_range);
cases->Add(clause, zone()); cases->Add(clause, zone());
} }
Expect(Token::RBRACE, CHECK_OK); Expect(Token::RBRACE, CHECK_OK);
scope()->set_end_position(scanner()->location().end_pos); int end_position = scanner()->location().end_pos;
scope()->set_end_position(end_position);
int continuation_pos = end_position;
return impl()->RewriteSwitchStatement(tag, switch_statement, cases, return impl()->RewriteSwitchStatement(tag, switch_statement, cases,
scope()->FinalizeBlockScope()); scope()->FinalizeBlockScope(),
continuation_pos);
} }
} }
......
...@@ -1641,7 +1641,8 @@ Expression* Parser::RewriteDoExpression(Block* body, int pos, bool* ok) { ...@@ -1641,7 +1641,8 @@ Expression* Parser::RewriteDoExpression(Block* body, int pos, bool* ok) {
Statement* Parser::RewriteSwitchStatement(Expression* tag, Statement* Parser::RewriteSwitchStatement(Expression* tag,
SwitchStatement* switch_statement, SwitchStatement* switch_statement,
ZoneList<CaseClause*>* cases, ZoneList<CaseClause*>* cases,
Scope* scope) { Scope* scope,
int32_t continuation_pos) {
// In order to get the CaseClauses to execute in their own lexical scope, // In order to get the CaseClauses to execute in their own lexical scope,
// but without requiring downstream code to have special scope handling // but without requiring downstream code to have special scope handling
// code for switch statements, desugar into blocks as follows: // code for switch statements, desugar into blocks as follows:
...@@ -1672,7 +1673,7 @@ Statement* Parser::RewriteSwitchStatement(Expression* tag, ...@@ -1672,7 +1673,7 @@ Statement* Parser::RewriteSwitchStatement(Expression* tag,
zone()); zone());
Expression* tag_read = factory()->NewVariableProxy(tag_variable); Expression* tag_read = factory()->NewVariableProxy(tag_variable);
switch_statement->Initialize(tag_read, cases); switch_statement->Initialize(tag_read, cases, continuation_pos);
Block* cases_block = factory()->NewBlock(NULL, 1, false, kNoSourcePosition); Block* cases_block = factory()->NewBlock(NULL, 1, false, kNoSourcePosition);
cases_block->statements()->Add(switch_statement, zone()); cases_block->statements()->Add(switch_statement, zone());
cases_block->set_scope(scope); cases_block->set_scope(scope);
...@@ -4615,7 +4616,8 @@ Expression* Parser::RewriteYieldStar(Expression* iterable, int pos) { ...@@ -4615,7 +4616,8 @@ Expression* Parser::RewriteYieldStar(Expression* iterable, int pos) {
cases->Add(factory()->NewCaseClause(kreturn, case_return, nopos), zone()); cases->Add(factory()->NewCaseClause(kreturn, case_return, nopos), zone());
cases->Add(factory()->NewCaseClause(kthrow, case_throw, nopos), zone()); cases->Add(factory()->NewCaseClause(kthrow, case_throw, nopos), zone());
switch_mode->Initialize(factory()->NewVariableProxy(var_mode), cases); switch_mode->Initialize(factory()->NewVariableProxy(var_mode), cases,
kNoSourcePosition);
} }
// while (true) { ... } // while (true) { ... }
......
...@@ -340,7 +340,8 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { ...@@ -340,7 +340,8 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
Expression* RewriteReturn(Expression* return_value, int pos); Expression* RewriteReturn(Expression* return_value, int pos);
Statement* RewriteSwitchStatement(Expression* tag, Statement* RewriteSwitchStatement(Expression* tag,
SwitchStatement* switch_statement, SwitchStatement* switch_statement,
ZoneList<CaseClause*>* cases, Scope* scope); ZoneList<CaseClause*>* cases, Scope* scope,
int32_t continuation_pos);
void RewriteCatchPattern(CatchInfo* catch_info, bool* ok); void RewriteCatchPattern(CatchInfo* catch_info, bool* ok);
void ValidateCatchBlock(const CatchInfo& catch_info, bool* ok); void ValidateCatchBlock(const CatchInfo& catch_info, bool* ok);
Statement* RewriteTryStatement(Block* try_block, Block* catch_block, Statement* RewriteTryStatement(Block* try_block, Block* catch_block,
......
...@@ -769,7 +769,8 @@ class PreParserFactory { ...@@ -769,7 +769,8 @@ class PreParserFactory {
} }
PreParserStatement NewCaseClause(PreParserExpression label, PreParserStatement NewCaseClause(PreParserExpression label,
PreParserStatementList statements, int pos) { PreParserStatementList statements, int pos,
const SourceRange& clause_range) {
return PreParserStatement::Default(); return PreParserStatement::Default();
} }
...@@ -1067,7 +1068,7 @@ class PreParser : public ParserBase<PreParser> { ...@@ -1067,7 +1068,7 @@ class PreParser : public ParserBase<PreParser> {
} }
V8_INLINE PreParserStatement RewriteSwitchStatement( V8_INLINE PreParserStatement RewriteSwitchStatement(
PreParserExpression tag, PreParserStatement switch_statement, PreParserExpression tag, PreParserStatement switch_statement,
PreParserStatementList cases, Scope* scope) { PreParserStatementList cases, Scope* scope, int32_t continuation_pos) {
return PreParserStatement::Default(); return PreParserStatement::Default();
} }
......
...@@ -342,4 +342,22 @@ TestCoverage( ...@@ -342,4 +342,22 @@ TestCoverage(
{"start":513,"end":564,"count":0}] {"start":513,"end":564,"count":0}]
); );
TestCoverage(
"switch statements",
`
!function() { // 0000
var x = 42; // 0050
switch (x) { // 0100
case 41: nop(); break; // 0150
case 42: nop(); break; // 0200
default: nop(); break; // 0250
} // 0300
}(); // 0350
`,
[{"start":0,"end":399,"count":1},
{"start":1,"end":351,"count":1},
{"start":154,"end":204,"count":0},
{"start":226,"end":303,"count":0}]
);
%DebugToggleBlockCoverage(false); %DebugToggleBlockCoverage(false);
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