| // 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 com.google.common.base.Predicates.alwaysTrue; | |
| 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, | |
| alwaysTrue()); | |
| 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; | |
| } | |
| } |