blob: 36993e7213d15725b4f62987ba32affe42fc2134 [file] [log] [blame]
// Copyright (c) 2018, 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.shaking.annotations;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class ReflectiveAnnotationUseTest extends KotlinTestBase {
private static final String FOLDER = "internal_annotation";
private static final String MAIN_CLASS_NAME = "internal_annotation.MainKt";
private static final String ANNOTATION_NAME = "internal_annotation.Annotation";
private static final String KEEP_ANNOTATIONS = "-keepattributes *Annotation*";
private static final String JAVA_OUTPUT = StringUtils.lines(
"Impl::toString",
"Impl::Annotation::field2.Impl::Annotation::field2(Impl::Annotation::field2:2)"
);
private static final String OUTPUT_WITHOUT_ANNOTATION = StringUtils.lines(
"Impl::toString",
"null"
);
private final Backend backend;
private final boolean minify;
@Parameterized.Parameters(name = "Backend: {0} target: {1} minify: {2}")
public static Collection<Object[]> data() {
return buildParameters(Backend.values(), KotlinTargetVersion.values(), BooleanUtils.values());
}
public ReflectiveAnnotationUseTest(
Backend backend, KotlinTargetVersion targetVersion, boolean minify) {
super(targetVersion);
this.backend = backend;
this.minify = minify;
}
@Test
public void b120951621_JVMoutput() throws Exception {
assumeTrue("Only run JVM reference once (for CF backend)", backend == Backend.CF);
AndroidApp app = AndroidApp.builder()
.addProgramFile(getKotlinJarFile(FOLDER))
.addProgramFile(getJavaJarFile(FOLDER))
.build();
String result = runOnJava(app, MAIN_CLASS_NAME);
assertEquals(JAVA_OUTPUT, result);
}
@Test
public void b120951621_keepAll() throws Exception {
R8TestBuilder builder = testForR8(backend)
.addProgramFiles(getKotlinJarFile(FOLDER))
.addProgramFiles(getJavaJarFile(FOLDER))
.addKeepMainRule(MAIN_CLASS_NAME)
.addKeepRules(KEEP_ANNOTATIONS)
.addKeepRules(
"-keep @interface " + ANNOTATION_NAME + " {",
" *;",
"}"
);
if (!minify) {
builder.noMinification();
}
TestRunResult result = builder.run(MAIN_CLASS_NAME);
result.assertSuccessWithOutput(JAVA_OUTPUT);
CodeInspector inspector = result.inspector();
ClassSubject clazz = inspector.clazz(ANNOTATION_NAME);
assertThat(clazz, isPresent());
assertThat(clazz, not(isRenamed()));
MethodSubject f1 = clazz.uniqueMethodWithName("f1");
assertThat(f1, isPresent());
assertThat(f1, not(isRenamed()));
MethodSubject f2 = clazz.uniqueMethodWithName("f2");
assertThat(f2, isPresent());
assertThat(f2, not(isRenamed()));
MethodSubject f3 = clazz.uniqueMethodWithName("f3");
assertThat(f3, isPresent());
assertThat(f3, not(isRenamed()));
MethodSubject f4 = clazz.uniqueMethodWithName("f4");
assertThat(f4, isPresent());
assertThat(f4, not(isRenamed()));
}
@Test
public void b120951621_partiallyKeep() throws Exception {
R8TestBuilder builder = testForR8(backend)
.addProgramFiles(getKotlinJarFile(FOLDER))
.addProgramFiles(getJavaJarFile(FOLDER))
.addKeepMainRule(MAIN_CLASS_NAME)
.addKeepRules(KEEP_ANNOTATIONS)
.addKeepRules(
"-keep,allowobfuscation @interface " + ANNOTATION_NAME + " {",
" java.lang.String *f2();",
"}"
);
if (!minify) {
builder.noMinification();
}
R8TestRunResult result = builder.run(MAIN_CLASS_NAME);
if (backend == Backend.DEX) {
if (ToolHelper.getDexVm().getVersion().isOlderThanOrEqual(Version.V4_4_4)) {
result.assertFailureWithErrorThatMatches(
containsString("failure in processEncodedAnnotation"));
return;
}
if (ToolHelper.getDexVm().getVersion().isOlderThanOrEqual(Version.V6_0_1)) {
result.assertFailureWithErrorThatMatches(containsString("IncompatibleClassChangeError"));
return;
}
result.assertSuccessWithOutput(OUTPUT_WITHOUT_ANNOTATION);
} else {
result.assertSuccessWithOutput(JAVA_OUTPUT);
}
CodeInspector inspector = result.inspector();
ClassSubject clazz = inspector.clazz(ANNOTATION_NAME);
assertThat(clazz, isPresent());
assertEquals(minify, clazz.isRenamed());
MethodSubject f1 = clazz.uniqueMethodWithName("f1");
assertThat(f1, isPresent());
assertThat(f1, not(isRenamed()));
MethodSubject f2 = clazz.uniqueMethodWithName("f2");
assertThat(f2, isPresent());
assertThat(f2, not(isRenamed()));
MethodSubject f3 = clazz.uniqueMethodWithName("f3");
assertThat(f3, not(isPresent()));
MethodSubject f4 = clazz.uniqueMethodWithName("f4");
assertThat(f4, not(isPresent()));
}
@Test
public void b120951621_keepAnnotation() throws Exception {
R8TestBuilder builder = testForR8(backend)
.addProgramFiles(getKotlinJarFile(FOLDER))
.addProgramFiles(getJavaJarFile(FOLDER))
.addKeepMainRule(MAIN_CLASS_NAME)
.addKeepRules(KEEP_ANNOTATIONS);
if (!minify) {
builder.noMinification();
}
R8TestRunResult result = builder.run(MAIN_CLASS_NAME);
if (backend == Backend.DEX) {
if (ToolHelper.getDexVm().getVersion().isOlderThanOrEqual(Version.V4_4_4)) {
result.assertFailureWithErrorThatMatches(
containsString("failure in processEncodedAnnotation"));
return;
}
if (ToolHelper.getDexVm().getVersion().isOlderThanOrEqual(Version.V6_0_1)) {
result.assertFailureWithErrorThatMatches(containsString("IncompatibleClassChangeError"));
return;
}
result.assertSuccessWithOutput(OUTPUT_WITHOUT_ANNOTATION);
} else {
result.assertSuccessWithOutput(JAVA_OUTPUT);
}
CodeInspector inspector = result.inspector();
ClassSubject clazz = inspector.clazz(ANNOTATION_NAME);
assertThat(clazz, isPresent());
assertEquals(minify, clazz.isRenamed());
MethodSubject f1 = clazz.uniqueMethodWithName("f1");
assertThat(f1, isPresent());
assertThat(f1, not(isRenamed()));
MethodSubject f2 = clazz.uniqueMethodWithName("f2");
assertThat(f2, isPresent());
assertThat(f2, not(isRenamed()));
MethodSubject f3 = clazz.uniqueMethodWithName("f3");
assertThat(f3, not(isPresent()));
MethodSubject f4 = clazz.uniqueMethodWithName("f4");
assertThat(f4, not(isPresent()));
}
@Test
public void b120951621_noKeep() throws Exception {
R8TestBuilder builder = testForR8(backend)
.addProgramFiles(getKotlinJarFile(FOLDER))
.addProgramFiles(getJavaJarFile(FOLDER))
.addKeepMainRule(MAIN_CLASS_NAME);
if (!minify) {
builder.noMinification();
}
TestRunResult result = builder.run(MAIN_CLASS_NAME);
result.assertSuccessWithOutput(OUTPUT_WITHOUT_ANNOTATION);
CodeInspector inspector = result.inspector();
ClassSubject clazz = inspector.clazz(ANNOTATION_NAME);
assertThat(clazz, isPresent());
assertEquals(minify, clazz.isRenamed());
MethodSubject f1 = clazz.uniqueMethodWithName("f1");
assertThat(f1, not(isPresent()));
MethodSubject f2 = clazz.uniqueMethodWithName("f2");
assertThat(f2, not(isPresent()));
MethodSubject f3 = clazz.uniqueMethodWithName("f3");
assertThat(f3, not(isPresent()));
MethodSubject f4 = clazz.uniqueMethodWithName("f4");
assertThat(f4, not(isPresent()));
}
}