Correctly generate applied graph lens for rewritten files

Bug: 168690628
Bug: 165000217
Change-Id: I41e5d22373e37242f36dc3633279ff27fff58fd9
diff --git a/src/main/java/com/android/tools/r8/DataEntryResource.java b/src/main/java/com/android/tools/r8/DataEntryResource.java
index 6b8f9c5..0aa263b 100644
--- a/src/main/java/com/android/tools/r8/DataEntryResource.java
+++ b/src/main/java/com/android/tools/r8/DataEntryResource.java
@@ -26,6 +26,15 @@
     return new ByteDataEntryResource(bytes, name, origin);
   }
 
+  static DataEntryResource fromString(String name, Origin origin, String... lines) {
+    StringBuilder sb = new StringBuilder();
+    for (String line : lines) {
+      sb.append(line);
+      sb.append("\n");
+    }
+    return new ByteDataEntryResource(sb.toString().getBytes(), name, origin);
+  }
+
   static DataEntryResource fromFile(Path dir, Path file) {
     return new LocalDataEntryResource(dir.resolve(file).toFile(),
         file.toString().replace(File.separatorChar, SEPARATOR));
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index dad6c63..ebc0bc6 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -532,7 +532,7 @@
                   mainDexTracingResult);
           VerticalClassMergerGraphLens lens = verticalClassMerger.run();
           if (lens != null) {
-            appView.setVerticallyMergedClasses(verticalClassMerger.getMergedClasses());
+            appView.setVerticallyMergedClasses(lens.getMergedClasses());
             appView.rewriteWithLens(lens);
             runtimeTypeCheckInfo = runtimeTypeCheckInfo.rewriteWithLens(lens);
           }
diff --git a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
index ae54120..bae65e8 100644
--- a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
@@ -5,11 +5,12 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
-import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
 import com.android.tools.r8.utils.MapUtils;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
@@ -77,23 +78,11 @@
   private void recordOriginalTypeNames(
       DexProgramClass clazz, AppView<? extends AppInfoWithClassHierarchy> appView) {
     DexType type = clazz.getType();
-    VerticallyMergedClasses verticallyMergedClasses = appView.verticallyMergedClasses();
-    if (verticallyMergedClasses != null && verticallyMergedClasses.hasBeenMergedIntoSubtype(type)) {
-      return;
-    }
 
-    DexType original = appView.graphLens().getOriginalType(type);
-    if (verticallyMergedClasses != null) {
-      List<DexType> sources = verticallyMergedClasses.getSourcesFor(type);
-      if (!sources.isEmpty()) {
-        renamedTypeNames.put(original, type);
-        sources.forEach(source -> renamedTypeNames.put(source, type));
-        return;
-      }
-    }
-
-    if (original != type) {
-      renamedTypeNames.put(original, type);
+    List<DexType> originalTypes = Lists.newArrayList(appView.graphLens().getOriginalTypes(type));
+    boolean isIdentity = originalTypes.size() == 1 && originalTypes.get(0) == type;
+    if (!isIdentity) {
+      originalTypes.forEach(originalType -> renamedTypeNames.put(originalType, type));
     }
   }
 
@@ -112,6 +101,15 @@
   }
 
   @Override
+  public Iterable<DexType> getOriginalTypes(DexType type) {
+    Set<DexType> originalTypes = renamedTypeNames.getKeys(type);
+    if (originalTypes == null) {
+      return ImmutableList.of(type);
+    }
+    return originalTypes;
+  }
+
+  @Override
   public DexField getOriginalFieldSignature(DexField field) {
     return originalFieldSignatures.getOrDefault(field, field);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index ac9b854..d00b9d9 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.ir.desugar.InterfaceProcessor.InterfaceProcessorNestedGraphLens;
 import com.android.tools.r8.shaking.KeepInfoCollection;
 import com.android.tools.r8.utils.Action;
+import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.BiMap;
@@ -273,6 +274,8 @@
 
   public abstract DexType getOriginalType(DexType type);
 
+  public abstract Iterable<DexType> getOriginalTypes(DexType type);
+
   public abstract DexField getOriginalFieldSignature(DexField field);
 
   public abstract DexMethod getOriginalMethodSignature(DexMethod method);
@@ -761,6 +764,11 @@
     }
 
     @Override
+    public Iterable<DexType> getOriginalTypes(DexType type) {
+      return IterableUtils.singleton(type);
+    }
+
+    @Override
     public DexField getOriginalFieldSignature(DexField field) {
       return field;
     }
@@ -845,6 +853,11 @@
     }
 
     @Override
+    public Iterable<DexType> getOriginalTypes(DexType type) {
+      return getPrevious().getOriginalTypes(type);
+    }
+
+    @Override
     public DexField getOriginalFieldSignature(DexField field) {
       return getPrevious().getOriginalFieldSignature(field);
     }
@@ -965,9 +978,22 @@
       return new Builder();
     }
 
+    protected DexType internalGetOriginalType(DexType previous) {
+      return previous;
+    }
+
+    protected Iterable<DexType> internalGetOriginalTypes(DexType previous) {
+      return IterableUtils.singleton(internalGetOriginalType(previous));
+    }
+
     @Override
     public DexType getOriginalType(DexType type) {
-      return getPrevious().getOriginalType(type);
+      return getPrevious().getOriginalType(internalGetOriginalType(type));
+    }
+
+    @Override
+    public Iterable<DexType> getOriginalTypes(DexType type) {
+      return IterableUtils.flatMap(internalGetOriginalTypes(type), getPrevious()::getOriginalTypes);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
index f76f9e7..ed69ace 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
@@ -7,27 +7,28 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Maps;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Map;
+import java.util.Set;
 
 public class VerticallyMergedClasses implements MergedClasses {
 
   private final Map<DexType, DexType> mergedClasses;
-  private final Map<DexType, List<DexType>> sources;
+  private final Map<DexType, Set<DexType>> mergedClassesInverse;
 
-  public VerticallyMergedClasses(Map<DexType, DexType> mergedClasses) {
-    Map<DexType, List<DexType>> sources = Maps.newIdentityHashMap();
-    mergedClasses.forEach(
-        (source, target) -> sources.computeIfAbsent(target, key -> new ArrayList<>()).add(source));
+  public VerticallyMergedClasses(
+      Map<DexType, DexType> mergedClasses, Map<DexType, Set<DexType>> mergedClassesInverse) {
     this.mergedClasses = mergedClasses;
-    this.sources = sources;
+    this.mergedClassesInverse = mergedClassesInverse;
   }
 
-  public List<DexType> getSourcesFor(DexType type) {
-    return sources.getOrDefault(type, ImmutableList.of());
+  public Map<DexType, DexType> getForwardMap() {
+    return mergedClasses;
+  }
+
+  public Collection<DexType> getSourcesFor(DexType type) {
+    return mergedClassesInverse.getOrDefault(type, Collections.emptySet());
   }
 
   public DexType getTargetFor(DexType type) {
@@ -45,7 +46,7 @@
 
   @Override
   public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) {
-    for (List<DexType> sourcesForTarget : sources.values()) {
+    for (Collection<DexType> sourcesForTarget : mergedClassesInverse.values()) {
       for (DexType source : sourcesForTarget) {
         assert appView.appInfo().wasPruned(source)
             : "Expected vertically merged class `" + source.toSourceString() + "` to be absent";
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index 5f5a62b..867725b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -7,9 +7,11 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
 import com.android.tools.r8.ir.conversion.ExtraParameter;
+import com.android.tools.r8.utils.IterableUtils;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import java.util.ArrayList;
@@ -24,10 +26,11 @@
   private final AppView<?> appView;
   private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters;
   private final Map<DexMethod, DexMethod> originalConstructorSignatures;
+  private final HorizontallyMergedClasses mergedClasses;
 
   private HorizontalClassMergerGraphLens(
       AppView<?> appView,
-      HorizontallyMergedClasses horizontallyMergedClasses,
+      HorizontallyMergedClasses mergedClasses,
       Map<DexMethod, List<ExtraParameter>> methodExtraParameters,
       Map<DexField, DexField> fieldMap,
       Map<DexMethod, DexMethod> methodMap,
@@ -36,7 +39,7 @@
       Map<DexMethod, DexMethod> originalConstructorSignatures,
       GraphLens previousLens) {
     super(
-        horizontallyMergedClasses.getForwardMap(),
+        mergedClasses.getForwardMap(),
         methodMap,
         fieldMap,
         originalFieldSignatures,
@@ -46,6 +49,12 @@
     this.appView = appView;
     this.methodExtraParameters = methodExtraParameters;
     this.originalConstructorSignatures = originalConstructorSignatures;
+    this.mergedClasses = mergedClasses;
+  }
+
+  @Override
+  protected Iterable<DexType> internalGetOriginalTypes(DexType previous) {
+    return IterableUtils.prependSingleton(previous, mergedClasses.getSourcesFor(previous));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
index 653e31a..080102a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
@@ -34,6 +34,10 @@
     return mergedClasses.keySet();
   }
 
+  public Set<DexType> getSourcesFor(DexType type) {
+    return mergedClasses.getKeys(type);
+  }
+
   public boolean hasBeenMergedIntoDifferentType(DexType type) {
     return mergedClasses.hasKey(type);
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
index 7e66078..9b12469 100644
--- a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
@@ -60,6 +60,11 @@
   }
 
   @Override
+  public Iterable<DexType> getOriginalTypes(DexType type) {
+    return getPrevious().getOriginalTypes(type);
+  }
+
+  @Override
   public DexField getOriginalFieldSignature(DexField field) {
     return getPrevious().getOriginalFieldSignature(field);
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index 3f625f2..02b56e3 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -84,6 +84,11 @@
   }
 
   @Override
+  public Iterable<DexType> getOriginalTypes(DexType type) {
+    return getPrevious().getOriginalTypes(type);
+  }
+
+  @Override
   public DexField getOriginalFieldSignature(DexField field) {
     return getPrevious().getOriginalFieldSignature(field);
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
index 9e23dd0..867ec0b 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -52,6 +52,11 @@
   }
 
   @Override
+  public Iterable<DexType> getOriginalTypes(DexType type) {
+    return getPrevious().getOriginalTypes(type);
+  }
+
+  @Override
   public DexField getOriginalFieldSignature(DexField field) {
     return getPrevious().getOriginalFieldSignature(field);
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
index 9fe3e29..6a6c3bb 100644
--- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
@@ -49,6 +49,11 @@
   }
 
   @Override
+  public Iterable<DexType> getOriginalTypes(DexType type) {
+    return getPrevious().getOriginalTypes(type);
+  }
+
+  @Override
   public DexField getOriginalFieldSignature(DexField field) {
     return getPrevious().getOriginalFieldSignature(field);
   }
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 6100000..de95f92 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -241,8 +241,8 @@
     initializeMergeCandidates(classes);
   }
 
-  public VerticallyMergedClasses getMergedClasses() {
-    return new VerticallyMergedClasses(mergedClasses);
+  private VerticallyMergedClasses getMergedClasses() {
+    return new VerticallyMergedClasses(mergedClasses, mergedClassesInverse);
   }
 
   private void initializeMergeCandidates(Iterable<DexProgramClass> classes) {
@@ -1484,7 +1484,7 @@
       for (SynthesizedBridgeCode synthesizedBridge : synthesizedBridges) {
         synthesizedBridge.updateMethodSignatures(this::fixupMethod);
       }
-      VerticalClassMergerGraphLens lens = lensBuilder.build(appView, mergedClasses);
+      VerticalClassMergerGraphLens lens = lensBuilder.build(appView, getMergedClasses());
       if (lens != null) {
         new AnnotationFixer(lens).run(appView.appInfo().classes());
       }
@@ -1728,6 +1728,11 @@
     }
 
     @Override
+    public Iterable<DexType> getOriginalTypes(DexType type) {
+      throw new Unreachable();
+    }
+
+    @Override
     public DexField getOriginalFieldSignature(DexField field) {
       throw new Unreachable();
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
index a95cfb4..a5481fd 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
@@ -14,10 +14,14 @@
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
 import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.utils.IterableUtils;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import java.util.Collection;
 import java.util.IdentityHashMap;
 import java.util.Map;
 import java.util.Set;
@@ -55,6 +59,7 @@
 
   private final AppView<?> appView;
 
+  private VerticallyMergedClasses mergedClasses;
   private final Map<DexType, Map<DexMethod, GraphLensLookupResultProvider>>
       contextualVirtualToDirectMethodMaps;
   private Set<DexMethod> mergedMethods;
@@ -62,7 +67,7 @@
 
   private VerticalClassMergerGraphLens(
       AppView<?> appView,
-      Map<DexType, DexType> typeMap,
+      VerticallyMergedClasses mergedClasses,
       Map<DexField, DexField> fieldMap,
       Map<DexMethod, DexMethod> methodMap,
       Set<DexMethod> mergedMethods,
@@ -73,7 +78,7 @@
       Map<DexMethod, DexMethod> originalMethodSignaturesForBridges,
       GraphLens previousLens) {
     super(
-        typeMap,
+        mergedClasses.getForwardMap(),
         methodMap,
         fieldMap,
         originalFieldSignatures,
@@ -81,14 +86,24 @@
         previousLens,
         appView.dexItemFactory());
     this.appView = appView;
+    this.mergedClasses = mergedClasses;
     this.contextualVirtualToDirectMethodMaps = contextualVirtualToDirectMethodMaps;
     this.mergedMethods = mergedMethods;
     this.originalMethodSignaturesForBridges = originalMethodSignaturesForBridges;
   }
 
+  public VerticallyMergedClasses getMergedClasses() {
+    return mergedClasses;
+  }
+
   @Override
-  public DexType getOriginalType(DexType type) {
-    return getPrevious().getOriginalType(type);
+  protected Iterable<DexType> internalGetOriginalTypes(DexType previous) {
+    Collection<DexType> originalTypes = mergedClasses.getSourcesFor(previous);
+    Iterable<DexType> currentType = IterableUtils.singleton(previous);
+    if (originalTypes == null) {
+      return currentType;
+    }
+    return Iterables.concat(currentType, originalTypes);
   }
 
   @Override
@@ -219,8 +234,8 @@
     }
 
     public VerticalClassMergerGraphLens build(
-        AppView<?> appView, Map<DexType, DexType> mergedClasses) {
-      if (mergedClasses.isEmpty()) {
+        AppView<?> appView, VerticallyMergedClasses mergedClasses) {
+      if (mergedClasses.getForwardMap().isEmpty()) {
         return null;
       }
       BiMap<DexField, DexField> originalFieldSignatures = fieldMap.inverse();
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
index 15a8198..57cf52b 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -4,9 +4,12 @@
 
 package com.android.tools.r8.utils;
 
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.function.Function;
 import java.util.function.Predicate;
 
 public class IterableUtils {
@@ -53,4 +56,25 @@
   public static <T> boolean isEmpty(Iterable<T> iterable) {
     return !iterable.iterator().hasNext();
   }
+
+  public static <T> Iterable<T> singleton(T element) {
+    return () -> Iterators.singletonIterator(element);
+  }
+
+  public static <T> Iterable<T> prependSingleton(T t, Iterable<T> iterable) {
+    return Iterables.concat(singleton(t), iterable);
+  }
+
+  public static <T, U> Iterable<U> flatMap(
+      Iterable<T> iterable, Function<? super T, Iterable<U>> map) {
+    return Iterables.concat(Iterables.transform(iterable, val -> map.apply(val)));
+  }
+
+  public static <T> Iterable<T> emptyIf(Iterable<T> iterable, boolean condition) {
+    if (condition) {
+      return Collections.emptySet();
+    } else {
+      return iterable;
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
index 3c3d37c..7410339 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
@@ -45,6 +45,10 @@
     return inverse.getOrDefault(value, Collections.emptySet());
   }
 
+  public Set<K> getKeysOrNull(V value) {
+    return inverse.get(value);
+  }
+
   public boolean isEmpty() {
     return backing.isEmpty();
   }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptResourceFileContentsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptResourceFileContentsTest.java
new file mode 100644
index 0000000..2c4ac9d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptResourceFileContentsTest.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.EmptyClassTest.B;
+import com.android.tools.r8.classmerging.horizontal.EmptyClassTest.Main;
+import com.android.tools.r8.classmerging.horizontal.ServiceLoaderTest.A;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.DataResourceConsumerForTesting;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class AdaptResourceFileContentsTest extends HorizontalClassMergingTestBase {
+  public AdaptResourceFileContentsTest(
+      TestParameters parameters, boolean enableHorizontalClassMerging) {
+    super(parameters, enableHorizontalClassMerging);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting();
+    CodeInspector codeInspector =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(getClass())
+            .addKeepMainRule(Main.class)
+            .addOptionsModification(
+                options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            .addOptionsModification(options -> options.dataResourceConsumer = dataResourceConsumer)
+            .enableNeverClassInliningAnnotations()
+            .addDataEntryResources(
+                DataEntryResource.fromString(
+                    "foo.txt", Origin.unknown(), A.class.getTypeName(), B.class.getTypeName()))
+            .addKeepRules("-adaptresourcefilecontents foo.txt")
+            .setMinApi(parameters.getApiLevel())
+            .addHorizontallyMergedClassesInspectorIf(
+                enableHorizontalClassMerging,
+                inspector -> inspector.assertMergedInto(B.class, A.class))
+            .compile()
+            .run(parameters.getRuntime(), Main.class)
+            .assertSuccessWithOutputLines("a", "b")
+            .inspector();
+
+    ClassSubject aClassSubject = codeInspector.clazz(A.class);
+    assertThat(aClassSubject, isPresent());
+
+    ClassSubject bClassSubject = codeInspector.clazz(B.class);
+    assertThat(bClassSubject, notIf(isPresent(), enableHorizontalClassMerging));
+
+    // Check that the class name has been rewritten.
+    String newClassBName =
+        (enableHorizontalClassMerging ? aClassSubject : bClassSubject).getFinalName();
+    assertEquals(
+        dataResourceConsumer.get("foo.txt"),
+        ImmutableList.of(aClassSubject.getFinalName(), newClassBName));
+  }
+
+  @NeverClassInline
+  public static class A {
+    public A() {
+      System.out.println("a");
+    }
+  }
+
+  @NeverClassInline
+  public static class B {
+    public B() {
+      System.out.println("b");
+    }
+  }
+
+  public static class Main {
+    public static void main(String[] args) {
+      new A();
+      new B();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java
new file mode 100644
index 0000000..ea50082
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.DataResourceConsumerForTesting;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class AdaptVerticallyMergedResourceFileContentsTest extends HorizontalClassMergingTestBase {
+  public AdaptVerticallyMergedResourceFileContentsTest(
+      TestParameters parameters, boolean enableHorizontalClassMerging) {
+    super(parameters, enableHorizontalClassMerging);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting();
+    CodeInspector codeInspector =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(getClass())
+            .addKeepMainRule(Main.class)
+            .addOptionsModification(
+                options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            .addOptionsModification(options -> options.dataResourceConsumer = dataResourceConsumer)
+            .enableNeverClassInliningAnnotations()
+            .addDataEntryResources(
+                DataEntryResource.fromString(
+                    "foo.txt",
+                    Origin.unknown(),
+                    Parent.class.getTypeName(),
+                    A.class.getTypeName(),
+                    B.class.getTypeName()))
+            .addKeepRules("-adaptresourcefilecontents foo.txt")
+            .setMinApi(parameters.getApiLevel())
+            .addHorizontallyMergedClassesInspectorIf(
+                enableHorizontalClassMerging,
+                inspector -> inspector.assertMergedInto(B.class, A.class))
+            .compile()
+            .run(parameters.getRuntime(), Main.class)
+            .assertSuccessWithOutputLines("a", "foo parent", "b")
+            .inspector();
+
+    assertThat(codeInspector.clazz(Parent.class), not(isPresent()));
+
+    ClassSubject aClassSubject = codeInspector.clazz(A.class);
+    assertThat(aClassSubject, isPresent());
+
+    ClassSubject bClassSubject = codeInspector.clazz(B.class);
+    assertThat(bClassSubject, notIf(isPresent(), enableHorizontalClassMerging));
+
+    // Check that the class name has been rewritten.
+    String newClassName =
+        (enableHorizontalClassMerging ? aClassSubject : bClassSubject).getFinalName();
+    assertEquals(
+        dataResourceConsumer.get("foo.txt"),
+        ImmutableList.of(aClassSubject.getFinalName(), aClassSubject.getFinalName(), newClassName));
+  }
+
+  @NeverClassInline
+  public static class Parent {
+    @NeverInline
+    public void foo() {
+      System.out.println("foo parent");
+    }
+  }
+
+  @NeverClassInline
+  public static class A extends Parent {
+    public A() {
+      System.out.println("a");
+    }
+  }
+
+  @NeverClassInline
+  public static class B {
+    public B() {
+      System.out.println("b");
+    }
+  }
+
+  public static class Main {
+    @NeverInline
+    public static void parent(Parent p) {
+      p.foo();
+    }
+
+    public static void main(String[] args) {
+      parent(new A());
+      new B();
+    }
+  }
+}