Capture enclosing instance of a lambda
- ECj can tell to Jack to not capture the enclosing instance of a lambda
when it is needed. Thus, we force to capture the enclosing instance of a
lambda when a creation of an object need this enclosing instance as parameter
of its constructor
Bug: 27330340
Bug: 27301904
Change-Id: I137e9395b1f0a30562474d79338d84b8b94b24bf
diff --git a/jack-tests/tests/com/android/jack/java8/LambdaTest.java b/jack-tests/tests/com/android/jack/java8/LambdaTest.java
index 2700d71..c7fa480 100644
--- a/jack-tests/tests/com/android/jack/java8/LambdaTest.java
+++ b/jack-tests/tests/com/android/jack/java8/LambdaTest.java
@@ -192,6 +192,10 @@
AbstractTestTools.getTestRootDir("com.android.jack.java8.lambda.test035"),
"com.android.jack.java8.lambda.test035.jack.Tests");
+ private RuntimeTestInfo LAMBDA036 = new RuntimeTestInfo(
+ AbstractTestTools.getTestRootDir("com.android.jack.java8.lambda.test036"),
+ "com.android.jack.java8.lambda.test036.jack.Tests");
+
@Test
public void testLamba001() throws Exception {
run(LAMBDA001);
@@ -405,6 +409,11 @@
run(LAMBDA035);
}
+ @Test
+ public void testLamba036() throws Exception {
+ run(LAMBDA036);
+ }
+
private void run(@Nonnull RuntimeTestInfo rti) throws Exception {
new RuntimeTestHelper(rti)
.setSourceLevel(SourceLevel.JAVA_8)
diff --git a/jack-tests/tests/com/android/jack/java8/lambda/test036/jack/Tests.java b/jack-tests/tests/com/android/jack/java8/lambda/test036/jack/Tests.java
new file mode 100644
index 0000000..fb44e51
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/java8/lambda/test036/jack/Tests.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * 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.android.jack.java8.lambda.test036.jack;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+interface I {
+ int getLength();
+}
+
+/**
+ * Test that trigger a bug where enclosing instance of a lambda expression is not captured.
+ */
+public class Tests {
+
+ @Test
+ public void test() {
+ class A<T> {
+ List<T> l;
+
+ public A(List<T> l) {
+ this.l = l;
+ }
+ }
+
+ List<Integer> list = new ArrayList<>();
+ I i = () -> new A<>(list).l.size();
+ Assert.assertEquals(0, i.getLength());
+ }
+}
diff --git a/jack/src/com/android/jack/ir/impl/JackIrBuilder.java b/jack/src/com/android/jack/ir/impl/JackIrBuilder.java
index 44e1d9f..b269071 100644
--- a/jack/src/com/android/jack/ir/impl/JackIrBuilder.java
+++ b/jack/src/com/android/jack/ir/impl/JackIrBuilder.java
@@ -1726,6 +1726,8 @@
}
}
+ boolean shouldCaptureInstance =
+ lambdaExpression.shouldCaptureInstance | curMethod.forceCaptureOfThis;
// Do not move before generateImplicitReturn since the method need the method where return
// must be added
popMethodInfo();
@@ -1735,7 +1737,7 @@
getJMethodIdWithReturnType(lambdaExpression.descriptor.original()),
lambdaMethodInfo.method,
(JDefinedInterface) getTypeMap().get(getLambdaType(lambdaExpression, blockScope)),
- lambdaExpression.shouldCaptureInstance, getInterfaceBounds(lambdaExpression, blockScope));
+ shouldCaptureInstance, getInterfaceBounds(lambdaExpression, blockScope));
lambda.addBridgeMethodIds(getBridges(lambdaExpression));
// Capture all local variable that are not already moved into fields
@@ -3433,6 +3435,7 @@
JMultiExpression multiExpr = new JMultiExpression(info, exprs);
call.addArg(multiExpr);
} else {
+ curMethod.forceCaptureOfThis = true;
// check supplementary error
getEmulationPath(scope, argType, false /* onlyExactMatch */,
true /* denyEnclosingArgInConstructorCall */, x);
@@ -3695,6 +3698,7 @@
public final MethodScope scope;
@Nonnull
private final AstVisitor ast;
+ private boolean forceCaptureOfThis;
public MethodInfo(@Nonnull AstVisitor ast, @Nonnull JMethod method,
@CheckForNull JMethodBody methodBody, @CheckForNull MethodScope methodScope) {