| // Copyright (c) 2019, 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.shaking.ifrule; | |
| import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; | |
| import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed; | |
| import static org.hamcrest.CoreMatchers.not; | |
| import static org.hamcrest.MatcherAssert.assertThat; | |
| import static org.junit.Assert.assertEquals; | |
| import com.android.tools.r8.TestBase; | |
| import com.android.tools.r8.TestParameters; | |
| import com.android.tools.r8.utils.BooleanUtils; | |
| import com.android.tools.r8.utils.StringUtils; | |
| import com.android.tools.r8.utils.codeinspector.ClassSubject; | |
| import java.util.List; | |
| import org.junit.Assume; | |
| import org.junit.Test; | |
| import org.junit.runner.RunWith; | |
| import org.junit.runners.Parameterized; | |
| @RunWith(Parameterized.class) | |
| public class ConditionalKeepIfKeptTest extends TestBase { | |
| static final String EXPECTED = | |
| StringUtils.lines("class " + StaticallyReferenced.class.getTypeName()); | |
| private final TestParameters parameters; | |
| private final boolean useMarker; | |
| @Parameterized.Parameters(name = "{0}, marker:{1}") | |
| public static List<Object[]> data() { | |
| return buildParameters( | |
| getTestParameters().withCfRuntimes().withDexRuntimes().withAllApiLevels().build(), | |
| BooleanUtils.values()); | |
| } | |
| public ConditionalKeepIfKeptTest(TestParameters parameters, boolean useMarker) { | |
| this.parameters = parameters; | |
| this.useMarker = useMarker; | |
| } | |
| private String getConditionalRulePrefix() { | |
| String clazz = StaticallyReferenced.class.getTypeName(); | |
| return useMarker | |
| ? "-if @" + Marked.class.getTypeName() + " class * -keep class <1>" | |
| : "-if class " + clazz + " -keep class " + clazz; | |
| } | |
| @Test | |
| public void testIfKeepNoMembers() throws Exception { | |
| testForR8(parameters.getBackend()) | |
| .addInnerClasses(ConditionalKeepIfKeptTest.class) | |
| .addKeepRules(getConditionalRulePrefix()) | |
| .addKeepMainRule(TestClass.class) | |
| .setMinApi(parameters.getApiLevel()) | |
| .run(parameters.getRuntime(), TestClass.class) | |
| .assertSuccessWithOutput(EXPECTED) | |
| .inspect( | |
| inspector -> { | |
| ClassSubject classSubject = inspector.clazz(StaticallyReferenced.class); | |
| assertThat(classSubject, isPresent()); | |
| assertEquals(0, classSubject.allFields().size()); | |
| // TODO(b/132318799): Full mode no-marker should not keep <init>() when not specified. | |
| assertEquals(useMarker ? 0 : 1, classSubject.allMethods().size()); | |
| assertEquals(!useMarker, classSubject.init().isPresent()); | |
| }); | |
| } | |
| @Test | |
| public void testIfKeepAllMembers() throws Exception { | |
| Assume.assumeFalse("b/132318609", useMarker); | |
| testForR8(parameters.getBackend()) | |
| .addInnerClasses(ConditionalKeepIfKeptTest.class) | |
| .addKeepRules(getConditionalRulePrefix() + " { *; }") | |
| .addKeepMainRule(TestClass.class) | |
| .setMinApi(parameters.getApiLevel()) | |
| .run(parameters.getRuntime(), TestClass.class) | |
| .assertSuccessWithOutput(EXPECTED) | |
| .inspect( | |
| inspector -> { | |
| ClassSubject classSubject = inspector.clazz(StaticallyReferenced.class); | |
| assertThat(classSubject, isPresent()); | |
| assertEquals(2, classSubject.allFields().size()); | |
| assertEquals(3, classSubject.allMethods().size()); | |
| }); | |
| } | |
| @Test | |
| public void testIfKeepStaticMembers() throws Exception { | |
| Assume.assumeFalse("b/132318609", useMarker); | |
| testForR8(parameters.getBackend()) | |
| .addInnerClasses(ConditionalKeepIfKeptTest.class) | |
| .addKeepRules(getConditionalRulePrefix() + " { static *; }") | |
| .addKeepMainRule(TestClass.class) | |
| .setMinApi(parameters.getApiLevel()) | |
| .run(parameters.getRuntime(), TestClass.class) | |
| .assertSuccessWithOutput(EXPECTED) | |
| .inspect( | |
| inspector -> { | |
| ClassSubject classSubject = inspector.clazz(StaticallyReferenced.class); | |
| assertThat(classSubject, isPresent()); | |
| assertEquals(1, classSubject.allFields().size()); | |
| assertEquals(1, classSubject.allMethods().size()); | |
| }); | |
| } | |
| @Test | |
| public void testIfKeepNonStaticMembers() throws Exception { | |
| Assume.assumeFalse("b/132318609", useMarker); | |
| testForR8(parameters.getBackend()) | |
| .addInnerClasses(ConditionalKeepIfKeptTest.class) | |
| .addKeepRules(getConditionalRulePrefix() + " { !static *; }") | |
| .addKeepMainRule(TestClass.class) | |
| .setMinApi(parameters.getApiLevel()) | |
| .run(parameters.getRuntime(), TestClass.class) | |
| .assertSuccessWithOutput(EXPECTED) | |
| .inspect( | |
| inspector -> { | |
| ClassSubject classSubject = inspector.clazz(StaticallyReferenced.class); | |
| assertThat(classSubject, isPresent()); | |
| assertEquals(1, classSubject.allFields().size()); | |
| assertEquals(2, classSubject.allMethods().size()); | |
| }); | |
| } | |
| @Test | |
| public void testIfKeepFields() throws Exception { | |
| Assume.assumeFalse("b/132318609", useMarker); | |
| testForR8(parameters.getBackend()) | |
| .addInnerClasses(ConditionalKeepIfKeptTest.class) | |
| .addKeepRules(getConditionalRulePrefix() + " { <fields>; }") | |
| .addKeepMainRule(TestClass.class) | |
| .setMinApi(parameters.getApiLevel()) | |
| .run(parameters.getRuntime(), TestClass.class) | |
| .assertSuccessWithOutput(EXPECTED) | |
| .inspect( | |
| inspector -> { | |
| ClassSubject classSubject = inspector.clazz(StaticallyReferenced.class); | |
| assertThat(classSubject, isPresent()); | |
| assertEquals(2, classSubject.allFields().size()); | |
| assertEquals(0, classSubject.allMethods().size()); | |
| }); | |
| } | |
| @Test | |
| public void testIfKeepMethods() throws Exception { | |
| Assume.assumeFalse("b/132318609", useMarker); | |
| testForR8(parameters.getBackend()) | |
| .addInnerClasses(ConditionalKeepIfKeptTest.class) | |
| .addKeepRules(getConditionalRulePrefix() + " { <methods>; }") | |
| .addKeepMainRule(TestClass.class) | |
| .setMinApi(parameters.getApiLevel()) | |
| .run(parameters.getRuntime(), TestClass.class) | |
| .assertSuccessWithOutput(EXPECTED) | |
| .inspect( | |
| inspector -> { | |
| ClassSubject classSubject = inspector.clazz(StaticallyReferenced.class); | |
| assertThat(classSubject, isPresent()); | |
| assertEquals(3, classSubject.allMethods().size()); | |
| classSubject.allMethods().forEach(m -> assertThat(m, not(isRenamed()))); | |
| // Keeping methods will cause the fields to be kept too (but allow renaming them). | |
| assertEquals(2, classSubject.allFields().size()); | |
| classSubject.allFields().forEach(f -> assertThat(f, isRenamed())); | |
| }); | |
| } | |
| @interface Marked {} | |
| @Marked | |
| static class StaticallyReferenced { | |
| static long staticField; | |
| static long staticMethod() { | |
| return staticField += System.nanoTime(); | |
| } | |
| long nonStaticField; | |
| StaticallyReferenced() { | |
| nonStaticField = System.nanoTime(); | |
| } | |
| long nonStaticMethod() { | |
| return nonStaticField; | |
| } | |
| } | |
| static class TestClass { | |
| public static void main(String[] args) { | |
| System.out.println(StaticallyReferenced.class); | |
| } | |
| } | |
| } |