diff options
16 files changed, 722 insertions, 202 deletions
diff --git a/runtime/Android.bp b/runtime/Android.bp index 57413dcaab..c6f479ff40 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -120,6 +120,7 @@ cc_defaults { "linear_alloc.cc", "mem_map.cc", "memory_region.cc", + "method_handles.cc", "mirror/array.cc", "mirror/class.cc", "mirror/class_ext.cc", diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 981cc1b998..4843c4dc59 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -48,7 +48,8 @@ static ALWAYS_INLINE void DoFieldGetCommon(Thread* self, const ShadowFrame& shadow_frame, ObjPtr<mirror::Object>& obj, ArtField* field, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { field->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self); // Report this field access to instrumentation if needed. @@ -299,6 +300,42 @@ EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-qui EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-quick. #undef EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL +static JValue GetFieldValue(const ShadowFrame& shadow_frame, + Primitive::Type field_type, + uint32_t vreg) + REQUIRES_SHARED(Locks::mutator_lock_) { + JValue field_value; + switch (field_type) { + case Primitive::kPrimBoolean: + field_value.SetZ(static_cast<uint8_t>(shadow_frame.GetVReg(vreg))); + break; + case Primitive::kPrimByte: + field_value.SetB(static_cast<int8_t>(shadow_frame.GetVReg(vreg))); + break; + case Primitive::kPrimChar: + field_value.SetC(static_cast<uint16_t>(shadow_frame.GetVReg(vreg))); + break; + case Primitive::kPrimShort: + field_value.SetS(static_cast<int16_t>(shadow_frame.GetVReg(vreg))); + break; + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + field_value.SetI(shadow_frame.GetVReg(vreg)); + break; + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + field_value.SetJ(shadow_frame.GetVRegLong(vreg)); + break; + case Primitive::kPrimNot: + field_value.SetL(shadow_frame.GetVRegReference(vreg)); + break; + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable: " << field_type; + UNREACHABLE(); + } + return field_value; +} + template<Primitive::Type field_type> static JValue GetFieldValue(const ShadowFrame& shadow_frame, uint32_t vreg) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -337,7 +374,8 @@ static inline bool DoFieldPutCommon(Thread* self, const ShadowFrame& shadow_frame, ObjPtr<mirror::Object>& obj, ArtField* f, - size_t vregA) REQUIRES_SHARED(Locks::mutator_lock_) { + const JValue& value) + REQUIRES_SHARED(Locks::mutator_lock_) { f->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self); // Report this field access to instrumentation if needed. Since we only have the offset of @@ -347,36 +385,35 @@ static inline bool DoFieldPutCommon(Thread* self, StackHandleScope<1> hs(self); // Wrap in handle wrapper in case the listener does thread suspension. HandleWrapperObjPtr<mirror::Object> h(hs.NewHandleWrapper(&obj)); - JValue field_value = GetFieldValue<field_type>(shadow_frame, vregA); ObjPtr<mirror::Object> this_object = f->IsStatic() ? nullptr : obj; instrumentation->FieldWriteEvent(self, this_object.Ptr(), shadow_frame.GetMethod(), shadow_frame.GetDexPC(), f, - field_value); + value); } switch (field_type) { case Primitive::kPrimBoolean: - f->SetBoolean<transaction_active>(obj, shadow_frame.GetVReg(vregA)); + f->SetBoolean<transaction_active>(obj, value.GetZ()); break; case Primitive::kPrimByte: - f->SetByte<transaction_active>(obj, shadow_frame.GetVReg(vregA)); + f->SetByte<transaction_active>(obj, value.GetB()); break; case Primitive::kPrimChar: - f->SetChar<transaction_active>(obj, shadow_frame.GetVReg(vregA)); + f->SetChar<transaction_active>(obj, value.GetC()); break; case Primitive::kPrimShort: - f->SetShort<transaction_active>(obj, shadow_frame.GetVReg(vregA)); + f->SetShort<transaction_active>(obj, value.GetS()); break; case Primitive::kPrimInt: - f->SetInt<transaction_active>(obj, shadow_frame.GetVReg(vregA)); + f->SetInt<transaction_active>(obj, value.GetI()); break; case Primitive::kPrimLong: - f->SetLong<transaction_active>(obj, shadow_frame.GetVRegLong(vregA)); + f->SetLong<transaction_active>(obj, value.GetJ()); break; case Primitive::kPrimNot: { - ObjPtr<mirror::Object> reg = shadow_frame.GetVRegReference(vregA); + ObjPtr<mirror::Object> reg = value.GetL(); if (do_assignability_check && reg != nullptr) { // FieldHelper::GetType can resolve classes, use a handle wrapper which will restore the // object in the destructor. @@ -434,11 +471,12 @@ bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction } uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data); + JValue value = GetFieldValue<field_type>(shadow_frame, vregA); return DoFieldPutCommon<field_type, do_assignability_check, transaction_active>(self, shadow_frame, obj, f, - vregA); + value); } // Explicitly instantiate all DoFieldPut functions. @@ -479,37 +517,34 @@ bool DoFieldPutForInvokePolymorphic(Thread* self, ObjPtr<mirror::Object>& obj, ArtField* field, Primitive::Type field_type, - size_t vregA) REQUIRES_SHARED(Locks::mutator_lock_) { + const JValue& value) + REQUIRES_SHARED(Locks::mutator_lock_) { static const bool kDoCheckAssignability = false; static const bool kTransaction = false; switch (field_type) { case Primitive::kPrimBoolean: return DoFieldPutCommon<Primitive::kPrimBoolean, kDoCheckAssignability, kTransaction>( - self, shadow_frame, obj, field, vregA); + self, shadow_frame, obj, field, value); case Primitive::kPrimByte: return DoFieldPutCommon<Primitive::kPrimByte, kDoCheckAssignability, kTransaction>( - self, shadow_frame, obj, field, vregA); + self, shadow_frame, obj, field, value); case Primitive::kPrimChar: return DoFieldPutCommon<Primitive::kPrimChar, kDoCheckAssignability, kTransaction>( - self, shadow_frame, obj, field, vregA); + self, shadow_frame, obj, field, value); case Primitive::kPrimShort: return DoFieldPutCommon<Primitive::kPrimShort, kDoCheckAssignability, kTransaction>( - self, shadow_frame, obj, field, vregA); + self, shadow_frame, obj, field, value); case Primitive::kPrimInt: - return DoFieldPutCommon<Primitive::kPrimInt, kDoCheckAssignability, kTransaction>( - self, shadow_frame, obj, field, vregA); - case Primitive::kPrimLong: - return DoFieldPutCommon<Primitive::kPrimLong, kDoCheckAssignability, kTransaction>( - self, shadow_frame, obj, field, vregA); case Primitive::kPrimFloat: return DoFieldPutCommon<Primitive::kPrimInt, kDoCheckAssignability, kTransaction>( - self, shadow_frame, obj, field, vregA); + self, shadow_frame, obj, field, value); + case Primitive::kPrimLong: case Primitive::kPrimDouble: return DoFieldPutCommon<Primitive::kPrimLong, kDoCheckAssignability, kTransaction>( - self, shadow_frame, obj, field, vregA); + self, shadow_frame, obj, field, value); case Primitive::kPrimNot: return DoFieldPutCommon<Primitive::kPrimNot, kDoCheckAssignability, kTransaction>( - self, shadow_frame, obj, field, vregA); + self, shadow_frame, obj, field, value); case Primitive::kPrimVoid: LOG(FATAL) << "Unreachable: " << field_type; UNREACHABLE(); @@ -797,7 +832,8 @@ inline bool DoInvokePolymorphic(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { // Invoke-polymorphic instructions always take a receiver. i.e, they are never static. const uint32_t vRegC = (is_range) ? inst->VRegC_4rcc() : inst->VRegC_45cc(); const int invoke_method_idx = (is_range) ? inst->VRegB_4rcc() : inst->VRegB_45cc(); @@ -960,20 +996,23 @@ inline bool DoInvokePolymorphic(Thread* self, Primitive::Type field_type = field->GetTypeAsPrimitiveType();; if (!is_invoke_exact) { - // TODO(oth): conversion plumbing for invoke(). - UNIMPLEMENTED(FATAL); + if (handle_type->GetPTypes()->GetLength() != callsite_type->GetPTypes()->GetLength()) { + // Too many arguments to setter or getter. + ThrowWrongMethodTypeException(callsite_type.Get(), handle_type.Get()); + return false; + } } switch (handle_kind) { case kInstanceGet: { ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(first_src_reg); DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result); + if (!ConvertReturnValue(callsite_type, handle_type, result)) { + DCHECK(self->IsExceptionPending()); + return false; + } return true; } - case kInstancePut: { - ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(first_src_reg); - return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, arg[1]); - } case kStaticGet: { ObjPtr<mirror::Object> obj = GetAndInitializeDeclaringClass(self, field); if (obj == nullptr) { @@ -981,15 +1020,31 @@ inline bool DoInvokePolymorphic(Thread* self, return false; } DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result); + if (!ConvertReturnValue(callsite_type, handle_type, result)) { + DCHECK(self->IsExceptionPending()); + return false; + } return true; } + case kInstancePut: { + JValue value = GetFieldValue(shadow_frame, field_type, arg[1]); + if (!ConvertArgumentValue(callsite_type, handle_type, 1, &value)) { + DCHECK(self->IsExceptionPending()); + return false; + } + ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(first_src_reg); + result->SetL(0); + return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value); + } case kStaticPut: { - ObjPtr<mirror::Object> obj = GetAndInitializeDeclaringClass(self, field); - if (obj == nullptr) { + JValue value = GetFieldValue(shadow_frame, field_type, arg[0]); + if (!ConvertArgumentValue(callsite_type, handle_type, 0, &value)) { DCHECK(self->IsExceptionPending()); return false; } - return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, arg[0]); + ObjPtr<mirror::Object> obj = field->GetDeclaringClass(); + result->SetL(0); + return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value); } default: LOG(FATAL) << "Unreachable: " << handle_kind; diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h index b279a3778c..1240792643 100644 --- a/runtime/method_handles-inl.h +++ b/runtime/method_handles-inl.h @@ -31,127 +31,70 @@ namespace art { -// Assigns |type| to the primitive type associated with |dst_class|. Returns -// true iff. |dst_class| was a boxed type (Integer, Long etc.), false otherwise. -REQUIRES_SHARED(Locks::mutator_lock_) -static inline bool GetPrimitiveType(ObjPtr<mirror::Class> dst_class, Primitive::Type* type) { - if (dst_class->DescriptorEquals("Ljava/lang/Boolean;")) { - (*type) = Primitive::kPrimBoolean; +inline bool ConvertArgumentValue(Handle<mirror::MethodType> callsite_type, + Handle<mirror::MethodType> callee_type, + int index, + JValue* value) REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr<mirror::Class> from_class(callsite_type->GetPTypes()->GetWithoutChecks(index)); + ObjPtr<mirror::Class> to_class(callee_type->GetPTypes()->GetWithoutChecks(index)); + if (from_class == to_class) { return true; - } else if (dst_class->DescriptorEquals("Ljava/lang/Byte;")) { - (*type) = Primitive::kPrimByte; - return true; - } else if (dst_class->DescriptorEquals("Ljava/lang/Character;")) { - (*type) = Primitive::kPrimChar; - return true; - } else if (dst_class->DescriptorEquals("Ljava/lang/Float;")) { - (*type) = Primitive::kPrimFloat; - return true; - } else if (dst_class->DescriptorEquals("Ljava/lang/Double;")) { - (*type) = Primitive::kPrimDouble; - return true; - } else if (dst_class->DescriptorEquals("Ljava/lang/Integer;")) { - (*type) = Primitive::kPrimInt; - return true; - } else if (dst_class->DescriptorEquals("Ljava/lang/Long;")) { - (*type) = Primitive::kPrimLong; - return true; - } else if (dst_class->DescriptorEquals("Ljava/lang/Short;")) { - (*type) = Primitive::kPrimShort; + } + + // |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)) { + DCHECK(!Thread::Current()->IsExceptionPending()); return true; } else { + DCHECK(Thread::Current()->IsExceptionPending()); + value->SetJ(0); return false; } } -inline bool ConvertJValue(Handle<mirror::Class> from, - Handle<mirror::Class> to, - const JValue& from_value, - JValue* to_value) { - const Primitive::Type from_type = from->GetPrimitiveType(); - const Primitive::Type to_type = to->GetPrimitiveType(); - - // This method must be called only when the types don't match. - DCHECK(from.Get() != to.Get()); - - if ((from_type != Primitive::kPrimNot) && (to_type != Primitive::kPrimNot)) { - // Throws a ClassCastException if we're unable to convert a primitive value. - return ConvertPrimitiveValue(false, from_type, to_type, from_value, to_value); - } else if ((from_type == Primitive::kPrimNot) && (to_type == Primitive::kPrimNot)) { - // They're both reference types. If "from" is null, we can pass it - // through unchanged. If not, we must generate a cast exception if - // |to| is not assignable from the dynamic type of |ref|. - mirror::Object* const ref = from_value.GetL(); - if (ref == nullptr || to->IsAssignableFrom(ref->GetClass())) { - to_value->SetL(ref); - return true; - } else { - ThrowClassCastException(to.Get(), ref->GetClass()); - return false; - } - } else { - // Precisely one of the source or the destination are reference types. - // We must box or unbox. - if (to_type == Primitive::kPrimNot) { - // The target type is a reference, we must box. - Primitive::Type type; - // TODO(narayan): This is a CHECK for now. There might be a few corner cases - // here that we might not have handled yet. For exmple, if |to| is java/lang/Number;, - // we will need to box this "naturally". - CHECK(GetPrimitiveType(to.Get(), &type)); - // First perform a primitive conversion to the unboxed equivalent of the target, - // if necessary. This should be for the rarer cases like (int->Long) etc. - if (UNLIKELY(from_type != type)) { - if (!ConvertPrimitiveValue(false, from_type, type, from_value, to_value)) { - return false; - } - } else { - *to_value = from_value; - } - - // Then perform the actual boxing, and then set the reference. Note that - // BoxPrimitive can return null if an OOM occurs. - ObjPtr<mirror::Object> boxed = BoxPrimitive(type, from_value); - if (boxed.Ptr() == nullptr) { - DCHECK(Thread::Current()->IsExceptionPending()); - return false; - } - - to_value->SetL(boxed.Ptr()); - return true; - } else { - // The target type is a primitive, we must unbox. - ObjPtr<mirror::Object> ref(from_value.GetL()); - - // Note that UnboxPrimitiveForResult already performs all of the type - // conversions that we want, based on |to|. - JValue unboxed_value; - return UnboxPrimitiveForResult(ref, to.Get(), to_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) { + return true; } - 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)) { + DCHECK(!Thread::Current()->IsExceptionPending()); + return true; + } else { + DCHECK(Thread::Current()->IsExceptionPending()); + value->SetJ(0); + return false; + } } template <typename G, typename S> bool PerformConversions(Thread* self, - Handle<mirror::ObjectArray<mirror::Class>> from_types, - Handle<mirror::ObjectArray<mirror::Class>> to_types, + Handle<mirror::MethodType> callsite_type, + Handle<mirror::MethodType> callee_type, G* getter, S* setter, - int32_t num_conversions) { + int32_t num_conversions) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<2> hs(self); - MutableHandle<mirror::Class> from(hs.NewHandle<mirror::Class>(nullptr)); - MutableHandle<mirror::Class> to(hs.NewHandle<mirror::Class>(nullptr)); + 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 = 0; i < num_conversions; ++i) { - from.Assign(from_types->GetWithoutChecks(i)); - to.Assign(to_types->GetWithoutChecks(i)); - - const Primitive::Type from_type = from->GetPrimitiveType(); - const Primitive::Type to_type = to->GetPrimitiveType(); - - if (from.Get() == to.Get()) { + ObjPtr<mirror::Class> from(from_types->GetWithoutChecks(i)); + ObjPtr<mirror::Class> to(to_types->GetWithoutChecks(i)); + const Primitive::Type from_type = from_types->GetWithoutChecks(i)->GetPrimitiveType(); + const Primitive::Type to_type = to_types->GetWithoutChecks(i)->GetPrimitiveType(); + if (from == to) { // Easy case - the types are identical. Nothing left to do except to pass // the arguments along verbatim. if (Primitive::Is64BitType(from_type)) { @@ -162,28 +105,28 @@ bool PerformConversions(Thread* self, setter->Set(getter->Get()); } } else { - JValue from_value; - JValue to_value; + JValue value; if (Primitive::Is64BitType(from_type)) { - from_value.SetJ(getter->GetLong()); + value.SetJ(getter->GetLong()); } else if (from_type == Primitive::kPrimNot) { - from_value.SetL(getter->GetReference()); + value.SetL(getter->GetReference()); } else { - from_value.SetI(getter->Get()); + value.SetI(getter->Get()); } - if (!ConvertJValue(from, to, from_value, &to_value)) { + // Caveat emptor - ObjPtr's not guaranteed valid after this call. + if (!ConvertArgumentValue(callsite_type, callee_type, i, &value)) { DCHECK(self->IsExceptionPending()); return false; } if (Primitive::Is64BitType(to_type)) { - setter->SetLong(to_value.GetJ()); + setter->SetLong(value.GetJ()); } else if (to_type == Primitive::kPrimNot) { - setter->SetReference(to_value.GetL()); + setter->SetReference(value.GetL()); } else { - setter->Set(to_value.GetI()); + setter->Set(value.GetI()); } } } @@ -199,10 +142,10 @@ bool ConvertAndCopyArgumentsFromCallerFrame(Thread* self, uint32_t first_src_reg, uint32_t first_dest_reg, const uint32_t (&arg)[Instruction::kMaxVarArgRegs], - ShadowFrame* callee_frame) { - StackHandleScope<4> 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())); + ShadowFrame* callee_frame) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr<mirror::ObjectArray<mirror::Class>> from_types(callsite_type->GetPTypes()); + ObjPtr<mirror::ObjectArray<mirror::Class>> to_types(callee_type->GetPTypes()); const int32_t num_method_params = from_types->GetLength(); if (to_types->GetLength() != num_method_params) { @@ -214,8 +157,8 @@ bool ConvertAndCopyArgumentsFromCallerFrame(Thread* self, ShadowFrameSetter setter(callee_frame, first_dest_reg); return PerformConversions<ShadowFrameGetter<is_range>, ShadowFrameSetter>(self, - from_types, - to_types, + callsite_type, + callee_type, &getter, &setter, num_method_params); diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc new file mode 100644 index 0000000000..491d13926f --- /dev/null +++ b/runtime/method_handles.cc @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "method_handles.h" + +#include "method_handles-inl.h" +#include "jvalue.h" +#include "jvalue-inl.h" +#include "reflection.h" +#include "reflection-inl.h" + +namespace art { + +namespace { + +static const char* kBoxedBooleanClass = "Ljava/lang/Boolean;"; +static const char* kBoxedByteClass = "Ljava/lang/Byte;"; +static const char* kBoxedCharacterClass = "Ljava/lang/Character;"; +static const char* kBoxedDoubleClass = "Ljava/lang/Double;"; +static const char* kBoxedFloatClass = "Ljava/lang/Float;"; +static const char* kBoxedIntegerClass = "Ljava/lang/Integer;"; +static const char* kBoxedLongClass = "Ljava/lang/Long;"; +static const char* kBoxedShortClass = "Ljava/lang/Short;"; + +// Assigns |type| to the primitive type associated with |klass|. Returns +// true iff. |klass| was a boxed type (Integer, Long etc.), false otherwise. +bool GetUnboxedPrimitiveType(ObjPtr<mirror::Class> klass, Primitive::Type* type) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedAssertNoThreadSuspension ants(__FUNCTION__); + if (klass->DescriptorEquals(kBoxedBooleanClass)) { + (*type) = Primitive::kPrimBoolean; + return true; + } else if (klass->DescriptorEquals(kBoxedByteClass)) { + (*type) = Primitive::kPrimByte; + return true; + } else if (klass->DescriptorEquals(kBoxedCharacterClass)) { + (*type) = Primitive::kPrimChar; + return true; + } else if (klass->DescriptorEquals(kBoxedFloatClass)) { + (*type) = Primitive::kPrimFloat; + return true; + } else if (klass->DescriptorEquals(kBoxedDoubleClass)) { + (*type) = Primitive::kPrimDouble; + return true; + } else if (klass->DescriptorEquals(kBoxedIntegerClass)) { + (*type) = Primitive::kPrimInt; + return true; + } else if (klass->DescriptorEquals(kBoxedLongClass)) { + (*type) = Primitive::kPrimLong; + return true; + } else if (klass->DescriptorEquals(kBoxedShortClass)) { + (*type) = Primitive::kPrimShort; + return true; + } else { + return false; + } +} + +// Returns the class corresponding to the boxed type for the primitive |type|. +ObjPtr<mirror::Class> GetBoxedPrimitiveClass(Primitive::Type type) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedAssertNoThreadSuspension ants(__FUNCTION__); + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + switch (type) { + case Primitive::kPrimBoolean: + return class_linker->FindSystemClass(Thread::Current(), kBoxedBooleanClass); + case Primitive::kPrimByte: + return class_linker->FindSystemClass(Thread::Current(), kBoxedByteClass); + case Primitive::kPrimChar: + return class_linker->FindSystemClass(Thread::Current(), kBoxedCharacterClass); + case Primitive::kPrimShort: + return class_linker->FindSystemClass(Thread::Current(), kBoxedShortClass); + case Primitive::kPrimInt: + return class_linker->FindSystemClass(Thread::Current(), kBoxedIntegerClass); + case Primitive::kPrimLong: + return class_linker->FindSystemClass(Thread::Current(), kBoxedLongClass); + case Primitive::kPrimFloat: + return class_linker->FindSystemClass(Thread::Current(), kBoxedFloatClass); + case Primitive::kPrimDouble: + return class_linker->FindSystemClass(Thread::Current(), kBoxedDoubleClass); + case Primitive::kPrimNot: + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable"; + return nullptr; + } +} + +// Returns true if |klass| is a boxed primitive type or a sub-class of a boxed primitive type. +bool IsSubClassOfBoxedPrimitive(const Handle<mirror::Class>& klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<1> hs(Thread::Current()); + MutableHandle<mirror::Class> h_klass(hs.NewHandle(klass.Get())); + do { + Primitive::Type type; + if (GetUnboxedPrimitiveType(h_klass.Get(), &type)) { + return true; + } + h_klass.Assign(h_klass->GetSuperClass()); + } while (h_klass.Get() != nullptr); + return false; +} + +// Unboxed the value |o| to |unboxed_value| of type |dst_class|. +// |unboxed_value| must be zero on entry to avoid dangling pointers. +// Returns true on success, false if an exception is raised. +bool UnboxPrimitiveForMethodHandles(ObjPtr<mirror::Object> o, + ObjPtr<mirror::Class> dst_class, + JValue* unboxed_value) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Check unboxed_value does not contain a dangling pointer. + DCHECK_EQ(unboxed_value->GetJ(), 0); + DCHECK(dst_class->IsPrimitive()); + + // This is derived from UnboxPrimitive() in reflection.cc, but with + // exceptions appropriate to method handles. + if (UNLIKELY(dst_class->GetPrimitiveType() == Primitive::kPrimVoid)) { + ThrowClassCastException(o->GetClass(), dst_class); + return false; + } + if (UNLIKELY(o == nullptr)) { + ThrowNullPointerException( + StringPrintf("Expected to unbox a '%s' primitive type but was returned null", + dst_class->PrettyDescriptor().c_str()).c_str()); + return false; + } + + JValue boxed_value; + ObjPtr<mirror::Class> klass = o->GetClass(); + ObjPtr<mirror::Class> src_class = nullptr; + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + ArtField* primitive_field = &klass->GetIFieldsPtr()->At(0); + if (klass->DescriptorEquals(kBoxedBooleanClass)) { + src_class = class_linker->FindPrimitiveClass('Z'); + boxed_value.SetZ(primitive_field->GetBoolean(o)); + } else if (klass->DescriptorEquals(kBoxedByteClass)) { + src_class = class_linker->FindPrimitiveClass('B'); + boxed_value.SetB(primitive_field->GetByte(o)); + } else if (klass->DescriptorEquals(kBoxedCharacterClass)) { + src_class = class_linker->FindPrimitiveClass('C'); + boxed_value.SetC(primitive_field->GetChar(o)); + } else if (klass->DescriptorEquals(kBoxedFloatClass)) { + src_class = class_linker->FindPrimitiveClass('F'); + boxed_value.SetF(primitive_field->GetFloat(o)); + } else if (klass->DescriptorEquals(kBoxedDoubleClass)) { + src_class = class_linker->FindPrimitiveClass('D'); + boxed_value.SetD(primitive_field->GetDouble(o)); + } else if (klass->DescriptorEquals(kBoxedIntegerClass)) { + src_class = class_linker->FindPrimitiveClass('I'); + boxed_value.SetI(primitive_field->GetInt(o)); + } else if (klass->DescriptorEquals(kBoxedLongClass)) { + src_class = class_linker->FindPrimitiveClass('J'); + boxed_value.SetJ(primitive_field->GetLong(o)); + } else if (klass->DescriptorEquals(kBoxedShortClass)) { + src_class = class_linker->FindPrimitiveClass('S'); + boxed_value.SetS(primitive_field->GetShort(o)); + } else { + std::string temp; + ThrowIllegalArgumentException( + StringPrintf("result has type %s, got %s", + dst_class->PrettyDescriptor().c_str(), + PrettyDescriptor(o->GetClass()->GetDescriptor(&temp)).c_str()).c_str()); + return false; + } + + if (!ConvertPrimitiveValueNoThrow(src_class->GetPrimitiveType(), + dst_class->GetPrimitiveType(), + boxed_value, + unboxed_value)) { + ThrowClassCastException(src_class, dst_class); + return false; + } + return true; +} + +inline bool IsReferenceType(Primitive::Type type) { + return type == Primitive::kPrimNot; +} + +inline bool IsPrimitiveType(Primitive::Type type) { + return !IsReferenceType(type); +} + +} // namespace + +bool ConvertJValueCommon( + Handle<mirror::MethodType> callsite_type, + Handle<mirror::MethodType> callee_type, + ObjPtr<mirror::Class> from, + ObjPtr<mirror::Class> to, + 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 + // of boxing |value| contains a non-reference type. + + const Primitive::Type from_type = from->GetPrimitiveType(); + const Primitive::Type to_type = to->GetPrimitiveType(); + + // This method must be called only when the types don't match. + DCHECK(from != to); + + if (IsPrimitiveType(from_type) && IsPrimitiveType(to_type)) { + // The source and target types are both primitives. + if (UNLIKELY(!ConvertPrimitiveValueNoThrow(from_type, to_type, *value, value))) { + ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get()); + value->SetJ(0); + return false; + } + return true; + } else if (IsReferenceType(from_type) && IsReferenceType(to_type)) { + // They're both reference types. If "from" is null, we can pass it + // through unchanged. If not, we must generate a cast exception if + // |to| is not assignable from the dynamic type of |ref|. + // + // Playing it safe with StackHandleScope here, not expecting any allocation + // in mirror::Class::IsAssignable(). + StackHandleScope<2> hs(Thread::Current()); + Handle<mirror::Class> h_to(hs.NewHandle(to)); + Handle<mirror::Object> h_obj(hs.NewHandle(value->GetL())); + + // |value| will now be the result value, invalidate its existing value + // as |h_obj| now owns it. + value->SetJ(0); + + if (h_obj.Get() != nullptr && !to->IsAssignableFrom(h_obj->GetClass())) { + ThrowClassCastException(h_to.Get(), h_obj->GetClass()); + return false; + } + value->SetL(h_obj.Get()); + return true; + } else if (IsReferenceType(to_type)) { + DCHECK(IsPrimitiveType(from_type)); + // Playing it safe with StackHandleScope here with regards to + // GetUnboxedPrimitiveType() and GetBoxedPrimitiveClass(). + StackHandleScope<1> hs(Thread::Current()); + Handle<mirror::Class> h_to(hs.NewHandle(to)); + // The source type is a primitive and the target type is a reference, so we must box. + // The target type maybe a super class of the boxed source type, for example, + // if the source type is int, it's boxed type is java.lang.Integer, and the target + // type could be java.lang.Number. + Primitive::Type type; + if (!GetUnboxedPrimitiveType(to, &type)) { + ObjPtr<mirror::Class> boxed_from_class = GetBoxedPrimitiveClass(from_type); + if (boxed_from_class->IsSubClass(h_to.Get())) { + type = from_type; + } else { + value->SetJ(0); + ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get()); + return false; + } + } + + if (UNLIKELY(from_type != type)) { + value->SetJ(0); + ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get()); + return false; + } + + if (!ConvertPrimitiveValueNoThrow(from_type, type, *value, value)) { + value->SetJ(0); + ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get()); + return false; + } + + // Then perform the actual boxing, and then set the reference. + ObjPtr<mirror::Object> boxed = BoxPrimitive(type, *value); + value->SetL(boxed.Ptr()); + return true; + } else { + // The source type is a reference and the target type is a primitive, so we must unbox. + DCHECK(IsReferenceType(from_type)); + DCHECK(IsPrimitiveType(to_type)); + + // Use StackHandleScope to protect |from|, |to|, and the reference + // in |value| from heap re-arrangements that could be triggered + // ahead of unboxing step. + StackHandleScope<3> hs(Thread::Current()); + Handle<mirror::Class> h_to(hs.NewHandle(to)); + Handle<mirror::Class> h_from(hs.NewHandle(from)); + Handle<mirror::Object> h_obj(hs.NewHandle(value->GetL())); + + // |value| will now be the result value, invalidate its existing value + // as |h_obj| now owns it. + value->SetJ(0); + + // Check source type is a boxed primitive or has a boxed primitive super-class. + ObjPtr<mirror::Class> boxed_to_class = GetBoxedPrimitiveClass(to_type); + if (!IsSubClassOfBoxedPrimitive(h_from) && !boxed_to_class->IsSubClass(h_from.Get())) { + ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get()); + return false; + } + + if (h_obj.Get() == nullptr) { + ThrowNullPointerException( + StringPrintf("Expected to unbox a '%s' but instance was null", + h_from->PrettyDescriptor().c_str()).c_str()); + return false; + } + + return UnboxPrimitiveForMethodHandles(h_obj.Get(), h_to.Get(), value); + } +} + +} // namespace art diff --git a/runtime/method_handles.h b/runtime/method_handles.h index 06509bf13a..0cc69f2046 100644 --- a/runtime/method_handles.h +++ b/runtime/method_handles.h @@ -20,7 +20,10 @@ #include <ostream> #include "dex_instruction.h" +#include "handle.h" #include "jvalue.h" +#include "mirror/class.h" +#include "mirror/method_type.h" namespace art { @@ -56,12 +59,36 @@ inline bool IsInvoke(const MethodHandleKind handle_kind) { return handle_kind <= kLastInvokeKind; } -// Performs a single argument conversion from type |from| to a distinct -// type |to|. Returns true on success, false otherwise. -ALWAYS_INLINE bool ConvertJValue(Handle<mirror::Class> from, - Handle<mirror::Class> to, - const JValue& from_value, - JValue* to_value) REQUIRES_SHARED(Locks::mutator_lock_); +// 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, + ObjPtr<mirror::Class> from, + ObjPtr<mirror::Class> to, + 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) + 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) + 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 @@ -109,8 +136,8 @@ ALWAYS_INLINE bool ConvertJValue(Handle<mirror::Class> from, // overridden by concrete classes. template <typename G, typename S> bool PerformConversions(Thread* self, - Handle<mirror::ObjectArray<mirror::Class>> from_types, - Handle<mirror::ObjectArray<mirror::Class>> to_types, + Handle<mirror::MethodType> callsite_type, + Handle<mirror::MethodType> callee_type, G* getter, S* setter, int32_t num_conversions) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc index 861d416489..d607040486 100644 --- a/runtime/mirror/emulated_stack_frame.cc +++ b/runtime/mirror/emulated_stack_frame.cc @@ -188,7 +188,7 @@ mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs( ShadowFrameGetter<is_range> getter(first_src_reg, arg, caller_frame); EmulatedStackFrameAccessor setter(references, stack_frame, stack_frame->GetLength()); if (!PerformConversions<ShadowFrameGetter<is_range>, EmulatedStackFrameAccessor>( - self, from_types, to_types, &getter, &setter, num_method_params)) { + self, caller_type, callee_type, &getter, &setter, num_method_params)) { return nullptr; } @@ -206,9 +206,8 @@ bool EmulatedStackFrame::WriteToShadowFrame(Thread* self, Handle<mirror::MethodType> callee_type, const uint32_t first_dest_reg, ShadowFrame* callee_frame) { - StackHandleScope<4> hs(self); - Handle<mirror::ObjectArray<mirror::Class>> from_types(hs.NewHandle(GetType()->GetPTypes())); - Handle<mirror::ObjectArray<mirror::Class>> to_types(hs.NewHandle(callee_type->GetPTypes())); + ObjPtr<mirror::ObjectArray<mirror::Class>> from_types(GetType()->GetPTypes()); + ObjPtr<mirror::ObjectArray<mirror::Class>> to_types(callee_type->GetPTypes()); const int32_t num_method_params = from_types->GetLength(); if (to_types->GetLength() != num_method_params) { @@ -216,6 +215,8 @@ bool EmulatedStackFrame::WriteToShadowFrame(Thread* self, return false; } + StackHandleScope<3> hs(self); + Handle<mirror::MethodType> frame_callsite_type(hs.NewHandle(GetType())); Handle<mirror::ObjectArray<mirror::Object>> references(hs.NewHandle(GetReferences())); Handle<ByteArray> stack_frame(hs.NewHandle(GetStackFrame())); @@ -223,7 +224,7 @@ bool EmulatedStackFrame::WriteToShadowFrame(Thread* self, ShadowFrameSetter setter(callee_frame, first_dest_reg); return PerformConversions<EmulatedStackFrameAccessor, ShadowFrameSetter>( - self, from_types, to_types, &getter, &setter, num_method_params); + self, frame_callsite_type, callee_type, &getter, &setter, num_method_params); } void EmulatedStackFrame::GetReturnValue(Thread* self, JValue* value) { diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h index c4d4fae17c..68e7a10e01 100644 --- a/runtime/reflection-inl.h +++ b/runtime/reflection-inl.h @@ -29,11 +29,10 @@ namespace art { -inline bool ConvertPrimitiveValue(bool unbox_for_result, - Primitive::Type srcType, - Primitive::Type dstType, - const JValue& src, - JValue* dst) { +inline bool ConvertPrimitiveValueNoThrow(Primitive::Type srcType, + Primitive::Type dstType, + const JValue& src, + JValue* dst) { DCHECK(srcType != Primitive::kPrimNot && dstType != Primitive::kPrimNot); if (LIKELY(srcType == dstType)) { dst->SetJ(src.GetJ()); @@ -91,6 +90,18 @@ inline bool ConvertPrimitiveValue(bool unbox_for_result, default: break; } + return false; +} + +inline bool ConvertPrimitiveValue(bool unbox_for_result, + Primitive::Type srcType, + Primitive::Type dstType, + const JValue& src, + JValue* dst) { + if (ConvertPrimitiveValueNoThrow(srcType, dstType, src, dst)) { + return true; + } + if (!unbox_for_result) { ThrowIllegalArgumentException(StringPrintf("Invalid primitive conversion from %s to %s", PrettyDescriptor(srcType).c_str(), diff --git a/runtime/reflection.h b/runtime/reflection.h index 6e5ef712a7..f2652fd4b6 100644 --- a/runtime/reflection.h +++ b/runtime/reflection.h @@ -47,6 +47,12 @@ bool UnboxPrimitiveForResult(ObjPtr<mirror::Object> o, JValue* unboxed_value) REQUIRES_SHARED(Locks::mutator_lock_); +ALWAYS_INLINE bool ConvertPrimitiveValueNoThrow(Primitive::Type src_class, + Primitive::Type dst_class, + const JValue& src, + JValue* dst) + REQUIRES_SHARED(Locks::mutator_lock_); + ALWAYS_INLINE bool ConvertPrimitiveValue(bool unbox_for_result, Primitive::Type src_class, Primitive::Type dst_class, diff --git a/test/955-methodhandles-smali/expected.txt b/test/955-methodhandles-smali/expected.txt index 047a287eb8..5de1274e37 100644 --- a/test/955-methodhandles-smali/expected.txt +++ b/test/955-methodhandles-smali/expected.txt @@ -5,4 +5,5 @@ 40 43 44 -0-11 +0 +-1 diff --git a/test/955-methodhandles-smali/smali/Main.smali b/test/955-methodhandles-smali/smali/Main.smali index 9681d56f00..52460a8985 100644 --- a/test/955-methodhandles-smali/smali/Main.smali +++ b/test/955-methodhandles-smali/smali/Main.smali @@ -220,24 +220,22 @@ invoke-polymorphic {v0, v1, v1}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Long;Ljava/lang/Long;)I move-result v3 sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream; - invoke-virtual {v4, v3}, Ljava/io/PrintStream;->print(I)V + invoke-virtual {v4, v3}, Ljava/io/PrintStream;->println(I)V # Call compareTo(long) - this is an implicit box. const-wide v2, 44 invoke-polymorphic {v0, v1, v2, v3}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Long;J)I move-result v3 sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream; - invoke-virtual {v4, v3}, Ljava/io/PrintStream;->print(I)V + invoke-virtual {v4, v3}, Ljava/io/PrintStream;->println(I)V # Call compareTo(int) - this is an implicit box. - const v2, 40 - invoke-polymorphic {v0, v1, v2}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Long;I)I - move-result v3 - sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream; - invoke-virtual {v4, v3}, Ljava/io/PrintStream;->print(I)V - - # Add a newline at the end of file. - invoke-virtual {v4}, Ljava/io/PrintStream;->println()V +# This throws WrongMethodTypeException as it's a two step conversion int->long->Long or int->Integer->Long. +# const v2, 40 +# invoke-polymorphic {v0, v1, v2}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Long;I)I +# move-result v3 +# sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream; +# invoke-virtual {v4, v3}, Ljava/io/PrintStream;->print(I)V return-void .end method diff --git a/test/979-invoke-polymorphic-accessors/build b/test/959-invoke-polymorphic-accessors/build index a423ca6b4e..a423ca6b4e 100644 --- a/test/979-invoke-polymorphic-accessors/build +++ b/test/959-invoke-polymorphic-accessors/build diff --git a/test/959-invoke-polymorphic-accessors/expected.txt b/test/959-invoke-polymorphic-accessors/expected.txt new file mode 100644 index 0000000000..de2916b6dd --- /dev/null +++ b/test/959-invoke-polymorphic-accessors/expected.txt @@ -0,0 +1,4 @@ +1515870810 +Passed MethodHandles.Lookup tests for accessors. +Passed MethodHandle.invokeExact() tests for accessors. +Passed MethodHandle.invoke() tests for accessors. diff --git a/test/979-invoke-polymorphic-accessors/info.txt b/test/959-invoke-polymorphic-accessors/info.txt index b2f55f0172..b2f55f0172 100644 --- a/test/979-invoke-polymorphic-accessors/info.txt +++ b/test/959-invoke-polymorphic-accessors/info.txt diff --git a/test/979-invoke-polymorphic-accessors/run b/test/959-invoke-polymorphic-accessors/run index a9f182288c..a9f182288c 100644 --- a/test/979-invoke-polymorphic-accessors/run +++ b/test/959-invoke-polymorphic-accessors/run diff --git a/test/979-invoke-polymorphic-accessors/src/Main.java b/test/959-invoke-polymorphic-accessors/src/Main.java index 8f1e361dc3..824a436f3b 100644 --- a/test/979-invoke-polymorphic-accessors/src/Main.java +++ b/test/959-invoke-polymorphic-accessors/src/Main.java @@ -44,7 +44,26 @@ public class Main { public static final int s_fi = 0x5a5a5a5a; } - public static class InvokeExactTester { + public static class Tester { + public static void assertActualAndExpectedMatch(boolean actual, boolean expected) + throws AssertionError { + if (actual != expected) { + throw new AssertionError("Actual != Expected (" + actual + " != " + expected + ")"); + } + } + + public static void assertTrue(boolean value) throws AssertionError { + if (!value) { + throw new AssertionError("Value is not true"); + } + } + + public static void unreachable() throws Throwable{ + throw new Error("unreachable"); + } + } + + public static class InvokeExactTester extends Tester { private enum PrimitiveType { Boolean, Byte, @@ -64,19 +83,6 @@ public class Main { SGET, } - private static void assertActualAndExpectedMatch(boolean actual, boolean expected) - throws AssertionError { - if (actual != expected) { - throw new AssertionError("Actual != Expected (" + actual + " != " + expected + ")"); - } - } - - private static void assertTrue(boolean value) throws AssertionError { - if (!value) { - throw new AssertionError("Value is not true"); - } - } - static void setByte(MethodHandle m, ValueHolder v, byte value, boolean expectFailure) throws Throwable { boolean exceptionThrown = false; @@ -677,11 +683,11 @@ public class Main { assertTrue(s.equals(ValueHolder.s_l)); } - System.out.println("Passed InvokeExact tests for accessors."); + System.out.println("Passed MethodHandle.invokeExact() tests for accessors."); } } - public static class FindAccessorTester { + public static class FindAccessorTester extends Tester { public static void main() throws Throwable { // NB having a static field test here is essential for // this test. MethodHandles need to ensure the class @@ -723,10 +729,161 @@ public class Main { lookup.findSetter(ValueHolder.class, "m_fi", int.class); unreachable(); } catch (IllegalAccessException e) {} + + System.out.println("Passed MethodHandles.Lookup tests for accessors."); } + } - public static void unreachable() throws Throwable{ - throw new Error("unreachable"); + public static class InvokeTester extends Tester { + private static void testStaticGetter() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle h0 = lookup.findStaticGetter(ValueHolder.class, "s_fi", int.class); + h0.invoke(); + Number t = (Number)h0.invoke(); + int u = (int)h0.invoke(); + Integer v = (Integer)h0.invoke(); + long w = (long)h0.invoke(); + try { + byte x = (byte)h0.invoke(); + unreachable(); + } catch (WrongMethodTypeException e) {} + try { + String y = (String)h0.invoke(); + unreachable(); + } catch (WrongMethodTypeException e) {} + try { + Long z = (Long)h0.invoke(); + unreachable(); + } catch (WrongMethodTypeException e) {} + } + + private static void testMemberGetter() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle h0 = lookup.findGetter(ValueHolder.class, "m_fi", int.class); + h0.invoke(valueHolder); + Number t = (Number)h0.invoke(valueHolder); + int u = (int)h0.invoke(valueHolder); + Integer v = (Integer)h0.invoke(valueHolder); + long w = (long)h0.invoke(valueHolder); + try { + byte x = (byte)h0.invoke(valueHolder); + unreachable(); + } catch (WrongMethodTypeException e) {} + try { + String y = (String)h0.invoke(valueHolder); + unreachable(); + } catch (WrongMethodTypeException e) {} + try { + Long z = (Long)h0.invoke(valueHolder); + unreachable(); + } catch (WrongMethodTypeException e) {} + } + + private static void testMemberSetter() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle h0 = lookup.findSetter(ValueHolder.class, "m_f", float.class); + h0.invoke(valueHolder, 0.22f); + h0.invoke(valueHolder, new Float(1.11f)); + Number floatNumber = new Float(0.88f); + h0.invoke(valueHolder, floatNumber); + assertTrue(valueHolder.m_f == floatNumber.floatValue()); + + try { + h0.invoke(valueHolder, (Float)null); + unreachable(); + } catch (NullPointerException e) {} + + h0.invoke(valueHolder, (byte)1); + h0.invoke(valueHolder, (short)2); + h0.invoke(valueHolder, 3); + h0.invoke(valueHolder, 4l); + try { + h0.invoke(valueHolder, 0.33); + unreachable(); + } catch (WrongMethodTypeException e) {} + try { + Number doubleNumber = new Double(0.89); + h0.invoke(valueHolder, doubleNumber); + unreachable(); + } catch (ClassCastException e) {} + try { + Number doubleNumber = null; + h0.invoke(valueHolder, doubleNumber); + unreachable(); + } catch (NullPointerException e) {} + try { + // Mismatched return type - float != void + float tmp = (float)h0.invoke(valueHolder, 0.45f); + assertTrue(tmp == 0.0); + } catch (Exception e) { unreachable(); } + try { + h0.invoke(valueHolder, "bam"); + unreachable(); + } catch (WrongMethodTypeException e) {} + try { + String s = null; + h0.invoke(valueHolder, s); + unreachable(); + } catch (WrongMethodTypeException e) {} + } + + private static void testStaticSetter() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle h0 = lookup.findStaticSetter(ValueHolder.class, "s_f", float.class); + h0.invoke(0.22f); + h0.invoke(new Float(1.11f)); + Number floatNumber = new Float(0.88f); + h0.invoke(floatNumber); + assertTrue(ValueHolder.s_f == floatNumber.floatValue()); + + try { + h0.invoke((Float)null); + unreachable(); + } catch (NullPointerException e) {} + + h0.invoke((byte)1); + h0.invoke((short)2); + h0.invoke(3); + h0.invoke(4l); + try { + h0.invoke(0.33); + unreachable(); + } catch (WrongMethodTypeException e) {} + try { + Number doubleNumber = new Double(0.89); + h0.invoke(doubleNumber); + unreachable(); + } catch (ClassCastException e) {} + try { + Number doubleNumber = new Double(1.01); + doubleNumber = (doubleNumber.doubleValue() != 0.1) ? null : doubleNumber; + h0.invoke(doubleNumber); + unreachable(); + } catch (NullPointerException e) {} + try { + // Mismatched return type - float != void + float tmp = (float)h0.invoke(0.45f); + assertTrue(tmp == 0.0); + } catch (Exception e) { unreachable(); } + try { + h0.invoke("bam"); + unreachable(); + } catch (WrongMethodTypeException e) {} + try { + String s = null; + h0.invoke(s); + unreachable(); + } catch (WrongMethodTypeException e) {} + } + + public static void main() throws Throwable{ + testStaticGetter(); + testMemberGetter(); + testStaticSetter(); + testMemberSetter(); + System.out.println("Passed MethodHandle.invoke() tests for accessors."); } } @@ -735,5 +892,6 @@ public class Main { // file to ensure class initialization test is run. FindAccessorTester.main(); InvokeExactTester.main(); + InvokeTester.main(); } } diff --git a/test/979-invoke-polymorphic-accessors/expected.txt b/test/979-invoke-polymorphic-accessors/expected.txt deleted file mode 100644 index 22f9f2d34c..0000000000 --- a/test/979-invoke-polymorphic-accessors/expected.txt +++ /dev/null @@ -1,2 +0,0 @@ -1515870810 -Passed InvokeExact tests for accessors. |