View Javadoc

    1   /***************************************************************************************
    2    * Copyright (c) Jonas Bon?r, Alexandre Vasseur. All rights reserved.                 *
    3    * http://aspectwerkz.codehaus.org                                                    *
    4    * ---------------------------------------------------------------------------------- *
    5    * The software in this package is published under the terms of the LGPL license      *
    6    * a copy of which has been included with this distribution in the license.txt file.  *
    7    **************************************************************************************/
    8   package org.codehaus.aspectwerkz.annotation.instrumentation.asm;
    9
    10  import org.objectweb.asm.ClassVisitor;
    11  import org.objectweb.asm.Attribute;
    12  import org.objectweb.asm.CodeVisitor;
    13  import org.objectweb.asm.Label;
    14  import org.objectweb.asm.Type;
    15  import org.objectweb.asm.attrs.RuntimeInvisibleAnnotations;
    16  import org.objectweb.asm.attrs.Annotation;
    17  import org.objectweb.asm.attrs.RuntimeVisibleAnnotations;
    18  import org.objectweb.asm.attrs.AnnotationDefaultAttribute;
    19  import org.objectweb.asm.attrs.Attributes;
    20  import org.codehaus.aspectwerkz.annotation.AnnotationInfo;
    21  import org.codehaus.aspectwerkz.annotation.AnnotationDefault;
    22  import org.codehaus.aspectwerkz.annotation.Java5AnnotationInvocationHandler;
    23
    24  import java.util.List;
    25  import java.util.Iterator;
    26
    27  /***
    28   * Helper visitor to extract Annotations.
    29   * The visitors are not writing any bytecode and using a Null ClassVisitor / Code Visitor as a target instead.
    30   *
    31   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur</a>
    32   */
    33  public class AsmAnnotationHelper {
    34
    35      private final static String INIT_METHOD_NAME = "<init>";
    36
    37      /***
    38       * Generic extractor
    39       */
    40      private static class AnnotationExtractor extends NullClassAdapter {
    41
    42          /***
    43           * The list where encountered annotation will be put
    44           */
    45          protected List m_annotations;
    46
    47          /***
    48           * This classloader will be used to instantiate the proxy instance for Custom Annotation support (1.3/1.4).
    49           * See CustomAttribute that wraps in a RuntimeInvisibleAnnotation the user custom annotations.
    50           * <br/>Note: no weak reference is used since the visitor is created for a one shot usage.
    51           */
    52          protected ClassLoader m_loader;
    53
    54          /***
    55           * Generic extractor
    56           *
    57           * @param annotations list where to put annotations
    58           * @param loader      classLoader used to instantiate proxy of custom annotations
    59           */
    60          private AnnotationExtractor(List annotations, final ClassLoader loader) {
    61              m_annotations = annotations;
    62              m_loader = loader;
    63          }
    64      }
    65
    66      /***
    67       * Extracts class level annotations
    68       */
    69      public static class ClassAnnotationExtractor extends AnnotationExtractor {
    70
    71          public ClassAnnotationExtractor(List annotations, final ClassLoader loader) {
    72              super(annotations, loader);
    73          }
    74
    75          public void visitAttribute(final Attribute attribute) {
    76              m_annotations = extractAnnotations(m_annotations, attribute, m_loader);
    77              super.visitAttribute(attribute);
    78          }
    79      }
    80
    81      /***
    82       * Generic extractor for member (ctor, method, field) annotations extraction
    83       */
    84      private static class MemberAnnotationExtractor extends AnnotationExtractor {
    85
    86          /***
    87           * Member name (method name, "<init>", field name
    88           */
    89          protected String m_name;
    90
    91          /***
    92           * Member descriptor (as in visitMethod/visitField ASM methods)
    93           */
    94          protected String m_desc;
    95
    96          /***
    97           * Method annotation extractor
    98           *
    99           * @param annotations
    100          * @param name        of the member for which we want the annotations
    101          * @param desc        of the member for which we want the annotations
    102          * @param loader
    103          */
    104         private MemberAnnotationExtractor(List annotations, String name, String desc, final ClassLoader loader) {
    105             super(annotations, loader);
    106             m_name = name;
    107             m_desc = desc;
    108         }
    109     }
    110
    111     /***
    112      * Method annotations extractor
    113      */
    114     public static class MethodAnnotationExtractor extends MemberAnnotationExtractor {
    115
    116         public MethodAnnotationExtractor(List annotations, String name, String desc, final ClassLoader loader) {
    117             super(annotations, name, desc, loader);
    118         }
    119
    120         public CodeVisitor visitMethod(final int access,
    121                                        final String name,
    122                                        final String desc,
    123                                        final String[] exceptions,
    124                                        final Attribute attrs) {
    125             if (name.equals(m_name) && desc.equals(m_desc)) {
    126                 m_annotations = extractAnnotations(m_annotations, attrs, m_loader);
    127             }
    128             return super.visitMethod(access, name, desc, exceptions, attrs);
    129         }
    130     }
    131
    132     /***
    133      * Constructor annotations extractor
    134      */
    135     public static class ConstructorAnnotationExtractor extends MethodAnnotationExtractor {
    136
    137         public ConstructorAnnotationExtractor(List annotations, String desc, final ClassLoader loader) {
    138             super(annotations, INIT_METHOD_NAME, desc, loader);
    139         }
    140     }
    141
    142     /***
    143      * Field annotations extractor
    144      */
    145     public static class FieldAnnotationExtractor extends MemberAnnotationExtractor {
    146
    147         public FieldAnnotationExtractor(List annotations, String name, final ClassLoader loader) {
    148             super(annotations, name, null, loader);
    149         }
    150
    151         public void visitField(final int access,
    152                                final String name,
    153                                final String desc,
    154                                final Object value,
    155                                final Attribute attrs) {
    156             // no match on desc
    157             if (name.equals(m_name)) {
    158                 m_annotations = extractAnnotations(m_annotations, attrs, m_loader);
    159             }
    160             super.visitField(access, name, desc, value, attrs);
    161         }
    162     }
    163
    164     /***
    165      * A NullClassAdapter that does nothing.
    166      * Can be used to speed up ASM and avoid unecessary bytecode writing thru a regular ClassWriter when this is not
    167      * needed (read only purpose).
    168      */
    169     public static class NullClassAdapter implements ClassVisitor {
    170
    171         public final static ClassVisitor NULL_CLASS_ADAPTER = new NullClassAdapter();
    172
    173         public void visit(int i, int i1, String s, String s1, String[] strings, String s2) {
    174         }
    175
    176         public void visitInnerClass(String s, String s1, String s2, int i) {
    177         }
    178
    179         public void visitField(int i, String s, String s1, Object o, Attribute attribute) {
    180         }
    181
    182         public CodeVisitor visitMethod(int i, String s, String s1, String[] strings, Attribute attribute) {
    183             return NullCodeAdapter.NULL_CODE_ADAPTER;
    184         }
    185
    186         public void visitAttribute(Attribute attribute) {
    187         }
    188
    189         public void visitEnd() {
    190         }
    191     }
    192
    193     /***
    194      * A NullCodeAdapter that does nothing.
    195      * Can be used to speed up ASM and avoid unecessary bytecode writing thru a regular CodeWriter when this is not
    196      * needed (read only purpose)
    197      */
    198     public static class NullCodeAdapter implements CodeVisitor {
    199
    200         public final static CodeVisitor NULL_CODE_ADAPTER = new NullCodeAdapter();
    201
    202         public void visitInsn(int opcode) {
    203         }
    204
    205         public void visitIntInsn(int opcode, int operand) {
    206         }
    207
    208         public void visitVarInsn(int opcode, int var) {
    209         }
    210
    211         public void visitTypeInsn(int opcode, String desc) {
    212         }
    213
    214         public void visitFieldInsn(int opcode, String owner, String name, String desc) {
    215         }
    216
    217         public void visitMethodInsn(int opcode, String owner, String name, String desc) {
    218         }
    219
    220         public void visitJumpInsn(int opcode, Label label) {
    221         }
    222
    223         public void visitLabel(Label label) {
    224         }
    225
    226         public void visitLdcInsn(Object cst) {
    227         }
    228
    229         public void visitIincInsn(int var, int increment) {
    230         }
    231
    232         public void visitTableSwitchInsn(int min, int max, Label dflt, Label labels[]) {
    233         }
    234
    235         public void visitLookupSwitchInsn(Label dflt, int keys[], Label labels[]) {
    236         }
    237
    238         public void visitMultiANewArrayInsn(String desc, int dims) {
    239         }
    240
    241         public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
    242         }
    243
    244         public void visitMaxs(int maxStack, int maxLocals) {
    245         }
    246
    247         public void visitLocalVariable(String name, String desc, Label start, Label end, int index) {
    248         }
    249
    250         public void visitLineNumber(int line, Label start) {
    251         }
    252
    253         public void visitAttribute(Attribute attr) {
    254         }
    255     }
    256
    257     /***
    258      * Helper method to extract Runtime(In)VisibleAnnotations and unwrap custom annotation proxies
    259      *
    260      * @param annotations
    261      * @param attribute
    262      * @param loader
    263      * @return annotations list populated
    264      */
    265     public static List extractAnnotations(List annotations, final Attribute attribute, final ClassLoader loader) {
    266         for (Attribute current = attribute; current != null; current = current.next) {
    267             if (current instanceof RuntimeInvisibleAnnotations) {
    268                 for (Iterator it = ((RuntimeInvisibleAnnotations) current).annotations.iterator(); it.hasNext();) {
    269                     Annotation annotation = (Annotation) it.next();
    270                     if (CustomAttribute.TYPE.equals(annotation.type)) {
    271                         annotations.add(CustomAttributeHelper.extractCustomAnnotation(annotation));
    272                     } else {
    273                         AnnotationInfo annotationInfo = getAnnotationInfo(annotation, loader);
    274                         annotations.add(annotationInfo);
    275                     }
    276                 }
    277             } else if (current instanceof RuntimeVisibleAnnotations) {
    278                 for (Iterator it = ((RuntimeVisibleAnnotations) current).annotations.iterator(); it.hasNext();) {
    279                     Annotation annotation = (Annotation) it.next();
    280                     AnnotationInfo annotationInfo = getAnnotationInfo(annotation, loader);
    281                     annotations.add(annotationInfo);
    282                 }
    283             } else if (current instanceof AnnotationDefaultAttribute) {
    284                 AnnotationDefaultAttribute defaultAttribute = (AnnotationDefaultAttribute) current;
    285                 AnnotationInfo annotationInfo = new AnnotationInfo(
    286                         AnnotationDefault.NAME,
    287                         new AnnotationDefault.AnnotationDefaultImpl(defaultAttribute.defaultValue)
    288                 );
    289                 annotations.add(annotationInfo);
    290             }
    291         }
    292         return annotations;
    293     }
    294
    295     /***
    296      * Creates and returns a new annotation info build up from the Java5 annotation.
    297      *
    298      * @param annotation the ASM annotation abstraction
    299      * @param loader     the class loader that has loaded the proxy class to use
    300      * @return the annotation info
    301      */
    302     public static AnnotationInfo getAnnotationInfo(final Annotation annotation, final ClassLoader loader) {
    303         String annotationName = Type.getType(annotation.type).getClassName();
    304         return new AnnotationInfo(annotationName,
    305                                   Java5AnnotationInvocationHandler.getAnnotationProxy(annotation, loader)
    306         );
    307     }
    308 }