[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:
parent
3cb560adfe
commit
3002ff44ee
@ -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,
|
||||
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;
|
||||
|
@ -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 : [
|
||||
|
@ -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}]
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user