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);
} }
@@ -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) {
throw new IllegalStateException(
"Element doesn't have any UnsupportedAppUsage annotation");
}
try {
Method expectedSignatureMethod = annotationType.getMethod("expectedSignature");
// 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( mMessager.printMessage(
WARNING, WARNING,
String.format("Expected signature doesn't match generated signature.\n" String.format(
"Expected signature doesn't match generated signature.\n"
+ " Expected: %s\n Generated: %s", + " Expected: %s\n Generated: %s",
uba.expectedSignature(), signature), expectedSignature, signature),
element); element);
} }
} }
return signature; 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);
}
} 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;
} }
} }
@@ -164,20 +170,25 @@ 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 (Class<? extends Annotation> supportedAnnotation : SUPPORTED_ANNOTATIONS) {
Set<? extends Element> annotated = roundEnv.getElementsAnnotatedWith(
supportedAnnotation);
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) { for (Element e : annotated) {
String sig = sb.buildSignature(e); String sig = sb.buildSignature(supportedAnnotation, e);
if (sig != null) { if (sig != null) {
signatureMap.put(sig, e); signatureMap.put(sig, e);
} }
} }
}
if (!signatureMap.isEmpty()) {
try { try {
writeToFile(INDEX_CSV, writeToFile(INDEX_CSV,
getCsvHeaders(), getCsvHeaders(),
@@ -187,6 +198,7 @@ public class UnsupportedAppUsageProcessor extends AbstractProcessor {
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("Failed to write output", e); throw new RuntimeException("Failed to write output", e);
} }
}
return true; return true;
} }
} }