Merge "Support all UnsupportedAppUsage annotations in processor"

This commit is contained in:
Andrei-Valentin Onea
2019-05-09 12:13:29 +00:00
committed by Gerrit Code Review
3 changed files with 83 additions and 46 deletions

View File

@@ -763,6 +763,7 @@ java_library_host {
srcs: [ srcs: [
"core/java/android/annotation/IntDef.java", "core/java/android/annotation/IntDef.java",
"core/java/android/annotation/UnsupportedAppUsage.java", "core/java/android/annotation/UnsupportedAppUsage.java",
":unsupportedappusage_annotation_files",
], ],
} }

View File

@@ -20,12 +20,13 @@ import static javax.lang.model.element.ElementKind.PACKAGE;
import static javax.tools.Diagnostic.Kind.ERROR; import static javax.tools.Diagnostic.Kind.ERROR;
import static javax.tools.Diagnostic.Kind.WARNING; import static javax.tools.Diagnostic.Kind.WARNING;
import android.annotation.UnsupportedAppUsage;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Type;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -69,6 +70,7 @@ public class SignatureBuilder {
public SignatureBuilderException(String message) { public SignatureBuilderException(String message) {
super(message); super(message);
} }
public void report(Element offendingElement) { public void report(Element offendingElement) {
mMessager.printMessage(ERROR, getMessage(), offendingElement); mMessager.printMessage(ERROR, getMessage(), offendingElement);
} }
@@ -153,7 +155,7 @@ public class SignatureBuilder {
/** /**
* Get the signature for an executable, either a method or a constructor. * Get the signature for an executable, either a method or a constructor.
* *
* @param name "<init>" for constructor, else the method name * @param name "<init>" for constructor, else the method name
* @param method The executable element in question. * @param method The executable element in question.
*/ */
private String getExecutableSignature(CharSequence name, ExecutableElement method) private String getExecutableSignature(CharSequence name, ExecutableElement method)
@@ -191,8 +193,13 @@ public class SignatureBuilder {
return sig.toString(); return sig.toString();
} }
public String buildSignature(Element element) { /**
UnsupportedAppUsage uba = element.getAnnotation(UnsupportedAppUsage.class); * Creates the signature for an annotated element.
*
* @param annotationType type of annotation being processed.
* @param element element for which we want to create a signature.
*/
public String buildSignature(Class<? extends Annotation> annotationType, Element element) {
try { try {
String signature; String signature;
switch (element.getKind()) { switch (element.getKind()) {
@@ -208,18 +215,35 @@ public class SignatureBuilder {
default: default:
return null; return null;
} }
// if we have an expected signature on the annotation, warn if it doesn't match. // Obtain annotation objects
if (!Strings.isNullOrEmpty(uba.expectedSignature())) { Annotation annotation = element.getAnnotation(annotationType);
if (!signature.equals(uba.expectedSignature())) { if (annotation == null) {
mMessager.printMessage( throw new IllegalStateException(
WARNING, "Element doesn't have any UnsupportedAppUsage annotation");
String.format("Expected signature doesn't match generated signature.\n" }
+ " Expected: %s\n Generated: %s", try {
uba.expectedSignature(), signature), Method expectedSignatureMethod = annotationType.getMethod("expectedSignature");
element); // If we have an expected signature on the annotation, warn if it doesn't match.
} String expectedSignature = expectedSignatureMethod.invoke(annotation).toString();
if (!Strings.isNullOrEmpty(expectedSignature)) {
if (!signature.equals(expectedSignature)) {
mMessager.printMessage(
WARNING,
String.format(
"Expected signature doesn't match generated signature.\n"
+ " Expected: %s\n Generated: %s",
expectedSignature, signature),
element);
}
}
return signature;
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
"Annotation type does not have expectedSignature parameter", e);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException(
"Could not get expectedSignature parameter for annotation", e);
} }
return signature;
} catch (SignatureBuilderException problem) { } catch (SignatureBuilderException problem) {
problem.report(element); problem.report(element);
return null; return null;

View File

@@ -18,9 +18,8 @@ package android.processor.unsupportedappusage;
import static javax.tools.StandardLocation.CLASS_OUTPUT; import static javax.tools.StandardLocation.CLASS_OUTPUT;
import android.annotation.UnsupportedAppUsage;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
import com.sun.tools.javac.model.JavacElements; import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.util.Pair; import com.sun.tools.javac.util.Pair;
@@ -28,6 +27,7 @@ import com.sun.tools.javac.util.Position;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream; import java.io.PrintStream;
import java.lang.annotation.Annotation;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -47,14 +47,14 @@ import javax.lang.model.element.TypeElement;
/** /**
* Annotation processor for {@link UnsupportedAppUsage} annotations. * Annotation processor for {@link UnsupportedAppUsage} annotations.
* *
* This processor currently outputs two things: * This processor currently outputs a CSV file with a mapping of dex signatures to corresponding
* 1. A greylist.txt containing dex signatures of all annotated elements. * source positions.
* 2. A CSV file with a mapping of dex signatures to corresponding source positions.
* *
* The first will be used at a later stage of the build to add access flags to the dex file. The * This is used for automating updates to the annotations themselves.
* second is used for automating updates to the annotations themselves.
*/ */
@SupportedAnnotationTypes({"android.annotation.UnsupportedAppUsage"}) @SupportedAnnotationTypes({"android.annotation.UnsupportedAppUsage",
"dalvik.annotation.compat.UnsupportedAppUsage"
})
public class UnsupportedAppUsageProcessor extends AbstractProcessor { public class UnsupportedAppUsageProcessor extends AbstractProcessor {
// Package name for writing output. Output will be written to the "class output" location within // Package name for writing output. Output will be written to the "class output" location within
@@ -62,6 +62,13 @@ public class UnsupportedAppUsageProcessor extends AbstractProcessor {
private static final String PACKAGE = "unsupportedappusage"; private static final String PACKAGE = "unsupportedappusage";
private static final String INDEX_CSV = "unsupportedappusage_index.csv"; private static final String INDEX_CSV = "unsupportedappusage_index.csv";
private static final ImmutableSet<Class<? extends Annotation>> SUPPORTED_ANNOTATIONS =
ImmutableSet.of(android.annotation.UnsupportedAppUsage.class,
dalvik.annotation.compat.UnsupportedAppUsage.class);
private static final ImmutableSet<String> SUPPORTED_ANNOTATION_NAMES =
SUPPORTED_ANNOTATIONS.stream().map(annotation -> annotation.getCanonicalName()).collect(
ImmutableSet.toImmutableSet());
@Override @Override
public SourceVersion getSupportedSourceVersion() { public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest(); return SourceVersion.latest();
@@ -92,8 +99,7 @@ public class UnsupportedAppUsageProcessor extends AbstractProcessor {
private AnnotationMirror getUnsupportedAppUsageAnnotationMirror(Element e) { private AnnotationMirror getUnsupportedAppUsageAnnotationMirror(Element e) {
for (AnnotationMirror m : e.getAnnotationMirrors()) { for (AnnotationMirror m : e.getAnnotationMirrors()) {
TypeElement type = (TypeElement) m.getAnnotationType().asElement(); TypeElement type = (TypeElement) m.getAnnotationType().asElement();
if (type.getQualifiedName().toString().equals( if (SUPPORTED_ANNOTATION_NAMES.contains(type.getQualifiedName().toString())) {
UnsupportedAppUsage.class.getCanonicalName())) {
return m; return m;
} }
} }
@@ -133,12 +139,12 @@ public class UnsupportedAppUsageProcessor extends AbstractProcessor {
/** /**
* Maps an annotated element to the source position of the @UnsupportedAppUsage annotation * Maps an annotated element to the source position of the @UnsupportedAppUsage annotation
* attached to it. It returns CSV in the format: * attached to it. It returns CSV in the format:
* dex-signature,filename,start-line,start-col,end-line,end-col * dex-signature,filename,start-line,start-col,end-line,end-col
* *
* The positions refer to the annotation itself, *not* the annotated member. This can therefore * The positions refer to the annotation itself, *not* the annotated member. This can therefore
* be used to read just the annotation from the file, and to perform in-place edits on it. * be used to read just the annotation from the file, and to perform in-place edits on it.
* *
* @param signature the dex signature for the element. * @param signature the dex signature for the element.
* @param annotatedElement The annotated element * @param annotatedElement The annotated element
* @return A single line of CSV text * @return A single line of CSV text
*/ */
@@ -164,28 +170,34 @@ public class UnsupportedAppUsageProcessor extends AbstractProcessor {
*/ */
@Override @Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> annotated = roundEnv.getElementsAnnotatedWith(
UnsupportedAppUsage.class);
if (annotated.size() == 0) {
return true;
}
// build signatures for each annotated member, and put them in a map of signature to member
Map<String, Element> signatureMap = new TreeMap<>(); Map<String, Element> signatureMap = new TreeMap<>();
SignatureBuilder sb = new SignatureBuilder(processingEnv.getMessager()); SignatureBuilder sb = new SignatureBuilder(processingEnv.getMessager());
for (Element e : annotated) { for (Class<? extends Annotation> supportedAnnotation : SUPPORTED_ANNOTATIONS) {
String sig = sb.buildSignature(e); Set<? extends Element> annotated = roundEnv.getElementsAnnotatedWith(
if (sig != null) { supportedAnnotation);
signatureMap.put(sig, e); if (annotated.size() == 0) {
continue;
}
// Build signatures for each annotated member and put them in a map from signature to
// member.
for (Element e : annotated) {
String sig = sb.buildSignature(supportedAnnotation, e);
if (sig != null) {
signatureMap.put(sig, e);
}
} }
} }
try {
writeToFile(INDEX_CSV, if (!signatureMap.isEmpty()) {
getCsvHeaders(), try {
signatureMap.entrySet() writeToFile(INDEX_CSV,
.stream() getCsvHeaders(),
.map(e -> getAnnotationIndex(e.getKey() ,e.getValue()))); signatureMap.entrySet()
} catch (IOException e) { .stream()
throw new RuntimeException("Failed to write output", e); .map(e -> getAnnotationIndex(e.getKey(), e.getValue())));
} catch (IOException e) {
throw new RuntimeException("Failed to write output", e);
}
} }
return true; return true;
} }