blob: 23271e2b7105b4d48b056689dc23e292b082d35d [file] [log] [blame]
// 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.naming.b130791310;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.ProguardVersion;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
class TestClass {
SomeLogic instance;
public void create() {
instance = new SomeLogic(new Object());
}
void caller() {
if (instance.someMethod(new SomeClass()) == null) {
System.out.println("TestMain#caller");
}
}
public static void main(String[] args) {
TestClass m = new TestClass();
m.create();
m.caller();
}
}
class SomeLogic {
Object otherField;
public SomeLogic(Object o) {
otherField = o;
}
public SomeInterface someMethod(SomeClass list) {
System.out.println("list is" + ((list == null) ? "" : " not") + " null");
return null;
}
}
interface SomeInterface {
byte foo();
}
@NeverClassInline
class SomeClass implements SomeInterface {
@Override
public byte foo() {
return 0x8;
}
}
// SomeInterface has a single implementer SomeClass: vertical merging candidate.
// If -keepnames specifies a member whose signature can be changed due to vertical merging, their
// names are not preserved because that rule allows shrinking (and optimization) implicitly.
// According to b/130791310, Proguard (via AGP) still keeps that name, but this reproduction shows
// that both R8 and Proguard ignores -keepnames. It turns out that, as per
// https://android.googlesource.com/platform/sdk/+/master/files/proguard-android-optimize.txt#13
// class merging is disabled for Proguard.
@RunWith(Parameterized.class)
public class B130791310 extends TestBase {
private static final Class<?> MAIN = TestClass.class;
private static final List<Class<?>> CLASSES =
ImmutableList.of(MAIN, SomeLogic.class, SomeInterface.class, SomeClass.class);
private static final String RULES = StringUtils.lines(
"-keepnames class **.SomeLogic {",
" **.SomeInterface someMethod(**.SomeClass);",
"}"
);
private final boolean enableClassMerging;
private final boolean onlyForceInlining;
private final TestParameters parameters;
@Parameterized.Parameters(name = "{2}, enable class merging: {0}, only force inlining: {1}")
public static List<Object[]> data() {
return buildParameters(
BooleanUtils.values(),
BooleanUtils.values(),
getTestParameters().withAllRuntimesAndApiLevels().build());
}
public B130791310(
boolean enableClassMerging, boolean onlyForceInlining, TestParameters parameters) {
this.enableClassMerging = enableClassMerging;
this.onlyForceInlining = onlyForceInlining;
this.parameters = parameters;
}
private void inspect(CodeInspector inspector) {
ClassSubject holder = inspector.clazz(SomeLogic.class);
assertThat(holder, isPresentAndNotRenamed());
MethodSubject someMethod = holder.uniqueMethodWithName("someMethod");
assertThat(someMethod, isPresentAndNotRenamed());
}
@Test
public void testProguard() throws Exception {
assumeFalse(onlyForceInlining);
assumeTrue(parameters.isCfRuntime());
testForProguard(ProguardVersion.getLatest())
.addProgramClasses(CLASSES)
.addKeepClassAndMembersRules(MAIN)
.addKeepRules(RULES)
.addTestingAnnotationsAsProgramClasses()
.setMinApi(parameters.getApiLevel())
.applyIf(
!enableClassMerging, builder -> builder.addKeepRules("-optimizations !class/merging/*"))
.compile()
.inspect(this::inspect);
}
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
.addProgramClasses(CLASSES)
.addKeepClassAndMembersRules(MAIN)
.addKeepRules(RULES)
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.addOptionsModification(o -> o.enableVerticalClassMerging = enableClassMerging)
.applyIf(
onlyForceInlining,
builder ->
builder.addOptionsModification(
o -> o.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE)))
.compile()
.inspect(this::inspect);
}
}