Ensure handling of optional type args and inner if class was merged
Bug: 147800709
Change-Id: I71d7041df0d455fcf07287b50385181d78fdb50a
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java
index dc0aa8c..74b17d6 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java
@@ -229,20 +229,16 @@
qualIdent.append(this.identifier);
T parsedEnclosingType = actions.parsedTypeName(qualIdent.toString(), parserPosition);
- if (parsedEnclosingType != null) {
- // We should only parse any optional type arguments and member classes if we have not merged
- // the class into the current subtype.
- updateOptTypeArguments();
+ updateOptTypeArguments();
- while (symbol == '.') {
- // Deal with Member Classes.
- actions.parsedSymbol(symbol);
- scanSymbol();
- scanIdentifier();
- assert identifier != null;
- parsedEnclosingType = actions.parsedInnerTypeName(parsedEnclosingType, identifier);
- updateOptTypeArguments();
- }
+ while (symbol == '.') {
+ // Deal with Member Classes.
+ actions.parsedSymbol(symbol);
+ scanSymbol();
+ scanIdentifier();
+ assert identifier != null;
+ parsedEnclosingType = actions.parsedInnerTypeName(parsedEnclosingType, identifier);
+ updateOptTypeArguments();
}
actions.parsedSymbol(symbol);
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
index 13c2c43..45c59a8 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
@@ -24,6 +24,7 @@
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import java.util.function.Predicate;
import java.util.function.Supplier;
public class GenericSignatureRewriter {
@@ -154,6 +155,7 @@
private class GenericSignatureCollector implements GenericSignatureAction<DexType> {
private StringBuilder renamedSignature;
private DexProgramClass currentClassContext;
+ private DexType lastWrittenType = null;
String getRenamedSignature() {
return renamedSignature.toString();
@@ -165,8 +167,13 @@
@Override
public void parsedSymbol(char symbol) {
- if (symbol == ';' && renamedSignature.charAt(renamedSignature.length() - 1) == ';') {
- // The type was never written (maybe because it was merged with it's subtype)
+ if (symbol == ';' && lastWrittenType == null) {
+ // The type was never written (maybe because it was merged with it's subtype).
+ return;
+ }
+ // If the super-class or interface has been merged, we will stop writing out type
+ // arguments, resulting in a signature on the form '<>' if we do not remove it.
+ if (symbol == '>' && removeWrittenCharacter(c -> c == '<')) {
return;
}
renamedSignature.append(symbol);
@@ -179,6 +186,12 @@
@Override
public DexType parsedTypeName(String name, ParserPosition parserPosition) {
+ if (parserPosition == ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION
+ && lastWrittenType == null) {
+ // We are writing type-arguments for a merged class.
+ removeWrittenClassCharacter();
+ return null;
+ }
String originalDescriptor = getDescriptorFromClassBinaryName(name);
DexType type =
appView.graphLense().lookupType(appView.dexItemFactory().createType(originalDescriptor));
@@ -192,16 +205,36 @@
DexString classDescriptor = currentClassContext.type.descriptor;
if (!originalDescriptor.equals(classDescriptor.toString())
&& renamedDescriptor.equals(classDescriptor)) {
- renamedSignature.deleteCharAt(renamedSignature.length() - 1);
- return null;
+ lastWrittenType = null;
+ removeWrittenClassCharacter();
+ return type;
}
}
renamedSignature.append(getClassBinaryNameFromDescriptor(renamedDescriptor.toString()));
+ lastWrittenType = type;
return type;
}
+ private boolean removeWrittenCharacter(Predicate<Character> removeIf) {
+ int index = renamedSignature.length() - 1;
+ if (index < 0 || !removeIf.test(renamedSignature.charAt(index))) {
+ return false;
+ }
+ renamedSignature.deleteCharAt(index);
+ return true;
+ }
+
+ private void removeWrittenClassCharacter() {
+ removeWrittenCharacter(c -> c == 'L');
+ }
+
@Override
public DexType parsedInnerTypeName(DexType enclosingType, String name) {
+ if (enclosingType == null) {
+ // We are writing inner type names
+ removeWrittenClassCharacter();
+ return null;
+ }
assert enclosingType.isClassType();
String enclosingDescriptor = enclosingType.toDescriptorString();
DexType type =
diff --git a/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java b/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java
index a9fc20f..01a95ba 100644
--- a/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java
@@ -10,16 +10,18 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.naming.signature.merging.I;
import com.android.tools.r8.naming.signature.merging.ImplI;
import com.android.tools.r8.naming.signature.merging.ImplK;
+import com.android.tools.r8.naming.signature.merging.ImplL;
import com.android.tools.r8.naming.signature.merging.InterfaceToKeep;
import com.android.tools.r8.naming.signature.merging.J;
import com.android.tools.r8.naming.signature.merging.K;
+import com.android.tools.r8.naming.signature.merging.L;
import java.io.IOException;
-import java.lang.invoke.LambdaConversionException;
import java.lang.reflect.Type;
import java.util.concurrent.ExecutionException;
import org.junit.Test;
@@ -43,11 +45,18 @@
@Test
public void testRemovalOfMergedInterfaceOnSameClass()
- throws IOException, CompilationFailedException, ExecutionException,
- LambdaConversionException {
+ throws IOException, CompilationFailedException, ExecutionException {
testForR8(parameters.getBackend())
.addProgramClasses(
- ImplI.class, ImplK.class, I.class, J.class, InterfaceToKeep.class, K.class, Main.class)
+ ImplI.class,
+ ImplK.class,
+ I.class,
+ J.class,
+ InterfaceToKeep.class,
+ K.class,
+ Main.class,
+ L.class,
+ ImplL.class)
.addKeepMainRule(Main.class)
.addKeepClassRules(InterfaceToKeep.class)
.addKeepAttributes("Signature, InnerClasses, EnclosingMethod, *Annotation*")
@@ -57,6 +66,7 @@
internalOptions -> {
internalOptions.enableUnusedInterfaceRemoval = false;
})
+ .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(
"ImplI.foo",
@@ -64,7 +74,8 @@
"K: com.android.tools.r8.naming.signature.merging.InterfaceToKeep<java.lang.Void>",
"ImplK.foo",
"ImplK.bar",
- "ImplK: interface com.android.tools.r8.naming.signature.merging.K")
+ "ImplK: interface com.android.tools.r8.naming.signature.merging.K",
+ "ImplL.print")
.inspect(
codeInspector -> {
assertThat(codeInspector.clazz(I.class), not(isPresent()));
@@ -117,6 +128,11 @@
for (Type genericInterface : ImplK.class.getGenericInterfaces()) {
System.out.println("ImplK: " + genericInterface);
}
+ L<ImplL> l = new ImplL();
+ l.print((ImplL) l);
+ for (Type genericInterface : ImplL.class.getGenericInterfaces()) {
+ System.out.println("ImplL: " + genericInterface);
+ }
}
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/ImplL.java b/src/test/java/com/android/tools/r8/naming/signature/merging/ImplL.java
new file mode 100644
index 0000000..e50bdb3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/signature/merging/ImplL.java
@@ -0,0 +1,13 @@
+// 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.naming.signature.merging;
+
+public class ImplL implements L<ImplL> {
+
+ @Override
+ public void print(ImplL implL) {
+ System.out.println("ImplL.print");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/L.java b/src/test/java/com/android/tools/r8/naming/signature/merging/L.java
new file mode 100644
index 0000000..65656dd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/signature/merging/L.java
@@ -0,0 +1,10 @@
+// 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.naming.signature.merging;
+
+public interface L<T> {
+
+ void print(T t);
+}