Switch from Nested to VerticalClassMergerGraphLense

Change-Id: I24c0f64c6309d4df691b50687557649613b89598
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 2377e3a..d196dd2 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -52,18 +52,16 @@
  * <p>A common use-case for this is to merge an interface into its single implementation.
  *
  * <p>The class merger only fixes the structure of the graph but leaves the actual instructions
- * untouched. Fixup of instructions is deferred via a {@link GraphLense} to the Ir building phase.
+ * untouched. Fixup of instructions is deferred via a {@link GraphLense} to the IR building phase.
  */
 public class VerticalClassMerger {
 
   private final DexApplication application;
   private final AppInfoWithLiveness appInfo;
   private final GraphLense graphLense;
-  private final GraphLense.Builder renamedMembersLense = GraphLense.builder();
   private final Map<DexType, DexType> mergedClasses = new IdentityHashMap<>();
   private final Timing timing;
   private Collection<DexMethod> invokes;
-  private int numberOfMerges = 0;
 
   public VerticalClassMerger(
       DexApplication application,
@@ -170,6 +168,12 @@
     Deque<DexProgramClass> worklist = new ArrayDeque<>();
     Set<DexProgramClass> seenBefore = new HashSet<>();
 
+    int numberOfMerges = 0;
+
+    // The resulting graph lense that should be used after class merging.
+    VerticalClassMergerGraphLense.Builder renamedMembersLense =
+        new VerticalClassMergerGraphLense.Builder();
+
     Iterator<DexProgramClass> classIterator = classes.iterator();
 
     // Visit the program classes in a top-down order according to the class hierarchy.
@@ -218,7 +222,12 @@
         }
         continue;
       }
-      boolean merged = new ClassMerger(clazz, targetClass).merge();
+      ClassMerger merger = new ClassMerger(clazz, targetClass);
+      boolean merged = merger.merge();
+      if (merged) {
+        // Commit the changes to the graph lense.
+        renamedMembersLense.merge(merger.getRenamings());
+      }
       if (Log.ENABLED) {
         if (merged) {
           numberOfMerges++;
@@ -239,8 +248,7 @@
     if (Log.ENABLED) {
       Log.debug(getClass(), "Merged %d classes.", numberOfMerges);
     }
-    return new VerticalClassMergerGraphLense(
-        renamedMembersLense.build(application.dexItemFactory, graphLense));
+    return renamedMembersLense.build(graphLense);
   }
 
   private class ClassMerger {
@@ -249,7 +257,8 @@
 
     private final DexClass source;
     private final DexClass target;
-    private final Map<DexEncodedMethod, DexEncodedMethod> deferredRenamings = new HashMap<>();
+    private final VerticalClassMergerGraphLense.Builder deferredRenamings =
+        new VerticalClassMergerGraphLense.Builder();
     private boolean abortMerge = false;
 
     private ClassMerger(DexClass source, DexClass target) {
@@ -338,17 +347,19 @@
       source.interfaces = DexTypeList.empty();
       // Step 4: Record merging.
       mergedClasses.put(source.type, target.type);
-      // Step 5: Make deferred renamings final.
-      deferredRenamings.forEach((from, to) -> renamedMembersLense.map(from.method, to.method));
       return true;
     }
 
+    public VerticalClassMergerGraphLense.Builder getRenamings() {
+      return deferredRenamings;
+    }
+
     private DexEncodedMethod filterShadowedInterfaceMethods(DexEncodedMethod m) {
       DexEncodedMethod actual = appInfo.resolveMethod(target.type, m.method).asSingleTarget();
       assert actual != null;
       if (actual != m) {
         // We will drop a method here, so record it as a potential renaming.
-        deferredRenamings.put(m, actual);
+        deferredRenamings.map(m.method, actual.method);
         return null;
       }
       // We will keep the method, so the class better be abstract.
@@ -426,7 +437,7 @@
       if (method.accessFlags.isAbstract()) {
         // We make a method disappear here, so record the renaming so that calls to the previous
         // target get forwarded properly.
-        deferredRenamings.put(method, existing);
+        deferredRenamings.map(method.method, existing.method);
         return existing;
       } else if (existing.accessFlags.isBridge()) {
         InvokeSingleTargetExtractor extractor = new InvokeSingleTargetExtractor();
@@ -450,7 +461,7 @@
       DexEncodedMethod result = method
           .toRenamedMethod(makeMergedName(CONSTRUCTOR_NAME, holder), application.dexItemFactory);
       result.markForceInline();
-      deferredRenamings.put(method, result);
+      deferredRenamings.map(method.method, result.method);
       // Renamed constructors turn into ordinary private functions. They can be private, as
       // they are only references from their direct subclass, which they were merged into.
       result.accessFlags.unsetConstructor();
@@ -468,7 +479,7 @@
       String name = method.method.name.toSourceString();
       DexEncodedMethod result = method
           .toRenamedMethod(makeMergedName(name, holder), application.dexItemFactory);
-      renamedMembersLense.map(method.method, result.method);
+      deferredRenamings.map(method.method, result.method);
       return result;
     }
 
@@ -478,7 +489,7 @@
       DexEncodedField result = field
           .toRenamedField(makeMergedName(oldName.toSourceString(), holder),
               application.dexItemFactory);
-      renamedMembersLense.map(field.field, result.field);
+      deferredRenamings.map(field.field, result.field);
       return result;
     }
   }
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 1bdeb7c..54393b5 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
@@ -10,7 +10,9 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.ir.code.Invoke.Type;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import java.util.Map;
 import java.util.Set;
 
 // This graph lense is instantiated during vertical class merging. The graph lense is context
@@ -40,8 +42,16 @@
 public class VerticalClassMergerGraphLense extends GraphLense {
   private final GraphLense previousLense;
 
-  public VerticalClassMergerGraphLense(GraphLense previousLense) {
+  private final Map<DexField, DexField> fieldMap;
+  private final Map<DexMethod, DexMethod> methodMap;
+
+  public VerticalClassMergerGraphLense(
+      Map<DexField, DexField> fieldMap,
+      Map<DexMethod, DexMethod> methodMap,
+      GraphLense previousLense) {
     this.previousLense = previousLense;
+    this.fieldMap = fieldMap;
+    this.methodMap = methodMap;
   }
 
   @Override
@@ -54,32 +64,66 @@
     // TODO(christofferqa): If [type] is Type.SUPER and [method] has been merged into the class of
     // [context], then return the DIRECT method that has been created for [method] by SimpleClass-
     // Merger. Otherwise, return the VIRTUAL method corresponding to [method].
-    return previousLense.lookupMethod(method, context, type);
+    assert isContextFreeForMethod(method) || context != null;
+    DexMethod previous = previousLense.lookupMethod(method, context, type);
+    return methodMap.getOrDefault(previous, previous);
   }
 
   @Override
   public Set<DexMethod> lookupMethodInAllContexts(DexMethod method) {
-    DexMethod result = lookupMethod(method);
-    if (result != null) {
-      return ImmutableSet.of(result);
+    ImmutableSet.Builder<DexMethod> builder = ImmutableSet.builder();
+    for (DexMethod previous : previousLense.lookupMethodInAllContexts(method)) {
+      builder.add(methodMap.getOrDefault(previous, previous));
     }
-    return ImmutableSet.of();
+    return builder.build();
   }
 
   @Override
   public DexField lookupField(DexField field) {
-    return previousLense.lookupField(field);
+    DexField previous = previousLense.lookupField(field);
+    return fieldMap.getOrDefault(previous, previous);
   }
 
   @Override
   public boolean isContextFreeForMethods() {
-    return false;
+    return previousLense.isContextFreeForMethods();
   }
 
   @Override
   public boolean isContextFreeForMethod(DexMethod method) {
     // TODO(christofferqa): Should return false for methods where this graph lense is context
     // sensitive.
-    return true;
+    return previousLense.isContextFreeForMethod(method);
+  }
+
+  public static class Builder {
+
+    private final ImmutableMap.Builder<DexField, DexField> fieldMapBuilder = ImmutableMap.builder();
+    private final ImmutableMap.Builder<DexMethod, DexMethod> methodMapBuilder =
+        ImmutableMap.builder();
+
+    public Builder() {}
+
+    public GraphLense build(GraphLense previousLense) {
+      Map<DexField, DexField> fieldMap = fieldMapBuilder.build();
+      Map<DexMethod, DexMethod> methodMap = methodMapBuilder.build();
+      if (fieldMap.isEmpty() && methodMap.isEmpty()) {
+        return previousLense;
+      }
+      return new VerticalClassMergerGraphLense(fieldMap, methodMap, previousLense);
+    }
+
+    public void map(DexField from, DexField to) {
+      fieldMapBuilder.put(from, to);
+    }
+
+    public void map(DexMethod from, DexMethod to) {
+      methodMapBuilder.put(from, to);
+    }
+
+    public void merge(VerticalClassMergerGraphLense.Builder builder) {
+      fieldMapBuilder.putAll(builder.fieldMapBuilder.build());
+      methodMapBuilder.putAll(builder.methodMapBuilder.build());
+    }
   }
 }