| // Copyright (c) 2019, 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.naming; |
| |
| import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; |
| import static junit.framework.TestCase.assertEquals; |
| import static junit.framework.TestCase.assertTrue; |
| import static org.hamcrest.MatcherAssert.assertThat; |
| |
| import com.android.tools.r8.CompilationFailedException; |
| import com.android.tools.r8.TestBase; |
| import com.android.tools.r8.TestParameters; |
| import com.android.tools.r8.TestParametersCollection; |
| import com.android.tools.r8.transformers.ClassTransformer; |
| import com.android.tools.r8.utils.codeinspector.CodeInspector; |
| import com.android.tools.r8.utils.codeinspector.FoundMethodSubject; |
| import com.android.tools.r8.utils.codeinspector.MethodSubject; |
| import java.io.IOException; |
| import java.lang.reflect.Method; |
| import java.util.HashSet; |
| import java.util.Set; |
| import java.util.concurrent.ExecutionException; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| import org.junit.runners.Parameterized.Parameters; |
| import org.objectweb.asm.MethodVisitor; |
| |
| @RunWith(Parameterized.class) |
| public class OverloadUniqueNameTest extends TestBase { |
| |
| public static class A { |
| public void foo(int i) {} |
| |
| public void foo(Object o) {} |
| } |
| |
| public static class B extends A { |
| |
| // This is here to ensure that foo is not visited first when iterating sorted methods. |
| public void baz(int i) {} |
| |
| // This is an override, so it needs to have the same name as A.foo(int). |
| @Override |
| public void foo(int i) {} |
| |
| public void foo(boolean b) {} |
| |
| public void foo(int i, int j) {} |
| |
| private void foo(char c) {} |
| |
| private static void foo(String s) {} |
| } |
| |
| private interface I1 { |
| |
| void foo(long l); |
| } |
| |
| private interface I2 { |
| |
| void bar(long l); |
| |
| void foo(long l); |
| } |
| |
| public static class C extends B implements I1, I2 { |
| |
| @Override |
| public void bar(long l) {} |
| |
| @Override |
| // This method should be named according to I1.foo() and I2.foo(). |
| public void foo(long l) {} |
| |
| @Override |
| public void foo(int i) {} |
| } |
| |
| private interface I3 { |
| void foo(long l); |
| } |
| |
| private interface I4 extends I3 { |
| @Override |
| void foo(long l); |
| } |
| |
| public static class LambdaTest { |
| |
| public static void lambdaInterface() { |
| I1 lambda = ((I1 & I3) l -> {}); |
| } |
| } |
| |
| public static class ReturnType { |
| |
| int foo() { // <-- changed to int foo() to test overloading on return type |
| System.out.println("int foo();"); |
| return 0; |
| } |
| |
| void rewrite(int dummy) { |
| System.out.println("void foo();"); |
| } |
| } |
| |
| private final TestParameters parameters; |
| |
| @Parameters(name = "{0}") |
| public static TestParametersCollection data() { |
| return getTestParameters().withNoneRuntime().build(); |
| } |
| |
| public OverloadUniqueNameTest(TestParameters parameters) { |
| this.parameters = parameters; |
| } |
| |
| @Test |
| public void test() throws CompilationFailedException, IOException, ExecutionException { |
| testForR8(Backend.DEX) |
| .addProgramClasses( |
| A.class, B.class, I1.class, I2.class, C.class, I3.class, I4.class, LambdaTest.class) |
| .addKeepAllClassesRuleWithAllowObfuscation() |
| .compile() |
| .inspect( |
| codeInspector -> { |
| inspectUniqueMethodsInClass(codeInspector, I1.class); |
| inspectUniqueMethodsInClass(codeInspector, I2.class); |
| inspectUniqueMethodsInClass(codeInspector, I3.class); |
| inspectUniqueMethodsInClass(codeInspector, I4.class); |
| inspectUniqueMethodsInClass(codeInspector, A.class); |
| inspectUniqueMethodsInClass(codeInspector, B.class); |
| inspectUniqueMethodsInClass(codeInspector, C.class); |
| |
| // Ensure that virtual overrides in the class hierarchy has the same name. |
| final MethodSubject aFoo = codeInspector.clazz(A.class).method("void", "foo", "int"); |
| final MethodSubject bFoo = codeInspector.clazz(B.class).method("void", "foo", "int"); |
| assertEquals(aFoo.getFinalName(), bFoo.getFinalName()); |
| final MethodSubject cFoo = codeInspector.clazz(C.class).method("void", "foo", "int"); |
| assertEquals(aFoo.getFinalName(), cFoo.getFinalName()); |
| |
| // Ensure that all SAM interfaces has same method name. |
| final MethodSubject i1Foo = codeInspector.clazz(I1.class).uniqueMethodWithName("foo"); |
| final MethodSubject i2Foo = codeInspector.clazz(I2.class).uniqueMethodWithName("foo"); |
| assertEquals(i1Foo.getFinalName(), i2Foo.getFinalName()); |
| final MethodSubject i3Foo = codeInspector.clazz(I3.class).uniqueMethodWithName("foo"); |
| assertEquals(i1Foo.getFinalName(), i3Foo.getFinalName()); |
| |
| // Ensure C has the correct name for the interface method. |
| final MethodSubject cIFoo = |
| codeInspector.clazz(C.class).method("void", "foo", "long"); |
| assertEquals(cIFoo.getFinalName(), i1Foo.getFinalName()); |
| |
| // Ensure that I4.foo(int) has the same name as I3.foo(int). |
| final MethodSubject i4Foo = codeInspector.clazz(I4.class).uniqueMethodWithName("foo"); |
| assertEquals(i3Foo.getFinalName(), i4Foo.getFinalName()); |
| }); |
| } |
| |
| @Test |
| public void testReturnType() throws IOException, CompilationFailedException, ExecutionException { |
| testForR8(Backend.DEX) |
| .addProgramClassFileData( |
| transformer(ReturnType.class) |
| .addClassTransformer( |
| new ClassTransformer() { |
| @Override |
| public MethodVisitor visitMethod( |
| int access, |
| String name, |
| String descriptor, |
| String signature, |
| String[] exceptions) { |
| if (name.equals("rewrite")) { |
| return super.visitMethod(access, "foo", "()V", signature, exceptions); |
| } |
| return super.visitMethod(access, name, descriptor, signature, exceptions); |
| } |
| }) |
| .transform()) |
| .addKeepAllClassesRuleWithAllowObfuscation() |
| .compile() |
| .inspect( |
| codeInspector -> { |
| Set<String> seenMethodNames = new HashSet<>(); |
| for (FoundMethodSubject method : codeInspector.clazz(ReturnType.class).allMethods()) { |
| assertTrue(seenMethodNames.add(method.getFinalName())); |
| } |
| }); |
| } |
| |
| private void inspectUniqueMethodsInClass(CodeInspector inspector, Class<?> clazz) { |
| Set<String> newNames = new HashSet<>(); |
| for (Method method : clazz.getDeclaredMethods()) { |
| final MethodSubject methodSubject = inspector.method(method); |
| assertThat(methodSubject, isPresent()); |
| assertTrue(methodSubject.isRenamed()); |
| assertTrue(newNames.add(methodSubject.getFinalName())); |
| } |
| } |
| } |