|  | // Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | package com.android.tools.r8.cf; | 
|  |  | 
|  | import com.android.tools.r8.NoVerticalClassMerging; | 
|  | import java.lang.invoke.MethodHandle; | 
|  | import java.lang.invoke.MethodHandles; | 
|  | import java.lang.invoke.MethodType; | 
|  |  | 
|  | public class MethodHandleTest { | 
|  |  | 
|  | public static class C { | 
|  | public C(int i) { | 
|  | System.out.println("C " + i); | 
|  | } | 
|  |  | 
|  | public C() { | 
|  | System.out.println("C"); | 
|  | } | 
|  |  | 
|  | public int vi; | 
|  | public static int si; | 
|  |  | 
|  | public static void svi(int i) { | 
|  | System.out.println("svi " + i); | 
|  | } | 
|  |  | 
|  | public static long sji(int i) { | 
|  | System.out.println("sji " + i); | 
|  | return 42L; | 
|  | } | 
|  |  | 
|  | public static void svic(int i, char c) { | 
|  | System.out.println("svic " + i); | 
|  | } | 
|  |  | 
|  | public static long sjic(int i, char c) { | 
|  | System.out.println("sjic " + i); | 
|  | return 42L; | 
|  | } | 
|  |  | 
|  | public void vvi(int i) { | 
|  | System.out.println("vvi " + i); | 
|  | } | 
|  |  | 
|  | public long vji(int i) { | 
|  | System.out.println("vji " + i); | 
|  | return 42L; | 
|  | } | 
|  |  | 
|  | public void vvic(int i, char c) { | 
|  | System.out.println("vvic " + i); | 
|  | } | 
|  |  | 
|  | public long vjic(int i, char c) { | 
|  | System.out.println("vjic " + i); | 
|  | return 42L; | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class D extends C { | 
|  | public static MethodHandle vcviSpecialMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findSpecial(C.class, "vvi", viType(), D.class); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public void vvi(int i) { | 
|  | // Overridden to output nothing. | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class E { | 
|  | // Class that is only mentioned in parameter list of LDC(MethodType)-instruction. | 
|  | } | 
|  |  | 
|  | public static class F { | 
|  | // Class that is only mentioned in return value of LDC(MethodType)-instruction. | 
|  | } | 
|  |  | 
|  | @NoVerticalClassMerging | 
|  | public interface I { | 
|  | int ii = 42; | 
|  |  | 
|  | static void svi(int i) { | 
|  | System.out.println("svi " + i); | 
|  | } | 
|  |  | 
|  | static long sji(int i) { | 
|  | System.out.println("sji " + i); | 
|  | return 42L; | 
|  | } | 
|  |  | 
|  | static void svic(int i, char c) { | 
|  | System.out.println("svic " + i); | 
|  | } | 
|  |  | 
|  | static long sjic(int i, char c) { | 
|  | System.out.println("sjic " + i); | 
|  | return 42L; | 
|  | } | 
|  |  | 
|  | default void dvi(int i) { | 
|  | System.out.println("dvi " + i); | 
|  | } | 
|  |  | 
|  | default long dji(int i) { | 
|  | System.out.println("dji " + i); | 
|  | return 42L; | 
|  | } | 
|  |  | 
|  | default void dvic(int i, char c) { | 
|  | System.out.println("dvic " + i); | 
|  | } | 
|  |  | 
|  | default long djic(int i, char c) { | 
|  | System.out.println("djic " + i); | 
|  | return 42L; | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class Impl implements I {} | 
|  |  | 
|  | public static void main(String[] args) { | 
|  | // When MethodHandleTestRunner invokes this program with the JVM, "fail" is passed as arg. | 
|  | // When invoked with Art, no arg is passed since interface fields may be modified on Art. | 
|  | String expectedResult = args[0]; | 
|  | C c = new C(42); | 
|  | I i = new Impl(); | 
|  | try { | 
|  | scviMethod().invoke(1); | 
|  | assertEquals(42L, (long) scjiMethod().invoke(2)); | 
|  | scvicMethod().invoke(3, 'x'); | 
|  | assertEquals(42L, (long) scjicMethod().invoke(4, 'x')); | 
|  | vcviMethod().invoke(c, 5); | 
|  | assertEquals(42L, (long) vcjiMethod().invoke(c, 6)); | 
|  | vcvicMethod().invoke(c, 7, 'x'); | 
|  | assertEquals(42L, (long) vcjicMethod().invoke(c, 8, 'x')); | 
|  | siviMethod().invoke(9); | 
|  | assertEquals(42L, (long) sijiMethod().invoke(10)); | 
|  | sivicMethod().invoke(11, 'x'); | 
|  | assertEquals(42L, (long) sijicMethod().invoke(12, 'x')); | 
|  | diviMethod().invoke(i, 13); | 
|  | assertEquals(42L, (long) dijiMethod().invoke(i, 14)); | 
|  | divicMethod().invoke(i, 15, 'x'); | 
|  | assertEquals(42L, (long) dijicMethod().invoke(i, 16, 'x')); | 
|  | vciSetField().invoke(c, 17); | 
|  | assertEquals(17, (int) vciGetField().invoke(c)); | 
|  | sciSetField().invoke(18); | 
|  | assertEquals(18, (int) sciGetField().invoke()); | 
|  | String interfaceSetResult; | 
|  | try { | 
|  | iiSetField().invoke(19); | 
|  | interfaceSetResult = "pass"; | 
|  | } catch (RuntimeException e) { | 
|  | if (e.getCause() instanceof IllegalAccessException) { | 
|  | interfaceSetResult = "exception"; | 
|  | } else { | 
|  | throw e; | 
|  | } | 
|  | } catch (IllegalAccessError e) { | 
|  | interfaceSetResult = "error"; | 
|  | } | 
|  | if (!interfaceSetResult.equals(expectedResult)) { | 
|  | throw new RuntimeException( | 
|  | "Wrong outcome of iiSetField().invoke(): Expected " | 
|  | + expectedResult | 
|  | + " but got " | 
|  | + interfaceSetResult); | 
|  | } | 
|  | assertEquals(interfaceSetResult.equals("pass") ? 19 : 42, (int) iiGetField().invoke()); | 
|  | MethodHandle methodHandle = D.vcviSpecialMethod(); | 
|  | methodHandle.invoke(new D(), 20); | 
|  | constructorMethod().invoke(21); | 
|  | System.out.println(veType().parameterType(0).getName().lastIndexOf('.')); | 
|  | System.out.println(fType().returnType().getName().lastIndexOf('.')); | 
|  | } catch (Throwable e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void assertEquals(long l, long x) { | 
|  | if (l != x) { | 
|  | throw new AssertionError("Not equal: " + l + " != " + x); | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void assertEquals(int l, int x) { | 
|  | if (l != x) { | 
|  | throw new AssertionError("Not equal: " + l + " != " + x); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodType viType() { | 
|  | return MethodType.methodType(void.class, int.class); | 
|  | } | 
|  |  | 
|  | public static MethodType jiType() { | 
|  | return MethodType.methodType(long.class, int.class); | 
|  | } | 
|  |  | 
|  | public static MethodType vicType() { | 
|  | return MethodType.methodType(void.class, int.class, char.class); | 
|  | } | 
|  |  | 
|  | public static MethodType jicType() { | 
|  | return MethodType.methodType(long.class, int.class, char.class); | 
|  | } | 
|  |  | 
|  | public static MethodType veType() { | 
|  | return MethodType.methodType(void.class, E.class); | 
|  | } | 
|  |  | 
|  | public static MethodType fType() { | 
|  | return MethodType.methodType(F.class); | 
|  | } | 
|  |  | 
|  | public static MethodHandle scviMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findStatic(C.class, "svi", viType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle scjiMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findStatic(C.class, "sji", jiType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle scvicMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findStatic(C.class, "svic", vicType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle scjicMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findStatic(C.class, "sjic", jicType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle vcviMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findVirtual(C.class, "vvi", viType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle vcjiMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findVirtual(C.class, "vji", jiType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle vcvicMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findVirtual(C.class, "vvic", vicType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle vcjicMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findVirtual(C.class, "vjic", jicType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle siviMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findStatic(I.class, "svi", viType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle sijiMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findStatic(I.class, "sji", jiType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle sivicMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findStatic(I.class, "svic", vicType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle sijicMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findStatic(I.class, "sjic", jicType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle diviMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findVirtual(I.class, "dvi", viType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle dijiMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findVirtual(I.class, "dji", jiType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle divicMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findVirtual(I.class, "dvic", vicType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle dijicMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findVirtual(I.class, "djic", jicType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle vciSetField() { | 
|  | try { | 
|  | return MethodHandles.lookup().findSetter(C.class, "vi", int.class); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle sciSetField() { | 
|  | try { | 
|  | return MethodHandles.lookup().findStaticSetter(C.class, "si", int.class); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle vciGetField() { | 
|  | try { | 
|  | return MethodHandles.lookup().findGetter(C.class, "vi", int.class); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle sciGetField() { | 
|  | try { | 
|  | return MethodHandles.lookup().findStaticGetter(C.class, "si", int.class); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle iiSetField() { | 
|  | try { | 
|  | return MethodHandles.lookup().findStaticSetter(I.class, "ii", int.class); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle iiGetField() { | 
|  | try { | 
|  | return MethodHandles.lookup().findStaticGetter(I.class, "ii", int.class); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static MethodHandle constructorMethod() { | 
|  | try { | 
|  | return MethodHandles.lookup().findConstructor(C.class, viType()); | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  | } |