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 }