Correctly handle '***' in ProguardTypeMatcher
Bug: 124584385
Change-Id: Ibe10c89cb036b0cd7feac9eeead1a25e0ddc033c
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
index dc8925a..a59a08c 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -386,24 +386,43 @@
wildcard = wildcards.get(wildcardIndex);
assert wildcard.isPattern();
wildcardPattern = wildcard.asPattern();
+
boolean includeSeparators = pattern.length() > (i + 1) && pattern.charAt(i + 1) == '*';
- int nextPatternIndex = i + (includeSeparators ? 2 : 1);
- // Fast cases for the common case where a pattern ends with '**' or '*'.
+ boolean includeAll = pattern.length() > (i + 2) && pattern.charAt(i + 2) == '*';
+ int nextPatternIndex = i + 1;
+ if (includeAll) {
+ nextPatternIndex += 2;
+ } else if (includeSeparators) {
+ nextPatternIndex += 1;
+ }
+
+ // Fast cases for the common case where a pattern ends with '*', '**', or '***'.
if (nextPatternIndex == pattern.length()) {
- wildcardPattern.setCaptured(name.substring(nameIndex, name.length()));
+ wildcardPattern.setCaptured(name.substring(nameIndex));
+ if (includeAll) {
+ return true;
+ }
if (includeSeparators) {
return kind == ClassOrType.CLASS || !isArrayType(name);
}
boolean hasSeparators = containsSeparatorsStartingAt(name, nameIndex);
return !hasSeparators && (kind == ClassOrType.CLASS || !isArrayType(name));
}
+
// Match the rest of the pattern against the (non-empty) rest of the class name.
for (int nextNameIndex = nameIndex; nextNameIndex < name.length(); nextNameIndex++) {
wildcardPattern.setCaptured(name.substring(nameIndex, nextNameIndex));
- if (!includeSeparators && name.charAt(nextNameIndex) == '.') {
- return matchClassOrTypeNameImpl(
- pattern, nextPatternIndex, name, nextNameIndex, wildcards, wildcardIndex + 1,
- kind);
+ if (!includeSeparators) {
+ if (name.charAt(nextNameIndex) == '.') {
+ return matchClassOrTypeNameImpl(
+ pattern,
+ nextPatternIndex,
+ name,
+ nextNameIndex,
+ wildcards,
+ wildcardIndex + 1,
+ kind);
+ }
}
if (kind == ClassOrType.TYPE && name.charAt(nextNameIndex) == '[') {
return matchClassOrTypeNameImpl(
@@ -416,11 +435,12 @@
return true;
}
}
- // Finally, check the case where the '*' or '**' eats all of the class name.
- wildcardPattern.setCaptured(name.substring(nameIndex, name.length()));
+
+ // Finally, check the case where the '*', '**', or '***' eats all of the class name.
+ wildcardPattern.setCaptured(name.substring(nameIndex));
return matchClassOrTypeNameImpl(
- pattern, nextPatternIndex, name, name.length(), wildcards, wildcardIndex + 1,
- kind);
+ pattern, nextPatternIndex, name, name.length(), wildcards, wildcardIndex + 1, kind);
+
case '?':
wildcard = wildcards.get(wildcardIndex);
assert wildcard.isPattern();
@@ -432,6 +452,7 @@
nameIndex++;
wildcardIndex++;
break;
+
case '<':
wildcard = wildcards.get(wildcardIndex);
assert wildcard.isBackReference();
@@ -446,6 +467,7 @@
wildcardIndex++;
i = pattern.indexOf(">", i);
break;
+
default:
if (nameIndex == name.length() || patternChar != name.charAt(nameIndex++)) {
return false;
diff --git a/src/test/java/com/android/tools/r8/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java b/src/test/java/com/android/tools/r8/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java
index 55731c9..d5f456b 100644
--- a/src/test/java/com/android/tools/r8/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java
+++ b/src/test/java/com/android/tools/r8/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java
@@ -8,24 +8,52 @@
import static org.junit.Assert.assertThat;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import org.junit.Ignore;
import org.junit.Test;
/** Regression test for b/124584385. */
public class ProguardMatchAllRuleWithPrefixTest extends TestBase {
- @Ignore("b/124584385")
@Test
public void test() throws Exception {
CodeInspector inspector =
testForR8(Backend.DEX)
.addProgramClasses(TestClass.class)
- .addKeepRules("-keep class com.android.tools.r8.***")
+ .addKeepRules(
+ "-keep,allowobfuscation class com.android.tools.r8.*** {",
+ " com.android.tools.r8.*** methodA();",
+ " com.android.tools.r8.***Class methodB();",
+ " com.android.tools.r8.***[] methodC();",
+ " com.android.tools.r8.***Class[] methodD();",
+ "}")
.compile()
.inspector();
- assertThat(inspector.clazz(TestClass.class), isPresent());
+
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(classSubject, isPresent());
+ assertThat(classSubject.uniqueMethodWithName("methodA"), isPresent());
+ assertThat(classSubject.uniqueMethodWithName("methodB"), isPresent());
+ assertThat(classSubject.uniqueMethodWithName("methodC"), isPresent());
+ assertThat(classSubject.uniqueMethodWithName("methodD"), isPresent());
}
- static class TestClass {}
+ static class TestClass {
+
+ TestClass methodA() {
+ return new TestClass();
+ }
+
+ TestClass methodB() {
+ return new TestClass();
+ }
+
+ TestClass[] methodC() {
+ return new TestClass[0];
+ }
+
+ TestClass[] methodD() {
+ return new TestClass[0];
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index aa083da..f585226 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -321,6 +321,14 @@
return obfuscatedTypeName != null ? obfuscatedTypeName : originalTypeName;
}
+ String getOriginalTypeName(String minifiedTypeName) {
+ String originalTypeName = null;
+ if (mapping != null) {
+ originalTypeName = mapType(obfuscatedToOriginalMapping, minifiedTypeName);
+ }
+ return originalTypeName != null ? originalTypeName : minifiedTypeName;
+ }
+
InstructionSubject createInstructionSubject(Instruction instruction) {
DexInstructionSubject dexInst = new DexInstructionSubject(instruction);
if (dexInst.isInvoke()) {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 6581a47..dd7cd85 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -143,18 +143,14 @@
// X method(X) -> a
//
// whereas the final signature is for X.a is "a (a)"
- String[] OriginalParameters = new String[signature.parameters.length];
- for (int i = 0; i < OriginalParameters.length; i++) {
- String obfuscated = signature.parameters[i];
- String original = codeInspector.obfuscatedToOriginalMapping.get(obfuscated);
- OriginalParameters[i] = original != null ? original : obfuscated;
+ String[] originalParameters = new String[signature.parameters.length];
+ for (int i = 0; i < originalParameters.length; i++) {
+ originalParameters[i] = codeInspector.getOriginalTypeName(signature.parameters[i]);
}
- String obfuscatedReturnType = signature.type;
- String originalReturnType = codeInspector.obfuscatedToOriginalMapping.get(obfuscatedReturnType);
- String returnType = originalReturnType != null ? originalReturnType : obfuscatedReturnType;
+ String returnType = codeInspector.getOriginalTypeName(signature.type);
MethodSignature lookupSignature =
- new MethodSignature(signature.name, returnType, OriginalParameters);
+ new MethodSignature(signature.name, returnType, originalParameters);
MemberNaming memberNaming = clazz.naming.lookup(lookupSignature);
return memberNaming != null ? (MethodSignature) memberNaming.getOriginalSignature() : signature;