| // 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")); | |
| assertTrue(matchClassName("foo", "!boo")); | |
| assertTrue(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()); | |
| } | |
| } | |
| } | |
| } |