Add test for field removal
Bug: 33230777
Change-Id: I0aa76df03bc625a5e148b1af5ff68757a25831de
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 4d707a8..925ba5b 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -135,7 +135,7 @@
*/
protected AndroidApp compileWithR8(List<Class> classes, String proguardConfig)
throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
- return compileWithR8(readClasses(classes), writeTextToTempFile(proguardConfig));
+ return compileWithR8(readClasses(classes), proguardConfig);
}
/**
@@ -170,10 +170,22 @@
/**
* Compile an application with R8 using the supplied proguard configuration.
*/
+ protected AndroidApp compileWithR8(AndroidApp app, String proguardConfig)
+ throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+ return compileWithR8(app, proguardConfig, null);
+ }
+
+ /**
+ * Compile an application with R8 using the supplied proguard configuration.
+ */
protected AndroidApp compileWithR8(
AndroidApp app, String proguardConfig, Consumer<InternalOptions> optionsConsumer)
throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
- return compileWithR8(app, writeTextToTempFile(proguardConfig), optionsConsumer);
+ R8Command command =
+ ToolHelper.prepareR8CommandBuilder(app)
+ .addProguardConfiguration(ImmutableList.of(proguardConfig))
+ .build();
+ return ToolHelper.runR8(command, optionsConsumer);
}
/**
@@ -194,7 +206,15 @@
* of the specified class.
*/
public String keepMainProguardConfiguration(Class clazz) {
- return "-keep public class " + clazz.getCanonicalName() + " {\n"
+ return keepMainProguardConfiguration(clazz.getCanonicalName());
+ }
+
+ /**
+ * Generate a Proguard configuration for keeping the "public static void main(String[])" method
+ * of the specified class.
+ */
+ public String keepMainProguardConfiguration(String clazz) {
+ return "-keep public class " + clazz + " {\n"
+ " public static void main(java.lang.String[]);\n"
+ "}\n";
}
diff --git a/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java b/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java
new file mode 100644
index 0000000..674124a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java
@@ -0,0 +1,118 @@
+// 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.smali;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class RemoveWriteOfUnusedFieldsTest extends SmaliTestBase {
+
+ @Test
+ public void unreadStaticFieldsRemoved() throws Exception {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ // All these static fields are set but never read.
+ builder.addStaticField("booleanField", "Z");
+ builder.addStaticField("byteField", "B");
+ builder.addStaticField("shortField", "S");
+ builder.addStaticField("intField", "I");
+ builder.addStaticField("longField", "J");
+ builder.addStaticField("floatField", "F");
+ builder.addStaticField("doubleField", "D");
+ builder.addStaticField("charField", "C");
+ builder.addStaticField("objectField", "Ljava/lang/Object;");
+ builder.addStaticField("stringField", "Ljava/lang/String;");
+ builder.addStaticField("testField", "LTest;");
+
+ builder.addStaticMethod("void", "test", ImmutableList.of(),
+ 2,
+ "const v0, 0",
+ "sput-byte v0, LTest;->booleanField:Z",
+ "sput-byte v0, LTest;->byteField:B",
+ "sput-short v0, LTest;->shortField:S",
+ "sput v0, LTest;->intField:I",
+ "sput v0, LTest;->floatField:F",
+ "sput-char v0, LTest;->charField:C",
+ "sput-object v0, LTest;->objectField:Ljava/lang/Object;",
+ "sput-object v0, LTest;->stringField:Ljava/lang/String;",
+ "sput-object v0, LTest;->testField:LTest;",
+ "const-wide v0, 0",
+ "sput-wide v0, LTest;->longField:J",
+ "sput-wide v0, LTest;->doubleField:D",
+ "return-void");
+
+ builder.addMainMethod(
+ 0,
+ " invoke-static { }, LTest;->test()V",
+ " return-void ");
+
+ AndroidApp app = compileWithR8(
+ AndroidApp.fromDexProgramData(builder.compile()),
+ keepMainProguardConfiguration("Test"),
+ options -> options.inlineAccessors = false);
+
+ DexInspector inspector = new DexInspector(app);
+ MethodSubject method = inspector.clazz("Test").method("void", "test", ImmutableList.of());
+ DexCode code = method.getMethod().getCode().asDexCode();
+ assertTrue(code.isEmptyVoidMethod());
+ }
+
+ @Test
+ public void unreadInstanceFieldsRemoved() throws Exception {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ // All these instance fields are set but never read.
+ builder.addInstanceField("booleanField", "Z");
+ builder.addInstanceField("byteField", "B");
+ builder.addInstanceField("shortField", "S");
+ builder.addInstanceField("intField", "I");
+ builder.addInstanceField("longField", "J");
+ builder.addInstanceField("floatField", "F");
+ builder.addInstanceField("doubleField", "D");
+ builder.addInstanceField("charField", "C");
+ builder.addInstanceField("objectField", "Ljava/lang/Object;");
+ builder.addInstanceField("stringField", "Ljava/lang/String;");
+ builder.addInstanceField("testField", "LTest;");
+
+ builder.addInstanceMethod("void", "test", ImmutableList.of(),
+ 2,
+ "const v0, 0",
+ "iput-byte v0, p0, LTest;->booleanField:Z",
+ "iput-byte v0, p0, LTest;->byteField:B",
+ "iput-short v0, p0, LTest;->shortField:S",
+ "iput v0, p0, LTest;->intField:I",
+ "iput v0, p0, LTest;->floatField:F",
+ "iput-char v0, p0, LTest;->charField:C",
+ "iput-object v0, p0, LTest;->objectField:Ljava/lang/Object;",
+ "iput-object v0, p0, LTest;->stringField:Ljava/lang/String;",
+ "iput-object v0, p0, LTest;->testField:LTest;",
+ "const-wide v0, 0",
+ "iput-wide v0, p0, LTest;->longField:J",
+ "iput-wide v0, p0, LTest;->doubleField:D",
+ "return-void");
+
+ builder.addMainMethod(
+ 1,
+ " new-instance v0, LTest;",
+ " invoke-virtual { v0 }, LTest;->test()V",
+ " return-void ");
+
+ AndroidApp app = compileWithR8(
+ AndroidApp.fromDexProgramData(builder.compile()),
+ keepMainProguardConfiguration("Test"),
+ options -> options.inlineAccessors = false);
+
+ DexInspector inspector = new DexInspector(app);
+ MethodSubject method = inspector.clazz("Test").method("void", "test", ImmutableList.of());
+ DexCode code = method.getMethod().getCode().asDexCode();
+ assertTrue(code.isEmptyVoidMethod());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index e2006d0..56059f6 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -24,7 +24,6 @@
import com.android.tools.r8.ir.code.ValueNumberGenerator;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.DexInspector;
@@ -235,6 +234,45 @@
addStaticField(name, type, null);
}
+ public void addInstanceField(String name, String type) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(".field ");
+ builder.append(name);
+ builder.append(":");
+ builder.append(type);
+ getSource(currentClassName).add(builder.toString());
+ }
+
+ private MethodSignature addMethod(String flags, String returnType, String name,
+ List<String> parameters, int locals, String code) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(".method ");
+ if (flags != null && flags.length() > 0) {
+ builder.append(flags);
+ builder.append(" ");
+ }
+ builder.append(name);
+ builder.append("(");
+ for (String parameter : parameters) {
+ builder.append(DescriptorUtils.javaTypeToDescriptor(parameter));
+ }
+ builder.append(")");
+ builder.append(DescriptorUtils.javaTypeToDescriptor(returnType));
+ builder.append("\n");
+ if (locals >= 0) {
+ builder.append(".locals ");
+ builder.append(locals);
+ builder.append("\n\n");
+ assert code != null;
+ builder.append(code);
+ } else {
+ assert code == null;
+ }
+ builder.append(".end method");
+ getSource(currentClassName).add(builder.toString());
+ return new MethodSignature(currentClassName, name, returnType, parameters);
+ }
+
public MethodSignature addStaticMethod(String returnType, String name, List<String> parameters,
int locals, String... instructions) {
StringBuilder builder = new StringBuilder();
@@ -266,43 +304,27 @@
private MethodSignature addStaticMethod(String flags, String returnType, String name,
List<String> parameters, int locals, String code) {
StringBuilder builder = new StringBuilder();
- builder.append(".method public static ");
- if (flags != null && flags.length() > 0) {
- builder.append(flags);
- builder.append(" ");
- }
- builder.append(name);
- builder.append("(");
- for (String parameter : parameters) {
- builder.append(DescriptorUtils.javaTypeToDescriptor(parameter));
- }
- builder.append(")");
- builder.append(DescriptorUtils.javaTypeToDescriptor(returnType));
- builder.append("\n");
- builder.append(".locals ");
- builder.append(locals);
- builder.append("\n\n");
- builder.append(code);
- builder.append(".end method");
- getSource(currentClassName).add(builder.toString());
- return new MethodSignature(currentClassName, name, returnType, parameters);
+ return addMethod("public static " + flags, returnType, name, parameters, locals, code);
}
public MethodSignature addAbstractMethod(
String returnType, String name, List<String> parameters) {
+ return addMethod("public abstract", returnType, name, parameters, -1, null);
+ }
+
+ public MethodSignature addInstanceMethod(String returnType, String name, List<String> parameters,
+ int locals, String... instructions) {
StringBuilder builder = new StringBuilder();
- builder.append(".method public abstract ");
- builder.append(name);
- builder.append("(");
- for (String parameter : parameters) {
- builder.append(DescriptorUtils.javaTypeToDescriptor(parameter));
+ for (String instruction : instructions) {
+ builder.append(instruction);
+ builder.append("\n");
}
- builder.append(")");
- builder.append(DescriptorUtils.javaTypeToDescriptor(returnType));
- builder.append("\n");
- builder.append(".end method");
- getSource(currentClassName).add(builder.toString());
- return new MethodSignature(currentClassName, name, returnType, parameters);
+ return addInstanceMethod(returnType, name, parameters, locals, builder.toString());
+ }
+
+ public MethodSignature addInstanceMethod(String returnType, String name, List<String> parameters,
+ int locals, String code) {
+ return addMethod("public", returnType, name, parameters, locals, code);
}
public MethodSignature addMainMethod(int locals, String... instructions) {