Fix exception handling during deoptimization
When interpreting a deoptimized shadow frame, we may start with a
pending exception thrown by a previous deoptimized shadow frame (from
a previous invoke). Therefore, we need to handle it before executing
any instruction, otherwise we execute incorrect code.
Because we need the DEX pc of the throwing instruction to find a
matching catch handler, we initialize deoptimized shadow frames with
the current DEX pc at the time the stack is deoptimized.
When we are about to interpret a deoptimized shadow frame, we need to
update the shadow frame with the DEX pc of the next instruction to
interpret. There are three cases:
- if there is no pending exception, this is the instruction following
the current one.
- if there is a pending exception and we found a matching catch
handler, this is the first instruction of this handler.
- if there is a pending exception but there is no matching catch
handler, we do not execute the deoptimized shadow frame and continue
to its caller.
The verifier now fails when a method starts with a move-exception
instruction. Indeed we cannot start executing a method with a pending
exception.
Bug: 19057915
Bug: 19041195
Bug: 18607595
Change-Id: I355ac81e6ac098edc7e3cc8c13dbfa24a2969ab2
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 38665c7..2f85587 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -69,7 +69,10 @@
uint32_t dex_pc = shadow_frame.GetDexPC();
bool notified_method_entry_event = false;
const instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation();
- if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing..
+ if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing.
+ if (kIsDebugBuild) {
+ self->AssertNoPendingException();
+ }
if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
shadow_frame.GetMethod(), 0);
@@ -161,6 +164,7 @@
case Instruction::MOVE_EXCEPTION: {
PREAMBLE();
Throwable* exception = self->GetException(nullptr);
+ DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction";
shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
self->ClearException();
inst = inst->Next_1xx();