| // 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.accessrelaxation; |
| |
| import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; |
| import static org.hamcrest.MatcherAssert.assertThat; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertTrue; |
| |
| import com.android.tools.r8.R8TestRunResult; |
| import com.android.tools.r8.TestParameters; |
| import com.android.tools.r8.TestParametersCollection; |
| import com.android.tools.r8.utils.StringUtils; |
| import com.android.tools.r8.utils.codeinspector.ClassSubject; |
| import com.android.tools.r8.utils.codeinspector.CodeInspector; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| |
| class L1 { |
| private final String x; |
| |
| private L1(String x) { |
| this.x = x; |
| } |
| |
| private L1() { |
| this("private_x"); |
| } |
| |
| static L1 create() { |
| return new L1(); |
| } |
| |
| L1(int i) { |
| this(String.valueOf(i)); |
| } |
| |
| @Override |
| public String toString() { |
| return x; |
| } |
| } |
| |
| class L2_1 extends L1 { |
| private String y; |
| |
| private L2_1() { |
| this(21); |
| this.y = "private_L2_1_y"; |
| } |
| |
| L2_1(int i) { |
| super(i); |
| this.y = "L2_1_y"; |
| } |
| |
| private L2_1(String y) { |
| this(21); |
| this.y = y; |
| } |
| |
| static L2_1 create(String y) { |
| return new L2_1(y); |
| } |
| |
| @Override |
| public String toString() { |
| return super.toString() + "_" + y; |
| } |
| } |
| |
| class L2_2 extends L1 { |
| private String y; |
| |
| private L2_2(int i) { |
| super(i); |
| this.y = "private_L2_2_y"; |
| } |
| |
| L2_2(String y) { |
| this(22); |
| this.y = y; |
| } |
| |
| static L2_1 create() { |
| return new L2_1(22); |
| } |
| |
| @Override |
| public String toString() { |
| return super.toString() + "_" + y; |
| } |
| } |
| |
| class L3_1 extends L2_1 { |
| private final String z; |
| |
| private L3_1(int i) { |
| this(String.valueOf(i)); |
| } |
| |
| private L3_1(String z) { |
| super(31); |
| this.z = z; |
| } |
| |
| static L3_1 create(int i) { |
| return new L3_1(i); |
| } |
| |
| @Override |
| public String toString() { |
| return super.toString() + "_" + z; |
| } |
| } |
| |
| class L3_2 extends L2_2 { |
| private String z; |
| |
| private L3_2() { |
| super("private_L3_2_y"); |
| this.z = "private_L3_2_z"; |
| } |
| |
| private L3_2(int i) { |
| super(String.valueOf(i)); |
| this.z = "private_L3_2_z" + "_" + i; |
| } |
| |
| L3_2(String z) { |
| this(32); |
| this.z = z; |
| } |
| |
| static L3_2 create(String z) { |
| return new L3_2(z); |
| } |
| |
| @Override |
| public String toString() { |
| return super.toString() + "_" + z; |
| } |
| } |
| |
| class CtorTestMain { |
| public static void main(String[] args) { |
| System.out.println(L1.create()); |
| System.out.println(L2_1.create("main_y")); |
| System.out.println(L2_2.create()); |
| System.out.println(L3_1.create(41)); |
| System.out.println(L3_2.create("main_z")); |
| } |
| } |
| |
| @RunWith(Parameterized.class) |
| public final class ConstructorRelaxationTest extends AccessRelaxationTestBase { |
| private static final Class<?>[] CLASSES = { |
| L1.class, L2_1.class, L2_2.class, L3_1.class, L3_2.class |
| }; |
| |
| @Parameterized.Parameters(name = "Backend: {0}") |
| public static TestParametersCollection data() { |
| return getTestParameters().withAllRuntimes().build(); |
| } |
| |
| public ConstructorRelaxationTest(TestParameters parameters) { |
| super(parameters); |
| } |
| |
| @Test |
| public void test() throws Exception { |
| String expectedOutput = |
| StringUtils.lines( |
| "private_x", |
| "21_main_y", |
| "22_L2_1_y", |
| "31_L2_1_y_41", |
| "22_32_main_z"); |
| Class mainClass = CtorTestMain.class; |
| |
| R8TestRunResult result = |
| testForR8(parameters.getBackend()) |
| .addProgramClasses(mainClass) |
| .addProgramClasses(CLASSES) |
| .addOptionsModification( |
| o -> { |
| o.enableInlining = false; |
| o.enableVerticalClassMerging = false; |
| }) |
| .noMinification() |
| .addKeepMainRule(mainClass) |
| .allowAccessModification() |
| .setMinApi(parameters.getRuntime()) |
| .run(parameters.getRuntime(), mainClass); |
| |
| assertEquals( |
| expectedOutput, |
| result |
| .getStdOut() |
| .replace("java.lang.IncompatibleClassChangeError", "java.lang.IllegalAccessError")); |
| |
| CodeInspector codeInspector = result.inspector(); |
| for (Class clazz : CLASSES) { |
| ClassSubject classSubject = codeInspector.clazz(clazz); |
| assertThat(classSubject, isPresent()); |
| classSubject |
| .getDexProgramClass() |
| .forEachMethod( |
| m -> { |
| assertTrue(!m.isInstanceInitializer() || m.isPublicMethod()); |
| }); |
| } |
| } |
| |
| } |