Merge "ART: Improve class initializer and constructor verification."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 5bdfbc7..cea38e4 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -120,14 +120,14 @@
 ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \
   $(HOST_CORE_IMAGE_optimizing_pic_64) \
   $(HOST_CORE_IMAGE_optimizing_pic_32) \
-  $(HOST_CORE_IMAGE_optimizing_no-pic_64) \
-  $(HOST_CORE_IMAGE_optimizing_no-pic_32) \
+  $(HOST_CORE_IMAGE_interpreter_pic_64) \
+  $(HOST_CORE_IMAGE_interpreter_pic_32) \
   $(HOST_OUT_EXECUTABLES)/patchoatd
 ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \
   $(TARGET_CORE_IMAGE_optimizing_pic_64) \
   $(TARGET_CORE_IMAGE_optimizing_pic_32) \
-  $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \
-  $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \
+  $(TARGET_CORE_IMAGE_interpreter_pic_64) \
+  $(TARGET_CORE_IMAGE_interpreter_pic_32) \
   $(TARGET_OUT_EXECUTABLES)/patchoatd
 
 ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 5da59f3..faf8b41 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1060,13 +1060,13 @@
   virtual bool operator()(ObjPtr<mirror::Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
     const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
     for (auto& m : c->GetMethods(pointer_size)) {
-      ResolveExceptionsForMethod(&m, pointer_size);
+      ResolveExceptionsForMethod(&m);
     }
     return true;
   }
 
  private:
-  void ResolveExceptionsForMethod(ArtMethod* method_handle, PointerSize pointer_size)
+  void ResolveExceptionsForMethod(ArtMethod* method_handle)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     const DexFile::CodeItem* code_item = method_handle->GetCodeItem();
     if (code_item == nullptr) {
@@ -1088,8 +1088,7 @@
         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_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx,
-                                              pointer_size)) {
+        if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
           exceptions_to_resolve_.emplace(encoded_catch_handler_handlers_type_idx,
                                          method_handle->GetDexFile());
         }
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 86d92ff..c69ed31 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -487,7 +487,7 @@
   EXPECT_EQ(72U, sizeof(OatHeader));
   EXPECT_EQ(4U, sizeof(OatMethodOffsets));
   EXPECT_EQ(20U, sizeof(OatQuickMethodHeader));
-  EXPECT_EQ(164 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
+  EXPECT_EQ(159 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
             sizeof(QuickEntryPoints));
 }
 
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 05a76e1..ecabc58 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -431,10 +431,12 @@
       // TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to
       // kSaveEverything and use a temporary for the .bss entry address in the fast path,
       // so that we can avoid another calculation here.
+      UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
+      vixl32::Register temp = temps.Acquire();
       CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
           arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
-      arm_codegen->EmitMovwMovtPlaceholder(labels, ip);
-      __ Str(OutputRegister(cls_), MemOperand(ip));
+      arm_codegen->EmitMovwMovtPlaceholder(labels, temp);
+      __ Str(OutputRegister(cls_), MemOperand(temp));
     }
     __ B(GetExitLabel());
   }
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 50aa442..78a4251 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1538,8 +1538,6 @@
     }
   }
 
-  PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-
   // Iterate over the list of parameter types and test whether any of the
   // actual inputs has a more specific reference type than the type declared in
   // the signature.
@@ -1551,9 +1549,9 @@
        ++param_idx, ++input_idx) {
     HInstruction* input = invoke_instruction->InputAt(input_idx);
     if (input->GetType() == Primitive::kPrimNot) {
-      mirror::Class* param_cls = resolved_method->GetDexCacheResolvedType(
+      mirror::Class* param_cls = resolved_method->GetClassFromTypeIndex(
           param_list->GetTypeItem(param_idx).type_idx_,
-          pointer_size);
+          /* resolve */ false);
       if (IsReferenceTypeRefinement(GetClassRTI(param_cls),
                                     /* declared_can_be_null */ true,
                                     input)) {
@@ -1602,8 +1600,7 @@
         // TODO: we could be more precise by merging the phi inputs but that requires
         // some functionality from the reference type propagation.
         DCHECK(return_replacement->IsPhi());
-        PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-        mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */, pointer_size);
+        mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */);
         return_replacement->SetReferenceTypeInfo(GetClassRTI(cls));
       }
     }
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index a4d59ab..8854a2b 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -840,10 +840,8 @@
   }
 
   ScopedObjectAccess soa(Thread::Current());
-  ClassLinker* cl = Runtime::Current()->GetClassLinker();
-  PointerSize pointer_size = cl->GetImagePointerSize();
   ArtMethod* method = instr->GetResolvedMethod();
-  mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false, pointer_size);
+  mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(/* resolve */ false);
   SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
 }
 
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 5e55210..a3fce02 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -5610,7 +5610,7 @@
   " 214:	ecbd 8a10 	vpop	{s16-s31}\n",
   " 218:	e8bd 8de0 	ldmia.w	sp!, {r5, r6, r7, r8, sl, fp, pc}\n",
   " 21c:	4660      	mov	r0, ip\n",
-  " 21e:	f8d9 c2c0 	ldr.w	ip, [r9, #704]	; 0x2c0\n",
+  " 21e:	f8d9 c2ac 	ldr.w	ip, [r9, #684]	; 0x2ac\n",
   " 222:	47e0      	blx	ip\n",
   nullptr
 };
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 21b03eb..3fbdb89 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1095,6 +1095,8 @@
         compiler_options_->GetNativeDebuggable() ? OatHeader::kTrueValue : OatHeader::kFalseValue);
     key_value_store_->Put(OatHeader::kCompilerFilter,
         CompilerFilter::NameOfFilter(compiler_options_->GetCompilerFilter()));
+    key_value_store_->Put(OatHeader::kConcurrentCopying,
+                          kUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue);
   }
 
   // Parse the arguments from the command line. In case of an unrecognized option or impossible
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 7ae13a5..a4b414d 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -54,48 +54,6 @@
 
 namespace art {
 
-static bool LocationToFilename(const std::string& location, InstructionSet isa,
-                               std::string* filename) {
-  bool has_system = false;
-  bool has_cache = false;
-  // image_location = /system/framework/boot.art
-  // system_image_filename = /system/framework/<image_isa>/boot.art
-  std::string system_filename(GetSystemImageFilename(location.c_str(), isa));
-  if (OS::FileExists(system_filename.c_str())) {
-    has_system = true;
-  }
-
-  bool have_android_data = false;
-  bool dalvik_cache_exists = false;
-  bool is_global_cache = false;
-  std::string dalvik_cache;
-  GetDalvikCache(GetInstructionSetString(isa), false, &dalvik_cache,
-                 &have_android_data, &dalvik_cache_exists, &is_global_cache);
-
-  std::string cache_filename;
-  if (have_android_data && dalvik_cache_exists) {
-    // Always set output location even if it does not exist,
-    // so that the caller knows where to create the image.
-    //
-    // image_location = /system/framework/boot.art
-    // *image_filename = /data/dalvik-cache/<image_isa>/boot.art
-    std::string error_msg;
-    if (GetDalvikCacheFilename(location.c_str(), dalvik_cache.c_str(),
-                               &cache_filename, &error_msg)) {
-      has_cache = true;
-    }
-  }
-  if (has_system) {
-    *filename = system_filename;
-    return true;
-  } else if (has_cache) {
-    *filename = cache_filename;
-    return true;
-  } else {
-    return false;
-  }
-}
-
 static const OatHeader* GetOatHeader(const ElfFile* elf_file) {
   uint64_t off = 0;
   if (!elf_file->GetSectionOffsetAndSize(".rodata", &off, nullptr)) {
@@ -106,28 +64,10 @@
   return oat_header;
 }
 
-// This function takes an elf file and reads the current patch delta value
-// encoded in its oat header value
-static bool ReadOatPatchDelta(const ElfFile* elf_file, off_t* delta, std::string* error_msg) {
-  const OatHeader* oat_header = GetOatHeader(elf_file);
-  if (oat_header == nullptr) {
-    *error_msg = "Unable to get oat header from elf file.";
-    return false;
-  }
-  if (!oat_header->IsValid()) {
-    *error_msg = "Elf file has an invalid oat header";
-    return false;
-  }
-  *delta = oat_header->GetImagePatchDelta();
-  return true;
-}
-
-static File* CreateOrOpen(const char* name, bool* created) {
+static File* CreateOrOpen(const char* name) {
   if (OS::FileExists(name)) {
-    *created = false;
     return OS::OpenFileReadWrite(name);
   } else {
-    *created = true;
     std::unique_ptr<File> f(OS::CreateEmptyFile(name));
     if (f.get() != nullptr) {
       if (fchmod(f->Fd(), 0644) != 0) {
@@ -206,12 +146,11 @@
   Thread::Current()->TransitionFromRunnableToSuspended(kNative);
   ScopedObjectAccess soa(Thread::Current());
 
-  t.NewTiming("Image and oat Patching setup");
+  t.NewTiming("Image Patching setup");
   std::vector<gc::space::ImageSpace*> spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces();
   std::map<gc::space::ImageSpace*, std::unique_ptr<File>> space_to_file_map;
   std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>> space_to_memmap_map;
   std::map<gc::space::ImageSpace*, PatchOat> space_to_patchoat_map;
-  std::map<gc::space::ImageSpace*, bool> space_to_skip_patching_map;
 
   for (size_t i = 0; i < spaces.size(); ++i) {
     gc::space::ImageSpace* space = spaces[i];
@@ -255,8 +194,7 @@
     space_to_memmap_map.emplace(space, std::move(image));
   }
 
-  // Do a first pass over the image spaces. Symlink PIC oat and vdex files, and
-  // prepare PatchOat instances for the rest.
+  // Symlink PIC oat and vdex files and patch the image spaces in memory.
   for (size_t i = 0; i < spaces.size(); ++i) {
     gc::space::ImageSpace* space = spaces[i];
     std::string input_image_filename = space->GetImageFilename();
@@ -277,14 +215,17 @@
       return false;
     }
 
-    bool skip_patching_oat = false;
     MaybePic is_oat_pic = IsOatPic(elf.get());
     if (is_oat_pic >= ERROR_FIRST) {
       // Error logged by IsOatPic
       return false;
-    } else if (is_oat_pic == PIC) {
-      // Do not need to do ELF-file patching. Create a symlink and skip the ELF patching.
+    } else if (is_oat_pic == NOT_PIC) {
+      LOG(ERROR) << "patchoat cannot be used on non-PIC oat file: " << input_oat_file->GetPath();
+      return false;
+    } else {
+      CHECK(is_oat_pic == PIC);
 
+      // Create a symlink.
       std::string converted_image_filename = space->GetImageLocation();
       std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@');
       std::string output_image_filename = output_directory +
@@ -296,23 +237,16 @@
           ImageHeader::GetOatLocationFromImageLocation(output_image_filename);
 
       if (!ReplaceOatFileWithSymlink(input_oat_file->GetPath(),
-                                     output_oat_filename,
-                                     false,
-                                     true) ||
+                                     output_oat_filename) ||
           !SymlinkFile(input_vdex_filename, output_vdex_filename)) {
         // Errors already logged by above call.
         return false;
       }
-      // Don't patch the OAT, since we just symlinked it. Image still needs patching.
-      skip_patching_oat = true;
-    } else {
-      CHECK(is_oat_pic == NOT_PIC);
     }
 
     PatchOat& p = space_to_patchoat_map.emplace(space,
                                                 PatchOat(
                                                     isa,
-                                                    elf.release(),
                                                     space_to_memmap_map.find(space)->second.get(),
                                                     space->GetLiveBitmap(),
                                                     space->GetMemMap(),
@@ -320,36 +254,24 @@
                                                     &space_to_memmap_map,
                                                     timings)).first->second;
 
-    t.NewTiming("Patching files");
-    if (!skip_patching_oat && !p.PatchElf()) {
-      LOG(ERROR) << "Failed to patch oat file " << input_oat_file->GetPath();
-      return false;
-    }
+    t.NewTiming("Patching image");
     if (!p.PatchImage(i == 0)) {
       LOG(ERROR) << "Failed to patch image file " << input_image_filename;
       return false;
     }
-
-    space_to_skip_patching_map.emplace(space, skip_patching_oat);
   }
 
-  // Do a second pass over the image spaces. Patch image files, non-PIC oat files
-  // and symlink their corresponding vdex files.
+  // Write the patched image spaces.
   for (size_t i = 0; i < spaces.size(); ++i) {
     gc::space::ImageSpace* space = spaces[i];
-    std::string input_image_filename = space->GetImageFilename();
-    std::string input_vdex_filename =
-        ImageHeader::GetVdexLocationFromImageLocation(input_image_filename);
 
-    t.NewTiming("Writing files");
+    t.NewTiming("Writing image");
     std::string converted_image_filename = space->GetImageLocation();
     std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@');
     std::string output_image_filename = output_directory +
         (android::base::StartsWith(converted_image_filename, "/") ? "" : "/") +
         converted_image_filename;
-    bool new_oat_out;
-    std::unique_ptr<File>
-        output_image_file(CreateOrOpen(output_image_filename.c_str(), &new_oat_out));
+    std::unique_ptr<File> output_image_file(CreateOrOpen(output_image_filename.c_str()));
     if (output_image_file.get() == nullptr) {
       LOG(ERROR) << "Failed to open output image file at " << output_image_filename;
       return false;
@@ -362,48 +284,10 @@
     if (!success) {
       return false;
     }
-
-    bool skip_patching_oat = space_to_skip_patching_map.find(space)->second;
-    if (!skip_patching_oat) {
-      std::string output_vdex_filename =
-          ImageHeader::GetVdexLocationFromImageLocation(output_image_filename);
-      std::string output_oat_filename =
-          ImageHeader::GetOatLocationFromImageLocation(output_image_filename);
-
-      std::unique_ptr<File>
-          output_oat_file(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
-      if (output_oat_file.get() == nullptr) {
-        LOG(ERROR) << "Failed to open output oat file at " << output_oat_filename;
-        return false;
-      }
-      success = p.WriteElf(output_oat_file.get());
-      success = FinishFile(output_oat_file.get(), success);
-      if (success) {
-        success = SymlinkFile(input_vdex_filename, output_vdex_filename);
-      }
-      if (!success) {
-        return false;
-      }
-    }
   }
   return true;
 }
 
-bool PatchOat::WriteElf(File* out) {
-  TimingLogger::ScopedTiming t("Writing Elf File", timings_);
-
-  CHECK(oat_file_.get() != nullptr);
-  CHECK(out != nullptr);
-  size_t expect = oat_file_->Size();
-  if (out->WriteFully(reinterpret_cast<char*>(oat_file_->Begin()), expect) &&
-      out->SetLength(expect) == 0) {
-    return true;
-  } else {
-    LOG(ERROR) << "Writing to oat file " << out->GetPath() << " failed.";
-    return false;
-  }
-}
-
 bool PatchOat::WriteImage(File* out) {
   TimingLogger::ScopedTiming t("Writing image File", timings_);
   std::string error_msg;
@@ -466,22 +350,7 @@
 }
 
 bool PatchOat::ReplaceOatFileWithSymlink(const std::string& input_oat_filename,
-                                         const std::string& output_oat_filename,
-                                         bool output_oat_opened_from_fd,
-                                         bool new_oat_out) {
-  // Need a file when we are PIC, since we symlink over it. Refusing to symlink into FD.
-  if (output_oat_opened_from_fd) {
-    // TODO: installd uses --output-oat-fd. Should we change class linking logic for PIC?
-    LOG(ERROR) << "No output oat filename specified, needs filename for when we are PIC";
-    return false;
-  }
-
-  // Image was PIC. Create symlink where the oat is supposed to go.
-  if (!new_oat_out) {
-    LOG(ERROR) << "Oat file " << output_oat_filename << " already exists, refusing to overwrite";
-    return false;
-  }
-
+                                         const std::string& output_oat_filename) {
   // Delete the original file, since we won't need it.
   unlink(output_oat_filename.c_str());
 
@@ -799,133 +668,6 @@
       object->GetDataPtrSize(pointer_size)), pointer_size);
 }
 
-bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogger* timings,
-                     bool output_oat_opened_from_fd, bool new_oat_out) {
-  CHECK(input_oat != nullptr);
-  CHECK(output_oat != nullptr);
-  CHECK_GE(input_oat->Fd(), 0);
-  CHECK_GE(output_oat->Fd(), 0);
-  TimingLogger::ScopedTiming t("Setup Oat File Patching", timings);
-
-  std::string error_msg;
-  std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat,
-                                             PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg));
-  if (elf.get() == nullptr) {
-    LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
-    return false;
-  }
-
-  MaybePic is_oat_pic = IsOatPic(elf.get());
-  if (is_oat_pic >= ERROR_FIRST) {
-    // Error logged by IsOatPic
-    return false;
-  } else if (is_oat_pic == PIC) {
-    // Do not need to do ELF-file patching. Create a symlink and skip the rest.
-    // Any errors will be logged by the function call.
-    return ReplaceOatFileWithSymlink(input_oat->GetPath(),
-                                     output_oat->GetPath(),
-                                     output_oat_opened_from_fd,
-                                     new_oat_out);
-  } else {
-    CHECK(is_oat_pic == NOT_PIC);
-  }
-
-  PatchOat p(elf.release(), delta, timings);
-  t.NewTiming("Patch Oat file");
-  if (!p.PatchElf()) {
-    return false;
-  }
-
-  t.NewTiming("Writing oat file");
-  if (!p.WriteElf(output_oat)) {
-    return false;
-  }
-  return true;
-}
-
-template <typename ElfFileImpl>
-bool PatchOat::PatchOatHeader(ElfFileImpl* oat_file) {
-  auto rodata_sec = oat_file->FindSectionByName(".rodata");
-  if (rodata_sec == nullptr) {
-    return false;
-  }
-  OatHeader* oat_header = reinterpret_cast<OatHeader*>(oat_file->Begin() + rodata_sec->sh_offset);
-  if (!oat_header->IsValid()) {
-    LOG(ERROR) << "Elf file " << oat_file->GetFilePath() << " has an invalid oat header";
-    return false;
-  }
-  oat_header->RelocateOat(delta_);
-  return true;
-}
-
-bool PatchOat::PatchElf() {
-  if (oat_file_->Is64Bit()) {
-    return PatchElf<ElfFileImpl64>(oat_file_->GetImpl64());
-  } else {
-    return PatchElf<ElfFileImpl32>(oat_file_->GetImpl32());
-  }
-}
-
-template <typename ElfFileImpl>
-bool PatchOat::PatchElf(ElfFileImpl* oat_file) {
-  TimingLogger::ScopedTiming t("Fixup Elf Text Section", timings_);
-
-  // Fix up absolute references to locations within the boot image.
-  if (!oat_file->ApplyOatPatchesTo(".text", delta_)) {
-    return false;
-  }
-
-  // Update the OatHeader fields referencing the boot image.
-  if (!PatchOatHeader<ElfFileImpl>(oat_file)) {
-    return false;
-  }
-
-  bool need_boot_oat_fixup = true;
-  for (unsigned int i = 0; i < oat_file->GetProgramHeaderNum(); ++i) {
-    auto hdr = oat_file->GetProgramHeader(i);
-    if (hdr->p_type == PT_LOAD && hdr->p_vaddr == 0u) {
-      need_boot_oat_fixup = false;
-      break;
-    }
-  }
-  if (!need_boot_oat_fixup) {
-    // This is an app oat file that can be loaded at an arbitrary address in memory.
-    // Boot image references were patched above and there's nothing else to do.
-    return true;
-  }
-
-  // This is a boot oat file that's loaded at a particular address and we need
-  // to patch all absolute addresses, starting with ELF program headers.
-
-  t.NewTiming("Fixup Elf Headers");
-  // Fixup Phdr's
-  oat_file->FixupProgramHeaders(delta_);
-
-  t.NewTiming("Fixup Section Headers");
-  // Fixup Shdr's
-  oat_file->FixupSectionHeaders(delta_);
-
-  t.NewTiming("Fixup Dynamics");
-  oat_file->FixupDynamic(delta_);
-
-  t.NewTiming("Fixup Elf Symbols");
-  // Fixup dynsym
-  if (!oat_file->FixupSymbols(delta_, true)) {
-    return false;
-  }
-  // Fixup symtab
-  if (!oat_file->FixupSymbols(delta_, false)) {
-    return false;
-  }
-
-  t.NewTiming("Fixup Debug Sections");
-  if (!oat_file->FixupDebugSections(delta_)) {
-    return false;
-  }
-
-  return true;
-}
-
 static int orig_argc;
 static char** orig_argv;
 
@@ -960,32 +702,10 @@
   UsageError("Usage: patchoat [options]...");
   UsageError("");
   UsageError("  --instruction-set=<isa>: Specifies the instruction set the patched code is");
-  UsageError("      compiled for. Required if you use --input-oat-location");
-  UsageError("");
-  UsageError("  --input-oat-file=<file.oat>: Specifies the exact filename of the oat file to be");
-  UsageError("      patched.");
-  UsageError("");
-  UsageError("  --input-oat-fd=<file-descriptor>: Specifies the file-descriptor of the oat file");
-  UsageError("      to be patched.");
-  UsageError("");
-  UsageError("  --input-vdex-fd=<file-descriptor>: Specifies the file-descriptor of the vdex file");
-  UsageError("      associated with the oat file.");
-  UsageError("");
-  UsageError("  --input-oat-location=<file.oat>: Specifies the 'location' to read the patched");
-  UsageError("      oat file from. If used one must also supply the --instruction-set");
+  UsageError("      compiled for (required).");
   UsageError("");
   UsageError("  --input-image-location=<file.art>: Specifies the 'location' of the image file to");
-  UsageError("      be patched. If --instruction-set is not given it will use the instruction set");
-  UsageError("      extracted from the --input-oat-file.");
-  UsageError("");
-  UsageError("  --output-oat-file=<file.oat>: Specifies the exact file to write the patched oat");
-  UsageError("      file to.");
-  UsageError("");
-  UsageError("  --output-oat-fd=<file-descriptor>: Specifies the file-descriptor to write the");
-  UsageError("      patched oat file to.");
-  UsageError("");
-  UsageError("  --output-vdex-fd=<file-descriptor>: Specifies the file-descriptor to copy the");
-  UsageError("      the vdex file associated with the patch oat file to.");
+  UsageError("      be patched.");
   UsageError("");
   UsageError("  --output-image-file=<file.art>: Specifies the exact file to write the patched");
   UsageError("      image file to.");
@@ -993,15 +713,6 @@
   UsageError("  --base-offset-delta=<delta>: Specify the amount to change the old base-offset by.");
   UsageError("      This value may be negative.");
   UsageError("");
-  UsageError("  --patched-image-location=<file.art>: Relocate the oat file to be the same as the");
-  UsageError("      image at the given location. If used one must also specify the");
-  UsageError("      --instruction-set flag. It will search for this image in the same way that");
-  UsageError("      is done when loading one.");
-  UsageError("");
-  UsageError("  --lock-output: Obtain a flock on output oat file before starting.");
-  UsageError("");
-  UsageError("  --no-lock-output: Do not attempt to obtain a flock on output oat file.");
-  UsageError("");
   UsageError("  --dump-timings: dump out patch timing information");
   UsageError("");
   UsageError("  --no-dump-timings: do not dump out patch timing information");
@@ -1010,34 +721,6 @@
   exit(EXIT_FAILURE);
 }
 
-static bool ReadBaseDelta(const char* name, off_t* delta, std::string* error_msg) {
-  CHECK(name != nullptr);
-  CHECK(delta != nullptr);
-  std::unique_ptr<File> file;
-  if (OS::FileExists(name)) {
-    file.reset(OS::OpenFileForReading(name));
-    if (file.get() == nullptr) {
-      *error_msg = "Failed to open file %s for reading";
-      return false;
-    }
-  } else {
-    *error_msg = "File %s does not exist";
-    return false;
-  }
-  CHECK(file.get() != nullptr);
-  ImageHeader hdr;
-  if (sizeof(hdr) != file->Read(reinterpret_cast<char*>(&hdr), sizeof(hdr), 0)) {
-    *error_msg = "Failed to read file %s";
-    return false;
-  }
-  if (!hdr.IsValid()) {
-    *error_msg = "%s does not contain a valid image header.";
-    return false;
-  }
-  *delta = hdr.GetPatchDelta();
-  return true;
-}
-
 static int patchoat_image(TimingLogger& timings,
                           InstructionSet isa,
                           const std::string& input_image_location,
@@ -1076,293 +759,6 @@
   return ret ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
-static int patchoat_oat(TimingLogger& timings,
-                        InstructionSet isa,
-                        const std::string& patched_image_location,
-                        off_t base_delta,
-                        bool base_delta_set,
-                        int input_oat_fd,
-                        int input_vdex_fd,
-                        const std::string& input_oat_location,
-                        std::string input_oat_filename,
-                        bool have_input_oat,
-                        int output_oat_fd,
-                        int output_vdex_fd,
-                        std::string output_oat_filename,
-                        bool have_output_oat,
-                        bool lock_output,
-                        bool debug) {
-  {
-    // Only 1 of these may be set.
-    uint32_t cnt = 0;
-    cnt += (base_delta_set) ? 1 : 0;
-    cnt += (!patched_image_location.empty()) ? 1 : 0;
-    if (cnt > 1) {
-      Usage("Only one of --base-offset-delta or --patched-image-location may be used.");
-    } else if (cnt == 0) {
-      Usage("Must specify --base-offset-delta or --patched-image-location.");
-    }
-  }
-
-  if (!have_input_oat || !have_output_oat) {
-    Usage("Both input and output oat must be supplied to patch an app odex.");
-  }
-
-  if (!input_oat_location.empty()) {
-    if (!LocationToFilename(input_oat_location, isa, &input_oat_filename)) {
-      Usage("Unable to find filename for input oat location %s", input_oat_location.c_str());
-    }
-    if (debug) {
-      LOG(INFO) << "Using input-oat-file " << input_oat_filename;
-    }
-  }
-
-  if ((input_oat_fd == -1) != (input_vdex_fd == -1)) {
-    Usage("Either both input oat and vdex have to be passed as file descriptors or none of them");
-  } else if ((output_oat_fd == -1) != (output_vdex_fd == -1)) {
-    Usage("Either both output oat and vdex have to be passed as file descriptors or none of them");
-  }
-
-  bool match_delta = false;
-  if (!patched_image_location.empty()) {
-    std::string system_filename;
-    bool has_system = false;
-    std::string cache_filename;
-    bool has_cache = false;
-    bool has_android_data_unused = false;
-    bool is_global_cache = false;
-    if (!gc::space::ImageSpace::FindImageFilename(patched_image_location.c_str(), isa,
-                                                  &system_filename, &has_system, &cache_filename,
-                                                  &has_android_data_unused, &has_cache,
-                                                  &is_global_cache)) {
-      Usage("Unable to determine image file for location %s", patched_image_location.c_str());
-    }
-    std::string patched_image_filename;
-    if (has_cache) {
-      patched_image_filename = cache_filename;
-    } else if (has_system) {
-      LOG(WARNING) << "Only image file found was in /system for image location "
-          << patched_image_location;
-      patched_image_filename = system_filename;
-    } else {
-      Usage("Unable to determine image file for location %s", patched_image_location.c_str());
-    }
-    if (debug) {
-      LOG(INFO) << "Using patched-image-file " << patched_image_filename;
-    }
-
-    base_delta_set = true;
-    match_delta = true;
-    std::string error_msg;
-    if (!ReadBaseDelta(patched_image_filename.c_str(), &base_delta, &error_msg)) {
-      Usage(error_msg.c_str(), patched_image_filename.c_str());
-    }
-  }
-
-  if (!IsAligned<kPageSize>(base_delta)) {
-    Usage("Base offset/delta must be alligned to a pagesize (0x%08x) boundary.", kPageSize);
-  }
-
-  // We can symlink VDEX only if we have both input and output specified as filenames.
-  // Store that piece of information before we possibly create bogus filenames for
-  // files passed as file descriptors.
-  bool symlink_vdex = !input_oat_filename.empty() && !output_oat_filename.empty();
-
-  // Infer names of VDEX files.
-  std::string input_vdex_filename;
-  std::string output_vdex_filename;
-  if (!input_oat_filename.empty()) {
-    input_vdex_filename = ReplaceFileExtension(input_oat_filename, "vdex");
-  }
-  if (!output_oat_filename.empty()) {
-    output_vdex_filename = ReplaceFileExtension(output_oat_filename, "vdex");
-  }
-
-  // Do we need to cleanup output files if we fail?
-  bool new_oat_out = false;
-  bool new_vdex_out = false;
-
-  std::unique_ptr<File> input_oat;
-  std::unique_ptr<File> output_oat;
-
-  if (input_oat_fd != -1) {
-    if (input_oat_filename.empty()) {
-      input_oat_filename = "input-oat-file";
-    }
-    input_oat.reset(new File(input_oat_fd, input_oat_filename, false));
-    if (input_oat_fd == output_oat_fd) {
-      input_oat.get()->DisableAutoClose();
-    }
-    if (input_oat == nullptr) {
-      // Unlikely, but ensure exhaustive logging in non-0 exit code case
-      LOG(ERROR) << "Failed to open input oat file by its FD" << input_oat_fd;
-      return EXIT_FAILURE;
-    }
-  } else {
-    CHECK(!input_oat_filename.empty());
-    input_oat.reset(OS::OpenFileForReading(input_oat_filename.c_str()));
-    if (input_oat == nullptr) {
-      int err = errno;
-      LOG(ERROR) << "Failed to open input oat file " << input_oat_filename
-          << ": " << strerror(err) << "(" << err << ")";
-      return EXIT_FAILURE;
-    }
-  }
-
-  std::string error_msg;
-  std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat.get(), PROT_READ, MAP_PRIVATE, &error_msg));
-  if (elf.get() == nullptr) {
-    LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
-    return EXIT_FAILURE;
-  }
-  if (!elf->HasSection(".text.oat_patches")) {
-    LOG(ERROR) << "missing oat patch section in input oat file " << input_oat->GetPath();
-    return EXIT_FAILURE;
-  }
-
-  if (output_oat_fd != -1) {
-    if (output_oat_filename.empty()) {
-      output_oat_filename = "output-oat-file";
-    }
-    output_oat.reset(new File(output_oat_fd, output_oat_filename, true));
-    if (output_oat == nullptr) {
-      // Unlikely, but ensure exhaustive logging in non-0 exit code case
-      LOG(ERROR) << "Failed to open output oat file by its FD" << output_oat_fd;
-    }
-  } else {
-    CHECK(!output_oat_filename.empty());
-    output_oat.reset(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
-    if (output_oat == nullptr) {
-      int err = errno;
-      LOG(ERROR) << "Failed to open output oat file " << output_oat_filename
-          << ": " << strerror(err) << "(" << err << ")";
-    }
-  }
-
-  // Open VDEX files if we are not symlinking them.
-  std::unique_ptr<File> input_vdex;
-  std::unique_ptr<File> output_vdex;
-  if (symlink_vdex) {
-    new_vdex_out = !OS::FileExists(output_vdex_filename.c_str());
-  } else {
-    if (input_vdex_fd != -1) {
-      input_vdex.reset(new File(input_vdex_fd, input_vdex_filename, true));
-      if (input_vdex == nullptr) {
-        // Unlikely, but ensure exhaustive logging in non-0 exit code case
-        LOG(ERROR) << "Failed to open input vdex file by its FD" << input_vdex_fd;
-      }
-    } else {
-      input_vdex.reset(OS::OpenFileForReading(input_vdex_filename.c_str()));
-      if (input_vdex == nullptr) {
-        PLOG(ERROR) << "Failed to open input vdex file " << input_vdex_filename;
-        return EXIT_FAILURE;
-      }
-    }
-    if (output_vdex_fd != -1) {
-      output_vdex.reset(new File(output_vdex_fd, output_vdex_filename, true));
-      if (output_vdex == nullptr) {
-        // Unlikely, but ensure exhaustive logging in non-0 exit code case
-        LOG(ERROR) << "Failed to open output vdex file by its FD" << output_vdex_fd;
-      }
-    } else {
-      output_vdex.reset(CreateOrOpen(output_vdex_filename.c_str(), &new_vdex_out));
-      if (output_vdex == nullptr) {
-        PLOG(ERROR) << "Failed to open output vdex file " << output_vdex_filename;
-        return EXIT_FAILURE;
-      }
-    }
-  }
-
-  // TODO: get rid of this.
-  auto cleanup = [&output_oat_filename, &output_vdex_filename, &new_oat_out, &new_vdex_out]
-                 (bool success) {
-    if (!success) {
-      if (new_oat_out) {
-        CHECK(!output_oat_filename.empty());
-        unlink(output_oat_filename.c_str());
-      }
-      if (new_vdex_out) {
-        CHECK(!output_vdex_filename.empty());
-        unlink(output_vdex_filename.c_str());
-      }
-    }
-
-    if (kIsDebugBuild) {
-      LOG(INFO) << "Cleaning up.. success? " << success;
-    }
-  };
-
-  if (output_oat.get() == nullptr) {
-    cleanup(false);
-    return EXIT_FAILURE;
-  }
-
-  if (match_delta) {
-    // Figure out what the current delta is so we can match it to the desired delta.
-    off_t current_delta = 0;
-    if (!ReadOatPatchDelta(elf.get(), &current_delta, &error_msg)) {
-      LOG(ERROR) << "Unable to get current delta: " << error_msg;
-      cleanup(false);
-      return EXIT_FAILURE;
-    }
-    // Before this line base_delta is the desired final delta. We need it to be the actual amount to
-    // change everything by. We subtract the current delta from it to make it this.
-    base_delta -= current_delta;
-    if (!IsAligned<kPageSize>(base_delta)) {
-      LOG(ERROR) << "Given image file was relocated by an illegal delta";
-      cleanup(false);
-      return false;
-    }
-  }
-
-  if (debug) {
-    LOG(INFO) << "moving offset by " << base_delta
-        << " (0x" << std::hex << base_delta << ") bytes or "
-        << std::dec << (base_delta/kPageSize) << " pages.";
-  }
-
-  ScopedFlock output_oat_lock;
-  if (lock_output) {
-    if (!output_oat_lock.Init(output_oat.get(), &error_msg)) {
-      LOG(ERROR) << "Unable to lock output oat " << output_oat->GetPath() << ": " << error_msg;
-      cleanup(false);
-      return EXIT_FAILURE;
-    }
-  }
-
-  TimingLogger::ScopedTiming pt("patch oat", &timings);
-  bool ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings,
-                             output_oat_fd >= 0,  // was it opened from FD?
-                             new_oat_out);
-  ret = FinishFile(output_oat.get(), ret);
-
-  if (ret) {
-    if (symlink_vdex) {
-      ret = SymlinkFile(input_vdex_filename, output_vdex_filename);
-    } else {
-      ret = unix_file::CopyFile(*input_vdex.get(), output_vdex.get());
-    }
-  }
-
-  if (kIsDebugBuild) {
-    LOG(INFO) << "Exiting with return ... " << ret;
-  }
-  cleanup(ret);
-  return ret ? EXIT_SUCCESS : EXIT_FAILURE;
-}
-
-static int ParseFd(const StringPiece& option, const char* cmdline_arg) {
-  int fd;
-  const char* fd_str = option.substr(strlen(cmdline_arg)).data();
-  if (!ParseInt(fd_str, &fd)) {
-    Usage("Failed to parse %d argument '%s' as an integer", cmdline_arg, fd_str);
-  }
-  if (fd < 0) {
-    Usage("%s pass a negative value %d", cmdline_arg, fd);
-  }
-  return fd;
-}
-
 static int patchoat(int argc, char **argv) {
   InitLogging(argv, Runtime::Aborter);
   MemMap::Init();
@@ -1384,23 +780,11 @@
   // cmd line args
   bool isa_set = false;
   InstructionSet isa = kNone;
-  std::string input_oat_filename;
-  std::string input_oat_location;
-  int input_oat_fd = -1;
-  int input_vdex_fd = -1;
-  bool have_input_oat = false;
   std::string input_image_location;
-  std::string output_oat_filename;
-  int output_oat_fd = -1;
-  int output_vdex_fd = -1;
-  bool have_output_oat = false;
   std::string output_image_filename;
   off_t base_delta = 0;
   bool base_delta_set = false;
-  std::string patched_image_filename;
-  std::string patched_image_location;
   bool dump_timings = kIsDebugBuild;
-  bool lock_output = true;
 
   for (int i = 0; i < argc; ++i) {
     const StringPiece option(argv[i]);
@@ -1415,42 +799,8 @@
       if (isa == kNone) {
         Usage("Unknown or invalid instruction set %s", isa_str);
       }
-    } else if (option.starts_with("--input-oat-location=")) {
-      if (have_input_oat) {
-        Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used.");
-      }
-      have_input_oat = true;
-      input_oat_location = option.substr(strlen("--input-oat-location=")).data();
-    } else if (option.starts_with("--input-oat-file=")) {
-      if (have_input_oat) {
-        Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used.");
-      }
-      have_input_oat = true;
-      input_oat_filename = option.substr(strlen("--input-oat-file=")).data();
-    } else if (option.starts_with("--input-oat-fd=")) {
-      if (have_input_oat) {
-        Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used.");
-      }
-      have_input_oat = true;
-      input_oat_fd = ParseFd(option, "--input-oat-fd=");
-    } else if (option.starts_with("--input-vdex-fd=")) {
-      input_vdex_fd = ParseFd(option, "--input-vdex-fd=");
     } else if (option.starts_with("--input-image-location=")) {
       input_image_location = option.substr(strlen("--input-image-location=")).data();
-    } else if (option.starts_with("--output-oat-file=")) {
-      if (have_output_oat) {
-        Usage("Only one of --output-oat-file, and --output-oat-fd may be used.");
-      }
-      have_output_oat = true;
-      output_oat_filename = option.substr(strlen("--output-oat-file=")).data();
-    } else if (option.starts_with("--output-oat-fd=")) {
-      if (have_output_oat) {
-        Usage("Only one of --output-oat-file, --output-oat-fd may be used.");
-      }
-      have_output_oat = true;
-      output_oat_fd = ParseFd(option, "--output-oat-fd=");
-    } else if (option.starts_with("--output-vdex-fd=")) {
-      output_vdex_fd = ParseFd(option, "--output-vdex-fd=");
     } else if (option.starts_with("--output-image-file=")) {
       output_image_filename = option.substr(strlen("--output-image-file=")).data();
     } else if (option.starts_with("--base-offset-delta=")) {
@@ -1459,12 +809,6 @@
       if (!ParseInt(base_delta_str, &base_delta)) {
         Usage("Failed to parse --base-offset-delta argument '%s' as an off_t", base_delta_str);
       }
-    } else if (option.starts_with("--patched-image-location=")) {
-      patched_image_location = option.substr(strlen("--patched-image-location=")).data();
-    } else if (option == "--lock-output") {
-      lock_output = true;
-    } else if (option == "--no-lock-output") {
-      lock_output = false;
     } else if (option == "--dump-timings") {
       dump_timings = true;
     } else if (option == "--no-dump-timings") {
@@ -1479,33 +823,13 @@
     Usage("Instruction set must be set.");
   }
 
-  int ret;
-  if (!input_image_location.empty()) {
-    ret = patchoat_image(timings,
-                         isa,
-                         input_image_location,
-                         output_image_filename,
-                         base_delta,
-                         base_delta_set,
-                         debug);
-  } else {
-    ret = patchoat_oat(timings,
-                       isa,
-                       patched_image_location,
-                       base_delta,
-                       base_delta_set,
-                       input_oat_fd,
-                       input_vdex_fd,
-                       input_oat_location,
-                       input_oat_filename,
-                       have_input_oat,
-                       output_oat_fd,
-                       output_vdex_fd,
-                       output_oat_filename,
-                       have_output_oat,
-                       lock_output,
-                       debug);
-  }
+  int ret = patchoat_image(timings,
+                           isa,
+                           input_image_location,
+                           output_image_filename,
+                           base_delta,
+                           base_delta_set,
+                           debug);
 
   timings.EndTiming();
   if (dump_timings) {
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index a519631..e15a6bc 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -44,17 +44,7 @@
 
 class PatchOat {
  public:
-  // Patch only the oat file
-  static bool Patch(File* oat_in, off_t delta, File* oat_out, TimingLogger* timings,
-                    bool output_oat_opened_from_fd,  // Was this using --oatput-oat-fd ?
-                    bool new_oat_out);               // Output oat was a new file created by us?
-
-  // Patch only the image (art file)
-  static bool Patch(const std::string& art_location, off_t delta, File* art_out, InstructionSet isa,
-                    TimingLogger* timings);
-
-  // Patch both the image and the oat file
-  static bool Patch(const std::string& art_location,
+  static bool Patch(const std::string& image_location,
                     off_t delta,
                     const std::string& output_directory,
                     InstructionSet isa,
@@ -64,18 +54,11 @@
   PatchOat(PatchOat&&) = default;
 
  private:
-  // Takes ownership only of the ElfFile. All other pointers are only borrowed.
-  PatchOat(ElfFile* oat_file, off_t delta, TimingLogger* timings)
-      : oat_file_(oat_file), image_(nullptr), bitmap_(nullptr), heap_(nullptr), delta_(delta),
-        isa_(kNone), space_map_(nullptr), timings_(timings) {}
-  PatchOat(InstructionSet isa, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap,
-           MemMap* heap, off_t delta, TimingLogger* timings)
-      : image_(image), bitmap_(bitmap), heap_(heap),
-        delta_(delta), isa_(isa), space_map_(nullptr), timings_(timings) {}
-  PatchOat(InstructionSet isa, ElfFile* oat_file, MemMap* image,
+  // All pointers are only borrowed.
+  PatchOat(InstructionSet isa, MemMap* image,
            gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta,
            std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>>* map, TimingLogger* timings)
-      : oat_file_(oat_file), image_(image), bitmap_(bitmap), heap_(heap),
+      : image_(image), bitmap_(bitmap), heap_(heap),
         delta_(delta), isa_(isa), space_map_(map), timings_(timings) {}
 
   // Was the .art image at image_path made with --compile-pic ?
@@ -94,9 +77,7 @@
   // Attempt to replace the file with a symlink
   // Returns false if it fails
   static bool ReplaceOatFileWithSymlink(const std::string& input_oat_filename,
-                                        const std::string& output_oat_filename,
-                                        bool output_oat_opened_from_fd,
-                                        bool new_oat_out);  // Output oat was newly created?
+                                        const std::string& output_oat_filename);
 
   static void BitmapCallback(mirror::Object* obj, void* arg)
       REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -108,13 +89,6 @@
   void FixupMethod(ArtMethod* object, ArtMethod* copy)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Patches oat in place, modifying the oat_file given to the constructor.
-  bool PatchElf();
-  template <typename ElfFileImpl>
-  bool PatchElf(ElfFileImpl* oat_file);
-  template <typename ElfFileImpl>
-  bool PatchOatHeader(ElfFileImpl* oat_file);
-
   bool PatchImage(bool primary_image) REQUIRES_SHARED(Locks::mutator_lock_);
   void PatchArtFields(const ImageHeader* image_header) REQUIRES_SHARED(Locks::mutator_lock_);
   void PatchArtMethods(const ImageHeader* image_header) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -128,7 +102,6 @@
   void PatchDexFileArrays(mirror::ObjectArray<mirror::Object>* img_roots)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  bool WriteElf(File* out);
   bool WriteImage(File* out);
 
   template <typename T>
@@ -175,19 +148,6 @@
     return reinterpret_cast<T*>(ret);
   }
 
-  template <typename T>
-  T RelocatedAddressOfIntPointer(T obj) const {
-    if (obj == 0) {
-      return obj;
-    }
-    T ret = obj + delta_;
-    // Trim off high bits in case negative relocation with 64 bit patchoat.
-    if (Is32BitISA()) {
-      ret = static_cast<T>(static_cast<uint32_t>(ret));
-    }
-    return ret;
-  }
-
   bool Is32BitISA() const {
     return InstructionSetPointerSize(isa_) == PointerSize::k32;
   }
@@ -213,8 +173,6 @@
     mirror::Object* const copy_;
   };
 
-  // The elf file we are patching.
-  std::unique_ptr<ElfFile> oat_file_;
   // A mmap of the image we are patching. This is modified.
   const MemMap* const image_;
   // The bitmap over the image within the heap we are patching. This is not modified.
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 102c313..db1cad6 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -856,27 +856,6 @@
 #endif  // USE_READ_BARRIER
 .endm
 
-    /*
-     * Entry from managed code for array put operations of objects where the value being stored
-     * needs to be checked for compatibility.
-     * r0 = array, r1 = index, r2 = value
-     */
-ENTRY art_quick_aput_obj_with_null_and_bound_check
-    tst r0, r0
-    bne art_quick_aput_obj_with_bound_check
-    b art_quick_throw_null_pointer_exception
-END art_quick_aput_obj_with_null_and_bound_check
-
-    .hidden art_quick_aput_obj_with_bound_check
-ENTRY art_quick_aput_obj_with_bound_check
-    ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET]
-    cmp r3, r1
-    bhi art_quick_aput_obj
-    mov r0, r1
-    mov r1, r3
-    b art_quick_throw_array_bounds
-END art_quick_aput_obj_with_bound_check
-
 #ifdef USE_READ_BARRIER
     .extern artReadBarrierSlow
 #endif
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 3b3783c..ed24a07 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1404,33 +1404,6 @@
 #endif  // USE_READ_BARRIER
 .endm
 
-    /*
-     * Entry from managed code for array put operations of objects where the value being stored
-     * needs to be checked for compatibility.
-     * x0 = array, x1 = index, x2 = value
-     *
-     * Currently all values should fit into w0/w1/w2, and w1 always will as indices are 32b. We
-     * assume, though, that the upper 32b are zeroed out. At least for x1/w1 we can do better by
-     * using index-zero-extension in load/stores.
-     *
-     * Temporaries: x3, x4
-     * TODO: x4 OK? ip seems wrong here.
-     */
-ENTRY art_quick_aput_obj_with_null_and_bound_check
-    tst x0, x0
-    bne art_quick_aput_obj_with_bound_check
-    b art_quick_throw_null_pointer_exception
-END art_quick_aput_obj_with_null_and_bound_check
-
-ENTRY art_quick_aput_obj_with_bound_check
-    ldr w3, [x0, #MIRROR_ARRAY_LENGTH_OFFSET]
-    cmp w3, w1
-    bhi art_quick_aput_obj
-    mov x0, x1
-    mov x1, x3
-    b art_quick_throw_array_bounds
-END art_quick_aput_obj_with_bound_check
-
 #ifdef USE_READ_BARRIER
     .extern artReadBarrierSlow
 #endif
@@ -1675,8 +1648,6 @@
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB) implemented in asm
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 5c56923..36f9ea7 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -142,16 +142,8 @@
   static_assert(!IsDirectEntrypoint(kQuickGetObjStatic), "Non-direct C stub marked direct.");
 
   // Array
-  qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
-  static_assert(!IsDirectEntrypoint(kQuickAputObjectWithNullAndBoundCheck),
-                "Non-direct C stub marked direct.");
-  qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
-  static_assert(!IsDirectEntrypoint(kQuickAputObjectWithBoundCheck),
-                "Non-direct C stub marked direct.");
   qpoints->pAputObject = art_quick_aput_obj;
   static_assert(!IsDirectEntrypoint(kQuickAputObject), "Non-direct C stub marked direct.");
-  qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
-  static_assert(!IsDirectEntrypoint(kQuickHandleFillArrayData), "Non-direct C stub marked direct.");
 
   // JNI
   qpoints->pJniMethodStart = JniMethodStart;
@@ -262,6 +254,7 @@
       art_quick_invoke_virtual_trampoline_with_access_check;
   static_assert(!IsDirectEntrypoint(kQuickInvokeVirtualTrampolineWithAccessCheck),
                 "Non-direct C stub marked direct.");
+  qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic;
 
   // Thread
   qpoints->pTestSuspend = art_quick_test_suspend;
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 3acc0a9..76218fb 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1389,28 +1389,6 @@
 #endif  // USE_READ_BARRIER
 .endm
 
-    /*
-     * Entry from managed code for array put operations of objects where the value being stored
-     * needs to be checked for compatibility.
-     * a0 = array, a1 = index, a2 = value
-     */
-ENTRY art_quick_aput_obj_with_null_and_bound_check
-    bnez    $a0, .Lart_quick_aput_obj_with_bound_check_gp_set
-    nop
-    b art_quick_throw_null_pointer_exception
-    nop
-END art_quick_aput_obj_with_null_and_bound_check
-
-ENTRY art_quick_aput_obj_with_bound_check
-    lw $t0, MIRROR_ARRAY_LENGTH_OFFSET($a0)
-    sltu $t1, $a1, $t0
-    bnez $t1, .Lart_quick_aput_obj_gp_set
-    nop
-    move $a0, $a1
-    b art_quick_throw_array_bounds
-    move $a1, $t0
-END art_quick_aput_obj_with_bound_check
-
 #ifdef USE_READ_BARRIER
     .extern artReadBarrierSlow
 #endif
@@ -2285,15 +2263,12 @@
 ENTRY art_quick_invoke_polymorphic
     SETUP_SAVE_REFS_AND_ARGS_FRAME
     move  $a2, rSELF                          # Make $a2 an alias for the current Thread.
-    move  $a3, $sp                            # Make $a3 a pointer to the saved frame context.
-    addiu $sp, $sp, -24                       # Reserve space for JValue result and 4 words for callee.
-    .cfi_adjust_cfa_offset 24
+    addiu $a3, $sp, ARG_SLOT_SIZE             # Make $a3 a pointer to the saved frame context.
     sw    $zero, 20($sp)                      # Initialize JValue result.
     sw    $zero, 16($sp)
-    addiu $a0, $sp, 16                        # Make $a0 a pointer to the JValue result
     la    $t9, artInvokePolymorphic
     jalr  $t9                                 # (result, receiver, Thread*, context)
-    nop
+    addiu $a0, $sp, 16                        # Make $a0 a pointer to the JValue result
 .macro MATCH_RETURN_TYPE c, handler
     li    $t0, \c
     beq   $v0, $t0, \handler
@@ -2307,18 +2282,17 @@
     MATCH_RETURN_TYPE 'D', .Lstore_double_result
     MATCH_RETURN_TYPE 'F', .Lstore_float_result
     MATCH_RETURN_TYPE 'S', .Lstore_int_result
+    MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result
 .purgem MATCH_RETURN_TYPE
     nop
     b .Lcleanup_and_return
     nop
 .Lstore_boolean_result:
+    b .Lcleanup_and_return
     lbu   $v0, 16($sp)                        # Move byte from JValue result to return value register.
-    b .Lcleanup_and_return
-    nop
 .Lstore_char_result:
-    lhu   $v0, 16($sp)                        # Move char from JValue result to return value register.
     b .Lcleanup_and_return
-    nop
+    lhu   $v0, 16($sp)                        # Move char from JValue result to return value register.
 .Lstore_double_result:
 .Lstore_float_result:
     LDu   $f0, $f1, 16, $sp, $t0              # Move double/float from JValue result to return value register.
@@ -2331,8 +2305,6 @@
     lw    $v0, 16($sp)                        # Move lower bits from JValue result to return value register.
     // Fall-through to clean up and return.
 .Lcleanup_and_return:
-    addiu $sp, $sp, 24                        # Remove space for JValue result and the 4 words for the callee.
-    .cfi_adjust_cfa_offset -24
     lw    $t7, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
     bnez  $t7, 1f                             # Success if no exception is pending.
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index ae786fe..b53fd10 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1360,29 +1360,6 @@
 #endif  // USE_READ_BARRIER
 .endm
 
-    /*
-     * Entry from managed code for array put operations of objects where the value being stored
-     * needs to be checked for compatibility.
-     * a0 = array, a1 = index, a2 = value
-     */
-ENTRY art_quick_aput_obj_with_null_and_bound_check
-    bne    $a0, $zero, .Lart_quick_aput_obj_with_bound_check_gp_set
-    nop
-    b art_quick_throw_null_pointer_exception
-    .cpreturn                       # Restore gp from t8 in branch delay slot.
-END art_quick_aput_obj_with_null_and_bound_check
-
-ENTRY art_quick_aput_obj_with_bound_check
-    lwu  $t0, MIRROR_ARRAY_LENGTH_OFFSET($a0)
-    sltu $t1, $a1, $t0
-    bne  $t1, $zero, .Lart_quick_aput_obj_gp_set
-    nop
-    move $a0, $a1
-    move $a1, $t0
-    b art_quick_throw_array_bounds
-    .cpreturn                       # Restore gp from t8 in branch delay slot.
-END art_quick_aput_obj_with_bound_check
-
 ENTRY art_quick_aput_obj
     beq  $a2, $zero, .Ldo_aput_null
     nop
@@ -2132,9 +2109,8 @@
     daddiu $sp, $sp, -8                        # Reserve space for JValue result.
     .cfi_adjust_cfa_offset 8
     sd     $zero, 0($sp)                       # Initialize JValue result.
-    move   $a0, $sp                            # Make $a0 a pointer to the JValue result
     jal    artInvokePolymorphic                # (result, receiver, Thread*, context)
-    nop
+    move   $a0, $sp                            # Make $a0 a pointer to the JValue result
 .macro MATCH_RETURN_TYPE c, handler
     li     $t0, \c
     beq    $v0, $t0, \handler
@@ -2148,27 +2124,24 @@
     MATCH_RETURN_TYPE 'D', .Lstore_double_result
     MATCH_RETURN_TYPE 'F', .Lstore_float_result
     MATCH_RETURN_TYPE 'S', .Lstore_long_result
+    MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result
 .purgem MATCH_RETURN_TYPE
     nop
     b .Lcleanup_and_return
     nop
 .Lstore_boolean_result:
+    b      .Lcleanup_and_return
     lbu    $v0, 0($sp)                         # Move byte from JValue result to return value register.
-    b      .Lcleanup_and_return
-    nop
 .Lstore_char_result:
-    lhu    $v0, 0($sp)                         # Move char from JValue result to return value register.
     b      .Lcleanup_and_return
-    nop
+    lhu    $v0, 0($sp)                         # Move char from JValue result to return value register.
 .Lstore_double_result:
 .Lstore_float_result:
+    b      .Lcleanup_and_return
     l.d    $f0, 0($sp)                         # Move double/float from JValue result to return value register.
-    b      .Lcleanup_and_return
-    nop
 .Lstore_ref_result:
-    lwu    $v0, 0($sp)                         # Move zero extended lower 32-bits to return value register.
     b      .Lcleanup_and_return
-    nop
+    lwu    $v0, 0($sp)                         # Move zero extended lower 32-bits to return value register.
 .Lstore_long_result:
     ld     $v0, 0($sp)                         # Move long from JValue result to return value register.
     // Fall-through to clean up and return.
diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S
index abd9046..3959cbe 100644
--- a/runtime/arch/quick_alloc_entrypoints.S
+++ b/runtime/arch/quick_alloc_entrypoints.S
@@ -29,10 +29,6 @@
 // Called by managed code to allocate an array when the caller doesn't know whether it has access
 // to the created type.
 THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check\c_suffix, artAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
-THREE_ARG_DOWNCALL art_quick_check_and_alloc_array\c_suffix, artCheckAndAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
-THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check\c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate a string from bytes
 FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes\c_suffix, artAllocStringFromBytesFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate a string from chars
@@ -71,10 +67,6 @@
   THREE_ARG_DOWNCALL art_quick_alloc_array_resolved ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \
   THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check ## c_suffix, artAllocArrayFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-#define GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(c_suffix, cxx_suffix) \
-  THREE_ARG_DOWNCALL art_quick_check_and_alloc_array ## c_suffix, artCheckAndAllocArrayFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-#define GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \
-  THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check ## c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(c_suffix, cxx_suffix) \
   FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes ## c_suffix, artAllocStringFromBytesFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(c_suffix, cxx_suffix) \
@@ -95,8 +87,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
@@ -110,8 +100,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB)
@@ -129,8 +117,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_dlmalloc, DlMalloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc, DlMalloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc, DlMalloc)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc, DlMalloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc, DlMalloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc, DlMalloc)
@@ -141,8 +127,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_dlmalloc_instrumented, DlMallocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc_instrumented, DlMallocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc_instrumented, DlMallocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc_instrumented, DlMallocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc_instrumented, DlMallocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc_instrumented, DlMallocInstrumented)
@@ -154,8 +138,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_rosalloc, RosAlloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc, RosAlloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc, RosAlloc)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc, RosAlloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc, RosAlloc)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc, RosAlloc)
@@ -166,8 +148,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_rosalloc_instrumented, RosAllocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc_instrumented, RosAllocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc_instrumented, RosAllocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc_instrumented, RosAllocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc_instrumented, RosAllocInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc_instrumented, RosAllocInstrumented)
@@ -178,8 +158,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_bump_pointer, BumpPointer)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer, BumpPointer)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer, BumpPointer)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer, BumpPointer)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer, BumpPointer)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer, BumpPointer)
@@ -190,8 +168,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_bump_pointer_instrumented, BumpPointerInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer_instrumented, BumpPointerInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer_instrumented, BumpPointerInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer_instrumented, BumpPointerInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer_instrumented, BumpPointerInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer_instrumented, BumpPointerInstrumented)
@@ -202,8 +178,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab_instrumented, TLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab_instrumented, TLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab_instrumented, TLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab_instrumented, TLABInstrumented)
@@ -214,8 +188,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region, Region)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region, Region)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region, Region)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region, Region)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region, Region)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region, Region)
@@ -226,8 +198,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_instrumented, RegionInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_instrumented, RegionInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_instrumented, RegionInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_instrumented, RegionInstrumented)
@@ -238,8 +208,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab_instrumented, RegionTLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab_instrumented, RegionTLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab_instrumented, RegionTLABInstrumented)
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index ee65fa8..393dfe6 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -908,139 +908,6 @@
 #endif
 }
 
-
-TEST_F(StubTest, APutObj) {
-#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
-    (defined(__x86_64__) && !defined(__APPLE__))
-  Thread* self = Thread::Current();
-
-  // Do not check non-checked ones, we'd need handlers and stuff...
-  const uintptr_t art_quick_aput_obj_with_null_and_bound_check =
-      StubTest::GetEntrypoint(self, kQuickAputObjectWithNullAndBoundCheck);
-
-  // Create an object
-  ScopedObjectAccess soa(self);
-  // garbage is created during ClassLinker::Init
-
-  StackHandleScope<5> hs(soa.Self());
-  Handle<mirror::Class> c(
-      hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
-  Handle<mirror::Class> ca(
-      hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;")));
-
-  // Build a string array of size 1
-  Handle<mirror::ObjectArray<mirror::Object>> array(
-      hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), ca.Get(), 10)));
-
-  // Build a string -> should be assignable
-  Handle<mirror::String> str_obj(
-      hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!")));
-
-  // Build a generic object -> should fail assigning
-  Handle<mirror::Object> obj_obj(hs.NewHandle(c->AllocObject(soa.Self())));
-
-  // Play with it...
-
-  // 1) Success cases
-  // 1.1) Assign str_obj to array[0..3]
-
-  EXPECT_FALSE(self->IsExceptionPending());
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(str_obj.Get()),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(str_obj.Get(), array->Get(0));
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 1U, reinterpret_cast<size_t>(str_obj.Get()),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(str_obj.Get(), array->Get(1));
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 2U, reinterpret_cast<size_t>(str_obj.Get()),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(str_obj.Get(), array->Get(2));
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 3U, reinterpret_cast<size_t>(str_obj.Get()),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(str_obj.Get(), array->Get(3));
-
-  // 1.2) Assign null to array[0..3]
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(nullptr),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(nullptr, array->Get(0));
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 1U, reinterpret_cast<size_t>(nullptr),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(nullptr, array->Get(1));
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 2U, reinterpret_cast<size_t>(nullptr),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(nullptr, array->Get(2));
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 3U, reinterpret_cast<size_t>(nullptr),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(nullptr, array->Get(3));
-
-  // TODO: Check _which_ exception is thrown. Then make 3) check that it's the right check order.
-
-  // 2) Failure cases (str into str[])
-  // 2.1) Array = null
-  // TODO: Throwing NPE needs actual DEX code
-
-//  Invoke3(reinterpret_cast<size_t>(nullptr), 0U, reinterpret_cast<size_t>(str_obj.Get()),
-//          reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
-//
-//  EXPECT_TRUE(self->IsExceptionPending());
-//  self->ClearException();
-
-  // 2.2) Index < 0
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), static_cast<size_t>(-1),
-          reinterpret_cast<size_t>(str_obj.Get()),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_TRUE(self->IsExceptionPending());
-  self->ClearException();
-
-  // 2.3) Index > 0
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 10U, reinterpret_cast<size_t>(str_obj.Get()),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_TRUE(self->IsExceptionPending());
-  self->ClearException();
-
-  // 3) Failure cases (obj into str[])
-
-  Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(obj_obj.Get()),
-          art_quick_aput_obj_with_null_and_bound_check, self);
-
-  EXPECT_TRUE(self->IsExceptionPending());
-  self->ClearException();
-
-  // Tests done.
-#else
-  LOG(INFO) << "Skipping aput_obj as I don't know how to do that on " << kRuntimeISA;
-  // Force-print to std::cout so it's also outside the logcat.
-  std::cout << "Skipping aput_obj as I don't know how to do that on " << kRuntimeISA << std::endl;
-#endif
-}
-
 TEST_F(StubTest, AllocObject) {
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
     (defined(__x86_64__) && !defined(__APPLE__))
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 1d979d8..c420259 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1352,26 +1352,6 @@
 #endif  // USE_READ_BARRIER
 END_MACRO
 
-    /*
-     * Entry from managed code for array put operations of objects where the value being stored
-     * needs to be checked for compatibility.
-     * eax = array, ecx = index, edx = value
-     */
-DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check
-    testl %eax, %eax
-    jnz SYMBOL(art_quick_aput_obj_with_bound_check)
-    jmp SYMBOL(art_quick_throw_null_pointer_exception)
-END_FUNCTION art_quick_aput_obj_with_null_and_bound_check
-
-DEFINE_FUNCTION art_quick_aput_obj_with_bound_check
-    movl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ebx
-    cmpl %ebx, %ecx
-    jb SYMBOL(art_quick_aput_obj)
-    mov %ecx, %eax
-    mov %ebx, %ecx
-    jmp SYMBOL(art_quick_throw_array_bounds)
-END_FUNCTION art_quick_aput_obj_with_bound_check
-
 DEFINE_FUNCTION art_quick_aput_obj
     test %edx, %edx              // store of null
     jz .Ldo_aput_null
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 28034c9..550a5e8 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -989,8 +989,6 @@
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
@@ -1000,8 +998,6 @@
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB)
 // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB)
 GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB)
@@ -1466,7 +1462,7 @@
      * 64b PUSH/POP and 32b argument.
      * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
      *
-     * As with art_quick_aput_obj* functions, the 64b versions are in comments.
+     * As with art_quick_aput_obj function, the 64b versions are in comments.
      */
 MACRO4(READ_BARRIER, obj_reg, offset, dest_reg32, dest_reg64)
 #ifdef USE_READ_BARRIER
@@ -1503,46 +1499,6 @@
 #endif  // USE_READ_BARRIER
 END_MACRO
 
-    /*
-     * Entry from managed code for array put operations of objects where the value being stored
-     * needs to be checked for compatibility.
-     *
-     * Currently all the parameters should fit into the 32b portions of the registers. Index always
-     * will. So we optimize for a tighter encoding. The 64b versions are in comments.
-     *
-     * rdi(edi) = array, rsi(esi) = index, rdx(edx) = value
-     */
-DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check
-#if defined(__APPLE__)
-    int3
-    int3
-#else
-    testl %edi, %edi
-//  testq %rdi, %rdi
-    jnz art_quick_aput_obj_with_bound_check
-    jmp art_quick_throw_null_pointer_exception
-#endif  // __APPLE__
-END_FUNCTION art_quick_aput_obj_with_null_and_bound_check
-
-
-DEFINE_FUNCTION art_quick_aput_obj_with_bound_check
-#if defined(__APPLE__)
-    int3
-    int3
-#else
-    movl MIRROR_ARRAY_LENGTH_OFFSET(%edi), %ecx
-//  movl MIRROR_ARRAY_LENGTH_OFFSET(%rdi), %ecx  // This zero-extends, so value(%rcx)=value(%ecx)
-    cmpl %ecx, %esi
-    jb art_quick_aput_obj
-    mov %esi, %edi
-//  mov %rsi, %rdi
-    mov %ecx, %esi
-//  mov %rcx, %rsi
-    jmp art_quick_throw_array_bounds
-#endif  // __APPLE__
-END_FUNCTION art_quick_aput_obj_with_bound_check
-
-
 DEFINE_FUNCTION art_quick_aput_obj
     testl %edx, %edx                // store of null
 //  test %rdx, %rdx
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 9c20740..0fd891c 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -180,20 +180,6 @@
                                                   pointer_size);
 }
 
-template <bool kWithCheck>
-inline mirror::Class* ArtMethod::GetDexCacheResolvedType(dex::TypeIndex type_index,
-                                                         PointerSize pointer_size) {
-  if (kWithCheck) {
-    mirror::DexCache* dex_cache = GetInterfaceMethodIfProxy(pointer_size)->GetDexCache();
-    if (UNLIKELY(type_index.index_ >= dex_cache->NumResolvedTypes())) {
-      ThrowArrayIndexOutOfBoundsException(type_index.index_, dex_cache->NumResolvedTypes());
-      return nullptr;
-    }
-  }
-  mirror::Class* klass = GetDexCacheResolvedTypes(pointer_size)[type_index.index_].Read();
-  return (klass != nullptr && !klass->IsErroneous()) ? klass : nullptr;
-}
-
 inline bool ArtMethod::HasDexCacheResolvedTypes(PointerSize pointer_size) {
   return GetDexCacheResolvedTypes(pointer_size) != nullptr;
 }
@@ -207,15 +193,15 @@
   return GetDexCacheResolvedTypes(pointer_size) == other->GetDexCacheResolvedTypes(pointer_size);
 }
 
-inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx,
-                                                       bool resolve,
-                                                       PointerSize pointer_size) {
-  mirror::Class* type = GetDexCacheResolvedType(type_idx, pointer_size);
-  if (type == nullptr && resolve) {
-    type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this);
+inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) {
+  ObjPtr<mirror::DexCache> dex_cache = GetDexCache();
+  ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
+  if (UNLIKELY(type == nullptr) && resolve) {
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    type = class_linker->ResolveType(type_idx, this);
     CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
   }
-  return type;
+  return type.Ptr();
 }
 
 inline bool ArtMethod::CheckIncompatibleClassChange(InvokeType type) {
@@ -333,9 +319,9 @@
   return GetDexFile()->GetCodeItem(GetCodeItemOffset());
 }
 
-inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size) {
+inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx) {
   DCHECK(!IsProxyMethod());
-  return GetDexCacheResolvedType(type_idx, pointer_size) != nullptr;
+  return GetClassFromTypeIndex(type_idx, /* resolve */ false) != nullptr;
 }
 
 inline int32_t ArtMethod::GetLineNumFromDexPC(uint32_t dex_pc) {
@@ -435,18 +421,13 @@
   SetNativePointer(DexCacheResolvedTypesOffset(pointer_size), new_dex_cache_types, pointer_size);
 }
 
-inline mirror::Class* ArtMethod::GetReturnType(bool resolve, PointerSize pointer_size) {
+inline mirror::Class* ArtMethod::GetReturnType(bool resolve) {
   DCHECK(!IsProxyMethod());
   const DexFile* dex_file = GetDexFile();
   const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex());
   const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id);
   dex::TypeIndex return_type_idx = proto_id.return_type_idx_;
-  mirror::Class* type = GetDexCacheResolvedType(return_type_idx, pointer_size);
-  if (type == nullptr && resolve) {
-    type = Runtime::Current()->GetClassLinker()->ResolveType(return_type_idx, this);
-    CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
-  }
-  return type;
+  return GetClassFromTypeIndex(return_type_idx, resolve);
 }
 
 inline bool ArtMethod::HasSingleImplementation() {
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index dfc7837..d7d39af 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -236,7 +236,6 @@
   // Default to handler not found.
   uint32_t found_dex_pc = DexFile::kDexNoIndex;
   // Iterate over the catch handlers associated with dex_pc.
-  PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
   for (CatchHandlerIterator it(*code_item, dex_pc); it.HasNext(); it.Next()) {
     dex::TypeIndex iter_type_idx = it.GetHandlerTypeIndex();
     // Catch all case
@@ -245,9 +244,7 @@
       break;
     }
     // Does this catch exception type apply?
-    mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx,
-                                                               true /* resolve */,
-                                                               pointer_size);
+    mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx, true /* resolve */);
     if (UNLIKELY(iter_exception_type == nullptr)) {
       // Now have a NoClassDefFoundError as exception. Ignore in case the exception class was
       // removed by a pro-guard like tool.
diff --git a/runtime/art_method.h b/runtime/art_method.h
index b80ed9c..d292630 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -351,9 +351,6 @@
   bool HasSameDexCacheResolvedMethods(ArtMethod** other_cache, PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  template <bool kWithCheck = true>
-  mirror::Class* GetDexCacheResolvedType(dex::TypeIndex type_idx, PointerSize pointer_size)
-      REQUIRES_SHARED(Locks::mutator_lock_);
   void SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_cache_types,
                                 PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -364,9 +361,7 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Get the Class* from the type index into this method's dex cache.
-  mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx,
-                                       bool resolve,
-                                       PointerSize pointer_size)
+  mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Returns true if this method has the same name and signature of the other method.
@@ -558,8 +553,7 @@
 
   const DexFile::CodeItem* GetCodeItem() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  bool IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size)
-      REQUIRES_SHARED(Locks::mutator_lock_);
+  bool IsResolvedTypeIdx(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
 
   int32_t GetLineNumFromDexPC(uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -580,8 +574,7 @@
 
   // May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large
   // number of bugs at call sites.
-  mirror::Class* GetReturnType(bool resolve, PointerSize pointer_size)
-      REQUIRES_SHARED(Locks::mutator_lock_);
+  mirror::Class* GetReturnType(bool resolve) REQUIRES_SHARED(Locks::mutator_lock_);
 
   mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 4b15a22..18a53c9 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -104,7 +104,7 @@
 
 // Offset of field Thread::tlsPtr_.mterp_current_ibase.
 #define THREAD_CURRENT_IBASE_OFFSET \
-    (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 164) * __SIZEOF_POINTER__)
+    (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 159) * __SIZEOF_POINTER__)
 ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET,
             art::Thread::MterpCurrentIBaseOffset<POINTER_SIZE>().Int32Value())
 // Offset of field Thread::tlsPtr_.mterp_default_ibase.
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 9116097..e05a85a 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -46,6 +46,7 @@
 ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr;
 Mutex* Locks::instrument_entrypoints_lock_ = nullptr;
 Mutex* Locks::intern_table_lock_ = nullptr;
+Mutex* Locks::jni_function_table_lock_ = nullptr;
 Mutex* Locks::jni_libraries_lock_ = nullptr;
 Mutex* Locks::logging_lock_ = nullptr;
 Mutex* Locks::mem_maps_lock_ = nullptr;
@@ -957,6 +958,7 @@
     DCHECK(verifier_deps_lock_ != nullptr);
     DCHECK(host_dlopen_handles_lock_ != nullptr);
     DCHECK(intern_table_lock_ != nullptr);
+    DCHECK(jni_function_table_lock_ != nullptr);
     DCHECK(jni_libraries_lock_ != nullptr);
     DCHECK(logging_lock_ != nullptr);
     DCHECK(mutator_lock_ != nullptr);
@@ -1098,6 +1100,10 @@
     DCHECK(jni_weak_globals_lock_ == nullptr);
     jni_weak_globals_lock_ = new Mutex("JNI weak global reference table lock", current_lock_level);
 
+    UPDATE_CURRENT_LOCK_LEVEL(kJniFunctionTableLock);
+    DCHECK(jni_function_table_lock_ == nullptr);
+    jni_function_table_lock_ = new Mutex("JNI function table lock", current_lock_level);
+
     UPDATE_CURRENT_LOCK_LEVEL(kAbortLock);
     DCHECK(abort_lock_ == nullptr);
     abort_lock_ = new Mutex("abort lock", current_lock_level, true);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 2adeb8c..21dd437 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -68,6 +68,7 @@
   kRosAllocBulkFreeLock,
   kMarkSweepMarkStackLock,
   kTransactionLogLock,
+  kJniFunctionTableLock,
   kJniWeakGlobalsLock,
   kJniGlobalsLock,
   kReferenceQueueSoftReferencesLock,
@@ -698,8 +699,11 @@
   // Guard accesses to the JNI Weak Global Reference table.
   static Mutex* jni_weak_globals_lock_ ACQUIRED_AFTER(jni_globals_lock_);
 
+  // Guard accesses to the JNI function table override.
+  static Mutex* jni_function_table_lock_ ACQUIRED_AFTER(jni_weak_globals_lock_);
+
   // Have an exclusive aborting thread.
-  static Mutex* abort_lock_ ACQUIRED_AFTER(jni_weak_globals_lock_);
+  static Mutex* abort_lock_ ACQUIRED_AFTER(jni_function_table_lock_);
 
   // Allow mutual exclusion when manipulating Thread::suspend_count_.
   // TODO: Does the trade-off of a per-thread lock make sense?
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 5fc5f1a..2e17dd8 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -25,7 +25,6 @@
 #include "mirror/class_loader.h"
 #include "mirror/dex_cache-inl.h"
 #include "mirror/iftable.h"
-#include "mirror/throwable.h"
 #include "mirror/object_array.h"
 #include "handle_scope-inl.h"
 #include "scoped_thread_state_change-inl.h"
@@ -90,25 +89,16 @@
   if (kIsDebugBuild) {
     Thread::Current()->AssertNoPendingException();
   }
-  ObjPtr<mirror::Class> resolved_type =
-      referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_);
+  ObjPtr<mirror::Class> resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx);
   if (UNLIKELY(resolved_type == nullptr)) {
     StackHandleScope<2> hs(Thread::Current());
-    // There could be an out of bounds exception from GetDexCacheResolvedType, don't call
-    // ResolveType for this case.
-    if (LIKELY(!hs.Self()->IsExceptionPending())) {
-      ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
-      Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
-      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.
-    } else {
-      // Make sure its an array out of bounds exception.
-      DCHECK(hs.Self()->GetException()->GetClass()->DescriptorEquals(
-          "Ljava/lang/ArrayIndexOutOfBoundsException;"));
-    }
+    ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
+    Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
+    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();
 }
@@ -256,8 +246,8 @@
     // Locate the dex cache of the original interface/Object
     for (const DexCacheData& data : dex_caches_) {
       if (!self->IsJWeakCleared(data.weak_root) &&
-          proxy_method->HasSameDexCacheResolvedTypes(data.resolved_types,
-                                                     image_pointer_size_)) {
+          proxy_method->HasSameDexCacheResolvedMethods(data.resolved_methods,
+                                                       image_pointer_size_)) {
         ObjPtr<mirror::DexCache> dex_cache =
             ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root));
         if (dex_cache != nullptr) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 448b460..07c6eda 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3303,7 +3303,7 @@
   DexCacheData data;
   data.weak_root = dex_cache_jweak;
   data.dex_file = dex_cache->GetDexFile();
-  data.resolved_types = dex_cache->GetResolvedTypes();
+  data.resolved_methods = dex_cache->GetResolvedMethods();
   dex_caches_.push_back(data);
 }
 
@@ -4373,8 +4373,7 @@
   CHECK_STREQ(np->GetName(), prototype->GetName());
   CHECK_STREQ(np->GetShorty(), prototype->GetShorty());
   // More complex sanity - via dex cache
-  CHECK_EQ(np->GetReturnType(true /* resolve */, image_pointer_size_),
-           prototype->GetReturnType(true /* resolve */, image_pointer_size_));
+  CHECK_EQ(np->GetReturnType(true /* resolve */), prototype->GetReturnType(true /* resolve */));
 }
 
 bool ClassLinker::CanWeInitializeClass(ObjPtr<mirror::Class> klass, bool can_init_statics,
@@ -4836,7 +4835,6 @@
 }
 
 static bool HasSameSignatureWithDifferentClassLoaders(Thread* self,
-                                                      PointerSize pointer_size,
                                                       Handle<mirror::Class> klass,
                                                       Handle<mirror::Class> super_klass,
                                                       ArtMethod* method1,
@@ -4844,14 +4842,12 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   {
     StackHandleScope<1> hs(self);
-    Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */,
-                                                                          pointer_size)));
+    Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */)));
     if (UNLIKELY(return_type.Get() == nullptr)) {
       ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method1);
       return false;
     }
-    ObjPtr<mirror::Class> other_return_type = method2->GetReturnType(true /* resolve */,
-                                                              pointer_size);
+    ObjPtr<mirror::Class> other_return_type = method2->GetReturnType(true /* resolve */);
     if (UNLIKELY(other_return_type == nullptr)) {
       ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method2);
       return false;
@@ -4896,7 +4892,7 @@
     StackHandleScope<1> hs(self);
     dex::TypeIndex param_type_idx = types1->GetTypeItem(i).type_idx_;
     Handle<mirror::Class> param_type(hs.NewHandle(
-        method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */, pointer_size)));
+        method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */)));
     if (UNLIKELY(param_type.Get() == nullptr)) {
       ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
                                              method1, i, param_type_idx);
@@ -4904,7 +4900,7 @@
     }
     dex::TypeIndex other_param_type_idx = types2->GetTypeItem(i).type_idx_;
     ObjPtr<mirror::Class> other_param_type =
-        method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */, pointer_size);
+        method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */);
     if (UNLIKELY(other_param_type == nullptr)) {
       ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
                                              method2, i, other_param_type_idx);
@@ -4940,9 +4936,11 @@
       auto* m = klass->GetVTableEntry(i, image_pointer_size_);
       auto* super_m = klass->GetSuperClass()->GetVTableEntry(i, image_pointer_size_);
       if (m != super_m) {
-        if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_,
-                                                                klass, super_klass,
-                                                                m, super_m))) {
+        if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self,
+                                                                klass,
+                                                                super_klass,
+                                                                m,
+                                                                super_m))) {
           self->AssertPendingException();
           return false;
         }
@@ -4958,9 +4956,11 @@
             j, image_pointer_size_);
         auto* super_m = super_klass->GetVirtualMethod(j, image_pointer_size_);
         if (m != super_m) {
-          if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_,
-                                                                  klass, super_klass,
-                                                                  m, super_m))) {
+          if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self,
+                                                                  klass,
+                                                                  super_klass,
+                                                                  m,
+                                                                  super_m))) {
             self->AssertPendingException();
             return false;
           }
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index cad674e..9b98671 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -649,6 +649,10 @@
   ClassTable* ClassTableForClassLoader(ObjPtr<mirror::ClassLoader> class_loader)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  void AppendToBootClassPath(Thread* self, const DexFile& dex_file)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!Locks::dex_lock_);
+
   struct DexCacheData {
     // Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may
     // not work properly.
@@ -657,7 +661,7 @@
     // jweak decode that triggers read barriers (and mark them alive unnecessarily and mess with
     // class unloading.)
     const DexFile* dex_file;
-    GcRoot<mirror::Class>* resolved_types;
+    ArtMethod** resolved_methods;
   };
 
  private:
@@ -744,9 +748,6 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
 
-  void AppendToBootClassPath(Thread* self, const DexFile& dex_file)
-      REQUIRES_SHARED(Locks::mutator_lock_)
-      REQUIRES(!Locks::dex_lock_);
   void AppendToBootClassPath(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index df4413d..006476b 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -3985,9 +3985,7 @@
         if (shorty[i + 1] == 'L') {
           // Did we really get an argument of an appropriate reference type?
           mirror::Class* parameter_type =
-              m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_,
-                                       true /* resolve */,
-                                       kRuntimePointerSize);
+              m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_, true /* resolve */);
           mirror::Object* argument = gRegistry->Get<mirror::Object*>(arg_values[i], &error);
           if (error != JDWP::ERR_NONE) {
             return JDWP::ERR_INVALID_OBJECT;
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index b0c4597..7ae9f03 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -160,7 +160,7 @@
   // image at GetImageLocation(). This is used for testing mismatched
   // image checksums in the oat_file_assistant_tests.
   std::string GetImageLocation2() const {
-    return GetImageDirectory() + "/core-npic.art";
+    return GetImageDirectory() + "/core-interpreter.art";
   }
 
   std::string GetDexSrc1() const {
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index 9504e8b..16a447b 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -608,7 +608,7 @@
     return nullptr;
   }
   Handle<mirror::Class> method_return(hs.NewHandle(
-      annotation_method->GetReturnType(true /* resolve */, pointer_size)));
+      annotation_method->GetReturnType(true /* resolve */)));
 
   DexFile::AnnotationValue annotation_value;
   if (!ProcessAnnotationValue(klass, annotation, &annotation_value, method_return,
@@ -948,9 +948,7 @@
   DexFile::AnnotationValue annotation_value;
   StackHandleScope<2> hs(Thread::Current());
   Handle<mirror::Class> h_klass(hs.NewHandle(klass));
-  PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-  Handle<mirror::Class> return_type(hs.NewHandle(
-      method->GetReturnType(true /* resolve */, pointer_size)));
+  Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType(true /* resolve */)));
   if (!ProcessAnnotationValue(h_klass, &annotation, &annotation_value, return_type,
                               DexFile::kAllObjects)) {
     return nullptr;
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 7d6f866..87046bc 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -241,10 +241,9 @@
     *slow_path = true;
     return nullptr;  // Failure
   }
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  PointerSize pointer_size = class_linker->GetImagePointerSize();
-  mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, pointer_size);
+  mirror::Class* klass = method->GetDexCache()->GetResolvedType(type_idx);
   if (UNLIKELY(klass == nullptr)) {  // Not in dex cache so try to resolve
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     klass = class_linker->ResolveType(type_idx, method);
     *slow_path = true;
     if (klass == nullptr) {  // Error
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index b17e1a8..25fd727 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -38,98 +38,13 @@
 
 namespace art {
 
-static inline mirror::Class* CheckFilledNewArrayAlloc(dex::TypeIndex type_idx,
-                                                      int32_t component_count,
-                                                      ArtMethod* referrer,
-                                                      Thread* self,
-                                                      bool access_check)
-    REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
-  if (UNLIKELY(component_count < 0)) {
-    ThrowNegativeArraySizeException(component_count);
-    return nullptr;  // Failure
-  }
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  PointerSize pointer_size = class_linker->GetImagePointerSize();
-  mirror::Class* klass = referrer->GetDexCacheResolvedType<false>(type_idx, pointer_size);
-  if (UNLIKELY(klass == nullptr)) {  // Not in dex cache so try to resolve
-    klass = class_linker->ResolveType(type_idx, referrer);
-    if (klass == nullptr) {  // Error
-      DCHECK(self->IsExceptionPending());
-      return nullptr;  // Failure
-    }
-  }
-  if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) {
-    if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) {
-      ThrowRuntimeException("Bad filled array request for type %s",
-                            klass->PrettyDescriptor().c_str());
-    } else {
-      self->ThrowNewExceptionF(
-          "Ljava/lang/InternalError;",
-          "Found type %s; filled-new-array not implemented for anything but 'int'",
-          klass->PrettyDescriptor().c_str());
-    }
-    return nullptr;  // Failure
-  }
-  if (access_check) {
-    mirror::Class* referrer_klass = referrer->GetDeclaringClass();
-    if (UNLIKELY(!referrer_klass->CanAccess(klass))) {
-      ThrowIllegalAccessErrorClass(referrer_klass, klass);
-      return nullptr;  // Failure
-    }
-  }
-  DCHECK(klass->IsArrayClass()) << klass->PrettyClass();
-  return klass;
-}
-
-// Helper function to allocate array for FILLED_NEW_ARRAY.
-mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx,
-                                          int32_t component_count,
-                                          ArtMethod* referrer,
-                                          Thread* self,
-                                          bool access_check,
-                                          gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) {
-  mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self,
-                                                  access_check);
-  if (UNLIKELY(klass == nullptr)) {
-    return nullptr;
-  }
-  // Always go slow path for now, filled new array is not common.
-  gc::Heap* heap = Runtime::Current()->GetHeap();
-  // Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then
-  // the heap switched the allocator type while we were suspended.
-  return mirror::Array::Alloc<false>(self, klass, component_count,
-                                     klass->GetComponentSizeShift(),
-                                     heap->GetCurrentAllocator());
-}
-
-// Helper function to allocate array for FILLED_NEW_ARRAY.
-mirror::Array* CheckAndAllocArrayFromCodeInstrumented(
-    dex::TypeIndex type_idx,
-    int32_t component_count,
-    ArtMethod* referrer,
-    Thread* self,
-    bool access_check,
-    gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) {
-  mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self,
-                                                  access_check);
-  if (UNLIKELY(klass == nullptr)) {
-    return nullptr;
-  }
-  gc::Heap* heap = Runtime::Current()->GetHeap();
-  // Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then
-  // the heap switched the allocator type while we were suspended.
-  return mirror::Array::Alloc<true>(self, klass, component_count,
-                                    klass->GetComponentSizeShift(),
-                                    heap->GetCurrentAllocator());
-}
-
 void CheckReferenceResult(Handle<mirror::Object> o, Thread* self) {
   if (o.Get() == nullptr) {
     return;
   }
   // Make sure that the result is an instance of the type this method was expected to return.
   ArtMethod* method = self->GetCurrentMethod(nullptr);
-  mirror::Class* return_type = method->GetReturnType(true /* resolve */, kRuntimePointerSize);
+  mirror::Class* return_type = method->GetReturnType(true /* resolve */);
 
   if (!o->InstanceOf(return_type)) {
     Runtime::Current()->GetJavaVM()->JniAbortF(nullptr,
@@ -192,8 +107,7 @@
       ArtMethod* interface_method =
           soa.Decode<mirror::Method>(interface_method_jobj)->GetArtMethod();
       // This can cause thread suspension.
-      PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-      mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */, pointer_size);
+      mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */);
       ObjPtr<mirror::Object> result_ref = soa.Decode<mirror::Object>(result);
       JValue result_unboxed;
       if (!UnboxPrimitiveForResult(result_ref.Ptr(), result_type, &result_unboxed)) {
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index d4cf83c..b427c07 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -102,24 +102,6 @@
     REQUIRES_SHARED(Locks::mutator_lock_)
     REQUIRES(!Roles::uninterruptible_);
 
-mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx,
-                                          int32_t component_count,
-                                          ArtMethod* method,
-                                          Thread* self,
-                                          bool access_check,
-                                          gc::AllocatorType allocator_type)
-    REQUIRES_SHARED(Locks::mutator_lock_)
-    REQUIRES(!Roles::uninterruptible_);
-
-mirror::Array* CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex type_idx,
-                                                      int32_t component_count,
-                                                      ArtMethod* method,
-                                                      Thread* self,
-                                                      bool access_check,
-                                                      gc::AllocatorType allocator_type)
-    REQUIRES_SHARED(Locks::mutator_lock_)
-    REQUIRES(!Roles::uninterruptible_);
-
 // Type of find field operation for fast and slow case.
 enum FindFieldType {
   InstanceObjectRead,
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 2d06508..23a99a0 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -109,46 +109,6 @@
                                                      self, \
                                                      allocator_type); \
 } \
-extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \
-    uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \
-    REQUIRES_SHARED(Locks::mutator_lock_) { \
-  ScopedQuickEntrypointChecks sqec(self); \
-  if (!(instrumented_bool)) { \
-    return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \
-                                      component_count, \
-                                      method, \
-                                      self, \
-                                      false, \
-                                      allocator_type); \
-  } else { \
-    return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \
-                                                  component_count, \
-                                                  method, \
-                                                  self, \
-                                                  false, \
-                                                  allocator_type); \
-  } \
-} \
-extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \
-    uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \
-    REQUIRES_SHARED(Locks::mutator_lock_) { \
-  ScopedQuickEntrypointChecks sqec(self); \
-  if (!(instrumented_bool)) { \
-    return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \
-                                      component_count, \
-                                      method, \
-                                      self, \
-                                      true, \
-                                      allocator_type); \
-  } else { \
-    return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \
-                                                  component_count, \
-                                                  method, \
-                                                  self, \
-                                                  true, \
-                                                  allocator_type); \
-  } \
-} \
 extern "C" mirror::String* artAllocStringFromBytesFromCode##suffix##suffix2( \
     mirror::ByteArray* byte_array, int32_t high, int32_t offset, int32_t byte_count, \
     Thread* self) \
@@ -219,8 +179,6 @@
     qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix##_instrumented; \
     qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix##_instrumented; \
     qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix##_instrumented; \
-    qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix##_instrumented; \
-    qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented; \
     qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix##_instrumented; \
     qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix##_instrumented; \
     qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix##_instrumented; \
@@ -231,8 +189,6 @@
     qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix; \
     qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix; \
     qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix; \
-    qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix; \
-    qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix; \
     qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix; \
     qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix; \
     qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix; \
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
index 8ce61c1..6481b97 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -66,10 +66,7 @@
   qpoints->pGetObjStatic = art_quick_get_obj_static;
 
   // Array
-  qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
-  qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
   qpoints->pAputObject = art_quick_aput_obj;
-  qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
 
   // JNI
   qpoints->pJniMethodStart = JniMethodStart;
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index 4d5d6de..0e75e94 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -26,8 +26,6 @@
   V(AllocObjectResolved, void*, mirror::Class*) \
   V(AllocObjectInitialized, void*, mirror::Class*) \
   V(AllocObjectWithChecks, void*, mirror::Class*) \
-  V(CheckAndAllocArray, void*, uint32_t, int32_t, ArtMethod*) \
-  V(CheckAndAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*) \
   V(AllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t) \
   V(AllocStringFromChars, void*, int32_t, int32_t, void*) \
   V(AllocStringFromString, void*, void*) \
@@ -65,10 +63,7 @@
   V(GetObjInstance, void*, uint32_t, void*) \
   V(GetObjStatic, void*, uint32_t) \
 \
-  V(AputObjectWithNullAndBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \
-  V(AputObjectWithBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \
   V(AputObject, void, mirror::Array*, int32_t, mirror::Object*) \
-  V(HandleFillArrayData, void, void*, void*) \
 \
   V(JniMethodStart, uint32_t, Thread*) \
   V(JniMethodFastStart, uint32_t, Thread*) \
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 6301f93..b9caf0f 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -161,12 +161,8 @@
                          sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectInitialized, pAllocObjectWithChecks,
                          sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pCheckAndAllocArray,
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pAllocStringFromBytes,
                          sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArray, pCheckAndAllocArrayWithAccessCheck,
-                         sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArrayWithAccessCheck,
-                         pAllocStringFromBytes, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromBytes, pAllocStringFromChars,
                          sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromChars, pAllocStringFromString,
@@ -205,13 +201,8 @@
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Instance, pGet64Static, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Static, pGetObjInstance, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjInstance, pGetObjStatic, sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjStatic, pAputObjectWithNullAndBoundCheck,
-                         sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithNullAndBoundCheck,
-                         pAputObjectWithBoundCheck, sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithBoundCheck, pAputObject, sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pHandleFillArrayData, sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pHandleFillArrayData, pJniMethodStart, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjStatic, pAputObject, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pJniMethodStart, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodStart, pJniMethodFastStart,
                          sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodFastStart, pJniMethodStartSynchronized,
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 7b86339..6044053 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -846,7 +846,7 @@
         // Some threads in 'runnable_thread_ids' are probably stuck. Try to dump their stacks.
         // Avoid using ThreadList::Dump() initially because it is likely to get stuck as well.
         {
-          ReaderMutexLock mu0(self, *Locks::mutator_lock_);
+          ScopedObjectAccess soa(self);
           MutexLock mu1(self, *Locks::thread_list_lock_);
           for (Thread* thread : thread_list->GetList()) {
             uint32_t tid = thread->GetThreadId();
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index ca26207..76777d9 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -751,16 +751,14 @@
         case 'L': {
           ObjPtr<mirror::Object> o = shadow_frame.GetVRegReference(src_reg);
           if (do_assignability_check && o != nullptr) {
-            PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
             const dex::TypeIndex type_idx = params->GetTypeItem(shorty_pos).type_idx_;
-            ObjPtr<mirror::Class> arg_type = method->GetDexCacheResolvedType(type_idx,
-                                                                             pointer_size);
+            ObjPtr<mirror::Class> arg_type = method->GetDexCache()->GetResolvedType(type_idx);
             if (arg_type == nullptr) {
               StackHandleScope<1> hs(self);
               // Preserve o since it is used below and GetClassFromTypeIndex may cause thread
               // suspension.
               HandleWrapperObjPtr<mirror::Object> h = hs.NewHandleWrapper(&o);
-              arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */, pointer_size);
+              arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */);
               if (arg_type == nullptr) {
                 CHECK(self->IsExceptionPending());
                 return false;
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index d7dfcd4..a77a3fc 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -285,9 +285,7 @@
         const size_t ref_idx = inst->VRegA_11x(inst_data);
         ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx);
         if (do_assignability_check && obj_result != nullptr) {
-          PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-          ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */,
-                                                                    pointer_size);
+          ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */);
           // Re-load since it might have moved.
           obj_result = shadow_frame.GetVRegReference(ref_idx);
           if (return_type == nullptr) {
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index 5a3fafa..0148a1c 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -29,6 +29,7 @@
 #include "mirror/object-inl.h"
 #include "nth_caller_visitor.h"
 #include "thread-inl.h"
+#include "thread_list.h"
 
 namespace art {
 
@@ -37,6 +38,8 @@
 static constexpr size_t kMonitorsInitial = 32;  // Arbitrary.
 static constexpr size_t kMonitorsMax = 4096;  // Arbitrary sanity check.
 
+const JNINativeInterface* JNIEnvExt::table_override_ = nullptr;
+
 // Checking "locals" requires the mutator lock, but at creation time we're really only interested
 // in validity, which isn't changing. To avoid grabbing the mutator lock, factored out and tagged
 // with NO_THREAD_SAFETY_ANALYSIS.
@@ -78,10 +81,10 @@
       runtime_deleted(false),
       critical(0),
       monitors("monitors", kMonitorsInitial, kMonitorsMax) {
-  functions = unchecked_functions = GetJniNativeInterface();
-  if (vm->IsCheckJniEnabled()) {
-    SetCheckJniEnabled(true);
-  }
+  MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_);
+  check_jni = vm->IsCheckJniEnabled();
+  functions = GetFunctionTable(check_jni);
+  unchecked_functions = GetJniNativeInterface();
 }
 
 void JNIEnvExt::SetFunctionsToRuntimeShutdownFunctions() {
@@ -107,7 +110,12 @@
 
 void JNIEnvExt::SetCheckJniEnabled(bool enabled) {
   check_jni = enabled;
-  functions = enabled ? GetCheckJniNativeInterface() : GetJniNativeInterface();
+  MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_);
+  functions = GetFunctionTable(enabled);
+  // Check whether this is a no-op because of override.
+  if (enabled && JNIEnvExt::table_override_ != nullptr) {
+    LOG(WARNING) << "Enabling CheckJNI after a JNIEnv function table override is not functional.";
+  }
 }
 
 void JNIEnvExt::DumpReferenceTables(std::ostream& os) {
@@ -269,4 +277,33 @@
   }
 }
 
+static void ThreadResetFunctionTable(Thread* thread, void* arg ATTRIBUTE_UNUSED)
+    REQUIRES(Locks::jni_function_table_lock_) {
+  JNIEnvExt* env = thread->GetJniEnv();
+  bool check_jni = env->check_jni;
+  env->functions = JNIEnvExt::GetFunctionTable(check_jni);
+}
+
+void JNIEnvExt::SetTableOverride(const JNINativeInterface* table_override) {
+  MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+  MutexLock mu2(Thread::Current(), *Locks::jni_function_table_lock_);
+
+  JNIEnvExt::table_override_ = table_override;
+
+  // See if we have a runtime. Note: we cannot run other code (like JavaVMExt's CheckJNI install
+  // code), as we'd have to recursively lock the mutex.
+  Runtime* runtime = Runtime::Current();
+  if (runtime != nullptr) {
+    runtime->GetThreadList()->ForEach(ThreadResetFunctionTable, nullptr);
+  }
+}
+
+const JNINativeInterface* JNIEnvExt::GetFunctionTable(bool check_jni) {
+  const JNINativeInterface* override = JNIEnvExt::table_override_;
+  if (override != nullptr) {
+    return override;
+  }
+  return check_jni ? GetCheckJniNativeInterface() : GetJniNativeInterface();
+}
+
 }  // namespace art
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
index 5cca0ae..4004c45 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -43,7 +43,7 @@
   void DumpReferenceTables(std::ostream& os)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  void SetCheckJniEnabled(bool enabled);
+  void SetCheckJniEnabled(bool enabled) REQUIRES(!Locks::jni_function_table_lock_);
 
   void PushFrame(int capacity) REQUIRES_SHARED(Locks::mutator_lock_);
   void PopFrame() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -104,10 +104,27 @@
   // Set the functions to the runtime shutdown functions.
   void SetFunctionsToRuntimeShutdownFunctions();
 
+  // Set the function table override. This will install the override (or original table, if null)
+  // to all threads.
+  // Note: JNI function table overrides are sensitive to the order of operations wrt/ CheckJNI.
+  //       After overriding the JNI function table, CheckJNI toggling is ignored.
+  static void SetTableOverride(const JNINativeInterface* table_override)
+      REQUIRES(!Locks::thread_list_lock_, !Locks::jni_function_table_lock_);
+
+  // Return either the regular, or the CheckJNI function table. Will return table_override_ instead
+  // if it is not null.
+  static const JNINativeInterface* GetFunctionTable(bool check_jni)
+      REQUIRES(Locks::jni_function_table_lock_);
+
  private:
+  // Override of function tables. This applies to both default as well as instrumented (CheckJNI)
+  // function tables.
+  static const JNINativeInterface* table_override_ GUARDED_BY(Locks::jni_function_table_lock_);
+
   // The constructor should not be called directly. It may leave the object in an erroneous state,
   // and the result needs to be checked.
-  JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg);
+  JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg)
+      REQUIRES(!Locks::jni_function_table_lock_);
 
   // All locked objects, with the (Java caller) stack frame that locked them. Used in CheckJNI
   // to ensure that only monitors locked in this native frame are being unlocked, and that at
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 4da5e23..08d1eeb 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -2346,4 +2346,39 @@
   EXPECT_EQ(segment_state_now, segment_state_computed);
 }
 
+static size_t gGlobalRefCount = 0;
+static const JNINativeInterface* gOriginalEnv = nullptr;
+
+static jobject CountNewGlobalRef(JNIEnv* env, jobject o) {
+  ++gGlobalRefCount;
+  return gOriginalEnv->NewGlobalRef(env, o);
+}
+
+// Test the table override.
+TEST_F(JniInternalTest, JNIEnvExtTableOverride) {
+  JNINativeInterface env_override;
+  memcpy(&env_override, env_->functions, sizeof(JNINativeInterface));
+
+  gOriginalEnv = env_->functions;
+  env_override.NewGlobalRef = CountNewGlobalRef;
+  gGlobalRefCount = 0;
+
+  jclass local = env_->FindClass("java/lang/Object");
+  ASSERT_TRUE(local != nullptr);
+
+  // Set the table, add a global ref, see whether the counter increases.
+  JNIEnvExt::SetTableOverride(&env_override);
+
+  jobject global = env_->NewGlobalRef(local);
+  EXPECT_EQ(1u, gGlobalRefCount);
+  env_->DeleteGlobalRef(global);
+
+  // Reset
+  JNIEnvExt::SetTableOverride(nullptr);
+
+  jobject global2 = env_->NewGlobalRef(local);
+  EXPECT_EQ(1u, gGlobalRefCount);
+  env_->DeleteGlobalRef(global2);
+}
+
 }  // namespace art
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index a6f56ae..6a4ec9d 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -306,23 +306,6 @@
 }
 
 
-TEST_F(ObjectTest, CheckAndAllocArrayFromCode) {
-  // pretend we are trying to call 'new char[3]' from String.toCharArray
-  ScopedObjectAccess soa(Thread::Current());
-  Class* java_util_Arrays = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/Arrays;");
-  ArtMethod* sort = java_util_Arrays->FindDirectMethod("sort", "([I)V", kRuntimePointerSize);
-  const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId("[I");
-  ASSERT_TRUE(type_id != nullptr);
-  dex::TypeIndex type_idx = java_lang_dex_file_->GetIndexForTypeId(*type_id);
-  Object* array = CheckAndAllocArrayFromCodeInstrumented(
-      type_idx, 3, sort, Thread::Current(), false,
-      Runtime::Current()->GetHeap()->GetCurrentAllocator());
-  EXPECT_TRUE(array->IsArrayInstance());
-  EXPECT_EQ(3, array->AsArray()->GetLength());
-  EXPECT_TRUE(array->GetClass()->IsArrayClass());
-  EXPECT_TRUE(array->GetClass()->GetComponentType()->IsPrimitive());
-}
-
 TEST_F(ObjectTest, CreateMultiArray) {
   ScopedObjectAccess soa(Thread::Current());
 
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 1a07cdc..d14b399 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -473,6 +473,10 @@
   return IsKeyEnabled(OatHeader::kDebuggableKey);
 }
 
+bool OatHeader::IsConcurrentCopying() const {
+  return IsKeyEnabled(OatHeader::kConcurrentCopying);
+}
+
 bool OatHeader::IsNativeDebuggable() const {
   return IsKeyEnabled(OatHeader::kNativeDebuggableKey);
 }
diff --git a/runtime/oat.h b/runtime/oat.h
index ab03252..f9396ae 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[] = { '0', '9', '8', '\0' };  // art::Thread fields reorder
+  static constexpr uint8_t kOatVersion[] = { '0', '9', '9', '\0' };  // Remove old entrypoints
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
@@ -43,6 +43,7 @@
   static constexpr const char* kCompilerFilter = "compiler-filter";
   static constexpr const char* kClassPathKey = "classpath";
   static constexpr const char* kBootClassPathKey = "bootclasspath";
+  static constexpr const char* kConcurrentCopying = "concurrent-copying";
 
   static constexpr const char kTrueValue[] = "true";
   static constexpr const char kFalseValue[] = "false";
@@ -112,6 +113,7 @@
   bool IsDebuggable() const;
   bool IsNativeDebuggable() const;
   CompilerFilter::Filter GetCompilerFilter() const;
+  bool IsConcurrentCopying() const;
 
  private:
   bool KeyHasValue(const char* key, const char* value, size_t value_size) const;
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index f12a5e7..8554fa2 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -304,6 +304,13 @@
 }
 
 OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) {
+  // Verify the ART_USE_READ_BARRIER state.
+  const bool is_cc = file.GetOatHeader().IsConcurrentCopying();
+  constexpr bool kRuntimeIsCC = kUseReadBarrier;
+  if (is_cc != kRuntimeIsCC) {
+    return kOatCannotOpen;
+  }
+
   // Verify the dex checksum.
   // Note: GetOatDexFile will return null if the dex checksum doesn't match
   // what we provide, which verifies the primary dex checksum for us.
@@ -903,4 +910,3 @@
   return std::unique_ptr<OatFile>();
 }
 }  // namespace art
-
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index af027f6..d5c6520 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -23,10 +23,12 @@
            "ti_class.cc",
            "ti_field.cc",
            "ti_heap.cc",
+           "ti_jni.cc",
            "ti_method.cc",
            "ti_monitor.cc",
            "ti_object.cc",
            "ti_properties.cc",
+           "ti_search.cc",
            "ti_stack.cc",
            "ti_redefine.cc",
            "ti_thread.cc",
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index d9aea01..90467db 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -51,11 +51,13 @@
 #include "ti_class.h"
 #include "ti_field.h"
 #include "ti_heap.h"
+#include "ti_jni.h"
 #include "ti_method.h"
 #include "ti_monitor.h"
 #include "ti_object.h"
 #include "ti_properties.h"
 #include "ti_redefine.h"
+#include "ti_search.h"
 #include "ti_stack.h"
 #include "ti_thread.h"
 #include "ti_threadgroup.h"
@@ -801,11 +803,11 @@
   }
 
   static jvmtiError SetJNIFunctionTable(jvmtiEnv* env, const jniNativeInterface* function_table) {
-    return ERR(NOT_IMPLEMENTED);
+    return JNIUtil::SetJNIFunctionTable(env, function_table);
   }
 
   static jvmtiError GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table) {
-    return ERR(NOT_IMPLEMENTED);
+    return JNIUtil::GetJNIFunctionTable(env, function_table);
   }
 
   // TODO: This will require locking, so that an agent can't remove callbacks when we're dispatching
@@ -856,7 +858,8 @@
       }
     }
 
-    return gEventHandler.SetEvent(ArtJvmTiEnv::AsArtJvmTiEnv(env), art_thread, event_type, mode);
+    ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env);
+    return gEventHandler.SetEvent(art_env, art_thread, GetArtJvmtiEvent(art_env, event_type), mode);
   }
 
   static jvmtiError GenerateEvents(jvmtiEnv* env, jvmtiEvent event_type) {
@@ -902,11 +905,15 @@
     ENSURE_NON_NULL(capabilities_ptr);
     ArtJvmTiEnv* art_env = static_cast<ArtJvmTiEnv*>(env);
     jvmtiError ret = OK;
+    jvmtiCapabilities changed;
 #define ADD_CAPABILITY(e) \
     do { \
       if (capabilities_ptr->e == 1) { \
         if (kPotentialCapabilities.e == 1) { \
-          art_env->capabilities.e = 1;\
+          if (art_env->capabilities.e != 1) { \
+            art_env->capabilities.e = 1; \
+            changed.e = 1; \
+          }\
         } else { \
           ret = ERR(NOT_AVAILABLE); \
         } \
@@ -955,6 +962,9 @@
     ADD_CAPABILITY(can_generate_resource_exhaustion_heap_events);
     ADD_CAPABILITY(can_generate_resource_exhaustion_threads_events);
 #undef ADD_CAPABILITY
+    gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env),
+                                            changed,
+                                            /*added*/true);
     return ret;
   }
 
@@ -963,10 +973,14 @@
     ENSURE_VALID_ENV(env);
     ENSURE_NON_NULL(capabilities_ptr);
     ArtJvmTiEnv* art_env = reinterpret_cast<ArtJvmTiEnv*>(env);
+    jvmtiCapabilities changed;
 #define DEL_CAPABILITY(e) \
     do { \
       if (capabilities_ptr->e == 1) { \
-        art_env->capabilities.e = 0;\
+        if (art_env->capabilities.e == 1) { \
+          art_env->capabilities.e = 0;\
+          changed.e = 1; \
+        } \
       } \
     } while (false)
 
@@ -1012,6 +1026,9 @@
     DEL_CAPABILITY(can_generate_resource_exhaustion_heap_events);
     DEL_CAPABILITY(can_generate_resource_exhaustion_threads_events);
 #undef DEL_CAPABILITY
+    gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env),
+                                            changed,
+                                            /*added*/false);
     return OK;
   }
 
@@ -1052,11 +1069,11 @@
   }
 
   static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment) {
-    return ERR(NOT_IMPLEMENTED);
+    return SearchUtil::AddToBootstrapClassLoaderSearch(env, segment);
   }
 
   static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment) {
-    return ERR(NOT_IMPLEMENTED);
+    return SearchUtil::AddToSystemClassLoaderSearch(env, segment);
   }
 
   static jvmtiError GetSystemProperties(jvmtiEnv* env, jint* count_ptr, char*** property_ptr) {
@@ -1231,7 +1248,12 @@
   }
 
   static jvmtiError GetJLocationFormat(jvmtiEnv* env, jvmtiJlocationFormat* format_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    // Report BCI as jlocation format. We report dex bytecode indices.
+    if (format_ptr == nullptr) {
+      return ERR(NULL_POINTER);
+    }
+    *format_ptr = jvmtiJlocationFormat::JVMTI_JLOCATION_JVMBCI;
+    return ERR(NONE);
   }
 
   // TODO Remove this once events are working.
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index d027201..1e07bc6 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -17,14 +17,28 @@
 #ifndef ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
 #define ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
 
+#include <array>
+
 #include "events.h"
 
 #include "art_jvmti.h"
 
 namespace openjdkjvmti {
 
+static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) {
+  if (UNLIKELY(e == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) {
+    if (env->capabilities.can_retransform_classes) {
+      return ArtJvmtiEvent::kClassFileLoadHookRetransformable;
+    } else {
+      return ArtJvmtiEvent::kClassFileLoadHookNonRetransformable;
+    }
+  } else {
+    return static_cast<ArtJvmtiEvent>(e);
+  }
+}
+
 template <typename FnType>
-ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, jvmtiEvent event) {
+ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, ArtJvmtiEvent event) {
   if (env->event_callbacks == nullptr) {
     return nullptr;
   }
@@ -33,84 +47,80 @@
   //       function.
 
   switch (event) {
-    case JVMTI_EVENT_VM_INIT:
+    case ArtJvmtiEvent::kVmInit:
       return reinterpret_cast<FnType*>(env->event_callbacks->VMInit);
-    case JVMTI_EVENT_VM_DEATH:
+    case ArtJvmtiEvent::kVmDeath:
       return reinterpret_cast<FnType*>(env->event_callbacks->VMDeath);
-    case JVMTI_EVENT_THREAD_START:
+    case ArtJvmtiEvent::kThreadStart:
       return reinterpret_cast<FnType*>(env->event_callbacks->ThreadStart);
-    case JVMTI_EVENT_THREAD_END:
+    case ArtJvmtiEvent::kThreadEnd:
       return reinterpret_cast<FnType*>(env->event_callbacks->ThreadEnd);
-    case JVMTI_EVENT_CLASS_FILE_LOAD_HOOK:
+    case ArtJvmtiEvent::kClassFileLoadHookRetransformable:
+    case ArtJvmtiEvent::kClassFileLoadHookNonRetransformable:
       return reinterpret_cast<FnType*>(env->event_callbacks->ClassFileLoadHook);
-    case JVMTI_EVENT_CLASS_LOAD:
+    case ArtJvmtiEvent::kClassLoad:
       return reinterpret_cast<FnType*>(env->event_callbacks->ClassLoad);
-    case JVMTI_EVENT_CLASS_PREPARE:
+    case ArtJvmtiEvent::kClassPrepare:
       return reinterpret_cast<FnType*>(env->event_callbacks->ClassPrepare);
-    case JVMTI_EVENT_VM_START:
+    case ArtJvmtiEvent::kVmStart:
       return reinterpret_cast<FnType*>(env->event_callbacks->VMStart);
-    case JVMTI_EVENT_EXCEPTION:
+    case ArtJvmtiEvent::kException:
       return reinterpret_cast<FnType*>(env->event_callbacks->Exception);
-    case JVMTI_EVENT_EXCEPTION_CATCH:
+    case ArtJvmtiEvent::kExceptionCatch:
       return reinterpret_cast<FnType*>(env->event_callbacks->ExceptionCatch);
-    case JVMTI_EVENT_SINGLE_STEP:
+    case ArtJvmtiEvent::kSingleStep:
       return reinterpret_cast<FnType*>(env->event_callbacks->SingleStep);
-    case JVMTI_EVENT_FRAME_POP:
+    case ArtJvmtiEvent::kFramePop:
       return reinterpret_cast<FnType*>(env->event_callbacks->FramePop);
-    case JVMTI_EVENT_BREAKPOINT:
+    case ArtJvmtiEvent::kBreakpoint:
       return reinterpret_cast<FnType*>(env->event_callbacks->Breakpoint);
-    case JVMTI_EVENT_FIELD_ACCESS:
+    case ArtJvmtiEvent::kFieldAccess:
       return reinterpret_cast<FnType*>(env->event_callbacks->FieldAccess);
-    case JVMTI_EVENT_FIELD_MODIFICATION:
+    case ArtJvmtiEvent::kFieldModification:
       return reinterpret_cast<FnType*>(env->event_callbacks->FieldModification);
-    case JVMTI_EVENT_METHOD_ENTRY:
+    case ArtJvmtiEvent::kMethodEntry:
       return reinterpret_cast<FnType*>(env->event_callbacks->MethodEntry);
-    case JVMTI_EVENT_METHOD_EXIT:
+    case ArtJvmtiEvent::kMethodExit:
       return reinterpret_cast<FnType*>(env->event_callbacks->MethodExit);
-    case JVMTI_EVENT_NATIVE_METHOD_BIND:
+    case ArtJvmtiEvent::kNativeMethodBind:
       return reinterpret_cast<FnType*>(env->event_callbacks->NativeMethodBind);
-    case JVMTI_EVENT_COMPILED_METHOD_LOAD:
+    case ArtJvmtiEvent::kCompiledMethodLoad:
       return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodLoad);
-    case JVMTI_EVENT_COMPILED_METHOD_UNLOAD:
+    case ArtJvmtiEvent::kCompiledMethodUnload:
       return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodUnload);
-    case JVMTI_EVENT_DYNAMIC_CODE_GENERATED:
+    case ArtJvmtiEvent::kDynamicCodeGenerated:
       return reinterpret_cast<FnType*>(env->event_callbacks->DynamicCodeGenerated);
-    case JVMTI_EVENT_DATA_DUMP_REQUEST:
+    case ArtJvmtiEvent::kDataDumpRequest:
       return reinterpret_cast<FnType*>(env->event_callbacks->DataDumpRequest);
-    case JVMTI_EVENT_MONITOR_WAIT:
+    case ArtJvmtiEvent::kMonitorWait:
       return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWait);
-    case JVMTI_EVENT_MONITOR_WAITED:
+    case ArtJvmtiEvent::kMonitorWaited:
       return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWaited);
-    case JVMTI_EVENT_MONITOR_CONTENDED_ENTER:
+    case ArtJvmtiEvent::kMonitorContendedEnter:
       return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEnter);
-    case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED:
+    case ArtJvmtiEvent::kMonitorContendedEntered:
       return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEntered);
-    case JVMTI_EVENT_RESOURCE_EXHAUSTED:
+    case ArtJvmtiEvent::kResourceExhausted:
       return reinterpret_cast<FnType*>(env->event_callbacks->ResourceExhausted);
-    case JVMTI_EVENT_GARBAGE_COLLECTION_START:
+    case ArtJvmtiEvent::kGarbageCollectionStart:
       return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionStart);
-    case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
+    case ArtJvmtiEvent::kGarbageCollectionFinish:
       return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionFinish);
-    case JVMTI_EVENT_OBJECT_FREE:
+    case ArtJvmtiEvent::kObjectFree:
       return reinterpret_cast<FnType*>(env->event_callbacks->ObjectFree);
-    case JVMTI_EVENT_VM_OBJECT_ALLOC:
+    case ArtJvmtiEvent::kVmObjectAlloc:
       return reinterpret_cast<FnType*>(env->event_callbacks->VMObjectAlloc);
   }
   return nullptr;
 }
 
 template <typename ...Args>
-inline void EventHandler::DispatchEvent(art::Thread* thread, jvmtiEvent event, Args... args) {
+inline void EventHandler::DispatchEvent(art::Thread* thread,
+                                        ArtJvmtiEvent event,
+                                        Args... args) const {
   using FnType = void(jvmtiEnv*, Args...);
   for (ArtJvmTiEnv* env : envs) {
-    bool dispatch = env->event_masks.global_event_mask.Test(event);
-
-    if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) {
-      EventMask* mask = env->event_masks.GetEventMaskOrNull(thread);
-      dispatch = mask != nullptr && mask->Test(event);
-    }
-
-    if (dispatch) {
+    if (ShouldDispatch(event, env, thread)) {
       FnType* callback = GetCallback<FnType>(env, event);
       if (callback != nullptr) {
         (*callback)(env, args...);
@@ -119,6 +129,52 @@
   }
 }
 
+inline bool EventHandler::ShouldDispatch(ArtJvmtiEvent event,
+                                         ArtJvmTiEnv* env,
+                                         art::Thread* thread) {
+  bool dispatch = env->event_masks.global_event_mask.Test(event);
+
+  if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) {
+    EventMask* mask = env->event_masks.GetEventMaskOrNull(thread);
+    dispatch = mask != nullptr && mask->Test(event);
+  }
+  return dispatch;
+}
+
+inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) {
+  bool union_value = false;
+  for (const ArtJvmTiEnv* stored_env : envs) {
+    union_value |= stored_env->event_masks.global_event_mask.Test(event);
+    union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event);
+    if (union_value) {
+      break;
+    }
+  }
+  global_mask.Set(event, union_value);
+}
+
+inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env,
+                                           const jvmtiCapabilities& caps,
+                                           bool added) {
+  ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable
+                              : ArtJvmtiEvent::kClassFileLoadHookRetransformable;
+  return caps.can_retransform_classes == 1 &&
+      IsEventEnabledAnywhere(event) &&
+      env->event_masks.IsEnabledAnywhere(event);
+}
+
+inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env,
+                                                    const jvmtiCapabilities& caps,
+                                                    bool added) {
+  if (UNLIKELY(NeedsEventUpdate(env, caps, added))) {
+    env->event_masks.HandleChangedCapabilities(caps, added);
+    if (caps.can_retransform_classes == 1) {
+      RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookRetransformable);
+      RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
+    }
+  }
+}
+
 }  // namespace openjdkjvmti
 
 #endif  // ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 12692a1..f38aa86 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -47,6 +47,10 @@
 
 namespace openjdkjvmti {
 
+bool EventMasks::IsEnabledAnywhere(ArtJvmtiEvent event) {
+  return global_event_mask.Test(event) || unioned_thread_event_mask.Test(event);
+}
+
 EventMask& EventMasks::GetEventMask(art::Thread* thread) {
   if (thread == nullptr) {
     return global_event_mask;
@@ -83,7 +87,7 @@
 }
 
 
-void EventMasks::EnableEvent(art::Thread* thread, jvmtiEvent event) {
+void EventMasks::EnableEvent(art::Thread* thread, ArtJvmtiEvent event) {
   DCHECK(EventMask::EventIsInRange(event));
   GetEventMask(thread).Set(event);
   if (thread != nullptr) {
@@ -91,7 +95,7 @@
   }
 }
 
-void EventMasks::DisableEvent(art::Thread* thread, jvmtiEvent event) {
+void EventMasks::DisableEvent(art::Thread* thread, ArtJvmtiEvent event) {
   DCHECK(EventMask::EventIsInRange(event));
   GetEventMask(thread).Set(event, false);
   if (thread != nullptr) {
@@ -107,20 +111,49 @@
   }
 }
 
+void EventMasks::HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added) {
+  if (UNLIKELY(caps.can_retransform_classes == 1)) {
+    // If we are giving this env the retransform classes cap we need to switch all events of
+    // NonTransformable to Transformable and vice versa.
+    ArtJvmtiEvent to_remove = caps_added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable
+                                         : ArtJvmtiEvent::kClassFileLoadHookRetransformable;
+    ArtJvmtiEvent to_add = caps_added ? ArtJvmtiEvent::kClassFileLoadHookRetransformable
+                                      : ArtJvmtiEvent::kClassFileLoadHookNonRetransformable;
+    if (global_event_mask.Test(to_remove)) {
+      CHECK(!global_event_mask.Test(to_add));
+      global_event_mask.Set(to_remove, false);
+      global_event_mask.Set(to_add, true);
+    }
+
+    if (unioned_thread_event_mask.Test(to_remove)) {
+      CHECK(!unioned_thread_event_mask.Test(to_add));
+      unioned_thread_event_mask.Set(to_remove, false);
+      unioned_thread_event_mask.Set(to_add, true);
+    }
+    for (auto thread_mask : thread_event_masks) {
+      if (thread_mask.second.Test(to_remove)) {
+        CHECK(!thread_mask.second.Test(to_add));
+        thread_mask.second.Set(to_remove, false);
+        thread_mask.second.Set(to_add, true);
+      }
+    }
+  }
+}
+
 void EventHandler::RegisterArtJvmTiEnv(ArtJvmTiEnv* env) {
   envs.push_back(env);
 }
 
-static bool IsThreadControllable(jvmtiEvent event) {
+static bool IsThreadControllable(ArtJvmtiEvent event) {
   switch (event) {
-    case JVMTI_EVENT_VM_INIT:
-    case JVMTI_EVENT_VM_START:
-    case JVMTI_EVENT_VM_DEATH:
-    case JVMTI_EVENT_THREAD_START:
-    case JVMTI_EVENT_COMPILED_METHOD_LOAD:
-    case JVMTI_EVENT_COMPILED_METHOD_UNLOAD:
-    case JVMTI_EVENT_DYNAMIC_CODE_GENERATED:
-    case JVMTI_EVENT_DATA_DUMP_REQUEST:
+    case ArtJvmtiEvent::kVmInit:
+    case ArtJvmtiEvent::kVmStart:
+    case ArtJvmtiEvent::kVmDeath:
+    case ArtJvmtiEvent::kThreadStart:
+    case ArtJvmtiEvent::kCompiledMethodLoad:
+    case ArtJvmtiEvent::kCompiledMethodUnload:
+    case ArtJvmtiEvent::kDynamicCodeGenerated:
+    case ArtJvmtiEvent::kDataDumpRequest:
       return false;
 
     default:
@@ -136,7 +169,7 @@
       OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
     DCHECK_EQ(self, art::Thread::Current());
 
-    if (handler_->IsEventEnabledAnywhere(JVMTI_EVENT_VM_OBJECT_ALLOC)) {
+    if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kVmObjectAlloc)) {
       art::StackHandleScope<1> hs(self);
       auto h = hs.NewHandleWrapper(obj);
       // jvmtiEventVMObjectAlloc parameters:
@@ -162,7 +195,7 @@
           jni_env, jni_env->AddLocalReference<jclass>(obj->Ptr()->GetClass()));
 
       handler_->DispatchEvent(self,
-                              JVMTI_EVENT_VM_OBJECT_ALLOC,
+                              ArtJvmtiEvent::kVmObjectAlloc,
                               jni_env,
                               thread.get(),
                               object.get(),
@@ -196,11 +229,11 @@
         finish_enabled_(false) {}
 
   void StartPause() OVERRIDE {
-    handler_->DispatchEvent(nullptr, JVMTI_EVENT_GARBAGE_COLLECTION_START);
+    handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionStart);
   }
 
   void EndPause() OVERRIDE {
-    handler_->DispatchEvent(nullptr, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH);
+    handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionFinish);
   }
 
   bool IsEnabled() {
@@ -221,10 +254,10 @@
   bool finish_enabled_;
 };
 
-static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, jvmtiEvent event, bool enable) {
+static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, ArtJvmtiEvent event, bool enable) {
   bool old_state = listener->IsEnabled();
 
-  if (event == JVMTI_EVENT_GARBAGE_COLLECTION_START) {
+  if (event == ArtJvmtiEvent::kGarbageCollectionStart) {
     listener->SetStartEnabled(enable);
   } else {
     listener->SetFinishEnabled(enable);
@@ -242,14 +275,14 @@
 }
 
 // Handle special work for the given event type, if necessary.
-void EventHandler::HandleEventType(jvmtiEvent event, bool enable) {
+void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) {
   switch (event) {
-    case JVMTI_EVENT_VM_OBJECT_ALLOC:
+    case ArtJvmtiEvent::kVmObjectAlloc:
       SetupObjectAllocationTracking(alloc_listener_.get(), enable);
       return;
 
-    case JVMTI_EVENT_GARBAGE_COLLECTION_START:
-    case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
+    case ArtJvmtiEvent::kGarbageCollectionStart:
+    case ArtJvmtiEvent::kGarbageCollectionFinish:
       SetupGcPauseTracking(gc_pause_listener_.get(), event, enable);
       return;
 
@@ -260,7 +293,7 @@
 
 jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env,
                                   art::Thread* thread,
-                                  jvmtiEvent event,
+                                  ArtJvmtiEvent event,
                                   jvmtiEventMode mode) {
   if (thread != nullptr) {
     art::ThreadState state = thread->GetState();
@@ -293,17 +326,7 @@
     DCHECK_EQ(mode, JVMTI_DISABLE);
 
     env->event_masks.DisableEvent(thread, event);
-
-    // Gotta recompute the global mask.
-    bool union_value = false;
-    for (const ArtJvmTiEnv* stored_env : envs) {
-      union_value |= stored_env->event_masks.global_event_mask.Test(event);
-      union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event);
-      if (union_value) {
-        break;
-      }
-    }
-    global_mask.Set(event, union_value);
+    RecalculateGlobalEventMask(event);
   }
 
   bool new_state = global_mask.Test(event);
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 07d6bfd..7990141 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -30,22 +30,76 @@
 class JvmtiAllocationListener;
 class JvmtiGcPauseListener;
 
+// an enum for ArtEvents. This differs from the JVMTI events only in that we distinguish between
+// retransformation capable and incapable loading
+enum class ArtJvmtiEvent {
+    kMinEventTypeVal = JVMTI_MIN_EVENT_TYPE_VAL,
+    kVmInit = JVMTI_EVENT_VM_INIT,
+    kVmDeath = JVMTI_EVENT_VM_DEATH,
+    kThreadStart = JVMTI_EVENT_THREAD_START,
+    kThreadEnd = JVMTI_EVENT_THREAD_END,
+    kClassFileLoadHookNonRetransformable = JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
+    kClassLoad = JVMTI_EVENT_CLASS_LOAD,
+    kClassPrepare = JVMTI_EVENT_CLASS_PREPARE,
+    kVmStart = JVMTI_EVENT_VM_START,
+    kException = JVMTI_EVENT_EXCEPTION,
+    kExceptionCatch = JVMTI_EVENT_EXCEPTION_CATCH,
+    kSingleStep = JVMTI_EVENT_SINGLE_STEP,
+    kFramePop = JVMTI_EVENT_FRAME_POP,
+    kBreakpoint = JVMTI_EVENT_BREAKPOINT,
+    kFieldAccess = JVMTI_EVENT_FIELD_ACCESS,
+    kFieldModification = JVMTI_EVENT_FIELD_MODIFICATION,
+    kMethodEntry = JVMTI_EVENT_METHOD_ENTRY,
+    kMethodExit = JVMTI_EVENT_METHOD_EXIT,
+    kNativeMethodBind = JVMTI_EVENT_NATIVE_METHOD_BIND,
+    kCompiledMethodLoad = JVMTI_EVENT_COMPILED_METHOD_LOAD,
+    kCompiledMethodUnload = JVMTI_EVENT_COMPILED_METHOD_UNLOAD,
+    kDynamicCodeGenerated = JVMTI_EVENT_DYNAMIC_CODE_GENERATED,
+    kDataDumpRequest = JVMTI_EVENT_DATA_DUMP_REQUEST,
+    kMonitorWait = JVMTI_EVENT_MONITOR_WAIT,
+    kMonitorWaited = JVMTI_EVENT_MONITOR_WAITED,
+    kMonitorContendedEnter = JVMTI_EVENT_MONITOR_CONTENDED_ENTER,
+    kMonitorContendedEntered = JVMTI_EVENT_MONITOR_CONTENDED_ENTERED,
+    kResourceExhausted = JVMTI_EVENT_RESOURCE_EXHAUSTED,
+    kGarbageCollectionStart = JVMTI_EVENT_GARBAGE_COLLECTION_START,
+    kGarbageCollectionFinish = JVMTI_EVENT_GARBAGE_COLLECTION_FINISH,
+    kObjectFree = JVMTI_EVENT_OBJECT_FREE,
+    kVmObjectAlloc = JVMTI_EVENT_VM_OBJECT_ALLOC,
+    kClassFileLoadHookRetransformable = JVMTI_MAX_EVENT_TYPE_VAL + 1,
+    kMaxEventTypeVal = kClassFileLoadHookRetransformable,
+};
+
+// Convert a jvmtiEvent into a ArtJvmtiEvent
+ALWAYS_INLINE static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e);
+
+static inline jvmtiEvent GetJvmtiEvent(ArtJvmtiEvent e) {
+  if (UNLIKELY(e == ArtJvmtiEvent::kClassFileLoadHookRetransformable)) {
+    return JVMTI_EVENT_CLASS_FILE_LOAD_HOOK;
+  } else {
+    return static_cast<jvmtiEvent>(e);
+  }
+}
+
 struct EventMask {
-  static constexpr size_t kEventsSize = JVMTI_MAX_EVENT_TYPE_VAL - JVMTI_MIN_EVENT_TYPE_VAL + 1;
+  static constexpr size_t kEventsSize =
+      static_cast<size_t>(ArtJvmtiEvent::kMaxEventTypeVal) -
+      static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal) + 1;
   std::bitset<kEventsSize> bit_set;
 
-  static bool EventIsInRange(jvmtiEvent event) {
-    return event >= JVMTI_MIN_EVENT_TYPE_VAL && event <= JVMTI_MAX_EVENT_TYPE_VAL;
+  static bool EventIsInRange(ArtJvmtiEvent event) {
+    return event >= ArtJvmtiEvent::kMinEventTypeVal && event <= ArtJvmtiEvent::kMaxEventTypeVal;
   }
 
-  void Set(jvmtiEvent event, bool value = true) {
+  void Set(ArtJvmtiEvent event, bool value = true) {
     DCHECK(EventIsInRange(event));
-    bit_set.set(event - JVMTI_MIN_EVENT_TYPE_VAL, value);
+    bit_set.set(static_cast<size_t>(event) - static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal),
+                value);
   }
 
-  bool Test(jvmtiEvent event) const {
+  bool Test(ArtJvmtiEvent event) const {
     DCHECK(EventIsInRange(event));
-    return bit_set.test(event - JVMTI_MIN_EVENT_TYPE_VAL);
+    return bit_set.test(
+        static_cast<size_t>(event) - static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal));
   }
 };
 
@@ -68,8 +122,13 @@
 
   EventMask& GetEventMask(art::Thread* thread);
   EventMask* GetEventMaskOrNull(art::Thread* thread);
-  void EnableEvent(art::Thread* thread, jvmtiEvent event);
-  void DisableEvent(art::Thread* thread, jvmtiEvent event);
+  void EnableEvent(art::Thread* thread, ArtJvmtiEvent event);
+  void DisableEvent(art::Thread* thread, ArtJvmtiEvent event);
+  bool IsEnabledAnywhere(ArtJvmtiEvent event);
+  // Make any changes to event masks needed for the given capability changes. If caps_added is true
+  // then caps is all the newly set capabilities of the jvmtiEnv. If it is false then caps is the
+  // set of all capabilities that were removed from the jvmtiEnv.
+  void HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added);
 };
 
 // Helper class for event handling.
@@ -82,20 +141,44 @@
   // enabled, yet.
   void RegisterArtJvmTiEnv(ArtJvmTiEnv* env);
 
-  bool IsEventEnabledAnywhere(jvmtiEvent event) {
+  bool IsEventEnabledAnywhere(ArtJvmtiEvent event) const {
     if (!EventMask::EventIsInRange(event)) {
       return false;
     }
     return global_mask.Test(event);
   }
 
-  jvmtiError SetEvent(ArtJvmTiEnv* env, art::Thread* thread, jvmtiEvent event, jvmtiEventMode mode);
+  jvmtiError SetEvent(ArtJvmTiEnv* env,
+                      art::Thread* thread,
+                      ArtJvmtiEvent event,
+                      jvmtiEventMode mode);
 
   template <typename ...Args>
-  ALWAYS_INLINE inline void DispatchEvent(art::Thread* thread, jvmtiEvent event, Args... args);
+  ALWAYS_INLINE
+  inline void DispatchEvent(art::Thread* thread, ArtJvmtiEvent event, Args... args) const;
+
+  // Tell the event handler capabilities were added/lost so it can adjust the sent events.If
+  // caps_added is true then caps is all the newly set capabilities of the jvmtiEnv. If it is false
+  // then caps is the set of all capabilities that were removed from the jvmtiEnv.
+  ALWAYS_INLINE
+  inline void HandleChangedCapabilities(ArtJvmTiEnv* env,
+                                        const jvmtiCapabilities& caps,
+                                        bool added);
 
  private:
-  void HandleEventType(jvmtiEvent event, bool enable);
+  ALWAYS_INLINE
+  static inline bool ShouldDispatch(ArtJvmtiEvent event, ArtJvmTiEnv* env, art::Thread* thread);
+
+  ALWAYS_INLINE
+  inline bool NeedsEventUpdate(ArtJvmTiEnv* env,
+                               const jvmtiCapabilities& caps,
+                               bool added);
+
+  // Recalculates the event mask for the given event.
+  ALWAYS_INLINE
+  inline void RecalculateGlobalEventMask(ArtJvmtiEvent event);
+
+  void HandleEventType(ArtJvmtiEvent event, bool enable);
 
   // List of all JvmTiEnv objects that have been created, in their creation order.
   std::vector<ArtJvmTiEnv*> envs;
diff --git a/runtime/openjdkjvmti/jvmti.h b/runtime/openjdkjvmti/jvmti.h
index ee708cb..de07c16 100644
--- a/runtime/openjdkjvmti/jvmti.h
+++ b/runtime/openjdkjvmti/jvmti.h
@@ -74,7 +74,7 @@
 typedef jlong jlocation;
 struct _jrawMonitorID;
 typedef struct _jrawMonitorID *jrawMonitorID;
-typedef struct JNINativeInterface_ jniNativeInterface;
+typedef struct JNINativeInterface jniNativeInterface;
 
     /* Constants */
 
diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc
index b983e79..94cb46a 100644
--- a/runtime/openjdkjvmti/object_tagging.cc
+++ b/runtime/openjdkjvmti/object_tagging.cc
@@ -177,7 +177,7 @@
 }
 
 void ObjectTagTable::Sweep(art::IsMarkedVisitor* visitor) {
-  if (event_handler_->IsEventEnabledAnywhere(JVMTI_EVENT_OBJECT_FREE)) {
+  if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kObjectFree)) {
     SweepImpl<true>(visitor);
   } else {
     SweepImpl<false>(visitor);
@@ -207,7 +207,7 @@
 }
 
 void ObjectTagTable::HandleNullSweep(jlong tag) {
-  event_handler_->DispatchEvent(nullptr, JVMTI_EVENT_OBJECT_FREE, tag);
+  event_handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kObjectFree, tag);
 }
 
 template <typename T, ObjectTagTable::TableUpdateNullTarget kTargetNull>
diff --git a/runtime/openjdkjvmti/ti_jni.cc b/runtime/openjdkjvmti/ti_jni.cc
new file mode 100644
index 0000000..88f0395
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_jni.cc
@@ -0,0 +1,91 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_jni.h"
+
+#include "jni.h"
+
+#include "art_jvmti.h"
+#include "base/mutex.h"
+#include "java_vm_ext.h"
+#include "jni_env_ext.h"
+#include "runtime.h"
+#include "thread-inl.h"
+
+namespace openjdkjvmti {
+
+jvmtiError JNIUtil::SetJNIFunctionTable(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                        const jniNativeInterface* function_table) {
+  // While we supporting setting null (which will reset the table), the spec says no.
+  if (function_table == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::JNIEnvExt::SetTableOverride(function_table);
+  return ERR(NONE);
+}
+
+jvmtiError JNIUtil::GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table) {
+  if (function_table == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  // We use the generic JNIEnvExt::GetFunctionTable instead of querying a specific JNIEnv, as
+  // this has to work in the start phase.
+
+  // Figure out which table is current. Conservatively assume check-jni is off.
+  bool check_jni = false;
+  art::Runtime* runtime = art::Runtime::Current();
+  if (runtime != nullptr && runtime->GetJavaVM() != nullptr) {
+    check_jni = runtime->GetJavaVM()->IsCheckJniEnabled();
+  }
+
+  // Get that table.
+  const JNINativeInterface* current_table;
+  {
+    art::MutexLock mu(art::Thread::Current(), *art::Locks::jni_function_table_lock_);
+    current_table = art::JNIEnvExt::GetFunctionTable(check_jni);
+  }
+
+  // Allocate memory and copy the table.
+  unsigned char* data;
+  jvmtiError data_result = env->Allocate(sizeof(JNINativeInterface), &data);
+  if (data_result != ERR(NONE)) {
+    return data_result;
+  }
+  memcpy(data, current_table, sizeof(JNINativeInterface));
+
+  *function_table = reinterpret_cast<JNINativeInterface*>(data);
+
+  return ERR(NONE);
+}
+
+}  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_jni.h b/runtime/openjdkjvmti/ti_jni.h
new file mode 100644
index 0000000..906aab0
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_jni.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+// Note: Currently, JNI function table changes are sensitive to the order of operations wrt/
+//       CheckJNI. If an agent sets the function table, and a program than late-enables CheckJNI,
+//       CheckJNI will not be working (as the agent will forward to the non-CheckJNI table).
+//
+//       This behavior results from our usage of the function table to avoid a check of the
+//       CheckJNI flag. A future implementation may install on loading of this plugin an
+//       intermediate function table that explicitly checks the flag, so that switching CheckJNI
+//       is transparently handled.
+
+class JNIUtil {
+ public:
+  static jvmtiError SetJNIFunctionTable(jvmtiEnv* env, const jniNativeInterface* function_table);
+
+  static jvmtiError GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table);
+};
+
+}  // namespace openjdkjvmti
+
+#endif  // ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_
diff --git a/runtime/openjdkjvmti/ti_search.cc b/runtime/openjdkjvmti/ti_search.cc
new file mode 100644
index 0000000..913d2b6
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_search.cc
@@ -0,0 +1,122 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_search.h"
+
+#include "jni.h"
+
+#include "art_jvmti.h"
+#include "base/macros.h"
+#include "class_linker.h"
+#include "dex_file.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "ScopedLocalRef.h"
+
+namespace openjdkjvmti {
+
+jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                                       const char* segment) {
+  art::Runtime* current = art::Runtime::Current();
+  if (current == nullptr) {
+    return ERR(WRONG_PHASE);
+  }
+  if (current->GetClassLinker() == nullptr) {
+    // TODO: Support boot classpath change in OnLoad.
+    return ERR(WRONG_PHASE);
+  }
+  if (segment == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  std::string error_msg;
+  std::vector<std::unique_ptr<const art::DexFile>> dex_files;
+  if (!art::DexFile::Open(segment, segment, true, &error_msg, &dex_files)) {
+    LOG(WARNING) << "Could not open " << segment << " for boot classpath extension: " << error_msg;
+    return ERR(ILLEGAL_ARGUMENT);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  for (std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
+    current->GetClassLinker()->AppendToBootClassPath(art::Thread::Current(), *dex_file.release());
+  }
+
+  return ERR(NONE);
+}
+
+jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED,
+                                                    const char* segment) {
+  if (segment == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::Runtime* current = art::Runtime::Current();
+  if (current == nullptr) {
+    return ERR(WRONG_PHASE);
+  }
+  jobject sys_class_loader = current->GetSystemClassLoader();
+  if (sys_class_loader == nullptr) {
+    // TODO: Support classpath change in OnLoad.
+    return ERR(WRONG_PHASE);
+  }
+
+  // We'll use BaseDexClassLoader.addDexPath, as it takes care of array resizing etc. As a downside,
+  // exceptions are swallowed.
+
+  art::Thread* self = art::Thread::Current();
+  JNIEnv* env = self->GetJniEnv();
+  if (!env->IsInstanceOf(sys_class_loader,
+                         art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
+    return ERR(INTERNAL);
+  }
+
+  jmethodID add_dex_path_id = env->GetMethodID(
+      art::WellKnownClasses::dalvik_system_BaseDexClassLoader,
+      "addDexPath",
+      "(Ljava/lang/String;)V");
+  if (add_dex_path_id == nullptr) {
+    return ERR(INTERNAL);
+  }
+
+  ScopedLocalRef<jstring> dex_path(env, env->NewStringUTF(segment));
+  if (dex_path.get() == nullptr) {
+    return ERR(INTERNAL);
+  }
+  env->CallVoidMethod(sys_class_loader, add_dex_path_id, dex_path.get());
+
+  if (env->ExceptionCheck()) {
+    env->ExceptionClear();
+    return ERR(ILLEGAL_ARGUMENT);
+  }
+  return ERR(NONE);
+}
+
+}  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_search.h b/runtime/openjdkjvmti/ti_search.h
new file mode 100644
index 0000000..6a52e80
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_search.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
+
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class SearchUtil {
+ public:
+  static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment);
+
+  static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment);
+};
+
+}  // namespace openjdkjvmti
+
+#endif  // ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 4d24501..75176f9 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -227,11 +227,11 @@
     for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {
       ObjPtr<mirror::Object> arg(args->Get(args_offset));
       if (((shorty_[i] == 'L') && (arg != nullptr)) || ((arg == nullptr && shorty_[i] != 'L'))) {
-        PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+        // Note: The method's parameter's type must have been previously resolved.
         ObjPtr<mirror::Class> dst_class(
             m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_,
-                                     true /* resolve */,
-                                     pointer_size));
+                                     false /* resolve */));
+        DCHECK(dst_class != nullptr) << m->PrettyMethod() << " arg #" << i;
         if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) {
           ThrowIllegalArgumentException(
               StringPrintf("method %s argument %zd has type %s, got %s",
@@ -363,12 +363,9 @@
   }
   // TODO: If args contain object references, it may cause problems.
   Thread* const self = Thread::Current();
-  PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
   for (uint32_t i = 0; i < num_params; i++) {
     dex::TypeIndex type_idx = params->GetTypeItem(i).type_idx_;
-    ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx,
-                                                              true /* resolve*/,
-                                                              pointer_size));
+    ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx, true /* resolve */));
     if (param_type == nullptr) {
       CHECK(self->IsExceptionPending());
       LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: "
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 40b6d73..016a379 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2632,8 +2632,6 @@
   QUICK_ENTRY_POINT_INFO(pAllocObjectResolved)
   QUICK_ENTRY_POINT_INFO(pAllocObjectInitialized)
   QUICK_ENTRY_POINT_INFO(pAllocObjectWithChecks)
-  QUICK_ENTRY_POINT_INFO(pCheckAndAllocArray)
-  QUICK_ENTRY_POINT_INFO(pCheckAndAllocArrayWithAccessCheck)
   QUICK_ENTRY_POINT_INFO(pAllocStringFromBytes)
   QUICK_ENTRY_POINT_INFO(pAllocStringFromChars)
   QUICK_ENTRY_POINT_INFO(pAllocStringFromString)
@@ -2667,10 +2665,7 @@
   QUICK_ENTRY_POINT_INFO(pGet64Static)
   QUICK_ENTRY_POINT_INFO(pGetObjInstance)
   QUICK_ENTRY_POINT_INFO(pGetObjStatic)
-  QUICK_ENTRY_POINT_INFO(pAputObjectWithNullAndBoundCheck)
-  QUICK_ENTRY_POINT_INFO(pAputObjectWithBoundCheck)
   QUICK_ENTRY_POINT_INFO(pAputObject)
-  QUICK_ENTRY_POINT_INFO(pHandleFillArrayData)
   QUICK_ENTRY_POINT_INFO(pJniMethodStart)
   QUICK_ENTRY_POINT_INFO(pJniMethodStartSynchronized)
   QUICK_ENTRY_POINT_INFO(pJniMethodEnd)
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 25a179b..b915457 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2901,9 +2901,7 @@
       ArtMethod* called_method = VerifyInvocationArgs(inst, type, is_range);
       const RegType* return_type = nullptr;
       if (called_method != nullptr) {
-        PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-        mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_,
-                                                                        pointer_size);
+        mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
         if (return_type_class != nullptr) {
           return_type = &FromClass(called_method->GetReturnTypeDescriptor(),
                                    return_type_class,
@@ -2946,9 +2944,7 @@
       } else {
         is_constructor = called_method->IsConstructor();
         return_type_descriptor = called_method->GetReturnTypeDescriptor();
-        PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-        mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_,
-                                                                        pointer_size);
+        mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
         if (return_type_class != nullptr) {
           return_type = &FromClass(return_type_descriptor,
                                    return_type_class,
@@ -5133,9 +5129,7 @@
 const RegType& MethodVerifier::GetMethodReturnType() {
   if (return_type_ == nullptr) {
     if (mirror_method_ != nullptr) {
-      PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-      mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_,
-                                                                       pointer_size);
+      mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_);
       if (return_type_class != nullptr) {
         return_type_ = &FromClass(mirror_method_->GetReturnTypeDescriptor(),
                                   return_type_class,
diff --git a/test/928-jni-table/build b/test/928-jni-table/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/928-jni-table/build
@@ -0,0 +1,17 @@
+#!/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/expected.txt b/test/928-jni-table/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/test/928-jni-table/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/928-jni-table/info.txt b/test/928-jni-table/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/928-jni-table/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/928-jni-table/jni_table.cc b/test/928-jni-table/jni_table.cc
new file mode 100644
index 0000000..5123d3a
--- /dev/null
+++ b/test/928-jni-table/jni_table.cc
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2013 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 <stdio.h>
+
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test927JNITable {
+
+// This test is equivalent to the jni_internal_test JNIEnvExtTableOverride.
+
+static size_t gGlobalRefCount = 0;
+static JNINativeInterface* gOriginalEnv = nullptr;
+
+static jobject CountNewGlobalRef(JNIEnv* env, jobject o) {
+  ++gGlobalRefCount;
+  return gOriginalEnv->NewGlobalRef(env, o);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_doJNITableTest(
+    JNIEnv* env, jclass klass) {
+  // Get the current table, as the delegate.
+  jvmtiError getorig_result = jvmti_env->GetJNIFunctionTable(&gOriginalEnv);
+  if (JvmtiErrorToException(env, getorig_result)) {
+    return;
+  }
+
+  // Get the current table, as the override we'll install.
+  JNINativeInterface* env_override;
+  jvmtiError getoverride_result = jvmti_env->GetJNIFunctionTable(&env_override);
+  if (JvmtiErrorToException(env, getoverride_result)) {
+    return;
+  }
+
+  env_override->NewGlobalRef = CountNewGlobalRef;
+  gGlobalRefCount = 0;
+
+  // Install the override.
+  jvmtiError setoverride_result = jvmti_env->SetJNIFunctionTable(env_override);
+  if (JvmtiErrorToException(env, setoverride_result)) {
+    return;
+  }
+
+  jobject global = env->NewGlobalRef(klass);
+  CHECK_EQ(1u, gGlobalRefCount);
+  env->DeleteGlobalRef(global);
+
+  // Install the "original." There is no real reset.
+  jvmtiError setoverride2_result = jvmti_env->SetJNIFunctionTable(gOriginalEnv);
+  if (JvmtiErrorToException(env, setoverride2_result)) {
+    return;
+  }
+
+  jobject global2 = env->NewGlobalRef(klass);
+  CHECK_EQ(1u, gGlobalRefCount);
+  env->DeleteGlobalRef(global2);
+
+  // Try to install null. Should return NULL_POINTER error.
+  jvmtiError setoverride3_result = jvmti_env->SetJNIFunctionTable(nullptr);
+  if (setoverride3_result != JVMTI_ERROR_NULL_POINTER) {
+    LOG(FATAL) << "Didn't receive NULL_POINTER";
+  }
+
+  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(env_override));
+}
+
+}  // namespace Test927JNITable
+}  // namespace art
diff --git a/test/928-jni-table/run b/test/928-jni-table/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/928-jni-table/run
@@ -0,0 +1,19 @@
+#!/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-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --jvmti
diff --git a/test/928-jni-table/src/Main.java b/test/928-jni-table/src/Main.java
new file mode 100644
index 0000000..b0baea1
--- /dev/null
+++ b/test/928-jni-table/src/Main.java
@@ -0,0 +1,27 @@
+/*
+ * 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 Main {
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[1]);
+
+    doJNITableTest();
+
+    System.out.println("Done");
+  }
+
+  public static native void doJNITableTest();
+}
diff --git a/test/929-search/build b/test/929-search/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/929-search/build
@@ -0,0 +1,17 @@
+#!/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/expected.txt b/test/929-search/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/test/929-search/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/929-search/info.txt b/test/929-search/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/929-search/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/929-search/run b/test/929-search/run
new file mode 100755
index 0000000..0a8d067
--- /dev/null
+++ b/test/929-search/run
@@ -0,0 +1,23 @@
+#!/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.
+
+# This test checks whether dex files can be injected into parent classloaders. App images preload
+# classes, which will make the injection moot. Turn off app images to avoid the issue.
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --jvmti \
+                   --no-app-image
diff --git a/test/929-search/search.cc b/test/929-search/search.cc
new file mode 100644
index 0000000..d1c6984
--- /dev/null
+++ b/test/929-search/search.cc
@@ -0,0 +1,53 @@
+/*
+ * 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 "android-base/stringprintf.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedUtfChars.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test929Search {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_addToBootClassLoader(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring segment) {
+  ScopedUtfChars utf(env, segment);
+  if (utf.c_str() == nullptr) {
+    return;
+  }
+  jvmtiError result = jvmti_env->AddToBootstrapClassLoaderSearch(utf.c_str());
+  JvmtiErrorToException(env, result);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_addToSystemClassLoader(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring segment) {
+  ScopedUtfChars utf(env, segment);
+  if (utf.c_str() == nullptr) {
+    return;
+  }
+  jvmtiError result = jvmti_env->AddToSystemClassLoaderSearch(utf.c_str());
+  JvmtiErrorToException(env, result);
+}
+
+}  // namespace Test929Search
+}  // namespace art
diff --git a/test/929-search/src-ex/A.java b/test/929-search/src-ex/A.java
new file mode 100644
index 0000000..64acb2f
--- /dev/null
+++ b/test/929-search/src-ex/A.java
@@ -0,0 +1,18 @@
+/*
+ * 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 A {
+}
\ No newline at end of file
diff --git a/test/929-search/src/B.java b/test/929-search/src/B.java
new file mode 100644
index 0000000..f1458c3
--- /dev/null
+++ b/test/929-search/src/B.java
@@ -0,0 +1,18 @@
+/*
+ * 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 B {
+}
\ No newline at end of file
diff --git a/test/929-search/src/Main.java b/test/929-search/src/Main.java
new file mode 100644
index 0000000..d253e6f
--- /dev/null
+++ b/test/929-search/src/Main.java
@@ -0,0 +1,54 @@
+/*
+ * 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.Arrays;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[1]);
+
+    doTest();
+  }
+
+  private static void doTest() throws Exception {
+    doTest(true, DEX1, "B");
+    doTest(false, DEX2, "A");
+    System.out.println("Done");
+  }
+
+  private static void doTest(boolean boot, String segment, String className) throws Exception {
+    ClassLoader expectedClassLoader;
+    if (boot) {
+      expectedClassLoader = Object.class.getClassLoader();
+      addToBootClassLoader(segment);
+    } else {
+      expectedClassLoader = ClassLoader.getSystemClassLoader();
+      addToSystemClassLoader(segment);
+    }
+
+    Class<?> c = Class.forName(className);
+    if (c.getClassLoader() != expectedClassLoader) {
+      throw new RuntimeException(className + "(" + boot + "/" + segment + "): " +
+          c.getClassLoader() + " vs " + expectedClassLoader);
+    }
+  }
+
+  private static native void addToBootClassLoader(String s);
+  private static native void addToSystemClassLoader(String s);
+
+  private static final String DEX1 = System.getenv("DEX_LOCATION") + "/929-search.jar";
+  private static final String DEX2 = System.getenv("DEX_LOCATION") + "/929-search-ex.jar";
+}
diff --git a/test/Android.bp b/test/Android.bp
index c551b9d..965d07a 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -267,6 +267,8 @@
         "924-threads/threads.cc",
         "925-threadgroups/threadgroups.cc",
         "927-timers/timers.cc",
+        "928-jni-table/jni_table.cc",
+        "929-search/search.cc",
     ],
     shared_libs: [
         "libbase",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 11d069a..e604c93 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -280,6 +280,7 @@
 # These 9** tests are not supported in current form due to linker
 # restrictions. See b/31681198
 TEST_ART_BROKEN_TARGET_TESTS += \
+  901-hello-ti-agent \
   902-hello-transformation \
   903-hello-tagging \
   904-object-allocation \
@@ -306,6 +307,8 @@
   925-threadgroups \
   926-multi-obsolescence \
   927-timers \
+  928-jni-table \
+  929-search \
 
 ifneq (,$(filter target,$(TARGET_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -466,10 +469,12 @@
   629-vdex-speed
 
 # This test fails without an image.
-# 964 often times out due to the large number of classes it tries to compile.
+# 018, 961, 964 often time out. b/34369284
 TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \
   137-cfi \
   138-duplicate-classes-check \
+  018-stack-overflow \
+  961-default-iface-resolution-gen \
   964-default-iface-init
 
 ifneq (,$(filter no-dex2oat,$(PREBUILD_TYPES)))