MethodHandles: Implement MethodHandle.asType.

Tracks libcore change 16fa583fb5ee489.

Test: make test-art-host
Bug: 30550796

Change-Id: I2457b563f67a183c4eebf94ddbe74cc55f772ee0
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;