| // Copyright (c) 2017, 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.bridgeremoval; |
| |
| 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.assertFalse; |
| |
| import com.android.tools.r8.TestBase; |
| import com.android.tools.r8.ToolHelper; |
| import com.android.tools.r8.ToolHelper.ProcessResult; |
| import com.android.tools.r8.bridgeremoval.bridgestoremove.Main; |
| import com.android.tools.r8.bridgeremoval.bridgestoremove.Outer; |
| import com.android.tools.r8.jasmin.JasminBuilder; |
| import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder; |
| import com.android.tools.r8.utils.AndroidApp; |
| import com.android.tools.r8.utils.codeinspector.ClassSubject; |
| import com.android.tools.r8.utils.codeinspector.CodeInspector; |
| import com.android.tools.r8.utils.codeinspector.MethodSubject; |
| import com.google.common.collect.ImmutableList; |
| import java.lang.reflect.Method; |
| import java.nio.file.Path; |
| import java.util.Collections; |
| import java.util.List; |
| import org.junit.Test; |
| |
| public class RemoveVisibilityBridgeMethodsTest extends TestBase { |
| |
| private void run(boolean obfuscate) throws Exception { |
| List<Class<?>> classes = ImmutableList.of(Outer.class, Main.class); |
| String proguardConfig = keepMainProguardConfiguration(Main.class, true, obfuscate); |
| CodeInspector inspector = new CodeInspector(compileWithR8(classes, proguardConfig)); |
| |
| List<Method> removedMethods = ImmutableList.of( |
| Outer.SubClass.class.getMethod("method"), |
| Outer.StaticSubClass.class.getMethod("method")); |
| |
| removedMethods.forEach(method -> assertFalse(inspector.method(method).isPresent())); |
| } |
| |
| @Test |
| public void testWithObfuscation() throws Exception { |
| run(true); |
| } |
| |
| @Test |
| public void testWithoutObfuscation() throws Exception { |
| run(false); |
| } |
| |
| @Test |
| public void regressionTest_b76383728_WithObfuscation() throws Exception { |
| runRegressionTest_b76383728(true); |
| } |
| |
| @Test |
| public void regressionTest_b76383728_WithoutObfuscation() throws Exception { |
| runRegressionTest_b76383728(false); |
| } |
| |
| /** |
| * Regression test for b76383728 to make sure we correctly identify and remove real visibility |
| * forward bridge methods synthesized by javac. |
| */ |
| private void runRegressionTest_b76383728(boolean obfuscate) throws Exception { |
| JasminBuilder jasminBuilder = new JasminBuilder(); |
| |
| ClassBuilder superClass = jasminBuilder.addClass("SuperClass"); |
| superClass.addDefaultConstructor(); |
| superClass.addVirtualMethod("method", Collections.emptyList(), "Ljava/lang/String;", |
| ".limit stack 1", |
| "ldc \"Hello World\"", |
| "areturn"); |
| |
| // Generate a subclass with a method with same |
| ClassBuilder subclass = jasminBuilder.addClass("SubClass", superClass.name); |
| subclass.addBridgeMethod("getMethod", Collections.emptyList(), "Ljava/lang/String;", |
| ".limit stack 1", |
| "aload_0", |
| "invokespecial " + superClass.name + "/method()Ljava/lang/String;", |
| "areturn"); |
| |
| ClassBuilder mainClass = jasminBuilder.addClass("Main"); |
| mainClass.addMainMethod( |
| ".limit stack 3", |
| ".limit locals 2", |
| "getstatic java/lang/System/out Ljava/io/PrintStream;", |
| "new " + subclass.name, |
| "dup", |
| "invokespecial " + subclass.name + "/<init>()V", |
| "invokevirtual " + subclass.name + "/getMethod()Ljava/lang/String;", |
| "invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V", |
| "return" |
| ); |
| |
| final String mainClassName = mainClass.name; |
| |
| String proguardConfig = keepMainProguardConfiguration(mainClass.name, true, obfuscate); |
| |
| // Run input program on java. |
| Path outputDirectory = temp.newFolder().toPath(); |
| jasminBuilder.writeClassFiles(outputDirectory); |
| ProcessResult javaResult = ToolHelper.runJava(outputDirectory, mainClassName); |
| assertEquals(0, javaResult.exitCode); |
| |
| AndroidApp optimizedApp = compileWithR8(jasminBuilder.build(), proguardConfig, |
| // Disable inlining to avoid the (short) tested method from being inlined and then removed. |
| internalOptions -> internalOptions.enableInlining = false); |
| |
| // Run optimized (output) program on ART |
| String artResult = runOnArt(optimizedApp, mainClassName); |
| assertEquals(javaResult.stdout, artResult); |
| |
| CodeInspector inspector = new CodeInspector(optimizedApp); |
| |
| ClassSubject classSubject = inspector.clazz(superClass.name); |
| assertThat(classSubject, isPresent()); |
| MethodSubject methodSubject = classSubject |
| .method("java.lang.String", "method", Collections.emptyList()); |
| assertThat(methodSubject, isPresent()); |
| |
| classSubject = inspector.clazz(subclass.name); |
| assertThat(classSubject, isPresent()); |
| methodSubject = classSubject.method("java.lang.String", "getMethod", Collections.emptyList()); |
| assertThat(methodSubject, isPresent()); |
| } |
| } |