diff options
author | 2022-01-05 14:29:05 +0000 | |
---|---|---|
committer | 2022-01-17 15:56:37 +0000 | |
commit | b08e11586f27cd5902ded2043727dacac2835ff9 (patch) | |
tree | 62da6cf6cbcc53e4c689c14ca13e0730b613bed0 | |
parent | 3a7d0d8da758471e090f87b32f15ed610cb818f8 (diff) |
MethodHandles: change asType() / invoke() implementation
This CL changes how we deal with non-exact invokes and asType().
Specifically:
1. It drops the concept of `nominalType` from our implementation and
adds an AsTypeAdapter transformer for performing pairwise
conversions. This means asType() conversions are orthogonal to other
transforms and easier to reason about.
2. It switches to use the asTypeCache to reduce the cost of non-exact
invokes.
3. It adds MethodType::IsInPlaceConvertible in the fast-path for
MethodHandle.invoke() to avoid an asTypeAdapter transformer when the
required conversions can be performed in-place with no-ops. This is
possible because the ShadowFrame deals in 32-bit vregs so
conversions like byte -> int are no-ops.
Bug: 207844518
Test: art/test.py --host
Test: atest CtsLibcoreTestCases
Change-Id: I2faf260b164767104c7ae0bc061fbf4f6579bbff
-rw-r--r-- | runtime/class_linker_test.cc | 2 | ||||
-rw-r--r-- | runtime/method_handles.cc | 169 | ||||
-rw-r--r-- | runtime/mirror/method_handle_impl-inl.h | 25 | ||||
-rw-r--r-- | runtime/mirror/method_handle_impl.cc | 2 | ||||
-rw-r--r-- | runtime/mirror/method_handle_impl.h | 27 | ||||
-rw-r--r-- | runtime/mirror/method_type.cc | 51 | ||||
-rw-r--r-- | runtime/mirror/method_type.h | 5 | ||||
-rw-r--r-- | runtime/mirror/method_type_test.cc | 124 | ||||
-rw-r--r-- | runtime/native/java_lang_invoke_MethodHandleImpl.cc | 2 | ||||
-rw-r--r-- | runtime/well_known_classes.cc | 3 | ||||
-rw-r--r-- | runtime/well_known_classes.h | 1 | ||||
-rw-r--r-- | test/956-methodhandles/src/Main.java | 42 |
12 files changed, 299 insertions, 154 deletions
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 2400f9fb8e..a789831238 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -743,10 +743,10 @@ struct MethodHandleOffsets : public CheckOffsets<mirror::MethodHandle> { MethodHandleOffsets() : CheckOffsets<mirror::MethodHandle>( false, "Ljava/lang/invoke/MethodHandle;") { addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, art_field_or_method_), "artFieldOrMethod"); + addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, as_type_cache_), "asTypeCache"); addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, cached_spread_invoker_), "cachedSpreadInvoker"); addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, handle_kind_), "handleKind"); - addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, nominal_type_), "nominalType"); addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, method_type_), "type"); } }; diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 76ad139da9..aa456f51dc 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -423,7 +423,6 @@ static inline bool InvokedFromTransform(Handle<mirror::MethodType> callsite_type static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, Handle<mirror::MethodType> callsite_type, Handle<mirror::MethodType> target_type, - Handle<mirror::MethodType> nominal_type, Thread* self, ShadowFrame& shadow_frame, const InstructionOperands* const operands, @@ -545,12 +544,6 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, DCHECK(self->IsExceptionPending()); return false; } - - if (nominal_type != nullptr) { - return ConvertReturnValue(nominal_type, target_type, result) && - ConvertReturnValue(callsite_type, nominal_type, result); - } - return ConvertReturnValue(callsite_type, target_type, result); } @@ -722,7 +715,6 @@ bool DoInvokePolymorphicMethod(Thread* self, REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<2> hs(self); Handle<mirror::MethodType> handle_type(hs.NewHandle(method_handle->GetMethodType())); - Handle<mirror::MethodType> nominal_handle_type(hs.NewHandle(method_handle->GetNominalType())); const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); DCHECK(IsInvoke(handle_kind)); @@ -768,7 +760,6 @@ bool DoInvokePolymorphicMethod(Thread* self, return MethodHandleInvokeMethod(called_method, callsite_type, handle_type, - nominal_handle_type, self, shadow_frame, operands, @@ -896,7 +887,6 @@ static JValue GetValueFromShadowFrame(const ShadowFrame& shadow_frame, return field_value; } -template <bool do_conversions> bool MethodHandleFieldAccess(Thread* self, ShadowFrame& shadow_frame, Handle<mirror::MethodHandle> method_handle, @@ -904,7 +894,6 @@ bool MethodHandleFieldAccess(Thread* self, const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(self); - Handle<mirror::MethodType> handle_type(hs.NewHandle(method_handle->GetMethodType())); const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); ArtField* field = method_handle->GetTargetField(); Primitive::Type field_type = field->GetTypeAsPrimitiveType(); @@ -913,10 +902,6 @@ bool MethodHandleFieldAccess(Thread* self, size_t obj_reg = operands->GetOperand(0); ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg); MethodHandleFieldGet(self, shadow_frame, obj, field, field_type, result); - if (do_conversions && !ConvertReturnValue(callsite_type, handle_type, result)) { - DCHECK(self->IsExceptionPending()); - return false; - } return true; } case mirror::MethodHandle::kStaticGet: { @@ -926,10 +911,6 @@ bool MethodHandleFieldAccess(Thread* self, return false; } MethodHandleFieldGet(self, shadow_frame, obj, field, field_type, result); - if (do_conversions && !ConvertReturnValue(callsite_type, handle_type, result)) { - DCHECK(self->IsExceptionPending()); - return false; - } return true; } case mirror::MethodHandle::kInstancePut: { @@ -942,13 +923,6 @@ bool MethodHandleFieldAccess(Thread* self, shadow_frame, callsite_type->GetPTypes()->Get(kPTypeIndex)->GetPrimitiveType(), value_reg); - if (do_conversions && !ConvertArgumentValue(callsite_type, - handle_type, - kPTypeIndex, - &value)) { - DCHECK(self->IsExceptionPending()); - return false; - } ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg); return MethodHandleFieldPut(self, shadow_frame, obj, field, field_type, value); } @@ -966,13 +940,6 @@ bool MethodHandleFieldAccess(Thread* self, shadow_frame, callsite_type->GetPTypes()->Get(kPTypeIndex)->GetPrimitiveType(), value_reg); - if (do_conversions && !ConvertArgumentValue(callsite_type, - handle_type, - kPTypeIndex, - &value)) { - DCHECK(self->IsExceptionPending()); - return false; - } return MethodHandleFieldPut(self, shadow_frame, obj, field, field_type, value); } default: @@ -1016,23 +983,11 @@ bool DoVarHandleInvokeTranslationUnchecked(Thread* self, bool DoVarHandleInvokeTranslation(Thread* self, ShadowFrame& shadow_frame, - bool invokeExact, Handle<mirror::MethodHandle> method_handle, Handle<mirror::MethodType> callsite_type, const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - if (!invokeExact) { - // Exact invokes are checked for compatability higher up. The - // non-exact invoke path doesn't have a similar check due to - // transformers which have EmulatedStack frame arguments with the - // actual method type associated with the frame. - if (UNLIKELY(!callsite_type->IsConvertible(method_handle->GetMethodType()))) { - ThrowWrongMethodTypeException(method_handle->GetMethodType(), callsite_type.Get()); - return false; - } - } - // // Basic checks that apply in all cases. // @@ -1098,47 +1053,6 @@ bool DoVarHandleInvokeTranslation(Thread* self, result); } -static inline bool MethodHandleInvokeInternal(Thread* self, - ShadowFrame& shadow_frame, - Handle<mirror::MethodHandle> method_handle, - Handle<mirror::MethodType> callsite_type, - const InstructionOperands* const operands, - JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { - const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); - if (IsFieldAccess(handle_kind)) { - ObjPtr<mirror::MethodType> handle_type(method_handle->GetMethodType()); - DCHECK(!callsite_type->IsExactMatch(handle_type.Ptr())); - if (!callsite_type->IsConvertible(handle_type.Ptr())) { - ThrowWrongMethodTypeException(handle_type.Ptr(), callsite_type.Get()); - return false; - } - const bool do_convert = true; - return MethodHandleFieldAccess<do_convert>( - self, - shadow_frame, - method_handle, - callsite_type, - operands, - result); - } - if (IsInvokeVarHandle(handle_kind)) { - return DoVarHandleInvokeTranslation(self, - shadow_frame, - /*invokeExact=*/ false, - method_handle, - callsite_type, - operands, - result); - } - return DoInvokePolymorphicMethod(self, - shadow_frame, - method_handle, - callsite_type, - operands, - result); -} - static inline bool MethodHandleInvokeExactInternal( Thread* self, ShadowFrame& shadow_frame, @@ -1149,20 +1063,20 @@ static inline bool MethodHandleInvokeExactInternal( REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(self); Handle<mirror::MethodType> method_handle_type(hs.NewHandle(method_handle->GetMethodType())); + const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); + if (!callsite_type->IsExactMatch(method_handle_type.Get())) { ThrowWrongMethodTypeException(method_handle_type.Get(), callsite_type.Get()); return false; } - const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); if (IsFieldAccess(handle_kind)) { - const bool do_convert = false; - return MethodHandleFieldAccess<do_convert>(self, - shadow_frame, - method_handle, - callsite_type, - operands, - result); + return MethodHandleFieldAccess(self, + shadow_frame, + method_handle, + callsite_type, + operands, + result); } // Slow-path check. @@ -1177,7 +1091,6 @@ static inline bool MethodHandleInvokeExactInternal( } else if (IsInvokeVarHandle(handle_kind)) { return DoVarHandleInvokeTranslation(self, shadow_frame, - /*invokeExact=*/ true, method_handle, callsite_type, operands, @@ -1310,14 +1223,50 @@ bool MethodHandleInvoke(Thread* self, Handle<mirror::MethodType> callsite_type, const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - if (UNLIKELY(callsite_type->IsExactMatch(method_handle->GetMethodType()))) { - // A non-exact invoke that can be invoked exactly. - return MethodHandleInvokeExactInternal( - self, shadow_frame, method_handle, callsite_type, operands, result); - } else { - return MethodHandleInvokeInternal( - self, shadow_frame, method_handle, callsite_type, operands, result); + StackHandleScope<2> hs(self); + Handle<mirror::MethodType> method_handle_type(hs.NewHandle(method_handle->GetMethodType())); + + // Non-exact invoke behaves as calling mh.asType(newType). In ART, asType() is implemented + // as a transformer and it is expensive to call so check first if it's really necessary. + // + // There are two cases where the asType() transformation can be skipped: + // + // 1) the call site and type of the MethodHandle match, ie code is calling invoke() + // unnecessarily. + // + // 2) when the call site can be trivally converted to the MethodHandle type due to how + // values are represented in the ShadowFrame, ie all registers in the shadow frame are + // 32-bit, there is no byte, short, char, etc. So a call site with arguments of these + // kinds can be trivially converted to one with int arguments. Similarly if the reference + // types are assignable between the call site and MethodHandle type, then as asType() + // transformation isn't really doing any work. + if (callsite_type->IsInPlaceConvertible(method_handle_type.Get())) { + return MethodHandleInvokeExact( + self, shadow_frame, method_handle, method_handle_type, operands, result); + } + + // Use asType() variant of this MethodHandle to adapt callsite to the target. + MutableHandle<mirror::MethodHandle> atc(hs.NewHandle(method_handle->GetAsTypeCache())); + if (atc == nullptr || !callsite_type->IsExactMatch(atc->GetMethodType())) { + // Cached asType adapter does not exist or is for another call site. Call + // MethodHandle::asType() to get an appropriate adapter. + ArtMethod* as_type = + jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_asType); + uint32_t as_type_args[] = { + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(method_handle.Get())), + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(callsite_type.Get()))}; + JValue atc_result; + as_type->Invoke(self, as_type_args, sizeof(as_type_args), &atc_result, "LL"); + if (atc_result.GetL() == nullptr) { + DCHECK(self->IsExceptionPending()); + return false; + } + ObjPtr<mirror::MethodHandle> atc_method_handle = + down_cast<mirror::MethodHandle*>(atc_result.GetL()); + atc.Assign(atc_method_handle); + DCHECK(!atc.IsNull()); } + return MethodHandleInvokeExact(self, shadow_frame, atc, callsite_type, operands, result); }; return InvokeInContext( self, shadow_frame, method_handle, callsite_type, operands, result, invoke); @@ -1336,22 +1285,6 @@ bool MethodHandleInvokeExact(Thread* self, Handle<mirror::MethodType> callsite_type, const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - // We need to check the nominal type of the handle in addition to the - // real type. The "nominal" type is present when MethodHandle.asType is - // called any handle, and results in the declared type of the handle - // changing. - ObjPtr<mirror::MethodType> nominal_type(method_handle->GetNominalType()); - if (UNLIKELY(nominal_type != nullptr)) { - if (UNLIKELY(!callsite_type->IsExactMatch(nominal_type.Ptr()))) { - ThrowWrongMethodTypeException(nominal_type.Ptr(), callsite_type.Get()); - return false; - } - if (LIKELY(!nominal_type->IsExactMatch(method_handle->GetMethodType()))) { - // Different nominal type means we have to treat as non-exact. - return MethodHandleInvokeInternal( - self, shadow_frame, method_handle, callsite_type, operands, result); - } - } return MethodHandleInvokeExactInternal( self, shadow_frame, method_handle, callsite_type, operands, result); }; diff --git a/runtime/mirror/method_handle_impl-inl.h b/runtime/mirror/method_handle_impl-inl.h index 27ccc53e83..8c5e4799b9 100644 --- a/runtime/mirror/method_handle_impl-inl.h +++ b/runtime/mirror/method_handle_impl-inl.h @@ -25,12 +25,31 @@ namespace art { namespace mirror { -inline ObjPtr<mirror::MethodType> MethodHandle::GetMethodType() { +inline MethodHandle::Kind MethodHandle::GetHandleKind() REQUIRES_SHARED(Locks::mutator_lock_) { + const int32_t handle_kind = GetField32(OFFSET_OF_OBJECT_MEMBER(MethodHandle, handle_kind_)); + DCHECK(handle_kind >= 0 && handle_kind <= static_cast<int32_t>(Kind::kLastValidKind)); + return static_cast<Kind>(handle_kind); +} + +inline ObjPtr<mirror::MethodType> MethodHandle::GetMethodType() + REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldObject<mirror::MethodType>(OFFSET_OF_OBJECT_MEMBER(MethodHandle, method_type_)); } -inline ObjPtr<mirror::MethodType> MethodHandle::GetNominalType() { - return GetFieldObject<mirror::MethodType>(OFFSET_OF_OBJECT_MEMBER(MethodHandle, nominal_type_)); +inline ObjPtr<mirror::MethodHandle> MethodHandle::GetAsTypeCache() + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetFieldObject<mirror::MethodHandle>( + OFFSET_OF_OBJECT_MEMBER(MethodHandle, as_type_cache_)); +} + +inline ArtField* MethodHandle::GetTargetField() REQUIRES_SHARED(Locks::mutator_lock_) { + return reinterpret_cast<ArtField*>( + GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_))); +} + +inline ArtMethod* MethodHandle::GetTargetMethod() REQUIRES_SHARED(Locks::mutator_lock_) { + return reinterpret_cast<ArtMethod*>( + GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_))); } } // namespace mirror diff --git a/runtime/mirror/method_handle_impl.cc b/runtime/mirror/method_handle_impl.cc index 4f1a18b7be..79ed2b0be3 100644 --- a/runtime/mirror/method_handle_impl.cc +++ b/runtime/mirror/method_handle_impl.cc @@ -36,8 +36,8 @@ void MethodHandle::Initialize(uintptr_t art_field_or_method, REQUIRES_SHARED(Locks::mutator_lock_) { CHECK(!Runtime::Current()->IsActiveTransaction()); SetFieldObject<false>(CachedSpreadInvokerOffset(), nullptr); - SetFieldObject<false>(NominalTypeOffset(), nullptr); SetFieldObject<false>(MethodTypeOffset(), method_type.Get()); + SetFieldObject<false>(AsTypeCacheOffset(), nullptr); SetField32<false>(HandleKindOffset(), static_cast<uint32_t>(kind)); SetField64<false>(ArtFieldOrMethodOffset(), art_field_or_method); } diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h index 6ba6e25f8a..0a7042248d 100644 --- a/runtime/mirror/method_handle_impl.h +++ b/runtime/mirror/method_handle_impl.h @@ -63,26 +63,15 @@ class MANAGED MethodHandle : public Object { kLastInvokeKind = kInvokeVarHandleExact }; - Kind GetHandleKind() REQUIRES_SHARED(Locks::mutator_lock_) { - const int32_t handle_kind = GetField32(OFFSET_OF_OBJECT_MEMBER(MethodHandle, handle_kind_)); - DCHECK(handle_kind >= 0 && - handle_kind <= static_cast<int32_t>(Kind::kLastValidKind)); - return static_cast<Kind>(handle_kind); - } + Kind GetHandleKind() REQUIRES_SHARED(Locks::mutator_lock_); - ALWAYS_INLINE ObjPtr<mirror::MethodType> GetMethodType() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<mirror::MethodType> GetMethodType() REQUIRES_SHARED(Locks::mutator_lock_); - ALWAYS_INLINE ObjPtr<mirror::MethodType> GetNominalType() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<mirror::MethodHandle> GetAsTypeCache() REQUIRES_SHARED(Locks::mutator_lock_); - ArtField* GetTargetField() REQUIRES_SHARED(Locks::mutator_lock_) { - return reinterpret_cast<ArtField*>( - GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_))); - } + ArtField* GetTargetField() REQUIRES_SHARED(Locks::mutator_lock_); - ArtMethod* GetTargetMethod() REQUIRES_SHARED(Locks::mutator_lock_) { - return reinterpret_cast<ArtMethod*>( - GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_))); - } + ArtMethod* GetTargetMethod() REQUIRES_SHARED(Locks::mutator_lock_); // Gets the return type for a named invoke method, or nullptr if the invoke method is not // supported. @@ -97,8 +86,8 @@ class MANAGED MethodHandle : public Object { REQUIRES_SHARED(Locks::mutator_lock_); private: + HeapReference<mirror::MethodHandle> as_type_cache_; HeapReference<mirror::MethodHandle> cached_spread_invoker_; - HeapReference<mirror::MethodType> nominal_type_; HeapReference<mirror::MethodType> method_type_; uint32_t handle_kind_; uint64_t art_field_or_method_; @@ -107,8 +96,8 @@ class MANAGED MethodHandle : public Object { static MemberOffset CachedSpreadInvokerOffset() { return MemberOffset(OFFSETOF_MEMBER(MethodHandle, cached_spread_invoker_)); } - static MemberOffset NominalTypeOffset() { - return MemberOffset(OFFSETOF_MEMBER(MethodHandle, nominal_type_)); + static MemberOffset AsTypeCacheOffset() { + return MemberOffset(OFFSETOF_MEMBER(MethodHandle, as_type_cache_)); } static MemberOffset MethodTypeOffset() { return MemberOffset(OFFSETOF_MEMBER(MethodHandle, method_type_)); diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc index 7e6ae45940..ccc2b9d679 100644 --- a/runtime/mirror/method_type.cc +++ b/runtime/mirror/method_type.cc @@ -161,6 +161,57 @@ bool MethodType::IsConvertible(ObjPtr<MethodType> target) { return true; } +static bool IsParameterInPlaceConvertible(ObjPtr<Class> from, ObjPtr<Class> to) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (from == to) { + return true; + } + + if (from->IsPrimitive() != to->IsPrimitive()) { + return false; // No in-place conversion from place conversion for box/unboxing. + } + + if (from->IsPrimitive()) { + // `from` and `to` are both primitives. The supported in-place conversions use a 32-bit + // interpreter representation and are a subset of permitted conversions for MethodHandles. + // Conversions are documented in JLS 11 S5.1.2 "Widening Primitive Conversion". + Primitive::Type src = from->GetPrimitiveType(); + Primitive::Type dst = to->GetPrimitiveType(); + switch (src) { + case Primitive::Type::kPrimByte: + return dst == Primitive::Type::kPrimShort || dst == Primitive::Type::kPrimInt; + case Primitive::Type::kPrimChar: + FALLTHROUGH_INTENDED; + case Primitive::Type::kPrimShort: + return dst == Primitive::Type::kPrimInt; + default: + return false; + } + } + + // `from` and `to` are both references, apply an assignability check. + return to->IsAssignableFrom(from); +} + +bool MethodType::IsInPlaceConvertible(ObjPtr<MethodType> target) { + const ObjPtr<ObjectArray<Class>> ptypes = GetPTypes(); + const ObjPtr<ObjectArray<Class>> target_ptypes = target->GetPTypes(); + const int32_t ptypes_length = ptypes->GetLength(); + if (ptypes_length != target_ptypes->GetLength()) { + return false; + } + + for (int32_t i = 0; i < ptypes_length; ++i) { + if (!IsParameterInPlaceConvertible(ptypes->GetWithoutChecks(i), + target_ptypes->GetWithoutChecks(i))) { + return false; + } + } + + return GetRType()->IsPrimitiveVoid() || + IsParameterInPlaceConvertible(target->GetRType(), GetRType()); +} + std::string MethodType::PrettyDescriptor() { std::ostringstream ss; ss << "("; diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h index 872c07beab..19444bb6ce 100644 --- a/runtime/mirror/method_type.h +++ b/runtime/mirror/method_type.h @@ -67,6 +67,11 @@ class MANAGED MethodType : public Object { // iff. they have convertible return types and parameter types. bool IsConvertible(ObjPtr<MethodType> target) REQUIRES_SHARED(Locks::mutator_lock_); + // Returns true iff. |this| can be converted to match |target| method type within the + // current frame of the current MethodType. This limits conversions to assignability check + // for references and between scalar 32-bit types. + bool IsInPlaceConvertible(ObjPtr<MethodType> target) REQUIRES_SHARED(Locks::mutator_lock_); + // Returns the pretty descriptor for this method type, suitable for display in // exception messages and the like. std::string PrettyDescriptor() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/mirror/method_type_test.cc b/runtime/mirror/method_type_test.cc index 742960bea1..c9d7e91a47 100644 --- a/runtime/mirror/method_type_test.cc +++ b/runtime/mirror/method_type_test.cc @@ -38,32 +38,39 @@ static std::string FullyQualifiedType(const std::string& shorthand) { return "Ljava/lang/" + shorthand + ";"; } +ObjPtr<mirror::Class> FindClass(Thread* self, ClassLinker* const cl, const std::string& shorthand) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> boot_class_loader = hs.NewHandle<mirror::ClassLoader>(nullptr); + if (shorthand.size() == 1) { + return cl->FindSystemClass(self, shorthand.c_str()); + } else if (shorthand.find('/') == std::string::npos) { + return cl->FindClass(self, FullyQualifiedType(shorthand).c_str(), boot_class_loader); + } else { + return cl->FindClass(self, shorthand.c_str(), boot_class_loader); + } +} + static ObjPtr<mirror::MethodType> CreateMethodType(const std::string& return_type, const std::vector<std::string>& param_types) { - CHECK_LT(param_types.size(), 3u); - Runtime* const runtime = Runtime::Current(); ClassLinker* const class_linker = runtime->GetClassLinker(); Thread* const self = Thread::Current(); ScopedObjectAccess soa(self); - StackHandleScope<5> hs(soa.Self()); - - Handle<mirror::ClassLoader> boot_class_loader = hs.NewHandle<mirror::ClassLoader>(nullptr); + StackHandleScope<2> hs(soa.Self()); - Handle<mirror::Class> return_clazz = hs.NewHandle(class_linker->FindClass( - soa.Self(), FullyQualifiedType(return_type).c_str(), boot_class_loader)); + Handle<mirror::Class> return_clazz = hs.NewHandle(FindClass(self, class_linker, return_type)); CHECK(return_clazz != nullptr); ObjPtr<mirror::Class> class_array_type = GetClassRoot<mirror::ObjectArray<mirror::Class>>(class_linker); Handle<mirror::ObjectArray<mirror::Class>> param_classes = hs.NewHandle( mirror::ObjectArray<mirror::Class>::Alloc(self, class_array_type, param_types.size())); - for (uint32_t i = 0; i < param_types.size(); ++i) { - Handle<mirror::Class> param = hs.NewHandle(class_linker->FindClass( - soa.Self(), FullyQualifiedType(param_types[i]).c_str(), boot_class_loader)); - param_classes->Set(i, param.Get()); + ObjPtr<mirror::Class> param = FindClass(self, class_linker, param_types[i]); + CHECK(!param.IsNull()); + param_classes->Set(i, param); } return mirror::MethodType::Create(self, return_clazz, param_classes); @@ -105,5 +112,100 @@ TEST_F(MethodTypeTest, IsExactMatch) { } } +TEST_F(MethodTypeTest, IsInPlaceConvertible) { + ScopedObjectAccess soa(Thread::Current()); + + // Call site has void return type, value is discarded. + { + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("V", { "Integer" })); + Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("String", { "Integer" })); + ASSERT_TRUE(cs->IsInPlaceConvertible(mh.Get())); + } + + // MethodHandle has void return type, value is required + { + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("String", { "Integer" })); + Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("V", { "Integer" })); + ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get())); + } + + // Assignable Reference Types + { + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("Object", { "Integer" })); + Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("String", { "Object" })); + ASSERT_TRUE(cs->IsInPlaceConvertible(mh.Get())); + } + + // Not assignable Reference Types + { + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("Integer", { "Object" })); + Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("Object", { "String" })); + ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get())); + } + + // Widenable primitives + { + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("I", { "B", "C", "S" })); + Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("S", { "I", "I", "I" })); + ASSERT_TRUE(cs->IsInPlaceConvertible(mh.Get())); + } + + // Non-widenable primitives + { + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("V", { "Z" })); + Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("V", { "I" })); + ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get())); + } + { + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("V", { "I" })); + Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("V", { "Z" })); + ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get())); + } + { + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("V", { "S" })); + Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("V", { "C" })); + ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get())); + ASSERT_FALSE(mh->IsInPlaceConvertible(cs.Get())); + } + { + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("V", { "C" })); + Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("V", { "S" })); + ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get())); + } + { + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("V", { "I" })); + Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("V", { "J" })); + ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get())); + } + { + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("V", { "F" })); + Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("V", { "D" })); + ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get())); + } + { + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("V", { "D" })); + Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("V", { "F" })); + ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get())); + } + { + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("I", {})); + Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("Z", {})); + ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get())); + } +} + } // namespace mirror } // namespace art diff --git a/runtime/native/java_lang_invoke_MethodHandleImpl.cc b/runtime/native/java_lang_invoke_MethodHandleImpl.cc index 45d9a8abef..00ce01f11a 100644 --- a/runtime/native/java_lang_invoke_MethodHandleImpl.cc +++ b/runtime/native/java_lang_invoke_MethodHandleImpl.cc @@ -23,7 +23,7 @@ #include "jni/jni_internal.h" #include "mirror/field.h" #include "mirror/method.h" -#include "mirror/method_handle_impl.h" +#include "mirror/method_handle_impl-inl.h" #include "native_util.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 4bd34c9872..62e405af61 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -104,6 +104,7 @@ jmethodID WellKnownClasses::java_lang_Double_valueOf; jmethodID WellKnownClasses::java_lang_Float_floatToRawIntBits; jmethodID WellKnownClasses::java_lang_Float_valueOf; jmethodID WellKnownClasses::java_lang_Integer_valueOf; +jmethodID WellKnownClasses::java_lang_invoke_MethodHandle_asType; jmethodID WellKnownClasses::java_lang_invoke_MethodHandles_lookup; jmethodID WellKnownClasses::java_lang_invoke_MethodHandles_Lookup_findConstructor; jmethodID WellKnownClasses::java_lang_Long_valueOf; @@ -401,6 +402,7 @@ void WellKnownClasses::InitFieldsAndMethodsOnly(JNIEnv* env) { java_lang_Daemons_start = CacheMethod(env, java_lang_Daemons, true, "start", "()V"); java_lang_Daemons_stop = CacheMethod(env, java_lang_Daemons, true, "stop", "()V"); java_lang_Daemons_waitForDaemonStart = CacheMethod(env, java_lang_Daemons, true, "waitForDaemonStart", "()V"); + java_lang_invoke_MethodHandle_asType = CacheMethod(env, "java/lang/invoke/MethodHandle", false, "asType", "(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;"); java_lang_invoke_MethodHandles_lookup = CacheMethod(env, "java/lang/invoke/MethodHandles", true, "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;"); java_lang_invoke_MethodHandles_Lookup_findConstructor = CacheMethod(env, "java/lang/invoke/MethodHandles$Lookup", false, "findConstructor", "(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;"); @@ -601,6 +603,7 @@ void WellKnownClasses::Clear() { java_lang_Float_floatToRawIntBits = nullptr; java_lang_Float_valueOf = nullptr; java_lang_Integer_valueOf = nullptr; + java_lang_invoke_MethodHandle_asType = nullptr; java_lang_invoke_MethodHandles_lookup = nullptr; java_lang_invoke_MethodHandles_Lookup_findConstructor = nullptr; java_lang_Long_valueOf = nullptr; diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 41801e17ab..67a47f0f43 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -117,6 +117,7 @@ struct WellKnownClasses { static jmethodID java_lang_Float_floatToRawIntBits; static jmethodID java_lang_Float_valueOf; static jmethodID java_lang_Integer_valueOf; + static jmethodID java_lang_invoke_MethodHandle_asType; static jmethodID java_lang_invoke_MethodHandles_lookup; static jmethodID java_lang_invoke_MethodHandles_Lookup_findConstructor; static jmethodID java_lang_Long_valueOf; diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java index cbe6fe9651..168862c040 100644 --- a/test/956-methodhandles/src/Main.java +++ b/test/956-methodhandles/src/Main.java @@ -1922,4 +1922,46 @@ public class Main { System.out.println("Got expected IAE when invoke-special on an abstract interface method"); } } + + private static int returnInput(int value) { return value; } + private static byte returnInput(byte value) { return value; } + private static char returnInput(char value) { return value; } + + private static void testFastInvoke() throws Throwable { + // This tests use of invoke() that have different types and require widening, but do not + // require require an explicit asType() transform. + MethodHandle mh0 = + MethodHandles.lookup().findStatic( + Main.class, "returnInput", MethodType.methodType(int.class, int.class)); + assertEquals((byte) 127, (byte) (int) mh0.invoke((byte) 127)); + assertEquals((byte) -128, (byte) (int) mh0.invoke((byte) -128)); + assertEquals((short) 127, (short) (int) mh0.invoke((byte) 127)); + assertEquals((short) -128, (short) (int) mh0.invoke((byte) -128)); + assertEquals((char) 127, (char) (int) mh0.invoke((byte) 127)); + assertEquals((char) 65535, (char) (int) mh0.invoke((byte) -1)); + assertEquals((char) 0, (char) (int) mh0.invoke((char) 0)); + assertEquals((char) 65535, (char) (int) mh0.invoke((char) 65535)); + assertEquals((short) 127, (short) (int) mh0.invoke((short) 127)); + assertEquals((short) -128, (short) (int) mh0.invoke((short) -128)); + assertEquals((int) 127, (int) mh0.invoke((byte) 127)); + assertEquals((int) -128, (int) mh0.invoke((byte) -128)); + assertEquals((int) 127, (int) mh0.invoke((short) 127)); + assertEquals((int) -128, (int) mh0.invoke((short) -128)); + assertEquals((int) 0, (int) mh0.invoke((char) 0)); + assertEquals((int) 65535, (int) mh0.invoke((char) 65535)); + + MethodHandle mh1 = + MethodHandles.lookup().findStatic( + Main.class, "returnInput", MethodType.methodType(char.class, char.class)); + assertEquals((int) 0, (int) mh1.invoke((char) 0)); + assertEquals((int) 65535, (int) mh1.invoke((char) 65535)); + + MethodHandle mh2 = + MethodHandles.lookup().findStatic( + Main.class, "returnInput", MethodType.methodType(byte.class, byte.class)); + assertEquals((int) -128, (int) mh2.invoke((byte) -128)); + assertEquals((int) 127, (int) mh2.invoke((byte) 127)); + assertEquals((short) -128, (short) mh2.invoke((byte) -128)); + assertEquals((short) 127, (short) mh2.invoke((byte) 127)); + } } |