| // 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.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.StringDiagnostic; | |
| import com.android.tools.r8.utils.internal.collections.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 boolean isType() { | |
| return this == TYPE; | |
| } | |
| } | |
| 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 DexEncodedAnnotation getAnnotation() { | |
| return annotation; | |
| } | |
| public boolean isClassRetentionAnnotation() { | |
| return visibility == VISIBILITY_BUILD; | |
| } | |
| 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); | |
| } | |
| public static boolean retainCompileTimeAnnotation( | |
| DexType annotationType, InternalOptions options) { | |
| return options.retainCompileTimeAnnotations | |
| || alwaysRetainCompileTimeAnnotation(annotationType, options); | |
| } | |
| public static boolean alwaysRetainCompileTimeAnnotation( | |
| DexType annotationType, InternalOptions options) { | |
| DexItemFactory factory = options.itemFactory; | |
| if (annotationType.isIdenticalTo(factory.annotationSynthesizedClass)) { | |
| return true; | |
| } | |
| DexString descriptor = annotationType.getDescriptor(); | |
| if (descriptor.startsWith(factory.dalvikAnnotationPrefix)) { | |
| if (descriptor.startsWith(factory.dalvikAnnotationCodegenCovariantReturnTypePrefix)) { | |
| if (options.processCovariantReturnTypeAnnotations) { | |
| return true; | |
| } | |
| throw options.reporter.fatalError( | |
| new StringDiagnostic( | |
| "Unexpected @CovariantReturnType annotation in non-platform build")); | |
| } | |
| return descriptor.startsWith(factory.dalvikAnnotationOptimizationPrefix); | |
| } | |
| 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; | |
| } | |
| public static boolean isEnclosingClassAnnotation( | |
| DexAnnotation annotation, DexItemFactory factory) { | |
| return factory.annotationEnclosingClass.isIdenticalTo(annotation.annotation.type); | |
| } | |
| public static boolean isEnclosingMethodAnnotation( | |
| DexAnnotation annotation, DexItemFactory factory) { | |
| return factory.annotationEnclosingMethod.isIdenticalTo(annotation.annotation.type); | |
| } | |
| public static boolean isInnerClassAnnotation(DexAnnotation annotation, DexItemFactory factory) { | |
| return factory.annotationInnerClass.isIdenticalTo(annotation.annotation.type); | |
| } | |
| public static boolean isMemberClassesAnnotation( | |
| DexAnnotation annotation, DexItemFactory factory) { | |
| return factory.annotationMemberClasses.isIdenticalTo(annotation.annotation.type); | |
| } | |
| public static boolean isNestHostAnnotation(DexAnnotation annotation, DexItemFactory factory) { | |
| return factory.annotationNestHost.isIdenticalTo(annotation.annotation.type); | |
| } | |
| public static boolean isNestMembersAnnotation(DexAnnotation annotation, DexItemFactory factory) { | |
| return factory.annotationNestMembers.isIdenticalTo(annotation.annotation.type); | |
| } | |
| public static boolean isPermittedSubclassesAnnotation( | |
| DexAnnotation annotation, DexItemFactory factory) { | |
| return factory.annotationPermittedSubclasses.isIdenticalTo(annotation.annotation.type); | |
| } | |
| public static boolean isRecordAnnotation(DexAnnotation annotation, DexItemFactory factory) { | |
| return factory.annotationRecord.isIdenticalTo(annotation.getAnnotationType()); | |
| } | |
| public static DexAnnotation createInnerClassAnnotation( | |
| DexString clazz, int access, DexItemFactory factory) { | |
| return new DexAnnotation( | |
| VISIBILITY_SYSTEM, | |
| new DexEncodedAnnotation( | |
| factory.annotationInnerClass, | |
| new DexAnnotationElement[] { | |
| new DexAnnotationElement(factory.accessFlagsString, DexValueInt.create(access)), | |
| new DexAnnotationElement( | |
| factory.nameString, | |
| (clazz == null) ? DexValueNull.NULL : new DexValueString(clazz)) | |
| })); | |
| } | |
| 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.isIdenticalTo(factory.nameString)) { | |
| if (element.value.isDexValueString()) { | |
| result.setFirst(element.value.asDexValueString().getValue()); | |
| } | |
| } else { | |
| assert element.name.isIdenticalTo(factory.accessFlagsString); | |
| 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.valueString, 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.namesString, new DexValueArray(names)), | |
| new DexAnnotationElement(factory.accessFlagsString, 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.valueString, value)})); | |
| } | |
| private static DexValue getSystemValueAnnotationValue(DexType type, DexAnnotation annotation) { | |
| assert annotation.visibility == VISIBILITY_SYSTEM; | |
| assert annotation.annotation.type.isIdenticalTo(type); | |
| return annotation.annotation.elements.length == 0 | |
| ? null | |
| : annotation.annotation.elements[0].value; | |
| } | |
| private static DexValue getSystemValueAnnotationValueWithName( | |
| DexType type, DexAnnotation annotation, DexString name) { | |
| assert annotation.visibility == VISIBILITY_SYSTEM; | |
| assert annotation.getAnnotationType().isIdenticalTo(type); | |
| for (DexAnnotationElement element : annotation.annotation.elements) { | |
| if (element.name.isIdenticalTo(name)) { | |
| return element.value; | |
| } | |
| } | |
| return null; | |
| } | |
| public static boolean isSignatureAnnotation(DexAnnotation annotation, DexItemFactory factory) { | |
| return factory.annotationSignature.isIdenticalTo(annotation.annotation.type); | |
| } | |
| public static boolean isThrowsAnnotation(DexAnnotation annotation, DexItemFactory factory) { | |
| return factory.annotationThrows.isIdenticalTo(annotation.annotation.type); | |
| } | |
| public static boolean isAnnotationDefaultAnnotation( | |
| DexAnnotation annotation, DexItemFactory factory) { | |
| return factory.annotationDefault.isIdenticalTo(annotation.annotation.type); | |
| } | |
| public static boolean isJavaLangRetentionAnnotation( | |
| DexAnnotation annotation, DexItemFactory factory) { | |
| return factory.retentionType.isIdenticalTo(annotation.getAnnotationType()); | |
| } | |
| public static boolean isSourceDebugExtension(DexAnnotation annotation, DexItemFactory factory) { | |
| return factory.annotationSourceDebugExtension.isIdenticalTo(annotation.annotation.type); | |
| } | |
| public static boolean isParameterNameAnnotation( | |
| DexAnnotation annotation, DexItemFactory factory) { | |
| return factory.annotationMethodParameters.isIdenticalTo(annotation.annotation.type); | |
| } | |
| /** | |
| * 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; | |
| } | |
| public static SynthesizedAnnotationClassInfo getSynthesizedClassAnnotationInfo( | |
| DexAnnotationSet annotations, | |
| DexItemFactory factory, | |
| SyntheticItems synthetics, | |
| AndroidApiLevelCompute apiLevelCompute) { | |
| if (annotations.size() != 1) { | |
| return null; | |
| } | |
| DexAnnotation annotation = annotations.annotations[0]; | |
| if (!factory.annotationSynthesizedClass.isIdenticalTo(annotation.annotation.type)) { | |
| 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.isIdenticalTo(factory.kindString)) { | |
| return null; | |
| } | |
| if (!kindElement.value.isDexValueInt()) { | |
| return null; | |
| } | |
| if (!versionHashElement.name.isIdenticalTo(factory.versionHashString)) { | |
| return null; | |
| } | |
| if (!versionHashElement.value.isDexValueString()) { | |
| return null; | |
| } | |
| if (!apiLevelElement.name.isIdenticalTo(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.equals(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)); | |
| } | |
| } | |
| public DexAnnotation rewrite(Function<DexEncodedAnnotation, DexEncodedAnnotation> rewriter) { | |
| DexEncodedAnnotation rewritten = rewriter.apply(annotation); | |
| if (rewritten == null) { | |
| return null; | |
| } | |
| if (rewritten.isIdenticalTo(annotation)) { | |
| return this; | |
| } | |
| if (isTypeAnnotation()) { | |
| DexTypeAnnotation typeAnnotation = asTypeAnnotation(); | |
| return new DexTypeAnnotation( | |
| visibility, rewritten, typeAnnotation.getTypeRef(), typeAnnotation.getTypePath()); | |
| } else { | |
| 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; | |
| } | |
| } | |
| } |