| // Copyright (c) 2024, 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.horizontalclassmerging; |
| |
| import com.android.tools.r8.TestBase; |
| import com.android.tools.r8.TestParameters; |
| import com.android.tools.r8.TestParametersCollection; |
| import com.android.tools.r8.horizontalclassmerging.ConditionalMethodRulesAndHorizontalMergingTest.Example1.BaseClass; |
| import com.android.tools.r8.horizontalclassmerging.ConditionalMethodRulesAndHorizontalMergingTest.Example1.MyHiddenMethodCaller; |
| import com.android.tools.r8.horizontalclassmerging.ConditionalMethodRulesAndHorizontalMergingTest.Example2.MyFieldValuePrinter; |
| import com.android.tools.r8.horizontalclassmerging.ConditionalMethodRulesAndHorizontalMergingTest.Example2.PrintableFieldInterface; |
| import com.android.tools.r8.utils.AndroidApiLevel; |
| import com.android.tools.r8.utils.StringUtils; |
| import com.google.common.collect.ImmutableList; |
| import java.lang.reflect.Field; |
| import java.util.List; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| |
| @RunWith(Parameterized.class) |
| public class ConditionalMethodRulesAndHorizontalMergingTest extends TestBase { |
| |
| static final String EXPECTED = |
| StringUtils.lines("on Base", "on Sub", "intField = 42", "stringField = Hello!"); |
| |
| private final TestParameters parameters; |
| |
| @Parameterized.Parameters(name = "{0}") |
| public static TestParametersCollection data() { |
| return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build(); |
| } |
| |
| public ConditionalMethodRulesAndHorizontalMergingTest(TestParameters parameters) { |
| this.parameters = parameters; |
| } |
| |
| @Test |
| public void testReference() throws Exception { |
| testForRuntime(parameters) |
| .addProgramClasses(TestClass.class) |
| .addProgramClassesAndInnerClasses(getExampleClasses()) |
| .run(parameters.getRuntime(), TestClass.class) |
| .assertSuccessWithOutput(EXPECTED); |
| } |
| |
| @Test |
| public void testR8() throws Exception { |
| testForR8(parameters.getBackend()) |
| .addKeepMainRule(TestClass.class) |
| // The following rules are what was generated by the keepanno extraction for the doc tests. |
| .addKeepRules( |
| "-if class " + typeName(MyHiddenMethodCaller.class) + " {", |
| " void callHiddenMethod(" + typeName(BaseClass.class) + ");", |
| "}", |
| "-keepclassmembers class * extends " + typeName(BaseClass.class) + " {", |
| " *** hiddenMethod();", |
| "}", |
| "-if class " + typeName(MyHiddenMethodCaller.class) + " {", |
| " void callHiddenMethod(" + typeName(BaseClass.class) + ");", |
| "}", |
| "-keepclassmembers class " + typeName(BaseClass.class) + " {", |
| " *** hiddenMethod();", |
| "}", |
| "-if class " + typeName(MyFieldValuePrinter.class) + " {", |
| " void printFieldValues(" + typeName(PrintableFieldInterface.class) + ");", |
| "}", |
| "-keepclassmembers class * extends " + typeName(PrintableFieldInterface.class) + " {", |
| " *** *;", |
| "}" |
| // Removed the rule variant on the base interface as it does not have fields. |
| ) |
| .addProgramClasses(TestClass.class) |
| .addProgramClassesAndInnerClasses(getExampleClasses()) |
| .setMinApi(parameters) |
| .run(parameters.getRuntime(), TestClass.class) |
| .assertSuccessWithOutput(EXPECTED); |
| } |
| |
| public List<Class<?>> getExampleClasses() { |
| return ImmutableList.of(Example1.class, Example2.class); |
| } |
| |
| static class TestClass { |
| public static void main(String[] args) throws Exception { |
| Example1.run(); |
| Example2.run(); |
| } |
| } |
| |
| static class Example1 { |
| |
| static class BaseClass { |
| void hiddenMethod() { |
| System.out.println("on Base"); |
| } |
| } |
| |
| static class SubClass extends BaseClass { |
| void hiddenMethod() { |
| System.out.println("on Sub"); |
| } |
| } |
| |
| public static class MyHiddenMethodCaller { |
| public void callHiddenMethod(BaseClass base) throws Exception { |
| base.getClass().getDeclaredMethod("hiddenMethod").invoke(base); |
| } |
| } |
| |
| static void run() throws Exception { |
| new MyHiddenMethodCaller().callHiddenMethod(new BaseClass()); |
| new MyHiddenMethodCaller().callHiddenMethod(new SubClass()); |
| } |
| } |
| |
| static class Example2 { |
| |
| interface PrintableFieldInterface {} |
| |
| static class ClassWithFields implements PrintableFieldInterface { |
| final int intField = 42; |
| String stringField = "Hello!"; |
| } |
| |
| public static class MyFieldValuePrinter { |
| public void printFieldValues(PrintableFieldInterface objectWithFields) throws Exception { |
| for (Field field : objectWithFields.getClass().getDeclaredFields()) { |
| System.out.println(field.getName() + " = " + field.get(objectWithFields)); |
| } |
| } |
| } |
| |
| static void run() throws Exception { |
| new MyFieldValuePrinter().printFieldValues(new ClassWithFields()); |
| } |
| } |
| } |