diff options
author | 2017-01-18 09:23:12 +0000 | |
---|---|---|
committer | 2017-02-14 14:04:33 +0000 | |
commit | c069a30d42aefd902c20e8bc09dfad1683f07ded (patch) | |
tree | 8bbf72bea7ea5d243b57f8e0ab64b687a9f60e4b | |
parent | 3f38398380b80d1ded078ebed1211b7e4f51460f (diff) |
ART: invoke-custom support
Adds invoke-custom instruction to the interpreter.
Bug: 33191717,30550796
Test: art/test/run-test --host 952
Change-Id: I3b754128649a8b3a00ade79ba2518d0e377f3a1e
55 files changed, 3173 insertions, 203 deletions
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index c72edb18a3..70ff327e72 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -2254,6 +2254,14 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache, 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 d5776fa61e..5656ddd59c 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -881,26 +881,30 @@ static std::unique_ptr<char[]> indexString(const DexFile* pDexFile, 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 @@ static void dumpClass(const DexFile* pDexFile, int idx, char** pLastPackage) { 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 @@ static void processDexFile(const char* fileName, 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 9a73830f99..b9be5f2605 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -688,6 +688,16 @@ void PatchOat::PatchDexFileArrays(mirror::ObjectArray<mirror::Object>* img_roots 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 9585ba2d8e..c871301052 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -123,6 +123,7 @@ cc_defaults { "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 @@ cc_defaults { "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 7db83688e2..110241bbb3 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 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b 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 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { 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 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( 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 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( 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 @@ ClassLinker::~ClassLinker() { 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 @@ void ClassLinker::LoadClassMembers(Thread* self, last_field_idx = field_idx; } } + // Load instance fields. LengthPrefixedArray<ArtField>* ifields = AllocArtFieldArray(self, allocator, @@ -3128,6 +3164,7 @@ void ClassLinker::LoadClassMembers(Thread* self, 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 @@ mirror::MethodType* ClassLinker::ResolveMethodType(const DexFile& dex_file, 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 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) { "[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 62d3c29a19..e27a53d15c 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -55,6 +55,8 @@ namespace mirror { 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 @@ class ClassLinker { kJavaLangReflectConstructorArrayClass, kJavaLangReflectFieldArrayClass, kJavaLangReflectMethodArrayClass, + kJavaLangInvokeCallSite, kJavaLangInvokeMethodHandleImpl, + kJavaLangInvokeMethodHandlesLookup, kJavaLangInvokeMethodType, kJavaLangClassLoader, kJavaLangThrowable, @@ -366,6 +370,12 @@ class ClassLinker { 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 03105cb6fb..ee8aed72cb 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 @@ struct DexCacheOffsets : public CheckOffsets<mirror::DexCache> { 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 MethodHandleImplOffsets : public CheckOffsets<mirror::MethodHandleImpl> { } }; +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 EmulatedStackFrameOffsets : public CheckOffsets<mirror::EmulatedStackFram } }; +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 @@ TEST_F(ClassLinkerTest, ValidateFieldOrderOfJavaCppUnionClasses) { 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 a44f79e193..4f4bed0169 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -126,6 +126,22 @@ void ThrowArrayStoreException(ObjPtr<mirror::Class> element_class, 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 76ea2ae6c8..55a89388ea 100644 --- a/runtime/common_throws.h +++ b/runtime/common_throws.h @@ -56,6 +56,14 @@ void ThrowArrayStoreException(ObjPtr<mirror::Class> element_class, 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 @@ void ThrowVerifyError(ObjPtr<mirror::Class> referrer, const char* fmt, ...) __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 cf90bca620..20bd52b060 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -264,11 +264,11 @@ class DexFile { // 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 @@ class DexFile { // 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 @@ class DexFile { 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 d870e52a52..0b3f16a3cb 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -2374,10 +2374,10 @@ bool DexFileVerifier::CheckInterMethodHandleItem() { 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 37f3ac92e9..091085a85c 100644 --- a/runtime/dex_instruction.cc +++ b/runtime/dex_instruction.cc @@ -407,6 +407,20 @@ std::string Instruction::DumpString(const DexFile* file) const { 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 @@ std::string Instruction::DumpString(const DexFile* file) const { 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 @@ std::string Instruction::DumpString(const DexFile* file) const { 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 @@ std::string Instruction::DumpString(const DexFile* file) const { 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 578550cae2..d269110570 100644 --- a/runtime/dex_instruction.h +++ b/runtime/dex_instruction.h @@ -126,14 +126,15 @@ class Instruction { 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 @@ class Instruction { }; 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 ca2ce1d990..a5ce3c2f8a 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 3ef47c427e..c2bca5305d 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -2435,8 +2435,8 @@ extern "C" uintptr_t artInvokePolymorphic( // 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 @@ extern "C" uintptr_t artInvokePolymorphic( // 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 442a42ef30..55bd1d4736 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1283,6 +1283,14 @@ class ImageSpaceLoader { } 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 28bcb97105..3d4a2d1bb8 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -519,7 +519,7 @@ void SetStringInitValueToAllAliases(ShadowFrame* shadow_frame, } } -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 @@ bool DoInvokePolymorphic(Thread* self, // 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 @@ bool DoInvokePolymorphic(Thread* self, // 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, false); 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 7ef3508164..6b22af9829 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 @@ static inline bool DoInvoke(Thread* self, } // 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 a77a3fc2b3..b191dd79a1 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -1524,7 +1524,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, 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 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, 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 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, 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 99886e5c2f..9825ad680f 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -555,7 +555,7 @@ static inline bool DoCallTransform(ArtMethod* called_method, 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 @@ inline static ObjPtr<mirror::Class> GetAndInitializeDeclaringClass(Thread* self, 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 @@ inline static void DoFieldGetForInvokePolymorphic(Thread* self, } // 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 @@ inline bool DoFieldPutForInvokePolymorphic(Thread* self, 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 @@ static JValue GetValueFromShadowFrame(const ShadowFrame& shadow_frame, 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 @@ bool DoInvokePolymorphicFieldAccess(Thread* self, 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 @@ bool DoInvokePolymorphicFieldAccess(Thread* self, 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 @@ bool DoInvokePolymorphicFieldAccess(Thread* self, } } -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 @@ static inline bool DoInvokePolymorphicNonExact(Thread* self, 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 @@ static inline bool DoInvokePolymorphicNonExact(Thread* self, 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 @@ static inline bool DoInvokePolymorphicNonExact(Thread* self, } } -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 @@ bool DoInvokePolymorphicExact(Thread* self, 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 @@ bool DoInvokePolymorphicExact(Thread* self, 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 @@ bool DoInvokePolymorphicExact(Thread* self, } // 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) - -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); +#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); +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 734d7c7bf4..5bea0ab5cd 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 @@ class ShadowFrameSetter { 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 0000000000..eb613df4c6 --- /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 0000000000..db244a5442 --- /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 a59bb7b880..973c8ed07d 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 @@ inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* reso 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 @@ inline void DexCache::VisitReferences(ObjPtr<Class> klass, const Visitor& visito 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 @@ inline void DexCache::FixupResolvedMethodTypes(mirror::MethodTypeDexCacheType* d } } +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 741cf3bb47..0f6acab7e1 100644 --- a/runtime/mirror/dex_cache.cc +++ b/runtime/mirror/dex_cache.cc @@ -90,6 +90,10 @@ void DexCache::InitializeDexCache(Thread* self, 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 @@ void DexCache::InitializeDexCache(Thread* self, 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 @@ void DexCache::InitializeDexCache(Thread* self, dex_file->NumFieldIds(), method_types, num_method_types, + call_sites, + dex_file->NumCallSiteIds(), image_pointer_size); } @@ -151,6 +160,8 @@ void DexCache::Init(const DexFile* dex_file, 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 @@ void DexCache::Init(const DexFile* dex_file, 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 @@ void DexCache::Init(const DexFile* dex_file, 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 6f88cc5df4..10bb5aa01c 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -36,6 +36,7 @@ class Thread; namespace mirror { +class CallSite; class MethodType; class String; @@ -163,6 +164,10 @@ class MANAGED DexCache FINAL : public Object { 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 @@ class MANAGED DexCache FINAL : public Object { 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 @@ class MANAGED DexCache FINAL : public Object { 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 @@ class MANAGED DexCache FINAL : public Object { 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 @@ class MANAGED DexCache FINAL : public Object { 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 @@ class MANAGED DexCache FINAL : public Object { 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 @@ class MANAGED DexCache FINAL : public Object { 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 @@ class MANAGED DexCache FINAL : public Object { 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 @@ class MANAGED DexCache FINAL : public Object { 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 4f1c448b56..fa4d25a031 100644 --- a/runtime/mirror/method_handle_impl.cc +++ b/runtime/mirror/method_handle_impl.cc @@ -28,6 +28,18 @@ mirror::Class* MethodHandle::StaticClass() { 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 @@ void MethodHandleImpl::VisitRoots(RootVisitor* visitor) { 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 53d267b52c..9938af8abf 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 @@ class MANAGED MethodHandle : public Object { 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 @@ class MANAGED MethodHandle : public Object { 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 @@ class MANAGED MethodHandle : public Object { // 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 0000000000..c758e54dd4 --- /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 0000000000..63eb428f94 --- /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 5d77a16e7d..4b8dfacec6 100644 --- a/runtime/mirror/method_type.cc +++ b/runtime/mirror/method_type.cc @@ -44,6 +44,22 @@ mirror::MethodType* MethodType::Create(Thread* const self, 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 9a98143144..374bbe5df3 100644 --- a/runtime/mirror/method_type.h +++ b/runtime/mirror/method_type.h @@ -44,6 +44,10 @@ class MANAGED MethodType : public Object { 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 31eb1ccdc8..528eddc306 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -273,6 +273,36 @@ inline static bool ReadOatDexFileData(const OatFile& oat_file, 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 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { 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 9609bee022..f8f3d766c0 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 @@ void Runtime::VisitConstantRoots(RootVisitor* visitor) { 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 67438b5881..96e5bfa8ec 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -301,6 +301,30 @@ constexpr PointerSize ConvertToPointerSize(T any) { } } +// 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 bd1b044dae..98658215f7 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 @@ inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size, 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::MethodTypesSize(size_t num_elements) const { 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 7d4b23a8dd..ed677ed3f4 100644 --- a/runtime/utils/dex_cache_arrays_layout.h +++ b/runtime/utils/dex_cache_arrays_layout.h @@ -37,11 +37,14 @@ class DexCacheArraysLayout { 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 @@ class DexCacheArraysLayout { 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 @@ class DexCacheArraysLayout { 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 02f1e1bbfe..634bd47f05 100644 --- a/runtime/utils_test.cc +++ b/runtime/utils_test.cc @@ -408,4 +408,23 @@ TEST_F(UtilsTest, IsValidDescriptor) { 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 5f55f3fd29..16739fa3bc 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3114,6 +3114,44 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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(¶m_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(®_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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { /* 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 @@ void MethodVerifier::VerifyInvocationArgsUnresolvedMethod(const Instruction* ins 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 fa5a698423..7b67967c28 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -697,6 +697,11 @@ class MethodVerifier { 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 0000000000..a423ca6b4e --- /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 0000000000..bb87296e60 --- /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 0000000000..5d5cae4147 --- /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 0000000000..93d96a9a7b --- /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 0000000000..4e4d97e15e --- /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 0000000000..90a60e6f8f --- /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 0000000000..e9a9f6c4a5 --- /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 0000000000..2abc3122fa --- /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 0000000000..9a77e67e49 --- /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" + + "AAAYAAAAIwAAACQAAAAmAAAAJwAAACgAAAApAAAAKgAAACsAAAAsAAAALQAAAC4AAAAvAAAA" + + "MAAAADEAAAAyAAAAMwAAADQAAAA1AAAANgAAADgAAAA5AAAAOgAAADsAAAA8AAAAPQAAAD4A" + + "AAA/AAAAQAAAAEIAAABGAAAAVAAAAFYAAABXAAAAWAAAAFkAAABaAAAAFQAAAAQAAAAAAAAA" + + "FgAAAAQAAADcEAAAIQAAABAAAADkEAAAGQAAABMAAAAAAAAAHQAAABMAAADcEAAAGQAAABQA" + + "AAAAAAAAGQAAABUAAAAAAAAAGgAAABYAAADsEAAAGwAAABYAAAD0EAAAHAAAABYAAAD8EAAA" + + "HQAAABYAAADcEAAAHgAAABYAAAAEEQAAHwAAABYAAAAMEQAAHwAAABYAAAAUEQAAJQAAABYA" + + "AAAcEQAAIgAAABsAAAAkEQAAIgAAAB0AAAAwEQAAIAAAAB0AAAA8EQAAIAAAAB0AAABIEQAA" + + "GQAAACAAAAAAAAAAGQAAACEAAAAAAAAARgAAACMAAAAAAAAARwAAACMAAABUEQAASAAAACMA" + + "AABcEQAASQAAACMAAABkEQAASgAAACMAAABsEQAASwAAACMAAADcEAAATAAAACMAAAB0EQAA" + + "TQAAACMAAAAEEQAATgAAACMAAAB8EQAATwAAACMAAAAMEQAAUAAAACMAAACEEQAATwAAACMA" + + "AAAUEQAAUAAAACMAAACMEQAATwAAACMAAACUEQAAUQAAACMAAACcEQAAUgAAACMAAAAcEQAA" + + "VQAAACQAAAAMEQAABwAEAEEAAAAHACkAbwAAAAcAKACEAAAABwAhAI8AAAAHACkAngAAAAcA" + + "GQChAAAACgAKABcAAAATABIAQwAAABcAEACSAAAABgAVAA4AAAAGAAMAggAAAAYABQCCAAAA" + + "BwAUAAsAAAAHABUADQAAAAcAFQAOAAAABwAWAF4AAAAHABcAXgAAAAcAGABeAAAABwAZAF4A" + + "AAAHABsAXgAAAAcAHQBeAAAABwAfAF4AAAAHACEAXgAAAAcAIwBeAAAABwAfAGcAAAAHACQA" + + "aQAAAAcAAAB9AAAABwAPAIsAAAAHAAEAkAAAAAcAFQCXAAAABwABAJsAAAAHABUAnwAAABAA" + + "AgB6AAAAEAAgAJUAAAARAB4ADgAAABMAAACFAAAAEwAEAKYAAAAUACUAdgAAABUAJQB2AAAA" + + "FgAVAA4AAAAWAAcAXAAAABYACABcAAAAFgAJAFwAAAAWAAoAXAAAABYACwBcAAAAFgAMAFwA" + + "AAAWAA0AXAAAABYADgBcAAAAFgAGAKMAAAAYABUADgAAABgAFQCHAAAAGAAcAJwAAAAYABUA" + + "nQAAABkAFQAOAAAAGQAFAHsAAAAcACIADgAAAB0AEwCkAAAAHgAQAHkAAAAfABEAcwAAAB8A" + + "EgCDAAAAIAAAAJQAAAAhABoADgAAACEAAAB7AAAAIQAAAHwAAAAhABoAmgAAAFMcAAAGAAAA" + + "EAAAABkAAAAAAAAARAAAAJwQAABaHAAAAAAAAAcAAAABAAAAGAAAAAAAAABEAAAArBAAAG0c" + + "AABQHAAABAAAABIAAAADAAAA9BsAAPsbAAAEHAAAAQAAABMcAAABAAAABBwAAAEAAAAcHAAA" + + "AQAAACUcAAABAAEAAQAAANUaAAAEAAAAcBAsAAAADgACAAEAAQAAAN0aAAANAAAAcQADAAAA" + + "DABuEDYAAAAKAHEQGwAAAAwAEQAAAAIAAQABAAAA4hoAAAUAAABuEAEAAQAMABEAAAABAAAA" + + "AAAAAAAAAAADAAAAYgADABEAAAADAAAAAgAAAOcaAAAeAAAAEwIQACIAIQASAXAgNAAQAGkA" + + "AwAiAAYAcBAAAAAAaQAFACMgKABpAAIAIyApAGkAAQAjICkAaQAEAA4AAQABAAEAAAD0GgAA" + + "BAAAAHAQKAAAAA4ABQACAAIAAAD5GgAAKAAAADNDAwAOACIAEQAiARYAcBAeAAEAGwJfAAAA" + + "biAlACEADAFuICIAMQAMARsCAwAAAG4gJQAhAAwBbiAiAEEADAFuECcAAQAMAXAgGQAQACcA" + + "BQACAAIAAAACGwAAKAAAADNDAwAOACIAEQAiARYAcBAeAAEAGwJgAAAAbiAlACEADAFuIB8A" + + "MQAMARsCBAAAAG4gJQAhAAwBbiAfAEEADAFuECcAAQAMAXAgGQAQACcACAAEAAMAAAALGwAA" + + "KgAAAC8ABAY5AAMADgAiABEAIgEWAHAQHgABABsCYQAAAG4gJQAhAAwBbjAgAEEFDAEbAgUA" + + "AABuICUAIQAMAW4wIABhBwwBbhAnAAEADAFwIBkAEAAnAAUAAgACAAAAFBsAACoAAAAtAAME" + + "OQADAA4AIgARACIBFgBwEB4AAQAbAmIAAABuICUAIQAMAW4gIQAxAAwBGwIGAAAAbiAlACEA" + + "DAFuICEAQQAMAW4QJwABAAwBcCAZABAAJwAFAAIAAgAAAB0bAAAoAAAAM0MDAA4AIgARACIB" + + "FgBwEB4AAQAbAmMAAABuICUAIQAMAW4gIgAxAAwBGwIHAAAAbiAlACEADAFuICIAQQAMAW4Q" + + "JwABAAwBcCAZABAAJwAIAAQAAwAAACgbAAAqAAAAMQAEBjkAAwAOACIAEQAiARYAcBAeAAEA" + + "GwJkAAAAbiAlACEADAFuMCMAQQUMARsCCAAAAG4gJQAhAAwBbjAjAGEHDAFuECcAAQAMAXAg" + + "GQAQACcABQACAAIAAAAzGwAAMwAAADNDAwAOADgDCwA4BAkAbiAcAEMACgA4AAMADgAiABEA" + + "IgEWAHAQHgABABsCZgAAAG4gJQAhAAwBbiAkADEADAEbAgkAAABuICUAIQAMAW4gJABBAAwB" + + "bhAnAAEADAFwIBkAEAAnAAAABQACAAIAAAA/GwAAMwAAADNDAwAOADgDCwA4BAkAbiAdAEMA" + + "CgA4AAMADgAiABEAIgEWAHAQHgABABsCZQAAAG4gJQAhAAwBbiAlADEADAEbAgoAAABuICUA" + + "IQAMAW4gJQBBAAwBbhAnAAEADAFwIBkAEAAnAAAABQACAAIAAABNGwAAKAAAADNDAwAOACIA" + + "EQAiARYAcBAeAAEAGwJlAAAAbiAlACEADAFuICIAMQAMARsCCgAAAG4gJQAhAAwBbiAiAEEA" + + "DAFuECcAAQAMAXAgGQAQACcABQACAAIAAABYGwAANQAAADJDAwAOADgDDQA4BAsAbiAcAEMA" + + "CgDfAAABOAADAA4AIgARACIBFgBwEB4AAQAbAmgAAABuICUAIQAMAW4gJAAxAAwBGwIJAAAA" + + "biAlACEADAFuICQAQQAMAW4QJwABAAwBcCAZABAAJwAAAAQAAQACAAAAZBsAAB0AAAA5AxwA" + + "IgARACIBFgBwEB4AAQAbAmoAAABuICUAIQAMAW4gJgAxAAwBbhAnAAEADAFwIBkAEAAnAA4A" + + "AAABAAAAAQAAAHAbAAANAAAAYgAFAG4QLQAAAAwAHwATAG4QGgAAAAoADwAAAAkAAwAEAAAA" + + "dRsAAGEAAAASFRIEcQARAAAACgEcAgcAbkAwACaHDABuEC8AAAAMAnEgDAAoAG4QLwAAAAwC" + + "bhAzAAIACgJxIAoAUgAjUiYAcRAbAAEADANNAwIEcTAyAEACDAAjUiUAYgMHAE0DAgRxMDEA" + + "QAIMAG4QLwAAAAwCbhAzAAIACgJxIAoAUgBuEC8AAAAMAnEgDAAoABYCfQBxICoAMgBiAgIA" + + "cQARAAAACgMiBBwAcCAuAAQATQQCA2ICAgBxABEAAAAKA0YCAgMRAgAAAQABAAAAAACTGwAA" + + "AQAAAA8AAAADAAEAAgAAAJobAAAUAAAAYgABAEYAAAJuEDYAAABiAAQAcQARAAAACgFGAAAB" + + "biA3ACAAEgAPAAwAAAADAAAAoxsAAOgAAAASKxIaEgkTCBAAEgA1gBcAYgQBACIFIQBwIDQA" + + "lQBNBQQAYgQEACIFIQBwIDQAlQBNBQQA2AAAASjqI4EnABIANYARACIEBwBwEAUABABNBAEA" + + "RgQBAG4QKwAEANgAAAEo8BIANYAKAEYEAQBuECkABADYAAABKPcSAxICEgA1gCIAYgQCAEYE" + + "BAASBXEgDwBUAGIEAQBGBAQAbhA1AAQACgQ4BA0A2AMDAWIEAQBGBAQAbhA1AAQACgSwQtgA" + + "AAEo32IECAAiBRYAcBAeAAUAGwZTAAAAbiAlAGUADAVuICIANQAMBRsGAgAAAG4gJQBlAAwF" + + "biAiACUADAVuECcABQAMBW4gGABUADKjLgBiBAgAGwVFAAAAbiAYAFQAEgA1gCMAYgQIABsF" + + "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 0000000000..443a7af187 --- /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" + + "KAAAADNDAwAOACIADQAiARIAcBAWAAEAGwJPAAAAbiAdACEADAFuIBoAMQAMARsCAwAAAG4g" + + "HQAhAAwBbiAaAEEADAFuEB8AAQAMAXAgEQAQACcABQACAAIAAACHEwAAKAAAADNDAwAOACIA" + + "DQAiARIAcBAWAAEAGwJQAAAAbiAdACEADAFuIBcAMQAMARsCBAAAAG4gHQAhAAwBbiAXAEEA" + + "DAFuEB8AAQAMAXAgEQAQACcACAAEAAMAAACPEwAAKgAAAC8ABAY5AAMADgAiAA0AIgESAHAQ" + + "FgABABsCUQAAAG4gHQAhAAwBbjAYAEEFDAEbAgUAAABuIB0AIQAMAW4wGABhBwwBbhAfAAEA" + + "DAFwIBEAEAAnAAUAAgACAAAAlxMAACoAAAAtAAMEOQADAA4AIgANACIBEgBwEBYAAQAbAlIA" + + "AABuIB0AIQAMAW4gGQAxAAwBGwIGAAAAbiAdACEADAFuIBkAQQAMAW4QHwABAAwBcCARABAA" + + "JwAFAAIAAgAAAJ8TAAAoAAAAM0MDAA4AIgANACIBEgBwEBYAAQAbAlMAAABuIB0AIQAMAW4g" + + "GgAxAAwBGwIHAAAAbiAdACEADAFuIBoAQQAMAW4QHwABAAwBcCARABAAJwAIAAQAAwAAAKcT" + + "AAAqAAAAMQAEBjkAAwAOACIADQAiARIAcBAWAAEAGwJUAAAAbiAdACEADAFuMBsAQQUMARsC" + + "CAAAAG4gHQAhAAwBbjAbAGEHDAFuEB8AAQAMAXAgEQAQACcABQACAAIAAACvEwAAMwAAADND" + + "AwAOADgDCwA4BAkAbiAUAEMACgA4AAMADgAiAA0AIgESAHAQFgABABsCVgAAAG4gHQAhAAwB" + + "biAcADEADAEbAgkAAABuIB0AIQAMAW4gHABBAAwBbhAfAAEADAFwIBEAEAAnAAAABQACAAIA" + + "AAC4EwAAMwAAADNDAwAOADgDCwA4BAkAbiAVAEMACgA4AAMADgAiAA0AIgESAHAQFgABABsC" + + "VQAAAG4gHQAhAAwBbiAdADEADAEbAgoAAABuIB0AIQAMAW4gHQBBAAwBbhAfAAEADAFwIBEA" + + "EAAnAAAABQACAAIAAADDEwAAKAAAADNDAwAOACIADQAiARIAcBAWAAEAGwJVAAAAbiAdACEA" + + "DAFuIBoAMQAMARsCCgAAAG4gHQAhAAwBbiAaAEEADAFuEB8AAQAMAXAgEQAQACcABAABAAIA" + + "AADLEwAAHQAAADkDHAAiAA0AIgESAHAQFgABABsCWAAAAG4gHQAhAAwBbiAeADEADAFuEB8A" + + "AQAMAXAgEQAQACcADgAAAAcAAwAEAAAA1RMAAGoAAABiAQYAIgISAHAQFgACABsDcAAAAG4g" + + "HQAyAAwCYAMEAG4gGgAyAAwCbhAfAAIADAJuIA8AIQAcAQYAbkAhABRlDABgAQQAYAIAADMh" + + "KABiAQYAIgISAHAQFgACABsDNwAAAG4gHQAyAAwCbiAdAFIADAIbAwAAAABuIB0AMgAMAm4g" + + "HABiAAwCbhAfAAIADAJuIA8AIQASAREBYAEEAGACAQAzIRMAYgEGABsCOwAAAG4gDwAhACIB" + + "DwAbAm8AAABwIBIAIQAnASIBFgBwICAAAQARAQYAAwACAAAA7RMAAFAAAAASERICYAACADQD" + + "SAABEHEQDAAAAGAAAwA2A0IAcRAMAAEAZwMEAJAABAX8IAAAVAAKAXEgBwAQAGIABgAiARIA" + + "cBAWAAEAGwIVAAAAbiAdACEADAFuIBoAMQAMARsCAQAAAG4gHQAhAAwBbiAaAEEADAFuIBoA" + + "UQAMARsCAgAAAG4gHQAhAAwBbhAfAAEADAFuIA8AEAAOAAEgKLoBISi/AAAAAAAAAAADAAAA" + + "AAAAAAIAAACsBQAADQAAALQFAAAOAAAAtAUAAAIAAAAEAAQAAQAAAAEAAAABAAAAAgAAAAEA" + + "AAADAAAAAQAAAAQAAAABAAAABQAAAAEAAAAQAAAAAQAAABEAAAABAAAAHAAAAAMAAAAYABEA" + + "GQAAAAMAAAAOABEAGQAAAAIAAAAAAAAAAgAAAAEAAQACAAAAAgACAAIAAAADAAMAAwAAAAQA" + + "BAAEAAAAAgAAAAUABQACAAAAEAAQAAIAAAARABEAAQAAABcAAAACAAAAGgAaAAEgAAIgKAAB" + + "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 0000000000..b96e18486f --- /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 Binary files differnew file mode 100644 index 0000000000..67261cad79 --- /dev/null +++ b/test/dexdump/invoke-custom.dex diff --git a/test/dexdump/invoke-custom.lst b/test/dexdump/invoke-custom.lst new file mode 100644 index 0000000000..3540bd10d5 --- /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 0000000000..e92549a086 --- /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 0000000000..2a2966738a --- /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> |