blob: 609465f6d74c4c2166de5e3abab06fdd617c33de [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.applymapping;
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 com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import org.junit.Assume;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class ApplyMappingAfterVerticalMergingMethodTest extends TestBase {
private static final String OUTPUT = "LibraryBase::foo";
private static final String EXPECTED_SUCCESS = StringUtils.lines(OUTPUT);
// Base class will be vertical class merged into subclass
public static class LibraryBase {
@NeverPropagateValue
@NeverInline
public String foo() {
return OUTPUT;
}
}
// Subclass targeted via vertical class merging. The main method ensures a reference to foo.
public static class LibrarySubclass extends LibraryBase {
public static void main(String[] args) {
System.out.println(new LibrarySubclass().foo());
}
}
// Program class that uses LibrarySubclass and accesses foo via its main.
public static class ProgramClass extends LibrarySubclass {
public static void main(String[] args) {
LibrarySubclass.main(args);
}
}
// Test runner code follows.
// Result of the shared compilation.
private static class CompilationResult {
final R8TestCompileResult library;
final R8TestCompileResult program;
final Path libraryPath;
public CompilationResult(
R8TestCompileResult library, R8TestCompileResult program, Path libraryPath) {
this.library = library;
this.program = program;
this.libraryPath = libraryPath;
}
}
private static final Class<?>[] LIBRARY_CLASSES = {LibraryBase.class, LibrarySubclass.class};
private static final Class<?>[] PROGRAM_CLASSES = {
ProgramClass.class
};
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimes().build();
}
private static Function<Backend, CompilationResult> compilationResults =
memoizeFunction(ApplyMappingAfterVerticalMergingMethodTest::compile);
@ClassRule
public static TemporaryFolder staticTemp = ToolHelper.getTemporaryFolderForTest();
public static CompilationResult compile(Backend backend)
throws ExecutionException, CompilationFailedException, IOException {
R8TestCompileResult library = compileLibrary(backend);
R8TestCompileResult program = compileProgram(backend, library.getProguardMap());
return new CompilationResult(library, program, library.writeToZip());
}
private static R8TestCompileResult compileLibrary(Backend backend)
throws CompilationFailedException, IOException {
return testForR8(staticTemp, backend)
.enableInliningAnnotations()
.enableMemberValuePropagationAnnotations()
.addProgramClasses(LIBRARY_CLASSES)
.addKeepMainRule(LibrarySubclass.class)
.addKeepClassAndDefaultConstructor(LibrarySubclass.class)
.addVerticallyMergedClassesInspector(
inspector -> inspector.assertMergedIntoSubtype(LibraryBase.class))
.setMinApi(AndroidApiLevel.B)
.compile()
.inspect(
inspector -> {
assertThat(inspector.clazz(LibraryBase.class), not(isPresent()));
assertThat(inspector.clazz(LibrarySubclass.class), isPresent());
List<FoundMethodSubject> methods =
inspector.clazz(LibrarySubclass.class).allMethods();
assertEquals(4, methods.size());
assertEquals(
1, methods.stream().filter(FoundMethodSubject::isInstanceInitializer).count());
assertEquals(
1, methods.stream().filter(m -> m.getFinalName().contains("main")).count());
assertEquals(
2, methods.stream().filter(m -> m.getOriginalName().contains("foo")).count());
});
}
private static R8TestCompileResult compileProgram(Backend backend, String proguardMap)
throws CompilationFailedException {
return testForR8(staticTemp, backend)
.noTreeShaking()
.addDontObfuscate()
.addProgramClasses(PROGRAM_CLASSES)
.addApplyMapping(proguardMap)
.addLibraryClasses(LIBRARY_CLASSES)
.addLibraryFiles(runtimeJar(backend))
.setMinApi(AndroidApiLevel.B)
.compile();
}
private TestParameters parameters;
public ApplyMappingAfterVerticalMergingMethodTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void runOnJvm() throws Throwable {
Assume.assumeTrue(parameters.isCfRuntime());
testForJvm()
.addProgramClasses(LIBRARY_CLASSES)
.addProgramClasses(PROGRAM_CLASSES)
.run(parameters.getRuntime(), ProgramClass.class)
.assertSuccessWithOutput(EXPECTED_SUCCESS);
}
@Test
public void b121042934() throws Exception {
CompilationResult compilationResult = compilationResults.apply(parameters.getBackend());
compilationResult
.program
.addRunClasspathFiles(compilationResult.libraryPath)
.run(parameters.getRuntime(), ProgramClass.class)
.assertSuccessWithOutput(EXPECTED_SUCCESS);
}
}