From 3d617ac3d87f7fa98793406818e54b057bd701a1 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Wed, 19 Oct 2016 14:00:46 +0100 Subject: [PATCH] Getter/Setter support for invoke-polymorphic of invokeExact(). Test: make test-art-host Bug: 30550796 Change-Id: I427a6e0afba88b223655ad1ba30843aaf255182b --- runtime/interpreter/interpreter_common.cc | 313 +++++++-- runtime/interpreter/interpreter_switch_impl.cc | 1 - runtime/mirror/method_handle_impl.h | 5 + runtime/native/java_lang_reflect_Field.cc | 7 + test/979-invoke-polymorphic-accessors/build | 25 + test/979-invoke-polymorphic-accessors/expected.txt | 1 + test/979-invoke-polymorphic-accessors/info.txt | 1 + test/979-invoke-polymorphic-accessors/run | 20 + .../979-invoke-polymorphic-accessors/src/Main.java | 727 +++++++++++++++++++++ 9 files changed, 1037 insertions(+), 63 deletions(-) create mode 100644 test/979-invoke-polymorphic-accessors/build create mode 100644 test/979-invoke-polymorphic-accessors/expected.txt create mode 100644 test/979-invoke-polymorphic-accessors/info.txt create mode 100644 test/979-invoke-polymorphic-accessors/run create mode 100644 test/979-invoke-polymorphic-accessors/src/Main.java diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 1ed3d550b..73fc41028 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -42,6 +42,59 @@ void ThrowNullPointerExceptionFromInterpreter() { ThrowNullPointerExceptionFromDexPC(); } +template +static ALWAYS_INLINE void DoFieldGetCommon(Thread* self, + const ShadowFrame& shadow_frame, + ObjPtr& obj, + ArtField* field, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { + field->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self); + + // Report this field access to instrumentation if needed. + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + if (UNLIKELY(instrumentation->HasFieldReadListeners())) { + StackHandleScope<1> hs(self); + // Wrap in handle wrapper in case the listener does thread suspension. + HandleWrapperObjPtr h(hs.NewHandleWrapper(&obj)); + ObjPtr this_object; + if (!field->IsStatic()) { + this_object = obj; + } + instrumentation->FieldReadEvent(self, + this_object.Ptr(), + shadow_frame.GetMethod(), + shadow_frame.GetDexPC(), + field); + } + + switch (field_type) { + case Primitive::kPrimBoolean: + result->SetZ(field->GetBoolean(obj)); + break; + case Primitive::kPrimByte: + result->SetB(field->GetByte(obj)); + break; + case Primitive::kPrimChar: + result->SetC(field->GetChar(obj)); + break; + case Primitive::kPrimShort: + result->SetS(field->GetShort(obj)); + break; + case Primitive::kPrimInt: + result->SetI(field->GetInt(obj)); + break; + case Primitive::kPrimLong: + result->SetJ(field->GetLong(obj)); + break; + case Primitive::kPrimNot: + result->SetL(field->GetObject(obj)); + break; + default: + LOG(FATAL) << "Unreachable: " << field_type; + UNREACHABLE(); + } +} + template bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { @@ -64,45 +117,31 @@ bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst return false; } } - f->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self); - // Report this field access to instrumentation if needed. - instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); - if (UNLIKELY(instrumentation->HasFieldReadListeners())) { - StackHandleScope<1> hs(self); - // Wrap in handle wrapper in case the listener does thread suspension. - HandleWrapperObjPtr h(hs.NewHandleWrapper(&obj)); - ObjPtr this_object; - if (!f->IsStatic()) { - this_object = obj; - } - instrumentation->FieldReadEvent(self, - this_object.Ptr(), - shadow_frame.GetMethod(), - shadow_frame.GetDexPC(), - f); - } + + JValue result; + DoFieldGetCommon(self, shadow_frame, obj, f, &result); uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data); switch (field_type) { case Primitive::kPrimBoolean: - shadow_frame.SetVReg(vregA, f->GetBoolean(obj)); + shadow_frame.SetVReg(vregA, result.GetZ()); break; case Primitive::kPrimByte: - shadow_frame.SetVReg(vregA, f->GetByte(obj)); + shadow_frame.SetVReg(vregA, result.GetB()); break; case Primitive::kPrimChar: - shadow_frame.SetVReg(vregA, f->GetChar(obj)); + shadow_frame.SetVReg(vregA, result.GetC()); break; case Primitive::kPrimShort: - shadow_frame.SetVReg(vregA, f->GetShort(obj)); + shadow_frame.SetVReg(vregA, result.GetS()); break; case Primitive::kPrimInt: - shadow_frame.SetVReg(vregA, f->GetInt(obj)); + shadow_frame.SetVReg(vregA, result.GetI()); break; case Primitive::kPrimLong: - shadow_frame.SetVRegLong(vregA, f->GetLong(obj)); + shadow_frame.SetVRegLong(vregA, result.GetJ()); break; case Primitive::kPrimNot: - shadow_frame.SetVRegReference(vregA, f->GetObject(obj).Ptr()); + shadow_frame.SetVRegReference(vregA, result.GetL()); break; default: LOG(FATAL) << "Unreachable: " << field_type; @@ -143,6 +182,48 @@ EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticObjectRead, Primitive::kPrimNot) #undef EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL #undef EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL +// Helper for getters in invoke-polymorphic. +inline static void DoFieldGetForInvokePolymorphic(Thread* self, + const ShadowFrame& shadow_frame, + ObjPtr& obj, + ArtField* field, + Primitive::Type field_type, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + switch (field_type) { + case Primitive::kPrimBoolean: + DoFieldGetCommon(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimByte: + DoFieldGetCommon(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimChar: + DoFieldGetCommon(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimShort: + DoFieldGetCommon(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimInt: + DoFieldGetCommon(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimLong: + DoFieldGetCommon(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimFloat: + DoFieldGetCommon(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimDouble: + DoFieldGetCommon(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimNot: + DoFieldGetCommon(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable: " << field_type; + UNREACHABLE(); + } +} + // Handles iget-quick, iget-wide-quick and iget-object-quick instructions. // Returns true on success, otherwise throws an exception and returns false. template @@ -250,32 +331,14 @@ static JValue GetFieldValue(const ShadowFrame& shadow_frame, uint32_t vreg) return field_value; } -template -bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction* inst, - uint16_t inst_data) { - bool do_assignability_check = do_access_check; - bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite); - uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); - ArtField* f = - FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self, - Primitive::ComponentSize(field_type)); - if (UNLIKELY(f == nullptr)) { - CHECK(self->IsExceptionPending()); - return false; - } - ObjPtr obj; - if (is_static) { - obj = f->GetDeclaringClass(); - } else { - obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); - if (UNLIKELY(obj == nullptr)) { - ThrowNullPointerExceptionForFieldAccess(f, false); - return false; - } - } +template +static inline bool DoFieldPutCommon(Thread* self, + const ShadowFrame& shadow_frame, + ObjPtr& obj, + ArtField* f, + size_t vregA) REQUIRES_SHARED(Locks::mutator_lock_) { f->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self); - uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data); + // Report this field access to instrumentation if needed. Since we only have the offset of // the field from the base of the object, we need to look for it first. instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); @@ -291,6 +354,7 @@ bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction f, field_value); } + switch (field_type) { case Primitive::kPrimBoolean: f->SetBoolean(obj, shadow_frame.GetVReg(vregA)); @@ -343,6 +407,39 @@ bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction return true; } +template +bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction* inst, + uint16_t inst_data) { + const bool do_assignability_check = do_access_check; + bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite); + uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); + ArtField* f = + FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self, + Primitive::ComponentSize(field_type)); + if (UNLIKELY(f == nullptr)) { + CHECK(self->IsExceptionPending()); + return false; + } + ObjPtr obj; + if (is_static) { + obj = f->GetDeclaringClass(); + } else { + obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); + if (UNLIKELY(obj == nullptr)) { + ThrowNullPointerExceptionForFieldAccess(f, false); + return false; + } + } + + uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data); + return DoFieldPutCommon(self, + shadow_frame, + obj, + f, + vregA); +} + // Explicitly instantiate all DoFieldPut functions. #define EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, _do_check, _transaction_active) \ template bool DoFieldPut<_find_type, _field_type, _do_check, _transaction_active>(Thread* self, \ @@ -375,6 +472,49 @@ EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticObjectWrite, Primitive::kPrimNot) #undef EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL #undef EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL +// Helper for setters in invoke-polymorphic. +bool DoFieldPutForInvokePolymorphic(Thread* self, + ShadowFrame& shadow_frame, + ObjPtr& obj, + ArtField* field, + Primitive::Type field_type, + size_t vregA) REQUIRES_SHARED(Locks::mutator_lock_) { + static const bool kDoCheckAssignability = false; + static const bool kTransaction = false; + switch (field_type) { + case Primitive::kPrimBoolean: + return DoFieldPutCommon( + self, shadow_frame, obj, field, vregA); + case Primitive::kPrimByte: + return DoFieldPutCommon( + self, shadow_frame, obj, field, vregA); + case Primitive::kPrimChar: + return DoFieldPutCommon( + self, shadow_frame, obj, field, vregA); + case Primitive::kPrimShort: + return DoFieldPutCommon( + self, shadow_frame, obj, field, vregA); + case Primitive::kPrimInt: + return DoFieldPutCommon( + self, shadow_frame, obj, field, vregA); + case Primitive::kPrimLong: + return DoFieldPutCommon( + self, shadow_frame, obj, field, vregA); + case Primitive::kPrimFloat: + return DoFieldPutCommon( + self, shadow_frame, obj, field, vregA); + case Primitive::kPrimDouble: + return DoFieldPutCommon( + self, shadow_frame, obj, field, vregA); + case Primitive::kPrimNot: + return DoFieldPutCommon( + self, shadow_frame, obj, field, vregA); + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable: " << field_type; + UNREACHABLE(); + } +} + template bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { ObjPtr obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); @@ -620,6 +760,17 @@ void SetStringInitValueToAllAliases(ShadowFrame* shadow_frame, } } +inline static bool IsInvokeExact(const DexFile& dex_file, int invoke_method_idx) { + // This check uses string comparison as it needs less code and data + // to do than fetching the associated ArtMethod from the DexCache + // and checking against ArtMethods in the well known classes. The + // verifier needs to perform a more rigorous check. + const char* method_name = dex_file.GetMethodName(dex_file.GetMethodId(invoke_method_idx)); + bool is_invoke_exact = (0 == strcmp(method_name, "invokeExact")); + DCHECK(is_invoke_exact || (0 == strcmp(method_name, "invoke"))); + return is_invoke_exact; +} + template inline bool DoInvokePolymorphic(Thread* self, ShadowFrame& shadow_frame, @@ -628,8 +779,14 @@ inline bool DoInvokePolymorphic(Thread* self, 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(); + + // Determine if this invocation is MethodHandle.invoke() or + // MethodHandle.invokeExact(). + bool is_invoke_exact = IsInvokeExact(shadow_frame.GetMethod()->GetDeclaringClass()->GetDexFile(), + invoke_method_idx); - // The method_idx here is the name of the signature polymorphic method that + // The invoke_method_idx here is the name of the signature polymorphic method that // was symbolically invoked in bytecode (say MethodHandle.invoke or MethodHandle.invokeExact) // and not the method that we'll dispatch to in the end. // @@ -642,11 +799,9 @@ inline bool DoInvokePolymorphic(Thread* self, ObjPtr::DownCast( MakeObjPtr(shadow_frame.GetVRegReference(vRegC))))); if (UNLIKELY(method_handle.Get() == nullptr)) { - const int method_idx = (is_range) ? inst->VRegB_4rcc() : inst->VRegB_45cc(); // Note that the invoke type is kVirtual here because a call to a signature // polymorphic method is shaped like a virtual call at the bytecode level. - ThrowNullPointerExceptionForMethodAccess(method_idx, InvokeType::kVirtual); - + ThrowNullPointerExceptionForMethodAccess(invoke_method_idx, InvokeType::kVirtual); result->SetJ(0); return false; } @@ -672,15 +827,13 @@ inline bool DoInvokePolymorphic(Thread* self, return false; } - // Get the method we're actually invoking along with the kind of - // invoke that is desired. We don't need to perform access checks at this - // point because they would have been performed on our behalf at the point - // of creation of the method handle. - ArtMethod* called_method = method_handle->GetTargetMethod(); const MethodHandleKind handle_kind = method_handle->GetHandleKind(); Handle handle_type(hs.NewHandle(method_handle->GetMethodType())); - CHECK(called_method != nullptr); CHECK(handle_type.Get() != nullptr); + if (UNLIKELY(is_invoke_exact && !callsite_type->IsExactMatch(handle_type.Get()))) { + ThrowWrongMethodTypeException(callsite_type.Get(), handle_type.Get()); + return false; + } uint32_t arg[Instruction::kMaxVarArgRegs] = {}; uint32_t receiver_vregC = 0; @@ -697,6 +850,13 @@ inline bool DoInvokePolymorphic(Thread* self, } if (IsInvoke(handle_kind)) { + // Get the method we're actually invoking along with the kind of + // invoke that is desired. We don't need to perform access checks at this + // point because they would have been performed on our behalf at the point + // of creation of the method handle. + ArtMethod* called_method = method_handle->GetTargetMethod(); + CHECK(called_method != nullptr); + if (handle_kind == kInvokeVirtual || handle_kind == kInvokeInterface) { ObjPtr receiver = shadow_frame.GetVRegReference(receiver_vregC); ObjPtr declaring_class = called_method->GetDeclaringClass(); @@ -761,9 +921,38 @@ inline bool DoInvokePolymorphic(Thread* self, receiver_vregC); } } else { - // TODO(narayan): Implement field getters and setters. - UNIMPLEMENTED(FATAL) << "Field references in method handles are not implemented yet."; - return false; + DCHECK(!is_range); + ArtField* field = method_handle->GetTargetField(); + Primitive::Type field_type = field->GetTypeAsPrimitiveType();; + + if (!is_invoke_exact) { + // TODO(oth): conversion plumbing for invoke(). + UNIMPLEMENTED(FATAL); + } + + switch (handle_kind) { + case kInstanceGet: { + ObjPtr obj = shadow_frame.GetVRegReference(receiver_vregC); + DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result); + return true; + } + case kInstancePut: { + ObjPtr obj = shadow_frame.GetVRegReference(receiver_vregC); + return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, arg[1]); + } + case kStaticGet: { + ObjPtr obj = field->GetDeclaringClass(); + DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result); + return true; + } + case kStaticPut: { + ObjPtr obj = field->GetDeclaringClass(); + return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, arg[0]); + } + default: + LOG(FATAL) << "Unreachable: " << handle_kind; + UNREACHABLE(); + } } } diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 243ed57c4..435ac62a9 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -1571,7 +1571,6 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, self, shadow_frame, inst, inst_data, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx); break; - break; } case Instruction::NEG_INT: PREAMBLE(); diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h index 40716ad7c..7bf9c5b24 100644 --- a/runtime/mirror/method_handle_impl.h +++ b/runtime/mirror/method_handle_impl.h @@ -36,6 +36,11 @@ class MANAGED MethodHandle : public Object { return GetFieldObject(OFFSET_OF_OBJECT_MEMBER(MethodHandle, method_type_)); } + ArtField* GetTargetField() REQUIRES_SHARED(Locks::mutator_lock_) { + return reinterpret_cast( + GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_))); + } + ArtMethod* GetTargetMethod() REQUIRES_SHARED(Locks::mutator_lock_) { return reinterpret_cast( GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_))); diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 329aae96e..620694822 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -446,6 +446,12 @@ static jobject Field_getAnnotationNative(JNIEnv* env, jobject javaField, jclass return soa.AddLocalReference(annotations::GetAnnotationForField(field, klass)); } +static jlong Field_getArtField(JNIEnv* env, jobject javaField) { + ScopedFastNativeObjectAccess soa(env); + ArtField* field = soa.Decode(javaField)->GetArtField(); + return reinterpret_cast(field); +} + static jobjectArray Field_getDeclaredAnnotations(JNIEnv* env, jobject javaField) { ScopedFastNativeObjectAccess soa(env); ArtField* field = soa.Decode(javaField)->GetArtField(); @@ -489,6 +495,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(Field, getChar, "!(Ljava/lang/Object;)C"), NATIVE_METHOD(Field, getAnnotationNative, "!(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"), + NATIVE_METHOD(Field, getArtField, "!()J"), NATIVE_METHOD(Field, getDeclaredAnnotations, "!()[Ljava/lang/annotation/Annotation;"), NATIVE_METHOD(Field, getSignatureAnnotation, "!()[Ljava/lang/String;"), NATIVE_METHOD(Field, getDouble, "!(Ljava/lang/Object;)D"), diff --git a/test/979-invoke-polymorphic-accessors/build b/test/979-invoke-polymorphic-accessors/build new file mode 100644 index 000000000..a423ca6b4 --- /dev/null +++ b/test/979-invoke-polymorphic-accessors/build @@ -0,0 +1,25 @@ +#!/bin/bash +# +# Copyright 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. + +# make us exit on a failure +set -e + +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm. + export USE_JACK=true +fi + +./default-build "$@" --experimental method-handles diff --git a/test/979-invoke-polymorphic-accessors/expected.txt b/test/979-invoke-polymorphic-accessors/expected.txt new file mode 100644 index 000000000..2987b6c5c --- /dev/null +++ b/test/979-invoke-polymorphic-accessors/expected.txt @@ -0,0 +1 @@ +Passed InvokeExact tests for accessors. diff --git a/test/979-invoke-polymorphic-accessors/info.txt b/test/979-invoke-polymorphic-accessors/info.txt new file mode 100644 index 000000000..b2f55f017 --- /dev/null +++ b/test/979-invoke-polymorphic-accessors/info.txt @@ -0,0 +1 @@ +This test requires Jack with invoke-polymorphic support. diff --git a/test/979-invoke-polymorphic-accessors/run b/test/979-invoke-polymorphic-accessors/run new file mode 100644 index 000000000..a9f182288 --- /dev/null +++ b/test/979-invoke-polymorphic-accessors/run @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright 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. + +# make us exit on a failure +set -e + +./default-run "$@" --experimental method-handles diff --git a/test/979-invoke-polymorphic-accessors/src/Main.java b/test/979-invoke-polymorphic-accessors/src/Main.java new file mode 100644 index 000000000..6cdcd1006 --- /dev/null +++ b/test/979-invoke-polymorphic-accessors/src/Main.java @@ -0,0 +1,727 @@ +/* + * 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. + */ +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.WrongMethodTypeException; + +public class Main { + + public static class ValueHolder { + public boolean m_z = false; + public byte m_b = 0; + public char m_c = 'a'; + public short m_s = 0; + public int m_i = 0; + public float m_f = 0.0f; + public double m_d = 0.0; + public long m_j = 0; + public String m_l = "a"; + + public static boolean s_z; + public static byte s_b; + public static char s_c; + public static short s_s; + public static int s_i; + public static float s_f; + public static double s_d; + public static long s_j; + public static String s_l; + + public final int m_fi = 0xa5a5a5a5; + public static final int s_fi = 0x5a5a5a5a; + } + + public static class InvokeExactTester { + private enum PrimitiveType { + Boolean, + Byte, + Char, + Short, + Int, + Long, + Float, + Double, + String, + } + + private enum AccessorType { + IPUT, + SPUT, + IGET, + 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; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void setByte(MethodHandle m, byte value, boolean expectFailure) throws Throwable { + setByte(m, null, value, expectFailure); + } + + static void getByte(MethodHandle m, ValueHolder v, byte value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final byte got; + if (v == null) { + got = (byte)m.invokeExact(); + } else { + got = (byte)m.invokeExact(v); + } + assertTrue(got == value); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void getByte(MethodHandle m, byte value, boolean expectFailure) throws Throwable { + getByte(m, null, value, expectFailure); + } + + static void setChar(MethodHandle m, ValueHolder v, char value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void setChar(MethodHandle m, char value, boolean expectFailure) throws Throwable { + setChar(m, null, value, expectFailure); + } + + static void getChar(MethodHandle m, ValueHolder v, char value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final char got; + if (v == null) { + got = (char)m.invokeExact(); + } else { + got = (char)m.invokeExact(v); + } + assertTrue(got == value); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void getChar(MethodHandle m, char value, boolean expectFailure) throws Throwable { + getChar(m, null, value, expectFailure); + } + + static void setShort(MethodHandle m, ValueHolder v, short value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void setShort(MethodHandle m, short value, boolean expectFailure) throws Throwable { + setShort(m, null, value, expectFailure); + } + + static void getShort(MethodHandle m, ValueHolder v, short value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final short got = (v == null) ? (short)m.invokeExact() : (short)m.invokeExact(v); + assertTrue(got == value); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void getShort(MethodHandle m, short value, boolean expectFailure) throws Throwable { + getShort(m, null, value, expectFailure); + } + + static void setInt(MethodHandle m, ValueHolder v, int value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void setInt(MethodHandle m, int value, boolean expectFailure) throws Throwable { + setInt(m, null, value, expectFailure); + } + + static void getInt(MethodHandle m, ValueHolder v, int value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final int got = (v == null) ? (int)m.invokeExact() : (int)m.invokeExact(v); + assertTrue(got == value); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void getInt(MethodHandle m, int value, boolean expectFailure) throws Throwable { + getInt(m, null, value, expectFailure); + } + + static void setLong(MethodHandle m, ValueHolder v, long value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void setLong(MethodHandle m, long value, boolean expectFailure) throws Throwable { + setLong(m, null, value, expectFailure); + } + + static void getLong(MethodHandle m, ValueHolder v, long value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final long got = (v == null) ? (long)m.invokeExact() : (long)m.invokeExact(v); + assertTrue(got == value); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void getLong(MethodHandle m, long value, boolean expectFailure) throws Throwable { + getLong(m, null, value, expectFailure); + } + + static void setFloat(MethodHandle m, ValueHolder v, float value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void setFloat(MethodHandle m, float value, boolean expectFailure) throws Throwable { + setFloat(m, null, value, expectFailure); + } + + static void getFloat(MethodHandle m, ValueHolder v, float value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final float got = (v == null) ? (float)m.invokeExact() : (float)m.invokeExact(v); + assertTrue(got == value); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void getFloat(MethodHandle m, float value, boolean expectFailure) throws Throwable { + getFloat(m, null, value, expectFailure); + } + + static void setDouble(MethodHandle m, ValueHolder v, double value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void setDouble(MethodHandle m, double value, boolean expectFailure) + throws Throwable { + setDouble(m, null, value, expectFailure); + } + + static void getDouble(MethodHandle m, ValueHolder v, double value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final double got = (v == null) ? (double)m.invokeExact() : (double)m.invokeExact(v); + assertTrue(got == value); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void getDouble(MethodHandle m, double value, boolean expectFailure) + throws Throwable { + getDouble(m, null, value, expectFailure); + } + + static void setString(MethodHandle m, ValueHolder v, String value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void setString(MethodHandle m, String value, boolean expectFailure) + throws Throwable { + setString(m, null, value, expectFailure); + } + + static void getString(MethodHandle m, ValueHolder v, String value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final String got = (v == null) ? (String)m.invokeExact() : (String)m.invokeExact(v); + assertTrue(got.equals(value)); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void getString(MethodHandle m, String value, boolean expectFailure) + throws Throwable { + getString(m, null, value, expectFailure); + } + + static void setBoolean(MethodHandle m, ValueHolder v, boolean value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + if (v == null) { + m.invokeExact(value); + } + else { + m.invokeExact(v, value); + } + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void setBoolean(MethodHandle m, boolean value, boolean expectFailure) + throws Throwable { + setBoolean(m, null, value, expectFailure); + } + + static void getBoolean(MethodHandle m, ValueHolder v, boolean value, boolean expectFailure) + throws Throwable { + boolean exceptionThrown = false; + try { + final boolean got = + (v == null) ? (boolean)m.invokeExact() : (boolean)m.invokeExact(v); + assertTrue(got == value); + } + catch (WrongMethodTypeException e) { + exceptionThrown = true; + } + assertActualAndExpectedMatch(exceptionThrown, expectFailure); + } + + static void getBoolean(MethodHandle m, boolean value, boolean expectFailure) + throws Throwable { + getBoolean(m, null, value, expectFailure); + } + + static boolean resultFor(PrimitiveType actualType, PrimitiveType expectedType, + AccessorType actualAccessor, + AccessorType expectedAccessor) { + return (actualType != expectedType) || (actualAccessor != expectedAccessor); + } + + static void tryAccessor(MethodHandle methodHandle, + ValueHolder valueHolder, + PrimitiveType primitive, + Object value, + AccessorType accessor) throws Throwable { + boolean booleanValue = + value instanceof Boolean ? ((Boolean)value).booleanValue() : false; + setBoolean(methodHandle, valueHolder, booleanValue, + resultFor(primitive, PrimitiveType.Boolean, accessor, AccessorType.IPUT)); + setBoolean(methodHandle, booleanValue, + resultFor(primitive, PrimitiveType.Boolean, accessor, AccessorType.SPUT)); + getBoolean(methodHandle, valueHolder, booleanValue, + resultFor(primitive, PrimitiveType.Boolean, accessor, AccessorType.IGET)); + getBoolean(methodHandle, booleanValue, + resultFor(primitive, PrimitiveType.Boolean, accessor, AccessorType.SGET)); + + byte byteValue = value instanceof Byte ? ((Byte)value).byteValue() : (byte)0; + setByte(methodHandle, valueHolder, byteValue, + resultFor(primitive, PrimitiveType.Byte, accessor, AccessorType.IPUT)); + setByte(methodHandle, byteValue, + resultFor(primitive, PrimitiveType.Byte, accessor, AccessorType.SPUT)); + getByte(methodHandle, valueHolder, byteValue, + resultFor(primitive, PrimitiveType.Byte, accessor, AccessorType.IGET)); + getByte(methodHandle, byteValue, + resultFor(primitive, PrimitiveType.Byte, accessor, AccessorType.SGET)); + + char charValue = value instanceof Character ? ((Character)value).charValue() : 'z'; + setChar(methodHandle, valueHolder, charValue, + resultFor(primitive, PrimitiveType.Char, accessor, AccessorType.IPUT)); + setChar(methodHandle, charValue, + resultFor(primitive, PrimitiveType.Char, accessor, AccessorType.SPUT)); + getChar(methodHandle, valueHolder, charValue, + resultFor(primitive, PrimitiveType.Char, accessor, AccessorType.IGET)); + getChar(methodHandle, charValue, + resultFor(primitive, PrimitiveType.Char, accessor, AccessorType.SGET)); + + short shortValue = value instanceof Short ? ((Short)value).shortValue() : (short)0; + setShort(methodHandle, valueHolder, shortValue, + resultFor(primitive, PrimitiveType.Short, accessor, AccessorType.IPUT)); + setShort(methodHandle, shortValue, + resultFor(primitive, PrimitiveType.Short, accessor, AccessorType.SPUT)); + getShort(methodHandle, valueHolder, shortValue, + resultFor(primitive, PrimitiveType.Short, accessor, AccessorType.IGET)); + getShort(methodHandle, shortValue, + resultFor(primitive, PrimitiveType.Short, accessor, AccessorType.SGET)); + + int intValue = value instanceof Integer ? ((Integer)value).intValue() : -1; + setInt(methodHandle, valueHolder, intValue, + resultFor(primitive, PrimitiveType.Int, accessor, AccessorType.IPUT)); + setInt(methodHandle, intValue, + resultFor(primitive, PrimitiveType.Int, accessor, AccessorType.SPUT)); + getInt(methodHandle, valueHolder, intValue, + resultFor(primitive, PrimitiveType.Int, accessor, AccessorType.IGET)); + getInt(methodHandle, intValue, + resultFor(primitive, PrimitiveType.Int, accessor, AccessorType.SGET)); + + long longValue = value instanceof Long ? ((Long)value).longValue() : (long)-1; + setLong(methodHandle, valueHolder, longValue, + resultFor(primitive, PrimitiveType.Long, accessor, AccessorType.IPUT)); + setLong(methodHandle, longValue, + resultFor(primitive, PrimitiveType.Long, accessor, AccessorType.SPUT)); + getLong(methodHandle, valueHolder, longValue, + resultFor(primitive, PrimitiveType.Long, accessor, AccessorType.IGET)); + getLong(methodHandle, longValue, + resultFor(primitive, PrimitiveType.Long, accessor, AccessorType.SGET)); + + float floatValue = value instanceof Float ? ((Float)value).floatValue() : -1.0f; + setFloat(methodHandle, valueHolder, floatValue, + resultFor(primitive, PrimitiveType.Float, accessor, AccessorType.IPUT)); + setFloat(methodHandle, floatValue, + resultFor(primitive, PrimitiveType.Float, accessor, AccessorType.SPUT)); + getFloat(methodHandle, valueHolder, floatValue, + resultFor(primitive, PrimitiveType.Float, accessor, AccessorType.IGET)); + getFloat(methodHandle, floatValue, + resultFor(primitive, PrimitiveType.Float, accessor, AccessorType.SGET)); + + double doubleValue = value instanceof Double ? ((Double)value).doubleValue() : -1.0; + setDouble(methodHandle, valueHolder, doubleValue, + resultFor(primitive, PrimitiveType.Double, accessor, AccessorType.IPUT)); + setDouble(methodHandle, doubleValue, + resultFor(primitive, PrimitiveType.Double, accessor, AccessorType.SPUT)); + getDouble(methodHandle, valueHolder, doubleValue, + resultFor(primitive, PrimitiveType.Double, accessor, AccessorType.IGET)); + getDouble(methodHandle, doubleValue, + resultFor(primitive, PrimitiveType.Double, accessor, AccessorType.SGET)); + + String stringValue = value instanceof String ? ((String) value) : "No Spock, no"; + setString(methodHandle, valueHolder, stringValue, + resultFor(primitive, PrimitiveType.String, accessor, AccessorType.IPUT)); + setString(methodHandle, stringValue, + resultFor(primitive, PrimitiveType.String, accessor, AccessorType.SPUT)); + getString(methodHandle, valueHolder, stringValue, + resultFor(primitive, PrimitiveType.String, accessor, AccessorType.IGET)); + getString(methodHandle, stringValue, + resultFor(primitive, PrimitiveType.String, accessor, AccessorType.SGET)); + } + + public static void main() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + boolean [] booleans = { false, true, false }; + for (boolean b : booleans) { + Boolean boxed = new Boolean(b); + tryAccessor(lookup.findSetter(ValueHolder.class, "m_z", boolean.class), + valueHolder, PrimitiveType.Boolean, boxed, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_z", boolean.class), + valueHolder, PrimitiveType.Boolean, boxed, AccessorType.IGET); + assertTrue(valueHolder.m_z == b); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_z", boolean.class), + valueHolder, PrimitiveType.Boolean, boxed, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_z", boolean.class), + valueHolder, PrimitiveType.Boolean, boxed, AccessorType.SGET); + assertTrue(ValueHolder.s_z == b); + } + + byte [] bytes = { (byte)0x73, (byte)0xfe }; + for (byte b : bytes) { + Byte boxed = new Byte(b); + tryAccessor(lookup.findSetter(ValueHolder.class, "m_b", byte.class), + valueHolder, PrimitiveType.Byte, boxed, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_b", byte.class), + valueHolder, PrimitiveType.Byte, boxed, AccessorType.IGET); + assertTrue(valueHolder.m_b == b); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_b", byte.class), + valueHolder, PrimitiveType.Byte, boxed, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_b", byte.class), + valueHolder, PrimitiveType.Byte, boxed, AccessorType.SGET); + assertTrue(ValueHolder.s_b == b); + } + + char [] chars = { 'a', 'b', 'c' }; + for (char c : chars) { + Character boxed = new Character(c); + tryAccessor(lookup.findSetter(ValueHolder.class, "m_c", char.class), + valueHolder, PrimitiveType.Char, boxed, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_c", char.class), + valueHolder, PrimitiveType.Char, boxed, AccessorType.IGET); + assertTrue(valueHolder.m_c == c); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_c", char.class), + valueHolder, PrimitiveType.Char, boxed, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_c", char.class), + valueHolder, PrimitiveType.Char, boxed, AccessorType.SGET); + assertTrue(ValueHolder.s_c == c); + } + + short [] shorts = { (short)0x1234, (short)0x4321 }; + for (short s : shorts) { + Short boxed = new Short(s); + tryAccessor(lookup.findSetter(ValueHolder.class, "m_s", short.class), + valueHolder, PrimitiveType.Short, boxed, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_s", short.class), + valueHolder, PrimitiveType.Short, boxed, AccessorType.IGET); + assertTrue(valueHolder.m_s == s); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_s", short.class), + valueHolder, PrimitiveType.Short, boxed, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_s", short.class), + valueHolder, PrimitiveType.Short, boxed, AccessorType.SGET); + assertTrue(ValueHolder.s_s == s); + } + + int [] ints = { -100000000, 10000000 }; + for (int i : ints) { + Integer boxed = new Integer(i); + tryAccessor(lookup.findSetter(ValueHolder.class, "m_i", int.class), + valueHolder, PrimitiveType.Int, boxed, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_i", int.class), + valueHolder, PrimitiveType.Int, boxed, AccessorType.IGET); + assertTrue(valueHolder.m_i == i); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_i", int.class), + valueHolder, PrimitiveType.Int, boxed, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_i", int.class), + valueHolder, PrimitiveType.Int, boxed, AccessorType.SGET); + assertTrue(ValueHolder.s_i == i); + } + + float [] floats = { 0.99f, -1.23e-17f }; + for (float f : floats) { + Float boxed = new Float(f); + tryAccessor(lookup.findSetter(ValueHolder.class, "m_f", float.class), + valueHolder, PrimitiveType.Float, boxed, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_f", float.class), + valueHolder, PrimitiveType.Float, boxed, AccessorType.IGET); + assertTrue(valueHolder.m_f == f); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_f", float.class), + valueHolder, PrimitiveType.Float, boxed, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_f", float.class), + valueHolder, PrimitiveType.Float, boxed, AccessorType.SGET); + assertTrue(ValueHolder.s_f == f); + } + + double [] doubles = { 0.44444444444e37, -0.555555555e-37 }; + for (double d : doubles) { + Double boxed = new Double(d); + tryAccessor(lookup.findSetter(ValueHolder.class, "m_d", double.class), + valueHolder, PrimitiveType.Double, boxed, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_d", double.class), + valueHolder, PrimitiveType.Double, boxed, AccessorType.IGET); + assertTrue(valueHolder.m_d == d); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_d", double.class), + valueHolder, PrimitiveType.Double, boxed, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_d", double.class), + valueHolder, PrimitiveType.Double, boxed, AccessorType.SGET); + assertTrue(ValueHolder.s_d == d); + } + + long [] longs = { 0x0123456789abcdefl, 0xfedcba9876543210l }; + for (long j : longs) { + Long boxed = new Long(j); + tryAccessor(lookup.findSetter(ValueHolder.class, "m_j", long.class), + valueHolder, PrimitiveType.Long, boxed, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_j", long.class), + valueHolder, PrimitiveType.Long, boxed, AccessorType.IGET); + assertTrue(valueHolder.m_j == j); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_j", long.class), + valueHolder, PrimitiveType.Long, boxed, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_j", long.class), + valueHolder, PrimitiveType.Long, boxed, AccessorType.SGET); + assertTrue(ValueHolder.s_j == j); + } + + String [] strings = { "octopus", "crab" }; + for (String s : strings) { + tryAccessor(lookup.findSetter(ValueHolder.class, "m_l", String.class), + valueHolder, PrimitiveType.String, s, AccessorType.IPUT); + tryAccessor(lookup.findGetter(ValueHolder.class, "m_l", String.class), + valueHolder, PrimitiveType.String, s, AccessorType.IGET); + assertTrue(s.equals(valueHolder.m_l)); + tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_l", String.class), + valueHolder, PrimitiveType.String, s, AccessorType.SPUT); + tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_l", String.class), + valueHolder, PrimitiveType.String, s, AccessorType.SGET); + assertTrue(s.equals(ValueHolder.s_l)); + } + + System.out.println("Passed InvokeExact tests for accessors."); + } + } + + public static class FindAccessorTester { + public static void main() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + lookup.findStaticGetter(ValueHolder.class, "s_fi", int.class); + try { + lookup.findStaticGetter(ValueHolder.class, "s_fi", byte.class); + unreachable(); + } catch (NoSuchFieldException e) {} + try { + lookup.findGetter(ValueHolder.class, "s_fi", byte.class); + unreachable(); + } catch (NoSuchFieldException e) {} + try { + lookup.findStaticSetter(ValueHolder.class, "s_fi", int.class); + unreachable(); + } catch (IllegalAccessException e) {} + + lookup.findGetter(ValueHolder.class, "m_fi", int.class); + try { + lookup.findGetter(ValueHolder.class, "m_fi", byte.class); + unreachable(); + } catch (NoSuchFieldException e) {} + try { + lookup.findStaticGetter(ValueHolder.class, "m_fi", byte.class); + unreachable(); + } catch (NoSuchFieldException e) {} + try { + lookup.findSetter(ValueHolder.class, "m_fi", int.class); + unreachable(); + } catch (IllegalAccessException e) {} + } + + public static void unreachable() throws Throwable{ + throw new Error("unreachable"); + } + } + + public static void main(String[] args) throws Throwable { + FindAccessorTester.main(); + InvokeExactTester.main(); + } +} -- 2.11.0