blob: 67e965d299170aa1ee2dc2ef4ae4b1e81aa10b0c [file] [log] [blame]
// 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 com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
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 RemoveWriteOfUnusedFieldsTest extends SmaliTestBase {
private final TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withDexRuntimes().build();
}
public RemoveWriteOfUnusedFieldsTest(TestParameters parameters) {
this.parameters = parameters;
}
@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", // single non-float typed zero (ie, int type)
"sput-boolean v0, LTest;->booleanField:Z",
"sput-byte v0, LTest;->byteField:B",
"sput-char v0, LTest;->charField:C",
"sput-short v0, LTest;->shortField:S",
"sput v0, LTest;->intField:I",
"const v0, 0", // float typed zero
"sput v0, LTest;->floatField:F",
"const v0, 0", // reference typed null
"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", // wide typed long
"sput-wide v0, LTest;->longField:J",
"const-wide v0, 0", // wide typed double
"sput-wide v0, LTest;->doubleField:D",
"return-void");
builder.addMainMethod(
0,
" invoke-static { }, LTest;->test()V",
" return-void ");
AndroidApp input =
AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build();
Path inputPath = temp.getRoot().toPath().resolve("input.zip");
input.writeToZip(inputPath, OutputMode.DexIndexed);
ToolHelper.runArtNoVerificationErrors(inputPath.toString(), DEFAULT_CLASS_NAME);
testForR8(parameters.getBackend())
.addProgramFiles(inputPath)
.addKeepMainRule("Test")
.setMinApi(parameters.getRuntime())
.compile()
.inspect(
inspector -> {
MethodSubject method =
inspector.clazz("Test").method("void", "test", ImmutableList.of());
assertThat(
"Expected method to be removed entirely because it does not have side effects",
method,
not(isPresent()));
});
}
@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-boolean v0, p0, LTest;->booleanField:Z",
"iput-byte v0, p0, LTest;->byteField:B",
"iput-char v0, p0, LTest;->charField:C",
"iput-short v0, p0, LTest;->shortField:S",
"iput v0, p0, LTest;->intField:I",
"const v0, 0",
"iput v0, p0, LTest;->floatField:F",
"const v0, 0",
"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",
"const-wide v0, 0",
"iput-wide v0, p0, LTest;->doubleField:D",
"return-void");
builder.addInitializer(ImmutableList.of(), 0,
"invoke-direct {p0}, Ljava/lang/Object;-><init>()V",
"return-void"
);
builder.addMainMethod(
1,
" new-instance v0, LTest;",
" invoke-direct { v0 }, LTest;-><init>()V",
" invoke-virtual { v0 }, LTest;->test()V",
" return-void ");
AndroidApp input =
AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build();
Path inputPath = temp.getRoot().toPath().resolve("input.zip");
input.writeToZip(inputPath, OutputMode.DexIndexed);
ToolHelper.runArtNoVerificationErrors(inputPath.toString(), DEFAULT_CLASS_NAME);
testForR8(parameters.getBackend())
.addProgramFiles(inputPath)
.addKeepMainRule("Test")
.addKeepRules("-keep class Test { void test(); }")
.setMinApi(parameters.getRuntime())
.compile()
.inspect(
inspector -> {
MethodSubject method =
inspector.clazz("Test").method("void", "test", ImmutableList.of());
DexCode code = method.getMethod().getCode().asDexCode();
assertTrue(code.isEmptyVoidMethod());
});
}
}