Add tests for (package-)naming.
These tests and keep-rules came from:
http://cs/android/toolchain/jack/jack-tests/tests/com/android/jack/shrob/
with the same numberings for tracking purpose.
Bug: 37764746
Change-Id: I4909cfa38d1e55980eb203622d0bd298cb68716d
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 3feae5f..6685da7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -87,6 +87,7 @@
public final DexString assertionsDisabled = createString("$assertionsDisabled");
public final DexString stringDescriptor = createString("Ljava/lang/String;");
+ public final DexString stringArrayDescriptor = createString("[Ljava/lang/String;");
public final DexString objectDescriptor = createString("Ljava/lang/Object;");
public final DexString classDescriptor = createString("Ljava/lang/Class;");
public final DexString enumDescriptor = createString("Ljava/lang/Enum;");
@@ -124,6 +125,7 @@
public final DexType boxedNumberType = createType(boxedNumberDescriptor);
public final DexType stringType = createType(stringDescriptor);
+ public final DexType stringArrayType = createType(stringArrayDescriptor);
public final DexType objectType = createType(objectDescriptor);
public final DexType enumType = createType(enumDescriptor);
public final DexType annotationType = createType(annotationDescriptor);
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 d8d3b8d..29b791d 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -229,7 +229,8 @@
}
ClassNamingState(String packageName, String separator) {
- this.packagePrefix = ("L" + packageName + (packageName.isEmpty() ? "" : separator))
+ this.packagePrefix = ("L" + DescriptorUtils.getPackageBinaryNameFromJavaType(packageName)
+ + (packageName.isEmpty() ? "" : separator))
.toCharArray();
this.dictionaryIterator = dictionary.iterator();
}
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 025002d..7a4f8f6 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -131,5 +131,24 @@
|| virtualTargetHolder != null && virtualTargetHolder.isLibraryClass()
|| staticTargetHolder != null && staticTargetHolder.isLibraryClass();
}
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ renaming.forEach((item, str) -> {
+ if (item instanceof DexType) {
+ builder.append("[c] ");
+ } else if (item instanceof DexMethod) {
+ builder.append("[m] ");
+ } else if (item instanceof DexField) {
+ builder.append("[f] ");
+ }
+ builder.append(item.toSourceString());
+ builder.append(" -> ");
+ builder.append(str.toSourceString());
+ builder.append('\n');
+ });
+ return builder.toString();
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index 277ee0f..3e7c8dd 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -148,6 +148,15 @@
return classDescriptor.substring(1, classDescriptor.length() - 1);
}
+ /**
+ * Convert package name to a binary name.
+ *
+ * @param packageName a package name i.e., "java.lang"
+ * @return java pacakge name in a binary name format, i.e., java/lang
+ */
+ public static String getPackageBinaryNameFromJavaType(String packageName) {
+ return packageName.replace(JAVA_PACKAGE_SEPARATOR, DESCRIPTOR_PACKAGE_SEPARATOR);
+ }
/**
* Convert a class binary name to a descriptor.
diff --git a/src/test/examples/naming001/A.java b/src/test/examples/naming001/A.java
new file mode 100644
index 0000000..092bcf7
--- /dev/null
+++ b/src/test/examples/naming001/A.java
@@ -0,0 +1,25 @@
+// 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 naming001;
+
+public class A {
+
+ A() {
+ }
+
+ A(int i) {
+ }
+
+ static {
+ C.m();
+ }
+
+ void m() {
+ }
+
+ @SuppressWarnings("unused")
+ private void privateFunc() {
+ }
+}
+
diff --git a/src/test/examples/naming001/B.java b/src/test/examples/naming001/B.java
new file mode 100644
index 0000000..30e520c
--- /dev/null
+++ b/src/test/examples/naming001/B.java
@@ -0,0 +1,8 @@
+// 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 naming001;
+
+public class B {
+}
+
diff --git a/src/test/examples/naming001/C.java b/src/test/examples/naming001/C.java
new file mode 100644
index 0000000..24cd8ec
--- /dev/null
+++ b/src/test/examples/naming001/C.java
@@ -0,0 +1,13 @@
+// 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 naming001;
+
+public class C {
+ static void f() {
+ }
+
+ static void m() {
+ }
+}
+
diff --git a/src/test/examples/naming001/D.java b/src/test/examples/naming001/D.java
new file mode 100644
index 0000000..c1ec759
--- /dev/null
+++ b/src/test/examples/naming001/D.java
@@ -0,0 +1,21 @@
+// 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 naming001;
+
+public class D {
+ public void keep() {
+ System.out.println();
+ }
+
+ public static void main(String[] args) {
+ D d = new E();
+ d.keep();
+ }
+
+ public static void main2(String[] args) {
+ D d = new D();
+ d.keep();
+ }
+}
+
diff --git a/src/test/examples/naming001/E.java b/src/test/examples/naming001/E.java
new file mode 100644
index 0000000..b445323
--- /dev/null
+++ b/src/test/examples/naming001/E.java
@@ -0,0 +1,11 @@
+// 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 naming001;
+
+public class E extends D {
+ @Override
+ public void keep() {
+ }
+}
+
diff --git a/src/test/examples/naming001/F.java b/src/test/examples/naming001/F.java
new file mode 100644
index 0000000..3ca60f3
--- /dev/null
+++ b/src/test/examples/naming001/F.java
@@ -0,0 +1,11 @@
+// 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 naming001;
+
+public class F extends E {
+ @Override
+ public void keep() {
+ }
+}
+
diff --git a/src/test/examples/naming001/G.java b/src/test/examples/naming001/G.java
new file mode 100644
index 0000000..175ae8c
--- /dev/null
+++ b/src/test/examples/naming001/G.java
@@ -0,0 +1,16 @@
+// 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 naming001;
+
+public class G implements H {
+ @Override
+ public void m() {
+ }
+
+ public static void main(String[] args) {
+ H i = new G();
+ i.m();
+ }
+}
+
diff --git a/src/test/examples/naming001/H.java b/src/test/examples/naming001/H.java
new file mode 100644
index 0000000..5a82977
--- /dev/null
+++ b/src/test/examples/naming001/H.java
@@ -0,0 +1,8 @@
+// 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 naming001;
+
+public interface H extends I {
+}
+
diff --git a/src/test/examples/naming001/I.java b/src/test/examples/naming001/I.java
new file mode 100644
index 0000000..6ee9ef1
--- /dev/null
+++ b/src/test/examples/naming001/I.java
@@ -0,0 +1,9 @@
+// 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 naming001;
+
+public interface I {
+ void m();
+}
+
diff --git a/src/test/examples/naming001/J.java b/src/test/examples/naming001/J.java
new file mode 100644
index 0000000..f609989
--- /dev/null
+++ b/src/test/examples/naming001/J.java
@@ -0,0 +1,31 @@
+// 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 naming001;
+
+public class J {
+ int[][][] m5(int a, boolean b, long c) {
+ return null;
+ }
+
+ int[][][] m3() {
+ return null;
+ }
+
+ int[][] m2() {
+ return null;
+ }
+
+ int[] m1() {
+ return null;
+ }
+
+ int m0() {
+ return 0;
+ }
+
+ int[][][] m() {
+ return null;
+ }
+}
+
diff --git a/src/test/examples/naming001/K.java b/src/test/examples/naming001/K.java
new file mode 100644
index 0000000..4998773
--- /dev/null
+++ b/src/test/examples/naming001/K.java
@@ -0,0 +1,29 @@
+// 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 naming001;
+
+public class K {
+ private int i;
+ private int h;
+ private final int i2 = 7;
+ private static int j;
+ private final static int i3 = 7;
+
+ private static final Object o = "TAG";
+ private static final String TAG = "TAG";
+ private final String TAG2 = "TAG";
+
+ static {
+ j = 6;
+ }
+
+ {
+ i = 6;
+ }
+
+ void keep() {
+ h = 7;
+ }
+}
+
diff --git a/src/test/examples/naming001/L.java b/src/test/examples/naming001/L.java
new file mode 100644
index 0000000..7e8b03a
--- /dev/null
+++ b/src/test/examples/naming001/L.java
@@ -0,0 +1,13 @@
+// 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 naming001;
+
+public class L {
+ private static final String TAG = "TAG";
+
+ void onReceive() {
+ System.out.println(TAG);
+ }
+}
+
diff --git a/src/test/examples/naming001/Reflect.java b/src/test/examples/naming001/Reflect.java
new file mode 100644
index 0000000..15b920e
--- /dev/null
+++ b/src/test/examples/naming001/Reflect.java
@@ -0,0 +1,53 @@
+// 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 naming001;
+
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+public class Reflect {
+ void keep() throws ClassNotFoundException {
+ Class.forName("naming001.Reflect2");
+ Class.forName("ClassThatDoesNotExists");
+ }
+
+ void keep2() throws NoSuchFieldException, SecurityException {
+ Reflect2.class.getField("fieldPublic");
+ Reflect2.class.getField("fieldPrivate");
+ }
+
+ void keep3() throws NoSuchFieldException, SecurityException {
+ Reflect2.class.getDeclaredField("fieldPublic");
+ Reflect2.class.getDeclaredField("fieldPrivate");
+ }
+
+ void keep4() throws SecurityException, NoSuchMethodException {
+ Reflect2.class.getMethod("m", new Class[] {naming001.Reflect2.A.class});
+ Reflect2.class.getMethod("m", new Class[] {naming001.Reflect2.B.class});
+ Reflect2.class.getMethod("methodThatDoesNotExist",
+ new Class[] {naming001.Reflect2.A.class});
+ }
+
+ void keep5() throws SecurityException, NoSuchMethodException {
+ Reflect2.class.getDeclaredMethod("m", new Class[] {naming001.Reflect2.A.class});
+ Reflect2.class.getDeclaredMethod("m", new Class[] {naming001.Reflect2.B.class});
+ }
+
+ void keep6() throws SecurityException {
+ AtomicIntegerFieldUpdater.newUpdater(Reflect2.class, "fieldPublic");
+ }
+
+ void keep7() throws SecurityException {
+ AtomicLongFieldUpdater.newUpdater(Reflect2.class, "fieldLong");
+ AtomicLongFieldUpdater.newUpdater(Reflect2.class, "fieldLong2");
+ }
+
+ void keep8() throws SecurityException {
+ AtomicReferenceFieldUpdater.newUpdater(Reflect2.class, Reflect2.A.class, "a");
+ AtomicReferenceFieldUpdater.newUpdater(Reflect2.class, Reflect2.A.class, "b");
+ AtomicReferenceFieldUpdater.newUpdater(Reflect2.class, Object.class, "c");
+ }
+}
+
diff --git a/src/test/examples/naming001/Reflect2.java b/src/test/examples/naming001/Reflect2.java
new file mode 100644
index 0000000..42b3075
--- /dev/null
+++ b/src/test/examples/naming001/Reflect2.java
@@ -0,0 +1,40 @@
+// 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 naming001;
+
+public class Reflect2 {
+ public volatile int fieldPublic;
+
+ private volatile int fieldPrivate;
+
+ public volatile long fieldLong;
+
+ private volatile long fieldLong2;
+
+ volatile long fieldLong3;
+
+ protected volatile long fieldLong4;
+
+ public volatile A a;
+
+ public volatile B b;
+
+ private volatile Object c;
+
+ private void calledMethod() {
+ }
+
+ public void m(A a) {
+ }
+
+ private void privateMethod(B b) {
+ }
+
+ class A {
+ }
+
+ class B {
+ }
+}
+
diff --git a/src/test/examples/naming001/keep-rules-001.txt b/src/test/examples/naming001/keep-rules-001.txt
new file mode 100644
index 0000000..72be5a2
--- /dev/null
+++ b/src/test/examples/naming001/keep-rules-001.txt
@@ -0,0 +1,7 @@
+# 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.
+
+-allowaccessmodification
+
+-keep class naming001.A
diff --git a/src/test/examples/naming001/keep-rules-002.txt b/src/test/examples/naming001/keep-rules-002.txt
new file mode 100644
index 0000000..1554e31
--- /dev/null
+++ b/src/test/examples/naming001/keep-rules-002.txt
@@ -0,0 +1,9 @@
+# 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.
+
+-allowaccessmodification
+
+-keep !final class naming001.A {
+ private <methods>;
+}
diff --git a/src/test/examples/naming001/keep-rules-003.txt b/src/test/examples/naming001/keep-rules-003.txt
new file mode 100644
index 0000000..cdcab79
--- /dev/null
+++ b/src/test/examples/naming001/keep-rules-003.txt
@@ -0,0 +1,9 @@
+# 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.
+
+-allowaccessmodification
+
+-keep !final class naming001.A {
+ !private <methods>;
+}
diff --git a/src/test/examples/naming001/keep-rules-005.txt b/src/test/examples/naming001/keep-rules-005.txt
new file mode 100644
index 0000000..ef4b5c8
--- /dev/null
+++ b/src/test/examples/naming001/keep-rules-005.txt
@@ -0,0 +1,9 @@
+# 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.
+
+-allowaccessmodification
+
+-keep class naming001.D {
+ public static void main(...);
+}
diff --git a/src/test/examples/naming001/keep-rules-006.txt b/src/test/examples/naming001/keep-rules-006.txt
new file mode 100644
index 0000000..7a8e99e
--- /dev/null
+++ b/src/test/examples/naming001/keep-rules-006.txt
@@ -0,0 +1,9 @@
+# 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.
+
+-allowaccessmodification
+
+-keep class naming001.G {
+ public static void main(...);
+}
diff --git a/src/test/examples/naming001/keep-rules-014.txt b/src/test/examples/naming001/keep-rules-014.txt
new file mode 100644
index 0000000..6423782
--- /dev/null
+++ b/src/test/examples/naming001/keep-rules-014.txt
@@ -0,0 +1,9 @@
+# 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.
+
+-allowaccessmodification
+
+-keep class naming001.Reflect {
+ *** keep6();
+}
diff --git a/src/test/examples/naming001/keep-rules-017.txt b/src/test/examples/naming001/keep-rules-017.txt
new file mode 100644
index 0000000..b146df8
--- /dev/null
+++ b/src/test/examples/naming001/keep-rules-017.txt
@@ -0,0 +1,9 @@
+# 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.
+
+-allowaccessmodification
+
+-keep class naming001.K {
+ void keep();
+}
diff --git a/src/test/examples/naming044/A.java b/src/test/examples/naming044/A.java
new file mode 100644
index 0000000..799f765
--- /dev/null
+++ b/src/test/examples/naming044/A.java
@@ -0,0 +1,8 @@
+// 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 naming044;
+
+public class A {
+ static int f = 8;
+}
diff --git a/src/test/examples/naming044/B.java b/src/test/examples/naming044/B.java
new file mode 100644
index 0000000..7580945
--- /dev/null
+++ b/src/test/examples/naming044/B.java
@@ -0,0 +1,10 @@
+// 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 naming044;
+
+public class B {
+ public static int m() {
+ return A.f;
+ }
+}
diff --git a/src/test/examples/naming044/keep-rules-001.txt b/src/test/examples/naming044/keep-rules-001.txt
new file mode 100644
index 0000000..a191b54
--- /dev/null
+++ b/src/test/examples/naming044/keep-rules-001.txt
@@ -0,0 +1,11 @@
+# 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.
+
+-allowaccessmodification
+
+-repackageclasses ''
+
+-keep,allowobfuscation class * {
+ *;
+}
diff --git a/src/test/examples/naming044/keep-rules-002.txt b/src/test/examples/naming044/keep-rules-002.txt
new file mode 100644
index 0000000..c6e495b
--- /dev/null
+++ b/src/test/examples/naming044/keep-rules-002.txt
@@ -0,0 +1,11 @@
+# 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.
+
+-allowaccessmodification
+
+-repackageclasses 'p44.x'
+
+-keep,allowobfuscation class * {
+ *;
+}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 1683557..e4b37f2 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -11,10 +11,17 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.shaking.ProguardConfigurationParser;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.shaking.RootSetBuilder;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -421,6 +428,17 @@
.read();
}
+ public static ProguardConfiguration loadProguardConfiguration(
+ DexItemFactory factory, List<Path> configPaths)
+ throws IOException, ProguardRuleParserException {
+ if (configPaths.isEmpty()) {
+ return ProguardConfiguration.defaultConfiguration(factory);
+ }
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(factory);
+ parser.parse(configPaths);
+ return parser.getConfig();
+ }
+
public static R8Command.Builder prepareR8CommandBuilder(AndroidApp app) {
return R8Command.builder(app);
}
diff --git a/src/test/java/com/android/tools/r8/naming/MinifierTest.java b/src/test/java/com/android/tools/r8/naming/MinifierTest.java
new file mode 100644
index 0000000..1124ceb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/MinifierTest.java
@@ -0,0 +1,200 @@
+// 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.naming;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.Timing;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+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 MinifierTest extends NamingTestBase {
+
+ public MinifierTest(
+ String test,
+ List<String> keepRulesFiles,
+ BiConsumer<DexItemFactory, NamingLens> inspection) {
+ super(test, keepRulesFiles, inspection, new Timing("MinifierTest"));
+ }
+
+ @Test
+ public void minifierTest() throws Exception {
+ NamingLens naming = runMinifier(ListUtils.map(keepRulesFiles, Paths::get));
+ inspection.accept(dexItemFactory, naming);
+ }
+
+ @Parameters(name = "test: {0} keep: {1}")
+ public static Collection<Object[]> data() {
+ List<String> tests = Arrays.asList("naming001");
+
+ Map<String, BiConsumer<DexItemFactory, NamingLens>> inspections = new HashMap<>();
+ inspections.put("naming001:keep-rules-001.txt", MinifierTest::test001_rule001);
+ inspections.put("naming001:keep-rules-002.txt", MinifierTest::test001_rule002);
+ inspections.put("naming001:keep-rules-003.txt", MinifierTest::test001_rule003);
+ inspections.put("naming001:keep-rules-005.txt", MinifierTest::test001_rule005);
+ inspections.put("naming001:keep-rules-006.txt", MinifierTest::test001_rule006);
+ inspections.put("naming001:keep-rules-014.txt", MinifierTest::test001_rule014);
+ inspections.put("naming001:keep-rules-017.txt", MinifierTest::test001_rule017);
+
+ return createTests(tests, inspections);
+ }
+
+ private static void test001_rule001(DexItemFactory dexItemFactory, NamingLens naming) {
+ DexType a = dexItemFactory.createType("Lnaming001/A;");
+ // class naming001.A should be kept, according to the keep rule.
+ assertEquals("Lnaming001/A;", naming.lookupDescriptor(a).toSourceString());
+
+ DexMethod m = dexItemFactory.createMethod(
+ a, dexItemFactory.createProto(dexItemFactory.voidType), "m");
+ // method naming001.A.m would be renamed.
+ assertNotEquals("m", naming.lookupName(m).toSourceString());
+
+ DexMethod p = dexItemFactory.createMethod(
+ a, dexItemFactory.createProto(dexItemFactory.voidType), "privateFunc");
+ // method naming001.A.privateFunc would be renamed.
+ assertNotEquals("privateFunc", naming.lookupName(p).toSourceString());
+
+ DexType k = dexItemFactory.createType("Lnaming001/K;");
+ DexField h = dexItemFactory.createField(k, dexItemFactory.intType, "h");
+ // field naming001.K.h is dead, not renamed; hence returned as same via identityLens.
+ assertEquals("h", naming.lookupName(h).toSourceString());
+ }
+
+ private static void test001_rule002(DexItemFactory dexItemFactory, NamingLens naming) {
+ DexType a = dexItemFactory.createType("Lnaming001/A;");
+ // class naming001.A should be kept, according to the keep rule.
+ assertEquals("Lnaming001/A;", naming.lookupDescriptor(a).toSourceString());
+
+ DexMethod m = dexItemFactory.createMethod(
+ a, dexItemFactory.createProto(dexItemFactory.voidType), "m");
+ // method naming001.A.m would be renamed.
+ assertNotEquals("m", naming.lookupName(m).toSourceString());
+
+ DexMethod p = dexItemFactory.createMethod(
+ a, dexItemFactory.createProto(dexItemFactory.voidType), "privateFunc");
+ // method naming001.A.privateFunc should be kept, according to the keep rule.
+ assertEquals("privateFunc", naming.lookupName(p).toSourceString());
+ }
+
+ private static void test001_rule003(DexItemFactory dexItemFactory, NamingLens naming) {
+ DexType a = dexItemFactory.createType("Lnaming001/A;");
+ // class naming001.A should be kept, according to the keep rule.
+ assertEquals("Lnaming001/A;", naming.lookupDescriptor(a).toSourceString());
+
+ DexMethod m = dexItemFactory.createMethod(
+ a, dexItemFactory.createProto(dexItemFactory.voidType), "m");
+ // method naming001.A.m should be kept, according to the keep rule.
+ assertEquals("m", naming.lookupName(m).toSourceString());
+
+ DexMethod p = dexItemFactory.createMethod(
+ a, dexItemFactory.createProto(dexItemFactory.voidType), "privateFunc");
+ // method naming001.A.privateFunc would be renamed.
+ assertNotEquals("privateFunc", naming.lookupName(p).toSourceString());
+ }
+
+ private static void test001_rule005(DexItemFactory dexItemFactory, NamingLens naming) {
+ DexType d = dexItemFactory.createType("Lnaming001/D;");
+ // class naming001.D should be kept, according to the keep rule.
+ assertEquals("Lnaming001/D;", naming.lookupDescriptor(d).toSourceString());
+
+ DexMethod main = dexItemFactory.createMethod(
+ d,
+ dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.stringArrayType),
+ "main");
+ // method naming001.D.main should be kept, according to the keep rule.
+ assertEquals("main", naming.lookupName(main).toSourceString());
+
+ DexMethod k = dexItemFactory.createMethod(
+ d, dexItemFactory.createProto(dexItemFactory.voidType), "keep");
+ // method naming001.D.keep would be renamed.
+ assertNotEquals("keep", naming.lookupName(k).toSourceString());
+
+ // Note that naming001.E extends naming001.D.
+ DexType e = dexItemFactory.createType("Lnaming001/E;");
+ DexMethod inherited_k = dexItemFactory.createMethod(
+ e, dexItemFactory.createProto(dexItemFactory.voidType), "keep");
+ // method naming001.E.keep should be renamed to the same name as naming001.D.keep.
+ assertEquals(
+ naming.lookupName(inherited_k).toSourceString(), naming.lookupName(k).toSourceString());
+ }
+
+ private static void test001_rule006(DexItemFactory dexItemFactory, NamingLens naming) {
+ DexType g = dexItemFactory.createType("Lnaming001/G;");
+ // class naming001.G should be kept, according to the keep rule.
+ assertEquals("Lnaming001/G;", naming.lookupDescriptor(g).toSourceString());
+
+ DexMethod main = dexItemFactory.createMethod(
+ g,
+ dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.stringArrayType),
+ "main");
+ // method naming001.G.main should be kept, according to the keep rule.
+ assertEquals("main", naming.lookupName(main).toSourceString());
+
+ DexMethod impl_m = dexItemFactory.createMethod(
+ g, dexItemFactory.createProto(dexItemFactory.voidType), "m");
+ // method naming001.G.m would be renamed.
+ assertNotEquals("m", naming.lookupName(impl_m).toSourceString());
+
+ // Note that naming001.G implements H that extends I, where method m is declared.
+ DexType i = dexItemFactory.createType("Lnaming001/I;");
+ DexMethod def_m = dexItemFactory.createMethod(
+ i, dexItemFactory.createProto(dexItemFactory.voidType), "m");
+ // method naming001.I.m should be renamed to the same name as naming001.G.m.
+ assertEquals(
+ naming.lookupName(impl_m).toSourceString(), naming.lookupName(def_m).toSourceString());
+ }
+
+ private static void test001_rule014(DexItemFactory dexItemFactory, NamingLens naming) {
+ DexType reflect = dexItemFactory.createType("Lnaming001/Reflect;");
+ // class naming001.Reflect should be kept, according to the keep rule.
+ assertEquals("Lnaming001/Reflect;", naming.lookupDescriptor(reflect).toSourceString());
+
+ DexMethod keep6 = dexItemFactory.createMethod(
+ reflect, dexItemFactory.createProto(dexItemFactory.voidType), "keep6");
+ // method naming001.Reflect.keep6 should be kept, according to the keep rule.
+ assertEquals("keep6", naming.lookupName(keep6).toSourceString());
+
+ DexType reflect2 = dexItemFactory.createType("Lnaming001/Reflect2;");
+ DexField fieldPublic = dexItemFactory.createField(
+ reflect2, dexItemFactory.intType, "fieldPublic");
+ // method naming001.Reflect.keep6 accesses to naming001.Reflect2.fieldPublic via reflection.
+ assertEquals("fieldPublic", naming.lookupName(fieldPublic).toSourceString());
+ }
+
+ private static void test001_rule017(DexItemFactory dexItemFactory, NamingLens naming) {
+ DexType k = dexItemFactory.createType("Lnaming001/K;");
+ // class naming001.K should be kept, according to the keep rule.
+ assertEquals("Lnaming001/K;", naming.lookupDescriptor(k).toSourceString());
+
+ DexMethod keep = dexItemFactory.createMethod(
+ k, dexItemFactory.createProto(dexItemFactory.voidType), "keep");
+ // method naming001.K.keep should be kept, according to the keep rule.
+ assertEquals("keep", naming.lookupName(keep).toSourceString());
+
+ DexField i = dexItemFactory.createField(k, dexItemFactory.intType, "i");
+ // field naming001.K.i
+ assertEquals("i", naming.lookupName(i).toSourceString());
+
+ DexField j = dexItemFactory.createField(k, dexItemFactory.intType, "j");
+ // field naming001.K.j
+ assertEquals("j", naming.lookupName(j).toSourceString());
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
new file mode 100644
index 0000000..b0fe113
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -0,0 +1,124 @@
+// 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.naming;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.ClassAndMemberPublicizer;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.shaking.RootSetBuilder;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.function.BiConsumer;
+import org.junit.Before;
+
+abstract class NamingTestBase {
+
+ private final String appFileName;
+ final List<String> keepRulesFiles;
+ final BiConsumer<DexItemFactory, NamingLens> inspection;
+
+ private final Timing timing;
+
+ private DexApplication program;
+ DexItemFactory dexItemFactory;
+ private AppInfoWithSubtyping appInfo;
+
+ NamingTestBase(
+ String test,
+ List<String> keepRulesFiles,
+ BiConsumer<DexItemFactory, NamingLens> inspection,
+ Timing timing) {
+ appFileName = ToolHelper.EXAMPLES_BUILD_DIR + test + "/classes.dex";
+ this.keepRulesFiles = keepRulesFiles;
+ this.inspection = inspection;
+ this.timing = timing;
+ }
+
+ @Before
+ public void readApp() throws IOException, ExecutionException, ProguardRuleParserException {
+ program = ToolHelper.buildApplication(ImmutableList.of(appFileName));
+ dexItemFactory = program.dexItemFactory;
+ appInfo = new AppInfoWithSubtyping(program);
+ }
+
+ NamingLens runMinifier(List<Path> configPaths)
+ throws IOException, ProguardRuleParserException, ExecutionException {
+ ProguardConfiguration configuration =
+ ToolHelper.loadProguardConfiguration(dexItemFactory, configPaths);
+ InternalOptions options = new InternalOptions();
+ copyProguardConfigurationToInternalOptions(configuration, options);
+
+ if (options.allowAccessModification) {
+ ClassAndMemberPublicizer.run(program);
+ }
+
+ RootSet rootSet = new RootSetBuilder(program, appInfo, configuration.getRules())
+ .run(ThreadUtils.getExecutorService(options));
+ Enqueuer enqueuer = new Enqueuer(appInfo);
+ appInfo = enqueuer.traceApplication(rootSet, timing);
+ return new Minifier(appInfo.withLiveness(), rootSet, options).run(timing);
+ }
+
+ private void copyProguardConfigurationToInternalOptions(
+ ProguardConfiguration config, InternalOptions options) {
+ options.packagePrefix = config.getPackagePrefix();
+ options.allowAccessModification = config.getAllowAccessModification();
+ options.classObfuscationDictionary = config.getClassObfuscationDictionary();
+ options.obfuscationDictionary = config.getObfuscationDictionary();
+ options.keepRules = config.getRules();
+ }
+
+ static <T> Collection<Object[]> createTests(List<String> tests, Map<String, T> inspections) {
+ List<Object[]> testCases = new ArrayList<>();
+ Set<String> usedInspections = new HashSet<>();
+ for (String test : tests) {
+ File[] keepFiles = new File(ToolHelper.EXAMPLES_DIR + test)
+ .listFiles(file -> file.isFile() && file.getName().endsWith(".txt"));
+ for (File keepFile : keepFiles) {
+ String keepName = keepFile.getName();
+ T inspection = getTestOptionalParameter(inspections, usedInspections, test, keepName);
+ if (inspection != null) {
+ testCases.add(new Object[]{test, ImmutableList.of(keepFile.getPath()), inspection});
+ }
+ }
+ }
+ assert usedInspections.size() == inspections.size();
+ return testCases;
+ }
+
+ private static <T> T getTestOptionalParameter(
+ Map<String, T> specifications,
+ Set<String> usedSpecifications,
+ String test,
+ String keepName) {
+ T parameter = specifications.get(test);
+ if (parameter == null) {
+ parameter = specifications.get(test + ":" + keepName);
+ if (parameter != null) {
+ usedSpecifications.add(test + ":" + keepName);
+ }
+ } else {
+ usedSpecifications.add(test);
+ }
+ return parameter;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
new file mode 100644
index 0000000..3e9b269
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
@@ -0,0 +1,72 @@
+// 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.naming;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.Timing;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+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 PackageNamingTest extends NamingTestBase {
+
+ public PackageNamingTest(
+ String test,
+ List<String> keepRulesFiles,
+ BiConsumer<DexItemFactory, NamingLens> inspection) {
+ super(test, keepRulesFiles, inspection, new Timing("PackageNamingTest"));
+ }
+
+ @Test
+ public void packageNamingTest() throws Exception {
+ NamingLens naming = runMinifier(ListUtils.map(keepRulesFiles, Paths::get));
+ inspection.accept(dexItemFactory, naming);
+ }
+
+ @Parameters(name = "test: {0} keep: {1}")
+ public static Collection<Object[]> data() {
+ List<String> tests = Arrays.asList("naming044");
+
+ Map<String, BiConsumer<DexItemFactory, NamingLens>> inspections = new HashMap<>();
+ inspections.put("naming044:keep-rules-001.txt", PackageNamingTest::test044_rule001);
+ inspections.put("naming044:keep-rules-002.txt", PackageNamingTest::test044_rule002);
+
+ return createTests(tests, inspections);
+ }
+
+ private static void test044_rule001(DexItemFactory dexItemFactory, NamingLens naming) {
+ DexType b = dexItemFactory.createType("Lnaming044/B;");
+ // All classes are moved to the top-level package, hence no package separator.
+ assertFalse(naming.lookupDescriptor(b).toSourceString().contains("/"));
+
+ DexMethod m = dexItemFactory.createMethod(
+ b, dexItemFactory.createProto(dexItemFactory.intType), "m");
+ // method naming044.B.m would be renamed.
+ assertNotEquals("m", naming.lookupName(m).toSourceString());
+ }
+
+ private static void test044_rule002(DexItemFactory dexItemFactory, NamingLens naming) {
+ DexType a = dexItemFactory.createType("Lnaming044/A;");
+ assertTrue(naming.lookupDescriptor(a).toSourceString().startsWith("Lp44/x/"));
+
+ DexType b = dexItemFactory.createType("Lnaming044/B;");
+ assertTrue(naming.lookupDescriptor(b).toSourceString().startsWith("Lp44/x/"));
+ }
+}