Use repackaging framework for minifying packages
ClassNameMinifier do not know about package privacy. This CL adds a
minification option for repackaging to check for -keeppackagenames and
allows for the ClassNameMinifier to rely on all classes being placed
in packages already.
Bug: 172496438
Bug: 150589374
Bug: 172254047
Change-Id: I1fb747691ff5da030bc5d2792a80e1db3dd3d752
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index afc04c7..5c9fc0f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1184,6 +1184,9 @@
return this;
}
Builder builder = builder(this);
+ if (isNonPrivateVirtualMethod() && isLibraryMethodOverride() != OptionalBool.unknown()) {
+ builder.setIsLibraryMethodOverride(isLibraryMethodOverride());
+ }
builder.setMethod(method);
// TODO(b/112847660): Fix type fixers that use this method: Class staticizer
// TODO(b/112847660): Fix type fixers that use this method: Uninstantiated type optimization
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 0752d20..d1c5b3f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -2330,8 +2330,17 @@
MethodHandleType type,
DexMember<? extends DexItem, ? extends DexMember<?, ?>> fieldOrMethod,
boolean isInterface) {
+ return createMethodHandle(type, fieldOrMethod, isInterface, null);
+ }
+
+ public DexMethodHandle createMethodHandle(
+ MethodHandleType type,
+ DexMember<? extends DexItem, ? extends DexMember<?, ?>> fieldOrMethod,
+ boolean isInterface,
+ DexMethod rewrittenTarget) {
assert !sorted;
- DexMethodHandle methodHandle = new DexMethodHandle(type, fieldOrMethod, isInterface);
+ DexMethodHandle methodHandle =
+ new DexMethodHandle(type, fieldOrMethod, isInterface, rewrittenTarget);
return canonicalize(methodHandles, methodHandle);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index 0bdb208..71bc104 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -207,13 +207,6 @@
public DexMethodHandle(
MethodHandleType type,
DexMember<? extends DexItem, ? extends DexMember<?, ?>> member,
- boolean isInterface) {
- this(type, member, isInterface, null);
- }
-
- public DexMethodHandle(
- MethodHandleType type,
- DexMember<? extends DexItem, ? extends DexMember<?, ?>> member,
boolean isInterface,
DexMethod rewrittenTarget) {
this.type = type;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
index 4bc8714..b8a70f9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
@@ -137,17 +137,21 @@
if (newType != oldType || actualTarget != invokedMethod || rewrittenTarget != actualTarget) {
DexClass holder = definitions.definitionFor(actualTarget.holder, context);
boolean isInterface = holder != null ? holder.isInterface() : methodHandle.isInterface;
- return new DexMethodHandle(
- newType,
- actualTarget,
- isInterface,
- rewrittenTarget != actualTarget ? rewrittenTarget : null);
+ return definitions
+ .dexItemFactory()
+ .createMethodHandle(
+ newType,
+ actualTarget,
+ isInterface,
+ rewrittenTarget != actualTarget ? rewrittenTarget : null);
}
} else {
DexField field = methodHandle.asField();
DexField actualField = graphLens().lookupField(field);
if (actualField != field) {
- return new DexMethodHandle(methodHandle.type, actualField, methodHandle.isInterface);
+ return definitions
+ .dexItemFactory()
+ .createMethodHandle(methodHandle.type, actualField, methodHandle.isInterface);
}
}
return methodHandle;
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 3a25d00..1587e8a 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.jar;
+import static com.android.tools.r8.repackaging.Repackaging.DefaultRepackagingConfiguration.TEMPORARY_PACKAGE_NAME;
import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION;
import com.android.tools.r8.ByteDataView;
@@ -185,6 +186,7 @@
}
String desc = namingLens.lookupDescriptor(clazz.type).toString();
String name = namingLens.lookupInternalName(clazz.type);
+ assert !name.contains(TEMPORARY_PACKAGE_NAME);
String signature = clazz.getClassSignature().toRenamedString(namingLens, isTypeMissing);
String superName =
clazz.type == options.itemFactory.objectType
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java
index 8aa95b2..2e1ddf9 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeReference.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.naming.NamingLens;
@@ -36,6 +35,10 @@
assert unknown != null;
}
+ public DexType getKnown() {
+ return known;
+ }
+
static KotlinTypeReference fromBinaryName(String binaryName, DexItemFactory factory) {
if (DescriptorUtils.isValidBinaryName(binaryName)) {
return fromDescriptor(
@@ -63,19 +66,11 @@
return unknown;
}
assert known != null;
- if (!known.isClassType()) {
- return known.descriptor.toString();
- }
- DexType rewrittenType = appView.graphLens().lookupClassType(known);
- if (appView.appInfo().hasLiveness()
- && !appView.withLiveness().appInfo().isNonProgramTypeOrLiveProgramType(rewrittenType)) {
+ DexType rewrittenType = toRewrittenTypeOrNull(appView, known);
+ if (rewrittenType == null) {
return defaultValue;
}
- DexString descriptor = namingLens.lookupDescriptor(rewrittenType);
- if (descriptor != null) {
- return descriptor.toString();
- }
- return defaultValue;
+ return namingLens.lookupDescriptor(rewrittenType).toString();
}
String toRenamedBinaryNameOrDefault(
@@ -95,6 +90,25 @@
return DescriptorUtils.getBinaryNameFromDescriptor(descriptor);
}
+ private static DexType toRewrittenTypeOrNull(AppView<?> appView, DexType type) {
+ if (type.isArrayType()) {
+ DexType rewrittenBaseType =
+ toRewrittenTypeOrNull(appView, type.toBaseType(appView.dexItemFactory()));
+ return rewrittenBaseType != null
+ ? type.replaceBaseType(rewrittenBaseType, appView.dexItemFactory())
+ : null;
+ }
+ if (!type.isClassType()) {
+ return type;
+ }
+ DexType rewrittenType = appView.graphLens().lookupClassType(type);
+ if (appView.appInfo().hasLiveness()
+ && !appView.withLiveness().appInfo().isNonProgramTypeOrLiveProgramType(rewrittenType)) {
+ return null;
+ }
+ return rewrittenType;
+ }
+
@Override
public String toString() {
return known != null ? known.descriptor.toString() : unknown;
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 771c336..e6937ea 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -39,8 +39,6 @@
private final ClassNamingStrategy classNamingStrategy;
private final PackageNamingStrategy packageNamingStrategy;
private final Iterable<? extends DexClass> classes;
- private final boolean isAccessModificationAllowed;
- private final Set<String> noObfuscationPrefixes = Sets.newHashSet();
private final Set<String> usedPackagePrefixes = Sets.newHashSet();
private final Set<String> usedTypeNames = Sets.newHashSet();
private final Map<DexType, DexString> renaming = Maps.newIdentityHashMap();
@@ -50,6 +48,7 @@
private final Namespace topLevelState;
private final boolean allowMixedCaseNaming;
private final Predicate<String> isUsed;
+ private final ProguardPackageNameList keepPackageNames;
ClassNameMinifier(
AppView<AppInfoWithLiveness> appView,
@@ -61,8 +60,6 @@
this.packageNamingStrategy = packageNamingStrategy;
this.classes = classes;
InternalOptions options = appView.options();
- this.isAccessModificationAllowed =
- options.getProguardConfiguration().isAccessModificationAllowed();
this.keepInnerClassStructure = options.keepInnerClassStructure();
// Initialize top-level naming state.
@@ -70,9 +67,8 @@
String newPackageDescriptor =
StringUtils.replaceAll(options.getProguardConfiguration().getPackagePrefix(), ".", "/");
if (!newPackageDescriptor.isEmpty()) {
- registerPackagePrefixesAsUsed(newPackageDescriptor, false);
+ registerPackagePrefixesAsUsed(newPackageDescriptor);
}
-
states.put("", topLevelState);
if (options.getProguardConfiguration().hasDontUseMixedCaseClassnames()) {
@@ -82,6 +78,7 @@
allowMixedCaseNaming = true;
isUsed = usedTypeNames::contains;
}
+ keepPackageNames = options.getProguardConfiguration().getKeepPackageNamesPatterns();
}
private void setUsedTypeName(String typeName) {
@@ -180,8 +177,7 @@
private void registerClassAsUsed(DexType type, DexString descriptor) {
renaming.put(type, descriptor);
registerPackagePrefixesAsUsed(
- getParentPackagePrefix(getClassBinaryNameFromDescriptor(descriptor.toSourceString())),
- isAccessModificationAllowed);
+ getParentPackagePrefix(getClassBinaryNameFromDescriptor(descriptor.toSourceString())));
setUsedTypeName(descriptor.toString());
if (keepInnerClassStructure) {
DexType outerClass = getOutClassForType(type);
@@ -197,15 +193,13 @@
}
/** Registers the given package prefix and all of parent packages as used. */
- private void registerPackagePrefixesAsUsed(String packagePrefix, boolean isMinificationAllowed) {
- // If -allowaccessmodification is not set, we may keep classes in their original packages,
- // accounting for package-private accesses.
- if (!isMinificationAllowed) {
- noObfuscationPrefixes.add(packagePrefix);
- }
+ private void registerPackagePrefixesAsUsed(String packagePrefix) {
String usedPrefix = packagePrefix;
while (usedPrefix.length() > 0) {
+ // Only reserve the actual package and not all parent packages.
+ // TODO(b/178563208): Only mark prefix as used if usedPrefix.equals(packagePrefix)
usedPackagePrefixes.add(usedPrefix);
+ states.computeIfAbsent(usedPrefix, Namespace::new);
usedPrefix = getParentPackagePrefix(usedPrefix);
}
}
@@ -259,9 +253,7 @@
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.
- ProguardPackageNameList keepPackageNames =
- appView.options().getProguardConfiguration().getKeepPackageNamesPatterns();
- if (noObfuscationPrefixes.contains(packageName) || keepPackageNames.matches(type)) {
+ if (keepPackageNames.matches(type)) {
return states.computeIfAbsent(packageName, Namespace::new);
}
return getStateForPackagePrefix(packageName);
@@ -272,15 +264,9 @@
if (state == null) {
// Calculate the parent package prefix, e.g., La/b/c -> La/b
String parentPackage = getParentPackagePrefix(prefix);
- Namespace superState;
- if (noObfuscationPrefixes.contains(parentPackage)) {
- // Restore a state for parent package prefix if it should be kept.
- superState = states.computeIfAbsent(parentPackage, Namespace::new);
- } else {
- // Create a state for parent package prefix, if necessary, in a recursive manner.
- // That recursion should end when the parent package hits the top-level, "".
- superState = getStateForPackagePrefix(parentPackage);
- }
+ // Create a state for parent package prefix, if necessary, in a recursive manner.
+ // That recursion should end when the parent package hits the top-level, "".
+ Namespace superState = getStateForPackagePrefix(parentPackage);
// From the super state, get a renamed package prefix for the current level.
String renamedPackagePrefix = superState.nextPackagePrefix();
// Create a new state, which corresponds to a new name space, for the current level.
diff --git a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
index eba0a87..cdfa649 100644
--- a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
+++ b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
@@ -9,6 +9,7 @@
import static com.android.tools.r8.utils.DescriptorUtils.INNER_CLASS_SEPARATOR;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
@@ -26,6 +27,7 @@
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
@@ -291,26 +293,46 @@
public static class DefaultRepackagingConfiguration implements RepackagingConfiguration {
+ private final AppView<?> appView;
private final DexItemFactory dexItemFactory;
private final ProguardConfiguration proguardConfiguration;
+ public static final String TEMPORARY_PACKAGE_NAME = "TEMPORARY_PACKAGE_NAME_FOR_";
- public DefaultRepackagingConfiguration(
- DexItemFactory dexItemFactory, ProguardConfiguration proguardConfiguration) {
- this.dexItemFactory = dexItemFactory;
- this.proguardConfiguration = proguardConfiguration;
+ public DefaultRepackagingConfiguration(AppView<?> appView) {
+ this.appView = appView;
+ this.dexItemFactory = appView.dexItemFactory();
+ this.proguardConfiguration = appView.options().getProguardConfiguration();
}
@Override
public String getNewPackageDescriptor(ProgramPackage pkg, Set<String> seenPackageDescriptors) {
String newPackageDescriptor =
DescriptorUtils.getBinaryNameFromJavaType(proguardConfiguration.getPackagePrefix());
- if (proguardConfiguration.getPackageObfuscationMode().isRepackageClasses()) {
+ PackageObfuscationMode packageObfuscationMode =
+ proguardConfiguration.getPackageObfuscationMode();
+ if (packageObfuscationMode.isRepackageClasses()) {
return newPackageDescriptor;
}
+ assert packageObfuscationMode.isFlattenPackageHierarchy()
+ || packageObfuscationMode.isMinification();
if (!newPackageDescriptor.isEmpty()) {
newPackageDescriptor += DESCRIPTOR_PACKAGE_SEPARATOR;
}
- newPackageDescriptor += pkg.getLastPackageName();
+ if (packageObfuscationMode.isMinification()) {
+ assert !proguardConfiguration.hasApplyMappingFile();
+ // Always keep top-level classes since there packages can never be minified.
+ if (pkg.getPackageDescriptor().equals("")
+ || proguardConfiguration.getKeepPackageNamesPatterns().matches(pkg)
+ || mayHavePinnedPackagePrivateOrProtectedItem(pkg)) {
+ return pkg.getPackageDescriptor();
+ }
+ // For us to rename shaking/A to a/a if we have a class shaking/Kept, we have to propose
+ // a different name than the last package name - the class will be minified in
+ // ClassNameMinifier.
+ newPackageDescriptor += TEMPORARY_PACKAGE_NAME + pkg.getLastPackageName();
+ } else {
+ newPackageDescriptor += pkg.getLastPackageName();
+ }
String finalPackageDescriptor = newPackageDescriptor;
for (int i = 1; seenPackageDescriptors.contains(finalPackageDescriptor); i++) {
finalPackageDescriptor = newPackageDescriptor + INNER_CLASS_SEPARATOR + i;
@@ -318,6 +340,24 @@
return finalPackageDescriptor;
}
+ private boolean mayHavePinnedPackagePrivateOrProtectedItem(ProgramPackage pkg) {
+ // Go through all package classes and members to see if there is a pinned package-private
+ // item, in which case we cannot move it because there may be a reflective access to it.
+ for (DexProgramClass clazz : pkg.classesInPackage()) {
+ if (clazz.getAccessFlags().isPackagePrivateOrProtected()
+ && appView.getKeepInfo().getClassInfo(clazz).isPinned()) {
+ return true;
+ }
+ for (DexEncodedMember<?, ?> member : clazz.members()) {
+ if (member.getAccessFlags().isPackagePrivateOrProtected()
+ && appView.getKeepInfo().getMemberInfo(member, clazz).isPinned()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
@Override
public DexType getRepackagedType(
DexProgramClass clazz,
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index da98174..81239aa 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
@@ -72,6 +73,14 @@
*/
public abstract KeepFieldInfo getFieldInfo(DexEncodedField field, DexProgramClass holder);
+ public KeepMemberInfo<?, ?> getMemberInfo(DexEncodedMember<?, ?> member, DexProgramClass holder) {
+ if (member.isDexEncodedField()) {
+ return getFieldInfo(member.asDexEncodedField(), holder);
+ }
+ assert member.isDexEncodedMethod();
+ return getMethodInfo(member.asDexEncodedMethod(), holder);
+ }
+
public final KeepClassInfo getClassInfo(DexType type, DexDefinitionSupplier definitions) {
DexProgramClass clazz = asProgramClassOrNull(definitions.definitionFor(type));
return clazz == null ? keepInfoForNonProgramClass() : getClassInfo(clazz);
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 8fdd330..6d3a8ca 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -397,6 +397,12 @@
synthesizeKeepRulesForRecompilation();
}
+ if (packageObfuscationMode == PackageObfuscationMode.NONE
+ && obfuscating
+ && !hasApplyMappingFile()) {
+ packageObfuscationMode = PackageObfuscationMode.MINIFICATION;
+ }
+
return buildRaw();
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardPackageMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardPackageMatcher.java
index 48c4703..f31087d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardPackageMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardPackageMatcher.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.shaking;
-import com.android.tools.r8.graph.DexType;
-
public class ProguardPackageMatcher {
private final String pattern;
@@ -13,8 +11,8 @@
this.pattern = pattern;
}
- public boolean matches(DexType type) {
- return matchPackageNameImpl(pattern, 0, type.getPackageName(), 0);
+ public boolean matches(String packageName) {
+ return matchPackageNameImpl(pattern, 0, packageName, 0);
}
private static boolean matchPackageNameImpl(
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardPackageNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardPackageNameList.java
index 6d45459..6dcd07b 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardPackageNameList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardPackageNameList.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.shaking;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramPackage;
import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
@@ -56,9 +57,17 @@
}
public boolean matches(DexType type) {
+ return matches(type.getPackageName());
+ }
+
+ public boolean matches(ProgramPackage pkg) {
+ return matches(pkg.getPackageName());
+ }
+
+ private boolean matches(String pkgName) {
for (Object2BooleanMap.Entry<ProguardPackageMatcher> packageName :
packageNames.object2BooleanEntrySet()) {
- if (packageName.getKey().matches(type)) {
+ if (packageName.getKey().matches(pkgName)) {
// If we match a negation, abort as non-match. If we match a positive, return true.
return !packageName.getBooleanValue();
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 4e787b6..ea310aa 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1046,8 +1046,11 @@
}
public enum PackageObfuscationMode {
- // General package obfuscation.
+ // No package obfuscation.
NONE,
+ // Strategy based on ordinary package obfuscation when no package-obfuscation mode is specified
+ // by the users. In practice this falls back to FLATTEN but with keeping package-names.
+ MINIFICATION,
// Repackaging all classes into the single user-given (or top-level) package.
REPACKAGE,
// Repackaging all packages into the single user-given (or top-level) package.
@@ -1065,6 +1068,10 @@
return this == REPACKAGE;
}
+ public boolean isMinification() {
+ return this == MINIFICATION;
+ }
+
public boolean isSome() {
return !isNone();
}
@@ -1242,10 +1249,7 @@
public Consumer<String> processingContextsConsumer = null;
public Function<AppView<AppInfoWithLiveness>, RepackagingConfiguration>
- repackagingConfigurationFactory =
- appView ->
- new DefaultRepackagingConfiguration(
- appView.dexItemFactory(), appView.options().getProguardConfiguration());
+ repackagingConfigurationFactory = DefaultRepackagingConfiguration::new;
public BiConsumer<DexItemFactory, HorizontallyMergedClasses> horizontallyMergedClassesConsumer =
ConsumerUtils.emptyBiConsumer();
diff --git a/src/test/examples/naming101/c.java b/src/test/examples/naming101/c.java
index 21744bc..7477024 100644
--- a/src/test/examples/naming101/c.java
+++ b/src/test/examples/naming101/c.java
@@ -4,5 +4,5 @@
package naming101;
public class c {
- public static int i = 1;
+ static int i = 1;
}
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 1ad5db8..aad62cb 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -4,7 +4,6 @@
package com.android.tools.r8;
-
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.dexsplitter.SplitterTestBase.RunInterface;
import com.android.tools.r8.references.MethodReference;
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/PackagePrivateOverridePublicizerTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/PackagePrivateOverridePublicizerTest.java
index 630ff62..5a9da04 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/PackagePrivateOverridePublicizerTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/PackagePrivateOverridePublicizerTest.java
@@ -23,6 +23,7 @@
private final TestParameters parameters;
private final String[] EXPECTED = new String[] {"SubViewModel.clear()", "ViewModel.clear()"};
+ private final String[] R8_OUT = new String[] {"SubViewModel.clear()", "SubViewModel.clear()"};
@Parameters(name = "{0}")
public static TestParametersCollection data() {
@@ -50,12 +51,9 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.allowAccessModification()
- .addOptionsModification(
- options -> {
- options.enablePackagePrivateAwarePublicization = true;
- })
.run(parameters.getRuntime(), Main.class)
- .apply(this::assertSuccessOutput);
+ // TODO(b/172496438): This should be EXPECTED.
+ .assertSuccessWithOutputLines(R8_OUT);
}
private void assertSuccessOutput(TestRunResult<?> result) {
diff --git a/src/test/java/com/android/tools/r8/cf/methodhandles/InvalidBootstrapMethodHandleTestClass.java b/src/test/java/com/android/tools/r8/cf/methodhandles/InvalidBootstrapMethodHandleTestClass.java
index 9040a68..d63ead6 100644
--- a/src/test/java/com/android/tools/r8/cf/methodhandles/InvalidBootstrapMethodHandleTestClass.java
+++ b/src/test/java/com/android/tools/r8/cf/methodhandles/InvalidBootstrapMethodHandleTestClass.java
@@ -25,6 +25,7 @@
public int nonStaticField = 42;
// Virtual method to target.
+ @Override
public CallSite virtualMethod(MethodHandles.Lookup caller, String name, MethodType type)
throws Exception {
return new ConstantCallSite(caller.findStatic(caller.lookupClass(), name, type));
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
index 04bb9d7..c551a26 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
@@ -32,18 +32,19 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
+ .addKeepPackageNamesRule(Main.class.getPackage())
.addHorizontallyMergedClassesInspector(
HorizontallyMergedClassesInspector::assertNoClassesMerged)
.run(parameters.getRuntime(), Main.class)
- .assertFailureWithErrorThatMatches(
- allOf(
- containsString("java.lang.UnsatisfiedLinkError:"),
- containsString("com.android.tools.r8.classmerging.horizontal.b.a(")))
.inspectFailure(
codeInspector -> {
assertThat(codeInspector.clazz(A.class), isPresent());
assertThat(codeInspector.clazz(B.class), isPresent());
- });
+ })
+ .assertFailureWithErrorThatMatches(
+ allOf(
+ containsString("java.lang.UnsatisfiedLinkError:"),
+ containsString("com.android.tools.r8.classmerging.horizontal.b.a(")));
}
@NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java b/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
index 034f226..68e882e 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
@@ -388,7 +388,7 @@
buildWithMemberNamesRule(testForR8(parameters.getBackend())).compile());
}
- // Tests for "-keepclassmembernames" and *no* minification.
+ // Tests for "-keepclassmembernames" and minification.
private <
C extends BaseCompilerCommand,
diff --git a/src/test/java/com/android/tools/r8/compatproguard/KeepClassMemberNamesMinificationTest.java b/src/test/java/com/android/tools/r8/compatproguard/KeepClassMemberNamesMinificationTest.java
new file mode 100644
index 0000000..009cf9f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compatproguard/KeepClassMemberNamesMinificationTest.java
@@ -0,0 +1,120 @@
+// Copyright (c) 2020, 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.compatproguard;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class KeepClassMemberNamesMinificationTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public KeepClassMemberNamesMinificationTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testForRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Main.class, A.class, ReflectiveCallerOfA.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello World!", "Hello World!");
+ }
+
+ @Test
+ public void testPG() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testKeepNames(testForProguard(ProguardVersion.V7_0_0).addKeepRules("-dontwarn"));
+ }
+
+ @Test
+ public void testR8Compat() throws Exception {
+ testKeepNames(
+ testForR8Compat(parameters.getBackend())
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations());
+ }
+
+ @Test
+ public void testR8Full() throws Exception {
+ testKeepNames(
+ testForR8(parameters.getBackend())
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations());
+ }
+
+ private void testKeepNames(TestShrinkerBuilder<?, ?, ?, ?, ?> shrinkerBuilder) throws Exception {
+ shrinkerBuilder
+ .addProgramClasses(Main.class, A.class, ReflectiveCallerOfA.class)
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRulesWithAllowObfuscation(A.class)
+ .addKeepRules("-keepclassmembernames class ** { *; }")
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClass = inspector.clazz(A.class);
+ assertThat(aClass, isPresentAndRenamed());
+ assertThat(aClass.uniqueFieldWithName("foo"), isPresentAndNotRenamed());
+ assertThat(aClass.uniqueMethodWithName("foo"), isPresentAndNotRenamed());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello World!", "Hello World!");
+ }
+
+ @NeverClassInline
+ public static class A {
+
+ public String foo = "Hello World!";
+
+ public void foo() {
+ System.out.println("Hello World!");
+ }
+
+ @NeverInline
+ public void callMySelf() throws Exception {
+ ReflectiveCallerOfA.callA(this.getClass().getName());
+ }
+ }
+
+ public static class ReflectiveCallerOfA {
+
+ @NeverInline
+ public static void callA(String className) throws Exception {
+ Class<?> aClass = Class.forName(className);
+ Object o = aClass.getDeclaredConstructor().newInstance();
+ System.out.println(aClass.getDeclaredField("foo").get(o));
+ aClass.getDeclaredMethod("foo").invoke(o);
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) throws Exception {
+ new A().callMySelf();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
index ccd3cdf..0ee446d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
@@ -99,10 +99,8 @@
shrinkDesugaredLibrary)
.addRunClasspathFiles(CUSTOM_LIB)
.run(parameters.getRuntime(), Executor.class)
- .apply(
- r ->
- r.assertSuccessWithOutput(
- supportAllCallbacksFromLibrary ? EXPECTED_RESULT : FAILING_EXPECTED_RESULT));
+ .assertSuccessWithOutput(
+ supportAllCallbacksFromLibrary ? EXPECTED_RESULT : FAILING_EXPECTED_RESULT);
}
private void assertDoubleForEach(CodeInspector inspector) {
@@ -164,10 +162,8 @@
shrinkDesugaredLibrary)
.addRunClasspathFiles(CUSTOM_LIB)
.run(parameters.getRuntime(), Executor.class)
- .apply(
- r ->
- r.assertSuccessWithOutput(
- supportAllCallbacksFromLibrary ? EXPECTED_RESULT : FAILING_EXPECTED_RESULT));
+ .assertSuccessWithOutput(
+ supportAllCallbacksFromLibrary ? EXPECTED_RESULT : FAILING_EXPECTED_RESULT);
}
static class CustomLibClass {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java
index b62045b..a96a874 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
-import com.android.tools.r8.desugar.desugaredlibrary.conversiontests.MoreFunctionConversionTest.CustomLibClass;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
index f1884e9..2abe133 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
@@ -96,6 +96,7 @@
})
.addProgramFiles(classesMatching(outerNestName))
.applyIf(parameters.isCfRuntime(), Jdk9TestUtils.addJdk9LibraryFiles(temp))
+ .addKeepPackageNamesRule("nesthostexample")
.addInliningAnnotations()
.compile()
.inspect(
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java
index 073c7b1..9f41a17 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java
@@ -99,6 +99,7 @@
.enableInliningAnnotations()
.addProgramFiles(bothNestsAndOutsideClassToCompile)
.applyIf(parameters.isCfRuntime(), Jdk9TestUtils.addJdk9LibraryFiles(temp))
+ .addKeepPackageNamesRule("nesthostexample")
.compile()
.inspect(NestAttributesUpdateTest::assertNestAttributesCorrect);
for (int i = 0; i < mainClasses.length; i++) {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
index 227ce07..7cc81c3 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
@@ -86,6 +86,7 @@
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.minification(minification)
+ .addKeepPackageNamesRule(libClass.getPackage())
.setMinApi(parameters.getApiLevel())
.compile()
.writeToZip();
diff --git a/src/test/java/com/android/tools/r8/graph/invokestatic/InvokeStaticOnInterfaceTest.java b/src/test/java/com/android/tools/r8/graph/invokestatic/InvokeStaticOnInterfaceTest.java
index 4be7975..a94f02a 100644
--- a/src/test/java/com/android/tools/r8/graph/invokestatic/InvokeStaticOnInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokestatic/InvokeStaticOnInterfaceTest.java
@@ -49,8 +49,9 @@
if (parameters.getRuntime().asCf().isNewerThan(CfVm.JDK8)) {
runResult.assertFailureWithErrorThatMatches(
containsString(
- "java.lang.IncompatibleClassChangeError: Method"
- + " com.android.tools.r8.graph.invokestatic.InvokeStaticOnInterfaceTest$I.foo()V"
+ "java.lang.IncompatibleClassChangeError: Method "
+ + I.class.getTypeName()
+ + ".foo()V"
+ " must be InterfaceMethodref constant"));
} else {
runResult.assertSuccessWithOutputLines("Hello World!");
@@ -79,13 +80,14 @@
.enableInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.addOptionsModification(o -> o.testing.allowInvokeErrors = true)
+ .noMinification()
.addKeepMainRule(Main.class)
.run(parameters.getRuntime(), Main.class);
if (parameters.getRuntime().asCf().isNewerThan(CfVm.JDK8)) {
runResult.assertFailureWithErrorThatMatches(
containsString(
"java.lang.IncompatibleClassChangeError: Method"
- + " com.android.tools.r8.graph.invokestatic.a.a()V"
+ + " com.android.tools.r8.graph.invokestatic.InvokeStaticOnInterfaceTest$I.foo()V"
+ " must be InterfaceMethodref constant"));
} else {
runResult.assertSuccessWithOutputLines("Hello World!");
diff --git a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
index 2811374..6a01e83 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
@@ -86,6 +86,7 @@
return String.join(
System.lineSeparator(),
adaptResourceFilenamesRule,
+ "-keeppackagenames adaptresourcefilenames**",
"-keep class adaptresourcefilenames.TestClass {",
" public static void main(...);",
"}");
diff --git a/src/test/java/com/android/tools/r8/naming/AvoidRTest.java b/src/test/java/com/android/tools/r8/naming/AvoidRTest.java
index 8feb257..4a35018 100644
--- a/src/test/java/com/android/tools/r8/naming/AvoidRTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AvoidRTest.java
@@ -47,16 +47,16 @@
JasminBuilder jasminBuilder = new JasminBuilder();
R8FullTestBuilder builder = testForR8(parameters.getBackend());
for (int i = 0; i < 4; i++) {
- jasminBuilder.addClass("TopLevel" + Integer.toString(i));
+ jasminBuilder.addClass("TopLevel" + i);
}
for (int i = 0; i < 4; i++) {
- jasminBuilder.addClass("p1/SecondLevel" + Integer.toString(i));
+ jasminBuilder.addClass("p1/SecondLevel" + i);
}
for (int i = 0; i < 4; i++) {
- jasminBuilder.addClass("p1/p2/ThirdLevel" + Integer.toString(i));
+ jasminBuilder.addClass("p1/p2/ThirdLevel" + i);
}
for (int i = 0; i < 4; i++) {
- jasminBuilder.addClass("p2/SecondLevel" + Integer.toString(i));
+ jasminBuilder.addClass("p2/SecondLevel" + i);
}
builder.addProgramClassFileData(jasminBuilder.buildClasses());
Set<String> usedDescriptors = new HashSet<>();
@@ -84,7 +84,7 @@
JasminBuilder jasminBuilder = new JasminBuilder();
R8FullTestBuilder builder = testForR8(parameters.getBackend());
for (int i = 0; i < 26 * 2; i++) {
- jasminBuilder.addClass("TestClass" + Integer.toString(i));
+ jasminBuilder.addClass("TestClass" + i);
}
builder.addProgramClassFileData(jasminBuilder.buildClasses());
Set<String> usedNames = new HashSet<>();
diff --git a/src/test/java/com/android/tools/r8/naming/ClassObfuscationDictionaryDuplicateTest.java b/src/test/java/com/android/tools/r8/naming/ClassObfuscationDictionaryDuplicateTest.java
index e0ef67d..4683db2 100644
--- a/src/test/java/com/android/tools/r8/naming/ClassObfuscationDictionaryDuplicateTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ClassObfuscationDictionaryDuplicateTest.java
@@ -5,8 +5,9 @@
package com.android.tools.r8.naming;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static junit.framework.TestCase.assertTrue;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
@@ -16,8 +17,6 @@
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import java.io.IOException;
import java.nio.file.Path;
-import java.util.HashSet;
-import java.util.Set;
import java.util.concurrent.ExecutionException;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -51,16 +50,11 @@
public void test() throws IOException, CompilationFailedException, ExecutionException {
Path dictionary = temp.getRoot().toPath().resolve("dictionary.txt");
FileUtils.writeTextFile(dictionary, "a");
-
- Set<String> finalNames = new HashSet<>();
- finalNames.add(A.class.getPackage().getName() + ".a");
- finalNames.add(B.class.getPackage().getName() + ".b");
-
testForR8(parameters.getBackend())
.addProgramClasses(A.class, B.class, C.class)
- .noTreeShaking()
.addKeepRules("-classobfuscationdictionary " + dictionary.toString())
.addKeepMainRule(C.class)
+ .addKeepClassRulesWithAllowObfuscation(A.class, B.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), C.class)
.assertSuccessWithOutput("HELLO WORLD!")
@@ -68,11 +62,12 @@
inspector -> {
ClassSubject clazzA = inspector.clazz(A.class);
assertThat(clazzA, isPresent());
- assertTrue(finalNames.contains(clazzA.getFinalName()));
- finalNames.remove(clazzA.getFinalName());
ClassSubject clazzB = inspector.clazz(B.class);
assertThat(clazzB, isPresent());
- assertTrue(finalNames.contains(clazzB.getFinalName()));
+ assertEquals(
+ clazzA.getDexProgramClass().type.getPackageName(),
+ clazzB.getDexProgramClass().type.getPackageName());
+ assertNotEquals(clazzA.getFinalName(), clazzB.getFinalName());
});
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/DontUseMixedCaseClassNamesExistingClassTest.java b/src/test/java/com/android/tools/r8/naming/DontUseMixedCaseClassNamesExistingClassTest.java
index 6804b54..bb7e06d 100644
--- a/src/test/java/com/android/tools/r8/naming/DontUseMixedCaseClassNamesExistingClassTest.java
+++ b/src/test/java/com/android/tools/r8/naming/DontUseMixedCaseClassNamesExistingClassTest.java
@@ -50,6 +50,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepClassRulesWithAllowObfuscation(A.class)
.addKeepMainRule(Main.class)
+ .addKeepPackageNamesRule(Main.class.getPackage())
.addKeepRules("-classobfuscationdictionary " + dictionary.toString())
.applyIf(dontUseMixedCase, b -> b.addKeepRules("-dontusemixedcaseclassnames"))
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/naming/KeepPackageNamesTest.java b/src/test/java/com/android/tools/r8/naming/KeepPackageNamesTest.java
index f72abf5..bb73c3f 100644
--- a/src/test/java/com/android/tools/r8/naming/KeepPackageNamesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/KeepPackageNamesTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import com.android.tools.r8.ProguardVersion;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.keeppackagenames.Top;
@@ -47,6 +48,7 @@
assertEquals(
getPackageNameFromDescriptor(top.getOriginalDescriptor()),
getPackageNameFromDescriptor(top.getFinalDescriptor()));
+ assertEquals(PACKAGE_NAME, getPackageNameFromDescriptor(top.getFinalDescriptor()));
ClassSubject sub = inspector.clazz(SubClass.class);
assertThat(sub, isPresentAndRenamed());
@@ -55,13 +57,13 @@
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()));
+ assertThat(
+ getPackageNameFromDescriptor(sub.getFinalDescriptor()), containsString(PACKAGE_NAME));
break;
}
}
@@ -80,7 +82,7 @@
@Test
public void testProguard() throws Exception {
- testForProguard()
+ testForProguard(ProguardVersion.V6_0_1)
.addProgramClasses(CLASSES)
.addKeepAllClassesRuleWithAllowObfuscation()
.addKeepRules(config.getKeepRule())
diff --git a/src/test/java/com/android/tools/r8/naming/MinificationMixedCaseAndNumbersTest.java b/src/test/java/com/android/tools/r8/naming/MinificationMixedCaseAndNumbersTest.java
index f27f20f..5c050a6 100644
--- a/src/test/java/com/android/tools/r8/naming/MinificationMixedCaseAndNumbersTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MinificationMixedCaseAndNumbersTest.java
@@ -34,7 +34,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public MinificationMixedCaseAndNumbersTest(TestParameters parameters) {
@@ -55,8 +55,9 @@
.addInnerClasses(MinificationMixedCaseAndNumbersTest.class)
.addKeepMainRule(Main.class)
.noTreeShaking()
- .addKeepRules("-dontusemixedcaseclassnames")
- .setMinApi(parameters.getRuntime())
+ .addKeepRules(
+ "-dontusemixedcaseclassnames", "-keeppackagenames com.android.tools.r8.naming")
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
.inspect(
inspector -> {
diff --git a/src/test/java/com/android/tools/r8/naming/MinifyPackageWithKeepToPackagePrivateMemberTest.java b/src/test/java/com/android/tools/r8/naming/MinifyPackageWithKeepToPackagePrivateMemberTest.java
new file mode 100644
index 0000000..8064520
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/MinifyPackageWithKeepToPackagePrivateMemberTest.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2020, 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 com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MinifyPackageWithKeepToPackagePrivateMemberTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public MinifyPackageWithKeepToPackagePrivateMemberTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClassFileData(
+ transformer(A.class).removeInnerClasses().transform(),
+ transformer(ReflectiveCallerOfA.class).removeInnerClasses().transform(),
+ transformer(Main.class).removeInnerClasses().transform())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello World!", "Hello World!");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(
+ transformer(A.class).removeInnerClasses().transform(),
+ transformer(ReflectiveCallerOfA.class).removeInnerClasses().transform(),
+ transformer(Main.class).removeInnerClasses().transform())
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRulesWithAllowObfuscation(A.class)
+ .addKeepRules("-keepclassmembernames class ** { *; }")
+ .addKeepRules(
+ "-identifiernamestring class "
+ + ReflectiveCallerOfA.class.getTypeName()
+ + " { java.lang.String className; }")
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello World!", "Hello World!");
+ }
+
+ static class A {
+
+ A() {}
+
+ String foo = "Hello World!";
+
+ void foo() {
+ System.out.println("Hello World!");
+ }
+ }
+
+ public static class ReflectiveCallerOfA {
+
+ private static String className =
+ "com.android.tools.r8.naming.MinifyPackageWithKeepToPackagePrivateMemberTest$A";
+
+ @NeverInline
+ public static void callA() throws Exception {
+ Class<?> aClass = Class.forName(className);
+ Object o = aClass.getDeclaredConstructor().newInstance();
+ System.out.println(aClass.getDeclaredField("foo").get(o));
+ aClass.getDeclaredMethod("foo").invoke(o);
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) throws Exception {
+ ReflectiveCallerOfA.callA();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/MinifyPackageWithKeepToPublicMemberTest.java b/src/test/java/com/android/tools/r8/naming/MinifyPackageWithKeepToPublicMemberTest.java
new file mode 100644
index 0000000..240506d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/MinifyPackageWithKeepToPublicMemberTest.java
@@ -0,0 +1,129 @@
+// Copyright (c) 2020, 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.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MinifyPackageWithKeepToPublicMemberTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final boolean minify;
+ private final String[] EXPECTED = new String[] {"Hello World!", "Hello World!"};
+
+ @Parameters(name = "{0}, minifyA: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ }
+
+ public MinifyPackageWithKeepToPublicMemberTest(TestParameters parameters, boolean minify) {
+ this.parameters = parameters;
+ this.minify = minify;
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClassFileData(
+ transformer(A.class).removeInnerClasses().transform(),
+ transformer(ReflectiveCallerOfA.class).removeInnerClasses().transform(),
+ transformer(Main.class).removeInnerClasses().transform())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(
+ transformer(A.class).removeInnerClasses().transform(),
+ transformer(ReflectiveCallerOfA.class).removeInnerClasses().transform(),
+ transformer(Main.class).removeInnerClasses().transform())
+ .addKeepMainRule(Main.class)
+ .applyIf(
+ minify,
+ builder -> builder.addKeepClassAndMembersRulesWithAllowObfuscation(A.class),
+ builder -> builder.addKeepClassAndMembersRules(A.class))
+ .addKeepRules("-keepclassmembernames class ** { *; }")
+ .addKeepRules(
+ "-identifiernamestring class "
+ + ReflectiveCallerOfA.class.getTypeName()
+ + " { java.lang.String className; }")
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClazz = inspector.clazz(A.class);
+ assertThat(aClazz, isPresentAndRenamed(minify));
+ ClassSubject reflectiveCallerOfAClass = inspector.clazz(ReflectiveCallerOfA.class);
+ assertThat(reflectiveCallerOfAClass, isPresentAndRenamed());
+ ClassSubject mainClass = inspector.clazz(Main.class);
+ assertThat(mainClass, isPresentAndNotRenamed());
+ assertNotEquals(
+ mainClass.getDexProgramClass().type.getPackageDescriptor(),
+ reflectiveCallerOfAClass.getDexProgramClass().type.getPackageDescriptor());
+ if (minify) {
+ assertEquals(
+ aClazz.getDexProgramClass().type.getPackageDescriptor(),
+ reflectiveCallerOfAClass.getDexProgramClass().type.getPackageDescriptor());
+ } else {
+ assertNotEquals(
+ aClazz.getDexProgramClass().type.getPackageDescriptor(),
+ reflectiveCallerOfAClass.getDexProgramClass().type.getPackageDescriptor());
+ }
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ public static class A {
+
+ public A() {}
+
+ public String foo = "Hello World!";
+
+ public void foo() {
+ System.out.println("Hello World!");
+ }
+ }
+
+ public static class ReflectiveCallerOfA {
+
+ private static String className =
+ "com.android.tools.r8.naming.MinifyPackageWithKeepToPublicMemberTest$A";
+
+ @NeverInline
+ public static void callA() throws Exception {
+ Class<?> aClass = Class.forName(className);
+ Object o = aClass.getDeclaredConstructor().newInstance();
+ System.out.println(aClass.getDeclaredField("foo").get(o));
+ aClass.getDeclaredMethod("foo").invoke(o);
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) throws Exception {
+ ReflectiveCallerOfA.callA();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
index 66ec3bb..9079f61 100644
--- a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
@@ -238,9 +238,9 @@
b.getDexProgramClass().getType().getPackageName());
ClassSubject sub_a = inspector.clazz("naming044.sub.SubA");
- assertEquals(2, countPackageDepth(sub_a));
+ assertEquals(1, countPackageDepth(sub_a));
ClassSubject sub_b = inspector.clazz("naming044.sub.SubB");
- assertEquals(2, countPackageDepth(sub_b));
+ assertEquals(1, countPackageDepth(sub_b));
assertEquals(
sub_a.getDexProgramClass().getType().getPackageName(),
sub_b.getDexProgramClass().getType().getPackageName());
@@ -327,9 +327,9 @@
// All packages are renamed somehow. Need to check package hierarchy is consistent.
ClassSubject aa = inspector.clazz("naming101.a.a");
- assertEquals(2, countPackageDepth(aa));
+ assertEquals(1, countPackageDepth(aa));
ClassSubject abc = inspector.clazz("naming101.a.b.c");
- assertEquals(3, countPackageDepth(abc));
+ assertEquals(1, countPackageDepth(abc));
// naming101.a/a; -> La/a/a; --prefix--> La/a
// naming101.a/b/c; -> La/a/a/a; --prefix--> La/a/a --prefix--> La/a
@@ -356,14 +356,20 @@
ba.getDexProgramClass().getType().getPackageName(),
bb.getDexProgramClass().getType().getPackageName());
- // All other classes can be repackaged to naming101.a, but naming101.a.a exists to make a name
- // conflict. Thus, those should not be renamed to 'a'.
- List<String> klasses = ImmutableList.of("naming101.c", "naming101.d", "naming101.a.b.c");
+ // We cannot repackage c or d since these have package-private members and a
+ // keep,allowobfuscation. For us to be able to repackage them, we have to use
+ // -allowaccesmodification.
+ List<String> klasses = ImmutableList.of("naming101.c", "naming101.d");
for (String klass : klasses) {
ClassSubject k = inspector.clazz(klass);
- assertEquals("naming101.a", k.getDexProgramClass().getType().getPackageName());
- assertNotEquals("naming101.a.a", k.getFinalName());
+ assertNotEquals("naming101.a", k.getDexProgramClass().getType().getPackageName());
}
+
+ // All other classes can be repackaged to naming101.a, but naming101.a.a exists to make a name
+ // conflict. Thus, those should not be renamed to 'a'.
+ ClassSubject namingAbc = inspector.clazz("naming101.a.b.c");
+ assertEquals("naming101.a", namingAbc.getDexProgramClass().getType().getPackageName());
+ assertNotEquals("naming101.a.a", namingAbc.getFinalName());
}
private static void test101_rule104(CodeInspector inspector) {
@@ -411,11 +417,12 @@
ClassSubject topD = inspector.clazz("naming101.d");
assertEquals("naming101", topD.getDexProgramClass().getType().getPackageName());
- // Due to the keep rule for naming101.c, package prefix naming101 is reserved.
- // That is, every renamed class should have naming101 as parent package prefix.
- for (String klass : klasses) {
- ClassSubject t = inspector.clazz(klass);
- assertTrue(t.getFinalName().startsWith("naming101."));
- }
+ // The remaining classes are in subpackages of naming101 and will therefore not have
+ // package-private access to namin101.c
+ ClassSubject subAC = inspector.clazz("naming101.a.c");
+ assertEquals(1, countPackageDepth(subAC));
+
+ ClassSubject subABC = inspector.clazz("naming101.a.b.c");
+ assertEquals(1, countPackageDepth(subABC));
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/PackageObfuscationDictionaryDuplicateTest.java b/src/test/java/com/android/tools/r8/naming/PackageObfuscationDictionaryDuplicateTest.java
index 133e607..0ac480f 100644
--- a/src/test/java/com/android/tools/r8/naming/PackageObfuscationDictionaryDuplicateTest.java
+++ b/src/test/java/com/android/tools/r8/naming/PackageObfuscationDictionaryDuplicateTest.java
@@ -57,9 +57,9 @@
.inspect(
inspector -> {
ClassSubject clazzTop = inspector.clazz(Top.class);
- assertTrue(clazzTop.getFinalName().endsWith(".a.a"));
+ assertTrue(clazzTop.getFinalName().endsWith("a.a"));
ClassSubject clazzA = inspector.clazz(A.class);
- assertTrue(clazzA.getFinalName().endsWith(".b.a"));
+ assertTrue(clazzA.getFinalName().endsWith("b.a"));
});
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/PackagePrivateAllowAccessModificationPackageTest.java b/src/test/java/com/android/tools/r8/naming/PackagePrivateAllowAccessModificationPackageTest.java
index 473df64..4be6559 100644
--- a/src/test/java/com/android/tools/r8/naming/PackagePrivateAllowAccessModificationPackageTest.java
+++ b/src/test/java/com/android/tools/r8/naming/PackagePrivateAllowAccessModificationPackageTest.java
@@ -53,10 +53,6 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
- .addOptionsModification(
- options -> {
- options.enablePackagePrivateAwarePublicization = true;
- })
.compile()
.inspect(
inspector -> {
@@ -64,8 +60,7 @@
assertThat(clazz, isPresentAndRenamed());
})
.run(parameters.getRuntime(), Main.class)
- // TODO(b/172496438): This should not fail.
- .assertFailureWithErrorThatThrows(IllegalAccessError.class);
+ .assertSuccessWithOutputLines(EXPECTED);
}
@NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/naming/b155249069/DontUseMixedCaseClassNamesExistingClassPackageTest.java b/src/test/java/com/android/tools/r8/naming/b155249069/DontUseMixedCaseClassNamesExistingClassPackageTest.java
index f662aa6..696704f 100644
--- a/src/test/java/com/android/tools/r8/naming/b155249069/DontUseMixedCaseClassNamesExistingClassPackageTest.java
+++ b/src/test/java/com/android/tools/r8/naming/b155249069/DontUseMixedCaseClassNamesExistingClassPackageTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.naming.b155249069.package_b.A;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import java.io.IOException;
@@ -29,6 +30,8 @@
private final TestParameters parameters;
private final boolean dontUseMixedCase;
+ private final String renamedATypeName = "Testpackage.A";
+
@Parameters(name = "{0}, dontusemixedcaseclassnames: {1}")
public static List<Object[]> data() {
return buildParameters(
@@ -45,12 +48,22 @@
public void testR8() throws ExecutionException, CompilationFailedException, IOException {
Path packageDictionary = temp.getRoot().toPath().resolve("packagedictionary.txt");
// Suggest the name 'a' for the package, to force a collision with A.A.
- FileUtils.writeTextFile(packageDictionary, "a");
+ FileUtils.writeTextFile(packageDictionary, "testpackage");
testForR8(parameters.getBackend())
- .addProgramClasses(Main.class, com.android.tools.r8.naming.b155249069.A.A.class, A.class)
+ .addProgramClasses(A.class)
+ .addProgramClassFileData(
+ transformer(Main.class)
+ .replaceClassDescriptorInMethodInstructions(
+ DescriptorUtils.javaTypeToDescriptor(
+ com.android.tools.r8.naming.b155249069.A.A.class.getTypeName()),
+ DescriptorUtils.javaTypeToDescriptor(renamedATypeName))
+ .transform(),
+ transformer(com.android.tools.r8.naming.b155249069.A.A.class)
+ .setClassDescriptor(DescriptorUtils.javaTypeToDescriptor(renamedATypeName))
+ .transform())
.setMinApi(parameters.getApiLevel())
- // Keep A.A such that the package A is kept.
- .addKeepClassRules(com.android.tools.r8.naming.b155249069.A.A.class)
+ // Keep testpackage.A such that the package A is kept.
+ .addKeepClassRules(renamedATypeName)
.addKeepClassRulesWithAllowObfuscation(A.class)
.addKeepMainRule(Main.class)
.addKeepRules("-packageobfuscationdictionary " + packageDictionary.toString())
@@ -59,8 +72,7 @@
.assertSuccessWithOutputLines("A.A.foo()", "package_b.B.foo()")
.inspect(
inspector -> {
- ClassSubject aSubject =
- inspector.clazz(com.android.tools.r8.naming.b155249069.A.A.class);
+ ClassSubject aSubject = inspector.clazz(renamedATypeName);
ClassSubject bSubject = inspector.clazz(A.class);
if (dontUseMixedCase) {
assertNotEquals(
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java
index 63201fa..df0bfa6 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java
@@ -4,12 +4,14 @@
package com.android.tools.r8.repackage;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -50,12 +52,16 @@
.inspect(
inspector -> {
// Find the generated lambda class
- assertThat(
- ClassWithLambda.class,
- isRepackagedIf(inspector, repackage && parameters.isDexRuntime()));
if (!parameters.isDexRuntime()) {
+ assertThat(ClassWithLambda.class, isNotRepackaged(inspector));
return;
}
+ if (repackage) {
+ assertThat(ClassWithLambda.class, isRepackaged(inspector));
+ } else {
+ ClassSubject classWithLambdaSubject = inspector.clazz(ClassWithLambda.class);
+ assertThat(classWithLambdaSubject, isPresentAndRenamed());
+ }
inspector.forAllClasses(
clazz -> {
if (clazz.isSynthesizedJavaLambdaClass()) {
diff --git a/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java b/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
index 617676f..c5c0859 100644
--- a/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
@@ -24,7 +24,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
}
public MissingInterfaceTest(TestParameters parameters) {
@@ -39,6 +39,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(TestClassForB112849320.class)
.addOptionsModification(options -> options.enableInlining = false)
+ .addKeepPackageNamesRule(GoingToBeMissed.class.getPackage())
.compile()
.addRunClasspathFiles(
buildOnDexRuntime(