| // 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()); |
| }); |
| } |
| } |