class_linker: Add support for resolving method types.

- Add a new fixed size dex cache array for resolved method types.
  The size of this array is set to 1024.
- Also introduces a new runtime flag that controls this feature.

Test: make test-art-host
Bug: 30550796

Change-Id: I147b33398d71ee21f2e91b418d3700d4630801ff
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 4f63099..850702a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -33,6 +33,7 @@
   Interfaces \
   Lookup \
   Main \
+  MethodTypes \
   MultiDex \
   MultiDexModifiedSecondary \
   MyClass \
@@ -80,9 +81,9 @@
 # Dex file dependencies for each gtest.
 ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
 
-ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MultiDex MyClass Nested Statics StaticsFromCode
+ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
 ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
-ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages
+ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
 ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested
 ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics
 ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index b74e588..b229be4 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -775,6 +775,8 @@
       existing = existing | ExperimentalFlags::kAgents;
     } else if (option == "runtime-plugins") {
       existing = existing | ExperimentalFlags::kRuntimePlugins;
+    } else if (option == "method-handles") {
+      existing = existing | ExperimentalFlags::kMethodHandles;
     } else {
       return Result::Failure(std::string("Unknown option '") + option + "'");
     }
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 0a1f7a9..7dea614 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1287,7 +1287,7 @@
       const DexFile* const dex_file = dex_cache->GetDexFile();
       const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
       if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
-      // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and
+        // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and
         // copy over the arrays.
         DCHECK(dex_file != nullptr);
         size_t num_strings = mirror::DexCache::kDexCacheStringCacheSize;
@@ -1297,10 +1297,19 @@
         const size_t num_types = dex_file->NumTypeIds();
         const size_t num_methods = dex_file->NumMethodIds();
         const size_t num_fields = dex_file->NumFieldIds();
+        size_t num_method_types = 0;
+        if (Runtime::Current()->IsMethodHandlesEnabled()) {
+          num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize;
+          if (dex_file->NumProtoIds() < num_method_types) {
+            num_method_types = dex_file->NumProtoIds();
+          }
+        }
+
         CHECK_EQ(num_strings, dex_cache->NumStrings());
         CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
         CHECK_EQ(num_methods, dex_cache->NumResolvedMethods());
         CHECK_EQ(num_fields, dex_cache->NumResolvedFields());
+        CHECK_EQ(num_method_types, dex_cache->NumResolvedMethodTypes());
         DexCacheArraysLayout layout(image_pointer_size_, dex_file);
         uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays();
         if (num_strings != 0u) {
@@ -1351,6 +1360,25 @@
           std::copy_n(dex_cache->GetResolvedFields(), num_fields, fields);
           dex_cache->SetResolvedFields(fields);
         }
+        if (num_method_types != 0u) {
+          // NOTE: We currently (Sep 2016) do not resolve any method types at
+          // compile time, but plan to in the future. This code exists for the
+          // sake of completeness.
+          mirror::MethodTypeDexCacheType* const image_resolved_method_types =
+              dex_cache->GetResolvedMethodTypes();
+          mirror::MethodTypeDexCacheType* const method_types =
+              reinterpret_cast<mirror::MethodTypeDexCacheType*>(
+                  raw_arrays + layout.MethodTypesOffset());
+          for (size_t j = 0; j < num_method_types; ++j) {
+            DCHECK_EQ(method_types[j].load(std::memory_order_relaxed).index, 0u);
+            DCHECK(method_types[j].load(std::memory_order_relaxed).object.IsNull());
+            method_types[j].store(
+                image_resolved_method_types[j].load(std::memory_order_relaxed),
+                std::memory_order_relaxed);
+          }
+
+          dex_cache->SetResolvedMethodTypes(method_types);
+        }
       }
       {
         WriterMutexLock mu2(self, dex_lock_);
@@ -2104,6 +2132,7 @@
     // Zero-initialized.
     raw_arrays = reinterpret_cast<uint8_t*>(linear_alloc->Alloc(self, layout.Size()));
   }
+
   mirror::StringDexCacheType* strings = (dex_file.NumStringIds() == 0u) ? nullptr :
       reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset());
   GcRoot<mirror::Class>* types = (dex_file.NumTypeIds() == 0u) ? nullptr :
@@ -2112,10 +2141,35 @@
       reinterpret_cast<ArtMethod**>(raw_arrays + layout.MethodsOffset());
   ArtField** fields = (dex_file.NumFieldIds() == 0u) ? nullptr :
       reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset());
+
   size_t num_strings = mirror::DexCache::kDexCacheStringCacheSize;
   if (dex_file.NumStringIds() < num_strings) {
     num_strings = dex_file.NumStringIds();
   }
+
+  // Note that we allocate the method type dex caches regardless of this flag,
+  // and we make sure here that they're not used by the runtime. This is in the
+  // interest of simplicity and to avoid extensive compiler and layout class changes.
+  //
+  // If this needs to be mitigated in a production system running this code,
+  // DexCache::kDexCacheMethodTypeCacheSize can be set to zero.
+  const bool is_method_handles_enabled = Runtime::Current()->IsMethodHandlesEnabled();
+  mirror::MethodTypeDexCacheType* method_types = nullptr;
+  size_t num_method_types = 0;
+
+  if (is_method_handles_enabled) {
+    if (dex_file.NumProtoIds() < mirror::DexCache::kDexCacheMethodTypeCacheSize) {
+      num_method_types = dex_file.NumProtoIds();
+    } else {
+      num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize;
+    }
+
+    if (num_method_types > 0) {
+      method_types = reinterpret_cast<mirror::MethodTypeDexCacheType*>(
+          raw_arrays + layout.MethodTypesOffset());
+    }
+  }
+
   DCHECK_ALIGNED(raw_arrays, alignof(mirror::StringDexCacheType)) <<
                  "Expected raw_arrays to align to StringDexCacheType.";
   DCHECK_ALIGNED(layout.StringsOffset(), alignof(mirror::StringDexCacheType)) <<
@@ -2139,10 +2193,17 @@
     for (size_t i = 0; i < dex_file.NumFieldIds(); ++i) {
       CHECK(mirror::DexCache::GetElementPtrSize(fields, i, image_pointer_size_) == nullptr);
     }
+    for (size_t i = 0; i < num_method_types; ++i) {
+      CHECK_EQ(method_types[i].load(std::memory_order_relaxed).index, 0u);
+      CHECK(method_types[i].load(std::memory_order_relaxed).object.IsNull());
+    }
   }
   if (strings != nullptr) {
     mirror::StringDexCachePair::Initialize(strings);
   }
+  if (method_types != nullptr) {
+    mirror::MethodTypeDexCachePair::Initialize(method_types);
+  }
   dex_cache->Init(&dex_file,
                   location.Get(),
                   strings,
@@ -2153,6 +2214,8 @@
                   dex_file.NumMethodIds(),
                   fields,
                   dex_file.NumFieldIds(),
+                  method_types,
+                  num_method_types,
                   image_pointer_size_);
   return dex_cache.Get();
 }
@@ -7935,6 +7998,68 @@
   return resolved;
 }
 
+mirror::MethodType* ClassLinker::ResolveMethodType(const DexFile& dex_file,
+                                                   uint32_t proto_idx,
+                                                   Handle<mirror::DexCache> dex_cache,
+                                                   Handle<mirror::ClassLoader> class_loader) {
+  DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+  DCHECK(dex_cache.Get() != nullptr);
+
+  mirror::MethodType* resolved = dex_cache->GetResolvedMethodType(proto_idx);
+  if (resolved != nullptr) {
+    return resolved;
+  }
+
+  Thread* const self = Thread::Current();
+  StackHandleScope<4> hs(self);
+
+  // First resolve the return type.
+  const DexFile::ProtoId& proto_id = dex_file.GetProtoId(proto_idx);
+  Handle<mirror::Class> return_type(hs.NewHandle(
+      ResolveType(dex_file, proto_id.return_type_idx_, dex_cache, class_loader)));
+  if (return_type.Get() == nullptr) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  // Then resolve the argument types.
+  //
+  // TODO: Is there a better way to figure out the number of method arguments
+  // other than by looking at the shorty ?
+  const size_t num_method_args = strlen(dex_file.StringDataByIdx(proto_id.shorty_idx_)) - 1;
+
+  mirror::Class* class_type = mirror::Class::GetJavaLangClass();
+  mirror::Class* array_of_class = FindArrayClass(self, &class_type);
+  Handle<mirror::ObjectArray<mirror::Class>> method_params(hs.NewHandle(
+      mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, num_method_args)));
+  if (method_params.Get() == nullptr) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  DexFileParameterIterator it(dex_file, proto_id);
+  int32_t i = 0;
+  MutableHandle<mirror::Class> param_class = hs.NewHandle<mirror::Class>(nullptr);
+  for (; it.HasNext(); it.Next()) {
+    const uint16_t type_idx = it.GetTypeIdx();
+    param_class.Assign(ResolveType(dex_file, type_idx, dex_cache, class_loader));
+    if (param_class.Get() == nullptr) {
+      DCHECK(self->IsExceptionPending());
+      return nullptr;
+    }
+
+    method_params->Set(i++, param_class.Get());
+  }
+
+  DCHECK(!it.HasNext());
+
+  Handle<mirror::MethodType> type = hs.NewHandle(
+      mirror::MethodType::Create(self, return_type, method_params));
+  dex_cache->SetResolvedMethodType(proto_idx, type.Get());
+
+  return type.Get();
+}
+
 const char* ClassLinker::MethodShorty(uint32_t method_idx,
                                       ArtMethod* referrer,
                                       uint32_t* length) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 8f7051e..df7fb61 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -49,6 +49,7 @@
   class ClassLoader;
   class DexCache;
   class DexCachePointerArray;
+  class DexCacheMethodHandlesTest_Open_Test;
   class DexCacheTest_Open_Test;
   class IfTable;
   class MethodType;
@@ -362,6 +363,15 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
+  // Resolve a method type with a given ID from the DexFile, storing
+  // the result in the DexCache.
+  mirror::MethodType* ResolveMethodType(const DexFile& dex_file,
+                                        uint32_t proto_idx,
+                                        Handle<mirror::DexCache> dex_cache,
+                                        Handle<mirror::ClassLoader> class_loader)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+
   // Get shorty from method index without resolution. Used to do handlerization.
   const char* MethodShorty(uint32_t method_idx, ArtMethod* referrer, uint32_t* length)
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1181,6 +1191,7 @@
   friend class JniCompilerTest;  // for GetRuntimeQuickGenericJniStub
   friend class JniInternalTest;  // for GetRuntimeQuickGenericJniStub
   ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName);  // for DexLock, and RegisterDexFileLocked
+  ART_FRIEND_TEST(mirror::DexCacheMethodHandlesTest, Open);  // for AllocDexCache
   ART_FRIEND_TEST(mirror::DexCacheTest, Open);  // for AllocDexCache
   DISALLOW_COPY_AND_ASSIGN(ClassLinker);
 };
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index a5aa0d0..593702d 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -451,6 +451,14 @@
   };
 };
 
+class ClassLinkerMethodHandlesTest : public ClassLinkerTest {
+ protected:
+  virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
+    CommonRuntimeTest::SetUpRuntimeOptions(options);
+    options->push_back(std::make_pair("-Xexperimental:method-handles", nullptr));
+  }
+};
+
 struct CheckOffset {
   size_t cpp_offset;
   const char* java_name;
@@ -650,10 +658,12 @@
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_), "dexFile");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_fields_), "numResolvedFields");
+    addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_method_types_), "numResolvedMethodTypes");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_methods_), "numResolvedMethods");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_types_), "numResolvedTypes");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_strings_), "numStrings");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_), "resolvedFields");
+    addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_method_types_), "resolvedMethodTypes");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_), "resolvedMethods");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_types_), "resolvedTypes");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, strings_), "strings");
@@ -1294,4 +1304,59 @@
   }
 }
 
+TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) {
+  ScopedObjectAccess soa(Thread::Current());
+
+  StackHandleScope<7> hs(soa.Self());
+
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("MethodTypes"))));
+  Handle<mirror::Class> method_types(
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "LMethodTypes;", class_loader)));
+  class_linker_->EnsureInitialized(soa.Self(), method_types, true, true);
+
+  ArtMethod* method1 = method_types->FindVirtualMethod("method1",
+                                                       "(Ljava/lang/String;)Ljava/lang/String;",
+                                                       kRuntimePointerSize);
+
+  const DexFile& dex_file = *(method1->GetDexFile());
+  Handle<mirror::DexCache> dex_cache = hs.NewHandle(
+      class_linker_->FindDexCache(Thread::Current(), dex_file));
+
+  const DexFile::MethodId& method1_id = dex_file.GetMethodId(method1->GetDexMethodIndex());
+
+  // This is the MethodType corresponding to the prototype of
+  // String MethodTypes# method1(String).
+  // Its RType = Ljava/lang/String;
+  // Its PTypes = { Ljava/langString; }
+  Handle<mirror::MethodType> method1_type = hs.NewHandle(
+      class_linker_->ResolveMethodType(dex_file, method1_id.proto_idx_, dex_cache, class_loader));
+
+  // Assert that the method type was resolved successfully.
+  ASSERT_TRUE(method1_type.Get() != nullptr);
+
+  // Assert that the return type and the method arguments are as we expect.
+  Handle<mirror::Class> string_class(
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "Ljava/lang/String;", class_loader)));
+  ASSERT_EQ(string_class.Get(), method1_type->GetRType());
+  ASSERT_EQ(string_class.Get(), method1_type->GetPTypes()->Get(0));
+
+  // Resolve the method type again and assert that we get back the same value.
+  Handle<mirror::MethodType> method1_type2 = hs.NewHandle(
+      class_linker_->ResolveMethodType(dex_file, method1_id.proto_idx_, dex_cache, class_loader));
+  ASSERT_EQ(method1_type.Get(), method1_type2.Get());
+
+  // Resolve the MethodType associated with a different method signature
+  // and assert it's different.
+  ArtMethod* method2 = method_types->FindVirtualMethod(
+      "method2",
+      "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
+      kRuntimePointerSize);
+  const DexFile::MethodId& method2_id = dex_file.GetMethodId(method2->GetDexMethodIndex());
+  Handle<mirror::MethodType> method2_type = hs.NewHandle(
+      class_linker_->ResolveMethodType(dex_file, method2_id.proto_idx_, dex_cache, class_loader));
+
+  ASSERT_TRUE(method1_type.Get() != method2_type.Get());
+}
+
 }  // namespace art
diff --git a/runtime/experimental_flags.h b/runtime/experimental_flags.h
index 54d2c35..5ddb9fa 100644
--- a/runtime/experimental_flags.h
+++ b/runtime/experimental_flags.h
@@ -28,6 +28,7 @@
     kNone           = 0x0000,
     kAgents         = 0x0001,  // 0b00000001
     kRuntimePlugins = 0x0002,  // 0b00000010
+    kMethodHandles  = 0x0004,  // 0b00000100
   };
 
   constexpr ExperimentalFlags() : value_(0x0000) {}
@@ -74,6 +75,10 @@
     stream << (started ? "|" : "") << "kRuntimePlugins";
     started = true;
   }
+  if (e & ExperimentalFlags::kMethodHandles) {
+    stream << (started ? "|" : "") << "kMethodHandles";
+    started = true;
+  }
   if (!started) {
     stream << "kNone";
   }
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 359462d..41692da 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -25,6 +25,7 @@
 #include "base/enums.h"
 #include "base/logging.h"
 #include "mirror/class.h"
+#include "mirror/method_type.h"
 #include "runtime.h"
 
 #include <atomic>
@@ -82,6 +83,24 @@
   Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
 }
 
+inline MethodType* DexCache::GetResolvedMethodType(uint32_t proto_idx) {
+  DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+  DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds());
+  return MethodTypeDexCachePair::Lookup(
+      GetResolvedMethodTypes(), proto_idx, NumResolvedMethodTypes()).Read();
+}
+
+inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) {
+  DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+  DCHECK_LT(proto_idx, NumResolvedMethodTypes());  // NOTE: Unchecked, i.e. not throwing AIOOB.
+
+  GetResolvedMethodTypes()[proto_idx % NumResolvedMethodTypes()].store(
+      MethodTypeDexCachePair(resolved, proto_idx), 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);
+}
+
 inline ArtField* DexCache::GetResolvedField(uint32_t field_idx, PointerSize ptr_size) {
   DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
   DCHECK_LT(field_idx, NumResolvedFields());  // NOTE: Unchecked, i.e. not throwing AIOOB.
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index cfcec9c..66f858c 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -41,6 +41,8 @@
                     uint32_t num_resolved_methods,
                     ArtField** resolved_fields,
                     uint32_t num_resolved_fields,
+                    MethodTypeDexCacheType* resolved_method_types,
+                    uint32_t num_resolved_method_types,
                     PointerSize pointer_size) {
   CHECK(dex_file != nullptr);
   CHECK(location != nullptr);
@@ -48,6 +50,7 @@
   CHECK_EQ(num_resolved_types != 0u, resolved_types != nullptr);
   CHECK_EQ(num_resolved_methods != 0u, resolved_methods != nullptr);
   CHECK_EQ(num_resolved_fields != 0u, resolved_fields != nullptr);
+  CHECK_EQ(num_resolved_method_types != 0u, resolved_method_types != nullptr);
 
   SetDexFile(dex_file);
   SetLocation(location);
@@ -55,10 +58,12 @@
   SetResolvedTypes(resolved_types);
   SetResolvedMethods(resolved_methods);
   SetResolvedFields(resolved_fields);
+  SetResolvedMethodTypes(resolved_method_types);
   SetField32<false>(NumStringsOffset(), num_strings);
   SetField32<false>(NumResolvedTypesOffset(), num_resolved_types);
   SetField32<false>(NumResolvedMethodsOffset(), num_resolved_methods);
   SetField32<false>(NumResolvedFieldsOffset(), num_resolved_fields);
+  SetField32<false>(NumResolvedMethodTypesOffset(), num_resolved_method_types);
 
   Runtime* const runtime = Runtime::Current();
   if (runtime->HasResolutionMethod()) {
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index d81dedc..92d9c1d 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -33,6 +33,7 @@
 
 namespace mirror {
 
+class MethodType;
 class String;
 
 template <typename T> struct PACKED(8) DexCachePair {
@@ -92,6 +93,9 @@
 using StringDexCachePair = DexCachePair<mirror::String>;
 using StringDexCacheType = std::atomic<StringDexCachePair>;
 
+using MethodTypeDexCachePair = DexCachePair<mirror::MethodType>;
+using MethodTypeDexCacheType = std::atomic<MethodTypeDexCachePair>;
+
 // C++ mirror of java.lang.DexCache.
 class MANAGED DexCache FINAL : public Object {
  public:
@@ -103,10 +107,20 @@
   static_assert(IsPowerOfTwo(kDexCacheStringCacheSize),
                 "String dex cache size is not a power of 2.");
 
+  // Size of method type dex cache. Needs to be a power of 2 for entrypoint assumptions
+  // to hold.
+  static constexpr size_t kDexCacheMethodTypeCacheSize = 1024;
+  static_assert(IsPowerOfTwo(kDexCacheMethodTypeCacheSize),
+                "MethodType dex cache size is not a power of 2.");
+
   static constexpr size_t StaticStringSize() {
     return kDexCacheStringCacheSize;
   }
 
+  static constexpr size_t StaticMethodTypeSize() {
+    return kDexCacheMethodTypeCacheSize;
+  }
+
   // Size of an instance of java.lang.DexCache not including referenced values.
   static constexpr uint32_t InstanceSize() {
     return sizeof(DexCache);
@@ -122,6 +136,8 @@
             uint32_t num_resolved_methods,
             ArtField** resolved_fields,
             uint32_t num_resolved_fields,
+            MethodTypeDexCacheType* resolved_methodtypes,
+            uint32_t num_resolved_methodtypes,
             PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_);
 
   void Fixup(ArtMethod* trampoline, PointerSize pointer_size)
@@ -159,6 +175,10 @@
     return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_methods_);
   }
 
+  static MemberOffset ResolvedMethodTypesOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_method_types_);
+  }
+
   static MemberOffset NumStringsOffset() {
     return OFFSET_OF_OBJECT_MEMBER(DexCache, num_strings_);
   }
@@ -175,6 +195,10 @@
     return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_methods_);
   }
 
+  static MemberOffset NumResolvedMethodTypesOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_method_types_);
+  }
+
   mirror::String* GetResolvedString(uint32_t string_idx) ALWAYS_INLINE
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -205,6 +229,10 @@
   ALWAYS_INLINE void SetResolvedField(uint32_t idx, ArtField* field, PointerSize ptr_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  MethodType* GetResolvedMethodType(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+
+  void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) REQUIRES_SHARED(Locks::mutator_lock_);
+
   StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetFieldPtr64<StringDexCacheType*>(StringsOffset());
   }
@@ -243,6 +271,17 @@
     SetFieldPtr<false>(ResolvedFieldsOffset(), resolved_fields);
   }
 
+  MethodTypeDexCacheType* GetResolvedMethodTypes()
+      ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetFieldPtr<MethodTypeDexCacheType*>(ResolvedMethodTypesOffset());
+  }
+
+  void SetResolvedMethodTypes(MethodTypeDexCacheType* resolved_method_types)
+      ALWAYS_INLINE
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    SetFieldPtr<false>(ResolvedMethodTypesOffset(), resolved_method_types);
+  }
+
   size_t NumStrings() REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetField32(NumStringsOffset());
   }
@@ -259,6 +298,10 @@
     return GetField32(NumResolvedFieldsOffset());
   }
 
+  size_t NumResolvedMethodTypes() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetField32(NumResolvedMethodTypesOffset());
+  }
+
   const DexFile* GetDexFile() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetFieldPtr<const DexFile*>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_));
   }
@@ -290,16 +333,20 @@
 
   HeapReference<Object> dex_;
   HeapReference<String> location_;
-  uint64_t dex_file_;           // const DexFile*
-  uint64_t resolved_fields_;    // ArtField*, array with num_resolved_fields_ elements.
-  uint64_t resolved_methods_;   // ArtMethod*, array with num_resolved_methods_ elements.
-  uint64_t resolved_types_;     // GcRoot<Class>*, array with num_resolved_types_ elements.
-  uint64_t strings_;            // std::atomic<StringDexCachePair>*,
-                                // array with num_strings_ elements.
-  uint32_t num_resolved_fields_;    // Number of elements in the resolved_fields_ array.
-  uint32_t num_resolved_methods_;   // Number of elements in the resolved_methods_ array.
-  uint32_t num_resolved_types_;     // Number of elements in the resolved_types_ array.
-  uint32_t num_strings_;            // Number of elements in the strings_ array.
+  uint64_t dex_file_;               // const DexFile*
+  uint64_t resolved_fields_;        // ArtField*, array with num_resolved_fields_ elements.
+  uint64_t resolved_method_types_;  // std::atomic<MethodTypeDexCachePair>* array with
+                                    // num_resolved_method_types_ elements.
+  uint64_t resolved_methods_;       // ArtMethod*, array with num_resolved_methods_ elements.
+  uint64_t resolved_types_;         // GcRoot<Class>*, array with num_resolved_types_ elements.
+  uint64_t strings_;                // std::atomic<StringDexCachePair>*, array with num_strings_
+                                    // elements.
+
+  uint32_t num_resolved_fields_;        // Number of elements in the resolved_fields_ array.
+  uint32_t num_resolved_method_types_;  // Number of elements in the resolved_method_types_ array.
+  uint32_t num_resolved_methods_;       // Number of elements in the resolved_methods_ array.
+  uint32_t num_resolved_types_;         // Number of elements in the resolved_types_ array.
+  uint32_t num_strings_;                // Number of elements in the strings_ array.
 
   friend struct art::DexCacheOffsets;  // for verifying offset information
   friend class Object;  // For VisitReferences
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index ac04200..12301b8 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -31,6 +31,14 @@
 
 class DexCacheTest : public CommonRuntimeTest {};
 
+class DexCacheMethodHandlesTest : public DexCacheTest {
+ protected:
+  virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
+    CommonRuntimeTest::SetUpRuntimeOptions(options);
+    options->push_back(std::make_pair("-Xexperimental:method-handles", nullptr));
+  }
+};
+
 TEST_F(DexCacheTest, Open) {
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<1> hs(soa.Self());
@@ -46,21 +54,35 @@
   EXPECT_EQ(java_lang_dex_file_->NumTypeIds(),   dex_cache->NumResolvedTypes());
   EXPECT_EQ(java_lang_dex_file_->NumMethodIds(), dex_cache->NumResolvedMethods());
   EXPECT_EQ(java_lang_dex_file_->NumFieldIds(),  dex_cache->NumResolvedFields());
+  // This should always be zero because the -Xexperimental:method-handles isn't
+  // set.
+  EXPECT_EQ(0u, dex_cache->NumResolvedMethodTypes());
+}
+
+TEST_F(DexCacheMethodHandlesTest, Open) {
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<1> hs(soa.Self());
+  ASSERT_TRUE(java_lang_dex_file_ != nullptr);
+  Handle<DexCache> dex_cache(
+      hs.NewHandle(class_linker_->AllocDexCache(soa.Self(),
+                                                *java_lang_dex_file_,
+                                                Runtime::Current()->GetLinearAlloc())));
+
+  EXPECT_TRUE(dex_cache->StaticMethodTypeSize() == dex_cache->NumResolvedMethodTypes()
+      || java_lang_dex_file_->NumProtoIds() == dex_cache->NumResolvedMethodTypes());
 }
 
 TEST_F(DexCacheTest, LinearAlloc) {
   ScopedObjectAccess soa(Thread::Current());
   jobject jclass_loader(LoadDex("Main"));
   ASSERT_TRUE(jclass_loader != nullptr);
-  Runtime* const runtime = Runtime::Current();
-  ClassLinker* const class_linker = runtime->GetClassLinker();
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
       soa.Decode<mirror::ClassLoader>(jclass_loader)));
-  mirror::Class* klass = class_linker->FindClass(soa.Self(), "LMain;", class_loader);
+  mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader);
   ASSERT_TRUE(klass != nullptr);
   LinearAlloc* const linear_alloc = klass->GetClassLoader()->GetAllocator();
-  EXPECT_NE(linear_alloc, runtime->GetLinearAlloc());
+  EXPECT_NE(linear_alloc, runtime_->GetLinearAlloc());
   EXPECT_TRUE(linear_alloc->Contains(klass->GetDexCache()->GetResolvedMethods()));
 }
 
@@ -68,16 +90,14 @@
   ScopedObjectAccess soa(Thread::Current());
   jobject jclass_loader(LoadDex("Packages"));
   ASSERT_TRUE(jclass_loader != nullptr);
-  Runtime* const runtime = Runtime::Current();
-  ClassLinker* const class_linker = runtime->GetClassLinker();
   StackHandleScope<3> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
       soa.Decode<mirror::ClassLoader>(jclass_loader)));
   Handle<mirror::Class> klass1 =
-      hs.NewHandle(class_linker->FindClass(soa.Self(), "Lpackage1/Package1;", class_loader));
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "Lpackage1/Package1;", class_loader));
   ASSERT_TRUE(klass1.Get() != nullptr);
   Handle<mirror::Class> klass2 =
-      hs.NewHandle(class_linker->FindClass(soa.Self(), "Lpackage2/Package2;", class_loader));
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "Lpackage2/Package2;", class_loader));
   ASSERT_TRUE(klass2.Get() != nullptr);
   EXPECT_EQ(klass1->GetDexCache(), klass2->GetDexCache());
 
@@ -92,5 +112,60 @@
   }
 }
 
+TEST_F(DexCacheMethodHandlesTest, TestResolvedMethodTypes) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject jclass_loader(LoadDex("MethodTypes"));
+  ASSERT_TRUE(jclass_loader != nullptr);
+
+  StackHandleScope<5> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(jclass_loader)));
+
+  Handle<mirror::Class> method_types(
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "LMethodTypes;", class_loader)));
+  class_linker_->EnsureInitialized(soa.Self(), method_types, true, true);
+
+  ArtMethod* method1 = method_types->FindVirtualMethod(
+      "method1",
+      "(Ljava/lang/String;)Ljava/lang/String;",
+      kRuntimePointerSize);
+  ArtMethod* method2 = method_types->FindVirtualMethod(
+      "method2",
+      "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
+      kRuntimePointerSize);
+
+  const DexFile& dex_file = *(method1->GetDexFile());
+  Handle<mirror::DexCache> dex_cache = hs.NewHandle(
+      class_linker_->FindDexCache(Thread::Current(), dex_file));
+
+  const DexFile::MethodId& method1_id = dex_file.GetMethodId(method1->GetDexMethodIndex());
+  const DexFile::MethodId& method2_id = dex_file.GetMethodId(method2->GetDexMethodIndex());
+
+  Handle<mirror::MethodType> method1_type = hs.NewHandle(
+      class_linker_->ResolveMethodType(dex_file, method1_id.proto_idx_, dex_cache, class_loader));
+  Handle<mirror::MethodType> method2_type = hs.NewHandle(
+      class_linker_->ResolveMethodType(dex_file, method2_id.proto_idx_, dex_cache, class_loader));
+
+  EXPECT_EQ(method1_type.Get(), dex_cache->GetResolvedMethodType(method1_id.proto_idx_));
+  EXPECT_EQ(method2_type.Get(), dex_cache->GetResolvedMethodType(method2_id.proto_idx_));
+
+  // The MethodTypes dex file contains a single interface with two abstract
+  // methods. It must therefore contain precisely two method IDs.
+  ASSERT_EQ(2u, dex_file.NumProtoIds());
+  ASSERT_EQ(dex_file.NumProtoIds(), dex_cache->NumResolvedMethodTypes());
+  MethodTypeDexCacheType* method_types_cache = dex_cache->GetResolvedMethodTypes();
+
+  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_) {
+      ASSERT_EQ(method1_type.Get(), pair.object.Read());
+    } else if (pair.index == method2_id.proto_idx_) {
+      ASSERT_EQ(method2_type.Get(), pair.object.Read());
+    } else {
+      ASSERT_TRUE(false);
+    }
+  }
+}
+
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 4f70b04..f937ca7 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -761,6 +761,8 @@
                        "(Enable new and experimental agent support)\n");
   UsageMessage(stream, "  -Xexperimental:agents"
                        "(Enable new and experimental agent support)\n");
+  UsageMessage(stream, "  -Xexperimental:method-handles"
+                       "(Enable new and experimental method handles support)\n");
   UsageMessage(stream, "\n");
 
   UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n");
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 9e63564..30f1b4a 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -314,6 +314,10 @@
     return "2.1.0";
   }
 
+  bool IsMethodHandlesEnabled() const {
+    return experimental_flags_ & ExperimentalFlags::kMethodHandles;
+  }
+
   void DisallowNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_);
   void AllowNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_);
   void BroadcastForNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 146afc7..b01a570 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -117,7 +117,7 @@
 RUNTIME_OPTIONS_KEY (Unit,                NoDexFileFallback)
 RUNTIME_OPTIONS_KEY (std::string,         CpuAbiList)
 RUNTIME_OPTIONS_KEY (std::string,         Fingerprint)
-RUNTIME_OPTIONS_KEY (ExperimentalFlags,   Experimental,     ExperimentalFlags::kNone) // -Xexperimental:{none, agents}
+RUNTIME_OPTIONS_KEY (ExperimentalFlags,   Experimental,     ExperimentalFlags::kNone) // -Xexperimental:{none, agents, method-handles}
 RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>,         AgentLib)  // -agentlib:<libname>=<options>, Requires -Xexperimental:agents
 RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>,         AgentPath)  // -agentpath:<libname>=<options>, Requires -Xexperimental:agents
 RUNTIME_OPTIONS_KEY (std::vector<Plugin>,            Plugins)  // -Xplugin:<library> Requires -Xexperimental:runtime-plugins
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
index a85d033..5ccd446 100644
--- a/runtime/utils/dex_cache_arrays_layout-inl.h
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -38,8 +38,10 @@
           RoundUp(methods_offset_ + MethodsSize(header.method_ids_size_), StringsAlignment())),
       fields_offset_(
           RoundUp(strings_offset_ + StringsSize(header.string_ids_size_), FieldsAlignment())),
+      method_types_offset_(
+          RoundUp(fields_offset_ + FieldsSize(header.field_ids_size_), Alignment())),
       size_(
-          RoundUp(fields_offset_ + FieldsSize(header.field_ids_size_), Alignment())) {
+          RoundUp(method_types_offset_ + MethodTypesSize(header.proto_ids_size_), Alignment())) {
 }
 
 inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size, const DexFile* dex_file)
@@ -118,6 +120,27 @@
   return static_cast<size_t>(pointer_size_);
 }
 
+inline size_t DexCacheArraysLayout::MethodTypeOffset(uint32_t proto_idx) const {
+  return strings_offset_
+      + ElementOffset(PointerSize::k64,
+                      proto_idx % mirror::DexCache::kDexCacheMethodTypeCacheSize);
+}
+
+inline size_t DexCacheArraysLayout::MethodTypesSize(size_t num_elements) const {
+  size_t cache_size = mirror::DexCache::kDexCacheMethodTypeCacheSize;
+  if (num_elements < cache_size) {
+    cache_size = num_elements;
+  }
+
+  return ArraySize(PointerSize::k64, cache_size);
+}
+
+inline size_t DexCacheArraysLayout::MethodTypesAlignment() const {
+  static_assert(alignof(mirror::MethodTypeDexCacheType) == 8,
+                "alignof(MethodTypeDexCacheType) != 8");
+  return alignof(mirror::MethodTypeDexCacheType);
+}
+
 inline size_t DexCacheArraysLayout::ElementOffset(PointerSize element_size, uint32_t idx) {
   return static_cast<size_t>(element_size) * idx;
 }
diff --git a/runtime/utils/dex_cache_arrays_layout.h b/runtime/utils/dex_cache_arrays_layout.h
index 20ffa90..e222b46 100644
--- a/runtime/utils/dex_cache_arrays_layout.h
+++ b/runtime/utils/dex_cache_arrays_layout.h
@@ -35,6 +35,7 @@
         methods_offset_(0u),
         strings_offset_(0u),
         fields_offset_(0u),
+        method_types_offset_(0u),
         size_(0u) {
   }
 
@@ -94,12 +95,23 @@
 
   size_t FieldsAlignment() const;
 
+  size_t MethodTypesOffset() const {
+    return method_types_offset_;
+  }
+
+  size_t MethodTypeOffset(uint32_t method_type_idx) const;
+
+  size_t MethodTypesSize(size_t num_elements) const;
+
+  size_t MethodTypesAlignment() const;
+
  private:
   static constexpr size_t types_offset_ = 0u;
   const PointerSize pointer_size_;  // Must be first for construction initialization order.
   const size_t methods_offset_;
   const size_t strings_offset_;
   const size_t fields_offset_;
+  const size_t method_types_offset_;
   const size_t size_;
 
   static size_t Alignment(PointerSize pointer_size);
diff --git a/test/MethodTypes/MethodTypes.java b/test/MethodTypes/MethodTypes.java
new file mode 100644
index 0000000..f6f8e08
--- /dev/null
+++ b/test/MethodTypes/MethodTypes.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface MethodTypes {
+    public String method1(String a);
+    public String method2(String a, String b);
+}