summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Orion Hodson <oth@google.com> 2017-04-10 14:53:47 +0100
committer Orion Hodson <oth@google.com> 2017-07-04 11:57:13 +0100
commit631827d9b200c93f24816c6869d72426f9fed8e3 (patch)
tree9d5d768df39e21a2678d4b9148c5ec4d1c016911
parentd64fbfa3471c47d6628d6014bc4a3ac780abd26a (diff)
Fixes for constant method handles
Add support for constant direct and interface method handles in the DEX file. Add error handling for field and method resolution failures in ClassLinker::ResolveMethodHandle(). Bug: 36957105 Test: art/test/run-test --host 952-invoke-custom-kinds Change-Id: I91a2a23ba3365310eccb8cadd193b62f57e5811c
-rw-r--r--compiler/image_writer.cc5
-rw-r--r--dexdump/dexdump.cc52
-rw-r--r--dexlayout/dex_ir.cc6
-rw-r--r--runtime/class_linker.cc160
-rw-r--r--runtime/dex_file.h4
-rw-r--r--runtime/dex_file_verifier.cc4
-rw-r--r--runtime/interpreter/interpreter_common.cc12
-rw-r--r--runtime/verifier/method_verifier.cc3
-rw-r--r--test/952-invoke-custom-kinds/build22
-rw-r--r--test/952-invoke-custom-kinds/classes/Main.classbin0 -> 302 bytes
-rw-r--r--test/952-invoke-custom-kinds/classes/invokecustom/Interface.classbin0 -> 152 bytes
-rw-r--r--test/952-invoke-custom-kinds/classes/invokecustom/InterfaceImplementor.classbin0 -> 503 bytes
-rw-r--r--test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$Interface.classbin0 -> 239 bytes
-rw-r--r--test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$InterfaceImplementor.classbin0 -> 634 bytes
-rw-r--r--test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.classbin0 -> 7055 bytes
-rw-r--r--test/952-invoke-custom-kinds/classes/invokecustom/Super.classbin0 -> 468 bytes
-rw-r--r--test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator$1.classbin0 -> 1124 bytes
-rw-r--r--test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.classbin0 -> 8641 bytes
-rw-r--r--test/952-invoke-custom-kinds/expected.txt38
-rw-r--r--test/952-invoke-custom-kinds/info.txt7
20 files changed, 258 insertions, 55 deletions
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index a8fdecaa4a..e75f75bbd9 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -473,6 +473,11 @@ void ImageWriter::PrepareDexCacheArraySlots() {
start + layout.MethodTypesOffset(),
dex_cache);
}
+ if (dex_cache->GetResolvedCallSites() != nullptr) {
+ AddDexCacheArrayRelocation(dex_cache->GetResolvedCallSites(),
+ start + layout.CallSitesOffset(),
+ dex_cache);
+ }
}
}
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index df0169f7d0..65232bfaa5 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -1582,31 +1582,53 @@ static void dumpClass(const DexFile* pDexFile, int idx, char** pLastPackage) {
static void dumpMethodHandle(const DexFile* pDexFile, u4 idx) {
const DexFile::MethodHandleItem& mh = pDexFile->GetMethodHandle(idx);
+ const char* type = nullptr;
+ bool is_instance = false;
bool is_invoke = false;
- const char* type;
switch (static_cast<DexFile::MethodHandleType>(mh.method_handle_type_)) {
case DexFile::MethodHandleType::kStaticPut:
type = "put-static";
+ is_instance = false;
+ is_invoke = false;
break;
case DexFile::MethodHandleType::kStaticGet:
type = "get-static";
+ is_instance = false;
+ is_invoke = false;
break;
case DexFile::MethodHandleType::kInstancePut:
type = "put-instance";
+ is_instance = true;
+ is_invoke = false;
break;
case DexFile::MethodHandleType::kInstanceGet:
type = "get-instance";
+ is_instance = true;
+ is_invoke = false;
break;
case DexFile::MethodHandleType::kInvokeStatic:
type = "invoke-static";
+ is_instance = false;
is_invoke = true;
break;
case DexFile::MethodHandleType::kInvokeInstance:
type = "invoke-instance";
+ is_instance = true;
is_invoke = true;
break;
case DexFile::MethodHandleType::kInvokeConstructor:
type = "invoke-constructor";
+ is_instance = true;
+ is_invoke = true;
+ break;
+ case DexFile::MethodHandleType::kInvokeDirect:
+ type = "invoke-direct";
+ is_instance = true;
+ is_invoke = true;
+ break;
+ case DexFile::MethodHandleType::kInvokeInterface:
+ type = "invoke-interface";
+ is_instance = true;
is_invoke = true;
break;
}
@@ -1614,16 +1636,26 @@ static void dumpMethodHandle(const DexFile* pDexFile, u4 idx) {
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();
+ if (type != nullptr) {
+ 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 (is_instance) {
+ member_type = android::base::StringPrintf("(%s%s", declaring_class, member_type.c_str() + 1);
+ }
} 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);
+ type = "?";
+ declaring_class = "?";
+ member = "?";
+ member_type = "?";
}
if (gOptions.outputFormat == OUTPUT_PLAIN) {
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index a200d8d9c7..5913832f96 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -793,8 +793,10 @@ void Collections::CreateMethodHandleItem(const DexFile& dex_file, uint32_t i) {
static_cast<DexFile::MethodHandleType>(disk_method_handle.method_handle_type_);
bool is_invoke = type == DexFile::MethodHandleType::kInvokeStatic ||
type == DexFile::MethodHandleType::kInvokeInstance ||
- type == DexFile::MethodHandleType::kInvokeConstructor;
- static_assert(DexFile::MethodHandleType::kLast == DexFile::MethodHandleType::kInvokeConstructor,
+ type == DexFile::MethodHandleType::kInvokeConstructor ||
+ type == DexFile::MethodHandleType::kInvokeDirect ||
+ type == DexFile::MethodHandleType::kInvokeInterface;
+ static_assert(DexFile::MethodHandleType::kLast == DexFile::MethodHandleType::kInvokeInterface,
"Unexpected method handle types.");
IndexedItem* field_or_method_id;
if (is_invoke) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 10e0bd28c3..7aab9de1ed 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -8256,65 +8256,139 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandle(uint32_t method_handle_id
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;
+ ArtField* target_field = nullptr;
+ ArtMethod* target_method = nullptr;
+
DexFile::MethodHandleType handle_type =
static_cast<DexFile::MethodHandleType>(mh.method_handle_type_);
switch (handle_type) {
case DexFile::MethodHandleType::kStaticPut: {
+ target_field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */);
+ break;
+ }
+ case DexFile::MethodHandleType::kStaticGet: {
+ target_field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */);
+ break;
+ }
+ case DexFile::MethodHandleType::kInstancePut: {
+ target_field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */);
+ break;
+ }
+ case DexFile::MethodHandleType::kInstanceGet: {
+ target_field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */);
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeStatic: {
+ target_method = ResolveMethod<kNoICCECheckForCache>(self,
+ mh.field_or_method_idx_,
+ referrer,
+ InvokeType::kStatic);
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeInstance: {
+ target_method = ResolveMethod<kNoICCECheckForCache>(self,
+ mh.field_or_method_idx_,
+ referrer,
+ InvokeType::kVirtual);
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeConstructor: {
+ UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform.";
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeDirect: {
+ target_method = ResolveMethod<kNoICCECheckForCache>(self,
+ mh.field_or_method_idx_,
+ referrer,
+ InvokeType::kDirect);
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeInterface: {
+ target_method = ResolveMethod<kNoICCECheckForCache>(self,
+ mh.field_or_method_idx_,
+ referrer,
+ InvokeType::kInterface);
+ break;
+ }
+ }
+
+ if (target_field != nullptr) {
+ ObjPtr<mirror::Class> target_class = target_field->GetDeclaringClass();
+ ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
+ if (!referring_class->CanAccessMember(target_class, target_field->GetAccessFlags())) {
+ ThrowIllegalAccessErrorField(referring_class, target_field);
+ return nullptr;
+ }
+ } else if (target_method != nullptr) {
+ ObjPtr<mirror::Class> target_class = target_method->GetDeclaringClass();
+ ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
+ if (!referring_class->CanAccessMember(target_class, target_method->GetAccessFlags())) {
+ ThrowIllegalAccessErrorMethod(referring_class, target_method);
+ return nullptr;
+ }
+ } else {
+ // Common check for resolution failure.
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return nullptr;
+ }
+
+ // Determine the kind and number of parameters after it's safe to
+ // follow the field or method pointer.
+ mirror::MethodHandle::Kind kind;
+ uint32_t num_params;
+ 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.
+ target_method->GetShorty(&shorty_length);
+ num_params = shorty_length - 1; // Remove 1 for the 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.
+ target_method->GetShorty(&shorty_length);
+ num_params = shorty_length; // Add 1 for the receiver, remove 1 for the return value.
break;
}
case DexFile::MethodHandleType::kInvokeConstructor: {
UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform.";
num_params = 0;
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeDirect: {
+ kind = mirror::MethodHandle::Kind::kInvokeDirect;
+ uint32_t shorty_length;
+ target_method->GetShorty(&shorty_length);
+ num_params = shorty_length; // Add 1 for the receiver, remove 1 for the return value.
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeInterface: {
+ kind = mirror::MethodHandle::Kind::kInvokeInterface;
+ uint32_t shorty_length;
+ target_method->GetShorty(&shorty_length);
+ num_params = shorty_length; // Add 1 for the receiver, remove 1 for the return value.
+ break;
}
}
@@ -8331,44 +8405,51 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandle(uint32_t method_handle_id
Handle<mirror::Class> return_type;
switch (handle_type) {
case DexFile::MethodHandleType::kStaticPut: {
- method_params->Set(0, target.field->GetType<true>());
+ 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>());
+ 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>());
+ 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>());
+ method_params->Set(0, target_field->GetDeclaringClass());
+ return_type = hs.NewHandle(target_field->GetType<true>());
break;
}
- case DexFile::MethodHandleType::kInvokeStatic:
- case DexFile::MethodHandleType::kInvokeInstance: {
+ case DexFile::MethodHandleType::kInvokeDirect:
+ case DexFile::MethodHandleType::kInvokeInstance:
+ case DexFile::MethodHandleType::kInvokeInterface:
+ case DexFile::MethodHandleType::kInvokeStatic: {
// 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()) {
+ DexFileParameterIterator it(*dex_file, target_method->GetPrototype());
+ int32_t index = 0;
+ if (handle_type != DexFile::MethodHandleType::kInvokeStatic) {
+ method_params->Set(index++, target_method->GetDeclaringClass());
+ }
+ while (it.HasNext()) {
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);
+ method_params->Set(index++, klass);
+ it.Next();
}
- return_type = hs.NewHandle(target.method->GetReturnType(true));
+ return_type = hs.NewHandle(target_method->GetReturnType(true));
break;
}
case DexFile::MethodHandleType::kInvokeConstructor: {
@@ -8388,7 +8469,14 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandle(uint32_t method_handle_id
DCHECK(self->IsExceptionPending());
return nullptr;
}
- return mirror::MethodHandleImpl::Create(self, target.field_or_method, kind, mt);
+
+ uintptr_t target;
+ if (target_field != nullptr) {
+ target = reinterpret_cast<uintptr_t>(target_field);
+ } else {
+ target = reinterpret_cast<uintptr_t>(target_method);
+ }
+ return mirror::MethodHandleImpl::Create(self, target, kind, mt);
}
bool ClassLinker::IsQuickResolutionStub(const void* entry_point) const {
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 81a39afbee..eb3b210cd1 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -272,7 +272,9 @@ class DexFile {
// can be any non-static method on any class (or interface) except
// for “<init>”.
kInvokeConstructor = 0x0006, // an invoker for a given constructor.
- kLast = kInvokeConstructor
+ kInvokeDirect = 0x0007, // an invoker for a direct (special) method.
+ kInvokeInterface = 0x0008, // an invoker for an interface method.
+ kLast = kInvokeInterface
};
// raw method_handle_item
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index c18ab47739..c5c4eda98f 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -2483,7 +2483,9 @@ bool DexFileVerifier::CheckInterMethodHandleItem() {
}
case DexFile::MethodHandleType::kInvokeStatic:
case DexFile::MethodHandleType::kInvokeInstance:
- case DexFile::MethodHandleType::kInvokeConstructor: {
+ case DexFile::MethodHandleType::kInvokeConstructor:
+ case DexFile::MethodHandleType::kInvokeDirect:
+ case DexFile::MethodHandleType::kInvokeInterface: {
LOAD_METHOD(method, index, "method_handle_item method_idx", return false);
break;
}
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 1b36c3f12b..b57e2b2b40 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -640,7 +640,7 @@ static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self,
const DexFile* dex_file = referrer->GetDexFile();
const DexFile::CallSiteIdItem& csi = dex_file->GetCallSiteId(call_site_idx);
- StackHandleScope<9> hs(self);
+ StackHandleScope<10> hs(self);
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
@@ -836,9 +836,13 @@ static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self,
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());
+ // Check the target method type matches the method type requested modulo the receiver
+ // needs to be compatible rather than exact.
+ Handle<mirror::MethodType> target_method_type = hs.NewHandle(target->GetMethodType());
+ if (UNLIKELY(!target_method_type->IsExactMatch(method_type.Get()) &&
+ !IsParameterTypeConvertible(target_method_type->GetPTypes()->GetWithoutChecks(0),
+ method_type->GetPTypes()->GetWithoutChecks(0)))) {
+ ThrowWrongMethodTypeException(target_method_type.Get(), method_type.Get());
return nullptr;
}
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 9b652553df..efb02f6205 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -4159,7 +4159,8 @@ bool MethodVerifier::CheckCallSite(uint32_t call_site_idx) {
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";
+ << " argument 0 method handle type is not InvokeStatic: "
+ << mh.method_handle_type_;
return false;
}
diff --git a/test/952-invoke-custom-kinds/build b/test/952-invoke-custom-kinds/build
new file mode 100644
index 0000000000..a02cdc3769
--- /dev/null
+++ b/test/952-invoke-custom-kinds/build
@@ -0,0 +1,22 @@
+#!/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.
+
+# Stop if something fails.
+set -e
+
+${DX} --dex --min-sdk-version=26 --output=classes.dex classes
+
+zip $TEST_NAME.jar classes.dex
diff --git a/test/952-invoke-custom-kinds/classes/Main.class b/test/952-invoke-custom-kinds/classes/Main.class
new file mode 100644
index 0000000000..6bc04e326a
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/Main.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/Interface.class b/test/952-invoke-custom-kinds/classes/invokecustom/Interface.class
new file mode 100644
index 0000000000..5dfe958795
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/Interface.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InterfaceImplementor.class b/test/952-invoke-custom-kinds/classes/invokecustom/InterfaceImplementor.class
new file mode 100644
index 0000000000..a11ee696bf
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/InterfaceImplementor.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$Interface.class b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$Interface.class
new file mode 100644
index 0000000000..e233febbf4
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$Interface.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$InterfaceImplementor.class b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$InterfaceImplementor.class
new file mode 100644
index 0000000000..41e1d431f2
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$InterfaceImplementor.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.class b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.class
new file mode 100644
index 0000000000..4f0f497c72
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/Super.class b/test/952-invoke-custom-kinds/classes/invokecustom/Super.class
new file mode 100644
index 0000000000..5990f28d47
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/Super.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator$1.class b/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator$1.class
new file mode 100644
index 0000000000..c3266e49f7
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator$1.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.class b/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.class
new file mode 100644
index 0000000000..711db2343b
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/expected.txt b/test/952-invoke-custom-kinds/expected.txt
new file mode 100644
index 0000000000..c72da25c99
--- /dev/null
+++ b/test/952-invoke-custom-kinds/expected.txt
@@ -0,0 +1,38 @@
+bsmLookupStatic []
+Hello World!
+bsmLookupStatic []
+true
+127
+c
+1024
+123456
+1.2
+123456789
+3.5123456789
+String
+bsmLookupStaticWithExtraArgs [1, 123456789, 123.456, 123456.789123]
+targetMethodTest3 from InvokeCustom
+bsmCreateCallSite [MethodHandle(Super)void]
+targetMethodTest4 from Super
+bsmLookupStatic []
+targetMethodTest5 1000 + -923 = 77
+targetMethodTest5 returned: 77
+bsmLookupStatic []
+targetMethodTest6 8209686820727 + -1172812402961 = 7036874417766
+targetMethodTest6 returned: 7036874417766
+bsmLookupStatic []
+targetMethodTest7 0.50097656 * -0.50097656 = -0.2509775161743164
+targetMethodTest6 returned: -0.2509775161743164
+bsmLookupStatic []
+targetMethodTest8 First invokedynamic invocation
+bsmLookupStatic []
+targetMethodTest8 Second invokedynamic invocation
+bsmLookupStatic []
+targetMethodTest8 Dupe first invokedynamic invocation
+bsmLookupTest9 [MethodHandle()int, MethodHandle(int)void, MethodHandle(InvokeCustom)float, MethodHandle(InvokeCustom,float)void]
+targetMethodTest9 ()void
+checkStaticFieldTest9: old 0 new 1985229328 expected 1985229328 OK
+checkFieldTest9: old 0.0 new 1.99E-19 expected 1.99E-19 OK
+helperMethodTest9 in class invokecustom.InvokeCustom
+run() for Test9
+targetMethodTest9()
diff --git a/test/952-invoke-custom-kinds/info.txt b/test/952-invoke-custom-kinds/info.txt
new file mode 100644
index 0000000000..cddb96dcf0
--- /dev/null
+++ b/test/952-invoke-custom-kinds/info.txt
@@ -0,0 +1,7 @@
+This test checks call sites and constant method handles in DEX files used
+by invoke-custom.
+
+It is derived from a dx test (dalvik/dx/tests/135-invoke-custom) which
+generates the invoke-custom using ASM to generate class files. The
+test here differs as it not include an instance a constant method
+handle with a constructor kind (see b/62774190).