Add -identifiernamestring rule for Java reflection on demand.
Bug: 76181966
Change-Id: Ie178b941744735fddbc0dcd1c4c083c45eb2a545
diff --git a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
index b2560ff..f2f59a2 100644
--- a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
@@ -4,37 +4,9 @@
package com.android.tools.r8;
-import com.android.tools.r8.origin.EmbeddedOrigin;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
-import java.util.List;
public class CompatProguardCommandBuilder extends R8Command.Builder {
- @VisibleForTesting
- public static final List<String> REFLECTIONS = ImmutableList.of(
- "-identifiernamestring public class java.lang.Class {",
- " public static java.lang.Class forName(java.lang.String);",
- " public java.lang.reflect.Field getField(java.lang.String);",
- " public java.lang.reflect.Field getDeclaredField(java.lang.String);",
- " public java.lang.reflect.Method getMethod(java.lang.String, java.lang.Class[]);",
- " public java.lang.reflect.Method getDeclaredMethod(java.lang.String, java.lang.Class[]);",
- "}",
- "-identifiernamestring public class java.util.concurrent.atomic.AtomicIntegerFieldUpdater {",
- " public static java.util.concurrent.atomic.AtomicIntegerFieldUpdater",
- " newUpdater(java.lang.Class, java.lang.String);",
- "}",
- "-identifiernamestring public class java.util.concurrent.atomic.AtomicLongFieldUpdater {",
- " public static java.util.concurrent.atomic.AtomicLongFieldUpdater",
- " newUpdater(java.lang.Class, java.lang.String);",
- "}",
- "-identifiernamestring public class",
- " java.util.concurrent.atomic.AtomicReferenceFieldUpdater {",
- " public static java.util.concurrent.atomic.AtomicReferenceFieldUpdater",
- " newUpdater(java.lang.Class, java.lang.Class, java.lang.String);",
- "}"
- );
-
public CompatProguardCommandBuilder() {
this(true);
}
@@ -43,13 +15,11 @@
boolean forceProguardCompatibility, DiagnosticsHandler diagnosticsHandler) {
super(forceProguardCompatibility, diagnosticsHandler);
setIgnoreDexInArchive(true);
- addProguardConfiguration(REFLECTIONS, EmbeddedOrigin.INSTANCE);
}
public CompatProguardCommandBuilder(boolean forceProguardCompatibility) {
super(forceProguardCompatibility);
setIgnoreDexInArchive(true);
- addProguardConfiguration(REFLECTIONS, EmbeddedOrigin.INSTANCE);
}
public void setProguardCompatibilityRulesOutput(Path path) {
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 59c2639..21cab49 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -285,8 +285,8 @@
.run(executorService);
ProtoLiteExtension protoLiteExtension =
options.forceProguardCompatibility ? null : new ProtoLiteExtension(appInfo);
- appInfo = new Enqueuer(appInfo, options, compatibility, protoLiteExtension)
- .traceApplication(rootSet, timing);
+ Enqueuer enqueuer = new Enqueuer(appInfo, options, compatibility, protoLiteExtension);
+ appInfo = enqueuer.traceApplication(rootSet, timing);
if (options.proguardConfiguration.isPrintSeeds()) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
PrintStream out = new PrintStream(bytes);
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 b11b999..b733faf 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentiferNameString;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
+import static com.android.tools.r8.shaking.ProguardConfigurationUtils.buildIdentifierNameStringRule;
import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.Diagnostic;
@@ -103,6 +104,7 @@
private final ProtoLiteExtension protoLiteExtension;
private final Set<DexField> protoLiteFields = Sets.newIdentityHashSet();
+ private final Set<DexItem> identifierNameStrings = Sets.newIdentityHashSet();
/**
* This map keeps a view of all virtual methods that are reachable from virtual invokes. A method
@@ -272,9 +274,14 @@
@Override
public boolean registerInvokeVirtual(DexMethod method) {
- if (options.forceProguardCompatibility
- && appInfo.dexItemFactory.classMethods.isReflectiveMemberLookup(method)) {
- pendingProguardReflectiveCompatibility.add(currentMethod);
+ if (appInfo.dexItemFactory.classMethods.isReflectiveMemberLookup(method)) {
+ if (options.forceProguardCompatibility) {
+ // TODO(b/76181966): whether or not add this rule in normal mode.
+ if (identifierNameStrings.add(method) && compatibility != null) {
+ compatibility.addRule(buildIdentifierNameStringRule(method));
+ }
+ pendingProguardReflectiveCompatibility.add(currentMethod);
+ }
}
if (!registerItemWithTarget(virtualInvokes, method)) {
return false;
@@ -300,10 +307,15 @@
@Override
public boolean registerInvokeStatic(DexMethod method) {
- if (options.forceProguardCompatibility
- && (method == appInfo.dexItemFactory.classMethods.forName
- || appInfo.dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(method))) {
- pendingProguardReflectiveCompatibility.add(currentMethod);
+ if (method == appInfo.dexItemFactory.classMethods.forName
+ || appInfo.dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(method)) {
+ if (options.forceProguardCompatibility) {
+ // TODO(b/76181966): whether or not add this rule in normal mode.
+ if (identifierNameStrings.add(method) && compatibility != null) {
+ compatibility.addRule(buildIdentifierNameStringRule(method));
+ }
+ pendingProguardReflectiveCompatibility.add(currentMethod);
+ }
}
if (!registerItemWithTarget(staticInvokes, method)) {
return false;
@@ -1510,7 +1522,8 @@
this.noSideEffects = enqueuer.rootSet.noSideEffects;
this.assumedValues = enqueuer.rootSet.assumedValues;
this.alwaysInline = enqueuer.rootSet.alwaysInline;
- this.identifierNameStrings = enqueuer.rootSet.identifierNameStrings;
+ this.identifierNameStrings =
+ Sets.union(enqueuer.rootSet.identifierNameStrings, enqueuer.identifierNameStrings);
this.protoLiteFields = enqueuer.protoLiteFields;
this.prunedTypes = Collections.emptySet();
this.switchMaps = Collections.emptyMap();
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 b8b7780..5852cd9 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -7,6 +7,10 @@
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.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import java.util.List;
@@ -82,4 +86,37 @@
builder.getMemberRules().add(memberRuleBuilder.build());
return builder.build();
}
+
+ public static ProguardIdentifierNameStringRule buildIdentifierNameStringRule(DexItem item) {
+ assert item instanceof DexField || item instanceof DexMethod;
+ ProguardIdentifierNameStringRule.Builder builder = ProguardIdentifierNameStringRule.builder();
+ ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
+ DexType holderType;
+ if (item instanceof DexField) {
+ DexField field = (DexField) item;
+ holderType = field.getHolder();
+ memberRuleBuilder.setRuleType(ProguardMemberType.FIELD);
+ memberRuleBuilder.setName(field.name.toString());
+ memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(field.type));
+ } else {
+ DexMethod method = (DexMethod) item;
+ holderType = method.getHolder();
+ memberRuleBuilder.setRuleType(ProguardMemberType.METHOD);
+ memberRuleBuilder.setName(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();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
index 2f53fd9..eb1f245 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
@@ -9,7 +9,6 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.CompatProguardCommandBuilder;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.ToolHelper;
@@ -22,7 +21,6 @@
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import java.nio.file.Path;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,14 +38,16 @@
pgConfig.setPrintMappingFile(out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE));
})
.addProguardConfiguration(
- ImmutableList.copyOf(Iterables.concat(ImmutableList.of(
+ ImmutableList.of(
keepMainProguardConfiguration(main),
overloadaggressively ? "-overloadaggressively" : ""),
- CompatProguardCommandBuilder.REFLECTIONS)),
Origin.unknown())
.setOutput(out, OutputMode.DexIndexed)
.build();
- return ToolHelper.runR8(command, o -> o.enableInlining = false);
+ return ToolHelper.runR8(command, o -> {
+ o.enableInlining = false;
+ o.forceProguardCompatibility = true;
+ });
}
private void fieldUpdater(boolean overloadaggressively) throws Exception {
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 4681d7d..abbe3d3 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,6 +23,7 @@
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardConfigurationParser;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.shaking.ProguardIdentifierNameStringRule;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.shaking.ProguardKeepRule;
import com.android.tools.r8.shaking.ProguardKeepRuleType;
@@ -42,6 +43,7 @@
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import java.io.File;
@@ -271,13 +273,12 @@
parser.parse(proguardCompatibilityRules);
ProguardConfiguration configuration = parser.getConfigRawForTesting();
if (forceProguardCompatibility) {
- assertEquals(2, configuration.getRules().size());
- configuration.getRules().forEach(rule -> {
- assertTrue(rule instanceof ProguardKeepRule);
- ProguardKeepRule keepRule = (ProguardKeepRule) rule;
- assertEquals(ProguardKeepRuleType.KEEP, keepRule.getType());
- assertTrue(keepRule.getModifiers().allowsObfuscation);
- assertTrue(keepRule.getModifiers().allowsOptimization);
+ List<ProguardConfigurationRule> rules = configuration.getRules();
+ assertEquals(3, rules.size());
+ Iterables.filter(rules, ProguardKeepRule.class).forEach(rule -> {
+ assertEquals(ProguardKeepRuleType.KEEP, rule.getType());
+ assertTrue(rule.getModifiers().allowsObfuscation);
+ assertTrue(rule.getModifiers().allowsOptimization);
Set<ProguardMemberRule> memberRules = rule.getMemberRules();
ProguardClassNameList classNames = rule.getClassNames();
assertEquals(1, classNames.size());
@@ -293,6 +294,17 @@
assertEquals(ProguardMemberType.INIT, memberRules.iterator().next().getRuleType());
}
});
+ Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> {
+ Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+ ProguardClassNameList classNames = rule.getClassNames();
+ assertEquals(1, classNames.size());
+ DexType type = classNames.asSpecificDexTypes().get(0);
+ assertEquals("java.lang.Class", type.toSourceString());
+ assertEquals(1, memberRules.size());
+ ProguardMemberRule memberRule = memberRules.iterator().next();
+ assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType());
+ assertEquals("forName", memberRule.getName().toString());
+ });
} else {
assertEquals(0, configuration.getRules().size());
}
@@ -370,13 +382,12 @@
parser.parse(proguardCompatibilityRules);
ProguardConfiguration configuration = parser.getConfigRawForTesting();
if (forceProguardCompatibility) {
- assertEquals(2, configuration.getRules().size());
- configuration.getRules().forEach(rule -> {
- assertTrue(rule instanceof ProguardKeepRule);
- ProguardKeepRule keepRule = (ProguardKeepRule) rule;
- assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, keepRule.getType());
- assertTrue(keepRule.getModifiers().allowsObfuscation);
- assertTrue(keepRule.getModifiers().allowsOptimization);
+ List<ProguardConfigurationRule> rules = configuration.getRules();
+ assertEquals(4, rules.size());
+ Iterables.filter(rules, ProguardKeepRule.class).forEach(rule -> {
+ assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, rule.getType());
+ assertTrue(rule.getModifiers().allowsObfuscation);
+ assertTrue(rule.getModifiers().allowsOptimization);
Set<ProguardMemberRule> memberRules = rule.getMemberRules();
ProguardClassNameList classNames = rule.getClassNames();
assertEquals(1, classNames.size());
@@ -391,6 +402,17 @@
assertTrue(memberRule.getName().matches("bar"));
}
});
+ Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> {
+ Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+ ProguardClassNameList classNames = rule.getClassNames();
+ assertEquals(1, classNames.size());
+ DexType type = classNames.asSpecificDexTypes().get(0);
+ assertEquals("java.lang.Class", type.toSourceString());
+ assertEquals(1, memberRules.size());
+ ProguardMemberRule memberRule = memberRules.iterator().next();
+ assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType());
+ assertTrue(memberRule.getName().toString().startsWith("getDeclared"));
+ });
} else {
assertEquals(0, configuration.getRules().size());
}
@@ -475,14 +497,13 @@
parser.parse(proguardCompatibilityRules);
ProguardConfiguration configuration = parser.getConfigRawForTesting();
if (forceProguardCompatibility) {
- assertEquals(3, configuration.getRules().size());
+ List<ProguardConfigurationRule> rules = configuration.getRules();
+ assertEquals(6, rules.size());
Object2BooleanMap<String> keptFields = new Object2BooleanArrayMap<>();
- configuration.getRules().forEach(rule -> {
- assertTrue(rule instanceof ProguardKeepRule);
- ProguardKeepRule keepRule = (ProguardKeepRule) rule;
- assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, keepRule.getType());
- assertTrue(keepRule.getModifiers().allowsObfuscation);
- assertTrue(keepRule.getModifiers().allowsOptimization);
+ Iterables.filter(rules, ProguardKeepRule.class).forEach(rule -> {
+ assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, rule.getType());
+ assertTrue(rule.getModifiers().allowsObfuscation);
+ assertTrue(rule.getModifiers().allowsOptimization);
Set<ProguardMemberRule> memberRules = rule.getMemberRules();
ProguardClassNameList classNames = rule.getClassNames();
assertEquals(1, classNames.size());
@@ -497,6 +518,18 @@
assertTrue(keptFields.containsKey("intField"));
assertTrue(keptFields.containsKey("longField"));
assertTrue(keptFields.containsKey("objField"));
+ Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> {
+ Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+ ProguardClassNameList classNames = rule.getClassNames();
+ assertEquals(1, classNames.size());
+ DexType type = classNames.asSpecificDexTypes().get(0);
+ assertTrue(type.toSourceString().startsWith("java.util.concurrent.atomic.Atomic"));
+ assertTrue(type.toSourceString().endsWith("FieldUpdater"));
+ assertEquals(1, memberRules.size());
+ ProguardMemberRule memberRule = memberRules.iterator().next();
+ assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType());
+ assertEquals("newUpdater", memberRule.getName().toString());
+ });
} else {
assertEquals(0, configuration.getRules().size());
}