blob: 8c31345fae635c74ef21286cf29824858f0baea3 [file] [log] [blame]
// Copyright (c) 2018, 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.isNotRenamed;
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.shaking.forceproguardcompatibility.ProguardCompatibilityTestBase;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FieldSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
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 IfOnClassTest extends ProguardCompatibilityTestBase {
private static final List<Class<?>> CLASSES =
ImmutableList.of(
EmptyMainClassForIfOnClassTests.class,
Precondition.class,
DependentUser.class,
Dependent.class);
private final Shrinker shrinker;
private final boolean keepPrecondition;
public IfOnClassTest(Shrinker shrinker, boolean keepPrecondition) {
this.shrinker = shrinker;
this.keepPrecondition = keepPrecondition;
}
@Parameters(name = "shrinker: {0} precondition: {1}")
public static Collection<Object[]> data() {
return ImmutableList.of(
new Object[] {Shrinker.PROGUARD6, true},
new Object[] {Shrinker.PROGUARD6, false},
new Object[] {Shrinker.R8, true},
new Object[] {Shrinker.R8, false},
new Object[] {Shrinker.R8_CF, true},
new Object[] {Shrinker.R8_CF, false});
}
private String adaptConfiguration(String proguardConfig) {
List<String> configWithPrecondition = new ArrayList<>();
configWithPrecondition.add(
keepMainProguardConfiguration(EmptyMainClassForIfOnClassTests.class));
if (keepPrecondition) {
configWithPrecondition.add("-keep class **.Precondition");
}
configWithPrecondition.add(proguardConfig);
return String.join(System.lineSeparator(), configWithPrecondition);
}
@Override
protected CodeInspector inspectR8Result(
List<Class<?>> programClasses,
String proguardConfig,
Consumer<InternalOptions> configure,
Backend backend)
throws Exception {
return super.inspectR8Result(
programClasses, adaptConfiguration(proguardConfig), configure, backend);
}
@Override
protected CodeInspector inspectProguard5Result(
List<Class<?>> programClasses, String proguardConfig) throws Exception {
return super.inspectProguard5Result(programClasses, adaptConfiguration(proguardConfig));
}
@Override
protected CodeInspector inspectProguard6Result(
List<Class<?>> programClasses, String proguardConfig) throws Exception {
return super.inspectProguard6Result(programClasses, adaptConfiguration(proguardConfig));
}
@Test
public void ifThenKeep_withoutNthWildcard() throws Exception {
List<String> config = ImmutableList.of(
"-if class **.Precondition",
"-keep,allowobfuscation class **.*User",
"-if class **.DependentUser",
"-keep,allowobfuscation class **.Dependent"
);
CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config);
if (!keepPrecondition) {
// TODO(b/73708139): Proguard6 kept Dependent (w/o any members), which is not necessary.
if (shrinker == Shrinker.PROGUARD6) {
return;
}
assertEquals(1, codeInspector.allClasses().size());
return;
}
ClassSubject clazz = codeInspector.clazz(DependentUser.class);
assertThat(clazz, isRenamed());
// Members of DependentUser are not used anywhere.
MethodSubject m = clazz.method("void", "callFoo", ImmutableList.of());
assertThat(m, not(isPresent()));
FieldSubject f = clazz.field("int", "canBeShrinked");
assertThat(f, not(isPresent()));
// Although DependentUser#callFoo is shrinked, Dependent is kept via -if.
clazz = codeInspector.clazz(Dependent.class);
assertThat(clazz, isRenamed());
// But, its members are gone.
m = clazz.method("java.lang.String", "foo", ImmutableList.of());
assertThat(m, not(isPresent()));
f = clazz.field("int", "intField");
assertThat(f, not(isPresent()));
}
@Test
public void ifThenKeep_withNthWildcard() throws Exception {
List<String> config = ImmutableList.of(
"-if class **.Precondition",
"-keep,allowobfuscation class **.*User",
"-if class **.*User",
"-keep,allowobfuscation class <1>.<2>"
);
CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config);
if (!keepPrecondition) {
// TODO(b/73708139): Proguard6 kept Dependent (w/o any members), which is not necessary.
if (shrinker == Shrinker.PROGUARD6) {
return;
}
assertEquals(1, codeInspector.allClasses().size());
return;
}
ClassSubject clazz = codeInspector.clazz(DependentUser.class);
assertThat(clazz, isRenamed());
// Members of DependentUser are not used anywhere.
MethodSubject m = clazz.method("void", "callFoo", ImmutableList.of());
assertThat(m, not(isPresent()));
FieldSubject f = clazz.field("int", "canBeShrinked");
assertThat(f, not(isPresent()));
// Although DependentUser#callFoo is shrinked, Dependent is kept via -if.
clazz = codeInspector.clazz(Dependent.class);
assertThat(clazz, isRenamed());
// But, its members are gone.
m = clazz.method("java.lang.String", "foo", ImmutableList.of());
assertThat(m, not(isPresent()));
f = clazz.field("int", "intField");
assertThat(f, not(isPresent()));
}
@Test
public void ifThenKeepClassesWithMembers() throws Exception {
List<String> config = ImmutableList.of(
"-if class **.Precondition",
"-keepclasseswithmembers,allowobfuscation class **.*User {",
" static void callFoo(...);",
"}"
);
CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config);
if (!keepPrecondition) {
assertEquals(1, codeInspector.allClasses().size());
return;
}
ClassSubject clazz = codeInspector.clazz(DependentUser.class);
assertThat(clazz, isRenamed());
MethodSubject m = clazz.method("void", "callFoo", ImmutableList.of());
assertThat(m, isRenamed());
FieldSubject f = clazz.field("int", "canBeShrinked");
assertThat(f, not(isPresent()));
// Dependent is kept due to DependentUser#callFoo, but renamed.
clazz = codeInspector.clazz(Dependent.class);
assertThat(clazz, isRenamed());
m = clazz.method("java.lang.String", "foo", ImmutableList.of());
assertThat(m, isRenamed());
f = clazz.field("int", "intField");
assertThat(f, isRenamed());
}
@Test
public void ifThenKeepClassMembers_withoutNthWildcard() throws Exception {
List<String> config = ImmutableList.of(
"-if class **.Precondition",
"-keepclassmembers,allowobfuscation class **.*User {",
" static void callFoo(...);",
"}",
// If the member is kept, keep the enclosing class too.
"-if class **.*User {",
" static void callFoo(...);",
"}",
"-keep,allowobfuscation class **.*User"
);
CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config);
if (!keepPrecondition) {
// TODO(b/73708139): Proguard6 kept DependentUser (w/o any members), which is not necessary.
if (shrinker == Shrinker.PROGUARD6) {
return;
}
assertEquals(1, codeInspector.allClasses().size());
return;
}
ClassSubject clazz = codeInspector.clazz(DependentUser.class);
assertThat(clazz, isRenamed());
MethodSubject m = clazz.method("void", "callFoo", ImmutableList.of());
assertThat(m, isRenamed());
FieldSubject f = clazz.field("int", "canBeShrinked");
assertThat(f, not(isPresent()));
// Dependent is kept due to DependentUser#callFoo, but renamed.
clazz = codeInspector.clazz(Dependent.class);
assertThat(clazz, isRenamed());
m = clazz.method("java.lang.String", "foo", ImmutableList.of());
assertThat(m, isRenamed());
f = clazz.field("int", "intField");
assertThat(f, isRenamed());
}
@Test
public void ifThenKeepClassMembers_withNthWildcard() throws Exception {
List<String> config = ImmutableList.of(
"-if class **.Precondition",
"-keepclassmembers,allowobfuscation class **.*User {",
" static void callFoo(...);",
"}",
// If the member is kept, keep the enclosing class too.
"-if class **.*User {",
" static void callFoo(...);",
"}",
"-keep,allowobfuscation class <1>.<2>User"
);
CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config);
if (!keepPrecondition) {
// TODO(b/73708139): Proguard6 kept DependentUser (w/o any members), which is not necessary.
if (shrinker == Shrinker.PROGUARD6) {
return;
}
assertEquals(1, codeInspector.allClasses().size());
return;
}
if (shrinker.isR8()) {
// The -keepclassmembers rule should not keep Dependent nor DependentUser since DependentUser
// is never referenced.
assertThat(codeInspector.clazz(EmptyMainClassForIfOnClassTests.class), isPresent());
assertThat(codeInspector.clazz(Precondition.class), isPresent());
assertEquals(2, codeInspector.allClasses().size());
return;
}
ClassSubject clazz = codeInspector.clazz(DependentUser.class);
assertThat(clazz, isRenamed());
MethodSubject m = clazz.method("void", "callFoo", ImmutableList.of());
assertThat(m, isRenamed());
FieldSubject f = clazz.field("int", "canBeShrinked");
assertThat(f, not(isPresent()));
// Dependent is kept due to DependentUser#callFoo, but renamed.
clazz = codeInspector.clazz(Dependent.class);
assertThat(clazz, isRenamed());
m = clazz.method("java.lang.String", "foo", ImmutableList.of());
assertThat(m, isRenamed());
f = clazz.field("int", "intField");
assertThat(f, isRenamed());
}
@Test
public void ifThenKeepNames() throws Exception {
List<String> config = ImmutableList.of(
// To keep DependentUser#callFoo, which in turn kept Dependent#<init> as well.
// We're testing renaming of Dependent itself and members.
"-keepclasseswithmembers,allowobfuscation class **.*User {",
" static void callFoo(...);",
"}",
"-if class **.Precondition",
"-keepnames class **.Dependent"
);
CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config);
ClassSubject clazz = codeInspector.clazz(Dependent.class);
// Only class name is not renamed, if triggered.
assertThat(clazz, keepPrecondition ? isNotRenamed() : isRenamed());
MethodSubject m = clazz.method("java.lang.String", "foo", ImmutableList.of());
assertThat(m, isRenamed());
FieldSubject f = clazz.field("int", "intField");
assertThat(f, isRenamed());
}
@Test
public void ifThenKeepClassesWithMemberNames() throws Exception {
List<String> config = ImmutableList.of(
// To keep DependentUser#callFoo, which in turn kept Dependent#<init> as well.
// We're testing renaming of Dependent itself and members.
"-keepclasseswithmembers,allowobfuscation class **.*User {",
" static void callFoo(...);",
"}",
"-if class **.Precondition",
"-keepclasseswithmembernames class **.Dependent {",
" <methods>;",
"}"
);
CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config);
ClassSubject clazz = codeInspector.clazz(Dependent.class);
// Class name is not renamed, if triggered.
assertThat(clazz, keepPrecondition ? isNotRenamed() : isRenamed());
MethodSubject m = clazz.method("java.lang.String", "foo", ImmutableList.of());
// Method name is not renamed either, if triggered.
assertThat(m, keepPrecondition ? isNotRenamed() : isRenamed());
FieldSubject f = clazz.field("int", "intField");
assertThat(f, isRenamed());
}
@Test
public void ifThenKeepClassMemberNames() throws Exception {
List<String> config = ImmutableList.of(
// To keep DependentUser#callFoo, which in turn kept Dependent#<init> as well.
// We're testing renaming of Dependent itself and members.
"-keepclasseswithmembers,allowobfuscation class **.*User {",
" static void callFoo(...);",
"}",
"-if class **.Precondition",
"-keepclassmembernames class **.Dependent {",
" <methods>;",
"}"
);
CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config);
ClassSubject clazz = codeInspector.clazz(Dependent.class);
assertThat(clazz, isRenamed());
MethodSubject m = clazz.method("java.lang.String", "foo", ImmutableList.of());
// Only method name is not renamed, if triggered.
assertThat(m, keepPrecondition ? isNotRenamed() : isRenamed());
FieldSubject f = clazz.field("int", "intField");
assertThat(f, isRenamed());
}
}