Merge commit 'c3f20e7a6db394955ae6359bb4c7f11de74453be' into dev-release
diff --git a/build.gradle b/build.gradle
index 5220ac2..8e20e6d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -428,13 +428,7 @@
"gmail/gmail_android_170604.16",
"gmail/gmail_android_180826.15",
"gmscore/gmscore_v10",
- "gmscore/gmscore_v9",
"gmscore/latest",
- "gmscore/v4",
- "gmscore/v5",
- "gmscore/v6",
- "gmscore/v7",
- "gmscore/v8",
"nest/nest_20180926_7c6cfb",
"proguard/proguard_internal_159423826",
"proguardsettings",
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index ec9955d..6fea9a2 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -160,6 +160,7 @@
assert !internal.minimalMainDex;
internal.minApiLevel = getMinApiLevel();
assert !internal.intermediate;
+ internal.intermediate = true;
assert internal.readCompileTimeAnnotations;
internal.programConsumer = getProgramConsumer();
assert internal.programConsumer instanceof ClassFileConsumer;
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index ab55c16..df45e6b 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
import com.android.tools.r8.origin.Origin;
@@ -130,13 +131,15 @@
public void addSynthesizedClass(DexProgramClass clazz, ProgramDefinition context) {
assert checkIfObsolete();
assert context != null;
- syntheticItems.addLegacySyntheticClass(clazz, context);
+ syntheticItems.addLegacySyntheticClass(clazz, context, FeatureSplit.BASE);
}
- public void addSynthesizedClass(DexProgramClass clazz, Iterable<DexProgramClass> contexts) {
+ public void addSynthesizedClassToBase(DexProgramClass clazz, Iterable<DexProgramClass> contexts) {
assert checkIfObsolete();
assert !IterableUtils.isEmpty(contexts);
- contexts.forEach(context -> addSynthesizedClass(clazz, context));
+ SyntheticItems syntheticItems = getSyntheticItems();
+ contexts.forEach(
+ context -> syntheticItems.addLegacySyntheticClass(clazz, context, FeatureSplit.BASE));
}
public List<DexProgramClass> classes() {
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index c2c02ad..9b55af9 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.TraversalContinuation.BREAK;
import static com.android.tools.r8.utils.TraversalContinuation.CONTINUE;
+import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.ResolutionResult.ArrayCloneMethodResult;
@@ -156,6 +157,15 @@
return this;
}
+ @Override
+ public void addSynthesizedClass(DexProgramClass clazz, ProgramDefinition context) {
+ assert checkIfObsolete();
+ assert context != null;
+ FeatureSplit featureSplit =
+ classToFeatureSplitMap.getFeatureSplit(context, getSyntheticItems());
+ getSyntheticItems().addLegacySyntheticClass(clazz, context, featureSplit);
+ }
+
/**
* Primitive traversal over all supertypes of a given type.
*
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index ea1d102..6a878f0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.desugar.itf;
import static com.android.tools.r8.ir.code.Invoke.Type.DIRECT;
+import static com.android.tools.r8.ir.code.Invoke.Type.INTERFACE;
import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
import static com.android.tools.r8.ir.code.Invoke.Type.SUPER;
import static com.android.tools.r8.ir.code.Invoke.Type.VIRTUAL;
@@ -231,8 +232,12 @@
if (clazz != null && clazz.isInterface()) {
return true;
}
+ return emulatedMethods.contains(method.getName());
}
- return emulatedMethods.contains(method.getName());
+ if (invokeType == VIRTUAL || invokeType == INTERFACE) {
+ return defaultMethodForEmulatedDispatchOrNull(method, invokeType) != null;
+ }
+ return true;
}
DexType getEmulatedInterface(DexType itf) {
@@ -621,27 +626,41 @@
}
}
- private void rewriteInvokeInterfaceOrInvokeVirtual(
- InvokeMethodWithReceiver invoke, InstructionListIterator instructions) {
- DexMethod invokedMethod = invoke.getInvokedMethod();
- DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
+ private DexClassAndMethod defaultMethodForEmulatedDispatchOrNull(
+ DexMethod method, Type invokeType) {
+ assert invokeType == VIRTUAL || invokeType == INTERFACE;
+ boolean interfaceBit = invokeType.isInterface();
+ DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(method);
if (emulatedItf == null) {
- return;
+ return null;
}
-
// The call potentially ends up in a library class, in which case we need to rewrite, since the
// code may be in the desugared library.
SingleResolutionResult resolution =
- appView
- .appInfoForDesugaring()
- .resolveMethod(invokedMethod, invoke.getInterfaceBit())
- .asSingleResolution();
+ appView.appInfoForDesugaring().resolveMethod(method, interfaceBit).asSingleResolution();
if (resolution != null
&& (resolution.getResolvedHolder().isLibraryClass()
|| appView.options().isDesugaredLibraryCompilation())) {
- assert needsRewriting(invokedMethod, VIRTUAL);
- rewriteCurrentInstructionToEmulatedInterfaceCall(
- emulatedItf, invokedMethod, invoke, instructions);
+ DexClassAndMethod defaultMethod =
+ appView.definitionFor(emulatedItf).lookupClassMethod(method);
+ if (defaultMethod != null && !dontRewrite(defaultMethod)) {
+ assert !defaultMethod.getAccessFlags().isAbstract();
+ return defaultMethod;
+ }
+ }
+ return null;
+ }
+
+ private void rewriteInvokeInterfaceOrInvokeVirtual(
+ InvokeMethodWithReceiver invoke, InstructionListIterator instructions) {
+ DexClassAndMethod defaultMethod =
+ defaultMethodForEmulatedDispatchOrNull(invoke.getInvokedMethod(), invoke.getType());
+ if (defaultMethod != null) {
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(
+ emulateInterfaceLibraryMethod(defaultMethod, factory),
+ invoke.outValue(),
+ invoke.arguments()));
}
}
@@ -733,23 +752,6 @@
return null;
}
- private void rewriteCurrentInstructionToEmulatedInterfaceCall(
- DexType emulatedItf,
- DexMethod invokedMethod,
- InvokeMethod invokeMethod,
- InstructionListIterator instructions) {
- DexClassAndMethod defaultMethod =
- appView.definitionFor(emulatedItf).lookupClassMethod(invokedMethod);
- if (defaultMethod != null && !dontRewrite(defaultMethod)) {
- assert !defaultMethod.getAccessFlags().isAbstract();
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- emulateInterfaceLibraryMethod(defaultMethod, factory),
- invokeMethod.outValue(),
- invokeMethod.arguments()));
- }
- }
-
private boolean isNonDesugaredLibraryClass(DexClass clazz) {
return clazz.isLibraryClass() && !isInDesugaredLibrary(clazz);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index e8e1cdd..e847977 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -432,6 +432,10 @@
}
}
if (method.accessFlags.isBridge()) {
+ if (appView.options().cfToCfDesugar) {
+ // TODO(b/187176895): Find the compilation causing this to not be removed.
+ return false;
+ }
Deque<Pair<DexClass, DexType>> worklist = new ArrayDeque<>();
Set<DexType> seenBefore = new HashSet<>();
addSuperTypes(iface, worklist);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
index c29dd86..54c416a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
@@ -179,7 +179,7 @@
appView.dexItemFactory().getSkipNameValidationForTesting(),
DexProgramClass::checksumFromType);
appBuilder.addSynthesizedClass(syntheticClass);
- appView.appInfo().addSynthesizedClass(syntheticClass, contexts);
+ appView.appInfo().addSynthesizedClassToBase(syntheticClass, contexts);
return syntheticClass;
}
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 9da3a40..5f23e34 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.graph.DexEncodedAnnotation;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
@@ -42,17 +43,19 @@
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.AsmUtils;
+import com.android.tools.r8.utils.ComparatorUtils;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.PredicateUtils;
import com.android.tools.r8.utils.structural.Ordered;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
-import com.google.common.collect.Sets;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
import java.util.Optional;
-import java.util.SortedSet;
import java.util.function.Predicate;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
@@ -236,12 +239,10 @@
writeField(field, writer);
}
if (options.desugarSpecificOptions().sortMethodsOnCfOutput) {
- SortedSet<ProgramMethod> programMethodSortedSet =
- Sets.newTreeSet(
- (a, b) ->
- a.getDefinition().getReference().compareTo(b.getDefinition().getReference()));
- clazz.forEachProgramMethod(programMethodSortedSet::add);
- programMethodSortedSet.forEach(
+ List<ProgramMethod> programMethodSorted = new ArrayList<>();
+ clazz.forEachProgramMethod(programMethodSorted::add);
+ programMethodSorted.sort(this::compareMethodsThroughLens);
+ programMethodSorted.forEach(
method -> writeMethod(method, version, rewriter, writer, defaults));
} else {
clazz.forEachProgramMethod(
@@ -263,6 +264,26 @@
options.reporter, handler -> consumer.accept(ByteDataView.of(result), desc, handler));
}
+ private int compareTypesThroughLens(DexType a, DexType b) {
+ return namingLens.lookupDescriptor(a).compareTo(namingLens.lookupDescriptor(b));
+ }
+
+ private DexString returnTypeThroughLens(DexMethod method) {
+ return namingLens.lookupDescriptor(method.getReturnType());
+ }
+
+ private int compareMethodsThroughLens(ProgramMethod a, ProgramMethod b) {
+ // When writing class files, methods are only compared within the same class.
+ assert a.getHolder().equals(b.getHolder());
+ return Comparator.comparing(this::returnTypeThroughLens)
+ .thenComparing(DexMethod::getName)
+ // .thenComparingInt(m -> m.getProto().getArity()) // Done in arrayComp below.
+ .thenComparing(
+ m -> m.getProto().parameters.values,
+ ComparatorUtils.arrayComparator(this::compareTypesThroughLens))
+ .compare(a.getReference(), b.getReference());
+ }
+
private CfVersion getClassFileVersion(DexEncodedMethod method) {
if (!method.hasClassFileVersion()) {
// In this case bridges have been introduced for the Cf back-end,
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
index 7e66ec1..735a163 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
@@ -152,7 +152,7 @@
@Override
public RetracedMethodReference getRetracedMethod() {
- return null;
+ return methodReference;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index d377362..f6d9e6e 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -156,6 +156,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.BiConsumer;
@@ -3065,7 +3066,7 @@
private final ProcessorContext processorContext;
private Map<DexMethod, MethodProcessingContext> methodProcessingContexts =
- new IdentityHashMap<>();
+ new ConcurrentHashMap<>();
List<ProgramMethod> desugaredMethods = new LinkedList<>();
diff --git a/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java b/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
index adc1298..f71c94e 100644
--- a/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
@@ -38,7 +38,7 @@
this.application = application;
this.committed = committed;
this.committedProgramTypes = committedProgramTypes;
- committed.verifyTypesAreInApp(application);
+ assert committed.verifyTypesAreInApp(application);
}
// Conversion to a mutable synthetic items collection. Should only be used in AppInfo creation.
diff --git a/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java b/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
index f8c56fa..cd80b53 100644
--- a/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
+++ b/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
@@ -298,23 +298,18 @@
}
CommittedSyntheticsCollection rewriteWithLens(NonIdentityGraphLens lens) {
+ ImmutableSet.Builder<DexType> syntheticInputsBuilder = ImmutableSet.builder();
return new CommittedSyntheticsCollection(
- rewriteItems(legacyTypes, lens),
- rewriteItems(nonLegacyMethods, lens),
- rewriteItems(nonLegacyClasses, lens),
- rewriteItems(syntheticInputs, lens));
- }
-
- private static ImmutableSet<DexType> rewriteItems(Set<DexType> items, NonIdentityGraphLens lens) {
- ImmutableSet.Builder<DexType> rewrittenItems = ImmutableSet.builder();
- for (DexType item : items) {
- rewrittenItems.add(lens.lookupType(item));
- }
- return rewrittenItems.build();
+ rewriteItems(legacyTypes, lens, syntheticInputsBuilder),
+ rewriteItems(nonLegacyMethods, lens, syntheticInputsBuilder),
+ rewriteItems(nonLegacyClasses, lens, syntheticInputsBuilder),
+ syntheticInputsBuilder.build());
}
private <R extends Rewritable<R>> ImmutableMap<DexType, List<R>> rewriteItems(
- Map<DexType, List<R>> items, NonIdentityGraphLens lens) {
+ Map<DexType, List<R>> items,
+ NonIdentityGraphLens lens,
+ ImmutableSet.Builder<DexType> syntheticInputsBuilder) {
Map<DexType, List<R>> rewrittenItems = new IdentityHashMap<>();
for (R reference : IterableUtils.flatten(items.values())) {
R rewritten = reference.rewrite(lens);
@@ -322,6 +317,9 @@
rewrittenItems
.computeIfAbsent(rewritten.getHolder(), ignore -> new ArrayList<>())
.add(rewritten);
+ if (syntheticInputs.contains(reference.getHolder())) {
+ syntheticInputsBuilder.add(rewritten.getHolder());
+ }
}
}
return ImmutableMap.copyOf(rewrittenItems);
diff --git a/src/main/java/com/android/tools/r8/synthesis/LegacySyntheticDefinition.java b/src/main/java/com/android/tools/r8/synthesis/LegacySyntheticDefinition.java
index 47a9556..3e50bf5 100644
--- a/src/main/java/com/android/tools/r8/synthesis/LegacySyntheticDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/LegacySyntheticDefinition.java
@@ -3,25 +3,28 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.synthesis;
+import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.utils.IterableUtils;
import com.google.common.collect.ImmutableSet;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class LegacySyntheticDefinition {
private final DexProgramClass clazz;
- private final Map<DexType, DexType> contexts = new ConcurrentHashMap<>();
+ private final Map<DexType, FeatureSplit> contexts = new ConcurrentHashMap<>();
public LegacySyntheticDefinition(DexProgramClass clazz) {
this.clazz = clazz;
}
- public void addContext(ProgramDefinition clazz) {
+ public void addContext(ProgramDefinition clazz, FeatureSplit featureSplit) {
DexType type = clazz.getContextType();
- contexts.put(type, type);
+ contexts.put(type, featureSplit);
}
public Set<DexType> getContexts() {
@@ -29,7 +32,22 @@
}
public LegacySyntheticReference toReference() {
- return new LegacySyntheticReference(clazz.getType(), ImmutableSet.copyOf(contexts.keySet()));
+ return new LegacySyntheticReference(
+ clazz.getType(), ImmutableSet.copyOf(contexts.keySet()), getFeatureSplit());
+ }
+
+ public FeatureSplit getFeatureSplit() {
+ assert verifyConsistentFeatures();
+ if (contexts.isEmpty()) {
+ return FeatureSplit.BASE;
+ }
+ return IterableUtils.first(contexts.values());
+ }
+
+ private boolean verifyConsistentFeatures() {
+ HashSet<FeatureSplit> features = new HashSet<>(contexts.values());
+ assert features.size() < 2;
+ return true;
}
public DexProgramClass getDefinition() {
diff --git a/src/main/java/com/android/tools/r8/synthesis/LegacySyntheticReference.java b/src/main/java/com/android/tools/r8/synthesis/LegacySyntheticReference.java
index 06d2e2c..715e790 100644
--- a/src/main/java/com/android/tools/r8/synthesis/LegacySyntheticReference.java
+++ b/src/main/java/com/android/tools/r8/synthesis/LegacySyntheticReference.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.synthesis;
+import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import java.util.Set;
@@ -10,10 +11,12 @@
public class LegacySyntheticReference implements Rewritable<LegacySyntheticReference> {
private final DexType type;
private final Set<DexType> contexts;
+ private final FeatureSplit featureSplit;
- public LegacySyntheticReference(DexType type, Set<DexType> contexts) {
+ public LegacySyntheticReference(DexType type, Set<DexType> contexts, FeatureSplit featureSplit) {
this.type = type;
this.contexts = contexts;
+ this.featureSplit = featureSplit;
}
@Override
@@ -25,6 +28,10 @@
return contexts;
}
+ public FeatureSplit getFeatureSplit() {
+ return featureSplit;
+ }
+
@Override
public LegacySyntheticReference rewrite(NonIdentityGraphLens lens) {
DexType rewrittenType = lens.lookupType(type);
@@ -32,6 +39,6 @@
if (type == rewrittenType && contexts.equals(rewrittenContexts)) {
return this;
}
- return new LegacySyntheticReference(rewrittenType, rewrittenContexts);
+ return new LegacySyntheticReference(rewrittenType, rewrittenContexts, featureSplit);
}
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
index 3c91d68..ba68e81 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
@@ -92,17 +92,15 @@
D other,
boolean includeContext,
GraphLens graphLens,
- ClassToFeatureSplitMap classToFeatureSplitMap,
- SyntheticItems syntheticItems) {
- return compareTo(other, includeContext, graphLens, classToFeatureSplitMap, syntheticItems) == 0;
+ ClassToFeatureSplitMap classToFeatureSplitMap) {
+ return compareTo(other, includeContext, graphLens, classToFeatureSplitMap) == 0;
}
int compareTo(
D other,
boolean includeContext,
GraphLens graphLens,
- ClassToFeatureSplitMap classToFeatureSplitMap,
- SyntheticItems syntheticItems) {
+ ClassToFeatureSplitMap classToFeatureSplitMap) {
DexType thisType = getHolder().getType();
DexType otherType = other.getHolder().getType();
if (getKind().isFixedSuffixSynthetic) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 46bb432..ac15138 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
@@ -24,10 +25,12 @@
import com.android.tools.r8.graph.TreeFixerBase;
import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IterableUtils;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
@@ -36,6 +39,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.common.hash.HashCode;
import java.util.ArrayList;
@@ -47,6 +51,8 @@
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
public class SyntheticFinalization {
@@ -123,43 +129,6 @@
}
}
- public static class EquivalenceGroup<T extends SyntheticDefinition<?, T, ?>> {
- private final List<T> members;
-
- public EquivalenceGroup(T representative, List<T> members) {
- assert !members.isEmpty();
- assert members.get(0) == representative;
- this.members = members;
- }
-
- public T getRepresentative() {
- return members.get(0);
- }
-
- public List<T> getMembers() {
- return members;
- }
-
- public int compareToIncludingContext(
- EquivalenceGroup<T> other,
- GraphLens graphLens,
- ClassToFeatureSplitMap classToFeatureSplitMap,
- SyntheticItems syntheticItems) {
- return getRepresentative()
- .compareTo(
- other.getRepresentative(), true, graphLens, classToFeatureSplitMap, syntheticItems);
- }
-
- @Override
- public String toString() {
- return "EquivalenceGroup{ members = "
- + members.size()
- + ", repr = "
- + getRepresentative()
- + " }";
- }
- }
-
private final InternalOptions options;
private final SyntheticItems synthetics;
private final CommittedSyntheticsCollection committed;
@@ -257,9 +226,15 @@
SyntheticFinalizationGraphLens syntheticFinalizationGraphLens = lensBuilder.build(appView);
- ImmutableSet.Builder<DexType> finalInputSyntheticsBuilder = ImmutableSet.builder();
- committed.forEachSyntheticInput(
- type -> finalInputSyntheticsBuilder.add(syntheticFinalizationGraphLens.lookupType(type)));
+ ImmutableSet<DexType> finalInputSynthetics =
+ syntheticFinalizationGraphLens != null
+ ? SetUtils.newImmutableSet(
+ builder ->
+ committed.forEachSyntheticInput(
+ syntheticInputType ->
+ builder.accept(
+ syntheticFinalizationGraphLens.lookupType(syntheticInputType))))
+ : committed.syntheticInputs;
// TODO(b/181858113): Remove once deprecated main-dex-list is removed.
MainDexInfo.Builder mainDexInfoBuilder = appView.appInfo().getMainDexInfo().builderFromCopy();
@@ -270,10 +245,7 @@
SyntheticItems.INVALID_ID_AFTER_SYNTHETIC_FINALIZATION,
application,
new CommittedSyntheticsCollection(
- committed.getLegacyTypes(),
- finalMethods,
- finalClasses,
- finalInputSyntheticsBuilder.build()),
+ committed.getLegacyTypes(), finalMethods, finalClasses, finalInputSynthetics),
ImmutableList.of()),
syntheticFinalizationGraphLens,
PrunedItems.builder().setPrunedApp(application).addRemovedClasses(prunedSynthetics).build(),
@@ -390,18 +362,16 @@
SyntheticMethodDefinition representative = syntheticGroup.getRepresentative();
SynthesizingContext context = representative.getContext();
context.registerPrefixRewriting(syntheticType, appView);
- DexProgramClass representativeClass = representative.getHolder();
- addSyntheticMarker(representative.getKind(), representativeClass, context, appView);
- assert representativeClass.getMethodCollection().size() == 1;
- for (SyntheticMethodDefinition member : syntheticGroup.getMembers()) {
- if (member != representative) {
- pruned.add(member.getHolder());
- deduplicatedClasses.add(member.getHolder());
- }
- if (member.getContext().isDerivedFromMainDexList(mainDexInfo)) {
- derivedMainDexSynthetics.add(syntheticType);
- }
+ addSyntheticMarker(
+ representative.getKind(), representative.getHolder(), context, appView);
+ if (syntheticGroup.isDerivedFromMainDexList(mainDexInfo)) {
+ derivedMainDexSynthetics.add(syntheticType);
}
+ syntheticGroup.forEachNonRepresentativeMember(
+ member -> {
+ pruned.add(member.getHolder());
+ deduplicatedClasses.add(member.getHolder());
+ });
});
syntheticClassGroups.forEach(
@@ -411,16 +381,14 @@
context.registerPrefixRewriting(syntheticType, appView);
addSyntheticMarker(
representative.getKind(), representative.getHolder(), context, appView);
- for (SyntheticProgramClassDefinition member : syntheticGroup.getMembers()) {
- DexProgramClass memberClass = member.getHolder();
- if (member != representative) {
- pruned.add(memberClass);
- deduplicatedClasses.add(memberClass);
- }
- if (member.getContext().isDerivedFromMainDexList(mainDexInfo)) {
- derivedMainDexSynthetics.add(syntheticType);
- }
+ if (syntheticGroup.isDerivedFromMainDexList(mainDexInfo)) {
+ derivedMainDexSynthetics.add(syntheticType);
}
+ syntheticGroup.forEachNonRepresentativeMember(
+ member -> {
+ pruned.add(member.getHolder());
+ deduplicatedClasses.add(member.getHolder());
+ });
});
// Only create a new application if anything changed.
@@ -454,6 +422,8 @@
syntheticClassGroups.forEach(
(syntheticType, syntheticGroup) -> {
DexProgramClass externalSyntheticClass = appForLookup.programDefinitionFor(syntheticType);
+ assert externalSyntheticClass != null
+ : "Expected definition for " + syntheticType.getTypeName();
SyntheticProgramClassDefinition representative = syntheticGroup.getRepresentative();
addFinalSyntheticClass.accept(
externalSyntheticClass,
@@ -493,10 +463,10 @@
boolean verifyNonRepresentativesRemovedFromApplication(
DexApplication application, Map<DexType, EquivalenceGroup<T>> syntheticGroups) {
for (EquivalenceGroup<?> syntheticGroup : syntheticGroups.values()) {
- for (SyntheticDefinition<?, ?, ?> member : syntheticGroup.getMembers()) {
- assert member == syntheticGroup.getRepresentative()
- || application.definitionFor(member.getHolder().getType()) == null;
- }
+ syntheticGroup.forEachNonRepresentativeMember(
+ member -> {
+ assert application.definitionFor(member.getHolder().getType()) == null;
+ });
}
return true;
}
@@ -529,34 +499,35 @@
ClassToFeatureSplitMap classToFeatureSplitMap,
Builder lensBuilder) {
Map<String, List<EquivalenceGroup<T>>> groupsPerPrefix = new HashMap<>();
+ Map<DexType, EquivalenceGroup<T>> equivalences = new IdentityHashMap<>();
potentialEquivalences.forEach(
members -> {
- List<List<T>> groups =
- groupEquivalent(
- members, intermediate, appView.graphLens(), classToFeatureSplitMap, synthetics);
- for (List<T> group : groups) {
- T representative =
- findDeterministicRepresentative(
- group, appView.graphLens(), classToFeatureSplitMap, synthetics);
- // The representative is required to be the first element of the group.
- group.remove(representative);
- group.add(0, representative);
- groupsPerPrefix
- .computeIfAbsent(
- representative.getPrefixForExternalSyntheticType(), k -> new ArrayList<>())
- .add(new EquivalenceGroup<>(representative, group));
+ List<EquivalenceGroup<T>> groups =
+ groupEquivalent(appView, members, intermediate, classToFeatureSplitMap);
+ for (EquivalenceGroup<T> group : groups) {
+ // If the group already has a representative, then this representative is pinned.
+ // Otherwise, we select a deterministic representative.
+ if (group.hasRepresentative()) {
+ EquivalenceGroup<T> previous =
+ equivalences.put(group.getRepresentative().getHolder().getType(), group);
+ assert previous == null;
+ } else {
+ group.selectDeterministicRepresentative();
+ groupsPerPrefix
+ .computeIfAbsent(
+ group.getRepresentative().getPrefixForExternalSyntheticType(),
+ k -> new ArrayList<>())
+ .add(group);
+ }
}
});
-
- Map<DexType, EquivalenceGroup<T>> equivalences = new IdentityHashMap<>();
groupsPerPrefix.forEach(
(externalSyntheticTypePrefix, groups) -> {
// Sort the equivalence groups that go into 'context' including the context type of the
// representative which is equal to 'context' here (see assert below).
groups.sort(
(a, b) ->
- a.compareToIncludingContext(
- b, appView.graphLens(), classToFeatureSplitMap, synthetics));
+ a.compareToIncludingContext(b, appView.graphLens(), classToFeatureSplitMap));
for (int i = 0; i < groups.size(); i++) {
EquivalenceGroup<T> group = groups.get(i);
assert group
@@ -567,84 +538,114 @@
// of the synthetic name will be non-deterministic between the two.
assert i == 0
|| checkGroupsAreDistinct(
- groups.get(i - 1),
- group,
- appView.graphLens(),
- classToFeatureSplitMap,
- synthetics);
- SyntheticKind kind = group.members.get(0).getKind();
+ groups.get(i - 1), group, appView.graphLens(), classToFeatureSplitMap);
+ SyntheticKind kind = group.getRepresentative().getKind();
DexType representativeType =
- createExternalType(kind, externalSyntheticTypePrefix, generators, appView);
+ createExternalType(
+ kind,
+ externalSyntheticTypePrefix,
+ generators,
+ appView,
+ equivalences::containsKey);
equivalences.put(representativeType, group);
- for (T member : group.getMembers()) {
- lensBuilder.move(member.getHolder().getType(), representativeType);
- }
}
});
+ equivalences.forEach(
+ (representativeType, group) ->
+ group.forEach(
+ member -> lensBuilder.move(member.getHolder().getType(), representativeType)));
return equivalences;
}
- private static <T extends SyntheticDefinition<?, T, ?>> List<List<T>> groupEquivalent(
+ private static <T extends SyntheticDefinition<?, T, ?>> List<EquivalenceGroup<T>> groupEquivalent(
+ AppView<?> appView,
List<T> potentialEquivalence,
boolean intermediate,
- GraphLens graphLens,
- ClassToFeatureSplitMap classToFeatureSplitMap,
- SyntheticItems syntheticItems) {
- List<List<T>> groups = new ArrayList<>();
+ ClassToFeatureSplitMap classToFeatureSplitMap) {
+ List<EquivalenceGroup<T>> groups = new ArrayList<>();
// Each other member is in a shared group if it is actually equivalent to the first member.
for (T synthetic : potentialEquivalence) {
- boolean requireNewGroup = true;
- for (List<T> group : groups) {
+ boolean mustBeRepresentative = isPinned(appView, synthetic);
+ EquivalenceGroup<T> equivalenceGroup = null;
+ for (EquivalenceGroup<T> group : groups) {
if (synthetic.isEquivalentTo(
- group.get(0), intermediate, graphLens, classToFeatureSplitMap, syntheticItems)) {
- requireNewGroup = false;
- group.add(synthetic);
+ group.hasRepresentative()
+ ? group.getRepresentative()
+ : group.getFirstNonRepresentativeMember(),
+ intermediate,
+ appView.graphLens(),
+ classToFeatureSplitMap)) {
+ if (mustBeRepresentative && group.hasRepresentative()) {
+ // Check if the current synthetic is smaller than the group's representative. If so,
+ // then replace the representative, to ensure deterministic groups, and create a new
+ // singleton group containing the old representative. Otherwise, just add a singleton
+ // group containing the new synthetic.
+ T representative = group.getRepresentative();
+ if (representative
+ .toReference()
+ .getReference()
+ .compareTo(synthetic.toReference().getReference())
+ > 0) {
+ group.replaceAndRemoveRepresentative(synthetic);
+ synthetic = representative;
+ }
+ } else {
+ equivalenceGroup = group;
+ }
break;
}
}
- if (requireNewGroup) {
- List<T> newGroup = new ArrayList<>();
- newGroup.add(synthetic);
- groups.add(newGroup);
+ if (equivalenceGroup != null) {
+ equivalenceGroup.add(synthetic, mustBeRepresentative);
+ } else {
+ groups.add(new EquivalenceGroup<>(synthetic, mustBeRepresentative));
}
}
return groups;
}
+ /**
+ * In R8, keep rules may apply to synthetics from the input, if the input has been compiled using
+ * intermediate mode.
+ */
+ private static <D extends SyntheticDefinition<?, D, ?>> boolean isPinned(
+ AppView<?> appView, D definition) {
+ if (!appView.enableWholeProgramOptimizations()) {
+ return false;
+ }
+ if (!definition.getHolder().isProgramClass()) {
+ return true;
+ }
+ DexProgramClass holder = definition.getHolder().asProgramClass();
+ KeepInfoCollection keepInfo = appView.getKeepInfo();
+ if (keepInfo.getClassInfo(holder).isPinned()) {
+ return true;
+ }
+ for (DexEncodedMember<?, ?> member : holder.members()) {
+ if (keepInfo.getMemberInfo(member, holder).isPinned()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private static <T extends SyntheticDefinition<?, T, ?>> boolean checkGroupsAreDistinct(
EquivalenceGroup<T> g1,
EquivalenceGroup<T> g2,
GraphLens graphLens,
- ClassToFeatureSplitMap classToFeatureSplitMap,
- SyntheticItems syntheticItems) {
- int order = g1.compareToIncludingContext(g2, graphLens, classToFeatureSplitMap, syntheticItems);
+ ClassToFeatureSplitMap classToFeatureSplitMap) {
+ int order = g1.compareToIncludingContext(g2, graphLens, classToFeatureSplitMap);
assert order != 0;
- assert order
- != g2.compareToIncludingContext(g1, graphLens, classToFeatureSplitMap, syntheticItems);
+ assert order != g2.compareToIncludingContext(g1, graphLens, classToFeatureSplitMap);
return true;
}
- private static <T extends SyntheticDefinition<?, T, ?>> T findDeterministicRepresentative(
- List<T> members,
- GraphLens graphLens,
- ClassToFeatureSplitMap classToFeatureSplitMap,
- SyntheticItems syntheticItems) {
- // Pick a deterministic member as representative.
- T smallest = members.get(0);
- for (int i = 1; i < members.size(); i++) {
- T next = members.get(i);
- if (next.toReference().getReference().compareTo(smallest.toReference().getReference()) < 0) {
- smallest = next;
- }
- }
- return smallest;
- }
-
private DexType createExternalType(
SyntheticKind kind,
String externalSyntheticTypePrefix,
Map<String, NumberGenerator> generators,
- AppView<?> appView) {
+ AppView<?> appView,
+ Predicate<DexType> reserved) {
DexItemFactory factory = appView.dexItemFactory();
if (kind.isFixedSuffixSynthetic) {
return SyntheticNaming.createExternalType(kind, externalSyntheticTypePrefix, "", factory);
@@ -656,6 +657,12 @@
externalType =
SyntheticNaming.createExternalType(
kind, externalSyntheticTypePrefix, Integer.toString(generator.next()), factory);
+ // If the generated external type matches an external synthetic from the input, which is kept,
+ // then continue.
+ if (reserved.test(externalType)) {
+ externalType = null;
+ continue;
+ }
DexClass clazz = appView.appInfo().definitionForWithoutExistenceAssert(externalType);
if (clazz != null && isNotSyntheticType(clazz.type)) {
assert options.testing.allowConflictingSyntheticTypes
@@ -724,4 +731,101 @@
}
return definitions;
}
+
+ private static class EquivalenceGroup<T extends SyntheticDefinition<?, T, ?>> {
+
+ // The members of the equivalence group, *excluding* the representative.
+ private List<T> members = new ArrayList<>();
+ private T representative;
+
+ EquivalenceGroup(T member, boolean isRepresentative) {
+ add(member, isRepresentative);
+ }
+
+ void add(T member, boolean isRepresentative) {
+ if (isRepresentative) {
+ assert !hasRepresentative();
+ representative = member;
+ } else {
+ members.add(member);
+ }
+ }
+
+ int compareToIncludingContext(
+ EquivalenceGroup<T> other,
+ GraphLens graphLens,
+ ClassToFeatureSplitMap classToFeatureSplitMap) {
+ return getRepresentative()
+ .compareTo(other.getRepresentative(), true, graphLens, classToFeatureSplitMap);
+ }
+
+ public void forEach(Consumer<T> consumer) {
+ consumer.accept(getRepresentative());
+ members.forEach(consumer);
+ }
+
+ public void forEachNonRepresentativeMember(Consumer<T> consumer) {
+ members.forEach(consumer);
+ }
+
+ T getFirstNonRepresentativeMember() {
+ assert !members.isEmpty();
+ return members.get(0);
+ }
+
+ T getRepresentative() {
+ assert hasRepresentative();
+ return representative;
+ }
+
+ boolean hasRepresentative() {
+ return representative != null;
+ }
+
+ boolean isDerivedFromMainDexList(MainDexInfo mainDexInfo) {
+ return getRepresentative().getContext().isDerivedFromMainDexList(mainDexInfo)
+ || Iterables.any(
+ members, member -> member.getContext().isDerivedFromMainDexList(mainDexInfo));
+ }
+
+ void replaceAndRemoveRepresentative(T representative) {
+ assert hasRepresentative();
+ this.representative = representative;
+ }
+
+ void selectDeterministicRepresentative() {
+ // Pick a deterministic member as representative.
+ assert !hasRepresentative();
+ int representativeIndex = 0;
+ for (int i = 1; i < members.size(); i++) {
+ T next = members.get(i);
+ T representative = members.get(representativeIndex);
+ if (next.toReference().getReference().compareTo(representative.toReference().getReference())
+ < 0) {
+ representativeIndex = i;
+ }
+ }
+ T representative = members.get(representativeIndex);
+ members.set(representativeIndex, ListUtils.last(members));
+ ListUtils.removeLast(members);
+ setRepresentative(representative);
+ }
+
+ void setRepresentative(T representative) {
+ assert !hasRepresentative();
+ this.representative = representative;
+ }
+
+ @Override
+ public String toString() {
+ if (hasRepresentative()) {
+ return "EquivalenceGroup{ size = "
+ + (members.size() + 1)
+ + ", repr = "
+ + getRepresentative()
+ + " }";
+ }
+ return "EquivalenceGroup{ size = " + members.size() + " }";
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index befe594..f709533 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -249,7 +249,7 @@
public boolean isSubjectToKeepRules(DexProgramClass clazz) {
assert isSyntheticClass(clazz);
- return committed.containsSyntheticInput(clazz.getType());
+ return isSyntheticInput(clazz);
}
public boolean isSyntheticClass(DexType type) {
@@ -260,20 +260,37 @@
return isSyntheticClass(clazz.type);
}
+ boolean isSyntheticInput(DexProgramClass clazz) {
+ return committed.containsSyntheticInput(clazz.getType());
+ }
+
public FeatureSplit getContextualFeatureSplit(DexType type) {
+ if (pending.legacyClasses.containsKey(type)) {
+ LegacySyntheticDefinition definition = pending.legacyClasses.get(type);
+ return definition.getFeatureSplit();
+ }
+ if (committed.containsLegacyType(type)) {
+ List<LegacySyntheticReference> types = committed.getLegacyTypes(type);
+ if (types.isEmpty()) {
+ return null;
+ }
+ assert verifyAllHaveSameFeature(types, LegacySyntheticReference::getFeatureSplit);
+ return types.get(0).getFeatureSplit();
+ }
List<SynthesizingContext> contexts = getSynthesizingContexts(type);
if (contexts.isEmpty()) {
return null;
}
- assert verifyAllContextsHaveSameFeature(contexts);
+ assert verifyAllHaveSameFeature(contexts, SynthesizingContext::getFeatureSplit);
return contexts.get(0).getFeatureSplit();
}
- private boolean verifyAllContextsHaveSameFeature(List<SynthesizingContext> contexts) {
- assert !contexts.isEmpty();
- FeatureSplit featureSplit = contexts.get(0).getFeatureSplit();
- for (int i = 1; i < contexts.size(); i++) {
- assert featureSplit == contexts.get(i).getFeatureSplit();
+ private static <T> boolean verifyAllHaveSameFeature(
+ List<T> items, Function<T, FeatureSplit> getter) {
+ assert !items.isEmpty();
+ FeatureSplit featureSplit = getter.apply(items.get(0));
+ for (int i = 1; i < items.size(); i++) {
+ assert featureSplit == getter.apply(items.get(i));
}
return true;
}
@@ -379,9 +396,10 @@
}
// TODO(b/158159959): Remove the usage of this direct class addition.
- public void addLegacySyntheticClass(DexProgramClass clazz, ProgramDefinition context) {
+ public void addLegacySyntheticClass(
+ DexProgramClass clazz, ProgramDefinition context, FeatureSplit featureSplit) {
LegacySyntheticDefinition legacyItem = internalAddLegacySyntheticClass(clazz);
- legacyItem.addContext(context);
+ legacyItem.addContext(context, featureSplit);
}
private LegacySyntheticDefinition internalAddLegacySyntheticClass(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index e7c8e46..7b4976c 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -135,6 +135,12 @@
return mapOrElse(list, fn, list);
}
+ public static <T> ArrayList<T> newArrayList(T element) {
+ ArrayList<T> list = new ArrayList<>();
+ list.add(element);
+ return list;
+ }
+
public static <T> ArrayList<T> newArrayList(ForEachable<T> forEachable) {
ArrayList<T> list = new ArrayList<>();
forEachable.forEach(list::add);
@@ -155,6 +161,10 @@
return Optional.empty();
}
+ public static <T> T removeLast(List<T> list) {
+ return list.remove(list.size() - 1);
+ }
+
public static <T extends Comparable<T>> boolean verifyListIsOrdered(List<T> list) {
for (int i = list.size() - 1; i > 0; i--) {
if (list.get(i).compareTo(list.get(i - 1)) < 0) {
diff --git a/src/main/java/com/android/tools/r8/utils/SetUtils.java b/src/main/java/com/android/tools/r8/utils/SetUtils.java
index a278196..98ea2c4 100644
--- a/src/main/java/com/android/tools/r8/utils/SetUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/SetUtils.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.IdentityHashMap;
@@ -55,6 +56,12 @@
return result;
}
+ public static <T> ImmutableSet<T> newImmutableSet(ForEachable<T> forEachable) {
+ ImmutableSet.Builder<T> builder = ImmutableSet.builder();
+ forEachable.forEach(builder::add);
+ return builder.build();
+ }
+
public static <T, S> Set<T> mapIdentityHashSet(Set<S> set, Function<S, T> fn) {
Set<T> out = newIdentityHashSet(set.size());
for (S element : set) {
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 97a95be8..121e7c4 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -65,6 +65,7 @@
private MainDexClassesCollector mainDexClassesCollector;
private StringConsumer mainDexListConsumer;
protected int minApiLevel = ToolHelper.getMinApiLevelForDexVm().getLevel();
+ private boolean optimizeMultidexForLinearAlloc = false;
private Consumer<InternalOptions> optionsConsumer = DEFAULT_OPTIONS;
private ByteArrayOutputStream stdout = null;
private PrintStream oldStdout = null;
@@ -167,6 +168,7 @@
: "Don't set the API level directly through BaseCompilerCommand.Builder in tests";
builder.setMinApiLevel(minApiLevel);
}
+ builder.setOptimizeMultidexForLinearAlloc(optimizeMultidexForLinearAlloc);
if (useDefaultRuntimeLibrary) {
if (backend == Backend.DEX) {
assert builder.isMinApiLevelSet();
@@ -308,6 +310,11 @@
return self();
}
+ public T setOptimizeMultidexForLinearAlloc() {
+ this.optimizeMultidexForLinearAlloc = true;
+ return self();
+ }
+
public T disableDesugaring() {
builder.setDisableDesugaring(true);
return self();
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupTest.java
new file mode 100644
index 0000000..ae64e0e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupTest.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2021, 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.interfaces;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+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.NoUnusedInterfaceRemoval;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class CollisionWithDefaultMethodOutsideMergeGroupTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public CollisionWithDefaultMethodOutsideMergeGroupTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ // I and J are not eligible for merging, since class A (implements I) inherits a default m()
+ // method from K, which is also on J.
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNoUnusedInterfaceRemovalAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
+ assertThat(aClassSubject, isImplementing(inspector.clazz(K.class)));
+
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+ assertThat(bClassSubject, isImplementing(inspector.clazz(J.class)));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("K", "J");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A().m();
+ new B().m();
+ }
+ }
+
+ @NoUnusedInterfaceRemoval
+ @NoVerticalClassMerging
+ interface I {}
+
+ @NoUnusedInterfaceRemoval
+ @NoVerticalClassMerging
+ interface J {
+ @NeverInline
+ default void m() {
+ System.out.println("J");
+ }
+ }
+
+ @NoHorizontalClassMerging
+ @NoUnusedInterfaceRemoval
+ @NoVerticalClassMerging
+ interface K {
+ @NeverInline
+ default void m() {
+ System.out.println("K");
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class A implements I, K {}
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class B implements J {}
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesMergingTest.java
new file mode 100644
index 0000000..0fd3ab3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesMergingTest.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2021, 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.interfaces;
+
+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;
+
+@RunWith(Parameterized.class)
+public class DisjointFunctionalInterfacesMergingTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public DisjointFunctionalInterfacesMergingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector -> inspector.assertClassesNotMerged(I.class, J.class))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("I", "J");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ ((I) () -> System.out.println("I")).f();
+ ((J) () -> System.out.println("J")).g();
+ }
+ }
+
+ interface I {
+ void f();
+ }
+
+ interface J {
+ void g();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java
new file mode 100644
index 0000000..c9a0d19
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java
@@ -0,0 +1,91 @@
+// Copyright (c) 2021, 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.interfaces;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoUnusedInterfaceRemoval;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DisjointInterfacesWithDefaultMethodsMergingTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public DisjointInterfacesWithDefaultMethodsMergingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ // TODO(b/173990042): I and J should be merged.
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoUnusedInterfaceRemovalAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
+ assertThat(aClassSubject, isImplementing(inspector.clazz(J.class)));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("I", "J");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A().f();
+ new A().g();
+ }
+ }
+
+ @NoUnusedInterfaceRemoval
+ @NoVerticalClassMerging
+ interface I {
+ @NeverInline
+ default void f() {
+ System.out.println("I");
+ }
+ }
+
+ @NoUnusedInterfaceRemoval
+ @NoVerticalClassMerging
+ interface J {
+ @NeverInline
+ default void g() {
+ System.out.println("J");
+ }
+ }
+
+ @NeverClassInline
+ static class A implements I, J {}
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithoutDefaultMethodsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithoutDefaultMethodsMergingTest.java
new file mode 100644
index 0000000..9a9ccc3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithoutDefaultMethodsMergingTest.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2021, 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.interfaces;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoUnusedInterfaceRemoval;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DisjointInterfacesWithoutDefaultMethodsMergingTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public DisjointInterfacesWithoutDefaultMethodsMergingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ // TODO(b/173990042): I and J should be merged.
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoUnusedInterfaceRemovalAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
+ assertThat(aClassSubject, isImplementing(inspector.clazz(J.class)));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("I", "J");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A().f();
+ new A().g();
+ }
+ }
+
+ @NoUnusedInterfaceRemoval
+ @NoVerticalClassMerging
+ interface I {
+ void f();
+ }
+
+ @NoUnusedInterfaceRemoval
+ @NoVerticalClassMerging
+ interface J {
+ void g();
+ }
+
+ @NeverClassInline
+ static class A implements I, J {
+ @NeverInline
+ @Override
+ public void f() {
+ System.out.println("I");
+ }
+
+ @NeverInline
+ @Override
+ public void g() {
+ System.out.println("J");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfacesMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfacesMergingTest.java
new file mode 100644
index 0000000..74aa943
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfacesMergingTest.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2021, 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.interfaces;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NoUnusedInterfaceRemoval;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class EmptyInterfacesMergingTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public EmptyInterfacesMergingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ // TODO(b/173990042): I and J should be merged.
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .enableNoUnusedInterfaceRemovalAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
+ assertThat(aClassSubject, isImplementing(inspector.clazz(J.class)));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccess();
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(A.class);
+ }
+ }
+
+ @NoUnusedInterfaceRemoval
+ @NoVerticalClassMerging
+ interface I {}
+
+ @NoUnusedInterfaceRemoval
+ @NoVerticalClassMerging
+ interface J {}
+
+ static class A implements I, J {}
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java
new file mode 100644
index 0000000..6a1b373
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2021, 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.interfaces;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+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.NoUnusedInterfaceRemoval;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class NoDefaultMethodMergingTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public NoDefaultMethodMergingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ // I and J are not eligible for merging, since they declare the same default method.
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNoUnusedInterfaceRemovalAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
+
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+ assertThat(bClassSubject, isImplementing(inspector.clazz(J.class)));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("I", "J");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A().m();
+ new B().m();
+ }
+ }
+
+ @NoUnusedInterfaceRemoval
+ @NoVerticalClassMerging
+ interface I {
+ @NeverInline
+ default void m() {
+ System.out.println("I");
+ }
+ }
+
+ @NoUnusedInterfaceRemoval
+ @NoVerticalClassMerging
+ interface J {
+ @NeverInline
+ default void m() {
+ System.out.println("J");
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class A implements I {}
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class B implements J {}
+}
diff --git a/src/test/java/com/android/tools/r8/internal/D8GMSCoreV10DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8GMSCoreV10DeployJarVerificationTest.java
deleted file mode 100644
index 7f685c5..0000000
--- a/src/test/java/com/android/tools/r8/internal/D8GMSCoreV10DeployJarVerificationTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (c) 2017, 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.internal;
-
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.D8Command;
-import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.AndroidApp;
-import java.nio.file.Paths;
-import org.junit.Test;
-
-public class D8GMSCoreV10DeployJarVerificationTest extends GMSCoreDeployJarVerificationTest {
-
- @Test
- public void buildDebugFromDeployJar() throws Exception {
- buildFromDeployJar(
- CompilerUnderTest.D8, CompilationMode.DEBUG,
- GMSCoreCompilationTestBase.GMSCORE_V10_DIR, false);
- }
-
- @Test
- public void buildReleaseFromDeployJar() throws Exception {
- buildFromDeployJar(
- CompilerUnderTest.D8, CompilationMode.RELEASE,
- GMSCoreCompilationTestBase.GMSCORE_V10_DIR, false);
- }
-
- @Test
- public void testDeterminismDebugLegacyMultidexFromDeployJar() throws Exception {
- D8Command.Builder builder =
- D8Command.builder()
- .addProgramFiles(Paths.get(GMSCORE_V10_DIR + DEPLOY_JAR))
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.K.getLevel()))
- .setMode(CompilationMode.DEBUG)
- .setMinApiLevel(AndroidApiLevel.K.getLevel())
- .addMainDexListFiles(Paths.get(GMSCORE_V10_DIR + "main_dex_list.txt"));
-
- AndroidApp app1 = runAndCheckVerification(builder, null);
- D8Command.Builder builder2 =
- D8Command.builder()
- .addProgramFiles(Paths.get(GMSCORE_V10_DIR + DEPLOY_JAR))
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.K.getLevel()))
- .setMode(CompilationMode.DEBUG)
- .setMinApiLevel(AndroidApiLevel.K.getLevel())
- .addMainDexListFiles(Paths.get(GMSCORE_V10_DIR + "main_dex_list.txt"));
-
- AndroidApp app2 = runAndCheckVerification(builder2, null);
- // Verify that the result of the two compilations was the same.
- assertIdenticalApplications(app1, app2);
- }
-
- @Test
- public void buildDebugLegagyMultidexForDexOptFromDeployJar() throws Exception {
- D8Command.Builder builder =
- D8Command.builder()
- .addProgramFiles(Paths.get(GMSCORE_V10_DIR + DEPLOY_JAR))
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.K.getLevel()))
- .setMode(CompilationMode.DEBUG)
- .setMinApiLevel(AndroidApiLevel.K.getLevel())
- .setOptimizeMultidexForLinearAlloc(true)
- .addMainDexListFiles(Paths.get(GMSCORE_V10_DIR + "main_dex_list.txt"));
-
- runAndCheckVerification(builder, null);
- }
-
- @Test
- public void buildReleaseLegagyMultidexFromDeployJar() throws Exception {
- D8Command.Builder builder =
- D8Command.builder()
- .addProgramFiles(Paths.get(GMSCORE_V10_DIR + DEPLOY_JAR))
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.K.getLevel()))
- .setMode(CompilationMode.DEBUG)
- .setMinApiLevel(AndroidApiLevel.K.getLevel())
- .addMainDexListFiles(Paths.get(GMSCORE_V10_DIR + "main_dex_list.txt"));
-
- runAndCheckVerification(builder, null);
- }
-
- @Test
- public void buildReleaseLegagyMultidexForDexOptFromDeployJar() throws Exception {
- D8Command.Builder builder =
- D8Command.builder()
- .addProgramFiles(Paths.get(GMSCORE_V10_DIR + DEPLOY_JAR))
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.K.getLevel()))
- .setMode(CompilationMode.DEBUG)
- .setMinApiLevel(AndroidApiLevel.K.getLevel())
- .setOptimizeMultidexForLinearAlloc(true)
- .addMainDexListFiles(Paths.get(GMSCORE_V10_DIR + "main_dex_list.txt"));
-
- runAndCheckVerification(builder, null);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/internal/D8GMSCoreV9DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8GMSCoreV9DeployJarVerificationTest.java
deleted file mode 100644
index 25f4412..0000000
--- a/src/test/java/com/android/tools/r8/internal/D8GMSCoreV9DeployJarVerificationTest.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2017, 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.internal;
-
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
-import org.junit.Test;
-
-public class D8GMSCoreV9DeployJarVerificationTest extends GMSCoreDeployJarVerificationTest {
-
- @Test
- public void buildDebugFromDeployJar() throws Exception {
- buildFromDeployJar(
- CompilerUnderTest.D8, CompilationMode.DEBUG,
- GMSCoreCompilationTestBase.GMSCORE_V9_DIR, true);
- }
-
- @Test
- public void buildReleaseFromDeployJar() throws Exception {
- buildFromDeployJar(
- CompilerUnderTest.D8, CompilationMode.RELEASE,
- GMSCoreCompilationTestBase.GMSCORE_V9_DIR, true);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java
index de9b402..4ed740b 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java
@@ -3,61 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.internal;
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
-import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableList;
-import java.io.IOException;
-import java.nio.file.Paths;
-import java.util.concurrent.ExecutionException;
-import java.util.function.Consumer;
-
public abstract class GMSCoreCompilationTestBase extends CompilationTestBase {
- public static final String GMSCORE_V4_DIR = "third_party/gmscore/v4/";
- public static final String GMSCORE_V5_DIR = "third_party/gmscore/v5/";
- public static final String GMSCORE_V6_DIR = "third_party/gmscore/v6/";
- public static final String GMSCORE_V7_DIR = "third_party/gmscore/v7/";
- public static final String GMSCORE_V8_DIR = "third_party/gmscore/v8/";
- public static final String GMSCORE_V9_DIR = "third_party/gmscore/gmscore_v9/";
- public static final String GMSCORE_V10_DIR = "third_party/gmscore/gmscore_v10/";
- public static final String GMSCORE_LATEST_DIR = "third_party/gmscore/latest/";
-
- public static final int GMSCORE_V9_MAX_SIZE = 35000000;
- public static final int GMSCORE_V10_MAX_SIZE = 35000000;
- public static final int GMSCORE_LATEST_MAX_SIZE = 35000000;
-
- static final String GMSCORE_APK = "GMSCore.apk";
-
// Files pertaining to the full GMSCore build.
- static final String PG_MAP = "GmsCore_prod_alldpi_release_all_locales_proguard.map";
static final String PG_CONF = "GmsCore_prod_alldpi_release_all_locales_proguard.config";
static final String DEPLOY_JAR = "GmsCore_prod_alldpi_release_all_locales_deploy.jar";
- static final String REFERENCE_APK = "noshrink_x86_GmsCore_prod_alldpi_release_unsigned.apk";
-
- public void runR8AndCheckVerification(CompilationMode mode, String version)
- throws ExecutionException, IOException, CompilationFailedException {
- runR8AndCheckVerification(mode, version, null);
- }
-
- public void runR8AndCheckVerification(
- CompilationMode mode, String version, Consumer<InternalOptions> optionsConsumer)
- throws ExecutionException, IOException, CompilationFailedException {
- runAndCheckVerification(CompilerUnderTest.R8, mode, version, optionsConsumer);
- }
-
- private void runAndCheckVerification(
- CompilerUnderTest compiler,
- CompilationMode mode,
- String version,
- Consumer<InternalOptions> optionsConsumer)
- throws ExecutionException, IOException, CompilationFailedException {
- runAndCheckVerification(
- compiler,
- mode,
- version + GMSCORE_APK,
- null,
- optionsConsumer,
- ImmutableList.of(Paths.get(version, GMSCORE_APK).toString()));
- }
}
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
deleted file mode 100644
index 634ff6f..0000000
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2016, 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.internal;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.InternalOptions;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.concurrent.ExecutionException;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-
-public class GMSCoreDeployJarVerificationTest extends GMSCoreCompilationTestBase {
-
- public AndroidApp buildFromDeployJar(
- CompilerUnderTest compiler, CompilationMode mode, String base, boolean hasReference)
- throws ExecutionException, IOException, ProguardRuleParserException,
- CompilationFailedException {
- return runAndCheckVerification(
- compiler, mode, hasReference ? base + REFERENCE_APK : null, null, base + DEPLOY_JAR);
- }
-
-
- public AndroidApp buildFromDeployJar(
- CompilerUnderTest compiler, CompilationMode mode, String base, boolean hasReference,
- Consumer<InternalOptions> optionsConsumer)
- throws ExecutionException, IOException, ProguardRuleParserException,
- CompilationFailedException {
- return runAndCheckVerification(
- compiler,
- mode,
- hasReference ? base + REFERENCE_APK : null,
- null,
- optionsConsumer,
- Collections.singletonList(base + DEPLOY_JAR));
- }
-
- public AndroidApp buildFromDeployJar(
- CompilerUnderTest compiler, CompilationMode mode, String base, boolean hasReference,
- Consumer<InternalOptions> optionsConsumer, Supplier<DexIndexedConsumer> consumerSupplier)
- throws ExecutionException, IOException, ProguardRuleParserException,
- CompilationFailedException {
- return runAndCheckVerification(
- compiler,
- mode,
- hasReference ? base + REFERENCE_APK : null,
- null,
- optionsConsumer,
- consumerSupplier,
- Collections.singletonList(base + DEPLOY_JAR));
- }
-
-}
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java b/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
new file mode 100644
index 0000000..3beab34
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
@@ -0,0 +1,133 @@
+// 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.
+package com.android.tools.r8.internal;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.core.AnyOf.anyOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AssertionUtils;
+import com.google.common.collect.Sets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class GMSCoreLatestTest extends GMSCoreCompilationTestBase {
+
+ private static final Path base = Paths.get("third_party/gmscore/latest/");
+
+ private static Path sanitizedLibrary;
+ private static Path sanitizedProguardConfiguration;
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntime(Version.V9_0_0)
+ .withApiLevel(AndroidApiLevel.L)
+ .build();
+ }
+
+ public GMSCoreLatestTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ LibrarySanitizer librarySanitizer =
+ new LibrarySanitizer(getStaticTemp())
+ .addProguardConfigurationFiles(base.resolve(PG_CONF))
+ .sanitize();
+ sanitizedLibrary = librarySanitizer.getSanitizedLibrary();
+ sanitizedProguardConfiguration = librarySanitizer.getSanitizedProguardConfiguration();
+ }
+
+ @Test
+ public void testR8Determinism() throws Exception {
+ Map<String, String> idsRoundOne = new ConcurrentHashMap<>();
+ R8TestCompileResult compileResult =
+ compileWithR8(
+ builder ->
+ builder.addOptionsModification(
+ options ->
+ options.testing.processingContextsConsumer =
+ id -> assertNull(idsRoundOne.put(id, id))));
+
+ compileResult.runDex2Oat(parameters.getRuntime()).assertNoVerificationErrors();
+
+ Map<String, String> idsRoundTwo = new ConcurrentHashMap<>();
+ R8TestCompileResult otherCompileResult =
+ compileWithR8(
+ builder ->
+ builder.addOptionsModification(
+ options ->
+ options.testing.processingContextsConsumer =
+ id -> {
+ AssertionUtils.assertNotNull(idsRoundOne.get(id));
+ assertNull(idsRoundTwo.put(id, id));
+ }));
+
+ // Verify that the result of the two compilations was the same.
+ assertEquals(
+ Collections.emptySet(),
+ Sets.symmetricDifference(idsRoundOne.keySet(), idsRoundTwo.keySet()));
+ assertIdenticalApplications(compileResult.getApp(), otherCompileResult.getApp());
+ assertEquals(compileResult.getProguardMap(), otherCompileResult.getProguardMap());
+ }
+
+ private R8TestCompileResult compileWithR8(ThrowableConsumer<R8FullTestBuilder> configuration)
+ throws Exception {
+ // Program files are included in Proguard configuration.
+ return testForR8(Backend.DEX)
+ .addLibraryFiles(sanitizedLibrary)
+ .addKeepRuleFiles(sanitizedProguardConfiguration)
+ .addDontWarn(
+ "android.hardware.location.IActivityRecognitionHardware",
+ "android.hardware.location.IFusedLocationHardware",
+ "android.location.FusedBatchOptions",
+ "android.location.GeocoderParams$1",
+ "android.location.ILocationManager",
+ "android.media.IRemoteDisplayCallback",
+ "android.media.RemoteDisplayState$RemoteDisplayInfo",
+ "com.android.internal.location.ProviderProperties",
+ "com.android.internal.location.ProviderRequest",
+ "com.google.protobuf.java_com_google_android_libraries_performance_primes_release"
+ + "_gmscore__primes_bcdd2915GeneratedExtensionRegistryLite$Loader")
+ .allowDiagnosticMessages()
+ .allowUnusedProguardConfigurationRules()
+ .setMinApi(parameters.getApiLevel())
+ .apply(configuration)
+ .compile()
+ .assertAllInfoMessagesMatch(
+ anyOf(
+ containsString("Ignoring option: -optimizations"),
+ containsString(
+ "Invalid parameter counts in MethodParameter attributes. "
+ + "This is likely due to Proguard having removed a parameter."),
+ containsString("Methods with invalid MethodParameter attributes:"),
+ containsString("Proguard configuration rule does not match anything")))
+ .assertAllWarningMessagesMatch(
+ anyOf(
+ containsString(
+ "Expected stack map table for method with non-linear control flow. "
+ + "In later version of R8, the method may be assumed not reachable."),
+ containsString("Ignoring option: -outjars")));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java b/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
new file mode 100644
index 0000000..30716fb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
@@ -0,0 +1,212 @@
+// Copyright (c) 2017, 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.internal;
+
+import static com.android.tools.r8.utils.AssertionUtils.assertNotNull;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.core.AnyOf.anyOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.D8TestBuilder;
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.Sets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class GMSCoreV10Test extends GMSCoreCompilationTestBase {
+
+ private static final Path base = Paths.get("third_party/gmscore/gmscore_v10/");
+
+ private static Path sanitizedLibrary;
+ private static Path sanitizedProguardConfiguration;
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntime(Version.V9_0_0)
+ .withApiLevel(AndroidApiLevel.L)
+ .build();
+ }
+
+ public GMSCoreV10Test(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ LibrarySanitizer librarySanitizer =
+ new LibrarySanitizer(getStaticTemp())
+ .addProguardConfigurationFiles(base.resolve(PG_CONF))
+ .sanitize();
+ sanitizedLibrary = librarySanitizer.getSanitizedLibrary();
+ sanitizedProguardConfiguration = librarySanitizer.getSanitizedProguardConfiguration();
+ }
+
+ @Test
+ public void testR8Determinism() throws Exception {
+ Map<String, String> idsRoundOne = new ConcurrentHashMap<>();
+ R8TestCompileResult compileResult =
+ compileWithR8(
+ builder ->
+ builder.addOptionsModification(
+ options ->
+ options.testing.processingContextsConsumer =
+ id -> assertNull(idsRoundOne.put(id, id))));
+
+ compileResult.runDex2Oat(parameters.getRuntime()).assertNoVerificationErrors();
+
+ Map<String, String> idsRoundTwo = new ConcurrentHashMap<>();
+ R8TestCompileResult otherCompileResult =
+ compileWithR8(
+ builder ->
+ builder.addOptionsModification(
+ options ->
+ options.testing.processingContextsConsumer =
+ id -> {
+ assertNotNull(idsRoundOne.get(id));
+ assertNull(idsRoundTwo.put(id, id));
+ }));
+
+ // Verify that the result of the two compilations was the same.
+ assertEquals(
+ Collections.emptySet(),
+ Sets.symmetricDifference(idsRoundOne.keySet(), idsRoundTwo.keySet()));
+ assertIdenticalApplications(compileResult.getApp(), otherCompileResult.getApp());
+ assertEquals(compileResult.getProguardMap(), otherCompileResult.getProguardMap());
+ }
+
+ @Test
+ public void testR8ForceJumboStringProcessing() throws Exception {
+ compileWithR8(
+ builder ->
+ builder.addOptionsModification(
+ options -> options.testing.forceJumboStringProcessing = true))
+ .runDex2Oat(parameters.getRuntime())
+ .assertNoVerificationErrors();
+ }
+
+ @Test
+ public void testD8Debug() throws Exception {
+ compileWithD8Debug(ThrowableConsumer.empty());
+ }
+
+ @Test
+ public void testD8DebugLegacyMultidex() throws Exception {
+ compileWithD8Debug(
+ builder ->
+ builder
+ .addMainDexListFiles(base.resolve("main_dex_list.txt"))
+ .setMinApi(AndroidApiLevel.K))
+ .runDex2Oat(parameters.getRuntime())
+ .assertNoVerificationErrors();
+ }
+
+ @Test
+ public void testD8DebugLegacyMultidexDexOpt() throws Exception {
+ compileWithD8Debug(
+ builder ->
+ builder
+ .addMainDexListFiles(base.resolve("main_dex_list.txt"))
+ .setMinApi(AndroidApiLevel.K)
+ .setOptimizeMultidexForLinearAlloc())
+ .runDex2Oat(parameters.getRuntime())
+ .assertNoVerificationErrors();
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ compileWithD8Release(ThrowableConsumer.empty())
+ .runDex2Oat(parameters.getRuntime())
+ .assertNoVerificationErrors();
+ }
+
+ @Test
+ public void testD8ReleaseLegacyMultidex() throws Exception {
+ compileWithD8Release(
+ builder ->
+ builder
+ .addMainDexListFiles(base.resolve("main_dex_list.txt"))
+ .setMinApi(AndroidApiLevel.K))
+ .runDex2Oat(parameters.getRuntime())
+ .assertNoVerificationErrors();
+ }
+
+ @Test
+ public void buildD8ReleaseLegacyMultidexDexOpt() throws Exception {
+ compileWithD8Release(
+ builder ->
+ builder
+ .addMainDexListFiles(base.resolve("main_dex_list.txt"))
+ .setMinApi(AndroidApiLevel.K)
+ .setOptimizeMultidexForLinearAlloc())
+ .runDex2Oat(parameters.getRuntime())
+ .assertNoVerificationErrors();
+ }
+
+ private D8TestCompileResult compileWithD8Debug(ThrowableConsumer<D8TestBuilder> configuration)
+ throws Exception {
+ return compileWithD8(configuration.andThen(TestCompilerBuilder::debug));
+ }
+
+ private D8TestCompileResult compileWithD8Release(ThrowableConsumer<D8TestBuilder> configuration)
+ throws Exception {
+ return compileWithD8(configuration.andThen(TestCompilerBuilder::release));
+ }
+
+ private D8TestCompileResult compileWithD8(ThrowableConsumer<D8TestBuilder> configuration)
+ throws Exception {
+ return testForD8()
+ .addProgramFiles(base.resolve(DEPLOY_JAR))
+ .setMinApi(AndroidApiLevel.L)
+ .apply(configuration)
+ .compile();
+ }
+
+ private R8TestCompileResult compileWithR8(ThrowableConsumer<R8FullTestBuilder> configuration)
+ throws Exception {
+ // Program files are included in Proguard configuration.
+ return testForR8(Backend.DEX)
+ .addLibraryFiles(sanitizedLibrary)
+ .addKeepRuleFiles(sanitizedProguardConfiguration)
+ .addDontWarn(
+ "android.hardware.location.IActivityRecognitionHardware",
+ "android.hardware.location.IFusedLocationHardware",
+ "android.location.FusedBatchOptions",
+ "android.location.GeocoderParams$1",
+ "android.location.ILocationManager",
+ "android.media.IRemoteDisplayCallback",
+ "android.media.RemoteDisplayState$RemoteDisplayInfo",
+ "com.android.internal.location.ProviderProperties",
+ "com.android.internal.location.ProviderRequest")
+ .allowDiagnosticMessages()
+ .allowUnusedProguardConfigurationRules()
+ .setMinApi(parameters.getApiLevel())
+ .apply(configuration)
+ .compile()
+ .assertAllInfoMessagesMatch(
+ anyOf(
+ containsString("Ignoring option: -optimizations"),
+ containsString("Proguard configuration rule does not match anything")))
+ .assertAllWarningMessagesMatch(containsString("Ignoring option: -outjars"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/LibrarySanitizer.java b/src/test/java/com/android/tools/r8/internal/LibrarySanitizer.java
index 4a256bb..e43dd8e 100644
--- a/src/test/java/com/android/tools/r8/internal/LibrarySanitizer.java
+++ b/src/test/java/com/android/tools/r8/internal/LibrarySanitizer.java
@@ -14,6 +14,7 @@
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import org.junit.rules.TemporaryFolder;
@@ -56,6 +57,10 @@
return this;
}
+ LibrarySanitizer addProguardConfigurationFiles(Path... proguardConfigurationFiles) {
+ return addProguardConfigurationFiles(Arrays.asList(proguardConfigurationFiles));
+ }
+
public Path getSanitizedLibrary() {
return sanitizedLibrary;
}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
deleted file mode 100644
index cba792d..0000000
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (c) 2016, 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.internal;
-
-import static junit.framework.TestCase.assertEquals;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.R8Command;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.IROrdering.NondeterministicIROrdering;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import org.junit.Test;
-
-public class R8GMSCoreDeterministicTest extends GMSCoreCompilationTestBase {
-
- private static class CompilationResult {
- AndroidApp app;
- String proguardMap;
- }
-
- private CompilationResult doRun() throws CompilationFailedException {
- R8Command command =
- R8Command.builder()
- .setDisableTreeShaking(true)
- .setDisableMinification(true)
- .addProgramFiles(Paths.get(GMSCORE_V7_DIR, GMSCORE_APK))
- .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
- .setMinApiLevel(AndroidApiLevel.L.getLevel())
- .build();
- CompilationResult result = new CompilationResult();
- result.app =
- ToolHelper.runR8(
- command,
- options -> {
- // For this test just do random shuffle.
- options.testing.irOrdering = NondeterministicIROrdering.getInstance();
- // Only use one thread to process to process in the order decided by the callback.
- options.threadCount = 1;
- // Ignore the missing classes.
- options.ignoreMissingClasses = true;
- // Store the generated Proguard map.
- options.proguardMapConsumer =
- ToolHelper.consumeString(proguardMap -> result.proguardMap = proguardMap);
- });
- return result;
- }
-
- @Test
- public void deterministic() throws Exception {
- // Run two independent compilations.
- CompilationResult result1 = doRun();
- CompilationResult result2 = doRun();
-
- // Check that the generated bytecode runs through the dex2oat verifier with no errors.
- Path combinedInput = temp.getRoot().toPath().resolve("all.jar");
- Path oatFile = temp.getRoot().toPath().resolve("all.oat");
- result1.app.writeToZip(combinedInput, OutputMode.DexIndexed);
- ToolHelper.runDex2Oat(combinedInput, oatFile);
-
- // Verify that the result of the two compilations was the same.
- assertIdenticalApplications(result1.app, result2.app);
- assertEquals(result1.proguardMap, result2.proguardMap);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java
deleted file mode 100644
index 67979ba..0000000
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (c) 2016, 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.internal;
-
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.InternalOptions;
-import java.nio.file.Paths;
-import org.junit.Test;
-
-public class R8GMSCoreFixedPointTest extends GMSCoreCompilationTestBase {
-
- @Test
- public void fixedPoint() throws Exception {
- // First compilation.
- AndroidApp app = ToolHelper.builderFromProgramDirectory(Paths.get(GMSCORE_V7_DIR)).build();
-
- AndroidApp app1 =
- ToolHelper.runR8(
- ToolHelper.prepareR8CommandBuilder(app)
- .setMode(CompilationMode.DEBUG)
- .setMinApiLevel(AndroidApiLevel.L.getLevel())
- .build(),
- this::configure);
-
- // Second compilation.
- // Add option --skip-outline-opt for second compilation. The second compilation can find
- // additional outlining opportunities as member rebinding from the first compilation can move
- // methods.
- // See b/33410508 and b/33475705.
- AndroidApp app2 =
- ToolHelper.runR8(
- ToolHelper.prepareR8CommandBuilder(app1)
- .setMode(CompilationMode.DEBUG)
- .setMinApiLevel(AndroidApiLevel.L.getLevel())
- .build(),
- this::configure);
-
- assertIdenticalApplicationsUpToCode(app1, app2, false);
- }
-
- private void configure(InternalOptions options) {
- options.ignoreMissingClasses = true;
- }
-}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLatestTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLatestTreeShakeJarVerificationTest.java
deleted file mode 100644
index d4e1e43..0000000
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLatestTreeShakeJarVerificationTest.java
+++ /dev/null
@@ -1,71 +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.
-package com.android.tools.r8.internal;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.AndroidApp;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import org.junit.Test;
-
-public class R8GMSCoreLatestTreeShakeJarVerificationTest
- extends R8GMSCoreTreeShakeJarVerificationTest {
-
- private String proguardMap1 = null;
- private String proguardMap2 = null;
-
- @Test
- public void buildAndTreeShakeFromDeployJar() throws Exception {
- List<String> additionalProguardConfiguration =
- ImmutableList.of(
- ToolHelper.PROGUARD_SETTINGS_FOR_INTERNAL_APPS + "GmsCore_proguard.config");
- Map<String, String> idsRoundOne = new ConcurrentHashMap<>();
- AndroidApp app1 =
- buildAndTreeShakeFromDeployJar(
- CompilationMode.RELEASE,
- GMSCORE_LATEST_DIR,
- false,
- GMSCORE_LATEST_MAX_SIZE,
- additionalProguardConfiguration,
- options -> {
- options.testing.processingContextsConsumer =
- id -> assertNull(idsRoundOne.put(id, id));
- options.proguardMapConsumer =
- ToolHelper.consumeString(proguardMap -> this.proguardMap1 = proguardMap);
- });
- Map<String, String> idsRoundTwo = new ConcurrentHashMap<>();
- AndroidApp app2 =
- buildAndTreeShakeFromDeployJar(
- CompilationMode.RELEASE,
- GMSCORE_LATEST_DIR,
- false,
- GMSCORE_LATEST_MAX_SIZE,
- additionalProguardConfiguration,
- options -> {
- options.testing.processingContextsConsumer =
- id -> {
- assertNotNull(idsRoundOne.get(id));
- assertNull(idsRoundTwo.put(id, id));
- };
- options.proguardMapConsumer =
- ToolHelper.consumeString(proguardMap -> this.proguardMap2 = proguardMap);
- });
-
- // Verify that the result of the two compilations was the same.
- assertEquals(
- Collections.emptySet(),
- Sets.symmetricDifference(idsRoundOne.keySet(), idsRoundTwo.keySet()));
- assertIdenticalApplications(app1, app2);
- assertEquals(proguardMap1, proguardMap2);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
deleted file mode 100644
index c577d60..0000000
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) 2016, 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.internal;
-
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-
-public class R8GMSCoreTreeShakeJarVerificationTest extends GMSCoreCompilationTestBase {
-
- public AndroidApp buildAndTreeShakeFromDeployJar(
- CompilationMode mode,
- String base,
- boolean hasReference,
- int maxSize,
- Consumer<InternalOptions> optionsConsumer)
- throws Exception {
- return buildAndTreeShakeFromDeployJar(
- mode, base, hasReference, maxSize, ImmutableList.of(), optionsConsumer);
- }
-
- public AndroidApp buildAndTreeShakeFromDeployJar(
- CompilationMode mode,
- String base,
- boolean hasReference,
- int maxSize,
- List<String> additionalProguardConfigurations,
- Consumer<InternalOptions> optionsConsumer)
- throws Exception {
- List<String> proguardConfigurations = new ArrayList<>(additionalProguardConfigurations);
- proguardConfigurations.add(base + PG_CONF);
- AndroidApp app =
- runAndCheckVerification(
- CompilerUnderTest.R8,
- mode,
- hasReference ? base + REFERENCE_APK : null,
- proguardConfigurations,
- optionsConsumer,
- // Don't pass any inputs. The input will be read from the -injars in the Proguard
- // configuration file.
- ImmutableList.of());
- int bytes = app.applicationSize();
- assertTrue("Expected max size of " + maxSize + ", got " + bytes, bytes < maxSize);
- return app;
- }
-}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
deleted file mode 100644
index 2c2adbf..0000000
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) 2017, 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.internal;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
-import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.AndroidApp;
-import com.google.common.collect.Sets;
-import java.io.File;
-import java.util.Collections;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import org.junit.Test;
-
-public class R8GMSCoreV10DeployJarVerificationTest extends GMSCoreDeployJarVerificationTest {
-
- private String proguardMap1 = null;
- private String proguardMap2 = null;
-
- @Test
- public void buildFromDeployJar() throws Exception {
- File tempFolder = temp.newFolder();
- File app1Zip = new File(tempFolder, "app1.zip");
- Map<String, String> idsRoundOne = new ConcurrentHashMap<>();
- AndroidApp app1 =
- buildFromDeployJar(
- CompilerUnderTest.R8,
- CompilationMode.RELEASE,
- GMSCoreCompilationTestBase.GMSCORE_V10_DIR,
- false,
- options -> {
- options.testing.processingContextsConsumer =
- id -> assertNull(idsRoundOne.put(id, id));
- options.proguardMapConsumer =
- ToolHelper.consumeString(proguardMap -> this.proguardMap1 = proguardMap);
- },
- () -> new ArchiveConsumer(app1Zip.toPath(), true));
-
- File app2Zip = new File(tempFolder, "app2.zip");
- Map<String, String> idsRoundTwo = new ConcurrentHashMap<>();
- AndroidApp app2 =
- buildFromDeployJar(
- CompilerUnderTest.R8,
- CompilationMode.RELEASE,
- GMSCoreCompilationTestBase.GMSCORE_V10_DIR,
- false,
- options -> {
- options.testing.processingContextsConsumer =
- id -> {
- assertNotNull(idsRoundOne.get(id));
- assertNull(idsRoundTwo.put(id, id));
- };
- options.proguardMapConsumer =
- ToolHelper.consumeString(proguardMap -> this.proguardMap2 = proguardMap);
- },
- () -> new ArchiveConsumer(app2Zip.toPath(), true));
-
- // Verify that the result of the two compilations was the same.
- assertEquals(
- Collections.emptySet(),
- Sets.symmetricDifference(idsRoundOne.keySet(), idsRoundTwo.keySet()));
- assertIdenticalApplications(app1, app2);
- assertIdenticalZipFiles(app1Zip, app2Zip);
- assertEquals(proguardMap1, proguardMap2);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10JumboStringTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10JumboStringTest.java
deleted file mode 100644
index 365dc58..0000000
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10JumboStringTest.java
+++ /dev/null
@@ -1,20 +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.
-package com.android.tools.r8.internal;
-
-import com.android.tools.r8.CompilationMode;
-import org.junit.Test;
-
-public class R8GMSCoreV10JumboStringTest extends R8GMSCoreTreeShakeJarVerificationTest {
-
- @Test
- public void verify() throws Exception {
- buildAndTreeShakeFromDeployJar(
- CompilationMode.RELEASE,
- GMSCORE_V10_DIR,
- false,
- GMSCORE_V10_MAX_SIZE,
- options -> options.testing.forceJumboStringProcessing = true);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10TreeShakeJarVerificationTest.java
deleted file mode 100644
index dc78be2..0000000
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10TreeShakeJarVerificationTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (c) 2017, 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.internal;
-
-import static com.android.tools.r8.utils.AssertionUtils.assertNotNull;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.AndroidApp;
-import com.google.common.collect.Sets;
-import java.util.Collections;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import org.junit.Test;
-
-public class R8GMSCoreV10TreeShakeJarVerificationTest
- extends R8GMSCoreTreeShakeJarVerificationTest {
-
- private String proguardMap1 = null;
- private String proguardMap2 = null;
-
- @Test
- public void buildAndTreeShakeFromDeployJar() throws Exception {
- Map<String, String> idsRoundOne = new ConcurrentHashMap<>();
- AndroidApp app1 =
- buildAndTreeShakeFromDeployJar(
- CompilationMode.RELEASE,
- GMSCORE_V10_DIR,
- false,
- GMSCORE_V10_MAX_SIZE,
- options -> {
- options.testing.processingContextsConsumer =
- id -> assertNull(idsRoundOne.put(id, id));
- options.proguardMapConsumer =
- ToolHelper.consumeString(proguardMap -> this.proguardMap1 = proguardMap);
- });
- Map<String, String> idsRoundTwo = new ConcurrentHashMap<>();
- AndroidApp app2 =
- buildAndTreeShakeFromDeployJar(
- CompilationMode.RELEASE,
- GMSCORE_V10_DIR,
- false,
- GMSCORE_V10_MAX_SIZE,
- options -> {
- options.testing.processingContextsConsumer =
- id -> {
- assertNotNull(idsRoundOne.get(id));
- assertNull(idsRoundTwo.put(id, id));
- };
- options.proguardMapConsumer =
- ToolHelper.consumeString(proguardMap -> this.proguardMap2 = proguardMap);
- });
-
- // Verify that the result of the two compilations was the same.
- assertEquals(
- Collections.emptySet(),
- Sets.symmetricDifference(idsRoundOne.keySet(), idsRoundTwo.keySet()));
- assertIdenticalApplications(app1, app2);
- assertEquals(proguardMap1, proguardMap2);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV4VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV4VerificationTest.java
deleted file mode 100644
index 1b650e5..0000000
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV4VerificationTest.java
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright (c) 2016, 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.internal;
-
-import com.android.tools.r8.CompilationMode;
-import org.junit.Test;
-
-public class R8GMSCoreV4VerificationTest extends GMSCoreCompilationTestBase {
- @Test
- public void verify() throws Exception {
- runR8AndCheckVerification(CompilationMode.RELEASE, GMSCORE_V4_DIR);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV5VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV5VerificationTest.java
deleted file mode 100644
index df73644..0000000
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV5VerificationTest.java
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright (c) 2016, 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.internal;
-
-import com.android.tools.r8.CompilationMode;
-import org.junit.Test;
-
-public class R8GMSCoreV5VerificationTest extends GMSCoreCompilationTestBase {
- @Test
- public void verify() throws Exception {
- runR8AndCheckVerification(CompilationMode.RELEASE, GMSCORE_V5_DIR);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV6VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV6VerificationTest.java
deleted file mode 100644
index a872ff5..0000000
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV6VerificationTest.java
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright (c) 2016, 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.internal;
-
-import com.android.tools.r8.CompilationMode;
-import org.junit.Test;
-
-public class R8GMSCoreV6VerificationTest extends GMSCoreCompilationTestBase {
- @Test
- public void verify() throws Exception {
- runR8AndCheckVerification(CompilationMode.RELEASE, GMSCORE_V6_DIR);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV7VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV7VerificationTest.java
deleted file mode 100644
index 2cda255..0000000
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV7VerificationTest.java
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2016, 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.internal;
-
-import com.android.tools.r8.CompilationMode;
-import org.junit.Test;
-
-public class R8GMSCoreV7VerificationTest extends GMSCoreCompilationTestBase {
-
- @Test
- public void verify() throws Exception {
- runR8AndCheckVerification(CompilationMode.RELEASE, GMSCORE_V7_DIR);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV8VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV8VerificationTest.java
deleted file mode 100644
index 3f48336..0000000
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV8VerificationTest.java
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2016, 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.internal;
-
-import com.android.tools.r8.CompilationMode;
-import org.junit.Test;
-
-public class R8GMSCoreV8VerificationTest extends GMSCoreCompilationTestBase {
-
- @Test
- public void verify() throws Exception {
- runR8AndCheckVerification(CompilationMode.RELEASE, GMSCORE_V8_DIR);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9DeployJarVerificationTest.java
deleted file mode 100644
index fb06e84..0000000
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9DeployJarVerificationTest.java
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) 2017, 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.internal;
-
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
-import org.junit.Test;
-
-public class R8GMSCoreV9DeployJarVerificationTest extends GMSCoreDeployJarVerificationTest {
-
- @Test
- public void buildFromDeployJar() throws Exception {
- buildFromDeployJar(
- CompilerUnderTest.R8, CompilationMode.RELEASE,
- GMSCoreCompilationTestBase.GMSCORE_V9_DIR, true);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java
deleted file mode 100644
index a9e28c9..0000000
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (c) 2017, 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.internal;
-
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.StringConsumer.FileConsumer;
-import java.io.File;
-import java.nio.file.Path;
-import org.junit.Test;
-
-public class R8GMSCoreV9TreeShakeJarVerificationTest extends R8GMSCoreTreeShakeJarVerificationTest {
-
- @Test
- public void buildAndTreeShakeFromDeployJar() throws Exception {
- Path proguardMapPath = File.createTempFile("mapping", ".txt", temp.getRoot()).toPath();
- buildAndTreeShakeFromDeployJar(
- CompilationMode.RELEASE,
- GMSCORE_V9_DIR,
- true,
- GMSCORE_V9_MAX_SIZE,
- options -> options.proguardMapConsumer = new FileConsumer(proguardMapPath));
- }
-}
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 0e2de72f..402d6b3 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
@@ -5,7 +5,7 @@
package com.android.tools.r8.utils.codeinspector;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AccessFlags;
+import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.naming.ClassNamingForNameMapper;
@@ -115,7 +115,7 @@
}
@Override
- public AccessFlags<?> getAccessFlags() {
+ public ClassAccessFlags getAccessFlags() {
throw new Unreachable("Absent class has no access flags");
}
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 f614059..0a4f65a 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
@@ -6,6 +6,7 @@
import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
+import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.naming.ClassNamingForNameMapper;
@@ -167,6 +168,9 @@
return null;
}
+ @Override
+ public abstract ClassAccessFlags getAccessFlags();
+
public abstract boolean isAbstract();
public abstract boolean isAnnotation();
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 859f58f..4ac4f95 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
@@ -11,7 +11,7 @@
import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.graph.AccessFlags;
+import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
@@ -356,7 +356,7 @@
}
@Override
- public AccessFlags<?> getAccessFlags() {
+ public ClassAccessFlags getAccessFlags() {
return getDexProgramClass().getAccessFlags();
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
index 5083d42..3e70473 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.utils.codeinspector;
import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.Collectors;
import com.android.tools.r8.errors.Unreachable;
@@ -101,6 +102,28 @@
};
}
+ public static Matcher<ClassSubject> isInterface() {
+ return new TypeSafeMatcher<ClassSubject>() {
+ @Override
+ public boolean matchesSafely(ClassSubject subject) {
+ return subject.isPresent() && subject.getAccessFlags().isInterface();
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("interface");
+ }
+
+ @Override
+ public void describeMismatchSafely(ClassSubject subject, Description description) {
+ description
+ .appendText(type(subject) + " ")
+ .appendValue(name(subject))
+ .appendText(" was not");
+ }
+ };
+ }
+
public static Matcher<Subject> isAbsent() {
return not(isPresent());
}
@@ -499,6 +522,22 @@
};
}
+ public static Matcher<ClassSubject> isImplementing(ClassSubject interfaceSubject) {
+ assertThat(interfaceSubject, isPresent());
+ assertThat(interfaceSubject, isInterface());
+ return new TypeSafeMatcher<ClassSubject>() {
+ @Override
+ public boolean matchesSafely(ClassSubject subject) {
+ return subject.isPresent() && subject.isImplementing(interfaceSubject);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("implements ").appendText(interfaceSubject.getOriginalName());
+ }
+ };
+ }
+
public static Matcher<RetraceFrameResult> isInlineFrame() {
return new TypeSafeMatcher<RetraceFrameResult>() {
@Override
diff --git a/third_party/gmscore/gmscore_v9.tar.gz.sha1 b/third_party/gmscore/gmscore_v9.tar.gz.sha1
deleted file mode 100644
index 5983b5b..0000000
--- a/third_party/gmscore/gmscore_v9.tar.gz.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0066065faeb293c5a850d3319f2cb8a48d1e760d
\ No newline at end of file
diff --git a/third_party/gmscore/v4.tar.gz.sha1 b/third_party/gmscore/v4.tar.gz.sha1
deleted file mode 100644
index e4747ae..0000000
--- a/third_party/gmscore/v4.tar.gz.sha1
+++ /dev/null
@@ -1 +0,0 @@
-2330b38e52fc7cfe78c6aa94ae497a0b1a42b9a2
\ No newline at end of file
diff --git a/third_party/gmscore/v5.tar.gz.sha1 b/third_party/gmscore/v5.tar.gz.sha1
deleted file mode 100644
index 8ca5043..0000000
--- a/third_party/gmscore/v5.tar.gz.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c96d3360ba55718eca7d9c7ac9edb801a2ed4cc5
\ No newline at end of file
diff --git a/third_party/gmscore/v6.tar.gz.sha1 b/third_party/gmscore/v6.tar.gz.sha1
deleted file mode 100644
index 6cca80b..0000000
--- a/third_party/gmscore/v6.tar.gz.sha1
+++ /dev/null
@@ -1 +0,0 @@
-05eaeaa62842453108ca7191595801bb52e24861
\ No newline at end of file
diff --git a/third_party/gmscore/v7.tar.gz.sha1 b/third_party/gmscore/v7.tar.gz.sha1
deleted file mode 100644
index 502e1cf..0000000
--- a/third_party/gmscore/v7.tar.gz.sha1
+++ /dev/null
@@ -1 +0,0 @@
-01da4f538ff748867d7fa9a7a9ba262d8aa62922
\ No newline at end of file
diff --git a/third_party/gmscore/v8.tar.gz.sha1 b/third_party/gmscore/v8.tar.gz.sha1
deleted file mode 100644
index 9eab429..0000000
--- a/third_party/gmscore/v8.tar.gz.sha1
+++ /dev/null
@@ -1 +0,0 @@
-ee245a82b2470a1c60ac7fa085d2b5ce2101a3da
\ No newline at end of file
diff --git a/tools/compiledump.py b/tools/compiledump.py
index b508c6f..cc71d22 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -176,11 +176,10 @@
return Dump(args.dump)
dump_file = zipfile.ZipFile(os.path.abspath(args.dump), 'r')
with utils.ChangedWorkingDirectory(temp):
- if args.override or not os.path.isfile(
- os.path.join(temp, 'r8-version')):
+ if args.override or not os.path.isfile('r8-version'):
print("Extracting into: %s" % temp)
dump_file.extractall()
- if not os.path.isfile(os.path.join(temp, 'r8-version')):
+ if not os.path.isfile('r8-version'):
error("Did not extract into %s. Either the zip file is invalid or the "
"dump is missing files" % temp)
return Dump(temp)
diff --git a/tools/gmscore_data.py b/tools/gmscore_data.py
index bef5ba3..95ee0bb 100644
--- a/tools/gmscore_data.py
+++ b/tools/gmscore_data.py
@@ -6,89 +6,18 @@
import os
import utils
+ANDROID_L_API = '21'
BASE = os.path.join(utils.THIRD_PARTY, 'gmscore')
-V4_BASE = os.path.join(BASE, 'v4')
-V5_BASE = os.path.join(BASE, 'v5')
-V6_BASE = os.path.join(BASE, 'v6')
-V7_BASE = os.path.join(BASE, 'v7')
-V8_BASE = os.path.join(BASE, 'v8')
-
-V9_BASE = os.path.join(BASE, 'gmscore_v9')
-V9_PREFIX = os.path.join(V9_BASE, 'GmsCore_prod_alldpi_release_all_locales')
-
V10_BASE = os.path.join(BASE, 'gmscore_v10')
V10_PREFIX = os.path.join(V10_BASE, 'GmsCore_prod_alldpi_release_all_locales')
LATEST_BASE = os.path.join(BASE, 'latest')
LATEST_PREFIX = os.path.join(LATEST_BASE, 'GmsCore_prod_alldpi_release_all_locales')
-ANDROID_L_API = '21'
-# NOTE: we always use android.jar for SDK v25, later we might want to revise it
-# to use proper android.jar version for each of gmscore version separately.
-ANDROID_JAR = utils.get_android_jar(25)
+LATEST_VERSION = 'latest'
VERSIONS = {
- 'v4': {
- 'dex' : {
- 'inputs' : glob.glob(os.path.join(V4_BASE, '*.dex')),
- 'pgmap' : os.path.join(V4_BASE, 'proguard.map'),
- 'libraries' : [ANDROID_JAR],
- 'min-api' : ANDROID_L_API,
- }
- },
- 'v5': {
- 'dex' : {
- 'inputs' : glob.glob(os.path.join(V5_BASE, '*.dex')),
- 'pgmap' : os.path.join(V5_BASE, 'proguard.map'),
- 'libraries' : [ANDROID_JAR],
- 'min-api' : ANDROID_L_API,
- }
- },
- 'v6': {
- 'dex' : {
- 'inputs' : glob.glob(os.path.join(V6_BASE, '*.dex')),
- 'pgmap' : os.path.join(V6_BASE, 'proguard.map'),
- 'libraries' : [ANDROID_JAR],
- 'min-api' : ANDROID_L_API,
- }
- },
- 'v7': {
- 'dex' : {
- 'inputs' : glob.glob(os.path.join(V7_BASE, '*.dex')),
- 'pgmap' : os.path.join(V7_BASE, 'proguard.map'),
- 'libraries' : [ANDROID_JAR],
- 'min-api' : ANDROID_L_API,
- }
- },
- 'v8': {
- 'dex' : {
- 'inputs' : glob.glob(os.path.join(V8_BASE, '*.dex')),
- 'pgmap' : os.path.join(V8_BASE, 'proguard.map'),
- 'libraries' : [ANDROID_JAR],
- 'min-api' : ANDROID_L_API,
- }
- },
- 'v9': {
- 'dex' : {
- 'flags': '--no-desugaring',
- 'inputs': [os.path.join(V9_BASE, 'armv7_GmsCore_prod_alldpi_release.apk')],
- 'main-dex-list': os.path.join(V9_BASE, 'main_dex_list.txt'),
- 'pgmap': '%s_proguard.map' % V9_PREFIX,
- },
- 'deploy' : {
- 'pgconf': ['%s_proguard.config' % V9_PREFIX,
- utils.IGNORE_WARNINGS_RULES],
- 'inputs': ['%s_deploy.jar' % V9_PREFIX],
- 'min-api' : ANDROID_L_API,
- },
- 'proguarded' : {
- 'flags': '--no-desugaring',
- 'inputs': ['%s_proguard.jar' % V9_PREFIX],
- 'main-dex-list': os.path.join(V9_BASE, 'main_dex_list.txt'),
- 'pgmap': '%s_proguard.map' % V9_PREFIX,
- }
- },
'v10': {
'dex' : {
'flags': '--no-desugaring',
diff --git a/tools/r8_release.py b/tools/r8_release.py
index 397c97d..7914bf9 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -15,7 +15,7 @@
import utils
-R8_DEV_BRANCH = '3.0'
+R8_DEV_BRANCH = '3.1'
R8_VERSION_FILE = os.path.join(
'src', 'main', 'java', 'com', 'android', 'tools', 'r8', 'Version.java')
THIS_FILE_RELATIVE = os.path.join('tools', 'r8_release.py')
@@ -126,7 +126,7 @@
def version_change_diff(diff, old_version, new_version):
invalid_line = None
- for line in diff.splitlines():
+ for line in str(diff).splitlines():
if line.startswith('- ') and \
line != '- public static final String LABEL = "%s";' % old_version:
invalid_line = line
@@ -634,7 +634,7 @@
def branch_change_diff(diff, old_version, new_version):
invalid_line = None
- for line in diff.splitlines():
+ for line in str(diff).splitlines():
if line.startswith('-R8') and \
line != "-R8_DEV_BRANCH = '%s'" % old_version:
print(line)
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index c775aa0..cdceb6b 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -390,7 +390,7 @@
def get_version_and_data(options):
if options.app == 'gmscore':
- version = options.version or 'v9'
+ version = options.version or gmscore_data.LATEST_VERSION
data = gmscore_data
elif options.app == 'nest':
version = options.version or '20180926'