Introduce a generic signature evaluator
This CL also introduce the notion of an invalid generic signature that
is maintained.
Bug: 172999267
Change-Id: I350acc7142509be28ef71ccada81aefe1b91ae72
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 a92dc75..1dc1f4f 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -134,6 +134,12 @@
default boolean hasNoSignature() {
return !hasSignature();
}
+
+ default boolean isInvalid() {
+ return false;
+ }
+
+ DexDefinitionSignature<T> toInvalid();
}
public static class FormalTypeParameter {
@@ -207,6 +213,12 @@
}
@Override
+ public InvalidClassSignature toInvalid() {
+ // Since we could create the structure we are also able to generate a string for it.
+ return new InvalidClassSignature(toString());
+ }
+
+ @Override
public boolean isClassSignature() {
return true;
}
@@ -216,6 +228,10 @@
return this;
}
+ public List<FormalTypeParameter> getFormalTypeParameters() {
+ return formalTypeParameters;
+ }
+
public void visit(GenericSignatureVisitor visitor) {
visitor.visitFormalTypeParameters(formalTypeParameters);
visitor.visitSuperClass(superClassSignature);
@@ -244,6 +260,42 @@
}
}
+ private static class InvalidClassSignature extends ClassSignature {
+
+ private final String genericSignatureString;
+
+ InvalidClassSignature(String genericSignatureString) {
+ super(EMPTY_TYPE_PARAMS, NO_FIELD_TYPE_SIGNATURE, EMPTY_SUPER_INTERFACES);
+ this.genericSignatureString = genericSignatureString;
+ }
+
+ @Override
+ public String toRenamedString(NamingLens namingLens, Predicate<DexType> isTypeMissing) {
+ return genericSignatureString;
+ }
+
+ @Override
+ public String toString() {
+ return genericSignatureString;
+ }
+
+ @Override
+ public InvalidClassSignature toInvalid() {
+ assert false : "Should not invoke toInvalid on an invalid signature";
+ return this;
+ }
+
+ @Override
+ public void visit(GenericSignatureVisitor visitor) {
+ assert false : "Should not visit an invalid signature";
+ }
+
+ @Override
+ public boolean isInvalid() {
+ return true;
+ }
+ }
+
public abstract static class TypeSignature {
public boolean isFieldTypeSignature() {
@@ -354,6 +406,43 @@
public static FieldTypeSignature noSignature() {
return NO_FIELD_TYPE_SIGNATURE;
}
+
+ @Override
+ public InvalidFieldTypeSignature toInvalid() {
+ return new InvalidFieldTypeSignature(toString());
+ }
+ }
+
+ private static class InvalidFieldTypeSignature extends FieldTypeSignature {
+
+ private final String genericSignature;
+
+ public InvalidFieldTypeSignature(String genericSignature) {
+ super(WildcardIndicator.NONE);
+ this.genericSignature = genericSignature;
+ }
+
+ @Override
+ public FieldTypeSignature asArgument(WildcardIndicator indicator) {
+ assert false : "Should not be called for an invalid signature";
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return genericSignature;
+ }
+
+ @Override
+ public String toRenamedString(NamingLens namingLens, Predicate<DexType> isTypeMissing) {
+ return genericSignature;
+ }
+
+ @Override
+ public InvalidFieldTypeSignature toInvalid() {
+ assert false : " Should not be called for an invalid signature";
+ return this;
+ }
}
static final class StarFieldTypeSignature extends FieldTypeSignature {
@@ -436,10 +525,6 @@
return argument;
}
- public boolean isNoSignature() {
- return this == NO_FIELD_TYPE_SIGNATURE;
- }
-
@Override
public ArrayTypeSignature toArrayTypeSignature() {
return new ArrayTypeSignature(this);
@@ -538,7 +623,7 @@
return new ArrayTypeSignature(this);
}
- public String getTypeVariable() {
+ public String typeVariable() {
return typeVariable;
}
}
@@ -674,6 +759,47 @@
public String toString() {
return toRenamedString(NamingLens.getIdentityLens(), alwaysTrue());
}
+
+ @Override
+ public InvalidMethodTypeSignature toInvalid() {
+ return new InvalidMethodTypeSignature(toString());
+ }
+ }
+
+ private static class InvalidMethodTypeSignature extends MethodTypeSignature {
+
+ private final String genericSignature;
+
+ public InvalidMethodTypeSignature(String genericSignature) {
+ super(EMPTY_TYPE_PARAMS, EMPTY_TYPE_SIGNATURES, ReturnType.VOID, EMPTY_TYPE_SIGNATURES);
+ this.genericSignature = genericSignature;
+ }
+
+ @Override
+ public String toRenamedString(NamingLens namingLens, Predicate<DexType> isTypeMissing) {
+ return genericSignature;
+ }
+
+ @Override
+ public String toString() {
+ return genericSignature;
+ }
+
+ @Override
+ public boolean isInvalid() {
+ return true;
+ }
+
+ @Override
+ public void visit(GenericSignatureVisitor visitor) {
+ assert false : "Should not visit an invalid signature";
+ }
+
+ @Override
+ public InvalidMethodTypeSignature toInvalid() {
+ assert false : "Should not be called for an invalid signature";
+ return this;
+ }
}
public static ClassSignature parseClassSignature(
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
new file mode 100644
index 0000000..a7979f6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
@@ -0,0 +1,305 @@
+// 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;
+
+import static com.android.tools.r8.graph.GenericSignatureCorrectnessHelper.SignatureEvaluationResult.INVALID_APPLICATION_COUNT;
+import static com.android.tools.r8.graph.GenericSignatureCorrectnessHelper.SignatureEvaluationResult.INVALID_INTERFACE_COUNT;
+import static com.android.tools.r8.graph.GenericSignatureCorrectnessHelper.SignatureEvaluationResult.INVALID_SUPER_TYPE;
+import static com.android.tools.r8.graph.GenericSignatureCorrectnessHelper.SignatureEvaluationResult.INVALID_TYPE_VARIABLE_UNDEFINED;
+import static com.android.tools.r8.graph.GenericSignatureCorrectnessHelper.SignatureEvaluationResult.VALID;
+
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.DexDefinitionSignature;
+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.ReturnType;
+import com.android.tools.r8.graph.GenericSignature.TypeSignature;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public class GenericSignatureCorrectnessHelper {
+
+ private enum Mode {
+ VERIFY,
+ MARK_AS_INVALID;
+
+ public boolean doNotVerify() {
+ return markAsInvalid();
+ }
+
+ public boolean markAsInvalid() {
+ return this == MARK_AS_INVALID;
+ }
+ }
+
+ public enum SignatureEvaluationResult {
+ INVALID_SUPER_TYPE,
+ INVALID_INTERFACE_COUNT,
+ INVALID_APPLICATION_COUNT,
+ INVALID_TYPE_VARIABLE_UNDEFINED,
+ VALID;
+
+ boolean isValid() {
+ return this == VALID;
+ }
+
+ boolean isInvalid() {
+ return this != VALID;
+ }
+ }
+
+ private final AppView<?> appView;
+ private final Mode mode;
+
+ private GenericSignatureCorrectnessHelper(AppView<?> appView, Mode mode) {
+ this.appView = appView;
+ this.mode = mode;
+ }
+
+ public static GenericSignatureCorrectnessHelper createForInitialCheck(AppView<?> appView) {
+ return new GenericSignatureCorrectnessHelper(appView, Mode.MARK_AS_INVALID);
+ }
+
+ public static GenericSignatureCorrectnessHelper createForVerification(AppView<?> appView) {
+ return new GenericSignatureCorrectnessHelper(appView, Mode.VERIFY);
+ }
+
+ public void run() {
+ appView.appInfo().classes().forEach(this::evaluateSignaturesForClass);
+ }
+
+ public SignatureEvaluationResult evaluateSignaturesForClass(DexProgramClass clazz) {
+ GenericSignatureContextEvaluator genericSignatureContextEvaluator =
+ new GenericSignatureContextEvaluator(appView, clazz, mode);
+ ClassSignature classSignature = clazz.getClassSignature();
+ SignatureEvaluationResult result = VALID;
+ if (classSignature.hasNoSignature() || !classSignature.isInvalid()) {
+ result = genericSignatureContextEvaluator.evaluateClassSignature(classSignature);
+ if (result.isInvalid() && mode.markAsInvalid()) {
+ clazz.setClassSignature(classSignature.toInvalid());
+ }
+ }
+ for (DexEncodedMethod method : clazz.methods()) {
+ SignatureEvaluationResult methodResult =
+ evaluate(
+ method::getGenericSignature,
+ genericSignatureContextEvaluator::visitMethodSignature,
+ method::setGenericSignature);
+ if (result.isValid() && methodResult.isInvalid()) {
+ result = methodResult;
+ }
+ }
+ for (DexEncodedField field : clazz.fields()) {
+ SignatureEvaluationResult fieldResult =
+ evaluate(
+ field::getGenericSignature,
+ genericSignatureContextEvaluator::visitFieldTypeSignature,
+ field::setGenericSignature);
+ if (result.isValid() && fieldResult.isInvalid()) {
+ result = fieldResult;
+ }
+ }
+ return result;
+ }
+
+ private <T extends DexDefinitionSignature<?>> SignatureEvaluationResult evaluate(
+ Supplier<T> getter, Function<T, SignatureEvaluationResult> evaluate, Consumer<T> setter) {
+ T signature = getter.get();
+ if (signature.hasNoSignature() || signature.isInvalid()) {
+ // Already marked as invalid, do nothing
+ return VALID;
+ }
+ SignatureEvaluationResult signatureResult = evaluate.apply(signature);
+ assert signatureResult.isValid() || mode.doNotVerify();
+ if (signatureResult.isInvalid() && mode.doNotVerify()) {
+ setter.accept((T) signature.toInvalid());
+ }
+ return signatureResult;
+ }
+
+ private static class GenericSignatureContextEvaluator {
+
+ private final AppView<?> appView;
+ private final DexProgramClass context;
+ private final Set<String> classFormalTypeParameters = new HashSet<>();
+ private final Set<String> methodTypeArguments = new HashSet<>();
+ private final Mode mode;
+
+ public GenericSignatureContextEvaluator(
+ AppView<?> appView, DexProgramClass context, Mode mode) {
+ this.appView = appView;
+ this.context = context;
+ this.mode = mode;
+ }
+
+ private SignatureEvaluationResult evaluateClassSignature(ClassSignature classSignature) {
+ classSignature
+ .getFormalTypeParameters()
+ .forEach(param -> classFormalTypeParameters.add(param.name));
+ if (classSignature.hasNoSignature()) {
+ return VALID;
+ }
+ SignatureEvaluationResult signatureEvaluationResult =
+ evaluateFormalTypeParameters(classSignature.formalTypeParameters);
+ if (signatureEvaluationResult.isInvalid()) {
+ return signatureEvaluationResult;
+ }
+ if ((context.superType != appView.dexItemFactory().objectType
+ && context.superType != classSignature.superClassSignature().type())
+ || (context.superType == appView.dexItemFactory().objectType
+ && classSignature.superClassSignature().hasNoSignature())) {
+ assert mode.doNotVerify();
+ return INVALID_SUPER_TYPE;
+ }
+ signatureEvaluationResult =
+ evaluateTypeArgumentsAppliedToType(
+ classSignature.superClassSignature().typeArguments(), context.superType);
+ if (signatureEvaluationResult.isInvalid()) {
+ return signatureEvaluationResult;
+ }
+ List<ClassTypeSignature> superInterfaces = classSignature.superInterfaceSignatures();
+ if (context.interfaces.size() != superInterfaces.size()) {
+ assert mode.doNotVerify();
+ return INVALID_INTERFACE_COUNT;
+ }
+ DexType[] actualInterfaces = context.interfaces.values;
+ for (int i = 0; i < actualInterfaces.length; i++) {
+ signatureEvaluationResult =
+ evaluateTypeArgumentsAppliedToType(
+ superInterfaces.get(i).typeArguments(), actualInterfaces[i]);
+ if (signatureEvaluationResult.isInvalid()) {
+ return signatureEvaluationResult;
+ }
+ }
+ return VALID;
+ }
+
+ private SignatureEvaluationResult visitMethodSignature(MethodTypeSignature methodSignature) {
+ methodSignature
+ .getFormalTypeParameters()
+ .forEach(param -> methodTypeArguments.add(param.name));
+ SignatureEvaluationResult evaluateResult =
+ evaluateFormalTypeParameters(methodSignature.getFormalTypeParameters());
+ if (evaluateResult.isInvalid()) {
+ return evaluateResult;
+ }
+ evaluateResult = evaluateTypeArguments(methodSignature.typeSignatures);
+ if (evaluateResult.isInvalid()) {
+ return evaluateResult;
+ }
+ evaluateResult = evaluateTypeArguments(methodSignature.throwsSignatures);
+ if (evaluateResult.isInvalid()) {
+ return evaluateResult;
+ }
+ ReturnType returnType = methodSignature.returnType();
+ if (!returnType.isVoidDescriptor()) {
+ evaluateResult = evaluateTypeArgument(returnType.typeSignature());
+ if (evaluateResult.isInvalid()) {
+ return evaluateResult;
+ }
+ }
+ methodTypeArguments.clear();
+ return evaluateResult;
+ }
+
+ private SignatureEvaluationResult evaluateTypeArguments(List<TypeSignature> typeSignatures) {
+ for (TypeSignature typeSignature : typeSignatures) {
+ SignatureEvaluationResult signatureEvaluationResult = evaluateTypeArgument(typeSignature);
+ if (signatureEvaluationResult.isInvalid()) {
+ return signatureEvaluationResult;
+ }
+ }
+ return VALID;
+ }
+
+ private SignatureEvaluationResult visitFieldTypeSignature(FieldTypeSignature fieldSignature) {
+ return evaluateTypeArgument(fieldSignature);
+ }
+
+ private SignatureEvaluationResult evaluateFormalTypeParameters(
+ List<FormalTypeParameter> typeParameters) {
+ for (FormalTypeParameter typeParameter : typeParameters) {
+ SignatureEvaluationResult evaluationResult = evaluateTypeParameter(typeParameter);
+ if (evaluationResult.isInvalid()) {
+ return evaluationResult;
+ }
+ }
+ return VALID;
+ }
+
+ private SignatureEvaluationResult evaluateTypeParameter(FormalTypeParameter typeParameter) {
+ SignatureEvaluationResult evaluationResult = evaluateTypeArgument(typeParameter.classBound);
+ if (evaluationResult.isInvalid()) {
+ return evaluationResult;
+ }
+ if (typeParameter.interfaceBounds != null) {
+ for (FieldTypeSignature interfaceBound : typeParameter.interfaceBounds) {
+ evaluationResult = evaluateTypeArgument(interfaceBound);
+ if (evaluationResult != VALID) {
+ return evaluationResult;
+ }
+ }
+ }
+ return VALID;
+ }
+
+ private SignatureEvaluationResult evaluateTypeArgument(TypeSignature typeSignature) {
+ if (typeSignature.isBaseTypeSignature()) {
+ return VALID;
+ }
+ FieldTypeSignature fieldTypeSignature = typeSignature.asFieldTypeSignature();
+ if (fieldTypeSignature.hasNoSignature()) {
+ return VALID;
+ }
+ if (fieldTypeSignature.isTypeVariableSignature()) {
+ // This is in an applied position, just check that the variable is registered.
+ String typeVariable = fieldTypeSignature.asTypeVariableSignature().typeVariable();
+ if (classFormalTypeParameters.contains(typeVariable)
+ || methodTypeArguments.contains(typeVariable)) {
+ return VALID;
+ }
+ assert mode.doNotVerify();
+ return INVALID_TYPE_VARIABLE_UNDEFINED;
+ }
+ if (fieldTypeSignature.isArrayTypeSignature()) {
+ return evaluateTypeArgument(fieldTypeSignature.asArrayTypeSignature().elementSignature());
+ }
+ assert fieldTypeSignature.isClassTypeSignature();
+ return evaluateTypeArguments(fieldTypeSignature.asClassTypeSignature());
+ }
+
+ private SignatureEvaluationResult evaluateTypeArguments(ClassTypeSignature classTypeSignature) {
+ return evaluateTypeArgumentsAppliedToType(
+ classTypeSignature.typeArguments, classTypeSignature.type());
+ }
+
+ private SignatureEvaluationResult evaluateTypeArgumentsAppliedToType(
+ List<FieldTypeSignature> typeArguments, DexType type) {
+ for (FieldTypeSignature typeArgument : typeArguments) {
+ SignatureEvaluationResult evaluationResult = evaluateTypeArgument(typeArgument);
+ if (evaluationResult.isInvalid()) {
+ assert mode.doNotVerify();
+ return evaluationResult;
+ }
+ }
+ DexClass clazz = appView.definitionFor(type);
+ if (clazz == null) {
+ // We do not know if the application of arguments works or not.
+ return VALID;
+ }
+ if (typeArguments.size() != clazz.classSignature.getFormalTypeParameters().size()) {
+ assert mode.doNotVerify();
+ return INVALID_APPLICATION_COUNT;
+ }
+ return VALID;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java b/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
index f68675f..a670da0 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
@@ -148,7 +148,7 @@
} else {
assert fieldTypeSignature.isClassTypeSignature();
ClassTypeSignature classTypeSignature = fieldTypeSignature.asClassTypeSignature();
- if (classTypeSignature.isNoSignature()) {
+ if (classTypeSignature.hasNoSignature()) {
return;
}
String renamedString = namingLens.lookupDescriptor(classTypeSignature.type).toString();
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
index fd77b73..d20833f 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
@@ -57,14 +57,14 @@
}
public ClassSignature rewrite(ClassSignature classSignature) {
- if (classSignature.hasNoSignature()) {
+ if (classSignature.hasNoSignature() || classSignature.isInvalid()) {
return classSignature;
}
return new ClassSignatureRewriter().run(classSignature);
}
public FieldTypeSignature rewrite(FieldTypeSignature fieldTypeSignature) {
- if (fieldTypeSignature.hasNoSignature()) {
+ if (fieldTypeSignature.hasNoSignature() || fieldTypeSignature.isInvalid()) {
return fieldTypeSignature;
}
FieldTypeSignature rewrittenSignature = new TypeSignatureRewriter().run(fieldTypeSignature);
@@ -72,7 +72,7 @@
}
public MethodTypeSignature rewrite(MethodTypeSignature methodTypeSignature) {
- if (methodTypeSignature.hasNoSignature()) {
+ if (methodTypeSignature.hasNoSignature() || methodTypeSignature.isInvalid()) {
return methodTypeSignature;
}
return new MethodTypeSignatureRewriter().run(methodTypeSignature);
@@ -117,7 +117,7 @@
classSignature.visit(this);
if (rewrittenTypeParameters.isEmpty()
&& rewrittenSuperInterfaces.isEmpty()
- && rewrittenSuperClass.isNoSignature()
+ && rewrittenSuperClass.hasNoSignature()
&& rewrittenSuperClass.type == factory.objectType) {
return ClassSignature.noSignature();
}
@@ -255,7 +255,7 @@
}
assert fieldTypeSignature.isClassTypeSignature();
ClassTypeSignature classTypeSignature = fieldTypeSignature.asClassTypeSignature();
- if (classTypeSignature.isNoSignature()) {
+ if (classTypeSignature.hasNoSignature()) {
return classTypeSignature;
}
return new ClassTypeSignatureRewriter(false).run(classTypeSignature);
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureCorrectnessHelperTests.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureCorrectnessHelperTests.java
new file mode 100644
index 0000000..db76bf9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureCorrectnessHelperTests.java
@@ -0,0 +1,244 @@
+// Copyright (c) 2021, 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GenericSignatureCorrectnessHelper;
+import com.android.tools.r8.graph.GenericSignatureCorrectnessHelper.SignatureEvaluationResult;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+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 GenericSignatureCorrectnessHelperTests extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public GenericSignatureCorrectnessHelperTests(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testAllValid() throws Exception {
+ AppView<AppInfoWithClassHierarchy> appView =
+ computeAppViewWithClassHierachy(
+ buildInnerClasses(GenericSignatureCorrectnessHelperTests.class)
+ .addLibraryFile(ToolHelper.getJava8RuntimeJar())
+ .build());
+ GenericSignatureCorrectnessHelper check =
+ GenericSignatureCorrectnessHelper.createForVerification(appView);
+ check.run();
+ }
+
+ @Test
+ public void testMissingTypeArgumentInClassBound() throws Exception {
+ runTest(
+ ImmutableList.of(Base.class),
+ ImmutableList.of(
+ transformer(ClassWithClassBound.class)
+ .setGenericSignature(
+ existing -> {
+ // Replace the generic type parameter T with R.
+ return existing.replace("<T:", "<R:");
+ })
+ .transform()),
+ ClassWithClassBound.class,
+ SignatureEvaluationResult.INVALID_TYPE_VARIABLE_UNDEFINED);
+ }
+
+ @Test
+ public void testMissingTypeArgumentInInterfaceBound() throws Exception {
+ runTest(
+ ImmutableList.of(I.class, J.class),
+ ImmutableList.of(
+ transformer(ClassWithInterfaceBound.class)
+ .setGenericSignature(
+ existing -> {
+ // Replace the generic type parameter T with R.
+ return existing.replace("<T:", "<R:");
+ })
+ .transform()),
+ ClassWithInterfaceBound.class,
+ SignatureEvaluationResult.INVALID_TYPE_VARIABLE_UNDEFINED);
+ }
+
+ @Test
+ public void testMembersHavingInvalidTypeReference() throws Exception {
+ runTest(
+ ImmutableList.of(),
+ ImmutableList.of(
+ transformer(ClassWithMembersHavingInvalidTypeReference.class)
+ .setGenericSignature(
+ existing -> {
+ // Replace the generic type parameter T with R.
+ return existing.replace("<T:", "<R:");
+ })
+ .transform()),
+ ClassWithMembersHavingInvalidTypeReference.class,
+ SignatureEvaluationResult.INVALID_TYPE_VARIABLE_UNDEFINED);
+ }
+
+ @Test
+ public void testMethodHavingInvalidTypeReferences() throws Exception {
+ runTest(
+ ImmutableList.of(),
+ ImmutableList.of(
+ transformer(ClassWithMethodMissingTypeParameters.class)
+ .setGenericSignature(
+ MethodPredicate.onName("test"),
+ existing -> {
+ // Replace the generic type parameter T with R.
+ return existing.replace("<T:", "<R:");
+ })
+ .transform()),
+ ClassWithMethodMissingTypeParameters.class,
+ SignatureEvaluationResult.INVALID_TYPE_VARIABLE_UNDEFINED);
+ }
+
+ @Test
+ public void testIncorrectNumberOfSuperInterfaces() throws Exception {
+ runTest(
+ ImmutableList.of(),
+ ImmutableList.of(
+ transformer(ClassWithInvalidNumberOfSuperInterfaces.class)
+ .setImplements(I.class)
+ .transform()),
+ ClassWithInvalidNumberOfSuperInterfaces.class,
+ SignatureEvaluationResult.INVALID_INTERFACE_COUNT);
+ }
+
+ @Test
+ public void testMissingArgument() throws Exception {
+ runTest(
+ ImmutableList.of(J.class),
+ ImmutableList.of(
+ transformer(ClassWithInvalidArgumentCount.class)
+ .setGenericSignature(
+ existing -> {
+ // Replace the generic type argument <TT;> with nothing
+ return existing.replace("<TT;>", "");
+ })
+ .transform()),
+ ClassWithInvalidArgumentCount.class,
+ SignatureEvaluationResult.INVALID_APPLICATION_COUNT);
+ }
+
+ @Test
+ public void testTooManyArguments() throws Exception {
+ runTest(
+ ImmutableList.of(J.class),
+ ImmutableList.of(
+ transformer(ClassWithInvalidArgumentCount.class)
+ .setGenericSignature(
+ existing -> {
+ // Replace the generic type argument <TT;> with nothing
+ return existing.replace("<TT;>", "<TT;TT;>");
+ })
+ .transform()),
+ ClassWithInvalidArgumentCount.class,
+ SignatureEvaluationResult.INVALID_APPLICATION_COUNT);
+ }
+
+ @Test
+ public void testClassWithInvalidSuperType() throws Exception {
+ runTest(
+ ImmutableList.of(Base.class, OtherBase.class),
+ ImmutableList.of(
+ transformer(ClassWithInvalidSuperType.class)
+ .setSuper(DescriptorUtils.javaTypeToDescriptor(OtherBase.class.getTypeName()))
+ .transform()),
+ ClassWithInvalidSuperType.class,
+ SignatureEvaluationResult.INVALID_SUPER_TYPE);
+ }
+
+ private void runTest(
+ List<Class<?>> classes,
+ List<byte[]> transformations,
+ Class<?> classToVerify,
+ SignatureEvaluationResult expected)
+ throws Exception {
+ AppView<AppInfoWithClassHierarchy> appView =
+ computeAppViewWithClassHierachy(
+ buildClasses(classes)
+ .addClassProgramData(transformations)
+ .addLibraryFile(ToolHelper.getJava8RuntimeJar())
+ .build());
+ GenericSignatureCorrectnessHelper check =
+ GenericSignatureCorrectnessHelper.createForInitialCheck(appView);
+ DexProgramClass clazz =
+ appView
+ .definitionFor(
+ appView
+ .dexItemFactory()
+ .createType(DescriptorUtils.javaTypeToDescriptor(classToVerify.getTypeName())))
+ .asProgramClass();
+ assertNotNull(clazz);
+ assertEquals(expected, check.evaluateSignaturesForClass(clazz));
+ }
+
+ public interface I {}
+
+ public interface J<T> {
+ <R extends Object & I & J<Integer>> R foo(T foo) throws CustomException;
+ }
+
+ public static class Base<T> {}
+
+ public static class CustomException extends Exception {}
+
+ public static class Empty {}
+
+ public static class ClassWithClassBound<T extends Base<T /* R */>> {}
+
+ public static class ClassWithInterfaceBound<T extends I & J<T /* R */>> {}
+
+ public abstract static class ClassWithMembersHavingInvalidTypeReference<T /* R */> {
+
+ T t;
+
+ public abstract T testReturn();
+
+ public abstract void testParameter(T t);
+ }
+
+ public abstract static class ClassOverridingTypeArgument<T> {
+
+ public abstract <T> T test();
+ }
+
+ public abstract static class ClassWithMethodMissingTypeParameters {
+
+ public abstract <T /* R */> T test(T foo);
+ }
+
+ public abstract static class ClassWithInvalidNumberOfSuperInterfaces<T>
+ implements I, J<T> /* I */ {}
+
+ public abstract static class ClassWithInvalidArgumentCount<T>
+ implements J<T> /* J and J<T,T> */ {}
+
+ public static class OtherBase<T> {}
+
+ public abstract static class ClassWithInvalidSuperType<T> extends Base<T> /* OtherBase<T> */ {}
+}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 2b341aa..f828cfc 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -31,6 +31,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.stream.Collectors;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
@@ -331,6 +332,10 @@
}
public ClassFileTransformer setGenericSignature(String newGenericSignature) {
+ return setGenericSignature(signature -> newGenericSignature);
+ }
+
+ public ClassFileTransformer setGenericSignature(Function<String, String> newGenericSignature) {
return addClassTransformer(
new ClassTransformer() {
@Override
@@ -341,7 +346,8 @@
String signature,
String superName,
String[] interfaces) {
- super.visit(version, access, name, newGenericSignature, superName, interfaces);
+ super.visit(
+ version, access, name, newGenericSignature.apply(signature), superName, interfaces);
}
});
}
@@ -587,6 +593,21 @@
});
}
+ public ClassFileTransformer setGenericSignature(
+ MethodPredicate predicate, Function<String, String> newSignature) {
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String descriptor, String signature, String[] exceptions) {
+ return predicate.test(access, name, descriptor, signature, exceptions)
+ ? super.visitMethod(
+ access, name, descriptor, newSignature.apply(signature), exceptions)
+ : super.visitMethod(access, name, descriptor, signature, exceptions);
+ }
+ });
+ }
+
public ClassFileTransformer removeFields(FieldPredicate predicate) {
return addClassTransformer(
new ClassTransformer() {