Reduce and speed-up class def searches.

Use the class linker for descriptor lookups from the compile driver so that
dex caches are populated.
Reduce the scope of functions for scanning class paths to just the class
linker where they are performed.
If we see more than a threshold number of find class def misses on a dex file
lazily compute an index, so that future lookups are constant time (part of the
collection code is taken from
https://android-review.googlesource.com/#/c/103865/3). Note that we take a lazy
approach so that we don't serialize on loading dex files, this avoids the
reason the index was removed in 8b2c0b9abc3f520495f4387ea040132ba85cae69.
Remove an implicit and unnecessary std::string creation for PrintableString.

Single threaded interpret-only dex2oat performance is improved by roughly 10%.

Bug: 16853450

Change-Id: Icf72df76b0a4328f2a24075e81f4ff267b9401f4
(cherry picked from commit 68b56858367e29461ae290fd797443a1ef6d8005)
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index c1610c2..91c7aec 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1457,51 +1457,6 @@
   DISALLOW_COPY_AND_ASSIGN(ParallelCompilationManager);
 };
 
-static bool SkipClassCheckClassPath(const char* descriptor, const DexFile& dex_file,
-                                    const std::vector<const DexFile*>& classpath) {
-  DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, classpath);
-  CHECK(pair.second != NULL);
-  if (pair.first != &dex_file) {
-    LOG(WARNING) << "Skipping class " << descriptor << " from " << dex_file.GetLocation()
-                 << " previously found in " << pair.first->GetLocation();
-    return true;
-  }
-  return false;
-}
-
-// Return true if the class should be skipped during compilation.
-//
-// The first case where we skip is for redundant class definitions in
-// the boot classpath. We skip all but the first definition in that case.
-//
-// The second case where we skip is when an app bundles classes found
-// in the boot classpath. Since at runtime we will select the class from
-// the boot classpath, we ignore the one from the app.
-//
-// The third case is if the app itself has the class defined in multiple dex files. Then we skip
-// it if it is not the first occurrence.
-static bool SkipClass(ClassLinker* class_linker, jobject class_loader, const DexFile& dex_file,
-                      const std::vector<const DexFile*>& dex_files,
-                      const DexFile::ClassDef& class_def) {
-  const char* descriptor = dex_file.GetClassDescriptor(class_def);
-
-  if (class_loader == NULL) {
-    return SkipClassCheckClassPath(descriptor, dex_file, class_linker->GetBootClassPath());
-  }
-
-  if (class_linker->IsInBootClassPath(descriptor)) {
-    return true;
-  }
-
-  if (dex_files.size() > 1) {
-    // Multi-dex compilation, only take first class.
-    return SkipClassCheckClassPath(descriptor, dex_file, dex_files);
-  } else {
-    // Single dex, take everything.
-    return false;
-  }
-}
-
 // A fast version of SkipClass above if the class pointer is available
 // that avoids the expensive FindInClassPath search.
 static bool SkipClass(jobject class_loader, const DexFile& dex_file, mirror::Class* klass)
@@ -1568,81 +1523,84 @@
   // definitions, since many of them many never be referenced by
   // generated code.
   const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
-  if (!SkipClass(class_linker, jclass_loader, dex_file, manager->GetDexFiles(), class_def)) {
-    ScopedObjectAccess soa(self);
-    StackHandleScope<2> hs(soa.Self());
-    Handle<mirror::ClassLoader> class_loader(
-        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
-    Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file)));
-    // Resolve the class.
-    mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache,
-                                                     class_loader);
-    bool resolve_fields_and_methods;
-    if (klass == NULL) {
-      // Class couldn't be resolved, for example, super-class is in a different dex file. Don't
-      // attempt to resolve methods and fields when there is no declaring class.
-      CheckAndClearResolveException(soa.Self());
-      resolve_fields_and_methods = false;
-    } else {
-      resolve_fields_and_methods = manager->GetCompiler()->IsImage();
+  ScopedObjectAccess soa(self);
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+  Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file)));
+  // Resolve the class.
+  mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache,
+                                                   class_loader);
+  bool resolve_fields_and_methods;
+  if (klass == nullptr) {
+    // Class couldn't be resolved, for example, super-class is in a different dex file. Don't
+    // attempt to resolve methods and fields when there is no declaring class.
+    CheckAndClearResolveException(soa.Self());
+    resolve_fields_and_methods = false;
+  } else {
+    // We successfully resolved a class, should we skip it?
+    if (SkipClass(jclass_loader, dex_file, klass)) {
+      return;
     }
-    // Note the class_data pointer advances through the headers,
-    // static fields, instance fields, direct methods, and virtual
-    // methods.
-    const byte* class_data = dex_file.GetClassData(class_def);
-    if (class_data == NULL) {
-      // Empty class such as a marker interface.
-      requires_constructor_barrier = false;
-    } else {
-      ClassDataItemIterator it(dex_file, class_data);
-      while (it.HasNextStaticField()) {
-        if (resolve_fields_and_methods) {
-          mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
-                                                               dex_cache, class_loader, true);
-          if (field == NULL) {
-            CheckAndClearResolveException(soa.Self());
-          }
+    // We want to resolve the methods and fields eagerly.
+    resolve_fields_and_methods = true;
+  }
+  // Note the class_data pointer advances through the headers,
+  // static fields, instance fields, direct methods, and virtual
+  // methods.
+  const byte* class_data = dex_file.GetClassData(class_def);
+  if (class_data == NULL) {
+    // Empty class such as a marker interface.
+    requires_constructor_barrier = false;
+  } else {
+    ClassDataItemIterator it(dex_file, class_data);
+    while (it.HasNextStaticField()) {
+      if (resolve_fields_and_methods) {
+        mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
+                                                             dex_cache, class_loader, true);
+        if (field == NULL) {
+          CheckAndClearResolveException(soa.Self());
         }
-        it.Next();
       }
-      // We require a constructor barrier if there are final instance fields.
-      requires_constructor_barrier = false;
-      while (it.HasNextInstanceField()) {
-        if ((it.GetMemberAccessFlags() & kAccFinal) != 0) {
-          requires_constructor_barrier = true;
-        }
-        if (resolve_fields_and_methods) {
-          mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
-                                                               dex_cache, class_loader, false);
-          if (field == NULL) {
-            CheckAndClearResolveException(soa.Self());
-          }
-        }
-        it.Next();
+      it.Next();
+    }
+    // We require a constructor barrier if there are final instance fields.
+    requires_constructor_barrier = false;
+    while (it.HasNextInstanceField()) {
+      if ((it.GetMemberAccessFlags() & kAccFinal) != 0) {
+        requires_constructor_barrier = true;
       }
       if (resolve_fields_and_methods) {
-        while (it.HasNextDirectMethod()) {
-          mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
-                                                                  dex_cache, class_loader,
-                                                                  NullHandle<mirror::ArtMethod>(),
-                                                                  it.GetMethodInvokeType(class_def));
-          if (method == NULL) {
-            CheckAndClearResolveException(soa.Self());
-          }
-          it.Next();
+        mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
+                                                             dex_cache, class_loader, false);
+        if (field == NULL) {
+          CheckAndClearResolveException(soa.Self());
         }
-        while (it.HasNextVirtualMethod()) {
-          mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
-                                                                  dex_cache, class_loader,
-                                                                  NullHandle<mirror::ArtMethod>(),
-                                                                  it.GetMethodInvokeType(class_def));
-          if (method == NULL) {
-            CheckAndClearResolveException(soa.Self());
-          }
-          it.Next();
-        }
-        DCHECK(!it.HasNext());
       }
+      it.Next();
+    }
+    if (resolve_fields_and_methods) {
+      while (it.HasNextDirectMethod()) {
+        mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
+                                                                dex_cache, class_loader,
+                                                                NullHandle<mirror::ArtMethod>(),
+                                                                it.GetMethodInvokeType(class_def));
+        if (method == NULL) {
+          CheckAndClearResolveException(soa.Self());
+        }
+        it.Next();
+      }
+      while (it.HasNextVirtualMethod()) {
+        mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
+                                                                dex_cache, class_loader,
+                                                                NullHandle<mirror::ArtMethod>(),
+                                                                it.GetMethodInvokeType(class_def));
+        if (method == NULL) {
+          CheckAndClearResolveException(soa.Self());
+        }
+        it.Next();
+      }
+      DCHECK(!it.HasNext());
     }
   }
   if (requires_constructor_barrier) {
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 75bc49b..c1fb267 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -916,7 +916,7 @@
     } else if (type->IsStringClass()) {
       mirror::String* string = value->AsString();
       os << StringPrintf("%p   String: %s\n", string,
-                         PrintableString(string->ToModifiedUtf8()).c_str());
+                         PrintableString(string->ToModifiedUtf8().c_str()).c_str());
     } else if (type->IsClassClass()) {
       mirror::Class* klass = value->AsClass();
       os << StringPrintf("%p   Class: %s\n", klass, PrettyDescriptor(klass).c_str());
@@ -1049,7 +1049,7 @@
                          PrettyMethod(obj->AsArtMethod()).c_str());
     } else if (obj_class->IsStringClass()) {
       os << StringPrintf("%p: java.lang.String %s\n", obj,
-                         PrintableString(obj->AsString()->ToModifiedUtf8()).c_str());
+                         PrintableString(obj->AsString()->ToModifiedUtf8().c_str()).c_str());
     } else {
       os << StringPrintf("%p: %s\n", obj, PrettyDescriptor(obj_class).c_str());
     }
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 3af90b2..d05f7af 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -29,11 +29,6 @@
 
 namespace art {
 
-inline bool ClassLinker::IsInBootClassPath(const char* descriptor) {
-  DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_);
-  return pair.second != nullptr;
-}
-
 inline mirror::Class* ClassLinker::FindSystemClass(Thread* self, const char* descriptor) {
   return FindClass(self, descriptor, NullHandle<mirror::ClassLoader>());
 }
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 51ee448..453ee00 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1899,6 +1899,23 @@
   return klass;
 }
 
+typedef std::pair<const DexFile*, const DexFile::ClassDef*> ClassPathEntry;
+
+// Search a collection of DexFiles for a descriptor
+ClassPathEntry FindInClassPath(const char* descriptor,
+                               const std::vector<const DexFile*>& class_path) {
+  for (size_t i = 0; i != class_path.size(); ++i) {
+    const DexFile* dex_file = class_path[i];
+    const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
+    if (dex_class_def != NULL) {
+      return ClassPathEntry(dex_file, dex_class_def);
+    }
+  }
+  // TODO: remove reinterpret_cast when issue with -std=gnu++0x host issue resolved
+  return ClassPathEntry(reinterpret_cast<const DexFile*>(NULL),
+                        reinterpret_cast<const DexFile::ClassDef*>(NULL));
+}
+
 mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
                                       Handle<mirror::ClassLoader> class_loader) {
   DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
@@ -1911,25 +1928,31 @@
   }
   // Find the class in the loaded classes table.
   mirror::Class* klass = LookupClass(descriptor, class_loader.Get());
-  if (klass != NULL) {
+  if (klass != nullptr) {
     return EnsureResolved(self, descriptor, klass);
   }
   // Class is not yet loaded.
   if (descriptor[0] == '[') {
     return CreateArrayClass(self, descriptor, class_loader);
   } else if (class_loader.Get() == nullptr) {
-    DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_);
-    if (pair.second != NULL) {
+    ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+    if (pair.second != nullptr) {
       StackHandleScope<1> hs(self);
       return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
     }
   } else if (Runtime::Current()->UseCompileTimeClassPath()) {
-    // First try the boot class path, we check the descriptor first to avoid an unnecessary
-    // throw of a NoClassDefFoundError.
-    if (IsInBootClassPath(descriptor)) {
-      mirror::Class* system_class = FindSystemClass(self, descriptor);
-      CHECK(system_class != NULL);
-      return system_class;
+    // First try with the bootstrap class loader.
+    if (class_loader.Get() != nullptr) {
+      klass = LookupClass(descriptor, nullptr);
+      if (klass != nullptr) {
+        return EnsureResolved(self, descriptor, klass);
+      }
+    }
+    // If the lookup failed search the boot class path. We don't perform a recursive call to avoid a NoClassDefFoundError being allocated.
+    ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+    if (pair.second != nullptr) {
+      StackHandleScope<1> hs(self);
+      return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
     }
     // Next try the compile time class path.
     const std::vector<const DexFile*>* class_path;
@@ -1939,38 +1962,36 @@
                                             soa.AddLocalReference<jobject>(class_loader.Get()));
       class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get());
     }
-
-    DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, *class_path);
-    if (pair.second != NULL) {
+    pair = FindInClassPath(descriptor, *class_path);
+    if (pair.second != nullptr) {
       return DefineClass(descriptor, class_loader, *pair.first, *pair.second);
     }
-
   } else {
     ScopedObjectAccessUnchecked soa(self);
     ScopedLocalRef<jobject> class_loader_object(soa.Env(),
                                                 soa.AddLocalReference<jobject>(class_loader.Get()));
     std::string class_name_string(DescriptorToDot(descriptor));
-    ScopedLocalRef<jobject> result(soa.Env(), NULL);
+    ScopedLocalRef<jobject> result(soa.Env(), nullptr);
     {
       ScopedThreadStateChange tsc(self, kNative);
       ScopedLocalRef<jobject> class_name_object(soa.Env(),
                                                 soa.Env()->NewStringUTF(class_name_string.c_str()));
-      if (class_name_object.get() == NULL) {
-        return NULL;
+      if (class_name_object.get() == nullptr) {
+        return nullptr;
       }
-      CHECK(class_loader_object.get() != NULL);
+      CHECK(class_loader_object.get() != nullptr);
       result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
                                                WellKnownClasses::java_lang_ClassLoader_loadClass,
                                                class_name_object.get()));
     }
     if (self->IsExceptionPending()) {
       // If the ClassLoader threw, pass that exception up.
-      return NULL;
-    } else if (result.get() == NULL) {
+      return nullptr;
+    } else if (result.get() == nullptr) {
       // broken loader - throw NPE to be compatible with Dalvik
-      ThrowNullPointerException(NULL, StringPrintf("ClassLoader.loadClass returned null for %s",
+      ThrowNullPointerException(nullptr, StringPrintf("ClassLoader.loadClass returned null for %s",
                                                    class_name_string.c_str()).c_str());
-      return NULL;
+      return nullptr;
     } else {
       // success, return mirror::Class*
       return soa.Decode<mirror::Class*>(result.get());
@@ -1978,7 +1999,7 @@
   }
 
   ThrowNoClassDefFoundError("Class %s not found", PrintableString(descriptor).c_str());
-  return NULL;
+  return nullptr;
 }
 
 mirror::Class* ClassLinker::DefineClass(const char* descriptor,
@@ -3084,7 +3105,7 @@
       // Searching the image dex files/caches failed, we don't want to get into this situation
       // often as map searches are faster, so after kMaxFailedDexCacheLookups move all image
       // classes into the class table.
-      const int32_t kMaxFailedDexCacheLookups = 1000;
+      constexpr uint32_t kMaxFailedDexCacheLookups = 1000;
       if (++failed_dex_cache_class_lookups_ > kMaxFailedDexCacheLookups) {
         MoveImageClassesToClassTable();
       }
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index bf2e781..fbfb8fa 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -68,8 +68,6 @@
   // Initialize class linker from one or more images.
   void InitFromImage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool IsInBootClassPath(const char* descriptor);
-
   // Finds a class by its descriptor, loading it if necessary.
   // If class_loader is null, searches boot_class_path_.
   mirror::Class* FindClass(Thread* self, const char* descriptor,
@@ -638,7 +636,7 @@
   bool dex_cache_image_class_lookup_required_;
   // Number of times we've searched dex caches for a class. After a certain number of misses we move
   // the classes into the class_table_ to avoid dex cache based searches.
-  AtomicInteger failed_dex_cache_class_lookups_;
+  Atomic<uint32_t> failed_dex_cache_class_lookups_;
 
   mirror::Class* LookupClassFromTableLocked(const char* descriptor,
                                             const mirror::ClassLoader* class_loader,
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index ddc2147..38ea754 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -50,20 +50,6 @@
 const byte DexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' };
 const byte DexFile::kDexMagicVersion[] = { '0', '3', '5', '\0' };
 
-DexFile::ClassPathEntry DexFile::FindInClassPath(const char* descriptor,
-                                                 const ClassPath& class_path) {
-  for (size_t i = 0; i != class_path.size(); ++i) {
-    const DexFile* dex_file = class_path[i];
-    const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
-    if (dex_class_def != NULL) {
-      return ClassPathEntry(dex_file, dex_class_def);
-    }
-  }
-  // TODO: remove reinterpret_cast when issue with -std=gnu++0x host issue resolved
-  return ClassPathEntry(reinterpret_cast<const DexFile*>(NULL),
-                        reinterpret_cast<const DexFile::ClassDef*>(NULL));
-}
-
 static int OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) {
   CHECK(magic != NULL);
   ScopedFd fd(open(filename, O_RDONLY, 0));
@@ -366,7 +352,10 @@
       field_ids_(reinterpret_cast<const FieldId*>(base + header_->field_ids_off_)),
       method_ids_(reinterpret_cast<const MethodId*>(base + header_->method_ids_off_)),
       proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)),
-      class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)) {
+      class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
+      find_class_def_misses_(0),
+      class_def_index_(nullptr),
+      build_class_def_index_mutex_("DexFile index creation mutex") {
   CHECK(begin_ != NULL) << GetLocation();
   CHECK_GT(size_, 0U) << GetLocation();
 }
@@ -376,6 +365,8 @@
   // that's only called after DetachCurrentThread, which means there's no JNIEnv. We could
   // re-attach, but cleaning up these global references is not obviously useful. It's not as if
   // the global reference table is otherwise empty!
+  // Remove the index if one were created.
+  delete class_def_index_.LoadRelaxed();
 }
 
 bool DexFile::Init(std::string* error_msg) {
@@ -424,26 +415,51 @@
 }
 
 const DexFile::ClassDef* DexFile::FindClassDef(const char* descriptor) const {
-  size_t num_class_defs = NumClassDefs();
+  // If we have an index lookup the descriptor via that as its constant time to search.
+  Index* index = class_def_index_.LoadSequentiallyConsistent();
+  if (index != nullptr) {
+    auto it = index->find(descriptor);
+    return (it == index->end()) ? nullptr : it->second;
+  }
+  // Fast path for rate no class defs case.
+  uint32_t num_class_defs = NumClassDefs();
   if (num_class_defs == 0) {
-    return NULL;
+    return nullptr;
   }
+  // Search for class def with 2 binary searches and then a linear search.
   const StringId* string_id = FindStringId(descriptor);
-  if (string_id == NULL) {
-    return NULL;
-  }
-  const TypeId* type_id = FindTypeId(GetIndexForStringId(*string_id));
-  if (type_id == NULL) {
-    return NULL;
-  }
-  uint16_t type_idx = GetIndexForTypeId(*type_id);
-  for (size_t i = 0; i < num_class_defs; ++i) {
-    const ClassDef& class_def = GetClassDef(i);
-    if (class_def.class_idx_ == type_idx) {
-      return &class_def;
+  if (string_id != nullptr) {
+    const TypeId* type_id = FindTypeId(GetIndexForStringId(*string_id));
+    if (type_id != nullptr) {
+      uint16_t type_idx = GetIndexForTypeId(*type_id);
+      for (size_t i = 0; i < num_class_defs; ++i) {
+        const ClassDef& class_def = GetClassDef(i);
+        if (class_def.class_idx_ == type_idx) {
+          return &class_def;
+        }
+      }
     }
   }
-  return NULL;
+  // A miss. If we've had kMaxFailedDexClassDefLookups misses then build an index to speed things
+  // up. This isn't done eagerly at construction as construction is not performed in multi-threaded
+  // sections of tools like dex2oat. If we're lazy we hopefully increase the chance of balancing
+  // out which thread builds the index.
+  find_class_def_misses_++;
+  const uint32_t kMaxFailedDexClassDefLookups = 100;
+  if (find_class_def_misses_ > kMaxFailedDexClassDefLookups) {
+    MutexLock mu(Thread::Current(), build_class_def_index_mutex_);
+    // Are we the first ones building the index?
+    if (class_def_index_.LoadSequentiallyConsistent() == nullptr) {
+      index = new Index(num_class_defs);
+      for (uint32_t i = 0; i < num_class_defs;  ++i) {
+        const ClassDef& class_def = GetClassDef(i);
+        const char* descriptor = GetClassDescriptor(class_def);
+        index->insert(std::make_pair(descriptor, &class_def));
+      }
+      class_def_index_.StoreSequentiallyConsistent(index);
+    }
+  }
+  return nullptr;
 }
 
 const DexFile::ClassDef* DexFile::FindClassDef(uint16_t type_idx) const {
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index a71a5e1..1da1f81 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -19,6 +19,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/logging.h"
@@ -27,7 +28,7 @@
 #include "invoke_type.h"
 #include "jni.h"
 #include "modifiers.h"
-#include "safe_map.h"
+#include "utf.h"
 
 namespace art {
 
@@ -356,13 +357,6 @@
     DISALLOW_COPY_AND_ASSIGN(AnnotationItem);
   };
 
-  typedef std::pair<const DexFile*, const DexFile::ClassDef*> ClassPathEntry;
-  typedef std::vector<const DexFile*> ClassPath;
-
-  // Search a collection of DexFiles for a descriptor
-  static ClassPathEntry FindInClassPath(const char* descriptor,
-                                        const ClassPath& class_path);
-
   // Returns the checksum of a file for comparison with GetLocationChecksum().
   // For .dex files, this is the header checksum.
   // For zip files, this is the classes.dex zip entry CRC32 checksum.
@@ -478,7 +472,7 @@
   const StringId* FindStringId(const uint16_t* string) const;
 
   // Returns the number of type identifiers in the .dex file.
-  size_t NumTypeIds() const {
+  uint32_t NumTypeIds() const {
     DCHECK(header_ != NULL) << GetLocation();
     return header_->type_ids_size_;
   }
@@ -607,7 +601,7 @@
     return StringDataAndUtf16LengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length);
   }
   // Returns the number of class definitions in the .dex file.
-  size_t NumClassDefs() const {
+  uint32_t NumClassDefs() const {
     DCHECK(header_ != NULL) << GetLocation();
     return header_->class_defs_size_;
   }
@@ -969,6 +963,23 @@
 
   // Points to the base of the class definition list.
   const ClassDef* const class_defs_;
+
+  // Number of misses finding a class def from a descriptor.
+  mutable Atomic<uint32_t> find_class_def_misses_;
+
+  struct UTF16HashCmp {
+    // Hash function.
+    size_t operator()(const char* key) const {
+      return ComputeUtf8Hash(key);
+    }
+    // std::equal function.
+    bool operator()(const char* a, const char* b) const {
+      return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(a, b) == 0;
+    }
+  };
+  typedef std::unordered_map<const char*, const ClassDef*, UTF16HashCmp, UTF16HashCmp> Index;
+  mutable Atomic<Index*> class_def_index_;
+  mutable Mutex build_class_def_index_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER;
 };
 std::ostream& operator<<(std::ostream& os, const DexFile& dex_file);
 
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index b9379f5..330d235 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -921,7 +921,7 @@
   ObjectId stringObject = request.ReadObjectId();
   std::string str(Dbg::StringToUtf8(stringObject));
 
-  VLOG(jdwp) << StringPrintf("    --> %s", PrintableString(str).c_str());
+  VLOG(jdwp) << StringPrintf("    --> %s", PrintableString(str.c_str()).c_str());
 
   expandBufAddUtf8String(pReply, str);
 
diff --git a/runtime/utf.cc b/runtime/utf.cc
index e48d6d2..02cbe3b 100644
--- a/runtime/utf.cc
+++ b/runtime/utf.cc
@@ -85,6 +85,14 @@
   return hash;
 }
 
+int32_t ComputeUtf8Hash(const char* chars) {
+  int32_t hash = 0;
+  while (*chars != '\0') {
+    hash = hash * 31 + GetUtf16FromUtf8(&chars);
+  }
+  return hash;
+}
+
 int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8_1, const uint16_t* utf8_2) {
   for (;;) {
     if (*utf8_1 == '\0') {
diff --git a/runtime/utf.h b/runtime/utf.h
index 63cdbdc..a09db9d 100644
--- a/runtime/utf.h
+++ b/runtime/utf.h
@@ -79,6 +79,9 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 int32_t ComputeUtf16Hash(const uint16_t* chars, size_t char_count);
 
+// Compute a hash code of a UTF-8 string.
+int32_t ComputeUtf8Hash(const char* chars);
+
 /*
  * Retrieve the next UTF-16 character from a UTF-8 string.
  *
diff --git a/runtime/utils.cc b/runtime/utils.cc
index d704eec..c4e84f3 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -568,10 +568,10 @@
   return result;
 }
 
-std::string PrintableString(const std::string& utf) {
+std::string PrintableString(const char* utf) {
   std::string result;
   result += '"';
-  const char* p = utf.c_str();
+  const char* p = utf;
   size_t char_count = CountModifiedUtf8Chars(p);
   for (size_t i = 0; i < char_count; ++i) {
     uint16_t ch = GetUtf16FromUtf8(&p);
diff --git a/runtime/utils.h b/runtime/utils.h
index c4a5895..a34a01d 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -261,7 +261,7 @@
 
 // Returns an ASCII string corresponding to the given UTF-8 string.
 // Java escapes are used for non-ASCII characters.
-std::string PrintableString(const std::string& utf8);
+std::string PrintableString(const char* utf8);
 
 // Tests whether 's' starts with 'prefix'.
 bool StartsWith(const std::string& s, const char* prefix);