Clear exception if catch block doesn't have move-exception.

Bug: 10040419

Change-Id: Icc7a55cb3cdfbc3efd2b161bbe22b3e5007de35f
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 8d7d028..37c45fa 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -975,7 +975,9 @@
   self->VerifyStack();
   ThrowLocation throw_location;
   mirror::Throwable* exception = self->GetException(&throw_location);
-  uint32_t found_dex_pc = shadow_frame.GetMethod()->FindCatchBlock(exception->GetClass(), dex_pc);
+  bool clear_exception;
+  uint32_t found_dex_pc = shadow_frame.GetMethod()->FindCatchBlock(exception->GetClass(), dex_pc,
+                                                                   &clear_exception);
   if (found_dex_pc == DexFile::kDexNoIndex) {
     instrumentation->MethodUnwindEvent(self, this_object_ref.get(),
                                        shadow_frame.GetMethod(), dex_pc);
@@ -984,6 +986,9 @@
     instrumentation->ExceptionCaughtEvent(self, throw_location,
                                           shadow_frame.GetMethod(),
                                           found_dex_pc, exception);
+    if (clear_exception) {
+      self->ClearException();
+    }
     return Instruction::At(insns + found_dex_pc);
   }
 }
diff --git a/runtime/mirror/abstract_method.cc b/runtime/mirror/abstract_method.cc
index 58ef5f7..d08708f 100644
--- a/runtime/mirror/abstract_method.cc
+++ b/runtime/mirror/abstract_method.cc
@@ -20,6 +20,7 @@
 #include "base/stringpiece.h"
 #include "class-inl.h"
 #include "dex_file-inl.h"
+#include "dex_instruction.h"
 #include "gc/accounting/card_table-inl.h"
 #include "interpreter/interpreter.h"
 #include "jni_internal.h"
@@ -225,7 +226,8 @@
   return 0;
 }
 
-uint32_t AbstractMethod::FindCatchBlock(Class* exception_type, uint32_t dex_pc) const {
+uint32_t AbstractMethod::FindCatchBlock(Class* exception_type, uint32_t dex_pc,
+                                        bool* has_no_move_exception) const {
   MethodHelper mh(this);
   const DexFile::CodeItem* code_item = mh.GetCodeItem();
   // Iterate over the catch handlers associated with dex_pc
@@ -242,7 +244,11 @@
       LOG(WARNING) << "Unresolved exception class when finding catch block: "
           << mh.GetTypeDescriptorFromTypeIdx(iter_type_idx);
     } else if (iter_exception_type->IsAssignableFrom(exception_type)) {
-      return it.GetHandlerAddress();
+      uint32_t found_dex_pc = it.GetHandlerAddress();
+      const Instruction* first_catch_instr =
+          Instruction::At(&mh.GetCodeItem()->insns_[found_dex_pc]);
+      *has_no_move_exception = (first_catch_instr->Opcode() != Instruction::MOVE_EXCEPTION);
+      return found_dex_pc;
     }
   }
   // Handler not found
diff --git a/runtime/mirror/abstract_method.h b/runtime/mirror/abstract_method.h
index bbebece..2e6e262 100644
--- a/runtime/mirror/abstract_method.h
+++ b/runtime/mirror/abstract_method.h
@@ -407,8 +407,10 @@
   uintptr_t ToFirstNativeSafepointPc(const uint32_t dex_pc)
       const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Find the catch block for the given exception type and dex_pc
-  uint32_t FindCatchBlock(Class* exception_type, uint32_t dex_pc) const
+  // Find the catch block for the given exception type and dex_pc. When a catch block is found,
+  // indicates whether the found catch block is responsible for clearing the exception or whether
+  // a move-exception instruction is present.
+  uint32_t FindCatchBlock(Class* exception_type, uint32_t dex_pc, bool* has_no_move_exception) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   static void SetClasses(Class* java_lang_reflect_Constructor, Class* java_lang_reflect_Method);
diff --git a/runtime/runtime_support_llvm.cc b/runtime/runtime_support_llvm.cc
index 713bc40..93396d6 100644
--- a/runtime/runtime_support_llvm.cc
+++ b/runtime/runtime_support_llvm.cc
@@ -324,6 +324,12 @@
                                                                    current_method,
                                                                    catch_dex_pc,
                                                                    exception);
+    // If the catch block has no move-exception then clear the exception for it.
+    const Instruction* first_catch_instr =
+        Instruction::At(&mh.GetCodeItem()->insns_[catch_dex_pc]);
+    if (first_catch_instr->Opcode() != Instruction::MOVE_EXCEPTION) {
+      self->ClearException();
+    }
   }
   return result;
 }
diff --git a/runtime/thread.cc b/runtime/thread.cc
index ea12c2e..d5fdd20 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1709,7 +1709,7 @@
         self_(self), exception_(exception), is_deoptimization_(is_deoptimization),
         to_find_(is_deoptimization ? NULL : exception->GetClass()), throw_location_(throw_location),
         handler_quick_frame_(NULL), handler_quick_frame_pc_(0), handler_dex_pc_(0),
-        native_method_count_(0),
+        native_method_count_(0), clear_exception_(false),
         method_tracing_active_(is_deoptimization ||
                                Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()),
         instrumentation_frames_to_pop_(0), top_shadow_frame_(NULL), prev_shadow_frame_(NULL) {
@@ -1754,7 +1754,7 @@
       dex_pc = GetDexPc();
     }
     if (dex_pc != DexFile::kDexNoIndex) {
-      uint32_t found_dex_pc = method->FindCatchBlock(to_find_, dex_pc);
+      uint32_t found_dex_pc = method->FindCatchBlock(to_find_, dex_pc, &clear_exception_);
       if (found_dex_pc != DexFile::kDexNoIndex) {
         handler_dex_pc_ = found_dex_pc;
         handler_quick_frame_pc_ = method->ToNativePc(found_dex_pc);
@@ -1820,8 +1820,13 @@
         LOG(INFO) << "Handler: " << PrettyMethod(catch_method) << " (line: " << line_number << ")";
       }
     }
-    // Put exception back in root set and clear throw location.
-    self_->SetException(ThrowLocation(), exception_);
+    if (clear_exception_) {
+      // Exception was cleared as part of delivery.
+      DCHECK(!self_->IsExceptionPending());
+    } else {
+      // Put exception back in root set with clear throw location.
+      self_->SetException(ThrowLocation(), exception_);
+    }
     self_->EndAssertNoThreadSuspension(last_no_assert_suspension_cause_);
     // Do instrumentation events after allowing thread suspension again.
     instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
@@ -1864,6 +1869,8 @@
   uint32_t handler_dex_pc_;
   // Number of native methods passed in crawl (equates to number of SIRTs to pop)
   uint32_t native_method_count_;
+  // Should the exception be cleared as the catch block has no move-exception?
+  bool clear_exception_;
   // Is method tracing active?
   const bool method_tracing_active_;
   // Support for nesting no thread suspension checks.
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 1481ad9..9f0d911 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -555,12 +555,6 @@
             << "exception handler starts at bad address (" << dex_pc << ")";
         return false;
       }
-      const Instruction* inst = Instruction::At(code_item_->insns_ + dex_pc);
-      if (inst->Opcode() != Instruction::MOVE_EXCEPTION) {
-        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "exception handler doesn't start with move-exception ("
-                                          << dex_pc << ")";
-        return false;
-      }
       insn_flags_[dex_pc].SetBranchTarget();
       // Ensure exception types are resolved so that they don't need resolution to be delivered,
       // unresolved exception types will be ignored by exception delivery