diff options
-rw-r--r-- | runtime/class_linker_test.cc | 2 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_common.cc | 20 | ||||
-rw-r--r-- | runtime/mirror/method_handle_impl.h | 10 | ||||
-rw-r--r-- | test/956-methodhandles/src/Main.java | 49 | ||||
-rw-r--r-- | test/957-methodhandle-transforms/expected.txt | 2 | ||||
-rw-r--r-- | test/957-methodhandle-transforms/src/Main.java | 20 |
6 files changed, 96 insertions, 7 deletions
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 7a3ebad082..ab2d9d0a95 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -742,8 +742,8 @@ struct MethodHandleImplOffsets : public CheckOffsets<mirror::MethodHandleImpl> { 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 1a99100eb4..e607d4d8bb 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -833,9 +833,23 @@ inline bool DoInvokePolymorphic(Thread* self, 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 7bf9c5b24c..9054216065 100644 --- a/runtime/mirror/method_handle_impl.h +++ b/runtime/mirror/method_handle_impl.h @@ -36,6 +36,10 @@ class MANAGED MethodHandle : public Object { 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 @@ class MANAGED MethodHandle : public Object { } 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 d824c6040c..780513f9ed 100644 --- a/test/956-methodhandles/src/Main.java +++ b/test/956-methodhandles/src/Main.java @@ -64,6 +64,7 @@ public class Main { testExceptionDetailMessages(); testfindVirtual(); testUnreflects(); + testAsType(); } public static void testfindSpecial_invokeSuperBehaviour() throws Throwable { @@ -428,6 +429,54 @@ public class Main { 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 fc4fdd6070..7540ef74f3 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 +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 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 4a27086924..e50391b299 100644 --- a/test/957-methodhandle-transforms/src/Main.java +++ b/test/957-methodhandle-transforms/src/Main.java @@ -90,6 +90,12 @@ public class Main { // 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 @@ public class Main { 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 @@ public class Main { 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() { |