Merge "Don't generate compat rules when tracing reflective use"
diff --git a/src/main/java/com/android/tools/r8/experimental/graphinfo/GraphEdgeInfo.java b/src/main/java/com/android/tools/r8/experimental/graphinfo/GraphEdgeInfo.java
index 0c838ac..e3fa01b 100644
--- a/src/main/java/com/android/tools/r8/experimental/graphinfo/GraphEdgeInfo.java
+++ b/src/main/java/com/android/tools/r8/experimental/graphinfo/GraphEdgeInfo.java
@@ -16,9 +16,11 @@
InvokedFrom,
InvokedFromLambdaCreatedIn,
ReferencedFrom,
+ ReflectiveUseFrom,
ReachableFromLiveType,
ReferencedInAnnotation,
IsLibraryMethod,
+ MethodHandleUseFrom
}
private final EdgeKind kind;
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 f10d000..673314d 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -604,7 +604,7 @@
if (methodHandle.isMethodHandle() && use != MethodHandleUse.ARGUMENT_TO_LAMBDA_METAFACTORY) {
DexClass holder = appInfo.definitionFor(methodHandle.asMethod().holder);
if (holder != null) {
- markClassAsInstantiatedWithMethodHandleRule(holder);
+ markInstantiated(holder.type, KeepReason.methodHandleReferencedIn(currentMethod));
}
}
}
@@ -1189,8 +1189,10 @@
private void markVirtualMethodAsLive(DexEncodedMethod method, KeepReason reason) {
assert method != null;
- // Only explicit keep rules should make abstract methods live.
- assert !method.accessFlags.isAbstract() || reason.isDueToKeepRule();
+ // Only explicit keep rules or reflective use should make abstract methods live.
+ assert !method.accessFlags.isAbstract()
+ || reason.isDueToKeepRule()
+ || reason.isDueToReflectiveUse();
if (!liveMethods.contains(method)) {
if (Log.ENABLED) {
Log.verbose(getClass(), "Adding virtual method `%s` to live set.", method.method);
@@ -1662,16 +1664,8 @@
collectReachedFields(staticFields, this::tryLookupStaticField)));
}
- private void markClassAsInstantiatedWithMethodHandleRule(DexClass clazz) {
- ProguardKeepRule rule =
- ProguardConfigurationUtils.buildMethodHandleKeepRule(clazz);
- proguardCompatibilityWorkList.add(
- Action.markInstantiated(clazz, KeepReason.dueToProguardCompatibilityKeepRule(rule)));
- }
-
private void markClassAsInstantiatedWithCompatRule(DexClass clazz) {
- ProguardKeepRule rule =
- ProguardConfigurationUtils.buildDefaultInitializerKeepRule(clazz);
+ ProguardKeepRule rule = ProguardConfigurationUtils.buildDefaultInitializerKeepRule(clazz);
proguardCompatibilityWorkList.add(
Action.markInstantiated(clazz, KeepReason.dueToProguardCompatibilityKeepRule(rule)));
if (clazz.hasDefaultInitializer()) {
@@ -1681,19 +1675,6 @@
}
}
- private void markFieldAsKeptWithCompatRule(DexEncodedField field, boolean keepClass) {
- DexClass holderClass = appInfo.definitionFor(field.field.getHolder());
- ProguardKeepRule rule =
- ProguardConfigurationUtils.buildFieldKeepRule(holderClass, field, keepClass);
- if (keepClass) {
- proguardCompatibilityWorkList.add(
- Action.markInstantiated(
- holderClass, KeepReason.dueToProguardCompatibilityKeepRule(rule)));
- }
- proguardCompatibilityWorkList.add(
- Action.markFieldKept(field, KeepReason.dueToProguardCompatibilityKeepRule(rule)));
- }
-
private void markMethodAsKeptWithCompatRule(DexEncodedMethod method) {
DexClass holderClass = appInfo.definitionFor(method.method.getHolder());
ProguardKeepRule rule =
@@ -1733,7 +1714,11 @@
if (identifierItem.isDexType()) {
DexClass clazz = appInfo.definitionFor(identifierItem.asDexType());
if (clazz != null) {
- markClassAsInstantiatedWithCompatRule(clazz);
+ markInstantiated(clazz.type, KeepReason.reflectiveUseIn(method));
+ if (clazz.hasDefaultInitializer()) {
+ markDirectStaticOrConstructorMethodAsLive(
+ clazz.getDefaultInitializer(), KeepReason.reflectiveUseIn(method));
+ }
}
} else if (identifierItem.isDexField()) {
DexEncodedField encodedField = appInfo.definitionFor(identifierItem.asDexField());
@@ -1746,13 +1731,22 @@
boolean keepClass =
!encodedField.accessFlags.isStatic()
&& appInfo.dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod);
- markFieldAsKeptWithCompatRule(encodedField, keepClass);
+ if (keepClass) {
+ DexClass holderClass = appInfo.definitionFor(encodedField.field.getHolder());
+ markInstantiated(holderClass.type, KeepReason.reflectiveUseIn(method));
+ }
+ markFieldAsKept(encodedField, KeepReason.reflectiveUseIn(method));
}
} else {
assert identifierItem.isDexMethod();
DexEncodedMethod encodedMethod = appInfo.definitionFor(identifierItem.asDexMethod());
if (encodedMethod != null) {
- markMethodAsKeptWithCompatRule(encodedMethod);
+ if (encodedMethod.accessFlags.isStatic() || encodedMethod.accessFlags.isConstructor()) {
+ markDirectStaticOrConstructorMethodAsLive(
+ encodedMethod, KeepReason.reflectiveUseIn(method));
+ } else {
+ markVirtualMethodAsLive(encodedMethod, KeepReason.reflectiveUseIn(method));
+ }
}
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepReason.java b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
index 16d78a7..a797742 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepReason.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
@@ -61,6 +61,10 @@
return false;
}
+ public boolean isDueToReflectiveUse() {
+ return false;
+ }
+
public boolean isDueToProguardCompatibility() {
return false;
}
@@ -73,6 +77,14 @@
return new TargetedBySuper(from);
}
+ public static KeepReason reflectiveUseIn(DexEncodedMethod method) {
+ return new ReflectiveUseFrom(method);
+ }
+
+ public static KeepReason methodHandleReferencedIn(DexEncodedMethod method) {
+ return new MethodHandleReferencedFrom(method);
+ }
+
private static class DueToKeepRule extends KeepReason {
final ProguardKeepRule keepRule;
@@ -289,4 +301,43 @@
return enqueuer.getAnnotationGraphNode(holder);
}
}
+
+ private static class ReflectiveUseFrom extends BasedOnOtherMethod {
+
+ private ReflectiveUseFrom(DexEncodedMethod method) {
+ super(method);
+ }
+
+ @Override
+ public boolean isDueToReflectiveUse() {
+ return true;
+ }
+
+ @Override
+ public EdgeKind edgeKind() {
+ return EdgeKind.ReflectiveUseFrom;
+ }
+
+ @Override
+ String getKind() {
+ return "reflective use in";
+ }
+ }
+
+ private static class MethodHandleReferencedFrom extends BasedOnOtherMethod {
+
+ private MethodHandleReferencedFrom(DexEncodedMethod method) {
+ super(method);
+ }
+
+ @Override
+ public EdgeKind edgeKind() {
+ return EdgeKind.MethodHandleUseFrom;
+ }
+
+ @Override
+ String getKind() {
+ return "method handle referenced from";
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index 163c60b..f9846b8 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -5,13 +5,8 @@
package com.android.tools.r8.shaking;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexReference;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -31,19 +26,6 @@
}
};
- public static ProguardKeepRule buildMethodHandleKeepRule(DexClass clazz) {
- ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
- builder.setOrigin(proguardCompatOrigin);
- builder.setType(ProguardKeepRuleType.KEEP);
- builder.getModifiersBuilder().setAllowsObfuscation(true);
- builder.getModifiersBuilder().setAllowsOptimization(true);
- builder.setClassType(
- clazz.isInterface() ? ProguardClassType.INTERFACE : ProguardClassType.CLASS);
- builder.setClassNames(
- ProguardClassNameList.singletonList(ProguardTypeMatcher.create(clazz.type)));
- return builder.build();
- }
-
public static ProguardKeepRule buildDefaultInitializerKeepRule(DexClass clazz) {
ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
builder.setOrigin(proguardCompatOrigin);
@@ -64,36 +46,6 @@
return builder.build();
}
- public static ProguardKeepRule buildFieldKeepRule(
- DexClass clazz, DexEncodedField field, boolean keepClass) {
- assert clazz.type == field.field.getHolder();
- ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
- builder.setOrigin(proguardCompatOrigin);
- if (keepClass) {
- builder.setType(ProguardKeepRuleType.KEEP);
- } else {
- builder.setType(ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
- }
- builder.getModifiersBuilder().setAllowsObfuscation(true);
- builder.getModifiersBuilder().setAllowsOptimization(true);
- builder.getClassAccessFlags().setVisibility(clazz.accessFlags);
- if (clazz.isInterface()) {
- builder.setClassType(ProguardClassType.INTERFACE);
- } else {
- builder.setClassType(ProguardClassType.CLASS);
- }
- builder.setClassNames(
- ProguardClassNameList.singletonList(ProguardTypeMatcher.create(clazz.type)));
- ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
- memberRuleBuilder.setRuleType(ProguardMemberType.FIELD);
- memberRuleBuilder.getAccessFlags().setFlags(field.accessFlags);
- memberRuleBuilder.setName(
- IdentifierPatternWithWildcards.withoutWildcards(field.field.name.toString()));
- memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(field.field.type));
- builder.getMemberRules().add(memberRuleBuilder.build());
- return builder.build();
- }
-
public static ProguardKeepRule buildMethodKeepRule(DexClass clazz, DexEncodedMethod method) {
// TODO(b/122295241): These generated rules should be linked into the graph, eg, the method
// using identified reflection should be the source keeping the target alive.
@@ -125,41 +77,6 @@
return builder.build();
}
- public static ProguardIdentifierNameStringRule buildIdentifierNameStringRule(DexReference item) {
- assert item.isDexField() || item.isDexMethod();
- ProguardIdentifierNameStringRule.Builder builder = ProguardIdentifierNameStringRule.builder();
- ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
- DexType holderType;
- if (item.isDexField()) {
- DexField field = item.asDexField();
- holderType = field.getHolder();
- memberRuleBuilder.setRuleType(ProguardMemberType.FIELD);
- memberRuleBuilder.setName(
- IdentifierPatternWithWildcards.withoutWildcards(field.name.toString()));
- memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(field.type));
- } else {
- DexMethod method = item.asDexMethod();
- holderType = method.getHolder();
- memberRuleBuilder.setRuleType(ProguardMemberType.METHOD);
- memberRuleBuilder.setName(
- IdentifierPatternWithWildcards.withoutWildcards(method.name.toString()));
- memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(method.proto.returnType));
- List<ProguardTypeMatcher> arguments = Arrays.stream(method.proto.parameters.values)
- .map(ProguardTypeMatcher::create)
- .collect(Collectors.toList());
- memberRuleBuilder.setArguments(arguments);
- }
- if (holderType.isInterface()) {
- builder.setClassType(ProguardClassType.INTERFACE);
- } else {
- builder.setClassType(ProguardClassType.CLASS);
- }
- builder.setClassNames(
- ProguardClassNameList.singletonList(ProguardTypeMatcher.create(holderType)));
- builder.getMemberRules().add(memberRuleBuilder.build());
- return builder.build();
- }
-
public static ProguardAssumeValuesRule buildAssumeValuesForApiLevel(
DexItemFactory factory, AndroidApiLevel apiLevel) {
Origin synthesizedFromApiLevel =
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index 6504169..906aee9 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -23,8 +23,6 @@
import com.android.tools.r8.shaking.ProguardConfigurationParser;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.shaking.ProguardKeepRule;
-import com.android.tools.r8.shaking.ProguardKeepRuleType;
import com.android.tools.r8.shaking.ProguardMemberRule;
import com.android.tools.r8.shaking.ProguardMemberType;
import com.android.tools.r8.shaking.forceproguardcompatibility.defaultmethods.ClassImplementingInterface;
@@ -40,8 +38,6 @@
import com.android.tools.r8.utils.codeinspector.FieldSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
-import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
-import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -277,39 +273,6 @@
assertEquals(subject.isPresent() && allowObfuscation, subject.isRenamed());
});
- // Check the Proguard compatibility rules generated.
- ProguardConfigurationParser parser =
- new ProguardConfigurationParser(new DexItemFactory(), new Reporter());
- parser.parse(proguardCompatibilityRules);
- ProguardConfiguration configuration = parser.getConfigRawForTesting();
- if (forceProguardCompatibility) {
- List<ProguardConfigurationRule> rules = configuration.getRules();
- assertEquals(2, rules.size());
- for (ProguardConfigurationRule r : rules) {
- assertTrue(r instanceof ProguardKeepRule);
- ProguardKeepRule rule = (ProguardKeepRule) r;
- assertEquals(ProguardKeepRuleType.KEEP, rule.getType());
- assertTrue(rule.getModifiers().allowsObfuscation);
- assertTrue(rule.getModifiers().allowsOptimization);
- List<ProguardMemberRule> memberRules = rule.getMemberRules();
- ProguardClassNameList classNames = rule.getClassNames();
- assertEquals(1, classNames.size());
- DexType type = classNames.asSpecificDexTypes().get(0);
- if (type.toSourceString().equals(forNameClass1.getCanonicalName())) {
- assertEquals(1, memberRules.size());
- assertEquals(ProguardMemberType.INIT, memberRules.iterator().next().getRuleType());
- } else {
- assertTrue(type.toSourceString().equals(forNameClass2.getCanonicalName()));
- // During parsing we add in the default constructor if there are otherwise no single
- // member rule.
- assertEquals(1, memberRules.size());
- assertEquals(ProguardMemberType.INIT, memberRules.iterator().next().getRuleType());
- }
- }
- } else {
- assertEquals(0, configuration.getRules().size());
- }
-
if (isRunProguard()) {
Path proguardedJar = File.createTempFile("proguarded", ".jar", temp.getRoot()).toPath();
Path proguardConfigFile = File.createTempFile("proguard", ".config", temp.getRoot()).toPath();
@@ -376,38 +339,6 @@
assertTrue(bar.isPresent());
assertEquals(bar.isPresent() && allowObfuscation, bar.isRenamed());
- // Check the Proguard compatibility rules generated.
- ProguardConfigurationParser parser =
- new ProguardConfigurationParser(new DexItemFactory(), new Reporter());
- parser.parse(proguardCompatibilityRules);
- ProguardConfiguration configuration = parser.getConfigRawForTesting();
- if (forceProguardCompatibility) {
- List<ProguardConfigurationRule> rules = configuration.getRules();
- assertEquals(2, rules.size());
- for (ProguardConfigurationRule r : rules) {
- assertTrue(r instanceof ProguardKeepRule);
- ProguardKeepRule rule = (ProguardKeepRule) r;
- assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, rule.getType());
- assertTrue(rule.getModifiers().allowsObfuscation);
- assertTrue(rule.getModifiers().allowsOptimization);
- List<ProguardMemberRule> memberRules = rule.getMemberRules();
- ProguardClassNameList classNames = rule.getClassNames();
- assertEquals(1, classNames.size());
- DexType type = classNames.asSpecificDexTypes().get(0);
- assertEquals(withMemberClass.getCanonicalName(), type.toSourceString());
- assertEquals(1, memberRules.size());
- ProguardMemberRule memberRule = memberRules.iterator().next();
- if (memberRule.getRuleType() == ProguardMemberType.FIELD) {
- assertTrue(memberRule.getName().matches("foo"));
- } else {
- assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType());
- assertTrue(memberRule.getName().matches("bar"));
- }
- }
- } else {
- assertEquals(0, configuration.getRules().size());
- }
-
if (isRunProguard()) {
Path proguardedJar = File.createTempFile("proguarded", ".jar", temp.getRoot()).toPath();
Path proguardConfigFile = File.createTempFile("proguard", ".config", temp.getRoot()).toPath();
@@ -481,39 +412,6 @@
assertTrue(f.isPresent());
assertEquals(f.isPresent() && allowObfuscation, f.isRenamed());
- // Check the Proguard compatibility rules generated.
- ProguardConfigurationParser parser =
- new ProguardConfigurationParser(new DexItemFactory(), new Reporter());
- parser.parse(proguardCompatibilityRules);
- ProguardConfiguration configuration = parser.getConfigRawForTesting();
- if (forceProguardCompatibility) {
- List<ProguardConfigurationRule> rules = configuration.getRules();
- assertEquals(3, rules.size());
- Object2BooleanMap<String> keptFields = new Object2BooleanArrayMap<>();
- for (ProguardConfigurationRule r : rules) {
- assertTrue(r instanceof ProguardKeepRule);
- ProguardKeepRule rule = (ProguardKeepRule) r;
- assertEquals(ProguardKeepRuleType.KEEP, rule.getType());
- assertTrue(rule.getModifiers().allowsObfuscation);
- assertTrue(rule.getModifiers().allowsOptimization);
- List<ProguardMemberRule> memberRules = rule.getMemberRules();
- ProguardClassNameList classNames = rule.getClassNames();
- assertEquals(1, classNames.size());
- DexType type = classNames.asSpecificDexTypes().get(0);
- assertEquals(withVolatileFields.getCanonicalName(), type.toSourceString());
- assertEquals(1, memberRules.size());
- ProguardMemberRule memberRule = memberRules.iterator().next();
- assertEquals(ProguardMemberType.FIELD, memberRule.getRuleType());
- keptFields.put(memberRule.getName().toString(), true);
- }
- assertEquals(3, keptFields.size());
- assertTrue(keptFields.containsKey("intField"));
- assertTrue(keptFields.containsKey("longField"));
- assertTrue(keptFields.containsKey("objField"));
- } else {
- assertEquals(0, configuration.getRules().size());
- }
-
if (isRunProguard()) {
Path proguardedJar = File.createTempFile("proguarded", ".jar", temp.getRoot()).toPath();
Path proguardConfigFile = File.createTempFile("proguard", ".config", temp.getRoot()).toPath();