| // 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.repackage; |
| |
| import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; |
| import static org.hamcrest.MatcherAssert.assertThat; |
| |
| import com.android.tools.r8.NeverClassInline; |
| import com.android.tools.r8.NeverInline; |
| import com.android.tools.r8.NoHorizontalClassMerging; |
| import com.android.tools.r8.NoVerticalClassMerging; |
| import com.android.tools.r8.TestParameters; |
| 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; |
| |
| @RunWith(Parameterized.class) |
| public class RepackageWithBridgeHoistingTest extends RepackageTestBase { |
| |
| public RepackageWithBridgeHoistingTest( |
| String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) { |
| super(flattenPackageHierarchyOrRepackageClasses, parameters); |
| } |
| |
| @Test |
| public void test() throws Exception { |
| testForR8(parameters.getBackend()) |
| .addProgramClasses( |
| TestClass.class, GreeterInterface.class, GreeterBase.class, Greeting.class) |
| .addProgramClassFileData( |
| transformer(PrintGreeter.class) |
| .setBridge(PrintGreeter.class.getDeclaredMethod("greetBridge", Greeting.class)) |
| .transform(), |
| transformer(PrintlnGreeter.class) |
| .setBridge(PrintlnGreeter.class.getDeclaredMethod("greetBridge", Greeting.class)) |
| .transform()) |
| .addKeepMainRule(TestClass.class) |
| .apply(this::configureRepackaging) |
| .enableInliningAnnotations() |
| .enableNeverClassInliningAnnotations() |
| .enableNoHorizontalClassMergingAnnotations() |
| .enableNoVerticalClassMergingAnnotations() |
| .setMinApi(parameters.getApiLevel()) |
| .compile() |
| .inspect(this::inspect) |
| .run(parameters.getRuntime(), TestClass.class) |
| .assertSuccessWithOutputLines("Hello world!"); |
| } |
| |
| private void inspect(CodeInspector inspector) { |
| ClassSubject greeterBase = inspector.clazz(GreeterBase.class); |
| assertThat(greeterBase, isPresent()); |
| assertThat(greeterBase.uniqueMethodWithName("greetBridge"), isPresent()); |
| } |
| |
| public static class TestClass { |
| |
| public static void main(String[] args) { |
| new PrintGreeter().greetBridge(new Greeting("Hello")); |
| new PrintGreeter().greetBridge(new Greeting(" world!")); |
| |
| // Pass in an unknown greeter to prevent devirtualization in println(). |
| println(System.currentTimeMillis() >= 0 ? new PrintlnGreeter() : new PrintGreeter()); |
| } |
| |
| @NeverInline |
| static void println(GreeterInterface greeter) { |
| greeter.greet(new Greeting("")); |
| } |
| } |
| |
| @NoVerticalClassMerging |
| public interface GreeterInterface { |
| |
| void greet(Greeting greeting); |
| } |
| |
| public abstract static class GreeterBase implements GreeterInterface { |
| |
| // Will become the holder for greetBridge() after bridge hoisting. The bridge implementation |
| // will be "invoke-virtual GreeterBase.greet()". This signature is a new non-rebound method |
| // signature introduced by bridge hoisting. |
| } |
| |
| @NeverClassInline |
| @NoHorizontalClassMerging |
| public static class PrintGreeter extends GreeterBase { |
| |
| // Will be hoisted to GreeterBase.greetBridge(). |
| @NeverInline |
| public /*bridge*/ void greetBridge(Greeting greeting) { |
| greet(greeting); |
| } |
| |
| @NeverInline |
| public void greet(Greeting greeting) { |
| System.out.print(greeting); |
| } |
| } |
| |
| @NeverClassInline |
| @NoHorizontalClassMerging |
| public static class PrintlnGreeter extends GreeterBase { |
| |
| // Will be hoisted to GreeterBase.greetBridge(). |
| @NeverInline |
| public /*bridge*/ void greetBridge(Greeting greeting) { |
| greet(greeting); |
| } |
| |
| @NeverInline |
| public void greet(Greeting greeting) { |
| System.out.println(greeting); |
| } |
| } |
| |
| public static class Greeting { |
| |
| private final String greeting; |
| |
| public Greeting(String greeting) { |
| this.greeting = greeting; |
| } |
| |
| @Override |
| public String toString() { |
| return greeting; |
| } |
| } |
| } |