Version 1.6.69

Cherry-pick: Remove $deserializeLambda$ from all classes.
CL: https://r8-review.googlesource.com/c/r8/+/48301/
Bug: 148734239
Change-Id: I971506caa4998089b9ea3b081620890a2965e23b
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 22bf5a7..5512001 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 = "1.6.68";
+  public static final String LABEL = "1.6.69";
 
   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 30c22b4..b3b92e8 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
@@ -202,6 +202,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();
@@ -214,14 +215,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..7b59252 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,80 @@
     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 {
+    if (parameters.isCfRuntime()) {
+      testForJvm()
+          .addProgramClasses(getClasses())
+          .run(parameters.getRuntime(), getMainClass())
+          .assertSuccessWithOutput(EXPECTED)
+          .inspect(this::checkPresenceOfDeserializedLambdas);
+    } else {
+      testForD8()
+          .setMinApi(parameters.getApiLevel())
+          .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());
+    }
   }
 }