Apply package renamings to resource filenames
If the package "foo.bar.baz" is renamed to "a.a.a", then all resources in the directory foo/bar/baz/ will be moved to a/a/a/ in the output.
This CL extends NamingLens with a method lookupPackageName and applies the package renamings to the resource directories.
Change-Id: I9c604ef347dbd0666b4ea486a4668e1ba6523bfd
diff --git a/src/main/java/com/android/tools/r8/dex/ResourceAdapter.java b/src/main/java/com/android/tools/r8/dex/ResourceAdapter.java
index e1af350..673fa6e 100644
--- a/src/main/java/com/android/tools/r8/dex/ResourceAdapter.java
+++ b/src/main/java/com/android/tools/r8/dex/ResourceAdapter.java
@@ -190,6 +190,11 @@
if (currentChar == getClassNameSeparator()
&& !eof(position + 1)
&& Character.isJavaIdentifierPart(contents.charAt(position + 1))) {
+ if (allowRenamingOfPrefixes()
+ && shouldRecordPrefix(currentChar)
+ && isRenamingCandidate(start, position)) {
+ prefixEndPositionsExclusive.push(position);
+ }
// Consume the dot and the Java identifier part that follows the dot.
position += 2;
continue;
@@ -205,7 +210,7 @@
while (!prefixEndPositionsExclusive.isEmpty() && !renamingSucceeded) {
int prefixEndExclusive = prefixEndPositionsExclusive.popInt();
assert isRenamingCandidate(start, prefixEndExclusive);
- renamingSucceeded = renameJavaTypeInRange(start, prefixEndExclusive);
+ renamingSucceeded = handlePrefix(start, prefixEndExclusive);
}
}
@@ -217,7 +222,7 @@
}
// Returns true if the Java type in the range [from; toExclusive[ was renamed.
- private boolean renameJavaTypeInRange(int from, int toExclusive) {
+ protected boolean renameJavaTypeInRange(int from, int toExclusive) {
String javaType = contents.substring(from, toExclusive);
if (getClassNameSeparator() != '.') {
javaType = javaType.replace(getClassNameSeparator(), '.');
@@ -246,12 +251,34 @@
return false;
}
+ // Returns true if the Java package in the range [from; toExclusive[ was renamed.
+ protected boolean renameJavaPackageInRange(int from, int toExclusive) {
+ String javaPackage = contents.substring(from, toExclusive);
+ if (getClassNameSeparator() != '/') {
+ javaPackage = javaPackage.replace(getClassNameSeparator(), '/');
+ }
+ String minifiedJavaPackage = namingLense.lookupPackageName(javaPackage);
+ if (!javaPackage.equals(minifiedJavaPackage)) {
+ outputRangeFromInput(outputFrom, from);
+ outputJavaType(
+ getClassNameSeparator() != '/'
+ ? minifiedJavaPackage.replace('/', getClassNameSeparator())
+ : minifiedJavaPackage);
+ outputFrom = toExclusive;
+ changed = true;
+ return true;
+ }
+ return false;
+ }
+
protected abstract char getClassNameSeparator();
protected abstract boolean allowRenamingOfPrefixes();
protected abstract boolean shouldRecordPrefix(char c);
+ protected abstract boolean handlePrefix(int from, int toExclusive);
+
protected abstract boolean isRenamingCandidate(int from, int toExclusive);
private void outputRangeFromInput(int from, int toExclusive) {
@@ -295,6 +322,11 @@
}
@Override
+ protected boolean handlePrefix(int from, int toExclusive) {
+ throw new Unreachable();
+ }
+
+ @Override
public boolean isRenamingCandidate(int from, int toExclusive) {
// If the Java type starts with '-' or '.', it should not be renamed.
return (from <= 0 || !isDashOrDot(contents.charAt(from - 1)))
@@ -328,6 +360,15 @@
}
@Override
+ protected boolean handlePrefix(int from, int toExclusive) {
+ assert !eof(toExclusive);
+ if (contents.charAt(toExclusive) == '/') {
+ return renameJavaPackageInRange(from, toExclusive);
+ }
+ return renameJavaTypeInRange(from, toExclusive);
+ }
+
+ @Override
public boolean isRenamingCandidate(int from, int toExclusive) {
return from == 0 && !eof(toExclusive);
}
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 01fff01..bf5dd5d 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -31,6 +31,7 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.lang.reflect.GenericSignatureFormatError;
@@ -38,6 +39,7 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -92,7 +94,18 @@
states.computeIfAbsent("", k -> topLevelState);
}
- Map<DexType, DexString> computeRenaming(Timing timing) {
+ static class ClassRenaming {
+ protected final Map<String, String> packageRenaming;
+ protected final Map<DexType, DexString> classRenaming;
+
+ private ClassRenaming(
+ Map<DexType, DexString> classRenaming, Map<String, String> packageRenaming) {
+ this.classRenaming = classRenaming;
+ this.packageRenaming = packageRenaming;
+ }
+ }
+
+ ClassRenaming computeRenaming(Timing timing) {
// Use deterministic class order to make sure renaming is deterministic.
Iterable<DexProgramClass> classes = appInfo.classesWithDeterministicOrder();
// Collect names we have to keep.
@@ -128,7 +141,19 @@
appInfo.dexItemFactory.forAllTypes(this::renameArrayTypeIfNeeded);
timing.end();
- return Collections.unmodifiableMap(renaming);
+ return new ClassRenaming(Collections.unmodifiableMap(renaming), getPackageRenaming());
+ }
+
+ private Map<String, String> getPackageRenaming() {
+ ImmutableMap.Builder<String, String> packageRenaming = ImmutableMap.builder();
+ for (Entry<String, Namespace> entry : states.entrySet()) {
+ String originalPackageName = entry.getKey();
+ String minifiedPackageName = entry.getValue().getPackageName();
+ if (!minifiedPackageName.equals(originalPackageName)) {
+ packageRenaming.put(originalPackageName, minifiedPackageName);
+ }
+ }
+ return packageRenaming.build();
}
private void renameDanglingTypes(DexClass clazz) {
@@ -406,6 +431,7 @@
private class Namespace {
+ private final String packageName;
private final char[] packagePrefix;
private int typeCounter = 1;
private int packageCounter = 1;
@@ -417,6 +443,7 @@
}
Namespace(String packageName, String separator) {
+ this.packageName = packageName;
this.packagePrefix = ("L" + packageName
// L or La/b/ (or La/b/C$)
+ (packageName.isEmpty() ? "" : separator))
@@ -425,6 +452,10 @@
this.classDictionaryIterator = classDictionary.iterator();
}
+ public String getPackageName() {
+ return packageName;
+ }
+
private String nextSuggestedNameForClass() {
StringBuilder nextName = new StringBuilder();
if (classDictionaryIterator.hasNext()) {
@@ -466,7 +497,6 @@
usedPackagePrefixes.add(candidate);
return candidate;
}
-
}
private class GenericSignatureRewriter implements GenericSignatureAction<DexType> {
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 382ac92..10aa8af 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
import com.android.tools.r8.naming.MethodNameMinifier.MethodRenaming;
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
@@ -42,8 +43,8 @@
public NamingLens run(Timing timing) {
assert options.enableMinification;
timing.begin("MinifyClasses");
- Map<DexType, DexString> classRenaming =
- new ClassNameMinifier(appInfo, rootSet, options).computeRenaming(timing);
+ ClassNameMinifier classNameMinifier = new ClassNameMinifier(appInfo, rootSet, options);
+ ClassRenaming classRenaming = classNameMinifier.computeRenaming(timing);
timing.end();
timing.begin("MinifyMethods");
MethodRenaming methodRenaming =
@@ -53,7 +54,12 @@
Map<DexField, DexString> fieldRenaming =
new FieldNameMinifier(appInfo, rootSet, options).computeRenaming(timing);
timing.end();
- NamingLens lens = new MinifiedRenaming(classRenaming, methodRenaming, fieldRenaming, appInfo);
+ NamingLens lens =
+ new MinifiedRenaming(
+ classRenaming,
+ methodRenaming,
+ fieldRenaming,
+ appInfo);
timing.begin("MinifyIdentifiers");
new IdentifierMinifier(appInfo, options.proguardConfiguration.getAdaptClassStrings(), lens)
.run();
@@ -64,21 +70,28 @@
private static class MinifiedRenaming extends NamingLens {
private final AppInfo appInfo;
+ private final Map<String, String> packageRenaming;
private final Map<DexItem, DexString> renaming = new IdentityHashMap<>();
private MinifiedRenaming(
- Map<DexType, DexString> classRenaming,
+ ClassRenaming classRenaming,
MethodRenaming methodRenaming,
Map<DexField, DexString> fieldRenaming,
AppInfo appInfo) {
this.appInfo = appInfo;
- renaming.putAll(classRenaming);
+ this.packageRenaming = classRenaming.packageRenaming;
+ renaming.putAll(classRenaming.classRenaming);
renaming.putAll(methodRenaming.renaming);
renaming.putAll(methodRenaming.callSiteRenaming);
renaming.putAll(fieldRenaming);
}
@Override
+ public String lookupPackageName(String packageName) {
+ return packageRenaming.getOrDefault(packageName, packageName);
+ }
+
+ @Override
public DexString lookupDescriptor(DexType type) {
return renaming.getOrDefault(type, type.descriptor);
}
diff --git a/src/main/java/com/android/tools/r8/naming/NamingLens.java b/src/main/java/com/android/tools/r8/naming/NamingLens.java
index f1f13b3..47459c0 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingLens.java
@@ -30,6 +30,8 @@
*/
public abstract class NamingLens {
+ public abstract String lookupPackageName(String packageName);
+
public abstract DexString lookupDescriptor(DexType type);
public abstract String lookupSimpleName(DexType inner, DexString innerName);
@@ -99,6 +101,11 @@
}
@Override
+ public String lookupPackageName(String packageName) {
+ return packageName;
+ }
+
+ @Override
void forAllRenamedTypes(Consumer<DexType> consumer) {
// Intentionally left empty.
}
diff --git a/src/test/examples/adaptresourcefilenames/TestClass.java b/src/test/examples/adaptresourcefilenames/TestClass.java
index 14aa7f8..a5e96e5 100644
--- a/src/test/examples/adaptresourcefilenames/TestClass.java
+++ b/src/test/examples/adaptresourcefilenames/TestClass.java
@@ -4,10 +4,14 @@
package adaptresourcefilenames;
+import adaptresourcefilenames.pkg.C;
+import adaptresourcefilenames.pkg.innerpkg.D;
+
public class TestClass {
public static void main(String[] args) {
- B obj = new B();
- obj.method();
+ new B().method();
+ new C().method();
+ new D().method();
}
}
diff --git a/src/test/examples/adaptresourcefilenames/pkg/C.java b/src/test/examples/adaptresourcefilenames/pkg/C.java
new file mode 100644
index 0000000..93d4446
--- /dev/null
+++ b/src/test/examples/adaptresourcefilenames/pkg/C.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2018, 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 adaptresourcefilenames.pkg;
+
+public class C {
+
+ public void method() {
+ System.out.println("In C.method()");
+ }
+}
diff --git a/src/test/examples/adaptresourcefilenames/pkg/innerpkg/D.java b/src/test/examples/adaptresourcefilenames/pkg/innerpkg/D.java
new file mode 100644
index 0000000..2107547
--- /dev/null
+++ b/src/test/examples/adaptresourcefilenames/pkg/innerpkg/D.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2018, 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 adaptresourcefilenames.pkg.innerpkg;
+
+public class D {
+
+ public void method() {
+ System.out.println("In D.method()");
+ }
+}
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 33a297b..dbe6541 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
@@ -368,7 +368,27 @@
"adaptresourcefilenames/prefixB.txt",
// Filename with numeric prefix and extension.
"adaptresourcefilenames/42TestClass.txt",
- "adaptresourcefilenames/42B.txt");
+ "adaptresourcefilenames/42B.txt",
+ //
+ // PACKAGE RENAMING TESTS:
+ //
+ // Filename that matches a type, but only the directory should be renamed.
+ "adaptresourcefilenames/pkg/C",
+ // Filename that matches a type that should be renamed.
+ "adaptresourcefilenames/pkg/C.txt",
+ // Filename that does not match a type, but where the directory should be renamed.
+ "adaptresourcefilenames/pkg/file.txt",
+ // Filename that does not match a type, but where a directory-prefix should be renamed.
+ "adaptresourcefilenames/pkg/directory/file.txt",
+ // Filename that matches a type, but only the directory should be renamed.
+ "adaptresourcefilenames/pkg/innerpkg/D",
+ // Filename that matches a type that should be renamed.
+ "adaptresourcefilenames/pkg/innerpkg/D.txt",
+ // Filename that does not match a type, but where the directory should be renamed.
+ "adaptresourcefilenames/pkg/innerpkg/file.txt",
+ // Filename that does not match a type, but where a directory-prefix should be renamed.
+ "adaptresourcefilenames/pkg/innerpkg/directory/file.txt"
+ );
return filenames
.stream()
.map(filename -> DataEntryResource.fromBytes(new byte[0], filename, Origin.unknown()))
@@ -424,6 +444,17 @@
typeName = "adaptresourcefilenames.B";
suffix = " suffix.txt";
break;
+ //
+ // PACKAGE RENAMING TESTS
+ //
+ case "adaptresourcefilenames/pkg/C.txt":
+ typeName = "adaptresourcefilenames.pkg.C";
+ suffix = ".txt";
+ break;
+ case "adaptresourcefilenames/pkg/innerpkg/D.txt":
+ typeName = "adaptresourcefilenames.pkg.innerpkg.D";
+ suffix = ".txt";
+ break;
}
if (typeName != null) {
String renamedName = mapper.getObfuscatedToOriginalMapping().inverse().get(typeName);
@@ -431,6 +462,44 @@
assertNotEquals(typeName, renamedName);
return renamedName.replace('.', '/') + suffix;
}
+ // Renamings for files in directories that match packages that have been renamed,
+ // but where the filename itself should not be renamed.
+ String samePackageAsType = null;
+ switch (filename) {
+ case "adaptresourcefilenames/pkg/C":
+ samePackageAsType = "adaptresourcefilenames.pkg.C";
+ suffix = "C";
+ break;
+ case "adaptresourcefilenames/pkg/file.txt":
+ samePackageAsType = "adaptresourcefilenames.pkg.C";
+ suffix = "file.txt";
+ break;
+ case "adaptresourcefilenames/pkg/directory/file.txt":
+ samePackageAsType = "adaptresourcefilenames.pkg.C";
+ suffix = "directory/file.txt";
+ break;
+ case "adaptresourcefilenames/pkg/innerpkg/D":
+ samePackageAsType = "adaptresourcefilenames.pkg.innerpkg.D";
+ suffix = "D";
+ break;
+ case "adaptresourcefilenames/pkg/innerpkg/file.txt":
+ samePackageAsType = "adaptresourcefilenames.pkg.innerpkg.D";
+ suffix = "file.txt";
+ break;
+ case "adaptresourcefilenames/pkg/innerpkg/directory/file.txt":
+ samePackageAsType = "adaptresourcefilenames.pkg.innerpkg.D";
+ suffix = "directory/file.txt";
+ break;
+ }
+ if (samePackageAsType != null) {
+ String renamedName = mapper.getObfuscatedToOriginalMapping().inverse().get(samePackageAsType);
+ assertNotNull(renamedName);
+ assertNotEquals(samePackageAsType, renamedName);
+ if (renamedName.contains(".")) {
+ String renamedPackageName = renamedName.substring(0, renamedName.lastIndexOf('.'));
+ return renamedPackageName.replace('.', '/') + "/" + suffix;
+ }
+ }
return filename;
}
}