Map context during graph lense lookup

Bug: 123012641
Change-Id: I81ed27e5f54c6edf021ac998e5ced0b7c44b952a
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index f070078..df23b1c 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -74,22 +74,24 @@
   }
 
   public DirectMappedDexApplication rewrittenWithLense(GraphLense graphLense) {
-    assert mappingIsValid(graphLense, programClasses.getAllTypes());
-    assert mappingIsValid(graphLense, libraryClasses.keySet());
     // As a side effect, this will rebuild the program classes and library classes maps.
-    return this.builder().build().asDirect();
+    DirectMappedDexApplication rewrittenApplication = this.builder().build().asDirect();
+    assert rewrittenApplication.mappingIsValid(graphLense, programClasses.getAllTypes());
+    assert rewrittenApplication.mappingIsValid(graphLense, libraryClasses.keySet());
+    return rewrittenApplication;
   }
 
   private boolean mappingIsValid(GraphLense graphLense, Iterable<DexType> types) {
-    // The lense might either map to a different type that is already present in the application
+    // The lens might either map to a different type that is already present in the application
     // (e.g. relinking a type) or it might encode a type that was renamed, in which case the
     // original type will point to a definition that was renamed.
     for (DexType type : types) {
       DexType renamed = graphLense.lookupType(type);
       if (renamed != type) {
-        if (definitionFor(type).type != renamed && definitionFor(renamed) == null) {
-          return false;
+        if (definitionFor(type) == null && definitionFor(renamed) != null) {
+          continue;
         }
+        assert definitionFor(type).type != renamed && definitionFor(renamed) == null;
       }
     }
     return true;
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 898afed..bc22da0 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -359,6 +359,8 @@
     return new Builder();
   }
 
+  public abstract DexType getOriginalType(DexType type);
+
   public abstract DexField getOriginalFieldSignature(DexField field);
 
   public abstract DexMethod getOriginalMethodSignature(DexMethod method);
@@ -391,7 +393,7 @@
   }
 
   public abstract GraphLenseLookupResult lookupMethod(
-      DexMethod method, DexEncodedMethod context, Type type);
+      DexMethod method, DexMethod context, Type type);
 
   public abstract RewrittenPrototypeDescription lookupPrototypeChanges(DexMethod method);
 
@@ -626,6 +628,11 @@
     }
 
     @Override
+    public DexType getOriginalType(DexType type) {
+      return type;
+    }
+
+    @Override
     public DexField getOriginalFieldSignature(DexField field) {
       return field;
     }
@@ -651,8 +658,7 @@
     }
 
     @Override
-    public GraphLenseLookupResult lookupMethod(
-        DexMethod method, DexEncodedMethod context, Type type) {
+    public GraphLenseLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
       return new GraphLenseLookupResult(method, type);
     }
 
@@ -673,14 +679,14 @@
   }
 
   /**
-   * GraphLense implementation with a parent lense using a simple mapping for type, method and
-   * field mapping.
+   * GraphLense implementation with a parent lense using a simple mapping for type, method and field
+   * mapping.
    *
-   * Subclasses can override the lookup methods.
+   * <p>Subclasses can override the lookup methods.
    *
-   * For method mapping where invocation type can change just override
-   * {@link #mapInvocationType(DexMethod, DexMethod, DexEncodedMethod, Type)} if
-   * the default name mapping applies, and only invocation type might need to change.
+   * <p>For method mapping where invocation type can change just override {@link
+   * #mapInvocationType(DexMethod, DexMethod, DexMethod, Type)} if the default name mapping applies,
+   * and only invocation type might need to change.
    */
   public static class NestedGraphLense extends GraphLense {
 
@@ -715,6 +721,11 @@
     }
 
     @Override
+    public DexType getOriginalType(DexType type) {
+      return previousLense.getOriginalType(type);
+    }
+
+    @Override
     public DexField getOriginalFieldSignature(DexField field) {
       DexField originalField =
           originalFieldSignatures != null
@@ -772,9 +783,12 @@
     }
 
     @Override
-    public GraphLenseLookupResult lookupMethod(
-        DexMethod method, DexEncodedMethod context, Type type) {
-      GraphLenseLookupResult previous = previousLense.lookupMethod(method, context, type);
+    public GraphLenseLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
+      DexMethod previousContext =
+          originalMethodSignatures != null
+              ? originalMethodSignatures.getOrDefault(context, context)
+              : context;
+      GraphLenseLookupResult previous = previousLense.lookupMethod(method, previousContext, type);
       DexMethod newMethod = methodMap.get(previous.getMethod());
       if (newMethod == null) {
         return previous;
@@ -782,7 +796,7 @@
       // TODO(sgjesse): Should we always do interface to virtual mapping? Is it a performance win
       // that only subclasses which are known to need it actually do it?
       return new GraphLenseLookupResult(
-          newMethod, mapInvocationType(newMethod, method, context, previous.getType()));
+          newMethod, mapInvocationType(newMethod, method, previous.getType()));
     }
 
     @Override
@@ -793,22 +807,20 @@
     /**
      * Default invocation type mapping.
      *
-     * This is an identity mapping. If a subclass need invocation type mapping either override
-     * this method or {@link #lookupMethod(DexMethod, DexEncodedMethod, Type)}
+     * <p>This is an identity mapping. If a subclass need invocation type mapping either override
+     * this method or {@link #lookupMethod(DexMethod, DexMethod, Type)}
      */
-    protected Type mapInvocationType(
-        DexMethod newMethod, DexMethod originalMethod, DexEncodedMethod context, Type type) {
+    protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
       return type;
     }
 
     /**
      * Standard mapping between interface and virtual invoke type.
      *
-     * Handle methods moved from interface to class or class to interface.
+     * <p>Handle methods moved from interface to class or class to interface.
      */
-    final protected Type mapVirtualInterfaceInvocationTypes(
-        AppInfo appInfo, DexMethod newMethod, DexMethod originalMethod,
-        DexEncodedMethod context, Type type) {
+    protected final Type mapVirtualInterfaceInvocationTypes(
+        AppInfo appInfo, DexMethod newMethod, DexMethod originalMethod, Type type) {
       if (type == Type.VIRTUAL || type == Type.INTERFACE) {
         // Get the invoke type of the actual definition.
         DexClass newTargetClass = appInfo.definitionFor(newMethod.holder);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
index d9040c5..c12ea40 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
@@ -532,7 +532,7 @@
 
     private void processInvoke(Type type, DexMethod method) {
       DexEncodedMethod source = caller.method;
-      GraphLenseLookupResult result = graphLense.lookupMethod(method, source, type);
+      GraphLenseLookupResult result = graphLense.lookupMethod(method, source.method, type);
       method = result.getMethod();
       type = result.getType();
       DexEncodedMethod definition = appInfo.lookup(type, method, source.method.holder);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index cf88f0f..fd968d6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -156,7 +156,7 @@
             checkInvokeDirect(method.method, invoke.asInvokeDirect());
           }
           GraphLenseLookupResult lenseLookup =
-              graphLense.lookupMethod(invokedMethod, method, invoke.getType());
+              graphLense.lookupMethod(invokedMethod, method.method, invoke.getType());
           DexMethod actualTarget = lenseLookup.getMethod();
           Invoke.Type actualInvokeType = lenseLookup.getType();
           if (actualInvokeType == Type.VIRTUAL) {
@@ -314,10 +314,11 @@
           }
         } else if (current.isMoveException()) {
           MoveException moveException = current.asMoveException();
-          if (moveException.hasOutValue()) {
-            // Conservatively add the out-value to `newSSAValues` since the catch handler guards
-            // may have been renamed as a result of class merging.
-            newSSAValues.add(moveException.outValue());
+          DexType newExceptionType = graphLense.lookupType(moveException.getExceptionType());
+          if (newExceptionType != moveException.getExceptionType()) {
+            iterator.replaceCurrentInstruction(
+                new MoveException(
+                    makeOutValue(moveException, code, newSSAValues), newExceptionType, options));
           }
         } else if (current.isNewArrayEmpty()) {
           NewArrayEmpty newArrayEmpty = current.asNewArrayEmpty();
@@ -328,7 +329,7 @@
             iterator.replaceCurrentInstruction(newNewArray);
           }
         } else if (current.isNewInstance()) {
-          NewInstance newInstance= current.asNewInstance();
+          NewInstance newInstance = current.asNewInstance();
           DexType newClazz = graphLense.lookupType(newInstance.clazz);
           if (newClazz != newInstance.clazz) {
             NewInstance newNewInstance = new NewInstance(
@@ -464,7 +465,7 @@
       DexMethod invokedMethod = methodHandle.asMethod();
       MethodHandleType oldType = methodHandle.type;
       GraphLenseLookupResult lenseLookup =
-          graphLense.lookupMethod(invokedMethod, context, oldType.toInvokeType());
+          graphLense.lookupMethod(invokedMethod, context.method, oldType.toInvokeType());
       DexMethod rewrittenTarget = lenseLookup.getMethod();
       DexMethod actualTarget;
       MethodHandleType newType;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriterGraphLense.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriterGraphLense.java
index 26f890e..d5ceeb1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriterGraphLense.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriterGraphLense.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.desugar;
 
-import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLense;
@@ -28,13 +27,12 @@
   }
 
   @Override
-  protected Type mapInvocationType(
-      DexMethod newMethod, DexMethod originalMethod, DexEncodedMethod context, Type type) {
+  protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
     if (methodMap.get(originalMethod) == newMethod) {
       assert type == Type.VIRTUAL || type == Type.DIRECT;
       return Type.STATIC;
     }
-    return super.mapInvocationType(newMethod, originalMethod, context, type);
+    return super.mapInvocationType(newMethod, originalMethod, type);
   }
 
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLense.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLense.java
index 9bdd55e..6613534 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLense.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLense.java
@@ -36,14 +36,12 @@
   }
 
   @Override
-  protected Type mapInvocationType(
-      DexMethod newMethod, DexMethod originalMethod,
-      DexEncodedMethod context, Type type) {
+  protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
     if (methodMap.get(originalMethod) == newMethod) {
       assert type == Type.VIRTUAL || type == Type.DIRECT;
       return Type.STATIC;
     }
-    return super.mapInvocationType(newMethod, originalMethod, context, type);
+    return super.mapInvocationType(newMethod, originalMethod, type);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
index 98a0da4..5e19d48 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.optimize;
 
 import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLense;
@@ -47,11 +46,8 @@
     return new Builder(appInfo);
   }
 
-
   @Override
-  protected Type mapInvocationType(
-      DexMethod newMethod, DexMethod originalMethod, DexEncodedMethod context, Type type) {
-    return super.mapVirtualInterfaceInvocationTypes(
-        appInfo, newMethod, originalMethod, context, type);
+  protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
+    return super.mapVirtualInterfaceInvocationTypes(appInfo, newMethod, originalMethod, type);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java b/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java
index 2db2dce..c685d84 100644
--- a/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java
+++ b/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java
@@ -15,6 +15,7 @@
 import java.util.Set;
 
 final class PublicizerLense extends NestedGraphLense {
+
   private final AppView appView;
   private final Set<DexMethod> publicizedMethods;
 
@@ -34,8 +35,7 @@
   }
 
   @Override
-  public GraphLenseLookupResult lookupMethod(
-      DexMethod method, DexEncodedMethod context, Type type) {
+  public GraphLenseLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
     GraphLenseLookupResult previous = previousLense.lookupMethod(method, context, type);
     method = previous.getMethod();
     type = previous.getType();
@@ -46,7 +46,7 @@
     return super.lookupMethod(method, context, type);
   }
 
-  private boolean publicizedMethodIsPresentOnHolder(DexMethod method, DexEncodedMethod context) {
+  private boolean publicizedMethodIsPresentOnHolder(DexMethod method, DexMethod context) {
     GraphLenseLookupResult lookup =
         appView.graphLense().lookupMethod(method, context, Type.VIRTUAL);
     DexMethod signatureInCurrentWorld = lookup.getMethod();
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 2d8392b..956d6bd 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -6,7 +6,6 @@
 import static com.android.tools.r8.ir.code.Invoke.Type.DIRECT;
 import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
 
-import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfo.ResolutionResult;
@@ -53,6 +52,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 import com.google.common.collect.Streams;
 import it.unimi.dsi.fastutil.ints.Int2IntMap;
 import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
@@ -211,13 +211,13 @@
   private final Set<DexProgramClass> mergeCandidates = new LinkedHashSet<>();
 
   // Map from source class to target class.
-  private final Map<DexType, DexType> mergedClasses = new HashMap<>();
+  private final Map<DexType, DexType> mergedClasses = new IdentityHashMap<>();
 
   // Map from target class to the super classes that have been merged into the target class.
-  private final Map<DexType, Set<DexType>> mergedClassesInverse = new HashMap<>();
+  private final Map<DexType, Set<DexType>> mergedClassesInverse = new IdentityHashMap<>();
 
   // Set of types that must not be merged into their subtype.
-  private final Set<DexType> pinnedTypes = new HashSet<>();
+  private final Set<DexType> pinnedTypes = Sets.newIdentityHashSet();
 
   // The resulting graph lense that should be used after class merging.
   private final VerticalClassMergerGraphLense.Builder renamedMembersLense;
@@ -686,7 +686,7 @@
     if (Log.ENABLED) {
       Log.debug(getClass(), "Merged %d classes.", mergedClasses.size());
     }
-    return renamedMembersLense.build(graphLense, mergedClasses, synthesizedBridges, appInfo);
+    return renamedMembersLense.build(graphLense, mergedClasses, appInfo);
   }
 
   private boolean methodResolutionMayChange(DexClass source, DexClass target) {
@@ -1427,42 +1427,19 @@
       for (DexProgramClass clazz : appInfo.classes()) {
         clazz.setDirectMethods(substituteTypesIn(clazz.directMethods()));
         clazz.setVirtualMethods(substituteTypesIn(clazz.virtualMethods()));
-        clazz.setVirtualMethods(removeDupes(clazz.virtualMethods()));
         clazz.setStaticFields(substituteTypesIn(clazz.staticFields()));
         clazz.setInstanceFields(substituteTypesIn(clazz.instanceFields()));
       }
+      for (SynthesizedBridgeCode synthesizedBridge : synthesizedBridges) {
+        synthesizedBridge.updateMethodSignatures(this::fixupMethod);
+      }
       // Record type renamings so check-cast and instance-of checks are also fixed.
       for (DexType type : mergedClasses.keySet()) {
-        DexType fixed = fixupType(type);
-        lense.map(type, fixed);
+        lense.map(type, fixupType(type));
       }
       return lense.build(application.dexItemFactory, graphLense);
     }
 
-    private DexEncodedMethod[] removeDupes(DexEncodedMethod[] methods) {
-      if (methods == null) {
-        return null;
-      }
-      Map<DexMethod, DexEncodedMethod> filtered = new IdentityHashMap<>();
-      for (DexEncodedMethod method : methods) {
-        DexEncodedMethod previous = filtered.put(method.method, method);
-        if (previous != null) {
-          if (!previous.accessFlags.isBridge()) {
-            if (!method.accessFlags.isBridge()) {
-              throw new CompilationError(
-                  "Class merging produced invalid result on: " + previous.toSourceString());
-            } else {
-              filtered.put(previous.method, previous);
-            }
-          }
-        }
-      }
-      if (filtered.size() == methods.length) {
-        return methods;
-      }
-      return filtered.values().toArray(DexEncodedMethod.EMPTY_ARRAY);
-    }
-
     private DexEncodedMethod[] substituteTypesIn(DexEncodedMethod[] methods) {
       if (methods == null) {
         return null;
@@ -1470,12 +1447,9 @@
       for (int i = 0; i < methods.length; i++) {
         DexEncodedMethod encodedMethod = methods[i];
         DexMethod method = encodedMethod.method;
-        DexProto newProto = getUpdatedProto(method.proto);
-        DexType newHolder = fixupType(method.holder);
-        DexMethod newMethod = application.dexItemFactory.createMethod(newHolder, newProto,
-            method.name);
-        if (newMethod != encodedMethod.method) {
-          lense.move(encodedMethod.method, newMethod);
+        DexMethod newMethod = fixupMethod(method);
+        if (newMethod != method) {
+          lense.move(method, newMethod);
           methods[i] = encodedMethod.toTypeSubstitutedMethod(newMethod);
         }
       }
@@ -1500,7 +1474,12 @@
       return fields;
     }
 
-    private DexProto getUpdatedProto(DexProto proto) {
+    private DexMethod fixupMethod(DexMethod method) {
+      return application.dexItemFactory.createMethod(
+          fixupType(method.holder), fixupProto(method.proto), method.name);
+    }
+
+    private DexProto fixupProto(DexProto proto) {
       DexProto result = protoFixupCache.get(proto);
       if (result == null) {
         DexType returnType = fixupType(proto.returnType);
@@ -1517,12 +1496,13 @@
         DexType fixed = fixupType(base);
         if (base == fixed) {
           return type;
-        } else {
-          return type.replaceBaseType(fixed, application.dexItemFactory);
         }
+        return type.replaceBaseType(fixed, application.dexItemFactory);
       }
-      while (mergedClasses.containsKey(type)) {
-        type = mergedClasses.get(type);
+      if (type.isClassType()) {
+        while (mergedClasses.containsKey(type)) {
+          type = mergedClasses.get(type);
+        }
       }
       return type;
     }
@@ -1675,6 +1655,11 @@
     }
 
     @Override
+    public DexType getOriginalType(DexType type) {
+      throw new Unreachable();
+    }
+
+    @Override
     public DexField getOriginalFieldSignature(DexField field) {
       throw new Unreachable();
     }
@@ -1700,8 +1685,7 @@
     }
 
     @Override
-    public GraphLenseLookupResult lookupMethod(
-        DexMethod method, DexEncodedMethod context, Type type) {
+    public GraphLenseLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
       // First look up the method using the existing graph lense (for example, the type will have
       // changed if the method was publicized by ClassAndMemberPublicizer).
       GraphLenseLookupResult lookup = graphLense.lookupMethod(method, context, type);
@@ -1745,7 +1729,7 @@
   public static class IllegalAccessDetector extends UseRegistry {
 
     private boolean foundIllegalAccess = false;
-    private DexEncodedMethod context = null;
+    private DexMethod context = null;
 
     private final AppView<? extends AppInfo> appView;
     private final DexClass source;
@@ -1761,7 +1745,7 @@
     }
 
     public void setContext(DexEncodedMethod context) {
-      this.context = context;
+      this.context = context.method;
     }
 
     private boolean checkFieldReference(DexField field) {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
index 20a05ab..6576433 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.shaking;
 
 import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
@@ -14,17 +13,14 @@
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
 import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.shaking.VerticalClassMerger.SynthesizedBridgeCode;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.Function;
 
 // This graph lense is instantiated during vertical class merging. The graph lense is context
 // sensitive in the enclosing class of a given invoke *and* the type of the invoke (e.g., invoke-
@@ -55,7 +51,7 @@
 
   private final Map<DexType, Map<DexMethod, GraphLenseLookupResult>>
       contextualVirtualToDirectMethodMaps;
-  private final Set<DexMethod> mergedMethods;
+  private Set<DexMethod> mergedMethods;
   private final Map<DexMethod, DexMethod> originalMethodSignaturesForBridges;
 
   public VerticalClassMergerGraphLense(
@@ -83,19 +79,29 @@
   }
 
   @Override
+  public DexType getOriginalType(DexType type) {
+    return previousLense.getOriginalType(type);
+  }
+
+  @Override
   public DexMethod getOriginalMethodSignature(DexMethod method) {
     return super.getOriginalMethodSignature(
         originalMethodSignaturesForBridges.getOrDefault(method, method));
   }
 
   @Override
-  public GraphLenseLookupResult lookupMethod(
-      DexMethod method, DexEncodedMethod context, Type type) {
+  public GraphLenseLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
     assert isContextFreeForMethod(method) || (context != null && type != null);
-    GraphLenseLookupResult previous = previousLense.lookupMethod(method, context, type);
-    if (previous.getType() == Type.SUPER && !mergedMethods.contains(context.method)) {
+    DexMethod previousContext =
+        originalMethodSignaturesForBridges.containsKey(context)
+            ? originalMethodSignaturesForBridges.get(context)
+            : originalMethodSignatures != null
+                ? originalMethodSignatures.getOrDefault(context, context)
+                : context;
+    GraphLenseLookupResult previous = previousLense.lookupMethod(method, previousContext, type);
+    if (previous.getType() == Type.SUPER && !mergedMethods.contains(context)) {
       Map<DexMethod, GraphLenseLookupResult> virtualToDirectMethodMap =
-          contextualVirtualToDirectMethodMaps.get(context.method.holder);
+          contextualVirtualToDirectMethodMaps.get(context.holder);
       if (virtualToDirectMethodMap != null) {
         GraphLenseLookupResult lookup = virtualToDirectMethodMap.get(previous.getMethod());
         if (lookup != null) {
@@ -113,10 +119,8 @@
   }
 
   @Override
-  protected Type mapInvocationType(
-      DexMethod newMethod, DexMethod originalMethod, DexEncodedMethod context, Type type) {
-    return super.mapVirtualInterfaceInvocationTypes(
-        appInfo, newMethod, originalMethod, context, type);
+  protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
+    return super.mapVirtualInterfaceInvocationTypes(appInfo, newMethod, originalMethod, type);
   }
 
   @Override
@@ -170,7 +174,6 @@
     public GraphLense build(
         GraphLense previousLense,
         Map<DexType, DexType> mergedClasses,
-        List<SynthesizedBridgeCode> synthesizedBridges,
         AppInfo appInfo) {
       if (fieldMap.isEmpty()
           && methodMap.isEmpty()
@@ -179,14 +182,6 @@
       }
       Map<DexProto, DexProto> cache = new HashMap<>();
       BiMap<DexField, DexField> originalFieldSignatures = fieldMap.inverse();
-      // Update all synthesized bridges.
-      Function<DexMethod, DexMethod> synthesizedBridgeTransformer =
-          method ->
-              getMethodSignatureAfterClassMerging(
-                  method, mergedClasses, appInfo.dexItemFactory, cache);
-      for (SynthesizedBridgeCode synthesizedBridge : synthesizedBridges) {
-        synthesizedBridge.updateMethodSignatures(synthesizedBridgeTransformer);
-      }
       // Build new graph lense.
       return new VerticalClassMergerGraphLense(
           appInfo,
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index ec12f82..c9be1b8 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -28,6 +28,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.naming.ClassNameMapper;
@@ -150,16 +151,17 @@
       // At this point we don't know if we really need to add this class to the builder.
       // It depends on whether any methods/fields are renamed or some methods contain positions.
       // Create a supplier which creates a new, cached ClassNaming.Builder on-demand.
+      DexType originalType = graphLense.getOriginalType(clazz.type);
       DexString renamedClassName = namingLens.lookupDescriptor(clazz.getType());
       Supplier<ClassNaming.Builder> onDemandClassNamingBuilder =
           Suppliers.memoize(
               () ->
                   classNameMapperBuilder.classNamingBuilder(
                       DescriptorUtils.descriptorToJavaType(renamedClassName.toString()),
-                      clazz.toString()));
+                      originalType.toSourceString()));
 
       // If the class is renamed add it to the classNamingBuilder.
-      addClassToClassNaming(clazz, renamedClassName, onDemandClassNamingBuilder);
+      addClassToClassNaming(originalType, renamedClassName, onDemandClassNamingBuilder);
 
       // First transfer renamed fields to classNamingBuilder.
       addFieldsToClassNaming(graphLense, namingLens, clazz, onDemandClassNamingBuilder);
@@ -306,10 +308,12 @@
   }
 
   @SuppressWarnings("ReturnValueIgnored")
-  private static void addClassToClassNaming(DexProgramClass clazz, DexString renamedClassName,
+  private static void addClassToClassNaming(
+      DexType originalType,
+      DexString renamedClassName,
       Supplier<Builder> onDemandClassNamingBuilder) {
     // We do know we need to create a ClassNaming.Builder if the class itself had been renamed.
-    if (!clazz.toString().equals(renamedClassName.toString())) {
+    if (originalType.descriptor != renamedClassName) {
       // Not using return value, it's registered in classNameMapperBuilder
       onDemandClassNamingBuilder.get();
     }
diff --git a/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
index bb89443..1836a29 100644
--- a/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
@@ -86,16 +86,13 @@
 
   private void runR8(Path proguardConfig, Consumer<InternalOptions> optionsConsumer)
       throws IOException, ExecutionException, CompilationFailedException {
-    ToolHelper.runR8(
-        R8Command.builder()
-            .setOutput(Paths.get(temp.getRoot().getCanonicalPath()), OutputMode.DexIndexed)
+    inspector =
+        testForR8(Backend.DEX)
             .addProgramFiles(EXAMPLE_JAR)
-            .addProguardConfigurationFiles(proguardConfig)
-            .setDisableMinification(true)
-            .build(),
-        optionsConsumer);
-    inspector = new CodeInspector(
-        Paths.get(temp.getRoot().getCanonicalPath()).resolve("classes.dex"));
+            .addKeepRuleFiles(proguardConfig)
+            .addOptionsModification(optionsConsumer)
+            .compile()
+            .inspector();
   }
 
   private CodeInspector inspector;
diff --git a/src/test/java/com/android/tools/r8/proguard/printmapping/PrintMappingTest.java b/src/test/java/com/android/tools/r8/proguard/printmapping/PrintMappingTest.java
index e9bf581..378cf0e 100644
--- a/src/test/java/com/android/tools/r8/proguard/printmapping/PrintMappingTest.java
+++ b/src/test/java/com/android/tools/r8/proguard/printmapping/PrintMappingTest.java
@@ -27,14 +27,11 @@
   private void test(Path mapping) throws Exception {
     testForR8(Backend.DEX)
         .addInnerClasses(PrintMappingTest.class)
-        .addKeepMainRule(TestClass.class)
+        .addKeepRules("-keep,allowobfuscation class " + TestClass.class.getTypeName())
         .addKeepRules("-printmapping " + mapping)
         .compile();
     assertTrue(mapping.toFile().exists());
   }
 
-  static class TestClass {
-
-    public static void main(String[] args) {}
-  }
+  static class TestClass {}
 }
diff --git a/src/test/java/com/android/tools/r8/resource/KeepDirectoriesTest.java b/src/test/java/com/android/tools/r8/resource/KeepDirectoriesTest.java
index 1c28054..5b7a11e 100644
--- a/src/test/java/com/android/tools/r8/resource/KeepDirectoriesTest.java
+++ b/src/test/java/com/android/tools/r8/resource/KeepDirectoriesTest.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.DataResourceConsumer;
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.R8Command;
+import com.android.tools.r8.StringResource;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.naming.ClassNameMapper;
@@ -65,10 +66,15 @@
 
   // Return the package name in the app for this package.
   private String pathForThisPackage(AndroidApp app) throws Exception {
-    ClassNameMapper mapper =
-        ClassNameMapper.mapperFromString(app.getProguardMapOutputData().getString());
-    String x = mapper.getObfuscatedToOriginalMapping().inverse.get(Main.class.getCanonicalName());
-    return x.substring(0, x.lastIndexOf('.')).replace('.', '/');
+    String name;
+    if (app.getProguardMapOutputData() != null) {
+      ClassNameMapper mapper =
+          ClassNameMapper.mapperFromString(app.getProguardMapOutputData().getString());
+      name = mapper.getObfuscatedToOriginalMapping().inverse.get(Main.class.getCanonicalName());
+    } else {
+      name = Main.class.getTypeName();
+    }
+    return name.substring(0, name.lastIndexOf('.')).replace('.', '/');
   }
 
   private void checkResourceNames(Set<String> resourceNames, String expectedPackageName) {
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index b302615..0b246a5 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -260,9 +260,8 @@
     }
 
     if (inspection != null) {
-      CodeInspector inspector = new CodeInspector(out,
-          minify.isMinify() ? proguardMap.toString()
-              : null);
+      CodeInspector inspector =
+          new CodeInspector(out, minify.isMinify() ? proguardMap.toString() : null);
       inspection.accept(inspector);
     }
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking11Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking11Test.java
index f65c1d4..3833645 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking11Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking11Test.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking.examples;
 
-import com.android.tools.r8.TestBase.MinifyMode;
 import com.android.tools.r8.shaking.TreeShakingTest;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 9983320..a9058ff 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -45,6 +45,7 @@
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.lang.reflect.Method;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Collections;
@@ -82,13 +83,14 @@
   public CodeInspector(
       List<Path> files, String mappingFile, Consumer<InternalOptions> optionsConsumer)
       throws IOException, ExecutionException {
-    if (mappingFile != null) {
-      this.mapping = ClassNameMapper.mapperFromFile(Paths.get(mappingFile));
-      BiMapContainer<String, String> nameMapping = this.mapping.getObfuscatedToOriginalMapping();
+    Path mappingPath = mappingFile != null ? Paths.get(mappingFile) : null;
+    if (mappingPath != null && Files.exists(mappingPath)) {
+      mapping = ClassNameMapper.mapperFromFile(mappingPath);
+      BiMapContainer<String, String> nameMapping = mapping.getObfuscatedToOriginalMapping();
       obfuscatedToOriginalMapping = nameMapping.original;
       originalToObfuscatedMapping = nameMapping.inverse;
     } else {
-      this.mapping = null;
+      mapping = null;
       originalToObfuscatedMapping = null;
       obfuscatedToOriginalMapping = null;
     }