Merge commit '961e3fb4895d6759fe87a26bf579373777508f3c' into dev-release
diff --git a/build.gradle b/build.gradle
index b7c7830..99676ef 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1006,9 +1006,7 @@
task R8Lib {
dependsOn r8LibCreateTask(
"Main",
- ["src/main/keep.txt",
- "src/main/keep-applymapping.txt",
- generateR8LibKeepRules.outputs.files[0]],
+ ["src/main/keep.txt", generateR8LibKeepRules.outputs.files[0]],
r8NoManifestWithRelocatedDeps,
r8LibPath,
).dependsOn(generateR8LibKeepRules)
@@ -1019,7 +1017,7 @@
task R8LibNoDeps {
dependsOn r8LibCreateTask(
"NoDeps",
- ["src/main/keep.txt", "src/main/keep-applymapping.txt"],
+ ["src/main/keep.txt"],
r8NoManifestWithoutDeps,
r8LibExludeDepsPath,
"--release",
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 16d3642..92d9c12 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -410,19 +410,16 @@
// Recompute the subtyping information.
Set<DexType> removedClasses = pruner.getRemovedClasses();
- appView.setAppInfo(
- appView
- .appInfo()
- .withLiveness()
- .prunedCopyFrom(
- prunedApp,
- removedClasses,
- pruner.getMethodsToKeepForConfigurationDebugging()));
- appView.setAppServices(appView.appServices().prunedCopy(removedClasses));
+ appView.removePrunedClasses(
+ prunedApp, removedClasses, pruner.getMethodsToKeepForConfigurationDebugging());
new AbstractMethodRemover(
appViewWithLiveness, appViewWithLiveness.appInfo().computeSubtypingInfo())
.run();
+ if (appView.options().protoShrinking().isProtoShrinkingEnabled()) {
+ appView.protoShrinker().enumProtoShrinker.clearDeadEnumLiteMaps();
+ }
+
AnnotationRemover annotationRemover =
annotationRemoverBuilder
.computeClassesToRetainInnerClassAttributeFor(appViewWithLiveness)
@@ -571,7 +568,6 @@
appView.appInfo().app().asDirect().builder();
HorizontalClassMergerGraphLens lens = merger.run(appBuilder);
if (lens != null) {
- appView.setHorizontallyMergedClasses(lens.getHorizontallyMergedClasses());
appView.rewriteWithLensAndApplication(lens, appBuilder.build());
}
timing.end();
@@ -592,8 +588,6 @@
appViewWithLiveness.setAppInfo(new EnumValueInfoMapCollector(appViewWithLiveness).run());
}
- appView.setAppServices(appView.appServices().rewrittenWithLens(appView.graphLens()));
-
// Collect the already pruned types before creating a new app info without liveness.
// TODO: we should avoid removing liveness.
Set<DexType> prunedTypes = appView.withLiveness().appInfo().getPrunedTypes();
@@ -731,14 +725,11 @@
ExceptionUtils.withFinishedResourceHandler(
options.reporter, options.usageInformationConsumer);
}
- appViewWithLiveness.setAppInfo(
- appViewWithLiveness
- .appInfo()
- .prunedCopyFrom(
- application,
- CollectionUtils.mergeSets(prunedTypes, removedClasses),
- pruner.getMethodsToKeepForConfigurationDebugging()));
- appView.setAppServices(appView.appServices().prunedCopy(removedClasses));
+
+ appView.removePrunedClasses(
+ application,
+ CollectionUtils.mergeSets(prunedTypes, removedClasses),
+ pruner.getMethodsToKeepForConfigurationDebugging());
new BridgeHoisting(appViewWithLiveness).run();
@@ -779,6 +770,8 @@
}
if (appView.options().protoShrinking().isProtoShrinkingEnabled()) {
+ appView.protoShrinker().enumProtoShrinker.verifyDeadEnumLiteMapsAreDead();
+
IRConverter converter = new IRConverter(appView, timing, null, mainDexTracingResult);
// If proto shrinking is enabled, we need to reprocess every dynamicMethod(). This ensures
@@ -814,8 +807,7 @@
appView.setGraphLens(memberRebindingLens);
// Perform repackaging.
- // TODO(b/165783399): Consider making repacking available without minification.
- if (options.isMinifying() && options.testing.enableExperimentalRepackaging) {
+ if (options.isRepackagingEnabled() && options.testing.enableExperimentalRepackaging) {
DirectMappedDexApplication.Builder appBuilder =
appView.appInfo().app().asDirect().builder();
// TODO(b/165783399): We need to deal with non-rebound member references in the writer,
diff --git a/src/main/java/com/android/tools/r8/graph/AppServices.java b/src/main/java/com/android/tools/r8/graph/AppServices.java
index de7599a..d9ad820 100644
--- a/src/main/java/com/android/tools/r8/graph/AppServices.java
+++ b/src/main/java/com/android/tools/r8/graph/AppServices.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
@@ -93,12 +94,12 @@
}
// Check if service is defined feature
DexProgramClass serviceClass = appView.definitionForProgramType(serviceType);
- if (classToFeatureSplitMap.isInFeature(serviceClass)) {
+ if (serviceClass != null && classToFeatureSplitMap.isInFeature(serviceClass)) {
return true;
}
- for (DexType dexType : featureImplementations.get(FeatureSplit.BASE)) {
- DexProgramClass implementationClass = appView.definitionForProgramType(dexType);
- if (classToFeatureSplitMap.isInFeature(implementationClass)) {
+ for (DexType implementationType : featureImplementations.get(FeatureSplit.BASE)) {
+ DexProgramClass implementationClass = appView.definitionForProgramType(implementationType);
+ if (implementationClass != null && classToFeatureSplitMap.isInFeature(implementationClass)) {
return true;
}
}
@@ -187,19 +188,20 @@
public static class Builder {
private final AppView<?> appView;
+ private final InternalOptions options;
private final Map<DexType, Map<FeatureSplit, List<DexType>>> services = new LinkedHashMap<>();
private Builder(AppView<?> appView) {
this.appView = appView;
+ this.options = appView.options();
}
public AppServices build() {
for (DataResourceProvider provider : appView.appInfo().app().dataResourceProviders) {
readServices(provider, FeatureSplit.BASE);
}
- if (appView.options().featureSplitConfiguration != null) {
- List<FeatureSplit> featureSplits =
- appView.options().featureSplitConfiguration.getFeatureSplits();
+ if (options.featureSplitConfiguration != null) {
+ List<FeatureSplit> featureSplits = options.featureSplitConfiguration.getFeatureSplits();
for (FeatureSplit featureSplit : featureSplits) {
for (ProgramResourceProvider provider : featureSplit.getProgramResourceProviders()) {
DataResourceProvider dataResourceProvider = provider.getDataResourceProvider();
@@ -238,28 +240,45 @@
public void visit(DataEntryResource file) {
try {
String name = file.getName();
- if (name.startsWith(SERVICE_DIRECTORY_NAME)) {
- String serviceName = name.substring(SERVICE_DIRECTORY_NAME.length());
- if (DescriptorUtils.isValidJavaType(serviceName)) {
- String serviceDescriptor = DescriptorUtils.javaTypeToDescriptor(serviceName);
- DexType serviceType = appView.dexItemFactory().createType(serviceDescriptor);
- byte[] bytes = ByteStreams.toByteArray(file.getByteStream());
- String contents = new String(bytes, Charset.defaultCharset());
- Map<FeatureSplit, List<DexType>> featureSplitImplementations =
- services.computeIfAbsent(serviceType, k -> new LinkedHashMap<>());
- List<DexType> serviceImplementations =
- featureSplitImplementations.computeIfAbsent(featureSplit, f -> new ArrayList<>());
- readServiceImplementationsForService(
- contents, file.getOrigin(), serviceImplementations);
+ if (!name.startsWith(SERVICE_DIRECTORY_NAME)) {
+ return;
+ }
+ String serviceName = name.substring(SERVICE_DIRECTORY_NAME.length());
+ if (!DescriptorUtils.isValidJavaType(serviceName)) {
+ return;
+ }
+ String serviceDescriptor = DescriptorUtils.javaTypeToDescriptor(serviceName);
+ DexType serviceType = appView.dexItemFactory().createType(serviceDescriptor);
+ if (appView.enableWholeProgramOptimizations()) {
+ DexClass serviceClass =
+ appView.appInfo().definitionForWithoutExistenceAssert(serviceType);
+ if (serviceClass == null) {
+ warn(
+ "Unexpected reference to missing service class: META-INF/services/"
+ + serviceType.toSourceString()
+ + ".",
+ serviceType,
+ file.getOrigin());
}
}
+ byte[] bytes = ByteStreams.toByteArray(file.getByteStream());
+ String contents = new String(bytes, Charset.defaultCharset());
+ Map<FeatureSplit, List<DexType>> featureSplitImplementations =
+ services.computeIfAbsent(serviceType, k -> new LinkedHashMap<>());
+ List<DexType> serviceImplementations =
+ featureSplitImplementations.computeIfAbsent(featureSplit, f -> new ArrayList<>());
+ readServiceImplementationsForService(
+ contents, file.getOrigin(), serviceType, serviceImplementations);
} catch (IOException | ResourceException e) {
throw new CompilationError(e.getMessage(), e);
}
}
private void readServiceImplementationsForService(
- String contents, Origin origin, List<DexType> serviceImplementations) {
+ String contents,
+ Origin origin,
+ DexType serviceType,
+ List<DexType> serviceImplementations) {
if (contents != null) {
StringUtils.splitLines(contents).stream()
.map(String::trim)
@@ -272,17 +291,33 @@
serviceImplementationType -> {
if (!serviceImplementationType.isClassType()) {
// Should never happen.
- appView
- .options()
- .reporter
- .warning(
- new StringDiagnostic(
- "Unexpected service implementation found in META-INF/services/: `"
- + serviceImplementationType.toSourceString()
- + "`.",
- origin));
+ warn(
+ "Unexpected service implementation found in META-INF/services/"
+ + serviceType.toSourceString()
+ + ": "
+ + serviceImplementationType.toSourceString()
+ + ".",
+ serviceImplementationType,
+ origin);
return false;
}
+ if (appView.enableWholeProgramOptimizations()) {
+ DexClass serviceImplementationClass =
+ appView
+ .appInfo()
+ .definitionForWithoutExistenceAssert(serviceImplementationType);
+ if (serviceImplementationClass == null) {
+ warn(
+ "Unexpected reference to missing service implementation class in "
+ + "META-INF/services/"
+ + serviceType.toSourceString()
+ + ": "
+ + serviceImplementationType.toSourceString()
+ + ".",
+ serviceImplementationType,
+ origin);
+ }
+ }
// Only keep one of each implementation type in the list.
return !serviceImplementations.contains(serviceImplementationType);
})
@@ -294,6 +329,12 @@
int commentCharIndex = line.indexOf('#');
return commentCharIndex > -1 ? line.substring(0, commentCharIndex) : line;
}
+
+ private void warn(String message, DexType type, Origin origin) {
+ if (!options.getProguardConfiguration().getDontWarnPatterns().matches(type)) {
+ options.reporter.warning(new StringDiagnostic(message, origin));
+ }
+ }
}
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 3850a41..282f852 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -34,6 +34,7 @@
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
@@ -521,6 +522,32 @@
return !cfByteCodePassThrough.isEmpty();
}
+ public void removePrunedClasses(
+ DirectMappedDexApplication prunedApp,
+ Set<DexType> removedClasses,
+ Collection<DexMethod> methodsToKeepForConfigurationDebugging) {
+ assert enableWholeProgramOptimizations();
+ assert appInfo().hasLiveness();
+ removePrunedClasses(
+ prunedApp, removedClasses, methodsToKeepForConfigurationDebugging, withLiveness());
+ }
+
+ private static void removePrunedClasses(
+ DirectMappedDexApplication prunedApp,
+ Set<DexType> removedClasses,
+ Collection<DexMethod> methodsToKeepForConfigurationDebugging,
+ AppView<AppInfoWithLiveness> appView) {
+ if (removedClasses.isEmpty() && !appView.options().configurationDebugging) {
+ assert appView.appInfo.app() == prunedApp;
+ return;
+ }
+ appView.setAppInfo(
+ appView
+ .appInfo()
+ .prunedCopyFrom(prunedApp, removedClasses, methodsToKeepForConfigurationDebugging));
+ appView.setAppServices(appView.appServices().prunedCopy(removedClasses));
+ }
+
public void rewriteWithLens(NonIdentityGraphLens lens) {
if (lens != null) {
rewriteWithLens(lens, appInfo().app().asDirect(), withLiveness(), lens.getPrevious());
@@ -566,6 +593,7 @@
GraphLens.getIdentityLens(),
() -> {
appView.setAppInfo(appView.appInfo().rewrittenWithLens(application, lens));
+ appView.setAppServices(appView.appServices().rewrittenWithLens(lens));
if (appView.hasInitClassLens()) {
appView.setInitClassLens(appView.initClassLens().rewrittenWithLens(lens));
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 18ebdb4..cfccc2b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -1778,23 +1778,22 @@
return str.replace('.', '$');
}
- public DexString createFreshMemberString(String baseName, DexType holder, int index) {
- StringBuilder sb =
- new StringBuilder()
- .append(baseName)
- .append('$')
- .append(escapeMemberString(holder.toSourceString()));
+ public String createMemberString(String baseName, DexType holder, int index) {
+ StringBuilder sb = new StringBuilder().append(baseName);
+ if (holder != null) {
+ sb.append('$').append(escapeMemberString(holder.toSourceString()));
+ }
if (index > 0) {
sb.append("$").append(index);
}
- return createString(sb.toString());
+ return sb.toString();
}
/**
* Find a fresh method name that is not used by any other method. The method name takes the form
- * "basename$holdername$index".
+ * "basename$holdername" or "basename$holdername$index".
*
* @param tryString callback to check if the method name is in use.
*/
@@ -1802,7 +1801,7 @@
String baseName, DexType holder, Function<DexString, Optional<T>> tryString) {
int index = 0;
while (true) {
- DexString name = createFreshMemberString(baseName, holder, index++);
+ DexString name = createString(createMemberString(baseName, holder, index++));
Optional<T> result = tryString.apply(name);
if (result.isPresent()) {
return result.get();
@@ -1811,6 +1810,30 @@
}
/**
+ * Find a fresh method name that is not in the string pool. The name takes the form
+ * "basename$holdername" or "basename$holdername$index".
+ */
+ public DexString createGloballyFreshMemberString(String baseName, DexType holder) {
+ assert !sorted;
+ int index = 0;
+ while (true) {
+ String name = createMemberString(baseName, holder, index++);
+ DexString dexName = lookupString(name);
+ if (dexName == null) {
+ return createString(name);
+ }
+ }
+ }
+
+ /**
+ * Find a fresh method name that is not in the string pool. The name takes the form "basename" or
+ * "basename$index".
+ */
+ public DexString createGloballyFreshMemberString(String baseName) {
+ return createGloballyFreshMemberString(baseName, null);
+ }
+
+ /**
* Tries to find a method name for insertion into the class {@code target} of the form
* baseName$holder$n, where {@code baseName} and {@code holder} are supplied by the user, and
* {@code n} is picked to be the first number so that {@code isFresh.apply(method)} returns {@code
@@ -1839,6 +1862,26 @@
return method;
}
+ public DexMethod createInstanceInitializerWithFreshProto(
+ DexMethod method, DexType extraType, Predicate<DexMethod> isFresh) {
+ assert method.isInstanceInitializer(this);
+ return createInstanceInitializerWithFreshProto(
+ method.proto,
+ extraType,
+ proto -> Optional.of(createMethod(method.holder, proto, method.name)).filter(isFresh));
+ }
+
+ private DexMethod createInstanceInitializerWithFreshProto(
+ DexProto proto, DexType extraType, Function<DexProto, Optional<DexMethod>> isFresh) {
+ while (true) {
+ Optional<DexMethod> object = isFresh.apply(proto);
+ if (object.isPresent()) {
+ return object.get();
+ }
+ proto = appendTypeToProto(proto, extraType);
+ }
+ }
+
public DexString lookupString(int size, byte[] content) {
return strings.get(new DexString(size, content));
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index b3e11f5..128b353 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -247,4 +247,8 @@
public DexMethod withHolder(DexType holder, DexItemFactory dexItemFactory) {
return dexItemFactory.createMethod(holder, proto, name);
}
+
+ public DexMethod withName(DexString name, DexItemFactory dexItemFactory) {
+ return dexItemFactory.createMethod(holder, proto, name);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProto.java b/src/main/java/com/android/tools/r8/graph/DexProto.java
index a0cacd9..1eefc66 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProto.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProto.java
@@ -5,7 +5,9 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.naming.NamingLens;
+import com.google.common.collect.Iterables;
import com.google.common.hash.Hasher;
+import java.util.Collections;
import java.util.function.Consumer;
public class DexProto extends IndexedDexItem implements PresortedComparable<DexProto> {
@@ -22,6 +24,18 @@
this.parameters = parameters;
}
+ public Iterable<DexType> getParameterBaseTypes(DexItemFactory dexItemFactory) {
+ return Iterables.transform(parameters, type -> type.toBaseType(dexItemFactory));
+ }
+
+ public Iterable<DexType> getBaseTypes(DexItemFactory dexItemFactory) {
+ return Iterables.transform(getTypes(), type -> type.toBaseType(dexItemFactory));
+ }
+
+ public Iterable<DexType> getTypes() {
+ return Iterables.concat(Collections.singleton(returnType), parameters);
+ }
+
public void forEachType(Consumer<DexType> consumer) {
consumer.accept(returnType);
parameters.forEach(consumer);
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeList.java b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
index ae25597..87080e8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeList.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
@@ -8,11 +8,13 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.ArrayUtils;
+import com.google.common.collect.Iterators;
import java.util.Arrays;
+import java.util.Iterator;
import java.util.function.Consumer;
import java.util.stream.Stream;
-public class DexTypeList extends DexItem {
+public class DexTypeList extends DexItem implements Iterable<DexType> {
private static final DexTypeList theEmptyTypeList = new DexTypeList();
@@ -35,7 +37,8 @@
return ArrayUtils.contains(values, type);
}
- public void forEach(Consumer<DexType> consumer) {
+ @Override
+ public void forEach(Consumer<? super DexType> consumer) {
for (DexType value : values) {
consumer.accept(value);
}
@@ -121,4 +124,9 @@
}
throw new Unreachable();
}
+
+ @Override
+ public Iterator<DexType> iterator() {
+ return Iterators.forArray(values);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java b/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
index d95a723..a74f0c4 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
@@ -11,6 +11,7 @@
import it.unimi.dsi.fastutil.objects.Object2ReferenceLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ReferenceMap;
import it.unimi.dsi.fastutil.objects.Object2ReferenceRBTreeMap;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map.Entry;
@@ -58,14 +59,6 @@
}
}
- private void rehash() {
- Object2ReferenceMap<Wrapper<DexMethod>, DexEncodedMethod> newMap = createMap(methodMap.size());
- for (DexEncodedMethod method : methodMap.values()) {
- newMap.put(wrap(method.method), method);
- }
- methodMap = newMap;
- }
-
@Override
boolean verify() {
methodMap.forEach(
@@ -259,18 +252,14 @@
@Override
void replaceMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
- boolean rehash = false;
- for (Object2ReferenceMap.Entry<Wrapper<DexMethod>, DexEncodedMethod> entry :
- methodMap.object2ReferenceEntrySet()) {
- DexEncodedMethod newMethod = replacement.apply(entry.getValue());
- if (newMethod != entry.getValue()) {
- rehash = rehash || newMethod.method != entry.getKey().get();
- entry.setValue(newMethod);
+ ArrayList<DexEncodedMethod> initialValues = new ArrayList<>(methodMap.values());
+ for (DexEncodedMethod method : initialValues) {
+ DexEncodedMethod newMethod = replacement.apply(method);
+ if (newMethod != method) {
+ removeMethod(method.method);
+ addMethod(newMethod);
}
}
- if (rehash) {
- rehash();
- }
}
@Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index c80e363..73d1e68 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -44,6 +44,7 @@
private final Collection<DexProgramClass> toMergeGroup;
private final DexItemFactory dexItemFactory;
private final HorizontalClassMergerGraphLens.Builder lensBuilder;
+ private final HorizontallyMergedClasses.Builder mergedClassesBuilder;
private final FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder;
private final Reference2IntMap<DexType> classIdentifiers = new Reference2IntOpenHashMap<>();
@@ -54,6 +55,7 @@
private ClassMerger(
AppView<AppInfoWithLiveness> appView,
HorizontalClassMergerGraphLens.Builder lensBuilder,
+ HorizontallyMergedClasses.Builder mergedClassesBuilder,
FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
DexProgramClass target,
Collection<DexProgramClass> toMergeGroup,
@@ -62,6 +64,7 @@
Collection<ConstructorMerger> constructorMergers) {
this.appView = appView;
this.lensBuilder = lensBuilder;
+ this.mergedClassesBuilder = mergedClassesBuilder;
this.fieldAccessChangesBuilder = fieldAccessChangesBuilder;
this.target = target;
this.toMergeGroup = toMergeGroup;
@@ -156,9 +159,9 @@
public void mergeGroup(SyntheticArgumentClass syntheticArgumentClass) {
appendClassIdField();
+ mergedClassesBuilder.addMergeGroup(target, toMergeGroup);
for (DexProgramClass clazz : toMergeGroup) {
merge(clazz);
- lensBuilder.mapType(clazz.type, target.type);
}
mergeConstructors(syntheticArgumentClass);
@@ -222,6 +225,7 @@
public ClassMerger build(
AppView<AppInfoWithLiveness> appView,
+ HorizontallyMergedClasses.Builder mergedClassesBuilder,
HorizontalClassMergerGraphLens.Builder lensBuilder,
FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
@@ -244,6 +248,7 @@
return new ClassMerger(
appView,
lensBuilder,
+ mergedClassesBuilder,
fieldAccessChangesBuilder,
target,
toMergeGroup,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
index 345905b..9baf6ad 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
@@ -140,7 +140,8 @@
classFileVersion = Integer.max(classFileVersion, constructor.getClassFileVersion());
}
DexMethod movedConstructor = moveConstructor(constructor);
- lensBuilder.recordOriginalSignature(constructor.method, movedConstructor);
+ lensBuilder.mapMethod(movedConstructor, movedConstructor);
+ lensBuilder.mapMethodInverse(constructor.method, movedConstructor);
typeConstructorClassMap.put(
classIdentifiers.getInt(constructor.getHolderType()), movedConstructor);
}
@@ -152,14 +153,13 @@
assert target.lookupMethod(newConstructorReference) == null;
}
- DexMethod originalConstructorReference =
- appView.graphLens().getOriginalMethodSignature(constructors.iterator().next().method);
+ DexMethod representativeConstructorReference = constructors.iterator().next().method;
ConstructorEntryPointSynthesizedCode synthesizedCode =
new ConstructorEntryPointSynthesizedCode(
typeConstructorClassMap,
newConstructorReference,
classIdField,
- originalConstructorReference);
+ appView.graphLens().getOriginalMethodSignature(representativeConstructorReference));
DexEncodedMethod newConstructor =
new DexEncodedMethod(
newConstructorReference,
@@ -177,10 +177,10 @@
if (addExtraNull) {
List<ExtraParameter> extraParameters = new LinkedList<>();
extraParameters.add(new ExtraUnusedNullParameter());
- lensBuilder.mapMergedConstructor(
+ lensBuilder.moveMergedConstructor(
oldConstructor.method, newConstructorReference, extraParameters);
} else {
- lensBuilder.mapMethod(oldConstructor.method, newConstructorReference);
+ lensBuilder.moveMethod(oldConstructor.method, newConstructorReference);
}
} else {
// Map each old constructor to the newly synthesized constructor in the graph lens.
@@ -193,12 +193,13 @@
extraParameters.add(new ExtraUnusedNullParameter());
}
- lensBuilder.mapMergedConstructor(
+ lensBuilder.moveMergedConstructor(
oldConstructor.method, newConstructorReference, extraParameters);
}
}
// Map the first constructor to the newly synthesized constructor.
- lensBuilder.recordExtraOriginalSignature(originalConstructorReference, newConstructorReference);
+ lensBuilder.recordExtraOriginalSignature(
+ representativeConstructorReference, newConstructorReference);
target.addDirectMethod(newConstructor);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 1886a48..d87bddc 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.horizontalclassmerging.policies.PreventMergeIntoMainDex;
import com.android.tools.r8.horizontalclassmerging.policies.RespectPackageBoundaries;
import com.android.tools.r8.horizontalclassmerging.policies.SameFeatureSplit;
+import com.android.tools.r8.horizontalclassmerging.policies.SameNestHost;
import com.android.tools.r8.horizontalclassmerging.policies.SameParentClass;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ClassMergingEnqueuerExtension;
@@ -64,6 +65,7 @@
new NotEntryPoint(appView.dexItemFactory()),
new PreventMergeIntoMainDex(appView, mainDexTracingResult),
new SameParentClass(),
+ new SameNestHost(),
new PreventChangingVisibility(),
new SameFeatureSplit(appView),
new RespectPackageBoundaries(appView),
@@ -90,6 +92,8 @@
return null;
}
+ HorizontallyMergedClasses.Builder mergedClassesBuilder =
+ new HorizontallyMergedClasses.Builder();
HorizontalClassMergerGraphLens.Builder lensBuilder =
new HorizontalClassMergerGraphLens.Builder();
FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder =
@@ -97,7 +101,8 @@
// Set up a class merger for each group.
Collection<ClassMerger> classMergers =
- initializeClassMergers(lensBuilder, fieldAccessChangesBuilder, groups);
+ initializeClassMergers(
+ mergedClassesBuilder, lensBuilder, fieldAccessChangesBuilder, groups);
Iterable<DexProgramClass> allMergeClasses =
Iterables.concat(Iterables.transform(classMergers, ClassMerger::getClasses));
@@ -107,7 +112,10 @@
applyClassMergers(classMergers, syntheticArgumentClass);
// Generate the class lens.
- return createLens(lensBuilder, fieldAccessChangesBuilder);
+ HorizontallyMergedClasses mergedClasses = mergedClassesBuilder.build();
+ appView.setHorizontallyMergedClasses(mergedClasses);
+ return createLens(
+ mergedClasses, lensBuilder, fieldAccessChangesBuilder, syntheticArgumentClass);
}
/**
@@ -115,6 +123,7 @@
* be merged and how the merging should be performed.
*/
private Collection<ClassMerger> initializeClassMergers(
+ HorizontallyMergedClasses.Builder mergedClassesBuilder,
HorizontalClassMergerGraphLens.Builder lensBuilder,
FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
Collection<Collection<DexProgramClass>> groups) {
@@ -130,7 +139,7 @@
ClassMerger merger =
new ClassMerger.Builder(target)
.addClassesToMerge(group)
- .build(appView, lensBuilder, fieldAccessChangesBuilder);
+ .build(appView, mergedClassesBuilder, lensBuilder, fieldAccessChangesBuilder);
classMergers.add(merger);
}
@@ -150,11 +159,19 @@
* containing all changes performed by horizontal class merging.
*/
private HorizontalClassMergerGraphLens createLens(
+ HorizontallyMergedClasses mergedClasses,
HorizontalClassMergerGraphLens.Builder lensBuilder,
- FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder) {
+ FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
+ SyntheticArgumentClass syntheticArgumentClass) {
HorizontalClassMergerGraphLens lens =
- new TreeFixer(appView, lensBuilder, fieldAccessChangesBuilder).fixupTypeReferences();
+ new TreeFixer(
+ appView,
+ mergedClasses,
+ lensBuilder,
+ fieldAccessChangesBuilder,
+ syntheticArgumentClass)
+ .fixupTypeReferences();
return lens;
}
}
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 395409f..a10b066 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -7,18 +7,18 @@
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.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
+import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Function;
public class HorizontalClassMergerGraphLens extends NestedGraphLens {
private final AppView<?> appView;
@@ -27,8 +27,8 @@
private HorizontalClassMergerGraphLens(
AppView<?> appView,
+ HorizontallyMergedClasses horizontallyMergedClasses,
Map<DexMethod, List<ExtraParameter>> methodExtraParameters,
- Map<DexType, DexType> typeMap,
Map<DexField, DexField> fieldMap,
Map<DexMethod, DexMethod> methodMap,
BiMap<DexField, DexField> originalFieldSignatures,
@@ -36,7 +36,7 @@
Map<DexMethod, DexMethod> originalConstructorSignatures,
GraphLens previousLens) {
super(
- typeMap,
+ horizontallyMergedClasses.getForwardMap(),
methodMap,
fieldMap,
originalFieldSignatures,
@@ -57,10 +57,6 @@
return getPrevious().getOriginalMethodSignature(originalConstructor);
}
- public HorizontallyMergedClasses getHorizontallyMergedClasses() {
- return new HorizontallyMergedClasses(this.typeMap);
- }
-
/**
* If an overloaded constructor is requested, add the constructor id as a parameter to the
* constructor. Otherwise return the lookup on the underlying graph lens.
@@ -81,102 +77,67 @@
}
public static class Builder {
- private final Map<DexType, DexType> typeMap = new IdentityHashMap<>();
private final BiMap<DexField, DexField> fieldMap = HashBiMap.create();
- private final Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
- private final Map<DexMethod, Set<DexMethod>> completeInverseMethodMap = new IdentityHashMap<>();
- private final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
- private final Map<DexMethod, DexMethod> extraOriginalMethodSignatures = new IdentityHashMap<>();
+ private ManyToOneMap<DexMethod, DexMethod> methodMap = new ManyToOneMap<>();
private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters =
new IdentityHashMap<>();
Builder() {}
- public HorizontalClassMergerGraphLens build(AppView<?> appView) {
- assert !typeMap.isEmpty();
-
+ public HorizontalClassMergerGraphLens build(
+ AppView<?> appView, HorizontallyMergedClasses mergedClasses) {
+ ManyToOneInverseMap<DexMethod, DexMethod> inverseMethodMap =
+ methodMap.inverse(
+ group -> {
+ // Every group should have a representative. Fail in debug mode.
+ assert false;
+ return group.iterator().next();
+ });
BiMap<DexField, DexField> originalFieldSignatures = fieldMap.inverse();
+
return new HorizontalClassMergerGraphLens(
appView,
+ mergedClasses,
methodExtraParameters,
- typeMap,
fieldMap,
- methodMap,
+ methodMap.getForwardMap(),
originalFieldSignatures,
- originalMethodSignatures,
- extraOriginalMethodSignatures,
+ inverseMethodMap.getBiMap(),
+ inverseMethodMap.getExtraMap(),
appView.graphLens());
}
- public DexType lookupType(DexType type) {
- return typeMap.getOrDefault(type, type);
- }
-
- public Builder mapType(DexType from, DexType to) {
- typeMap.put(from, to);
- return this;
- }
-
- /** Bidirectional mapping from one method to another. */
- public Builder moveMethod(DexMethod from, DexMethod to) {
- if (from == to) {
- return this;
- }
-
- mapMethod(from, to);
- recordOriginalSignature(from, to);
- return this;
- }
-
- public Builder recordOriginalSignature(DexMethod from, DexMethod to) {
- if (from == to) {
- return this;
- }
-
- originalMethodSignatures.forcePut(to, originalMethodSignatures.getOrDefault(from, from));
- return this;
+ public void remapMethods(BiMap<DexMethod, DexMethod> remapMethods) {
+ methodMap = methodMap.remap(remapMethods, Function.identity(), Function.identity());
}
/** Unidirectional mapping from one method to another. */
public Builder recordExtraOriginalSignature(DexMethod from, DexMethod to) {
- if (from == to) {
- return this;
- }
+ methodMap.setRepresentative(from, to);
- extraOriginalMethodSignatures.put(to, extraOriginalMethodSignatures.getOrDefault(from, from));
return this;
}
/** Unidirectional mapping from one method to another. */
public Builder mapMethod(DexMethod from, DexMethod to) {
- if (from == to) {
- return this;
- }
-
- for (DexMethod existingFrom :
- completeInverseMethodMap.getOrDefault(from, Collections.emptySet())) {
- methodMap.put(existingFrom, to);
-
- // We currently assume that a single method can only be remapped twice.
- assert completeInverseMethodMap
- .getOrDefault(existingFrom, Collections.emptySet())
- .isEmpty();
- }
-
methodMap.put(from, to);
- completeInverseMethodMap.computeIfAbsent(to, ignore -> new HashSet<>()).add(from);
return this;
}
- public boolean hasExtraSignatureMappingFor(DexMethod method) {
- return extraOriginalMethodSignatures.containsKey(method);
+ /** Unidirectional mapping from one method to another. */
+ public Builder mapMethodInverse(DexMethod from, DexMethod to) {
+ methodMap.putInverse(from, to);
+
+ return this;
}
- public boolean hasOriginalSignatureMappingFor(DexMethod method) {
- return originalMethodSignatures.containsKey(method);
+ public Builder moveMethod(DexMethod from, DexMethod to) {
+ mapMethod(from, to);
+ mapMethodInverse(from, to);
+ return this;
}
/**
@@ -184,11 +145,24 @@
* where many constructors are merged into a single constructor. The synthesized constructor
* therefore does not have a unique reverse constructor.
*/
- public Builder mapMergedConstructor(
+ public Builder moveMergedConstructor(
DexMethod from, DexMethod to, List<ExtraParameter> extraParameters) {
- mapMethod(from, to);
+ moveMethod(from, to);
methodExtraParameters.put(from, extraParameters);
return this;
}
+
+ public Builder addExtraParameters(DexMethod to, List<ExtraParameter> extraParameters) {
+ Set<DexMethod> mapsFrom = methodMap.lookupReverse(to);
+ if (mapsFrom == null) {
+ mapsFrom = Collections.singleton(to);
+ }
+ mapsFrom.forEach(
+ originalFrom ->
+ methodExtraParameters
+ .computeIfAbsent(originalFrom, ignore -> new ArrayList<>(extraParameters.size()))
+ .addAll(extraParameters));
+ return this;
+ }
}
}
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 00632e5..b912cf7 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
@@ -5,21 +5,49 @@
package com.android.tools.r8.horizontalclassmerging;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.classmerging.MergedClasses;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
+import java.util.Collection;
import java.util.Map;
public class HorizontallyMergedClasses implements MergedClasses {
- private final Map<DexType, DexType> horizontallyMergedClasses;
+ private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses;
- public HorizontallyMergedClasses(Map<DexType, DexType> horizontallyMergedClasses) {
- this.horizontallyMergedClasses = horizontallyMergedClasses;
+ public HorizontallyMergedClasses(BidirectionalManyToOneMap<DexType, DexType> mergedClasses) {
+ this.mergedClasses = mergedClasses;
+ }
+
+ public DexType getMergeTargetOrDefault(DexType type) {
+ return mergedClasses.getOrDefault(type, type);
+ }
+
+ public boolean hasBeenMergedIntoDifferentType(DexType type) {
+ return mergedClasses.hasKey(type);
+ }
+
+ @Override
+ public boolean hasBeenMerged(DexType type) {
+ return hasBeenMergedIntoDifferentType(type);
+ }
+
+ public boolean isMergeTarget(DexType type) {
+ return mergedClasses.hasValue(type);
+ }
+
+ public boolean hasBeenMergedOrIsMergeTarget(DexType type) {
+ return hasBeenMerged(type) || isMergeTarget(type);
+ }
+
+ Map<DexType, DexType> getForwardMap() {
+ return mergedClasses.getForwardMap();
}
@Override
public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) {
- for (DexType source : horizontallyMergedClasses.keySet()) {
+ for (DexType source : mergedClasses.keySet()) {
assert appView.appInfo().wasPruned(source)
: "Expected horizontally merged lambda class `"
+ source.toSourceString()
@@ -28,8 +56,18 @@
return true;
}
- @Override
- public boolean hasBeenMerged(DexType type) {
- return horizontallyMergedClasses.containsKey(type);
+ public static class Builder {
+ private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses =
+ new BidirectionalManyToOneMap<>();
+
+ public HorizontallyMergedClasses build() {
+ return new HorizontallyMergedClasses(mergedClasses);
+ }
+
+ public void addMergeGroup(DexProgramClass target, Collection<DexProgramClass> toMergeGroup) {
+ for (DexProgramClass clazz : toMergeGroup) {
+ mergedClasses.put(clazz.type, target.type);
+ }
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java
new file mode 100644
index 0000000..8da9b0f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java
@@ -0,0 +1,27 @@
+// 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.horizontalclassmerging;
+
+import com.google.common.collect.BiMap;
+import java.util.Map;
+
+/** The inverse of a {@link ManyToOneMap} used for generating graph lens maps. */
+public class ManyToOneInverseMap<K, V> {
+ private final BiMap<V, K> biMap;
+ private final Map<V, K> extraMap;
+
+ ManyToOneInverseMap(BiMap<V, K> biMap, Map<V, K> extraMap) {
+ this.biMap = biMap;
+ this.extraMap = extraMap;
+ }
+
+ public BiMap<V, K> getBiMap() {
+ return biMap;
+ }
+
+ public Map<V, K> getExtraMap() {
+ return extraMap;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java
new file mode 100644
index 0000000..675d24c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java
@@ -0,0 +1,120 @@
+// 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.horizontalclassmerging;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.function.Function;
+
+/**
+ * This mapping class is used to track method mappings for horizontal class merging. Essentially it
+ * is a bidirectional many to one map, but with support for having unidirectional mappings and with
+ * support for remapping the values to new values using {@link ManyToOneMap#remap(BiMap, Function,
+ * Function)}. It also supports generating an inverse mapping {@link ManyToOneInverseMap} that can
+ * be used by the graph lens using {@link ManyToOneMap#inverse(Function)}. The inverse map is a
+ * bidirectional one to one map with additional non-bidirectional representative entries.
+ */
+public class ManyToOneMap<K, V> {
+ private final Map<K, V> forwardMap = new IdentityHashMap<>();
+ private final Map<V, Set<K>> inverseMap = new IdentityHashMap<>();
+ private final Map<V, K> representativeMap = new IdentityHashMap<>();
+
+ public Map<K, V> getForwardMap() {
+ return forwardMap;
+ }
+
+ public Set<K> lookupReverse(V to) {
+ return inverseMap.get(to);
+ }
+
+ public V put(K from, V to) {
+ return forwardMap.put(from, to);
+ }
+
+ public void putInverse(K from, V to) {
+ inverseMap.computeIfAbsent(to, ignore -> new HashSet<>()).add(from);
+ }
+
+ public K setRepresentative(K from, V to) {
+ putInverse(from, to);
+ return representativeMap.put(to, from);
+ }
+
+ public ManyToOneInverseMap<K, V> inverse(Function<Set<K>, K> pickRepresentative) {
+ BiMap<V, K> biMap = HashBiMap.create();
+ Map<V, K> extraMap = new HashMap<>();
+ for (Entry<V, Set<K>> entry : inverseMap.entrySet()) {
+ K representative = representativeMap.get(entry.getKey());
+ if (entry.getValue().size() == 1) {
+ K singleton = entry.getValue().iterator().next();
+ assert representative == null || singleton == representative;
+ if (representative == null) {
+ biMap.put(entry.getKey(), singleton);
+ } else {
+ extraMap.put(entry.getKey(), singleton);
+ }
+ } else {
+ if (representative == null) {
+ representative = pickRepresentative.apply(entry.getValue());
+ } else {
+ assert representative == entry.getKey() || entry.getValue().contains(representative);
+ }
+ extraMap.put(entry.getKey(), representative);
+ }
+ }
+
+ return new ManyToOneInverseMap<>(biMap, extraMap);
+ }
+
+ public <NewV> ManyToOneMap<K, NewV> remap(
+ BiMap<V, NewV> biMap, Function<V, NewV> notInBiMap, Function<V, K> notInForwardMap) {
+ ManyToOneMap<K, NewV> newMap = new ManyToOneMap<>();
+
+ // All entries that should be remapped and are already in the forward and/or inverse mappings
+ // should only be remapped in the directions they are already mapped in.
+ BiMap<V, NewV> biMapCopy = HashBiMap.create(biMap);
+ for (Entry<V, Set<K>> entry : inverseMap.entrySet()) {
+ NewV to = biMapCopy.remove(entry.getKey());
+ if (to == null) {
+ to = biMap.getOrDefault(entry.getKey(), notInBiMap.apply(entry.getKey()));
+ }
+ newMap.inverseMap.put(to, entry.getValue());
+ }
+ for (Entry<K, V> entry : forwardMap.entrySet()) {
+ NewV newTo = biMapCopy.remove(entry.getValue());
+ if (newTo == null) {
+ newTo = biMap.getOrDefault(entry.getValue(), notInBiMap.apply(entry.getValue()));
+ }
+ newMap.forwardMap.put(entry.getKey(), newTo);
+ }
+
+ // All new entries should be mapped in both directions.
+ for (Entry<V, NewV> entry : biMapCopy.entrySet()) {
+ newMap.forwardMap.put(notInForwardMap.apply(entry.getKey()), entry.getValue());
+ newMap
+ .inverseMap
+ .computeIfAbsent(entry.getValue(), ignore -> new HashSet<>())
+ .add(notInForwardMap.apply(entry.getKey()));
+ }
+
+ // Representatives are always in the inverse mapping, so they should always be remapped as new
+ // representatives.
+ for (Entry<V, K> entry : representativeMap.entrySet()) {
+ NewV newTo = biMap.get(entry.getKey());
+ if (newTo == null) {
+ newTo = notInBiMap.apply(entry.getKey());
+ }
+ newMap.representativeMap.put(newTo, entry.getValue());
+ }
+
+ return newMap;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassSameReferencePolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassSameReferencePolicy.java
index 3407306..0a7e988 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassSameReferencePolicy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassSameReferencePolicy.java
@@ -11,7 +11,6 @@
import java.util.Map;
public abstract class MultiClassSameReferencePolicy<T> extends MultiClassPolicy {
-
@Override
public final Collection<Collection<DexProgramClass>> apply(Collection<DexProgramClass> group) {
Map<T, Collection<DexProgramClass>> groups = new IdentityHashMap<>();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/Policy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/Policy.java
index 8d6fc72..f6b86a3 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/Policy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/Policy.java
@@ -9,6 +9,9 @@
* {@link SingleClassPolicy} or {@link MultiClassPolicy}.
*/
public abstract class Policy {
+ /** Counter keeping track of how many classes this policy has removed. For debugging only. */
+ public int numberOfRemovedClasses;
+
public boolean shouldSkipPolicy() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
index f0ac605..6192da8 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
@@ -26,7 +26,9 @@
Iterator<Collection<DexProgramClass>> i = groups.iterator();
while (i.hasNext()) {
Collection<DexProgramClass> group = i.next();
+ int previousNumberOfClasses = group.size();
group.removeIf(clazz -> !policy.canMerge(clazz));
+ policy.numberOfRemovedClasses += previousNumberOfClasses - group.size();
if (group.size() < 2) {
i.remove();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index a35db17..b57adbf 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -9,17 +9,31 @@
import com.android.tools.r8.graph.DexEncodedField;
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;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
import com.android.tools.r8.shaking.AnnotationFixer;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.OptionalBool;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.IdentityHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
/**
* The tree fixer traverses all program classes and finds and fixes references to old classes which
@@ -28,49 +42,221 @@
*/
class TreeFixer {
private final Map<DexProto, DexProto> protoFixupCache = new IdentityHashMap<>();
+ private final HorizontallyMergedClasses mergedClasses;
private final HorizontalClassMergerGraphLens.Builder lensBuilder;
private final FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder;
private final AppView<AppInfoWithLiveness> appView;
+ private final DexItemFactory dexItemFactory;
+ private final BiMap<DexMethod, DexMethod> movedMethods = HashBiMap.create();
+ private final SyntheticArgumentClass syntheticArgumentClass;
+ private final BiMap<Wrapper<DexMethod>, Wrapper<DexMethod>> reservedInterfaceSignatures =
+ HashBiMap.create();
+
+ // Store which methods have been renamed in parent classes.
+ private final Map<DexType, Map<Wrapper<DexMethod>, DexString>> renamedVirtualMethods =
+ new IdentityHashMap<>();
public TreeFixer(
AppView<AppInfoWithLiveness> appView,
+ HorizontallyMergedClasses mergedClasses,
HorizontalClassMergerGraphLens.Builder lensBuilder,
- FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder) {
+ FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
+ SyntheticArgumentClass syntheticArgumentClass) {
+ this.appView = appView;
+ this.mergedClasses = mergedClasses;
this.lensBuilder = lensBuilder;
this.fieldAccessChangesBuilder = fieldAccessChangesBuilder;
- this.appView = appView;
+ this.syntheticArgumentClass = syntheticArgumentClass;
+ this.dexItemFactory = appView.dexItemFactory();
}
- HorizontalClassMergerGraphLens fixupTypeReferences() {
- // Globally substitute merged class types in protos and holders.
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- clazz.superType = lensBuilder.lookupType(clazz.superType);
- clazz.getMethodCollection().replaceMethods(this::fixupMethod);
- fixupFields(clazz.staticFields(), clazz::setStaticField);
- fixupFields(clazz.instanceFields(), clazz::setInstanceField);
- }
- HorizontalClassMergerGraphLens lens = lensBuilder.build(appView);
- fieldAccessChangesBuilder.build(this::fixupMethod).modify(appView);
+ /**
+ * Lets assume the following initial classes, where the class B should be merged into A: <code>
+ * class A {
+ * public A(A a) { ... }
+ * public A(A a, int v) { ... }
+ * public A(B b) { ... }
+ * public A(B b, int v) { ... }
+ * }
+ *
+ * class B {
+ * public B(A a) { ... }
+ * public B(B b) { ... }
+ * }
+ * </code>
+ *
+ * <p>The {@link ClassMerger} merges the constructors {@code A.<init>(B)} and {@code B.<init>(B)}
+ * into the constructor {@code A.<init>(B, int)} to prevent any collisions when merging the
+ * constructor into A. The extra integer argument determines which class' constructor is called.
+ * The SynthArg is used to prevent a collision with the existing {@code A.<init>(B, int)}
+ * constructor. All constructors {@code A.<init>(A, ...)} generate a constructor {@code
+ * A.<init>(A, int, SynthClass)} but are otherwise ignored. During ClassMerging the constructor
+ * produces the following mappings in the graph lens builder:
+ *
+ * <ul>
+ * <li>{@code B.<init>(B) <--> A.<init>(B, int, SynthArg)}
+ * <li>{@code A.<init>(B) <--> A.<init>(B, int, SynthArg)} (This mapping is representative)
+ * <li>{@code A.constructor$B(B) ---> A.constructor$B(B)}
+ * <li>{@code B.<init>(B) <--- A.constructor$B(B)}
+ * </ul>
+ *
+ * <p>Note: The identity mapping is needed so that the method is remapped in the forward direction
+ * if there are changes in the tree fixer. Otherwise, methods are only remapped in directions they
+ * are already mapped in.
+ *
+ * <p>During the fixup, all type references to B are changed into A. This causes a collision
+ * between {@code A.<init>(A, int, SynthClass)} and {@code A.<init>(B, int, SynthClass)}. This
+ * collision should be fixed by adding an extra argument to {@code A.<init>(B, int, SynthClass)}.
+ * The TreeFixer generates the following mapping of renamed methods:
+ *
+ * <ul>
+ * <li>{@code A.<init>(B, int, SynthArg) <--> A.<init>(A, int, SynthArg, ExtraArg)}
+ * <li>{@code A.constructor$B(B) <--> A.constructor$B(A)}
+ * </ul>
+ *
+ * <p>This rewrites the previous method mappings to:
+ *
+ * <ul>
+ * <li>{@code B.<init>(B) <--- A.constructor$B(A)}
+ * <li>{@code A.constructor$B(B) ---> A.constructor$B(A)}
+ * <li>{@code B.<init>(B) <--> A.<init>(A, int, SynthArg, ExtraArg)}
+ * <li>{@code A.<init>(B) <--> A.<init>(A, int, SynthArg, ExtraArg)} (including represents)
+ * </ul>
+ */
+ public HorizontalClassMergerGraphLens fixupTypeReferences() {
+ Iterable<DexProgramClass> classes = appView.appInfo().classesWithDeterministicOrder();
+ Iterables.filter(classes, DexProgramClass::isInterface).forEach(this::fixupInterfaceClass);
+
+ forEachClassTypeTraverseHierarchy(
+ Iterables.filter(classes, clazz -> !clazz.isInterface()), this::fixupProgramClass);
+
+ lensBuilder.remapMethods(movedMethods);
+
+ HorizontalClassMergerGraphLens lens = lensBuilder.build(appView, mergedClasses);
+ fieldAccessChangesBuilder.build(this::fixupMethodReference).modify(appView);
new AnnotationFixer(lens).run(appView.appInfo().classes());
return lens;
}
- private DexEncodedMethod fixupMethod(DexEncodedMethod method) {
- DexMethod methodReference = method.method;
- DexMethod newMethodReference = fixupMethod(methodReference);
- if (newMethodReference == methodReference) {
+ private void fixupProgramClass(DexProgramClass clazz) {
+ assert !clazz.isInterface();
+
+ // TODO(b/169395592): ensure merged classes have been removed using:
+ // assert !mergedClasses.hasBeenMergedIntoDifferentType(clazz.type);
+
+ Map<Wrapper<DexMethod>, DexString> renamedClassVirtualMethods =
+ new HashMap<>(renamedVirtualMethods.getOrDefault(clazz.superType, new HashMap<>()));
+
+ Set<DexMethod> newDirectMethodReferences = new LinkedHashSet<>();
+ Set<DexMethod> newVirtualMethodReferences = new LinkedHashSet<>();
+
+ clazz
+ .getMethodCollection()
+ .replaceVirtualMethods(
+ method ->
+ fixupVirtualMethod(renamedClassVirtualMethods, newVirtualMethodReferences, method));
+ clazz
+ .getMethodCollection()
+ .replaceDirectMethods(method -> fixupDirectMethod(newDirectMethodReferences, method));
+
+ if (!renamedClassVirtualMethods.isEmpty()) {
+ renamedVirtualMethods.put(clazz.type, renamedClassVirtualMethods);
+ }
+ fixupFields(clazz.staticFields(), clazz::setStaticField);
+ fixupFields(clazz.instanceFields(), clazz::setInstanceField);
+ }
+
+ private void traverseUp(
+ DexProgramClass clazz, Set<DexProgramClass> seenClasses, Consumer<DexProgramClass> fn) {
+ if (clazz == null || !seenClasses.add(clazz)) {
+ return;
+ }
+
+ clazz.superType = mergedClasses.getMergeTargetOrDefault(clazz.superType);
+ if (clazz.superType != null) {
+ DexProgramClass superClass = appView.programDefinitionFor(clazz.superType, clazz);
+ traverseUp(superClass, seenClasses, fn);
+ }
+
+ fn.accept(clazz);
+ }
+
+ private void forEachClassTypeTraverseHierarchy(
+ Iterable<DexProgramClass> classes, Consumer<DexProgramClass> fn) {
+ Set<DexProgramClass> seenClasses = Sets.newIdentityHashSet();
+ for (DexProgramClass clazz : classes) {
+ traverseUp(clazz, seenClasses, fn);
+ }
+ }
+
+ private DexEncodedMethod fixupVirtualInterfaceMethod(DexEncodedMethod method) {
+ DexMethod originalMethodReference = method.getReference();
+
+ // Don't process this method if it does not refer to a merge class type.
+ boolean referencesMergeClass =
+ Iterables.any(
+ originalMethodReference.proto.getBaseTypes(dexItemFactory),
+ mergedClasses::hasBeenMergedOrIsMergeTarget);
+ if (!referencesMergeClass) {
return method;
}
- // If the method is a synthesized method, then don't record the original signature.
- if ((method.getCode() instanceof ConstructorEntryPointSynthesizedCode)) {
- assert lensBuilder.hasExtraSignatureMappingFor(methodReference);
- lensBuilder.recordExtraOriginalSignature(methodReference, newMethodReference);
- lensBuilder.mapMethod(methodReference, newMethodReference);
- } else {
- lensBuilder.moveMethod(methodReference, newMethodReference);
+ MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
+ Wrapper<DexMethod> originalMethodSignature = equivalence.wrap(originalMethodReference);
+ Wrapper<DexMethod> newMethodSignature =
+ reservedInterfaceSignatures.get(originalMethodSignature);
+
+ if (newMethodSignature == null) {
+ newMethodSignature = equivalence.wrap(fixupMethodReference(originalMethodReference));
+
+ // If the signature is already reserved by another interface, find a fresh one.
+ if (reservedInterfaceSignatures.containsValue(newMethodSignature)) {
+ DexString name =
+ dexItemFactory.createGloballyFreshMemberString(
+ originalMethodReference.getName().toSourceString());
+ newMethodSignature =
+ equivalence.wrap(
+ dexItemFactory.createMethod(
+ newMethodSignature.get().holder, newMethodSignature.get().proto, name));
+ }
+
+ assert !reservedInterfaceSignatures.containsValue(newMethodSignature);
+ reservedInterfaceSignatures.put(originalMethodSignature, newMethodSignature);
}
+ DexMethod newMethodReference =
+ newMethodSignature
+ .get()
+ .withHolder(originalMethodReference.getHolderType(), dexItemFactory);
+ movedMethods.put(originalMethodReference, newMethodReference);
+
+ return method.toTypeSubstitutedMethod(newMethodReference);
+ }
+
+ private void fixupInterfaceClass(DexProgramClass iface) {
+ Set<DexMethod> newDirectMethods = new LinkedHashSet<>();
+
+ assert iface.superType == dexItemFactory.objectType;
+ iface.superType = mergedClasses.getMergeTargetOrDefault(iface.superType);
+
+ iface
+ .getMethodCollection()
+ .replaceDirectMethods(method -> fixupDirectMethod(newDirectMethods, method));
+ iface.getMethodCollection().replaceVirtualMethods(this::fixupVirtualInterfaceMethod);
+ fixupFields(iface.staticFields(), iface::setStaticField);
+ fixupFields(iface.instanceFields(), iface::setInstanceField);
+ }
+
+ private DexEncodedMethod fixupProgramMethod(
+ DexMethod newMethodReference, DexEncodedMethod method) {
+ DexMethod originalMethodReference = method.getReference();
+
+ if (newMethodReference == originalMethodReference) {
+ return method;
+ }
+
+ movedMethods.put(originalMethodReference, newMethodReference);
+
DexEncodedMethod newMethod = method.toTypeSubstitutedMethod(newMethodReference);
if (newMethod.isNonPrivateVirtualMethod()) {
// Since we changed the return type or one of the parameters, this method cannot be a
@@ -78,9 +264,122 @@
assert !method.isLibraryMethodOverride().isTrue();
newMethod.setLibraryMethodOverride(OptionalBool.FALSE);
}
+
return newMethod;
}
+ private DexEncodedMethod fixupDirectMethod(Set<DexMethod> newMethods, DexEncodedMethod method) {
+ DexMethod originalMethodReference = method.getReference();
+
+ // Fix all type references in the method prototype.
+ DexMethod newMethodReference = fixupMethodReference(originalMethodReference);
+
+ if (newMethods.contains(newMethodReference)) {
+ // If the method collides with a direct method on the same class then rename it to a globally
+ // fresh name and record the signature.
+
+ if (method.isInstanceInitializer()) {
+ // If the method is an instance initializer, then add extra nulls.
+ newMethodReference =
+ dexItemFactory.createInstanceInitializerWithFreshProto(
+ newMethodReference,
+ syntheticArgumentClass.getArgumentClass(),
+ tryMethod -> !newMethods.contains(tryMethod));
+ int extraNulls = newMethodReference.getArity() - originalMethodReference.getArity();
+ lensBuilder.addExtraParameters(
+ originalMethodReference,
+ Collections.nCopies(extraNulls, new ExtraUnusedNullParameter()));
+ } else {
+ DexString newMethodName =
+ dexItemFactory.createGloballyFreshMemberString(
+ originalMethodReference.getName().toSourceString(), null);
+ newMethodReference =
+ dexItemFactory.createMethod(
+ newMethodReference.holder, newMethodReference.proto, newMethodName);
+ }
+ }
+
+ boolean changed = newMethods.add(newMethodReference);
+ assert changed;
+
+ return fixupProgramMethod(newMethodReference, method);
+ }
+
+ private DexString lookupReservedVirtualName(
+ DexMethod originalMethodReference,
+ Map<Wrapper<DexMethod>, DexString> renamedClassVirtualMethods) {
+ Wrapper<DexMethod> originalSignature =
+ MethodSignatureEquivalence.get().wrap(originalMethodReference);
+
+ // Determine if the original method has been rewritten by a parent class
+ DexString renamedVirtualName =
+ renamedClassVirtualMethods != null
+ ? renamedClassVirtualMethods.get(originalSignature)
+ : null;
+
+ if (renamedVirtualName == null) {
+ // Determine if there is a signature mapping.
+ Wrapper<DexMethod> mappedInterfaceSignature =
+ reservedInterfaceSignatures.get(originalSignature);
+ if (mappedInterfaceSignature != null) {
+ renamedVirtualName = mappedInterfaceSignature.get().name;
+ }
+ } else {
+ assert !reservedInterfaceSignatures.containsKey(originalSignature);
+ }
+
+ return renamedVirtualName;
+ }
+
+ private DexEncodedMethod fixupVirtualMethod(
+ Map<Wrapper<DexMethod>, DexString> renamedClassVirtualMethods,
+ Set<DexMethod> newMethods,
+ DexEncodedMethod method) {
+ DexMethod originalMethodReference = method.getReference();
+ Wrapper<DexMethod> originalSignature =
+ MethodSignatureEquivalence.get().wrap(originalMethodReference);
+
+ DexString renamedVirtualName =
+ lookupReservedVirtualName(originalMethodReference, renamedClassVirtualMethods);
+
+ // Fix all type references in the method prototype.
+ DexMethod newMethodReference = fixupMethodReference(originalMethodReference);
+ Wrapper<DexMethod> newSignature = MethodSignatureEquivalence.get().wrap(newMethodReference);
+
+ if (renamedVirtualName != null) {
+ // If the method was renamed in a parent, rename it in the child.
+ newMethodReference = newMethodReference.withName(renamedVirtualName, dexItemFactory);
+
+ assert !newMethods.contains(newMethodReference);
+ } else if (reservedInterfaceSignatures.containsValue(newSignature)
+ || newMethods.contains(newMethodReference)) {
+ // If the method potentially collides with an interface method or with another virtual method
+ // rename it to a globally fresh name and record the name.
+
+ DexString newMethodName =
+ dexItemFactory.createGloballyFreshMemberString(
+ originalMethodReference.getName().toSourceString(), null);
+ newMethodReference = newMethodReference.withName(newMethodName, dexItemFactory);
+
+ // Record signature renaming so that subclasses perform the identical rename.
+ renamedClassVirtualMethods.put(originalSignature, newMethodReference.getName());
+ } else {
+ // There was no reserved name and the new signature is available.
+
+ if (Iterables.any(
+ newMethodReference.proto.getParameterBaseTypes(dexItemFactory),
+ mergedClasses::isMergeTarget)) {
+ // If any of the parameter types have been merged, record the signature mapping.
+ renamedClassVirtualMethods.put(originalSignature, newMethodReference.getName());
+ }
+ }
+
+ boolean changed = newMethods.add(newMethodReference);
+ assert changed;
+
+ return fixupProgramMethod(newMethodReference, method);
+ }
+
private void fixupFields(List<DexEncodedField> fields, FieldSetter setter) {
if (fields == null) {
return;
@@ -88,9 +387,7 @@
for (int i = 0; i < fields.size(); i++) {
DexEncodedField encodedField = fields.get(i);
DexField field = encodedField.field;
- DexType newType = fixupType(field.type);
- DexType newHolder = fixupType(field.holder);
- DexField newField = appView.dexItemFactory().createField(newHolder, newType, field.name);
+ DexField newField = fixupFieldReference(field);
if (newField != encodedField.field) {
// TODO(b/165498187): track mapped fields
/* lensBuilder.map(field, newField); */
@@ -99,7 +396,13 @@
}
}
- private DexMethod fixupMethod(DexMethod method) {
+ public DexField fixupFieldReference(DexField field) {
+ DexType newType = fixupType(field.type);
+ DexType newHolder = fixupType(field.holder);
+ return appView.dexItemFactory().createField(newHolder, newType, field.name);
+ }
+
+ private DexMethod fixupMethodReference(DexMethod method) {
return appView
.dexItemFactory()
.createMethod(fixupType(method.holder), fixupProto(method.proto), method.name);
@@ -126,11 +429,7 @@
return type.replaceBaseType(fixed, appView.dexItemFactory());
}
if (type.isClassType()) {
- while (true) {
- DexType mapped = lensBuilder.lookupType(type);
- if (mapped == type) break;
- type = mapped;
- }
+ type = mergedClasses.getMergeTargetOrDefault(type);
}
return type;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index c8f2f6d..2bf2b97 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -145,7 +145,8 @@
Integer.max(classFileVersion, method.getDefinition().getClassFileVersion());
}
DexMethod newMethod = moveMethod(method);
- lensBuilder.recordOriginalSignature(method.getReference(), newMethod);
+ lensBuilder.mapMethod(newMethod, newMethod);
+ lensBuilder.mapMethodInverse(method.getReference(), newMethod);
classIdToMethodMap.put(classIdentifiers.getInt(method.getHolderType()), newMethod);
}
@@ -175,7 +176,7 @@
// Map each old method to the newly synthesized method in the graph lens.
for (ProgramMethod oldMethod : methods) {
- lensBuilder.mapMethod(oldMethod.getReference(), newMethodReference);
+ lensBuilder.moveMethod(oldMethod.getReference(), newMethodReference);
}
lensBuilder.recordExtraOriginalSignature(originalMethodReference, newMethodReference);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameNestHost.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameNestHost.java
new file mode 100644
index 0000000..6619878
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameNestHost.java
@@ -0,0 +1,16 @@
+// 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.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
+
+public class SameNestHost extends MultiClassSameReferencePolicy<DexType> {
+ @Override
+ public DexType getMergeKey(DexProgramClass clazz) {
+ return clazz.getNestHost();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameParentClass.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameParentClass.java
index 5b274d1..12c8e31 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameParentClass.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameParentClass.java
@@ -6,23 +6,12 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
-import java.util.Collection;
-import java.util.IdentityHashMap;
-import java.util.LinkedList;
-import java.util.Map;
+import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
-public class SameParentClass extends MultiClassPolicy {
+public class SameParentClass extends MultiClassSameReferencePolicy<DexType> {
@Override
- public Collection<Collection<DexProgramClass>> apply(Collection<DexProgramClass> group) {
- Map<DexType, Collection<DexProgramClass>> groups = new IdentityHashMap<>();
- for (DexProgramClass clazz : group) {
- groups
- .computeIfAbsent(clazz.superType, ignore -> new LinkedList<DexProgramClass>())
- .add(clazz);
- }
- removeTrivialGroups(groups.values());
- return groups.values();
+ public DexType getMergeKey(DexProgramClass clazz) {
+ return clazz.superType;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index 0df3c80..3bc0ebe 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -311,7 +311,6 @@
DexEncodedField field =
appView.appInfo().resolveField(info.getField()).getResolvedField();
if (field == null) {
- assert false;
return;
}
if (!info.hasReflectiveAccess() && !info.isWrittenFromMethodHandle()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/EnumLiteProtoShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/EnumLiteProtoShrinker.java
new file mode 100644
index 0000000..055882b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/EnumLiteProtoShrinker.java
@@ -0,0 +1,148 @@
+// 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.ir.analysis.proto;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+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.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.Sets;
+import java.util.Set;
+
+/**
+ * EnumLite proto enums include the following code: <code>
+ * private static final EnumLiteMap<ProtoEnum> internalValueMap =
+ * new EnumLiteMap<ProtoEnum>() {
+ * public ProtoEnum findValueByNumber(int number) {
+ * return ProtoEnum.forNumber(number);
+ * }
+ * };
+ * </code>
+ *
+ * <p>If the field internalValueMap on the EnumLite is effectively unused (never read), the
+ * anonymous subclass of EnumLiteMap is effectively dead. R8 figures that out however too late,
+ * during the second round of tree shaking, and the bridge findValueByNumber in the anonymous
+ * subclass of EnumLiteMap prevents the EnumLite to be unboxed.
+ *
+ * <p>This class prematurely clears the virtual methods of the anonymous subclasses of EnumLiteMap,
+ * when it can prove that the internalValueMap field of the corresponding EnumLite is never read.
+ */
+public class EnumLiteProtoShrinker {
+
+ private AppView<AppInfoWithLiveness> appView;
+ private ProtoReferences references;
+ private Set<DexType> deadEnumLiteMaps = Sets.newIdentityHashSet();
+
+ public EnumLiteProtoShrinker(AppView<AppInfoWithLiveness> appView, ProtoReferences references) {
+ this.appView = appView;
+ this.references = references;
+ }
+
+ private DexField createInternalValueMapField(DexType holder) {
+ return appView
+ .dexItemFactory()
+ .createField(holder, references.enumLiteMapType, references.internalValueMapFieldName);
+ }
+
+ public void clearDeadEnumLiteMaps() {
+ if (!appView.options().protoShrinking().enableEnumLiteProtoShrinking) {
+ return;
+ }
+ // The optimization only enables further enums to be unboxed, no point to run it if enum
+ // unboxing is disabled.
+ if (!appView.options().enableEnumUnboxing) {
+ return;
+ }
+ // The optimization relies on shrinking and member value propagation to actually clear
+ // the anonymous subclasses of EnumLiteMap.
+ if (!appView.options().isShrinking() || !appView.options().enableValuePropagation) {
+ return;
+ }
+ internalClearDeadEnumLiteMaps();
+ }
+
+ private void internalClearDeadEnumLiteMaps() {
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (clazz.interfaces.contains(references.enumLiteMapType)) {
+ DexProgramClass enumLite = computeCorrespondingEnumLite(clazz);
+ if (enumLite != null) {
+ DexEncodedField field = enumLite.lookupField(createInternalValueMapField(enumLite.type));
+ if (field != null) {
+ if (appView.appInfo().isStaticFieldWrittenOnlyInEnclosingStaticInitializer(field)
+ && !appView.appInfo().isFieldRead(field)) {
+ deadEnumLiteMaps.add(clazz.type);
+ // Clears the EnumLiteMap methods to avoid them being IR processed.
+ clazz.setVirtualMethods(DexEncodedMethod.EMPTY_ARRAY);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Each EnumLiteMap subclass has only two virtual methods findValueByNumber:
+ *
+ * <ul>
+ * <li>EnumLite findValueByNumber(int)
+ * <li>ConcreteEnumLiteSubType findValueByNumber(int)
+ * </ul>
+ *
+ * <p>The method with the EnumLite return type is the bridge, we extract the concrete EnumLite
+ * subtype from the other method return type.
+ *
+ * <p>We bail out if other virtual methods than the two expected ones are found and return null.
+ */
+ private DexProgramClass computeCorrespondingEnumLite(DexProgramClass enumLiteMap) {
+ if (enumLiteMap.getMethodCollection().numberOfVirtualMethods() != 2) {
+ return null;
+ }
+ DexType enumLiteCandidate = null;
+ for (DexEncodedMethod virtualMethod : enumLiteMap.virtualMethods()) {
+ if (!matchesFindValueByNumberMethod(virtualMethod.method)) {
+ return null;
+ }
+ if (virtualMethod.returnType() == references.enumLiteType) {
+ continue;
+ }
+ if (enumLiteCandidate != null) {
+ return null;
+ }
+ enumLiteCandidate = virtualMethod.returnType();
+ }
+ if (enumLiteCandidate == null) {
+ return null;
+ }
+ DexProgramClass enumLite = appView.programDefinitionFor(enumLiteCandidate, enumLiteMap);
+ if (enumLite != null
+ && enumLite.isEnum()
+ && enumLite.interfaces.contains(references.enumLiteType)) {
+ return enumLite;
+ }
+ return null;
+ }
+
+ private boolean matchesFindValueByNumberMethod(DexMethod method) {
+ return method.name == references.findValueByNumberName
+ && method.getArity() == 1
+ && method.getParameters().values[0] == appView.dexItemFactory().intType;
+ }
+
+ public void verifyDeadEnumLiteMapsAreDead() {
+ for (DexType deadEnumLiteMap : deadEnumLiteMaps) {
+ if (appView.appInfo().definitionForWithoutExistenceAssert(deadEnumLiteMap) != null) {
+ throw new CompilationError(
+ "EnumLite Proto Shrinker failure: Type "
+ + deadEnumLiteMap
+ + " was assumed to be dead during optimizations, but it is not.");
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
index 8fd43ee..84aabe8 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
@@ -19,6 +19,7 @@
private final DexItemFactory dexItemFactory;
+ public final DexType enumLiteType;
public final DexType enumLiteMapType;
public final DexType extendableMessageType;
public final DexType extensionDescriptorType;
@@ -41,9 +42,11 @@
public final MethodToInvokeMembers methodToInvokeMembers;
public final DexString defaultInstanceFieldName;
+ public final DexString internalValueMapFieldName;
public final DexString dynamicMethodName;
public final DexString findLiteExtensionByNumberName;
public final DexString newBuilderMethodName;
+ public final DexString findValueByNumberName;
public final DexString protobufPackageDescriptorPrefix;
@@ -58,6 +61,7 @@
dexItemFactory = factory;
// Types.
+ enumLiteType = factory.createType("Lcom/google/protobuf/Internal$EnumLite;");
enumLiteMapType = factory.createType("Lcom/google/protobuf/Internal$EnumLiteMap;");
extendableMessageType =
factory.createType("Lcom/google/protobuf/GeneratedMessageLite$ExtendableMessage;");
@@ -81,9 +85,11 @@
// Names.
defaultInstanceFieldName = factory.createString("DEFAULT_INSTANCE");
+ internalValueMapFieldName = factory.createString("internalValueMap");
dynamicMethodName = factory.createString("dynamicMethod");
findLiteExtensionByNumberName = factory.createString("findLiteExtensionByNumber");
newBuilderMethodName = factory.createString("newBuilder");
+ findValueByNumberName = factory.createString("findValueByNumber");
// Other names.
protobufPackageDescriptorPrefix = factory.createString("Lcom/google/protobuf/");
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java
index 78f9e6e..6cec099 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java
@@ -20,6 +20,7 @@
public final GeneratedExtensionRegistryShrinker generatedExtensionRegistryShrinker;
public final GeneratedMessageLiteShrinker generatedMessageLiteShrinker;
public final GeneratedMessageLiteBuilderShrinker generatedMessageLiteBuilderShrinker;
+ public final EnumLiteProtoShrinker enumProtoShrinker;
public final ProtoReferences references;
private Set<DexType> deadProtoTypes = Sets.newIdentityHashSet();
@@ -41,6 +42,7 @@
appView.options().protoShrinking().enableGeneratedMessageLiteBuilderShrinking
? new GeneratedMessageLiteBuilderShrinker(appView, references)
: null;
+ this.enumProtoShrinker = new EnumLiteProtoShrinker(appView, references);
this.references = references;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index 6deea80..de727c4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -625,7 +625,20 @@
locals, stack, getCanonicalDebugPositionAtOffset(currentInstructionIndex));
// Update the incoming state as well with precise information.
assert incomingState.get(currentBlockIndex) != null;
- incomingState.put(currentBlockIndex, snapshot);
+ if (isFirstFrameInBlock()) {
+ incomingState.put(currentBlockIndex, snapshot);
+ }
+ }
+
+ private boolean isFirstFrameInBlock() {
+ for (int i = currentBlockIndex; i < currentInstructionIndex; i++) {
+ CfInstruction cfInstruction = code.getInstructions().get(i);
+ if (cfInstruction.isPosition() || cfInstruction.isLabel()) {
+ continue;
+ }
+ return false;
+ }
+ return true;
}
private DexType convertUninitialized(FrameType type) {
@@ -716,16 +729,17 @@
return localVariablesWithRegister.get(0).getLocal().type;
}
}
- appView
- .options()
- .reporter
- .warning(
- new CfCodeDiagnostics(
- origin,
- method.getReference(),
- "Could not find phi type for register "
- + register
- + ". This is most likely due to invalid stack maps in input."));
+ // TODO(b/169346184): Delay reporting errors here due to invalid debug info until resolved.
+ // appView
+ // .options()
+ // .reporter
+ // .warning(
+ // new CfCodeDiagnostics(
+ // origin,
+ // method.getReference(),
+ // "Could not find phi type for register "
+ // + register
+ // + ". This is most likely due to invalid stack maps in input."));
return null;
}
if (slot.isPrecise()) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 100131c..b695024 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -543,6 +543,9 @@
}
private boolean needsIRConversion(Code code, ProgramMethod method) {
+ if (options.testing.forceIRForCfToCfDesugar) {
+ return true;
+ }
if (options.isDesugaredLibraryCompilation()) {
// TODO(b/169035524): Create method for evaluating if a code object needs rewriting for
// library desugaring.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
index e0b6ff5..b4bd339 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
@@ -89,7 +89,8 @@
&& staticField.accessFlags.isFinal()
&& staticField.field.name == factory.enumValuesFieldName) {
// Field $VALUES, valid, do nothing.
- } else {
+ } else if (appView.appInfo().isFieldRead(staticField)) {
+ // Only non read static fields are valid, and they are assumed unused.
return false;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index 869cb6e..9bc6826 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMember;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
@@ -15,6 +16,7 @@
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
@@ -42,10 +44,40 @@
originalMethodSignatures,
previousLens,
dexItemFactory);
+ assert noDuplicateEntries(fieldMap, originalFieldSignatures);
+ assert noDuplicateEntries(methodMap, originalMethodSignatures);
this.prototypeChangesPerMethod = prototypeChangesPerMethod;
this.unboxedEnums = unboxedEnums;
}
+ private <T extends DexMember<?, ?>> boolean noDuplicateEntries(
+ Map<T, T> map, BiMap<T, T> originalSignatures) {
+ if (map.size() == originalSignatures.size()) {
+ return true;
+ }
+ IdentityHashMap<T, T> methodMapReverse = new IdentityHashMap<>();
+ IdentityHashMap<T, Set<T>> duplicate = new IdentityHashMap<>();
+ map.forEach(
+ (k, v) -> {
+ if (methodMapReverse.containsKey(v)) {
+ Set<T> dexMethods = duplicate.computeIfAbsent(v, ignored -> Sets.newIdentityHashSet());
+ dexMethods.add(methodMapReverse.get(v));
+ dexMethods.add(k);
+ } else {
+ methodMapReverse.put(v, k);
+ }
+ });
+ assert !duplicate.isEmpty();
+ StringBuilder sb = new StringBuilder();
+ sb.append("Enum unboxing has created duplicate members: \n");
+ duplicate.forEach(
+ (target, origins) -> {
+ sb.append(origins).append(" -> ").append(target).append("\n");
+ });
+ assert false : sb.toString();
+ return false;
+ }
+
@Override
protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 7d40dba..c937356 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -192,7 +192,6 @@
iterator, invokeMethod, zeroCheckMethod, m -> synthesizeZeroCheckMethod());
}
}
- // TODO(b/147860220): rewrite also other enum methods.
} else if (instruction.isInvokeStatic()) {
InvokeStatic invokeStatic = instruction.asInvokeStatic();
DexMethod invokedMethod = invokeStatic.getInvokedMethod();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 879c909..634d74e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -145,14 +145,11 @@
DexClass holder = appView.definitionFor(encodedMethod.holder());
assert holder != null;
if (encodedMethod.isInstanceInitializer()) {
- while (holder.lookupMethod(newMethod) != null) {
- newMethod =
- factory.createMethod(
- newMethod.holder,
- factory.appendTypeToProto(
- newMethod.proto, relocator.getDefaultEnumUnboxingUtility()),
- newMethod.name);
- }
+ newMethod =
+ factory.createInstanceInitializerWithFreshProto(
+ newMethod,
+ relocator.getDefaultEnumUnboxingUtility(),
+ tryMethod -> holder.lookupMethod(tryMethod) == null);
} else {
int index = 0;
while (holder.lookupMethod(newMethod) != null) {
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
index b50015b..fbaf1b8 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
@@ -54,7 +54,7 @@
// package descriptor.
boolean hasPinnedItem = false;
for (DexProgramClass clazz : pkg) {
- boolean isPinned = !appView.appInfo().isMinificationAllowed(clazz.getType());
+ boolean isPinned = !appView.appInfo().isRepackagingAllowed(clazz.getType());
Node classNode = createNode(clazz);
if (isPinned) {
pinnedNodes.add(classNode);
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 6c28f2e..acb52c5 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -409,7 +409,7 @@
AppInfoWithLiveness previous,
DirectMappedDexApplication application,
Set<DexType> removedClasses,
- Collection<DexReference> additionalPinnedItems) {
+ Collection<? extends DexReference> additionalPinnedItems) {
this(
previous.getSyntheticItems().commitPrunedClasses(application, removedClasses),
previous.getClassToFeatureSplitMap().withoutPrunedClasses(removedClasses),
@@ -460,7 +460,7 @@
}
private static KeepInfoCollection extendPinnedItems(
- AppInfoWithLiveness previous, Collection<DexReference> additionalPinnedItems) {
+ AppInfoWithLiveness previous, Collection<? extends DexReference> additionalPinnedItems) {
if (additionalPinnedItems == null || additionalPinnedItems.isEmpty()) {
return previous.keepInfo;
}
@@ -912,6 +912,11 @@
return keepInfo.getInfo(reference, this).isAccessModificationAllowed(options());
}
+ public boolean isRepackagingAllowed(DexType type) {
+ return options().isRepackagingEnabled()
+ && keepInfo.getClassInfo(type, this).isRepackagingAllowed(options());
+ }
+
public boolean isPinned(DexReference reference) {
assert checkIfObsolete();
return keepInfo.isPinned(reference, this);
@@ -941,7 +946,7 @@
public AppInfoWithLiveness prunedCopyFrom(
DirectMappedDexApplication application,
Set<DexType> removedClasses,
- Collection<DexReference> additionalPinnedItems) {
+ Collection<? extends DexReference> additionalPinnedItems) {
assert checkIfObsolete();
if (!removedClasses.isEmpty()) {
// Rebuild the hierarchy.
diff --git a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
index 18ae09e..624ee95 100644
--- a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
@@ -11,4 +11,6 @@
boolean isMinificationEnabled();
boolean isAccessModificationEnabled();
+
+ boolean isRepackagingEnabled();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
index 58367a9..1e32804 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
@@ -33,6 +33,20 @@
return new Joiner(this);
}
+ /**
+ * True if a class may be repackaged.
+ *
+ * <p>This method requires knowledge of the global configuration as that can override the concrete
+ * value on a given item.
+ */
+ public boolean isRepackagingAllowed(GlobalKeepInfoConfiguration configuration) {
+ return configuration.isRepackagingEnabled() && internalIsRepackagingAllowed();
+ }
+
+ boolean internalIsRepackagingAllowed() {
+ return internalIsMinificationAllowed();
+ }
+
@Override
public boolean isTop() {
return this.equals(top());
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 6db253e..951e2e5 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.graph.DexMember;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DirectMappedDexApplication;
@@ -61,13 +60,14 @@
DirectMappedDexApplication application = appView.appInfo().app().asDirect();
Timing timing = application.timing;
timing.begin("Pruning application...");
- DirectMappedDexApplication result;
try {
- result = removeUnused(application).build();
+ DirectMappedDexApplication.Builder builder = removeUnused(application);
+ return prunedTypes.isEmpty() && !appView.options().configurationDebugging
+ ? application
+ : builder.build();
} finally {
timing.end();
}
- return result;
}
private DirectMappedDexApplication.Builder removeUnused(DirectMappedDexApplication application) {
@@ -359,7 +359,7 @@
return Collections.unmodifiableSet(prunedTypes);
}
- public Collection<DexReference> getMethodsToKeepForConfigurationDebugging() {
+ public Collection<DexMethod> getMethodsToKeepForConfigurationDebugging() {
return Collections.unmodifiableCollection(methodsToKeepForConfigurationDebugging);
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
index 4808947..d0ec93e 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
@@ -3,16 +3,22 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.tracereferences;
-import static com.android.tools.r8.utils.ExceptionUtils.STATUS_ERROR;
-
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.Keep;
import com.android.tools.r8.ProgramResource;
+import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.ProgramResourceProvider;
import com.android.tools.r8.Version;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ExceptionDiagnostic;
+import com.android.tools.r8.utils.ExceptionUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
import java.util.HashSet;
import java.util.Set;
@@ -53,7 +59,25 @@
.forEach(provider -> tagetDescriptors.addAll(provider.getClassDescriptors()));
for (ProgramResourceProvider provider : command.getSource()) {
for (ProgramResource programResource : provider.getProgramResources()) {
- tagetDescriptors.removeAll(programResource.getClassDescriptors());
+ if (programResource.getKind() == Kind.DEX) {
+ command
+ .getDiagnosticsHandler()
+ .warning(new StringDiagnostic("DEX files not fully supported"));
+ assert programResource.getClassDescriptors() == null;
+ for (DexProgramClass clazz :
+ new ApplicationReader(
+ AndroidApp.builder()
+ .addDexProgramData(ImmutableList.of(programResource.getBytes()))
+ .build(),
+ new InternalOptions(),
+ Timing.empty())
+ .read()
+ .classes()) {
+ tagetDescriptors.remove(clazz.getType().toDescriptorString());
+ }
+ } else {
+ tagetDescriptors.removeAll(programResource.getClassDescriptors());
+ }
}
}
Tracer tracer = new Tracer(tagetDescriptors, builder.build());
@@ -77,25 +101,20 @@
formatter.format(result);
}
- public static void main(String... args) {
- try {
- TraceReferencesCommand command = TraceReferencesCommand.parse(args, Origin.root()).build();
- if (command.isPrintHelp()) {
- System.out.println(TraceReferencesCommandParser.USAGE_MESSAGE);
- return;
- }
- if (command.isPrintVersion()) {
- System.out.println("referencetrace " + Version.getVersionString());
- return;
- }
- run(command);
- } catch (CompilationFailedException e) {
- System.exit(STATUS_ERROR);
- } catch (Throwable e) {
- System.err.println("ReferenceTrace failed with an internal error.");
- Throwable cause = e.getCause() == null ? e : e.getCause();
- cause.printStackTrace();
- System.exit(STATUS_ERROR);
+ public static void run(String... args) throws CompilationFailedException {
+ TraceReferencesCommand command = TraceReferencesCommand.parse(args, Origin.root()).build();
+ if (command.isPrintHelp()) {
+ System.out.println(TraceReferencesCommandParser.USAGE_MESSAGE);
+ return;
}
+ if (command.isPrintVersion()) {
+ System.out.println("referencetrace " + Version.getVersionString());
+ return;
+ }
+ run(command);
+ }
+
+ public static void main(String[] args) {
+ ExceptionUtils.withMainProgramHandler(() -> run(args));
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 0164b4e..2bbe3fe 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -576,7 +576,7 @@
if (featureSplitConfiguration != null) {
DexType type = dexItemFactory.createType(classDescriptor);
FeatureSplit featureSplit = classToFeatureSplitMap.getFeatureSplit(type);
- if (featureSplit != null) {
+ if (featureSplit != null && !featureSplit.isBase()) {
return featureSplitArchiveOutputStreams.get(featureSplit);
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index a7481b7..15183a1 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -171,6 +171,7 @@
protoShrinking.enableGeneratedMessageLiteShrinking = true;
protoShrinking.enableGeneratedMessageLiteBuilderShrinking = true;
protoShrinking.enableGeneratedExtensionRegistryShrinking = true;
+ protoShrinking.enableEnumLiteProtoShrinking = true;
}
void disableAllOptimizations() {
@@ -530,6 +531,12 @@
return isMinifying();
}
+ @Override
+ public boolean isRepackagingEnabled() {
+ return proguardConfiguration.getPackageObfuscationMode().isSome()
+ && (isShrinking() || isMinifying());
+ }
+
/**
* If any non-static class merging is enabled, information about types referred to by instanceOf
* and check cast instructions needs to be collected.
@@ -1107,6 +1114,10 @@
public boolean isRepackageClasses() {
return this == REPACKAGE;
}
+
+ public boolean isSome() {
+ return !isNone();
+ }
}
public static class OutlineOptions {
@@ -1176,11 +1187,13 @@
public boolean enableGeneratedMessageLiteShrinking = false;
public boolean enableGeneratedMessageLiteBuilderShrinking = false;
public boolean traverseOneOfAndRepeatedProtoFields = false;
+ public boolean enableEnumLiteProtoShrinking = false;
public boolean isProtoShrinkingEnabled() {
return enableGeneratedExtensionRegistryShrinking
|| enableGeneratedMessageLiteShrinking
- || enableGeneratedMessageLiteBuilderShrinking;
+ || enableGeneratedMessageLiteBuilderShrinking
+ || enableEnumLiteProtoShrinking;
}
}
@@ -1256,6 +1269,8 @@
public boolean assertConsistentRenamingOfSignature = false;
public boolean allowStaticInterfaceMethodsForPreNApiLevel = false;
public int verificationSizeLimitInBytesOverride = -1;
+ public boolean forceIRForCfToCfDesugar =
+ System.getProperty("com.android.tools.r8.forceIRForCfToCfDesugar") != null;
// Flag to allow processing of resources in D8. A data resource consumer still needs to be
// specified.
diff --git a/src/main/java/com/android/tools/r8/utils/ThrowingAction.java b/src/main/java/com/android/tools/r8/utils/ThrowingAction.java
new file mode 100644
index 0000000..d5b4fa8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ThrowingAction.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2018, 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.utils;
+
+@FunctionalInterface
+public interface ThrowingAction<E extends Throwable> {
+ void execute() throws E;
+}
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 ad65701..cae7430 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
@@ -19,6 +19,22 @@
return backing.getOrDefault(key, value);
}
+ public Map<K, V> getForwardMap() {
+ return backing;
+ }
+
+ public Set<K> keySet() {
+ return backing.keySet();
+ }
+
+ public boolean hasKey(K key) {
+ return backing.containsKey(key);
+ }
+
+ public boolean hasValue(V value) {
+ return inverse.containsKey(value);
+ }
+
public Set<K> getKeys(V value) {
return inverse.getOrDefault(value, Collections.emptySet());
}
diff --git a/src/main/keep-applymapping.txt b/src/main/keep-applymapping.txt
deleted file mode 100644
index c404d21..0000000
--- a/src/main/keep-applymapping.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright (c) 2019, 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.
-
-# TODO(b/133091438,b/139344231) These rules are needed for applymapping but should be able to be
-# removed when we have --classpath.
--keepclassmembers,allowobfuscation class com.android.tools.r8.ir.optimize.MemberPoolCollection {
- *** buildForHierarchy(...);
-}
--keepclassmembers,allowobfuscation class com.android.tools.r8.jar.CfApplicationWriter {
- void write(com.android.tools.r8.ClassFileConsumer,java.util.concurrent.ExecutorService);
- void writeApplication(com.android.tools.r8.ClassFileConsumer,java.util.concurrent.ExecutorService);
-}
--keep class com.android.tools.r8.BaseCommand {
- com.android.tools.r8.utils.AndroidApp getInputApp();
-}
-
-# Obfuscating the members below can result in naming-conflicts so just keep them.
--keep class com.android.tools.r8.joptsimple.OptionDescriptor {
- java.lang.String argumentDescription();
-}
diff --git a/src/test/examplesJava11/com/android/tools/r8/NeverClassInline.java b/src/test/examplesJava11/com/android/tools/r8/NeverClassInline.java
new file mode 100644
index 0000000..19c4504
--- /dev/null
+++ b/src/test/examplesJava11/com/android/tools/r8/NeverClassInline.java
@@ -0,0 +1,7 @@
+// 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;
+
+public @interface NeverClassInline {}
diff --git a/src/test/examplesJava11/nesthostexample/NeverInline.java b/src/test/examplesJava11/com/android/tools/r8/NeverInline.java
similarity index 75%
rename from src/test/examplesJava11/nesthostexample/NeverInline.java
rename to src/test/examplesJava11/com/android/tools/r8/NeverInline.java
index d800658..cd01ce1 100644
--- a/src/test/examplesJava11/nesthostexample/NeverInline.java
+++ b/src/test/examplesJava11/com/android/tools/r8/NeverInline.java
@@ -1,8 +1,8 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// 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 nesthostexample;
+package com.android.tools.r8;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
diff --git a/src/test/examplesJava11/horizontalclassmerging/BasicNestHostHorizontalClassMerging.java b/src/test/examplesJava11/horizontalclassmerging/BasicNestHostHorizontalClassMerging.java
new file mode 100644
index 0000000..2362165
--- /dev/null
+++ b/src/test/examplesJava11/horizontalclassmerging/BasicNestHostHorizontalClassMerging.java
@@ -0,0 +1,43 @@
+// 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 horizontalclassmerging;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+
+public class BasicNestHostHorizontalClassMerging {
+ // Prevent merging with BasicNestHostHorizontalClassMerging2.
+ private String name;
+
+ private BasicNestHostHorizontalClassMerging(String name) {
+ this.name = name;
+ }
+
+ @NeverInline
+ private void print(String v) {
+ System.out.println(name + ": " + v);
+ }
+
+ public static void main(String[] args) {
+ BasicNestHostHorizontalClassMerging host = new BasicNestHostHorizontalClassMerging("1");
+ new A(host);
+ new B(host);
+ BasicNestHostHorizontalClassMerging2.main(args);
+ }
+
+ @NeverClassInline
+ public static class A {
+ public A(BasicNestHostHorizontalClassMerging parent) {
+ parent.print("a");
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public B(BasicNestHostHorizontalClassMerging parent) {
+ parent.print("b");
+ }
+ }
+}
diff --git a/src/test/examplesJava11/horizontalclassmerging/BasicNestHostHorizontalClassMerging2.java b/src/test/examplesJava11/horizontalclassmerging/BasicNestHostHorizontalClassMerging2.java
new file mode 100644
index 0000000..78718e1
--- /dev/null
+++ b/src/test/examplesJava11/horizontalclassmerging/BasicNestHostHorizontalClassMerging2.java
@@ -0,0 +1,35 @@
+// 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 horizontalclassmerging;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+
+public class BasicNestHostHorizontalClassMerging2 {
+ @NeverInline
+ public static void main(String[] args) {
+ new A();
+ new B();
+ }
+
+ @NeverInline
+ private static void print(String v) {
+ System.out.println("2: " + v);
+ }
+
+ @NeverClassInline
+ public static class A {
+ public A() {
+ print("a");
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public B() {
+ print("b");
+ }
+ }
+}
diff --git a/src/test/examplesJava11/nesthostexample/BasicNestHostClassMerging.java b/src/test/examplesJava11/nesthostexample/BasicNestHostClassMerging.java
index 6e511a0..184c201 100644
--- a/src/test/examplesJava11/nesthostexample/BasicNestHostClassMerging.java
+++ b/src/test/examplesJava11/nesthostexample/BasicNestHostClassMerging.java
@@ -4,6 +4,8 @@
package nesthostexample;
+import com.android.tools.r8.NeverInline;
+
public class BasicNestHostClassMerging {
private String field = "Outer";
diff --git a/src/test/examplesJava11/nesthostexample/BasicNestHostTreePruning.java b/src/test/examplesJava11/nesthostexample/BasicNestHostTreePruning.java
index eb2b3bf..484c81a 100644
--- a/src/test/examplesJava11/nesthostexample/BasicNestHostTreePruning.java
+++ b/src/test/examplesJava11/nesthostexample/BasicNestHostTreePruning.java
@@ -4,6 +4,8 @@
package nesthostexample;
+import com.android.tools.r8.NeverInline;
+
public class BasicNestHostTreePruning {
private String field = System.currentTimeMillis() >= 0 ? "NotPruned" : "Dead";
diff --git a/src/test/examplesJava11/nesthostexample/NestHostInliningSubclasses.java b/src/test/examplesJava11/nesthostexample/NestHostInliningSubclasses.java
index a6283a0..4221307 100644
--- a/src/test/examplesJava11/nesthostexample/NestHostInliningSubclasses.java
+++ b/src/test/examplesJava11/nesthostexample/NestHostInliningSubclasses.java
@@ -4,6 +4,8 @@
package nesthostexample;
+import com.android.tools.r8.NeverInline;
+
public class NestHostInliningSubclasses {
public static class InnerWithPrivAccess extends NestHostInlining.InnerWithPrivAccess {
diff --git a/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java b/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java
index dd76928..cdbe725 100644
--- a/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java
+++ b/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java
@@ -4,6 +4,8 @@
package nesthostexample;
+import com.android.tools.r8.NeverInline;
+
public class NestPvtMethodCallInlined {
public static class Inner {
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 4e8d99e..7c68c50 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -101,6 +101,7 @@
@Override
@Test
public void lambdaDesugaring() throws Throwable {
+ expectThrowsWithHorizontalClassMerging();
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
.withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
.withOptionConsumer(opts -> opts.enableClassInlining = false)
@@ -120,6 +121,7 @@
@Test
public void testMultipleInterfacesLambdaOutValue() throws Throwable {
+ expectThrowsWithHorizontalClassMerging();
// We can only remove trivial check casts for the lambda objects if we keep track all the
// multiple interfaces we additionally specified for the lambdas
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
@@ -141,6 +143,7 @@
@Test
@IgnoreIfVmOlderThan(Version.V7_0_0)
public void lambdaDesugaringWithDefaultMethods() throws Throwable {
+ expectThrowsWithHorizontalClassMerging();
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
.withMinApiLevel(AndroidApiLevel.N)
.withOptionConsumer(opts -> opts.enableClassInlining = false)
@@ -161,6 +164,7 @@
@Override
@Test
public void lambdaDesugaringNPlus() throws Throwable {
+ expectThrowsWithHorizontalClassMerging();
test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
.withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
.withInterfaceMethodDesugaring(OffOrAuto.Auto)
@@ -185,6 +189,7 @@
@Test
@IgnoreIfVmOlderThan(Version.V7_0_0)
public void lambdaDesugaringNPlusWithDefaultMethods() throws Throwable {
+ expectThrowsWithHorizontalClassMerging();
test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
.withMinApiLevel(AndroidApiLevel.N)
.withInterfaceMethodDesugaring(OffOrAuto.Auto)
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
index 9ac01a0..8edbd69 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
@@ -51,6 +51,7 @@
@Test
public void invokeCustomWithShrinking() throws Throwable {
+ expectThrowsWithHorizontalClassMerging();
test("invokecustom-with-shrinking", "invokecustom", "InvokeCustom")
.withMinApiLevel(AndroidApiLevel.P.getLevel())
.withBuilderTransformation(builder ->
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 6568713..c72a3ea 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -53,8 +53,9 @@
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
-public abstract class RunExamplesAndroidOTest
- <B extends BaseCommand.Builder<? extends BaseCommand, B>> {
+public abstract class RunExamplesAndroidOTest<
+ B extends BaseCommand.Builder<? extends BaseCommand, B>>
+ extends TestBase {
static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR;
abstract class TestRunner<C extends TestRunner<C>> {
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
index 661c815..d06d976 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
@@ -45,8 +45,9 @@
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
-public abstract class RunExamplesAndroidPTest
- <B extends BaseCommand.Builder<? extends BaseCommand, B>> {
+public abstract class RunExamplesAndroidPTest<
+ B extends BaseCommand.Builder<? extends BaseCommand, B>>
+ extends TestBase {
static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_ANDROID_P_BUILD_DIR;
abstract class TestRunner<C extends TestRunner<C>> {
diff --git a/src/test/java/com/android/tools/r8/SwitchDebugLocalsConflictTest.java b/src/test/java/com/android/tools/r8/SwitchDebugLocalsConflictTest.java
index 7a679e5..7a76f4a 100644
--- a/src/test/java/com/android/tools/r8/SwitchDebugLocalsConflictTest.java
+++ b/src/test/java/com/android/tools/r8/SwitchDebugLocalsConflictTest.java
@@ -39,8 +39,6 @@
diagnotics.assertNoErrors();
diagnotics.assertInfoThatMatches(
diagnosticMessage(containsString("invalid locals information")));
- diagnotics.assertAllWarningsMatch(
- diagnosticMessage(containsString("Could not find phi type for register 14")));
});
}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index b76e08f..c3c51de 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -8,6 +8,7 @@
import static com.google.common.collect.Lists.cartesianProduct;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.DataResourceProvider.Visitor;
@@ -71,9 +72,12 @@
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.PreloadedClassFileProvider;
+import com.android.tools.r8.utils.ReflectiveBuildPathUtils;
+import com.android.tools.r8.utils.ReflectiveBuildPathUtils.ExamplesClass;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.TestDescriptionWatcher;
+import com.android.tools.r8.utils.ThrowingAction;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -118,6 +122,7 @@
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
+import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
@@ -351,6 +356,8 @@
// Actually running r8.jar in a forked process.
private static final boolean RUN_R8_JAR = System.getProperty("run_r8_jar") != null;
+ @Rule public ExpectedException thrown = ExpectedException.none();
+
@Rule
public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
@@ -1649,6 +1656,10 @@
return clazz.getTypeName();
}
+ public static String examplesTypeName(Class<? extends ExamplesClass> clazz) throws Exception {
+ return ReflectiveBuildPathUtils.resolveClassName(clazz);
+ }
+
public static AndroidApiLevel apiLevelWithDefaultInterfaceMethodsSupport() {
return AndroidApiLevel.N;
}
@@ -1688,4 +1699,32 @@
.compile()
.writeToZip();
}
+
+ public static <E extends Throwable> void assertThrowsWithHorizontalClassMerging(
+ ThrowingAction<E> action) throws E {
+ try {
+ action.execute();
+ if (isHorizontalClassMergingEnabled()) {
+ fail();
+ }
+ } catch (Throwable throwable) {
+ if (!isHorizontalClassMergingEnabled()) {
+ throw throwable;
+ }
+ }
+ }
+
+ public void expectThrowsWithHorizontalClassMerging() {
+ expectThrowsWithHorizontalClassMergingIf(true);
+ }
+
+ public void expectThrowsWithHorizontalClassMergingIf(boolean condition) {
+ if (isHorizontalClassMergingEnabled() && condition) {
+ thrown.expect(Throwable.class);
+ }
+ }
+
+ private static boolean isHorizontalClassMergingEnabled() {
+ return System.getProperty("com.android.tools.r8.horizontalClassMerging") != null;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/TestBaseBuilder.java b/src/test/java/com/android/tools/r8/TestBaseBuilder.java
index 1fe43fb..1e75de5 100644
--- a/src/test/java/com/android/tools/r8/TestBaseBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBaseBuilder.java
@@ -10,6 +10,8 @@
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.ReflectiveBuildPathUtils;
+import com.android.tools.r8.utils.ReflectiveBuildPathUtils.ExamplesRootPackage;
import com.google.common.collect.ImmutableMap;
import java.nio.file.Path;
import java.util.Arrays;
@@ -54,6 +56,12 @@
return self();
}
+ public T addExamplesProgramFiles(Class<? extends ExamplesRootPackage> rootPackage)
+ throws Exception {
+ Collection<Path> files = ReflectiveBuildPathUtils.allClassFiles(rootPackage);
+ return addProgramFiles(files);
+ }
+
public T addLibraryProvider(ClassFileResourceProvider provider) {
builder.addLibraryResourceProvider(provider);
return self();
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index e6fd9e0..9740354 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -790,6 +790,30 @@
return Files.exists(path);
}
+ public static boolean shouldHaveAndroidJar(AndroidApiLevel apiLevel) {
+ switch (apiLevel) {
+ case B_1_1:
+ case C:
+ case D:
+ case E:
+ case E_0_1:
+ case E_MR1:
+ case F:
+ case G:
+ case G_MR1:
+ case H:
+ case H_MR1:
+ case H_MR2:
+ case J:
+ case J_MR1:
+ case J_MR2:
+ case K_WATCH:
+ return false;
+ default:
+ return true;
+ }
+ }
+
public static Path getAndroidJar(AndroidApiLevel apiLevel) {
Path path = getAndroidJarPath(apiLevel);
assert Files.exists(path)
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
index 7756299..bf103e4 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
@@ -47,6 +47,7 @@
@Test
public void testStaticMethodRelaxation() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
String expectedOutput =
StringUtils.lines(
"A::baz()",
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
index 76ebd58..09bf5e1 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
@@ -64,6 +64,7 @@
*/
@Test
public void test_bridgeTargetInBase_differentBridges() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
JasminBuilder jasminBuilder = new JasminBuilder();
ClassBuilder absCls = jasminBuilder.addClass("AbsCls");
@@ -185,6 +186,7 @@
*/
@Test
public void test_bridgeTargetInBase_bridgeAndNonBridge() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
JasminBuilder jasminBuilder = new JasminBuilder();
ClassBuilder baseCls = jasminBuilder.addClass("Base");
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
index 32fa0b8..aed9abc 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
@@ -39,6 +39,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(RemoveVisibilityBridgeMethodsTest.class)
.addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
index c9f7d21..fc125dc 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
@@ -38,6 +38,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addProgramClasses(TestClass.class)
.addInnerClasses(BridgeHoistingAccessibilityTestClasses.class)
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
index d764895..2665473 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
@@ -34,6 +34,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addProgramClasses(TestClass.class, A.class, B3.class, B4.class)
.addProgramClassFileData(
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index 1bde24a..4baa16c 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -71,10 +71,13 @@
@BeforeClass
public static void beforeAll() throws Exception {
- if (data().stream().count() > 0) {
- r8R8Debug = compileR8(CompilationMode.DEBUG);
- r8R8Release = compileR8(CompilationMode.RELEASE);
- }
+ assertThrowsWithHorizontalClassMerging(
+ () -> {
+ if (data().stream().count() > 0) {
+ r8R8Debug = compileR8(CompilationMode.DEBUG);
+ r8R8Release = compileR8(CompilationMode.RELEASE);
+ }
+ });
}
@Parameters(name = "{0}")
@@ -120,6 +123,7 @@
@Test
public void testRetrace() throws IOException {
+ expectThrowsWithHorizontalClassMerging();
ProcessResult processResult =
ToolHelper.runProcess(
new ProcessBuilder()
@@ -205,6 +209,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
Path helloJar = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "hello" + JAR_EXTENSION);
ProcessResult runResult = ToolHelper.runJava(helloJar, "hello.Hello");
assertEquals(0, runResult.exitCode);
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
index 722e4f6..1c7a809 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
@@ -73,6 +73,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
// Run hello.jar to ensure it exists and is valid.
Path hello = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "hello" + JAR_EXTENSION);
ProcessResult runHello = ToolHelper.runJava(hello, "hello.Hello");
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassTest.java
new file mode 100644
index 0000000..5edc29f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassTest.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.isPrivate;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isStatic;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.classmerging.horizontal.NestClassTest.R.horizontalclassmerging.BasicNestHostHorizontalClassMerging;
+import com.android.tools.r8.classmerging.horizontal.NestClassTest.R.horizontalclassmerging.BasicNestHostHorizontalClassMerging2;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.ReflectiveBuildPathUtils.ExamplesClass;
+import com.android.tools.r8.utils.ReflectiveBuildPathUtils.ExamplesJava11RootPackage;
+import com.android.tools.r8.utils.ReflectiveBuildPathUtils.ExamplesPackage;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runners.Parameterized;
+
+public class NestClassTest extends HorizontalClassMergingTestBase {
+ public static class R extends ExamplesJava11RootPackage {
+ public static class horizontalclassmerging extends ExamplesPackage {
+ public static class BasicNestHostHorizontalClassMerging extends ExamplesClass {
+ public static class A extends ExamplesClass {}
+
+ public static class B extends ExamplesClass {}
+ }
+
+ public static class BasicNestHostHorizontalClassMerging2 extends ExamplesClass {
+ public static class A extends ExamplesClass {}
+
+ public static class B extends ExamplesClass {}
+ }
+ }
+ }
+
+ public NestClassTest(TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Parameterized.Parameters(name = "{0}, horizontalClassMerging:{1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimesStartingFromIncluding(CfVm.JDK11).build(),
+ BooleanUtils.values());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addKeepMainRule(examplesTypeName(BasicNestHostHorizontalClassMerging.class))
+ .addExamplesProgramFiles(R.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .compile()
+ .run(parameters.getRuntime(), examplesTypeName(BasicNestHostHorizontalClassMerging.class))
+ .assertSuccessWithOutputLines("1: a", "1: b", "2: a", "2: b")
+ .inspect(
+ codeInspector -> {
+ ClassSubject class1A =
+ codeInspector.clazz(
+ examplesTypeName(BasicNestHostHorizontalClassMerging.A.class));
+ ClassSubject class2A =
+ codeInspector.clazz(
+ examplesTypeName(BasicNestHostHorizontalClassMerging2.A.class));
+ ClassSubject class1 =
+ codeInspector.clazz(examplesTypeName(BasicNestHostHorizontalClassMerging.class));
+ ClassSubject class2 =
+ codeInspector.clazz(examplesTypeName(BasicNestHostHorizontalClassMerging2.class));
+ assertThat(class1, isPresent());
+ assertThat(class2, isPresent());
+ assertThat(class1A, isPresent());
+ assertThat(class2A, isPresent());
+
+ MethodSubject printClass1MethodSubject =
+ class1.method("void", "print", String.class.getTypeName());
+ assertThat(printClass1MethodSubject, isPresent());
+ assertThat(printClass1MethodSubject, isPrivate());
+
+ MethodSubject printClass2MethodSubject =
+ class2.method("void", "print", String.class.getTypeName());
+ assertThat(printClass2MethodSubject, isPresent());
+ assertThat(printClass2MethodSubject, isPrivate());
+ assertThat(printClass2MethodSubject, isStatic());
+
+ assertThat(
+ codeInspector.clazz(
+ examplesTypeName(BasicNestHostHorizontalClassMerging.B.class)),
+ notIf(isPresent(), enableHorizontalClassMerging));
+ assertThat(
+ codeInspector.clazz(
+ examplesTypeName(BasicNestHostHorizontalClassMerging2.B.class)),
+ notIf(isPresent(), enableHorizontalClassMerging));
+
+ // TODO(b/165517236): Explicitly check 1.B is merged into 1.A, and 2.B into 2.A.
+ });
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerCollisionTest.java
new file mode 100644
index 0000000..b603f66
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerCollisionTest.java
@@ -0,0 +1,113 @@
+// 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 com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class TreeFixerCollisionTest extends HorizontalClassMergingTestBase {
+ public TreeFixerCollisionTest(TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(
+ "print a: foo a", "print b: foo b", "print d: foo a", "print e: foo b")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
+ assertThat(codeInspector.clazz(Group2.class), isPresent());
+ assertThat(codeInspector.clazz(C.class), isPresent());
+ assertThat(codeInspector.clazz(D.class), isPresent());
+ assertThat(
+ codeInspector.clazz(E.class), notIf(isPresent(), enableHorizontalClassMerging));
+ });
+ }
+
+ @NeverClassInline
+ public static class A {
+ @NeverInline
+ public String foo() {
+ return "foo a";
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+
+ @NeverInline
+ public String foo() {
+ return "foo b";
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ public static class C {
+ @NeverInline
+ public void print(A a) {
+ System.out.println("print a: " + a.foo());
+ }
+
+ @NeverInline
+ public void print(B a) {
+ System.out.println("print b: " + a.foo());
+ }
+ }
+
+ @NoVerticalClassMerging
+ @NoHorizontalClassMerging
+ public static class Group2 {}
+
+ @NeverClassInline
+ public static class D extends Group2 {
+ @NeverInline
+ public void print(A a) {
+ System.out.println("print d: " + a.foo());
+ }
+ }
+
+ @NeverClassInline
+ public static class E extends Group2 {
+ @NeverInline
+ public void print(B b) {
+ System.out.println("print e: " + b.foo());
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ A a = new A();
+ B b = new B();
+ C c = new C();
+ c.print(a);
+ c.print(b);
+ new D().print(a);
+ new E().print(b);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerConstructorCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerConstructorCollisionTest.java
new file mode 100644
index 0000000..a5c5555
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerConstructorCollisionTest.java
@@ -0,0 +1,109 @@
+// 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 com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.TreeFixerCollisionTest.A;
+import com.android.tools.r8.classmerging.horizontal.TreeFixerCollisionTest.B;
+import com.android.tools.r8.classmerging.horizontal.TreeFixerCollisionTest.C;
+import com.android.tools.r8.classmerging.horizontal.TreeFixerCollisionTest.Main;
+import org.junit.Test;
+
+public class TreeFixerConstructorCollisionTest extends HorizontalClassMergingTestBase {
+ public TreeFixerConstructorCollisionTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(
+ "constructor a",
+ "constructor 2 a",
+ "constructor b",
+ "constructor 2 b",
+ "constructor c a: foo a: c1",
+ "constructor c b: foo b: c2")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
+ assertThat(codeInspector.clazz(C.class), isPresent());
+ });
+ }
+
+ @NeverClassInline
+ public static class A {
+ public A() {
+ System.out.println("constructor a");
+ }
+
+ public A(A a) {
+ System.out.println("constructor 2 a");
+ }
+
+ @NeverInline
+ public String foo(String v) {
+ return "foo a: " + v;
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public B() {
+ System.out.println("constructor b");
+ }
+
+ public B(B b) {
+ System.out.println("constructor 2 b");
+ }
+
+ @NeverInline
+ public String foo(String v) {
+ return "foo b: " + v;
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ public static class C {
+ public C(A a, String v) {
+ System.out.println("constructor c a: " + a.foo(v));
+ }
+
+ public C(B b, String v) {
+ System.out.println("constructor c b: " + b.foo(v));
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ A a = new A();
+ a = new A(a);
+ B b = new B();
+ b = new B(b);
+ new C(a, "c1");
+ new C(b, "c2");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java
new file mode 100644
index 0000000..f1301a0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java
@@ -0,0 +1,135 @@
+// 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 com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.TreeFixerInterfaceFixedCollisionTest.B;
+import com.android.tools.r8.classmerging.horizontal.TreeFixerInterfaceImplementedByParentTest.I;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+
+/**
+ * This test creates a conflict between Parent#foo(B) and C#foo(A), because A and B are merged.
+ * Normally C#foo(A) would be renamed, but because it is an interface method it should not be
+ * changed.
+ */
+public class TreeFixerInterfaceCollisionTest extends HorizontalClassMergingTestBase {
+ public TreeFixerInterfaceCollisionTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .noMinification()
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("print a: c", "print b: parent", "print a: e")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
+
+ ClassSubject parentClassSubject = codeInspector.clazz(Parent.class);
+ assertThat(parentClassSubject, isPresent());
+ if (enableHorizontalClassMerging) {
+ // Parent#foo is renamed to Parent#foo1 to prevent collision.
+ assertThat(parentClassSubject.uniqueMethodWithName("foo$1"), isPresent());
+ }
+
+ ClassSubject cClassSubject = codeInspector.clazz(C.class);
+ assertThat(cClassSubject, isPresent());
+ // C#foo is not renamed to match interface.
+ assertThat(cClassSubject.uniqueMethodWithName("foo"), isPresent());
+
+ ClassSubject iClassSubject = codeInspector.clazz(I.class);
+ assertThat(iClassSubject, isPresent());
+ assertThat(iClassSubject.uniqueMethodWithFinalName("foo"), isPresent());
+ });
+ }
+
+ @NeverClassInline
+ @NoVerticalClassMerging
+ public interface I {
+ @NeverInline
+ void foo(A a);
+ }
+
+ @NoVerticalClassMerging
+ @NoHorizontalClassMerging
+ public static class Parent {
+ @NeverInline
+ void foo(B b) {
+ b.print("parent");
+ }
+ }
+
+ @NeverClassInline
+ public static class A {
+ @NeverInline
+ public void print(String v) {
+ System.out.println("print a: " + v);
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ @NeverInline
+ public void print(String v) {
+ System.out.println("print b: " + v);
+ }
+ }
+
+ public static class C extends Parent implements I {
+ @Override
+ @NeverInline
+ public void foo(A a) {
+ a.print("c");
+ }
+ }
+
+ @NeverClassInline
+ public static class E implements I {
+ @NeverInline
+ public void foo(A a) {
+ a.print("e");
+ }
+ }
+
+ public static class Main {
+ @NeverInline
+ public static void fooI(I i, A a) {
+ i.foo(a);
+ }
+
+ public static void main(String[] args) {
+ A a = new A();
+ B b = new B();
+ C c = new C();
+ I i = c;
+ fooI(i, a);
+ c.foo(b);
+ fooI(new E(), a);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java
new file mode 100644
index 0000000..2f6895a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java
@@ -0,0 +1,137 @@
+// 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 com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.TreeFixerInterfaceCollisionTest.C;
+import com.android.tools.r8.classmerging.horizontal.TreeFixerInterfaceImplementedByParentTest.B;
+import com.android.tools.r8.classmerging.horizontal.TreeFixerInterfaceImplementedByParentTest.E;
+import com.android.tools.r8.classmerging.horizontal.TreeFixerInterfaceImplementedByParentTest.I;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+
+/**
+ * This test creates a conflict between Parent#foo(B) and C#foo(A), because A and B are merged.
+ * Normally C#foo(A) would be renamed, but because it is an interface method it should not be
+ * changed.
+ */
+public class TreeFixerInterfaceFixedCollisionTest extends HorizontalClassMergingTestBase {
+ public TreeFixerInterfaceFixedCollisionTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .noMinification()
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("print a: parent", "print b: c", "print b: e")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
+
+ ClassSubject parentClassSubject = codeInspector.clazz(Parent.class);
+ assertThat(parentClassSubject, isPresent());
+ if (enableHorizontalClassMerging) {
+ // Parent#foo is renamed to Parent#foo1 to prevent collision.
+ assertThat(parentClassSubject.uniqueMethodWithFinalName("foo$1"), isPresent());
+ }
+
+ ClassSubject cClassSubject = codeInspector.clazz(C.class);
+ assertThat(cClassSubject, isPresent());
+ // C#foo is not renamed to match interface.
+ assertThat(cClassSubject.uniqueMethodWithName("foo"), isPresent());
+
+ ClassSubject iClassSubject = codeInspector.clazz(I.class);
+ assertThat(iClassSubject, isPresent());
+ assertThat(iClassSubject.uniqueMethodWithFinalName("foo"), isPresent());
+ });
+ }
+
+ @NeverClassInline
+ @NoVerticalClassMerging
+ public interface I {
+ @NeverInline
+ void foo(B b);
+ }
+
+ @NoVerticalClassMerging
+ @NoHorizontalClassMerging
+ public static class Parent {
+ @NeverInline
+ void foo(A a) {
+ a.print("parent");
+ }
+ }
+
+ @NeverClassInline
+ public static class A {
+ @NeverInline
+ public void print(String v) {
+ System.out.println("print a: " + v);
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ @NeverInline
+ public void print(String v) {
+ System.out.println("print b: " + v);
+ }
+ }
+
+ public static class C extends Parent implements I {
+ @Override
+ @NeverInline
+ public void foo(B b) {
+ b.print("c");
+ }
+ }
+
+ @NeverClassInline
+ public static class E implements I {
+ @NeverInline
+ public void foo(B b) {
+ b.print("e");
+ }
+ }
+
+ public static class Main {
+ @NeverInline
+ public static void fooI(I i, B b) {
+ i.foo(b);
+ }
+
+ public static void main(String[] args) {
+ A a = new A();
+ B b = new B();
+ C c = new C();
+ I i = c;
+ c.foo(a);
+ fooI(i, b);
+ fooI(new E(), b);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceImplementedByParentTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceImplementedByParentTest.java
new file mode 100644
index 0000000..b6a320c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceImplementedByParentTest.java
@@ -0,0 +1,132 @@
+// 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 com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+
+/**
+ * This test creates a conflict between Parent#foo(B) and C#foo(A), because A and B are merged.
+ * Normally C#foo(A) would be renamed, but because it is an interface method it should not be
+ * changed.
+ */
+public class TreeFixerInterfaceImplementedByParentTest extends HorizontalClassMergingTestBase {
+ public TreeFixerInterfaceImplementedByParentTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .noMinification()
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("print a: parent", "print b: i", "print b: e")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
+
+ ClassSubject parentClassSubject = codeInspector.clazz(Parent.class);
+ assertThat(parentClassSubject, isPresent());
+ // Parent #foo(A) is kept as is.
+ if (enableHorizontalClassMerging) {
+ // Parent#foo(B) is renamed to Parent#foo1 to prevent collision.
+ assertThat(parentClassSubject.uniqueMethodWithFinalName("foo"), isPresent());
+ assertThat(parentClassSubject.uniqueMethodWithFinalName("foo$1"), isPresent());
+ }
+
+ assertThat(codeInspector.clazz(C.class), isPresent());
+
+ ClassSubject iClassSubject = codeInspector.clazz(I.class);
+ assertThat(iClassSubject, isPresent());
+ assertThat(iClassSubject.uniqueMethodWithFinalName("foo"), isPresent());
+ });
+ }
+
+ @NeverClassInline
+ @NoVerticalClassMerging
+ public interface I {
+ @NeverInline
+ void foo(B b);
+ }
+
+ @NoVerticalClassMerging
+ @NoHorizontalClassMerging
+ public static class Parent {
+ @NeverInline
+ public void foo(B b) {
+ b.print("i");
+ }
+
+ @NeverInline
+ public void foo(A a) {
+ a.print("parent");
+ }
+ }
+
+ @NeverClassInline
+ public static class A {
+ @NeverInline
+ public void print(String v) {
+ System.out.println("print a: " + v);
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ @NeverInline
+ public void print(String v) {
+ System.out.println("print b: " + v);
+ }
+ }
+
+ @NeverClassInline
+ public static class C extends Parent implements I {}
+
+ @NeverClassInline
+ public static class E implements I {
+ @NeverInline
+ public void foo(B b) {
+ b.print("e");
+ }
+ }
+
+ public static class Main {
+ @NeverInline
+ public static void fooI(I i, B b) {
+ i.foo(b);
+ }
+
+ public static void main(String[] args) {
+ A a = new A();
+ B b = new B();
+ C c = new C();
+ I i = c;
+ c.foo(a);
+ fooI(i, b);
+ fooI(new E(), b);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java
new file mode 100644
index 0000000..e5c6561
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java
@@ -0,0 +1,110 @@
+// 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 com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+
+public class TreeFixerSubClassCollisionTest extends HorizontalClassMergingTestBase {
+ public TreeFixerSubClassCollisionTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .noMinification()
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("print a: foo c a", "print b: foo c b", "print b: foo d b")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
+
+ ClassSubject cClassSubject = codeInspector.clazz(C.class);
+ assertThat(cClassSubject, isPresent());
+ // C#foo(B) is renamed to C#foo$2(A) to not collide with D#foo$1(A).
+ if (enableHorizontalClassMerging) {
+ assertThat(cClassSubject.uniqueMethodWithFinalName("foo"), isPresent());
+ assertThat(cClassSubject.uniqueMethodWithFinalName("foo$2"), isPresent());
+ }
+
+ ClassSubject dClassSubject = codeInspector.clazz(D.class);
+ assertThat(dClassSubject, isPresent());
+ assertThat(dClassSubject.uniqueMethodWithFinalName("foo$1"), isPresent());
+ });
+ }
+
+ @NeverClassInline
+ public static class A {
+ @NeverInline
+ public void print(String v) {
+ System.out.println("print a: " + v);
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ @NeverInline
+ public void print(String v) {
+ System.out.println("print b: " + v);
+ }
+ }
+
+ @NoHorizontalClassMerging
+ @NeverClassInline
+ public static class C {
+ @NeverInline
+ public void foo(A a) {
+ a.print("foo c a");
+ }
+
+ @NeverInline
+ public void foo(B b) {
+ b.print("foo c b");
+ }
+ }
+
+ @NoVerticalClassMerging
+ @NeverClassInline
+ public static class D extends C {
+ @NeverInline
+ public void foo$1(B b) {
+ b.print("foo d b");
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ A a = new A();
+ B b = new B();
+ C c = new C();
+ c.foo(a);
+ c.foo(b);
+ D d = new D();
+ d.foo$1(b);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodSuperMerged.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodSuperMergedTest.java
similarity index 93%
rename from src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodSuperMerged.java
rename to src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodSuperMergedTest.java
index 19c458f..3d29697 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodSuperMerged.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodSuperMergedTest.java
@@ -14,8 +14,9 @@
import com.android.tools.r8.TestParameters;
import org.junit.Test;
-public class VirtualMethodSuperMerged extends HorizontalClassMergingTestBase {
- public VirtualMethodSuperMerged(TestParameters parameters, boolean enableHorizontalClassMerging) {
+public class VirtualMethodSuperMergedTest extends HorizontalClassMergingTestBase {
+ public VirtualMethodSuperMergedTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
super(parameters, enableHorizontalClassMerging);
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerVisibilityTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerVisibilityTest.java
index 9d545e5..937953a 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerVisibilityTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerVisibilityTest.java
@@ -37,6 +37,7 @@
@Test
public void testStaticClassIsRemoved() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
CodeInspector inspector =
testForR8(parameters.getBackend())
.addInnerClasses(StaticClassMergerVisibilityTest.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/IncorrectRewritingOfInvokeSuperTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/IncorrectRewritingOfInvokeSuperTest.java
index 505aa07..156836d 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/IncorrectRewritingOfInvokeSuperTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/IncorrectRewritingOfInvokeSuperTest.java
@@ -30,6 +30,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(IncorrectRewritingOfInvokeSuperTest.class)
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 2411076..4c5d998 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -125,6 +125,7 @@
@Test
public void testClassesHaveBeenMerged() throws Throwable {
+ expectThrowsWithHorizontalClassMerging();
runR8(EXAMPLE_KEEP, this::configure);
// GenericInterface should be merged into GenericInterfaceImpl.
for (String candidate : CAN_BE_MERGED) {
@@ -410,6 +411,7 @@
@Test
public void testMethodCollision() throws Throwable {
+ expectThrowsWithHorizontalClassMerging();
String main = "classmerging.MethodCollisionTest";
Path[] programFiles =
new Path[] {
@@ -1009,6 +1011,7 @@
@Test
public void testSyntheticBridgeSignatures() throws Throwable {
+ expectThrowsWithHorizontalClassMerging();
// Try both with and without inlining. If the bridge signatures are not updated properly, and
// inlining is enabled, then there can be issues with our inlining invariants regarding the
// outermost caller. If inlining is disabled, there is a risk that the methods will end up
@@ -1082,6 +1085,7 @@
// should be updated, and class A should be removed entirely.
@Test
public void testExceptionTables() throws Throwable {
+ expectThrowsWithHorizontalClassMerging();
String main = "classmerging.ExceptionTest";
Path[] programFiles =
new Path[] {
@@ -1218,6 +1222,7 @@
@Test
public void testNoIllegalClassAccessWithAccessModifications() throws Throwable {
+ expectThrowsWithHorizontalClassMerging();
// If access modifications are allowed then SimpleInterface should be merged into
// SimpleInterfaceImpl.
String main = "classmerging.SimpleInterfaceAccessTest";
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
index 99b1316..380703c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
@@ -6,10 +6,7 @@
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.L8Command;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.OutputMode;
import com.android.tools.r8.StringResource;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
@@ -139,18 +136,8 @@
.assertSuccessWithOutput(expectedOutput());
} else {
// Build the desugared library in class file format.
- Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs.jar");
- L8Command.Builder l8Builder =
- L8Command.builder()
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
- .addProgramFiles(ToolHelper.getDesugarJDKLibs())
- .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
- .setMode(CompilationMode.DEBUG)
- .addDesugaredLibraryConfiguration(
- StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
- .setMinApiLevel(parameters.getApiLevel().getLevel())
- .setOutput(desugaredLib, OutputMode.ClassFile);
- ToolHelper.runL8(l8Builder.build(), this::configurationForLibraryCompilation);
+ Path desugaredLib =
+ getDesugaredLibraryInCF(parameters, this::configurationForLibraryCompilation);
// Run on the JVM with desuagred library on classpath.
testForJvm()
@@ -163,6 +150,8 @@
@Test
public void testBufferedReaderD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
Assume.assumeTrue(parameters.getRuntime().isDex());
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
@@ -191,6 +180,8 @@
@Test
public void testBufferedReaderR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
Assume.assumeTrue(parameters.getRuntime().isDex());
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassTest.java
index ae709f3..0fcb214 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassTest.java
@@ -4,13 +4,18 @@
package com.android.tools.r8.desugar.desugaredlibrary;
+import static org.junit.Assert.assertEquals;
+
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -28,7 +33,8 @@
@Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
public static List<Object[]> data() {
return buildParameters(
- BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ BooleanUtils.values(),
+ getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
}
public ConcurrentHashMapSubclassTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
@@ -38,6 +44,9 @@
@Test
public void testCustomCollectionD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
+ Assume.assumeTrue(parameters.getRuntime().isDex());
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.addInnerClasses(ConcurrentHashMapSubclassTest.class)
@@ -54,7 +63,48 @@
}
@Test
+ public void testCustomCollectionD8CF() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ Path jar =
+ testForD8(Backend.CF)
+ .addInnerClasses(ConcurrentHashMapSubclassTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .writeToZip();
+ String desugaredLibraryKeepRules = "";
+ if (shrinkDesugaredLibrary && keepRuleConsumer.get() != null) {
+ // Collection keep rules is only implemented in the DEX writer.
+ assertEquals(0, keepRuleConsumer.get().length());
+ desugaredLibraryKeepRules = "-keep class * { *; }";
+ }
+ if (parameters.getRuntime().isDex()) {
+ testForD8()
+ .addProgramFiles(jar)
+ .setMinApi(parameters.getApiLevel())
+ .disableDesugaring()
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ desugaredLibraryKeepRules,
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ } else {
+ testForJvm()
+ .addProgramFiles(jar)
+ .addRunClasspathFiles(getDesugaredLibraryInCF(parameters, options -> {}))
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+ }
+
+ @Test
public void testCustomCollectionR8() throws Exception {
+ Assume.assumeTrue(parameters.getRuntime().isDex());
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForR8(Backend.DEX)
.addInnerClasses(ConcurrentHashMapSubclassTest.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
index 8334f1d..0a3ce0a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
@@ -45,6 +45,8 @@
@Test
public void testCustomCollectionD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.addInnerClasses(CustomCollectionForwardingTest.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionInterfaceSuperTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionInterfaceSuperTest.java
index 4a92fd8..111ad12 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionInterfaceSuperTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionInterfaceSuperTest.java
@@ -7,6 +7,7 @@
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.util.Collection;
@@ -56,6 +57,8 @@
.assertSuccessWithOutput(EXPECTED_OUTPUT);
return;
}
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.addInnerClasses(CustomCollectionInterfaceSuperTest.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
index 0097b3f..ac353f4 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import java.util.ArrayList;
import java.util.List;
@@ -36,6 +37,8 @@
@Test
public void testCustomCollectionSuperCallsD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
D8TestRunResult d8TestRunResult =
testForD8()
@@ -55,6 +58,8 @@
@Test
public void testCustomCollectionSuperCallsR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
R8TestRunResult r8TestRunResult =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
index 2330854..eadaa9b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -52,6 +53,8 @@
@Test
public void testCustomCollectionD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
D8TestRunResult d8TestRunResult =
testForD8()
@@ -84,6 +87,8 @@
@Test
public void testCustomCollectionR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
R8TestRunResult r8TestRunResult =
testForR8(Backend.DEX)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredGenericSignatureTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredGenericSignatureTest.java
index ff07b24..92ec5ee 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredGenericSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredGenericSignatureTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -42,6 +43,8 @@
@Test
public void testD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.addInnerClasses(DesugaredGenericSignatureTest.class)
@@ -61,6 +64,8 @@
@Test
public void testR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForR8(parameters.getBackend())
.addInnerClasses(DesugaredGenericSignatureTest.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index e520016..e4b3869 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -7,6 +7,7 @@
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.L8Command;
@@ -167,6 +168,25 @@
return new AbsentKeepRuleConsumer();
}
+ public Path getDesugaredLibraryInCF(
+ TestParameters parameters, Consumer<InternalOptions> configurationForLibraryCompilation)
+ throws IOException, CompilationFailedException {
+ Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs.jar");
+ L8Command.Builder l8Builder =
+ L8Command.builder()
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addProgramFiles(ToolHelper.getDesugarJDKLibs())
+ .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
+ .setMode(CompilationMode.DEBUG)
+ .addDesugaredLibraryConfiguration(
+ StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+ .setMinApiLevel(parameters.getApiLevel().getLevel())
+ .setOutput(desugaredLib, OutputMode.ClassFile);
+
+ ToolHelper.runL8(l8Builder.build(), configurationForLibraryCompilation);
+ return desugaredLib;
+ }
+
public interface KeepRuleConsumer extends StringConsumer {
String get();
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLocalDateReflectedTypePassedToStaticType.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLocalDateReflectedTypePassedToStaticType.java
index c1c4ce3..ef92b08 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLocalDateReflectedTypePassedToStaticType.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLocalDateReflectedTypePassedToStaticType.java
@@ -26,7 +26,7 @@
private final boolean shrinkDesugaredLibrary;
private static final String EXPECTED = StringUtils.lines("1992");
- @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withDexRuntimes().withAllApiLevels().build(), BooleanUtils.values());
@@ -40,6 +40,8 @@
@Test
public void testD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
D8TestRunResult runResult =
testForD8()
@@ -64,6 +66,8 @@
@Test
public void testR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
R8TestRunResult runResult =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredReflectedDesugaredTypePassedToStaticTypeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredReflectedDesugaredTypePassedToStaticTypeTest.java
index f56829c..ccf43f0 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredReflectedDesugaredTypePassedToStaticTypeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredReflectedDesugaredTypePassedToStaticTypeTest.java
@@ -27,7 +27,7 @@
private final boolean shrinkDesugaredLibrary;
private static final String EXPECTED = StringUtils.lines("1992", "1992");
- @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withDexRuntimes().withAllApiLevels().build(), BooleanUtils.values());
@@ -41,6 +41,8 @@
@Test
public void testD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
D8TestRunResult runResult =
testForD8()
@@ -65,6 +67,8 @@
@Test
public void testR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
R8TestRunResult runResult =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FeatureSplitTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FeatureSplitTest.java
index d4f08fc..831d83a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FeatureSplitTest.java
@@ -50,6 +50,8 @@
@Test
public void testTwoFeatures() throws CompilationFailedException, IOException, ExecutionException {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
CompiledWithFeature compiledWithFeature = new CompiledWithFeature().invoke(this);
Path basePath = compiledWithFeature.getBasePath();
Path feature1Path = compiledWithFeature.getFeature1Path();
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InvalidLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InvalidLibraryTest.java
index 64f7a72..7c1f6a1 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InvalidLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InvalidLibraryTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.errors.InvalidLibrarySuperclassDiagnostic;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
@@ -64,6 +65,11 @@
@Test
public void testProgramSupertype() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary
+ && parameters.getApiLevel().isLessThan(AndroidApiLevel.N)
+ && parameters.getDexRuntimeVersion() != Version.V5_1_1
+ && parameters.getDexRuntimeVersion() != Version.V6_0_1);
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.setMinApi(parameters.getApiLevel())
@@ -84,6 +90,11 @@
@Test
public void testClasspathSupertype() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary
+ && parameters.getApiLevel().isLessThan(AndroidApiLevel.N)
+ && parameters.getDexRuntimeVersion() != Version.V5_1_1
+ && parameters.getDexRuntimeVersion() != Version.V6_0_1);
Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
index 558b5f1..bca838b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
@@ -13,12 +13,12 @@
import com.android.tools.r8.L8Command;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.PrintUses;
import com.android.tools.r8.StringResource;
import com.android.tools.r8.TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.tracereferences.TraceReferences;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.FileUtils;
@@ -32,6 +32,7 @@
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.android.tools.r8.utils.codeinspector.TryCatchSubject;
import com.android.tools.r8.utils.codeinspector.TypeSubject;
+import com.google.common.base.Charsets;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableSet;
import java.nio.file.Path;
@@ -39,7 +40,6 @@
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
-import kotlin.text.Charsets;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -51,7 +51,7 @@
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
- private final boolean printUsesKeepRules;
+ private final boolean traceReferencesKeepRules;
private static final String expectedOutput =
StringUtils.lines(
"Caught java.time.format.DateTimeParseException",
@@ -61,7 +61,7 @@
"GMT",
"Hello, world");
- @Parameters(name = "{2}, shrinkDesugaredLibrary: {0}, printUsesKeepRules {1}")
+ @Parameters(name = "{2}, shrinkDesugaredLibrary: {0}, traceReferencesKeepRules {1}")
public static List<Object[]> data() {
return buildParameters(
BooleanUtils.values(),
@@ -74,9 +74,9 @@
}
public JavaTimeTest(
- boolean shrinkDesugaredLibrary, boolean printUsesKeepRules, TestParameters parameters) {
+ boolean shrinkDesugaredLibrary, boolean traceReferencesKeepRules, TestParameters parameters) {
this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
- this.printUsesKeepRules = printUsesKeepRules;
+ this.traceReferencesKeepRules = traceReferencesKeepRules;
this.parameters = parameters;
}
@@ -159,16 +159,16 @@
}
});
- private String collectKeepRulesWithPrintUses(
+ private String collectKeepRulesWithTraceReferences(
Path desugaredProgramClassFile, Path desugaredLibraryClassFile) throws Exception {
- Path printUsesKeepRules = temp.newFile().toPath();
- PrintUses.main(
- "--keeprules",
- ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
- desugaredLibraryClassFile.toString(),
- desugaredProgramClassFile.toString(),
- printUsesKeepRules.toString());
- return FileUtils.readTextFile(printUsesKeepRules, Charsets.UTF_8);
+ Path generatedKeepRules = temp.newFile().toPath();
+ TraceReferences.run(
+ "--format", "keep",
+ "--lib", ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
+ "--target", desugaredLibraryClassFile.toString(),
+ "--source", desugaredProgramClassFile.toString(),
+ "--output", generatedKeepRules.toString());
+ return FileUtils.readTextFile(generatedKeepRules, Charsets.UTF_8);
}
private String desugaredLibraryKeepRules(
@@ -178,9 +178,10 @@
if (shrinkDesugaredLibrary) {
desugaredLibraryKeepRules = keepRuleConsumer.get();
if (desugaredLibraryKeepRules != null) {
- if (printUsesKeepRules) {
+ if (traceReferencesKeepRules) {
desugaredLibraryKeepRules =
- collectKeepRulesWithPrintUses(programSupplier.get(), desugaredLibraryClassFile.get());
+ collectKeepRulesWithTraceReferences(
+ programSupplier.get(), desugaredLibraryClassFile.get());
}
}
}
@@ -189,7 +190,11 @@
@Test
public void testTimeD8Cf() throws Exception {
- Assume.assumeTrue(shrinkDesugaredLibrary || !printUsesKeepRules);
+ Assume.assumeTrue(shrinkDesugaredLibrary || !traceReferencesKeepRules);
+ expectThrowsWithHorizontalClassMergingIf(
+ parameters.isDexRuntime()
+ && traceReferencesKeepRules
+ && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
// Use D8 to desugar with Java classfile output.
@@ -203,7 +208,7 @@
.writeToZip();
String desugaredLibraryKeepRules;
- if (shrinkDesugaredLibrary && !printUsesKeepRules && keepRuleConsumer.get() != null) {
+ if (shrinkDesugaredLibrary && !traceReferencesKeepRules && keepRuleConsumer.get() != null) {
// Collection keep rules is only implemented in the DEX writer.
assertEquals(0, keepRuleConsumer.get().length());
desugaredLibraryKeepRules = "-keep class * { *; }";
@@ -239,7 +244,9 @@
@Test
public void testTimeD8() throws Exception {
Assume.assumeTrue(parameters.getRuntime().isDex());
- Assume.assumeTrue(shrinkDesugaredLibrary || !printUsesKeepRules);
+ Assume.assumeTrue(shrinkDesugaredLibrary || !traceReferencesKeepRules);
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
TestCompileResult<?, ?> result =
@@ -261,8 +268,10 @@
@Test
public void testTimeR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
Assume.assumeTrue(parameters.getRuntime().isDex());
- Assume.assumeTrue(shrinkDesugaredLibrary || !printUsesKeepRules);
+ Assume.assumeTrue(shrinkDesugaredLibrary || !traceReferencesKeepRules);
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
TestCompileResult<?, ?> result =
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibraryEmptySubclassInterfaceTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibraryEmptySubclassInterfaceTest.java
index 0eab37e..c66ff1d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibraryEmptySubclassInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibraryEmptySubclassInterfaceTest.java
@@ -8,6 +8,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@@ -36,6 +37,8 @@
@Test
public void testD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.addInnerClasses(LibraryEmptySubclassInterfaceTest.class)
@@ -64,6 +67,8 @@
@Test
public void testR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForR8(Backend.DEX)
.addInnerClasses(LibraryEmptySubclassInterfaceTest.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibrarySubclassInterfaceTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibrarySubclassInterfaceTest.java
index 6e300ba..d69b28c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibrarySubclassInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibrarySubclassInterfaceTest.java
@@ -8,6 +8,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import dalvik.system.PathClassLoader;
import java.sql.SQLDataException;
@@ -49,6 +50,8 @@
@Test
public void testCustomCollectionD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String stdOut =
testForD8()
@@ -69,6 +72,8 @@
@Test
public void testCustomCollectionR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String stdOut =
testForR8(Backend.DEX)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
index 69f366f..b0c3645 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
@@ -54,6 +54,8 @@
@Test
public void testProgramD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
ArrayList<Path> coreLambdaStubs = new ArrayList<>();
coreLambdaStubs.add(ToolHelper.getCoreLambdaStubs());
for (Boolean coreLambdaStubsActive : BooleanUtils.values()) {
@@ -111,6 +113,8 @@
@Test
public void testProgramR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
Assume.assumeTrue(
"TODO(b/139451198): Make the test run with new SDK.",
parameters.getApiLevel().getLevel() < AndroidApiLevel.O.getLevel());
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
index c2d88b0..7c65b9e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
@@ -5,6 +5,8 @@
package com.android.tools.r8.desugar.desugaredlibrary;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import java.time.Instant;
import java.time.ZonedDateTime;
@@ -37,6 +39,11 @@
@Test
public void testRetargetOverrideD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary
+ && parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.K)
+ && parameters.getDexRuntimeVersion() != Version.V5_1_1
+ && parameters.getDexRuntimeVersion() != Version.V6_0_1);
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String stdout =
testForD8()
@@ -57,6 +64,11 @@
@Test
public void testRetargetOverrideR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary
+ && parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.K)
+ && parameters.getDexRuntimeVersion() != Version.V5_1_1
+ && parameters.getDexRuntimeVersion() != Version.V6_0_1);
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String stdout =
testForR8(Backend.DEX)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/StaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/StaticInterfaceMethodTest.java
index 84917f1..5d50648 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/StaticInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/StaticInterfaceMethodTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.desugar.desugaredlibrary;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.time.chrono.Chronology;
@@ -44,6 +45,8 @@
.assertSuccessWithOutput(EXPECTED_OUTPUT);
return;
}
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.addInnerClasses(StaticInterfaceMethodTest.class)
@@ -64,6 +67,8 @@
// Desugared library tests do not make sense in the Cf to Cf, and the JVM is already tested
// in the D8 test. Just return.
Assume.assumeFalse(parameters.isCfRuntime());
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForR8(parameters.getBackend())
.addKeepMainRule(Executor.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java
index f9d1828..9e23d5d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java
@@ -53,6 +53,8 @@
@Test
public void testFinalMethod() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.setMinApi(AndroidApiLevel.B)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
index 5a03f73..2726e3e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
@@ -69,6 +69,8 @@
@Test
public void testRewrittenAPICallsD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.setMinApi(parameters.getApiLevel())
@@ -90,6 +92,8 @@
@Test
public void testRewrittenAPICallsR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForR8(parameters.getBackend())
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
index 4ac3ccc..dfa62f1 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
@@ -55,6 +55,8 @@
@Test
public void testRewrittenAPICallsD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.K));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.setMinApi(parameters.getApiLevel())
@@ -74,6 +76,8 @@
@Test
public void testRewrittenAPICallsR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.K));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForR8(parameters.getBackend())
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ClockAPIConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ClockAPIConversionTest.java
index 382f55d..8583e29 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ClockAPIConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ClockAPIConversionTest.java
@@ -50,6 +50,8 @@
@Test
public void testClockD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.setMinApi(parameters.getApiLevel())
@@ -70,6 +72,8 @@
@Test
public void testClockR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForR8(parameters.getBackend())
.addKeepMainRule(Executor.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
index 14f3ded..572f5b3 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
@@ -68,6 +68,7 @@
@Test
public void testNoInterfaceMethodsD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(shrinkDesugaredLibrary);
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java
index 7fe00d5..9bce171 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java
@@ -66,6 +66,7 @@
@Test
public void testStatsD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(shrinkDesugaredLibrary && shrinkDesugaredLibrary);
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.setMinApi(parameters.getApiLevel())
@@ -85,6 +86,7 @@
@Test
public void testStatsR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(shrinkDesugaredLibrary && shrinkDesugaredLibrary);
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForR8(parameters.getBackend())
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java
index 4dbcccb..d983dfa 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java
@@ -54,6 +54,8 @@
@Test
public void testBaselineD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.setMinApi(parameters.getApiLevel())
@@ -73,6 +75,8 @@
@Test
public void testBaselineR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForR8(parameters.getBackend())
.setMinApi(parameters.getApiLevel())
@@ -93,6 +97,8 @@
@Test
public void testTryCatchD8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.setMinApi(parameters.getApiLevel())
@@ -112,6 +118,8 @@
@Test
public void testTryCatchR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForR8(parameters.getBackend())
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11AtomicTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11AtomicTests.java
index 2801eb9..8e96ae1 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11AtomicTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11AtomicTests.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.TestRuntime;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
@@ -72,6 +73,8 @@
@Test
public void testD8AtomicReference() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String verbosity = "2";
testForD8()
@@ -95,6 +98,8 @@
@Test
public void testD8AtomicUpdaters() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String verbosity = "2";
testForD8()
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentMapTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentMapTests.java
index 7d0fc6b..19b1b1e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentMapTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentMapTests.java
@@ -98,6 +98,8 @@
@Test
public void testD8Concurrent() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
// TODO(b/134732760): Support Java 9+ libraries.
// We skip the ConcurrentRemoveIf test because of the non desugared class CompletableFuture.
Assume.assumeFalse("b/144975341",
@@ -157,6 +159,8 @@
@Test
public void testD8ConcurrentHash() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
Assume.assumeFalse("b/144975341",
parameters.getRuntime().asDex().getVm().getVersion() == Version.V10_0_0);
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
index 9d9b5cb..3268a35 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
@@ -181,6 +181,8 @@
@Test
public void testStream() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
Assume.assumeFalse(
"getAllFilesWithSuffixInDirectory() seems to find different files on Windows",
ToolHelper.isWindows());
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java
index 6288598..4ad9faa 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java
@@ -133,6 +133,8 @@
@Test
public void testTime() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String verbosity = "2";
D8TestCompileResult compileResult =
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
index 9004df6..61e7c03 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.kotlin.KotlinMetadataWriter;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
@@ -131,6 +132,10 @@
@Test
public void testTimeR8() throws Exception {
boolean desugarLibrary = parameters.isDexRuntime() && requiresAnyCoreLibDesugaring(parameters);
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary
+ && parameters.isDexRuntime()
+ && parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.N));
final R8FullTestBuilder testBuilder =
testForR8(parameters.getBackend())
.addProgramFiles(compiledJars.get(targetVersion))
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/FieldAccessTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/FieldAccessTest.java
index 5211d3d..6c48313 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/FieldAccessTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/FieldAccessTest.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.time.ZoneId;
@@ -34,6 +35,8 @@
@Test
public void testField() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.addProgramClasses(Executor.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/InheritanceTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/InheritanceTest.java
index a6061c8..da15390 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/InheritanceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/InheritanceTest.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.time.Clock;
@@ -36,6 +37,8 @@
@Test
public void testInheritance() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.addProgramClasses(Impl.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/KeepRuleShrinkTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/KeepRuleShrinkTest.java
index 2a673bf..2295e35 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/KeepRuleShrinkTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/KeepRuleShrinkTest.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import java.util.List;
import java.util.Map;
@@ -37,6 +38,8 @@
@Test
public void testMapProblem() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
D8TestRunResult d8TestRunResult =
testForD8()
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
index 5a788bf..cbd5710 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
@@ -57,8 +57,11 @@
@BeforeClass
public static void beforeAll() throws Exception {
- r8Lib11NoDesugar = compileR8(false);
- r8Lib11Desugar = compileR8(true);
+ assertThrowsWithHorizontalClassMerging(
+ () -> {
+ r8Lib11NoDesugar = compileR8(false);
+ r8Lib11Desugar = compileR8(true);
+ });
}
private static Path compileR8(boolean desugar) throws Exception {
@@ -94,6 +97,7 @@
@Test
public void testHello() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
Assume.assumeTrue(!ToolHelper.isWindows());
Path prevGeneratedJar = null;
String prevRunResult = null;
@@ -127,6 +131,7 @@
@Test
public void testR8() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
Assume.assumeTrue(!ToolHelper.isWindows());
Assume.assumeTrue(parameters.isCfRuntime());
Assume.assumeTrue(CfVm.JDK11 == parameters.getRuntime().asCf().getVm());
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
index 762916e..e1d892d 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
@@ -47,6 +47,7 @@
@Test
public void testR8CompiledWithR8() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.setMinApi(parameters.getApiLevel())
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java
index ee31562..e45ef92 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java
@@ -60,6 +60,7 @@
@Test
public void testClassMergeAcrossNestAndNonNest() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
// Potentially merge classes from a nest with non nest classes.
testClassMergeAcrossNest(
new String[] {
@@ -95,7 +96,7 @@
options.enableClassInlining = false;
options.enableNestReduction = false;
})
- .enableInliningAnnotations("nesthostexample")
+ .enableInliningAnnotations()
.addProgramFiles(bothNestsAndOutsideClassToCompile)
.compile()
.inspect(NestAttributesUpdateTest::assertNestAttributesCorrect);
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java
index 4fcfea3..373f6fc 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java
@@ -59,7 +59,7 @@
options.enableClassInlining = false;
options.enableVerticalClassMerging = false;
})
- .enableInliningAnnotations("nesthostexample")
+ .enableInliningAnnotations()
.addProgramFiles(toCompile)
.compile()
.inspect(this::assertMethodsInlined)
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
index c9dd274..9431067 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
@@ -49,6 +49,7 @@
@Test
public void testInliningFromFeature() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
// Static merging is based on sorting order, we assert that we merged to the feature.
ThrowingConsumer<R8TestCompileResult, Exception> ensureMergingToFeature =
r8TestCompileResult -> {
diff --git a/src/test/java/com/android/tools/r8/graph/initializedclasses/InitializedClassesInInstanceMethodsTest.java b/src/test/java/com/android/tools/r8/graph/initializedclasses/InitializedClassesInInstanceMethodsTest.java
index 9251ab8..da5dc5c 100644
--- a/src/test/java/com/android/tools/r8/graph/initializedclasses/InitializedClassesInInstanceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/graph/initializedclasses/InitializedClassesInInstanceMethodsTest.java
@@ -43,6 +43,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(InitializedClassesInInstanceMethodsTest.class)
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/DebugLocalStartOutsideRangeTest.java b/src/test/java/com/android/tools/r8/ir/DebugLocalStartOutsideRangeTest.java
new file mode 100644
index 0000000..ac6d2dd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/DebugLocalStartOutsideRangeTest.java
@@ -0,0 +1,493 @@
+// 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.ir;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.ir.DebugLocalStartOutsideRangeTest.PrintHelper$PrintUriAdapter$1Dump.dump;
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class DebugLocalStartOutsideRangeTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ public DebugLocalStartOutsideRangeTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws CompilationFailedException {
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(dump())
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ diagnostics.assertNoWarningsMatch(
+ diagnosticMessage(containsString("Could not find phi type for register")));
+ });
+ }
+
+ public static class PrintHelper$PrintUriAdapter$1Dump implements Opcodes {
+
+ public static byte[] dump() {
+
+ ClassWriter classWriter = new ClassWriter(0);
+ FieldVisitor fieldVisitor;
+ MethodVisitor methodVisitor;
+
+ classWriter.visit(
+ V1_7,
+ ACC_SUPER,
+ "androidx/print/PrintHelper$PrintUriAdapter$1",
+ "Landroid/os/AsyncTask<Landroid/net/Uri;Ljava/lang/Boolean;Landroid/graphics/Bitmap;>;",
+ "android/os/AsyncTask",
+ null);
+
+ classWriter.visitSource("PrintHelper.java", null);
+
+ classWriter.visitOuterClass(
+ "androidx/print/PrintHelper$PrintUriAdapter",
+ "onLayout",
+ "(Landroid/print/PrintAttributes;Landroid/print/PrintAttributes;Landroid/os/CancellationSignal;Landroid/print/PrintDocumentAdapter$LayoutResultCallback;Landroid/os/Bundle;)V");
+
+ classWriter.visitInnerClass(
+ "androidx/print/PrintHelper$PrintUriAdapter",
+ "androidx/print/PrintHelper",
+ "PrintUriAdapter",
+ ACC_PRIVATE);
+
+ classWriter.visitInnerClass("androidx/print/PrintHelper$PrintUriAdapter$1", null, null, 0);
+
+ classWriter.visitInnerClass(
+ "android/print/PrintAttributes$MediaSize",
+ "android/print/PrintAttributes",
+ "MediaSize",
+ ACC_PUBLIC | ACC_FINAL | ACC_STATIC);
+
+ classWriter.visitInnerClass(
+ "android/print/PrintDocumentInfo$Builder",
+ "android/print/PrintDocumentInfo",
+ "Builder",
+ ACC_PUBLIC | ACC_FINAL | ACC_STATIC);
+
+ {
+ fieldVisitor =
+ classWriter.visitField(
+ ACC_FINAL | ACC_SYNTHETIC,
+ "val$cancellationSignal",
+ "Landroid/os/CancellationSignal;",
+ null,
+ null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ fieldVisitor =
+ classWriter.visitField(
+ ACC_FINAL | ACC_SYNTHETIC,
+ "val$newPrintAttributes",
+ "Landroid/print/PrintAttributes;",
+ null,
+ null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ fieldVisitor =
+ classWriter.visitField(
+ ACC_FINAL | ACC_SYNTHETIC,
+ "val$oldPrintAttributes",
+ "Landroid/print/PrintAttributes;",
+ null,
+ null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ fieldVisitor =
+ classWriter.visitField(
+ ACC_FINAL | ACC_SYNTHETIC,
+ "val$layoutResultCallback",
+ "Landroid/print/PrintDocumentAdapter$LayoutResultCallback;",
+ null,
+ null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ fieldVisitor =
+ classWriter.visitField(
+ ACC_FINAL | ACC_SYNTHETIC,
+ "this$1",
+ "Landroidx/print/PrintHelper$PrintUriAdapter;",
+ null,
+ null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ methodVisitor =
+ classWriter.visitMethod(
+ ACC_PROTECTED, "onPostExecute", "(Landroid/graphics/Bitmap;)V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ Label label1 = new Label();
+ Label label2 = new Label();
+ methodVisitor.visitTryCatchBlock(label0, label1, label2, null);
+ Label label3 = new Label();
+ methodVisitor.visitTryCatchBlock(label2, label3, label2, null);
+ Label label4 = new Label();
+ methodVisitor.visitLabel(label4);
+ methodVisitor.visitLineNumber(450, label4);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitMethodInsn(
+ INVOKESPECIAL, "android/os/AsyncTask", "onPostExecute", "(Ljava/lang/Object;)V", false);
+ Label label5 = new Label();
+ methodVisitor.visitLabel(label5);
+ methodVisitor.visitLineNumber(454, label5);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ Label label6 = new Label();
+ methodVisitor.visitJumpInsn(IFNULL, label6);
+ methodVisitor.visitFieldInsn(
+ GETSTATIC, "androidx/print/PrintHelper", "PRINT_ACTIVITY_RESPECTS_ORIENTATION", "Z");
+ Label label7 = new Label();
+ methodVisitor.visitJumpInsn(IFEQ, label7);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "androidx/print/PrintHelper$PrintUriAdapter$1",
+ "this$1",
+ "Landroidx/print/PrintHelper$PrintUriAdapter;");
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "androidx/print/PrintHelper$PrintUriAdapter",
+ "this$0",
+ "Landroidx/print/PrintHelper;");
+ methodVisitor.visitFieldInsn(GETFIELD, "androidx/print/PrintHelper", "mOrientation", "I");
+ methodVisitor.visitJumpInsn(IFNE, label6);
+ methodVisitor.visitLabel(label7);
+ methodVisitor.visitLineNumber(458, label7);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitVarInsn(ASTORE, 3);
+ methodVisitor.visitInsn(MONITORENTER);
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(459, label0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "androidx/print/PrintHelper$PrintUriAdapter$1",
+ "this$1",
+ "Landroidx/print/PrintHelper$PrintUriAdapter;");
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "androidx/print/PrintHelper$PrintUriAdapter",
+ "mAttributes",
+ "Landroid/print/PrintAttributes;");
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "android/print/PrintAttributes",
+ "getMediaSize",
+ "()Landroid/print/PrintAttributes$MediaSize;",
+ false);
+ methodVisitor.visitVarInsn(ASTORE, 2);
+ Label label8 = new Label();
+ methodVisitor.visitLabel(label8);
+ methodVisitor.visitLineNumber(460, label8);
+ methodVisitor.visitVarInsn(ALOAD, 3);
+ methodVisitor.visitInsn(MONITOREXIT);
+ methodVisitor.visitLabel(label1);
+ Label label9 = new Label();
+ methodVisitor.visitJumpInsn(GOTO, label9);
+ methodVisitor.visitLabel(label2);
+ methodVisitor.visitFrame(
+ Opcodes.F_FULL,
+ 4,
+ new Object[] {
+ "androidx/print/PrintHelper$PrintUriAdapter$1",
+ "android/graphics/Bitmap",
+ Opcodes.TOP,
+ "java/lang/Object"
+ },
+ 1,
+ new Object[] {"java/lang/Throwable"});
+ methodVisitor.visitVarInsn(ASTORE, 4);
+ methodVisitor.visitVarInsn(ALOAD, 3);
+ methodVisitor.visitInsn(MONITOREXIT);
+ methodVisitor.visitLabel(label3);
+ methodVisitor.visitVarInsn(ALOAD, 4);
+ methodVisitor.visitInsn(ATHROW);
+ methodVisitor.visitLabel(label9);
+ methodVisitor.visitLineNumber(462, label9);
+ methodVisitor.visitFrame(
+ Opcodes.F_FULL,
+ 3,
+ new Object[] {
+ "androidx/print/PrintHelper$PrintUriAdapter$1",
+ "android/graphics/Bitmap",
+ "android/print/PrintAttributes$MediaSize"
+ },
+ 0,
+ new Object[] {});
+ methodVisitor.visitVarInsn(ALOAD, 2);
+ methodVisitor.visitJumpInsn(IFNULL, label6);
+ Label label10 = new Label();
+ methodVisitor.visitLabel(label10);
+ methodVisitor.visitLineNumber(463, label10);
+ methodVisitor.visitVarInsn(ALOAD, 2);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "android/print/PrintAttributes$MediaSize", "isPortrait", "()Z", false);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC,
+ "androidx/print/PrintHelper",
+ "isPortrait",
+ "(Landroid/graphics/Bitmap;)Z",
+ false);
+ methodVisitor.visitJumpInsn(IF_ICMPEQ, label6);
+ Label label11 = new Label();
+ methodVisitor.visitLabel(label11);
+ methodVisitor.visitLineNumber(464, label11);
+ methodVisitor.visitTypeInsn(NEW, "android/graphics/Matrix");
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitMethodInsn(
+ INVOKESPECIAL, "android/graphics/Matrix", "<init>", "()V", false);
+ methodVisitor.visitVarInsn(ASTORE, 3);
+ Label label12 = new Label();
+ methodVisitor.visitLabel(label12);
+ methodVisitor.visitLineNumber(466, label12);
+ methodVisitor.visitVarInsn(ALOAD, 3);
+ methodVisitor.visitLdcInsn(new Float("90.0"));
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "android/graphics/Matrix", "postRotate", "(F)Z", false);
+ methodVisitor.visitInsn(POP);
+ Label label13 = new Label();
+ methodVisitor.visitLabel(label13);
+ methodVisitor.visitLineNumber(467, label13);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitInsn(ICONST_0);
+ methodVisitor.visitInsn(ICONST_0);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ Label label14 = new Label();
+ methodVisitor.visitLabel(label14);
+ methodVisitor.visitLineNumber(468, label14);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "android/graphics/Bitmap", "getWidth", "()I", false);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "android/graphics/Bitmap", "getHeight", "()I", false);
+ methodVisitor.visitVarInsn(ALOAD, 3);
+ methodVisitor.visitInsn(ICONST_1);
+ Label label15 = new Label();
+ methodVisitor.visitLabel(label15);
+ methodVisitor.visitLineNumber(467, label15);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC,
+ "android/graphics/Bitmap",
+ "createBitmap",
+ "(Landroid/graphics/Bitmap;IIIILandroid/graphics/Matrix;Z)Landroid/graphics/Bitmap;",
+ false);
+ methodVisitor.visitVarInsn(ASTORE, 1);
+ methodVisitor.visitLabel(label6);
+ methodVisitor.visitLineNumber(474, label6);
+ methodVisitor.visitFrame(Opcodes.F_CHOP, 1, null, 0, null);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "androidx/print/PrintHelper$PrintUriAdapter$1",
+ "this$1",
+ "Landroidx/print/PrintHelper$PrintUriAdapter;");
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitFieldInsn(
+ PUTFIELD,
+ "androidx/print/PrintHelper$PrintUriAdapter",
+ "mBitmap",
+ "Landroid/graphics/Bitmap;");
+ Label label16 = new Label();
+ methodVisitor.visitLabel(label16);
+ methodVisitor.visitLineNumber(475, label16);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ Label label17 = new Label();
+ methodVisitor.visitJumpInsn(IFNULL, label17);
+ Label label18 = new Label();
+ methodVisitor.visitLabel(label18);
+ methodVisitor.visitLineNumber(476, label18);
+ methodVisitor.visitTypeInsn(NEW, "android/print/PrintDocumentInfo$Builder");
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "androidx/print/PrintHelper$PrintUriAdapter$1",
+ "this$1",
+ "Landroidx/print/PrintHelper$PrintUriAdapter;");
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "androidx/print/PrintHelper$PrintUriAdapter",
+ "mJobName",
+ "Ljava/lang/String;");
+ methodVisitor.visitMethodInsn(
+ INVOKESPECIAL,
+ "android/print/PrintDocumentInfo$Builder",
+ "<init>",
+ "(Ljava/lang/String;)V",
+ false);
+ methodVisitor.visitInsn(ICONST_1);
+ Label label19 = new Label();
+ methodVisitor.visitLabel(label19);
+ methodVisitor.visitLineNumber(477, label19);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "android/print/PrintDocumentInfo$Builder",
+ "setContentType",
+ "(I)Landroid/print/PrintDocumentInfo$Builder;",
+ false);
+ methodVisitor.visitInsn(ICONST_1);
+ Label label20 = new Label();
+ methodVisitor.visitLabel(label20);
+ methodVisitor.visitLineNumber(478, label20);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "android/print/PrintDocumentInfo$Builder",
+ "setPageCount",
+ "(I)Landroid/print/PrintDocumentInfo$Builder;",
+ false);
+ Label label21 = new Label();
+ methodVisitor.visitLabel(label21);
+ methodVisitor.visitLineNumber(479, label21);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "android/print/PrintDocumentInfo$Builder",
+ "build",
+ "()Landroid/print/PrintDocumentInfo;",
+ false);
+ methodVisitor.visitVarInsn(ASTORE, 2);
+ Label label22 = new Label();
+ methodVisitor.visitLabel(label22);
+ methodVisitor.visitLineNumber(481, label22);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "androidx/print/PrintHelper$PrintUriAdapter$1",
+ "val$newPrintAttributes",
+ "Landroid/print/PrintAttributes;");
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "androidx/print/PrintHelper$PrintUriAdapter$1",
+ "val$oldPrintAttributes",
+ "Landroid/print/PrintAttributes;");
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "android/print/PrintAttributes",
+ "equals",
+ "(Ljava/lang/Object;)Z",
+ false);
+ Label label23 = new Label();
+ methodVisitor.visitJumpInsn(IFNE, label23);
+ methodVisitor.visitInsn(ICONST_1);
+ Label label24 = new Label();
+ methodVisitor.visitJumpInsn(GOTO, label24);
+ methodVisitor.visitLabel(label23);
+ methodVisitor.visitFrame(
+ Opcodes.F_APPEND, 1, new Object[] {"android/print/PrintDocumentInfo"}, 0, null);
+ methodVisitor.visitInsn(ICONST_0);
+ methodVisitor.visitLabel(label24);
+ methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {Opcodes.INTEGER});
+ methodVisitor.visitVarInsn(ISTORE, 3);
+ Label label25 = new Label();
+ methodVisitor.visitLabel(label25);
+ methodVisitor.visitLineNumber(483, label25);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "androidx/print/PrintHelper$PrintUriAdapter$1",
+ "val$layoutResultCallback",
+ "Landroid/print/PrintDocumentAdapter$LayoutResultCallback;");
+ methodVisitor.visitVarInsn(ALOAD, 2);
+ methodVisitor.visitVarInsn(ILOAD, 3);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "android/print/PrintDocumentAdapter$LayoutResultCallback",
+ "onLayoutFinished",
+ "(Landroid/print/PrintDocumentInfo;Z)V",
+ false);
+ Label label26 = new Label();
+ methodVisitor.visitLabel(label26);
+ methodVisitor.visitLineNumber(485, label26);
+ Label label27 = new Label();
+ methodVisitor.visitJumpInsn(GOTO, label27);
+ methodVisitor.visitLabel(label17);
+ methodVisitor.visitLineNumber(486, label17);
+ methodVisitor.visitFrame(Opcodes.F_CHOP, 1, null, 0, null);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "androidx/print/PrintHelper$PrintUriAdapter$1",
+ "val$layoutResultCallback",
+ "Landroid/print/PrintDocumentAdapter$LayoutResultCallback;");
+ methodVisitor.visitInsn(ACONST_NULL);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "android/print/PrintDocumentAdapter$LayoutResultCallback",
+ "onLayoutFailed",
+ "(Ljava/lang/CharSequence;)V",
+ false);
+ methodVisitor.visitLabel(label27);
+ methodVisitor.visitLineNumber(488, label27);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "androidx/print/PrintHelper$PrintUriAdapter$1",
+ "this$1",
+ "Landroidx/print/PrintHelper$PrintUriAdapter;");
+ methodVisitor.visitInsn(ACONST_NULL);
+ methodVisitor.visitFieldInsn(
+ PUTFIELD,
+ "androidx/print/PrintHelper$PrintUriAdapter",
+ "mLoadBitmap",
+ "Landroid/os/AsyncTask;");
+ Label label28 = new Label();
+ methodVisitor.visitLabel(label28);
+ methodVisitor.visitLineNumber(489, label28);
+ methodVisitor.visitInsn(RETURN);
+ Label label29 = new Label();
+ methodVisitor.visitLabel(label29);
+ methodVisitor.visitLocalVariable(
+ "rotation", "Landroid/graphics/Matrix;", null, label12, label6, 3);
+ methodVisitor.visitLocalVariable(
+ "mediaSize", "Landroid/print/PrintAttributes$MediaSize;", null, label8, label6, 2);
+ methodVisitor.visitLocalVariable(
+ "info", "Landroid/print/PrintDocumentInfo;", null, label22, label26, 2);
+ methodVisitor.visitLocalVariable("changed", "Z", null, label25, label26, 3);
+ methodVisitor.visitLocalVariable(
+ "this", "Landroidx/print/PrintHelper$PrintUriAdapter$1;", null, label4, label29, 0);
+ methodVisitor.visitLocalVariable(
+ "bitmap", "Landroid/graphics/Bitmap;", null, label4, label29, 1);
+ methodVisitor.visitMaxs(7, 5);
+ methodVisitor.visitEnd();
+ }
+ classWriter.visitEnd();
+
+ return classWriter.toByteArray();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/DebugLocalStartRangeInLinearBlockWithFrameTest.java b/src/test/java/com/android/tools/r8/ir/DebugLocalStartRangeInLinearBlockWithFrameTest.java
new file mode 100644
index 0000000..0b3e53b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/DebugLocalStartRangeInLinearBlockWithFrameTest.java
@@ -0,0 +1,400 @@
+// 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.ir;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+@RunWith(Parameterized.class)
+public class DebugLocalStartRangeInLinearBlockWithFrameTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public DebugLocalStartRangeInLinearBlockWithFrameTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(AbstractAjaxCallbackDump.dump())
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+ }
+
+ public static class AbstractAjaxCallbackDump implements Opcodes {
+
+ public static byte[] dump() {
+
+ ClassWriter classWriter = new ClassWriter(0);
+ FieldVisitor fieldVisitor;
+ MethodVisitor methodVisitor;
+
+ classWriter.visit(
+ V1_6,
+ ACC_PUBLIC | ACC_SUPER | ACC_ABSTRACT,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "<T:Ljava/lang/Object;K:Ljava/lang/Object;>Ljava/lang/Object;Ljava/lang/Runnable;",
+ "java/lang/Object",
+ new String[] {"java/lang/Runnable"});
+
+ classWriter.visitSource("AbstractAjaxCallback.java", null);
+
+ classWriter.visitInnerClass(
+ "android/os/Build$VERSION", "android/os/Build", "VERSION", ACC_PUBLIC | ACC_STATIC);
+
+ classWriter.visitInnerClass(
+ "com/androidquery/callback/AbstractAjaxCallback$1", null, null, 0);
+
+ classWriter.visitInnerClass(
+ "java/net/Proxy$Type",
+ "java/net/Proxy",
+ "Type",
+ ACC_PUBLIC | ACC_FINAL | ACC_STATIC | ACC_ENUM);
+
+ classWriter.visitInnerClass(
+ "java/util/Map$Entry",
+ "java/util/Map",
+ "Entry",
+ ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
+
+ {
+ fieldVisitor =
+ classWriter.visitField(
+ ACC_PRIVATE, "type", "Ljava/lang/Class;", "Ljava/lang/Class<TT;>;", null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ fieldVisitor =
+ classWriter.visitField(ACC_PRIVATE, "handler", "Ljava/lang/Object;", null, null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ fieldVisitor =
+ classWriter.visitField(ACC_PRIVATE, "callback", "Ljava/lang/String;", null, null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ fieldVisitor = classWriter.visitField(ACC_PRIVATE, "url", "Ljava/lang/String;", null, null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ fieldVisitor =
+ classWriter.visitField(ACC_PROTECTED, "result", "Ljava/lang/Object;", "TT;", null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ fieldVisitor =
+ classWriter.visitField(
+ ACC_PROTECTED, "status", "Lcom/androidquery/callback/AjaxStatus;", null, null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ fieldVisitor = classWriter.visitField(ACC_PRIVATE, "completed", "Z", null, null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ fieldVisitor = classWriter.visitField(ACC_PRIVATE, "blocked", "Z", null, null);
+ fieldVisitor.visitEnd();
+ }
+ {
+ methodVisitor = classWriter.visitMethod(0, "callback", "()V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ Label label1 = new Label();
+ Label label2 = new Label();
+ methodVisitor.visitTryCatchBlock(label0, label1, label2, "java/lang/Exception");
+ Label label3 = new Label();
+ methodVisitor.visitLabel(label3);
+ methodVisitor.visitLineNumber(568, label3);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitInsn(ICONST_0);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "showProgress",
+ "(Z)V",
+ false);
+ Label label4 = new Label();
+ methodVisitor.visitLabel(label4);
+ methodVisitor.visitLineNumber(570, label4);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitInsn(ICONST_1);
+ methodVisitor.visitFieldInsn(
+ PUTFIELD, "com/androidquery/callback/AbstractAjaxCallback", "completed", "Z");
+ Label label5 = new Label();
+ methodVisitor.visitLabel(label5);
+ methodVisitor.visitLineNumber(572, label5);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(
+ INVOKESPECIAL,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "isActive",
+ "()Z",
+ false);
+ Label label6 = new Label();
+ methodVisitor.visitJumpInsn(IFEQ, label6);
+ Label label7 = new Label();
+ methodVisitor.visitLabel(label7);
+ methodVisitor.visitLineNumber(574, label7);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "callback",
+ "Ljava/lang/String;");
+ methodVisitor.visitJumpInsn(IFNULL, label0);
+ Label label8 = new Label();
+ methodVisitor.visitLabel(label8);
+ methodVisitor.visitLineNumber(575, label8);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "getHandler",
+ "()Ljava/lang/Object;",
+ false);
+ methodVisitor.visitVarInsn(ASTORE, 1);
+ Label label9 = new Label();
+ methodVisitor.visitLabel(label9);
+ methodVisitor.visitLineNumber(576, label9);
+ methodVisitor.visitInsn(ICONST_3);
+ methodVisitor.visitTypeInsn(ANEWARRAY, "java/lang/Class");
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitInsn(ICONST_0);
+ methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/String;"));
+ methodVisitor.visitInsn(AASTORE);
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitInsn(ICONST_1);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "type",
+ "Ljava/lang/Class;");
+ methodVisitor.visitInsn(AASTORE);
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitInsn(ICONST_2);
+ methodVisitor.visitLdcInsn(Type.getType("Lcom/androidquery/callback/AjaxStatus;"));
+ methodVisitor.visitInsn(AASTORE);
+ methodVisitor.visitVarInsn(ASTORE, 2);
+ Label label10 = new Label();
+ methodVisitor.visitLabel(label10);
+ methodVisitor.visitLineNumber(577, label10);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "callback",
+ "Ljava/lang/String;");
+ methodVisitor.visitInsn(ICONST_1);
+ methodVisitor.visitInsn(ICONST_1);
+ methodVisitor.visitVarInsn(ALOAD, 2);
+ methodVisitor.visitFieldInsn(
+ GETSTATIC,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "DEFAULT_SIG",
+ "[Ljava/lang/Class;");
+ methodVisitor.visitInsn(ICONST_3);
+ methodVisitor.visitTypeInsn(ANEWARRAY, "java/lang/Object");
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitInsn(ICONST_0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "url",
+ "Ljava/lang/String;");
+ methodVisitor.visitInsn(AASTORE);
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitInsn(ICONST_1);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "result",
+ "Ljava/lang/Object;");
+ methodVisitor.visitInsn(AASTORE);
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitInsn(ICONST_2);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "status",
+ "Lcom/androidquery/callback/AjaxStatus;");
+ methodVisitor.visitInsn(AASTORE);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC,
+ "com/androidquery/util/AQUtility",
+ "invokeHandler",
+ "(Ljava/lang/Object;Ljava/lang/String;ZZ[Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Object;)Ljava/lang/Object;",
+ false);
+ methodVisitor.visitInsn(POP);
+ Label label11 = new Label();
+ methodVisitor.visitLabel(label11);
+ methodVisitor.visitLineNumber(578, label11);
+ Label label12 = new Label();
+ methodVisitor.visitJumpInsn(GOTO, label12);
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(580, label0);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "url",
+ "Ljava/lang/String;");
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "result",
+ "Ljava/lang/Object;");
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "status",
+ "Lcom/androidquery/callback/AjaxStatus;");
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "callback",
+ "(Ljava/lang/String;Ljava/lang/Object;Lcom/androidquery/callback/AjaxStatus;)V",
+ false);
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLineNumber(581, label1);
+ methodVisitor.visitJumpInsn(GOTO, label12);
+ methodVisitor.visitLabel(label2);
+ methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Exception"});
+ methodVisitor.visitVarInsn(ASTORE, 1);
+ Label label13 = new Label();
+ methodVisitor.visitLabel(label13);
+ methodVisitor.visitLineNumber(582, label13);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC,
+ "com/androidquery/util/AQUtility",
+ "report",
+ "(Ljava/lang/Throwable;)V",
+ false);
+ Label label14 = new Label();
+ methodVisitor.visitLabel(label14);
+ methodVisitor.visitLineNumber(586, label14);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitJumpInsn(GOTO, label12);
+ methodVisitor.visitLabel(label6);
+ methodVisitor.visitLineNumber(587, label6);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "url",
+ "Ljava/lang/String;");
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "result",
+ "Ljava/lang/Object;");
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "status",
+ "Lcom/androidquery/callback/AjaxStatus;");
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "skip",
+ "(Ljava/lang/String;Ljava/lang/Object;Lcom/androidquery/callback/AjaxStatus;)V",
+ false);
+ methodVisitor.visitLabel(label12);
+ methodVisitor.visitLineNumber(591, label12);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(
+ INVOKESPECIAL,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "filePut",
+ "()V",
+ false);
+ Label label15 = new Label();
+ methodVisitor.visitLabel(label15);
+ methodVisitor.visitLineNumber(593, label15);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD, "com/androidquery/callback/AbstractAjaxCallback", "blocked", "Z");
+ Label label16 = new Label();
+ methodVisitor.visitJumpInsn(IFNE, label16);
+ Label label17 = new Label();
+ methodVisitor.visitLabel(label17);
+ methodVisitor.visitLineNumber(594, label17);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitFieldInsn(
+ GETFIELD,
+ "com/androidquery/callback/AbstractAjaxCallback",
+ "status",
+ "Lcom/androidquery/callback/AjaxStatus;");
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "com/androidquery/callback/AjaxStatus", "close", "()V", false);
+ methodVisitor.visitLabel(label16);
+ methodVisitor.visitLineNumber(597, label16);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(
+ INVOKESPECIAL, "com/androidquery/callback/AbstractAjaxCallback", "wake", "()V", false);
+ Label label18 = new Label();
+ methodVisitor.visitLabel(label18);
+ methodVisitor.visitLineNumber(598, label18);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC, "com/androidquery/util/AQUtility", "debugNotify", "()V", false);
+ Label label19 = new Label();
+ methodVisitor.visitLabel(label19);
+ methodVisitor.visitLineNumber(599, label19);
+ methodVisitor.visitInsn(RETURN);
+ Label label20 = new Label();
+ methodVisitor.visitLabel(label20);
+ methodVisitor.visitLocalVariable(
+ "this",
+ "Lcom/androidquery/callback/AbstractAjaxCallback;",
+ "Lcom/androidquery/callback/AbstractAjaxCallback<TT;TK;>;",
+ label3,
+ label20,
+ 0);
+ methodVisitor.visitLocalVariable("handler", "Ljava/lang/Object;", null, label9, label11, 1);
+ methodVisitor.visitLocalVariable(
+ "AJAX_SIG", "[Ljava/lang/Class;", null, label10, label11, 2);
+ methodVisitor.visitLocalVariable("e", "Ljava/lang/Exception;", null, label13, label14, 1);
+ methodVisitor.visitMaxs(10, 3);
+ methodVisitor.visitEnd();
+ }
+ classWriter.visitEnd();
+
+ return classWriter.toByteArray();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/InvokeMultiNewArraySideEffectTest.java b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/InvokeMultiNewArraySideEffectTest.java
index 487d60a..11268e8 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/InvokeMultiNewArraySideEffectTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/InvokeMultiNewArraySideEffectTest.java
@@ -34,6 +34,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(parameters.isCfRuntime());
testForR8(parameters.getBackend())
.addInnerClasses(InvokeMultiNewArraySideEffectTest.class)
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/PutObjectWithFinalizeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/PutObjectWithFinalizeTest.java
index dbbf99a..2eb4a58 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/PutObjectWithFinalizeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/PutObjectWithFinalizeTest.java
@@ -41,6 +41,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(PutObjectWithFinalizeTest.class)
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
index 964848a..4429dc9 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
@@ -171,6 +171,7 @@
@Test
public void testNonNullParamAfterInvokeVirtual() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
Class<?> mainClass = NonNullParamAfterInvokeVirtualMain.class;
CodeInspector inspector =
buildAndRun(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index 807b061..143f206 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -131,32 +131,35 @@
@Before
public void generateR8Version() throws Exception {
- outputDir = temp.newFolder().toPath();
- Path mapFile = outputDir.resolve(DEFAULT_MAP_FILENAME);
- generateR8Version(outputDir, mapFile, true);
- String output;
- if (parameters.isDexRuntime()) {
- output =
- ToolHelper.runArtNoVerificationErrors(
- Collections.singletonList(outputDir.resolve(DEFAULT_DEX_FILENAME).toString()),
- "inlining.Inlining",
- builder -> {},
- parameters.getRuntime().asDex().getVm());
- } else {
- assert parameters.isCfRuntime();
- output =
- ToolHelper.runJava(
- parameters.getRuntime().asCf(),
- Collections.singletonList("-noverify"),
- Collections.singletonList(outputDir),
- "inlining.Inlining")
- .stdout;
- }
+ assertThrowsWithHorizontalClassMerging(
+ () -> {
+ outputDir = temp.newFolder().toPath();
+ Path mapFile = outputDir.resolve(DEFAULT_MAP_FILENAME);
+ generateR8Version(outputDir, mapFile, true);
+ String output;
+ if (parameters.isDexRuntime()) {
+ output =
+ ToolHelper.runArtNoVerificationErrors(
+ Collections.singletonList(outputDir.resolve(DEFAULT_DEX_FILENAME).toString()),
+ "inlining.Inlining",
+ builder -> {},
+ parameters.getRuntime().asDex().getVm());
+ } else {
+ assert parameters.isCfRuntime();
+ output =
+ ToolHelper.runJava(
+ parameters.getRuntime().asCf(),
+ Collections.singletonList("-noverify"),
+ Collections.singletonList(outputDir),
+ "inlining.Inlining")
+ .stdout;
+ }
- // Compare result with Java to make sure we have the same behavior.
- ProcessResult javaResult = ToolHelper.runJava(getInputFile(), "inlining.Inlining");
- assertEquals(0, javaResult.exitCode);
- assertEquals(javaResult.stdout, output);
+ // Compare result with Java to make sure we have the same behavior.
+ ProcessResult javaResult = ToolHelper.runJava(getInputFile(), "inlining.Inlining");
+ assertEquals(0, javaResult.exitCode);
+ assertEquals(javaResult.stdout, output);
+ });
}
private void checkAbsentBooleanMethod(ClassSubject clazz, String name) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
index 43f777d..aa4dd9a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
@@ -41,6 +41,7 @@
@Test
public void testR8() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(InvokeVirtualWithRefinedReceiverTest.class)
.addKeepMainRule(MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/WithStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/WithStaticizerTest.java
index c1839b9..3fb859c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/WithStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/WithStaticizerTest.java
@@ -39,6 +39,7 @@
@Test
public void testR8() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(WithStaticizerTest.class)
.addKeepMainRule(MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
index 39bfa4c..1f8b7c1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
@@ -41,6 +41,7 @@
@Test
public void testR8() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(InvokeVirtualNegativeTest.class)
.addKeepMainRule(MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
index 2595df5..32d0307 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
@@ -41,6 +41,7 @@
@Test
public void testR8() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(InvokeVirtualPositiveTest.class)
.addKeepMainRule(MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index ffacc6a..c6f7772 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -64,6 +64,7 @@
@Test
public void testTrivial() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
Class<?> main = TrivialTestClass.class;
Class<?>[] classes = {
TrivialTestClass.class,
@@ -220,6 +221,7 @@
@Test
public void testInvalidatedRoot() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
Class<?> main = InvalidRootsTestClass.class;
Class<?>[] classes = {
InvalidRootsTestClass.class,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliningOracleTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliningOracleTest.java
index e0bd65e..2ed13fa 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliningOracleTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliningOracleTest.java
@@ -41,6 +41,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(enableInvokeSuperToInvokeVirtualRewriting);
testForR8(parameters.getBackend())
.addInnerClasses(ClassInliningOracleTest.class)
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
index 9ebb411..a684ea3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
@@ -41,6 +41,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(SingleTargetAfterInliningTest.class)
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
index 0d81ff2..406e16d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
@@ -208,6 +208,7 @@
@Test
public void testR8() throws Exception {
boolean isRelease = mode == CompilationMode.RELEASE;
+ expectThrowsWithHorizontalClassMergingIf(isRelease);
boolean expectCallPresent = !isRelease;
int expectedGetClassCount = isRelease ? 0 : 5;
int expectedConstClassCount = isRelease ? (parameters.isCfRuntime() ? 8 : 6) : 1;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index 77c4810..df009b4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -267,6 +268,7 @@
@Test
public void testMoveToHost() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
Class<?> main = MoveToHostTestClass.class;
Class<?>[] classes = {
NeverInline.class,
@@ -281,7 +283,7 @@
CandidateConflictField.class
};
String javaOutput = runOnJava(main);
- SingleTestRunResult result =
+ R8TestRunResult result =
testForR8(parameters.getBackend())
.addProgramClasses(classes)
.enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/typechecks/InstanceOfMethodSpecializationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/typechecks/InstanceOfMethodSpecializationTest.java
index 5d13f66..4173bb2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/typechecks/InstanceOfMethodSpecializationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/typechecks/InstanceOfMethodSpecializationTest.java
@@ -50,6 +50,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(InstanceOfMethodSpecializationTest.class)
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithNonNullParamCheckTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithNonNullParamCheckTest.java
index 828f4c0..d6ef4b2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithNonNullParamCheckTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithNonNullParamCheckTest.java
@@ -48,6 +48,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
String expected =
StringUtils.joinLines(
"Caught NullPointerException from testRewriteInvokeStaticToThrowNull",
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/PrivateInstanceMethodCollisionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/PrivateInstanceMethodCollisionTest.java
index 41a1935..e14bb7a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/PrivateInstanceMethodCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/PrivateInstanceMethodCollisionTest.java
@@ -48,6 +48,7 @@
@Test
public void b139769782() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
String expectedOutput = StringUtils.lines("A#foo(B)", "A#foo(B, Object)");
if (parameters.isCfRuntime() && !minification && !allowAccessModification) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/SynchronizedMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/SynchronizedMethodTest.java
index 88459a5..946523e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/SynchronizedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/SynchronizedMethodTest.java
@@ -23,6 +23,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
String expectedOutput = StringUtils.lines("In A.m()", "Got NullPointerException");
CodeInspector inspector =
testForR8(Backend.DEX)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java
index 7d3eb83..b3425b8 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java
@@ -37,6 +37,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
String expected =
StringUtils.lines(
"Factory.createStatic() -> null",
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAndUninstantiatedTypesTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAndUninstantiatedTypesTest.java
index af6296b..b453359 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAndUninstantiatedTypesTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAndUninstantiatedTypesTest.java
@@ -34,6 +34,7 @@
@Test
public void testUnusedAndUninstantiatedTypes() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(UnusedAndUninstantiatedTypesTest.class)
.addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
index 6fb984f..179333f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
@@ -17,7 +17,6 @@
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.google.common.base.Predicates;
import java.util.Collection;
-import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -37,6 +36,7 @@
@Test
public void testCompanionAndRegularObjects() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
final String mainClassName = "class_staticizer.MainKt";
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index e5ce02c..33ffbca 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -82,6 +82,7 @@
@Test
public void testCompanionProperty_primitivePropertyIsAlwaysInlined() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
"companionProperties_usePrimitiveProp");
@@ -116,6 +117,7 @@
@Test
public void testCompanionProperty_privatePropertyIsAlwaysInlined() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
"companionProperties_usePrivateProp");
@@ -151,6 +153,7 @@
@Test
public void testCompanionProperty_internalPropertyIsAlwaysInlined() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
"companionProperties_useInternalProp");
@@ -185,6 +188,7 @@
@Test
public void testCompanionProperty_publicPropertyIsAlwaysInlined() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
"companionProperties_usePublicProp");
@@ -219,6 +223,7 @@
@Test
public void testCompanionLateInitProperty_privatePropertyIsAlwaysInlined() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
"companionLateInitProperties_usePrivateLateInitProp");
@@ -252,6 +257,7 @@
@Test
public void testCompanionLateInitProperty_internalPropertyIsAlwaysInlined() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
"companionLateInitProperties_useInternalLateInitProp");
@@ -276,6 +282,7 @@
@Test
public void testCompanionLateInitProperty_publicPropertyIsAlwaysInlined() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
"companionLateInitProperties_usePublicLateInitProp");
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index f572789..ec7d173 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -418,6 +418,7 @@
@Test
public void testCompanionProperty_primitivePropertyCannotBeInlined() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
String mainClass = addMainToClasspath(
"properties.CompanionPropertiesKt", "companionProperties_usePrimitiveProp");
runTest(
@@ -451,6 +452,7 @@
@Test
public void testCompanionProperty_privatePropertyIsAlwaysInlined() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
String mainClass = addMainToClasspath(
"properties.CompanionPropertiesKt", "companionProperties_usePrivateProp");
runTest(
@@ -489,6 +491,7 @@
@Test
public void testCompanionProperty_internalPropertyCannotBeInlined() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
String mainClass = addMainToClasspath(
"properties.CompanionPropertiesKt", "companionProperties_useInternalProp");
runTest(
@@ -521,6 +524,7 @@
@Test
public void testCompanionProperty_publicPropertyCannotBeInlined() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
String mainClass = addMainToClasspath(
"properties.CompanionPropertiesKt", "companionProperties_usePublicProp");
runTest(
@@ -554,6 +558,7 @@
@Test
public void testCompanionProperty_privateLateInitPropertyIsAlwaysInlined() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
"companionLateInitProperties_usePrivateLateInitProp");
@@ -590,6 +595,7 @@
@Test
public void testCompanionProperty_internalLateInitPropertyCannotBeInlined() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
"companionLateInitProperties_useInternalLateInitProp");
@@ -617,6 +623,7 @@
@Test
public void testCompanionProperty_publicLateInitPropertyCannotBeInlined() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
"companionLateInitProperties_usePublicLateInitProp");
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
index 2498467..3705c2d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
@@ -302,6 +302,7 @@
@Test
public void testTrivialKs() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final String mainClassName = "lambdas_kstyle_trivial.MainKt";
runTest(
"lambdas_kstyle_trivial",
@@ -383,6 +384,7 @@
@Test
public void testGenericsNoSignatureKs() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
final String mainClassName = "lambdas_kstyle_generics.MainKt";
runTest(
"lambdas_kstyle_generics",
@@ -411,6 +413,7 @@
@Test
public void testInnerClassesAndEnclosingMethodsKs() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
final String mainClassName = "lambdas_kstyle_generics.MainKt";
runTest(
"lambdas_kstyle_generics",
@@ -475,6 +478,7 @@
@Test
public void testTrivialJs() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final String mainClassName = "lambdas_jstyle_trivial.MainKt";
runTest(
"lambdas_jstyle_trivial",
@@ -524,6 +528,7 @@
@Test
public void testSingleton() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final String mainClassName = "lambdas_singleton.MainKt";
runTest(
"lambdas_singleton",
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
index 446d982..84ee93d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
@@ -35,6 +35,7 @@
@Test
public void testJStyleRunnable() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final String mainClassName = "lambdas_jstyle_runnable.MainKt";
runTest("lambdas_jstyle_runnable", mainClassName, optionsModifier, null);
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index dec2ff2..0028070 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -41,6 +41,7 @@
@Test
public void testJstyleRunnable() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
final String folder = "lambdas_jstyle_runnable";
final String mainClassName = "lambdas_jstyle_runnable.MainKt";
final String implementer1ClassName = "lambdas_jstyle_runnable.Implementer1Kt";
diff --git a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileContentsTest.java b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileContentsTest.java
index 2a32355..fb89853 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileContentsTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileContentsTest.java
@@ -7,7 +7,6 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
@@ -16,12 +15,12 @@
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.DataResourceConsumer;
import com.android.tools.r8.DataResourceProvider.Visitor;
-import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.TestCompileResult;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatibilityTestBase;
import com.android.tools.r8.utils.ArchiveResourceProvider;
+import com.android.tools.r8.utils.DataResourceConsumerForTesting;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
@@ -30,10 +29,8 @@
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Test;
@@ -60,57 +57,6 @@
this.backend = backend;
}
- public static class DataResourceConsumerForTesting implements DataResourceConsumer {
-
- private final DataResourceConsumer inner;
- private final Map<String, ImmutableList<String>> resources = new HashMap<>();
-
- public DataResourceConsumerForTesting() {
- this(null);
- }
-
- public DataResourceConsumerForTesting(DataResourceConsumer inner) {
- this.inner = inner;
- }
-
- @Override
- public void accept(DataDirectoryResource directory, DiagnosticsHandler diagnosticsHandler) {
- if (inner != null) {
- inner.accept(directory, diagnosticsHandler);
- }
- }
-
- @Override
- public void accept(DataEntryResource file, DiagnosticsHandler diagnosticsHandler) {
- assertFalse(resources.containsKey(file.getName()));
- try {
- byte[] bytes = ByteStreams.toByteArray(file.getByteStream());
- String contents = new String(bytes, Charset.defaultCharset());
- resources.put(file.getName(), ImmutableList.copyOf(contents.split(System.lineSeparator())));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- if (inner != null) {
- inner.accept(file, diagnosticsHandler);
- }
- }
-
- @Override
- public void finished(DiagnosticsHandler handler) {}
-
- public ImmutableList<String> get(String name) {
- return resources.get(name);
- }
-
- public boolean isEmpty() {
- return size() == 0;
- }
-
- public int size() {
- return resources.size();
- }
- }
-
private static final ImmutableList<String> originalAllChangedResource =
ImmutableList.of(
"com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$A",
diff --git a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
index 188e58d..8102aaf 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
@@ -19,11 +19,11 @@
import com.android.tools.r8.R8Command;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.naming.AdaptResourceFileContentsTest.DataResourceConsumerForTesting;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatibilityTestBase;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ArchiveResourceProvider;
+import com.android.tools.r8.utils.DataResourceConsumerForTesting;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.KeepingDiagnosticHandler;
import com.android.tools.r8.utils.StringUtils;
@@ -112,6 +112,7 @@
@Test
public void testEnabled() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting();
compileWithR8(
getProguardConfigWithNeverInline(true, null),
diff --git a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
index d0cb2bf..d84c94c 100644
--- a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
+++ b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
@@ -70,6 +70,7 @@
@Test
public void innerConstructsOuter() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
Class<?> clazz = com.android.tools.r8.regress.b69825683.innerconstructsouter.Outer.class;
CodeInspector inspector =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
index 2f6574b..d912811 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
@@ -98,6 +98,7 @@
@Test
public void testR8() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addProgramFiles(ToolHelper.getClassFilesForTestPackage(TestClass.class.getPackage()))
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
index 2000456..7435fe1 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
@@ -85,6 +85,7 @@
@Test
public void testR8() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addProgramClasses(getClasses())
.addProgramClassFileData(getTransformedClasses())
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
index 0460ce5..6e9ddce 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.OptionalBool;
-import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import java.util.List;
@@ -29,8 +28,6 @@
@RunWith(Parameterized.class)
public class NestStaticMethodAccessWithIntermediateClassTest extends TestBase {
- static final String EXPECTED = StringUtils.lines("A::bar");
-
private final TestParameters parameters;
private final boolean inSameNest;
@@ -87,6 +84,7 @@
@Test
public void testR8() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addProgramClasses(getClasses())
.addProgramClassFileData(getTransformedClasses())
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
index 5e2743c..bc9973b 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
@@ -87,6 +87,7 @@
@Test
public void testR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(!inSameNest);
testForR8(parameters.getBackend())
.addProgramClasses(getClasses())
.addProgramClassFileData(getTransformedClasses())
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
index 4aff595..0deacd4 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
@@ -17,7 +17,6 @@
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.OptionalBool;
-import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import java.util.List;
@@ -28,8 +27,6 @@
@RunWith(Parameterized.class)
public class NestVirtualMethodAccessWithIntermediateClassTest extends TestBase {
- static final String EXPECTED = StringUtils.lines("A::bar");
-
private final TestParameters parameters;
private final boolean inSameNest;
@@ -86,6 +83,7 @@
@Test
public void testR8() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addProgramClasses(getClasses())
.addProgramClassFileData(getTransformedClasses())
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java
index eb88c69..8ae84a0 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java
@@ -79,6 +79,7 @@
@Test
public void testR8() throws IOException, CompilationFailedException, ExecutionException {
+ expectThrowsWithHorizontalClassMergingIf(parameters.isCfRuntime());
testForR8(parameters.getBackend())
.addInnerClasses(MultipleImplementsTest.class)
.enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java
index 5628ea2..829a26b 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java
@@ -85,6 +85,7 @@
@Test
public void testR8() throws IOException, CompilationFailedException, ExecutionException {
+ expectThrowsWithHorizontalClassMergingIf(parameters.isCfRuntime());
testForR8(parameters.getBackend())
.addInnerClasses(SubTypeMissingOverridesTest.class)
.enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
index c1f2301..0afee04 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
@@ -86,6 +86,7 @@
@Test
public void testR8() throws IOException, CompilationFailedException, ExecutionException {
+ expectThrowsWithHorizontalClassMergingIf(parameters.isCfRuntime());
testForR8(parameters.getBackend())
.addInnerClasses(TargetInDefaultMethodTest.class)
.enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java b/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
index b299b76..943e3b3 100644
--- a/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
@@ -53,6 +53,7 @@
@Test
public void testR8() throws IOException, CompilationFailedException, ExecutionException {
+ expectThrowsWithHorizontalClassMergingIf(parameters.isDexRuntime());
testForR8(parameters.getBackend())
.addInnerClasses(JavaScriptScriptEngineTest.class)
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassTest.java b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassTest.java
new file mode 100644
index 0000000..94d7781
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassTest.java
@@ -0,0 +1,134 @@
+// 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.rewrite.serviceloaders;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.AppServices;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.rewrite.serviceloaders.MissingServiceImplementationClassTest.Service;
+import com.android.tools.r8.rewrite.serviceloaders.MissingServiceImplementationClassTest.ServiceImpl;
+import com.android.tools.r8.utils.DataResourceConsumerForTesting;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+import java.util.ServiceLoader;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// Test where the service class and the service implementation class are missing.
+@RunWith(Parameterized.class)
+public class MissingServiceClassTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public MissingServiceClassTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting();
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addKeepMainRule(TestClass.class)
+ .addDataEntryResources(
+ DataEntryResource.fromBytes(
+ StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
+ AppServices.SERVICE_DIRECTORY_NAME + Service.class.getTypeName(),
+ Origin.unknown()))
+ .addOptionsModification(o -> o.dataResourceConsumer = dataResourceConsumer)
+ .allowDiagnosticWarningMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectDiagnosticMessages(
+ inspector -> {
+ inspector.assertWarningsCount(2);
+ inspector.assertAllWarningsMatch(
+ diagnosticMessage(
+ anyOf(
+ containsString(
+ "Unexpected reference to missing service class: "
+ + AppServices.SERVICE_DIRECTORY_NAME
+ + Service.class.getTypeName()
+ + "."),
+ containsString(
+ "Unexpected reference to missing service implementation class in "
+ + AppServices.SERVICE_DIRECTORY_NAME
+ + Service.class.getTypeName()
+ + ": "
+ + ServiceImpl.class.getTypeName()
+ + "."))));
+ })
+ .apply(this::configureRunClasspath)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithEmptyOutput();
+
+ inspectResource(
+ dataResourceConsumer.get(AppServices.SERVICE_DIRECTORY_NAME + Service.class.getTypeName()));
+ }
+
+ private void inspectResource(List<String> contents) {
+ assertNotNull(contents);
+ assertEquals(1, contents.size());
+ assertEquals(ServiceImpl.class.getTypeName(), contents.get(0));
+ }
+
+ private void configureRunClasspath(R8TestCompileResult compileResult) throws Exception {
+ if (parameters.isCfRuntime()) {
+ compileResult.addRunClasspathClasses(Service.class, ServiceImpl.class);
+ } else {
+ compileResult.addRunClasspathFiles(
+ testForD8(temp)
+ .addProgramClasses(Service.class, ServiceImpl.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .writeToZip());
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) throws ClassNotFoundException {
+ for (Object service : ServiceLoader.load(getServiceClass())) {
+ System.out.println(service.toString());
+ }
+ }
+
+ static Class<?> getServiceClass() throws ClassNotFoundException {
+ // Get the Service class without accessing it directly. Accessing it directly would cause a
+ // compilation error due the the class being absent. This can be avoided by adding -dontwarn,
+ // but then R8 don't report a warning for the incorrect resource in META-INF/services, which
+ // this is trying to test.
+ return Class.forName(
+ "com.android.tools.r8.rewrite.serviceloaders.MissingServiceClassTest$Service");
+ }
+ }
+
+ public interface Service {}
+
+ public static class ServiceImpl implements Service {
+
+ @Override
+ public String toString() {
+ return "Hello world!";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassWithFeatureTest.java b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassWithFeatureTest.java
new file mode 100644
index 0000000..f36c122
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassWithFeatureTest.java
@@ -0,0 +1,75 @@
+// 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.rewrite.serviceloaders;
+
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.AppServices;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.ServiceLoader;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// Test where a service implementation class is missing in presence of feature splits.
+@RunWith(Parameterized.class)
+public class MissingServiceClassWithFeatureTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public MissingServiceClassWithFeatureTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, ServiceImpl.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassAndMembersRules(FeatureClass.class)
+ .addKeepRules("-dontwarn " + Service.class.getTypeName())
+ .addDataEntryResources(
+ DataEntryResource.fromBytes(
+ StringUtils.lines("java.lang.Object").getBytes(),
+ AppServices.SERVICE_DIRECTORY_NAME + Service.class.getTypeName(),
+ Origin.unknown()))
+ .addFeatureSplit(FeatureClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ for (Service service : ServiceLoader.load(Service.class, TestClass.class.getClassLoader())) {
+ service.greet();
+ }
+ }
+ }
+
+ public interface Service {
+
+ void greet();
+ }
+
+ public static class ServiceImpl implements Service {
+
+ @Override
+ public void greet() {
+ System.out.println("Hello world!");
+ }
+ }
+
+ public static class FeatureClass {}
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassTest.java b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassTest.java
new file mode 100644
index 0000000..7f3e416
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassTest.java
@@ -0,0 +1,180 @@
+// 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.rewrite.serviceloaders;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.AppServices;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.rewrite.serviceloaders.MissingServiceClassTest.Service;
+import com.android.tools.r8.rewrite.serviceloaders.MissingServiceClassTest.ServiceImpl;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.DataResourceConsumerForTesting;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.io.ByteStreams;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// Test where a service implementation class is missing.
+@RunWith(Parameterized.class)
+public class MissingServiceImplementationClassTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public MissingServiceImplementationClassTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ Box<DataResourceConsumerForTesting> dataResourceConsumer = new Box<>();
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, Service.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassAndMembersRulesWithAllowObfuscation(Service.class)
+ .addDataEntryResources(
+ DataEntryResource.fromBytes(
+ StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
+ AppServices.SERVICE_DIRECTORY_NAME + Service.class.getTypeName(),
+ Origin.unknown()))
+ .addOptionsModification(
+ options -> {
+ dataResourceConsumer.set(
+ new DataResourceConsumerForTesting(options.dataResourceConsumer));
+ options.dataResourceConsumer = dataResourceConsumer.get();
+ })
+ .allowDiagnosticWarningMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectDiagnosticMessages(
+ inspector -> {
+ inspector.assertWarningsCount(1);
+ inspector.assertAllWarningsMatch(
+ diagnosticMessage(
+ containsString(
+ "Unexpected reference to missing service implementation class in "
+ + AppServices.SERVICE_DIRECTORY_NAME
+ + Service.class.getTypeName()
+ + ": "
+ + ServiceImpl.class.getTypeName()
+ + ".")));
+ });
+
+ CodeInspector inspector = compileResult.inspector();
+ ClassSubject serviceClassSubject = inspector.clazz(Service.class);
+ assertThat(serviceClassSubject, isPresent());
+
+ // Verify that ServiceImpl was not removed from META-INF/services/[...]Service.
+ inspectResource(
+ dataResourceConsumer.get().get("META-INF/services/" + serviceClassSubject.getFinalName()));
+
+ // Execution fails since META-INF/services/[...]Service is referring to a missing class.
+ compileResult
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertFailureWithErrorThatThrows(ServiceConfigurationError.class);
+
+ // Verify that it is still possible to inject a new implementation of Service after the
+ // compilation.
+ testForR8(parameters.getBackend())
+ .addProgramClasses(ServiceImpl.class)
+ .addClasspathClasses(Service.class)
+ .addKeepAllClassesRule()
+ .addApplyMapping(compileResult.getProguardMap())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addRunClasspathFiles(
+ transformServiceDeclarationInProgram(
+ compileResult.writeToZip(),
+ AppServices.SERVICE_DIRECTORY_NAME + serviceClassSubject.getFinalName(),
+ ServiceImpl.class.getTypeName()))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspectResource(List<String> contents) {
+ assertNotNull(contents);
+ assertEquals(1, contents.size());
+ assertEquals(ServiceImpl.class.getTypeName(), contents.get(0));
+ }
+
+ private Path transformServiceDeclarationInProgram(Path program, String fileName, String contents)
+ throws Exception {
+ Path newProgram = temp.newFolder().toPath().resolve("program.jar");
+ try (ZipOutputStream outputStream = new ZipOutputStream(Files.newOutputStream(newProgram))) {
+ try (ZipInputStream inputStream = new ZipInputStream(Files.newInputStream(program))) {
+ ZipEntry next = inputStream.getNextEntry();
+ while (next != null) {
+ outputStream.putNextEntry(new ZipEntry(next.getName()));
+ if (next.getName().equals(fileName)) {
+ outputStream.write(contents.getBytes());
+ } else {
+ outputStream.write(ByteStreams.toByteArray(inputStream));
+ }
+ outputStream.closeEntry();
+ next = inputStream.getNextEntry();
+ }
+ }
+ }
+ return newProgram;
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ for (Object object : ServiceLoader.load(getServiceClass())) {
+ if (object instanceof Service) {
+ Service service = (Service) object;
+ service.greet();
+ }
+ }
+ }
+
+ static Class<?> getServiceClass() {
+ return System.currentTimeMillis() >= 0 ? Service.class : Object.class;
+ }
+ }
+
+ public interface Service {
+
+ void greet();
+ }
+
+ public static class ServiceImpl implements Service {
+
+ @NeverInline
+ public void greet() {
+ System.out.println("Hello world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassWithFeatureTest.java b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassWithFeatureTest.java
new file mode 100644
index 0000000..b3f120c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassWithFeatureTest.java
@@ -0,0 +1,82 @@
+// 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.rewrite.serviceloaders;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.AppServices;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.rewrite.serviceloaders.MissingServiceImplementationClassTest.Service;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.ServiceLoader;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// Test where a service implementation class is missing in presence of feature splits.
+@RunWith(Parameterized.class)
+public class MissingServiceImplementationClassWithFeatureTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public MissingServiceImplementationClassWithFeatureTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, Service.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassAndMembersRules(FeatureClass.class)
+ .addDataEntryResources(
+ DataEntryResource.fromBytes(
+ StringUtils.lines("MissingClass").getBytes(),
+ AppServices.SERVICE_DIRECTORY_NAME + Service.class.getTypeName(),
+ Origin.unknown()))
+ .addFeatureSplit(FeatureClass.class)
+ .allowDiagnosticWarningMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectDiagnosticMessages(
+ inspector -> {
+ inspector.assertWarningsCount(1);
+ inspector.assertAllWarningsMatch(
+ diagnosticMessage(
+ containsString(
+ "Unexpected reference to missing service implementation class in "
+ + AppServices.SERVICE_DIRECTORY_NAME
+ + Service.class.getTypeName()
+ + ": MissingClass.")));
+ });
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ for (Service service : ServiceLoader.load(Service.class, TestClass.class.getClassLoader())) {
+ service.greet();
+ }
+ }
+ }
+
+ public interface Service {
+
+ void greet();
+ }
+
+ public static class FeatureClass {}
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/ServiceWithoutResourceFileAtCompileTimeTest.java b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/ServiceWithoutResourceFileAtCompileTimeTest.java
new file mode 100644
index 0000000..1ea55b8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/ServiceWithoutResourceFileAtCompileTimeTest.java
@@ -0,0 +1,131 @@
+// 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.rewrite.serviceloaders;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.AppServices;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.io.ByteStreams;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ServiceLoader;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// Test where the resource META-INF/services/Service for a service class Service is missing.
+//
+// The program succeeds with the expected output when META-INF/services/Service is bundled with the
+// application after the compilation.
+@RunWith(Parameterized.class)
+public class ServiceWithoutResourceFileAtCompileTimeTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ServiceWithoutResourceFileAtCompileTimeTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, Service.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassAndMembersRulesWithAllowObfuscation(Service.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+
+ CodeInspector inspector = compileResult.inspector();
+ ClassSubject serviceClassSubject = inspector.clazz(Service.class);
+ assertThat(serviceClassSubject, isPresent());
+
+ // Execution succeeds with the empty output since META-INF/services/[...]Service is missing.
+ compileResult.run(parameters.getRuntime(), TestClass.class).assertSuccessWithEmptyOutput();
+
+ // Verify that it is still possible to inject a new implementation of Service after the
+ // compilation.
+ testForR8(parameters.getBackend())
+ .addProgramClasses(ServiceImpl.class)
+ .addClasspathClasses(Service.class)
+ .addKeepAllClassesRule()
+ .addApplyMapping(compileResult.getProguardMap())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addRunClasspathFiles(
+ injectServiceDeclarationInProgram(
+ compileResult.writeToZip(),
+ AppServices.SERVICE_DIRECTORY_NAME + serviceClassSubject.getFinalName(),
+ ServiceImpl.class.getTypeName()))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private Path injectServiceDeclarationInProgram(Path program, String fileName, String contents)
+ throws Exception {
+ Path newProgram = temp.newFolder().toPath().resolve("program.jar");
+ try (ZipOutputStream outputStream = new ZipOutputStream(Files.newOutputStream(newProgram))) {
+ try (ZipInputStream inputStream = new ZipInputStream(Files.newInputStream(program))) {
+ ZipEntry next = inputStream.getNextEntry();
+ while (next != null) {
+ outputStream.putNextEntry(new ZipEntry(next.getName()));
+ outputStream.write(ByteStreams.toByteArray(inputStream));
+ outputStream.closeEntry();
+ next = inputStream.getNextEntry();
+ }
+ }
+ outputStream.putNextEntry(new ZipEntry(fileName));
+ outputStream.write(contents.getBytes());
+ outputStream.closeEntry();
+ }
+ return newProgram;
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ for (Object object : ServiceLoader.load(getServiceClass())) {
+ if (object instanceof Service) {
+ Service service = (Service) object;
+ service.greet();
+ }
+ }
+ }
+
+ static Class<?> getServiceClass() {
+ return System.currentTimeMillis() >= 0 ? Service.class : Object.class;
+ }
+ }
+
+ public interface Service {
+
+ void greet();
+ }
+
+ public static class ServiceImpl implements Service {
+
+ @NeverInline
+ public void greet() {
+ System.out.println("Hello world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/AbstractMethodOnNonAbstractClassTest.java b/src/test/java/com/android/tools/r8/shaking/AbstractMethodOnNonAbstractClassTest.java
index 2f95543..4d65424 100644
--- a/src/test/java/com/android/tools/r8/shaking/AbstractMethodOnNonAbstractClassTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/AbstractMethodOnNonAbstractClassTest.java
@@ -40,6 +40,7 @@
@Test
public void testCompat() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
R8TestCompileResult compileResult =
testForR8Compat(parameters.getBackend())
.addInnerClasses(AbstractMethodOnNonAbstractClassTest.class)
diff --git a/src/test/java/com/android/tools/r8/shaking/EventuallyNonTargetedMethodTest.java b/src/test/java/com/android/tools/r8/shaking/EventuallyNonTargetedMethodTest.java
index 382f515..b85fe68 100644
--- a/src/test/java/com/android/tools/r8/shaking/EventuallyNonTargetedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/EventuallyNonTargetedMethodTest.java
@@ -37,6 +37,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.enableInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
index fd676a3..6bd40a0 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
@@ -100,6 +100,7 @@
@Test
public void testWithMembersPresentAnnotation() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(Backend.CF)
.addProgramFiles(R8_JAR)
.addKeepRules("-keepclasseswithmembers class * { @" + PRESENT_ANNOTATION + " *** *(...); }")
@@ -183,6 +184,7 @@
@Test
public void testConditionalEqualsKeepClassMembers() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
GraphInspector referenceInspector =
testForR8(Backend.CF)
.enableGraphInspector()
diff --git a/src/test/java/com/android/tools/r8/shaking/NonTargetedMethodTest.java b/src/test/java/com/android/tools/r8/shaking/NonTargetedMethodTest.java
index f813bf2..42dab36 100644
--- a/src/test/java/com/android/tools/r8/shaking/NonTargetedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/NonTargetedMethodTest.java
@@ -38,6 +38,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.enableInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
index e23b80b..b21fc5f 100644
--- a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
@@ -19,11 +19,11 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.graph.AppServices;
-import com.android.tools.r8.naming.AdaptResourceFileContentsTest.DataResourceConsumerForTesting;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.DataResourceConsumerForTesting;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnFieldsTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnFieldsTest.java
index edd818e..d0e3b9e 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnFieldsTest.java
@@ -45,6 +45,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8Compat(backend)
.enableNeverClassInliningAnnotations()
.addProgramClasses(CLASSES)
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
index 86a77a5..816c073 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
@@ -141,6 +141,7 @@
@Test
public void testR8() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(config != TestConfig.NON_SPECIFIC_RULES_ALL);
testForR8(parameters.getBackend())
.addInnerClasses(AssumenosideeffectsPropagationTest.class)
.addKeepMainRule(MAIN)
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithSuperCallTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithSuperCallTest.java
index ba5da12..ecf26e9 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithSuperCallTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithSuperCallTest.java
@@ -73,6 +73,7 @@
@Test
public void testR8() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(AssumenosideeffectsPropagationWithSuperCallTest.class)
.addKeepMainRule(MAIN)
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking12Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking12Test.java
index b30a14d..47f8b62 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking12Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking12Test.java
@@ -44,6 +44,8 @@
@Test
public void testKeeprules() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ getFrontend() != Frontend.JAR || getParameters().isDexRuntime());
runTest(
TreeShaking12Test::shaking12OnlyInstantiatedClassesHaveConstructors,
null,
@@ -53,6 +55,8 @@
@Test
public void testKeeprulesprintusage() throws Exception {
+ expectThrowsWithHorizontalClassMergingIf(
+ getFrontend() != Frontend.JAR || getParameters().isDexRuntime());
runTest(
null,
null,
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
index d91a2fd..a1a2748 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
@@ -39,6 +39,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
runTest(
TreeShaking18Test::unusedRemoved,
null,
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAbstractMethodRemovalTest.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAbstractMethodRemovalTest.java
index b008254..a82fede 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAbstractMethodRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAbstractMethodRemovalTest.java
@@ -37,6 +37,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
runTest(
null,
null,
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingInliningTest.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingInliningTest.java
index 08dfc6a..97ad730 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingInliningTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingInliningTest.java
@@ -37,11 +37,13 @@
@Test
public void testKeeprules() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
runTest(null, null, null, ImmutableList.of("src/test/examples/inlining/keep-rules.txt"));
}
@Test
public void testKeeprulesdiscard() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
// On the cf backend, we don't inline into constructors, see: b/136250031
List<String> keepRules =
getParameters().isCfRuntime()
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingMinificationTest.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingMinificationTest.java
index b8151d6..f703896 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingMinificationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingMinificationTest.java
@@ -38,6 +38,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
runTest(null, null, null, ImmutableList.of("src/test/examples/minification/keep-rules.txt"));
}
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
index a686567..8ab404c 100644
--- a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
@@ -82,8 +82,9 @@
@Test
public void testNoIncludesDescriptorClasses() throws Exception {
- for (Class mainClass : mainClasses) {
- List<Class> allClasses = new ArrayList<>(applicationClasses);
+ expectThrowsWithHorizontalClassMerging();
+ for (Class<?> mainClass : mainClasses) {
+ List<Class<?>> allClasses = new ArrayList<>(applicationClasses);
allClasses.add(mainClass);
Path proguardConfig = writeTextToTempFile(
@@ -160,7 +161,8 @@
@Test
public void testKeepClassMemberNames() throws Exception {
- for (Class mainClass : mainClasses) {
+ expectThrowsWithHorizontalClassMerging();
+ for (Class<?> mainClass : mainClasses) {
Path proguardConfig = writeTextToTempFile(
keepMainProguardConfiguration(mainClass),
// same as -keepclassmembers,allowshrinking,includedescriptorclasses
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
index cd6e4a3..367bfbc 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
@@ -45,6 +45,7 @@
@Test
public void test() throws Throwable {
+ expectThrowsWithHorizontalClassMerging();
testForR8(Backend.CF)
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
.addKeepRuleFiles(MAIN_KEEP)
diff --git a/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideTest.java b/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideTest.java
index bc12cfc..908f9f7 100644
--- a/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideTest.java
@@ -37,6 +37,7 @@
@Test
public void test() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(LibraryMethodOverrideTest.class)
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java b/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
index 41533ec..8d56292 100644
--- a/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
@@ -50,6 +50,7 @@
@Test
public void testDefaultInlining() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
CodeInspector inspector =
runTest(
ImmutableList.of(
@@ -78,6 +79,7 @@
@Test
public void testNeverInline() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
CodeInspector inspector =
runTest(
ImmutableList.of(
@@ -105,6 +107,7 @@
@Test
public void testForceInline() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
CodeInspector inspector =
runTest(
ImmutableList.of(
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index fa627ef..4779b8e 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -162,6 +162,14 @@
/** Unconditionally replace the implements clause of a class. */
public ClassFileTransformer setImplements(Class<?>... interfaces) {
+ return setImplementsClassDescriptors(
+ Arrays.stream(interfaces)
+ .map(clazz -> DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName()))
+ .toArray(String[]::new));
+ }
+
+ /** Unconditionally replace the implements clause of a class. */
+ public ClassFileTransformer setImplementsClassDescriptors(String... classDescriptors) {
return addClassTransformer(
new ClassTransformer() {
@Override
@@ -178,8 +186,8 @@
name,
signature,
superName,
- Arrays.stream(interfaces)
- .map(clazz -> DescriptorUtils.getBinaryNameFromJavaType(clazz.getTypeName()))
+ Arrays.stream(classDescriptors)
+ .map(DescriptorUtils::getBinaryNameFromDescriptor)
.toArray(String[]::new));
}
});
diff --git a/src/test/java/com/android/tools/r8/utils/AndroidJarPresenceTest.java b/src/test/java/com/android/tools/r8/utils/AndroidJarPresenceTest.java
new file mode 100644
index 0000000..e84aaa2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/AndroidJarPresenceTest.java
@@ -0,0 +1,35 @@
+// 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.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AndroidJarPresenceTest extends TestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public AndroidJarPresenceTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ @Test
+ public void test() throws Exception {
+ for (AndroidApiLevel apiLevel : AndroidApiLevel.values()) {
+ assertEquals(ToolHelper.shouldHaveAndroidJar(apiLevel), ToolHelper.hasAndroidJar(apiLevel));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/DataResourceConsumerForTesting.java b/src/test/java/com/android/tools/r8/utils/DataResourceConsumerForTesting.java
new file mode 100644
index 0000000..77274e6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/DataResourceConsumerForTesting.java
@@ -0,0 +1,68 @@
+// 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.utils;
+
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.DataDirectoryResource;
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.DataResourceConsumer;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Map;
+
+public class DataResourceConsumerForTesting implements DataResourceConsumer {
+
+ private final DataResourceConsumer inner;
+ private final Map<String, ImmutableList<String>> resources = new HashMap<>();
+
+ public DataResourceConsumerForTesting() {
+ this(null);
+ }
+
+ public DataResourceConsumerForTesting(DataResourceConsumer inner) {
+ this.inner = inner;
+ }
+
+ @Override
+ public void accept(DataDirectoryResource directory, DiagnosticsHandler diagnosticsHandler) {
+ if (inner != null) {
+ inner.accept(directory, diagnosticsHandler);
+ }
+ }
+
+ @Override
+ public void accept(DataEntryResource file, DiagnosticsHandler diagnosticsHandler) {
+ assertFalse(resources.containsKey(file.getName()));
+ try {
+ byte[] bytes = ByteStreams.toByteArray(file.getByteStream());
+ String contents = new String(bytes, Charset.defaultCharset());
+ resources.put(file.getName(), ImmutableList.copyOf(contents.split(System.lineSeparator())));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ if (inner != null) {
+ inner.accept(file, diagnosticsHandler);
+ }
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {}
+
+ public ImmutableList<String> get(String name) {
+ return resources.get(name);
+ }
+
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ public int size() {
+ return resources.size();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/ReflectiveBuildPathUtils.java b/src/test/java/com/android/tools/r8/utils/ReflectiveBuildPathUtils.java
new file mode 100644
index 0000000..ed991f2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/ReflectiveBuildPathUtils.java
@@ -0,0 +1,168 @@
+// 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.utils;
+
+import com.android.tools.r8.ToolHelper;
+import com.google.common.collect.Iterables;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public class ReflectiveBuildPathUtils {
+
+ public interface PackageUtils {
+ String getPackageName() throws Exception;
+
+ Path getPackagePath() throws Exception;
+
+ PackageUtils getParentPackageUtils() throws Exception;
+
+ Iterable<PackageUtils> getAllPackageUtils();
+ }
+
+ public interface ClassUtils {
+ String getClassName() throws Exception;
+
+ String getSimpleClassName() throws Exception;
+
+ Path getClassPath() throws Exception;
+ }
+
+ public abstract static class ExamplesRootPackage implements PackageUtils {
+ public abstract Path getPackagePath();
+
+ @Override
+ public String getPackageName() {
+ return "";
+ }
+
+ @Override
+ public PackageUtils getParentPackageUtils() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Iterable<PackageUtils> getAllPackageUtils() {
+ return instantiateAllPackageUtils(getClass());
+ }
+ }
+
+ public abstract static class ExamplesJava11RootPackage extends ExamplesRootPackage {
+ @Override
+ public Path getPackagePath() {
+ return Paths.get(ToolHelper.EXAMPLES_JAVA11_BUILD_DIR);
+ }
+ }
+
+ public abstract static class ExamplesPackage implements PackageUtils {
+ public List<String> getName() {
+ return Collections.singletonList(getClass().getSimpleName());
+ }
+
+ @Override
+ public PackageUtils getParentPackageUtils() throws Exception {
+ return (PackageUtils) getClass().getDeclaringClass().getConstructor().newInstance();
+ }
+
+ @Override
+ public Path getPackagePath() throws Exception {
+ Path path = getParentPackageUtils().getPackagePath();
+ for (String folder : getName()) {
+ path = path.resolve(folder);
+ }
+ return path;
+ }
+
+ @Override
+ public String getPackageName() throws Exception {
+ return getParentPackageUtils().getPackageName() + String.join(".", getName()) + ".";
+ }
+
+ @Override
+ public Iterable<PackageUtils> getAllPackageUtils() {
+ return instantiateAllPackageUtils(getClass());
+ }
+ }
+
+ public static class ExamplesClass implements PackageUtils, ClassUtils {
+ @Override
+ public PackageUtils getParentPackageUtils() throws Exception {
+ return (PackageUtils) getClass().getDeclaringClass().getConstructor().newInstance();
+ }
+
+ public String getSimpleClassName() throws Exception {
+ Object parent = getClass().getDeclaringClass().getConstructor().newInstance();
+ if (parent instanceof ClassUtils) {
+ return ((ClassUtils) parent).getSimpleClassName() + "$" + getClass().getSimpleName();
+ } else {
+ return getClass().getSimpleName();
+ }
+ }
+
+ @Override
+ public String getClassName() throws Exception {
+ return getParentPackageUtils().getPackageName() + getSimpleClassName();
+ }
+
+ @Override
+ public Path getClassPath() throws Exception {
+ return getParentPackageUtils().getPackagePath().resolve(getSimpleClassName() + ".class");
+ }
+
+ @Override
+ public String getPackageName() throws Exception {
+ return getParentPackageUtils().getPackageName();
+ }
+
+ @Override
+ public Path getPackagePath() throws Exception {
+ return getParentPackageUtils().getPackagePath();
+ }
+
+ @Override
+ public Iterable<PackageUtils> getAllPackageUtils() {
+ return instantiateAllPackageUtils(getClass());
+ }
+ }
+
+ public static Iterable<PackageUtils> instantiateAllPackageUtils(Class<?> parentClazz) {
+ Collection<PackageUtils> children = instantiatePackageUtils(parentClazz);
+ return Iterables.concat(
+ children,
+ Iterables.concat(Iterables.transform(children, PackageUtils::getAllPackageUtils)));
+ }
+
+ public static Collection<PackageUtils> instantiatePackageUtils(Class<?> parentClazz) {
+ Collection<PackageUtils> packageUtils = new ArrayList<>();
+ for (Class<?> clazz : parentClazz.getDeclaredClasses()) {
+ try {
+ Object obj = clazz.getConstructor().newInstance();
+ if (obj instanceof PackageUtils) {
+ packageUtils.add((PackageUtils) obj);
+ }
+ } catch (Exception ex) {
+ }
+ }
+ return packageUtils;
+ }
+
+ public static String resolveClassName(Class<? extends ExamplesClass> clazz) throws Exception {
+ return clazz.getConstructor().newInstance().getClassName();
+ }
+
+ public static Collection<Path> allClassFiles(Class<? extends ExamplesRootPackage> clazz)
+ throws Exception {
+ Collection<Path> classFiles = new ArrayList<>();
+ for (PackageUtils util : clazz.getConstructor().newInstance().getAllPackageUtils()) {
+ if (util instanceof ClassUtils) {
+ classFiles.add(((ClassUtils) util).getClassPath());
+ }
+ }
+ return classFiles;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index 978db99..6160dc6 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -47,6 +47,11 @@
}
@Override
+ public MethodSubject uniqueMethodWithFinalName(String name) {
+ return new AbsentMethodSubject();
+ }
+
+ @Override
public void forAllFields(Consumer<FoundFieldSubject> inspection) {}
@Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index 6c7368b..60203c1 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -100,6 +100,8 @@
public abstract MethodSubject uniqueMethodWithName(String name);
+ public abstract MethodSubject uniqueMethodWithFinalName(String name);
+
public MethodSubject mainMethod() {
return method("void", "main", ImmutableList.of("java.lang.String[]"));
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index f42bc3e..ab1d03b 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -146,6 +146,18 @@
}
@Override
+ public MethodSubject uniqueMethodWithFinalName(String name) {
+ MethodSubject methodSubject = null;
+ for (FoundMethodSubject candidate : allMethods()) {
+ if (candidate.getFinalName().equals(name)) {
+ assert methodSubject == null;
+ methodSubject = candidate;
+ }
+ }
+ return methodSubject != null ? methodSubject : new AbsentMethodSubject();
+ }
+
+ @Override
public void forAllFields(Consumer<FoundFieldSubject> inspection) {
forAllInstanceFields(inspection);
forAllStaticFields(inspection);
diff --git a/tools/test.py b/tools/test.py
index 27f15a8..c949fb3 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -163,11 +163,6 @@
(options, args) = ParseOptions()
if utils.is_bot():
- if options.horizontal_class_merging:
- # This flag is in preparation of running horizontal class merging
- # but currently is the same as the default tests. Don't run to
- # save resources on the bots.
- return 0
gradle.RunGradle(['--no-daemon', 'clean'])
gradle_args = ['--stacktrace']