Version 2.0.27
Cherry-pick: Remove $deserializeLambda$ from all classes.
CL: https://r8-review.googlesource.com/48301
Bug: 148734239
Change-Id: Iab8787cf610b0241e9ebd2a851211073da59160c
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 3ec09d4..076b8a3 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "2.0.26";
+ public static final String LABEL = "2.0.27";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 7a2a30f..17b227e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -228,6 +228,7 @@
/** Remove lambda deserialization methods. */
public boolean removeLambdaDeserializationMethods(Iterable<DexProgramClass> classes) {
+ boolean anyRemoved = false;
for (DexProgramClass clazz : classes) {
// Search for a lambda deserialization method and remove it if found.
List<DexEncodedMethod> directMethods = clazz.directMethods();
@@ -240,14 +241,15 @@
assert encoded.accessFlags.isStatic();
assert encoded.accessFlags.isSynthetic();
clazz.removeDirectMethod(i);
+ anyRemoved = true;
// We assume there is only one such method in the class.
- return true;
+ break;
}
}
}
}
- return false;
+ return anyRemoved;
}
/** Adjust accessibility of referenced application symbols or creates necessary accessors. */
diff --git a/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTest.java b/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTest.java
index 4dc81f4..1415b49 100644
--- a/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTest.java
+++ b/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTest.java
@@ -6,6 +6,7 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
@@ -20,22 +21,30 @@
Method runMethod = o.getClass().getMethod("run");
runMethod.invoke(o);
}
+
+ static Serializable getLambda() {
+ return (Runnable & Serializable) () -> System.out.println("base lambda");
+ }
}
class KeepDeserializeLambdaMethodTestDex extends KeepDeserializeLambdaMethodTest {
public static void main(String[] args) throws Exception {
- Serializable myLambda =
+ invokeLambda(getLambda());
+ invokeLambda(
(Runnable & Serializable)
- () -> System.out.println(KeepDeserializeLambdaMethodTest.LAMBDA_MESSAGE);
- invokeLambda(myLambda);
+ () -> System.out.println(KeepDeserializeLambdaMethodTest.LAMBDA_MESSAGE));
}
}
class KeepDeserializeLambdaMethodTestCf extends KeepDeserializeLambdaMethodTest {
public static void main(String[] args) throws Exception {
- Serializable myLambda = (Runnable & Serializable) () -> System.out.println(LAMBDA_MESSAGE);
+ invokeLambda(roundtrip(getLambda()));
+ invokeLambda(roundtrip((Runnable & Serializable) () -> System.out.println(LAMBDA_MESSAGE)));
+ }
+ private static Object roundtrip(Serializable myLambda)
+ throws IOException, ClassNotFoundException {
byte[] bytes;
{
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
@@ -51,10 +60,6 @@
o = in.readObject();
in.close();
}
- String name = o.getClass().getName();
- if (!name.contains("KeepDeserializeLambdaMethodTestCf")) {
- throw new RuntimeException("Unexpected class name " + name);
- }
- invokeLambda(o);
+ return o;
}
}
diff --git a/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java b/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java
index 9c5d05d..6cda55c 100644
--- a/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java
@@ -4,19 +4,19 @@
package com.android.tools.r8.cf;
-import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.R8CompatTestBuilder;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
import java.io.IOException;
+import java.util.List;
import java.util.concurrent.ExecutionException;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,9 +31,12 @@
private static final Class TEST_CLASS_DEX =
com.android.tools.r8.cf.KeepDeserializeLambdaMethodTestDex.class;
+ private static final String EXPECTED =
+ StringUtils.lines("base lambda", KeepDeserializeLambdaMethodTest.LAMBDA_MESSAGE);
+
@Parameters(name = "{0}")
public static TestParametersCollection params() {
- return getTestParameters().withCfRuntimes().withDexRuntime(Version.last()).build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -42,48 +45,71 @@
this.parameters = parameters;
}
+ private Class<?> getMainClass() {
+ return parameters.isCfRuntime() ? TEST_CLASS_CF : TEST_CLASS_DEX;
+ }
+
+ private List<Class<?>> getClasses() {
+ return ImmutableList.of(KeepDeserializeLambdaMethodTest.class, getMainClass());
+ }
+
@Test
- public void testNoTreeShakingCf() throws Exception {
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(getClasses())
+ .run(parameters.getRuntime(), getMainClass())
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkPresenceOfDeserializedLambdas);
+ }
+
+ @Test
+ public void testDontKeepDeserializeLambdaR8() throws Exception {
test(false);
}
@Test
- public void testKeepRuleCf() throws Exception {
- // Only run for CF runtimes, since the keep rule does not match anything when compiling for the
- // DEX.
- assumeTrue(parameters.isCfRuntime());
+ public void testKeepDeserializedLambdaR8() throws Exception {
test(true);
}
- private void test(boolean keepRule)
+ private void test(boolean addKeepDeserializedLambdaRule)
throws IOException, CompilationFailedException, ExecutionException {
- Class testClass = parameters.isCfRuntime() ? TEST_CLASS_CF : TEST_CLASS_DEX;
R8CompatTestBuilder builder =
testForR8Compat(parameters.getBackend())
- .addProgramClasses(
- com.android.tools.r8.cf.KeepDeserializeLambdaMethodTest.class, testClass)
- .setMinApi(parameters.getRuntime())
- .addKeepMainRule(testClass)
- .noMinification();
- if (keepRule) {
+ .addProgramClasses(getClasses())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(getMainClass());
+ if (addKeepDeserializedLambdaRule) {
+ if (parameters.isDexRuntime()) {
+ builder.allowUnusedProguardConfigurationRules();
+ }
builder.addKeepRules(
"-keepclassmembers class * {",
"private static synthetic java.lang.Object "
+ "$deserializeLambda$(java.lang.invoke.SerializedLambda);",
- "}");
+ "}",
+ // TODO(b/148836254): Support deserialized lambdas without the need of additional rules.
+ "-keep class * { private static synthetic void lambda$*(); }");
+ // TODO(b/148831667): The above rule should pin the names but does not.
+ // Remove the noMinification call once the above rule takes effect.
+ builder.noMinification();
} else {
+ builder.noMinification();
builder.noTreeShaking();
}
- R8TestRunResult result =
- builder
- .run(parameters.getRuntime(), testClass)
- .assertSuccessWithOutputThatMatches(
- containsString(KeepDeserializeLambdaMethodTest.LAMBDA_MESSAGE))
- .inspect(
- inspector -> {
- MethodSubject method =
- inspector.clazz(testClass).uniqueMethodWithName("$deserializeLambda$");
- assertEquals(parameters.isCfRuntime(), method.isPresent());
- });
+ builder
+ .run(parameters.getRuntime(), getMainClass())
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkPresenceOfDeserializedLambdas);
+ }
+
+ private void checkPresenceOfDeserializedLambdas(CodeInspector inspector) {
+ for (Class<?> clazz : getClasses()) {
+ MethodSubject method = inspector.clazz(clazz).uniqueMethodWithName("$deserializeLambda$");
+ assertEquals(
+ "Unexpected status for $deserializedLambda$ on " + clazz.getSimpleName(),
+ parameters.isCfRuntime(),
+ method.isPresent());
+ }
}
}