|  | // Copyright (c) 2017, 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 lambdadesugaring; | 
|  |  | 
|  | import java.io.Serializable; | 
|  | import java.util.ArrayList; | 
|  | import lambdadesugaring.legacy.Legacy; | 
|  | import lambdadesugaring.other.OtherRefs; | 
|  |  | 
|  | public class LambdaDesugaring { | 
|  | interface I { | 
|  | String foo(); | 
|  | } | 
|  |  | 
|  | interface V { | 
|  | void foo(); | 
|  | } | 
|  |  | 
|  | interface VT<T> { | 
|  | void foo(T t); | 
|  | } | 
|  |  | 
|  | interface P1<X> { | 
|  | X foo(int i); | 
|  | } | 
|  |  | 
|  | interface I2 extends I { | 
|  | } | 
|  |  | 
|  | interface I3 { | 
|  | String foo(); | 
|  | } | 
|  |  | 
|  | interface M1 { | 
|  | } | 
|  |  | 
|  | interface M2 { | 
|  | } | 
|  |  | 
|  | interface J { | 
|  | String foo(String a, int b, boolean c); | 
|  | } | 
|  |  | 
|  | interface G { | 
|  | A foo(); | 
|  | } | 
|  |  | 
|  | interface H<T extends A> { | 
|  | T foo(T o); | 
|  | } | 
|  |  | 
|  | interface K { | 
|  | Object foo(String a, String b, String c); | 
|  | } | 
|  |  | 
|  | interface ObjectProvider { | 
|  | Object act(); | 
|  | } | 
|  |  | 
|  | interface S2Z { | 
|  | boolean foo(String a); | 
|  | } | 
|  |  | 
|  | interface SS2Z { | 
|  | boolean foo(String a, String b); | 
|  | } | 
|  |  | 
|  | interface ArrayTransformerA<T> { | 
|  | T[] transform(T[] a); | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("unchecked") | 
|  | interface ArrayTransformerB<T> { | 
|  | T[] transform(T... a); | 
|  | } | 
|  |  | 
|  | static <T> void print(T[] a) { | 
|  | StringBuilder builder = new StringBuilder("{"); | 
|  | String sep = ""; | 
|  | for (T s : a) { | 
|  | builder.append(sep).append(s.toString()); | 
|  | sep = ", "; | 
|  | } | 
|  | builder.append("}"); | 
|  | System.out.println(builder.toString()); | 
|  | } | 
|  |  | 
|  | <T> T[] reorder(T[] a) { | 
|  | int size = a.length; | 
|  | for (int x = 0; x < size / 2; x++) { | 
|  | T t = a[x]; | 
|  | a[x] = a[size - 1 - x]; | 
|  | a[size - 1 - x] = t; | 
|  | } | 
|  | return a; | 
|  | } | 
|  |  | 
|  | static void atA(ArrayTransformerA<Integer> f) { | 
|  | print(f.transform(new Integer[] { 1, 2, 3 })); | 
|  | } | 
|  |  | 
|  | static void atB(ArrayTransformerB<String> f) { | 
|  | print(f.transform("A", "B", "C")); | 
|  | } | 
|  |  | 
|  | public static String staticUnused() { | 
|  | return "ReleaseTests::staticUnused"; | 
|  | } | 
|  |  | 
|  | public static void testUnusedLambdas() { | 
|  | System.out.print("Before unused ... "); | 
|  | Object o = (I) LambdaDesugaring::staticUnused; | 
|  | System.out.println("after unused."); | 
|  | } | 
|  |  | 
|  | class A { | 
|  | final String toString; | 
|  |  | 
|  | A(String toString) { | 
|  | this.toString = toString; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return toString; | 
|  | } | 
|  | } | 
|  |  | 
|  | class B extends A { | 
|  | B(String toString) { | 
|  | super(toString); | 
|  | } | 
|  | } | 
|  |  | 
|  | class C extends B { | 
|  | C(String toString) { | 
|  | super(toString); | 
|  | } | 
|  | } | 
|  |  | 
|  | class D extends C { | 
|  | D(String toString) { | 
|  | super(toString); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class Refs { | 
|  | public static String f(I i) { | 
|  | return i.foo(); | 
|  | } | 
|  |  | 
|  | public static void v(V v) { | 
|  | v.foo(); | 
|  | } | 
|  |  | 
|  | public static void vt(VT<String> v) { | 
|  | v.foo(null); | 
|  | } | 
|  |  | 
|  | public static String p1(P1 p) { | 
|  | return p.foo(123).getClass().getCanonicalName(); | 
|  | } | 
|  |  | 
|  | public static String pSS2Z(SS2Z p) { | 
|  | return "" + p.foo("123", "321"); | 
|  | } | 
|  |  | 
|  | public static String pS2Z(S2Z p) { | 
|  | return "" + p.foo("123"); | 
|  | } | 
|  |  | 
|  | public static String p3(K k) { | 
|  | return k.foo("A", "B", "C").toString(); | 
|  | } | 
|  |  | 
|  | public static String g(ObjectProvider op) { | 
|  | return op.act().toString(); | 
|  | } | 
|  |  | 
|  | static class A extends OtherRefs { | 
|  | String fooInternal() { | 
|  | return "Refs::A::fooInternal()"; | 
|  | } | 
|  |  | 
|  | protected String fooProtected() { | 
|  | return "Refs::A::fooProtected()"; | 
|  | } | 
|  |  | 
|  | protected String fooProtectedOverridden() { | 
|  | return "Refs::A::fooProtectedOverridden()"; | 
|  | } | 
|  |  | 
|  | protected static String staticProtected() { | 
|  | return "Refs::A::staticProtected()"; | 
|  | } | 
|  |  | 
|  | static String staticInternal() { | 
|  | return "Refs::A::staticInternal()"; | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class B extends A { | 
|  | public void test() { | 
|  | System.out.println(f(new A()::fooInternal)); | 
|  | System.out.println(f(this::fooInternal)); | 
|  | System.out.println(f(this::fooProtected)); | 
|  | System.out.println(f(this::fooProtectedOverridden)); | 
|  | System.out.println(f(this::fooPublic)); | 
|  | System.out.println(f(this::fooInternal)); | 
|  |  | 
|  | System.out.println(f(super::fooProtectedOverridden)); | 
|  | System.out.println(f(this::fooOtherProtected)); | 
|  | System.out.println(f(this::fooOtherPublic)); | 
|  |  | 
|  | System.out.println(g(this::fooPrivate)); | 
|  | System.out.println(g(new Integer(123)::toString)); | 
|  | System.out.println(g(System::lineSeparator)); | 
|  |  | 
|  | System.out.println(f(A::staticInternal)); | 
|  | System.out.println(f(A::staticProtected)); | 
|  | System.out.println(f(B::staticPrivate)); | 
|  | System.out.println(f(OtherRefs::staticOtherPublic)); | 
|  | System.out.println(f(OtherRefs::staticOtherProtected)); | 
|  |  | 
|  | System.out.println(g(StringBuilder::new)); | 
|  | System.out.println(g(OtherRefs.PublicInit::new)); | 
|  | System.out.println(ProtectedInit.testProtected()); | 
|  | System.out.println(g(ProtectedInit::new)); | 
|  | System.out.println(g(InternalInit::new)); | 
|  | System.out.println(PrivateInit.testPrivate()); | 
|  | System.out.println(g(PrivateInit::new)); | 
|  |  | 
|  | System.out.println(p1(LambdaDesugaring[]::new)); | 
|  | System.out.println(p1(Integer::new)); | 
|  | System.out.println(p1(B::staticArray)); | 
|  |  | 
|  | System.out.println(pSS2Z(String::equalsIgnoreCase)); | 
|  | System.out.println(pS2Z("123321"::contains)); | 
|  | System.out.println(pS2Z(String::isEmpty)); | 
|  |  | 
|  | System.out.println(p3(B::fooConcat)); | 
|  |  | 
|  | v(D::new); // Discarding the return value | 
|  | vt((new ArrayList<String>())::add); | 
|  |  | 
|  | I3 i3 = this::fooPrivate; | 
|  | System.out.println(f(i3::foo)); | 
|  | } | 
|  |  | 
|  | private static String staticPrivate() { | 
|  | return "Refs::B::staticPrivate()"; | 
|  | } | 
|  |  | 
|  | private String fooPrivate() { | 
|  | return "Refs::B::fooPrivate()"; | 
|  | } | 
|  |  | 
|  | String fooInternal() { | 
|  | return "Refs::B::fooInternal()"; | 
|  | } | 
|  |  | 
|  | public static StringBuilder fooConcat(Object... objs) { | 
|  | StringBuilder builder = new StringBuilder("Refs::B::fooConcat("); | 
|  | String sep = ""; | 
|  | for (Object obj : objs) { | 
|  | builder.append(sep).append(obj.toString()); | 
|  | sep = ", "; | 
|  | } | 
|  | return builder.append(")"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | protected String fooProtectedOverridden() { | 
|  | return "Refs::B::fooProtectedOverridden()"; | 
|  | } | 
|  |  | 
|  | public String fooPublic() { | 
|  | return "Refs::B::fooPublic()"; | 
|  | } | 
|  |  | 
|  | static int[] staticArray(int size) { | 
|  | return new int[size]; | 
|  | } | 
|  | } | 
|  |  | 
|  | static class D { | 
|  | D() { | 
|  | System.out.println("Refs::D::init()"); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class ProtectedInit extends OtherRefs.PublicInit { | 
|  | protected ProtectedInit() { | 
|  | } | 
|  |  | 
|  | static String testProtected() { | 
|  | return g(ProtectedInit::new); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "OtherRefs::ProtectedInit::init()"; | 
|  | } | 
|  | } | 
|  |  | 
|  | static class InternalInit extends ProtectedInit { | 
|  | InternalInit() { | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "Refs::InternalInit::init()"; | 
|  | } | 
|  | } | 
|  |  | 
|  | static class PrivateInit extends InternalInit { | 
|  | private PrivateInit() { | 
|  | } | 
|  |  | 
|  | static String testPrivate() { | 
|  | return g(PrivateInit::new); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "Refs::PrivateInit::init()"; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | public void testLambdasSimple() { | 
|  | System.out.println(f(() -> "testLambdasSimple#1")); | 
|  | System.out.println( | 
|  | g((a, b, c) -> "{" + a + ":" + b + ":" + c + "}", | 
|  | "testLambdasSimple#2", 123, true)); | 
|  | } | 
|  |  | 
|  | public void testLambdasSimpleWithCaptures() { | 
|  | String s = "<stirng>"; | 
|  | long l = 1234567890123456789L; | 
|  | char c = '#'; | 
|  |  | 
|  | System.out.println( | 
|  | g((x, y, z) -> "{" + s + ":" + l + ":" + c + ":" + x + ":" + y + ":" + z + "}", | 
|  | "param1", 2, false)); | 
|  |  | 
|  | I i1 = () -> "i1"; | 
|  | I i2 = () -> i1.foo() + ":i2"; | 
|  | I i3 = () -> i2.foo() + ":i3"; | 
|  | System.out.println(f(() -> "{" + i3.foo() + ":anonymous}")); | 
|  | } | 
|  |  | 
|  | public void testInstructionPatchingWithCatchHandlers() { | 
|  | try { | 
|  | int a = 1, b = 0; | 
|  | System.out.println(f(() -> "testInstructionPatchingWithCatchHandlers:1")); | 
|  | System.out.println(f(() -> ("does not matter " + (a / b)))); | 
|  | } catch (IndexOutOfBoundsException | ArithmeticException e) { | 
|  | System.out.println("testInstructionPatchingWithCatchHandlers:Divide By Zero"); | 
|  | } catch (RuntimeException re) { | 
|  | throw re; | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  |  | 
|  | int changes = -1; | 
|  | try { | 
|  | if (f(() -> "").isEmpty()) { | 
|  | changes = 32; | 
|  | System.out.println(f(() -> "testInstructionPatchingWithCatchHandlers:lambda")); | 
|  | throw new RuntimeException(); | 
|  | } else { | 
|  | changes = 42; | 
|  | throw new RuntimeException(); | 
|  | } | 
|  | } catch (Throwable t) { | 
|  | System.out.println("testInstructionPatchingWithCatchHandlers:changes=" + changes); | 
|  | } | 
|  | } | 
|  |  | 
|  | public void testInstanceLambdaMethods() { | 
|  | Integer i = 12345; | 
|  | System.out.println(h(() -> new A("{testInstanceLambdaMethods:" + i + "}"))); | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("unchecked") | 
|  | private void testEnforcedSignatureHelper() { | 
|  | H h = ((H<B>) x -> new B("{testEnforcedSignature:" + x + "}")); | 
|  | System.out.println(h.foo(new A("A")).toString()); | 
|  | } | 
|  |  | 
|  | public void testEnforcedSignature() { | 
|  | String capture = "capture"; | 
|  | System.out.println(i(x -> new B("{testEnforcedSignature:" + x + "}"))); | 
|  | System.out.println(i(x -> new B("{testEnforcedSignature:" + capture + "}"))); | 
|  |  | 
|  | try { | 
|  | testEnforcedSignatureHelper(); | 
|  | } catch (Exception e) { | 
|  | if (e.getMessage().contains("cannot be cast to lambdadesugaring.LambdaDesugaring$B") | 
|  | || e.getMessage() | 
|  | .contains("cannot be cast to class lambdadesugaring.LambdaDesugaring$B")) { | 
|  | System.out.println( | 
|  | "lambdadesugaring.LambdaDesugaring$A cannot be cast to" | 
|  | + " lambdadesugaring.LambdaDesugaring$B"); | 
|  | } else { | 
|  | System.out.println(e.getMessage()); | 
|  | } | 
|  | } | 
|  |  | 
|  | atA(t -> new LambdaDesugaring().reorder(t)); | 
|  | atB(t -> new LambdaDesugaring().reorder(t)); | 
|  | } | 
|  |  | 
|  | public void testMultipleInterfaces() { | 
|  | System.out.println(j((I2 & M1 & I3 & M2) () -> "{testMultipleInterfaces:1}")); | 
|  |  | 
|  | Object o = (I2 & M1 & I3 & M2) () -> "{testMultipleInterfaces:2}"; | 
|  | M1 m1 = (M1) o; | 
|  | M2 m2 = (M2) m1; | 
|  | I i = (I) m2; | 
|  | System.out.println(((I3) i).foo()); | 
|  |  | 
|  | o = (I2 & Serializable & M2) () -> "{testMultipleInterfaces:3}"; | 
|  | m2 = (M2) o; | 
|  | Serializable s = (Serializable) m2; | 
|  | System.out.println(((I) s).foo()); | 
|  | } | 
|  |  | 
|  | public void testBridges() { | 
|  | k((Legacy.BH) (x -> x), "{testBridges:1}"); | 
|  | k((Legacy.BK<Legacy.D> & Serializable) (x -> x), new Legacy.D("{testBridges:2}")); | 
|  | // k((Legacy.BL) (x -> x), new Legacy.B("{testBridges:3}")); crashes javac | 
|  | k((Legacy.BM) (x -> x), new Legacy.C("{testBridges:4}")); | 
|  | } | 
|  |  | 
|  | public String f(I i) { | 
|  | return i.foo(); | 
|  | } | 
|  |  | 
|  | String g(J j, String a, int b, boolean c) { | 
|  | return j.foo(a, b, c); | 
|  | } | 
|  |  | 
|  | String h(G g) { | 
|  | return g.foo().toString(); | 
|  | } | 
|  |  | 
|  | String i(H<B> h) { | 
|  | return h.foo(new B("i(H<B>)")).toString(); | 
|  | } | 
|  |  | 
|  | <T extends I2 & M1 & M2 & I3> String j(T l) { | 
|  | return ((I3) ((M2) ((M1) (((I2) l))))).foo(); | 
|  | } | 
|  |  | 
|  | static <T> void k(Legacy.BI<T> i, T v) { | 
|  | System.out.println(i.foo(v).toString()); | 
|  | } | 
|  |  | 
|  | static I statelessLambda() { | 
|  | return InstanceAndClassChecks::staticProvider; | 
|  | } | 
|  |  | 
|  | static I statefulLambda() { | 
|  | return InstanceAndClassChecks.INSTANCE::instanceProvider; | 
|  | } | 
|  |  | 
|  | static class InstanceAndClassChecks { | 
|  | static final InstanceAndClassChecks INSTANCE = new InstanceAndClassChecks(); | 
|  |  | 
|  | static void test() { | 
|  | assertDifferentInstance( | 
|  | INSTANCE::instanceProvider, | 
|  | INSTANCE::instanceProvider, | 
|  | "Instances must be different"); | 
|  | assertDifferentInstance( | 
|  | INSTANCE::instanceProvider, | 
|  | statefulLambda(), "Instances must be different"); | 
|  | } | 
|  |  | 
|  | public static String staticProvider() { | 
|  | return "staticProvider"; | 
|  | } | 
|  |  | 
|  | public String instanceProvider() { | 
|  | return "instanceProvider"; | 
|  | } | 
|  |  | 
|  | static void assertSameInstance(I a, I b, String msg) { | 
|  | if (a != b) { | 
|  | throw new AssertionError(msg); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void assertDifferentInstance(I a, I b, String msg) { | 
|  | if (a == b) { | 
|  | throw new AssertionError(msg); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | public static void main(String[] args) { | 
|  | LambdaDesugaring tests = new LambdaDesugaring(); | 
|  | tests.testLambdasSimple(); | 
|  | LambdaDesugaring.testUnusedLambdas(); | 
|  | tests.testLambdasSimpleWithCaptures(); | 
|  | tests.testInstructionPatchingWithCatchHandlers(); | 
|  | tests.testInstanceLambdaMethods(); | 
|  | tests.testEnforcedSignature(); | 
|  | tests.testMultipleInterfaces(); | 
|  | tests.testBridges(); | 
|  | new Refs.B().test(); | 
|  | if (isAndroid()) { | 
|  | InstanceAndClassChecks.test(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static boolean isAndroid() { | 
|  | try { | 
|  | Class.forName("dalvik.system.VMRuntime"); | 
|  | return true; | 
|  | } catch (Exception ignored) { | 
|  | } | 
|  | return false; | 
|  | } | 
|  | } |