Skip to content

Commit

Permalink
Add ast and compilation tests for lambdas in this()/super() calls
Browse files Browse the repository at this point in the history
  • Loading branch information
niloc132 committed Jan 8, 2025
1 parent 6398bf4 commit ed59ac4
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 0 deletions.
82 changes: 82 additions & 0 deletions dev/core/test/com/google/gwt/dev/jjs/impl/Java8AstTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,88 @@ public void testCompileLambdaCaptureLocalAndField() throws Exception {
formatSource(samMethod.toSource()));
}

public void testCompileLambdaOuterFieldCaptureInConstructor() throws Exception {
addSnippetClassDecl("int y = 22;");
addSnippetClassDecl("class Foo {" +
"Foo(Runnable r) {}" +
"Foo() {" +
"this(() -> {y = 42;});" +
"}" +
"}");

JProgram program = compileSnippet("void", "new Foo();", false);
JDeclaredType entrypointType = program.getFromTypeMap("test.EntryPoint");

JMethod lambda = findMethod(program.getFromTypeMap("test.EntryPoint$Foo"), "lambda$0");
assertNotNull(lambda);
assertEquals("{{this$0.y=42;}}", formatSource(lambda.getBody().toSource()));

// Lambda closes over inner class's this$0 reference - lambda type constructor takes that
// parameter and assigns to a field.
JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$Foo$lambda$0$Type");
assertNotNull(lambdaInnerClass);

JMethod ctor = findMethod(lambdaInnerClass, "EntryPoint$Foo$lambda$0$Type");
assertTrue(ctor instanceof JConstructor);
System.out.println(ctor.getOriginalParamTypes());
assertEquals(1, ctor.getParams().size());
assertEquals(entrypointType, ctor.getOriginalParamTypes().get(0));

assertEquals(1, lambdaInnerClass.getFields().size());
assertEquals(entrypointType, lambdaInnerClass.getFields().get(0).getType());

// interface impl passes this.this$0 to the actual lambda method as a parameter
assertEquals("{this.this$0=this$0;}", formatSource(ctor.getBody().toSource()));

// abstract method implementation passes field to lambda method
JMethod samMethod = findMethod(lambdaInnerClass, "run");
assertNotNull(samMethod);
assertEquals("{EntryPoint$Foo.lambda$0(this.this$0);}", formatSource(samMethod.getBody().toSource()));
}

public void testCompileLambdaOuterFieldCaptureInConstructorSuper() throws Exception {
addSnippetClassDecl("int y = 22;");
addSnippetClassDecl("class Foo {" +
"Foo(Runnable r) {}" +
"}" +
"class Bar extends Foo {" +
"Bar() {" +
"super(() -> {y = 42;});" +
"}" +
"}");

JProgram program = compileSnippet("void", "new Bar();", false);
JDeclaredType entrypointType = program.getFromTypeMap("test.EntryPoint");

JMethod lambda = findMethod(program.getFromTypeMap("test.EntryPoint$Bar"), "lambda$0");
assertNotNull(lambda);
assertEquals(1, lambda.getParams().size());
assertEquals(entrypointType, lambda.getOriginalParamTypes().get(0));
assertEquals("{{this$0.y=42;}}", formatSource(lambda.getBody().toSource()));

// Lambda closes over inner class's this$0 reference - lambda type constructor takes that
// parameter and assigns to a field.
JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$Bar$lambda$0$Type");
assertNotNull(lambdaInnerClass);

JMethod ctor = findMethod(lambdaInnerClass, "EntryPoint$Bar$lambda$0$Type");
assertTrue(ctor instanceof JConstructor);
System.out.println(ctor.getOriginalParamTypes());
assertEquals(1, ctor.getParams().size());
assertEquals(entrypointType, ctor.getOriginalParamTypes().get(0));

assertEquals(1, lambdaInnerClass.getFields().size());
assertEquals(entrypointType, lambdaInnerClass.getFields().get(0).getType());

// interface impl passes this.this$0 to the actual lambda method as a parameter
assertEquals("{this.this$0=this$0;}", formatSource(ctor.getBody().toSource()));

// abstract method implementation passes field to lambda method
JMethod samMethod = findMethod(lambdaInnerClass, "run");
assertNotNull(samMethod);
assertEquals("{EntryPoint$Bar.lambda$0(this.this$0);}", formatSource(samMethod.getBody().toSource()));
}

// make sure nested scoping of identically named variables works
public void testCompileLambdaCaptureOuterInnerField() throws Exception {
addSnippetClassDecl("private int y = 22;");
Expand Down
33 changes: 33 additions & 0 deletions user/test/com/google/gwt/dev/jjs/test/Java8Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,39 @@ public void testLambdaCaptureLocalAndFieldWithInnerClass() {
assertEquals(82, new AcceptsLambda<Integer>().accept(l).intValue());
}

class CtorAcceptsLambda {
CtorAcceptsLambda() {
this(() -> local = -1);
}
CtorAcceptsLambda(Runnable lambda) {
lambda.run();
}
}

public void testCompileLambdaOuterFieldCaptureInConstructor() {
assertEquals(42, local);
new CtorAcceptsLambda();
assertEquals(-1, local);
}

abstract class AbstractCtorAcceptsLambda {
AbstractCtorAcceptsLambda(Runnable lambda) {
lambda.run();
}
}

class CtorAcceptsLambdaSubtype extends AbstractCtorAcceptsLambda {
CtorAcceptsLambdaSubtype() {
super(() -> local = -1);
}
}

public void testCompileLambdaOuterFieldCaptureInConstructorSuper() throws Exception {
assertEquals(42, local);
new CtorAcceptsLambdaSubtype();
assertEquals(-1, local);
}

public void testCompileLambdaCaptureOuterInnerField() throws Exception {
new Inner().run();
}
Expand Down

0 comments on commit ed59ac4

Please sign in to comment.