Skip to content

Commit

Permalink
Correctly produce capturing lambdas (fixes #717)
Browse files Browse the repository at this point in the history
	Change on 2016/03/11 by lukhnos <lukhnos@google.com>
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=116970555
  • Loading branch information
lukhnos authored and tomball committed Mar 11, 2016
1 parent 8997de4 commit 1ba3ebf
Show file tree
Hide file tree
Showing 6 changed files with 393 additions and 33 deletions.
Expand Up @@ -50,7 +50,6 @@ default String getDefaultTag() {
interface B extends A {
boolean f();

// TODO: This should return a capturing lambda to capture the implicit this/self.
default B not() {
return () -> !f();
}
Expand Down Expand Up @@ -114,13 +113,12 @@ public void testLambdaWithDefaultMethods() {
B b1 = () -> true;
assertTrue(b1.f());
assertFalse(b1.notF());
// TODO: Enable this assertion once the non-capturing lambda bug is fixed.
// assertFalse(b1.not().f());
assertFalse(b1.not().f());

B b2 = () -> false;
assertFalse(b2.f());
assertTrue(b2.notF());
// assertTrue(b2.not().f());
assertTrue(b2.not().f());

D d = (name) -> D.getDPrefix() + name;
assertEquals("static-default", d.getDefaultTag());
Expand Down
274 changes: 274 additions & 0 deletions jre_emul/Tests/com/google/j2objc/java8/LambdaTest.java
Expand Up @@ -234,4 +234,278 @@ public void testBasicCallable() throws Exception {
Callable c = () -> 42;
assertEquals(42, c.call());
}

// Adapted from translator/src/test/java/com/google/devtools/j2objc/ast/LambdaExpressionTest.java
interface NumberFunction<T extends Number, R> {
R apply(T t);
}

static class CaptureTest1 {
int y;
NumberFunction f = x -> y + x.intValue();
}

static class CaptureTest2 {
int y;
class InnerCaptureTest {
int y;
NumberFunction fOuter = x -> CaptureTest2.this.y + x.intValue();
NumberFunction fInner = x -> y + x.intValue();
}
}

public void testImplicitCapture() throws Exception {
CaptureTest1 t1 = new CaptureTest1();
assertEquals(1, t1.f.apply(1));
t1.y = 1;
assertEquals(3, t1.f.apply(2));

CaptureTest2 t2 = new CaptureTest2();
CaptureTest2.InnerCaptureTest t2i = t2.new InnerCaptureTest();
t2.y = 10;
t2i.y = 20;
assertEquals(9, t2i.fOuter.apply(-1));
assertEquals(19, t2i.fInner.apply(-1));
}

interface IntSupplier {
int get();

static int f(IntSupplier s) {
return s.get();
}
}

// x + y * 2
private int getFromSimpleLambda(int x, int y) {
return IntSupplier.f(() -> x + y) + y;
}

// x + y * 3
private int getFromNestedLambdas(int x, int y) {
return IntSupplier.f(() -> IntSupplier.f(() -> x + y) + y) + y;
}

// x + y * 3
private int getFromAnonymousClassesAndLambda(int x, int y) {
return IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return IntSupplier.f(() -> x + y) + y;
}
}) + y;
}

// x + y * 3
private int getFromLambdaAndAnonymousClass(int x, int y) {
return IntSupplier.f(() -> IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return x + y;
}
}) + y) + y;
}

// x + y * 5
private int getFromNestedAnonymousClassesAndLambdas(int x, int y) {
return IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return IntSupplier.f(() -> IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return IntSupplier.f(() -> x + y) + y;
}
}) + y) + y;
}
}) + y;
}

// x + y * 5
private int getFromNestedLambdasAndAnonymousClasses(int x, int y) {
return IntSupplier.f(() -> IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return IntSupplier.f(() -> IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return x + y;
}
}) + y) + y;
}
}) + y) + y;
}

// Putting everything together
static class ComplexLambda {
static final int P = 1;
final int q = 2;
int r = Integer.MIN_VALUE;

public class Inner {
static final int S = 4;
final int t = 5;
int u = Integer.MIN_VALUE;

// Should be x + y * 6 + P + Q + R + S + T + U;
public int get(int x, int y) {
return IntSupplier.f(() -> IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return IntSupplier.f(() -> IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return IntSupplier.f(() -> x + y + P) + y + q;
}
}) + y + r) + y + S;
}
}) + y + t) + y + u;
}
}
}

public void testNestingLambdasAndAnonymousClasses() throws Exception {
// Each method is called twice to make sure that all lambdas are capturing ones.
assertEquals(102, getFromSimpleLambda(100, 1));
assertEquals(104, getFromSimpleLambda(100, 2));
assertEquals(109, getFromNestedLambdas(100, 3));
assertEquals(112, getFromNestedLambdas(100, 4));
assertEquals(115, getFromAnonymousClassesAndLambda(100, 5));
assertEquals(118, getFromAnonymousClassesAndLambda(100, 6));
assertEquals(121, getFromLambdaAndAnonymousClass(100, 7));
assertEquals(124, getFromLambdaAndAnonymousClass(100, 8));
assertEquals(145, getFromNestedAnonymousClassesAndLambdas(100, 9));
assertEquals(150, getFromNestedLambdasAndAnonymousClasses(100, 10));

int z = 10;
class Local {
int w = 1;

// x + y * 3 + w + z
private int get(int x, int y) {
return IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return IntSupplier.f(() -> x + y + z + w) + y;
}
}) + y;
}
}

Local l = new Local();
assertEquals(171, l.get(100, 20));
assertEquals(186, l.get(100, 25));
l.w = 500;
assertEquals(670, l.get(100, 20));
assertEquals(685, l.get(100, 25));

ComplexLambda cl = new ComplexLambda();
cl.r = 3;
ComplexLambda.Inner inner = cl.new Inner();
inner.u = 6;

// Should be 1000 + 20 * 6 + sum(1...6)
assertEquals(1141, inner.get(1000, 20));

cl.r += 10000;
inner.u += 10000;
// Should be 1000 + 20 * 6 + sum(1...6) + 10000 + 10000;
assertEquals(21141, inner.get(1000, 20));
}

interface ImplicitCaptures {
interface F {
int f();

default F g() {
return () -> f();
}
}

interface G extends F {
default F g() {
return () -> F.super.g().f();
}
}

static class P {
int x = 0;

void setX(int x) {
this.x = x;
}

int getX() {
return x;
}
}

static class Q extends P {
public F getSuperGetX() {
return () -> super.getX();
}

public F getFieldX() {
return () -> x;
}

public F getThisFieldX() {
return () -> this.x;
}

public F getThisGetX() {
return () -> getX();
}
}
}

public void testCapturingLambdasFromQualifiedSuperInvocations() throws Exception {
ImplicitCaptures.G g1 = new ImplicitCaptures.G() {

@Override
public int f() {
return 10;
}
};

ImplicitCaptures.G g2 = new ImplicitCaptures.G() {
@Override
public int f() {
return 20;
}
};

assertEquals(10, g1.g().f());

// () -> F.super.g().f() must be a non-capturing lambda to return this value correctly.
assertEquals(20, g2.g().f());
}

public void testImplicitLambdaCaptures() throws Exception {
ImplicitCaptures.Q q1 = new ImplicitCaptures.Q() {
@Override
int getX() {
return 20;
}
};

ImplicitCaptures.Q q2 = new ImplicitCaptures.Q() {
@Override
int getX() {
return 40;
}
};

q1.setX(10);
assertEquals(10, q1.getSuperGetX().f());
assertEquals(10, q1.getFieldX().f());
assertEquals(10, q1.getThisFieldX().f());
assertEquals(20, q1.getThisGetX().f()); // From q1's overridden getX()

q2.setX(30);
assertEquals(30, q2.getSuperGetX().f());
assertEquals(30, q2.getFieldX().f());
assertEquals(30, q2.getThisFieldX().f());
assertEquals(40, q2.getThisGetX().f()); // From q2's overridden getX()
}
}

0 comments on commit 1ba3ebf

Please sign in to comment.