ART: invoke-custom support

Adds invoke-custom instruction to the interpreter.

Bug: 33191717,30550796
Test: art/test/run-test --host 952
Change-Id: I3b754128649a8b3a00ade79ba2518d0e377f3a1e
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index c72edb1..70ff327 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -2254,6 +2254,14 @@
     orig_dex_cache->FixupResolvedMethodTypes(NativeCopyLocation(orig_method_types, orig_dex_cache),
                                              ImageAddressVisitor(this));
   }
+  GcRoot<mirror::CallSite>* orig_call_sites = orig_dex_cache->GetResolvedCallSites();
+  if (orig_call_sites != nullptr) {
+    copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedCallSitesOffset(),
+                                               NativeLocationInImage(orig_call_sites),
+                                               PointerSize::k64);
+    orig_dex_cache->FixupResolvedCallSites(NativeCopyLocation(orig_call_sites, orig_dex_cache),
+                                           ImageAddressVisitor(this));
+  }
 
   // Remove the DexFile pointers. They will be fixed up when the runtime loads the oat file. Leaving
   // compiler pointers in here will make the output non-deterministic.
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index d5776fa..5656ddd 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -881,26 +881,30 @@
       outSize = snprintf(buf.get(), bufSize, "[obj+%0*x]", width, index);
       break;
     case Instruction::kIndexMethodAndProtoRef: {
-        std::string method("<method?>");
-        std::string proto("<proto?>");
-        if (index < pDexFile->GetHeader().method_ids_size_) {
-          const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(index);
-          const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_);
-          const Signature signature = pDexFile->GetMethodSignature(pMethodId);
-          const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_);
-          method = android::base::StringPrintf("%s.%s:%s",
-                                               backDescriptor,
-                                               name,
-                                               signature.ToString().c_str());
-        }
-        if (secondary_index < pDexFile->GetHeader().proto_ids_size_) {
-          const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index);
-          const Signature signature = pDexFile->GetProtoSignature(protoId);
-          proto = signature.ToString();
-        }
-        outSize = snprintf(buf.get(), bufSize, "%s, %s // method@%0*x, proto@%0*x",
-                           method.c_str(), proto.c_str(), width, index, width, secondary_index);
+      std::string method("<method?>");
+      std::string proto("<proto?>");
+      if (index < pDexFile->GetHeader().method_ids_size_) {
+        const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(index);
+        const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_);
+        const Signature signature = pDexFile->GetMethodSignature(pMethodId);
+        const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_);
+        method = android::base::StringPrintf("%s.%s:%s",
+                                             backDescriptor,
+                                             name,
+                                             signature.ToString().c_str());
       }
+      if (secondary_index < pDexFile->GetHeader().proto_ids_size_) {
+        const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index);
+        const Signature signature = pDexFile->GetProtoSignature(protoId);
+        proto = signature.ToString();
+      }
+      outSize = snprintf(buf.get(), bufSize, "%s, %s // method@%0*x, proto@%0*x",
+                         method.c_str(), proto.c_str(), width, index, width, secondary_index);
+      break;
+    }
+    case Instruction::kIndexCallSiteRef:
+      // Call site information is too large to detail in disassembly so just output the index.
+      outSize = snprintf(buf.get(), bufSize, "call_site@%0*x", width, index);
       break;
     // SOME NOT SUPPORTED:
     // case Instruction::kIndexVaries:
@@ -1581,6 +1585,198 @@
   free(accessStr);
 }
 
+static void dumpMethodHandle(const DexFile* pDexFile, u4 idx) {
+  const DexFile::MethodHandleItem& mh = pDexFile->GetMethodHandle(idx);
+  bool is_invoke = false;
+  const char* type;
+  switch (static_cast<DexFile::MethodHandleType>(mh.method_handle_type_)) {
+    case DexFile::MethodHandleType::kStaticPut:
+      type = "put-static";
+      break;
+    case DexFile::MethodHandleType::kStaticGet:
+      type = "get-static";
+      break;
+    case DexFile::MethodHandleType::kInstancePut:
+      type = "put-instance";
+      break;
+    case DexFile::MethodHandleType::kInstanceGet:
+      type = "get-instance";
+      break;
+    case DexFile::MethodHandleType::kInvokeStatic:
+      type = "invoke-static";
+      is_invoke = true;
+      break;
+    case DexFile::MethodHandleType::kInvokeInstance:
+      type = "invoke-instance";
+      is_invoke = true;
+      break;
+    case DexFile::MethodHandleType::kInvokeConstructor:
+      type = "invoke-constructor";
+      is_invoke = true;
+      break;
+  }
+
+  const char* declaring_class;
+  const char* member;
+  std::string member_type;
+  if (is_invoke) {
+    const DexFile::MethodId& method_id = pDexFile->GetMethodId(mh.field_or_method_idx_);
+    declaring_class = pDexFile->GetMethodDeclaringClassDescriptor(method_id);
+    member = pDexFile->GetMethodName(method_id);
+    member_type = pDexFile->GetMethodSignature(method_id).ToString();
+  } else {
+    const DexFile::FieldId& field_id = pDexFile->GetFieldId(mh.field_or_method_idx_);
+    declaring_class = pDexFile->GetFieldDeclaringClassDescriptor(field_id);
+    member = pDexFile->GetFieldName(field_id);
+    member_type = pDexFile->GetFieldTypeDescriptor(field_id);
+  }
+
+  if (gOptions.outputFormat == OUTPUT_PLAIN) {
+    fprintf(gOutFile, "Method handle #%u:\n", idx);
+    fprintf(gOutFile, "  type        : %s\n", type);
+    fprintf(gOutFile, "  target      : %s %s\n", declaring_class, member);
+    fprintf(gOutFile, "  target_type : %s\n", member_type.c_str());
+  } else {
+    fprintf(gOutFile, "<method_handle index=\"%u\"\n", idx);
+    fprintf(gOutFile, " type=\"%s\"\n", type);
+    fprintf(gOutFile, " target_class=\"%s\"\n", declaring_class);
+    fprintf(gOutFile, " target_member=\"%s\"\n", member);
+    fprintf(gOutFile, " target_member_type=");
+    dumpEscapedString(member_type.c_str());
+    fprintf(gOutFile, "\n>\n</method_handle>\n");
+  }
+}
+
+static void dumpCallSite(const DexFile* pDexFile, u4 idx) {
+  const DexFile::CallSiteIdItem& call_site_id = pDexFile->GetCallSiteId(idx);
+  CallSiteArrayValueIterator it(*pDexFile, call_site_id);
+  if (it.Size() < 3) {
+    fprintf(stderr, "ERROR: Call site %u has too few values.\n", idx);
+    return;
+  }
+
+  uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+  it.Next();
+  dex::StringIndex method_name_idx = static_cast<dex::StringIndex>(it.GetJavaValue().i);
+  const char* method_name = pDexFile->StringDataByIdx(method_name_idx);
+  it.Next();
+  uint32_t method_type_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+  const DexFile::ProtoId& method_type_id = pDexFile->GetProtoId(method_type_idx);
+  std::string method_type = pDexFile->GetProtoSignature(method_type_id).ToString();
+  it.Next();
+
+  if (gOptions.outputFormat == OUTPUT_PLAIN) {
+    fprintf(gOutFile, "Call site #%u:\n", idx);
+    fprintf(gOutFile, "  link_argument[0] : %u (MethodHandle)\n", method_handle_idx);
+    fprintf(gOutFile, "  link_argument[1] : %s (String)\n", method_name);
+    fprintf(gOutFile, "  link_argument[2] : %s (MethodType)\n", method_type.c_str());
+  } else {
+    fprintf(gOutFile, "<call_site index=\"%u\">\n", idx);
+    fprintf(gOutFile,
+            "<link_argument index=\"0\" type=\"MethodHandle\" value=\"%u\"/>\n",
+            method_handle_idx);
+    fprintf(gOutFile,
+            "<link_argument index=\"1\" type=\"String\" values=\"%s\"/>\n",
+            method_name);
+    fprintf(gOutFile,
+            "<link_argument index=\"2\" type=\"MethodType\" value=\"%s\"/>\n",
+            method_type.c_str());
+  }
+
+  size_t argument = 3;
+  while (it.HasNext()) {
+    const char* type;
+    std::string value;
+    switch (it.GetValueType()) {
+      case EncodedArrayValueIterator::ValueType::kByte:
+        type = "byte";
+        value = android::base::StringPrintf("%u", it.GetJavaValue().b);
+        break;
+      case EncodedArrayValueIterator::ValueType::kShort:
+        type = "short";
+        value = android::base::StringPrintf("%d", it.GetJavaValue().s);
+        break;
+      case EncodedArrayValueIterator::ValueType::kChar:
+        type = "char";
+        value = android::base::StringPrintf("%u", it.GetJavaValue().c);
+        break;
+      case EncodedArrayValueIterator::ValueType::kInt:
+        type = "int";
+        value = android::base::StringPrintf("%d", it.GetJavaValue().i);
+        break;
+      case EncodedArrayValueIterator::ValueType::kLong:
+        type = "long";
+        value = android::base::StringPrintf("%" PRId64, it.GetJavaValue().j);
+        break;
+      case EncodedArrayValueIterator::ValueType::kFloat:
+        type = "float";
+        value = android::base::StringPrintf("%g", it.GetJavaValue().f);
+        break;
+      case EncodedArrayValueIterator::ValueType::kDouble:
+        type = "double";
+        value = android::base::StringPrintf("%g", it.GetJavaValue().d);
+        break;
+      case EncodedArrayValueIterator::ValueType::kMethodType: {
+        type = "MethodType";
+        uint32_t proto_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+        const DexFile::ProtoId& proto_id = pDexFile->GetProtoId(proto_idx);
+        value = pDexFile->GetProtoSignature(proto_id).ToString();
+        break;
+      }
+      case EncodedArrayValueIterator::ValueType::kMethodHandle:
+        type = "MethodHandle";
+        value = android::base::StringPrintf("%d", it.GetJavaValue().i);
+        break;
+      case EncodedArrayValueIterator::ValueType::kString: {
+        type = "String";
+        dex::StringIndex string_idx = static_cast<dex::StringIndex>(it.GetJavaValue().i);
+        value = pDexFile->StringDataByIdx(string_idx);
+        break;
+      }
+      case EncodedArrayValueIterator::ValueType::kType: {
+        type = "Class";
+        dex::TypeIndex type_idx = static_cast<dex::TypeIndex>(it.GetJavaValue().i);
+        const DexFile::ClassDef* class_def = pDexFile->FindClassDef(type_idx);
+        value = pDexFile->GetClassDescriptor(*class_def);
+        value = descriptorClassToDot(value.c_str()).get();
+        break;
+      }
+      case EncodedArrayValueIterator::ValueType::kField:
+      case EncodedArrayValueIterator::ValueType::kMethod:
+      case EncodedArrayValueIterator::ValueType::kEnum:
+      case EncodedArrayValueIterator::ValueType::kArray:
+      case EncodedArrayValueIterator::ValueType::kAnnotation:
+        // Unreachable based on current EncodedArrayValueIterator::Next().
+        UNIMPLEMENTED(FATAL) << " type " << type;
+        UNREACHABLE();
+        break;
+      case EncodedArrayValueIterator::ValueType::kNull:
+        type = "Null";
+        value = "null";
+        break;
+      case EncodedArrayValueIterator::ValueType::kBoolean:
+        type = "boolean";
+        value = it.GetJavaValue().z ? "true" : "false";
+        break;
+    }
+
+    if (gOptions.outputFormat == OUTPUT_PLAIN) {
+      fprintf(gOutFile, "  link_argument[%zu] : %s (%s)\n", argument, value.c_str(), type);
+    } else {
+      fprintf(gOutFile, "<link_argument index=\"%zu\" type=\"%s\" value=", argument, type);
+      dumpEscapedString(value.c_str());
+      fprintf(gOutFile, "/>\n");
+    }
+
+    it.Next();
+    argument++;
+  }
+
+  if (gOptions.outputFormat == OUTPUT_XML) {
+    fprintf(gOutFile, "</call_site>\n");
+  }
+}
+
 /*
  * Dumps the requested sections of the file.
  */
@@ -1612,6 +1808,16 @@
     dumpClass(pDexFile, i, &package);
   }  // for
 
+  // Iterate over all method handles.
+  for (u4 i = 0; i < pDexFile->NumMethodHandles(); ++i) {
+    dumpMethodHandle(pDexFile, i);
+  }  // for
+
+  // Iterate over all call site ids.
+  for (u4 i = 0; i < pDexFile->NumCallSiteIds(); ++i) {
+    dumpCallSite(pDexFile, i);
+  }  // for
+
   // Free the last package allocated.
   if (package != nullptr) {
     fprintf(gOutFile, "</package>\n");
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 9a73830..b9be5f2 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -688,6 +688,16 @@
       orig_dex_cache->FixupResolvedMethodTypes(RelocatedCopyOf(orig_method_types),
                                                RelocatedPointerVisitor(this));
     }
+
+    GcRoot<mirror::CallSite>* orig_call_sites = orig_dex_cache->GetResolvedCallSites();
+    GcRoot<mirror::CallSite>* relocated_call_sites = RelocatedAddressOfPointer(orig_call_sites);
+    copy_dex_cache->SetField64<false>(
+        mirror::DexCache::ResolvedCallSitesOffset(),
+        static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_call_sites)));
+    if (orig_call_sites != nullptr) {
+      orig_dex_cache->FixupResolvedCallSites(RelocatedCopyOf(orig_call_sites),
+                                             RelocatedPointerVisitor(this));
+    }
   }
 }
 
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 9585ba2..c871301 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -123,6 +123,7 @@
         "memory_region.cc",
         "method_handles.cc",
         "mirror/array.cc",
+        "mirror/call_site.cc",
         "mirror/class.cc",
         "mirror/class_ext.cc",
         "mirror/dex_cache.cc",
@@ -131,6 +132,7 @@
         "mirror/field.cc",
         "mirror/method.cc",
         "mirror/method_handle_impl.cc",
+        "mirror/method_handles_lookup.cc",
         "mirror/method_type.cc",
         "mirror/object.cc",
         "mirror/reference.cc",
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 7db8368..110241b 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -70,6 +70,7 @@
 #include "jni_internal.h"
 #include "leb128.h"
 #include "linear_alloc.h"
+#include "mirror/call_site.h"
 #include "mirror/class.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_ext.h"
@@ -82,6 +83,7 @@
 #include "mirror/method.h"
 #include "mirror/method_type.h"
 #include "mirror/method_handle_impl.h"
+#include "mirror/method_handles_lookup.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/proxy.h"
@@ -695,6 +697,18 @@
   SetClassRoot(kJavaLangInvokeMethodHandleImpl, class_root);
   mirror::MethodHandleImpl::SetClass(class_root);
 
+  // Create java.lang.invoke.MethodHandles.Lookup.class root
+  class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodHandles$Lookup;");
+  CHECK(class_root != nullptr);
+  SetClassRoot(kJavaLangInvokeMethodHandlesLookup, class_root);
+  mirror::MethodHandlesLookup::SetClass(class_root);
+
+  // Create java.lang.invoke.CallSite.class root
+  class_root = FindSystemClass(self, "Ljava/lang/invoke/CallSite;");
+  CHECK(class_root != nullptr);
+  SetClassRoot(kJavaLangInvokeCallSite, class_root);
+  mirror::CallSite::SetClass(class_root);
+
   class_root = FindSystemClass(self, "Ldalvik/system/EmulatedStackFrame;");
   CHECK(class_root != nullptr);
   SetClassRoot(kDalvikSystemEmulatedStackFrame, class_root);
@@ -981,6 +995,8 @@
   mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass));
   mirror::MethodType::SetClass(GetClassRoot(kJavaLangInvokeMethodType));
   mirror::MethodHandleImpl::SetClass(GetClassRoot(kJavaLangInvokeMethodHandleImpl));
+  mirror::MethodHandlesLookup::SetClass(GetClassRoot(kJavaLangInvokeMethodHandlesLookup));
+  mirror::CallSite::SetClass(GetClassRoot(kJavaLangInvokeCallSite));
   mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference));
   mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass));
   mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
@@ -1231,12 +1247,13 @@
         if (dex_file->NumProtoIds() < num_method_types) {
           num_method_types = dex_file->NumProtoIds();
         }
-
+        const size_t num_call_sites = dex_file->NumCallSiteIds();
         CHECK_EQ(num_strings, dex_cache->NumStrings());
         CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
         CHECK_EQ(num_methods, dex_cache->NumResolvedMethods());
         CHECK_EQ(num_fields, dex_cache->NumResolvedFields());
         CHECK_EQ(num_method_types, dex_cache->NumResolvedMethodTypes());
+        CHECK_EQ(num_call_sites, dex_cache->NumResolvedCallSites());
         DexCacheArraysLayout layout(image_pointer_size_, dex_file);
         uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays();
         if (num_strings != 0u) {
@@ -1316,6 +1333,22 @@
           mirror::MethodTypeDexCachePair::Initialize(method_types);
           dex_cache->SetResolvedMethodTypes(method_types);
         }
+        if (num_call_sites != 0u) {
+          GcRoot<mirror::CallSite>* const image_resolved_call_sites =
+              dex_cache->GetResolvedCallSites();
+          GcRoot<mirror::CallSite>* const call_sites =
+              reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset());
+          for (size_t j = 0; kIsDebugBuild && j < num_call_sites; ++j) {
+            DCHECK(call_sites[j].IsNull());
+          }
+          CopyNonNull(image_resolved_call_sites,
+                      num_call_sites,
+                      call_sites,
+                      [](const GcRoot<mirror::CallSite>& elem) {
+                          return elem.IsNull();
+                      });
+          dex_cache->SetResolvedCallSites(call_sites);
+        }
       }
       {
         WriterMutexLock mu2(self, *Locks::dex_lock_);
@@ -2115,6 +2148,8 @@
   mirror::ShortArray::ResetArrayClass();
   mirror::MethodType::ResetClass();
   mirror::MethodHandleImpl::ResetClass();
+  mirror::MethodHandlesLookup::ResetClass();
+  mirror::CallSite::ResetClass();
   mirror::EmulatedStackFrame::ResetClass();
   Thread* const self = Thread::Current();
   for (const ClassLoaderData& data : class_loaders_) {
@@ -3112,6 +3147,7 @@
         last_field_idx = field_idx;
       }
     }
+
     // Load instance fields.
     LengthPrefixedArray<ArtField>* ifields = AllocArtFieldArray(self,
                                                                 allocator,
@@ -3128,6 +3164,7 @@
         last_field_idx = field_idx;
       }
     }
+
     if (UNLIKELY(num_sfields != it.NumStaticFields()) ||
         UNLIKELY(num_ifields != it.NumInstanceFields())) {
       LOG(WARNING) << "Duplicate fields in class " << klass->PrettyDescriptor()
@@ -8189,6 +8226,148 @@
   return type.Get();
 }
 
+mirror::MethodHandle* ClassLinker::ResolveMethodHandle(uint32_t method_handle_idx,
+                                                       ArtMethod* referrer)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  Thread* const self = Thread::Current();
+  const DexFile* const dex_file = referrer->GetDexFile();
+  const DexFile::MethodHandleItem& mh = dex_file->GetMethodHandle(method_handle_idx);
+
+  union {
+    ArtField* field;
+    ArtMethod* method;
+    uintptr_t field_or_method;
+  } target;
+  uint32_t num_params;
+  mirror::MethodHandle::Kind kind;
+  DexFile::MethodHandleType handle_type =
+      static_cast<DexFile::MethodHandleType>(mh.method_handle_type_);
+  switch (handle_type) {
+    case DexFile::MethodHandleType::kStaticPut: {
+      kind = mirror::MethodHandle::Kind::kStaticPut;
+      target.field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */);
+      num_params = 1;
+      break;
+    }
+    case DexFile::MethodHandleType::kStaticGet: {
+      kind = mirror::MethodHandle::Kind::kStaticGet;
+      target.field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */);
+      num_params = 0;
+      break;
+    }
+    case DexFile::MethodHandleType::kInstancePut: {
+      kind = mirror::MethodHandle::Kind::kInstancePut;
+      target.field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */);
+      num_params = 2;
+      break;
+    }
+    case DexFile::MethodHandleType::kInstanceGet: {
+      kind = mirror::MethodHandle::Kind::kInstanceGet;
+      target.field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */);
+      num_params = 1;
+      break;
+    }
+    case DexFile::MethodHandleType::kInvokeStatic: {
+      kind = mirror::MethodHandle::Kind::kInvokeStatic;
+      target.method = ResolveMethod<kNoICCECheckForCache>(self,
+                                                          mh.field_or_method_idx_,
+                                                          referrer,
+                                                          InvokeType::kStatic);
+      uint32_t shorty_length;
+      target.method->GetShorty(&shorty_length);
+      num_params = shorty_length - 1;  // Remove 1 for return value.
+      break;
+    }
+    case DexFile::MethodHandleType::kInvokeInstance: {
+      kind = mirror::MethodHandle::Kind::kInvokeVirtual;
+      target.method = ResolveMethod<kNoICCECheckForCache>(self,
+                                                          mh.field_or_method_idx_,
+                                                          referrer,
+                                                          InvokeType::kVirtual);
+      uint32_t shorty_length;
+      target.method->GetShorty(&shorty_length);
+      num_params = shorty_length - 1;  // Remove 1 for return value.
+      break;
+    }
+    case DexFile::MethodHandleType::kInvokeConstructor: {
+      UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform.";
+      num_params = 0;
+    }
+  }
+
+  StackHandleScope<5> hs(self);
+  ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
+  ObjPtr<mirror::Class> array_of_class = FindArrayClass(self, &class_type);
+  Handle<mirror::ObjectArray<mirror::Class>> method_params(hs.NewHandle(
+      mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, num_params)));
+  if (method_params.Get() == nullptr) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  Handle<mirror::Class> return_type;
+  switch (handle_type) {
+    case DexFile::MethodHandleType::kStaticPut: {
+      method_params->Set(0, target.field->GetType<true>());
+      return_type = hs.NewHandle(FindPrimitiveClass('V'));
+      break;
+    }
+    case DexFile::MethodHandleType::kStaticGet: {
+      return_type = hs.NewHandle(target.field->GetType<true>());
+      break;
+    }
+    case DexFile::MethodHandleType::kInstancePut: {
+      method_params->Set(0, target.field->GetDeclaringClass());
+      method_params->Set(1, target.field->GetType<true>());
+      return_type = hs.NewHandle(FindPrimitiveClass('V'));
+      break;
+    }
+    case DexFile::MethodHandleType::kInstanceGet: {
+      method_params->Set(0, target.field->GetDeclaringClass());
+      return_type = hs.NewHandle(target.field->GetType<true>());
+      break;
+    }
+    case DexFile::MethodHandleType::kInvokeStatic:
+    case DexFile::MethodHandleType::kInvokeInstance: {
+      // TODO(oth): This will not work for varargs methods as this
+      // requires instantiating a Transformer. This resolution step
+      // would be best done in managed code rather than in the run
+      // time (b/35235705)
+      Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
+      Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
+      DexFileParameterIterator it(*dex_file, target.method->GetPrototype());
+      for (int32_t i = 0; it.HasNext(); i++, it.Next()) {
+        const dex::TypeIndex type_idx = it.GetTypeIdx();
+        mirror::Class* klass = ResolveType(*dex_file, type_idx, dex_cache, class_loader);
+        if (nullptr == klass) {
+          DCHECK(self->IsExceptionPending());
+          return nullptr;
+        }
+        method_params->Set(i, klass);
+      }
+      return_type = hs.NewHandle(target.method->GetReturnType(true));
+      break;
+    }
+    case DexFile::MethodHandleType::kInvokeConstructor: {
+      // TODO(oth): b/35235705
+      UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform.";
+    }
+  }
+
+  if (return_type.IsNull()) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  Handle<mirror::MethodType>
+      mt(hs.NewHandle(mirror::MethodType::Create(self, return_type, method_params)));
+  if (mt.IsNull()) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+  return mirror::MethodHandleImpl::Create(self, target.field_or_method, kind, mt);
+}
+
 bool ClassLinker::IsQuickResolutionStub(const void* entry_point) const {
   return (entry_point == GetQuickResolutionStub()) ||
       (quick_resolution_trampoline_ == entry_point);
@@ -8304,7 +8483,9 @@
     "[Ljava/lang/reflect/Constructor;",
     "[Ljava/lang/reflect/Field;",
     "[Ljava/lang/reflect/Method;",
+    "Ljava/lang/invoke/CallSite;",
     "Ljava/lang/invoke/MethodHandleImpl;",
+    "Ljava/lang/invoke/MethodHandles$Lookup;",
     "Ljava/lang/invoke/MethodType;",
     "Ljava/lang/ClassLoader;",
     "Ljava/lang/Throwable;",
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 62d3c29..e27a53d 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -55,6 +55,8 @@
   class DexCacheMethodHandlesTest_Open_Test;
   class DexCacheTest_Open_Test;
   class IfTable;
+  class MethodHandle;
+  class MethodHandlesLookup;
   class MethodType;
   template<class T> class ObjectArray;
   class StackTraceElement;
@@ -106,7 +108,9 @@
     kJavaLangReflectConstructorArrayClass,
     kJavaLangReflectFieldArrayClass,
     kJavaLangReflectMethodArrayClass,
+    kJavaLangInvokeCallSite,
     kJavaLangInvokeMethodHandleImpl,
+    kJavaLangInvokeMethodHandlesLookup,
     kJavaLangInvokeMethodType,
     kJavaLangClassLoader,
     kJavaLangThrowable,
@@ -366,6 +370,12 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
 
+  // Resolve a method handle with a given ID from the DexFile. The
+  // result is not cached in the DexCache as the instance will only be
+  // used once in most circumstances.
+  mirror::MethodHandle* ResolveMethodHandle(uint32_t method_handle_idx, ArtMethod* referrer)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Returns true on success, false if there's an exception pending.
   // can_run_clinit=false allows the compiler to attempt to init a class,
   // given the restriction that no <clinit> execution is possible.
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 03105cb..ee8aed7 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -32,6 +32,7 @@
 #include "entrypoints/entrypoint_utils-inl.h"
 #include "gc/heap.h"
 #include "mirror/accessible_object.h"
+#include "mirror/call_site.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_ext.h"
 #include "mirror/dex_cache.h"
@@ -40,6 +41,7 @@
 #include "mirror/field.h"
 #include "mirror/method_type.h"
 #include "mirror/method_handle_impl.h"
+#include "mirror/method_handles_lookup.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/proxy.h"
@@ -669,11 +671,13 @@
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_), "dex");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_), "dexFile");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location");
+    addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_call_sites_), "numResolvedCallSites");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_fields_), "numResolvedFields");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_method_types_), "numResolvedMethodTypes");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_methods_), "numResolvedMethods");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_types_), "numResolvedTypes");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_strings_), "numStrings");
+    addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_call_sites_), "resolvedCallSites");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_), "resolvedFields");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_method_types_), "resolvedMethodTypes");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_), "resolvedMethods");
@@ -762,6 +766,14 @@
   }
 };
 
+struct MethodHandlesLookupOffsets : public CheckOffsets<mirror::MethodHandlesLookup> {
+  MethodHandlesLookupOffsets() : CheckOffsets<mirror::MethodHandlesLookup>(
+      false, "Ljava/lang/invoke/MethodHandles$Lookup;") {
+    addOffset(OFFSETOF_MEMBER(mirror::MethodHandlesLookup, allowed_modes_), "allowedModes");
+    addOffset(OFFSETOF_MEMBER(mirror::MethodHandlesLookup, lookup_class_), "lookupClass");
+  }
+};
+
 struct EmulatedStackFrameOffsets : public CheckOffsets<mirror::EmulatedStackFrame> {
   EmulatedStackFrameOffsets() : CheckOffsets<mirror::EmulatedStackFrame>(
       false, "Ldalvik/system/EmulatedStackFrame;") {
@@ -772,6 +784,13 @@
   }
 };
 
+struct CallSiteOffsets : public CheckOffsets<mirror::CallSite> {
+  CallSiteOffsets() : CheckOffsets<mirror::CallSite>(
+      false, "Ljava/lang/invoke/CallSite;") {
+    addOffset(OFFSETOF_MEMBER(mirror::CallSite, target_), "target");
+  }
+};
+
 // C++ fields must exactly match the fields in the Java classes. If this fails,
 // reorder the fields in the C++ class. Managed class fields are ordered by
 // ClassLinker::LinkFields.
@@ -794,7 +813,9 @@
   EXPECT_TRUE(MethodTypeOffsets().Check());
   EXPECT_TRUE(MethodHandleOffsets().Check());
   EXPECT_TRUE(MethodHandleImplOffsets().Check());
+  EXPECT_TRUE(MethodHandlesLookupOffsets().Check());
   EXPECT_TRUE(EmulatedStackFrameOffsets().Check());
+  EXPECT_TRUE(CallSiteOffsets().Check());
 }
 
 TEST_F(ClassLinkerTest, FindClassNonexistent) {
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index a44f79e..4f4bed0 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -126,6 +126,22 @@
                               mirror::Class::PrettyDescriptor(array_class).c_str()).c_str());
 }
 
+// BootstrapMethodError
+
+void ThrowBootstrapMethodError(const char* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  ThrowException("Ljava/lang/BootstrapMethodError;", nullptr, fmt, &args);
+  va_end(args);
+}
+
+void ThrowWrappedBootstrapMethodError(const char* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  ThrowWrappedException("Ljava/lang/BootstrapMethodError;", nullptr, fmt, &args);
+  va_end(args);
+}
+
 // ClassCastException
 
 void ThrowClassCastException(ObjPtr<mirror::Class> dest_type, ObjPtr<mirror::Class> src_type) {
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 76ea2ae..55a8938 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -56,6 +56,14 @@
                               ObjPtr<mirror::Class> array_class)
     REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
 
+// BootstrapMethodError
+
+void ThrowBootstrapMethodError(const char* fmt, ...)
+    REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
+void ThrowWrappedBootstrapMethodError(const char* fmt, ...)
+    REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
 // ClassCircularityError
 
 void ThrowClassCircularityError(ObjPtr<mirror::Class> c)
@@ -236,7 +244,7 @@
     __attribute__((__format__(__printf__, 2, 3)))
     REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
 
-// WrontMethodTypeException
+// WrongMethodTypeException
 void ThrowWrongMethodTypeException(mirror::MethodType* callee_type,
                                    mirror::MethodType* callsite_type)
     REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index cf90bca..20bd52b 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -264,11 +264,11 @@
 
   // MethodHandle Types
   enum class MethodHandleType : uint16_t {  // private
-    kPutStatic         = 0x0000,  // a setter for a given static field.
-    kGetStatic         = 0x0001,  // a getter for a given static field.
-    kPutInstance       = 0x0002,  // a setter for a given instance field.
-    kGetInstance       = 0x0003,  // a getter for a given instance field.
-    kInvokeStatic      = 0x0004,  // an invoker for a given static method
+    kStaticPut         = 0x0000,  // a setter for a given static field.
+    kStaticGet         = 0x0001,  // a getter for a given static field.
+    kInstancePut       = 0x0002,  // a setter for a given instance field.
+    kInstanceGet       = 0x0003,  // a getter for a given instance field.
+    kInvokeStatic      = 0x0004,  // an invoker for a given static method.
     kInvokeInstance    = 0x0005,  // invoke_instance : an invoker for a given instance method. This
                                   // can be any non-static method on any class (or interface) except
                                   // for “<init>”.
@@ -279,9 +279,9 @@
   // raw method_handle_item
   struct MethodHandleItem {
     uint16_t method_handle_type_;
-    uint16_t reserved1_;  // Reserved for future use.
-    uint16_t field_or_method_idx_;
-    uint16_t reserved2_;  // Reserved for future use.
+    uint16_t reserved1_;            // Reserved for future use.
+    uint16_t field_or_method_idx_;  // Field index for accessors, method index otherwise.
+    uint16_t reserved2_;            // Reserved for future use.
    private:
     DISALLOW_COPY_AND_ASSIGN(MethodHandleItem);
   };
@@ -722,6 +722,20 @@
     return num_method_handles_;
   }
 
+  const MethodHandleItem& GetMethodHandle(uint32_t idx) const {
+    CHECK_LT(idx, NumMethodHandles());
+    return method_handles_[idx];
+  }
+
+  uint32_t NumCallSiteIds() const {
+    return num_call_site_ids_;
+  }
+
+  const CallSiteIdItem& GetCallSiteId(uint32_t idx) const {
+    CHECK_LT(idx, NumCallSiteIds());
+    return call_site_ids_[idx];
+  }
+
   // Returns a pointer to the raw memory mapped class_data_item
   const uint8_t* GetClassData(const ClassDef& class_def) const {
     if (class_def.class_data_off_ == 0) {
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index d870e52..0b3f16a 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -2374,10 +2374,10 @@
 
   uint32_t index = item->field_or_method_idx_;
   switch (method_handle_type) {
-    case DexFile::MethodHandleType::kPutStatic:
-    case DexFile::MethodHandleType::kGetStatic:
-    case DexFile::MethodHandleType::kPutInstance:
-    case DexFile::MethodHandleType::kGetInstance: {
+    case DexFile::MethodHandleType::kStaticPut:
+    case DexFile::MethodHandleType::kStaticGet:
+    case DexFile::MethodHandleType::kInstancePut:
+    case DexFile::MethodHandleType::kInstanceGet: {
       LOAD_FIELD(field, index, "method_handle_item field_idx", return false);
       break;
     }
diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc
index 37f3ac9..091085a 100644
--- a/runtime/dex_instruction.cc
+++ b/runtime/dex_instruction.cc
@@ -407,6 +407,20 @@
             break;
           }
           FALLTHROUGH_INTENDED;
+        case INVOKE_CUSTOM:
+          if (file != nullptr) {
+            os << opcode << " {";
+            uint32_t call_site_idx = VRegB_35c();
+            for (size_t i = 0; i < VRegA_35c(); ++i) {
+              if (i != 0) {
+                os << ", ";
+              }
+              os << "v" << arg[i];
+            }
+            os << "},  // call_site@" << call_site_idx;
+            break;
+          }
+          FALLTHROUGH_INTENDED;
         default:
           os << opcode << " {v" << arg[0] << ", v" << arg[1] << ", v" << arg[2]
                        << ", v" << arg[3] << ", v" << arg[4] << "}, thing@" << VRegB_35c();
@@ -415,6 +429,8 @@
       break;
     }
     case k3rc: {
+      uint16_t first_reg = VRegC_3rc();
+      uint16_t last_reg =  VRegC_3rc() + VRegA_3rc() - 1;
       switch (Opcode()) {
         case INVOKE_VIRTUAL_RANGE:
         case INVOKE_SUPER_RANGE:
@@ -423,7 +439,7 @@
         case INVOKE_INTERFACE_RANGE:
           if (file != nullptr) {
             uint32_t method_idx = VRegB_3rc();
-            os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1))
+            os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
                << file->PrettyMethod(method_idx) << " // method@" << method_idx;
             break;
           }
@@ -431,14 +447,22 @@
         case INVOKE_VIRTUAL_RANGE_QUICK:
           if (file != nullptr) {
             uint32_t method_idx = VRegB_3rc();
-            os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1))
+            os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
                << "// vtable@" << method_idx;
             break;
           }
           FALLTHROUGH_INTENDED;
+        case INVOKE_CUSTOM_RANGE:
+          if (file != nullptr) {
+            uint32_t call_site_idx = VRegB_3rc();
+            os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
+               << "// call_site@" << call_site_idx;
+            break;
+          }
+          FALLTHROUGH_INTENDED;
         default:
-          os << StringPrintf("%s, {v%d .. v%d}, thing@%d", opcode, VRegC_3rc(),
-                             (VRegC_3rc() + VRegA_3rc() - 1), VRegB_3rc());
+          os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
+             << "thing@" << VRegB_3rc();
           break;
       }
       break;
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index 578550c..d269110 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -126,14 +126,15 @@
 
   enum IndexType {
     kIndexUnknown = 0,
-    kIndexNone,              // has no index
-    kIndexTypeRef,           // type reference index
-    kIndexStringRef,         // string reference index
-    kIndexMethodRef,         // method reference index
-    kIndexFieldRef,          // field reference index
-    kIndexFieldOffset,       // field offset (for static linked fields)
-    kIndexVtableOffset,      // vtable offset (for static linked methods)
-    kIndexMethodAndProtoRef  // method and a proto reference index (for invoke-polymorphic)
+    kIndexNone,               // has no index
+    kIndexTypeRef,            // type reference index
+    kIndexStringRef,          // string reference index
+    kIndexMethodRef,          // method reference index
+    kIndexFieldRef,           // field reference index
+    kIndexFieldOffset,        // field offset (for static linked fields)
+    kIndexVtableOffset,       // vtable offset (for static linked methods)
+    kIndexMethodAndProtoRef,  // method and a proto reference index (for invoke-polymorphic)
+    kIndexCallSiteRef,        // call site reference index
   };
 
   enum Flags {
@@ -165,31 +166,32 @@
   };
 
   enum VerifyFlag {
-    kVerifyNone               = 0x000000,
-    kVerifyRegA               = 0x000001,
-    kVerifyRegAWide           = 0x000002,
-    kVerifyRegB               = 0x000004,
-    kVerifyRegBField          = 0x000008,
-    kVerifyRegBMethod         = 0x000010,
-    kVerifyRegBNewInstance    = 0x000020,
-    kVerifyRegBString         = 0x000040,
-    kVerifyRegBType           = 0x000080,
-    kVerifyRegBWide           = 0x000100,
-    kVerifyRegC               = 0x000200,
-    kVerifyRegCField          = 0x000400,
-    kVerifyRegCNewArray       = 0x000800,
-    kVerifyRegCType           = 0x001000,
-    kVerifyRegCWide           = 0x002000,
-    kVerifyArrayData          = 0x004000,
-    kVerifyBranchTarget       = 0x008000,
-    kVerifySwitchTargets      = 0x010000,
-    kVerifyVarArg             = 0x020000,
-    kVerifyVarArgNonZero      = 0x040000,
-    kVerifyVarArgRange        = 0x080000,
-    kVerifyVarArgRangeNonZero = 0x100000,
-    kVerifyRuntimeOnly        = 0x200000,
-    kVerifyError              = 0x400000,
-    kVerifyRegHPrototype      = 0x800000
+    kVerifyNone               = 0x0000000,
+    kVerifyRegA               = 0x0000001,
+    kVerifyRegAWide           = 0x0000002,
+    kVerifyRegB               = 0x0000004,
+    kVerifyRegBField          = 0x0000008,
+    kVerifyRegBMethod         = 0x0000010,
+    kVerifyRegBNewInstance    = 0x0000020,
+    kVerifyRegBString         = 0x0000040,
+    kVerifyRegBType           = 0x0000080,
+    kVerifyRegBWide           = 0x0000100,
+    kVerifyRegC               = 0x0000200,
+    kVerifyRegCField          = 0x0000400,
+    kVerifyRegCNewArray       = 0x0000800,
+    kVerifyRegCType           = 0x0001000,
+    kVerifyRegCWide           = 0x0002000,
+    kVerifyArrayData          = 0x0004000,
+    kVerifyBranchTarget       = 0x0008000,
+    kVerifySwitchTargets      = 0x0010000,
+    kVerifyVarArg             = 0x0020000,
+    kVerifyVarArgNonZero      = 0x0040000,
+    kVerifyVarArgRange        = 0x0080000,
+    kVerifyVarArgRangeNonZero = 0x0100000,
+    kVerifyRuntimeOnly        = 0x0200000,
+    kVerifyError              = 0x0400000,
+    kVerifyRegHPrototype      = 0x0800000,
+    kVerifyRegBCallSite       = 0x1000000
   };
 
   static constexpr uint32_t kMaxVarArgRegs = 5;
diff --git a/runtime/dex_instruction_list.h b/runtime/dex_instruction_list.h
index ca2ce1d..a5ce3c2 100644
--- a/runtime/dex_instruction_list.h
+++ b/runtime/dex_instruction_list.h
@@ -271,8 +271,8 @@
   V(0xF9, UNUSED_F9, "unused-f9", k10x, kIndexUnknown, 0, kVerifyError) \
   V(0xFA, INVOKE_POLYMORPHIC, "invoke-polymorphic", k45cc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgNonZero | kVerifyRegHPrototype) \
   V(0xFB, INVOKE_POLYMORPHIC_RANGE, "invoke-polymorphic/range", k4rcc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRangeNonZero | kVerifyRegHPrototype) \
-  V(0xFC, UNUSED_FC, "unused-fc", k10x, kIndexUnknown, 0, kVerifyError) \
-  V(0xFD, UNUSED_FD, "unused-fd", k10x, kIndexUnknown, 0, kVerifyError) \
+  V(0xFC, INVOKE_CUSTOM, "invoke-custom", k35c, kIndexCallSiteRef, kContinue | kThrow, kVerifyRegBCallSite) \
+  V(0xFD, INVOKE_CUSTOM_RANGE, "invoke-custom/range", k3rc, kIndexCallSiteRef, kContinue | kThrow, kVerifyRegBCallSite) \
   V(0xFE, UNUSED_FE, "unused-fe", k10x, kIndexUnknown, 0, kVerifyError) \
   V(0xFF, UNUSED_FF, "unused-ff", k10x, kIndexUnknown, 0, kVerifyError)
 
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 3ef47c4..c2bca53 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2435,8 +2435,8 @@
 
   // Wrap raw_method_handle in a Handle for safety.
   StackHandleScope<5> hs(self);
-  Handle<mirror::MethodHandleImpl> method_handle(
-      hs.NewHandle(ObjPtr<mirror::MethodHandleImpl>::DownCast(MakeObjPtr(raw_method_handle))));
+  Handle<mirror::MethodHandle> method_handle(
+      hs.NewHandle(ObjPtr<mirror::MethodHandle>::DownCast(MakeObjPtr(raw_method_handle))));
   raw_method_handle = nullptr;
   self->EndAssertNoThreadSuspension(old_cause);
 
@@ -2497,15 +2497,14 @@
   // consecutive order.
   uint32_t unused_args[Instruction::kMaxVarArgRegs] = {};
   uint32_t first_callee_arg = first_arg + 1;
-  const bool do_assignability_check = false;
-  if (!DoInvokePolymorphic<true /* is_range */, do_assignability_check>(self,
-                                                                        resolved_method,
-                                                                        *shadow_frame,
-                                                                        method_handle,
-                                                                        method_type,
-                                                                        unused_args,
-                                                                        first_callee_arg,
-                                                                        result)) {
+  if (!DoInvokePolymorphic<true /* is_range */>(self,
+                                                resolved_method,
+                                                *shadow_frame,
+                                                method_handle,
+                                                method_type,
+                                                unused_args,
+                                                first_callee_arg,
+                                                result)) {
     DCHECK(self->IsExceptionPending());
   }
 
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 442a42e..55bd1d4 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1283,6 +1283,14 @@
           }
           dex_cache->FixupResolvedMethodTypes<kWithoutReadBarrier>(new_method_types, fixup_adapter);
         }
+        GcRoot<mirror::CallSite>* call_sites = dex_cache->GetResolvedCallSites();
+        if (call_sites != nullptr) {
+          GcRoot<mirror::CallSite>* new_call_sites = fixup_adapter.ForwardObject(call_sites);
+          if (call_sites != new_call_sites) {
+            dex_cache->SetResolvedCallSites(new_call_sites);
+          }
+          dex_cache->FixupResolvedCallSites<kWithoutReadBarrier>(new_call_sites, fixup_adapter);
+        }
       }
     }
     {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 28bcb97..3d4a2d1 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -519,7 +519,7 @@
   }
 }
 
-template<bool is_range, bool do_access_check>
+template<bool is_range>
 bool DoInvokePolymorphic(Thread* self,
                          ShadowFrame& shadow_frame,
                          const Instruction* inst,
@@ -539,8 +539,8 @@
   // was symbolically invoked in bytecode (say MethodHandle.invoke or MethodHandle.invokeExact)
   // and not the method that we'll dispatch to in the end.
   StackHandleScope<5> hs(self);
-  Handle<mirror::MethodHandleImpl> method_handle(hs.NewHandle(
-      ObjPtr<mirror::MethodHandleImpl>::DownCast(
+  Handle<mirror::MethodHandle> method_handle(hs.NewHandle(
+      ObjPtr<mirror::MethodHandle>::DownCast(
           MakeObjPtr(shadow_frame.GetVRegReference(vRegC)))));
   if (UNLIKELY(method_handle.Get() == nullptr)) {
     // Note that the invoke type is kVirtual here because a call to a signature
@@ -584,31 +584,300 @@
     // VRegC is the register holding the method handle. Arguments passed
     // to the method handle's target do not include the method handle.
     uint32_t first_arg = inst->VRegC_4rcc() + 1;
-    return DoInvokePolymorphic<is_range, do_access_check>(self,
-                                                          invoke_method,
-                                                          shadow_frame,
-                                                          method_handle,
-                                                          callsite_type,
-                                                          args /* unused */,
-                                                          first_arg,
-                                                          result);
+    return DoInvokePolymorphic<is_range>(self,
+                                         invoke_method,
+                                         shadow_frame,
+                                         method_handle,
+                                         callsite_type,
+                                         args /* unused */,
+                                         first_arg,
+                                         result);
   } else {
     // Get the register arguments for the invoke.
     inst->GetVarArgs(args, inst_data);
     // Drop the first register which is the method handle performing the invoke.
     memmove(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1));
     args[Instruction::kMaxVarArgRegs - 1] = 0;
-    return DoInvokePolymorphic<is_range, do_access_check>(self,
-                                                          invoke_method,
-                                                          shadow_frame,
-                                                          method_handle,
-                                                          callsite_type,
-                                                          args,
-                                                          args[0],
-                                                          result);
+    return DoInvokePolymorphic<is_range>(self,
+                                         invoke_method,
+                                         shadow_frame,
+                                         method_handle,
+                                         callsite_type,
+                                         args,
+                                         args[0],
+                                         result);
   }
 }
 
+static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self,
+                                                      ShadowFrame& shadow_frame,
+                                                      uint32_t call_site_idx)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ArtMethod* referrer = shadow_frame.GetMethod();
+  const DexFile* dex_file = referrer->GetDexFile();
+  const DexFile::CallSiteIdItem& csi = dex_file->GetCallSiteId(call_site_idx);
+
+  StackHandleScope<9> hs(self);
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
+  Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
+
+  CallSiteArrayValueIterator it(*dex_file, csi);
+  uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Handle<mirror::MethodHandle>
+      bootstrap(hs.NewHandle(class_linker->ResolveMethodHandle(method_handle_idx, referrer)));
+  if (bootstrap.IsNull()) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+  Handle<mirror::MethodType> bootstrap_method_type = hs.NewHandle(bootstrap->GetMethodType());
+  it.Next();
+
+  DCHECK_EQ(static_cast<size_t>(bootstrap->GetMethodType()->GetPTypes()->GetLength()), it.Size());
+  const size_t num_bootstrap_vregs = bootstrap->GetMethodType()->NumberOfVRegs();
+
+  // Set-up a shadow frame for invoking the bootstrap method handle.
+  ShadowFrameAllocaUniquePtr bootstrap_frame =
+      CREATE_SHADOW_FRAME(num_bootstrap_vregs, nullptr, referrer, shadow_frame.GetDexPC());
+  ScopedStackedShadowFramePusher pusher(
+      self, bootstrap_frame.get(), StackedShadowFrameType::kShadowFrameUnderConstruction);
+  size_t vreg = 0;
+
+  // The first parameter is a MethodHandles lookup instance.
+  {
+    Handle<mirror::Class> lookup_class(hs.NewHandle(bootstrap->GetTargetClass()));
+    ObjPtr<mirror::MethodHandlesLookup> lookup =
+        mirror::MethodHandlesLookup::Create(self, lookup_class);
+    if (lookup.IsNull()) {
+      DCHECK(self->IsExceptionPending());
+      return nullptr;
+    }
+    bootstrap_frame->SetVRegReference(vreg++, lookup.Ptr());
+  }
+
+  // The second parameter is the name to lookup.
+  {
+    dex::StringIndex name_idx(static_cast<uint32_t>(it.GetJavaValue().i));
+    ObjPtr<mirror::String> name = class_linker->ResolveString(*dex_file, name_idx, dex_cache);
+    if (name.IsNull()) {
+      DCHECK(self->IsExceptionPending());
+      return nullptr;
+    }
+    bootstrap_frame->SetVRegReference(vreg++, name.Ptr());
+  }
+  it.Next();
+
+  // The third parameter is the method type associated with the name.
+  uint32_t method_type_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+  Handle<mirror::MethodType>
+      method_type(hs.NewHandle(class_linker->ResolveMethodType(*dex_file,
+                                                               method_type_idx,
+                                                               dex_cache,
+                                                               class_loader)));
+  if (method_type.IsNull()) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+  bootstrap_frame->SetVRegReference(vreg++, method_type.Get());
+  it.Next();
+
+  // Append remaining arguments (if any).
+  while (it.HasNext()) {
+    const jvalue& jvalue = it.GetJavaValue();
+    switch (it.GetValueType()) {
+      case EncodedArrayValueIterator::ValueType::kBoolean:
+      case EncodedArrayValueIterator::ValueType::kByte:
+      case EncodedArrayValueIterator::ValueType::kChar:
+      case EncodedArrayValueIterator::ValueType::kShort:
+      case EncodedArrayValueIterator::ValueType::kInt:
+        bootstrap_frame->SetVReg(vreg, jvalue.i);
+        vreg += 1;
+        break;
+      case EncodedArrayValueIterator::ValueType::kLong:
+        bootstrap_frame->SetVRegLong(vreg, jvalue.j);
+        vreg += 2;
+        break;
+      case EncodedArrayValueIterator::ValueType::kFloat:
+        bootstrap_frame->SetVRegFloat(vreg, jvalue.f);
+        vreg += 1;
+        break;
+      case EncodedArrayValueIterator::ValueType::kDouble:
+        bootstrap_frame->SetVRegDouble(vreg, jvalue.d);
+        vreg += 2;
+        break;
+      case EncodedArrayValueIterator::ValueType::kMethodType: {
+        uint32_t idx = static_cast<uint32_t>(jvalue.i);
+        ObjPtr<mirror::MethodType> ref =
+            class_linker->ResolveMethodType(*dex_file, idx, dex_cache, class_loader);
+        if (ref.IsNull()) {
+          DCHECK(self->IsExceptionPending());
+          return nullptr;
+        }
+        bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
+        vreg += 1;
+        break;
+      }
+      case EncodedArrayValueIterator::ValueType::kMethodHandle: {
+        uint32_t idx = static_cast<uint32_t>(jvalue.i);
+        ObjPtr<mirror::MethodHandle> ref =
+            class_linker->ResolveMethodHandle(idx, referrer);
+        if (ref.IsNull()) {
+          DCHECK(self->IsExceptionPending());
+          return nullptr;
+        }
+        bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
+        vreg += 1;
+        break;
+      }
+      case EncodedArrayValueIterator::ValueType::kString: {
+        dex::StringIndex idx(static_cast<uint32_t>(jvalue.i));
+        ObjPtr<mirror::String> ref = class_linker->ResolveString(*dex_file, idx, dex_cache);
+        if (ref.IsNull()) {
+          DCHECK(self->IsExceptionPending());
+          return nullptr;
+        }
+        bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
+        vreg += 1;
+        break;
+      }
+      case EncodedArrayValueIterator::ValueType::kType: {
+        dex::TypeIndex idx(static_cast<uint32_t>(jvalue.i));
+        ObjPtr<mirror::Class> ref =
+            class_linker->ResolveType(*dex_file, idx, dex_cache, class_loader);
+        if (ref.IsNull()) {
+          DCHECK(self->IsExceptionPending());
+          return nullptr;
+        }
+        bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
+        vreg += 1;
+        break;
+      }
+      case EncodedArrayValueIterator::ValueType::kNull:
+        bootstrap_frame->SetVRegReference(vreg, nullptr);
+        vreg += 1;
+        break;
+      case EncodedArrayValueIterator::ValueType::kField:
+      case EncodedArrayValueIterator::ValueType::kMethod:
+      case EncodedArrayValueIterator::ValueType::kEnum:
+      case EncodedArrayValueIterator::ValueType::kArray:
+      case EncodedArrayValueIterator::ValueType::kAnnotation:
+        // Unreachable based on current EncodedArrayValueIterator::Next().
+        UNREACHABLE();
+    }
+
+    it.Next();
+  }
+
+  // Invoke the bootstrap method handle.
+  JValue result;
+
+  // This array of arguments is unused. DoInvokePolymorphic() operates on either a
+  // an argument array or a range, but always takes an array argument.
+  uint32_t args_unused[Instruction::kMaxVarArgRegs];
+  ArtMethod* invoke_exact =
+      jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact);
+  bool invoke_success = DoInvokePolymorphic<true /* is_range */>(self,
+                                                                 invoke_exact,
+                                                                 *bootstrap_frame,
+                                                                 bootstrap,
+                                                                 bootstrap_method_type,
+                                                                 args_unused,
+                                                                 0,
+                                                                 &result);
+  if (!invoke_success) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  Handle<mirror::Object> object(hs.NewHandle(result.GetL()));
+
+  // Check the result is not null.
+  if (UNLIKELY(object.IsNull())) {
+    ThrowNullPointerException("CallSite == null");
+    return nullptr;
+  }
+
+  // Check the result type is a subclass of CallSite.
+  if (UNLIKELY(!object->InstanceOf(mirror::CallSite::StaticClass()))) {
+    ThrowClassCastException(object->GetClass(), mirror::CallSite::StaticClass());
+    return nullptr;
+  }
+
+  Handle<mirror::CallSite> call_site =
+      hs.NewHandle(ObjPtr<mirror::CallSite>::DownCast(ObjPtr<mirror::Object>(result.GetL())));
+
+  // Check the call site target is not null as we're going to invoke it.
+  Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget());
+  if (UNLIKELY(target.IsNull())) {
+    ThrowNullPointerException("CallSite target == null");
+    return nullptr;
+  }
+
+  // Check the target method type matches the method type requested.
+  if (UNLIKELY(!target->GetMethodType()->IsExactMatch(method_type.Get()))) {
+    ThrowWrongMethodTypeException(target->GetMethodType(), method_type.Get());
+    return nullptr;
+  }
+
+  return call_site.Get();
+}
+
+template<bool is_range>
+bool DoInvokeCustom(Thread* self,
+                    ShadowFrame& shadow_frame,
+                    const Instruction* inst,
+                    uint16_t inst_data,
+                    JValue* result)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  // invoke-custom is not supported in transactions. In transactions
+  // there is a limited set of types supported. invoke-custom allows
+  // running arbitrary code and instantiating arbitrary types.
+  CHECK(!Runtime::Current()->IsActiveTransaction());
+  StackHandleScope<4> hs(self);
+  Handle<mirror::DexCache> dex_cache(hs.NewHandle(shadow_frame.GetMethod()->GetDexCache()));
+  const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+  MutableHandle<mirror::CallSite>
+      call_site(hs.NewHandle(dex_cache->GetResolvedCallSite(call_site_idx)));
+  if (call_site.IsNull()) {
+    call_site.Assign(InvokeBootstrapMethod(self, shadow_frame, call_site_idx));
+    if (UNLIKELY(call_site.IsNull())) {
+      CHECK(self->IsExceptionPending());
+      ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method",
+                                       call_site_idx);
+      result->SetJ(0);
+      return false;
+    }
+    mirror::CallSite* winning_call_site =
+        dex_cache->SetResolvedCallSite(call_site_idx, call_site.Get());
+    call_site.Assign(winning_call_site);
+  }
+
+  // CallSite.java checks the re-assignment of the call site target
+  // when mutating call site targets. We only check the target is
+  // non-null and has the right type during bootstrap method execution.
+  Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget());
+  Handle<mirror::MethodType> target_method_type = hs.NewHandle(target->GetMethodType());
+  DCHECK_EQ(static_cast<size_t>(inst->VRegA()), target_method_type->NumberOfVRegs());
+
+  uint32_t args[Instruction::kMaxVarArgRegs];
+  if (is_range) {
+    args[0] = inst->VRegC_3rc();
+  } else {
+    inst->GetVarArgs(args, inst_data);
+  }
+
+  ArtMethod* invoke_exact =
+      jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact);
+  return DoInvokePolymorphic<is_range>(self,
+                                       invoke_exact,
+                                       shadow_frame,
+                                       target,
+                                       target_method_type,
+                                       args,
+                                       args[0],
+                                       result);
+}
+
 template <bool is_range>
 inline void CopyRegisters(ShadowFrame& caller_frame,
                           ShadowFrame* callee_frame,
@@ -975,17 +1244,24 @@
 EXPLICIT_DO_CALL_TEMPLATE_DECL(true, true);
 #undef EXPLICIT_DO_CALL_TEMPLATE_DECL
 
-// Explicit DoInvokePolymorphic template function declarations.
-#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range, _do_assignability_check)  \
-  template REQUIRES_SHARED(Locks::mutator_lock_)                                          \
-  bool DoInvokePolymorphic<_is_range, _do_assignability_check>(                           \
-      Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,                   \
+// Explicit DoInvokeCustom template function declarations.
+#define EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(_is_range)               \
+  template REQUIRES_SHARED(Locks::mutator_lock_)                         \
+  bool DoInvokeCustom<_is_range>(                                        \
+      Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,  \
       uint16_t inst_data, JValue* result)
+EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(false);
+EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(true);
+#undef EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL
 
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, false);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, true);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, false);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, true);
+// Explicit DoInvokePolymorphic template function declarations.
+#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range)          \
+  template REQUIRES_SHARED(Locks::mutator_lock_)                         \
+  bool DoInvokePolymorphic<_is_range>(                                   \
+      Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,  \
+      uint16_t inst_data, JValue* result)
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false);
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true);
 #undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL
 
 // Explicit DoFilledNewArray template function declarations.
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 7ef3508..6b22af9 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -40,9 +40,11 @@
 #include "entrypoints/entrypoint_utils-inl.h"
 #include "handle_scope-inl.h"
 #include "jit/jit.h"
+#include "mirror/call_site.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache.h"
 #include "mirror/method.h"
+#include "mirror/method_handles_lookup.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/string-inl.h"
@@ -154,13 +156,21 @@
 }
 
 // Performs a signature polymorphic invoke (invoke-polymorphic/invoke-polymorphic-range).
-template<bool is_range, bool do_access_check>
+template<bool is_range>
 bool DoInvokePolymorphic(Thread* self,
                          ShadowFrame& shadow_frame,
                          const Instruction* inst,
                          uint16_t inst_data,
                          JValue* result);
 
+// Performs a custom invoke (invoke-custom/invoke-custom-range).
+template<bool is_range>
+bool DoInvokeCustom(Thread* self,
+                    ShadowFrame& shadow_frame,
+                    const Instruction* inst,
+                    uint16_t inst_data,
+                    JValue* result);
+
 // Handles invoke-virtual-quick and invoke-virtual-quick-range instructions.
 // Returns true on success, otherwise throws an exception and returns false.
 template<bool is_range>
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index a77a3fc..b191dd7 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -1524,7 +1524,7 @@
       case Instruction::INVOKE_POLYMORPHIC: {
         PREAMBLE();
         DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
-        bool success = DoInvokePolymorphic<false, do_access_check>(
+        bool success = DoInvokePolymorphic<false /* is_range */>(
             self, shadow_frame, inst, inst_data, &result_register);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx);
         break;
@@ -1532,11 +1532,27 @@
       case Instruction::INVOKE_POLYMORPHIC_RANGE: {
         PREAMBLE();
         DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
-        bool success = DoInvokePolymorphic<true, do_access_check>(
+        bool success = DoInvokePolymorphic<true /* is_range */>(
             self, shadow_frame, inst, inst_data, &result_register);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx);
         break;
       }
+      case Instruction::INVOKE_CUSTOM: {
+        PREAMBLE();
+        DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+        bool success = DoInvokeCustom<false /* is_range */>(
+            self, shadow_frame, inst, inst_data, &result_register);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+        break;
+      }
+      case Instruction::INVOKE_CUSTOM_RANGE: {
+        PREAMBLE();
+        DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+        bool success = DoInvokeCustom<true /* is_range */>(
+            self, shadow_frame, inst, inst_data, &result_register);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+        break;
+      }
       case Instruction::NEG_INT:
         PREAMBLE();
         shadow_frame.SetVReg(
@@ -2315,7 +2331,7 @@
         break;
       case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
       case Instruction::UNUSED_F3 ... Instruction::UNUSED_F9:
-      case Instruction::UNUSED_FC ... Instruction::UNUSED_FF:
+      case Instruction::UNUSED_FE ... Instruction::UNUSED_FF:
       case Instruction::UNUSED_79:
       case Instruction::UNUSED_7A:
         UnexpectedOpcode(inst, shadow_frame);
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 99886e5..9825ad6 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -555,7 +555,7 @@
                                    Handle<mirror::MethodType> callee_type,
                                    Thread* self,
                                    ShadowFrame& shadow_frame,
-                                   Handle<mirror::MethodHandleImpl> receiver,
+                                   Handle<mirror::MethodHandle> receiver,
                                    const uint32_t (&args)[Instruction::kMaxVarArgRegs],
                                    uint32_t first_arg,
                                    JValue* result)
@@ -645,7 +645,7 @@
 template <bool is_range>
 bool DoInvokePolymorphicUnchecked(Thread* self,
                                   ShadowFrame& shadow_frame,
-                                  Handle<mirror::MethodHandleImpl> method_handle,
+                                  Handle<mirror::MethodHandle> method_handle,
                                   Handle<mirror::MethodType> callsite_type,
                                   const uint32_t (&args)[Instruction::kMaxVarArgRegs],
                                   uint32_t first_arg,
@@ -780,7 +780,6 @@
 }
 
 // Helper for setters in invoke-polymorphic.
-template <bool do_assignability_check>
 inline bool DoFieldPutForInvokePolymorphic(Thread* self,
                                            ShadowFrame& shadow_frame,
                                            ObjPtr<mirror::Object>& obj,
@@ -788,30 +787,33 @@
                                            Primitive::Type field_type,
                                            const JValue& value)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  static const bool kTransaction = false;
+  DCHECK(!Runtime::Current()->IsActiveTransaction());
+  static const bool kTransaction = false;         // Not in a transaction.
+  static const bool kAssignabilityCheck = false;  // No access check.
   switch (field_type) {
     case Primitive::kPrimBoolean:
-      return DoFieldPutCommon<Primitive::kPrimBoolean, do_assignability_check, kTransaction>(
-          self, shadow_frame, obj, field, value);
+      return
+          DoFieldPutCommon<Primitive::kPrimBoolean, kAssignabilityCheck, kTransaction>(
+              self, shadow_frame, obj, field, value);
     case Primitive::kPrimByte:
-      return DoFieldPutCommon<Primitive::kPrimByte, do_assignability_check, kTransaction>(
+      return DoFieldPutCommon<Primitive::kPrimByte, kAssignabilityCheck, kTransaction>(
           self, shadow_frame, obj, field, value);
     case Primitive::kPrimChar:
-      return DoFieldPutCommon<Primitive::kPrimChar, do_assignability_check, kTransaction>(
+      return DoFieldPutCommon<Primitive::kPrimChar, kAssignabilityCheck, kTransaction>(
           self, shadow_frame, obj, field, value);
     case Primitive::kPrimShort:
-      return DoFieldPutCommon<Primitive::kPrimShort, do_assignability_check, kTransaction>(
+      return DoFieldPutCommon<Primitive::kPrimShort, kAssignabilityCheck, kTransaction>(
           self, shadow_frame, obj, field, value);
     case Primitive::kPrimInt:
     case Primitive::kPrimFloat:
-      return DoFieldPutCommon<Primitive::kPrimInt, do_assignability_check, kTransaction>(
+      return DoFieldPutCommon<Primitive::kPrimInt, kAssignabilityCheck, kTransaction>(
           self, shadow_frame, obj, field, value);
     case Primitive::kPrimLong:
     case Primitive::kPrimDouble:
-      return DoFieldPutCommon<Primitive::kPrimLong, do_assignability_check, kTransaction>(
+      return DoFieldPutCommon<Primitive::kPrimLong, kAssignabilityCheck, kTransaction>(
           self, shadow_frame, obj, field, value);
     case Primitive::kPrimNot:
-      return DoFieldPutCommon<Primitive::kPrimNot, do_assignability_check, kTransaction>(
+      return DoFieldPutCommon<Primitive::kPrimNot, kAssignabilityCheck, kTransaction>(
           self, shadow_frame, obj, field, value);
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable: " << field_type;
@@ -855,10 +857,10 @@
   return field_value;
 }
 
-template <bool is_range, bool do_conversions, bool do_assignability_check>
+template <bool is_range, bool do_conversions>
 bool DoInvokePolymorphicFieldAccess(Thread* self,
                                     ShadowFrame& shadow_frame,
-                                    Handle<mirror::MethodHandleImpl> method_handle,
+                                    Handle<mirror::MethodHandle> method_handle,
                                     Handle<mirror::MethodType> callsite_type,
                                     const uint32_t (&args)[Instruction::kMaxVarArgRegs],
                                     uint32_t first_arg,
@@ -903,12 +905,7 @@
         return false;
       }
       ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg);
-      return DoFieldPutForInvokePolymorphic<do_assignability_check>(self,
-                                                                    shadow_frame,
-                                                                    obj,
-                                                                    field,
-                                                                    field_type,
-                                                                    value);
+      return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value);
     }
     case mirror::MethodHandle::kStaticPut: {
       ObjPtr<mirror::Object> obj = GetAndInitializeDeclaringClass(self, field);
@@ -922,12 +919,7 @@
         DCHECK(self->IsExceptionPending());
         return false;
       }
-      return DoFieldPutForInvokePolymorphic<do_assignability_check>(self,
-                                                                    shadow_frame,
-                                                                    obj,
-                                                                    field,
-                                                                    field_type,
-                                                                    value);
+      return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value);
     }
     default:
       LOG(FATAL) << "Unreachable: " << handle_kind;
@@ -935,10 +927,10 @@
   }
 }
 
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
 static inline bool DoInvokePolymorphicNonExact(Thread* self,
                                                ShadowFrame& shadow_frame,
-                                               Handle<mirror::MethodHandleImpl> method_handle,
+                                               Handle<mirror::MethodHandle> method_handle,
                                                Handle<mirror::MethodType> callsite_type,
                                                const uint32_t (&args)[Instruction::kMaxVarArgRegs],
                                                uint32_t first_arg,
@@ -959,7 +951,7 @@
   if (IsFieldAccess(handle_kind)) {
     if (UNLIKELY(callsite_type->IsExactMatch(handle_type.Ptr()))) {
       const bool do_convert = false;
-      return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>(
+      return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
           self,
           shadow_frame,
           method_handle,
@@ -969,7 +961,7 @@
           result);
     } else {
       const bool do_convert = true;
-      return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>(
+      return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
           self,
           shadow_frame,
           method_handle,
@@ -999,10 +991,10 @@
   }
 }
 
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
 bool DoInvokePolymorphicExact(Thread* self,
                               ShadowFrame& shadow_frame,
-                              Handle<mirror::MethodHandleImpl> method_handle,
+                              Handle<mirror::MethodHandle> method_handle,
                               Handle<mirror::MethodType> callsite_type,
                               const uint32_t (&args)[Instruction::kMaxVarArgRegs],
                               uint32_t first_arg,
@@ -1018,13 +1010,13 @@
       ThrowWrongMethodTypeException(nominal_type.Ptr(), callsite_type.Get());
       return false;
     }
-    return DoInvokePolymorphicNonExact<is_range, do_assignability_check>(self,
-                                                                         shadow_frame,
-                                                                         method_handle,
-                                                                         callsite_type,
-                                                                         args,
-                                                                         first_arg,
-                                                                         result);
+    return DoInvokePolymorphicNonExact<is_range>(self,
+                                                 shadow_frame,
+                                                 method_handle,
+                                                 callsite_type,
+                                                 args,
+                                                 first_arg,
+                                                 result);
   }
 
   ObjPtr<mirror::MethodType> handle_type(method_handle->GetMethodType());
@@ -1036,7 +1028,7 @@
   const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind();
   if (IsFieldAccess(handle_kind)) {
     const bool do_convert = false;
-    return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>(
+    return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
         self,
         shadow_frame,
         method_handle,
@@ -1057,51 +1049,49 @@
 
 }  // namespace
 
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
 bool DoInvokePolymorphic(Thread* self,
                          ArtMethod* invoke_method,
                          ShadowFrame& shadow_frame,
-                         Handle<mirror::MethodHandleImpl> method_handle,
+                         Handle<mirror::MethodHandle> method_handle,
                          Handle<mirror::MethodType> callsite_type,
                          const uint32_t (&args)[Instruction::kMaxVarArgRegs],
                          uint32_t first_arg,
                          JValue* result)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (IsMethodHandleInvokeExact(invoke_method)) {
-    return DoInvokePolymorphicExact<is_range, do_assignability_check>(self,
-                                                                      shadow_frame,
-                                                                      method_handle,
-                                                                      callsite_type,
-                                                                      args,
-                                                                      first_arg,
-                                                                      result);
+    return DoInvokePolymorphicExact<is_range>(self,
+                                              shadow_frame,
+                                              method_handle,
+                                              callsite_type,
+                                              args,
+                                              first_arg,
+                                              result);
   } else {
-    return DoInvokePolymorphicNonExact<is_range, do_assignability_check>(self,
-                                                                         shadow_frame,
-                                                                         method_handle,
-                                                                         callsite_type,
-                                                                         args,
-                                                                         first_arg,
-                                                                         result);
+    return DoInvokePolymorphicNonExact<is_range>(self,
+                                                 shadow_frame,
+                                                 method_handle,
+                                                 callsite_type,
+                                                 args,
+                                                 first_arg,
+                                                 result);
   }
 }
 
-#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range, _do_assignability_check) \
-template REQUIRES_SHARED(Locks::mutator_lock_)                                           \
-bool DoInvokePolymorphic<_is_range, _do_assignability_check>(                            \
-    Thread* self,                                                                        \
-    ArtMethod* invoke_method,                                                            \
-    ShadowFrame& shadow_frame,                                                           \
-    Handle<mirror::MethodHandleImpl> method_handle,                                      \
-    Handle<mirror::MethodType> callsite_type,                                            \
-    const uint32_t (&args)[Instruction::kMaxVarArgRegs],                                 \
-    uint32_t first_arg,                                                                  \
-    JValue* result)
+#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range)  \
+  template REQUIRES_SHARED(Locks::mutator_lock_)                 \
+  bool DoInvokePolymorphic<_is_range>(                           \
+      Thread* self,                                              \
+      ArtMethod* invoke_method,                                  \
+      ShadowFrame& shadow_frame,                                 \
+      Handle<mirror::MethodHandle> method_handle,                \
+      Handle<mirror::MethodType> callsite_type,                  \
+      const uint32_t (&args)[Instruction::kMaxVarArgRegs],       \
+      uint32_t first_arg,                                        \
+      JValue* result)
 
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, true);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, false);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, true);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, false);
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true);
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false);
 #undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL
 
 }  // namespace art
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 734d7c7..5bea0ab 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -27,7 +27,7 @@
 namespace art {
 
 namespace mirror {
-  class MethodHandleImpl;
+  class MethodHandle;
   class MethodType;
 }  // mirror
 
@@ -202,11 +202,11 @@
   size_t arg_index_;
 };
 
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
 bool DoInvokePolymorphic(Thread* self,
                          ArtMethod* invoke_method,
                          ShadowFrame& shadow_frame,
-                         Handle<mirror::MethodHandleImpl> method_handle,
+                         Handle<mirror::MethodHandle> method_handle,
                          Handle<mirror::MethodType> callsite_type,
                          const uint32_t (&args)[Instruction::kMaxVarArgRegs],
                          uint32_t first_arg,
diff --git a/runtime/mirror/call_site.cc b/runtime/mirror/call_site.cc
new file mode 100644
index 0000000..eb613df
--- /dev/null
+++ b/runtime/mirror/call_site.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "call_site.h"
+
+#include "class-inl.h"
+#include "gc_root-inl.h"
+
+namespace art {
+namespace mirror {
+
+GcRoot<mirror::Class> CallSite::static_class_;
+
+mirror::CallSite* CallSite::Create(Thread* const self, Handle<MethodHandle> target) {
+  StackHandleScope<1> hs(self);
+  Handle<mirror::CallSite> cs(
+      hs.NewHandle(ObjPtr<CallSite>::DownCast(StaticClass()->AllocObject(self))));
+  CHECK(!Runtime::Current()->IsActiveTransaction());
+  cs->SetFieldObject<false>(TargetOffset(), target.Get());
+  return cs.Get();
+}
+
+void CallSite::SetClass(Class* klass) {
+  CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+  CHECK(klass != nullptr);
+  static_class_ = GcRoot<Class>(klass);
+}
+
+void CallSite::ResetClass() {
+  CHECK(!static_class_.IsNull());
+  static_class_ = GcRoot<Class>(nullptr);
+}
+
+void CallSite::VisitRoots(RootVisitor* visitor) {
+  static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+}  // namespace mirror
+}  // namespace art
diff --git a/runtime/mirror/call_site.h b/runtime/mirror/call_site.h
new file mode 100644
index 0000000..db244a5
--- /dev/null
+++ b/runtime/mirror/call_site.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_MIRROR_CALL_SITE_H_
+#define ART_RUNTIME_MIRROR_CALL_SITE_H_
+
+#include "mirror/method_handle_impl.h"
+#include "utils.h"
+
+namespace art {
+
+struct CallSiteOffsets;
+
+namespace mirror {
+
+// C++ mirror of java.lang.invoke.CallSite
+class MANAGED CallSite : public Object {
+ public:
+  static mirror::CallSite* Create(Thread* const self,
+                                  Handle<MethodHandle> method_handle)
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+  static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return static_class_.Read();
+  }
+
+  MethodHandle* GetTarget() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetFieldObject<MethodHandle>(TargetOffset());
+  }
+
+  static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+  static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+  static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+  static inline MemberOffset TargetOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(CallSite, target_));
+  }
+
+  HeapReference<mirror::MethodHandle> target_;
+
+  static GcRoot<mirror::Class> static_class_;  // java.lang.invoke.CallSite.class
+
+  friend struct art::CallSiteOffsets;  // for verifying offset information
+  DISALLOW_IMPLICIT_CONSTRUCTORS(CallSite);
+};
+
+}  // namespace mirror
+}  // namespace art
+
+#endif  // ART_RUNTIME_MIRROR_CALL_SITE_H_
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index a59bb7b..973c8ed 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -26,6 +26,7 @@
 #include "base/logging.h"
 #include "gc_root.h"
 #include "mirror/class.h"
+#include "mirror/call_site.h"
 #include "mirror/method_type.h"
 #include "runtime.h"
 #include "obj_ptr.h"
@@ -106,6 +107,35 @@
   Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
 }
 
+inline CallSite* DexCache::GetResolvedCallSite(uint32_t call_site_idx) {
+  DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+  DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds());
+  GcRoot<mirror::CallSite>& target = GetResolvedCallSites()[call_site_idx];
+  Atomic<GcRoot<mirror::CallSite>>& ref =
+      reinterpret_cast<Atomic<GcRoot<mirror::CallSite>>&>(target);
+  return ref.LoadSequentiallyConsistent().Read();
+}
+
+inline CallSite* DexCache::SetResolvedCallSite(uint32_t call_site_idx, CallSite* call_site) {
+  DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+  DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds());
+
+  GcRoot<mirror::CallSite> null_call_site(nullptr);
+  GcRoot<mirror::CallSite> candidate(call_site);
+  GcRoot<mirror::CallSite>& target = GetResolvedCallSites()[call_site_idx];
+
+  // The first assignment for a given call site wins.
+  Atomic<GcRoot<mirror::CallSite>>& ref =
+      reinterpret_cast<Atomic<GcRoot<mirror::CallSite>>&>(target);
+  if (ref.CompareExchangeStrongSequentiallyConsistent(null_call_site, candidate)) {
+    // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
+    Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
+    return call_site;
+  } else {
+    return target.Read();
+  }
+}
+
 inline ArtField* DexCache::GetResolvedField(uint32_t field_idx, PointerSize ptr_size) {
   DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
   DCHECK_LT(field_idx, NumResolvedFields());  // NOTE: Unchecked, i.e. not throwing AIOOB.
@@ -208,6 +238,11 @@
 
     VisitDexCachePairs<mirror::MethodType, kReadBarrierOption, Visitor>(
         GetResolvedMethodTypes(), NumResolvedMethodTypes(), visitor);
+
+    GcRoot<mirror::CallSite>* resolved_call_sites = GetResolvedCallSites();
+    for (size_t i = 0, num_call_sites = NumResolvedCallSites(); i != num_call_sites; ++i) {
+      visitor.VisitRootIfNonNull(resolved_call_sites[i].AddressWithoutBarrier());
+    }
   }
 }
 
@@ -246,6 +281,17 @@
   }
 }
 
+template <ReadBarrierOption kReadBarrierOption, typename Visitor>
+inline void DexCache::FixupResolvedCallSites(GcRoot<mirror::CallSite>* dest,
+                                             const Visitor& visitor) {
+  GcRoot<mirror::CallSite>* src = GetResolvedCallSites();
+  for (size_t i = 0, count = NumResolvedCallSites(); i < count; ++i) {
+    mirror::CallSite* source = src[i].Read<kReadBarrierOption>();
+    mirror::CallSite* new_source = visitor(source);
+    dest[i] = GcRoot<mirror::CallSite>(new_source);
+  }
+}
+
 }  // namespace mirror
 }  // namespace art
 
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index 741cf3b..0f6acab 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -90,6 +90,10 @@
         raw_arrays + layout.MethodTypesOffset());
   }
 
+  GcRoot<mirror::CallSite>* call_sites = (dex_file->NumCallSiteIds() == 0)
+      ? nullptr
+      : reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset());
+
   DCHECK_ALIGNED(raw_arrays, alignof(mirror::StringDexCacheType)) <<
                  "Expected raw_arrays to align to StringDexCacheType.";
   DCHECK_ALIGNED(layout.StringsOffset(), alignof(mirror::StringDexCacheType)) <<
@@ -117,6 +121,9 @@
       CHECK_EQ(method_types[i].load(std::memory_order_relaxed).index, 0u);
       CHECK(method_types[i].load(std::memory_order_relaxed).object.IsNull());
     }
+    for (size_t i = 0; i < dex_file->NumCallSiteIds(); ++i) {
+      CHECK(call_sites[i].IsNull());
+    }
   }
   if (strings != nullptr) {
     mirror::StringDexCachePair::Initialize(strings);
@@ -136,6 +143,8 @@
                   dex_file->NumFieldIds(),
                   method_types,
                   num_method_types,
+                  call_sites,
+                  dex_file->NumCallSiteIds(),
                   image_pointer_size);
 }
 
@@ -151,6 +160,8 @@
                     uint32_t num_resolved_fields,
                     MethodTypeDexCacheType* resolved_method_types,
                     uint32_t num_resolved_method_types,
+                    GcRoot<CallSite>* resolved_call_sites,
+                    uint32_t num_resolved_call_sites,
                     PointerSize pointer_size) {
   CHECK(dex_file != nullptr);
   CHECK(location != nullptr);
@@ -159,6 +170,7 @@
   CHECK_EQ(num_resolved_methods != 0u, resolved_methods != nullptr);
   CHECK_EQ(num_resolved_fields != 0u, resolved_fields != nullptr);
   CHECK_EQ(num_resolved_method_types != 0u, resolved_method_types != nullptr);
+  CHECK_EQ(num_resolved_call_sites != 0u, resolved_call_sites != nullptr);
 
   SetDexFile(dex_file);
   SetLocation(location);
@@ -167,11 +179,13 @@
   SetResolvedMethods(resolved_methods);
   SetResolvedFields(resolved_fields);
   SetResolvedMethodTypes(resolved_method_types);
+  SetResolvedCallSites(resolved_call_sites);
   SetField32<false>(NumStringsOffset(), num_strings);
   SetField32<false>(NumResolvedTypesOffset(), num_resolved_types);
   SetField32<false>(NumResolvedMethodsOffset(), num_resolved_methods);
   SetField32<false>(NumResolvedFieldsOffset(), num_resolved_fields);
   SetField32<false>(NumResolvedMethodTypesOffset(), num_resolved_method_types);
+  SetField32<false>(NumResolvedCallSitesOffset(), num_resolved_call_sites);
 
   Runtime* const runtime = Runtime::Current();
   if (runtime->HasResolutionMethod()) {
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 6f88cc5..10bb5aa 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -36,6 +36,7 @@
 
 namespace mirror {
 
+class CallSite;
 class MethodType;
 class String;
 
@@ -163,6 +164,10 @@
   void FixupResolvedMethodTypes(MethodTypeDexCacheType* dest, const Visitor& visitor)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
+  void FixupResolvedCallSites(GcRoot<mirror::CallSite>* dest, const Visitor& visitor)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   String* GetLocation() REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_));
   }
@@ -191,6 +196,10 @@
     return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_method_types_);
   }
 
+  static MemberOffset ResolvedCallSitesOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_call_sites_);
+  }
+
   static MemberOffset NumStringsOffset() {
     return OFFSET_OF_OBJECT_MEMBER(DexCache, num_strings_);
   }
@@ -211,6 +220,10 @@
     return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_method_types_);
   }
 
+  static MemberOffset NumResolvedCallSitesOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_call_sites_);
+  }
+
   mirror::String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -244,7 +257,18 @@
 
   MethodType* GetResolvedMethodType(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) REQUIRES_SHARED(Locks::mutator_lock_);
+  void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  CallSite* GetResolvedCallSite(uint32_t call_site_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+
+  // Attempts to bind |call_site_idx| to the call site |resolved|. The
+  // caller must use the return value in place of |resolved|. This is
+  // because multiple threads can invoke the bootstrap method each
+  // producing a call site, but the method handle invocation on the
+  // call site must be on a common agreed value.
+  CallSite* SetResolvedCallSite(uint32_t call_site_idx, CallSite* resolved) WARN_UNUSED
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetFieldPtr64<StringDexCacheType*>(StringsOffset());
@@ -295,6 +319,18 @@
     SetFieldPtr<false>(ResolvedMethodTypesOffset(), resolved_method_types);
   }
 
+  GcRoot<CallSite>* GetResolvedCallSites()
+      ALWAYS_INLINE
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetFieldPtr<GcRoot<CallSite>*>(ResolvedCallSitesOffset());
+  }
+
+  void SetResolvedCallSites(GcRoot<CallSite>* resolved_call_sites)
+      ALWAYS_INLINE
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    SetFieldPtr<false>(ResolvedCallSitesOffset(), resolved_call_sites);
+  }
+
   size_t NumStrings() REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetField32(NumStringsOffset());
   }
@@ -315,6 +351,10 @@
     return GetField32(NumResolvedMethodTypesOffset());
   }
 
+  size_t NumResolvedCallSites() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetField32(NumResolvedCallSitesOffset());
+  }
+
   const DexFile* GetDexFile() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetFieldPtr<const DexFile*>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_));
   }
@@ -346,8 +386,10 @@
             uint32_t num_resolved_methods,
             ArtField** resolved_fields,
             uint32_t num_resolved_fields,
-            MethodTypeDexCacheType* resolved_methodtypes,
-            uint32_t num_resolved_methodtypes,
+            MethodTypeDexCacheType* resolved_method_types,
+            uint32_t num_resolved_method_types,
+            GcRoot<CallSite>* resolved_call_sites,
+            uint32_t num_resolved_call_sites,
             PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -362,6 +404,8 @@
   HeapReference<Object> dex_;
   HeapReference<String> location_;
   uint64_t dex_file_;               // const DexFile*
+  uint64_t resolved_call_sites_;    // GcRoot<CallSite>* array with num_resolved_call_sites_
+                                    // elements.
   uint64_t resolved_fields_;        // ArtField*, array with num_resolved_fields_ elements.
   uint64_t resolved_method_types_;  // std::atomic<MethodTypeDexCachePair>* array with
                                     // num_resolved_method_types_ elements.
@@ -370,6 +414,7 @@
   uint64_t strings_;                // std::atomic<StringDexCachePair>*, array with num_strings_
                                     // elements.
 
+  uint32_t num_resolved_call_sites_;    // Number of elements in the call_sites_ array.
   uint32_t num_resolved_fields_;        // Number of elements in the resolved_fields_ array.
   uint32_t num_resolved_method_types_;  // Number of elements in the resolved_method_types_ array.
   uint32_t num_resolved_methods_;       // Number of elements in the resolved_methods_ array.
diff --git a/runtime/mirror/method_handle_impl.cc b/runtime/mirror/method_handle_impl.cc
index 4f1c448..fa4d25a 100644
--- a/runtime/mirror/method_handle_impl.cc
+++ b/runtime/mirror/method_handle_impl.cc
@@ -28,6 +28,18 @@
   return klass;
 }
 
+void MethodHandle::Initialize(uintptr_t art_field_or_method,
+                              Kind kind,
+                              Handle<MethodType> method_type)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  CHECK(!Runtime::Current()->IsActiveTransaction());
+  SetFieldObject<false>(CachedSpreadInvokerOffset(), nullptr);
+  SetFieldObject<false>(NominalTypeOffset(), nullptr);
+  SetFieldObject<false>(MethodTypeOffset(), method_type.Get());
+  SetField32<false>(HandleKindOffset(), static_cast<uint32_t>(kind));
+  SetField64<false>(ArtFieldOrMethodOffset(), art_field_or_method);
+}
+
 GcRoot<mirror::Class> MethodHandleImpl::static_class_;
 
 void MethodHandleImpl::SetClass(Class* klass) {
@@ -45,5 +57,17 @@
   static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
 }
 
+mirror::MethodHandleImpl* MethodHandleImpl::Create(Thread* const self,
+                                                   uintptr_t art_field_or_method,
+                                                   MethodHandle::Kind kind,
+                                                   Handle<MethodType> method_type)
+    REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
+  StackHandleScope<1> hs(self);
+  Handle<mirror::MethodHandleImpl> mh(
+      hs.NewHandle(ObjPtr<MethodHandleImpl>::DownCast(StaticClass()->AllocObject(self))));
+  mh->Initialize(art_field_or_method, kind, method_type);
+  return mh.Get();
+}
+
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
index 53d267b..9938af8 100644
--- a/runtime/mirror/method_handle_impl.h
+++ b/runtime/mirror/method_handle_impl.h
@@ -17,10 +17,11 @@
 #ifndef ART_RUNTIME_MIRROR_METHOD_HANDLE_IMPL_H_
 #define ART_RUNTIME_MIRROR_METHOD_HANDLE_IMPL_H_
 
+#include "art_field.h"
+#include "art_method.h"
 #include "class.h"
 #include "gc_root.h"
 #include "object-inl.h"
-#include "method_handles.h"
 #include "method_type.h"
 
 namespace art {
@@ -82,10 +83,19 @@
         GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_)));
   }
 
+  ObjPtr<mirror::Class> GetTargetClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+    Kind kind = GetHandleKind();
+    return (kind <= kLastValidKind) ?
+        GetTargetMethod()->GetDeclaringClass() : GetTargetField()->GetDeclaringClass();
+  }
+
   static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
 
+ protected:
+  void Initialize(uintptr_t art_field_or_method, Kind kind, Handle<MethodType> method_type)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
-  // NOTE: cached_spread_invoker_ isn't used by the runtime.
   HeapReference<mirror::MethodHandle> cached_spread_invoker_;
   HeapReference<mirror::MethodType> nominal_type_;
   HeapReference<mirror::MethodType> method_type_;
@@ -93,6 +103,9 @@
   uint64_t art_field_or_method_;
 
  private:
+  static MemberOffset CachedSpreadInvokerOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodHandle, cached_spread_invoker_));
+  }
   static MemberOffset NominalTypeOffset() {
     return MemberOffset(OFFSETOF_MEMBER(MethodHandle, nominal_type_));
   }
@@ -113,6 +126,12 @@
 // C++ mirror of java.lang.invoke.MethodHandleImpl
 class MANAGED MethodHandleImpl : public MethodHandle {
  public:
+  static mirror::MethodHandleImpl* Create(Thread* const self,
+                                          uintptr_t art_field_or_method,
+                                          MethodHandle::Kind kind,
+                                          Handle<MethodType> method_type)
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
   static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
     return static_class_.Read();
   }
diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc
new file mode 100644
index 0000000..c758e54
--- /dev/null
+++ b/runtime/mirror/method_handles_lookup.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "method_handles_lookup.h"
+
+#include "class.h"
+#include "gc_root-inl.h"
+#include "object-inl.h"
+#include "handle_scope.h"
+#include "modifiers.h"
+
+namespace art {
+namespace mirror {
+
+GcRoot<mirror::Class> MethodHandlesLookup::static_class_;
+
+void MethodHandlesLookup::SetClass(Class* klass) {
+  CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+  CHECK(klass != nullptr);
+  static_class_ = GcRoot<Class>(klass);
+}
+
+void MethodHandlesLookup::ResetClass() {
+  CHECK(!static_class_.IsNull());
+  static_class_ = GcRoot<Class>(nullptr);
+}
+
+void MethodHandlesLookup::VisitRoots(RootVisitor* visitor) {
+  static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+MethodHandlesLookup* MethodHandlesLookup::Create(Thread* const self, Handle<Class> lookup_class)
+  REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
+  static constexpr uint32_t kAllModes = kAccPublic | kAccPrivate | kAccProtected | kAccStatic;
+
+  StackHandleScope<1> hs(self);
+  Handle<MethodHandlesLookup> mhl(
+      hs.NewHandle(ObjPtr<MethodHandlesLookup>::DownCast(StaticClass()->AllocObject(self))));
+  mhl->SetFieldObject<false>(LookupClassOffset(), lookup_class.Get());
+  mhl->SetField32<false>(AllowedModesOffset(), kAllModes);
+  return mhl.Get();
+}
+
+}  // namespace mirror
+}  // namespace art
diff --git a/runtime/mirror/method_handles_lookup.h b/runtime/mirror/method_handles_lookup.h
new file mode 100644
index 0000000..63eb428
--- /dev/null
+++ b/runtime/mirror/method_handles_lookup.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_
+#define ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_
+
+#include "obj_ptr.h"
+#include "gc_root.h"
+#include "object.h"
+#include "handle.h"
+#include "utils.h"
+
+namespace art {
+
+struct MethodHandlesLookupOffsets;
+class RootVisitor;
+
+namespace mirror {
+
+// C++ mirror of java.lang.invoke.MethodHandles.Lookup
+class MANAGED MethodHandlesLookup : public Object {
+ public:
+  static mirror::MethodHandlesLookup* Create(Thread* const self,
+                                             Handle<Class> lookup_class)
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+  static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return static_class_.Read();
+  }
+
+  static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+  static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+  static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+  static MemberOffset AllowedModesOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodHandlesLookup, allowed_modes_));
+  }
+
+  static MemberOffset LookupClassOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodHandlesLookup, lookup_class_));
+  }
+
+  HeapReference<mirror::Class> lookup_class_;
+
+  int32_t allowed_modes_;
+
+  static GcRoot<mirror::Class> static_class_;  // java.lang.invoke.MethodHandles.Lookup.class
+
+  friend struct art::MethodHandlesLookupOffsets;  // for verifying offset information
+  DISALLOW_IMPLICIT_CONSTRUCTORS(MethodHandlesLookup);
+};
+
+}  // namespace mirror
+}  // namespace art
+
+#endif  // ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_
diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc
index 5d77a16..4b8dfac 100644
--- a/runtime/mirror/method_type.cc
+++ b/runtime/mirror/method_type.cc
@@ -44,6 +44,22 @@
   return mt.Get();
 }
 
+size_t MethodType::NumberOfVRegs() REQUIRES_SHARED(Locks::mutator_lock_) {
+  mirror::ObjectArray<Class>* const p_types = GetPTypes();
+  const int32_t p_types_length = p_types->GetLength();
+
+  // Initialize |num_vregs| with number of parameters and only increment it for
+  // types requiring a second vreg.
+  size_t num_vregs = static_cast<size_t>(p_types_length);
+  for (int32_t i = 0; i < p_types_length; ++i) {
+    mirror::Class* klass = p_types->GetWithoutChecks(i);
+    if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) {
+      ++num_vregs;
+    }
+  }
+  return num_vregs;
+}
+
 bool MethodType::IsExactMatch(mirror::MethodType* target) REQUIRES_SHARED(Locks::mutator_lock_) {
   mirror::ObjectArray<Class>* const p_types = GetPTypes();
   const int32_t params_length = p_types->GetLength();
diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h
index 9a98143..374bbe5 100644
--- a/runtime/mirror/method_type.h
+++ b/runtime/mirror/method_type.h
@@ -44,6 +44,10 @@
     return GetFieldObject<ObjectArray<Class>>(OFFSET_OF_OBJECT_MEMBER(MethodType, p_types_));
   }
 
+  // Number of virtual registers required to hold the parameters for
+  // this method type.
+  size_t NumberOfVRegs() REQUIRES_SHARED(Locks::mutator_lock_);
+
   Class* GetRType() REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(MethodType, r_type_));
   }
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 31eb1cc..528eddc 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -273,6 +273,36 @@
   return true;
 }
 
+static bool FindDexFileMapItem(const uint8_t* dex_begin,
+                               const uint8_t* dex_end,
+                               DexFile::MapItemType map_item_type,
+                               const DexFile::MapItem** result_item) {
+  *result_item = nullptr;
+
+  const DexFile::Header* header =
+      BoundsCheckedCast<const DexFile::Header*>(dex_begin, dex_begin, dex_end);
+  if (nullptr == header) return false;
+
+  if (!DexFile::IsMagicValid(header->magic_)) return true;  // Not a dex file, not an error.
+
+  const DexFile::MapList* map_list =
+      BoundsCheckedCast<const DexFile::MapList*>(dex_begin + header->map_off_, dex_begin, dex_end);
+  if (nullptr == map_list) return false;
+
+  const DexFile::MapItem* map_item = map_list->list_;
+  size_t count = map_list->size_;
+  while (count--) {
+    if (map_item->type_ == static_cast<uint16_t>(map_item_type)) {
+      *result_item = map_item;
+      break;
+    }
+    map_item = BoundsCheckedCast<const DexFile::MapItem*>(map_item + 1, dex_begin, dex_end);
+    if (nullptr == map_item) return false;
+  }
+
+  return true;
+}
+
 bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) {
   if (!GetOatHeader().IsValid()) {
     std::string cause = GetOatHeader().GetValidationErrorMessage();
@@ -501,7 +531,19 @@
 
     uint8_t* current_dex_cache_arrays = nullptr;
     if (dex_cache_arrays != nullptr) {
-      DexCacheArraysLayout layout(pointer_size, *header);
+      // All DexCache types except for CallSite have their instance counts in the
+      // DexFile header. For CallSites, we need to read the info from the MapList.
+      const DexFile::MapItem* call_sites_item = nullptr;
+      if (!FindDexFileMapItem(DexBegin(),
+                              DexEnd(),
+                              DexFile::MapItemType::kDexTypeCallSiteIdItem,
+                              &call_sites_item)) {
+        *error_msg = StringPrintf("In oat file '%s' could not read data from truncated DexFile map",
+                                  GetLocation().c_str());
+        return false;
+      }
+      size_t num_call_sites = call_sites_item == nullptr ? 0 : call_sites_item->size_;
+      DexCacheArraysLayout layout(pointer_size, *header, num_call_sites);
       if (layout.Size() != 0u) {
         if (static_cast<size_t>(dex_cache_arrays_end - dex_cache_arrays) < layout.Size()) {
           *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with "
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 9609bee..f8f3d76 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -95,6 +95,7 @@
 #include "mirror/field.h"
 #include "mirror/method.h"
 #include "mirror/method_handle_impl.h"
+#include "mirror/method_handles_lookup.h"
 #include "mirror/method_type.h"
 #include "mirror/stack_trace_element.h"
 #include "mirror/throwable.h"
@@ -1715,6 +1716,7 @@
   mirror::Field::VisitRoots(visitor);
   mirror::MethodType::VisitRoots(visitor);
   mirror::MethodHandleImpl::VisitRoots(visitor);
+  mirror::MethodHandlesLookup::VisitRoots(visitor);
   mirror::EmulatedStackFrame::VisitRoots(visitor);
   mirror::ClassExt::VisitRoots(visitor);
   // Visit all the primitive array types classes.
diff --git a/runtime/utils.h b/runtime/utils.h
index 67438b5..96e5bfa 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -301,6 +301,30 @@
   }
 }
 
+// Returns a type cast pointer if object pointed to is within the provided bounds.
+// Otherwise returns nullptr.
+template <typename T>
+inline static T BoundsCheckedCast(const void* pointer,
+                                  const void* lower,
+                                  const void* upper) {
+  const uint8_t* bound_begin = static_cast<const uint8_t*>(lower);
+  const uint8_t* bound_end = static_cast<const uint8_t*>(upper);
+  DCHECK(bound_begin <= bound_end);
+
+  T result = reinterpret_cast<T>(pointer);
+  const uint8_t* begin = static_cast<const uint8_t*>(pointer);
+  const uint8_t* end = begin + sizeof(*result);
+  if (begin < bound_begin || end > bound_end || begin > end) {
+    return nullptr;
+  }
+  return result;
+}
+
+template <typename T, size_t size>
+constexpr size_t ArrayCount(const T (&)[size]) {
+  return size;
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_UTILS_H_
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
index bd1b044..9865821 100644
--- a/runtime/utils/dex_cache_arrays_layout-inl.h
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -29,7 +29,8 @@
 namespace art {
 
 inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size,
-                                                  const DexFile::Header& header)
+                                                  const DexFile::Header& header,
+                                                  uint32_t num_call_sites)
     : pointer_size_(pointer_size),
       /* types_offset_ is always 0u, so it's constexpr */
       methods_offset_(
@@ -40,12 +41,14 @@
           RoundUp(strings_offset_ + StringsSize(header.string_ids_size_), FieldsAlignment())),
       method_types_offset_(
           RoundUp(fields_offset_ + FieldsSize(header.field_ids_size_), MethodTypesAlignment())),
-      size_(
-          RoundUp(method_types_offset_ + MethodTypesSize(header.proto_ids_size_), Alignment())) {
+    call_sites_offset_(
+        RoundUp(method_types_offset_ + MethodTypesSize(header.proto_ids_size_),
+                MethodTypesAlignment())),
+      size_(RoundUp(call_sites_offset_ + CallSitesSize(num_call_sites), Alignment())) {
 }
 
 inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size, const DexFile* dex_file)
-    : DexCacheArraysLayout(pointer_size, dex_file->GetHeader()) {
+    : DexCacheArraysLayout(pointer_size, dex_file->GetHeader(), dex_file->NumCallSiteIds()) {
 }
 
 inline constexpr size_t DexCacheArraysLayout::Alignment() {
@@ -131,10 +134,18 @@
 
 inline size_t DexCacheArraysLayout::MethodTypesAlignment() const {
   static_assert(alignof(mirror::MethodTypeDexCacheType) == 8,
-                "alignof(MethodTypeDexCacheType) != 8");
+                "Expecting alignof(MethodTypeDexCacheType) == 8");
   return alignof(mirror::MethodTypeDexCacheType);
 }
 
+inline size_t DexCacheArraysLayout::CallSitesSize(size_t num_elements) const {
+  return ArraySize(GcRootAsPointerSize<mirror::CallSite>(), num_elements);
+}
+
+inline size_t DexCacheArraysLayout::CallSitesAlignment() const {
+  return alignof(GcRoot<mirror::CallSite>);
+}
+
 inline size_t DexCacheArraysLayout::ElementOffset(PointerSize element_size, uint32_t idx) {
   return static_cast<size_t>(element_size) * idx;
 }
diff --git a/runtime/utils/dex_cache_arrays_layout.h b/runtime/utils/dex_cache_arrays_layout.h
index 7d4b23a..ed677ed 100644
--- a/runtime/utils/dex_cache_arrays_layout.h
+++ b/runtime/utils/dex_cache_arrays_layout.h
@@ -37,11 +37,14 @@
         strings_offset_(0u),
         fields_offset_(0u),
         method_types_offset_(0u),
+        call_sites_offset_(0u),
         size_(0u) {
   }
 
   // Construct a layout for a particular dex file header.
-  DexCacheArraysLayout(PointerSize pointer_size, const DexFile::Header& header);
+  DexCacheArraysLayout(PointerSize pointer_size,
+                       const DexFile::Header& header,
+                       uint32_t num_call_sites);
 
   // Construct a layout for a particular dex file.
   DexCacheArraysLayout(PointerSize pointer_size, const DexFile* dex_file);
@@ -104,6 +107,14 @@
 
   size_t MethodTypesAlignment() const;
 
+  size_t CallSitesOffset() const {
+    return call_sites_offset_;
+  }
+
+  size_t CallSitesSize(size_t num_elements) const;
+
+  size_t CallSitesAlignment() const;
+
  private:
   static constexpr size_t types_offset_ = 0u;
   const PointerSize pointer_size_;  // Must be first for construction initialization order.
@@ -111,6 +122,7 @@
   const size_t strings_offset_;
   const size_t fields_offset_;
   const size_t method_types_offset_;
+  const size_t call_sites_offset_;
   const size_t size_;
 
   static size_t Alignment(PointerSize pointer_size);
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index 02f1e1b..634bd47 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -408,4 +408,23 @@
       IsValidDescriptor(reinterpret_cast<char*>(&unpaired_surrogate_with_multibyte_sequence[0])));
 }
 
+TEST_F(UtilsTest, ArrayCount) {
+  int i[64];
+  EXPECT_EQ(ArrayCount(i), 64u);
+  char c[7];
+  EXPECT_EQ(ArrayCount(c), 7u);
+}
+
+TEST_F(UtilsTest, BoundsCheckedCast) {
+  char buffer[64];
+  const char* buffer_end = buffer + ArrayCount(buffer);
+  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(nullptr, buffer, buffer_end), nullptr);
+  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer, buffer, buffer_end),
+            reinterpret_cast<const uint64_t*>(buffer));
+  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 56, buffer, buffer_end),
+            reinterpret_cast<const uint64_t*>(buffer + 56));
+  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer - 1, buffer, buffer_end), nullptr);
+  EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 57, buffer, buffer_end), nullptr);
+}
+
 }  // namespace art
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 5f55f3f..16739fa 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3114,6 +3114,44 @@
       just_set_result = true;
       break;
     }
+    case Instruction::INVOKE_CUSTOM:
+    case Instruction::INVOKE_CUSTOM_RANGE: {
+      // Verify registers based on method_type in the call site.
+      bool is_range = (inst->Opcode() == Instruction::INVOKE_CUSTOM_RANGE);
+
+      // Step 1. Check the call site that produces the method handle for invocation
+      const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+      if (!CheckCallSite(call_site_idx)) {
+        DCHECK(HasFailures());
+        break;
+      }
+
+      // Step 2. Check the register arguments correspond to the expected arguments for the
+      // method handle produced by step 1. The dex file verifier has checked ranges for
+      // the first three arguments and CheckCallSite has checked the method handle type.
+      CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx));
+      it.Next();  // Skip to name.
+      it.Next();  // Skip to method type of the method handle
+      const uint32_t proto_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+      const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(proto_idx);
+      DexFileParameterIterator param_it(*dex_file_, proto_id);
+      // Treat method as static as it has yet to be determined.
+      VerifyInvocationArgsFromIterator(&param_it, inst, METHOD_STATIC, is_range, nullptr);
+      const char* return_descriptor = dex_file_->GetReturnTypeDescriptor(proto_id);
+
+      // Step 3. Propagate return type information
+      const RegType& return_type =
+          reg_types_.FromDescriptor(GetClassLoader(), return_descriptor, false);
+      if (!return_type.IsLowHalf()) {
+        work_line_->SetResultRegisterType(this, return_type);
+      } else {
+        work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(&reg_types_));
+      }
+      just_set_result = true;
+      // TODO: Add compiler support for invoke-custom (b/35337872).
+      Fail(VERIFY_ERROR_FORCE_INTERPRETER);
+      break;
+    }
     case Instruction::NEG_INT:
     case Instruction::NOT_INT:
       work_line_->CheckUnaryOp(this, inst, reg_types_.Integer(), reg_types_.Integer());
@@ -3423,7 +3461,7 @@
     /* These should never appear during verification. */
     case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
     case Instruction::UNUSED_F3 ... Instruction::UNUSED_F9:
-    case Instruction::UNUSED_FC ... Instruction::UNUSED_FF:
+    case Instruction::UNUSED_FE ... Instruction::UNUSED_FF:
     case Instruction::UNUSED_79:
     case Instruction::UNUSED_7A:
       Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_);
@@ -4094,6 +4132,116 @@
   VerifyInvocationArgsFromIterator(&it, inst, method_type, is_range, nullptr);
 }
 
+bool MethodVerifier::CheckCallSite(uint32_t call_site_idx) {
+  CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx));
+  // Check essential arguments are provided. The dex file verifier has verified indicies of the
+  // main values (method handle, name, method_type).
+  if (it.Size() < 3) {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+                                      << " has too few arguments: "
+                                      << it.Size() << "< 3";
+    return false;
+  }
+
+  // Get and check the first argument: the method handle.
+  uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+  it.Next();
+  const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(method_handle_idx);
+  if (mh.method_handle_type_ != static_cast<uint16_t>(DexFile::MethodHandleType::kInvokeStatic)) {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+                                      << " argument 0 method handle type is not InvokeStatic";
+    return false;
+  }
+
+  // Skip the second argument, the name to resolve, as checked by the
+  // dex file verifier.
+  it.Next();
+
+  // Skip the third argument, the method type expected, as checked by
+  // the dex file verifier.
+  it.Next();
+
+  // Check the bootstrap method handle and remaining arguments.
+  const DexFile::MethodId& method_id = dex_file_->GetMethodId(mh.field_or_method_idx_);
+  uint32_t length;
+  const char* shorty = dex_file_->GetMethodShorty(method_id, &length);
+
+  if (it.Size() < length - 1) {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+                                      << " too few arguments for bootstrap method: "
+                                      << it.Size() << " < " << (length - 1);
+    return false;
+  }
+
+  // Check the return type and first 3 arguments are references
+  // (CallSite, Lookup, String, MethodType). If they are not of the
+  // expected types (or subtypes), it will trigger a
+  // WrongMethodTypeException during execution.
+  if (shorty[0] != 'L') {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+                                      << " bootstrap return type is not a reference";
+    return false;
+  }
+
+  for (uint32_t i = 1; i < 4; ++i) {
+    if (shorty[i] != 'L') {
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+                                        << " bootstrap method argument " << (i - 1)
+                                        << " is not a reference";
+      return false;
+    }
+  }
+
+  // Check the optional arguments.
+  for (uint32_t i = 4; i < length; ++i, it.Next()) {
+    bool match = false;
+    switch (it.GetValueType()) {
+      case EncodedArrayValueIterator::ValueType::kBoolean:
+      case EncodedArrayValueIterator::ValueType::kByte:
+      case EncodedArrayValueIterator::ValueType::kShort:
+      case EncodedArrayValueIterator::ValueType::kChar:
+      case EncodedArrayValueIterator::ValueType::kInt:
+        // These all fit within one register and encoders do not seem
+        // too exacting on the encoding type they use (ie using
+        // integer for all of these).
+        match = (strchr("ZBCSI", shorty[i]) != nullptr);
+        break;
+      case EncodedArrayValueIterator::ValueType::kLong:
+        match = ('J' == shorty[i]);
+        break;
+      case EncodedArrayValueIterator::ValueType::kFloat:
+        match = ('F' == shorty[i]);
+        break;
+      case EncodedArrayValueIterator::ValueType::kDouble:
+        match = ('D' == shorty[i]);
+        break;
+      case EncodedArrayValueIterator::ValueType::kMethodType:
+      case EncodedArrayValueIterator::ValueType::kMethodHandle:
+      case EncodedArrayValueIterator::ValueType::kString:
+      case EncodedArrayValueIterator::ValueType::kType:
+      case EncodedArrayValueIterator::ValueType::kNull:
+        match = ('L' == shorty[i]);
+        break;
+      case EncodedArrayValueIterator::ValueType::kField:
+      case EncodedArrayValueIterator::ValueType::kMethod:
+      case EncodedArrayValueIterator::ValueType::kEnum:
+      case EncodedArrayValueIterator::ValueType::kArray:
+      case EncodedArrayValueIterator::ValueType::kAnnotation:
+        // Unreachable based on current EncodedArrayValueIterator::Next().
+        UNREACHABLE();
+    }
+
+    if (!match) {
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+                                        << " bootstrap method argument " << (i - 1)
+                                        << " expected " << shorty[i]
+                                        << " got value type: " << it.GetValueType();
+      return false;
+    }
+  }
+  return true;
+}
+
 class MethodParamListDescriptorIterator {
  public:
   explicit MethodParamListDescriptorIterator(ArtMethod* res_method) :
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index fa5a698..7b67967 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -697,6 +697,11 @@
   REQUIRES_SHARED(Locks::mutator_lock_);
 
   /*
+   * Verify the arguments present for a call site. Returns "true" if all is well, "false" otherwise.
+   */
+  bool CheckCallSite(uint32_t call_site_idx);
+
+  /*
    * Verify that the target instruction is not "move-exception". It's important that the only way
    * to execute a move-exception is as the first instruction of an exception handler.
    * Returns "true" if all is well, "false" if the target instruction is move-exception.
diff --git a/test/952-invoke-custom/build b/test/952-invoke-custom/build
new file mode 100644
index 0000000..a423ca6
--- /dev/null
+++ b/test/952-invoke-custom/build
@@ -0,0 +1,25 @@
+#!/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.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ != *"--jvm"* ]]; then
+  # Don't do anything with jvm.
+  export USE_JACK=true
+fi
+
+./default-build "$@" --experimental method-handles
diff --git a/test/952-invoke-custom/expected.txt b/test/952-invoke-custom/expected.txt
new file mode 100644
index 0000000..bb87296
--- /dev/null
+++ b/test/952-invoke-custom/expected.txt
@@ -0,0 +1,14 @@
+Caught exception from uninitialized call site
+Caught exception from uninitialized call site
+linkerMethod failure type 1
+Returning null instead of CallSite for add (int,int)int
+linkerMethod failure type 2
+Throwing InstantiationException in linkerMethod()
+linkerMethod failure type 3
+Throwing ArithmeticException in add()
+Failure Type + 0 (1013)
+Linking add (int,int)int
+100
+-9000
+9000
+Winners 1 Votes 16
diff --git a/test/952-invoke-custom/generator/TestInvokeCustomWithConcurrentThreads.java b/test/952-invoke-custom/generator/TestInvokeCustomWithConcurrentThreads.java
new file mode 100644
index 0000000..5d5cae4
--- /dev/null
+++ b/test/952-invoke-custom/generator/TestInvokeCustomWithConcurrentThreads.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.android.jack.annotations.CalledByInvokeCustom;
+import com.android.jack.annotations.Constant;
+import com.android.jack.annotations.LinkerMethodHandle;
+import com.android.jack.annotations.MethodHandleKind;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+import java.lang.Thread;
+import java.lang.ThreadLocal;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class TestInvokeCustomWithConcurrentThreads extends Thread {
+  private static final int NUMBER_OF_THREADS = 16;
+
+  private static final AtomicInteger nextIndex = new AtomicInteger(0);
+
+  private static final ThreadLocal<Integer> threadIndex =
+      new ThreadLocal<Integer>() {
+        @Override
+        protected Integer initialValue() {
+          return nextIndex.getAndIncrement();
+        }
+      };
+
+  // Array of call sites instantiated, one per thread
+  private static CallSite[] instantiated = new CallSite[NUMBER_OF_THREADS];
+
+  // Array of counters for how many times each instantiated call site is called
+  private static AtomicInteger[] called = new AtomicInteger[NUMBER_OF_THREADS];
+
+  // Array of call site indicies of which call site a thread invoked
+  private static AtomicInteger[] targetted = new AtomicInteger[NUMBER_OF_THREADS];
+
+  private TestInvokeCustomWithConcurrentThreads() {}
+
+  private static int getThreadIndex() {
+    return threadIndex.get().intValue();
+  }
+
+  public static int notUsed(int x) {
+    return x;
+  }
+
+  @Override
+  public void run() {
+    int x = setCalled(-1 /* argument dropped */);
+    notUsed(x);
+  }
+
+  @CalledByInvokeCustom(
+      invokeMethodHandle = @LinkerMethodHandle(kind = MethodHandleKind.INVOKE_STATIC,
+          enclosingType = TestInvokeCustomWithConcurrentThreads.class,
+          name = "linkerMethod",
+          argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class}),
+      name = "setCalled",
+      returnType = int.class,
+      argumentTypes = {int.class})
+  private static int setCalled(int index) {
+    called[index].getAndIncrement();
+    targetted[getThreadIndex()].set(index);
+    return 0;
+  }
+
+  @SuppressWarnings("unused")
+  private static CallSite linkerMethod(MethodHandles.Lookup caller,
+                                       String name,
+                                       MethodType methodType) throws Throwable {
+    int threadIndex = getThreadIndex();
+    MethodHandle mh =
+        caller.findStatic(TestInvokeCustomWithConcurrentThreads.class, name, methodType);
+    assertEquals(methodType, mh.type());
+    assertEquals(mh.type().parameterCount(), 1);
+    mh = MethodHandles.insertArguments(mh, 0, threadIndex);
+    mh = MethodHandles.dropArguments(mh, 0, int.class);
+    assertEquals(mh.type().parameterCount(), 1);
+    assertEquals(methodType, mh.type());
+
+    // Sleep to try to get concurrent executions of this
+    // method. Multiple call sites should be created, but only one
+    // invoked.
+    Thread.sleep(125);
+
+    instantiated[getThreadIndex()] = new ConstantCallSite(mh);
+    return instantiated[getThreadIndex()];
+  }
+
+  public static void test() throws Throwable {
+    // Initialize counters for which call site gets invoked
+    for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+      called[i] = new AtomicInteger(0);
+      targetted[i] = new AtomicInteger(0);
+    }
+
+    // Run threads that each invoke-custom the call site
+    Thread [] threads = new Thread[NUMBER_OF_THREADS];
+    for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+      threads[i] = new TestInvokeCustomWithConcurrentThreads();
+      threads[i].start();
+    }
+
+    // Wait for all threads to complete
+    for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+      threads[i].join();
+    }
+
+    // Check one call site instance won
+    int winners = 0;
+    int votes = 0;
+    for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+      assertNotEquals(instantiated[i], null);
+      if (called[i].get() != 0) {
+        winners++;
+        votes += called[i].get();
+      }
+    }
+
+    System.out.println("Winners " + winners + " Votes " + votes);
+
+    // We assert this below but output details when there's an error as
+    // it's non-deterministic.
+    if (winners != 1) {
+      System.out.println("Threads did not the same call-sites:");
+      for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+        System.out.format(" Thread % 2d invoked call site instance #%02d\n",
+                          i, targetted[i].get());
+      }
+    }
+
+    // We assert this below but output details when there's an error as
+    // it's non-deterministic.
+    if (votes != NUMBER_OF_THREADS) {
+      System.out.println("Call-sites invocations :");
+      for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+        System.out.format(" Call site instance #%02d was invoked % 2d times\n",
+                          i, called[i].get());
+      }
+    }
+
+    assertEquals(winners, 1);
+    assertEquals(votes, NUMBER_OF_THREADS);
+  }
+
+  public static void assertTrue(boolean value) {
+    if (!value) {
+      throw new AssertionError("assertTrue value: " + value);
+    }
+  }
+
+  public static void assertEquals(byte b1, byte b2) {
+    if (b1 == b2) { return; }
+    throw new AssertionError("assertEquals b1: " + b1 + ", b2: " + b2);
+  }
+
+  public static void assertEquals(char c1, char c2) {
+    if (c1 == c2) { return; }
+    throw new AssertionError("assertEquals c1: " + c1 + ", c2: " + c2);
+  }
+
+  public static void assertEquals(short s1, short s2) {
+    if (s1 == s2) { return; }
+    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+  }
+
+  public static void assertEquals(int i1, int i2) {
+    if (i1 == i2) { return; }
+    throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
+  }
+
+  public static void assertEquals(long l1, long l2) {
+    if (l1 == l2) { return; }
+    throw new AssertionError("assertEquals l1: " + l1 + ", l2: " + l2);
+  }
+
+  public static void assertEquals(float f1, float f2) {
+    if (f1 == f2) { return; }
+    throw new AssertionError("assertEquals f1: " + f1 + ", f2: " + f2);
+  }
+
+  public static void assertEquals(double d1, double d2) {
+    if (d1 == d2) { return; }
+    throw new AssertionError("assertEquals d1: " + d1 + ", d2: " + d2);
+  }
+
+  public static void assertEquals(Object o, Object p) {
+    if (o == p) { return; }
+    if (o != null && p != null && o.equals(p)) { return; }
+    throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+  }
+
+  public static void assertNotEquals(Object o, Object p) {
+    if (o != p) { return; }
+    if (o != null && p != null && !o.equals(p)) { return; }
+    throw new AssertionError("assertNotEquals: o1: " + o + ", o2: " + p);
+  }
+
+  public static void assertEquals(String s1, String s2) {
+    if (s1 == s2) {
+      return;
+    }
+
+    if (s1 != null && s2 != null && s1.equals(s2)) {
+      return;
+    }
+
+    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+  }
+}
diff --git a/test/952-invoke-custom/generator/TestLinkerMethodMinimalArguments.java b/test/952-invoke-custom/generator/TestLinkerMethodMinimalArguments.java
new file mode 100644
index 0000000..93d96a9
--- /dev/null
+++ b/test/952-invoke-custom/generator/TestLinkerMethodMinimalArguments.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.android.jack.annotations.CalledByInvokeCustom;
+import com.android.jack.annotations.Constant;
+import com.android.jack.annotations.LinkerMethodHandle;
+import com.android.jack.annotations.MethodHandleKind;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class TestLinkerMethodMinimalArguments {
+
+  private static int forceFailureType = 0;
+
+  private static int FAILURE_TYPE_NONE = 0;
+  private static int FAILURE_TYPE_LINKER_METHOD_RETURNS_NULL = 1;
+  private static int FAILURE_TYPE_LINKER_METHOD_THROWS = 2;
+  private static int FAILURE_TYPE_TARGET_METHOD_THROWS = 3;
+
+  @CalledByInvokeCustom(
+      invokeMethodHandle = @LinkerMethodHandle(
+          kind = MethodHandleKind.INVOKE_STATIC,
+          enclosingType = TestLinkerMethodMinimalArguments.class,
+          argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class},
+          name = "linkerMethod"),
+      name = "add",
+      returnType = int.class,
+      argumentTypes = {int.class, int.class})
+  private static int add(int a, int b) {
+    if (forceFailureType == FAILURE_TYPE_TARGET_METHOD_THROWS) {
+      System.out.println("Throwing ArithmeticException in add()");
+      throw new ArithmeticException("add");
+    }
+    return a + b;
+  }
+
+  @SuppressWarnings("unused")
+  private static CallSite linkerMethod(MethodHandles.Lookup caller, String name,
+                                       MethodType methodType) throws Throwable {
+    System.out.println("linkerMethod failure type " + forceFailureType);
+    MethodHandle mh_add =
+        caller.findStatic(TestLinkerMethodMinimalArguments.class, name, methodType);
+    if (forceFailureType == FAILURE_TYPE_LINKER_METHOD_RETURNS_NULL) {
+      System.out.println("Returning null instead of CallSite for " + name + " " + methodType);
+      return null;
+    } else if (forceFailureType == FAILURE_TYPE_LINKER_METHOD_THROWS) {
+      System.out.println("Throwing InstantiationException in linkerMethod()");
+      throw new InstantiationException("linkerMethod");
+    } else {
+      return new ConstantCallSite(mh_add);
+    }
+  }
+
+  public static void test(int failureType, int x, int y) throws Throwable {
+    assertTrue(failureType >= FAILURE_TYPE_NONE);
+    assertTrue(failureType <= FAILURE_TYPE_TARGET_METHOD_THROWS);
+    forceFailureType = failureType;
+    assertEquals(x + y, add(x, y));
+    System.out.println("Failure Type + " + failureType + " (" + x + y+ ")");
+  }
+
+  public static void assertTrue(boolean value) {
+    if (!value) {
+      throw new AssertionError("assertTrue value: " + value);
+    }
+  }
+
+  public static void assertEquals(byte b1, byte b2) {
+    if (b1 == b2) { return; }
+    throw new AssertionError("assertEquals b1: " + b1 + ", b2: " + b2);
+  }
+
+  public static void assertEquals(char c1, char c2) {
+    if (c1 == c2) { return; }
+    throw new AssertionError("assertEquals c1: " + c1 + ", c2: " + c2);
+  }
+
+  public static void assertEquals(short s1, short s2) {
+    if (s1 == s2) { return; }
+    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+  }
+
+  public static void assertEquals(int i1, int i2) {
+    if (i1 == i2) { return; }
+    throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
+  }
+
+  public static void assertEquals(long l1, long l2) {
+    if (l1 == l2) { return; }
+    throw new AssertionError("assertEquals l1: " + l1 + ", l2: " + l2);
+  }
+
+  public static void assertEquals(float f1, float f2) {
+    if (f1 == f2) { return; }
+    throw new AssertionError("assertEquals f1: " + f1 + ", f2: " + f2);
+  }
+
+  public static void assertEquals(double d1, double d2) {
+    if (d1 == d2) { return; }
+    throw new AssertionError("assertEquals d1: " + d1 + ", d2: " + d2);
+  }
+
+  public static void assertEquals(Object o, Object p) {
+    if (o == p) { return; }
+    if (o != null && p != null && o.equals(p)) { return; }
+    throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+  }
+
+  public static void assertEquals(String s1, String s2) {
+    if (s1 == s2) {
+      return;
+    }
+
+    if (s1 != null && s2 != null && s1.equals(s2)) {
+      return;
+    }
+
+    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+  }
+}
diff --git a/test/952-invoke-custom/generator/TestLinkerMethodMultipleArgumentTypes.java b/test/952-invoke-custom/generator/TestLinkerMethodMultipleArgumentTypes.java
new file mode 100644
index 0000000..4e4d97e
--- /dev/null
+++ b/test/952-invoke-custom/generator/TestLinkerMethodMultipleArgumentTypes.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.android.jack.annotations.CalledByInvokeCustom;
+import com.android.jack.annotations.Constant;
+import com.android.jack.annotations.LinkerMethodHandle;
+import com.android.jack.annotations.MethodHandleKind;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class TestLinkerMethodMultipleArgumentTypes {
+
+  private static int bootstrapRunCount = 0;
+
+  @CalledByInvokeCustom(
+      invokeMethodHandle = @LinkerMethodHandle(kind = MethodHandleKind.INVOKE_STATIC,
+          enclosingType = TestLinkerMethodMultipleArgumentTypes.class,
+          name = "linkerMethod",
+          argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class,
+                           boolean.class, byte.class, char.class, short.class, int.class,
+                           float.class, double.class, String.class, Class.class, long.class}),
+      methodHandleExtraArgs = {@Constant(booleanValue = true), @Constant(byteValue = 1),
+                         @Constant(charValue = 'a'), @Constant(shortValue = 1024),
+                         @Constant(intValue = 1), @Constant(floatValue = 11.1f),
+                         @Constant(doubleValue = 2.2), @Constant(stringValue = "Hello"),
+                         @Constant(classValue = TestLinkerMethodMultipleArgumentTypes.class),
+                         @Constant(longValue = 123456789L)},
+      name = "add",
+      returnType = int.class,
+      argumentTypes = {int.class, int.class})
+  private static int add(int a, int b) {
+    return a + b;
+  }
+
+  @SuppressWarnings("unused")
+  private static CallSite linkerMethod(MethodHandles.Lookup caller, String name,
+                                       MethodType methodType, boolean v1, byte v2, char v3,
+                                       short v4, int v5, float v6, double v7,
+                                       String v8, Class<?> v9, long v10) throws Throwable {
+    System.out.println("Linking " + name + " " + methodType);
+    assertTrue(v1);
+    assertEquals(1, v2);
+    assertEquals('a', v3);
+    assertEquals(1024, v4);
+    assertEquals(1, v5);
+    assertEquals(11.1f, v6);
+    assertEquals(2.2, v7);
+    assertEquals("Hello", v8);
+    assertEquals(TestLinkerMethodMultipleArgumentTypes.class, v9);
+    assertEquals(123456789L, v10);
+    MethodHandle mh_add =
+        caller.findStatic(TestLinkerMethodMultipleArgumentTypes.class, name, methodType);
+    return new ConstantCallSite(mh_add);
+  }
+
+  public int GetBootstrapRunCount() {
+    return bootstrapRunCount;
+  }
+
+  public static void test(int x, int y) throws Throwable {
+    assertEquals(x + y, add(x, y));
+    System.out.println(x + y);
+  }
+
+  public static void assertTrue(boolean value) {
+    if (!value) {
+      throw new AssertionError("assertTrue value: " + value);
+    }
+  }
+
+  public static void assertEquals(byte b1, byte b2) {
+    if (b1 == b2) { return; }
+    throw new AssertionError("assertEquals b1: " + b1 + ", b2: " + b2);
+  }
+
+  public static void assertEquals(char c1, char c2) {
+    if (c1 == c2) { return; }
+    throw new AssertionError("assertEquals c1: " + c1 + ", c2: " + c2);
+  }
+
+  public static void assertEquals(short s1, short s2) {
+    if (s1 == s2) { return; }
+    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+  }
+
+  public static void assertEquals(int i1, int i2) {
+    if (i1 == i2) { return; }
+    throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
+  }
+
+  public static void assertEquals(long l1, long l2) {
+    if (l1 == l2) { return; }
+    throw new AssertionError("assertEquals l1: " + l1 + ", l2: " + l2);
+  }
+
+  public static void assertEquals(float f1, float f2) {
+    if (f1 == f2) { return; }
+    throw new AssertionError("assertEquals f1: " + f1 + ", f2: " + f2);
+  }
+
+  public static void assertEquals(double d1, double d2) {
+    if (d1 == d2) { return; }
+    throw new AssertionError("assertEquals d1: " + d1 + ", d2: " + d2);
+  }
+
+  public static void assertEquals(Object o, Object p) {
+    if (o == p) { return; }
+    if (o != null && p != null && o.equals(p)) { return; }
+    throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+  }
+
+  public static void assertEquals(String s1, String s2) {
+    if (s1 == s2) {
+      return;
+    }
+
+    if (s1 != null && s2 != null && s1.equals(s2)) {
+      return;
+    }
+
+    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+  }
+}
diff --git a/test/952-invoke-custom/generator/build-test.sh b/test/952-invoke-custom/generator/build-test.sh
new file mode 100755
index 0000000..90a60e6
--- /dev/null
+++ b/test/952-invoke-custom/generator/build-test.sh
@@ -0,0 +1,77 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+args="$@"
+while [ -h "${prog}" ]; do
+  newProg=`/bin/ls -ld "${prog}"`
+  newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+  if expr "x${newProg}" : 'x/' >/dev/null; then
+      prog="${newProg}"
+  else
+    progdir=`dirname "${prog}"`
+    prog="${progdir}/${newProg}"
+  fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+test_dir="test-$$"
+if [ -z "$TMPDIR" ]; then
+  tmp_dir="/tmp/$USER/${test_dir}"
+else
+  tmp_dir="${TMPDIR}/${test_dir}"
+fi
+
+# This is a custom drop that necessitates this complexity.
+JACK_ANNOTATIONS_LIB=$HOME/Downloads/jack-test-annotations-lib.jack
+
+# Compile test into a base64 string that can be instantiated via
+# reflection on hosts without the jack-test-annotations-lib.jack file.
+mkdir $tmp_dir
+for input_file in $progdir/*.java; do
+  i=${input_file##*/Test}
+  i=${i%%.java}
+  src_file=$progdir/Test$i.java
+  jack_file=./src.jack
+  dex_file=./classes.dex
+  base_64_file=$tmp_dir/TestData$i.base64
+  output_file=$progdir/../src/TestData$i.java
+  # Compile source file to jack file.
+  jack -g -cp $ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:$ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack:$JACK_ANNOTATIONS_LIB -D sched.runner=multi-threaded -D sched.runner.thread.kind=fixed -D sched.runner.thread.fixed.count=4 -D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b2 --output-jack $jack_file $src_file
+  # Compile jack file to classes.dex.
+  jack -g -cp $ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:$ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack -D sched.runner=multi-threaded -D sched.runner.thread.kind=fixed -D sched.runner.thread.fixed.count=4 -D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b2 --import $jack_file --output-dex .
+  # Pack the classes.dex file into a base64 string.
+  base64 -w 72 $dex_file > $base_64_file
+  # Emit a managed source file containing the base64 string. The test can be
+  # run by loading this string as a dex file and invoking it via reflection.
+cat > $output_file <<HEADER
+/* Generated by ${prog##*/} from ${src_file##*/} */
+public class TestData$i {
+  public static final String BASE64_DEX_FILE =
+HEADER
+sed -e 's/^\(.*\)$/    "\1" +/' -e '$s/ +/;/' $base_64_file >> $output_file
+cat >> $output_file <<FOOTER
+}
+FOOTER
+  rm $dex_file $jack_file
+done
+
+rm -rf $tmp_dir
diff --git a/test/952-invoke-custom/info.txt b/test/952-invoke-custom/info.txt
new file mode 100644
index 0000000..e9a9f6c
--- /dev/null
+++ b/test/952-invoke-custom/info.txt
@@ -0,0 +1,7 @@
+A test that is only available as a DEX binary.
+
+This tests execution of invoke-custom. There is no bytecode to emit
+invoke-custom directly. This test is generated using jack-test-annotations-lib.jack
+which is not a publicly supported at this time.
+
+The tests and a script to generate data from them are in the generator/ directory.
diff --git a/test/952-invoke-custom/src/Main.java b/test/952-invoke-custom/src/Main.java
new file mode 100644
index 0000000..2abc312
--- /dev/null
+++ b/test/952-invoke-custom/src/Main.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import dalvik.system.InMemoryDexClassLoader;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MutableCallSite;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.Base64;
+
+// This test is a stop-gap until we have support for generating invoke-custom
+// in the Android tree.
+
+public class Main {
+
+  private static void TestUninitializedCallSite() throws Throwable {
+    CallSite callSite = new MutableCallSite(MethodType.methodType(int.class));
+    try {
+      callSite.getTarget().invoke();
+      fail();
+    } catch (IllegalStateException e) {
+      System.out.println("Caught exception from uninitialized call site");
+    }
+
+    callSite = new MutableCallSite(MethodType.methodType(String.class, int.class, char.class));
+    try {
+      callSite.getTarget().invoke(1535, 'd');
+      fail();
+    } catch (IllegalStateException e) {
+      System.out.println("Caught exception from uninitialized call site");
+    }
+  }
+
+  private static void TestLinkerMethodMultipleArgumentTypes() throws Throwable {
+    // This is a more comprehensive test of invoke-custom, the linker
+    // method takes additional arguments of types boolean, byte, char,
+    // short, int, float, double, String, Class, and long (in this order)
+    // The test asserts the values passed to the linker method match their
+    // expected values.
+    byte[] base64Data = TestDataLinkerMethodMultipleArgumentTypes.BASE64_DEX_FILE.getBytes();
+    Base64.Decoder decoder = Base64.getDecoder();
+    ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data));
+
+    InMemoryDexClassLoader classLoader =
+        new InMemoryDexClassLoader(dexBuffer,
+                                   ClassLoader.getSystemClassLoader());
+    Class<?> testClass =
+        classLoader.loadClass("TestLinkerMethodMultipleArgumentTypes");
+    Method testMethod = testClass.getDeclaredMethod("test", int.class, int.class);
+    // First invocation should link via the bootstrap method (outputs "Linking add" ...).
+    testMethod.invoke(null, 33, 67);
+    // Subsequent invocations use the cached value of the CallSite and do not require linking.
+    testMethod.invoke(null, -10000, +1000);
+    testMethod.invoke(null, -1000, +10000);
+  }
+
+  private static void TestLinkerMethodMinimalArguments() throws Throwable {
+    // This test checks various failures when running the linker
+    // method and during invocation of the method handle.
+    byte[] base64Data = TestDataLinkerMethodMinimalArguments.BASE64_DEX_FILE.getBytes();
+    Base64.Decoder decoder = Base64.getDecoder();
+    ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data));
+
+    InMemoryDexClassLoader classLoader =
+        new InMemoryDexClassLoader(dexBuffer,
+                                   ClassLoader.getSystemClassLoader());
+    Class<?> testClass =
+        classLoader.loadClass("TestLinkerMethodMinimalArguments");
+    Method testMethod = testClass.getDeclaredMethod("test", int.class, int.class, int.class);
+
+    try {
+      testMethod.invoke(null, 1 /* linker method return null */, 10, 10);
+    } catch (InvocationTargetException e) {
+      assertEquals(e.getCause().getClass().getName(), "java.lang.BootstrapMethodError");
+      assertEquals(
+          e.getCause().getCause().getClass().getName(), "java.lang.NullPointerException");
+    }
+
+    try {
+      testMethod.invoke(null, 2 /* linker method throw InstantiationException */, 10, 11);
+    } catch (InvocationTargetException e) {
+      assertEquals(e.getCause().getClass().getName(), "java.lang.BootstrapMethodError");
+      assertEquals(
+          e.getCause().getCause().getClass().getName(), "java.lang.InstantiationException");
+    }
+    try {
+      // Creating the CallSite works here, but fail invoking the method.
+      testMethod.invoke(null, 3 /* target throw NPE */, 10, 12);
+    } catch (InvocationTargetException e) {
+      assertEquals(e.getCause().getClass().getName(), "java.lang.ArithmeticException");
+    }
+
+    // This should succeed using already resolved CallSite.
+    testMethod.invoke(null, 0 /* no error */, 10, 13);
+  }
+
+  private static void TestInvokeCustomWithConcurrentThreads() throws Throwable {
+    // This is a concurrency test that attempts to run invoke-custom on the same
+    // call site.
+    byte[] base64Data = TestDataInvokeCustomWithConcurrentThreads.BASE64_DEX_FILE.getBytes();
+    Base64.Decoder decoder = Base64.getDecoder();
+    ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data));
+
+    InMemoryDexClassLoader classLoader =
+        new InMemoryDexClassLoader(dexBuffer,
+                                   ClassLoader.getSystemClassLoader());
+    Class<?> testClass =
+        classLoader.loadClass("TestInvokeCustomWithConcurrentThreads");
+    Method testMethod = testClass.getDeclaredMethod("test");
+    testMethod.invoke(null);
+  }
+
+  public static void assertEquals(Object o, Object p) {
+    if (o == p) { return; }
+    if (o != null && p != null && o.equals(p)) { return; }
+    throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+  }
+
+  public static void assertEquals(String s1, String s2) {
+    if (s1 == s2) {
+      return;
+    }
+
+    if (s1 != null && s2 != null && s1.equals(s2)) {
+      return;
+    }
+
+    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+  }
+
+  private static void fail() {
+    System.out.println("fail");
+    Thread.dumpStack();
+  }
+
+  public static void main(String[] args) throws Throwable {
+    TestUninitializedCallSite();
+    TestLinkerMethodMinimalArguments();
+    TestLinkerMethodMultipleArgumentTypes();
+    TestInvokeCustomWithConcurrentThreads();
+  }
+}
\ No newline at end of file
diff --git a/test/952-invoke-custom/src/TestDataInvokeCustomWithConcurrentThreads.java b/test/952-invoke-custom/src/TestDataInvokeCustomWithConcurrentThreads.java
new file mode 100644
index 0000000..9a77e67
--- /dev/null
+++ b/test/952-invoke-custom/src/TestDataInvokeCustomWithConcurrentThreads.java
@@ -0,0 +1,145 @@
+/* Generated by build-test.sh from TestInvokeCustomWithConcurrentThreads.java */
+public class TestDataInvokeCustomWithConcurrentThreads {
+  public static final String BASE64_DEX_FILE =
+    "ZGV4CjAzOADq+VhgIFlZEzwomlDWIN5pEqcVmb8SN2y8HQAAcAAAAHhWNBIAAAAAAAAAANQc" +
+    "AACqAAAAcAAAACoAAAAYAwAAJgAAAMADAAAJAAAAiAUAADgAAADQBQAAAgAAAJQHAADgFQAA" +
+    "3AcAAKQRAADXEQAABxIAABASAAAYEgAAIBIAACgSAAAwEgAAOBIAAEASAABIEgAAUBIAAFcS" +
+    "AABaEgAAZBIAAGwSAABwEgAAcxIAAHYSAACQEgAAkxIAAJYSAACZEgAAnRIAAKwSAACvEgAA" +
+    "shIAALYSAAC6EgAAvhIAAMISAADGEgAAyhIAANASAADVEgAA2xIAAAYTAAAvEwAAMxMAAGgT" +
+    "AACbEwAAzBMAAPATAAAQFAAAMxQAAFIUAABuFAAAhRQAAKEUAAC0FAAAyRQAAN0UAADxFAAA" +
+    "DBUAACAVAAA0FQAATBUAAGUVAAB8FQAAmRUAAL4VAADfFQAACBYAACoWAABJFgAAdhYAAIkW" +
+    "AACMFgAAkhYAAL4WAADkFgAA5xYAAOwWAADxFgAA9hYAAPsWAAD/FgAABBcAAAgXAAANFwAA" +
+    "ERcAABYXAAAbFwAAHxcAACkXAAAsFwAAMBcAAEQXAABZFwAAbhcAAIwXAAC6FwAAxxcAAM8X" +
+    "AADeFwAA7BcAAP8XAAASGAAAJRgAADgYAABLGAAAXhgAAHEYAACFGAAAlhgAAK0YAAC5GAAA" +
+    "zRgAANEYAADVGAAA2RgAAN0YAADlGAAA7RgAAPEYAAD1GAAABBkAABgZAAAnGQAALxkAADMZ" +
+    "AAA3GQAAQxkAAEsZAABQGQAAYRkAAHEZAAB0GQAAeBkAAHwZAACDGQAAkRkAAKIZAACwGQAA" +
+    "uhkAAM4ZAADUGQAA2hkAAN4ZAADiGQAA8BkAAPwZAAAAGgAABhoAABEaAAAaGgAAHRoAACIa" +
+    "AAAlGgAANRoAAD4aAABKGgAATxoAAFMaAABXGgAAXBoAAGcaAABuGgAAdRoAAIAaAACGGgAA" +
+    "jBoAAJkaAACiGgAArBoAALIaAAC5GgAAwhoAAMkaAADSGgAAEAAAABEAAAATAAAAFAAAABUA" +
+    "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" +
+    "AQAAACO2JgBxEBsAAAAMB00HBgliBwQARgcHAG4QNQAHAAoHcRAbAAcADAdNBwYKbjAXAFQG" +
+    "2AAAASjeMoIuAGIECAAbBRIAAABuIBgAVAASADWAIwBiBAgAGwUAAAAAI7YmAHEQGwAAAAwH" +
+    "TQcGCWIHAQBGBwcAbhA1AAcACgdxEBsABwAMB00HBgpuMBcAVAbYAAABKN5xIAoAowBxIAoA" +
+    "ggAOAAMAAQABAAAA6BsAAAkAAAAS8fwQAAABAAoAcRATAAAADgAAANwHAAAAAAAAAAAAAAAA" +
+    "AADsBwAAAQAAAAMAAAAAAAAABQAAAPQHAAASAAAA/AcAABUAAAAECAAAFgAAAPwHAAABAAAA" +
+    "BAAAAAIAAAAVACYAAQAAAAEAAAABAAAAAgAAAAEAAAADAAAAAQAAAAUAAAABAAAAFAAAAAEA" +
+    "AAAVAAAAAQAAACQAAAADAAAAHgAVACAAAAADAAAAEgAVACAAAAADAAAAHQAEACUAAAADAAAA" +
+    "HQAEACYAAAACAAAAAAAAAAIAAAABAAEAAgAAAAIAAgACAAAAAwADAAIAAAAEAAQAAgAAAAUA" +
+    "BQACAAAAFAAUAAIAAAAVABUAAQAAAB0AAAACAAAAIgAiADEgQ2FsbCBzaXRlIGluc3RhbmNl" +
+    "ICMlMDJkIHdhcyBpbnZva2VkICUgMmQgdGltZXMKAC4gVGhyZWFkICUgMmQgaW52b2tlZCBj" +
+    "YWxsIHNpdGUgaW5zdGFuY2UgIyUwMmQKAAcgVm90ZXMgAAYsIGIyOiAABiwgYzI6IAAGLCBk" +
+    "MjogAAYsIGYyOiAABiwgaTI6IAAGLCBsMjogAAYsIG8yOiAABiwgczI6IAAFLWdldDAAATwA" +
+    "CDxjbGluaXQ+AAY8aW5pdD4AAj47AAFCAAFDABhDYWxsLXNpdGVzIGludm9jYXRpb25zIDoA" +
+    "AUQAAUYAAUkAAklJAA1JTlZPS0VfU1RBVElDAAFKAAFMAAJMQwACTEQAAkxGAAJMSQACTEoA" +
+    "AkxMAARMTElMAANMTEwABExMTEwAKUxUZXN0SW52b2tlQ3VzdG9tV2l0aENvbmN1cnJlbnRU" +
+    "aHJlYWRzJDE7ACdMVGVzdEludm9rZUN1c3RvbVdpdGhDb25jdXJyZW50VGhyZWFkczsAAkxa" +
+    "ADNMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9DYWxsZWRCeUludm9rZUN1c3RvbTsA" +
+    "MUxjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0xpbmtlck1ldGhvZEhhbmRsZTsAL0xj" +
+    "b20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL01ldGhvZEhhbmRsZUtpbmQ7ACJMZGFsdmlr" +
+    "L2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90YXRpb24vSW5uZXJD" +
+    "bGFzczsAIUxkYWx2aWsvYW5ub3RhdGlvbi9NZW1iZXJDbGFzc2VzOwAdTGRhbHZpay9hbm5v" +
+    "dGF0aW9uL1NpZ25hdHVyZTsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABVMamF2YS9p" +
+    "by9QcmludFN0cmVhbTsAGkxqYXZhL2xhbmcvQXNzZXJ0aW9uRXJyb3I7ABFMamF2YS9sYW5n" +
+    "L0NsYXNzOwATTGphdmEvbGFuZy9JbnRlZ2VyOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2" +
+    "YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsAEkxqYXZhL2xhbmcv" +
+    "U3lzdGVtOwASTGphdmEvbGFuZy9UaHJlYWQ7ABZMamF2YS9sYW5nL1RocmVhZExvY2FsABdM" +
+    "amF2YS9sYW5nL1RocmVhZExvY2FsOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9s" +
+    "YW5nL2ludm9rZS9DYWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNp" +
+    "dGU7AB9MamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9r" +
+    "ZS9NZXRob2RIYW5kbGVzJExvb2t1cDsAIExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRs" +
+    "ZXM7AB1MamF2YS9sYW5nL2ludm9rZS9NZXRob2RUeXBlOwArTGphdmEvdXRpbC9jb25jdXJy" +
+    "ZW50L2F0b21pYy9BdG9taWNJbnRlZ2VyOwARTlVNQkVSX09GX1RIUkVBRFMAAVMABFRZUEUA" +
+    "KlRlc3RJbnZva2VDdXN0b21XaXRoQ29uY3VycmVudFRocmVhZHMuamF2YQAkVGhyZWFkcyBk" +
+    "aWQgbm90IHRoZSBzYW1lIGNhbGwtc2l0ZXM6AAFWAANWQkIAA1ZDQwADVkREAANWRkYAAlZJ" +
+    "AANWSUkAAlZKAANWSkoAAlZMAANWTEwAA1ZTUwACVloACFdpbm5lcnMgAAFaAAJaTAASW0xq" +
+    "YXZhL2xhbmcvQ2xhc3M7ABNbTGphdmEvbGFuZy9PYmplY3Q7ABNbTGphdmEvbGFuZy9UaHJl" +
+    "YWQ7ABxbTGphdmEvbGFuZy9pbnZva2UvQ2FsbFNpdGU7ACxbTGphdmEvdXRpbC9jb25jdXJy" +
+    "ZW50L2F0b21pYy9BdG9taWNJbnRlZ2VyOwALYWNjZXNzRmxhZ3MABmFwcGVuZAANYXJndW1l" +
+    "bnRUeXBlcwAMYXNzZXJ0RXF1YWxzABFhc3NlcnRFcXVhbHMgYjE6IAARYXNzZXJ0RXF1YWxz" +
+    "IGMxOiAAEWFzc2VydEVxdWFscyBkMTogABFhc3NlcnRFcXVhbHMgZjE6IAARYXNzZXJ0RXF1" +
+    "YWxzIGkxOiAAEWFzc2VydEVxdWFscyBsMTogABFhc3NlcnRFcXVhbHMgczE6IAASYXNzZXJ0" +
+    "RXF1YWxzOiBvMTogAA9hc3NlcnROb3RFcXVhbHMAFWFzc2VydE5vdEVxdWFsczogbzE6IAAK" +
+    "YXNzZXJ0VHJ1ZQASYXNzZXJ0VHJ1ZSB2YWx1ZTogAAJiMQACYjIAAmMxAAJjMgAGY2FsbGVk" +
+    "AAZjYWxsZXIAAmQxAAJkMgANZHJvcEFyZ3VtZW50cwASZW1pdHRlcjogamFjay00LjI1AA1l" +
+    "bmNsb3NpbmdUeXBlAAZlcXVhbHMAAmYxAAJmMgAKZmluZFN0YXRpYwAGZm9ybWF0AANnZXQA" +
+    "D2dldEFuZEluY3JlbWVudAAOZ2V0VGhyZWFkSW5kZXgAAWkAAmkxAAJpMgAFaW5kZXgADGlu" +
+    "aXRpYWxWYWx1ZQAPaW5zZXJ0QXJndW1lbnRzAAxpbnN0YW50aWF0ZWQACGludFZhbHVlABJp" +
+    "bnZva2VNZXRob2RIYW5kbGUABGpvaW4ABGtpbmQAAmwxAAJsMgAMbGlua2VyTWV0aG9kAApt" +
+    "ZXRob2RUeXBlAAJtaAAEbmFtZQAJbmV4dEluZGV4AAdub3RVc2VkAAFvAANvdXQAAXAADnBh" +
+    "cmFtZXRlckNvdW50AAdwcmludGxuAApyZXR1cm5UeXBlAANydW4AAnMxAAJzMgADc2V0AAlz" +
+    "ZXRDYWxsZWQABXNsZWVwAAVzdGFydAAJdGFyZ2V0dGVkAAR0ZXN0AAR0aGlzAAt0aHJlYWRJ" +
+    "bmRleAAHdGhyZWFkcwAIdG9TdHJpbmcABHR5cGUABXZhbHVlAAd2YWx1ZU9mAAV2b3RlcwAH" +
+    "d2lubmVycwABeAAmAAcOAls7ACkABw4AJwAHDgAjAAcsiVg1TU0CbEoANgAHDgCqAQJsbQcO" +
+    "PACvAQJubwcOPADIAQJycwcOWgDDAQJ4eQcOWgC5AQKAAYEBBw48AL4BAooBiwEHDloAzQEC" +
+    "kgGUAQcOPLQA2QECmQGaAQcOLSClIAC0AQKZAZoBBw48ANMBApIBlAEHDjzSAKQBAaYBBw4t" +
+    "ARoQADkABw4AWANxjwGNAQcsTAMBogEFaQMAjgEeeLTDpbR8W9IAPQGqAQcOAE8BggEHDni0" +
+    "AG0AB1kBAQMAfwUtlpNBLQMBowEoPHhXQTxYQB4DA6kBBR4DAqgBBTyHpS2RQwEkEi2HPHgB" +
+    "FA06Qy2HPHgBFA06QTw8AEIABw5aAwCqAQU8AAILAaUBGAcCDAJbBAiOAR4CDgGlARwEFzcX" +
+    "DBcxFw8CDQGlARwBGAYCDwGlARwBGBoACARdHAEYBIYBHAEdCQRdHAMYHhgVGCB1GAeIARsG" +
+    "jgEXi44BF5uWARgEAQQQAxYAF5sVAQAAAQIAgIAEjBABBKQQAcQg0BAGABMBABoBCgEKARoB" +
+    "CgEaA4gg7BABiIAEhBEBgoAE0BEBCegRAQnIEgEJqBMBCYwUAQnwFAEJ0BUBCbQWAQmsFwEJ" +
+    "pBgBCYQZAQmAGgEKzBoBCvgaAQnMHAIK4BwBCZgdFAH4IAAAEwAAAAAAAAABAAAAAAAAAAEA" +
+    "AACqAAAAcAAAAAIAAAAqAAAAGAMAAAMAAAAmAAAAwAMAAAQAAAAJAAAAiAUAAAUAAAA4AAAA" +
+    "0AUAAAcAAAABAAAAkAcAAAYAAAACAAAAlAcAAAgAAAABAAAA1AcAAAMQAAAFAAAA3AcAAAEg" +
+    "AAAXAAAADAgAAAYgAAACAAAAnBAAAAEQAAAXAAAA3BAAAAIgAACqAAAApBEAAAMgAAAWAAAA" +
+    "1RoAAAQgAAAGAAAA9BsAAAUgAAACAAAAUBwAAAAgAAACAAAAWhwAAAAQAAABAAAA1BwAAA==";
+}
diff --git a/test/952-invoke-custom/src/TestDataLinkerMethodMinimalArguments.java b/test/952-invoke-custom/src/TestDataLinkerMethodMinimalArguments.java
new file mode 100644
index 0000000..443a7af
--- /dev/null
+++ b/test/952-invoke-custom/src/TestDataLinkerMethodMinimalArguments.java
@@ -0,0 +1,106 @@
+/* Generated by build-test.sh from TestLinkerMethodMinimalArguments.java */
+public class TestDataLinkerMethodMinimalArguments {
+  public static final String BASE64_DEX_FILE =
+    "ZGV4CjAzOADnZpVEc25JsNXLCW+vh64OuLf8RymAuINwFQAAcAAAAHhWNBIAAAAAAAAAAIgU" +
+    "AACBAAAAcAAAAB0AAAB0AgAAHAAAAOgCAAAHAAAAOAQAACIAAABwBAAAAQAAAIQFAADEDwAA" +
+    "rAUAAKAMAACjDAAApwwAAKoMAACyDAAAugwAAMIMAADKDAAA0gwAANoMAADiDAAA6gwAAPQM" +
+    "AAD8DAAA/wwAAAINAAAFDQAACA0AADENAABUDQAAZw0AAIoNAACbDQAAng0AAKMNAACyDQAA" +
+    "tQ0AALgNAAC8DQAAwA0AAMQNAADIDQAAzA0AANANAADWDQAA+g0AAP4NAAAzDgAAZg4AAJcO" +
+    "AACzDgAAyg4AAOsOAAAHDwAAGg8AAD4PAABSDwAAZg8AAIEPAACVDwAArA8AAMkPAADuDwAA" +
+    "DxAAADgQAABXEAAAgBAAAIMQAACqEAAA0RAAAAQRAAAHEQAADBEAABERAAAWEQAAGxEAACAR" +
+    "AAAmEQAAKxEAAC8RAAA0EQAAOREAAD0RAABAEQAARBEAAEcRAABMEQAAVBEAAGMRAABxEQAA" +
+    "hBEAAJcRAACqEQAAvREAANARAADjEQAA9hEAAAoSAAAWEgAAKhIAAC0SAAAxEgAANRIAADkS" +
+    "AAA9EgAARRIAAEkSAABNEgAAYRIAAHASAAB4EgAAfBIAAIASAACNEgAAmRIAAKsSAACvEgAA" +
+    "sxIAAMcSAADNEgAA0RIAANUSAADjEgAA/xIAAAsTAAATEwAAGRMAABwTAAAhEwAAJBMAAC0T" +
+    "AAA5EwAAPRMAAEETAABHEwAATRMAAFcTAABeEwAAYRMAAA0AAAAOAAAADwAAABAAAAAWAAAA" +
+    "GQAAACIAAAAkAAAAJQAAACYAAAAnAAAAKAAAACkAAAAqAAAAKwAAACwAAAAtAAAALgAAAC8A" +
+    "AAAwAAAAMQAAADIAAAAzAAAANAAAADUAAAA2AAAAOAAAADwAAABIAAAAFwAAAAQAAADsCwAA" +
+    "GgAAABEAAAAAAAAAGwAAABIAAAD0CwAAHAAAABIAAAD8CwAAHQAAABIAAAAEDAAAHgAAABIA" +
+    "AAAMDAAAHwAAABIAAAAUDAAAIAAAABIAAAAcDAAAIAAAABIAAAAkDAAAIwAAABIAAAAsDAAA" +
+    "IQAAABUAAAA0DAAAIQAAABcAAABADAAAPAAAABsAAAAAAAAAPQAAABsAAABMDAAAPgAAABsA" +
+    "AABUDAAAPwAAABsAAABcDAAAQAAAABsAAABkDAAAQQAAABsAAADsCwAAQgAAABsAAABsDAAA" +
+    "QwAAABsAAAB4DAAARAAAABsAAAAcDAAARQAAABsAAACADAAARAAAABsAAAAkDAAARQAAABsA" +
+    "AACIDAAARAAAABsAAACQDAAARgAAABsAAACYDAAARwAAABsAAAAsDAAASQAAABwAAAAcDAAA" +
+    "BgAEABEAAAAGAAQAEgAAAAYABAATAAAABgAEABQAAAAGAAQAaAAAAAkACQAYAAAAEwALAHUA" +
+    "AAAGAAwACwAAAAYADAAMAAAABgAAAEsAAAAGAA0ATgAAAAYADgBOAAAABgAPAE4AAAAGABAA" +
+    "TgAAAAYAEQBOAAAABgATAE4AAAAGABUATgAAAAYAFwBOAAAABgAZAE4AAAAGABoAVwAAAAYA" +
+    "CgBvAAAABgASAHsAAAALABYAdwAAAAwAFgAMAAAADQAUAAwAAAAPABYADAAAABAADAAMAAAA" +
+    "EAAbAGMAAAARABsAYwAAABIADAAMAAAAEgACAEwAAAASAAMATAAAABIABABMAAAAEgAFAEwA" +
+    "AAASAAYATAAAABIABwBMAAAAEgAIAEwAAAASAAkATAAAABIAAQB9AAAAFgAYAAwAAAAYAAsA" +
+    "ZwAAADIUAAAGAAAAAQAAABAAAAAAAAAAOQAAAMQLAAA5FAAAAAAAAAQAAAANAAAAAQAAAAIU" +
+    "AAABAAAAKhQAAAEAAAAAAAAAZBMAAA8AAAASAGcABABnAAIAEhBnAAAAEiBnAAEAEjBnAAMA" +
+    "DgAAAAEAAQABAAAAcBMAAAQAAABwEBMAAAAOAAQAAgACAAAAdRMAABoAAABgAAQAYAEDADMQ" +
+    "EwBiAAYAGwE6AAAAbiAPABAAIgAMABsBSwAAAHAgEAAQACcAkAACAw8ABQACAAIAAAB/EwAA" +
+    "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" +
+    "KQAGLCBiMjogAAYsIGMyOiAABiwgZDI6IAAGLCBmMjogAAYsIGkyOiAABiwgbDI6IAAGLCBv" +
+    "MjogAAYsIHMyOiAACDxjbGluaXQ+AAY8aW5pdD4AAUIAAUMAAUQAAUYAJ0ZBSUxVUkVfVFlQ" +
+    "RV9MSU5LRVJfTUVUSE9EX1JFVFVSTlNfTlVMTAAhRkFJTFVSRV9UWVBFX0xJTktFUl9NRVRI" +
+    "T0RfVEhST1dTABFGQUlMVVJFX1RZUEVfTk9ORQAhRkFJTFVSRV9UWVBFX1RBUkdFVF9NRVRI" +
+    "T0RfVEhST1dTAA9GYWlsdXJlIFR5cGUgKyAAAUkAA0lJSQANSU5WT0tFX1NUQVRJQwABSgAB" +
+    "TAACTEMAAkxEAAJMRgACTEkAAkxKAAJMTAAETExMTAAiTFRlc3RMaW5rZXJNZXRob2RNaW5p" +
+    "bWFsQXJndW1lbnRzOwACTFoAM0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0NhbGxl" +
+    "ZEJ5SW52b2tlQ3VzdG9tOwAxTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvTGlua2Vy" +
+    "TWV0aG9kSGFuZGxlOwAvTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvTWV0aG9kSGFu" +
+    "ZGxlS2luZDsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABVMamF2YS9pby9QcmludFN0" +
+    "cmVhbTsAH0xqYXZhL2xhbmcvQXJpdGhtZXRpY0V4Y2VwdGlvbjsAGkxqYXZhL2xhbmcvQXNz" +
+    "ZXJ0aW9uRXJyb3I7ABFMamF2YS9sYW5nL0NsYXNzOwAiTGphdmEvbGFuZy9JbnN0YW50aWF0" +
+    "aW9uRXhjZXB0aW9uOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsA" +
+    "GUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsAEkxqYXZhL2xhbmcvU3lzdGVtOwAVTGphdmEv" +
+    "bGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9DYWxsU2l0ZTsAI0xqYXZhL2xh" +
+    "bmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5nL2ludm9rZS9NZXRob2RI" +
+    "YW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExvb2t1cDsAHUxqYXZh" +
+    "L2xhbmcvaW52b2tlL01ldGhvZFR5cGU7ACdSZXR1cm5pbmcgbnVsbCBpbnN0ZWFkIG9mIENh" +
+    "bGxTaXRlIGZvciAAAVMAJVRlc3RMaW5rZXJNZXRob2RNaW5pbWFsQXJndW1lbnRzLmphdmEA" +
+    "JVRocm93aW5nIEFyaXRobWV0aWNFeGNlcHRpb24gaW4gYWRkKCkAMVRocm93aW5nIEluc3Rh" +
+    "bnRpYXRpb25FeGNlcHRpb24gaW4gbGlua2VyTWV0aG9kKCkAAVYAA1ZCQgADVkNDAANWREQA" +
+    "A1ZGRgADVklJAARWSUlJAANWSkoAAlZMAANWTEwAA1ZTUwACVloAAVoAAlpMAAFhAANhZGQA" +
+    "BmFwcGVuZAANYXJndW1lbnRUeXBlcwAMYXNzZXJ0RXF1YWxzABFhc3NlcnRFcXVhbHMgYjE6" +
+    "IAARYXNzZXJ0RXF1YWxzIGMxOiAAEWFzc2VydEVxdWFscyBkMTogABFhc3NlcnRFcXVhbHMg" +
+    "ZjE6IAARYXNzZXJ0RXF1YWxzIGkxOiAAEWFzc2VydEVxdWFscyBsMTogABFhc3NlcnRFcXVh" +
+    "bHMgczE6IAASYXNzZXJ0RXF1YWxzOiBvMTogAAphc3NlcnRUcnVlABJhc3NlcnRUcnVlIHZh" +
+    "bHVlOiAAAWIAAmIxAAJiMgACYzEAAmMyAAZjYWxsZXIAAmQxAAJkMgASZW1pdHRlcjogamFj" +
+    "ay00LjI1AA1lbmNsb3NpbmdUeXBlAAZlcXVhbHMAAmYxAAJmMgALZmFpbHVyZVR5cGUACmZp" +
+    "bmRTdGF0aWMAEGZvcmNlRmFpbHVyZVR5cGUAAmkxAAJpMgASaW52b2tlTWV0aG9kSGFuZGxl" +
+    "AARraW5kAAJsMQACbDIADGxpbmtlck1ldGhvZAAabGlua2VyTWV0aG9kIGZhaWx1cmUgdHlw" +
+    "ZSAACm1ldGhvZFR5cGUABm1oX2FkZAAEbmFtZQABbwADb3V0AAFwAAdwcmludGxuAApyZXR1" +
+    "cm5UeXBlAAJzMQACczIABHRlc3QABHRoaXMACHRvU3RyaW5nAAV2YWx1ZQABeAABeQAeAAcd" +
+    "Li08PAJ5OwAcAAcOAC8CS1oHDmmHlwBWAltcBw48AFsCXV4HDjwAdAJgYQcOWgBvAmVmBw5a" +
+    "AGUCamsHDjwAagJubwcOWgB5AnV3Bw48tAB/Anp7Bw4tIKUgAGACensHDjwAUAF/Bw4tARoQ" +
+    "ADkDX3RyBw4BGxBpAwBzGGkBJA8taYeXAEgDZ4ABgQEHLId4LZYBLw8CeywtAAAHBE0cAhgE" +
+    "GARrHAEdCARNHAMYGBgRGBliGAZsGwVzF29zF0t4GAQCCgF+HAEYFAMWABdLFQAFAA8AAAoB" +
+    "CgEKAQoBCgCIgAS8CwGBgATsCwEKhAwBCcgMAQmoDQEJiA4BCewOAQnQDwEJsBABCZQRAQmM" +
+    "EgEJhBMBCeQTAQqwFAEJlBYAEwAAAAAAAAABAAAAAAAAAAEAAACBAAAAcAAAAAIAAAAdAAAA" +
+    "dAIAAAMAAAAcAAAA6AIAAAQAAAAHAAAAOAQAAAUAAAAiAAAAcAQAAAcAAAABAAAAgAUAAAYA" +
+    "AAABAAAAhAUAAAgAAAABAAAApAUAAAMQAAACAAAArAUAAAEgAAAPAAAAvAUAAAYgAAABAAAA" +
+    "xAsAAAEQAAAVAAAA7AsAAAIgAACBAAAAoAwAAAMgAAAPAAAAZBMAAAQgAAACAAAAAhQAAAUg" +
+    "AAABAAAAMhQAAAAgAAABAAAAORQAAAAQAAABAAAAiBQAAA==";
+}
diff --git a/test/952-invoke-custom/src/TestDataLinkerMethodMultipleArgumentTypes.java b/test/952-invoke-custom/src/TestDataLinkerMethodMultipleArgumentTypes.java
new file mode 100644
index 0000000..b96e184
--- /dev/null
+++ b/test/952-invoke-custom/src/TestDataLinkerMethodMultipleArgumentTypes.java
@@ -0,0 +1,108 @@
+/* Generated by build-test.sh from TestLinkerMethodMultipleArgumentTypes.java */
+public class TestDataLinkerMethodMultipleArgumentTypes {
+  public static final String BASE64_DEX_FILE =
+    "ZGV4CjAzOADmj8ccx56N3pWZ9IunuZvI0eWD+wmFmSnEFQAAcAAAAHhWNBIAAAAAAAAAANwU" +
+    "AACTAAAAcAAAAB0AAAC8AgAAHQAAADADAAADAAAAjAQAACIAAACkBAAAAQAAALgFAADkDwAA" +
+    "4AUAAEQMAABHDAAASgwAAFIMAABaDAAAYgwAAGoMAAByDAAAegwAAIIMAACKDAAAkgwAAJwM" +
+    "AACkDAAApwwAAKoMAACtDAAAsAwAAMYMAADNDAAA0AwAANUMAADkDAAA5wwAAOoMAADuDAAA" +
+    "8gwAAPYMAAD6DAAA/gwAAAINAAAIDQAAGA0AAEENAABFDQAAeg0AAKMNAADWDQAABw4AACYO" +
+    "AABCDgAATA4AAGMOAAB/DgAAkQ4AAKQOAAC6DgAAzg4AAOIOAAD9DgAAEQ8AACgPAABFDwAA" +
+    "ag8AAIsPAAC0DwAA0w8AANYPAAACEAAABRAAAAoQAAAPEAAAFBAAABkQAAAdEAAAIhAAACcQ" +
+    "AAArEAAAMBAAADUQAAA5EAAAPBAAAEUQAABJEAAATBAAAFEQAABZEAAAaBAAAHYQAACJEAAA" +
+    "nBAAAK8QAADCEAAA1RAAAOgQAAD7EAAADxEAABsRAAAvEQAAMhEAADYRAAA6EQAASBEAAFsR" +
+    "AABmEQAAahEAAG4RAAB2EQAAgREAAI0RAACREQAAlREAAKIRAAC2EQAAxREAAM0RAADREQAA" +
+    "1REAAOERAADtEQAA8REAAPURAAD/EQAAExIAABkSAAAdEgAAIRIAAC8SAAA6EgAAURIAAF0S" +
+    "AABlEgAAaxIAAG4SAABzEgAAdhIAAH8SAACLEgAAjxIAAJMSAACfEgAArBIAALISAAC4EgAA" +
+    "whIAAMYSAADLEgAAzxIAANMSAADXEgAA2xIAAN8SAADjEgAA5xIAAOsSAADyEgAA9RIAAA0A" +
+    "AAAOAAAADwAAABAAAAATAAAAFgAAACAAAAAiAAAAIwAAACQAAAAlAAAAJgAAACcAAAApAAAA" +
+    "KgAAACwAAAAuAAAALwAAADAAAAAxAAAAMgAAADMAAAA0AAAANQAAADYAAAA3AAAAOAAAADoA" +
+    "AABGAAAAEwAAAAQAAAAAAAAAFAAAAAQAAACICwAAFwAAABEAAAAAAAAAGAAAABIAAACQCwAA" +
+    "GQAAABIAAACYCwAAGgAAABIAAACgCwAAGwAAABIAAACoCwAAHAAAABIAAACwCwAAHQAAABIA" +
+    "AAC4CwAAHQAAABIAAADACwAAIQAAABIAAADICwAAHwAAABUAAADQCwAAHgAAABcAAADwCwAA" +
+    "OgAAABsAAAAAAAAAOwAAABsAAAD8CwAAPAAAABsAAAAEDAAAPQAAABsAAAAMDAAAPgAAABsA" +
+    "AAAUDAAAPwAAABsAAACoCwAAQAAAABsAAACICwAAQQAAABsAAAAcDAAAQgAAABsAAAC4CwAA" +
+    "QwAAABsAAAAkDAAAQgAAABsAAADACwAAQwAAABsAAAAsDAAAQgAAABsAAAA0DAAARAAAABsA" +
+    "AAA8DAAARQAAABsAAADICwAASAAAABwAAAC4CwAABgAEAFwAAAAKAAoAFQAAABMADQB7AAAA" +
+    "BgANAAsAAAAGAA0ADAAAAAYAAAARAAAABgABAEoAAAAGAA4ATQAAAAYADwBNAAAABgAQAE0A" +
+    "AAAGABEATQAAAAYAEwBNAAAABgAUAE0AAAAGABYATQAAAAYAGABNAAAABgAaAE0AAAAGABsA" +
+    "VgAAAAYACwB0AAAABgATAIMAAAANABIAfQAAAA0AFwB9AAAADgAVAAwAAAAQAA0ADAAAABAA" +
+    "HABoAAAAEQAcAGgAAAASAA0ADAAAABIAAwBLAAAAEgAEAEsAAAASAAUASwAAABIABgBLAAAA" +
+    "EgAHAEsAAAASAAgASwAAABIACQBLAAAAEgAKAEsAAAASAAIAhQAAABYAGQAMAAAAGAAMAGsA" +
+    "AABpFAAABgAAAAEAAAAQAAAAAAAAADkAAABgCwAAkhQAAAAAAAAEAAAADgAAAAEAAACpEwAA" +
+    "AgAAAEcUAABgFAAAAQAAAGAUAAABAAAAAAAAAPgSAAAEAAAAEgBnAAAADgABAAEAAQAAAP4S" +
+    "AAAEAAAAcBATAAAADgADAAIAAAAAAAMTAAADAAAAkAABAg8AAAAFAAIAAgAAAAoTAAAoAAAA" +
+    "M0MDAA4AIgAOACIBEgBwEBYAAQAbAk4AAABuIB0AIQAMAW4gGgAxAAwBGwICAAAAbiAdACEA" +
+    "DAFuIBoAQQAMAW4QHwABAAwBcCASABAAJwAFAAIAAgAAABITAAAoAAAAM0MDAA4AIgAOACIB" +
+    "EgBwEBYAAQAbAk8AAABuIB0AIQAMAW4gFwAxAAwBGwIDAAAAbiAdACEADAFuIBcAQQAMAW4Q" +
+    "HwABAAwBcCASABAAJwAIAAQAAwAAABoTAAAqAAAALwAEBjkAAwAOACIADgAiARIAcBAWAAEA" +
+    "GwJQAAAAbiAdACEADAFuMBgAQQUMARsCBAAAAG4gHQAhAAwBbjAYAGEHDAFuEB8AAQAMAXAg" +
+    "EgAQACcABQACAAIAAAAiEwAAKgAAAC0AAwQ5AAMADgAiAA4AIgESAHAQFgABABsCUQAAAG4g" +
+    "HQAhAAwBbiAZADEADAEbAgUAAABuIB0AIQAMAW4gGQBBAAwBbhAfAAEADAFwIBIAEAAnAAUA" +
+    "AgACAAAAKhMAACgAAAAzQwMADgAiAA4AIgESAHAQFgABABsCUgAAAG4gHQAhAAwBbiAaADEA" +
+    "DAEbAgYAAABuIB0AIQAMAW4gGgBBAAwBbhAfAAEADAFwIBIAEAAnAAgABAADAAAAMhMAACoA" +
+    "AAAxAAQGOQADAA4AIgAOACIBEgBwEBYAAQAbAlMAAABuIB0AIQAMAW4wGwBBBQwBGwIHAAAA" +
+    "biAdACEADAFuMBsAYQcMAW4QHwABAAwBcCASABAAJwAFAAIAAgAAADoTAAAzAAAAM0MDAA4A" +
+    "OAMLADgECQBuIBQAQwAKADgAAwAOACIADgAiARIAcBAWAAEAGwJVAAAAbiAdACEADAFuIBwA" +
+    "MQAMARsCCAAAAG4gHQAhAAwBbiAcAEEADAFuEB8AAQAMAXAgEgAQACcAAAAFAAIAAgAAAEMT" +
+    "AAAzAAAAM0MDAA4AOAMLADgECQBuIBUAQwAKADgAAwAOACIADgAiARIAcBAWAAEAGwJUAAAA" +
+    "biAdACEADAFuIB0AMQAMARsCCQAAAG4gHQAhAAwBbiAdAEEADAFuEB8AAQAMAXAgEgAQACcA" +
+    "AAAFAAIAAgAAAFETAAAoAAAAM0MDAA4AIgAOACIBEgBwEBYAAQAbAlQAAABuIB0AIQAMAW4g" +
+    "GgAxAAwBGwIJAAAAbiAdACEADAFuIBoAQQAMAW4QHwABAAwBcCASABAAJwAEAAEAAgAAAFsT" +
+    "AAAdAAAAOQMcACIADgAiARIAcBAWAAEAGwJXAAAAbiAdACEADAFuIB4AMQAMAW4QHwABAAwB" +
+    "cCASABAAJwAOAAAAFgAPAAQAAABmEwAAbAAAAGIDAgAiBBIAcBAWAAQAGwUoAAAAbiAdAFQA" +
+    "DARuIB0AhAAMBBsFAAAAAG4gHQBUAAwEbiAcAJQADARuEB8ABAAMBG4gEQBDAHEQDQAKABIT" +
+    "cSAIALMAEwNhAHEgBQDDABMDAARxIAgA0wASE3EgCADjABQDmpkxQXEgBwDzABgEmpmZmZmZ" +
+    "AUAFABAAcUAGAFQQGwMSAAAACAASAHEgCwADABwDBgAIABMAcSAKAAMAFwQVzVsHBQAUAHFA" +
+    "CQBUEBwDBgBuQCEAN5gMAiIDFgBwICAAIwARAwQAAgACAAAAmRMAABEAAACQAAID/CAAADIA" +
+    "CgFxIAgAEABiAAIAkAECA24gEAAQAA4AAAACAAEAAAAAAKQTAAADAAAAYAAAAA8AAAAAAAAA" +
+    "AAAAAAMAAAAAAAAAAwAAAOAFAAAOAAAA6AUAAA8AAAD0BQAAAgAAAAQABAABAAAAAQAAAAEA" +
+    "AAACAAAAAQAAAAMAAAABAAAABAAAAAEAAAAFAAAAAQAAABAAAAABAAAAEQAAAAEAAAAcAAAA" +
+    "DQAAABgAEQAZABwAAAABABoABAADAAIAEQAPAAUAAAADAAAADwARABkAAAACAAAAAAAAAAIA" +
+    "AAABAAEAAgAAAAIAAgACAAAAAwADAAIAAAAFAAUAAgAAABAAEAACAAAAEQARAAEAAAAXAAAA" +
+    "AgAAABoAGgABIAABKAAGLCBiMjogAAYsIGMyOiAABiwgZDI6IAAGLCBmMjogAAYsIGkyOiAA" +
+    "BiwgbDI6IAAGLCBvMjogAAYsIHMyOiAABjwqPjtKKQAIPGNsaW5pdD4ABjxpbml0PgABQgAB" +
+    "QwABRAABRgAUR2V0Qm9vdHN0cmFwUnVuQ291bnQABUhlbGxvAAFJAANJSUkADUlOVk9LRV9T" +
+    "VEFUSUMAAUoAAUwAAkxDAAJMRAACTEYAAkxJAAJMSgACTEwABExMTEwADkxMTExaQkNTSUZE" +
+    "TExKACdMVGVzdExpbmtlck1ldGhvZE11bHRpcGxlQXJndW1lbnRUeXBlczsAAkxaADNMY29t" +
+    "L2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9DYWxsZWRCeUludm9rZUN1c3RvbTsAJ0xjb20v" +
+    "YW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0NvbnN0YW50OwAxTGNvbS9hbmRyb2lkL2phY2sv" +
+    "YW5ub3RhdGlvbnMvTGlua2VyTWV0aG9kSGFuZGxlOwAvTGNvbS9hbmRyb2lkL2phY2svYW5u" +
+    "b3RhdGlvbnMvTWV0aG9kSGFuZGxlS2luZDsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1" +
+    "cmU7ABpMZGFsdmlrL2Fubm90YXRpb24vVGhyb3dzOwAITGlua2luZyAAFUxqYXZhL2lvL1By" +
+    "aW50U3RyZWFtOwAaTGphdmEvbGFuZy9Bc3NlcnRpb25FcnJvcjsAEExqYXZhL2xhbmcvQ2xh" +
+    "c3MAEUxqYXZhL2xhbmcvQ2xhc3M7ABRMamF2YS9sYW5nL0NsYXNzPCo+OwASTGphdmEvbGFu" +
+    "Zy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRl" +
+    "cjsAEkxqYXZhL2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9s" +
+    "YW5nL2ludm9rZS9DYWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNp" +
+    "dGU7AB9MamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9r" +
+    "ZS9NZXRob2RIYW5kbGVzJExvb2t1cDsAHUxqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5cGU7" +
+    "AAFTACpUZXN0TGlua2VyTWV0aG9kTXVsdGlwbGVBcmd1bWVudFR5cGVzLmphdmEAAVYAA1ZC" +
+    "QgADVkNDAANWREQAA1ZGRgACVkkAA1ZJSQADVkpKAAJWTAADVkxMAANWU1MAAlZaAAFaAAda" +
+    "QkNTSUZEAAJaTAABYQADYWRkAAZhcHBlbmQADWFyZ3VtZW50VHlwZXMADGFzc2VydEVxdWFs" +
+    "cwARYXNzZXJ0RXF1YWxzIGIxOiAAEWFzc2VydEVxdWFscyBjMTogABFhc3NlcnRFcXVhbHMg" +
+    "ZDE6IAARYXNzZXJ0RXF1YWxzIGYxOiAAEWFzc2VydEVxdWFscyBpMTogABFhc3NlcnRFcXVh" +
+    "bHMgbDE6IAARYXNzZXJ0RXF1YWxzIHMxOiAAEmFzc2VydEVxdWFsczogbzE6IAAKYXNzZXJ0" +
+    "VHJ1ZQASYXNzZXJ0VHJ1ZSB2YWx1ZTogAAFiAAJiMQACYjIADGJvb2xlYW5WYWx1ZQARYm9v" +
+    "dHN0cmFwUnVuQ291bnQACWJ5dGVWYWx1ZQACYzEAAmMyAAZjYWxsZXIACWNoYXJWYWx1ZQAK" +
+    "Y2xhc3NWYWx1ZQACZDEAAmQyAAtkb3VibGVWYWx1ZQASZW1pdHRlcjogamFjay00LjI1AA1l" +
+    "bmNsb3NpbmdUeXBlAAZlcXVhbHMAAmYxAAJmMgAKZmluZFN0YXRpYwAKZmxvYXRWYWx1ZQAC" +
+    "aTEAAmkyAAhpbnRWYWx1ZQASaW52b2tlTWV0aG9kSGFuZGxlAARraW5kAAJsMQACbDIADGxp" +
+    "bmtlck1ldGhvZAAJbG9uZ1ZhbHVlABVtZXRob2RIYW5kbGVFeHRyYUFyZ3MACm1ldGhvZFR5" +
+    "cGUABm1oX2FkZAAEbmFtZQABbwADb3V0AAFwAAdwcmludGxuAApyZXR1cm5UeXBlAAJzMQAC" +
+    "czIACnNob3J0VmFsdWUAC3N0cmluZ1ZhbHVlAAR0ZXN0AAR0aGlzAAh0b1N0cmluZwACdjEA" +
+    "A3YxMAACdjIAAnYzAAJ2NAACdjUAAnY2AAJ2NwACdjgAAnY5AAV2YWx1ZQABeAABeQAeAAcO" +
+    "OQAcAAcOADECSlkHDgBZAlpbBw48AF4CX2AHDjwAdwJkZQcOWgByAmprBw5aAGgCbm8HDjwA" +
+    "bQJzdAcOWgB8Ant9Bw48tACCAQKAAYEBBw4tIKUgAGMCgAGBAQcOPABTAZEBBw4tARoQADkN" +
+    "YXp4hwGJAYoBiwGMAY0BjgGPAQCIAQQTkAEQLgcOASQPPEtaWktppYd4iGkDAnkYAE4CkgGT" +
+    "AQcOlngASgAHDgAABwVMHAIYBBgEcBwBHQkETBwNGBgYERgZGBwYABgBGBoYBBgDGAIYERgP" +
+    "GAVnGAZxGwF5F3R2HAodCAFbHAE/HQgBXRwBAAEdCAFhHAEDYR0IAYEBHAEiAAQdCAFvHAEE" +
+    "AR0IAWwcAXCamTFBHQgBZRwB8ZqZmZmZmQFAHQgBggEcARcSHQgBYhwBGAYdCAF1HAFmFc1b" +
+    "B3kXSn4YBAILAZABHAkXARc2Fy8XNxdHFy8XKxcKFzMCDAGQARwBGBQNFgAXShUBBAEEAQRh" +
+    "JAAEBAFwmpkxQfGamZmZmZkBQBcSGAZmFc1bBwEADwEACgCIgAT8CwGBgASUDAIKrAwBCcQM" +
+    "AQmkDQEJhA4BCegOAQnMDwEJrBABCZARAQmIEgEJgBMBCeATAQqsFAEJlBYCAcgWEwAAAAAA" +
+    "AAABAAAAAAAAAAEAAACTAAAAcAAAAAIAAAAdAAAAvAIAAAMAAAAdAAAAMAMAAAQAAAADAAAA" +
+    "jAQAAAUAAAAiAAAApAQAAAcAAAABAAAAtAUAAAYAAAABAAAAuAUAAAgAAAABAAAA2AUAAAMQ" +
+    "AAADAAAA4AUAAAEgAAAQAAAA/AUAAAYgAAABAAAAYAsAAAEQAAAUAAAAiAsAAAIgAACTAAAA" +
+    "RAwAAAMgAAAQAAAA+BIAAAQgAAADAAAAqRMAAAUgAAABAAAAaRQAAAAgAAABAAAAkhQAAAAQ" +
+    "AAABAAAA3BQAAA==";
+}
diff --git a/test/dexdump/invoke-custom.dex b/test/dexdump/invoke-custom.dex
new file mode 100644
index 0000000..67261ca
--- /dev/null
+++ b/test/dexdump/invoke-custom.dex
Binary files differ
diff --git a/test/dexdump/invoke-custom.lst b/test/dexdump/invoke-custom.lst
new file mode 100644
index 0000000..3540bd1
--- /dev/null
+++ b/test/dexdump/invoke-custom.lst
@@ -0,0 +1,6 @@
+#invoke-custom.dex
+0x000003fc 8 com.android.jack.java7.invokecustom.test004.Tests <init> ()V Tests.java 35
+0x00000414 6 com.android.jack.java7.invokecustom.test004.Tests add (II)I Tests.java 55
+0x0000042c 166 com.android.jack.java7.invokecustom.test004.Tests linkerMethod (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite; Tests.java 62
+0x000004e4 24 com.android.jack.java7.invokecustom.test004.Tests main ([Ljava/lang/String;)V Tests.java 82
+0x0000050c 22 com.android.jack.java7.invokecustom.test004.Tests test ()V Tests.java 78
diff --git a/test/dexdump/invoke-custom.txt b/test/dexdump/invoke-custom.txt
new file mode 100644
index 0000000..e92549a
--- /dev/null
+++ b/test/dexdump/invoke-custom.txt
@@ -0,0 +1,254 @@
+Processing 'invoke-custom.dex'...
+Opened 'invoke-custom.dex', DEX version '038'
+DEX file header:
+magic               : 'dex\n038\0'
+checksum            : db57516f
+signature           : 57be...ffc4
+file_size           : 3276
+header_size         : 112
+link_size           : 0
+link_off            : 0 (0x000000)
+string_ids_size     : 82
+string_ids_off      : 112 (0x000070)
+type_ids_size       : 31
+type_ids_off        : 440 (0x0001b8)
+proto_ids_size      : 16
+proto_ids_off       : 564 (0x000234)
+field_ids_size      : 3
+field_ids_off       : 756 (0x0002f4)
+method_ids_size     : 18
+method_ids_off      : 780 (0x00030c)
+class_defs_size     : 1
+class_defs_off      : 932 (0x0003a4)
+data_size           : 2304
+data_off            : 972 (0x0003cc)
+
+Class #0 header:
+class_idx           : 10
+access_flags        : 1 (0x0001)
+superclass_idx      : 15
+interfaces_off      : 0 (0x000000)
+source_file_idx     : 38
+annotations_off     : 1316 (0x000524)
+class_data_off      : 3014 (0x000bc6)
+static_fields_size  : 1
+instance_fields_size: 0
+direct_methods_size : 4
+virtual_methods_size: 1
+
+Class #0 annotations:
+Annotations on method #1 'add'
+  VISIBILITY_BUILD Lcom/android/jack/annotations/CalledByInvokeCustom; argumentTypes={ I I } invokeMethodHandle={ Lcom/android/jack/annotations/LinkerMethodHandle; argumentTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; Z B C S I F D Ljava/lang/String; Ljava/lang/Class; J } enclosingType=Lcom/android/jack/java7/invokecustom/test004/Tests; kind=INVOKE_STATIC name="linkerMethod" } methodHandleExtraArgs={ Lcom/android/jack/annotations/Constant; booleanValue={ true } Lcom/android/jack/annotations/Constant; byteValue={ 1 } Lcom/android/jack/annotations/Constant; charValue={ 97 } Lcom/android/jack/annotations/Constant; shortValue={ 1024 } Lcom/android/jack/annotations/Constant; intValue={ 1 } Lcom/android/jack/annotations/Constant; floatValue={ 11.1 } Lcom/android/jack/annotations/Constant; doubleValue={ 2.2 } Lcom/android/jack/annotations/Constant; stringValue={ "Hello" } Lcom/android/jack/annotations/Constant; classValue={ Lcom/android/jack/java7/invokecustom/test004/Tests; } Lcom/android/jack/annotations/Constant; longValue={ 123456789 } } name="add" returnType=I
+Annotations on method #2 'linkerMethod'
+  VISIBILITY_SYSTEM Ldalvik/annotation/Signature; value={ "(" "Ljava/lang/invoke/MethodHandles$Lookup;" "Ljava/lang/String;" "Ljava/lang/invoke/MethodType;" "ZBCSIFD" "Ljava/lang/String;" "Ljava/lang/Class" "<*>;J)" "Ljava/lang/invoke/CallSite;" }
+  VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; }
+Annotations on method #4 'test'
+  VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; }
+  VISIBILITY_RUNTIME Lorg/junit/Test;
+
+Class #0            -
+  Class descriptor  : 'Lcom/android/jack/java7/invokecustom/test004/Tests;'
+  Access flags      : 0x0001 (PUBLIC)
+  Superclass        : 'Ljava/lang/Object;'
+  Interfaces        -
+  Static fields     -
+    #0              : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+      name          : 'fieldCallSite'
+      type          : 'Ljava/lang/invoke/CallSite;'
+      access        : 0x0009 (PUBLIC STATIC)
+  Instance fields   -
+  Direct methods    -
+    #0              : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+      name          : '<init>'
+      type          : '()V'
+      access        : 0x10001 (PUBLIC CONSTRUCTOR)
+      code          -
+      registers     : 1
+      ins           : 1
+      outs          : 1
+      insns size    : 4 16-bit code units
+0003ec:                                        |[0003ec] com.android.jack.java7.invokecustom.test004.Tests.<init>:()V
+0003fc: 7010 0600 0000                         |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0006
+000402: 0e00                                   |0003: return-void
+      catches       : (none)
+      positions     : 
+        0x0000 line=35
+      locals        : 
+        0x0000 - 0x0004 reg=0 this Lcom/android/jack/java7/invokecustom/test004/Tests; 
+
+    #1              : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+      name          : 'add'
+      type          : '(II)I'
+      access        : 0x000a (PRIVATE STATIC)
+      code          -
+      registers     : 3
+      ins           : 2
+      outs          : 0
+      insns size    : 3 16-bit code units
+000404:                                        |[000404] com.android.jack.java7.invokecustom.test004.Tests.add:(II)I
+000414: 9000 0102                              |0000: add-int v0, v1, v2
+000418: 0f00                                   |0002: return v0
+      catches       : (none)
+      positions     : 
+        0x0000 line=55
+      locals        : 
+        0x0000 - 0x0003 reg=1 (null) I 
+        0x0000 - 0x0003 reg=2 (null) I 
+
+    #2              : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+      name          : 'linkerMethod'
+      type          : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;'
+      access        : 0x000a (PRIVATE STATIC)
+      code          -
+      registers     : 24
+      ins           : 15
+      outs          : 6
+      insns size    : 83 16-bit code units
+00041c:                                        |[00041c] com.android.jack.java7.invokecustom.test004.Tests.linkerMethod:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;
+00042c: 7110 1100 0c00                         |0000: invoke-static {v12}, Ljunit/framework/Assert;.assertTrue:(Z)V // method@0011
+000432: 1212                                   |0003: const/4 v2, #int 1 // #1
+000434: 7120 0d00 d200                         |0004: invoke-static {v2, v13}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d
+00043a: 1302 6100                              |0007: const/16 v2, #int 97 // #61
+00043e: 7120 0a00 e200                         |0009: invoke-static {v2, v14}, Ljunit/framework/Assert;.assertEquals:(CC)V // method@000a
+000444: 1302 0004                              |000c: const/16 v2, #int 1024 // #400
+000448: 7120 0d00 f200                         |000e: invoke-static {v2, v15}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d
+00044e: 1212                                   |0011: const/4 v2, #int 1 // #1
+000450: 0200 1000                              |0012: move/from16 v0, v16
+000454: 7120 0d00 0200                         |0014: invoke-static {v2, v0}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d
+00045a: 1202                                   |0017: const/4 v2, #int 0 // #0
+00045c: 1403 9a99 3141                         |0018: const v3, #float 11.1 // #4131999a
+000462: 0200 1100                              |001b: move/from16 v0, v17
+000466: 7130 0c00 0302                         |001d: invoke-static {v3, v0, v2}, Ljunit/framework/Assert;.assertEquals:(FFF)V // method@000c
+00046c: 1606 0000                              |0020: const-wide/16 v6, #int 0 // #0
+000470: 1802 9a99 9999 9999 0140               |0022: const-wide v2, #double 2.2 // #400199999999999a
+00047a: 0504 1200                              |0027: move-wide/from16 v4, v18
+00047e: 7706 0b00 0200                         |0029: invoke-static/range {v2, v3, v4, v5, v6, v7}, Ljunit/framework/Assert;.assertEquals:(DDD)V // method@000b
+000484: 1b02 0700 0000                         |002c: const-string/jumbo v2, "Hello" // string@00000007
+00048a: 0800 1400                              |002f: move-object/from16 v0, v20
+00048e: 7120 1000 0200                         |0031: invoke-static {v2, v0}, Ljunit/framework/Assert;.assertEquals:(Ljava/lang/String;Ljava/lang/String;)V // method@0010
+000494: 1c02 0a00                              |0034: const-class v2, Lcom/android/jack/java7/invokecustom/test004/Tests; // type@000a
+000498: 0800 1500                              |0036: move-object/from16 v0, v21
+00049c: 7120 0f00 0200                         |0038: invoke-static {v2, v0}, Ljunit/framework/Assert;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@000f
+0004a2: 1702 15cd 5b07                         |003b: const-wide/32 v2, #float 1.6536e-34 // #075bcd15
+0004a8: 0500 1600                              |003e: move-wide/from16 v0, v22
+0004ac: 7140 0e00 3210                         |0040: invoke-static {v2, v3, v0, v1}, Ljunit/framework/Assert;.assertEquals:(JJ)V // method@000e
+0004b2: 7100 0900 0000                         |0043: invoke-static {}, Ljava/lang/invoke/MethodHandles;.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; // method@0009
+0004b8: 0c02                                   |0046: move-result-object v2
+0004ba: 1c03 0a00                              |0047: const-class v3, Lcom/android/jack/java7/invokecustom/test004/Tests; // type@000a
+0004be: 6e40 0800 32ba                         |0049: invoke-virtual {v2, v3, v10, v11}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@0008
+0004c4: 0c02                                   |004c: move-result-object v2
+0004c6: 2203 1400                              |004d: new-instance v3, Ljava/lang/invoke/ConstantCallSite; // type@0014
+0004ca: 7020 0700 2300                         |004f: invoke-direct {v3, v2}, Ljava/lang/invoke/ConstantCallSite;.<init>:(Ljava/lang/invoke/MethodHandle;)V // method@0007
+0004d0: 1103                                   |0052: return-object v3
+      catches       : (none)
+      positions     : 
+        0x0000 line=62
+        0x0003 line=63
+        0x0007 line=64
+        0x000c line=65
+        0x0011 line=66
+        0x0017 line=67
+        0x0020 line=68
+        0x002c line=69
+        0x0034 line=70
+        0x003b line=71
+        0x0043 line=72
+        0x004d line=73
+      locals        : 
+        0x0000 - 0x0053 reg=9 (null) Ljava/lang/invoke/MethodHandles$Lookup; 
+        0x0000 - 0x0053 reg=10 (null) Ljava/lang/String; 
+        0x0000 - 0x0053 reg=11 (null) Ljava/lang/invoke/MethodType; 
+        0x0000 - 0x0053 reg=12 (null) Z 
+        0x0000 - 0x0053 reg=13 (null) B 
+        0x0000 - 0x0053 reg=14 (null) C 
+        0x0000 - 0x0053 reg=15 (null) S 
+        0x0000 - 0x0053 reg=16 (null) I 
+        0x0000 - 0x0053 reg=17 (null) F 
+        0x0000 - 0x0053 reg=18 (null) D 
+        0x0000 - 0x0053 reg=20 (null) Ljava/lang/String; 
+        0x0000 - 0x0053 reg=21 (null) Ljava/lang/Class; 
+        0x0000 - 0x0053 reg=22 (null) J 
+
+    #3              : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+      name          : 'main'
+      type          : '([Ljava/lang/String;)V'
+      access        : 0x0009 (PUBLIC STATIC)
+      code          -
+      registers     : 4
+      ins           : 1
+      outs          : 2
+      insns size    : 12 16-bit code units
+0004d4:                                        |[0004d4] com.android.jack.java7.invokecustom.test004.Tests.main:([Ljava/lang/String;)V
+0004e4: 6200 0200                              |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+0004e8: 1221                                   |0002: const/4 v1, #int 2 // #2
+0004ea: 1232                                   |0003: const/4 v2, #int 3 // #3
+0004ec: fc20 0000 2100                         |0004: invoke-custom {v1, v2}, call_site@0000
+0004f2: 0a01                                   |0007: move-result v1
+0004f4: 6e20 0500 1000                         |0008: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(I)V // method@0005
+0004fa: 0e00                                   |000b: return-void
+      catches       : (none)
+      positions     : 
+        0x0000 line=82
+        0x000b line=83
+      locals        : 
+        0x0000 - 0x000c reg=3 (null) [Ljava/lang/String; 
+
+  Virtual methods   -
+    #0              : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+      name          : 'test'
+      type          : '()V'
+      access        : 0x0001 (PUBLIC)
+      code          -
+      registers     : 3
+      ins           : 1
+      outs          : 2
+      insns size    : 11 16-bit code units
+0004fc:                                        |[0004fc] com.android.jack.java7.invokecustom.test004.Tests.test:()V
+00050c: 1220                                   |0000: const/4 v0, #int 2 // #2
+00050e: 1231                                   |0001: const/4 v1, #int 3 // #3
+000510: fc20 0100 1000                         |0002: invoke-custom {v0, v1}, call_site@0001
+000516: 0a00                                   |0005: move-result v0
+000518: 1251                                   |0006: const/4 v1, #int 5 // #5
+00051a: 7120 0d00 0100                         |0007: invoke-static {v1, v0}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d
+000520: 0e00                                   |000a: return-void
+      catches       : (none)
+      positions     : 
+        0x0000 line=78
+        0x000a line=79
+      locals        : 
+        0x0000 - 0x000b reg=2 this Lcom/android/jack/java7/invokecustom/test004/Tests; 
+
+  source_file_idx   : 38 (Tests.java)
+
+Method handle #0:
+  type        : invoke-static
+  target      : Lcom/android/jack/java7/invokecustom/test004/Tests; linkerMethod
+  target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;
+Call site #0:
+  link_argument[0] : 0 (MethodHandle)
+  link_argument[1] : add (String)
+  link_argument[2] : (II)I (MethodType)
+  link_argument[3] : 1 (int)
+  link_argument[4] : 1 (int)
+  link_argument[5] : 97 (int)
+  link_argument[6] : 1024 (int)
+  link_argument[7] : 1 (int)
+  link_argument[8] : 11.1 (float)
+  link_argument[9] : 2.2 (double)
+  link_argument[10] : Hello (String)
+  link_argument[11] : Tests (Class)
+  link_argument[12] : 123456789 (long)
+Call site #1:
+  link_argument[0] : 0 (MethodHandle)
+  link_argument[1] : add (String)
+  link_argument[2] : (II)I (MethodType)
+  link_argument[3] : 1 (int)
+  link_argument[4] : 1 (int)
+  link_argument[5] : 97 (int)
+  link_argument[6] : 1024 (int)
+  link_argument[7] : 1 (int)
+  link_argument[8] : 11.1 (float)
+  link_argument[9] : 2.2 (double)
+  link_argument[10] : Hello (String)
+  link_argument[11] : Tests (Class)
+  link_argument[12] : 123456789 (long)
diff --git a/test/dexdump/invoke-custom.xml b/test/dexdump/invoke-custom.xml
new file mode 100644
index 0000000..2a29667
--- /dev/null
+++ b/test/dexdump/invoke-custom.xml
@@ -0,0 +1,89 @@
+<api>
+<package name="com.android.jack.java7.invokecustom.test004"
+>
+<class name="Tests"
+ extends="java.lang.Object"
+ interface="false"
+ abstract="false"
+ static="false"
+ final="false"
+ visibility="public"
+>
+<field name="fieldCallSite"
+ type="java.lang.invoke.CallSite"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</field>
+<constructor name="Tests"
+ type="com.android.jack.java7.invokecustom.test004.Tests"
+ static="false"
+ final="false"
+ visibility="public"
+>
+</constructor>
+<method name="main"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+<parameter name="arg0" type="java.lang.String[]">
+</parameter>
+</method>
+<method name="test"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ visibility="public"
+>
+</method>
+</class>
+<method_handle index="0"
+ type="invoke-static"
+ target_class="Lcom/android/jack/java7/invokecustom/test004/Tests;"
+ target_member="linkerMethod"
+ target_member_type="(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;"
+>
+</method_handle>
+<call_site index="0">
+<link_argument index="0" type="MethodHandle" value="0"/>
+<link_argument index="1" type="String" values="add"/>
+<link_argument index="2" type="MethodType" value="(II)I"/>
+<link_argument index="3" type="int" value="1"/>
+<link_argument index="4" type="int" value="1"/>
+<link_argument index="5" type="int" value="97"/>
+<link_argument index="6" type="int" value="1024"/>
+<link_argument index="7" type="int" value="1"/>
+<link_argument index="8" type="float" value="11.1"/>
+<link_argument index="9" type="double" value="2.2"/>
+<link_argument index="10" type="String" value="Hello"/>
+<link_argument index="11" type="Class" value="Tests"/>
+<link_argument index="12" type="long" value="123456789"/>
+</call_site>
+<call_site index="1">
+<link_argument index="0" type="MethodHandle" value="0"/>
+<link_argument index="1" type="String" values="add"/>
+<link_argument index="2" type="MethodType" value="(II)I"/>
+<link_argument index="3" type="int" value="1"/>
+<link_argument index="4" type="int" value="1"/>
+<link_argument index="5" type="int" value="97"/>
+<link_argument index="6" type="int" value="1024"/>
+<link_argument index="7" type="int" value="1"/>
+<link_argument index="8" type="float" value="11.1"/>
+<link_argument index="9" type="double" value="2.2"/>
+<link_argument index="10" type="String" value="Hello"/>
+<link_argument index="11" type="Class" value="Tests"/>
+<link_argument index="12" type="long" value="123456789"/>
+</call_site>
+</package>
+</api>