Merge "Don't use r8'ed prebuilds before we archive proguard map"
diff --git a/pgconfs/d8.cfg b/pgconfs/d8.cfg
deleted file mode 100644
index 2d74e47..0000000
--- a/pgconfs/d8.cfg
+++ /dev/null
@@ -1,6 +0,0 @@
-# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-# for details. All rights reserved. Use of this source code is governed by a
-# BSD-style license that can be found in the LICENSE file.
-
--keep @com.android.tools.r8.Keep class * { public *; }
--keep @com.android.tools.r8.KeepForSubclassing class * { public *; protected *; }
diff --git a/pgconfs/r8.cfg b/pgconfs/r8.cfg
deleted file mode 100644
index d1ae99b..0000000
--- a/pgconfs/r8.cfg
+++ /dev/null
@@ -1,7 +0,0 @@
-# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-# for details. All rights reserved. Use of this source code is governed by a
-# BSD-style license that can be found in the LICENSE file.
-
--keep public class com.android.tools.r8.SwissArmyKnife { public static void main(java.lang.String[]); }
--keep @com.android.tools.r8.Keep class * { public *; }
--keep @com.android.tools.r8.KeepForSubclassing class * { public *; protected *; }
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 7f77c6b..494a62b 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -431,6 +431,7 @@
new SourceFileRewriter(appView.appInfo(), options).run();
timing.end();
+ MainDexClasses mainDexClasses = MainDexClasses.NONE;
if (!options.mainDexKeepRules.isEmpty()) {
appView.setAppInfo(new AppInfoWithSubtyping(application));
Enqueuer enqueuer = new Enqueuer(appView, options, true);
@@ -444,8 +445,7 @@
// LiveTypes is the tracing result.
Set<DexType> mainDexBaseClasses = new HashSet<>(mainDexAppInfo.liveTypes);
// Calculate the automatic main dex list according to legacy multidex constraints.
- MainDexClasses mainDexClasses =
- new MainDexListBuilder(mainDexBaseClasses, application).run();
+ mainDexClasses = new MainDexListBuilder(mainDexBaseClasses, application).run();
if (!mainDexRootSet.checkDiscarded.isEmpty()) {
new DiscardedChecker(
mainDexRootSet, mainDexClasses.getClasses(), appView.appInfo(), options)
@@ -459,8 +459,6 @@
ReasonPrinter reasonPrinter = enqueuer.getReasonPrinter(mainDexRootSet.reasonAsked);
reasonPrinter.run(mainDexApplication);
}
- // Add automatic main dex classes to an eventual manual list of classes.
- application = application.builder().addToMainDexList(mainDexClasses.getClasses()).build();
}
appView.setAppInfo(new AppInfoWithSubtyping(application));
@@ -484,12 +482,21 @@
reasonPrinter.run(application);
// Remove annotations that refer to types that no longer exist.
new AnnotationRemover(appView.appInfo().withLiveness(), options).run();
+ if (!mainDexClasses.isEmpty()) {
+ // Remove types that no longer exists from the computed main dex list.
+ mainDexClasses = mainDexClasses.prunedCopy(appView.appInfo().withLiveness());
+ }
}
} finally {
timing.end();
}
}
+ // Add automatic main dex classes to an eventual manual list of classes.
+ if (!options.mainDexKeepRules.isEmpty()) {
+ application = application.builder().addToMainDexList(mainDexClasses.getClasses()).build();
+ }
+
// Only perform discard-checking if tree-shaking is turned on.
if (options.enableTreeShaking && !rootSet.checkDiscarded.isEmpty()) {
new DiscardedChecker(rootSet, application, options).run();
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index bdea79c..d23f3d8 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.4.13-dev";
+ public static final String LABEL = "1.4.14-dev";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
index 6914bc1..724faf1 100644
--- a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
@@ -381,6 +381,37 @@
updateFirstStackByJoiningTheSecond(typesOfKept.stack, typesOfRemoved.stack);
}
+ @Override
+ public boolean hasEqualTypesAtEntry(BasicBlock first, BasicBlock second) {
+ if (!java.util.Objects.equals(first.getLocalsAtEntry(), second.getLocalsAtEntry())) {
+ return false;
+ }
+ // Check that stack at entry is same.
+ List<TypeInfo> firstStack = getTypesAtBlockEntry(first).stack;
+ List<TypeInfo> secondStack = getTypesAtBlockEntry(second).stack;
+ if (firstStack.size() != secondStack.size()) {
+ return false;
+ }
+ for (int i = 0; i < firstStack.size(); i++) {
+ if (firstStack.get(i).getDexType() != secondStack.get(i).getDexType()) {
+ return false;
+ }
+ }
+ // Check if the blocks are catch-handlers, which implies that the exception is transferred
+ // through the stack.
+ if (first.entry().isMoveException() != second.entry().isMoveException()) {
+ return false;
+ }
+ // If both blocks are catch handlers, we require that the exceptions are the same type.
+ if (first.entry().isMoveException()
+ && typeHelper.getTypeInfo(first.entry().outValue()).getDexType()
+ != typeHelper.getTypeInfo(second.entry().outValue()).getDexType()) {
+ return false;
+ }
+
+ return true;
+ }
+
// Return false if this is not an instruction with the type of outvalue dependent on types of
// invalues.
private boolean tryApplyInstructionWithDependentOutType(
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index e9966f5..1c04f9d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -993,13 +993,14 @@
public ReferenceTypeLatticeElement createReferenceTypeLatticeElement(
DexType type, boolean isNullable, AppInfo appInfo) {
+ ReferenceTypeLatticeElement typeLattice = referenceTypeLatticeElements.get(type);
+ if (typeLattice != null) {
+ return isNullable == typeLattice.isNullable() ? typeLattice
+ : typeLattice.getOrCreateDualLattice();
+ }
synchronized (type) {
- ReferenceTypeLatticeElement typeLattice = referenceTypeLatticeElements.get(type);
- if (typeLattice != null) {
- typeLattice = isNullable == typeLattice.isNullable() ? typeLattice
- : typeLattice.getOrCreateDualLattice();
- assert typeLattice.isNullable() == isNullable;
- } else {
+ typeLattice = referenceTypeLatticeElements.get(type);
+ if (typeLattice == null) {
if (type.isClassType()) {
if (!type.isUnknown() && type.isInterface()) {
typeLattice = new ClassTypeLatticeElement(
@@ -1020,9 +1021,11 @@
}
referenceTypeLatticeElements.put(type, typeLattice);
}
- assert typeLattice.isNullable() == isNullable;
- return typeLattice;
}
+ // The call to getOrCreateDualLattice can't be under the DexType synchronized block, since that
+ // can create deadlocks with ClassTypeLatticeElement::getInterfaces (both lock on the lattice).
+ return isNullable == typeLattice.isNullable() ? typeLattice
+ : typeLattice.getOrCreateDualLattice();
}
private static <S extends PresortedComparable<S>> void assignSortedIndices(Collection<S> items,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/BasicBlockInstructionsEquivalence.java b/src/main/java/com/android/tools/r8/ir/optimize/BasicBlockInstructionsEquivalence.java
index 73cf07f..17c8d1a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/BasicBlockInstructionsEquivalence.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/BasicBlockInstructionsEquivalence.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
import com.google.common.base.Equivalence;
-import com.google.common.base.Objects;
import java.util.Arrays;
import java.util.List;
@@ -40,7 +39,7 @@
}
}
- if (!Objects.equal(first.getLocalsAtEntry(), second.getLocalsAtEntry())) {
+ if (!allocator.hasEqualTypesAtEntry(first, second)) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index eb42723..30cd8b7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -128,9 +128,10 @@
Supplier<InliningOracle> defaultOracle) {
// Collect all the new-instance and static-get instructions in the code before inlining.
- List<Instruction> roots = Streams.stream(code.instructionIterator())
- .filter(insn -> insn.isNewInstance() || insn.isStaticGet())
- .collect(Collectors.toList());
+ List<Instruction> roots =
+ Streams.stream(code.instructionIterator())
+ .filter(insn -> insn.isNewInstance() || insn.isStaticGet())
+ .collect(Collectors.toList());
// We loop inlining iterations until there was no inlining, but still use same set
// of roots to avoid infinite inlining. Looping makes possible for some roots to
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index ade48f3..d952a6c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -104,8 +104,8 @@
return false;
}
- eligibleClass = isNewInstance() ?
- root.asNewInstance().clazz : root.asStaticGet().getField().type;
+ eligibleClass =
+ root.isNewInstance() ? root.asNewInstance().clazz : root.asStaticGet().getField().type;
eligibleClassDefinition = appInfo.definitionFor(eligibleClass);
if (eligibleClassDefinition == null && lambdaRewriter != null) {
// Check if the class is synthesized for a desugared lambda
@@ -131,7 +131,7 @@
return false;
}
- if (isNewInstance()) {
+ if (root.isNewInstance()) {
// NOTE: if the eligible class does not directly extend java.lang.Object,
// we also have to guarantee that it is initialized with initializer classified as
// TrivialInstanceInitializer. This will be checked in areInstanceUsersEligible(...).
@@ -197,8 +197,11 @@
// of class inlining
//
- if (eligibleClassDefinition.instanceFields().length > 0 ||
- !eligibleClassDefinition.accessFlags.isFinal()) {
+ if (eligibleClassDefinition.instanceFields().length > 0) {
+ return false;
+ }
+ if (eligibleClassDefinition.type.hasSubtypes()) {
+ assert !eligibleClassDefinition.accessFlags.isFinal();
return false;
}
@@ -796,10 +799,6 @@
}
}
- private boolean isNewInstance() {
- return root.isNewInstance();
- }
-
private DexEncodedMethod findSingleTarget(DexMethod callee, boolean isDirect) {
// We don't use computeSingleTarget(...) on invoke since it sometimes fails to
// find the single target, while this code may be more successful since we exactly
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index c971a14..40d31d2 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -3172,6 +3172,11 @@
}
@Override
+ public boolean hasEqualTypesAtEntry(BasicBlock first, BasicBlock second) {
+ return java.util.Objects.equals(first.getLocalsAtEntry(), second.getLocalsAtEntry());
+ }
+
+ @Override
public void addNewBlockToShareIdenticalSuffix(
BasicBlock block, int suffixSize, List<BasicBlock> predsBeforeSplit) {
// Intentionally empty, we don't need to track suffix sharing in this allocator.
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
index 1314b3f..43b41e5 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
@@ -17,6 +17,8 @@
void mergeBlocks(BasicBlock kept, BasicBlock removed);
+ boolean hasEqualTypesAtEntry(BasicBlock first, BasicBlock second);
+
// Call before removing the suffix from the preds. Block is used only as a key.
void addNewBlockToShareIdenticalSuffix(
BasicBlock block, int suffixSize, List<BasicBlock> predsBeforeSplit);
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java b/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java
index 3869db6..565a5df 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java
@@ -7,13 +7,19 @@
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
public class MainDexClasses {
+ public static MainDexClasses NONE = new MainDexClasses(ImmutableSet.of(), ImmutableSet.of());
+
public static class Builder {
public final AppInfo appInfo;
public final Set<DexType> roots = Sets.newIdentityHashSet();
@@ -23,6 +29,12 @@
this.appInfo = appInfo;
}
+ public Builder addRoot(DexType type) {
+ assert isProgramClass(type) : type.toSourceString();
+ roots.add(type);
+ return this;
+ }
+
public Builder addRoots(Collection<DexType> rootSet) {
assert rootSet.stream().allMatch(this::isProgramClass);
this.roots.addAll(rootSet);
@@ -50,21 +62,26 @@
}
// The classes in the root set.
- private final Set<DexType> rootSet;
- // Additional dependencies (direct dependencise and runtime annotations with enums).
+ private final Set<DexType> roots;
+ // Additional dependencies (direct dependencies and runtime annotations with enums).
private final Set<DexType> dependencies;
// All main dex classes.
private final Set<DexType> classes;
- private MainDexClasses(Set<DexType> rootSet, Set<DexType> dependencies) {
- assert Sets.intersection(rootSet, dependencies).isEmpty();
- this.rootSet = Collections.unmodifiableSet(rootSet);
+ private MainDexClasses(Set<DexType> roots, Set<DexType> dependencies) {
+ assert Sets.intersection(roots, dependencies).isEmpty();
+ this.roots = Collections.unmodifiableSet(roots);
this.dependencies = Collections.unmodifiableSet(dependencies);
- this.classes = Sets.union(rootSet, dependencies);
+ this.classes = Sets.union(roots, dependencies);
+ }
+
+ public boolean isEmpty() {
+ assert !roots.isEmpty() || dependencies.isEmpty();
+ return roots.isEmpty();
}
public Set<DexType> getRoots() {
- return rootSet;
+ return roots;
}
public Set<DexType> getDependencies() {
@@ -75,6 +92,24 @@
return classes;
}
+ private void collectTypesMatching(
+ Set<DexType> types, Predicate<DexType> predicate, Consumer<DexType> consumer) {
+ types.forEach(
+ type -> {
+ if (predicate.test(type)) {
+ consumer.accept(type);
+ }
+ });
+ }
+
+ public MainDexClasses prunedCopy(AppInfoWithLiveness appInfo) {
+ Builder builder = builder(appInfo);
+ Predicate<DexType> wasPruned = appInfo::wasPruned;
+ collectTypesMatching(roots, wasPruned.negate(), builder::addRoot);
+ collectTypesMatching(dependencies, wasPruned.negate(), builder::addDependency);
+ return builder.build();
+ }
+
public static Builder builder(AppInfo appInfo) {
return new Builder(appInfo);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
index eec636a..9d129d6 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
@@ -69,15 +69,17 @@
continue;
}
if (isAnnotation(dexType) && isAnnotationWithEnum(dexType)) {
- addDirectDependencyOrRuntimeAnnotationsWithEnum(dexType);
+ addAnnotationsWithEnum(clazz);
continue;
}
+ // Classes with annotations must be in the same dex file as the annotation. As all
+ // annotations with enums goes into the main dex, move annotated classes there as well.
clazz.forEachAnnotation(
annotation -> {
if (!mainDexClassesBuilder.contains(dexType)
&& annotation.visibility == DexAnnotation.VISIBILITY_RUNTIME
&& isAnnotationWithEnum(annotation.annotation.type)) {
- addDirectDependencyOrRuntimeAnnotationsWithEnum(dexType);
+ addClassAnnotatedWithAnnotationWithEnum(dexType);
}
});
}
@@ -89,9 +91,9 @@
DexClass clazz = appInfo.definitionFor(dexType);
if (clazz == null) {
// Information is missing lets be conservative.
- value = Boolean.TRUE;
+ value = true;
} else {
- value = Boolean.FALSE;
+ value = false;
// Browse annotation values types in search for enum.
// Each annotation value is represented by a virtual method.
for (DexEncodedMethod method : clazz.virtualMethods()) {
@@ -99,10 +101,10 @@
if (proto.parameters.isEmpty()) {
DexType valueType = proto.returnType.toBaseType(appInfo.dexItemFactory);
if (isEnum(valueType)) {
- value = Boolean.TRUE;
+ value = true;
break;
} else if (isAnnotation(valueType) && isAnnotationWithEnum(valueType)) {
- value = Boolean.TRUE;
+ value = true;
break;
}
}
@@ -110,7 +112,7 @@
}
annotationTypeContainEnum.put(dexType, value);
}
- return value.booleanValue();
+ return value;
}
private boolean isEnum(DexType valueType) {
@@ -127,11 +129,31 @@
}
private void traceMainDexDirectDependencies() {
- new MainDexDirectReferenceTracer(appInfo, this::addDirectDependencyOrRuntimeAnnotationsWithEnum)
+ new MainDexDirectReferenceTracer(appInfo, this::addDirectDependency)
.run(roots);
}
- private void addDirectDependencyOrRuntimeAnnotationsWithEnum(DexType type) {
+ private void addAnnotationsWithEnum(DexProgramClass clazz) {
+ // Add the annotation class as a direct dependency.
+ addDirectDependency(clazz);
+ // Add enum classes used for values as direct dependencies.
+ for (DexEncodedMethod method : clazz.virtualMethods()) {
+ DexProto proto = method.method.proto;
+ if (proto.parameters.isEmpty()) {
+ DexType valueType = proto.returnType.toBaseType(appInfo.dexItemFactory);
+ if (isEnum(valueType)) {
+ addDirectDependency(valueType);
+ }
+ }
+ }
+ }
+
+ private void addClassAnnotatedWithAnnotationWithEnum(DexType type) {
+ // Just add classes annotated with annotations with enum ad direct dependencies.
+ addDirectDependency(type);
+ }
+
+ private void addDirectDependency(DexType type) {
// Consider only component type of arrays
type = type.toBaseType(appInfo.dexItemFactory);
@@ -144,18 +166,18 @@
if (clazz == null || clazz.isLibraryClass()) {
return;
}
- addDirectDependencyOrRuntimeAnnotationsWithEnum(clazz.asProgramClass());
+ addDirectDependency(clazz.asProgramClass());
}
- private void addDirectDependencyOrRuntimeAnnotationsWithEnum(DexProgramClass dexClass) {
+ private void addDirectDependency(DexProgramClass dexClass) {
DexType type = dexClass.type;
assert !mainDexClassesBuilder.contains(type);
mainDexClassesBuilder.addDependency(type);
if (dexClass.superType != null) {
- addDirectDependencyOrRuntimeAnnotationsWithEnum(dexClass.superType);
+ addDirectDependency(dexClass.superType);
}
for (DexType interfaze : dexClass.interfaces.values) {
- addDirectDependencyOrRuntimeAnnotationsWithEnum(interfaze);
+ addDirectDependency(interfaze);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
index 747dea9..dc8925a 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -112,6 +112,10 @@
return null;
}
+ public final boolean matchesSpecificType() {
+ return getSpecificType() != null;
+ }
+
private static class MatchAllTypes extends ProguardTypeMatcher {
private static final ProguardTypeMatcher MATCH_ALL_TYPES = new MatchAllTypes();
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index de0537f..7485f31 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -593,19 +593,23 @@
implementsExpected = satisfyImplementsRule(clazz, rule);
}
if (extendsExpected || implementsExpected) {
- // Warn if users got it wrong, but only warn once.
- if (rule.getInheritanceIsExtends()) {
- if (implementsExpected && rulesThatUseExtendsOrImplementsWrong.add(rule)) {
+ // Warn if users got it wrong, but only warn once. Also, only warn if rule is actually
+ // specific (there is no correct way to write "keep class X that extends or implements from
+ // a class or interface in package Y").
+ if (rule.getInheritanceClassName().matchesSpecificType()) {
+ if (rule.getInheritanceIsExtends()) {
+ if (implementsExpected && rulesThatUseExtendsOrImplementsWrong.add(rule)) {
+ assert options.testing.allowProguardRulesThatUseExtendsOrImplementsWrong;
+ options.reporter.warning(
+ new StringDiagnostic(
+ "The rule `" + rule + "` uses extends but actually matches implements."));
+ }
+ } else if (extendsExpected && rulesThatUseExtendsOrImplementsWrong.add(rule)) {
assert options.testing.allowProguardRulesThatUseExtendsOrImplementsWrong;
options.reporter.warning(
new StringDiagnostic(
- "The rule `" + rule + "` uses extends but actually matches implements."));
+ "The rule `" + rule + "` uses implements but actually matches extends."));
}
- } else if (extendsExpected && rulesThatUseExtendsOrImplementsWrong.add(rule)) {
- assert options.testing.allowProguardRulesThatUseExtendsOrImplementsWrong;
- options.reporter.warning(
- new StringDiagnostic(
- "The rule `" + rule + "` uses implements but actually matches extends."));
}
return true;
}
diff --git a/pgconfs/compatdx.cfg b/src/main/keep-compatdx.txt
similarity index 100%
rename from pgconfs/compatdx.cfg
rename to src/main/keep-compatdx.txt
diff --git a/pgconfs/compatproguard.cfg b/src/main/keep-compatproguard.txt
similarity index 100%
rename from pgconfs/compatproguard.cfg
rename to src/main/keep-compatproguard.txt
diff --git a/src/main/keep.txt b/src/main/keep.txt
index 2d74e47..0d3812c 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -4,3 +4,11 @@
-keep @com.android.tools.r8.Keep class * { public *; }
-keep @com.android.tools.r8.KeepForSubclassing class * { public *; protected *; }
+
+-keep public class com.android.tools.r8.D8 { public static void main(java.lang.String[]); }
+-keep public class com.android.tools.r8.R8 { public static void main(java.lang.String[]); }
+-keep public class com.android.tools.r8.ExtractMarker { public static void main(java.lang.String[]); }
+-keep public class com.android.tools.r8.compatdexbuilder.CompatDexBuilder { public static void main(java.lang.String[]); }
+-keep public class com.android.tools.r8.compatdx.CompatDx { public static void main(java.lang.String[]); }
+-keep public class com.android.tools.r8.dexfilemerger.DexFileMerger { public static void main(java.lang.String[]); }
+-keep public class com.android.tools.r8.dexsplitter.DexSplitter { public static void main(java.lang.String[]); }
diff --git a/src/test/examples/multidex002/ref-list-1.txt b/src/test/examples/multidex002/ref-list-1.txt
index 922d417..5fbe5c1 100644
--- a/src/test/examples/multidex002/ref-list-1.txt
+++ b/src/test/examples/multidex002/ref-list-1.txt
@@ -7,6 +7,7 @@
Lmultidex002/AnnotatedVirtualMethod;
Lmultidex002/AnnotationWithClass;
Lmultidex002/AnnotationWithEnum2;
+Lmultidex002/AnnotationWithEnum3$Value3;
Lmultidex002/AnnotationWithEnum3;
Lmultidex002/AnnotationWithEnum;
Lmultidex002/AnnotationWithoutEnum;
diff --git a/src/test/examples/multidex003/ref-list-1.txt b/src/test/examples/multidex003/ref-list-1.txt
index 40c9dcd..cdf1292 100644
--- a/src/test/examples/multidex003/ref-list-1.txt
+++ b/src/test/examples/multidex003/ref-list-1.txt
@@ -1,7 +1,9 @@
Lmultidex003/Annotated2;
Lmultidex003/Annotated3;
Lmultidex003/Annotated;
+Lmultidex003/AnnotationValue;
Lmultidex003/TestAnnotation2;
+Lmultidex003/TestAnnotation3$Value;
Lmultidex003/TestAnnotation3;
Lmultidex003/TestAnnotation;
Lmultidex003/fakelibrary/MultiDex$V14;
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java
index 129176a..88d077b 100644
--- a/src/test/java/com/android/tools/r8/D8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -22,7 +22,7 @@
}
public static D8TestBuilder create(TestState state) {
- return new D8TestBuilder(state, D8Command.builder());
+ return new D8TestBuilder(state, D8Command.builder(state.getDiagnosticsHandler()));
}
@Override
diff --git a/src/test/java/com/android/tools/r8/D8TestCompileResult.java b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
index 178d351..79fb293 100644
--- a/src/test/java/com/android/tools/r8/D8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
@@ -18,6 +18,11 @@
}
@Override
+ public TestDiagnosticMessages getDiagnosticMessages() {
+ return state.getDiagnosticsMessages();
+ }
+
+ @Override
public D8TestRunResult createRunResult(AndroidApp app, ProcessResult result) {
return new D8TestRunResult(app, result);
}
diff --git a/src/test/java/com/android/tools/r8/DXTestCompileResult.java b/src/test/java/com/android/tools/r8/DXTestCompileResult.java
index fa1f314..da6a17a 100644
--- a/src/test/java/com/android/tools/r8/DXTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/DXTestCompileResult.java
@@ -19,6 +19,11 @@
}
@Override
+ public TestDiagnosticMessages getDiagnosticMessages() {
+ throw new UnsupportedOperationException("No diagnostics messages from dx");
+ }
+
+ @Override
public DXTestRunResult createRunResult(AndroidApp app, ProcessResult result) {
return new DXTestRunResult(app, result);
}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
index bef1e02..4ea8953 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
@@ -25,6 +25,11 @@
}
@Override
+ public TestDiagnosticMessages getDiagnosticMessages() {
+ throw new UnsupportedOperationException("No diagnostics messages from dx");
+ }
+
+ @Override
public CodeInspector inspector() throws IOException, ExecutionException {
return new CodeInspector(app, proguardMap);
}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 5550415..4404bae 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -10,10 +10,12 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
public class R8TestBuilder
extends TestShrinkerBuilder<
@@ -25,7 +27,9 @@
public static R8TestBuilder create(TestState state, Backend backend, R8Mode mode) {
R8Command.Builder builder =
- mode == R8Mode.Full ? R8Command.builder() : new CompatProguardCommandBuilder();
+ mode == R8Mode.Full
+ ? R8Command.builder(state.getDiagnosticsHandler())
+ : new CompatProguardCommandBuilder(true, state.getDiagnosticsHandler());
return new R8TestBuilder(state, builder, backend);
}
@@ -62,6 +66,28 @@
return self();
}
+ public R8TestBuilder addMainDexRules(Collection<String> rules) {
+ builder.addMainDexRules(new ArrayList<>(rules), Origin.unknown());
+ return self();
+ }
+
+ public R8TestBuilder addMainDexRules(String... rules) {
+ return addMainDexRules(Arrays.asList(rules));
+ }
+
+ public R8TestBuilder addMainDexClassRules(Class<?>... classes) {
+ for (Class<?> clazz : classes) {
+ addMainDexRules("-keep class " + clazz.getTypeName());
+ }
+ return self();
+ }
+
+ public R8TestBuilder addMainDexListClasses(Class<?>... classes) {
+ builder.addMainDexClasses(
+ Arrays.stream(classes).map(Class::getTypeName).collect(Collectors.toList()));
+ return self();
+ }
+
public R8TestBuilder enableInliningAnnotations() {
if (!enableInliningAnnotations) {
enableInliningAnnotations = true;
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index 35cb176..d27a4a0 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -27,6 +27,11 @@
}
@Override
+ public TestDiagnosticMessages getDiagnosticMessages() {
+ return state.getDiagnosticsMessages();
+ }
+
+ @Override
public CodeInspector inspector() throws IOException, ExecutionException {
return new CodeInspector(app, proguardMap);
}
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 3042cbe..4821c25 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -47,6 +47,7 @@
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import org.junit.Assert;
+import org.junit.Assume;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -581,7 +582,7 @@
void execute(String testName,
String qualifiedMainClass, Path[] jars, Path[] dexes, List<String> args) throws IOException {
-
+ Assume.assumeTrue(ToolHelper.artSupported() || ToolHelper.compareAgaintsGoldenFiles());
boolean expectedToFail = expectedToFail(testName);
try {
String output = ToolHelper.runArtNoVerificationErrors(
@@ -592,7 +593,9 @@
builder.appendProgramArgument(arg);
}
});
- if (!skipRunningOnJvm(testName) && !ToolHelper.compareAgaintsGoldenFiles()) {
+ if (!expectedToFail
+ && !skipRunningOnJvm(testName)
+ && !ToolHelper.compareAgaintsGoldenFiles()) {
ArrayList<String> javaArgs = Lists.newArrayList(args);
javaArgs.add(0, qualifiedMainClass);
ToolHelper.ProcessResult javaResult =
@@ -606,7 +609,7 @@
output.replace("\r", "").equals(javaResult.stdout.replace("\r", "")));
}
} catch (Throwable t) {
- assert expectedToFail && !ToolHelper.compareAgaintsGoldenFiles();
+ assert expectedToFail;
}
}
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index 61f9def..c750ded 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -4,6 +4,9 @@
package com.android.tools.r8;
import static com.android.tools.r8.TestBase.Backend.DEX;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.fail;
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.ToolHelper.DexVm;
@@ -18,6 +21,7 @@
import java.io.IOException;
import java.nio.file.Path;
import java.util.concurrent.ExecutionException;
+import org.hamcrest.Matcher;
public abstract class TestCompileResult<RR extends TestRunResult> {
final TestState state;
@@ -30,6 +34,8 @@
public abstract Backend getBackend();
+ public abstract TestDiagnosticMessages getDiagnosticMessages();
+
protected abstract RR createRunResult(AndroidApp add, ProcessResult result);
public RR run(Class<?> mainClass) throws IOException {
@@ -62,6 +68,61 @@
return this;
}
+ public TestCompileResult<RR> assertNoMessages() {
+ assertEquals(0, getDiagnosticMessages().getInfos().size());
+ assertEquals(0, getDiagnosticMessages().getWarnings().size());
+ assertEquals(0, getDiagnosticMessages().getErrors().size());
+ return this;
+ }
+
+ public TestCompileResult<RR> assertOnlyInfos() {
+ assertNotEquals(0, getDiagnosticMessages().getInfos().size());
+ assertEquals(0, getDiagnosticMessages().getWarnings().size());
+ assertEquals(0, getDiagnosticMessages().getErrors().size());
+ return this;
+ }
+
+ public TestCompileResult<RR> assertOnlyWarnings() {
+ assertEquals(0, getDiagnosticMessages().getInfos().size());
+ assertNotEquals(0, getDiagnosticMessages().getWarnings().size());
+ assertEquals(0, getDiagnosticMessages().getErrors().size());
+ return this;
+ }
+
+ public TestCompileResult<RR> assertWarningMessageThatMatches(Matcher<String> matcher) {
+ assertNotEquals(0, getDiagnosticMessages().getWarnings().size());
+ for (int i = 0; i < getDiagnosticMessages().getWarnings().size(); i++) {
+ if (matcher.matches(getDiagnosticMessages().getWarnings().get(i).getDiagnosticMessage())) {
+ return this;
+ }
+ }
+ StringBuilder builder = new StringBuilder("No warning matches " + matcher.toString());
+ builder.append(System.lineSeparator());
+ if (getDiagnosticMessages().getWarnings().size() == 0) {
+ builder.append("There where no warnings.");
+ } else {
+ builder.append("There where " + getDiagnosticMessages().getWarnings().size() + " warnings:");
+ builder.append(System.lineSeparator());
+ for (int i = 0; i < getDiagnosticMessages().getWarnings().size(); i++) {
+ builder.append(getDiagnosticMessages().getWarnings().get(i).getDiagnosticMessage());
+ builder.append(System.lineSeparator());
+ }
+ }
+ fail(builder.toString());
+ return this;
+ }
+
+ public TestCompileResult<RR> assertNoWarningMessageThatMatches(Matcher<String> matcher) {
+ assertNotEquals(0, getDiagnosticMessages().getWarnings().size());
+ for (int i = 0; i < getDiagnosticMessages().getWarnings().size(); i++) {
+ String message = getDiagnosticMessages().getWarnings().get(i).getDiagnosticMessage();
+ if (matcher.matches(message)) {
+ fail("The warning: \"" + message + "\" + matches " + matcher + ".");
+ }
+ }
+ return this;
+ }
+
public DebugTestConfig debugConfig() {
// Rethrow exceptions since debug config is usually used in a delayed wrapper which
// does not declare exceptions.
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
new file mode 100644
index 0000000..3e734cc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
@@ -0,0 +1,16 @@
+// 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;
+
+import java.util.List;
+
+public interface TestDiagnosticMessages {
+
+ public List<Diagnostic> getInfos();
+
+ public List<Diagnostic> getWarnings();
+
+ public List<Diagnostic> getErrors();
+}
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
new file mode 100644
index 0000000..d0476b9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
@@ -0,0 +1,41 @@
+// 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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestDiagnosticMessagesImpl implements DiagnosticsHandler, TestDiagnosticMessages {
+ private final List<Diagnostic> infos = new ArrayList<>();
+ private final List<Diagnostic> warnings = new ArrayList<>();
+ private final List<Diagnostic> errors = new ArrayList<>();
+
+ @Override
+ public void info(Diagnostic info) {
+ infos.add(info);
+ }
+
+ @Override
+ public void warning(Diagnostic warning) {
+ warnings.add(warning);
+ }
+
+ @Override
+ public void error(Diagnostic error) {
+ errors.add(error);
+ }
+
+ public List<Diagnostic> getInfos() {
+ return infos;
+ }
+
+ public List<Diagnostic> getWarnings() {
+ return warnings;
+ }
+
+ public List<Diagnostic> getErrors() {
+ return errors;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/TestState.java b/src/test/java/com/android/tools/r8/TestState.java
index d516c7c..bdb4e2f 100644
--- a/src/test/java/com/android/tools/r8/TestState.java
+++ b/src/test/java/com/android/tools/r8/TestState.java
@@ -10,6 +10,7 @@
public class TestState {
private final TemporaryFolder temp;
+ private final TestDiagnosticMessagesImpl messages = new TestDiagnosticMessagesImpl();
public TestState(TemporaryFolder temp) {
this.temp = temp;
@@ -18,4 +19,12 @@
public Path getNewTempFolder() throws IOException {
return temp.newFolder().toPath();
}
+
+ DiagnosticsHandler getDiagnosticsHandler() {
+ return messages;
+ }
+
+ public TestDiagnosticMessages getDiagnosticsMessages() {
+ return messages;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/cf/NonidenticalCatchHandlerTest.java b/src/test/java/com/android/tools/r8/cf/NonidenticalCatchHandlerTest.java
new file mode 100644
index 0000000..b778c41
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/NonidenticalCatchHandlerTest.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.cf.code.CfLabel;
+import com.android.tools.r8.cf.code.CfTryCatch;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
+import it.unimi.dsi.fastutil.ints.IntSet;
+import java.util.Set;
+import org.junit.Test;
+
+public class NonidenticalCatchHandlerTest extends TestBase {
+
+ private static class TestClass {
+ public void foo(Object a) {
+ int x = 0;
+ try {
+ x = ((String)a).length();
+ } catch (ClassCastException e) {
+ x = -1;
+ } catch (NullPointerException e) {
+ x = -1;
+ }
+ System.out.println(x);
+ }
+ }
+
+ @Test
+ public void test() throws Exception {
+ byte[] inputBytes = ToolHelper.getClassAsBytes(TestClass.class);
+ AndroidApp inputApp =
+ AndroidApp.builder()
+ .addClassProgramData(inputBytes, Origin.unknown())
+ .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
+ .build();
+ assertEquals(2, countCatchHandlers(inputApp));
+ AndroidApp outputDexApp = ToolHelper.runR8(inputApp);
+ assertEquals(1, countCatchHandlers(outputDexApp));
+ AndroidApp outputCfApp =
+ ToolHelper.runR8WithProgramConsumer(inputApp, ClassFileConsumer.emptyConsumer());
+ // ClassCastException and NullPointerException will not have same type on stack.
+ assertEquals(2, countCatchHandlers(outputCfApp));
+ }
+
+ private int countCatchHandlers(AndroidApp inputApp) throws Exception {
+ CodeInspector inspector = new CodeInspector(inputApp);
+ DexClass dexClass = inspector.clazz(TestClass.class).getDexClass();
+ Code code = dexClass.virtualMethods()[0].getCode();
+ if (code.isCfCode()) {
+ CfCode cfCode = code.asCfCode();
+ Set<CfLabel> targets = Sets.newIdentityHashSet();
+ for (CfTryCatch tryCatch : cfCode.getTryCatchRanges()) {
+ targets.addAll(tryCatch.targets);
+ }
+ return targets.size();
+ } else if (code.isDexCode()) {
+ DexCode dexCode = code.asDexCode();
+ IntSet targets = new IntOpenHashSet();
+ for (Try aTry : dexCode.tries) {
+ targets.add(aTry.handlerOffset);
+ }
+ return targets.size();
+ } else {
+ throw new Unimplemented(code.getClass().getName());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/configuration/WrongUseOfImplementsOrExtendsWarningTest.java b/src/test/java/com/android/tools/r8/configuration/WrongUseOfImplementsOrExtendsWarningTest.java
new file mode 100644
index 0000000..cc07ce3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/configuration/WrongUseOfImplementsOrExtendsWarningTest.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.configuration;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+
+public class WrongUseOfImplementsOrExtendsWarningTest extends TestBase {
+
+ @Test
+ public void testCorrectUseOfExtends() throws Exception {
+ CodeInspector inspector =
+ testForR8(Backend.DEX)
+ .addInnerClasses(WrongUseOfImplementsOrExtendsWarningTest.class)
+ .addKeepRules(
+ "-keep class " + B.class.getTypeName() + " extends " + A.class.getTypeName())
+ .compile()
+ .assertNoMessages()
+ .inspector();
+ assertThat(inspector.clazz(B.class), isPresent());
+ }
+
+ @Test
+ public void testWrongUseOfExtends() throws Exception {
+ CodeInspector inspector =
+ testForR8(Backend.DEX)
+ .addInnerClasses(WrongUseOfImplementsOrExtendsWarningTest.class)
+ .addKeepRules(
+ "-keep class " + B.class.getTypeName() + " extends " + I.class.getTypeName())
+ .compile()
+ .assertWarningMessageThatMatches(
+ containsString("uses extends but actually matches implements"))
+ .inspector();
+ assertThat(inspector.clazz(B.class), isPresent());
+ }
+
+ @Test
+ public void testUseOfExtendsWithWildcards() throws Exception {
+ CodeInspector inspector =
+ testForR8(Backend.DEX)
+ .addInnerClasses(WrongUseOfImplementsOrExtendsWarningTest.class)
+ .addKeepRules(
+ "-keep class " + B.class.getTypeName() + " extends **$" + I.class.getSimpleName())
+ .compile()
+ .assertNoMessages()
+ .inspector();
+ assertThat(inspector.clazz(B.class), isPresent());
+ }
+
+ @Test
+ public void testCorrectUseOfImplements() throws Exception {
+ CodeInspector inspector =
+ testForR8(Backend.DEX)
+ .addInnerClasses(WrongUseOfImplementsOrExtendsWarningTest.class)
+ .addKeepRules(
+ "-keep class " + B.class.getTypeName() + " implements " + I.class.getTypeName())
+ .compile()
+ .assertNoMessages()
+ .inspector();
+ assertThat(inspector.clazz(B.class), isPresent());
+ }
+
+ @Test
+ public void testWrongUseOfImplements() throws Exception {
+ CodeInspector inspector =
+ testForR8(Backend.DEX)
+ .addInnerClasses(WrongUseOfImplementsOrExtendsWarningTest.class)
+ .addKeepRules(
+ "-keep class " + B.class.getTypeName() + " implements " + A.class.getTypeName())
+ .compile()
+ .assertWarningMessageThatMatches(
+ containsString("uses implements but actually matches extends"))
+ .inspector();
+ assertThat(inspector.clazz(B.class), isPresent());
+ }
+
+ @Test
+ public void testUseOfImplementsWithWildcards() throws Exception {
+ CodeInspector inspector =
+ testForR8(Backend.DEX)
+ .addInnerClasses(WrongUseOfImplementsOrExtendsWarningTest.class)
+ .addKeepRules(
+ "-keep class "
+ + B.class.getTypeName()
+ + " implements **$"
+ + A.class.getSimpleName())
+ .compile()
+ .assertNoMessages()
+ .inspector();
+ assertThat(inspector.clazz(B.class), isPresent());
+ }
+
+ static class A {}
+
+ static class B extends A implements I {}
+
+ interface I {}
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/BuilderWithSelfReferenceTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/BuilderWithSelfReferenceTest.java
new file mode 100644
index 0000000..06659a6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/BuilderWithSelfReferenceTest.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.classinliner;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/** Regression test for b/120182628. */
+public class BuilderWithSelfReferenceTest extends TestBase {
+
+ @Test
+ @Ignore("b/120182628")
+ public void test() throws Exception {
+ testForR8(Backend.DEX)
+ .addInnerClasses(BuilderWithSelfReferenceTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .compile();
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new Builder().build();
+ }
+ }
+
+ static class Builder {
+
+ public Builder f = this;
+
+ public Object build() {
+ invoke(f);
+ return new Object();
+ }
+
+ @NeverInline
+ public static void invoke(Object o) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
index 5ed3cc5..12d0a78 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
@@ -51,6 +51,11 @@
}
@Override
+ public boolean hasEqualTypesAtEntry(BasicBlock first, BasicBlock second) {
+ return false;
+ }
+
+ @Override
public void addNewBlockToShareIdenticalSuffix(
BasicBlock block, int suffixSize, List<BasicBlock> predsBeforeSplit) {
// Intentionally empty, we don't need to track suffix sharing in this allocator.
diff --git a/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java b/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java
new file mode 100644
index 0000000..e6ca563
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.maindexlist.warnings;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.ForceInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.hamcrest.CoreMatchers;
+import org.junit.Test;
+
+public class MainDexWarningsTest extends TestBase {
+
+ private List<Class<?>> testClasses = ImmutableList.of(Main.class, Static.class, Static2.class);
+ private Class<?> mainClass = Main.class;
+
+ private void classStaticGone(CodeInspector inspector) {
+ assertThat(inspector.clazz(Static.class), CoreMatchers.not(isPresent()));
+ assertThat(inspector.clazz(Static2.class), CoreMatchers.not(isPresent()));
+ }
+
+ @Test
+ public void testNoWarningFromMainDexRules() throws Exception {
+ testForR8(Backend.DEX)
+ .setMinApi(AndroidApiLevel.K)
+ .enableInliningAnnotations()
+ .addProgramClasses(testClasses)
+ .addKeepMainRule(mainClass)
+ // Include main dex rule for class Static.
+ .addMainDexClassRules(Main.class, Static.class)
+ .compile()
+ .inspect(this::classStaticGone)
+ .assertNoMessages();
+ }
+
+ @Test
+ public void testWarningFromManualMainDexList() throws Exception {
+ testForR8(Backend.DEX)
+ .setMinApi(AndroidApiLevel.K)
+ .enableInliningAnnotations()
+ .addProgramClasses(testClasses)
+ .addKeepMainRule(mainClass)
+ // Include explicit main dex entry for class Static.
+ .addMainDexListClasses(Static.class)
+ .compile()
+ .inspect(this::classStaticGone)
+ .assertOnlyWarnings()
+ .assertWarningMessageThatMatches(containsString("Application does not contain"))
+ .assertWarningMessageThatMatches(containsString(Static.class.getTypeName()))
+ .assertWarningMessageThatMatches(containsString("as referenced in main-dex-list"));
+ }
+
+ @Test
+ public void testWarningFromManualMainDexListWithRuleAsWell() throws Exception {
+ testForR8(Backend.DEX)
+ .setMinApi(AndroidApiLevel.K)
+ .enableInliningAnnotations()
+ .addProgramClasses(testClasses)
+ .addKeepMainRule(mainClass)
+ // Include explicit main dex entry for class Static.
+ .addMainDexListClasses(Main.class, Static.class)
+ // Include main dex rule for class Static2.
+ .addMainDexClassRules(Static2.class)
+ .compile()
+ .inspect(this::classStaticGone)
+ .assertOnlyWarnings()
+ .assertWarningMessageThatMatches(containsString("Application does not contain"))
+ .assertWarningMessageThatMatches(containsString(Static.class.getTypeName()))
+ .assertWarningMessageThatMatches(containsString("as referenced in main-dex-list"))
+ .assertNoWarningMessageThatMatches(containsString(Static2.class.getTypeName()));
+ }
+}
+
+class Main {
+ public static void main(String[] args) {
+ System.out.println(Static.m());
+ System.out.println(Static2.m());
+ }
+}
+
+class Static {
+ @ForceInline
+ public static int m() {
+ return 1;
+ }
+}
+
+class Static2 {
+ @ForceInline
+ public static int m() {
+ return 1;
+ }
+}
diff --git a/tools/archive.py b/tools/archive.py
index 0fa811b..ad58bca 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -12,6 +12,7 @@
import toolhelper
import utils
import zipfile
+from build_r8lib import build_r8lib
ARCHIVE_BUCKET = 'r8-releases'
@@ -80,12 +81,17 @@
def Main():
if not 'BUILDBOT_BUILDERNAME' in os.environ:
raise Exception('You are not a bot, don\'t archive builds')
- # Create maven release first which uses a build that exclude dependencies.
+
+ # Generate an r8-ed build without dependencies.
+ # Note: build_r8lib does a gradle-clean, this must be the first command.
+ build_r8lib('r8', True, True, utils.R8LIB_KEEP_RULES, utils.R8_EXCLUDE_DEPS_JAR)
+
+ # Create maven release which uses a build that exclude dependencies.
create_maven_release.main(["--out", utils.LIBS])
- # Generate and copy the build that exclude dependencies.
+ # Generate and copy a full build without dependencies.
gradle.RunGradleExcludeDeps([utils.R8, utils.R8_SRC])
- shutil.copyfile(utils.R8_JAR, utils.R8_EXCLUDE_DEPS_JAR)
+ shutil.copyfile(utils.R8_JAR, utils.R8_FULL_EXCLUDE_DEPS_JAR)
# Ensure all archived artifacts has been built before archiving.
# The target tasks postfixed by 'r8' depend on the actual target task so
@@ -113,6 +119,7 @@
for file in [utils.D8_JAR, utils.D8R8_JAR,
utils.R8_JAR, utils.R8R8_JAR,
utils.R8_SRC_JAR,
+ utils.R8_FULL_EXCLUDE_DEPS_JAR,
utils.R8_EXCLUDE_DEPS_JAR,
utils.COMPATDX_JAR, utils.COMPATDXR8_JAR,
utils.COMPATPROGUARD_JAR, utils.COMPATPROGUARDR8_JAR,
diff --git a/tools/utils.py b/tools/utils.py
index 18f8870..d2c4935 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -40,6 +40,7 @@
R8R8_JAR = os.path.join(LIBS, 'r8-r8.jar')
R8_SRC_JAR = os.path.join(LIBS, 'r8-src.jar')
R8_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8-exclude-deps.jar')
+R8_FULL_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8-full-exclude-deps.jar')
COMPATDX_JAR = os.path.join(LIBS, 'compatdx.jar')
COMPATDXR8_JAR = os.path.join(LIBS, 'compatdx-r8.jar')
COMPATPROGUARD_JAR = os.path.join(LIBS, 'compatproguard.jar')