Commit e65e2f87 authored by jgruber's avatar jgruber Committed by Commit Bot

[coverage] Add support for iteration (For,While,DoWhile)

This adds block coverage support for simple iteration. For-of and
for-in loops are not yet covered, and we don't yet keep execution counts
for init, cond, and next statements.

BUG=v8:6000

Change-Id: I30b468a2c93f0bb60e857b6632be92920f6857e0
Reviewed-on: https://chromium-review.googlesource.com/527113
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45779}
parent 8ff91ab7
...@@ -546,6 +546,8 @@ class IterationStatement : public BreakableStatement { ...@@ -546,6 +546,8 @@ class IterationStatement : public BreakableStatement {
Statement* body() const { return body_; } Statement* body() const { return body_; }
void set_body(Statement* s) { body_ = s; } void set_body(Statement* s) { body_ = s; }
SourceRange body_range() const { return 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_; }
void set_suspend_count(int suspend_count) { suspend_count_ = suspend_count; } void set_suspend_count(int suspend_count) { suspend_count_ = suspend_count; }
...@@ -567,7 +569,10 @@ class IterationStatement : public BreakableStatement { ...@@ -567,7 +569,10 @@ class IterationStatement : public BreakableStatement {
suspend_count_(0), suspend_count_(0),
first_suspend_id_(0) {} first_suspend_id_(0) {}
static int parent_num_ids() { return BreakableStatement::num_ids(); } static int parent_num_ids() { return BreakableStatement::num_ids(); }
void Initialize(Statement* body) { body_ = body; } void Initialize(Statement* body, const SourceRange& body_range = {}) {
body_ = body;
body_range_ = body_range;
}
static const uint8_t kNextBitFieldIndex = static const uint8_t kNextBitFieldIndex =
BreakableStatement::kNextBitFieldIndex; BreakableStatement::kNextBitFieldIndex;
...@@ -576,6 +581,7 @@ class IterationStatement : public BreakableStatement { ...@@ -576,6 +581,7 @@ class IterationStatement : public BreakableStatement {
int local_id(int n) const { return base_id() + parent_num_ids() + n; } int local_id(int n) const { return base_id() + parent_num_ids() + n; }
Statement* body_; Statement* body_;
SourceRange body_range_;
Label continue_target_; Label continue_target_;
int suspend_count_; int suspend_count_;
int first_suspend_id_; int first_suspend_id_;
...@@ -584,8 +590,9 @@ class IterationStatement : public BreakableStatement { ...@@ -584,8 +590,9 @@ class IterationStatement : public BreakableStatement {
class DoWhileStatement final : public IterationStatement { class DoWhileStatement final : public IterationStatement {
public: public:
void Initialize(Expression* cond, Statement* body) { void Initialize(Expression* cond, Statement* body,
IterationStatement::Initialize(body); const SourceRange& body_range = {}) {
IterationStatement::Initialize(body, body_range);
cond_ = cond; cond_ = cond;
} }
...@@ -611,8 +618,9 @@ class DoWhileStatement final : public IterationStatement { ...@@ -611,8 +618,9 @@ class DoWhileStatement final : public IterationStatement {
class WhileStatement final : public IterationStatement { class WhileStatement final : public IterationStatement {
public: public:
void Initialize(Expression* cond, Statement* body) { void Initialize(Expression* cond, Statement* body,
IterationStatement::Initialize(body); const SourceRange& body_range = {}) {
IterationStatement::Initialize(body, body_range);
cond_ = cond; cond_ = cond;
} }
...@@ -638,11 +646,9 @@ class WhileStatement final : public IterationStatement { ...@@ -638,11 +646,9 @@ class WhileStatement final : public IterationStatement {
class ForStatement final : public IterationStatement { class ForStatement final : public IterationStatement {
public: public:
void Initialize(Statement* init, void Initialize(Statement* init, Expression* cond, Statement* next,
Expression* cond, Statement* body, const SourceRange& body_range = {}) {
Statement* next, IterationStatement::Initialize(body, body_range);
Statement* body) {
IterationStatement::Initialize(body);
init_ = init; init_ = init;
cond_ = cond; cond_ = cond;
next_ = next; next_ = next;
......
...@@ -1368,15 +1368,20 @@ void BytecodeGenerator::VisitIterationBody(IterationStatement* stmt, ...@@ -1368,15 +1368,20 @@ 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()); 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());
...@@ -1388,6 +1393,8 @@ void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { ...@@ -1388,6 +1393,8 @@ void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
} }
void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) { void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
int body_slot = AllocateBlockCoverageSlotIfEnabled(stmt->body_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;
...@@ -1402,11 +1409,14 @@ void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) { ...@@ -1402,11 +1409,14 @@ 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());
if (stmt->init() != nullptr) { if (stmt->init() != nullptr) {
Visit(stmt->init()); Visit(stmt->init());
} }
...@@ -1425,6 +1435,7 @@ void BytecodeGenerator::VisitForStatement(ForStatement* stmt) { ...@@ -1425,6 +1435,7 @@ 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());
......
...@@ -96,9 +96,13 @@ class SourceRangeScope final { ...@@ -96,9 +96,13 @@ class SourceRangeScope final {
PositionKind post_kind = POSITION) PositionKind post_kind = POSITION)
: scanner_(scanner), range_(range), post_kind_(post_kind) { : scanner_(scanner), range_(range), post_kind_(post_kind) {
range_->start = GetPosition(pre_kind); range_->start = GetPosition(pre_kind);
DCHECK_NE(range_->start, kNoSourcePosition);
} }
~SourceRangeScope() { range_->end = GetPosition(post_kind_); } ~SourceRangeScope() {
range_->end = GetPosition(post_kind_);
DCHECK_NE(range_->end, kNoSourcePosition);
}
private: private:
int32_t GetPosition(PositionKind kind) { int32_t GetPosition(PositionKind kind) {
...@@ -5357,8 +5361,14 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDoWhileStatement( ...@@ -5357,8 +5361,14 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDoWhileStatement(
auto loop = factory()->NewDoWhileStatement(labels, peek_position()); auto loop = factory()->NewDoWhileStatement(labels, peek_position());
typename Types::Target target(this, loop); typename Types::Target target(this, loop);
SourceRange body_range;
StatementT body = impl()->NullStatement();
Expect(Token::DO, CHECK_OK); Expect(Token::DO, CHECK_OK);
StatementT body = ParseStatement(nullptr, CHECK_OK); {
SourceRangeScope range_scope(scanner(), &body_range);
body = ParseStatement(nullptr, CHECK_OK);
}
Expect(Token::WHILE, CHECK_OK); Expect(Token::WHILE, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK); Expect(Token::LPAREN, CHECK_OK);
...@@ -5371,7 +5381,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDoWhileStatement( ...@@ -5371,7 +5381,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDoWhileStatement(
// ExpectSemicolon() functionality here. // ExpectSemicolon() functionality here.
Check(Token::SEMICOLON); Check(Token::SEMICOLON);
loop->Initialize(cond, body); loop->Initialize(cond, body, body_range);
return loop; return loop;
} }
...@@ -5384,13 +5394,19 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWhileStatement( ...@@ -5384,13 +5394,19 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWhileStatement(
auto loop = factory()->NewWhileStatement(labels, peek_position()); auto loop = factory()->NewWhileStatement(labels, peek_position());
typename Types::Target target(this, loop); typename Types::Target target(this, loop);
SourceRange body_range;
StatementT body = impl()->NullStatement();
Expect(Token::WHILE, CHECK_OK); Expect(Token::WHILE, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK); Expect(Token::LPAREN, CHECK_OK);
ExpressionT cond = ParseExpression(true, CHECK_OK); ExpressionT cond = ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
StatementT body = ParseStatement(nullptr, CHECK_OK); {
SourceRangeScope range_scope(scanner(), &body_range);
body = ParseStatement(nullptr, CHECK_OK);
}
loop->Initialize(cond, body); loop->Initialize(cond, body, body_range);
return loop; return loop;
} }
...@@ -5774,6 +5790,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStandardForLoop( ...@@ -5774,6 +5790,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStandardForLoop(
StatementT next = impl()->NullStatement(); StatementT next = impl()->NullStatement();
StatementT body = impl()->NullStatement(); StatementT body = impl()->NullStatement();
SourceRange body_range;
// If there are let bindings, then condition and the next statement of the // If there are let bindings, then condition and the next statement of the
// for loop must be parsed in a new scope. // for loop must be parsed in a new scope.
Scope* inner_scope = scope(); Scope* inner_scope = scope();
...@@ -5795,6 +5813,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStandardForLoop( ...@@ -5795,6 +5813,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStandardForLoop(
} }
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
SourceRangeScope range_scope(scanner(), &body_range);
body = ParseStatement(nullptr, CHECK_OK); body = ParseStatement(nullptr, CHECK_OK);
} }
...@@ -5804,7 +5823,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStandardForLoop( ...@@ -5804,7 +5823,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStandardForLoop(
(is_resumable() || function_state_->contains_function_or_eval())) { (is_resumable() || function_state_->contains_function_or_eval())) {
scope()->set_is_hidden(); scope()->set_is_hidden();
return impl()->DesugarLexicalBindingsInForStatement( return impl()->DesugarLexicalBindingsInForStatement(
loop, init, cond, next, body, inner_scope, *for_info, CHECK_OK); loop, init, cond, next, body, body_range, inner_scope, *for_info,
CHECK_OK);
} }
Scope* for_scope = scope()->FinalizeBlockScope(); Scope* for_scope = scope()->FinalizeBlockScope();
...@@ -5834,11 +5854,11 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStandardForLoop( ...@@ -5834,11 +5854,11 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStandardForLoop(
} }
block->statements()->Add(loop, zone()); block->statements()->Add(loop, zone());
block->set_scope(for_scope); block->set_scope(for_scope);
loop->Initialize(init, cond, next, body); loop->Initialize(init, cond, next, body, body_range);
return block; return block;
} }
loop->Initialize(init, cond, next, body); loop->Initialize(init, cond, next, body, body_range);
return loop; return loop;
} }
......
...@@ -2202,7 +2202,8 @@ Statement* Parser::InitializeForOfStatement( ...@@ -2202,7 +2202,8 @@ Statement* Parser::InitializeForOfStatement(
Statement* Parser::DesugarLexicalBindingsInForStatement( Statement* Parser::DesugarLexicalBindingsInForStatement(
ForStatement* loop, Statement* init, Expression* cond, Statement* next, ForStatement* loop, Statement* init, Expression* cond, Statement* next,
Statement* body, Scope* inner_scope, const ForInfo& for_info, bool* ok) { Statement* body, const SourceRange& body_range, Scope* inner_scope,
const ForInfo& for_info, bool* ok) {
// ES6 13.7.4.8 specifies that on each loop iteration the let variables are // ES6 13.7.4.8 specifies that on each loop iteration the let variables are
// copied into a new environment. Moreover, the "next" statement must be // copied into a new environment. Moreover, the "next" statement must be
// evaluated not in the environment of the just completed iteration but in // evaluated not in the environment of the just completed iteration but in
...@@ -2432,7 +2433,7 @@ Statement* Parser::DesugarLexicalBindingsInForStatement( ...@@ -2432,7 +2433,7 @@ Statement* Parser::DesugarLexicalBindingsInForStatement(
inner_block->set_scope(inner_scope); inner_block->set_scope(inner_scope);
} }
outer_loop->Initialize(NULL, NULL, NULL, inner_block); outer_loop->Initialize(NULL, NULL, NULL, inner_block, body_range);
return outer_block; return outer_block;
} }
......
...@@ -499,7 +499,8 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { ...@@ -499,7 +499,8 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
Statement* DesugarLexicalBindingsInForStatement( Statement* DesugarLexicalBindingsInForStatement(
ForStatement* loop, Statement* init, Expression* cond, Statement* next, ForStatement* loop, Statement* init, Expression* cond, Statement* next,
Statement* body, Scope* inner_scope, const ForInfo& for_info, bool* ok); Statement* body, const SourceRange& body_range, Scope* inner_scope,
const ForInfo& for_info, bool* ok);
Expression* RewriteDoExpression(Block* body, int pos, bool* ok); Expression* RewriteDoExpression(Block* body, int pos, bool* ok);
......
...@@ -522,9 +522,11 @@ class PreParserStatement { ...@@ -522,9 +522,11 @@ class PreParserStatement {
PreParserStatementList statements() { return PreParserStatementList(); } PreParserStatementList statements() { return PreParserStatementList(); }
void set_scope(Scope* scope) {} void set_scope(Scope* scope) {}
void Initialize(PreParserExpression cond, PreParserStatement body) {} void Initialize(PreParserExpression cond, PreParserStatement body,
const SourceRange& body_range = {}) {}
void Initialize(PreParserStatement init, PreParserExpression cond, void Initialize(PreParserStatement init, PreParserExpression cond,
PreParserStatement next, PreParserStatement body) {} PreParserStatement next, PreParserStatement body,
const SourceRange& body_range = {}) {}
private: private:
enum Type { enum Type {
...@@ -1359,8 +1361,8 @@ class PreParser : public ParserBase<PreParser> { ...@@ -1359,8 +1361,8 @@ class PreParser : public ParserBase<PreParser> {
V8_INLINE StatementT DesugarLexicalBindingsInForStatement( V8_INLINE StatementT DesugarLexicalBindingsInForStatement(
PreParserStatement loop, PreParserStatement init, PreParserStatement loop, PreParserStatement init,
PreParserExpression cond, PreParserStatement next, PreParserExpression cond, PreParserStatement next,
PreParserStatement body, Scope* inner_scope, const ForInfo& for_info, PreParserStatement body, const SourceRange& body_range,
bool* ok) { Scope* inner_scope, const ForInfo& for_info, bool* ok) {
// See Parser::DesugarLexicalBindingsInForStatement. // See Parser::DesugarLexicalBindingsInForStatement.
if (track_unresolved_variables_) { if (track_unresolved_variables_) {
for (auto name : for_info.bound_names) { for (auto name : for_info.bound_names) {
......
...@@ -17,10 +17,13 @@ function TestCoverage(name, source, expectation) { ...@@ -17,10 +17,13 @@ function TestCoverage(name, source, expectation) {
source = source.trim(); source = source.trim();
eval(source); eval(source);
%CollectGarbage("collect dead objects"); %CollectGarbage("collect dead objects");
var coverage = GetCoverage(source); var covfefe = GetCoverage(source);
var result = JSON.stringify(coverage); var stringified_result = JSON.stringify(covfefe);
print(result); var stringified_expectation = JSON.stringify(expectation);
assertEquals(JSON.stringify(expectation), result, name + " failed"); if (stringified_result != stringified_expectation) {
print(JSON.stringify(covfefe, undefined, 1));
}
assertEquals(stringified_expectation, stringified_result, name + " failed");
} }
%DebugToggleBlockCoverage(true); %DebugToggleBlockCoverage(true);
...@@ -35,13 +38,8 @@ TestCoverage( ...@@ -35,13 +38,8 @@ TestCoverage(
TestCoverage( TestCoverage(
"call locally allocated function", "call locally allocated function",
` `let f = () => 1; f();`,
for (var i = 0; i < 10; i++) { [{"start":0,"end":21,"count":1},{"start":8,"end":15,"count":1}]
let f = () => 1;
i += f();
}
`,
[{"start":0,"end":63,"count":1},{"start":41,"end":48,"count":5}]
); );
TestCoverage( TestCoverage(
...@@ -80,4 +78,83 @@ f(43); ...@@ -80,4 +78,83 @@ f(43);
{"start":236,"end":241,"count":2}] {"start":236,"end":241,"count":2}]
); );
TestCoverage(
"for statements",
`
function g() {}
!function() {
for (var i = 0; i < 12; i++) g();
for (var i = 0; i < 12; i++) {
g();
}
for (var i = 0; false; i++) g();
for (var i = 0; true; i++) break;
for (var i = 0; i < 12; i++) {
if (i % 3 == 0) g(); else g();
}
}();
`,
[{"start":0,"end":259,"count":1},
{"start":0,"end":15,"count":36},
{"start":17,"end":256,"count":1},
{"start":59,"end":64,"count":12},
{"start":95,"end":110,"count":12},
{"start":140,"end":145,"count":0},
{"start":174,"end":181,"count":1},
{"start":212,"end":253,"count":12},
{"start":234,"end":239,"count":4},
{"start":241,"end":249,"count":8}]
);
TestCoverage(
"for statements pt. 2",
`
function g() {}
!function() {
let j = 0;
for (let i = 0; i < 12; i++) g();
for (const i = 0; j < 12; j++) g();
for (j = 0; j < 12; j++) g();
for (;;) break;
}();
`,
[{"start":0,"end":171,"count":1},
{"start":0,"end":15,"count":36},
{"start":17,"end":168,"count":1},
{"start":72,"end":77,"count":12},
{"start":110,"end":115,"count":12},
{"start":142,"end":147,"count":12},
{"start":158,"end":165,"count":1}]
);
TestCoverage(
"while and do-while statements",
`
function g() {}
!function() {
var i;
i = 0; while (i < 12) i++;
i = 0; while (i < 12) { g(); i++; }
i = 0; while (false) g();
i = 0; while (true) break;
i = 0; do i++; while (i < 12);
i = 0; do { g(); i++; } while (i < 12);
i = 0; do { g(); } while (false);
i = 0; do { break; } while (true);
}();
`,
[{"start":0,"end":316,"count":1},
{"start":0,"end":15,"count":25},
{"start":17,"end":313,"count":1},
{"start":61,"end":66,"count":12},
{"start":90,"end":104,"count":12},
{"start":127,"end":132,"count":0},
{"start":154,"end":161,"count":1},
{"start":173,"end":179,"count":12},
{"start":206,"end":221,"count":12},
{"start":248,"end":258,"count":1},
{"start":284,"end":296,"count":1}]
);
%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