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