blob: 75306f7dd57a250d3b0a17b7d3d33fb188de4fa8 [file] [log] [blame]
// 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.graph.genericsignature;
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.google.common.base.Predicates.alwaysFalse;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GenericSignaturePrinter;
import com.android.tools.r8.graph.GenericSignatureTypeRewriter;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.Reporter;
import java.util.function.Function;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class ClassSignatureTest extends TestBase {
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withNoneRuntime().build();
}
public ClassSignatureTest(TestParameters parameters) {
parameters.assertNoneRuntime();
}
@Test
public void testSuperClass() {
testParsingAndPrintingEqual("Lfoo/bar/baz;");
}
@Test
public void testSuperClassWithInnerClasses() {
testParsingAndPrintingEqual("Lfoo/bar/baz.Foo.Bar;");
}
@Test
public void testSuperClassWithInterfaces1() {
testParsingAndPrintingEqual("Lfoo/bar/baz;Lfoo/bar/qux;");
}
@Test
public void testSuperClassWithInterfaces2() {
testParsingAndPrintingEqual("Lfoo/bar/baz;Lfoo/bar/qux;Lfoo/bar/quux;");
}
@Test
public void testSuperClassWithInterfacesInnerClassesSeparatedByDollar() {
testParsingAndPrintingEqual("Lfoo/bar/baz$Foo$Bar;Lfoo/bar/qux;Lfoo/bar/quux$Foo$Bar;");
}
@Test
public void testSuperClassWithInterfacesInnerClassesSeparatedByPeriod() {
testParsingAndPrintingEqual("Lfoo/bar/baz.Foo.Bar;Lfoo/bar/qux;Lfoo/bar/quux.Foo.Bar;");
}
@Test
public void testInnerClassesWithSeparatorInName1() {
testParsingAndPrintingEqual("Lfoo/bar/baz<*>.Foo$$$<*>.Bar;");
}
@Test
public void testInnerClassesWithSeparatorInName2() {
testParsingAndPrintingEqual("Lfoo/bar/baz<*>.Foo$Bar$$<*>.Qux;");
}
@Test
public void testSuperClassWithInnerClassesGenericArguments2() {
testParsingAndPrintingEqual("Lfoo/bar/baz<LFoo;>.Foo<LFoo;>.Bar<LFoo;>;");
}
@Test
public void testSuperClassWithInterfacesInnerClassesGenericArguments2() {
testParsingAndPrintingEqual(
"Lfoo/bar/baz<LFoo;>.Foo<LFoo;>.Bar<LFoo;>;Lfoo/bar/qux<LFoo;>;Lfoo/bar/quux<LFoo;>.Foo<LFoo;>.Bar<LFoo;>;");
}
@Test
public void testSuperClassWithInterfacesInnerClassesGenericArguments3() {
testParsingAndPrintingEqual(
"Lfoo/bar/baz<LFoo;[[I[[[LBar<LFoo;>;>.Foo<LFoo;>.Bar<LFoo;>;Lfoo/bar/qux<LFoo;>;Lfoo/bar/quux<LFoo;>.Foo<LFoo;>.Bar<LFoo;>;");
}
@Test
public void testWildCards() {
testParsingAndPrintingEqual("Lfoo/Bar<*>;");
}
@Test
public void testWildCardsPositiveNegative() {
testParsingAndPrintingEqual("Lfoo/Bar<*+[I-LFoo<+LBar;>;>;");
}
@Test
public void testSuperClassError() {
testParsingAndPrintingError("Lfoo/bar/baz")
.assertAllWarningsMatch(
diagnosticMessage(containsString("Invalid signature 'Lfoo/bar/baz'")));
}
@Test
public void testReferenceToTypeVariable() {
testParsingAndPrintingEqual("Lfoo/bar/baz<TT;>;");
}
@Test
public void testFormalTypeParameters() {
testParsingAndPrintingEqual("<T:Ljava/lang/Object;>Lfoo/bar/baz<TT;>;");
}
@Test
public void testFormalTypeWithEmpty() {
testParsingAndPrintingEqual("<T:>Lfoo/bar/baz<TT;>;");
}
@Test
public void testFormalTypeParametersWithInterfaces() {
testParsingAndPrintingEqual("<T:Ljava/lang/Object;:LI;>Lfoo/bar/baz<TT;>;");
}
@Test
public void testFormalTypeParametersArguments() {
testParsingAndPrintingEqual(
"<T:Ljava/lang/Object;:LI;R:LFoo<TT;[Lfoo/bar<TT;>.Baz<TT;[I>;>;>Lfoo/bar/baz<TT;>;");
}
@Test
public void testFormalTypeParameterEmptyInterfaceError() {
testParsingAndPrintingError("<T:Ljava/lang/Object;:>Lfoo/bar/baz<TT;>;")
.assertAllWarningsMatch(
diagnosticMessage(
containsString("Invalid signature '<T:Ljava/lang/Object;:>Lfoo/bar/baz<TT;>;'")));
}
@Test
public void testFormalTypeParametersEmptyError() {
// TODO(b/169716723): This should throw an error
assertThrows(AssertionError.class, () -> testParsingAndPrintingError("<>Lfoo/bar/baz<TT;>;"));
}
@Test
public void testPruningInterfaceBound() {
DexItemFactory factory = new DexItemFactory();
DexType context = factory.createType("Lj$/util/stream/Node$OfPrimitive;");
String methodName = "j$.util.stream.Node$OfPrimitive.foo()";
TestDiagnosticMessagesImpl testDiagnosticMessages = new TestDiagnosticMessagesImpl();
MethodTypeSignature parsedMethodSignature =
GenericSignature.parseMethodSignature(
methodName,
"<T_SPLITR::Lj$/util/Spliterator$OfPrimitive;T_NODE:Ljava/lang/Object;>()V",
Origin.unknown(),
factory,
testDiagnosticMessages);
testDiagnosticMessages.assertNoMessages();
assertTrue(parsedMethodSignature.hasSignature());
GenericSignatureTypeRewriter rewriter =
new GenericSignatureTypeRewriter(
factory,
dexType -> dexType.toDescriptorString().equals("Lj$/util/Spliterator$OfPrimitive;"),
Function.identity(),
null);
MethodTypeSignature rewritten = rewriter.rewrite(parsedMethodSignature);
assertNotNull(rewritten);
assertTrue(rewritten.hasSignature());
MethodTypeSignature reparsed =
GenericSignature.parseMethodSignature(
methodName, rewritten.toString(), Origin.unknown(), factory, testDiagnosticMessages);
assertTrue(reparsed.hasSignature());
testDiagnosticMessages.assertNoMessages();
assertEquals(rewritten.toString(), reparsed.toString());
}
private void testParsingAndPrintingEqual(String signature) {
ClassSignature parsed =
GenericSignature.parseClassSignature(
"A", signature, Origin.unknown(), new DexItemFactory(), new Reporter());
GenericSignaturePrinter genericSignaturePrinter =
new GenericSignaturePrinter(NamingLens.getIdentityLens(), alwaysFalse());
genericSignaturePrinter.visitClassSignature(parsed);
String outSignature = genericSignaturePrinter.toString();
assertEquals(signature, outSignature);
}
private TestDiagnosticMessages testParsingAndPrintingError(String signature) {
TestDiagnosticMessagesImpl testDiagnosticMessages = new TestDiagnosticMessagesImpl();
ClassSignature parsed =
GenericSignature.parseClassSignature(
"A",
signature,
Origin.unknown(),
new DexItemFactory(),
new Reporter(testDiagnosticMessages));
assertEquals(ClassSignature.noSignature(), parsed);
return testDiagnosticMessages;
}
}