[ignition] Add helper for TryCatch building

Change-Id: Ia39d2157eb7c0c644348e1762ee32fef84c6b51d
Reviewed-on: https://chromium-review.googlesource.com/c/1409428
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58816}
This commit is contained in:
Leszek Swirski 2019-01-14 16:47:45 +01:00 committed by Commit Bot
parent 15e803c1b9
commit 4c8dd3c9f9
2 changed files with 105 additions and 94 deletions

View File

@ -1501,6 +1501,36 @@ void BytecodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
} }
} }
template <typename TryBodyFunc, typename CatchBodyFunc>
void BytecodeGenerator::BuildTryCatch(
TryBodyFunc try_body_func, CatchBodyFunc catch_body_func,
HandlerTable::CatchPrediction catch_prediction,
TryCatchStatement* stmt_for_coverage) {
TryCatchBuilder try_control_builder(
builder(),
stmt_for_coverage == nullptr ? nullptr : block_coverage_builder_,
stmt_for_coverage, catch_prediction);
// Preserve the context in a dedicated register, so that it can be restored
// when the handler is entered by the stack-unwinding machinery.
// TODO(mstarzinger): Be smarter about register allocation.
Register context = register_allocator()->NewRegister();
builder()->MoveRegister(Register::current_context(), context);
// Evaluate the try-block inside a control scope. This simulates a handler
// that is intercepting 'throw' control commands.
try_control_builder.BeginTry(context);
{
ControlScopeForTryCatch scope(this, &try_control_builder);
try_body_func();
}
try_control_builder.EndTry();
catch_body_func(context);
try_control_builder.EndCatch();
}
template <typename TryBodyFunc, typename FinallyBodyFunc> template <typename TryBodyFunc, typename FinallyBodyFunc>
void BytecodeGenerator::BuildTryFinally( void BytecodeGenerator::BuildTryFinally(
TryBodyFunc try_body_func, FinallyBodyFunc finally_body_func, TryBodyFunc try_body_func, FinallyBodyFunc finally_body_func,
@ -1806,46 +1836,36 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
HandlerTable::CatchPrediction outer_catch_prediction = catch_prediction(); HandlerTable::CatchPrediction outer_catch_prediction = catch_prediction();
set_catch_prediction(stmt->GetCatchPrediction(outer_catch_prediction)); set_catch_prediction(stmt->GetCatchPrediction(outer_catch_prediction));
TryCatchBuilder try_control_builder(builder(), block_coverage_builder_, stmt, BuildTryCatch(
catch_prediction()); // Try body.
[&]() {
Visit(stmt->try_block());
set_catch_prediction(outer_catch_prediction);
},
// Catch body.
[&](Register context) {
if (stmt->scope()) {
// Create a catch scope that binds the exception.
BuildNewLocalCatchContext(stmt->scope());
builder()->StoreAccumulatorInRegister(context);
}
// Preserve the context in a dedicated register, so that it can be restored // If requested, clear message object as we enter the catch block.
// when the handler is entered by the stack-unwinding machinery. if (stmt->ShouldClearPendingException(outer_catch_prediction)) {
// TODO(mstarzinger): Be smarter about register allocation. builder()->LoadTheHole().SetPendingMessage();
Register context = register_allocator()->NewRegister(); }
builder()->MoveRegister(Register::current_context(), context);
// Evaluate the try-block inside a control scope. This simulates a handler // Load the catch context into the accumulator.
// that is intercepting 'throw' control commands. builder()->LoadAccumulatorWithRegister(context);
try_control_builder.BeginTry(context);
{
ControlScopeForTryCatch scope(this, &try_control_builder);
Visit(stmt->try_block());
set_catch_prediction(outer_catch_prediction);
}
try_control_builder.EndTry();
if (stmt->scope()) { // Evaluate the catch-block.
// Create a catch scope that binds the exception. if (stmt->scope()) {
BuildNewLocalCatchContext(stmt->scope()); VisitInScope(stmt->catch_block(), stmt->scope());
builder()->StoreAccumulatorInRegister(context); } else {
} VisitBlock(stmt->catch_block());
}
// If requested, clear message object as we enter the catch block. },
if (stmt->ShouldClearPendingException(outer_catch_prediction)) { catch_prediction(), stmt);
builder()->LoadTheHole().SetPendingMessage();
}
// Load the catch context into the accumulator.
builder()->LoadAccumulatorWithRegister(context);
// Evaluate the catch-block.
if (stmt->scope()) {
VisitInScope(stmt->catch_block(), stmt->scope());
} else {
VisitBlock(stmt->catch_block());
}
try_control_builder.EndCatch();
} }
void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
@ -3173,67 +3193,54 @@ void BytecodeGenerator::BuildFinalizeIteration(
} }
builder()->Bind(&if_callable); builder()->Bind(&if_callable);
// try {
// let return_val = method.call(iterator)
// if (!%IsObject(return_val)) throw TypeError
// }
{ {
RegisterAllocationScope register_scope(this); RegisterAllocationScope register_scope(this);
TryCatchBuilder try_control_builder(builder(), nullptr, nullptr, BuildTryCatch(
HandlerTable::UNCAUGHT); // try {
// let return_val = method.call(iterator)
// if (!%IsObject(return_val)) throw TypeError
// }
[&]() {
RegisterList args(iterator.object());
builder()->CallProperty(
method, args, feedback_index(feedback_spec()->AddCallICSlot()));
if (iterator.type() == IteratorType::kAsync) {
BuildAwait();
}
builder()->JumpIfJSReceiver(iterator_is_done.New());
{
// Throw this exception inside the try block so that it is
// suppressed by the iteration continuation if necessary.
RegisterAllocationScope register_scope(this);
Register return_result = register_allocator()->NewRegister();
builder()
->StoreAccumulatorInRegister(return_result)
.CallRuntime(Runtime::kThrowIteratorResultNotAnObject,
return_result);
}
},
// Preserve the context in a dedicated register, so that it can be restored // catch (e) {
// when the handler is entered by the stack-unwinding machinery. // if (iteration_continuation != RETHROW)
// TODO(mstarzinger): Be smarter about register allocation. // rethrow e
Register context = register_allocator()->NewRegister(); // }
builder()->MoveRegister(Register::current_context(), context); [&](Register context) {
// Reuse context register to store the exception.
Register close_exception = context;
builder()->StoreAccumulatorInRegister(close_exception);
// Evaluate the try-block inside a control scope. This simulates a handler BytecodeLabel suppress_close_exception;
// that is intercepting 'throw' control commands. builder()
try_control_builder.BeginTry(context); ->LoadLiteral(
{ Smi::FromInt(ControlScope::DeferredCommands::kRethrowToken))
ControlScopeForTryCatch scope(this, &try_control_builder); .CompareReference(iteration_continuation_token)
.JumpIfTrue(ToBooleanMode::kAlreadyBoolean,
RegisterList args(iterator.object()); &suppress_close_exception)
builder()->CallProperty(method, args, .LoadAccumulatorWithRegister(close_exception)
feedback_index(feedback_spec()->AddCallICSlot())); .ReThrow()
if (iterator.type() == IteratorType::kAsync) { .Bind(&suppress_close_exception);
BuildAwait(); },
} HandlerTable::UNCAUGHT);
builder()->JumpIfJSReceiver(iterator_is_done.New());
{
// Throw this exception inside the try block so that it is suppressed by
// the iteration continuation if necessary.
RegisterAllocationScope register_scope(this);
Register return_result = register_allocator()->NewRegister();
builder()
->StoreAccumulatorInRegister(return_result)
.CallRuntime(Runtime::kThrowIteratorResultNotAnObject,
return_result);
}
}
try_control_builder.EndTry();
// catch (e) {
// if (iteration_continuation != RETHROW)
// rethrow e
// }
// Reuse context register to store the exception.
Register close_exception = context;
builder()->StoreAccumulatorInRegister(close_exception);
BytecodeLabel suppress_close_exception;
builder()
->LoadLiteral(
Smi::FromInt(ControlScope::DeferredCommands::kRethrowToken))
.CompareReference(iteration_continuation_token)
.JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &suppress_close_exception)
.LoadAccumulatorWithRegister(close_exception)
.ReThrow()
.Bind(&suppress_close_exception);
try_control_builder.EndCatch();
} }
iterator_is_done.Bind(builder()); iterator_is_done.Bind(builder());

View File

@ -344,6 +344,10 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void BuildTest(ToBooleanMode mode, BytecodeLabels* then_labels, void BuildTest(ToBooleanMode mode, BytecodeLabels* then_labels,
BytecodeLabels* else_labels, TestFallthrough fallthrough); BytecodeLabels* else_labels, TestFallthrough fallthrough);
template <typename TryBodyFunc, typename CatchBodyFunc>
void BuildTryCatch(TryBodyFunc try_body_func, CatchBodyFunc catch_body_func,
HandlerTable::CatchPrediction catch_prediction,
TryCatchStatement* stmt_for_coverage = nullptr);
template <typename TryBodyFunc, typename FinallyBodyFunc> template <typename TryBodyFunc, typename FinallyBodyFunc>
void BuildTryFinally(TryBodyFunc try_body_func, void BuildTryFinally(TryBodyFunc try_body_func,
FinallyBodyFunc finally_body_func, FinallyBodyFunc finally_body_func,