| // 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.naming; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assume.assumeTrue; |
| |
| import com.android.tools.r8.ByteDataView; |
| import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer; |
| 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.ToolHelper.ProcessResult; |
| import com.android.tools.r8.utils.DescriptorUtils; |
| import com.android.tools.r8.utils.FileUtils; |
| import com.android.tools.r8.utils.StringUtils; |
| import java.io.IOException; |
| import java.nio.file.Path; |
| import org.junit.Before; |
| 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 LambdaRenamingTestRunner extends TestBase { |
| |
| private static final Class<?> CLASS = LambdaRenamingTest.class; |
| private static final Class<?>[] CLASSES = LambdaRenamingTest.CLASSES; |
| private static final String EXPECTED = |
| StringUtils.lines( |
| "null", "null", "null", "null", "10", "null", "null", "null", "null", "11", "10", "30", |
| "10", "30", "101", "301", "101", "301", "102", "302", "102", "302"); |
| |
| private final TestParameters parameters; |
| |
| private Path inputJar; |
| |
| @Parameters(name = "{0}") |
| public static TestParametersCollection data() { |
| return getTestParameters().withAllRuntimesAndApiLevels().build(); |
| } |
| |
| public LambdaRenamingTestRunner(TestParameters parameters) { |
| this.parameters = parameters; |
| } |
| |
| @Before |
| public void writeAndRunInputJar() throws IOException { |
| inputJar = temp.getRoot().toPath().resolve("input.jar"); |
| ArchiveConsumer buildInput = new ArchiveConsumer(inputJar); |
| for (Class<?> clazz : CLASSES) { |
| buildInput.accept( |
| ByteDataView.of(ToolHelper.getClassAsBytes(clazz)), |
| DescriptorUtils.javaTypeToDescriptor(clazz.getName()), |
| null); |
| } |
| buildInput.finished(null); |
| } |
| |
| @Test |
| public void testProguard() throws Exception { |
| assumeTrue(parameters.isCfRuntime()); |
| buildAndRunProguard("pg.jar", false); |
| } |
| |
| @Test |
| public void testProguardAggressive() throws Exception { |
| assumeTrue(parameters.isCfRuntime()); |
| buildAndRunProguard("pg-aggressive.jar", true); |
| } |
| |
| @Test |
| public void testR8() throws Exception { |
| testR8(false); |
| } |
| |
| @Test |
| public void testR8Aggressive() throws Exception { |
| testR8(true); |
| } |
| |
| private void testR8(boolean aggressive) throws Exception { |
| testForR8(parameters.getBackend()) |
| .addProgramFiles(inputJar) |
| .addKeepMainRule(CLASS) |
| .addKeepRules( |
| "-keep interface " + CLASS.getCanonicalName() + "$ReservedNameObjectInterface1 {", |
| " public java.lang.Object reservedMethod1();", |
| "}", |
| "-keep interface " + CLASS.getCanonicalName() + "$ReservedNameIntegerInterface2 {", |
| " public java.lang.Integer reservedMethod2();", |
| "}") |
| .applyIf(aggressive, builder -> builder.addKeepRules("-overloadaggressively")) |
| .debug() |
| .setMinApi(parameters.getApiLevel()) |
| .compile() |
| .apply( |
| compileResult -> |
| compileResult.run(parameters.getRuntime(), CLASS).assertSuccessWithOutput(EXPECTED)) |
| .applyIf( |
| parameters.isDexRuntime(), |
| compileResult -> |
| compileResult.runDex2Oat(parameters.getRuntime()).assertNoVerificationErrors()); |
| } |
| |
| private void buildAndRunProguard(String outName, boolean aggressive) throws Exception { |
| Path pgConfig = writeProguardRules(aggressive); |
| Path outPg = temp.getRoot().toPath().resolve(outName); |
| ProcessResult proguardResult = |
| ToolHelper.runProguard6Raw( |
| inputJar, outPg, ToolHelper.getJava8RuntimeJar(), pgConfig, null); |
| System.out.println(proguardResult.stdout); |
| if (proguardResult.exitCode != 0) { |
| System.out.println(proguardResult.stderr); |
| } |
| assertEquals(0, proguardResult.exitCode); |
| ProcessResult runPg = ToolHelper.runJava(outPg, CLASS.getCanonicalName()); |
| // Proguard renames IntegerInterface.inexactMethod() and ObjectInterface.inexactMethod() |
| // to different names, which causes AbstractMethodError. |
| assertNotEquals(-1, runPg.stderr.indexOf("AbstractMethodError")); |
| assertNotEquals(0, runPg.exitCode); |
| } |
| |
| private Path writeProguardRules(boolean aggressive) throws IOException { |
| Path pgConfig = temp.getRoot().toPath().resolve("keep.txt"); |
| FileUtils.writeTextFile( |
| pgConfig, |
| "-keep public class " + CLASS.getCanonicalName() + " {", |
| " public static void main(...);", |
| "}", |
| "-keep interface " + CLASS.getCanonicalName() + "$ReservedNameObjectInterface1 {", |
| " public java.lang.Object reservedMethod1();", |
| "}", |
| "-keep interface " + CLASS.getCanonicalName() + "$ReservedNameIntegerInterface2 {", |
| " public java.lang.Integer reservedMethod2();", |
| "}", |
| aggressive ? "-overloadaggressively" : "# Not overloading aggressively"); |
| return pgConfig; |
| } |
| } |