diff options
author | 2017-09-22 16:17:41 +0100 | |
---|---|---|
committer | 2017-10-02 16:08:18 +0100 | |
commit | 2e59994c3c586d8b753e4b14a94c81bce3dba1a9 (patch) | |
tree | ab749ce709732821c7c00c81eb76afef032fd888 | |
parent | 865a0503515f78611dce103bd04b7c66c051d304 (diff) |
ART: support for const-method-{handle,type}
Adds support for const-method-handle and const-method-type bytecodes.
Bug: 66562269
Test: art/test/run-test --host 979
Test: art/test/dexdump/run-all-tests
Change-Id: I812eaf0f2439d6126b4287483fe2348366cacf90
23 files changed, 608 insertions, 18 deletions
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 0db790b47a..7599d230d2 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -906,14 +906,27 @@ static std::unique_ptr<char[]> indexString(const DexFile* pDexFile, // 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: - // case Instruction::kIndexInlineMethod: - default: - outSize = snprintf(buf.get(), bufSize, "<?>"); + case Instruction::kIndexMethodHandleRef: + // Method handle information is too large to detail in disassembly so just output the index. + outSize = snprintf(buf.get(), bufSize, "method_handle@%0*x", width, index); + break; + case Instruction::kIndexProtoRef: + if (index < pDexFile->GetHeader().proto_ids_size_) { + const DexFile::ProtoId& protoId = pDexFile->GetProtoId(index); + const Signature signature = pDexFile->GetProtoSignature(protoId); + const std::string& proto = signature.ToString(); + outSize = snprintf(buf.get(), bufSize, "%s // proto@%0*x", proto.c_str(), width, index); + } else { + outSize = snprintf(buf.get(), bufSize, "<?> // proto@%0*x", width, index); + } break; } // switch + if (outSize == 0) { + // The index type has not been handled in the switch above. + outSize = snprintf(buf.get(), bufSize, "<?>"); + } + // Determine success of string construction. if (outSize >= bufSize) { // The buffer wasn't big enough; retry with computed size. Note: snprintf() diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 772f0420fd..eeb5569995 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -8033,6 +8033,15 @@ mirror::MethodType* ClassLinker::ResolveMethodType(const DexFile& dex_file, return type.Get(); } +mirror::MethodType* ClassLinker::ResolveMethodType(uint32_t proto_idx, ArtMethod* referrer) { + Thread* const self = Thread::Current(); + StackHandleScope<2> hs(self); + const DexFile* dex_file = referrer->GetDexFile(); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader())); + return ResolveMethodType(*dex_file, proto_idx, dex_cache, class_loader); +} + mirror::MethodHandle* ClassLinker::ResolveMethodHandleForField( Thread* self, const DexFile::MethodHandleItem& method_handle, diff --git a/runtime/class_linker.h b/runtime/class_linker.h index f97433c0b7..e436b99c4d 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -371,6 +371,9 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); + mirror::MethodType* ResolveMethodType(uint32_t proto_idx, ArtMethod* referrer) + REQUIRES_SHARED(Locks::mutator_lock_); + // 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. diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index be157a3f4c..2e776b0e61 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -66,8 +66,10 @@ const uint8_t DexFile::kDexMagicVersions[DexFile::kNumDexVersions][DexFile::kDex // Dex version 036 skipped because of an old dalvik bug on some versions of android where dex // files with that version number would erroneously be accepted and run. {'0', '3', '7', '\0'}, - // Dex version 038: Android "O" and beyond. - {'0', '3', '8', '\0'} + // Dex version 038: Android "O". + {'0', '3', '8', '\0'}, + // Dex verion 039: Beyond Android "O". + {'0', '3', '9', '\0'}, }; uint32_t DexFile::CalculateChecksum() const { diff --git a/runtime/dex_file.h b/runtime/dex_file.h index ac91d52a45..9c5fd10a36 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -47,7 +47,7 @@ class DexFile { static const uint32_t kClassDefinitionOrderEnforcedVersion = 37; static const uint8_t kDexMagic[]; - static constexpr size_t kNumDexVersions = 3; + static constexpr size_t kNumDexVersions = 4; static constexpr size_t kDexVersionLen = 4; static const uint8_t kDexMagicVersions[kNumDexVersions][kDexVersionLen]; diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index a7bf59eb29..67cd42803d 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -75,7 +75,7 @@ static const char kRawDex[] = "AAACAAAAQAEAAAEgAAACAAAAVAEAAAYgAAACAAAAiAEAAAEQAAABAAAAqAEAAAIgAAAPAAAArgEA" "AAMgAAACAAAAiAIAAAQgAAADAAAAlAIAAAAgAAACAAAAqwIAAAAQAAABAAAAxAIAAA=="; -// kRawDex38 and 39 are dex'ed versions of the following Java source : +// kRawDex{38,39,40,41} are dex'ed versions of the following Java source : // // public class Main { // public static void main(String[] foo) { @@ -108,6 +108,30 @@ static const char kRawDex39[] = "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC" "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA=="; +static const char kRawDex40[] = + "ZGV4CjA0MAC4OovJlJ1089ikzK6asMf/f8qp3Kve5VsgAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAI" + "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAwAQAA8AAAACIB" + "AAAqAQAAMgEAAEYBAABRAQAAVAEAAFgBAABtAQAAAQAAAAIAAAAEAAAABgAAAAQAAAACAAAAAAAA" + "AAUAAAACAAAAHAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAADAAAA" + "AAAAAH4BAAAAAAAAAQABAAEAAABzAQAABAAAAHAQAgAAAA4AAQABAAAAAAB4AQAAAQAAAA4AAAAB" + "AAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJW" + "TAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEbWFpbgABAAcOAAMBAAcOAAAAAgAAgYAE8AEBCYgCDAAA" + "AAAAAAABAAAAAAAAAAEAAAAIAAAAcAAAAAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAA" + "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC" + "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA=="; + +static const char kRawDex41[] = + "ZGV4CjA0MQC4OovJlJ1089ikzK6asMf/f8qp3Kve5VsgAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAI" + "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAwAQAA8AAAACIB" + "AAAqAQAAMgEAAEYBAABRAQAAVAEAAFgBAABtAQAAAQAAAAIAAAAEAAAABgAAAAQAAAACAAAAAAAA" + "AAUAAAACAAAAHAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAADAAAA" + "AAAAAH4BAAAAAAAAAQABAAEAAABzAQAABAAAAHAQAgAAAA4AAQABAAAAAAB4AQAAAQAAAA4AAAAB" + "AAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJW" + "TAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEbWFpbgABAAcOAAMBAAcOAAAAAgAAgYAE8AEBCYgCDAAA" + "AAAAAAABAAAAAAAAAAEAAAAIAAAAcAAAAAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAA" + "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC" + "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA=="; + static const char kRawDexZeroLength[] = "UEsDBAoAAAAAAOhxAkkAAAAAAAAAAAAAAAALABwAY2xhc3Nlcy5kZXhVVAkAA2QNoVdnDaFXdXgL" "AAEE5AMBAASIEwAAUEsBAh4DCgAAAAAA6HECSQAAAAAAAAAAAAAAAAsAGAAAAAAAAAAAAKCBAAAA" @@ -323,10 +347,31 @@ TEST_F(DexFileTest, Version38Accepted) { EXPECT_EQ(38u, header.GetVersion()); } -TEST_F(DexFileTest, Version39Rejected) { +TEST_F(DexFileTest, Version39Accepted) { + ScratchFile tmp; + std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex39, tmp.GetFilename().c_str())); + ASSERT_TRUE(raw.get() != nullptr); + + const DexFile::Header& header = raw->GetHeader(); + EXPECT_EQ(39u, header.GetVersion()); +} + +TEST_F(DexFileTest, Version40Rejected) { + ScratchFile tmp; + const char* location = tmp.GetFilename().c_str(); + DecodeAndWriteDexFile(kRawDex40, location); + + ScopedObjectAccess soa(Thread::Current()); + static constexpr bool kVerifyChecksum = true; + std::string error_msg; + std::vector<std::unique_ptr<const DexFile>> dex_files; + ASSERT_FALSE(DexFile::Open(location, location, kVerifyChecksum, &error_msg, &dex_files)); +} + +TEST_F(DexFileTest, Version41Rejected) { ScratchFile tmp; const char* location = tmp.GetFilename().c_str(); - DecodeAndWriteDexFile(kRawDex39, location); + DecodeAndWriteDexFile(kRawDex41, location); ScopedObjectAccess soa(Thread::Current()); static constexpr bool kVerifyChecksum = true; diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h index 9a1757601c..2f28dffa2b 100644 --- a/runtime/dex_instruction.h +++ b/runtime/dex_instruction.h @@ -135,6 +135,8 @@ class Instruction { kIndexVtableOffset, // vtable offset (for static linked methods) kIndexMethodAndProtoRef, // method and a proto reference index (for invoke-polymorphic) kIndexCallSiteRef, // call site reference index + kIndexMethodHandleRef, // constant method handle reference index + kIndexProtoRef, // prototype reference index }; enum Flags : uint8_t { @@ -195,7 +197,9 @@ class Instruction { kVerifyRuntimeOnly = 0x0200000, kVerifyError = 0x0400000, kVerifyRegHPrototype = 0x0800000, - kVerifyRegBCallSite = 0x1000000 + kVerifyRegBCallSite = 0x1000000, + kVerifyRegBMethodHandle = 0x2000000, + kVerifyRegBPrototype = 0x4000000, }; // Collect the enums in a struct for better locality. diff --git a/runtime/dex_instruction_list.h b/runtime/dex_instruction_list.h index d0a4ae51a7..ef83bdc216 100644 --- a/runtime/dex_instruction_list.h +++ b/runtime/dex_instruction_list.h @@ -273,8 +273,8 @@ V(0xFB, INVOKE_POLYMORPHIC_RANGE, "invoke-polymorphic/range", k4rcc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero | kVerifyRegHPrototype) \ V(0xFC, INVOKE_CUSTOM, "invoke-custom", k35c, kIndexCallSiteRef, kContinue | kThrow, 0, kVerifyRegBCallSite | kVerifyVarArg) \ V(0xFD, INVOKE_CUSTOM_RANGE, "invoke-custom/range", k3rc, kIndexCallSiteRef, kContinue | kThrow, 0, kVerifyRegBCallSite | kVerifyVarArgRange) \ - V(0xFE, UNUSED_FE, "unused-fe", k10x, kIndexUnknown, 0, 0, kVerifyError) \ - V(0xFF, UNUSED_FF, "unused-ff", k10x, kIndexUnknown, 0, 0, kVerifyError) + V(0xFE, CONST_METHOD_HANDLE, "const-method-handle", k21c, kIndexMethodHandleRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBMethodHandle) \ + V(0xFF, CONST_METHOD_TYPE, "const-method-type", k21c, kIndexProtoRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBPrototype) #define DEX_INSTRUCTION_FORMAT_LIST(V) \ V(k10x) \ diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 50bd7e73cd..fafb2a36f7 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -202,6 +202,25 @@ static inline bool DoInvoke(Thread* self, } } +static inline mirror::MethodHandle* ResolveMethodHandle(uint32_t method_handle_index, + ArtMethod* referrer) + REQUIRES_SHARED(Locks::mutator_lock_) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + return class_linker->ResolveMethodHandle(method_handle_index, referrer); +} + +static inline mirror::MethodType* ResolveMethodType(Thread* self, + uint32_t method_type_index, + ArtMethod* referrer) + REQUIRES_SHARED(Locks::mutator_lock_) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + const DexFile* dex_file = referrer->GetDexFile(); + StackHandleScope<2> hs(self); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader())); + return class_linker->ResolveMethodType(*dex_file, method_type_index, dex_cache, class_loader); +} + // Performs a signature polymorphic invoke (invoke-polymorphic/invoke-polymorphic-range). template<bool is_range> bool DoInvokePolymorphic(Thread* self, diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 850419bd9d..2514f0c383 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -525,6 +525,30 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } break; } + case Instruction::CONST_METHOD_HANDLE: { + PREAMBLE(); + ObjPtr<mirror::MethodHandle> mh = + Runtime::Current()->GetClassLinker()->ResolveMethodHandle(inst->VRegB_21c(), method); + if (UNLIKELY(mh == nullptr)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mh.Ptr()); + inst = inst->Next_2xx(); + } + break; + } + case Instruction::CONST_METHOD_TYPE: { + PREAMBLE(); + ObjPtr<mirror::MethodType> mt = + Runtime::Current()->GetClassLinker()->ResolveMethodType(inst->VRegB_21c(), method); + if (UNLIKELY(mt == nullptr)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mt.Ptr()); + inst = inst->Next_2xx(); + } + break; + } case Instruction::MONITOR_ENTER: { PREAMBLE(); ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); @@ -2435,10 +2459,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, inst = inst->Next_2xx(); break; case Instruction::UNUSED_3E ... Instruction::UNUSED_43: + case Instruction::UNUSED_79 ... Instruction::UNUSED_7A: case Instruction::UNUSED_F3 ... Instruction::UNUSED_F9: - case Instruction::UNUSED_FE ... Instruction::UNUSED_FF: - case Instruction::UNUSED_79: - case Instruction::UNUSED_7A: UnexpectedOpcode(inst, shadow_frame); } } while (!interpret_one_instruction); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index ee428edf28..7246bae4cb 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -43,6 +43,7 @@ #include "mirror/class.h" #include "mirror/dex_cache-inl.h" #include "mirror/method_handle_impl.h" +#include "mirror/method_type.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "reg_type-inl.h" @@ -1169,6 +1170,15 @@ bool MethodVerifier::VerifyInstruction(const Instruction* inst, uint32_t code_of case Instruction::kVerifyRegBWide: result = result && CheckWideRegisterIndex(inst->VRegB()); break; + case Instruction::kVerifyRegBCallSite: + result = result && CheckCallSiteIndex(inst->VRegB()); + break; + case Instruction::kVerifyRegBMethodHandle: + result = result && CheckMethodHandleIndex(inst->VRegB()); + break; + case Instruction::kVerifyRegBPrototype: + result = result && CheckPrototypeIndex(inst->VRegB()); + break; } switch (inst->GetVerifyTypeArgumentC()) { case Instruction::kVerifyRegC: @@ -1260,6 +1270,16 @@ inline bool MethodVerifier::CheckWideRegisterIndex(uint32_t idx) { return true; } +inline bool MethodVerifier::CheckCallSiteIndex(uint32_t idx) { + uint32_t limit = dex_file_->NumCallSiteIds(); + if (UNLIKELY(idx >= limit)) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad call site index " << idx << " (max " + << limit << ")"; + return false; + } + return true; +} + inline bool MethodVerifier::CheckFieldIndex(uint32_t idx) { if (UNLIKELY(idx >= dex_file_->GetHeader().field_ids_size_)) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad field index " << idx << " (max " @@ -1278,6 +1298,16 @@ inline bool MethodVerifier::CheckMethodIndex(uint32_t idx) { return true; } +inline bool MethodVerifier::CheckMethodHandleIndex(uint32_t idx) { + uint32_t limit = dex_file_->NumMethodHandles(); + if (UNLIKELY(idx >= limit)) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad method handle index " << idx << " (max " + << limit << ")"; + return false; + } + return true; +} + inline bool MethodVerifier::CheckNewInstance(dex::TypeIndex idx) { if (UNLIKELY(idx.index_ >= dex_file_->GetHeader().type_ids_size_)) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx.index_ << " (max " @@ -2320,6 +2350,18 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { : reg_types_.JavaLangClass()); break; } + case Instruction::CONST_METHOD_HANDLE: + work_line_->SetRegisterType<LockOp::kClear>( + this, inst->VRegA_21c(), reg_types_.JavaLangInvokeMethodHandle()); + // TODO: add compiler support for const-method-{handle,type} (b/66890674) + Fail(VERIFY_ERROR_FORCE_INTERPRETER); + break; + case Instruction::CONST_METHOD_TYPE: + work_line_->SetRegisterType<LockOp::kClear>( + this, inst->VRegA_21c(), reg_types_.JavaLangInvokeMethodType()); + // TODO: add compiler support for const-method-{handle,type} (b/66890674) + Fail(VERIFY_ERROR_FORCE_INTERPRETER); + break; case Instruction::MONITOR_ENTER: work_line_->PushMonitor(this, inst->VRegA_11x(), work_insn_idx_); // Check whether the previous instruction is a move-object with vAA as a source, creating @@ -3454,7 +3496,6 @@ 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_FE ... Instruction::UNUSED_FF: case Instruction::UNUSED_79: case Instruction::UNUSED_7A: Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_); diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index da4102a786..8afbe78c68 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -404,6 +404,10 @@ class MethodVerifier { /* Ensure that the wide register index is valid for this code item. */ bool CheckWideRegisterIndex(uint32_t idx); + // Perform static checks on an instruction referencing a CallSite. All we do here is ensure that + // the call site index is in the valid range. + bool CheckCallSiteIndex(uint32_t idx); + // Perform static checks on a field Get or set instruction. All we do here is ensure that the // field index is in the valid range. bool CheckFieldIndex(uint32_t idx); @@ -412,6 +416,10 @@ class MethodVerifier { // method index is in the valid range. bool CheckMethodIndex(uint32_t idx); + // Perform static checks on an instruction referencing a constant method handle. All we do here + // is ensure that the method index is in the valid range. + bool CheckMethodHandleIndex(uint32_t idx); + // Perform static checks on a "new-instance" instruction. Specifically, make sure the class // reference isn't for an array class. bool CheckNewInstance(dex::TypeIndex idx); diff --git a/runtime/verifier/reg_type_cache-inl.h b/runtime/verifier/reg_type_cache-inl.h index b57a2c86d4..197c97671e 100644 --- a/runtime/verifier/reg_type_cache-inl.h +++ b/runtime/verifier/reg_type_cache-inl.h @@ -19,6 +19,8 @@ #include "class_linker.h" #include "mirror/class-inl.h" +#include "mirror/method_handle_impl.h" +#include "mirror/method_type.h" #include "mirror/string.h" #include "mirror/throwable.h" #include "reg_type.h" @@ -131,6 +133,20 @@ inline const PreciseReferenceType& RegTypeCache::JavaLangString() { return *down_cast<const PreciseReferenceType*>(result); } +inline const PreciseReferenceType& RegTypeCache::JavaLangInvokeMethodHandle() { + const RegType* result = &FromClass("Ljava/lang/invoke/MethodHandle;", + mirror::MethodHandle::StaticClass(), true); + DCHECK(result->IsPreciseReference()); + return *down_cast<const PreciseReferenceType*>(result); +} + +inline const PreciseReferenceType& RegTypeCache::JavaLangInvokeMethodType() { + const RegType* result = &FromClass("Ljava/lang/invoke/MethodType;", + mirror::MethodType::StaticClass(), true); + DCHECK(result->IsPreciseReference()); + return *down_cast<const PreciseReferenceType*>(result); +} + inline const RegType& RegTypeCache::JavaLangThrowable(bool precise) { const RegType* result = &FromClass("Ljava/lang/Throwable;", mirror::Throwable::GetJavaLangThrowable(), precise); diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h index 7077c55a8d..96eca05dc4 100644 --- a/runtime/verifier/reg_type_cache.h +++ b/runtime/verifier/reg_type_cache.h @@ -126,6 +126,8 @@ class RegTypeCache { const PreciseReferenceType& JavaLangClass() REQUIRES_SHARED(Locks::mutator_lock_); const PreciseReferenceType& JavaLangString() REQUIRES_SHARED(Locks::mutator_lock_); + const PreciseReferenceType& JavaLangInvokeMethodHandle() REQUIRES_SHARED(Locks::mutator_lock_); + const PreciseReferenceType& JavaLangInvokeMethodType() REQUIRES_SHARED(Locks::mutator_lock_); const RegType& JavaLangThrowable(bool precise) REQUIRES_SHARED(Locks::mutator_lock_); const RegType& JavaLangObject(bool precise) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/test/979-const-method-handle/build b/test/979-const-method-handle/build new file mode 100644 index 0000000000..966ecf4ce4 --- /dev/null +++ b/test/979-const-method-handle/build @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Stop if something fails. +set -e + +${DX} --dex --min-sdk-version=27 --output=classes.dex classes + +zip $TEST_NAME.jar classes.dex diff --git a/test/979-const-method-handle/classes/Main.class b/test/979-const-method-handle/classes/Main.class Binary files differnew file mode 100644 index 0000000000..8d6b7d88bb --- /dev/null +++ b/test/979-const-method-handle/classes/Main.class diff --git a/test/979-const-method-handle/classes/constmethodhandle/ConstTest.class b/test/979-const-method-handle/classes/constmethodhandle/ConstTest.class Binary files differnew file mode 100644 index 0000000000..a21b0a336c --- /dev/null +++ b/test/979-const-method-handle/classes/constmethodhandle/ConstTest.class diff --git a/test/979-const-method-handle/expected.txt b/test/979-const-method-handle/expected.txt new file mode 100644 index 0000000000..573b80da99 --- /dev/null +++ b/test/979-const-method-handle/expected.txt @@ -0,0 +1,2 @@ +MethodHandle MethodHandle(Object)Class => class java.lang.Float +MethodType (char,short,int,long,float,double,Object)boolean diff --git a/test/979-const-method-handle/info.txt b/test/979-const-method-handle/info.txt new file mode 100644 index 0000000000..e8514ce91f --- /dev/null +++ b/test/979-const-method-handle/info.txt @@ -0,0 +1,7 @@ +This test checks const-method-handle and const-method-type bytecodes. + +The class files in this test come from: + + dalvik/dx/tests/142-const-method-handle + +and are built using ASM bytecode manipulation library. diff --git a/test/dexdump/const-method-handle.dex b/test/dexdump/const-method-handle.dex Binary files differnew file mode 100644 index 0000000000..1fe28e572c --- /dev/null +++ b/test/dexdump/const-method-handle.dex diff --git a/test/dexdump/const-method-handle.lst b/test/dexdump/const-method-handle.lst new file mode 100644 index 0000000000..961d427fe7 --- /dev/null +++ b/test/dexdump/const-method-handle.lst @@ -0,0 +1,9 @@ +#const-method-handle.dex +0x000003c0 8 Main <init> ()V Main.java 22 +0x000003d8 50 Main main ([Ljava/lang/String;)V Main.java 26 +0x0000041c 8 constmethodhandle.ConstTest <init> ()V ConstTest.java 22 +0x00000434 94 constmethodhandle.ConstTest displayMethodHandle (Ljava/lang/invoke/MethodHandle;)V ConstTest.java 24 +0x000004a4 50 constmethodhandle.ConstTest displayMethodType (Ljava/lang/invoke/MethodType;)V ConstTest.java 29 +0x000004e8 30 constmethodhandle.ConstTest main ([Ljava/lang/String;)V ConstTest.java -1 +0x00000518 6 constmethodhandle.ConstTest test1 ()Ljava/lang/invoke/MethodHandle; ConstTest.java -1 +0x00000530 6 constmethodhandle.ConstTest test2 ()Ljava/lang/invoke/MethodType; ConstTest.java -1 diff --git a/test/dexdump/const-method-handle.txt b/test/dexdump/const-method-handle.txt new file mode 100644 index 0000000000..6b33502105 --- /dev/null +++ b/test/dexdump/const-method-handle.txt @@ -0,0 +1,275 @@ +Processing 'const-method-handle.dex'... +Opened 'const-method-handle.dex', DEX version '039' +DEX file header: +magic : 'dex\n039\0' +checksum : 16656a27 +signature : 1953...5aa5 +file_size : 2524 +header_size : 112 +link_size : 0 +link_off : 0 (0x000000) +string_ids_size : 57 +string_ids_off : 112 (0x000070) +type_ids_size : 26 +type_ids_off : 340 (0x000154) +proto_ids_size : 18 +proto_ids_off : 444 (0x0001bc) +field_ids_size : 2 +field_ids_off : 660 (0x000294) +method_ids_size : 23 +method_ids_off : 676 (0x0002a4) +class_defs_size : 2 +class_defs_off : 860 (0x00035c) +data_size : 1588 +data_off : 936 (0x0003a8) + +Class #0 header: +class_idx : 5 +access_flags : 1 (0x0001) +superclass_idx : 11 +interfaces_off : 0 (0x000000) +source_file_idx : 29 +annotations_off : 1336 (0x000538) +class_data_off : 2270 (0x0008de) +static_fields_size : 0 +instance_fields_size: 0 +direct_methods_size : 2 +virtual_methods_size: 0 + +Class #0 annotations: +Annotations on method #1 'main' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } + +Class #0 - + Class descriptor : 'LMain;' + Access flags : 0x0001 (PUBLIC) + Superclass : 'Ljava/lang/Object;' + Interfaces - + Static fields - + Instance fields - + Direct methods - + #0 : (in LMain;) + name : '<init>' + type : '()V' + access : 0x10001 (PUBLIC CONSTRUCTOR) + code - + registers : 1 + ins : 1 + outs : 1 + insns size : 4 16-bit code units +0003b0: |[0003b0] Main.<init>:()V +0003c0: 7010 0a00 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@000a +0003c6: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=22 + locals : + 0x0000 - 0x0004 reg=0 this LMain; + + #1 : (in LMain;) + name : 'main' + type : '([Ljava/lang/String;)V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 6 + ins : 1 + outs : 4 + insns size : 25 16-bit code units +0003c8: |[0003c8] Main.main:([Ljava/lang/String;)V +0003d8: 7100 1500 0000 |0000: invoke-static {}, Ljava/lang/invoke/MethodHandles;.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; // method@0015 +0003de: 0c00 |0003: move-result-object v0 +0003e0: 1c01 0600 |0004: const-class v1, Lconstmethodhandle/ConstTest; // type@0006 +0003e4: 1a02 3000 |0006: const-string v2, "main" // string@0030 +0003e8: 6203 0100 |0008: sget-object v3, Ljava/lang/Void;.TYPE:Ljava/lang/Class; // field@0001 +0003ec: 1c04 1900 |000a: const-class v4, [Ljava/lang/String; // type@0019 +0003f0: 7120 1600 4300 |000c: invoke-static {v3, v4}, Ljava/lang/invoke/MethodType;.methodType:(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodType; // method@0016 +0003f6: 0c03 |000f: move-result-object v3 +0003f8: 6e40 1400 1032 |0010: invoke-virtual {v0, v1, v2, v3}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@0014 +0003fe: 0c00 |0013: move-result-object v0 +000400: fa20 1200 5000 1000 |0014: invoke-polymorphic {v0, v5}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/String;)V // method@0012, proto@0010 +000408: 0e00 |0018: return-void + catches : (none) + positions : + 0x0000 line=26 + 0x000c line=27 + 0x0014 line=28 + 0x0018 line=29 + locals : + 0x0000 - 0x0019 reg=5 (null) [Ljava/lang/String; + + Virtual methods - + source_file_idx : 29 (Main.java) + +Class #1 header: +class_idx : 6 +access_flags : 1 (0x0001) +superclass_idx : 11 +interfaces_off : 0 (0x000000) +source_file_idx : 3 +annotations_off : 1360 (0x000550) +class_data_off : 2284 (0x0008ec) +static_fields_size : 0 +instance_fields_size: 0 +direct_methods_size : 6 +virtual_methods_size: 0 + +Class #1 annotations: +Annotations on method #3 'displayMethodHandle' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } + +Class #1 - + Class descriptor : 'Lconstmethodhandle/ConstTest;' + Access flags : 0x0001 (PUBLIC) + Superclass : 'Ljava/lang/Object;' + Interfaces - + Static fields - + Instance fields - + Direct methods - + #0 : (in Lconstmethodhandle/ConstTest;) + name : '<init>' + type : '()V' + access : 0x10001 (PUBLIC CONSTRUCTOR) + code - + registers : 1 + ins : 1 + outs : 1 + insns size : 4 16-bit code units +00040c: |[00040c] constmethodhandle.ConstTest.<init>:()V +00041c: 7010 0a00 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@000a +000422: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=22 + locals : + 0x0000 - 0x0004 reg=0 this Lconstmethodhandle/ConstTest; + + #1 : (in Lconstmethodhandle/ConstTest;) + name : 'displayMethodHandle' + type : '(Ljava/lang/invoke/MethodHandle;)V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 4 + ins : 1 + outs : 2 + insns size : 47 16-bit code units +000424: |[000424] constmethodhandle.ConstTest.displayMethodHandle:(Ljava/lang/invoke/MethodHandle;)V +000434: 6200 0000 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000 +000438: 2201 0d00 |0002: new-instance v1, Ljava/lang/StringBuilder; // type@000d +00043c: 7010 0c00 0100 |0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.<init>:()V // method@000c +000442: 1a02 1e00 |0007: const-string v2, "MethodHandle " // string@001e +000446: 6e20 0e00 2100 |0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@000e +00044c: 0c01 |000c: move-result-object v1 +00044e: 6e20 0d00 3100 |000d: invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@000d +000454: 0c01 |0010: move-result-object v1 +000456: 1a02 0000 |0011: const-string v2, " => " // string@0000 +00045a: 6e20 0e00 2100 |0013: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@000e +000460: 0c01 |0016: move-result-object v1 +000462: 1402 0030 4046 |0017: const v2, #float 12300 // #46403000 +000468: 7110 0900 0200 |001a: invoke-static {v2}, Ljava/lang/Float;.valueOf:(F)Ljava/lang/Float; // method@0009 +00046e: 0c02 |001d: move-result-object v2 +000470: fa20 1100 2300 0100 |001e: invoke-polymorphic {v3, v2}, Ljava/lang/invoke/MethodHandle;.invoke:([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Object;)Ljava/lang/Class; // method@0011, proto@0001 +000478: 0c02 |0022: move-result-object v2 +00047a: 6e20 0d00 2100 |0023: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@000d +000480: 0c01 |0026: move-result-object v1 +000482: 6e10 0f00 0100 |0027: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@000f +000488: 0c01 |002a: move-result-object v1 +00048a: 6e20 0800 1000 |002b: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0008 +000490: 0e00 |002e: return-void + catches : (none) + positions : + 0x0000 line=24 + 0x001a line=25 + 0x002b line=24 + 0x002e line=26 + locals : + 0x0000 - 0x002f reg=3 (null) Ljava/lang/invoke/MethodHandle; + + #2 : (in Lconstmethodhandle/ConstTest;) + name : 'displayMethodType' + type : '(Ljava/lang/invoke/MethodType;)V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 4 + ins : 1 + outs : 2 + insns size : 25 16-bit code units +000494: |[000494] constmethodhandle.ConstTest.displayMethodType:(Ljava/lang/invoke/MethodType;)V +0004a4: 6200 0000 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000 +0004a8: 2201 0d00 |0002: new-instance v1, Ljava/lang/StringBuilder; // type@000d +0004ac: 7010 0c00 0100 |0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.<init>:()V // method@000c +0004b2: 1a02 1f00 |0007: const-string v2, "MethodType " // string@001f +0004b6: 6e20 0e00 2100 |0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@000e +0004bc: 0c01 |000c: move-result-object v1 +0004be: 6e20 0d00 3100 |000d: invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@000d +0004c4: 0c01 |0010: move-result-object v1 +0004c6: 6e10 0f00 0100 |0011: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@000f +0004cc: 0c01 |0014: move-result-object v1 +0004ce: 6e20 0800 1000 |0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0008 +0004d4: 0e00 |0018: return-void + catches : (none) + positions : + 0x0000 line=29 + 0x0018 line=30 + locals : + 0x0000 - 0x0019 reg=3 (null) Ljava/lang/invoke/MethodType; + + #3 : (in Lconstmethodhandle/ConstTest;) + name : 'main' + type : '([Ljava/lang/String;)V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 1 + outs : 1 + insns size : 15 16-bit code units +0004d8: |[0004d8] constmethodhandle.ConstTest.main:([Ljava/lang/String;)V +0004e8: 7100 0600 0000 |0000: invoke-static {}, Lconstmethodhandle/ConstTest;.test1:()Ljava/lang/invoke/MethodHandle; // method@0006 +0004ee: 0c00 |0003: move-result-object v0 +0004f0: 7110 0300 0000 |0004: invoke-static {v0}, Lconstmethodhandle/ConstTest;.displayMethodHandle:(Ljava/lang/invoke/MethodHandle;)V // method@0003 +0004f6: 7100 0700 0000 |0007: invoke-static {}, Lconstmethodhandle/ConstTest;.test2:()Ljava/lang/invoke/MethodType; // method@0007 +0004fc: 0c00 |000a: move-result-object v0 +0004fe: 7110 0400 0000 |000b: invoke-static {v0}, Lconstmethodhandle/ConstTest;.displayMethodType:(Ljava/lang/invoke/MethodType;)V // method@0004 +000504: 0e00 |000e: return-void + catches : (none) + positions : + locals : + + #4 : (in Lconstmethodhandle/ConstTest;) + name : 'test1' + type : '()Ljava/lang/invoke/MethodHandle;' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 3 16-bit code units +000508: |[000508] constmethodhandle.ConstTest.test1:()Ljava/lang/invoke/MethodHandle; +000518: fe00 0000 |0000: const-method-handle v0, method_handle@0000 +00051c: 1100 |0002: return-object v0 + catches : (none) + positions : + locals : + + #5 : (in Lconstmethodhandle/ConstTest;) + name : 'test2' + type : '()Ljava/lang/invoke/MethodType;' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 3 16-bit code units +000520: |[000520] constmethodhandle.ConstTest.test2:()Ljava/lang/invoke/MethodType; +000530: ff00 1100 |0000: const-method-type v0, (CSIJFDLjava/lang/Object;)Z // proto@0011 +000534: 1100 |0002: return-object v0 + catches : (none) + positions : + locals : + + Virtual methods - + source_file_idx : 3 (ConstTest.java) + +Method handle #0: + type : invoke-instance + target : Ljava/lang/Object; getClass + target_type : (Ljava/lang/Object;)Ljava/lang/Class; diff --git a/test/dexdump/const-method-handle.xml b/test/dexdump/const-method-handle.xml new file mode 100644 index 0000000000..f1cf9f8557 --- /dev/null +++ b/test/dexdump/const-method-handle.xml @@ -0,0 +1,91 @@ +<api> +<package name="" +> +<class name="Main" + extends="java.lang.Object" + interface="false" + abstract="false" + static="false" + final="false" + visibility="public" +> +<constructor name="Main" + type="Main" + 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> +</class> +</package> +<package name="constmethodhandle" +> +<class name="ConstTest" + extends="java.lang.Object" + interface="false" + abstract="false" + static="false" + final="false" + visibility="public" +> +<constructor name="ConstTest" + type="constmethodhandle.ConstTest" + 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="test1" + return="java.lang.invoke.MethodHandle" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="test2" + return="java.lang.invoke.MethodType" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +</class> +<method_handle index="0" + type="invoke-instance" + target_class="Ljava/lang/Object;" + target_member="getClass" + target_member_type="(Ljava/lang/Object;)Ljava/lang/Class;" +> +</method_handle> +</package> +</api> |