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 {
Block* catch_block() const { return catch_block_; }
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
// will be caught.
//
......@@ -1038,15 +1040,18 @@ class TryCatchStatement final : public TryStatement {
friend class AstNodeFactory;
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),
scope_(scope),
catch_block_(catch_block),
catch_prediction_(catch_prediction) {}
catch_prediction_(catch_prediction),
catch_range_(catch_range) {}
Scope* scope_;
Block* catch_block_;
HandlerTable::CatchPrediction catch_prediction_;
SourceRange catch_range_;
};
......@@ -1055,14 +1060,19 @@ class TryFinallyStatement final : public TryStatement {
Block* finally_block() const { return finally_block_; }
void set_finally_block(Block* b) { finally_block_ = b; }
SourceRange finally_range() const { return finally_range_; }
private:
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),
finally_block_(finally_block) {}
finally_block_(finally_block),
finally_range_(finally_range) {}
Block* finally_block_;
SourceRange finally_range_;
};
......@@ -2421,14 +2431,20 @@ class Throw final : public Expression {
public:
Expression* exception() const { return exception_; }
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:
friend class AstNodeFactory;
Throw(Expression* exception, int pos)
: Expression(pos, kThrow), exception_(exception) {}
Throw(Expression* exception, int pos, int32_t continuation_pos)
: Expression(pos, kThrow),
exception_(exception),
continuation_pos_(continuation_pos) {}
Expression* exception_;
int32_t continuation_pos_;
};
......@@ -3206,9 +3222,10 @@ class AstNodeFactory final BASE_EMBEDDED {
}
TryCatchStatement* NewTryCatchStatement(Block* try_block, Scope* scope,
Block* catch_block, int pos) {
return new (zone_) TryCatchStatement(try_block, scope, catch_block,
HandlerTable::CAUGHT, pos);
Block* catch_block, int pos,
const SourceRange catch_range = {}) {
return new (zone_) TryCatchStatement(
try_block, scope, catch_block, HandlerTable::CAUGHT, pos, catch_range);
}
TryCatchStatement* NewTryCatchStatementForReThrow(Block* try_block,
......@@ -3216,7 +3233,7 @@ class AstNodeFactory final BASE_EMBEDDED {
Block* catch_block,
int pos) {
return new (zone_) TryCatchStatement(try_block, scope, catch_block,
HandlerTable::UNCAUGHT, pos);
HandlerTable::UNCAUGHT, pos, {});
}
TryCatchStatement* NewTryCatchStatementForDesugaring(Block* try_block,
......@@ -3224,7 +3241,7 @@ class AstNodeFactory final BASE_EMBEDDED {
Block* catch_block,
int pos) {
return new (zone_) TryCatchStatement(try_block, scope, catch_block,
HandlerTable::DESUGARING, pos);
HandlerTable::DESUGARING, pos, {});
}
TryCatchStatement* NewTryCatchStatementForAsyncAwait(Block* try_block,
......@@ -3232,12 +3249,14 @@ class AstNodeFactory final BASE_EMBEDDED {
Block* catch_block,
int pos) {
return new (zone_) TryCatchStatement(try_block, scope, catch_block,
HandlerTable::ASYNC_AWAIT, pos);
HandlerTable::ASYNC_AWAIT, pos, {});
}
TryFinallyStatement* NewTryFinallyStatement(Block* try_block,
Block* finally_block, int pos) {
return new (zone_) TryFinallyStatement(try_block, finally_block, pos);
TryFinallyStatement* NewTryFinallyStatement(
Block* try_block, Block* finally_block, int pos,
const SourceRange& finally_range = {}) {
return new (zone_)
TryFinallyStatement(try_block, finally_block, pos, finally_range);
}
DebuggerStatement* NewDebuggerStatement(int pos) {
......@@ -3453,8 +3472,9 @@ class AstNodeFactory final BASE_EMBEDDED {
return new (zone_) YieldStar(expression, pos, flags);
}
Throw* NewThrow(Expression* exception, int pos) {
return new (zone_) Throw(exception, pos);
Throw* NewThrow(Expression* exception, int pos,
int32_t continuation_pos = kNoSourcePosition) {
return new (zone_) Throw(exception, pos, continuation_pos);
}
FunctionLiteral* NewFunctionLiteral(
......
......@@ -1233,8 +1233,6 @@ void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) {
int then_slot = AllocateBlockCoverageSlotIfEnabled(stmt->then_range());
int else_slot = AllocateBlockCoverageSlotIfEnabled(stmt->else_range());
int continuation_slot =
AllocateBlockCoverageSlotIfEnabled(stmt->continuation_range());
if (stmt->condition()->ToBooleanIsTrue()) {
// Generate then block unconditionally as always true.
......@@ -1269,7 +1267,7 @@ void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) {
}
builder()->Bind(&end_label);
}
BuildIncrementBlockCoverageCounterIfEnabled(continuation_slot);
BuildIncrementBlockCoverageCounterIfEnabled(stmt->continuation_range());
}
void BytecodeGenerator::VisitSloppyBlockFunctionStatement(
......@@ -1614,6 +1612,7 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
builder()->LoadAccumulatorWithRegister(context);
// Evaluate the catch-block.
BuildIncrementBlockCoverageCounterIfEnabled(stmt->catch_range());
VisitInScope(stmt->catch_block(), stmt->scope());
try_control_builder.EndCatch();
}
......@@ -1672,6 +1671,7 @@ void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
message);
// Evaluate the finally-block.
BuildIncrementBlockCoverageCounterIfEnabled(stmt->finally_range());
Visit(stmt->finally_block());
try_control_builder.EndFinally();
......@@ -2953,6 +2953,8 @@ void BytecodeGenerator::VisitYieldStar(YieldStar* expr) {
}
void BytecodeGenerator::VisitThrow(Throw* expr) {
AllocateBlockCoverageSlotIfEnabled(
{expr->continuation_pos(), kNoSourcePosition});
VisitForAccumulatorValue(expr->exception());
builder()->SetExpressionPosition(expr);
builder()->Throw();
......@@ -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)
? BlockCoverageBuilder::kNoCoverageArraySlot
: block_coverage_builder_->AllocateBlockCoverageSlot(range);
......@@ -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.
BytecodeGenerator::TypeHint BytecodeGenerator::VisitForAccumulatorValue(
Expression* expr) {
......
......@@ -192,8 +192,9 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void BuildLoadPropertyKey(LiteralProperty* property, Register out_reg);
int AllocateBlockCoverageSlotIfEnabled(SourceRange range);
int AllocateBlockCoverageSlotIfEnabled(const SourceRange& range);
void BuildIncrementBlockCoverageCounterIfEnabled(int coverage_array_slot);
void BuildIncrementBlockCoverageCounterIfEnabled(const SourceRange& range);
void BuildTest(ToBooleanMode mode, BytecodeLabels* then_labels,
BytecodeLabels* else_labels, TestFallthrough fallthrough);
......
......@@ -5429,8 +5429,9 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseThrowStatement(
}
ExpressionT exception = ParseExpression(true, 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>
......@@ -5525,6 +5526,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement(
return impl()->NullStatement();
}
SourceRange catch_range, finally_range;
BlockT catch_block = impl()->NullBlock();
if (Check(Token::CATCH)) {
Expect(Token::LPAREN, CHECK_OK);
......@@ -5565,6 +5568,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement(
catch_block->statements()->Add(catch_info.init_block, zone());
}
SourceRangeScope range_scope(scanner(), &catch_range);
catch_info.inner_block = ParseBlock(nullptr, CHECK_OK);
catch_block->statements()->Add(catch_info.inner_block, zone());
impl()->ValidateCatchBlock(catch_info, CHECK_OK);
......@@ -5579,11 +5583,13 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement(
BlockT finally_block = impl()->NullBlock();
DCHECK(peek() == Token::FINALLY || !impl()->IsNullStatement(catch_block));
if (Check(Token::FINALLY)) {
SourceRangeScope range_scope(scanner(), &finally_range);
finally_block = ParseBlock(nullptr, CHECK_OK);
}
return impl()->RewriteTryStatement(try_block, catch_block, finally_block,
catch_info, pos);
return impl()->RewriteTryStatement(try_block, catch_block, catch_range,
finally_block, finally_range, catch_info,
pos);
}
template <typename Impl>
......
......@@ -1736,7 +1736,9 @@ void Parser::ValidateCatchBlock(const CatchInfo& catch_info, bool* ok) {
}
Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block,
const SourceRange& catch_range,
Block* finally_block,
const SourceRange& finally_range,
const CatchInfo& catch_info, int pos) {
// Simplify the AST nodes by converting:
// 'try B0 catch B1 finally B2'
......@@ -1748,7 +1750,8 @@ Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block,
DCHECK_NOT_NULL(catch_info.scope);
TryCatchStatement* statement;
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->statements()->Add(statement, zone());
......@@ -1764,10 +1767,11 @@ Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block,
DCHECK_NULL(finally_block);
DCHECK_NOT_NULL(catch_info.scope);
return factory()->NewTryCatchStatement(try_block, catch_info.scope,
catch_block, pos);
catch_block, pos, catch_range);
} else {
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>) {
void RewriteCatchPattern(CatchInfo* catch_info, bool* ok);
void ValidateCatchBlock(const CatchInfo& catch_info, bool* ok);
Statement* RewriteTryStatement(Block* try_block, Block* catch_block,
const SourceRange& catch_range,
Block* finally_block,
const SourceRange& finally_range,
const CatchInfo& catch_info, int pos);
void ParseAndRewriteGeneratorFunctionBody(int pos, FunctionKind kind,
ZoneList<Statement*>* body,
bool* ok);
......@@ -1049,9 +1050,10 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
ZoneList<Expression*>* args, int pos,
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(
factory()->NewThrow(exception, pos), pos);
factory()->NewThrow(exception, pos, continuation_pos), pos);
}
V8_INLINE void AddParameterInitializationBlock(
......
......@@ -1094,7 +1094,8 @@ class PreParser : public ParserBase<PreParser> {
V8_INLINE void ValidateCatchBlock(const CatchInfo& catch_info, bool* ok) {}
V8_INLINE PreParserStatement RewriteTryStatement(
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();
}
......@@ -1636,7 +1637,8 @@ class PreParser : public ParserBase<PreParser> {
}
V8_INLINE PreParserStatement NewThrowStatement(PreParserExpression exception,
int pos) {
int pos,
int32_t continuation_pos) {
return PreParserStatement::Jump();
}
......
......@@ -312,4 +312,34 @@ TestCoverage(
{"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);
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