Version 1.0.21
Merge: Extend main dex list tracing to handle Class.forName
CL: https://r8-review.googlesource.com/c/r8/+/19062
Change-Id: I3fa479c65dfa5e4f84b1a491c97cc39d1b343257
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 4a3fc2e..a696aa4 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -8,14 +8,18 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.MainDexListBuilder;
+import com.android.tools.r8.shaking.ReasonPrinter;
import com.android.tools.r8.shaking.RootSetBuilder;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.shaking.TreePruner;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -37,9 +41,11 @@
AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
RootSet mainDexRootSet =
new RootSetBuilder(application, appInfo, options.mainDexKeepRules, options).run(executor);
- Set<DexType> mainDexBaseClasses =
- new Enqueuer(appInfo, options).traceMainDex(mainDexRootSet, timing);
- Set<DexType> mainDexClasses = new MainDexListBuilder(mainDexBaseClasses, application).run();
+ Enqueuer enqueuer = new Enqueuer(appInfo, options, true);
+ AppInfoWithLiveness mainDexAppInfo = enqueuer.traceMainDex(mainDexRootSet, timing);
+ // LiveTypes is the result.
+ Set<DexType> mainDexClasses =
+ new MainDexListBuilder(new HashSet<>(mainDexAppInfo.liveTypes), application).run();
List<String> result = mainDexClasses.stream()
.map(c -> c.toSourceString().replace('.', '/') + ".class")
@@ -50,6 +56,15 @@
options.mainDexListConsumer.accept(String.join("\n", result), options.reporter);
}
+ // Print -whyareyoukeeping results if any.
+ if (mainDexRootSet.reasonAsked.size() > 0) {
+ // Print reasons on the application after pruning, so that we reflect the actual result.
+ TreePruner pruner = new TreePruner(application, appInfo.withLiveness(), options);
+ application = pruner.run();
+ ReasonPrinter reasonPrinter = enqueuer.getReasonPrinter(mainDexRootSet.reasonAsked);
+ reasonPrinter.run(application);
+ }
+
return result;
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 9609280..9b7ccc1 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -35,6 +35,7 @@
import com.android.tools.r8.shaking.AnnotationRemover;
import com.android.tools.r8.shaking.DiscardedChecker;
import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.MainDexListBuilder;
import com.android.tools.r8.shaking.ProguardClassFilter;
import com.android.tools.r8.shaking.ProguardConfiguration;
@@ -272,7 +273,8 @@
.run(executorService);
ProtoLiteExtension protoLiteExtension =
options.forceProguardCompatibility ? null : new ProtoLiteExtension(appInfo);
- appInfo = new Enqueuer(appInfo, options, compatibility, protoLiteExtension)
+ appInfo = new Enqueuer(appInfo, options, options.forceProguardCompatibility,
+ compatibility, protoLiteExtension)
.traceApplication(rootSet, timing);
if (options.proguardConfiguration.isPrintSeeds()) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
@@ -378,12 +380,15 @@
if (!options.mainDexKeepRules.isEmpty()) {
appInfo = new AppInfoWithSubtyping(application);
- Enqueuer enqueuer = new Enqueuer(appInfo, options);
+ Enqueuer enqueuer = new Enqueuer(appInfo, options, true);
// Lets find classes which may have code executed before secondary dex files installation.
RootSet mainDexRootSet =
new RootSetBuilder(application, appInfo, options.mainDexKeepRules, options)
.run(executorService);
- Set<DexType> mainDexBaseClasses = enqueuer.traceMainDex(mainDexRootSet, timing);
+ AppInfoWithLiveness mainDexAppInfo = enqueuer.traceMainDex(mainDexRootSet, timing);
+
+ // LiveTypes is the result.
+ Set<DexType> mainDexBaseClasses = new HashSet<>(mainDexAppInfo.liveTypes);
// Calculate the automatic main dex list according to legacy multidex constraints.
// Add those classes to an eventual manual list of classes.
@@ -397,7 +402,7 @@
if (options.useTreeShaking || !options.skipMinification) {
timing.begin("Post optimization code stripping");
try {
- Enqueuer enqueuer = new Enqueuer(appInfo, options);
+ Enqueuer enqueuer = new Enqueuer(appInfo, options, options.forceProguardCompatibility);
appInfo = enqueuer.traceApplication(rootSet, timing);
if (options.useTreeShaking) {
TreePruner pruner = new TreePruner(application, appInfo.withLiveness(), options);
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index a528e39..398d25d 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 = "v1.0.20";
+ public static final String LABEL = "v1.0.21";
private Version() {
}
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 d674ccb..db9b78c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -399,6 +399,10 @@
return createType(createString(descriptor));
}
+ synchronized public DexType lookupType(DexString descriptor) {
+ return types.get(descriptor);
+ }
+
public DexType createArrayType(int nesting, DexType baseType) {
assert nesting > 0;
return createType(Strings.repeat("[", nesting) + baseType.toDescriptorString());
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 9aff920..90bd458 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -78,6 +78,8 @@
* field descriptions for details.
*/
public class Enqueuer {
+
+ private final boolean forceProguardCompatibility;
private boolean tracingMainDex = false;
private final AppInfoWithSubtyping appInfo;
@@ -187,16 +189,19 @@
*/
private final ProguardConfiguration.Builder compatibility;
- public Enqueuer(AppInfoWithSubtyping appInfo, InternalOptions options) {
- this(appInfo, options, null, null);
+ public Enqueuer(AppInfoWithSubtyping appInfo, InternalOptions options,
+ boolean forceProguardCompatibility) {
+ this(appInfo, options, forceProguardCompatibility, null, null);
}
public Enqueuer(AppInfoWithSubtyping appInfo, InternalOptions options,
+ boolean forceProguardCompatibility,
ProguardConfiguration.Builder compatibility, ProtoLiteExtension protoLiteExtension) {
this.appInfo = appInfo;
this.compatibility = compatibility;
this.options = options;
this.protoLiteExtension = protoLiteExtension;
+ this.forceProguardCompatibility = forceProguardCompatibility;
}
private void enqueueRootItems(Map<DexItem, ProguardKeepRule> items) {
@@ -271,7 +276,7 @@
@Override
public boolean registerInvokeStatic(DexMethod method) {
- if (options.forceProguardCompatibility
+ if (forceProguardCompatibility
&& method == appInfo.dexItemFactory.classMethods.forName) {
pendingProguardReflectiveCompatibility.add(currentMethod);
}
@@ -440,7 +445,7 @@
enqueueRootItems(rootSet.getDependentStaticMembers(type));
// For Proguard compatibility keep the default initializer for live types.
- if (options.forceProguardCompatibility) {
+ if (forceProguardCompatibility) {
if (holder.isProgramClass() && holder.hasDefaultInitializer()) {
markClassAsInstantiatedWithCompatRule(holder);
}
@@ -512,7 +517,7 @@
Diagnostic message = new StringDiagnostic("Library class " + context.toSourceString()
+ (holder.isInterface() ? " implements " : " extends ")
+ "program class " + type.toSourceString());
- if (options.forceProguardCompatibility) {
+ if (forceProguardCompatibility) {
options.reporter.warning(message);
} else {
options.reporter.error(message);
@@ -544,7 +549,7 @@
Log.verbose(getClass(), "Method `%s` is targeted.", encodedMethod.method);
}
targetedMethods.add(encodedMethod, reason);
- if (options.forceProguardCompatibility) {
+ if (forceProguardCompatibility) {
// Keep targeted default methods in compatibility mode. The tree pruner will otherwise make
// these methods abstract, whereas Proguard does not (seem to) touch their code.
DexClass clazz = appInfo.definitionFor(encodedMethod.method.holder);
@@ -950,15 +955,14 @@
reachability, instantiatedTypes.getReasons());
}
- public Set<DexType> traceMainDex(RootSet rootSet, Timing timing) {
+ public AppInfoWithLiveness traceMainDex(RootSet rootSet, Timing timing) {
this.tracingMainDex = true;
this.rootSet = rootSet;
// Translate the result of root-set computation into enqueuer actions.
enqueueRootItems(rootSet.noShrinking);
AppInfoWithLiveness appInfo = trace(timing);
options.reporter.failIfPendingErrors();
- // LiveTypes is the result, just make a copy because further work will modify its content.
- return new HashSet<>(appInfo.liveTypes);
+ return appInfo;
}
public AppInfoWithLiveness traceApplication(RootSet rootSet, Timing timing) {
diff --git a/src/test/examples/multidex005/ReflectionReference.java b/src/test/examples/multidex005/ReflectionReference.java
new file mode 100644
index 0000000..791e7d2
--- /dev/null
+++ b/src/test/examples/multidex005/ReflectionReference.java
@@ -0,0 +1,18 @@
+// 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 multidex005;
+
+public class ReflectionReference {
+ Class directlyReferencedClass;
+
+ public ReflectionReference() throws ClassNotFoundException {
+ directlyReferencedClass = Class.forName("multidex005.ClassReference");
+ }
+
+ public Object noReference() throws ClassNotFoundException {
+ return Class.forName("multidex005.IndirectlyReferenced");
+ }
+
+}
diff --git a/src/test/examples/multidex005/main-dex-rules-7.txt b/src/test/examples/multidex005/main-dex-rules-7.txt
new file mode 100644
index 0000000..84120e0
--- /dev/null
+++ b/src/test/examples/multidex005/main-dex-rules-7.txt
@@ -0,0 +1,7 @@
+# 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 *.ReflectionReference {
+ <init>();
+}
diff --git a/src/test/examples/multidex005/ref-list-7.txt b/src/test/examples/multidex005/ref-list-7.txt
new file mode 100644
index 0000000..5f01577
--- /dev/null
+++ b/src/test/examples/multidex005/ref-list-7.txt
@@ -0,0 +1,9 @@
+Lmultidex005/ClassReference;
+Lmultidex005/DirectlyReferenced;
+Lmultidex005/Interface1;
+Lmultidex005/Interface2;
+Lmultidex005/Interface3;
+Lmultidex005/ReflectionReference;
+Lmultidex005/SuperClass;
+Lmultidex005/SuperInterface;
+Lmultidex005/SuperSuperClass;
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index 8c57058..263aff7 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -125,6 +125,11 @@
}
@Test
+ public void traceMainDexList005_7() throws Throwable {
+ doTest5(7);
+ }
+
+ @Test
public void traceMainDexList006() throws Throwable {
doTest(
"traceMainDexList006",
@@ -137,7 +142,7 @@
private void doTest5(int variant) throws Throwable {
doTest(
- "traceMainDexList003",
+ "traceMainDexList005",
"multidex005",
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex005", "main-dex-rules-" + variant + ".txt"),
diff --git a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
index b55aea0..1b1f823 100644
--- a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -76,7 +76,7 @@
RootSet rootSet = new RootSetBuilder(program, appInfo, configuration.getRules(), options)
.run(ThreadUtils.getExecutorService(options));
- Enqueuer enqueuer = new Enqueuer(appInfo, options);
+ Enqueuer enqueuer = new Enqueuer(appInfo, options, options.forceProguardCompatibility);
appInfo = enqueuer.traceApplication(rootSet, timing);
return new Minifier(appInfo.withLiveness(), rootSet, options).run(timing);
}