[coverage] Add dedicated FunctionLiteral counters

Prior to this CL, call counts at function scope were taken from the
FeedbackVector::invocation_count field. This had two major drawbacks:
1. for generator functions, these count the number of resumptions
instead of the number of calls; and 2. the invocation count is not
maintained in optimized code.

The solution implemented here is to add a dedicated call counter at
function scope which is incremented exactly once each time the
function is called.

A minor complication is that our coverage output format expects
function-scope counts in the dedicated CoverageFunction object, and
not as a CoverageBlock. Thus function-scope block counts are initially
marked with magic positions, and later recognized and rewritten during
processing.

This CL thus fixes reported generator function call counts and enables
optimizations in block coverage modes (more to come in a follow-up).

Drive-by: Don't report functions with empty source ranges.

Bug: v8:6000,v8:9148,v8:9212
Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_layout_ng
Change-Id: Idbe5edb35a595cf12b6649314738ac00efd173b8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1613996
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61574}
This commit is contained in:
Jakob Gruber 2019-05-16 13:56:18 +02:00 committed by Commit Bot
parent 3cb560adfe
commit 3002ff44ee
11 changed files with 241 additions and 177 deletions

View File

@ -25,6 +25,18 @@ struct SourceRange {
int end = kNoSourcePosition) { int end = kNoSourcePosition) {
return that.IsEmpty() ? Empty() : SourceRange(that.end, end); return that.IsEmpty() ? Empty() : SourceRange(that.end, end);
} }
static constexpr int kFunctionLiteralSourcePosition = -2;
STATIC_ASSERT(kFunctionLiteralSourcePosition == kNoSourcePosition - 1);
// Source ranges associated with a function literal do not contain real
// source positions; instead, they are created with special marker values.
// These are later recognized and rewritten during processing in
// Coverage::Collect().
static SourceRange FunctionLiteralMarkerRange() {
return {kFunctionLiteralSourcePosition, kFunctionLiteralSourcePosition};
}
int32_t start, end; int32_t start, end;
}; };
@ -35,6 +47,7 @@ struct SourceRange {
V(Block) \ V(Block) \
V(CaseClause) \ V(CaseClause) \
V(Conditional) \ V(Conditional) \
V(FunctionLiteral) \
V(IfStatement) \ V(IfStatement) \
V(IterationStatement) \ V(IterationStatement) \
V(JumpStatement) \ V(JumpStatement) \
@ -155,6 +168,18 @@ class ConditionalSourceRanges final : public AstNodeSourceRanges {
SourceRange else_range_; SourceRange else_range_;
}; };
class FunctionLiteralSourceRanges final : public AstNodeSourceRanges {
public:
SourceRange GetRange(SourceRangeKind kind) override {
DCHECK(HasRange(kind));
return SourceRange::FunctionLiteralMarkerRange();
}
bool HasRange(SourceRangeKind kind) override {
return kind == SourceRangeKind::kBody;
}
};
class IfStatementSourceRanges final : public AstNodeSourceRanges { class IfStatementSourceRanges final : public AstNodeSourceRanges {
public: public:
explicit IfStatementSourceRanges(const SourceRange& then_range, explicit IfStatementSourceRanges(const SourceRange& then_range,

View File

@ -4,6 +4,7 @@
#include "src/debug/debug-coverage.h" #include "src/debug/debug-coverage.h"
#include "src/ast/ast-source-ranges.h"
#include "src/ast/ast.h" #include "src/ast/ast.h"
#include "src/base/hashmap.h" #include "src/base/hashmap.h"
#include "src/debug/debug.h" #include "src/debug/debug.h"
@ -103,11 +104,7 @@ std::vector<CoverageBlock> GetSortedBlockData(SharedFunctionInfo shared) {
class CoverageBlockIterator final { class CoverageBlockIterator final {
public: public:
explicit CoverageBlockIterator(CoverageFunction* function) explicit CoverageBlockIterator(CoverageFunction* function)
: function_(function), : function_(function) {
ended_(false),
delete_current_(false),
read_index_(-1),
write_index_(-1) {
DCHECK(std::is_sorted(function_->blocks.begin(), function_->blocks.end(), DCHECK(std::is_sorted(function_->blocks.begin(), function_->blocks.end(),
CompareCoverageBlock)); CompareCoverageBlock));
} }
@ -223,10 +220,10 @@ class CoverageBlockIterator final {
CoverageFunction* function_; CoverageFunction* function_;
std::vector<CoverageBlock> nesting_stack_; std::vector<CoverageBlock> nesting_stack_;
bool ended_; bool ended_ = false;
bool delete_current_; bool delete_current_ = false;
int read_index_; int read_index_ = -1;
int write_index_; int write_index_ = -1;
}; };
bool HaveSameSourceRange(const CoverageBlock& lhs, const CoverageBlock& rhs) { bool HaveSameSourceRange(const CoverageBlock& lhs, const CoverageBlock& rhs) {
@ -312,6 +309,30 @@ void MergeNestedRanges(CoverageFunction* function) {
} }
} }
void RewriteFunctionScopeCounter(CoverageFunction* function) {
// Every function must have at least the top-level function counter.
DCHECK(!function->blocks.empty());
CoverageBlockIterator iter(function);
if (iter.Next()) {
DCHECK(iter.IsTopLevel());
CoverageBlock& block = iter.GetBlock();
if (block.start == SourceRange::kFunctionLiteralSourcePosition &&
block.end == SourceRange::kFunctionLiteralSourcePosition) {
// If a function-scope block exists, overwrite the function count. It has
// a more reliable count than what we get from the FeedbackVector (which
// is imprecise e.g. for generator functions and optimized code).
function->count = block.count;
// Then delete it; for compatibility with non-block coverage modes, the
// function-scope block is expected in CoverageFunction, not as a
// CoverageBlock.
iter.DeleteBlock();
}
}
}
void FilterAliasedSingletons(CoverageFunction* function) { void FilterAliasedSingletons(CoverageFunction* function) {
CoverageBlockIterator iter(function); CoverageBlockIterator iter(function);
@ -395,16 +416,32 @@ bool IsBinaryMode(debug::CoverageMode mode) {
} }
} }
void CollectBlockCoverage(CoverageFunction* function, SharedFunctionInfo info, void CollectBlockCoverageInternal(CoverageFunction* function,
SharedFunctionInfo info,
debug::CoverageMode mode) { debug::CoverageMode mode) {
DCHECK(IsBlockMode(mode)); DCHECK(IsBlockMode(mode));
// Functions with empty source ranges are not interesting to report. This can
// happen e.g. for internally-generated functions like class constructors.
if (!function->HasNonEmptySourceRange()) return;
function->has_block_coverage = true; function->has_block_coverage = true;
function->blocks = GetSortedBlockData(info); function->blocks = GetSortedBlockData(info);
// If in binary mode, only report counts of 0/1. // If in binary mode, only report counts of 0/1.
if (mode == debug::CoverageMode::kBlockBinary) ClampToBinary(function); if (mode == debug::CoverageMode::kBlockBinary) ClampToBinary(function);
// To stay compatible with non-block coverage modes, the function-scope count
// is expected to be in the CoverageFunction, not as part of its blocks.
// This finds the function-scope counter, overwrites CoverageFunction::count,
// and removes it from the block list.
//
// Important: Must be called before other transformation passes.
RewriteFunctionScopeCounter(function);
// Functions without blocks don't need to be processed further.
if (!function->HasBlocks()) return;
// Remove singleton ranges with the same start position as a full range and // Remove singleton ranges with the same start position as a full range and
// throw away their counts. // throw away their counts.
// Singleton ranges are only intended to split existing full ranges and should // Singleton ranges are only intended to split existing full ranges and should
@ -435,6 +472,11 @@ void CollectBlockCoverage(CoverageFunction* function, SharedFunctionInfo info,
// Filter out ranges of zero length. // Filter out ranges of zero length.
FilterEmptyRanges(function); FilterEmptyRanges(function);
}
void CollectBlockCoverage(CoverageFunction* function, SharedFunctionInfo info,
debug::CoverageMode mode) {
CollectBlockCoverageInternal(function, info, mode);
// Reset all counters on the DebugInfo to zero. // Reset all counters on the DebugInfo to zero.
ResetAllBlockCounts(info); ResetAllBlockCounts(info);
@ -589,12 +631,17 @@ std::unique_ptr<Coverage> Coverage::Collect(
} }
// Only include a function range if itself or its parent function is // Only include a function range if itself or its parent function is
// covered, or if it contains non-trivial block coverage. // covered, or if it contains non-trivial block coverage. It must also
// have a non-empty source range (otherwise it is not interesting to
// report).
bool is_covered = (count != 0); bool is_covered = (count != 0);
bool parent_is_covered = bool parent_is_covered =
(!nesting.empty() && functions->at(nesting.back()).count != 0); (!nesting.empty() && functions->at(nesting.back()).count != 0);
bool has_block_coverage = !function.blocks.empty(); bool has_block_coverage = !function.blocks.empty();
if (is_covered || parent_is_covered || has_block_coverage) { bool function_is_relevant =
(is_covered || parent_is_covered || has_block_coverage);
if (function.HasNonEmptySourceRange() && function_is_relevant) {
nesting.push_back(functions->size()); nesting.push_back(functions->size());
functions->emplace_back(function); functions->emplace_back(function);
} }

View File

@ -20,6 +20,7 @@ class Isolate;
struct CoverageBlock { struct CoverageBlock {
CoverageBlock(int s, int e, uint32_t c) : start(s), end(e), count(c) {} CoverageBlock(int s, int e, uint32_t c) : start(s), end(e), count(c) {}
CoverageBlock() : CoverageBlock(kNoSourcePosition, kNoSourcePosition, 0) {} CoverageBlock() : CoverageBlock(kNoSourcePosition, kNoSourcePosition, 0) {}
int start; int start;
int end; int end;
uint32_t count; uint32_t count;
@ -28,6 +29,10 @@ struct CoverageBlock {
struct CoverageFunction { struct CoverageFunction {
CoverageFunction(int s, int e, uint32_t c, Handle<String> n) CoverageFunction(int s, int e, uint32_t c, Handle<String> n)
: start(s), end(e), count(c), name(n), has_block_coverage(false) {} : start(s), end(e), count(c), name(n), has_block_coverage(false) {}
bool HasNonEmptySourceRange() const { return start < end && start >= 0; }
bool HasBlocks() const { return !blocks.empty(); }
int start; int start;
int end; int end;
uint32_t count; uint32_t count;

View File

@ -1128,7 +1128,8 @@ void BytecodeGenerator::GenerateBytecodeBody() {
// Create a generator object if necessary and initialize the // Create a generator object if necessary and initialize the
// {.generator_object} variable. // {.generator_object} variable.
if (IsResumableFunction(info()->literal()->kind())) { FunctionLiteral* literal = info()->literal();
if (IsResumableFunction(literal->kind())) {
BuildGeneratorObjectVariableInitialization(); BuildGeneratorObjectVariableInitialization();
} }
@ -1146,6 +1147,9 @@ void BytecodeGenerator::GenerateBytecodeBody() {
} }
} }
// Increment the function-scope block coverage counter.
BuildIncrementBlockCoverageCounterIfEnabled(literal, SourceRangeKind::kBody);
// Visit declarations within the function scope. // Visit declarations within the function scope.
VisitDeclarations(closure_scope()->declarations()); VisitDeclarations(closure_scope()->declarations());
@ -1153,22 +1157,22 @@ void BytecodeGenerator::GenerateBytecodeBody() {
VisitModuleNamespaceImports(); VisitModuleNamespaceImports();
// Perform a stack-check before the body. // Perform a stack-check before the body.
builder()->StackCheck(info()->literal()->start_position()); builder()->StackCheck(literal->start_position());
// The derived constructor case is handled in VisitCallSuper. // The derived constructor case is handled in VisitCallSuper.
if (IsBaseConstructor(function_kind())) { if (IsBaseConstructor(function_kind())) {
if (info()->literal()->requires_brand_initialization()) { if (literal->requires_brand_initialization()) {
BuildPrivateBrandInitialization(builder()->Receiver()); BuildPrivateBrandInitialization(builder()->Receiver());
} }
if (info()->literal()->requires_instance_members_initializer()) { if (literal->requires_instance_members_initializer()) {
BuildInstanceMemberInitialization(Register::function_closure(), BuildInstanceMemberInitialization(Register::function_closure(),
builder()->Receiver()); builder()->Receiver());
} }
} }
// Visit statements in the function body. // Visit statements in the function body.
VisitStatements(info()->literal()->body()); VisitStatements(literal->body());
// Emit an implicit return instruction in case control flow can fall off the // Emit an implicit return instruction in case control flow can fall off the
// end of the function without an explicit return being present on all paths. // end of the function without an explicit return being present on all paths.

View File

@ -4177,6 +4177,7 @@ ParserBase<Impl>::ParseArrowFunctionLiteral(
function_literal->set_function_token_position( function_literal->set_function_token_position(
formal_parameters.scope->start_position()); formal_parameters.scope->start_position());
impl()->RecordFunctionLiteralSourceRange(function_literal);
impl()->AddFunctionForNameInference(function_literal); impl()->AddFunctionForNameInference(function_literal);
if (V8_UNLIKELY((FLAG_log_function_events))) { if (V8_UNLIKELY((FLAG_log_function_events))) {

View File

@ -638,6 +638,9 @@ FunctionLiteral* Parser::DoParseProgram(Isolate* isolate, ParseInfo* info) {
DCHECK_NULL(target_stack_); DCHECK_NULL(target_stack_);
if (has_error()) return nullptr; if (has_error()) return nullptr;
RecordFunctionLiteralSourceRange(result);
return result; return result;
} }
@ -2450,6 +2453,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
function_literal->set_function_token_position(function_token_pos); function_literal->set_function_token_position(function_token_pos);
function_literal->set_suspend_count(suspend_count); function_literal->set_suspend_count(suspend_count);
RecordFunctionLiteralSourceRange(function_literal);
if (should_post_parallel_task) { if (should_post_parallel_task) {
// Start a parallel parse / compile task on the compiler dispatcher. // Start a parallel parse / compile task on the compiler dispatcher.
info()->parallel_tasks()->Enqueue(info(), function_name, function_literal); info()->parallel_tasks()->Enqueue(info(), function_name, function_literal);
@ -2884,12 +2889,16 @@ FunctionLiteral* Parser::CreateInitializerFunction(
InitializeClassMembersStatement* stmt = InitializeClassMembersStatement* stmt =
factory()->NewInitializeClassMembersStatement(fields, kNoSourcePosition); factory()->NewInitializeClassMembersStatement(fields, kNoSourcePosition);
statements.Add(stmt); statements.Add(stmt);
return factory()->NewFunctionLiteral( FunctionLiteral* result = factory()->NewFunctionLiteral(
ast_value_factory()->GetOneByteString(name), scope, statements, 0, 0, 0, ast_value_factory()->GetOneByteString(name), scope, statements, 0, 0, 0,
FunctionLiteral::kNoDuplicateParameters, FunctionLiteral::kNoDuplicateParameters,
FunctionLiteral::kAnonymousExpression, FunctionLiteral::kAnonymousExpression,
FunctionLiteral::kShouldEagerCompile, scope->start_position(), false, FunctionLiteral::kShouldEagerCompile, scope->start_position(), false,
GetNextFunctionLiteralId()); GetNextFunctionLiteralId());
RecordFunctionLiteralSourceRange(result);
return result;
} }
// This method generates a ClassLiteral AST node. // This method generates a ClassLiteral AST node.

View File

@ -933,6 +933,11 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
new (zone()) ConditionalSourceRanges(then_range, else_range)); new (zone()) ConditionalSourceRanges(then_range, else_range));
} }
V8_INLINE void RecordFunctionLiteralSourceRange(FunctionLiteral* node) {
if (source_range_map_ == nullptr) return;
source_range_map_->Insert(node, new (zone()) FunctionLiteralSourceRanges);
}
V8_INLINE void RecordBinaryOperationSourceRange( V8_INLINE void RecordBinaryOperationSourceRange(
Expression* node, const SourceRange& right_range) { Expression* node, const SourceRange& right_range) {
if (source_range_map_ == nullptr) return; if (source_range_map_ == nullptr) return;

View File

@ -812,22 +812,6 @@ Running test: testPreciseBinaryCoverage
[0] : { [0] : {
functions : [ functions : [
[0] : { [0] : {
functionName : fib
isBlockCoverage : true
ranges : [
[0] : {
count : 0
endOffset : 73
startOffset : 1
}
[1] : {
count : 1
endOffset : 72
startOffset : 32
}
]
}
[1] : {
functionName : is_optimized functionName : is_optimized
isBlockCoverage : true isBlockCoverage : true
ranges : [ ranges : [

View File

@ -22,9 +22,8 @@ f(); f(); %OptimizeFunctionOnNextCall(f); // 0100
f(); f(); f(); f(); f(); f(); // 0150 f(); f(); f(); f(); f(); f(); // 0150
`, `,
[{"start":0,"end":199,"count":1}, [{"start":0,"end":199,"count":1},
{"start":0,"end":33,"count":4}, // TODO(jgruber): Invocation count is off. {"start":0,"end":33,"count":16},
{"start":25,"end":31,"count":16}, {"start":50,"end":76,"count":8}]
{"start":50,"end":76,"count":2}] // TODO(jgruber): Invocation count is off.
); );
// This test is tricky: it requires a non-toplevel, optimized function. // This test is tricky: it requires a non-toplevel, optimized function.
@ -44,8 +43,8 @@ TestCoverage("Partial coverage collection",
f(false); // 0350 f(false); // 0350
}(); // 0400 }(); // 0400
`, `,
[{"start":52,"end":153,"count":0}, [{"start":52,"end":153,"count":1},
{"start":121,"end":137,"count":1}] {"start":111,"end":121,"count":0}]
); );
%DebugToggleBlockCoverage(false); %DebugToggleBlockCoverage(false);

View File

@ -216,9 +216,8 @@ TestCoverage(
%PerformMicrotaskCheckpoint(); // 0250 %PerformMicrotaskCheckpoint(); // 0250
`, `,
[{"start":0,"end":299,"count":1}, [{"start":0,"end":299,"count":1},
{"start":1,"end":201,"count":6}, // TODO(jgruber): Invocation count is off. {"start":1,"end":201,"count":1},
{"start":83,"end":153,"count":4}, {"start":83,"end":153,"count":4}]
{"start":153,"end":200,"count":1}]
); );
TestCoverage( TestCoverage(
@ -415,7 +414,7 @@ TestCoverage(
{"start":286,"end":350,"count":0}, {"start":286,"end":350,"count":0},
{"start":401,"end":701,"count":1}, {"start":401,"end":701,"count":1},
{"start":603,"end":700,"count":0}, {"start":603,"end":700,"count":0},
{"start":561,"end":568,"count":0}, // TODO(jgruber): Sorting. {"start":561,"end":568,"count":0},
{"start":751,"end":1051,"count":1}, {"start":751,"end":1051,"count":1},
{"start":819,"end":820,"count":0}, {"start":819,"end":820,"count":0},
{"start":861,"end":1050,"count":0}] {"start":861,"end":1050,"count":0}]
@ -540,10 +539,25 @@ const it = function*() { // 0000
it.next(); it.next(); // 0250 it.next(); it.next(); // 0250
`, `,
[{"start":0,"end":299,"count":1}, [{"start":0,"end":299,"count":1},
{"start":11,"end":201,"count":3}, {"start":11,"end":201,"count":1},
{"start":64,"end":114,"count":1}, {"start":114,"end":121,"count":0},
{"start":129,"end":200,"count":0}]
);
TestCoverage(
"yield expressions twice",
`
function* gen() { // 0000
yield nop(); // 0050
yield nop() ? nop() : nop() // 0100
return nop(); // 0150
}; // 0200
{const it = gen(); it.next(); it.next();} // 0250
{const it = gen(); it.next(); it.next();} // 0300
`,
[{"start":0,"end":349,"count":1},
{"start":0,"end":201,"count":2},
{"start":114,"end":121,"count":0}, {"start":114,"end":121,"count":0},
{"start":122,"end":129,"count":1},
{"start":129,"end":200,"count":0}] {"start":129,"end":200,"count":0}]
); );
@ -563,9 +577,9 @@ try { // 0200
`, `,
[{"start":0,"end":499,"count":1}, [{"start":0,"end":499,"count":1},
{"start":451,"end":452,"count":0}, {"start":451,"end":452,"count":0},
{"start":12,"end":101,"count":3}, {"start":12,"end":101,"count":1},
{"start":60,"end":100,"count":0}, {"start":60,"end":100,"count":0},
{"start":264,"end":353,"count":3}, {"start":264,"end":353,"count":1},
{"start":312,"end":352,"count":0}] {"start":312,"end":352,"count":0}]
); );
@ -582,9 +596,8 @@ const it = function*() { // 0000
it.next(); it.return(); // 0450 it.next(); it.return(); // 0450
`, `,
[{"start":0,"end":449,"count":1}, [{"start":0,"end":449,"count":1},
{"start":11,"end":351,"count":3}, {"start":11,"end":351,"count":1},
{"start":112,"end":254,"count":0}, {"start":112,"end":254,"count":0},
{"start":254,"end":272,"count":1},
{"start":272,"end":350,"count":0}] {"start":272,"end":350,"count":0}]
); );
@ -601,9 +614,8 @@ const it = function*() { // 0000
it.next(); it.throw(42); // 0550 it.next(); it.throw(42); // 0550
`, `,
[{"start":0,"end":449,"count":1}, [{"start":0,"end":449,"count":1},
{"start":11,"end":351,"count":3}, {"start":11,"end":351,"count":1},
{"start":112,"end":154,"count":0}, {"start":112,"end":154,"count":0},
{"start":154,"end":310,"count":1},
{"start":310,"end":350,"count":0}] {"start":310,"end":350,"count":0}]
); );
@ -619,10 +631,8 @@ it.next(); it.next(); it.next(); // 0250
it.next(); it.next(); it.next(); // 0300 it.next(); it.next(); it.next(); // 0300
`, `,
[{"start":0,"end":349,"count":1}, [{"start":0,"end":349,"count":1},
{"start":11,"end":201,"count":7}, {"start":11,"end":201,"count":1},
{"start":65,"end":115,"count":1},
{"start":115,"end":122,"count":0}, {"start":115,"end":122,"count":0},
{"start":123,"end":130,"count":1},
{"start":130,"end":200,"count":0}] {"start":130,"end":200,"count":0}]
); );
@ -642,9 +652,9 @@ try { // 0200
`, `,
[{"start":0,"end":499,"count":1}, [{"start":0,"end":499,"count":1},
{"start":451,"end":452,"count":0}, {"start":451,"end":452,"count":0},
{"start":12,"end":101,"count":3}, {"start":12,"end":101,"count":1},
{"start":65,"end":100,"count":0}, {"start":65,"end":100,"count":0},
{"start":264,"end":353,"count":3}, {"start":264,"end":353,"count":1},
{"start":317,"end":352,"count":0}] {"start":317,"end":352,"count":0}]
); );
@ -659,8 +669,7 @@ f(); // 0200
%PerformMicrotaskCheckpoint(); // 0250 %PerformMicrotaskCheckpoint(); // 0250
`, `,
[{"start":0,"end":299,"count":1}, [{"start":0,"end":299,"count":1},
{"start":0,"end":151,"count":3}, {"start":0,"end":151,"count":1}]
{"start":61,"end":150,"count":1}]
); );
TestCoverage( TestCoverage(
@ -676,7 +685,8 @@ b() // 0250
[{"start":0,"end":299,"count":1}, [{"start":0,"end":299,"count":1},
{"start":15,"end":20,"count":0}, {"start":15,"end":20,"count":0},
{"start":50,"end":151,"count":2}, {"start":50,"end":151,"count":2},
{"start":114,"end":118,"count":0}]); {"start":114,"end":118,"count":0}]
);
TestCoverage( TestCoverage(
"LogicalOrExpression IsTest()", "LogicalOrExpression IsTest()",
@ -705,7 +715,8 @@ const c = true && 50 // 0300
[{"start":0,"end":349,"count":1}, [{"start":0,"end":349,"count":1},
{"start":16,"end":21,"count":0}, {"start":16,"end":21,"count":0},
{"start":50,"end":151,"count":2}, {"start":50,"end":151,"count":2},
{"start":114,"end":118,"count":0}]); {"start":114,"end":118,"count":0}]
);
TestCoverage( TestCoverage(
"LogicalAndExpression IsTest()", "LogicalAndExpression IsTest()",

View File

@ -10,62 +10,55 @@
TestCoverage( TestCoverage(
"class with no fields", "class with no fields",
`class X { // 000 `
class X { // 000
}; // 050 }; // 050
`, `,
[ [{"start":0,"end":99,"count":1}]
{ start: 0, end: 98, count: 1 },
{ start: 0, end: 0, count: 0 },
]
); );
TestCoverage( TestCoverage(
"class that's not created", "class that's not created",
`class X { // 000 `
class X { // 000
x = function() { } // 050 x = function() { } // 050
}; // 100 }; // 100
`, `,
[ [{"start":0,"end":149,"count":1},
{ start: 0, end: 148, count: 1 }, {"start":52,"end":70,"count":0}]
{ start: 0, end: 0, count: 0 },
{ start: 51, end: 69, count: 0 },
]
); );
TestCoverage( TestCoverage(
"class with field thats not called", "class with field thats not called",
`class X { // 000 `
class X { // 000
x = function() { } // 050 x = function() { } // 050
}; // 100 }; // 100
let x = new X(); // 150 let x = new X(); // 150
`, `,
[ [{"start":0,"end":199,"count":1},
{ start: 0, end: 198, count: 1 }, {"start":52,"end":70,"count":1},
{ start: 0, end: 0, count: 1 }, {"start":56,"end":70,"count":0}]
{ start: 51, end: 69, count: 1 },
{ start: 55, end: 69, count: 0 }
]
); );
TestCoverage( TestCoverage(
"class field", "class field",
`class X { // 000 `
class X { // 000
x = function() { } // 050 x = function() { } // 050
}; // 100 }; // 100
let x = new X(); // 150 let x = new X(); // 150
x.x(); // 200 x.x(); // 200
`, `,
[ [{"start":0,"end":249,"count":1},
{ start: 0, end: 248, count: 1 }, {"start":52,"end":70,"count":1},
{ start: 0, end: 0, count: 1 }, {"start":56,"end":70,"count":1}]
{ start: 51, end: 69, count: 1 },
{ start: 55, end: 69, count: 1 }
]
); );
TestCoverage( TestCoverage(
"non contiguous class field", "non contiguous class field",
`class X { // 000 `
class X { // 000
x = function() { } // 050 x = function() { } // 050
foo() { } // 100 foo() { } // 100
y = function() {} // 150 y = function() {} // 150
@ -74,19 +67,17 @@ let x = new X(); // 250
x.x(); // 300 x.x(); // 300
x.y(); // 350 x.y(); // 350
`, `,
[ [{"start":0,"end":399,"count":1},
{ start: 0, end: 398, count: 1 }, {"start":52,"end":169,"count":1},
{ start: 0, end: 0, count: 1 }, {"start":56,"end":70,"count":1},
{ start: 51, end: 168, count: 1 }, {"start":102,"end":111,"count":0},
{ start: 55, end: 69, count: 1 }, {"start":156,"end":169,"count":1}]
{ start: 101, end: 110, count: 0 },
{ start: 155, end: 168, count: 1 },
]
); );
TestCoverage( TestCoverage(
"non contiguous class field thats called", "non contiguous class field thats called",
`class X { // 000 `
class X { // 000
x = function() { } // 050 x = function() { } // 050
foo() { } // 100 foo() { } // 100
y = function() {} // 150 y = function() {} // 150
@ -96,29 +87,24 @@ x.x(); // 300
x.y(); // 350 x.y(); // 350
x.foo(); // 400 x.foo(); // 400
`, `,
[ [{"start":0,"end":449,"count":1},
{ start: 0, end: 448, count: 1 }, {"start":52,"end":169,"count":1},
{ start: 0, end: 0, count: 1 }, {"start":56,"end":70,"count":1},
{ start: 51, end: 168, count: 1 }, {"start":102,"end":111,"count":1},
{ start: 55, end: 69, count: 1 }, {"start":156,"end":169,"count":1}]
{ start: 101, end: 110, count: 1 },
{ start: 155, end: 168, count: 1 },
]
); );
TestCoverage( TestCoverage(
"class with initializer iife", "class with initializer iife",
`class X { // 000 `
class X { // 000
x = (function() { })() // 050 x = (function() { })() // 050
}; // 100 }; // 100
let x = new X(); // 150 let x = new X(); // 150
`, `,
[ [{"start":0,"end":199,"count":1},
{ start: 0, end: 198, count: 1 }, {"start":52,"end":74,"count":1},
{ start: 0, end: 0, count: 1 }, {"start":57,"end":71,"count":1}]
{ start: 51, end: 73, count: 1 },
{ start: 56, end: 70, count: 1 }
]
); );
TestCoverage( TestCoverage(
@ -130,56 +116,47 @@ class X { // 050
}; // 150 }; // 150
let x = new X(); // 200 let x = new X(); // 200
`, `,
[ [{"start":0,"end":249,"count":1},
{ start: 0, end: 249, count: 1 }, {"start":0,"end":15,"count":1},
{ start: 0, end: 15, count: 1 }, {"start":102,"end":128,"count":1},
{ start: 50, end: 50, count: 1 }, {"start":111,"end":125,"count":1}]
{ start: 102, end: 128, count: 1 },
{ start: 111, end: 125, count: 1 }
]
); );
TestCoverage( TestCoverage(
"static class field that's not called", "static class field that's not called",
`class X { // 000 `
class X { // 000
static x = function() { } // 050 static x = function() { } // 050
}; // 100 }; // 100
`, `,
[ [{"start":0,"end":149,"count":1},
{ start: 0, end: 148, count: 1 }, {"start":52,"end":77,"count":1},
{ start: 0, end: 0, count: 0 }, {"start":63,"end":77,"count":0}]
{ start: 51, end: 76, count: 1 },
{ start: 62, end: 76, count: 0 }
]
); );
TestCoverage( TestCoverage(
"static class field", "static class field",
`class X { // 000 `
class X { // 000
static x = function() { } // 050 static x = function() { } // 050
}; // 100 }; // 100
X.x(); // 150 X.x(); // 150
`, `,
[ [{"start":0,"end":199,"count":1},
{ start: 0, end: 198, count: 1 }, {"start":52,"end":77,"count":1},
{ start: 0, end: 0, count: 0 }, {"start":63,"end":77,"count":1}]
{ start: 51, end: 76, count: 1 },
{ start: 62, end: 76, count: 1 }
]
); );
TestCoverage( TestCoverage(
"static class field with iife", "static class field with iife",
`class X { // 000 `
class X { // 000
static x = (function() { })() // 050 static x = (function() { })() // 050
}; // 100 }; // 100
`, `,
[ [{"start":0,"end":149,"count":1},
{ start: 0, end: 148, count: 1 }, {"start":52,"end":81,"count":1},
{ start: 0, end: 0, count: 0 }, {"start":64,"end":78,"count":1}]
{ start: 51, end: 80, count: 1 },
{ start: 63, end: 77, count: 1 }
]
); );
TestCoverage( TestCoverage(
@ -190,11 +167,8 @@ class X { // 050
static [f()] = (function() { })() // 100 static [f()] = (function() { })() // 100
}; // 150 }; // 150
`, `,
[ [{"start":0,"end":199,"count":1},
{ start: 0, end: 199, count: 1 }, {"start":0,"end":15,"count":1},
{ start: 0, end: 15, count: 1 }, {"start":102,"end":135,"count":1},
{ start: 50, end: 50, count: 0 }, {"start":118,"end":132,"count":1}]
{ start: 102, end: 135, count: 1 },
{ start: 118, end: 132, count: 1 }
]
); );