diff options
author | 2023-11-09 18:07:24 +0100 | |
---|---|---|
committer | 2023-11-20 10:38:07 +0000 | |
commit | 4f2fcccce50f229cb2b00cc07c040473a59c120b (patch) | |
tree | 2f5a6d61b796b95ea63e60bea47cbf6d9be2c482 | |
parent | 58310a99bcda9a45bc1e07f8d36f4c847f345457 (diff) |
Do not create `MethodType` during early init...
... when interpreting `VarHandle` invoke-polymorphic.
Use `VariableSizedHandleScope` as a raw method type that can
be converted to the managed `MethodType` when desires but
can also be used directly without the conversion. Add helper
templates that facilitate using either the raw method type
or the managed `MethodType` easily by templated code.
Change `VarHandleInvokeAccessorWithConversions()` to avoid
allocating the `MethodType` (avoid unnecessary work).
Change `VarHandle` invokes in the interpreter to avoid
allocating the `MethodType` when the `ThreadLocalRandom`
is not initialized. This can avoid circular initialization
when we reland
https://android-review.googlesource.com/2769639 .
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 297147201
Change-Id: I6a9372543a547366b28e5bf49d15d6140a75f770
-rw-r--r-- | runtime/class_linker.cc | 75 | ||||
-rw-r--r-- | runtime/class_linker.h | 9 | ||||
-rw-r--r-- | runtime/handle_scope-inl.h | 37 | ||||
-rw-r--r-- | runtime/handle_scope.h | 5 | ||||
-rw-r--r-- | runtime/handle_scope_test.cc | 10 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_common.cc | 87 | ||||
-rw-r--r-- | runtime/method_handles-inl.h | 100 | ||||
-rw-r--r-- | runtime/method_handles.cc | 17 | ||||
-rw-r--r-- | runtime/method_handles.h | 73 | ||||
-rw-r--r-- | runtime/method_handles_test.cc | 69 | ||||
-rw-r--r-- | runtime/mirror/method_type-inl.h | 110 | ||||
-rw-r--r-- | runtime/mirror/method_type.cc | 86 | ||||
-rw-r--r-- | runtime/mirror/method_type.h | 97 | ||||
-rw-r--r-- | runtime/mirror/var_handle.cc | 89 | ||||
-rw-r--r-- | runtime/mirror/var_handle.h | 19 | ||||
-rw-r--r-- | runtime/var_handles.cc | 102 | ||||
-rw-r--r-- | runtime/var_handles.h | 13 | ||||
-rw-r--r-- | runtime/well_known_classes.cc | 9 | ||||
-rw-r--r-- | runtime/well_known_classes.h | 1 |
19 files changed, 700 insertions, 308 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 3b0dda299e..42027cc223 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -118,7 +118,7 @@ #include "mirror/method.h" #include "mirror/method_handle_impl.h" #include "mirror/method_handles_lookup.h" -#include "mirror/method_type.h" +#include "mirror/method_type-inl.h" #include "mirror/object-inl.h" #include "mirror/object-refvisitor-inl.h" #include "mirror/object.h" @@ -10112,58 +10112,59 @@ ObjPtr<mirror::MethodType> ClassLinker::ResolveMethodType( return resolved; } - StackHandleScope<4> hs(self); + VariableSizedHandleScope raw_method_type_hs(self); + mirror::RawMethodType raw_method_type(&raw_method_type_hs); + if (!ResolveMethodType(self, proto_idx, dex_cache, class_loader, raw_method_type)) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + // The handle scope was filled with return type and paratemer types. + DCHECK_EQ(raw_method_type_hs.Size(), + dex_cache->GetDexFile()->GetShortyView(proto_idx).length()); + ObjPtr<mirror::MethodType> method_type = mirror::MethodType::Create(self, raw_method_type); + if (method_type != nullptr) { + // Ensure all stores for the newly created MethodType are visible, before we attempt to place + // it in the DexCache (b/224733324). + std::atomic_thread_fence(std::memory_order_release); + dex_cache->SetResolvedMethodType(proto_idx, method_type.Ptr()); + } + return method_type; +} + +bool ClassLinker::ResolveMethodType(Thread* self, + dex::ProtoIndex proto_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + /*out*/ mirror::RawMethodType method_type) { + DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); + DCHECK(dex_cache != nullptr); + DCHECK(dex_cache->GetClassLoader() == class_loader.Get()); // First resolve the return type. const DexFile& dex_file = *dex_cache->GetDexFile(); const dex::ProtoId& proto_id = dex_file.GetProtoId(proto_idx); - Handle<mirror::Class> return_type(hs.NewHandle( - ResolveType(proto_id.return_type_idx_, dex_cache, class_loader))); + ObjPtr<mirror::Class> return_type = + ResolveType(proto_id.return_type_idx_, dex_cache, class_loader); if (return_type == nullptr) { DCHECK(self->IsExceptionPending()); - return nullptr; + return false; } + method_type.SetRType(return_type); // 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; - - ObjPtr<mirror::Class> array_of_class = GetClassRoot<mirror::ObjectArray<mirror::Class>>(this); - Handle<mirror::ObjectArray<mirror::Class>> method_params(hs.NewHandle( - mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, num_method_args))); - if (method_params == 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 dex::TypeIndex type_idx = it.GetTypeIdx(); - param_class.Assign(ResolveType(type_idx, dex_cache, class_loader)); - if (param_class == nullptr) { + ObjPtr<mirror::Class> param_type = ResolveType(type_idx, dex_cache, class_loader); + if (param_type == nullptr) { DCHECK(self->IsExceptionPending()); - return nullptr; + return false; } - - method_params->Set(i++, param_class.Get()); + method_type.AddPType(param_type); } - DCHECK(!it.HasNext()); - - Handle<mirror::MethodType> type = hs.NewHandle( - mirror::MethodType::Create(self, return_type, method_params)); - if (type != nullptr) { - // Ensure all stores for the newly created MethodType are visible, before we attempt to place - // it in the DexCache (b/224733324). - std::atomic_thread_fence(std::memory_order_release); - dex_cache->SetResolvedMethodType(proto_idx, type.Get()); - } - - return type.Get(); + return true; } ObjPtr<mirror::MethodType> ClassLinker::ResolveMethodType(Thread* self, diff --git a/runtime/class_linker.h b/runtime/class_linker.h index e2ac03a58d..f1cede1756 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -97,6 +97,7 @@ class MethodHandle; class MethodHandlesLookup; class MethodType; template<class T> class ObjectArray; +class RawMethodType; class StackTraceElement; } // namespace mirror @@ -448,6 +449,14 @@ class ClassLinker { ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_); + bool ResolveMethodType(Thread* self, + dex::ProtoIndex proto_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + /*out*/ mirror::RawMethodType method_type) + 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. diff --git a/runtime/handle_scope-inl.h b/runtime/handle_scope-inl.h index 1874237174..e620e92e1f 100644 --- a/runtime/handle_scope-inl.h +++ b/runtime/handle_scope-inl.h @@ -19,6 +19,7 @@ #include "handle_scope.h" +#include "base/casts.h" #include "base/mutex.h" #include "handle.h" #include "handle_wrapper.h" @@ -246,12 +247,12 @@ inline uint32_t VariableSizedHandleScope::Size() const { DCHECK(cur != nullptr); // The linked list of local scopes starts from the latest which may not be fully filled. uint32_t sum = cur->Size(); - cur = reinterpret_cast<const LocalScopeType*>(cur->GetLink()); + cur = down_cast<const LocalScopeType*>(cur->GetLink()); while (cur != nullptr) { // All other local scopes are fully filled. DCHECK_EQ(cur->Size(), kNumReferencesPerScope); sum += kNumReferencesPerScope; - cur = reinterpret_cast<const LocalScopeType*>(cur->GetLink()); + cur = down_cast<const LocalScopeType*>(cur->GetLink()); } return sum; } @@ -262,7 +263,7 @@ inline uint32_t VariableSizedHandleScope::Capacity() const { while (cur != nullptr) { DCHECK_EQ(cur->Capacity(), kNumReferencesPerScope); sum += kNumReferencesPerScope; - cur = reinterpret_cast<const LocalScopeType*>(cur->GetLink()); + cur = down_cast<const LocalScopeType*>(cur->GetLink()); } return sum; } @@ -274,17 +275,41 @@ inline bool VariableSizedHandleScope::Contains(StackReference<mirror::Object>* h if (cur->Contains(handle_scope_entry)) { return true; } - cur = reinterpret_cast<const LocalScopeType*>(cur->GetLink()); + cur = down_cast<const LocalScopeType*>(cur->GetLink()); } return false; } +template<class T> +Handle<T> VariableSizedHandleScope::GetHandle(size_t i) { + // Handle the most common path efficiently. + if (i < kNumReferencesPerScope) { + return first_scope_.GetHandle<T>(i); + } + + uint32_t size = Size(); + DCHECK_GT(size, kNumReferencesPerScope); + DCHECK_LT(i, size); + LocalScopeType* cur = current_scope_; + DCHECK(cur != &first_scope_); + // The linked list of local scopes starts from the latest which may not be fully filled. + uint32_t cur_start = size - cur->Size(); + DCHECK_EQ(cur_start % kNumReferencesPerScope, 0u); // All other local scopes are fully filled. + while (i < cur_start) { + cur = down_cast<LocalScopeType*>(cur->GetLink()); + DCHECK(cur != nullptr); + DCHECK_EQ(cur->Size(), kNumReferencesPerScope); + cur_start -= kNumReferencesPerScope; + } + return cur->GetHandle<T>(i - cur_start); +} + template <typename Visitor> inline void VariableSizedHandleScope::VisitRoots(Visitor& visitor) { LocalScopeType* cur = current_scope_; while (cur != nullptr) { cur->VisitRoots(visitor); - cur = reinterpret_cast<LocalScopeType*>(cur->GetLink()); + cur = down_cast<LocalScopeType*>(cur->GetLink()); } } @@ -293,7 +318,7 @@ inline void VariableSizedHandleScope::VisitHandles(Visitor& visitor) { LocalScopeType* cur = current_scope_; while (cur != nullptr) { cur->VisitHandles(visitor); - cur = reinterpret_cast<LocalScopeType*>(cur->GetLink()); + cur = down_cast<LocalScopeType*>(cur->GetLink()); } } diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h index 4cf6e5e6fe..791023085a 100644 --- a/runtime/handle_scope.h +++ b/runtime/handle_scope.h @@ -244,6 +244,11 @@ class VariableSizedHandleScope : public BaseHandleScope { // The current capacity of this handle scope. ALWAYS_INLINE uint32_t Capacity() const; + // Retrieve a `Handle<>` based on the slot index (in handle creation order). + // Note: This is linear in the size of the scope, so it should be used carefully. + template<class T> + ALWAYS_INLINE Handle<T> GetHandle(size_t i) REQUIRES_SHARED(Locks::mutator_lock_); + ALWAYS_INLINE bool Contains(StackReference<mirror::Object>* handle_scope_entry) const; template <typename Visitor> diff --git a/runtime/handle_scope_test.cc b/runtime/handle_scope_test.cc index fe85f25d96..2e45bdae4a 100644 --- a/runtime/handle_scope_test.cc +++ b/runtime/handle_scope_test.cc @@ -97,15 +97,16 @@ class CollectVisitor { TEST_F(HandleScopeTest, VariableSized) { ScopedObjectAccess soa(Thread::Current()); VariableSizedHandleScope hs(soa.Self()); + std::vector<Handle<mirror::Object>> handles; ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); Handle<mirror::Class> c = hs.NewHandle(class_linker->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); + handles.push_back(c); // Test nested scopes. StackHandleScope<1> inner(soa.Self()); inner.NewHandle(c->AllocObject(soa.Self())); // Add a bunch of handles and make sure callbacks work. static const size_t kNumHandles = 100; - std::vector<Handle<mirror::Object>> handles; for (size_t i = 0; i < kNumHandles; ++i) { BaseHandleScope* base = &hs; ObjPtr<mirror::Object> o = c->AllocObject(soa.Self()); @@ -116,7 +117,8 @@ TEST_F(HandleScopeTest, VariableSized) { EXPECT_EQ(hs.Capacity(), base->Capacity()); } // Add one null handle. - hs.NewHandle<mirror::Object>(nullptr); + Handle<mirror::Object> null_handle = hs.NewHandle<mirror::Object>(nullptr); + handles.push_back(null_handle); CollectVisitor visitor; BaseHandleScope* base = &hs; base->VisitRoots(visitor); @@ -125,6 +127,10 @@ TEST_F(HandleScopeTest, VariableSized) { for (StackReference<mirror::Object>* ref : visitor.visited) { EXPECT_TRUE(base->Contains(ref)); } + // Test `VariableSizedHandleScope::GetHandle<.>()`. + for (size_t i = 0, size = handles.size(); i != size; ++i) { + EXPECT_EQ(handles[i].GetReference(), hs.GetHandle<mirror::Object>(i).GetReference()); + } } } // namespace art diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 28da3da3f7..cafe3c4418 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -452,36 +452,55 @@ static bool DoVarHandleInvokeCommon(Thread* self, return false; } - StackHandleScope<2> hs(self); bool is_var_args = inst->HasVarArgs(); - const uint16_t vRegH = is_var_args ? inst->VRegH_45cc() : inst->VRegH_4rcc(); - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - Handle<mirror::MethodType> callsite_type(hs.NewHandle( - class_linker->ResolveMethodType(self, dex::ProtoIndex(vRegH), shadow_frame.GetMethod()))); - // This implies we couldn't resolve one or more types in this VarHandle. - if (UNLIKELY(callsite_type == nullptr)) { - CHECK(self->IsExceptionPending()); - return false; - } - const uint32_t vRegC = is_var_args ? inst->VRegC_45cc() : inst->VRegC_4rcc(); - ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(vRegC)); - Handle<mirror::VarHandle> var_handle(hs.NewHandle(ObjPtr<mirror::VarHandle>::DownCast(receiver))); + const uint16_t vRegH = is_var_args ? inst->VRegH_45cc() : inst->VRegH_4rcc(); + StackHandleScope<4> hs(self); + Handle<mirror::VarHandle> var_handle = hs.NewHandle( + ObjPtr<mirror::VarHandle>::DownCast(shadow_frame.GetVRegReference(vRegC))); + ArtMethod* method = shadow_frame.GetMethod(); + Handle<mirror::DexCache> dex_cache = hs.NewHandle(method->GetDexCache()); + Handle<mirror::ClassLoader> class_loader = hs.NewHandle(method->GetClassLoader()); + uint32_t var_args[Instruction::kMaxVarArgRegs]; + std::optional<VarArgsInstructionOperands> var_args_operands(std::nullopt); + std::optional<RangeInstructionOperands> range_operands(std::nullopt); + InstructionOperands* all_operands; if (is_var_args) { - uint32_t args[Instruction::kMaxVarArgRegs]; - inst->GetVarArgs(args, inst_data); - VarArgsInstructionOperands all_operands(args, inst->VRegA_45cc()); - NoReceiverInstructionOperands operands(&all_operands); - return VarHandleInvokeAccessor(self, - shadow_frame, - var_handle, - callsite_type, - access_mode, - &operands, - result); + inst->GetVarArgs(var_args, inst_data); + var_args_operands.emplace(var_args, inst->VRegA_45cc()); + all_operands = &var_args_operands.value(); } else { - RangeInstructionOperands all_operands(inst->VRegC_4rcc(), inst->VRegA_4rcc()); - NoReceiverInstructionOperands operands(&all_operands); + range_operands.emplace(inst->VRegC_4rcc(), inst->VRegA_4rcc()); + all_operands = &range_operands.value(); + } + NoReceiverInstructionOperands operands(all_operands); + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + + // TODO: The "`MethodType` caching" comment below refers to code implemented by + // https://android-review.googlesource.com/2768053 which has been reverted. + // Reland that CL and remove this part of the comment when doing so. + // + // If the `ThreadLocalRandom` class is not yet initialized, do the `VarHandle` operation + // without creating a managed `MethodType` object. This avoids a circular initialization + // issue when `ThreadLocalRandom.<clinit>` indirectly calls `AtomicLong.compareAndSet()` + // (implemented with a `VarHandle`) and the `MethodType` caching circles back to the + // `ThreadLocalRandom` with uninitialized `seeder` and throws NPE. + // + // Do a quick test for "visibly initialized" without a read barrier and, if that fails, + // do a thorough test for "initialized" (including load acquire) with the read barrier. + ArtField* field = WellKnownClasses::java_util_concurrent_ThreadLocalRandom_seeder; + if (UNLIKELY(!field->GetDeclaringClass<kWithoutReadBarrier>()->IsVisiblyInitialized()) && + !field->GetDeclaringClass()->IsInitialized()) { + VariableSizedHandleScope callsite_type_hs(self); + mirror::RawMethodType callsite_type(&callsite_type_hs); + if (!class_linker->ResolveMethodType(self, + dex::ProtoIndex(vRegH), + dex_cache, + class_loader, + callsite_type)) { + CHECK(self->IsExceptionPending()); + return false; + } return VarHandleInvokeAccessor(self, shadow_frame, var_handle, @@ -490,6 +509,22 @@ static bool DoVarHandleInvokeCommon(Thread* self, &operands, result); } + + Handle<mirror::MethodType> callsite_type(hs.NewHandle( + class_linker->ResolveMethodType(self, dex::ProtoIndex(vRegH), dex_cache, class_loader))); + // This implies we couldn't resolve one or more types in this VarHandle. + if (UNLIKELY(callsite_type == nullptr)) { + CHECK(self->IsExceptionPending()); + return false; + } + + return VarHandleInvokeAccessor(self, + shadow_frame, + var_handle, + callsite_type, + access_mode, + &operands, + result); } #define DO_VAR_HANDLE_ACCESSOR(_access_mode) \ diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h index 1a1507a06c..cdf9c837f8 100644 --- a/runtime/method_handles-inl.h +++ b/runtime/method_handles-inl.h @@ -105,20 +105,18 @@ class ShadowFrameSetter { size_t arg_index_; }; -inline bool ConvertArgumentValue(Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> callee_type, - ObjPtr<mirror::Class> from_class, - ObjPtr<mirror::Class> to_class, - JValue* value) REQUIRES_SHARED(Locks::mutator_lock_) { - if (from_class == to_class) { +inline bool ConvertArgumentValue(const ThrowWrongMethodTypeFunction& throw_wmt, + ObjPtr<mirror::Class> from, + ObjPtr<mirror::Class> to, + /*inout*/ JValue* value) { + if (from == to) { return true; } - // |value| may contain a bare heap pointer which is generally - // |unsafe. ConvertJValueCommon() saves |value|, |from_class|, and - // |to_class| to Handles where necessary to avoid issues if the heap - // changes. - if (ConvertJValueCommon(callsite_type, callee_type, from_class, to_class, value)) { + // `*value` may contain a bare heap pointer which is generally unsafe. + // `ConvertJValueCommon()` saves `*value`, `from`, and `to` to Handles + // where necessary to avoid issues if the heap changes. + if (ConvertJValueCommon(throw_wmt, from, to, value)) { DCHECK(!Thread::Current()->IsExceptionPending()); return true; } else { @@ -128,31 +126,18 @@ inline bool ConvertArgumentValue(Handle<mirror::MethodType> callsite_type, } } -inline bool ConvertArgumentValue(Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> callee_type, - int index, - JValue* value) REQUIRES_SHARED(Locks::mutator_lock_) { - return ConvertArgumentValue(callsite_type, - callee_type, - callsite_type->GetPTypes()->GetWithoutChecks(index), - callee_type->GetPTypes()->GetWithoutChecks(index), - value); -} - -inline bool ConvertReturnValue(Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> callee_type, - JValue* value) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr<mirror::Class> from_class(callee_type->GetRType()); - ObjPtr<mirror::Class> to_class(callsite_type->GetRType()); - if (to_class->GetPrimitiveType() == Primitive::kPrimVoid || from_class == to_class) { +inline bool ConvertReturnValue(const ThrowWrongMethodTypeFunction& throw_wmt, + ObjPtr<mirror::Class> from, + ObjPtr<mirror::Class> to, + /*inout*/ JValue* value) { + if (to->GetPrimitiveType() == Primitive::kPrimVoid || from == to) { return true; } - // |value| may contain a bare heap pointer which is generally - // unsafe. ConvertJValueCommon() saves |value|, |from_class|, and - // |to_class| to Handles where necessary to avoid issues if the heap - // changes. - if (ConvertJValueCommon(callsite_type, callee_type, from_class, to_class, value)) { + // `*value` may contain a bare heap pointer which is generally unsafe. + // `ConvertJValueCommon()` saves `*value`, `from`, and `to` to Handles + // where necessary to avoid issues if the heap changes. + if (ConvertJValueCommon(throw_wmt, from, to, value)) { DCHECK(!Thread::Current()->IsExceptionPending()); return true; } else { @@ -162,21 +147,16 @@ inline bool ConvertReturnValue(Handle<mirror::MethodType> callsite_type, } } -template <typename G, typename S> -bool PerformConversions(Thread* self, - Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> callee_type, +template <typename FromPTypes, typename ToPTypes, typename G, typename S> +bool PerformConversions(const ThrowWrongMethodTypeFunction& throw_wmt, + FromPTypes from_types, + ToPTypes to_types, G* getter, - S* setter, - int32_t start_index, - int32_t end_index) REQUIRES_SHARED(Locks::mutator_lock_) { - StackHandleScope<2> hs(self); - Handle<mirror::ObjectArray<mirror::Class>> from_types(hs.NewHandle(callsite_type->GetPTypes())); - Handle<mirror::ObjectArray<mirror::Class>> to_types(hs.NewHandle(callee_type->GetPTypes())); - - for (int32_t i = start_index; i < end_index; ++i) { - ObjPtr<mirror::Class> from(from_types->GetWithoutChecks(i)); - ObjPtr<mirror::Class> to(to_types->GetWithoutChecks(i - start_index)); + S* setter) { + DCHECK_EQ(from_types.GetLength(), to_types.GetLength()); + for (int32_t i = 0, length = to_types.GetLength(); i != length; ++i) { + ObjPtr<mirror::Class> from = from_types.Get(i); + ObjPtr<mirror::Class> to = to_types.Get(i); const Primitive::Type from_type = from->GetPrimitiveType(); const Primitive::Type to_type = to->GetPrimitiveType(); if (from == to) { @@ -199,8 +179,8 @@ bool PerformConversions(Thread* self, value.SetI(getter->Get()); } // Caveat emptor - ObjPtr's not guaranteed valid after this call. - if (!ConvertArgumentValue(callsite_type, callee_type, from, to, &value)) { - DCHECK(self->IsExceptionPending()); + if (!ConvertArgumentValue(throw_wmt, from, to, &value)) { + DCHECK(Thread::Current()->IsExceptionPending()); return false; } if (Primitive::Is64BitType(to_type)) { @@ -216,28 +196,6 @@ bool PerformConversions(Thread* self, } template <typename G, typename S> -bool PerformConversions(Thread* self, - Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> callee_type, - G* getter, - S* setter, - int32_t num_conversions) - REQUIRES_SHARED(Locks::mutator_lock_) { - return PerformConversions(self, callsite_type, callee_type, getter, setter, 0, num_conversions); -} - -template <typename G, typename S> -bool PerformConversions(Thread* self, - Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> callee_type, - G* getter, - S* setter) - REQUIRES_SHARED(Locks::mutator_lock_) { - int32_t num_conversions = callee_type->GetPTypes()->GetLength(); - return PerformConversions(self, callsite_type, callee_type, getter, setter, 0, num_conversions); -} - -template <typename G, typename S> bool CopyArguments(Thread* self, Handle<mirror::MethodType> method_type, G* getter, diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index c8c6ef9a73..a8f7a84e94 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -197,11 +197,10 @@ bool IsReturnTypeConvertible(ObjPtr<mirror::Class> from, ObjPtr<mirror::Class> t } bool ConvertJValueCommon( - Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> callee_type, + const ThrowWrongMethodTypeFunction& throw_wmt, ObjPtr<mirror::Class> from, ObjPtr<mirror::Class> to, - JValue* value) { + /*inout*/ JValue* value) { // The reader maybe concerned about the safety of the heap object // that may be in |value|. There is only one case where allocation // is obviously needed and that's for boxing. However, in the case @@ -226,7 +225,7 @@ bool ConvertJValueCommon( if (IsPrimitiveType(from_type) && IsPrimitiveType(to_type)) { // The source and target types are both primitives. if (UNLIKELY(!ConvertPrimitiveValueNoThrow(from_type, to_type, src_value, value))) { - ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get()); + throw_wmt(); return false; } return true; @@ -258,18 +257,18 @@ bool ConvertJValueCommon( if (LIKELY(boxed_from_class->IsSubClass(to))) { type = from_type; } else { - ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get()); + throw_wmt(); return false; } } if (UNLIKELY(from_type != type)) { - ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get()); + throw_wmt(); return false; } if (UNLIKELY(!ConvertPrimitiveValueNoThrow(from_type, type, src_value, value))) { - ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get()); + throw_wmt(); return false; } @@ -300,7 +299,7 @@ bool ConvertJValueCommon( Primitive::Type unboxed_type; JValue unboxed_value; if (UNLIKELY(!GetUnboxedTypeAndValue(from_obj, &unboxed_type, &unboxed_value))) { - ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get()); + throw_wmt(); return false; } @@ -311,7 +310,7 @@ bool ConvertJValueCommon( ThrowClassCastException(from, to); } else { // CallSite is incompatible, e.g. Integer for a short. - ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get()); + throw_wmt(); } return false; } diff --git a/runtime/method_handles.h b/runtime/method_handles.h index 510b6e1678..d439d6238e 100644 --- a/runtime/method_handles.h +++ b/runtime/method_handles.h @@ -44,42 +44,47 @@ bool IsParameterTypeConvertible(ObjPtr<mirror::Class> from, bool IsReturnTypeConvertible(ObjPtr<mirror::Class> from, ObjPtr<mirror::Class> to); -// Performs a conversion from type |from| to a distinct type |to| as -// part of conversion of |caller_type| to |callee_type|. The value to -// be converted is in |value|. Returns true on success and updates -// |value| with the converted value, false otherwise. -bool ConvertJValueCommon(Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> callee_type, +// Interface for throwing `WrongMethodTypeException` by conversion functions. +class ThrowWrongMethodTypeFunction { + public: + virtual void operator()() const REQUIRES_SHARED(Locks::mutator_lock_) = 0; + + protected: + ~ThrowWrongMethodTypeFunction() {} +}; + +// Performs a conversion from type `from` to a distinct type `to`. +// The value to be converted is in `*value`. Returns true on success +// and updates `*value` with the converted value, false otherwise. +bool ConvertJValueCommon(const ThrowWrongMethodTypeFunction& throw_wmt, ObjPtr<mirror::Class> from, ObjPtr<mirror::Class> to, - JValue* value) + /*inout*/ JValue* value) REQUIRES_SHARED(Locks::mutator_lock_); -// Converts the value of the argument at position |index| from type -// expected by |callee_type| to type used by |callsite_type|. |value| -// represents the value to be converted. Returns true on success and -// updates |value|, false otherwise. -ALWAYS_INLINE bool ConvertArgumentValue(Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> callee_type, - int index, - JValue* value) +// Converts the value of the argument from type `from` to type `to`. +// `*value` represents the value to be converted. Returns true on success +// and updates `*value`, false otherwise. +ALWAYS_INLINE bool ConvertArgumentValue(const ThrowWrongMethodTypeFunction& throw_wmt, + ObjPtr<mirror::Class> from, + ObjPtr<mirror::Class> to, + /*inout*/ JValue* value) REQUIRES_SHARED(Locks::mutator_lock_); -// Converts the return value from return type yielded by -// |callee_type| to the return type yielded by -// |callsite_type|. |value| represents the value to be -// converted. Returns true on success and updates |value|, false -// otherwise. -ALWAYS_INLINE bool ConvertReturnValue(Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> callee_type, - JValue* value) +// Converts the return value from return type `from` to the return type `to`. +// `*value` represents the value to be converted. Returns true on success and +// updates `*value`, false otherwise. +ALWAYS_INLINE bool ConvertReturnValue(const ThrowWrongMethodTypeFunction& throw_wmt, + ObjPtr<mirror::Class> from, + ObjPtr<mirror::Class> to, + /*inout*/ JValue* value) REQUIRES_SHARED(Locks::mutator_lock_); -// Perform argument conversions between |callsite_type| (the type of the -// incoming arguments) and |callee_type| (the type of the method being -// invoked). These include widening and narrowing conversions as well as -// boxing and unboxing. Returns true on success, on false on failure. A -// pending exception will always be set on failure. +// Perform argument conversions between `from_types` (the types of the incoming +// arguments) and `to_types` (the parameter types of the method being invoked). +// These include widening and narrowing conversions as well as boxing and +// unboxing. Returns true on success, false on failure. A pending exception +// will always be set on failure. // // The values to be converted are read from an input source (of type G) // that provides three methods : @@ -119,14 +124,12 @@ ALWAYS_INLINE bool ConvertReturnValue(Handle<mirror::MethodType> callsite_type, // TODO(narayan): If we find that the instantiations of this function take // up too much space, we can make G / S abstract base classes that are // overridden by concrete classes. -template <typename G, typename S> -bool PerformConversions(Thread* self, - Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> callee_type, +template <typename FromPTypes, typename ToPTypes, typename G, typename S> +bool PerformConversions(const ThrowWrongMethodTypeFunction& throw_wmt, + FromPTypes from_types, + ToPTypes to_types, G* getter, - S* setter, - int32_t start_index, - int32_t end_index) REQUIRES_SHARED(Locks::mutator_lock_); + S* setter) REQUIRES_SHARED(Locks::mutator_lock_); template <typename G, typename S> bool CopyArguments(Thread* self, diff --git a/runtime/method_handles_test.cc b/runtime/method_handles_test.cc index 588f8612d0..d7740e3118 100644 --- a/runtime/method_handles_test.cc +++ b/runtime/method_handles_test.cc @@ -46,28 +46,15 @@ namespace { return throwable->GetClass()->DescriptorEquals("Ljava/lang/invoke/WrongMethodTypeException;"); } - static ObjPtr<mirror::MethodType> CreateVoidMethodType(Thread* self, - Handle<mirror::Class> parameter_type) - REQUIRES_SHARED(Locks::mutator_lock_) { - ClassLinker* cl = Runtime::Current()->GetClassLinker(); - StackHandleScope<2> hs(self); - ObjPtr<mirror::Class> class_array_type = GetClassRoot<mirror::ObjectArray<mirror::Class>>(cl); - auto parameter_types = hs.NewHandle( - mirror::ObjectArray<mirror::Class>::Alloc(self, class_array_type, 1)); - parameter_types->Set(0, parameter_type.Get()); - Handle<mirror::Class> void_class = hs.NewHandle(GetClassRoot(ClassRoot::kPrimitiveVoid, cl)); - return mirror::MethodType::Create(self, void_class, parameter_types); - } - - static bool TryConversion(Thread* self, - Handle<mirror::Class> from, - Handle<mirror::Class> to, - JValue* value) + static bool TryConversion(Handle<mirror::Class> from, Handle<mirror::Class> to, JValue* value) REQUIRES_SHARED(Locks::mutator_lock_) { - StackHandleScope<2> hs(self); - Handle<mirror::MethodType> from_mt = hs.NewHandle(CreateVoidMethodType(self, from)); - Handle<mirror::MethodType> to_mt = hs.NewHandle(CreateVoidMethodType(self, to)); - return ConvertJValueCommon(from_mt, to_mt, from.Get(), to.Get(), value); + class ThrowWrongMethodTypeFunctionImpl final : public ThrowWrongMethodTypeFunction { + void operator()() const override REQUIRES_SHARED(Locks::mutator_lock_) { + ThrowWrongMethodTypeException("<callee-descriptor>", "<callsite-descriptor>"); + } + }; + ThrowWrongMethodTypeFunctionImpl throw_wmt; + return ConvertJValueCommon(throw_wmt, from.Get(), to.Get(), value); } } // namespace @@ -89,7 +76,7 @@ TEST_F(MethodHandlesTest, SupportedPrimitiveWideningBI) { Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('B')); Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I')); JValue value = JValue::FromPrimitive(static_cast<int8_t>(3)); - ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(TryConversion(from, to, &value)); ASSERT_EQ(3, value.GetI()); ASSERT_FALSE(soa.Self()->IsExceptionPending()); } @@ -102,7 +89,7 @@ TEST_F(MethodHandlesTest, SupportedPrimitiveWideningCJ) { Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('J')); uint16_t raw_value = 0x8000; JValue value = JValue::FromPrimitive(raw_value); - ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(TryConversion(from, to, &value)); ASSERT_FALSE(soa.Self()->IsExceptionPending()); ASSERT_EQ(static_cast<int64_t>(raw_value), value.GetJ()); } @@ -114,7 +101,7 @@ TEST_F(MethodHandlesTest, SupportedPrimitiveWideningIF) { Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I')); Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('F')); JValue value = JValue::FromPrimitive(-16); - ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(TryConversion(from, to, &value)); ASSERT_FALSE(soa.Self()->IsExceptionPending()); ASSERT_FLOAT_EQ(-16.0f, value.GetF()); } @@ -127,7 +114,7 @@ TEST_F(MethodHandlesTest, UnsupportedPrimitiveWideningBC) { Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('C')); JValue value; value.SetB(0); - ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(TryConversion(from, to, &value)); ASSERT_TRUE(soa.Self()->IsExceptionPending()); ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); soa.Self()->ClearException(); @@ -141,7 +128,7 @@ TEST_F(MethodHandlesTest, UnsupportedPrimitiveWideningSC) { Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('C')); JValue value; value.SetS(0x1234); - ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(TryConversion(from, to, &value)); ASSERT_TRUE(soa.Self()->IsExceptionPending()); ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); soa.Self()->ClearException(); @@ -155,7 +142,7 @@ TEST_F(MethodHandlesTest, UnsupportedPrimitiveWideningDJ) { Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('J')); JValue value; value.SetD(1e72); - ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(TryConversion(from, to, &value)); ASSERT_TRUE(soa.Self()->IsExceptionPending()); ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); soa.Self()->ClearException(); @@ -169,7 +156,7 @@ TEST_F(MethodHandlesTest, UnsupportedPrimitiveWideningZI) { Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I')); JValue value; value.SetZ(true); - ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(TryConversion(from, to, &value)); ASSERT_TRUE(soa.Self()->IsExceptionPending()); ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); soa.Self()->ClearException(); @@ -189,7 +176,7 @@ TEST_F(MethodHandlesTest, SupportedReferenceCast) { Handle<mirror::Class> from = hs.NewHandle(boxed_value->GetClass()); Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Number;")); value.SetL(boxed_value.Get()); - ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(TryConversion(from, to, &value)); ASSERT_FALSE(soa.Self()->IsExceptionPending()); JValue unboxed_value; ASSERT_TRUE(UnboxPrimitiveForResult(value.GetL(), cl->FindPrimitiveClass('I'), &unboxed_value)); @@ -206,7 +193,7 @@ TEST_F(MethodHandlesTest, UnsupportedReferenceCast) { Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); value.SetL(boxed_value.Get()); ASSERT_FALSE(soa.Self()->IsExceptionPending()); - ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(TryConversion(from, to, &value)); ASSERT_TRUE(soa.Self()->IsExceptionPending()); ASSERT_TRUE(IsClassCastException(soa.Self()->GetException())); soa.Self()->ClearException(); @@ -224,7 +211,7 @@ TEST_F(MethodHandlesTest, SupportedPrimitiveConversionPrimitiveToBoxed) { JValue value = JValue::FromPrimitive(kInitialValue); Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I')); Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); - ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(TryConversion(from, to, &value)); ASSERT_FALSE(soa.Self()->IsExceptionPending()); JValue unboxed_to_value; ASSERT_TRUE(UnboxPrimitiveForResult(value.GetL(), from.Get(), &unboxed_to_value)); @@ -239,7 +226,7 @@ TEST_F(MethodHandlesTest, SupportedPrimitiveConversionPrimitiveToBoxedSuper) { JValue value = JValue::FromPrimitive(kInitialValue); Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I')); Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Number;")); - ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(TryConversion(from, to, &value)); ASSERT_FALSE(soa.Self()->IsExceptionPending()); JValue unboxed_to_value; ASSERT_TRUE(UnboxPrimitiveForResult(value.GetL(), from.Get(), &unboxed_to_value)); @@ -254,7 +241,7 @@ TEST_F(MethodHandlesTest, UnsupportedPrimitiveConversionNotBoxable) { JValue value = JValue::FromPrimitive(kInitialValue); Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I')); Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Runtime;")); - ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(TryConversion(from, to, &value)); ASSERT_TRUE(soa.Self()->IsExceptionPending()); ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); soa.Self()->ClearException(); @@ -268,7 +255,7 @@ TEST_F(MethodHandlesTest, UnsupportedPrimitiveConversionPrimitiveToBoxedWider) { JValue value = JValue::FromPrimitive(kInitialValue); Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I')); Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Long;")); - ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(TryConversion(from, to, &value)); ASSERT_TRUE(soa.Self()->IsExceptionPending()); ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); soa.Self()->ClearException(); @@ -282,7 +269,7 @@ TEST_F(MethodHandlesTest, UnsupportedPrimitiveConversionPrimitiveToBoxedNarrower JValue value = JValue::FromPrimitive(kInitialValue); Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I')); Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Byte;")); - ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(TryConversion(from, to, &value)); ASSERT_TRUE(soa.Self()->IsExceptionPending()); ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); soa.Self()->ClearException(); @@ -302,7 +289,7 @@ TEST_F(MethodHandlesTest, SupportedBoxedToPrimitiveConversion) { Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I')); value.SetL(boxed_value.Get()); - ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(TryConversion(from, to, &value)); ASSERT_FALSE(soa.Self()->IsExceptionPending()); ASSERT_EQ(kInitialValue, value.GetI()); } @@ -317,7 +304,7 @@ TEST_F(MethodHandlesTest, SupportedBoxedToWiderPrimitiveConversion) { Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('J')); value.SetL(boxed_value.Get()); - ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_TRUE(TryConversion(from, to, &value)); ASSERT_EQ(kInitialValue, value.GetJ()); } @@ -330,7 +317,7 @@ TEST_F(MethodHandlesTest, UnsupportedNullBoxedToPrimitiveConversion) { Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I')); value.SetL(boxed_value.Get()); - ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(TryConversion(from, to, &value)); ASSERT_TRUE(soa.Self()->IsExceptionPending()); ASSERT_TRUE(IsNullPointerException(soa.Self()->GetException())); soa.Self()->ClearException(); @@ -345,7 +332,7 @@ TEST_F(MethodHandlesTest, UnsupportedNotBoxReferenceToPrimitiveConversion) { // Set value to be converted as some non-primitive type. JValue value; value.SetL(cl->FindPrimitiveClass('V')); - ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(TryConversion(from, to, &value)); ASSERT_TRUE(soa.Self()->IsExceptionPending()); ASSERT_TRUE(IsClassCastException(soa.Self()->GetException())); soa.Self()->ClearException(); @@ -361,7 +348,7 @@ TEST_F(MethodHandlesTest, UnsupportedBoxedToNarrowerPrimitiveConversionNoCast) { Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('S')); value.SetL(boxed_value.Get()); - ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(TryConversion(from, to, &value)); ASSERT_TRUE(soa.Self()->IsExceptionPending()); ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException())); soa.Self()->ClearException(); @@ -377,7 +364,7 @@ TEST_F(MethodHandlesTest, UnsupportedBoxedToNarrowerPrimitiveConversionWithCast) Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Number;")); Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('F')); value.SetL(boxed_value.Get()); - ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value)); + ASSERT_FALSE(TryConversion(from, to, &value)); ASSERT_TRUE(soa.Self()->IsExceptionPending()); ASSERT_TRUE(IsClassCastException(soa.Self()->GetException())); soa.Self()->ClearException(); diff --git a/runtime/mirror/method_type-inl.h b/runtime/mirror/method_type-inl.h index 86b8099f19..fca87fe944 100644 --- a/runtime/mirror/method_type-inl.h +++ b/runtime/mirror/method_type-inl.h @@ -19,11 +19,56 @@ #include "method_type.h" +#include "base/casts.h" +#include "handle_scope-inl.h" #include "mirror/object-inl.h" namespace art { namespace mirror { +inline RawMethodType::RawMethodType(VariableSizedHandleScope* hs) + : hs_(hs) { + DCHECK(hs != nullptr); +} + +inline bool RawMethodType::IsValid() const { + return hs_->Size() != 0u; +} + +inline void RawMethodType::SetRType(ObjPtr<mirror::Class> rtype) { + DCHECK(rtype != nullptr); + DCHECK_EQ(hs_->Size(), 0u); + hs_->NewHandle(rtype); + DCHECK_EQ(rtype, GetRType()); +} + +inline void RawMethodType::AddPType(ObjPtr<mirror::Class> ptype) { + DCHECK(ptype != nullptr); + DCHECK_NE(hs_->Size(), 0u); + hs_->NewHandle(ptype); + DCHECK_NE(GetNumberOfPTypes(), 0); + DCHECK_EQ(GetPType(GetNumberOfPTypes() - 1), ptype); +} + +inline int32_t RawMethodType::GetNumberOfPTypes() const { + DCHECK_NE(hs_->Size(), 0u); + return dchecked_integral_cast<int32_t>(hs_->Size() - 1u); +} + +inline ObjPtr<mirror::Class> RawMethodType::GetPType(int32_t i) const { + DCHECK_LT(i, GetNumberOfPTypes()); + return hs_->GetHandle<mirror::Class>(i + 1).Get(); +} + +inline ObjPtr<mirror::Class> RawMethodType::GetRType() const { + return GetRTypeHandle().Get(); +} + +inline Handle<mirror::Class> RawMethodType::GetRTypeHandle() const { + DCHECK_NE(hs_->Size(), 0u); + return hs_->GetHandle<mirror::Class>(0u); +} + inline ObjPtr<ObjectArray<Class>> MethodType::GetPTypes() { return GetFieldObject<ObjectArray<Class>>(OFFSET_OF_OBJECT_MEMBER(MethodType, p_types_)); } @@ -36,6 +81,71 @@ inline ObjPtr<Class> MethodType::GetRType() { return GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(MethodType, r_type_)); } +template <typename PTypesType> +inline MethodType::PTypesAccessor<PTypesType>::PTypesAccessor(PTypesType p_types) + : p_types_(p_types) {} + +template <typename PTypesType> +inline int32_t MethodType::PTypesAccessor<PTypesType>::GetLength() const { + return p_types_->GetLength(); +} + +template <typename PTypesType> +inline ObjPtr<mirror::Class> MethodType::PTypesAccessor<PTypesType>::Get(int32_t i) const { + DCHECK_LT(i, GetLength()); + return p_types_->GetWithoutChecks(i); +} + +inline MethodType::RawPTypesAccessor::RawPTypesAccessor(RawMethodType method_type) + : method_type_(method_type) { + DCHECK(method_type.IsValid()); +} + +inline int32_t MethodType::RawPTypesAccessor::GetLength() const { + return method_type_.GetNumberOfPTypes(); +} + +inline ObjPtr<mirror::Class> MethodType::RawPTypesAccessor::Get(int32_t i) const { + return method_type_.GetPType(i); +} + +template <typename HandleScopeType> +inline MethodType::HandlePTypesAccessor MethodType::NewHandlePTypes( + Handle<MethodType> method_type, HandleScopeType* hs) { + Handle<ObjectArray<mirror::Class>> p_types = hs->NewHandle(method_type->GetPTypes()); + return HandlePTypesAccessor(p_types); +} + +template <typename HandleScopeType> +inline MethodType::RawPTypesAccessor MethodType::NewHandlePTypes( + RawMethodType method_type, [[maybe_unused]] HandleScopeType* hs) { + return RawPTypesAccessor(method_type); +} + +inline MethodType::ObjPtrPTypesAccessor MethodType::GetPTypes(ObjPtr<MethodType> method_type) { + return ObjPtrPTypesAccessor(method_type->GetPTypes()); +} + +inline MethodType::ObjPtrPTypesAccessor MethodType::GetPTypes(Handle<MethodType> method_type) { + return GetPTypes(method_type.Get()); +} + +inline MethodType::RawPTypesAccessor MethodType::GetPTypes(RawMethodType method_type) { + return RawPTypesAccessor(method_type); +} + +inline ObjPtr<mirror::Class> MethodType::GetRType(ObjPtr<MethodType> method_type) { + return method_type->GetRType(); +} + +inline ObjPtr<mirror::Class> MethodType::GetRType(Handle<MethodType> method_type) { + return GetRType(method_type.Get()); +} + +inline ObjPtr<mirror::Class> MethodType::GetRType(RawMethodType method_type) { + return method_type.GetRType(); +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc index ccc2b9d679..fc3dcb969a 100644 --- a/runtime/mirror/method_type.cc +++ b/runtime/mirror/method_type.cc @@ -18,6 +18,7 @@ #include "class-alloc-inl.h" #include "class_root-inl.h" +#include "handle_scope-inl.h" #include "method_handles.h" #include "obj_ptr-inl.h" #include "object_array-alloc-inl.h" @@ -36,7 +37,7 @@ ObjPtr<ObjectArray<Class>> AllocatePTypesArray(Thread* self, int count) } // namespace -ObjPtr<MethodType> MethodType::Create(Thread* const self, +ObjPtr<MethodType> MethodType::Create(Thread* self, Handle<Class> return_type, Handle<ObjectArray<Class>> parameter_types) { StackHandleScope<1> hs(self); @@ -64,7 +65,29 @@ ObjPtr<MethodType> MethodType::Create(Thread* const self, return mt.Get(); } -ObjPtr<MethodType> MethodType::CloneWithoutLeadingParameter(Thread* const self, +ObjPtr<MethodType> MethodType::Create(Thread* self, RawMethodType method_type) { + Handle<mirror::Class> return_type = method_type.GetRTypeHandle(); + RawPTypesAccessor p_types(method_type); + int32_t num_method_args = p_types.GetLength(); + + // Create the argument types array. + StackHandleScope<1u> hs(self); + Handle<mirror::ObjectArray<mirror::Class>> method_params = hs.NewHandle( + mirror::ObjectArray<mirror::Class>::Alloc( + self, GetClassRoot<mirror::ObjectArray<mirror::Class>>(), num_method_args)); + if (method_params == nullptr) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + for (int32_t i = 0; i != num_method_args; ++i) { + method_params->Set(i, p_types.Get(i)); + } + + return Create(self, return_type, method_params); +} + +ObjPtr<MethodType> MethodType::CloneWithoutLeadingParameter(Thread* self, ObjPtr<MethodType> method_type) { StackHandleScope<3> hs(self); Handle<ObjectArray<Class>> src_ptypes = hs.NewHandle(method_type->GetPTypes()); @@ -104,15 +127,16 @@ ObjPtr<MethodType> MethodType::CollectTrailingArguments(Thread* self, return Create(self, dst_rtype, dst_ptypes); } -size_t MethodType::NumberOfVRegs() { - const ObjPtr<ObjectArray<Class>> p_types = GetPTypes(); - const int32_t p_types_length = p_types->GetLength(); +template <typename MethodTypeType> +size_t NumberOfVRegsImpl(MethodTypeType method_type) REQUIRES_SHARED(Locks::mutator_lock_) { + auto p_types = MethodType::GetPTypes(method_type); + 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) { - ObjPtr<Class> klass = p_types->GetWithoutChecks(i); + ObjPtr<Class> klass = p_types.Get(i); if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) { ++num_vregs; } @@ -120,6 +144,24 @@ size_t MethodType::NumberOfVRegs() { return num_vregs; } +size_t MethodType::NumberOfVRegs() { + return NumberOfVRegs(this); +} + +size_t MethodType::NumberOfVRegs(ObjPtr<mirror::MethodType> method_type) { + DCHECK(method_type != nullptr); + return NumberOfVRegsImpl(method_type); +} + +size_t MethodType::NumberOfVRegs(Handle<mirror::MethodType> method_type) { + return NumberOfVRegs(method_type.Get()); +} + +size_t MethodType::NumberOfVRegs(RawMethodType method_type) { + DCHECK(method_type.IsValid()); + return NumberOfVRegsImpl(method_type); +} + bool MethodType::IsExactMatch(ObjPtr<MethodType> target) { const ObjPtr<ObjectArray<Class>> p_types = GetPTypes(); const int32_t params_length = p_types->GetLength(); @@ -212,24 +254,46 @@ bool MethodType::IsInPlaceConvertible(ObjPtr<MethodType> target) { IsParameterInPlaceConvertible(target->GetRType(), GetRType()); } -std::string MethodType::PrettyDescriptor() { +template <typename MethodTypeType> +std::string PrettyDescriptorImpl(MethodTypeType method_type) + REQUIRES_SHARED(Locks::mutator_lock_) { + auto p_types = MethodType::GetPTypes(method_type); + ObjPtr<mirror::Class> r_type = MethodType::GetRType(method_type); + std::ostringstream ss; ss << "("; - const ObjPtr<ObjectArray<Class>> p_types = GetPTypes(); - const int32_t params_length = p_types->GetLength(); + const int32_t params_length = p_types.GetLength(); for (int32_t i = 0; i < params_length; ++i) { - ss << p_types->GetWithoutChecks(i)->PrettyDescriptor(); + ss << p_types.Get(i)->PrettyDescriptor(); if (i != (params_length - 1)) { ss << ", "; } } ss << ")"; - ss << GetRType()->PrettyDescriptor(); + ss << r_type->PrettyDescriptor(); return ss.str(); } +std::string MethodType::PrettyDescriptor() { + return PrettyDescriptor(this); +} + +std::string MethodType::PrettyDescriptor(ObjPtr<mirror::MethodType> method_type) { + DCHECK(method_type != nullptr); + return PrettyDescriptorImpl(method_type); +} + +std::string MethodType::PrettyDescriptor(Handle<MethodType> method_type) { + return PrettyDescriptor(method_type.Get()); +} + +std::string MethodType::PrettyDescriptor(RawMethodType method_type) { + DCHECK(method_type.IsValid()); + return PrettyDescriptorImpl(method_type); +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h index 19444bb6ce..bbb4a47f19 100644 --- a/runtime/mirror/method_type.h +++ b/runtime/mirror/method_type.h @@ -24,26 +24,52 @@ namespace art { struct MethodTypeOffsets; +class VariableSizedHandleScope; namespace mirror { +// We use a wrapped `VariableSizedHandleScope` as a raw method type without allocating a managed +// object. It must contain the return type followed by argument types and no other handles. +// The data is filled by calling `SetRType()` followed by `AddPType()` for each argument. +class RawMethodType { + public: + explicit RawMethodType(VariableSizedHandleScope* hs); + + bool IsValid() const; + + void SetRType(ObjPtr<mirror::Class> rtype) REQUIRES_SHARED(Locks::mutator_lock_); + void AddPType(ObjPtr<mirror::Class> ptype) REQUIRES_SHARED(Locks::mutator_lock_); + + int32_t GetNumberOfPTypes() const REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<mirror::Class> GetPType(int32_t i) const REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<mirror::Class> GetRType() const REQUIRES_SHARED(Locks::mutator_lock_); + Handle<mirror::Class> GetRTypeHandle() const REQUIRES_SHARED(Locks::mutator_lock_); + + private: + VariableSizedHandleScope* hs_; +}; + // C++ mirror of java.lang.invoke.MethodType class MANAGED MethodType : public Object { public: MIRROR_CLASS("Ljava/lang/invoke/MethodType;"); - static ObjPtr<MethodType> Create(Thread* const self, + static ObjPtr<MethodType> Create(Thread* self, Handle<Class> return_type, Handle<ObjectArray<Class>> param_types) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static ObjPtr<MethodType> CloneWithoutLeadingParameter(Thread* const self, + // Create a `MethodType` from a `RawMethodType`. + static ObjPtr<MethodType> Create(Thread* self, RawMethodType method_type) + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); + + static ObjPtr<MethodType> CloneWithoutLeadingParameter(Thread* self, ObjPtr<MethodType> method_type) REQUIRES_SHARED(Locks::mutator_lock_); // Collects trailing parameter types into an array. Assumes caller // has checked trailing arguments are all of the same type. - static ObjPtr<MethodType> CollectTrailingArguments(Thread* const self, + static ObjPtr<MethodType> CollectTrailingArguments(Thread* self, ObjPtr<MethodType> method_type, ObjPtr<Class> collector_array_class, int32_t start_index) @@ -76,6 +102,71 @@ class MANAGED MethodType : public Object { // exception messages and the like. std::string PrettyDescriptor() REQUIRES_SHARED(Locks::mutator_lock_); + // The `PTypesType` is either `ObjPtr<>` or `Handle<>`. + template <typename PTypesType> + class PTypesAccessor { + public: + explicit PTypesAccessor(PTypesType p_types) REQUIRES_SHARED(Locks::mutator_lock_); + + int32_t GetLength() const REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<mirror::Class> Get(int32_t i) const REQUIRES_SHARED(Locks::mutator_lock_); + + private: + static_assert(std::is_same_v<PTypesType, ObjPtr<ObjectArray<Class>>> || + std::is_same_v<PTypesType, Handle<ObjectArray<Class>>>); + + const PTypesType p_types_; + }; + + using ObjPtrPTypesAccessor = PTypesAccessor<ObjPtr<ObjectArray<Class>>>; + using HandlePTypesAccessor = PTypesAccessor<Handle<ObjectArray<Class>>>; + + class RawPTypesAccessor { + public: + explicit RawPTypesAccessor(RawMethodType method_type); + + int32_t GetLength() const REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<mirror::Class> Get(int32_t i) const REQUIRES_SHARED(Locks::mutator_lock_); + + private: + RawMethodType method_type_; + }; + + template <typename HandleScopeType> + static HandlePTypesAccessor NewHandlePTypes(Handle<MethodType> method_type, HandleScopeType* hs) + REQUIRES_SHARED(Locks::mutator_lock_); + template <typename HandleScopeType> + static RawPTypesAccessor NewHandlePTypes(RawMethodType method_type, HandleScopeType* hs) + REQUIRES_SHARED(Locks::mutator_lock_); + + static ObjPtrPTypesAccessor GetPTypes(ObjPtr<MethodType> method_type) + REQUIRES_SHARED(Locks::mutator_lock_); + static ObjPtrPTypesAccessor GetPTypes(Handle<MethodType> method_type) + REQUIRES_SHARED(Locks::mutator_lock_); + static RawPTypesAccessor GetPTypes(RawMethodType method_type) + REQUIRES_SHARED(Locks::mutator_lock_); + + static ObjPtr<mirror::Class> GetRType(ObjPtr<MethodType> method_type) + REQUIRES_SHARED(Locks::mutator_lock_); + static ObjPtr<mirror::Class> GetRType(Handle<MethodType> method_type) + REQUIRES_SHARED(Locks::mutator_lock_); + static ObjPtr<mirror::Class> GetRType(RawMethodType method_type) + REQUIRES_SHARED(Locks::mutator_lock_); + + static size_t NumberOfVRegs(ObjPtr<mirror::MethodType> method_type) + REQUIRES_SHARED(Locks::mutator_lock_); + static size_t NumberOfVRegs(Handle<mirror::MethodType> method_type) + REQUIRES_SHARED(Locks::mutator_lock_); + static size_t NumberOfVRegs(RawMethodType method_type) + REQUIRES_SHARED(Locks::mutator_lock_); + + static std::string PrettyDescriptor(ObjPtr<MethodType> method_type) + REQUIRES_SHARED(Locks::mutator_lock_); + static std::string PrettyDescriptor(Handle<MethodType> method_type) + REQUIRES_SHARED(Locks::mutator_lock_); + static std::string PrettyDescriptor(RawMethodType method_type) + REQUIRES_SHARED(Locks::mutator_lock_); + private: static MemberOffset FormOffset() { return MemberOffset(OFFSETOF_MEMBER(MethodType, form_)); diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc index 2220f92e75..c923075099 100644 --- a/runtime/mirror/var_handle.cc +++ b/runtime/mirror/var_handle.cc @@ -1335,13 +1335,13 @@ int32_t VarHandle::GetAccessModesBitMask() { return GetField32(AccessModesBitMaskOffset()); } -VarHandle::MatchKind VarHandle::GetMethodTypeMatchForAccessMode(AccessMode access_mode, - ObjPtr<MethodType> method_type) { +template <typename MethodTypeType> +VarHandle::MatchKind VarHandle::GetMethodTypeMatchForAccessModeImpl( + AccessMode access_mode, ObjPtr<VarHandle> var_handle, MethodTypeType method_type) { MatchKind match = MatchKind::kExact; - ObjPtr<VarHandle> vh = this; - ObjPtr<Class> var_type = vh->GetVarType(); - ObjPtr<Class> mt_rtype = method_type->GetRType(); + ObjPtr<Class> var_type = var_handle->GetVarType(); + ObjPtr<Class> mt_rtype = MethodType::GetRType(method_type); ObjPtr<Class> void_type = WellKnownClasses::ToClass(WellKnownClasses::java_lang_Void); AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); @@ -1358,28 +1358,28 @@ VarHandle::MatchKind VarHandle::GetMethodTypeMatchForAccessMode(AccessMode acces } // Check the number of parameters matches. - ObjPtr<Class> vh_ptypes[VarHandle::kMaxAccessorParameters]; + ObjPtr<Class> vh_ptypes[kMaxAccessorParameters]; const int32_t vh_ptypes_count = BuildParameterArray(vh_ptypes, access_mode_template, var_type, - GetCoordinateType0(), - GetCoordinateType1()); - if (vh_ptypes_count != method_type->GetPTypes()->GetLength()) { + var_handle->GetCoordinateType0(), + var_handle->GetCoordinateType1()); + auto mt_ptypes = MethodType::GetPTypes(method_type); + if (vh_ptypes_count != mt_ptypes.GetLength()) { return MatchKind::kNone; } // Check the parameter types are compatible. - ObjPtr<ObjectArray<Class>> mt_ptypes = method_type->GetPTypes(); for (int32_t i = 0; i < vh_ptypes_count; ++i) { - if (vh_ptypes[i]->IsAssignableFrom(mt_ptypes->Get(i))) { + if (vh_ptypes[i]->IsAssignableFrom(mt_ptypes.Get(i))) { continue; } - if (mt_ptypes->Get(i) == void_type && !vh_ptypes[i]->IsPrimitive()) { + if (mt_ptypes.Get(i) == void_type && !vh_ptypes[i]->IsPrimitive()) { // The expected parameter is a reference and the parameter type from the call site is j.l.Void // which means the value is null. It is always valid for a reference parameter to be null. continue; } - if (!IsParameterTypeConvertible(mt_ptypes->Get(i), vh_ptypes[i])) { + if (!IsParameterTypeConvertible(mt_ptypes.Get(i), vh_ptypes[i])) { return MatchKind::kNone; } match = MatchKind::kWithConversions; @@ -1387,39 +1387,46 @@ VarHandle::MatchKind VarHandle::GetMethodTypeMatchForAccessMode(AccessMode acces return match; } -ObjPtr<MethodType> VarHandle::GetMethodTypeForAccessMode(Thread* self, - ObjPtr<VarHandle> var_handle, - AccessMode access_mode) { - // This is a static method as the var_handle might be moved by the GC during it's execution. - AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); +VarHandle::MatchKind VarHandle::GetMethodTypeMatchForAccessMode( + AccessMode access_mode, ObjPtr<MethodType> method_type) { + return GetMethodTypeMatchForAccessModeImpl(access_mode, this, method_type); +} - StackHandleScope<3> hs(self); - Handle<VarHandle> vh = hs.NewHandle(var_handle); - Handle<Class> rtype = hs.NewHandle(GetReturnType(access_mode_template, vh->GetVarType())); - const int32_t ptypes_count = GetNumberOfParameters(access_mode_template, - vh->GetCoordinateType0(), - vh->GetCoordinateType1()); - ObjPtr<Class> array_of_class = GetClassRoot<ObjectArray<Class>>(); - Handle<ObjectArray<Class>> ptypes = - hs.NewHandle(ObjectArray<Class>::Alloc(Thread::Current(), array_of_class, ptypes_count)); - if (ptypes == nullptr) { - return nullptr; - } +VarHandle::MatchKind VarHandle::GetMethodTypeMatchForAccessMode( + AccessMode access_mode, Handle<MethodType> method_type) { + return GetMethodTypeMatchForAccessMode(access_mode, method_type.Get()); +} - ObjPtr<Class> ptypes_array[VarHandle::kMaxAccessorParameters]; - BuildParameterArray(ptypes_array, - access_mode_template, - vh->GetVarType(), - vh->GetCoordinateType0(), - vh->GetCoordinateType1()); - for (int32_t i = 0; i < ptypes_count; ++i) { - ptypes->Set(i, ptypes_array[i]); - } - return MethodType::Create(self, rtype, ptypes); +VarHandle::MatchKind VarHandle::GetMethodTypeMatchForAccessMode( + AccessMode access_mode, RawMethodType method_type) { + return GetMethodTypeMatchForAccessModeImpl(access_mode, this, method_type); } ObjPtr<MethodType> VarHandle::GetMethodTypeForAccessMode(Thread* self, AccessMode access_mode) { - return GetMethodTypeForAccessMode(self, this, access_mode); + VariableSizedHandleScope method_type_hs(self); + RawMethodType method_type(&method_type_hs); + GetMethodTypeForAccessMode(access_mode, method_type); + return MethodType::Create(self, method_type); +} + +void VarHandle::GetMethodTypeForAccessMode(AccessMode access_mode, + /*out*/ RawMethodType method_type) { + DCHECK(!method_type.IsValid()); + AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); + + // Store return type in `method_type`. + method_type.SetRType(GetReturnType(access_mode_template, GetVarType())); + + // Store parameter types in `method_type`. + ObjPtr<Class> ptypes_array[kMaxAccessorParameters]; + int32_t ptypes_count = BuildParameterArray(ptypes_array, + access_mode_template, + GetVarType(), + GetCoordinateType0(), + GetCoordinateType1()); + for (int32_t i = 0; i < ptypes_count; ++i) { + method_type.AddPType(ptypes_array[i]); + } } std::string VarHandle::PrettyDescriptorForAccessMode(AccessMode access_mode) { diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h index 18e0c3a482..1419bd013f 100644 --- a/runtime/mirror/var_handle.h +++ b/runtime/mirror/var_handle.h @@ -43,6 +43,7 @@ class ShadowFrameGetter; namespace mirror { class MethodType; +class RawMethodType; class VarHandleTest; // C++ mirror of java.lang.invoke.VarHandle @@ -128,12 +129,21 @@ class MANAGED VarHandle : public Object { // 'access_mode' and the provided 'method_type'. MatchKind GetMethodTypeMatchForAccessMode(AccessMode access_mode, ObjPtr<MethodType> method_type) REQUIRES_SHARED(Locks::mutator_lock_); + MatchKind GetMethodTypeMatchForAccessMode(AccessMode access_mode, Handle<MethodType> method_type) + REQUIRES_SHARED(Locks::mutator_lock_); + MatchKind GetMethodTypeMatchForAccessMode(AccessMode access_mode, RawMethodType method_type) + REQUIRES_SHARED(Locks::mutator_lock_); // Allocates and returns the MethodType associated with the // AccessMode. No check is made for whether the AccessMode is a // supported operation so the MethodType can be used when raising a // WrongMethodTypeException exception. - ObjPtr<MethodType> GetMethodTypeForAccessMode(Thread* self, AccessMode accessMode) + ObjPtr<MethodType> GetMethodTypeForAccessMode(Thread* self, AccessMode access_mode) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Overload that fills a handle scope with the return type and argument types + // instead of creating an actual `MethodType`. + void GetMethodTypeForAccessMode(AccessMode access_mode, /*out*/ RawMethodType method_type) REQUIRES_SHARED(Locks::mutator_lock_); // Returns a string representing the descriptor of the MethodType associated with @@ -193,10 +203,11 @@ class MANAGED VarHandle : public Object { ObjPtr<Class> GetCoordinateType1() REQUIRES_SHARED(Locks::mutator_lock_); int32_t GetAccessModesBitMask() REQUIRES_SHARED(Locks::mutator_lock_); - static ObjPtr<MethodType> GetMethodTypeForAccessMode(Thread* self, + template <typename MethodTypeType> + static MatchKind GetMethodTypeMatchForAccessModeImpl(AccessMode access_mode, ObjPtr<VarHandle> var_handle, - AccessMode access_mode) - REQUIRES_SHARED(Locks::mutator_lock_); + MethodTypeType method_type) + REQUIRES_SHARED(Locks::mutator_lock_); HeapReference<mirror::Class> coordinate_type0_; HeapReference<mirror::Class> coordinate_type1_; diff --git a/runtime/var_handles.cc b/runtime/var_handles.cc index 0c7e3bd476..884dd5c8a1 100644 --- a/runtime/var_handles.cc +++ b/runtime/var_handles.cc @@ -27,44 +27,80 @@ namespace art { namespace { +template <typename CallSiteType, typename CalleeType> +class ThrowWrongMethodTypeFunctionImpl final : public ThrowWrongMethodTypeFunction { + public: + ThrowWrongMethodTypeFunctionImpl(CallSiteType callsite_type, CalleeType callee_type) + : callsite_type_(callsite_type), + callee_type_(callee_type) {} + + ~ThrowWrongMethodTypeFunctionImpl() {} + + void operator()() const override REQUIRES_SHARED(Locks::mutator_lock_) { + ThrowWrongMethodTypeException(mirror::MethodType::PrettyDescriptor(callee_type_), + mirror::MethodType::PrettyDescriptor(callsite_type_)); + } + + private: + CallSiteType callsite_type_; + CalleeType callee_type_; +}; + +template <typename CallSiteType> bool VarHandleInvokeAccessorWithConversions(Thread* self, ShadowFrame& shadow_frame, Handle<mirror::VarHandle> var_handle, - Handle<mirror::MethodType> callsite_type, - const mirror::VarHandle::AccessMode access_mode, - const InstructionOperands* const operands, + CallSiteType callsite_type, + mirror::VarHandle::AccessMode access_mode, + const InstructionOperands* operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - StackHandleScope<1> hs(self); - Handle<mirror::MethodType> accessor_type(hs.NewHandle( - var_handle->GetMethodTypeForAccessMode(self, access_mode))); - const size_t num_vregs = accessor_type->NumberOfVRegs(); - const int num_params = accessor_type->GetPTypes()->GetLength(); + // Use a raw method handle for `accessor_type`, avoid allocating a managed `MethodType`. + VariableSizedHandleScope accessor_type_hs(self); + mirror::RawMethodType accessor_type(&accessor_type_hs); + var_handle->GetMethodTypeForAccessMode(access_mode, accessor_type); + using HandleScopeType = std::conditional_t< + std::is_same_v<VariableSizedHandleScope*, CallSiteType>, + Thread*, // No handle scope needed, use `Thread*` that can be initialized from `self`. + StackHandleScope<3>>; + HandleScopeType hs(self); + ThrowWrongMethodTypeFunctionImpl throw_wmt(callsite_type, accessor_type); + auto from_types = mirror::MethodType::NewHandlePTypes(callsite_type, &hs); + auto to_types = mirror::MethodType::NewHandlePTypes(accessor_type, &hs); + const size_t num_vregs = mirror::MethodType::NumberOfVRegs(accessor_type); ShadowFrameAllocaUniquePtr accessor_frame = CREATE_SHADOW_FRAME(num_vregs, shadow_frame.GetMethod(), shadow_frame.GetDexPC()); ShadowFrameGetter getter(shadow_frame, operands); static const uint32_t kFirstDestinationReg = 0; ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg); - if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) { + if (!PerformConversions(throw_wmt, from_types, to_types, &getter, &setter)) { + DCHECK(self->IsExceptionPending()); return false; } RangeInstructionOperands accessor_operands(kFirstDestinationReg, kFirstDestinationReg + num_vregs); if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) { + DCHECK(self->IsExceptionPending()); return false; } - return ConvertReturnValue(callsite_type, accessor_type, result); + if (!ConvertReturnValue(throw_wmt, + mirror::MethodType::GetRType(accessor_type), + mirror::MethodType::GetRType(callsite_type), + result)) { + DCHECK(self->IsExceptionPending()); + return false; + } + return true; } -} // namespace - -bool VarHandleInvokeAccessor(Thread* self, - ShadowFrame& shadow_frame, - Handle<mirror::VarHandle> var_handle, - Handle<mirror::MethodType> callsite_type, - const mirror::VarHandle::AccessMode access_mode, - const InstructionOperands* const operands, - JValue* result) { +template <typename CallSiteType> +bool VarHandleInvokeAccessorImpl(Thread* self, + ShadowFrame& shadow_frame, + Handle<mirror::VarHandle> var_handle, + CallSiteType callsite_type, + const mirror::VarHandle::AccessMode access_mode, + const InstructionOperands* const operands, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { if (var_handle.IsNull()) { ThrowNullPointerExceptionFromDexPC(); return false; @@ -76,7 +112,7 @@ bool VarHandleInvokeAccessor(Thread* self, } mirror::VarHandle::MatchKind match_kind = - var_handle->GetMethodTypeMatchForAccessMode(access_mode, callsite_type.Get()); + var_handle->GetMethodTypeMatchForAccessMode(access_mode, callsite_type); if (LIKELY(match_kind == mirror::VarHandle::MatchKind::kExact)) { return var_handle->Access(access_mode, &shadow_frame, operands, result); } else if (match_kind == mirror::VarHandle::MatchKind::kWithConversions) { @@ -90,9 +126,33 @@ bool VarHandleInvokeAccessor(Thread* self, } else { DCHECK_EQ(match_kind, mirror::VarHandle::MatchKind::kNone); ThrowWrongMethodTypeException(var_handle->PrettyDescriptorForAccessMode(access_mode), - callsite_type->PrettyDescriptor()); + mirror::MethodType::PrettyDescriptor(callsite_type)); return false; } } +} // namespace + +bool VarHandleInvokeAccessor(Thread* self, + ShadowFrame& shadow_frame, + Handle<mirror::VarHandle> var_handle, + Handle<mirror::MethodType> callsite_type, + const mirror::VarHandle::AccessMode access_mode, + const InstructionOperands* const operands, + JValue* result) { + return VarHandleInvokeAccessorImpl( + self, shadow_frame, var_handle, callsite_type, access_mode, operands, result); +} + +bool VarHandleInvokeAccessor(Thread* self, + ShadowFrame& shadow_frame, + Handle<mirror::VarHandle> var_handle, + mirror::RawMethodType callsite_type, + const mirror::VarHandle::AccessMode access_mode, + const InstructionOperands* const operands, + JValue* result) { + return VarHandleInvokeAccessorImpl( + self, shadow_frame, var_handle, callsite_type, access_mode, operands, result); +} + } // namespace art diff --git a/runtime/var_handles.h b/runtime/var_handles.h index 2ff8405f03..dd04a544b7 100644 --- a/runtime/var_handles.h +++ b/runtime/var_handles.h @@ -21,6 +21,10 @@ namespace art { +namespace mirror { +class RawMethodType; +} // namespace mirror + bool VarHandleInvokeAccessor(Thread* self, ShadowFrame& shadow_frame, Handle<mirror::VarHandle> var_handle, @@ -30,6 +34,15 @@ bool VarHandleInvokeAccessor(Thread* self, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); +bool VarHandleInvokeAccessor(Thread* self, + ShadowFrame& shadow_frame, + Handle<mirror::VarHandle> var_handle, + mirror::RawMethodType callsite_type, + const mirror::VarHandle::AccessMode access_mode, + const InstructionOperands* const operands, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); + } // namespace art #endif // ART_RUNTIME_VAR_HANDLES_H_ diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 7c77af9479..0569964f71 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -154,6 +154,7 @@ ArtField* WellKnownClasses::java_nio_ByteBuffer_hb; ArtField* WellKnownClasses::java_nio_ByteBuffer_isReadOnly; ArtField* WellKnownClasses::java_nio_ByteBuffer_offset; ArtField* WellKnownClasses::java_util_Collections_EMPTY_LIST; +ArtField* WellKnownClasses::java_util_concurrent_ThreadLocalRandom_seeder; ArtField* WellKnownClasses::jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer; ArtField* WellKnownClasses::jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image; ArtField* WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT; @@ -414,7 +415,7 @@ void WellKnownClasses::InitFieldsAndMethodsOnly(JNIEnv* env) { java_lang_Long_value = CacheValueInBoxField( class_linker, self, "Ljava/lang/Long;", "J"); - StackHandleScope<42u> hs(self); + StackHandleScope<43u> hs(self); Handle<mirror::Class> d_s_bdcl = hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/BaseDexClassLoader;")); Handle<mirror::Class> d_s_dlcl = @@ -481,6 +482,8 @@ void WellKnownClasses::InitFieldsAndMethodsOnly(JNIEnv* env) { hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/nio/DirectByteBuffer;")); Handle<mirror::Class> j_u_c = hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/util/Collections;")); + Handle<mirror::Class> j_u_c_tlr = + hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/util/concurrent/ThreadLocalRandom;")); Handle<mirror::Class> j_u_f_c = hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/util/function/Consumer;")); Handle<mirror::Class> j_i_m_fd = @@ -797,6 +800,9 @@ void WellKnownClasses::InitFieldsAndMethodsOnly(JNIEnv* env) { java_util_Collections_EMPTY_LIST = CacheField(j_u_c.Get(), /*is_static=*/ true, "EMPTY_LIST", "Ljava/util/List;"); + java_util_concurrent_ThreadLocalRandom_seeder = CacheField( + j_u_c_tlr.Get(), /*is_static=*/ true, "seeder", "Ljava/util/concurrent/atomic/AtomicLong;"); + jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer = CacheField(j_i_m_fd_btab.Get(), /*is_static=*/ false, "buffer", "[C"); jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image = CacheField( @@ -953,6 +959,7 @@ void WellKnownClasses::Clear() { java_nio_ByteBuffer_isReadOnly = nullptr; java_nio_ByteBuffer_offset = nullptr; java_util_Collections_EMPTY_LIST = nullptr; + java_util_concurrent_ThreadLocalRandom_seeder = nullptr; jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer = nullptr; jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image = nullptr; libcore_util_EmptyArray_STACK_TRACE_ELEMENT = nullptr; diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index f717030d82..203a061db8 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -199,6 +199,7 @@ struct WellKnownClasses { static ArtField* java_nio_ByteBuffer_isReadOnly; static ArtField* java_nio_ByteBuffer_offset; static ArtField* java_util_Collections_EMPTY_LIST; + static ArtField* java_util_concurrent_ThreadLocalRandom_seeder; static ArtField* jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer; static ArtField* jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image; static ArtField* libcore_util_EmptyArray_STACK_TRACE_ELEMENT; |