blob: efe3ead447e02eb33a30abc49f8f08c3a670b685 [file] [log] [blame]
// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.graph.DexValue.DexValueByte;
import com.android.tools.r8.graph.DexValue.DexValueInt;
import com.android.tools.r8.graph.DexValue.DexValueMethod;
import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.structural.StructuralItem;
import com.android.tools.r8.utils.structural.StructuralMapping;
import com.android.tools.r8.utils.structural.StructuralSpecification;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
public class DexAnnotation extends DexItem implements StructuralItem<DexAnnotation> {
public enum AnnotatedKind {
FIELD,
METHOD,
TYPE,
PARAMETER;
public static AnnotatedKind from(DexDefinition definition) {
return from(definition.getReference());
}
public static AnnotatedKind from(ProgramDefinition definition) {
return from(definition.getReference());
}
public static AnnotatedKind from(DexReference reference) {
return reference.apply(type -> TYPE, field -> FIELD, method -> METHOD);
}
public boolean isMethod() {
return this == METHOD;
}
public boolean isParameter() {
return this == PARAMETER;
}
}
public static final DexAnnotation[] EMPTY_ARRAY = {};
public static final int VISIBILITY_BUILD = 0x00;
public static final int VISIBILITY_RUNTIME = 0x01;
public static final int VISIBILITY_SYSTEM = 0x02;
public final int visibility;
public final DexEncodedAnnotation annotation;
private static final int UNKNOWN_API_LEVEL = -1;
private static final int NOT_SET_API_LEVEL = -2;
protected static void specify(StructuralSpecification<DexAnnotation, ?> spec) {
spec.withItem(a -> a.annotation).withInt(a -> a.visibility);
}
public DexAnnotation(int visibility, DexEncodedAnnotation annotation) {
this.visibility = visibility;
this.annotation = annotation;
}
public boolean isTypeAnnotation() {
return false;
}
public DexTypeAnnotation asTypeAnnotation() {
return null;
}
@Override
public DexAnnotation self() {
return this;
}
@Override
public StructuralMapping<DexAnnotation> getStructuralMapping() {
return DexAnnotation::specify;
}
public DexType getAnnotationType() {
return annotation.type;
}
@Override
public int hashCode() {
return visibility + annotation.hashCode() * 3;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other instanceof DexAnnotation) {
DexAnnotation o = (DexAnnotation) other;
return (visibility == o.visibility) && annotation.equals(o.annotation);
}
return false;
}
@Override
public String toString() {
return visibility + " " + annotation;
}
public int getVisibility() {
return visibility;
}
public void collectIndexedItems(AppView<?> appView, IndexedItemCollection indexedItems) {
annotation.collectIndexedItems(appView, indexedItems);
}
@Override
protected void collectMixedSectionItems(MixedSectionCollection mixedItems) {
mixedItems.add(this);
}
@SuppressWarnings("ReferenceEquality")
public static boolean retainCompileTimeAnnotation(DexType annotation, InternalOptions options) {
if (options.retainCompileTimeAnnotations) {
return true;
}
if (annotation == options.itemFactory.annotationSynthesizedClass
|| annotation
.getDescriptor()
.startsWith(options.itemFactory.dalvikAnnotationOptimizationPrefix)) {
return true;
}
if (options.processCovariantReturnTypeAnnotations) {
// @CovariantReturnType annotations are processed by CovariantReturnTypeAnnotationTransformer,
// they thus need to be read here and will then be removed as part of the processing.
return CovariantReturnTypeAnnotationTransformer.isCovariantReturnTypeAnnotation(
annotation, options.itemFactory);
}
return false;
}
public static DexAnnotation createEnclosingClassAnnotation(DexType enclosingClass,
DexItemFactory factory) {
return createSystemValueAnnotation(factory.annotationEnclosingClass, factory,
new DexValueType(enclosingClass));
}
public static DexType getEnclosingClassFromAnnotation(
DexAnnotation annotation, DexItemFactory factory) {
DexValue value = getSystemValueAnnotationValue(factory.annotationEnclosingClass, annotation);
if (value == null) {
return null;
}
return value.asDexValueType().value;
}
public static DexAnnotation createEnclosingMethodAnnotation(DexMethod enclosingMethod,
DexItemFactory factory) {
return createSystemValueAnnotation(factory.annotationEnclosingMethod, factory,
new DexValueMethod(enclosingMethod));
}
public static DexMethod getEnclosingMethodFromAnnotation(
DexAnnotation annotation, DexItemFactory factory) {
DexValue value = getSystemValueAnnotationValue(factory.annotationEnclosingMethod, annotation);
if (value == null) {
return null;
}
return value.asDexValueMethod().value;
}
@SuppressWarnings("ReferenceEquality")
public static boolean isEnclosingClassAnnotation(
DexAnnotation annotation, DexItemFactory factory) {
return annotation.annotation.type == factory.annotationEnclosingClass;
}
@SuppressWarnings("ReferenceEquality")
public static boolean isEnclosingMethodAnnotation(
DexAnnotation annotation, DexItemFactory factory) {
return annotation.annotation.type == factory.annotationEnclosingMethod;
}
@SuppressWarnings("ReferenceEquality")
public static boolean isInnerClassAnnotation(DexAnnotation annotation, DexItemFactory factory) {
return annotation.annotation.type == factory.annotationInnerClass;
}
@SuppressWarnings("ReferenceEquality")
public static boolean isMemberClassesAnnotation(
DexAnnotation annotation, DexItemFactory factory) {
return annotation.annotation.type == factory.annotationMemberClasses;
}
@SuppressWarnings("ReferenceEquality")
public static boolean isNestHostAnnotation(DexAnnotation annotation, DexItemFactory factory) {
return annotation.annotation.type == factory.annotationNestHost;
}
@SuppressWarnings("ReferenceEquality")
public static boolean isNestMembersAnnotation(DexAnnotation annotation, DexItemFactory factory) {
return annotation.annotation.type == factory.annotationNestMembers;
}
@SuppressWarnings("ReferenceEquality")
public static boolean isPermittedSubclassesAnnotation(
DexAnnotation annotation, DexItemFactory factory) {
return annotation.annotation.type == factory.annotationPermittedSubclasses;
}
@SuppressWarnings("ReferenceEquality")
public static boolean isRecordAnnotation(DexAnnotation annotation, DexItemFactory factory) {
return annotation.getAnnotationType() == factory.annotationRecord;
}
public static DexAnnotation createInnerClassAnnotation(
DexString clazz, int access, DexItemFactory factory) {
return new DexAnnotation(
VISIBILITY_SYSTEM,
new DexEncodedAnnotation(
factory.annotationInnerClass,
new DexAnnotationElement[] {
new DexAnnotationElement(
factory.createString("accessFlags"), DexValueInt.create(access)),
new DexAnnotationElement(
factory.createString("name"),
(clazz == null) ? DexValueNull.NULL : new DexValueString(clazz))
}));
}
@SuppressWarnings("ReferenceEquality")
public static Pair<DexString, Integer> getInnerClassFromAnnotation(
DexAnnotation annotation, DexItemFactory factory) {
assert isInnerClassAnnotation(annotation, factory);
DexAnnotationElement[] elements = annotation.annotation.elements;
Pair<DexString, Integer> result = new Pair<>();
for (DexAnnotationElement element : elements) {
if (element.name == factory.createString("name")) {
if (element.value.isDexValueString()) {
result.setFirst(element.value.asDexValueString().getValue());
}
} else {
assert element.name == factory.createString("accessFlags");
result.setSecond(element.value.asDexValueInt().getValue());
}
}
return result;
}
public static DexAnnotation createMemberClassesAnnotation(List<DexType> classes,
DexItemFactory factory) {
DexValue[] values = new DexValue[classes.size()];
for (int i = 0; i < classes.size(); i++) {
values[i] = new DexValueType(classes.get(i));
}
return createSystemValueAnnotation(factory.annotationMemberClasses, factory,
new DexValueArray(values));
}
public static List<DexType> getMemberClassesFromAnnotation(
DexAnnotation annotation, DexItemFactory factory) {
DexValue value = getSystemValueAnnotationValue(factory.annotationMemberClasses, annotation);
if (value == null) {
return null;
}
DexValueArray membersArray = value.asDexValueArray();
List<DexType> types = new ArrayList<>(membersArray.getValues().length);
for (DexValue elementValue : membersArray.getValues()) {
types.add(elementValue.asDexValueType().value);
}
return types;
}
public static DexType getNestHostFromAnnotation(
DexAnnotation annotation, DexItemFactory factory) {
DexValue value = getSystemValueAnnotationValue(factory.annotationNestHost, annotation);
if (value == null) {
return null;
}
return value.asDexValueType().getValue();
}
private static List<DexType> getTypesFromAnnotation(
DexType annotationType, DexAnnotation annotation) {
DexValue value = getSystemValueAnnotationValue(annotationType, annotation);
if (value == null) {
return null;
}
DexValueArray membersArray = value.asDexValueArray();
List<DexType> types = new ArrayList<>(membersArray.getValues().length);
for (DexValue elementValue : membersArray.getValues()) {
types.add(elementValue.asDexValueType().value);
}
return types;
}
public static List<DexType> getNestMembersFromAnnotation(
DexAnnotation annotation, DexItemFactory factory) {
return getTypesFromAnnotation(factory.annotationNestMembers, annotation);
}
public static List<DexType> getPermittedSubclassesFromAnnotation(
DexAnnotation annotation, DexItemFactory factory) {
return getTypesFromAnnotation(factory.annotationPermittedSubclasses, annotation);
}
/** See {@link #createRecordAnnotation(DexProgramClass, AppView)} for the representation. */
public static List<RecordComponentInfo> getRecordComponentInfoFromAnnotation(
DexType type, DexAnnotation annotation, DexItemFactory factory, Origin origin) {
DexValue componentNamesValue =
getSystemValueAnnotationValueWithName(
factory.annotationRecord, annotation, factory.annotationRecordComponentNames);
DexValue componentTypesValue =
getSystemValueAnnotationValueWithName(
factory.annotationRecord, annotation, factory.annotationRecordComponentTypes);
DexValue componentSignaturesValue =
getSystemValueAnnotationValueWithName(
factory.annotationRecord, annotation, factory.annotationRecordComponentSignatures);
DexValue componentAnnotationVisibilitiesValue =
getSystemValueAnnotationValueWithName(
factory.annotationRecord,
annotation,
factory.annotationRecordComponentAnnotationVisibilities);
DexValue componentAnnotationsValue =
getSystemValueAnnotationValueWithName(
factory.annotationRecord, annotation, factory.annotationRecordComponentAnnotations);
if (componentNamesValue == null
|| componentTypesValue == null
|| componentSignaturesValue == null
|| componentAnnotationVisibilitiesValue == null
|| componentAnnotationsValue == null) {
return null;
}
if (!componentNamesValue.isDexValueArray()
|| !componentTypesValue.isDexValueArray()
|| !componentSignaturesValue.isDexValueArray()
|| !componentAnnotationVisibilitiesValue.isDexValueArray()
|| !componentAnnotationsValue.isDexValueArray()) {
return null;
}
DexValueArray componentNamesValueArray = componentNamesValue.asDexValueArray();
DexValueArray componentTypesValueArray = componentTypesValue.asDexValueArray();
DexValueArray componentSignaturesValueArray = componentSignaturesValue.asDexValueArray();
DexValueArray componentAnnotationVisibilitiesValueArray =
componentAnnotationVisibilitiesValue.asDexValueArray();
DexValueArray componentAnnotationsValueArray = componentAnnotationsValue.asDexValueArray();
if (componentNamesValueArray.size() != componentTypesValueArray.size()
|| componentNamesValueArray.size() != componentSignaturesValueArray.size()
|| componentNamesValueArray.size() != componentAnnotationVisibilitiesValueArray.size()
|| componentNamesValueArray.size() != componentAnnotationsValueArray.size()) {
return null;
}
List<RecordComponentInfo> result = new ArrayList<>(componentNamesValueArray.size());
for (int componentIndex = 0;
componentIndex < componentNamesValueArray.size();
componentIndex++) {
DexValue nameValue = componentNamesValueArray.getValue(componentIndex);
DexValue typeValue = componentTypesValueArray.getValue(componentIndex);
DexValue signatureValue = componentSignaturesValueArray.getValue(componentIndex);
DexValue visibilitiesValue =
componentAnnotationVisibilitiesValueArray.getValue(componentIndex);
DexValue annotationsValue = componentAnnotationsValueArray.getValue(componentIndex);
if (!nameValue.isDexValueString()
|| !typeValue.isDexValueType()
|| !(signatureValue.isDexValueAnnotation() || signatureValue.isDexValueNull())
|| !visibilitiesValue.isDexValueArray()
|| !annotationsValue.isDexValueArray()) {
return null;
}
DexValueArray visibilitiesValueArray = visibilitiesValue.asDexValueArray();
DexValueArray annotationsValueArray = annotationsValue.asDexValueArray();
if (visibilitiesValueArray.size() != annotationsValueArray.size()) {
return null;
}
List<DexAnnotation> componentAnnotations = Collections.emptyList();
if (annotationsValueArray.size() > 0) {
componentAnnotations = new ArrayList<>(annotationsValueArray.size());
for (int annotationIndex = 0;
annotationIndex < annotationsValueArray.size();
annotationIndex++) {
DexValue visibilityValue = visibilitiesValueArray.getValue(annotationIndex);
DexValue annotationValue = annotationsValueArray.getValue(annotationIndex);
if (!visibilityValue.isDexValueByte() || !annotationValue.isDexValueAnnotation()) {
return null;
}
componentAnnotations.add(
new DexAnnotation(
visibilityValue.asDexValueByte().getValue(),
annotationValue.asDexValueAnnotation().getValue()));
}
}
FieldTypeSignature componentSignature =
GenericSignature.parseFieldTypeSignature(
nameValue.asDexValueString().getValue().toString(),
signatureValue.isDexValueAnnotation()
? getSignature(signatureValue.asDexValueAnnotation().getValue())
: null,
origin,
factory,
null);
DexType componentType = typeValue.asDexValueType().getValue();
DexString componentName = nameValue.asDexValueString().getValue();
DexField componentField = factory.createField(type, componentType, componentName);
result.add(new RecordComponentInfo(componentField, componentSignature, componentAnnotations));
}
return result;
}
public static DexAnnotation createSourceDebugExtensionAnnotation(DexValue value,
DexItemFactory factory) {
return new DexAnnotation(VISIBILITY_SYSTEM,
new DexEncodedAnnotation(factory.annotationSourceDebugExtension,
new DexAnnotationElement[] {
new DexAnnotationElement(factory.createString("value"), value)
}));
}
public static DexAnnotation createMethodParametersAnnotation(DexValue[] names,
DexValue[] accessFlags, DexItemFactory factory) {
assert names.length == accessFlags.length;
return new DexAnnotation(VISIBILITY_SYSTEM,
new DexEncodedAnnotation(factory.annotationMethodParameters,
new DexAnnotationElement[]{
new DexAnnotationElement(
factory.createString("names"),
new DexValueArray(names)),
new DexAnnotationElement(
factory.createString("accessFlags"),
new DexValueArray(accessFlags))
}));
}
public static DexAnnotation createAnnotationDefaultAnnotation(DexType type,
List<DexAnnotationElement> defaults, DexItemFactory factory) {
return createSystemValueAnnotation(factory.annotationDefault, factory,
new DexValueAnnotation(
new DexEncodedAnnotation(type,
defaults.toArray(DexAnnotationElement.EMPTY_ARRAY)))
);
}
public static DexAnnotation createSignatureAnnotation(String signature, DexItemFactory factory) {
return createSystemValueAnnotation(factory.annotationSignature, factory,
compressSignature(signature, factory));
}
public static DexAnnotation createNestHostAnnotation(
NestHostClassAttribute host, DexItemFactory factory) {
return createSystemValueAnnotation(
factory.annotationNestHost, factory, new DexValue.DexValueType(host.getNestHost()));
}
public static DexAnnotation createNestMembersAnnotation(
List<NestMemberClassAttribute> members, DexItemFactory factory) {
List<DexValueType> list = new ArrayList<>(members.size());
for (NestMemberClassAttribute member : members) {
list.add(new DexValue.DexValueType(member.getNestMember()));
}
return createSystemValueAnnotation(
factory.annotationNestMembers,
factory,
new DexValue.DexValueArray(list.toArray(DexValue.EMPTY_ARRAY)));
}
public static DexAnnotation createPermittedSubclassesAnnotation(
List<PermittedSubclassAttribute> permittedSubclasses, DexItemFactory factory) {
List<DexValueType> list = new ArrayList<>(permittedSubclasses.size());
for (PermittedSubclassAttribute permittedSubclass : permittedSubclasses) {
list.add(new DexValue.DexValueType(permittedSubclass.getPermittedSubclass()));
}
return createSystemValueAnnotation(
factory.annotationPermittedSubclasses,
factory,
new DexValue.DexValueArray(list.toArray(DexValue.EMPTY_ARRAY)));
}
/**
* Record component information is written to DEX as a system annotation named <code>
* dalvik.annotation.Record</code> with the following content:
*
* <pre>
* componentAnnotationVisibilities byte[][]
* componentAnnotations Annotation[][]
* componentNames String[]
* componentSignatures Annotation[] // Annotation dalvik.annotation.Signature or NULL
* componentTypes String[]
* </pre>
*
* Each of the arrays have one element for each component.
*
* <p>Example of a two component record with two annotations on the first component and one on the
* second and with a signature on the first component.
*
* <pre>
* .annotation system Ldalvik/annotation/Record;
* componentAnnotationVisibilities = {
* {
* 0x1,
* 0x1
* },
* {
* 0x1
* }
* }
* componentAnnotations = {
* {
* .subannotation LAnnotation1;
* value = "a"
* .end subannotation,
* .subannotation LAnnotation2;
* value = "c"
* .end subannotation
* },
* {
* .subannotation LAnnotation3;
* value = "z"
* .end subannotation
* }
* }
* componentNames = {
* "name",
* "age"
* }
* componentSignatures = {
* .subannotation Ldalvik/annotation/Signature;
* value = {
* "TX;"
* }
* .end subannotation,
* NULL
* }
* componentTypes = {
* Ljava/lang/CharSequence;,
* Ljava/lang/Object;
* }
* .end annotation
* </pre>
*/
public static DexAnnotation createRecordAnnotation(DexProgramClass clazz, AppView<?> appView) {
DexItemFactory factory = appView.dexItemFactory();
int componentCount = clazz.getRecordComponents().size();
DexValueString[] componentNames = new DexValueString[componentCount];
DexValueType[] componentTypes = new DexValueType[componentCount];
DexValue[] componentSignatures = new DexValue[componentCount];
DexValueArray[] componentAnnotationVisibilities = new DexValueArray[componentCount];
DexValueArray[] componentAnnotations = new DexValueArray[componentCount];
for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
RecordComponentInfo info = clazz.getRecordComponents().get(componentIndex);
componentNames[componentIndex] =
new DexValueString(appView.getNamingLens().lookupName(info.getField()));
componentTypes[componentIndex] = new DexValueType(info.getType());
if (info.getSignature().hasNoSignature()) {
componentSignatures[componentIndex] = DexValueNull.NULL;
} else {
componentSignatures[componentIndex] =
new DexValueAnnotation(
createSignatureAnnotation(info.getSignature().toString(), factory).annotation);
}
int annotationsSize = info.getAnnotations().size();
DexValueByte[] visibilities = new DexValueByte[annotationsSize];
DexValueAnnotation[] annotations = new DexValueAnnotation[annotationsSize];
componentAnnotationVisibilities[componentIndex] = new DexValueArray(visibilities);
componentAnnotations[componentIndex] = new DexValueArray(annotations);
for (int annotationIndex = 0; annotationIndex < annotationsSize; annotationIndex++) {
DexAnnotation annotation = info.getAnnotations().get(annotationIndex);
visibilities[annotationIndex] = DexValueByte.create((byte) annotation.getVisibility());
annotations[annotationIndex] = new DexValueAnnotation(annotation.annotation);
}
}
return new DexAnnotation(
VISIBILITY_SYSTEM,
new DexEncodedAnnotation(
factory.annotationRecord,
new DexAnnotationElement[] {
new DexAnnotationElement(
factory.annotationRecordComponentNames, new DexValueArray(componentNames)),
new DexAnnotationElement(
factory.annotationRecordComponentTypes, new DexValueArray(componentTypes)),
new DexAnnotationElement(
factory.annotationRecordComponentSignatures,
new DexValueArray(componentSignatures)),
new DexAnnotationElement(
factory.annotationRecordComponentAnnotationVisibilities,
new DexValueArray(componentAnnotationVisibilities)),
new DexAnnotationElement(
factory.annotationRecordComponentAnnotations,
new DexValueArray(componentAnnotations))
}));
}
public static String getSignature(DexAnnotation signatureAnnotation) {
return getSignature(signatureAnnotation.annotation);
}
public static String getSignature(DexEncodedAnnotation signatureAnnotation) {
return getSignature(signatureAnnotation.elements[0].value.asDexValueArray());
}
public static String getSignature(DexValueArray elements) {
StringBuilder signature = new StringBuilder();
for (DexValue element : elements.getValues()) {
signature.append(element.asDexValueString().value.toString());
}
return signature.toString();
}
public static String getSignature(DexAnnotationSet signatureAnnotations, DexItemFactory factory) {
DexAnnotation signature = signatureAnnotations.getFirstMatching(factory.annotationSignature);
return signature == null ? null : getSignature(signature);
}
public static DexAnnotation createThrowsAnnotation(DexValue[] exceptions,
DexItemFactory factory) {
return createSystemValueAnnotation(factory.annotationThrows, factory,
new DexValueArray(exceptions));
}
private static DexAnnotation createSystemValueAnnotation(DexType type, DexItemFactory factory,
DexValue value) {
return new DexAnnotation(VISIBILITY_SYSTEM,
new DexEncodedAnnotation(type, new DexAnnotationElement[]{
new DexAnnotationElement(factory.createString("value"), value)
}));
}
@SuppressWarnings("ReferenceEquality")
private static DexValue getSystemValueAnnotationValue(DexType type, DexAnnotation annotation) {
assert annotation.visibility == VISIBILITY_SYSTEM;
assert annotation.annotation.type == type;
return annotation.annotation.elements.length == 0
? null
: annotation.annotation.elements[0].value;
}
@SuppressWarnings("ReferenceEquality")
private static DexValue getSystemValueAnnotationValueWithName(
DexType type, DexAnnotation annotation, DexString name) {
assert annotation.visibility == VISIBILITY_SYSTEM;
assert annotation.getAnnotationType() == type;
for (DexAnnotationElement element : annotation.annotation.elements) {
if (element.name == name) {
return element.value;
}
}
return null;
}
@SuppressWarnings("ReferenceEquality")
public static boolean isThrowingAnnotation(DexAnnotation annotation, DexItemFactory factory) {
return annotation.annotation.type == factory.annotationThrows;
}
@SuppressWarnings("ReferenceEquality")
public static boolean isSignatureAnnotation(DexAnnotation annotation, DexItemFactory factory) {
return annotation.annotation.type == factory.annotationSignature;
}
@SuppressWarnings("ReferenceEquality")
public static boolean isAnnotationDefaultAnnotation(
DexAnnotation annotation, DexItemFactory factory) {
return annotation.annotation.type == factory.annotationDefault;
}
@SuppressWarnings("ReferenceEquality")
public static boolean isJavaLangRetentionAnnotation(
DexAnnotation annotation, DexItemFactory factory) {
return annotation.getAnnotationType() == factory.retentionType;
}
@SuppressWarnings("ReferenceEquality")
public static boolean isSourceDebugExtension(DexAnnotation annotation, DexItemFactory factory) {
return annotation.annotation.type == factory.annotationSourceDebugExtension;
}
@SuppressWarnings("ReferenceEquality")
public static boolean isParameterNameAnnotation(
DexAnnotation annotation, DexItemFactory factory) {
return annotation.annotation.type == factory.annotationMethodParameters;
}
/**
* As a simple heuristic for compressing a signature by splitting on fully qualified class names
* and make them individual part. All other parts of the signature are simply grouped and separate
* the names.
* For examples, "()Ljava/lang/List<Lfoo/bar/Baz;>;" splits into:
* <pre>
* ["()", "Ljava/lang/List<", "Lfoo/bar/Baz;", ">;"]
* </pre>
*/
private static DexValue compressSignature(String signature, DexItemFactory factory) {
final int length = signature.length();
List<DexValue> parts = new ArrayList<>();
for (int at = 0; at < length; /*at*/) {
char c = signature.charAt(at);
int endAt = at + 1;
if (c == 'L') {
// Scan to ';' or '<' and consume them.
while (endAt < length) {
c = signature.charAt(endAt);
if (c == ';' || c == '<') {
endAt++;
break;
}
endAt++;
}
} else {
// Scan to 'L' without consuming it.
while (endAt < length) {
c = signature.charAt(endAt);
if (c == 'L') {
break;
}
endAt++;
}
}
parts.add(toDexValue(signature.substring(at, endAt), factory));
at = endAt;
}
return new DexValueArray(parts.toArray(DexValue.EMPTY_ARRAY));
}
private static DexValue toDexValue(String string, DexItemFactory factory) {
return new DexValueString(factory.createString(string));
}
public static DexAnnotation createAnnotationSynthesizedClass(
SyntheticKind kind, DexItemFactory dexItemFactory, ComputedApiLevel computedApiLevel) {
DexString versionHash =
dexItemFactory.createString(dexItemFactory.getSyntheticNaming().getVersionHash());
DexAnnotationElement kindElement =
new DexAnnotationElement(dexItemFactory.kindString, DexValueInt.create(kind.getId()));
DexAnnotationElement versionHashElement =
new DexAnnotationElement(dexItemFactory.versionHashString, new DexValueString(versionHash));
int apiLevel = getApiLevelForSerialization(computedApiLevel);
DexAnnotationElement apiLevelElement =
new DexAnnotationElement(dexItemFactory.apiLevelString, DexValueInt.create(apiLevel));
DexAnnotationElement[] elements =
new DexAnnotationElement[] {apiLevelElement, kindElement, versionHashElement};
return new DexAnnotation(
VISIBILITY_BUILD,
new DexEncodedAnnotation(dexItemFactory.annotationSynthesizedClass, elements));
}
public static boolean hasSynthesizedClassAnnotation(
DexAnnotationSet annotations,
DexItemFactory factory,
SyntheticItems synthetics,
AndroidApiLevelCompute apiLevelCompute) {
return getSynthesizedClassAnnotationInfo(annotations, factory, synthetics, apiLevelCompute)
!= null;
}
@SuppressWarnings("ReferenceEquality")
public static SynthesizedAnnotationClassInfo getSynthesizedClassAnnotationInfo(
DexAnnotationSet annotations,
DexItemFactory factory,
SyntheticItems synthetics,
AndroidApiLevelCompute apiLevelCompute) {
if (annotations.size() != 1) {
return null;
}
DexAnnotation annotation = annotations.annotations[0];
if (annotation.annotation.type != factory.annotationSynthesizedClass) {
return null;
}
int length = annotation.annotation.elements.length;
if (length != 3) {
return null;
}
assert factory.kindString.isLessThan(factory.versionHashString);
DexAnnotationElement apiLevelElement = annotation.annotation.elements[0];
DexAnnotationElement kindElement = annotation.annotation.elements[1];
DexAnnotationElement versionHashElement = annotation.annotation.elements[2];
if (kindElement.name != factory.kindString) {
return null;
}
if (!kindElement.value.isDexValueInt()) {
return null;
}
if (versionHashElement.name != factory.versionHashString) {
return null;
}
if (!versionHashElement.value.isDexValueString()) {
return null;
}
if (apiLevelElement.name != factory.apiLevelString || !apiLevelElement.value.isDexValueInt()) {
return null;
}
String currentVersionHash = synthetics.getNaming().getVersionHash();
String syntheticVersionHash = versionHashElement.value.asDexValueString().getValue().toString();
if (!currentVersionHash.equals(syntheticVersionHash)) {
return null;
}
int apiLevelValue = apiLevelElement.value.asDexValueInt().getValue();
ComputedApiLevel computedApiLevel = getSerializedApiLevel(apiLevelCompute, apiLevelValue);
SyntheticKind syntheticKind =
synthetics.getNaming().fromId(kindElement.value.asDexValueInt().getValue());
assert syntheticKind != synthetics.getNaming().API_MODEL_OUTLINE
|| computedApiLevel.isKnownApiLevel();
return SynthesizedAnnotationClassInfo.create(syntheticKind, computedApiLevel);
}
private static int getApiLevelForSerialization(ComputedApiLevel computedApiLevel) {
if (computedApiLevel.isNotSetApiLevel()) {
return NOT_SET_API_LEVEL;
} else if (computedApiLevel.isUnknownApiLevel()) {
return UNKNOWN_API_LEVEL;
} else {
assert computedApiLevel.isKnownApiLevel();
return computedApiLevel.asKnownApiLevel().getApiLevel().getLevel();
}
}
private static ComputedApiLevel getSerializedApiLevel(
AndroidApiLevelCompute apiLevelCompute, int apiLevelValue) {
if (apiLevelValue == NOT_SET_API_LEVEL) {
return ComputedApiLevel.notSet();
} else if (apiLevelValue == UNKNOWN_API_LEVEL) {
return ComputedApiLevel.unknown();
} else {
return apiLevelCompute.of(AndroidApiLevel.getAndroidApiLevel(apiLevelValue));
}
}
@SuppressWarnings("ReferenceEquality")
public DexAnnotation rewrite(Function<DexEncodedAnnotation, DexEncodedAnnotation> rewriter) {
DexEncodedAnnotation rewritten = rewriter.apply(annotation);
if (rewritten == annotation) {
return this;
}
if (rewritten == null) {
return null;
}
return new DexAnnotation(visibility, rewritten);
}
public static class SynthesizedAnnotationClassInfo {
private final SyntheticKind syntheticKind;
private final ComputedApiLevel computedApiLevel;
private SynthesizedAnnotationClassInfo(
SyntheticKind syntheticKind, ComputedApiLevel computedApiLevel) {
this.syntheticKind = syntheticKind;
this.computedApiLevel = computedApiLevel;
}
private static SynthesizedAnnotationClassInfo create(
SyntheticKind syntheticKind, ComputedApiLevel computedApiLevel) {
return new SynthesizedAnnotationClassInfo(syntheticKind, computedApiLevel);
}
public SyntheticKind getSyntheticKind() {
return syntheticKind;
}
public ComputedApiLevel getComputedApiLevel() {
return computedApiLevel;
}
}
}