Commit 95882f0e authored by jgruber's avatar jgruber Committed by Commit Bot

[coverage] Add continuation counters

Track execution counts of the continuations of block structures (e.g.
IfStatements) to capture cases in which execution does not continue after a
block. For example:

for (;;) {
  return;
}
// Never reached, tracked by continuation counter.

A continuation counter only has a start position; it's range is implicitly
until the next sibling range or the end of the parent range.

Bug: v8:6000
Change-Id: I8e8f1f5b140b64c86754b916e626eb50f0707d70
Reviewed-on: https://chromium-review.googlesource.com/530846
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46006}
parent 2325ef53
...@@ -1678,6 +1678,7 @@ v8_source_set("v8_base") { ...@@ -1678,6 +1678,7 @@ v8_source_set("v8_base") {
"src/identity-map.h", "src/identity-map.h",
"src/interface-descriptors.cc", "src/interface-descriptors.cc",
"src/interface-descriptors.h", "src/interface-descriptors.h",
"src/interpreter/block-coverage-builder.h",
"src/interpreter/bytecode-array-accessor.cc", "src/interpreter/bytecode-array-accessor.cc",
"src/interpreter/bytecode-array-accessor.h", "src/interpreter/bytecode-array-accessor.h",
"src/interpreter/bytecode-array-builder.cc", "src/interpreter/bytecode-array-builder.cc",
......
...@@ -182,8 +182,13 @@ class AstProperties final BASE_EMBEDDED { ...@@ -182,8 +182,13 @@ class AstProperties final BASE_EMBEDDED {
DEFINE_OPERATORS_FOR_FLAGS(AstProperties::Flags) DEFINE_OPERATORS_FOR_FLAGS(AstProperties::Flags)
struct SourceRange { struct SourceRange {
SourceRange() : start(kNoSourcePosition), end(kNoSourcePosition) {} SourceRange() : SourceRange(kNoSourcePosition, kNoSourcePosition) {}
SourceRange(int start, int end) : start(start), end(end) {}
bool IsEmpty() const { return start == kNoSourcePosition; } bool IsEmpty() const { return start == kNoSourcePosition; }
static SourceRange ContinuationOf(const SourceRange& that) {
return that.IsEmpty() ? SourceRange()
: SourceRange(that.end + 1, kNoSourcePosition);
}
int32_t start, end; int32_t start, end;
}; };
...@@ -495,6 +500,9 @@ class IterationStatement : public BreakableStatement { ...@@ -495,6 +500,9 @@ 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_; }
SourceRange continuation_range() const {
return SourceRange::ContinuationOf(body_range_);
}
int suspend_count() const { return suspend_count_; } int suspend_count() const { return suspend_count_; }
int first_suspend_id() const { return first_suspend_id_; } int first_suspend_id() const { return first_suspend_id_; }
...@@ -922,6 +930,11 @@ class IfStatement final : public Statement { ...@@ -922,6 +930,11 @@ 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_; }
SourceRange continuation_range() const {
SourceRange trailing_range =
HasElseStatement() ? else_range() : then_range();
return SourceRange::ContinuationOf(trailing_range);
}
void set_condition(Expression* e) { condition_ = e; } void set_condition(Expression* e) { condition_ = e; }
void set_then_statement(Statement* s) { then_statement_ = s; } void set_then_statement(Statement* s) { then_statement_ = s; }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/debug/debug-coverage.h" #include "src/debug/debug-coverage.h"
#include "src/ast/ast.h"
#include "src/base/hashmap.h" #include "src/base/hashmap.h"
#include "src/deoptimizer.h" #include "src/deoptimizer.h"
#include "src/frames-inl.h" #include "src/frames-inl.h"
...@@ -59,8 +60,8 @@ bool CompareSharedFunctionInfo(SharedFunctionInfo* a, SharedFunctionInfo* b) { ...@@ -59,8 +60,8 @@ bool CompareSharedFunctionInfo(SharedFunctionInfo* a, SharedFunctionInfo* b) {
} }
bool CompareCoverageBlock(const CoverageBlock& a, const CoverageBlock& b) { bool CompareCoverageBlock(const CoverageBlock& a, const CoverageBlock& b) {
DCHECK(a.start != kNoSourcePosition && a.end != kNoSourcePosition); DCHECK(a.start != kNoSourcePosition);
DCHECK(b.start != kNoSourcePosition && b.end != kNoSourcePosition); DCHECK(b.start != kNoSourcePosition);
if (a.start == b.start) return a.end > b.end; if (a.start == b.start) return a.end > b.end;
return a.start < b.start; return a.start < b.start;
} }
...@@ -81,8 +82,6 @@ std::vector<CoverageBlock> GetSortedBlockData(Isolate* isolate, ...@@ -81,8 +82,6 @@ std::vector<CoverageBlock> GetSortedBlockData(Isolate* isolate,
const int count = coverage_info->BlockCount(i); const int count = coverage_info->BlockCount(i);
DCHECK(start_pos != kNoSourcePosition); DCHECK(start_pos != kNoSourcePosition);
DCHECK(until_pos != kNoSourcePosition);
result.emplace_back(start_pos, until_pos, count); result.emplace_back(start_pos, until_pos, count);
} }
...@@ -91,6 +90,44 @@ std::vector<CoverageBlock> GetSortedBlockData(Isolate* isolate, ...@@ -91,6 +90,44 @@ std::vector<CoverageBlock> GetSortedBlockData(Isolate* isolate,
return result; return result;
} }
// Rewrite position singletons (produced by unconditional control flow
// like return statements, and by continuation counters) into source
// ranges that end at the next sibling range or the end of the parent
// range, whichever comes first.
void RewritePositionSingletonsToRanges(CoverageFunction* function) {
std::vector<SourceRange> nesting_stack;
nesting_stack.emplace_back(function->start, function->end);
const int blocks_count = static_cast<int>(function->blocks.size());
for (int i = 0; i < blocks_count; i++) {
CoverageBlock& block = function->blocks[i];
while (nesting_stack.back().end < block.start) {
nesting_stack.pop_back();
}
const SourceRange& parent_range = nesting_stack.back();
DCHECK(block.start != kNoSourcePosition);
if (block.end == kNoSourcePosition) {
// The current block ends at the next sibling block (if it exists) or the
// end of the parent block otherwise.
if (i < blocks_count - 1 &&
function->blocks[i + 1].start <= parent_range.end) {
block.end = function->blocks[i + 1].start - 1;
} else {
block.end = parent_range.end;
}
}
if (i < blocks_count - 1) {
nesting_stack.emplace_back(block.start, block.end);
}
}
DCHECK_EQ(1, nesting_stack.size());
}
} // anonymous namespace } // anonymous namespace
Coverage* Coverage::CollectPrecise(Isolate* isolate) { Coverage* Coverage::CollectPrecise(Isolate* isolate) {
...@@ -207,6 +244,7 @@ Coverage* Coverage::Collect(Isolate* isolate, ...@@ -207,6 +244,7 @@ Coverage* Coverage::Collect(Isolate* isolate,
if (FLAG_block_coverage && info->HasCoverageInfo()) { if (FLAG_block_coverage && info->HasCoverageInfo()) {
CoverageFunction* function = &functions->back(); CoverageFunction* function = &functions->back();
function->blocks = GetSortedBlockData(isolate, info); function->blocks = GetSortedBlockData(isolate, info);
RewritePositionSingletonsToRanges(function);
} }
} }
} }
......
// Copyright 2017 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.
#ifndef V8_INTERPRETER_BLOCK_COVERAGE_BUILDER_H_
#define V8_INTERPRETER_BLOCK_COVERAGE_BUILDER_H_
#include "src/ast/ast.h"
#include "src/interpreter/bytecode-array-builder.h"
#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
namespace interpreter {
// Used to generate IncBlockCounter bytecodes and the {source range, slot}
// mapping for block coverage.
class BlockCoverageBuilder final : public ZoneObject {
public:
BlockCoverageBuilder(Zone* zone, BytecodeArrayBuilder* builder)
: slots_(0, zone), builder_(builder) {}
static const int kNoCoverageArraySlot = -1;
int AllocateBlockCoverageSlot(SourceRange range) {
if (range.IsEmpty()) return kNoCoverageArraySlot;
const int slot = static_cast<int>(slots_.size());
slots_.emplace_back(range);
return slot;
}
void IncrementBlockCounter(int coverage_array_slot) {
if (coverage_array_slot == kNoCoverageArraySlot) return;
builder_->IncBlockCounter(coverage_array_slot);
}
const ZoneVector<SourceRange>& slots() const { return slots_; }
private:
// Contains source range information for allocated block coverage counter
// slots. Slot i covers range slots_[i].
ZoneVector<SourceRange> slots_;
BytecodeArrayBuilder* builder_;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BLOCK_COVERAGE_BUILDER_H_
...@@ -737,36 +737,6 @@ class BytecodeGenerator::GlobalDeclarationsBuilder final : public ZoneObject { ...@@ -737,36 +737,6 @@ class BytecodeGenerator::GlobalDeclarationsBuilder final : public ZoneObject {
bool has_constant_pool_entry_; bool has_constant_pool_entry_;
}; };
// Used to generate IncBlockCounter bytecodes and the {source range, slot}
// mapping for block coverage.
class BytecodeGenerator::BlockCoverageBuilder final : public ZoneObject {
public:
explicit BlockCoverageBuilder(Zone* zone, BytecodeArrayBuilder* builder)
: slots_(0, zone), builder_(builder) {}
static const int kNoCoverageArraySlot = -1;
int AllocateBlockCoverageSlot(SourceRange range) {
if (range.IsEmpty()) return kNoCoverageArraySlot;
const int slot = static_cast<int>(slots_.size());
slots_.emplace_back(range);
return slot;
}
void IncrementBlockCounter(int coverage_array_slot) {
if (coverage_array_slot == kNoCoverageArraySlot) return;
builder_->IncBlockCounter(coverage_array_slot);
}
const ZoneVector<SourceRange>& slots() const { return slots_; }
private:
// Contains source range information for allocated block coverage counter
// slots. Slot i covers range slots_[i].
ZoneVector<SourceRange> slots_;
BytecodeArrayBuilder* builder_;
};
class BytecodeGenerator::CurrentScope final { class BytecodeGenerator::CurrentScope final {
public: public:
CurrentScope(BytecodeGenerator* generator, Scope* scope) CurrentScope(BytecodeGenerator* generator, Scope* scope)
...@@ -1237,6 +1207,8 @@ void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) { ...@@ -1237,6 +1207,8 @@ void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) {
int then_slot = AllocateBlockCoverageSlotIfEnabled(stmt->then_range()); int then_slot = AllocateBlockCoverageSlotIfEnabled(stmt->then_range());
int else_slot = AllocateBlockCoverageSlotIfEnabled(stmt->else_range()); int else_slot = AllocateBlockCoverageSlotIfEnabled(stmt->else_range());
int continuation_slot =
AllocateBlockCoverageSlotIfEnabled(stmt->continuation_range());
if (stmt->condition()->ToBooleanIsTrue()) { if (stmt->condition()->ToBooleanIsTrue()) {
// Generate then block unconditionally as always true. // Generate then block unconditionally as always true.
...@@ -1271,6 +1243,7 @@ void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) { ...@@ -1271,6 +1243,7 @@ void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) {
} }
builder()->Bind(&end_label); builder()->Bind(&end_label);
} }
BuildIncrementBlockCoverageCounterIfEnabled(continuation_slot);
} }
void BytecodeGenerator::VisitSloppyBlockFunctionStatement( void BytecodeGenerator::VisitSloppyBlockFunctionStatement(
...@@ -1361,6 +1334,7 @@ void BytecodeGenerator::VisitCaseClause(CaseClause* clause) { ...@@ -1361,6 +1334,7 @@ void BytecodeGenerator::VisitCaseClause(CaseClause* clause) {
void BytecodeGenerator::VisitIterationBody(IterationStatement* stmt, void BytecodeGenerator::VisitIterationBody(IterationStatement* stmt,
LoopBuilder* loop_builder) { LoopBuilder* loop_builder) {
loop_builder->LoopBody();
ControlScopeForIteration execution_control(this, stmt, loop_builder); ControlScopeForIteration execution_control(this, stmt, loop_builder);
builder()->StackCheck(stmt->position()); builder()->StackCheck(stmt->position());
Visit(stmt->body()); Visit(stmt->body());
...@@ -1368,20 +1342,16 @@ void BytecodeGenerator::VisitIterationBody(IterationStatement* stmt, ...@@ -1368,20 +1342,16 @@ void BytecodeGenerator::VisitIterationBody(IterationStatement* stmt,
} }
void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
int body_slot = AllocateBlockCoverageSlotIfEnabled(stmt->body_range()); LoopBuilder loop_builder(builder(), block_coverage_builder_,
stmt->body_range(), stmt->continuation_range());
LoopBuilder loop_builder(builder());
if (stmt->cond()->ToBooleanIsFalse()) { if (stmt->cond()->ToBooleanIsFalse()) {
BuildIncrementBlockCoverageCounterIfEnabled(body_slot);
VisitIterationBody(stmt, &loop_builder); VisitIterationBody(stmt, &loop_builder);
} else if (stmt->cond()->ToBooleanIsTrue()) { } else if (stmt->cond()->ToBooleanIsTrue()) {
VisitIterationHeader(stmt, &loop_builder); VisitIterationHeader(stmt, &loop_builder);
BuildIncrementBlockCoverageCounterIfEnabled(body_slot);
VisitIterationBody(stmt, &loop_builder); VisitIterationBody(stmt, &loop_builder);
loop_builder.JumpToHeader(loop_depth_); loop_builder.JumpToHeader(loop_depth_);
} else { } else {
VisitIterationHeader(stmt, &loop_builder); VisitIterationHeader(stmt, &loop_builder);
BuildIncrementBlockCoverageCounterIfEnabled(body_slot);
VisitIterationBody(stmt, &loop_builder); VisitIterationBody(stmt, &loop_builder);
builder()->SetExpressionAsStatementPosition(stmt->cond()); builder()->SetExpressionAsStatementPosition(stmt->cond());
BytecodeLabels loop_backbranch(zone()); BytecodeLabels loop_backbranch(zone());
...@@ -1393,14 +1363,14 @@ void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { ...@@ -1393,14 +1363,14 @@ void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
} }
void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) { void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
int body_slot = AllocateBlockCoverageSlotIfEnabled(stmt->body_range()); LoopBuilder loop_builder(builder(), block_coverage_builder_,
stmt->body_range(), stmt->continuation_range());
if (stmt->cond()->ToBooleanIsFalse()) { if (stmt->cond()->ToBooleanIsFalse()) {
// If the condition is false there is no need to generate the loop. // If the condition is false there is no need to generate the loop.
return; return;
} }
LoopBuilder loop_builder(builder());
VisitIterationHeader(stmt, &loop_builder); VisitIterationHeader(stmt, &loop_builder);
if (!stmt->cond()->ToBooleanIsTrue()) { if (!stmt->cond()->ToBooleanIsTrue()) {
builder()->SetExpressionAsStatementPosition(stmt->cond()); builder()->SetExpressionAsStatementPosition(stmt->cond());
...@@ -1409,13 +1379,13 @@ void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) { ...@@ -1409,13 +1379,13 @@ void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
TestFallthrough::kThen); TestFallthrough::kThen);
loop_body.Bind(builder()); loop_body.Bind(builder());
} }
BuildIncrementBlockCoverageCounterIfEnabled(body_slot);
VisitIterationBody(stmt, &loop_builder); VisitIterationBody(stmt, &loop_builder);
loop_builder.JumpToHeader(loop_depth_); loop_builder.JumpToHeader(loop_depth_);
} }
void BytecodeGenerator::VisitForStatement(ForStatement* stmt) { void BytecodeGenerator::VisitForStatement(ForStatement* stmt) {
int body_slot = AllocateBlockCoverageSlotIfEnabled(stmt->body_range()); LoopBuilder loop_builder(builder(), block_coverage_builder_,
stmt->body_range(), stmt->continuation_range());
if (stmt->init() != nullptr) { if (stmt->init() != nullptr) {
Visit(stmt->init()); Visit(stmt->init());
...@@ -1426,7 +1396,6 @@ void BytecodeGenerator::VisitForStatement(ForStatement* stmt) { ...@@ -1426,7 +1396,6 @@ void BytecodeGenerator::VisitForStatement(ForStatement* stmt) {
return; return;
} }
LoopBuilder loop_builder(builder());
VisitIterationHeader(stmt, &loop_builder); VisitIterationHeader(stmt, &loop_builder);
if (stmt->cond() && !stmt->cond()->ToBooleanIsTrue()) { if (stmt->cond() && !stmt->cond()->ToBooleanIsTrue()) {
builder()->SetExpressionAsStatementPosition(stmt->cond()); builder()->SetExpressionAsStatementPosition(stmt->cond());
...@@ -1435,7 +1404,6 @@ void BytecodeGenerator::VisitForStatement(ForStatement* stmt) { ...@@ -1435,7 +1404,6 @@ void BytecodeGenerator::VisitForStatement(ForStatement* stmt) {
TestFallthrough::kThen); TestFallthrough::kThen);
loop_body.Bind(builder()); loop_body.Bind(builder());
} }
BuildIncrementBlockCoverageCounterIfEnabled(body_slot);
VisitIterationBody(stmt, &loop_builder); VisitIterationBody(stmt, &loop_builder);
if (stmt->next() != nullptr) { if (stmt->next() != nullptr) {
builder()->SetStatementPosition(stmt->next()); builder()->SetStatementPosition(stmt->next());
......
...@@ -21,6 +21,7 @@ namespace interpreter { ...@@ -21,6 +21,7 @@ namespace interpreter {
class GlobalDeclarationsBuilder; class GlobalDeclarationsBuilder;
class LoopBuilder; class LoopBuilder;
class BlockCoverageBuilder;
class BytecodeJumpTable; class BytecodeJumpTable;
class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> { class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
...@@ -51,7 +52,6 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> { ...@@ -51,7 +52,6 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
class ExpressionResultScope; class ExpressionResultScope;
class EffectResultScope; class EffectResultScope;
class GlobalDeclarationsBuilder; class GlobalDeclarationsBuilder;
class BlockCoverageBuilder;
class RegisterAllocationScope; class RegisterAllocationScope;
class TestResultScope; class TestResultScope;
class ValueResultScope; class ValueResultScope;
......
...@@ -52,6 +52,11 @@ LoopBuilder::~LoopBuilder() { ...@@ -52,6 +52,11 @@ LoopBuilder::~LoopBuilder() {
if (generator_jump_table_location_ != nullptr) { if (generator_jump_table_location_ != nullptr) {
*generator_jump_table_location_ = parent_generator_jump_table_; *generator_jump_table_location_ = parent_generator_jump_table_;
} }
// Generate block coverage counter for the continuation.
if (block_coverage_builder_ != nullptr) {
block_coverage_builder_->IncrementBlockCounter(
block_coverage_continuation_slot_);
}
} }
void LoopBuilder::LoopHeader() { void LoopBuilder::LoopHeader() {
...@@ -83,6 +88,12 @@ void LoopBuilder::LoopHeaderInGenerator( ...@@ -83,6 +88,12 @@ void LoopBuilder::LoopHeaderInGenerator(
builder()->AllocateJumpTable(resume_count, first_resume_id); builder()->AllocateJumpTable(resume_count, first_resume_id);
} }
void LoopBuilder::LoopBody() {
if (block_coverage_builder_ != nullptr) {
block_coverage_builder_->IncrementBlockCounter(block_coverage_body_slot_);
}
}
void LoopBuilder::JumpToHeader(int loop_depth) { void LoopBuilder::JumpToHeader(int loop_depth) {
// Pass the proper loop nesting level to the backwards branch, to trigger // Pass the proper loop nesting level to the backwards branch, to trigger
// on-stack replacement when armed for the given loop nesting depth. // on-stack replacement when armed for the given loop nesting depth.
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "src/interpreter/bytecode-array-builder.h" #include "src/interpreter/bytecode-array-builder.h"
#include "src/interpreter/block-coverage-builder.h"
#include "src/interpreter/bytecode-label.h" #include "src/interpreter/bytecode-label.h"
#include "src/zone/zone-containers.h" #include "src/zone/zone-containers.h"
...@@ -87,16 +88,29 @@ class V8_EXPORT_PRIVATE BlockBuilder final ...@@ -87,16 +88,29 @@ class V8_EXPORT_PRIVATE BlockBuilder final
// their loop. // their loop.
class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder { class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder {
public: public:
explicit LoopBuilder(BytecodeArrayBuilder* builder) LoopBuilder(BytecodeArrayBuilder* builder,
BlockCoverageBuilder* block_coverage_builder = nullptr,
const SourceRange& body_range = {},
const SourceRange& continuation_range = {})
: BreakableControlFlowBuilder(builder), : BreakableControlFlowBuilder(builder),
continue_labels_(builder->zone()), continue_labels_(builder->zone()),
generator_jump_table_location_(nullptr), generator_jump_table_location_(nullptr),
parent_generator_jump_table_(nullptr) {} parent_generator_jump_table_(nullptr),
block_coverage_builder_(block_coverage_builder) {
if (block_coverage_builder_ != nullptr) {
block_coverage_body_slot_ =
block_coverage_builder_->AllocateBlockCoverageSlot(body_range);
block_coverage_continuation_slot_ =
block_coverage_builder_->AllocateBlockCoverageSlot(
continuation_range);
}
}
~LoopBuilder(); ~LoopBuilder();
void LoopHeader(); void LoopHeader();
void LoopHeaderInGenerator(BytecodeJumpTable** parent_generator_jump_table, void LoopHeaderInGenerator(BytecodeJumpTable** parent_generator_jump_table,
int first_resume_id, int resume_count); int first_resume_id, int resume_count);
void LoopBody();
void JumpToHeader(int loop_depth); void JumpToHeader(int loop_depth);
void BindContinueTarget(); void BindContinueTarget();
...@@ -120,6 +134,10 @@ class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder { ...@@ -120,6 +134,10 @@ class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder {
// field is ugly, figure out a better way to do this. // field is ugly, figure out a better way to do this.
BytecodeJumpTable** generator_jump_table_location_; BytecodeJumpTable** generator_jump_table_location_;
BytecodeJumpTable* parent_generator_jump_table_; BytecodeJumpTable* parent_generator_jump_table_;
int block_coverage_body_slot_;
int block_coverage_continuation_slot_;
BlockCoverageBuilder* block_coverage_builder_;
}; };
......
...@@ -1135,6 +1135,7 @@ ...@@ -1135,6 +1135,7 @@
'identity-map.h', 'identity-map.h',
'interface-descriptors.cc', 'interface-descriptors.cc',
'interface-descriptors.h', 'interface-descriptors.h',
'interpreter/block-coverage-builder.h',
'interpreter/bytecodes.cc', 'interpreter/bytecodes.cc',
'interpreter/bytecodes.h', 'interpreter/bytecodes.h',
'interpreter/bytecode-array-accessor.cc', 'interpreter/bytecode-array-accessor.cc',
......
...@@ -21,7 +21,7 @@ function TestCoverage(name, source, expectation) { ...@@ -21,7 +21,7 @@ function TestCoverage(name, source, expectation) {
var stringified_result = JSON.stringify(covfefe); var stringified_result = JSON.stringify(covfefe);
var stringified_expectation = JSON.stringify(expectation); var stringified_expectation = JSON.stringify(expectation);
if (stringified_result != stringified_expectation) { if (stringified_result != stringified_expectation) {
print(JSON.stringify(covfefe, undefined, 1)); print(stringified_result.replace(/[}],[{]/g, "},\n {"));
} }
assertEquals(stringified_expectation, stringified_result, name + " failed"); assertEquals(stringified_expectation, stringified_result, name + " failed");
} }
...@@ -66,16 +66,44 @@ f(43); ...@@ -66,16 +66,44 @@ f(43);
{"start":45,"end":83,"count":1}, {"start":45,"end":83,"count":1},
{"start":64,"end":69,"count":0}, {"start":64,"end":69,"count":0},
{"start":71,"end":79,"count":1}, {"start":71,"end":79,"count":1},
{"start":80,"end":83,"count":1},
{"start":84,"end":97,"count":2},
{"start":98,"end":107,"count":1}, {"start":98,"end":107,"count":1},
{"start":109,"end":121,"count":1}, {"start":109,"end":121,"count":1},
{"start":122,"end":135,"count":2},
{"start":136,"end":141,"count":1}, {"start":136,"end":141,"count":1},
{"start":143,"end":151,"count":1}, {"start":143,"end":151,"count":1},
{"start":152,"end":163,"count":2},
{"start":164,"end":169,"count":0}, {"start":164,"end":169,"count":0},
{"start":171,"end":179,"count":2}, {"start":171,"end":179,"count":2},
{"start":180,"end":191,"count":2},
{"start":192,"end":197,"count":0}, {"start":192,"end":197,"count":0},
{"start":198,"end":208,"count":2},
{"start":209,"end":214,"count":2}, {"start":209,"end":214,"count":2},
{"start":216,"end":224,"count":0}, {"start":216,"end":224,"count":0},
{"start":236,"end":241,"count":2}] {"start":225,"end":235,"count":2},
{"start":236,"end":241,"count":2},
{"start":242,"end":244,"count":2}]
);
function nop() {}
TestCoverage(
"if statement (early return)",
`
!function() { // 0000
if (true) { // 0050
nop(); // 0100
return; // 0150
nop(); // 0200
} // 0250
nop(); // 0300
}() // 0350
`,
[{"start":0,"end":399,"count":1},
{"start":1,"end":351,"count":1},
{"start":60,"end":252,"count":1},
{"start":253,"end":351,"count":0}]
); );
TestCoverage( TestCoverage(
...@@ -98,12 +126,18 @@ function g() {} ...@@ -98,12 +126,18 @@ function g() {}
{"start":0,"end":15,"count":36}, {"start":0,"end":15,"count":36},
{"start":17,"end":256,"count":1}, {"start":17,"end":256,"count":1},
{"start":59,"end":64,"count":12}, {"start":59,"end":64,"count":12},
{"start":65,"end":94,"count":1},
{"start":95,"end":110,"count":12}, {"start":95,"end":110,"count":12},
{"start":111,"end":139,"count":1},
{"start":140,"end":145,"count":0}, {"start":140,"end":145,"count":0},
{"start":146,"end":173,"count":1},
{"start":174,"end":181,"count":1}, {"start":174,"end":181,"count":1},
{"start":182,"end":211,"count":1},
{"start":212,"end":253,"count":12}, {"start":212,"end":253,"count":12},
{"start":234,"end":239,"count":4}, {"start":234,"end":239,"count":4},
{"start":241,"end":249,"count":8}] {"start":241,"end":249,"count":8},
{"start":250,"end":253,"count":12},
{"start":254,"end":256,"count":1}]
); );
TestCoverage( TestCoverage(
...@@ -122,9 +156,47 @@ function g() {} ...@@ -122,9 +156,47 @@ function g() {}
{"start":0,"end":15,"count":36}, {"start":0,"end":15,"count":36},
{"start":17,"end":168,"count":1}, {"start":17,"end":168,"count":1},
{"start":72,"end":77,"count":12}, {"start":72,"end":77,"count":12},
{"start":78,"end":109,"count":1},
{"start":110,"end":115,"count":12}, {"start":110,"end":115,"count":12},
{"start":116,"end":141,"count":1},
{"start":142,"end":147,"count":12}, {"start":142,"end":147,"count":12},
{"start":158,"end":165,"count":1}] {"start":148,"end":157,"count":1},
{"start":158,"end":165,"count":1},
{"start":166,"end":168,"count":1}]
);
TestCoverage(
"for statement (early return)",
`
!function() { // 0000
for (var i = 0; i < 10; i++) { // 0050
nop(); // 0100
continue; // 0150
nop(); // 0200
} // 0250
nop(); // 0300
for (;;) { // 0350
nop(); // 0400
break; // 0450
nop(); // 0500
} // 0550
nop(); // 0600
for (;;) { // 0650
nop(); // 0700
return; // 0750
nop(); // 0800
} // 0850
nop(); // 0900
}() // 0950
`,
[{"start":0,"end":999,"count":1},
{"start":1,"end":951,"count":1},
{"start":79,"end":252,"count":10},
{"start":253,"end":358,"count":1},
{"start":359,"end":552,"count":1},
{"start":553,"end":658,"count":1},
{"start":659,"end":852,"count":1},
{"start":853,"end":951,"count":0}]
); );
TestCoverage( TestCoverage(
...@@ -148,13 +220,91 @@ function g() {} ...@@ -148,13 +220,91 @@ function g() {}
{"start":0,"end":15,"count":25}, {"start":0,"end":15,"count":25},
{"start":17,"end":313,"count":1}, {"start":17,"end":313,"count":1},
{"start":61,"end":66,"count":12}, {"start":61,"end":66,"count":12},
{"start":67,"end":89,"count":1},
{"start":90,"end":104,"count":12}, {"start":90,"end":104,"count":12},
{"start":105,"end":126,"count":1},
{"start":127,"end":132,"count":0}, {"start":127,"end":132,"count":0},
{"start":133,"end":153,"count":1},
{"start":154,"end":161,"count":1}, {"start":154,"end":161,"count":1},
{"start":162,"end":172,"count":1},
{"start":173,"end":179,"count":12}, {"start":173,"end":179,"count":12},
{"start":180,"end":205,"count":1},
{"start":206,"end":221,"count":12}, {"start":206,"end":221,"count":12},
{"start":222,"end":247,"count":1},
{"start":248,"end":258,"count":1}, {"start":248,"end":258,"count":1},
{"start":284,"end":296,"count":1}] {"start":259,"end":283,"count":1},
{"start":284,"end":296,"count":1},
{"start":297,"end":313,"count":1}]
);
TestCoverage(
"while statement (early return)",
`
!function() { // 0000
let i = 0; // 0050
while (i < 10) { // 0100
i++; // 0150
continue; // 0200
nop(); // 0250
} // 0300
nop(); // 0350
while (true) { // 0400
nop(); // 0450
break; // 0500
nop(); // 0550
} // 0600
nop(); // 0650
while (true) { // 0700
nop(); // 0750
return; // 0800
nop(); // 0850
} // 0900
nop(); // 0950
}() // 1000
`,
[{"start":0,"end":1049,"count":1},
{"start":1,"end":1001,"count":1},
{"start":115,"end":302,"count":10},
{"start":303,"end":412,"count":1},
{"start":413,"end":602,"count":1},
{"start":603,"end":712,"count":1},
{"start":713,"end":902,"count":1},
{"start":903,"end":1001,"count":0}]
);
TestCoverage(
"do-while statement (early return)",
`
!function() { // 0000
let i = 0; // 0050
do { // 0100
i++; // 0150
continue; // 0200
nop(); // 0250
} while (i < 10); // 0300
nop(); // 0350
do { // 0400
nop(); // 0450
break; // 0500
nop(); // 0550
} while (true); // 0600
nop(); // 0650
do { // 0700
nop(); // 0750
return; // 0800
nop(); // 0850
} while (true); // 0900
nop(); // 0950
}() // 1000
`,
[{"start":0,"end":1049,"count":1},
{"start":1,"end":1001,"count":1},
{"start":102,"end":302,"count":10},
{"start":303,"end":401,"count":1},
{"start":402,"end":602,"count":1},
{"start":603,"end":701,"count":1},
{"start":702,"end":902,"count":1},
{"start":903,"end":1001,"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