package net.bytebuddy.implementation.attribute; import lombok.EqualsAndHashCode; import net.bytebuddy.description.annotation.AnnotationDescription; import net.bytebuddy.description.enumeration.EnumerationDescription; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeList; import org.objectweb.asm.*; import java.lang.reflect.Array; import java.util.List; /** * Annotation appenders are capable of writing annotations to a specified target. */ public interface AnnotationAppender { /** * A constant for informing ASM over ignoring a given name. */ String NO_NAME = null; /** * Writes the given annotation to the target that this appender represents. * * @param annotationDescription The annotation to be written. * @param annotationValueFilter The annotation value filter to use. * @return Usually {@code this} or any other annotation appender capable of writing another annotation to the specified target. */ AnnotationAppender append(AnnotationDescription annotationDescription, AnnotationValueFilter annotationValueFilter); /** * Writes the given type annotation to the target that this appender represents. * * @param annotationDescription The annotation to be written. * @param annotationValueFilter The annotation value filter to use. * @param typeReference The type variable's type reference. * @param typePath The type variable's type path. * @return Usually {@code this} or any other annotation appender capable of writing another annotation to the specified target. */ AnnotationAppender append(AnnotationDescription annotationDescription, AnnotationValueFilter annotationValueFilter, int typeReference, String typePath); /** * Represents a target for an annotation writing process. */ interface Target { /** * Creates an annotation visitor for writing the specified annotation. * * @param annotationTypeDescriptor The type descriptor for the annotation to be written. * @param visible {@code true} if the annotation is to be visible at runtime. * @return An annotation visitor for consuming the specified annotation. */ AnnotationVisitor visit(String annotationTypeDescriptor, boolean visible); /** * Creates an annotation visitor for writing the specified type annotation. * * @param annotationTypeDescriptor The type descriptor for the annotation to be written. * @param visible {@code true} if the annotation is to be visible at runtime. * @param typeReference The type annotation's type reference. * @param typePath The type annotation's type path. * @return An annotation visitor for consuming the specified annotation. */ AnnotationVisitor visit(String annotationTypeDescriptor, boolean visible, int typeReference, String typePath); /** * Target for an annotation that is written to a Java type. */ @EqualsAndHashCode class OnType implements Target { /** * The class visitor to write the annotation to. */ private final ClassVisitor classVisitor; /** * Creates a new wrapper for a Java type. * * @param classVisitor The ASM class visitor to which the annotations are appended to. */ public OnType(ClassVisitor classVisitor) { this.classVisitor = classVisitor; } @Override public AnnotationVisitor visit(String annotationTypeDescriptor, boolean visible) { return classVisitor.visitAnnotation(annotationTypeDescriptor, visible); } @Override public AnnotationVisitor visit(String annotationTypeDescriptor, boolean visible, int typeReference, String typePath) { return classVisitor.visitTypeAnnotation(typeReference, TypePath.fromString(typePath), annotationTypeDescriptor, visible); } } /** * Target for an annotation that is written to a Java method or constructor. */ @EqualsAndHashCode class OnMethod implements Target { /** * The method visitor to write the annotation to. */ private final MethodVisitor methodVisitor; /** * Creates a new wrapper for a Java method or constructor. * * @param methodVisitor The ASM method visitor to which the annotations are appended to. */ public OnMethod(MethodVisitor methodVisitor) { this.methodVisitor = methodVisitor; } @Override public AnnotationVisitor visit(String annotationTypeDescriptor, boolean visible) { return methodVisitor.visitAnnotation(annotationTypeDescriptor, visible); } @Override public AnnotationVisitor visit(String annotationTypeDescriptor, boolean visible, int typeReference, String typePath) { return methodVisitor.visitTypeAnnotation(typeReference, TypePath.fromString(typePath), annotationTypeDescriptor, visible); } } /** * Target for an annotation that is written to a Java method or constructor parameter. */ @EqualsAndHashCode class OnMethodParameter implements Target { /** * The method visitor to write the annotation to. */ private final MethodVisitor methodVisitor; /** * The method parameter index to write the annotation to. */ private final int parameterIndex; /** * Creates a new wrapper for a Java method or constructor. * * @param methodVisitor The ASM method visitor to which the annotations are appended to. * @param parameterIndex The index of the method parameter. */ public OnMethodParameter(MethodVisitor methodVisitor, int parameterIndex) { this.methodVisitor = methodVisitor; this.parameterIndex = parameterIndex; } @Override public AnnotationVisitor visit(String annotationTypeDescriptor, boolean visible) { return methodVisitor.visitParameterAnnotation(parameterIndex, annotationTypeDescriptor, visible); } @Override public AnnotationVisitor visit(String annotationTypeDescriptor, boolean visible, int typeReference, String typePath) { return methodVisitor.visitTypeAnnotation(typeReference, TypePath.fromString(typePath), annotationTypeDescriptor, visible); } } /** * Target for an annotation that is written to a Java field. */ @EqualsAndHashCode class OnField implements Target { /** * The field visitor to write the annotation to. */ private final FieldVisitor fieldVisitor; /** * Creates a new wrapper for a Java field. * * @param fieldVisitor The ASM field visitor to which the annotations are appended to. */ public OnField(FieldVisitor fieldVisitor) { this.fieldVisitor = fieldVisitor; } @Override public AnnotationVisitor visit(String annotationTypeDescriptor, boolean visible) { return fieldVisitor.visitAnnotation(annotationTypeDescriptor, visible); } @Override public AnnotationVisitor visit(String annotationTypeDescriptor, boolean visible, int typeReference, String typePath) { return fieldVisitor.visitTypeAnnotation(typeReference, TypePath.fromString(typePath), annotationTypeDescriptor, visible); } } } /** * A default implementation for an annotation appender that writes annotations to a given byte consumer * represented by an ASM {@link org.objectweb.asm.AnnotationVisitor}. */ @EqualsAndHashCode class Default implements AnnotationAppender { /** * The target onto which an annotation write process is to be applied. */ private final Target target; /** * Creates a default annotation appender. * * @param target The target to which annotations are written to. */ public Default(Target target) { this.target = target; } /** * Handles the writing of a single annotation to an annotation visitor. * * @param annotationVisitor The annotation visitor the write process is to be applied on. * @param annotation The annotation to be written. * @param annotationValueFilter The value filter to apply for discovering which values of an annotation should be written. */ private static void handle(AnnotationVisitor annotationVisitor, AnnotationDescription annotation, AnnotationValueFilter annotationValueFilter) { for (MethodDescription.InDefinedShape methodDescription : annotation.getAnnotationType().getDeclaredMethods()) { if (annotationValueFilter.isRelevant(annotation, methodDescription)) { apply(annotationVisitor, methodDescription.getReturnType().asErasure(), methodDescription.getName(), annotation.getValue(methodDescription).resolve()); } } annotationVisitor.visitEnd(); } /** * Performs the writing of a given annotation value to an annotation visitor. * * @param annotationVisitor The annotation visitor the write process is to be applied on. * @param valueType The type of the annotation value. * @param name The name of the annotation type. * @param value The annotation's value. */ public static void apply(AnnotationVisitor annotationVisitor, TypeDescription valueType, String name, Object value) { if (valueType.isArray()) { // The Android emulator reads annotation arrays as annotation types. Therefore, this check needs to come first. AnnotationVisitor arrayVisitor = annotationVisitor.visitArray(name); int length = Array.getLength(value); TypeDescription componentType = valueType.getComponentType(); for (int index = 0; index < length; index++) { apply(arrayVisitor, componentType, NO_NAME, Array.get(value, index)); } arrayVisitor.visitEnd(); } else if (valueType.isAnnotation()) { handle(annotationVisitor.visitAnnotation(name, valueType.getDescriptor()), (AnnotationDescription) value, AnnotationValueFilter.Default.APPEND_DEFAULTS); } else if (valueType.isEnum()) { annotationVisitor.visitEnum(name, valueType.getDescriptor(), ((EnumerationDescription) value).getValue()); } else if (valueType.represents(Class.class)) { annotationVisitor.visit(name, Type.getType(((TypeDescription) value).getDescriptor())); } else { annotationVisitor.visit(name, value); } } @Override public AnnotationAppender append(AnnotationDescription annotationDescription, AnnotationValueFilter annotationValueFilter) { switch (annotationDescription.getRetention()) { case RUNTIME: doAppend(annotationDescription, true, annotationValueFilter); break; case CLASS: doAppend(annotationDescription, false, annotationValueFilter); break; case SOURCE: break; default: throw new IllegalStateException("Unexpected retention policy: " + annotationDescription.getRetention()); } return this; } /** * Tries to append a given annotation by reflectively reading an annotation. * * @param annotation The annotation to be written. * @param visible {@code true} if this annotation should be treated as visible at runtime. * @param annotationValueFilter The annotation value filter to apply. */ private void doAppend(AnnotationDescription annotation, boolean visible, AnnotationValueFilter annotationValueFilter) { handle(target.visit(annotation.getAnnotationType().getDescriptor(), visible), annotation, annotationValueFilter); } @Override public AnnotationAppender append(AnnotationDescription annotationDescription, AnnotationValueFilter annotationValueFilter, int typeReference, String typePath) { switch (annotationDescription.getRetention()) { case RUNTIME: doAppend(annotationDescription, true, annotationValueFilter, typeReference, typePath); break; case CLASS: doAppend(annotationDescription, false, annotationValueFilter, typeReference, typePath); break; case SOURCE: break; default: throw new IllegalStateException("Unexpected retention policy: " + annotationDescription.getRetention()); } return this; } /** * Tries to append a given annotation by reflectively reading an annotation. * * @param annotation The annotation to be written. * @param visible {@code true} if this annotation should be treated as visible at runtime. * @param annotationValueFilter The annotation value filter to apply. * @param typeReference The type annotation's type reference. * @param typePath The type annotation's type path. */ private void doAppend(AnnotationDescription annotation, boolean visible, AnnotationValueFilter annotationValueFilter, int typeReference, String typePath) { handle(target.visit(annotation.getAnnotationType().getDescriptor(), visible, typeReference, typePath), annotation, annotationValueFilter); } } /** * A type visitor that visits all type annotations of a generic type and writes any discovered annotation to a * supplied {@link AnnotationAppender}. */ @EqualsAndHashCode class ForTypeAnnotations implements TypeDescription.Generic.Visitor<AnnotationAppender> { /** * Indicates that type variables type annotations are written on a Java type. */ public static final boolean VARIABLE_ON_TYPE = true; /** * Indicates that type variables type annotations are written on a Java method or constructor. */ public static final boolean VARIABLE_ON_INVOKEABLE = false; /** * Represents an empty type path. */ private static final String EMPTY_TYPE_PATH = ""; /** * Represents a step to a component type within a type path. */ private static final char COMPONENT_TYPE_PATH = '['; /** * Represents a wildcard type step within a type path. */ private static final char WILDCARD_TYPE_PATH = '*'; /** * Represents a (reversed) type step to an inner class within a type path. */ private static final char INNER_CLASS_PATH = '.'; /** * Represents an index tzpe delimiter within a type path. */ private static final char INDEXED_TYPE_DELIMITER = ';'; /** * The index that indicates that super type type annotations are written onto a super class. */ private static final int SUPER_CLASS_INDEX = -1; /** * The annotation appender to use. */ private final AnnotationAppender annotationAppender; /** * The annotation value filter to use. */ private final AnnotationValueFilter annotationValueFilter; /** * The type reference to use. */ private final int typeReference; /** * The type path to use. */ private final String typePath; /** * Creates a new type annotation appending visitor for an empty type path. * * @param annotationAppender The annotation appender to use. * @param annotationValueFilter The annotation value filter to use. * @param typeReference The type reference to use. */ protected ForTypeAnnotations(AnnotationAppender annotationAppender, AnnotationValueFilter annotationValueFilter, TypeReference typeReference) { this(annotationAppender, annotationValueFilter, typeReference.getValue(), EMPTY_TYPE_PATH); } /** * Creates a new type annotation appending visitor. * * @param annotationAppender The annotation appender to use. * @param annotationValueFilter The annotation value filter to use. * @param typeReference The type reference to use. * @param typePath The type path to use. */ protected ForTypeAnnotations(AnnotationAppender annotationAppender, AnnotationValueFilter annotationValueFilter, int typeReference, String typePath) { this.annotationAppender = annotationAppender; this.annotationValueFilter = annotationValueFilter; this.typeReference = typeReference; this.typePath = typePath; } /** * Creates a type annotation appender for a type annotations of a super class type. * * @param annotationAppender The annotation appender to write any type annotation to. * @param annotationValueFilter The annotation value filter to apply. * @return A visitor for appending type annotations of a super class. */ public static TypeDescription.Generic.Visitor<AnnotationAppender> ofSuperClass(AnnotationAppender annotationAppender, AnnotationValueFilter annotationValueFilter) { return new ForTypeAnnotations(annotationAppender, annotationValueFilter, TypeReference.newSuperTypeReference(SUPER_CLASS_INDEX)); } /** * Creates a type annotation appender for type annotations of an interface type. * * @param annotationAppender The annotation appender to write any type annotation to. * @param annotationValueFilter The annotation value filter to apply. * @param index The index of the interface type. * @return A visitor for appending type annotations of an interface type. */ public static TypeDescription.Generic.Visitor<AnnotationAppender> ofInterfaceType(AnnotationAppender annotationAppender, AnnotationValueFilter annotationValueFilter, int index) { return new ForTypeAnnotations(annotationAppender, annotationValueFilter, TypeReference.newSuperTypeReference(index)); } /** * Creates a type annotation appender for type annotations of a field's type. * * @param annotationAppender The annotation appender to write any type annotation to. * @param annotationValueFilter The annotation value filter to apply. * @return A visitor for appending type annotations of a field's type. */ public static TypeDescription.Generic.Visitor<AnnotationAppender> ofFieldType(AnnotationAppender annotationAppender, AnnotationValueFilter annotationValueFilter) { return new ForTypeAnnotations(annotationAppender, annotationValueFilter, TypeReference.newTypeReference(TypeReference.FIELD)); } /** * Creates a type annotation appender for type annotations of a method's return type. * * @param annotationAppender The annotation appender to write any type annotation to. * @param annotationValueFilter The annotation value filter to apply. * @return A visitor for appending type annotations of a method's return type. */ public static TypeDescription.Generic.Visitor<AnnotationAppender> ofMethodReturnType(AnnotationAppender annotationAppender, AnnotationValueFilter annotationValueFilter) { return new ForTypeAnnotations(annotationAppender, annotationValueFilter, TypeReference.newTypeReference(TypeReference.METHOD_RETURN)); } /** * Creates a type annotation appender for type annotations of a method's parameter type. * * @param annotationAppender The annotation appender to write any type annotation to. * @param annotationValueFilter The annotation value filter to apply. * @param index The parameter index. * @return A visitor for appending type annotations of a method's parameter type. */ public static TypeDescription.Generic.Visitor<AnnotationAppender> ofMethodParameterType(AnnotationAppender annotationAppender, AnnotationValueFilter annotationValueFilter, int index) { return new ForTypeAnnotations(annotationAppender, annotationValueFilter, TypeReference.newFormalParameterReference(index)); } /** * Creates a type annotation appender for type annotations of a method's exception type. * * @param annotationAppender The annotation appender to write any type annotation to. * @param annotationValueFilter The annotation value filter to apply. * @param index The exception type's index. * @return A visitor for appending type annotations of a method's exception type. */ public static TypeDescription.Generic.Visitor<AnnotationAppender> ofExceptionType(AnnotationAppender annotationAppender, AnnotationValueFilter annotationValueFilter, int index) { return new ForTypeAnnotations(annotationAppender, annotationValueFilter, TypeReference.newExceptionReference(index)); } /** * Creates a type annotation appender for type annotations of a method's receiver type. * * @param annotationAppender The annotation appender to write any type annotation to. * @param annotationValueFilter The annotation value filter to apply. * @return A visitor for appending type annotations of a method's receiver type. */ public static TypeDescription.Generic.Visitor<AnnotationAppender> ofReceiverType(AnnotationAppender annotationAppender, AnnotationValueFilter annotationValueFilter) { return new ForTypeAnnotations(annotationAppender, annotationValueFilter, TypeReference.newTypeReference(TypeReference.METHOD_RECEIVER)); } /** * Appends all supplied type variables to the supplied method appender. * * @param annotationAppender The annotation appender to write any type annotation to. * @param annotationValueFilter The annotation value filter to apply. * @param variableOnType {@code true} if the type variables are declared by a type, {@code false} if they are declared by a method. * @param typeVariables The type variables to append. * @return The resulting annotation appender. */ public static AnnotationAppender ofTypeVariable(AnnotationAppender annotationAppender, AnnotationValueFilter annotationValueFilter, boolean variableOnType, List<? extends TypeDescription.Generic> typeVariables) { return ofTypeVariable(annotationAppender, annotationValueFilter, variableOnType, 0, typeVariables); } /** * Appends all supplied type variables to the supplied method appender. * * @param annotationAppender The annotation appender to write any type annotation to. * @param annotationValueFilter The annotation value filter to apply. * @param variableOnType {@code true} if the type variables are declared by a type, {@code false} if they are declared by a method. * @param subListIndex The index of the first type variable to append. All previous type variables are ignored. * @param typeVariables The type variables to append. * @return The resulting annotation appender. */ public static AnnotationAppender ofTypeVariable(AnnotationAppender annotationAppender, AnnotationValueFilter annotationValueFilter, boolean variableOnType, int subListIndex, List<? extends TypeDescription.Generic> typeVariables) { int typeVariableIndex = subListIndex, variableBaseReference, variableBoundBaseBase; if (variableOnType) { variableBaseReference = TypeReference.CLASS_TYPE_PARAMETER; variableBoundBaseBase = TypeReference.CLASS_TYPE_PARAMETER_BOUND; } else { variableBaseReference = TypeReference.METHOD_TYPE_PARAMETER; variableBoundBaseBase = TypeReference.METHOD_TYPE_PARAMETER_BOUND; } for (TypeDescription.Generic typeVariable : typeVariables.subList(subListIndex, typeVariables.size())) { int typeReference = TypeReference.newTypeParameterReference(variableBaseReference, typeVariableIndex).getValue(); for (AnnotationDescription annotationDescription : typeVariable.getDeclaredAnnotations()) { annotationAppender = annotationAppender.append(annotationDescription, annotationValueFilter, typeReference, EMPTY_TYPE_PATH); } int boundIndex = !typeVariable.getUpperBounds().get(0).getSort().isTypeVariable() && typeVariable.getUpperBounds().get(0).isInterface() ? 1 : 0; for (TypeDescription.Generic typeBound : typeVariable.getUpperBounds()) { annotationAppender = typeBound.accept(new ForTypeAnnotations(annotationAppender, annotationValueFilter, TypeReference.newTypeParameterBoundReference(variableBoundBaseBase, typeVariableIndex, boundIndex++))); } typeVariableIndex++; } return annotationAppender; } @Override public AnnotationAppender onGenericArray(TypeDescription.Generic genericArray) { return genericArray.getComponentType().accept(new ForTypeAnnotations(apply(genericArray, typePath), annotationValueFilter, typeReference, typePath + COMPONENT_TYPE_PATH)); } @Override public AnnotationAppender onWildcard(TypeDescription.Generic wildcard) { TypeList.Generic lowerBounds = wildcard.getLowerBounds(); return (lowerBounds.isEmpty() ? wildcard.getUpperBounds().getOnly() : lowerBounds.getOnly()).accept(new ForTypeAnnotations(apply(wildcard, typePath), annotationValueFilter, typeReference, typePath + WILDCARD_TYPE_PATH)); } @Override public AnnotationAppender onParameterizedType(TypeDescription.Generic parameterizedType) { StringBuilder typePath = new StringBuilder(this.typePath); for (int index = 0; index < parameterizedType.asErasure().getSegmentCount(); index++) { typePath = typePath.append(INNER_CLASS_PATH); } AnnotationAppender annotationAppender = apply(parameterizedType, typePath.toString()); TypeDescription.Generic ownerType = parameterizedType.getOwnerType(); if (ownerType != null) { annotationAppender = ownerType.accept(new ForTypeAnnotations(annotationAppender, annotationValueFilter, typeReference, this.typePath)); } int index = 0; for (TypeDescription.Generic typeArgument : parameterizedType.getTypeArguments()) { annotationAppender = typeArgument.accept(new ForTypeAnnotations(annotationAppender, annotationValueFilter, typeReference, typePath.toString() + index++ + INDEXED_TYPE_DELIMITER)); } return annotationAppender; } @Override public AnnotationAppender onTypeVariable(TypeDescription.Generic typeVariable) { return apply(typeVariable, typePath); } @Override public AnnotationAppender onNonGenericType(TypeDescription.Generic typeDescription) { StringBuilder typePath = new StringBuilder(this.typePath); for (int index = 0; index < typeDescription.asErasure().getSegmentCount(); index++) { typePath = typePath.append(INNER_CLASS_PATH); } AnnotationAppender annotationAppender = apply(typeDescription, typePath.toString()); if (typeDescription.isArray()) { annotationAppender = typeDescription.getComponentType().accept(new ForTypeAnnotations(annotationAppender, annotationValueFilter, typeReference, this.typePath + COMPONENT_TYPE_PATH)); // Impossible to be inner class } return annotationAppender; } /** * Writes all annotations of the supplied type to this instance's annotation appender. * * @param typeDescription The type of what all annotations should be written of. * @param typePath The type path to use. * @return The resulting annotation appender. */ private AnnotationAppender apply(TypeDescription.Generic typeDescription, String typePath) { AnnotationAppender annotationAppender = this.annotationAppender; for (AnnotationDescription annotationDescription : typeDescription.getDeclaredAnnotations()) { annotationAppender = annotationAppender.append(annotationDescription, annotationValueFilter, typeReference, typePath); } return annotationAppender; } } }