|  | // 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()); | 
|  | } | 
|  | } | 
|  | } | 
|  | } |