blob: 588ad2344a8676747c439358c2c32c219c0668ff [file] [log] [blame]
// 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.TestDiagnosticMessagesImpl;
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.GenericSignatureContextBuilder;
import com.android.tools.r8.graph.GenericSignatureCorrectnessHelper;
import com.android.tools.r8.graph.GenericSignatureCorrectnessHelper.SignatureEvaluationResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.Reporter;
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 {
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withNoneRuntime().build();
}
public GenericSignatureCorrectnessHelperTests(TestParameters parameters) {
parameters.assertNoneRuntime();
}
@Test
public void testAllValid() throws Exception {
AppView<AppInfoWithClassHierarchy> appView =
computeAppViewWithClassHierarchy(
buildInnerClasses(GenericSignatureCorrectnessHelperTests.class)
.addLibraryFile(ToolHelper.getJava8RuntimeJar())
.build(),
factory -> {
ProguardConfiguration.Builder builder =
ProguardConfiguration.builder(
factory, new Reporter(new TestDiagnosticMessagesImpl()));
builder.addKeepAttributePatterns(ImmutableList.of(ProguardKeepAttributes.SIGNATURE));
return builder.build();
});
GenericSignatureContextBuilder contextBuilder = GenericSignatureContextBuilder.create(appView);
GenericSignatureCorrectnessHelper.createForVerification(appView, contextBuilder)
.run(appView.appInfo().classes());
}
@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.VALID);
}
@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<AppInfoWithLiveness> appView =
computeAppViewWithLiveness(
buildClasses(classes)
.addClassProgramData(transformations)
.addLibraryFile(ToolHelper.getJava8RuntimeJar())
.build(),
factory -> {
ProguardConfiguration.Builder builder =
ProguardConfiguration.builder(
factory, new Reporter(new TestDiagnosticMessagesImpl()));
builder.addKeepAttributePatterns(ImmutableList.of(ProguardKeepAttributes.SIGNATURE));
return builder.build();
});
GenericSignatureContextBuilder contextBuilder = GenericSignatureContextBuilder.create(appView);
GenericSignatureCorrectnessHelper check =
GenericSignatureCorrectnessHelper.createForInitialCheck(appView, contextBuilder);
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> */ {}
}