| // Copyright (c) 2020, 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.cf; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertTrue; |
| |
| import com.android.tools.r8.NeverClassInline; |
| import com.android.tools.r8.NeverInline; |
| import com.android.tools.r8.TestBase; |
| import com.android.tools.r8.TestParameters; |
| import com.android.tools.r8.utils.StringUtils; |
| import com.android.tools.r8.utils.codeinspector.CodeInspector; |
| import java.io.IOException; |
| import java.util.List; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| |
| @RunWith(Parameterized.class) |
| public class GetClassLdcClassTest extends TestBase { |
| |
| static final String EXPECTED = StringUtils.lines(Runner.class.getName()); |
| |
| private final TestParameters parameters; |
| private final CfVersion version; |
| |
| @Parameterized.Parameters(name = "{0}, cf:{1}") |
| public static List<Object[]> data() { |
| return buildParameters( |
| getTestParameters().withAllRuntimesAndApiLevels().build(), |
| new CfVersion[] {CfVersion.V1_1, CfVersion.V1_4, CfVersion.V1_5}); |
| } |
| |
| public GetClassLdcClassTest(TestParameters parameters, CfVersion version) { |
| this.parameters = parameters; |
| this.version = version; |
| } |
| |
| @Test |
| public void testReference() throws Exception { |
| // Check the program works with the code as-is and the version downgraded. |
| testForRuntime(parameters) |
| .addProgramClassFileData(getDowngradedClass(Runner.class)) |
| .addProgramClassFileData(getDowngradedClass(TestClass.class)) |
| .run(parameters.getRuntime(), TestClass.class) |
| .assertSuccessWithOutput(EXPECTED) |
| .inspect( |
| inspector -> { |
| if (parameters.isCfRuntime()) { |
| checkVersion(inspector, TestClass.class, version); |
| checkVersion(inspector, Runner.class, version); |
| } |
| }); |
| } |
| |
| @Test |
| public void testNoVersionUpgrade() throws Exception { |
| testForR8(parameters.getBackend()) |
| .addProgramClassFileData(getDowngradedClass(Runner.class)) |
| .addProgramClassFileData(getDowngradedClass(TestClass.class)) |
| .setMinApi(parameters.getApiLevel()) |
| .addKeepMainRule(TestClass.class) |
| // We cannot keep class Runner, as that prohibits getClass optimization. |
| // Instead disable minification and inlining of the Runner class and method. |
| .noMinification() |
| .enableInliningAnnotations() |
| .enableNeverClassInliningAnnotations() |
| .run(parameters.getRuntime(), TestClass.class) |
| .assertSuccessWithOutput(EXPECTED) |
| .inspect( |
| inspector -> { |
| if (parameters.isCfRuntime()) { |
| checkVersion(inspector, TestClass.class, version); |
| checkVersion(inspector, Runner.class, version); |
| } |
| }); |
| } |
| |
| @Test |
| public void testWithVersionUpgrade() throws Exception { |
| testForR8(parameters.getBackend()) |
| .addProgramClassFileData(getDowngradedClass(Runner.class)) |
| // Here the main class is not downgraded, thus the output may upgrade to that version. |
| .addProgramClasses(TestClass.class) |
| .setMinApi(parameters.getApiLevel()) |
| .addKeepMainRule(TestClass.class) |
| // We cannot keep class Runner, as that prohibits getClass optimization. |
| // Instead disable minification and inlining of the Runner class and method. |
| .noMinification() |
| .enableInliningAnnotations() |
| .enableNeverClassInliningAnnotations() |
| .run(parameters.getRuntime(), TestClass.class) |
| .assertSuccessWithOutput(EXPECTED) |
| .inspect( |
| inspector -> { |
| if (parameters.isCfRuntime()) { |
| // We are assuming the runtimes we are testing are post CF SE 1.4 (version 48). |
| CfVersion cfVersionForRuntime = getVersion(inspector, TestClass.class); |
| assertTrue(CfVersion.V1_4.isLessThan(cfVersionForRuntime)); |
| // Check that the downgraded class has been bumped to at least SE 1.5 (version 49). |
| CfVersion cfVersionAfterUpgrade = getVersion(inspector, Runner.class); |
| assertTrue(CfVersion.V1_4.isLessThan(cfVersionAfterUpgrade)); |
| } |
| // Check that the method uses a const class instruction. |
| assertTrue( |
| inspector |
| .clazz(Runner.class) |
| .uniqueMethodWithName("run") |
| .streamInstructions() |
| .anyMatch(i -> i.isConstClass(Runner.class.getTypeName()))); |
| }); |
| } |
| |
| private static CfVersion getVersion(CodeInspector inspector, Class<?> clazz) { |
| return inspector.clazz(clazz).getDexProgramClass().getInitialClassFileVersion(); |
| } |
| |
| private static void checkVersion(CodeInspector inspector, Class<?> clazz, CfVersion version) { |
| assertEquals(version, getVersion(inspector, clazz)); |
| } |
| |
| private byte[] getDowngradedClass(Class<?> clazz) throws IOException { |
| return transformer(clazz).setVersion(version).transform(); |
| } |
| |
| @NeverClassInline |
| static class Runner { |
| |
| @NeverInline |
| public void run() { |
| System.out.println(getClass().getName()); |
| } |
| } |
| |
| static class TestClass { |
| |
| public static void main(String[] args) { |
| new Runner().run(); |
| } |
| } |
| } |