| // 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 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; |
| } |
| } |
| } |