diff options
| author | 2020-12-10 15:26:03 +0000 | |
|---|---|---|
| committer | 2020-12-10 17:19:30 +0000 | |
| commit | 0b8b5a731f37491e1b135f577b16a5376bf4b753 (patch) | |
| tree | 01326421b20f18a730b9fc5a6a6fee6239246d76 | |
| parent | 0615dd9138d273567da6cca17176bdf560377418 (diff) | |
Move art/tools/class2nonsdklist to tools/platform-compat
Test: m droid
Bug: 175299477
Change-Id: I36bf4200c1caa8ce155ebb8283215498271b2faf
40 files changed, 0 insertions, 3517 deletions
diff --git a/tools/class2nonsdklist/Android.bp b/tools/class2nonsdklist/Android.bp deleted file mode 100644 index 507673460c..0000000000 --- a/tools/class2nonsdklist/Android.bp +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright (C) 2018 The Android Open Source Project -// -// 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. -// - -java_library_host { - name: "class2nonsdklistlib", - srcs: ["src/**/*.java"], - static_libs: [ - "commons-cli-1.2", - "apache-bcel", - "guava", - "testng", - "hamcrest-library", - ], -} - -java_binary_host { - name: "class2nonsdklist", - manifest: "src/class2nonsdklist.mf", - static_libs: [ - "class2nonsdklistlib", - ], -} diff --git a/tools/class2nonsdklist/src/class2nonsdklist.mf b/tools/class2nonsdklist/src/class2nonsdklist.mf deleted file mode 100644 index 5bd80d94f7..0000000000 --- a/tools/class2nonsdklist/src/class2nonsdklist.mf +++ /dev/null @@ -1 +0,0 @@ -Main-Class: com.android.class2nonsdklist.Class2NonSdkList diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AlternativeNotFoundError.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/AlternativeNotFoundError.java deleted file mode 100644 index 37f1f51a7d..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AlternativeNotFoundError.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -public class AlternativeNotFoundError extends Exception { -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotatedClassContext.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotatedClassContext.java deleted file mode 100644 index 8b5a8e1ca0..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotatedClassContext.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import java.util.Formatter; -import java.util.Locale; -import org.apache.bcel.Const; -import org.apache.bcel.classfile.FieldOrMethod; -import org.apache.bcel.classfile.JavaClass; - -/** - * Encapsulates context for a single annotation on a class. - */ -public class AnnotatedClassContext extends AnnotationContext { - - public final String signatureFormatString; - - public AnnotatedClassContext( - Status status, - JavaClass definingClass, - String signatureFormatString) { - super(status, definingClass); - this.signatureFormatString = signatureFormatString; - } - - @Override - public String getMemberDescriptor() { - return String.format(Locale.US, signatureFormatString, getClassDescriptor()); - } - - private String buildReportString(String message, Object... args) { - Formatter error = new Formatter(); - error - .format("%s: %s: ", definingClass.getSourceFileName(), definingClass.getClassName()) - .format(Locale.US, message, args); - return error.toString(); - } - - @Override - public void reportError(String message, Object... args) { - status.error(buildReportString(message, args)); - } - - @Override - public void reportWarning(String message, Object... args) { - status.warning(buildReportString(message, args)); - } - -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotatedMemberContext.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotatedMemberContext.java deleted file mode 100644 index 6276040fd7..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotatedMemberContext.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import java.util.Formatter; -import org.apache.bcel.Const; -import org.apache.bcel.classfile.FieldOrMethod; -import org.apache.bcel.classfile.JavaClass; - -import java.util.Locale; - -/** - * Encapsulates context for a single annotation on a class member. - */ -public class AnnotatedMemberContext extends AnnotationContext { - - public final FieldOrMethod member; - public final String signatureFormatString; - - public AnnotatedMemberContext( - Status status, - JavaClass definingClass, - FieldOrMethod member, - String signatureFormatString) { - super(status, definingClass); - this.member = member; - this.signatureFormatString = signatureFormatString; - } - - @Override - public String getMemberDescriptor() { - return String.format(Locale.US, signatureFormatString, - getClassDescriptor(), member.getName(), member.getSignature()); - } - - private String buildReportString(String message, Object... args) { - Formatter error = new Formatter(); - error - .format("%s: %s.%s: ", definingClass.getSourceFileName(), - definingClass.getClassName(), member.getName()) - .format(Locale.US, message, args); - return error.toString(); - } - - @Override - public void reportError(String message, Object... args) { - status.error(buildReportString(message, args)); - } - - @Override - public void reportWarning(String message, Object... args) { - status.warning(buildReportString(message, args)); - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationConsumer.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationConsumer.java deleted file mode 100644 index f527f61f1a..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationConsumer.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.android.class2nonsdklist; - -import java.util.Map; -import java.util.Set; - -public interface AnnotationConsumer { - /** - * Handle a parsed annotation for a class member. - * - * @param apiSignature Signature of the class member. - * @param annotationProperties Map of stringified properties of this annotation. - * @param parsedFlags Array of flags parsed from the annotation for this member. - */ - public void consume(String apiSignature, Map<String, String> annotationProperties, - Set<String> parsedFlags); - - public void close(); -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationContext.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationContext.java deleted file mode 100644 index 7ea6807eb1..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationContext.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import org.apache.bcel.Const; -import org.apache.bcel.classfile.JavaClass; - -/** - */ -public abstract class AnnotationContext implements StatusReporter { - - public final Status status; - public final JavaClass definingClass; - - public AnnotationContext(Status status, JavaClass definingClass) { - this.status = status; - this.definingClass = definingClass; - } - - public String getClassDescriptor() { - // JavaClass.getName() returns the Java-style name (with . not /), so we must fetch - // the original class name from the constant pool. - return definingClass.getConstantPool().getConstantString( - definingClass.getClassNameIndex(), Const.CONSTANT_Class); - } - - /** - * @return the full descriptor of this member, in the format expected in - * the greylist. - */ - public abstract String getMemberDescriptor(); -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationHandler.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationHandler.java deleted file mode 100644 index 0f7c00bb38..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationHandler.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.android.class2nonsdklist; - -import java.util.Map; -import java.util.HashMap; - -import org.apache.bcel.classfile.AnnotationEntry; -import org.apache.bcel.classfile.ElementValuePair; - - -/** - * Base class for an annotation handler, which handle individual annotations on - * class members. - */ -public abstract class AnnotationHandler { - abstract void handleAnnotation(AnnotationEntry annotation, AnnotationContext context); - - protected Map<String, String> stringifyAnnotationProperties(AnnotationEntry annotation) { - Map<String, String> content = new HashMap<String, String>(); - - // Stringify all annotation properties. - for (ElementValuePair prop : annotation.getElementValuePairs()) { - content.put(prop.getNameString(), prop.getValue().stringifyValue()); - } - - return content; - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationPropertyWriter.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationPropertyWriter.java deleted file mode 100644 index 5cf01c76ef..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationPropertyWriter.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.android.class2nonsdklist; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.OutputStream; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -public class AnnotationPropertyWriter implements AnnotationConsumer { - - private final PrintStream mOutput; - private final List<Map<String, String>> mContents; - private final Set<String> mColumns; - - public AnnotationPropertyWriter(String csvFile) throws FileNotFoundException { - mOutput = new PrintStream(new FileOutputStream(new File(csvFile))); - mContents = new ArrayList<>(); - mColumns = new HashSet<>(); - } - - public AnnotationPropertyWriter(OutputStream output) { - mOutput = new PrintStream(output); - mContents = new ArrayList<>(); - mColumns = new HashSet<>(); - } - - public void consume(String apiSignature, Map<String, String> annotationProperties, - Set<String> parsedFlags) { - // Clone properties map. - Map<String, String> contents = new HashMap(annotationProperties); - - // Append the member signature. - contents.put("signature", apiSignature); - - // Store data. - mColumns.addAll(contents.keySet()); - mContents.add(contents); - } - - private static String escapeCsvColumn(String column) { - // Using '|' as a quote character, as in frameworks/base/tools/hiddenapi/merge_csv.py - // Escape '|' characters in the column, then wrap the column in '|' characters. - column = column.replace("|", "||"); - return "|" + column + "|"; - } - - public void close() { - // Sort columns by name and print header row. - List<String> columns = new ArrayList<>(mColumns); - columns.sort(Comparator.naturalOrder()); - mOutput.println(columns.stream().collect(Collectors.joining(","))); - - // Sort contents according to columns and print. - for (Map<String, String> row : mContents) { - mOutput.println(columns.stream().map(column -> row.getOrDefault(column, "")) - .map(column -> escapeCsvColumn(column)) - .collect(Collectors.joining(","))); - } - - // Close output. - mOutput.close(); - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationVisitor.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationVisitor.java deleted file mode 100644 index 64e5f4b6bf..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/AnnotationVisitor.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import org.apache.bcel.classfile.AnnotationEntry; -import org.apache.bcel.classfile.DescendingVisitor; -import org.apache.bcel.classfile.EmptyVisitor; -import org.apache.bcel.classfile.Field; -import org.apache.bcel.classfile.FieldOrMethod; -import org.apache.bcel.classfile.JavaClass; -import org.apache.bcel.classfile.Method; - -import java.util.Map; - -/** - * Visits a JavaClass instance and passes any annotated members to a {@link AnnotationHandler} - * according to the map provided. - */ -public class AnnotationVisitor extends EmptyVisitor { - - private final JavaClass mClass; - private final Status mStatus; - private final DescendingVisitor mDescendingVisitor; - private final Map<String, AnnotationHandler> mAnnotationHandlers; - - /** - * Creates a visitor for a class. - * - * @param clazz Class to visit - * @param status For reporting debug information - * @param handlers Map of {@link AnnotationHandler}. The keys should be annotation names, as - * class descriptors. - */ - public AnnotationVisitor(JavaClass clazz, Status status, - Map<String, AnnotationHandler> handlers) { - mClass = clazz; - mStatus = status; - mAnnotationHandlers = handlers; - mDescendingVisitor = new DescendingVisitor(clazz, this); - } - - public void visit() { - mStatus.debug("Visit class %s", mClass.getClassName()); - AnnotationContext context = new AnnotatedClassContext(mStatus, mClass, "L%s;"); - AnnotationEntry[] annotationEntries = mClass.getAnnotationEntries(); - handleAnnotations(context, annotationEntries); - - mDescendingVisitor.visit(); - } - - @Override - public void visitMethod(Method method) { - visitMember(method, "L%s;->%s%s"); - } - - @Override - public void visitField(Field field) { - visitMember(field, "L%s;->%s:%s"); - } - - private void visitMember(FieldOrMethod member, String signatureFormatString) { - mStatus.debug("Visit member %s : %s", member.getName(), member.getSignature()); - AnnotationContext context = new AnnotatedMemberContext(mStatus, - (JavaClass) mDescendingVisitor.predecessor(), member, - signatureFormatString); - AnnotationEntry[] annotationEntries = member.getAnnotationEntries(); - handleAnnotations(context, annotationEntries); - } - - private void handleAnnotations(AnnotationContext context, AnnotationEntry[] annotationEntries) { - for (AnnotationEntry a : annotationEntries) { - if (mAnnotationHandlers.containsKey(a.getAnnotationType())) { - mStatus.debug("Member has annotation %s for which we have a handler", - a.getAnnotationType()); - mAnnotationHandlers.get(a.getAnnotationType()).handleAnnotation(a, context); - } else { - mStatus.debug("Member has annotation %s for which we do not have a handler", - a.getAnnotationType()); - } - } - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/ApiComponents.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/ApiComponents.java deleted file mode 100644 index f1f42f0c19..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/ApiComponents.java +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * Class which can parse either dex style signatures (e.g. Lfoo/bar/baz$bat;->foo()V) or javadoc - * links to class members (e.g. {@link #toString()} or {@link java.util.List#clear()}). - */ -public class ApiComponents { - private static final String PRIMITIVE_TYPES = "ZBCSIJFD"; - private final PackageAndClassName mPackageAndClassName; - // The reference can be just to a class, in which case mMemberName should be empty. - private final String mMemberName; - // If the member being referenced is a field, this will always be empty. - private final String mMethodParameterTypes; - - private ApiComponents(PackageAndClassName packageAndClassName, String memberName, - String methodParameterTypes) { - mPackageAndClassName = packageAndClassName; - mMemberName = memberName; - mMethodParameterTypes = methodParameterTypes; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder() - .append(mPackageAndClassName.packageName) - .append(".") - .append(mPackageAndClassName.className); - if (!mMemberName.isEmpty()) { - sb.append("#").append(mMemberName).append("(").append(mMethodParameterTypes).append( - ")"); - } - return sb.toString(); - } - - public PackageAndClassName getPackageAndClassName() { - return mPackageAndClassName; - } - - public String getMemberName() { - return mMemberName; - } - - public String getMethodParameterTypes() { - return mMethodParameterTypes; - } - - /** - * Parse a JNI class descriptor. e.g. Lfoo/bar/Baz; - * - * @param sc Cursor over string assumed to contain a JNI class descriptor. - * @return The fully qualified class, in 'dot notation' (e.g. foo.bar.Baz for a class named Baz - * in the foo.bar package). The cursor will be placed after the semicolon. - */ - private static String parseJNIClassDescriptor(StringCursor sc) - throws SignatureSyntaxError, StringCursorOutOfBoundsException { - if (sc.peek() != 'L') { - throw new SignatureSyntaxError( - "Expected JNI class descriptor to start with L, but instead got " + sc.peek(), - sc); - } - // Consume the L. - sc.next(); - int semiColonPos = sc.find(';'); - if (semiColonPos == -1) { - throw new SignatureSyntaxError("Expected semicolon at the end of JNI class descriptor", - sc); - } - String jniClassDescriptor = sc.next(semiColonPos); - // Consume the semicolon. - sc.next(); - return jniClassDescriptor.replace("/", "."); - } - - /** - * Parse a primitive JNI type - * - * @param sc Cursor over a string assumed to contain a primitive JNI type. - * @return String containing parsed primitive JNI type. - */ - private static String parseJNIPrimitiveType(StringCursor sc) - throws SignatureSyntaxError, StringCursorOutOfBoundsException { - char c = sc.next(); - switch (c) { - case 'Z': - return "boolean"; - case 'B': - return "byte"; - case 'C': - return "char"; - case 'S': - return "short"; - case 'I': - return "int"; - case 'J': - return "long"; - case 'F': - return "float"; - case 'D': - return "double"; - default: - throw new SignatureSyntaxError(c + " is not a primitive type!", sc); - } - } - - /** - * Parse a JNI type; can be either a primitive or object type. Arrays are handled separately. - * - * @param sc Cursor over the string assumed to contain a JNI type. - * @return String containing parsed JNI type. - */ - private static String parseJniTypeWithoutArrayDimensions(StringCursor sc) - throws SignatureSyntaxError, StringCursorOutOfBoundsException { - char c = sc.peek(); - if (PRIMITIVE_TYPES.indexOf(c) != -1) { - return parseJNIPrimitiveType(sc); - } else if (c == 'L') { - return parseJNIClassDescriptor(sc); - } - throw new SignatureSyntaxError("Illegal token " + c + " within signature", sc); - } - - /** - * Parse a JNI type. - * - * This parameter can be an array, in which case it will be preceded by a number of open square - * brackets (corresponding to its dimensionality) - * - * @param sc Cursor over the string assumed to contain a JNI type. - * @return Same as {@link #parseJniTypeWithoutArrayDimensions}, but also handle arrays. - */ - private static String parseJniType(StringCursor sc) - throws SignatureSyntaxError, StringCursorOutOfBoundsException { - int arrayDimension = 0; - while (sc.peek() == '[') { - ++arrayDimension; - sc.next(); - } - StringBuilder sb = new StringBuilder(); - sb.append(parseJniTypeWithoutArrayDimensions(sc)); - for (int i = 0; i < arrayDimension; ++i) { - sb.append("[]"); - } - return sb.toString(); - } - - /** - * Converts the parameters of method from JNI notation to Javadoc link notation. e.g. - * "(IILfoo/bar/Baz;)V" turns into "int, int, foo.bar.Baz". The parentheses and return type are - * discarded. - * - * @param sc Cursor over the string assumed to contain a JNI method parameters. - * @return Comma separated list of parameter types. - */ - private static String convertJNIMethodParametersToJavadoc(StringCursor sc) - throws SignatureSyntaxError, StringCursorOutOfBoundsException { - List<String> methodParameterTypes = new ArrayList<>(); - if (sc.next() != '(') { - throw new IllegalArgumentException("Trying to parse method params of an invalid dex " + - "signature: " + sc.getOriginalString()); - } - while (sc.peek() != ')') { - methodParameterTypes.add(parseJniType(sc)); - } - return String.join(", ", methodParameterTypes); - } - - /** - * Generate ApiComponents from a dex signature. - * - * This is used to extract the necessary context for an alternative API to try to infer missing - * information. - * - * @param signature Dex signature. - * @return ApiComponents instance with populated package, class name, and parameter types if - * applicable. - */ - public static ApiComponents fromDexSignature(String signature) throws SignatureSyntaxError { - StringCursor sc = new StringCursor(signature); - try { - String fullyQualifiedClass = parseJNIClassDescriptor(sc); - - PackageAndClassName packageAndClassName = - PackageAndClassName.splitClassName(fullyQualifiedClass); - if (!sc.peek(2).equals("->")) { - throw new SignatureSyntaxError("Expected '->'", sc); - } - // Consume "->" - sc.next(2); - String memberName = ""; - String methodParameterTypes = ""; - int leftParenPos = sc.find('('); - if (leftParenPos != -1) { - memberName = sc.next(leftParenPos); - methodParameterTypes = convertJNIMethodParametersToJavadoc(sc); - } else { - int colonPos = sc.find(':'); - if (colonPos == -1) { - throw new IllegalArgumentException("Expected : or -> beyond position " - + sc.position() + " in " + signature); - } else { - memberName = sc.next(colonPos); - // Consume the ':'. - sc.next(); - // Consume the type. - parseJniType(sc); - } - } - return new ApiComponents(packageAndClassName, memberName, methodParameterTypes); - } catch (StringCursorOutOfBoundsException e) { - throw new SignatureSyntaxError( - "Unexpectedly reached end of string while trying to parse signature ", sc); - } - } - - /** - * Generate ApiComponents from a link tag. - * - * @param linkTag The contents of a link tag. - * @param contextSignature The signature of the private API that this is an alternative for. - * Used to infer unspecified components. - */ - public static ApiComponents fromLinkTag(String linkTag, String contextSignature) - throws JavadocLinkSyntaxError { - ApiComponents contextAlternative; - try { - contextAlternative = fromDexSignature(contextSignature); - } catch (SignatureSyntaxError e) { - throw new RuntimeException( - "Failed to parse the context signature for public alternative!"); - } - StringCursor sc = new StringCursor(linkTag); - try { - - String memberName = ""; - String methodParameterTypes = ""; - - int tagPos = sc.find('#'); - String fullyQualifiedClassName = sc.next(tagPos); - - PackageAndClassName packageAndClassName = - PackageAndClassName.splitClassName(fullyQualifiedClassName); - - if (packageAndClassName.packageName.isEmpty()) { - packageAndClassName.packageName = contextAlternative.getPackageAndClassName() - .packageName; - } - - if (packageAndClassName.className.isEmpty()) { - packageAndClassName.className = contextAlternative.getPackageAndClassName() - .className; - } - - if (tagPos == -1) { - // This suggested alternative is just a class. We can allow that. - return new ApiComponents(packageAndClassName, "", ""); - } else { - // Consume the #. - sc.next(); - } - - int leftParenPos = sc.find('('); - memberName = sc.next(leftParenPos); - if (leftParenPos != -1) { - // Consume the '('. - sc.next(); - int rightParenPos = sc.find(')'); - if (rightParenPos == -1) { - throw new JavadocLinkSyntaxError( - "Linked method is missing a closing parenthesis", sc); - } else { - methodParameterTypes = sc.next(rightParenPos); - } - } - - return new ApiComponents(packageAndClassName, memberName, methodParameterTypes); - } catch (StringCursorOutOfBoundsException e) { - throw new JavadocLinkSyntaxError( - "Unexpectedly reached end of string while trying to parse javadoc link", sc); - } - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof ApiComponents)) { - return false; - } - ApiComponents other = (ApiComponents) obj; - return mPackageAndClassName.equals(other.mPackageAndClassName) && mMemberName.equals( - other.mMemberName) && mMethodParameterTypes.equals(other.mMethodParameterTypes); - } - - @Override - public int hashCode() { - return Objects.hash(mPackageAndClassName, mMemberName, mMethodParameterTypes); - } - - /** - * Less restrictive comparator to use in case a link tag is missing a method's parameters. - * e.g. foo.bar.Baz#foo will be considered the same as foo.bar.Baz#foo(int, int) and - * foo.bar.Baz#foo(long, long). If the class only has one method with that name, then specifying - * its parameter types is optional within the link tag. - */ - public boolean equalsIgnoringParam(ApiComponents other) { - return mPackageAndClassName.equals(other.mPackageAndClassName) && - mMemberName.equals(other.mMemberName); - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/ApiResolver.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/ApiResolver.java deleted file mode 100644 index 461bb69e3a..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/ApiResolver.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import com.google.common.base.Strings; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -public class ApiResolver { - private final List<ApiComponents> mPotentialPublicAlternatives; - private final Set<PackageAndClassName> mPublicApiClasses; - - private static final Pattern LINK_TAG_PATTERN = Pattern.compile("\\{@link ([^\\}]+)\\}"); - private static final Pattern CODE_TAG_PATTERN = Pattern.compile("\\{@code ([^\\}]+)\\}"); - private static final Integer MIN_SDK_REQUIRING_PUBLIC_ALTERNATIVES = 29; - - public ApiResolver() { - mPotentialPublicAlternatives = null; - mPublicApiClasses = null; - } - - public ApiResolver(Set<String> publicApis) { - mPotentialPublicAlternatives = publicApis.stream() - .map(api -> { - try { - return ApiComponents.fromDexSignature(api); - } catch (SignatureSyntaxError e) { - throw new RuntimeException("Could not parse public API signature:", e); - } - }) - .collect(Collectors.toList()); - mPublicApiClasses = mPotentialPublicAlternatives.stream() - .map(api -> api.getPackageAndClassName()) - .collect(Collectors.toCollection(HashSet::new)); - } - - /** - * Verify that all public alternatives are valid. - * - * @param publicAlternativesString String containing public alternative explanations. - * @param signature Signature of the member that has the annotation. - */ - public void resolvePublicAlternatives(String publicAlternativesString, String signature, - Integer maxSdkVersion) - throws JavadocLinkSyntaxError, AlternativeNotFoundError, - RequiredAlternativeNotSpecifiedError, MultipleAlternativesFoundWarning { - if (Strings.isNullOrEmpty(publicAlternativesString) && maxSdkVersion != null - && maxSdkVersion >= MIN_SDK_REQUIRING_PUBLIC_ALTERNATIVES) { - throw new RequiredAlternativeNotSpecifiedError(); - } - if (publicAlternativesString != null && mPotentialPublicAlternatives != null) { - // Grab all instances of type {@link foo} - Matcher matcher = LINK_TAG_PATTERN.matcher(publicAlternativesString); - boolean hasLinkAlternative = false; - // Validate all link tags - while (matcher.find()) { - hasLinkAlternative = true; - String alternativeString = matcher.group(1); - ApiComponents alternative = ApiComponents.fromLinkTag(alternativeString, - signature); - if (alternative.getMemberName().isEmpty()) { - // Provided class as alternative - if (!mPublicApiClasses.contains(alternative.getPackageAndClassName())) { - throw new ClassAlternativeNotFoundError(alternative); - } - } else if (!mPotentialPublicAlternatives.contains(alternative)) { - // If the link is not a public alternative, it must because the link does not - // contain the method parameter types, e.g. {@link foo.bar.Baz#foo} instead of - // {@link foo.bar.Baz#foo(int)}. If the method name is unique within the class, - // we can handle it. - if (!Strings.isNullOrEmpty(alternative.getMethodParameterTypes())) { - throw new MemberAlternativeNotFoundError(alternative); - } - List<ApiComponents> almostMatches = mPotentialPublicAlternatives.stream() - .filter(api -> api.equalsIgnoringParam(alternative)) - .collect(Collectors.toList()); - if (almostMatches.size() == 0) { - throw new MemberAlternativeNotFoundError(alternative); - } else if (almostMatches.size() > 1) { - throw new MultipleAlternativesFoundWarning(alternative, almostMatches); - } - } - } - // No {@link ...} alternatives exist; try looking for {@code ...} - if (!hasLinkAlternative) { - if (!CODE_TAG_PATTERN.matcher(publicAlternativesString).find()) { - throw new NoAlternativesSpecifiedError(); - } - } - } - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/Class2NonSdkList.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/Class2NonSdkList.java deleted file mode 100644 index 9be1e9b56a..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/Class2NonSdkList.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap.Builder; -import com.google.common.io.Files; - -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.GnuParser; -import org.apache.commons.cli.HelpFormatter; -import org.apache.commons.cli.OptionBuilder; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.ParseException; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * Build time tool for extracting a list of members from jar files that have the - * @UnsupportedAppUsage annotation, for building the non SDK API lists. - */ -public class Class2NonSdkList { - - private static final String UNSUPPORTED_APP_USAGE_ANNOTATION = - "android.compat.annotation.UnsupportedAppUsage"; - - private static final String FLAG_UNSUPPORTED = "unsupported"; - private static final String FLAG_BLOCKED = "blocked"; - private static final String FLAG_MAX_TARGET_O = "max-target-o"; - private static final String FLAG_MAX_TARGET_P = "max-target-p"; - private static final String FLAG_MAX_TARGET_Q = "max-target-q"; - private static final String FLAG_MAX_TARGET_R = "max-target-r"; - - private static final String FLAG_PUBLIC_API = "public-api"; - - private static final Map<Integer, String> TARGET_SDK_TO_LIST_MAP; - static { - Map<Integer, String> map = new HashMap<>(); - map.put(null, FLAG_UNSUPPORTED); - map.put(0, FLAG_BLOCKED); - map.put(26, FLAG_MAX_TARGET_O); - map.put(28, FLAG_MAX_TARGET_P); - map.put(29, FLAG_MAX_TARGET_Q); - map.put(30, FLAG_MAX_TARGET_R); - TARGET_SDK_TO_LIST_MAP = Collections.unmodifiableMap(map); - } - - private final Status mStatus; - private final String[] mJarFiles; - private final AnnotationConsumer mOutput; - private final Set<String> mPublicApis; - - public static void main(String[] args) { - Options options = new Options(); - options.addOption(OptionBuilder - .withLongOpt("stub-api-flags") - .hasArgs(1) - .withDescription("CSV file with API flags generated from public API stubs. " + - "Used to de-dupe bridge methods.") - .create("s")); - options.addOption(OptionBuilder - .withLongOpt("write-flags-csv") - .hasArgs(1) - .withDescription("Specify file to write hiddenapi flags to.") - .create('w')); - options.addOption(OptionBuilder - .withLongOpt("debug") - .hasArgs(0) - .withDescription("Enable debug") - .create("d")); - options.addOption(OptionBuilder - .withLongOpt("dump-all-members") - .withDescription("Dump all members from jar files to stdout. Ignore annotations. " + - "Do not use in conjunction with any other arguments.") - .hasArgs(0) - .create('m')); - options.addOption(OptionBuilder - .withLongOpt("write-metadata-csv") - .hasArgs(1) - .withDescription("Specify a file to write API metaadata to. This is a CSV file " + - "containing any annotation properties for all members. Do not use in " + - "conjunction with --write-flags-csv.") - .create('c')); - options.addOption(OptionBuilder - .withLongOpt("help") - .hasArgs(0) - .withDescription("Show this help") - .create('h')); - - CommandLineParser parser = new GnuParser(); - CommandLine cmd; - - try { - cmd = parser.parse(options, args); - } catch (ParseException e) { - System.err.println(e.getMessage()); - help(options); - return; - } - if (cmd.hasOption('h')) { - help(options); - } - - - String[] jarFiles = cmd.getArgs(); - if (jarFiles.length == 0) { - System.err.println("Error: no jar files specified."); - help(options); - } - - Status status = new Status(cmd.hasOption('d')); - - if (cmd.hasOption('m')) { - dumpAllMembers(status, jarFiles); - } else { - try { - Class2NonSdkList c2nsl = new Class2NonSdkList( - status, - cmd.getOptionValue('s', null), - cmd.getOptionValue('w', null), - cmd.getOptionValue('c', null), - jarFiles); - c2nsl.main(); - } catch (IOException e) { - status.error(e); - } - } - - if (status.ok()) { - System.exit(0); - } else { - System.exit(1); - } - - } - - private Class2NonSdkList(Status status, String stubApiFlagsFile, String csvFlagsFile, - String csvMetadataFile, String[] jarFiles) - throws IOException { - mStatus = status; - mJarFiles = jarFiles; - if (csvMetadataFile != null) { - mOutput = new AnnotationPropertyWriter(csvMetadataFile); - } else { - mOutput = new HiddenapiFlagsWriter(csvFlagsFile); - } - - if (stubApiFlagsFile != null) { - mPublicApis = - Files.readLines(new File(stubApiFlagsFile), StandardCharsets.UTF_8).stream() - .map(s -> Splitter.on(",").splitToList(s)) - .filter(s -> s.contains(FLAG_PUBLIC_API)) - .map(s -> s.get(0)) - .collect(Collectors.toSet()); - } else { - mPublicApis = Collections.emptySet(); - } - } - - private Map<String, AnnotationHandler> createAnnotationHandlers() { - Builder<String, AnnotationHandler> builder = ImmutableMap.builder(); - UnsupportedAppUsageAnnotationHandler greylistAnnotationHandler = - new UnsupportedAppUsageAnnotationHandler( - mStatus, mOutput, mPublicApis, TARGET_SDK_TO_LIST_MAP); - - addRepeatedAnnotationHandlers( - builder, - classNameToSignature(UNSUPPORTED_APP_USAGE_ANNOTATION), - classNameToSignature(UNSUPPORTED_APP_USAGE_ANNOTATION + "$Container"), - greylistAnnotationHandler); - - CovariantReturnTypeHandler covariantReturnTypeHandler = new CovariantReturnTypeHandler( - mOutput, mPublicApis, FLAG_PUBLIC_API); - - return addRepeatedAnnotationHandlers(builder, CovariantReturnTypeHandler.ANNOTATION_NAME, - CovariantReturnTypeHandler.REPEATED_ANNOTATION_NAME, covariantReturnTypeHandler) - .build(); - } - - private String classNameToSignature(String a) { - return "L" + a.replace('.', '/') + ";"; - } - - /** - * Add a handler for an annotation as well as an handler for the container annotation that is - * used when the annotation is repeated. - * - * @param builder the builder for the map to which the handlers will be added. - * @param annotationName the name of the annotation. - * @param containerAnnotationName the name of the annotation container. - * @param handler the handler for the annotation. - */ - private static Builder<String, AnnotationHandler> addRepeatedAnnotationHandlers( - Builder<String, AnnotationHandler> builder, - String annotationName, String containerAnnotationName, - AnnotationHandler handler) { - return builder - .put(annotationName, handler) - .put(containerAnnotationName, new RepeatedAnnotationHandler(annotationName, handler)); - } - - private void main() { - Map<String, AnnotationHandler> handlers = createAnnotationHandlers(); - for (String jarFile : mJarFiles) { - mStatus.debug("Processing jar file %s", jarFile); - try { - JarReader reader = new JarReader(mStatus, jarFile); - reader.stream().forEach(clazz -> new AnnotationVisitor(clazz, mStatus, handlers) - .visit()); - reader.close(); - } catch (IOException e) { - mStatus.error(e); - } - } - mOutput.close(); - } - - private static void dumpAllMembers(Status status, String[] jarFiles) { - for (String jarFile : jarFiles) { - status.debug("Processing jar file %s", jarFile); - try { - JarReader reader = new JarReader(status, jarFile); - reader.stream().forEach(clazz -> new MemberDumpingVisitor(clazz, status) - .visit()); - reader.close(); - } catch (IOException e) { - status.error(e); - } - } - } - - private static void help(Options options) { - new HelpFormatter().printHelp( - "class2nonsdklist path/to/classes.jar [classes2.jar ...]", - "Extracts nonsdk entries from classes jar files given", - options, null, true); - System.exit(1); - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/ClassAlternativeNotFoundError.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/ClassAlternativeNotFoundError.java deleted file mode 100644 index 49135bf793..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/ClassAlternativeNotFoundError.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -public class ClassAlternativeNotFoundError extends AlternativeNotFoundError { - public final ApiComponents alternative; - - ClassAlternativeNotFoundError(ApiComponents alternative) { - this.alternative = alternative; - } - - @Override - public String toString() { - return "Specified class " + alternative.getPackageAndClassName() + " does not exist!"; - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/CovariantReturnTypeHandler.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/CovariantReturnTypeHandler.java deleted file mode 100644 index df300c01a7..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/CovariantReturnTypeHandler.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.android.class2nonsdklist; - -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableSet; - -import org.apache.bcel.classfile.AnnotationEntry; -import org.apache.bcel.classfile.ElementValuePair; -import org.apache.bcel.classfile.Method; - -import java.util.Locale; -import java.util.Set; - -/** - * Handles {@code CovariantReturnType} annotations, generating whitelist - * entries from them. - * - * <p>A whitelist entry is generated with the same descriptor as the original - * method, but with the return type replaced with than specified by the - * {@link #RETURN_TYPE} property. - * - * <p>Methods are also validated against the public API list, to assert that - * the annotated method is already a public API. - */ -public class CovariantReturnTypeHandler extends AnnotationHandler { - - private static final String SHORT_NAME = "CovariantReturnType"; - public static final String ANNOTATION_NAME = "Ldalvik/annotation/codegen/CovariantReturnType;"; - public static final String REPEATED_ANNOTATION_NAME = - "Ldalvik/annotation/codegen/CovariantReturnType$CovariantReturnTypes;"; - - private static final String RETURN_TYPE = "returnType"; - - private final AnnotationConsumer mAnnotationConsumer; - private final Set<String> mPublicApis; - private final String mHiddenapiFlag; - - public CovariantReturnTypeHandler(AnnotationConsumer consumer, Set<String> publicApis, - String hiddenapiFlag) { - mAnnotationConsumer = consumer; - mPublicApis = publicApis; - mHiddenapiFlag = hiddenapiFlag; - } - - @Override - public void handleAnnotation(AnnotationEntry annotation, AnnotationContext context) { - if (context instanceof AnnotatedClassContext) { - return; - } - handleAnnotation(annotation, (AnnotatedMemberContext) context); - } - - private void handleAnnotation(AnnotationEntry annotation, AnnotatedMemberContext context) { - // Verify that the annotation has been applied to what we expect, and - // has the right form. Note, this should not strictly be necessary, as - // the annotation has a target of just 'method' and the property - // returnType does not have a default value, but checking makes the code - // less brittle to future changes. - if (!(context.member instanceof Method)) { - context.reportError("Cannot specify %s on a field", RETURN_TYPE); - return; - } - String returnType = findReturnType(annotation); - if (returnType == null) { - context.reportError("No %s set on @%s", RETURN_TYPE, SHORT_NAME); - return; - } - if (!mPublicApis.contains(context.getMemberDescriptor())) { - context.reportError("Found @%s on non-SDK method", SHORT_NAME); - return; - } - - // Generate the signature of overload that we expect the annotation will - // cause the platform dexer to create. - String typeSignature = context.member.getSignature(); - int closingBrace = typeSignature.indexOf(')'); - Preconditions.checkState(closingBrace != -1, - "No ) found in method type signature %s", typeSignature); - typeSignature = new StringBuilder() - .append(typeSignature.substring(0, closingBrace + 1)) - .append(returnType) - .toString(); - String signature = String.format(Locale.US, context.signatureFormatString, - context.getClassDescriptor(), context.member.getName(), typeSignature); - - if (mPublicApis.contains(signature)) { - context.reportError("Signature %s generated from @%s already exists as a public API", - signature, SHORT_NAME); - return; - } - - mAnnotationConsumer.consume(signature, stringifyAnnotationProperties(annotation), - ImmutableSet.of(mHiddenapiFlag)); - } - - private String findReturnType(AnnotationEntry a) { - for (ElementValuePair property : a.getElementValuePairs()) { - if (property.getNameString().equals(RETURN_TYPE)) { - return property.getValue().stringifyValue(); - } - } - // not found - return null; - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/HiddenapiFlagsWriter.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/HiddenapiFlagsWriter.java deleted file mode 100644 index 7c4fd113fc..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/HiddenapiFlagsWriter.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.android.class2nonsdklist; - -import com.google.common.annotations.VisibleForTesting; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class HiddenapiFlagsWriter implements AnnotationConsumer { - - private final PrintStream mOutput; - - public HiddenapiFlagsWriter(String csvFile) throws FileNotFoundException { - mOutput = new PrintStream(new FileOutputStream(new File(csvFile))); - } - - public void consume(String apiSignature, Map<String, String> annotationProperties, - Set<String> parsedFlags) { - if (parsedFlags.size() > 0) { - mOutput.println(apiSignature + "," + String.join(",", asSortedList(parsedFlags))); - } - } - - public void close() { - mOutput.close(); - } - - private static List<String> asSortedList(Set<String> s) { - List<String> list = new ArrayList<>(s); - Collections.sort(list); - return list; - } - -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/JarReader.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/JarReader.java deleted file mode 100644 index 8d512517f0..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/JarReader.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import org.apache.bcel.classfile.ClassParser; -import org.apache.bcel.classfile.JavaClass; - -import java.io.IOException; -import java.util.Objects; -import java.util.stream.Stream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -/** - * Reads {@link JavaClass} members from a zip/jar file, providing a stream of them for processing. - * Any errors are reported via {@link Status#error(Throwable)}. - */ -public class JarReader { - - private final Status mStatus; - private final String mFileName; - private final ZipFile mZipFile; - - public JarReader(Status s, String filename) throws IOException { - mStatus = s; - mFileName = filename; - mZipFile = new ZipFile(mFileName); - } - - private JavaClass openZipEntry(ZipEntry e) { - try { - mStatus.debug("Reading %s from %s", e.getName(), mFileName); - return new ClassParser(mZipFile.getInputStream(e), e.getName()).parse(); - } catch (IOException ioe) { - mStatus.error(ioe); - return null; - } - } - - - public Stream<JavaClass> stream() { - return mZipFile.stream() - .filter(zipEntry -> zipEntry.getName().endsWith(".class")) - .map(zipEntry -> openZipEntry(zipEntry)) - .filter(Objects::nonNull); - } - - public void close() throws IOException { - mZipFile.close(); - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/JavadocLinkSyntaxError.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/JavadocLinkSyntaxError.java deleted file mode 100644 index eaf78c225e..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/JavadocLinkSyntaxError.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -public class JavadocLinkSyntaxError extends Exception { - public final String expected; - public final int position; - public final String context; - - public JavadocLinkSyntaxError(String expected, StringCursor sc) { - super(expected + " at position " + sc.position() + " in " + sc.getOriginalString()); - this.expected = expected; - this.position = sc.position(); - this.context = sc.getOriginalString(); - } -} - diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/MemberAlternativeNotFoundError.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/MemberAlternativeNotFoundError.java deleted file mode 100644 index e0547a2962..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/MemberAlternativeNotFoundError.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -public class MemberAlternativeNotFoundError extends AlternativeNotFoundError { - public final ApiComponents alternative; - - MemberAlternativeNotFoundError(ApiComponents alternative) { - this.alternative = alternative; - } - - @Override - public String toString() { - return "Could not find public api " + alternative + "."; - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/MemberDumpingVisitor.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/MemberDumpingVisitor.java deleted file mode 100644 index e00af53bde..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/MemberDumpingVisitor.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.android.class2nonsdklist; - -import org.apache.bcel.classfile.DescendingVisitor; -import org.apache.bcel.classfile.EmptyVisitor; -import org.apache.bcel.classfile.Field; -import org.apache.bcel.classfile.FieldOrMethod; -import org.apache.bcel.classfile.JavaClass; -import org.apache.bcel.classfile.Method; - -/** - * A class file visitor that simply prints to stdout the signature of every member within the class. - */ -public class MemberDumpingVisitor extends EmptyVisitor { - - private final Status mStatus; - private final DescendingVisitor mDescendingVisitor; - - /** - * Creates a visitor for a class. - * - * @param clazz Class to visit - */ - public MemberDumpingVisitor(JavaClass clazz, Status status) { - mStatus = status; - mDescendingVisitor = new DescendingVisitor(clazz, this); - } - - public void visit() { - mDescendingVisitor.visit(); - } - - @Override - public void visitMethod(Method method) { - visitMember(method, "L%s;->%s%s"); - } - - @Override - public void visitField(Field field) { - visitMember(field, "L%s;->%s:%s"); - } - - private void visitMember(FieldOrMethod member, String signatureFormatString) { - AnnotationContext context = new AnnotatedMemberContext(mStatus, - (JavaClass) mDescendingVisitor.predecessor(), member, - signatureFormatString); - System.out.println(context.getMemberDescriptor()); - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/MultipleAlternativesFoundWarning.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/MultipleAlternativesFoundWarning.java deleted file mode 100644 index 738c906264..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/MultipleAlternativesFoundWarning.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import com.google.common.base.Joiner; - -import java.util.List; - -public class MultipleAlternativesFoundWarning extends Exception { - public final ApiComponents alternative; - public final List<ApiComponents> almostMatches; - - public MultipleAlternativesFoundWarning(ApiComponents alternative, - List<ApiComponents> almostMatches) { - this.alternative = alternative; - this.almostMatches = almostMatches; - } - - @Override - public String toString() { - return "Alternative " + alternative + " returned multiple matches. Consider adding method" + - " parameters to make the match unique. Matches: " - + Joiner.on(", ").join(almostMatches); - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/NoAlternativesSpecifiedError.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/NoAlternativesSpecifiedError.java deleted file mode 100644 index fbc1962555..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/NoAlternativesSpecifiedError.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -public class NoAlternativesSpecifiedError extends AlternativeNotFoundError { - - @Override - public String toString() { - return "Hidden API has a public alternative annotation field, but no concrete " - + "explanations. Please provide either a reference to an SDK method using javadoc " - + "syntax, e.g. {@link foo.bar.Baz#bat}, or a small code snippet if the " - + "alternative is part of a support library or third party library, e.g. " - + "{@code foo.bar.Baz bat = new foo.bar.Baz(); bat.doSomething();}.\n" - + "If this is too restrictive for your use case, please contact compat-team@."; - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/PackageAndClassName.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/PackageAndClassName.java deleted file mode 100644 index 7967cf5f03..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/PackageAndClassName.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import java.util.Objects; - -class PackageAndClassName{ - public String packageName; - public String className; - - private PackageAndClassName(String packageName, String className) { - this.packageName = packageName; - this.className = className; - } - - /** - * Given a potentially fully qualified class name, split it into package and class. - * - * @param fullyQualifiedClassName potentially fully qualified class name. - * @return A pair of strings, containing the package name (or empty if not specified) and - * the - * class name (or empty if string is empty). - */ - public static PackageAndClassName splitClassName(String fullyQualifiedClassName) { - int lastDotIdx = fullyQualifiedClassName.lastIndexOf('.'); - if (lastDotIdx == -1) { - return new PackageAndClassName("", fullyQualifiedClassName); - } - String packageName = fullyQualifiedClassName.substring(0, lastDotIdx); - String className = fullyQualifiedClassName.substring(lastDotIdx + 1); - return new PackageAndClassName(packageName, className); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof PackageAndClassName)) { - return false; - } - PackageAndClassName other = (PackageAndClassName) obj; - return Objects.equals(packageName, other.packageName) && Objects.equals(className, - other.className); - } - - @Override - public String toString() { - return packageName + "." + className; - } - - @Override - public int hashCode() { - return Objects.hash(packageName, className); - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/RepeatedAnnotationHandler.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/RepeatedAnnotationHandler.java deleted file mode 100644 index 5c796a57fb..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/RepeatedAnnotationHandler.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.android.class2nonsdklist; - -import com.google.common.base.Preconditions; -import org.apache.bcel.classfile.AnnotationElementValue; -import org.apache.bcel.classfile.AnnotationEntry; -import org.apache.bcel.classfile.ArrayElementValue; -import org.apache.bcel.classfile.ElementValue; -import org.apache.bcel.classfile.ElementValuePair; - -/** - * Handles a repeated annotation container. - * - * <p>The enclosed annotations are passed to the {@link #mWrappedHandler}. - */ -public class RepeatedAnnotationHandler extends AnnotationHandler { - - private static final String VALUE = "value"; - - private final AnnotationHandler mWrappedHandler; - private final String mInnerAnnotationName; - - RepeatedAnnotationHandler(String innerAnnotationName, AnnotationHandler wrappedHandler) { - mWrappedHandler = wrappedHandler; - mInnerAnnotationName = innerAnnotationName; - } - - @Override - public void handleAnnotation(AnnotationEntry annotation, AnnotationContext context) { - // Verify that the annotation has the form we expect - ElementValuePair value = findValue(annotation); - if (value == null) { - context.reportError("No value found on %s", annotation.getAnnotationType()); - return; - } - Preconditions.checkArgument(value.getValue() instanceof ArrayElementValue); - ArrayElementValue array = (ArrayElementValue) value.getValue(); - - // call wrapped handler on each enclosed annotation: - for (ElementValue v : array.getElementValuesArray()) { - Preconditions.checkArgument(v instanceof AnnotationElementValue); - AnnotationElementValue aev = (AnnotationElementValue) v; - Preconditions.checkArgument( - aev.getAnnotationEntry().getAnnotationType().equals(mInnerAnnotationName)); - mWrappedHandler.handleAnnotation(aev.getAnnotationEntry(), context); - } - } - - private ElementValuePair findValue(AnnotationEntry a) { - for (ElementValuePair property : a.getElementValuePairs()) { - if (property.getNameString().equals(VALUE)) { - return property; - } - } - // not found - return null; - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/RequiredAlternativeNotSpecifiedError.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/RequiredAlternativeNotSpecifiedError.java deleted file mode 100644 index 88c6f7da98..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/RequiredAlternativeNotSpecifiedError.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -/** - * Exception to be thrown when a greylisted private api gets restricted to max-FOO (where FOO is Q - * or later), without providing a public API alternative. - */ -public class RequiredAlternativeNotSpecifiedError extends Exception { - -} - diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/SignatureSyntaxError.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/SignatureSyntaxError.java deleted file mode 100644 index ca7a83e62f..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/SignatureSyntaxError.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -public class SignatureSyntaxError extends Exception { - public final String expected; - public final int position; - public final String context; - public SignatureSyntaxError(String expected, StringCursor sc) { - super(expected + " at position " + sc.position() + " in " + sc.getOriginalString()); - this.expected = expected; - this.position = sc.position(); - this.context = sc.getOriginalString(); - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/Status.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/Status.java deleted file mode 100644 index 1460a4f71a..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/Status.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import java.util.Locale; - -public class Status { - - // Highlight "Error:" in red. - private static final String ERROR = "\u001B[31mError: \u001B[0m"; - - // Highlight "Warning:" in yellow. - private static final String WARNING = "\u001B[33mWarning: \u001B[0m"; - - private final boolean mDebug; - private boolean mHasErrors; - - public Status(boolean debug) { - mDebug = debug; - } - - public void debug(String msg, Object... args) { - if (mDebug) { - System.err.println(String.format(Locale.US, msg, args)); - } - } - - public void error(Throwable t) { - System.err.print(ERROR); - t.printStackTrace(System.err); - mHasErrors = true; - } - - public void error(String message, Object... args) { - System.err.print(ERROR); - System.err.println(String.format(Locale.US, message, args)); - mHasErrors = true; - } - - public void warning(String message, Object... args) { - System.err.print(WARNING); - System.err.println(String.format(Locale.US, message, args)); - } - - public boolean ok() { - return !mHasErrors; - } -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/StatusReporter.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/StatusReporter.java deleted file mode 100644 index 52154909b8..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/StatusReporter.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -public interface StatusReporter { - /** - * Report an error in this context. The final error message will include - * the class and member names, and the source file name. - */ - void reportError(String message, Object... args); - - /** - * Report a warning in this context. The final message will include the - * class and member names, and the source file name. - */ - void reportWarning(String message, Object... args); -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/StringCursor.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/StringCursor.java deleted file mode 100644 index 45e10f4526..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/StringCursor.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -/** - * Utility class to simplify parsing of signatures. - */ -public class StringCursor { - - private final String mString; - private int mCursor; - - public StringCursor(String str) { - mString = str; - mCursor = 0; - } - - /** - * Position of cursor in string. - * - * @return Current position of cursor in string. - */ - public int position() { - return mCursor; - } - - /** - * Peek current cursor position. - * - * @return The character at the current cursor position. - */ - public char peek() { - return mString.charAt(mCursor); - } - - /** - * Peek several characters at the current cursor position without moving the cursor. - * - * @param n The number of characters to peek. - * @return A string with x characters from the cursor position. If n is -1, return the whole - * rest of the string. - */ - public String peek(int n) throws StringCursorOutOfBoundsException { - if (n == -1) { - return mString.substring(mCursor); - } - if (n < 0 || (n + mCursor) >= mString.length()) { - throw new StringCursorOutOfBoundsException(); - } - return mString.substring(mCursor, mCursor + n); - } - - /** - * Consume the character at the current cursor position and move the cursor forwards. - * - * @return The character at the current cursor position. - */ - public char next() throws StringCursorOutOfBoundsException { - if (!hasNext()) { - throw new StringCursorOutOfBoundsException(); - } - return mString.charAt(mCursor++); - } - - /** - * Consume several characters at the current cursor position and move the cursor further along. - * - * @param n The number of characters to consume. - * @return A string with x characters from the cursor position. If n is -1, return the whole - * rest of the string. - */ - public String next(int n) throws StringCursorOutOfBoundsException { - if (n == -1) { - String restOfString = mString.substring(mCursor); - mCursor = mString.length(); - return restOfString; - } - if (n < 0) { - throw new StringCursorOutOfBoundsException(); - } - mCursor += n; - return mString.substring(mCursor - n, mCursor); - } - - /** - * Search for the first occurrence of a character beyond the current cursor position. - * - * @param c The character to search for. - * @return The offset of the first occurrence of c in the string beyond the cursor position. - * If the character does not exist, return -1. - */ - public int find(char c) { - int firstIndex = mString.indexOf(c, mCursor); - if (firstIndex == -1) { - return -1; - } - return firstIndex - mCursor; - } - - /** - * Check if cursor has reached end of string. - * - * @return Cursor has reached end of string. - */ - public boolean hasNext() { - return mCursor < mString.length(); - } - - @Override - public String toString() { - return mString.substring(mCursor); - } - - public String getOriginalString() { - return mString; - } -}
\ No newline at end of file diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/StringCursorOutOfBoundsException.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/StringCursorOutOfBoundsException.java deleted file mode 100644 index 1c56d5e362..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/StringCursorOutOfBoundsException.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -public class StringCursorOutOfBoundsException extends IndexOutOfBoundsException { - -} diff --git a/tools/class2nonsdklist/src/com/android/class2nonsdklist/UnsupportedAppUsageAnnotationHandler.java b/tools/class2nonsdklist/src/com/android/class2nonsdklist/UnsupportedAppUsageAnnotationHandler.java deleted file mode 100644 index a213d51a2f..0000000000 --- a/tools/class2nonsdklist/src/com/android/class2nonsdklist/UnsupportedAppUsageAnnotationHandler.java +++ /dev/null @@ -1,207 +0,0 @@ -package com.android.class2nonsdklist; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableSet; - -import org.apache.bcel.Const; -import org.apache.bcel.classfile.AnnotationEntry; -import org.apache.bcel.classfile.ElementValue; -import org.apache.bcel.classfile.ElementValuePair; -import org.apache.bcel.classfile.FieldOrMethod; -import org.apache.bcel.classfile.Method; -import org.apache.bcel.classfile.SimpleElementValue; - -import java.util.Map; -import java.util.Set; -import java.util.function.Predicate; - -/** - * Processes {@code UnsupportedAppUsage} annotations to generate greylist - * entries. - * - * Any annotations with a {@link #EXPECTED_SIGNATURE_PROPERTY} property will have their - * generated signature verified against this, and an error will be reported if - * it does not match. Exclusions are made for bridge methods. - * - * Any {@link #MAX_TARGET_SDK_PROPERTY} properties will be validated against the given - * set of valid values, then passed through to the greylist consumer. - */ -public class UnsupportedAppUsageAnnotationHandler extends AnnotationHandler { - - // properties of greylist annotations: - private static final String EXPECTED_SIGNATURE_PROPERTY = "expectedSignature"; - private static final String MAX_TARGET_SDK_PROPERTY = "maxTargetSdk"; - private static final String IMPLICIT_MEMBER_PROPERTY = "implicitMember"; - private static final String PUBLIC_ALTERNATIVES_PROPERTY = "publicAlternatives"; - private static final String TRACKING_BUG_PROPERTY = "trackingBug"; - // we are temporarilly treating this bug id as if it is specified with maxTargetSdk=0 - private static final Long RESTRICT_UNUSED_APIS_BUG = 170729553L; - private static final Integer SDK_VERSION_R = 30; - - private final Status mStatus; - private final Predicate<ClassMember> mClassMemberFilter; - private final Map<Integer, String> mSdkVersionToFlagMap; - private final AnnotationConsumer mAnnotationConsumer; - - private ApiResolver mApiResolver; - - /** - * Represents a member of a class file (a field or method). - */ - @VisibleForTesting - public static class ClassMember { - - /** - * Signature of this class member. - */ - public final String signature; - - /** - * Indicates if this is a synthetic bridge method. - */ - public final boolean isBridgeMethod; - - public ClassMember(String signature, boolean isBridgeMethod) { - this.signature = signature; - this.isBridgeMethod = isBridgeMethod; - } - } - - public UnsupportedAppUsageAnnotationHandler(Status status, - AnnotationConsumer annotationConsumer, Set<String> publicApis, - Map<Integer, String> sdkVersionToFlagMap) { - this(status, annotationConsumer, - member -> !(member.isBridgeMethod && publicApis.contains(member.signature)), - sdkVersionToFlagMap); - mApiResolver = new ApiResolver(publicApis); - } - - @VisibleForTesting - public UnsupportedAppUsageAnnotationHandler(Status status, - AnnotationConsumer annotationConsumer, Predicate<ClassMember> memberFilter, - Map<Integer, String> sdkVersionToFlagMap) { - mStatus = status; - mAnnotationConsumer = annotationConsumer; - mClassMemberFilter = memberFilter; - mSdkVersionToFlagMap = sdkVersionToFlagMap; - mApiResolver = new ApiResolver(); - } - - @Override - public void handleAnnotation(AnnotationEntry annotation, AnnotationContext context) { - boolean isBridgeMethod = false; - if (context instanceof AnnotatedMemberContext) { - AnnotatedMemberContext memberContext = (AnnotatedMemberContext) context; - FieldOrMethod member = memberContext.member; - isBridgeMethod = (member instanceof Method) && - (member.getAccessFlags() & Const.ACC_BRIDGE) != 0; - if (isBridgeMethod) { - mStatus.debug("Member is a bridge method"); - } - } - - String signature = context.getMemberDescriptor(); - Integer maxTargetSdk = null; - String implicitMemberSignature = null; - String publicAlternativesString = null; - Long trackingBug = null; - - for (ElementValuePair property : annotation.getElementValuePairs()) { - switch (property.getNameString()) { - case EXPECTED_SIGNATURE_PROPERTY: - String expected = property.getValue().stringifyValue(); - // Don't enforce for bridge methods; they're generated so won't match. - if (!isBridgeMethod && !signature.equals(expected)) { - context.reportError("Expected signature does not match generated:\n" - + "Expected: %s\n" - + "Generated: %s", expected, signature); - return; - } - break; - case MAX_TARGET_SDK_PROPERTY: - if (property.getValue().getElementValueType() != ElementValue.PRIMITIVE_INT) { - context.reportError("Expected property %s to be of type int; got %d", - property.getNameString(), - property.getValue().getElementValueType()); - return; - } - - maxTargetSdk = ((SimpleElementValue) property.getValue()).getValueInt(); - break; - case IMPLICIT_MEMBER_PROPERTY: - implicitMemberSignature = property.getValue().stringifyValue(); - if (context instanceof AnnotatedClassContext) { - signature = String.format("L%s;->%s", - context.getClassDescriptor(), implicitMemberSignature); - } else { - context.reportError( - "Expected annotation with an %s property to be on a class but is " - + "on %s", - IMPLICIT_MEMBER_PROPERTY, - signature); - return; - } - break; - case PUBLIC_ALTERNATIVES_PROPERTY: - publicAlternativesString = property.getValue().stringifyValue(); - break; - case TRACKING_BUG_PROPERTY: - if (property.getValue().getElementValueType() != ElementValue.PRIMITIVE_LONG) { - context.reportError("Expected property %s to be of type long; got %d", - property.getNameString(), - property.getValue().getElementValueType()); - return; - } - trackingBug = ((SimpleElementValue) property.getValue()).getValueLong(); - break; - } - } - - boolean isSpecialTrackingBug = RESTRICT_UNUSED_APIS_BUG.equals(trackingBug) && - SDK_VERSION_R.equals(maxTargetSdk); - if (isSpecialTrackingBug) { - maxTargetSdk = 0; - } - - if (context instanceof AnnotatedClassContext && implicitMemberSignature == null) { - context.reportError( - "Missing property %s on annotation on class %s", - IMPLICIT_MEMBER_PROPERTY, - signature); - return; - } - - // Verify that maxTargetSdk is valid. - if (!mSdkVersionToFlagMap.containsKey(maxTargetSdk)) { - context.reportError("Invalid value for %s: got %d, expected one of [%s]", - MAX_TARGET_SDK_PROPERTY, - maxTargetSdk, - mSdkVersionToFlagMap.keySet()); - return; - } - - try { - mApiResolver.resolvePublicAlternatives(publicAlternativesString, signature, - maxTargetSdk); - } catch (MultipleAlternativesFoundWarning e) { - context.reportWarning(e.toString()); - } catch (JavadocLinkSyntaxError | AlternativeNotFoundError e) { - context.reportError(e.toString()); - } catch (RequiredAlternativeNotSpecifiedError e) { - context.reportError("Signature %s moved to %s without specifying public " - + "alternatives; Refer to go/unsupportedappusage-public-alternatives " - + "for details.", - signature, mSdkVersionToFlagMap.get(maxTargetSdk)); - } - - // Consume this annotation if it matches the predicate. - if (mClassMemberFilter.test(new ClassMember(signature, isBridgeMethod))) { - Map<String, String> annotationProperties = stringifyAnnotationProperties(annotation); - if (isSpecialTrackingBug) { - annotationProperties.put(MAX_TARGET_SDK_PROPERTY, "0"); - } - mAnnotationConsumer.consume(signature, annotationProperties, - ImmutableSet.of(mSdkVersionToFlagMap.get(maxTargetSdk))); - } - } -} diff --git a/tools/class2nonsdklist/test/Android.bp b/tools/class2nonsdklist/test/Android.bp deleted file mode 100644 index bd4143c901..0000000000 --- a/tools/class2nonsdklist/test/Android.bp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2018 The Android Open Source Project -// -// 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. - -java_test_host { - name: "class2nonsdklisttest", - - // Only compile source java files in this apk. - srcs: ["src/**/*.java"], - - static_libs: [ - "class2nonsdklistlib", - "libjavac", - "truth-host-prebuilt", - "mockito-host", - "junit-host", - "objenesis", - ], -} diff --git a/tools/class2nonsdklist/test/AndroidTest.xml b/tools/class2nonsdklist/test/AndroidTest.xml deleted file mode 100644 index 9616c62575..0000000000 --- a/tools/class2nonsdklist/test/AndroidTest.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2018 The Android Open Source Project - - 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. ---> -<configuration description="class2nonsdklist tests"> - <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" > - <option name="jar" value="class2nonsdklisttest.jar" /> - <option name="runtime-hint" value="1m" /> - </test> -</configuration>
\ No newline at end of file diff --git a/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/AnnotationHandlerTestBase.java b/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/AnnotationHandlerTestBase.java deleted file mode 100644 index fa779287e6..0000000000 --- a/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/AnnotationHandlerTestBase.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.withSettings; - -import com.android.javac.Javac; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.rules.TestName; - -import java.io.IOException; - -public class AnnotationHandlerTestBase { - - @Rule - public TestName mTestName = new TestName(); - - protected Javac mJavac; - protected AnnotationConsumer mConsumer; - protected Status mStatus; - - @Before - public void baseSetup() throws IOException { - System.out.println(String.format("\n============== STARTING TEST: %s ==============\n", - mTestName.getMethodName())); - mConsumer = mock(AnnotationConsumer.class); - mStatus = mock(Status.class, withSettings().verboseLogging()); - mJavac = new Javac(); - } - - protected void assertNoErrors() { - verify(mStatus, never()).error(any(Throwable.class)); - verify(mStatus, never()).error(any(), any()); - } -} diff --git a/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/AnnotationPropertyWriterTest.java b/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/AnnotationPropertyWriterTest.java deleted file mode 100644 index 4cdf017f40..0000000000 --- a/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/AnnotationPropertyWriterTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.common.collect.ImmutableMap; - -import org.junit.Before; -import org.junit.Test; - -import java.io.ByteArrayOutputStream; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - - -public class AnnotationPropertyWriterTest { - - private ByteArrayOutputStream mByteArrayOutputStream; - private AnnotationPropertyWriter mAnnotationPropertyWriter; - - @Before - public void setup() { - mByteArrayOutputStream = new ByteArrayOutputStream(); - mAnnotationPropertyWriter = new AnnotationPropertyWriter(mByteArrayOutputStream); - } - - @Test - public void testExportPropertiesNoEscaping() { - String signature = "foo"; - Map<String, String> annotationProperties = ImmutableMap.of( - "prop", "val" - ); - Set<String> parsedFlags = new HashSet<String>(); - mAnnotationPropertyWriter.consume(signature, annotationProperties, parsedFlags); - mAnnotationPropertyWriter.close(); - - String output = mByteArrayOutputStream.toString(); - String expected = "prop,signature\n" - + "|val|,|foo|\n"; - assertThat(output).isEqualTo(expected); - } - - @Test - public void testExportPropertiesEscapeQuotes() { - String signature = "foo"; - Map<String, String> annotationProperties = ImmutableMap.of( - "prop", "val1 | val2 | val3" - ); - Set<String> parsedFlags = new HashSet<String>(); - mAnnotationPropertyWriter.consume(signature, annotationProperties, parsedFlags); - mAnnotationPropertyWriter.close(); - - String output = mByteArrayOutputStream.toString(); - String expected = "prop,signature\n" - + "|val1 || val2 || val3|,|foo|\n"; - assertThat(output).isEqualTo(expected); - } -} diff --git a/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/ApiComponentsTest.java b/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/ApiComponentsTest.java deleted file mode 100644 index 61f502a081..0000000000 --- a/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/ApiComponentsTest.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import static com.google.common.truth.Truth.assertThat; - -import static org.testng.Assert.assertThrows; - -import org.junit.Test; - - -public class ApiComponentsTest extends AnnotationHandlerTestBase { - - @Test - public void testGetApiComponentsPackageFromSignature() throws SignatureSyntaxError { - ApiComponents api = ApiComponents.fromDexSignature("La/b/C;->foo()V"); - PackageAndClassName packageAndClassName = api.getPackageAndClassName(); - assertThat(packageAndClassName.packageName).isEqualTo("a.b"); - } - - @Test - public void testGetApiComponentsFromSignature() throws SignatureSyntaxError { - ApiComponents api = ApiComponents.fromDexSignature("La/b/C;->foo(IJLfoo2/bar/Baz;)V"); - PackageAndClassName packageAndClassName = api.getPackageAndClassName(); - assertThat(packageAndClassName.className).isEqualTo("C"); - assertThat(api.getMemberName()).isEqualTo("foo"); - assertThat(api.getMethodParameterTypes()).isEqualTo("int, long, foo2.bar.Baz"); - } - - @Test - public void testInvalidDexSignatureInvalidClassFormat() throws SignatureSyntaxError { - assertThrows(SignatureSyntaxError.class, () -> { - ApiComponents.fromDexSignature("a/b/C;->foo()V"); - }); - assertThrows(SignatureSyntaxError.class, () -> { - ApiComponents.fromDexSignature("La/b/C->foo()V"); - }); - } - - @Test - public void testInvalidDexSignatureInvalidParameterType() throws SignatureSyntaxError { - assertThrows(SignatureSyntaxError.class, () -> { - ApiComponents.fromDexSignature("a/b/C;->foo(foo)V"); - }); - } - - @Test - public void testInvalidDexSignatureInvalidReturnType() throws SignatureSyntaxError { - assertThrows(SignatureSyntaxError.class, () -> { - ApiComponents.fromDexSignature("a/b/C;->foo()foo"); - }); - } - - @Test - public void testInvalidDexSignatureMissingReturnType() throws SignatureSyntaxError { - assertThrows(SignatureSyntaxError.class, () -> { - ApiComponents.fromDexSignature("a/b/C;->foo(I)"); - }); - } - - @Test - public void testInvalidDexSignatureMissingArrowOrColon() throws SignatureSyntaxError { - assertThrows(SignatureSyntaxError.class, () -> { - ApiComponents.fromDexSignature("La/b/C;foo()V"); - }); - } - - @Test - public void testGetApiComponentsFromFieldLink() throws JavadocLinkSyntaxError { - ApiComponents api = ApiComponents.fromLinkTag("a.b.C#foo(int, long, foo2.bar.Baz)", - "La/b/C;->foo:I"); - PackageAndClassName packageAndClassName = api.getPackageAndClassName(); - assertThat(packageAndClassName.packageName).isEqualTo("a.b"); - assertThat(packageAndClassName.className).isEqualTo("C"); - assertThat(api.getMemberName()).isEqualTo("foo"); - } - - @Test - public void testGetApiComponentsLinkOnlyClass() throws JavadocLinkSyntaxError { - ApiComponents api = ApiComponents.fromLinkTag("b.c.D", "La/b/C;->foo:I"); - PackageAndClassName packageAndClassName = api.getPackageAndClassName(); - assertThat(packageAndClassName.packageName).isEqualTo("b.c"); - assertThat(packageAndClassName.className).isEqualTo("D"); - assertThat(api.getMethodParameterTypes()).isEqualTo(""); - } - - @Test - public void testGetApiComponentsFromLinkOnlyClassDeducePackage() throws JavadocLinkSyntaxError { - ApiComponents api = ApiComponents.fromLinkTag("D", "La/b/C;->foo:I"); - PackageAndClassName packageAndClassName = api.getPackageAndClassName(); - assertThat(packageAndClassName.packageName).isEqualTo("a.b"); - assertThat(packageAndClassName.className).isEqualTo("D"); - assertThat(api.getMemberName().isEmpty()).isTrue(); - assertThat(api.getMethodParameterTypes().isEmpty()).isTrue(); - } - - @Test - public void testGetApiComponentsParametersFromMethodLink() throws JavadocLinkSyntaxError { - ApiComponents api = ApiComponents.fromLinkTag("a.b.C#foo(int, long, foo2.bar.Baz)", - "La/b/C;->foo:I"); - assertThat(api.getMethodParameterTypes()).isEqualTo("int, long, foo2.bar.Baz"); - } - - @Test - public void testDeduceApiComponentsPackageFromLinkUsingContext() throws JavadocLinkSyntaxError { - ApiComponents api = ApiComponents.fromLinkTag("C#foo(int, long, foo2.bar.Baz)", - "La/b/C;->foo:I"); - PackageAndClassName packageAndClassName = api.getPackageAndClassName(); - assertThat(packageAndClassName.packageName).isEqualTo("a.b"); - } - - @Test - public void testDeduceApiComponentsPackageAndClassFromLinkUsingContext() - throws JavadocLinkSyntaxError { - ApiComponents api = ApiComponents.fromLinkTag("#foo(int, long, foo2.bar.Baz)", - "La/b/C;->foo:I"); - PackageAndClassName packageAndClassName = api.getPackageAndClassName(); - assertThat(packageAndClassName.packageName).isEqualTo("a.b"); - assertThat(packageAndClassName.className).isEqualTo("C"); - } - - @Test - public void testInvalidLinkTagUnclosedParenthesis() throws JavadocLinkSyntaxError { - assertThrows(JavadocLinkSyntaxError.class, () -> { - ApiComponents.fromLinkTag("a.b.C#foo(int,float", "La/b/C;->foo()V"); - }); - } - -}
\ No newline at end of file diff --git a/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/ApiResolverTest.java b/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/ApiResolverTest.java deleted file mode 100644 index b51efcda86..0000000000 --- a/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/ApiResolverTest.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import static com.google.common.truth.Truth.assertThat; - -import static org.testng.Assert.expectThrows; -import static org.testng.Assert.assertThrows; - -import org.junit.Test; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -public class ApiResolverTest extends AnnotationHandlerTestBase { - @Test - public void testFindPublicAlternativeExactly() throws Exception { - Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>( - Arrays.asList("La/b/C;->foo(I)V", "La/b/C;->bar(I)V"))); - ApiResolver resolver = new ApiResolver(publicApis); - resolver.resolvePublicAlternatives("{@link a.b.C#foo(int)}", "Lb/c/D;->bar()V", 1); - } - - @Test - public void testFindPublicAlternativeDeducedPackageName() throws Exception { - Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>( - Arrays.asList("La/b/C;->foo(I)V", "La/b/C;->bar(I)V"))); - ApiResolver resolver = new ApiResolver(publicApis); - resolver.resolvePublicAlternatives("{@link C#foo(int)}", "La/b/D;->bar()V", 1); - } - - @Test - public void testFindPublicAlternativeDeducedPackageAndClassName() throws Exception { - Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>( - Arrays.asList("La/b/C;->foo(I)V", "La/b/C;->bar(I)V"))); - ApiResolver resolver = new ApiResolver(publicApis); - resolver.resolvePublicAlternatives("{@link #foo(int)}", "La/b/C;->bar()V", 1); - } - - @Test - public void testFindPublicAlternativeDeducedParameterTypes() throws Exception { - Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>( - Arrays.asList("La/b/C;->foo(I)V", "La/b/C;->bar(I)V"))); - ApiResolver resolver = new ApiResolver(publicApis); - resolver.resolvePublicAlternatives("{@link #foo}", "La/b/C;->bar()V", 1); - } - - @Test - public void testFindPublicAlternativeWarnsOnMultipleParameterTypes() - throws SignatureSyntaxError { - Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>( - Arrays.asList("La/b/C;->foo(I)V", "La/b/C;->bar(I)I", "La/b/C;->foo(II)V"))); - ApiResolver resolver = new ApiResolver(publicApis); - MultipleAlternativesFoundWarning e = expectThrows(MultipleAlternativesFoundWarning.class, - () -> resolver.resolvePublicAlternatives("{@link #foo}", "La/b/C;->bar()V", 1)); - assertThat(e.almostMatches).containsExactly( - ApiComponents.fromDexSignature("La/b/C;->foo(I)V"), - ApiComponents.fromDexSignature("La/b/C;->foo(II)V") - ); - } - - @Test - public void testFindPublicAlternativeFailNoAlternative() { - Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>( - Arrays.asList("La/b/C;->bar(I)V"))); - ApiResolver resolver = new ApiResolver(publicApis); - assertThrows(MemberAlternativeNotFoundError.class, () - -> resolver.resolvePublicAlternatives("{@link #foo(int)}", "La/b/C;->bar()V", 1)); - } - - @Test - public void testFindPublicAlternativeFailNoAlternativeNoParameterTypes() { - - Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>( - Arrays.asList("La/b/C;->bar(I)V"))); - ApiResolver resolver = new ApiResolver(publicApis); - assertThrows(MemberAlternativeNotFoundError.class, - () -> resolver.resolvePublicAlternatives("{@link #foo}", "La/b/C;->bar()V", 1)); - } - - @Test - public void testNoPublicClassAlternatives() { - Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>()); - ApiResolver resolver = new ApiResolver(publicApis); - expectThrows(NoAlternativesSpecifiedError.class, - () -> resolver.resolvePublicAlternatives("Foo", "La/b/C;->bar()V", 1)); - } - - @Test - public void testPublicAlternativesJustPackageAndClassName() throws Exception { - Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>( - Arrays.asList("La/b/C;->bar(I)V"))); - ApiResolver resolver = new ApiResolver(publicApis); - resolver.resolvePublicAlternatives("Foo {@link a.b.C}", "Lb/c/D;->bar()V", 1); - } - - @Test - public void testPublicAlternativesJustClassName() throws Exception { - Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>( - Arrays.asList("La/b/C;->bar(I)V"))); - ApiResolver resolver = new ApiResolver(publicApis); - resolver.resolvePublicAlternatives("Foo {@link C}", "La/b/D;->bar()V", 1); - } - - @Test - public void testNoPublicAlternativesButHasExplanation() throws Exception { - Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>()); - ApiResolver resolver = new ApiResolver(publicApis); - resolver.resolvePublicAlternatives("Foo {@code bar}", "La/b/C;->bar()V", 1); - } - - @Test - public void testNoPublicAlternativesSpecifiedWithMaxSdk() { - Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>()); - ApiResolver resolver = new ApiResolver(publicApis); - assertThrows(RequiredAlternativeNotSpecifiedError.class, - () -> resolver.resolvePublicAlternatives(null, "La/b/C;->bar()V", 29)); - } - - @Test - public void testNoPublicAlternativesSpecifiedWithMaxLessThanQ() throws Exception { - Set<String> publicApis = Collections.unmodifiableSet(new HashSet<>()); - ApiResolver resolver = new ApiResolver(publicApis); - resolver.resolvePublicAlternatives(null, "La/b/C;->bar()V", 28); - } - -}
\ No newline at end of file diff --git a/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/CovariantReturnTypeHandlerTest.java b/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/CovariantReturnTypeHandlerTest.java deleted file mode 100644 index aa87148d44..0000000000 --- a/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/CovariantReturnTypeHandlerTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import static java.util.Collections.emptySet; - -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; - -import java.io.IOException; -import java.util.Map; - -public class CovariantReturnTypeHandlerTest extends AnnotationHandlerTestBase { - - private static final String ANNOTATION = "Lannotation/Annotation;"; - private static final String FLAG = "test-flag"; - - @Before - public void setup() throws IOException { - // To keep the test simpler and more concise, we don't use the real - // @CovariantReturnType annotation here, but use our own @Annotation. - // It doesn't have to match the real annotation, just have the same - // property (returnType). - mJavac.addSource("annotation.Annotation", Joiner.on('\n').join( - "package annotation;", - "import static java.lang.annotation.RetentionPolicy.CLASS;", - "import java.lang.annotation.Retention;", - "@Retention(CLASS)", - "public @interface Annotation {", - " Class<?> returnType();", - "}")); - } - - @Test - public void testReturnTypeWhitelisted() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Annotation;", - "public class Class {", - " @Annotation(returnType=Integer.class)", - " public String method() {return null;}", - "}")); - mJavac.compile(); - - Map<String, AnnotationHandler> handlerMap = - ImmutableMap.of(ANNOTATION, - new CovariantReturnTypeHandler( - mConsumer, - ImmutableSet.of("La/b/Class;->method()Ljava/lang/String;"), - FLAG)); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit(); - - assertNoErrors(); - verify(mConsumer, times(1)).consume( - eq("La/b/Class;->method()Ljava/lang/Integer;"), any(), eq(ImmutableSet.of(FLAG))); - } - - @Test - public void testAnnotatedMemberNotPublicApi() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Annotation;", - "public class Class {", - " @Annotation(returnType=Integer.class)", - " public String method() {return null;}", - "}")); - mJavac.compile(); - - Map<String, AnnotationHandler> handlerMap = - ImmutableMap.of(ANNOTATION, - new CovariantReturnTypeHandler( - mConsumer, - emptySet(), - FLAG)); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit(); - - verify(mStatus, atLeastOnce()).error(any(), any()); - } - - @Test - public void testReturnTypeAlreadyWhitelisted() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Annotation;", - "public class Class {", - " @Annotation(returnType=Integer.class)", - " public String method() {return null;}", - "}")); - mJavac.compile(); - - Map<String, AnnotationHandler> handlerMap = - ImmutableMap.of(ANNOTATION, - new CovariantReturnTypeHandler( - mConsumer, - ImmutableSet.of( - "La/b/Class;->method()Ljava/lang/String;", - "La/b/Class;->method()Ljava/lang/Integer;" - ), - FLAG)); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit(); - - verify(mStatus, atLeastOnce()).error(any(), any()); - } - - @Test - public void testAnnotationOnField() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Annotation;", - "public class Class {", - " @Annotation(returnType=Integer.class)", - " public String field;", - "}")); - mJavac.compile(); - - Map<String, AnnotationHandler> handlerMap = - ImmutableMap.of(ANNOTATION, - new CovariantReturnTypeHandler( - mConsumer, - emptySet(), - FLAG)); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit(); - - verify(mStatus, atLeastOnce()).error(any(), any()); - } -} diff --git a/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/RepeatedAnnotationHandlerTest.java b/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/RepeatedAnnotationHandlerTest.java deleted file mode 100644 index 6d1df31d3a..0000000000 --- a/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/RepeatedAnnotationHandlerTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableMap; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import org.apache.bcel.classfile.AnnotationEntry; -import org.junit.Before; -import org.junit.Test; - -public class RepeatedAnnotationHandlerTest extends AnnotationHandlerTestBase { - - @Before - public void setup() { - // To keep the test simpler and more concise, we don't use a real annotation here, but use - // our own @Annotation and @Annotation.Multi that have the same relationship. - mJavac.addSource("annotation.Annotation", Joiner.on('\n').join( - "package annotation;", - "import static java.lang.annotation.RetentionPolicy.CLASS;", - "import java.lang.annotation.Repeatable;", - "import java.lang.annotation.Retention;", - "@Repeatable(Annotation.Multi.class)", - "@Retention(CLASS)", - "public @interface Annotation {", - " Class<?> clazz();", - " @Retention(CLASS)", - " @interface Multi {", - " Annotation[] value();", - " }", - "}")); - } - - @Test - public void testRepeated() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Annotation;", - "public class Class {", - " @Annotation(clazz=Integer.class)", - " @Annotation(clazz=Long.class)", - " public String method() {return null;}", - "}")); - mJavac.compile(); - - TestAnnotationHandler handler = new TestAnnotationHandler(); - Map<String, AnnotationHandler> handlerMap = - ImmutableMap.of("Lannotation/Annotation$Multi;", - new RepeatedAnnotationHandler("Lannotation/Annotation;", handler)); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit(); - - assertNoErrors(); - assertThat(handler.getClasses()).containsExactly( - "Ljava/lang/Integer;", - "Ljava/lang/Long;"); - } - - private static class TestAnnotationHandler extends AnnotationHandler { - - private final List<String> classes; - - private TestAnnotationHandler() { - this.classes = new ArrayList<>(); - } - - @Override - void handleAnnotation(AnnotationEntry annotation, - AnnotationContext context) { - classes.add(annotation.getElementValuePairs()[0].getValue().stringifyValue()); - } - - private List<String> getClasses() { - return classes; - } - } -} diff --git a/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/UnsupportedAppUsageAnnotationHandlerTest.java b/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/UnsupportedAppUsageAnnotationHandlerTest.java deleted file mode 100644 index 7e7d22cf7c..0000000000 --- a/tools/class2nonsdklist/test/src/com/android/class2nonsdklist/UnsupportedAppUsageAnnotationHandlerTest.java +++ /dev/null @@ -1,614 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * 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.android.class2nonsdklist; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import static java.util.Collections.emptyMap; - -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; - -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.function.Predicate; - -public class UnsupportedAppUsageAnnotationHandlerTest extends AnnotationHandlerTestBase { - - private static final String ANNOTATION = "Lannotation/Anno;"; - - private static final Map<Integer, String> NULL_SDK_MAP; - static { - Map<Integer, String> map = new HashMap<>(); - map.put(null, "flag-null"); - NULL_SDK_MAP = Collections.unmodifiableMap(map); - } - - @Before - public void setup() throws IOException { - mJavac.addSource("annotation.Anno", Joiner.on('\n').join( - "package annotation;", - "import static java.lang.annotation.RetentionPolicy.CLASS;", - "import java.lang.annotation.Retention;", - "import java.lang.annotation.Repeatable;", - "@Retention(CLASS)", - "@Repeatable(Anno.Container.class)", - "public @interface Anno {", - " String expectedSignature() default \"\";", - " int maxTargetSdk() default Integer.MAX_VALUE;", - " String implicitMember() default \"\";", - " @Retention(CLASS)", - " public @interface Container {", - " Anno[] value();", - " }", - "}")); - } - - private UnsupportedAppUsageAnnotationHandler createGreylistHandler( - Predicate<UnsupportedAppUsageAnnotationHandler.ClassMember> greylistFilter, - Map<Integer, String> validMaxTargetSdkValues) { - return new UnsupportedAppUsageAnnotationHandler( - mStatus, mConsumer, greylistFilter, validMaxTargetSdkValues); - } - - @Test - public void testGreylistMethod() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "public class Class {", - " @Anno", - " public void method() {}", - "}")); - mJavac.compile(); - - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, - ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP)) - ).visit(); - - assertNoErrors(); - ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class); - verify(mConsumer, times(1)).consume(greylist.capture(), any(), any()); - assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method()V"); - } - - @Test - public void testGreylistConstructor() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "public class Class {", - " @Anno", - " public Class() {}", - "}")); - mJavac.compile(); - - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, - ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP)) - ).visit(); - - assertNoErrors(); - ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class); - verify(mConsumer, times(1)).consume(greylist.capture(), any(), any()); - assertThat(greylist.getValue()).isEqualTo("La/b/Class;-><init>()V"); - } - - @Test - public void testGreylistField() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "public class Class {", - " @Anno", - " public int i;", - "}")); - mJavac.compile(); - - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, - ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP)) - ).visit(); - - assertNoErrors(); - ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class); - verify(mConsumer, times(1)).consume(greylist.capture(), any(), any()); - assertThat(greylist.getValue()).isEqualTo("La/b/Class;->i:I"); - } - - @Test - public void testGreylistImplicit() throws IOException { - mJavac.addSource("a.b.EnumClass", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "@Anno(implicitMember=\"values()[La/b/EnumClass;\")", - "public enum EnumClass {", - " VALUE", - "}")); - mJavac.compile(); - - new AnnotationVisitor(mJavac.getCompiledClass("a.b.EnumClass"), mStatus, - ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP)) - ).visit(); - - assertNoErrors(); - ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class); - verify(mConsumer, times(1)).consume(greylist.capture(), any(), any()); - assertThat(greylist.getValue()).isEqualTo("La/b/EnumClass;->values()[La/b/EnumClass;"); - } - - @Test - public void testGreylistImplicit_Invalid_MissingOnClass() throws IOException { - mJavac.addSource("a.b.EnumClass", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "@Anno", - "public enum EnumClass {", - " VALUE", - "}")); - mJavac.compile(); - - new AnnotationVisitor(mJavac.getCompiledClass("a.b.EnumClass"), mStatus, - ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP)) - ).visit(); - - ArgumentCaptor<String> format = ArgumentCaptor.forClass(String.class); - verify(mStatus, times(1)).error(format.capture(), any()); - // Ensure that the correct error is reported. - assertThat(format.getValue()) - .contains("Missing property implicitMember on annotation on class"); - } - - @Test - public void testGreylistImplicit_Invalid_PresentOnMember() throws IOException { - mJavac.addSource("a.b.EnumClass", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "public enum EnumClass {", - " @Anno(implicitMember=\"values()[La/b/EnumClass;\")", - " VALUE", - "}")); - mJavac.compile(); - - new AnnotationVisitor(mJavac.getCompiledClass("a.b.EnumClass"), mStatus, - ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP)) - ).visit(); - - ArgumentCaptor<String> format = ArgumentCaptor.forClass(String.class); - verify(mStatus, times(1)).error(format.capture(), any()); - assertThat(format.getValue()) - .contains("Expected annotation with an implicitMember property to be on a class"); - } - - @Test - public void testGreylistMethodExpectedSignature() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "public class Class {", - " @Anno(expectedSignature=\"La/b/Class;->method()V\")", - " public void method() {}", - "}")); - mJavac.compile(); - - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, - ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP)) - ).visit(); - - assertNoErrors(); - ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class); - verify(mConsumer, times(1)).consume(greylist.capture(), any(), any()); - assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method()V"); - } - - @Test - public void testGreylistMethodExpectedSignatureWrong() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "public class Class {", - " @Anno(expectedSignature=\"La/b/Class;->nomethod()V\")", - " public void method() {}", - "}")); - mJavac.compile(); - - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, - ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP)) - ).visit(); - - verify(mStatus, times(1)).error(any(), any()); - } - - @Test - public void testGreylistInnerClassMethod() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "public class Class {", - " public class Inner {", - " @Anno", - " public void method() {}", - " }", - "}")); - mJavac.compile(); - - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class$Inner"), mStatus, - ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP)) - ).visit(); - - assertNoErrors(); - ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class); - verify(mConsumer, times(1)).consume(greylist.capture(), any(), any()); - assertThat(greylist.getValue()).isEqualTo("La/b/Class$Inner;->method()V"); - } - - @Test - public void testMethodNotGreylisted() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "public class Class {", - " public void method() {}", - "}")); - mJavac.compile(); - - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, - ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP)) - ).visit(); - - assertNoErrors(); - verify(mConsumer, never()).consume(any(String.class), any(), any()); - } - - @Test - public void testMethodArgGenerics() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "public class Class<T extends String> {", - " @Anno(expectedSignature=\"La/b/Class;->method(Ljava/lang/String;)V\")", - " public void method(T arg) {}", - "}")); - mJavac.compile(); - - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, - ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP)) - ).visit(); - - assertNoErrors(); - ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class); - verify(mConsumer, times(1)).consume(greylist.capture(), any(), any()); - assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method(Ljava/lang/String;)V"); - } - - @Test - public void testOverrideMethodWithBridge() throws IOException { - mJavac.addSource("a.b.Base", Joiner.on('\n').join( - "package a.b;", - "abstract class Base<T> {", - " protected abstract void method(T arg);", - "}")); - - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "public class Class<T extends String> extends Base<T> {", - " @Override", - " @Anno(expectedSignature=\"La/b/Class;->method(Ljava/lang/String;)V\")", - " public void method(T arg) {}", - "}")); - mJavac.compile(); - - Map<String, AnnotationHandler> handlerMap = - ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP)); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Base"), mStatus, handlerMap).visit(); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit(); - - assertNoErrors(); - ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class); - // A bridge method is generated for the above, so we expect 2 greylist entries. - verify(mConsumer, times(2)).consume(greylist.capture(), any(), any()); - assertThat(greylist.getAllValues()).containsExactly( - "La/b/Class;->method(Ljava/lang/Object;)V", - "La/b/Class;->method(Ljava/lang/String;)V"); - } - - @Test - public void testOverridePublicMethodWithBridge() throws IOException { - mJavac.addSource("a.b.Base", Joiner.on('\n').join( - "package a.b;", - "public abstract class Base<T> {", - " public void method(T arg) {}", - "}")); - - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "public class Class<T extends String> extends Base<T> {", - " @Override", - " @Anno(expectedSignature=\"La/b/Class;->method(Ljava/lang/String;)V\")", - " public void method(T arg) {}", - "}")); - mJavac.compile(); - - Map<String, AnnotationHandler> handlerMap = - ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP)); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Base"), mStatus, handlerMap).visit(); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit(); - - assertNoErrors(); - ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class); - // A bridge method is generated for the above, so we expect 2 greylist entries. - verify(mConsumer, times(2)).consume(greylist.capture(), any(), any()); - assertThat(greylist.getAllValues()).containsExactly( - "La/b/Class;->method(Ljava/lang/Object;)V", - "La/b/Class;->method(Ljava/lang/String;)V"); - } - - @Test - public void testBridgeMethodsFromInterface() throws IOException { - mJavac.addSource("a.b.Interface", Joiner.on('\n').join( - "package a.b;", - "public interface Interface {", - " public void method(Object arg);", - "}")); - - mJavac.addSource("a.b.Base", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "class Base {", - " @Anno(expectedSignature=\"La/b/Base;->method(Ljava/lang/Object;)V\")", - " public void method(Object arg) {}", - "}")); - - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "public class Class extends Base implements Interface {", - "}")); - mJavac.compile(); - - Map<String, AnnotationHandler> handlerMap = - ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP)); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Interface"), mStatus, handlerMap) - .visit(); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Base"), mStatus, handlerMap).visit(); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit(); - - assertNoErrors(); - ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class); - // A bridge method is generated for the above, so we expect 2 greylist entries. - verify(mConsumer, times(2)).consume(greylist.capture(), any(), any()); - assertThat(greylist.getAllValues()).containsExactly( - "La/b/Class;->method(Ljava/lang/Object;)V", - "La/b/Base;->method(Ljava/lang/Object;)V"); - } - - @Test - public void testPublicBridgeExcluded() throws IOException { - mJavac.addSource("a.b.Base", Joiner.on('\n').join( - "package a.b;", - "public abstract class Base<T> {", - " public void method(T arg) {}", - "}")); - - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "public class Class<T extends String> extends Base<T> {", - " @Override", - " @Anno", - " public void method(T arg) {}", - "}")); - mJavac.compile(); - - Set<String> publicApis = Sets.newHashSet( - "La/b/Base;->method(Ljava/lang/Object;)V", - "La/b/Class;->method(Ljava/lang/Object;)V"); - Map<String, AnnotationHandler> handlerMap = - ImmutableMap.of(ANNOTATION, - new UnsupportedAppUsageAnnotationHandler( - mStatus, - mConsumer, - publicApis, - NULL_SDK_MAP)); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Base"), mStatus, handlerMap).visit(); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit(); - - assertNoErrors(); - ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class); - // The bridge method generated for the above, is a public API so should be excluded - verify(mConsumer, times(1)).consume(greylist.capture(), any(), any()); - assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method(Ljava/lang/String;)V"); - } - - @Test - public void testVolatileField() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "public class Class {", - " @Anno(expectedSignature=\"La/b/Class;->field:I\")", - " public volatile int field;", - "}")); - mJavac.compile(); - - Map<String, AnnotationHandler> handlerMap = - ImmutableMap.of(ANNOTATION, createGreylistHandler( - member -> !member.isBridgeMethod, // exclude bridge methods - NULL_SDK_MAP)); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit(); - assertNoErrors(); - ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class); - verify(mConsumer, times(1)).consume(greylist.capture(), any(), any()); - assertThat(greylist.getValue()).isEqualTo("La/b/Class;->field:I"); - } - - @Test - public void testVolatileFieldWrongSignature() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "public class Class {", - " @Anno(expectedSignature=\"La/b/Class;->wrong:I\")", - " public volatile int field;", - "}")); - mJavac.compile(); - - Map<String, AnnotationHandler> handlerMap = - ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP)); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit(); - verify(mStatus, times(1)).error(any(), any()); - } - - @Test - public void testMethodMaxTargetSdk() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "public class Class {", - " @Anno(maxTargetSdk=1)", - " public int field;", - "}")); - mJavac.compile(); - - Map<String, AnnotationHandler> handlerMap = - ImmutableMap.of(ANNOTATION, createGreylistHandler( - x -> true, - ImmutableMap.of(1, "flag1"))); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit(); - assertNoErrors(); - verify(mConsumer, times(1)).consume(any(), any(), eq(ImmutableSet.of("flag1"))); - } - - @Test - public void testMethodNoMaxTargetSdk() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "public class Class {", - " @Anno", - " public int field;", - "}")); - mJavac.compile(); - - Map<String, AnnotationHandler> handlerMap = - ImmutableMap.of(ANNOTATION, createGreylistHandler( - x -> true, - NULL_SDK_MAP)); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit(); - assertNoErrors(); - verify(mConsumer, times(1)).consume(any(), any(), eq(ImmutableSet.of("flag-null"))); - } - - @Test - public void testMethodMaxTargetSdkOutOfRange() throws IOException { - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno;", - "public class Class {", - " @Anno(maxTargetSdk=2)", - " public int field;", - "}")); - mJavac.compile(); - - Map<String, AnnotationHandler> handlerMap = - ImmutableMap.of(ANNOTATION, createGreylistHandler( - x -> true, - NULL_SDK_MAP)); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit(); - verify(mStatus, times(1)).error(any(), any()); - } - - @Test - public void testAnnotationPropertiesIntoMap() throws IOException { - mJavac.addSource("annotation.Anno2", Joiner.on('\n').join( - "package annotation;", - "import static java.lang.annotation.RetentionPolicy.CLASS;", - "import java.lang.annotation.Retention;", - "@Retention(CLASS)", - "public @interface Anno2 {", - " String expectedSignature() default \"\";", - " int maxTargetSdk() default Integer.MAX_VALUE;", - " long trackingBug() default 0;", - "}")); - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno2;", - "public class Class {", - " @Anno2(maxTargetSdk=2, trackingBug=123456789)", - " public int field;", - "}")); - mJavac.compile(); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, - ImmutableMap.of("Lannotation/Anno2;", createGreylistHandler(x -> true, - ImmutableMap.of(2, "flag2"))) - ).visit(); - - assertNoErrors(); - ArgumentCaptor<Map<String, String>> properties = ArgumentCaptor.forClass(Map.class); - verify(mConsumer, times(1)).consume(any(), properties.capture(), any()); - assertThat(properties.getValue()).containsExactly( - "maxTargetSdk", "2", - "trackingBug", "123456789"); - } - - - @Test - public void testSpecialCaseBug170729553AnnotationPropertiesIntoMap() throws IOException { - mJavac.addSource("annotation.Anno2", Joiner.on('\n').join( - "package annotation;", - "import static java.lang.annotation.RetentionPolicy.CLASS;", - "import java.lang.annotation.Retention;", - "@Retention(CLASS)", - "public @interface Anno2 {", - " String expectedSignature() default \"\";", - " int maxTargetSdk() default Integer.MAX_VALUE;", - " long trackingBug() default 0;", - "}")); - mJavac.addSource("a.b.Class", Joiner.on('\n').join( - "package a.b;", - "import annotation.Anno2;", - "public class Class {", - " @Anno2(maxTargetSdk=30, trackingBug=170729553)", - " public int field;", - "}")); - mJavac.compile(); - new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, - ImmutableMap.of("Lannotation/Anno2;", createGreylistHandler(x -> true, - ImmutableMap.of(0, "flag0"))) - ).visit(); - - assertNoErrors(); - ArgumentCaptor<Map<String, String>> properties = ArgumentCaptor.forClass(Map.class); - verify(mConsumer, times(1)).consume(any(), properties.capture(), any()); - assertThat(properties.getValue()).containsExactly( - "maxTargetSdk", "0", - "trackingBug", "170729553"); - } -} |