Merge "Allow synthetic methods in the root set"
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 8ebc02f..cb038b3 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -376,7 +376,7 @@
           // We can now remove visibility bridges. Note that we do not need to update the
           // invoke-targets here, as the existing invokes will simply dispatch to the now
           // visible super-method. MemberRebinding, if run, will then dispatch it correctly.
-          application = new VisibilityBridgeRemover(appView.appInfo(), application).run();
+          new VisibilityBridgeRemover(appView.withLiveness()).run();
         }
       }
 
@@ -481,12 +481,15 @@
             new EnumOrdinalMapCollector(appViewWithLiveness, options).run());
       }
 
+      assert appView.appInfo().hasLiveness();
+
       timing.begin("Create IR");
       Set<DexCallSite> desugaredCallSites;
       CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
       try {
         IRConverter converter =
-            new IRConverter(appView, options, timing, printer, mainDexClasses, rootSet);
+            new IRConverter(
+                appView.withLiveness(), options, timing, printer, mainDexClasses, rootSet);
         application = converter.optimize(application, executorService);
         desugaredCallSites = converter.getDesugaredCallSites();
       } finally {
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index f9a9dde..e232cb9 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -106,6 +106,11 @@
     }
 
     @Override
+    public VerticallyMergedClasses verticallyMergedClasses() {
+      return AppView.this.verticallyMergedClasses();
+    }
+
+    @Override
     public AppView<AppInfoWithLiveness> withLiveness() {
       return this;
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 6b9257b..54895ac 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -267,6 +267,10 @@
   public final DexProto twrCloseResourceMethodProto =
       createProto(voidType, throwableType, autoCloseableType);
 
+  public final DexString deserializeLambdaMethodName = createString("$deserializeLambda$");
+  public final DexProto deserializeLambdaMethodProto =
+      createProto(objectType, createType("Ljava/lang/invoke/SerializedLambda;"));
+
   // Dex system annotations.
   // See https://source.android.com/devices/tech/dalvik/dex-format.html#system-annotation
   public final DexType annotationDefault = createType("Ldalvik/annotation/AnnotationDefault;");
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 3aba90c..bd86c12 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -185,6 +185,11 @@
     return builder.toString();
   }
 
+  public boolean isLambdaDeserializeMethod(DexItemFactory dexItemFactory) {
+    return name == dexItemFactory.deserializeLambdaMethodName
+        && proto == dexItemFactory.deserializeLambdaMethodProto;
+  }
+
   synchronized public void setSingleVirtualMethodCache(
       DexType receiverType, DexEncodedMethod method) {
     if (singleTargetCache == null) {
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 7065297..8571409 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
@@ -160,7 +160,7 @@
       InternalOptions options,
       Timing timing,
       CfgPrinter printer,
-      AppView<? extends AppInfoWithSubtyping> appView,
+      AppView<? extends AppInfoWithLiveness> appView,
       MainDexClasses mainDexClasses,
       RootSet rootSet) {
     assert appInfo != null;
@@ -181,7 +181,8 @@
     this.lambdaRewriter = options.enableDesugaring ? new LambdaRewriter(this) : null;
     this.interfaceMethodRewriter =
         options.isInterfaceMethodDesugaringEnabled()
-            ? new InterfaceMethodRewriter(this, options) : null;
+            ? new InterfaceMethodRewriter(appView, this, options)
+            : null;
     this.twrCloseResourceRewriter =
         (options.enableDesugaring && enableTwrCloseResourceDesugaring())
             ? new TwrCloseResourceRewriter(this) : null;
@@ -276,7 +277,7 @@
    * Create an IR converter for processing methods with full program optimization enabled.
    */
   public IRConverter(
-      AppView<AppInfoWithSubtyping> appView,
+      AppView<? extends AppInfoWithLiveness> appView,
       InternalOptions options,
       Timing timing,
       CfgPrinter printer,
@@ -317,10 +318,11 @@
     );
   }
 
-  private void removeLambdaDeserializationMethods() {
+  private boolean removeLambdaDeserializationMethods() {
     if (lambdaRewriter != null) {
-      lambdaRewriter.removeLambdaDeserializationMethods(appInfo.classes());
+      return lambdaRewriter.removeLambdaDeserializationMethods(appInfo.classes());
     }
+    return false;
   }
 
   private void synthesizeLambdaClasses(Builder<?> builder, ExecutorService executorService)
@@ -528,8 +530,13 @@
 
   public DexApplication optimize(DexApplication application, ExecutorService executorService)
       throws ExecutionException {
+    if (options.enableTreeShaking) {
+      assert !removeLambdaDeserializationMethods();
+    } else {
+      removeLambdaDeserializationMethods();
+    }
+
     computeReachabilitySensitivity(application);
-    removeLambdaDeserializationMethods();
     collectLambdaMergingCandidates(application);
     collectStaticizerCandidates(application);
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index a9a571f..e79017d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -32,6 +32,7 @@
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.desugar.DefaultMethodsHelper.Collection;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.Sets;
@@ -78,6 +79,7 @@
   public static final String DEFAULT_METHOD_PREFIX = "$default$";
   public static final String PRIVATE_METHOD_PREFIX = "$private$";
 
+  private final AppView<? extends AppInfoWithLiveness> appView;
   private final IRConverter converter;
   private final InternalOptions options;
   final DexItemFactory factory;
@@ -112,8 +114,12 @@
     ExcludeDexResources
   }
 
-  public InterfaceMethodRewriter(IRConverter converter, InternalOptions options) {
+  public InterfaceMethodRewriter(
+      AppView<? extends AppInfoWithLiveness> appView,
+      IRConverter converter,
+      InternalOptions options) {
     assert converter != null;
+    this.appView = appView;
     this.converter = converter;
     this.options = options;
     this.factory = options.itemFactory;
@@ -429,7 +435,7 @@
 
   private Map<DexType, DexProgramClass> processInterfaces(Builder<?> builder, Flavor flavour) {
     NestedGraphLense.Builder graphLensBuilder = GraphLense.builder();
-    InterfaceProcessor processor = new InterfaceProcessor(this);
+    InterfaceProcessor processor = new InterfaceProcessor(appView, this);
     for (DexProgramClass clazz : builder.getProgramClasses()) {
       if (shouldProcess(clazz, flavour, true)) {
         processor.process(clazz.asProgramClass(), graphLensBuilder);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index fb40b0a..01b647b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexAnnotationSet;
@@ -30,6 +31,7 @@
 import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
 import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -47,11 +49,16 @@
 //
 // Also moves static interface methods into a companion class.
 final class InterfaceProcessor {
+
+  private final AppView<? extends AppInfoWithLiveness> appView;
   private final InterfaceMethodRewriter rewriter;
+
   // All created companion and dispatch classes indexed by interface type.
   final Map<DexType, DexProgramClass> syntheticClasses = new IdentityHashMap<>();
 
-  InterfaceProcessor(InterfaceMethodRewriter rewriter) {
+  InterfaceProcessor(
+      AppView<? extends AppInfoWithLiveness> appView, InterfaceMethodRewriter rewriter) {
+    this.appView = appView;
     this.rewriter = rewriter;
   }
 
@@ -106,7 +113,7 @@
       }
     }
 
-    // If at least one bridge methods was removed update the table.
+    // If at least one bridge method was removed then update the table.
     if (remainingMethods.size() < iface.virtualMethods().length) {
       iface.setVirtualMethods(remainingMethods.toArray(
           new DexEncodedMethod[remainingMethods.size()]));
@@ -303,6 +310,9 @@
   // methods. Bridge methods that does not override an implementation in a super-interface must
   // also be kept (such a situation can happen if the vertical class merger merges two interfaces).
   private boolean interfaceMethodRemovalChangesApi(DexEncodedMethod method, DexClass iface) {
+    if (appView != null && appView.appInfo().isPinned(method.method)) {
+      return true;
+    }
     if (method.accessFlags.isBridge()) {
       Deque<DexType> worklist = new ArrayDeque<>();
       Set<DexType> seenBefore = new HashSet<>();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index bf36c20..b78f6df 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -517,11 +517,6 @@
       for (int i = 0; i < directMethods.length; i++) {
         DexEncodedMethod encodedMethod = directMethods[i];
         if (implMethod.match(encodedMethod)) {
-          // Check that this method is synthetic, since this implies that the method cannot be kept
-          // by an explicit -keep rule. This is necessary because we could otherwise be changing
-          // the signature of a kept method here, which is not allowed (see b/120971047).
-          assert encodedMethod.accessFlags.isSynthetic();
-
           // We need to create a new static method with the same code to be able to safely
           // relax its accessibility without making it virtual.
           MethodAccessFlags newAccessFlags = encodedMethod.accessFlags.copy();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index 6836ad3..235c96d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -152,7 +152,7 @@
   }
 
   /** If the lambda delegates to lambda$ method. */
-  boolean delegatesToLambdaImplMethod() {
+  public boolean delegatesToLambdaImplMethod() {
     DexString methodName = implHandle.asMethod().name;
     return methodName.toString().startsWith(LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX);
   }
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 707078f..df52ef1 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,6 @@
  * lambda class generation, and instruction patching.
  */
 public class LambdaRewriter {
-  private static final String SERIALIZED_LAMBDA_TYPE_DESCR = "Ljava/lang/invoke/SerializedLambda;";
-
-  private static final String DESERIALIZE_LAMBDA_METHOD_NAME = "$deserializeLambda$";
 
   // Public for testing.
   public static final String LAMBDA_CLASS_NAME_PREFIX = "-$$Lambda$";
@@ -66,9 +63,6 @@
   final DexString classConstructorName;
   final DexString instanceFieldName;
 
-  final DexString deserializeLambdaMethodName;
-  final DexProto deserializeLambdaMethodProto;
-
   final BiMap<DexMethod, DexMethod> methodMapping = HashBiMap.create();
 
   // Maps call sites seen so far to inferred lambda descriptor. It is intended
@@ -98,10 +92,6 @@
     this.objectInitMethod = factory.createMethod(factory.objectType, initProto, constructorName);
     this.classConstructorName = factory.createString(Constants.CLASS_INITIALIZER_NAME);
     this.instanceFieldName = factory.createString(LAMBDA_INSTANCE_FIELD_NAME);
-
-    this.deserializeLambdaMethodName = factory.createString(DESERIALIZE_LAMBDA_METHOD_NAME);
-    this.deserializeLambdaMethodProto = factory.createProto(
-        factory.objectType, factory.createType(SERIALIZED_LAMBDA_TYPE_DESCR));
   }
 
   /**
@@ -138,7 +128,8 @@
   }
 
   /** Remove lambda deserialization methods. */
-  public void removeLambdaDeserializationMethods(Iterable<DexProgramClass> classes) {
+  public boolean removeLambdaDeserializationMethods(Iterable<DexProgramClass> classes) {
+    boolean anyRemoved = false;
     for (DexProgramClass clazz : classes) {
       // Search for a lambda deserialization method and remove it if found.
       DexEncodedMethod[] directMethods = clazz.directMethods();
@@ -147,8 +138,7 @@
         for (int i = 0; i < methodCount; i++) {
           DexEncodedMethod encoded = directMethods[i];
           DexMethod method = encoded.method;
-          if (method.name == deserializeLambdaMethodName &&
-              method.proto == deserializeLambdaMethodProto) {
+          if (method.isLambdaDeserializeMethod(appInfo.dexItemFactory)) {
             assert encoded.accessFlags.isStatic();
             assert encoded.accessFlags.isSynthetic();
 
@@ -157,12 +147,15 @@
             System.arraycopy(directMethods, i + 1, newMethods, i, methodCount - i - 1);
             clazz.setDirectMethods(newMethods);
 
+            anyRemoved = true;
+
             // We assume there is only one such method in the class.
             break;
           }
         }
       }
     }
+    return anyRemoved;
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
index debba04..59caf25 100644
--- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -3,87 +3,89 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.optimize;
 
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.google.common.collect.Sets;
 import java.util.Arrays;
-import java.util.List;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 public class VisibilityBridgeRemover {
-  private final AppInfoWithSubtyping appInfo;
-  private final DexApplication application;
-  private final Set<DexEncodedMethod> unneededVisibilityBridges = Sets.newIdentityHashSet();
 
-  public VisibilityBridgeRemover(AppInfoWithSubtyping appInfo, DexApplication application) {
-    this.appInfo = appInfo;
-    this.application = application;
+  private final AppView<? extends AppInfoWithLiveness> appView;
+
+  public VisibilityBridgeRemover(AppView<? extends AppInfoWithLiveness> appView) {
+    this.appView = appView;
   }
 
-  private void identifyBridgeMethod(DexEncodedMethod method) {
+  private void removeUnneededVisibilityBridgesFromClass(DexProgramClass clazz) {
+    clazz.setDirectMethods(removeUnneededVisibilityBridges(clazz.directMethods()));
+    clazz.setVirtualMethods(removeUnneededVisibilityBridges(clazz.virtualMethods()));
+  }
+
+  private DexEncodedMethod[] removeUnneededVisibilityBridges(DexEncodedMethod[] methods) {
+    Set<DexEncodedMethod> methodsToBeRemoved = null;
+    for (DexEncodedMethod method : methods) {
+      if (isUnneededVisibilityBridge(method)) {
+        if (methodsToBeRemoved == null) {
+          methodsToBeRemoved = Sets.newIdentityHashSet();
+        }
+        methodsToBeRemoved.add(method);
+      }
+    }
+    if (methodsToBeRemoved != null) {
+      Set<DexEncodedMethod> finalMethodsToBeRemoved = methodsToBeRemoved;
+      return Arrays.stream(methods)
+          .filter(method -> !finalMethodsToBeRemoved.contains(method))
+          .toArray(DexEncodedMethod[]::new);
+    }
+    return methods;
+  }
+
+  private boolean isUnneededVisibilityBridge(DexEncodedMethod method) {
+    if (appView.appInfo().isPinned(method.method)) {
+      return false;
+    }
     MethodAccessFlags accessFlags = method.accessFlags;
-    if (accessFlags.isBridge() && !accessFlags.isAbstract()) {
-      InvokeSingleTargetExtractor targetExtractor =
-          new InvokeSingleTargetExtractor(appInfo.dexItemFactory);
-      method.getCode().registerCodeReferences(targetExtractor);
-      DexMethod target = targetExtractor.getTarget();
-      InvokeKind kind = targetExtractor.getKind();
-      // javac-generated visibility forward bridge method has same descriptor (name, signature and
-      // return type).
-      if (target != null && target.hasSameProtoAndName(method.method)) {
-        assert !accessFlags.isPrivate() && !accessFlags.isConstructor();
-        if (kind == InvokeKind.SUPER) {
-          // This is a visibility forward, so check for the direct target.
-          DexEncodedMethod targetMethod =
-              appInfo.resolveMethod(target.getHolder(), target).asSingleTarget();
-          if (targetMethod != null && targetMethod.accessFlags.isPublic()) {
-            if (Log.ENABLED) {
-              Log.info(getClass(), "Removing visibility forwarding %s -> %s", method.method,
-                  targetMethod.method);
-            }
-            unneededVisibilityBridges.add(method);
+    if (!accessFlags.isBridge() || accessFlags.isAbstract()) {
+      return false;
+    }
+    InvokeSingleTargetExtractor targetExtractor =
+        new InvokeSingleTargetExtractor(appView.dexItemFactory());
+    method.getCode().registerCodeReferences(targetExtractor);
+    DexMethod target = targetExtractor.getTarget();
+    InvokeKind kind = targetExtractor.getKind();
+    // javac-generated visibility forward bridge method has same descriptor (name, signature and
+    // return type).
+    if (target != null && target.hasSameProtoAndName(method.method)) {
+      assert !accessFlags.isPrivate() && !accessFlags.isConstructor();
+      if (kind == InvokeKind.SUPER) {
+        // This is a visibility forward, so check for the direct target.
+        DexEncodedMethod targetMethod =
+            appView.appInfo().resolveMethod(target.getHolder(), target).asSingleTarget();
+        if (targetMethod != null && targetMethod.accessFlags.isPublic()) {
+          if (Log.ENABLED) {
+            Log.info(
+                getClass(),
+                "Removing visibility forwarding %s -> %s",
+                method.method,
+                targetMethod.method);
           }
+          return true;
         }
       }
     }
+    return false;
   }
 
-  private void removeUnneededVisibilityBridges() {
-    Set<DexType> classes = unneededVisibilityBridges.stream()
-        .map(method -> method.method.getHolder())
-        .collect(Collectors.toSet());
-    for (DexType type : classes) {
-      DexClass clazz = appInfo.definitionFor(type);
-      clazz.setVirtualMethods(removeMethods(clazz.virtualMethods(), unneededVisibilityBridges));
+  public void run() {
+    for (DexProgramClass clazz : appView.appInfo().classes()) {
+      removeUnneededVisibilityBridgesFromClass(clazz);
     }
   }
-
-  private DexEncodedMethod[] removeMethods(DexEncodedMethod[] methods,
-      Set<DexEncodedMethod> removals) {
-    assert methods != null;
-    List<DexEncodedMethod> newMethods = Arrays.stream(methods)
-        .filter(method -> !removals.contains(method))
-        .collect(Collectors.toList());
-    assert newMethods.size() < methods.length;
-    return newMethods.toArray(new DexEncodedMethod[newMethods.size()]);
-  }
-
-  public DexApplication run() {
-    for (DexClass clazz : appInfo.classes()) {
-      clazz.forEachMethod(this::identifyBridgeMethod);
-    }
-    if (!unneededVisibilityBridges.isEmpty()) {
-      removeUnneededVisibilityBridges();
-    }
-    return application;
-  }
-
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 13dd3ea..dc6dabc 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -193,6 +193,10 @@
    */
   private final Set<DexMethod> methodsTargetedByInvokeDynamic = Sets.newIdentityHashSet();
   /**
+   * Set of direct lambda methods that are the immediate target of an invoke-dynamic.
+   */
+  private final Set<DexMethod> lambdaMethodsTargetedByInvokeDynamic = Sets.newIdentityHashSet();
+  /**
    * Set of virtual methods that are the immediate target of an invoke-direct.
    * */
   private final Set<DexMethod> virtualMethodsTargetedByInvokeDirect = Sets.newIdentityHashSet();
@@ -237,15 +241,15 @@
   private final Set<DexMethod> virtualTargetsMarkedAsReachable = Sets.newIdentityHashSet();
 
   /**
-   * A set of dexitems we have reported missing to dedupe warnings.
+   * A set of references we have reported missing to dedupe warnings.
    */
   private final Set<DexReference> reportedMissing = Sets.newIdentityHashSet();
 
   /**
-   * A set of items that we are keeping due to keep rules. This may differ from the rootSet due to
-   * dependent keep rules.
+   * A set of references that we are keeping due to keep rules. This may differ from the root set
+   * due to dependent keep rules.
    */
-  private final Set<DexDefinition> pinnedItems = Sets.newIdentityHashSet();
+  private final Set<DexReference> pinnedItems = Sets.newIdentityHashSet();
 
   /**
    * A map from classes to annotations that need to be processed should the classes ever become
@@ -305,6 +309,8 @@
     DexDefinition item = appInfo.definitionFor(root.getKey());
     if (item != null) {
       enqueueRootItem(item, root.getValue());
+    } else {
+      assert false : "Expected root item `" + root.getKey().toSourceString() + "` to be present";
     }
   }
 
@@ -351,7 +357,7 @@
     } else {
       throw new IllegalArgumentException(item.toString());
     }
-    pinnedItems.add(item);
+    pinnedItems.add(item.toReference());
   }
 
   private void enqueueFirstNonSerializableClassInitializer(DexClass clazz, KeepReason reason) {
@@ -648,6 +654,10 @@
       assert implHandle != null;
 
       DexMethod method = implHandle.asMethod();
+      if (descriptor.delegatesToLambdaImplMethod()) {
+        lambdaMethodsTargetedByInvokeDynamic.add(method);
+      }
+
       if (!methodsTargetedByInvokeDynamic.add(method)) {
         return;
       }
@@ -1517,9 +1527,18 @@
     } finally {
       timing.end();
     }
+    unpinLambdaMethods();
     return new AppInfoWithLiveness(appInfo, this);
   }
 
+  private void unpinLambdaMethods() {
+    for (DexMethod method : lambdaMethodsTargetedByInvokeDynamic) {
+      pinnedItems.remove(method);
+      rootSet.prune(method);
+    }
+    lambdaMethodsTargetedByInvokeDynamic.clear();
+  }
+
   private void markMethodAsKept(DexEncodedMethod target, KeepReason reason) {
     DexClass holder = appInfo.definitionFor(target.method.holder);
     // If this method no longer has a corresponding class then we have shaken it away before.
@@ -2029,8 +2048,7 @@
           instanceFieldReads.keySet(), staticFieldReads.keySet());
       this.fieldsWritten = enqueuer.mergeFieldAccesses(
           instanceFieldWrites.keySet(), staticFieldWrites.keySet());
-      this.pinnedItems =
-          DexDefinition.mapToReference(enqueuer.pinnedItems.stream()).collect(Collectors.toSet());
+      this.pinnedItems = enqueuer.pinnedItems;
       this.virtualInvokes = joinInvokedMethods(enqueuer.virtualInvokes);
       this.interfaceInvokes = joinInvokedMethods(enqueuer.interfaceInvokes);
       this.superInvokes = joinInvokedMethods(enqueuer.superInvokes, TargetWithContext::getTarget);
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 9e6be06..f94954b 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -897,10 +897,8 @@
     if (context instanceof ProguardKeepRule) {
       if (item.isDexEncodedMethod()) {
         DexEncodedMethod encodedMethod = item.asDexEncodedMethod();
-        if (encodedMethod.isSyntheticMethod()) {
-          // Don't keep synthetic methods (see b/120971047 for additional details).
-          // TODO(b/122819537): need to distinguish lambda desugared synthetic methods v.s. kotlinc
-          // synthetic methods?
+        if (encodedMethod.method.isLambdaDeserializeMethod(appView.dexItemFactory())) {
+          // Don't keep lambda deserialization methods.
           return;
         }
         // If desugaring is enabled, private and static interface methods will be moved to a
@@ -1232,7 +1230,8 @@
       Map<DexType, Set<DexReference>> requiredReferencesPerType = new IdentityHashMap<>();
       for (DexReference reference : noShrinking.keySet()) {
         // Check that `pinnedItems` is a super set of the root set.
-        assert pinnedItems == null || pinnedItems.contains(reference);
+        assert pinnedItems == null || pinnedItems.contains(reference)
+            : "Expected reference `" + reference.toSourceString() + "` to be pinned";
         if (reference.isDexType()) {
           DexType type = reference.asDexType();
           requiredReferencesPerType.putIfAbsent(type, Sets.newIdentityHashSet());
@@ -1263,7 +1262,10 @@
                       .map(DexEncodedField::getKey)
                       .collect(Collectors.toSet());
             }
-            assert fields.contains(requiredField);
+            assert fields.contains(requiredField)
+                : "Expected field `"
+                    + requiredField.toSourceString()
+                    + "` from the root set to be present";
           } else if (requiredReference.isDexMethod()) {
             DexMethod requiredMethod = requiredReference.asDexMethod();
             if (methods == null) {
@@ -1273,7 +1275,10 @@
                       .map(DexEncodedMethod::getKey)
                       .collect(Collectors.toSet());
             }
-            assert methods.contains(requiredMethod);
+            assert methods.contains(requiredMethod)
+                : "Expected method `"
+                    + requiredMethod.toSourceString()
+                    + "` from the root set to be present";
           } else {
             assert false;
           }
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
index f19b089..f796046 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
@@ -39,10 +39,6 @@
 
   @Test
   public void testDontShrinkAndDontOptimize() throws Exception {
-    // TODO(b/122819537): need to trace kotlinc-generated synthetic methods.
-    if (backend == Backend.DEX) {
-      return;
-    }
     test("-dontshrink", "-dontoptimize");
   }
 
@@ -53,10 +49,6 @@
 
   @Test
   public void testDontShrink() throws Exception {
-    // TODO(b/122819537): need to trace kotlinc-generated synthetic methods.
-    if (backend == Backend.DEX) {
-      return;
-    }
     test("-dontshrink");
   }
 
@@ -69,5 +61,4 @@
   public void testDontObfuscate() throws Exception {
     test("-dontobfuscate");
   }
-
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/synthetic/StaticCallInSyntheticMethodAsmTest.java b/src/test/java/com/android/tools/r8/shaking/synthetic/StaticCallInSyntheticMethodAsmTest.java
index cad2410..b08be31 100644
--- a/src/test/java/com/android/tools/r8/shaking/synthetic/StaticCallInSyntheticMethodAsmTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/synthetic/StaticCallInSyntheticMethodAsmTest.java
@@ -26,17 +26,8 @@
   //   static synthetic void bar() { Sub.foo(); }
   // }
   // class Sub extends Base {}
-  //
-  // As per b/120971047, we do not add synthetic methods to the root set.
-  // When running the above example with -dontshrink, the static call in the synthetic method is not
-  // traced, so no chance to rebind that call to Base#foo. Then, at the end of dex writing, it hits
-  // assertion error in the naming lense that checks if call targets are eligible for renaming.
   @Test
   public void test() throws Exception {
-    // TODO(b/122819537): need to trace kotlinc-generated synthetic methods.
-    if (backend == Backend.DEX) {
-      return;
-    }
     testForR8(backend)
         .addProgramClassFileData(Base.dump(), Sub.dump())
         .addKeepRules("-dontshrink")