Version 1.2.36
Merge: Regard multiple asterisks in member names as separate wildcards.
CL: https://r8-review.googlesource.com/c/r8/+/24620
and then adjust tests a bit as code inspector refactoring is not there.
Bug: 111974287
Change-Id: I9883fe6bfb63094bcbabbf760648d9798732fcaf
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 4bb9fb2..73f0611 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 = "1.2.35";
+ public static final String LABEL = "1.2.36";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index c4276dc..c4e6251 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -1194,6 +1194,7 @@
private IdentifierPatternWithWildcards acceptIdentifierWithBackreference(IdentifierType kind) {
ImmutableList.Builder<ProguardWildcard> wildcardsCollector = ImmutableList.builder();
StringBuilder currentAsterisks = null;
+ int asteriskCount = 0;
StringBuilder currentBackreference = null;
skipWhitespace();
int start = position;
@@ -1235,19 +1236,35 @@
}
} else if (currentAsterisks != null) {
if (current == '*') {
+ // only '*', '**', and '***' are allowed.
+ // E.g., '****' should be regarded as two separate wildcards (e.g., '***' and '*')
+ if (asteriskCount >= 3) {
+ wildcardsCollector.add(new ProguardWildcard.Pattern(currentAsterisks.toString()));
+ currentAsterisks = new StringBuilder();
+ asteriskCount = 0;
+ }
currentAsterisks.append((char) current);
+ asteriskCount++;
end += Character.charCount(current);
continue;
} else {
wildcardsCollector.add(new ProguardWildcard.Pattern(currentAsterisks.toString()));
currentAsterisks = null;
+ asteriskCount = 0;
}
}
// From now on, neither in asterisk collecting state nor back reference collecting state.
assert currentAsterisks == null && currentBackreference == null;
if (current == '*') {
- currentAsterisks = new StringBuilder();
- currentAsterisks.append((char) current);
+ if (kind == IdentifierType.CLASS_NAME) {
+ // '**' and '***' are only allowed in type name.
+ currentAsterisks = new StringBuilder();
+ currentAsterisks.append((char) current);
+ asteriskCount = 1;
+ } else {
+ // For member names, regard '**' or '***' as separate single-asterisk wildcards.
+ wildcardsCollector.add(new ProguardWildcard.Pattern(String.valueOf((char) current)));
+ }
end += Character.charCount(current);
} else if (current == '?' || current == '%') {
wildcardsCollector.add(new ProguardWildcard.Pattern(String.valueOf((char) current)));
diff --git a/src/test/java/com/android/tools/r8/shaking/AsterisksTest.java b/src/test/java/com/android/tools/r8/shaking/AsterisksTest.java
new file mode 100644
index 0000000..8290521
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/AsterisksTest.java
@@ -0,0 +1,176 @@
+// 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 com.android.tools.r8.shaking;
+
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static com.android.tools.r8.utils.DexInspectorMatchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.FieldSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+class B111974287 {
+ B111974287 self;
+ B111974287[] clones;
+
+ B111974287() {
+ self = this;
+ clones = new B111974287[1];
+ clones[0] = self;
+ }
+
+ B111974287 fooX() {
+ System.out.println("fooX");
+ return self;
+ }
+
+ B111974287 fooYY() {
+ System.out.println("fooYY");
+ return self;
+ }
+
+ B111974287 fooZZZ() {
+ System.out.println("fooZZZ");
+ return self;
+ }
+}
+
+@RunWith(Parameterized.class)
+public class AsterisksTest extends ProguardCompatabilityTestBase {
+ private final static List<Class> CLASSES = ImmutableList.of(B111974287.class);
+ private final Shrinker shrinker;
+
+ public AsterisksTest(Shrinker shrinker) {
+ this.shrinker = shrinker;
+ }
+
+ @Parameters(name = "shrinker: {0}")
+ public static Collection<Object> data() {
+ return ImmutableList.of(Shrinker.PROGUARD6, Shrinker.R8);
+ }
+
+ @Test
+ public void doubleAsterisksInField() throws Exception {
+ List<String> config = ImmutableList.of(
+ "-keep class **." + B111974287.class.getSimpleName() + "{",
+ " ** **;",
+ "}"
+ );
+ DexInspector codeInspector = runShrinker(shrinker, CLASSES, config);
+ ClassSubject classSubject = codeInspector.clazz(B111974287.class);
+ assertThat(classSubject, isPresent());
+ assertThat(classSubject, not(isRenamed()));
+ FieldSubject fieldSubject = classSubject.field(B111974287.class.getTypeName(), "self");
+ assertThat(fieldSubject, isPresent());
+ assertThat(fieldSubject, not(isRenamed()));
+ fieldSubject = classSubject.field(B111974287.class.getTypeName() + "[]", "clones");
+ // TODO(b/111974287): Proguard6 kept and renamed the field with array type.
+ if (shrinker == Shrinker.PROGUARD6) {
+ return;
+ }
+ assertThat(fieldSubject, not(isPresent()));
+ }
+
+ @Test
+ public void doubleAsterisksInMethod() throws Exception {
+ List<String> config = ImmutableList.of(
+ "-keep class **." + B111974287.class.getSimpleName() + "{",
+ " ** foo**(...);",
+ "}"
+ );
+ DexInspector codeInspector = runShrinker(shrinker, CLASSES, config);
+ ClassSubject classSubject = codeInspector.clazz(B111974287.class);
+ assertThat(classSubject, isPresent());
+ assertThat(classSubject, not(isRenamed()));
+ DexClass clazz = classSubject.getDexClass();
+ assertEquals(3, clazz.virtualMethods().length);
+ for (DexEncodedMethod encodedMethod : clazz.virtualMethods()) {
+ assertTrue(encodedMethod.method.name.toString().startsWith("foo"));
+ MethodSubject methodSubject =
+ classSubject.method(MethodSignature.fromDexMethod(encodedMethod.method));
+ assertThat(methodSubject, isPresent());
+ assertThat(methodSubject, not(isRenamed()));
+ }
+ }
+
+ @Test
+ public void tripleAsterisksInField() throws Exception {
+ List<String> config = ImmutableList.of(
+ "-keep class **." + B111974287.class.getSimpleName() + "{",
+ " *** ***;",
+ "}"
+ );
+ DexInspector codeInspector = runShrinker(shrinker, CLASSES, config);
+ ClassSubject classSubject = codeInspector.clazz(B111974287.class);
+ assertThat(classSubject, isPresent());
+ assertThat(classSubject, not(isRenamed()));
+ FieldSubject fieldSubject = classSubject.field(B111974287.class.getTypeName(), "self");
+ assertThat(fieldSubject, isPresent());
+ assertThat(fieldSubject, not(isRenamed()));
+ fieldSubject = classSubject.field(B111974287.class.getTypeName() + "[]", "clones");
+ assertThat(fieldSubject, isPresent());
+ assertThat(fieldSubject, not(isRenamed()));
+ }
+
+ @Test
+ public void tripleAsterisksInMethod() throws Exception {
+ List<String> config = ImmutableList.of(
+ "-keep class **." + B111974287.class.getSimpleName() + "{",
+ " *** foo***(...);",
+ "}"
+ );
+ DexInspector codeInspector = runShrinker(shrinker, CLASSES, config);
+ ClassSubject classSubject = codeInspector.clazz(B111974287.class);
+ assertThat(classSubject, isPresent());
+ assertThat(classSubject, not(isRenamed()));
+ DexClass clazz = classSubject.getDexClass();
+ assertEquals(3, clazz.virtualMethods().length);
+ for (DexEncodedMethod encodedMethod : clazz.virtualMethods()) {
+ assertTrue(encodedMethod.method.name.toString().startsWith("foo"));
+ MethodSubject methodSubject =
+ classSubject.method(MethodSignature.fromDexMethod(encodedMethod.method));
+ assertThat(methodSubject, isPresent());
+ assertThat(methodSubject, not(isRenamed()));
+ }
+ }
+
+ @Test
+ public void quadrupleAsterisksInType() throws Exception {
+ List<String> config = ImmutableList.of(
+ "-keep class **** {",
+ " **** foo***(...);",
+ "}"
+ );
+ DexInspector codeInspector = runShrinker(shrinker, CLASSES, config);
+ ClassSubject classSubject = codeInspector.clazz(B111974287.class);
+ assertThat(classSubject, isPresent());
+ assertThat(classSubject, not(isRenamed()));
+ DexClass clazz = classSubject.getDexClass();
+ assertEquals(3, clazz.virtualMethods().length);
+ for (DexEncodedMethod encodedMethod : clazz.virtualMethods()) {
+ assertTrue(encodedMethod.method.name.toString().startsWith("foo"));
+ MethodSubject methodSubject =
+ classSubject.method(MethodSignature.fromDexMethod(encodedMethod.method));
+ assertThat(methodSubject, isPresent());
+ assertThat(methodSubject, not(isRenamed()));
+ }
+ }
+
+}