Merge "Revert "Revert "Add test for changing annotations."""
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 5c49f19..c319b1a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -92,7 +92,7 @@
 ART_GTEST_class_table_test_DEX_DEPS := XandY
 ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
 ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
-ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested
+ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested MultiDex
 ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps
 ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
 ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB
@@ -102,6 +102,7 @@
 ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
 ART_GTEST_oat_file_assistant_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
 ART_GTEST_dexoptanalyzer_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
+ART_GTEST_image_space_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
 ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex
 ART_GTEST_oat_test_DEX_DEPS := Main
 ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
@@ -118,8 +119,8 @@
 ART_GTEST_dex_to_dex_decompiler_test_DEX_DEPS := VerifierDeps DexToDexDecompiler
 
 # The elf writer test has dependencies on core.oat.
-ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_optimizing_no-pic_64) $(HOST_CORE_IMAGE_optimizing_no-pic_32)
-ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_optimizing_no-pic_64) $(TARGET_CORE_IMAGE_optimizing_no-pic_32)
+ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32)
+ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_DEFAULT_64) $(TARGET_CORE_IMAGE_DEFAULT_32)
 
 ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \
   $(HOST_CORE_IMAGE_optimizing_pic_64) \
@@ -146,69 +147,74 @@
   $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \
   dexoptanalyzerd
 
+ART_GTEST_image_space_test_HOST_DEPS := \
+  $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
+ART_GTEST_image_space_test_TARGET_DEPS := \
+  $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
+
 ART_GTEST_dex2oat_test_HOST_DEPS := \
   $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
 ART_GTEST_dex2oat_test_TARGET_DEPS := \
   $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
 
 # TODO: document why this is needed.
-ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_optimizing_no-pic_64) $(HOST_CORE_IMAGE_optimizing_no-pic_32)
+ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32)
 
 # The dexdump test requires an image and the dexdump utility.
 # TODO: rename into dexdump when migration completes
 ART_GTEST_dexdump_test_HOST_DEPS := \
-  $(HOST_CORE_IMAGE_optimizing_no-pic_64) \
-  $(HOST_CORE_IMAGE_optimizing_no-pic_32) \
+  $(HOST_CORE_IMAGE_DEFAULT_64) \
+  $(HOST_CORE_IMAGE_DEFAULT_32) \
   $(HOST_OUT_EXECUTABLES)/dexdump2
 ART_GTEST_dexdump_test_TARGET_DEPS := \
-  $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \
-  $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \
+  $(TARGET_CORE_IMAGE_DEFAULT_64) \
+  $(TARGET_CORE_IMAGE_DEFAULT_32) \
   dexdump2
 
 # The dexlayout test requires an image and the dexlayout utility.
 # TODO: rename into dexdump when migration completes
 ART_GTEST_dexlayout_test_HOST_DEPS := \
-  $(HOST_CORE_IMAGE_optimizing_no-pic_64) \
-  $(HOST_CORE_IMAGE_optimizing_no-pic_32) \
+  $(HOST_CORE_IMAGE_DEFAULT_64) \
+  $(HOST_CORE_IMAGE_DEFAULT_32) \
   $(HOST_OUT_EXECUTABLES)/dexlayout \
   $(HOST_OUT_EXECUTABLES)/dexdump2
 ART_GTEST_dexlayout_test_TARGET_DEPS := \
-  $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \
-  $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \
+  $(TARGET_CORE_IMAGE_DEFAULT_64) \
+  $(TARGET_CORE_IMAGE_DEFAULT_32) \
   dexlayout \
   dexdump2
 
 # The dexlist test requires an image and the dexlist utility.
 ART_GTEST_dexlist_test_HOST_DEPS := \
-  $(HOST_CORE_IMAGE_optimizing_no-pic_64) \
-  $(HOST_CORE_IMAGE_optimizing_no-pic_32) \
+  $(HOST_CORE_IMAGE_DEFAULT_64) \
+  $(HOST_CORE_IMAGE_DEFAULT_32) \
   $(HOST_OUT_EXECUTABLES)/dexlist
 ART_GTEST_dexlist_test_TARGET_DEPS := \
-  $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \
-  $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \
+  $(TARGET_CORE_IMAGE_DEFAULT_64) \
+  $(TARGET_CORE_IMAGE_DEFAULT_32) \
   dexlist
 
 # The imgdiag test has dependencies on core.oat since it needs to load it during the test.
 # For the host, also add the installed tool (in the base size, that should suffice). For the
 # target, just the module is fine, the sync will happen late enough.
 ART_GTEST_imgdiag_test_HOST_DEPS := \
-  $(HOST_CORE_IMAGE_optimizing_no-pic_64) \
-  $(HOST_CORE_IMAGE_optimizing_no-pic_32) \
+  $(HOST_CORE_IMAGE_DEFAULT_64) \
+  $(HOST_CORE_IMAGE_DEFAULT_32) \
   $(HOST_OUT_EXECUTABLES)/imgdiagd
 ART_GTEST_imgdiag_test_TARGET_DEPS := \
-  $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \
-  $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \
+  $(TARGET_CORE_IMAGE_DEFAULT_64) \
+  $(TARGET_CORE_IMAGE_DEFAULT_32) \
   imgdiagd
 
 # Oatdump test requires an image and oatfile to dump.
 ART_GTEST_oatdump_test_HOST_DEPS := \
-  $(HOST_CORE_IMAGE_optimizing_no-pic_64) \
-  $(HOST_CORE_IMAGE_optimizing_no-pic_32) \
+  $(HOST_CORE_IMAGE_DEFAULT_64) \
+  $(HOST_CORE_IMAGE_DEFAULT_32) \
   $(HOST_OUT_EXECUTABLES)/oatdumpd \
   $(HOST_OUT_EXECUTABLES)/oatdumpds
 ART_GTEST_oatdump_test_TARGET_DEPS := \
-  $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \
-  $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \
+  $(TARGET_CORE_IMAGE_DEFAULT_64) \
+  $(TARGET_CORE_IMAGE_DEFAULT_32) \
   oatdump
 
 # Profile assistant tests requires profman utility.
@@ -627,6 +633,9 @@
 ART_GTEST_dexoptanalyzer_test_DEX_DEPS :=
 ART_GTEST_dexoptanalyzer_test_HOST_DEPS :=
 ART_GTEST_dexoptanalyzer_test_TARGET_DEPS :=
+ART_GTEST_image_space_test_DEX_DEPS :=
+ART_GTEST_image_space_test_HOST_DEPS :=
+ART_GTEST_image_space_test_TARGET_DEPS :=
 ART_GTEST_dex2oat_test_DEX_DEPS :=
 ART_GTEST_dex2oat_test_HOST_DEPS :=
 ART_GTEST_dex2oat_test_TARGET_DEPS :=
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index e297b4f..0086660 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -281,3 +281,10 @@
 valgrind-test-art-host-dex2oat-target: $(valgrindTARGET_CORE_IMG_OUTS)
 
 valgrind-test-art-host-dex2oat: valgrind-test-art-host-dex2oat-host valgrind-test-art-host-dex2oat-target
+
+# Define a default core image that can be used for things like gtests that
+# need some image to run, but don't otherwise care which image is used.
+HOST_CORE_IMAGE_DEFAULT_32 := $(HOST_CORE_IMAGE_optimizing_pic_32)
+HOST_CORE_IMAGE_DEFAULT_64 := $(HOST_CORE_IMAGE_optimizing_pic_64)
+TARGET_CORE_IMAGE_DEFAULT_32 := $(TARGET_CORE_IMAGE_optimizing_pic_32)
+TARGET_CORE_IMAGE_DEFAULT_64 := $(TARGET_CORE_IMAGE_optimizing_pic_64)
diff --git a/cmdline/cmdline.h b/cmdline/cmdline.h
index f4540ff..98010d7 100644
--- a/cmdline/cmdline.h
+++ b/cmdline/cmdline.h
@@ -267,7 +267,7 @@
       std::string file_name;
       if (!LocationToFilename(boot_image_location, instruction_set_, &file_name)) {
         *error_msg = android::base::StringPrintf("No corresponding file for location '%s' exists",
-                                                 file_name.c_str());
+                                                 boot_image_location.c_str());
         return false;
       }
 
diff --git a/compiler/compiler.h b/compiler/compiler.h
index 908d366..2ca0b77 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -27,7 +27,6 @@
   class JitCodeCache;
 }
 namespace mirror {
-  class ClassLoader;
   class DexCache;
 }
 
@@ -64,7 +63,7 @@
                                   InvokeType invoke_type,
                                   uint16_t class_def_idx,
                                   uint32_t method_idx,
-                                  Handle<mirror::ClassLoader> class_loader,
+                                  jobject class_loader,
                                   const DexFile& dex_file,
                                   Handle<mirror::DexCache> dex_cache) const = 0;
 
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 76aeaa5..d4f6545 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -284,13 +284,16 @@
   }
   uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
   ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(unit_.GetClassLoader())));
 
   ClassLinker* class_linker = unit_.GetClassLinker();
   ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::kForceICCECheck>(
       GetDexFile(),
       method_idx,
       unit_.GetDexCache(),
-      unit_.GetClassLoader(),
+      class_loader,
       /* referrer */ nullptr,
       kVirtual);
 
@@ -327,7 +330,7 @@
     InvokeType invoke_type ATTRIBUTE_UNUSED,
     uint16_t class_def_idx,
     uint32_t method_idx,
-    Handle<mirror::ClassLoader> class_loader,
+    jobject class_loader,
     const DexFile& dex_file,
     DexToDexCompilationLevel dex_to_dex_compilation_level) {
   DCHECK(driver != nullptr);
diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h
index 00c596d..0a00d45 100644
--- a/compiler/dex/dex_to_dex_compiler.h
+++ b/compiler/dex/dex_to_dex_compiler.h
@@ -18,7 +18,6 @@
 #define ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_
 
 #include "dex_file.h"
-#include "handle.h"
 #include "invoke_type.h"
 
 namespace art {
@@ -26,10 +25,6 @@
 class CompiledMethod;
 class CompilerDriver;
 
-namespace mirror {
-class ClassLoader;
-}  // namespace mirror
-
 namespace optimizer {
 
 enum class DexToDexCompilationLevel {
@@ -45,7 +40,7 @@
                               InvokeType invoke_type,
                               uint16_t class_def_idx,
                               uint32_t method_idx,
-                              Handle<mirror::ClassLoader> class_loader,
+                              jobject class_loader,
                               const DexFile& dex_file,
                               DexToDexCompilationLevel dex_to_dex_compilation_level);
 
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 5823306..f296851 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -31,12 +31,17 @@
 
 namespace art {
 
+inline mirror::ClassLoader* CompilerDriver::GetClassLoader(const ScopedObjectAccess& soa,
+                                                           const DexCompilationUnit* mUnit) {
+  return soa.Decode<mirror::ClassLoader>(mUnit->GetClassLoader()).Ptr();
+}
+
 inline mirror::Class* CompilerDriver::ResolveClass(
     const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader, dex::TypeIndex cls_index,
     const DexCompilationUnit* mUnit) {
   DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile());
-  DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
+  DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
   mirror::Class* cls = mUnit->GetClassLinker()->ResolveType(
       *mUnit->GetDexFile(), cls_index, dex_cache, class_loader);
   DCHECK_EQ(cls == nullptr, soa.Self()->IsExceptionPending());
@@ -51,7 +56,7 @@
     const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) {
   DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile());
-  DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
+  DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
   const DexFile::MethodId& referrer_method_id =
       mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex());
   return ResolveClass(soa, dex_cache, class_loader, referrer_method_id.class_idx_, mUnit);
@@ -82,7 +87,7 @@
     const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
     uint32_t field_idx, bool is_static) {
-  DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
+  DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
   return ResolveFieldWithDexFile(soa, dex_cache, class_loader, mUnit->GetDexFile(), field_idx,
                                  is_static);
 }
@@ -134,7 +139,7 @@
     ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
     uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change) {
-  DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
+  DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
   ArtMethod* resolved_method =
       check_incompatible_class_change
           ? mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kForceICCECheck>(
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index b738d5c..26c0818 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -311,9 +311,6 @@
 
   compiler_->Init();
 
-  if (compiler_options->VerifyOnlyProfile()) {
-    CHECK(profile_compilation_info_ != nullptr) << "Requires profile";
-  }
   if (GetCompilerOptions().IsBootImage()) {
     CHECK(image_classes_.get() != nullptr) << "Expected image classes for boot image";
   }
@@ -583,7 +580,7 @@
                           InvokeType invoke_type,
                           uint16_t class_def_idx,
                           uint32_t method_idx,
-                          Handle<mirror::ClassLoader> class_loader,
+                          jobject class_loader,
                           const DexFile& dex_file,
                           optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level,
                           bool compilation_enabled,
@@ -624,6 +621,9 @@
       // Look-up the ArtMethod associated with this code_item (if any)
       // -- It is later used to lookup any [optimization] annotations for this method.
       ScopedObjectAccess soa(self);
+      StackHandleScope<1> hs(soa.Self());
+      Handle<mirror::ClassLoader> class_loader_handle(hs.NewHandle(
+          soa.Decode<mirror::ClassLoader>(class_loader)));
 
       // TODO: Lookup annotation from DexFile directly without resolving method.
       ArtMethod* method =
@@ -631,7 +631,7 @@
               dex_file,
               method_idx,
               dex_cache,
-              class_loader,
+              class_loader_handle,
               /* referrer */ nullptr,
               invoke_type);
 
@@ -678,14 +678,9 @@
 
     if (compile) {
       // NOTE: if compiler declines to compile this method, it will return null.
-      compiled_method = driver->GetCompiler()->Compile(code_item,
-                                                       access_flags,
-                                                       invoke_type,
-                                                       class_def_idx,
-                                                       method_idx,
-                                                       class_loader,
-                                                       dex_file,
-                                                       dex_cache);
+      compiled_method = driver->GetCompiler()->Compile(code_item, access_flags, invoke_type,
+                                                       class_def_idx, method_idx, class_loader,
+                                                       dex_file, dex_cache);
     }
     if (compiled_method == nullptr &&
         dex_to_dex_compilation_level != optimizer::DexToDexCompilationLevel::kDontDexToDexCompile) {
@@ -732,14 +727,12 @@
   uint32_t method_idx = method->GetDexMethodIndex();
   uint32_t access_flags = method->GetAccessFlags();
   InvokeType invoke_type = method->GetInvokeType();
-  StackHandleScope<2> hs(self);
+  StackHandleScope<1> hs(self);
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
-  Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(method->GetDeclaringClass()->GetClassLoader()));
   {
     ScopedObjectAccessUnchecked soa(self);
     ScopedLocalRef<jobject> local_class_loader(
-        soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get()));
+        soa.Env(), soa.AddLocalReference<jobject>(method->GetDeclaringClass()->GetClassLoader()));
     jclass_loader = soa.Env()->NewGlobalRef(local_class_loader.get());
     // Find the dex_file
     dex_file = method->GetDexFile();
@@ -773,7 +766,7 @@
                 invoke_type,
                 class_def_idx,
                 method_idx,
-                class_loader,
+                jclass_loader,
                 *dex_file,
                 dex_to_dex_compilation_level,
                 true,
@@ -799,7 +792,7 @@
                   invoke_type,
                   class_def_idx,
                   method_idx,
-                  class_loader,
+                  jclass_loader,
                   *dex_file,
                   dex_to_dex_compilation_level,
                   true,
@@ -964,7 +957,7 @@
       const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
       const char* descriptor = dex_file->GetClassDescriptor(class_def);
       cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader));
-      if (cls.Get() == nullptr) {
+      if (cls == nullptr) {
         soa.Self()->ClearException();
       } else if (&cls->GetDexFile() == dex_file) {
         DCHECK(cls->IsErroneous() || cls->IsVerified() || cls->IsCompileTimeVerified())
@@ -1074,30 +1067,22 @@
 
 class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor {
  public:
-  ResolveCatchBlockExceptionsClassVisitor() : classes_() {}
+  explicit ResolveCatchBlockExceptionsClassVisitor(
+      std::set<std::pair<dex::TypeIndex, const DexFile*>>& exceptions_to_resolve)
+     : exceptions_to_resolve_(exceptions_to_resolve) {}
 
   virtual bool operator()(ObjPtr<mirror::Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
-    classes_.push_back(c);
+    const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+    for (auto& m : c->GetMethods(pointer_size)) {
+      ResolveExceptionsForMethod(&m);
+    }
     return true;
   }
 
-  void FindExceptionTypesToResolve(
-      std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-    for (ObjPtr<mirror::Class> klass : classes_) {
-      for (ArtMethod& method : klass->GetMethods(pointer_size)) {
-        FindExceptionTypesToResolveForMethod(&method, exceptions_to_resolve);
-      }
-    }
-  }
-
  private:
-  void FindExceptionTypesToResolveForMethod(
-      ArtMethod* method,
-      std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve)
+  void ResolveExceptionsForMethod(ArtMethod* method_handle)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    const DexFile::CodeItem* code_item = method->GetCodeItem();
+    const DexFile::CodeItem* code_item = method_handle->GetCodeItem();
     if (code_item == nullptr) {
       return;  // native or abstract method
     }
@@ -1117,9 +1102,9 @@
         dex::TypeIndex encoded_catch_handler_handlers_type_idx =
             dex::TypeIndex(DecodeUnsignedLeb128(&encoded_catch_handler_list));
         // Add to set of types to resolve if not already in the dex cache resolved types
-        if (!method->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
-          exceptions_to_resolve->emplace(encoded_catch_handler_handlers_type_idx,
-                                         method->GetDexFile());
+        if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
+          exceptions_to_resolve_.emplace(encoded_catch_handler_handlers_type_idx,
+                                         method_handle->GetDexFile());
         }
         // ignore address associated with catch handler
         DecodeUnsignedLeb128(&encoded_catch_handler_list);
@@ -1131,7 +1116,7 @@
     }
   }
 
-  std::vector<ObjPtr<mirror::Class>> classes_;
+  std::set<std::pair<dex::TypeIndex, const DexFile*>>& exceptions_to_resolve_;
 };
 
 class RecordImageClassesVisitor : public ClassVisitor {
@@ -1167,7 +1152,7 @@
     StackHandleScope<1> hs(self);
     Handle<mirror::Class> klass(
         hs.NewHandle(class_linker->FindSystemClass(self, descriptor.c_str())));
-    if (klass.Get() == nullptr) {
+    if (klass == nullptr) {
       VLOG(compiler) << "Failed to find class " << descriptor;
       image_classes_->erase(it++);
       self->ClearException();
@@ -1185,14 +1170,8 @@
       hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/lang/Throwable;")));
   do {
     unresolved_exception_types.clear();
-    {
-      // Thread suspension is not allowed while ResolveCatchBlockExceptionsClassVisitor
-      // is using a std::vector<ObjPtr<mirror::Class>>.
-      ScopedAssertNoThreadSuspension ants(__FUNCTION__);
-      ResolveCatchBlockExceptionsClassVisitor visitor;
-      class_linker->VisitClasses(&visitor);
-      visitor.FindExceptionTypesToResolve(&unresolved_exception_types);
-    }
+    ResolveCatchBlockExceptionsClassVisitor visitor(unresolved_exception_types);
+    class_linker->VisitClasses(&visitor);
     for (const auto& exception_type : unresolved_exception_types) {
       dex::TypeIndex exception_type_idx = exception_type.first;
       const DexFile* dex_file = exception_type.second;
@@ -1200,13 +1179,13 @@
       Handle<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(*dex_file,
                                                                                      nullptr)));
       Handle<mirror::Class> klass(hs2.NewHandle(
-          (dex_cache.Get() != nullptr)
+          (dex_cache != nullptr)
               ? class_linker->ResolveType(*dex_file,
                                           exception_type_idx,
                                           dex_cache,
                                           ScopedNullHandle<mirror::ClassLoader>())
               : nullptr));
-      if (klass.Get() == nullptr) {
+      if (klass == nullptr) {
         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;
@@ -1443,14 +1422,19 @@
   dex_to_dex_references_.back().GetMethodIndexes().SetBit(method_ref.dex_method_index);
 }
 
-bool CompilerDriver::CanAccessTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class,
-                                                ObjPtr<mirror::Class> resolved_class) {
+bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx,
+                                                Handle<mirror::DexCache> dex_cache,
+                                                dex::TypeIndex type_idx) {
+  // Get type from dex cache assuming it was populated by the verifier
+  mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
   if (resolved_class == nullptr) {
     stats_->TypeNeedsAccessCheck();
     return false;  // Unknown class needs access checks.
   }
+  const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(referrer_idx);
   bool is_accessible = resolved_class->IsPublic();  // Public classes are always accessible.
   if (!is_accessible) {
+    mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
     if (referrer_class == nullptr) {
       stats_->TypeNeedsAccessCheck();
       return false;  // Incomplete referrer knowledge needs access check.
@@ -1467,9 +1451,12 @@
   return is_accessible;
 }
 
-bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class,
-                                                            ObjPtr<mirror::Class> resolved_class,
+bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx,
+                                                            Handle<mirror::DexCache> dex_cache,
+                                                            dex::TypeIndex type_idx,
                                                             bool* finalizable) {
+  // Get type from dex cache assuming it was populated by the verifier.
+  mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
   if (resolved_class == nullptr) {
     stats_->TypeNeedsAccessCheck();
     // Be conservative.
@@ -1477,8 +1464,10 @@
     return false;  // Unknown class needs access checks.
   }
   *finalizable = resolved_class->IsFinalizable();
+  const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(referrer_idx);
   bool is_accessible = resolved_class->IsPublic();  // Public classes are always accessible.
   if (!is_accessible) {
+    mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
     if (referrer_class == nullptr) {
       stats_->TypeNeedsAccessCheck();
       return false;  // Incomplete referrer knowledge needs access check.
@@ -1522,7 +1511,9 @@
   mirror::Class* referrer_class;
   Handle<mirror::DexCache> dex_cache(mUnit->GetDexCache());
   {
-    Handle<mirror::ClassLoader> class_loader_handle = mUnit->GetClassLoader();
+    StackHandleScope<1> hs(soa.Self());
+    Handle<mirror::ClassLoader> class_loader_handle(
+        hs.NewHandle(soa.Decode<mirror::ClassLoader>(mUnit->GetClassLoader())));
     resolved_field = ResolveField(soa, dex_cache, class_loader_handle, mUnit, field_idx, false);
     referrer_class = resolved_field != nullptr
         ? ResolveCompilingMethodsClass(soa, dex_cache, class_loader_handle, mUnit) : nullptr;
@@ -1883,7 +1874,7 @@
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->RegisterDexFile(
         dex_file,
         class_loader.Get())));
-    ObjPtr<mirror::Class> klass = (dex_cache.Get() != nullptr)
+    ObjPtr<mirror::Class> klass = (dex_cache != nullptr)
         ? class_linker->ResolveType(dex_file, dex::TypeIndex(type_idx), dex_cache, class_loader)
         : nullptr;
 
@@ -1984,7 +1975,7 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   Handle<mirror::Class> cls(hs.NewHandle<mirror::Class>(
       class_linker->FindClass(self, descriptor, class_loader)));
-  if (cls.Get() != nullptr) {
+  if (cls != nullptr) {
     // Check that the class is resolved with the current dex file. We might get
     // a boot image class, or a class in a different dex file for multidex, and
     // we should not update the status in that case.
@@ -2132,7 +2123,7 @@
     Handle<mirror::Class> klass(
         hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
     verifier::MethodVerifier::FailureKind failure_kind;
-    if (klass.Get() == nullptr) {
+    if (klass == nullptr) {
       CHECK(soa.Self()->IsExceptionPending());
       soa.Self()->ClearException();
 
@@ -2234,7 +2225,7 @@
     Handle<mirror::Class> klass(
         hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
     // Class might have failed resolution. Then don't set it to verified.
-    if (klass.Get() != nullptr) {
+    if (klass != nullptr) {
       // Only do this if the class is resolved. If even resolution fails, quickening will go very,
       // very wrong.
       if (klass->IsResolved() && !klass->IsErroneousResolved()) {
@@ -2296,7 +2287,7 @@
     Handle<mirror::Class> klass(
         hs.NewHandle(manager_->GetClassLinker()->FindClass(soa.Self(), descriptor, class_loader)));
 
-    if (klass.Get() != nullptr && !SkipClass(jclass_loader, dex_file, klass.Get())) {
+    if (klass != nullptr && !SkipClass(jclass_loader, dex_file, klass.Get())) {
       // Only try to initialize classes that were successfully verified.
       if (klass->IsVerified()) {
         // Attempt to initialize the class but bail if we either need to initialize the super-class
@@ -2546,7 +2537,7 @@
     Handle<mirror::Class> klass(
         hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
     Handle<mirror::DexCache> dex_cache;
-    if (klass.Get() == nullptr) {
+    if (klass == nullptr) {
       soa.Self()->AssertPendingException();
       soa.Self()->ClearException();
       dex_cache = hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file));
@@ -2594,18 +2585,10 @@
         continue;
       }
       previous_direct_method_idx = method_idx;
-      CompileMethod(soa.Self(),
-                    driver,
-                    it.GetMethodCodeItem(),
-                    it.GetMethodAccessFlags(),
-                    it.GetMethodInvokeType(class_def),
-                    class_def_index,
-                    method_idx,
-                    class_loader,
-                    dex_file,
-                    dex_to_dex_compilation_level,
-                    compilation_enabled,
-                    dex_cache);
+      CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
+                    it.GetMethodInvokeType(class_def), class_def_index,
+                    method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
+                    compilation_enabled, dex_cache);
       it.Next();
     }
     // Compile virtual methods
@@ -2619,17 +2602,10 @@
         continue;
       }
       previous_virtual_method_idx = method_idx;
-      CompileMethod(soa.Self(),
-                    driver, it.GetMethodCodeItem(),
-                    it.GetMethodAccessFlags(),
-                    it.GetMethodInvokeType(class_def),
-                    class_def_index,
-                    method_idx,
-                    class_loader,
-                    dex_file,
-                    dex_to_dex_compilation_level,
-                    compilation_enabled,
-                    dex_cache);
+      CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
+                    it.GetMethodInvokeType(class_def), class_def_index,
+                    method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
+                    compilation_enabled, dex_cache);
       it.Next();
     }
     DCHECK(!it.HasNext());
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 1e5c43d..5b4c751 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -187,14 +187,16 @@
       REQUIRES(!requires_constructor_barrier_lock_);
 
   // Are runtime access checks necessary in the compiled code?
-  bool CanAccessTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class,
-                                  ObjPtr<mirror::Class> resolved_class)
+  bool CanAccessTypeWithoutChecks(uint32_t referrer_idx,
+                                  Handle<mirror::DexCache> dex_cache,
+                                  dex::TypeIndex type_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Are runtime access and instantiable checks necessary in the code?
   // out_is_finalizable is set to whether the type is finalizable.
-  bool CanAccessInstantiableTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class,
-                                              ObjPtr<mirror::Class> resolved_class,
+  bool CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx,
+                                              Handle<mirror::DexCache> dex_cache,
+                                              dex::TypeIndex type_idx,
                                               bool* out_is_finalizable)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -368,6 +370,10 @@
                                       uint32_t field_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  mirror::ClassLoader* GetClassLoader(const ScopedObjectAccess& soa,
+                                      const DexCompilationUnit* mUnit)
+    REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
   void PreCompile(jobject class_loader,
                   const std::vector<const DexFile*>& dex_files,
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index e4b66eb..1e4ca16 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -101,7 +101,6 @@
 };
 
 // Disabled due to 10 second runtime on host
-// TODO: Update the test for hash-based dex cache arrays. Bug: 30627598
 TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) {
   CompileAll(nullptr);
 
diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc
index 7e8e812..47b1929 100644
--- a/compiler/driver/dex_compilation_unit.cc
+++ b/compiler/driver/dex_compilation_unit.cc
@@ -21,7 +21,7 @@
 
 namespace art {
 
-DexCompilationUnit::DexCompilationUnit(Handle<mirror::ClassLoader> class_loader,
+DexCompilationUnit::DexCompilationUnit(jobject class_loader,
                                        ClassLinker* class_linker,
                                        const DexFile& dex_file,
                                        const DexFile::CodeItem* code_item,
diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h
index 24a9a5b..854927d 100644
--- a/compiler/driver/dex_compilation_unit.h
+++ b/compiler/driver/dex_compilation_unit.h
@@ -34,7 +34,7 @@
 
 class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> {
  public:
-  DexCompilationUnit(Handle<mirror::ClassLoader> class_loader,
+  DexCompilationUnit(jobject class_loader,
                      ClassLinker* class_linker,
                      const DexFile& dex_file,
                      const DexFile::CodeItem* code_item,
@@ -44,7 +44,7 @@
                      const VerifiedMethod* verified_method,
                      Handle<mirror::DexCache> dex_cache);
 
-  Handle<mirror::ClassLoader> GetClassLoader() const {
+  jobject GetClassLoader() const {
     return class_loader_;
   }
 
@@ -113,7 +113,7 @@
   }
 
  private:
-  const Handle<mirror::ClassLoader> class_loader_;
+  const jobject class_loader_;
 
   ClassLinker* const class_linker_;
 
@@ -125,7 +125,7 @@
   const uint32_t access_flags_;
   const VerifiedMethod* verified_method_;
 
-  const Handle<mirror::DexCache> dex_cache_;
+  Handle<mirror::DexCache> dex_cache_;
 
   std::string symbol_;
 };
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
index 9669c4a..fa2d78d 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -41,7 +41,6 @@
                                                                  symbol_name, \
                                                                  build_map)); \
     EXPECT_NE(nullptr, addr); \
-    EXPECT_LT(static_cast<uintptr_t>(ART_BASE_ADDRESS), reinterpret_cast<uintptr_t>(addr)); \
     if ((expected_value) == nullptr) { \
       (expected_value) = addr; \
     }                        \
@@ -60,7 +59,7 @@
   void* dl_oatlastword = nullptr;
 
   std::unique_ptr<File> file(OS::OpenFileForReading(elf_filename.c_str()));
-  ASSERT_TRUE(file.get() != nullptr);
+  ASSERT_TRUE(file.get() != nullptr) << elf_filename;
   {
     std::string error_msg;
     std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(),
@@ -86,17 +85,22 @@
     EXPECT_ELF_FILE_ADDRESS(ef, dl_oatlastword, "oatlastword", true);
   }
   {
+    uint8_t* base = reinterpret_cast<uint8_t*>(ART_BASE_ADDRESS);
     std::string error_msg;
     std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(),
                                               false,
                                               true,
                                               /*low_4gb*/false,
-                                              &error_msg));
+                                              &error_msg,
+                                              base));
     CHECK(ef.get() != nullptr) << error_msg;
     CHECK(ef->Load(file.get(), false, /*low_4gb*/false, &error_msg)) << error_msg;
-    EXPECT_EQ(dl_oatdata, ef->FindDynamicSymbolAddress("oatdata"));
-    EXPECT_EQ(dl_oatexec, ef->FindDynamicSymbolAddress("oatexec"));
-    EXPECT_EQ(dl_oatlastword, ef->FindDynamicSymbolAddress("oatlastword"));
+    EXPECT_EQ(reinterpret_cast<uintptr_t>(dl_oatdata) + reinterpret_cast<uintptr_t>(base),
+        reinterpret_cast<uintptr_t>(ef->FindDynamicSymbolAddress("oatdata")));
+    EXPECT_EQ(reinterpret_cast<uintptr_t>(dl_oatexec) + reinterpret_cast<uintptr_t>(base),
+        reinterpret_cast<uintptr_t>(ef->FindDynamicSymbolAddress("oatexec")));
+    EXPECT_EQ(reinterpret_cast<uintptr_t>(dl_oatlastword) + reinterpret_cast<uintptr_t>(base),
+        reinterpret_cast<uintptr_t>(ef->FindDynamicSymbolAddress("oatlastword")));
   }
 }
 
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 3e9ae08..d2dd30d 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -940,11 +940,9 @@
     }
     ObjPtr<mirror::DexCache> dex_cache = self->DecodeJObject(data.weak_root)->AsDexCache();
     for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
-      mirror::TypeDexCachePair pair =
-          dex_cache->GetResolvedTypes()[i].load(std::memory_order_relaxed);
-      mirror::Class* klass = pair.object.Read();
+      Class* klass = dex_cache->GetResolvedType(dex::TypeIndex(i));
       if (klass != nullptr && !KeepClass(klass)) {
-        dex_cache->ClearResolvedType(dex::TypeIndex(pair.index));
+        dex_cache->SetResolvedType(dex::TypeIndex(i), nullptr);
       }
     }
     ArtMethod** resolved_methods = dex_cache->GetResolvedMethods();
@@ -1080,7 +1078,7 @@
   }
   Handle<ObjectArray<Object>> dex_caches(
       hs.NewHandle(ObjectArray<Object>::Alloc(self, object_array_class.Get(), dex_cache_count)));
-  CHECK(dex_caches.Get() != nullptr) << "Failed to allocate a dex cache array.";
+  CHECK(dex_caches != nullptr) << "Failed to allocate a dex cache array.";
   {
     ReaderMutexLock mu(self, *Locks::dex_lock_);
     size_t non_image_dex_caches = 0;
@@ -1924,7 +1922,8 @@
     // above comment for intern tables.
     ClassTable temp_class_table;
     temp_class_table.ReadFromMemory(class_table_memory_ptr);
-    ObjPtr<mirror::ClassLoader> class_loader = GetClassLoader();
+    CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u);
+    mirror::ClassLoader* class_loader = compile_app_image_ ? *class_loaders_.begin() : nullptr;
     CHECK_EQ(temp_class_table.NumZygoteClasses(class_loader),
              table->NumNonZygoteClasses(class_loader) + table->NumZygoteClasses(class_loader));
     UnbufferedRootVisitor visitor(&root_visitor, RootInfo(kRootUnknown));
@@ -2214,7 +2213,7 @@
     orig_dex_cache->FixupStrings(NativeCopyLocation(orig_strings, orig_dex_cache),
                                  ImageAddressVisitor(this));
   }
-  mirror::TypeDexCacheType* orig_types = orig_dex_cache->GetResolvedTypes();
+  GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes();
   if (orig_types != nullptr) {
     copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedTypesOffset(),
                                                NativeLocationInImage(orig_types),
@@ -2255,6 +2254,14 @@
     orig_dex_cache->FixupResolvedMethodTypes(NativeCopyLocation(orig_method_types, orig_dex_cache),
                                              ImageAddressVisitor(this));
   }
+  GcRoot<mirror::CallSite>* orig_call_sites = orig_dex_cache->GetResolvedCallSites();
+  if (orig_call_sites != nullptr) {
+    copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedCallSitesOffset(),
+                                               NativeLocationInImage(orig_call_sites),
+                                               PointerSize::k64);
+    orig_dex_cache->FixupResolvedCallSites(NativeCopyLocation(orig_call_sites, orig_dex_cache),
+                                           ImageAddressVisitor(this));
+  }
 
   // Remove the DexFile pointers. They will be fixed up when the runtime loads the oat file. Leaving
   // compiler pointers in here will make the output non-deterministic.
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index bdc7146..cc7df1c 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -51,13 +51,8 @@
 }  // namespace space
 }  // namespace gc
 
-namespace mirror {
-class ClassLoader;
-}  // namespace mirror
-
 class ClassLoaderVisitor;
 class ClassTable;
-class ImtConflictTable;
 
 static constexpr int kInvalidFd = -1;
 
@@ -84,11 +79,6 @@
     return true;
   }
 
-  ObjPtr<mirror::ClassLoader> GetClassLoader() {
-    CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u);
-    return compile_app_image_ ? *class_loaders_.begin() : nullptr;
-  }
-
   template <typename T>
   T* GetImageAddress(T* object) const REQUIRES_SHARED(Locks::mutator_lock_) {
     if (object == nullptr || IsInBootImage(object)) {
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 0ea1125..7c0cdbf 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1060,7 +1060,6 @@
   WriteCodeMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset,
                          size_t relative_offset) SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
     : OatDexMethodVisitor(writer, relative_offset),
-      class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr),
       out_(out),
       file_offset_(file_offset),
       soa_(Thread::Current()),
@@ -1246,7 +1245,6 @@
   }
 
  private:
-  ObjPtr<mirror::ClassLoader> class_loader_;
   OutputStream* const out_;
   const size_t file_offset_;
   const ScopedObjectAccess soa_;
@@ -1305,12 +1303,10 @@
   }
 
   mirror::Class* GetTargetType(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(writer_->HasImage());
     ObjPtr<mirror::DexCache> dex_cache = GetDexCache(patch.TargetTypeDexFile());
-    ObjPtr<mirror::Class> type =
-        ClassLinker::LookupResolvedType(patch.TargetTypeIndex(), dex_cache, class_loader_);
+    mirror::Class* type = dex_cache->GetResolvedType(patch.TargetTypeIndex());
     CHECK(type != nullptr);
-    return type.Ptr();
+    return type;
   }
 
   mirror::String* GetTargetString(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 3a4c9db..e4ad422 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -54,10 +54,7 @@
         compiler_driver_(driver),
         compilation_stats_(compiler_stats),
         block_builder_(graph, dex_file, code_item),
-        ssa_builder_(graph,
-                     dex_compilation_unit->GetClassLoader(),
-                     dex_compilation_unit->GetDexCache(),
-                     handles),
+        ssa_builder_(graph, dex_compilation_unit->GetDexCache(), handles),
         instruction_builder_(graph,
                              &block_builder_,
                              &ssa_builder_,
@@ -83,12 +80,10 @@
         code_item_(code_item),
         dex_compilation_unit_(nullptr),
         compiler_driver_(nullptr),
+        null_dex_cache_(),
         compilation_stats_(nullptr),
         block_builder_(graph, nullptr, code_item),
-        ssa_builder_(graph,
-                     handles->NewHandle<mirror::ClassLoader>(nullptr),
-                     handles->NewHandle<mirror::DexCache>(nullptr),
-                     handles),
+        ssa_builder_(graph, null_dex_cache_, handles),
         instruction_builder_(graph,
                              &block_builder_,
                              &ssa_builder_,
@@ -101,7 +96,7 @@
                              /* code_generator */ nullptr,
                              /* interpreter_metadata */ nullptr,
                              /* compiler_stats */ nullptr,
-                             handles->NewHandle<mirror::DexCache>(nullptr),
+                             null_dex_cache_,
                              handles) {}
 
   GraphAnalysisResult BuildGraph();
@@ -122,6 +117,8 @@
 
   CompilerDriver* const compiler_driver_;
 
+  ScopedNullHandle<mirror::DexCache> null_dex_cache_;
+
   OptimizingCompilerStats* compilation_stats_;
 
   HBasicBlockBuilder block_builder_;
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index d68aa51..8dd423f 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -58,6 +58,9 @@
 
 namespace art {
 
+// If true, we record the static and direct invokes in the invoke infos.
+static constexpr bool kEnableDexLayoutOptimizations = false;
+
 // Return whether a location is consistent with a type.
 static bool CheckType(Primitive::Type type, Location location) {
   if (location.IsFpuRegister()
@@ -801,7 +804,18 @@
                                        outer_environment_size,
                                        inlining_depth);
 
-  EmitEnvironment(instruction->GetEnvironment(), slow_path);
+  HEnvironment* const environment = instruction->GetEnvironment();
+  EmitEnvironment(environment, slow_path);
+  // Record invoke info, the common case for the trampoline is super and static invokes. Only
+  // record these to reduce oat file size.
+  if (kEnableDexLayoutOptimizations) {
+    if (environment != nullptr &&
+        instruction->IsInvoke() &&
+        instruction->IsInvokeStaticOrDirect()) {
+      HInvoke* const invoke = instruction->AsInvoke();
+      stack_map_stream_.AddInvoke(invoke->GetInvokeType(), invoke->GetDexMethodIndex());
+    }
+  }
   stack_map_stream_.EndStackMapEntry();
 
   HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
@@ -818,7 +832,6 @@
     EmitEnvironment(instruction->GetEnvironment(), slow_path);
     stack_map_stream_.EndStackMapEntry();
     if (kIsDebugBuild) {
-      HEnvironment* environment = instruction->GetEnvironment();
       for (size_t i = 0, environment_size = environment->Size(); i < environment_size; ++i) {
         HInstruction* in_environment = environment->GetInstructionAt(i);
         if (in_environment != nullptr) {
@@ -1419,7 +1432,7 @@
 
 QuickEntrypointEnum CodeGenerator::GetArrayAllocationEntrypoint(Handle<mirror::Class> array_klass) {
   ScopedObjectAccess soa(Thread::Current());
-  if (array_klass.Get() == nullptr) {
+  if (array_klass == nullptr) {
     // This can only happen for non-primitive arrays, as primitive arrays can always
     // be resolved.
     return kQuickAllocArrayResolved32;
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index b56ef0f..e012a42 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -69,38 +69,32 @@
     // doing some logic in the runtime to discover if a method could have been inlined.
     return;
   }
-  const ArenaVector<HBasicBlock*>& blocks = graph_->GetReversePostOrder();
+  // Keep a copy of all blocks when starting the visit.
+  ArenaVector<HBasicBlock*> blocks = graph_->GetReversePostOrder();
   DCHECK(!blocks.empty());
-  HBasicBlock* next_block = blocks[0];
-  for (size_t i = 0; i < blocks.size(); ++i) {
-    // Because we are changing the graph when inlining, we need to remember the next block.
-    // This avoids doing the inlining work again on the inlined blocks.
-    if (blocks[i] != next_block) {
-      continue;
-    }
-    HBasicBlock* block = next_block;
-    next_block = (i == blocks.size() - 1) ? nullptr : blocks[i + 1];
+  // Because we are changing the graph when inlining,
+  // we just iterate over the blocks of the outer method.
+  // This avoids doing the inlining work again on the inlined blocks.
+  for (HBasicBlock* block : blocks) {
     for (HInstruction* instruction = block->GetFirstInstruction(); instruction != nullptr;) {
       HInstruction* next = instruction->GetNext();
       HInvoke* call = instruction->AsInvoke();
       // As long as the call is not intrinsified, it is worth trying to inline.
       if (call != nullptr && call->GetIntrinsic() == Intrinsics::kNone) {
-        // We use the original invoke type to ensure the resolution of the called method
-        // works properly.
-        if (!TryInline(call)) {
-          if (kIsDebugBuild && IsCompilingWithCoreImage()) {
-            std::string callee_name =
-                outer_compilation_unit_.GetDexFile()->PrettyMethod(call->GetDexMethodIndex());
-            bool should_inline = callee_name.find("$inline$") != std::string::npos;
-            CHECK(!should_inline) << "Could not inline " << callee_name;
+        if (kIsDebugBuild && IsCompilingWithCoreImage()) {
+          // Debugging case: directives in method names control or assert on inlining.
+          std::string callee_name = outer_compilation_unit_.GetDexFile()->PrettyMethod(
+              call->GetDexMethodIndex(), /* with_signature */ false);
+          // Tests prevent inlining by having $noinline$ in their method names.
+          if (callee_name.find("$noinline$") == std::string::npos) {
+            if (!TryInline(call)) {
+              bool should_have_inlined = (callee_name.find("$inline$") != std::string::npos);
+              CHECK(!should_have_inlined) << "Could not inline " << callee_name;
+            }
           }
         } else {
-          if (kIsDebugBuild && IsCompilingWithCoreImage()) {
-            std::string callee_name =
-                outer_compilation_unit_.GetDexFile()->PrettyMethod(call->GetDexMethodIndex());
-            bool must_not_inline = callee_name.find("$noinline$") != std::string::npos;
-            CHECK(!must_not_inline) << "Should not have inlined " << callee_name;
-          }
+          // Normal case: try to inline.
+          TryInline(call);
         }
       }
       instruction = next;
@@ -198,9 +192,9 @@
 }
 
 static dex::TypeIndex FindClassIndexIn(mirror::Class* cls,
-                                       const DexCompilationUnit& compilation_unit)
+                                       const DexFile& dex_file,
+                                       Handle<mirror::DexCache> dex_cache)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  const DexFile& dex_file = *compilation_unit.GetDexFile();
   dex::TypeIndex index;
   if (cls->GetDexCache() == nullptr) {
     DCHECK(cls->IsArrayClass()) << cls->PrettyClass();
@@ -209,19 +203,22 @@
     DCHECK(cls->IsProxyClass()) << cls->PrettyClass();
     // TODO: deal with proxy classes.
   } else if (IsSameDexFile(cls->GetDexFile(), dex_file)) {
-    DCHECK_EQ(cls->GetDexCache(), compilation_unit.GetDexCache().Get());
+    DCHECK_EQ(cls->GetDexCache(), dex_cache.Get());
     index = cls->GetDexTypeIndex();
+    // Update the dex cache to ensure the class is in. The generated code will
+    // consider it is. We make it safe by updating the dex cache, as other
+    // dex files might also load the class, and there is no guarantee the dex
+    // cache of the dex file of the class will be updated.
+    if (dex_cache->GetResolvedType(index) == nullptr) {
+      dex_cache->SetResolvedType(index, cls);
+    }
   } else {
     index = cls->FindTypeIndexInOtherDexFile(dex_file);
-    // We cannot guarantee the entry will resolve to the same class,
+    // We cannot guarantee the entry in the dex cache will resolve to the same class,
     // as there may be different class loaders. So only return the index if it's
-    // the right class already resolved with the class loader.
-    if (index.IsValid()) {
-      ObjPtr<mirror::Class> resolved = ClassLinker::LookupResolvedType(
-          index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get());
-      if (resolved != cls) {
-        index = dex::TypeIndex::Invalid();
-      }
+    // the right class in the dex cache already.
+    if (index.IsValid() && dex_cache->GetResolvedType(index) != cls) {
+      index = dex::TypeIndex::Invalid();
     }
   }
 
@@ -377,7 +374,7 @@
               soa.Self(),
               class_linker->GetClassRoot(ClassLinker::kClassArrayClass),
               InlineCache::kIndividualCacheSize));
-      if (inline_cache.Get() == nullptr) {
+      if (inline_cache == nullptr) {
         // We got an OOME. Just clear the exception, and don't inline.
         DCHECK(soa.Self()->IsExceptionPending());
         soa.Self()->ClearException();
@@ -448,8 +445,9 @@
   DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface())
       << invoke_instruction->DebugName();
 
+  const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
   dex::TypeIndex class_index = FindClassIndexIn(
-      GetMonomorphicType(classes), caller_compilation_unit_);
+      GetMonomorphicType(classes), caller_dex_file, caller_compilation_unit_.GetDexCache());
   if (!class_index.IsValid()) {
     VLOG(compiler) << "Call to " << ArtMethod::PrettyMethod(resolved_method)
                    << " from inline cache is not inlined because its class is not"
@@ -492,7 +490,6 @@
   // Run type propagation to get the guard typed, and eventually propagate the
   // type of the receiver.
   ReferenceTypePropagation rtp_fixup(graph_,
-                                     outer_compilation_unit_.GetClassLoader(),
                                      outer_compilation_unit_.GetDexCache(),
                                      handles_,
                                      /* is_first_run */ false);
@@ -563,7 +560,6 @@
   bb_cursor->InsertInstructionAfter(load_class, receiver_class);
   load_class->SetLoadKind(kind);
 
-  // TODO: Extend reference type propagation to understand the guard.
   HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
   bb_cursor->InsertInstructionAfter(compare, load_class);
   if (with_deoptimization) {
@@ -587,6 +583,7 @@
 
   ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
   PointerSize pointer_size = class_linker->GetImagePointerSize();
+  const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
 
   bool all_targets_inlined = true;
   bool one_target_inlined = false;
@@ -608,7 +605,8 @@
     HInstruction* cursor = invoke_instruction->GetPrevious();
     HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
 
-    dex::TypeIndex class_index = FindClassIndexIn(handle.Get(), caller_compilation_unit_);
+    dex::TypeIndex class_index = FindClassIndexIn(
+        handle.Get(), caller_dex_file, caller_compilation_unit_.GetDexCache());
     HInstruction* return_replacement = nullptr;
     if (!class_index.IsValid() ||
         !TryBuildAndInline(invoke_instruction,
@@ -664,7 +662,6 @@
 
   // Run type propagation to get the guards typed.
   ReferenceTypePropagation rtp_fixup(graph_,
-                                     outer_compilation_unit_.GetClassLoader(),
                                      outer_compilation_unit_.GetDexCache(),
                                      handles_,
                                      /* is_first_run */ false);
@@ -846,7 +843,6 @@
   if (outermost_graph_->IsCompilingOsr()) {
     CreateDiamondPatternForPolymorphicInline(compare, return_replacement, invoke_instruction);
   } else {
-    // TODO: Extend reference type propagation to understand the guard.
     HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
         compare, invoke_instruction->GetDexPc());
     bb_cursor->InsertInstructionAfter(deoptimize, compare);
@@ -859,7 +855,6 @@
 
   // Run type propagation to get the guard typed.
   ReferenceTypePropagation rtp_fixup(graph_,
-                                     outer_compilation_unit_.GetClassLoader(),
                                      outer_compilation_unit_.GetDexCache(),
                                      handles_,
                                      /* is_first_run */ false);
@@ -928,7 +923,6 @@
     // Actual return value has a more specific type than the method's declared
     // return type. Run RTP again on the outer graph to propagate it.
     ReferenceTypePropagation(graph_,
-                             outer_compilation_unit_.GetClassLoader(),
                              outer_compilation_unit_.GetDexCache(),
                              handles_,
                              /* is_first_run */ false).Run();
@@ -1181,11 +1175,7 @@
       /* dex_pc */ 0);
   if (iget->GetType() == Primitive::kPrimNot) {
     // Use the same dex_cache that we used for field lookup as the hint_dex_cache.
-    ReferenceTypePropagation rtp(graph_,
-                                 outer_compilation_unit_.GetClassLoader(),
-                                 dex_cache,
-                                 handles_,
-                                 /* is_first_run */ false);
+    ReferenceTypePropagation rtp(graph_, dex_cache, handles_, /* is_first_run */ false);
     rtp.Visit(iget);
   }
   return iget;
@@ -1231,7 +1221,7 @@
       resolved_method->GetDeclaringClass()->GetClassLoader()));
 
   DexCompilationUnit dex_compilation_unit(
-      class_loader,
+      class_loader.ToJObject(),
       class_linker,
       callee_dex_file,
       code_item,
@@ -1348,7 +1338,6 @@
   // are more specific than the declared ones, run RTP again on the inner graph.
   if (run_rtp || ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) {
     ReferenceTypePropagation(callee_graph,
-                             outer_compilation_unit_.GetClassLoader(),
                              dex_compilation_unit.GetDexCache(),
                              handles_,
                              /* is_first_run */ false).Run();
@@ -1359,9 +1348,6 @@
       RunOptimizations(callee_graph, code_item, dex_compilation_unit);
   number_of_instructions_budget += number_of_inlined_instructions;
 
-  // TODO: We should abort only if all predecessors throw. However,
-  // HGraph::InlineInto currently does not handle an exit block with
-  // a throw predecessor.
   HBasicBlock* exit_block = callee_graph->GetExitBlock();
   if (exit_block == nullptr) {
     VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
@@ -1369,16 +1355,30 @@
     return false;
   }
 
-  bool has_throw_predecessor = false;
+  bool has_one_return = false;
   for (HBasicBlock* predecessor : exit_block->GetPredecessors()) {
     if (predecessor->GetLastInstruction()->IsThrow()) {
-      has_throw_predecessor = true;
-      break;
+      if (invoke_instruction->GetBlock()->IsTryBlock()) {
+        // TODO(ngeoffray): Support adding HTryBoundary in Hgraph::InlineInto.
+        VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
+                       << " could not be inlined because one branch always throws and"
+                       << " caller is in a try/catch block";
+        return false;
+      } else if (graph_->GetExitBlock() == nullptr) {
+        // TODO(ngeoffray): Support adding HExit in the caller graph.
+        VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
+                       << " could not be inlined because one branch always throws and"
+                       << " caller does not have an exit block";
+        return false;
+      }
+    } else {
+      has_one_return = true;
     }
   }
-  if (has_throw_predecessor) {
+
+  if (!has_one_return) {
     VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
-                   << " could not be inlined because one branch always throws";
+                   << " could not be inlined because it always throws";
     return false;
   }
 
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 3aaf2ca..3374e42 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -669,17 +669,18 @@
 
 ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) {
   ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<2> hs(soa.Self());
+  StackHandleScope<3> hs(soa.Self());
 
   ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker();
-  Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader())));
   Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass()));
   // We fetch the referenced class eagerly (that is, the class pointed by in the MethodId
   // at method_idx), as `CanAccessResolvedMethod` expects it be be in the dex cache.
   Handle<mirror::Class> methods_class(hs.NewHandle(class_linker->ResolveReferencedClassOfMethod(
       method_idx, dex_compilation_unit_->GetDexCache(), class_loader)));
 
-  if (UNLIKELY(methods_class.Get() == nullptr)) {
+  if (UNLIKELY(methods_class == nullptr)) {
     // Clean up any exception left by type resolution.
     soa.Self()->ClearException();
     return nullptr;
@@ -701,7 +702,7 @@
 
   // Check access. The class linker has a fast path for looking into the dex cache
   // and does not check the access if it hits it.
-  if (compiling_class.Get() == nullptr) {
+  if (compiling_class == nullptr) {
     if (!resolved_method->IsPublic()) {
       return nullptr;
     }
@@ -717,7 +718,7 @@
   // make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of
   // which require runtime handling.
   if (invoke_type == kSuper) {
-    if (compiling_class.Get() == nullptr) {
+    if (compiling_class == nullptr) {
       // We could not determine the method's class we need to wait until runtime.
       DCHECK(Runtime::Current()->IsAotCompiler());
       return nullptr;
@@ -953,7 +954,7 @@
   }
 
   // Consider classes we haven't resolved as potentially finalizable.
-  bool finalizable = (klass.Get() == nullptr) || klass->IsFinalizable();
+  bool finalizable = (klass == nullptr) || klass->IsFinalizable();
 
   AppendInstruction(new (arena_) HNewInstance(
       cls,
@@ -971,7 +972,7 @@
 }
 
 bool HInstructionBuilder::IsInitialized(Handle<mirror::Class> cls) const {
-  if (cls.Get() == nullptr) {
+  if (cls == nullptr) {
     return false;
   }
 
@@ -1259,7 +1260,9 @@
 static mirror::Class* GetClassFrom(CompilerDriver* driver,
                                    const DexCompilationUnit& compilation_unit) {
   ScopedObjectAccess soa(Thread::Current());
-  Handle<mirror::ClassLoader> class_loader = compilation_unit.GetClassLoader();
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(compilation_unit.GetClassLoader())));
   Handle<mirror::DexCache> dex_cache = compilation_unit.GetDexCache();
 
   return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit);
@@ -1275,9 +1278,10 @@
 
 bool HInstructionBuilder::IsOutermostCompilingClass(dex::TypeIndex type_index) const {
   ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<2> hs(soa.Self());
+  StackHandleScope<3> hs(soa.Self());
   Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
-  Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader())));
   Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass(
       soa, dex_cache, class_loader, type_index, dex_compilation_unit_)));
   Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
@@ -1288,7 +1292,7 @@
   // When this happens we cannot establish a direct relation between the current
   // class and the outer class, so we return false.
   // (Note that this is only used for optimizing invokes and field accesses)
-  return (cls.Get() != nullptr) && (outer_class.Get() == cls.Get());
+  return (cls != nullptr) && (outer_class.Get() == cls.Get());
 }
 
 void HInstructionBuilder::BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
@@ -1313,7 +1317,8 @@
   StackHandleScope<2> hs(soa.Self());
 
   ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker();
-  Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader())));
   Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass()));
 
   ArtField* resolved_field = class_linker->ResolveField(*dex_compilation_unit_->GetDexFile(),
@@ -1335,7 +1340,7 @@
   }
 
   // Check access.
-  if (compiling_class.Get() == nullptr) {
+  if (compiling_class == nullptr) {
     if (!resolved_field->IsPublic()) {
       return nullptr;
     }
@@ -1607,7 +1612,7 @@
 
 static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (cls.Get() == nullptr) {
+  if (cls == nullptr) {
     return TypeCheckKind::kUnresolvedCheck;
   } else if (cls->IsInterface()) {
     return TypeCheckKind::kInterfaceCheck;
@@ -1630,13 +1635,15 @@
 
 HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) {
   ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<2> hs(soa.Self());
   const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
-  Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader())));
   Handle<mirror::Class> klass = handles_->NewHandle(compiler_driver_->ResolveClass(
       soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_));
 
   bool needs_access_check = true;
-  if (klass.Get() != nullptr) {
+  if (klass != nullptr) {
     if (klass->IsPublic()) {
       needs_access_check = false;
     } else {
@@ -1672,7 +1679,7 @@
       type_index,
       *actual_dex_file,
       klass,
-      klass.Get() != nullptr && (klass.Get() == GetOutermostCompilingClass()),
+      klass != nullptr && (klass.Get() == GetOutermostCompilingClass()),
       dex_pc,
       needs_access_check);
 
@@ -1715,9 +1722,17 @@
   }
 }
 
-bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, bool* finalizable) const {
+bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index,
+                                           Handle<mirror::DexCache> dex_cache,
+                                           bool* finalizable) const {
   return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks(
-      LookupReferrerClass(), LookupResolvedType(type_index, *dex_compilation_unit_), finalizable);
+      dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index, finalizable);
+}
+
+bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, bool* finalizable) const {
+  ScopedObjectAccess soa(Thread::Current());
+  Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
+  return NeedsAccessCheck(type_index, dex_cache, finalizable);
 }
 
 bool HInstructionBuilder::CanDecodeQuickenedInfo() const {
@@ -2757,18 +2772,4 @@
   return true;
 }  // NOLINT(readability/fn_size)
 
-ObjPtr<mirror::Class> HInstructionBuilder::LookupResolvedType(
-    dex::TypeIndex type_index,
-    const DexCompilationUnit& compilation_unit) const {
-  return ClassLinker::LookupResolvedType(
-        type_index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get());
-}
-
-ObjPtr<mirror::Class> HInstructionBuilder::LookupReferrerClass() const {
-  // TODO: Cache the result in a Handle<mirror::Class>.
-  const DexFile::MethodId& method_id =
-      dex_compilation_unit_->GetDexFile()->GetMethodId(dex_compilation_unit_->GetDexMethodIndex());
-  return LookupResolvedType(method_id.class_idx_, *dex_compilation_unit_);
-}
-
 }  // namespace art
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index e735a0c..3bb680c 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -106,8 +106,11 @@
 
   // Returns whether the current method needs access check for the type.
   // Output parameter finalizable is set to whether the type is finalizable.
-  bool NeedsAccessCheck(dex::TypeIndex type_index, /*out*/bool* finalizable) const
+  bool NeedsAccessCheck(dex::TypeIndex type_index,
+                        Handle<mirror::DexCache> dex_cache,
+                        /*out*/bool* finalizable) const
       REQUIRES_SHARED(Locks::mutator_lock_);
+  bool NeedsAccessCheck(dex::TypeIndex type_index, /*out*/bool* finalizable) const;
 
   template<typename T>
   void Unop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
@@ -297,12 +300,6 @@
   // be found.
   ArtField* ResolveField(uint16_t field_idx, bool is_static, bool is_put);
 
-  ObjPtr<mirror::Class> LookupResolvedType(dex::TypeIndex type_index,
-                                           const DexCompilationUnit& compilation_unit) const
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  ObjPtr<mirror::Class> LookupReferrerClass() const REQUIRES_SHARED(Locks::mutator_lock_);
-
   ArenaAllocator* const arena_;
   HGraph* const graph_;
   VariableSizedHandleScope* handles_;
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 1047d3b..86e5429 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -23,7 +23,7 @@
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "intrinsics.h"
 #include "mirror/array-inl.h"
-#include "mirror/string.h"
+#include "mirror/string-inl.h"
 #include "thread.h"
 #include "utils/arm64/assembler_arm64.h"
 
@@ -1450,16 +1450,47 @@
   }
 }
 
+// The cut off for unrolling the loop in String.equals() intrinsic for const strings.
+// The normal loop plus the pre-header is 9 instructions without string compression and 12
+// instructions with string compression. We can compare up to 8 bytes in 4 instructions
+// (LDR+LDR+CMP+BNE) and up to 16 bytes in 5 instructions (LDP+LDP+CMP+CCMP+BNE). Allow up
+// to 10 instructions for the unrolled loop.
+constexpr size_t kShortConstStringEqualsCutoffInBytes = 32;
+
+static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
+  if (candidate->IsLoadString()) {
+    HLoadString* load_string = candidate->AsLoadString();
+    const DexFile& dex_file = load_string->GetDexFile();
+    return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
+  }
+  return nullptr;
+}
+
 void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
   LocationSummary* locations = new (arena_) LocationSummary(invoke,
                                                             LocationSummary::kNoCall,
                                                             kIntrinsified);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
-  // Temporary registers to store lengths of strings and for calculations.
-  locations->AddTemp(Location::RequiresRegister());
-  locations->AddTemp(Location::RequiresRegister());
 
+  // For the generic implementation and for long const strings we need a temporary.
+  // We do not need it for short const strings, up to 8 bytes, see code generation below.
+  uint32_t const_string_length = 0u;
+  const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
+  if (const_string == nullptr) {
+    const_string = GetConstString(invoke->InputAt(1), &const_string_length);
+  }
+  bool is_compressed =
+      mirror::kUseStringCompression &&
+      const_string != nullptr &&
+      mirror::String::DexFileStringAllASCII(const_string, const_string_length);
+  if (const_string == nullptr || const_string_length > (is_compressed ? 8u : 4u)) {
+    locations->AddTemp(Location::RequiresRegister());
+  }
+
+  // TODO: If the String.equals() is used only for an immediately following HIf, we can
+  // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
+  // Then we shall need an extra temporary register instead of the output register.
   locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
 }
 
@@ -1473,8 +1504,7 @@
 
   UseScratchRegisterScope scratch_scope(masm);
   Register temp = scratch_scope.AcquireW();
-  Register temp1 = WRegisterFrom(locations->GetTemp(0));
-  Register temp2 = WRegisterFrom(locations->GetTemp(1));
+  Register temp1 = scratch_scope.AcquireW();
 
   vixl::aarch64::Label loop;
   vixl::aarch64::Label end;
@@ -1510,47 +1540,99 @@
     __ B(&return_false, ne);
   }
 
-  // Load `count` fields of this and argument strings.
-  __ Ldr(temp, MemOperand(str.X(), count_offset));
-  __ Ldr(temp1, MemOperand(arg.X(), count_offset));
-  // Check if `count` fields are equal, return false if they're not.
-  // Also compares the compression style, if differs return false.
-  __ Cmp(temp, temp1);
-  __ B(&return_false, ne);
-  // Return true if both strings are empty. Even with string compression `count == 0` means empty.
-  static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
-                "Expecting 0=compressed, 1=uncompressed");
-  __ Cbz(temp, &return_true);
+  // Check if one of the inputs is a const string. Do not special-case both strings
+  // being const, such cases should be handled by constant folding if needed.
+  uint32_t const_string_length = 0u;
+  const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
+  if (const_string == nullptr) {
+    const_string = GetConstString(invoke->InputAt(1), &const_string_length);
+    if (const_string != nullptr) {
+      std::swap(str, arg);  // Make sure the const string is in `str`.
+    }
+  }
+  bool is_compressed =
+      mirror::kUseStringCompression &&
+      const_string != nullptr &&
+      mirror::String::DexFileStringAllASCII(const_string, const_string_length);
+
+  if (const_string != nullptr) {
+    // Load `count` field of the argument string and check if it matches the const string.
+    // Also compares the compression style, if differs return false.
+    __ Ldr(temp, MemOperand(arg.X(), count_offset));
+    __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
+    __ B(&return_false, ne);
+  } else {
+    // Load `count` fields of this and argument strings.
+    __ Ldr(temp, MemOperand(str.X(), count_offset));
+    __ Ldr(temp1, MemOperand(arg.X(), count_offset));
+    // Check if `count` fields are equal, return false if they're not.
+    // Also compares the compression style, if differs return false.
+    __ Cmp(temp, temp1);
+    __ B(&return_false, ne);
+  }
 
   // Assertions that must hold in order to compare strings 8 bytes at a time.
   DCHECK_ALIGNED(value_offset, 8);
   static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
 
-  if (mirror::kUseStringCompression) {
-    // For string compression, calculate the number of bytes to compare (not chars).
-    // This could in theory exceed INT32_MAX, so treat temp as unsigned.
-    __ Lsr(temp, temp, 1u);             // Extract length.
-    __ And(temp1, temp1, Operand(1));   // Extract compression flag.
-    __ Lsl(temp, temp, temp1);          // Calculate number of bytes to compare.
+  if (const_string != nullptr &&
+      const_string_length < (is_compressed ? kShortConstStringEqualsCutoffInBytes
+                                           : kShortConstStringEqualsCutoffInBytes / 2u)) {
+    // Load and compare the contents. Though we know the contents of the short const string
+    // at compile time, materializing constants may be more code than loading from memory.
+    int32_t offset = value_offset;
+    size_t remaining_bytes =
+        RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u);
+    temp = temp.X();
+    temp1 = temp1.X();
+    while (remaining_bytes > 8u) {
+      Register temp2 = XRegisterFrom(locations->GetTemp(0));
+      __ Ldp(temp, temp1, MemOperand(str.X(), offset));
+      __ Ldp(temp2, out, MemOperand(arg.X(), offset));
+      __ Cmp(temp, temp2);
+      __ Ccmp(temp1, out, NoFlag, eq);
+      __ B(&return_false, ne);
+      offset += 2u * sizeof(uint64_t);
+      remaining_bytes -= 2u * sizeof(uint64_t);
+    }
+    if (remaining_bytes != 0u) {
+      __ Ldr(temp, MemOperand(str.X(), offset));
+      __ Ldr(temp1, MemOperand(arg.X(), offset));
+      __ Cmp(temp, temp1);
+      __ B(&return_false, ne);
+    }
+  } else {
+    // Return true if both strings are empty. Even with string compression `count == 0` means empty.
+    static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
+                  "Expecting 0=compressed, 1=uncompressed");
+    __ Cbz(temp, &return_true);
+
+    if (mirror::kUseStringCompression) {
+      // For string compression, calculate the number of bytes to compare (not chars).
+      // This could in theory exceed INT32_MAX, so treat temp as unsigned.
+      __ And(temp1, temp, Operand(1));    // Extract compression flag.
+      __ Lsr(temp, temp, 1u);             // Extract length.
+      __ Lsl(temp, temp, temp1);          // Calculate number of bytes to compare.
+    }
+
+    // Store offset of string value in preparation for comparison loop
+    __ Mov(temp1, value_offset);
+
+    temp1 = temp1.X();
+    Register temp2 = XRegisterFrom(locations->GetTemp(0));
+    // Loop to compare strings 8 bytes at a time starting at the front of the string.
+    // Ok to do this because strings are zero-padded to kObjectAlignment.
+    __ Bind(&loop);
+    __ Ldr(out, MemOperand(str.X(), temp1));
+    __ Ldr(temp2, MemOperand(arg.X(), temp1));
+    __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
+    __ Cmp(out, temp2);
+    __ B(&return_false, ne);
+    // With string compression, we have compared 8 bytes, otherwise 4 chars.
+    __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags);
+    __ B(&loop, hi);
   }
 
-  // Store offset of string value in preparation for comparison loop
-  __ Mov(temp1, value_offset);
-
-  temp1 = temp1.X();
-  temp2 = temp2.X();
-  // Loop to compare strings 8 bytes at a time starting at the front of the string.
-  // Ok to do this because strings are zero-padded to kObjectAlignment.
-  __ Bind(&loop);
-  __ Ldr(out, MemOperand(str.X(), temp1));
-  __ Ldr(temp2, MemOperand(arg.X(), temp1));
-  __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
-  __ Cmp(out, temp2);
-  __ B(&return_false, ne);
-  // With string compression, we have compared 8 bytes, otherwise 4 chars.
-  __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags);
-  __ B(&loop, hi);
-
   // Return true and exit the function.
   // If loop does not result in returning false, we return true.
   __ Bind(&return_true);
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index abbb91a..71a26eb 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -2038,6 +2038,8 @@
 
   HInstruction* return_value = nullptr;
   if (GetBlocks().size() == 3) {
+    // Inliner already made sure we don't inline methods that always throw.
+    DCHECK(!GetBlocks()[1]->GetLastInstruction()->IsThrow());
     // Simple case of an entry block, a body block, and an exit block.
     // Put the body block's instruction into `invoke`'s block.
     HBasicBlock* body = GetBlocks()[1];
@@ -2119,33 +2121,60 @@
     UpdateLoopAndTryInformationOfNewBlock(to, at, /* replace_if_back_edge */ true);
 
     // Update all predecessors of the exit block (now the `to` block)
-    // to not `HReturn` but `HGoto` instead.
-    bool returns_void = to->GetPredecessors()[0]->GetLastInstruction()->IsReturnVoid();
-    if (to->GetPredecessors().size() == 1) {
-      HBasicBlock* predecessor = to->GetPredecessors()[0];
+    // to not `HReturn` but `HGoto` instead. Special case throwing blocks
+    // to now get the outer graph exit block as successor. Note that the inliner
+    // currently doesn't support inlining methods with try/catch.
+    HPhi* return_value_phi = nullptr;
+    bool rerun_dominance = false;
+    bool rerun_loop_analysis = false;
+    for (size_t pred = 0; pred < to->GetPredecessors().size(); ++pred) {
+      HBasicBlock* predecessor = to->GetPredecessors()[pred];
       HInstruction* last = predecessor->GetLastInstruction();
-      if (!returns_void) {
-        return_value = last->InputAt(0);
-      }
-      predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc()));
-      predecessor->RemoveInstruction(last);
-    } else {
-      if (!returns_void) {
-        // There will be multiple returns.
-        return_value = new (allocator) HPhi(
-            allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType()), to->GetDexPc());
-        to->AddPhi(return_value->AsPhi());
-      }
-      for (HBasicBlock* predecessor : to->GetPredecessors()) {
-        HInstruction* last = predecessor->GetLastInstruction();
-        if (!returns_void) {
+      if (last->IsThrow()) {
+        DCHECK(!at->IsTryBlock());
+        predecessor->ReplaceSuccessor(to, outer_graph->GetExitBlock());
+        --pred;
+        // We need to re-run dominance information, as the exit block now has
+        // a new dominator.
+        rerun_dominance = true;
+        if (predecessor->GetLoopInformation() != nullptr) {
+          // The exit block and blocks post dominated by the exit block do not belong
+          // to any loop. Because we do not compute the post dominators, we need to re-run
+          // loop analysis to get the loop information correct.
+          rerun_loop_analysis = true;
+        }
+      } else {
+        if (last->IsReturnVoid()) {
+          DCHECK(return_value == nullptr);
+          DCHECK(return_value_phi == nullptr);
+        } else {
           DCHECK(last->IsReturn());
-          return_value->AsPhi()->AddInput(last->InputAt(0));
+          if (return_value_phi != nullptr) {
+            return_value_phi->AddInput(last->InputAt(0));
+          } else if (return_value == nullptr) {
+            return_value = last->InputAt(0);
+          } else {
+            // There will be multiple returns.
+            return_value_phi = new (allocator) HPhi(
+                allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType()), to->GetDexPc());
+            to->AddPhi(return_value_phi);
+            return_value_phi->AddInput(return_value);
+            return_value_phi->AddInput(last->InputAt(0));
+            return_value = return_value_phi;
+          }
         }
         predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc()));
         predecessor->RemoveInstruction(last);
       }
     }
+    if (rerun_loop_analysis) {
+      outer_graph->ClearLoopInformation();
+      outer_graph->ClearDominanceInformation();
+      outer_graph->BuildDominatorTree();
+    } else if (rerun_dominance) {
+      outer_graph->ClearDominanceInformation();
+      outer_graph->ComputeDominanceInformation();
+    }
   }
 
   // Walk over the entry block and:
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 0375c66..8638e34 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -306,7 +306,7 @@
                           InvokeType invoke_type,
                           uint16_t class_def_idx,
                           uint32_t method_idx,
-                          Handle<mirror::ClassLoader> class_loader,
+                          jobject class_loader,
                           const DexFile& dex_file,
                           Handle<mirror::DexCache> dex_cache) const OVERRIDE;
 
@@ -375,7 +375,7 @@
                             InvokeType invoke_type,
                             uint16_t class_def_idx,
                             uint32_t method_idx,
-                            Handle<mirror::ClassLoader> class_loader,
+                            jobject class_loader,
                             const DexFile& dex_file,
                             Handle<mirror::DexCache> dex_cache,
                             ArtMethod* method,
@@ -875,7 +875,7 @@
                                               InvokeType invoke_type,
                                               uint16_t class_def_idx,
                                               uint32_t method_idx,
-                                              Handle<mirror::ClassLoader> class_loader,
+                                              jobject class_loader,
                                               const DexFile& dex_file,
                                               Handle<mirror::DexCache> dex_cache,
                                               ArtMethod* method,
@@ -946,8 +946,11 @@
   const uint8_t* interpreter_metadata = nullptr;
   if (method == nullptr) {
     ScopedObjectAccess soa(Thread::Current());
+    StackHandleScope<1> hs(soa.Self());
+    Handle<mirror::ClassLoader> loader(hs.NewHandle(
+        soa.Decode<mirror::ClassLoader>(class_loader)));
     method = compiler_driver->ResolveMethod(
-        soa, dex_cache, class_loader, &dex_compilation_unit, method_idx, invoke_type);
+        soa, dex_cache, loader, &dex_compilation_unit, method_idx, invoke_type);
   }
   // For AOT compilation, we may not get a method, for example if its class is erroneous.
   // JIT should always have a method.
@@ -956,6 +959,16 @@
     graph->SetArtMethod(method);
     ScopedObjectAccess soa(Thread::Current());
     interpreter_metadata = method->GetQuickenedInfo(class_linker->GetImagePointerSize());
+    dex::TypeIndex type_index = method->GetDeclaringClass()->GetDexTypeIndex();
+
+    // Update the dex cache if the type is not in it yet. Note that under AOT,
+    // the verifier must have set it, but under JIT, there's no guarantee, as we
+    // don't necessarily run the verifier.
+    // The compiler and the compiler driver assume the compiling class is
+    // in the dex cache.
+    if (dex_cache->GetResolvedType(type_index) == nullptr) {
+      dex_cache->SetResolvedType(type_index, method->GetDeclaringClass());
+    }
   }
 
   std::unique_ptr<CodeGenerator> codegen(
@@ -1036,7 +1049,7 @@
                                             InvokeType invoke_type,
                                             uint16_t class_def_idx,
                                             uint32_t method_idx,
-                                            Handle<mirror::ClassLoader> jclass_loader,
+                                            jobject jclass_loader,
                                             const DexFile& dex_file,
                                             Handle<mirror::DexCache> dex_cache) const {
   CompilerDriver* compiler_driver = GetCompilerDriver();
@@ -1150,6 +1163,7 @@
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
   DCHECK(method->IsCompilable());
 
+  jobject jclass_loader = class_loader.ToJObject();
   const DexFile* dex_file = method->GetDexFile();
   const uint16_t class_def_idx = method->GetClassDefIndex();
   const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset());
@@ -1173,7 +1187,7 @@
                    invoke_type,
                    class_def_idx,
                    method_idx,
-                   class_loader,
+                   jclass_loader,
                    *dex_file,
                    dex_cache,
                    method,
@@ -1200,7 +1214,7 @@
   Handle<mirror::ObjectArray<mirror::Object>> roots(
       hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc(
           self, class_linker->GetClassRoot(ClassLinker::kObjectArrayClass), number_of_roots)));
-  if (roots.Get() == nullptr) {
+  if (roots == nullptr) {
     // Out of memory, just clear the exception to avoid any Java exception uncaught problems.
     DCHECK(self->IsExceptionPending());
     self->ClearException();
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 6e332ca..c55fccc 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -65,13 +65,11 @@
 class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor {
  public:
   RTPVisitor(HGraph* graph,
-             Handle<mirror::ClassLoader> class_loader,
              Handle<mirror::DexCache> hint_dex_cache,
              HandleCache* handle_cache,
              ArenaVector<HInstruction*>* worklist,
              bool is_first_run)
     : HGraphDelegateVisitor(graph),
-      class_loader_(class_loader),
       hint_dex_cache_(hint_dex_cache),
       handle_cache_(handle_cache),
       worklist_(worklist),
@@ -103,7 +101,6 @@
                                bool is_exact);
 
  private:
-  Handle<mirror::ClassLoader> class_loader_;
   Handle<mirror::DexCache> hint_dex_cache_;
   HandleCache* handle_cache_;
   ArenaVector<HInstruction*>* worklist_;
@@ -111,13 +108,11 @@
 };
 
 ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph,
-                                                   Handle<mirror::ClassLoader> class_loader,
                                                    Handle<mirror::DexCache> hint_dex_cache,
                                                    VariableSizedHandleScope* handles,
                                                    bool is_first_run,
                                                    const char* name)
     : HOptimization(graph, name),
-      class_loader_(class_loader),
       hint_dex_cache_(hint_dex_cache),
       handle_cache_(handles),
       worklist_(graph->GetArena()->Adapter(kArenaAllocReferenceTypePropagation)),
@@ -152,12 +147,7 @@
 }
 
 void ReferenceTypePropagation::Visit(HInstruction* instruction) {
-  RTPVisitor visitor(graph_,
-                     class_loader_,
-                     hint_dex_cache_,
-                     &handle_cache_,
-                     &worklist_,
-                     is_first_run_);
+  RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_);
   instruction->Accept(&visitor);
 }
 
@@ -331,12 +321,7 @@
 }
 
 void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
-  RTPVisitor visitor(graph_,
-                     class_loader_,
-                     hint_dex_cache_,
-                     &handle_cache_,
-                     &worklist_,
-                     is_first_run_);
+  RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_);
   // Handle Phis first as there might be instructions in the same block who depend on them.
   for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
     VisitPhi(it.Current()->AsPhi());
@@ -557,9 +542,8 @@
 
   ScopedObjectAccess soa(Thread::Current());
   ObjPtr<mirror::DexCache> dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_);
-  ObjPtr<mirror::Class> klass =
-      ClassLinker::LookupResolvedType(type_idx, dex_cache, class_loader_.Get());
-  SetClassAsTypeInfo(instr, klass, is_exact);
+  // Get type from dex cache assuming it was populated by the verifier.
+  SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact);
 }
 
 void ReferenceTypePropagation::RTPVisitor::VisitNewInstance(HNewInstance* instr) {
@@ -572,13 +556,25 @@
   SetClassAsTypeInfo(instr, instr->GetLoadClass()->GetClass().Get(), /* is_exact */ true);
 }
 
+static mirror::Class* GetClassFromDexCache(Thread* self,
+                                           const DexFile& dex_file,
+                                           dex::TypeIndex type_idx,
+                                           Handle<mirror::DexCache> hint_dex_cache)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ObjPtr<mirror::DexCache> dex_cache = FindDexCacheWithHint(self, dex_file, hint_dex_cache);
+  // Get type from dex cache assuming it was populated by the verifier.
+  return dex_cache->GetResolvedType(type_idx);
+}
+
 void ReferenceTypePropagation::RTPVisitor::VisitParameterValue(HParameterValue* instr) {
   // We check if the existing type is valid: the inliner may have set it.
   if (instr->GetType() == Primitive::kPrimNot && !instr->GetReferenceTypeInfo().IsValid()) {
-    UpdateReferenceTypeInfo(instr,
-                            instr->GetTypeIndex(),
-                            instr->GetDexFile(),
-                            /* is_exact */ false);
+    ScopedObjectAccess soa(Thread::Current());
+    mirror::Class* resolved_class = GetClassFromDexCache(soa.Self(),
+                                                         instr->GetDexFile(),
+                                                         instr->GetTypeIndex(),
+                                                         hint_dex_cache_);
+    SetClassAsTypeInfo(instr, resolved_class, /* is_exact */ false);
   }
 }
 
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 215e967..4663471 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -33,7 +33,6 @@
 class ReferenceTypePropagation : public HOptimization {
  public:
   ReferenceTypePropagation(HGraph* graph,
-                           Handle<mirror::ClassLoader> class_loader,
                            Handle<mirror::DexCache> hint_dex_cache,
                            VariableSizedHandleScope* handles,
                            bool is_first_run,
@@ -106,8 +105,6 @@
 
   void ValidateTypes();
 
-  Handle<mirror::ClassLoader> class_loader_;
-
   // Note: hint_dex_cache_ is usually, but not necessarily, the dex cache associated with
   // graph_->GetDexFile(). Since we may look up also in other dex files, it's used only
   // as a hint, to reduce the number of calls to the costly ClassLinker::FindDexCache().
diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc
index 84a4bab..b061c87 100644
--- a/compiler/optimizing/reference_type_propagation_test.cc
+++ b/compiler/optimizing/reference_type_propagation_test.cc
@@ -38,7 +38,6 @@
   void SetupPropagation(VariableSizedHandleScope* handles) {
     graph_->InitializeInexactObjectRTI(handles);
     propagation_ = new (&allocator_) ReferenceTypePropagation(graph_,
-                                                              Handle<mirror::ClassLoader>(),
                                                               Handle<mirror::DexCache>(),
                                                               handles,
                                                               true,
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index f07f02a..be40092 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -163,7 +163,7 @@
       if (!compiler_driver->GetSupportBootImageFixup()) {
         // compiler_driver_test. Do not sharpen.
         desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
-      } else if ((klass.Get() != nullptr) && compiler_driver->IsImageClass(
+      } else if ((klass != nullptr) && compiler_driver->IsImageClass(
           dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) {
         is_in_boot_image = true;
         desired_load_kind = codegen->GetCompilerOptions().GetCompilePic()
@@ -175,7 +175,7 @@
         desired_load_kind = HLoadClass::LoadKind::kBssEntry;
       }
     } else {
-      is_in_boot_image = (klass.Get() != nullptr) &&
+      is_in_boot_image = (klass != nullptr) &&
           runtime->GetHeap()->ObjectIsInBootImageSpace(klass.Get());
       if (runtime->UseJitCompilation()) {
         // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
@@ -183,7 +183,7 @@
         if (is_in_boot_image) {
           // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787
           desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
-        } else if (klass.Get() != nullptr) {
+        } else if (klass != nullptr) {
           desired_load_kind = HLoadClass::LoadKind::kJitTableAddress;
         } else {
           // Class not loaded yet. This happens when the dex code requesting
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 50ab11b..487e4dd 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -499,11 +499,7 @@
 
   // 4) Compute type of reference type instructions. The pass assumes that
   // NullConstant has been fixed up.
-  ReferenceTypePropagation(graph_,
-                           class_loader_,
-                           dex_cache_,
-                           handles_,
-                           /* is_first_run */ true).Run();
+  ReferenceTypePropagation(graph_, dex_cache_, handles_, /* is_first_run */ true).Run();
 
   // 5) HInstructionBuilder duplicated ArrayGet instructions with ambiguous type
   // (int/float or long/double) and marked ArraySets with ambiguous input type.
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index 978f113..45dac54 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -48,11 +48,9 @@
 class SsaBuilder : public ValueObject {
  public:
   SsaBuilder(HGraph* graph,
-             Handle<mirror::ClassLoader> class_loader,
              Handle<mirror::DexCache> dex_cache,
              VariableSizedHandleScope* handles)
       : graph_(graph),
-        class_loader_(class_loader),
         dex_cache_(dex_cache),
         handles_(handles),
         agets_fixed_(false),
@@ -117,7 +115,6 @@
   void RemoveRedundantUninitializedStrings();
 
   HGraph* graph_;
-  Handle<mirror::ClassLoader> class_loader_;
   Handle<mirror::DexCache> dex_cache_;
   VariableSizedHandleScope* const handles_;
 
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index f8e01b7..eeae96e 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -38,20 +38,15 @@
   current_entry_.native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_);
   current_entry_.register_mask = register_mask;
   current_entry_.sp_mask = sp_mask;
-  current_entry_.num_dex_registers = num_dex_registers;
   current_entry_.inlining_depth = inlining_depth;
-  current_entry_.dex_register_locations_start_index = dex_register_locations_.size();
   current_entry_.inline_infos_start_index = inline_infos_.size();
-  current_entry_.dex_register_map_hash = 0;
-  current_entry_.same_dex_register_map_as_ = kNoSameDexMapFound;
   current_entry_.stack_mask_index = 0;
-  if (num_dex_registers != 0) {
-    current_entry_.live_dex_registers_mask =
-        ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream);
-  } else {
-    current_entry_.live_dex_registers_mask = nullptr;
-  }
-
+  current_entry_.dex_method_index = DexFile::kDexNoIndex;
+  current_entry_.dex_register_entry.num_dex_registers = num_dex_registers;
+  current_entry_.dex_register_entry.locations_start_index = dex_register_locations_.size();
+  current_entry_.dex_register_entry.live_dex_registers_mask = (num_dex_registers != 0)
+      ? ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream)
+      : nullptr;
   if (sp_mask != nullptr) {
     stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet());
   }
@@ -65,7 +60,7 @@
 }
 
 void StackMapStream::EndStackMapEntry() {
-  current_entry_.same_dex_register_map_as_ = FindEntryWithTheSameDexMap();
+  current_entry_.dex_register_map_index = AddDexRegisterMapEntry(current_entry_.dex_register_entry);
   stack_maps_.push_back(current_entry_);
   current_entry_ = StackMapEntry();
 }
@@ -91,23 +86,24 @@
       dex_register_locations_.push_back(index);
       location_catalog_entries_indices_.Insert(std::make_pair(location, index));
     }
-
-    if (in_inline_frame_) {
-      // TODO: Support sharing DexRegisterMap across InlineInfo.
-      DCHECK_LT(current_dex_register_, current_inline_info_.num_dex_registers);
-      current_inline_info_.live_dex_registers_mask->SetBit(current_dex_register_);
-    } else {
-      DCHECK_LT(current_dex_register_, current_entry_.num_dex_registers);
-      current_entry_.live_dex_registers_mask->SetBit(current_dex_register_);
-      current_entry_.dex_register_map_hash += (1 <<
-          (current_dex_register_ % (sizeof(current_entry_.dex_register_map_hash) * kBitsPerByte)));
-      current_entry_.dex_register_map_hash += static_cast<uint32_t>(value);
-      current_entry_.dex_register_map_hash += static_cast<uint32_t>(kind);
-    }
+    DexRegisterMapEntry* const entry = in_inline_frame_
+        ? &current_inline_info_.dex_register_entry
+        : &current_entry_.dex_register_entry;
+    DCHECK_LT(current_dex_register_, entry->num_dex_registers);
+    entry->live_dex_registers_mask->SetBit(current_dex_register_);
+    entry->hash += (1 <<
+        (current_dex_register_ % (sizeof(DexRegisterMapEntry::hash) * kBitsPerByte)));
+    entry->hash += static_cast<uint32_t>(value);
+    entry->hash += static_cast<uint32_t>(kind);
   }
   current_dex_register_++;
 }
 
+void StackMapStream::AddInvoke(InvokeType invoke_type, uint32_t dex_method_index) {
+  current_entry_.invoke_type = invoke_type;
+  current_entry_.dex_method_index = dex_method_index;
+}
+
 void StackMapStream::BeginInlineInfoEntry(ArtMethod* method,
                                           uint32_t dex_pc,
                                           uint32_t num_dex_registers,
@@ -124,20 +120,19 @@
     current_inline_info_.method_index = method->GetDexMethodIndexUnchecked();
   }
   current_inline_info_.dex_pc = dex_pc;
-  current_inline_info_.num_dex_registers = num_dex_registers;
-  current_inline_info_.dex_register_locations_start_index = dex_register_locations_.size();
-  if (num_dex_registers != 0) {
-    current_inline_info_.live_dex_registers_mask =
-        ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream);
-  } else {
-    current_inline_info_.live_dex_registers_mask = nullptr;
-  }
+  current_inline_info_.dex_register_entry.num_dex_registers = num_dex_registers;
+  current_inline_info_.dex_register_entry.locations_start_index = dex_register_locations_.size();
+  current_inline_info_.dex_register_entry.live_dex_registers_mask = (num_dex_registers != 0)
+      ? ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream)
+      : nullptr;
   current_dex_register_ = 0;
 }
 
 void StackMapStream::EndInlineInfoEntry() {
+  current_inline_info_.dex_register_map_index =
+      AddDexRegisterMapEntry(current_inline_info_.dex_register_entry);
   DCHECK(in_inline_frame_);
-  DCHECK_EQ(current_dex_register_, current_inline_info_.num_dex_registers)
+  DCHECK_EQ(current_dex_register_, current_inline_info_.dex_register_entry.num_dex_registers)
       << "Inline information contains less registers than expected";
   in_inline_frame_ = false;
   inline_infos_.push_back(current_inline_info_);
@@ -176,6 +171,7 @@
       encoding.inline_info.num_entries,
       encoding.register_mask.num_entries,
       encoding.stack_mask.num_entries);
+  ComputeInvokeInfoEncoding(&encoding);
   DCHECK_EQ(code_info_encoding_.size(), 0u);
   encoding.Compress(&code_info_encoding_);
   encoding.ComputeTableOffsets();
@@ -193,8 +189,7 @@
   return size;
 }
 
-size_t StackMapStream::ComputeDexRegisterMapSize(uint32_t num_dex_registers,
-                                                 const BitVector* live_dex_registers_mask) const {
+size_t StackMapStream::DexRegisterMapEntry::ComputeSize(size_t catalog_size) const {
   // For num_dex_registers == 0u live_dex_registers_mask may be null.
   if (num_dex_registers == 0u) {
     return 0u;  // No register map will be emitted.
@@ -208,8 +203,7 @@
   // Compute the size of the set of live Dex register entries.
   size_t number_of_live_dex_registers = live_dex_registers_mask->NumSetBits();
   size_t map_entries_size_in_bits =
-      DexRegisterMap::SingleEntrySizeInBits(location_catalog_entries_.size())
-      * number_of_live_dex_registers;
+      DexRegisterMap::SingleEntrySizeInBits(catalog_size) * number_of_live_dex_registers;
   size_t map_entries_size_in_bytes =
       RoundUp(map_entries_size_in_bits, kBitsPerByte) / kBitsPerByte;
   size += map_entries_size_in_bytes;
@@ -218,22 +212,30 @@
 
 size_t StackMapStream::ComputeDexRegisterMapsSize() const {
   size_t size = 0;
-  size_t inline_info_index = 0;
-  for (const StackMapEntry& entry : stack_maps_) {
-    if (entry.same_dex_register_map_as_ == kNoSameDexMapFound) {
-      size += ComputeDexRegisterMapSize(entry.num_dex_registers, entry.live_dex_registers_mask);
-    } else {
-      // Entries with the same dex map will have the same offset.
-    }
-    for (size_t j = 0; j < entry.inlining_depth; ++j) {
-      InlineInfoEntry inline_entry = inline_infos_[inline_info_index++];
-      size += ComputeDexRegisterMapSize(inline_entry.num_dex_registers,
-                                        inline_entry.live_dex_registers_mask);
-    }
+  for (const DexRegisterMapEntry& entry : dex_register_entries_) {
+    size += entry.ComputeSize(location_catalog_entries_.size());
   }
   return size;
 }
 
+void StackMapStream::ComputeInvokeInfoEncoding(CodeInfoEncoding* encoding) {
+  DCHECK(encoding != nullptr);
+  uint32_t native_pc_max = 0;
+  uint16_t method_index_max = 0;
+  size_t invoke_infos_count = 0;
+  size_t invoke_type_max = 0;
+  for (const StackMapEntry& entry : stack_maps_) {
+    if (entry.dex_method_index != DexFile::kDexNoIndex) {
+      native_pc_max = std::max(native_pc_max, entry.native_pc_code_offset.CompressedValue());
+      method_index_max = std::max(method_index_max, static_cast<uint16_t>(entry.dex_method_index));
+      invoke_type_max = std::max(invoke_type_max, static_cast<size_t>(entry.invoke_type));
+      ++invoke_infos_count;
+    }
+  }
+  encoding->invoke_info.num_entries = invoke_infos_count;
+  encoding->invoke_info.encoding.SetFromSizes(native_pc_max, invoke_type_max, method_index_max);
+}
+
 void StackMapStream::ComputeInlineInfoEncoding(InlineInfoEncoding* encoding,
                                                size_t dex_register_maps_bytes) {
   uint32_t method_index_max = 0;
@@ -264,6 +266,30 @@
   encoding->SetFromSizes(method_index_max, dex_pc_max, extra_data_max, dex_register_maps_bytes);
 }
 
+size_t StackMapStream::MaybeCopyDexRegisterMap(DexRegisterMapEntry& entry,
+                                               size_t* current_offset,
+                                               MemoryRegion dex_register_locations_region) {
+  DCHECK(current_offset != nullptr);
+  if ((entry.num_dex_registers == 0) || (entry.live_dex_registers_mask->NumSetBits() == 0)) {
+    // No dex register map needed.
+    return StackMap::kNoDexRegisterMap;
+  }
+  if (entry.offset == DexRegisterMapEntry::kOffsetUnassigned) {
+    // Not already copied, need to copy and and assign an offset.
+    entry.offset = *current_offset;
+    const size_t entry_size = entry.ComputeSize(location_catalog_entries_.size());
+    DexRegisterMap dex_register_map(
+        dex_register_locations_region.Subregion(entry.offset, entry_size));
+    *current_offset += entry_size;
+    // Fill in the map since it was just added.
+    FillInDexRegisterMap(dex_register_map,
+                         entry.num_dex_registers,
+                         *entry.live_dex_registers_mask,
+                         entry.locations_start_index);
+  }
+  return entry.offset;
+}
+
 void StackMapStream::FillIn(MemoryRegion region) {
   DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry";
   DCHECK_NE(0u, needed_size_) << "PrepareForFillIn not called before FillIn";
@@ -302,6 +328,7 @@
   ArenaBitVector empty_bitmask(allocator_, 0, /* expandable */ false, kArenaAllocStackMapStream);
   uintptr_t next_dex_register_map_offset = 0;
   uintptr_t next_inline_info_index = 0;
+  size_t invoke_info_idx = 0;
   for (size_t i = 0, e = stack_maps_.size(); i < e; ++i) {
     StackMap stack_map = code_info.GetStackMapAt(i, encoding);
     StackMapEntry entry = stack_maps_[i];
@@ -311,34 +338,17 @@
     stack_map.SetRegisterMaskIndex(encoding.stack_map.encoding, entry.register_mask_index);
     stack_map.SetStackMaskIndex(encoding.stack_map.encoding, entry.stack_mask_index);
 
-    if (entry.num_dex_registers == 0 || (entry.live_dex_registers_mask->NumSetBits() == 0)) {
-      // No dex map available.
-      stack_map.SetDexRegisterMapOffset(encoding.stack_map.encoding, StackMap::kNoDexRegisterMap);
-    } else {
-      // Search for an entry with the same dex map.
-      if (entry.same_dex_register_map_as_ != kNoSameDexMapFound) {
-        // If we have a hit reuse the offset.
-        stack_map.SetDexRegisterMapOffset(
-            encoding.stack_map.encoding,
-            code_info.GetStackMapAt(entry.same_dex_register_map_as_, encoding)
-                .GetDexRegisterMapOffset(encoding.stack_map.encoding));
-      } else {
-        // New dex registers maps should be added to the stack map.
-        MemoryRegion register_region = dex_register_locations_region.Subregion(
-            next_dex_register_map_offset,
-            ComputeDexRegisterMapSize(entry.num_dex_registers, entry.live_dex_registers_mask));
-        next_dex_register_map_offset += register_region.size();
-        DexRegisterMap dex_register_map(register_region);
-        stack_map.SetDexRegisterMapOffset(
-            encoding.stack_map.encoding,
-            register_region.begin() - dex_register_locations_region.begin());
+    size_t offset = MaybeCopyDexRegisterMap(dex_register_entries_[entry.dex_register_map_index],
+                                            &next_dex_register_map_offset,
+                                            dex_register_locations_region);
+    stack_map.SetDexRegisterMapOffset(encoding.stack_map.encoding, offset);
 
-        // Set the dex register location.
-        FillInDexRegisterMap(dex_register_map,
-                             entry.num_dex_registers,
-                             *entry.live_dex_registers_mask,
-                             entry.dex_register_locations_start_index);
-      }
+    if (entry.dex_method_index != DexFile::kDexNoIndex) {
+      InvokeInfo invoke_info(code_info.GetInvokeInfo(encoding, invoke_info_idx));
+      invoke_info.SetNativePcCodeOffset(encoding.invoke_info.encoding, entry.native_pc_code_offset);
+      invoke_info.SetInvokeType(encoding.invoke_info.encoding, entry.invoke_type);
+      invoke_info.SetMethodIndex(encoding.invoke_info.encoding, entry.dex_method_index);
+      ++invoke_info_idx;
     }
 
     // Set the inlining info.
@@ -371,29 +381,13 @@
           inline_info.SetExtraDataAtDepth(encoding.inline_info.encoding, depth, 1);
         }
         inline_info.SetDexPcAtDepth(encoding.inline_info.encoding, depth, inline_entry.dex_pc);
-        if (inline_entry.num_dex_registers == 0) {
-          // No dex map available.
-          inline_info.SetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding,
-                                                     depth,
-                                                     StackMap::kNoDexRegisterMap);
-          DCHECK(inline_entry.live_dex_registers_mask == nullptr);
-        } else {
-          MemoryRegion register_region = dex_register_locations_region.Subregion(
-              next_dex_register_map_offset,
-              ComputeDexRegisterMapSize(inline_entry.num_dex_registers,
-                                        inline_entry.live_dex_registers_mask));
-          next_dex_register_map_offset += register_region.size();
-          DexRegisterMap dex_register_map(register_region);
-          inline_info.SetDexRegisterMapOffsetAtDepth(
-              encoding.inline_info.encoding,
-              depth,
-              register_region.begin() - dex_register_locations_region.begin());
-
-          FillInDexRegisterMap(dex_register_map,
-                               inline_entry.num_dex_registers,
-                               *inline_entry.live_dex_registers_mask,
-                               inline_entry.dex_register_locations_start_index);
-        }
+        size_t dex_register_map_offset = MaybeCopyDexRegisterMap(
+            dex_register_entries_[inline_entry.dex_register_map_index],
+            &next_dex_register_map_offset,
+            dex_register_locations_region);
+        inline_info.SetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding,
+                                                   depth,
+                                                   dex_register_map_offset);
       }
     } else if (encoding.stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) {
       stack_map.SetInlineInfoIndex(encoding.stack_map.encoding, StackMap::kNoInlineInfo);
@@ -448,34 +442,31 @@
   }
 }
 
-size_t StackMapStream::FindEntryWithTheSameDexMap() {
-  size_t current_entry_index = stack_maps_.size();
-  auto entries_it = dex_map_hash_to_stack_map_indices_.find(current_entry_.dex_register_map_hash);
+size_t StackMapStream::AddDexRegisterMapEntry(const DexRegisterMapEntry& entry) {
+  const size_t current_entry_index = dex_register_entries_.size();
+  auto entries_it = dex_map_hash_to_stack_map_indices_.find(entry.hash);
   if (entries_it == dex_map_hash_to_stack_map_indices_.end()) {
     // We don't have a perfect hash functions so we need a list to collect all stack maps
     // which might have the same dex register map.
     ArenaVector<uint32_t> stack_map_indices(allocator_->Adapter(kArenaAllocStackMapStream));
     stack_map_indices.push_back(current_entry_index);
-    dex_map_hash_to_stack_map_indices_.Put(current_entry_.dex_register_map_hash,
-                                           std::move(stack_map_indices));
-    return kNoSameDexMapFound;
-  }
-
-  // We might have collisions, so we need to check whether or not we really have a match.
-  for (uint32_t test_entry_index : entries_it->second) {
-    if (HaveTheSameDexMaps(GetStackMap(test_entry_index), current_entry_)) {
-      return test_entry_index;
+    dex_map_hash_to_stack_map_indices_.Put(entry.hash, std::move(stack_map_indices));
+  } else {
+    // We might have collisions, so we need to check whether or not we really have a match.
+    for (uint32_t test_entry_index : entries_it->second) {
+      if (DexRegisterMapEntryEquals(dex_register_entries_[test_entry_index], entry)) {
+        return test_entry_index;
+      }
     }
+    entries_it->second.push_back(current_entry_index);
   }
-  entries_it->second.push_back(current_entry_index);
-  return kNoSameDexMapFound;
+  dex_register_entries_.push_back(entry);
+  return current_entry_index;
 }
 
-bool StackMapStream::HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const {
-  if (a.live_dex_registers_mask == nullptr && b.live_dex_registers_mask == nullptr) {
-    return true;
-  }
-  if (a.live_dex_registers_mask == nullptr || b.live_dex_registers_mask == nullptr) {
+bool StackMapStream::DexRegisterMapEntryEquals(const DexRegisterMapEntry& a,
+                                               const DexRegisterMapEntry& b) const {
+  if ((a.live_dex_registers_mask == nullptr) != (b.live_dex_registers_mask == nullptr)) {
     return false;
   }
   if (a.num_dex_registers != b.num_dex_registers) {
@@ -489,12 +480,12 @@
     }
     size_t number_of_live_dex_registers = a.live_dex_registers_mask->NumSetBits();
     DCHECK_LE(number_of_live_dex_registers, dex_register_locations_.size());
-    DCHECK_LE(a.dex_register_locations_start_index,
+    DCHECK_LE(a.locations_start_index,
               dex_register_locations_.size() - number_of_live_dex_registers);
-    DCHECK_LE(b.dex_register_locations_start_index,
+    DCHECK_LE(b.locations_start_index,
               dex_register_locations_.size() - number_of_live_dex_registers);
-    auto a_begin = dex_register_locations_.begin() + a.dex_register_locations_start_index;
-    auto b_begin = dex_register_locations_.begin() + b.dex_register_locations_start_index;
+    auto a_begin = dex_register_locations_.begin() + a.locations_start_index;
+    auto b_begin = dex_register_locations_.begin() + b.locations_start_index;
     if (!std::equal(a_begin, a_begin + number_of_live_dex_registers, b_begin)) {
       return false;
     }
@@ -570,6 +561,7 @@
   CodeInfo code_info(region);
   CodeInfoEncoding encoding = code_info.ExtractEncoding();
   DCHECK_EQ(code_info.GetNumberOfStackMaps(encoding), stack_maps_.size());
+  size_t invoke_info_index = 0;
   for (size_t s = 0; s < stack_maps_.size(); ++s) {
     const StackMap stack_map = code_info.GetStackMapAt(s, encoding);
     const StackMapEncoding& stack_map_encoding = encoding.stack_map.encoding;
@@ -594,13 +586,20 @@
         DCHECK_EQ(stack_mask.LoadBit(b), 0u);
       }
     }
-
+    if (entry.dex_method_index != DexFile::kDexNoIndex) {
+      InvokeInfo invoke_info = code_info.GetInvokeInfo(encoding, invoke_info_index);
+      DCHECK_EQ(invoke_info.GetNativePcOffset(encoding.invoke_info.encoding, instruction_set_),
+                entry.native_pc_code_offset.Uint32Value(instruction_set_));
+      DCHECK_EQ(invoke_info.GetInvokeType(encoding.invoke_info.encoding), entry.invoke_type);
+      DCHECK_EQ(invoke_info.GetMethodIndex(encoding.invoke_info.encoding), entry.dex_method_index);
+      invoke_info_index++;
+    }
     CheckDexRegisterMap(code_info,
                         code_info.GetDexRegisterMapOf(
-                            stack_map, encoding, entry.num_dex_registers),
-                        entry.num_dex_registers,
-                        entry.live_dex_registers_mask,
-                        entry.dex_register_locations_start_index);
+                            stack_map, encoding, entry.dex_register_entry.num_dex_registers),
+                        entry.dex_register_entry.num_dex_registers,
+                        entry.dex_register_entry.live_dex_registers_mask,
+                        entry.dex_register_entry.locations_start_index);
 
     // Check inline info.
     DCHECK_EQ(stack_map.HasInlineInfo(stack_map_encoding), (entry.inlining_depth != 0));
@@ -623,10 +622,13 @@
 
         CheckDexRegisterMap(code_info,
                             code_info.GetDexRegisterMapAtDepth(
-                                d, inline_info, encoding, inline_entry.num_dex_registers),
-                            inline_entry.num_dex_registers,
-                            inline_entry.live_dex_registers_mask,
-                            inline_entry.dex_register_locations_start_index);
+                                d,
+                                inline_info,
+                                encoding,
+                                inline_entry.dex_register_entry.num_dex_registers),
+                            inline_entry.dex_register_entry.num_dex_registers,
+                            inline_entry.dex_register_entry.live_dex_registers_mask,
+                            inline_entry.dex_register_entry.locations_start_index);
       }
     }
   }
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 08c1d3e..4225a87 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -70,6 +70,7 @@
         inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)),
         stack_masks_(allocator->Adapter(kArenaAllocStackMapStream)),
         register_masks_(allocator->Adapter(kArenaAllocStackMapStream)),
+        dex_register_entries_(allocator->Adapter(kArenaAllocStackMapStream)),
         stack_mask_max_(-1),
         dex_pc_max_(0),
         register_mask_max_(0),
@@ -89,30 +90,44 @@
     code_info_encoding_.reserve(16);
   }
 
+  // A dex register map entry for a single stack map entry, contains what registers are live as
+  // well as indices into the location catalog.
+  class DexRegisterMapEntry {
+   public:
+    static const size_t kOffsetUnassigned = -1;
+
+    BitVector* live_dex_registers_mask;
+    uint32_t num_dex_registers;
+    size_t locations_start_index;
+    // Computed fields
+    size_t hash = 0;
+    size_t offset = kOffsetUnassigned;
+
+    size_t ComputeSize(size_t catalog_size) const;
+  };
+
   // See runtime/stack_map.h to know what these fields contain.
   struct StackMapEntry {
     uint32_t dex_pc;
     CodeOffset native_pc_code_offset;
     uint32_t register_mask;
     BitVector* sp_mask;
-    uint32_t num_dex_registers;
     uint8_t inlining_depth;
-    size_t dex_register_locations_start_index;
     size_t inline_infos_start_index;
-    BitVector* live_dex_registers_mask;
-    uint32_t dex_register_map_hash;
-    size_t same_dex_register_map_as_;
     uint32_t stack_mask_index;
     uint32_t register_mask_index;
+    DexRegisterMapEntry dex_register_entry;
+    size_t dex_register_map_index;
+    InvokeType invoke_type;
+    uint32_t dex_method_index;
   };
 
   struct InlineInfoEntry {
     uint32_t dex_pc;  // DexFile::kDexNoIndex for intrinsified native methods.
     ArtMethod* method;
     uint32_t method_index;
-    uint32_t num_dex_registers;
-    BitVector* live_dex_registers_mask;
-    size_t dex_register_locations_start_index;
+    DexRegisterMapEntry dex_register_entry;
+    size_t dex_register_map_index;
   };
 
   void BeginStackMapEntry(uint32_t dex_pc,
@@ -125,6 +140,8 @@
 
   void AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value);
 
+  void AddInvoke(InvokeType type, uint32_t dex_method_index);
+
   void BeginInlineInfoEntry(ArtMethod* method,
                             uint32_t dex_pc,
                             uint32_t num_dex_registers,
@@ -140,7 +157,8 @@
   }
 
   void SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offset) {
-    stack_maps_[i].native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_);
+    stack_maps_[i].native_pc_code_offset =
+        CodeOffset::FromOffset(native_pc_offset, instruction_set_);
   }
 
   // Prepares the stream to fill in a memory region. Must be called before FillIn.
@@ -150,8 +168,6 @@
 
  private:
   size_t ComputeDexRegisterLocationCatalogSize() const;
-  size_t ComputeDexRegisterMapSize(uint32_t num_dex_registers,
-                                   const BitVector* live_dex_registers_mask) const;
   size_t ComputeDexRegisterMapsSize() const;
   void ComputeInlineInfoEncoding(InlineInfoEncoding* encoding,
                                  size_t dex_register_maps_bytes);
@@ -164,15 +180,32 @@
   // Returns the number of unique register masks.
   size_t PrepareRegisterMasks();
 
+  // Deduplicate entry if possible and return the corresponding index into dex_register_entries_
+  // array. If entry is not a duplicate, a new entry is added to dex_register_entries_.
+  size_t AddDexRegisterMapEntry(const DexRegisterMapEntry& entry);
+
+  // Return true if the two dex register map entries are equal.
+  bool DexRegisterMapEntryEquals(const DexRegisterMapEntry& a, const DexRegisterMapEntry& b) const;
+
+  // Fill in the corresponding entries of a register map.
+  void ComputeInvokeInfoEncoding(CodeInfoEncoding* encoding);
+
   // Returns the index of an entry with the same dex register map as the current_entry,
   // or kNoSameDexMapFound if no such entry exists.
   size_t FindEntryWithTheSameDexMap();
   bool HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const;
+
+  // Fill in the corresponding entries of a register map.
   void FillInDexRegisterMap(DexRegisterMap dex_register_map,
                             uint32_t num_dex_registers,
                             const BitVector& live_dex_registers_mask,
                             uint32_t start_index_in_dex_register_locations) const;
 
+  // Returns the offset for the dex register inside of the dex register location region. See FillIn.
+  // Only copies the dex register map if the offset for the entry is not already assigned.
+  size_t MaybeCopyDexRegisterMap(DexRegisterMapEntry& entry,
+                                 size_t* current_offset,
+                                 MemoryRegion dex_register_locations_region);
   void CheckDexRegisterMap(const CodeInfo& code_info,
                            const DexRegisterMap& dex_register_map,
                            size_t num_dex_registers,
@@ -199,6 +232,7 @@
   ArenaVector<InlineInfoEntry> inline_infos_;
   ArenaVector<uint8_t> stack_masks_;
   ArenaVector<uint32_t> register_masks_;
+  ArenaVector<DexRegisterMapEntry> dex_register_entries_;
   int stack_mask_max_;
   uint32_t dex_pc_max_;
   uint32_t register_mask_max_;
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index bd0aa6d..330f7f2 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -410,6 +410,100 @@
   }
 }
 
+TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) {
+  ArenaPool pool;
+  ArenaAllocator arena(&pool);
+  StackMapStream stream(&arena, kRuntimeISA);
+  ArtMethod art_method;
+
+  ArenaBitVector sp_mask1(&arena, 0, true);
+  sp_mask1.SetBit(2);
+  sp_mask1.SetBit(4);
+  const size_t number_of_dex_registers = 2;
+  const size_t number_of_dex_registers_in_inline_info = 2;
+  stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 1);
+  stream.AddDexRegisterEntry(Kind::kInStack, 0);         // Short location.
+  stream.AddDexRegisterEntry(Kind::kConstant, -2);       // Large location.
+  stream.BeginInlineInfoEntry(&art_method, 3, number_of_dex_registers_in_inline_info);
+  stream.AddDexRegisterEntry(Kind::kInStack, 0);         // Short location.
+  stream.AddDexRegisterEntry(Kind::kConstant, -2);       // Large location.
+  stream.EndInlineInfoEntry();
+  stream.EndStackMapEntry();
+
+  size_t size = stream.PrepareForFillIn();
+  void* memory = arena.Alloc(size, kArenaAllocMisc);
+  MemoryRegion region(memory, size);
+  stream.FillIn(region);
+
+  CodeInfo code_info(region);
+  CodeInfoEncoding encoding = code_info.ExtractEncoding();
+  ASSERT_EQ(1u, code_info.GetNumberOfStackMaps(encoding));
+
+  uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding);
+  ASSERT_EQ(2u, number_of_catalog_entries);
+  DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding);
+  // The Dex register location catalog contains:
+  // - one 1-byte short Dex register locations, and
+  // - one 5-byte large Dex register location.
+  const size_t expected_location_catalog_size = 1u + 5u;
+  ASSERT_EQ(expected_location_catalog_size, location_catalog.Size());
+
+  // First stack map.
+  {
+    StackMap stack_map = code_info.GetStackMapAt(0, encoding);
+    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding)));
+    ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding)));
+    ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding));
+    ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA));
+    ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map));
+
+    ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask1));
+
+    ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding));
+    DexRegisterMap map(code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers));
+    ASSERT_TRUE(map.IsDexRegisterLive(0));
+    ASSERT_TRUE(map.IsDexRegisterLive(1));
+    ASSERT_EQ(2u, map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
+    // The Dex register map contains:
+    // - one 1-byte live bit mask, and
+    // - one 1-byte set of location catalog entry indices composed of two 2-bit values.
+    size_t expected_map_size = 1u + 1u;
+    ASSERT_EQ(expected_map_size, map.Size());
+
+    ASSERT_EQ(Kind::kInStack, map.GetLocationKind(0, number_of_dex_registers, code_info, encoding));
+    ASSERT_EQ(Kind::kConstant,
+              map.GetLocationKind(1, number_of_dex_registers, code_info, encoding));
+    ASSERT_EQ(Kind::kInStack,
+              map.GetLocationInternalKind(0, number_of_dex_registers, code_info, encoding));
+    ASSERT_EQ(Kind::kConstantLargeValue,
+              map.GetLocationInternalKind(1, number_of_dex_registers, code_info, encoding));
+    ASSERT_EQ(0, map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info, encoding));
+    ASSERT_EQ(-2, map.GetConstant(1, number_of_dex_registers, code_info, encoding));
+
+    const size_t index0 =
+        map.GetLocationCatalogEntryIndex(0, number_of_dex_registers, number_of_catalog_entries);
+    const size_t index1 =
+        map.GetLocationCatalogEntryIndex(1, number_of_dex_registers, number_of_catalog_entries);
+    ASSERT_EQ(0u, index0);
+    ASSERT_EQ(1u, index1);
+    DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0);
+    DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1);
+    ASSERT_EQ(Kind::kInStack, location0.GetKind());
+    ASSERT_EQ(Kind::kConstant, location1.GetKind());
+    ASSERT_EQ(Kind::kInStack, location0.GetInternalKind());
+    ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind());
+    ASSERT_EQ(0, location0.GetValue());
+    ASSERT_EQ(-2, location1.GetValue());
+
+    // Test that the inline info dex register map deduplicated to the same offset as the stack map
+    // one.
+    ASSERT_TRUE(stack_map.HasInlineInfo(encoding.stack_map.encoding));
+    InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
+    EXPECT_EQ(inline_info.GetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, 0),
+              stack_map.GetDexRegisterMapOffset(encoding.stack_map.encoding));
+  }
+}
+
 TEST(StackMapTest, TestNonLiveDexRegisters) {
   ArenaPool pool;
   ArenaAllocator arena(&pool);
@@ -840,7 +934,6 @@
   EXPECT_EQ(offset_mips64.Uint32Value(kMips64), kMips64InstructionAlignment);
 }
 
-
 TEST(StackMapTest, TestDeduplicateStackMask) {
   ArenaPool pool;
   ArenaAllocator arena(&pool);
@@ -869,4 +962,48 @@
             stack_map2.GetStackMaskIndex(encoding.stack_map.encoding));
 }
 
+TEST(StackMapTest, TestInvokeInfo) {
+  ArenaPool pool;
+  ArenaAllocator arena(&pool);
+  StackMapStream stream(&arena, kRuntimeISA);
+
+  ArenaBitVector sp_mask(&arena, 0, true);
+  sp_mask.SetBit(1);
+  stream.BeginStackMapEntry(0, 4, 0x3, &sp_mask, 0, 0);
+  stream.AddInvoke(kSuper, 1);
+  stream.EndStackMapEntry();
+  stream.BeginStackMapEntry(0, 8, 0x3, &sp_mask, 0, 0);
+  stream.AddInvoke(kStatic, 3);
+  stream.EndStackMapEntry();
+  stream.BeginStackMapEntry(0, 16, 0x3, &sp_mask, 0, 0);
+  stream.AddInvoke(kDirect, 65535);
+  stream.EndStackMapEntry();
+
+  const size_t size = stream.PrepareForFillIn();
+  MemoryRegion region(arena.Alloc(size, kArenaAllocMisc), size);
+  stream.FillIn(region);
+
+  CodeInfo code_info(region);
+  CodeInfoEncoding encoding = code_info.ExtractEncoding();
+  ASSERT_EQ(3u, code_info.GetNumberOfStackMaps(encoding));
+
+  InvokeInfo invoke1(code_info.GetInvokeInfoForNativePcOffset(4, encoding));
+  InvokeInfo invoke2(code_info.GetInvokeInfoForNativePcOffset(8, encoding));
+  InvokeInfo invoke3(code_info.GetInvokeInfoForNativePcOffset(16, encoding));
+  InvokeInfo invoke_invalid(code_info.GetInvokeInfoForNativePcOffset(12, encoding));
+  EXPECT_FALSE(invoke_invalid.IsValid());  // No entry for that index.
+  EXPECT_TRUE(invoke1.IsValid());
+  EXPECT_TRUE(invoke2.IsValid());
+  EXPECT_TRUE(invoke3.IsValid());
+  EXPECT_EQ(invoke1.GetInvokeType(encoding.invoke_info.encoding), kSuper);
+  EXPECT_EQ(invoke1.GetMethodIndex(encoding.invoke_info.encoding), 1u);
+  EXPECT_EQ(invoke1.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA), 4u);
+  EXPECT_EQ(invoke2.GetInvokeType(encoding.invoke_info.encoding), kStatic);
+  EXPECT_EQ(invoke2.GetMethodIndex(encoding.invoke_info.encoding), 3u);
+  EXPECT_EQ(invoke2.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA), 8u);
+  EXPECT_EQ(invoke3.GetInvokeType(encoding.invoke_info.encoding), kDirect);
+  EXPECT_EQ(invoke3.GetMethodIndex(encoding.invoke_info.encoding), 65535u);
+  EXPECT_EQ(invoke3.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA), 16u);
+}
+
 }  // namespace art
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index a24d49e..5a466e1 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -783,6 +783,79 @@
 }
 
 
+void X86Assembler::movdqa(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0x6F);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::movdqa(XmmRegister dst, const Address& src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0x6F);
+  EmitOperand(dst, src);
+}
+
+
+void X86Assembler::movdqu(XmmRegister dst, const Address& src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xF3);
+  EmitUint8(0x0F);
+  EmitUint8(0x6F);
+  EmitOperand(dst, src);
+}
+
+
+void X86Assembler::movdqa(const Address& dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0x7F);
+  EmitOperand(src, dst);
+}
+
+
+void X86Assembler::movdqu(const Address& dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xF3);
+  EmitUint8(0x0F);
+  EmitUint8(0x7F);
+  EmitOperand(src, dst);
+}
+
+
+void X86Assembler::paddd(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0xFE);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::psubd(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0xFA);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::pmulld(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0x38);
+  EmitUint8(0x40);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
 void X86Assembler::cvtsi2ss(XmmRegister dst, Register src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xF3);
@@ -990,10 +1063,27 @@
 }
 
 
-void X86Assembler::andps(XmmRegister dst, XmmRegister src) {
+void X86Assembler::xorps(XmmRegister dst, const Address& src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x0F);
-  EmitUint8(0x54);
+  EmitUint8(0x57);
+  EmitOperand(dst, src);
+}
+
+
+void X86Assembler::xorps(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x0F);
+  EmitUint8(0x57);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::pxor(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0xEF);
   EmitXmmRegisterOperand(dst, src);
 }
 
@@ -1007,35 +1097,19 @@
 }
 
 
-void X86Assembler::orpd(XmmRegister dst, XmmRegister src) {
+void X86Assembler::andpd(XmmRegister dst, const Address& src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x66);
   EmitUint8(0x0F);
-  EmitUint8(0x56);
-  EmitXmmRegisterOperand(dst, src);
-}
-
-
-void X86Assembler::xorps(XmmRegister dst, const Address& src) {
-  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
-  EmitUint8(0x0F);
-  EmitUint8(0x57);
+  EmitUint8(0x54);
   EmitOperand(dst, src);
 }
 
 
-void X86Assembler::orps(XmmRegister dst, XmmRegister src) {
+void X86Assembler::andps(XmmRegister dst, XmmRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x0F);
-  EmitUint8(0x56);
-  EmitXmmRegisterOperand(dst, src);
-}
-
-
-void X86Assembler::xorps(XmmRegister dst, XmmRegister src) {
-  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
-  EmitUint8(0x0F);
-  EmitUint8(0x57);
+  EmitUint8(0x54);
   EmitXmmRegisterOperand(dst, src);
 }
 
@@ -1048,12 +1122,38 @@
 }
 
 
-void X86Assembler::andpd(XmmRegister dst, const Address& src) {
+void X86Assembler::pand(XmmRegister dst, XmmRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x66);
   EmitUint8(0x0F);
-  EmitUint8(0x54);
-  EmitOperand(dst, src);
+  EmitUint8(0xDB);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::orpd(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0x56);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::orps(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x0F);
+  EmitUint8(0x56);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::por(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0xEB);
+  EmitXmmRegisterOperand(dst, src);
 }
 
 
@@ -1076,6 +1176,16 @@
 }
 
 
+void X86Assembler::pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0x70);
+  EmitXmmRegisterOperand(dst, src);
+  EmitUint8(imm.value());
+}
+
+
 void X86Assembler::fldl(const Address& src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xDD);
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index 4056ca6..4343e2e 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -430,6 +430,16 @@
   void mulpd(XmmRegister dst, XmmRegister src);
   void divpd(XmmRegister dst, XmmRegister src);
 
+  void movdqa(XmmRegister dst, XmmRegister src);     // move
+  void movdqa(XmmRegister dst, const Address& src);  // load aligned
+  void movdqu(XmmRegister dst, const Address& src);  // load unaligned
+  void movdqa(const Address& dst, XmmRegister src);  // store aligned
+  void movdqu(const Address& dst, XmmRegister src);  // store unaligned
+
+  void paddd(XmmRegister dst, XmmRegister src);  // no addr variant (for now)
+  void psubd(XmmRegister dst, XmmRegister src);
+  void pmulld(XmmRegister dst, XmmRegister src);
+
   void cvtsi2ss(XmmRegister dst, Register src);
   void cvtsi2sd(XmmRegister dst, Register src);
 
@@ -463,17 +473,21 @@
   void xorpd(XmmRegister dst, XmmRegister src);
   void xorps(XmmRegister dst, const Address& src);
   void xorps(XmmRegister dst, XmmRegister src);
+  void pxor(XmmRegister dst, XmmRegister src);  // no addr variant (for now)
 
   void andpd(XmmRegister dst, XmmRegister src);
   void andpd(XmmRegister dst, const Address& src);
   void andps(XmmRegister dst, XmmRegister src);
   void andps(XmmRegister dst, const Address& src);
+  void pand(XmmRegister dst, XmmRegister src);  // no addr variant (for now)
 
-  void orpd(XmmRegister dst, XmmRegister src);
+  void orpd(XmmRegister dst, XmmRegister src);  // no addr variant (for now)
   void orps(XmmRegister dst, XmmRegister src);
+  void por(XmmRegister dst, XmmRegister src);
 
   void shufpd(XmmRegister dst, XmmRegister src, const Immediate& imm);
   void shufps(XmmRegister dst, XmmRegister src, const Immediate& imm);
+  void pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm);
 
   void flds(const Address& src);
   void fstps(const Address& dst);
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 1768d8b..c6ab893 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -467,6 +467,28 @@
   DriverStr(expected, "movupd_address");
 }
 
+TEST_F(AssemblerX86Test, Movdqa) {
+  DriverStr(RepeatFF(&x86::X86Assembler::movdqa, "movdqa %{reg2}, %{reg1}"), "movdqa");
+}
+
+TEST_F(AssemblerX86Test, MovdqaAddr) {
+  GetAssembler()->movdqa(x86::XmmRegister(x86::XMM0), x86::Address(x86::Register(x86::ESP), 4));
+  GetAssembler()->movdqa(x86::Address(x86::Register(x86::ESP), 2), x86::XmmRegister(x86::XMM1));
+  const char* expected =
+    "movdqa 0x4(%ESP), %xmm0\n"
+    "movdqa %xmm1, 0x2(%ESP)\n";
+  DriverStr(expected, "movdqa_address");
+}
+
+TEST_F(AssemblerX86Test, MovdquAddr) {
+  GetAssembler()->movdqu(x86::XmmRegister(x86::XMM0), x86::Address(x86::Register(x86::ESP), 4));
+  GetAssembler()->movdqu(x86::Address(x86::Register(x86::ESP), 2), x86::XmmRegister(x86::XMM1));
+  const char* expected =
+    "movdqu 0x4(%ESP), %xmm0\n"
+    "movdqu %xmm1, 0x2(%ESP)\n";
+  DriverStr(expected, "movdqu_address");
+}
+
 TEST_F(AssemblerX86Test, AddPS) {
   DriverStr(RepeatFF(&x86::X86Assembler::addps, "addps %{reg2}, %{reg1}"), "addps");
 }
@@ -499,6 +521,54 @@
   DriverStr(RepeatFF(&x86::X86Assembler::divpd, "divpd %{reg2}, %{reg1}"), "divpd");
 }
 
+TEST_F(AssemblerX86Test, PAddD) {
+  DriverStr(RepeatFF(&x86::X86Assembler::paddd, "paddd %{reg2}, %{reg1}"), "paddd");
+}
+
+TEST_F(AssemblerX86Test, PSubD) {
+  DriverStr(RepeatFF(&x86::X86Assembler::psubd, "psubd %{reg2}, %{reg1}"), "psubd");
+}
+
+TEST_F(AssemblerX86Test, PMullD) {
+  DriverStr(RepeatFF(&x86::X86Assembler::pmulld, "pmulld %{reg2}, %{reg1}"), "pmulld");
+}
+
+TEST_F(AssemblerX86Test, XorPD) {
+  DriverStr(RepeatFF(&x86::X86Assembler::xorpd, "xorpd %{reg2}, %{reg1}"), "xorpd");
+}
+
+TEST_F(AssemblerX86Test, XorPS) {
+  DriverStr(RepeatFF(&x86::X86Assembler::xorps, "xorps %{reg2}, %{reg1}"), "xorps");
+}
+
+TEST_F(AssemblerX86Test, PXor) {
+  DriverStr(RepeatFF(&x86::X86Assembler::pxor, "pxor %{reg2}, %{reg1}"), "pxor");
+}
+
+TEST_F(AssemblerX86Test, AndPD) {
+  DriverStr(RepeatFF(&x86::X86Assembler::andpd, "andpd %{reg2}, %{reg1}"), "andpd");
+}
+
+TEST_F(AssemblerX86Test, AndPS) {
+  DriverStr(RepeatFF(&x86::X86Assembler::andps, "andps %{reg2}, %{reg1}"), "andps");
+}
+
+TEST_F(AssemblerX86Test, PAnd) {
+  DriverStr(RepeatFF(&x86::X86Assembler::pand, "pand %{reg2}, %{reg1}"), "pand");
+}
+
+TEST_F(AssemblerX86Test, OrPD) {
+  DriverStr(RepeatFF(&x86::X86Assembler::orpd, "orpd %{reg2}, %{reg1}"), "orpd");
+}
+
+TEST_F(AssemblerX86Test, OrPS) {
+  DriverStr(RepeatFF(&x86::X86Assembler::orps, "orps %{reg2}, %{reg1}"), "orps");
+}
+
+TEST_F(AssemblerX86Test, POr) {
+  DriverStr(RepeatFF(&x86::X86Assembler::por, "por %{reg2}, %{reg1}"), "por");
+}
+
 TEST_F(AssemblerX86Test, ShufPS) {
   DriverStr(RepeatFFI(&x86::X86Assembler::shufps, 1, "shufps ${imm}, %{reg2}, %{reg1}"), "shufps");
 }
@@ -507,6 +577,10 @@
   DriverStr(RepeatFFI(&x86::X86Assembler::shufpd, 1, "shufpd ${imm}, %{reg2}, %{reg1}"), "shufpd");
 }
 
+TEST_F(AssemblerX86Test, PShufD) {
+  DriverStr(RepeatFFI(&x86::X86Assembler::pshufd, 1, "pshufd ${imm}, %{reg2}, %{reg1}"), "pshufd");
+}
+
 /////////////////
 // Near labels //
 /////////////////
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index c2c44ab..b41be80 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -832,6 +832,87 @@
 }
 
 
+void X86_64Assembler::movdqa(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0x6F);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::movdqa(XmmRegister dst, const Address& src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0x6F);
+  EmitOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::movdqu(XmmRegister dst, const Address& src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xF3);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0x6F);
+  EmitOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::movdqa(const Address& dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(src, dst);
+  EmitUint8(0x0F);
+  EmitUint8(0x7F);
+  EmitOperand(src.LowBits(), dst);
+}
+
+
+void X86_64Assembler::movdqu(const Address& dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xF3);
+  EmitOptionalRex32(src, dst);
+  EmitUint8(0x0F);
+  EmitUint8(0x7F);
+  EmitOperand(src.LowBits(), dst);
+}
+
+
+void X86_64Assembler::paddd(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0xFE);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::psubd(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0xFA);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::pmulld(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0x38);
+  EmitUint8(0x40);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
 void X86_64Assembler::cvtsi2ss(XmmRegister dst, CpuRegister src) {
   cvtsi2ss(dst, src, false);
 }
@@ -1170,6 +1251,16 @@
 }
 
 
+void X86_64Assembler::pxor(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0xEF);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
 void X86_64Assembler::andpd(XmmRegister dst, const Address& src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x66);
@@ -1196,6 +1287,15 @@
   EmitXmmRegisterOperand(dst.LowBits(), src);
 }
 
+void X86_64Assembler::pand(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0xDB);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
 void X86_64Assembler::orpd(XmmRegister dst, XmmRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x66);
@@ -1213,6 +1313,14 @@
   EmitXmmRegisterOperand(dst.LowBits(), src);
 }
 
+void X86_64Assembler::por(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0xEB);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
 
 void X86_64Assembler::shufpd(XmmRegister dst, XmmRegister src, const Immediate& imm) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
@@ -1235,6 +1343,17 @@
 }
 
 
+void X86_64Assembler::pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0x70);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+  EmitUint8(imm.value());
+}
+
+
 void X86_64Assembler::fldl(const Address& src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xDD);
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index e140b45..43ea12a 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -446,6 +446,16 @@
   void mulpd(XmmRegister dst, XmmRegister src);
   void divpd(XmmRegister dst, XmmRegister src);
 
+  void movdqa(XmmRegister dst, XmmRegister src);     // move
+  void movdqa(XmmRegister dst, const Address& src);  // load aligned
+  void movdqu(XmmRegister dst, const Address& src);  // load unaligned
+  void movdqa(const Address& dst, XmmRegister src);  // store aligned
+  void movdqu(const Address& dst, XmmRegister src);  // store unaligned
+
+  void paddd(XmmRegister dst, XmmRegister src);  // no addr variant (for now)
+  void psubd(XmmRegister dst, XmmRegister src);
+  void pmulld(XmmRegister dst, XmmRegister src);
+
   void cvtsi2ss(XmmRegister dst, CpuRegister src);  // Note: this is the r/m32 version.
   void cvtsi2ss(XmmRegister dst, CpuRegister src, bool is64bit);
   void cvtsi2ss(XmmRegister dst, const Address& src, bool is64bit);
@@ -487,16 +497,20 @@
   void xorpd(XmmRegister dst, XmmRegister src);
   void xorps(XmmRegister dst, const Address& src);
   void xorps(XmmRegister dst, XmmRegister src);
+  void pxor(XmmRegister dst, XmmRegister src);  // no addr variant (for now)
 
   void andpd(XmmRegister dst, const Address& src);
   void andpd(XmmRegister dst, XmmRegister src);
-  void andps(XmmRegister dst, XmmRegister src);
+  void andps(XmmRegister dst, XmmRegister src);  // no addr variant (for now)
+  void pand(XmmRegister dst, XmmRegister src);
 
-  void orpd(XmmRegister dst, XmmRegister src);
+  void orpd(XmmRegister dst, XmmRegister src);  // no addr variant (for now)
   void orps(XmmRegister dst, XmmRegister src);
+  void por(XmmRegister dst, XmmRegister src);
 
   void shufpd(XmmRegister dst, XmmRegister src, const Immediate& imm);
   void shufps(XmmRegister dst, XmmRegister src, const Immediate& imm);
+  void pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm);
 
   void flds(const Address& src);
   void fstps(const Address& dst);
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index efa5cc9..aeb1911 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -1034,6 +1034,28 @@
   DriverStr(RepeatFF(&x86_64::X86_64Assembler::movsd, "movsd %{reg2}, %{reg1}"), "movsd");
 }
 
+TEST_F(AssemblerX86_64Test, Movdqa) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::movdqa, "movdqa %{reg2}, %{reg1}"), "movapd");
+}
+
+TEST_F(AssemblerX86_64Test, MovdqaAddr) {
+  GetAssembler()->movdqa(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4));
+  GetAssembler()->movdqa(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1));
+  const char* expected =
+    "movdqa 0x4(%RSP), %xmm0\n"
+    "movdqa %xmm1, 0x2(%RSP)\n";
+  DriverStr(expected, "movdqa_address");
+}
+
+TEST_F(AssemblerX86_64Test, MovdquAddr) {
+  GetAssembler()->movdqu(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4));
+  GetAssembler()->movdqu(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1));
+  const char* expected =
+    "movdqu 0x4(%RSP), %xmm0\n"
+    "movdqu %xmm1, 0x2(%RSP)\n";
+  DriverStr(expected, "movdqu_address");
+}
+
 TEST_F(AssemblerX86_64Test, Movd1) {
   DriverStr(RepeatFR(&x86_64::X86_64Assembler::movd, "movd %{reg2}, %{reg1}"), "movd.1");
 }
@@ -1106,6 +1128,18 @@
   DriverStr(RepeatFF(&x86_64::X86_64Assembler::divpd, "divpd %{reg2}, %{reg1}"), "divpd");
 }
 
+TEST_F(AssemblerX86_64Test, Paddd) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddd, "paddd %{reg2}, %{reg1}"), "paddd");
+}
+
+TEST_F(AssemblerX86_64Test, Psubd) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubd, "psubd %{reg2}, %{reg1}"), "psubd");
+}
+
+TEST_F(AssemblerX86_64Test, Pmulld) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::pmulld, "pmulld %{reg2}, %{reg1}"), "pmulld");
+}
+
 TEST_F(AssemblerX86_64Test, Cvtsi2ss) {
   DriverStr(RepeatFr(&x86_64::X86_64Assembler::cvtsi2ss, "cvtsi2ss %{reg2}, %{reg1}"), "cvtsi2ss");
 }
@@ -1187,6 +1221,10 @@
   DriverStr(RepeatFF(&x86_64::X86_64Assembler::xorpd, "xorpd %{reg2}, %{reg1}"), "xorpd");
 }
 
+TEST_F(AssemblerX86_64Test, Pxor) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::pxor, "pxor %{reg2}, %{reg1}"), "pxor");
+}
+
 TEST_F(AssemblerX86_64Test, Andps) {
   DriverStr(RepeatFF(&x86_64::X86_64Assembler::andps, "andps %{reg2}, %{reg1}"), "andps");
 }
@@ -1195,6 +1233,10 @@
   DriverStr(RepeatFF(&x86_64::X86_64Assembler::andpd, "andpd %{reg2}, %{reg1}"), "andpd");
 }
 
+TEST_F(AssemblerX86_64Test, Pand) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::pand, "pand %{reg2}, %{reg1}"), "pand");
+}
+
 TEST_F(AssemblerX86_64Test, Orps) {
   DriverStr(RepeatFF(&x86_64::X86_64Assembler::orps, "orps %{reg2}, %{reg1}"), "orps");
 }
@@ -1203,6 +1245,10 @@
   DriverStr(RepeatFF(&x86_64::X86_64Assembler::orpd, "orpd %{reg2}, %{reg1}"), "orpd");
 }
 
+TEST_F(AssemblerX86_64Test, Por) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::por, "por %{reg2}, %{reg1}"), "por");
+}
+
 TEST_F(AssemblerX86_64Test, Shufps) {
   DriverStr(RepeatFFI(&x86_64::X86_64Assembler::shufps, 1, "shufps ${imm}, %{reg2}, %{reg1}"), "shufps");
 }
@@ -1211,6 +1257,10 @@
   DriverStr(RepeatFFI(&x86_64::X86_64Assembler::shufpd, 1, "shufpd ${imm}, %{reg2}, %{reg1}"), "shufpd");
 }
 
+TEST_F(AssemblerX86_64Test, PShufd) {
+  DriverStr(RepeatFFI(&x86_64::X86_64Assembler::pshufd, 1, "pshufd ${imm}, %{reg2}, %{reg1}"), "pshufd");
+}
+
 TEST_F(AssemblerX86_64Test, UcomissAddress) {
   GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(
       x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12));
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 5fc9972..c892b25 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -233,7 +233,7 @@
         const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
         const char* descriptor = dex_file->GetClassDescriptor(class_def);
         cls.Assign(class_linker_->FindClass(soa.Self(), descriptor, class_loader_handle));
-        if (cls.Get() == nullptr) {
+        if (cls == nullptr) {
           CHECK(soa.Self()->IsExceptionPending());
           soa.Self()->ClearException();
         } else if (set.find(class_def.class_idx_) == set.end()) {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 192fc27..026a567 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -419,12 +419,9 @@
   } while (false)
 
  public:
-  explicit WatchDog(bool is_watch_dog_enabled) {
-    is_watch_dog_enabled_ = is_watch_dog_enabled;
-    if (!is_watch_dog_enabled_) {
-      return;
-    }
-    shutting_down_ = false;
+  explicit WatchDog(int64_t timeout_in_milliseconds)
+      : timeout_in_milliseconds_(timeout_in_milliseconds),
+        shutting_down_(false) {
     const char* reason = "dex2oat watch dog thread startup";
     CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_init, (&mutex_, nullptr), reason);
     CHECK_WATCH_DOG_PTHREAD_CALL(pthread_cond_init, (&cond_, nullptr), reason);
@@ -433,9 +430,6 @@
     CHECK_WATCH_DOG_PTHREAD_CALL(pthread_attr_destroy, (&attr_), reason);
   }
   ~WatchDog() {
-    if (!is_watch_dog_enabled_) {
-      return;
-    }
     const char* reason = "dex2oat watch dog thread shutdown";
     CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_lock, (&mutex_), reason);
     shutting_down_ = true;
@@ -448,6 +442,23 @@
     CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_destroy, (&mutex_), reason);
   }
 
+  // TODO: tune the multiplier for GC verification, the following is just to make the timeout
+  //       large.
+  static constexpr int64_t kWatchdogVerifyMultiplier =
+      kVerifyObjectSupport > kVerifyObjectModeFast ? 100 : 1;
+
+  // When setting timeouts, keep in mind that the build server may not be as fast as your
+  // desktop. Debug builds are slower so they have larger timeouts.
+  static constexpr int64_t kWatchdogSlowdownFactor = kIsDebugBuild ? 5U : 1U;
+
+  // 9.5 minutes scaled by kSlowdownFactor. This is slightly smaller than the Package Manager
+  // watchdog (PackageManagerService.WATCHDOG_TIMEOUT, 10 minutes), so that dex2oat will abort
+  // itself before that watchdog would take down the system server.
+  static constexpr int64_t kWatchDogTimeoutSeconds = kWatchdogSlowdownFactor * (9 * 60 + 30);
+
+  static constexpr int64_t kDefaultWatchdogTimeoutInMS =
+      kWatchdogVerifyMultiplier * kWatchDogTimeoutSeconds * 1000;
+
  private:
   static void* CallBack(void* arg) {
     WatchDog* self = reinterpret_cast<WatchDog*>(arg);
@@ -470,18 +481,15 @@
   }
 
   void Wait() {
-    // TODO: tune the multiplier for GC verification, the following is just to make the timeout
-    //       large.
-    constexpr int64_t multiplier = kVerifyObjectSupport > kVerifyObjectModeFast ? 100 : 1;
     timespec timeout_ts;
-    InitTimeSpec(true, CLOCK_REALTIME, multiplier * kWatchDogTimeoutSeconds * 1000, 0, &timeout_ts);
+    InitTimeSpec(true, CLOCK_REALTIME, timeout_in_milliseconds_, 0, &timeout_ts);
     const char* reason = "dex2oat watch dog thread waiting";
     CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_lock, (&mutex_), reason);
     while (!shutting_down_) {
       int rc = TEMP_FAILURE_RETRY(pthread_cond_timedwait(&cond_, &mutex_, &timeout_ts));
       if (rc == ETIMEDOUT) {
         Fatal(StringPrintf("dex2oat did not finish after %" PRId64 " seconds",
-                           kWatchDogTimeoutSeconds));
+                           timeout_in_milliseconds_/1000));
       } else if (rc != 0) {
         std::string message(StringPrintf("pthread_cond_timedwait failed: %s",
                                          strerror(errno)));
@@ -491,16 +499,7 @@
     CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_unlock, (&mutex_), reason);
   }
 
-  // When setting timeouts, keep in mind that the build server may not be as fast as your desktop.
-  // Debug builds are slower so they have larger timeouts.
-  static constexpr int64_t kSlowdownFactor = kIsDebugBuild ? 5U : 1U;
-
-  // 9.5 minutes scaled by kSlowdownFactor. This is slightly smaller than the Package Manager
-  // watchdog (PackageManagerService.WATCHDOG_TIMEOUT, 10 minutes), so that dex2oat will abort
-  // itself before that watchdog would take down the system server.
-  static constexpr int64_t kWatchDogTimeoutSeconds = kSlowdownFactor * (9 * 60 + 30);
-
-  bool is_watch_dog_enabled_;
+  const int64_t timeout_in_milliseconds_;
   bool shutting_down_;
   // TODO: Switch to Mutex when we can guarantee it won't prevent shutdown in error cases.
   pthread_mutex_t mutex_;
@@ -591,6 +590,7 @@
   struct ParserOptions {
     std::vector<const char*> oat_symbols;
     std::string boot_image_filename;
+    int64_t watch_dog_timeout_in_ms = -1;
     bool watch_dog_enabled = true;
     bool requested_specific_compiler = false;
     std::string error_msg;
@@ -919,7 +919,10 @@
 
     // Done with usage checks, enable watchdog if requested
     if (parser_options->watch_dog_enabled) {
-      watchdog_.reset(new WatchDog(true));
+      int64_t timeout = parser_options->watch_dog_timeout_in_ms > 0
+                            ? parser_options->watch_dog_timeout_in_ms
+                            : WatchDog::kDefaultWatchdogTimeoutInMS;
+      watchdog_.reset(new WatchDog(timeout));
     }
 
     // Fill some values into the key-value store for the oat header.
@@ -1150,6 +1153,11 @@
         parser_options->watch_dog_enabled = true;
       } else if (option == "--no-watch-dog") {
         parser_options->watch_dog_enabled = false;
+      } else if (option.starts_with("--watchdog-timeout=")) {
+        ParseIntOption(option,
+                       "--watchdog-timeout",
+                       &parser_options->watch_dog_timeout_in_ms,
+                       Usage);
       } else if (option.starts_with("-j")) {
         ParseJ(option);
       } else if (option.starts_with("--image=")) {
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index e208337..6881f75 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -66,7 +66,7 @@
     bool success = Dex2Oat(args, &error_msg);
 
     if (expect_success) {
-      ASSERT_TRUE(success) << error_msg;
+      ASSERT_TRUE(success) << error_msg << std::endl << output_;
 
       // Verify the odex file was generated as expected.
       std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
@@ -399,6 +399,11 @@
 };
 
 TEST_F(Dex2oatSwapUseTest, CheckSwapUsage) {
+  // The `native_alloc_2_ >= native_alloc_1_` assertion below may not
+  // hold true on some x86 systems; disable this test while we
+  // investigate (b/29259363).
+  TEST_DISABLED_FOR_X86();
+
   RunTest(false /* use_fd */,
           false /* expect_use */);
   GrabResult1();
@@ -653,4 +658,41 @@
   RunTest();
 }
 
+class Dex2oatWatchdogTest : public Dex2oatTest {
+ protected:
+  void RunTest(bool expect_success, const std::vector<std::string>& extra_args = {}) {
+    std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar";
+    std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex";
+
+    Copy(GetTestDexFileName(), dex_location);
+
+    std::vector<std::string> copy(extra_args);
+
+    std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap";
+    copy.push_back("--swap-file=" + swap_location);
+    GenerateOdexForTest(dex_location,
+                        odex_location,
+                        CompilerFilter::kSpeed,
+                        copy,
+                        expect_success);
+  }
+
+  std::string GetTestDexFileName() {
+    return GetDexSrc1();
+  }
+};
+
+TEST_F(Dex2oatWatchdogTest, TestWatchdogOK) {
+  // Check with default.
+  RunTest(true);
+
+  // Check with ten minutes.
+  RunTest(true, { "--watchdog-timeout=600000" });
+}
+
+TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) {
+  // Check with ten milliseconds.
+  RunTest(false, { "--watchdog-timeout=10" });
+}
+
 }  // namespace art
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index d5776fa..5656ddd 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -881,26 +881,30 @@
       outSize = snprintf(buf.get(), bufSize, "[obj+%0*x]", width, index);
       break;
     case Instruction::kIndexMethodAndProtoRef: {
-        std::string method("<method?>");
-        std::string proto("<proto?>");
-        if (index < pDexFile->GetHeader().method_ids_size_) {
-          const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(index);
-          const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_);
-          const Signature signature = pDexFile->GetMethodSignature(pMethodId);
-          const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_);
-          method = android::base::StringPrintf("%s.%s:%s",
-                                               backDescriptor,
-                                               name,
-                                               signature.ToString().c_str());
-        }
-        if (secondary_index < pDexFile->GetHeader().proto_ids_size_) {
-          const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index);
-          const Signature signature = pDexFile->GetProtoSignature(protoId);
-          proto = signature.ToString();
-        }
-        outSize = snprintf(buf.get(), bufSize, "%s, %s // method@%0*x, proto@%0*x",
-                           method.c_str(), proto.c_str(), width, index, width, secondary_index);
+      std::string method("<method?>");
+      std::string proto("<proto?>");
+      if (index < pDexFile->GetHeader().method_ids_size_) {
+        const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(index);
+        const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_);
+        const Signature signature = pDexFile->GetMethodSignature(pMethodId);
+        const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_);
+        method = android::base::StringPrintf("%s.%s:%s",
+                                             backDescriptor,
+                                             name,
+                                             signature.ToString().c_str());
       }
+      if (secondary_index < pDexFile->GetHeader().proto_ids_size_) {
+        const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index);
+        const Signature signature = pDexFile->GetProtoSignature(protoId);
+        proto = signature.ToString();
+      }
+      outSize = snprintf(buf.get(), bufSize, "%s, %s // method@%0*x, proto@%0*x",
+                         method.c_str(), proto.c_str(), width, index, width, secondary_index);
+      break;
+    }
+    case Instruction::kIndexCallSiteRef:
+      // Call site information is too large to detail in disassembly so just output the index.
+      outSize = snprintf(buf.get(), bufSize, "call_site@%0*x", width, index);
       break;
     // SOME NOT SUPPORTED:
     // case Instruction::kIndexVaries:
@@ -1581,6 +1585,198 @@
   free(accessStr);
 }
 
+static void dumpMethodHandle(const DexFile* pDexFile, u4 idx) {
+  const DexFile::MethodHandleItem& mh = pDexFile->GetMethodHandle(idx);
+  bool is_invoke = false;
+  const char* type;
+  switch (static_cast<DexFile::MethodHandleType>(mh.method_handle_type_)) {
+    case DexFile::MethodHandleType::kStaticPut:
+      type = "put-static";
+      break;
+    case DexFile::MethodHandleType::kStaticGet:
+      type = "get-static";
+      break;
+    case DexFile::MethodHandleType::kInstancePut:
+      type = "put-instance";
+      break;
+    case DexFile::MethodHandleType::kInstanceGet:
+      type = "get-instance";
+      break;
+    case DexFile::MethodHandleType::kInvokeStatic:
+      type = "invoke-static";
+      is_invoke = true;
+      break;
+    case DexFile::MethodHandleType::kInvokeInstance:
+      type = "invoke-instance";
+      is_invoke = true;
+      break;
+    case DexFile::MethodHandleType::kInvokeConstructor:
+      type = "invoke-constructor";
+      is_invoke = true;
+      break;
+  }
+
+  const char* declaring_class;
+  const char* member;
+  std::string member_type;
+  if (is_invoke) {
+    const DexFile::MethodId& method_id = pDexFile->GetMethodId(mh.field_or_method_idx_);
+    declaring_class = pDexFile->GetMethodDeclaringClassDescriptor(method_id);
+    member = pDexFile->GetMethodName(method_id);
+    member_type = pDexFile->GetMethodSignature(method_id).ToString();
+  } else {
+    const DexFile::FieldId& field_id = pDexFile->GetFieldId(mh.field_or_method_idx_);
+    declaring_class = pDexFile->GetFieldDeclaringClassDescriptor(field_id);
+    member = pDexFile->GetFieldName(field_id);
+    member_type = pDexFile->GetFieldTypeDescriptor(field_id);
+  }
+
+  if (gOptions.outputFormat == OUTPUT_PLAIN) {
+    fprintf(gOutFile, "Method handle #%u:\n", idx);
+    fprintf(gOutFile, "  type        : %s\n", type);
+    fprintf(gOutFile, "  target      : %s %s\n", declaring_class, member);
+    fprintf(gOutFile, "  target_type : %s\n", member_type.c_str());
+  } else {
+    fprintf(gOutFile, "<method_handle index=\"%u\"\n", idx);
+    fprintf(gOutFile, " type=\"%s\"\n", type);
+    fprintf(gOutFile, " target_class=\"%s\"\n", declaring_class);
+    fprintf(gOutFile, " target_member=\"%s\"\n", member);
+    fprintf(gOutFile, " target_member_type=");
+    dumpEscapedString(member_type.c_str());
+    fprintf(gOutFile, "\n>\n</method_handle>\n");
+  }
+}
+
+static void dumpCallSite(const DexFile* pDexFile, u4 idx) {
+  const DexFile::CallSiteIdItem& call_site_id = pDexFile->GetCallSiteId(idx);
+  CallSiteArrayValueIterator it(*pDexFile, call_site_id);
+  if (it.Size() < 3) {
+    fprintf(stderr, "ERROR: Call site %u has too few values.\n", idx);
+    return;
+  }
+
+  uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+  it.Next();
+  dex::StringIndex method_name_idx = static_cast<dex::StringIndex>(it.GetJavaValue().i);
+  const char* method_name = pDexFile->StringDataByIdx(method_name_idx);
+  it.Next();
+  uint32_t method_type_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+  const DexFile::ProtoId& method_type_id = pDexFile->GetProtoId(method_type_idx);
+  std::string method_type = pDexFile->GetProtoSignature(method_type_id).ToString();
+  it.Next();
+
+  if (gOptions.outputFormat == OUTPUT_PLAIN) {
+    fprintf(gOutFile, "Call site #%u:\n", idx);
+    fprintf(gOutFile, "  link_argument[0] : %u (MethodHandle)\n", method_handle_idx);
+    fprintf(gOutFile, "  link_argument[1] : %s (String)\n", method_name);
+    fprintf(gOutFile, "  link_argument[2] : %s (MethodType)\n", method_type.c_str());
+  } else {
+    fprintf(gOutFile, "<call_site index=\"%u\">\n", idx);
+    fprintf(gOutFile,
+            "<link_argument index=\"0\" type=\"MethodHandle\" value=\"%u\"/>\n",
+            method_handle_idx);
+    fprintf(gOutFile,
+            "<link_argument index=\"1\" type=\"String\" values=\"%s\"/>\n",
+            method_name);
+    fprintf(gOutFile,
+            "<link_argument index=\"2\" type=\"MethodType\" value=\"%s\"/>\n",
+            method_type.c_str());
+  }
+
+  size_t argument = 3;
+  while (it.HasNext()) {
+    const char* type;
+    std::string value;
+    switch (it.GetValueType()) {
+      case EncodedArrayValueIterator::ValueType::kByte:
+        type = "byte";
+        value = android::base::StringPrintf("%u", it.GetJavaValue().b);
+        break;
+      case EncodedArrayValueIterator::ValueType::kShort:
+        type = "short";
+        value = android::base::StringPrintf("%d", it.GetJavaValue().s);
+        break;
+      case EncodedArrayValueIterator::ValueType::kChar:
+        type = "char";
+        value = android::base::StringPrintf("%u", it.GetJavaValue().c);
+        break;
+      case EncodedArrayValueIterator::ValueType::kInt:
+        type = "int";
+        value = android::base::StringPrintf("%d", it.GetJavaValue().i);
+        break;
+      case EncodedArrayValueIterator::ValueType::kLong:
+        type = "long";
+        value = android::base::StringPrintf("%" PRId64, it.GetJavaValue().j);
+        break;
+      case EncodedArrayValueIterator::ValueType::kFloat:
+        type = "float";
+        value = android::base::StringPrintf("%g", it.GetJavaValue().f);
+        break;
+      case EncodedArrayValueIterator::ValueType::kDouble:
+        type = "double";
+        value = android::base::StringPrintf("%g", it.GetJavaValue().d);
+        break;
+      case EncodedArrayValueIterator::ValueType::kMethodType: {
+        type = "MethodType";
+        uint32_t proto_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+        const DexFile::ProtoId& proto_id = pDexFile->GetProtoId(proto_idx);
+        value = pDexFile->GetProtoSignature(proto_id).ToString();
+        break;
+      }
+      case EncodedArrayValueIterator::ValueType::kMethodHandle:
+        type = "MethodHandle";
+        value = android::base::StringPrintf("%d", it.GetJavaValue().i);
+        break;
+      case EncodedArrayValueIterator::ValueType::kString: {
+        type = "String";
+        dex::StringIndex string_idx = static_cast<dex::StringIndex>(it.GetJavaValue().i);
+        value = pDexFile->StringDataByIdx(string_idx);
+        break;
+      }
+      case EncodedArrayValueIterator::ValueType::kType: {
+        type = "Class";
+        dex::TypeIndex type_idx = static_cast<dex::TypeIndex>(it.GetJavaValue().i);
+        const DexFile::ClassDef* class_def = pDexFile->FindClassDef(type_idx);
+        value = pDexFile->GetClassDescriptor(*class_def);
+        value = descriptorClassToDot(value.c_str()).get();
+        break;
+      }
+      case EncodedArrayValueIterator::ValueType::kField:
+      case EncodedArrayValueIterator::ValueType::kMethod:
+      case EncodedArrayValueIterator::ValueType::kEnum:
+      case EncodedArrayValueIterator::ValueType::kArray:
+      case EncodedArrayValueIterator::ValueType::kAnnotation:
+        // Unreachable based on current EncodedArrayValueIterator::Next().
+        UNIMPLEMENTED(FATAL) << " type " << type;
+        UNREACHABLE();
+        break;
+      case EncodedArrayValueIterator::ValueType::kNull:
+        type = "Null";
+        value = "null";
+        break;
+      case EncodedArrayValueIterator::ValueType::kBoolean:
+        type = "boolean";
+        value = it.GetJavaValue().z ? "true" : "false";
+        break;
+    }
+
+    if (gOptions.outputFormat == OUTPUT_PLAIN) {
+      fprintf(gOutFile, "  link_argument[%zu] : %s (%s)\n", argument, value.c_str(), type);
+    } else {
+      fprintf(gOutFile, "<link_argument index=\"%zu\" type=\"%s\" value=", argument, type);
+      dumpEscapedString(value.c_str());
+      fprintf(gOutFile, "/>\n");
+    }
+
+    it.Next();
+    argument++;
+  }
+
+  if (gOptions.outputFormat == OUTPUT_XML) {
+    fprintf(gOutFile, "</call_site>\n");
+  }
+}
+
 /*
  * Dumps the requested sections of the file.
  */
@@ -1612,6 +1808,16 @@
     dumpClass(pDexFile, i, &package);
   }  // for
 
+  // Iterate over all method handles.
+  for (u4 i = 0; i < pDexFile->NumMethodHandles(); ++i) {
+    dumpMethodHandle(pDexFile, i);
+  }  // for
+
+  // Iterate over all call site ids.
+  for (u4 i = 0; i < pDexFile->NumCallSiteIds(); ++i) {
+    dumpCallSite(pDexFile, i);
+  }  // for
+
   // Free the last package allocated.
   if (package != nullptr) {
     fprintf(gOutFile, "</package>\n");
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index b1e66be..43de342 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -588,11 +588,14 @@
   const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(&disk_code_item);
   DebugInfoItem* debug_info = nullptr;
   if (debug_info_stream != nullptr) {
-    uint32_t debug_info_size = GetDebugInfoStreamSize(debug_info_stream);
-    uint8_t* debug_info_buffer = new uint8_t[debug_info_size];
-    memcpy(debug_info_buffer, debug_info_stream, debug_info_size);
-    debug_info = new DebugInfoItem(debug_info_size, debug_info_buffer);
-    debug_info_items_.AddItem(debug_info, disk_code_item.debug_info_off_);
+    debug_info = debug_info_items_.GetExistingObject(disk_code_item.debug_info_off_);
+    if (debug_info == nullptr) {
+      uint32_t debug_info_size = GetDebugInfoStreamSize(debug_info_stream);
+      uint8_t* debug_info_buffer = new uint8_t[debug_info_size];
+      memcpy(debug_info_buffer, debug_info_stream, debug_info_size);
+      debug_info = new DebugInfoItem(debug_info_size, debug_info_buffer);
+      debug_info_items_.AddItem(debug_info, disk_code_item.debug_info_off_);
+    }
   }
 
   uint32_t insns_size = disk_code_item.insns_size_in_code_units_;
@@ -683,8 +686,8 @@
     const DexFile& dex_file, const uint8_t* encoded_data, uint32_t offset) {
   // Read the fields and methods defined by the class, resolving the circular reference from those
   // to classes by setting class at the same time.
-  ClassData* class_data = nullptr;
-  if (encoded_data != nullptr) {
+  ClassData* class_data = class_datas_.GetExistingObject(offset);
+  if (class_data == nullptr && encoded_data != nullptr) {
     ClassDataItemIterator cdii(dex_file, encoded_data);
     // Static fields.
     FieldItemVector* static_fields = new FieldItemVector();
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index e2ee940..3a5b644 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -134,9 +134,17 @@
  public:
   CollectionMap() = default;
 
+  // Returns the existing item if it is already inserted, null otherwise.
+  T* GetExistingObject(uint32_t offset) {
+    auto it = collection_.find(offset);
+    return it != collection_.end() ? it->second.get() : nullptr;
+  }
+
   void AddItem(T* object, uint32_t offset) {
     object->SetOffset(offset);
-    collection_.emplace(offset, std::unique_ptr<T>(object));
+    auto it = collection_.emplace(offset, std::unique_ptr<T>(object));
+    CHECK(it.second) << "CollectionMap already has an object with offset " << offset << " "
+                     << " and address " << it.first->second.get();
   }
   uint32_t Size() const { return collection_.size(); }
   std::map<uint32_t, std::unique_ptr<T>>& Collection() { return collection_; }
diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index 9f49ec6..ff05733 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -859,6 +859,22 @@
         has_modrm = true;
         store = true;
         break;
+      case 0x7F:
+        if (prefix[2] == 0x66) {
+          src_reg_file = dst_reg_file = SSE;
+          opcode1 = "movdqa";
+          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
+        } else if (prefix[0] == 0xF3) {
+          src_reg_file = dst_reg_file = SSE;
+          opcode1 = "movdqu";
+          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
+        } else {
+          dst_reg_file = MMX;
+          opcode1 = "movq";
+        }
+        store = true;
+        has_modrm = true;
+        break;
       case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
       case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: case 0x8F:
         opcode1 = "j";
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 147be4a..4413286 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -587,6 +587,7 @@
       kByteKindCodeInfoLocationCatalog,
       kByteKindCodeInfoDexRegisterMap,
       kByteKindCodeInfoEncoding,
+      kByteKindCodeInfoInvokeInfo,
       kByteKindCodeInfoStackMasks,
       kByteKindCodeInfoRegisterMasks,
       kByteKindStackMapNativePc,
@@ -637,6 +638,7 @@
         Dump(os, "CodeInfoDexRegisterMap          ", bits[kByteKindCodeInfoDexRegisterMap], sum);
         Dump(os, "CodeInfoStackMasks              ", bits[kByteKindCodeInfoStackMasks], sum);
         Dump(os, "CodeInfoRegisterMasks           ", bits[kByteKindCodeInfoRegisterMasks], sum);
+        Dump(os, "CodeInfoInvokeInfo              ", bits[kByteKindCodeInfoInvokeInfo], sum);
         // Stack map section.
         const int64_t stack_map_bits = std::accumulate(bits + kByteKindStackMapFirst,
                                                        bits + kByteKindStackMapLast + 1,
@@ -1473,7 +1475,7 @@
       Runtime* const runtime = Runtime::Current();
       Handle<mirror::DexCache> dex_cache(
           hs->NewHandle(runtime->GetClassLinker()->RegisterDexFile(*dex_file, nullptr)));
-      CHECK(dex_cache.Get() != nullptr);
+      CHECK(dex_cache != nullptr);
       DCHECK(options_.class_loader_ != nullptr);
       return verifier::MethodVerifier::VerifyMethodAndDump(
           soa.Self(), vios, dex_method_idx, dex_file, dex_cache, *options_.class_loader_,
@@ -1592,10 +1594,8 @@
         CodeInfoEncoding encoding(helper.GetEncoding());
         StackMapEncoding stack_map_encoding(encoding.stack_map.encoding);
         const size_t num_stack_maps = encoding.stack_map.num_entries;
-        std::vector<uint8_t> size_vector;
-        encoding.Compress(&size_vector);
         if (stats_.AddBitsIfUnique(Stats::kByteKindCodeInfoEncoding,
-                                   size_vector.size() * kBitsPerByte,
+                                   encoding.HeaderSize() * kBitsPerByte,
                                    oat_method.GetVmapTable())) {
           // Stack maps
           stats_.AddBits(
@@ -1627,6 +1627,13 @@
               Stats::kByteKindCodeInfoRegisterMasks,
               encoding.register_mask.encoding.BitSize() * encoding.register_mask.num_entries);
 
+          // Invoke infos
+          if (encoding.invoke_info.num_entries > 0u) {
+            stats_.AddBits(
+                Stats::kByteKindCodeInfoInvokeInfo,
+                encoding.invoke_info.encoding.BitSize() * encoding.invoke_info.num_entries);
+          }
+
           // Location catalog
           const size_t location_catalog_bytes =
               helper.GetCodeInfo().GetDexRegisterLocationCatalogSize(encoding);
@@ -2235,14 +2242,9 @@
           ScopedIndentation indent2(&state->vios_);
           auto* resolved_types = dex_cache->GetResolvedTypes();
           for (size_t i = 0; i < num_types; ++i) {
-            auto pair = resolved_types[i].load(std::memory_order_relaxed);
+            auto* elem = resolved_types[i].Read();
             size_t run = 0;
-            for (size_t j = i + 1; j != num_types; ++j) {
-              auto other_pair = resolved_types[j].load(std::memory_order_relaxed);
-              if (pair.index != other_pair.index ||
-                  pair.object.Read() != other_pair.object.Read()) {
-                break;
-              }
+            for (size_t j = i + 1; j != num_types && elem == resolved_types[j].Read(); ++j) {
               ++run;
             }
             if (run == 0) {
@@ -2252,13 +2254,12 @@
               i = i + run;
             }
             std::string msg;
-            auto* elem = pair.object.Read();
             if (elem == nullptr) {
               msg = "null";
             } else {
               msg = elem->PrettyClass();
             }
-            os << StringPrintf("%p   %u %s\n", elem, pair.index, msg.c_str());
+            os << StringPrintf("%p   %s\n", elem, msg.c_str());
           }
         }
       }
@@ -2956,7 +2957,7 @@
         const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
         const char* descriptor = dex_file->GetClassDescriptor(class_def);
         h_klass.Assign(class_linker->FindClass(self, descriptor, h_class_loader));
-        if (h_klass.Get() == nullptr) {
+        if (h_klass == nullptr) {
           std::cerr << "Warning: could not load " << descriptor << std::endl;
           continue;
         }
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 2546822..b9be5f2 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -643,8 +643,8 @@
     if (orig_strings != nullptr) {
       orig_dex_cache->FixupStrings(RelocatedCopyOf(orig_strings), RelocatedPointerVisitor(this));
     }
-    mirror::TypeDexCacheType* orig_types = orig_dex_cache->GetResolvedTypes();
-    mirror::TypeDexCacheType* relocated_types = RelocatedAddressOfPointer(orig_types);
+    GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes();
+    GcRoot<mirror::Class>* relocated_types = RelocatedAddressOfPointer(orig_types);
     copy_dex_cache->SetField64<false>(
         mirror::DexCache::ResolvedTypesOffset(),
         static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_types)));
@@ -688,6 +688,16 @@
       orig_dex_cache->FixupResolvedMethodTypes(RelocatedCopyOf(orig_method_types),
                                                RelocatedPointerVisitor(this));
     }
+
+    GcRoot<mirror::CallSite>* orig_call_sites = orig_dex_cache->GetResolvedCallSites();
+    GcRoot<mirror::CallSite>* relocated_call_sites = RelocatedAddressOfPointer(orig_call_sites);
+    copy_dex_cache->SetField64<false>(
+        mirror::DexCache::ResolvedCallSitesOffset(),
+        static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_call_sites)));
+    if (orig_call_sites != nullptr) {
+      orig_dex_cache->FixupResolvedCallSites(RelocatedCopyOf(orig_call_sites),
+                                             RelocatedPointerVisitor(this));
+    }
   }
 }
 
diff --git a/profman/profman.cc b/profman/profman.cc
index f6b145a..8f35a76 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -216,61 +216,32 @@
       }
     }
 
-    bool has_profiles = !profile_files_.empty() || !profile_files_fd_.empty();
-    bool has_reference_profile = !reference_profile_file_.empty() ||
-        FdIsValid(reference_profile_file_fd_);
-
-    if (!test_profile_.empty()) {
-      if (test_profile_method_ratio_ > 100) {
-        Usage("Invalid ratio for --generate-test-profile-method-ratio");
-      }
-      if (test_profile_class_ratio_ > 100) {
-        Usage("Invalid ratio for --generate-test-profile-class-ratio");
-      }
-      return;
-    }
-    if (!apk_files_.empty() && !apks_fd_.empty()) {
-      Usage("APK files should not be specified with both --apk-fd and --apk");
-    }
-    if (!create_profile_from_file_.empty()) {
-      if (apk_files_.empty() && apks_fd_.empty()) {
-        Usage("APK files must be specified");
-      }
-      if (dex_locations_.empty()) {
-        Usage("DEX locations must be specified");
-      }
-      if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
-        Usage("Reference profile must be specified with --reference-profile-file or "
-              "--reference-profile-file-fd");
-      }
-      if (has_profiles) {
-        Usage("Profile must be specified with --reference-profile-file or "
-              "--reference-profile-file-fd");
-      }
-      return;
-    }
-    // --dump-only and --dump-classes may be specified with only --reference-profiles present.
-    if (!dump_only_ && !dump_classes_ && !has_profiles) {
-      Usage("No profile files specified.");
-    }
+    // Validate global consistency between file/fd options.
     if (!profile_files_.empty() && !profile_files_fd_.empty()) {
       Usage("Profile files should not be specified with both --profile-file-fd and --profile-file");
     }
-    if (!dump_only_ && !dump_classes_ && !has_reference_profile) {
-      Usage("No reference profile file specified.");
-    }
     if (!reference_profile_file_.empty() && FdIsValid(reference_profile_file_fd_)) {
       Usage("Reference profile should not be specified with both "
             "--reference-profile-file-fd and --reference-profile-file");
     }
-    if ((!profile_files_.empty() && FdIsValid(reference_profile_file_fd_)) ||
-        (!dump_only_ && !profile_files_fd_.empty() && !FdIsValid(reference_profile_file_fd_))) {
-      Usage("Options --profile-file-fd and --reference-profile-file-fd "
-            "should only be used together");
+    if (!apk_files_.empty() && !apks_fd_.empty()) {
+      Usage("APK files should not be specified with both --apk-fd and --apk");
     }
   }
 
   ProfileAssistant::ProcessingResult ProcessProfiles() {
+    // Validate that at least one profile file was passed, as well as a reference profile.
+    if (profile_files_.empty() && profile_files_fd_.empty()) {
+      Usage("No profile files specified.");
+    }
+    if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
+      Usage("No reference profile file specified.");
+    }
+    if ((!profile_files_.empty() && FdIsValid(reference_profile_file_fd_)) ||
+        (!profile_files_fd_.empty() && !FdIsValid(reference_profile_file_fd_))) {
+      Usage("Options --profile-file-fd and --reference-profile-file-fd "
+            "should only be used together");
+    }
     ProfileAssistant::ProcessingResult result;
     if (profile_files_.empty()) {
       // The file doesn't need to be flushed here (ProcessProfiles will do it)
@@ -287,11 +258,15 @@
   void OpenApkFilesFromLocations(std::vector<std::unique_ptr<const DexFile>>* dex_files) {
     bool use_apk_fd_list = !apks_fd_.empty();
     if (use_apk_fd_list) {
-      CHECK(apk_files_.empty());
+      // Get the APKs from the collection of FDs.
       CHECK_EQ(dex_locations_.size(), apks_fd_.size());
-    } else {
+    } else if (!apk_files_.empty()) {
+      // Get the APKs from the collection of filenames.
       CHECK_EQ(dex_locations_.size(), apk_files_.size());
-      CHECK(!apk_files_.empty());
+    } else {
+      // No APKs were specified.
+      CHECK(dex_locations_.empty());
+      return;
     }
     static constexpr bool kVerifyChecksum = true;
     for (size_t i = 0; i < dex_locations_.size(); ++i) {
@@ -350,6 +325,11 @@
   }
 
   int DumpProfileInfo() {
+    // Validate that at least one profile file or reference was specified.
+    if (profile_files_.empty() && profile_files_fd_.empty() &&
+        reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
+      Usage("No profile files or reference profile specified.");
+    }
     static const char* kEmptyString = "";
     static const char* kOrdinaryProfile = "=== profile ===";
     static const char* kReferenceProfile = "=== reference profile ===";
@@ -446,6 +426,11 @@
   }
 
   int DumpClasses() {
+    // Validate that at least one profile file or reference was specified.
+    if (profile_files_.empty() && profile_files_fd_.empty() &&
+        reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
+      Usage("No profile files or reference profile specified.");
+    }
     // Open apk/zip files and and read dex files.
     MemMap::Init();  // for ZipArchive::OpenFromFd
     // Open the dex files to get the names for classes.
@@ -538,7 +523,23 @@
   }
 
   int CreateProfile() {
-    MemMap::Init();  // for ZipArchive::OpenFromFd
+    // Validate parameters for this command.
+    if (apk_files_.empty() && apks_fd_.empty()) {
+      Usage("APK files must be specified");
+    }
+    if (dex_locations_.empty()) {
+      Usage("DEX locations must be specified");
+    }
+    if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
+      Usage("Reference profile must be specified with --reference-profile-file or "
+            "--reference-profile-file-fd");
+    }
+    if (!profile_files_.empty() || !profile_files_fd_.empty()) {
+      Usage("Profile must be specified with --reference-profile-file or "
+            "--reference-profile-file-fd");
+    }
+    // for ZipArchive::OpenFromFd
+    MemMap::Init();
     // Open the profile output file if needed.
     int fd = reference_profile_file_fd_;
     if (!FdIsValid(fd)) {
@@ -607,6 +608,14 @@
   }
 
   int GenerateTestProfile() {
+    // Validate parameters for this command.
+    if (test_profile_method_ratio_ > 100) {
+      Usage("Invalid ratio for --generate-test-profile-method-ratio");
+    }
+    if (test_profile_class_ratio_ > 100) {
+      Usage("Invalid ratio for --generate-test-profile-class-ratio");
+    }
+    // ShouldGenerateTestProfile confirms !test_profile_.empty().
     int profile_test_fd = open(test_profile_.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
     if (profile_test_fd < 0) {
       LOG(ERROR) << "Cannot open " << test_profile_ << strerror(errno);
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 9585ba2..d3a81a9 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -123,6 +123,7 @@
         "memory_region.cc",
         "method_handles.cc",
         "mirror/array.cc",
+        "mirror/call_site.cc",
         "mirror/class.cc",
         "mirror/class_ext.cc",
         "mirror/dex_cache.cc",
@@ -131,6 +132,7 @@
         "mirror/field.cc",
         "mirror/method.cc",
         "mirror/method_handle_impl.cc",
+        "mirror/method_handles_lookup.cc",
         "mirror/method_type.cc",
         "mirror/object.cc",
         "mirror/reference.cc",
@@ -546,6 +548,7 @@
         "gc/reference_queue_test.cc",
         "gc/space/dlmalloc_space_static_test.cc",
         "gc/space/dlmalloc_space_random_test.cc",
+        "gc/space/image_space_test.cc",
         "gc/space/large_object_space_test.cc",
         "gc/space/rosalloc_space_static_test.cc",
         "gc/space/rosalloc_space_random_test.cc",
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index cfe8406..8531091 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -2049,9 +2049,13 @@
 
 .Lnot_marked_rb_\name:
     // Test that both the forwarding state bits are 1.
-    mvn ip, ip
-    tst ip, #(LOCK_WORD_STATE_FORWARDING_ADDRESS << LOCK_WORD_STATE_SHIFT)
-    beq .Lret_forwarding_address\name
+#if (LOCK_WORD_STATE_SHIFT != 30) || (LOCK_WORD_STATE_FORWARDING_ADDRESS != 3)
+    // To use "CMP ip, #modified-immediate; BHS", we need the lock word state in
+    // the highest bits and the "forwarding address" state to have all bits set.
+#error "Unexpected lock word state shift or forwarding address state value."
+#endif
+    cmp ip, #(LOCK_WORD_STATE_FORWARDING_ADDRESS << LOCK_WORD_STATE_SHIFT)
+    bhs .Lret_forwarding_address\name
 
 .Lslow_rb_\name:
     // Save IP: The kSaveEverything entrypoint art_quick_resolve_string used to
@@ -2118,7 +2122,6 @@
 .Lret_forwarding_address\name:
     // Shift left by the forwarding address shift. This clears out the state bits since they are
     // in the top 2 bits of the lock word.
-    mvn ip, ip
     lsl \reg, ip, #LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT
     bx lr
 END \name
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 0bf08a6..207bf9d 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -984,7 +984,7 @@
     while (length > 10) {
       Handle<mirror::Object> h(hsp->NewHandle<mirror::Object>(
           mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), ca.Get(), length / 4)));
-      if (self->IsExceptionPending() || h.Get() == nullptr) {
+      if (self->IsExceptionPending() || h == nullptr) {
         self->ClearException();
 
         // Try a smaller length
@@ -1003,7 +1003,7 @@
     // Allocate simple objects till it fails.
     while (!self->IsExceptionPending()) {
       Handle<mirror::Object> h = hsp->NewHandle(c->AllocObject(soa.Self()));
-      if (!self->IsExceptionPending() && h.Get() != nullptr) {
+      if (!self->IsExceptionPending() && h != nullptr) {
         handles.push_back(h);
       }
     }
diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h
index 16b73c6..80af8e7 100644
--- a/runtime/art_field-inl.h
+++ b/runtime/art_field-inl.h
@@ -311,8 +311,6 @@
 
 template <bool kResolve>
 inline ObjPtr<mirror::Class> ArtField::GetType() {
-  // TODO: Refactor this function into two functions, ResolveType() and LookupType()
-  // so that we can properly annotate it with no-suspension possible / suspension possible.
   const uint32_t field_index = GetDexFieldIndex();
   ObjPtr<mirror::Class> declaring_class = GetDeclaringClass();
   if (UNLIKELY(declaring_class->IsProxyClass())) {
@@ -322,16 +320,9 @@
   const DexFile* const dex_file = dex_cache->GetDexFile();
   const DexFile::FieldId& field_id = dex_file->GetFieldId(field_index);
   ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(field_id.type_idx_);
-  if (UNLIKELY(type == nullptr)) {
-    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    if (kResolve) {
-      type = class_linker->ResolveType(*dex_file, field_id.type_idx_, declaring_class);
-      CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
-    } else {
-      type = class_linker->LookupResolvedType(
-          *dex_file, field_id.type_idx_, dex_cache, declaring_class->GetClassLoader());
-      DCHECK(!Thread::Current()->IsExceptionPending());
-    }
+  if (kResolve && UNLIKELY(type == nullptr)) {
+    type = ResolveGetType(field_id.type_idx_);
+    CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
   }
   return type;
 }
diff --git a/runtime/art_field.cc b/runtime/art_field.cc
index 7e13104..a4a6e5a 100644
--- a/runtime/art_field.cc
+++ b/runtime/art_field.cc
@@ -48,6 +48,10 @@
   return Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), descriptor);
 }
 
+ObjPtr<mirror::Class> ArtField::ResolveGetType(dex::TypeIndex type_idx) {
+  return Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this);
+}
+
 ObjPtr<mirror::String> ArtField::ResolveGetStringName(Thread* self,
                                                       const DexFile& dex_file,
                                                       dex::StringIndex string_idx,
diff --git a/runtime/art_field.h b/runtime/art_field.h
index 75dd981..427e103 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -217,6 +217,8 @@
  private:
   ObjPtr<mirror::Class> ProxyFindSystemClass(const char* descriptor)
       REQUIRES_SHARED(Locks::mutator_lock_);
+  ObjPtr<mirror::Class> ResolveGetType(dex::TypeIndex type_idx)
+      REQUIRES_SHARED(Locks::mutator_lock_);
   ObjPtr<mirror::String> ResolveGetStringName(Thread* self,
                                               const DexFile& dex_file,
                                               dex::StringIndex string_idx,
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 473d9cf..950f1aa 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -175,19 +175,12 @@
 }
 
 inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) {
-  // TODO: Refactor this function into two functions, Resolve...() and Lookup...()
-  // so that we can properly annotate it with no-suspension possible / suspension possible.
   ObjPtr<mirror::DexCache> dex_cache = GetDexCache();
   ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
-  if (UNLIKELY(type == nullptr)) {
+  if (UNLIKELY(type == nullptr) && resolve) {
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    if (resolve) {
-      type = class_linker->ResolveType(type_idx, this);
-      CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
-    } else {
-      type = class_linker->LookupResolvedType(
-          *dex_cache->GetDexFile(), type_idx, dex_cache, GetClassLoader());
-    }
+    type = class_linker->ResolveType(type_idx, this);
+    CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
   }
   return type.Ptr();
 }
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 4902ad4..9d74e7c 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -274,7 +274,7 @@
     *has_no_move_exception = (first_catch_instr->Opcode() != Instruction::MOVE_EXCEPTION);
   }
   // Put the exception back.
-  if (exception.Get() != nullptr) {
+  if (exception != nullptr) {
     self->SetException(exception.Get());
   }
   return found_dex_pc;
@@ -441,12 +441,56 @@
   UNREACHABLE();
 }
 
+// We use the method's DexFile and declaring class name to find the OatMethod for an obsolete
+// method.  This is extremely slow but we need it if we want to be able to have obsolete native
+// methods since we need this to find the size of its stack frames.
+//
+// NB We could (potentially) do this differently and rely on the way the transformation is applied
+// in order to use the entrypoint to find this information. However, for debugging reasons (most
+// notably making sure that new invokes of obsolete methods fail) we choose to instead get the data
+// directly from the dex file.
+static const OatFile::OatMethod FindOatMethodFromDexFileFor(ArtMethod* method, bool* found)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK(method->IsObsolete() && method->IsNative());
+  const DexFile* dex_file = method->GetDexFile();
+
+  // recreate the class_def_index from the descriptor.
+  std::string descriptor_storage;
+  const DexFile::TypeId* declaring_class_type_id =
+      dex_file->FindTypeId(method->GetDeclaringClass()->GetDescriptor(&descriptor_storage));
+  CHECK(declaring_class_type_id != nullptr);
+  dex::TypeIndex declaring_class_type_index = dex_file->GetIndexForTypeId(*declaring_class_type_id);
+  const DexFile::ClassDef* declaring_class_type_def =
+      dex_file->FindClassDef(declaring_class_type_index);
+  CHECK(declaring_class_type_def != nullptr);
+  uint16_t declaring_class_def_index = dex_file->GetIndexForClassDef(*declaring_class_type_def);
+
+  size_t oat_method_index = GetOatMethodIndexFromMethodIndex(*dex_file,
+                                                             declaring_class_def_index,
+                                                             method->GetDexMethodIndex());
+
+  OatFile::OatClass oat_class = OatFile::FindOatClass(*dex_file,
+                                                      declaring_class_def_index,
+                                                      found);
+  if (!(*found)) {
+    return OatFile::OatMethod::Invalid();
+  }
+  return oat_class.GetOatMethod(oat_method_index);
+}
+
 static const OatFile::OatMethod FindOatMethodFor(ArtMethod* method,
                                                  PointerSize pointer_size,
                                                  bool* found)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  // We shouldn't be calling this with obsolete methods.
-  DCHECK(!method->IsObsolete());
+  if (UNLIKELY(method->IsObsolete())) {
+    // We shouldn't be calling this with obsolete methods except for native obsolete methods for
+    // which we need to use the oat method to figure out how large the quick frame is.
+    DCHECK(method->IsNative()) << "We should only be finding the OatMethod of obsolete methods in "
+                               << "order to allow stack walking. Other obsolete methods should "
+                               << "never need to access this information.";
+    DCHECK_EQ(pointer_size, kRuntimePointerSize) << "Obsolete method in compiler!";
+    return FindOatMethodFromDexFileFor(method, found);
+  }
   // Although we overwrite the trampoline of non-static methods, we may get here via the resolution
   // method for direct methods (or virtual methods made direct).
   mirror::Class* declaring_class = method->GetDeclaringClass();
@@ -489,7 +533,7 @@
   const auto& proto_id = dex_file->GetMethodPrototype(method_id);
   const DexFile::TypeList* proto_params = dex_file->GetProtoParameters(proto_id);
   auto count = proto_params != nullptr ? proto_params->Size() : 0u;
-  auto param_len = params.Get() != nullptr ? params->GetLength() : 0u;
+  auto param_len = params != nullptr ? params->GetLength() : 0u;
   if (param_len != count) {
     return false;
   }
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 3836303..3d51fdd 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -432,6 +432,7 @@
   }
 
   ProfilingInfo* GetProfilingInfo(PointerSize pointer_size) {
+    DCHECK(!IsNative());
     return reinterpret_cast<ProfilingInfo*>(GetDataPtrSize(pointer_size));
   }
 
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index c7a94a9..4a2e34f 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -246,7 +246,7 @@
 ADD_TEST_EQ(MIRROR_STRING_VALUE_OFFSET, art::mirror::String::ValueOffset().Int32Value())
 
 // String compression feature.
-#define STRING_COMPRESSION_FEATURE 0
+#define STRING_COMPRESSION_FEATURE 1
 ADD_TEST_EQ(STRING_COMPRESSION_FEATURE, art::mirror::kUseStringCompression);
 
 #if defined(__cplusplus)
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 7bba944..b93b293 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -1167,6 +1167,8 @@
     expected_mutexes_on_weak_ref_access_.push_back(dex_lock_);
     classlinker_classes_lock_->SetShouldRespondToEmptyCheckpointRequest(true);
     expected_mutexes_on_weak_ref_access_.push_back(classlinker_classes_lock_);
+    jni_libraries_lock_->SetShouldRespondToEmptyCheckpointRequest(true);
+    expected_mutexes_on_weak_ref_access_.push_back(jni_libraries_lock_);
 
     InitConditions();
   }
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index bd510ca..3438810 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -78,18 +78,6 @@
   return string.Ptr();
 }
 
-inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(
-    dex::TypeIndex type_idx,
-    ObjPtr<mirror::DexCache> dex_cache,
-    ObjPtr<mirror::ClassLoader> class_loader) {
-  ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
-  if (type == nullptr) {
-    type = Runtime::Current()->GetClassLinker()->LookupResolvedType(
-        *dex_cache->GetDexFile(), type_idx, dex_cache, class_loader);
-  }
-  return type;
-}
-
 inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) {
   Thread::PoisonObjectPointersIfDebug();
   if (kIsDebugBuild) {
@@ -103,6 +91,25 @@
     Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
     const DexFile& dex_file = *dex_cache->GetDexFile();
     resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader);
+    // Note: We cannot check here to see whether we added the type to the cache. The type
+    //       might be an erroneous class, which results in it being hidden from us.
+  }
+  return resolved_type.Ptr();
+}
+
+inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtField* referrer) {
+  Thread::PoisonObjectPointersIfDebug();
+  ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
+  ObjPtr<mirror::DexCache> dex_cache_ptr = declaring_class->GetDexCache();
+  ObjPtr<mirror::Class> resolved_type = dex_cache_ptr->GetResolvedType(type_idx);
+  if (UNLIKELY(resolved_type == nullptr)) {
+    StackHandleScope<2> hs(Thread::Current());
+    Handle<mirror::DexCache> dex_cache(hs.NewHandle(dex_cache_ptr));
+    Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
+    const DexFile& dex_file = *dex_cache->GetDexFile();
+    resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader);
+    // Note: We cannot check here to see whether we added the type to the cache. The type
+    //       might be an erroneous class, which results in it being hidden from us.
   }
   return resolved_type.Ptr();
 }
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9380588..d02cf17 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -70,6 +70,7 @@
 #include "jni_internal.h"
 #include "leb128.h"
 #include "linear_alloc.h"
+#include "mirror/call_site.h"
 #include "mirror/class.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_ext.h"
@@ -82,6 +83,7 @@
 #include "mirror/method.h"
 #include "mirror/method_type.h"
 #include "mirror/method_handle_impl.h"
+#include "mirror/method_handles_lookup.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/proxy.h"
@@ -405,7 +407,7 @@
   auto class_class_size = mirror::Class::ClassClassSize(image_pointer_size_);
   Handle<mirror::Class> java_lang_Class(hs.NewHandle(down_cast<mirror::Class*>(
       heap->AllocNonMovableObject<true>(self, nullptr, class_class_size, VoidFunctor()))));
-  CHECK(java_lang_Class.Get() != nullptr);
+  CHECK(java_lang_Class != nullptr);
   mirror::Class::SetClassClass(java_lang_Class.Get());
   java_lang_Class->SetClass(java_lang_Class.Get());
   if (kUseBakerReadBarrier) {
@@ -425,7 +427,7 @@
   // java_lang_Object comes next so that object_array_class can be created.
   Handle<mirror::Class> java_lang_Object(hs.NewHandle(
       AllocClass(self, java_lang_Class.Get(), mirror::Object::ClassSize(image_pointer_size_))));
-  CHECK(java_lang_Object.Get() != nullptr);
+  CHECK(java_lang_Object != nullptr);
   // backfill Object as the super class of Class.
   java_lang_Class->SetSuperClass(java_lang_Object.Get());
   mirror::Class::SetStatus(java_lang_Object, mirror::Class::kStatusLoaded, self);
@@ -624,9 +626,9 @@
 
   // Setup the single, global copy of "iftable".
   auto java_lang_Cloneable = hs.NewHandle(FindSystemClass(self, "Ljava/lang/Cloneable;"));
-  CHECK(java_lang_Cloneable.Get() != nullptr);
+  CHECK(java_lang_Cloneable != nullptr);
   auto java_io_Serializable = hs.NewHandle(FindSystemClass(self, "Ljava/io/Serializable;"));
-  CHECK(java_io_Serializable.Get() != nullptr);
+  CHECK(java_io_Serializable != nullptr);
   // We assume that Cloneable/Serializable don't have superinterfaces -- normally we'd have to
   // crawl up and explicitly list all of the supers as well.
   array_iftable_.Read()->SetInterface(0, java_lang_Cloneable.Get());
@@ -695,6 +697,18 @@
   SetClassRoot(kJavaLangInvokeMethodHandleImpl, class_root);
   mirror::MethodHandleImpl::SetClass(class_root);
 
+  // Create java.lang.invoke.MethodHandles.Lookup.class root
+  class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodHandles$Lookup;");
+  CHECK(class_root != nullptr);
+  SetClassRoot(kJavaLangInvokeMethodHandlesLookup, class_root);
+  mirror::MethodHandlesLookup::SetClass(class_root);
+
+  // Create java.lang.invoke.CallSite.class root
+  class_root = FindSystemClass(self, "Ljava/lang/invoke/CallSite;");
+  CHECK(class_root != nullptr);
+  SetClassRoot(kJavaLangInvokeCallSite, class_root);
+  mirror::CallSite::SetClass(class_root);
+
   class_root = FindSystemClass(self, "Ldalvik/system/EmulatedStackFrame;");
   CHECK(class_root != nullptr);
   SetClassRoot(kDalvikSystemEmulatedStackFrame, class_root);
@@ -981,6 +995,8 @@
   mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass));
   mirror::MethodType::SetClass(GetClassRoot(kJavaLangInvokeMethodType));
   mirror::MethodHandleImpl::SetClass(GetClassRoot(kJavaLangInvokeMethodHandleImpl));
+  mirror::MethodHandlesLookup::SetClass(GetClassRoot(kJavaLangInvokeMethodHandlesLookup));
+  mirror::CallSite::SetClass(GetClassRoot(kJavaLangInvokeCallSite));
   mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference));
   mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass));
   mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
@@ -1171,23 +1187,6 @@
   }
 }
 
-template <typename T>
-static void CopyDexCachePairs(const std::atomic<mirror::DexCachePair<T>>* src,
-                              size_t count,
-                              std::atomic<mirror::DexCachePair<T>>* dst) {
-  DCHECK_NE(count, 0u);
-  DCHECK(!src[0].load(std::memory_order_relaxed).object.IsNull() ||
-         src[0].load(std::memory_order_relaxed).index != 0u);
-  for (size_t i = 0; i < count; ++i) {
-    DCHECK_EQ(dst[i].load(std::memory_order_relaxed).index, 0u);
-    DCHECK(dst[i].load(std::memory_order_relaxed).object.IsNull());
-    mirror::DexCachePair<T> source = src[i].load(std::memory_order_relaxed);
-    if (source.index != 0u || !source.object.IsNull()) {
-      dst[i].store(source, std::memory_order_relaxed);
-    }
-  }
-}
-
 bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
     gc::space::ImageSpace* space,
     Handle<mirror::ClassLoader> class_loader,
@@ -1241,36 +1240,48 @@
         if (dex_file->NumStringIds() < num_strings) {
           num_strings = dex_file->NumStringIds();
         }
-        size_t num_types = mirror::DexCache::kDexCacheTypeCacheSize;
-        if (dex_file->NumTypeIds() < num_types) {
-          num_types = dex_file->NumTypeIds();
-        }
+        const size_t num_types = dex_file->NumTypeIds();
         const size_t num_methods = dex_file->NumMethodIds();
         const size_t num_fields = dex_file->NumFieldIds();
         size_t num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize;
         if (dex_file->NumProtoIds() < num_method_types) {
           num_method_types = dex_file->NumProtoIds();
         }
-
+        const size_t num_call_sites = dex_file->NumCallSiteIds();
         CHECK_EQ(num_strings, dex_cache->NumStrings());
         CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
         CHECK_EQ(num_methods, dex_cache->NumResolvedMethods());
         CHECK_EQ(num_fields, dex_cache->NumResolvedFields());
         CHECK_EQ(num_method_types, dex_cache->NumResolvedMethodTypes());
+        CHECK_EQ(num_call_sites, dex_cache->NumResolvedCallSites());
         DexCacheArraysLayout layout(image_pointer_size_, dex_file);
         uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays();
         if (num_strings != 0u) {
           mirror::StringDexCacheType* const image_resolved_strings = dex_cache->GetStrings();
           mirror::StringDexCacheType* const strings =
               reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset());
-          CopyDexCachePairs(image_resolved_strings, num_strings, strings);
+          for (size_t j = 0; j < num_strings; ++j) {
+            DCHECK_EQ(strings[j].load(std::memory_order_relaxed).index, 0u);
+            DCHECK(strings[j].load(std::memory_order_relaxed).object.IsNull());
+            strings[j].store(image_resolved_strings[j].load(std::memory_order_relaxed),
+                             std::memory_order_relaxed);
+          }
+          mirror::StringDexCachePair::Initialize(strings);
           dex_cache->SetStrings(strings);
         }
         if (num_types != 0u) {
-          mirror::TypeDexCacheType* const image_resolved_types = dex_cache->GetResolvedTypes();
-          mirror::TypeDexCacheType* const types =
-              reinterpret_cast<mirror::TypeDexCacheType*>(raw_arrays + layout.TypesOffset());
-          CopyDexCachePairs(image_resolved_types, num_types, types);
+          GcRoot<mirror::Class>* const image_resolved_types = dex_cache->GetResolvedTypes();
+          GcRoot<mirror::Class>* const types =
+              reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset());
+          for (size_t j = 0; kIsDebugBuild && j < num_types; ++j) {
+            DCHECK(types[j].IsNull());
+          }
+          CopyNonNull(image_resolved_types,
+                      num_types,
+                      types,
+                      [](const GcRoot<mirror::Class>& elem) {
+                          return elem.IsNull();
+                      });
           dex_cache->SetResolvedTypes(types);
         }
         if (num_methods != 0u) {
@@ -1311,9 +1322,33 @@
           mirror::MethodTypeDexCacheType* const method_types =
               reinterpret_cast<mirror::MethodTypeDexCacheType*>(
                   raw_arrays + layout.MethodTypesOffset());
-          CopyDexCachePairs(image_resolved_method_types, num_method_types, method_types);
+          for (size_t j = 0; j < num_method_types; ++j) {
+            DCHECK_EQ(method_types[j].load(std::memory_order_relaxed).index, 0u);
+            DCHECK(method_types[j].load(std::memory_order_relaxed).object.IsNull());
+            method_types[j].store(
+                image_resolved_method_types[j].load(std::memory_order_relaxed),
+                std::memory_order_relaxed);
+          }
+
+          mirror::MethodTypeDexCachePair::Initialize(method_types);
           dex_cache->SetResolvedMethodTypes(method_types);
         }
+        if (num_call_sites != 0u) {
+          GcRoot<mirror::CallSite>* const image_resolved_call_sites =
+              dex_cache->GetResolvedCallSites();
+          GcRoot<mirror::CallSite>* const call_sites =
+              reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset());
+          for (size_t j = 0; kIsDebugBuild && j < num_call_sites; ++j) {
+            DCHECK(call_sites[j].IsNull());
+          }
+          CopyNonNull(image_resolved_call_sites,
+                      num_call_sites,
+                      call_sites,
+                      [](const GcRoot<mirror::CallSite>& elem) {
+                          return elem.IsNull();
+                      });
+          dex_cache->SetResolvedCallSites(call_sites);
+        }
       }
       {
         WriterMutexLock mu2(self, *Locks::dex_lock_);
@@ -1325,11 +1360,11 @@
       }
       if (kIsDebugBuild) {
         CHECK(new_class_set != nullptr);
-        mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
+        GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes();
         const size_t num_types = dex_cache->NumResolvedTypes();
-        for (size_t j = 0; j != num_types; ++j) {
+        for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) {
           // The image space is not yet added to the heap, avoid read barriers.
-          ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read();
+          ObjPtr<mirror::Class> klass = types[j].Read();
           if (space->HasAddress(klass.Ptr())) {
             DCHECK(!klass->IsErroneous()) << klass->GetStatus();
             auto it = new_class_set->Find(ClassTable::TableSlot(klass));
@@ -1613,7 +1648,7 @@
   DCHECK(out_dex_files != nullptr);
   DCHECK(error_msg != nullptr);
   const uint64_t start_time = NanoTime();
-  const bool app_image = class_loader.Get() != nullptr;
+  const bool app_image = class_loader != nullptr;
   const ImageHeader& header = space->GetImageHeader();
   ObjPtr<mirror::Object> dex_caches_object = header.GetImageRoot(ImageHeader::kDexCaches);
   DCHECK(dex_caches_object != nullptr);
@@ -1643,7 +1678,7 @@
                 "Class loader should be the last image root.");
   MutableHandle<mirror::ClassLoader> image_class_loader(hs.NewHandle(
       app_image ? header.GetImageRoot(ImageHeader::kClassLoader)->AsClassLoader() : nullptr));
-  DCHECK(class_roots.Get() != nullptr);
+  DCHECK(class_roots != nullptr);
   if (class_roots->GetLength() != static_cast<int32_t>(kClassRootsMax)) {
     *error_msg = StringPrintf("Expected %d class roots but got %d",
                               class_roots->GetLength(),
@@ -1688,9 +1723,9 @@
       // The current dex file field is bogus, overwrite it so that we can get the dex file in the
       // loop below.
       dex_cache->SetDexFile(dex_file.get());
-      mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
+      GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes();
       for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
-        ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read();
+        ObjPtr<mirror::Class> klass = types[j].Read();
         if (klass != nullptr) {
           DCHECK(!klass->IsErroneous()) << klass->GetStatus();
         }
@@ -2072,7 +2107,7 @@
       ObjPtr<mirror::Class> array_of_class = FindArrayClass(self, &class_type);
       classes.Assign(
           mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, class_table_size));
-      CHECK(classes.Get() != nullptr);  // OOME.
+      CHECK(classes != nullptr);  // OOME.
       GetClassInToObjectArray accumulator(classes.Get());
       VisitClasses(&accumulator);
       if (accumulator.Succeeded()) {
@@ -2113,6 +2148,8 @@
   mirror::ShortArray::ResetArrayClass();
   mirror::MethodType::ResetClass();
   mirror::MethodHandleImpl::ResetClass();
+  mirror::MethodHandlesLookup::ResetClass();
+  mirror::CallSite::ResetClass();
   mirror::EmulatedStackFrame::ResetClass();
   Thread* const self = Thread::Current();
   for (const ClassLoaderData& data : class_loaders_) {
@@ -2150,7 +2187,7 @@
   DCHECK(out_location != nullptr);
   auto dex_cache(hs.NewHandle(ObjPtr<mirror::DexCache>::DownCast(
       GetClassRoot(kJavaLangDexCache)->AllocObject(self))));
-  if (dex_cache.Get() == nullptr) {
+  if (dex_cache == nullptr) {
     self->AssertPendingOOMException();
     return nullptr;
   }
@@ -2451,7 +2488,7 @@
     return EnsureResolved(self, descriptor, klass);
   }
   // Class is not yet loaded.
-  if (descriptor[0] != '[' && class_loader.Get() == nullptr) {
+  if (descriptor[0] != '[' && class_loader == nullptr) {
     // Non-array class and the boot class loader, search the boot class path.
     ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
     if (pair.second != nullptr) {
@@ -2614,14 +2651,14 @@
     }
   }
 
-  if (klass.Get() == nullptr) {
+  if (klass == nullptr) {
     // Allocate a class with the status of not ready.
     // Interface object should get the right size here. Regular class will
     // figure out the right size later and be replaced with one of the right
     // size when the class becomes resolved.
     klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
   }
-  if (UNLIKELY(klass.Get() == nullptr)) {
+  if (UNLIKELY(klass == nullptr)) {
     self->AssertPendingOOMException();
     return nullptr;
   }
@@ -2714,7 +2751,7 @@
     return nullptr;
   }
   self->AssertNoPendingException();
-  CHECK(h_new_class.Get() != nullptr) << descriptor;
+  CHECK(h_new_class != nullptr) << descriptor;
   CHECK(h_new_class->IsResolved() && !h_new_class->IsErroneousResolved()) << descriptor;
 
   // Instrumentation may have updated entrypoints for all methods of all
@@ -2995,7 +3032,7 @@
                              const DexFile::ClassDef& dex_class_def,
                              Handle<mirror::Class> klass,
                              ObjPtr<mirror::ClassLoader> class_loader) {
-  CHECK(klass.Get() != nullptr);
+  CHECK(klass != nullptr);
   CHECK(klass->GetDexCache() != nullptr);
   CHECK_EQ(mirror::Class::kStatusNotReady, klass->GetStatus());
   const char* descriptor = dex_file.GetClassDescriptor(dex_class_def);
@@ -3110,6 +3147,7 @@
         last_field_idx = field_idx;
       }
     }
+
     // Load instance fields.
     LengthPrefixedArray<ArtField>* ifields = AllocArtFieldArray(self,
                                                                 allocator,
@@ -3126,6 +3164,7 @@
         last_field_idx = field_idx;
       }
     }
+
     if (UNLIKELY(num_sfields != it.NumStaticFields()) ||
         UNLIKELY(num_ifields != it.NumInstanceFields())) {
       LOG(WARNING) << "Duplicate fields in class " << klass->PrettyDescriptor()
@@ -3365,7 +3404,7 @@
     WriterMutexLock mu(self, *Locks::dex_lock_);
     old_data = FindDexCacheDataLocked(dex_file);
     old_dex_cache = DecodeDexCache(self, old_data);
-    if (old_dex_cache == nullptr && h_dex_cache.Get() != nullptr) {
+    if (old_dex_cache == nullptr && h_dex_cache != nullptr) {
       // Do InitializeDexCache while holding dex lock to make sure two threads don't call it at the
       // same time with the same dex cache. Since the .bss is shared this can cause failing DCHECK
       // that the arrays are null.
@@ -3381,12 +3420,12 @@
   if (old_dex_cache != nullptr) {
     // Another thread managed to initialize the dex cache faster, so use that DexCache.
     // If this thread encountered OOME, ignore it.
-    DCHECK_EQ(h_dex_cache.Get() == nullptr, self->IsExceptionPending());
+    DCHECK_EQ(h_dex_cache == nullptr, self->IsExceptionPending());
     self->ClearException();
     // We cannot call EnsureSameClassLoader() while holding the dex_lock_.
     return EnsureSameClassLoader(self, old_dex_cache, old_data, h_class_loader.Get());
   }
-  if (h_dex_cache.Get() == nullptr) {
+  if (h_dex_cache == nullptr) {
     self->AssertPendingOOMException();
     return nullptr;
   }
@@ -3515,12 +3554,12 @@
   StackHandleScope<2> hs(self);
   MutableHandle<mirror::Class> component_type(hs.NewHandle(FindClass(self, descriptor + 1,
                                                                      class_loader)));
-  if (component_type.Get() == nullptr) {
+  if (component_type == nullptr) {
     DCHECK(self->IsExceptionPending());
     // We need to accept erroneous classes as component types.
     const size_t component_hash = ComputeModifiedUtf8Hash(descriptor + 1);
     component_type.Assign(LookupClass(self, descriptor + 1, component_hash, class_loader.Get()));
-    if (component_type.Get() == nullptr) {
+    if (component_type == nullptr) {
       DCHECK(self->IsExceptionPending());
       return nullptr;
     } else {
@@ -3581,9 +3620,9 @@
       new_class.Assign(GetClassRoot(kLongArrayClass));
     }
   }
-  if (new_class.Get() == nullptr) {
+  if (new_class == nullptr) {
     new_class.Assign(AllocClass(self, mirror::Array::ClassSize(image_pointer_size_)));
-    if (new_class.Get() == nullptr) {
+    if (new_class == nullptr) {
       self->AssertPendingOOMException();
       return nullptr;
     }
@@ -3816,8 +3855,8 @@
                                                Handle<mirror::Class> klass,
                                                Handle<mirror::Class> supertype) {
   DCHECK(self != nullptr);
-  DCHECK(klass.Get() != nullptr);
-  DCHECK(supertype.Get() != nullptr);
+  DCHECK(klass != nullptr);
+  DCHECK(supertype != nullptr);
 
   if (!supertype->IsVerified() && !supertype->IsErroneous()) {
     VerifyClass(self, supertype);
@@ -3834,13 +3873,13 @@
   LOG(WARNING) << error_msg  << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
   StackHandleScope<1> hs(self);
   Handle<mirror::Throwable> cause(hs.NewHandle(self->GetException()));
-  if (cause.Get() != nullptr) {
+  if (cause != nullptr) {
     // Set during VerifyClass call (if at all).
     self->ClearException();
   }
   // Change into a verify error.
   ThrowVerifyError(klass.Get(), "%s", error_msg.c_str());
-  if (cause.Get() != nullptr) {
+  if (cause != nullptr) {
     self->GetException()->SetCause(cause.Get());
   }
   ClassReference ref(klass->GetDexCache()->GetDexFile(), klass->GetDexClassDefIndex());
@@ -3919,7 +3958,7 @@
   StackHandleScope<2> hs(self);
   MutableHandle<mirror::Class> supertype(hs.NewHandle(klass->GetSuperClass()));
   // If we have a superclass and we get a hard verification failure we can return immediately.
-  if (supertype.Get() != nullptr && !AttemptSupertypeVerification(self, klass, supertype)) {
+  if (supertype != nullptr && !AttemptSupertypeVerification(self, klass, supertype)) {
     CHECK(self->IsExceptionPending()) << "Verification error should be pending.";
     return verifier::MethodVerifier::kHardFailure;
   }
@@ -3934,14 +3973,14 @@
   //     but choose not to for an optimization. If the interfaces is being verified due to a class
   //     initialization (which would need all the default interfaces to be verified) the class code
   //     will trigger the recursive verification anyway.
-  if ((supertype.Get() == nullptr || supertype->IsVerified())  // See (1)
+  if ((supertype == nullptr || supertype->IsVerified())  // See (1)
       && !klass->IsInterface()) {                              // See (2)
     int32_t iftable_count = klass->GetIfTableCount();
     MutableHandle<mirror::Class> iface(hs.NewHandle<mirror::Class>(nullptr));
     // Loop through all interfaces this class has defined. It doesn't matter the order.
     for (int32_t i = 0; i < iftable_count; i++) {
       iface.Assign(klass->GetIfTable()->GetInterface(i));
-      DCHECK(iface.Get() != nullptr);
+      DCHECK(iface != nullptr);
       // We only care if we have default interfaces and can skip if we are already verified...
       if (LIKELY(!iface->HasDefaultMethods() || iface->IsVerified())) {
         continue;
@@ -3961,7 +4000,7 @@
   // At this point if verification failed, then supertype is the "first" supertype that failed
   // verification (without a specific order). If verification succeeded, then supertype is either
   // null or the original superclass of klass and is verified.
-  DCHECK(supertype.Get() == nullptr ||
+  DCHECK(supertype == nullptr ||
          supertype.Get() == klass->GetSuperClass() ||
          !supertype->IsVerified());
 
@@ -4002,7 +4041,7 @@
     if (verifier_failure == verifier::MethodVerifier::kNoFailure) {
       // Even though there were no verifier failures we need to respect whether the super-class and
       // super-default-interfaces were verified or requiring runtime reverification.
-      if (supertype.Get() == nullptr || supertype->IsVerified()) {
+      if (supertype == nullptr || supertype->IsVerified()) {
         mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, self);
       } else {
         CHECK_EQ(supertype->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime);
@@ -4185,7 +4224,7 @@
   StackHandleScope<10> hs(self);
   MutableHandle<mirror::Class> klass(hs.NewHandle(
       AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::Class))));
-  if (klass.Get() == nullptr) {
+  if (klass == nullptr) {
     CHECK(self->IsExceptionPending());  // OOME.
     return nullptr;
   }
@@ -4609,7 +4648,7 @@
       MutableHandle<mirror::Class> handle_scope_iface(hs_iface.NewHandle<mirror::Class>(nullptr));
       for (size_t i = 0; i < num_direct_interfaces; i++) {
         handle_scope_iface.Assign(mirror::Class::GetDirectInterface(self, klass.Get(), i));
-        CHECK(handle_scope_iface.Get() != nullptr);
+        CHECK(handle_scope_iface != nullptr);
         CHECK(handle_scope_iface->IsInterface());
         if (handle_scope_iface->HasBeenRecursivelyInitialized()) {
           // We have already done this for this interface. Skip it.
@@ -4888,7 +4927,7 @@
   {
     StackHandleScope<1> hs(self);
     Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */)));
-    if (UNLIKELY(return_type.Get() == nullptr)) {
+    if (UNLIKELY(return_type == nullptr)) {
       ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method1);
       return false;
     }
@@ -4938,7 +4977,7 @@
     dex::TypeIndex param_type_idx = types1->GetTypeItem(i).type_idx_;
     Handle<mirror::Class> param_type(hs.NewHandle(
         method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */)));
-    if (UNLIKELY(param_type.Get() == nullptr)) {
+    if (UNLIKELY(param_type == nullptr)) {
       ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
                                              method1, i, param_type_idx);
       return false;
@@ -5020,7 +5059,7 @@
                                     Handle<mirror::Class> c,
                                     bool can_init_fields,
                                     bool can_init_parents) {
-  DCHECK(c.Get() != nullptr);
+  DCHECK(c != nullptr);
   if (c->IsInitialized()) {
     EnsureSkipAccessChecksMethods(c, image_pointer_size_);
     self->AssertNoPendingException();
@@ -5200,7 +5239,7 @@
     klass->SetMethodsPtrUnchecked(nullptr, 0, 0);
     klass->SetSFieldsPtrUnchecked(nullptr);
     klass->SetIFieldsPtrUnchecked(nullptr);
-    if (UNLIKELY(h_new_class.Get() == nullptr)) {
+    if (UNLIKELY(h_new_class == nullptr)) {
       self->AssertPendingOOMException();
       mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
       return false;
@@ -5744,7 +5783,7 @@
     MutableHandle<mirror::PointerArray> vtable;
     if (super_class->ShouldHaveEmbeddedVTable()) {
       vtable = hs.NewHandle(AllocPointerArray(self, max_count));
-      if (UNLIKELY(vtable.Get() == nullptr)) {
+      if (UNLIKELY(vtable == nullptr)) {
         self->AssertPendingOOMException();
         return false;
       }
@@ -5773,7 +5812,7 @@
       }
       vtable = hs.NewHandle(down_cast<mirror::PointerArray*>(
           super_vtable->CopyOf(self, max_count)));
-      if (UNLIKELY(vtable.Get() == nullptr)) {
+      if (UNLIKELY(vtable == nullptr)) {
         self->AssertPendingOOMException();
         return false;
       }
@@ -5909,7 +5948,7 @@
     CHECK_LE(actual_count, max_count);
     if (actual_count < max_count) {
       vtable.Assign(down_cast<mirror::PointerArray*>(vtable->CopyOf(self, actual_count)));
-      if (UNLIKELY(vtable.Get() == nullptr)) {
+      if (UNLIKELY(vtable == nullptr)) {
         self->AssertPendingOOMException();
         return false;
       }
@@ -5962,8 +6001,8 @@
                                        PointerSize image_pointer_size)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(self != nullptr);
-  DCHECK(iface.Get() != nullptr);
-  DCHECK(iftable.Get() != nullptr);
+  DCHECK(iface != nullptr);
+  DCHECK(iftable != nullptr);
   DCHECK_GE(ifstart, 0u);
   DCHECK_LT(ifstart, iftable->Count());
   DCHECK_EQ(iface.Get(), iftable->GetInterface(ifstart));
@@ -6048,7 +6087,7 @@
                      << "This will be a fatal error in subsequent versions of android. "
                      << "Continuing anyway.";
       }
-      if (UNLIKELY(chosen_iface.Get() != nullptr)) {
+      if (UNLIKELY(chosen_iface != nullptr)) {
         // We have multiple default impls of the same method. This is a potential default conflict.
         // We need to check if this possibly conflicting method is either a superclass of the chosen
         // default implementation or is overridden by a non-default interface method. In either case
@@ -6503,7 +6542,7 @@
   StackHandleScope<1> hs(self);
   const bool has_superclass = klass->HasSuperClass();
   const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U;
-  const bool have_interfaces = interfaces.Get() != nullptr;
+  const bool have_interfaces = interfaces != nullptr;
   const size_t num_interfaces =
       have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces();
   if (num_interfaces == 0) {
@@ -6549,7 +6588,7 @@
   }
   // Create the interface function table.
   MutableHandle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount)));
-  if (UNLIKELY(iftable.Get() == nullptr)) {
+  if (UNLIKELY(iftable == nullptr)) {
     self->AssertPendingOOMException();
     return false;
   }
@@ -6587,7 +6626,7 @@
     DCHECK_NE(num_interfaces, 0U);
     iftable.Assign(down_cast<mirror::IfTable*>(
         iftable->CopyOf(self, new_ifcount * mirror::IfTable::kMax)));
-    if (UNLIKELY(iftable.Get() == nullptr)) {
+    if (UNLIKELY(iftable == nullptr)) {
       self->AssertPendingOOMException();
       return false;
     }
@@ -6628,7 +6667,7 @@
   Handle<mirror::PointerArray> check_vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
   ObjPtr<mirror::Class> super_temp = (klass->HasSuperClass()) ? klass->GetSuperClass() : nullptr;
   Handle<mirror::Class> superclass(hs.NewHandle(super_temp));
-  int32_t super_vtable_length = (superclass.Get() != nullptr) ? superclass->GetVTableLength() : 0;
+  int32_t super_vtable_length = (superclass != nullptr) ? superclass->GetVTableLength() : 0;
   for (int32_t i = 0; i < check_vtable->GetLength(); ++i) {
     ArtMethod* m = check_vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size);
     CHECK(m != nullptr);
@@ -7287,7 +7326,7 @@
         // For a new interface, however, we need the whole vtable in case a new
         // interface method is implemented in the whole superclass.
         using_virtuals = false;
-        DCHECK(vtable.Get() != nullptr);
+        DCHECK(vtable != nullptr);
         input_vtable_array = vtable;
         input_array_length = input_vtable_array->GetLength();
       }
@@ -7430,7 +7469,7 @@
 
     if (fill_tables) {
       vtable.Assign(helper.UpdateVtable(default_translations, vtable.Get()));
-      if (UNLIKELY(vtable.Get() == nullptr)) {
+      if (UNLIKELY(vtable == nullptr)) {
         // The helper has already called self->AssertPendingOOMException();
         return false;
       }
@@ -7450,12 +7489,12 @@
 }
 
 bool ClassLinker::LinkInstanceFields(Thread* self, Handle<mirror::Class> klass) {
-  CHECK(klass.Get() != nullptr);
+  CHECK(klass != nullptr);
   return LinkFields(self, klass, false, nullptr);
 }
 
 bool ClassLinker::LinkStaticFields(Thread* self, Handle<mirror::Class> klass, size_t* class_size) {
-  CHECK(klass.Get() != nullptr);
+  CHECK(klass != nullptr);
   return LinkFields(self, klass, true, class_size);
 }
 
@@ -7711,7 +7750,7 @@
 mirror::String* ClassLinker::ResolveString(const DexFile& dex_file,
                                            dex::StringIndex string_idx,
                                            Handle<mirror::DexCache> dex_cache) {
-  DCHECK(dex_cache.Get() != nullptr);
+  DCHECK(dex_cache != nullptr);
   Thread::PoisonObjectPointersIfDebug();
   ObjPtr<mirror::String> resolved = dex_cache->GetResolvedString(string_idx);
   if (resolved != nullptr) {
@@ -7720,16 +7759,14 @@
   uint32_t utf16_length;
   const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length);
   ObjPtr<mirror::String> string = intern_table_->InternStrong(utf16_length, utf8_data);
-  if (string != nullptr) {
-    dex_cache->SetResolvedString(string_idx, string);
-  }
+  dex_cache->SetResolvedString(string_idx, string);
   return string.Ptr();
 }
 
 mirror::String* ClassLinker::LookupString(const DexFile& dex_file,
                                           dex::StringIndex string_idx,
                                           Handle<mirror::DexCache> dex_cache) {
-  DCHECK(dex_cache.Get() != nullptr);
+  DCHECK(dex_cache != nullptr);
   ObjPtr<mirror::String> resolved = dex_cache->GetResolvedString(string_idx);
   if (resolved != nullptr) {
     return resolved.Ptr();
@@ -7763,16 +7800,11 @@
       // Find the class in the loaded classes table.
       type = LookupClass(self, descriptor, hash, class_loader.Ptr());
     }
-    if (type != nullptr) {
-      if (type->IsResolved()) {
-        dex_cache->SetResolvedType(type_idx, type);
-      } else {
-        type = nullptr;
-      }
-    }
   }
-  DCHECK(type == nullptr || type->IsResolved());
-  return type;
+  if (type != nullptr && type->IsResolved()) {
+    return type.Ptr();
+  }
+  return nullptr;
 }
 
 mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file,
@@ -7788,16 +7820,10 @@
                                         dex::TypeIndex type_idx,
                                         Handle<mirror::DexCache> dex_cache,
                                         Handle<mirror::ClassLoader> class_loader) {
-  DCHECK(dex_cache.Get() != nullptr);
+  DCHECK(dex_cache != nullptr);
   Thread::PoisonObjectPointersIfDebug();
   ObjPtr<mirror::Class> resolved = dex_cache->GetResolvedType(type_idx);
   if (resolved == nullptr) {
-    // TODO: Avoid this lookup as it duplicates work done in FindClass(). It is here
-    // as a workaround for FastNative JNI to avoid AssertNoPendingException() when
-    // trying to resolve annotations while an exception may be pending. Bug: 34659969
-    resolved = LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get());
-  }
-  if (resolved == nullptr) {
     Thread* self = Thread::Current();
     const char* descriptor = dex_file.StringByTypeIdx(type_idx);
     resolved = FindClass(self, descriptor, class_loader);
@@ -7832,7 +7858,7 @@
                                       Handle<mirror::ClassLoader> class_loader,
                                       ArtMethod* referrer,
                                       InvokeType type) {
-  DCHECK(dex_cache.Get() != nullptr);
+  DCHECK(dex_cache != nullptr);
   // Check for hit in the dex cache.
   ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);
   Thread::PoisonObjectPointersIfDebug();
@@ -8071,7 +8097,7 @@
                                     Handle<mirror::DexCache> dex_cache,
                                     Handle<mirror::ClassLoader> class_loader,
                                     bool is_static) {
-  DCHECK(dex_cache.Get() != nullptr);
+  DCHECK(dex_cache != nullptr);
   ArtField* resolved = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
   Thread::PoisonObjectPointersIfDebug();
   if (resolved != nullptr) {
@@ -8112,7 +8138,7 @@
                                        uint32_t field_idx,
                                        Handle<mirror::DexCache> dex_cache,
                                        Handle<mirror::ClassLoader> class_loader) {
-  DCHECK(dex_cache.Get() != nullptr);
+  DCHECK(dex_cache != nullptr);
   ArtField* resolved = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
   Thread::PoisonObjectPointersIfDebug();
   if (resolved != nullptr) {
@@ -8143,7 +8169,7 @@
                                                    Handle<mirror::DexCache> dex_cache,
                                                    Handle<mirror::ClassLoader> class_loader) {
   DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
-  DCHECK(dex_cache.Get() != nullptr);
+  DCHECK(dex_cache != nullptr);
 
   ObjPtr<mirror::MethodType> resolved = dex_cache->GetResolvedMethodType(proto_idx);
   if (resolved != nullptr) {
@@ -8157,7 +8183,7 @@
   const DexFile::ProtoId& proto_id = dex_file.GetProtoId(proto_idx);
   Handle<mirror::Class> return_type(hs.NewHandle(
       ResolveType(dex_file, proto_id.return_type_idx_, dex_cache, class_loader)));
-  if (return_type.Get() == nullptr) {
+  if (return_type == nullptr) {
     DCHECK(self->IsExceptionPending());
     return nullptr;
   }
@@ -8172,7 +8198,7 @@
   ObjPtr<mirror::Class> array_of_class = FindArrayClass(self, &class_type);
   Handle<mirror::ObjectArray<mirror::Class>> method_params(hs.NewHandle(
       mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, num_method_args)));
-  if (method_params.Get() == nullptr) {
+  if (method_params == nullptr) {
     DCHECK(self->IsExceptionPending());
     return nullptr;
   }
@@ -8183,7 +8209,7 @@
   for (; it.HasNext(); it.Next()) {
     const dex::TypeIndex type_idx = it.GetTypeIdx();
     param_class.Assign(ResolveType(dex_file, type_idx, dex_cache, class_loader));
-    if (param_class.Get() == nullptr) {
+    if (param_class == nullptr) {
       DCHECK(self->IsExceptionPending());
       return nullptr;
     }
@@ -8200,6 +8226,148 @@
   return type.Get();
 }
 
+mirror::MethodHandle* ClassLinker::ResolveMethodHandle(uint32_t method_handle_idx,
+                                                       ArtMethod* referrer)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  Thread* const self = Thread::Current();
+  const DexFile* const dex_file = referrer->GetDexFile();
+  const DexFile::MethodHandleItem& mh = dex_file->GetMethodHandle(method_handle_idx);
+
+  union {
+    ArtField* field;
+    ArtMethod* method;
+    uintptr_t field_or_method;
+  } target;
+  uint32_t num_params;
+  mirror::MethodHandle::Kind kind;
+  DexFile::MethodHandleType handle_type =
+      static_cast<DexFile::MethodHandleType>(mh.method_handle_type_);
+  switch (handle_type) {
+    case DexFile::MethodHandleType::kStaticPut: {
+      kind = mirror::MethodHandle::Kind::kStaticPut;
+      target.field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */);
+      num_params = 1;
+      break;
+    }
+    case DexFile::MethodHandleType::kStaticGet: {
+      kind = mirror::MethodHandle::Kind::kStaticGet;
+      target.field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */);
+      num_params = 0;
+      break;
+    }
+    case DexFile::MethodHandleType::kInstancePut: {
+      kind = mirror::MethodHandle::Kind::kInstancePut;
+      target.field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */);
+      num_params = 2;
+      break;
+    }
+    case DexFile::MethodHandleType::kInstanceGet: {
+      kind = mirror::MethodHandle::Kind::kInstanceGet;
+      target.field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */);
+      num_params = 1;
+      break;
+    }
+    case DexFile::MethodHandleType::kInvokeStatic: {
+      kind = mirror::MethodHandle::Kind::kInvokeStatic;
+      target.method = ResolveMethod<kNoICCECheckForCache>(self,
+                                                          mh.field_or_method_idx_,
+                                                          referrer,
+                                                          InvokeType::kStatic);
+      uint32_t shorty_length;
+      target.method->GetShorty(&shorty_length);
+      num_params = shorty_length - 1;  // Remove 1 for return value.
+      break;
+    }
+    case DexFile::MethodHandleType::kInvokeInstance: {
+      kind = mirror::MethodHandle::Kind::kInvokeVirtual;
+      target.method = ResolveMethod<kNoICCECheckForCache>(self,
+                                                          mh.field_or_method_idx_,
+                                                          referrer,
+                                                          InvokeType::kVirtual);
+      uint32_t shorty_length;
+      target.method->GetShorty(&shorty_length);
+      num_params = shorty_length - 1;  // Remove 1 for return value.
+      break;
+    }
+    case DexFile::MethodHandleType::kInvokeConstructor: {
+      UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform.";
+      num_params = 0;
+    }
+  }
+
+  StackHandleScope<5> hs(self);
+  ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
+  ObjPtr<mirror::Class> array_of_class = FindArrayClass(self, &class_type);
+  Handle<mirror::ObjectArray<mirror::Class>> method_params(hs.NewHandle(
+      mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, num_params)));
+  if (method_params.Get() == nullptr) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  Handle<mirror::Class> return_type;
+  switch (handle_type) {
+    case DexFile::MethodHandleType::kStaticPut: {
+      method_params->Set(0, target.field->GetType<true>());
+      return_type = hs.NewHandle(FindPrimitiveClass('V'));
+      break;
+    }
+    case DexFile::MethodHandleType::kStaticGet: {
+      return_type = hs.NewHandle(target.field->GetType<true>());
+      break;
+    }
+    case DexFile::MethodHandleType::kInstancePut: {
+      method_params->Set(0, target.field->GetDeclaringClass());
+      method_params->Set(1, target.field->GetType<true>());
+      return_type = hs.NewHandle(FindPrimitiveClass('V'));
+      break;
+    }
+    case DexFile::MethodHandleType::kInstanceGet: {
+      method_params->Set(0, target.field->GetDeclaringClass());
+      return_type = hs.NewHandle(target.field->GetType<true>());
+      break;
+    }
+    case DexFile::MethodHandleType::kInvokeStatic:
+    case DexFile::MethodHandleType::kInvokeInstance: {
+      // TODO(oth): This will not work for varargs methods as this
+      // requires instantiating a Transformer. This resolution step
+      // would be best done in managed code rather than in the run
+      // time (b/35235705)
+      Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
+      Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
+      DexFileParameterIterator it(*dex_file, target.method->GetPrototype());
+      for (int32_t i = 0; it.HasNext(); i++, it.Next()) {
+        const dex::TypeIndex type_idx = it.GetTypeIdx();
+        mirror::Class* klass = ResolveType(*dex_file, type_idx, dex_cache, class_loader);
+        if (nullptr == klass) {
+          DCHECK(self->IsExceptionPending());
+          return nullptr;
+        }
+        method_params->Set(i, klass);
+      }
+      return_type = hs.NewHandle(target.method->GetReturnType(true));
+      break;
+    }
+    case DexFile::MethodHandleType::kInvokeConstructor: {
+      // TODO(oth): b/35235705
+      UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform.";
+    }
+  }
+
+  if (return_type.IsNull()) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  Handle<mirror::MethodType>
+      mt(hs.NewHandle(mirror::MethodType::Create(self, return_type, method_params)));
+  if (mt.IsNull()) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+  return mirror::MethodHandleImpl::Create(self, target.field_or_method, kind, mt);
+}
+
 bool ClassLinker::IsQuickResolutionStub(const void* entry_point) const {
   return (entry_point == GetQuickResolutionStub()) ||
       (quick_resolution_trampoline_ == entry_point);
@@ -8315,7 +8483,9 @@
     "[Ljava/lang/reflect/Constructor;",
     "[Ljava/lang/reflect/Field;",
     "[Ljava/lang/reflect/Method;",
+    "Ljava/lang/invoke/CallSite;",
     "Ljava/lang/invoke/MethodHandleImpl;",
+    "Ljava/lang/invoke/MethodHandles$Lookup;",
     "Ljava/lang/invoke/MethodType;",
     "Ljava/lang/ClassLoader;",
     "Ljava/lang/Throwable;",
@@ -8363,7 +8533,7 @@
       jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements);
 
   Handle<mirror::Class> dex_elements_class(hs.NewHandle(dex_elements_field->GetType<true>()));
-  DCHECK(dex_elements_class.Get() != nullptr);
+  DCHECK(dex_elements_class != nullptr);
   DCHECK(dex_elements_class->IsArrayClass());
   Handle<mirror::ObjectArray<mirror::Object>> h_dex_elements(hs.NewHandle(
       mirror::ObjectArray<mirror::Object>::Alloc(self,
@@ -8392,21 +8562,21 @@
     Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc(
         self,
         kDexFileIndexStart + 1));
-    DCHECK(h_long_array.Get() != nullptr);
+    DCHECK(h_long_array != nullptr);
     h_long_array->Set(kDexFileIndexStart, reinterpret_cast<intptr_t>(dex_file));
 
     Handle<mirror::Object> h_dex_file = hs2.NewHandle(
         cookie_field->GetDeclaringClass()->AllocObject(self));
-    DCHECK(h_dex_file.Get() != nullptr);
+    DCHECK(h_dex_file != nullptr);
     cookie_field->SetObject<false>(h_dex_file.Get(), h_long_array.Get());
 
     Handle<mirror::String> h_file_name = hs2.NewHandle(
         mirror::String::AllocFromModifiedUtf8(self, dex_file->GetLocation().c_str()));
-    DCHECK(h_file_name.Get() != nullptr);
+    DCHECK(h_file_name != nullptr);
     file_name_field->SetObject<false>(h_dex_file.Get(), h_file_name.Get());
 
     Handle<mirror::Object> h_element = hs2.NewHandle(h_dex_element_class->AllocObject(self));
-    DCHECK(h_element.Get() != nullptr);
+    DCHECK(h_element != nullptr);
     element_file_field->SetObject<false>(h_element.Get(), h_dex_file.Get());
 
     h_dex_elements->Set(index, h_element.Get());
@@ -8417,7 +8587,7 @@
   // Create DexPathList.
   Handle<mirror::Object> h_dex_path_list = hs.NewHandle(
       dex_elements_field->GetDeclaringClass()->AllocObject(self));
-  DCHECK(h_dex_path_list.Get() != nullptr);
+  DCHECK(h_dex_path_list != nullptr);
   // Set elements.
   dex_elements_field->SetObject<false>(h_dex_path_list.Get(), h_dex_elements.Get());
 
@@ -8426,7 +8596,7 @@
       soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
   Handle<mirror::Object> h_path_class_loader = hs.NewHandle(
       h_path_class_class->AllocObject(self));
-  DCHECK(h_path_class_loader.Get() != nullptr);
+  DCHECK(h_path_class_loader != nullptr);
   // Set DexPathList.
   ArtField* path_list_field =
       jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index a880a10..e27a53d 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -55,6 +55,8 @@
   class DexCacheMethodHandlesTest_Open_Test;
   class DexCacheTest_Open_Test;
   class IfTable;
+  class MethodHandle;
+  class MethodHandlesLookup;
   class MethodType;
   template<class T> class ObjectArray;
   class StackTraceElement;
@@ -106,7 +108,9 @@
     kJavaLangReflectConstructorArrayClass,
     kJavaLangReflectFieldArrayClass,
     kJavaLangReflectMethodArrayClass,
+    kJavaLangInvokeCallSite,
     kJavaLangInvokeMethodHandleImpl,
+    kJavaLangInvokeMethodHandlesLookup,
     kJavaLangInvokeMethodType,
     kJavaLangClassLoader,
     kJavaLangThrowable,
@@ -262,6 +266,10 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
 
+  mirror::Class* ResolveType(dex::TypeIndex type_idx, ArtField* referrer)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
+
   // Look up a resolved type with the given ID from the DexFile. The ClassLoader is used to search
   // for the type, since it may be referenced from but not contained within the given DexFile.
   ObjPtr<mirror::Class> LookupResolvedType(const DexFile& dex_file,
@@ -269,10 +277,6 @@
                                            ObjPtr<mirror::DexCache> dex_cache,
                                            ObjPtr<mirror::ClassLoader> class_loader)
       REQUIRES_SHARED(Locks::mutator_lock_);
-  static ObjPtr<mirror::Class> LookupResolvedType(dex::TypeIndex type_idx,
-                                                  ObjPtr<mirror::DexCache> dex_cache,
-                                                  ObjPtr<mirror::ClassLoader> class_loader)
-      REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Resolve a type with the given ID from the DexFile, storing the
   // result in DexCache. The ClassLoader is used to search for the
@@ -366,6 +370,12 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
 
+  // Resolve a method handle with a given ID from the DexFile. The
+  // result is not cached in the DexCache as the instance will only be
+  // used once in most circumstances.
+  mirror::MethodHandle* ResolveMethodHandle(uint32_t method_handle_idx, ArtMethod* referrer)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Returns true on success, false if there's an exception pending.
   // can_run_clinit=false allows the compiler to attempt to init a class,
   // given the restriction that no <clinit> execution is possible.
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index de1cd6d..07f3744 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -32,6 +32,7 @@
 #include "entrypoints/entrypoint_utils-inl.h"
 #include "gc/heap.h"
 #include "mirror/accessible_object.h"
+#include "mirror/call_site.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_ext.h"
 #include "mirror/dex_cache.h"
@@ -40,6 +41,7 @@
 #include "mirror/field.h"
 #include "mirror/method_type.h"
 #include "mirror/method_handle_impl.h"
+#include "mirror/method_handles_lookup.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/proxy.h"
@@ -185,7 +187,7 @@
 
   void AssertArrayClass(const std::string& array_descriptor, Handle<mirror::Class> array)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    ASSERT_TRUE(array.Get() != nullptr);
+    ASSERT_TRUE(array != nullptr);
     ASSERT_TRUE(array->GetClass() != nullptr);
     ASSERT_EQ(array->GetClass(), array->GetClass()->GetClass());
     EXPECT_TRUE(array->GetClass()->GetSuperClass() != nullptr);
@@ -409,7 +411,7 @@
     StackHandleScope<1> hs(self);
     Handle<mirror::Class> klass(
         hs.NewHandle(class_linker_->FindSystemClass(self, descriptor.c_str())));
-    ASSERT_TRUE(klass.Get() != nullptr);
+    ASSERT_TRUE(klass != nullptr);
     std::string temp;
     EXPECT_STREQ(descriptor.c_str(), klass.Get()->GetDescriptor(&temp));
     EXPECT_EQ(class_loader, klass->GetClassLoader());
@@ -669,11 +671,13 @@
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_), "dex");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_), "dexFile");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location");
+    addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_call_sites_), "numResolvedCallSites");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_fields_), "numResolvedFields");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_method_types_), "numResolvedMethodTypes");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_methods_), "numResolvedMethods");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_types_), "numResolvedTypes");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_strings_), "numStrings");
+    addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_call_sites_), "resolvedCallSites");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_), "resolvedFields");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_method_types_), "resolvedMethodTypes");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_), "resolvedMethods");
@@ -762,6 +766,14 @@
   }
 };
 
+struct MethodHandlesLookupOffsets : public CheckOffsets<mirror::MethodHandlesLookup> {
+  MethodHandlesLookupOffsets() : CheckOffsets<mirror::MethodHandlesLookup>(
+      false, "Ljava/lang/invoke/MethodHandles$Lookup;") {
+    addOffset(OFFSETOF_MEMBER(mirror::MethodHandlesLookup, allowed_modes_), "allowedModes");
+    addOffset(OFFSETOF_MEMBER(mirror::MethodHandlesLookup, lookup_class_), "lookupClass");
+  }
+};
+
 struct EmulatedStackFrameOffsets : public CheckOffsets<mirror::EmulatedStackFrame> {
   EmulatedStackFrameOffsets() : CheckOffsets<mirror::EmulatedStackFrame>(
       false, "Ldalvik/system/EmulatedStackFrame;") {
@@ -772,6 +784,13 @@
   }
 };
 
+struct CallSiteOffsets : public CheckOffsets<mirror::CallSite> {
+  CallSiteOffsets() : CheckOffsets<mirror::CallSite>(
+      false, "Ljava/lang/invoke/CallSite;") {
+    addOffset(OFFSETOF_MEMBER(mirror::CallSite, target_), "target");
+  }
+};
+
 // C++ fields must exactly match the fields in the Java classes. If this fails,
 // reorder the fields in the C++ class. Managed class fields are ordered by
 // ClassLinker::LinkFields.
@@ -794,7 +813,9 @@
   EXPECT_TRUE(MethodTypeOffsets().Check());
   EXPECT_TRUE(MethodHandleOffsets().Check());
   EXPECT_TRUE(MethodHandleImplOffsets().Check());
+  EXPECT_TRUE(MethodHandlesLookupOffsets().Check());
   EXPECT_TRUE(EmulatedStackFrameOffsets().Check());
+  EXPECT_TRUE(CallSiteOffsets().Check());
 }
 
 TEST_F(ClassLinkerTest, FindClassNonexistent) {
@@ -914,7 +935,7 @@
       class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()),
       klass);
   // Zero out the resolved type and make sure LookupResolvedType still finds it.
-  dex_cache->ClearResolvedType(type_idx);
+  dex_cache->SetResolvedType(type_idx, nullptr);
   EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr);
   EXPECT_OBJ_PTR_EQ(
       class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()),
@@ -949,7 +970,7 @@
       class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()),
       array_klass);
   // Zero out the resolved type and make sure LookupResolvedType() still finds it.
-  dex_cache->ClearResolvedType(array_idx);
+  dex_cache->SetResolvedType(array_idx, nullptr);
   EXPECT_TRUE(dex_cache->GetResolvedType(array_idx) == nullptr);
   EXPECT_OBJ_PTR_EQ(
       class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()),
@@ -972,7 +993,7 @@
       class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
       klass.Get());
   // Zero out the resolved type and make sure LookupResolvedType still finds it.
-  dex_cache->ClearResolvedType(type_idx);
+  dex_cache->SetResolvedType(type_idx, nullptr);
   EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr);
   EXPECT_OBJ_PTR_EQ(
       class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
@@ -990,7 +1011,7 @@
       class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
       klass.Get());
   // Zero out the resolved type and make sure LookupResolvedType() still finds it.
-  dex_cache->ClearResolvedType(type_idx);
+  dex_cache->SetResolvedType(type_idx, nullptr);
   EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr);
   EXPECT_OBJ_PTR_EQ(
       class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
@@ -1411,13 +1432,13 @@
   // java.lang.Object is a bootstrap class.
   Handle<mirror::Class> jlo_class(
       hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
-  ASSERT_TRUE(jlo_class.Get() != nullptr);
+  ASSERT_TRUE(jlo_class != nullptr);
   EXPECT_TRUE(jlo_class.Get()->IsBootStrapClassLoaded());
 
   // Statics is not a bootstrap class.
   Handle<mirror::Class> statics(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStatics;", class_loader)));
-  ASSERT_TRUE(statics.Get() != nullptr);
+  ASSERT_TRUE(statics != nullptr);
   EXPECT_FALSE(statics.Get()->IsBootStrapClassLoaded());
 }
 
@@ -1431,11 +1452,11 @@
     ReaderMutexLock mu(soa.Self(), *Locks::dex_lock_);
     for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
       dex_cache.Assign(soa.Self()->DecodeJObject(data.weak_root)->AsDexCache());
-      if (dex_cache.Get() != nullptr) {
+      if (dex_cache != nullptr) {
         break;
       }
     }
-    ASSERT_TRUE(dex_cache.Get() != nullptr);
+    ASSERT_TRUE(dex_cache != nullptr);
   }
   // Make a copy of the dex cache and change the name.
   dex_cache.Assign(dex_cache->Clone(soa.Self())->AsDexCache());
@@ -1487,7 +1508,7 @@
       class_linker_->ResolveMethodType(dex_file, method1_id.proto_idx_, dex_cache, class_loader));
 
   // Assert that the method type was resolved successfully.
-  ASSERT_TRUE(method1_type.Get() != nullptr);
+  ASSERT_TRUE(method1_type != nullptr);
 
   // Assert that the return type and the method arguments are as we expect.
   Handle<mirror::Class> string_class(
diff --git a/runtime/class_table_test.cc b/runtime/class_table_test.cc
index f1248eb..18c2b82 100644
--- a/runtime/class_table_test.cc
+++ b/runtime/class_table_test.cc
@@ -80,7 +80,7 @@
   Handle<mirror::Class> h_Y(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), descriptor_y, class_loader)));
   Handle<mirror::Object> obj_X = hs.NewHandle(h_X->AllocObject(soa.Self()));
-  ASSERT_TRUE(obj_X.Get() != nullptr);
+  ASSERT_TRUE(obj_X != nullptr);
   ClassTable table;
   EXPECT_EQ(table.NumZygoteClasses(class_loader.Get()), 0u);
   EXPECT_EQ(table.NumNonZygoteClasses(class_loader.Get()), 0u);
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index fc82264..78ba6e7 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -625,9 +625,9 @@
   if (IsHost()) {
     const char* host_dir = getenv("ANDROID_HOST_OUT");
     CHECK(host_dir != nullptr);
-    location = StringPrintf("%s/framework/core-npic.%s", host_dir, suffix);
+    location = StringPrintf("%s/framework/core.%s", host_dir, suffix);
   } else {
-    location = StringPrintf("/data/art-test/core-npic.%s", suffix);
+    location = StringPrintf("/data/art-test/core.%s", suffix);
   }
 
   return location;
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 26ec364..d7abe2a 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -223,6 +223,12 @@
     return; \
   }
 
+#define TEST_DISABLED_FOR_X86() \
+  if (kRuntimeISA == kX86) { \
+    printf("WARNING: TEST DISABLED FOR X86\n"); \
+    return; \
+  }
+
 #define TEST_DISABLED_FOR_STRING_COMPRESSION() \
   if (mirror::kUseStringCompression) { \
     printf("WARNING: TEST DISABLED FOR STRING COMPRESSION\n"); \
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index a44f79e..4f4bed0 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -126,6 +126,22 @@
                               mirror::Class::PrettyDescriptor(array_class).c_str()).c_str());
 }
 
+// BootstrapMethodError
+
+void ThrowBootstrapMethodError(const char* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  ThrowException("Ljava/lang/BootstrapMethodError;", nullptr, fmt, &args);
+  va_end(args);
+}
+
+void ThrowWrappedBootstrapMethodError(const char* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  ThrowWrappedException("Ljava/lang/BootstrapMethodError;", nullptr, fmt, &args);
+  va_end(args);
+}
+
 // ClassCastException
 
 void ThrowClassCastException(ObjPtr<mirror::Class> dest_type, ObjPtr<mirror::Class> src_type) {
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 76ea2ae..55a8938 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -56,6 +56,14 @@
                               ObjPtr<mirror::Class> array_class)
     REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
 
+// BootstrapMethodError
+
+void ThrowBootstrapMethodError(const char* fmt, ...)
+    REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
+void ThrowWrappedBootstrapMethodError(const char* fmt, ...)
+    REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
 // ClassCircularityError
 
 void ThrowClassCircularityError(ObjPtr<mirror::Class> c)
@@ -236,7 +244,7 @@
     __attribute__((__format__(__printf__, 2, 3)))
     REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
 
-// WrontMethodTypeException
+// WrongMethodTypeException
 void ThrowWrongMethodTypeException(mirror::MethodType* callee_type,
                                    mirror::MethodType* callsite_type)
     REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
diff --git a/runtime/compiler_filter.cc b/runtime/compiler_filter.cc
index cb8c11d..dc55ab8 100644
--- a/runtime/compiler_filter.cc
+++ b/runtime/compiler_filter.cc
@@ -113,7 +113,10 @@
     case CompilerFilter::kSpeed:
     case CompilerFilter::kEverything: return false;
 
-    case CompilerFilter::kVerifyProfile:
+    // verify-profile doesn't look at profiles anymore.
+    // TODO(ngeoffray): this will be cleaned up with b/34715556.
+    case CompilerFilter::kVerifyProfile: return false;
+
     case CompilerFilter::kSpaceProfile:
     case CompilerFilter::kSpeedProfile:
     case CompilerFilter::kEverythingProfile: return true;
@@ -134,7 +137,9 @@
       return filter;
 
     case CompilerFilter::kVerifyProfile:
-      return CompilerFilter::kInterpretOnly;
+      // verify-profile doesn't look at profiles anymore.
+      // TODO(ngeoffray): this will be cleaned up with b/34715556.
+      return filter;
 
     case CompilerFilter::kSpaceProfile:
       return CompilerFilter::kSpace;
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 1a0cec0..b2fba67 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -800,14 +800,14 @@
     monitor_info = MonitorInfo(o);
   }
   if (monitor_info.owner_ != nullptr) {
-    expandBufAddObjectId(reply, gRegistry->Add(monitor_info.owner_->GetPeer()));
+    expandBufAddObjectId(reply, gRegistry->Add(monitor_info.owner_->GetPeerFromOtherThread()));
   } else {
     expandBufAddObjectId(reply, gRegistry->Add(nullptr));
   }
   expandBufAdd4BE(reply, monitor_info.entry_count_);
   expandBufAdd4BE(reply, monitor_info.waiters_.size());
   for (size_t i = 0; i < monitor_info.waiters_.size(); ++i) {
-    expandBufAddObjectId(reply, gRegistry->Add(monitor_info.waiters_[i]->GetPeer()));
+    expandBufAddObjectId(reply, gRegistry->Add(monitor_info.waiters_[i]->GetPeerFromOtherThread()));
   }
   return JDWP::ERR_NONE;
 }
@@ -1352,7 +1352,7 @@
   JDWP::JdwpError error;
   mirror::Object* expected_thread_peer = gRegistry->Get<mirror::Object*>(
       expected_thread_id, &error);
-  return expected_thread_peer == event_thread->GetPeer();
+  return expected_thread_peer == event_thread->GetPeerFromOtherThread();
 }
 
 bool Dbg::MatchLocation(const JDWP::JdwpLocation& expected_location,
@@ -1765,13 +1765,13 @@
   StackHandleScope<2> hs(self);
   MutableHandle<mirror::Object>
       o(hs.NewHandle(Dbg::GetObjectRegistry()->Get<mirror::Object*>(object_id, &error)));
-  if ((!is_static && o.Get() == nullptr) || error != JDWP::ERR_NONE) {
+  if ((!is_static && o == nullptr) || error != JDWP::ERR_NONE) {
     return JDWP::ERR_INVALID_OBJECT;
   }
   ArtField* f = FromFieldId(field_id);
 
   mirror::Class* receiver_class = c;
-  if (receiver_class == nullptr && o.Get() != nullptr) {
+  if (receiver_class == nullptr && o != nullptr) {
     receiver_class = o->GetClass();
   }
 
@@ -1899,7 +1899,7 @@
   StackHandleScope<2> hs(self);
   MutableHandle<mirror::Object>
       o(hs.NewHandle(Dbg::GetObjectRegistry()->Get<mirror::Object*>(object_id, &error)));
-  if ((!is_static && o.Get() == nullptr) || error != JDWP::ERR_NONE) {
+  if ((!is_static && o == nullptr) || error != JDWP::ERR_NONE) {
     return JDWP::ERR_INVALID_OBJECT;
   }
   ArtField* f = FromFieldId(field_id);
@@ -2273,7 +2273,7 @@
       // not completely started yet so we must ignore it.
       continue;
     }
-    mirror::Object* peer = t->GetPeer();
+    mirror::Object* peer = t->GetPeerFromOtherThread();
     if (peer == nullptr) {
       // peer might be null if the thread is still starting up. We can't tell the debugger about
       // this thread yet.
@@ -2386,7 +2386,7 @@
 
 JDWP::ObjectId Dbg::GetThreadId(Thread* thread) {
   ScopedObjectAccessUnchecked soa(Thread::Current());
-  return gRegistry->Add(thread->GetPeer());
+  return gRegistry->Add(thread->GetPeerFromOtherThread());
 }
 
 void Dbg::SuspendVM() {
@@ -2867,7 +2867,7 @@
   StackHandleScope<1> hs(self);
   Handle<mirror::Throwable> pending_exception(hs.NewHandle(self->GetException()));
   self->ClearException();
-  if (kIsDebugBuild && pending_exception.Get() != nullptr) {
+  if (kIsDebugBuild && pending_exception != nullptr) {
     const DexFile::CodeItem* code_item = location.method->GetCodeItem();
     const Instruction* instr = Instruction::At(&code_item->insns_[location.dex_pc]);
     CHECK_EQ(Instruction::MOVE_EXCEPTION, instr->Opcode());
@@ -2875,7 +2875,7 @@
 
   gJdwpState->PostLocationEvent(&location, this_object, event_flags, return_value);
 
-  if (pending_exception.Get() != nullptr) {
+  if (pending_exception != nullptr) {
     self->SetException(pending_exception.Get());
   }
 }
@@ -4027,7 +4027,7 @@
   ExecuteMethodWithoutPendingException(soa, pReq);
 
   // If an exception was pending before the invoke, restore it now.
-  if (old_exception.Get() != nullptr) {
+  if (old_exception != nullptr) {
     soa.Self()->SetException(old_exception.Get());
   }
 }
@@ -4356,9 +4356,9 @@
     ScopedObjectAccessUnchecked soa(Thread::Current());
     StackHandleScope<1> hs(soa.Self());
     Handle<mirror::String> name(hs.NewHandle(t->GetThreadName()));
-    size_t char_count = (name.Get() != nullptr) ? name->GetLength() : 0;
-    const jchar* chars = (name.Get() != nullptr) ? name->GetValue() : nullptr;
-    bool is_compressed = (name.Get() != nullptr) ? name->IsCompressed() : false;
+    size_t char_count = (name != nullptr) ? name->GetLength() : 0;
+    const jchar* chars = (name != nullptr) ? name->GetValue() : nullptr;
+    bool is_compressed = (name != nullptr) ? name->IsCompressed() : false;
 
     std::vector<uint8_t> bytes;
     JDWP::Append4BE(bytes, t->GetThreadId());
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index 8b0c51c..e58c6f5 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -53,7 +53,7 @@
     ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700));
 
     // Verify the environment is as we expect
-    uint32_t checksum;
+    std::vector<uint32_t> checksums;
     std::string error_msg;
     ASSERT_TRUE(OS::FileExists(GetSystemImageFile().c_str()))
       << "Expected pre-compiled boot image to be at: " << GetSystemImageFile();
@@ -61,7 +61,7 @@
       << "Expected dex file to be at: " << GetDexSrc1();
     ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str()))
       << "Expected stripped dex file to be at: " << GetStrippedDexSrc1();
-    ASSERT_FALSE(DexFile::GetChecksum(GetStrippedDexSrc1().c_str(), &checksum, &error_msg))
+    ASSERT_FALSE(DexFile::GetMultiDexChecksums(GetStrippedDexSrc1().c_str(), &checksums, &error_msg))
       << "Expected stripped dex file to be stripped: " << GetStrippedDexSrc1();
     ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str()))
       << "Expected dex file to be at: " << GetDexSrc2();
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index f59420d..b6a2e09 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -72,23 +72,13 @@
   uint8_t type_;
 };
 
-bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg) {
-  CHECK(checksum != nullptr);
+bool DexFile::GetMultiDexChecksums(const char* filename,
+                                   std::vector<uint32_t>* checksums,
+                                   std::string* error_msg) {
+  CHECK(checksums != nullptr);
   uint32_t magic;
 
-  // Strip ":...", which is the location
-  const char* zip_entry_name = kClassesDex;
-  const char* file_part = filename;
-  std::string file_part_storage;
-
-  if (DexFile::IsMultiDexLocation(filename)) {
-    file_part_storage = GetBaseLocation(filename);
-    file_part = file_part_storage.c_str();
-    zip_entry_name = filename + file_part_storage.size() + 1;
-    DCHECK_EQ(zip_entry_name[-1], kMultiDexSeparator);
-  }
-
-  File fd = OpenAndReadMagic(file_part, &magic, error_msg);
+  File fd = OpenAndReadMagic(filename, &magic, error_msg);
   if (fd.Fd() == -1) {
     DCHECK(!error_msg->empty());
     return false;
@@ -97,17 +87,25 @@
     std::unique_ptr<ZipArchive> zip_archive(
         ZipArchive::OpenFromFd(fd.Release(), filename, error_msg));
     if (zip_archive.get() == nullptr) {
-      *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", file_part,
+      *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", filename,
                                 error_msg->c_str());
       return false;
     }
-    std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name, error_msg));
+
+    uint32_t i = 0;
+    std::string zip_entry_name = GetMultiDexClassesDexName(i++);
+    std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg));
     if (zip_entry.get() == nullptr) {
-      *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", file_part,
-                                zip_entry_name, error_msg->c_str());
+      *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename,
+          zip_entry_name.c_str(), error_msg->c_str());
       return false;
     }
-    *checksum = zip_entry->GetCrc32();
+
+    do {
+      checksums->push_back(zip_entry->GetCrc32());
+      zip_entry_name = DexFile::GetMultiDexClassesDexName(i++);
+      zip_entry.reset(zip_archive->Find(zip_entry_name.c_str(), error_msg));
+    } while (zip_entry.get() != nullptr);
     return true;
   }
   if (IsDexMagic(magic)) {
@@ -116,7 +114,7 @@
     if (dex_file.get() == nullptr) {
       return false;
     }
-    *checksum = dex_file->GetHeader().checksum_;
+    checksums->push_back(dex_file->GetHeader().checksum_);
     return true;
   }
   *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
@@ -333,7 +331,32 @@
     *error_code = ZipOpenErrorCode::kDexFileError;
     return nullptr;
   }
-  std::unique_ptr<MemMap> map(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg));
+
+  std::unique_ptr<MemMap> map;
+  if (zip_entry->IsUncompressed()) {
+    if (!zip_entry->IsAlignedTo(alignof(Header))) {
+      // Do not mmap unaligned ZIP entries because
+      // doing so would fail dex verification which requires 4 byte alignment.
+      LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
+                   << "please zipalign to " << alignof(Header) << " bytes. "
+                   << "Falling back to extracting file.";
+    } else {
+      // Map uncompressed files within zip as file-backed to avoid a dirty copy.
+      map.reset(zip_entry->MapDirectlyFromFile(location.c_str(), /*out*/error_msg));
+      if (map == nullptr) {
+        LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
+                     << "is your ZIP file corrupted? Falling back to extraction.";
+        // Try again with Extraction which still has a chance of recovery.
+      }
+    }
+  }
+
+  if (map == nullptr) {
+    // Default path for compressed ZIP entries,
+    // and fallback for stored ZIP entries.
+    map.reset(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg));
+  }
+
   if (map == nullptr) {
     *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
                               error_msg->c_str());
@@ -415,7 +438,7 @@
                                                                          &error_code));
       if (next_dex_file.get() == nullptr) {
         if (error_code != ZipOpenErrorCode::kEntryNotFound) {
-          LOG(WARNING) << error_msg;
+          LOG(WARNING) << "Zip open failed: " << *error_msg;
         }
         break;
       } else {
@@ -497,9 +520,19 @@
       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_)),
+      method_handles_(nullptr),
+      num_method_handles_(0),
+      call_site_ids_(nullptr),
+      num_call_site_ids_(0),
       oat_dex_file_(oat_dex_file) {
   CHECK(begin_ != nullptr) << GetLocation();
   CHECK_GT(size_, 0U) << GetLocation();
+  // Check base (=header) alignment.
+  // Must be 4-byte aligned to avoid undefined behavior when accessing
+  // any of the sections via a pointer.
+  CHECK_ALIGNED(begin_, alignof(Header));
+
+  InitializeSectionsFromMapList();
 }
 
 DexFile::~DexFile() {
@@ -540,6 +573,29 @@
   return true;
 }
 
+void DexFile::InitializeSectionsFromMapList() {
+  const MapList* map_list = reinterpret_cast<const MapList*>(begin_ + header_->map_off_);
+  const size_t count = map_list->size_;
+
+  size_t map_limit = header_->map_off_ + count * sizeof(MapItem);
+  if (header_->map_off_ >= map_limit || map_limit > size_) {
+    // Overflow or out out of bounds. The dex file verifier runs after
+    // this method and will reject the file as it is malformed.
+    return;
+  }
+
+  for (size_t i = 0; i < count; ++i) {
+    const MapItem& map_item = map_list->list_[i];
+    if (map_item.type_ == kDexTypeMethodHandleItem) {
+      method_handles_ = reinterpret_cast<const MethodHandleItem*>(begin_ + map_item.offset_);
+      num_method_handles_ = map_item.size_;
+    } else if (map_item.type_ == kDexTypeCallSiteIdItem) {
+      call_site_ids_ = reinterpret_cast<const CallSiteIdItem*>(begin_ + map_item.offset_);
+      num_call_site_ids_ = map_item.size_;
+    }
+  }
+}
+
 bool DexFile::IsMagicValid(const uint8_t* magic) {
   return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0);
 }
@@ -1339,24 +1395,20 @@
   }
 }
 
-EncodedStaticFieldValueIterator::EncodedStaticFieldValueIterator(const DexFile& dex_file,
-                                                                 const DexFile::ClassDef& class_def)
+EncodedArrayValueIterator::EncodedArrayValueIterator(const DexFile& dex_file,
+                                                     const uint8_t* array_data)
     : dex_file_(dex_file),
       array_size_(),
       pos_(-1),
+      ptr_(array_data),
       type_(kByte) {
-  ptr_ = dex_file_.GetEncodedStaticFieldValuesArray(class_def);
-  if (ptr_ == nullptr) {
-    array_size_ = 0;
-  } else {
-    array_size_ = DecodeUnsignedLeb128(&ptr_);
-  }
+  array_size_ = (ptr_ != nullptr) ? DecodeUnsignedLeb128(&ptr_) : 0;
   if (array_size_ > 0) {
     Next();
   }
 }
 
-void EncodedStaticFieldValueIterator::Next() {
+void EncodedArrayValueIterator::Next() {
   pos_++;
   if (pos_ >= array_size_) {
     return;
@@ -1396,6 +1448,8 @@
     break;
   case kString:
   case kType:
+  case kMethodType:
+  case kMethodHandle:
     jval_.i = DexFile::ReadUnsignedInt(ptr_, value_arg, false);
     break;
   case kField:
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index cb7f174..58b8e79 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -103,7 +103,7 @@
   };
 
   // Map item type codes.
-  enum {
+  enum MapItemType : uint16_t {  // private
     kDexTypeHeaderItem               = 0x0000,
     kDexTypeStringIdItem             = 0x0001,
     kDexTypeTypeIdItem               = 0x0002,
@@ -111,6 +111,8 @@
     kDexTypeFieldIdItem              = 0x0004,
     kDexTypeMethodIdItem             = 0x0005,
     kDexTypeClassDefItem             = 0x0006,
+    kDexTypeCallSiteIdItem           = 0x0007,
+    kDexTypeMethodHandleItem         = 0x0008,
     kDexTypeMapList                  = 0x1000,
     kDexTypeTypeList                 = 0x1001,
     kDexTypeAnnotationSetRefList     = 0x1002,
@@ -260,6 +262,37 @@
     DISALLOW_COPY_AND_ASSIGN(TypeList);
   };
 
+  // MethodHandle Types
+  enum class MethodHandleType : uint16_t {  // private
+    kStaticPut         = 0x0000,  // a setter for a given static field.
+    kStaticGet         = 0x0001,  // a getter for a given static field.
+    kInstancePut       = 0x0002,  // a setter for a given instance field.
+    kInstanceGet       = 0x0003,  // a getter for a given instance field.
+    kInvokeStatic      = 0x0004,  // an invoker for a given static method.
+    kInvokeInstance    = 0x0005,  // invoke_instance : an invoker for a given instance method. This
+                                  // can be any non-static method on any class (or interface) except
+                                  // for “<init>”.
+    kInvokeConstructor = 0x0006,  // an invoker for a given constructor.
+    kLast = kInvokeConstructor
+  };
+
+  // raw method_handle_item
+  struct MethodHandleItem {
+    uint16_t method_handle_type_;
+    uint16_t reserved1_;            // Reserved for future use.
+    uint16_t field_or_method_idx_;  // Field index for accessors, method index otherwise.
+    uint16_t reserved2_;            // Reserved for future use.
+   private:
+    DISALLOW_COPY_AND_ASSIGN(MethodHandleItem);
+  };
+
+  // raw call_site_id_item
+  struct CallSiteIdItem {
+    uint32_t data_off_;  // Offset into data section pointing to encoded array items.
+   private:
+    DISALLOW_COPY_AND_ASSIGN(CallSiteIdItem);
+  };
+
   // Raw code_item.
   struct CodeItem {
     uint16_t registers_size_;            // the number of registers used by this code
@@ -302,6 +335,8 @@
     kDexAnnotationLong          = 0x06,
     kDexAnnotationFloat         = 0x10,
     kDexAnnotationDouble        = 0x11,
+    kDexAnnotationMethodType    = 0x15,
+    kDexAnnotationMethodHandle  = 0x16,
     kDexAnnotationString        = 0x17,
     kDexAnnotationType          = 0x18,
     kDexAnnotationField         = 0x19,
@@ -389,11 +424,18 @@
 
   struct AnnotationValue;
 
-  // 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.
-  // Return true if the checksum could be found, false otherwise.
-  static bool GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg);
+  // Returns the checksums of a file for comparison with GetLocationChecksum().
+  // For .dex files, this is the single header checksum.
+  // For zip files, this is the zip entry CRC32 checksum for classes.dex and
+  // each additional multidex entry classes2.dex, classes3.dex, etc.
+  // Return true if the checksums could be found, false otherwise.
+  static bool GetMultiDexChecksums(const char* filename,
+                                   std::vector<uint32_t>* checksums,
+                                   std::string* error_msg);
+
+  // Check whether a location denotes a multidex dex file. This is a very simple check: returns
+  // whether the string contains the separator character.
+  static bool IsMultiDexLocation(const char* location);
 
   // Opens .dex file, backed by existing memory
   static std::unique_ptr<const DexFile> Open(const uint8_t* base,
@@ -683,6 +725,24 @@
     }
   }
 
+  uint32_t NumMethodHandles() const {
+    return num_method_handles_;
+  }
+
+  const MethodHandleItem& GetMethodHandle(uint32_t idx) const {
+    CHECK_LT(idx, NumMethodHandles());
+    return method_handles_[idx];
+  }
+
+  uint32_t NumCallSiteIds() const {
+    return num_call_site_ids_;
+  }
+
+  const CallSiteIdItem& GetCallSiteId(uint32_t idx) const {
+    CHECK_LT(idx, NumCallSiteIds());
+    return call_site_ids_[idx];
+  }
+
   // Returns a pointer to the raw memory mapped class_data_item
   const uint8_t* GetClassData(const ClassDef& class_def) const {
     if (class_def.class_data_off_ == 0) {
@@ -761,6 +821,10 @@
     }
   }
 
+  const uint8_t* GetCallSiteEncodedValuesArray(const CallSiteIdItem& call_site_id) const {
+    return begin_ + call_site_id.data_off_;
+  }
+
   static const TryItem* GetTryItems(const CodeItem& code_item, uint32_t offset);
 
   // Get the base of the encoded data for the given DexCode.
@@ -1101,9 +1165,8 @@
   // Returns true if the header magic and version numbers are of the expected values.
   bool CheckMagicAndVersion(std::string* error_msg) const;
 
-  // Check whether a location denotes a multidex dex file. This is a very simple check: returns
-  // whether the string contains the separator character.
-  static bool IsMultiDexLocation(const char* location);
+  // Initialize section info for sections only found in map. Returns true on success.
+  void InitializeSectionsFromMapList();
 
   // The base address of the memory mapping.
   const uint8_t* const begin_;
@@ -1143,6 +1206,18 @@
   // Points to the base of the class definition list.
   const ClassDef* const class_defs_;
 
+  // Points to the base of the method handles list.
+  const MethodHandleItem* method_handles_;
+
+  // Number of elements in the method handles list.
+  size_t num_method_handles_;
+
+  // Points to the base of the call sites id list.
+  const CallSiteIdItem* call_site_ids_;
+
+  // Number of elements in the call sites list.
+  size_t num_call_site_ids_;
+
   // If this dex file was loaded from an oat file, oat_dex_file_ contains a
   // pointer to the OatDexFile it was loaded from. Otherwise oat_dex_file_ is
   // null.
@@ -1409,32 +1484,33 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(ClassDataItemIterator);
 };
 
-class EncodedStaticFieldValueIterator {
+class EncodedArrayValueIterator {
  public:
-  EncodedStaticFieldValueIterator(const DexFile& dex_file,
-                                  const DexFile::ClassDef& class_def);
+  EncodedArrayValueIterator(const DexFile& dex_file, const uint8_t* array_data);
 
   bool HasNext() const { return pos_ < array_size_; }
 
   void Next();
 
   enum ValueType {
-    kByte = 0x00,
-    kShort = 0x02,
-    kChar = 0x03,
-    kInt = 0x04,
-    kLong = 0x06,
-    kFloat = 0x10,
-    kDouble = 0x11,
-    kString = 0x17,
-    kType = 0x18,
-    kField = 0x19,
-    kMethod = 0x1a,
-    kEnum = 0x1b,
-    kArray = 0x1c,
-    kAnnotation = 0x1d,
-    kNull = 0x1e,
-    kBoolean = 0x1f
+    kByte         = 0x00,
+    kShort        = 0x02,
+    kChar         = 0x03,
+    kInt          = 0x04,
+    kLong         = 0x06,
+    kFloat        = 0x10,
+    kDouble       = 0x11,
+    kMethodType   = 0x15,
+    kMethodHandle = 0x16,
+    kString       = 0x17,
+    kType         = 0x18,
+    kField        = 0x19,
+    kMethod       = 0x1a,
+    kEnum         = 0x1b,
+    kArray        = 0x1c,
+    kAnnotation   = 0x1d,
+    kNull         = 0x1e,
+    kBoolean      = 0x1f,
   };
 
   ValueType GetValueType() const { return type_; }
@@ -1452,10 +1528,38 @@
   jvalue jval_;  // Value of current encoded value.
 
  private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(EncodedArrayValueIterator);
+};
+std::ostream& operator<<(std::ostream& os, const EncodedArrayValueIterator::ValueType& code);
+
+class EncodedStaticFieldValueIterator : public EncodedArrayValueIterator {
+ public:
+  EncodedStaticFieldValueIterator(const DexFile& dex_file,
+                                  const DexFile::ClassDef& class_def)
+      : EncodedArrayValueIterator(dex_file,
+                                  dex_file.GetEncodedStaticFieldValuesArray(class_def))
+  {}
+
+ private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(EncodedStaticFieldValueIterator);
 };
 std::ostream& operator<<(std::ostream& os, const EncodedStaticFieldValueIterator::ValueType& code);
 
+class CallSiteArrayValueIterator : public EncodedArrayValueIterator {
+ public:
+  CallSiteArrayValueIterator(const DexFile& dex_file,
+                             const DexFile::CallSiteIdItem& call_site_id)
+      : EncodedArrayValueIterator(dex_file,
+                                  dex_file.GetCallSiteEncodedValuesArray(call_site_id))
+  {}
+
+  uint32_t Size() const { return array_size_; }
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(CallSiteArrayValueIterator);
+};
+std::ostream& operator<<(std::ostream& os, const CallSiteArrayValueIterator::ValueType& code);
+
 class CatchHandlerIterator {
   public:
     CatchHandlerIterator(const DexFile::CodeItem& code_item, uint32_t address);
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index 16a447b..a95f94c 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -252,7 +252,7 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   Handle<mirror::Class> annotation_class(hs.NewHandle(
       class_linker->ResolveType(klass->GetDexFile(), dex::TypeIndex(type_index), klass.Get())));
-  if (annotation_class.Get() == nullptr) {
+  if (annotation_class == nullptr) {
     LOG(INFO) << "Unable to resolve " << klass->PrettyClass() << " annotation class " << type_index;
     DCHECK(Thread::Current()->IsExceptionPending());
     Thread::Current()->ClearException();
@@ -481,7 +481,7 @@
       break;
     }
     case DexFile::kDexAnnotationArray:
-      if (result_style == DexFile::kAllRaw || array_class.Get() == nullptr) {
+      if (result_style == DexFile::kAllRaw || array_class == nullptr) {
         return false;
       } else {
         ScopedObjectAccessUnchecked soa(self);
@@ -491,7 +491,7 @@
         Handle<mirror::Array> new_array(hs.NewHandle(mirror::Array::Alloc<true>(
             self, array_class.Get(), size, array_class->GetComponentSizeShift(),
             Runtime::Current()->GetHeap()->GetCurrentAllocator())));
-        if (new_array.Get() == nullptr) {
+        if (new_array == nullptr) {
           LOG(ERROR) << "Annotation element array allocation failed with size " << size;
           return false;
         }
@@ -631,8 +631,8 @@
   }
   Handle<mirror::Method> method_object(hs.NewHandle(method_obj_ptr));
 
-  if (new_member.Get() == nullptr || string_name.Get() == nullptr ||
-      method_object.Get() == nullptr || method_return.Get() == nullptr) {
+  if (new_member == nullptr || string_name == nullptr ||
+      method_object == nullptr || method_return == nullptr) {
     LOG(ERROR) << StringPrintf("Failed creating annotation element (m=%p n=%p a=%p r=%p",
         new_member.Get(), string_name.Get(), method_object.Get(), method_return.Get());
     return nullptr;
@@ -740,7 +740,7 @@
   ObjPtr<mirror::Class> string_class = mirror::String::GetJavaLangString();
   Handle<mirror::Class> string_array_class(hs.NewHandle(
       Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class)));
-  if (string_array_class.Get() == nullptr) {
+  if (string_array_class == nullptr) {
     return nullptr;
   }
   mirror::Object* obj =
@@ -766,7 +766,7 @@
   ObjPtr<mirror::Class> class_class = mirror::Class::GetJavaLangClass();
   Handle<mirror::Class> class_array_class(hs.NewHandle(
       Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &class_class)));
-  if (class_array_class.Get() == nullptr) {
+  if (class_array_class == nullptr) {
     return nullptr;
   }
   mirror::Object* obj =
@@ -796,7 +796,7 @@
   uint32_t size = annotation_set->size_;
   Handle<mirror::ObjectArray<mirror::Object>> result(hs.NewHandle(
       mirror::ObjectArray<mirror::Object>::Alloc(self, annotation_array_class.Get(), size)));
-  if (result.Get() == nullptr) {
+  if (result == nullptr) {
     return nullptr;
   }
 
@@ -854,7 +854,7 @@
   }
   Handle<mirror::ObjectArray<mirror::Object>> annotation_array_array(hs.NewHandle(
       mirror::ObjectArray<mirror::Object>::Alloc(self, annotation_array_array_class, size)));
-  if (annotation_array_array.Get() == nullptr) {
+  if (annotation_array_array == nullptr) {
     LOG(ERROR) << "Annotation set ref array allocation failed";
     return nullptr;
   }
@@ -1056,7 +1056,7 @@
   ObjPtr<mirror::Class> string_class = mirror::String::GetJavaLangString();
   Handle<mirror::Class> string_array_class(hs.NewHandle(
       Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class)));
-  if (UNLIKELY(string_array_class.Get() == nullptr)) {
+  if (UNLIKELY(string_array_class == nullptr)) {
     return false;
   }
 
@@ -1067,13 +1067,13 @@
                                       "names",
                                       string_array_class,
                                       DexFile::kDexAnnotationArray));
-  if (names_obj.Get() == nullptr) {
+  if (names_obj == nullptr) {
     return false;
   }
 
   // Extract the parameters' access flags int[].
   Handle<mirror::Class> int_array_class(hs.NewHandle(mirror::IntArray::GetArrayClass()));
-  if (UNLIKELY(int_array_class.Get() == nullptr)) {
+  if (UNLIKELY(int_array_class == nullptr)) {
     return false;
   }
   Handle<mirror::Object> access_flags_obj =
@@ -1082,7 +1082,7 @@
                                       "accessFlags",
                                       int_array_class,
                                       DexFile::kDexAnnotationArray));
-  if (access_flags_obj.Get() == nullptr) {
+  if (access_flags_obj == nullptr) {
     return false;
   }
 
@@ -1146,7 +1146,7 @@
   ObjPtr<mirror::Class> class_class = mirror::Class::GetJavaLangClass();
   Handle<mirror::Class> class_array_class(hs.NewHandle(
       Runtime::Current()->GetClassLinker()->FindArrayClass(hs.Self(), &class_class)));
-  if (class_array_class.Get() == nullptr) {
+  if (class_array_class == nullptr) {
     return nullptr;
   }
   mirror::Object* obj =
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 9dca4c0..9131715 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -326,12 +326,32 @@
 }
 
 TEST_F(DexFileTest, GetChecksum) {
-  uint32_t checksum;
+  std::vector<uint32_t> checksums;
   ScopedObjectAccess soa(Thread::Current());
   std::string error_msg;
-  EXPECT_TRUE(DexFile::GetChecksum(GetLibCoreDexFileNames()[0].c_str(), &checksum, &error_msg))
+  EXPECT_TRUE(DexFile::GetMultiDexChecksums(GetLibCoreDexFileNames()[0].c_str(), &checksums, &error_msg))
       << error_msg;
-  EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksum);
+  ASSERT_EQ(1U, checksums.size());
+  EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksums[0]);
+}
+
+TEST_F(DexFileTest, GetMultiDexChecksums) {
+  std::string error_msg;
+  std::vector<uint32_t> checksums;
+  std::string multidex_file = GetTestDexFileName("MultiDex");
+  EXPECT_TRUE(DexFile::GetMultiDexChecksums(multidex_file.c_str(),
+                                            &checksums,
+                                            &error_msg)) << error_msg;
+
+  std::vector<std::unique_ptr<const DexFile>> dexes = OpenTestDexFiles("MultiDex");
+  ASSERT_EQ(2U, dexes.size());
+  ASSERT_EQ(2U, checksums.size());
+
+  EXPECT_EQ(dexes[0]->GetLocation(), DexFile::GetMultiDexLocation(0, multidex_file.c_str()));
+  EXPECT_EQ(dexes[0]->GetLocationChecksum(), checksums[0]);
+
+  EXPECT_EQ(dexes[1]->GetLocation(), DexFile::GetMultiDexLocation(1, multidex_file.c_str()));
+  EXPECT_EQ(dexes[1]->GetLocationChecksum(), checksums[1]);
 }
 
 TEST_F(DexFileTest, ClassDefs) {
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 318123e..0b3f16a 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -46,8 +46,8 @@
   return (high == 0);
 }
 
-static uint32_t MapTypeToBitMask(uint32_t map_type) {
-  switch (map_type) {
+static uint32_t MapTypeToBitMask(DexFile::MapItemType map_item_type) {
+  switch (map_item_type) {
     case DexFile::kDexTypeHeaderItem:               return 1 << 0;
     case DexFile::kDexTypeStringIdItem:             return 1 << 1;
     case DexFile::kDexTypeTypeIdItem:               return 1 << 2;
@@ -55,23 +55,25 @@
     case DexFile::kDexTypeFieldIdItem:              return 1 << 4;
     case DexFile::kDexTypeMethodIdItem:             return 1 << 5;
     case DexFile::kDexTypeClassDefItem:             return 1 << 6;
-    case DexFile::kDexTypeMapList:                  return 1 << 7;
-    case DexFile::kDexTypeTypeList:                 return 1 << 8;
-    case DexFile::kDexTypeAnnotationSetRefList:     return 1 << 9;
-    case DexFile::kDexTypeAnnotationSetItem:        return 1 << 10;
-    case DexFile::kDexTypeClassDataItem:            return 1 << 11;
-    case DexFile::kDexTypeCodeItem:                 return 1 << 12;
-    case DexFile::kDexTypeStringDataItem:           return 1 << 13;
-    case DexFile::kDexTypeDebugInfoItem:            return 1 << 14;
-    case DexFile::kDexTypeAnnotationItem:           return 1 << 15;
-    case DexFile::kDexTypeEncodedArrayItem:         return 1 << 16;
-    case DexFile::kDexTypeAnnotationsDirectoryItem: return 1 << 17;
+    case DexFile::kDexTypeCallSiteIdItem:           return 1 << 7;
+    case DexFile::kDexTypeMethodHandleItem:         return 1 << 8;
+    case DexFile::kDexTypeMapList:                  return 1 << 9;
+    case DexFile::kDexTypeTypeList:                 return 1 << 10;
+    case DexFile::kDexTypeAnnotationSetRefList:     return 1 << 11;
+    case DexFile::kDexTypeAnnotationSetItem:        return 1 << 12;
+    case DexFile::kDexTypeClassDataItem:            return 1 << 13;
+    case DexFile::kDexTypeCodeItem:                 return 1 << 14;
+    case DexFile::kDexTypeStringDataItem:           return 1 << 15;
+    case DexFile::kDexTypeDebugInfoItem:            return 1 << 16;
+    case DexFile::kDexTypeAnnotationItem:           return 1 << 17;
+    case DexFile::kDexTypeEncodedArrayItem:         return 1 << 18;
+    case DexFile::kDexTypeAnnotationsDirectoryItem: return 1 << 19;
   }
   return 0;
 }
 
-static bool IsDataSectionType(uint32_t map_type) {
-  switch (map_type) {
+static bool IsDataSectionType(DexFile::MapItemType map_item_type) {
+  switch (map_item_type) {
     case DexFile::kDexTypeHeaderItem:
     case DexFile::kDexTypeStringIdItem:
     case DexFile::kDexTypeTypeIdItem:
@@ -80,6 +82,20 @@
     case DexFile::kDexTypeMethodIdItem:
     case DexFile::kDexTypeClassDefItem:
       return false;
+    case DexFile::kDexTypeCallSiteIdItem:
+    case DexFile::kDexTypeMethodHandleItem:
+    case DexFile::kDexTypeMapList:
+    case DexFile::kDexTypeTypeList:
+    case DexFile::kDexTypeAnnotationSetRefList:
+    case DexFile::kDexTypeAnnotationSetItem:
+    case DexFile::kDexTypeClassDataItem:
+    case DexFile::kDexTypeCodeItem:
+    case DexFile::kDexTypeStringDataItem:
+    case DexFile::kDexTypeDebugInfoItem:
+    case DexFile::kDexTypeAnnotationItem:
+    case DexFile::kDexTypeEncodedArrayItem:
+    case DexFile::kDexTypeAnnotationsDirectoryItem:
+      return true;
   }
   return true;
 }
@@ -455,7 +471,8 @@
       return false;
     }
 
-    if (IsDataSectionType(item->type_)) {
+    DexFile::MapItemType item_type = static_cast<DexFile::MapItemType>(item->type_);
+    if (IsDataSectionType(item_type)) {
       uint32_t icount = item->size_;
       if (UNLIKELY(icount > data_items_left)) {
         ErrorStringPrintf("Too many items in data section: %ud", data_item_count + icount);
@@ -465,7 +482,7 @@
       data_item_count += icount;
     }
 
-    uint32_t bit = MapTypeToBitMask(item->type_);
+    uint32_t bit = MapTypeToBitMask(item_type);
 
     if (UNLIKELY(bit == 0)) {
       ErrorStringPrintf("Unknown map section type %x", item->type_);
@@ -837,6 +854,28 @@
         return false;
       }
       break;
+    case DexFile::kDexAnnotationMethodType: {
+      if (UNLIKELY(value_arg > 3)) {
+        ErrorStringPrintf("Bad encoded_value method type size %x", value_arg);
+        return false;
+      }
+      uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
+      if (!CheckIndex(idx, header_->proto_ids_size_, "method_type value")) {
+        return false;
+      }
+      break;
+    }
+    case DexFile::kDexAnnotationMethodHandle: {
+      if (UNLIKELY(value_arg > 3)) {
+        ErrorStringPrintf("Bad encoded_value method handle size %x", value_arg);
+        return false;
+      }
+      uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
+      if (!CheckIndex(idx, dex_file_->NumMethodHandles(), "method_handle value")) {
+        return false;
+      }
+      break;
+    }
     default:
       ErrorStringPrintf("Bogus encoded_value value_type %x", value_type);
       return false;
@@ -1455,7 +1494,7 @@
 }
 
 bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_count,
-                                               uint16_t type) {
+                                               DexFile::MapItemType type) {
   // Get the right alignment mask for the type of section.
   size_t alignment_mask;
   switch (type) {
@@ -1481,6 +1520,7 @@
     }
 
     // Check depending on the section type.
+    const uint8_t* start_ptr = ptr_;
     switch (type) {
       case DexFile::kDexTypeStringIdItem: {
         if (!CheckListSize(ptr_, 1, sizeof(DexFile::StringId), "string_ids")) {
@@ -1524,6 +1564,20 @@
         ptr_ += sizeof(DexFile::ClassDef);
         break;
       }
+      case DexFile::kDexTypeCallSiteIdItem: {
+        if (!CheckListSize(ptr_, 1, sizeof(DexFile::CallSiteIdItem), "call_site_ids")) {
+          return false;
+        }
+        ptr_ += sizeof(DexFile::CallSiteIdItem);
+        break;
+      }
+      case DexFile::kDexTypeMethodHandleItem: {
+        if (!CheckListSize(ptr_, 1, sizeof(DexFile::MethodHandleItem), "method_handles")) {
+          return false;
+        }
+        ptr_ += sizeof(DexFile::MethodHandleItem);
+        break;
+      }
       case DexFile::kDexTypeTypeList: {
         if (!CheckList(sizeof(DexFile::TypeItem), "type_list", &ptr_)) {
           return false;
@@ -1584,9 +1638,14 @@
         }
         break;
       }
-      default:
-        ErrorStringPrintf("Unknown map item type %x", type);
-        return false;
+      case DexFile::kDexTypeHeaderItem:
+      case DexFile::kDexTypeMapList:
+        break;
+    }
+
+    if (start_ptr == ptr_) {
+      ErrorStringPrintf("Unknown map item type %x", type);
+      return false;
     }
 
     if (IsDataSectionType(type)) {
@@ -1610,7 +1669,9 @@
   return true;
 }
 
-bool DexFileVerifier::CheckIntraIdSection(size_t offset, uint32_t count, uint16_t type) {
+bool DexFileVerifier::CheckIntraIdSection(size_t offset,
+                                          uint32_t count,
+                                          DexFile::MapItemType type) {
   uint32_t expected_offset;
   uint32_t expected_size;
 
@@ -1658,7 +1719,9 @@
   return CheckIntraSectionIterate(offset, count, type);
 }
 
-bool DexFileVerifier::CheckIntraDataSection(size_t offset, uint32_t count, uint16_t type) {
+bool DexFileVerifier::CheckIntraDataSection(size_t offset,
+                                            uint32_t count,
+                                            DexFile::MapItemType type) {
   size_t data_start = header_->data_off_;
   size_t data_end = data_start + header_->data_size_;
 
@@ -1684,16 +1747,16 @@
 bool DexFileVerifier::CheckIntraSection() {
   const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ + header_->map_off_);
   const DexFile::MapItem* item = map->list_;
-
-  uint32_t count = map->size_;
   size_t offset = 0;
+  uint32_t count = map->size_;
   ptr_ = begin_;
 
   // Check the items listed in the map.
   while (count--) {
+    const size_t current_offset = offset;
     uint32_t section_offset = item->offset_;
     uint32_t section_count = item->size_;
-    uint16_t type = item->type_;
+    DexFile::MapItemType type = static_cast<DexFile::MapItemType>(item->type_);
 
     // Check for padding and overlap between items.
     if (!CheckPadding(offset, section_offset)) {
@@ -1741,6 +1804,11 @@
         ptr_ += sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem));
         offset = section_offset + sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem));
         break;
+      case DexFile::kDexTypeMethodHandleItem:
+      case DexFile::kDexTypeCallSiteIdItem:
+        CheckIntraSectionIterate(section_offset, section_count, type);
+        offset = ptr_ - begin_;
+        break;
       case DexFile::kDexTypeTypeList:
       case DexFile::kDexTypeAnnotationSetRefList:
       case DexFile::kDexTypeAnnotationSetItem:
@@ -1756,7 +1824,9 @@
         }
         offset = ptr_ - begin_;
         break;
-      default:
+    }
+
+    if (offset == current_offset) {
         ErrorStringPrintf("Unknown map item type %x", type);
         return false;
     }
@@ -2237,6 +2307,92 @@
   return true;
 }
 
+bool DexFileVerifier::CheckInterCallSiteIdItem() {
+  const DexFile::CallSiteIdItem* item = reinterpret_cast<const DexFile::CallSiteIdItem*>(ptr_);
+
+  // Check call site referenced by item is in encoded array section.
+  if (!CheckOffsetToTypeMap(item->data_off_, DexFile::kDexTypeEncodedArrayItem)) {
+    ErrorStringPrintf("Invalid offset in CallSideIdItem");
+    return false;
+  }
+
+  CallSiteArrayValueIterator it(*dex_file_, *item);
+
+  // Check Method Handle
+  if (!it.HasNext() || it.GetValueType() != EncodedArrayValueIterator::ValueType::kMethodHandle) {
+    ErrorStringPrintf("CallSiteArray missing method handle");
+    return false;
+  }
+
+  uint32_t handle_index = static_cast<uint32_t>(it.GetJavaValue().i);
+  if (handle_index >= dex_file_->NumMethodHandles()) {
+    ErrorStringPrintf("CallSite has bad method handle id: %x", handle_index);
+    return false;
+  }
+
+  // Check target method name.
+  it.Next();
+  if (!it.HasNext() ||
+      it.GetValueType() != EncodedArrayValueIterator::ValueType::kString) {
+    ErrorStringPrintf("CallSiteArray missing target method name");
+    return false;
+  }
+
+  uint32_t name_index = static_cast<uint32_t>(it.GetJavaValue().i);
+  if (name_index >= dex_file_->NumStringIds()) {
+    ErrorStringPrintf("CallSite has bad method name id: %x", name_index);
+    return false;
+  }
+
+  // Check method type.
+  it.Next();
+  if (!it.HasNext() ||
+      it.GetValueType() != EncodedArrayValueIterator::ValueType::kMethodType) {
+    ErrorStringPrintf("CallSiteArray missing method type");
+    return false;
+  }
+
+  uint32_t proto_index = static_cast<uint32_t>(it.GetJavaValue().i);
+  if (proto_index >= dex_file_->NumProtoIds()) {
+    ErrorStringPrintf("CallSite has bad method type: %x", proto_index);
+    return false;
+  }
+
+  ptr_ += sizeof(DexFile::CallSiteIdItem);
+  return true;
+}
+
+bool DexFileVerifier::CheckInterMethodHandleItem() {
+  const DexFile::MethodHandleItem* item = reinterpret_cast<const DexFile::MethodHandleItem*>(ptr_);
+
+  DexFile::MethodHandleType method_handle_type =
+      static_cast<DexFile::MethodHandleType>(item->method_handle_type_);
+  if (method_handle_type > DexFile::MethodHandleType::kLast) {
+    ErrorStringPrintf("Bad method handle type %x", item->method_handle_type_);
+    return false;
+  }
+
+  uint32_t index = item->field_or_method_idx_;
+  switch (method_handle_type) {
+    case DexFile::MethodHandleType::kStaticPut:
+    case DexFile::MethodHandleType::kStaticGet:
+    case DexFile::MethodHandleType::kInstancePut:
+    case DexFile::MethodHandleType::kInstanceGet: {
+      LOAD_FIELD(field, index, "method_handle_item field_idx", return false);
+      break;
+    }
+    case DexFile::MethodHandleType::kInvokeStatic:
+    case DexFile::MethodHandleType::kInvokeInstance:
+    case DexFile::MethodHandleType::kInvokeConstructor: {
+      LOAD_METHOD(method, index, "method_handle_item method_idx", return false);
+      break;
+    }
+  }
+
+  ptr_ += sizeof(DexFile::MethodHandleItem);
+  return true;
+}
+
 bool DexFileVerifier::CheckInterAnnotationSetRefList() {
   const DexFile::AnnotationSetRefList* list =
       reinterpret_cast<const DexFile::AnnotationSetRefList*>(ptr_);
@@ -2386,7 +2542,9 @@
   return true;
 }
 
-bool DexFileVerifier::CheckInterSectionIterate(size_t offset, uint32_t count, uint16_t type) {
+bool DexFileVerifier::CheckInterSectionIterate(size_t offset,
+                                               uint32_t count,
+                                               DexFile::MapItemType type) {
   // Get the right alignment mask for the type of section.
   size_t alignment_mask;
   switch (type) {
@@ -2405,8 +2563,22 @@
     ptr_ = begin_ + new_offset;
     const uint8_t* prev_ptr = ptr_;
 
+    if (MapTypeToBitMask(type) == 0) {
+      ErrorStringPrintf("Unknown map item type %x", type);
+      return false;
+    }
+
     // Check depending on the section type.
     switch (type) {
+      case DexFile::kDexTypeHeaderItem:
+      case DexFile::kDexTypeMapList:
+      case DexFile::kDexTypeTypeList:
+      case DexFile::kDexTypeCodeItem:
+      case DexFile::kDexTypeStringDataItem:
+      case DexFile::kDexTypeDebugInfoItem:
+      case DexFile::kDexTypeAnnotationItem:
+      case DexFile::kDexTypeEncodedArrayItem:
+        break;
       case DexFile::kDexTypeStringIdItem: {
         if (!CheckInterStringIdItem()) {
           return false;
@@ -2451,6 +2623,18 @@
         }
         break;
       }
+      case DexFile::kDexTypeCallSiteIdItem: {
+        if (!CheckInterCallSiteIdItem()) {
+          return false;
+        }
+        break;
+      }
+      case DexFile::kDexTypeMethodHandleItem: {
+        if (!CheckInterMethodHandleItem()) {
+          return false;
+        }
+        break;
+      }
       case DexFile::kDexTypeAnnotationSetRefList: {
         if (!CheckInterAnnotationSetRefList()) {
           return false;
@@ -2483,9 +2667,6 @@
         }
         break;
       }
-      default:
-        ErrorStringPrintf("Unknown map item type %x", type);
-        return false;
     }
 
     previous_item_ = prev_ptr;
@@ -2504,7 +2685,8 @@
   while (count--) {
     uint32_t section_offset = item->offset_;
     uint32_t section_count = item->size_;
-    uint16_t type = item->type_;
+    DexFile::MapItemType type = static_cast<DexFile::MapItemType>(item->type_);
+    bool found = false;
 
     switch (type) {
       case DexFile::kDexTypeHeaderItem:
@@ -2515,6 +2697,7 @@
       case DexFile::kDexTypeDebugInfoItem:
       case DexFile::kDexTypeAnnotationItem:
       case DexFile::kDexTypeEncodedArrayItem:
+        found = true;
         break;
       case DexFile::kDexTypeStringIdItem:
       case DexFile::kDexTypeTypeIdItem:
@@ -2522,6 +2705,8 @@
       case DexFile::kDexTypeFieldIdItem:
       case DexFile::kDexTypeMethodIdItem:
       case DexFile::kDexTypeClassDefItem:
+      case DexFile::kDexTypeCallSiteIdItem:
+      case DexFile::kDexTypeMethodHandleItem:
       case DexFile::kDexTypeAnnotationSetRefList:
       case DexFile::kDexTypeAnnotationSetItem:
       case DexFile::kDexTypeClassDataItem:
@@ -2529,11 +2714,14 @@
         if (!CheckInterSectionIterate(section_offset, section_count, type)) {
           return false;
         }
+        found = true;
         break;
       }
-      default:
-        ErrorStringPrintf("Unknown map item type %x", type);
-        return false;
+    }
+
+    if (!found) {
+      ErrorStringPrintf("Unknown map item type %x", item->type_);
+      return false;
     }
 
     item++;
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index ae20613..71b316c 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -122,9 +122,9 @@
   bool CheckIntraAnnotationItem();
   bool CheckIntraAnnotationsDirectoryItem();
 
-  bool CheckIntraSectionIterate(size_t offset, uint32_t count, uint16_t type);
-  bool CheckIntraIdSection(size_t offset, uint32_t count, uint16_t type);
-  bool CheckIntraDataSection(size_t offset, uint32_t count, uint16_t type);
+  bool CheckIntraSectionIterate(size_t offset, uint32_t count, DexFile::MapItemType type);
+  bool CheckIntraIdSection(size_t offset, uint32_t count, DexFile::MapItemType type);
+  bool CheckIntraDataSection(size_t offset, uint32_t count, DexFile::MapItemType type);
   bool CheckIntraSection();
 
   bool CheckOffsetToTypeMap(size_t offset, uint16_t type);
@@ -140,12 +140,14 @@
   bool CheckInterFieldIdItem();
   bool CheckInterMethodIdItem();
   bool CheckInterClassDefItem();
+  bool CheckInterCallSiteIdItem();
+  bool CheckInterMethodHandleItem();
   bool CheckInterAnnotationSetRefList();
   bool CheckInterAnnotationSetItem();
   bool CheckInterClassDataItem();
   bool CheckInterAnnotationsDirectoryItem();
 
-  bool CheckInterSectionIterate(size_t offset, uint32_t count, uint16_t type);
+  bool CheckInterSectionIterate(size_t offset, uint32_t count, DexFile::MapItemType type);
   bool CheckInterSection();
 
   // Load a string by (type) index. Checks whether the index is in bounds, printing the error if
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index c56b200..7736f3d 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -1885,4 +1885,209 @@
                                        &error_msg));
 }
 
+static const char* kInvokeCustomDexFiles[] = {
+  // TODO(oth): Revisit this test when we have smali / dx support.
+  // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test001/Tests.java
+  "ZGV4CjAzOAAEj12s/acmmdGuDL92SWSBh6iLBjxgomWkCAAAcAAAAHhWNBIAAAAAAAAAALwHAAAx"
+  "AAAAcAAAABYAAAA0AQAACQAAAIwBAAADAAAA+AEAAAsAAAAQAgAAAQAAAHACAAAMBgAAmAIAAMID"
+  "AADKAwAAzQMAANIDAADhAwAA5AMAAOoDAAAfBAAAUgQAAIMEAAC4BAAA1AQAAOsEAAD+BAAAEgUA"
+  "ACYFAAA6BQAAUQUAAG4FAACTBQAAtAUAAN0FAAD/BQAAHgYAADgGAABKBgAAVgYAAFkGAABdBgAA"
+  "YgYAAGYGAAB7BgAAgAYAAI8GAACdBgAAtAYAAMMGAADSBgAA3gYAAPIGAAD4BgAABgcAAA4HAAAU"
+  "BwAAGgcAAB8HAAAoBwAANAcAADoHAAABAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0A"
+  "AAAOAAAADwAAABAAAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABoAAAAeAAAAAgAA"
+  "AAAAAACMAwAABQAAAAwAAACUAwAABQAAAA4AAACgAwAABAAAAA8AAAAAAAAAGgAAABQAAAAAAAAA"
+  "GwAAABQAAACsAwAAHAAAABQAAACMAwAAHQAAABQAAAC0AwAAHQAAABQAAAC8AwAAAwADAAMAAAAE"
+  "AAwAJAAAAAoABgAsAAAABAAEAAAAAAAEAAAAHwAAAAQAAQAoAAAABAAIACoAAAAEAAQALwAAAAYA"
+  "BQAtAAAACAAEAAAAAAANAAcAAAAAAA8AAgAlAAAAEAADACkAAAASAAYAIQAAAJYHAACWBwAABAAA"
+  "AAEAAAAIAAAAAAAAABkAAABkAwAAnQcAAAAAAAAEAAAAAgAAAAEAAABjBwAAAQAAAIsHAAACAAAA"
+  "iwcAAJMHAAABAAEAAQAAAEEHAAAEAAAAcBAGAAAADgADAAIAAAAAAEYHAAADAAAAkAABAg8AAAAF"
+  "AAMABAAAAE0HAAAQAAAAcQAJAAAADAAcAQQAbkAIABBDDAAiAQ0AcCAHAAEAEQEEAAEAAgAAAFYH"
+  "AAAMAAAAYgACABIhEjL8IAAAIQAKAW4gBQAQAA4AAwABAAIAAABdBwAACwAAABIgEjH8IAEAEAAK"
+  "ABJRcSAKAAEADgAAAAAAAAAAAAAAAwAAAAAAAAABAAAAmAIAAAIAAACgAgAABAAAAKgCAAACAAAA"
+  "AAAAAAMAAAAPAAkAEQAAAAMAAAAHAAkAEQAAAAEAAAAAAAAAAQAAAA4AAAABAAAAFQAGPGluaXQ+"
+  "AAFJAANJSUkADUlOVk9LRV9TVEFUSUMAAUwABExMTEwAM0xjb20vYW5kcm9pZC9qYWNrL2Fubm90"
+  "YXRpb25zL0NhbGxlZEJ5SW52b2tlQ3VzdG9tOwAxTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlv"
+  "bnMvTGlua2VyTWV0aG9kSGFuZGxlOwAvTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvTWV0"
+  "aG9kSGFuZGxlS2luZDsAM0xjb20vYW5kcm9pZC9qYWNrL2phdmE3L2ludm9rZWN1c3RvbS90ZXN0"
+  "MDAxL1Rlc3RzOwAaTGRhbHZpay9hbm5vdGF0aW9uL1Rocm93czsAFUxqYXZhL2lvL1ByaW50U3Ry"
+  "ZWFtOwARTGphdmEvbGFuZy9DbGFzczsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9T"
+  "dHJpbmc7ABJMamF2YS9sYW5nL1N5c3RlbTsAFUxqYXZhL2xhbmcvVGhyb3dhYmxlOwAbTGphdmEv"
+  "bGFuZy9pbnZva2UvQ2FsbFNpdGU7ACNMamF2YS9sYW5nL2ludm9rZS9Db25zdGFudENhbGxTaXRl"
+  "OwAfTGphdmEvbGFuZy9pbnZva2UvTWV0aG9kSGFuZGxlOwAnTGphdmEvbGFuZy9pbnZva2UvTWV0"
+  "aG9kSGFuZGxlcyRMb29rdXA7ACBMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzOwAdTGph"
+  "dmEvbGFuZy9pbnZva2UvTWV0aG9kVHlwZTsAGExqdW5pdC9mcmFtZXdvcmsvQXNzZXJ0OwAQTG9y"
+  "Zy9qdW5pdC9UZXN0OwAKVGVzdHMuamF2YQABVgACVkkAA1ZJSQACVkwAE1tMamF2YS9sYW5nL1N0"
+  "cmluZzsAA2FkZAANYXJndW1lbnRUeXBlcwAMYXNzZXJ0RXF1YWxzABVlbWl0dGVyOiBqYWNrLTQu"
+  "MC1lbmcADWVuY2xvc2luZ1R5cGUADWZpZWxkQ2FsbFNpdGUACmZpbmRTdGF0aWMAEmludm9rZU1l"
+  "dGhvZEhhbmRsZQAEa2luZAAMbGlua2VyTWV0aG9kAAZsb29rdXAABG1haW4ABG5hbWUAA291dAAH"
+  "cHJpbnRsbgAKcmV0dXJuVHlwZQAEdGVzdAAFdmFsdWUAIgAHDgAvAgAABw4ANQMAAAAHDqUAPwEA"
+  "Bw60ADsABw6lAAABBCAcAhgAGAAmHAEdAgQgHAMYDxgJGBEjGAQnGwArFygrFx8uGAACBQEwHAEY"
+  "CwETAAMWABcfFQABAAQBAQkAgYAEtAUBCswFAQrkBQEJlAYEAbwGAAAAEwAAAAAAAAABAAAAAAAA"
+  "AAEAAAAxAAAAcAAAAAIAAAAWAAAANAEAAAMAAAAJAAAAjAEAAAQAAAADAAAA+AEAAAUAAAALAAAA"
+  "EAIAAAcAAAACAAAAaAIAAAYAAAABAAAAcAIAAAgAAAABAAAAkAIAAAMQAAADAAAAmAIAAAEgAAAF"
+  "AAAAtAIAAAYgAAABAAAAZAMAAAEQAAAGAAAAjAMAAAIgAAAxAAAAwgMAAAMgAAAFAAAAQQcAAAQg"
+  "AAADAAAAYwcAAAUgAAABAAAAlgcAAAAgAAABAAAAnQcAAAAQAAABAAAAvAcAAA==",
+  // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test002/Tests.java
+  "ZGV4CjAzOAAzq3aGAwKhT4QQj4lqNfZJAO8Tm24uTyNICQAAcAAAAHhWNBIAAAAAAAAAAGAIAAA2"
+  "AAAAcAAAABgAAABIAQAACQAAAKgBAAAEAAAAFAIAAA0AAAA0AgAAAQAAAKQCAAB8BgAAzAIAACYE"
+  "AAAwBAAAOAQAAEQEAABHBAAATAQAAE8EAABVBAAAigQAALwEAADtBAAAIgUAAD4FAABVBQAAaAUA"
+  "AH0FAACRBQAApQUAALkFAADQBQAA7QUAABIGAAAzBgAAXAYAAH4GAACdBgAAtwYAAMkGAADPBgAA"
+  "2wYAAN4GAADiBgAA5wYAAOsGAAD/BgAAFAcAABkHAAAoBwAANgcAAE0HAABcBwAAawcAAH4HAACK"
+  "BwAAkAcAAJgHAACeBwAAqgcAALAHAAC1BwAAxgcAAM8HAADbBwAA4QcAAAMAAAAHAAAACAAAAAkA"
+  "AAAKAAAACwAAAAwAAAANAAAADgAAAA8AAAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAA"
+  "ABgAAAAZAAAAGgAAAB0AAAAhAAAAIgAAAAQAAAAAAAAA8AMAAAYAAAAPAAAA+AMAAAUAAAAQAAAA"
+  "AAAAAAYAAAASAAAABAQAAB0AAAAVAAAAAAAAAB4AAAAVAAAAEAQAAB8AAAAVAAAA8AMAACAAAAAV"
+  "AAAAGAQAACAAAAAVAAAAIAQAAAMAAwACAAAABAANACgAAAAIAAcAGwAAAAsABgAwAAAABAAEAAAA"
+  "AAAEAAQAAQAAAAQAAAAjAAAABAAIAC0AAAAEAAQANAAAAAYABQAyAAAACQAEAAEAAAAMAAQAMQAA"
+  "AA4ABwABAAAAEAABACoAAAARAAIALAAAABIAAwAuAAAAEwAGACUAAAA4CAAAOAgAAAQAAAABAAAA"
+  "CQAAAAAAAAAcAAAA0AMAAD8IAAAAAAAAAQAAAAEAAAABAAAADggAAAIAAAAtCAAANQgAAAgAAAAE"
+  "AAEA6AcAACoAAABxAAoAAAAMABwBBAAbAiMAAABiAwIAYgQCABIVI1UWAGIGAgASB00GBQdxMAsA"
+  "QwUMA25ACQAQMgwAIgEOAHAgCAABAGkBAQAOAA0AbhAHAAAAKPsAAAAAJAABAAEBDCUBAAEAAQAA"
+  "APUHAAAEAAAAcBAGAAAADgADAAIAAAAAAPoHAAADAAAAkAABAg8AAAAEAAEAAgAAAAEIAAAMAAAA"
+  "YgADABIhEjL8IAAAIQAKAW4gBQAQAA4AAwABAAIAAAAICAAACwAAABIgEjH8IAEAEAAKABJRcSAM"
+  "AAEADgAAAAAAAAAAAAAAAgAAAAAAAAACAAAAzAIAAAQAAADUAgAAAgAAAAAAAAADAAAABwAKABIA"
+  "AAADAAAABwAHABYAAAABAAAAAAAAAAEAAAAPAAAAAQAAABcACDxjbGluaXQ+AAY8aW5pdD4ACkdF"
+  "VF9TVEFUSUMAAUkAA0lJSQABTAAETExMTAAzTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMv"
+  "Q2FsbGVkQnlJbnZva2VDdXN0b207ADBMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9MaW5r"
+  "ZXJGaWVsZEhhbmRsZTsAL0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL01ldGhvZEhhbmRs"
+  "ZUtpbmQ7ADNMY29tL2FuZHJvaWQvamFjay9qYXZhNy9pbnZva2VjdXN0b20vdGVzdDAwMi9UZXN0"
+  "czsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABVMamF2YS9pby9QcmludFN0cmVhbTsAEUxq"
+  "YXZhL2xhbmcvQ2xhc3M7ABNMamF2YS9sYW5nL0ludGVnZXI7ABJMamF2YS9sYW5nL09iamVjdDsA"
+  "EkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07ABVMamF2YS9sYW5nL1Rocm93"
+  "YWJsZTsAG0xqYXZhL2xhbmcvaW52b2tlL0NhbGxTaXRlOwAjTGphdmEvbGFuZy9pbnZva2UvQ29u"
+  "c3RhbnRDYWxsU2l0ZTsAH0xqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZTsAJ0xqYXZhL2xh"
+  "bmcvaW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwOwAgTGphdmEvbGFuZy9pbnZva2UvTWV0aG9k"
+  "SGFuZGxlczsAHUxqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5cGU7ABhManVuaXQvZnJhbWV3b3Jr"
+  "L0Fzc2VydDsAEExvcmcvanVuaXQvVGVzdDsABFRZUEUAClRlc3RzLmphdmEAAVYAAlZJAANWSUkA"
+  "AlZMABJbTGphdmEvbGFuZy9DbGFzczsAE1tMamF2YS9sYW5nL1N0cmluZzsAA2FkZAANYXJndW1l"
+  "bnRUeXBlcwAMYXNzZXJ0RXF1YWxzABVlbWl0dGVyOiBqYWNrLTQuMC1lbmcADWVuY2xvc2luZ1R5"
+  "cGUADWZpZWxkQ2FsbFNpdGUAEWZpZWxkTWV0aG9kSGFuZGxlAApmaW5kU3RhdGljAARraW5kAAZs"
+  "b29rdXAABG1haW4ACm1ldGhvZFR5cGUABG5hbWUAA291dAAPcHJpbnRTdGFja1RyYWNlAAdwcmlu"
+  "dGxuAApyZXR1cm5UeXBlAAR0ZXN0AAV2YWx1ZQAoAAcOAR0PAnh3Jh4AIQAHDgA2AgAABw4APwEA"
+  "Bw60ADsABw6lAAABBCQcAhgAGAApHAEdAgMnGAQrGwAvFygvFyMzGAACBQE1HAEYDAEUAAMWABcj"
+  "FQABAAQBAQkAiIAE4AUBgYAE0AYBCugGAQmABwQBqAcAAAATAAAAAAAAAAEAAAAAAAAAAQAAADYA"
+  "AABwAAAAAgAAABgAAABIAQAAAwAAAAkAAACoAQAABAAAAAQAAAAUAgAABQAAAA0AAAA0AgAABwAA"
+  "AAIAAACcAgAABgAAAAEAAACkAgAACAAAAAEAAADEAgAAAxAAAAIAAADMAgAAASAAAAUAAADgAgAA"
+  "BiAAAAEAAADQAwAAARAAAAYAAADwAwAAAiAAADYAAAAmBAAAAyAAAAUAAADoBwAABCAAAAMAAAAO"
+  "CAAABSAAAAEAAAA4CAAAACAAAAEAAAA/CAAAABAAAAEAAABgCAAA",
+  // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test003/Tests.java
+  "ZGV4CjAzOABjnhkFatj30/7cHTCJsfr7vAjz9/p+Y+TcCAAAcAAAAHhWNBIAAAAAAAAAAPQHAAAx"
+  "AAAAcAAAABYAAAA0AQAACQAAAIwBAAADAAAA+AEAAAsAAAAQAgAAAQAAAHACAABEBgAAmAIAAOoD"
+  "AADyAwAA9QMAAP4DAAANBAAAEAQAABYEAABLBAAAfgQAAK8EAADkBAAAAAUAABcFAAAqBQAAPgUA"
+  "AFIFAABmBQAAfQUAAJoFAAC/BQAA4AUAAAkGAAArBgAASgYAAGQGAAB2BgAAggYAAIUGAACJBgAA"
+  "jgYAAJIGAACnBgAArAYAALsGAADJBgAA4AYAAO8GAAD+BgAACgcAAB4HAAAkBwAAMgcAADoHAABA"
+  "BwAARgcAAEsHAABUBwAAYAcAAGYHAAABAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0A"
+  "AAAOAAAADwAAABAAAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABoAAAAeAAAAAgAA"
+  "AAAAAACkAwAABQAAAAwAAAC0AwAABQAAAA4AAADAAwAABAAAAA8AAAAAAAAAGgAAABQAAAAAAAAA"
+  "GwAAABQAAADMAwAAHAAAABQAAADUAwAAHQAAABQAAADcAwAAHQAAABQAAADkAwAAAwADAAMAAAAE"
+  "AAwAJAAAAAoABgAsAAAABAAEAAAAAAAEAAAAHwAAAAQAAQAoAAAABAAIACoAAAAEAAQALwAAAAYA"
+  "BQAtAAAACAAEAAAAAAANAAcAAAAAAA8AAgAlAAAAEAADACkAAAASAAYAIQAAAM4HAADOBwAABAAA"
+  "AAEAAAAIAAAAAAAAABkAAAB8AwAA1QcAAAAAAAAEAAAAAgAAAAEAAACTBwAAAQAAAMMHAAACAAAA"
+  "wwcAAMsHAAABAAEAAQAAAG0HAAAEAAAAcBAGAAAADgAHAAYAAAAAAHIHAAAHAAAAkAABArAwsECw"
+  "ULBgDwAAAAUAAwAEAAAAfQcAABAAAABxAAkAAAAMABwBBABuQAgAEEMMACIBDQBwIAcAAQARAQgA"
+  "AQACAAAAhgcAABAAAABiBgIAEhASIRIyEkMSVBJl/QYAAAAACgBuIAUABgAOAAcAAQACAAAAjQcA"
+  "ABAAAAASEBIhEjISQxJUEmX9BgEAAAAKABMBFQBxIAoAAQAOAAAAAAAAAAAAAwAAAAAAAAABAAAA"
+  "mAIAAAIAAACgAgAABAAAAKgCAAAGAAAAAAAAAAAAAAAAAAAAAwAAAA8ACQARAAAAAwAAAAcACQAR"
+  "AAAAAQAAAAAAAAACAAAAAAAAAAEAAAAOAAAAAQAAABUABjxpbml0PgABSQAHSUlJSUlJSQANSU5W"
+  "T0tFX1NUQVRJQwABTAAETExMTAAzTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvQ2FsbGVk"
+  "QnlJbnZva2VDdXN0b207ADFMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9MaW5rZXJNZXRo"
+  "b2RIYW5kbGU7AC9MY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9NZXRob2RIYW5kbGVLaW5k"
+  "OwAzTGNvbS9hbmRyb2lkL2phY2svamF2YTcvaW52b2tlY3VzdG9tL3Rlc3QwMDMvVGVzdHM7ABpM"
+  "ZGFsdmlrL2Fubm90YXRpb24vVGhyb3dzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABFMamF2YS9s"
+  "YW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZh"
+  "L2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9D"
+  "YWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5n"
+  "L2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExv"
+  "b2t1cDsAIExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXM7AB1MamF2YS9sYW5nL2ludm9r"
+  "ZS9NZXRob2RUeXBlOwAYTGp1bml0L2ZyYW1ld29yay9Bc3NlcnQ7ABBMb3JnL2p1bml0L1Rlc3Q7"
+  "AApUZXN0cy5qYXZhAAFWAAJWSQADVklJAAJWTAATW0xqYXZhL2xhbmcvU3RyaW5nOwADYWRkAA1h"
+  "cmd1bWVudFR5cGVzAAxhc3NlcnRFcXVhbHMAFWVtaXR0ZXI6IGphY2stNC4wLWVuZwANZW5jbG9z"
+  "aW5nVHlwZQANZmllbGRDYWxsU2l0ZQAKZmluZFN0YXRpYwASaW52b2tlTWV0aG9kSGFuZGxlAARr"
+  "aW5kAAxsaW5rZXJNZXRob2QABmxvb2t1cAAEbWFpbgAEbmFtZQADb3V0AAdwcmludGxuAApyZXR1"
+  "cm5UeXBlAAR0ZXN0AAV2YWx1ZQAiAAcOAC8GAAAAAAAABw4ANQMAAAAHDqUAPwEABw7wADsABw7w"
+  "AAABBCAcBhgAGAAYABgAGAAYACYcAR0CBCAcAxgPGAkYESMYBCcbACsXKCsXHy4YAAIFATAcARgL"
+  "ARMAAxYAFx8VAAEABAEBCQCBgAS0BQEKzAUBCuwFAQmcBgQBzAYAAAATAAAAAAAAAAEAAAAAAAAA"
+  "AQAAADEAAABwAAAAAgAAABYAAAA0AQAAAwAAAAkAAACMAQAABAAAAAMAAAD4AQAABQAAAAsAAAAQ"
+  "AgAABwAAAAIAAABoAgAABgAAAAEAAABwAgAACAAAAAEAAACQAgAAAxAAAAMAAACYAgAAASAAAAUA"
+  "AAC0AgAABiAAAAEAAAB8AwAAARAAAAcAAACkAwAAAiAAADEAAADqAwAAAyAAAAUAAABtBwAABCAA"
+  "AAMAAACTBwAABSAAAAEAAADOBwAAACAAAAEAAADVBwAAABAAAAEAAAD0BwAA",
+  // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test004/Tests.java
+  "ZGV4CjAzOABvUVfbV74qWbSOEsgKP+EzahlNQLW2/8TMDAAAcAAAAHhWNBIAAAAAAAAAAOQLAABS"
+  "AAAAcAAAAB8AAAC4AQAAEAAAADQCAAADAAAA9AIAABIAAAAMAwAAAQAAAKQDAAAACQAAzAMAANYF"
+  "AADZBQAA4QUAAOkFAADsBQAA7wUAAPIFAAD1BQAA/AUAAP8FAAAEBgAAEwYAABYGAAAZBgAAHwYA"
+  "AC8GAABkBgAAjQYAAMAGAADxBgAAJgcAAEUHAABhBwAAeAcAAIoHAACdBwAAsQcAAMUHAADZBwAA"
+  "8AcAAA0IAAAyCAAAUwgAAHwIAACeCAAAvQgAANcIAADpCAAA7AgAAPgIAAD7CAAAAAkAAAYJAAAM"
+  "CQAAEAkAABUJAAAaCQAAHgkAACMJAAAnCQAAKgkAADMJAABICQAATQkAAFwJAABqCQAAdgkAAIQJ"
+  "AACPCQAAmgkAAKYJAACzCQAAygkAANkJAADoCQAA9AkAAAAKAAAKCgAAHgoAACQKAAAyCgAAPQoA"
+  "AEUKAABLCgAAYgoAAGgKAABtCgAAdgoAAIIKAACOCgAAmwoAAKEKAAADAAAABAAAAAUAAAAGAAAA"
+  "CAAAAAsAAAAPAAAAEAAAABEAAAASAAAAEwAAABQAAAAVAAAAFgAAABgAAAAZAAAAGgAAABsAAAAc"
+  "AAAAHQAAAB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJwAAADEAAAAzAAAACQAAAAQA"
+  "AABMBQAADgAAABMAAABUBQAADQAAABUAAAB0BQAADAAAABYAAAAAAAAAJwAAABwAAAAAAAAAKAAA"
+  "ABwAAACABQAAKQAAABwAAACIBQAAKgAAABwAAACUBQAAKwAAABwAAACgBQAALAAAABwAAABMBQAA"
+  "LQAAABwAAACoBQAALwAAABwAAACwBQAALwAAABwAAAC4BQAALgAAABwAAADABQAAMAAAABwAAADI"
+  "BQAALgAAABwAAADQBQAACQAJAAoAAAAKABMAPwAAABEADQBLAAAACgAEAAIAAAAKAAAANAAAAAoA"
+  "AQBFAAAACgAPAEgAAAAKAAQAUAAAAA0ACABMAAAADwAEAAIAAAAUAA0AAgAAABYAAgBAAAAAFwAD"
+  "AEcAAAAZAAUANgAAABkABgA2AAAAGQAHADYAAAAZAAkANgAAABkACgA2AAAAGQALADYAAAAZAAwA"
+  "NgAAABkADgA3AAAAnQsAAJ0LAAAKAAAAAQAAAA8AAAAAAAAAJgAAACQFAADGCwAAAAAAAAQAAAAC"
+  "AAAAAQAAAN4KAAACAAAAegsAAJILAAACAAAAkgsAAJoLAAABAAEAAQAAAKgKAAAEAAAAcBAGAAAA"
+  "DgADAAIAAAAAAK0KAAADAAAAkAABAg8AAAAYAA8ABgAAALQKAABTAAAAcRARAAwAEhJxIA0A0gAT"
+  "AmEAcSAKAOIAEwIABHEgDQDyABISAgAQAHEgDQACABICFAOamTFBAgARAHEwDAADAhYGAAAYApqZ"
+  "mZmZmQFABQQSAHcGCwACABsCBwAAAAgAFABxIBAAAgAcAgoACAAVAHEgDwACABcCFc1bBwUAFgBx"
+  "QA4AMhBxAAkAAAAMAhwDCgBuQAgAMroMAiIDFABwIAcAIwARAwAABAABAAIAAADRCgAADAAAAGIA"
+  "AgASIRIy/CAAACEACgFuIAUAEAAOAAMAAQACAAAA2AoAAAsAAAASIBIx/CABABAACgASUXEgDQAB"
+  "AA4AAAAAAAAAAAAAAAMAAAAAAAAAAQAAAMwDAAACAAAA1AMAAAQAAADgAwAAAgAAAAQABAANAAAA"
+  "FgAQABgAHQAAAAEAGwAEAAMAAgAQAA4ABQAAAAMAAAAOABAAGAAAAAIAAAABAAEAAwAAAAIAAgAC"
+  "AAAAAwAAAAMAAwADAAAAAQAAAAQAAAACAAAABQAFAAIAAAAPAA8AAgAAABAAEAABAAAAFQAAAAEA"
+  "AAAdAAAAAQAAAB4AASgABjwqPjtKKQAGPGluaXQ+AAFCAAFDAAFEAAFGAAVIZWxsbwABSQADSUlJ"
+  "AA1JTlZPS0VfU1RBVElDAAFKAAFMAARMTExMAA5MTExMWkJDU0lGRExMSgAzTGNvbS9hbmRyb2lk"
+  "L2phY2svYW5ub3RhdGlvbnMvQ2FsbGVkQnlJbnZva2VDdXN0b207ACdMY29tL2FuZHJvaWQvamFj"
+  "ay9hbm5vdGF0aW9ucy9Db25zdGFudDsAMUxjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0xp"
+  "bmtlck1ldGhvZEhhbmRsZTsAL0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL01ldGhvZEhh"
+  "bmRsZUtpbmQ7ADNMY29tL2FuZHJvaWQvamFjay9qYXZhNy9pbnZva2VjdXN0b20vdGVzdDAwNC9U"
+  "ZXN0czsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1cmU7ABpMZGFsdmlrL2Fubm90YXRpb24v"
+  "VGhyb3dzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABBMamF2YS9sYW5nL0NsYXNzABFMamF2YS9s"
+  "YW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZh"
+  "L2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9D"
+  "YWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5n"
+  "L2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExv"
+  "b2t1cDsAIExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXM7AB1MamF2YS9sYW5nL2ludm9r"
+  "ZS9NZXRob2RUeXBlOwAYTGp1bml0L2ZyYW1ld29yay9Bc3NlcnQ7ABBMb3JnL2p1bml0L1Rlc3Q7"
+  "AAFTAApUZXN0cy5qYXZhAAFWAANWQ0MABFZEREQABFZGRkYAAlZJAANWSUkAA1ZKSgACVkwAA1ZM"
+  "TAACVloAAVoAB1pCQ1NJRkQAE1tMamF2YS9sYW5nL1N0cmluZzsAA2FkZAANYXJndW1lbnRUeXBl"
+  "cwAMYXNzZXJ0RXF1YWxzAAphc3NlcnRUcnVlAAxib29sZWFuVmFsdWUACWJ5dGVWYWx1ZQAJY2hh"
+  "clZhbHVlAApjbGFzc1ZhbHVlAAtkb3VibGVWYWx1ZQAVZW1pdHRlcjogamFjay00LjAtZW5nAA1l"
+  "bmNsb3NpbmdUeXBlAA1maWVsZENhbGxTaXRlAApmaW5kU3RhdGljAApmbG9hdFZhbHVlAAhpbnRW"
+  "YWx1ZQASaW52b2tlTWV0aG9kSGFuZGxlAARraW5kAAxsaW5rZXJNZXRob2QACWxvbmdWYWx1ZQAG"
+  "bG9va3VwAARtYWluABVtZXRob2RIYW5kbGVFeHRyYUFyZ3MABG5hbWUAA291dAAHcHJpbnRsbgAK"
+  "cmV0dXJuVHlwZQAKc2hvcnRWYWx1ZQALc3RyaW5nVmFsdWUABHRlc3QABXZhbHVlACMABw4ANwIA"
+  "AAcOAD4NAAAAAAAAAAAAAAAAAAcOPEtaWmmWw4d4h6UAUgEABw60AE4ABw6lAAAGBTUcAhgEGARD"
+  "HAEdCAQ1HA0YFhgQGBgYHRgAGAEYGxgEGAMYAhgQGA4YBT4YCkQbAEoXRUkcCh0HATgcAT8dBwE5"
+  "HAEAAR0HATocAQNhHQcBThwBIgAEHQcBQhwBBAEdBwFBHAFwmpkxQR0HATwcAfGamZmZmZkBQB0H"
+  "AU8cARcHHQcBOxwBGAodBwFGHAFmFc1bB0oXNE0YBAILAVEcCRcAFyAXGhciFzIXGhcXFwEXHQIM"
+  "AVEcARgSARoADRYAFzQVAAQBBAEEYSQABAQBcJqZMUHxmpmZmZmZAUAXBxgKZhXNWwcBAAQBAQkA"
+  "gYAE7AcBCoQIAQqcCAEJ1AkEAfwJAAATAAAAAAAAAAEAAAAAAAAAAQAAAFIAAABwAAAAAgAAAB8A"
+  "AAC4AQAAAwAAABAAAAA0AgAABAAAAAMAAAD0AgAABQAAABIAAAAMAwAABwAAAAIAAACcAwAABgAA"
+  "AAEAAACkAwAACAAAAAEAAADEAwAAAxAAAAMAAADMAwAAASAAAAUAAADsAwAABiAAAAEAAAAkBQAA"
+  "ARAAAA0AAABMBQAAAiAAAFIAAADWBQAAAyAAAAUAAACoCgAABCAAAAQAAADeCgAABSAAAAEAAACd"
+  "CwAAACAAAAEAAADGCwAAABAAAAEAAADkCwAA"
+};
+
+TEST_F(DexFileVerifierTest, InvokeCustomDexSamples) {
+  for (size_t i = 0; i < arraysize(kInvokeCustomDexFiles); ++i) {
+    size_t length;
+    std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kInvokeCustomDexFiles[i], &length));
+    CHECK(dex_bytes != nullptr);
+    // Note: `dex_file` will be destroyed before `dex_bytes`.
+    std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+    std::string error_msg;
+    EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(),
+                                        dex_file->Begin(),
+                                        dex_file->Size(),
+                                        "good checksum, verify",
+                                        /*verify_checksum*/ true,
+                                        &error_msg));
+    // TODO(oth): Test corruptions (b/35308502)
+  }
+}
+
 }  // namespace art
diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc
index 37f3ac9..091085a 100644
--- a/runtime/dex_instruction.cc
+++ b/runtime/dex_instruction.cc
@@ -407,6 +407,20 @@
             break;
           }
           FALLTHROUGH_INTENDED;
+        case INVOKE_CUSTOM:
+          if (file != nullptr) {
+            os << opcode << " {";
+            uint32_t call_site_idx = VRegB_35c();
+            for (size_t i = 0; i < VRegA_35c(); ++i) {
+              if (i != 0) {
+                os << ", ";
+              }
+              os << "v" << arg[i];
+            }
+            os << "},  // call_site@" << call_site_idx;
+            break;
+          }
+          FALLTHROUGH_INTENDED;
         default:
           os << opcode << " {v" << arg[0] << ", v" << arg[1] << ", v" << arg[2]
                        << ", v" << arg[3] << ", v" << arg[4] << "}, thing@" << VRegB_35c();
@@ -415,6 +429,8 @@
       break;
     }
     case k3rc: {
+      uint16_t first_reg = VRegC_3rc();
+      uint16_t last_reg =  VRegC_3rc() + VRegA_3rc() - 1;
       switch (Opcode()) {
         case INVOKE_VIRTUAL_RANGE:
         case INVOKE_SUPER_RANGE:
@@ -423,7 +439,7 @@
         case INVOKE_INTERFACE_RANGE:
           if (file != nullptr) {
             uint32_t method_idx = VRegB_3rc();
-            os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1))
+            os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
                << file->PrettyMethod(method_idx) << " // method@" << method_idx;
             break;
           }
@@ -431,14 +447,22 @@
         case INVOKE_VIRTUAL_RANGE_QUICK:
           if (file != nullptr) {
             uint32_t method_idx = VRegB_3rc();
-            os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1))
+            os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
                << "// vtable@" << method_idx;
             break;
           }
           FALLTHROUGH_INTENDED;
+        case INVOKE_CUSTOM_RANGE:
+          if (file != nullptr) {
+            uint32_t call_site_idx = VRegB_3rc();
+            os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
+               << "// call_site@" << call_site_idx;
+            break;
+          }
+          FALLTHROUGH_INTENDED;
         default:
-          os << StringPrintf("%s, {v%d .. v%d}, thing@%d", opcode, VRegC_3rc(),
-                             (VRegC_3rc() + VRegA_3rc() - 1), VRegB_3rc());
+          os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
+             << "thing@" << VRegB_3rc();
           break;
       }
       break;
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index 578550c..d269110 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -126,14 +126,15 @@
 
   enum IndexType {
     kIndexUnknown = 0,
-    kIndexNone,              // has no index
-    kIndexTypeRef,           // type reference index
-    kIndexStringRef,         // string reference index
-    kIndexMethodRef,         // method reference index
-    kIndexFieldRef,          // field reference index
-    kIndexFieldOffset,       // field offset (for static linked fields)
-    kIndexVtableOffset,      // vtable offset (for static linked methods)
-    kIndexMethodAndProtoRef  // method and a proto reference index (for invoke-polymorphic)
+    kIndexNone,               // has no index
+    kIndexTypeRef,            // type reference index
+    kIndexStringRef,          // string reference index
+    kIndexMethodRef,          // method reference index
+    kIndexFieldRef,           // field reference index
+    kIndexFieldOffset,        // field offset (for static linked fields)
+    kIndexVtableOffset,       // vtable offset (for static linked methods)
+    kIndexMethodAndProtoRef,  // method and a proto reference index (for invoke-polymorphic)
+    kIndexCallSiteRef,        // call site reference index
   };
 
   enum Flags {
@@ -165,31 +166,32 @@
   };
 
   enum VerifyFlag {
-    kVerifyNone               = 0x000000,
-    kVerifyRegA               = 0x000001,
-    kVerifyRegAWide           = 0x000002,
-    kVerifyRegB               = 0x000004,
-    kVerifyRegBField          = 0x000008,
-    kVerifyRegBMethod         = 0x000010,
-    kVerifyRegBNewInstance    = 0x000020,
-    kVerifyRegBString         = 0x000040,
-    kVerifyRegBType           = 0x000080,
-    kVerifyRegBWide           = 0x000100,
-    kVerifyRegC               = 0x000200,
-    kVerifyRegCField          = 0x000400,
-    kVerifyRegCNewArray       = 0x000800,
-    kVerifyRegCType           = 0x001000,
-    kVerifyRegCWide           = 0x002000,
-    kVerifyArrayData          = 0x004000,
-    kVerifyBranchTarget       = 0x008000,
-    kVerifySwitchTargets      = 0x010000,
-    kVerifyVarArg             = 0x020000,
-    kVerifyVarArgNonZero      = 0x040000,
-    kVerifyVarArgRange        = 0x080000,
-    kVerifyVarArgRangeNonZero = 0x100000,
-    kVerifyRuntimeOnly        = 0x200000,
-    kVerifyError              = 0x400000,
-    kVerifyRegHPrototype      = 0x800000
+    kVerifyNone               = 0x0000000,
+    kVerifyRegA               = 0x0000001,
+    kVerifyRegAWide           = 0x0000002,
+    kVerifyRegB               = 0x0000004,
+    kVerifyRegBField          = 0x0000008,
+    kVerifyRegBMethod         = 0x0000010,
+    kVerifyRegBNewInstance    = 0x0000020,
+    kVerifyRegBString         = 0x0000040,
+    kVerifyRegBType           = 0x0000080,
+    kVerifyRegBWide           = 0x0000100,
+    kVerifyRegC               = 0x0000200,
+    kVerifyRegCField          = 0x0000400,
+    kVerifyRegCNewArray       = 0x0000800,
+    kVerifyRegCType           = 0x0001000,
+    kVerifyRegCWide           = 0x0002000,
+    kVerifyArrayData          = 0x0004000,
+    kVerifyBranchTarget       = 0x0008000,
+    kVerifySwitchTargets      = 0x0010000,
+    kVerifyVarArg             = 0x0020000,
+    kVerifyVarArgNonZero      = 0x0040000,
+    kVerifyVarArgRange        = 0x0080000,
+    kVerifyVarArgRangeNonZero = 0x0100000,
+    kVerifyRuntimeOnly        = 0x0200000,
+    kVerifyError              = 0x0400000,
+    kVerifyRegHPrototype      = 0x0800000,
+    kVerifyRegBCallSite       = 0x1000000
   };
 
   static constexpr uint32_t kMaxVarArgRegs = 5;
diff --git a/runtime/dex_instruction_list.h b/runtime/dex_instruction_list.h
index ca2ce1d..a5ce3c2 100644
--- a/runtime/dex_instruction_list.h
+++ b/runtime/dex_instruction_list.h
@@ -271,8 +271,8 @@
   V(0xF9, UNUSED_F9, "unused-f9", k10x, kIndexUnknown, 0, kVerifyError) \
   V(0xFA, INVOKE_POLYMORPHIC, "invoke-polymorphic", k45cc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgNonZero | kVerifyRegHPrototype) \
   V(0xFB, INVOKE_POLYMORPHIC_RANGE, "invoke-polymorphic/range", k4rcc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRangeNonZero | kVerifyRegHPrototype) \
-  V(0xFC, UNUSED_FC, "unused-fc", k10x, kIndexUnknown, 0, kVerifyError) \
-  V(0xFD, UNUSED_FD, "unused-fd", k10x, kIndexUnknown, 0, kVerifyError) \
+  V(0xFC, INVOKE_CUSTOM, "invoke-custom", k35c, kIndexCallSiteRef, kContinue | kThrow, kVerifyRegBCallSite) \
+  V(0xFD, INVOKE_CUSTOM_RANGE, "invoke-custom/range", k3rc, kIndexCallSiteRef, kContinue | kThrow, kVerifyRegBCallSite) \
   V(0xFE, UNUSED_FE, "unused-fe", k10x, kIndexUnknown, 0, kVerifyError) \
   V(0xFF, UNUSED_FF, "unused-ff", k10x, kIndexUnknown, 0, kVerifyError)
 
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 3bc49b8..28aca6c 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -709,10 +709,10 @@
     return resolved_method;
   } else if (type == kSuper) {
     // TODO This lookup is rather slow.
-    ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache();
-    dex::TypeIndex method_type_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
-    ObjPtr<mirror::Class> method_reference_class = ClassLinker::LookupResolvedType(
-        method_type_idx, dex_cache, referrer->GetClassLoader());
+    dex::TypeIndex method_type_idx =
+        referrer->GetDexFile()->GetMethodId(method_idx).class_idx_;
+    mirror::Class* method_reference_class =
+        referrer->GetDexCache()->GetResolvedType(method_type_idx);
     if (method_reference_class == nullptr) {
       // Need to do full type resolution...
       return nullptr;
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index fb8139b..6301362 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -39,7 +39,7 @@
 namespace art {
 
 void CheckReferenceResult(Handle<mirror::Object> o, Thread* self) {
-  if (o.Get() == nullptr) {
+  if (o == nullptr) {
     return;
   }
   // Make sure that the result is an instance of the type this method was expected to return.
diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc
index 4544aef..822c5a8 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -48,7 +48,7 @@
   StackHandleScope<1> hs(self);
   HandleWrapper<mirror::Object> h(hs.NewHandleWrapper(obj));
   ArtField* field = FindFieldFromCode<type, kAccessCheck>(field_idx, referrer, self, size);
-  if (LIKELY(field != nullptr) && UNLIKELY(h.Get() == nullptr)) {
+  if (LIKELY(field != nullptr) && UNLIKELY(h == nullptr)) {
     ThrowNullPointerExceptionForFieldAccess(field, /*is_read*/FindFieldTypeIsRead(type));
     return nullptr;
   }
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 3ef47c4..4c3990a 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -358,6 +358,29 @@
     }
   }
 
+  static bool GetInvokeType(ArtMethod** sp, InvokeType* invoke_type, uint32_t* dex_method_index)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK((*sp)->IsCalleeSaveMethod());
+    const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, Runtime::kSaveRefsAndArgs);
+    ArtMethod** caller_sp = reinterpret_cast<ArtMethod**>(
+        reinterpret_cast<uintptr_t>(sp) + callee_frame_size);
+    uintptr_t outer_pc = QuickArgumentVisitor::GetCallingPc(sp);
+    const OatQuickMethodHeader* current_code = (*caller_sp)->GetOatQuickMethodHeader(outer_pc);
+    if (!current_code->IsOptimized()) {
+      return false;
+    }
+    uintptr_t outer_pc_offset = current_code->NativeQuickPcOffset(outer_pc);
+    CodeInfo code_info = current_code->GetOptimizedCodeInfo();
+    CodeInfoEncoding encoding = code_info.ExtractEncoding();
+    InvokeInfo invoke(code_info.GetInvokeInfoForNativePcOffset(outer_pc_offset, encoding));
+    if (invoke.IsValid()) {
+      *invoke_type = static_cast<InvokeType>(invoke.GetInvokeType(encoding.invoke_info.encoding));
+      *dex_method_index = invoke.GetMethodIndex(encoding.invoke_info.encoding);
+      return true;
+    }
+    return false;
+  }
+
   // For the given quick ref and args quick frame, return the caller's PC.
   static uintptr_t GetCallingPc(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) {
     DCHECK((*sp)->IsCalleeSaveMethod());
@@ -977,60 +1000,87 @@
   ArtMethod* caller = nullptr;
   if (!called_method_known_on_entry) {
     caller = QuickArgumentVisitor::GetCallingMethod(sp);
-    uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
-    const DexFile::CodeItem* code;
     called_method.dex_file = caller->GetDexFile();
-    code = caller->GetCodeItem();
-    CHECK_LT(dex_pc, code->insns_size_in_code_units_);
-    const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
-    Instruction::Code instr_code = instr->Opcode();
-    bool is_range;
-    switch (instr_code) {
-      case Instruction::INVOKE_DIRECT:
-        invoke_type = kDirect;
-        is_range = false;
-        break;
-      case Instruction::INVOKE_DIRECT_RANGE:
-        invoke_type = kDirect;
-        is_range = true;
-        break;
-      case Instruction::INVOKE_STATIC:
-        invoke_type = kStatic;
-        is_range = false;
-        break;
-      case Instruction::INVOKE_STATIC_RANGE:
-        invoke_type = kStatic;
-        is_range = true;
-        break;
-      case Instruction::INVOKE_SUPER:
-        invoke_type = kSuper;
-        is_range = false;
-        break;
-      case Instruction::INVOKE_SUPER_RANGE:
-        invoke_type = kSuper;
-        is_range = true;
-        break;
-      case Instruction::INVOKE_VIRTUAL:
-        invoke_type = kVirtual;
-        is_range = false;
-        break;
-      case Instruction::INVOKE_VIRTUAL_RANGE:
-        invoke_type = kVirtual;
-        is_range = true;
-        break;
-      case Instruction::INVOKE_INTERFACE:
-        invoke_type = kInterface;
-        is_range = false;
-        break;
-      case Instruction::INVOKE_INTERFACE_RANGE:
-        invoke_type = kInterface;
-        is_range = true;
-        break;
-      default:
-        LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(nullptr);
-        UNREACHABLE();
+
+    InvokeType stack_map_invoke_type;
+    uint32_t stack_map_dex_method_idx;
+    const bool found_stack_map = QuickArgumentVisitor::GetInvokeType(sp,
+                                                                     &stack_map_invoke_type,
+                                                                     &stack_map_dex_method_idx);
+    // For debug builds, we make sure both of the paths are consistent by also looking at the dex
+    // code.
+    if (!found_stack_map || kIsDebugBuild) {
+      uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
+      const DexFile::CodeItem* code;
+      code = caller->GetCodeItem();
+      CHECK_LT(dex_pc, code->insns_size_in_code_units_);
+      const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
+      Instruction::Code instr_code = instr->Opcode();
+      bool is_range;
+      switch (instr_code) {
+        case Instruction::INVOKE_DIRECT:
+          invoke_type = kDirect;
+          is_range = false;
+          break;
+        case Instruction::INVOKE_DIRECT_RANGE:
+          invoke_type = kDirect;
+          is_range = true;
+          break;
+        case Instruction::INVOKE_STATIC:
+          invoke_type = kStatic;
+          is_range = false;
+          break;
+        case Instruction::INVOKE_STATIC_RANGE:
+          invoke_type = kStatic;
+          is_range = true;
+          break;
+        case Instruction::INVOKE_SUPER:
+          invoke_type = kSuper;
+          is_range = false;
+          break;
+        case Instruction::INVOKE_SUPER_RANGE:
+          invoke_type = kSuper;
+          is_range = true;
+          break;
+        case Instruction::INVOKE_VIRTUAL:
+          invoke_type = kVirtual;
+          is_range = false;
+          break;
+        case Instruction::INVOKE_VIRTUAL_RANGE:
+          invoke_type = kVirtual;
+          is_range = true;
+          break;
+        case Instruction::INVOKE_INTERFACE:
+          invoke_type = kInterface;
+          is_range = false;
+          break;
+        case Instruction::INVOKE_INTERFACE_RANGE:
+          invoke_type = kInterface;
+          is_range = true;
+          break;
+        default:
+          LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(nullptr);
+          UNREACHABLE();
+      }
+      called_method.dex_method_index = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();
+      // Check that the invoke matches what we expected, note that this path only happens for debug
+      // builds.
+      if (found_stack_map) {
+        DCHECK_EQ(stack_map_invoke_type, invoke_type);
+        if (invoke_type != kSuper) {
+          // Super may be sharpened.
+          DCHECK_EQ(stack_map_dex_method_idx, called_method.dex_method_index)
+              << called_method.dex_file->PrettyMethod(stack_map_dex_method_idx) << " "
+              << called_method.dex_file->PrettyMethod(called_method.dex_method_index);
+        }
+      } else {
+        VLOG(oat) << "Accessed dex file for invoke " << invoke_type << " "
+                  << called_method.dex_method_index;
+      }
+    } else {
+      invoke_type = stack_map_invoke_type;
+      called_method.dex_method_index = stack_map_dex_method_idx;
     }
-    called_method.dex_method_index = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();
   } else {
     invoke_type = kStatic;
     called_method.dex_file = called->GetDexFile();
@@ -2435,8 +2485,8 @@
 
   // Wrap raw_method_handle in a Handle for safety.
   StackHandleScope<5> hs(self);
-  Handle<mirror::MethodHandleImpl> method_handle(
-      hs.NewHandle(ObjPtr<mirror::MethodHandleImpl>::DownCast(MakeObjPtr(raw_method_handle))));
+  Handle<mirror::MethodHandle> method_handle(
+      hs.NewHandle(ObjPtr<mirror::MethodHandle>::DownCast(MakeObjPtr(raw_method_handle))));
   raw_method_handle = nullptr;
   self->EndAssertNoThreadSuspension(old_cause);
 
@@ -2497,15 +2547,14 @@
   // consecutive order.
   uint32_t unused_args[Instruction::kMaxVarArgRegs] = {};
   uint32_t first_callee_arg = first_arg + 1;
-  const bool do_assignability_check = false;
-  if (!DoInvokePolymorphic<true /* is_range */, do_assignability_check>(self,
-                                                                        resolved_method,
-                                                                        *shadow_frame,
-                                                                        method_handle,
-                                                                        method_type,
-                                                                        unused_args,
-                                                                        first_callee_arg,
-                                                                        result)) {
+  if (!DoInvokePolymorphic<true /* is_range */>(self,
+                                                resolved_method,
+                                                *shadow_frame,
+                                                method_handle,
+                                                method_type,
+                                                unused_args,
+                                                first_callee_arg,
+                                                result)) {
     DCHECK(self->IsExceptionPending());
   }
 
diff --git a/runtime/gc/reference_queue_test.cc b/runtime/gc/reference_queue_test.cc
index 3ca3353..613b034 100644
--- a/runtime/gc/reference_queue_test.cc
+++ b/runtime/gc/reference_queue_test.cc
@@ -38,11 +38,11 @@
   auto ref_class = hs.NewHandle(
       Runtime::Current()->GetClassLinker()->FindClass(self, "Ljava/lang/ref/WeakReference;",
                                                       ScopedNullHandle<mirror::ClassLoader>()));
-  ASSERT_TRUE(ref_class.Get() != nullptr);
+  ASSERT_TRUE(ref_class != nullptr);
   auto ref1(hs.NewHandle(ref_class->AllocObject(self)->AsReference()));
-  ASSERT_TRUE(ref1.Get() != nullptr);
+  ASSERT_TRUE(ref1 != nullptr);
   auto ref2(hs.NewHandle(ref_class->AllocObject(self)->AsReference()));
-  ASSERT_TRUE(ref2.Get() != nullptr);
+  ASSERT_TRUE(ref2 != nullptr);
   queue.EnqueueReference(ref1.Get());
   ASSERT_TRUE(!queue.IsEmpty());
   ASSERT_EQ(queue.GetLength(), 1U);
@@ -73,15 +73,15 @@
   auto weak_ref_class = hs.NewHandle(
       Runtime::Current()->GetClassLinker()->FindClass(self, "Ljava/lang/ref/WeakReference;",
                                                       ScopedNullHandle<mirror::ClassLoader>()));
-  ASSERT_TRUE(weak_ref_class.Get() != nullptr);
+  ASSERT_TRUE(weak_ref_class != nullptr);
   auto finalizer_ref_class = hs.NewHandle(
       Runtime::Current()->GetClassLinker()->FindClass(self, "Ljava/lang/ref/FinalizerReference;",
                                                       ScopedNullHandle<mirror::ClassLoader>()));
-  ASSERT_TRUE(finalizer_ref_class.Get() != nullptr);
+  ASSERT_TRUE(finalizer_ref_class != nullptr);
   auto ref1(hs.NewHandle(weak_ref_class->AllocObject(self)->AsReference()));
-  ASSERT_TRUE(ref1.Get() != nullptr);
+  ASSERT_TRUE(ref1 != nullptr);
   auto ref2(hs.NewHandle(finalizer_ref_class->AllocObject(self)->AsReference()));
-  ASSERT_TRUE(ref2.Get() != nullptr);
+  ASSERT_TRUE(ref2 != nullptr);
 
   queue.EnqueueReference(ref1.Get());
   oss.str("");
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 4be4ef0..2163a20 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -692,7 +692,7 @@
     if (validate_oat_file) {
       TimingLogger::ScopedTiming timing("ValidateOatFile", &logger);
       CHECK(space->oat_file_ != nullptr);
-      if (!ValidateOatFile(*space, *space->oat_file_, error_msg)) {
+      if (!ImageSpace::ValidateOatFile(*space->oat_file_, error_msg)) {
         DCHECK(!error_msg->empty());
         return nullptr;
       }
@@ -1237,9 +1237,9 @@
           }
           dex_cache->FixupStrings<kWithoutReadBarrier>(new_strings, fixup_adapter);
         }
-        mirror::TypeDexCacheType* types = dex_cache->GetResolvedTypes();
+        GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes();
         if (types != nullptr) {
-          mirror::TypeDexCacheType* new_types = fixup_adapter.ForwardObject(types);
+          GcRoot<mirror::Class>* new_types = fixup_adapter.ForwardObject(types);
           if (types != new_types) {
             dex_cache->SetResolvedTypes(new_types);
           }
@@ -1283,6 +1283,14 @@
           }
           dex_cache->FixupResolvedMethodTypes<kWithoutReadBarrier>(new_method_types, fixup_adapter);
         }
+        GcRoot<mirror::CallSite>* call_sites = dex_cache->GetResolvedCallSites();
+        if (call_sites != nullptr) {
+          GcRoot<mirror::CallSite>* new_call_sites = fixup_adapter.ForwardObject(call_sites);
+          if (call_sites != new_call_sites) {
+            dex_cache->SetResolvedCallSites(new_call_sites);
+          }
+          dex_cache->FixupResolvedCallSites<kWithoutReadBarrier>(new_call_sites, fixup_adapter);
+        }
       }
     }
     {
@@ -1379,33 +1387,6 @@
 
     return oat_file;
   }
-
-  static bool ValidateOatFile(const ImageSpace& space,
-                              const OatFile& oat_file,
-                              std::string* error_msg) {
-    for (const OatFile::OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) {
-      const std::string& dex_file_location = oat_dex_file->GetDexFileLocation();
-      uint32_t dex_file_location_checksum;
-      if (!DexFile::GetChecksum(dex_file_location.c_str(), &dex_file_location_checksum, error_msg)) {
-        *error_msg = StringPrintf("Failed to get checksum of dex file '%s' referenced by image %s: "
-                                  "%s",
-                                  dex_file_location.c_str(),
-                                  space.GetName(),
-                                  error_msg->c_str());
-        return false;
-      }
-      if (dex_file_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) {
-        *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file '%s' and "
-                                  "dex file '%s' (0x%x != 0x%x)",
-                                  oat_file.GetLocation().c_str(),
-                                  dex_file_location.c_str(),
-                                  oat_dex_file->GetDexFileLocationChecksum(),
-                                  dex_file_location_checksum);
-        return false;
-      }
-    }
-    return true;
-  }
 };
 
 static constexpr uint64_t kLowSpaceValue = 50 * MB;
@@ -1782,6 +1763,63 @@
   return bootcp_oss.str();
 }
 
+bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg) {
+  for (const OatFile::OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) {
+    const std::string& dex_file_location = oat_dex_file->GetDexFileLocation();
+
+    // Skip multidex locations - These will be checked when we visit their
+    // corresponding primary non-multidex location.
+    if (DexFile::IsMultiDexLocation(dex_file_location.c_str())) {
+      continue;
+    }
+
+    std::vector<uint32_t> checksums;
+    if (!DexFile::GetMultiDexChecksums(dex_file_location.c_str(), &checksums, error_msg)) {
+      *error_msg = StringPrintf("ValidateOatFile failed to get checksums of dex file '%s' "
+                                "referenced by oat file %s: %s",
+                                dex_file_location.c_str(),
+                                oat_file.GetLocation().c_str(),
+                                error_msg->c_str());
+      return false;
+    }
+    CHECK(!checksums.empty());
+    if (checksums[0] != oat_dex_file->GetDexFileLocationChecksum()) {
+      *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file "
+                                "'%s' and dex file '%s' (0x%x != 0x%x)",
+                                oat_file.GetLocation().c_str(),
+                                dex_file_location.c_str(),
+                                oat_dex_file->GetDexFileLocationChecksum(),
+                                checksums[0]);
+      return false;
+    }
+
+    // Verify checksums for any related multidex entries.
+    for (size_t i = 1; i < checksums.size(); i++) {
+      std::string multi_dex_location = DexFile::GetMultiDexLocation(i, dex_file_location.c_str());
+      const OatFile::OatDexFile* multi_dex = oat_file.GetOatDexFile(multi_dex_location.c_str(),
+                                                                    nullptr,
+                                                                    error_msg);
+      if (multi_dex == nullptr) {
+        *error_msg = StringPrintf("ValidateOatFile oat file '%s' is missing entry '%s'",
+                                  oat_file.GetLocation().c_str(),
+                                  multi_dex_location.c_str());
+        return false;
+      }
+
+      if (checksums[i] != multi_dex->GetDexFileLocationChecksum()) {
+        *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file "
+                                  "'%s' and dex file '%s' (0x%x != 0x%x)",
+                                  oat_file.GetLocation().c_str(),
+                                  multi_dex_location.c_str(),
+                                  multi_dex->GetDexFileLocationChecksum(),
+                                  checksums[i]);
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 void ImageSpace::ExtractMultiImageLocations(const std::string& input_image_file_name,
                                             const std::string& boot_classpath,
                                             std::vector<std::string>* image_file_names) {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 489a289..199bbdd 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -131,6 +131,17 @@
                                                 const std::vector<const char*>& oat_filenames,
                                                 const std::vector<const char*>& image_filenames);
 
+  // Returns true if the dex checksums in the given oat file match the
+  // checksums of the original dex files on disk. This is intended to be used
+  // to validate the boot image oat file, which may contain dex entries from
+  // multiple different (possibly multidex) dex files on disk. Prefer the
+  // OatFileAssistant for validating regular app oat files because the
+  // OatFileAssistant caches dex checksums that are reused to check both the
+  // oat and odex file.
+  //
+  // This function is exposed for testing purposes.
+  static bool ValidateOatFile(const OatFile& oat_file, std::string* error_msg);
+
   // Return the end of the image which includes non-heap objects such as ArtMethods and ArtFields.
   uint8_t* GetImageEnd() const {
     return Begin() + GetImageHeader().GetImageSize();
diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc
new file mode 100644
index 0000000..7a38074
--- /dev/null
+++ b/runtime/gc/space/image_space_test.cc
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "dexopt_test.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+TEST_F(DexoptTest, ValidateOatFile) {
+  std::string dex1 = GetScratchDir() + "/Dex1.jar";
+  std::string multidex1 = GetScratchDir() + "/MultiDex1.jar";
+  std::string dex2 = GetScratchDir() + "/Dex2.jar";
+  std::string oat_location = GetScratchDir() + "/Oat.oat";
+
+  Copy(GetDexSrc1(), dex1);
+  Copy(GetMultiDexSrc1(), multidex1);
+  Copy(GetDexSrc2(), dex2);
+
+  std::string error_msg;
+  std::vector<std::string> args;
+  args.push_back("--dex-file=" + dex1);
+  args.push_back("--dex-file=" + multidex1);
+  args.push_back("--dex-file=" + dex2);
+  args.push_back("--oat-file=" + oat_location);
+  ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+
+  std::unique_ptr<OatFile> oat(OatFile::Open(oat_location.c_str(),
+                                             oat_location.c_str(),
+                                             nullptr,
+                                             nullptr,
+                                             false,
+                                             /*low_4gb*/false,
+                                             nullptr,
+                                             &error_msg));
+  ASSERT_TRUE(oat != nullptr) << error_msg;
+
+  // Originally all the dex checksums should be up to date.
+  EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+  // Invalidate the dex1 checksum.
+  Copy(GetDexSrc2(), dex1);
+  EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+  // Restore the dex1 checksum.
+  Copy(GetDexSrc1(), dex1);
+  EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+  // Invalidate the non-main multidex checksum.
+  Copy(GetMultiDexSrc2(), multidex1);
+  EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+  // Restore the multidex checksum.
+  Copy(GetMultiDexSrc1(), multidex1);
+  EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+  // Invalidate the dex2 checksum.
+  Copy(GetDexSrc1(), dex2);
+  EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+  // restore the dex2 checksum.
+  Copy(GetDexSrc2(), dex2);
+  EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+  // Replace the multidex file with a non-multidex file.
+  Copy(GetDexSrc1(), multidex1);
+  EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+  // Restore the multidex file
+  Copy(GetMultiDexSrc1(), multidex1);
+  EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+  // Replace dex1 with a multidex file.
+  Copy(GetMultiDexSrc1(), dex1);
+  EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+  // Restore the dex1 file.
+  Copy(GetDexSrc1(), dex1);
+  EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+  // Remove the dex2 file.
+  EXPECT_EQ(0, unlink(dex2.c_str()));
+  EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+  // Restore the dex2 file.
+  Copy(GetDexSrc2(), dex2);
+  EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+  // Remove the multidex file.
+  EXPECT_EQ(0, unlink(multidex1.c_str()));
+  EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+}
+
+}  // namespace space
+}  // namespace gc
+}  // namespace art
diff --git a/runtime/gc/space/space_create_test.cc b/runtime/gc/space/space_create_test.cc
index 7bc4dc4..ca5f306 100644
--- a/runtime/gc/space/space_create_test.cc
+++ b/runtime/gc/space/space_create_test.cc
@@ -108,7 +108,7 @@
                                                         &ptr1_bytes_allocated,
                                                         &ptr1_usable_size,
                                                         &ptr1_bytes_tl_bulk_allocated)));
-  EXPECT_TRUE(ptr1.Get() != nullptr);
+  EXPECT_TRUE(ptr1 != nullptr);
   EXPECT_LE(1U * MB, ptr1_bytes_allocated);
   EXPECT_LE(1U * MB, ptr1_usable_size);
   EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated);
@@ -126,7 +126,7 @@
                                                                   &ptr3_bytes_allocated,
                                                                   &ptr3_usable_size,
                                                                   &ptr3_bytes_tl_bulk_allocated)));
-  EXPECT_TRUE(ptr3.Get() != nullptr);
+  EXPECT_TRUE(ptr3 != nullptr);
   EXPECT_LE(8U * MB, ptr3_bytes_allocated);
   EXPECT_LE(8U * MB, ptr3_usable_size);
   EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated);
@@ -154,7 +154,7 @@
                                                            &ptr6_bytes_allocated,
                                                            &ptr6_usable_size,
                                                            &ptr6_bytes_tl_bulk_allocated)));
-  EXPECT_TRUE(ptr6.Get() != nullptr);
+  EXPECT_TRUE(ptr6 != nullptr);
   EXPECT_LE(9U * MB, ptr6_bytes_allocated);
   EXPECT_LE(9U * MB, ptr6_usable_size);
   EXPECT_LE(ptr6_usable_size, ptr6_bytes_allocated);
@@ -193,7 +193,7 @@
                     &ptr1_bytes_allocated,
                     &ptr1_usable_size,
                     &ptr1_bytes_tl_bulk_allocated));
-  EXPECT_TRUE(ptr1.Get() != nullptr);
+  EXPECT_TRUE(ptr1 != nullptr);
   EXPECT_LE(1U * MB, ptr1_bytes_allocated);
   EXPECT_LE(1U * MB, ptr1_usable_size);
   EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated);
@@ -210,7 +210,7 @@
                               &ptr3_bytes_allocated,
                               &ptr3_usable_size,
                               &ptr3_bytes_tl_bulk_allocated));
-  EXPECT_TRUE(ptr3.Get() != nullptr);
+  EXPECT_TRUE(ptr3 != nullptr);
   EXPECT_LE(2U * MB, ptr3_bytes_allocated);
   EXPECT_LE(2U * MB, ptr3_usable_size);
   EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated);
@@ -242,7 +242,7 @@
                                                         &ptr1_bytes_allocated,
                                                         &ptr1_usable_size,
                                                         &ptr1_bytes_tl_bulk_allocated)));
-  EXPECT_TRUE(ptr1.Get() != nullptr);
+  EXPECT_TRUE(ptr1 != nullptr);
   EXPECT_LE(1U * MB, ptr1_bytes_allocated);
   EXPECT_LE(1U * MB, ptr1_usable_size);
   EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated);
@@ -260,7 +260,7 @@
                                                                   &ptr3_bytes_allocated,
                                                                   &ptr3_usable_size,
                                                                   &ptr3_bytes_tl_bulk_allocated)));
-  EXPECT_TRUE(ptr3.Get() != nullptr);
+  EXPECT_TRUE(ptr3 != nullptr);
   EXPECT_LE(8U * MB, ptr3_bytes_allocated);
   EXPECT_LE(8U * MB, ptr3_usable_size);
   EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated);
@@ -288,7 +288,7 @@
                                                            &ptr6_bytes_allocated,
                                                            &ptr6_usable_size,
                                                            &ptr6_bytes_tl_bulk_allocated)));
-  EXPECT_TRUE(ptr6.Get() != nullptr);
+  EXPECT_TRUE(ptr6 != nullptr);
   EXPECT_LE(9U * MB, ptr6_bytes_allocated);
   EXPECT_LE(9U * MB, ptr6_usable_size);
   EXPECT_LE(ptr6_usable_size, ptr6_bytes_allocated);
diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h
index cbb3d73..1fe3fb2 100644
--- a/runtime/gc/space/space_test.h
+++ b/runtime/gc/space/space_test.h
@@ -200,7 +200,7 @@
       }
       footprint = space->GetFootprint();
       EXPECT_GE(space->Size(), footprint);  // invariant
-      if (object.Get() != nullptr) {  // allocation succeeded
+      if (object != nullptr) {  // allocation succeeded
         lots_of_objects[i] = object.Get();
         size_t allocation_size = space->AllocationSize(object.Get(), nullptr);
         EXPECT_EQ(bytes_allocated, allocation_size);
@@ -296,7 +296,7 @@
     large_object.Assign(AllocWithGrowth(space, self, three_quarters_space, &bytes_allocated,
                                         nullptr, &bytes_tl_bulk_allocated));
   }
-  EXPECT_TRUE(large_object.Get() != nullptr);
+  EXPECT_TRUE(large_object != nullptr);
 
   // Sanity check footprint
   footprint = space->GetFootprint();
diff --git a/runtime/handle.h b/runtime/handle.h
index e4b6d29..ccff575 100644
--- a/runtime/handle.h
+++ b/runtime/handle.h
@@ -81,6 +81,14 @@
     return reference_;
   }
 
+  ALWAYS_INLINE bool operator!=(std::nullptr_t) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return !IsNull();
+  }
+
+  ALWAYS_INLINE bool operator==(std::nullptr_t) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return IsNull();
+  }
+
  protected:
   template<typename S>
   explicit Handle(StackReference<S>* reference)
diff --git a/runtime/image.cc b/runtime/image.cc
index 87f4295..1acfcf5 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -25,7 +25,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '7', '\0' };  // hash-based DexCache types
+const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '7', '\0' };  // Enable string compression.
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/imtable_test.cc b/runtime/imtable_test.cc
index 8cbe291..17149df 100644
--- a/runtime/imtable_test.cc
+++ b/runtime/imtable_test.cc
@@ -53,7 +53,7 @@
         ObjPtr<mirror::ClassLoader>::DownCast(self->DecodeJObject(jclass_loader_a)));
     Handle<mirror::Class> h_class_a(
           hs.NewHandle(class_linker->FindClass(self, class_name.c_str(), h_class_loader)));
-    if (h_class_a.Get() == nullptr) {
+    if (h_class_a == nullptr) {
       LOG(ERROR) << self->GetException()->Dump();
       CHECK(false) << "h_class_a == nullptr";
     }
@@ -63,7 +63,7 @@
         ObjPtr<mirror::ClassLoader>::DownCast(self->DecodeJObject(jclass_loader_b)));
     Handle<mirror::Class> h_class_b(
           hs.NewHandle(class_linker->FindClass(self, class_name.c_str(), h_class_loader)));
-    if (h_class_b.Get() == nullptr) {
+    if (h_class_b == nullptr) {
       LOG(ERROR) << self->GetException()->Dump();
       CHECK(false) << "h_class_b == nullptr";
     }
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index bf4cab2..6aefe23 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -64,13 +64,13 @@
   StackHandleScope<4> hs(soa.Self());
   ASSERT_TRUE(c != nullptr);
   Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
-  ASSERT_TRUE(obj0.Get() != nullptr);
+  ASSERT_TRUE(obj0 != nullptr);
   Handle<mirror::Object> obj1 = hs.NewHandle(c->AllocObject(soa.Self()));
-  ASSERT_TRUE(obj1.Get() != nullptr);
+  ASSERT_TRUE(obj1 != nullptr);
   Handle<mirror::Object> obj2 = hs.NewHandle(c->AllocObject(soa.Self()));
-  ASSERT_TRUE(obj2.Get() != nullptr);
+  ASSERT_TRUE(obj2 != nullptr);
   Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self()));
-  ASSERT_TRUE(obj3.Get() != nullptr);
+  ASSERT_TRUE(obj3 != nullptr);
 
   const IRTSegmentState cookie = kIRTFirstSegment;
 
@@ -282,15 +282,15 @@
   StackHandleScope<5> hs(soa.Self());
   ASSERT_TRUE(c != nullptr);
   Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
-  ASSERT_TRUE(obj0.Get() != nullptr);
+  ASSERT_TRUE(obj0 != nullptr);
   Handle<mirror::Object> obj1 = hs.NewHandle(c->AllocObject(soa.Self()));
-  ASSERT_TRUE(obj1.Get() != nullptr);
+  ASSERT_TRUE(obj1 != nullptr);
   Handle<mirror::Object> obj2 = hs.NewHandle(c->AllocObject(soa.Self()));
-  ASSERT_TRUE(obj2.Get() != nullptr);
+  ASSERT_TRUE(obj2 != nullptr);
   Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self()));
-  ASSERT_TRUE(obj3.Get() != nullptr);
+  ASSERT_TRUE(obj3 != nullptr);
   Handle<mirror::Object> obj4 = hs.NewHandle(c->AllocObject(soa.Self()));
-  ASSERT_TRUE(obj4.Get() != nullptr);
+  ASSERT_TRUE(obj4 != nullptr);
 
   std::string error_msg;
 
@@ -491,7 +491,7 @@
   StackHandleScope<1> hs(soa.Self());
   ASSERT_TRUE(c != nullptr);
   Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
-  ASSERT_TRUE(obj0.Get() != nullptr);
+  ASSERT_TRUE(obj0 != nullptr);
 
   std::string error_msg;
   IndirectReferenceTable irt(kTableMax,
diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc
index 3991d65..f0d0260 100644
--- a/runtime/intern_table_test.cc
+++ b/runtime/intern_table_test.cc
@@ -36,10 +36,10 @@
   Handle<mirror::String> foo_3(
       hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "foo")));
   Handle<mirror::String> bar(hs.NewHandle(intern_table.InternStrong(3, "bar")));
-  ASSERT_TRUE(foo_1.Get() != nullptr);
-  ASSERT_TRUE(foo_2.Get() != nullptr);
-  ASSERT_TRUE(foo_3.Get() != nullptr);
-  ASSERT_TRUE(bar.Get() != nullptr);
+  ASSERT_TRUE(foo_1 != nullptr);
+  ASSERT_TRUE(foo_2 != nullptr);
+  ASSERT_TRUE(foo_3 != nullptr);
+  ASSERT_TRUE(bar != nullptr);
   EXPECT_EQ(foo_1.Get(), foo_2.Get());
   EXPECT_TRUE(foo_1->Equals("foo"));
   EXPECT_TRUE(foo_2->Equals("foo"));
@@ -204,9 +204,9 @@
   Handle<mirror::String> foo(hs.NewHandle(intern_table.InternStrong(3, "foo")));
   Handle<mirror::String> bar(hs.NewHandle(intern_table.InternStrong(3, "bar")));
   Handle<mirror::String> foobar(hs.NewHandle(intern_table.InternStrong(6, "foobar")));
-  ASSERT_TRUE(foo.Get() != nullptr);
-  ASSERT_TRUE(bar.Get() != nullptr);
-  ASSERT_TRUE(foobar.Get() != nullptr);
+  ASSERT_TRUE(foo != nullptr);
+  ASSERT_TRUE(bar != nullptr);
+  ASSERT_TRUE(foobar != nullptr);
   ASSERT_TRUE(foo->Equals("foo"));
   ASSERT_TRUE(bar->Equals("bar"));
   ASSERT_TRUE(foobar->Equals("foobar"));
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 28bcb97..8978bfd 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -519,7 +519,7 @@
   }
 }
 
-template<bool is_range, bool do_access_check>
+template<bool is_range>
 bool DoInvokePolymorphic(Thread* self,
                          ShadowFrame& shadow_frame,
                          const Instruction* inst,
@@ -539,10 +539,10 @@
   // was symbolically invoked in bytecode (say MethodHandle.invoke or MethodHandle.invokeExact)
   // and not the method that we'll dispatch to in the end.
   StackHandleScope<5> hs(self);
-  Handle<mirror::MethodHandleImpl> method_handle(hs.NewHandle(
-      ObjPtr<mirror::MethodHandleImpl>::DownCast(
+  Handle<mirror::MethodHandle> method_handle(hs.NewHandle(
+      ObjPtr<mirror::MethodHandle>::DownCast(
           MakeObjPtr(shadow_frame.GetVRegReference(vRegC)))));
-  if (UNLIKELY(method_handle.Get() == nullptr)) {
+  if (UNLIKELY(method_handle == nullptr)) {
     // Note that the invoke type is kVirtual here because a call to a signature
     // polymorphic method is shaped like a virtual call at the bytecode level.
     ThrowNullPointerExceptionForMethodAccess(invoke_method_idx, InvokeType::kVirtual);
@@ -564,7 +564,7 @@
       hs.NewHandle<mirror::ClassLoader>(caller_class->GetClassLoader()))));
 
   // This implies we couldn't resolve one or more types in this method handle.
-  if (UNLIKELY(callsite_type.Get() == nullptr)) {
+  if (UNLIKELY(callsite_type == nullptr)) {
     CHECK(self->IsExceptionPending());
     return false;
   }
@@ -584,31 +584,300 @@
     // VRegC is the register holding the method handle. Arguments passed
     // to the method handle's target do not include the method handle.
     uint32_t first_arg = inst->VRegC_4rcc() + 1;
-    return DoInvokePolymorphic<is_range, do_access_check>(self,
-                                                          invoke_method,
-                                                          shadow_frame,
-                                                          method_handle,
-                                                          callsite_type,
-                                                          args /* unused */,
-                                                          first_arg,
-                                                          result);
+    return DoInvokePolymorphic<is_range>(self,
+                                         invoke_method,
+                                         shadow_frame,
+                                         method_handle,
+                                         callsite_type,
+                                         args /* unused */,
+                                         first_arg,
+                                         result);
   } else {
     // Get the register arguments for the invoke.
     inst->GetVarArgs(args, inst_data);
     // Drop the first register which is the method handle performing the invoke.
     memmove(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1));
     args[Instruction::kMaxVarArgRegs - 1] = 0;
-    return DoInvokePolymorphic<is_range, do_access_check>(self,
-                                                          invoke_method,
-                                                          shadow_frame,
-                                                          method_handle,
-                                                          callsite_type,
-                                                          args,
-                                                          args[0],
-                                                          result);
+    return DoInvokePolymorphic<is_range>(self,
+                                         invoke_method,
+                                         shadow_frame,
+                                         method_handle,
+                                         callsite_type,
+                                         args,
+                                         args[0],
+                                         result);
   }
 }
 
+static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self,
+                                                      ShadowFrame& shadow_frame,
+                                                      uint32_t call_site_idx)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ArtMethod* referrer = shadow_frame.GetMethod();
+  const DexFile* dex_file = referrer->GetDexFile();
+  const DexFile::CallSiteIdItem& csi = dex_file->GetCallSiteId(call_site_idx);
+
+  StackHandleScope<9> hs(self);
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
+  Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
+
+  CallSiteArrayValueIterator it(*dex_file, csi);
+  uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Handle<mirror::MethodHandle>
+      bootstrap(hs.NewHandle(class_linker->ResolveMethodHandle(method_handle_idx, referrer)));
+  if (bootstrap.IsNull()) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+  Handle<mirror::MethodType> bootstrap_method_type = hs.NewHandle(bootstrap->GetMethodType());
+  it.Next();
+
+  DCHECK_EQ(static_cast<size_t>(bootstrap->GetMethodType()->GetPTypes()->GetLength()), it.Size());
+  const size_t num_bootstrap_vregs = bootstrap->GetMethodType()->NumberOfVRegs();
+
+  // Set-up a shadow frame for invoking the bootstrap method handle.
+  ShadowFrameAllocaUniquePtr bootstrap_frame =
+      CREATE_SHADOW_FRAME(num_bootstrap_vregs, nullptr, referrer, shadow_frame.GetDexPC());
+  ScopedStackedShadowFramePusher pusher(
+      self, bootstrap_frame.get(), StackedShadowFrameType::kShadowFrameUnderConstruction);
+  size_t vreg = 0;
+
+  // The first parameter is a MethodHandles lookup instance.
+  {
+    Handle<mirror::Class> lookup_class(hs.NewHandle(bootstrap->GetTargetClass()));
+    ObjPtr<mirror::MethodHandlesLookup> lookup =
+        mirror::MethodHandlesLookup::Create(self, lookup_class);
+    if (lookup.IsNull()) {
+      DCHECK(self->IsExceptionPending());
+      return nullptr;
+    }
+    bootstrap_frame->SetVRegReference(vreg++, lookup.Ptr());
+  }
+
+  // The second parameter is the name to lookup.
+  {
+    dex::StringIndex name_idx(static_cast<uint32_t>(it.GetJavaValue().i));
+    ObjPtr<mirror::String> name = class_linker->ResolveString(*dex_file, name_idx, dex_cache);
+    if (name.IsNull()) {
+      DCHECK(self->IsExceptionPending());
+      return nullptr;
+    }
+    bootstrap_frame->SetVRegReference(vreg++, name.Ptr());
+  }
+  it.Next();
+
+  // The third parameter is the method type associated with the name.
+  uint32_t method_type_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+  Handle<mirror::MethodType>
+      method_type(hs.NewHandle(class_linker->ResolveMethodType(*dex_file,
+                                                               method_type_idx,
+                                                               dex_cache,
+                                                               class_loader)));
+  if (method_type.IsNull()) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+  bootstrap_frame->SetVRegReference(vreg++, method_type.Get());
+  it.Next();
+
+  // Append remaining arguments (if any).
+  while (it.HasNext()) {
+    const jvalue& jvalue = it.GetJavaValue();
+    switch (it.GetValueType()) {
+      case EncodedArrayValueIterator::ValueType::kBoolean:
+      case EncodedArrayValueIterator::ValueType::kByte:
+      case EncodedArrayValueIterator::ValueType::kChar:
+      case EncodedArrayValueIterator::ValueType::kShort:
+      case EncodedArrayValueIterator::ValueType::kInt:
+        bootstrap_frame->SetVReg(vreg, jvalue.i);
+        vreg += 1;
+        break;
+      case EncodedArrayValueIterator::ValueType::kLong:
+        bootstrap_frame->SetVRegLong(vreg, jvalue.j);
+        vreg += 2;
+        break;
+      case EncodedArrayValueIterator::ValueType::kFloat:
+        bootstrap_frame->SetVRegFloat(vreg, jvalue.f);
+        vreg += 1;
+        break;
+      case EncodedArrayValueIterator::ValueType::kDouble:
+        bootstrap_frame->SetVRegDouble(vreg, jvalue.d);
+        vreg += 2;
+        break;
+      case EncodedArrayValueIterator::ValueType::kMethodType: {
+        uint32_t idx = static_cast<uint32_t>(jvalue.i);
+        ObjPtr<mirror::MethodType> ref =
+            class_linker->ResolveMethodType(*dex_file, idx, dex_cache, class_loader);
+        if (ref.IsNull()) {
+          DCHECK(self->IsExceptionPending());
+          return nullptr;
+        }
+        bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
+        vreg += 1;
+        break;
+      }
+      case EncodedArrayValueIterator::ValueType::kMethodHandle: {
+        uint32_t idx = static_cast<uint32_t>(jvalue.i);
+        ObjPtr<mirror::MethodHandle> ref =
+            class_linker->ResolveMethodHandle(idx, referrer);
+        if (ref.IsNull()) {
+          DCHECK(self->IsExceptionPending());
+          return nullptr;
+        }
+        bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
+        vreg += 1;
+        break;
+      }
+      case EncodedArrayValueIterator::ValueType::kString: {
+        dex::StringIndex idx(static_cast<uint32_t>(jvalue.i));
+        ObjPtr<mirror::String> ref = class_linker->ResolveString(*dex_file, idx, dex_cache);
+        if (ref.IsNull()) {
+          DCHECK(self->IsExceptionPending());
+          return nullptr;
+        }
+        bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
+        vreg += 1;
+        break;
+      }
+      case EncodedArrayValueIterator::ValueType::kType: {
+        dex::TypeIndex idx(static_cast<uint32_t>(jvalue.i));
+        ObjPtr<mirror::Class> ref =
+            class_linker->ResolveType(*dex_file, idx, dex_cache, class_loader);
+        if (ref.IsNull()) {
+          DCHECK(self->IsExceptionPending());
+          return nullptr;
+        }
+        bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
+        vreg += 1;
+        break;
+      }
+      case EncodedArrayValueIterator::ValueType::kNull:
+        bootstrap_frame->SetVRegReference(vreg, nullptr);
+        vreg += 1;
+        break;
+      case EncodedArrayValueIterator::ValueType::kField:
+      case EncodedArrayValueIterator::ValueType::kMethod:
+      case EncodedArrayValueIterator::ValueType::kEnum:
+      case EncodedArrayValueIterator::ValueType::kArray:
+      case EncodedArrayValueIterator::ValueType::kAnnotation:
+        // Unreachable based on current EncodedArrayValueIterator::Next().
+        UNREACHABLE();
+    }
+
+    it.Next();
+  }
+
+  // Invoke the bootstrap method handle.
+  JValue result;
+
+  // This array of arguments is unused. DoInvokePolymorphic() operates on either a
+  // an argument array or a range, but always takes an array argument.
+  uint32_t args_unused[Instruction::kMaxVarArgRegs];
+  ArtMethod* invoke_exact =
+      jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact);
+  bool invoke_success = DoInvokePolymorphic<true /* is_range */>(self,
+                                                                 invoke_exact,
+                                                                 *bootstrap_frame,
+                                                                 bootstrap,
+                                                                 bootstrap_method_type,
+                                                                 args_unused,
+                                                                 0,
+                                                                 &result);
+  if (!invoke_success) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  Handle<mirror::Object> object(hs.NewHandle(result.GetL()));
+
+  // Check the result is not null.
+  if (UNLIKELY(object.IsNull())) {
+    ThrowNullPointerException("CallSite == null");
+    return nullptr;
+  }
+
+  // Check the result type is a subclass of CallSite.
+  if (UNLIKELY(!object->InstanceOf(mirror::CallSite::StaticClass()))) {
+    ThrowClassCastException(object->GetClass(), mirror::CallSite::StaticClass());
+    return nullptr;
+  }
+
+  Handle<mirror::CallSite> call_site =
+      hs.NewHandle(ObjPtr<mirror::CallSite>::DownCast(ObjPtr<mirror::Object>(result.GetL())));
+
+  // Check the call site target is not null as we're going to invoke it.
+  Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget());
+  if (UNLIKELY(target.IsNull())) {
+    ThrowNullPointerException("CallSite target == null");
+    return nullptr;
+  }
+
+  // Check the target method type matches the method type requested.
+  if (UNLIKELY(!target->GetMethodType()->IsExactMatch(method_type.Get()))) {
+    ThrowWrongMethodTypeException(target->GetMethodType(), method_type.Get());
+    return nullptr;
+  }
+
+  return call_site.Get();
+}
+
+template<bool is_range>
+bool DoInvokeCustom(Thread* self,
+                    ShadowFrame& shadow_frame,
+                    const Instruction* inst,
+                    uint16_t inst_data,
+                    JValue* result)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  // invoke-custom is not supported in transactions. In transactions
+  // there is a limited set of types supported. invoke-custom allows
+  // running arbitrary code and instantiating arbitrary types.
+  CHECK(!Runtime::Current()->IsActiveTransaction());
+  StackHandleScope<4> hs(self);
+  Handle<mirror::DexCache> dex_cache(hs.NewHandle(shadow_frame.GetMethod()->GetDexCache()));
+  const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+  MutableHandle<mirror::CallSite>
+      call_site(hs.NewHandle(dex_cache->GetResolvedCallSite(call_site_idx)));
+  if (call_site.IsNull()) {
+    call_site.Assign(InvokeBootstrapMethod(self, shadow_frame, call_site_idx));
+    if (UNLIKELY(call_site.IsNull())) {
+      CHECK(self->IsExceptionPending());
+      ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method",
+                                       call_site_idx);
+      result->SetJ(0);
+      return false;
+    }
+    mirror::CallSite* winning_call_site =
+        dex_cache->SetResolvedCallSite(call_site_idx, call_site.Get());
+    call_site.Assign(winning_call_site);
+  }
+
+  // CallSite.java checks the re-assignment of the call site target
+  // when mutating call site targets. We only check the target is
+  // non-null and has the right type during bootstrap method execution.
+  Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget());
+  Handle<mirror::MethodType> target_method_type = hs.NewHandle(target->GetMethodType());
+  DCHECK_EQ(static_cast<size_t>(inst->VRegA()), target_method_type->NumberOfVRegs());
+
+  uint32_t args[Instruction::kMaxVarArgRegs];
+  if (is_range) {
+    args[0] = inst->VRegC_3rc();
+  } else {
+    inst->GetVarArgs(args, inst_data);
+  }
+
+  ArtMethod* invoke_exact =
+      jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact);
+  return DoInvokePolymorphic<is_range>(self,
+                                       invoke_exact,
+                                       shadow_frame,
+                                       target,
+                                       target_method_type,
+                                       args,
+                                       args[0],
+                                       result);
+}
+
 template <bool is_range>
 inline void CopyRegisters(ShadowFrame& caller_frame,
                           ShadowFrame* callee_frame,
@@ -975,17 +1244,24 @@
 EXPLICIT_DO_CALL_TEMPLATE_DECL(true, true);
 #undef EXPLICIT_DO_CALL_TEMPLATE_DECL
 
-// Explicit DoInvokePolymorphic template function declarations.
-#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range, _do_assignability_check)  \
-  template REQUIRES_SHARED(Locks::mutator_lock_)                                          \
-  bool DoInvokePolymorphic<_is_range, _do_assignability_check>(                           \
-      Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,                   \
+// Explicit DoInvokeCustom template function declarations.
+#define EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(_is_range)               \
+  template REQUIRES_SHARED(Locks::mutator_lock_)                         \
+  bool DoInvokeCustom<_is_range>(                                        \
+      Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,  \
       uint16_t inst_data, JValue* result)
+EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(false);
+EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(true);
+#undef EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL
 
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, false);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, true);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, false);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, true);
+// Explicit DoInvokePolymorphic template function declarations.
+#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range)          \
+  template REQUIRES_SHARED(Locks::mutator_lock_)                         \
+  bool DoInvokePolymorphic<_is_range>(                                   \
+      Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,  \
+      uint16_t inst_data, JValue* result)
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false);
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true);
 #undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL
 
 // Explicit DoFilledNewArray template function declarations.
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 7ef3508..6b22af9 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -40,9 +40,11 @@
 #include "entrypoints/entrypoint_utils-inl.h"
 #include "handle_scope-inl.h"
 #include "jit/jit.h"
+#include "mirror/call_site.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache.h"
 #include "mirror/method.h"
+#include "mirror/method_handles_lookup.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/string-inl.h"
@@ -154,13 +156,21 @@
 }
 
 // Performs a signature polymorphic invoke (invoke-polymorphic/invoke-polymorphic-range).
-template<bool is_range, bool do_access_check>
+template<bool is_range>
 bool DoInvokePolymorphic(Thread* self,
                          ShadowFrame& shadow_frame,
                          const Instruction* inst,
                          uint16_t inst_data,
                          JValue* result);
 
+// Performs a custom invoke (invoke-custom/invoke-custom-range).
+template<bool is_range>
+bool DoInvokeCustom(Thread* self,
+                    ShadowFrame& shadow_frame,
+                    const Instruction* inst,
+                    uint16_t inst_data,
+                    JValue* result);
+
 // Handles invoke-virtual-quick and invoke-virtual-quick-range instructions.
 // Returns true on success, otherwise throws an exception and returns false.
 template<bool is_range>
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index a77a3fc..b191dd7 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -1524,7 +1524,7 @@
       case Instruction::INVOKE_POLYMORPHIC: {
         PREAMBLE();
         DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
-        bool success = DoInvokePolymorphic<false, do_access_check>(
+        bool success = DoInvokePolymorphic<false /* is_range */>(
             self, shadow_frame, inst, inst_data, &result_register);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx);
         break;
@@ -1532,11 +1532,27 @@
       case Instruction::INVOKE_POLYMORPHIC_RANGE: {
         PREAMBLE();
         DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
-        bool success = DoInvokePolymorphic<true, do_access_check>(
+        bool success = DoInvokePolymorphic<true /* is_range */>(
             self, shadow_frame, inst, inst_data, &result_register);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx);
         break;
       }
+      case Instruction::INVOKE_CUSTOM: {
+        PREAMBLE();
+        DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+        bool success = DoInvokeCustom<false /* is_range */>(
+            self, shadow_frame, inst, inst_data, &result_register);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+        break;
+      }
+      case Instruction::INVOKE_CUSTOM_RANGE: {
+        PREAMBLE();
+        DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+        bool success = DoInvokeCustom<true /* is_range */>(
+            self, shadow_frame, inst, inst_data, &result_register);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+        break;
+      }
       case Instruction::NEG_INT:
         PREAMBLE();
         shadow_frame.SetVReg(
@@ -2315,7 +2331,7 @@
         break;
       case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
       case Instruction::UNUSED_F3 ... Instruction::UNUSED_F9:
-      case Instruction::UNUSED_FC ... Instruction::UNUSED_FF:
+      case Instruction::UNUSED_FE ... Instruction::UNUSED_FF:
       case Instruction::UNUSED_79:
       case Instruction::UNUSED_7A:
         UnexpectedOpcode(inst, shadow_frame);
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 545cc1a..66f14b9 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -124,7 +124,7 @@
                                       const std::string& method_name, bool initialize_class,
                                       bool abort_if_not_found)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  CHECK(className.Get() != nullptr);
+  CHECK(className != nullptr);
   std::string descriptor(DotToDescriptor(className->ToModifiedUtf8().c_str()));
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
 
@@ -239,7 +239,7 @@
   Handle<mirror::Class> h_klass(hs.NewHandle(klass));
 
   // Check that it's not null.
-  if (h_klass.Get() == nullptr) {
+  if (h_klass == nullptr) {
     AbortTransactionOrFail(self, "Class reference is null for newInstance");
     return;
   }
@@ -263,7 +263,7 @@
     auto* cons = h_klass->FindDeclaredDirectMethod("<init>", "()V", cl->GetImagePointerSize());
     if (cons != nullptr) {
       Handle<mirror::Object> h_obj(hs.NewHandle(klass->AllocObject(self)));
-      CHECK(h_obj.Get() != nullptr);  // We don't expect OOM at compile-time.
+      CHECK(h_obj != nullptr);  // We don't expect OOM at compile-time.
       EnterInterpreterFromInvoke(self, cons, h_obj.Get(), nullptr, nullptr);
       if (!self->IsExceptionPending()) {
         result->SetL(h_obj.Get());
@@ -542,7 +542,7 @@
 
   // Create byte array for content.
   Handle<mirror::ByteArray> h_array(hs.NewHandle(mirror::ByteArray::Alloc(self, map_size)));
-  if (h_array.Get() == nullptr) {
+  if (h_array == nullptr) {
     AbortTransactionOrFail(self, "Could not find/create byte array class");
     return;
   }
@@ -556,7 +556,7 @@
       runtime->GetClassLinker()->FindClass(self,
                                            "Ljava/io/ByteArrayInputStream;",
                                            ScopedNullHandle<mirror::ClassLoader>())));
-  if (h_class.Get() == nullptr) {
+  if (h_class == nullptr) {
     AbortTransactionOrFail(self, "Could not find ByteArrayInputStream class");
     return;
   }
@@ -566,7 +566,7 @@
   }
 
   Handle<mirror::Object> h_obj(hs.NewHandle(h_class->AllocObject(self)));
-  if (h_obj.Get() == nullptr) {
+  if (h_obj == nullptr) {
     AbortTransactionOrFail(self, "Could not allocate ByteArrayInputStream object");
     return;
   }
@@ -800,7 +800,7 @@
   StackHandleScope<4> hs(self);
   Handle<mirror::String> h_key(
       hs.NewHandle(reinterpret_cast<mirror::String*>(shadow_frame->GetVRegReference(arg_offset))));
-  if (h_key.Get() == nullptr) {
+  if (h_key == nullptr) {
     AbortTransactionOrFail(self, "getProperty key was null");
     return;
   }
@@ -815,7 +815,7 @@
       class_linker->FindClass(self,
                               "Ljava/lang/AndroidHardcodedSystemProperties;",
                               ScopedNullHandle<mirror::ClassLoader>())));
-  if (h_props_class.Get() == nullptr) {
+  if (h_props_class == nullptr) {
     AbortTransactionOrFail(self, "Could not find AndroidHardcodedSystemProperties");
     return;
   }
@@ -837,7 +837,7 @@
   ObjPtr<mirror::Object> props = static_properties->GetObject(h_props_class.Get());
   Handle<mirror::ObjectArray<mirror::ObjectArray<mirror::String>>> h_2string_array(hs.NewHandle(
       props->AsObjectArray<mirror::ObjectArray<mirror::String>>()));
-  if (h_2string_array.Get() == nullptr) {
+  if (h_2string_array == nullptr) {
     AbortTransactionOrFail(self, "Field %s is null", kAndroidHardcodedSystemPropertiesFieldName);
     return;
   }
@@ -849,7 +849,7 @@
       hs.NewHandle<mirror::ObjectArray<mirror::String>>(nullptr));
   for (int32_t i = 0; i < prop_count; ++i) {
     h_string_array.Assign(h_2string_array->Get(i));
-    if (h_string_array.Get() == nullptr ||
+    if (h_string_array == nullptr ||
         h_string_array->GetLength() != 2 ||
         h_string_array->Get(0) == nullptr) {
       AbortTransactionOrFail(self,
@@ -924,7 +924,7 @@
   StackHandleScope<2> hs(self);
   Handle<mirror::Class> h_class(hs.NewHandle(klass));
   Handle<mirror::Object> h_obj(hs.NewHandle(h_class->AllocObject(self)));
-  if (h_obj.Get() != nullptr) {
+  if (h_obj != nullptr) {
     ArtMethod* init_method = h_class->FindDirectMethod(
         "<init>", "()V", class_linker->GetImagePointerSize());
     if (init_method == nullptr) {
@@ -955,6 +955,59 @@
   }
 }
 
+void UnstartedRuntime::UnstartedThreadCurrentThread(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset ATTRIBUTE_UNUSED) {
+  if (CheckCallers(shadow_frame,
+                   { "void java.lang.Thread.init(java.lang.ThreadGroup, java.lang.Runnable, "
+                         "java.lang.String, long)",
+                     "void java.lang.Thread.<init>()",
+                     "void java.util.logging.LogManager$Cleaner.<init>("
+                         "java.util.logging.LogManager)" })) {
+    // Whitelist LogManager$Cleaner, which is an unstarted Thread (for a shutdown hook). The
+    // Thread constructor only asks for the current thread to set up defaults and add the
+    // thread as unstarted to the ThreadGroup. A faked-up main thread peer is good enough for
+    // these purposes.
+    Runtime::Current()->InitThreadGroups(self);
+    jobject main_peer =
+        self->CreateCompileTimePeer(self->GetJniEnv(),
+                                    "main",
+                                    false,
+                                    Runtime::Current()->GetMainThreadGroup());
+    if (main_peer == nullptr) {
+      AbortTransactionOrFail(self, "Failed allocating peer");
+      return;
+    }
+
+    result->SetL(self->DecodeJObject(main_peer));
+    self->GetJniEnv()->DeleteLocalRef(main_peer);
+  } else {
+    AbortTransactionOrFail(self,
+                           "Thread.currentThread() does not support %s",
+                           GetImmediateCaller(shadow_frame).c_str());
+  }
+}
+
+void UnstartedRuntime::UnstartedThreadGetNativeState(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset ATTRIBUTE_UNUSED) {
+  if (CheckCallers(shadow_frame,
+                   { "java.lang.Thread$State java.lang.Thread.getState()",
+                     "java.lang.ThreadGroup java.lang.Thread.getThreadGroup()",
+                     "void java.lang.Thread.init(java.lang.ThreadGroup, java.lang.Runnable, "
+                         "java.lang.String, long)",
+                     "void java.lang.Thread.<init>()",
+                     "void java.util.logging.LogManager$Cleaner.<init>("
+                         "java.util.logging.LogManager)" })) {
+    // Whitelist reading the state of the "main" thread when creating another (unstarted) thread
+    // for LogManager. Report the thread as "new" (it really only counts that it isn't terminated).
+    constexpr int32_t kJavaRunnable = 1;
+    result->SetI(kJavaRunnable);
+  } else {
+    AbortTransactionOrFail(self,
+                           "Thread.getNativeState() does not support %s",
+                           GetImmediateCaller(shadow_frame).c_str());
+  }
+}
+
 void UnstartedRuntime::UnstartedMathCeil(
     Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
   result->SetD(ceil(shadow_frame->GetVRegDouble(arg_offset)));
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
index 96b35e4..929b747 100644
--- a/runtime/interpreter/unstarted_runtime_list.h
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -66,6 +66,8 @@
   V(StringFactoryNewStringFromString, "java.lang.String java.lang.StringFactory.newStringFromString(java.lang.String)") \
   V(StringFastSubstring, "java.lang.String java.lang.String.fastSubstring(int, int)") \
   V(StringToCharArray, "char[] java.lang.String.toCharArray()") \
+  V(ThreadCurrentThread, "java.lang.Thread java.lang.Thread.currentThread()") \
+  V(ThreadGetNativeState, "int java.lang.Thread.nativeGetStatus(boolean)") \
   V(UnsafeCompareAndSwapLong, "boolean sun.misc.Unsafe.compareAndSwapLong(java.lang.Object, long, long, long)") \
   V(UnsafeCompareAndSwapObject, "boolean sun.misc.Unsafe.compareAndSwapObject(java.lang.Object, long, java.lang.Object, java.lang.Object)") \
   V(UnsafeGetObjectVolatile, "java.lang.Object sun.misc.Unsafe.getObjectVolatile(java.lang.Object, long)") \
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index 31be587..98a17e4 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -960,7 +960,7 @@
         class_linker->FindClass(self,
                                 "Lsun/misc/FloatingDecimal;",
                                 ScopedNullHandle<mirror::ClassLoader>()));
-    ASSERT_TRUE(floating_decimal.Get() != nullptr);
+    ASSERT_TRUE(floating_decimal != nullptr);
     ASSERT_TRUE(class_linker->EnsureInitialized(self, floating_decimal, true, true));
 
     ArtMethod* caller_method = floating_decimal->FindDeclaredDirectMethod(
@@ -1014,7 +1014,7 @@
           class_linker->FindClass(self,
                                   "Ljava/lang/Double;",
                                   ScopedNullHandle<mirror::ClassLoader>()));
-  ASSERT_TRUE(double_class.Get() != nullptr);
+  ASSERT_TRUE(double_class != nullptr);
   ASSERT_TRUE(class_linker->EnsureInitialized(self, double_class, true, true));
 
   ArtMethod* method = double_class->FindDeclaredDirectMethod("toString",
@@ -1039,5 +1039,50 @@
   ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
 }
 
+TEST_F(UnstartedRuntimeTest, ThreadCurrentThread) {
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+
+  JValue result;
+  ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  StackHandleScope<1> hs(self);
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Handle<mirror::Class> thread_class = hs.NewHandle(
+      class_linker->FindClass(self, "Ljava/lang/Thread;", ScopedNullHandle<mirror::ClassLoader>()));
+  ASSERT_TRUE(thread_class.Get() != nullptr);
+  ASSERT_TRUE(class_linker->EnsureInitialized(self, thread_class, true, true));
+
+  // Negative test. In general, currentThread should fail (as we should not leak a peer that will
+  // be recreated at runtime).
+  PrepareForAborts();
+
+  {
+    Transaction transaction;
+    Runtime::Current()->EnterTransactionMode(&transaction);
+    UnstartedThreadCurrentThread(self, shadow_frame, &result, 0);
+    Runtime::Current()->ExitTransactionMode();
+    ASSERT_TRUE(self->IsExceptionPending());
+    ASSERT_TRUE(transaction.IsAborted());
+    self->ClearException();
+  }
+
+  ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
+}
+
+TEST_F(UnstartedRuntimeTest, LogManager) {
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+
+  StackHandleScope<1> hs(self);
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Handle<mirror::Class> log_manager_class = hs.NewHandle(
+          class_linker->FindClass(self,
+                                  "Ljava/util/logging/LogManager;",
+                                  ScopedNullHandle<mirror::ClassLoader>()));
+  ASSERT_TRUE(log_manager_class.Get() != nullptr);
+  ASSERT_TRUE(class_linker->EnsureInitialized(self, log_manager_class, true, true));
+}
+
 }  // namespace interpreter
 }  // namespace art
diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc
index 4615574..bd7251b 100644
--- a/runtime/jdwp/object_registry.cc
+++ b/runtime/jdwp/object_registry.cc
@@ -57,7 +57,7 @@
 // Template instantiations must be declared below.
 template<class T>
 JDWP::ObjectId ObjectRegistry::Add(Handle<T> obj_h) {
-  if (obj_h.Get() == nullptr) {
+  if (obj_h == nullptr) {
     return 0;
   }
   return InternalAdd(obj_h);
@@ -76,7 +76,7 @@
 
 template<class T>
 JDWP::ObjectId ObjectRegistry::InternalAdd(Handle<T> obj_h) {
-  CHECK(obj_h.Get() != nullptr);
+  CHECK(obj_h != nullptr);
 
   Thread* const self = Thread::Current();
   self->AssertNoPendingException();
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 0ac388a..60ab275 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -686,6 +686,10 @@
 // shouldn't be used since it is no longer logically in the jit code cache.
 // TODO We should add DCHECKS that validate that the JIT is paused when this method is entered.
 void JitCodeCache::MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) {
+  // Native methods have no profiling info and need no special handling from the JIT code cache.
+  if (old_method->IsNative()) {
+    return;
+  }
   MutexLock mu(Thread::Current(), lock_);
   // Update ProfilingInfo to the new one and remove it from the old_method.
   if (old_method->GetProfilingInfo(kRuntimePointerSize) != nullptr) {
diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc
index 405280d..7d80d2c 100644
--- a/runtime/jit/profiling_info.cc
+++ b/runtime/jit/profiling_info.cc
@@ -77,20 +77,18 @@
 }
 
 InlineCache* ProfilingInfo::GetInlineCache(uint32_t dex_pc) {
-  InlineCache* cache = nullptr;
   // TODO: binary search if array is too long.
   for (size_t i = 0; i < number_of_inline_caches_; ++i) {
     if (cache_[i].dex_pc_ == dex_pc) {
-      cache = &cache_[i];
-      break;
+      return &cache_[i];
     }
   }
-  return cache;
+  LOG(FATAL) << "No inline cache found for "  << ArtMethod::PrettyMethod(method_) << "@" << dex_pc;
+  UNREACHABLE();
 }
 
 void ProfilingInfo::AddInvokeInfo(uint32_t dex_pc, mirror::Class* cls) {
   InlineCache* cache = GetInlineCache(dex_pc);
-  CHECK(cache != nullptr) << ArtMethod::PrettyMethod(method_) << "@" << dex_pc;
   for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) {
     mirror::Class* existing = cache->classes_[i].Read();
     if (existing == cls) {
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
index 9fbf2e3..1c58a83 100644
--- a/runtime/jit/profiling_info.h
+++ b/runtime/jit/profiling_info.h
@@ -73,7 +73,9 @@
     return method_;
   }
 
-  InlineCache* GetInlineCache(uint32_t dex_pc);
+  // Mutator lock only required for debugging output.
+  InlineCache* GetInlineCache(uint32_t dex_pc)
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool IsMethodBeingCompiled(bool osr) const {
     return osr
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 3c641b0..547b5b8 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -196,7 +196,7 @@
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::Class> c(
       hs.NewHandle(EnsureInitialized(soa.Self(), soa.Decode<mirror::Class>(jni_class))));
-  if (c.Get() == nullptr) {
+  if (c == nullptr) {
     return nullptr;
   }
   ArtField* field = nullptr;
diff --git a/runtime/jobject_comparator.cc b/runtime/jobject_comparator.cc
index 443f095..4c45e38 100644
--- a/runtime/jobject_comparator.cc
+++ b/runtime/jobject_comparator.cc
@@ -34,9 +34,9 @@
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::Object> obj1(hs.NewHandle(soa.Decode<mirror::Object>(jobj1)));
   Handle<mirror::Object> obj2(hs.NewHandle(soa.Decode<mirror::Object>(jobj2)));
-  if (obj1.Get() == nullptr) {
+  if (obj1 == nullptr) {
     return true;
-  } else if (obj2.Get() == nullptr) {
+  } else if (obj2 == nullptr) {
     return false;
   }
   // Sort by class...
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 99886e5..6ecfd8c 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -220,7 +220,7 @@
     StackHandleScope<2> hs(Thread::Current());
     Handle<mirror::Class> h_to(hs.NewHandle(to));
     Handle<mirror::Object> h_obj(hs.NewHandle(src_value.GetL()));
-    if (h_obj.Get() != nullptr && !to->IsAssignableFrom(h_obj->GetClass())) {
+    if (h_obj != nullptr && !to->IsAssignableFrom(h_obj->GetClass())) {
       ThrowClassCastException(h_to.Get(), h_obj->GetClass());
       return false;
     }
@@ -555,7 +555,7 @@
                                    Handle<mirror::MethodType> callee_type,
                                    Thread* self,
                                    ShadowFrame& shadow_frame,
-                                   Handle<mirror::MethodHandleImpl> receiver,
+                                   Handle<mirror::MethodHandle> receiver,
                                    const uint32_t (&args)[Instruction::kMaxVarArgRegs],
                                    uint32_t first_arg,
                                    JValue* result)
@@ -599,7 +599,7 @@
 
     // Something went wrong while creating the emulated stack frame, we should
     // throw the pending exception.
-    if (sf.Get() == nullptr) {
+    if (sf == nullptr) {
       DCHECK(self->IsExceptionPending());
       return false;
     }
@@ -645,7 +645,7 @@
 template <bool is_range>
 bool DoInvokePolymorphicUnchecked(Thread* self,
                                   ShadowFrame& shadow_frame,
-                                  Handle<mirror::MethodHandleImpl> method_handle,
+                                  Handle<mirror::MethodHandle> method_handle,
                                   Handle<mirror::MethodType> callsite_type,
                                   const uint32_t (&args)[Instruction::kMaxVarArgRegs],
                                   uint32_t first_arg,
@@ -780,7 +780,6 @@
 }
 
 // Helper for setters in invoke-polymorphic.
-template <bool do_assignability_check>
 inline bool DoFieldPutForInvokePolymorphic(Thread* self,
                                            ShadowFrame& shadow_frame,
                                            ObjPtr<mirror::Object>& obj,
@@ -788,30 +787,33 @@
                                            Primitive::Type field_type,
                                            const JValue& value)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  static const bool kTransaction = false;
+  DCHECK(!Runtime::Current()->IsActiveTransaction());
+  static const bool kTransaction = false;         // Not in a transaction.
+  static const bool kAssignabilityCheck = false;  // No access check.
   switch (field_type) {
     case Primitive::kPrimBoolean:
-      return DoFieldPutCommon<Primitive::kPrimBoolean, do_assignability_check, kTransaction>(
-          self, shadow_frame, obj, field, value);
+      return
+          DoFieldPutCommon<Primitive::kPrimBoolean, kAssignabilityCheck, kTransaction>(
+              self, shadow_frame, obj, field, value);
     case Primitive::kPrimByte:
-      return DoFieldPutCommon<Primitive::kPrimByte, do_assignability_check, kTransaction>(
+      return DoFieldPutCommon<Primitive::kPrimByte, kAssignabilityCheck, kTransaction>(
           self, shadow_frame, obj, field, value);
     case Primitive::kPrimChar:
-      return DoFieldPutCommon<Primitive::kPrimChar, do_assignability_check, kTransaction>(
+      return DoFieldPutCommon<Primitive::kPrimChar, kAssignabilityCheck, kTransaction>(
           self, shadow_frame, obj, field, value);
     case Primitive::kPrimShort:
-      return DoFieldPutCommon<Primitive::kPrimShort, do_assignability_check, kTransaction>(
+      return DoFieldPutCommon<Primitive::kPrimShort, kAssignabilityCheck, kTransaction>(
           self, shadow_frame, obj, field, value);
     case Primitive::kPrimInt:
     case Primitive::kPrimFloat:
-      return DoFieldPutCommon<Primitive::kPrimInt, do_assignability_check, kTransaction>(
+      return DoFieldPutCommon<Primitive::kPrimInt, kAssignabilityCheck, kTransaction>(
           self, shadow_frame, obj, field, value);
     case Primitive::kPrimLong:
     case Primitive::kPrimDouble:
-      return DoFieldPutCommon<Primitive::kPrimLong, do_assignability_check, kTransaction>(
+      return DoFieldPutCommon<Primitive::kPrimLong, kAssignabilityCheck, kTransaction>(
           self, shadow_frame, obj, field, value);
     case Primitive::kPrimNot:
-      return DoFieldPutCommon<Primitive::kPrimNot, do_assignability_check, kTransaction>(
+      return DoFieldPutCommon<Primitive::kPrimNot, kAssignabilityCheck, kTransaction>(
           self, shadow_frame, obj, field, value);
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable: " << field_type;
@@ -855,10 +857,10 @@
   return field_value;
 }
 
-template <bool is_range, bool do_conversions, bool do_assignability_check>
+template <bool is_range, bool do_conversions>
 bool DoInvokePolymorphicFieldAccess(Thread* self,
                                     ShadowFrame& shadow_frame,
-                                    Handle<mirror::MethodHandleImpl> method_handle,
+                                    Handle<mirror::MethodHandle> method_handle,
                                     Handle<mirror::MethodType> callsite_type,
                                     const uint32_t (&args)[Instruction::kMaxVarArgRegs],
                                     uint32_t first_arg,
@@ -903,12 +905,7 @@
         return false;
       }
       ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg);
-      return DoFieldPutForInvokePolymorphic<do_assignability_check>(self,
-                                                                    shadow_frame,
-                                                                    obj,
-                                                                    field,
-                                                                    field_type,
-                                                                    value);
+      return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value);
     }
     case mirror::MethodHandle::kStaticPut: {
       ObjPtr<mirror::Object> obj = GetAndInitializeDeclaringClass(self, field);
@@ -922,12 +919,7 @@
         DCHECK(self->IsExceptionPending());
         return false;
       }
-      return DoFieldPutForInvokePolymorphic<do_assignability_check>(self,
-                                                                    shadow_frame,
-                                                                    obj,
-                                                                    field,
-                                                                    field_type,
-                                                                    value);
+      return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value);
     }
     default:
       LOG(FATAL) << "Unreachable: " << handle_kind;
@@ -935,10 +927,10 @@
   }
 }
 
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
 static inline bool DoInvokePolymorphicNonExact(Thread* self,
                                                ShadowFrame& shadow_frame,
-                                               Handle<mirror::MethodHandleImpl> method_handle,
+                                               Handle<mirror::MethodHandle> method_handle,
                                                Handle<mirror::MethodType> callsite_type,
                                                const uint32_t (&args)[Instruction::kMaxVarArgRegs],
                                                uint32_t first_arg,
@@ -959,7 +951,7 @@
   if (IsFieldAccess(handle_kind)) {
     if (UNLIKELY(callsite_type->IsExactMatch(handle_type.Ptr()))) {
       const bool do_convert = false;
-      return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>(
+      return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
           self,
           shadow_frame,
           method_handle,
@@ -969,7 +961,7 @@
           result);
     } else {
       const bool do_convert = true;
-      return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>(
+      return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
           self,
           shadow_frame,
           method_handle,
@@ -999,10 +991,10 @@
   }
 }
 
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
 bool DoInvokePolymorphicExact(Thread* self,
                               ShadowFrame& shadow_frame,
-                              Handle<mirror::MethodHandleImpl> method_handle,
+                              Handle<mirror::MethodHandle> method_handle,
                               Handle<mirror::MethodType> callsite_type,
                               const uint32_t (&args)[Instruction::kMaxVarArgRegs],
                               uint32_t first_arg,
@@ -1018,13 +1010,13 @@
       ThrowWrongMethodTypeException(nominal_type.Ptr(), callsite_type.Get());
       return false;
     }
-    return DoInvokePolymorphicNonExact<is_range, do_assignability_check>(self,
-                                                                         shadow_frame,
-                                                                         method_handle,
-                                                                         callsite_type,
-                                                                         args,
-                                                                         first_arg,
-                                                                         result);
+    return DoInvokePolymorphicNonExact<is_range>(self,
+                                                 shadow_frame,
+                                                 method_handle,
+                                                 callsite_type,
+                                                 args,
+                                                 first_arg,
+                                                 result);
   }
 
   ObjPtr<mirror::MethodType> handle_type(method_handle->GetMethodType());
@@ -1036,7 +1028,7 @@
   const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind();
   if (IsFieldAccess(handle_kind)) {
     const bool do_convert = false;
-    return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>(
+    return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
         self,
         shadow_frame,
         method_handle,
@@ -1057,51 +1049,49 @@
 
 }  // namespace
 
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
 bool DoInvokePolymorphic(Thread* self,
                          ArtMethod* invoke_method,
                          ShadowFrame& shadow_frame,
-                         Handle<mirror::MethodHandleImpl> method_handle,
+                         Handle<mirror::MethodHandle> method_handle,
                          Handle<mirror::MethodType> callsite_type,
                          const uint32_t (&args)[Instruction::kMaxVarArgRegs],
                          uint32_t first_arg,
                          JValue* result)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (IsMethodHandleInvokeExact(invoke_method)) {
-    return DoInvokePolymorphicExact<is_range, do_assignability_check>(self,
-                                                                      shadow_frame,
-                                                                      method_handle,
-                                                                      callsite_type,
-                                                                      args,
-                                                                      first_arg,
-                                                                      result);
+    return DoInvokePolymorphicExact<is_range>(self,
+                                              shadow_frame,
+                                              method_handle,
+                                              callsite_type,
+                                              args,
+                                              first_arg,
+                                              result);
   } else {
-    return DoInvokePolymorphicNonExact<is_range, do_assignability_check>(self,
-                                                                         shadow_frame,
-                                                                         method_handle,
-                                                                         callsite_type,
-                                                                         args,
-                                                                         first_arg,
-                                                                         result);
+    return DoInvokePolymorphicNonExact<is_range>(self,
+                                                 shadow_frame,
+                                                 method_handle,
+                                                 callsite_type,
+                                                 args,
+                                                 first_arg,
+                                                 result);
   }
 }
 
-#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range, _do_assignability_check) \
-template REQUIRES_SHARED(Locks::mutator_lock_)                                           \
-bool DoInvokePolymorphic<_is_range, _do_assignability_check>(                            \
-    Thread* self,                                                                        \
-    ArtMethod* invoke_method,                                                            \
-    ShadowFrame& shadow_frame,                                                           \
-    Handle<mirror::MethodHandleImpl> method_handle,                                      \
-    Handle<mirror::MethodType> callsite_type,                                            \
-    const uint32_t (&args)[Instruction::kMaxVarArgRegs],                                 \
-    uint32_t first_arg,                                                                  \
-    JValue* result)
+#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range)  \
+  template REQUIRES_SHARED(Locks::mutator_lock_)                 \
+  bool DoInvokePolymorphic<_is_range>(                           \
+      Thread* self,                                              \
+      ArtMethod* invoke_method,                                  \
+      ShadowFrame& shadow_frame,                                 \
+      Handle<mirror::MethodHandle> method_handle,                \
+      Handle<mirror::MethodType> callsite_type,                  \
+      const uint32_t (&args)[Instruction::kMaxVarArgRegs],       \
+      uint32_t first_arg,                                        \
+      JValue* result)
 
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, true);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, false);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, true);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, false);
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true);
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false);
 #undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL
 
 }  // namespace art
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 734d7c7..5bea0ab 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -27,7 +27,7 @@
 namespace art {
 
 namespace mirror {
-  class MethodHandleImpl;
+  class MethodHandle;
   class MethodType;
 }  // mirror
 
@@ -202,11 +202,11 @@
   size_t arg_index_;
 };
 
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
 bool DoInvokePolymorphic(Thread* self,
                          ArtMethod* invoke_method,
                          ShadowFrame& shadow_frame,
-                         Handle<mirror::MethodHandleImpl> method_handle,
+                         Handle<mirror::MethodHandle> method_handle,
                          Handle<mirror::MethodType> callsite_type,
                          const uint32_t (&args)[Instruction::kMaxVarArgRegs],
                          uint32_t first_arg,
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index cc548b9..f283ec3 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -52,7 +52,7 @@
           Array::Alloc<true>(self, array_class.Get(), array_length,
                              array_class->GetComponentSizeShift(),
                              Runtime::Current()->GetHeap()->GetCurrentAllocator())));
-  if (UNLIKELY(new_array.Get() == nullptr)) {
+  if (UNLIKELY(new_array == nullptr)) {
     CHECK(self->IsExceptionPending());
     return nullptr;
   }
@@ -98,14 +98,14 @@
   StackHandleScope<1> hs(self);
   MutableHandle<mirror::Class> array_class(
       hs.NewHandle(class_linker->FindArrayClass(self, &element_class_ptr)));
-  if (UNLIKELY(array_class.Get() == nullptr)) {
+  if (UNLIKELY(array_class == nullptr)) {
     CHECK(self->IsExceptionPending());
     return nullptr;
   }
   for (int32_t i = 1; i < dimensions->GetLength(); ++i) {
     ObjPtr<mirror::Class> array_class_ptr = array_class.Get();
     array_class.Assign(class_linker->FindArrayClass(self, &array_class_ptr));
-    if (UNLIKELY(array_class.Get() == nullptr)) {
+    if (UNLIKELY(array_class == nullptr)) {
       CHECK(self->IsExceptionPending());
       return nullptr;
     }
diff --git a/runtime/mirror/call_site.cc b/runtime/mirror/call_site.cc
new file mode 100644
index 0000000..eb613df
--- /dev/null
+++ b/runtime/mirror/call_site.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "call_site.h"
+
+#include "class-inl.h"
+#include "gc_root-inl.h"
+
+namespace art {
+namespace mirror {
+
+GcRoot<mirror::Class> CallSite::static_class_;
+
+mirror::CallSite* CallSite::Create(Thread* const self, Handle<MethodHandle> target) {
+  StackHandleScope<1> hs(self);
+  Handle<mirror::CallSite> cs(
+      hs.NewHandle(ObjPtr<CallSite>::DownCast(StaticClass()->AllocObject(self))));
+  CHECK(!Runtime::Current()->IsActiveTransaction());
+  cs->SetFieldObject<false>(TargetOffset(), target.Get());
+  return cs.Get();
+}
+
+void CallSite::SetClass(Class* klass) {
+  CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+  CHECK(klass != nullptr);
+  static_class_ = GcRoot<Class>(klass);
+}
+
+void CallSite::ResetClass() {
+  CHECK(!static_class_.IsNull());
+  static_class_ = GcRoot<Class>(nullptr);
+}
+
+void CallSite::VisitRoots(RootVisitor* visitor) {
+  static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+}  // namespace mirror
+}  // namespace art
diff --git a/runtime/mirror/call_site.h b/runtime/mirror/call_site.h
new file mode 100644
index 0000000..db244a5
--- /dev/null
+++ b/runtime/mirror/call_site.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_MIRROR_CALL_SITE_H_
+#define ART_RUNTIME_MIRROR_CALL_SITE_H_
+
+#include "mirror/method_handle_impl.h"
+#include "utils.h"
+
+namespace art {
+
+struct CallSiteOffsets;
+
+namespace mirror {
+
+// C++ mirror of java.lang.invoke.CallSite
+class MANAGED CallSite : public Object {
+ public:
+  static mirror::CallSite* Create(Thread* const self,
+                                  Handle<MethodHandle> method_handle)
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+  static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return static_class_.Read();
+  }
+
+  MethodHandle* GetTarget() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetFieldObject<MethodHandle>(TargetOffset());
+  }
+
+  static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+  static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+  static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+  static inline MemberOffset TargetOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(CallSite, target_));
+  }
+
+  HeapReference<mirror::MethodHandle> target_;
+
+  static GcRoot<mirror::Class> static_class_;  // java.lang.invoke.CallSite.class
+
+  friend struct art::CallSiteOffsets;  // for verifying offset information
+  DISALLOW_IMPLICIT_CONSTRUCTORS(CallSite);
+};
+
+}  // namespace mirror
+}  // namespace art
+
+#endif  // ART_RUNTIME_MIRROR_CALL_SITE_H_
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index f7ff735..9a9a5d8 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -81,7 +81,7 @@
   self->ClearException();
   // Allocate the ClassExt
   Handle<ClassExt> new_ext(hs.NewHandle(ClassExt::Alloc(self)));
-  if (new_ext.Get() == nullptr) {
+  if (new_ext == nullptr) {
     // OOM allocating the classExt.
     // TODO Should we restore the suppressed exception?
     self->AssertPendingOOMException();
@@ -103,7 +103,7 @@
     DCHECK(!set || h_this->GetExtData() == new_ext.Get());
     CHECK(!ret.IsNull());
     // Restore the exception if there was one.
-    if (throwable.Get() != nullptr) {
+    if (throwable != nullptr) {
       self->SetException(throwable.Get());
     }
     return ret.Ptr();
@@ -269,10 +269,10 @@
   os << "----- " << (IsInterface() ? "interface" : "class") << " "
      << "'" << GetDescriptor(&temp) << "' cl=" << GetClassLoader() << " -----\n",
   os << "  objectSize=" << SizeOf() << " "
-     << "(" << (h_super.Get() != nullptr ? h_super->SizeOf() : -1) << " from super)\n",
+     << "(" << (h_super != nullptr ? h_super->SizeOf() : -1) << " from super)\n",
   os << StringPrintf("  access=0x%04x.%04x\n",
       GetAccessFlags() >> 16, GetAccessFlags() & kAccJavaFlagsMask);
-  if (h_super.Get() != nullptr) {
+  if (h_super != nullptr) {
     os << "  super='" << h_super->PrettyClass() << "' (cl=" << h_super->GetClassLoader()
        << ")\n";
   }
@@ -297,7 +297,7 @@
   } else {
     // After this point, this may have moved due to GetDirectInterface.
     os << "  vtable (" << h_this->NumVirtualMethods() << " entries, "
-        << (h_super.Get() != nullptr ? h_super->NumVirtualMethods() : 0) << " in super):\n";
+        << (h_super != nullptr ? h_super->NumVirtualMethods() : 0) << " in super):\n";
     for (size_t i = 0; i < NumVirtualMethods(); ++i) {
       os << StringPrintf("    %2zd: %s\n", i, ArtMethod::PrettyMethod(
           h_this->GetVirtualMethodDuringLinking(i, image_pointer_size)).c_str());
@@ -951,8 +951,7 @@
     return interfaces->Get(idx);
   } else {
     dex::TypeIndex type_idx = klass->GetDirectInterfaceTypeIdx(idx);
-    ObjPtr<Class> interface = ClassLinker::LookupResolvedType(
-        type_idx, klass->GetDexCache(), klass->GetClassLoader());
+    ObjPtr<Class> interface = klass->GetDexCache()->GetResolvedType(type_idx);
     return interface;
   }
 }
@@ -972,7 +971,7 @@
 }
 
 ObjPtr<Class> Class::GetCommonSuperClass(Handle<Class> klass) {
-  DCHECK(klass.Get() != nullptr);
+  DCHECK(klass != nullptr);
   DCHECK(!klass->IsInterface());
   DCHECK(!IsInterface());
   ObjPtr<Class> common_super_class = this;
@@ -1166,7 +1165,7 @@
   constexpr uint32_t kSkipModifiers = kAccMiranda | kAccSynthetic;
   StackHandleScope<3> hs(self);
   auto h_method_name = hs.NewHandle(name);
-  if (UNLIKELY(h_method_name.Get() == nullptr)) {
+  if (UNLIKELY(h_method_name == nullptr)) {
     ThrowNullPointerException("name == null");
     return nullptr;
   }
diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc
index efd949e..7270079 100644
--- a/runtime/mirror/class_ext.cc
+++ b/runtime/mirror/class_ext.cc
@@ -58,8 +58,8 @@
   Handle<ObjectArray<DexCache>> old_dex_caches(hs.NewHandle(h_this->GetObsoleteDexCaches()));
   ClassLinker* cl = Runtime::Current()->GetClassLinker();
   size_t new_len;
-  if (old_methods.Get() == nullptr) {
-    CHECK(old_dex_caches.Get() == nullptr);
+  if (old_methods == nullptr) {
+    CHECK(old_dex_caches == nullptr);
     new_len = increase;
   } else {
     CHECK_EQ(old_methods->GetLength(), old_dex_caches->GetLength());
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index bef3ad2..973c8ed 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -26,6 +26,7 @@
 #include "base/logging.h"
 #include "gc_root.h"
 #include "mirror/class.h"
+#include "mirror/call_site.h"
 #include "mirror/method_type.h"
 #include "runtime.h"
 #include "obj_ptr.h"
@@ -40,22 +41,14 @@
   return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0, pointer_size);
 }
 
-inline uint32_t DexCache::StringSlotIndex(dex::StringIndex string_idx) {
+inline mirror::String* DexCache::GetResolvedString(dex::StringIndex string_idx) {
   DCHECK_LT(string_idx.index_, GetDexFile()->NumStringIds());
-  const uint32_t slot_idx = string_idx.index_ % kDexCacheStringCacheSize;
-  DCHECK_LT(slot_idx, NumStrings());
-  return slot_idx;
+  return StringDexCachePair::Lookup(GetStrings(), string_idx.index_, NumStrings()).Read();
 }
 
-inline String* DexCache::GetResolvedString(dex::StringIndex string_idx) {
-  return GetStrings()[StringSlotIndex(string_idx)].load(
-      std::memory_order_relaxed).GetObjectForIndex(string_idx.index_);
-}
-
-inline void DexCache::SetResolvedString(dex::StringIndex string_idx, ObjPtr<String> resolved) {
-  DCHECK(resolved != nullptr);
-  GetStrings()[StringSlotIndex(string_idx)].store(
-      StringDexCachePair(resolved, string_idx.index_), std::memory_order_relaxed);
+inline void DexCache::SetResolvedString(dex::StringIndex string_idx,
+                                        ObjPtr<mirror::String> resolved) {
+  StringDexCachePair::Assign(GetStrings(), string_idx.index_, resolved.Ptr(), NumStrings());
   Runtime* const runtime = Runtime::Current();
   if (UNLIKELY(runtime->IsActiveTransaction())) {
     DCHECK(runtime->IsAotCompiler());
@@ -66,74 +59,83 @@
 }
 
 inline void DexCache::ClearString(dex::StringIndex string_idx) {
+  const uint32_t slot_idx = string_idx.index_ % NumStrings();
   DCHECK(Runtime::Current()->IsAotCompiler());
-  uint32_t slot_idx = StringSlotIndex(string_idx);
   StringDexCacheType* slot = &GetStrings()[slot_idx];
   // This is racy but should only be called from the transactional interpreter.
   if (slot->load(std::memory_order_relaxed).index == string_idx.index_) {
-    StringDexCachePair cleared(nullptr, StringDexCachePair::InvalidIndexForSlot(slot_idx));
+    StringDexCachePair cleared(
+        nullptr,
+        StringDexCachePair::InvalidIndexForSlot(slot_idx));
     slot->store(cleared, std::memory_order_relaxed);
   }
 }
 
-inline uint32_t DexCache::TypeSlotIndex(dex::TypeIndex type_idx) {
-  DCHECK_LT(type_idx.index_, GetDexFile()->NumTypeIds());
-  const uint32_t slot_idx = type_idx.index_ % kDexCacheTypeCacheSize;
-  DCHECK_LT(slot_idx, NumResolvedTypes());
-  return slot_idx;
-}
-
 inline Class* DexCache::GetResolvedType(dex::TypeIndex type_idx) {
   // It is theorized that a load acquire is not required since obtaining the resolved class will
   // always have an address dependency or a lock.
-  return GetResolvedTypes()[TypeSlotIndex(type_idx)].load(
-      std::memory_order_relaxed).GetObjectForIndex(type_idx.index_);
+  DCHECK_LT(type_idx.index_, NumResolvedTypes());
+  return GetResolvedTypes()[type_idx.index_].Read();
 }
 
 inline void DexCache::SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved) {
-  DCHECK(resolved != nullptr);
+  DCHECK_LT(type_idx.index_, NumResolvedTypes());  // NOTE: Unchecked, i.e. not throwing AIOOB.
   // TODO default transaction support.
   // Use a release store for SetResolvedType. This is done to prevent other threads from seeing a
   // class but not necessarily seeing the loaded members like the static fields array.
   // See b/32075261.
-  GetResolvedTypes()[TypeSlotIndex(type_idx)].store(
-      TypeDexCachePair(resolved, type_idx.index_), std::memory_order_release);
+  reinterpret_cast<Atomic<GcRoot<mirror::Class>>&>(GetResolvedTypes()[type_idx.index_]).
+      StoreRelease(GcRoot<Class>(resolved));
   // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
   Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
 }
 
-inline void DexCache::ClearResolvedType(dex::TypeIndex type_idx) {
-  DCHECK(Runtime::Current()->IsAotCompiler());
-  uint32_t slot_idx = TypeSlotIndex(type_idx);
-  TypeDexCacheType* slot = &GetResolvedTypes()[slot_idx];
-  // This is racy but should only be called from the single-threaded ImageWriter and tests.
-  if (slot->load(std::memory_order_relaxed).index == type_idx.index_) {
-    TypeDexCachePair cleared(nullptr, TypeDexCachePair::InvalidIndexForSlot(slot_idx));
-    slot->store(cleared, std::memory_order_relaxed);
-  }
-}
-
-inline uint32_t DexCache::MethodTypeSlotIndex(uint32_t proto_idx) {
-  DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
-  DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds());
-  const uint32_t slot_idx = proto_idx % kDexCacheMethodTypeCacheSize;
-  DCHECK_LT(slot_idx, NumResolvedMethodTypes());
-  return slot_idx;
-}
-
 inline MethodType* DexCache::GetResolvedMethodType(uint32_t proto_idx) {
-  return GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].load(
-      std::memory_order_relaxed).GetObjectForIndex(proto_idx);
+  DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+  DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds());
+  return MethodTypeDexCachePair::Lookup(
+      GetResolvedMethodTypes(), proto_idx, NumResolvedMethodTypes()).Read();
 }
 
 inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) {
-  DCHECK(resolved != nullptr);
-  GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].store(
-      MethodTypeDexCachePair(resolved, proto_idx), std::memory_order_relaxed);
+  DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+  DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds());
+
+  MethodTypeDexCachePair::Assign(GetResolvedMethodTypes(), proto_idx, resolved,
+                                 NumResolvedMethodTypes());
   // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
   Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
 }
 
+inline CallSite* DexCache::GetResolvedCallSite(uint32_t call_site_idx) {
+  DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+  DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds());
+  GcRoot<mirror::CallSite>& target = GetResolvedCallSites()[call_site_idx];
+  Atomic<GcRoot<mirror::CallSite>>& ref =
+      reinterpret_cast<Atomic<GcRoot<mirror::CallSite>>&>(target);
+  return ref.LoadSequentiallyConsistent().Read();
+}
+
+inline CallSite* DexCache::SetResolvedCallSite(uint32_t call_site_idx, CallSite* call_site) {
+  DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+  DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds());
+
+  GcRoot<mirror::CallSite> null_call_site(nullptr);
+  GcRoot<mirror::CallSite> candidate(call_site);
+  GcRoot<mirror::CallSite>& target = GetResolvedCallSites()[call_site_idx];
+
+  // The first assignment for a given call site wins.
+  Atomic<GcRoot<mirror::CallSite>>& ref =
+      reinterpret_cast<Atomic<GcRoot<mirror::CallSite>>&>(target);
+  if (ref.CompareExchangeStrongSequentiallyConsistent(null_call_site, candidate)) {
+    // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
+    Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
+    return call_site;
+  } else {
+    return target.Read();
+  }
+}
+
 inline ArtField* DexCache::GetResolvedField(uint32_t field_idx, PointerSize ptr_size) {
   DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
   DCHECK_LT(field_idx, NumResolvedFields());  // NOTE: Unchecked, i.e. not throwing AIOOB.
@@ -226,51 +228,67 @@
   VisitInstanceFieldsReferences<kVerifyFlags, kReadBarrierOption>(klass, visitor);
   // Visit arrays after.
   if (kVisitNativeRoots) {
-    VisitDexCachePairs<String, kReadBarrierOption, Visitor>(
+    VisitDexCachePairs<mirror::String, kReadBarrierOption, Visitor>(
         GetStrings(), NumStrings(), visitor);
 
-    VisitDexCachePairs<Class, kReadBarrierOption, Visitor>(
-        GetResolvedTypes(), NumResolvedTypes(), visitor);
+    GcRoot<mirror::Class>* resolved_types = GetResolvedTypes();
+    for (size_t i = 0, num_types = NumResolvedTypes(); i != num_types; ++i) {
+      visitor.VisitRootIfNonNull(resolved_types[i].AddressWithoutBarrier());
+    }
 
-    VisitDexCachePairs<MethodType, kReadBarrierOption, Visitor>(
+    VisitDexCachePairs<mirror::MethodType, kReadBarrierOption, Visitor>(
         GetResolvedMethodTypes(), NumResolvedMethodTypes(), visitor);
+
+    GcRoot<mirror::CallSite>* resolved_call_sites = GetResolvedCallSites();
+    for (size_t i = 0, num_call_sites = NumResolvedCallSites(); i != num_call_sites; ++i) {
+      visitor.VisitRootIfNonNull(resolved_call_sites[i].AddressWithoutBarrier());
+    }
   }
 }
 
 template <ReadBarrierOption kReadBarrierOption, typename Visitor>
-inline void DexCache::FixupStrings(StringDexCacheType* dest, const Visitor& visitor) {
-  StringDexCacheType* src = GetStrings();
+inline void DexCache::FixupStrings(mirror::StringDexCacheType* dest, const Visitor& visitor) {
+  mirror::StringDexCacheType* src = GetStrings();
   for (size_t i = 0, count = NumStrings(); i < count; ++i) {
     StringDexCachePair source = src[i].load(std::memory_order_relaxed);
-    String* ptr = source.object.Read<kReadBarrierOption>();
-    String* new_source = visitor(ptr);
+    mirror::String* ptr = source.object.Read<kReadBarrierOption>();
+    mirror::String* new_source = visitor(ptr);
     source.object = GcRoot<String>(new_source);
     dest[i].store(source, std::memory_order_relaxed);
   }
 }
 
 template <ReadBarrierOption kReadBarrierOption, typename Visitor>
-inline void DexCache::FixupResolvedTypes(TypeDexCacheType* dest, const Visitor& visitor) {
-  TypeDexCacheType* src = GetResolvedTypes();
+inline void DexCache::FixupResolvedTypes(GcRoot<mirror::Class>* dest, const Visitor& visitor) {
+  GcRoot<mirror::Class>* src = GetResolvedTypes();
   for (size_t i = 0, count = NumResolvedTypes(); i < count; ++i) {
-    TypeDexCachePair source = src[i].load(std::memory_order_relaxed);
-    Class* ptr = source.object.Read<kReadBarrierOption>();
-    Class* new_source = visitor(ptr);
-    source.object = GcRoot<Class>(new_source);
+    mirror::Class* source = src[i].Read<kReadBarrierOption>();
+    mirror::Class* new_source = visitor(source);
+    dest[i] = GcRoot<mirror::Class>(new_source);
+  }
+}
+
+template <ReadBarrierOption kReadBarrierOption, typename Visitor>
+inline void DexCache::FixupResolvedMethodTypes(mirror::MethodTypeDexCacheType* dest,
+                                               const Visitor& visitor) {
+  mirror::MethodTypeDexCacheType* src = GetResolvedMethodTypes();
+  for (size_t i = 0, count = NumResolvedMethodTypes(); i < count; ++i) {
+    MethodTypeDexCachePair source = src[i].load(std::memory_order_relaxed);
+    mirror::MethodType* ptr = source.object.Read<kReadBarrierOption>();
+    mirror::MethodType* new_source = visitor(ptr);
+    source.object = GcRoot<MethodType>(new_source);
     dest[i].store(source, std::memory_order_relaxed);
   }
 }
 
 template <ReadBarrierOption kReadBarrierOption, typename Visitor>
-inline void DexCache::FixupResolvedMethodTypes(MethodTypeDexCacheType* dest,
-                                               const Visitor& visitor) {
-  MethodTypeDexCacheType* src = GetResolvedMethodTypes();
-  for (size_t i = 0, count = NumResolvedMethodTypes(); i < count; ++i) {
-    MethodTypeDexCachePair source = src[i].load(std::memory_order_relaxed);
-    MethodType* ptr = source.object.Read<kReadBarrierOption>();
-    MethodType* new_source = visitor(ptr);
-    source.object = GcRoot<MethodType>(new_source);
-    dest[i].store(source, std::memory_order_relaxed);
+inline void DexCache::FixupResolvedCallSites(GcRoot<mirror::CallSite>* dest,
+                                             const Visitor& visitor) {
+  GcRoot<mirror::CallSite>* src = GetResolvedCallSites();
+  for (size_t i = 0, count = NumResolvedCallSites(); i < count; ++i) {
+    mirror::CallSite* source = src[i].Read<kReadBarrierOption>();
+    mirror::CallSite* new_source = visitor(source);
+    dest[i] = GcRoot<mirror::CallSite>(new_source);
   }
 }
 
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index 3103a92..0f6acab 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -58,8 +58,8 @@
 
   mirror::StringDexCacheType* strings = (dex_file->NumStringIds() == 0u) ? nullptr :
       reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset());
-  mirror::TypeDexCacheType* types = (dex_file->NumTypeIds() == 0u) ? nullptr :
-      reinterpret_cast<mirror::TypeDexCacheType*>(raw_arrays + layout.TypesOffset());
+  GcRoot<mirror::Class>* types = (dex_file->NumTypeIds() == 0u) ? nullptr :
+      reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset());
   ArtMethod** methods = (dex_file->NumMethodIds() == 0u) ? nullptr :
       reinterpret_cast<ArtMethod**>(raw_arrays + layout.MethodsOffset());
   ArtField** fields = (dex_file->NumFieldIds() == 0u) ? nullptr :
@@ -69,10 +69,6 @@
   if (dex_file->NumStringIds() < num_strings) {
     num_strings = dex_file->NumStringIds();
   }
-  size_t num_types = mirror::DexCache::kDexCacheTypeCacheSize;
-  if (dex_file->NumTypeIds() < num_types) {
-    num_types = dex_file->NumTypeIds();
-  }
 
   // Note that we allocate the method type dex caches regardless of this flag,
   // and we make sure here that they're not used by the runtime. This is in the
@@ -94,6 +90,10 @@
         raw_arrays + layout.MethodTypesOffset());
   }
 
+  GcRoot<mirror::CallSite>* call_sites = (dex_file->NumCallSiteIds() == 0)
+      ? nullptr
+      : reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset());
+
   DCHECK_ALIGNED(raw_arrays, alignof(mirror::StringDexCacheType)) <<
                  "Expected raw_arrays to align to StringDexCacheType.";
   DCHECK_ALIGNED(layout.StringsOffset(), alignof(mirror::StringDexCacheType)) <<
@@ -108,9 +108,8 @@
       CHECK_EQ(strings[i].load(std::memory_order_relaxed).index, 0u);
       CHECK(strings[i].load(std::memory_order_relaxed).object.IsNull());
     }
-    for (size_t i = 0; i < num_types; ++i) {
-      CHECK_EQ(types[i].load(std::memory_order_relaxed).index, 0u);
-      CHECK(types[i].load(std::memory_order_relaxed).object.IsNull());
+    for (size_t i = 0; i < dex_file->NumTypeIds(); ++i) {
+      CHECK(types[i].IsNull());
     }
     for (size_t i = 0; i < dex_file->NumMethodIds(); ++i) {
       CHECK(mirror::DexCache::GetElementPtrSize(methods, i, image_pointer_size) == nullptr);
@@ -122,13 +121,13 @@
       CHECK_EQ(method_types[i].load(std::memory_order_relaxed).index, 0u);
       CHECK(method_types[i].load(std::memory_order_relaxed).object.IsNull());
     }
+    for (size_t i = 0; i < dex_file->NumCallSiteIds(); ++i) {
+      CHECK(call_sites[i].IsNull());
+    }
   }
   if (strings != nullptr) {
     mirror::StringDexCachePair::Initialize(strings);
   }
-  if (types != nullptr) {
-    mirror::TypeDexCachePair::Initialize(types);
-  }
   if (method_types != nullptr) {
     mirror::MethodTypeDexCachePair::Initialize(method_types);
   }
@@ -137,13 +136,15 @@
                   strings,
                   num_strings,
                   types,
-                  num_types,
+                  dex_file->NumTypeIds(),
                   methods,
                   dex_file->NumMethodIds(),
                   fields,
                   dex_file->NumFieldIds(),
                   method_types,
                   num_method_types,
+                  call_sites,
+                  dex_file->NumCallSiteIds(),
                   image_pointer_size);
 }
 
@@ -151,7 +152,7 @@
                     ObjPtr<String> location,
                     StringDexCacheType* strings,
                     uint32_t num_strings,
-                    TypeDexCacheType* resolved_types,
+                    GcRoot<Class>* resolved_types,
                     uint32_t num_resolved_types,
                     ArtMethod** resolved_methods,
                     uint32_t num_resolved_methods,
@@ -159,6 +160,8 @@
                     uint32_t num_resolved_fields,
                     MethodTypeDexCacheType* resolved_method_types,
                     uint32_t num_resolved_method_types,
+                    GcRoot<CallSite>* resolved_call_sites,
+                    uint32_t num_resolved_call_sites,
                     PointerSize pointer_size) {
   CHECK(dex_file != nullptr);
   CHECK(location != nullptr);
@@ -167,6 +170,7 @@
   CHECK_EQ(num_resolved_methods != 0u, resolved_methods != nullptr);
   CHECK_EQ(num_resolved_fields != 0u, resolved_fields != nullptr);
   CHECK_EQ(num_resolved_method_types != 0u, resolved_method_types != nullptr);
+  CHECK_EQ(num_resolved_call_sites != 0u, resolved_call_sites != nullptr);
 
   SetDexFile(dex_file);
   SetLocation(location);
@@ -175,11 +179,13 @@
   SetResolvedMethods(resolved_methods);
   SetResolvedFields(resolved_fields);
   SetResolvedMethodTypes(resolved_method_types);
+  SetResolvedCallSites(resolved_call_sites);
   SetField32<false>(NumStringsOffset(), num_strings);
   SetField32<false>(NumResolvedTypesOffset(), num_resolved_types);
   SetField32<false>(NumResolvedMethodsOffset(), num_resolved_methods);
   SetField32<false>(NumResolvedFieldsOffset(), num_resolved_fields);
   SetField32<false>(NumResolvedMethodTypesOffset(), num_resolved_method_types);
+  SetField32<false>(NumResolvedCallSitesOffset(), num_resolved_call_sites);
 
   Runtime* const runtime = Runtime::Current();
   if (runtime->HasResolutionMethod()) {
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index e68b0c7..10bb5aa 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -18,14 +18,14 @@
 #define ART_RUNTIME_MIRROR_DEX_CACHE_H_
 
 #include "array.h"
-#include "base/bit_utils.h"
+#include "art_field.h"
+#include "class.h"
 #include "dex_file_types.h"
 #include "object.h"
 #include "object_array.h"
 
 namespace art {
 
-class ArtField;
 class ArtMethod;
 struct DexCacheOffsets;
 class DexFile;
@@ -36,7 +36,7 @@
 
 namespace mirror {
 
-class Class;
+class CallSite;
 class MethodType;
 class String;
 
@@ -61,7 +61,7 @@
   // it's always non-null if the id branch succeeds (except for the 0th id).
   // Set the initial state for the 0th entry to be {0,1} which is guaranteed to fail
   // the lookup id == stored id branch.
-  DexCachePair(ObjPtr<T> object, uint32_t index)
+  DexCachePair(T* object, uint32_t index)
       : object(object),
         index(index) {}
   DexCachePair() = default;
@@ -75,28 +75,39 @@
     dex_cache[0].store(first_elem, std::memory_order_relaxed);
   }
 
+  static GcRoot<T> Lookup(std::atomic<DexCachePair<T>>* dex_cache,
+                          uint32_t idx,
+                          uint32_t cache_size) {
+    DCHECK_NE(cache_size, 0u);
+    DexCachePair<T> element = dex_cache[idx % cache_size].load(std::memory_order_relaxed);
+    if (idx != element.index) {
+      return GcRoot<T>(nullptr);
+    }
+
+    DCHECK(!element.object.IsNull());
+    return element.object;
+  }
+
+  static void Assign(std::atomic<DexCachePair<T>>* dex_cache,
+                     uint32_t idx,
+                     T* object,
+                     uint32_t cache_size) {
+    DCHECK_LT(idx % cache_size, cache_size);
+    dex_cache[idx % cache_size].store(
+        DexCachePair<T>(object, idx), std::memory_order_relaxed);
+  }
+
   static uint32_t InvalidIndexForSlot(uint32_t slot) {
     // Since the cache size is a power of two, 0 will always map to slot 0.
     // Use 1 for slot 0 and 0 for all other slots.
     return (slot == 0) ? 1u : 0u;
   }
-
-  T* GetObjectForIndex(uint32_t idx) REQUIRES_SHARED(Locks::mutator_lock_) {
-    if (idx != index) {
-      return nullptr;
-    }
-    DCHECK(!object.IsNull());
-    return object.Read();
-  }
 };
 
-using TypeDexCachePair = DexCachePair<Class>;
-using TypeDexCacheType = std::atomic<TypeDexCachePair>;
-
-using StringDexCachePair = DexCachePair<String>;
+using StringDexCachePair = DexCachePair<mirror::String>;
 using StringDexCacheType = std::atomic<StringDexCachePair>;
 
-using MethodTypeDexCachePair = DexCachePair<MethodType>;
+using MethodTypeDexCachePair = DexCachePair<mirror::MethodType>;
 using MethodTypeDexCacheType = std::atomic<MethodTypeDexCachePair>;
 
 // C++ mirror of java.lang.DexCache.
@@ -105,11 +116,6 @@
   // Size of java.lang.DexCache.class.
   static uint32_t ClassSize(PointerSize pointer_size);
 
-  // Size of type dex cache. Needs to be a power of 2 for entrypoint assumptions to hold.
-  static constexpr size_t kDexCacheTypeCacheSize = 1024;
-  static_assert(IsPowerOfTwo(kDexCacheTypeCacheSize),
-                "Type dex cache size is not a power of 2.");
-
   // Size of string dex cache. Needs to be a power of 2 for entrypoint assumptions to hold.
   static constexpr size_t kDexCacheStringCacheSize = 1024;
   static_assert(IsPowerOfTwo(kDexCacheStringCacheSize),
@@ -121,10 +127,6 @@
   static_assert(IsPowerOfTwo(kDexCacheMethodTypeCacheSize),
                 "MethodType dex cache size is not a power of 2.");
 
-  static constexpr size_t StaticTypeSize() {
-    return kDexCacheTypeCacheSize;
-  }
-
   static constexpr size_t StaticStringSize() {
     return kDexCacheStringCacheSize;
   }
@@ -155,13 +157,17 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
-  void FixupResolvedTypes(TypeDexCacheType* dest, const Visitor& visitor)
+  void FixupResolvedTypes(GcRoot<mirror::Class>* dest, const Visitor& visitor)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
   void FixupResolvedMethodTypes(MethodTypeDexCacheType* dest, const Visitor& visitor)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
+  void FixupResolvedCallSites(GcRoot<mirror::CallSite>* dest, const Visitor& visitor)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   String* GetLocation() REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_));
   }
@@ -190,6 +196,10 @@
     return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_method_types_);
   }
 
+  static MemberOffset ResolvedCallSitesOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_call_sites_);
+  }
+
   static MemberOffset NumStringsOffset() {
     return OFFSET_OF_OBJECT_MEMBER(DexCache, num_strings_);
   }
@@ -210,7 +220,11 @@
     return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_method_types_);
   }
 
-  String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE
+  static MemberOffset NumResolvedCallSitesOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_call_sites_);
+  }
+
+  mirror::String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   void SetResolvedString(dex::StringIndex string_idx, ObjPtr<mirror::String> resolved) ALWAYS_INLINE
@@ -225,8 +239,6 @@
   void SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  void ClearResolvedType(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
-
   ALWAYS_INLINE ArtMethod* GetResolvedMethod(uint32_t method_idx, PointerSize ptr_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -245,7 +257,18 @@
 
   MethodType* GetResolvedMethodType(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) REQUIRES_SHARED(Locks::mutator_lock_);
+  void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  CallSite* GetResolvedCallSite(uint32_t call_site_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+
+  // Attempts to bind |call_site_idx| to the call site |resolved|. The
+  // caller must use the return value in place of |resolved|. This is
+  // because multiple threads can invoke the bootstrap method each
+  // producing a call site, but the method handle invocation on the
+  // call site must be on a common agreed value.
+  CallSite* SetResolvedCallSite(uint32_t call_site_idx, CallSite* resolved) WARN_UNUSED
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetFieldPtr64<StringDexCacheType*>(StringsOffset());
@@ -255,11 +278,11 @@
     SetFieldPtr<false>(StringsOffset(), strings);
   }
 
-  TypeDexCacheType* GetResolvedTypes() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetFieldPtr<TypeDexCacheType*>(ResolvedTypesOffset());
+  GcRoot<Class>* GetResolvedTypes() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetFieldPtr<GcRoot<Class>*>(ResolvedTypesOffset());
   }
 
-  void SetResolvedTypes(TypeDexCacheType* resolved_types)
+  void SetResolvedTypes(GcRoot<Class>* resolved_types)
       ALWAYS_INLINE
       REQUIRES_SHARED(Locks::mutator_lock_) {
     SetFieldPtr<false>(ResolvedTypesOffset(), resolved_types);
@@ -296,6 +319,18 @@
     SetFieldPtr<false>(ResolvedMethodTypesOffset(), resolved_method_types);
   }
 
+  GcRoot<CallSite>* GetResolvedCallSites()
+      ALWAYS_INLINE
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetFieldPtr<GcRoot<CallSite>*>(ResolvedCallSitesOffset());
+  }
+
+  void SetResolvedCallSites(GcRoot<CallSite>* resolved_call_sites)
+      ALWAYS_INLINE
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    SetFieldPtr<false>(ResolvedCallSitesOffset(), resolved_call_sites);
+  }
+
   size_t NumStrings() REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetField32(NumStringsOffset());
   }
@@ -316,6 +351,10 @@
     return GetField32(NumResolvedMethodTypesOffset());
   }
 
+  size_t NumResolvedCallSites() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetField32(NumResolvedCallSitesOffset());
+  }
+
   const DexFile* GetDexFile() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetFieldPtr<const DexFile*>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_));
   }
@@ -324,7 +363,7 @@
     SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file);
   }
 
-  void SetLocation(ObjPtr<String> location) REQUIRES_SHARED(Locks::mutator_lock_);
+  void SetLocation(ObjPtr<mirror::String> location) REQUIRES_SHARED(Locks::mutator_lock_);
 
   // NOTE: Get/SetElementPtrSize() are intended for working with ArtMethod** and ArtField**
   // provided by GetResolvedMethods/Fields() and ArtMethod::GetDexCacheResolvedMethods(),
@@ -341,40 +380,41 @@
             ObjPtr<String> location,
             StringDexCacheType* strings,
             uint32_t num_strings,
-            TypeDexCacheType* resolved_types,
+            GcRoot<Class>* resolved_types,
             uint32_t num_resolved_types,
             ArtMethod** resolved_methods,
             uint32_t num_resolved_methods,
             ArtField** resolved_fields,
             uint32_t num_resolved_fields,
-            MethodTypeDexCacheType* resolved_methodtypes,
-            uint32_t num_resolved_methodtypes,
+            MethodTypeDexCacheType* resolved_method_types,
+            uint32_t num_resolved_method_types,
+            GcRoot<CallSite>* resolved_call_sites,
+            uint32_t num_resolved_call_sites,
             PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  uint32_t StringSlotIndex(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
-  uint32_t TypeSlotIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
-  uint32_t MethodTypeSlotIndex(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Visit instance fields of the dex cache as well as its associated arrays.
   template <bool kVisitNativeRoots,
             VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
             ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
             typename Visitor>
-  void VisitReferences(ObjPtr<Class> klass, const Visitor& visitor)
+  void VisitReferences(ObjPtr<mirror::Class> klass, const Visitor& visitor)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
 
   HeapReference<Object> dex_;
   HeapReference<String> location_;
   uint64_t dex_file_;               // const DexFile*
+  uint64_t resolved_call_sites_;    // GcRoot<CallSite>* array with num_resolved_call_sites_
+                                    // elements.
   uint64_t resolved_fields_;        // ArtField*, array with num_resolved_fields_ elements.
   uint64_t resolved_method_types_;  // std::atomic<MethodTypeDexCachePair>* array with
                                     // num_resolved_method_types_ elements.
   uint64_t resolved_methods_;       // ArtMethod*, array with num_resolved_methods_ elements.
-  uint64_t resolved_types_;         // TypeDexCacheType*, array with num_resolved_types_ elements.
+  uint64_t resolved_types_;         // GcRoot<Class>*, array with num_resolved_types_ elements.
   uint64_t strings_;                // std::atomic<StringDexCachePair>*, array with num_strings_
                                     // elements.
 
+  uint32_t num_resolved_call_sites_;    // Number of elements in the call_sites_ array.
   uint32_t num_resolved_fields_;        // Number of elements in the resolved_fields_ array.
   uint32_t num_resolved_method_types_;  // Number of elements in the resolved_method_types_ array.
   uint32_t num_resolved_methods_;       // Number of elements in the resolved_methods_ array.
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index 5693f67..5a2ab71 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -47,12 +47,11 @@
           soa.Self(),
           *java_lang_dex_file_,
           Runtime::Current()->GetLinearAlloc())));
-  ASSERT_TRUE(dex_cache.Get() != nullptr);
+  ASSERT_TRUE(dex_cache != nullptr);
 
   EXPECT_TRUE(dex_cache->StaticStringSize() == dex_cache->NumStrings()
       || java_lang_dex_file_->NumStringIds() == dex_cache->NumStrings());
-  EXPECT_TRUE(dex_cache->StaticTypeSize() == dex_cache->NumResolvedTypes()
-      || java_lang_dex_file_->NumTypeIds() == dex_cache->NumResolvedTypes());
+  EXPECT_EQ(java_lang_dex_file_->NumTypeIds(),   dex_cache->NumResolvedTypes());
   EXPECT_EQ(java_lang_dex_file_->NumMethodIds(), dex_cache->NumResolvedMethods());
   EXPECT_EQ(java_lang_dex_file_->NumFieldIds(),  dex_cache->NumResolvedFields());
   EXPECT_TRUE(dex_cache->StaticMethodTypeSize() == dex_cache->NumResolvedMethodTypes()
@@ -96,10 +95,10 @@
       soa.Decode<mirror::ClassLoader>(jclass_loader)));
   Handle<mirror::Class> klass1 =
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "Lpackage1/Package1;", class_loader));
-  ASSERT_TRUE(klass1.Get() != nullptr);
+  ASSERT_TRUE(klass1 != nullptr);
   Handle<mirror::Class> klass2 =
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "Lpackage2/Package2;", class_loader));
-  ASSERT_TRUE(klass2.Get() != nullptr);
+  ASSERT_TRUE(klass2 != nullptr);
   EXPECT_EQ(klass1->GetDexCache(), klass2->GetDexCache());
 
   EXPECT_NE(klass1->NumStaticFields(), 0u);
diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc
index 978cc32..be0eac0 100644
--- a/runtime/mirror/emulated_stack_frame.cc
+++ b/runtime/mirror/emulated_stack_frame.cc
@@ -173,13 +173,13 @@
 
   Handle<mirror::ObjectArray<mirror::Object>> references(hs.NewHandle(
       mirror::ObjectArray<mirror::Object>::Alloc(self, array_class, refs_size)));
-  if (references.Get() == nullptr) {
+  if (references == nullptr) {
     DCHECK(self->IsExceptionPending());
     return nullptr;
   }
 
   Handle<ByteArray> stack_frame(hs.NewHandle(ByteArray::Alloc(self, frame_size)));
-  if (stack_frame.Get() == nullptr) {
+  if (stack_frame == nullptr) {
     DCHECK(self->IsExceptionPending());
     return nullptr;
   }
diff --git a/runtime/mirror/field-inl.h b/runtime/mirror/field-inl.h
index c03f20a..2496989 100644
--- a/runtime/mirror/field-inl.h
+++ b/runtime/mirror/field-inl.h
@@ -33,7 +33,7 @@
   // Try to resolve type before allocating since this is a thread suspension point.
   Handle<mirror::Class> type = hs.NewHandle(field->GetType<true>());
 
-  if (type.Get() == nullptr) {
+  if (type == nullptr) {
     if (force_resolve) {
       if (kIsDebugBuild) {
         self->AssertPendingException();
@@ -49,7 +49,7 @@
     }
   }
   auto ret = hs.NewHandle(ObjPtr<Field>::DownCast(StaticClass()->AllocObject(self)));
-  if (UNLIKELY(ret.Get() == nullptr)) {
+  if (UNLIKELY(ret == nullptr)) {
     self->AssertPendingOOMException();
     return nullptr;
   }
diff --git a/runtime/mirror/method_handle_impl.cc b/runtime/mirror/method_handle_impl.cc
index 4f1c448..fa4d25a 100644
--- a/runtime/mirror/method_handle_impl.cc
+++ b/runtime/mirror/method_handle_impl.cc
@@ -28,6 +28,18 @@
   return klass;
 }
 
+void MethodHandle::Initialize(uintptr_t art_field_or_method,
+                              Kind kind,
+                              Handle<MethodType> method_type)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  CHECK(!Runtime::Current()->IsActiveTransaction());
+  SetFieldObject<false>(CachedSpreadInvokerOffset(), nullptr);
+  SetFieldObject<false>(NominalTypeOffset(), nullptr);
+  SetFieldObject<false>(MethodTypeOffset(), method_type.Get());
+  SetField32<false>(HandleKindOffset(), static_cast<uint32_t>(kind));
+  SetField64<false>(ArtFieldOrMethodOffset(), art_field_or_method);
+}
+
 GcRoot<mirror::Class> MethodHandleImpl::static_class_;
 
 void MethodHandleImpl::SetClass(Class* klass) {
@@ -45,5 +57,17 @@
   static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
 }
 
+mirror::MethodHandleImpl* MethodHandleImpl::Create(Thread* const self,
+                                                   uintptr_t art_field_or_method,
+                                                   MethodHandle::Kind kind,
+                                                   Handle<MethodType> method_type)
+    REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
+  StackHandleScope<1> hs(self);
+  Handle<mirror::MethodHandleImpl> mh(
+      hs.NewHandle(ObjPtr<MethodHandleImpl>::DownCast(StaticClass()->AllocObject(self))));
+  mh->Initialize(art_field_or_method, kind, method_type);
+  return mh.Get();
+}
+
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
index 53d267b..9938af8 100644
--- a/runtime/mirror/method_handle_impl.h
+++ b/runtime/mirror/method_handle_impl.h
@@ -17,10 +17,11 @@
 #ifndef ART_RUNTIME_MIRROR_METHOD_HANDLE_IMPL_H_
 #define ART_RUNTIME_MIRROR_METHOD_HANDLE_IMPL_H_
 
+#include "art_field.h"
+#include "art_method.h"
 #include "class.h"
 #include "gc_root.h"
 #include "object-inl.h"
-#include "method_handles.h"
 #include "method_type.h"
 
 namespace art {
@@ -82,10 +83,19 @@
         GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_)));
   }
 
+  ObjPtr<mirror::Class> GetTargetClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+    Kind kind = GetHandleKind();
+    return (kind <= kLastValidKind) ?
+        GetTargetMethod()->GetDeclaringClass() : GetTargetField()->GetDeclaringClass();
+  }
+
   static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
 
+ protected:
+  void Initialize(uintptr_t art_field_or_method, Kind kind, Handle<MethodType> method_type)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
-  // NOTE: cached_spread_invoker_ isn't used by the runtime.
   HeapReference<mirror::MethodHandle> cached_spread_invoker_;
   HeapReference<mirror::MethodType> nominal_type_;
   HeapReference<mirror::MethodType> method_type_;
@@ -93,6 +103,9 @@
   uint64_t art_field_or_method_;
 
  private:
+  static MemberOffset CachedSpreadInvokerOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodHandle, cached_spread_invoker_));
+  }
   static MemberOffset NominalTypeOffset() {
     return MemberOffset(OFFSETOF_MEMBER(MethodHandle, nominal_type_));
   }
@@ -113,6 +126,12 @@
 // C++ mirror of java.lang.invoke.MethodHandleImpl
 class MANAGED MethodHandleImpl : public MethodHandle {
  public:
+  static mirror::MethodHandleImpl* Create(Thread* const self,
+                                          uintptr_t art_field_or_method,
+                                          MethodHandle::Kind kind,
+                                          Handle<MethodType> method_type)
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
   static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
     return static_class_.Read();
   }
diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc
new file mode 100644
index 0000000..c758e54
--- /dev/null
+++ b/runtime/mirror/method_handles_lookup.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "method_handles_lookup.h"
+
+#include "class.h"
+#include "gc_root-inl.h"
+#include "object-inl.h"
+#include "handle_scope.h"
+#include "modifiers.h"
+
+namespace art {
+namespace mirror {
+
+GcRoot<mirror::Class> MethodHandlesLookup::static_class_;
+
+void MethodHandlesLookup::SetClass(Class* klass) {
+  CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+  CHECK(klass != nullptr);
+  static_class_ = GcRoot<Class>(klass);
+}
+
+void MethodHandlesLookup::ResetClass() {
+  CHECK(!static_class_.IsNull());
+  static_class_ = GcRoot<Class>(nullptr);
+}
+
+void MethodHandlesLookup::VisitRoots(RootVisitor* visitor) {
+  static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+MethodHandlesLookup* MethodHandlesLookup::Create(Thread* const self, Handle<Class> lookup_class)
+  REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
+  static constexpr uint32_t kAllModes = kAccPublic | kAccPrivate | kAccProtected | kAccStatic;
+
+  StackHandleScope<1> hs(self);
+  Handle<MethodHandlesLookup> mhl(
+      hs.NewHandle(ObjPtr<MethodHandlesLookup>::DownCast(StaticClass()->AllocObject(self))));
+  mhl->SetFieldObject<false>(LookupClassOffset(), lookup_class.Get());
+  mhl->SetField32<false>(AllowedModesOffset(), kAllModes);
+  return mhl.Get();
+}
+
+}  // namespace mirror
+}  // namespace art
diff --git a/runtime/mirror/method_handles_lookup.h b/runtime/mirror/method_handles_lookup.h
new file mode 100644
index 0000000..63eb428
--- /dev/null
+++ b/runtime/mirror/method_handles_lookup.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_
+#define ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_
+
+#include "obj_ptr.h"
+#include "gc_root.h"
+#include "object.h"
+#include "handle.h"
+#include "utils.h"
+
+namespace art {
+
+struct MethodHandlesLookupOffsets;
+class RootVisitor;
+
+namespace mirror {
+
+// C++ mirror of java.lang.invoke.MethodHandles.Lookup
+class MANAGED MethodHandlesLookup : public Object {
+ public:
+  static mirror::MethodHandlesLookup* Create(Thread* const self,
+                                             Handle<Class> lookup_class)
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+  static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return static_class_.Read();
+  }
+
+  static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+  static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+  static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+  static MemberOffset AllowedModesOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodHandlesLookup, allowed_modes_));
+  }
+
+  static MemberOffset LookupClassOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodHandlesLookup, lookup_class_));
+  }
+
+  HeapReference<mirror::Class> lookup_class_;
+
+  int32_t allowed_modes_;
+
+  static GcRoot<mirror::Class> static_class_;  // java.lang.invoke.MethodHandles.Lookup.class
+
+  friend struct art::MethodHandlesLookupOffsets;  // for verifying offset information
+  DISALLOW_IMPLICIT_CONSTRUCTORS(MethodHandlesLookup);
+};
+
+}  // namespace mirror
+}  // namespace art
+
+#endif  // ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_
diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc
index 5d77a16..4b8dfac 100644
--- a/runtime/mirror/method_type.cc
+++ b/runtime/mirror/method_type.cc
@@ -44,6 +44,22 @@
   return mt.Get();
 }
 
+size_t MethodType::NumberOfVRegs() REQUIRES_SHARED(Locks::mutator_lock_) {
+  mirror::ObjectArray<Class>* const p_types = GetPTypes();
+  const int32_t p_types_length = p_types->GetLength();
+
+  // Initialize |num_vregs| with number of parameters and only increment it for
+  // types requiring a second vreg.
+  size_t num_vregs = static_cast<size_t>(p_types_length);
+  for (int32_t i = 0; i < p_types_length; ++i) {
+    mirror::Class* klass = p_types->GetWithoutChecks(i);
+    if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) {
+      ++num_vregs;
+    }
+  }
+  return num_vregs;
+}
+
 bool MethodType::IsExactMatch(mirror::MethodType* target) REQUIRES_SHARED(Locks::mutator_lock_) {
   mirror::ObjectArray<Class>* const p_types = GetPTypes();
   const int32_t params_length = p_types->GetLength();
diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h
index 9a98143..374bbe5 100644
--- a/runtime/mirror/method_type.h
+++ b/runtime/mirror/method_type.h
@@ -44,6 +44,10 @@
     return GetFieldObject<ObjectArray<Class>>(OFFSET_OF_OBJECT_MEMBER(MethodType, p_types_));
   }
 
+  // Number of virtual registers required to hold the parameters for
+  // this method type.
+  size_t NumberOfVRegs() REQUIRES_SHARED(Locks::mutator_lock_);
+
   Class* GetRType() REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(MethodType, r_type_));
   }
diff --git a/runtime/mirror/method_type_test.cc b/runtime/mirror/method_type_test.cc
index 637bafd..41231ef 100644
--- a/runtime/mirror/method_type_test.cc
+++ b/runtime/mirror/method_type_test.cc
@@ -51,7 +51,7 @@
 
   Handle<mirror::Class> return_clazz = hs.NewHandle(class_linker->FindClass(
           soa.Self(), FullyQualifiedType(return_type).c_str(), boot_class_loader));
-  CHECK(return_clazz.Get() != nullptr);
+  CHECK(return_clazz != nullptr);
 
   ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
   mirror::Class* class_array_type = class_linker->FindArrayClass(self, &class_type);
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index 6a4ec9d..e761e4d 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -530,8 +530,8 @@
 
   Handle<Object> x(hs.NewHandle(X->AllocObject(soa.Self())));
   Handle<Object> y(hs.NewHandle(Y->AllocObject(soa.Self())));
-  ASSERT_TRUE(x.Get() != nullptr);
-  ASSERT_TRUE(y.Get() != nullptr);
+  ASSERT_TRUE(x != nullptr);
+  ASSERT_TRUE(y != nullptr);
 
   EXPECT_TRUE(x->InstanceOf(X));
   EXPECT_FALSE(x->InstanceOf(Y));
@@ -650,7 +650,7 @@
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<1> hs(soa.Self());
   Handle<String> s(hs.NewHandle(String::AllocFromModifiedUtf8(soa.Self(), "ABC")));
-  ASSERT_TRUE(s.Get() != nullptr);
+  ASSERT_TRUE(s != nullptr);
   Class* c = s->GetClass();
   ASSERT_TRUE(c != nullptr);
 
@@ -684,9 +684,9 @@
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<4> hs(soa.Self());
   Handle<String> s(hs.NewHandle(String::AllocFromModifiedUtf8(soa.Self(), "ABC")));
-  ASSERT_TRUE(s.Get() != nullptr);
+  ASSERT_TRUE(s != nullptr);
   Handle<Class> c(hs.NewHandle(s->GetClass()));
-  ASSERT_TRUE(c.Get() != nullptr);
+  ASSERT_TRUE(c != nullptr);
 
   // Wrong type.
   EXPECT_TRUE(c->FindDeclaredStaticField("CASE_INSENSITIVE_ORDER", "I") == nullptr);
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 9b8445d..c2407d7 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -308,7 +308,7 @@
 }
 
 template<typename MemoryType>
-bool String::AllASCII(const MemoryType* const chars, const int length) {
+inline bool String::AllASCII(const MemoryType* chars, const int length) {
   static_assert(std::is_unsigned<MemoryType>::value, "Expecting unsigned MemoryType");
   for (int i = 0; i < length; ++i) {
     // Valid ASCII characters are in range 1..0x7f. Zero is not considered ASCII
@@ -320,6 +320,13 @@
   return true;
 }
 
+inline bool String::DexFileStringAllASCII(const char* chars, const int length) {
+  // For strings from the dex file we just need to check that
+  // the terminating character is at the right position.
+  DCHECK_EQ(AllASCII(reinterpret_cast<const uint8_t*>(chars), length), chars[length] == 0);
+  return chars[length] == 0;
+}
+
 }  // namespace mirror
 }  // namespace art
 
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 409c6c2..08296c6 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -32,7 +32,7 @@
 namespace mirror {
 
 // String Compression
-static constexpr bool kUseStringCompression = false;
+static constexpr bool kUseStringCompression = true;
 enum class StringCompressionFlag : uint32_t {
     kCompressed = 0u,
     kUncompressed = 1u
@@ -184,7 +184,9 @@
   bool IsValueNull() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<typename MemoryType>
-  static bool AllASCII(const MemoryType* const chars, const int length);
+  static bool AllASCII(const MemoryType* chars, const int length);
+
+  static bool DexFileStringAllASCII(const char* chars, const int length);
 
   ALWAYS_INLINE static bool IsCompressed(int32_t count) {
     return GetCompressionFlagFromCount(count) == StringCompressionFlag::kCompressed;
diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc
index 4fbfe47..27ce149 100644
--- a/runtime/monitor_test.cc
+++ b/runtime/monitor_test.cc
@@ -77,7 +77,7 @@
   while (length > 10) {
     MutableHandle<mirror::Object> h((*hsp)->NewHandle<mirror::Object>(
         mirror::ObjectArray<mirror::Object>::Alloc(self, ca.Get(), length / 4)));
-    if (self->IsExceptionPending() || h.Get() == nullptr) {
+    if (self->IsExceptionPending() || h == nullptr) {
       self->ClearException();
 
       // Try a smaller length
@@ -95,7 +95,7 @@
   // Allocate simple objects till it fails.
   while (!self->IsExceptionPending()) {
     MutableHandle<mirror::Object> h = (*hsp)->NewHandle<mirror::Object>(c->AllocObject(self));
-    if (!self->IsExceptionPending() && h.Get() != nullptr) {
+    if (!self->IsExceptionPending() && h != nullptr) {
       handles->push_back(h);
     }
   }
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 24308d9..6bfccdc 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -350,7 +350,7 @@
   Thread* const self = Thread::Current();
   StackHandleScope<1> hs(self);
   Handle<mirror::Class> klass(hs.NewHandle(dex_cache->GetResolvedType(field_id.class_idx_)));
-  if (klass.Get() == nullptr) {
+  if (klass == nullptr) {
     return;
   }
   if (is_static) {
@@ -512,7 +512,7 @@
     CHECK(dex_file != nullptr);
     StackHandleScope<1> hs(soa.Self());
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->RegisterDexFile(*dex_file, nullptr)));
-    CHECK(dex_cache.Get() != nullptr);  // Boot class path dex caches are never unloaded.
+    CHECK(dex_cache != nullptr);  // Boot class path dex caches are never unloaded.
     if (kPreloadDexCachesStrings) {
       for (size_t j = 0; j < dex_cache->NumStrings(); j++) {
         PreloadDexCachesResolveString(dex_cache, dex::StringIndex(j), strings);
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index 268d71a..be6f7f2 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -41,7 +41,7 @@
     Thread* heap_task_thread =
         Runtime::Current()->GetHeap()->GetTaskProcessor()->GetRunningThread();
     // heap_task_thread could be null if the daemons aren't yet started.
-    if (heap_task_thread != nullptr && decoded_peer == heap_task_thread->GetPeer()) {
+    if (heap_task_thread != nullptr && decoded_peer == heap_task_thread->GetPeerFromOtherThread()) {
       return nullptr;
     }
     // Suspend thread to build stack trace.
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 5438a6d..256787b 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -81,7 +81,7 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   Handle<mirror::Class> c(
       hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader)));
-  if (c.Get() == nullptr) {
+  if (c == nullptr) {
     ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
     env->ExceptionClear();
     jthrowable cnfe = reinterpret_cast<jthrowable>(
@@ -137,7 +137,7 @@
   size_t array_idx = 0;
   auto object_array = hs.NewHandle(mirror::ObjectArray<mirror::Field>::Alloc(
       self, mirror::Field::ArrayClass(), array_size));
-  if (object_array.Get() == nullptr) {
+  if (object_array == nullptr) {
     return nullptr;
   }
   for (ArtField& field : ifields) {
@@ -267,7 +267,7 @@
   Handle<mirror::String> h_name(hs.NewHandle(name));
 
   // We search the current class, its direct interfaces then its superclass.
-  while (h_clazz.Get() != nullptr) {
+  while (h_clazz != nullptr) {
     mirror::Field* result = GetDeclaredField(self, h_clazz.Get(), h_name.Get());
     if ((result != nullptr) && (result->GetAccessFlags() & kAccPublic)) {
       return result;
@@ -319,14 +319,14 @@
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<3> hs(soa.Self());
   Handle<mirror::String> h_string = hs.NewHandle(soa.Decode<mirror::String>(name));
-  if (h_string.Get() == nullptr) {
+  if (h_string == nullptr) {
     ThrowNullPointerException("name == null");
     return nullptr;
   }
   Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis));
   Handle<mirror::Field> result =
       hs.NewHandle(GetDeclaredField(soa.Self(), h_klass.Get(), h_string.Get()));
-  if (result.Get() == nullptr) {
+  if (result == nullptr) {
     std::string name_str = h_string->ToModifiedUtf8();
     if (name_str == "value" && h_klass->IsStringClass()) {
       // We log the error for this specific case, as the user might just swallow the exception.
@@ -377,7 +377,7 @@
   }
   auto h_constructors = hs.NewHandle(mirror::ObjectArray<mirror::Constructor>::Alloc(
       soa.Self(), mirror::Constructor::ArrayClass(), constructor_count));
-  if (UNLIKELY(h_constructors.Get() == nullptr)) {
+  if (UNLIKELY(h_constructors == nullptr)) {
     soa.Self()->AssertPendingException();
     return nullptr;
   }
@@ -428,7 +428,7 @@
   }
   auto ret = hs.NewHandle(mirror::ObjectArray<mirror::Method>::Alloc(
       soa.Self(), mirror::Method::ArrayClass(), num_methods));
-  if (ret.Get() == nullptr) {
+  if (ret == nullptr) {
     soa.Self()->AssertPendingOOMException();
     return nullptr;
   }
@@ -645,7 +645,7 @@
   // Verify that we can access the class.
   if (!klass->IsPublic()) {
     caller.Assign(GetCallingClass(soa.Self(), 1));
-    if (caller.Get() != nullptr && !caller->CanAccess(klass.Get())) {
+    if (caller != nullptr && !caller->CanAccess(klass.Get())) {
       soa.Self()->ThrowNewExceptionF(
           "Ljava/lang/IllegalAccessException;", "%s is not accessible from %s",
           klass->PrettyClass().c_str(), caller->PrettyClass().c_str());
@@ -673,17 +673,17 @@
     }
   }
   auto receiver = hs.NewHandle(klass->AllocObject(soa.Self()));
-  if (UNLIKELY(receiver.Get() == nullptr)) {
+  if (UNLIKELY(receiver == nullptr)) {
     soa.Self()->AssertPendingOOMException();
     return nullptr;
   }
   // Verify that we can access the constructor.
   auto* declaring_class = constructor->GetDeclaringClass();
   if (!constructor->IsPublic()) {
-    if (caller.Get() == nullptr) {
+    if (caller == nullptr) {
       caller.Assign(GetCallingClass(soa.Self(), 1));
     }
-    if (UNLIKELY(caller.Get() != nullptr && !VerifyAccess(receiver.Get(),
+    if (UNLIKELY(caller != nullptr && !VerifyAccess(receiver.Get(),
                                                           declaring_class,
                                                           constructor->GetAccessFlags(),
                                                           caller.Get()))) {
diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc
index ee6dda5..b1ed74a 100644
--- a/runtime/native/java_lang_DexCache.cc
+++ b/runtime/native/java_lang_DexCache.cc
@@ -53,7 +53,7 @@
 static jobject DexCache_getResolvedType(JNIEnv* env, jobject javaDexCache, jint type_index) {
   ScopedFastNativeObjectAccess soa(env);
   ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache);
-  CHECK_LT(static_cast<size_t>(type_index), dex_cache->GetDexFile()->NumTypeIds());
+  CHECK_LT(static_cast<size_t>(type_index), dex_cache->NumResolvedTypes());
   return soa.AddLocalReference<jobject>(dex_cache->GetResolvedType(dex::TypeIndex(type_index)));
 }
 
diff --git a/runtime/native/java_lang_invoke_MethodHandleImpl.cc b/runtime/native/java_lang_invoke_MethodHandleImpl.cc
index 72a37f8..9113841 100644
--- a/runtime/native/java_lang_invoke_MethodHandleImpl.cc
+++ b/runtime/native/java_lang_invoke_MethodHandleImpl.cc
@@ -57,7 +57,7 @@
     }
   }
 
-  if (UNLIKELY(h_object.Get() == nullptr)) {
+  if (UNLIKELY(h_object == nullptr)) {
     soa.Self()->AssertPendingOOMException();
     return nullptr;
   }
diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc
index ee59c4a..2a39428 100644
--- a/runtime/native/java_lang_reflect_Executable.cc
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -103,7 +103,7 @@
   }
 
   // Validate the MethodParameters system annotation data.
-  if (UNLIKELY(names.Get() == nullptr || access_flags.Get() == nullptr)) {
+  if (UNLIKELY(names == nullptr || access_flags == nullptr)) {
     ThrowIllegalArgumentException(
         StringPrintf("Missing parameter metadata for names or access flags for %s",
                      art_method->PrettyMethod().c_str()).c_str());
@@ -132,7 +132,7 @@
           mirror::ObjectArray<mirror::Object>::Alloc(self,
                                                      parameter_array_class.Get(),
                                                      names_count));
-  if (UNLIKELY(parameter_array.Get() == nullptr)) {
+  if (UNLIKELY(parameter_array == nullptr)) {
     self->AssertPendingException();
     return nullptr;
   }
@@ -154,7 +154,7 @@
 
     // Allocate / initialize the Parameter to add to parameter_array.
     parameter.Assign(parameter_class->AllocObject(self));
-    if (UNLIKELY(parameter.Get() == nullptr)) {
+    if (UNLIKELY(parameter == nullptr)) {
       self->AssertPendingOOMException();
       return nullptr;
     }
diff --git a/runtime/native/libcore_util_CharsetUtils.cc b/runtime/native/libcore_util_CharsetUtils.cc
index 2590452..e51b6d2 100644
--- a/runtime/native/libcore_util_CharsetUtils.cc
+++ b/runtime/native/libcore_util_CharsetUtils.cc
@@ -155,7 +155,7 @@
   ScopedObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String>(java_string)));
-  if (string.Get() == nullptr) {
+  if (string == nullptr) {
     return nullptr;
   }
 
@@ -192,7 +192,7 @@
   ScopedObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String>(java_string)));
-  if (string.Get() == nullptr) {
+  if (string == nullptr) {
     return nullptr;
   }
 
diff --git a/runtime/oat.h b/runtime/oat.h
index e454c64..656b868 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  static constexpr uint8_t kOatVersion[] = { '1', '1', '1', '\0' };  // hash-based DexCache types.
+  static constexpr uint8_t kOatVersion[] = { '1', '1', '3', '\0' };  // Invoke info change.
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 31eb1cc..493da27 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -273,6 +273,36 @@
   return true;
 }
 
+static bool FindDexFileMapItem(const uint8_t* dex_begin,
+                               const uint8_t* dex_end,
+                               DexFile::MapItemType map_item_type,
+                               const DexFile::MapItem** result_item) {
+  *result_item = nullptr;
+
+  const DexFile::Header* header =
+      BoundsCheckedCast<const DexFile::Header*>(dex_begin, dex_begin, dex_end);
+  if (nullptr == header) return false;
+
+  if (!DexFile::IsMagicValid(header->magic_)) return true;  // Not a dex file, not an error.
+
+  const DexFile::MapList* map_list =
+      BoundsCheckedCast<const DexFile::MapList*>(dex_begin + header->map_off_, dex_begin, dex_end);
+  if (nullptr == map_list) return false;
+
+  const DexFile::MapItem* map_item = map_list->list_;
+  size_t count = map_list->size_;
+  while (count--) {
+    if (map_item->type_ == static_cast<uint16_t>(map_item_type)) {
+      *result_item = map_item;
+      break;
+    }
+    map_item = BoundsCheckedCast<const DexFile::MapItem*>(map_item + 1, dex_begin, dex_end);
+    if (nullptr == map_item) return false;
+  }
+
+  return true;
+}
+
 bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) {
   if (!GetOatHeader().IsValid()) {
     std::string cause = GetOatHeader().GetValidationErrorMessage();
@@ -501,7 +531,19 @@
 
     uint8_t* current_dex_cache_arrays = nullptr;
     if (dex_cache_arrays != nullptr) {
-      DexCacheArraysLayout layout(pointer_size, *header);
+      // All DexCache types except for CallSite have their instance counts in the
+      // DexFile header. For CallSites, we need to read the info from the MapList.
+      const DexFile::MapItem* call_sites_item = nullptr;
+      if (!FindDexFileMapItem(DexBegin(),
+                              DexEnd(),
+                              DexFile::MapItemType::kDexTypeCallSiteIdItem,
+                              &call_sites_item)) {
+        *error_msg = StringPrintf("In oat file '%s' could not read data from truncated DexFile map",
+                                  GetLocation().c_str());
+        return false;
+      }
+      size_t num_call_sites = call_sites_item == nullptr ? 0 : call_sites_item->size_;
+      DexCacheArraysLayout layout(pointer_size, *header, num_call_sites);
       if (layout.Size() != 0u) {
         if (static_cast<size_t>(dex_cache_arrays_end - dex_cache_arrays) < layout.Size()) {
           *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with "
@@ -1468,77 +1510,6 @@
   return out.str();
 }
 
-bool OatFile::CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg) {
-  if (dex_dependencies == nullptr || dex_dependencies[0] == 0) {
-    // No dependencies.
-    return true;
-  }
-
-  // Assumption: this is not performance-critical. So it's OK to do this with a std::string and
-  //             Split() instead of manual parsing of the combined char*.
-  std::vector<std::string> split;
-  Split(dex_dependencies, kDexClassPathEncodingSeparator, &split);
-  if (split.size() % 2 != 0) {
-    // Expected pairs of location and checksum.
-    *msg = StringPrintf("Odd number of elements in dependency list %s", dex_dependencies);
-    return false;
-  }
-
-  for (auto it = split.begin(), end = split.end(); it != end; it += 2) {
-    std::string& location = *it;
-    std::string& checksum = *(it + 1);
-    int64_t converted = strtoll(checksum.c_str(), nullptr, 10);
-    if (converted == 0) {
-      // Conversion error.
-      *msg = StringPrintf("Conversion error for %s", checksum.c_str());
-      return false;
-    }
-
-    uint32_t dex_checksum;
-    std::string error_msg;
-    if (DexFile::GetChecksum(DexFile::GetDexCanonicalLocation(location.c_str()).c_str(),
-                             &dex_checksum,
-                             &error_msg)) {
-      if (converted != dex_checksum) {
-        *msg = StringPrintf("Checksums don't match for %s: %" PRId64 " vs %u",
-                            location.c_str(), converted, dex_checksum);
-        return false;
-      }
-    } else {
-      // Problem retrieving checksum.
-      // TODO: odex files?
-      *msg = StringPrintf("Could not retrieve checksum for %s: %s", location.c_str(),
-                          error_msg.c_str());
-      return false;
-    }
-  }
-
-  return true;
-}
-
-bool OatFile::GetDexLocationsFromDependencies(const char* dex_dependencies,
-                                              std::vector<std::string>* locations) {
-  DCHECK(locations != nullptr);
-  if (dex_dependencies == nullptr || dex_dependencies[0] == 0) {
-    return true;
-  }
-
-  // Assumption: this is not performance-critical. So it's OK to do this with a std::string and
-  //             Split() instead of manual parsing of the combined char*.
-  std::vector<std::string> split;
-  Split(dex_dependencies, kDexClassPathEncodingSeparator, &split);
-  if (split.size() % 2 != 0) {
-    // Expected pairs of location and checksum.
-    return false;
-  }
-
-  for (auto it = split.begin(), end = split.end(); it != end; it += 2) {
-    locations->push_back(*it);
-  }
-
-  return true;
-}
-
 OatFile::OatClass OatFile::FindOatClass(const DexFile& dex_file,
                                         uint16_t class_def_idx,
                                         bool* found) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 111755e..d24283a 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -290,15 +290,6 @@
   // Create a dependency list (dex locations and checksums) for the given dex files.
   static std::string EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files);
 
-  // Check the given dependency list against their dex files - thus the name "Static," this does
-  // not check the class-loader environment, only whether there have been file updates.
-  static bool CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg);
-
-  // Get the dex locations of a dependency list. Note: this is *not* cleaned for synthetic
-  // locations of multidex files.
-  static bool GetDexLocationsFromDependencies(const char* dex_dependencies,
-                                              std::vector<std::string>* locations);
-
   // Finds the associated oat class for a dex_file and descriptor. Returns an invalid OatClass on
   // error and sets found to false.
   static OatClass FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found);
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 77cdd28..5ae2fc5 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -38,6 +38,8 @@
 
 namespace art {
 
+using android::base::StringPrintf;
+
 std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStatus status) {
   switch (status) {
     case OatFileAssistant::kOatCannotOpen:
@@ -264,7 +266,7 @@
     const OatFile& oat_file, const char* dex_location) {
   std::vector<std::unique_ptr<const DexFile>> dex_files;
 
-  // Load the primary dex file.
+  // Load the main dex file.
   std::string error_msg;
   const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(
       dex_location, nullptr, &error_msg);
@@ -280,12 +282,12 @@
   }
   dex_files.push_back(std::move(dex_file));
 
-  // Load secondary multidex files
+  // Load the rest of the multidex entries
   for (size_t i = 1; ; i++) {
-    std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location);
-    oat_dex_file = oat_file.GetOatDexFile(secondary_dex_location.c_str(), nullptr);
+    std::string multidex_dex_location = DexFile::GetMultiDexLocation(i, dex_location);
+    oat_dex_file = oat_file.GetOatDexFile(multidex_dex_location.c_str(), nullptr);
     if (oat_dex_file == nullptr) {
-      // There are no more secondary dex files to load.
+      // There are no more multidex entries to load.
       break;
     }
 
@@ -300,10 +302,10 @@
 }
 
 bool OatFileAssistant::HasOriginalDexFiles() {
-  // Ensure GetRequiredDexChecksum has been run so that
+  // Ensure GetRequiredDexChecksums has been run so that
   // has_original_dex_files_ is initialized. We don't care about the result of
-  // GetRequiredDexChecksum.
-  GetRequiredDexChecksum();
+  // GetRequiredDexChecksums.
+  GetRequiredDexChecksums();
   return has_original_dex_files_;
 }
 
@@ -316,88 +318,66 @@
 }
 
 bool OatFileAssistant::DexChecksumUpToDate(const VdexFile& file, std::string* error_msg) {
-  if (file.GetHeader().GetNumberOfDexFiles() <= 0) {
-    VLOG(oat) << "Vdex does not contain any dex files";
+  const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums();
+  if (required_dex_checksums == nullptr) {
+    LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date.";
+    return true;
+  }
+
+  uint32_t number_of_dex_files = file.GetHeader().GetNumberOfDexFiles();
+  if (required_dex_checksums->size() != number_of_dex_files) {
+    *error_msg = StringPrintf("expected %zu dex files but found %u",
+                              required_dex_checksums->size(),
+                              number_of_dex_files);
     return false;
   }
 
-  // TODO: Use GetRequiredDexChecksum to get secondary checksums as well, not
-  // just the primary. Because otherwise we may fail to see a secondary
-  // checksum failure in the case when the original (multidex) files are
-  // stripped but we have a newer odex file.
-  const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum();
-  if (dex_checksum_pointer != nullptr) {
-    uint32_t actual_checksum = file.GetLocationChecksum(0);
-    if (*dex_checksum_pointer != actual_checksum) {
-      VLOG(oat) << "Dex checksum does not match for primary dex: " << dex_location_
-        << ". Expected: " << *dex_checksum_pointer
-        << ", Actual: " << actual_checksum;
+  for (uint32_t i = 0; i < number_of_dex_files; i++) {
+    uint32_t expected_checksum = (*required_dex_checksums)[i];
+    uint32_t actual_checksum = file.GetLocationChecksum(i);
+    if (expected_checksum != actual_checksum) {
+      std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
+      *error_msg = StringPrintf("Dex checksum does not match for dex: %s."
+                                "Expected: %u, actual: %u",
+                                dex.c_str(),
+                                expected_checksum,
+                                actual_checksum);
       return false;
     }
   }
 
-  // Verify the dex checksums for any secondary multidex files
-  for (uint32_t i = 1; i < file.GetHeader().GetNumberOfDexFiles(); i++) {
-    std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
-    uint32_t expected_secondary_checksum = 0;
-    if (DexFile::GetChecksum(secondary_dex_location.c_str(),
-                             &expected_secondary_checksum,
-                             error_msg)) {
-      uint32_t actual_secondary_checksum = file.GetLocationChecksum(i);
-      if (expected_secondary_checksum != actual_secondary_checksum) {
-        VLOG(oat) << "Dex checksum does not match for secondary dex: "
-          << secondary_dex_location
-          << ". Expected: " << expected_secondary_checksum
-          << ", Actual: " << actual_secondary_checksum;
-        return false;
-      }
-    } else {
-      // If we can't get the checksum for the secondary location, we assume
-      // the dex checksum is up to date for this and all other secondary dex
-      // files.
-      break;
-    }
-  }
   return true;
 }
 
 bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* error_msg) {
-  // Note: GetOatDexFile will return null if the dex checksum doesn't match
-  // what we provide, which verifies the primary dex checksum for us.
-  const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum();
-  const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile(
-      dex_location_.c_str(), dex_checksum_pointer, error_msg);
-  if (oat_dex_file == nullptr) {
+  const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums();
+  if (required_dex_checksums == nullptr) {
+    LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date.";
+    return true;
+  }
+
+  uint32_t number_of_dex_files = file.GetOatHeader().GetDexFileCount();
+  if (required_dex_checksums->size() != number_of_dex_files) {
+    *error_msg = StringPrintf("expected %zu dex files but found %u",
+                              required_dex_checksums->size(),
+                              number_of_dex_files);
     return false;
   }
 
-  // Verify the dex checksums for any secondary multidex files
-  for (size_t i = 1; ; i++) {
-    std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
-    const OatFile::OatDexFile* secondary_oat_dex_file
-      = file.GetOatDexFile(secondary_dex_location.c_str(), nullptr);
-    if (secondary_oat_dex_file == nullptr) {
-      // There are no more secondary dex files to check.
-      break;
+  for (uint32_t i = 0; i < number_of_dex_files; i++) {
+    std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
+    uint32_t expected_checksum = (*required_dex_checksums)[i];
+    const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile(dex.c_str(), nullptr);
+    if (oat_dex_file == nullptr) {
+      *error_msg = StringPrintf("failed to find %s in %s", dex.c_str(), file.GetLocation().c_str());
+      return false;
     }
-
-    uint32_t expected_secondary_checksum = 0;
-    if (DexFile::GetChecksum(secondary_dex_location.c_str(),
-          &expected_secondary_checksum, error_msg)) {
-      uint32_t actual_secondary_checksum
-        = secondary_oat_dex_file->GetDexFileLocationChecksum();
-      if (expected_secondary_checksum != actual_secondary_checksum) {
-        VLOG(oat) << "Dex checksum does not match for secondary dex: "
-          << secondary_dex_location
-          << ". Expected: " << expected_secondary_checksum
-          << ", Actual: " << actual_secondary_checksum;
-        return false;
-      }
-    } else {
-      // If we can't get the checksum for the secondary location, we assume
-      // the dex checksum is up to date for this and all other secondary dex
-      // files.
-      break;
+    uint32_t actual_checksum = oat_dex_file->GetDexFileLocationChecksum();
+    if (expected_checksum != actual_checksum) {
+      VLOG(oat) << "Dex checksum does not match for dex: " << dex
+        << ". Expected: " << expected_checksum
+        << ", Actual: " << actual_checksum;
+      return false;
     }
   }
   return true;
@@ -710,13 +690,16 @@
   return image_spaces[0]->GetImageLocation();
 }
 
-const uint32_t* OatFileAssistant::GetRequiredDexChecksum() {
-  if (!required_dex_checksum_attempted_) {
-    required_dex_checksum_attempted_ = true;
-    required_dex_checksum_found_ = false;
+const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() {
+  if (!required_dex_checksums_attempted_) {
+    required_dex_checksums_attempted_ = true;
+    required_dex_checksums_found_ = false;
+    cached_required_dex_checksums_.clear();
     std::string error_msg;
-    if (DexFile::GetChecksum(dex_location_.c_str(), &cached_required_dex_checksum_, &error_msg)) {
-      required_dex_checksum_found_ = true;
+    if (DexFile::GetMultiDexChecksums(dex_location_.c_str(),
+                                      &cached_required_dex_checksums_,
+                                      &error_msg)) {
+      required_dex_checksums_found_ = true;
       has_original_dex_files_ = true;
     } else {
       // This can happen if the original dex file has been stripped from the
@@ -724,19 +707,23 @@
       VLOG(oat) << "OatFileAssistant: " << error_msg;
       has_original_dex_files_ = false;
 
-      // Get the checksum from the odex if we can.
+      // Get the checksums from the odex if we can.
       const OatFile* odex_file = odex_.GetFile();
       if (odex_file != nullptr) {
-        const OatFile::OatDexFile* odex_dex_file
-            = odex_file->GetOatDexFile(dex_location_.c_str(), nullptr);
-        if (odex_dex_file != nullptr) {
-          cached_required_dex_checksum_ = odex_dex_file->GetDexFileLocationChecksum();
-          required_dex_checksum_found_ = true;
+        required_dex_checksums_found_ = true;
+        for (size_t i = 0; i < odex_file->GetOatHeader().GetDexFileCount(); i++) {
+          std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
+          const OatFile::OatDexFile* odex_dex_file = odex_file->GetOatDexFile(dex.c_str(), nullptr);
+          if (odex_dex_file == nullptr) {
+            required_dex_checksums_found_ = false;
+            break;
+          }
+          cached_required_dex_checksums_.push_back(odex_dex_file->GetDexFileLocationChecksum());
         }
       }
     }
   }
-  return required_dex_checksum_found_ ? &cached_required_dex_checksum_ : nullptr;
+  return required_dex_checksums_found_ ? &cached_required_dex_checksums_ : nullptr;
 }
 
 const OatFileAssistant::ImageInfo* OatFileAssistant::GetImageInfo() {
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 6d47ad2..3ede29f 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -400,13 +400,13 @@
   // the oat file assistant.
   static std::string ImageLocation();
 
-  // Gets the dex checksum required for an up-to-date oat file.
-  // Returns dex_checksum if a required checksum was located. Returns
-  // null if the required checksum was not found.
-  // The caller shouldn't clean up or free the returned pointer.
-  // This sets the has_original_dex_files_ field to true if a checksum was
-  // found for the dex_location_ dex file.
-  const uint32_t* GetRequiredDexChecksum();
+  // Gets the dex checksums required for an up-to-date oat file.
+  // Returns cached_required_dex_checksums if the required checksums were
+  // located. Returns null if the required checksums were not found.  The
+  // caller shouldn't clean up or free the returned pointer.  This sets the
+  // has_original_dex_files_ field to true if the checksums were found for the
+  // dex_location_ dex file.
+  const std::vector<uint32_t>* GetRequiredDexChecksums();
 
   // Returns the loaded image info.
   // Loads the image info if needed. Returns null if the image info failed
@@ -430,11 +430,11 @@
   // Whether we will attempt to load oat files executable.
   bool load_executable_ = false;
 
-  // Cached value of the required dex checksum.
-  // This should be accessed only by the GetRequiredDexChecksum() method.
-  uint32_t cached_required_dex_checksum_;
-  bool required_dex_checksum_attempted_ = false;
-  bool required_dex_checksum_found_;
+  // Cached value of the required dex checksums.
+  // This should be accessed only by the GetRequiredDexChecksums() method.
+  std::vector<uint32_t> cached_required_dex_checksums_;
+  bool required_dex_checksums_attempted_ = false;
+  bool required_dex_checksums_found_;
   bool has_original_dex_files_;
 
   OatFileInfo odex_;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index f777340..9b35489 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -237,16 +237,16 @@
   EXPECT_EQ(2u, dex_files.size());
 }
 
-// Case: We have a MultiDEX file where the secondary dex file is out of date.
+// Case: We have a MultiDEX file where the non-main multdex entry is out of date.
 // Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, MultiDexSecondaryOutOfDate) {
-  std::string dex_location = GetScratchDir() + "/MultiDexSecondaryOutOfDate.jar";
+TEST_F(OatFileAssistantTest, MultiDexNonMainOutOfDate) {
+  std::string dex_location = GetScratchDir() + "/MultiDexNonMainOutOfDate.jar";
 
   // Compile code for GetMultiDexSrc1.
   Copy(GetMultiDexSrc1(), dex_location);
   GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
 
-  // Now overwrite the dex file with GetMultiDexSrc2 so the secondary checksum
+  // Now overwrite the dex file with GetMultiDexSrc2 so the non-main checksum
   // is out of date.
   Copy(GetMultiDexSrc2(), dex_location);
 
@@ -256,6 +256,37 @@
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
+// Case: We have a stripped MultiDEX file where the non-main multidex entry is
+// out of date with respect to the odex file.
+TEST_F(OatFileAssistantTest, StrippedMultiDexNonMainOutOfDate) {
+  std::string dex_location = GetScratchDir() + "/StrippedMultiDexNonMainOutOfDate.jar";
+  std::string odex_location = GetOdexDir() + "/StrippedMultiDexNonMainOutOfDate.odex";
+
+  // Compile the oat from GetMultiDexSrc1.
+  Copy(GetMultiDexSrc1(), dex_location);
+  GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+
+  // Compile the odex from GetMultiDexSrc2, which has a different non-main
+  // dex checksum.
+  Copy(GetMultiDexSrc2(), dex_location);
+  GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kInterpretOnly);
+
+  // Strip the dex file.
+  Copy(GetStrippedDexSrc1(), dex_location);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, /*load_executable*/false);
+
+  // Because the dex file is stripped, the odex file is considered the source
+  // of truth for the dex checksums. The oat file should be considered
+  // unusable.
+  std::unique_ptr<OatFile> best_file = oat_file_assistant.GetBestOatFile();
+  ASSERT_TRUE(best_file.get() != nullptr);
+  EXPECT_EQ(best_file->GetLocation(), odex_location);
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
+}
+
 // Case: We have a MultiDEX file and up-to-date OAT file for it with relative
 // encoded dex locations.
 // Expect: The oat file status is kNoDexOptNeeded.
@@ -336,16 +367,16 @@
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 }
 
-// Case: We have a MultiDEX (ODEX) VDEX file where the secondary dex file is
-// out of date and there is no corresponding ODEX file.
-TEST_F(OatFileAssistantTest, VdexMultiDexSecondaryOutOfDate) {
+// Case: We have a MultiDEX (ODEX) VDEX file where the non-main multidex entry
+// is out of date and there is no corresponding ODEX file.
+TEST_F(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) {
   // This test case is only meaningful if vdex is enabled.
   if (!kIsVdexEnabled) {
     return;
   }
 
-  std::string dex_location = GetScratchDir() + "/VdexMultiDexSecondaryOutOfDate.jar";
-  std::string oat_location = GetOdexDir() + "/VdexMultiDexSecondaryOutOfDate.oat";
+  std::string dex_location = GetScratchDir() + "/VdexMultiDexNonMainOutOfDate.jar";
+  std::string oat_location = GetOdexDir() + "/VdexMultiDexNonMainOutOfDate.oat";
 
   Copy(GetMultiDexSrc1(), dex_location);
   GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed);
@@ -1028,7 +1059,7 @@
   ClassLinker* linker = Runtime::Current()->GetClassLinker();
   Handle<mirror::Class> dexfile(
       hs.NewHandle(linker->FindSystemClass(soa.Self(), "Ldalvik/system/DexFile;")));
-  ASSERT_FALSE(dexfile.Get() == nullptr);
+  ASSERT_FALSE(dexfile == nullptr);
   linker->EnsureInitialized(soa.Self(), dexfile, true, true);
 
   for (std::pair<OatFileAssistant::DexOptNeeded, const char*> field : mapping) {
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index a46b470..7079614 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -342,7 +342,7 @@
     ScopedObjectAccessAlreadyRunnable& soa,
     Handle<mirror::ObjectArray<mirror::Object>> dex_elements,
     std::priority_queue<DexFileAndClassPair>* queue) REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (dex_elements.Get() == nullptr) {
+  if (dex_elements == nullptr) {
     // Nothing to do.
     return;
   }
@@ -463,14 +463,14 @@
         hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader));
     Handle<mirror::ObjectArray<mirror::Object>> h_dex_elements =
         hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements));
-    if (h_class_loader.Get() != nullptr &&
+    if (h_class_loader != nullptr &&
         GetDexFilesFromClassLoader(soa, h_class_loader.Get(), &queue)) {
       class_loader_ok = true;
 
       // In this case, also take into account the dex_elements array, if given. We don't need to
       // read it otherwise, as we'll compare against all open oat files anyways.
       GetDexFilesFromDexElementsArray(soa, h_dex_elements, &queue);
-    } else if (h_class_loader.Get() != nullptr) {
+    } else if (h_class_loader != nullptr) {
       VLOG(class_linker) << "Something unsupported with "
                          << mirror::Class::PrettyClass(h_class_loader->GetClass());
     }
@@ -658,7 +658,7 @@
         Handle<mirror::ClassLoader> h_loader(
             hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
         // Can not load app image without class loader.
-        if (h_loader.Get() != nullptr) {
+        if (h_loader != nullptr) {
           std::string temp_error_msg;
           // Add image space has a race condition since other threads could be reading from the
           // spaces array.
diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc
index b416b9d..d5fe1f3 100644
--- a/runtime/oat_file_test.cc
+++ b/runtime/oat_file_test.cc
@@ -62,54 +62,4 @@
         "/data/app/foo/base.apk", "o/base.apk"));
 }
 
-static std::vector<const DexFile*> ToConstDexFiles(
-    const std::vector<std::unique_ptr<const DexFile>>& in) {
-  std::vector<const DexFile*> ret;
-  for (auto& d : in) {
-    ret.push_back(d.get());
-  }
-  return ret;
-}
-
-TEST_F(OatFileTest, DexFileDependencies) {
-  std::string error_msg;
-
-  // No dependencies.
-  EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(nullptr, &error_msg)) << error_msg;
-  EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies("", &error_msg)) << error_msg;
-
-  // Ill-formed dependencies.
-  EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc", &error_msg));
-  EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*def", &error_msg));
-  EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*def*", &error_msg));
-
-  // Unsatisfiable dependency.
-  EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*", &error_msg));
-
-  // Load some dex files to be able to do a real test.
-  ScopedObjectAccess soa(Thread::Current());
-
-  std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Main");
-  std::vector<const DexFile*> dex_files_const1 = ToConstDexFiles(dex_files1);
-  std::string encoding1 = OatFile::EncodeDexFileDependencies(dex_files_const1);
-  EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding1.c_str(), &error_msg))
-      << error_msg << " " << encoding1;
-  std::vector<std::string> split1;
-  EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding1.c_str(), &split1));
-  ASSERT_EQ(split1.size(), 1U);
-  EXPECT_EQ(split1[0], dex_files_const1[0]->GetLocation());
-
-  std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex");
-  EXPECT_GT(dex_files2.size(), 1U);
-  std::vector<const DexFile*> dex_files_const2 = ToConstDexFiles(dex_files2);
-  std::string encoding2 = OatFile::EncodeDexFileDependencies(dex_files_const2);
-  EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding2.c_str(), &error_msg))
-      << error_msg << " " << encoding2;
-  std::vector<std::string> split2;
-  EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding2.c_str(), &split2));
-  ASSERT_EQ(split2.size(), 2U);
-  EXPECT_EQ(split2[0], dex_files_const2[0]->GetLocation());
-  EXPECT_EQ(split2[1], dex_files_const2[1]->GetLocation());
-}
-
 }  // namespace art
diff --git a/runtime/object_lock.cc b/runtime/object_lock.cc
index 39ab52f..f6db544 100644
--- a/runtime/object_lock.cc
+++ b/runtime/object_lock.cc
@@ -24,7 +24,7 @@
 
 template <typename T>
 ObjectLock<T>::ObjectLock(Thread* self, Handle<T> object) : self_(self), obj_(object) {
-  CHECK(object.Get() != nullptr);
+  CHECK(object != nullptr);
   obj_->MonitorEnter(self_);
 }
 
@@ -50,7 +50,7 @@
 
 template <typename T>
 ObjectTryLock<T>::ObjectTryLock(Thread* self, Handle<T> object) : self_(self), obj_(object) {
-  CHECK(object.Get() != nullptr);
+  CHECK(object != nullptr);
   acquired_ = obj_->MonitorTryEnter(self_) != nullptr;
 }
 
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index fc4b6fe..7ca233f 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -52,6 +52,7 @@
 #include "mirror/class_ext.h"
 #include "mirror/object_reference.h"
 #include "mirror/object-inl.h"
+#include "mirror/reference.h"
 #include "runtime.h"
 #include "runtime_callbacks.h"
 #include "ScopedLocalRef.h"
@@ -463,8 +464,17 @@
         }
       }
 
+      void operator()(art::ObjPtr<art::mirror::Class> klass ATTRIBUTE_UNUSED,
+                      art::ObjPtr<art::mirror::Reference> reference) const
+          REQUIRES_SHARED(art::Locks::mutator_lock_) {
+        art::mirror::Object* val = reference->GetReferent();
+        if (val == input_) {
+          reference->SetReferent<false>(output_);
+        }
+      }
+
       void VisitRoot(art::mirror::CompressedReference<art::mirror::Object>* root ATTRIBUTE_UNUSED)
-      const {
+          const {
         LOG(FATAL) << "Unreachable";
       }
 
@@ -478,7 +488,7 @@
         HeapFixupVisitor* hfv = reinterpret_cast<HeapFixupVisitor*>(arg);
 
         // Visit references, not native roots.
-        obj->VisitReferences<false>(*hfv, art::VoidFunctor());
+        obj->VisitReferences<false>(*hfv, *hfv);
       }
 
      private:
diff --git a/runtime/openjdkjvmti/ti_class_loader.cc b/runtime/openjdkjvmti/ti_class_loader.cc
index afec0bf..d05f579 100644
--- a/runtime/openjdkjvmti/ti_class_loader.cc
+++ b/runtime/openjdkjvmti/ti_class_loader.cc
@@ -119,11 +119,11 @@
     art::Handle<art::mirror::LongArray> cookie,
     const art::DexFile* dex_file) {
   art::StackHandleScope<1> hs(self);
-  CHECK(cookie.Get() != nullptr);
+  CHECK(cookie != nullptr);
   CHECK_GE(cookie->GetLength(), 1);
   art::Handle<art::mirror::LongArray> new_cookie(
       hs.NewHandle(art::mirror::LongArray::Alloc(self, cookie->GetLength() + 1)));
-  if (new_cookie.Get() == nullptr) {
+  if (new_cookie == nullptr) {
     self->AssertPendingOOMException();
     return nullptr;
   }
@@ -183,13 +183,13 @@
   // Start navigating the fields of the loader (now known to be a BaseDexClassLoader derivative)
   art::Handle<art::mirror::Object> path_list(
       hs.NewHandle(path_list_field->GetObject(loader.Get())));
-  CHECK(path_list.Get() != nullptr);
+  CHECK(path_list != nullptr);
   CHECK(!self->IsExceptionPending());
   art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(hs.NewHandle(
       dex_path_list_element_field->GetObject(path_list.Get())->
       AsObjectArray<art::mirror::Object>()));
   CHECK(!self->IsExceptionPending());
-  CHECK(dex_elements_list.Get() != nullptr);
+  CHECK(dex_elements_list != nullptr);
   size_t num_elements = dex_elements_list->GetLength();
   // Iterate over the DexPathList$Element to find the right one
   for (size_t i = 0; i < num_elements; i++) {
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index a53796c..8436045 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -68,6 +68,66 @@
 
 using android::base::StringPrintf;
 
+// A helper that fills in a classes obsolete_methods_ and obsolete_dex_caches_ classExt fields as
+// they are created. This ensures that we can always call any method of an obsolete ArtMethod object
+// almost as soon as they are created since the GetObsoleteDexCache method will succeed.
+class ObsoleteMap {
+ public:
+  art::ArtMethod* FindObsoleteVersion(art::ArtMethod* original)
+      REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) {
+    auto method_pair = id_map_.find(original);
+    if (method_pair != id_map_.end()) {
+      art::ArtMethod* res = obsolete_methods_->GetElementPtrSize<art::ArtMethod*>(
+          method_pair->second, art::kRuntimePointerSize);
+      DCHECK(res != nullptr);
+      DCHECK_EQ(original, res->GetNonObsoleteMethod());
+      return res;
+    } else {
+      return nullptr;
+    }
+  }
+
+  void RecordObsolete(art::ArtMethod* original, art::ArtMethod* obsolete)
+      REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) {
+    DCHECK(original != nullptr);
+    DCHECK(obsolete != nullptr);
+    int32_t slot = next_free_slot_++;
+    DCHECK_LT(slot, obsolete_methods_->GetLength());
+    DCHECK(nullptr ==
+           obsolete_methods_->GetElementPtrSize<art::ArtMethod*>(slot, art::kRuntimePointerSize));
+    DCHECK(nullptr == obsolete_dex_caches_->Get(slot));
+    obsolete_methods_->SetElementPtrSize(slot, obsolete, art::kRuntimePointerSize);
+    obsolete_dex_caches_->Set(slot, original_dex_cache_);
+    id_map_.insert({original, slot});
+  }
+
+  ObsoleteMap(art::ObjPtr<art::mirror::PointerArray> obsolete_methods,
+              art::ObjPtr<art::mirror::ObjectArray<art::mirror::DexCache>> obsolete_dex_caches,
+              art::ObjPtr<art::mirror::DexCache> original_dex_cache)
+      : next_free_slot_(0),
+        obsolete_methods_(obsolete_methods),
+        obsolete_dex_caches_(obsolete_dex_caches),
+        original_dex_cache_(original_dex_cache) {
+    // Figure out where the first unused slot in the obsolete_methods_ array is.
+    while (obsolete_methods_->GetElementPtrSize<art::ArtMethod*>(
+        next_free_slot_, art::kRuntimePointerSize) != nullptr) {
+      DCHECK(obsolete_dex_caches_->Get(next_free_slot_) != nullptr);
+      next_free_slot_++;
+    }
+    // Sanity check that the same slot in obsolete_dex_caches_ is free.
+    DCHECK(obsolete_dex_caches_->Get(next_free_slot_) == nullptr);
+  }
+
+ private:
+  int32_t next_free_slot_;
+  std::unordered_map<art::ArtMethod*, int32_t> id_map_;
+  // Pointers to the fields in mirror::ClassExt. These can be held as ObjPtr since this is only used
+  // when we have an exclusive mutator_lock_ (i.e. all threads are suspended).
+  art::ObjPtr<art::mirror::PointerArray> obsolete_methods_;
+  art::ObjPtr<art::mirror::ObjectArray<art::mirror::DexCache>> obsolete_dex_caches_;
+  art::ObjPtr<art::mirror::DexCache> original_dex_cache_;
+};
+
 // This visitor walks thread stacks and allocates and sets up the obsolete methods. It also does
 // some basic sanity checks that the obsolete method is sane.
 class ObsoleteMethodStackVisitor : public art::StackVisitor {
@@ -76,7 +136,7 @@
       art::Thread* thread,
       art::LinearAlloc* allocator,
       const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
-      /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps)
+      ObsoleteMap* obsolete_maps)
         : StackVisitor(thread,
                        /*context*/nullptr,
                        StackVisitor::StackWalkKind::kIncludeInlinedFrames),
@@ -94,7 +154,7 @@
       art::Thread* thread,
       art::LinearAlloc* allocator,
       const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
-      /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps)
+      ObsoleteMap* obsolete_maps)
         REQUIRES(art::Locks::mutator_lock_) {
     ObsoleteMethodStackVisitor visitor(thread,
                                        allocator,
@@ -104,6 +164,7 @@
   }
 
   bool VisitFrame() OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+    art::ScopedAssertNoThreadSuspension snts("Fixing up the stack for obsolete methods.");
     art::ArtMethod* old_method = GetMethod();
     if (obsoleted_methods_.find(old_method) != obsoleted_methods_.end()) {
       // We cannot ensure that the right dex file is used in inlined frames so we don't support
@@ -113,9 +174,8 @@
       // TODO We should really support redefining intrinsics.
       // We don't support intrinsics so check for them here.
       DCHECK(!old_method->IsIntrinsic());
-      art::ArtMethod* new_obsolete_method = nullptr;
-      auto obsolete_method_pair = obsolete_maps_->find(old_method);
-      if (obsolete_method_pair == obsolete_maps_->end()) {
+      art::ArtMethod* new_obsolete_method = obsolete_maps_->FindObsoleteVersion(old_method);
+      if (new_obsolete_method == nullptr) {
         // Create a new Obsolete Method and put it in the list.
         art::Runtime* runtime = art::Runtime::Current();
         art::ClassLinker* cl = runtime->GetClassLinker();
@@ -129,7 +189,7 @@
         DCHECK_EQ(new_obsolete_method->GetDeclaringClass(), old_method->GetDeclaringClass());
         new_obsolete_method->SetIsObsolete();
         new_obsolete_method->SetDontCompile();
-        obsolete_maps_->insert({old_method, new_obsolete_method});
+        obsolete_maps_->RecordObsolete(old_method, new_obsolete_method);
         // Update JIT Data structures to point to the new method.
         art::jit::Jit* jit = art::Runtime::Current()->GetJit();
         if (jit != nullptr) {
@@ -137,8 +197,6 @@
           // structures to keep track of the new obsolete method.
           jit->GetCodeCache()->MoveObsoleteMethod(old_method, new_obsolete_method);
         }
-      } else {
-        new_obsolete_method = obsolete_method_pair->second;
       }
       DCHECK(new_obsolete_method != nullptr);
       SetMethod(new_obsolete_method);
@@ -152,9 +210,9 @@
   // The set of all methods which could be obsoleted.
   const std::unordered_set<art::ArtMethod*>& obsoleted_methods_;
   // A map from the original to the newly allocated obsolete method for frames on this thread. The
-  // values in this map must be added to the obsolete_methods_ (and obsolete_dex_caches_) fields of
-  // the redefined classes ClassExt by the caller.
-  std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps_;
+  // values in this map are added to the obsolete_methods_ (and obsolete_dex_caches_) fields of
+  // the redefined classes ClassExt as it is filled.
+  ObsoleteMap* obsolete_maps_;
 };
 
 jvmtiError Redefiner::IsModifiableClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
@@ -431,11 +489,12 @@
 }
 
 struct CallbackCtx {
+  ObsoleteMap* obsolete_map;
   art::LinearAlloc* allocator;
-  std::unordered_map<art::ArtMethod*, art::ArtMethod*> obsolete_map;
   std::unordered_set<art::ArtMethod*> obsolete_methods;
 
-  explicit CallbackCtx(art::LinearAlloc* alloc) : allocator(alloc) {}
+  explicit CallbackCtx(ObsoleteMap* map, art::LinearAlloc* alloc)
+      : obsolete_map(map), allocator(alloc) {}
 };
 
 void DoAllocateObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SAFETY_ANALYSIS {
@@ -443,7 +502,7 @@
   ObsoleteMethodStackVisitor::UpdateObsoleteFrames(t,
                                                    data->allocator,
                                                    data->obsolete_methods,
-                                                   &data->obsolete_map);
+                                                   data->obsolete_map);
 }
 
 // This creates any ArtMethod* structures needed for obsolete methods and ensures that the stack is
@@ -454,9 +513,18 @@
   art::mirror::ClassExt* ext = art_klass->GetExtData();
   CHECK(ext->GetObsoleteMethods() != nullptr);
   art::ClassLinker* linker = driver_->runtime_->GetClassLinker();
-  CallbackCtx ctx(linker->GetAllocatorForClassLoader(art_klass->GetClassLoader()));
+  // This holds pointers to the obsolete methods map fields which are updated as needed.
+  ObsoleteMap map(ext->GetObsoleteMethods(), ext->GetObsoleteDexCaches(), art_klass->GetDexCache());
+  CallbackCtx ctx(&map, linker->GetAllocatorForClassLoader(art_klass->GetClassLoader()));
   // Add all the declared methods to the map
   for (auto& m : art_klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
+    // It is possible to simply filter out some methods where they cannot really become obsolete,
+    // such as native methods and keep their original (possibly optimized) implementations. We don't
+    // do this, however, since we would need to mark these functions (still in the classes
+    // declared_methods array) as obsolete so we will find the correct dex file to get meta-data
+    // from (for example about stack-frame size). Furthermore we would be unable to get some useful
+    // error checking from the interpreter which ensure we don't try to start executing obsolete
+    // methods.
     ctx.obsolete_methods.insert(&m);
     // TODO Allow this or check in IsModifiableClass.
     DCHECK(!m.IsIntrinsic());
@@ -466,36 +534,6 @@
     art::ThreadList* list = art::Runtime::Current()->GetThreadList();
     list->ForEach(DoAllocateObsoleteMethodsCallback, static_cast<void*>(&ctx));
   }
-  FillObsoleteMethodMap(art_klass, ctx.obsolete_map);
-}
-
-// Fills the obsolete method map in the art_klass's extData. This is so obsolete methods are able to
-// figure out their DexCaches.
-void Redefiner::ClassRedefinition::FillObsoleteMethodMap(
-    art::mirror::Class* art_klass,
-    const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes) {
-  int32_t index = 0;
-  art::mirror::ClassExt* ext_data = art_klass->GetExtData();
-  art::mirror::PointerArray* obsolete_methods = ext_data->GetObsoleteMethods();
-  art::mirror::ObjectArray<art::mirror::DexCache>* obsolete_dex_caches =
-      ext_data->GetObsoleteDexCaches();
-  int32_t num_method_slots = obsolete_methods->GetLength();
-  // Find the first empty index.
-  for (; index < num_method_slots; index++) {
-    if (obsolete_methods->GetElementPtrSize<art::ArtMethod*>(
-          index, art::kRuntimePointerSize) == nullptr) {
-      break;
-    }
-  }
-  // Make sure we have enough space.
-  CHECK_GT(num_method_slots, static_cast<int32_t>(obsoletes.size() + index));
-  CHECK(obsolete_dex_caches->Get(index) == nullptr);
-  // Fill in the map.
-  for (auto& obs : obsoletes) {
-    obsolete_methods->SetElementPtrSize(index, obs.second, art::kRuntimePointerSize);
-    obsolete_dex_caches->Set(index, art_klass->GetDexCache());
-    index++;
-  }
 }
 
 // Try and get the declared method. First try to get a virtual method then a direct method if that's
@@ -937,7 +975,7 @@
     art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(
         ClassLoaderHelper::FindSourceDexFileObject(driver_->self_, loader)));
     holder->SetJavaDexFile(klass_index, dex_file_obj.Get());
-    if (dex_file_obj.Get() == nullptr) {
+    if (dex_file_obj == nullptr) {
       // TODO Better error msg.
       RecordFailure(ERR(INTERNAL), "Unable to find dex file!");
       return false;
@@ -1210,13 +1248,13 @@
   art::StackHandleScope<2> hs(driver_->self_);
   art::Handle<art::mirror::Class> klass(hs.NewHandle(
       driver_->self_->DecodeJObject(klass_)->AsClass()));
-  if (klass.Get() == nullptr) {
+  if (klass == nullptr) {
     RecordFailure(ERR(INVALID_CLASS), "Unable to decode class argument!");
     return false;
   }
   // Allocate the classExt
   art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->EnsureExtDataPresent(driver_->self_)));
-  if (ext.Get() == nullptr) {
+  if (ext == nullptr) {
     // No memory. Clear exception (it's not useful) and return error.
     // TODO This doesn't need to be fatal. We could just not support obsolete methods after hitting
     // this case.
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index c441377..65ee291 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -155,12 +155,6 @@
     void FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass)
         REQUIRES(art::Locks::mutator_lock_);
 
-    void FillObsoleteMethodMap(
-        art::mirror::Class* art_klass,
-        const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes)
-          REQUIRES(art::Locks::mutator_lock_);
-
-
     // Checks that the dex file contains only the single expected class and that the top-level class
     // data has not been modified in an incompatible manner.
     bool CheckClass() REQUIRES_SHARED(art::Locks::mutator_lock_);
diff --git a/runtime/openjdkjvmti/ti_stack.cc b/runtime/openjdkjvmti/ti_stack.cc
index b5a6c6e..067c7c1 100644
--- a/runtime/openjdkjvmti/ti_stack.cc
+++ b/runtime/openjdkjvmti/ti_stack.cc
@@ -328,7 +328,7 @@
 
     // For the time being, set the thread to null. We don't have good ScopedLocalRef
     // infrastructure.
-    DCHECK(self->GetPeer() != nullptr);
+    DCHECK(self->GetPeerFromOtherThread() != nullptr);
     stack_info.thread = nullptr;
     stack_info.state = JVMTI_THREAD_STATE_SUSPENDED;
 
@@ -495,7 +495,7 @@
 
     // For the time being, set the thread to null. We don't have good ScopedLocalRef
     // infrastructure.
-    DCHECK(self->GetPeer() != nullptr);
+    DCHECK(self->GetPeerFromOtherThread() != nullptr);
     stack_info.thread = nullptr;
     stack_info.state = JVMTI_THREAD_STATE_SUSPENDED;
 
diff --git a/runtime/openjdkjvmti/ti_threadgroup.cc b/runtime/openjdkjvmti/ti_threadgroup.cc
index e63ce65..1423874 100644
--- a/runtime/openjdkjvmti/ti_threadgroup.cc
+++ b/runtime/openjdkjvmti/ti_threadgroup.cc
@@ -155,7 +155,7 @@
 static bool IsInDesiredThreadGroup(art::Handle<art::mirror::Object> desired_thread_group,
                                    art::ObjPtr<art::mirror::Object> peer)
     REQUIRES_SHARED(art::Locks::mutator_lock_) {
-  CHECK(desired_thread_group.Get() != nullptr);
+  CHECK(desired_thread_group != nullptr);
 
   art::ArtField* thread_group_field =
       art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group);
@@ -167,7 +167,7 @@
 static void GetThreads(art::Handle<art::mirror::Object> thread_group,
                        std::vector<art::ObjPtr<art::mirror::Object>>* thread_peers)
     REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!art::Locks::thread_list_lock_) {
-  CHECK(thread_group.Get() != nullptr);
+  CHECK(thread_group != nullptr);
 
   art::MutexLock mu(art::Thread::Current(), *art::Locks::thread_list_lock_);
   for (art::Thread* t : art::Runtime::Current()->GetThreadList()->GetList()) {
@@ -187,7 +187,7 @@
 static void GetChildThreadGroups(art::Handle<art::mirror::Object> thread_group,
                                  std::vector<art::ObjPtr<art::mirror::Object>>* thread_groups)
     REQUIRES_SHARED(art::Locks::mutator_lock_) {
-  CHECK(thread_group.Get() != nullptr);
+  CHECK(thread_group != nullptr);
 
   // Get the ThreadGroup[] "groups" out of this thread group...
   art::ArtField* groups_field =
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index 1292a81..5748475 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -114,8 +114,8 @@
       class_linker_->FindClass(soa.Self(), "LInterfaces$I;", class_loader)));
   Handle<mirror::Class> J(hs.NewHandle(
       class_linker_->FindClass(soa.Self(), "LInterfaces$J;", class_loader)));
-  ASSERT_TRUE(I.Get() != nullptr);
-  ASSERT_TRUE(J.Get() != nullptr);
+  ASSERT_TRUE(I != nullptr);
+  ASSERT_TRUE(J != nullptr);
 
   std::vector<mirror::Class*> interfaces;
   interfaces.push_back(I.Get());
@@ -123,7 +123,7 @@
   Handle<mirror::Class> proxy_class(hs.NewHandle(
       GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces)));
   interfaces.clear();  // Don't least possibly stale objects in the array as good practice.
-  ASSERT_TRUE(proxy_class.Get() != nullptr);
+  ASSERT_TRUE(proxy_class != nullptr);
   ASSERT_TRUE(proxy_class->IsProxyClass());
   ASSERT_TRUE(proxy_class->IsInitialized());
 
@@ -148,8 +148,8 @@
       class_linker_->FindClass(soa.Self(), "LInterfaces$I;", class_loader)));
   Handle<mirror::Class> J(hs.NewHandle(
       class_linker_->FindClass(soa.Self(), "LInterfaces$J;", class_loader)));
-  ASSERT_TRUE(I.Get() != nullptr);
-  ASSERT_TRUE(J.Get() != nullptr);
+  ASSERT_TRUE(I != nullptr);
+  ASSERT_TRUE(J != nullptr);
 
   Handle<mirror::Class> proxyClass;
   {
@@ -159,7 +159,7 @@
     proxyClass = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces));
   }
 
-  ASSERT_TRUE(proxyClass.Get() != nullptr);
+  ASSERT_TRUE(proxyClass != nullptr);
   ASSERT_TRUE(proxyClass->IsProxyClass());
   ASSERT_TRUE(proxyClass->IsInitialized());
 
@@ -171,10 +171,10 @@
 
   Handle<mirror::Class> interfacesFieldClass(
       hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Class;")));
-  ASSERT_TRUE(interfacesFieldClass.Get() != nullptr);
+  ASSERT_TRUE(interfacesFieldClass != nullptr);
   Handle<mirror::Class> throwsFieldClass(
       hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Class;")));
-  ASSERT_TRUE(throwsFieldClass.Get() != nullptr);
+  ASSERT_TRUE(throwsFieldClass != nullptr);
 
   // Test "Class[] interfaces" field.
   ArtField* field = &static_fields->At(0);
@@ -208,10 +208,10 @@
     proxyClass1 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1", interfaces));
   }
 
-  ASSERT_TRUE(proxyClass0.Get() != nullptr);
+  ASSERT_TRUE(proxyClass0 != nullptr);
   ASSERT_TRUE(proxyClass0->IsProxyClass());
   ASSERT_TRUE(proxyClass0->IsInitialized());
-  ASSERT_TRUE(proxyClass1.Get() != nullptr);
+  ASSERT_TRUE(proxyClass1 != nullptr);
   ASSERT_TRUE(proxyClass1->IsProxyClass());
   ASSERT_TRUE(proxyClass1->IsInitialized());
 
diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc
index 9523e92..4ccfb6d 100644
--- a/runtime/reference_table_test.cc
+++ b/runtime/reference_table_test.cc
@@ -48,12 +48,12 @@
       class_linker->FindClass(self,
                               "Ljava/lang/ref/WeakReference;",
                               ScopedNullHandle<mirror::ClassLoader>())));
-  CHECK(h_ref_class.Get() != nullptr);
+  CHECK(h_ref_class != nullptr);
   CHECK(class_linker->EnsureInitialized(self, h_ref_class, true, true));
 
   Handle<mirror::Object> h_ref_instance(scope.NewHandle<mirror::Object>(
       h_ref_class->AllocObject(self)));
-  CHECK(h_ref_instance.Get() != nullptr);
+  CHECK(h_ref_instance != nullptr);
 
   ArtMethod* constructor = h_ref_class->FindDeclaredDirectMethod(
       "<init>", "(Ljava/lang/Object;)V", class_linker->GetImagePointerSize());
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index a2b4cb3..3c64d40 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -231,8 +231,8 @@
         hs.NewHandle<mirror::ObjectArray<mirror::Object>>(raw_args));
     for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {
       arg.Assign(args->Get(args_offset));
-      if (((shorty_[i] == 'L') && (arg.Get() != nullptr)) ||
-          ((arg.Get() == nullptr && shorty_[i] != 'L'))) {
+      if (((shorty_[i] == 'L') && (arg != nullptr)) ||
+          ((arg == nullptr && shorty_[i] != 'L'))) {
         // TODO: The method's parameter's type must have been previously resolved, yet
         // we've seen cases where it's not b/34440020.
         ObjPtr<mirror::Class> dst_class(
@@ -242,7 +242,7 @@
           CHECK(self->IsExceptionPending());
           return false;
         }
-        if (UNLIKELY(arg.Get() == nullptr || !arg->InstanceOf(dst_class))) {
+        if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) {
           ThrowIllegalArgumentException(
               StringPrintf("method %s argument %zd has type %s, got %s",
                   m->PrettyMethod(false).c_str(),
@@ -254,13 +254,13 @@
       }
 
 #define DO_FIRST_ARG(match_descriptor, get_fn, append) { \
-          if (LIKELY(arg.Get() != nullptr && \
+          if (LIKELY(arg != nullptr && \
               arg->GetClass()->DescriptorEquals(match_descriptor))) { \
             ArtField* primitive_field = arg->GetClass()->GetInstanceField(0); \
             append(primitive_field-> get_fn(arg.Get()));
 
 #define DO_ARG(match_descriptor, get_fn, append) \
-          } else if (LIKELY(arg.Get() != nullptr && \
+          } else if (LIKELY(arg != nullptr && \
                             arg->GetClass<>()->DescriptorEquals(match_descriptor))) { \
             ArtField* primitive_field = arg->GetClass()->GetInstanceField(0); \
             append(primitive_field-> get_fn(arg.Get()));
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 9609bee..f8f3d76 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -95,6 +95,7 @@
 #include "mirror/field.h"
 #include "mirror/method.h"
 #include "mirror/method_handle_impl.h"
+#include "mirror/method_handles_lookup.h"
 #include "mirror/method_type.h"
 #include "mirror/stack_trace_element.h"
 #include "mirror/throwable.h"
@@ -1715,6 +1716,7 @@
   mirror::Field::VisitRoots(visitor);
   mirror::MethodType::VisitRoots(visitor);
   mirror::MethodHandleImpl::VisitRoots(visitor);
+  mirror::MethodHandlesLookup::VisitRoots(visitor);
   mirror::EmulatedStackFrame::VisitRoots(visitor);
   mirror::ClassExt::VisitRoots(visitor);
   // Visit all the primitive array types classes.
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 30b1756..4a0169d 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -662,6 +662,8 @@
 
   RuntimeCallbacks* GetRuntimeCallbacks();
 
+  void InitThreadGroups(Thread* self);
+
  private:
   static void InitPlatformSignalHandlers();
 
@@ -672,7 +674,6 @@
   bool Init(RuntimeArgumentMap&& runtime_options)
       SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_);
   void InitNativeMethods() REQUIRES(!Locks::mutator_lock_);
-  void InitThreadGroups(Thread* self);
   void RegisterRuntimeNativeMethods(JNIEnv* env);
 
   void StartDaemonThreads();
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index f1e78b4..abe99e0 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -294,7 +294,7 @@
   const char* descriptor_y = "LY;";
   Handle<mirror::Class> h_Y(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), descriptor_y, class_loader)));
-  ASSERT_TRUE(h_Y.Get() != nullptr);
+  ASSERT_TRUE(h_Y != nullptr);
 
   bool expect1 = Expect({ "PreDefine:LY; <art-gtest-XandY.jar>",
                           "PreDefine:LX; <art-gtest-XandY.jar>",
diff --git a/runtime/stack.cc b/runtime/stack.cc
index d7ba1d7..51a24e4 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -874,9 +874,13 @@
               CHECK_EQ(GetMethod(), callee) << "Expected: " << ArtMethod::PrettyMethod(callee)
                                             << " Found: " << ArtMethod::PrettyMethod(GetMethod());
             } else {
-              CHECK_EQ(instrumentation_frame.method_, GetMethod())
-                  << "Expected: " << ArtMethod::PrettyMethod(instrumentation_frame.method_)
-                  << " Found: " << ArtMethod::PrettyMethod(GetMethod());
+              // Instrumentation generally doesn't distinguish between a method's obsolete and
+              // non-obsolete version.
+              CHECK_EQ(instrumentation_frame.method_->GetNonObsoleteMethod(),
+                       GetMethod()->GetNonObsoleteMethod())
+                  << "Expected: "
+                  << ArtMethod::PrettyMethod(instrumentation_frame.method_->GetNonObsoleteMethod())
+                  << " Found: " << ArtMethod::PrettyMethod(GetMethod()->GetNonObsoleteMethod());
             }
             if (num_frames_ != 0) {
               // Check agreement of frame Ids only if num_frames_ is computed to avoid infinite
@@ -903,7 +907,7 @@
               << " native=" << method->IsNative()
               << std::noboolalpha
               << " entrypoints=" << method->GetEntryPointFromQuickCompiledCode()
-              << "," << method->GetEntryPointFromJni()
+              << "," << (method->IsNative() ? method->GetEntryPointFromJni() : nullptr)
               << " next=" << *cur_quick_frame_;
         }
 
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 61d6a58..67f0b57 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -747,6 +747,7 @@
     return total_bit_size_;
   }
 
+  // Encode the encoding into the vector.
   template<typename Vector>
   void Encode(Vector* dest) const {
     static_assert(alignof(StackMapEncoding) == 1, "Should not require alignment");
@@ -754,6 +755,7 @@
     dest->insert(dest->end(), ptr, ptr + sizeof(*this));
   }
 
+  // Decode the encoding from a pointer, updates the pointer.
   void Decode(const uint8_t** ptr) {
     *this = *reinterpret_cast<const StackMapEncoding*>(*ptr);
     *ptr += sizeof(*this);
@@ -924,6 +926,7 @@
 
   void Dump(VariableIndentationOutputStream* vios) const;
 
+  // Encode the encoding into the vector.
   template<typename Vector>
   void Encode(Vector* dest) const {
     static_assert(alignof(InlineInfoEncoding) == 1, "Should not require alignment");
@@ -931,6 +934,7 @@
     dest->insert(dest->end(), ptr, ptr + sizeof(*this));
   }
 
+  // Decode the encoding from a pointer, updates the pointer.
   void Decode(const uint8_t** ptr) {
     *this = *reinterpret_cast<const InlineInfoEncoding*>(*ptr);
     *ptr += sizeof(*this);
@@ -1141,6 +1145,94 @@
   }
 };
 
+// Format is [native pc, invoke type, method index].
+class InvokeInfoEncoding {
+ public:
+  void SetFromSizes(size_t native_pc_max,
+                    size_t invoke_type_max,
+                    size_t method_index_max) {
+    total_bit_size_ = 0;
+    DCHECK_EQ(kNativePcBitOffset, total_bit_size_);
+    total_bit_size_ += MinimumBitsToStore(native_pc_max);
+    invoke_type_bit_offset_ = total_bit_size_;
+    total_bit_size_ += MinimumBitsToStore(invoke_type_max);
+    method_index_bit_offset_ = total_bit_size_;
+    total_bit_size_ += MinimumBitsToStore(method_index_max);
+  }
+
+  ALWAYS_INLINE FieldEncoding GetNativePcEncoding() const {
+    return FieldEncoding(kNativePcBitOffset, invoke_type_bit_offset_);
+  }
+
+  ALWAYS_INLINE FieldEncoding GetInvokeTypeEncoding() const {
+    return FieldEncoding(invoke_type_bit_offset_, method_index_bit_offset_);
+  }
+
+  ALWAYS_INLINE FieldEncoding GetMethodIndexEncoding() const {
+    return FieldEncoding(method_index_bit_offset_, total_bit_size_);
+  }
+
+  ALWAYS_INLINE size_t BitSize() const {
+    return total_bit_size_;
+  }
+
+  template<typename Vector>
+  void Encode(Vector* dest) const {
+    static_assert(alignof(InvokeInfoEncoding) == 1, "Should not require alignment");
+    const uint8_t* ptr = reinterpret_cast<const uint8_t*>(this);
+    dest->insert(dest->end(), ptr, ptr + sizeof(*this));
+  }
+
+  void Decode(const uint8_t** ptr) {
+    *this = *reinterpret_cast<const InvokeInfoEncoding*>(*ptr);
+    *ptr += sizeof(*this);
+  }
+
+ private:
+  static constexpr uint8_t kNativePcBitOffset = 0;
+  uint8_t invoke_type_bit_offset_;
+  uint8_t method_index_bit_offset_;
+  uint8_t total_bit_size_;
+};
+
+class InvokeInfo {
+ public:
+  explicit InvokeInfo(BitMemoryRegion region) : region_(region) {}
+
+  ALWAYS_INLINE uint32_t GetNativePcOffset(const InvokeInfoEncoding& encoding,
+                                           InstructionSet instruction_set) const {
+    CodeOffset offset(
+        CodeOffset::FromCompressedOffset(encoding.GetNativePcEncoding().Load(region_)));
+    return offset.Uint32Value(instruction_set);
+  }
+
+  ALWAYS_INLINE void SetNativePcCodeOffset(const InvokeInfoEncoding& encoding,
+                                           CodeOffset native_pc_offset) {
+    encoding.GetNativePcEncoding().Store(region_, native_pc_offset.CompressedValue());
+  }
+
+  ALWAYS_INLINE uint32_t GetInvokeType(const InvokeInfoEncoding& encoding) const {
+    return encoding.GetInvokeTypeEncoding().Load(region_);
+  }
+
+  ALWAYS_INLINE void SetInvokeType(const InvokeInfoEncoding& encoding, uint32_t invoke_type) {
+    encoding.GetInvokeTypeEncoding().Store(region_, invoke_type);
+  }
+
+  ALWAYS_INLINE uint32_t GetMethodIndex(const InvokeInfoEncoding& encoding) const {
+    return encoding.GetMethodIndexEncoding().Load(region_);
+  }
+
+  ALWAYS_INLINE void SetMethodIndex(const InvokeInfoEncoding& encoding, uint32_t method_index) {
+    encoding.GetMethodIndexEncoding().Store(region_, method_index);
+  }
+
+  bool IsValid() const { return region_.pointer() != nullptr; }
+
+ private:
+  BitMemoryRegion region_;
+};
+
 // Most of the fields are encoded as ULEB128 to save space.
 struct CodeInfoEncoding {
   static constexpr uint32_t kInvalidSize = static_cast<size_t>(-1);
@@ -1150,6 +1242,7 @@
   BitEncodingTable<StackMapEncoding> stack_map;
   BitEncodingTable<BitRegionEncoding> register_mask;
   BitEncodingTable<BitRegionEncoding> stack_mask;
+  BitEncodingTable<InvokeInfoEncoding> invoke_info;
   BitEncodingTable<InlineInfoEncoding> inline_info;
 
   CodeInfoEncoding() {}
@@ -1161,6 +1254,7 @@
     stack_map.Decode(&ptr);
     register_mask.Decode(&ptr);
     stack_mask.Decode(&ptr);
+    invoke_info.Decode(&ptr);
     if (stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) {
       inline_info.Decode(&ptr);
     } else {
@@ -1171,6 +1265,7 @@
     ComputeTableOffsets();
   }
 
+  // Compress is not const since it calculates cache_header_size. This is used by PrepareForFillIn.
   template<typename Vector>
   void Compress(Vector* dest) {
     dex_register_map.Encode(dest);
@@ -1178,6 +1273,7 @@
     stack_map.Encode(dest);
     register_mask.Encode(dest);
     stack_mask.Encode(dest);
+    invoke_info.Encode(dest);
     if (stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) {
       inline_info.Encode(dest);
     }
@@ -1194,6 +1290,7 @@
     stack_map.UpdateBitOffset(&bit_offset);
     register_mask.UpdateBitOffset(&bit_offset);
     stack_mask.UpdateBitOffset(&bit_offset);
+    invoke_info.UpdateBitOffset(&bit_offset);
     inline_info.UpdateBitOffset(&bit_offset);
     cache_non_header_size = RoundUp(bit_offset, kBitsPerByte) / kBitsPerByte - HeaderSize();
   }
@@ -1210,9 +1307,9 @@
 
  private:
   // Computed fields (not serialized).
-  // Header size in bytes.
+  // Header size in bytes, cached to avoid needing to re-decoding the encoding in HeaderSize.
   uint32_t cache_header_size = kInvalidSize;
-  // Non header size in bytes.
+  // Non header size in bytes, cached to avoid needing to re-decoding the encoding in NonHeaderSize.
   uint32_t cache_non_header_size = kInvalidSize;
 };
 
@@ -1221,7 +1318,13 @@
  * The information is of the form:
  *
  *   [CodeInfoEncoding, DexRegisterMap+, DexLocationCatalog+, StackMap+, RegisterMask+, StackMask+,
- *    DexRegisterMap+, InlineInfo*]
+ *    InlineInfo*]
+ *
+ * where CodeInfoEncoding is of the form:
+ *
+ *   [ByteSizedTable(dex_register_map), ByteSizedTable(location_catalog),
+ *    BitEncodingTable<StackMapEncoding>, BitEncodingTable<BitRegionEncoding>,
+ *    BitEncodingTable<BitRegionEncoding>, BitEncodingTable<InlineInfoEncoding>]
  */
 class CodeInfo {
  public:
@@ -1292,6 +1395,10 @@
     return encoding.stack_map.encoding.BitSize() * GetNumberOfStackMaps(encoding);
   }
 
+  InvokeInfo GetInvokeInfo(const CodeInfoEncoding& encoding, size_t index) const {
+    return InvokeInfo(encoding.invoke_info.BitRegion(region_, index));
+  }
+
   DexRegisterMap GetDexRegisterMapOf(StackMap stack_map,
                                      const CodeInfoEncoding& encoding,
                                      size_t number_of_dex_registers) const {
@@ -1331,7 +1438,9 @@
   }
 
   InlineInfo GetInlineInfo(size_t index, const CodeInfoEncoding& encoding) const {
-    // Since we do not know the depth, we just return the whole remaining map.
+    // Since we do not know the depth, we just return the whole remaining map. The caller may
+    // access the inline info for arbitrary depths. To return the precise inline info we would need
+    // to count the depth before returning.
     // TODO: Clean this up.
     const size_t bit_offset = encoding.inline_info.bit_offset +
         index * encoding.inline_info.encoding.BitSize();
@@ -1413,6 +1522,17 @@
     return StackMap();
   }
 
+  InvokeInfo GetInvokeInfoForNativePcOffset(uint32_t native_pc_offset,
+                                            const CodeInfoEncoding& encoding) {
+    for (size_t index = 0; index < encoding.invoke_info.num_entries; index++) {
+      InvokeInfo item = GetInvokeInfo(encoding, index);
+      if (item.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA) == native_pc_offset) {
+        return item;
+      }
+    }
+    return InvokeInfo(BitMemoryRegion());
+  }
+
   // Dump this CodeInfo object on `os`.  `code_offset` is the (absolute)
   // native PC of the compiled method and `number_of_dex_registers` the
   // number of Dex virtual registers used in this method.  If
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 7b65404..3abb9fc 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -874,35 +874,100 @@
   ScopedObjectAccess soa(self);
   StackHandleScope<1> hs(self);
   MutableHandle<mirror::String> peer_thread_name(hs.NewHandle(GetThreadName()));
-  if (peer_thread_name.Get() == nullptr) {
+  if (peer_thread_name == nullptr) {
     // The Thread constructor should have set the Thread.name to a
     // non-null value. However, because we can run without code
     // available (in the compiler, in tests), we manually assign the
     // fields the constructor should have set.
     if (runtime->IsActiveTransaction()) {
-      InitPeer<true>(soa, thread_is_daemon, thread_group, thread_name.get(), thread_priority);
+      InitPeer<true>(soa,
+                     tlsPtr_.opeer,
+                     thread_is_daemon,
+                     thread_group,
+                     thread_name.get(),
+                     thread_priority);
     } else {
-      InitPeer<false>(soa, thread_is_daemon, thread_group, thread_name.get(), thread_priority);
+      InitPeer<false>(soa,
+                      tlsPtr_.opeer,
+                      thread_is_daemon,
+                      thread_group,
+                      thread_name.get(),
+                      thread_priority);
     }
     peer_thread_name.Assign(GetThreadName());
   }
   // 'thread_name' may have been null, so don't trust 'peer_thread_name' to be non-null.
-  if (peer_thread_name.Get() != nullptr) {
+  if (peer_thread_name != nullptr) {
     SetThreadName(peer_thread_name->ToModifiedUtf8().c_str());
   }
 }
 
+jobject Thread::CreateCompileTimePeer(JNIEnv* env,
+                                      const char* name,
+                                      bool as_daemon,
+                                      jobject thread_group) {
+  Runtime* runtime = Runtime::Current();
+  CHECK(!runtime->IsStarted());
+
+  if (thread_group == nullptr) {
+    thread_group = runtime->GetMainThreadGroup();
+  }
+  ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF(name));
+  // Add missing null check in case of OOM b/18297817
+  if (name != nullptr && thread_name.get() == nullptr) {
+    CHECK(Thread::Current()->IsExceptionPending());
+    return nullptr;
+  }
+  jint thread_priority = GetNativePriority();
+  jboolean thread_is_daemon = as_daemon;
+
+  ScopedLocalRef<jobject> peer(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
+  if (peer.get() == nullptr) {
+    CHECK(Thread::Current()->IsExceptionPending());
+    return nullptr;
+  }
+
+  // We cannot call Thread.init, as it will recursively ask for currentThread.
+
+  // The Thread constructor should have set the Thread.name to a
+  // non-null value. However, because we can run without code
+  // available (in the compiler, in tests), we manually assign the
+  // fields the constructor should have set.
+  ScopedObjectAccessUnchecked soa(Thread::Current());
+  if (runtime->IsActiveTransaction()) {
+    InitPeer<true>(soa,
+                   soa.Decode<mirror::Object>(peer.get()),
+                   thread_is_daemon,
+                   thread_group,
+                   thread_name.get(),
+                   thread_priority);
+  } else {
+    InitPeer<false>(soa,
+                    soa.Decode<mirror::Object>(peer.get()),
+                    thread_is_daemon,
+                    thread_group,
+                    thread_name.get(),
+                    thread_priority);
+  }
+
+  return peer.release();
+}
+
 template<bool kTransactionActive>
-void Thread::InitPeer(ScopedObjectAccess& soa, jboolean thread_is_daemon, jobject thread_group,
-                      jobject thread_name, jint thread_priority) {
+void Thread::InitPeer(ScopedObjectAccessAlreadyRunnable& soa,
+                      ObjPtr<mirror::Object> peer,
+                      jboolean thread_is_daemon,
+                      jobject thread_group,
+                      jobject thread_name,
+                      jint thread_priority) {
   jni::DecodeArtField(WellKnownClasses::java_lang_Thread_daemon)->
-      SetBoolean<kTransactionActive>(tlsPtr_.opeer, thread_is_daemon);
+      SetBoolean<kTransactionActive>(peer, thread_is_daemon);
   jni::DecodeArtField(WellKnownClasses::java_lang_Thread_group)->
-      SetObject<kTransactionActive>(tlsPtr_.opeer, soa.Decode<mirror::Object>(thread_group));
+      SetObject<kTransactionActive>(peer, soa.Decode<mirror::Object>(thread_group));
   jni::DecodeArtField(WellKnownClasses::java_lang_Thread_name)->
-      SetObject<kTransactionActive>(tlsPtr_.opeer, soa.Decode<mirror::Object>(thread_name));
+      SetObject<kTransactionActive>(peer, soa.Decode<mirror::Object>(thread_name));
   jni::DecodeArtField(WellKnownClasses::java_lang_Thread_priority)->
-      SetInt<kTransactionActive>(tlsPtr_.opeer, thread_priority);
+      SetInt<kTransactionActive>(peer, thread_priority);
 }
 
 void Thread::SetThreadName(const char* name) {
@@ -2284,7 +2349,7 @@
     Handle<mirror::ObjectArray<mirror::Object>> trace(
         hs.NewHandle(
             mirror::ObjectArray<mirror::Object>::Alloc(hs.Self(), array_class, depth + 1)));
-    if (trace.Get() == nullptr) {
+    if (trace == nullptr) {
       // Acquire uninterruptible_ in all paths.
       self_->StartAssertNoThreadSuspension("Building internal stack trace");
       self_->AssertPendingOOMException();
@@ -2479,14 +2544,14 @@
       std::string class_name(PrettyDescriptor(descriptor));
       class_name_object.Assign(
           mirror::String::AllocFromModifiedUtf8(soa.Self(), class_name.c_str()));
-      if (class_name_object.Get() == nullptr) {
+      if (class_name_object == nullptr) {
         soa.Self()->AssertPendingOOMException();
         return nullptr;
       }
       const char* source_file = method->GetDeclaringClassSourceFile();
       if (source_file != nullptr) {
         source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file));
-        if (source_name_object.Get() == nullptr) {
+        if (source_name_object == nullptr) {
           soa.Self()->AssertPendingOOMException();
           return nullptr;
         }
@@ -2496,7 +2561,7 @@
     CHECK(method_name != nullptr);
     Handle<mirror::String> method_name_object(
         hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), method_name)));
-    if (method_name_object.Get() == nullptr) {
+    if (method_name_object == nullptr) {
       return nullptr;
     }
     ObjPtr<mirror::StackTraceElement> obj =mirror::StackTraceElement::Alloc(soa.Self(),
@@ -2554,7 +2619,7 @@
   auto* cl = runtime->GetClassLinker();
   Handle<mirror::Class> exception_class(
       hs.NewHandle(cl->FindClass(this, exception_class_descriptor, class_loader)));
-  if (UNLIKELY(exception_class.Get() == nullptr)) {
+  if (UNLIKELY(exception_class == nullptr)) {
     CHECK(IsExceptionPending());
     LOG(ERROR) << "No exception class " << PrettyDescriptor(exception_class_descriptor);
     return;
@@ -2570,7 +2635,7 @@
       hs.NewHandle(ObjPtr<mirror::Throwable>::DownCast(exception_class->AllocObject(this))));
 
   // If we couldn't allocate the exception, throw the pre-allocated out of memory exception.
-  if (exception.Get() == nullptr) {
+  if (exception == nullptr) {
     SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError());
     return;
   }
@@ -3485,7 +3550,8 @@
 }
 
 mirror::Object* Thread::GetPeerFromOtherThread() const {
-  mirror::Object* peer = GetPeer();
+  DCHECK(tlsPtr_.jpeer == nullptr);
+  mirror::Object* peer = tlsPtr_.opeer;
   if (kUseReadBarrier && Current()->GetIsGcMarking()) {
     // We may call Thread::Dump() in the middle of the CC thread flip and this thread's stack
     // may have not been flipped yet and peer may be a from-space (stale) ref. So explicitly
diff --git a/runtime/thread.h b/runtime/thread.h
index a46e799..d5fd9e9 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -359,6 +359,7 @@
   uint64_t GetCpuMicroTime() const;
 
   mirror::Object* GetPeer() const REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK(Thread::Current() == this) << "Use GetPeerFromOtherThread instead";
     CHECK(tlsPtr_.jpeer == nullptr);
     return tlsPtr_.opeer;
   }
@@ -1173,6 +1174,12 @@
     return false;
   }
 
+  static jobject CreateCompileTimePeer(JNIEnv* env,
+                                       const char* name,
+                                       bool as_daemon,
+                                       jobject thread_group)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
   explicit Thread(bool daemon);
   ~Thread() REQUIRES(!Locks::mutator_lock_, !Locks::thread_suspend_count_lock_);
@@ -1188,8 +1195,12 @@
   void CreatePeer(const char* name, bool as_daemon, jobject thread_group);
 
   template<bool kTransactionActive>
-  void InitPeer(ScopedObjectAccess& soa, jboolean thread_is_daemon, jobject thread_group,
-                jobject thread_name, jint thread_priority)
+  static void InitPeer(ScopedObjectAccessAlreadyRunnable& soa,
+                       ObjPtr<mirror::Object> peer,
+                       jboolean thread_is_daemon,
+                       jobject thread_group,
+                       jobject thread_name,
+                       jint thread_priority)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Avoid use, callers should use SetState. Used only by SignalCatcher::HandleSigQuit, ~Thread and
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index a43c967..97c1228 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -35,7 +35,7 @@
     StackHandleScope<2> hs(soa.Self());
     Handle<mirror::ClassLoader> class_loader(
         hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
-    ASSERT_TRUE(class_loader.Get() != nullptr);
+    ASSERT_TRUE(class_loader != nullptr);
 
     // Load and initialize java.lang.ExceptionInInitializerError and the exception class used
     // to abort transaction so they can be thrown during class initialization if the transaction
@@ -43,26 +43,26 @@
     MutableHandle<mirror::Class> h_klass(
         hs.NewHandle(class_linker_->FindSystemClass(soa.Self(),
                                                     "Ljava/lang/ExceptionInInitializerError;")));
-    ASSERT_TRUE(h_klass.Get() != nullptr);
+    ASSERT_TRUE(h_klass != nullptr);
     class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
     ASSERT_TRUE(h_klass->IsInitialized());
 
     h_klass.Assign(class_linker_->FindSystemClass(soa.Self(),
                                                   Transaction::kAbortExceptionSignature));
-    ASSERT_TRUE(h_klass.Get() != nullptr);
+    ASSERT_TRUE(h_klass != nullptr);
     class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
     ASSERT_TRUE(h_klass->IsInitialized());
 
     // Load and verify utility class.
     h_klass.Assign(class_linker_->FindClass(soa.Self(), "LTransaction$AbortHelperClass;",
                                             class_loader));
-    ASSERT_TRUE(h_klass.Get() != nullptr);
+    ASSERT_TRUE(h_klass != nullptr);
     class_linker_->VerifyClass(soa.Self(), h_klass);
     ASSERT_TRUE(h_klass->IsVerified());
 
     // Load and verify tested class.
     h_klass.Assign(class_linker_->FindClass(soa.Self(), tested_class_signature, class_loader));
-    ASSERT_TRUE(h_klass.Get() != nullptr);
+    ASSERT_TRUE(h_klass != nullptr);
     class_linker_->VerifyClass(soa.Self(), h_klass);
     ASSERT_TRUE(h_klass->IsVerified());
 
@@ -95,12 +95,12 @@
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
-  ASSERT_TRUE(h_klass.Get() != nullptr);
+  ASSERT_TRUE(h_klass != nullptr);
 
   Transaction transaction;
   Runtime::Current()->EnterTransactionMode(&transaction);
   Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self())));
-  ASSERT_TRUE(h_obj.Get() != nullptr);
+  ASSERT_TRUE(h_obj != nullptr);
   ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
   Runtime::Current()->ExitTransactionMode();
 
@@ -115,9 +115,9 @@
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
-  ASSERT_TRUE(h_klass.Get() != nullptr);
+  ASSERT_TRUE(h_klass != nullptr);
   Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self())));
-  ASSERT_TRUE(h_obj.Get() != nullptr);
+  ASSERT_TRUE(h_obj != nullptr);
   ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
 
   // Lock object's monitor outside the transaction.
@@ -144,7 +144,7 @@
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")));
-  ASSERT_TRUE(h_klass.Get() != nullptr);
+  ASSERT_TRUE(h_klass != nullptr);
 
   constexpr int32_t kArraySize = 2;
 
@@ -157,7 +157,7 @@
           mirror::Array::Alloc<true>(soa.Self(), h_klass.Get(), kArraySize,
                                      h_klass->GetComponentSizeShift(),
                                      Runtime::Current()->GetHeap()->GetCurrentAllocator())));
-  ASSERT_TRUE(h_obj.Get() != nullptr);
+  ASSERT_TRUE(h_obj != nullptr);
   ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
   Runtime::Current()->ExitTransactionMode();
 
@@ -172,11 +172,11 @@
   StackHandleScope<4> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
       hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
-  ASSERT_TRUE(class_loader.Get() != nullptr);
+  ASSERT_TRUE(class_loader != nullptr);
 
   Handle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStaticFieldsTest;", class_loader)));
-  ASSERT_TRUE(h_klass.Get() != nullptr);
+  ASSERT_TRUE(h_klass != nullptr);
   bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
   ASSERT_TRUE(success);
   ASSERT_TRUE(h_klass->IsInitialized());
@@ -232,9 +232,9 @@
   // Create a java.lang.Object instance to set objectField.
   Handle<mirror::Class> object_klass(
       hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
-  ASSERT_TRUE(object_klass.Get() != nullptr);
+  ASSERT_TRUE(object_klass != nullptr);
   Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self())));
-  ASSERT_TRUE(h_obj.Get() != nullptr);
+  ASSERT_TRUE(h_obj != nullptr);
   ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
 
   // Modify fields inside transaction then rollback changes.
@@ -270,11 +270,11 @@
   StackHandleScope<5> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
       hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
-  ASSERT_TRUE(class_loader.Get() != nullptr);
+  ASSERT_TRUE(class_loader != nullptr);
 
   Handle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LInstanceFieldsTest;", class_loader)));
-  ASSERT_TRUE(h_klass.Get() != nullptr);
+  ASSERT_TRUE(h_klass != nullptr);
   bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
   ASSERT_TRUE(success);
   ASSERT_TRUE(h_klass->IsInitialized());
@@ -282,7 +282,7 @@
 
   // Allocate an InstanceFieldTest object.
   Handle<mirror::Object> h_instance(hs.NewHandle(h_klass->AllocObject(soa.Self())));
-  ASSERT_TRUE(h_instance.Get() != nullptr);
+  ASSERT_TRUE(h_instance != nullptr);
 
   // Lookup fields.
   ArtField* booleanField = h_klass->FindDeclaredInstanceField("booleanField", "Z");
@@ -334,9 +334,9 @@
   // Create a java.lang.Object instance to set objectField.
   Handle<mirror::Class> object_klass(
       hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
-  ASSERT_TRUE(object_klass.Get() != nullptr);
+  ASSERT_TRUE(object_klass != nullptr);
   Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self())));
-  ASSERT_TRUE(h_obj.Get() != nullptr);
+  ASSERT_TRUE(h_obj != nullptr);
   ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
 
   // Modify fields inside transaction then rollback changes.
@@ -372,11 +372,11 @@
   StackHandleScope<4> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
       hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
-  ASSERT_TRUE(class_loader.Get() != nullptr);
+  ASSERT_TRUE(class_loader != nullptr);
 
   Handle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStaticArrayFieldsTest;", class_loader)));
-  ASSERT_TRUE(h_klass.Get() != nullptr);
+  ASSERT_TRUE(h_klass != nullptr);
   bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
   ASSERT_TRUE(success);
   ASSERT_TRUE(h_klass->IsInitialized());
@@ -451,9 +451,9 @@
   // Create a java.lang.Object instance to set objectField.
   Handle<mirror::Class> object_klass(
       hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
-  ASSERT_TRUE(object_klass.Get() != nullptr);
+  ASSERT_TRUE(object_klass != nullptr);
   Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self())));
-  ASSERT_TRUE(h_obj.Get() != nullptr);
+  ASSERT_TRUE(h_obj != nullptr);
   ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
 
   // Modify fields inside transaction then rollback changes.
@@ -489,15 +489,15 @@
   StackHandleScope<3> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
       hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
-  ASSERT_TRUE(class_loader.Get() != nullptr);
+  ASSERT_TRUE(class_loader != nullptr);
 
   Handle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$ResolveString;",
                                             class_loader)));
-  ASSERT_TRUE(h_klass.Get() != nullptr);
+  ASSERT_TRUE(h_klass != nullptr);
 
   Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(h_klass->GetDexCache()));
-  ASSERT_TRUE(h_dex_cache.Get() != nullptr);
+  ASSERT_TRUE(h_dex_cache != nullptr);
   const DexFile* const dex_file = h_dex_cache->GetDexFile();
   ASSERT_TRUE(dex_file != nullptr);
 
@@ -538,12 +538,12 @@
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
       hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
-  ASSERT_TRUE(class_loader.Get() != nullptr);
+  ASSERT_TRUE(class_loader != nullptr);
 
   Handle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$EmptyStatic;",
                                             class_loader)));
-  ASSERT_TRUE(h_klass.Get() != nullptr);
+  ASSERT_TRUE(h_klass != nullptr);
   class_linker_->VerifyClass(soa.Self(), h_klass);
   ASSERT_TRUE(h_klass->IsVerified());
 
@@ -562,12 +562,12 @@
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
       hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
-  ASSERT_TRUE(class_loader.Get() != nullptr);
+  ASSERT_TRUE(class_loader != nullptr);
 
   Handle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$StaticFieldClass;",
                                             class_loader)));
-  ASSERT_TRUE(h_klass.Get() != nullptr);
+  ASSERT_TRUE(h_klass != nullptr);
   class_linker_->VerifyClass(soa.Self(), h_klass);
   ASSERT_TRUE(h_klass->IsVerified());
 
diff --git a/runtime/utils.h b/runtime/utils.h
index 67438b5..96e5bfa 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -301,6 +301,30 @@
   }
 }
 
+// Returns a type cast pointer if object pointed to is within the provided bounds.
+// Otherwise returns nullptr.
+template <typename T>
+inline static T BoundsCheckedCast(const void* pointer,
+                                  const void* lower,
+                                  const void* upper) {
+  const uint8_t* bound_begin = static_cast<const uint8_t*>(lower);
+  const uint8_t* bound_end = static_cast<const uint8_t*>(upper);
+  DCHECK(bound_begin <= bound_end);
+
+  T result = reinterpret_cast<T>(pointer);
+  const uint8_t* begin = static_cast<const uint8_t*>(pointer);
+  const uint8_t* end = begin + sizeof(*result);
+  if (begin < bound_begin || end > bound_end || begin > end) {
+    return nullptr;
+  }
+  return result;
+}
+
+template <typename T, size_t size>
+constexpr size_t ArrayCount(const T (&)[size]) {
+  return size;
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_UTILS_H_
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
index 2812c21..9865821 100644
--- a/runtime/utils/dex_cache_arrays_layout-inl.h
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -29,7 +29,8 @@
 namespace art {
 
 inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size,
-                                                  const DexFile::Header& header)
+                                                  const DexFile::Header& header,
+                                                  uint32_t num_call_sites)
     : pointer_size_(pointer_size),
       /* types_offset_ is always 0u, so it's constexpr */
       methods_offset_(
@@ -40,19 +41,19 @@
           RoundUp(strings_offset_ + StringsSize(header.string_ids_size_), FieldsAlignment())),
       method_types_offset_(
           RoundUp(fields_offset_ + FieldsSize(header.field_ids_size_), MethodTypesAlignment())),
-      size_(
-          RoundUp(method_types_offset_ + MethodTypesSize(header.proto_ids_size_), Alignment())) {
+    call_sites_offset_(
+        RoundUp(method_types_offset_ + MethodTypesSize(header.proto_ids_size_),
+                MethodTypesAlignment())),
+      size_(RoundUp(call_sites_offset_ + CallSitesSize(num_call_sites), Alignment())) {
 }
 
 inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size, const DexFile* dex_file)
-    : DexCacheArraysLayout(pointer_size, dex_file->GetHeader()) {
+    : DexCacheArraysLayout(pointer_size, dex_file->GetHeader(), dex_file->NumCallSiteIds()) {
 }
 
-constexpr size_t DexCacheArraysLayout::Alignment() {
-  // mirror::Type/String/MethodTypeDexCacheType alignment is 8,
-  // i.e. higher than or equal to the pointer alignment.
-  static_assert(alignof(mirror::TypeDexCacheType) == 8,
-                "Expecting alignof(ClassDexCacheType) == 8");
+inline constexpr size_t DexCacheArraysLayout::Alignment() {
+  // GcRoot<> alignment is 4, i.e. lower than or equal to the pointer alignment.
+  static_assert(alignof(GcRoot<mirror::Class>) == 4, "Expecting alignof(GcRoot<>) == 4");
   static_assert(alignof(mirror::StringDexCacheType) == 8,
                 "Expecting alignof(StringDexCacheType) == 8");
   static_assert(alignof(mirror::MethodTypeDexCacheType) == 8,
@@ -62,22 +63,17 @@
 }
 
 template <typename T>
-constexpr PointerSize GcRootAsPointerSize() {
+static constexpr PointerSize GcRootAsPointerSize() {
   static_assert(sizeof(GcRoot<T>) == 4U, "Unexpected GcRoot size");
   return PointerSize::k32;
 }
 
 inline size_t DexCacheArraysLayout::TypeOffset(dex::TypeIndex type_idx) const {
-  return types_offset_ + ElementOffset(PointerSize::k64,
-                                       type_idx.index_ % mirror::DexCache::kDexCacheTypeCacheSize);
+  return types_offset_ + ElementOffset(GcRootAsPointerSize<mirror::Class>(), type_idx.index_);
 }
 
 inline size_t DexCacheArraysLayout::TypesSize(size_t num_elements) const {
-  size_t cache_size = mirror::DexCache::kDexCacheTypeCacheSize;
-  if (num_elements < cache_size) {
-    cache_size = num_elements;
-  }
-  return ArraySize(PointerSize::k64, cache_size);
+  return ArraySize(GcRootAsPointerSize<mirror::Class>(), num_elements);
 }
 
 inline size_t DexCacheArraysLayout::TypesAlignment() const {
@@ -138,10 +134,18 @@
 
 inline size_t DexCacheArraysLayout::MethodTypesAlignment() const {
   static_assert(alignof(mirror::MethodTypeDexCacheType) == 8,
-                "alignof(MethodTypeDexCacheType) != 8");
+                "Expecting alignof(MethodTypeDexCacheType) == 8");
   return alignof(mirror::MethodTypeDexCacheType);
 }
 
+inline size_t DexCacheArraysLayout::CallSitesSize(size_t num_elements) const {
+  return ArraySize(GcRootAsPointerSize<mirror::CallSite>(), num_elements);
+}
+
+inline size_t DexCacheArraysLayout::CallSitesAlignment() const {
+  return alignof(GcRoot<mirror::CallSite>);
+}
+
 inline size_t DexCacheArraysLayout::ElementOffset(PointerSize element_size, uint32_t idx) {
   return static_cast<size_t>(element_size) * idx;
 }
diff --git a/runtime/utils/dex_cache_arrays_layout.h b/runtime/utils/dex_cache_arrays_layout.h
index 7d4b23a..ed677ed 100644
--- a/runtime/utils/dex_cache_arrays_layout.h
+++ b/runtime/utils/dex_cache_arrays_layout.h
@@ -37,11 +37,14 @@
         strings_offset_(0u),
         fields_offset_(0u),
         method_types_offset_(0u),
+        call_sites_offset_(0u),
         size_(0u) {
   }
 
   // Construct a layout for a particular dex file header.
-  DexCacheArraysLayout(PointerSize pointer_size, const DexFile::Header& header);
+  DexCacheArraysLayout(PointerSize pointer_size,
+                       const DexFile::Header& header,
+                       uint32_t num_call_sites);
 
   // Construct a layout for a particular dex file.
   DexCacheArraysLayout(PointerSize pointer_size, const DexFile* dex_file);
@@ -104,6 +107,14 @@
 
   size_t MethodTypesAlignment() const;
 
+  size_t CallSitesOffset() const {
+    return call_sites_offset_;
+  }
+
+  size_t CallSitesSize(size_t num_elements) const;
+
+  size_t CallSitesAlignment() const;
+
  private:
   static constexpr size_t types_offset_ = 0u;
   const PointerSize pointer_size_;  // Must be first for construction initialization order.
@@ -111,6 +122,7 @@
   const size_t strings_offset_;
   const size_t fields_offset_;
   const size_t method_types_offset_;
+  const size_t call_sites_offset_;
   const size_t size_;
 
   static size_t Alignment(PointerSize pointer_size);
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index 02f1e1b..634bd47 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -408,4 +408,23 @@
       IsValidDescriptor(reinterpret_cast<char*>(&unpaired_surrogate_with_multibyte_sequence[0])));
 }
 
+TEST_F(UtilsTest, ArrayCount) {
+  int i[64];
+  EXPECT_EQ(ArrayCount(i), 64u);
+  char c[7];
+  EXPECT_EQ(ArrayCount(c), 7u);
+}
+
+TEST_F(UtilsTest, BoundsCheckedCast) {
+  char buffer[64];
+  const char* buffer_end = buffer + ArrayCount(buffer);
+  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(nullptr, buffer, buffer_end), nullptr);
+  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer, buffer, buffer_end),
+            reinterpret_cast<const uint64_t*>(buffer));
+  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 56, buffer, buffer_end),
+            reinterpret_cast<const uint64_t*>(buffer + 56));
+  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer - 1, buffer, buffer_end), nullptr);
+  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 57, buffer, buffer_end), nullptr);
+}
+
 }  // namespace art
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 9598870..16739fa 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2399,8 +2399,7 @@
       const RegType& res_type = ResolveClassAndCheckAccess(type_idx);
       if (res_type.IsConflict()) {
         // If this is a primitive type, fail HARD.
-        ObjPtr<mirror::Class> klass =
-            ClassLinker::LookupResolvedType(type_idx, dex_cache_.Get(), class_loader_.Get());
+        mirror::Class* klass = dex_cache_->GetResolvedType(type_idx);
         if (klass != nullptr && klass->IsPrimitive()) {
           Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "using primitive type "
               << dex_file_->StringByTypeIdx(type_idx) << " in instanceof in "
@@ -3115,6 +3114,44 @@
       just_set_result = true;
       break;
     }
+    case Instruction::INVOKE_CUSTOM:
+    case Instruction::INVOKE_CUSTOM_RANGE: {
+      // Verify registers based on method_type in the call site.
+      bool is_range = (inst->Opcode() == Instruction::INVOKE_CUSTOM_RANGE);
+
+      // Step 1. Check the call site that produces the method handle for invocation
+      const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+      if (!CheckCallSite(call_site_idx)) {
+        DCHECK(HasFailures());
+        break;
+      }
+
+      // Step 2. Check the register arguments correspond to the expected arguments for the
+      // method handle produced by step 1. The dex file verifier has checked ranges for
+      // the first three arguments and CheckCallSite has checked the method handle type.
+      CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx));
+      it.Next();  // Skip to name.
+      it.Next();  // Skip to method type of the method handle
+      const uint32_t proto_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+      const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(proto_idx);
+      DexFileParameterIterator param_it(*dex_file_, proto_id);
+      // Treat method as static as it has yet to be determined.
+      VerifyInvocationArgsFromIterator(&param_it, inst, METHOD_STATIC, is_range, nullptr);
+      const char* return_descriptor = dex_file_->GetReturnTypeDescriptor(proto_id);
+
+      // Step 3. Propagate return type information
+      const RegType& return_type =
+          reg_types_.FromDescriptor(GetClassLoader(), return_descriptor, false);
+      if (!return_type.IsLowHalf()) {
+        work_line_->SetResultRegisterType(this, return_type);
+      } else {
+        work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(&reg_types_));
+      }
+      just_set_result = true;
+      // TODO: Add compiler support for invoke-custom (b/35337872).
+      Fail(VERIFY_ERROR_FORCE_INTERPRETER);
+      break;
+    }
     case Instruction::NEG_INT:
     case Instruction::NOT_INT:
       work_line_->CheckUnaryOp(this, inst, reg_types_.Integer(), reg_types_.Integer());
@@ -3424,7 +3461,7 @@
     /* These should never appear during verification. */
     case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
     case Instruction::UNUSED_F3 ... Instruction::UNUSED_F9:
-    case Instruction::UNUSED_FC ... Instruction::UNUSED_FF:
+    case Instruction::UNUSED_FE ... Instruction::UNUSED_FF:
     case Instruction::UNUSED_79:
     case Instruction::UNUSED_7A:
       Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_);
@@ -3685,16 +3722,9 @@
 }
 
 const RegType& MethodVerifier::ResolveClassAndCheckAccess(dex::TypeIndex class_idx) {
-  mirror::Class* klass = can_load_classes_
-      ? Runtime::Current()->GetClassLinker()->ResolveType(
-          *dex_file_, class_idx, dex_cache_, class_loader_)
-      : ClassLinker::LookupResolvedType(class_idx, dex_cache_.Get(), class_loader_.Get()).Ptr();
-  if (can_load_classes_ && klass == nullptr) {
-    DCHECK(self_->IsExceptionPending());
-    self_->ClearException();
-  }
+  mirror::Class* klass = dex_cache_->GetResolvedType(class_idx);
   const RegType* result = nullptr;
-  if (klass != nullptr && !klass->IsErroneous()) {
+  if (klass != nullptr) {
     bool precise = klass->CannotBeAssignedFromOtherTypes();
     if (precise && !IsInstantiableOrPrimitive(klass)) {
       const char* descriptor = dex_file_->StringByTypeIdx(class_idx);
@@ -3717,6 +3747,10 @@
         << "' in " << GetDeclaringClass();
     return *result;
   }
+  if (klass == nullptr && !result->IsUnresolvedTypes()) {
+    klass = result->GetClass();
+    dex_cache_->SetResolvedType(class_idx, klass);
+  }
 
   // Record result of class resolution attempt.
   VerifierDeps::MaybeRecordClassResolution(*dex_file_, class_idx, klass);
@@ -4098,6 +4132,116 @@
   VerifyInvocationArgsFromIterator(&it, inst, method_type, is_range, nullptr);
 }
 
+bool MethodVerifier::CheckCallSite(uint32_t call_site_idx) {
+  CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx));
+  // Check essential arguments are provided. The dex file verifier has verified indicies of the
+  // main values (method handle, name, method_type).
+  if (it.Size() < 3) {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+                                      << " has too few arguments: "
+                                      << it.Size() << "< 3";
+    return false;
+  }
+
+  // Get and check the first argument: the method handle.
+  uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+  it.Next();
+  const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(method_handle_idx);
+  if (mh.method_handle_type_ != static_cast<uint16_t>(DexFile::MethodHandleType::kInvokeStatic)) {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+                                      << " argument 0 method handle type is not InvokeStatic";
+    return false;
+  }
+
+  // Skip the second argument, the name to resolve, as checked by the
+  // dex file verifier.
+  it.Next();
+
+  // Skip the third argument, the method type expected, as checked by
+  // the dex file verifier.
+  it.Next();
+
+  // Check the bootstrap method handle and remaining arguments.
+  const DexFile::MethodId& method_id = dex_file_->GetMethodId(mh.field_or_method_idx_);
+  uint32_t length;
+  const char* shorty = dex_file_->GetMethodShorty(method_id, &length);
+
+  if (it.Size() < length - 1) {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+                                      << " too few arguments for bootstrap method: "
+                                      << it.Size() << " < " << (length - 1);
+    return false;
+  }
+
+  // Check the return type and first 3 arguments are references
+  // (CallSite, Lookup, String, MethodType). If they are not of the
+  // expected types (or subtypes), it will trigger a
+  // WrongMethodTypeException during execution.
+  if (shorty[0] != 'L') {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+                                      << " bootstrap return type is not a reference";
+    return false;
+  }
+
+  for (uint32_t i = 1; i < 4; ++i) {
+    if (shorty[i] != 'L') {
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+                                        << " bootstrap method argument " << (i - 1)
+                                        << " is not a reference";
+      return false;
+    }
+  }
+
+  // Check the optional arguments.
+  for (uint32_t i = 4; i < length; ++i, it.Next()) {
+    bool match = false;
+    switch (it.GetValueType()) {
+      case EncodedArrayValueIterator::ValueType::kBoolean:
+      case EncodedArrayValueIterator::ValueType::kByte:
+      case EncodedArrayValueIterator::ValueType::kShort:
+      case EncodedArrayValueIterator::ValueType::kChar:
+      case EncodedArrayValueIterator::ValueType::kInt:
+        // These all fit within one register and encoders do not seem
+        // too exacting on the encoding type they use (ie using
+        // integer for all of these).
+        match = (strchr("ZBCSI", shorty[i]) != nullptr);
+        break;
+      case EncodedArrayValueIterator::ValueType::kLong:
+        match = ('J' == shorty[i]);
+        break;
+      case EncodedArrayValueIterator::ValueType::kFloat:
+        match = ('F' == shorty[i]);
+        break;
+      case EncodedArrayValueIterator::ValueType::kDouble:
+        match = ('D' == shorty[i]);
+        break;
+      case EncodedArrayValueIterator::ValueType::kMethodType:
+      case EncodedArrayValueIterator::ValueType::kMethodHandle:
+      case EncodedArrayValueIterator::ValueType::kString:
+      case EncodedArrayValueIterator::ValueType::kType:
+      case EncodedArrayValueIterator::ValueType::kNull:
+        match = ('L' == shorty[i]);
+        break;
+      case EncodedArrayValueIterator::ValueType::kField:
+      case EncodedArrayValueIterator::ValueType::kMethod:
+      case EncodedArrayValueIterator::ValueType::kEnum:
+      case EncodedArrayValueIterator::ValueType::kArray:
+      case EncodedArrayValueIterator::ValueType::kAnnotation:
+        // Unreachable based on current EncodedArrayValueIterator::Next().
+        UNREACHABLE();
+    }
+
+    if (!match) {
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+                                        << " bootstrap method argument " << (i - 1)
+                                        << " expected " << shorty[i]
+                                        << " got value type: " << it.GetValueType();
+      return false;
+    }
+  }
+  return true;
+}
+
 class MethodParamListDescriptorIterator {
  public:
   explicit MethodParamListDescriptorIterator(ArtMethod* res_method) :
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index fa5a698..7b67967 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -697,6 +697,11 @@
   REQUIRES_SHARED(Locks::mutator_lock_);
 
   /*
+   * Verify the arguments present for a call site. Returns "true" if all is well, "false" otherwise.
+   */
+  bool CheckCallSite(uint32_t call_site_idx);
+
+  /*
    * Verify that the target instruction is not "move-exception". It's important that the only way
    * to execute a move-exception is as the first instruction of an exception handler.
    * Returns "true" if all is well, "false" if the target instruction is move-exception.
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 1131607..000cf7c 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -890,12 +890,12 @@
     source.Assign(
         FindClassAndClearException(class_linker, self, source_desc.c_str(), class_loader));
 
-    if (destination.Get() == nullptr) {
+    if (destination == nullptr) {
       LOG(INFO) << "VerifiersDeps: Could not resolve class " << destination_desc;
       return false;
     }
 
-    if (source.Get() == nullptr) {
+    if (source == nullptr) {
       LOG(INFO) << "VerifierDeps: Could not resolve class " << source_desc;
       return false;
     }
@@ -925,7 +925,7 @@
     cls.Assign(FindClassAndClearException(class_linker, self, descriptor, class_loader));
 
     if (entry.IsResolved()) {
-      if (cls.Get() == nullptr) {
+      if (cls == nullptr) {
         LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor;
         return false;
       } else if (entry.GetAccessFlags() != GetAccessFlags(cls.Get())) {
@@ -939,7 +939,7 @@
                   << std::dec;
         return false;
       }
-    } else if (cls.Get() != nullptr) {
+    } else if (cls != nullptr) {
       LOG(INFO) << "VerifierDeps: Unexpected successful resolution of class " << descriptor;
       return false;
     }
diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc
index cd79bb6..416873f 100644
--- a/runtime/zip_archive.cc
+++ b/runtime/zip_archive.cc
@@ -23,10 +23,16 @@
 #include <unistd.h>
 #include <vector>
 
+#include "android-base/stringprintf.h"
 #include "base/unix_file/fd_file.h"
 
 namespace art {
 
+// Log file contents and mmap info when mapping entries directly.
+static constexpr const bool kDebugZipMapDirectly = false;
+
+using android::base::StringPrintf;
+
 uint32_t ZipEntry::GetUncompressedLength() {
   return zip_entry_->uncompressed_length;
 }
@@ -35,6 +41,15 @@
   return zip_entry_->crc32;
 }
 
+bool ZipEntry::IsUncompressed() {
+  return zip_entry_->method == kCompressStored;
+}
+
+bool ZipEntry::IsAlignedTo(size_t alignment) {
+  DCHECK(IsPowerOfTwo(alignment)) << alignment;
+  return IsAlignedParam(zip_entry_->offset, static_cast<int>(alignment));
+}
+
 ZipEntry::~ZipEntry() {
   delete zip_entry_;
 }
@@ -73,6 +88,102 @@
   return map.release();
 }
 
+MemMap* ZipEntry::MapDirectlyFromFile(const char* zip_filename, std::string* error_msg) {
+  const int zip_fd = GetFileDescriptor(handle_);
+  const char* entry_filename = entry_name_.c_str();
+
+  // Should not happen since we don't have a memory ZipArchive constructor.
+  // However the underlying ZipArchive isn't required to have an FD,
+  // so check to be sure.
+  CHECK_GE(zip_fd, 0) <<
+      StringPrintf("Cannot map '%s' (in zip '%s') directly because the zip archive "
+                   "is not file backed.",
+                   entry_filename,
+                   zip_filename);
+
+  if (!IsUncompressed()) {
+    *error_msg = StringPrintf("Cannot map '%s' (in zip '%s') directly because it is compressed.",
+                              entry_filename,
+                              zip_filename);
+    return nullptr;
+  } else if (zip_entry_->uncompressed_length != zip_entry_->compressed_length) {
+    *error_msg = StringPrintf("Cannot map '%s' (in zip '%s') directly because "
+                              "entry has bad size (%u != %u).",
+                              entry_filename,
+                              zip_filename,
+                              zip_entry_->uncompressed_length,
+                              zip_entry_->compressed_length);
+    return nullptr;
+  }
+
+  std::string name(entry_filename);
+  name += " mapped directly in memory from ";
+  name += zip_filename;
+
+  const off_t offset = zip_entry_->offset;
+
+  if (kDebugZipMapDirectly) {
+    LOG(INFO) << "zip_archive: " << "make mmap of " << name << " @ offset = " << offset;
+  }
+
+  std::unique_ptr<MemMap> map(
+      MemMap::MapFileAtAddress(nullptr,  // Expected pointer address
+                               GetUncompressedLength(),  // Byte count
+                               PROT_READ | PROT_WRITE,
+                               MAP_PRIVATE,
+                               zip_fd,
+                               offset,
+                               false,  // Don't restrict allocation to lower4GB
+                               false,  // Doesn't overlap existing map (reuse=false)
+                               name.c_str(),
+                               /*out*/error_msg));
+
+  if (map == nullptr) {
+    DCHECK(!error_msg->empty());
+  }
+
+  if (kDebugZipMapDirectly) {
+    // Dump contents of file, same format as using this shell command:
+    // $> od -j <offset> -t x1 <zip_filename>
+    static constexpr const int kMaxDumpChars = 15;
+    lseek(zip_fd, 0, SEEK_SET);
+
+    int count = offset + kMaxDumpChars;
+
+    std::string tmp;
+    char buf;
+
+    // Dump file contents.
+    int i = 0;
+    while (read(zip_fd, &buf, 1) > 0 && i < count) {
+      tmp += StringPrintf("%3d ", (unsigned int)buf);
+      ++i;
+    }
+
+    LOG(INFO) << "map_fd raw bytes starting at 0";
+    LOG(INFO) << "" << tmp;
+    LOG(INFO) << "---------------------------";
+
+    // Dump map contents.
+    if (map != nullptr) {
+      tmp = "";
+
+      count = kMaxDumpChars;
+
+      uint8_t* begin = map->Begin();
+      for (i = 0; i < count; ++i) {
+        tmp += StringPrintf("%3d ", (unsigned int)begin[i]);
+      }
+
+      LOG(INFO) << "map address " << StringPrintf("%p", begin);
+      LOG(INFO) << "map first " << kMaxDumpChars << " chars:";
+      LOG(INFO) << tmp;
+    }
+  }
+
+  return map.release();
+}
+
 static void SetCloseOnExec(int fd) {
   // This dance is more portable than Linux's O_CLOEXEC open(2) flag.
   int flags = fcntl(fd, F_GETFD);
@@ -129,7 +240,7 @@
     return nullptr;
   }
 
-  return new ZipEntry(handle_, zip_entry.release());
+  return new ZipEntry(handle_, zip_entry.release(), name);
 }
 
 ZipArchive::~ZipArchive() {
diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h
index 42bf55c..1858444 100644
--- a/runtime/zip_archive.h
+++ b/runtime/zip_archive.h
@@ -37,19 +37,35 @@
 class ZipEntry {
  public:
   bool ExtractToFile(File& file, std::string* error_msg);
+  // Extract this entry to anonymous memory (R/W).
+  // Returns null on failure and sets error_msg.
   MemMap* ExtractToMemMap(const char* zip_filename, const char* entry_filename,
                           std::string* error_msg);
+  // Create a file-backed private (clean, R/W) memory mapping to this entry.
+  // 'zip_filename' is used for diagnostics only,
+  //   the original file that the ZipArchive was open with is used
+  //   for the mapping.
+  //
+  // Will only succeed if the entry is stored uncompressed.
+  // Returns null on failure and sets error_msg.
+  MemMap* MapDirectlyFromFile(const char* zip_filename, /*out*/std::string* error_msg);
   virtual ~ZipEntry();
 
   uint32_t GetUncompressedLength();
   uint32_t GetCrc32();
 
+  bool IsUncompressed();
+  bool IsAlignedTo(size_t alignment);
+
  private:
   ZipEntry(ZipArchiveHandle handle,
-           ::ZipEntry* zip_entry) : handle_(handle), zip_entry_(zip_entry) {}
+           ::ZipEntry* zip_entry,
+           const std::string& entry_name)
+    : handle_(handle), zip_entry_(zip_entry), entry_name_(entry_name) {}
 
   ZipArchiveHandle handle_;
   ::ZipEntry* const zip_entry_;
+  std::string const entry_name_;
 
   friend class ZipArchive;
   DISALLOW_COPY_AND_ASSIGN(ZipEntry);
diff --git a/test/021-string2/src/Main.java b/test/021-string2/src/Main.java
index df0a3dd..5a43a4f 100644
--- a/test/021-string2/src/Main.java
+++ b/test/021-string2/src/Main.java
@@ -117,6 +117,9 @@
             " " + $noinline$equals(s0_3, s0_1) +
             " " + $noinline$equals(s0_3, s0_2) +
             " " + $noinline$equals(s0_3, s0_3));
+
+        testEqualsConstString();
+        testConstStringEquals();
     }
 
     public static void testCompareToAndEquals() {
@@ -539,6 +542,266 @@
         }
     }
 
+    public static void testEqualsConstString() {
+        Assert.assertTrue($noinline$equalsConstString0(""));
+        Assert.assertFalse($noinline$equalsConstString0("1"));
+
+        Assert.assertTrue($noinline$equalsConstString7("0123456"));
+        Assert.assertFalse($noinline$equalsConstString7("012345"));
+        Assert.assertFalse($noinline$equalsConstString7("01234567"));
+        Assert.assertFalse($noinline$equalsConstString7("012345x"));
+        Assert.assertFalse($noinline$equalsConstString7("012345\u0440"));
+
+        Assert.assertTrue($noinline$equalsConstString14("01234567890123"));
+        Assert.assertFalse($noinline$equalsConstString14("0123456789012"));
+        Assert.assertFalse($noinline$equalsConstString14("012345678901234"));
+        Assert.assertFalse($noinline$equalsConstString14("0123456789012x"));
+        Assert.assertFalse($noinline$equalsConstString14("0123456789012\u0440"));
+
+        Assert.assertTrue($noinline$equalsConstString24("012345678901234567890123"));
+        Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012"));
+        Assert.assertFalse($noinline$equalsConstString24("0123456789012345678901234"));
+        Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012x"));
+        Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012\u0440"));
+
+        Assert.assertTrue($noinline$equalsConstString29("01234567890123456789012345678"));
+        Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567"));
+        Assert.assertFalse($noinline$equalsConstString29("012345678901234567890123456789"));
+        Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567x"));
+        Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567\u0440"));
+
+        Assert.assertTrue($noinline$equalsConstString35("01234567890123456789012345678901234"));
+        Assert.assertFalse($noinline$equalsConstString35("0123456789012345678901234567890123"));
+        Assert.assertFalse($noinline$equalsConstString35("012345678901234567890123456789012345"));
+        Assert.assertFalse($noinline$equalsConstString35("0123456789012345678901234567890123x"));
+        Assert.assertFalse(
+            $noinline$equalsConstString35("0123456789012345678901234567890123\u0440"));
+
+        Assert.assertTrue($noinline$equalsConstNonAsciiString7("\u0440123456"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u044012345"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u04401234567"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u044012345x"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString7("0123456"));
+
+        Assert.assertTrue($noinline$equalsConstNonAsciiString14("\u04401234567890123"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u0440123456789012"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u044012345678901234"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u0440123456789012x"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString14("01234567890123"));
+
+        Assert.assertTrue($noinline$equalsConstNonAsciiString24("\u044012345678901234567890123"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u04401234567890123456789012"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u0440123456789012345678901234"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u04401234567890123456789012x"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString24("\012345678901234567890123"));
+
+        Assert.assertTrue(
+            $noinline$equalsConstNonAsciiString29("\u04401234567890123456789012345678"));
+        Assert.assertFalse(
+            $noinline$equalsConstNonAsciiString29("\u0440123456789012345678901234567"));
+        Assert.assertFalse(
+            $noinline$equalsConstNonAsciiString29("\u044012345678901234567890123456789"));
+        Assert.assertFalse(
+            $noinline$equalsConstNonAsciiString29("\u0440123456789012345678901234567x"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString29("01234567890123456789012345678"));
+
+        Assert.assertTrue(
+            $noinline$equalsConstNonAsciiString35("\u04401234567890123456789012345678901234"));
+        Assert.assertFalse(
+            $noinline$equalsConstNonAsciiString35("\u0440123456789012345678901234567890123"));
+        Assert.assertFalse(
+            $noinline$equalsConstNonAsciiString35("\u044012345678901234567890123456789012345"));
+        Assert.assertFalse(
+            $noinline$equalsConstNonAsciiString35("\u0440123456789012345678901234567890123x"));
+        Assert.assertFalse(
+            $noinline$equalsConstNonAsciiString35("01234567890123456789012345678901234"));
+    }
+
+    public static void testConstStringEquals() {
+        Assert.assertTrue($noinline$constString0Equals(""));
+        Assert.assertFalse($noinline$constString0Equals("1"));
+
+        Assert.assertTrue($noinline$constString7Equals("0123456"));
+        Assert.assertFalse($noinline$constString7Equals("012345"));
+        Assert.assertFalse($noinline$constString7Equals("01234567"));
+        Assert.assertFalse($noinline$constString7Equals("012345x"));
+        Assert.assertFalse($noinline$constString7Equals("012345\u0440"));
+
+        Assert.assertTrue($noinline$constString14Equals("01234567890123"));
+        Assert.assertFalse($noinline$constString14Equals("0123456789012"));
+        Assert.assertFalse($noinline$constString14Equals("012345678901234"));
+        Assert.assertFalse($noinline$constString14Equals("0123456789012x"));
+        Assert.assertFalse($noinline$constString14Equals("0123456789012\u0440"));
+
+        Assert.assertTrue($noinline$constString24Equals("012345678901234567890123"));
+        Assert.assertFalse($noinline$constString24Equals("01234567890123456789012"));
+        Assert.assertFalse($noinline$constString24Equals("0123456789012345678901234"));
+        Assert.assertFalse($noinline$constString24Equals("01234567890123456789012x"));
+        Assert.assertFalse($noinline$constString24Equals("01234567890123456789012\u0440"));
+
+        Assert.assertTrue($noinline$constString29Equals("01234567890123456789012345678"));
+        Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567"));
+        Assert.assertFalse($noinline$constString29Equals("012345678901234567890123456789"));
+        Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567x"));
+        Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567\u0440"));
+
+        Assert.assertTrue($noinline$constString35Equals("01234567890123456789012345678901234"));
+        Assert.assertFalse($noinline$constString35Equals("0123456789012345678901234567890123"));
+        Assert.assertFalse($noinline$constString35Equals("012345678901234567890123456789012345"));
+        Assert.assertFalse($noinline$constString35Equals("0123456789012345678901234567890123x"));
+        Assert.assertFalse(
+            $noinline$constString35Equals("0123456789012345678901234567890123\u0040"));
+
+        Assert.assertTrue($noinline$constNonAsciiString7Equals("\u0440123456"));
+        Assert.assertFalse($noinline$constNonAsciiString7Equals("\u044012345"));
+        Assert.assertFalse($noinline$constNonAsciiString7Equals("\u04401234567"));
+        Assert.assertFalse($noinline$constNonAsciiString7Equals("\u044012345x"));
+        Assert.assertFalse($noinline$constNonAsciiString7Equals("0123456"));
+
+        Assert.assertTrue($noinline$constNonAsciiString14Equals("\u04401234567890123"));
+        Assert.assertFalse($noinline$constNonAsciiString14Equals("\u0440123456789012"));
+        Assert.assertFalse($noinline$constNonAsciiString14Equals("\u044012345678901234"));
+        Assert.assertFalse($noinline$constNonAsciiString14Equals("\u0440123456789012x"));
+        Assert.assertFalse($noinline$constNonAsciiString14Equals("01234567890123"));
+
+        Assert.assertTrue($noinline$constNonAsciiString24Equals("\u044012345678901234567890123"));
+        Assert.assertFalse($noinline$constNonAsciiString24Equals("\u04401234567890123456789012"));
+        Assert.assertFalse($noinline$constNonAsciiString24Equals("\u0440123456789012345678901234"));
+        Assert.assertFalse($noinline$constNonAsciiString24Equals("\u04401234567890123456789012x"));
+        Assert.assertFalse($noinline$constNonAsciiString24Equals("\012345678901234567890123"));
+
+        Assert.assertTrue(
+            $noinline$constNonAsciiString29Equals("\u04401234567890123456789012345678"));
+        Assert.assertFalse(
+            $noinline$constNonAsciiString29Equals("\u0440123456789012345678901234567"));
+        Assert.assertFalse(
+            $noinline$constNonAsciiString29Equals("\u044012345678901234567890123456789"));
+        Assert.assertFalse(
+            $noinline$constNonAsciiString29Equals("\u0440123456789012345678901234567x"));
+        Assert.assertFalse($noinline$constNonAsciiString29Equals("01234567890123456789012345678"));
+
+        Assert.assertTrue(
+            $noinline$constNonAsciiString35Equals("\u04401234567890123456789012345678901234"));
+        Assert.assertFalse(
+            $noinline$constNonAsciiString35Equals("\u0440123456789012345678901234567890123"));
+        Assert.assertFalse(
+            $noinline$constNonAsciiString35Equals("\u044012345678901234567890123456789012345"));
+        Assert.assertFalse(
+            $noinline$constNonAsciiString35Equals("\u0440123456789012345678901234567890123x"));
+        Assert.assertFalse(
+            $noinline$constNonAsciiString35Equals("01234567890123456789012345678901234"));
+    }
+
+    public static boolean $noinline$equalsConstString0(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("");
+    }
+
+    public static boolean $noinline$equalsConstString7(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("0123456");
+    }
+
+    public static boolean $noinline$equalsConstString14(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("01234567890123");
+    }
+
+    public static boolean $noinline$equalsConstString24(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("012345678901234567890123");
+    }
+
+    public static boolean $noinline$equalsConstString29(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("01234567890123456789012345678");
+    }
+
+    public static boolean $noinline$equalsConstString35(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("01234567890123456789012345678901234");
+    }
+
+    public static boolean $noinline$equalsConstNonAsciiString7(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("\u0440123456");
+    }
+
+    public static boolean $noinline$equalsConstNonAsciiString14(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("\u04401234567890123");
+    }
+
+    public static boolean $noinline$equalsConstNonAsciiString24(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("\u044012345678901234567890123");
+    }
+
+    public static boolean $noinline$equalsConstNonAsciiString29(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("\u04401234567890123456789012345678");
+    }
+
+    public static boolean $noinline$equalsConstNonAsciiString35(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("\u04401234567890123456789012345678901234");
+    }
+
+    public static boolean $noinline$constString0Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("");
+    }
+
+    public static boolean $noinline$constString7Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "0123456".equals(s);
+    }
+
+    public static boolean $noinline$constString14Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "01234567890123".equals(s);
+    }
+
+    public static boolean $noinline$constString24Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "012345678901234567890123".equals(s);
+    }
+
+    public static boolean $noinline$constString29Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "01234567890123456789012345678".equals(s);
+    }
+
+    public static boolean $noinline$constString35Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "01234567890123456789012345678901234".equals(s);
+    }
+
+    public static boolean $noinline$constNonAsciiString7Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "\u0440123456".equals(s);
+    }
+
+    public static boolean $noinline$constNonAsciiString14Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "\u04401234567890123".equals(s);
+    }
+
+    public static boolean $noinline$constNonAsciiString24Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "\u044012345678901234567890123".equals(s);
+    }
+
+    public static boolean $noinline$constNonAsciiString29Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "\u04401234567890123456789012345678".equals(s);
+    }
+
+    public static boolean $noinline$constNonAsciiString35Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "\u04401234567890123456789012345678901234".equals(s);
+    }
+
     public static int $noinline$compareTo(String lhs, String rhs) {
         if (doThrow) { throw new Error(); }
         return lhs.compareTo(rhs);
diff --git a/test/044-proxy/expected.txt b/test/044-proxy/expected.txt
index 2a5f0b9..63a4620 100644
--- a/test/044-proxy/expected.txt
+++ b/test/044-proxy/expected.txt
@@ -87,7 +87,7 @@
 Proxy methods: [public final boolean $PROXY_CLASS_NAME1$.equals(java.lang.Object), public final java.lang.Object $PROXY_CLASS_NAME1$.foo(), public final java.lang.String $PROXY_CLASS_NAME1$.foo(), public final int $PROXY_CLASS_NAME1$.hashCode(), public final java.lang.String $PROXY_CLASS_NAME1$.toString()]
 Invocation of public abstract java.lang.String NarrowingTest$I2.foo()
 Invoking foo using I2 type: hello
-Invocation of public abstract java.lang.Object NarrowingTest$I1.foo()
+Invocation of public default java.lang.Object NarrowingTest$I2.foo()
 Invoking foo using I1 type: 1
 Invocation of public abstract java.lang.String NarrowingTest$I2.foo()
 Got expected exception
diff --git a/test/071-dexfile-map-clean/build b/test/071-dexfile-map-clean/build
new file mode 100755
index 0000000..a171fc3
--- /dev/null
+++ b/test/071-dexfile-map-clean/build
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Any JAR files used by this test shall have their classes.dex be stored, NOT compressed.
+# This is needed for our test correctness which validates classes.dex are mapped file-backed.
+#
+# In addition, align to at least 4 bytes since that's the dex alignment requirement.
+./default-build "$@" --zip-compression-method store --zip-align 4
diff --git a/test/071-dexfile-map-clean/expected.txt b/test/071-dexfile-map-clean/expected.txt
new file mode 100644
index 0000000..af7fb28
--- /dev/null
+++ b/test/071-dexfile-map-clean/expected.txt
@@ -0,0 +1,3 @@
+Another
+Secondary dexfile mmap is clean
+Another Instance
diff --git a/test/071-dexfile-map-clean/info.txt b/test/071-dexfile-map-clean/info.txt
new file mode 100644
index 0000000..7e45808
--- /dev/null
+++ b/test/071-dexfile-map-clean/info.txt
@@ -0,0 +1,11 @@
+Exercise Dalvik-specific DEX file feature. Will not work on RI.
+
+If these conditions are met:
+* When we are loading in a secondary dex file
+* and when dex2oat is not used
+* and the dex file is stored uncompressed in a ZIP file
+
+Then check:
+* The dex file is memory-mapped file-backed as clean memory
+(i.e. there is no extraction step)
+
diff --git a/test/071-dexfile-map-clean/run b/test/071-dexfile-map-clean/run
new file mode 100755
index 0000000..9c100ec
--- /dev/null
+++ b/test/071-dexfile-map-clean/run
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Run without dex2oat so that we don't create oat/vdex files
+# when trying to load the secondary dex file.
+
+# In this way, the secondary dex file will be forced to be
+# loaded directly.
+#
+# In addition, make sure we call 'sync'
+# before executing dalvikvm because otherwise
+# it's highly likely the pushed JAR files haven't
+# been committed to permanent storage yet,
+# and when we mmap them the kernel will think
+# the memory is dirty (despite being file-backed).
+# (Note: this was reproducible 100% of the time on
+# a target angler device).
+./default-run "$@" --no-dex2oat --sync
diff --git a/test/071-dexfile-map-clean/src-ex/Another.java b/test/071-dexfile-map-clean/src-ex/Another.java
new file mode 100644
index 0000000..58464a6
--- /dev/null
+++ b/test/071-dexfile-map-clean/src-ex/Another.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Another {
+    public Another() {
+        System.out.println("Another Instance");
+    }
+}
diff --git a/test/071-dexfile-map-clean/src/Main.java b/test/071-dexfile-map-clean/src/Main.java
new file mode 100644
index 0000000..8a196dd
--- /dev/null
+++ b/test/071-dexfile-map-clean/src/Main.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+/**
+ * DexFile tests (Dalvik-specific).
+ */
+public class Main {
+    private static final String CLASS_PATH =
+        System.getenv("DEX_LOCATION") + "/071-dexfile-map-clean-ex.jar";
+
+    /**
+     * Prep the environment then run the test.
+     */
+    public static void main(String[] args) throws Exception {
+        // Load the dex file, this is a pre-requisite to mmap-ing it in.
+        Class<?> AnotherClass = testDexFile();
+        // Check that the memory maps are clean.
+        testDexMemoryMaps();
+
+        // Prevent garbage collector from collecting our DexFile
+        // (and unmapping too early) by using it after we finish
+        // our verification.
+        AnotherClass.newInstance();
+    }
+
+    private static boolean checkSmapsEntry(String[] smapsLines, int offset) {
+      String nameDescription = smapsLines[offset];
+      String[] split = nameDescription.split(" ");
+
+      String permissions = split[1];
+      // Mapped as read-only + anonymous.
+      if (!permissions.startsWith("r--p")) {
+        return false;
+      }
+
+      boolean validated = false;
+
+      // We have the right entry, now make sure it's valid.
+      for (int i = offset; i < smapsLines.length; ++i) {
+        String line = smapsLines[i];
+
+        if (line.startsWith("Shared_Dirty") || line.startsWith("Private_Dirty")) {
+          String lineTrimmed = line.trim();
+          String[] lineSplit = lineTrimmed.split(" +");
+
+          String sizeUsuallyInKb = lineSplit[lineSplit.length - 2];
+
+          sizeUsuallyInKb = sizeUsuallyInKb.trim();
+
+          if (!sizeUsuallyInKb.equals("0")) {
+            System.out.println(
+                "ERROR: Memory mapping for " + CLASS_PATH + " is unexpectedly dirty");
+            System.out.println(line);
+          } else {
+            validated = true;
+          }
+        }
+
+        // VmFlags marks the "end" of an smaps entry.
+        if (line.startsWith("VmFlags")) {
+          break;
+        }
+      }
+
+      if (validated) {
+        System.out.println("Secondary dexfile mmap is clean");
+      } else {
+        System.out.println("ERROR: Memory mapping is missing Shared_Dirty/Private_Dirty entries");
+      }
+
+      return true;
+    }
+
+    // This test takes relies on dex2oat being skipped.
+    // (enforced in 'run' file by using '--no-dex2oat'
+    //
+    // This could happen in a non-test situation
+    // if a secondary dex file is loaded (but not yet maintenance-mode compiled)
+    // with JIT.
+    //
+    // Or it could also happen if a secondary dex file is loaded and forced
+    // into running into the interpreter (e.g. duplicate classes).
+    //
+    // Rather than relying on those weird fallbacks,
+    // we force the runtime not to dex2oat the dex file to ensure
+    // this test is repeatable and less brittle.
+    private static void testDexMemoryMaps() throws Exception {
+        // Ensure that the secondary dex file is mapped clean (directly from JAR file).
+        String smaps = new String(Files.readAllBytes(Paths.get("/proc/self/smaps")));
+
+        String[] smapsLines = smaps.split("\n");
+        boolean found = true;
+        for (int i = 0; i < smapsLines.length; ++i) {
+          if (smapsLines[i].contains(CLASS_PATH)) {
+            if (checkSmapsEntry(smapsLines, i)) {
+              return;
+            } // else we found the wrong one, keep going.
+          }
+        }
+
+        // Error case:
+        System.out.println("Could not find " + CLASS_PATH + " RO-anonymous smaps entry");
+        System.out.println(smaps);
+    }
+
+    private static Class<?> testDexFile() throws Exception {
+        ClassLoader classLoader = Main.class.getClassLoader();
+        Class<?> DexFile = classLoader.loadClass("dalvik.system.DexFile");
+        Method DexFile_loadDex = DexFile.getMethod("loadDex",
+                                                   String.class,
+                                                   String.class,
+                                                   Integer.TYPE);
+        Method DexFile_entries = DexFile.getMethod("entries");
+        Object dexFile = DexFile_loadDex.invoke(null, CLASS_PATH, null, 0);
+        Enumeration<String> e = (Enumeration<String>) DexFile_entries.invoke(dexFile);
+        while (e.hasMoreElements()) {
+            String className = e.nextElement();
+            System.out.println(className);
+        }
+
+        Method DexFile_loadClass = DexFile.getMethod("loadClass",
+                                                     String.class,
+                                                     ClassLoader.class);
+        Class<?> AnotherClass = (Class<?>)DexFile_loadClass.invoke(dexFile,
+            "Another", Main.class.getClassLoader());
+        return AnotherClass;
+    }
+}
diff --git a/test/121-modifiers/build b/test/121-modifiers/build
deleted file mode 100644
index 771dd51..0000000
--- a/test/121-modifiers/build
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Stop if something fails.
-set -e
-
-# The classes are pre-compiled and modified with ASM.
-#
-# To reproduce, compile the source files. Asm.java needs the ASM libraries (core and tree). Then
-# run Asm.java, which produces Inf.out and NonInf.out. Rename these to class files and put them
-# into the classes directory (this assumes the ASM libraries are names asm.jar and asm-tree.jar):
-#
-# javac Inf.java NonInf.java Main.java
-# javac -cp asm.jar:asm-tree.jar:. Asm.java
-# java -cp asm.jar:asm-tree.jar:. Asm
-# mv Inf.out classes/Inf.class
-# mv NonInf.out classes/NonInf.class
-# mv Main.class A.class A\$B.class A\$C.class classes/
-
-if [ ${USE_JACK} = "true" ]; then
-  jar cf classes.jill.jar -C classes .
-  # Workaround b/19561685: disable sanity checks to produce a DEX file with invalid modifiers.
-  ${JACK} --sanity-checks off --import classes.jill.jar --output-dex .
-else
-  ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes
-fi
-zip $TEST_NAME.jar classes.dex
diff --git a/test/121-modifiers/info.txt b/test/121-modifiers/info.txt
index 943cbf8..129aee8 100644
--- a/test/121-modifiers/info.txt
+++ b/test/121-modifiers/info.txt
@@ -1 +1,18 @@
 This is a test checking the modifier (access flags) handling of ART.
+
+The classes are pre-compiled and modified with ASM.
+
+To reproduce, compile the source files. Asm.java needs the ASM libraries (core and tree). Then
+run Asm.java, which produces Inf.out and NonInf.out. Rename these to class files and put them
+into the classes directory (this assumes the ASM libraries are names asm.jar and asm-tree.jar).
+Finally, compile with jack/jill or dx, and run baksmali.
+
+javac Inf.java NonInf.java Main.java
+javac -cp asm.jar:asm-tree.jar:. Asm.java
+java -cp asm.jar:asm-tree.jar:. Asm
+mv Inf.out classes/Inf.class
+mv NonInf.out classes/NonInf.class
+mv Main.class A.class A\$B.class A\$C.class classes/
+dx --debug --dex --output=classes.dex classes
+baksmali classes.dex
+mv out/*.smali smali/
diff --git a/test/121-modifiers/smali/A$B.smali b/test/121-modifiers/smali/A$B.smali
new file mode 100644
index 0000000..d6addc2
--- /dev/null
+++ b/test/121-modifiers/smali/A$B.smali
@@ -0,0 +1,42 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+.class LA$B;
+.super Ljava/lang/Object;
+.source "Main.java"
+
+
+# annotations
+.annotation system Ldalvik/annotation/EnclosingClass;
+    value = LA;
+.end annotation
+
+.annotation system Ldalvik/annotation/InnerClass;
+    accessFlags = 0xa
+    name = "B"
+.end annotation
+
+
+# direct methods
+.method private constructor <init>()V
+    .registers 1
+
+    .prologue
+    .line 19
+    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+
+    return-void
+.end method
diff --git a/test/900-hello-plugin/build b/test/121-modifiers/smali/A$C.smali
old mode 100755
new mode 100644
similarity index 60%
copy from test/900-hello-plugin/build
copy to test/121-modifiers/smali/A$C.smali
index 898e2e5..eba4756
--- a/test/900-hello-plugin/build
+++ b/test/121-modifiers/smali/A$C.smali
@@ -1,6 +1,5 @@
-#!/bin/bash
 #
-# Copyright 2016 The Android Open Source Project
+# Copyright (C) 2014 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -13,5 +12,19 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+#
 
-./default-build "$@" --experimental agents
+.class public interface abstract LA$C;
+.super Ljava/lang/Object;
+.source "Main.java"
+
+
+# annotations
+.annotation system Ldalvik/annotation/EnclosingClass;
+    value = LA;
+.end annotation
+
+.annotation system Ldalvik/annotation/InnerClass;
+    accessFlags = 0x60c
+    name = "C"
+.end annotation
diff --git a/test/121-modifiers/smali/A.smali b/test/121-modifiers/smali/A.smali
new file mode 100644
index 0000000..b1078bc
--- /dev/null
+++ b/test/121-modifiers/smali/A.smali
@@ -0,0 +1,41 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+.class LA;
+.super Ljava/lang/Object;
+.source "Main.java"
+
+
+# annotations
+.annotation system Ldalvik/annotation/MemberClasses;
+    value = {
+        LA$B;,
+        LA$C;
+    }
+.end annotation
+
+
+# direct methods
+.method constructor <init>()V
+    .registers 1
+
+    .prologue
+    .line 18
+    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+
+    .line 21
+    return-void
+.end method
diff --git a/test/900-hello-plugin/build b/test/121-modifiers/smali/Inf.smali
old mode 100755
new mode 100644
similarity index 73%
copy from test/900-hello-plugin/build
copy to test/121-modifiers/smali/Inf.smali
index 898e2e5..6a3a7ab
--- a/test/900-hello-plugin/build
+++ b/test/121-modifiers/smali/Inf.smali
@@ -1,6 +1,5 @@
-#!/bin/bash
 #
-# Copyright 2016 The Android Open Source Project
+# Copyright (C) 2014 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -13,5 +12,12 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+#
 
-./default-build "$@" --experimental agents
+.class public interface abstract LInf;
+.super Ljava/lang/Object;
+.source "Inf.java"
+
+
+# static fields
+.field public static final I:I
diff --git a/test/121-modifiers/smali/NonInf.smali b/test/121-modifiers/smali/NonInf.smali
new file mode 100644
index 0000000..34bf031
--- /dev/null
+++ b/test/121-modifiers/smali/NonInf.smali
@@ -0,0 +1,177 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+.class public abstract LNonInf;
+.super Ljava/lang/Object;
+.source "NonInf.java"
+
+
+# static fields
+.field static staticField:I
+
+
+# instance fields
+.field final finalField:I
+
+.field private privateField:I
+
+.field protected protectedField:I
+
+.field public publicField:I
+
+.field transient transientField:I
+
+.field volatile volatileField:I
+
+
+# direct methods
+.method public constructor <init>()V
+    .registers 2
+
+    .prologue
+    .line 11
+    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+
+    .line 12
+    const/4 v0, 0x0
+
+    iput v0, p0, LNonInf;->publicField:I
+
+    .line 13
+    const/4 v0, 0x1
+
+    iput v0, p0, LNonInf;->privateField:I
+
+    .line 14
+    const/4 v0, 0x2
+
+    iput v0, p0, LNonInf;->protectedField:I
+
+    .line 15
+    const/4 v0, 0x3
+
+    sput v0, LNonInf;->staticField:I
+
+    .line 16
+    const/4 v0, 0x4
+
+    iput v0, p0, LNonInf;->transientField:I
+
+    .line 17
+    const/4 v0, 0x5
+
+    iput v0, p0, LNonInf;->volatileField:I
+
+    .line 18
+    const/4 v0, 0x6
+
+    iput v0, p0, LNonInf;->finalField:I
+
+    .line 19
+    return-void
+.end method
+
+.method private privateMethod()I
+    .registers 2
+
+    .prologue
+    .line 24
+    const/4 v0, 0x0
+
+    return v0
+.end method
+
+.method public static staticMethod()I
+    .registers 1
+
+    .prologue
+    .line 42
+    const/4 v0, 0x0
+
+    return v0
+.end method
+
+
+# virtual methods
+.method public abstract abstractMethod()I
+.end method
+
+.method public final finalMethod()I
+    .registers 2
+
+    .prologue
+    .line 54
+    const/4 v0, 0x0
+
+    return v0
+.end method
+
+.method public native nativeMethod()V
+.end method
+
+.method protected protectedMethod()I
+    .registers 2
+
+    .prologue
+    .line 28
+    const/4 v0, 0x0
+
+    return v0
+.end method
+
+.method public publicMethod()I
+    .registers 2
+
+    .prologue
+    .line 32
+    const/4 v0, 0x0
+
+    return v0
+.end method
+
+.method public strictfp strictfpMethod()D
+    .registers 3
+
+    .prologue
+    .line 46
+    const-wide/16 v0, 0x0
+
+    return-wide v0
+.end method
+
+.method public declared-synchronized synchronizedMethod()I
+    .registers 2
+
+    .prologue
+    monitor-enter p0
+
+    .line 38
+    const/4 v0, 0x0
+
+    monitor-exit p0
+
+    return v0
+.end method
+
+.method public varargs varargsMethod([Ljava/lang/Object;)I
+    .registers 3
+
+    .prologue
+    .line 50
+    const/4 v0, 0x0
+
+    return v0
+.end method
diff --git a/test/121-modifiers/src-java/A.java b/test/121-modifiers/src-java/A.java
new file mode 100644
index 0000000..d97f6b3
--- /dev/null
+++ b/test/121-modifiers/src-java/A.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// These classes are to check the additional flags for inner classes.
+class A {
+  private static class B {
+  }
+  protected static interface C {
+  }
+}
diff --git a/test/121-modifiers/src/Asm.java b/test/121-modifiers/src-java/Asm.java
similarity index 100%
rename from test/121-modifiers/src/Asm.java
rename to test/121-modifiers/src-java/Asm.java
diff --git a/test/121-modifiers/src/Inf.java b/test/121-modifiers/src-java/Inf.java
similarity index 100%
rename from test/121-modifiers/src/Inf.java
rename to test/121-modifiers/src-java/Inf.java
diff --git a/test/121-modifiers/src/NonInf.java b/test/121-modifiers/src-java/NonInf.java
similarity index 100%
rename from test/121-modifiers/src/NonInf.java
rename to test/121-modifiers/src-java/NonInf.java
diff --git a/test/121-modifiers/src/Main.java b/test/121-modifiers/src2/Main.java
similarity index 97%
rename from test/121-modifiers/src/Main.java
rename to test/121-modifiers/src2/Main.java
index e21b789..62e65a8 100644
--- a/test/121-modifiers/src/Main.java
+++ b/test/121-modifiers/src2/Main.java
@@ -14,14 +14,6 @@
  * limitations under the License.
  */
 
-// These classes are to check the additional flags for inner classes.
-class A {
-  private static class B {
-  }
-  protected static interface C {
-  }
-}
-
 public class Main {
   public final static int INTERFACE_DEFINED_BITS =
       0x0001 |  // public, may be set.
diff --git a/test/155-java-set-resolved-type/src/Main.java b/test/155-java-set-resolved-type/src/Main.java
index 56b8c3e..f92363e 100644
--- a/test/155-java-set-resolved-type/src/Main.java
+++ b/test/155-java-set-resolved-type/src/Main.java
@@ -55,7 +55,11 @@
             Class<?> timpl = Class.forName("TestImplementation", false, mainLoader);
             // Clear the dex cache resolved types to force a proper lookup the next time
             // we need to find TestInterface.
-            clearResolvedTypes(timpl);
+            // TODO: Enable clearing the dex cache when we switch to the hash-based type array
+            // and do a proper lookup. Currently, ClassLinker fully relies on the DexCache.
+            if (false) {
+                clearResolvedTypes(timpl);
+            }
 
             // Force intialization of TestClass2. This expects the interface type to be
             // resolved and found through simple lookup.
diff --git a/test/476-checker-ctor-memory-barrier/src/Main.java b/test/476-checker-ctor-memory-barrier/src/Main.java
index c2a2a10..65486e9 100644
--- a/test/476-checker-ctor-memory-barrier/src/Main.java
+++ b/test/476-checker-ctor-memory-barrier/src/Main.java
@@ -27,15 +27,10 @@
   public ClassWithFinals obj;
   public static boolean doThrow = false;
 
-  /// CHECK-START: void ClassWithFinals.<init>(boolean) register (after)
-  /// CHECK:      MemoryBarrier kind:StoreStore
-  /// CHECK-NEXT: ReturnVoid
   public ClassWithFinals(boolean cond) {
-    x = 0;
-    if (doThrow) {
-      // avoid inlining
-      throw new RuntimeException();
-    }
+    x = 1;
+    throw new RuntimeException();
+    // should not inline this constructor
   }
 
   /// CHECK-START: void ClassWithFinals.<init>() register (after)
@@ -146,6 +141,7 @@
   /// CHECK-NOT:  MemoryBarrier kind:StoreStore
   public static ClassWithFinals noInlineNoConstructorBarrier() {
     return new ClassWithFinals(false);
+    // should not inline the constructor
   }
 
   /// CHECK-START: void Main.inlineNew() register (after)
diff --git a/test/478-checker-clinit-check-pruning/src/Main.java b/test/478-checker-clinit-check-pruning/src/Main.java
index c2982b4..63e2b95 100644
--- a/test/478-checker-clinit-check-pruning/src/Main.java
+++ b/test/478-checker-clinit-check-pruning/src/Main.java
@@ -400,8 +400,10 @@
   /// CHECK-NOT:                           ClinitCheck
 
   static void inlinedInvokeStaticViaNonStatic(Iterable<?> it) {
-    inlinedInvokeStaticViaNonStaticHelper(null);
-    inlinedInvokeStaticViaNonStaticHelper(it);
+    if (it != null) {
+      inlinedInvokeStaticViaNonStaticHelper(null);
+      inlinedInvokeStaticViaNonStaticHelper(it);
+    }
   }
 
   static void inlinedInvokeStaticViaNonStaticHelper(Iterable<?> it) {
@@ -417,8 +419,8 @@
     static void inlinedForNull(Iterable<?> it) {
       if (it != null) {
         it.iterator();
-        // We're not inlining throw at the moment.
-        if (doThrow) { throw new Error(""); }
+        // We're not inlining methods that always throw.
+        throw new Error("");
       }
     }
   }
@@ -441,7 +443,9 @@
   /// CHECK-NOT:                           ClinitCheck
 
   static void inlinedInvokeStaticViaStatic(Iterable<?> it) {
-    ClassWithClinit11.callInlinedForNull(it);
+    if (it != null) {
+      ClassWithClinit11.callInlinedForNull(it);
+    }
   }
 
   static class ClassWithClinit11 {
@@ -457,8 +461,8 @@
     static void inlinedForNull(Iterable<?> it) {
       it.iterator();
       if (it != null) {
-        // We're not inlining throw at the moment.
-        if (doThrow) { throw new Error(""); }
+        // We're not inlining methods that always throw.
+        throw new Error("");
       }
     }
   }
@@ -476,8 +480,10 @@
   /// CHECK-NOT:                           ClinitCheck
 
   static void inlinedInvokeStaticViaStaticTwice(Iterable<?> it) {
-    ClassWithClinit12.callInlinedForNull(null);
-    ClassWithClinit12.callInlinedForNull(it);
+    if (it != null) {
+      ClassWithClinit12.callInlinedForNull(null);
+      ClassWithClinit12.callInlinedForNull(it);
+    }
   }
 
   static class ClassWithClinit12 {
@@ -492,8 +498,8 @@
 
     static void inlinedForNull(Iterable<?> it) {
       if (it != null) {
-        // We're not inlining throw at the moment.
-        if (doThrow) { throw new Error(""); }
+        // We're not inlining methods that always throw.
+        throw new Error("");
       }
     }
   }
@@ -537,9 +543,21 @@
     constClassAndInvokeStatic(it);
     sgetAndInvokeStatic(it);
     constClassSgetAndInvokeStatic(it);
-    inlinedInvokeStaticViaNonStatic(it);
-    inlinedInvokeStaticViaStatic(it);
-    inlinedInvokeStaticViaStaticTwice(it);
+    try {
+      inlinedInvokeStaticViaNonStatic(it);
+    } catch (Error e) {
+      // Expected
+    }
+    try {
+      inlinedInvokeStaticViaStatic(it);
+    } catch (Error e) {
+      // Expected
+    }
+    try{
+      inlinedInvokeStaticViaStaticTwice(it);
+    } catch (Error e) {
+      // Expected
+    }
     $noinline$testInliningAndNewInstance(it);
   }
 }
diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java
index ed7524c..52f3f84 100644
--- a/test/536-checker-intrinsic-optimization/src/Main.java
+++ b/test/536-checker-intrinsic-optimization/src/Main.java
@@ -329,7 +329,7 @@
   /// CHECK-NOT:      cbz
   // Terminate the scope for the CHECK-NOT search at the reference or length comparison,
   // whichever comes first.
-  /// CHECK:          cmp {{w.*,}} {{w.*}}
+  /// CHECK:          cmp {{w.*,}} {{w.*|#.*}}
   public static boolean stringArgumentNotNull(Object obj) {
     obj.getClass();
     return "foo".equals(obj);
@@ -380,10 +380,10 @@
   // so repeat the check twice.
   /// CHECK-NOT:      ldr {{w\d+}}, [{{x\d+}}]
   /// CHECK-NOT:      ldr {{w\d+}}, [{{x\d+}}, #0]
-  /// CHECK:          cmp {{w\d+}}, {{w\d+}}
+  /// CHECK:          cmp {{w\d+}}, {{w\d+|#.*}}
   /// CHECK-NOT:      ldr {{w\d+}}, [{{x\d+}}]
   /// CHECK-NOT:      ldr {{w\d+}}, [{{x\d+}}, #0]
-  /// CHECK:          cmp {{w\d+}}, {{w\d+}}
+  /// CHECK:          cmp {{w\d+}}, {{w\d+|#.*}}
   public static boolean stringArgumentIsString() {
     return "foo".equals(myString);
   }
diff --git a/test/609-checker-inline-interface/src/Main.java b/test/609-checker-inline-interface/src/Main.java
index 413f2dd..249b778 100644
--- a/test/609-checker-inline-interface/src/Main.java
+++ b/test/609-checker-inline-interface/src/Main.java
@@ -21,12 +21,21 @@
   }
 
   public void doCall() {
-    if (doThrow) throw new Error("");
+    // We do not inline methods that always throw.
+    throw new Error("");
   }
 
   public static void main(String[] args) {
-    testInlineInterfaceCall();
-    testInterfaceToVirtualCall();
+    try {
+      testInlineInterfaceCall();
+    } catch (Error e) {
+      // Expected
+    }
+    try {
+      testInterfaceToVirtualCall();
+    } catch (Error e) {
+      // Expected.
+    }
   }
 
   /// CHECK-START: void Main.testInlineInterfaceCall() inliner (before)
@@ -62,7 +71,6 @@
 
   static Interface itf = new Main();
   static Main m = new Main();
-  static boolean doThrow = false;
 }
 
 interface Interface {
diff --git a/test/624-checker-stringops/src/Main.java b/test/624-checker-stringops/src/Main.java
index 75b782e..63da4f5 100644
--- a/test/624-checker-stringops/src/Main.java
+++ b/test/624-checker-stringops/src/Main.java
@@ -232,8 +232,9 @@
   /// CHECK-NOT: InvokeVirtual intrinsic:StringStringIndexOfAfter
   static int bufferDeadLoop() {
     StringBuffer b = new StringBuffer();
+    String x = "x";
     for (int i = 0; i < 10; i++) {
-      int d = b.toString().indexOf("x", 1);
+      int d = b.toString().indexOf(x, 1);
     }
     return b.length();
   }
@@ -252,8 +253,9 @@
   /// CHECK-NOT: InvokeVirtual intrinsic:StringStringIndexOfAfter
   static int builderDeadLoop() {
     StringBuilder b = new StringBuilder();
+    String x = "x";
     for (int i = 0; i < 10; i++) {
-      int d = b.toString().indexOf("x", 1);
+      int d = b.toString().indexOf(x, 1);
     }
     return b.length();
   }
diff --git a/test/626-const-class-linking/clear_dex_cache_types.cc b/test/626-const-class-linking/clear_dex_cache_types.cc
index ff5ae6b..b35dff4 100644
--- a/test/626-const-class-linking/clear_dex_cache_types.cc
+++ b/test/626-const-class-linking/clear_dex_cache_types.cc
@@ -27,8 +27,7 @@
   ScopedObjectAccess soa(Thread::Current());
   mirror::DexCache* dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache();
   for (size_t i = 0, num_types = dex_cache->NumResolvedTypes(); i != num_types; ++i) {
-    mirror::TypeDexCachePair cleared(nullptr, mirror::TypeDexCachePair::InvalidIndexForSlot(i));
-    dex_cache->GetResolvedTypes()[i].store(cleared, std::memory_order_relaxed);
+    dex_cache->SetResolvedType(dex::TypeIndex(i), ObjPtr<mirror::Class>(nullptr));
   }
 }
 
@@ -50,7 +49,7 @@
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::ObjectArray<mirror::Object>> classes =
       hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Object>>(array));
-  CHECK(classes.Get() != nullptr);
+  CHECK(classes != nullptr);
   for (size_t i = 0, length = classes->GetLength(); i != length; ++i) {
     CHECK(classes->Get(i) != nullptr) << i;
     CHECK(classes->Get(i)->IsClass())
diff --git a/test/637-checker-throw-inline/expected.txt b/test/637-checker-throw-inline/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/637-checker-throw-inline/expected.txt
diff --git a/test/637-checker-throw-inline/info.txt b/test/637-checker-throw-inline/info.txt
new file mode 100644
index 0000000..4fcf6a9
--- /dev/null
+++ b/test/637-checker-throw-inline/info.txt
@@ -0,0 +1 @@
+Test that the compiler can inline methods that throw.
diff --git a/test/637-checker-throw-inline/src/Main.java b/test/637-checker-throw-inline/src/Main.java
new file mode 100644
index 0000000..d4fbdf5
--- /dev/null
+++ b/test/637-checker-throw-inline/src/Main.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+  public static void $inline$doCall() {
+    if (doThrow) throw new Error("");
+  }
+
+  public static void tryInline() {
+    if (doThrow) throw new Error("");
+  }
+
+  /// CHECK-START: void Main.test() inliner (before)
+  /// CHECK:      InvokeStaticOrDirect method_name:Main.$inline$doCall loop:none
+
+  /// CHECK-START: void Main.test() inliner (after)
+  /// CHECK-NOT:  InvokeStaticOrDirect method_name:Main.$inline$doCall
+  public static void test() {
+    $inline$doCall();
+  }
+
+  /// CHECK-START: void Main.testInLoop() inliner (before)
+  /// CHECK:      InvokeStaticOrDirect method_name:Main.$inline$doCall loop:{{B\d+}}
+
+  /// CHECK-START: void Main.testInLoop() inliner (after)
+  /// CHECK-NOT:  InvokeStaticOrDirect method_name:Main.$inline$doCall
+  public static void testInLoop() {
+    for (int i = 0; i < 10; ++i) {
+      $inline$doCall();
+    }
+  }
+
+  /// CHECK-START: void Main.testInInfiniteLoop() inliner (before)
+  /// CHECK:      InvokeStaticOrDirect method_name:Main.tryInline loop:{{B\d+}}
+
+  /// CHECK-START: void Main.testInInfiniteLoop() inliner (after)
+  /// CHECK:      InvokeStaticOrDirect method_name:Main.tryInline loop:{{B\d+}}
+  public static void testInInfiniteLoop() {
+    while (true) {
+      tryInline();
+    }
+  }
+
+  public static void main(String[] args) {
+    test();
+    testInLoop();
+  }
+
+  static boolean doThrow = false;
+}
diff --git a/test/901-hello-ti-agent/build b/test/901-hello-ti-agent/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/901-hello-ti-agent/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/902-hello-transformation/build b/test/902-hello-transformation/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/902-hello-transformation/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/903-hello-tagging/build b/test/903-hello-tagging/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/903-hello-tagging/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/904-object-allocation/build b/test/904-object-allocation/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/904-object-allocation/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/905-object-free/build b/test/905-object-free/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/905-object-free/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/906-iterate-heap/build b/test/906-iterate-heap/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/906-iterate-heap/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/907-get-loaded-classes/build b/test/907-get-loaded-classes/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/907-get-loaded-classes/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/908-gc-start-finish/build b/test/908-gc-start-finish/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/908-gc-start-finish/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/909-attach-agent/build b/test/909-attach-agent/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/909-attach-agent/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/910-methods/build b/test/910-methods/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/910-methods/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/911-get-stack-trace/build b/test/911-get-stack-trace/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/911-get-stack-trace/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt
index 2687f85..feabb20 100644
--- a/test/911-get-stack-trace/expected.txt
+++ b/test/911-get-stack-trace/expected.txt
@@ -211,6 +211,36 @@
 ### Other threads (suspended) ###
 ################################
 ---------
+AllTraces Thread 0
+
+---------
+AllTraces Thread 1
+
+---------
+AllTraces Thread 2
+
+---------
+AllTraces Thread 3
+
+---------
+AllTraces Thread 4
+
+---------
+AllTraces Thread 5
+
+---------
+AllTraces Thread 6
+
+---------
+AllTraces Thread 7
+
+---------
+AllTraces Thread 8
+
+---------
+AllTraces Thread 9
+
+---------
 FinalizerDaemon
 <not printed>
 ---------
@@ -226,39 +256,89 @@
 Signal Catcher
 
 ---------
-Thread-10
-
----------
-Thread-11
-
----------
-Thread-12
-
----------
-Thread-13
-
----------
-Thread-4
-
----------
-Thread-5
-
----------
-Thread-6
-
----------
-Thread-7
-
----------
-Thread-8
-
----------
-Thread-9
-
----------
 main
 
 ---------
+AllTraces Thread 0
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+
+---------
+AllTraces Thread 1
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+
+---------
+AllTraces Thread 2
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+
+---------
+AllTraces Thread 3
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+
+---------
+AllTraces Thread 4
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+
+---------
+AllTraces Thread 5
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+
+---------
+AllTraces Thread 6
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+
+---------
+AllTraces Thread 7
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+
+---------
+AllTraces Thread 8
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+
+---------
+AllTraces Thread 9
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+
+---------
 FinalizerDaemon
 <not printed>
 ---------
@@ -274,93 +354,223 @@
 Signal Catcher
 
 ---------
-Thread-10
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
-
----------
-Thread-11
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
-
----------
-Thread-12
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
-
----------
-Thread-13
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
-
----------
-Thread-4
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
-
----------
-Thread-5
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
-
----------
-Thread-6
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
-
----------
-Thread-7
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
-
----------
-Thread-8
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
-
----------
-Thread-9
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
-
----------
 main
  getAllStackTraces (I)[[Ljava/lang/Object; -1 -2
  printAll (I)V 0 73
- doTest ()V 102 57
+ doTest ()V 128 57
  main ([Ljava/lang/String;)V 27 33
 
 ---------
+AllTraces Thread 0
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ run ()V 4 45
+
+---------
+AllTraces Thread 1
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ run ()V 4 45
+
+---------
+AllTraces Thread 2
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ run ()V 4 45
+
+---------
+AllTraces Thread 3
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ run ()V 4 45
+
+---------
+AllTraces Thread 4
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ run ()V 4 45
+
+---------
+AllTraces Thread 5
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ run ()V 4 45
+
+---------
+AllTraces Thread 6
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ run ()V 4 45
+
+---------
+AllTraces Thread 7
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ run ()V 4 45
+
+---------
+AllTraces Thread 8
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ run ()V 4 45
+
+---------
+AllTraces Thread 9
+ wait ()V -1 -2
+ printOrWait (IILControlData;)V 24 45
+ baz (IIILControlData;)Ljava/lang/Object; 2 30
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ baz (IIILControlData;)Ljava/lang/Object; 9 32
+ bar (IIILControlData;)J 0 24
+ foo (IIILControlData;)I 0 19
+ run ()V 4 45
+
+---------
 FinalizerDaemon
 <not printed>
 ---------
@@ -376,220 +586,10 @@
 Signal Catcher
 
 ---------
-Thread-10
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- run ()V 4 45
-
----------
-Thread-11
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- run ()V 4 45
-
----------
-Thread-12
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- run ()V 4 45
-
----------
-Thread-13
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- run ()V 4 45
-
----------
-Thread-4
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- run ()V 4 45
-
----------
-Thread-5
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- run ()V 4 45
-
----------
-Thread-6
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- run ()V 4 45
-
----------
-Thread-7
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- run ()V 4 45
-
----------
-Thread-8
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- run ()V 4 45
-
----------
-Thread-9
- wait ()V -1 -2
- printOrWait (IILControlData;)V 24 45
- baz (IIILControlData;)Ljava/lang/Object; 2 30
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- baz (IIILControlData;)Ljava/lang/Object; 9 32
- bar (IIILControlData;)J 0 24
- foo (IIILControlData;)I 0 19
- run ()V 4 45
-
----------
 main
  getAllStackTraces (I)[[Ljava/lang/Object; -1 -2
  printAll (I)V 0 73
- doTest ()V 107 59
+ doTest ()V 133 59
  main ([Ljava/lang/String;)V 27 33
 
 
@@ -597,25 +597,25 @@
 ### Other select threads (suspended) ###
 ########################################
 ---------
-Thread-14
+ThreadListTraces Thread 0
 
 ---------
-Thread-16
+ThreadListTraces Thread 2
 
 ---------
-Thread-18
+ThreadListTraces Thread 4
 
 ---------
-Thread-20
+ThreadListTraces Thread 6
 
 ---------
-Thread-22
+ThreadListTraces Thread 8
 
 ---------
 main
 
 ---------
-Thread-14
+ThreadListTraces Thread 0
  wait ()V -1 -2
  printOrWait (IILControlData;)V 24 45
  baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -623,7 +623,7 @@
  foo (IIILControlData;)I 0 19
 
 ---------
-Thread-16
+ThreadListTraces Thread 2
  wait ()V -1 -2
  printOrWait (IILControlData;)V 24 45
  baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -631,7 +631,7 @@
  foo (IIILControlData;)I 0 19
 
 ---------
-Thread-18
+ThreadListTraces Thread 4
  wait ()V -1 -2
  printOrWait (IILControlData;)V 24 45
  baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -639,7 +639,7 @@
  foo (IIILControlData;)I 0 19
 
 ---------
-Thread-20
+ThreadListTraces Thread 6
  wait ()V -1 -2
  printOrWait (IILControlData;)V 24 45
  baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -647,7 +647,7 @@
  foo (IIILControlData;)I 0 19
 
 ---------
-Thread-22
+ThreadListTraces Thread 8
  wait ()V -1 -2
  printOrWait (IILControlData;)V 24 45
  baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -658,11 +658,11 @@
 main
  getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2
  printList ([Ljava/lang/Thread;I)V 0 66
- doTest ()V 96 52
+ doTest ()V 116 52
  main ([Ljava/lang/String;)V 35 37
 
 ---------
-Thread-14
+ThreadListTraces Thread 0
  wait ()V -1 -2
  printOrWait (IILControlData;)V 24 45
  baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -683,7 +683,7 @@
  run ()V 4 35
 
 ---------
-Thread-16
+ThreadListTraces Thread 2
  wait ()V -1 -2
  printOrWait (IILControlData;)V 24 45
  baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -704,7 +704,7 @@
  run ()V 4 35
 
 ---------
-Thread-18
+ThreadListTraces Thread 4
  wait ()V -1 -2
  printOrWait (IILControlData;)V 24 45
  baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -725,7 +725,7 @@
  run ()V 4 35
 
 ---------
-Thread-20
+ThreadListTraces Thread 6
  wait ()V -1 -2
  printOrWait (IILControlData;)V 24 45
  baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -746,7 +746,7 @@
  run ()V 4 35
 
 ---------
-Thread-22
+ThreadListTraces Thread 8
  wait ()V -1 -2
  printOrWait (IILControlData;)V 24 45
  baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -770,7 +770,7 @@
 main
  getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2
  printList ([Ljava/lang/Thread;I)V 0 66
- doTest ()V 101 54
+ doTest ()V 121 54
  main ([Ljava/lang/String;)V 35 37
 
 
diff --git a/test/911-get-stack-trace/src/AllTraces.java b/test/911-get-stack-trace/src/AllTraces.java
index adf6f38..1d9aa96 100644
--- a/test/911-get-stack-trace/src/AllTraces.java
+++ b/test/911-get-stack-trace/src/AllTraces.java
@@ -26,8 +26,8 @@
     System.out.println("################################");
 
     // Also create an unstarted and a dead thread.
-    RETAIN.add(new Thread());
-    Thread deadThread = new Thread();
+    RETAIN.add(new Thread("UNSTARTED"));
+    Thread deadThread = new Thread("DEAD");
     RETAIN.add(deadThread);
     deadThread.start();
     deadThread.join();
@@ -40,7 +40,7 @@
     Thread threads[] = new Thread[N];
 
     for (int i = 0; i < N; i++) {
-      Thread t = new Thread() {
+      Thread t = new Thread("AllTraces Thread " + i) {
         public void run() {
           Recurse.foo(4, 0, 0, data);
         }
diff --git a/test/911-get-stack-trace/src/Frames.java b/test/911-get-stack-trace/src/Frames.java
index a1a11c3..54d4165 100644
--- a/test/911-get-stack-trace/src/Frames.java
+++ b/test/911-get-stack-trace/src/Frames.java
@@ -59,7 +59,7 @@
     System.out.println("################################");
     final ControlData data = new ControlData();
     data.waitFor = new Object();
-    Thread t = new Thread() {
+    Thread t = new Thread("Frames doTestOtherThreadWait") {
       public void run() {
         Recurse.foo(4, 0, 0, data);
       }
@@ -97,7 +97,7 @@
     System.out.println("### Other thread (live) ###");
     System.out.println("###########################");
     final ControlData data = new ControlData();
-    Thread t = new Thread() {
+    Thread t = new Thread("Frames doTestOtherThreadBusyLoop") {
       public void run() {
         Recurse.foo(4, 0, 0, data);
       }
diff --git a/test/911-get-stack-trace/src/OtherThread.java b/test/911-get-stack-trace/src/OtherThread.java
index 0748433..0a78523 100644
--- a/test/911-get-stack-trace/src/OtherThread.java
+++ b/test/911-get-stack-trace/src/OtherThread.java
@@ -21,7 +21,7 @@
     System.out.println("################################");
     final ControlData data = new ControlData();
     data.waitFor = new Object();
-    Thread t = new Thread() {
+    Thread t = new Thread("OtherThread doTestOtherThreadWait") {
       public void run() {
         Recurse.foo(4, 0, 0, data);
       }
@@ -54,7 +54,7 @@
     System.out.println("### Other thread (live) ###");
     System.out.println("###########################");
     final ControlData data = new ControlData();
-    Thread t = new Thread() {
+    Thread t = new Thread("OtherThread doTestOtherThreadBusyLoop") {
       public void run() {
         Recurse.foo(4, 0, 0, data);
       }
diff --git a/test/911-get-stack-trace/src/ThreadListTraces.java b/test/911-get-stack-trace/src/ThreadListTraces.java
index f66557f..14868e9 100644
--- a/test/911-get-stack-trace/src/ThreadListTraces.java
+++ b/test/911-get-stack-trace/src/ThreadListTraces.java
@@ -30,7 +30,7 @@
     Thread list[] = new Thread[N/2 + 1];
 
     for (int i = 0; i < N; i++) {
-      Thread t = new Thread() {
+      Thread t = new Thread("ThreadListTraces Thread " + i) {
         public void run() {
           Recurse.foo(4, 0, 0, data);
         }
diff --git a/test/912-classes/build b/test/912-classes/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/912-classes/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index c92e49f..3ccfe86 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -433,9 +433,13 @@
 class ClassLoadPrepareEquality {
  public:
   static constexpr const char* kClassName = "LMain$ClassE;";
-  static constexpr const char* kStorageClassName = "Main$ClassF";
   static constexpr const char* kStorageFieldName = "STATIC";
   static constexpr const char* kStorageFieldSig = "Ljava/lang/Object;";
+  static constexpr const char* kStorageWeakFieldName = "WEAK";
+  static constexpr const char* kStorageWeakFieldSig = "Ljava/lang/ref/Reference;";
+  static constexpr const char* kWeakClassName = "java/lang/ref/WeakReference";
+  static constexpr const char* kWeakInitSig = "(Ljava/lang/Object;)V";
+  static constexpr const char* kWeakGetSig = "()Ljava/lang/Object;";
 
   static void JNICALL ClassLoadCallback(jvmtiEnv* jenv,
                                         JNIEnv* jni_env,
@@ -472,6 +476,8 @@
 
   static void SetOrCompare(JNIEnv* jni_env, jobject value, bool set) {
     CHECK(storage_class_ != nullptr);
+
+    // Simple direct storage.
     jfieldID field = jni_env->GetStaticFieldID(storage_class_, kStorageFieldName, kStorageFieldSig);
     CHECK(field != nullptr);
 
@@ -482,6 +488,36 @@
       ScopedLocalRef<jobject> stored(jni_env, jni_env->GetStaticObjectField(storage_class_, field));
       CHECK(jni_env->IsSameObject(value, stored.get()));
     }
+
+    // Storage as a reference.
+    ScopedLocalRef<jclass> weak_ref_class(jni_env, jni_env->FindClass(kWeakClassName));
+    CHECK(weak_ref_class.get() != nullptr);
+    jfieldID weak_field = jni_env->GetStaticFieldID(storage_class_,
+                                                    kStorageWeakFieldName,
+                                                    kStorageWeakFieldSig);
+    CHECK(weak_field != nullptr);
+    if (set) {
+      // Create a WeakReference.
+      jmethodID weak_init = jni_env->GetMethodID(weak_ref_class.get(), "<init>", kWeakInitSig);
+      CHECK(weak_init != nullptr);
+      ScopedLocalRef<jobject> weak_obj(jni_env, jni_env->NewObject(weak_ref_class.get(),
+                                                                   weak_init,
+                                                                   value));
+      CHECK(weak_obj.get() != nullptr);
+      jni_env->SetStaticObjectField(storage_class_, weak_field, weak_obj.get());
+      CHECK(!jni_env->ExceptionCheck());
+    } else {
+      // Check the reference value.
+      jmethodID get_referent = jni_env->GetMethodID(weak_ref_class.get(), "get", kWeakGetSig);
+      CHECK(get_referent != nullptr);
+      ScopedLocalRef<jobject> weak_obj(jni_env, jni_env->GetStaticObjectField(storage_class_,
+                                                                              weak_field));
+      CHECK(weak_obj.get() != nullptr);
+      ScopedLocalRef<jobject> weak_referent(jni_env, jni_env->CallObjectMethod(weak_obj.get(),
+                                                                               get_referent));
+      CHECK(weak_referent.get() != nullptr);
+      CHECK(jni_env->IsSameObject(value, weak_referent.get()));
+    }
   }
 
   static void CheckFound() {
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
index 52a5194..005074f 100644
--- a/test/912-classes/src/Main.java
+++ b/test/912-classes/src/Main.java
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+import java.lang.ref.Reference;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Proxy;
 import java.util.Arrays;
@@ -433,6 +434,7 @@
 
   public static class ClassF {
     public static Object STATIC = null;
+    public static Reference<Object> WEAK = null;
   }
 
   private static final String DEX1 = System.getenv("DEX_LOCATION") + "/912-classes.jar";
diff --git a/test/913-heaps/build b/test/913-heaps/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/913-heaps/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/914-hello-obsolescence/build b/test/914-hello-obsolescence/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/914-hello-obsolescence/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/915-obsolete-2/build b/test/915-obsolete-2/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/915-obsolete-2/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/916-obsolete-jit/build b/test/916-obsolete-jit/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/916-obsolete-jit/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/917-fields-transformation/build b/test/917-fields-transformation/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/917-fields-transformation/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/918-fields/build b/test/918-fields/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/918-fields/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/919-obsolete-fields/build b/test/919-obsolete-fields/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/919-obsolete-fields/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/920-objects/build b/test/920-objects/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/920-objects/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/921-hello-failure/build b/test/921-hello-failure/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/921-hello-failure/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/922-properties/build b/test/922-properties/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/922-properties/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/923-monitors/build b/test/923-monitors/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/923-monitors/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/924-threads/build b/test/924-threads/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/924-threads/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/925-threadgroups/build b/test/925-threadgroups/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/925-threadgroups/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/926-multi-obsolescence/build b/test/926-multi-obsolescence/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/926-multi-obsolescence/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/927-timers/build b/test/927-timers/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/927-timers/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/928-jni-table/build b/test/928-jni-table/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/928-jni-table/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/929-search/build b/test/929-search/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/929-search/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/930-hello-retransform/build b/test/930-hello-retransform/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/930-hello-retransform/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/931-agent-thread/build b/test/931-agent-thread/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/931-agent-thread/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/932-transform-saves/build b/test/932-transform-saves/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/932-transform-saves/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/933-misc-events/build b/test/933-misc-events/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/933-misc-events/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/934-load-transform/build b/test/934-load-transform/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/934-load-transform/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/935-non-retransformable/build b/test/935-non-retransformable/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/935-non-retransformable/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/936-search-onload/build b/test/936-search-onload/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/936-search-onload/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/937-hello-retransform-package/build b/test/937-hello-retransform-package/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/937-hello-retransform-package/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/938-load-transform-bcp/build b/test/938-load-transform-bcp/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/938-load-transform-bcp/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/939-hello-transformation-bcp/build b/test/939-hello-transformation-bcp/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/939-hello-transformation-bcp/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/940-recursive-obsolete/build b/test/940-recursive-obsolete/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/940-recursive-obsolete/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/941-recurive-obsolete-jit/build b/test/941-recurive-obsolete-jit/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/941-recurive-obsolete-jit/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/942-private-recursive/build b/test/942-private-recursive/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/942-private-recursive/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/943-private-recursive-jit/build b/test/943-private-recursive-jit/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/943-private-recursive-jit/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/944-transform-classloaders/build b/test/944-transform-classloaders/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/944-transform-classloaders/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/945-obsolete-native/expected.txt b/test/945-obsolete-native/expected.txt
new file mode 100644
index 0000000..83efda1
--- /dev/null
+++ b/test/945-obsolete-native/expected.txt
@@ -0,0 +1,9 @@
+hello
+Not doing anything here
+goodbye
+hello
+transforming calling function
+goodbye
+Hello - Transformed
+Not doing anything here
+Goodbye - Transformed
diff --git a/test/945-obsolete-native/info.txt b/test/945-obsolete-native/info.txt
new file mode 100644
index 0000000..c8b892c
--- /dev/null
+++ b/test/945-obsolete-native/info.txt
@@ -0,0 +1 @@
+Tests basic obsolete method support
diff --git a/test/945-obsolete-native/obsolete_native.cc b/test/945-obsolete-native/obsolete_native.cc
new file mode 100644
index 0000000..061e7af
--- /dev/null
+++ b/test/945-obsolete-native/obsolete_native.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <memory>
+#include <stdio.h>
+
+#include "android-base/stringprintf.h"
+
+#include "android-base/stringprintf.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedLocalRef.h"
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test945ObsoleteNative {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_bindTest945ObsoleteNative(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+  BindFunctions(jvmti_env, env, "Transform");
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Transform_doExecute(JNIEnv* env,
+                                                           jclass klass ATTRIBUTE_UNUSED,
+                                                           jobject runnable) {
+  jclass runnable_klass = env->FindClass("java/lang/Runnable");
+  DCHECK(runnable_klass != nullptr);
+  jmethodID run_method = env->GetMethodID(runnable_klass, "run", "()V");
+  env->CallVoidMethod(runnable, run_method);
+}
+
+
+}  // namespace Test945ObsoleteNative
+}  // namespace art
diff --git a/test/900-hello-plugin/build b/test/945-obsolete-native/run
similarity index 92%
rename from test/900-hello-plugin/build
rename to test/945-obsolete-native/run
index 898e2e5..c6e62ae 100755
--- a/test/900-hello-plugin/build
+++ b/test/945-obsolete-native/run
@@ -14,4 +14,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-./default-build "$@" --experimental agents
+./default-run "$@" --jvmti
diff --git a/test/945-obsolete-native/src/Main.java b/test/945-obsolete-native/src/Main.java
new file mode 100644
index 0000000..5e2154e
--- /dev/null
+++ b/test/945-obsolete-native/src/Main.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+
+public class Main {
+  // class Transform {
+  //   public void sayHi(Runnable r) {
+  //     System.out.println("Hello - Transformed");
+  //     doExecute(r);
+  //     System.out.println("Goodbye - Transformed");
+  //   }
+  //
+  //   private static native void doExecute(Runnable r);
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAIgoACAASCQATABQIABUKABYAFwoABwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
+    "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+    "KVYBAAlkb0V4ZWN1dGUBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAe" +
+    "AQATSGVsbG8gLSBUcmFuc2Zvcm1lZAcAHwwAIAAhDAAPAA4BABVHb29kYnllIC0gVHJhbnNmb3Jt" +
+    "ZWQBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291" +
+    "dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxu" +
+    "AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABwAIAAAAAAADAAAACQAKAAEACwAAAB0AAQABAAAA" +
+    "BSq3AAGxAAAAAQAMAAAABgABAAAAEQABAA0ADgABAAsAAAA5AAIAAgAAABWyAAISA7YABCu4AAWy" +
+    "AAISBrYABLEAAAABAAwAAAASAAQAAAATAAgAFAAMABUAFAAWAQoADwAOAAAAAQAQAAAAAgAR");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQB1fZcJR/opPuXacK8mIla5shH0LSg72qJYAwAAcAAAAHhWNBIAAAAAAAAAALgCAAAR" +
+    "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAUAgAARAEAAKIB" +
+    "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAABuAgAAggIA" +
+    "AIcCAACQAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
+    "lAEAAAsAAAAGAAAAnAEAAAUAAQAOAAAAAAAAAAAAAAAAAAEADAAAAAAAAQAQAAAAAQACAA8AAAAC" +
+    "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAKUCAAAAAAAAAQABAAEAAACXAgAABAAAAHAQ" +
+    "BAAAAA4ABAACAAIAAACcAgAAFAAAAGIAAAAbAQIAAABuIAMAEABxEAEAAwBiAAAAGwEBAAAAbiAD" +
+    "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" +
+    "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" +
+    "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" +
+    "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAAJZG9FeGVjdXRlABJlbWl0" +
+    "dGVyOiBqYWNrLTQuMjUAA291dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAQAHDoc8hwAAAAIBAICA" +
+    "BMQCAYoCAAIB3AIADQAAAAAAAAABAAAAAAAAAAEAAAARAAAAcAAAAAIAAAAHAAAAtAAAAAMAAAAD" +
+    "AAAA0AAAAAQAAAABAAAA9AAAAAUAAAAFAAAA/AAAAAYAAAABAAAAJAEAAAEgAAACAAAARAEAAAEQ" +
+    "AAACAAAAlAEAAAIgAAARAAAAogEAAAMgAAACAAAAlwIAAAAgAAABAAAApQIAAAAQAAABAAAAuAIA" +
+    "AA==");
+
+  public static void main(String[] args) {
+    bindTest945ObsoleteNative();
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+    t.sayHi(() -> {
+      System.out.println("transforming calling function");
+      doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    });
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+  }
+
+  // Transforms the class
+  private static native void doCommonClassRedefinition(Class<?> target,
+                                                       byte[] classfile,
+                                                       byte[] dexfile);
+
+  private static native void bindTest945ObsoleteNative();
+}
diff --git a/test/945-obsolete-native/src/Transform.java b/test/945-obsolete-native/src/Transform.java
new file mode 100644
index 0000000..2b7cc1b
--- /dev/null
+++ b/test/945-obsolete-native/src/Transform.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Transform {
+  public void sayHi(Runnable r) {
+    System.out.println("hello");
+    doExecute(r);
+    System.out.println("goodbye");
+  }
+
+  private static native void doExecute(Runnable r);
+}
diff --git a/test/946-obsolete-throw/expected.txt b/test/946-obsolete-throw/expected.txt
new file mode 100644
index 0000000..91dd7df
--- /dev/null
+++ b/test/946-obsolete-throw/expected.txt
@@ -0,0 +1,14 @@
+hello
+Not doing anything here
+goodbye
+hello
+transforming calling function
+Received error : java.lang.Error: Throwing exception into an obsolete method!
+java.lang.Error: Throwing exception into an obsolete method!
+	at Main$DoRedefinitionClass.run(Main.java:64)
+	at Transform.sayHi(Transform.java:27)
+	at Main.doTest(Main.java:71)
+	at Main.main(Main.java:56)
+Hello - Transformed
+Not doing anything here
+Goodbye - Transformed
diff --git a/test/946-obsolete-throw/info.txt b/test/946-obsolete-throw/info.txt
new file mode 100644
index 0000000..7b7a63d
--- /dev/null
+++ b/test/946-obsolete-throw/info.txt
@@ -0,0 +1,3 @@
+Tests basic obsolete method support
+
+Tests that we correctly handle exceptions thrown through obsolete methods.
diff --git a/test/900-hello-plugin/build b/test/946-obsolete-throw/run
similarity index 85%
copy from test/900-hello-plugin/build
copy to test/946-obsolete-throw/run
index 898e2e5..e92b873 100755
--- a/test/900-hello-plugin/build
+++ b/test/946-obsolete-throw/run
@@ -1,6 +1,6 @@
 #!/bin/bash
 #
-# Copyright 2016 The Android Open Source Project
+# Copyright 2017 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,4 +14,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-./default-build "$@" --experimental agents
+./default-run "$@" --jvmti
diff --git a/test/946-obsolete-throw/src/Main.java b/test/946-obsolete-throw/src/Main.java
new file mode 100644
index 0000000..3ff97ae
--- /dev/null
+++ b/test/946-obsolete-throw/src/Main.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+
+public class Main {
+  // class Transform {
+  //   public void sayHi(Runnable r) {
+  //     System.out.println("Hello - Transformed");
+  //     r.run();
+  //     System.out.println("Goodbye - Transformed");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAJAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
+    "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+    "KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAeAQATSGVsbG8gLSBU" +
+    "cmFuc2Zvcm1lZAcAHwwAIAAhBwAiDAAjAAoBABVHb29kYnllIC0gVHJhbnNmb3JtZWQBAAlUcmFu" +
+    "c2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZh" +
+    "L2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZh" +
+    "L2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAABwAIAAAAAAACAAAA" +
+    "CQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAAAQABAA0ADgABAAsAAAA7AAIA" +
+    "AgAAABeyAAISA7YABCu5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAMACAAEAA4ABQAWAAYA" +
+    "AQAPAAAAAgAQ");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQAYeAMMXgYWxoeSHAS9EWKCCtVRSAGpqZVQAwAAcAAAAHhWNBIAAAAAAAAAALACAAAR" +
+    "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAMAgAARAEAAKIB" +
+    "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAAB3AgAAfAIA" +
+    "AIUCAACKAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
+    "lAEAAAsAAAAGAAAAnAEAAAUAAQANAAAAAAAAAAAAAAAAAAEAEAAAAAEAAgAOAAAAAgAAAAAAAAAD" +
+    "AAAADwAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAJ8CAAAAAAAAAQABAAEAAACRAgAABAAAAHAQ" +
+    "AwAAAA4ABAACAAIAAACWAgAAFAAAAGIAAAAbAQIAAABuIAIAEAByEAQAAwBiAAAAGwEBAAAAbiAC" +
+    "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" +
+    "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" +
+    "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" +
+    "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00" +
+    "LjEzAANvdXQAB3ByaW50bG4AA3J1bgAFc2F5SGkAAQAHDgADAQAHDoc8hwAAAAEBAICABMQCAQHc" +
+    "AgAAAA0AAAAAAAAAAQAAAAAAAAABAAAAEQAAAHAAAAACAAAABwAAALQAAAADAAAAAwAAANAAAAAE" +
+    "AAAAAQAAAPQAAAAFAAAABQAAAPwAAAAGAAAAAQAAACQBAAABIAAAAgAAAEQBAAABEAAAAgAAAJQB" +
+    "AAACIAAAEQAAAKIBAAADIAAAAgAAAJECAAAAIAAAAQAAAJ8CAAAAEAAAAQAAALACAAA=");
+
+  public static void main(String[] args) {
+    doTest(new Transform());
+  }
+
+  static class DoRedefinitionClass implements Runnable {
+    @Override
+    public void run() {
+      System.out.println("transforming calling function");
+      doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+      throw new Error("Throwing exception into an obsolete method!");
+    }
+  }
+
+  public static void doTest(Transform t) {
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+    try {
+      t.sayHi(new DoRedefinitionClass());
+    } catch (Throwable e) {
+      System.out.println("Received error : " + e);
+      e.printStackTrace();
+    }
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+  }
+
+  // Transforms the class
+  private static native void doCommonClassRedefinition(Class<?> target,
+                                                       byte[] classfile,
+                                                       byte[] dexfile);
+}
diff --git a/test/946-obsolete-throw/src/Transform.java b/test/946-obsolete-throw/src/Transform.java
new file mode 100644
index 0000000..4f43086
--- /dev/null
+++ b/test/946-obsolete-throw/src/Transform.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Transform {
+  public void sayHi(Runnable r) {
+    // Use lower 'h' to make sure the string will have a different string id
+    // than the transformation (the transformation code is the same except
+    // the actual printed String, which was making the test inacurately passing
+    // in JIT mode when loading the string from the dex cache, as the string ids
+    // of the two different strings were the same).
+    // We know the string ids will be different because lexicographically:
+    // "Hello" < "LTransform;" < "hello".
+    System.out.println("hello");
+    r.run();
+    System.out.println("goodbye");
+  }
+}
diff --git a/test/947-reflect-method/expected.txt b/test/947-reflect-method/expected.txt
new file mode 100644
index 0000000..4774b81
--- /dev/null
+++ b/test/947-reflect-method/expected.txt
@@ -0,0 +1,2 @@
+hello
+Goodbye
diff --git a/test/947-reflect-method/info.txt b/test/947-reflect-method/info.txt
new file mode 100644
index 0000000..f3d03c1
--- /dev/null
+++ b/test/947-reflect-method/info.txt
@@ -0,0 +1,4 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that we are able to use java/lang/reflect/Method objects to invoke methods
+that have been redefined.
diff --git a/test/900-hello-plugin/build b/test/947-reflect-method/run
similarity index 85%
copy from test/900-hello-plugin/build
copy to test/947-reflect-method/run
index 898e2e5..e92b873 100755
--- a/test/900-hello-plugin/build
+++ b/test/947-reflect-method/run
@@ -1,6 +1,6 @@
 #!/bin/bash
 #
-# Copyright 2016 The Android Open Source Project
+# Copyright 2017 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,4 +14,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-./default-build "$@" --experimental agents
+./default-run "$@" --jvmti
diff --git a/test/947-reflect-method/src/Main.java b/test/947-reflect-method/src/Main.java
new file mode 100644
index 0000000..a229dd4
--- /dev/null
+++ b/test/947-reflect-method/src/Main.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+import java.lang.reflect.Method;
+
+public class Main {
+
+  /**
+   * base64 encoded class/dex file for
+   * class Transform {
+   *   public void sayHi() {
+   *    System.out.println("Goodbye");
+   *   }
+   * }
+   */
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
+    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
+    "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" +
+    "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" +
+    "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG" +
+    "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" +
+    "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0=");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
+    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
+    "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
+    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
+    "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
+    "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
+    "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
+    "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" +
+    "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
+    "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
+    "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
+    "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
+
+  public static void main(String[] args) {
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    try {
+      Method say_hi_method = t.getClass().getDeclaredMethod("sayHi");
+      say_hi_method.invoke(t);
+      doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+      say_hi_method.invoke(t);
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+
+  // Transforms the class
+  private static native void doCommonClassRedefinition(Class<?> target,
+                                                       byte[] class_file,
+                                                       byte[] dex_file);
+}
diff --git a/test/947-reflect-method/src/Transform.java b/test/947-reflect-method/src/Transform.java
new file mode 100644
index 0000000..b8fe34a
--- /dev/null
+++ b/test/947-reflect-method/src/Transform.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Transform {
+  public void sayHi() {
+    // Use lower 'h' to make sure the string will have a different string id
+    // than the transformation (the transformation code is the same except
+    // the actual printed String, which was making the test inacurately passing
+    // in JIT mode when loading the string from the dex cache, as the string ids
+    // of the two different strings were the same).
+    // We know the string ids will be different because lexicographically:
+    // "Goodbye" < "LTransform;" < "hello".
+    System.out.println("hello");
+  }
+}
diff --git a/test/900-hello-plugin/build b/test/952-invoke-custom/build
old mode 100755
new mode 100644
similarity index 77%
copy from test/900-hello-plugin/build
copy to test/952-invoke-custom/build
index 898e2e5..a423ca6
--- a/test/900-hello-plugin/build
+++ b/test/952-invoke-custom/build
@@ -14,4 +14,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-./default-build "$@" --experimental agents
+# make us exit on a failure
+set -e
+
+if [[ $@ != *"--jvm"* ]]; then
+  # Don't do anything with jvm.
+  export USE_JACK=true
+fi
+
+./default-build "$@" --experimental method-handles
diff --git a/test/952-invoke-custom/expected.txt b/test/952-invoke-custom/expected.txt
new file mode 100644
index 0000000..bb87296
--- /dev/null
+++ b/test/952-invoke-custom/expected.txt
@@ -0,0 +1,14 @@
+Caught exception from uninitialized call site
+Caught exception from uninitialized call site
+linkerMethod failure type 1
+Returning null instead of CallSite for add (int,int)int
+linkerMethod failure type 2
+Throwing InstantiationException in linkerMethod()
+linkerMethod failure type 3
+Throwing ArithmeticException in add()
+Failure Type + 0 (1013)
+Linking add (int,int)int
+100
+-9000
+9000
+Winners 1 Votes 16
diff --git a/test/952-invoke-custom/generator/TestInvokeCustomWithConcurrentThreads.java b/test/952-invoke-custom/generator/TestInvokeCustomWithConcurrentThreads.java
new file mode 100644
index 0000000..9c0645b
--- /dev/null
+++ b/test/952-invoke-custom/generator/TestInvokeCustomWithConcurrentThreads.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.android.jack.annotations.CalledByInvokeCustom;
+import com.android.jack.annotations.Constant;
+import com.android.jack.annotations.LinkerMethodHandle;
+import com.android.jack.annotations.MethodHandleKind;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+import java.lang.Thread;
+import java.lang.ThreadLocal;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.CyclicBarrier;
+
+public class TestInvokeCustomWithConcurrentThreads extends Thread {
+  private static final int NUMBER_OF_THREADS = 16;
+
+  private static final AtomicInteger nextIndex = new AtomicInteger(0);
+
+  private static final ThreadLocal<Integer> threadIndex =
+      new ThreadLocal<Integer>() {
+        @Override
+        protected Integer initialValue() {
+          return nextIndex.getAndIncrement();
+        }
+      };
+
+  // Array of call sites instantiated, one per thread
+  private static final CallSite[] instantiated = new CallSite[NUMBER_OF_THREADS];
+
+  // Array of counters for how many times each instantiated call site is called
+  private static final AtomicInteger[] called = new AtomicInteger[NUMBER_OF_THREADS];
+
+  // Array of call site indicies of which call site a thread invoked
+  private static final AtomicInteger[] targetted = new AtomicInteger[NUMBER_OF_THREADS];
+
+  // Synchronization barrier all threads will wait on in the bootstrap method.
+  private static final CyclicBarrier barrier = new CyclicBarrier(NUMBER_OF_THREADS);
+
+  private TestInvokeCustomWithConcurrentThreads() {}
+
+  private static int getThreadIndex() {
+    return threadIndex.get().intValue();
+  }
+
+  public static int notUsed(int x) {
+    return x;
+  }
+
+  @Override
+  public void run() {
+    int x = setCalled(-1 /* argument dropped */);
+    notUsed(x);
+  }
+
+  @CalledByInvokeCustom(
+      invokeMethodHandle = @LinkerMethodHandle(kind = MethodHandleKind.INVOKE_STATIC,
+          enclosingType = TestInvokeCustomWithConcurrentThreads.class,
+          name = "linkerMethod",
+          argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class}),
+      name = "setCalled",
+      returnType = int.class,
+      argumentTypes = {int.class})
+  private static int setCalled(int index) {
+    called[index].getAndIncrement();
+    targetted[getThreadIndex()].set(index);
+    return 0;
+  }
+
+  @SuppressWarnings("unused")
+  private static CallSite linkerMethod(MethodHandles.Lookup caller,
+                                       String name,
+                                       MethodType methodType) throws Throwable {
+    int threadIndex = getThreadIndex();
+    MethodHandle mh =
+        caller.findStatic(TestInvokeCustomWithConcurrentThreads.class, name, methodType);
+    assertEquals(methodType, mh.type());
+    assertEquals(mh.type().parameterCount(), 1);
+    mh = MethodHandles.insertArguments(mh, 0, threadIndex);
+    mh = MethodHandles.dropArguments(mh, 0, int.class);
+    assertEquals(mh.type().parameterCount(), 1);
+    assertEquals(methodType, mh.type());
+
+    // Wait for all threads to be in this method.
+    // Multiple call sites should be created, but only one
+    // invoked.
+    barrier.await();
+
+    instantiated[getThreadIndex()] = new ConstantCallSite(mh);
+    return instantiated[getThreadIndex()];
+  }
+
+  public static void test() throws Throwable {
+    // Initialize counters for which call site gets invoked
+    for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+      called[i] = new AtomicInteger(0);
+      targetted[i] = new AtomicInteger(0);
+    }
+
+    // Run threads that each invoke-custom the call site
+    Thread [] threads = new Thread[NUMBER_OF_THREADS];
+    for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+      threads[i] = new TestInvokeCustomWithConcurrentThreads();
+      threads[i].start();
+    }
+
+    // Wait for all threads to complete
+    for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+      threads[i].join();
+    }
+
+    // Check one call site instance won
+    int winners = 0;
+    int votes = 0;
+    for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+      assertNotEquals(instantiated[i], null);
+      if (called[i].get() != 0) {
+        winners++;
+        votes += called[i].get();
+      }
+    }
+
+    System.out.println("Winners " + winners + " Votes " + votes);
+
+    // We assert this below but output details when there's an error as
+    // it's non-deterministic.
+    if (winners != 1) {
+      System.out.println("Threads did not the same call-sites:");
+      for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+        System.out.format(" Thread % 2d invoked call site instance #%02d\n",
+                          i, targetted[i].get());
+      }
+    }
+
+    // We assert this below but output details when there's an error as
+    // it's non-deterministic.
+    if (votes != NUMBER_OF_THREADS) {
+      System.out.println("Call-sites invocations :");
+      for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+        System.out.format(" Call site instance #%02d was invoked % 2d times\n",
+                          i, called[i].get());
+      }
+    }
+
+    assertEquals(winners, 1);
+    assertEquals(votes, NUMBER_OF_THREADS);
+  }
+
+  public static void assertTrue(boolean value) {
+    if (!value) {
+      throw new AssertionError("assertTrue value: " + value);
+    }
+  }
+
+  public static void assertEquals(byte b1, byte b2) {
+    if (b1 == b2) { return; }
+    throw new AssertionError("assertEquals b1: " + b1 + ", b2: " + b2);
+  }
+
+  public static void assertEquals(char c1, char c2) {
+    if (c1 == c2) { return; }
+    throw new AssertionError("assertEquals c1: " + c1 + ", c2: " + c2);
+  }
+
+  public static void assertEquals(short s1, short s2) {
+    if (s1 == s2) { return; }
+    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+  }
+
+  public static void assertEquals(int i1, int i2) {
+    if (i1 == i2) { return; }
+    throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
+  }
+
+  public static void assertEquals(long l1, long l2) {
+    if (l1 == l2) { return; }
+    throw new AssertionError("assertEquals l1: " + l1 + ", l2: " + l2);
+  }
+
+  public static void assertEquals(float f1, float f2) {
+    if (f1 == f2) { return; }
+    throw new AssertionError("assertEquals f1: " + f1 + ", f2: " + f2);
+  }
+
+  public static void assertEquals(double d1, double d2) {
+    if (d1 == d2) { return; }
+    throw new AssertionError("assertEquals d1: " + d1 + ", d2: " + d2);
+  }
+
+  public static void assertEquals(Object o, Object p) {
+    if (o == p) { return; }
+    if (o != null && p != null && o.equals(p)) { return; }
+    throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+  }
+
+  public static void assertNotEquals(Object o, Object p) {
+    if (o != p) { return; }
+    if (o != null && p != null && !o.equals(p)) { return; }
+    throw new AssertionError("assertNotEquals: o1: " + o + ", o2: " + p);
+  }
+
+  public static void assertEquals(String s1, String s2) {
+    if (s1 == s2) {
+      return;
+    }
+
+    if (s1 != null && s2 != null && s1.equals(s2)) {
+      return;
+    }
+
+    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+  }
+}
diff --git a/test/952-invoke-custom/generator/TestLinkerMethodMinimalArguments.java b/test/952-invoke-custom/generator/TestLinkerMethodMinimalArguments.java
new file mode 100644
index 0000000..93d96a9
--- /dev/null
+++ b/test/952-invoke-custom/generator/TestLinkerMethodMinimalArguments.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.android.jack.annotations.CalledByInvokeCustom;
+import com.android.jack.annotations.Constant;
+import com.android.jack.annotations.LinkerMethodHandle;
+import com.android.jack.annotations.MethodHandleKind;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class TestLinkerMethodMinimalArguments {
+
+  private static int forceFailureType = 0;
+
+  private static int FAILURE_TYPE_NONE = 0;
+  private static int FAILURE_TYPE_LINKER_METHOD_RETURNS_NULL = 1;
+  private static int FAILURE_TYPE_LINKER_METHOD_THROWS = 2;
+  private static int FAILURE_TYPE_TARGET_METHOD_THROWS = 3;
+
+  @CalledByInvokeCustom(
+      invokeMethodHandle = @LinkerMethodHandle(
+          kind = MethodHandleKind.INVOKE_STATIC,
+          enclosingType = TestLinkerMethodMinimalArguments.class,
+          argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class},
+          name = "linkerMethod"),
+      name = "add",
+      returnType = int.class,
+      argumentTypes = {int.class, int.class})
+  private static int add(int a, int b) {
+    if (forceFailureType == FAILURE_TYPE_TARGET_METHOD_THROWS) {
+      System.out.println("Throwing ArithmeticException in add()");
+      throw new ArithmeticException("add");
+    }
+    return a + b;
+  }
+
+  @SuppressWarnings("unused")
+  private static CallSite linkerMethod(MethodHandles.Lookup caller, String name,
+                                       MethodType methodType) throws Throwable {
+    System.out.println("linkerMethod failure type " + forceFailureType);
+    MethodHandle mh_add =
+        caller.findStatic(TestLinkerMethodMinimalArguments.class, name, methodType);
+    if (forceFailureType == FAILURE_TYPE_LINKER_METHOD_RETURNS_NULL) {
+      System.out.println("Returning null instead of CallSite for " + name + " " + methodType);
+      return null;
+    } else if (forceFailureType == FAILURE_TYPE_LINKER_METHOD_THROWS) {
+      System.out.println("Throwing InstantiationException in linkerMethod()");
+      throw new InstantiationException("linkerMethod");
+    } else {
+      return new ConstantCallSite(mh_add);
+    }
+  }
+
+  public static void test(int failureType, int x, int y) throws Throwable {
+    assertTrue(failureType >= FAILURE_TYPE_NONE);
+    assertTrue(failureType <= FAILURE_TYPE_TARGET_METHOD_THROWS);
+    forceFailureType = failureType;
+    assertEquals(x + y, add(x, y));
+    System.out.println("Failure Type + " + failureType + " (" + x + y+ ")");
+  }
+
+  public static void assertTrue(boolean value) {
+    if (!value) {
+      throw new AssertionError("assertTrue value: " + value);
+    }
+  }
+
+  public static void assertEquals(byte b1, byte b2) {
+    if (b1 == b2) { return; }
+    throw new AssertionError("assertEquals b1: " + b1 + ", b2: " + b2);
+  }
+
+  public static void assertEquals(char c1, char c2) {
+    if (c1 == c2) { return; }
+    throw new AssertionError("assertEquals c1: " + c1 + ", c2: " + c2);
+  }
+
+  public static void assertEquals(short s1, short s2) {
+    if (s1 == s2) { return; }
+    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+  }
+
+  public static void assertEquals(int i1, int i2) {
+    if (i1 == i2) { return; }
+    throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
+  }
+
+  public static void assertEquals(long l1, long l2) {
+    if (l1 == l2) { return; }
+    throw new AssertionError("assertEquals l1: " + l1 + ", l2: " + l2);
+  }
+
+  public static void assertEquals(float f1, float f2) {
+    if (f1 == f2) { return; }
+    throw new AssertionError("assertEquals f1: " + f1 + ", f2: " + f2);
+  }
+
+  public static void assertEquals(double d1, double d2) {
+    if (d1 == d2) { return; }
+    throw new AssertionError("assertEquals d1: " + d1 + ", d2: " + d2);
+  }
+
+  public static void assertEquals(Object o, Object p) {
+    if (o == p) { return; }
+    if (o != null && p != null && o.equals(p)) { return; }
+    throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+  }
+
+  public static void assertEquals(String s1, String s2) {
+    if (s1 == s2) {
+      return;
+    }
+
+    if (s1 != null && s2 != null && s1.equals(s2)) {
+      return;
+    }
+
+    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+  }
+}
diff --git a/test/952-invoke-custom/generator/TestLinkerMethodMultipleArgumentTypes.java b/test/952-invoke-custom/generator/TestLinkerMethodMultipleArgumentTypes.java
new file mode 100644
index 0000000..4e4d97e
--- /dev/null
+++ b/test/952-invoke-custom/generator/TestLinkerMethodMultipleArgumentTypes.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.android.jack.annotations.CalledByInvokeCustom;
+import com.android.jack.annotations.Constant;
+import com.android.jack.annotations.LinkerMethodHandle;
+import com.android.jack.annotations.MethodHandleKind;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class TestLinkerMethodMultipleArgumentTypes {
+
+  private static int bootstrapRunCount = 0;
+
+  @CalledByInvokeCustom(
+      invokeMethodHandle = @LinkerMethodHandle(kind = MethodHandleKind.INVOKE_STATIC,
+          enclosingType = TestLinkerMethodMultipleArgumentTypes.class,
+          name = "linkerMethod",
+          argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class,
+                           boolean.class, byte.class, char.class, short.class, int.class,
+                           float.class, double.class, String.class, Class.class, long.class}),
+      methodHandleExtraArgs = {@Constant(booleanValue = true), @Constant(byteValue = 1),
+                         @Constant(charValue = 'a'), @Constant(shortValue = 1024),
+                         @Constant(intValue = 1), @Constant(floatValue = 11.1f),
+                         @Constant(doubleValue = 2.2), @Constant(stringValue = "Hello"),
+                         @Constant(classValue = TestLinkerMethodMultipleArgumentTypes.class),
+                         @Constant(longValue = 123456789L)},
+      name = "add",
+      returnType = int.class,
+      argumentTypes = {int.class, int.class})
+  private static int add(int a, int b) {
+    return a + b;
+  }
+
+  @SuppressWarnings("unused")
+  private static CallSite linkerMethod(MethodHandles.Lookup caller, String name,
+                                       MethodType methodType, boolean v1, byte v2, char v3,
+                                       short v4, int v5, float v6, double v7,
+                                       String v8, Class<?> v9, long v10) throws Throwable {
+    System.out.println("Linking " + name + " " + methodType);
+    assertTrue(v1);
+    assertEquals(1, v2);
+    assertEquals('a', v3);
+    assertEquals(1024, v4);
+    assertEquals(1, v5);
+    assertEquals(11.1f, v6);
+    assertEquals(2.2, v7);
+    assertEquals("Hello", v8);
+    assertEquals(TestLinkerMethodMultipleArgumentTypes.class, v9);
+    assertEquals(123456789L, v10);
+    MethodHandle mh_add =
+        caller.findStatic(TestLinkerMethodMultipleArgumentTypes.class, name, methodType);
+    return new ConstantCallSite(mh_add);
+  }
+
+  public int GetBootstrapRunCount() {
+    return bootstrapRunCount;
+  }
+
+  public static void test(int x, int y) throws Throwable {
+    assertEquals(x + y, add(x, y));
+    System.out.println(x + y);
+  }
+
+  public static void assertTrue(boolean value) {
+    if (!value) {
+      throw new AssertionError("assertTrue value: " + value);
+    }
+  }
+
+  public static void assertEquals(byte b1, byte b2) {
+    if (b1 == b2) { return; }
+    throw new AssertionError("assertEquals b1: " + b1 + ", b2: " + b2);
+  }
+
+  public static void assertEquals(char c1, char c2) {
+    if (c1 == c2) { return; }
+    throw new AssertionError("assertEquals c1: " + c1 + ", c2: " + c2);
+  }
+
+  public static void assertEquals(short s1, short s2) {
+    if (s1 == s2) { return; }
+    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+  }
+
+  public static void assertEquals(int i1, int i2) {
+    if (i1 == i2) { return; }
+    throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
+  }
+
+  public static void assertEquals(long l1, long l2) {
+    if (l1 == l2) { return; }
+    throw new AssertionError("assertEquals l1: " + l1 + ", l2: " + l2);
+  }
+
+  public static void assertEquals(float f1, float f2) {
+    if (f1 == f2) { return; }
+    throw new AssertionError("assertEquals f1: " + f1 + ", f2: " + f2);
+  }
+
+  public static void assertEquals(double d1, double d2) {
+    if (d1 == d2) { return; }
+    throw new AssertionError("assertEquals d1: " + d1 + ", d2: " + d2);
+  }
+
+  public static void assertEquals(Object o, Object p) {
+    if (o == p) { return; }
+    if (o != null && p != null && o.equals(p)) { return; }
+    throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+  }
+
+  public static void assertEquals(String s1, String s2) {
+    if (s1 == s2) {
+      return;
+    }
+
+    if (s1 != null && s2 != null && s1.equals(s2)) {
+      return;
+    }
+
+    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+  }
+}
diff --git a/test/952-invoke-custom/generator/build-test.sh b/test/952-invoke-custom/generator/build-test.sh
new file mode 100755
index 0000000..d1d8221
--- /dev/null
+++ b/test/952-invoke-custom/generator/build-test.sh
@@ -0,0 +1,86 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+args="$@"
+while [ -h "${prog}" ]; do
+  newProg=`/bin/ls -ld "${prog}"`
+  newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+  if expr "x${newProg}" : 'x/' >/dev/null; then
+      prog="${newProg}"
+  else
+    progdir=`dirname "${prog}"`
+    prog="${progdir}/${newProg}"
+  fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+test_dir="test-$$"
+if [ -z "$TMPDIR" ]; then
+  tmp_dir="/tmp/$USER/${test_dir}"
+else
+  tmp_dir="${TMPDIR}/${test_dir}"
+fi
+
+if [ "x$ANDROID_BUILD_TOP" = "x" ]; then
+  echo Build environment is not set-up.
+  exit -1
+fi
+
+# This only works internally for now (sorry folks!)
+jack_annotations_lib=/google/data/rw/teams/android-runtime/jack/jack-test-annotations-lib.jack
+if [ ! -f $jack_annotations_lib ]; then
+  echo Try 'prodaccess' to access android-runtime directory.
+  exit -1
+fi
+
+# Compile test into a base64 string that can be instantiated via
+# reflection on hosts without the jack-test-annotations-lib.jack file.
+mkdir $tmp_dir
+for input_file in $progdir/*.java; do
+  i=${input_file##*/Test}
+  i=${i%%.java}
+  src_file=$progdir/Test$i.java
+  jack_file=./src.jack
+  dex_file=./classes.dex
+  base_64_file=$tmp_dir/TestData$i.base64
+  output_file=$progdir/../src/TestData$i.java
+  # Compile source file to jack file.
+  jack -g -cp $ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:$ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack:$jack_annotations_lib -D sched.runner=multi-threaded -D sched.runner.thread.kind=fixed -D sched.runner.thread.fixed.count=4 -D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b2 --output-jack $jack_file $src_file
+  # Compile jack file to classes.dex.
+  jack -g -cp $ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:$ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack -D sched.runner=multi-threaded -D sched.runner.thread.kind=fixed -D sched.runner.thread.fixed.count=4 -D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b2 --import $jack_file --output-dex .
+  # Pack the classes.dex file into a base64 string.
+  base64 -w 72 $dex_file > $base_64_file
+  # Emit a managed source file containing the base64 string. The test can be
+  # run by loading this string as a dex file and invoking it via reflection.
+cat > $output_file <<HEADER
+/* Generated by ${prog##*/} from ${src_file##*/} */
+public class TestData$i {
+  public static final String BASE64_DEX_FILE =
+HEADER
+sed -e 's/^\(.*\)$/    "\1" +/' -e '$s/ +/;/' $base_64_file >> $output_file
+cat >> $output_file <<FOOTER
+}
+FOOTER
+  rm $dex_file $jack_file
+done
+
+rm -rf $tmp_dir
diff --git a/test/952-invoke-custom/info.txt b/test/952-invoke-custom/info.txt
new file mode 100644
index 0000000..2954e55
--- /dev/null
+++ b/test/952-invoke-custom/info.txt
@@ -0,0 +1,9 @@
+A test that is only available as a DEX binary.
+
+This tests execution of invoke-custom. There is no bytecode to emit
+invoke-custom directly. This test is generated using an internal only jack file.
+
+Internal developers MUST regenerate the test data files after editing
+the tests under generator/ using:
+
+$ generator/build-tests.sh
diff --git a/test/952-invoke-custom/src/Main.java b/test/952-invoke-custom/src/Main.java
new file mode 100644
index 0000000..2abc312
--- /dev/null
+++ b/test/952-invoke-custom/src/Main.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import dalvik.system.InMemoryDexClassLoader;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MutableCallSite;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.Base64;
+
+// This test is a stop-gap until we have support for generating invoke-custom
+// in the Android tree.
+
+public class Main {
+
+  private static void TestUninitializedCallSite() throws Throwable {
+    CallSite callSite = new MutableCallSite(MethodType.methodType(int.class));
+    try {
+      callSite.getTarget().invoke();
+      fail();
+    } catch (IllegalStateException e) {
+      System.out.println("Caught exception from uninitialized call site");
+    }
+
+    callSite = new MutableCallSite(MethodType.methodType(String.class, int.class, char.class));
+    try {
+      callSite.getTarget().invoke(1535, 'd');
+      fail();
+    } catch (IllegalStateException e) {
+      System.out.println("Caught exception from uninitialized call site");
+    }
+  }
+
+  private static void TestLinkerMethodMultipleArgumentTypes() throws Throwable {
+    // This is a more comprehensive test of invoke-custom, the linker
+    // method takes additional arguments of types boolean, byte, char,
+    // short, int, float, double, String, Class, and long (in this order)
+    // The test asserts the values passed to the linker method match their
+    // expected values.
+    byte[] base64Data = TestDataLinkerMethodMultipleArgumentTypes.BASE64_DEX_FILE.getBytes();
+    Base64.Decoder decoder = Base64.getDecoder();
+    ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data));
+
+    InMemoryDexClassLoader classLoader =
+        new InMemoryDexClassLoader(dexBuffer,
+                                   ClassLoader.getSystemClassLoader());
+    Class<?> testClass =
+        classLoader.loadClass("TestLinkerMethodMultipleArgumentTypes");
+    Method testMethod = testClass.getDeclaredMethod("test", int.class, int.class);
+    // First invocation should link via the bootstrap method (outputs "Linking add" ...).
+    testMethod.invoke(null, 33, 67);
+    // Subsequent invocations use the cached value of the CallSite and do not require linking.
+    testMethod.invoke(null, -10000, +1000);
+    testMethod.invoke(null, -1000, +10000);
+  }
+
+  private static void TestLinkerMethodMinimalArguments() throws Throwable {
+    // This test checks various failures when running the linker
+    // method and during invocation of the method handle.
+    byte[] base64Data = TestDataLinkerMethodMinimalArguments.BASE64_DEX_FILE.getBytes();
+    Base64.Decoder decoder = Base64.getDecoder();
+    ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data));
+
+    InMemoryDexClassLoader classLoader =
+        new InMemoryDexClassLoader(dexBuffer,
+                                   ClassLoader.getSystemClassLoader());
+    Class<?> testClass =
+        classLoader.loadClass("TestLinkerMethodMinimalArguments");
+    Method testMethod = testClass.getDeclaredMethod("test", int.class, int.class, int.class);
+
+    try {
+      testMethod.invoke(null, 1 /* linker method return null */, 10, 10);
+    } catch (InvocationTargetException e) {
+      assertEquals(e.getCause().getClass().getName(), "java.lang.BootstrapMethodError");
+      assertEquals(
+          e.getCause().getCause().getClass().getName(), "java.lang.NullPointerException");
+    }
+
+    try {
+      testMethod.invoke(null, 2 /* linker method throw InstantiationException */, 10, 11);
+    } catch (InvocationTargetException e) {
+      assertEquals(e.getCause().getClass().getName(), "java.lang.BootstrapMethodError");
+      assertEquals(
+          e.getCause().getCause().getClass().getName(), "java.lang.InstantiationException");
+    }
+    try {
+      // Creating the CallSite works here, but fail invoking the method.
+      testMethod.invoke(null, 3 /* target throw NPE */, 10, 12);
+    } catch (InvocationTargetException e) {
+      assertEquals(e.getCause().getClass().getName(), "java.lang.ArithmeticException");
+    }
+
+    // This should succeed using already resolved CallSite.
+    testMethod.invoke(null, 0 /* no error */, 10, 13);
+  }
+
+  private static void TestInvokeCustomWithConcurrentThreads() throws Throwable {
+    // This is a concurrency test that attempts to run invoke-custom on the same
+    // call site.
+    byte[] base64Data = TestDataInvokeCustomWithConcurrentThreads.BASE64_DEX_FILE.getBytes();
+    Base64.Decoder decoder = Base64.getDecoder();
+    ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data));
+
+    InMemoryDexClassLoader classLoader =
+        new InMemoryDexClassLoader(dexBuffer,
+                                   ClassLoader.getSystemClassLoader());
+    Class<?> testClass =
+        classLoader.loadClass("TestInvokeCustomWithConcurrentThreads");
+    Method testMethod = testClass.getDeclaredMethod("test");
+    testMethod.invoke(null);
+  }
+
+  public static void assertEquals(Object o, Object p) {
+    if (o == p) { return; }
+    if (o != null && p != null && o.equals(p)) { return; }
+    throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+  }
+
+  public static void assertEquals(String s1, String s2) {
+    if (s1 == s2) {
+      return;
+    }
+
+    if (s1 != null && s2 != null && s1.equals(s2)) {
+      return;
+    }
+
+    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+  }
+
+  private static void fail() {
+    System.out.println("fail");
+    Thread.dumpStack();
+  }
+
+  public static void main(String[] args) throws Throwable {
+    TestUninitializedCallSite();
+    TestLinkerMethodMinimalArguments();
+    TestLinkerMethodMultipleArgumentTypes();
+    TestInvokeCustomWithConcurrentThreads();
+  }
+}
\ No newline at end of file
diff --git a/test/952-invoke-custom/src/TestDataInvokeCustomWithConcurrentThreads.java b/test/952-invoke-custom/src/TestDataInvokeCustomWithConcurrentThreads.java
new file mode 100644
index 0000000..076acd7
--- /dev/null
+++ b/test/952-invoke-custom/src/TestDataInvokeCustomWithConcurrentThreads.java
@@ -0,0 +1,147 @@
+/* Generated by build-test.sh from TestInvokeCustomWithConcurrentThreads.java */
+public class TestDataInvokeCustomWithConcurrentThreads {
+  public static final String BASE64_DEX_FILE =
+    "ZGV4CjAzOABWCyocbwmvY62Y5O92LHrb3B/jTa9jHG0IHgAAcAAAAHhWNBIAAAAAAAAAACAd" +
+    "AACrAAAAcAAAACsAAAAcAwAAJQAAAMgDAAAKAAAAhAUAADkAAADUBQAAAgAAAKAHAAAgFgAA" +
+    "6AcAAMARAADzEQAAIxIAACwSAAA0EgAAPBIAAEQSAABMEgAAVBIAAFwSAABkEgAAbBIAAHMS" +
+    "AAB2EgAAgBIAAIgSAACMEgAAjxIAAJISAACsEgAArxIAALISAAC1EgAAuRIAAMgSAADLEgAA" +
+    "zhIAANISAADWEgAA2hIAAN4SAADiEgAA5hIAAOwSAADxEgAA9xIAACITAABLEwAATxMAAIQT" +
+    "AAC3EwAA6BMAAAwUAAAsFAAATxQAAG4UAACKFAAAoRQAAL0UAADQFAAA5RQAAPkUAAANFQAA" +
+    "KBUAADwVAABQFQAAaBUAAIEVAACYFQAAtRUAANoVAAD7FQAAJBYAAEYWAABlFgAAixYAALgW" +
+    "AADLFgAAzhYAANQWAAAAFwAAJhcAACkXAAAuFwAAMxcAADgXAAA9FwAAQRcAAEYXAABLFwAA" +
+    "TxcAAFQXAABZFwAAXRcAAGcXAABqFwAAbhcAAIIXAACXFwAArBcAAMoXAAD4FwAABRgAAA0Y" +
+    "AAAcGAAAKhgAAD0YAABQGAAAYxgAAHYYAACJGAAAnBgAAK8YAADDGAAA1BgAAOsYAAD3GAAA" +
+    "CxkAABIZAAAWGQAAGhkAACMZAAAnGQAAKxkAADMZAAA7GQAAPxkAAEMZAABSGQAAZhkAAHUZ" +
+    "AAB9GQAAgRkAAIUZAACRGQAAmRkAAJ4ZAACvGQAAvxkAAMIZAADGGQAAyhkAANEZAADfGQAA" +
+    "8BkAAP4ZAAAIGgAAHBoAACIaAAAoGgAALBoAADAaAAA+GgAAShoAAE4aAABUGgAAXxoAAGga" +
+    "AABrGgAAcBoAAHMaAACDGgAAjBoAAJgaAACdGgAAoRoAAKUaAACqGgAAtRoAALwaAADHGgAA" +
+    "zRoAANMaAADgGgAA6RoAAPMaAAD5GgAAABsAAAkbAAAQGwAAGRsAABAAAAARAAAAEwAAABQA" +
+    "AAAVAAAAGAAAACMAAAAkAAAAJgAAACcAAAAoAAAAKQAAACoAAAArAAAALAAAAC0AAAAuAAAA" +
+    "LwAAADAAAAAxAAAAMgAAADMAAAA0AAAANQAAADYAAAA4AAAAOQAAADoAAAA7AAAAPAAAAD0A" +
+    "AAA+AAAAPwAAAEAAAABBAAAAQwAAAEcAAABUAAAAVgAAAFcAAABYAAAAWQAAAFoAAAAVAAAA" +
+    "BAAAAAAAAAAWAAAABAAAAPgQAAAhAAAAEAAAAAARAAAZAAAAEwAAAAAAAAAdAAAAEwAAAPgQ" +
+    "AAAZAAAAFAAAAAAAAAAZAAAAFQAAAAAAAAAaAAAAFgAAAAgRAAAbAAAAFgAAABARAAAcAAAA" +
+    "FgAAABgRAAAdAAAAFgAAAPgQAAAeAAAAFgAAACARAAAfAAAAFgAAACgRAAAfAAAAFgAAADAR" +
+    "AAAlAAAAFgAAADgRAAAiAAAAGwAAAEARAAAiAAAAHQAAAEwRAAAgAAAAHQAAAFgRAAAgAAAA" +
+    "HQAAAGQRAAAZAAAAIAAAAAAAAAAZAAAAIgAAAAAAAABHAAAAJAAAAAAAAABIAAAAJAAAAHAR" +
+    "AABJAAAAJAAAAHgRAABKAAAAJAAAAIARAABLAAAAJAAAAIgRAABMAAAAJAAAAPgQAABNAAAA" +
+    "JAAAAJARAABOAAAAJAAAAJgRAABPAAAAJAAAACgRAABQAAAAJAAAAKARAABPAAAAJAAAADAR" +
+    "AABQAAAAJAAAAKgRAABPAAAAJAAAALARAABRAAAAJAAAALgRAABSAAAAJAAAADgRAABVAAAA" +
+    "JQAAACgRAAAHAAQAQgAAAAcAIQBuAAAABwAqAHEAAAAHACkAhgAAAAcAIgCRAAAABwAqAJ8A" +
+    "AAAHABkAogAAAAoACgAXAAAAEwASAEQAAAAXABAAlAAAAAYAFQAOAAAABgADAIQAAAAGAAUA" +
+    "hAAAAAcAFAALAAAABwAVAA0AAAAHABUADgAAAAcAFgBeAAAABwAXAF4AAAAHABgAXgAAAAcA" +
+    "GQBeAAAABwAbAF4AAAAHABwAXgAAAAcAHgBeAAAABwAgAF4AAAAHACIAXgAAAAcAHgBnAAAA" +
+    "BwAjAGkAAAAHAAAAfwAAAAcADwCNAAAABwABAJIAAAAHABUAmQAAAAcAAQCdAAAABwAVAKAA" +
+    "AAAQAAIAfAAAABAAHwCXAAAAEQAdAA4AAAATAAAAhwAAABMABACnAAAAFAAkAHgAAAAVACQA" +
+    "eAAAABYAFQAOAAAAFgAHAFwAAAAWAAgAXAAAABYACQBcAAAAFgAKAFwAAAAWAAsAXAAAABYA" +
+    "DABcAAAAFgANAFwAAAAWAA4AXAAAABYABgCkAAAAGAAVAA4AAAAYABUAiQAAABgAFQCeAAAA" +
+    "GQAVAA4AAAAZAAUAfQAAABwAIQAOAAAAHQATAKUAAAAeABAAewAAAB8AEQB1AAAAHwASAIUA" +
+    "AAAgAAAAlgAAACEAGgAOAAAAIQAAAGsAAAAiABoADgAAACIAAAB9AAAAIgAAAH4AAAAiABoA" +
+    "nAAAAJwcAAAGAAAAEAAAABkAAAAAAAAARQAAALgQAACjHAAAAAAAAAcAAAABAAAAGAAAAAAA" +
+    "AABFAAAAyBAAALYcAACZHAAABAAAABIAAAADAAAAPRwAAEQcAABNHAAAAQAAAFwcAAABAAAA" +
+    "TRwAAAEAAABlHAAAAQAAAG4cAAABAAEAAQAAABwbAAAEAAAAcBArAAAADgACAAEAAQAAACQb" +
+    "AAANAAAAcQADAAAADABuEDcAAAAKAHEQGwAAAAwAEQAAAAIAAQABAAAAKRsAAAUAAABuEAEA" +
+    "AQAMABEAAAABAAAAAAAAAAAAAAADAAAAYgAEABEAAAADAAAAAgAAAC4bAAAlAAAAEwIQACIA" +
+    "IgASAXAgNQAQAGkABAAiAAYAcBAAAAAAaQAGACMgKQBpAAMAIyAqAGkAAgAjICoAaQAFACIA" +
+    "IQBwIDMAIABpAAEADgAAAAEAAQABAAAAPBsAAAQAAABwECgAAAAOAAUAAgACAAAAQRsAACgA" +
+    "AAAzQwMADgAiABEAIgEWAHAQHgABABsCXwAAAG4gJQAhAAwBbiAiADEADAEbAgMAAABuICUA" +
+    "IQAMAW4gIgBBAAwBbhAnAAEADAFwIBkAEAAnAAUAAgACAAAAShsAACgAAAAzQwMADgAiABEA" +
+    "IgEWAHAQHgABABsCYAAAAG4gJQAhAAwBbiAfADEADAEbAgQAAABuICUAIQAMAW4gHwBBAAwB" +
+    "bhAnAAEADAFwIBkAEAAnAAgABAADAAAAUxsAACoAAAAvAAQGOQADAA4AIgARACIBFgBwEB4A" +
+    "AQAbAmEAAABuICUAIQAMAW4wIABBBQwBGwIFAAAAbiAlACEADAFuMCAAYQcMAW4QJwABAAwB" +
+    "cCAZABAAJwAFAAIAAgAAAFwbAAAqAAAALQADBDkAAwAOACIAEQAiARYAcBAeAAEAGwJiAAAA" +
+    "biAlACEADAFuICEAMQAMARsCBgAAAG4gJQAhAAwBbiAhAEEADAFuECcAAQAMAXAgGQAQACcA" +
+    "BQACAAIAAABlGwAAKAAAADNDAwAOACIAEQAiARYAcBAeAAEAGwJjAAAAbiAlACEADAFuICIA" +
+    "MQAMARsCBwAAAG4gJQAhAAwBbiAiAEEADAFuECcAAQAMAXAgGQAQACcACAAEAAMAAABwGwAA" +
+    "KgAAADEABAY5AAMADgAiABEAIgEWAHAQHgABABsCZAAAAG4gJQAhAAwBbjAjAEEFDAEbAggA" +
+    "AABuICUAIQAMAW4wIwBhBwwBbhAnAAEADAFwIBkAEAAnAAUAAgACAAAAexsAADMAAAAzQwMA" +
+    "DgA4AwsAOAQJAG4gHABDAAoAOAADAA4AIgARACIBFgBwEB4AAQAbAmYAAABuICUAIQAMAW4g" +
+    "JAAxAAwBGwIJAAAAbiAlACEADAFuICQAQQAMAW4QJwABAAwBcCAZABAAJwAAAAUAAgACAAAA" +
+    "hxsAADMAAAAzQwMADgA4AwsAOAQJAG4gHQBDAAoAOAADAA4AIgARACIBFgBwEB4AAQAbAmUA" +
+    "AABuICUAIQAMAW4gJQAxAAwBGwIKAAAAbiAlACEADAFuICUAQQAMAW4QJwABAAwBcCAZABAA" +
+    "JwAAAAUAAgACAAAAlRsAACgAAAAzQwMADgAiABEAIgEWAHAQHgABABsCZQAAAG4gJQAhAAwB" +
+    "biAiADEADAEbAgoAAABuICUAIQAMAW4gIgBBAAwBbhAnAAEADAFwIBkAEAAnAAUAAgACAAAA" +
+    "oBsAADUAAAAyQwMADgA4Aw0AOAQLAG4gHABDAAoA3wAAATgAAwAOACIAEQAiARYAcBAeAAEA" +
+    "GwJoAAAAbiAlACEADAFuICQAMQAMARsCCQAAAG4gJQAhAAwBbiAkAEEADAFuECcAAQAMAXAg" +
+    "GQAQACcAAAAEAAEAAgAAAKwbAAAdAAAAOQMcACIAEQAiARYAcBAeAAEAGwJqAAAAbiAlACEA" +
+    "DAFuICYAMQAMAW4QJwABAAwBcCAZABAAJwAOAAAAAQAAAAEAAAC4GwAADQAAAGIABgBuECwA" +
+    "AAAMAB8AEwBuEBoAAAAKAA8AAAAJAAMABAAAAL0bAABhAAAAEhUSBHEAEQAAAAoBHAIHAG5A" +
+    "LwAmhwwAbhAuAAAADAJxIAwAKABuEC4AAAAMAm4QMgACAAoCcSAKAFIAI1InAHEQGwABAAwD" +
+    "TQMCBHEwMQBAAgwAI1ImAGIDCABNAwIEcTAwAEACDABuEC4AAAAMAm4QMgACAAoCcSAKAFIA" +
+    "bhAuAAAADAJxIAwAKABiAgEAbhA0AAIAYgIDAHEAEQAAAAoDIgQcAHAgLQAEAE0EAgNiAgMA" +
+    "cQARAAAACgNGAgIDEQIAAAEAAQAAAAAA2xsAAAEAAAAPAAAAAwABAAIAAADiGwAAFAAAAGIA" +
+    "AgBGAAACbhA3AAAAYgAFAHEAEQAAAAoBRgAAAW4gOAAgABIADwAMAAAAAwAAAOsbAADoAAAA" +
+    "EisSGhIJEwgQABIANYAXAGIEAgAiBSIAcCA1AJUATQUEAGIEBQAiBSIAcCA1AJUATQUEANgA" +
+    "AAEo6iOBKAASADWAEQAiBAcAcBAFAAQATQQBAEYEAQBuECoABADYAAABKPASADWACgBGBAEA" +
+    "bhApAAQA2AAAASj3EgMSAhIANYAiAGIEAwBGBAQAEgVxIA8AVABiBAIARgQEAG4QNgAEAAoE" +
+    "OAQNANgDAwFiBAIARgQEAG4QNgAEAAoEsELYAAABKN9iBAkAIgUWAHAQHgAFABsGUwAAAG4g" +
+    "JQBlAAwFbiAiADUADAUbBgIAAABuICUAZQAMBW4gIgAlAAwFbhAnAAUADAVuIBgAVAAyoy4A" +
+    "YgQJABsFRgAAAG4gGABUABIANYAjAGIECQAbBQEAAAAjticAcRAbAAAADAdNBwYJYgcFAEYH" +
+    "BwBuEDYABwAKB3EQGwAHAAwHTQcGCm4wFwBUBtgAAAEo3jKCLgBiBAkAGwUSAAAAbiAYAFQA" +
+    "EgA1gCMAYgQJABsFAAAAACO2JwBxEBsAAAAMB00HBgliBwIARgcHAG4QNgAHAAoHcRAbAAcA" +
+    "DAdNBwYKbjAXAFQG2AAAASjecSAKAKMAcSAKAIIADgADAAEAAQAAADEcAAAJAAAAEvH8EAAA" +
+    "AQAKAHEQEwAAAA4AAADoBwAAAAAAAAAAAAAAAAAA+AcAAAEAAAADAAAAAAAAAAYAAAAACAAA" +
+    "EgAAAAgIAAAVAAAAEAgAABYAAAAICAAAAQAAAAQAAAACAAAAFQAnAAEAAAABAAAAAQAAAAIA" +
+    "AAABAAAAAwAAAAEAAAAFAAAAAQAAABQAAAABAAAAFQAAAAEAAAAlAAAAAwAAAB4AFQAgAAAA" +
+    "AwAAABIAFQAgAAAAAwAAAB0ABAAmAAAAAwAAAB0ABAAnAAAAAgAAAAAAAAACAAAAAQABAAIA" +
+    "AAACAAIAAgAAAAMAAwACAAAABAAEAAIAAAAFAAUAAgAAABQAFAACAAAAFQAVAAEAAAAdAAAA" +
+    "AgAAACMAIwAxIENhbGwgc2l0ZSBpbnN0YW5jZSAjJTAyZCB3YXMgaW52b2tlZCAlIDJkIHRp" +
+    "bWVzCgAuIFRocmVhZCAlIDJkIGludm9rZWQgY2FsbCBzaXRlIGluc3RhbmNlICMlMDJkCgAH" +
+    "IFZvdGVzIAAGLCBiMjogAAYsIGMyOiAABiwgZDI6IAAGLCBmMjogAAYsIGkyOiAABiwgbDI6" +
+    "IAAGLCBvMjogAAYsIHMyOiAABS1nZXQwAAE8AAg8Y2xpbml0PgAGPGluaXQ+AAI+OwABQgAB" +
+    "QwAYQ2FsbC1zaXRlcyBpbnZvY2F0aW9ucyA6AAFEAAFGAAFJAAJJSQANSU5WT0tFX1NUQVRJ" +
+    "QwABSgABTAACTEMAAkxEAAJMRgACTEkAAkxKAAJMTAAETExJTAADTExMAARMTExMAClMVGVz" +
+    "dEludm9rZUN1c3RvbVdpdGhDb25jdXJyZW50VGhyZWFkcyQxOwAnTFRlc3RJbnZva2VDdXN0" +
+    "b21XaXRoQ29uY3VycmVudFRocmVhZHM7AAJMWgAzTGNvbS9hbmRyb2lkL2phY2svYW5ub3Rh" +
+    "dGlvbnMvQ2FsbGVkQnlJbnZva2VDdXN0b207ADFMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0" +
+    "aW9ucy9MaW5rZXJNZXRob2RIYW5kbGU7AC9MY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9u" +
+    "cy9NZXRob2RIYW5kbGVLaW5kOwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNz" +
+    "OwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ACFMZGFsdmlrL2Fubm90YXRpb24v" +
+    "TWVtYmVyQ2xhc3NlczsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1cmU7ABpMZGFsdmlr" +
+    "L2Fubm90YXRpb24vVGhyb3dzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABpMamF2YS9sYW5n" +
+    "L0Fzc2VydGlvbkVycm9yOwARTGphdmEvbGFuZy9DbGFzczsAE0xqYXZhL2xhbmcvSW50ZWdl" +
+    "cjsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABlMamF2YS9sYW5n" +
+    "L1N0cmluZ0J1aWxkZXI7ABJMamF2YS9sYW5nL1N5c3RlbTsAEkxqYXZhL2xhbmcvVGhyZWFk" +
+    "OwAWTGphdmEvbGFuZy9UaHJlYWRMb2NhbAAXTGphdmEvbGFuZy9UaHJlYWRMb2NhbDsAFUxq" +
+    "YXZhL2xhbmcvVGhyb3dhYmxlOwAbTGphdmEvbGFuZy9pbnZva2UvQ2FsbFNpdGU7ACNMamF2" +
+    "YS9sYW5nL2ludm9rZS9Db25zdGFudENhbGxTaXRlOwAfTGphdmEvbGFuZy9pbnZva2UvTWV0" +
+    "aG9kSGFuZGxlOwAnTGphdmEvbGFuZy9pbnZva2UvTWV0aG9kSGFuZGxlcyRMb29rdXA7ACBM" +
+    "amF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzOwAdTGphdmEvbGFuZy9pbnZva2UvTWV0" +
+    "aG9kVHlwZTsAJExqYXZhL3V0aWwvY29uY3VycmVudC9DeWNsaWNCYXJyaWVyOwArTGphdmEv" +
+    "dXRpbC9jb25jdXJyZW50L2F0b21pYy9BdG9taWNJbnRlZ2VyOwARTlVNQkVSX09GX1RIUkVB" +
+    "RFMAAVMABFRZUEUAKlRlc3RJbnZva2VDdXN0b21XaXRoQ29uY3VycmVudFRocmVhZHMuamF2" +
+    "YQAkVGhyZWFkcyBkaWQgbm90IHRoZSBzYW1lIGNhbGwtc2l0ZXM6AAFWAANWQkIAA1ZDQwAD" +
+    "VkREAANWRkYAAlZJAANWSUkAA1ZKSgACVkwAA1ZMTAADVlNTAAJWWgAIV2lubmVycyAAAVoA" +
+    "AlpMABJbTGphdmEvbGFuZy9DbGFzczsAE1tMamF2YS9sYW5nL09iamVjdDsAE1tMamF2YS9s" +
+    "YW5nL1RocmVhZDsAHFtMamF2YS9sYW5nL2ludm9rZS9DYWxsU2l0ZTsALFtMamF2YS91dGls" +
+    "L2NvbmN1cnJlbnQvYXRvbWljL0F0b21pY0ludGVnZXI7AAthY2Nlc3NGbGFncwAGYXBwZW5k" +
+    "AA1hcmd1bWVudFR5cGVzAAxhc3NlcnRFcXVhbHMAEWFzc2VydEVxdWFscyBiMTogABFhc3Nl" +
+    "cnRFcXVhbHMgYzE6IAARYXNzZXJ0RXF1YWxzIGQxOiAAEWFzc2VydEVxdWFscyBmMTogABFh" +
+    "c3NlcnRFcXVhbHMgaTE6IAARYXNzZXJ0RXF1YWxzIGwxOiAAEWFzc2VydEVxdWFscyBzMTog" +
+    "ABJhc3NlcnRFcXVhbHM6IG8xOiAAD2Fzc2VydE5vdEVxdWFscwAVYXNzZXJ0Tm90RXF1YWxz" +
+    "OiBvMTogAAphc3NlcnRUcnVlABJhc3NlcnRUcnVlIHZhbHVlOiAABWF3YWl0AAJiMQACYjIA" +
+    "B2JhcnJpZXIAAmMxAAJjMgAGY2FsbGVkAAZjYWxsZXIAAmQxAAJkMgANZHJvcEFyZ3VtZW50" +
+    "cwASZW1pdHRlcjogamFjay00LjI1AA1lbmNsb3NpbmdUeXBlAAZlcXVhbHMAAmYxAAJmMgAK" +
+    "ZmluZFN0YXRpYwAGZm9ybWF0AANnZXQAD2dldEFuZEluY3JlbWVudAAOZ2V0VGhyZWFkSW5k" +
+    "ZXgAAWkAAmkxAAJpMgAFaW5kZXgADGluaXRpYWxWYWx1ZQAPaW5zZXJ0QXJndW1lbnRzAAxp" +
+    "bnN0YW50aWF0ZWQACGludFZhbHVlABJpbnZva2VNZXRob2RIYW5kbGUABGpvaW4ABGtpbmQA" +
+    "AmwxAAJsMgAMbGlua2VyTWV0aG9kAAptZXRob2RUeXBlAAJtaAAEbmFtZQAJbmV4dEluZGV4" +
+    "AAdub3RVc2VkAAFvAANvdXQAAXAADnBhcmFtZXRlckNvdW50AAdwcmludGxuAApyZXR1cm5U" +
+    "eXBlAANydW4AAnMxAAJzMgADc2V0AAlzZXRDYWxsZWQABXN0YXJ0AAl0YXJnZXR0ZWQABHRl" +
+    "c3QABHRoaXMAC3RocmVhZEluZGV4AAd0aHJlYWRzAAh0b1N0cmluZwAEdHlwZQAFdmFsdWUA" +
+    "B3ZhbHVlT2YABXZvdGVzAAd3aW5uZXJzAAF4ACcABw4CWjsAKgAHDgAoAAcOACQAByyJWDVN" +
+    "TU0CaXcAOgAHDgCuAQJtbgcOPACzAQJwcQcOPADMAQJ0dQcOWgDHAQJ6ewcOWgC9AQKCAYMB" +
+    "Bw48AMIBAowBjQEHDloA0QEClAGWAQcOPLQA3QECmwGcAQcOLSClIAC4AQKbAZwBBw48ANcB" +
+    "ApQBlgEHDjzSAKgBAacBBw4tARoQAD0ABw4AXANzkQGPAQcsTAMBowEFaQMAkAEeeLTDpbR8" +
+    "W9IAQQGrAQcOAFMBhAEHDni0AHEAB1kBAQMAgQEFLZaTQS0DAaQBKTx4V0E8WEAeAwOqAQUe" +
+    "AwKpAQU8h6UtkUMBJBIthzx4ARQNOkMthzx4ARQNOkE8PABGAAcOWgMAqwEFPAACCwGmARgH" +
+    "AgwCWwQIkAEeAg4BpgEcBBc3FwwXMRcPAg0BpgEcARgGAg8BpgEcARgaAAgEXRwBGASIARwB" +
+    "HQkEXRwDGB4YFRggdxgHigEbB5ABF42QARedmAEYBAEEEAMWABedFQEAAAECAICABJgQAQSw" +
+    "EAHEINwQBwATAQAaARoBGgEaARoBGgEaA4gg+BABiIAEkBEBgoAE7BEBCYQSAQnkEgEJxBMB" +
+    "CagUAQmMFQEJ7BUBCdAWAQnIFwEJwBgBCaAZAQmcGgEK6BoBCpQbAQnoHAIK/BwBCbQdFAGU" +
+    "IQAAABMAAAAAAAAAAQAAAAAAAAABAAAAqwAAAHAAAAACAAAAKwAAABwDAAADAAAAJQAAAMgD" +
+    "AAAEAAAACgAAAIQFAAAFAAAAOQAAANQFAAAHAAAAAQAAAJwHAAAGAAAAAgAAAKAHAAAIAAAA" +
+    "AQAAAOAHAAADEAAABQAAAOgHAAABIAAAFwAAABgIAAAGIAAAAgAAALgQAAABEAAAFwAAAPgQ" +
+    "AAACIAAAqwAAAMARAAADIAAAFgAAABwbAAAEIAAABgAAAD0cAAAFIAAAAgAAAJkcAAAAIAAA" +
+    "AgAAAKMcAAAAEAAAAQAAACAdAAA=";
+}
diff --git a/test/952-invoke-custom/src/TestDataLinkerMethodMinimalArguments.java b/test/952-invoke-custom/src/TestDataLinkerMethodMinimalArguments.java
new file mode 100644
index 0000000..443a7af
--- /dev/null
+++ b/test/952-invoke-custom/src/TestDataLinkerMethodMinimalArguments.java
@@ -0,0 +1,106 @@
+/* Generated by build-test.sh from TestLinkerMethodMinimalArguments.java */
+public class TestDataLinkerMethodMinimalArguments {
+  public static final String BASE64_DEX_FILE =
+    "ZGV4CjAzOADnZpVEc25JsNXLCW+vh64OuLf8RymAuINwFQAAcAAAAHhWNBIAAAAAAAAAAIgU" +
+    "AACBAAAAcAAAAB0AAAB0AgAAHAAAAOgCAAAHAAAAOAQAACIAAABwBAAAAQAAAIQFAADEDwAA" +
+    "rAUAAKAMAACjDAAApwwAAKoMAACyDAAAugwAAMIMAADKDAAA0gwAANoMAADiDAAA6gwAAPQM" +
+    "AAD8DAAA/wwAAAINAAAFDQAACA0AADENAABUDQAAZw0AAIoNAACbDQAAng0AAKMNAACyDQAA" +
+    "tQ0AALgNAAC8DQAAwA0AAMQNAADIDQAAzA0AANANAADWDQAA+g0AAP4NAAAzDgAAZg4AAJcO" +
+    "AACzDgAAyg4AAOsOAAAHDwAAGg8AAD4PAABSDwAAZg8AAIEPAACVDwAArA8AAMkPAADuDwAA" +
+    "DxAAADgQAABXEAAAgBAAAIMQAACqEAAA0RAAAAQRAAAHEQAADBEAABERAAAWEQAAGxEAACAR" +
+    "AAAmEQAAKxEAAC8RAAA0EQAAOREAAD0RAABAEQAARBEAAEcRAABMEQAAVBEAAGMRAABxEQAA" +
+    "hBEAAJcRAACqEQAAvREAANARAADjEQAA9hEAAAoSAAAWEgAAKhIAAC0SAAAxEgAANRIAADkS" +
+    "AAA9EgAARRIAAEkSAABNEgAAYRIAAHASAAB4EgAAfBIAAIASAACNEgAAmRIAAKsSAACvEgAA" +
+    "sxIAAMcSAADNEgAA0RIAANUSAADjEgAA/xIAAAsTAAATEwAAGRMAABwTAAAhEwAAJBMAAC0T" +
+    "AAA5EwAAPRMAAEETAABHEwAATRMAAFcTAABeEwAAYRMAAA0AAAAOAAAADwAAABAAAAAWAAAA" +
+    "GQAAACIAAAAkAAAAJQAAACYAAAAnAAAAKAAAACkAAAAqAAAAKwAAACwAAAAtAAAALgAAAC8A" +
+    "AAAwAAAAMQAAADIAAAAzAAAANAAAADUAAAA2AAAAOAAAADwAAABIAAAAFwAAAAQAAADsCwAA" +
+    "GgAAABEAAAAAAAAAGwAAABIAAAD0CwAAHAAAABIAAAD8CwAAHQAAABIAAAAEDAAAHgAAABIA" +
+    "AAAMDAAAHwAAABIAAAAUDAAAIAAAABIAAAAcDAAAIAAAABIAAAAkDAAAIwAAABIAAAAsDAAA" +
+    "IQAAABUAAAA0DAAAIQAAABcAAABADAAAPAAAABsAAAAAAAAAPQAAABsAAABMDAAAPgAAABsA" +
+    "AABUDAAAPwAAABsAAABcDAAAQAAAABsAAABkDAAAQQAAABsAAADsCwAAQgAAABsAAABsDAAA" +
+    "QwAAABsAAAB4DAAARAAAABsAAAAcDAAARQAAABsAAACADAAARAAAABsAAAAkDAAARQAAABsA" +
+    "AACIDAAARAAAABsAAACQDAAARgAAABsAAACYDAAARwAAABsAAAAsDAAASQAAABwAAAAcDAAA" +
+    "BgAEABEAAAAGAAQAEgAAAAYABAATAAAABgAEABQAAAAGAAQAaAAAAAkACQAYAAAAEwALAHUA" +
+    "AAAGAAwACwAAAAYADAAMAAAABgAAAEsAAAAGAA0ATgAAAAYADgBOAAAABgAPAE4AAAAGABAA" +
+    "TgAAAAYAEQBOAAAABgATAE4AAAAGABUATgAAAAYAFwBOAAAABgAZAE4AAAAGABoAVwAAAAYA" +
+    "CgBvAAAABgASAHsAAAALABYAdwAAAAwAFgAMAAAADQAUAAwAAAAPABYADAAAABAADAAMAAAA" +
+    "EAAbAGMAAAARABsAYwAAABIADAAMAAAAEgACAEwAAAASAAMATAAAABIABABMAAAAEgAFAEwA" +
+    "AAASAAYATAAAABIABwBMAAAAEgAIAEwAAAASAAkATAAAABIAAQB9AAAAFgAYAAwAAAAYAAsA" +
+    "ZwAAADIUAAAGAAAAAQAAABAAAAAAAAAAOQAAAMQLAAA5FAAAAAAAAAQAAAANAAAAAQAAAAIU" +
+    "AAABAAAAKhQAAAEAAAAAAAAAZBMAAA8AAAASAGcABABnAAIAEhBnAAAAEiBnAAEAEjBnAAMA" +
+    "DgAAAAEAAQABAAAAcBMAAAQAAABwEBMAAAAOAAQAAgACAAAAdRMAABoAAABgAAQAYAEDADMQ" +
+    "EwBiAAYAGwE6AAAAbiAPABAAIgAMABsBSwAAAHAgEAAQACcAkAACAw8ABQACAAIAAAB/EwAA" +
+    "KAAAADNDAwAOACIADQAiARIAcBAWAAEAGwJPAAAAbiAdACEADAFuIBoAMQAMARsCAwAAAG4g" +
+    "HQAhAAwBbiAaAEEADAFuEB8AAQAMAXAgEQAQACcABQACAAIAAACHEwAAKAAAADNDAwAOACIA" +
+    "DQAiARIAcBAWAAEAGwJQAAAAbiAdACEADAFuIBcAMQAMARsCBAAAAG4gHQAhAAwBbiAXAEEA" +
+    "DAFuEB8AAQAMAXAgEQAQACcACAAEAAMAAACPEwAAKgAAAC8ABAY5AAMADgAiAA0AIgESAHAQ" +
+    "FgABABsCUQAAAG4gHQAhAAwBbjAYAEEFDAEbAgUAAABuIB0AIQAMAW4wGABhBwwBbhAfAAEA" +
+    "DAFwIBEAEAAnAAUAAgACAAAAlxMAACoAAAAtAAMEOQADAA4AIgANACIBEgBwEBYAAQAbAlIA" +
+    "AABuIB0AIQAMAW4gGQAxAAwBGwIGAAAAbiAdACEADAFuIBkAQQAMAW4QHwABAAwBcCARABAA" +
+    "JwAFAAIAAgAAAJ8TAAAoAAAAM0MDAA4AIgANACIBEgBwEBYAAQAbAlMAAABuIB0AIQAMAW4g" +
+    "GgAxAAwBGwIHAAAAbiAdACEADAFuIBoAQQAMAW4QHwABAAwBcCARABAAJwAIAAQAAwAAAKcT" +
+    "AAAqAAAAMQAEBjkAAwAOACIADQAiARIAcBAWAAEAGwJUAAAAbiAdACEADAFuMBsAQQUMARsC" +
+    "CAAAAG4gHQAhAAwBbjAbAGEHDAFuEB8AAQAMAXAgEQAQACcABQACAAIAAACvEwAAMwAAADND" +
+    "AwAOADgDCwA4BAkAbiAUAEMACgA4AAMADgAiAA0AIgESAHAQFgABABsCVgAAAG4gHQAhAAwB" +
+    "biAcADEADAEbAgkAAABuIB0AIQAMAW4gHABBAAwBbhAfAAEADAFwIBEAEAAnAAAABQACAAIA" +
+    "AAC4EwAAMwAAADNDAwAOADgDCwA4BAkAbiAVAEMACgA4AAMADgAiAA0AIgESAHAQFgABABsC" +
+    "VQAAAG4gHQAhAAwBbiAdADEADAEbAgoAAABuIB0AIQAMAW4gHQBBAAwBbhAfAAEADAFwIBEA" +
+    "EAAnAAAABQACAAIAAADDEwAAKAAAADNDAwAOACIADQAiARIAcBAWAAEAGwJVAAAAbiAdACEA" +
+    "DAFuIBoAMQAMARsCCgAAAG4gHQAhAAwBbiAaAEEADAFuEB8AAQAMAXAgEQAQACcABAABAAIA" +
+    "AADLEwAAHQAAADkDHAAiAA0AIgESAHAQFgABABsCWAAAAG4gHQAhAAwBbiAeADEADAFuEB8A" +
+    "AQAMAXAgEQAQACcADgAAAAcAAwAEAAAA1RMAAGoAAABiAQYAIgISAHAQFgACABsDcAAAAG4g" +
+    "HQAyAAwCYAMEAG4gGgAyAAwCbhAfAAIADAJuIA8AIQAcAQYAbkAhABRlDABgAQQAYAIAADMh" +
+    "KABiAQYAIgISAHAQFgACABsDNwAAAG4gHQAyAAwCbiAdAFIADAIbAwAAAABuIB0AMgAMAm4g" +
+    "HABiAAwCbhAfAAIADAJuIA8AIQASAREBYAEEAGACAQAzIRMAYgEGABsCOwAAAG4gDwAhACIB" +
+    "DwAbAm8AAABwIBIAIQAnASIBFgBwICAAAQARAQYAAwACAAAA7RMAAFAAAAASERICYAACADQD" +
+    "SAABEHEQDAAAAGAAAwA2A0IAcRAMAAEAZwMEAJAABAX8IAAAVAAKAXEgBwAQAGIABgAiARIA" +
+    "cBAWAAEAGwIVAAAAbiAdACEADAFuIBoAMQAMARsCAQAAAG4gHQAhAAwBbiAaAEEADAFuIBoA" +
+    "UQAMARsCAgAAAG4gHQAhAAwBbhAfAAEADAFuIA8AEAAOAAEgKLoBISi/AAAAAAAAAAADAAAA" +
+    "AAAAAAIAAACsBQAADQAAALQFAAAOAAAAtAUAAAIAAAAEAAQAAQAAAAEAAAABAAAAAgAAAAEA" +
+    "AAADAAAAAQAAAAQAAAABAAAABQAAAAEAAAAQAAAAAQAAABEAAAABAAAAHAAAAAMAAAAYABEA" +
+    "GQAAAAMAAAAOABEAGQAAAAIAAAAAAAAAAgAAAAEAAQACAAAAAgACAAIAAAADAAMAAwAAAAQA" +
+    "BAAEAAAAAgAAAAUABQACAAAAEAAQAAIAAAARABEAAQAAABcAAAACAAAAGgAaAAEgAAIgKAAB" +
+    "KQAGLCBiMjogAAYsIGMyOiAABiwgZDI6IAAGLCBmMjogAAYsIGkyOiAABiwgbDI6IAAGLCBv" +
+    "MjogAAYsIHMyOiAACDxjbGluaXQ+AAY8aW5pdD4AAUIAAUMAAUQAAUYAJ0ZBSUxVUkVfVFlQ" +
+    "RV9MSU5LRVJfTUVUSE9EX1JFVFVSTlNfTlVMTAAhRkFJTFVSRV9UWVBFX0xJTktFUl9NRVRI" +
+    "T0RfVEhST1dTABFGQUlMVVJFX1RZUEVfTk9ORQAhRkFJTFVSRV9UWVBFX1RBUkdFVF9NRVRI" +
+    "T0RfVEhST1dTAA9GYWlsdXJlIFR5cGUgKyAAAUkAA0lJSQANSU5WT0tFX1NUQVRJQwABSgAB" +
+    "TAACTEMAAkxEAAJMRgACTEkAAkxKAAJMTAAETExMTAAiTFRlc3RMaW5rZXJNZXRob2RNaW5p" +
+    "bWFsQXJndW1lbnRzOwACTFoAM0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0NhbGxl" +
+    "ZEJ5SW52b2tlQ3VzdG9tOwAxTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvTGlua2Vy" +
+    "TWV0aG9kSGFuZGxlOwAvTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvTWV0aG9kSGFu" +
+    "ZGxlS2luZDsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABVMamF2YS9pby9QcmludFN0" +
+    "cmVhbTsAH0xqYXZhL2xhbmcvQXJpdGhtZXRpY0V4Y2VwdGlvbjsAGkxqYXZhL2xhbmcvQXNz" +
+    "ZXJ0aW9uRXJyb3I7ABFMamF2YS9sYW5nL0NsYXNzOwAiTGphdmEvbGFuZy9JbnN0YW50aWF0" +
+    "aW9uRXhjZXB0aW9uOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsA" +
+    "GUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsAEkxqYXZhL2xhbmcvU3lzdGVtOwAVTGphdmEv" +
+    "bGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9DYWxsU2l0ZTsAI0xqYXZhL2xh" +
+    "bmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5nL2ludm9rZS9NZXRob2RI" +
+    "YW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExvb2t1cDsAHUxqYXZh" +
+    "L2xhbmcvaW52b2tlL01ldGhvZFR5cGU7ACdSZXR1cm5pbmcgbnVsbCBpbnN0ZWFkIG9mIENh" +
+    "bGxTaXRlIGZvciAAAVMAJVRlc3RMaW5rZXJNZXRob2RNaW5pbWFsQXJndW1lbnRzLmphdmEA" +
+    "JVRocm93aW5nIEFyaXRobWV0aWNFeGNlcHRpb24gaW4gYWRkKCkAMVRocm93aW5nIEluc3Rh" +
+    "bnRpYXRpb25FeGNlcHRpb24gaW4gbGlua2VyTWV0aG9kKCkAAVYAA1ZCQgADVkNDAANWREQA" +
+    "A1ZGRgADVklJAARWSUlJAANWSkoAAlZMAANWTEwAA1ZTUwACVloAAVoAAlpMAAFhAANhZGQA" +
+    "BmFwcGVuZAANYXJndW1lbnRUeXBlcwAMYXNzZXJ0RXF1YWxzABFhc3NlcnRFcXVhbHMgYjE6" +
+    "IAARYXNzZXJ0RXF1YWxzIGMxOiAAEWFzc2VydEVxdWFscyBkMTogABFhc3NlcnRFcXVhbHMg" +
+    "ZjE6IAARYXNzZXJ0RXF1YWxzIGkxOiAAEWFzc2VydEVxdWFscyBsMTogABFhc3NlcnRFcXVh" +
+    "bHMgczE6IAASYXNzZXJ0RXF1YWxzOiBvMTogAAphc3NlcnRUcnVlABJhc3NlcnRUcnVlIHZh" +
+    "bHVlOiAAAWIAAmIxAAJiMgACYzEAAmMyAAZjYWxsZXIAAmQxAAJkMgASZW1pdHRlcjogamFj" +
+    "ay00LjI1AA1lbmNsb3NpbmdUeXBlAAZlcXVhbHMAAmYxAAJmMgALZmFpbHVyZVR5cGUACmZp" +
+    "bmRTdGF0aWMAEGZvcmNlRmFpbHVyZVR5cGUAAmkxAAJpMgASaW52b2tlTWV0aG9kSGFuZGxl" +
+    "AARraW5kAAJsMQACbDIADGxpbmtlck1ldGhvZAAabGlua2VyTWV0aG9kIGZhaWx1cmUgdHlw" +
+    "ZSAACm1ldGhvZFR5cGUABm1oX2FkZAAEbmFtZQABbwADb3V0AAFwAAdwcmludGxuAApyZXR1" +
+    "cm5UeXBlAAJzMQACczIABHRlc3QABHRoaXMACHRvU3RyaW5nAAV2YWx1ZQABeAABeQAeAAcd" +
+    "Li08PAJ5OwAcAAcOAC8CS1oHDmmHlwBWAltcBw48AFsCXV4HDjwAdAJgYQcOWgBvAmVmBw5a" +
+    "AGUCamsHDjwAagJubwcOWgB5AnV3Bw48tAB/Anp7Bw4tIKUgAGACensHDjwAUAF/Bw4tARoQ" +
+    "ADkDX3RyBw4BGxBpAwBzGGkBJA8taYeXAEgDZ4ABgQEHLId4LZYBLw8CeywtAAAHBE0cAhgE" +
+    "GARrHAEdCARNHAMYGBgRGBliGAZsGwVzF29zF0t4GAQCCgF+HAEYFAMWABdLFQAFAA8AAAoB" +
+    "CgEKAQoBCgCIgAS8CwGBgATsCwEKhAwBCcgMAQmoDQEJiA4BCewOAQnQDwEJsBABCZQRAQmM" +
+    "EgEJhBMBCeQTAQqwFAEJlBYAEwAAAAAAAAABAAAAAAAAAAEAAACBAAAAcAAAAAIAAAAdAAAA" +
+    "dAIAAAMAAAAcAAAA6AIAAAQAAAAHAAAAOAQAAAUAAAAiAAAAcAQAAAcAAAABAAAAgAUAAAYA" +
+    "AAABAAAAhAUAAAgAAAABAAAApAUAAAMQAAACAAAArAUAAAEgAAAPAAAAvAUAAAYgAAABAAAA" +
+    "xAsAAAEQAAAVAAAA7AsAAAIgAACBAAAAoAwAAAMgAAAPAAAAZBMAAAQgAAACAAAAAhQAAAUg" +
+    "AAABAAAAMhQAAAAgAAABAAAAORQAAAAQAAABAAAAiBQAAA==";
+}
diff --git a/test/952-invoke-custom/src/TestDataLinkerMethodMultipleArgumentTypes.java b/test/952-invoke-custom/src/TestDataLinkerMethodMultipleArgumentTypes.java
new file mode 100644
index 0000000..b96e184
--- /dev/null
+++ b/test/952-invoke-custom/src/TestDataLinkerMethodMultipleArgumentTypes.java
@@ -0,0 +1,108 @@
+/* Generated by build-test.sh from TestLinkerMethodMultipleArgumentTypes.java */
+public class TestDataLinkerMethodMultipleArgumentTypes {
+  public static final String BASE64_DEX_FILE =
+    "ZGV4CjAzOADmj8ccx56N3pWZ9IunuZvI0eWD+wmFmSnEFQAAcAAAAHhWNBIAAAAAAAAAANwU" +
+    "AACTAAAAcAAAAB0AAAC8AgAAHQAAADADAAADAAAAjAQAACIAAACkBAAAAQAAALgFAADkDwAA" +
+    "4AUAAEQMAABHDAAASgwAAFIMAABaDAAAYgwAAGoMAAByDAAAegwAAIIMAACKDAAAkgwAAJwM" +
+    "AACkDAAApwwAAKoMAACtDAAAsAwAAMYMAADNDAAA0AwAANUMAADkDAAA5wwAAOoMAADuDAAA" +
+    "8gwAAPYMAAD6DAAA/gwAAAINAAAIDQAAGA0AAEENAABFDQAAeg0AAKMNAADWDQAABw4AACYO" +
+    "AABCDgAATA4AAGMOAAB/DgAAkQ4AAKQOAAC6DgAAzg4AAOIOAAD9DgAAEQ8AACgPAABFDwAA" +
+    "ag8AAIsPAAC0DwAA0w8AANYPAAACEAAABRAAAAoQAAAPEAAAFBAAABkQAAAdEAAAIhAAACcQ" +
+    "AAArEAAAMBAAADUQAAA5EAAAPBAAAEUQAABJEAAATBAAAFEQAABZEAAAaBAAAHYQAACJEAAA" +
+    "nBAAAK8QAADCEAAA1RAAAOgQAAD7EAAADxEAABsRAAAvEQAAMhEAADYRAAA6EQAASBEAAFsR" +
+    "AABmEQAAahEAAG4RAAB2EQAAgREAAI0RAACREQAAlREAAKIRAAC2EQAAxREAAM0RAADREQAA" +
+    "1REAAOERAADtEQAA8REAAPURAAD/EQAAExIAABkSAAAdEgAAIRIAAC8SAAA6EgAAURIAAF0S" +
+    "AABlEgAAaxIAAG4SAABzEgAAdhIAAH8SAACLEgAAjxIAAJMSAACfEgAArBIAALISAAC4EgAA" +
+    "whIAAMYSAADLEgAAzxIAANMSAADXEgAA2xIAAN8SAADjEgAA5xIAAOsSAADyEgAA9RIAAA0A" +
+    "AAAOAAAADwAAABAAAAATAAAAFgAAACAAAAAiAAAAIwAAACQAAAAlAAAAJgAAACcAAAApAAAA" +
+    "KgAAACwAAAAuAAAALwAAADAAAAAxAAAAMgAAADMAAAA0AAAANQAAADYAAAA3AAAAOAAAADoA" +
+    "AABGAAAAEwAAAAQAAAAAAAAAFAAAAAQAAACICwAAFwAAABEAAAAAAAAAGAAAABIAAACQCwAA" +
+    "GQAAABIAAACYCwAAGgAAABIAAACgCwAAGwAAABIAAACoCwAAHAAAABIAAACwCwAAHQAAABIA" +
+    "AAC4CwAAHQAAABIAAADACwAAIQAAABIAAADICwAAHwAAABUAAADQCwAAHgAAABcAAADwCwAA" +
+    "OgAAABsAAAAAAAAAOwAAABsAAAD8CwAAPAAAABsAAAAEDAAAPQAAABsAAAAMDAAAPgAAABsA" +
+    "AAAUDAAAPwAAABsAAACoCwAAQAAAABsAAACICwAAQQAAABsAAAAcDAAAQgAAABsAAAC4CwAA" +
+    "QwAAABsAAAAkDAAAQgAAABsAAADACwAAQwAAABsAAAAsDAAAQgAAABsAAAA0DAAARAAAABsA" +
+    "AAA8DAAARQAAABsAAADICwAASAAAABwAAAC4CwAABgAEAFwAAAAKAAoAFQAAABMADQB7AAAA" +
+    "BgANAAsAAAAGAA0ADAAAAAYAAAARAAAABgABAEoAAAAGAA4ATQAAAAYADwBNAAAABgAQAE0A" +
+    "AAAGABEATQAAAAYAEwBNAAAABgAUAE0AAAAGABYATQAAAAYAGABNAAAABgAaAE0AAAAGABsA" +
+    "VgAAAAYACwB0AAAABgATAIMAAAANABIAfQAAAA0AFwB9AAAADgAVAAwAAAAQAA0ADAAAABAA" +
+    "HABoAAAAEQAcAGgAAAASAA0ADAAAABIAAwBLAAAAEgAEAEsAAAASAAUASwAAABIABgBLAAAA" +
+    "EgAHAEsAAAASAAgASwAAABIACQBLAAAAEgAKAEsAAAASAAIAhQAAABYAGQAMAAAAGAAMAGsA" +
+    "AABpFAAABgAAAAEAAAAQAAAAAAAAADkAAABgCwAAkhQAAAAAAAAEAAAADgAAAAEAAACpEwAA" +
+    "AgAAAEcUAABgFAAAAQAAAGAUAAABAAAAAAAAAPgSAAAEAAAAEgBnAAAADgABAAEAAQAAAP4S" +
+    "AAAEAAAAcBATAAAADgADAAIAAAAAAAMTAAADAAAAkAABAg8AAAAFAAIAAgAAAAoTAAAoAAAA" +
+    "M0MDAA4AIgAOACIBEgBwEBYAAQAbAk4AAABuIB0AIQAMAW4gGgAxAAwBGwICAAAAbiAdACEA" +
+    "DAFuIBoAQQAMAW4QHwABAAwBcCASABAAJwAFAAIAAgAAABITAAAoAAAAM0MDAA4AIgAOACIB" +
+    "EgBwEBYAAQAbAk8AAABuIB0AIQAMAW4gFwAxAAwBGwIDAAAAbiAdACEADAFuIBcAQQAMAW4Q" +
+    "HwABAAwBcCASABAAJwAIAAQAAwAAABoTAAAqAAAALwAEBjkAAwAOACIADgAiARIAcBAWAAEA" +
+    "GwJQAAAAbiAdACEADAFuMBgAQQUMARsCBAAAAG4gHQAhAAwBbjAYAGEHDAFuEB8AAQAMAXAg" +
+    "EgAQACcABQACAAIAAAAiEwAAKgAAAC0AAwQ5AAMADgAiAA4AIgESAHAQFgABABsCUQAAAG4g" +
+    "HQAhAAwBbiAZADEADAEbAgUAAABuIB0AIQAMAW4gGQBBAAwBbhAfAAEADAFwIBIAEAAnAAUA" +
+    "AgACAAAAKhMAACgAAAAzQwMADgAiAA4AIgESAHAQFgABABsCUgAAAG4gHQAhAAwBbiAaADEA" +
+    "DAEbAgYAAABuIB0AIQAMAW4gGgBBAAwBbhAfAAEADAFwIBIAEAAnAAgABAADAAAAMhMAACoA" +
+    "AAAxAAQGOQADAA4AIgAOACIBEgBwEBYAAQAbAlMAAABuIB0AIQAMAW4wGwBBBQwBGwIHAAAA" +
+    "biAdACEADAFuMBsAYQcMAW4QHwABAAwBcCASABAAJwAFAAIAAgAAADoTAAAzAAAAM0MDAA4A" +
+    "OAMLADgECQBuIBQAQwAKADgAAwAOACIADgAiARIAcBAWAAEAGwJVAAAAbiAdACEADAFuIBwA" +
+    "MQAMARsCCAAAAG4gHQAhAAwBbiAcAEEADAFuEB8AAQAMAXAgEgAQACcAAAAFAAIAAgAAAEMT" +
+    "AAAzAAAAM0MDAA4AOAMLADgECQBuIBUAQwAKADgAAwAOACIADgAiARIAcBAWAAEAGwJUAAAA" +
+    "biAdACEADAFuIB0AMQAMARsCCQAAAG4gHQAhAAwBbiAdAEEADAFuEB8AAQAMAXAgEgAQACcA" +
+    "AAAFAAIAAgAAAFETAAAoAAAAM0MDAA4AIgAOACIBEgBwEBYAAQAbAlQAAABuIB0AIQAMAW4g" +
+    "GgAxAAwBGwIJAAAAbiAdACEADAFuIBoAQQAMAW4QHwABAAwBcCASABAAJwAEAAEAAgAAAFsT" +
+    "AAAdAAAAOQMcACIADgAiARIAcBAWAAEAGwJXAAAAbiAdACEADAFuIB4AMQAMAW4QHwABAAwB" +
+    "cCASABAAJwAOAAAAFgAPAAQAAABmEwAAbAAAAGIDAgAiBBIAcBAWAAQAGwUoAAAAbiAdAFQA" +
+    "DARuIB0AhAAMBBsFAAAAAG4gHQBUAAwEbiAcAJQADARuEB8ABAAMBG4gEQBDAHEQDQAKABIT" +
+    "cSAIALMAEwNhAHEgBQDDABMDAARxIAgA0wASE3EgCADjABQDmpkxQXEgBwDzABgEmpmZmZmZ" +
+    "AUAFABAAcUAGAFQQGwMSAAAACAASAHEgCwADABwDBgAIABMAcSAKAAMAFwQVzVsHBQAUAHFA" +
+    "CQBUEBwDBgBuQCEAN5gMAiIDFgBwICAAIwARAwQAAgACAAAAmRMAABEAAACQAAID/CAAADIA" +
+    "CgFxIAgAEABiAAIAkAECA24gEAAQAA4AAAACAAEAAAAAAKQTAAADAAAAYAAAAA8AAAAAAAAA" +
+    "AAAAAAMAAAAAAAAAAwAAAOAFAAAOAAAA6AUAAA8AAAD0BQAAAgAAAAQABAABAAAAAQAAAAEA" +
+    "AAACAAAAAQAAAAMAAAABAAAABAAAAAEAAAAFAAAAAQAAABAAAAABAAAAEQAAAAEAAAAcAAAA" +
+    "DQAAABgAEQAZABwAAAABABoABAADAAIAEQAPAAUAAAADAAAADwARABkAAAACAAAAAAAAAAIA" +
+    "AAABAAEAAgAAAAIAAgACAAAAAwADAAIAAAAFAAUAAgAAABAAEAACAAAAEQARAAEAAAAXAAAA" +
+    "AgAAABoAGgABIAABKAAGLCBiMjogAAYsIGMyOiAABiwgZDI6IAAGLCBmMjogAAYsIGkyOiAA" +
+    "BiwgbDI6IAAGLCBvMjogAAYsIHMyOiAABjwqPjtKKQAIPGNsaW5pdD4ABjxpbml0PgABQgAB" +
+    "QwABRAABRgAUR2V0Qm9vdHN0cmFwUnVuQ291bnQABUhlbGxvAAFJAANJSUkADUlOVk9LRV9T" +
+    "VEFUSUMAAUoAAUwAAkxDAAJMRAACTEYAAkxJAAJMSgACTEwABExMTEwADkxMTExaQkNTSUZE" +
+    "TExKACdMVGVzdExpbmtlck1ldGhvZE11bHRpcGxlQXJndW1lbnRUeXBlczsAAkxaADNMY29t" +
+    "L2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9DYWxsZWRCeUludm9rZUN1c3RvbTsAJ0xjb20v" +
+    "YW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0NvbnN0YW50OwAxTGNvbS9hbmRyb2lkL2phY2sv" +
+    "YW5ub3RhdGlvbnMvTGlua2VyTWV0aG9kSGFuZGxlOwAvTGNvbS9hbmRyb2lkL2phY2svYW5u" +
+    "b3RhdGlvbnMvTWV0aG9kSGFuZGxlS2luZDsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1" +
+    "cmU7ABpMZGFsdmlrL2Fubm90YXRpb24vVGhyb3dzOwAITGlua2luZyAAFUxqYXZhL2lvL1By" +
+    "aW50U3RyZWFtOwAaTGphdmEvbGFuZy9Bc3NlcnRpb25FcnJvcjsAEExqYXZhL2xhbmcvQ2xh" +
+    "c3MAEUxqYXZhL2xhbmcvQ2xhc3M7ABRMamF2YS9sYW5nL0NsYXNzPCo+OwASTGphdmEvbGFu" +
+    "Zy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRl" +
+    "cjsAEkxqYXZhL2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9s" +
+    "YW5nL2ludm9rZS9DYWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNp" +
+    "dGU7AB9MamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9r" +
+    "ZS9NZXRob2RIYW5kbGVzJExvb2t1cDsAHUxqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5cGU7" +
+    "AAFTACpUZXN0TGlua2VyTWV0aG9kTXVsdGlwbGVBcmd1bWVudFR5cGVzLmphdmEAAVYAA1ZC" +
+    "QgADVkNDAANWREQAA1ZGRgACVkkAA1ZJSQADVkpKAAJWTAADVkxMAANWU1MAAlZaAAFaAAda" +
+    "QkNTSUZEAAJaTAABYQADYWRkAAZhcHBlbmQADWFyZ3VtZW50VHlwZXMADGFzc2VydEVxdWFs" +
+    "cwARYXNzZXJ0RXF1YWxzIGIxOiAAEWFzc2VydEVxdWFscyBjMTogABFhc3NlcnRFcXVhbHMg" +
+    "ZDE6IAARYXNzZXJ0RXF1YWxzIGYxOiAAEWFzc2VydEVxdWFscyBpMTogABFhc3NlcnRFcXVh" +
+    "bHMgbDE6IAARYXNzZXJ0RXF1YWxzIHMxOiAAEmFzc2VydEVxdWFsczogbzE6IAAKYXNzZXJ0" +
+    "VHJ1ZQASYXNzZXJ0VHJ1ZSB2YWx1ZTogAAFiAAJiMQACYjIADGJvb2xlYW5WYWx1ZQARYm9v" +
+    "dHN0cmFwUnVuQ291bnQACWJ5dGVWYWx1ZQACYzEAAmMyAAZjYWxsZXIACWNoYXJWYWx1ZQAK" +
+    "Y2xhc3NWYWx1ZQACZDEAAmQyAAtkb3VibGVWYWx1ZQASZW1pdHRlcjogamFjay00LjI1AA1l" +
+    "bmNsb3NpbmdUeXBlAAZlcXVhbHMAAmYxAAJmMgAKZmluZFN0YXRpYwAKZmxvYXRWYWx1ZQAC" +
+    "aTEAAmkyAAhpbnRWYWx1ZQASaW52b2tlTWV0aG9kSGFuZGxlAARraW5kAAJsMQACbDIADGxp" +
+    "bmtlck1ldGhvZAAJbG9uZ1ZhbHVlABVtZXRob2RIYW5kbGVFeHRyYUFyZ3MACm1ldGhvZFR5" +
+    "cGUABm1oX2FkZAAEbmFtZQABbwADb3V0AAFwAAdwcmludGxuAApyZXR1cm5UeXBlAAJzMQAC" +
+    "czIACnNob3J0VmFsdWUAC3N0cmluZ1ZhbHVlAAR0ZXN0AAR0aGlzAAh0b1N0cmluZwACdjEA" +
+    "A3YxMAACdjIAAnYzAAJ2NAACdjUAAnY2AAJ2NwACdjgAAnY5AAV2YWx1ZQABeAABeQAeAAcO" +
+    "OQAcAAcOADECSlkHDgBZAlpbBw48AF4CX2AHDjwAdwJkZQcOWgByAmprBw5aAGgCbm8HDjwA" +
+    "bQJzdAcOWgB8Ant9Bw48tACCAQKAAYEBBw4tIKUgAGMCgAGBAQcOPABTAZEBBw4tARoQADkN" +
+    "YXp4hwGJAYoBiwGMAY0BjgGPAQCIAQQTkAEQLgcOASQPPEtaWktppYd4iGkDAnkYAE4CkgGT" +
+    "AQcOlngASgAHDgAABwVMHAIYBBgEcBwBHQkETBwNGBgYERgZGBwYABgBGBoYBBgDGAIYERgP" +
+    "GAVnGAZxGwF5F3R2HAodCAFbHAE/HQgBXRwBAAEdCAFhHAEDYR0IAYEBHAEiAAQdCAFvHAEE" +
+    "AR0IAWwcAXCamTFBHQgBZRwB8ZqZmZmZmQFAHQgBggEcARcSHQgBYhwBGAYdCAF1HAFmFc1b" +
+    "B3kXSn4YBAILAZABHAkXARc2Fy8XNxdHFy8XKxcKFzMCDAGQARwBGBQNFgAXShUBBAEEAQRh" +
+    "JAAEBAFwmpkxQfGamZmZmZkBQBcSGAZmFc1bBwEADwEACgCIgAT8CwGBgASUDAIKrAwBCcQM" +
+    "AQmkDQEJhA4BCegOAQnMDwEJrBABCZARAQmIEgEJgBMBCeATAQqsFAEJlBYCAcgWEwAAAAAA" +
+    "AAABAAAAAAAAAAEAAACTAAAAcAAAAAIAAAAdAAAAvAIAAAMAAAAdAAAAMAMAAAQAAAADAAAA" +
+    "jAQAAAUAAAAiAAAApAQAAAcAAAABAAAAtAUAAAYAAAABAAAAuAUAAAgAAAABAAAA2AUAAAMQ" +
+    "AAADAAAA4AUAAAEgAAAQAAAA/AUAAAYgAAABAAAAYAsAAAEQAAAUAAAAiAsAAAIgAACTAAAA" +
+    "RAwAAAMgAAAQAAAA+BIAAAQgAAADAAAAqRMAAAUgAAABAAAAaRQAAAAgAAABAAAAkhQAAAAQ" +
+    "AAABAAAA3BQAAA==";
+}
diff --git a/test/Android.bp b/test/Android.bp
index d3244a6..00c890a 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -274,6 +274,7 @@
         "933-misc-events/misc_events.cc",
         "936-search-onload/search_onload.cc",
         "944-transform-classloaders/classloader.cc",
+        "945-obsolete-native/obsolete_native.cc",
     ],
     shared_libs: [
         "libbase",
diff --git a/test/dexdump/invoke-custom.dex b/test/dexdump/invoke-custom.dex
new file mode 100644
index 0000000..67261ca
--- /dev/null
+++ b/test/dexdump/invoke-custom.dex
Binary files differ
diff --git a/test/dexdump/invoke-custom.lst b/test/dexdump/invoke-custom.lst
new file mode 100644
index 0000000..3540bd1
--- /dev/null
+++ b/test/dexdump/invoke-custom.lst
@@ -0,0 +1,6 @@
+#invoke-custom.dex
+0x000003fc 8 com.android.jack.java7.invokecustom.test004.Tests <init> ()V Tests.java 35
+0x00000414 6 com.android.jack.java7.invokecustom.test004.Tests add (II)I Tests.java 55
+0x0000042c 166 com.android.jack.java7.invokecustom.test004.Tests linkerMethod (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite; Tests.java 62
+0x000004e4 24 com.android.jack.java7.invokecustom.test004.Tests main ([Ljava/lang/String;)V Tests.java 82
+0x0000050c 22 com.android.jack.java7.invokecustom.test004.Tests test ()V Tests.java 78
diff --git a/test/dexdump/invoke-custom.txt b/test/dexdump/invoke-custom.txt
new file mode 100644
index 0000000..e92549a
--- /dev/null
+++ b/test/dexdump/invoke-custom.txt
@@ -0,0 +1,254 @@
+Processing 'invoke-custom.dex'...
+Opened 'invoke-custom.dex', DEX version '038'
+DEX file header:
+magic               : 'dex\n038\0'
+checksum            : db57516f
+signature           : 57be...ffc4
+file_size           : 3276
+header_size         : 112
+link_size           : 0
+link_off            : 0 (0x000000)
+string_ids_size     : 82
+string_ids_off      : 112 (0x000070)
+type_ids_size       : 31
+type_ids_off        : 440 (0x0001b8)
+proto_ids_size      : 16
+proto_ids_off       : 564 (0x000234)
+field_ids_size      : 3
+field_ids_off       : 756 (0x0002f4)
+method_ids_size     : 18
+method_ids_off      : 780 (0x00030c)
+class_defs_size     : 1
+class_defs_off      : 932 (0x0003a4)
+data_size           : 2304
+data_off            : 972 (0x0003cc)
+
+Class #0 header:
+class_idx           : 10
+access_flags        : 1 (0x0001)
+superclass_idx      : 15
+interfaces_off      : 0 (0x000000)
+source_file_idx     : 38
+annotations_off     : 1316 (0x000524)
+class_data_off      : 3014 (0x000bc6)
+static_fields_size  : 1
+instance_fields_size: 0
+direct_methods_size : 4
+virtual_methods_size: 1
+
+Class #0 annotations:
+Annotations on method #1 'add'
+  VISIBILITY_BUILD Lcom/android/jack/annotations/CalledByInvokeCustom; argumentTypes={ I I } invokeMethodHandle={ Lcom/android/jack/annotations/LinkerMethodHandle; argumentTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; Z B C S I F D Ljava/lang/String; Ljava/lang/Class; J } enclosingType=Lcom/android/jack/java7/invokecustom/test004/Tests; kind=INVOKE_STATIC name="linkerMethod" } methodHandleExtraArgs={ Lcom/android/jack/annotations/Constant; booleanValue={ true } Lcom/android/jack/annotations/Constant; byteValue={ 1 } Lcom/android/jack/annotations/Constant; charValue={ 97 } Lcom/android/jack/annotations/Constant; shortValue={ 1024 } Lcom/android/jack/annotations/Constant; intValue={ 1 } Lcom/android/jack/annotations/Constant; floatValue={ 11.1 } Lcom/android/jack/annotations/Constant; doubleValue={ 2.2 } Lcom/android/jack/annotations/Constant; stringValue={ "Hello" } Lcom/android/jack/annotations/Constant; classValue={ Lcom/android/jack/java7/invokecustom/test004/Tests; } Lcom/android/jack/annotations/Constant; longValue={ 123456789 } } name="add" returnType=I
+Annotations on method #2 'linkerMethod'
+  VISIBILITY_SYSTEM Ldalvik/annotation/Signature; value={ "(" "Ljava/lang/invoke/MethodHandles$Lookup;" "Ljava/lang/String;" "Ljava/lang/invoke/MethodType;" "ZBCSIFD" "Ljava/lang/String;" "Ljava/lang/Class" "<*>;J)" "Ljava/lang/invoke/CallSite;" }
+  VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; }
+Annotations on method #4 'test'
+  VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; }
+  VISIBILITY_RUNTIME Lorg/junit/Test;
+
+Class #0            -
+  Class descriptor  : 'Lcom/android/jack/java7/invokecustom/test004/Tests;'
+  Access flags      : 0x0001 (PUBLIC)
+  Superclass        : 'Ljava/lang/Object;'
+  Interfaces        -
+  Static fields     -
+    #0              : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+      name          : 'fieldCallSite'
+      type          : 'Ljava/lang/invoke/CallSite;'
+      access        : 0x0009 (PUBLIC STATIC)
+  Instance fields   -
+  Direct methods    -
+    #0              : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+      name          : '<init>'
+      type          : '()V'
+      access        : 0x10001 (PUBLIC CONSTRUCTOR)
+      code          -
+      registers     : 1
+      ins           : 1
+      outs          : 1
+      insns size    : 4 16-bit code units
+0003ec:                                        |[0003ec] com.android.jack.java7.invokecustom.test004.Tests.<init>:()V
+0003fc: 7010 0600 0000                         |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0006
+000402: 0e00                                   |0003: return-void
+      catches       : (none)
+      positions     : 
+        0x0000 line=35
+      locals        : 
+        0x0000 - 0x0004 reg=0 this Lcom/android/jack/java7/invokecustom/test004/Tests; 
+
+    #1              : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+      name          : 'add'
+      type          : '(II)I'
+      access        : 0x000a (PRIVATE STATIC)
+      code          -
+      registers     : 3
+      ins           : 2
+      outs          : 0
+      insns size    : 3 16-bit code units
+000404:                                        |[000404] com.android.jack.java7.invokecustom.test004.Tests.add:(II)I
+000414: 9000 0102                              |0000: add-int v0, v1, v2
+000418: 0f00                                   |0002: return v0
+      catches       : (none)
+      positions     : 
+        0x0000 line=55
+      locals        : 
+        0x0000 - 0x0003 reg=1 (null) I 
+        0x0000 - 0x0003 reg=2 (null) I 
+
+    #2              : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+      name          : 'linkerMethod'
+      type          : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;'
+      access        : 0x000a (PRIVATE STATIC)
+      code          -
+      registers     : 24
+      ins           : 15
+      outs          : 6
+      insns size    : 83 16-bit code units
+00041c:                                        |[00041c] com.android.jack.java7.invokecustom.test004.Tests.linkerMethod:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;
+00042c: 7110 1100 0c00                         |0000: invoke-static {v12}, Ljunit/framework/Assert;.assertTrue:(Z)V // method@0011
+000432: 1212                                   |0003: const/4 v2, #int 1 // #1
+000434: 7120 0d00 d200                         |0004: invoke-static {v2, v13}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d
+00043a: 1302 6100                              |0007: const/16 v2, #int 97 // #61
+00043e: 7120 0a00 e200                         |0009: invoke-static {v2, v14}, Ljunit/framework/Assert;.assertEquals:(CC)V // method@000a
+000444: 1302 0004                              |000c: const/16 v2, #int 1024 // #400
+000448: 7120 0d00 f200                         |000e: invoke-static {v2, v15}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d
+00044e: 1212                                   |0011: const/4 v2, #int 1 // #1
+000450: 0200 1000                              |0012: move/from16 v0, v16
+000454: 7120 0d00 0200                         |0014: invoke-static {v2, v0}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d
+00045a: 1202                                   |0017: const/4 v2, #int 0 // #0
+00045c: 1403 9a99 3141                         |0018: const v3, #float 11.1 // #4131999a
+000462: 0200 1100                              |001b: move/from16 v0, v17
+000466: 7130 0c00 0302                         |001d: invoke-static {v3, v0, v2}, Ljunit/framework/Assert;.assertEquals:(FFF)V // method@000c
+00046c: 1606 0000                              |0020: const-wide/16 v6, #int 0 // #0
+000470: 1802 9a99 9999 9999 0140               |0022: const-wide v2, #double 2.2 // #400199999999999a
+00047a: 0504 1200                              |0027: move-wide/from16 v4, v18
+00047e: 7706 0b00 0200                         |0029: invoke-static/range {v2, v3, v4, v5, v6, v7}, Ljunit/framework/Assert;.assertEquals:(DDD)V // method@000b
+000484: 1b02 0700 0000                         |002c: const-string/jumbo v2, "Hello" // string@00000007
+00048a: 0800 1400                              |002f: move-object/from16 v0, v20
+00048e: 7120 1000 0200                         |0031: invoke-static {v2, v0}, Ljunit/framework/Assert;.assertEquals:(Ljava/lang/String;Ljava/lang/String;)V // method@0010
+000494: 1c02 0a00                              |0034: const-class v2, Lcom/android/jack/java7/invokecustom/test004/Tests; // type@000a
+000498: 0800 1500                              |0036: move-object/from16 v0, v21
+00049c: 7120 0f00 0200                         |0038: invoke-static {v2, v0}, Ljunit/framework/Assert;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@000f
+0004a2: 1702 15cd 5b07                         |003b: const-wide/32 v2, #float 1.6536e-34 // #075bcd15
+0004a8: 0500 1600                              |003e: move-wide/from16 v0, v22
+0004ac: 7140 0e00 3210                         |0040: invoke-static {v2, v3, v0, v1}, Ljunit/framework/Assert;.assertEquals:(JJ)V // method@000e
+0004b2: 7100 0900 0000                         |0043: invoke-static {}, Ljava/lang/invoke/MethodHandles;.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; // method@0009
+0004b8: 0c02                                   |0046: move-result-object v2
+0004ba: 1c03 0a00                              |0047: const-class v3, Lcom/android/jack/java7/invokecustom/test004/Tests; // type@000a
+0004be: 6e40 0800 32ba                         |0049: invoke-virtual {v2, v3, v10, v11}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@0008
+0004c4: 0c02                                   |004c: move-result-object v2
+0004c6: 2203 1400                              |004d: new-instance v3, Ljava/lang/invoke/ConstantCallSite; // type@0014
+0004ca: 7020 0700 2300                         |004f: invoke-direct {v3, v2}, Ljava/lang/invoke/ConstantCallSite;.<init>:(Ljava/lang/invoke/MethodHandle;)V // method@0007
+0004d0: 1103                                   |0052: return-object v3
+      catches       : (none)
+      positions     : 
+        0x0000 line=62
+        0x0003 line=63
+        0x0007 line=64
+        0x000c line=65
+        0x0011 line=66
+        0x0017 line=67
+        0x0020 line=68
+        0x002c line=69
+        0x0034 line=70
+        0x003b line=71
+        0x0043 line=72
+        0x004d line=73
+      locals        : 
+        0x0000 - 0x0053 reg=9 (null) Ljava/lang/invoke/MethodHandles$Lookup; 
+        0x0000 - 0x0053 reg=10 (null) Ljava/lang/String; 
+        0x0000 - 0x0053 reg=11 (null) Ljava/lang/invoke/MethodType; 
+        0x0000 - 0x0053 reg=12 (null) Z 
+        0x0000 - 0x0053 reg=13 (null) B 
+        0x0000 - 0x0053 reg=14 (null) C 
+        0x0000 - 0x0053 reg=15 (null) S 
+        0x0000 - 0x0053 reg=16 (null) I 
+        0x0000 - 0x0053 reg=17 (null) F 
+        0x0000 - 0x0053 reg=18 (null) D 
+        0x0000 - 0x0053 reg=20 (null) Ljava/lang/String; 
+        0x0000 - 0x0053 reg=21 (null) Ljava/lang/Class; 
+        0x0000 - 0x0053 reg=22 (null) J 
+
+    #3              : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+      name          : 'main'
+      type          : '([Ljava/lang/String;)V'
+      access        : 0x0009 (PUBLIC STATIC)
+      code          -
+      registers     : 4
+      ins           : 1
+      outs          : 2
+      insns size    : 12 16-bit code units
+0004d4:                                        |[0004d4] com.android.jack.java7.invokecustom.test004.Tests.main:([Ljava/lang/String;)V
+0004e4: 6200 0200                              |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+0004e8: 1221                                   |0002: const/4 v1, #int 2 // #2
+0004ea: 1232                                   |0003: const/4 v2, #int 3 // #3
+0004ec: fc20 0000 2100                         |0004: invoke-custom {v1, v2}, call_site@0000
+0004f2: 0a01                                   |0007: move-result v1
+0004f4: 6e20 0500 1000                         |0008: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(I)V // method@0005
+0004fa: 0e00                                   |000b: return-void
+      catches       : (none)
+      positions     : 
+        0x0000 line=82
+        0x000b line=83
+      locals        : 
+        0x0000 - 0x000c reg=3 (null) [Ljava/lang/String; 
+
+  Virtual methods   -
+    #0              : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+      name          : 'test'
+      type          : '()V'
+      access        : 0x0001 (PUBLIC)
+      code          -
+      registers     : 3
+      ins           : 1
+      outs          : 2
+      insns size    : 11 16-bit code units
+0004fc:                                        |[0004fc] com.android.jack.java7.invokecustom.test004.Tests.test:()V
+00050c: 1220                                   |0000: const/4 v0, #int 2 // #2
+00050e: 1231                                   |0001: const/4 v1, #int 3 // #3
+000510: fc20 0100 1000                         |0002: invoke-custom {v0, v1}, call_site@0001
+000516: 0a00                                   |0005: move-result v0
+000518: 1251                                   |0006: const/4 v1, #int 5 // #5
+00051a: 7120 0d00 0100                         |0007: invoke-static {v1, v0}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d
+000520: 0e00                                   |000a: return-void
+      catches       : (none)
+      positions     : 
+        0x0000 line=78
+        0x000a line=79
+      locals        : 
+        0x0000 - 0x000b reg=2 this Lcom/android/jack/java7/invokecustom/test004/Tests; 
+
+  source_file_idx   : 38 (Tests.java)
+
+Method handle #0:
+  type        : invoke-static
+  target      : Lcom/android/jack/java7/invokecustom/test004/Tests; linkerMethod
+  target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;
+Call site #0:
+  link_argument[0] : 0 (MethodHandle)
+  link_argument[1] : add (String)
+  link_argument[2] : (II)I (MethodType)
+  link_argument[3] : 1 (int)
+  link_argument[4] : 1 (int)
+  link_argument[5] : 97 (int)
+  link_argument[6] : 1024 (int)
+  link_argument[7] : 1 (int)
+  link_argument[8] : 11.1 (float)
+  link_argument[9] : 2.2 (double)
+  link_argument[10] : Hello (String)
+  link_argument[11] : Tests (Class)
+  link_argument[12] : 123456789 (long)
+Call site #1:
+  link_argument[0] : 0 (MethodHandle)
+  link_argument[1] : add (String)
+  link_argument[2] : (II)I (MethodType)
+  link_argument[3] : 1 (int)
+  link_argument[4] : 1 (int)
+  link_argument[5] : 97 (int)
+  link_argument[6] : 1024 (int)
+  link_argument[7] : 1 (int)
+  link_argument[8] : 11.1 (float)
+  link_argument[9] : 2.2 (double)
+  link_argument[10] : Hello (String)
+  link_argument[11] : Tests (Class)
+  link_argument[12] : 123456789 (long)
diff --git a/test/dexdump/invoke-custom.xml b/test/dexdump/invoke-custom.xml
new file mode 100644
index 0000000..2a29667
--- /dev/null
+++ b/test/dexdump/invoke-custom.xml
@@ -0,0 +1,89 @@
+<api>
+<package name="com.android.jack.java7.invokecustom.test004"
+>
+<class name="Tests"
+ extends="java.lang.Object"
+ interface="false"
+ abstract="false"
+ static="false"
+ final="false"
+ visibility="public"
+>
+<field name="fieldCallSite"
+ type="java.lang.invoke.CallSite"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</field>
+<constructor name="Tests"
+ type="com.android.jack.java7.invokecustom.test004.Tests"
+ static="false"
+ final="false"
+ visibility="public"
+>
+</constructor>
+<method name="main"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+<parameter name="arg0" type="java.lang.String[]">
+</parameter>
+</method>
+<method name="test"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ visibility="public"
+>
+</method>
+</class>
+<method_handle index="0"
+ type="invoke-static"
+ target_class="Lcom/android/jack/java7/invokecustom/test004/Tests;"
+ target_member="linkerMethod"
+ target_member_type="(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;"
+>
+</method_handle>
+<call_site index="0">
+<link_argument index="0" type="MethodHandle" value="0"/>
+<link_argument index="1" type="String" values="add"/>
+<link_argument index="2" type="MethodType" value="(II)I"/>
+<link_argument index="3" type="int" value="1"/>
+<link_argument index="4" type="int" value="1"/>
+<link_argument index="5" type="int" value="97"/>
+<link_argument index="6" type="int" value="1024"/>
+<link_argument index="7" type="int" value="1"/>
+<link_argument index="8" type="float" value="11.1"/>
+<link_argument index="9" type="double" value="2.2"/>
+<link_argument index="10" type="String" value="Hello"/>
+<link_argument index="11" type="Class" value="Tests"/>
+<link_argument index="12" type="long" value="123456789"/>
+</call_site>
+<call_site index="1">
+<link_argument index="0" type="MethodHandle" value="0"/>
+<link_argument index="1" type="String" values="add"/>
+<link_argument index="2" type="MethodType" value="(II)I"/>
+<link_argument index="3" type="int" value="1"/>
+<link_argument index="4" type="int" value="1"/>
+<link_argument index="5" type="int" value="97"/>
+<link_argument index="6" type="int" value="1024"/>
+<link_argument index="7" type="int" value="1"/>
+<link_argument index="8" type="float" value="11.1"/>
+<link_argument index="9" type="double" value="2.2"/>
+<link_argument index="10" type="String" value="Hello"/>
+<link_argument index="11" type="Class" value="Tests"/>
+<link_argument index="12" type="long" value="123456789"/>
+</call_site>
+</package>
+</api>
diff --git a/test/etc/default-build b/test/etc/default-build
index e9e3886..4318966 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -60,6 +60,12 @@
   HAS_SRC_DEX2OAT_UNRESOLVED=false
 fi
 
+# Allow overriding ZIP_COMPRESSION_METHOD with e.g. 'store'
+ZIP_COMPRESSION_METHOD="deflate"
+# Align every ZIP file made by calling $ZIPALIGN command?
+WITH_ZIP_ALIGN=false
+ZIP_ALIGN_BYTES="-1"
+
 DX_FLAGS=""
 SKIP_DX_MERGER="false"
 EXPERIMENTAL=""
@@ -73,6 +79,7 @@
 JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
 JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
 JACK_EXPERIMENTAL_ARGS["method-handles"]="-D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b1"
+JACK_EXPERIMENTAL_ARGS[${DEFAULT_EXPERIMENT}]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
 
 declare -A SMALI_EXPERIMENTAL_ARGS
 SMALI_EXPERIMENTAL_ARGS["default-methods"]="--api-level 24"
@@ -83,6 +90,7 @@
 JAVAC_EXPERIMENTAL_ARGS["default-methods"]="-source 1.8 -target 1.8"
 JAVAC_EXPERIMENTAL_ARGS["lambdas"]="-source 1.8 -target 1.8"
 JAVAC_EXPERIMENTAL_ARGS["method-handles"]="-source 1.8 -target 1.8"
+# We need to leave javac at default 1.7 so that dx will continue to work
 JAVAC_EXPERIMENTAL_ARGS[${DEFAULT_EXPERIMENT}]="-source 1.7 -target 1.7"
 JAVAC_EXPERIMENTAL_ARGS["agents"]="-source 1.8 -target 1.8"
 
@@ -118,6 +126,17 @@
     DEFAULT_EXPERIMENT=""
     EXPERIMENTAL="${EXPERIMENTAL} $1"
     shift
+  elif [ "x$1" = "x--zip-compression-method" ]; then
+    # Allow using different zip compression method, e.g. 'store'
+    shift
+    ZIP_COMPRESSION_METHOD="$1"
+    shift
+  elif [ "x$1" = "x--zip-align" ]; then
+    # Align ZIP entries to some # of bytes.
+    shift
+    WITH_ZIP_ALIGN=true
+    ZIP_ALIGN_BYTES="$1"
+    shift
   elif expr "x$1" : "x--" >/dev/null 2>&1; then
     echo "unknown $0 option: $1" 1>&2
     exit 1
@@ -146,6 +165,26 @@
   JAVAC_ARGS="${JAVAC_ARGS} ${JAVAC_EXPERIMENTAL_ARGS[${experiment}]}"
 done
 
+#########################################
+
+# Catch all commands to 'ZIP' and prepend extra flags.
+# Optionally, zipalign results to some alignment.
+function zip() {
+  local zip_target="$1"
+  local entry_src="$2"
+  shift 2
+
+  command zip --compression-method "$ZIP_COMPRESSION_METHOD" "$zip_target" "$entry_src" "$@"
+
+  if "$WITH_ZIP_ALIGN"; then
+    # zipalign does not operate in-place, so write results to a temp file.
+    local tmp_file="$(mktemp)"
+    "$ZIPALIGN" -f "$ZIP_ALIGN_BYTES" "$zip_target" "$tmp_file"
+    # replace original zip target with our temp file.
+    mv "$tmp_file" "$zip_target"
+  fi
+}
+
 if [ -e classes.dex ]; then
   zip $TEST_NAME.jar classes.dex
   exit 0
@@ -234,7 +273,7 @@
   fi
 fi
 
-if [ "${HAS_SMALI}" = "true" ]; then
+if [ "${HAS_SMALI}" = "true" -a ${NEED_DEX} = "true" ]; then
   # Compile Smali classes
   ${SMALI} -JXmx512m ${SMALI_ARGS} --output smali_classes.dex `find smali -name '*.smali'`
 
@@ -246,7 +285,7 @@
   fi
 fi
 
-if [ "${HAS_SMALI_MULTIDEX}" = "true" ]; then
+if [ "${HAS_SMALI_MULTIDEX}" = "true" -a ${NEED_DEX} = "true" ]; then
   # Compile Smali classes
   ${SMALI} -JXmx512m ${SMALI_ARGS} --output smali_classes2.dex `find smali-multidex -name '*.smali'`
 
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 186a151..7d218f1 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -64,6 +64,10 @@
 APP_IMAGE="y"
 VDEX_FILTER=""
 
+# if "y", run 'sync' before dalvikvm to make sure all files from
+# build step (e.g. dex2oat) were finished writing.
+SYNC_BEFORE_RUN="n"
+
 while true; do
     if [ "x$1" = "x--quiet" ]; then
         QUIET="y"
@@ -262,6 +266,9 @@
         option="$1"
         VDEX_FILTER="--compiler-filter=$option"
         shift
+    elif [ "x$1" = "x--sync" ]; then
+        SYNC_BEFORE_RUN="y"
+        shift
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
         echo "unknown $0 option: $1" 1>&2
         exit 1
@@ -491,6 +498,7 @@
 vdex_cmdline="true"
 mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA"
 strip_cmdline="true"
+sync_cmdline="true"
 
 
 if [ "$PREBUILD" = "y" ]; then
@@ -530,6 +538,10 @@
   strip_cmdline="zip --quiet --delete $DEX_LOCATION/$TEST_NAME.jar classes.dex"
 fi
 
+if [ "$SYNC_BEFORE_RUN" = "y" ]; then
+  sync_cmdline="sync"
+fi
+
 DALVIKVM_ISA_FEATURES_ARGS=""
 if [ "x$INSTRUCTION_SET_FEATURES" != "x" ] ; then
   DALVIKVM_ISA_FEATURES_ARGS="-Xcompiler-option --instruction-set-features=${INSTRUCTION_SET_FEATURES}"
@@ -589,7 +601,8 @@
       LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBRARY_DIRECTORY:$LD_LIBRARY_PATH
     fi
 
-    PUBLIC_LIBS=libart.so:libartd.so
+    # System libraries needed by libarttestd.so
+    PUBLIC_LIBS=libart.so:libartd.so:libc++.so:libbacktrace.so:libbase.so:libnativehelper.so
 
     # Create a script with the command. The command can get longer than the longest
     # allowed adb command and there is no way to get the exit status from a adb shell
@@ -607,6 +620,7 @@
              $dex2oat_cmdline && \
              $vdex_cmdline && \
              $strip_cmdline && \
+             $sync_cmdline && \
              $dalvikvm_cmdline"
 
     cmdfile=$(tempfile -p "cmd-" -s "-$TEST_NAME")
@@ -679,7 +693,7 @@
     fi
 
     if [ "$DEV_MODE" = "y" ]; then
-      echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $vdex_cmdline && $strip_cmdline && $cmdline"
+      echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline"
     fi
 
     cd $ANDROID_BUILD_TOP
@@ -689,6 +703,7 @@
     $dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
     $vdex_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
     $strip_cmdline || { echo "Strip failed." >&2 ; exit 3; }
+    $sync_cmdline || { echo "Sync failed." >&2 ; exit 4; }
 
     # For running, we must turn off logging when dex2oat or patchoat are missing. Otherwise we use
     # the same defaults as for prebuilt: everything when --dev, otherwise errors and above only.
diff --git a/test/run-test b/test/run-test
index 27c700e..c926c11 100755
--- a/test/run-test
+++ b/test/run-test
@@ -90,6 +90,22 @@
 
 export JACK="$JACK -g -cp $JACK_CLASSPATH"
 
+# Zipalign is not on the PATH in some configs, auto-detect it.
+if [ -z "$ZIPALIGN" ]; then
+  if which zipalign >/dev/null; then
+    ZIPALIGN="zipalign";
+  else
+    # TODO: Add a dependency for zipalign in Android.run-test.mk
+    # once it doesn't depend on libandroidfw (b/35246701)
+    case "$OSTYPE" in
+      darwin*)  ZIPALIGN="$ANDROID_BUILD_TOP/prebuilts/sdk/tools/darwin/bin/zipalign" ;;
+      linux*)   ZIPALIGN="$ANDROID_BUILD_TOP/prebuilts/sdk/tools/linux/bin/zipalign" ;;
+      *)        echo "Can't find zipalign: unknown: $OSTYPE" >&2;;
+    esac
+  fi
+fi
+export ZIPALIGN
+
 info="info.txt"
 build="build"
 run="run"
@@ -495,7 +511,7 @@
         run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}:${ANDROID_HOST_OUT}/nativetest${suffix64}"
     else
         guess_target_arch_name
-        run_args="${run_args} --runtime-option -Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}:${android_root}/lib${suffix64}"
+        run_args="${run_args} --runtime-option -Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}"
         run_args="${run_args} --boot /data/art-test/core${image_suffix}${pic_image_suffix}${multi_image_suffix}.art"
     fi
     if [ "$relocate" = "yes" ]; then
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index 1dc8ce5..0b69718 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -100,7 +100,7 @@
 ART_TEST_JIT = getEnvBoolean('ART_TEST_JIT', ART_TEST_FULL)
 
 # Do you want optimizing compiler tests run?
-ART_TEST_OPTIMIZING = getEnvBoolean('ART_TEST_OPTIMIZING', True)
+ART_TEST_OPTIMIZING = getEnvBoolean('ART_TEST_OPTIMIZING', ART_TEST_FULL)
 
 # Do you want to test the optimizing compiler with graph coloring register allocation?
 ART_TEST_OPTIMIZING_GRAPH_COLOR = getEnvBoolean('ART_TEST_OPTIMIZING_GRAPH_COLOR', ART_TEST_FULL)
@@ -129,13 +129,13 @@
 ART_TEST_RUN_TEST_RELOCATE = getEnvBoolean('ART_TEST_RUN_TEST_RELOCATE', ART_TEST_FULL)
 
 # Do you want run-tests with prebuilding?
-ART_TEST_RUN_TEST_PREBUILD = getEnvBoolean('ART_TEST_RUN_TEST_PREBUILD', True)
+ART_TEST_RUN_TEST_PREBUILD = getEnvBoolean('ART_TEST_RUN_TEST_PREBUILD', ART_TEST_FULL)
 
 # Do you want run-tests with no prebuilding enabled run?
 ART_TEST_RUN_TEST_NO_PREBUILD = getEnvBoolean('ART_TEST_RUN_TEST_NO_PREBUILD', ART_TEST_FULL)
 
 # Do you want run-tests with a pregenerated core.art?
-ART_TEST_RUN_TEST_IMAGE = getEnvBoolean('ART_TEST_RUN_TEST_IMAGE', True)
+ART_TEST_RUN_TEST_IMAGE = getEnvBoolean('ART_TEST_RUN_TEST_IMAGE', ART_TEST_FULL)
 
 # Do you want run-tests without a pregenerated core.art?
 ART_TEST_RUN_TEST_NO_IMAGE = getEnvBoolean('ART_TEST_RUN_TEST_NO_IMAGE', ART_TEST_FULL)
@@ -148,7 +148,7 @@
 ART_TEST_RUN_TEST_NO_DEX2OAT = getEnvBoolean('ART_TEST_RUN_TEST_NO_DEX2OAT', ART_TEST_FULL)
 
 # Do you want run-tests with libartd.so?
-ART_TEST_RUN_TEST_DEBUG = getEnvBoolean('ART_TEST_RUN_TEST_DEBUG', True)
+ART_TEST_RUN_TEST_DEBUG = getEnvBoolean('ART_TEST_RUN_TEST_DEBUG', ART_TEST_FULL)
 
 # Do you want run-tests with libart.so?
 ART_TEST_RUN_TEST_NDEBUG = getEnvBoolean('ART_TEST_RUN_TEST_NDEBUG', ART_TEST_FULL)
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 81b7953..a5bfcff 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -95,12 +95,15 @@
 # The mutex object is used by the threads for exclusive access of test_count
 # to make any changes in its value.
 test_count_mutex = threading.Lock()
+
 # The set contains the list of all the possible run tests that are in art/test
 # directory.
 RUN_TEST_SET = set()
+
 # The semaphore object is used by the testrunner to limit the number of
 # threads to the user requested concurrency value.
 semaphore = threading.Semaphore(1)
+
 # The mutex object is used to provide exclusive access to a thread to print
 # its output.
 print_mutex = threading.Lock()
@@ -112,7 +115,6 @@
 test_count = 0
 total_test_count = 0
 verbose = False
-last_print_length = 0
 dry_run = False
 build = False
 gdb = False
@@ -166,12 +168,12 @@
     TARGET_TYPES.add('host')
     TARGET_TYPES.add('target')
 
-  if env.ART_TEST_RUN_TEST_PREBUILD:
-    PREBUILD_TYPES.add('prebuild')
   if env.ART_TEST_RUN_TEST_NO_PREBUILD:
     PREBUILD_TYPES.add('no-prebuild')
   if env.ART_TEST_RUN_TEST_NO_DEX2OAT:
     PREBUILD_TYPES.add('no-dex2oat')
+  if env.ART_TEST_RUN_TEST_PREBUILD or not PREBUILD_TYPES: # Default
+    PREBUILD_TYPES.add('prebuild')
 
   if env.ART_TEST_INTERPRETER_ACCESS_CHECKS:
     COMPILER_TYPES.add('interp-ac')
@@ -179,42 +181,39 @@
     COMPILER_TYPES.add('interpreter')
   if env.ART_TEST_JIT:
     COMPILER_TYPES.add('jit')
-
-  if env.ART_TEST_OPTIMIZING:
-    COMPILER_TYPES.add('optimizing')
-    OPTIMIZING_COMPILER_TYPES.add('optimizing')
   if env.ART_TEST_OPTIMIZING_GRAPH_COLOR:
     COMPILER_TYPES.add('regalloc_gc')
     OPTIMIZING_COMPILER_TYPES.add('regalloc_gc')
+  if env.ART_TEST_OPTIMIZING or not COMPILER_TYPES: # Default
+    COMPILER_TYPES.add('optimizing')
+    OPTIMIZING_COMPILER_TYPES.add('optimizing')
 
-  if not RELOCATE_TYPES:
-    RELOCATE_TYPES.add('no-relocate')
   if env.ART_TEST_RUN_TEST_RELOCATE:
     RELOCATE_TYPES.add('relocate')
   if env.ART_TEST_RUN_TEST_RELOCATE_NO_PATCHOAT:
     RELOCATE_TYPES.add('relocate-npatchoat')
+  if not RELOCATE_TYPES: # Default
+    RELOCATE_TYPES.add('no-relocate')
 
-  if not TRACE_TYPES:
-    TRACE_TYPES.add('ntrace')
   if env.ART_TEST_TRACE:
     TRACE_TYPES.add('trace')
   if env.ART_TEST_TRACE_STREAM:
     TRACE_TYPES.add('stream')
+  if not TRACE_TYPES: # Default
+    TRACE_TYPES.add('ntrace')
 
-  if not GC_TYPES:
-    GC_TYPES.add('cms')
   if env.ART_TEST_GC_STRESS:
     GC_TYPES.add('gcstress')
   if env.ART_TEST_GC_VERIFY:
     GC_TYPES.add('gcverify')
+  if not GC_TYPES: # Default
+    GC_TYPES.add('cms')
 
-  if not JNI_TYPES:
-    JNI_TYPES.add('checkjni')
   if env.ART_TEST_JNI_FORCECOPY:
     JNI_TYPES.add('forcecopy')
+  if not JNI_TYPES: # Default
+    JNI_TYPES.add('checkjni')
 
-  if env.ART_TEST_RUN_TEST_IMAGE:
-    IMAGE_TYPES.add('picimage')
   if env.ART_TEST_RUN_TEST_NO_IMAGE:
     IMAGE_TYPES.add('no-image')
   if env.ART_TEST_RUN_TEST_MULTI_IMAGE:
@@ -223,22 +222,23 @@
     IMAGE_TYPES.add('npicimage')
   if env.ART_TEST_RUN_TEST_MULTI_IMAGE:
     IMAGE_TYPES.add('multinpicimage')
+  if env.ART_TEST_RUN_TEST_IMAGE or not IMAGE_TYPES: # Default
+    IMAGE_TYPES.add('picimage')
 
-  if not PICTEST_TYPES:
-    PICTEST_TYPES.add('npictest')
   if env.ART_TEST_PIC_TEST:
     PICTEST_TYPES.add('pictest')
+  if not PICTEST_TYPES: # Default
+    PICTEST_TYPES.add('npictest')
 
-  if env.ART_TEST_RUN_TEST_DEBUG:
-    RUN_TYPES.add('debug')
   if env.ART_TEST_RUN_TEST_NDEBUG:
     RUN_TYPES.add('ndebug')
-
-  if not DEBUGGABLE_TYPES:
-    DEBUGGABLE_TYPES.add('ndebuggable')
+  if env.ART_TEST_RUN_TEST_DEBUG or not RUN_TYPES: # Default
+    RUN_TYPES.add('debug')
 
   if env.ART_TEST_RUN_TEST_DEBUGGABLE:
     DEBUGGABLE_TYPES.add('debuggable')
+  if not DEBUGGABLE_TYPES: # Default
+    DEBUGGABLE_TYPES.add('ndebuggable')
 
   if not ADDRESS_SIZES:
     ADDRESS_SIZES_TARGET['target'].add(env.ART_PHONY_TEST_TARGET_SUFFIX)
@@ -445,8 +445,6 @@
     test_variant: The set of variant for the test.
     test_name: The name of the test along with the variants.
   """
-  global last_print_length
-  global test_count
   global stop_testrunner
   if is_test_disabled(test, test_variant):
     test_skipped = True
@@ -456,42 +454,88 @@
     script_output = proc.stdout.read().strip()
     test_passed = not proc.wait()
 
-  # If verbose is set to True, every test information is printed on a new line.
-  # If not, the information is printed on the same line overriding the
-  # previous test output.
-  if not verbose:
-    suffix = '\r'
-    prefix = ' ' * last_print_length + '\r'
-  else:
-    suffix = '\n'
-    prefix = ''
-  test_count_mutex.acquire()
-  test_count += 1
-  percent = (test_count * 100) / total_test_count
-  out = '[ ' + str(percent) + '% ' + str(test_count) + '/' + str(total_test_count) + ' ] '
-  test_count_mutex.release()
-  out += test_name + ' '
   if not test_skipped:
     if test_passed:
-      out += COLOR_PASS + 'PASS' + COLOR_NORMAL
-      last_print_length = len(out)
+     print_test_info(test_name, 'PASS')
     else:
       failed_tests.append(test_name)
-      out += COLOR_ERROR + 'FAIL' + COLOR_NORMAL
-      out += '\n' + command + '\n' + script_output
       if not env.ART_TEST_KEEP_GOING:
         stop_testrunner = True
-      last_print_length = 0
+      print_test_info(test_name, 'FAIL', ('%s\n%s') % (
+        command, script_output))
   elif not dry_run:
-    out += COLOR_SKIP + 'SKIP' + COLOR_NORMAL
-    last_print_length = len(out)
+    print_test_info(test_name, 'SKIP')
     skipped_tests.append(test_name)
-  print_mutex.acquire()
-  print_text(prefix + out + suffix)
-  print_mutex.release()
+  else:
+    print_test_info(test_name, '')
   semaphore.release()
 
 
+def print_test_info(test_name, result, failed_test_info=""):
+  """Print the continous test information
+
+  If verbose is set to True, it continuously prints test status information
+  on a new line.
+  If verbose is set to False, it keeps on erasing test
+  information by overriding it with the latest test information. Also,
+  in this case it stictly makes sure that the information length doesn't
+  exceed the console width. It does so by shortening the test_name.
+
+  When a test fails, it prints the output of the run-test script and
+  command used to invoke the script. It doesn't override the failing
+  test information in either of the cases.
+  """
+  global test_count
+  info = ''
+  if not verbose:
+    # Without --verbose, the testrunner erases passing test info. It
+    # does that by overriding the printed text with white spaces all across
+    # the console width.
+    console_width = int(os.popen('stty size', 'r').read().split()[1])
+    info = '\r' + ' ' * console_width + '\r'
+  print_mutex.acquire()
+  test_count += 1
+  percent = (test_count * 100) / total_test_count
+  progress_info = ('[ %d%% %d/%d ]') % (
+    percent,
+    test_count,
+    total_test_count)
+
+  if result == "FAIL":
+    info += ('%s %s %s\n%s\n') % (
+      progress_info,
+      test_name,
+      COLOR_ERROR + 'FAIL' + COLOR_NORMAL,
+      failed_test_info)
+  else:
+    result_text = ''
+    if result == 'PASS':
+      result_text += COLOR_PASS + 'PASS' + COLOR_NORMAL
+    elif result == 'SKIP':
+      result_text += COLOR_SKIP + 'SKIP' + COLOR_NORMAL
+
+    if verbose:
+      info += ('%s %s %s\n') % (
+      progress_info,
+      test_name,
+      result_text)
+    else:
+      total_output_length = 2 # Two spaces
+      total_output_length += len(progress_info)
+      total_output_length += len(result)
+      allowed_test_length = console_width - total_output_length
+      test_name_len = len(test_name)
+      if allowed_test_length < test_name_len:
+        test_name = ('%s...%s') % (
+          test_name[:(allowed_test_length - 3)/2],
+          test_name[-(allowed_test_length - 3)/2:])
+      info += ('%s %s %s') % (
+        progress_info,
+        test_name,
+        result_text)
+  print_text(info)
+  print_mutex.release()
+
 def get_disabled_test_info():
   """Generate set of known failures.
 
@@ -588,7 +632,12 @@
 
 def print_analysis():
   if not verbose:
-    print_text(' ' * last_print_length + '\r')
+    # Without --verbose, the testrunner erases passing test info. It
+    # does that by overriding the printed text with white spaces all across
+    # the console width.
+    console_width = int(os.popen('stty size', 'r').read().split()[1])
+    eraser_text = '\r' + ' ' * console_width + '\r'
+    print_text(eraser_text)
   if skipped_tests:
     print_text(COLOR_SKIP + 'SKIPPED TESTS' + COLOR_NORMAL + '\n')
     for test in skipped_tests:
@@ -612,8 +661,12 @@
   variants required to run the test. Again, it returns the test_name
   without the variant information like 001-HelloWorld.
   """
-  if test_name in RUN_TEST_SET:
-    return {test_name}
+  test_set = set()
+  for test in RUN_TEST_SET:
+    if test.startswith(test_name):
+      test_set.add(test)
+  if test_set:
+    return test_set
 
   regex = '^test-art-'
   regex += '(' + '|'.join(VARIANT_TYPE_DICT['target']) + ')-'
@@ -645,6 +698,7 @@
     DEBUGGABLE_TYPES.add(match.group(11))
     ADDRESS_SIZES.add(match.group(13))
     return {match.group(12)}
+  raise ValueError(test_name + " is not a valid test")
 
 
 def parse_option():
@@ -788,10 +842,10 @@
     sys.exit(0)
   except SystemExit:
     pass
-  except:
+  except Exception, e:
     print_analysis()
+    print_text(str(e))
     sys.exit(1)
 
-
 if __name__ == '__main__':
   main()
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index c5a9356..351857d 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -122,6 +122,7 @@
   { "942-private-recursive", common_redefine::OnLoad, nullptr },
   { "943-private-recursive-jit", common_redefine::OnLoad, nullptr },
   { "944-transform-classloaders", common_redefine::OnLoad, nullptr },
+  { "945-obsolete-native", common_redefine::OnLoad, nullptr },
 };
 
 static AgentLib* FindAgent(char* name) {
diff --git a/tools/cpp-define-generator/presubmit-check-files-up-to-date b/tools/cpp-define-generator/presubmit-check-files-up-to-date
index 67a702a..0301a3e 100755
--- a/tools/cpp-define-generator/presubmit-check-files-up-to-date
+++ b/tools/cpp-define-generator/presubmit-check-files-up-to-date
@@ -22,7 +22,11 @@
 GEN_TOOL=cpp-define-generator-data
 
 if ! which "$GEN_TOOL"; then
-  echo "ERROR: Please build cpp-define-generator-data or source build/envsetup.sh" >&2
+  if [[ -z $ANDROID_BUILD_TOP ]]; then
+    echo "ERROR: Can't find '$GEN_TOOL' in \$PATH. Perhaps try 'source build/envsetup.sh' ?" >&2
+  else
+    echo "ERROR: Can't find '$GEN_TOOL' in \$PATH. Perhaps try 'make $GEN_TOOL' ?" >&2
+  fi
   exit 1
 fi
 
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 6e123ce..729a3e5 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -14,9 +14,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Exit as a stop-gap measure for b/35308152.
-exit 0
-
 if [ ! -d libcore ]; then
   echo "Script needs to be run at the root of the android tree"
   exit 1