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

[coverage] Support throw/try/catch/finally

This adds support for exception control flow by adding a counter behind throw
statements (never incremented), as well as a counter for catch and finally
blocks.

Bug: v8:6000
Change-Id: I3959772c889b543ab5e186ad7cd710e55a8aec23
Reviewed-on: https://chromium-review.googlesource.com/558993
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46476}
parent 17001a05
...@@ -986,6 +986,8 @@ class TryCatchStatement final : public TryStatement { ...@@ -986,6 +986,8 @@ class TryCatchStatement final : public TryStatement {
Block* catch_block() const { return catch_block_; } Block* catch_block() const { return catch_block_; }
void set_catch_block(Block* b) { catch_block_ = b; } void set_catch_block(Block* b) { catch_block_ = b; }
SourceRange catch_range() const { return catch_range_; }
// Prediction of whether exceptions thrown into the handler for this try block // Prediction of whether exceptions thrown into the handler for this try block
// will be caught. // will be caught.
// //
...@@ -1038,15 +1040,18 @@ class TryCatchStatement final : public TryStatement { ...@@ -1038,15 +1040,18 @@ class TryCatchStatement final : public TryStatement {
friend class AstNodeFactory; friend class AstNodeFactory;
TryCatchStatement(Block* try_block, Scope* scope, Block* catch_block, TryCatchStatement(Block* try_block, Scope* scope, Block* catch_block,
HandlerTable::CatchPrediction catch_prediction, int pos) HandlerTable::CatchPrediction catch_prediction, int pos,
const SourceRange& catch_range)
: TryStatement(try_block, pos, kTryCatchStatement), : TryStatement(try_block, pos, kTryCatchStatement),
scope_(scope), scope_(scope),
catch_block_(catch_block), catch_block_(catch_block),
catch_prediction_(catch_prediction) {} catch_prediction_(catch_prediction),
catch_range_(catch_range) {}
Scope* scope_; Scope* scope_;
Block* catch_block_; Block* catch_block_;
HandlerTable::CatchPrediction catch_prediction_; HandlerTable::CatchPrediction catch_prediction_;
SourceRange catch_range_;
}; };
...@@ -1055,14 +1060,19 @@ class TryFinallyStatement final : public TryStatement { ...@@ -1055,14 +1060,19 @@ class TryFinallyStatement final : public TryStatement {
Block* finally_block() const { return finally_block_; } Block* finally_block() const { return finally_block_; }
void set_finally_block(Block* b) { finally_block_ = b; } void set_finally_block(Block* b) { finally_block_ = b; }
SourceRange finally_range() const { return finally_range_; }
private: private:
friend class AstNodeFactory; friend class AstNodeFactory;
TryFinallyStatement(Block* try_block, Block* finally_block, int pos) TryFinallyStatement(Block* try_block, Block* finally_block, int pos,
const SourceRange& finally_range)
: TryStatement(try_block, pos, kTryFinallyStatement), : TryStatement(try_block, pos, kTryFinallyStatement),
finally_block_(finally_block) {} finally_block_(finally_block),
finally_range_(finally_range) {}
Block* finally_block_; Block* finally_block_;
SourceRange finally_range_;
}; };
...@@ -2421,14 +2431,20 @@ class Throw final : public Expression { ...@@ -2421,14 +2431,20 @@ class Throw final : public Expression {
public: public:
Expression* exception() const { return exception_; } Expression* exception() const { return exception_; }
void set_exception(Expression* e) { exception_ = e; } void set_exception(Expression* e) { exception_ = e; }
// The first source position past the end of the throw statement, used by
// block coverage.
int32_t continuation_pos() const { return continuation_pos_; }
private: private:
friend class AstNodeFactory; friend class AstNodeFactory;
Throw(Expression* exception, int pos) Throw(Expression* exception, int pos, int32_t continuation_pos)
: Expression(pos, kThrow), exception_(exception) {} : Expression(pos, kThrow),
exception_(exception),
continuation_pos_(continuation_pos) {}
Expression* exception_; Expression* exception_;
int32_t continuation_pos_;
}; };
...@@ -3206,9 +3222,10 @@ class AstNodeFactory final BASE_EMBEDDED { ...@@ -3206,9 +3222,10 @@ class AstNodeFactory final BASE_EMBEDDED {
} }
TryCatchStatement* NewTryCatchStatement(Block* try_block, Scope* scope, TryCatchStatement* NewTryCatchStatement(Block* try_block, Scope* scope,
Block* catch_block, int pos) { Block* catch_block, int pos,
return new (zone_) TryCatchStatement(try_block, scope, catch_block, const SourceRange catch_range = {}) {
HandlerTable::CAUGHT, pos); return new (zone_) TryCatchStatement(
try_block, scope, catch_block, HandlerTable::CAUGHT, pos, catch_range);
} }
TryCatchStatement* NewTryCatchStatementForReThrow(Block* try_block, TryCatchStatement* NewTryCatchStatementForReThrow(Block* try_block,
...@@ -3216,7 +3233,7 @@ class AstNodeFactory final BASE_EMBEDDED { ...@@ -3216,7 +3233,7 @@ class AstNodeFactory final BASE_EMBEDDED {
Block* catch_block, Block* catch_block,
int pos) { int pos) {
return new (zone_) TryCatchStatement(try_block, scope, catch_block, return new (zone_) TryCatchStatement(try_block, scope, catch_block,
HandlerTable::UNCAUGHT, pos); HandlerTable::UNCAUGHT, pos, {});
} }
TryCatchStatement* NewTryCatchStatementForDesugaring(Block* try_block, TryCatchStatement* NewTryCatchStatementForDesugaring(Block* try_block,
...@@ -3224,7 +3241,7 @@ class AstNodeFactory final BASE_EMBEDDED { ...@@ -3224,7 +3241,7 @@ class AstNodeFactory final BASE_EMBEDDED {
Block* catch_block, Block* catch_block,
int pos) { int pos) {
return new (zone_) TryCatchStatement(try_block, scope, catch_block, return new (zone_) TryCatchStatement(try_block, scope, catch_block,
HandlerTable::DESUGARING, pos); HandlerTable::DESUGARING, pos, {});
} }
TryCatchStatement* NewTryCatchStatementForAsyncAwait(Block* try_block, TryCatchStatement* NewTryCatchStatementForAsyncAwait(Block* try_block,
...@@ -3232,12 +3249,14 @@ class AstNodeFactory final BASE_EMBEDDED { ...@@ -3232,12 +3249,14 @@ class AstNodeFactory final BASE_EMBEDDED {
Block* catch_block, Block* catch_block,
int pos) { int pos) {
return new (zone_) TryCatchStatement(try_block, scope, catch_block, return new (zone_) TryCatchStatement(try_block, scope, catch_block,
HandlerTable::ASYNC_AWAIT, pos); HandlerTable::ASYNC_AWAIT, pos, {});
} }
TryFinallyStatement* NewTryFinallyStatement(Block* try_block, TryFinallyStatement* NewTryFinallyStatement(
Block* finally_block, int pos) { Block* try_block, Block* finally_block, int pos,
return new (zone_) TryFinallyStatement(try_block, finally_block, pos); const SourceRange& finally_range = {}) {
return new (zone_)
TryFinallyStatement(try_block, finally_block, pos, finally_range);
} }
DebuggerStatement* NewDebuggerStatement(int pos) { DebuggerStatement* NewDebuggerStatement(int pos) {
...@@ -3453,8 +3472,9 @@ class AstNodeFactory final BASE_EMBEDDED { ...@@ -3453,8 +3472,9 @@ class AstNodeFactory final BASE_EMBEDDED {
return new (zone_) YieldStar(expression, pos, flags); return new (zone_) YieldStar(expression, pos, flags);
} }
Throw* NewThrow(Expression* exception, int pos) { Throw* NewThrow(Expression* exception, int pos,
return new (zone_) Throw(exception, pos); int32_t continuation_pos = kNoSourcePosition) {
return new (zone_) Throw(exception, pos, continuation_pos);
} }
FunctionLiteral* NewFunctionLiteral( FunctionLiteral* NewFunctionLiteral(
......
...@@ -1233,8 +1233,6 @@ void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) { ...@@ -1233,8 +1233,6 @@ void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) {
int then_slot = AllocateBlockCoverageSlotIfEnabled(stmt->then_range()); int then_slot = AllocateBlockCoverageSlotIfEnabled(stmt->then_range());
int else_slot = AllocateBlockCoverageSlotIfEnabled(stmt->else_range()); int else_slot = AllocateBlockCoverageSlotIfEnabled(stmt->else_range());
int continuation_slot =
AllocateBlockCoverageSlotIfEnabled(stmt->continuation_range());
if (stmt->condition()->ToBooleanIsTrue()) { if (stmt->condition()->ToBooleanIsTrue()) {
// Generate then block unconditionally as always true. // Generate then block unconditionally as always true.
...@@ -1269,7 +1267,7 @@ void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) { ...@@ -1269,7 +1267,7 @@ void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) {
} }
builder()->Bind(&end_label); builder()->Bind(&end_label);
} }
BuildIncrementBlockCoverageCounterIfEnabled(continuation_slot); BuildIncrementBlockCoverageCounterIfEnabled(stmt->continuation_range());
} }
void BytecodeGenerator::VisitSloppyBlockFunctionStatement( void BytecodeGenerator::VisitSloppyBlockFunctionStatement(
...@@ -1614,6 +1612,7 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { ...@@ -1614,6 +1612,7 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
builder()->LoadAccumulatorWithRegister(context); builder()->LoadAccumulatorWithRegister(context);
// Evaluate the catch-block. // Evaluate the catch-block.
BuildIncrementBlockCoverageCounterIfEnabled(stmt->catch_range());
VisitInScope(stmt->catch_block(), stmt->scope()); VisitInScope(stmt->catch_block(), stmt->scope());
try_control_builder.EndCatch(); try_control_builder.EndCatch();
} }
...@@ -1672,6 +1671,7 @@ void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { ...@@ -1672,6 +1671,7 @@ void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
message); message);
// Evaluate the finally-block. // Evaluate the finally-block.
BuildIncrementBlockCoverageCounterIfEnabled(stmt->finally_range());
Visit(stmt->finally_block()); Visit(stmt->finally_block());
try_control_builder.EndFinally(); try_control_builder.EndFinally();
...@@ -2953,6 +2953,8 @@ void BytecodeGenerator::VisitYieldStar(YieldStar* expr) { ...@@ -2953,6 +2953,8 @@ void BytecodeGenerator::VisitYieldStar(YieldStar* expr) {
} }
void BytecodeGenerator::VisitThrow(Throw* expr) { void BytecodeGenerator::VisitThrow(Throw* expr) {
AllocateBlockCoverageSlotIfEnabled(
{expr->continuation_pos(), kNoSourcePosition});
VisitForAccumulatorValue(expr->exception()); VisitForAccumulatorValue(expr->exception());
builder()->SetExpressionPosition(expr); builder()->SetExpressionPosition(expr);
builder()->Throw(); builder()->Throw();
...@@ -4154,7 +4156,8 @@ void BytecodeGenerator::BuildLoadPropertyKey(LiteralProperty* property, ...@@ -4154,7 +4156,8 @@ void BytecodeGenerator::BuildLoadPropertyKey(LiteralProperty* property,
} }
} }
int BytecodeGenerator::AllocateBlockCoverageSlotIfEnabled(SourceRange range) { int BytecodeGenerator::AllocateBlockCoverageSlotIfEnabled(
const SourceRange& range) {
return (block_coverage_builder_ == nullptr) return (block_coverage_builder_ == nullptr)
? BlockCoverageBuilder::kNoCoverageArraySlot ? BlockCoverageBuilder::kNoCoverageArraySlot
: block_coverage_builder_->AllocateBlockCoverageSlot(range); : block_coverage_builder_->AllocateBlockCoverageSlot(range);
...@@ -4167,6 +4170,14 @@ void BytecodeGenerator::BuildIncrementBlockCoverageCounterIfEnabled( ...@@ -4167,6 +4170,14 @@ void BytecodeGenerator::BuildIncrementBlockCoverageCounterIfEnabled(
} }
} }
void BytecodeGenerator::BuildIncrementBlockCoverageCounterIfEnabled(
const SourceRange& range) {
if (block_coverage_builder_ != nullptr) {
int slot = block_coverage_builder_->AllocateBlockCoverageSlot(range);
block_coverage_builder_->IncrementBlockCounter(slot);
}
}
// Visits the expression |expr| and places the result in the accumulator. // Visits the expression |expr| and places the result in the accumulator.
BytecodeGenerator::TypeHint BytecodeGenerator::VisitForAccumulatorValue( BytecodeGenerator::TypeHint BytecodeGenerator::VisitForAccumulatorValue(
Expression* expr) { Expression* expr) {
......
...@@ -192,8 +192,9 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> { ...@@ -192,8 +192,9 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void BuildLoadPropertyKey(LiteralProperty* property, Register out_reg); void BuildLoadPropertyKey(LiteralProperty* property, Register out_reg);
int AllocateBlockCoverageSlotIfEnabled(SourceRange range); int AllocateBlockCoverageSlotIfEnabled(const SourceRange& range);
void BuildIncrementBlockCoverageCounterIfEnabled(int coverage_array_slot); void BuildIncrementBlockCoverageCounterIfEnabled(int coverage_array_slot);
void BuildIncrementBlockCoverageCounterIfEnabled(const SourceRange& range);
void BuildTest(ToBooleanMode mode, BytecodeLabels* then_labels, void BuildTest(ToBooleanMode mode, BytecodeLabels* then_labels,
BytecodeLabels* else_labels, TestFallthrough fallthrough); BytecodeLabels* else_labels, TestFallthrough fallthrough);
......
...@@ -5429,8 +5429,9 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseThrowStatement( ...@@ -5429,8 +5429,9 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseThrowStatement(
} }
ExpressionT exception = ParseExpression(true, CHECK_OK); ExpressionT exception = ParseExpression(true, CHECK_OK);
ExpectSemicolon(CHECK_OK); ExpectSemicolon(CHECK_OK);
int continuation_pos = scanner_->location().end_pos;
return impl()->NewThrowStatement(exception, pos); return impl()->NewThrowStatement(exception, pos, continuation_pos);
} }
template <typename Impl> template <typename Impl>
...@@ -5525,6 +5526,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement( ...@@ -5525,6 +5526,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement(
return impl()->NullStatement(); return impl()->NullStatement();
} }
SourceRange catch_range, finally_range;
BlockT catch_block = impl()->NullBlock(); BlockT catch_block = impl()->NullBlock();
if (Check(Token::CATCH)) { if (Check(Token::CATCH)) {
Expect(Token::LPAREN, CHECK_OK); Expect(Token::LPAREN, CHECK_OK);
...@@ -5565,6 +5568,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement( ...@@ -5565,6 +5568,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement(
catch_block->statements()->Add(catch_info.init_block, zone()); catch_block->statements()->Add(catch_info.init_block, zone());
} }
SourceRangeScope range_scope(scanner(), &catch_range);
catch_info.inner_block = ParseBlock(nullptr, CHECK_OK); catch_info.inner_block = ParseBlock(nullptr, CHECK_OK);
catch_block->statements()->Add(catch_info.inner_block, zone()); catch_block->statements()->Add(catch_info.inner_block, zone());
impl()->ValidateCatchBlock(catch_info, CHECK_OK); impl()->ValidateCatchBlock(catch_info, CHECK_OK);
...@@ -5579,11 +5583,13 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement( ...@@ -5579,11 +5583,13 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement(
BlockT finally_block = impl()->NullBlock(); BlockT finally_block = impl()->NullBlock();
DCHECK(peek() == Token::FINALLY || !impl()->IsNullStatement(catch_block)); DCHECK(peek() == Token::FINALLY || !impl()->IsNullStatement(catch_block));
if (Check(Token::FINALLY)) { if (Check(Token::FINALLY)) {
SourceRangeScope range_scope(scanner(), &finally_range);
finally_block = ParseBlock(nullptr, CHECK_OK); finally_block = ParseBlock(nullptr, CHECK_OK);
} }
return impl()->RewriteTryStatement(try_block, catch_block, finally_block, return impl()->RewriteTryStatement(try_block, catch_block, catch_range,
catch_info, pos); finally_block, finally_range, catch_info,
pos);
} }
template <typename Impl> template <typename Impl>
......
...@@ -1736,7 +1736,9 @@ void Parser::ValidateCatchBlock(const CatchInfo& catch_info, bool* ok) { ...@@ -1736,7 +1736,9 @@ void Parser::ValidateCatchBlock(const CatchInfo& catch_info, bool* ok) {
} }
Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block, Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block,
const SourceRange& catch_range,
Block* finally_block, Block* finally_block,
const SourceRange& finally_range,
const CatchInfo& catch_info, int pos) { const CatchInfo& catch_info, int pos) {
// Simplify the AST nodes by converting: // Simplify the AST nodes by converting:
// 'try B0 catch B1 finally B2' // 'try B0 catch B1 finally B2'
...@@ -1748,7 +1750,8 @@ Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block, ...@@ -1748,7 +1750,8 @@ Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block,
DCHECK_NOT_NULL(catch_info.scope); DCHECK_NOT_NULL(catch_info.scope);
TryCatchStatement* statement; TryCatchStatement* statement;
statement = factory()->NewTryCatchStatement(try_block, catch_info.scope, statement = factory()->NewTryCatchStatement(try_block, catch_info.scope,
catch_block, kNoSourcePosition); catch_block, kNoSourcePosition,
catch_range);
try_block = factory()->NewBlock(nullptr, 1, false, kNoSourcePosition); try_block = factory()->NewBlock(nullptr, 1, false, kNoSourcePosition);
try_block->statements()->Add(statement, zone()); try_block->statements()->Add(statement, zone());
...@@ -1764,10 +1767,11 @@ Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block, ...@@ -1764,10 +1767,11 @@ Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block,
DCHECK_NULL(finally_block); DCHECK_NULL(finally_block);
DCHECK_NOT_NULL(catch_info.scope); DCHECK_NOT_NULL(catch_info.scope);
return factory()->NewTryCatchStatement(try_block, catch_info.scope, return factory()->NewTryCatchStatement(try_block, catch_info.scope,
catch_block, pos); catch_block, pos, catch_range);
} else { } else {
DCHECK_NOT_NULL(finally_block); DCHECK_NOT_NULL(finally_block);
return factory()->NewTryFinallyStatement(try_block, finally_block, pos); return factory()->NewTryFinallyStatement(try_block, finally_block, pos,
finally_range);
} }
} }
......
...@@ -344,9 +344,10 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { ...@@ -344,9 +344,10 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
void RewriteCatchPattern(CatchInfo* catch_info, bool* ok); void RewriteCatchPattern(CatchInfo* catch_info, bool* ok);
void ValidateCatchBlock(const CatchInfo& catch_info, bool* ok); void ValidateCatchBlock(const CatchInfo& catch_info, bool* ok);
Statement* RewriteTryStatement(Block* try_block, Block* catch_block, Statement* RewriteTryStatement(Block* try_block, Block* catch_block,
const SourceRange& catch_range,
Block* finally_block, Block* finally_block,
const SourceRange& finally_range,
const CatchInfo& catch_info, int pos); const CatchInfo& catch_info, int pos);
void ParseAndRewriteGeneratorFunctionBody(int pos, FunctionKind kind, void ParseAndRewriteGeneratorFunctionBody(int pos, FunctionKind kind,
ZoneList<Statement*>* body, ZoneList<Statement*>* body,
bool* ok); bool* ok);
...@@ -1049,9 +1050,10 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { ...@@ -1049,9 +1050,10 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
ZoneList<Expression*>* args, int pos, ZoneList<Expression*>* args, int pos,
bool* ok); bool* ok);
V8_INLINE Statement* NewThrowStatement(Expression* exception, int pos) { V8_INLINE Statement* NewThrowStatement(Expression* exception, int pos,
int32_t continuation_pos) {
return factory()->NewExpressionStatement( return factory()->NewExpressionStatement(
factory()->NewThrow(exception, pos), pos); factory()->NewThrow(exception, pos, continuation_pos), pos);
} }
V8_INLINE void AddParameterInitializationBlock( V8_INLINE void AddParameterInitializationBlock(
......
...@@ -1094,7 +1094,8 @@ class PreParser : public ParserBase<PreParser> { ...@@ -1094,7 +1094,8 @@ class PreParser : public ParserBase<PreParser> {
V8_INLINE void ValidateCatchBlock(const CatchInfo& catch_info, bool* ok) {} V8_INLINE void ValidateCatchBlock(const CatchInfo& catch_info, bool* ok) {}
V8_INLINE PreParserStatement RewriteTryStatement( V8_INLINE PreParserStatement RewriteTryStatement(
PreParserStatement try_block, PreParserStatement catch_block, PreParserStatement try_block, PreParserStatement catch_block,
PreParserStatement finally_block, const CatchInfo& catch_info, int pos) { const SourceRange& catch_range, PreParserStatement finally_block,
const SourceRange& finally_range, const CatchInfo& catch_info, int pos) {
return PreParserStatement::Default(); return PreParserStatement::Default();
} }
...@@ -1636,7 +1637,8 @@ class PreParser : public ParserBase<PreParser> { ...@@ -1636,7 +1637,8 @@ class PreParser : public ParserBase<PreParser> {
} }
V8_INLINE PreParserStatement NewThrowStatement(PreParserExpression exception, V8_INLINE PreParserStatement NewThrowStatement(PreParserExpression exception,
int pos) { int pos,
int32_t continuation_pos) {
return PreParserStatement::Jump(); return PreParserStatement::Jump();
} }
......
...@@ -312,4 +312,34 @@ TestCoverage( ...@@ -312,4 +312,34 @@ TestCoverage(
{"start":81,"end":122,"count":0}] {"start":81,"end":122,"count":0}]
); );
TestCoverage(
"try/catch/finally statements",
`
!function() { // 0000
try { nop(); } catch (e) { nop(); } // 0050
try { nop(); } finally { nop(); } // 0100
try { // 0150
try { throw 42; } catch (e) { nop(); }// 0200
} catch (e) { nop(); } // 0250
try { // 0300
try { throw 42; } finally { nop(); } // 0350
} catch (e) { nop(); } // 0400
try { // 0450
throw 42; // 0500
} catch (e) { // 0550
nop(); // 0600
} finally { // 0650
nop(); // 0700
} // 0750
}(); // 0800
`,
[{"start":0,"end":849,"count":1},
{"start":1,"end":801,"count":1},
{"start":77,"end":87,"count":0},
{"start":219,"end":232,"count":0},
{"start":264,"end":274,"count":0},
{"start":369,"end":380,"count":0},
{"start":513,"end":564,"count":0}]
);
%DebugToggleBlockCoverage(false); %DebugToggleBlockCoverage(false);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment