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 {
}
CaseClause::CaseClause(Expression* label, ZoneList<Statement*>* statements,
int pos)
: Expression(pos, kCaseClause), label_(label), statements_(statements) {}
int pos, const SourceRange& clause_range)
: Expression(pos, kCaseClause),
label_(label),
statements_(statements),
clause_range_(clause_range) {}
void CaseClause::AssignFeedbackSlots(FeedbackVectorSpec* spec,
LanguageMode language_mode,
......
......@@ -188,9 +188,12 @@ struct SourceRange {
SourceRange() : SourceRange(kNoSourcePosition, kNoSourcePosition) {}
SourceRange(int start, int end) : start(start), end(end) {}
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) {
return that.IsEmpty() ? SourceRange()
: SourceRange(that.end, kNoSourcePosition);
return that.IsEmpty() ? Empty() : OpenEnded(that.end);
}
int32_t start, end;
};
......@@ -500,6 +503,8 @@ class IterationStatement : public BreakableStatement {
void set_body(Statement* s) { body_ = s; }
SourceRange body_range() const { return body_range_; }
// The range starting after the iteration body, used for block coverage.
SourceRange continuation_range() const {
return SourceRange::ContinuationOf(body_range_);
}
......@@ -771,7 +776,10 @@ class JumpStatement : public Statement {
public:
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:
JumpStatement(int pos, NodeType type, int32_t continuation_pos)
......@@ -872,6 +880,8 @@ class CaseClause final : public Expression {
Label* body_target() { return &body_target_; }
ZoneList<Statement*>* statements() const { return statements_; }
SourceRange clause_range() const { return clause_range_; }
void AssignFeedbackSlots(FeedbackVectorSpec* spec, LanguageMode language_mode,
FeedbackSlotCache* cache);
......@@ -880,20 +890,24 @@ class CaseClause final : public Expression {
private:
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_;
Expression* label_;
Label body_target_;
ZoneList<Statement*>* statements_;
SourceRange clause_range_;
};
class SwitchStatement final : public BreakableStatement {
public:
void Initialize(Expression* tag, ZoneList<CaseClause*>* cases) {
void Initialize(Expression* tag, ZoneList<CaseClause*>* cases,
int32_t continuation_pos) {
tag_ = tag;
cases_ = cases;
continuation_pos_ = continuation_pos;
}
Expression* tag() const { return tag_; }
......@@ -901,16 +915,23 @@ class SwitchStatement final : public BreakableStatement {
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:
friend class AstNodeFactory;
SwitchStatement(ZoneList<const AstRawString*>* labels, int pos)
: BreakableStatement(labels, TARGET_FOR_ANONYMOUS, pos, kSwitchStatement),
tag_(NULL),
cases_(NULL) {}
cases_(NULL),
continuation_pos_(kNoSourcePosition) {}
Expression* tag_;
ZoneList<CaseClause*>* cases_;
int32_t continuation_pos_;
};
......@@ -930,6 +951,8 @@ class IfStatement final : public Statement {
SourceRange then_range() const { return then_range_; }
SourceRange else_range() const { return else_range_; }
// The range starting after the if body, used for block coverage.
SourceRange continuation_range() const {
SourceRange trailing_range =
HasElseStatement() ? else_range() : then_range();
......@@ -2431,9 +2454,11 @@ class Throw final : public Expression {
public:
Expression* exception() const { return exception_; }
void set_exception(Expression* e) { exception_ = e; }
// The first source position past the end of the throw statement, used by
// block coverage.
int32_t continuation_pos() const { return continuation_pos_; }
// The range starting after the throw statement, used for block coverage.
SourceRange continuation_range() const {
return SourceRange::OpenEnded(continuation_pos_);
}
private:
friend class AstNodeFactory;
......@@ -3272,9 +3297,9 @@ class AstNodeFactory final BASE_EMBEDDED {
SloppyBlockFunctionStatement(NewEmptyStatement(kNoSourcePosition));
}
CaseClause* NewCaseClause(
Expression* label, ZoneList<Statement*>* statements, int pos) {
return new (zone_) CaseClause(label, statements, pos);
CaseClause* NewCaseClause(Expression* label, ZoneList<Statement*>* statements,
int pos, const SourceRange& clause_range = {}) {
return new (zone_) CaseClause(label, statements, pos, clause_range);
}
Literal* NewStringLiteral(const AstRawString* string, int pos) {
......
......@@ -1275,22 +1275,19 @@ void BytecodeGenerator::VisitSloppyBlockFunctionStatement(
}
void BytecodeGenerator::VisitContinueStatement(ContinueStatement* stmt) {
AllocateBlockCoverageSlotIfEnabled(
{stmt->continuation_pos(), kNoSourcePosition});
AllocateBlockCoverageSlotIfEnabled(stmt->continuation_range());
builder()->SetStatementPosition(stmt);
execution_control()->Continue(stmt->target());
}
void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
AllocateBlockCoverageSlotIfEnabled(
{stmt->continuation_pos(), kNoSourcePosition});
AllocateBlockCoverageSlotIfEnabled(stmt->continuation_range());
builder()->SetStatementPosition(stmt);
execution_control()->Break(stmt->target());
}
void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
AllocateBlockCoverageSlotIfEnabled(
{stmt->continuation_pos(), kNoSourcePosition});
AllocateBlockCoverageSlotIfEnabled(stmt->continuation_range());
builder()->SetStatementPosition(stmt);
VisitForAccumulatorValue(stmt->expression());
if (stmt->is_async_return()) {
......@@ -1351,9 +1348,11 @@ void BytecodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
for (int i = 0; i < clauses->length(); i++) {
CaseClause* clause = clauses->at(i);
switch_builder.SetCaseTarget(i);
BuildIncrementBlockCoverageCounterIfEnabled(clause->clause_range());
VisitStatements(clause->statements());
}
switch_builder.BindBreakTarget();
BuildIncrementBlockCoverageCounterIfEnabled(stmt->continuation_range());
}
void BytecodeGenerator::VisitCaseClause(CaseClause* clause) {
......@@ -2952,8 +2951,7 @@ void BytecodeGenerator::VisitYieldStar(YieldStar* expr) {
}
void BytecodeGenerator::VisitThrow(Throw* expr) {
AllocateBlockCoverageSlotIfEnabled(
{expr->continuation_pos(), kNoSourcePosition});
AllocateBlockCoverageSlotIfEnabled(expr->continuation_range());
VisitForAccumulatorValue(expr->exception());
builder()->SetExpressionPosition(expr);
builder()->Throw();
......
......@@ -99,7 +99,11 @@ class SourceRangeScope final {
DCHECK_NE(range_->start, kNoSourcePosition);
}
~SourceRangeScope() {
~SourceRangeScope() { Finalize(); }
void Finalize() {
if (is_finalized_) return;
is_finalized_ = true;
range_->end = GetPosition(post_kind_);
DCHECK_NE(range_->end, kNoSourcePosition);
}
......@@ -123,6 +127,7 @@ class SourceRangeScope final {
Scanner* scanner_;
SourceRange* range_;
PositionKind post_kind_;
bool is_finalized_ = false;
DISALLOW_IMPLICIT_CONSTRUCTORS(SourceRangeScope);
};
......@@ -5466,6 +5471,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement(
while (peek() != Token::RBRACE) {
// An empty label indicates the default case.
ExpressionT label = impl()->EmptyExpression();
SourceRange clause_range;
SourceRangeScope range_scope(scanner(), &clause_range);
if (Check(Token::CASE)) {
label = ParseExpression(true, CHECK_OK);
} else {
......@@ -5485,14 +5492,19 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement(
StatementT stat = ParseStatementListItem(CHECK_OK);
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());
}
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,
scope()->FinalizeBlockScope());
scope()->FinalizeBlockScope(),
continuation_pos);
}
}
......
......@@ -1641,7 +1641,8 @@ Expression* Parser::RewriteDoExpression(Block* body, int pos, bool* ok) {
Statement* Parser::RewriteSwitchStatement(Expression* tag,
SwitchStatement* switch_statement,
ZoneList<CaseClause*>* cases,
Scope* scope) {
Scope* scope,
int32_t continuation_pos) {
// In order to get the CaseClauses to execute in their own lexical scope,
// but without requiring downstream code to have special scope handling
// code for switch statements, desugar into blocks as follows:
......@@ -1672,7 +1673,7 @@ Statement* Parser::RewriteSwitchStatement(Expression* tag,
zone());
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);
cases_block->statements()->Add(switch_statement, zone());
cases_block->set_scope(scope);
......@@ -4615,7 +4616,8 @@ Expression* Parser::RewriteYieldStar(Expression* iterable, int pos) {
cases->Add(factory()->NewCaseClause(kreturn, case_return, 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) { ... }
......
......@@ -340,7 +340,8 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
Expression* RewriteReturn(Expression* return_value, int pos);
Statement* RewriteSwitchStatement(Expression* tag,
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 ValidateCatchBlock(const CatchInfo& catch_info, bool* ok);
Statement* RewriteTryStatement(Block* try_block, Block* catch_block,
......
......@@ -769,7 +769,8 @@ class PreParserFactory {
}
PreParserStatement NewCaseClause(PreParserExpression label,
PreParserStatementList statements, int pos) {
PreParserStatementList statements, int pos,
const SourceRange& clause_range) {
return PreParserStatement::Default();
}
......@@ -1067,7 +1068,7 @@ class PreParser : public ParserBase<PreParser> {
}
V8_INLINE PreParserStatement RewriteSwitchStatement(
PreParserExpression tag, PreParserStatement switch_statement,
PreParserStatementList cases, Scope* scope) {
PreParserStatementList cases, Scope* scope, int32_t continuation_pos) {
return PreParserStatement::Default();
}
......
......@@ -342,4 +342,22 @@ TestCoverage(
{"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);
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