Version 1.0.29
Merge: Keep fewer default constructors in Proguard compat mode
CL: https://r8-review.googlesource.com/c/r8/+/18121
Merge: Only add keep rule when there is a default ctor
CL: https://r8-review.googlesource.com/c/r8/+/21621
Merge: Fix use of -whyareyoukeeping with main dex rules
CL: https://r8-review.googlesource.com/c/r8/+/21240
Bug: 78678415
Change-Id: If975d4be4ec6bd8c054794f636f7fc60a25fddd2
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index a696aa4..84a7929 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -59,7 +59,7 @@
// 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);
+ TreePruner pruner = new TreePruner(application, mainDexAppInfo.withLiveness(), options);
application = pruner.run();
ReasonPrinter reasonPrinter = enqueuer.getReasonPrinter(mainDexRootSet.reasonAsked);
reasonPrinter.run(application);
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 6a39500..6b1ed51 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.28";
+ public static final String LABEL = "v1.0.29";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/code/CheckCast.java b/src/main/java/com/android/tools/r8/code/CheckCast.java
index d3576c1..9416a7b 100644
--- a/src/main/java/com/android/tools/r8/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/code/CheckCast.java
@@ -39,7 +39,7 @@
@Override
public void registerUse(UseRegistry registry) {
- registry.registerTypeReference(getType());
+ registry.registerCheckCast(getType());
}
public DexType getType() {
diff --git a/src/main/java/com/android/tools/r8/code/ConstClass.java b/src/main/java/com/android/tools/r8/code/ConstClass.java
index f140022..ef8b257 100644
--- a/src/main/java/com/android/tools/r8/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/code/ConstClass.java
@@ -39,7 +39,7 @@
@Override
public void registerUse(UseRegistry registry) {
- registry.registerTypeReference(getType());
+ registry.registerConstClass(getType());
}
public DexType getType() {
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index 78b2f56..48ef91d 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -27,6 +27,14 @@
public abstract boolean registerTypeReference(DexType type);
+ public boolean registerConstClass(DexType type) {
+ return registerTypeReference(type);
+ }
+
+ public boolean registerCheckCast(DexType type) {
+ return registerTypeReference(type);
+ }
+
public void registerMethodHandle(DexMethodHandle methodHandle) {
switch (methodHandle.type) {
case INSTANCE_GET:
diff --git a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
index f9a7f49..37c9aea 100644
--- a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
+++ b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
@@ -35,6 +35,8 @@
DexType type = application.getTypeFromName(name);
if (opcode == org.objectweb.asm.Opcodes.NEW) {
registry.registerNewInstance(type);
+ } else if (opcode == Opcodes.CHECKCAST) {
+ registry.registerCheckCast(type);
} else {
registry.registerTypeReference(type);
}
@@ -51,7 +53,7 @@
// Nothing to register for method type, it represents only a prototype not associated with a
// method name.
if (((Type) cst).getSort() != Type.METHOD) {
- registry.registerTypeReference(application.getType((Type) cst));
+ registry.registerConstClass(application.getType((Type) cst));
}
} else if (cst instanceof Handle) {
registerMethodHandleType((Handle) cst);
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 71ffe54..5200d06 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -205,11 +205,30 @@
}
private void enqueueRootItems(Map<DexItem, ProguardKeepRule> items) {
- workList.addAll(
- items.entrySet().stream().map(Action::forRootItem).collect(Collectors.toList()));
+ items.entrySet().forEach(this::enqueueRootItem);
pinnedItems.addAll(items.keySet());
}
+ private void enqueueRootItem(Map.Entry<DexItem, ProguardKeepRule> root) {
+ DexItem item = root.getKey();
+ KeepReason reason = KeepReason.dueToKeepRule(root.getValue());
+ if (item instanceof DexClass) {
+ DexClass clazz = (DexClass) item;
+ workList.add(Action.markInstantiated(clazz, reason));
+ if (options.forceProguardCompatibility && clazz.hasDefaultInitializer()) {
+ ProguardKeepRule rule = ProguardConfigurationUtils.buildDefaultInitializerKeepRule(clazz);
+ proguardCompatibilityWorkList.add(Action.markMethodLive(
+ clazz.getDefaultInitializer(), KeepReason.dueToProguardCompatibilityKeepRule(rule)));
+ }
+ } else if (item instanceof DexEncodedField) {
+ workList.add(Action.markFieldKept((DexEncodedField) item, reason));
+ } else if (item instanceof DexEncodedMethod) {
+ workList.add(Action.markMethodKept((DexEncodedMethod) item, reason));
+ } else {
+ throw new IllegalArgumentException(item.toString());
+ }
+ }
+
//
// Things to do with registering events. This is essentially the interface for byte-code
// traversals.
@@ -374,6 +393,16 @@
}
@Override
+ public boolean registerConstClass(DexType type) {
+ return registerConstClassOrCheckCast(type);
+ }
+
+ @Override
+ public boolean registerCheckCast(DexType type) {
+ return registerConstClassOrCheckCast(type);
+ }
+
+ @Override
public boolean registerTypeReference(DexType type) {
DexType baseType = type.toBaseType(appInfo.dexItemFactory);
if (baseType.isClassType()) {
@@ -382,6 +411,26 @@
}
return false;
}
+
+ private boolean registerConstClassOrCheckCast(DexType type) {
+ if (options.forceProguardCompatibility) {
+ DexType baseType = type.toBaseType(appInfo.dexItemFactory);
+ if (baseType.isClassType()) {
+ DexClass baseClass = appInfo.definitionFor(baseType);
+ if (baseClass != null && baseClass.isProgramClass()
+ && baseClass.hasDefaultInitializer()) {
+ markClassAsInstantiatedWithCompatRule(baseClass);
+ } else {
+ // This also handles reporting of missing classes.
+ markTypeAsLive(baseType);
+ }
+ return true;
+ }
+ return false;
+ } else {
+ return registerTypeReference(type);
+ }
+ }
}
private DexMethod getInvokeSuperTarget(DexMethod method, DexEncodedMethod currentMethod) {
@@ -441,14 +490,12 @@
annotations.forEach(this::handleAnnotationOfLiveType);
}
- // Add all dependent static members to the workqueue.
- enqueueRootItems(rootSet.getDependentStaticMembers(type));
-
- // For Proguard compatibility keep the default initializer for live types.
- if (forceProguardCompatibility) {
- if (holder.isProgramClass() && holder.hasDefaultInitializer()) {
- markClassAsInstantiatedWithCompatRule(holder);
- }
+ if (options.forceProguardCompatibility) {
+ // Add all dependent members to the workqueue.
+ enqueueRootItems(rootSet.getDependentItems(type));
+ } else {
+ // Add all dependent static members to the workqueue.
+ enqueueRootItems(rootSet.getDependentStaticMembers(type));
}
}
}
@@ -568,6 +615,7 @@
if (!instantiatedTypes.add(clazz.type, reason)) {
return;
}
+
collectProguardCompatibilityRule(reason);
if (Log.ENABLED) {
Log.verbose(getClass(), "Class `%s` is instantiated, processing...", clazz);
@@ -1287,20 +1335,6 @@
return new Action(Kind.MARK_FIELD_KEPT, method, null, reason);
}
- public static Action forRootItem(Map.Entry<DexItem, ProguardKeepRule> root) {
- DexItem item = root.getKey();
- KeepReason reason = KeepReason.dueToKeepRule(root.getValue());
- if (item instanceof DexClass) {
- return markInstantiated((DexClass) item, reason);
- } else if (item instanceof DexEncodedField) {
- return markFieldKept((DexEncodedField) item, reason);
- } else if (item instanceof DexEncodedMethod) {
- return markMethodKept((DexEncodedMethod) item, reason);
- } else {
- throw new IllegalArgumentException(item.toString());
- }
- }
-
private enum Kind {
MARK_REACHABLE_VIRTUAL,
MARK_REACHABLE_INTERFACE,
diff --git a/src/test/java/com/android/tools/r8/shaking/keepclassmembers/KeepClassMembersTest.java b/src/test/java/com/android/tools/r8/shaking/keepclassmembers/KeepClassMembersTest.java
deleted file mode 100644
index 7904400..0000000
--- a/src/test/java/com/android/tools/r8/shaking/keepclassmembers/KeepClassMembersTest.java
+++ /dev/null
@@ -1,72 +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.shaking.keepclassmembers;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.google.common.collect.ImmutableList;
-import org.junit.Test;
-
-public class KeepClassMembersTest extends TestBase {
-
- public void runTest(Class mainClass, Class<?> staticClass,
- boolean forceProguardCompatibility) throws Exception {
- boolean staticClassHasDefaultConstructor = true;
- try {
- staticClass.getDeclaredConstructor();
- } catch (NoSuchMethodException e) {
- staticClassHasDefaultConstructor = false;
- }
- String proguardConfig = String.join("\n", ImmutableList.of(
- "-keepclassmembers class **.PureStatic* {",
- " public static int b;",
- " public static int getA();",
- " public int getI();",
- "}",
- "-keep class **.MainUsing* {",
- " public static void main(java.lang.String[]);",
- "}",
- "-dontoptimize", "-dontobfuscate"
- ));
- DexInspector inspector = new DexInspector(
- compileWithR8(ImmutableList.of(mainClass, staticClass), proguardConfig,
- options -> options.forceProguardCompatibility = forceProguardCompatibility));
- assertTrue(inspector.clazz(mainClass).isPresent());
- ClassSubject staticClassSubject = inspector.clazz(staticClass);
- assertTrue(staticClassSubject.isPresent());
- assertTrue(staticClassSubject.method("int", "getA", ImmutableList.of()).isPresent());
- assertFalse(staticClassSubject.method("int", "getB", ImmutableList.of()).isPresent());
- assertTrue(staticClassSubject.field("int", "a").isPresent());
- assertTrue(staticClassSubject.field("int", "b").isPresent());
- assertFalse(staticClassSubject.field("int", "c").isPresent());
- // Force Proguard compatibility keeps the default constructor if present and then assumes
- // instantiated, hence keeps the instance method as well.
- assertEquals(forceProguardCompatibility && staticClassHasDefaultConstructor,
- staticClassSubject.init(ImmutableList.of()).isPresent());
- assertEquals(forceProguardCompatibility && staticClassHasDefaultConstructor,
- staticClassSubject.method("int", "getI", ImmutableList.of()).isPresent());
- assertEquals(forceProguardCompatibility && staticClassHasDefaultConstructor,
- staticClassSubject.field("int", "i").isPresent());
- assertFalse(staticClassSubject.method("int", "getJ", ImmutableList.of()).isPresent());
- assertFalse(staticClassSubject.field("int", "j").isPresent());
- }
-
- @Test
- public void regress69028743() throws Exception {
- runTest(MainUsingWithDefaultConstructor.class,
- PureStaticClassWithDefaultConstructor.class, false);
- runTest(MainUsingWithDefaultConstructor.class,
- PureStaticClassWithDefaultConstructor.class, true);
- runTest(MainUsingWithoutDefaultConstructor.class,
- PureStaticClassWithoutDefaultConstructor.class, false);
- runTest(MainUsingWithoutDefaultConstructor.class,
- PureStaticClassWithoutDefaultConstructor.class, true);
- }
-}