summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Narayan Kamath <narayan@google.com> 2017-01-25 10:46:54 +0000
committer Narayan Kamath <narayan@google.com> 2017-01-27 13:47:50 +0000
commitbd2fed5a813b68fa42fe941b07325ba4ff3190b0 (patch)
tree2e8ce7221f842133a1b7b8e6fc4793acbf4ed8f6
parentca21dc47adeed92a15a9d3fd090bdd0e6654679c (diff)
MethodHandles: Support and tests for revealDirect / reflectAs.
We need runtime support to materialize a Constructor, Field or Method object from a MethodHandle and its associated ArtField or ArtMethod pointer. An alternate approach might be to have all handles hold a reference to their associated Field or Method object (in addition to the raw ArtField or ArtMethod) but that seems unnecessary given that these APIs are not expected to be called often. Test: test-art-host Change-Id: I9450706b9c30359edebf2e8a7afbc8d89bd68b26
-rw-r--r--runtime/Android.bp1
-rw-r--r--runtime/class_linker_test.cc22
-rw-r--r--runtime/mirror/method_handle_impl.h8
-rw-r--r--runtime/native/java_lang_invoke_MethodHandleImpl.cc76
-rw-r--r--runtime/native/java_lang_invoke_MethodHandleImpl.h28
-rw-r--r--runtime/runtime.cc2
-rw-r--r--test/956-methodhandles/src/Main.java119
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());
+ }
}