Version 1.4.86
Cherry pick: New implementation of -keeppackagenames
CL: https://r8-review.googlesource.com/c/r8/+/36944
Cherry pick: Reproduce b/130135768: keeppackagenames with asterisks
CL: https://r8-review.googlesource.com/c/r8/+/36860
Bug: 130135768
Change-Id: I8a340afe4811ec3f8826956c99b94346f4c2c8b4
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index f7bc09f..99999c6 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.4.85";
+ public static final String LABEL = "1.4.86";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 9368a79..9a8513b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -335,6 +335,14 @@
return descriptor.toString();
}
+ public String toBinaryName() {
+ String descriptor = toDescriptorString();
+ assert descriptor.length() > 1
+ && descriptor.charAt(0) == 'L'
+ && descriptor.charAt(descriptor.length() - 1) == ';';
+ return descriptor.substring(1, descriptor.length() - 1);
+ }
+
@Override
public String toSourceString() {
if (toStringCache == null) {
@@ -682,4 +690,8 @@
}
return lubType;
}
+
+ public String getPackageName() {
+ return DescriptorUtils.getPackageNameFromBinaryName(toBinaryName());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index d7426fc..2634ca4 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -21,11 +21,11 @@
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.naming.signature.GenericSignatureRewriter;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.ProguardPackageNameList;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
-import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableMap;
@@ -44,7 +44,6 @@
private final AppView<AppInfoWithLiveness> appView;
private final AppInfoWithLiveness appInfo;
- private final Reporter reporter;
private final PackageObfuscationMode packageObfuscationMode;
private final boolean isAccessModificationAllowed;
private final Set<String> noObfuscationPrefixes = Sets.newHashSet();
@@ -58,7 +57,6 @@
private final boolean keepInnerClassStructure;
private final Set<DexType> noObfuscationTypes;
- private final Set<DexType> keepPackageName;
private final Namespace topLevelState;
@@ -66,7 +64,6 @@
this.appView = appView;
this.appInfo = appView.appInfo();
InternalOptions options = appView.options();
- this.reporter = options.reporter;
this.packageObfuscationMode = options.getProguardConfiguration().getPackageObfuscationMode();
this.isAccessModificationAllowed =
options.getProguardConfiguration().isAccessModificationAllowed();
@@ -74,11 +71,7 @@
this.classDictionary = options.getProguardConfiguration().getClassObfuscationDictionary();
this.keepInnerClassStructure = options.getProguardConfiguration().getKeepAttributes().signature;
this.noObfuscationTypes =
- DexReference.filterDexType(rootSet.noObfuscation.stream())
- .collect(Collectors.toSet());
- this.keepPackageName =
- DexReference.filterDexType(rootSet.keepPackageName.stream())
- .collect(Collectors.toSet());
+ DexReference.filterDexType(rootSet.noObfuscation.stream()).collect(Collectors.toSet());
// Initialize top-level naming state.
topLevelState = new Namespace(
@@ -257,8 +250,9 @@
String packageName = getPackageBinaryNameFromJavaType(type.getPackageDescriptor());
// Check whether the given class should be kept.
// or check whether the given class belongs to a package that is kept for another class.
- if (keepPackageName.contains(type)
- || noObfuscationPrefixes.contains(packageName)) {
+ ProguardPackageNameList keepPackageNames =
+ appView.options().getProguardConfiguration().getKeepPackageNamesPatterns();
+ if (noObfuscationPrefixes.contains(packageName) || keepPackageNames.matches(type)) {
return states.computeIfAbsent(packageName, Namespace::new);
}
Namespace state = topLevelState;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index a767c86..ec1d9ba 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -42,6 +42,8 @@
private boolean verbose;
private String renameSourceFileAttribute;
private final List<String> keepAttributePatterns = new ArrayList<>();
+ private final ProguardPackageNameList.Builder keepPackageNamesPatterns =
+ ProguardPackageNameList.builder();
private final ProguardClassFilter.Builder dontWarnPatterns = ProguardClassFilter.builder();
private final ProguardClassFilter.Builder dontNotePatterns = ProguardClassFilter.builder();
protected final Set<ProguardConfigurationRule> rules = Sets.newLinkedHashSet();
@@ -181,6 +183,10 @@
this.rules.add(rule);
}
+ public void addKeepPackageNamesPattern(boolean isNegated, ProguardPackageMatcher pattern) {
+ keepPackageNamesPatterns.addPackageName(isNegated, pattern);
+ }
+
public void addDontWarnPattern(ProguardClassNameList pattern) {
dontWarnPatterns.addPattern(pattern);
}
@@ -292,43 +298,45 @@
}
public ProguardConfiguration buildRaw() {
- ProguardConfiguration configuration = new ProguardConfiguration(
- String.join(System.lineSeparator(), parsedConfiguration),
- dexItemFactory,
- injars,
- libraryjars,
- packageObfuscationMode,
- packagePrefix,
- allowAccessModification,
- ignoreWarnings,
- optimizing,
- obfuscating,
- shrinking,
- printConfiguration,
- printConfigurationFile,
- printUsage,
- printUsageFile,
- printMapping,
- printMappingFile,
- applyMappingFile,
- verbose,
- renameSourceFileAttribute,
- ProguardKeepAttributes.fromPatterns(keepAttributePatterns),
- dontWarnPatterns.build(),
- dontNotePatterns.build(),
- rules,
- printSeeds,
- seedFile,
- overloadAggressively,
- DictionaryReader.readAllNames(obfuscationDictionary, reporter),
- DictionaryReader.readAllNames(classObfuscationDictionary, reporter),
- DictionaryReader.readAllNames(packageObfuscationDictionary, reporter),
- useUniqueClassMemberNames,
- keepParameterNames,
- adaptClassStrings.build(),
- adaptResourceFilenames.build(),
- adaptResourceFileContents.build(),
- keepDirectories.build());
+ ProguardConfiguration configuration =
+ new ProguardConfiguration(
+ String.join(System.lineSeparator(), parsedConfiguration),
+ dexItemFactory,
+ injars,
+ libraryjars,
+ packageObfuscationMode,
+ packagePrefix,
+ allowAccessModification,
+ ignoreWarnings,
+ optimizing,
+ obfuscating,
+ shrinking,
+ printConfiguration,
+ printConfigurationFile,
+ printUsage,
+ printUsageFile,
+ printMapping,
+ printMappingFile,
+ applyMappingFile,
+ verbose,
+ renameSourceFileAttribute,
+ ProguardKeepAttributes.fromPatterns(keepAttributePatterns),
+ keepPackageNamesPatterns.build(),
+ dontWarnPatterns.build(),
+ dontNotePatterns.build(),
+ rules,
+ printSeeds,
+ seedFile,
+ overloadAggressively,
+ DictionaryReader.readAllNames(obfuscationDictionary, reporter),
+ DictionaryReader.readAllNames(classObfuscationDictionary, reporter),
+ DictionaryReader.readAllNames(packageObfuscationDictionary, reporter),
+ useUniqueClassMemberNames,
+ keepParameterNames,
+ adaptClassStrings.build(),
+ adaptResourceFilenames.build(),
+ adaptResourceFileContents.build(),
+ keepDirectories.build());
reporter.failIfPendingErrors();
@@ -380,6 +388,7 @@
private final boolean verbose;
private final String renameSourceFileAttribute;
private final ProguardKeepAttributes keepAttributes;
+ private final ProguardPackageNameList keepPackageNamesPatterns;
private final ProguardClassFilter dontWarnPatterns;
private final ProguardClassFilter dontNotePatterns;
protected final ImmutableList<ProguardConfigurationRule> rules;
@@ -418,6 +427,7 @@
boolean verbose,
String renameSourceFileAttribute,
ProguardKeepAttributes keepAttributes,
+ ProguardPackageNameList keepPackageNamesPatterns,
ProguardClassFilter dontWarnPatterns,
ProguardClassFilter dontNotePatterns,
Set<ProguardConfigurationRule> rules,
@@ -454,6 +464,7 @@
this.verbose = verbose;
this.renameSourceFileAttribute = renameSourceFileAttribute;
this.keepAttributes = keepAttributes;
+ this.keepPackageNamesPatterns = keepPackageNamesPatterns;
this.dontWarnPatterns = dontWarnPatterns;
this.dontNotePatterns = dontNotePatterns;
this.rules = ImmutableList.copyOf(rules);
@@ -567,6 +578,10 @@
return keepAttributes;
}
+ public ProguardPackageNameList getKeepPackageNamesPatterns() {
+ return keepPackageNamesPatterns;
+ }
+
public ProguardClassFilter getDontWarnPatterns() {
return dontWarnPatterns;
}
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 991c763..10d3695 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -38,6 +38,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -178,6 +179,7 @@
}
private enum IdentifierType {
+ PACKAGE_NAME,
CLASS_NAME,
ANY
}
@@ -232,8 +234,7 @@
} else if (acceptString("keepattributes")) {
parseKeepAttributes();
} else if (acceptString("keeppackagenames")) {
- ProguardKeepPackageNamesRule rule = parseKeepPackageNamesRule(optionStart);
- configurationBuilder.addRule(rule);
+ parsePackageFilter(configurationBuilder::addKeepPackageNamesPattern);
} else if (acceptString("keepparameternames")) {
configurationBuilder.setKeepParameterNames(true, origin, getPosition(optionStart));
} else if (acceptString("checkdiscard")) {
@@ -610,17 +611,6 @@
return keepRuleBuilder.build();
}
- private ProguardKeepPackageNamesRule parseKeepPackageNamesRule(Position start)
- throws ProguardRuleParserException {
- ProguardKeepPackageNamesRule.Builder keepRuleBuilder =
- ProguardKeepPackageNamesRule.builder().setOrigin(origin).setStart(start);
- keepRuleBuilder.setClassNames(parseClassNames());
- Position end = getPosition();
- keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
- keepRuleBuilder.setEnd(end);
- return keepRuleBuilder.build();
- }
-
private ProguardCheckDiscardRule parseCheckDiscardRule(Position start)
throws ProguardRuleParserException {
ProguardCheckDiscardRule.Builder keepRuleBuilder = ProguardCheckDiscardRule.builder()
@@ -1441,6 +1431,13 @@
|| codePoint == '['
|| codePoint == ']';
+ private final Predicate<Integer> PACKAGE_NAME_PREDICATE =
+ codePoint ->
+ IdentifierUtils.isDexIdentifierPart(codePoint)
+ || codePoint == '.'
+ || codePoint == '*'
+ || codePoint == '?';
+
private String acceptClassName() {
return acceptString(CLASS_NAME_PREDICATE);
}
@@ -1539,9 +1536,11 @@
} else if (current == '?' || current == '%') {
wildcardsCollector.add(new ProguardWildcard.Pattern(String.valueOf((char) current)));
end += Character.charCount(current);
- } else if (CLASS_NAME_PREDICATE.test(current) || current == '>') {
+ } else if (kind == IdentifierType.PACKAGE_NAME
+ ? PACKAGE_NAME_PREDICATE.test(current)
+ : (CLASS_NAME_PREDICATE.test(current) || current == '>')) {
end += Character.charCount(current);
- } else if (current == '<') {
+ } else if (kind != IdentifierType.PACKAGE_NAME && current == '<') {
currentBackreference = new StringBuilder();
end += Character.charCount(current);
} else {
@@ -1651,6 +1650,25 @@
}
}
+ private void parsePackageFilter(BiConsumer<Boolean, ProguardPackageMatcher> consumer)
+ throws ProguardRuleParserException {
+ skipWhitespace();
+ if (isOptionalArgumentGiven()) {
+ do {
+ IdentifierPatternWithWildcardsAndNegation name =
+ acceptIdentifierWithBackreference(IdentifierType.PACKAGE_NAME, true);
+ if (name == null) {
+ throw parseError("Package name expected");
+ }
+ consumer.accept(
+ name.negated, new ProguardPackageMatcher(name.patternWithWildcards.pattern));
+ skipWhitespace();
+ } while (acceptChar(','));
+ } else {
+ consumer.accept(false, new ProguardPackageMatcher("**"));
+ }
+ }
+
private void parseClassFilter(Consumer<ProguardClassNameList> consumer)
throws ProguardRuleParserException {
skipWhitespace();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
deleted file mode 100644
index 767ecf7..0000000
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2017, 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 com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.position.Position;
-import java.util.List;
-
-public class ProguardKeepPackageNamesRule extends ProguardConfigurationRule {
-
- public static class Builder
- extends ProguardConfigurationRule.Builder<ProguardKeepPackageNamesRule, Builder> {
-
- private Builder() {
- super();
- }
-
- @Override
- public Builder self() {
- return this;
- }
-
- @Override
- public ProguardKeepPackageNamesRule build() {
- return new ProguardKeepPackageNamesRule(origin, getPosition(), source, classAnnotation,
- classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames,
- inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
- }
- }
-
- private ProguardKeepPackageNamesRule(
- Origin origin,
- Position position,
- String source,
- ProguardTypeMatcher classAnnotation,
- ProguardAccessFlags classAccessFlags,
- ProguardAccessFlags negatedClassAccessFlags,
- boolean classTypeNegated,
- ProguardClassType classType,
- ProguardClassNameList classNames,
- ProguardTypeMatcher inheritanceAnnotation,
- ProguardTypeMatcher inheritanceClassName,
- boolean inheritanceIsExtends,
- List<ProguardMemberRule> memberRules) {
- super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
- classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
- inheritanceIsExtends, memberRules);
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- @Override
- String typeString() {
- return "keeppackagenames";
- }
-}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardPackageMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardPackageMatcher.java
new file mode 100644
index 0000000..48c4703
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardPackageMatcher.java
@@ -0,0 +1,96 @@
+// Copyright (c) 2019, 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 com.android.tools.r8.graph.DexType;
+
+public class ProguardPackageMatcher {
+ private final String pattern;
+
+ public ProguardPackageMatcher(String pattern) {
+ this.pattern = pattern;
+ }
+
+ public boolean matches(DexType type) {
+ return matchPackageNameImpl(pattern, 0, type.getPackageName(), 0);
+ }
+
+ private static boolean matchPackageNameImpl(
+ String pattern, int patternIndex, String name, int nameIndex) {
+ for (int i = patternIndex; i < pattern.length(); i++) {
+ char patternChar = pattern.charAt(i);
+ switch (patternChar) {
+ case '*':
+ int nextPatternIndex = i + 1;
+ // Check for **.
+ boolean includeSeparators =
+ pattern.length() > (nextPatternIndex) && pattern.charAt(nextPatternIndex) == '*';
+ if (includeSeparators) {
+ nextPatternIndex += 1;
+ }
+
+ // Fast cases for the common case where a pattern ends with '*' or '**'.
+ if (nextPatternIndex == pattern.length()) {
+ if (includeSeparators) {
+ return true;
+ }
+ boolean hasSeparators = containsSeparatorsStartingAt(name, nameIndex);
+ return !hasSeparators;
+ }
+
+ // Match the rest of the pattern against the (non-empty) rest of the class name.
+ for (int nextNameIndex = nameIndex; nextNameIndex < name.length(); nextNameIndex++) {
+ if (!includeSeparators) {
+ // Stop at the first separator for just *.
+ if (name.charAt(nextNameIndex) == '.') {
+ return matchPackageNameImpl(pattern, nextPatternIndex, name, nextNameIndex);
+ }
+ }
+ if (matchPackageNameImpl(pattern, nextPatternIndex, name, nextNameIndex)) {
+ return true;
+ }
+ }
+
+ // Finally, check the case where the '*' or '**' eats all of the package name.
+ return matchPackageNameImpl(pattern, nextPatternIndex, name, name.length());
+
+ case '?':
+ if (nameIndex == name.length() || name.charAt(nameIndex) == '.') {
+ return false;
+ }
+ nameIndex++;
+ break;
+
+ default:
+ if (nameIndex == name.length() || patternChar != name.charAt(nameIndex++)) {
+ return false;
+ }
+ break;
+ }
+ }
+ return nameIndex == name.length();
+ }
+
+ private static boolean containsSeparatorsStartingAt(String className, int nameIndex) {
+ return className.indexOf('.', nameIndex) != -1;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof ProguardPackageMatcher)) {
+ return false;
+ }
+ ProguardPackageMatcher other = (ProguardPackageMatcher) o;
+ return pattern.equals(other.pattern);
+ }
+
+ @Override
+ public int hashCode() {
+ return pattern.hashCode();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardPackageNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardPackageNameList.java
new file mode 100644
index 0000000..6d45459
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardPackageNameList.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2019, 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 com.android.tools.r8.graph.DexType;
+import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
+import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
+import it.unimi.dsi.fastutil.objects.ObjectIterator;
+
+public class ProguardPackageNameList {
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+
+ /** Map used to store pairs of patterns and whether they are negated. */
+ private final Object2BooleanMap<ProguardPackageMatcher> matchers =
+ new Object2BooleanArrayMap<>();
+
+ private Builder() {}
+
+ public ProguardPackageNameList.Builder addPackageName(
+ boolean isNegated, ProguardPackageMatcher className) {
+ matchers.put(className, isNegated);
+ return this;
+ }
+
+ ProguardPackageNameList build() {
+ return new ProguardPackageNameList(matchers);
+ }
+ }
+
+ private final Object2BooleanMap<ProguardPackageMatcher> packageNames;
+
+ private ProguardPackageNameList(Object2BooleanMap<ProguardPackageMatcher> pacakgeNames) {
+ this.packageNames = pacakgeNames;
+ }
+
+ public void writeTo(StringBuilder builder) {
+ boolean first = true;
+ for (Object2BooleanMap.Entry<ProguardPackageMatcher> packageName :
+ packageNames.object2BooleanEntrySet()) {
+ if (!first) {
+ builder.append(',');
+ }
+ if (packageName.getBooleanValue()) {
+ builder.append('!');
+ }
+ builder.append(packageName.getKey().toString());
+ first = false;
+ }
+ }
+
+ public boolean matches(DexType type) {
+ for (Object2BooleanMap.Entry<ProguardPackageMatcher> packageName :
+ packageNames.object2BooleanEntrySet()) {
+ if (packageName.getKey().matches(type)) {
+ // If we match a negation, abort as non-match. If we match a positive, return true.
+ return !packageName.getBooleanValue();
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof ProguardPackageNameList)) {
+ return false;
+ }
+ ProguardPackageNameList other = (ProguardPackageNameList) o;
+ if (packageNames.size() != other.packageNames.size()) {
+ return false;
+ }
+ ObjectIterator<Object2BooleanMap.Entry<ProguardPackageMatcher>> i1 =
+ packageNames.object2BooleanEntrySet().iterator();
+ ObjectIterator<Object2BooleanMap.Entry<ProguardPackageMatcher>> i2 =
+ other.packageNames.object2BooleanEntrySet().iterator();
+ while (i1.hasNext()) {
+ Object2BooleanMap.Entry<ProguardPackageMatcher> e1 = i1.next();
+ Object2BooleanMap.Entry<ProguardPackageMatcher> e2 = i2.next();
+ if (!e1.equals(e2)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 0;
+ ObjectIterator<Object2BooleanMap.Entry<ProguardPackageMatcher>> iterator =
+ packageNames.object2BooleanEntrySet().iterator();
+ while (iterator.hasNext()) {
+ Object2BooleanMap.Entry<ProguardPackageMatcher> packageMatcher = iterator.next();
+ hash = hash * (packageMatcher.getBooleanValue() ? 1 : 2);
+ hash = hash * 13 + packageMatcher.getKey().hashCode();
+ }
+ return hash;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index a0c9735..4ce20ba 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -69,7 +69,6 @@
private final Set<DexReference> noOptimization = Sets.newIdentityHashSet();
private final Set<DexReference> noObfuscation = Sets.newIdentityHashSet();
private final LinkedHashMap<DexReference, DexReference> reasonAsked = new LinkedHashMap<>();
- private final Set<DexReference> keepPackageName = Sets.newIdentityHashSet();
private final Set<ProguardConfigurationRule> rulesThatUseExtendsOrImplementsWrong =
Sets.newIdentityHashSet();
private final Set<DexReference> checkDiscarded = Sets.newIdentityHashSet();
@@ -179,8 +178,7 @@
markMatchingFields(clazz, memberKeepRules, rule, preconditionSupplier);
markMatchingMethods(clazz, memberKeepRules, rule, preconditionSupplier);
}
- } else if (rule instanceof ProguardWhyAreYouKeepingRule
- || rule instanceof ProguardKeepPackageNamesRule) {
+ } else if (rule instanceof ProguardWhyAreYouKeepingRule) {
markClass(clazz, rule);
markMatchingVisibleMethods(clazz, memberKeepRules, rule, null, true);
markMatchingVisibleFields(clazz, memberKeepRules, rule, null, true);
@@ -264,7 +262,6 @@
noOptimization,
noObfuscation,
ImmutableList.copyOf(reasonAsked.values()),
- keepPackageName,
checkDiscarded,
alwaysInline,
forceInline,
@@ -946,8 +943,6 @@
noSideEffects.put(item.toReference(), rule);
} else if (context instanceof ProguardWhyAreYouKeepingRule) {
reasonAsked.computeIfAbsent(item.toReference(), i -> i);
- } else if (context instanceof ProguardKeepPackageNamesRule) {
- keepPackageName.add(item.toReference());
} else if (context instanceof ProguardAssumeValuesRule) {
assumedValues.put(item.toReference(), rule);
} else if (context instanceof ProguardCheckDiscardRule) {
@@ -1011,7 +1006,6 @@
public final Set<DexReference> noOptimization;
public final Set<DexReference> noObfuscation;
public final ImmutableList<DexReference> reasonAsked;
- public final Set<DexReference> keepPackageName;
public final Set<DexReference> checkDiscarded;
public final Set<DexMethod> alwaysInline;
public final Set<DexMethod> forceInline;
@@ -1032,7 +1026,6 @@
Set<DexReference> noOptimization,
Set<DexReference> noObfuscation,
ImmutableList<DexReference> reasonAsked,
- Set<DexReference> keepPackageName,
Set<DexReference> checkDiscarded,
Set<DexMethod> alwaysInline,
Set<DexMethod> forceInline,
@@ -1050,7 +1043,6 @@
this.noOptimization = noOptimization;
this.noObfuscation = noObfuscation;
this.reasonAsked = reasonAsked;
- this.keepPackageName = Collections.unmodifiableSet(keepPackageName);
this.checkDiscarded = Collections.unmodifiableSet(checkDiscarded);
this.alwaysInline = Collections.unmodifiableSet(alwaysInline);
this.forceInline = Collections.unmodifiableSet(forceInline);
@@ -1071,7 +1063,6 @@
this.noOptimization = lense.rewriteMutableReferencesConservatively(previous.noOptimization);
this.noObfuscation = lense.rewriteMutableReferencesConservatively(previous.noObfuscation);
this.reasonAsked = lense.rewriteReferencesConservatively(previous.reasonAsked);
- this.keepPackageName = lense.rewriteReferencesConservatively(previous.keepPackageName);
this.checkDiscarded = lense.rewriteReferencesConservatively(previous.checkDiscarded);
this.alwaysInline = lense.rewriteMethodsConservatively(previous.alwaysInline);
this.forceInline = lense.rewriteMethodsConservatively(previous.forceInline);
@@ -1310,7 +1301,6 @@
builder.append("\nnoOptimization: " + noOptimization.size());
builder.append("\nnoObfuscation: " + noObfuscation.size());
builder.append("\nreasonAsked: " + reasonAsked.size());
- builder.append("\nkeepPackageName: " + keepPackageName.size());
builder.append("\ncheckDiscarded: " + checkDiscarded.size());
builder.append("\nnoSideEffects: " + noSideEffects.size());
builder.append("\nassumedValues: " + assumedValues.size());
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 62b2b65..0047eec 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -64,6 +64,10 @@
return addKeepRules("-keep class ** { *; }");
}
+ public T addKeepAndMinifyAllClassesRule() {
+ return addKeepRules("-keep,allowobfuscation class ** { *; }");
+ }
+
public T addKeepAllInterfacesRule() {
return addKeepRules("-keep interface ** { *; }");
}
diff --git a/src/test/java/com/android/tools/r8/naming/KeepPackageNamesTest.java b/src/test/java/com/android/tools/r8/naming/KeepPackageNamesTest.java
new file mode 100644
index 0000000..e57c596
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/KeepPackageNamesTest.java
@@ -0,0 +1,103 @@
+// Copyright (c) 2019, 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.naming;
+
+import static com.android.tools.r8.utils.DescriptorUtils.getPackageNameFromDescriptor;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.naming.keeppackagenames.Top;
+import com.android.tools.r8.naming.keeppackagenames.sub.SubClass;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeepPackageNamesTest extends TestBase {
+ private static final String PACKAGE_NAME = Top.class.getPackage().getName();
+ private static final List<Class<?>> CLASSES = ImmutableList.of(Top.class, SubClass.class);
+
+ enum TestConfig {
+ SINGLE_ASTERISK,
+ DOUBLE_ASTERISKS;
+
+ public String getKeepRule() {
+ switch (this) {
+ case SINGLE_ASTERISK:
+ return "-keeppackagenames com.android.tools.r8.naming.keeppackage*";
+ case DOUBLE_ASTERISKS:
+ return "-keeppackagenames com.android.tools.r8.naming.keeppackage**";
+ }
+ throw new Unreachable();
+ }
+
+ public void inspect(CodeInspector inspector) {
+ ClassSubject top = inspector.clazz(Top.class);
+ assertThat(top, isPresent());
+ assertThat(top, isRenamed());
+ assertEquals(
+ getPackageNameFromDescriptor(top.getOriginalDescriptor()),
+ getPackageNameFromDescriptor(top.getFinalDescriptor()));
+
+ ClassSubject sub = inspector.clazz(SubClass.class);
+ assertThat(sub, isPresent());
+ assertThat(sub, isRenamed());
+ switch (this) {
+ case SINGLE_ASTERISK:
+ assertNotEquals(
+ getPackageNameFromDescriptor(sub.getOriginalDescriptor()),
+ getPackageNameFromDescriptor(sub.getFinalDescriptor()));
+ assertThat(
+ getPackageNameFromDescriptor(sub.getFinalDescriptor()), containsString(PACKAGE_NAME));
+ break;
+ case DOUBLE_ASTERISKS:
+ assertEquals(
+ getPackageNameFromDescriptor(sub.getOriginalDescriptor()),
+ getPackageNameFromDescriptor(sub.getFinalDescriptor()));
+ break;
+ }
+ }
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Object[] parameters() {
+ return TestConfig.values();
+ }
+
+ private final TestConfig config;
+
+ public KeepPackageNamesTest(TestConfig config) {
+ this.config = config;
+ }
+
+ @Test
+ public void testProguard() throws Exception {
+ testForProguard()
+ .addProgramClasses(CLASSES)
+ .addKeepAndMinifyAllClassesRule()
+ .addKeepRules(config.getKeepRule())
+ .compile()
+ .inspect(config::inspect);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(Backend.DEX)
+ .addProgramClasses(CLASSES)
+ .addKeepAndMinifyAllClassesRule()
+ .addKeepRules(config.getKeepRule())
+ .compile()
+ .inspect(config::inspect);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/keeppackagenames/Top.java b/src/test/java/com/android/tools/r8/naming/keeppackagenames/Top.java
new file mode 100644
index 0000000..0d156b4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/keeppackagenames/Top.java
@@ -0,0 +1,6 @@
+// Copyright (c) 2019, 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.naming.keeppackagenames;
+
+public class Top {}
diff --git a/src/test/java/com/android/tools/r8/naming/keeppackagenames/sub/SubClass.java b/src/test/java/com/android/tools/r8/naming/keeppackagenames/sub/SubClass.java
new file mode 100644
index 0000000..2497a53
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/keeppackagenames/sub/SubClass.java
@@ -0,0 +1,6 @@
+// Copyright (c) 2019, 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.naming.keeppackagenames.sub;
+
+public class SubClass {}
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index d82aef4..7516576 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -1304,6 +1304,51 @@
testKeepattributes(expected, config);
}
+ private void testKeeppackagenames(ProguardPackageNameList expected, String config) {
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(new DexItemFactory(), reporter);
+ parser.parse(createConfigurationForTesting(ImmutableList.of(config)));
+ verifyParserEndsCleanly();
+ assertEquals(expected, parser.getConfigRawForTesting().getKeepPackageNamesPatterns());
+ }
+
+ @Test
+ public void parseKeeppackagenames() {
+ ProguardPackageNameList xxxYYY =
+ ProguardPackageNameList.builder()
+ .addPackageName(false, new ProguardPackageMatcher("xxx"))
+ .addPackageName(false, new ProguardPackageMatcher("yyy"))
+ .build();
+ testKeeppackagenames(xxxYYY, "-keeppackagenames xxx,yyy");
+ testKeeppackagenames(xxxYYY, "-keeppackagenames xxx, yyy");
+ testKeeppackagenames(xxxYYY, "-keeppackagenames xxx ,yyy");
+ testKeeppackagenames(xxxYYY, "-keeppackagenames xxx , yyy");
+ testKeeppackagenames(xxxYYY, "-keeppackagenames xxx , yyy ");
+ testKeeppackagenames(xxxYYY, "-keeppackagenames xxx , yyy \n");
+ testKeeppackagenames(xxxYYY, "-keeppackagenames \"xxx\",\"yyy\"");
+
+ testKeeppackagenames(
+ ProguardPackageNameList.builder()
+ .addPackageName(false, new ProguardPackageMatcher("com.**"))
+ .addPackageName(false, new ProguardPackageMatcher("org.*"))
+ .build(),
+ "-keeppackagenames com.**, org.*");
+
+ testKeeppackagenames(
+ ProguardPackageNameList.builder()
+ .addPackageName(false, new ProguardPackageMatcher("c?m.**"))
+ .addPackageName(false, new ProguardPackageMatcher("?r?.*"))
+ .build(),
+ "-keeppackagenames c?m.**, ?r?.*");
+
+ testKeeppackagenames(
+ ProguardPackageNameList.builder()
+ .addPackageName(true, new ProguardPackageMatcher("c?m.**"))
+ .addPackageName(true, new ProguardPackageMatcher("?r?.*"))
+ .build(),
+ "-keeppackagenames !c?m.**, !?r?.*");
+ }
+
@Test
public void parseInvalidKeepattributes() throws Exception {
try {
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardPackageNameMatcherTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardPackageNameMatcherTest.java
new file mode 100644
index 0000000..d730c34
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardPackageNameMatcherTest.java
@@ -0,0 +1,146 @@
+// Copyright (c) 2019, 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 org.junit.Test;
+
+public class ProguardPackageNameMatcherTest {
+ private DexItemFactory dexItemFactory = new DexItemFactory();
+
+ private boolean matches(ProguardPackageNameList matcher, String packageName) {
+ return matcher.matches(dexItemFactory.createType("L" + packageName.replace('.', '/') + "/A;"));
+ }
+
+ @Test
+ public void testSimple() {
+ ProguardPackageNameList matcher =
+ ProguardPackageNameList.builder()
+ .addPackageName(false, new ProguardPackageMatcher("com.example"))
+ .build();
+ assertTrue(matches(matcher, "com.example"));
+ assertFalse(matches(matcher, "com.exampl"));
+ assertFalse(matches(matcher, "com.example.a"));
+ }
+
+ @Test
+ public void testSingleEnd() {
+ ProguardPackageNameList matcher =
+ ProguardPackageNameList.builder()
+ .addPackageName(false, new ProguardPackageMatcher("com.example*"))
+ .build();
+ assertTrue(matches(matcher, "com.example"));
+ assertTrue(matches(matcher, "com.example1"));
+ assertTrue(matches(matcher, "com.example2"));
+ assertFalse(matches(matcher, "com.exampl"));
+ assertFalse(matches(matcher, "com.example.a"));
+ }
+
+ @Test
+ public void testSingleBeginning() {
+ ProguardPackageNameList matcher =
+ ProguardPackageNameList.builder()
+ .addPackageName(false, new ProguardPackageMatcher("*.example"))
+ .build();
+ assertTrue(matches(matcher, "com.example"));
+ assertTrue(matches(matcher, "org.example"));
+ assertFalse(matches(matcher, "com.example1"));
+ assertFalse(matches(matcher, "org.example1"));
+ assertFalse(matches(matcher, "com.exampl"));
+ assertFalse(matches(matcher, "com.example.a"));
+ }
+
+ @Test
+ public void testDoubleEnd() {
+ ProguardPackageNameList matcher =
+ ProguardPackageNameList.builder()
+ .addPackageName(false, new ProguardPackageMatcher("com.example**"))
+ .build();
+ assertTrue(matches(matcher, "com.example"));
+ assertTrue(matches(matcher, "com.example1"));
+ assertTrue(matches(matcher, "com.example2"));
+ assertTrue(matches(matcher, "com.example.a"));
+ assertTrue(matches(matcher, "com.example.a.a"));
+ assertFalse(matches(matcher, "com.exampl"));
+ }
+
+ @Test
+ public void testDoubleBeginning() {
+ ProguardPackageNameList matcher =
+ ProguardPackageNameList.builder()
+ .addPackageName(false, new ProguardPackageMatcher("**example"))
+ .build();
+ assertTrue(matches(matcher, "com.example"));
+ assertTrue(matches(matcher, "org.example"));
+ assertTrue(matches(matcher, "com.a.example"));
+ assertTrue(matches(matcher, "com.a.a.example"));
+ assertTrue(matches(matcher, "comexample"));
+ assertFalse(matches(matcher, "com.example1"));
+ }
+
+ @Test
+ public void testQuestionMark() {
+ ProguardPackageNameList matcher =
+ ProguardPackageNameList.builder()
+ .addPackageName(false, new ProguardPackageMatcher("com.e?ample"))
+ .build();
+ assertTrue(matches(matcher, "com.example"));
+ assertTrue(matches(matcher, "com.eyample"));
+ assertFalse(matches(matcher, "com.example1"));
+ assertFalse(matches(matcher, "com.example.a"));
+ }
+
+ @Test
+ public void testList() {
+ ProguardPackageNameList matcher =
+ ProguardPackageNameList.builder()
+ .addPackageName(false, new ProguardPackageMatcher("com.example"))
+ .addPackageName(false, new ProguardPackageMatcher("org.example"))
+ .build();
+ assertTrue(matches(matcher, "com.example"));
+ assertTrue(matches(matcher, "org.example"));
+ assertFalse(matches(matcher, "com.example1"));
+ assertFalse(matches(matcher, "com.example.a"));
+ assertFalse(matches(matcher, "org.example1"));
+ assertFalse(matches(matcher, "org.example.a"));
+ }
+
+ @Test
+ public void testListNegation() {
+ ProguardPackageNameList matcher =
+ ProguardPackageNameList.builder()
+ .addPackageName(true, new ProguardPackageMatcher("!org.example"))
+ .addPackageName(false, new ProguardPackageMatcher("*.example"))
+ .build();
+ assertTrue(matches(matcher, "com.example"));
+ assertTrue(matches(matcher, "org.example"));
+ }
+
+ @Test
+ public void testListNegationNotMatched() {
+ ProguardPackageNameList matcher =
+ ProguardPackageNameList.builder()
+ .addPackageName(false, new ProguardPackageMatcher("*.example"))
+ .addPackageName(true, new ProguardPackageMatcher("!org.example"))
+ .build();
+ assertTrue(matches(matcher, "com.example"));
+ // Negations only stops attempts on subsequent names.
+ assertTrue(matches(matcher, "org.example"));
+ }
+
+ @Test
+ public void testNegateAll() {
+ ProguardPackageNameList matcher =
+ ProguardPackageNameList.builder()
+ .addPackageName(true, new ProguardPackageMatcher("!**"))
+ .build();
+ assertFalse(matches(matcher, "com"));
+ assertFalse(matches(matcher, "com.example"));
+ assertFalse(matches(matcher, "com.example.a"));
+ }
+}