Commit 2941d76c authored by jgruber's avatar jgruber Committed by Commit Bot

[coverage] Support for labeled blocks and blocks containing jumps

Both labeled blocks:

l0: { break l0; }

and blocks containing jump statements (break, return, continue) require a
continuation counter to correctly display coverage.

Bug: v8:6000
Change-Id: I3ae8ddd3d9f6c087622482b86014dd583b774b71
Reviewed-on: https://chromium-review.googlesource.com/568024Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46644}
parent df38420f
...@@ -29,6 +29,7 @@ struct SourceRange { ...@@ -29,6 +29,7 @@ struct SourceRange {
// The list of ast node kinds that have associated source ranges. // The list of ast node kinds that have associated source ranges.
#define AST_SOURCE_RANGE_LIST(V) \ #define AST_SOURCE_RANGE_LIST(V) \
V(Block) \
V(CaseClause) \ V(CaseClause) \
V(IfStatement) \ V(IfStatement) \
V(IterationStatement) \ V(IterationStatement) \
...@@ -67,6 +68,12 @@ class ContinuationSourceRanges : public AstNodeSourceRanges { ...@@ -67,6 +68,12 @@ class ContinuationSourceRanges : public AstNodeSourceRanges {
int32_t continuation_position_; int32_t continuation_position_;
}; };
class BlockSourceRanges final : public ContinuationSourceRanges {
public:
explicit BlockSourceRanges(int32_t continuation_position)
: ContinuationSourceRanges(continuation_position) {}
};
class CaseClauseSourceRanges final : public AstNodeSourceRanges { class CaseClauseSourceRanges final : public AstNodeSourceRanges {
public: public:
explicit CaseClauseSourceRanges(const SourceRange& body_range) explicit CaseClauseSourceRanges(const SourceRange& body_range)
......
...@@ -46,6 +46,11 @@ class BlockCoverageBuilder final : public ZoneObject { ...@@ -46,6 +46,11 @@ class BlockCoverageBuilder final : public ZoneObject {
builder_->IncBlockCounter(coverage_array_slot); builder_->IncBlockCounter(coverage_array_slot);
} }
void IncrementBlockCounter(AstNode* node, SourceRangeKind kind) {
int slot = AllocateBlockCoverageSlot(node, kind);
IncrementBlockCounter(slot);
}
const ZoneVector<SourceRange>& slots() const { return slots_; } const ZoneVector<SourceRange>& slots() const { return slots_; }
private: private:
......
...@@ -330,6 +330,7 @@ class BytecodeGenerator::ControlScopeForBreakable final ...@@ -330,6 +330,7 @@ class BytecodeGenerator::ControlScopeForBreakable final
protected: protected:
bool Execute(Command command, Statement* statement) override { bool Execute(Command command, Statement* statement) override {
control_builder_->set_needs_continuation_counter();
if (statement != statement_) return false; if (statement != statement_) return false;
switch (command) { switch (command) {
case CMD_BREAK: case CMD_BREAK:
...@@ -1058,13 +1059,13 @@ void BytecodeGenerator::VisitBlock(Block* stmt) { ...@@ -1058,13 +1059,13 @@ void BytecodeGenerator::VisitBlock(Block* stmt) {
} }
void BytecodeGenerator::VisitBlockDeclarationsAndStatements(Block* stmt) { void BytecodeGenerator::VisitBlockDeclarationsAndStatements(Block* stmt) {
BlockBuilder block_builder(builder()); BlockBuilder block_builder(builder(), block_coverage_builder_, stmt);
ControlScopeForBreakable execution_control(this, stmt, &block_builder); ControlScopeForBreakable execution_control(this, stmt, &block_builder);
if (stmt->scope() != nullptr) { if (stmt->scope() != nullptr) {
VisitDeclarations(stmt->scope()->declarations()); VisitDeclarations(stmt->scope()->declarations());
} }
VisitStatements(stmt->statements()); VisitStatements(stmt->statements());
if (stmt->labels() != nullptr) block_builder.EndBlock(); block_builder.EndBlock();
} }
void BytecodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) { void BytecodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) {
...@@ -4167,9 +4168,7 @@ int BytecodeGenerator::AllocateBlockCoverageSlotIfEnabled( ...@@ -4167,9 +4168,7 @@ int BytecodeGenerator::AllocateBlockCoverageSlotIfEnabled(
void BytecodeGenerator::BuildIncrementBlockCoverageCounterIfEnabled( void BytecodeGenerator::BuildIncrementBlockCoverageCounterIfEnabled(
AstNode* node, SourceRangeKind kind) { AstNode* node, SourceRangeKind kind) {
if (block_coverage_builder_ == nullptr) return; if (block_coverage_builder_ == nullptr) return;
block_coverage_builder_->IncrementBlockCounter(node, kind);
int slot = block_coverage_builder_->AllocateBlockCoverageSlot(node, kind);
block_coverage_builder_->IncrementBlockCounter(slot);
} }
void BytecodeGenerator::BuildIncrementBlockCoverageCounterIfEnabled( void BytecodeGenerator::BuildIncrementBlockCoverageCounterIfEnabled(
......
...@@ -41,8 +41,14 @@ void BreakableControlFlowBuilder::EmitJumpIfNull(BytecodeLabels* sites) { ...@@ -41,8 +41,14 @@ void BreakableControlFlowBuilder::EmitJumpIfNull(BytecodeLabels* sites) {
} }
void BlockBuilder::EndBlock() { void BlockBuilder::EndBlock() {
builder()->Bind(&block_end_); if (statement_->labels() != nullptr) {
BindBreakTarget(); builder()->Bind(&block_end_);
BindBreakTarget();
}
if (block_coverage_builder_ != nullptr && needs_continuation_counter_) {
block_coverage_builder_->IncrementBlockCounter(
statement_, SourceRangeKind::kContinuation);
}
} }
LoopBuilder::~LoopBuilder() { LoopBuilder::~LoopBuilder() {
......
...@@ -57,6 +57,8 @@ class V8_EXPORT_PRIVATE BreakableControlFlowBuilder ...@@ -57,6 +57,8 @@ class V8_EXPORT_PRIVATE BreakableControlFlowBuilder
BytecodeLabels* break_labels() { return &break_labels_; } BytecodeLabels* break_labels() { return &break_labels_; }
void set_needs_continuation_counter() { needs_continuation_counter_ = true; }
protected: protected:
void EmitJump(BytecodeLabels* labels); void EmitJump(BytecodeLabels* labels);
void EmitJumpIfTrue(BytecodeArrayBuilder::ToBooleanMode mode, void EmitJumpIfTrue(BytecodeArrayBuilder::ToBooleanMode mode,
...@@ -68,6 +70,10 @@ class V8_EXPORT_PRIVATE BreakableControlFlowBuilder ...@@ -68,6 +70,10 @@ class V8_EXPORT_PRIVATE BreakableControlFlowBuilder
// Unbound labels that identify jumps for break statements in the code. // Unbound labels that identify jumps for break statements in the code.
BytecodeLabels break_labels_; BytecodeLabels break_labels_;
// A continuation counter (for block coverage) is needed e.g. when
// encountering a break statement.
bool needs_continuation_counter_ = false;
}; };
...@@ -75,13 +81,19 @@ class V8_EXPORT_PRIVATE BreakableControlFlowBuilder ...@@ -75,13 +81,19 @@ class V8_EXPORT_PRIVATE BreakableControlFlowBuilder
class V8_EXPORT_PRIVATE BlockBuilder final class V8_EXPORT_PRIVATE BlockBuilder final
: public BreakableControlFlowBuilder { : public BreakableControlFlowBuilder {
public: public:
explicit BlockBuilder(BytecodeArrayBuilder* builder) BlockBuilder(BytecodeArrayBuilder* builder,
: BreakableControlFlowBuilder(builder) {} BlockCoverageBuilder* block_coverage_builder,
BreakableStatement* statement)
: BreakableControlFlowBuilder(builder),
block_coverage_builder_(block_coverage_builder),
statement_(statement) {}
void EndBlock(); void EndBlock();
private: private:
BytecodeLabel block_end_; BytecodeLabel block_end_;
BlockCoverageBuilder* block_coverage_builder_;
BreakableStatement* statement_;
}; };
......
...@@ -5035,7 +5035,9 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseBlock( ...@@ -5035,7 +5035,9 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseBlock(
} }
Expect(Token::RBRACE, CHECK_OK_CUSTOM(NullBlock)); Expect(Token::RBRACE, CHECK_OK_CUSTOM(NullBlock));
scope()->set_end_position(scanner()->location().end_pos); int end_pos = scanner()->location().end_pos;
scope()->set_end_position(end_pos);
impl()->RecordBlockSourceRange(body, end_pos);
body->set_scope(scope()->FinalizeBlockScope()); body->set_scope(scope()->FinalizeBlockScope());
} }
return body; return body;
......
...@@ -1146,6 +1146,13 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { ...@@ -1146,6 +1146,13 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
return parameters_end_pos_ != kNoSourcePosition; return parameters_end_pos_ != kNoSourcePosition;
} }
V8_INLINE void RecordBlockSourceRange(Block* node,
int32_t continuation_position) {
if (source_range_map_ == nullptr) return;
source_range_map_->Insert(
node, new (zone()) BlockSourceRanges(continuation_position));
}
V8_INLINE void RecordCaseClauseSourceRange(CaseClause* node, V8_INLINE void RecordCaseClauseSourceRange(CaseClause* node,
const SourceRange& body_range) { const SourceRange& body_range) {
if (source_range_map_ == nullptr) return; if (source_range_map_ == nullptr) return;
......
...@@ -1743,6 +1743,8 @@ class PreParser : public ParserBase<PreParser> { ...@@ -1743,6 +1743,8 @@ class PreParser : public ParserBase<PreParser> {
V8_INLINE bool ParsingDynamicFunctionDeclaration() const { return false; } V8_INLINE bool ParsingDynamicFunctionDeclaration() const { return false; }
V8_INLINE void RecordBlockSourceRange(PreParserStatement node,
int32_t continuation_position) {}
V8_INLINE void RecordCaseClauseSourceRange(PreParserStatement node, V8_INLINE void RecordCaseClauseSourceRange(PreParserStatement node,
const SourceRange& body_range) {} const SourceRange& body_range) {}
V8_INLINE void RecordIfStatementSourceRange(PreParserStatement node, V8_INLINE void RecordIfStatementSourceRange(PreParserStatement node,
......
...@@ -340,6 +340,7 @@ TestCoverage( ...@@ -340,6 +340,7 @@ TestCoverage(
{"start":219,"end":232,"count":0}, {"start":219,"end":232,"count":0},
{"start":264,"end":274,"count":0}, {"start":264,"end":274,"count":0},
{"start":369,"end":380,"count":0}, {"start":369,"end":380,"count":0},
{"start":403,"end":414,"count":0}, // TODO(jgruber): Include `catch` in range.
{"start":513,"end":564,"count":0}] {"start":513,"end":564,"count":0}]
); );
...@@ -361,16 +362,27 @@ TestCoverage( ...@@ -361,16 +362,27 @@ TestCoverage(
} // 0600 } // 0600
nop(); // 0650 nop(); // 0650
}(); // 0700 }(); // 0700
!function() { // 0750
try { throw 42; } catch (e) { // 0800
return; // 0850
nop(); // 0900
} // 0950
nop(); // 1000
}(); // 1050
`, `,
[{"start":0,"end":749,"count":1}, [{"start":0,"end":1099,"count":1},
{"start":1,"end":151,"count":1}, {"start":1,"end":151,"count":1},
{"start":67,"end":80,"count":0}, {"start":67,"end":80,"count":0},
{"start":89,"end":91,"count":0}, // TODO(jgruber): Missing continuation. {"start":89,"end":151,"count":0},
{"start":201,"end":351,"count":1}, {"start":201,"end":351,"count":1},
{"start":284,"end":286,"count":0}, // TODO(jgruber): Missing continuation. {"start":284,"end":351,"count":0},
{"start":401,"end":701,"count":1}, {"start":401,"end":701,"count":1},
{"start":569,"end":701,"count":0}, {"start":569,"end":701,"count":0},
{"start":561,"end":568,"count":0}] // TODO(jgruber): Sorting. {"start":561,"end":568,"count":0}, // TODO(jgruber): Sorting.
{"start":751,"end":1051,"count":1},
{"start":817,"end":830,"count":0},
{"start":861,"end":1051,"count":0}]
); );
TestCoverage( TestCoverage(
...@@ -391,4 +403,67 @@ TestCoverage( ...@@ -391,4 +403,67 @@ TestCoverage(
{"start":226,"end":303,"count":0}] {"start":226,"end":303,"count":0}]
); );
TestCoverage(
"labeled break statements",
`
!function() { // 0000
var x = 42; // 0050
l0: switch (x) { // 0100
case 41: return; // 0150
case 42: // 0200
switch (x) { case 42: break l0; } // 0250
break; // 0300
} // 0350
l1: for (;;) { // 0400
for (;;) break l1; // 0450
} // 0500
l2: while (true) { // 0550
while (true) break l2; // 0600
} // 0650
l3: do { // 0700
do { break l3; } while (true); // 0750
} while (true); // 0800
l4: { break l4; } // 0850
l5: for (;;) for (;;) break l5; // 0900
}(); // 0950
`,
[{"start":0,"end":999,"count":1},
{"start":1,"end":951,"count":1},
{"start":152,"end":202,"count":0},
{"start":285,"end":353,"count":0},
{"start":472,"end":503,"count":0},
{"start":626,"end":653,"count":0},
{"start":768,"end":803,"count":0},
{"start":867,"end":869,"count":0}]
);
TestCoverage(
"labeled continue statements",
`
!function() { // 0000
l0: for (var i0 = 0; i0 < 2; i0++) { // 0050
for (;;) continue l0; // 0100
} // 0150
var i1 = 0; // 0200
l1: while (i1 < 2) { // 0250
i1++; // 0300
while (true) continue l1; // 0350
} // 0400
var i2 = 0; // 0450
l2: do { // 0500
i2++; // 0550
do { continue l2; } while (true); // 0600
} while (i2 < 2); // 0650
}(); // 0700
`,
[{"start":0,"end":749,"count":1},
{"start":1,"end":701,"count":1},
{"start":87,"end":153,"count":2},
{"start":125,"end":153,"count":0},
{"start":271,"end":403,"count":2},
{"start":379,"end":403,"count":0},
{"start":509,"end":653,"count":2},
{"start":621,"end":653,"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