| /* |
| * Copyright 2017 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.gwt.dev.jjs.impl; |
| |
| import com.google.gwt.dev.javac.testing.impl.JavaResourceBase; |
| import com.google.gwt.dev.javac.testing.impl.MockJavaResource; |
| import com.google.gwt.dev.jjs.ast.JClassType; |
| import com.google.gwt.dev.jjs.ast.JConstructor; |
| import com.google.gwt.dev.jjs.ast.JInterfaceType; |
| import com.google.gwt.dev.jjs.ast.JMethod; |
| import com.google.gwt.dev.jjs.ast.JMethodBody; |
| import com.google.gwt.dev.jjs.ast.JPrimitiveType; |
| import com.google.gwt.dev.jjs.ast.JProgram; |
| import com.google.gwt.dev.util.arg.SourceLevel; |
| import com.google.gwt.thirdparty.guava.common.base.Joiner; |
| |
| import java.util.Collections; |
| |
| /** |
| * Tests that {@link com.google.gwt.dev.jjs.impl.GwtAstBuilder} correctly builds the AST for |
| * features introduced in Java 8. |
| */ |
| public class Java8AstTest extends FullCompileTestBase { |
| |
| @Override |
| public void setUp() throws Exception { |
| sourceLevel = SourceLevel.JAVA9; |
| super.setUp(); |
| addAll(LAMBDA_METAFACTORY); |
| |
| addAll(JavaResourceBase.createMockJavaResource("test.Runnable", |
| "package test;", |
| "public interface Runnable {", |
| " void run();", |
| "}" |
| )); |
| addAll(JavaResourceBase.createMockJavaResource("test.Lambda", |
| "package test;", |
| "public interface Lambda<T> {", |
| " T run(int a, int b);", |
| "}" |
| )); |
| addAll(JavaResourceBase.createMockJavaResource("test.Lambda2", |
| "package test;", |
| "public interface Lambda2<T> {", |
| " boolean run(T a, T b);", |
| "}" |
| )); |
| addAll(JavaResourceBase.createMockJavaResource("test.AcceptsLambda", |
| "package test;", |
| "public class AcceptsLambda<T> {", |
| " public T accept(Lambda<T> foo) {", |
| " return foo.run(10, 20);", |
| " }", |
| " public boolean accept2(Lambda2<String> foo) {", |
| " return foo.run(\"a\", \"b\");", |
| " }", |
| "}" |
| )); |
| |
| addAll(JavaResourceBase.createMockJavaResource("test.Pojo", |
| "package test;", |
| "public class Pojo {", |
| " public Pojo(int x, int y) {", |
| " }", |
| " public Integer fooInstance(int a, int b) {", |
| " return a + b;", |
| " }", |
| "}" |
| )); |
| |
| addAll(JavaResourceBase.createMockJavaResource("test.DefaultInterface", |
| "package test;", |
| "public interface DefaultInterface {", |
| " void method1();", |
| " default int method2() { return 42; }", |
| "}" |
| )); |
| |
| addAll(JavaResourceBase.createMockJavaResource("test.DefaultInterfaceImpl", |
| "package test;", |
| "public class DefaultInterfaceImpl implements DefaultInterface {", |
| " public void method1() {}", |
| "}" |
| )); |
| |
| addAll(JavaResourceBase.createMockJavaResource("test.DefaultInterfaceImpl2", |
| "package test;", |
| "public class DefaultInterfaceImpl2 implements DefaultInterface {", |
| " public void method1() {}", |
| " public int method2() { return DefaultInterface.super.method2(); }", |
| "}" |
| )); |
| } |
| |
| public void testCompileLambdaNoCapture() throws Exception { |
| String lambda = "new AcceptsLambda<Integer>().accept((a,b) -> a + b);"; |
| assertEqualBlock( |
| "(new AcceptsLambda()).accept(new EntryPoint$lambda$0$Type());", |
| lambda |
| ); |
| JProgram program = compileSnippet("void", lambda, false); |
| // created by JDT, should exist |
| assertNotNull(getMethod(program, "lambda$0")); |
| |
| // created by GwtAstBuilder |
| JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$lambda$0$Type"); |
| assertNotNull(lambdaInnerClass); |
| |
| // no fields |
| assertEquals(0, lambdaInnerClass.getFields().size()); |
| |
| // should have constructor taking no args |
| JMethod ctor = findMethod(lambdaInnerClass, "EntryPoint$lambda$0$Type"); |
| assertTrue(ctor instanceof JConstructor); |
| assertEquals(0, ctor.getParams().size()); |
| |
| // should extends test.Lambda |
| assertTrue(lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.Lambda"))); |
| // should implement run method |
| JMethod samMethod = findMethod(lambdaInnerClass, "run"); |
| assertEquals( |
| "public final Object run(int arg0,int arg1){return EntryPoint.lambda$0(arg0,arg1);}", |
| formatSource(samMethod.toSource())); |
| } |
| |
| public void testCompileLambdaCaptureLocal() throws Exception { |
| String lambda = "int x = 42; new AcceptsLambda<Integer>().accept((a,b) -> x + a + b);"; |
| assertEqualBlock( |
| "int x=42;(new AcceptsLambda()).accept(new EntryPoint$lambda$0$Type(x));", |
| lambda |
| ); |
| JProgram program = compileSnippet("void", lambda, false); |
| // created by JDT, should exist |
| assertNotNull(getMethod(program, "lambda$0")); |
| |
| // created by GwtAstBuilder |
| JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$lambda$0$Type"); |
| assertNotNull(lambdaInnerClass); |
| |
| // should have constructor taking x |
| JMethod ctor = findMethod(lambdaInnerClass, "EntryPoint$lambda$0$Type"); |
| assertTrue(ctor instanceof JConstructor); |
| assertEquals(1, ctor.getParams().size()); |
| assertEquals(JPrimitiveType.INT, ctor.getOriginalParamTypes().get(0)); |
| |
| // should have 1 field to store the local |
| assertEquals(1, lambdaInnerClass.getFields().size()); |
| assertEquals(JPrimitiveType.INT, lambdaInnerClass.getFields().get(0).getType()); |
| |
| // should contain assignment statement of ctor param to field |
| assertEquals("{this.x_0=x_0;}", formatSource(ctor.getBody().toSource())); |
| // should extends test.Lambda |
| assertTrue(lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.Lambda"))); |
| |
| // should implement run method and invoke lambda as static function |
| JMethod samMethod = findMethod(lambdaInnerClass, "run"); |
| assertEquals( |
| "public final Object run(int arg0,int arg1){" + |
| "return EntryPoint.lambda$0(this.x_0,arg0,arg1);}", |
| formatSource(samMethod.toSource())); |
| } |
| |
| public void testCompileLambdaCaptureLocalWithBlockInLambda() throws Exception { |
| String lambda = |
| "int x = 42; " |
| + "new AcceptsLambda<Integer>().accept((a,b) -> { int temp = x; return temp + a + b; });"; |
| assertEqualBlock( |
| "int x=42;(new AcceptsLambda()).accept(new EntryPoint$lambda$0$Type(x));", |
| lambda |
| ); |
| JProgram program = compileSnippet("void", lambda, false); |
| // created by JDT, should exist |
| assertNotNull(getMethod(program, "lambda$0")); |
| |
| // created by GwtAstBuilder |
| JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$lambda$0$Type"); |
| assertNotNull(lambdaInnerClass); |
| |
| // should have constructor taking x |
| JMethod ctor = findMethod(lambdaInnerClass, "EntryPoint$lambda$0$Type"); |
| assertTrue(ctor instanceof JConstructor); |
| assertEquals(1, ctor.getParams().size()); |
| assertEquals(JPrimitiveType.INT, ctor.getOriginalParamTypes().get(0)); |
| |
| // should have 1 field to store the local |
| assertEquals(1, lambdaInnerClass.getFields().size()); |
| assertEquals(JPrimitiveType.INT, lambdaInnerClass.getFields().get(0).getType()); |
| |
| // should contain assignment statement of ctor param to field |
| assertEquals("{this.x_0=x_0;}", formatSource(ctor.getBody().toSource())); |
| // should extends test.Lambda |
| assertTrue(lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.Lambda"))); |
| |
| // should implement run method and invoke lambda as static function |
| JMethod samMethod = findMethod(lambdaInnerClass, "run"); |
| assertEquals( |
| "public final Object run(int arg0,int arg1){" + |
| "return EntryPoint.lambda$0(this.x_0,arg0,arg1);}", |
| formatSource(samMethod.toSource())); |
| } |
| |
| // test whether local capture and outer scope capture work together |
| public void testCompileLambdaCaptureLocalAndField() throws Exception { |
| addSnippetClassDecl("private int y = 22;"); |
| String lambda = "int x = 42; new AcceptsLambda<Integer>().accept((a,b) -> x + y + a + b);"; |
| assertEqualBlock( |
| "int x=42;(new AcceptsLambda()).accept(new EntryPoint$lambda$0$Type(this,x));", |
| lambda |
| ); |
| JProgram program = compileSnippet("void", lambda, false); |
| // created by JDT, should exist |
| assertNotNull(getMethod(program, "lambda$0")); |
| |
| // created by GwtAstBuilder |
| JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$lambda$0$Type"); |
| assertNotNull(lambdaInnerClass); |
| |
| // should have constructor taking this and x |
| JMethod ctor = findMethod(lambdaInnerClass, "EntryPoint$lambda$0$Type"); |
| assertTrue(ctor instanceof JConstructor); |
| assertEquals(2, ctor.getParams().size()); |
| assertEquals(lambdaInnerClass.getEnclosingType(), ctor.getOriginalParamTypes().get(0)); |
| assertEquals(JPrimitiveType.INT, ctor.getOriginalParamTypes().get(1)); |
| |
| // should have 2 field to store the outer and local |
| assertEquals(2, lambdaInnerClass.getFields().size()); |
| assertEquals(lambdaInnerClass.getEnclosingType(), |
| lambdaInnerClass.getFields().get(0).getType()); |
| assertEquals(JPrimitiveType.INT, lambdaInnerClass.getFields().get(1).getType()); |
| |
| // should contain assignment statement of ctor params to field |
| assertEquals("{this.$$outer_0=$$outer_0;this.x_1=x_1;}", |
| formatSource(ctor.getBody().toSource())); |
| // should extends test.Lambda |
| assertTrue(lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.Lambda"))); |
| |
| // should implement run method and invoke lambda via captured instance |
| JMethod samMethod = findMethod(lambdaInnerClass, "run"); |
| assertEquals( |
| "public final Object run(int arg0,int arg1){" + |
| "return this.$$outer_0.lambda$0(this.x_1,arg0,arg1);}", |
| formatSource(samMethod.toSource())); |
| } |
| |
| // make sure nested scoping of identically named variables works |
| public void testCompileLambdaCaptureOuterInnerField() throws Exception { |
| addSnippetClassDecl("private int y = 22;"); |
| addSnippetClassDecl("class Foo { " + |
| "int y = 42;" + |
| "void m() {" + |
| "new AcceptsLambda<Integer>().accept((a,b) -> EntryPoint.this.y + y + a + b); }" + |
| " }"); |
| String lambda = "new Foo().m();"; |
| assertEqualBlock( |
| "(new EntryPoint$Foo(this)).m();", |
| lambda |
| ); |
| JProgram program = compileSnippet("void", lambda, false); |
| // created by JDT, should exist |
| JMethod lambdaMethod = findMethod(program.getFromTypeMap("test.EntryPoint$Foo"), "lambda$0"); |
| assertNotNull(lambdaMethod); |
| assertEquals("{return Integer.valueOf(this.this$01.y+this.y+a_0+b_1);}", |
| formatSource(lambdaMethod.getBody().toSource())); |
| // created by GwtAstBuilder |
| JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$Foo$lambda$0$Type"); |
| assertNotNull(lambdaInnerClass); |
| |
| // should have constructor taking this |
| JMethod ctor = findMethod(lambdaInnerClass, "EntryPoint$Foo$lambda$0$Type"); |
| assertTrue(ctor instanceof JConstructor); |
| assertEquals(1, ctor.getParams().size()); |
| assertEquals(lambdaInnerClass.getEnclosingType(), ctor.getOriginalParamTypes().get(0)); |
| |
| // should have 1 field to store the outer |
| assertEquals(1, lambdaInnerClass.getFields().size()); |
| assertEquals(lambdaInnerClass.getEnclosingType(), |
| lambdaInnerClass.getFields().get(0).getType()); |
| |
| // should contain assignment statement of ctor params to field |
| assertEquals("{this.$$outer_0=$$outer_0;}", formatSource(ctor.getBody().toSource())); |
| // should extends test.Lambda |
| assertTrue(lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.Lambda"))); |
| |
| // should implement run method and invoke lambda via captured instance |
| JMethod samMethod = findMethod(lambdaInnerClass, "run"); |
| assertEquals( |
| "public final Object run(int arg0,int arg1){return this.$$outer_0.lambda$0(arg0,arg1);}", |
| formatSource(samMethod.toSource())); |
| } |
| |
| public void testLambdaCaptureParameter() throws Exception { |
| addSnippetClassDecl("interface ClickHandler {\n" + |
| " int onClick(int a);\n" + |
| " }\n" + |
| " private int addClickHandler(ClickHandler clickHandler) {\n" + |
| " return clickHandler.onClick(1);\n" + |
| " }\n" + |
| " private int addClickHandler(int a) {\n" + |
| " return addClickHandler(x->{int temp = a; return temp;});\n" + |
| " }\n"); |
| JProgram program = compileSnippet("int", "return addClickHandler(2);", false); |
| JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$lambda$0$Type"); |
| assertNotNull(lambdaInnerClass); |
| |
| // should have constructor taking the outer variable from parameter |
| JMethod ctor = findMethod(lambdaInnerClass, "EntryPoint$lambda$0$Type"); |
| assertTrue(ctor instanceof JConstructor); |
| assertEquals(1, ctor.getParams().size()); |
| assertEquals(JPrimitiveType.INT, ctor.getOriginalParamTypes().get(0)); |
| |
| // should have 1 field to store the outer |
| assertEquals(1, lambdaInnerClass.getFields().size()); |
| assertEquals(JPrimitiveType.INT, |
| lambdaInnerClass.getFields().get(0).getType()); |
| |
| // should contain assignment statement of ctor params to field |
| assertEquals("{this.a_0=a_0;}", formatSource(ctor.getBody().toSource())); |
| // should extends test.Lambda |
| assertTrue(lambdaInnerClass.getImplements().contains( |
| program.getFromTypeMap("test.EntryPoint$ClickHandler"))); |
| |
| JMethod samMethod = findMethod(lambdaInnerClass, "onClick"); |
| assertEquals("public final int onClick(int a){return EntryPoint.lambda$0(this.a_0,a);}", |
| formatSource(samMethod.toSource())); |
| } |
| |
| public void testLambdaNestingCaptureLocal() throws Exception { |
| addSnippetClassDecl("interface Inner {\n" + |
| " void f();\n" + |
| " }\n"); |
| addSnippetClassDecl( |
| " interface Outer {\n" + |
| " void accept(Inner t);\n" + |
| " }\n"); |
| addSnippetClassDecl( |
| " public static void call(Outer a) {\n" + |
| " a.accept(() -> {});\n" + |
| " }\n"); |
| String nestedLambda = "boolean[] success = new boolean[] {false};\n" |
| + "call( sam1 -> { call(sam2 -> {success[0] = true;}); });"; |
| assertEqualBlock("boolean[]success=new boolean[]{false};" |
| + "EntryPoint.call(new EntryPoint$lambda$1$Type(success));", nestedLambda); |
| JProgram program = compileSnippet("void", nestedLambda, false); |
| JClassType lambdaInnerClass1 = (JClassType) getType(program, "test.EntryPoint$lambda$0$Type"); |
| JClassType lambdaInnerClass2 = (JClassType) getType(program, "test.EntryPoint$lambda$1$Type"); |
| JClassType lambdaInnerClass3 = (JClassType) getType(program, "test.EntryPoint$lambda$2$Type"); |
| assertNotNull(lambdaInnerClass1); |
| assertNotNull(lambdaInnerClass2); |
| assertNotNull(lambdaInnerClass3); |
| |
| // check constructors |
| JMethod ctor1 = findMethod(lambdaInnerClass1, "EntryPoint$lambda$0$Type"); |
| assertTrue(ctor1 instanceof JConstructor); |
| assertEquals(0, ctor1.getParams().size()); |
| |
| JMethod ctor2 = findMethod(lambdaInnerClass2, "EntryPoint$lambda$1$Type"); |
| assertTrue(ctor2 instanceof JConstructor); |
| assertEquals(1, ctor2.getParams().size()); |
| assertEquals("boolean[]", ctor2.getOriginalParamTypes().get(0).getName()); |
| |
| JMethod ctor3 = findMethod(lambdaInnerClass3, "EntryPoint$lambda$2$Type"); |
| assertTrue(ctor3 instanceof JConstructor); |
| assertEquals(1, ctor3.getParams().size()); |
| assertEquals("boolean[]", ctor3.getOriginalParamTypes().get(0).getName()); |
| |
| // check fields |
| assertEquals(0, lambdaInnerClass1.getFields().size()); |
| |
| assertEquals(1, lambdaInnerClass2.getFields().size()); |
| assertEquals("boolean[]", |
| lambdaInnerClass2.getFields().get(0).getType().getName()); |
| |
| assertEquals(1, lambdaInnerClass3.getFields().size()); |
| assertEquals("boolean[]", |
| lambdaInnerClass3.getFields().get(0).getType().getName()); |
| |
| // check constructor body |
| assertEquals("{this.success_0=success_0;}", formatSource(ctor2.getBody().toSource())); |
| assertEquals("{this.success_0=success_0;}", formatSource(ctor3.getBody().toSource())); |
| |
| // check super interface |
| assertTrue(lambdaInnerClass1.getImplements().contains( |
| program.getFromTypeMap("test.EntryPoint$Inner"))); |
| assertTrue(lambdaInnerClass2.getImplements().contains( |
| program.getFromTypeMap("test.EntryPoint$Outer"))); |
| assertTrue(lambdaInnerClass3.getImplements().contains( |
| program.getFromTypeMap("test.EntryPoint$Outer"))); |
| |
| // check samMethod |
| JMethod samMethod1 = findMethod(lambdaInnerClass2, "accept"); |
| JMethod samMethod2 = findMethod(lambdaInnerClass3, "accept"); |
| assertEquals( |
| "public final void accept(EntryPoint$Inner t){EntryPoint.lambda$1(this.success_0,t);}", |
| formatSource(samMethod1.toSource())); |
| assertEquals( |
| "public final void accept(EntryPoint$Inner t){EntryPoint.lambda$2(this.success_0,t);}", |
| formatSource(samMethod2.toSource())); |
| |
| // check lambda method |
| JMethod lambdaMethod1 = findMethod(program, "lambda$1"); |
| JMethod lambdaMethod2 = findMethod(program, "lambda$2"); |
| assertEquals( |
| "private static void lambda$1(boolean[]success_0,EntryPoint$Inner sam1_1)" |
| + "{{EntryPoint.call(new EntryPoint$lambda$2$Type(success_0));}}", |
| formatSource(lambdaMethod1.toSource())); |
| assertEquals( |
| "private static void lambda$2(boolean[]success_0,EntryPoint$Inner sam2_1)" |
| + "{{success_0[0]=true;}}", |
| formatSource(lambdaMethod2.toSource())); |
| } |
| |
| public void testLambdaNestingInAnonymousCaptureLocal() throws Exception { |
| String lambda = |
| "int x = 42;\n" + |
| "new Runnable() { public void run() { Lambda<Integer> l = (a, b) -> x + a + b; l.run(1, 2); } }.run();"; |
| assertEqualBlock("int x=42;(new EntryPoint$1(this,x)).run();", lambda); |
| JProgram program = compileSnippet("void", lambda, false); |
| JClassType outerClass = (JClassType) getType(program, "test.EntryPoint$1"); |
| |
| // check that anonymous class implementation uses synthetic field val$x2 to initialize lambda |
| // synthetic class |
| assertEquals( |
| "public void run(){Lambda l=new EntryPoint$1$lambda$0$Type(this.val$x2);l.run(1,2);}", |
| formatSource(findMethod(outerClass, "run").toSource())); |
| } |
| |
| public void testLambdaNestingInMultipleAnonymousCaptureLocal() throws Exception { |
| addSnippetClassDecl("interface I { int foo(Integer i); }"); |
| // checks that lambda has access to local variable and arguments when placed in local anonymous |
| // class with multiple nesting |
| String snippet = |
| "int[] x = new int[] {42};\n" + |
| "int result = new I(){\n" + |
| " public int foo(Integer i1){\n" + |
| " return new I(){\n" + |
| " public int foo(Integer i2){\n" + |
| " return new I(){\n" + |
| " public int foo(Integer i3){\n" + |
| " Lambda<Integer> l = (a, b) -> x[0] = x[0] + a + b + i1 + i2 + i3;\n" + |
| " return l.run(1, 2);\n" + |
| " }\n" + |
| " }.foo(3);\n" + |
| " }\n" + |
| " }.foo(2);\n" + |
| " }\n" + |
| "}.foo(1);\n"; |
| |
| JProgram program = compileSnippet("void", snippet, false); |
| JClassType outer1 = (JClassType) getType(program, "test.EntryPoint$1"); |
| JClassType outer2 = (JClassType) getType(program, "test.EntryPoint$1$1"); |
| JClassType outer3 = (JClassType) getType(program, "test.EntryPoint$1$1$1"); |
| |
| assertNotNull(outer1); |
| assertNotNull(outer2); |
| assertNotNull(outer3); |
| |
| JMethod outer1Method = findMethod(outer1, "foo"); |
| JMethod outer2Method = findMethod(outer2, "foo"); |
| JMethod outer3Method = findMethod(outer3, "foo"); |
| assertNotNull(outer3Method); |
| assertEquals( |
| "public int foo(Integer i1){" |
| + "return(new EntryPoint$1$1(this,this.val$x2,i1)).foo(Integer.valueOf(2));" |
| + "}", |
| formatSource(outer1Method.toSource())); |
| assertEquals( |
| "public int foo(Integer i2){" |
| + "return(new EntryPoint$1$1$1(this,this.val$x2,this.val$i13,i2)).foo(Integer.valueOf(3));" |
| + "}", |
| formatSource(outer2Method.toSource())); |
| // checks that lambda scope initialized similar to anonymous class |
| assertEquals( |
| "public int foo(Integer i3){" |
| + "Lambda l=new EntryPoint$1$1$1$lambda$0$Type(this.val$x2,this.val$i13,this.val$i24,i3);" |
| + "return((Integer)l.run(1,2)).intValue();" |
| + "}", |
| formatSource(outer3Method.toSource())); |
| } |
| |
| public void testLambdaNestingInMultipleMixedAnonymousCaptureLocal() throws Exception { |
| // checks that lambda has access to local variable and arguments when placed in mixed scopes |
| // Local Class -> Local Class -> Local Anonymous -> lambda -> Local Anonymous |
| addSnippetClassDecl("interface I { int foo(Integer i); }"); |
| addSnippetClassDecl("class A {\n" + |
| "int a() {\n" + |
| " int[] x = new int[] {42};\n" + |
| " class B {\n" + |
| " void b() {\n" + |
| " I i = new I(){\n" + |
| " public int foo(Integer arg){\n" + |
| " Runnable r = () ->{\n" + |
| " new Runnable() {\n" + |
| " public void run() {\n" + |
| " Lambda<Integer> l = (a, b) -> x[0] = x[0] + a + b + arg;\n" + |
| " x[0] = l.run(1, 2);\n" + |
| " }\n" + |
| " }.run();\n" + |
| " };\n" + |
| " r.run();\n" + |
| " return x[0];\n" + |
| " }\n" + |
| " };\n" + |
| " i.foo(1);\n" + |
| " }\n" + |
| " }\n" + |
| " B b = new B();\n" + |
| " b.b();\n" + |
| " return x[0];\n" + |
| "}\n" + |
| "}\n"); |
| |
| String snippet = "A a = new A();"; |
| JProgram program = compileSnippet("void", snippet, false); |
| JClassType lambdaOuter = (JClassType) getType(program, "test.EntryPoint$A$1B$1$1"); |
| assertNotNull(lambdaOuter); |
| |
| JMethod lambdaOuterMethod = findMethod(lambdaOuter, "run"); |
| assertNotNull(lambdaOuterMethod); |
| // checks that lambda initialization properly uses synthetic fields from outer class |
| assertEquals( |
| "public void run(){" |
| + "Lambda l=new EntryPoint$A$1B$1$1$lambda$0$Type(this.val$x2,this.val$arg3);" |
| + "this.val$x2[0]=((Integer)l.run(1,2)).intValue();" |
| + "}", |
| formatSource(lambdaOuterMethod.toSource())); |
| } |
| |
| public void testLambdaNestingCaptureField() throws Exception { |
| addSnippetClassDecl("interface Inner {\n" + |
| " void f();\n" + |
| " }\n"); |
| addSnippetClassDecl( |
| " interface Outer {\n" + |
| " void accept(Inner t);\n" + |
| " }\n"); |
| addSnippetClassDecl( |
| " static class A {\n" + |
| " public boolean[] success = new boolean[] {false};\n" + |
| " public void call(Outer a) {\n" + |
| " a.accept(() -> {});\n" + |
| " }\n" + |
| " }\n"); |
| String nestedLambda = "A a = new A();\n" |
| + "a.call( sam1 -> { a.call(sam2 -> {a.success[0] = true;}); });"; |
| assertEqualBlock("EntryPoint$A a=new EntryPoint$A();a.call(new EntryPoint$lambda$0$Type(a));", |
| nestedLambda); |
| JProgram program = compileSnippet("void", nestedLambda, false); |
| JClassType lambdaInnerClass1 = (JClassType) getType(program, "test.EntryPoint$lambda$0$Type"); |
| JClassType lambdaInnerClass2 = (JClassType) getType(program, "test.EntryPoint$lambda$1$Type"); |
| assertNotNull(lambdaInnerClass1); |
| assertNotNull(lambdaInnerClass2); |
| |
| // check constructors |
| JMethod ctor1 = findMethod(lambdaInnerClass1, "EntryPoint$lambda$0$Type"); |
| assertTrue(ctor1 instanceof JConstructor); |
| assertEquals(1, ctor1.getParams().size()); |
| assertEquals("test.EntryPoint$A", ctor1.getOriginalParamTypes().get(0).getName()); |
| |
| JMethod ctor2 = findMethod(lambdaInnerClass2, "EntryPoint$lambda$1$Type"); |
| assertTrue(ctor2 instanceof JConstructor); |
| assertEquals(1, ctor2.getParams().size()); |
| assertEquals("test.EntryPoint$A", ctor2.getOriginalParamTypes().get(0).getName()); |
| |
| // check fields |
| assertEquals(1, lambdaInnerClass1.getFields().size()); |
| assertEquals("test.EntryPoint$A", lambdaInnerClass2.getFields().get(0).getType().getName()); |
| |
| assertEquals(1, lambdaInnerClass2.getFields().size()); |
| assertEquals("test.EntryPoint$A", lambdaInnerClass2.getFields().get(0).getType().getName()); |
| |
| // check constructor body |
| assertEquals("{this.a_0=a_0;}", formatSource(ctor2.getBody().toSource())); |
| assertEquals("{this.a_0=a_0;}", formatSource(ctor2.getBody().toSource())); |
| |
| // check super interface |
| assertTrue(lambdaInnerClass1.getImplements().contains( |
| program.getFromTypeMap("test.EntryPoint$Outer"))); |
| assertTrue(lambdaInnerClass2.getImplements().contains( |
| program.getFromTypeMap("test.EntryPoint$Outer"))); |
| |
| // check samMethod |
| JMethod samMethod1 = findMethod(lambdaInnerClass1, "accept"); |
| JMethod samMethod2 = findMethod(lambdaInnerClass2, "accept"); |
| assertEquals( |
| "public final void accept(EntryPoint$Inner t){EntryPoint.lambda$0(this.a_0,t);}", |
| formatSource(samMethod1.toSource())); |
| assertEquals( |
| "public final void accept(EntryPoint$Inner t){EntryPoint.lambda$1(this.a_0,t);}", |
| formatSource(samMethod2.toSource())); |
| |
| // check lambda method |
| JMethod lambdaMethod1 = findMethod(program, "lambda$0"); |
| JMethod lambdaMethod2 = findMethod(program, "lambda$1"); |
| assertEquals( |
| "private static void lambda$0(EntryPoint$A a_0,EntryPoint$Inner sam1_1)" |
| + "{{a_0.call(new EntryPoint$lambda$1$Type(a_0));}}", |
| formatSource(lambdaMethod1.toSource())); |
| assertEquals( |
| "private static void lambda$1(EntryPoint$A a_0,EntryPoint$Inner sam2_1)" |
| + "{{a_0.success[0]=true;}}", |
| formatSource(lambdaMethod2.toSource())); |
| } |
| |
| public void testCompileStaticReferenceBinding() throws Exception { |
| addSnippetClassDecl("public static Integer foo(int x, int y) { return x + y; }"); |
| String lambda = "new AcceptsLambda<Integer>().accept(EntryPoint::foo);"; |
| |
| String generatedInnerClassName = "test.EntryPoint$0methodref$foo$Type"; |
| String simpleLambdaInnerClassName = generatedInnerClassName.substring("test.".length()); |
| |
| assertEqualBlock( |
| "(new AcceptsLambda()).accept(new " + simpleLambdaInnerClassName + "());", lambda); |
| JProgram program = compileSnippet("void", lambda, false); |
| // created by JDT, should exist |
| assertNotNull(getMethod(program, "foo")); |
| |
| // created by GwtAstBuilder |
| JClassType lambdaInnerClass = (JClassType) getType(program, generatedInnerClassName); |
| assertNotNull(lambdaInnerClass); |
| |
| // should have constructor taking this and x |
| JMethod ctor = findMethod(lambdaInnerClass, |
| simpleLambdaInnerClassName); |
| assertTrue(ctor instanceof JConstructor); |
| // no ctor args |
| assertEquals(0, ctor.getParams().size()); |
| |
| // no fields |
| assertEquals(0, lambdaInnerClass.getFields().size()); |
| |
| // should extends test.Lambda |
| assertTrue(lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.Lambda"))); |
| |
| // should implement run method and invoke lambda via captured instance |
| JMethod samMethod = findMethod(lambdaInnerClass, "run"); |
| assertEquals( |
| "public final Object run(int arg0,int arg1){return EntryPoint.foo(arg0,arg1);}", |
| formatSource(samMethod.toSource())); |
| } |
| |
| public void testCompileInstanceReferenceBinding() throws Exception { |
| addSnippetClassDecl("public Integer foo(int x, int y) { return x + y; }"); |
| String lambda = "new AcceptsLambda<Integer>().accept(this::foo);"; |
| |
| String generatedInnerClassName = "test.EntryPoint$0methodref$foo$Type"; |
| String simpleLambdaInnerClassName = generatedInnerClassName.substring("test.".length()); |
| |
| assertEqualBlock( |
| "(new AcceptsLambda()).accept(new " + simpleLambdaInnerClassName + "(this));", |
| lambda |
| ); |
| JProgram program = compileSnippet("void", lambda, false); |
| // created by JDT, should exist |
| assertNotNull(getMethod(program, "foo")); |
| |
| // created by GwtAstBuilder |
| JClassType lambdaInnerClass = (JClassType) getType(program, generatedInnerClassName); |
| assertNotNull(lambdaInnerClass); |
| |
| // should have constructor taking this and x |
| JMethod ctor = findMethod(lambdaInnerClass, simpleLambdaInnerClassName); |
| assertTrue(ctor instanceof JConstructor); |
| // instance capture |
| assertEquals(1, ctor.getParams().size()); |
| assertEquals(lambdaInnerClass.getEnclosingType(), ctor.getOriginalParamTypes().get(0)); |
| |
| // should have 1 field to store the captured instance |
| assertEquals(1, lambdaInnerClass.getFields().size()); |
| assertEquals(lambdaInnerClass.getEnclosingType(), |
| lambdaInnerClass.getFields().get(0).getType()); |
| |
| // should extends test.Lambda |
| assertTrue(lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.Lambda"))); |
| |
| // should implement run method and invoke lambda via captured instance |
| JMethod samMethod = findMethod(lambdaInnerClass, "run"); |
| assertEquals( |
| "public final Object run(int arg0,int arg1){return this.$$outer_0.foo(arg0,arg1);}", |
| formatSource(samMethod.toSource())); |
| } |
| |
| public void testCompileInstanceReferenceBindingMultiple() throws Exception { |
| addSnippetClassDecl("Pojo instance1 = new Pojo(1, 2);"); |
| addSnippetClassDecl("Pojo instance2 = new Pojo(3, 4);"); |
| String reference = |
| "new AcceptsLambda<Integer>().accept(instance1::fooInstance);\n" + |
| "new AcceptsLambda<Integer>().accept(instance2::fooInstance);"; |
| |
| String generatedInnerClassName = "test.EntryPoint$0methodref$fooInstance$Type"; |
| String simpleLambdaInnerClassName1 = generatedInnerClassName.substring("test.".length()); |
| String simpleLambdaInnerClassName2 = "EntryPoint$1methodref$fooInstance$Type"; |
| |
| assertEqualBlock( |
| "(new AcceptsLambda()).accept(new " + simpleLambdaInnerClassName1 + "(this.instance1));\n" |
| + "(new AcceptsLambda()).accept(new " + simpleLambdaInnerClassName2 + "(this.instance2));", |
| reference); |
| JProgram program = compileSnippet("void", reference, false); |
| |
| // created by GwtAstBuilder |
| JClassType lambdaInnerClass = (JClassType) getType(program, generatedInnerClassName); |
| assertNotNull(lambdaInnerClass); |
| assertEquals(1, Collections.frequency(program.getDeclaredTypes(), lambdaInnerClass)); |
| |
| // should have constructor taking the instance |
| JMethod ctor = findMethod(lambdaInnerClass, simpleLambdaInnerClassName1); |
| assertTrue(ctor instanceof JConstructor); |
| // instance capture |
| assertEquals(1, ctor.getParams().size()); |
| // should have 1 field to store the captured instance |
| assertEquals(1, lambdaInnerClass.getFields().size()); |
| assertEquals(lambdaInnerClass.getFields().get(0).getType(), |
| ctor.getOriginalParamTypes().get(0)); |
| |
| // should extends test.Lambda |
| assertTrue(lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.Lambda"))); |
| |
| // should implement run method and invoke lambda via captured instance |
| JMethod samMethod = findMethod(lambdaInnerClass, "run"); |
| assertEquals( |
| "public final Object run(int arg0,int arg1){return this.$$outer_0.fooInstance(arg0,arg1);}", |
| formatSource(samMethod.toSource())); |
| } |
| |
| public void testCompileInstanceReferenceBindingMultipleWithSameMethodSignature() throws Exception { |
| addSnippetClassDecl( |
| "static class TestMF_A {", |
| " public String getId() {", |
| " return \"A\";", |
| " }", |
| "}", |
| |
| "static class TestMF_B {", |
| " public String getId() {", |
| " return \"B\";", |
| " }", |
| "}", |
| |
| "interface Function<T> {", |
| " T apply();", |
| "}", |
| |
| "private String f(Function<String> arg) {", |
| " return arg.apply();", |
| " }"); |
| |
| String reference = Joiner.on('\n').join( |
| "TestMF_A a = new TestMF_A();", |
| "TestMF_B b = new TestMF_B();", |
| "f(a::getId);", |
| "f(b::getId);" |
| ); |
| |
| String generatedInnerClassNameForA = "test.EntryPoint$0methodref$getId$Type"; |
| String simpleLambdaInnerClassNameForA = |
| generatedInnerClassNameForA.substring("test.".length()); |
| |
| String generatedInnerClassNameForB = "test.EntryPoint$1methodref$getId$Type"; |
| String simpleLambdaInnerClassNameForB = |
| generatedInnerClassNameForB.substring("test.".length()); |
| |
| assertEqualBlock( |
| "EntryPoint$TestMF_A a=new EntryPoint$TestMF_A();" |
| + "EntryPoint$TestMF_B b=new EntryPoint$TestMF_B();" |
| + "this.f(new " + simpleLambdaInnerClassNameForA + "(a));" |
| + "this.f(new " + simpleLambdaInnerClassNameForB + "(b));", |
| reference); |
| JProgram program = compileSnippet("void", reference, false); |
| |
| // created by GwtAstBuilder |
| JClassType innerClassA = (JClassType) getType(program, generatedInnerClassNameForA); |
| JClassType innerClassB = (JClassType) getType(program, generatedInnerClassNameForB); |
| assertNotNull(innerClassA); |
| assertNotNull(innerClassB); |
| |
| // should have constructor taking this and x |
| JMethod ctorA = findMethod(innerClassA, simpleLambdaInnerClassNameForA); |
| assertTrue(ctorA instanceof JConstructor); |
| // instance capture |
| assertEquals(1, ctorA.getParams().size()); |
| assertEquals("test.EntryPoint$TestMF_A", ctorA.getOriginalParamTypes().get(0).getName()); |
| JMethod ctorB = findMethod(innerClassB, simpleLambdaInnerClassNameForB); |
| assertTrue(ctorB instanceof JConstructor); |
| // instance capture |
| assertEquals(1, ctorB.getParams().size()); |
| assertEquals("test.EntryPoint$TestMF_B", ctorB.getOriginalParamTypes().get(0).getName()); |
| |
| // should have 1 field to store the captured instance |
| assertEquals(1, innerClassA.getFields().size()); |
| assertEquals("test.EntryPoint$TestMF_A", |
| innerClassA.getFields().get(0).getType().getName()); |
| assertEquals(1, innerClassB.getFields().size()); |
| assertEquals("test.EntryPoint$TestMF_B", |
| innerClassB.getFields().get(0).getType().getName()); |
| |
| // should extends EntryPoint$Function |
| assertTrue( |
| innerClassA.getImplements().contains(program.getFromTypeMap("test.EntryPoint$Function"))); |
| assertTrue( |
| innerClassB.getImplements().contains(program.getFromTypeMap("test.EntryPoint$Function"))); |
| |
| // should implement apply method |
| JMethod samMethodA = findMethod(innerClassA, "apply"); |
| assertEquals( |
| "public final Object apply(){return this.$$outer_0.getId();}", |
| formatSource(samMethodA.toSource())); |
| JMethod samMethodB = findMethod(innerClassB, "apply"); |
| assertEquals( |
| "public final Object apply(){return this.$$outer_0.getId();}", |
| formatSource(samMethodB.toSource())); |
| } |
| |
| public void testCompileStaticReferenceBindingMultiple() throws Exception { |
| addSnippetClassDecl( |
| "static class TestMF_A {", |
| " public static String getId() {", |
| " return \"A\";", |
| " }", |
| "}", |
| |
| "static class TestMF_B {", |
| " public static String getId() {", |
| " return \"B\";", |
| " }", |
| "}", |
| |
| "interface Function<T> {", |
| " T apply();", |
| "}", |
| |
| "private String f(Function<String> arg) {", |
| " return arg.apply();", |
| " }"); |
| String reference = "f(TestMF_A::getId);\n" + "f(TestMF_B::getId);"; |
| |
| String generatedInnerClassNameForA = "test.EntryPoint$0methodref$getId$Type"; |
| String simpleLambdaInnerClassNameForA = |
| generatedInnerClassNameForA.substring("test.".length()); |
| |
| String generatedInnerClassNameForB = "test.EntryPoint$1methodref$getId$Type"; |
| String simpleLambdaInnerClassNameForB = |
| generatedInnerClassNameForB.substring("test.".length()); |
| |
| assertEqualBlock("this.f(new " + simpleLambdaInnerClassNameForA + "());" + |
| "this.f(new " + simpleLambdaInnerClassNameForB + "());", |
| reference); |
| JProgram program = compileSnippet("void", reference, false); |
| |
| // created by GwtAstBuilder |
| JClassType innerClassA = (JClassType) getType(program, generatedInnerClassNameForA); |
| JClassType innerClassB = (JClassType) getType(program, generatedInnerClassNameForB); |
| assertNotNull(innerClassA); |
| assertNotNull(innerClassB); |
| |
| // should extends EntryPoint$Function |
| assertTrue( |
| innerClassA.getImplements().contains(program.getFromTypeMap("test.EntryPoint$Function"))); |
| assertTrue( |
| innerClassB.getImplements().contains(program.getFromTypeMap("test.EntryPoint$Function"))); |
| |
| // should implement apply method |
| JMethod samMethodA = findMethod(innerClassA, "apply"); |
| assertEquals( |
| "public final Object apply(){return EntryPoint$TestMF_A.getId();}", |
| formatSource(samMethodA.toSource())); |
| JMethod samMethodB = findMethod(innerClassB, "apply"); |
| assertEquals( |
| "public final Object apply(){return EntryPoint$TestMF_B.getId();}", |
| formatSource(samMethodB.toSource())); |
| } |
| |
| public void testCompileImplicitQualifierReferenceBinding() throws Exception { |
| String lambda = "new AcceptsLambda<String>().accept2(String::equalsIgnoreCase);"; |
| |
| String generatedInnerClassName = "test.EntryPoint$0methodref$equalsIgnoreCase$Type"; |
| String simpleLambdaInnerClassName = |
| generatedInnerClassName.substring("test.".length()); |
| |
| assertEqualBlock( |
| "(new AcceptsLambda()).accept2(new " + simpleLambdaInnerClassName + " ());", |
| lambda |
| ); |
| JProgram program = compileSnippet("void", lambda, false); |
| |
| // created by GwtAstBuilder |
| JClassType lambdaInnerClass = (JClassType) getType(program, generatedInnerClassName); |
| assertNotNull(lambdaInnerClass); |
| |
| // should have constructor taking this and x |
| JMethod ctor = findMethod(lambdaInnerClass, simpleLambdaInnerClassName); |
| assertTrue(ctor instanceof JConstructor); |
| // no instance capture |
| assertEquals(0, ctor.getParams().size()); |
| |
| // no fields |
| assertEquals(0, lambdaInnerClass.getFields().size()); |
| |
| // should extends test.Lambda |
| assertTrue(lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.Lambda2"))); |
| |
| // should implement run method and invoke lambda via captured instance |
| JMethod samMethod = findMethod(lambdaInnerClass, "run"); |
| assertEquals( |
| "public final boolean run(Object arg0,Object arg1)" |
| + "{return((String)arg0).equalsIgnoreCase((String)arg1);}", |
| formatSource(samMethod.toSource())); |
| } |
| |
| public void testCompileConstructorReferenceBinding() throws Exception { |
| String lambda = "new AcceptsLambda<Pojo>().accept(Pojo::new);"; |
| |
| String generatedInnerClassName = "test.EntryPoint$0methodref$ctor$Type"; |
| String simpleLambdaInnerClassName = |
| generatedInnerClassName.substring("test.".length()); |
| |
| assertEqualBlock( |
| "(new AcceptsLambda()).accept(new " + simpleLambdaInnerClassName + "());", |
| lambda |
| ); |
| JProgram program = compileSnippet("void", lambda, false); |
| |
| // created by GwtAstBuilder |
| JClassType lambdaInnerClass = (JClassType) getType(program, generatedInnerClassName); |
| assertNotNull(lambdaInnerClass); |
| |
| // should have constructor taking this and x |
| JMethod ctor = findMethod(lambdaInnerClass, simpleLambdaInnerClassName); |
| assertTrue(ctor instanceof JConstructor); |
| // no instance capture |
| assertEquals(0, ctor.getParams().size()); |
| |
| // no fields |
| assertEquals(0, lambdaInnerClass.getFields().size()); |
| |
| // should extends test.Lambda |
| assertTrue(lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.Lambda"))); |
| |
| // should implement run method and invoke lambda via captured instance |
| JMethod samMethod = findMethod(lambdaInnerClass, "run"); |
| assertEquals( |
| "public final Object run(int arg0,int arg1){return new Pojo(arg0,arg1);}", |
| formatSource(samMethod.toSource())); |
| } |
| |
| public void testCompileConstructorReferenceBindingWithEnclosingInstanceCapture() |
| throws Exception { |
| addSnippetClassDecl( |
| "int field1, field2;", |
| |
| "class Pojo2 {", |
| " public Pojo2(int x, int y) {", |
| " }", |
| " public int someMethod() { ", |
| " return field1 + field2; ", |
| " }", |
| "}" |
| ); |
| |
| String lambda = "new AcceptsLambda<Pojo2>().accept(Pojo2::new);"; |
| |
| String generatedInnerClassName = "test.EntryPoint$0methodref$ctor$Type"; |
| String simpleLambdaInnerClassName = |
| generatedInnerClassName.substring("test.".length()); |
| |
| assertEqualBlock("(new AcceptsLambda()).accept(new " + simpleLambdaInnerClassName + "(this));", |
| lambda |
| ); |
| JProgram program = compileSnippet("void", lambda, false); |
| |
| // created by GwtAstBuilder |
| JClassType lambdaInnerClass = (JClassType) getType(program, generatedInnerClassName); |
| assertNotNull(lambdaInnerClass); |
| |
| // should have constructor taking this and x |
| JMethod ctor = findMethod(lambdaInnerClass, simpleLambdaInnerClassName); |
| assertTrue(ctor instanceof JConstructor); |
| // one instance capture |
| assertEquals(1, ctor.getParams().size()); |
| |
| // one field for instance |
| assertEquals(1, lambdaInnerClass.getFields().size()); |
| |
| // should extends test.Lambda |
| assertTrue(lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.Lambda"))); |
| |
| // should implement run method and invoke lambda via captured instance |
| JMethod samMethod = findMethod(lambdaInnerClass, "run"); |
| assertEquals( |
| "public final Object run(int arg0,int arg1){" |
| + "return new EntryPoint$Pojo2(this.test_EntryPoint,arg0,arg1);}", |
| formatSource(samMethod.toSource())); |
| } |
| |
| public void testIntersectionCast() throws Exception { |
| addSnippetClassDecl("static class A {void print() {} }"); |
| addSnippetClassDecl("interface I1 {}"); |
| addSnippetClassDecl("interface I2 {}"); |
| addSnippetClassDecl("interface I3 {}"); |
| addSnippetClassDecl("static class B extends A implements I1 {}"); |
| addSnippetClassDecl("static class C extends A implements I1, I2, I3 {}"); |
| String cast1 = "B b = new B(); ((A & I1) b).print();"; |
| assertEqualBlock("EntryPoint$B b=new EntryPoint$B();((EntryPoint$A)(EntryPoint$I1)b).print();", |
| cast1); |
| String cast2 = "C c = new C(); ((A & I1 & I2 & I3)c).print();"; |
| assertEqualBlock("EntryPoint$C c=new EntryPoint$C();" |
| + "((EntryPoint$A)(EntryPoint$I1)(EntryPoint$I2)(EntryPoint$I3)c).print();", cast2); |
| } |
| |
| public void testIntersectionCastOfLambda() throws Exception { |
| addSnippetClassDecl("interface I1 { public void foo(); }"); |
| addSnippetClassDecl("interface I2 { }"); |
| String lambda = "Object o = (I2 & I1) () -> {};"; |
| assertEqualBlock("Object o=(EntryPoint$I2)(EntryPoint$I1)new EntryPoint$lambda$0$Type();", |
| lambda); |
| |
| JProgram program = compileSnippet("void", lambda, false); |
| // created by JDT, should exist |
| assertNotNull(getMethod(program, "lambda$0")); |
| |
| // created by GwtAstBuilder |
| JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$lambda$0$Type"); |
| assertNotNull(lambdaInnerClass); |
| |
| // no fields |
| assertEquals(0, lambdaInnerClass.getFields().size()); |
| |
| // should have constructor taking no args |
| JMethod ctor = findMethod(lambdaInnerClass, "EntryPoint$lambda$0$Type"); |
| assertTrue(ctor instanceof JConstructor); |
| assertEquals(0, ctor.getParams().size()); |
| |
| // should implements I1 and I2 |
| assertTrue( |
| lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.EntryPoint$I1"))); |
| assertTrue( |
| lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.EntryPoint$I2"))); |
| // should implement foo method |
| JMethod samMethod = findMethod(lambdaInnerClass, "foo"); |
| assertEquals("public final void foo(){EntryPoint.lambda$0();}", |
| formatSource(samMethod.toSource())); |
| } |
| |
| public void testMultipleIntersectionCastOfLambda() throws Exception { |
| addSnippetClassDecl("interface I1 { public void foo(); }"); |
| addSnippetClassDecl("interface I2 { }"); |
| addSnippetClassDecl("interface I3 { }"); |
| String lambda = "I2 o = (I3 & I2 & I1) () -> {};"; |
| assertEqualBlock( |
| "EntryPoint$I2 o=(EntryPoint$I3)(EntryPoint$I2)(EntryPoint$I1)new EntryPoint$lambda$0$Type();", |
| lambda); |
| |
| JProgram program = compileSnippet("void", lambda, false); |
| // created by JDT, should exist |
| assertNotNull(getMethod(program, "lambda$0")); |
| |
| // created by GwtAstBuilder |
| JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$lambda$0$Type"); |
| assertNotNull(lambdaInnerClass); |
| |
| // no fields |
| assertEquals(0, lambdaInnerClass.getFields().size()); |
| |
| // should have constructor taking no args |
| JMethod ctor = findMethod(lambdaInnerClass, "EntryPoint$lambda$0$Type"); |
| assertTrue(ctor instanceof JConstructor); |
| assertEquals(0, ctor.getParams().size()); |
| |
| // should extends java.lang.Object, implements I1, I2 and I3 |
| assertEquals("java.lang.Object", lambdaInnerClass.getSuperClass().getName()); |
| assertTrue( |
| lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.EntryPoint$I1"))); |
| assertTrue( |
| lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.EntryPoint$I2"))); |
| assertTrue( |
| lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.EntryPoint$I3"))); |
| // should implement foo method |
| JMethod samMethod = findMethod(lambdaInnerClass, "foo"); |
| assertEquals("public final void foo(){EntryPoint.lambda$0();}", |
| formatSource(samMethod.toSource())); |
| } |
| |
| public void testIntersectionCastOfLambdaOneAbstractMethod() throws Exception { |
| addSnippetClassDecl("interface I1 { public void foo(); }"); |
| addSnippetClassDecl("interface I2 extends I1{ public void foo();}"); |
| String lambda = "Object o = (I1 & I2) () -> {};"; |
| // (I1 & I2) is resolved to I2 by JDT. |
| assertEqualBlock("Object o=(EntryPoint$I2)new EntryPoint$lambda$0$Type();", |
| lambda); |
| |
| JProgram program = compileSnippet("void", lambda, false); |
| |
| assertNotNull(getMethod(program, "lambda$0")); |
| |
| JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$lambda$0$Type"); |
| assertNotNull(lambdaInnerClass); |
| assertEquals("java.lang.Object", lambdaInnerClass.getSuperClass().getName()); |
| assertEquals(1, lambdaInnerClass.getImplements().size()); // only implements I2. |
| assertTrue( |
| lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.EntryPoint$I2"))); |
| // should implement foo method |
| JMethod samMethod = findMethod(lambdaInnerClass, "foo"); |
| assertEquals("public final void foo(){EntryPoint.lambda$0();}", |
| formatSource(samMethod.toSource())); |
| } |
| |
| public void testIntersectionCastMultipleAbstractMethods() throws Exception { |
| addSnippetClassDecl("interface I1 { public void foo(); }"); |
| addSnippetClassDecl("interface I2 { public void foo(); }"); |
| String lambda = "Object o = (I1 & I2) () -> {};"; |
| assertEqualBlock("Object o=(EntryPoint$I1)(EntryPoint$I2)new EntryPoint$lambda$0$Type();", |
| lambda); |
| |
| JProgram program = compileSnippet("void", lambda, false); |
| |
| assertNotNull(getMethod(program, "lambda$0")); |
| |
| JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$lambda$0$Type"); |
| assertNotNull(lambdaInnerClass); |
| assertEquals("java.lang.Object", lambdaInnerClass.getSuperClass().getName()); |
| assertEquals(2, lambdaInnerClass.getImplements().size()); |
| assertTrue( |
| lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.EntryPoint$I1"))); |
| // should implement foo method |
| JMethod samMethod = findMethod(lambdaInnerClass, "foo"); |
| assertEquals("public final void foo(){EntryPoint.lambda$0();}", |
| formatSource(samMethod.toSource())); |
| } |
| |
| private static final MockJavaResource LAMBDA_METAFACTORY = |
| JavaResourceBase.createMockJavaResource("java.lang.invoke.LambdaMetafactory", |
| "package java.lang.invoke;", |
| "public class LambdaMetafactory {", |
| "}"); |
| |
| public void testDefaultInterfaceMethod() throws Exception { |
| JProgram program = compileSnippet("void", "(new DefaultInterfaceImpl()).method2();", false); |
| |
| // created by GwtAstBuilder |
| JInterfaceType intf = (JInterfaceType) getType(program, "test.DefaultInterface"); |
| // should have an actual method with body on it |
| JMethod defaultMethod = findMethod(intf, "method2"); |
| assertNotNull(defaultMethod); |
| assertNotNull(defaultMethod.getBody()); |
| assertEquals(1, ((JMethodBody) defaultMethod.getBody()).getBlock().getStatements().size()); |
| } |
| |
| public void testDefaultInterfaceMethodSuperResolution() throws Exception { |
| JProgram program = compileSnippet("void", "new DefaultInterfaceImpl2();", false); |
| // created by GwtAstBuilder |
| JClassType clazz = (JClassType) getType(program, "test.DefaultInterfaceImpl2"); |
| JMethod defaultMethod = findMethod(clazz, "method2"); |
| assertNotNull(defaultMethod); |
| assertNotNull(defaultMethod.getBody()); |
| assertEquals("{return this.DefaultInterface.method2();}", |
| formatSource(defaultMethod.getBody().toSource())); |
| } |
| |
| /** |
| * Regression test for issue 9190. |
| */ |
| public void testMethodHandlerLambdaFromDifferentCompilationUnits() throws Exception { |
| MockJavaResource interfaceF = |
| JavaResourceBase.createMockJavaResource("test.F", |
| "package test;", |
| "@FunctionalInterface", |
| "public interface F {", |
| " boolean eval();", |
| "}"); |
| MockJavaResource classOne = |
| JavaResourceBase.createMockJavaResource("test.ClassOne", |
| "package test;", |
| "public class ClassOne {", |
| " public boolean m() { return true; }", |
| " public static boolean evaluateF(F f) { return f.eval(); }", |
| " public boolean evaluateM() {", |
| " ClassOne a = new ClassOne();", |
| " return evaluateF(this::m);", |
| " }", |
| "}"); |
| |
| String entryPointClass = Joiner.on('\n').join( |
| "package test;", |
| "public class EntryPoint {", |
| " public static void onModuleLoad() {", |
| " ClassOne classOne = new ClassOne();", |
| " ClassOne.evaluateF(classOne::m);", |
| " new ClassOne().evaluateM();", |
| " }", |
| "}"); |
| |
| addAll(interfaceF, classOne); |
| |
| // Compiling the snippet to Javascript makes sure JavaScript generation does not trigger the |
| // assertion in {@link GenerateJavaScriptAST.CreateNamesAndScopesVisitor#recordSymbol}. |
| // |
| // Also just performing the compile asserts that the AST is well formed. |
| compileSnippetToJS(entryPointClass); |
| } |
| |
| @Override |
| protected void optimizeJava() { |
| } |
| } |