Merge "Fix typo in tifast.cc"
diff --git a/build/art.go b/build/art.go
index 59480a0..3dabce3 100644
--- a/build/art.go
+++ b/build/art.go
@@ -278,6 +278,7 @@
 	android.RegisterModuleType("art_cc_test", artTest)
 	android.RegisterModuleType("art_cc_test_library", artTestLibrary)
 	android.RegisterModuleType("art_cc_defaults", artDefaultsFactory)
+	android.RegisterModuleType("libart_cc_defaults", libartDefaultsFactory)
 	android.RegisterModuleType("art_global_defaults", artGlobalDefaultsFactory)
 	android.RegisterModuleType("art_debug_defaults", artDebugDefaultsFactory)
 }
@@ -304,6 +305,33 @@
 	return module
 }
 
+func libartDefaultsFactory() android.Module {
+	c := &codegenProperties{}
+	module := cc.DefaultsFactory(c)
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		codegen(ctx, c, true)
+
+		type props struct {
+		  Target struct {
+		    Android struct {
+		      Shared_libs []string
+		    }
+		  }
+		}
+
+		p := &props{}
+		// TODO: express this in .bp instead b/79671158
+		if !envTrue(ctx, "ART_TARGET_LINUX") {
+		  p.Target.Android.Shared_libs = []string {
+		    "libmetricslogger",
+		  }
+		}
+		ctx.AppendProperties(p)
+	})
+
+	return module
+}
+
 func artLibrary() android.Module {
 	m, _ := cc.NewLibrary(android.HostAndDeviceSupported)
 	module := m.Init()
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index f573337..fb556f4 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -771,7 +771,7 @@
 
 void CodeGenerator::GenerateLoadMethodTypeRuntimeCall(HLoadMethodType* method_type) {
   LocationSummary* locations = method_type->GetLocations();
-  MoveConstant(locations->GetTemp(0), method_type->GetProtoIndex());
+  MoveConstant(locations->GetTemp(0), method_type->GetProtoIndex().index_);
   CheckEntrypointTypes<kQuickResolveMethodType, void*, uint32_t>();
   InvokeRuntime(kQuickResolveMethodType, method_type, method_type->GetDexPc());
 }
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 35a3945..0e20a65 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -1055,7 +1055,7 @@
 bool HInstructionBuilder::BuildInvokePolymorphic(const Instruction& instruction ATTRIBUTE_UNUSED,
                                                  uint32_t dex_pc,
                                                  uint32_t method_idx,
-                                                 uint32_t proto_idx,
+                                                 dex::ProtoIndex proto_idx,
                                                  uint32_t number_of_vreg_arguments,
                                                  bool is_range,
                                                  uint32_t* args,
@@ -1896,17 +1896,17 @@
   }
 }
 
-void HInstructionBuilder::BuildLoadMethodHandle(uint16_t proto_idx, uint32_t dex_pc) {
+void HInstructionBuilder::BuildLoadMethodHandle(uint16_t method_handle_index, uint32_t dex_pc) {
   const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
-  HLoadMethodHandle* load_method_handle =
-      new (allocator_) HLoadMethodHandle(graph_->GetCurrentMethod(), proto_idx, dex_file, dex_pc);
+  HLoadMethodHandle* load_method_handle = new (allocator_) HLoadMethodHandle(
+      graph_->GetCurrentMethod(), method_handle_index, dex_file, dex_pc);
   AppendInstruction(load_method_handle);
 }
 
-void HInstructionBuilder::BuildLoadMethodType(uint16_t proto_idx, uint32_t dex_pc) {
+void HInstructionBuilder::BuildLoadMethodType(dex::ProtoIndex proto_index, uint32_t dex_pc) {
   const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
   HLoadMethodType* load_method_type =
-      new (allocator_) HLoadMethodType(graph_->GetCurrentMethod(), proto_idx, dex_file, dex_pc);
+      new (allocator_) HLoadMethodType(graph_->GetCurrentMethod(), proto_index, dex_file, dex_pc);
   AppendInstruction(load_method_type);
 }
 
@@ -2189,7 +2189,7 @@
 
     case Instruction::INVOKE_POLYMORPHIC: {
       uint16_t method_idx = instruction.VRegB_45cc();
-      uint16_t proto_idx = instruction.VRegH_45cc();
+      dex::ProtoIndex proto_idx(instruction.VRegH_45cc());
       uint32_t number_of_vreg_arguments = instruction.VRegA_45cc();
       uint32_t args[5];
       instruction.GetVarArgs(args);
@@ -2205,7 +2205,7 @@
 
     case Instruction::INVOKE_POLYMORPHIC_RANGE: {
       uint16_t method_idx = instruction.VRegB_4rcc();
-      uint16_t proto_idx = instruction.VRegH_4rcc();
+      dex::ProtoIndex proto_idx(instruction.VRegH_4rcc());
       uint32_t number_of_vreg_arguments = instruction.VRegA_4rcc();
       uint32_t register_index = instruction.VRegC_4rcc();
       return BuildInvokePolymorphic(instruction,
@@ -2949,7 +2949,7 @@
     }
 
     case Instruction::CONST_METHOD_TYPE: {
-      uint16_t proto_idx = instruction.VRegB_21c();
+      dex::ProtoIndex proto_idx(instruction.VRegB_21c());
       BuildLoadMethodType(proto_idx, dex_pc);
       UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
       break;
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index 95ffa6b..9d886a8 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -178,7 +178,7 @@
   bool BuildInvokePolymorphic(const Instruction& instruction,
                               uint32_t dex_pc,
                               uint32_t method_idx,
-                              uint32_t proto_idx,
+                              dex::ProtoIndex proto_idx,
                               uint32_t number_of_vreg_arguments,
                               bool is_range,
                               uint32_t* args,
@@ -240,11 +240,11 @@
   bool LoadClassNeedsAccessCheck(Handle<mirror::Class> klass)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Builds a `HLoadMethodHandle` loading the given `method_handle_idx`.
+  // Builds a `HLoadMethodHandle` loading the given `method_handle_index`.
   void BuildLoadMethodHandle(uint16_t method_handle_idx, uint32_t dex_pc);
 
-  // Builds a `HLoadMethodType` loading the given `proto_idx`.
-  void BuildLoadMethodType(uint16_t proto_idx, uint32_t dex_pc);
+  // Builds a `HLoadMethodType` loading the given `proto_index`.
+  void BuildLoadMethodType(dex::ProtoIndex proto_index, uint32_t dex_pc);
 
   // Returns the outer-most compiling method's class.
   ObjPtr<mirror::Class> GetOutermostCompilingClass() const;
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index a7c2d0b..e786502 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -6548,7 +6548,7 @@
 class HLoadMethodType FINAL : public HInstruction {
  public:
   HLoadMethodType(HCurrentMethod* current_method,
-                  uint16_t proto_idx,
+                  dex::ProtoIndex proto_index,
                   const DexFile& dex_file,
                   uint32_t dex_pc)
       : HInstruction(kLoadMethodType,
@@ -6556,7 +6556,7 @@
                      SideEffectsForArchRuntimeCalls(),
                      dex_pc),
         special_input_(HUserRecord<HInstruction*>(current_method)),
-        proto_idx_(proto_idx),
+        proto_index_(proto_index),
         dex_file_(dex_file) {
   }
 
@@ -6568,7 +6568,7 @@
 
   bool IsClonable() const OVERRIDE { return true; }
 
-  uint16_t GetProtoIndex() const { return proto_idx_; }
+  dex::ProtoIndex GetProtoIndex() const { return proto_index_; }
 
   const DexFile& GetDexFile() const { return dex_file_; }
 
@@ -6585,7 +6585,7 @@
   // The special input is the HCurrentMethod for kRuntimeCall.
   HUserRecord<HInstruction*> special_input_;
 
-  const uint16_t proto_idx_;
+  const dex::ProtoIndex proto_index_;
   const DexFile& dex_file_;
 };
 
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 9536381..e72d49e 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -786,11 +786,10 @@
 static std::unique_ptr<char[]> indexString(const DexFile* pDexFile,
                                            const Instruction* pDecInsn,
                                            size_t bufSize) {
-  static const u4 kInvalidIndex = std::numeric_limits<u4>::max();
   std::unique_ptr<char[]> buf(new char[bufSize]);
   // Determine index and width of the string.
   u4 index = 0;
-  u4 secondary_index = kInvalidIndex;
+  u2 secondary_index = 0;
   u4 width = 4;
   switch (Instruction::FormatOf(pDecInsn->Opcode())) {
     // SOME NOT SUPPORTED:
@@ -898,7 +897,7 @@
                                              signature.ToString().c_str());
       }
       if (secondary_index < pDexFile->GetHeader().proto_ids_size_) {
-        const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index);
+        const DexFile::ProtoId& protoId = pDexFile->GetProtoId(dex::ProtoIndex(secondary_index));
         const Signature signature = pDexFile->GetProtoSignature(protoId);
         proto = signature.ToString();
       }
@@ -916,7 +915,7 @@
       break;
     case Instruction::kIndexProtoRef:
       if (index < pDexFile->GetHeader().proto_ids_size_) {
-        const DexFile::ProtoId& protoId = pDexFile->GetProtoId(index);
+        const DexFile::ProtoId& protoId = pDexFile->GetProtoId(dex::ProtoIndex(index));
         const Signature signature = pDexFile->GetProtoSignature(protoId);
         const std::string& proto = signature.ToString();
         outSize = snprintf(buf.get(), bufSize, "%s // proto@%0*x", proto.c_str(), width, index);
@@ -1705,7 +1704,7 @@
   dex::StringIndex method_name_idx = static_cast<dex::StringIndex>(it.GetJavaValue().i);
   const char* method_name = pDexFile->StringDataByIdx(method_name_idx);
   it.Next();
-  uint32_t method_type_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+  dex::ProtoIndex method_type_idx = static_cast<dex::ProtoIndex>(it.GetJavaValue().i);
   const DexFile::ProtoId& method_type_id = pDexFile->GetProtoId(method_type_idx);
   std::string method_type = pDexFile->GetProtoSignature(method_type_id).ToString();
   it.Next();
@@ -1763,7 +1762,7 @@
         break;
       case EncodedArrayValueIterator::ValueType::kMethodType: {
         type = "MethodType";
-        uint32_t proto_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+        dex::ProtoIndex proto_idx = static_cast<dex::ProtoIndex>(it.GetJavaValue().i);
         const DexFile::ProtoId& proto_id = pDexFile->GetProtoId(proto_idx);
         value = pDexFile->GetProtoSignature(proto_id).ToString();
         break;
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index 1525d53..b7d9db6 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -332,7 +332,7 @@
 }
 
 void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) {
-  const DexFile::ProtoId& disk_proto_id = dex_file.GetProtoId(i);
+  const DexFile::ProtoId& disk_proto_id = dex_file.GetProtoId(dex::ProtoIndex(i));
   const DexFile::TypeList* type_list = dex_file.GetProtoParameters(disk_proto_id);
   TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_);
 
@@ -353,7 +353,7 @@
 void Collections::CreateMethodId(const DexFile& dex_file, uint32_t i) {
   const DexFile::MethodId& disk_method_id = dex_file.GetMethodId(i);
   MethodId* method_id = new MethodId(GetTypeId(disk_method_id.class_idx_.index_),
-                                     GetProtoId(disk_method_id.proto_idx_),
+                                     GetProtoId(disk_method_id.proto_idx_.index_),
                                      GetStringId(disk_method_id.name_idx_.index_));
   AddIndexedItem(method_ids_, method_id, MethodIdsOffset() + i * MethodId::ItemSize(), i);
 }
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 065f3eb..c8a06ed 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -19,11 +19,13 @@
     defaults: ["art_defaults"],
     host_supported: true,
     srcs: [
+        "arch/instruction_set.cc",
         "base/allocator.cc",
         "base/arena_allocator.cc",
         "base/arena_bit_vector.cc",
         "base/bit_vector.cc",
         "base/file_magic.cc",
+        "base/file_utils.cc",
         "base/hex_dump.cc",
         "base/logging.cc",
         "base/malloc_arena_pool.cc",
@@ -80,6 +82,7 @@
     cmd: "$(location generate_operator_out) art/libartbase $(in) > $(out)",
     tools: ["generate_operator_out"],
     srcs: [
+        "arch/instruction_set.h",
         "base/allocator.h",
         "base/callee_save_type.h",
         "base/unix_file/fd_file.h",
@@ -141,12 +144,14 @@
         "art_gtest_defaults",
     ],
     srcs: [
+        "arch/instruction_set_test.cc",
         "base/arena_allocator_test.cc",
         "base/bit_field_test.cc",
         "base/bit_string_test.cc",
         "base/bit_struct_test.cc",
         "base/bit_utils_test.cc",
         "base/bit_vector_test.cc",
+        "base/file_utils_test.cc",
         "base/hash_set_test.cc",
         "base/hex_dump_test.cc",
         "base/histogram_test.cc",
diff --git a/runtime/arch/instruction_set.cc b/libartbase/arch/instruction_set.cc
similarity index 88%
rename from runtime/arch/instruction_set.cc
rename to libartbase/arch/instruction_set.cc
index b848eb2..a187663 100644
--- a/runtime/arch/instruction_set.cc
+++ b/libartbase/arch/instruction_set.cc
@@ -16,8 +16,6 @@
 
 #include "instruction_set.h"
 
-// Explicitly include our own elf.h to avoid Linux and other dependencies.
-#include "../elf.h"
 #include "android-base/logging.h"
 #include "base/bit_utils.h"
 #include "base/globals.h"
@@ -83,29 +81,6 @@
   return InstructionSet::kNone;
 }
 
-InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags) {
-  switch (e_machine) {
-    case EM_ARM:
-      return InstructionSet::kArm;
-    case EM_AARCH64:
-      return InstructionSet::kArm64;
-    case EM_386:
-      return InstructionSet::kX86;
-    case EM_X86_64:
-      return InstructionSet::kX86_64;
-    case EM_MIPS: {
-      if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R2 ||
-          (e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) {
-        return InstructionSet::kMips;
-      } else if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) {
-        return InstructionSet::kMips64;
-      }
-      break;
-    }
-  }
-  return InstructionSet::kNone;
-}
-
 size_t GetInstructionSetAlignment(InstructionSet isa) {
   switch (isa) {
     case InstructionSet::kArm:
diff --git a/runtime/arch/instruction_set.h b/libartbase/arch/instruction_set.h
similarity index 97%
rename from runtime/arch/instruction_set.h
rename to libartbase/arch/instruction_set.h
index 6434005..06bd53a 100644
--- a/runtime/arch/instruction_set.h
+++ b/libartbase/arch/instruction_set.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_ARCH_INSTRUCTION_SET_H_
-#define ART_RUNTIME_ARCH_INSTRUCTION_SET_H_
+#ifndef ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_
+#define ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_
 
 #include <iosfwd>
 #include <string>
@@ -89,8 +89,6 @@
 // Note: Returns kNone when the string cannot be parsed to a known value.
 InstructionSet GetInstructionSetFromString(const char* instruction_set);
 
-InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags);
-
 // Fatal logging out of line to keep the header clean of logging.h.
 NO_RETURN void InstructionSetAbort(InstructionSet isa);
 
@@ -299,4 +297,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_ARCH_INSTRUCTION_SET_H_
+#endif  // ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_
diff --git a/runtime/arch/instruction_set_test.cc b/libartbase/arch/instruction_set_test.cc
similarity index 100%
rename from runtime/arch/instruction_set_test.cc
rename to libartbase/arch/instruction_set_test.cc
diff --git a/runtime/base/file_utils.cc b/libartbase/base/file_utils.cc
similarity index 98%
rename from runtime/base/file_utils.cc
rename to libartbase/base/file_utils.cc
index 537216c..9450e1e 100644
--- a/runtime/base/file_utils.cc
+++ b/libartbase/base/file_utils.cc
@@ -47,7 +47,6 @@
 #include "base/os.h"
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
-#include "dex/dex_file_loader.h"
 
 #if defined(__APPLE__)
 #include <crt_externs.h>
@@ -64,6 +63,8 @@
 using android::base::StringAppendF;
 using android::base::StringPrintf;
 
+static constexpr const char* kClassesDex = "classes.dex";
+
 bool ReadFileToString(const std::string& file_name, std::string* result) {
   File file(file_name, O_RDONLY, false);
   if (!file.IsOpened()) {
@@ -224,7 +225,7 @@
       !android::base::EndsWith(location, ".art") &&
       !android::base::EndsWith(location, ".oat")) {
     cache_file += "/";
-    cache_file += DexFileLoader::kClassesDex;
+    cache_file += kClassesDex;
   }
   std::replace(cache_file.begin(), cache_file.end(), '/', '@');
   *filename = StringPrintf("%s/%s", cache_location, cache_file.c_str());
diff --git a/runtime/base/file_utils.h b/libartbase/base/file_utils.h
similarity index 95%
rename from runtime/base/file_utils.h
rename to libartbase/base/file_utils.h
index d4f6c57..063393b 100644
--- a/runtime/base/file_utils.h
+++ b/libartbase/base/file_utils.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_BASE_FILE_UTILS_H_
-#define ART_RUNTIME_BASE_FILE_UTILS_H_
+#ifndef ART_LIBARTBASE_BASE_FILE_UTILS_H_
+#define ART_LIBARTBASE_BASE_FILE_UTILS_H_
 
 #include <stdlib.h>
 
@@ -46,6 +46,7 @@
 // Returns the dalvik-cache location, with subdir appended. Returns the empty string if the cache
 // could not be found.
 std::string GetDalvikCache(const char* subdir);
+
 // Return true if we found the dalvik cache and stored it in the dalvik_cache argument.
 // have_android_data will be set to true if we have an ANDROID_DATA that exists,
 // dalvik_cache_exists will be true if there is a dalvik-cache directory that is present.
@@ -79,4 +80,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_BASE_FILE_UTILS_H_
+#endif  // ART_LIBARTBASE_BASE_FILE_UTILS_H_
diff --git a/runtime/base/file_utils_test.cc b/libartbase/base/file_utils_test.cc
similarity index 100%
rename from runtime/base/file_utils_test.cc
rename to libartbase/base/file_utils_test.cc
diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h
index d1b3200..e78e8d7 100644
--- a/libdexfile/dex/dex_file-inl.h
+++ b/libdexfile/dex/dex_file-inl.h
@@ -127,7 +127,7 @@
   return StringByTypeIdx(proto_id.return_type_idx_);
 }
 
-inline const char* DexFile::GetShorty(uint32_t proto_idx) const {
+inline const char* DexFile::GetShorty(dex::ProtoIndex proto_idx) const {
   const ProtoId& proto_id = GetProtoId(proto_idx);
   return StringDataByIdx(proto_id.shorty_idx_);
 }
diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc
index 8cfee66..9de260c 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -281,7 +281,7 @@
   // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx
   const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass);
   const dex::StringIndex name_idx = GetIndexForStringId(name);
-  const uint16_t proto_idx = GetIndexForProtoId(signature);
+  const dex::ProtoIndex proto_idx = GetIndexForProtoId(signature);
   int32_t lo = 0;
   int32_t hi = NumMethodIds() - 1;
   while (hi >= lo) {
@@ -373,7 +373,8 @@
   int32_t hi = NumProtoIds() - 1;
   while (hi >= lo) {
     int32_t mid = (hi + lo) / 2;
-    const DexFile::ProtoId& proto = GetProtoId(mid);
+    const dex::ProtoIndex proto_idx = static_cast<dex::ProtoIndex>(mid);
+    const DexFile::ProtoId& proto = GetProtoId(proto_idx);
     int compare = return_type_idx.index_ - proto.return_type_idx_.index_;
     if (compare == 0) {
       DexFileParameterIterator it(*this, proto);
@@ -777,6 +778,11 @@
 
 namespace dex {
 
+std::ostream& operator<<(std::ostream& os, const ProtoIndex& index) {
+  os << "ProtoIndex[" << index.index_ << "]";
+  return os;
+}
+
 std::ostream& operator<<(std::ostream& os, const StringIndex& index) {
   os << "StringIndex[" << index.index_ << "]";
   return os;
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index 4ca735a..87d2c48 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -189,7 +189,7 @@
   // Raw method_id_item.
   struct MethodId {
     dex::TypeIndex class_idx_;   // index into type_ids_ array for defining class
-    uint16_t proto_idx_;         // index into proto_ids_ array for method prototype
+    dex::ProtoIndex proto_idx_;  // index into proto_ids_ array for method prototype
     dex::StringIndex name_idx_;  // index into string_ids_ array for method name
 
    private:
@@ -692,15 +692,15 @@
   }
 
   // Returns the ProtoId at the specified index.
-  const ProtoId& GetProtoId(uint16_t idx) const {
-    DCHECK_LT(idx, NumProtoIds()) << GetLocation();
-    return proto_ids_[idx];
+  const ProtoId& GetProtoId(dex::ProtoIndex idx) const {
+    DCHECK_LT(idx.index_, NumProtoIds()) << GetLocation();
+    return proto_ids_[idx.index_];
   }
 
-  uint16_t GetIndexForProtoId(const ProtoId& proto_id) const {
+  dex::ProtoIndex GetIndexForProtoId(const ProtoId& proto_id) const {
     CHECK_GE(&proto_id, proto_ids_) << GetLocation();
     CHECK_LT(&proto_id, proto_ids_ + header_->proto_ids_size_) << GetLocation();
-    return &proto_id - proto_ids_;
+    return dex::ProtoIndex(&proto_id - proto_ids_);
   }
 
   // Looks up a proto id for a given return type and signature type list
@@ -722,7 +722,7 @@
   const Signature CreateSignature(const StringPiece& signature) const;
 
   // Returns the short form method descriptor for the given prototype.
-  const char* GetShorty(uint32_t proto_idx) const;
+  const char* GetShorty(dex::ProtoIndex proto_idx) const;
 
   const TypeList* GetProtoParameters(const ProtoId& proto_id) const {
     return DataPointer<TypeList>(proto_id.parameters_off_);
diff --git a/libdexfile/dex/dex_file_types.h b/libdexfile/dex/dex_file_types.h
index 2bb70ff..d4fb3de 100644
--- a/libdexfile/dex/dex_file_types.h
+++ b/libdexfile/dex/dex_file_types.h
@@ -25,73 +25,67 @@
 
 constexpr uint32_t kDexNoIndex = 0xFFFFFFFF;
 
-class StringIndex {
+template<typename T>
+class DexIndex {
  public:
-  uint32_t index_;
+  T index_;
 
-  constexpr StringIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {}
-  explicit constexpr StringIndex(uint32_t idx) : index_(idx) {}
+  constexpr DexIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {}
+  explicit constexpr DexIndex(T idx) : index_(idx) {}
 
   bool IsValid() const {
     return index_ != std::numeric_limits<decltype(index_)>::max();
   }
-  static StringIndex Invalid() {
-    return StringIndex(std::numeric_limits<decltype(index_)>::max());
+  static constexpr DexIndex Invalid() {
+    return DexIndex(std::numeric_limits<decltype(index_)>::max());
   }
-
-  bool operator==(const StringIndex& other) const {
+  bool operator==(const DexIndex& other) const {
     return index_ == other.index_;
   }
-  bool operator!=(const StringIndex& other) const {
+  bool operator!=(const DexIndex& other) const {
     return index_ != other.index_;
   }
-  bool operator<(const StringIndex& other) const {
+  bool operator<(const DexIndex& other) const {
     return index_ < other.index_;
   }
-  bool operator<=(const StringIndex& other) const {
+  bool operator<=(const DexIndex& other) const {
     return index_ <= other.index_;
   }
-  bool operator>(const StringIndex& other) const {
+  bool operator>(const DexIndex& other) const {
     return index_ > other.index_;
   }
-  bool operator>=(const StringIndex& other) const {
+  bool operator>=(const DexIndex& other) const {
     return index_ >= other.index_;
   }
 };
+
+class ProtoIndex : public DexIndex<uint16_t> {
+ public:
+  ProtoIndex() {}
+  explicit constexpr ProtoIndex(uint16_t index) : DexIndex<decltype(index_)>(index) {}
+  static constexpr ProtoIndex Invalid() {
+    return ProtoIndex(std::numeric_limits<decltype(index_)>::max());
+  }
+};
+std::ostream& operator<<(std::ostream& os, const ProtoIndex& index);
+
+class StringIndex : public DexIndex<uint32_t> {
+ public:
+  StringIndex() {}
+  explicit constexpr StringIndex(uint32_t index) : DexIndex<decltype(index_)>(index) {}
+  static constexpr StringIndex Invalid() {
+    return StringIndex(std::numeric_limits<decltype(index_)>::max());
+  }
+};
 std::ostream& operator<<(std::ostream& os, const StringIndex& index);
 
-class TypeIndex {
+class TypeIndex : public DexIndex<uint16_t> {
  public:
-  uint16_t index_;
-
-  constexpr TypeIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {}
-  explicit constexpr TypeIndex(uint16_t idx) : index_(idx) {}
-
-  bool IsValid() const {
-    return index_ != std::numeric_limits<decltype(index_)>::max();
-  }
-  static TypeIndex Invalid() {
+  TypeIndex() {}
+  explicit constexpr TypeIndex(uint16_t index) : DexIndex<uint16_t>(index) {}
+  static constexpr TypeIndex Invalid() {
     return TypeIndex(std::numeric_limits<decltype(index_)>::max());
   }
-
-  bool operator==(const TypeIndex& other) const {
-    return index_ == other.index_;
-  }
-  bool operator!=(const TypeIndex& other) const {
-    return index_ != other.index_;
-  }
-  bool operator<(const TypeIndex& other) const {
-    return index_ < other.index_;
-  }
-  bool operator<=(const TypeIndex& other) const {
-    return index_ <= other.index_;
-  }
-  bool operator>(const TypeIndex& other) const {
-    return index_ > other.index_;
-  }
-  bool operator>=(const TypeIndex& other) const {
-    return index_ >= other.index_;
-  }
 };
 std::ostream& operator<<(std::ostream& os, const TypeIndex& index);
 
@@ -100,15 +94,21 @@
 
 namespace std {
 
+template<> struct hash<art::dex::ProtoIndex> {
+  size_t operator()(const art::dex::ProtoIndex& index) const {
+    return hash<decltype(index.index_)>()(index.index_);
+  }
+};
+
 template<> struct hash<art::dex::StringIndex> {
   size_t operator()(const art::dex::StringIndex& index) const {
-    return hash<uint32_t>()(index.index_);
+    return hash<decltype(index.index_)>()(index.index_);
   }
 };
 
 template<> struct hash<art::dex::TypeIndex> {
   size_t operator()(const art::dex::TypeIndex& index) const {
-    return hash<uint16_t>()(index.index_);
+    return hash<decltype(index.index_)>()(index.index_);
   }
 };
 
diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc
index a32f64e..78db8b9 100644
--- a/libdexfile/dex/dex_file_verifier.cc
+++ b/libdexfile/dex/dex_file_verifier.cc
@@ -127,8 +127,9 @@
   return &dex_file_->GetMethodId(idx);
 }
 
-const DexFile::ProtoId* DexFileVerifier::CheckLoadProtoId(uint32_t idx, const char* err_string) {
-  if (UNLIKELY(!CheckIndex(idx, dex_file_->NumProtoIds(), err_string))) {
+const DexFile::ProtoId* DexFileVerifier::CheckLoadProtoId(dex::ProtoIndex idx,
+                                                          const char* err_string) {
+  if (UNLIKELY(!CheckIndex(idx.index_, dex_file_->NumProtoIds(), err_string))) {
     return nullptr;
   }
   return &dex_file_->GetProtoId(idx);
@@ -2208,7 +2209,7 @@
   }
 
   // Check that the proto id is valid.
-  if (UNLIKELY(!CheckIndex(item->proto_idx_, dex_file_->NumProtoIds(),
+  if (UNLIKELY(!CheckIndex(item->proto_idx_.index_, dex_file_->NumProtoIds(),
                            "inter_method_id_item proto_idx"))) {
     return false;
   }
diff --git a/libdexfile/dex/dex_file_verifier.h b/libdexfile/dex/dex_file_verifier.h
index 04d8d71..43d1093 100644
--- a/libdexfile/dex/dex_file_verifier.h
+++ b/libdexfile/dex/dex_file_verifier.h
@@ -164,7 +164,7 @@
   // error if not. If there is an error, null is returned.
   const DexFile::FieldId* CheckLoadFieldId(uint32_t idx, const char* error_fmt);
   const DexFile::MethodId* CheckLoadMethodId(uint32_t idx, const char* error_fmt);
-  const DexFile::ProtoId* CheckLoadProtoId(uint32_t idx, const char* error_fmt);
+  const DexFile::ProtoId* CheckLoadProtoId(dex::ProtoIndex idx, const char* error_fmt);
 
   void ErrorStringPrintf(const char* fmt, ...)
       __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR;
diff --git a/libdexfile/dex/dex_file_verifier_test.cc b/libdexfile/dex/dex_file_verifier_test.cc
index f82081f..c9bac0f 100644
--- a/libdexfile/dex/dex_file_verifier_test.cc
+++ b/libdexfile/dex/dex_file_verifier_test.cc
@@ -161,7 +161,7 @@
       "method_id_proto_idx",
       [](DexFile* dex_file) {
         DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0));
-        method_id->proto_idx_ = 0xFF;
+        method_id->proto_idx_ = dex::ProtoIndex(0xFF);
       },
       "inter_method_id_item proto_idx");
 
@@ -1425,12 +1425,13 @@
           CHECK_LT(method_idx + 1u, dex_file->NumMethodIds());
           CHECK_EQ(dex_file->GetMethodId(method_idx).name_idx_,
                    dex_file->GetMethodId(method_idx + 1).name_idx_);
-          CHECK_EQ(dex_file->GetMethodId(method_idx).proto_idx_ + 1u,
-                   dex_file->GetMethodId(method_idx + 1).proto_idx_);
+          CHECK_EQ(dex_file->GetMethodId(method_idx).proto_idx_.index_ + 1u,
+                   dex_file->GetMethodId(method_idx + 1).proto_idx_.index_);
           // Their return types should be the same.
-          uint32_t proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_;
+          dex::ProtoIndex proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_;
           const DexFile::ProtoId& proto1 = dex_file->GetProtoId(proto1_idx);
-          const DexFile::ProtoId& proto2 = dex_file->GetProtoId(proto1_idx + 1u);
+          dex::ProtoIndex proto2_idx(proto1_idx.index_ + 1u);
+          const DexFile::ProtoId& proto2 = dex_file->GetProtoId(proto2_idx);
           CHECK_EQ(proto1.return_type_idx_, proto2.return_type_idx_);
           // And the first should not have any parameters while the second should have some.
           CHECK(!DexFileParameterIterator(*dex_file, proto1).HasNext());
diff --git a/libdexfile/dex/dex_instruction.cc b/libdexfile/dex/dex_instruction.cc
index 8862181..8378211 100644
--- a/libdexfile/dex/dex_instruction.cc
+++ b/libdexfile/dex/dex_instruction.cc
@@ -467,10 +467,10 @@
     case k45cc: {
       uint32_t arg[kMaxVarArgRegs];
       GetVarArgs(arg);
-      uint32_t method_idx = VRegB_45cc();
-      uint32_t proto_idx = VRegH_45cc();
+      uint16_t method_idx = VRegB_45cc();
+      dex::ProtoIndex proto_idx(VRegH_45cc());
       os << opcode << " {";
-      for (int i = 0; i < VRegA_45cc(); ++i) {
+      for (uint32_t i = 0; i < VRegA_45cc(); ++i) {
         if (i != 0) {
           os << ", ";
         }
@@ -478,7 +478,8 @@
       }
       os << "}";
       if (file != nullptr) {
-        os << ", " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx)
+        os << ", " << file->PrettyMethod(method_idx)
+           << ", " << file->GetShorty(proto_idx)
            << " // ";
       } else {
         os << ", ";
@@ -490,18 +491,19 @@
       switch (Opcode()) {
         case INVOKE_POLYMORPHIC_RANGE: {
           if (file != nullptr) {
-            uint32_t method_idx = VRegB_4rcc();
-            uint32_t proto_idx = VRegH_4rcc();
+            uint16_t method_idx = VRegB_4rcc();
+            dex::ProtoIndex proto_idx(VRegH_4rcc());
             os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc())
-               << "}, " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx)
+               << "}, " << file->PrettyMethod(method_idx)
+               << ", " << file->GetShorty(dex::ProtoIndex(proto_idx))
                << " // method@" << method_idx << ", proto@" << proto_idx;
             break;
           }
         }
         FALLTHROUGH_INTENDED;
         default: {
-          uint32_t method_idx = VRegB_4rcc();
-          uint32_t proto_idx = VRegH_4rcc();
+          uint16_t method_idx = VRegB_4rcc();
+          dex::ProtoIndex proto_idx(VRegH_4rcc());
           os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc())
              << "}, method@" << method_idx << ", proto@" << proto_idx;
         }
diff --git a/patchoat/Android.bp b/patchoat/Android.bp
index 0e8e517..1e2f328 100644
--- a/patchoat/Android.bp
+++ b/patchoat/Android.bp
@@ -25,6 +25,7 @@
         },
     },
     shared_libs: [
+        "libartbase",
         "libbase",
         "libcrypto", // For computing the digest of image file
     ],
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 64e6796..0540b20 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -23,7 +23,7 @@
     "-Wl,--keep-unique,__dex_debug_register_code"
 ]
 
-cc_defaults {
+libart_cc_defaults {
     name: "libart_defaults",
     defaults: ["art_defaults"],
     host_supported: true,
@@ -32,7 +32,6 @@
         "art_field.cc",
         "art_method.cc",
         "barrier.cc",
-        "base/file_utils.cc",
         "base/mem_map_arena_pool.cc",
         "base/mutex.cc",
         "base/quasi_atomic.cc",
@@ -196,6 +195,7 @@
         "ti/agent.cc",
         "trace.cc",
         "transaction.cc",
+        "var_handles.cc",
         "vdex_file.cc",
         "verifier/instruction_flags.cc",
         "verifier/method_verifier.cc",
@@ -207,7 +207,6 @@
         "well_known_classes.cc",
 
         "arch/context.cc",
-        "arch/instruction_set.cc",
         "arch/instruction_set_features.cc",
         "arch/memcmp16.cc",
         "arch/arm/instruction_set_features_arm.cc",
@@ -396,7 +395,6 @@
         "libbacktrace",
         "liblz4",
         "liblog",
-        "libmetricslogger",
         // For atrace, properties, ashmem, set_sched_policy and socket_peer_is_trusted.
         "libcutils",
         // For common macros.
@@ -420,7 +418,6 @@
     cmd: "$(location generate_operator_out) art/runtime $(in) > $(out)",
     tools: ["generate_operator_out"],
     srcs: [
-        "arch/instruction_set.h",
         "base/mutex.h",
         "class_loader_context.h",
         "class_status.h",
@@ -531,7 +528,6 @@
     ],
     srcs: [
         "arch/arch_test.cc",
-        "arch/instruction_set_test.cc",
         "arch/instruction_set_features_test.cc",
         "arch/memcmp16_test.cc",
         "arch/stub_test.cc",
@@ -542,7 +538,6 @@
         "arch/x86/instruction_set_features_x86_test.cc",
         "arch/x86_64/instruction_set_features_x86_64_test.cc",
         "barrier_test.cc",
-        "base/file_utils_test.cc",
         "base/mutex_test.cc",
         "base/timing_logger_test.cc",
         "cha_test.cc",
diff --git a/runtime/arch/code_offset.h b/runtime/arch/code_offset.h
index 8e8dde4..f0c6d22 100644
--- a/runtime/arch/code_offset.h
+++ b/runtime/arch/code_offset.h
@@ -21,9 +21,9 @@
 
 #include <android-base/logging.h>
 
+#include "arch/instruction_set.h"
 #include "base/bit_utils.h"
 #include "base/macros.h"
-#include "instruction_set.h"
 
 namespace art {
 
diff --git a/runtime/arch/instruction_set_features.h b/runtime/arch/instruction_set_features.h
index 5f1a507..c31c927 100644
--- a/runtime/arch/instruction_set_features.h
+++ b/runtime/arch/instruction_set_features.h
@@ -21,8 +21,8 @@
 #include <ostream>
 #include <vector>
 
+#include "arch/instruction_set.h"
 #include "base/macros.h"
-#include "instruction_set.h"
 
 namespace art {
 
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 87fcb20..608e33c 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -710,6 +710,23 @@
   return GetOatMethodQuickCode(runtime->GetClassLinker()->GetImagePointerSize()) != nullptr;
 }
 
+void ArtMethod::SetNotIntrinsic() {
+  if (!IsIntrinsic()) {
+    return;
+  }
+
+  // Query the hidden API access flags of the intrinsic.
+  HiddenApiAccessFlags::ApiList intrinsic_api_list = GetHiddenApiAccessFlags();
+
+  // Clear intrinsic-related access flags.
+  ClearAccessFlags(kAccIntrinsic | kAccIntrinsicBits);
+
+  // Re-apply hidden API access flags now that the method is not an intrinsic.
+  SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(GetAccessFlags(), intrinsic_api_list));
+  DCHECK_EQ(GetHiddenApiAccessFlags(), intrinsic_api_list);
+}
+
+
 void ArtMethod::CopyFrom(ArtMethod* src, PointerSize image_pointer_size) {
   memcpy(reinterpret_cast<void*>(this), reinterpret_cast<const void*>(src),
          Size(image_pointer_size));
diff --git a/runtime/art_method.h b/runtime/art_method.h
index acaa4a6..012d706 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -194,9 +194,7 @@
     return (GetAccessFlags() & kAccIntrinsicBits) >> kAccFlagsShift;
   }
 
-  void SetNotIntrinsic() REQUIRES_SHARED(Locks::mutator_lock_) {
-    ClearAccessFlags(kAccIntrinsic | kAccIntrinsicBits);
-  }
+  void SetNotIntrinsic() REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool IsCopied() {
     static_assert((kAccCopied & (kAccIntrinsic | kAccIntrinsicBits)) == 0,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9c8b438..be9e08f 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -8174,7 +8174,7 @@
 
 ObjPtr<mirror::MethodType> ClassLinker::ResolveMethodType(
     Thread* self,
-    uint32_t proto_idx,
+    dex::ProtoIndex proto_idx,
     Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader) {
   DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
@@ -8236,7 +8236,7 @@
 }
 
 ObjPtr<mirror::MethodType> ClassLinker::ResolveMethodType(Thread* self,
-                                                          uint32_t proto_idx,
+                                                          dex::ProtoIndex proto_idx,
                                                           ArtMethod* referrer) {
   StackHandleScope<2> hs(self);
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 60cff9e..52ecf82 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -416,14 +416,14 @@
   // Resolve a method type with a given ID from the DexFile associated with a given DexCache
   // and ClassLoader, storing the result in the DexCache.
   ObjPtr<mirror::MethodType> ResolveMethodType(Thread* self,
-                                               uint32_t proto_idx,
+                                               dex::ProtoIndex proto_idx,
                                                Handle<mirror::DexCache> dex_cache,
                                                Handle<mirror::ClassLoader> class_loader)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
 
   ObjPtr<mirror::MethodType> ResolveMethodType(Thread* self,
-                                               uint32_t proto_idx,
+                                               dex::ProtoIndex proto_idx,
                                                ArtMethod* referrer)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h
index 37e074d..9c2a40b 100644
--- a/runtime/common_dex_operations.h
+++ b/runtime/common_dex_operations.h
@@ -29,6 +29,7 @@
 #include "instrumentation.h"
 #include "interpreter/shadow_frame.h"
 #include "interpreter/unstarted_runtime.h"
+#include "jvalue-inl.h"
 #include "mirror/class.h"
 #include "mirror/object.h"
 #include "obj_ptr-inl.h"
diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc
index 274a6df..d353c26 100644
--- a/runtime/dex/art_dex_file_loader_test.cc
+++ b/runtime/dex/art_dex_file_loader_test.cc
@@ -43,48 +43,7 @@
   dst_stream << src_stream.rdbuf();
 }
 
-class ArtDexFileLoaderTest : public CommonRuntimeTest {
- public:
-  virtual void SetUp() {
-    CommonRuntimeTest::SetUp();
-
-    std::string dex_location = GetTestDexFileName("Main");
-    std::string multidex_location = GetTestDexFileName("MultiDex");
-
-    data_location_path_ = android_data_ + "/foo.jar";
-    system_location_path_ = GetAndroidRoot() + "/foo.jar";
-    system_framework_location_path_ = GetAndroidRoot() + "/framework/foo.jar";
-    data_multi_location_path_ = android_data_ + "/multifoo.jar";
-    system_multi_location_path_ = GetAndroidRoot() + "/multifoo.jar";
-    system_framework_multi_location_path_ = GetAndroidRoot() + "/framework/multifoo.jar";
-
-    Copy(dex_location, data_location_path_);
-    Copy(dex_location, system_location_path_);
-    Copy(dex_location, system_framework_location_path_);
-
-    Copy(multidex_location, data_multi_location_path_);
-    Copy(multidex_location, system_multi_location_path_);
-    Copy(multidex_location, system_framework_multi_location_path_);
-  }
-
-  virtual void TearDown() {
-    remove(data_location_path_.c_str());
-    remove(system_location_path_.c_str());
-    remove(system_framework_location_path_.c_str());
-    remove(data_multi_location_path_.c_str());
-    remove(system_multi_location_path_.c_str());
-    remove(system_framework_multi_location_path_.c_str());
-    CommonRuntimeTest::TearDown();
-  }
-
- protected:
-  std::string data_location_path_;
-  std::string system_location_path_;
-  std::string system_framework_location_path_;
-  std::string data_multi_location_path_;
-  std::string system_multi_location_path_;
-  std::string system_framework_multi_location_path_;
-};
+class ArtDexFileLoaderTest : public CommonRuntimeTest {};
 
 // TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and
 // the tests that depend upon them should be moved to dex_file_loader_test.cc
@@ -286,7 +245,7 @@
 
 TEST_F(ArtDexFileLoaderTest, FindProtoId) {
   for (size_t i = 0; i < java_lang_dex_file_->NumProtoIds(); i++) {
-    const DexFile::ProtoId& to_find = java_lang_dex_file_->GetProtoId(i);
+    const DexFile::ProtoId& to_find = java_lang_dex_file_->GetProtoId(dex::ProtoIndex(i));
     const DexFile::TypeList* to_find_tl = java_lang_dex_file_->GetProtoParameters(to_find);
     std::vector<dex::TypeIndex> to_find_types;
     if (to_find_tl != nullptr) {
@@ -297,7 +256,7 @@
     const DexFile::ProtoId* found =
         java_lang_dex_file_->FindProtoId(to_find.return_type_idx_, to_find_types);
     ASSERT_TRUE(found != nullptr);
-    EXPECT_EQ(java_lang_dex_file_->GetIndexForProtoId(*found), i);
+    EXPECT_EQ(java_lang_dex_file_->GetIndexForProtoId(*found), dex::ProtoIndex(i));
   }
 }
 
@@ -353,21 +312,24 @@
   ASSERT_EQ(0, unlink(dex_location_sym.c_str()));
 }
 
-TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) {
-  ArtDexFileLoader loader;
-  bool success;
-  std::string error_msg;
-  std::vector<std::unique_ptr<const DexFile>> dex_files;
-
+TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_DataDir) {
   // Load file from a non-system directory and check that it is not flagged as framework.
-  ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path_.c_str()));
-  success = loader.Open(data_location_path_.c_str(),
-                        data_location_path_,
-                        /* verify */ false,
-                        /* verify_checksum */ false,
-                        &error_msg,
-                        &dex_files);
+  std::string data_location_path = android_data_ + "/foo.jar";
+  ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path.c_str()));
+
+  Copy(GetTestDexFileName("Main"), data_location_path);
+
+  ArtDexFileLoader loader;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  bool success = loader.Open(data_location_path.c_str(),
+                             data_location_path,
+                             /* verify */ false,
+                             /* verify_checksum */ false,
+                             &error_msg,
+                             &dex_files);
   ASSERT_TRUE(success) << error_msg;
+
   ASSERT_GE(dex_files.size(), 1u);
   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
     ASSERT_FALSE(dex_file->IsPlatformDexFile());
@@ -375,15 +337,27 @@
 
   dex_files.clear();
 
+  ASSERT_EQ(0, remove(data_location_path.c_str()));
+}
+
+TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemDir) {
   // Load file from a system, non-framework directory and check that it is not flagged as framework.
-  ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path_.c_str()));
-  success = loader.Open(system_location_path_.c_str(),
-                        system_location_path_,
-                        /* verify */ false,
-                        /* verify_checksum */ false,
-                        &error_msg,
-                        &dex_files);
-  ASSERT_TRUE(success);
+  std::string system_location_path = GetAndroidRoot() + "/foo.jar";
+  ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path.c_str()));
+
+  Copy(GetTestDexFileName("Main"), system_location_path);
+
+  ArtDexFileLoader loader;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  bool success = loader.Open(system_location_path.c_str(),
+                             system_location_path,
+                             /* verify */ false,
+                             /* verify_checksum */ false,
+                             &error_msg,
+                             &dex_files);
+  ASSERT_TRUE(success) << error_msg;
+
   ASSERT_GE(dex_files.size(), 1u);
   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
     ASSERT_FALSE(dex_file->IsPlatformDexFile());
@@ -391,15 +365,27 @@
 
   dex_files.clear();
 
+  ASSERT_EQ(0, remove(system_location_path.c_str()));
+}
+
+TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemFrameworkDir) {
   // Load file from a system/framework directory and check that it is flagged as a framework dex.
-  ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path_.c_str()));
-  success = loader.Open(system_framework_location_path_.c_str(),
-                        system_framework_location_path_,
-                        /* verify */ false,
-                        /* verify_checksum */ false,
-                        &error_msg,
-                        &dex_files);
-  ASSERT_TRUE(success);
+  std::string system_framework_location_path = GetAndroidRoot() + "/framework/foo.jar";
+  ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path.c_str()));
+
+  Copy(GetTestDexFileName("Main"), system_framework_location_path);
+
+  ArtDexFileLoader loader;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  bool success = loader.Open(system_framework_location_path.c_str(),
+                             system_framework_location_path,
+                             /* verify */ false,
+                             /* verify_checksum */ false,
+                             &error_msg,
+                             &dex_files);
+  ASSERT_TRUE(success) << error_msg;
+
   ASSERT_GE(dex_files.size(), 1u);
   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
     ASSERT_TRUE(dex_file->IsPlatformDexFile());
@@ -407,14 +393,27 @@
 
   dex_files.clear();
 
+  ASSERT_EQ(0, remove(system_framework_location_path.c_str()));
+}
+
+TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_DataDir_MultiDex) {
   // Load multidex file from a non-system directory and check that it is not flagged as framework.
-  success = loader.Open(data_multi_location_path_.c_str(),
-                        data_multi_location_path_,
-                        /* verify */ false,
-                        /* verify_checksum */ false,
-                        &error_msg,
-                        &dex_files);
+  std::string data_multi_location_path = android_data_ + "/multifoo.jar";
+  ASSERT_FALSE(LocationIsOnSystemFramework(data_multi_location_path.c_str()));
+
+  Copy(GetTestDexFileName("MultiDex"), data_multi_location_path);
+
+  ArtDexFileLoader loader;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  bool success = loader.Open(data_multi_location_path.c_str(),
+                             data_multi_location_path,
+                             /* verify */ false,
+                             /* verify_checksum */ false,
+                             &error_msg,
+                             &dex_files);
   ASSERT_TRUE(success) << error_msg;
+
   ASSERT_GT(dex_files.size(), 1u);
   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
     ASSERT_FALSE(dex_file->IsPlatformDexFile());
@@ -422,15 +421,28 @@
 
   dex_files.clear();
 
+  ASSERT_EQ(0, remove(data_multi_location_path.c_str()));
+}
+
+TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemDir_MultiDex) {
   // Load multidex file from a system, non-framework directory and check that it is not flagged
   // as framework.
-  success = loader.Open(system_multi_location_path_.c_str(),
-                        system_multi_location_path_,
-                        /* verify */ false,
-                        /* verify_checksum */ false,
-                        &error_msg,
-                        &dex_files);
-  ASSERT_TRUE(success);
+  std::string system_multi_location_path = GetAndroidRoot() + "/multifoo.jar";
+  ASSERT_FALSE(LocationIsOnSystemFramework(system_multi_location_path.c_str()));
+
+  Copy(GetTestDexFileName("MultiDex"), system_multi_location_path);
+
+  ArtDexFileLoader loader;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  bool success = loader.Open(system_multi_location_path.c_str(),
+                             system_multi_location_path,
+                             /* verify */ false,
+                             /* verify_checksum */ false,
+                             &error_msg,
+                             &dex_files);
+  ASSERT_TRUE(success) << error_msg;
+
   ASSERT_GT(dex_files.size(), 1u);
   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
     ASSERT_FALSE(dex_file->IsPlatformDexFile());
@@ -438,19 +450,36 @@
 
   dex_files.clear();
 
+  ASSERT_EQ(0, remove(system_multi_location_path.c_str()));
+}
+
+TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemFrameworkDir_MultiDex) {
   // Load multidex file from a system/framework directory and check that it is flagged as a
   // framework dex.
-  success = loader.Open(system_framework_multi_location_path_.c_str(),
-                        system_framework_multi_location_path_,
-                        /* verify */ false,
-                        /* verify_checksum */ false,
-                        &error_msg,
-                        &dex_files);
-  ASSERT_TRUE(success);
+  std::string system_framework_multi_location_path = GetAndroidRoot() + "/framework/multifoo.jar";
+  ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_multi_location_path.c_str()));
+
+  Copy(GetTestDexFileName("MultiDex"), system_framework_multi_location_path);
+
+  ArtDexFileLoader loader;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  bool success = loader.Open(system_framework_multi_location_path.c_str(),
+                             system_framework_multi_location_path,
+                             /* verify */ false,
+                             /* verify_checksum */ false,
+                             &error_msg,
+                             &dex_files);
+  ASSERT_TRUE(success) << error_msg;
+
   ASSERT_GT(dex_files.size(), 1u);
   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
     ASSERT_TRUE(dex_file->IsPlatformDexFile());
   }
+
+  dex_files.clear();
+
+  ASSERT_EQ(0, remove(system_framework_multi_location_path.c_str()));
 }
 
 }  // namespace art
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 719b4af..026b5da 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -1073,6 +1073,29 @@
   return true;
 }
 
+static InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags) {
+  switch (e_machine) {
+    case EM_ARM:
+      return InstructionSet::kArm;
+    case EM_AARCH64:
+      return InstructionSet::kArm64;
+    case EM_386:
+      return InstructionSet::kX86;
+    case EM_X86_64:
+      return InstructionSet::kX86_64;
+    case EM_MIPS: {
+      if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R2 ||
+          (e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) {
+        return InstructionSet::kMips;
+      } else if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) {
+        return InstructionSet::kMips64;
+      }
+      break;
+    }
+  }
+  return InstructionSet::kNone;
+}
+
 template <typename ElfTypes>
 bool ElfFileImpl<ElfTypes>::Load(File* file,
                                  bool executable,
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index a58946a..7fc8db3 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -268,7 +268,7 @@
 }
 
 ObjPtr<mirror::MethodType> ResolveMethodTypeFromCode(ArtMethod* referrer,
-                                                     uint32_t proto_idx) {
+                                                     dex::ProtoIndex proto_idx) {
   Thread::PoisonObjectPointersIfDebug();
   ObjPtr<mirror::MethodType> method_type =
       referrer->GetDexCache()->GetResolvedMethodType(proto_idx);
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 0a3b5df..e33de9c 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -158,7 +158,7 @@
     REQUIRES_SHARED(Locks::mutator_lock_)
     REQUIRES(!Roles::uninterruptible_);
 
-ObjPtr<mirror::MethodType> ResolveMethodTypeFromCode(ArtMethod* referrer, uint32_t proto_idx)
+ObjPtr<mirror::MethodType> ResolveMethodTypeFromCode(ArtMethod* referrer, dex::ProtoIndex proto_idx)
     REQUIRES_SHARED(Locks::mutator_lock_)
     REQUIRES(!Roles::uninterruptible_);
 
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index cf9ddd8..fa536c7 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -200,7 +200,7 @@
   auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self,
                                                                   CalleeSaveType::kSaveEverything);
   ArtMethod* caller = caller_and_outer.caller;
-  ObjPtr<mirror::MethodType> result = ResolveMethodTypeFromCode(caller, proto_idx);
+  ObjPtr<mirror::MethodType> result = ResolveMethodTypeFromCode(caller, dex::ProtoIndex(proto_idx));
   return result.Ptr();
 }
 
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 39429c5..0a186f4 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -42,6 +42,7 @@
 #include "mirror/method_handle_impl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
+#include "mirror/var_handle.h"
 #include "oat_file.h"
 #include "oat_quick_method_header.h"
 #include "quick_exception_handler.h"
@@ -49,6 +50,7 @@
 #include "scoped_thread_state_change-inl.h"
 #include "stack.h"
 #include "thread-inl.h"
+#include "var_handles.h"
 #include "well_known_classes.h"
 
 namespace art {
@@ -2766,7 +2768,7 @@
   const Instruction& inst = caller_method->DexInstructions().InstructionAt(dex_pc);
   DCHECK(inst.Opcode() == Instruction::INVOKE_POLYMORPHIC ||
          inst.Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE);
-  const uint32_t proto_idx = inst.VRegH();
+  const dex::ProtoIndex proto_idx(inst.VRegH());
   const char* shorty = caller_method->GetDexFile()->GetShorty(proto_idx);
   const size_t shorty_length = strlen(shorty);
   static const bool kMethodIsStatic = false;  // invoke() and invokeExact() are not static.
@@ -2789,13 +2791,6 @@
     return static_cast<uintptr_t>('V');
   }
 
-  // TODO(oth): Ensure this path isn't taken for VarHandle accessors (b/65872996).
-  DCHECK_EQ(resolved_method->GetDeclaringClass(),
-            WellKnownClasses::ToClass(WellKnownClasses::java_lang_invoke_MethodHandle));
-
-  Handle<mirror::MethodHandle> method_handle(hs.NewHandle(
-      ObjPtr<mirror::MethodHandle>::DownCast(MakeObjPtr(receiver_handle.Get()))));
-
   Handle<mirror::MethodType> method_type(
       hs.NewHandle(linker->ResolveMethodType(self, proto_idx, caller_method)));
 
@@ -2835,24 +2830,43 @@
   // Call DoInvokePolymorphic with |is_range| = true, as shadow frame has argument registers in
   // consecutive order.
   RangeInstructionOperands operands(first_arg + 1, num_vregs - 1);
-  bool isExact = (jni::EncodeArtMethod(resolved_method) ==
-                  WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact);
+  Intrinsics intrinsic = static_cast<Intrinsics>(resolved_method->GetIntrinsic());
   bool success = false;
-  if (isExact) {
-    success = MethodHandleInvokeExact(self,
+  if (resolved_method->GetDeclaringClass() == mirror::MethodHandle::StaticClass()) {
+    Handle<mirror::MethodHandle> method_handle(hs.NewHandle(
+        ObjPtr<mirror::MethodHandle>::DownCast(MakeObjPtr(receiver_handle.Get()))));
+    if (intrinsic == Intrinsics::kMethodHandleInvokeExact) {
+      success = MethodHandleInvokeExact(self,
+                                        *shadow_frame,
+                                        method_handle,
+                                        method_type,
+                                        &operands,
+                                        result);
+    } else {
+      DCHECK_EQ(static_cast<uint32_t>(intrinsic),
+                static_cast<uint32_t>(Intrinsics::kMethodHandleInvoke));
+      success = MethodHandleInvoke(self,
+                                   *shadow_frame,
+                                   method_handle,
+                                   method_type,
+                                   &operands,
+                                   result);
+    }
+  } else {
+    DCHECK_EQ(mirror::VarHandle::StaticClass(), resolved_method->GetDeclaringClass());
+    Handle<mirror::VarHandle> var_handle(hs.NewHandle(
+        ObjPtr<mirror::VarHandle>::DownCast(MakeObjPtr(receiver_handle.Get()))));
+    mirror::VarHandle::AccessMode access_mode =
+        mirror::VarHandle::GetAccessModeByIntrinsic(intrinsic);
+    success = VarHandleInvokeAccessor(self,
                                       *shadow_frame,
-                                      method_handle,
+                                      var_handle,
                                       method_type,
+                                      access_mode,
                                       &operands,
                                       result);
-  } else {
-    success = MethodHandleInvoke(self,
-                                 *shadow_frame,
-                                 method_handle,
-                                 method_type,
-                                 &operands,
-                                 result);
   }
+
   DCHECK(success || self->IsExceptionPending());
 
   // Pop transition record.
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index 9354d72..ee518ae 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#include <metricslogger/metrics_logger.h>
-
 #include "hidden_api.h"
 
 #include <nativehelper/scoped_local_ref.h>
@@ -24,11 +22,14 @@
 #include "thread-current-inl.h"
 #include "well_known_classes.h"
 
+#ifdef ART_TARGET_ANDROID
+#include <metricslogger/metrics_logger.h>
 using android::metricslogger::ComplexEventLogger;
 using android::metricslogger::ACTION_HIDDEN_API_ACCESSED;
 using android::metricslogger::FIELD_HIDDEN_API_ACCESS_METHOD;
 using android::metricslogger::FIELD_HIDDEN_API_ACCESS_DENIED;
 using android::metricslogger::FIELD_HIDDEN_API_SIGNATURE;
+#endif
 
 namespace art {
 namespace hiddenapi {
@@ -137,6 +138,7 @@
   LOG(WARNING) << "Accessing hidden " << (type_ == kField ? "field " : "method ")
                << Dumpable<MemberSignature>(*this) << " (" << list << ", " << access_method << ")";
 }
+#ifdef ART_TARGET_ANDROID
 // Convert an AccessMethod enum to a value for logging from the proto enum.
 // This method may look odd (the enum values are current the same), but it
 // prevents coupling the internal enum to the proto enum (which should never
@@ -156,8 +158,10 @@
       DCHECK(false);
   }
 }
+#endif
 
 void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action action_taken) {
+#ifdef ART_TARGET_ANDROID
   if (access_method == kLinking || access_method == kNone) {
     // Linking warnings come from static analysis/compilation of the bytecode
     // and can contain false positives (i.e. code that is never run). We choose
@@ -174,6 +178,10 @@
   Dump(signature_str);
   log_maker.AddTaggedData(FIELD_HIDDEN_API_SIGNATURE, signature_str.str());
   log_maker.Record();
+#else
+  UNUSED(access_method);
+  UNUSED(action_taken);
+#endif
 }
 
 static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) {
@@ -228,7 +236,7 @@
     }
   }
 
-  if (kIsTargetBuild) {
+  if (kIsTargetBuild && !kIsTargetLinux) {
     uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
     // Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
     static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 8e21fd3..580224e 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -95,6 +95,22 @@
   }
 }
 
+class ScopedHiddenApiEnforcementPolicySetting {
+ public:
+  explicit ScopedHiddenApiEnforcementPolicySetting(EnforcementPolicy new_policy)
+      : initial_policy_(Runtime::Current()->GetHiddenApiEnforcementPolicy()) {
+    Runtime::Current()->SetHiddenApiEnforcementPolicy(new_policy);
+  }
+
+  ~ScopedHiddenApiEnforcementPolicySetting() {
+    Runtime::Current()->SetHiddenApiEnforcementPolicy(initial_policy_);
+  }
+
+ private:
+  const EnforcementPolicy initial_policy_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedHiddenApiEnforcementPolicySetting);
+};
+
 // Implementation details. DO NOT ACCESS DIRECTLY.
 namespace detail {
 
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 8a85ee4..5a50ec5 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -24,7 +24,7 @@
 #include "entrypoints/runtime_asm_entrypoints.h"
 #include "intrinsics_enum.h"
 #include "jit/jit.h"
-#include "jvalue.h"
+#include "jvalue-inl.h"
 #include "method_handles-inl.h"
 #include "method_handles.h"
 #include "mirror/array-inl.h"
@@ -37,6 +37,7 @@
 #include "stack.h"
 #include "thread-inl.h"
 #include "transaction.h"
+#include "var_handles.h"
 #include "well_known_classes.h"
 
 namespace art {
@@ -626,7 +627,8 @@
 
   // The vRegH value gives the index of the proto_id associated with this
   // signature polymorphic call site.
-  const uint32_t callsite_proto_id = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc();
+  const uint16_t vRegH = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc();
+  const dex::ProtoIndex callsite_proto_id(vRegH);
 
   // Call through to the classlinker and ask it to resolve the static type associated
   // with the callsite. This information is stored in the dex cache so it's
@@ -724,38 +726,6 @@
   }
 }
 
-static bool DoVarHandleInvokeChecked(Thread* self,
-                                     Handle<mirror::VarHandle> var_handle,
-                                     Handle<mirror::MethodType> callsite_type,
-                                     mirror::VarHandle::AccessMode access_mode,
-                                     ShadowFrame& shadow_frame,
-                                     InstructionOperands* operands,
-                                     JValue* result)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  // TODO(oth): GetMethodTypeForAccessMode() allocates a MethodType()
-  // which is only required if we need to convert argument and/or
-  // return types.
-  StackHandleScope<1> hs(self);
-  Handle<mirror::MethodType> accessor_type(hs.NewHandle(
-      var_handle->GetMethodTypeForAccessMode(self, access_mode)));
-  const size_t num_vregs = accessor_type->NumberOfVRegs();
-  const int num_params = accessor_type->GetPTypes()->GetLength();
-  ShadowFrameAllocaUniquePtr accessor_frame =
-      CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC());
-  ShadowFrameGetter getter(shadow_frame, operands);
-  static const uint32_t kFirstDestinationReg = 0;
-  ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg);
-  if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) {
-    return false;
-  }
-  RangeInstructionOperands accessor_operands(kFirstDestinationReg,
-                                             kFirstDestinationReg + num_vregs);
-  if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) {
-    return false;
-  }
-  return ConvertReturnValue(callsite_type, accessor_type, result);
-}
-
 static bool DoVarHandleInvokeCommon(Thread* self,
                                     ShadowFrame& shadow_frame,
                                     const Instruction* inst,
@@ -768,59 +738,43 @@
     return false;
   }
 
-  bool is_var_args = inst->HasVarArgs();
-  const uint32_t vRegC = is_var_args ? inst->VRegC_45cc() : inst->VRegC_4rcc();
-  ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(vRegC));
-  if (receiver.IsNull()) {
-    ThrowNullPointerExceptionFromDexPC();
-    return false;
-  }
-
   StackHandleScope<2> hs(self);
-  Handle<mirror::VarHandle> var_handle(hs.NewHandle(down_cast<mirror::VarHandle*>(receiver.Ptr())));
-  if (!var_handle->IsAccessModeSupported(access_mode)) {
-    ThrowUnsupportedOperationException();
-    return false;
-  }
-
-  const uint32_t vRegH = is_var_args ? inst->VRegH_45cc() : inst->VRegH_4rcc();
+  bool is_var_args = inst->HasVarArgs();
+  const uint16_t vRegH = is_var_args ? inst->VRegH_45cc() : inst->VRegH_4rcc();
   ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
   Handle<mirror::MethodType> callsite_type(hs.NewHandle(
-      class_linker->ResolveMethodType(self, vRegH, shadow_frame.GetMethod())));
+      class_linker->ResolveMethodType(self, dex::ProtoIndex(vRegH), shadow_frame.GetMethod())));
   // This implies we couldn't resolve one or more types in this VarHandle.
   if (UNLIKELY(callsite_type == nullptr)) {
     CHECK(self->IsExceptionPending());
     return false;
   }
 
-  if (!var_handle->IsMethodTypeCompatible(access_mode, callsite_type.Get())) {
-    ThrowWrongMethodTypeException(var_handle->GetMethodTypeForAccessMode(self, access_mode),
-                                  callsite_type.Get());
-    return false;
-  }
-
+  const uint32_t vRegC = is_var_args ? inst->VRegC_45cc() : inst->VRegC_4rcc();
+  ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(vRegC));
+  Handle<mirror::VarHandle> var_handle(hs.NewHandle(down_cast<mirror::VarHandle*>(receiver.Ptr())));
   if (is_var_args) {
     uint32_t args[Instruction::kMaxVarArgRegs];
     inst->GetVarArgs(args, inst_data);
     VarArgsInstructionOperands all_operands(args, inst->VRegA_45cc());
     NoReceiverInstructionOperands operands(&all_operands);
-    return DoVarHandleInvokeChecked(self,
-                                    var_handle,
-                                    callsite_type,
-                                    access_mode,
-                                    shadow_frame,
-                                    &operands,
-                                    result);
+    return VarHandleInvokeAccessor(self,
+                                   shadow_frame,
+                                   var_handle,
+                                   callsite_type,
+                                   access_mode,
+                                   &operands,
+                                   result);
   } else {
     RangeInstructionOperands all_operands(inst->VRegC_4rcc(), inst->VRegA_4rcc());
     NoReceiverInstructionOperands operands(&all_operands);
-    return DoVarHandleInvokeChecked(self,
-                                    var_handle,
-                                    callsite_type,
-                                    access_mode,
-                                    shadow_frame,
-                                    &operands,
-                                    result);
+    return VarHandleInvokeAccessor(self,
+                                   shadow_frame,
+                                   var_handle,
+                                   callsite_type,
+                                   access_mode,
+                                   &operands,
+                                   result);
   }
 }
 
@@ -965,9 +919,10 @@
       StackHandleScope<2> hs(self);
       Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
       Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
-      uint32_t index = static_cast<uint32_t>(encoded_value->GetI());
+      dex::ProtoIndex proto_idx(encoded_value->GetC());
       ClassLinker* cl = Runtime::Current()->GetClassLinker();
-      ObjPtr<mirror::MethodType> o = cl->ResolveMethodType(self, index, dex_cache, class_loader);
+      ObjPtr<mirror::MethodType> o =
+          cl->ResolveMethodType(self, proto_idx, dex_cache, class_loader);
       if (UNLIKELY(o.IsNull())) {
         DCHECK(self->IsExceptionPending());
         return false;
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 0818e06..67a0349 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -217,7 +217,7 @@
 }
 
 static inline ObjPtr<mirror::MethodType> ResolveMethodType(Thread* self,
-                                                           uint32_t method_type_index,
+                                                           dex::ProtoIndex method_type_index,
                                                            ArtMethod* referrer)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 283885e..5c7838c 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -565,7 +565,7 @@
         PREAMBLE();
         ClassLinker* cl = Runtime::Current()->GetClassLinker();
         ObjPtr<mirror::MethodType> mt = cl->ResolveMethodType(self,
-                                                              inst->VRegB_21c(),
+                                                              dex::ProtoIndex(inst->VRegB_21c()),
                                                               shadow_frame.GetMethod());
         if (UNLIKELY(mt == nullptr)) {
           HANDLE_PENDING_EXCEPTION();
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 2a9ef2c..1b39a74 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -408,7 +408,8 @@
                                        ShadowFrame* shadow_frame,
                                        Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  ObjPtr<mirror::MethodType> mt = ResolveMethodType(self, index, shadow_frame->GetMethod());
+  ObjPtr<mirror::MethodType> mt =
+      ResolveMethodType(self, dex::ProtoIndex(index), shadow_frame->GetMethod());
   if (UNLIKELY(mt == nullptr)) {
     return true;
   }
diff --git a/runtime/jvalue-inl.h b/runtime/jvalue-inl.h
index 25e34b2..5bd4f17 100644
--- a/runtime/jvalue-inl.h
+++ b/runtime/jvalue-inl.h
@@ -19,7 +19,7 @@
 
 #include "jvalue.h"
 
-#include "obj_ptr.h"
+#include "obj_ptr-inl.h"
 
 namespace art {
 
diff --git a/runtime/jvalue.h b/runtime/jvalue.h
index 266abcf..b42d995 100644
--- a/runtime/jvalue.h
+++ b/runtime/jvalue.h
@@ -33,7 +33,7 @@
   // We default initialize JValue instances to all-zeros.
   JValue() : j(0) {}
 
-  template<typename T> static JValue FromPrimitive(T v);
+  template<typename T> ALWAYS_INLINE static JValue FromPrimitive(T v);
 
   int8_t GetB() const { return b; }
   void SetB(int8_t new_b) {
@@ -62,6 +62,7 @@
   mirror::Object* GetL() const REQUIRES_SHARED(Locks::mutator_lock_) {
     return l;
   }
+  ALWAYS_INLINE
   void SetL(ObjPtr<mirror::Object> new_l) REQUIRES_SHARED(Locks::mutator_lock_);
 
   int16_t GetS() const { return s; }
diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h
index 41c8384..00a8c00 100644
--- a/runtime/method_handles-inl.h
+++ b/runtime/method_handles-inl.h
@@ -22,7 +22,7 @@
 #include "common_throws.h"
 #include "dex/dex_instruction.h"
 #include "interpreter/interpreter_common.h"
-#include "jvalue.h"
+#include "jvalue-inl.h"
 #include "mirror/class.h"
 #include "mirror/method_type.h"
 #include "mirror/object.h"
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 64ab789..1d45aae 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -20,7 +20,6 @@
 
 #include "common_dex_operations.h"
 #include "jvalue-inl.h"
-#include "jvalue.h"
 #include "mirror/emulated_stack_frame.h"
 #include "mirror/method_handle_impl-inl.h"
 #include "mirror/method_type.h"
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 7a4876c..72f1443 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -127,23 +127,23 @@
   }
 }
 
-inline uint32_t DexCache::MethodTypeSlotIndex(uint32_t proto_idx) {
+inline uint32_t DexCache::MethodTypeSlotIndex(dex::ProtoIndex proto_idx) {
   DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
-  DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds());
-  const uint32_t slot_idx = proto_idx % kDexCacheMethodTypeCacheSize;
+  DCHECK_LT(proto_idx.index_, GetDexFile()->NumProtoIds());
+  const uint32_t slot_idx = proto_idx.index_ % kDexCacheMethodTypeCacheSize;
   DCHECK_LT(slot_idx, NumResolvedMethodTypes());
   return slot_idx;
 }
 
-inline MethodType* DexCache::GetResolvedMethodType(uint32_t proto_idx) {
+inline MethodType* DexCache::GetResolvedMethodType(dex::ProtoIndex proto_idx) {
   return GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].load(
-      std::memory_order_relaxed).GetObjectForIndex(proto_idx);
+      std::memory_order_relaxed).GetObjectForIndex(proto_idx.index_);
 }
 
-inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) {
+inline void DexCache::SetResolvedMethodType(dex::ProtoIndex proto_idx, MethodType* resolved) {
   DCHECK(resolved != nullptr);
   GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].store(
-      MethodTypeDexCachePair(resolved, proto_idx), std::memory_order_relaxed);
+      MethodTypeDexCachePair(resolved, proto_idx.index_), std::memory_order_relaxed);
   // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
   Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
 }
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index d940964..9aff9ec 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -307,9 +307,9 @@
   ALWAYS_INLINE void ClearResolvedField(uint32_t idx, PointerSize ptr_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  MethodType* GetResolvedMethodType(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+  MethodType* GetResolvedMethodType(dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved)
+  void SetResolvedMethodType(dex::ProtoIndex proto_idx, MethodType* resolved)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   CallSite* GetResolvedCallSite(uint32_t call_site_idx) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -432,7 +432,7 @@
   uint32_t TypeSlotIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
   uint32_t FieldSlotIndex(uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_);
   uint32_t MethodSlotIndex(uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_);
-  uint32_t MethodTypeSlotIndex(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+  uint32_t MethodTypeSlotIndex(dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
 
  private:
   void Init(const DexFile* dex_file,
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index d2bff2c..97e0ce6 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -169,9 +169,9 @@
 
   for (size_t i = 0; i < dex_file.NumProtoIds(); ++i) {
     const MethodTypeDexCachePair pair = method_types_cache[i].load(std::memory_order_relaxed);
-    if (pair.index == method1_id.proto_idx_) {
+    if (dex::ProtoIndex(pair.index) == method1_id.proto_idx_) {
       ASSERT_EQ(method1_type.Get(), pair.object.Read());
-    } else if (pair.index == method2_id.proto_idx_) {
+    } else if (dex::ProtoIndex(pair.index) == method2_id.proto_idx_) {
       ASSERT_EQ(method2_type.Get(), pair.object.Read());
     } else {
       ASSERT_TRUE(false);
diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc
index b309f59..d31e06c 100644
--- a/runtime/mirror/var_handle.cc
+++ b/runtime/mirror/var_handle.cc
@@ -1425,21 +1425,24 @@
   return GetField32(AccessModesBitMaskOffset());
 }
 
-bool VarHandle::IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type) {
-  StackHandleScope<3> hs(Thread::Current());
-  Handle<Class> mt_rtype(hs.NewHandle(method_type->GetRType()));
-  Handle<VarHandle> vh(hs.NewHandle(this));
-  Handle<Class> var_type(hs.NewHandle(vh->GetVarType()));
+VarHandle::MatchKind VarHandle::GetMethodTypeMatchForAccessMode(AccessMode access_mode,
+                                                                MethodType* method_type) {
+  MatchKind match = MatchKind::kExact;
+
+  ObjPtr<VarHandle> vh = this;
+  ObjPtr<Class> var_type = vh->GetVarType();
+  ObjPtr<Class> mt_rtype = method_type->GetRType();
   AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode);
 
-  // Check return type first.
-  if (mt_rtype->GetPrimitiveType() == Primitive::Type::kPrimVoid) {
-    // The result of the operation will be discarded. The return type
-    // of the VarHandle is immaterial.
-  } else {
-    ObjPtr<Class> vh_rtype(GetReturnType(access_mode_template, var_type.Get()));
-    if (!IsReturnTypeConvertible(vh_rtype, mt_rtype.Get())) {
-      return false;
+  // Check return type first. If the return type of the method
+  // of the VarHandle is immaterial.
+  if (mt_rtype->GetPrimitiveType() != Primitive::Type::kPrimVoid) {
+    ObjPtr<Class> vh_rtype = GetReturnType(access_mode_template, var_type.Ptr());
+    if (vh_rtype != mt_rtype) {
+      if (!IsReturnTypeConvertible(vh_rtype, mt_rtype)) {
+        return MatchKind::kNone;
+      }
+      match = MatchKind::kWithConversions;
     }
   }
 
@@ -1447,21 +1450,25 @@
   ObjPtr<Class> vh_ptypes[VarHandle::kMaxAccessorParameters];
   const int32_t vh_ptypes_count = BuildParameterArray(vh_ptypes,
                                                       access_mode_template,
-                                                      var_type.Get(),
+                                                      var_type,
                                                       GetCoordinateType0(),
                                                       GetCoordinateType1());
   if (vh_ptypes_count != method_type->GetPTypes()->GetLength()) {
-    return false;
+    return MatchKind::kNone;
   }
 
   // Check the parameter types are compatible.
   ObjPtr<ObjectArray<Class>> mt_ptypes = method_type->GetPTypes();
   for (int32_t i = 0; i < vh_ptypes_count; ++i) {
-    if (!IsParameterTypeConvertible(mt_ptypes->Get(i), vh_ptypes[i])) {
-      return false;
+    if (mt_ptypes->Get(i) == vh_ptypes[i]) {
+      continue;
     }
+    if (!IsParameterTypeConvertible(mt_ptypes->Get(i), vh_ptypes[i])) {
+      return MatchKind::kNone;
+    }
+    match = MatchKind::kWithConversions;
   }
-  return true;
+  return match;
 }
 
 bool VarHandle::IsInvokerMethodTypeCompatible(AccessMode access_mode,
@@ -1508,7 +1515,7 @@
 MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self,
                                                   ObjPtr<VarHandle> var_handle,
                                                   AccessMode access_mode) {
-  // This is a static as the var_handle might be moved by the GC during it's execution.
+  // This is a static method as the var_handle might be moved by the GC during it's execution.
   AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode);
 
   StackHandleScope<3> hs(self);
@@ -1540,7 +1547,7 @@
 
 bool VarHandle::Access(AccessMode access_mode,
                        ShadowFrame* shadow_frame,
-                       InstructionOperands* operands,
+                       const InstructionOperands* const operands,
                        JValue* result) {
   Class* klass = GetClass();
   if (klass == FieldVarHandle::StaticClass()) {
@@ -1671,7 +1678,7 @@
 
 bool FieldVarHandle::Access(AccessMode access_mode,
                             ShadowFrame* shadow_frame,
-                            InstructionOperands* operands,
+                            const InstructionOperands* const operands,
                             JValue* result) {
   ShadowFrameGetter getter(*shadow_frame, operands);
   ArtField* field = GetField();
@@ -1743,7 +1750,7 @@
 
 bool ArrayElementVarHandle::Access(AccessMode access_mode,
                                    ShadowFrame* shadow_frame,
-                                   InstructionOperands* operands,
+                                   const InstructionOperands* const operands,
                                    JValue* result) {
   ShadowFrameGetter getter(*shadow_frame, operands);
 
@@ -1856,7 +1863,7 @@
 
 bool ByteArrayViewVarHandle::Access(AccessMode access_mode,
                                     ShadowFrame* shadow_frame,
-                                    InstructionOperands* operands,
+                                    const InstructionOperands* const operands,
                                     JValue* result) {
   ShadowFrameGetter getter(*shadow_frame, operands);
 
@@ -1965,7 +1972,7 @@
 
 bool ByteBufferViewVarHandle::Access(AccessMode access_mode,
                                      ShadowFrame* shadow_frame,
-                                     InstructionOperands* operands,
+                                     const InstructionOperands* const operands,
                                      JValue* result) {
   ShadowFrameGetter getter(*shadow_frame, operands);
 
diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h
index d46d900..eb3704e 100644
--- a/runtime/mirror/var_handle.h
+++ b/runtime/mirror/var_handle.h
@@ -99,14 +99,16 @@
     return (GetAccessModesBitMask() & (1u << static_cast<uint32_t>(accessMode))) != 0;
   }
 
-  // Returns true if the MethodType specified is compatible with the
-  // method type associated with the specified AccessMode. The
-  // supplied MethodType is assumed to be from the point of invocation
-  // so it is valid for the supplied MethodType to have a void return
-  // value when the return value for the AccessMode is non-void. This
-  // corresponds to the result of the accessor being discarded.
-  bool IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type)
-      REQUIRES_SHARED(Locks::mutator_lock_);
+  enum MatchKind : uint8_t {
+    kNone,
+    kWithConversions,
+    kExact
+  };
+
+  // Returns match information on the compatability between the exact method type for
+  // 'access_mode' and the provided 'method_type'.
+  MatchKind GetMethodTypeMatchForAccessMode(AccessMode access_mode, MethodType* method_type)
+        REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Returns true if the MethodType specified is compatible with the
   // specified access_mode if the first parameter of method_type is
@@ -124,7 +126,7 @@
 
   bool Access(AccessMode access_mode,
               ShadowFrame* shadow_frame,
-              InstructionOperands* operands,
+              const InstructionOperands* const operands,
               JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -192,7 +194,7 @@
  public:
   bool Access(AccessMode access_mode,
               ShadowFrame* shadow_frame,
-              InstructionOperands* operands,
+              const InstructionOperands* const operands,
               JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -225,7 +227,7 @@
  public:
     bool Access(AccessMode access_mode,
                 ShadowFrame* shadow_frame,
-                InstructionOperands* operands,
+                const InstructionOperands* const operands,
                 JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -248,7 +250,7 @@
  public:
   bool Access(AccessMode access_mode,
               ShadowFrame* shadow_frame,
-              InstructionOperands* operands,
+              const InstructionOperands* const operands,
               JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -281,7 +283,7 @@
  public:
   bool Access(AccessMode access_mode,
               ShadowFrame* shadow_frame,
-              InstructionOperands* operands,
+              const InstructionOperands* const operands,
               JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc
index d9fa07f..005aba3 100644
--- a/runtime/mirror/var_handle_test.cc
+++ b/runtime/mirror/var_handle_test.cc
@@ -246,6 +246,47 @@
   return MethodType::Create(self, rtype, ptypes);
 }
 
+static bool AccessModeMatch(VarHandle* vh,
+                            VarHandle::AccessMode access_mode,
+                            MethodType* method_type,
+                            VarHandle::MatchKind expected_match)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return vh->GetMethodTypeMatchForAccessMode(access_mode, method_type) == expected_match;
+}
+
+template <typename VH>
+static bool AccessModeExactMatch(Handle<VH> vh,
+                                 VarHandle::AccessMode access_mode,
+                                 const char* descriptor)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return AccessModeMatch(vh.Get(),
+                         access_mode,
+                         MethodTypeOf(descriptor),
+                         VarHandle::MatchKind::kExact);
+}
+
+template <typename VH>
+static bool AccessModeWithConversionsMatch(Handle<VH> vh,
+                                          VarHandle::AccessMode access_mode,
+                                          const char* descriptor)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return AccessModeMatch(vh.Get(),
+                         access_mode,
+                         MethodTypeOf(descriptor),
+                         VarHandle::MatchKind::kWithConversions);
+}
+
+template <typename VH>
+static bool AccessModeNoMatch(Handle<VH> vh,
+                              VarHandle::AccessMode access_mode,
+                              const char* descriptor)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return AccessModeMatch(vh.Get(),
+                         access_mode,
+                         MethodTypeOf(descriptor),
+                         VarHandle::MatchKind::kNone);
+}
+
 TEST_F(VarHandleTest, InstanceFieldVarHandle) {
   Thread * const self = Thread::Current();
   ScopedObjectAccess soa(self);
@@ -296,47 +337,53 @@
   // Check compatibility - "Get" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;)I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;)V"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;)D"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)Z"));
   }
 
   // Check compatibility - "Set" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)V"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;S)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndSet" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode,
-                                             MethodTypeOf("(Ljava/lang/Integer;II)I")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)Z"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)V"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;II)Ljava/lang/Boolean;"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;IB)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;II)I"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndExchange" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(IIII)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)V"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;II)J"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;BS)F"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(IIII)V"));
   }
 
   // Check compatibility - "GetAndUpdate" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)S")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)S"));
   }
 
   // Check synthesized method types match expected forms.
@@ -430,48 +477,47 @@
   // Check compatibility - "Get" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "()I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "()V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)Z"));
   }
 
   // Check compatibility - "Set" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(F)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(F)V"));
   }
 
   // Check compatibility - "CompareAndSet" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode,
-                                             MethodTypeOf("(II)Ljava/lang/String;")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)Ljava/lang/String;"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndExchange" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(ID)I")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)S")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(IIJ)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(ID)I"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)S"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(IIJ)V"));
   }
 
   // Check compatibility - "GetAndUpdate" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)V"));
   }
 
   // Check synthesized method types match expected forms.
@@ -594,50 +640,46 @@
   // Check compatibility - "Get" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Ljava/lang/String;")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;Ljava/lang/String;)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;I)Ljava/lang/String;"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;Ljava/lang/String;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z"));
   }
 
   // Check compatibility - "Set" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndSet" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
-    EXPECT_TRUE(
-        vh->IsMethodTypeCompatible(
-            access_mode,
-            MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode,
-                                             MethodTypeOf("([Ljava/lang/String;III)I")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;III)I"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndExchange" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;II)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;II)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V"));
   }
 
   // Check compatibility - "GetAndUpdate" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V"));
   }
 
   // Check synthesized method types match expected forms.
@@ -747,50 +789,46 @@
   // Check compatibility - "Get" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)C")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BC)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BI)C"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BI)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BC)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z"));
   }
 
   // Check compatibility - "Set" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndSet" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
-    EXPECT_TRUE(
-        vh->IsMethodTypeCompatible(
-            access_mode,
-            MethodTypeOf("([BICC)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode,
-                                             MethodTypeOf("([BIII)I")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BIII)I"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndExchange" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BICC)C")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BICC)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BII)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)C"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BII)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V"));
   }
 
   // Check compatibility - "GetAndUpdate" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)C")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)C"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BIC)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V"));
   }
 
   // Check synthesized method types match expected forms.
@@ -900,50 +938,46 @@
   // Check compatibility - "Get" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)D")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;D)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)D"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;D)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z"));
   }
 
   // Check compatibility - "Set" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndSet" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
-    EXPECT_TRUE(
-        vh->IsMethodTypeCompatible(
-            access_mode,
-            MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode,
-                                             MethodTypeOf("(Ljava/nio/ByteBuffer;IDI)D")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDI)D"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndExchange" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)D")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;II)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)D"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;II)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V"));
   }
 
   // Check compatibility - "GetAndUpdate" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)D")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)D"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V"));
   }
 
   // Check synthesized method types match expected forms.
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index e0afbee..2559984 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -27,6 +27,7 @@
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_annotations.h"
 #include "jni/jni_internal.h"
+#include "jvalue-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/field-inl.h"
 #include "native_util.h"
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index c00e47f..6c869ca 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -36,6 +36,7 @@
 #include "exec_utils.h"
 #include "gc/heap.h"
 #include "gc/space/image_space.h"
+#include "hidden_api.h"
 #include "image.h"
 #include "oat.h"
 #include "runtime.h"
@@ -823,6 +824,11 @@
     argv.push_back("--compiler-filter=verify-none");
   }
 
+  if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kNoChecks) {
+    argv.push_back("--runtime-arg");
+    argv.push_back("-Xhidden-api-checks");
+  }
+
   if (runtime->MustRelocateIfPossible()) {
     argv.push_back("--runtime-arg");
     argv.push_back("-Xrelocate");
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 99bc0b2..0b3c61d 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -33,6 +33,7 @@
 #include "class_loader_context.h"
 #include "common_runtime_test.h"
 #include "dexopt_test.h"
+#include "hidden_api.h"
 #include "oat_file.h"
 #include "oat_file_manager.h"
 #include "scoped_thread_state_change-inl.h"
@@ -43,6 +44,8 @@
 static const std::string kSpecialSharedLibrary = "&";  // NOLINT [runtime/string] [4]
 static ClassLoaderContext* kSpecialSharedLibraryContext = nullptr;
 
+static constexpr char kDex2oatCmdLineHiddenApiArg[] = " --runtime-arg -Xhidden-api-checks";
+
 class OatFileAssistantTest : public DexoptTest {
  public:
   void VerifyOptimizationStatus(const std::string& file,
@@ -1413,6 +1416,46 @@
       oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
 }
 
+TEST_F(OatFileAssistantTest, MakeUpToDateWithHiddenApiDisabled) {
+  hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
+      hiddenapi::EnforcementPolicy::kNoChecks);
+
+  std::string dex_location = GetScratchDir() + "/TestDexHiddenApiDisabled.jar";
+  Copy(GetDexSrc1(), dex_location);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  std::string error_msg;
+  int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
+  EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  EXPECT_NE(nullptr, oat_file.get());
+
+  const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey);
+  EXPECT_NE(nullptr, cmd_line);
+  EXPECT_EQ(nullptr, strstr(cmd_line, kDex2oatCmdLineHiddenApiArg));
+}
+
+TEST_F(OatFileAssistantTest, MakeUpToDateWithHiddenApiEnabled) {
+  hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
+      hiddenapi::EnforcementPolicy::kBlacklistOnly);
+
+  std::string dex_location = GetScratchDir() + "/TestDexHiddenApiEnabled.jar";
+  Copy(GetDexSrc1(), dex_location);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  std::string error_msg;
+  int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
+  EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  EXPECT_NE(nullptr, oat_file.get());
+
+  const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey);
+  EXPECT_NE(nullptr, cmd_line);
+  EXPECT_NE(nullptr, strstr(cmd_line, kDex2oatCmdLineHiddenApiArg));
+}
+
 TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) {
   std::string dex_location = GetScratchDir() + "/TestDex.jar";
   std::string context_location = GetScratchDir() + "/ContextDex.jar";
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index dfa4b3d..66eba1e 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -25,6 +25,7 @@
 #include "indirect_reference_table-inl.h"
 #include "jni/java_vm_ext.h"
 #include "jni/jni_internal.h"
+#include "jvalue-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/executable.h"
 #include "mirror/object_array-inl.h"
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index b8775b8..4142cb0 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1198,7 +1198,7 @@
   // As is, we're encoding some logic here about which specific policy to use, which would be better
   // controlled by the framework.
   hidden_api_policy_ = do_hidden_api_checks
-      ? hiddenapi::EnforcementPolicy::kBlacklistOnly
+      ? hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList
       : hiddenapi::EnforcementPolicy::kNoChecks;
 
   no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain);
diff --git a/runtime/var_handles.cc b/runtime/var_handles.cc
new file mode 100644
index 0000000..d71745e
--- /dev/null
+++ b/runtime/var_handles.cc
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 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 "var_handles.h"
+
+#include "common_throws.h"
+#include "dex/dex_instruction.h"
+#include "handle.h"
+#include "method_handles-inl.h"
+#include "mirror/method_type.h"
+#include "mirror/var_handle.h"
+
+namespace art {
+
+bool VarHandleInvokeAccessor(Thread* self,
+                             ShadowFrame& shadow_frame,
+                             Handle<mirror::VarHandle> var_handle,
+                             Handle<mirror::MethodType> callsite_type,
+                             const mirror::VarHandle::AccessMode access_mode,
+                             const InstructionOperands* const operands,
+                             JValue* result) {
+  if (var_handle.IsNull()) {
+    ThrowNullPointerExceptionFromDexPC();
+    return false;
+  }
+
+  if (!var_handle->IsAccessModeSupported(access_mode)) {
+    ThrowUnsupportedOperationException();
+    return false;
+  }
+
+  mirror::VarHandle::MatchKind match_kind =
+      var_handle->GetMethodTypeMatchForAccessMode(access_mode, callsite_type.Get());
+  if (match_kind == mirror::VarHandle::MatchKind::kExact) {
+    return var_handle->Access(access_mode, &shadow_frame, operands, result);
+  }
+  if (match_kind == mirror::VarHandle::MatchKind::kNone) {
+    ThrowWrongMethodTypeException(var_handle->GetMethodTypeForAccessMode(self, access_mode),
+                                  callsite_type.Get());
+    return false;
+  }
+  DCHECK_EQ(mirror::VarHandle::MatchKind::kWithConversions, match_kind);
+
+  StackHandleScope<1> hs(self);
+  Handle<mirror::MethodType> accessor_type(hs.NewHandle(
+      var_handle->GetMethodTypeForAccessMode(self, access_mode)));
+  const size_t num_vregs = accessor_type->NumberOfVRegs();
+  const int num_params = accessor_type->GetPTypes()->GetLength();
+  ShadowFrameAllocaUniquePtr accessor_frame =
+      CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC());
+  ShadowFrameGetter getter(shadow_frame, operands);
+  static const uint32_t kFirstDestinationReg = 0;
+  ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg);
+  if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) {
+    return false;
+  }
+  RangeInstructionOperands accessor_operands(kFirstDestinationReg,
+                                             kFirstDestinationReg + num_vregs);
+  if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) {
+    return false;
+  }
+  return ConvertReturnValue(callsite_type, accessor_type, result);
+}
+
+}  // namespace art
diff --git a/runtime/var_handles.h b/runtime/var_handles.h
new file mode 100644
index 0000000..2ff8405
--- /dev/null
+++ b/runtime/var_handles.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_VAR_HANDLES_H_
+#define ART_RUNTIME_VAR_HANDLES_H_
+
+#include "mirror/var_handle.h"
+
+namespace art {
+
+bool VarHandleInvokeAccessor(Thread* self,
+                             ShadowFrame& shadow_frame,
+                             Handle<mirror::VarHandle> var_handle,
+                             Handle<mirror::MethodType> callsite_type,
+                             const mirror::VarHandle::AccessMode access_mode,
+                             const InstructionOperands* const operands,
+                             JValue* result)
+    REQUIRES_SHARED(Locks::mutator_lock_);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_VAR_HANDLES_H_
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 3518d2f..91cec23 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3086,7 +3086,8 @@
         DCHECK(HasFailures());
         break;
       }
-      const uint32_t proto_idx = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc();
+      const uint16_t vRegH = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc();
+      const dex::ProtoIndex proto_idx(vRegH);
       const char* return_descriptor =
           dex_file_->GetReturnTypeDescriptor(dex_file_->GetProtoId(proto_idx));
       const RegType& return_type =
@@ -3117,7 +3118,7 @@
       CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx));
       it.Next();  // Skip to name.
       it.Next();  // Skip to method type of the method handle
-      const uint32_t proto_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+      const dex::ProtoIndex proto_idx(it.GetJavaValue().c);
       const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(proto_idx);
       DexFileParameterIterator param_it(*dex_file_, proto_id);
       // Treat method as static as it has yet to be determined.
@@ -4190,7 +4191,8 @@
 
   if (UNLIKELY(method_type == METHOD_POLYMORPHIC)) {
     // Process the signature of the calling site that is invoking the method handle.
-    DexFileParameterIterator it(*dex_file_, dex_file_->GetProtoId(inst->VRegH()));
+    dex::ProtoIndex proto_idx(inst->VRegH());
+    DexFileParameterIterator it(*dex_file_, dex_file_->GetProtoId(proto_idx));
     return VerifyInvocationArgsFromIterator(&it, inst, method_type, is_range, res_method);
   } else {
     // Process the target method's signature.
@@ -4208,8 +4210,6 @@
     expected_return_descriptor = mirror::MethodHandle::GetReturnTypeDescriptor(method_name);
   } else if (klass == mirror::VarHandle::StaticClass()) {
     expected_return_descriptor = mirror::VarHandle::GetReturnTypeDescriptor(method_name);
-    // TODO: add compiler support for VarHandle accessor methods (b/71781600)
-    Fail(VERIFY_ERROR_FORCE_INTERPRETER);
   } else {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD)
         << "Signature polymorphic method in unsuppported class: " << klass->PrettyDescriptor();
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index b79334a..4843061 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -282,26 +282,9 @@
 }
 #undef STRING_INIT_LIST
 
-class ScopedHiddenApiExemption {
- public:
-  explicit ScopedHiddenApiExemption(Runtime* runtime)
-      : runtime_(runtime),
-        initial_policy_(runtime_->GetHiddenApiEnforcementPolicy()) {
-    runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks);
-  }
-
-  ~ScopedHiddenApiExemption() {
-    runtime_->SetHiddenApiEnforcementPolicy(initial_policy_);
-  }
-
- private:
-  Runtime* runtime_;
-  const hiddenapi::EnforcementPolicy initial_policy_;
-  DISALLOW_COPY_AND_ASSIGN(ScopedHiddenApiExemption);
-};
-
 void WellKnownClasses::Init(JNIEnv* env) {
-  ScopedHiddenApiExemption hiddenapi_exemption(Runtime::Current());
+  hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
+      hiddenapi::EnforcementPolicy::kNoChecks);
 
   dalvik_annotation_optimization_CriticalNative =
       CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
diff --git a/test/999-redefine-hiddenapi/api-blacklist.txt b/test/999-redefine-hiddenapi/api-blacklist.txt
new file mode 100644
index 0000000..63e37aa
--- /dev/null
+++ b/test/999-redefine-hiddenapi/api-blacklist.txt
@@ -0,0 +1,2 @@
+Lart/Test999;->foo()V
+Lart/Test999;->bar:I
diff --git a/test/999-redefine-hiddenapi/build b/test/999-redefine-hiddenapi/build
new file mode 100644
index 0000000..f4b029f
--- /dev/null
+++ b/test/999-redefine-hiddenapi/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2018 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.
+
+USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/999-redefine-hiddenapi/expected.txt b/test/999-redefine-hiddenapi/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/999-redefine-hiddenapi/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/999-redefine-hiddenapi/info.txt b/test/999-redefine-hiddenapi/info.txt
new file mode 100644
index 0000000..87bc30c
--- /dev/null
+++ b/test/999-redefine-hiddenapi/info.txt
@@ -0,0 +1 @@
+Tests that JVMTI class redefinition does not strip away hidden API access flags.
diff --git a/test/999-redefine-hiddenapi/run b/test/999-redefine-hiddenapi/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/999-redefine-hiddenapi/run
@@ -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-run "$@" --jvmti
diff --git a/test/999-redefine-hiddenapi/src-ex/Test999.java b/test/999-redefine-hiddenapi/src-ex/Test999.java
new file mode 100644
index 0000000..97495c5
--- /dev/null
+++ b/test/999-redefine-hiddenapi/src-ex/Test999.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package art;
+
+public class Test999 {
+  public void foo() {
+    System.out.println("hello");
+  }
+
+  public int bar = 42;
+}
diff --git a/test/999-redefine-hiddenapi/src-redefine/art/Test999.java b/test/999-redefine-hiddenapi/src-redefine/art/Test999.java
new file mode 100644
index 0000000..c1b838c
--- /dev/null
+++ b/test/999-redefine-hiddenapi/src-redefine/art/Test999.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package art;
+
+public class Test999 {
+  public void foo() {
+    System.out.println("Goodbye");
+  }
+
+  public int bar = 64;
+}
diff --git a/test/999-redefine-hiddenapi/src-redefine/gen.sh b/test/999-redefine-hiddenapi/src-redefine/gen.sh
new file mode 100755
index 0000000..6948cbb
--- /dev/null
+++ b/test/999-redefine-hiddenapi/src-redefine/gen.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+TMP=`mktemp -d`
+
+CLASS "art/Test999"
+
+(cd "$TMP" && javac -d "${TMP}" "$DIR/${CLASS}.java" && d8 --output . "$TMP/${CLASS}.class")
+
+echo '  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode('
+base64 "${TMP}/${CLASS}.class" | sed -E 's/^/    "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/'
+echo '  private static final byte[] DEX_BYTES = Base64.getDecoder().decode('
+base64 "${TMP}/classes.dex" | sed -E 's/^/    "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/'
+
+rm -rf "$TMP"
diff --git a/test/999-redefine-hiddenapi/src/Main.java b/test/999-redefine-hiddenapi/src/Main.java
new file mode 100644
index 0000000..c6365ac
--- /dev/null
+++ b/test/999-redefine-hiddenapi/src/Main.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 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.io.File;
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[0]);
+
+    // Run the initialization routine. This will enable hidden API checks in
+    // the runtime, in case they are not enabled by default.
+    init();
+
+    // Load the '-ex' APK and attach it to the boot class path.
+    appendToBootClassLoader(DEX_EXTRA);
+
+    // Find the test class in boot class loader and verify that its members are hidden.
+    Class<?> klass = Class.forName("art.Test999", true, BOOT_CLASS_LOADER);
+    assertMethodIsHidden(klass, "before redefinition");
+    assertFieldIsHidden(klass, "before redefinition");
+
+    // Redefine the class using JVMTI.
+    art.Redefinition.setTestConfiguration(art.Redefinition.Config.COMMON_REDEFINE);
+    art.Redefinition.doCommonClassRedefinition(klass, CLASS_BYTES, DEX_BYTES);
+
+    // Verify that the class members are still hidden.
+    assertMethodIsHidden(klass, "after redefinition");
+    assertFieldIsHidden(klass, "after redefinition");
+  }
+
+  private static void assertMethodIsHidden(Class<?> klass, String msg) throws Exception {
+    try {
+      klass.getDeclaredMethod("foo");
+      // Unexpected. Should have thrown NoSuchMethodException.
+      throw new Exception("Method should not be accessible " + msg);
+    } catch (NoSuchMethodException ex) {
+      // Expected.
+    }
+  }
+
+  private static void assertFieldIsHidden(Class<?> klass, String msg) throws Exception {
+    try {
+      klass.getDeclaredField("bar");
+      // Unexpected. Should have thrown NoSuchFieldException.
+      throw new Exception("Field should not be accessible " + msg);
+    } catch (NoSuchFieldException ex) {
+      // Expected.
+    }
+  }
+
+  private static final String DEX_EXTRA =
+      new File(System.getenv("DEX_LOCATION"), "999-redefine-hiddenapi-ex.jar").getAbsolutePath();
+
+  private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
+
+  // Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc.
+  private static native void appendToBootClassLoader(String dexPath);
+  private static native void init();
+
+  /**
+   * base64 encoded class/dex file for
+   *
+   * public class Test999 {
+   *   public void foo() {
+   *     System.out.println("Goodbye");
+   *   }
+   *
+   *   public int bar = 64;
+   * }
+   */
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADUAIAoABwARCQAGABIJABMAFAgAFQoAFgAXBwAYBwAZAQADYmFyAQABSQEABjxpbml0" +
+    "PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAANmb28BAApTb3VyY2VGaWxlAQAMVGVz" +
+    "dDk5OS5qYXZhDAAKAAsMAAgACQcAGgwAGwAcAQAHR29vZGJ5ZQcAHQwAHgAfAQALYXJ0L1Rlc3Q5" +
+    "OTkBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lv" +
+    "L1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xh" +
+    "bmcvU3RyaW5nOylWACEABgAHAAAAAQABAAgACQAAAAIAAQAKAAsAAQAMAAAAJwACAAEAAAALKrcA" +
+    "ASoQQLUAArEAAAABAA0AAAAKAAIAAAATAAQAGAABAA4ACwABAAwAAAAlAAIAAQAAAAmyAAMSBLYA" +
+    "BbEAAAABAA0AAAAKAAIAAAAVAAgAFgABAA8AAAACABA=");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQD0dZ+IWxOi+cJDSWjfTnUerlZj1Lll3ONIAwAAcAAAAHhWNBIAAAAAAAAAAJwCAAAQ" +
+    "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAAAUAgAANAEAAIYB" +
+    "AACOAQAAlwEAAJoBAACpAQAAwAEAANQBAADoAQAA/AEAAAoCAAANAgAAEQIAABYCAAAbAgAAIAIA" +
+    "ACkCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAACAAQAA" +
+    "AQAAAAsAAAAFAAIADQAAAAEAAAAAAAAAAQAAAAwAAAACAAEADgAAAAMAAAAAAAAAAQAAAAEAAAAD" +
+    "AAAAAAAAAAgAAAAAAAAAhwIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" +
+    "AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" +
+    "R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" +
+    "YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" +
+    "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAFx+fkQ4eyJtaW4tYXBpIjox" +
+    "LCJzaGEtMSI6IjU2YzJlMzBmNTIzM2I4NDRmZjZkZGQ4N2ZiNTNkMzRmYjE3MjM3ZGYiLCJ2ZXJz" +
+    "aW9uIjoidjEuMi4xNS1kZXYifQAAAQEBAAEAgYAEtAIBAdQCAAAAAAAOAAAAAAAAAAEAAAAAAAAA" +
+    "AQAAABAAAABwAAAAAgAAAAcAAACwAAAAAwAAAAIAAADMAAAABAAAAAIAAADkAAAABQAAAAQAAAD0" +
+    "AAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIAAAB0AQAAARAAAAEAAACAAQAAAiAAABAA" +
+    "AACGAQAAACAAAAEAAACHAgAAAxAAAAEAAACYAgAAABAAAAEAAACcAgAA");
+}
diff --git a/test/999-redefine-hiddenapi/src/art/Redefinition.java b/test/999-redefine-hiddenapi/src/art/Redefinition.java
new file mode 100644
index 0000000..1eec70b
--- /dev/null
+++ b/test/999-redefine-hiddenapi/src/art/Redefinition.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/Android.bp b/test/Android.bp
index 76189f6..84f2e22 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -370,22 +370,27 @@
 }
 
 art_cc_defaults {
-    name: "libtistress-defaults",
+    name: "libtistress-srcs",
     defaults: ["libartagent-defaults"],
     srcs: [
         "ti-stress/stress.cc",
     ],
+    header_libs: ["libopenjdkjvmti_headers"],
+}
+
+art_cc_defaults {
+    name: "libtistress-defaults",
+    defaults: ["libtistress-srcs"],
     shared_libs: [
         "libbase",
         "slicer",
     ],
-    header_libs: ["libopenjdkjvmti_headers"],
 }
 
 art_cc_test_library {
     name: "libtistress",
     defaults: ["libtistress-defaults"],
-    shared_libs: ["libart"],
+    shared_libs: ["libartbase"],
 }
 
 art_cc_test_library {
@@ -394,7 +399,30 @@
         "art_debug_defaults",
         "libtistress-defaults",
     ],
-    shared_libs: ["libartd"],
+    shared_libs: ["libartbased"],
+}
+
+art_cc_defaults {
+    name: "libtistress-static-defaults",
+    defaults: ["libtistress-srcs"],
+    static_libs: art_static_dependencies + [
+        "slicer",
+    ],
+}
+
+art_cc_test_library {
+    name: "libtistresss",
+    defaults: ["libtistress-static-defaults"],
+    static_libs: ["libartbase"],
+}
+
+art_cc_test_library {
+    name: "libtistressds",
+    defaults: [
+        "art_debug_defaults",
+        "libtistress-static-defaults"
+    ],
+    static_libs: ["libartbased"],
 }
 
 cc_defaults {
diff --git a/test/etc/default-build b/test/etc/default-build
index 8bb898c..c61de0a 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -561,6 +561,11 @@
 if [[ -d classes-ex ]] && [ ${NEED_DEX} = "true" ]; then
   make_dex classes-ex
 
+  # Apply hiddenapi on the dex files if the test has API list file(s).
+  if [ ${USE_HIDDENAPI} = "true" -a ${HAS_HIDDENAPI_SPEC} = "true" ]; then
+    make_hiddenapi classes-ex.dex
+  fi
+
   # quick shuffle so that the stored name is "classes.dex"
   mv classes.dex classes-1.dex
   mv classes-ex.dex classes.dex
diff --git a/test/knownfailures.json b/test/knownfailures.json
index f473a99..a202044 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -454,7 +454,8 @@
             "674-hiddenapi",
             "649-vdex-duplicate-method",
             "804-class-extends-itself",
-            "921-hello-failure"
+            "921-hello-failure",
+            "999-redefine-hiddenapi"
         ],
         "description": [
             "Tests that use illegal dex files or otherwise break dexter assumptions"
@@ -471,7 +472,8 @@
             "629-vdex-speed",
             "647-jni-get-field-id",
             "674-hiddenapi",
-            "944-transform-classloaders"
+            "944-transform-classloaders",
+            "999-redefine-hiddenapi"
         ],
         "description": [
             "Tests that use custom class loaders or other features not supported ",
@@ -876,7 +878,6 @@
           "667-jit-jni-stub",
           "667-out-of-bounds",
           "668-aiobe",
-          "674-hiddenapi",
           "674-hotness-compiled",
           "674-vdex-uncompress",
           "675-checker-unverified-method",
@@ -954,8 +955,10 @@
     },
     {
         "tests": ["616-cha-unloading",
+                  "674-hiddenapi",
                   "678-quickening",
-                  "679-locks"],
+                  "679-locks",
+                  "999-redefine-hiddenapi"],
         "variant": "jvm",
         "description": ["Doesn't run on RI."]
     },
diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc
index bbe7465..0eba742 100644
--- a/test/ti-stress/stress.cc
+++ b/test/ti-stress/stress.cc
@@ -25,7 +25,6 @@
 #include <jni.h>
 
 #include "base/utils.h"
-#include "exec_utils.h"
 #include "jvmti.h"
 
 #pragma clang diagnostic push
@@ -920,4 +919,8 @@
   return 0;
 }
 
+extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
+  return Agent_OnLoad(vm, options, reserved);
+}
+
 }  // namespace art
diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc
index a5f647c..c4aebc6 100644
--- a/tools/dexanalyze/dexanalyze.cc
+++ b/tools/dexanalyze/dexanalyze.cc
@@ -15,6 +15,7 @@
  */
 
 #include <cstdint>
+#include <iostream>
 #include <set>
 #include <sstream>
 
@@ -31,6 +32,15 @@
 class DexAnalyze {
   static const int kExitCodeUsageError = 1;
 
+  static void StdoutLogger(android::base::LogId,
+                           android::base::LogSeverity,
+                           const char*,
+                           const char*,
+                           unsigned int,
+                           const char* message) {
+    std::cout << message << std::endl;
+  }
+
   static int Usage(char** argv) {
     LOG(ERROR)
         << "Usage " << argv[0] << " [options] <dex files>\n"
@@ -105,6 +115,8 @@
 
  public:
   static int Run(int argc, char** argv) {
+    android::base::SetLogger(StdoutLogger);
+
     Options options;
     int result = options.Parse(argc, argv);
     if (result != 0) {
@@ -154,7 +166,6 @@
 }  // namespace art
 
 int main(int argc, char** argv) {
-  android::base::SetLogger(android::base::StderrLogger);
   return art::DexAnalyze::Run(argc, argv);
 }