blob: 6d9f975cb9213ec67c9e5c347ba58f8c1b735b24 [file] [log] [blame]
// 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()));
}
}
}