MethodHandles: Implement MethodHandle.asType.
Tracks libcore change 16fa583fb5ee489.
Test: make test-art-host
Bug: 30550796
Change-Id: I2457b563f67a183c4eebf94ddbe74cc55f772ee0
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 7a3ebad..ab2d9d0 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -742,8 +742,8 @@
MethodHandleImplOffsets() : CheckOffsets<mirror::MethodHandleImpl>(
false, "Ljava/lang/invoke/MethodHandle;") {
addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, art_field_or_method_), "artFieldOrMethod");
- addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, as_type_cache_), "asTypeCache");
addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, handle_kind_), "handleKind");
+ addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, nominal_type_), "nominalType");
addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, method_type_), "type");
}
};
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 1a99100..e607d4d 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -833,9 +833,23 @@
const MethodHandleKind handle_kind = method_handle->GetHandleKind();
Handle<mirror::MethodType> handle_type(hs.NewHandle(method_handle->GetMethodType()));
CHECK(handle_type.Get() != nullptr);
- if (UNLIKELY(is_invoke_exact && !callsite_type->IsExactMatch(handle_type.Get()))) {
- ThrowWrongMethodTypeException(handle_type.Get(), callsite_type.Get());
- return false;
+ if (is_invoke_exact) {
+ // We need to check the nominal type of the handle in addition to the
+ // real type. The "nominal" type is present when MethodHandle.asType is
+ // called any handle, and results in the declared type of the handle
+ // changing.
+ ObjPtr<mirror::MethodType> nominal_type(method_handle->GetNominalType());
+ ObjPtr<mirror::MethodType> check_type(nullptr);
+ if (LIKELY(nominal_type.Ptr() == nullptr)) {
+ check_type.Assign(handle_type.Get());
+ } else {
+ check_type.Assign(nominal_type.Ptr());
+ }
+
+ if (UNLIKELY(!callsite_type->IsExactMatch(check_type.Ptr()))) {
+ ThrowWrongMethodTypeException(check_type.Ptr(), callsite_type.Get());
+ return false;
+ }
}
uint32_t arg[Instruction::kMaxVarArgRegs] = {};
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
index 7bf9c5b..9054216 100644
--- a/runtime/mirror/method_handle_impl.h
+++ b/runtime/mirror/method_handle_impl.h
@@ -36,6 +36,10 @@
return GetFieldObject<mirror::MethodType>(OFFSET_OF_OBJECT_MEMBER(MethodHandle, method_type_));
}
+ mirror::MethodType* GetNominalType() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldObject<mirror::MethodType>(OFFSET_OF_OBJECT_MEMBER(MethodHandle, nominal_type_));
+ }
+
ArtField* GetTargetField() REQUIRES_SHARED(Locks::mutator_lock_) {
return reinterpret_cast<ArtField*>(
GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_)));
@@ -54,14 +58,14 @@
}
private:
- HeapReference<mirror::Object> as_type_cache_;
+ HeapReference<mirror::MethodType> nominal_type_;
HeapReference<mirror::MethodType> method_type_;
uint64_t art_field_or_method_;
uint32_t handle_kind_;
private:
- static MemberOffset AsTypeCacheOffset() {
- return MemberOffset(OFFSETOF_MEMBER(MethodHandle, as_type_cache_));
+ static MemberOffset NominalTypeOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(MethodHandle, nominal_type_));
}
static MemberOffset MethodTypeOffset() {
return MemberOffset(OFFSETOF_MEMBER(MethodHandle, method_type_));
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index d824c60..780513f 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -64,6 +64,7 @@
testExceptionDetailMessages();
testfindVirtual();
testUnreflects();
+ testAsType();
}
public static void testfindSpecial_invokeSuperBehaviour() throws Throwable {
@@ -428,6 +429,54 @@
assertEquals("updatedStaticValue2", (String) privateStaticField.get(null));
}
+ // This method only exists to fool Jack's handling of types. See b/32536744.
+ public static CharSequence getSequence() {
+ return "foo";
+ }
+
+ public static void testAsType() throws Throwable {
+ // The type of this handle is (String, String)String.
+ MethodHandle mh = MethodHandles.lookup().findVirtual(String.class,
+ "concat", MethodType.methodType(String.class, String.class));
+
+ // Change it to (CharSequence, String)Object.
+ MethodHandle asType = mh.asType(
+ MethodType.methodType(Object.class, CharSequence.class, String.class));
+
+ Object obj = asType.invokeExact((CharSequence) getSequence(), "bar");
+ assertEquals("foobar", (String) obj);
+
+ // Should fail due to a wrong return type.
+ try {
+ String str = (String) asType.invokeExact((CharSequence) getSequence(), "bar");
+ fail();
+ } catch (WrongMethodTypeException expected) {
+ }
+
+ // Should fail due to a wrong argument type (String instead of Charsequence).
+ try {
+ String str = (String) asType.invokeExact("baz", "bar");
+ fail();
+ } catch (WrongMethodTypeException expected) {
+ }
+
+ // Calls to asType should fail if the types are not convertible.
+ //
+ // Bad return type conversion.
+ try {
+ mh.asType(MethodType.methodType(int.class, String.class, String.class));
+ fail();
+ } catch (WrongMethodTypeException expected) {
+ }
+
+ // Bad argument conversion.
+ try {
+ mh.asType(MethodType.methodType(String.class, int.class, String.class));
+ fail();
+ } catch (WrongMethodTypeException expected) {
+ }
+ }
+
public static void assertEquals(String s1, String s2) {
if (s1 == s2) {
return;
diff --git a/test/957-methodhandle-transforms/expected.txt b/test/957-methodhandle-transforms/expected.txt
index fc4fdd6..7540ef7 100644
--- a/test/957-methodhandle-transforms/expected.txt
+++ b/test/957-methodhandle-transforms/expected.txt
@@ -4,6 +4,7 @@
Message: foo, Message2: 42
Message: foo, Message2: 42
Message: foo, Message2: 42
+Message: foo, Message2: 42
Target: Arg1: foo, Arg2: 42
Target: Arg1: foo, Arg2: 42
Handler: java.lang.IllegalArgumentException: exceptionMessage, Arg1: foo, Arg2: 42, ExMsg: exceptionMessage
@@ -14,3 +15,4 @@
target: target, 42, 56
fallback: fallback, 42, 56
target: target, 42, 56
+target: target, 42, 56
diff --git a/test/957-methodhandle-transforms/src/Main.java b/test/957-methodhandle-transforms/src/Main.java
index 4a27086..e50391b 100644
--- a/test/957-methodhandle-transforms/src/Main.java
+++ b/test/957-methodhandle-transforms/src/Main.java
@@ -90,6 +90,12 @@
// it's IAE and should be WMTE instead.
}
+ // Check that asType works as expected.
+ transform = MethodHandles.dropArguments(delegate, 0, int.class, Object.class);
+ transform = transform.asType(MethodType.methodType(void.class,
+ new Class<?>[] { short.class, Object.class, String.class, long.class }));
+ transform.invokeExact((short) 45, new Object(), "foo", 42l);
+
// Invalid argument location, should not be allowed.
try {
MethodHandles.dropArguments(delegate, -1, int.class, Object.class);
@@ -183,6 +189,14 @@
assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal);
returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage2");
assertEquals("java.lang.IllegalArgumentException: exceptionMessage2", returnVal);
+
+ // Check that asType works as expected.
+ adapter = MethodHandles.catchException(target, IllegalArgumentException.class,
+ handler);
+ adapter = adapter.asType(MethodType.methodType(String.class,
+ new Class<?>[] { String.class, int.class, String.class }));
+ returnVal = (String) adapter.invokeExact("foo", 42, "exceptionMessage");
+ assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal);
}
public static boolean testGuardWithTest_test(String arg1, long arg2) {
@@ -225,6 +239,12 @@
assertEquals("fallback", returnVal);
returnVal = (String) adapter.invokeExact("target", 42l, 56);
assertEquals("target", returnVal);
+
+ // Check that asType works as expected.
+ adapter = adapter.asType(MethodType.methodType(String.class,
+ new Class<?>[] { String.class, int.class, int.class }));
+ returnVal = (String) adapter.invokeExact("target", 42, 56);
+ assertEquals("target", returnVal);
}
public static void fail() {