Raise warning for -identifiernamestring rules matching inlinable fields
Bug: 214263216
Change-Id: Ibc135459f8f88285a3871db47db2c1d07203b1cf
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 3ae4b1f..1aa41dc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -367,10 +367,33 @@
isInlinableByJavaC = true;
}
- public boolean isInlinableByJavaC() {
+ public boolean getIsInlinableByJavaC() {
return isInlinableByJavaC;
}
+ public boolean getOrComputeIsInlinableByJavaC(DexItemFactory dexItemFactory) {
+ if (getIsInlinableByJavaC()) {
+ return true;
+ }
+ if (!isStatic() || !isFinal()) {
+ return false;
+ }
+ if (!hasExplicitStaticValue()) {
+ return false;
+ }
+ if (getType().isPrimitiveType()) {
+ return true;
+ }
+ if (getType() != dexItemFactory.stringType) {
+ return false;
+ }
+ if (!getStaticValue().isDexValueString()) {
+ return false;
+ }
+ markAsInlinableByJavaC();
+ return true;
+ }
+
public static class Builder {
private DexField field;
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
index 98ffa2a..8596642 100644
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
+++ b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
@@ -237,7 +237,6 @@
// Fields that are javac inlined are unsound as predicates for conditional rules.
// Ignore any such field members and record it for possible reporting later.
if (isFieldInlinedByJavaC(f)) {
- f.markAsInlinableByJavaC();
fieldsInlinedByJavaC.add(DexClassAndField.create(targetClass, f));
return false;
}
@@ -317,21 +316,9 @@
if (enqueuer.getMode().isFinalTreeShaking()) {
// Ignore any field value in the final tree shaking pass so it remains consistent with the
// initial pass.
- return field.isInlinableByJavaC();
+ return field.getIsInlinableByJavaC();
}
- if (!field.isStatic() || !field.isFinal()) {
- return false;
- }
- if (!field.hasExplicitStaticValue()) {
- return false;
- }
- if (field.getType().isPrimitiveType()) {
- return true;
- }
- if (field.getType() != appView.dexItemFactory().stringType) {
- return false;
- }
- return field.getStaticValue().isDexValueString();
+ return field.getOrComputeIsInlinableByJavaC(appView.dexItemFactory());
}
private void materializeIfRule(ProguardIfRule rule, Set<DexReference> preconditions) {
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 9629fb2..2f7d3f4 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -62,6 +62,7 @@
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.OriginWithPosition;
import com.android.tools.r8.utils.PredicateSet;
+import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.TraversalContinuation;
@@ -1299,13 +1300,7 @@
throw new Unreachable();
}
} else if (context instanceof ProguardIdentifierNameStringRule) {
- if (item.isField()) {
- identifierNameStrings.add(item.asField().getReference());
- context.markAsUsed();
- } else if (item.isMethod()) {
- identifierNameStrings.add(item.asMethod().getReference());
- context.markAsUsed();
- }
+ evaluateIdentifierNameStringRule(item, context, ifRule);
} else if (context instanceof ConstantArgumentRule) {
if (item.isMethod()) {
keepParametersWithConstantValue.add(item.asMethod().getReference());
@@ -1559,6 +1554,36 @@
}
}
+ private void evaluateIdentifierNameStringRule(
+ Definition item, ProguardConfigurationRule context, ProguardIfRule ifRule) {
+ // Main dex rules should not contain -identifiernamestring rules.
+ assert !isMainDexRootSetBuilder();
+
+ // We don't expect -identifiernamestring rules to be used as the subsequent of -if rules.
+ assert ifRule == null;
+
+ if (item.isClass()) {
+ return;
+ }
+
+ if (item.isField()) {
+ DexClassAndField field = item.asField();
+ if (field.getDefinition().getOrComputeIsInlinableByJavaC(appView.dexItemFactory())) {
+ Reporter reporter = appView.reporter();
+ reporter.warning(
+ new StringDiagnostic(
+ "Rule matches the static final field `"
+ + field.toSourceString()
+ + "`, which may have been inlined: "
+ + context.toString(),
+ context.getOrigin()));
+ }
+ }
+
+ identifierNameStrings.add(item.asMember().getReference());
+ context.markAsUsed();
+ }
+
private boolean isInterfaceMethodNeedingDesugaring(ProgramDefinition item) {
return options.isInterfaceMethodDesugaringEnabled()
&& item.isMethod()
diff --git a/src/test/java/com/android/tools/r8/naming/identifiernamestring/IdentifierNameStringStaticFinalFieldTest.java b/src/test/java/com/android/tools/r8/naming/identifiernamestring/IdentifierNameStringStaticFinalFieldTest.java
new file mode 100644
index 0000000..6d0c21d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/identifiernamestring/IdentifierNameStringStaticFinalFieldTest.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2022, 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.naming.identifiernamestring;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.equalTo;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class IdentifierNameStringStaticFinalFieldTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(Backend.DEX)
+ .addInnerClasses(getClass())
+ .addKeepClassAndMembersRules(Main.class)
+ .addKeepRules("-identifiernamestring class * { java.lang.String CLASS_NAME; }")
+ .allowDiagnosticWarningMessages()
+ .setMinApi(AndroidApiLevel.B)
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertWarningsMatch(
+ diagnosticMessage(
+ equalTo(
+ StringUtils.joinLines(
+ "Rule matches the static final field `java.lang.String "
+ + Main.class.getTypeName()
+ + ".CLASS_NAME`, which may have been inlined:"
+ + " -identifiernamestring class * {",
+ " java.lang.String CLASS_NAME;",
+ "}")))));
+ }
+
+ static class Main {
+
+ // @IdentifierNameString
+ public static final String CLASS_NAME = "Foo";
+
+ public static void main(String[] args) {}
+ }
+}