| // 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(D[]::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; |
| } |
| } |