diff options
author | Narayan Kamath <narayan@google.com> | 2016-08-03 12:46:58 +0100 |
---|---|---|
committer | Narayan Kamath <narayan@google.com> | 2016-10-03 09:48:50 +0100 |
commit | 25352fc06c84cdab8a2ab6d173b0514066ade2b9 (patch) | |
tree | bb8a6be0c9a144ccf33b07b3158409769a32daa2 | |
parent | 51cbe84ca33800c57fef9c96f1155f60bcc70bba (diff) | |
download | android_art-25352fc06c84cdab8a2ab6d173b0514066ade2b9.tar.gz android_art-25352fc06c84cdab8a2ab6d173b0514066ade2b9.tar.bz2 android_art-25352fc06c84cdab8a2ab6d173b0514066ade2b9.zip |
class_linker: Add support for resolving method types.
- Add a new fixed size dex cache array for resolved method types.
The size of this array is set to 1024.
- Also introduces a new runtime flag that controls this feature.
Test: make test-art-host
Bug: 30550796
Change-Id: I147b33398d71ee21f2e91b418d3700d4630801ff
-rw-r--r-- | build/Android.gtest.mk | 5 | ||||
-rw-r--r-- | cmdline/cmdline_types.h | 2 | ||||
-rw-r--r-- | runtime/class_linker.cc | 127 | ||||
-rw-r--r-- | runtime/class_linker.h | 11 | ||||
-rw-r--r-- | runtime/class_linker_test.cc | 65 | ||||
-rw-r--r-- | runtime/experimental_flags.h | 5 | ||||
-rw-r--r-- | runtime/mirror/dex_cache-inl.h | 19 | ||||
-rw-r--r-- | runtime/mirror/dex_cache.cc | 5 | ||||
-rw-r--r-- | runtime/mirror/dex_cache.h | 67 | ||||
-rw-r--r-- | runtime/mirror/dex_cache_test.cc | 91 | ||||
-rw-r--r-- | runtime/parsed_options.cc | 2 | ||||
-rw-r--r-- | runtime/runtime.h | 4 | ||||
-rw-r--r-- | runtime/runtime_options.def | 2 | ||||
-rw-r--r-- | runtime/utils/dex_cache_arrays_layout-inl.h | 25 | ||||
-rw-r--r-- | runtime/utils/dex_cache_arrays_layout.h | 12 | ||||
-rw-r--r-- | test/MethodTypes/MethodTypes.java | 20 |
16 files changed, 439 insertions, 23 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 4f63099930..850702ad23 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -33,6 +33,7 @@ GTEST_DEX_DIRECTORIES := \ Interfaces \ Lookup \ Main \ + MethodTypes \ MultiDex \ MultiDexModifiedSecondary \ MyClass \ @@ -80,9 +81,9 @@ $(ART_TEST_TARGET_GTEST_VerifierDeps_DEX): $(ART_TEST_GTEST_VerifierDeps_SRC) $( # Dex file dependencies for each gtest. ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested -ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MultiDex MyClass Nested Statics StaticsFromCode +ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex -ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages +ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index b74e588043..b229be4abb 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -775,6 +775,8 @@ struct CmdlineType<ExperimentalFlags> : CmdlineTypeParser<ExperimentalFlags> { existing = existing | ExperimentalFlags::kAgents; } else if (option == "runtime-plugins") { existing = existing | ExperimentalFlags::kRuntimePlugins; + } else if (option == "method-handles") { + existing = existing | ExperimentalFlags::kMethodHandles; } else { return Result::Failure(std::string("Unknown option '") + option + "'"); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 0a1f7a940a..7dea614fdb 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1287,7 +1287,7 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( const DexFile* const dex_file = dex_cache->GetDexFile(); const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) { - // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and + // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and // copy over the arrays. DCHECK(dex_file != nullptr); size_t num_strings = mirror::DexCache::kDexCacheStringCacheSize; @@ -1297,10 +1297,19 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( const size_t num_types = dex_file->NumTypeIds(); const size_t num_methods = dex_file->NumMethodIds(); const size_t num_fields = dex_file->NumFieldIds(); + size_t num_method_types = 0; + if (Runtime::Current()->IsMethodHandlesEnabled()) { + num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize; + if (dex_file->NumProtoIds() < num_method_types) { + num_method_types = dex_file->NumProtoIds(); + } + } + 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()); DexCacheArraysLayout layout(image_pointer_size_, dex_file); uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays(); if (num_strings != 0u) { @@ -1351,6 +1360,25 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( std::copy_n(dex_cache->GetResolvedFields(), num_fields, fields); dex_cache->SetResolvedFields(fields); } + if (num_method_types != 0u) { + // NOTE: We currently (Sep 2016) do not resolve any method types at + // compile time, but plan to in the future. This code exists for the + // sake of completeness. + mirror::MethodTypeDexCacheType* const image_resolved_method_types = + dex_cache->GetResolvedMethodTypes(); + mirror::MethodTypeDexCacheType* const method_types = + reinterpret_cast<mirror::MethodTypeDexCacheType*>( + raw_arrays + layout.MethodTypesOffset()); + for (size_t j = 0; j < num_method_types; ++j) { + DCHECK_EQ(method_types[j].load(std::memory_order_relaxed).index, 0u); + DCHECK(method_types[j].load(std::memory_order_relaxed).object.IsNull()); + method_types[j].store( + image_resolved_method_types[j].load(std::memory_order_relaxed), + std::memory_order_relaxed); + } + + dex_cache->SetResolvedMethodTypes(method_types); + } } { WriterMutexLock mu2(self, dex_lock_); @@ -2104,6 +2132,7 @@ mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, // Zero-initialized. raw_arrays = reinterpret_cast<uint8_t*>(linear_alloc->Alloc(self, layout.Size())); } + mirror::StringDexCacheType* strings = (dex_file.NumStringIds() == 0u) ? nullptr : reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset()); GcRoot<mirror::Class>* types = (dex_file.NumTypeIds() == 0u) ? nullptr : @@ -2112,10 +2141,35 @@ mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, reinterpret_cast<ArtMethod**>(raw_arrays + layout.MethodsOffset()); ArtField** fields = (dex_file.NumFieldIds() == 0u) ? nullptr : reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset()); + size_t num_strings = mirror::DexCache::kDexCacheStringCacheSize; if (dex_file.NumStringIds() < num_strings) { num_strings = dex_file.NumStringIds(); } + + // Note that we allocate the method type dex caches regardless of this flag, + // and we make sure here that they're not used by the runtime. This is in the + // interest of simplicity and to avoid extensive compiler and layout class changes. + // + // If this needs to be mitigated in a production system running this code, + // DexCache::kDexCacheMethodTypeCacheSize can be set to zero. + const bool is_method_handles_enabled = Runtime::Current()->IsMethodHandlesEnabled(); + mirror::MethodTypeDexCacheType* method_types = nullptr; + size_t num_method_types = 0; + + if (is_method_handles_enabled) { + if (dex_file.NumProtoIds() < mirror::DexCache::kDexCacheMethodTypeCacheSize) { + num_method_types = dex_file.NumProtoIds(); + } else { + num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize; + } + + if (num_method_types > 0) { + method_types = reinterpret_cast<mirror::MethodTypeDexCacheType*>( + raw_arrays + layout.MethodTypesOffset()); + } + } + DCHECK_ALIGNED(raw_arrays, alignof(mirror::StringDexCacheType)) << "Expected raw_arrays to align to StringDexCacheType."; DCHECK_ALIGNED(layout.StringsOffset(), alignof(mirror::StringDexCacheType)) << @@ -2139,10 +2193,17 @@ mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, for (size_t i = 0; i < dex_file.NumFieldIds(); ++i) { CHECK(mirror::DexCache::GetElementPtrSize(fields, i, image_pointer_size_) == nullptr); } + for (size_t i = 0; i < num_method_types; ++i) { + CHECK_EQ(method_types[i].load(std::memory_order_relaxed).index, 0u); + CHECK(method_types[i].load(std::memory_order_relaxed).object.IsNull()); + } } if (strings != nullptr) { mirror::StringDexCachePair::Initialize(strings); } + if (method_types != nullptr) { + mirror::MethodTypeDexCachePair::Initialize(method_types); + } dex_cache->Init(&dex_file, location.Get(), strings, @@ -2153,6 +2214,8 @@ mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, dex_file.NumMethodIds(), fields, dex_file.NumFieldIds(), + method_types, + num_method_types, image_pointer_size_); return dex_cache.Get(); } @@ -7935,6 +7998,68 @@ ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, return resolved; } +mirror::MethodType* ClassLinker::ResolveMethodType(const DexFile& dex_file, + uint32_t proto_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader) { + DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); + DCHECK(dex_cache.Get() != nullptr); + + mirror::MethodType* resolved = dex_cache->GetResolvedMethodType(proto_idx); + if (resolved != nullptr) { + return resolved; + } + + Thread* const self = Thread::Current(); + StackHandleScope<4> hs(self); + + // First resolve the return type. + const DexFile::ProtoId& proto_id = dex_file.GetProtoId(proto_idx); + Handle<mirror::Class> return_type(hs.NewHandle( + ResolveType(dex_file, proto_id.return_type_idx_, dex_cache, class_loader))); + if (return_type.Get() == nullptr) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + // Then resolve the argument types. + // + // TODO: Is there a better way to figure out the number of method arguments + // other than by looking at the shorty ? + const size_t num_method_args = strlen(dex_file.StringDataByIdx(proto_id.shorty_idx_)) - 1; + + mirror::Class* class_type = mirror::Class::GetJavaLangClass(); + 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_method_args))); + if (method_params.Get() == nullptr) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + DexFileParameterIterator it(dex_file, proto_id); + int32_t i = 0; + MutableHandle<mirror::Class> param_class = hs.NewHandle<mirror::Class>(nullptr); + for (; it.HasNext(); it.Next()) { + const uint16_t type_idx = it.GetTypeIdx(); + param_class.Assign(ResolveType(dex_file, type_idx, dex_cache, class_loader)); + if (param_class.Get() == nullptr) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + method_params->Set(i++, param_class.Get()); + } + + DCHECK(!it.HasNext()); + + Handle<mirror::MethodType> type = hs.NewHandle( + mirror::MethodType::Create(self, return_type, method_params)); + dex_cache->SetResolvedMethodType(proto_idx, type.Get()); + + return type.Get(); +} + const char* ClassLinker::MethodShorty(uint32_t method_idx, ArtMethod* referrer, uint32_t* length) { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 8f7051e3f9..df7fb6115f 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -49,6 +49,7 @@ namespace mirror { class ClassLoader; class DexCache; class DexCachePointerArray; + class DexCacheMethodHandlesTest_Open_Test; class DexCacheTest_Open_Test; class IfTable; class MethodType; @@ -362,6 +363,15 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); + // Resolve a method type with a given ID from the DexFile, storing + // the result in the DexCache. + mirror::MethodType* ResolveMethodType(const DexFile& dex_file, + uint32_t proto_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!dex_lock_, !Roles::uninterruptible_); + // Get shorty from method index without resolution. Used to do handlerization. const char* MethodShorty(uint32_t method_idx, ArtMethod* referrer, uint32_t* length) REQUIRES_SHARED(Locks::mutator_lock_); @@ -1181,6 +1191,7 @@ class ClassLinker { friend class JniCompilerTest; // for GetRuntimeQuickGenericJniStub friend class JniInternalTest; // for GetRuntimeQuickGenericJniStub ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName); // for DexLock, and RegisterDexFileLocked + ART_FRIEND_TEST(mirror::DexCacheMethodHandlesTest, Open); // for AllocDexCache ART_FRIEND_TEST(mirror::DexCacheTest, Open); // for AllocDexCache DISALLOW_COPY_AND_ASSIGN(ClassLinker); }; diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index a5aa0d08fe..593702d939 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -451,6 +451,14 @@ class ClassLinkerTest : public CommonRuntimeTest { }; }; +class ClassLinkerMethodHandlesTest : public ClassLinkerTest { + protected: + virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE { + CommonRuntimeTest::SetUpRuntimeOptions(options); + options->push_back(std::make_pair("-Xexperimental:method-handles", nullptr)); + } +}; + struct CheckOffset { size_t cpp_offset; const char* java_name; @@ -650,10 +658,12 @@ struct DexCacheOffsets : public CheckOffsets<mirror::DexCache> { addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_), "dexFile"); addOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location"); 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_fields_), "resolvedFields"); + addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_method_types_), "resolvedMethodTypes"); addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_), "resolvedMethods"); addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_types_), "resolvedTypes"); addOffset(OFFSETOF_MEMBER(mirror::DexCache, strings_), "strings"); @@ -1294,4 +1304,59 @@ TEST_F(ClassLinkerTest, RegisterDexFileName) { } } +TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { + ScopedObjectAccess soa(Thread::Current()); + + StackHandleScope<7> hs(soa.Self()); + + Handle<mirror::ClassLoader> class_loader( + hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("MethodTypes")))); + Handle<mirror::Class> method_types( + hs.NewHandle(class_linker_->FindClass(soa.Self(), "LMethodTypes;", class_loader))); + class_linker_->EnsureInitialized(soa.Self(), method_types, true, true); + + ArtMethod* method1 = method_types->FindVirtualMethod("method1", + "(Ljava/lang/String;)Ljava/lang/String;", + kRuntimePointerSize); + + const DexFile& dex_file = *(method1->GetDexFile()); + Handle<mirror::DexCache> dex_cache = hs.NewHandle( + class_linker_->FindDexCache(Thread::Current(), dex_file)); + + const DexFile::MethodId& method1_id = dex_file.GetMethodId(method1->GetDexMethodIndex()); + + // This is the MethodType corresponding to the prototype of + // String MethodTypes# method1(String). + // Its RType = Ljava/lang/String; + // Its PTypes = { Ljava/langString; } + Handle<mirror::MethodType> method1_type = hs.NewHandle( + class_linker_->ResolveMethodType(dex_file, method1_id.proto_idx_, dex_cache, class_loader)); + + // Assert that the method type was resolved successfully. + ASSERT_TRUE(method1_type.Get() != nullptr); + + // Assert that the return type and the method arguments are as we expect. + Handle<mirror::Class> string_class( + hs.NewHandle(class_linker_->FindClass(soa.Self(), "Ljava/lang/String;", class_loader))); + ASSERT_EQ(string_class.Get(), method1_type->GetRType()); + ASSERT_EQ(string_class.Get(), method1_type->GetPTypes()->Get(0)); + + // Resolve the method type again and assert that we get back the same value. + Handle<mirror::MethodType> method1_type2 = hs.NewHandle( + class_linker_->ResolveMethodType(dex_file, method1_id.proto_idx_, dex_cache, class_loader)); + ASSERT_EQ(method1_type.Get(), method1_type2.Get()); + + // Resolve the MethodType associated with a different method signature + // and assert it's different. + ArtMethod* method2 = method_types->FindVirtualMethod( + "method2", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", + kRuntimePointerSize); + const DexFile::MethodId& method2_id = dex_file.GetMethodId(method2->GetDexMethodIndex()); + Handle<mirror::MethodType> method2_type = hs.NewHandle( + class_linker_->ResolveMethodType(dex_file, method2_id.proto_idx_, dex_cache, class_loader)); + + ASSERT_TRUE(method1_type.Get() != method2_type.Get()); +} + } // namespace art diff --git a/runtime/experimental_flags.h b/runtime/experimental_flags.h index 54d2c35b7c..5ddb9fa27a 100644 --- a/runtime/experimental_flags.h +++ b/runtime/experimental_flags.h @@ -28,6 +28,7 @@ struct ExperimentalFlags { kNone = 0x0000, kAgents = 0x0001, // 0b00000001 kRuntimePlugins = 0x0002, // 0b00000010 + kMethodHandles = 0x0004, // 0b00000100 }; constexpr ExperimentalFlags() : value_(0x0000) {} @@ -74,6 +75,10 @@ inline std::ostream& operator<<(std::ostream& stream, const ExperimentalFlags& e stream << (started ? "|" : "") << "kRuntimePlugins"; started = true; } + if (e & ExperimentalFlags::kMethodHandles) { + stream << (started ? "|" : "") << "kMethodHandles"; + started = true; + } if (!started) { stream << "kNone"; } diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 359462d21a..41692da029 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -25,6 +25,7 @@ #include "base/enums.h" #include "base/logging.h" #include "mirror/class.h" +#include "mirror/method_type.h" #include "runtime.h" #include <atomic> @@ -82,6 +83,24 @@ inline void DexCache::SetResolvedType(uint32_t type_idx, Class* resolved) { Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this); } +inline MethodType* DexCache::GetResolvedMethodType(uint32_t proto_idx) { + DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); + DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds()); + return MethodTypeDexCachePair::Lookup( + GetResolvedMethodTypes(), proto_idx, NumResolvedMethodTypes()).Read(); +} + +inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) { + DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); + DCHECK_LT(proto_idx, NumResolvedMethodTypes()); // NOTE: Unchecked, i.e. not throwing AIOOB. + + GetResolvedMethodTypes()[proto_idx % NumResolvedMethodTypes()].store( + MethodTypeDexCachePair(resolved, proto_idx), std::memory_order_relaxed); + + // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this); +} + inline ArtField* DexCache::GetResolvedField(uint32_t field_idx, PointerSize ptr_size) { DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size); DCHECK_LT(field_idx, NumResolvedFields()); // NOTE: Unchecked, i.e. not throwing AIOOB. diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc index cfcec9cd3a..66f858c610 100644 --- a/runtime/mirror/dex_cache.cc +++ b/runtime/mirror/dex_cache.cc @@ -41,6 +41,8 @@ void DexCache::Init(const DexFile* dex_file, uint32_t num_resolved_methods, ArtField** resolved_fields, uint32_t num_resolved_fields, + MethodTypeDexCacheType* resolved_method_types, + uint32_t num_resolved_method_types, PointerSize pointer_size) { CHECK(dex_file != nullptr); CHECK(location != nullptr); @@ -48,6 +50,7 @@ void DexCache::Init(const DexFile* dex_file, CHECK_EQ(num_resolved_types != 0u, resolved_types != nullptr); CHECK_EQ(num_resolved_methods != 0u, resolved_methods != nullptr); CHECK_EQ(num_resolved_fields != 0u, resolved_fields != nullptr); + CHECK_EQ(num_resolved_method_types != 0u, resolved_method_types != nullptr); SetDexFile(dex_file); SetLocation(location); @@ -55,10 +58,12 @@ void DexCache::Init(const DexFile* dex_file, SetResolvedTypes(resolved_types); SetResolvedMethods(resolved_methods); SetResolvedFields(resolved_fields); + SetResolvedMethodTypes(resolved_method_types); SetField32<false>(NumStringsOffset(), num_strings); SetField32<false>(NumResolvedTypesOffset(), num_resolved_types); SetField32<false>(NumResolvedMethodsOffset(), num_resolved_methods); SetField32<false>(NumResolvedFieldsOffset(), num_resolved_fields); + SetField32<false>(NumResolvedMethodTypesOffset(), num_resolved_method_types); Runtime* const runtime = Runtime::Current(); if (runtime->HasResolutionMethod()) { diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index d81dedc538..92d9c1de21 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -33,6 +33,7 @@ union JValue; namespace mirror { +class MethodType; class String; template <typename T> struct PACKED(8) DexCachePair { @@ -92,6 +93,9 @@ template <typename T> struct PACKED(8) DexCachePair { using StringDexCachePair = DexCachePair<mirror::String>; using StringDexCacheType = std::atomic<StringDexCachePair>; +using MethodTypeDexCachePair = DexCachePair<mirror::MethodType>; +using MethodTypeDexCacheType = std::atomic<MethodTypeDexCachePair>; + // C++ mirror of java.lang.DexCache. class MANAGED DexCache FINAL : public Object { public: @@ -103,10 +107,20 @@ class MANAGED DexCache FINAL : public Object { static_assert(IsPowerOfTwo(kDexCacheStringCacheSize), "String dex cache size is not a power of 2."); + // Size of method type dex cache. Needs to be a power of 2 for entrypoint assumptions + // to hold. + static constexpr size_t kDexCacheMethodTypeCacheSize = 1024; + static_assert(IsPowerOfTwo(kDexCacheMethodTypeCacheSize), + "MethodType dex cache size is not a power of 2."); + static constexpr size_t StaticStringSize() { return kDexCacheStringCacheSize; } + static constexpr size_t StaticMethodTypeSize() { + return kDexCacheMethodTypeCacheSize; + } + // Size of an instance of java.lang.DexCache not including referenced values. static constexpr uint32_t InstanceSize() { return sizeof(DexCache); @@ -122,6 +136,8 @@ 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, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); void Fixup(ArtMethod* trampoline, PointerSize pointer_size) @@ -159,6 +175,10 @@ class MANAGED DexCache FINAL : public Object { return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_methods_); } + static MemberOffset ResolvedMethodTypesOffset() { + return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_method_types_); + } + static MemberOffset NumStringsOffset() { return OFFSET_OF_OBJECT_MEMBER(DexCache, num_strings_); } @@ -175,6 +195,10 @@ class MANAGED DexCache FINAL : public Object { return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_methods_); } + static MemberOffset NumResolvedMethodTypesOffset() { + return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_method_types_); + } + mirror::String* GetResolvedString(uint32_t string_idx) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_); @@ -205,6 +229,10 @@ class MANAGED DexCache FINAL : public Object { ALWAYS_INLINE void SetResolvedField(uint32_t idx, ArtField* field, PointerSize ptr_size) REQUIRES_SHARED(Locks::mutator_lock_); + MethodType* GetResolvedMethodType(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_); + + void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) REQUIRES_SHARED(Locks::mutator_lock_); + StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldPtr64<StringDexCacheType*>(StringsOffset()); } @@ -243,6 +271,17 @@ class MANAGED DexCache FINAL : public Object { SetFieldPtr<false>(ResolvedFieldsOffset(), resolved_fields); } + MethodTypeDexCacheType* GetResolvedMethodTypes() + ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { + return GetFieldPtr<MethodTypeDexCacheType*>(ResolvedMethodTypesOffset()); + } + + void SetResolvedMethodTypes(MethodTypeDexCacheType* resolved_method_types) + ALWAYS_INLINE + REQUIRES_SHARED(Locks::mutator_lock_) { + SetFieldPtr<false>(ResolvedMethodTypesOffset(), resolved_method_types); + } + size_t NumStrings() REQUIRES_SHARED(Locks::mutator_lock_) { return GetField32(NumStringsOffset()); } @@ -259,6 +298,10 @@ class MANAGED DexCache FINAL : public Object { return GetField32(NumResolvedFieldsOffset()); } + size_t NumResolvedMethodTypes() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetField32(NumResolvedMethodTypesOffset()); + } + const DexFile* GetDexFile() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldPtr<const DexFile*>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_)); } @@ -290,16 +333,20 @@ class MANAGED DexCache FINAL : public Object { HeapReference<Object> dex_; HeapReference<String> location_; - uint64_t dex_file_; // const DexFile* - uint64_t resolved_fields_; // ArtField*, array with num_resolved_fields_ elements. - uint64_t resolved_methods_; // ArtMethod*, array with num_resolved_methods_ elements. - uint64_t resolved_types_; // GcRoot<Class>*, array with num_resolved_types_ elements. - uint64_t strings_; // std::atomic<StringDexCachePair>*, - // array with num_strings_ elements. - uint32_t num_resolved_fields_; // Number of elements in the resolved_fields_ array. - uint32_t num_resolved_methods_; // Number of elements in the resolved_methods_ array. - uint32_t num_resolved_types_; // Number of elements in the resolved_types_ array. - uint32_t num_strings_; // Number of elements in the strings_ array. + uint64_t dex_file_; // const DexFile* + uint64_t resolved_fields_; // ArtField*, array with num_resolved_fields_ elements. + uint64_t resolved_method_types_; // std::atomic<MethodTypeDexCachePair>* array with + // num_resolved_method_types_ elements. + uint64_t resolved_methods_; // ArtMethod*, array with num_resolved_methods_ elements. + uint64_t resolved_types_; // GcRoot<Class>*, array with num_resolved_types_ elements. + uint64_t strings_; // std::atomic<StringDexCachePair>*, array with num_strings_ + // elements. + + uint32_t num_resolved_fields_; // Number of elements in the resolved_fields_ array. + uint32_t num_resolved_method_types_; // Number of elements in the resolved_method_types_ array. + uint32_t num_resolved_methods_; // Number of elements in the resolved_methods_ array. + uint32_t num_resolved_types_; // Number of elements in the resolved_types_ array. + uint32_t num_strings_; // Number of elements in the strings_ array. friend struct art::DexCacheOffsets; // for verifying offset information friend class Object; // For VisitReferences diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc index ac04200e22..12301b8809 100644 --- a/runtime/mirror/dex_cache_test.cc +++ b/runtime/mirror/dex_cache_test.cc @@ -31,6 +31,14 @@ namespace mirror { class DexCacheTest : public CommonRuntimeTest {}; +class DexCacheMethodHandlesTest : public DexCacheTest { + protected: + virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE { + CommonRuntimeTest::SetUpRuntimeOptions(options); + options->push_back(std::make_pair("-Xexperimental:method-handles", nullptr)); + } +}; + TEST_F(DexCacheTest, Open) { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); @@ -46,21 +54,35 @@ TEST_F(DexCacheTest, Open) { EXPECT_EQ(java_lang_dex_file_->NumTypeIds(), dex_cache->NumResolvedTypes()); EXPECT_EQ(java_lang_dex_file_->NumMethodIds(), dex_cache->NumResolvedMethods()); EXPECT_EQ(java_lang_dex_file_->NumFieldIds(), dex_cache->NumResolvedFields()); + // This should always be zero because the -Xexperimental:method-handles isn't + // set. + EXPECT_EQ(0u, dex_cache->NumResolvedMethodTypes()); +} + +TEST_F(DexCacheMethodHandlesTest, Open) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + ASSERT_TRUE(java_lang_dex_file_ != nullptr); + Handle<DexCache> dex_cache( + hs.NewHandle(class_linker_->AllocDexCache(soa.Self(), + *java_lang_dex_file_, + Runtime::Current()->GetLinearAlloc()))); + + EXPECT_TRUE(dex_cache->StaticMethodTypeSize() == dex_cache->NumResolvedMethodTypes() + || java_lang_dex_file_->NumProtoIds() == dex_cache->NumResolvedMethodTypes()); } TEST_F(DexCacheTest, LinearAlloc) { ScopedObjectAccess soa(Thread::Current()); jobject jclass_loader(LoadDex("Main")); ASSERT_TRUE(jclass_loader != nullptr); - Runtime* const runtime = Runtime::Current(); - ClassLinker* const class_linker = runtime->GetClassLinker(); StackHandleScope<1> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader(hs.NewHandle( soa.Decode<mirror::ClassLoader>(jclass_loader))); - mirror::Class* klass = class_linker->FindClass(soa.Self(), "LMain;", class_loader); + mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader); ASSERT_TRUE(klass != nullptr); LinearAlloc* const linear_alloc = klass->GetClassLoader()->GetAllocator(); - EXPECT_NE(linear_alloc, runtime->GetLinearAlloc()); + EXPECT_NE(linear_alloc, runtime_->GetLinearAlloc()); EXPECT_TRUE(linear_alloc->Contains(klass->GetDexCache()->GetResolvedMethods())); } @@ -68,16 +90,14 @@ TEST_F(DexCacheTest, TestResolvedFieldAccess) { ScopedObjectAccess soa(Thread::Current()); jobject jclass_loader(LoadDex("Packages")); ASSERT_TRUE(jclass_loader != nullptr); - Runtime* const runtime = Runtime::Current(); - ClassLinker* const class_linker = runtime->GetClassLinker(); StackHandleScope<3> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader(hs.NewHandle( soa.Decode<mirror::ClassLoader>(jclass_loader))); Handle<mirror::Class> klass1 = - hs.NewHandle(class_linker->FindClass(soa.Self(), "Lpackage1/Package1;", class_loader)); + hs.NewHandle(class_linker_->FindClass(soa.Self(), "Lpackage1/Package1;", class_loader)); ASSERT_TRUE(klass1.Get() != nullptr); Handle<mirror::Class> klass2 = - hs.NewHandle(class_linker->FindClass(soa.Self(), "Lpackage2/Package2;", class_loader)); + hs.NewHandle(class_linker_->FindClass(soa.Self(), "Lpackage2/Package2;", class_loader)); ASSERT_TRUE(klass2.Get() != nullptr); EXPECT_EQ(klass1->GetDexCache(), klass2->GetDexCache()); @@ -92,5 +112,60 @@ TEST_F(DexCacheTest, TestResolvedFieldAccess) { } } +TEST_F(DexCacheMethodHandlesTest, TestResolvedMethodTypes) { + ScopedObjectAccess soa(Thread::Current()); + jobject jclass_loader(LoadDex("MethodTypes")); + ASSERT_TRUE(jclass_loader != nullptr); + + StackHandleScope<5> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(jclass_loader))); + + Handle<mirror::Class> method_types( + hs.NewHandle(class_linker_->FindClass(soa.Self(), "LMethodTypes;", class_loader))); + class_linker_->EnsureInitialized(soa.Self(), method_types, true, true); + + ArtMethod* method1 = method_types->FindVirtualMethod( + "method1", + "(Ljava/lang/String;)Ljava/lang/String;", + kRuntimePointerSize); + ArtMethod* method2 = method_types->FindVirtualMethod( + "method2", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", + kRuntimePointerSize); + + const DexFile& dex_file = *(method1->GetDexFile()); + Handle<mirror::DexCache> dex_cache = hs.NewHandle( + class_linker_->FindDexCache(Thread::Current(), dex_file)); + + const DexFile::MethodId& method1_id = dex_file.GetMethodId(method1->GetDexMethodIndex()); + const DexFile::MethodId& method2_id = dex_file.GetMethodId(method2->GetDexMethodIndex()); + + Handle<mirror::MethodType> method1_type = hs.NewHandle( + class_linker_->ResolveMethodType(dex_file, method1_id.proto_idx_, dex_cache, class_loader)); + Handle<mirror::MethodType> method2_type = hs.NewHandle( + class_linker_->ResolveMethodType(dex_file, method2_id.proto_idx_, dex_cache, class_loader)); + + EXPECT_EQ(method1_type.Get(), dex_cache->GetResolvedMethodType(method1_id.proto_idx_)); + EXPECT_EQ(method2_type.Get(), dex_cache->GetResolvedMethodType(method2_id.proto_idx_)); + + // The MethodTypes dex file contains a single interface with two abstract + // methods. It must therefore contain precisely two method IDs. + ASSERT_EQ(2u, dex_file.NumProtoIds()); + ASSERT_EQ(dex_file.NumProtoIds(), dex_cache->NumResolvedMethodTypes()); + MethodTypeDexCacheType* method_types_cache = dex_cache->GetResolvedMethodTypes(); + + for (size_t i = 0; i < dex_file.NumProtoIds(); ++i) { + const MethodTypeDexCachePair pair = method_types_cache[i].load(std::memory_order_relaxed); + if (pair.index == method1_id.proto_idx_) { + ASSERT_EQ(method1_type.Get(), pair.object.Read()); + } else if (pair.index == method2_id.proto_idx_) { + ASSERT_EQ(method2_type.Get(), pair.object.Read()); + } else { + ASSERT_TRUE(false); + } + } +} + } // namespace mirror } // namespace art diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 4f70b04088..f937ca7603 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -761,6 +761,8 @@ void ParsedOptions::Usage(const char* fmt, ...) { "(Enable new and experimental agent support)\n"); UsageMessage(stream, " -Xexperimental:agents" "(Enable new and experimental agent support)\n"); + UsageMessage(stream, " -Xexperimental:method-handles" + "(Enable new and experimental method handles support)\n"); UsageMessage(stream, "\n"); UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n"); diff --git a/runtime/runtime.h b/runtime/runtime.h index 9e63564d58..30f1b4a431 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -314,6 +314,10 @@ class Runtime { return "2.1.0"; } + bool IsMethodHandlesEnabled() const { + return experimental_flags_ & ExperimentalFlags::kMethodHandles; + } + void DisallowNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_); void AllowNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_); void BroadcastForNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 146afc7ad8..b01a570269 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -117,7 +117,7 @@ RUNTIME_OPTIONS_KEY (unsigned int, ZygoteMaxFailedBoots, 10) RUNTIME_OPTIONS_KEY (Unit, NoDexFileFallback) RUNTIME_OPTIONS_KEY (std::string, CpuAbiList) RUNTIME_OPTIONS_KEY (std::string, Fingerprint) -RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::kNone) // -Xexperimental:{none, agents} +RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::kNone) // -Xexperimental:{none, agents, method-handles} RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentLib) // -agentlib:<libname>=<options>, Requires -Xexperimental:agents RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentPath) // -agentpath:<libname>=<options>, Requires -Xexperimental:agents RUNTIME_OPTIONS_KEY (std::vector<Plugin>, Plugins) // -Xplugin:<library> Requires -Xexperimental:runtime-plugins diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h index a85d0332c3..5ccd446e0f 100644 --- a/runtime/utils/dex_cache_arrays_layout-inl.h +++ b/runtime/utils/dex_cache_arrays_layout-inl.h @@ -38,8 +38,10 @@ inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size, RoundUp(methods_offset_ + MethodsSize(header.method_ids_size_), StringsAlignment())), fields_offset_( RoundUp(strings_offset_ + StringsSize(header.string_ids_size_), FieldsAlignment())), + method_types_offset_( + RoundUp(fields_offset_ + FieldsSize(header.field_ids_size_), Alignment())), size_( - RoundUp(fields_offset_ + FieldsSize(header.field_ids_size_), Alignment())) { + RoundUp(method_types_offset_ + MethodTypesSize(header.proto_ids_size_), Alignment())) { } inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size, const DexFile* dex_file) @@ -118,6 +120,27 @@ inline size_t DexCacheArraysLayout::FieldsAlignment() const { return static_cast<size_t>(pointer_size_); } +inline size_t DexCacheArraysLayout::MethodTypeOffset(uint32_t proto_idx) const { + return strings_offset_ + + ElementOffset(PointerSize::k64, + proto_idx % mirror::DexCache::kDexCacheMethodTypeCacheSize); +} + +inline size_t DexCacheArraysLayout::MethodTypesSize(size_t num_elements) const { + size_t cache_size = mirror::DexCache::kDexCacheMethodTypeCacheSize; + if (num_elements < cache_size) { + cache_size = num_elements; + } + + return ArraySize(PointerSize::k64, cache_size); +} + +inline size_t DexCacheArraysLayout::MethodTypesAlignment() const { + static_assert(alignof(mirror::MethodTypeDexCacheType) == 8, + "alignof(MethodTypeDexCacheType) != 8"); + return alignof(mirror::MethodTypeDexCacheType); +} + 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 20ffa90592..e222b46ef7 100644 --- a/runtime/utils/dex_cache_arrays_layout.h +++ b/runtime/utils/dex_cache_arrays_layout.h @@ -35,6 +35,7 @@ class DexCacheArraysLayout { methods_offset_(0u), strings_offset_(0u), fields_offset_(0u), + method_types_offset_(0u), size_(0u) { } @@ -94,12 +95,23 @@ class DexCacheArraysLayout { size_t FieldsAlignment() const; + size_t MethodTypesOffset() const { + return method_types_offset_; + } + + size_t MethodTypeOffset(uint32_t method_type_idx) const; + + size_t MethodTypesSize(size_t num_elements) const; + + size_t MethodTypesAlignment() const; + private: static constexpr size_t types_offset_ = 0u; const PointerSize pointer_size_; // Must be first for construction initialization order. const size_t methods_offset_; const size_t strings_offset_; const size_t fields_offset_; + const size_t method_types_offset_; const size_t size_; static size_t Alignment(PointerSize pointer_size); diff --git a/test/MethodTypes/MethodTypes.java b/test/MethodTypes/MethodTypes.java new file mode 100644 index 0000000000..f6f8e08e2f --- /dev/null +++ b/test/MethodTypes/MethodTypes.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +public interface MethodTypes { + public String method1(String a); + public String method2(String a, String b); +} |