diff options
-rw-r--r-- | runtime/Android.bp | 1 | ||||
-rw-r--r-- | runtime/class_linker_test.cc | 22 | ||||
-rw-r--r-- | runtime/mirror/method_handle_impl.h | 8 | ||||
-rw-r--r-- | runtime/native/java_lang_invoke_MethodHandleImpl.cc | 76 | ||||
-rw-r--r-- | runtime/native/java_lang_invoke_MethodHandleImpl.h | 28 | ||||
-rw-r--r-- | runtime/runtime.cc | 2 | ||||
-rw-r--r-- | test/956-methodhandles/src/Main.java | 119 |
7 files changed, 248 insertions, 8 deletions
diff --git a/runtime/Android.bp b/runtime/Android.bp index 7f985139bb..196c65e11a 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -154,6 +154,7 @@ cc_defaults { "native/java_lang_Thread.cc", "native/java_lang_Throwable.cc", "native/java_lang_VMClassLoader.cc", + "native/java_lang_invoke_MethodHandleImpl.cc", "native/java_lang_ref_FinalizerReference.cc", "native/java_lang_ref_Reference.cc", "native/java_lang_reflect_Array.cc", diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index e806e7d608..17510bb598 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -743,15 +743,22 @@ struct MethodTypeOffsets : public CheckOffsets<mirror::MethodType> { } }; -struct MethodHandleImplOffsets : public CheckOffsets<mirror::MethodHandleImpl> { - MethodHandleImplOffsets() : CheckOffsets<mirror::MethodHandleImpl>( +struct MethodHandleOffsets : public CheckOffsets<mirror::MethodHandle> { + MethodHandleOffsets() : CheckOffsets<mirror::MethodHandle>( false, "Ljava/lang/invoke/MethodHandle;") { - addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, art_field_or_method_), "artFieldOrMethod"); - addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, cached_spread_invoker_), + addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, art_field_or_method_), "artFieldOrMethod"); + addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, cached_spread_invoker_), "cachedSpreadInvoker"); - addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, handle_kind_), "handleKind"); - addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, nominal_type_), "nominalType"); - addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, method_type_), "type"); + addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, handle_kind_), "handleKind"); + addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, nominal_type_), "nominalType"); + addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, method_type_), "type"); + } +}; + +struct MethodHandleImplOffsets : public CheckOffsets<mirror::MethodHandleImpl> { + MethodHandleImplOffsets() : CheckOffsets<mirror::MethodHandleImpl>( + false, "Ljava/lang/invoke/MethodHandleImpl;") { + addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, info_), "info"); } }; @@ -785,6 +792,7 @@ TEST_F(ClassLinkerTest, ValidateFieldOrderOfJavaCppUnionClasses) { EXPECT_TRUE(FieldOffsets().Check()); EXPECT_TRUE(ExecutableOffsets().Check()); EXPECT_TRUE(MethodTypeOffsets().Check()); + EXPECT_TRUE(MethodHandleOffsets().Check()); EXPECT_TRUE(MethodHandleImplOffsets().Check()); EXPECT_TRUE(EmulatedStackFrameOffsets().Check()); } diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h index 2f26a22ca8..dca30626e0 100644 --- a/runtime/mirror/method_handle_impl.h +++ b/runtime/mirror/method_handle_impl.h @@ -25,6 +25,7 @@ namespace art { +struct MethodHandleOffsets; struct MethodHandleImplOffsets; namespace mirror { @@ -105,7 +106,7 @@ class MANAGED MethodHandle : public Object { return MemberOffset(OFFSETOF_MEMBER(MethodHandle, handle_kind_)); } - friend struct art::MethodHandleImplOffsets; // for verifying offset information + friend struct art::MethodHandleOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(MethodHandle); }; @@ -121,6 +122,11 @@ class MANAGED MethodHandleImpl : public MethodHandle { static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); private: + static MemberOffset InfoOffset() { + return MemberOffset(OFFSETOF_MEMBER(MethodHandleImpl, info_)); + } + + HeapReference<mirror::Object> info_; // Unused by the runtime. static GcRoot<mirror::Class> static_class_; // java.lang.invoke.MethodHandleImpl.class friend struct art::MethodHandleImplOffsets; // for verifying offset information diff --git a/runtime/native/java_lang_invoke_MethodHandleImpl.cc b/runtime/native/java_lang_invoke_MethodHandleImpl.cc new file mode 100644 index 0000000000..72a37f875d --- /dev/null +++ b/runtime/native/java_lang_invoke_MethodHandleImpl.cc @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2008 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 "java_lang_invoke_MethodHandleImpl.h" + +#include "art_method.h" +#include "handle_scope-inl.h" +#include "jni_internal.h" +#include "mirror/field.h" +#include "mirror/method.h" +#include "mirror/method_handle_impl.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" + +namespace art { + +static jobject MethodHandleImpl_getMemberInternal(JNIEnv* env, jobject thiz) { + ScopedObjectAccess soa(env); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::MethodHandleImpl> handle = hs.NewHandle( + soa.Decode<mirror::MethodHandleImpl>(thiz)); + + // Check the handle kind, we need to materialize a Field for field accessors, + // a Method for method invokers and a Constructor for constructors. + const mirror::MethodHandle::Kind handle_kind = handle->GetHandleKind(); + + // We check this here because we pass false to CreateFromArtField and + // CreateFromArtMethod. + DCHECK(!Runtime::Current()->IsActiveTransaction()); + + MutableHandle<mirror::Object> h_object(hs.NewHandle<mirror::Object>(nullptr)); + if (handle_kind >= mirror::MethodHandle::kFirstAccessorKind) { + ArtField* const field = handle->GetTargetField(); + h_object.Assign(mirror::Field::CreateFromArtField<kRuntimePointerSize, false>( + soa.Self(), field, false /* force_resolve */)); + } else { + ArtMethod* const method = handle->GetTargetMethod(); + if (method->IsConstructor()) { + h_object.Assign(mirror::Constructor::CreateFromArtMethod<kRuntimePointerSize, false>( + soa.Self(), method)); + } else { + h_object.Assign(mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>( + soa.Self(), method)); + } + } + + if (UNLIKELY(h_object.Get() == nullptr)) { + soa.Self()->AssertPendingOOMException(); + return nullptr; + } + + return soa.AddLocalReference<jobject>(h_object.Get()); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(MethodHandleImpl, getMemberInternal, "()Ljava/lang/reflect/Member;"), +}; + +void register_java_lang_invoke_MethodHandleImpl(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/lang/invoke/MethodHandleImpl"); +} + +} // namespace art diff --git a/runtime/native/java_lang_invoke_MethodHandleImpl.h b/runtime/native/java_lang_invoke_MethodHandleImpl.h new file mode 100644 index 0000000000..0e50371697 --- /dev/null +++ b/runtime/native/java_lang_invoke_MethodHandleImpl.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017 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. + */ + +#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_INVOKE_METHODHANDLEIMPL_H_ +#define ART_RUNTIME_NATIVE_JAVA_LANG_INVOKE_METHODHANDLEIMPL_H_ + +#include <jni.h> + +namespace art { + +void register_java_lang_invoke_MethodHandleImpl(JNIEnv* env); + +} // namespace art + +#endif // ART_RUNTIME_NATIVE_JAVA_LANG_INVOKE_METHODHANDLEIMPL_H_ diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 5e008a8bb8..b30e5109b6 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -114,6 +114,7 @@ #include "native/java_lang_Thread.h" #include "native/java_lang_Throwable.h" #include "native/java_lang_VMClassLoader.h" +#include "native/java_lang_invoke_MethodHandleImpl.h" #include "native/java_lang_ref_FinalizerReference.h" #include "native/java_lang_ref_Reference.h" #include "native/java_lang_reflect_Array.h" @@ -1536,6 +1537,7 @@ void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) { register_java_lang_Class(env); register_java_lang_DexCache(env); register_java_lang_Object(env); + register_java_lang_invoke_MethodHandleImpl(env); register_java_lang_ref_FinalizerReference(env); register_java_lang_reflect_Array(env); register_java_lang_reflect_Constructor(env); diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java index f8daba6239..801904d9c9 100644 --- a/test/956-methodhandles/src/Main.java +++ b/test/956-methodhandles/src/Main.java @@ -15,6 +15,7 @@ */ import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandleInfo; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; @@ -77,6 +78,7 @@ public class Main { testReturnValueConversions(); testVariableArity(); testVariableArity_MethodHandles_bind(); + testRevealDirect(); } public static void testfindSpecial_invokeSuperBehaviour() throws Throwable { @@ -384,6 +386,10 @@ public class Main { public String publicMethod() { return "publicMethod"; } + + public String publicVarArgsMethod(String... args) { + return "publicVarArgsMethod"; + } } public static void testUnreflects() throws Throwable { @@ -1486,4 +1492,117 @@ public class Main { fail(); } catch (WrongMethodTypeException e) {} } + + public static void testRevealDirect() throws Throwable { + // Test with a virtual method : + MethodType type = MethodType.methodType(String.class); + MethodHandle handle = MethodHandles.lookup().findVirtual( + UnreflectTester.class, "publicMethod", type); + + // Comparisons with an equivalent member obtained via reflection : + MethodHandleInfo info = MethodHandles.lookup().revealDirect(handle); + Method meth = UnreflectTester.class.getMethod("publicMethod"); + + assertEquals(MethodHandleInfo.REF_invokeVirtual, info.getReferenceKind()); + assertEquals("publicMethod", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertFalse(info.isVarArgs()); + assertEquals(meth, info.reflectAs(Method.class, MethodHandles.lookup())); + assertEquals(type, info.getMethodType()); + + // Resolution via a public lookup should fail because the method in question + // isn't public. + try { + info.reflectAs(Method.class, MethodHandles.publicLookup()); + fail(); + } catch (IllegalArgumentException expected) { + } + + // Test with a static method : + handle = MethodHandles.lookup().findStatic(UnreflectTester.class, + "publicStaticMethod", + MethodType.methodType(String.class)); + + info = MethodHandles.lookup().revealDirect(handle); + meth = UnreflectTester.class.getMethod("publicStaticMethod"); + assertEquals(MethodHandleInfo.REF_invokeStatic, info.getReferenceKind()); + assertEquals("publicStaticMethod", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertFalse(info.isVarArgs()); + assertEquals(meth, info.reflectAs(Method.class, MethodHandles.lookup())); + assertEquals(type, info.getMethodType()); + + // Test with a var-args method : + type = MethodType.methodType(String.class, String[].class); + handle = MethodHandles.lookup().findVirtual(UnreflectTester.class, + "publicVarArgsMethod", type); + + info = MethodHandles.lookup().revealDirect(handle); + meth = UnreflectTester.class.getMethod("publicVarArgsMethod", String[].class); + assertEquals(MethodHandleInfo.REF_invokeVirtual, info.getReferenceKind()); + assertEquals("publicVarArgsMethod", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertTrue(info.isVarArgs()); + assertEquals(meth, info.reflectAs(Method.class, MethodHandles.lookup())); + assertEquals(type, info.getMethodType()); + + // Test with a constructor : + Constructor cons = UnreflectTester.class.getConstructor(String.class, boolean.class); + type = MethodType.methodType(void.class, String.class, boolean.class); + handle = MethodHandles.lookup().findConstructor(UnreflectTester.class, type); + + info = MethodHandles.lookup().revealDirect(handle); + assertEquals(MethodHandleInfo.REF_newInvokeSpecial, info.getReferenceKind()); + assertEquals("<init>", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertFalse(info.isVarArgs()); + assertEquals(cons, info.reflectAs(Constructor.class, MethodHandles.lookup())); + assertEquals(type, info.getMethodType()); + + // Test with a static field : + Field field = UnreflectTester.class.getField("publicStaticField"); + + handle = MethodHandles.lookup().findStaticSetter( + UnreflectTester.class, "publicStaticField", String.class); + + info = MethodHandles.lookup().revealDirect(handle); + assertEquals(MethodHandleInfo.REF_putStatic, info.getReferenceKind()); + assertEquals("publicStaticField", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertFalse(info.isVarArgs()); + assertEquals(field, info.reflectAs(Field.class, MethodHandles.lookup())); + assertEquals(MethodType.methodType(void.class, String.class), info.getMethodType()); + + // Test with a setter on the same field, the type of the handle should change + // but everything else must remain the same. + handle = MethodHandles.lookup().findStaticGetter( + UnreflectTester.class, "publicStaticField", String.class); + info = MethodHandles.lookup().revealDirect(handle); + assertEquals(MethodHandleInfo.REF_getStatic, info.getReferenceKind()); + assertEquals(field, info.reflectAs(Field.class, MethodHandles.lookup())); + assertEquals(MethodType.methodType(String.class), info.getMethodType()); + + // Test with an instance field : + field = UnreflectTester.class.getField("publicField"); + + handle = MethodHandles.lookup().findSetter( + UnreflectTester.class, "publicField", String.class); + + info = MethodHandles.lookup().revealDirect(handle); + assertEquals(MethodHandleInfo.REF_putField, info.getReferenceKind()); + assertEquals("publicField", info.getName()); + assertTrue(UnreflectTester.class == info.getDeclaringClass()); + assertFalse(info.isVarArgs()); + assertEquals(field, info.reflectAs(Field.class, MethodHandles.lookup())); + assertEquals(MethodType.methodType(void.class, String.class), info.getMethodType()); + + // Test with a setter on the same field, the type of the handle should change + // but everything else must remain the same. + handle = MethodHandles.lookup().findGetter( + UnreflectTester.class, "publicField", String.class); + info = MethodHandles.lookup().revealDirect(handle); + assertEquals(MethodHandleInfo.REF_getField, info.getReferenceKind()); + assertEquals(field, info.reflectAs(Field.class, MethodHandles.lookup())); + assertEquals(MethodType.methodType(String.class), info.getMethodType()); + } } |