Commit 3002ff44 authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[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: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61574}
parent 3cb560ad
......@@ -25,6 +25,18 @@ struct SourceRange {
int end = kNoSourcePosition) {
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;
};
......@@ -35,6 +47,7 @@ struct SourceRange {
V(Block) \
V(CaseClause) \
V(Conditional) \
V(FunctionLiteral) \
V(IfStatement) \
V(IterationStatement) \
V(JumpStatement) \
......@@ -155,6 +168,18 @@ class ConditionalSourceRanges final : public AstNodeSourceRanges {
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 {
public:
explicit IfStatementSourceRanges(const SourceRange& then_range,
......
......@@ -4,6 +4,7 @@
#include "src/debug/debug-coverage.h"
#include "src/ast/ast-source-ranges.h"
#include "src/ast/ast.h"
#include "src/base/hashmap.h"
#include "src/debug/debug.h"
......@@ -103,11 +104,7 @@ std::vector<CoverageBlock> GetSortedBlockData(SharedFunctionInfo shared) {
class CoverageBlockIterator final {
public:
explicit CoverageBlockIterator(CoverageFunction* function)
: function_(function),
ended_(false),
delete_current_(false),
read_index_(-1),
write_index_(-1) {
: function_(function) {
DCHECK(std::is_sorted(function_->blocks.begin(), function_->blocks.end(),
CompareCoverageBlock));
}
......@@ -223,10 +220,10 @@ class CoverageBlockIterator final {
CoverageFunction* function_;
std::vector<CoverageBlock> nesting_stack_;
bool ended_;
bool delete_current_;
int read_index_;
int write_index_;
bool ended_ = false;
bool delete_current_ = false;
int read_index_ = -1;
int write_index_ = -1;
};
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) {
CoverageBlockIterator iter(function);
......@@ -395,16 +416,32 @@ bool IsBinaryMode(debug::CoverageMode mode) {
}
}
void CollectBlockCoverage(CoverageFunction* function, SharedFunctionInfo info,
debug::CoverageMode mode) {
void CollectBlockCoverageInternal(CoverageFunction* function,
SharedFunctionInfo info,
debug::CoverageMode 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->blocks = GetSortedBlockData(info);
// If in binary mode, only report counts of 0/1.
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
// throw away their counts.
// 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.
FilterEmptyRanges(function);
}
void CollectBlockCoverage(CoverageFunction* function, SharedFunctionInfo info,
debug::CoverageMode mode) {
CollectBlockCoverageInternal(function, info, mode);
// Reset all counters on the DebugInfo to zero.
ResetAllBlockCounts(info);
......@@ -589,12 +631,17 @@ std::unique_ptr<Coverage> Coverage::Collect(
}
// 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 parent_is_covered =
(!nesting.empty() && functions->at(nesting.back()).count != 0);
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());
functions->emplace_back(function);
}
......
......@@ -20,6 +20,7 @@ class Isolate;
struct CoverageBlock {
CoverageBlock(int s, int e, uint32_t c) : start(s), end(e), count(c) {}
CoverageBlock() : CoverageBlock(kNoSourcePosition, kNoSourcePosition, 0) {}
int start;
int end;
uint32_t count;
......@@ -28,6 +29,10 @@ struct CoverageBlock {
struct CoverageFunction {
CoverageFunction(int s, int e, uint32_t c, Handle<String> n)
: 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 end;
uint32_t count;
......
......@@ -1128,7 +1128,8 @@ void BytecodeGenerator::GenerateBytecodeBody() {
// Create a generator object if necessary and initialize the
// {.generator_object} variable.
if (IsResumableFunction(info()->literal()->kind())) {
FunctionLiteral* literal = info()->literal();
if (IsResumableFunction(literal->kind())) {
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.
VisitDeclarations(closure_scope()->declarations());
......@@ -1153,22 +1157,22 @@ void BytecodeGenerator::GenerateBytecodeBody() {
VisitModuleNamespaceImports();
// 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.
if (IsBaseConstructor(function_kind())) {
if (info()->literal()->requires_brand_initialization()) {
if (literal->requires_brand_initialization()) {
BuildPrivateBrandInitialization(builder()->Receiver());
}
if (info()->literal()->requires_instance_members_initializer()) {
if (literal->requires_instance_members_initializer()) {
BuildInstanceMemberInitialization(Register::function_closure(),
builder()->Receiver());
}
}
// 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
// end of the function without an explicit return being present on all paths.
......
......@@ -4177,6 +4177,7 @@ ParserBase<Impl>::ParseArrowFunctionLiteral(
function_literal->set_function_token_position(
formal_parameters.scope->start_position());
impl()->RecordFunctionLiteralSourceRange(function_literal);
impl()->AddFunctionForNameInference(function_literal);
if (V8_UNLIKELY((FLAG_log_function_events))) {
......
......@@ -638,6 +638,9 @@ FunctionLiteral* Parser::DoParseProgram(Isolate* isolate, ParseInfo* info) {
DCHECK_NULL(target_stack_);
if (has_error()) return nullptr;
RecordFunctionLiteralSourceRange(result);
return result;
}
......@@ -2450,6 +2453,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
function_literal->set_function_token_position(function_token_pos);
function_literal->set_suspend_count(suspend_count);
RecordFunctionLiteralSourceRange(function_literal);
if (should_post_parallel_task) {
// Start a parallel parse / compile task on the compiler dispatcher.
info()->parallel_tasks()->Enqueue(info(), function_name, function_literal);
......@@ -2884,12 +2889,16 @@ FunctionLiteral* Parser::CreateInitializerFunction(
InitializeClassMembersStatement* stmt =
factory()->NewInitializeClassMembersStatement(fields, kNoSourcePosition);
statements.Add(stmt);
return factory()->NewFunctionLiteral(
FunctionLiteral* result = factory()->NewFunctionLiteral(
ast_value_factory()->GetOneByteString(name), scope, statements, 0, 0, 0,
FunctionLiteral::kNoDuplicateParameters,
FunctionLiteral::kAnonymousExpression,
FunctionLiteral::kShouldEagerCompile, scope->start_position(), false,
GetNextFunctionLiteralId());
RecordFunctionLiteralSourceRange(result);
return result;
}
// This method generates a ClassLiteral AST node.
......
......@@ -933,6 +933,11 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
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(
Expression* node, const SourceRange& right_range) {
if (source_range_map_ == nullptr) return;
......
......@@ -34,7 +34,7 @@ Running test: testPreciseCountCoverage
[0] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : true
ranges : [
[0] : {
......@@ -94,7 +94,7 @@ Running test: testPreciseCountCoverage
[1] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : true
ranges : [
[0] : {
......@@ -106,7 +106,7 @@ Running test: testPreciseCountCoverage
}
]
scriptId : <scriptId>
url :
url :
}
]
}
......@@ -137,7 +137,7 @@ Running test: testPreciseCountCoverageIncremental
[0] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : true
ranges : [
[0] : {
......@@ -275,7 +275,7 @@ Running test: testPreciseCountCoverageIncremental
[1] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : true
ranges : [
[0] : {
......@@ -287,12 +287,12 @@ Running test: testPreciseCountCoverageIncremental
}
]
scriptId : <scriptId>
url :
url :
}
[2] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : true
ranges : [
[0] : {
......@@ -304,7 +304,7 @@ Running test: testPreciseCountCoverageIncremental
}
]
scriptId : <scriptId>
url :
url :
}
]
}
......@@ -373,7 +373,7 @@ Running test: testBestEffortCoverageWithPreciseBinaryEnabled
[0] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : false
ranges : [
[0] : {
......@@ -423,7 +423,7 @@ Running test: testBestEffortCoverageWithPreciseBinaryEnabled
[1] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : false
ranges : [
[0] : {
......@@ -435,7 +435,7 @@ Running test: testBestEffortCoverageWithPreciseBinaryEnabled
}
]
scriptId : <scriptId>
url :
url :
}
]
}
......@@ -447,7 +447,7 @@ Running test: testBestEffortCoverageWithPreciseBinaryEnabled
[0] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : false
ranges : [
[0] : {
......@@ -497,7 +497,7 @@ Running test: testBestEffortCoverageWithPreciseBinaryEnabled
[1] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : false
ranges : [
[0] : {
......@@ -509,7 +509,7 @@ Running test: testBestEffortCoverageWithPreciseBinaryEnabled
}
]
scriptId : <scriptId>
url :
url :
}
]
}
......@@ -533,7 +533,7 @@ Running test: testBestEffortCoverageWithPreciseCountEnabled
[0] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : false
ranges : [
[0] : {
......@@ -583,7 +583,7 @@ Running test: testBestEffortCoverageWithPreciseCountEnabled
[1] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : false
ranges : [
[0] : {
......@@ -595,7 +595,7 @@ Running test: testBestEffortCoverageWithPreciseCountEnabled
}
]
scriptId : <scriptId>
url :
url :
}
]
}
......@@ -607,7 +607,7 @@ Running test: testBestEffortCoverageWithPreciseCountEnabled
[0] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : false
ranges : [
[0] : {
......@@ -657,7 +657,7 @@ Running test: testBestEffortCoverageWithPreciseCountEnabled
[1] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : false
ranges : [
[0] : {
......@@ -669,7 +669,7 @@ Running test: testBestEffortCoverageWithPreciseCountEnabled
}
]
scriptId : <scriptId>
url :
url :
}
]
}
......@@ -691,7 +691,7 @@ Running test: testEnablePreciseCountCoverageAtPause
[0] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : true
ranges : [
[0] : {
......@@ -703,7 +703,7 @@ Running test: testEnablePreciseCountCoverageAtPause
}
]
scriptId : <scriptId>
url :
url :
}
]
}
......@@ -727,7 +727,7 @@ Running test: testPreciseBinaryCoverage
[0] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : true
ranges : [
[0] : {
......@@ -812,22 +812,6 @@ Running test: testPreciseBinaryCoverage
[0] : {
functions : [
[0] : {
functionName : fib
isBlockCoverage : true
ranges : [
[0] : {
count : 0
endOffset : 73
startOffset : 1
}
[1] : {
count : 1
endOffset : 72
startOffset : 32
}
]
}
[1] : {
functionName : is_optimized
isBlockCoverage : true
ranges : [
......@@ -845,7 +829,7 @@ Running test: testPreciseBinaryCoverage
[1] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : true
ranges : [
[0] : {
......@@ -857,12 +841,12 @@ Running test: testPreciseBinaryCoverage
}
]
scriptId : <scriptId>
url :
url :
}
[2] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : true
ranges : [
[0] : {
......@@ -874,7 +858,7 @@ Running test: testPreciseBinaryCoverage
}
]
scriptId : <scriptId>
url :
url :
}
]
}
......@@ -905,7 +889,7 @@ Running test: testPreciseCountCoveragePartial
[0] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : true
ranges : [
[0] : {
......@@ -1023,7 +1007,7 @@ Running test: testPreciseCountCoveragePartial
[1] : {
functions : [
[0] : {
functionName :
functionName :
isBlockCoverage : true
ranges : [
[0] : {
......@@ -1035,7 +1019,7 @@ Running test: testPreciseCountCoveragePartial
}
]
scriptId : <scriptId>
url :
url :
}
]
}
......
......@@ -22,9 +22,8 @@ f(); f(); %OptimizeFunctionOnNextCall(f); // 0100
f(); f(); f(); f(); f(); f(); // 0150
`,
[{"start":0,"end":199,"count":1},
{"start":0,"end":33,"count":4}, // TODO(jgruber): Invocation count is off.
{"start":25,"end":31,"count":16},
{"start":50,"end":76,"count":2}] // TODO(jgruber): Invocation count is off.
{"start":0,"end":33,"count":16},
{"start":50,"end":76,"count":8}]
);
// This test is tricky: it requires a non-toplevel, optimized function.
......@@ -44,8 +43,8 @@ TestCoverage("Partial coverage collection",
f(false); // 0350
}(); // 0400
`,
[{"start":52,"end":153,"count":0},
{"start":121,"end":137,"count":1}]
[{"start":52,"end":153,"count":1},
{"start":111,"end":121,"count":0}]
);
%DebugToggleBlockCoverage(false);
......@@ -216,9 +216,8 @@ TestCoverage(
%PerformMicrotaskCheckpoint(); // 0250
`,
[{"start":0,"end":299,"count":1},
{"start":1,"end":201,"count":6}, // TODO(jgruber): Invocation count is off.
{"start":83,"end":153,"count":4},
{"start":153,"end":200,"count":1}]
{"start":1,"end":201,"count":1},
{"start":83,"end":153,"count":4}]
);
TestCoverage(
......@@ -415,7 +414,7 @@ TestCoverage(
{"start":286,"end":350,"count":0},
{"start":401,"end":701,"count":1},
{"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":819,"end":820,"count":0},
{"start":861,"end":1050,"count":0}]
......@@ -540,10 +539,25 @@ const it = function*() { // 0000
it.next(); it.next(); // 0250
`,
[{"start":0,"end":299,"count":1},
{"start":11,"end":201,"count":3},
{"start":64,"end":114,"count":1},
{"start":11,"end":201,"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":122,"end":129,"count":1},
{"start":129,"end":200,"count":0}]
);
......@@ -563,9 +577,9 @@ try { // 0200
`,
[{"start":0,"end":499,"count":1},
{"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":264,"end":353,"count":3},
{"start":264,"end":353,"count":1},
{"start":312,"end":352,"count":0}]
);
......@@ -582,9 +596,8 @@ const it = function*() { // 0000
it.next(); it.return(); // 0450
`,
[{"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":254,"end":272,"count":1},
{"start":272,"end":350,"count":0}]
);
......@@ -601,9 +614,8 @@ const it = function*() { // 0000
it.next(); it.throw(42); // 0550
`,
[{"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":154,"end":310,"count":1},
{"start":310,"end":350,"count":0}]
);
......@@ -619,10 +631,8 @@ it.next(); it.next(); it.next(); // 0250
it.next(); it.next(); it.next(); // 0300
`,
[{"start":0,"end":349,"count":1},
{"start":11,"end":201,"count":7},
{"start":65,"end":115,"count":1},
{"start":11,"end":201,"count":1},
{"start":115,"end":122,"count":0},
{"start":123,"end":130,"count":1},
{"start":130,"end":200,"count":0}]
);
......@@ -642,9 +652,9 @@ try { // 0200
`,
[{"start":0,"end":499,"count":1},
{"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":264,"end":353,"count":3},
{"start":264,"end":353,"count":1},
{"start":317,"end":352,"count":0}]
);
......@@ -659,8 +669,7 @@ f(); // 0200
%PerformMicrotaskCheckpoint(); // 0250
`,
[{"start":0,"end":299,"count":1},
{"start":0,"end":151,"count":3},
{"start":61,"end":150,"count":1}]
{"start":0,"end":151,"count":1}]
);
TestCoverage(
......@@ -676,7 +685,8 @@ b() // 0250
[{"start":0,"end":299,"count":1},
{"start":15,"end":20,"count":0},
{"start":50,"end":151,"count":2},
{"start":114,"end":118,"count":0}]);
{"start":114,"end":118,"count":0}]
);
TestCoverage(
"LogicalOrExpression IsTest()",
......@@ -705,7 +715,8 @@ const c = true && 50 // 0300
[{"start":0,"end":349,"count":1},
{"start":16,"end":21,"count":0},
{"start":50,"end":151,"count":2},
{"start":114,"end":118,"count":0}]);
{"start":114,"end":118,"count":0}]
);
TestCoverage(
"LogicalAndExpression IsTest()",
......
......@@ -10,62 +10,55 @@
TestCoverage(
"class with no fields",
`class X { // 000
`
class X { // 000
}; // 050
`,
[
{ start: 0, end: 98, count: 1 },
{ start: 0, end: 0, count: 0 },
]
[{"start":0,"end":99,"count":1}]
);
TestCoverage(
"class that's not created",
`class X { // 000
`
class X { // 000
x = function() { } // 050
}; // 100
`,
[
{ start: 0, end: 148, count: 1 },
{ start: 0, end: 0, count: 0 },
{ start: 51, end: 69, count: 0 },
]
[{"start":0,"end":149,"count":1},
{"start":52,"end":70,"count":0}]
);
TestCoverage(
"class with field thats not called",
`class X { // 000
`
class X { // 000
x = function() { } // 050
}; // 100
let x = new X(); // 150
`,
[
{ start: 0, end: 198, count: 1 },
{ start: 0, end: 0, count: 1 },
{ start: 51, end: 69, count: 1 },
{ start: 55, end: 69, count: 0 }
]
[{"start":0,"end":199,"count":1},
{"start":52,"end":70,"count":1},
{"start":56,"end":70,"count":0}]
);
TestCoverage(
"class field",
`class X { // 000
`
class X { // 000
x = function() { } // 050
}; // 100
let x = new X(); // 150
x.x(); // 200
`,
[
{ start: 0, end: 248, count: 1 },
{ start: 0, end: 0, count: 1 },
{ start: 51, end: 69, count: 1 },
{ start: 55, end: 69, count: 1 }
]
[{"start":0,"end":249,"count":1},
{"start":52,"end":70,"count":1},
{"start":56,"end":70,"count":1}]
);
TestCoverage(
"non contiguous class field",
`class X { // 000
`
class X { // 000
x = function() { } // 050
foo() { } // 100
y = function() {} // 150
......@@ -74,19 +67,17 @@ let x = new X(); // 250
x.x(); // 300
x.y(); // 350
`,
[
{ start: 0, end: 398, count: 1 },
{ start: 0, end: 0, count: 1 },
{ start: 51, end: 168, count: 1 },
{ start: 55, end: 69, count: 1 },
{ start: 101, end: 110, count: 0 },
{ start: 155, end: 168, count: 1 },
]
[{"start":0,"end":399,"count":1},
{"start":52,"end":169,"count":1},
{"start":56,"end":70,"count":1},
{"start":102,"end":111,"count":0},
{"start":156,"end":169,"count":1}]
);
TestCoverage(
"non contiguous class field thats called",
`class X { // 000
`
class X { // 000
x = function() { } // 050
foo() { } // 100
y = function() {} // 150
......@@ -96,29 +87,24 @@ x.x(); // 300
x.y(); // 350
x.foo(); // 400
`,
[
{ start: 0, end: 448, count: 1 },
{ start: 0, end: 0, count: 1 },
{ start: 51, end: 168, count: 1 },
{ start: 55, end: 69, count: 1 },
{ start: 101, end: 110, count: 1 },
{ start: 155, end: 168, count: 1 },
]
[{"start":0,"end":449,"count":1},
{"start":52,"end":169,"count":1},
{"start":56,"end":70,"count":1},
{"start":102,"end":111,"count":1},
{"start":156,"end":169,"count":1}]
);
TestCoverage(
"class with initializer iife",
`class X { // 000
`
class X { // 000
x = (function() { })() // 050
}; // 100
let x = new X(); // 150
`,
[
{ start: 0, end: 198, count: 1 },
{ start: 0, end: 0, count: 1 },
{ start: 51, end: 73, count: 1 },
{ start: 56, end: 70, count: 1 }
]
[{"start":0,"end":199,"count":1},
{"start":52,"end":74,"count":1},
{"start":57,"end":71,"count":1}]
);
TestCoverage(
......@@ -130,56 +116,47 @@ class X { // 050
}; // 150
let x = new X(); // 200
`,
[
{ start: 0, end: 249, count: 1 },
{ start: 0, end: 15, count: 1 },
{ start: 50, end: 50, count: 1 },
{ start: 102, end: 128, count: 1 },
{ start: 111, end: 125, count: 1 }
]
[{"start":0,"end":249,"count":1},
{"start":0,"end":15,"count":1},
{"start":102,"end":128,"count":1},
{"start":111,"end":125,"count":1}]
);
TestCoverage(
"static class field that's not called",
`class X { // 000
`
class X { // 000
static x = function() { } // 050
}; // 100
`,
[
{ start: 0, end: 148, count: 1 },
{ start: 0, end: 0, count: 0 },
{ start: 51, end: 76, count: 1 },
{ start: 62, end: 76, count: 0 }
]
[{"start":0,"end":149,"count":1},
{"start":52,"end":77,"count":1},
{"start":63,"end":77,"count":0}]
);
TestCoverage(
"static class field",
`class X { // 000
`
class X { // 000
static x = function() { } // 050
}; // 100
X.x(); // 150
`,
[
{ start: 0, end: 198, count: 1 },
{ start: 0, end: 0, count: 0 },
{ start: 51, end: 76, count: 1 },
{ start: 62, end: 76, count: 1 }
]
[{"start":0,"end":199,"count":1},
{"start":52,"end":77,"count":1},
{"start":63,"end":77,"count":1}]
);
TestCoverage(
"static class field with iife",
`class X { // 000
`
class X { // 000
static x = (function() { })() // 050
}; // 100
`,
[
{ start: 0, end: 148, count: 1 },
{ start: 0, end: 0, count: 0 },
{ start: 51, end: 80, count: 1 },
{ start: 63, end: 77, count: 1 }
]
[{"start":0,"end":149,"count":1},
{"start":52,"end":81,"count":1},
{"start":64,"end":78,"count":1}]
);
TestCoverage(
......@@ -190,11 +167,8 @@ class X { // 050
static [f()] = (function() { })() // 100
}; // 150
`,
[
{ start: 0, end: 199, count: 1 },
{ start: 0, end: 15, count: 1 },
{ start: 50, end: 50, count: 0 },
{ start: 102, end: 135, count: 1 },
{ start: 118, end: 132, count: 1 }
]
[{"start":0,"end":199,"count":1},
{"start":0,"end":15,"count":1},
{"start":102,"end":135,"count":1},
{"start":118,"end":132,"count":1}]
);
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