Removing lambda deserialization methods in desugaring.

Lambda deserialization methods generated by RI compiler
are not used in desugared code. Removing them.

Bug:62168701

BUG=

Change-Id: I8056cbc4e5b0f761dfa3fa0db3e93629046c6b22
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 22ce470..7aef78c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -155,22 +155,37 @@
     }
   }
 
+  private void removeLambdaDeserializationMethods() {
+    if (lambdaRewriter != null) {
+      lambdaRewriter.removeLambdaDeserializationMethods(application.classes());
+    }
+  }
+
+  private void synthesizeLambdaClasses(Builder builder) {
+    if (lambdaRewriter != null) {
+      lambdaRewriter.adjustAccessibility(builder);
+      lambdaRewriter.synthesizeLambdaClasses(builder);
+    }
+  }
+
+  private void desugarInterfaceMethods(
+      Builder builder, InterfaceMethodRewriter.Flavor includeAllResources) {
+    if (interfaceMethodRewriter != null) {
+      interfaceMethodRewriter.desugarInterfaceMethods(builder, includeAllResources);
+    }
+  }
+
   public DexApplication convertToDex() {
+    removeLambdaDeserializationMethods();
+
     convertClassesToDex(application.classes());
 
     // Build a new application with jumbo string info,
     Builder builder = new Builder(application);
     builder.setHighestSortingString(highestSortingString);
 
-    // Lambda rewriter
-    if (lambdaRewriter != null) {
-      lambdaRewriter.adjustAccessibility(builder);
-      lambdaRewriter.synthesizeLambdaClasses(builder);
-    }
-
-    if (interfaceMethodRewriter != null) {
-      interfaceMethodRewriter.desugarInterfaceMethods(builder, ExcludeDexResources);
-    }
+    synthesizeLambdaClasses(builder);
+    desugarInterfaceMethods(builder, ExcludeDexResources);
 
     return builder.build();
   }
@@ -207,6 +222,8 @@
   }
 
   public DexApplication optimize(ExecutorService executorService) throws ExecutionException {
+    removeLambdaDeserializationMethods();
+
     timing.begin("Build call graph");
     callGraph = CallGraph.build(application, appInfo.withSubtyping(), graphLense);
     timing.end();
@@ -265,15 +282,8 @@
       }
     }
 
-    // Lambda rewriter.
-    if (lambdaRewriter != null) {
-      lambdaRewriter.adjustAccessibility(builder);
-      lambdaRewriter.synthesizeLambdaClasses(builder);
-    }
-
-    if (interfaceMethodRewriter != null) {
-      interfaceMethodRewriter.desugarInterfaceMethods(builder, IncludeAllResources);
-    }
+    synthesizeLambdaClasses(builder);
+    desugarInterfaceMethods(builder, IncludeAllResources);
 
     if (outliner != null) {
       timing.begin("IR conversion phase 2");
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 5644096..87241cf 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
@@ -46,9 +46,11 @@
   private static final String METHODHANDLE_TYPE_DESCR = "Ljava/lang/invoke/MethodHandle;";
   private static final String OBJECT_ARRAY_TYPE_DESCR = "[Ljava/lang/Object;";
   private static final String SERIALIZABLE_TYPE_DESCR = "Ljava/io/Serializable;";
+  private static final String SERIALIZED_LAMBDA_TYPE_DESCR = "Ljava/lang/invoke/SerializedLambda;";
 
   private static final String METAFACTORY_METHOD_NAME = "metafactory";
   private static final String METAFACTORY_ALT_METHOD_NAME = "altMetafactory";
+  private static final String DESERIALIZE_LAMBDA_METHOD_NAME = "$deserializeLambda$";
 
   static final String LAMBDA_CLASS_NAME_PREFIX = "-$$Lambda$";
   static final String EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$";
@@ -68,6 +70,9 @@
   final DexString classConstructorName;
   final DexString instanceFieldName;
 
+  final DexString deserializeLambdaMethodName;
+  final DexProto deserializeLambdaMethodProto;
+
   // Maps call sites seen so far to inferred lambda descriptor. It is intended
   // to help avoid re-matching call sites we already seen. Note that same call
   // site may match one or several lambda classes.
@@ -116,6 +121,10 @@
     this.classConstructorName = factory.createString(Constants.CLASS_INITIALIZER_NAME);
     this.instanceFieldName = factory.createString(LAMBDA_INSTANCE_FIELD_NAME);
     this.serializableType = factory.createType(SERIALIZABLE_TYPE_DESCR);
+
+    this.deserializeLambdaMethodName = factory.createString(DESERIALIZE_LAMBDA_METHOD_NAME);
+    this.deserializeLambdaMethodProto = factory.createProto(
+        factory.objectType, new DexType[] { factory.createType(SERIALIZED_LAMBDA_TYPE_DESCR) });
   }
 
   /**
@@ -151,6 +160,35 @@
     }
   }
 
+  /** Remove lambda deserialization methods. */
+  public void removeLambdaDeserializationMethods(Iterable<DexProgramClass> classes) {
+    for (DexProgramClass clazz : classes) {
+      // Search for a lambda deserialization method and remove it if found.
+      DexEncodedMethod[] directMethods = clazz.directMethods;
+      if (directMethods != null) {
+        int methodCount = directMethods.length;
+        for (int i = 0; i < methodCount; i++) {
+          DexEncodedMethod encoded = directMethods[i];
+          DexMethod method = encoded.method;
+          if (method.name == deserializeLambdaMethodName &&
+              method.proto == deserializeLambdaMethodProto) {
+            assert encoded.accessFlags.isStatic();
+            assert encoded.accessFlags.isPrivate();
+            assert encoded.accessFlags.isSynthetic();
+
+            DexEncodedMethod[] newMethods = new DexEncodedMethod[methodCount - 1];
+            System.arraycopy(directMethods, 0, newMethods, 0, i);
+            System.arraycopy(directMethods, i + 1, newMethods, i, methodCount - i - 1);
+            clazz.directMethods = newMethods;
+
+            // We assume there is only one such method in the class.
+            break;
+          }
+        }
+      }
+    }
+  }
+
   /**
    * Adjust accessibility of referenced application symbols or
    * creates necessary accessors.
diff --git a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
index d2e738d..8a1119a 100644
--- a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
+++ b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package lambdadesugaringnplus;
 
+import java.io.Serializable;
 import java.lang.annotation.Annotation;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -296,6 +297,23 @@
     }
   }
 
+  static class B62168701 {
+    interface I extends Serializable {
+      String getValue();
+    }
+
+    interface J {
+      static void dump() {
+        I i = () -> "B62168701 -- OK";
+        System.out.println(i.getValue());
+      }
+    }
+
+    static void test() {
+      J.dump();
+    }
+  }
+
   static void z(Z p) {
     System.out.println(p.foo(null));
   }
@@ -401,5 +419,6 @@
     B38306708.test();
     B38308515.test();
     B38302860.test();
+    B62168701.test();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index 978f9de..a5e944a 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -5,6 +5,8 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.errors.Unimplemented;
 import java.io.File;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -86,8 +88,10 @@
       D8Command command = builder.setOutputPath(out).build();
       try {
         ToolHelper.runD8(command, this::combinedOptionConsumer);
+      } catch (Unimplemented | CompilationError | InternalCompilerError re) {
+        throw re;
       } catch (RuntimeException re) {
-        throw re instanceof CompilationError ? re : re.getCause();
+        throw re.getCause() == null ? re : re.getCause();
       }
     }
   }
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
index ea94040..6f51624 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -5,6 +5,8 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.errors.Unimplemented;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.function.UnaryOperator;
@@ -32,8 +34,10 @@
       D8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
       try {
         ToolHelper.runD8(command, this::combinedOptionConsumer);
+      } catch (Unimplemented | CompilationError | InternalCompilerError re) {
+        throw re;
       } catch (RuntimeException re) {
-        throw re instanceof CompilationError ? re : re.getCause();
+        throw re.getCause() == null ? re : re.getCause();
       }
     }
   }