package net.bytebuddy.implementation.attribute; import lombok.EqualsAndHashCode; import net.bytebuddy.description.annotation.AnnotationDescription; import net.bytebuddy.description.annotation.AnnotationList; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeList; import org.objectweb.asm.ClassVisitor; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * An appender that writes attributes or annotations to a given ASM {@link org.objectweb.asm.ClassVisitor}. */ public interface TypeAttributeAppender { /** * Applies this type attribute appender. * * @param classVisitor The class visitor to which the annotations of this visitor should be written to. * @param instrumentedType A description of the instrumented type that is target of the ongoing instrumentation. * @param annotationValueFilter The annotation value filter to apply when writing annotations. */ void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter); /** * A type attribute appender that does not append any attributes. */ enum NoOp implements TypeAttributeAppender { /** * The singleton instance. */ INSTANCE; @Override public void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter) { /* do nothing */ } } /** * An attribute appender that writes all annotations that are found on a given target type to the * instrumented type this type attribute appender is applied onto. The visibility for the annotation * will be inferred from the annotations' {@link java.lang.annotation.RetentionPolicy}. */ enum ForInstrumentedType implements TypeAttributeAppender { /** * The singleton instance. */ INSTANCE; @Override public void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter) { AnnotationAppender annotationAppender = new AnnotationAppender.Default(new AnnotationAppender.Target.OnType(classVisitor)); annotationAppender = AnnotationAppender.ForTypeAnnotations.ofTypeVariable(annotationAppender, annotationValueFilter, AnnotationAppender.ForTypeAnnotations.VARIABLE_ON_TYPE, instrumentedType.getTypeVariables()); TypeDescription.Generic superClass = instrumentedType.getSuperClass(); if (superClass != null) { annotationAppender = superClass.accept(AnnotationAppender.ForTypeAnnotations.ofSuperClass(annotationAppender, annotationValueFilter)); } int interfaceIndex = 0; for (TypeDescription.Generic interfaceType : instrumentedType.getInterfaces()) { annotationAppender = interfaceType.accept(AnnotationAppender.ForTypeAnnotations.ofInterfaceType(annotationAppender, annotationValueFilter, interfaceIndex++)); } for (AnnotationDescription annotation : instrumentedType.getDeclaredAnnotations()) { annotationAppender = annotationAppender.append(annotation, annotationValueFilter); } } /** * A type attribute appender that writes all annotations of the instrumented but excludes annotations up to * a given index. */ @EqualsAndHashCode public static class Differentiating implements TypeAttributeAppender { /** * The index of the first annotations that should be directly written onto the type. */ private final int annotationIndex; /** * The index of the first type variable for which type annotations should be directly written onto the type. */ private final int typeVariableIndex; /** * The index of the first interface type for which type annotations should be directly written onto the type. */ private final int interfaceTypeIndex; /** * Creates a new differentiating type attribute appender. * * @param typeDescription The type for which to resolve all exclusion indices. */ public Differentiating(TypeDescription typeDescription) { this(typeDescription.getDeclaredAnnotations().size(), typeDescription.getTypeVariables().size(), typeDescription.getInterfaces().size()); } /** * Creates a new differentiating type attribute appender. * * @param annotationIndex The index of the first annotations that should be directly written onto the type. * @param typeVariableIndex The index of the first interface type for which type annotations should be directly written onto the type. * @param interfaceTypeIndex The index of the first interface type for which type annotations should be directly written onto the type. */ protected Differentiating(int annotationIndex, int typeVariableIndex, int interfaceTypeIndex) { this.annotationIndex = annotationIndex; this.typeVariableIndex = typeVariableIndex; this.interfaceTypeIndex = interfaceTypeIndex; } @Override public void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter) { AnnotationAppender annotationAppender = new AnnotationAppender.Default(new AnnotationAppender.Target.OnType(classVisitor)); AnnotationAppender.ForTypeAnnotations.ofTypeVariable(annotationAppender, annotationValueFilter, AnnotationAppender.ForTypeAnnotations.VARIABLE_ON_TYPE, typeVariableIndex, instrumentedType.getTypeVariables()); TypeList.Generic interfaceTypes = instrumentedType.getInterfaces(); int interfaceTypeIndex = this.interfaceTypeIndex; for (TypeDescription.Generic interfaceType : interfaceTypes.subList(this.interfaceTypeIndex, interfaceTypes.size())) { annotationAppender = interfaceType.accept(AnnotationAppender.ForTypeAnnotations.ofInterfaceType(annotationAppender, annotationValueFilter, interfaceTypeIndex++)); } AnnotationList declaredAnnotations = instrumentedType.getDeclaredAnnotations(); for (AnnotationDescription annotationDescription : declaredAnnotations.subList(annotationIndex, declaredAnnotations.size())) { annotationAppender = annotationAppender.append(annotationDescription, annotationValueFilter); } } } } /** * An attribute appender that appends a single annotation to a given type. The visibility for the annotation * will be inferred from the annotation's {@link java.lang.annotation.RetentionPolicy}. */ @EqualsAndHashCode class Explicit implements TypeAttributeAppender { /** * The annotations to write to the given type. */ private final List<? extends AnnotationDescription> annotations; /** * Creates a new annotation attribute appender for explicit annotation values. * * @param annotations The annotations to write to the given type. */ public Explicit(List<? extends AnnotationDescription> annotations) { this.annotations = annotations; } @Override public void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter) { AnnotationAppender appender = new AnnotationAppender.Default(new AnnotationAppender.Target.OnType(classVisitor)); for (AnnotationDescription annotation : annotations) { appender = appender.append(annotation, annotationValueFilter); } } } /** * A compound type attribute appender that concatenates a number of other attribute appenders. */ @EqualsAndHashCode class Compound implements TypeAttributeAppender { /** * The type attribute appenders this compound appender represents in their application order. */ private final List<TypeAttributeAppender> typeAttributeAppenders; /** * Creates a new compound attribute appender. * * @param typeAttributeAppender The type attribute appenders to concatenate in the order of their application. */ public Compound(TypeAttributeAppender... typeAttributeAppender) { this(Arrays.asList(typeAttributeAppender)); } /** * Creates a new compound attribute appender. * * @param typeAttributeAppenders The type attribute appenders to concatenate in the order of their application. */ public Compound(List<? extends TypeAttributeAppender> typeAttributeAppenders) { this.typeAttributeAppenders = new ArrayList<TypeAttributeAppender>(); for (TypeAttributeAppender typeAttributeAppender : typeAttributeAppenders) { if (typeAttributeAppender instanceof Compound) { this.typeAttributeAppenders.addAll(((Compound) typeAttributeAppender).typeAttributeAppenders); } else if (!(typeAttributeAppender instanceof NoOp)) { this.typeAttributeAppenders.add(typeAttributeAppender); } } } @Override public void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter) { for (TypeAttributeAppender typeAttributeAppender : typeAttributeAppenders) { typeAttributeAppender.apply(classVisitor, instrumentedType, annotationValueFilter); } } } }