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();
+ }
+ }
+}