Ensure referenced catch block exceptions are in the image.

This addresses Bug: 5732744.

Change-Id: Ia9319b821f94bb27a11358f11b5473bb405277bb
diff --git a/src/dex2oat.cc b/src/dex2oat.cc
index 1261bb9..61d445e 100644
--- a/src/dex2oat.cc
+++ b/src/dex2oat.cc
@@ -14,6 +14,7 @@
 #include "compiler.h"
 #include "file.h"
 #include "image_writer.h"
+#include "leb128.h"
 #include "oat_writer.h"
 #include "object_utils.h"
 #include "os.h"
@@ -119,7 +120,7 @@
       return NULL;
     }
 
-    // Load all the classes specifed in the file
+    // Load all the classes specified in the file
     ClassLinker* class_linker = runtime_->GetClassLinker();
     while (image_classes_file->good()) {
       std::string dot;
@@ -136,11 +137,40 @@
     }
     image_classes_file->close();
 
+    // Resolve exception classes referenced by the loaded classes. The catch logic assumes
+    // exceptions are resolved by the verifier when there is a catch block in an interested method.
+    // Do this here so that exception classes appear to have been specified image classes.
+    std::set<std::pair<uint16_t, const DexFile*> > unresolved_exception_types;
+    do {
+      unresolved_exception_types.clear();
+      class_linker->VisitClasses(ResolveCatchBlockExceptionsClassVisitor,
+                                 &unresolved_exception_types);
+      typedef std::set<std::pair<uint16_t, const DexFile*> >::const_iterator It;  // TODO: C++0x auto
+      for (It it = unresolved_exception_types.begin(),
+           end = unresolved_exception_types.end();
+           it != end; ++it) {
+        uint16_t exception_type_idx = it->first;
+        const DexFile* dex_file = it->second;
+        DexCache* dex_cache = class_linker->FindDexCache(*dex_file);
+        ClassLoader* class_loader = NULL;
+        SirtRef<Class> klass(class_linker->ResolveType(*dex_file, exception_type_idx, dex_cache,
+                                                       class_loader));
+        if (klass.get() == NULL) {
+          const DexFile::TypeId& type_id = dex_file->GetTypeId(exception_type_idx);
+          const char* descriptor = dex_file->GetTypeDescriptor(type_id);
+          LOG(FATAL) << "Failed to resolve class " << descriptor;
+        }
+        DCHECK(klass->IsThrowableClass());
+      }
+      // Resolving exceptions may load classes that reference more exceptions, iterate until no
+      // more are found
+    } while (!unresolved_exception_types.empty());
+
     // We walk the roots looking for classes so that we'll pick up the
     // above classes plus any classes them depend on such super
     // classes, interfaces, and the required ClassLinker roots.
     UniquePtr<std::set<std::string> > image_classes(new std::set<std::string>());
-    class_linker->VisitClasses(ClassVisitor, image_classes.get());
+    class_linker->VisitClasses(RecordImageClassesVisitor, image_classes.get());
     CHECK_NE(image_classes->size(), 0U);
     return image_classes.release();
   }
@@ -238,7 +268,59 @@
     return runtime;
   }
 
-  static bool ClassVisitor(Class* klass, void* arg) {
+  static void ResolveExceptionsForMethod(MethodHelper* mh,
+                           std::set<std::pair<uint16_t, const DexFile*> >& exceptions_to_resolve) {
+    const DexFile::CodeItem* code_item = mh->GetCodeItem();
+    if (code_item == NULL) {
+      return;  // native or abstract method
+    }
+    if (code_item->tries_size_ == 0) {
+      return;  // nothing to process
+    }
+    const byte* encoded_catch_handler_list = DexFile::GetCatchHandlerData(*code_item, 0);
+    size_t num_encoded_catch_handlers = DecodeUnsignedLeb128(&encoded_catch_handler_list);
+    for (size_t i = 0; i < num_encoded_catch_handlers; i++) {
+      int32_t encoded_catch_handler_size = DecodeSignedLeb128(&encoded_catch_handler_list);
+      bool has_catch_all = false;
+      if (encoded_catch_handler_size <= 0) {
+        encoded_catch_handler_size = -encoded_catch_handler_size;
+        has_catch_all = true;
+      }
+      for (int32_t j = 0; j < encoded_catch_handler_size; j++) {
+        uint16_t encoded_catch_handler_handlers_type_idx =
+            DecodeUnsignedLeb128(&encoded_catch_handler_list);
+        // Add to set of types to resolve if not already in the dex cache resolved types
+        if (!mh->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
+          exceptions_to_resolve.insert(
+              std::pair<uint16_t, const DexFile*>(encoded_catch_handler_handlers_type_idx,
+                                                  &mh->GetDexFile()));
+        }
+        // ignore address associated with catch handler
+        DecodeUnsignedLeb128(&encoded_catch_handler_list);
+      }
+      if (has_catch_all) {
+        // ignore catch all address
+        DecodeUnsignedLeb128(&encoded_catch_handler_list);
+      }
+    }
+  }
+  static bool ResolveCatchBlockExceptionsClassVisitor(Class* c, void* arg) {
+    std::set<std::pair<uint16_t, const DexFile*> >* exceptions_to_resolve =
+        reinterpret_cast<std::set<std::pair<uint16_t, const DexFile*> >*>(arg);
+    MethodHelper mh;
+    for (size_t i = 0; i < c->NumVirtualMethods(); ++i) {
+      Method* m = c->GetVirtualMethod(i);
+      mh.ChangeMethod(m);
+      ResolveExceptionsForMethod(&mh, *exceptions_to_resolve);
+    }
+    for (size_t i = 0; i < c->NumDirectMethods(); ++i) {
+      Method* m = c->GetDirectMethod(i);
+      mh.ChangeMethod(m);
+      ResolveExceptionsForMethod(&mh, *exceptions_to_resolve);
+    }
+    return true;
+  }
+  static bool RecordImageClassesVisitor(Class* klass, void* arg) {
     std::set<std::string>* image_classes = reinterpret_cast<std::set<std::string>*>(arg);
     if (klass->IsArrayClass() || klass->IsPrimitive()) {
       return true;
diff --git a/src/object.cc b/src/object.cc
index 8d48250..c08e122 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -815,6 +815,11 @@
   return this == String::GetJavaLangString();
 }
 
+bool Class::IsThrowableClass() const {
+  Class* throwable = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Throwable;");
+  return throwable->IsAssignableFrom(this);
+}
+
 ClassLoader* Class::GetClassLoader() const {
   return GetFieldObject<ClassLoader*>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), false);
 }
diff --git a/src/object.h b/src/object.h
index 1a58364..ddba9db 100644
--- a/src/object.h
+++ b/src/object.h
@@ -1246,6 +1246,8 @@
 
   bool IsStringClass() const;
 
+  bool IsThrowableClass() const;
+
   Class* GetComponentType() const {
     return GetFieldObject<Class*>(OFFSET_OF_OBJECT_MEMBER(Class, component_type_), false);
   }
diff --git a/src/object_utils.h b/src/object_utils.h
index dc48aa0..f323190 100644
--- a/src/object_utils.h
+++ b/src/object_utils.h
@@ -438,6 +438,9 @@
   const DexFile::CodeItem* GetCodeItem() {
     return GetDexFile().GetCodeItem(method_->GetCodeItemOffset());
   }
+  bool IsResolvedTypeIdx(uint16_t type_idx) const {
+    return method_->GetDexCacheResolvedTypes()->Get(type_idx) != NULL;
+  }
   Class* GetClassFromTypeIdx(uint16_t type_idx) {
     Class* type = method_->GetDexCacheResolvedTypes()->Get(type_idx);
     if (type == NULL) {