Extend GenericSignature with optional type parameters
Bug: 129925954
Change-Id: Ibfec1c7daa025e0b227ea8075b007c5a8998e5cb
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignature.java b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
index 91ba7b7..97d49a5 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -96,6 +96,8 @@
*/
public class GenericSignature {
+ private static final List<FormalTypeParameter> EMPTY_TYPE_PARAMS = ImmutableList.of();
+
interface DexDefinitionSignature<T extends DexDefinition> {
default boolean isClassSignature() {
return false;
@@ -122,19 +124,51 @@
}
}
+ public static class FormalTypeParameter {
+
+ final String name;
+ final FieldTypeSignature classBound;
+ final List<FieldTypeSignature> interfaceBounds;
+
+ FormalTypeParameter(
+ String name, FieldTypeSignature classBound, List<FieldTypeSignature> interfaceBounds) {
+ this.name = name;
+ this.classBound = classBound;
+ this.interfaceBounds = interfaceBounds;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public FieldTypeSignature getClassBound() {
+ return classBound;
+ }
+
+ public List<FieldTypeSignature> getInterfaceBounds() {
+ return interfaceBounds;
+ }
+ }
+
public static class ClassSignature implements DexDefinitionSignature<DexClass> {
static final ClassSignature UNKNOWN_CLASS_SIGNATURE =
- new ClassSignature(ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE, ImmutableList.of());
+ new ClassSignature(
+ ImmutableList.of(),
+ ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE,
+ ImmutableList.of());
- // TODO(b/129925954): encoding formal type parameters
+ final List<FormalTypeParameter> formalTypeParameters;
final ClassTypeSignature superClassSignature;
final List<ClassTypeSignature> superInterfaceSignatures;
ClassSignature(
+ List<FormalTypeParameter> formalTypeParameters,
ClassTypeSignature superClassSignature,
List<ClassTypeSignature> superInterfaceSignatures) {
+ assert formalTypeParameters != null;
assert superClassSignature != null;
assert superInterfaceSignatures != null;
+ this.formalTypeParameters = formalTypeParameters;
this.superClassSignature = superClassSignature;
this.superInterfaceSignatures = superInterfaceSignatures;
}
@@ -221,6 +255,10 @@
public TypeVariableSignature asTypeVariableSignature() {
return null;
}
+
+ public boolean isUnknown() {
+ return this == ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE;
+ }
}
public static class ClassTypeSignature extends FieldTypeSignature {
@@ -331,7 +369,7 @@
public static class TypeVariableSignature extends FieldTypeSignature {
final String typeVariable;
- TypeVariableSignature(String typeVariable) {
+ private TypeVariableSignature(String typeVariable) {
assert typeVariable != null;
this.typeVariable = typeVariable;
}
@@ -350,6 +388,10 @@
public ArrayTypeSignature toArrayTypeSignature(AppView<?> appView) {
return new ArrayTypeSignature(this);
}
+
+ public String getTypeVariable() {
+ return typeVariable;
+ }
}
// TODO(b/129925954): Canonicalization?
@@ -400,20 +442,24 @@
public static class MethodTypeSignature implements DexDefinitionSignature<DexEncodedMethod> {
static final MethodTypeSignature UNKNOWN_METHOD_TYPE_SIGNATURE =
- new MethodTypeSignature(ImmutableList.of(), ReturnType.VOID, ImmutableList.of());
+ new MethodTypeSignature(
+ ImmutableList.of(), ImmutableList.of(), ReturnType.VOID, ImmutableList.of());
- // TODO(b/129925954): encoding formal type parameters
+ final List<FormalTypeParameter> formalTypeParameters;
final List<TypeSignature> typeSignatures;
final ReturnType returnType;
final List<TypeSignature> throwsSignatures;
MethodTypeSignature(
+ final List<FormalTypeParameter> formalTypeParameters,
List<TypeSignature> typeSignatures,
ReturnType returnType,
List<TypeSignature> throwsSignatures) {
+ assert formalTypeParameters != null;
assert typeSignatures != null;
assert returnType != null;
assert throwsSignatures != null;
+ this.formalTypeParameters = formalTypeParameters;
this.typeSignatures = typeSignatures;
this.returnType = returnType;
this.throwsSignatures = throwsSignatures;
@@ -443,6 +489,10 @@
public MethodTypeSignature asMethodTypeSignature() {
return this;
}
+
+ public List<FormalTypeParameter> getFormalTypeParameters() {
+ return formalTypeParameters;
+ }
}
enum Kind {
@@ -688,7 +738,7 @@
private ClassSignature parseClassSignature() {
// ClassSignature ::= FormalTypeParameters? SuperclassSignature SuperinterfaceSignature*.
- parseOptFormalTypeParameters();
+ List<FormalTypeParameter> formalTypeParameters = parseOptFormalTypeParameters();
// SuperclassSignature ::= ClassTypeSignature.
ClassTypeSignature superClassSignature =
@@ -700,42 +750,53 @@
builder.add(parseClassTypeSignature(ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION));
}
- return new ClassSignature(superClassSignature, builder.build());
+ return new ClassSignature(formalTypeParameters, superClassSignature, builder.build());
}
- private void parseOptFormalTypeParameters() {
+ private List<FormalTypeParameter> parseOptFormalTypeParameters() {
// FormalTypeParameters ::= "<" FormalTypeParameter+ ">".
-
- if (symbol == '<') {
- scanSymbol();
-
- updateFormalTypeParameter();
-
- while ((symbol != '>') && (symbol > 0)) {
- updateFormalTypeParameter();
- }
-
- expect('>');
+ if (symbol != '<') {
+ return EMPTY_TYPE_PARAMS;
}
+ scanSymbol();
+
+ ImmutableList.Builder<FormalTypeParameter> builder = ImmutableList.builder();
+ while ((symbol != '>') && (symbol > 0)) {
+ builder.add(updateFormalTypeParameter());
+ }
+ expect('>');
+ return builder.build();
}
- private void updateFormalTypeParameter() {
+ private FormalTypeParameter updateFormalTypeParameter() {
// FormalTypeParameter ::= Identifier ClassBound InterfaceBound*.
scanIdentifier();
assert identifier != null;
+ String typeParameterIdentifier = identifier;
+
// ClassBound ::= ":" FieldTypeSignature?.
expect(':');
+ FieldTypeSignature classBound = ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE;
if (symbol == 'L' || symbol == '[' || symbol == 'T') {
- parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
+ classBound = parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
}
+ // Only build the interfacebound builder, which is uncommon, if we actually see an interface.
+ ImmutableList.Builder<FieldTypeSignature> builder = null;
while (symbol == ':') {
// InterfaceBound ::= ":" FieldTypeSignature.
+ if (builder == null) {
+ builder = ImmutableList.builder();
+ }
scanSymbol();
- parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
+ builder.add(parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION));
}
+ if (builder == null) {
+ return new FormalTypeParameter(typeParameterIdentifier, classBound, null);
+ }
+ return new FormalTypeParameter(typeParameterIdentifier, classBound, builder.build());
}
private FieldTypeSignature parseFieldTypeSignature(ParserPosition parserPosition) {
@@ -862,7 +923,7 @@
private MethodTypeSignature parseMethodTypeSignature() {
// MethodTypeSignature ::=
// FormalTypeParameters? "(" TypeSignature* ")" ReturnType ThrowsSignature*.
- parseOptFormalTypeParameters();
+ List<FormalTypeParameter> formalTypeParameters = parseOptFormalTypeParameters();
expect('(');
@@ -890,7 +951,10 @@
}
return new MethodTypeSignature(
- parameterSignatureBuilder.build(), returnType, throwsSignatureBuilder.build());
+ formalTypeParameters,
+ parameterSignatureBuilder.build(),
+ returnType,
+ throwsSignatureBuilder.build());
}
private ReturnType updateReturnType() {
diff --git a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
index 33321eb..582a282 100644
--- a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
@@ -8,16 +8,21 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GenericSignature.Parser;
import com.android.tools.r8.graph.GenericSignature.ReturnType;
import com.android.tools.r8.graph.GenericSignature.TypeSignature;
+import com.android.tools.r8.graph.GenericSignatureTestClassA.I;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -29,9 +34,20 @@
import java.util.function.Function;
import java.util.function.Supplier;
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 GenericSignatureTest extends TestBase {
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public GenericSignatureTest(TestParameters parameters) {}
+
@Test
public void test() throws Exception {
AndroidApp app =
@@ -62,6 +78,8 @@
assertThat(cy, isPresent());
ClassSubject cyy = inspector.clazz(GenericSignatureTestClassCYY.class);
assertThat(cyy, isPresent());
+ ClassSubject i = inspector.clazz(I.class);
+ assertThat(cyy, isPresent());
DexEncodedMethod method;
@@ -80,13 +98,22 @@
// Testing ClassSignature
//
- // class CYY<T extends A<T>.Y> extends CY<T>
+ // class <T:GenericSignatureTestClassA<T>.Y>CYY<T extends A<T>.Y> extends CY<T>
DexClass clazz = cyy.getDexClass();
assertNotNull(clazz);
classSignature = Parser.toClassSignature(clazz, appView);
assertNotNull(classSignature);
- // TODO(b/129925954): test formal type parameter of CYY
+ assertEquals(1, classSignature.formalTypeParameters.size());
+ FormalTypeParameter formalTypeParameter = classSignature.formalTypeParameters.get(0);
+ assertEquals("T", formalTypeParameter.name);
+ assertNull(formalTypeParameter.interfaceBounds);
+ assertTrue(formalTypeParameter.classBound.isClassTypeSignature());
+ ClassTypeSignature classBoundSignature = formalTypeParameter.classBound.asClassTypeSignature();
+ assertEquals(y.getDexClass().type, classBoundSignature.innerTypeSignature.type);
+ assertEquals(1, classBoundSignature.typeArguments.size());
+ assertEquals(
+ "T", classBoundSignature.typeArguments.get(0).asTypeVariableSignature().typeVariable);
assertTrue(classSignature.superInterfaceSignatures.isEmpty());
classTypeSignature = classSignature.superClassSignature;
@@ -126,6 +153,18 @@
methodTypeSignature = Parser.toMethodTypeSignature(method, appView);
assertNotNull(methodTypeSignature);
+ assertEquals(1, methodTypeSignature.formalTypeParameters.size());
+ FormalTypeParameter methodFormalParameter = methodTypeSignature.formalTypeParameters.get(0);
+ assertTrue(methodFormalParameter.classBound.isClassTypeSignature());
+ assertEquals(
+ y.getDexClass().getType(),
+ methodFormalParameter.classBound.asClassTypeSignature().innerTypeSignature.type);
+ assertNotNull(methodFormalParameter.interfaceBounds);
+ assertEquals(1, methodFormalParameter.interfaceBounds.size());
+ FieldTypeSignature interfaceBound = methodFormalParameter.interfaceBounds.get(0);
+ assertTrue(interfaceBound.isClassTypeSignature());
+ assertEquals(i.getDexClass().getType(), interfaceBound.asClassTypeSignature().type);
+
// return type: A$Y$YY
returnType = methodTypeSignature.returnType();
assertFalse(returnType.isVoidDescriptor());
@@ -257,6 +296,9 @@
//
class GenericSignatureTestClassA<T> {
+
+ interface I {}
+
class Y {
class YY {}
@@ -264,7 +306,7 @@
class ZZ<TT> extends YY {
public YY yy;
- YY newYY(GenericSignatureTestClassB... bs) {
+ <R extends Y & I> YY newYY(GenericSignatureTestClassB... bs) {
return new YY();
}