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/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