blob: c82a4a45c27dd39572fec1e7aebb7c905a91a118 [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.kotlin;
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.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.BooleanUtils;
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.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class KotlinUnusedSingletonTest extends AbstractR8KotlinTestBase {
@Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}")
public static Collection<Object[]> data() {
return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values());
}
private static final String printlnSignature =
"void java.io.PrintStream.println(java.lang.Object)";
public KotlinUnusedSingletonTest(
KotlinTargetVersion targetVersion, boolean allowAccessModification) {
super(targetVersion, allowAccessModification);
}
@Test
public void b110196118() throws Exception {
final String mainClassName = "unused_singleton.MainKt";
final String moduleName = "unused_singleton.TestModule";
runTest(
"unused_singleton",
mainClassName,
"-dontobfuscate",
app -> {
CodeInspector inspector = new CodeInspector(app);
ClassSubject main = inspector.clazz(mainClassName);
assertThat(main, isPresent());
MethodSubject mainMethod = main.mainMethod();
assertThat(mainMethod, isPresent());
// The const-string from provideGreeting() has been propagated.
assertTrue(
mainMethod
.iterateInstructions(i -> i.isConstString("Hello", JumboStringMode.ALLOW))
.hasNext());
// The method provideGreeting() is no longer being invoked -- i.e., we have been able to
// determine that the class initialization of the enclosing class is trivial.
ClassSubject module = inspector.clazz(moduleName);
assertThat(main, isPresent());
assertEquals(
0,
mainMethod
.streamInstructions()
.filter(InstructionSubject::isInvoke)
.map(i -> i.getMethod().toSourceString())
.filter(
invokedMethod ->
!invokedMethod.equals(checkParameterIsNotNullSignature)
&& !invokedMethod.equals(printlnSignature)
&& !invokedMethod.equals(throwParameterIsNotNullExceptionSignature))
.count());
// The field `INSTANCE` has been removed entirely.
FieldSubject instance = module.uniqueFieldWithName("INSTANCE");
assertThat(instance, not(isPresent()));
// The class initializer is no longer there.
MethodSubject clinit = module.clinit();
assertThat(clinit, not(isPresent()));
// Also, the instance initializer is no longer there, since it is only reachable from the
// class initializer.
MethodSubject init = module.init(ImmutableList.of());
assertThat(init, not(isPresent()));
});
}
}