| // 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) { | |
| 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() { | |
| assertSameInstance( | |
| InstanceAndClassChecks::staticProvider, | |
| InstanceAndClassChecks::staticProvider, | |
| "Instances must be same"); | |
| assertSameInstance( | |
| InstanceAndClassChecks::staticProvider, | |
| statelessLambda(), | |
| "Instances must be same"); | |
| 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; | |
| } | |
| } |