blob: be3ee346141879fc81f9fa797fb496bf8c077a0a [file] [log] [blame]
// 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.annotations;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.enumunboxing.EnumUnboxingTestBase;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collection;
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 AnnotationWithInjectedMethodsTest extends EnumUnboxingTestBase {
private final TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters()
.withAllRuntimes()
.withApiLevelsStartingAtIncluding(apiLevelWithDefaultInterfaceMethodsSupport())
.build();
}
public AnnotationWithInjectedMethodsTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void testRuntime() throws Exception {
testForRuntime(parameters)
.addProgramClasses(getProgramClasses())
.addProgramClassFileData(getProgramClassFileData())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutput(getExpectedOutput());
}
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
.addProgramClasses(getProgramClasses())
.addProgramClassFileData(getProgramClassFileData())
.addKeepMainRule(Main.class)
.addKeepClassAndMembersRules(AnnotationWithInjectedMethod.class)
.addKeepRuntimeVisibleAnnotations()
.addOptionsModification(options -> options.testing.allowInjectedAnnotationMethods = true)
.noMinification()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutput(getExpectedOutput());
}
private String getExpectedOutput() {
ImmutableList.Builder<String> builder = ImmutableList.builder();
builder.add(
"Foo",
"Bar",
"Caught IncompleteAnnotationException: "
+ typeName(AnnotationWithInjectedMethod.class)
+ " missing element getInstanceData");
if (parameters.isCfRuntime()) {
builder.add("Caught AssertionError: Too many parameters for an annotation method");
} else {
builder.add(
"Caught IllegalArgumentException: Invalid method for annotation type: "
+ "public "
+ (parameters.getRuntime().asDex().getVm().getVersion() == Version.V7_0_0
? ""
: "default ")
+ typeName(Data.class)
+ " "
+ typeName(AnnotationWithInjectedMethod.class)
+ ".getInstanceData("
+ typeName(Data.class)
+ ")");
}
return StringUtils.lines(builder.build());
}
private Collection<Class<?>> getProgramClasses() {
return ImmutableList.of(Data.class);
}
private Collection<byte[]> getProgramClassFileData() throws IOException {
return ImmutableList.of(
transformer(Main.class)
.replaceAnnotationDescriptor(
descriptor(ToBeAnnotationWithInjectedMethod.class),
descriptor(AnnotationWithInjectedMethod.class))
.transform(),
transformer(AnnotationWithInjectedMethod.class)
.setAnnotation()
.removeInnerClasses()
.replaceAnnotationDescriptor(
descriptor(ToBeRetention.class), descriptor(Retention.class))
.transform());
}
static class Data {
String value;
Data(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}
@Retention(RetentionPolicy.RUNTIME)
@interface ToBeRetention {
RetentionPolicy value();
}
@ToBeRetention(RetentionPolicy.RUNTIME)
/*@*/ interface AnnotationWithInjectedMethod extends Annotation {
default Data getInstanceData() {
return new Data("Baz");
}
default Data getInstanceData(Data in) {
return new Data(in.value);
}
static Data getStaticData() {
return new Data("Foo");
}
static Data getStaticData(Data in) {
return new Data(in.value);
}
}
@Retention(RetentionPolicy.RUNTIME)
@interface ToBeAnnotationWithInjectedMethod {}
@ToBeAnnotationWithInjectedMethod
static class Main {
public static void main(String[] args) {
System.out.println(AnnotationWithInjectedMethod.getStaticData());
System.out.println(AnnotationWithInjectedMethod.getStaticData(new Data("Bar")));
try {
System.out.println(getAnnotationWithInjectedMethod().getInstanceData());
} catch (Throwable e) {
System.out.println("Caught " + e.getClass().getSimpleName() + ": " + e.getMessage());
}
try {
System.out.println(getAnnotationWithInjectedMethod().getInstanceData(new Data("Qux")));
} catch (Throwable e) {
System.out.println("Caught " + e.getClass().getSimpleName() + ": " + e.getMessage());
}
}
static AnnotationWithInjectedMethod getAnnotationWithInjectedMethod() {
return Main.class.getAnnotation(AnnotationWithInjectedMethod.class);
}
}
}