blob: 983928b4549727f98da6b302aefe1f378c772228 [file] [log] [blame]
// Copyright (c) 2016, 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 org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
import com.android.tools.r8.shaking.ProguardTypeMatcher.ClassOrType;
import com.android.tools.r8.shaking.ProguardWildcard.BackReference;
import com.android.tools.r8.shaking.ProguardWildcard.Pattern;
import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
public class ProguardNameMatchingTest {
private static final String[] BASIC_TYPES = {
"char", "byte", "short", "int", "long", "float", "double", "boolean"
};
private static final DexItemFactory dexItemFactory = new DexItemFactory();
private static boolean matchTypeName(String typeName, String pattern) {
return ProguardTypeMatcher.create(
toIdentifierPatternWithWildCards(pattern, false), ClassOrType.TYPE, dexItemFactory)
.matches(dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(typeName)));
}
private static boolean matchClassName(String className, String... patterns) {
return matchClassName(className, ImmutableList.of(Arrays.asList(patterns)));
}
private static boolean matchClassName(String className, List<List<String>> patternsList) {
ProguardClassFilter.Builder builder = ProguardClassFilter.builder();
for (List<String> patterns : patternsList) {
ProguardClassNameList.Builder listBuilder = ProguardClassNameList.builder();
for (String pattern : patterns) {
boolean isNegated = pattern.startsWith("!");
String actualPattern = isNegated ? pattern.substring(1) : pattern;
listBuilder.addClassName(isNegated,
ProguardTypeMatcher.create(
toIdentifierPatternWithWildCards(actualPattern, false),
ClassOrType.CLASS, dexItemFactory));
}
builder.addPattern(listBuilder.build());
}
return builder.build()
.matches(dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(className)));
}
private static boolean matchMemberName(String pattern, String memberName) {
ProguardNameMatcher nameMatcher =
ProguardNameMatcher.create(toIdentifierPatternWithWildCards(pattern, true));
return nameMatcher.matches(memberName);
}
@Test
public void matchClassNames() {
assertTrue(matchClassName("", "**"));
assertTrue(matchClassName("a", "**"));
assertTrue(matchClassName("", "*"));
assertTrue(matchClassName("a", "*"));
assertFalse(matchClassName("", "?"));
assertTrue(matchClassName("a", "?"));
assertTrue(matchClassName("java.lang.Object", "**"));
assertFalse(matchClassName("java.lang.Object", "ja*"));
assertTrue(matchClassName("java.lang.Object", "ja**"));
assertTrue(matchClassName("java.lang.Object", "ja**ject"));
// Oddly, the proguard specification for this makes a lonely * synonymous with **.
assertTrue(matchClassName("java.lang.Object", "*"));
assertFalse(matchClassName("java.lang.Object", "ja*ject"));
assertTrue(matchClassName("java.lang.Object", "java.*g.O*"));
assertTrue(matchClassName("java.lang.Object", "java.*g.O?je?t"));
assertTrue(matchClassName("java.lang.Object", "?ava.*g.Ob<1>e*"));
assertTrue(matchClassName("java.lang.Object", "j?v<1>.*<1>*g.Obj*"));
assertTrue(matchClassName("java.lang.Object", "j*v?.*<2>*g.Obj*"));
assertFalse(matchClassName("java.lang.Object", "java.*g.O?je?t?"));
assertFalse(matchClassName("java.lang.Object", "java?lang.Object"));
assertTrue(matchClassName("java.lang.Object", "*a*.*a**"));
assertTrue(matchClassName("java.lang.Object", "*a**a**"));
assertTrue(matchClassName("java.lang.Object", "*?**<2>**"));
assertTrue(matchClassName("java.lang.Object", "!java.util.**", "java**"));
assertFalse(matchClassName("java.lang.Object", "!java.**", "java.lang.*"));
assertTrue(matchClassName("java.lang.Object", "java.lang.*", "!java.**"));
assertTrue(matchClassName("boobar", "!foobar", "*bar"));
assertTrue(matchClassName("boobar", "!foobar", "?*<1>ar"));
assertFalse(matchClassName("foobar", "!foobar", "*bar"));
assertFalse(matchClassName("foo", "!boo"));
assertFalse(matchClassName("foo", "baz,!boo"));
assertFalse(matchClassName("boo", "!boo", "**"));
assertFalse(matchClassName("boo", "!b*<1>", "**"));
assertFalse(matchClassName("boo", "!b?<1>", "**"));
assertTrue(matchClassName("boo", "**", "!boo"));
assertTrue(matchClassName("boo",
ImmutableList.of(ImmutableList.of("!boo"), ImmutableList.of("**"))));
assertFalse(matchClassName("boofoo", "!boo*,*foo,boofoo"));
assertTrue(matchClassName("boofoo",
ImmutableList.of(ImmutableList.of("!boo*,*foo"), ImmutableList.of("boofoo"))));
}
private void assertMatchesBasicTypes(String pattern) {
for (String type : BASIC_TYPES) {
assertTrue(matchTypeName(type, pattern));
}
}
private void assertDoesNotMatchBasicTypes(String pattern) {
for (String type : BASIC_TYPES) {
assertFalse(matchTypeName(type, pattern));
}
}
@Test
public void matchTypeNames() {
assertTrue(matchTypeName("java.lang.Object", "**"));
assertDoesNotMatchBasicTypes("**");
assertDoesNotMatchBasicTypes("*");
assertFalse(
matchTypeName("java.lang.Object[]", "**"));
assertFalse(
matchTypeName("java.lang.Object", "**z"));
assertFalse(matchTypeName("java.lang.Object[]", "java.**"
));
assertTrue(
matchTypeName("java.lang.Object[]", "***"));
assertTrue(matchTypeName("java.lang.Object[][]", "***"
));
assertFalse(matchTypeName("java.lang.Object", "%"));
assertTrue(matchTypeName("a", "**"));
assertTrue(matchTypeName("java.lang.Object[]", "**[]"
));
assertFalse(matchTypeName("java.lang.Object[][]", "**[]"
));
assertTrue(matchTypeName("abc", "*"));
assertMatchesBasicTypes("***");
assertMatchesBasicTypes("%");
}
@Test
public void matchFieldOrMethodNames() {
assertTrue(matchMemberName("*", ""));
assertTrue(matchMemberName("*", "get"));
assertTrue(matchMemberName("get*", "get"));
assertTrue(matchMemberName("get*", "getObject"));
assertTrue(matchMemberName("*t", "get"));
assertTrue(matchMemberName("g*t*", "getObject"));
assertTrue(matchMemberName("g*t***************", "getObject"));
assertFalse(matchMemberName("get*y", "getObject"));
assertFalse(matchMemberName("getObject?", "getObject"));
assertTrue(matchMemberName("getObject?", "getObject1"));
assertTrue(matchMemberName("getObject?", "getObject5"));
assertTrue(matchMemberName("ge?Objec<1>", "getObject"));
assertTrue(matchMemberName("ge*Objec<1>", "getObject"));
assertTrue(matchMemberName("ge*Objec<1>", "geObjec"));
assertTrue(matchMemberName("g?*Obj<1>c<2>", "getObject"));
assertTrue(matchMemberName("g??Obj<1>c<2>", "getObject"));
assertTrue(matchMemberName("g*?Obj<1>c<2>", "getObject"));
assertFalse(matchMemberName("g?*Obj<1>c<2>", "getObject1"));
assertTrue(matchMemberName("g**Obj<1>c<2>", "getObject"));
assertFalse(matchMemberName("g**Obj<1>c<2>", "getObject1"));
assertTrue(matchMemberName("g?*Obj<1>c<2>?", "getObject1"));
assertTrue(matchMemberName("g**Obj<1>c<2>?", "getObject1"));
assertTrue(matchMemberName("*foo<1>", "foofoofoo"));
assertTrue(matchMemberName("*foo<1>", "barfoobar"));
assertFalse(matchMemberName("*foo<1>", "barfoobaz"));
}
private static IdentifierPatternWithWildcards toIdentifierPatternWithWildCards(
String pattern, boolean isForNameMatcher) {
ImmutableList.Builder<ProguardWildcard> builder = ImmutableList.builder();
String allPattern = "";
String backReference = "";
for (int i = 0; i < pattern.length(); i++) {
char patternChar = pattern.charAt(i);
if (backReference.length() > 0) {
if (patternChar == '>') {
backReference += patternChar;
builder.add(new BackReference(
Integer.parseInt(backReference.substring(1, backReference.length() - 1))));
backReference = "";
} else {
backReference += patternChar;
}
continue;
} else if (allPattern.length() > 0) {
if (patternChar == '*') {
allPattern += patternChar;
continue;
} else {
builder.add(new Pattern(allPattern));
allPattern = "";
}
}
if (patternChar == '*') {
if (isForNameMatcher) {
builder.add(new Pattern(String.valueOf(patternChar)));
} else {
allPattern += patternChar;
}
} else if (patternChar == '?' || patternChar == '%') {
builder.add(new Pattern(String.valueOf(patternChar)));
} else if (patternChar == '<') {
backReference += patternChar;
}
}
if (allPattern.length() > 0) {
builder.add(new Pattern(allPattern));
}
List<ProguardWildcard> wildcards = builder.build();
linkBackReferences(wildcards);
return new IdentifierPatternWithWildcards(pattern, wildcards);
}
private static void linkBackReferences(Iterable<ProguardWildcard> wildcards) {
List<Pattern> patterns = new ArrayList<>();
for (ProguardWildcard wildcard : wildcards) {
if (wildcard.isBackReference()) {
BackReference backReference = wildcard.asBackReference();
backReference.setReference(patterns.get(backReference.referenceIndex - 1));
} else {
assert wildcard.isPattern();
patterns.add(wildcard.asPattern());
}
}
}
}