Add Local Type Annotation Use Desugaring
- Strips out type annotation reference from the method code session
- Type annotation use isn't supported in Java 7, and the Java7-undefined RuntimeVisibleTypeAnnotation attribute confuses D8 (b/149232699).
PiperOrigin-RevId: 302912545
diff --git a/src/test/java/com/google/devtools/build/android/desugar/BUILD b/src/test/java/com/google/devtools/build/android/desugar/BUILD
index dede1e9..590f41e 100644
--- a/src/test/java/com/google/devtools/build/android/desugar/BUILD
+++ b/src/test/java/com/google/devtools/build/android/desugar/BUILD
@@ -19,6 +19,7 @@
"//src/test/java/com/google/devtools/build/android/desugar/langmodel:srcs",
"//src/test/java/com/google/devtools/build/android/desugar/nest:srcs",
"//src/test/java/com/google/devtools/build/android/desugar/covariantreturn:srcs",
+ "//src/test/java/com/google/devtools/build/android/desugar/typeannotation:srcs",
"//src/test/java/com/google/devtools/build/android/desugar/runtime:srcs",
"//src/test/java/com/google/devtools/build/android/desugar/scan:srcs",
"//src/test/java/com/google/devtools/build/android/desugar/stringconcat:srcs",
diff --git a/src/test/java/com/google/devtools/build/android/desugar/typeannotation/AnnotationUser.java b/src/test/java/com/google/devtools/build/android/desugar/typeannotation/AnnotationUser.java
new file mode 100644
index 0000000..3adc41a
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/desugar/typeannotation/AnnotationUser.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2020 The Bazel Authors. All rights reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.devtools.build.android.desugar.typeannotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+/** The test source class for testing annotation desugaring. */
+public class AnnotationUser {
+
+ @SuppressWarnings("unused") // Test source
+ public void localVarWithTypeUseAnnotation(List<String> inTextList) {
+ List<@EnhancedType String> localTextList = inTextList;
+ }
+
+ public void instructionTypeUseAnnotation() {
+ new ArrayList<@EnhancedType String>();
+ }
+
+ public void tryCatchTypeAnnotation(RuntimeException inException) {
+ try {
+ throw inException;
+ } catch (@EnhancedType IllegalArgumentException e) {
+ throw new UnsupportedOperationException(e);
+ }
+ }
+
+ // @EnhancedVar is expected to be absent in Javac-compiled bytecode, even through @EnhancedVar is
+ // declared with a runtime retention policy. This test case ensures any retained annotation use
+ // within a method body is a type annotation.
+ @SuppressWarnings("unused") // Test source
+ public Function<Integer, Integer> localNonTypeAnnotations(String inputText) {
+ @EnhancedVar String localText = inputText;
+ try {
+ return (@EnhancedVar Integer x) -> 2 * x;
+ } catch (@EnhancedVar IllegalStateException e) {
+ throw new UnsupportedOperationException(e);
+ }
+ }
+
+ @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface EnhancedType {}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface EnhancedVar {}
+}
diff --git a/src/test/java/com/google/devtools/build/android/desugar/typeannotation/BUILD b/src/test/java/com/google/devtools/build/android/desugar/typeannotation/BUILD
new file mode 100644
index 0000000..c513585
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/desugar/typeannotation/BUILD
@@ -0,0 +1,48 @@
+load("@rules_java//java:defs.bzl", "java_test")
+
+package(
+ default_testonly = 1,
+ default_visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__subpackages__"],
+)
+
+licenses(["notice"]) # Apache 2.0
+
+java_test(
+ name = "LocalTypeAnnotationUseTest",
+ size = "medium",
+ srcs = ["LocalTypeAnnotationUseTest.java"],
+ data = [
+ ":annotation_test_src",
+ "//src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit:android_jar_for_testing",
+ "//src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit:jacoco_agent_jar",
+ ],
+ jvm_flags = [
+ "-Dinput_srcs=$(locations :annotation_test_src)",
+ # Required by Desugar#verifyLambdaDumpDirectoryRegistered
+ "-Djdk.internal.lambda.dumpProxyClasses=$$(mktemp -d)",
+ "-Dandroid_runtime_jar=$(location //src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit:android_jar_for_testing)",
+ "-Djacoco_agent_jar=$(location //src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit:jacoco_agent_jar)",
+ ],
+ test_class = "com.google.devtools.build.android.desugar.typeannotation.LocalTypeAnnotationUseTest",
+ deps = [
+ "//src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit:desugar_rule",
+ "//third_party:asm",
+ "//third_party:asm-tree",
+ "//third_party:guava",
+ "//third_party:jsr305",
+ "//third_party:jsr330_inject",
+ "//third_party:junit4",
+ "//third_party:truth",
+ ],
+)
+
+filegroup(
+ name = "annotation_test_src",
+ srcs = ["AnnotationUser.java"],
+)
+
+filegroup(
+ name = "srcs",
+ testonly = 0,
+ srcs = glob(["*"]),
+)
diff --git a/src/test/java/com/google/devtools/build/android/desugar/typeannotation/LocalTypeAnnotationUseTest.java b/src/test/java/com/google/devtools/build/android/desugar/typeannotation/LocalTypeAnnotationUseTest.java
new file mode 100644
index 0000000..0d3f8bb
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/desugar/typeannotation/LocalTypeAnnotationUseTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2020 The Bazel Authors. All rights reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.devtools.build.android.desugar.typeannotation;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.android.desugar.testing.junit.DesugarTestHelpers.filterInstructions;
+import static org.objectweb.asm.tree.AbstractInsnNode.TYPE_INSN;
+
+import com.google.devtools.build.android.desugar.testing.junit.AsmNode;
+import com.google.devtools.build.android.desugar.testing.junit.DesugarRule;
+import com.google.devtools.build.android.desugar.testing.junit.DesugarRunner;
+import com.google.devtools.build.android.desugar.testing.junit.JdkSuppress;
+import com.google.devtools.build.android.desugar.testing.junit.JdkVersion;
+import java.lang.invoke.MethodHandles;
+import java.util.Arrays;
+import java.util.Objects;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.objectweb.asm.tree.MethodNode;
+
+/** Tests for desugaring annotations. */
+@RunWith(DesugarRunner.class)
+@JdkSuppress(minJdkVersion = JdkVersion.V11)
+public class LocalTypeAnnotationUseTest {
+
+ @Rule
+ public final DesugarRule desugarRule =
+ DesugarRule.builder(this, MethodHandles.lookup())
+ .addSourceInputsFromJvmFlag("input_srcs")
+ .addJavacOptions("-source 11", "-target 11")
+ .setWorkingJavaPackage("com.google.devtools.build.android.desugar.typeannotation")
+ .addCommandOptions("desugar_try_with_resources_if_needed", "true")
+ .build();
+
+ @Test
+ public void localNonTypeAnnotationsWithRuntimeRetention_discardedByJavac(
+ @AsmNode(className = "AnnotationUser", memberName = "localNonTypeAnnotations", round = 0)
+ MethodNode before) {
+ assertThat(before.visibleLocalVariableAnnotations).isNull();
+ assertThat(
+ Arrays.stream(before.instructions.toArray())
+ .map(insn -> insn.visibleTypeAnnotations)
+ .allMatch(Objects::isNull))
+ .isTrue();
+ }
+
+ @Test
+ public void localVarWithTypeUseAnnotation(
+ @AsmNode(
+ className = "AnnotationUser",
+ memberName = "localVarWithTypeUseAnnotation",
+ round = 0)
+ MethodNode before,
+ @AsmNode(
+ className = "AnnotationUser",
+ memberName = "localVarWithTypeUseAnnotation",
+ round = 1)
+ MethodNode after) {
+ assertThat(before.visibleLocalVariableAnnotations).isNotEmpty();
+ assertThat(after.visibleLocalVariableAnnotations).isNull();
+ }
+
+ @Test
+ public void instructionTypeUseAnnotation(
+ @AsmNode(className = "AnnotationUser", memberName = "instructionTypeUseAnnotation", round = 0)
+ MethodNode before,
+ @AsmNode(className = "AnnotationUser", memberName = "instructionTypeUseAnnotation", round = 1)
+ MethodNode after) {
+ assertThat(getOnlyElement(filterInstructions(before, TYPE_INSN)).visibleTypeAnnotations)
+ .isNotEmpty();
+ assertThat(getOnlyElement(filterInstructions(after, TYPE_INSN)).visibleTypeAnnotations)
+ .isNull();
+ }
+
+ @Test
+ public void tryCatchTypeAnnotation(
+ @AsmNode(className = "AnnotationUser", memberName = "tryCatchTypeAnnotation", round = 0)
+ MethodNode before,
+ @AsmNode(className = "AnnotationUser", memberName = "tryCatchTypeAnnotation", round = 1)
+ MethodNode after) {
+ assertThat(getOnlyElement(before.tryCatchBlocks).visibleTypeAnnotations).isNotEmpty();
+ assertThat(getOnlyElement(after.tryCatchBlocks).visibleTypeAnnotations).isNull();
+ }
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/BUILD b/src/tools/android/java/com/google/devtools/build/android/desugar/BUILD
index 959a814..a078e03 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/BUILD
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/BUILD
@@ -46,6 +46,7 @@
"//src/tools/android/java/com/google/devtools/build/android/desugar/langmodel",
"//src/tools/android/java/com/google/devtools/build/android/desugar/nest",
"//src/tools/android/java/com/google/devtools/build/android/desugar/strconcat",
+ "//src/tools/android/java/com/google/devtools/build/android/desugar/typeannotation",
"//third_party:asm",
"//third_party:asm-commons",
"//third_party:asm-tree",
@@ -72,6 +73,7 @@
"//src/tools/android/java/com/google/devtools/build/android/desugar/langmodel:srcs",
"//src/tools/android/java/com/google/devtools/build/android/desugar/nest:srcs",
"//src/tools/android/java/com/google/devtools/build/android/desugar/covariantreturn:srcs",
+ "//src/tools/android/java/com/google/devtools/build/android/desugar/typeannotation:srcs",
"//src/tools/android/java/com/google/devtools/build/android/desugar/runtime:srcs",
"//src/tools/android/java/com/google/devtools/build/android/desugar/strconcat:srcs",
"//src/tools/android/java/com/google/devtools/build/android/desugar/scan:srcs",
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java b/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java
index 9eff3bc..0353151 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java
@@ -45,6 +45,7 @@
import com.google.devtools.build.android.desugar.nest.NestDesugaring;
import com.google.devtools.build.android.desugar.nest.NestDigest;
import com.google.devtools.build.android.desugar.strconcat.IndyStringConcatDesugaring;
+import com.google.devtools.build.android.desugar.typeannotation.LocalTypeAnnotationUse;
import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest;
import com.google.devtools.build.lib.worker.WorkerProtocol.WorkResponse;
import com.google.devtools.common.options.Option;
@@ -1067,6 +1068,8 @@
visitor = NioBufferRefConverter.create(visitor, rewriter.getPrefixer());
+ visitor = new LocalTypeAnnotationUse(visitor);
+
return visitor;
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit/DesugarTestHelpers.java b/src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit/DesugarTestHelpers.java
index 2292776..7acfd6f 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit/DesugarTestHelpers.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit/DesugarTestHelpers.java
@@ -37,6 +37,8 @@
/** Static utilities that facilities desugar testing. */
public class DesugarTestHelpers {
+ private DesugarTestHelpers() {}
+
/**
* A helper method that reads file paths into an array from the JVM flag value associated with
* {@param jvmFlagKey}.
@@ -61,9 +63,7 @@
Predicate<String> methodOwnerPredicate = Pattern.compile(methodOwnerRegex).asPredicate();
Predicate<String> methodNamePredicate = Pattern.compile(methodNameRegex).asPredicate();
Predicate<String> methodDescPredicate = Pattern.compile(methodDescRegex).asPredicate();
- AbstractInsnNode[] instructions = enclosingMethod.instructions.toArray();
- return Arrays.stream(instructions)
- .filter(node -> node.getType() == AbstractInsnNode.METHOD_INSN)
+ return filterInstructions(enclosingMethod, AbstractInsnNode.METHOD_INSN).stream()
.map(node -> (MethodInsnNode) node)
.filter(node -> methodOwnerPredicate.test(node.owner))
.filter(node -> methodNamePredicate.test(node.name))
@@ -78,5 +78,10 @@
.collect(toImmutableList());
}
- private DesugarTestHelpers() {}
+ public static ImmutableList<AbstractInsnNode> filterInstructions(
+ MethodNode enclosingMethod, int instructionType) {
+ return Arrays.stream(enclosingMethod.instructions.toArray())
+ .filter(node -> node.getType() == instructionType)
+ .collect(toImmutableList());
+ }
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/typeannotation/BUILD b/src/tools/android/java/com/google/devtools/build/android/desugar/typeannotation/BUILD
new file mode 100644
index 0000000..01b7825
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/typeannotation/BUILD
@@ -0,0 +1,28 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(
+ default_visibility = [
+ "//src/test/java/com/google/devtools/build/android/desugar:__subpackages__",
+ "//src/tools/android/java/com/google/devtools/build/android/desugar:__subpackages__",
+ ],
+)
+
+java_library(
+ name = "typeannotation",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//src/tools/android/java/com/google/devtools/build/android/desugar/langmodel",
+ "//third_party:asm",
+ "//third_party:asm-commons",
+ "//third_party:asm-tree",
+ "//third_party:auto_value",
+ "//third_party:guava",
+ "//third_party:jsr305",
+ ],
+)
+
+filegroup(
+ name = "srcs",
+ srcs = glob(["**"]),
+ visibility = ["//src/tools/android/java/com/google/devtools/build/android/desugar:__pkg__"],
+)
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/typeannotation/LocalTypeAnnotationUse.java b/src/tools/android/java/com/google/devtools/build/android/desugar/typeannotation/LocalTypeAnnotationUse.java
new file mode 100644
index 0000000..9aafc77
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/typeannotation/LocalTypeAnnotationUse.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2020 The Bazel Authors. All rights reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.devtools.build.android.desugar.typeannotation;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.TypePath;
+
+/** Desugars bytecode instructions with references to type annotations. */
+public class LocalTypeAnnotationUse extends ClassVisitor {
+
+ public LocalTypeAnnotationUse(ClassVisitor classVisitor) {
+ super(Opcodes.ASM7, classVisitor);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String descriptor, String signature, String[] exceptions) {
+ MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
+ return mv == null ? null : new LocalTypeAnnotationUseMethodVisitor(api, mv);
+ }
+
+ private static class LocalTypeAnnotationUseMethodVisitor extends MethodVisitor {
+
+ LocalTypeAnnotationUseMethodVisitor(int api, MethodVisitor methodVisitor) {
+ super(api, methodVisitor);
+ }
+
+ @Override
+ public AnnotationVisitor visitLocalVariableAnnotation(
+ int typeRef,
+ TypePath typePath,
+ Label[] start,
+ Label[] end,
+ int[] index,
+ String descriptor,
+ boolean visible) {
+ // The visited annotation has to be a type annotation. A LOCAL_VARIABLE-targeted annotation
+ // isn't retained in bytecode, regardless of retention policy. See details at,
+ // https://docs.oracle.com/javase/specs/jls/se11/html/jls-9.html#jls-9.6.4.2
+ return null;
+ }
+
+ @Override
+ public AnnotationVisitor visitInsnAnnotation(
+ int typeRef, TypePath typePath, String descriptor, boolean visible) {
+ // An instruction annotation has to be a type annotation.
+ return null;
+ }
+
+ @Override
+ public AnnotationVisitor visitTryCatchAnnotation(
+ int typeRef, TypePath typePath, String descriptor, boolean visible) {
+ // An exception parameter annotation present in Javac-compiled bytecode has to be a type
+ // annotation. See the JLS link in visitLocalVariableAnnotation of this class.
+ return null;
+ }
+ }
+}