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.transform.inlining.weaver;
9
10 import org.objectweb.asm.ClassAdapter;
11 import org.objectweb.asm.ClassVisitor;
12 import org.objectweb.asm.CodeVisitor;
13 import org.objectweb.asm.Attribute;
14 import org.objectweb.asm.CodeAdapter;
15 import org.objectweb.asm.Label;
16 import org.objectweb.asm.Type;
17 import org.codehaus.aspectwerkz.definition.SystemDefinition;
18 import org.codehaus.aspectwerkz.expression.ExpressionContext;
19 import org.codehaus.aspectwerkz.expression.PointcutType;
20 import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
21 import org.codehaus.aspectwerkz.reflect.ClassInfo;
22 import org.codehaus.aspectwerkz.reflect.MemberInfo;
23 import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
24 import org.codehaus.aspectwerkz.transform.Context;
25 import org.codehaus.aspectwerkz.transform.TransformationUtil;
26 import org.codehaus.aspectwerkz.transform.TransformationConstants;
27 import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
28 import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
29 import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
30
31 import java.util.Iterator;
32 import java.util.Set;
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.HashMap;
37 import java.lang.reflect.Modifier;
38
39 /***
40 * Advises catch clauses by inserting a call to the join point as the first thing in the catch block.
41 *
42 * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
43 */
44 public class HandlerVisitor extends ClassAdapter implements TransformationConstants {
45
46 /***
47 * A visitor that looks for all catch clause and keep track of them
48 * providing that they match
49 *
50 * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
51 */
52 public static class LookaheadCatchLabelsClassAdapter extends ClassAdapter {
53 /***
54 * list of CatchLabelStruct that matches
55 */
56 List m_catchLabels = new ArrayList();
57
58 /***
59 * map of Integer(index in whole class)-->asm.Label for all the visited labels
60 */
61 private final Map m_labelIndexes = new HashMap();
62
63 /***
64 * current label index in whole class, from 0 to N
65 */
66 private int m_labelIndex = -1;
67
68 private final ContextImpl m_ctx;
69 private final ClassLoader m_loader;
70 private final ClassInfo m_callerClassInfo;
71
72 /***
73 * Visit the class
74 *
75 * @param cv
76 * @param loader
77 * @param callerClassInfo
78 * @param ctx
79 * @param catchLabels
80 */
81 public LookaheadCatchLabelsClassAdapter(ClassVisitor cv, ClassLoader loader, ClassInfo callerClassInfo,
82 Context ctx, List catchLabels) {
83 super(cv);
84 m_catchLabels = catchLabels;
85 m_loader = loader;
86 m_callerClassInfo = callerClassInfo;
87 m_ctx = (ContextImpl) ctx;
88 }
89
90 /***
91 * Visit method bodies
92 *
93 * @param access
94 * @param callerMethodName
95 * @param callerMethodDesc
96 * @param exceptions
97 * @param attrs
98 * @return
99 */
100 public CodeVisitor visitMethod(final int access,
101 final String callerMethodName,
102 final String callerMethodDesc,
103 final String[] exceptions,
104 final Attribute attrs) {
105 if (callerMethodName.startsWith(WRAPPER_METHOD_PREFIX)) {
106 return super.visitMethod(access, callerMethodName, callerMethodDesc, exceptions, attrs);
107 }
108
109 CodeVisitor mv = cv.visitMethod(access, callerMethodName, callerMethodDesc, exceptions, attrs);
110 if (mv == null) {
111 return mv;
112 }
113
114 final MemberInfo callerMemberInfo;
115 if (CLINIT_METHOD_NAME.equals(callerMethodName)) {
116 callerMemberInfo = m_callerClassInfo.staticInitializer();
117 } else if (INIT_METHOD_NAME.equals(callerMethodName)) {
118 int hash = AsmHelper.calculateConstructorHash(callerMethodDesc);
119 callerMemberInfo = m_callerClassInfo.getConstructor(hash);
120 } else {
121 int hash = AsmHelper.calculateMethodHash(callerMethodName, callerMethodDesc);
122 callerMemberInfo = m_callerClassInfo.getMethod(hash);
123 }
124 if (callerMemberInfo == null) {
125 System.err.println(
126 "AW::WARNING " +
127 "metadata structure could not be build for method ["
128 + m_callerClassInfo.getName().replace('/', '.')
129 + '.' + callerMethodName + ':' + callerMethodDesc + ']'
130 );
131 return mv;
132 }
133
134 /***
135 * Visit the method, and keep track of all labels so that when visittryCatch is reached
136 * we can remember the index
137 *
138 * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
139 */
140 return new CodeAdapter(mv) {
141 public void visitLabel(Label label) {
142 m_labelIndexes.put(label, new Integer(++m_labelIndex));
143 super.visitLabel(label);
144 }
145
146 public void visitTryCatchBlock(Label startLabel, Label endLabel, Label handlerLabel,
147 String exceptionTypeName) {
148 if (exceptionTypeName == null) {
149
150 super.visitTryCatchBlock(startLabel, endLabel, handlerLabel, exceptionTypeName);
151 return;
152 }
153 final ClassInfo exceptionClassInfo = AsmClassInfo.getClassInfo(exceptionTypeName, m_loader);
154 final ExpressionContext ctx = new ExpressionContext(
155 PointcutType.HANDLER, exceptionClassInfo, callerMemberInfo
156 );
157 if (!handlerFilter(m_ctx.getDefinitions(), ctx)) {
158
159 Integer index = (Integer) m_labelIndexes.get(handlerLabel);
160 if (index != null) {
161 m_catchLabels.add(
162 new CatchLabelStruct(
163 index.intValue(),
164 exceptionClassInfo,
165 m_callerClassInfo,
166 callerMemberInfo
167 )
168 );
169 }
170 }
171 super.visitTryCatchBlock(startLabel, endLabel, handlerLabel, exceptionTypeName);
172 }
173 };
174 }
175 }
176
177
178
179 private final ContextImpl m_ctx;
180
181 /***
182 * List of matching catch clause
183 */
184 private final List m_catchLabels;
185
186 /***
187 * catch clause index in whole class
188 */
189 private int m_labelIndex = -1;
190
191 private Label m_lastLabelForLineNumber = EmittedJoinPoint.NO_LINE_NUMBER;
192
193
194 /***
195 * Creates a new instance.
196 *
197 * @param cv
198 * @param ctx
199 */
200 public HandlerVisitor(final ClassVisitor cv,
201 final Context ctx,
202 final List catchLabels) {
203 super(cv);
204 m_ctx = (ContextImpl) ctx;
205 m_catchLabels = catchLabels;
206 }
207
208 /***
209 * Visits the methods bodies to weave in JP calls at catch clauses
210 *
211 * @param access
212 * @param name
213 * @param desc
214 * @param exceptions
215 * @param attrs
216 * @return
217 */
218 public CodeVisitor visitMethod(final int access,
219 final String name,
220 final String desc,
221 final String[] exceptions,
222 final Attribute attrs) {
223 if (name.startsWith(WRAPPER_METHOD_PREFIX)) {
224 return super.visitMethod(access, name, desc, exceptions, attrs);
225 }
226
227 CodeVisitor mv = cv.visitMethod(access, name, desc, exceptions, attrs);
228 return mv == null ? null : new CatchClauseCodeAdapter(mv);
229 }
230
231 /***
232 * Advises catch clauses by inserting a call to the join point as the first thing in the catch block.
233 *
234 * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
235 */
236 public class CatchClauseCodeAdapter extends CodeAdapter {
237
238 /***
239 * Creates a new instance.
240 *
241 * @param ca
242 */
243 public CatchClauseCodeAdapter(final CodeVisitor ca) {
244 super(ca);
245 }
246
247 public void visitLabel(Label label) {
248 m_lastLabelForLineNumber = label;
249 super.visitLabel(label);
250
251
252 int index = ++m_labelIndex;
253 CatchLabelStruct catchLabel = null;
254 for (Iterator iterator = m_catchLabels.iterator(); iterator.hasNext();) {
255 CatchLabelStruct aCatchLabel = (CatchLabelStruct) iterator.next();
256 if (aCatchLabel.labelIndexInWholeClass == index) {
257 catchLabel = aCatchLabel;
258 break;
259 }
260 }
261 if (catchLabel == null) {
262 return;
263 }
264
265 m_ctx.markAsAdvised();
266 final String callerTypeName = catchLabel.caller.getName().replace('.', '/');
267 final String exceptionTypeDesc = catchLabel.exception.getSignature();
268 final String exceptionTypeName = Type.getType(exceptionTypeDesc).getInternalName();
269 final int joinPointHash = AsmHelper.calculateClassHash(exceptionTypeDesc);
270 final String joinPointClassName = TransformationUtil.getJoinPointClassName(
271 callerTypeName,
272 catchLabel.callerMember.getName(),
273 catchLabel.callerMember.getSignature(),
274 exceptionTypeName,
275 JoinPointType.HANDLER_INT,
276 joinPointHash
277 );
278
279
280
281
282 cv.visitInsn(DUP);
283
284
285 if (Modifier.isStatic(catchLabel.callerMember.getModifiers())) {
286 cv.visitInsn(ACONST_NULL);
287 } else {
288 cv.visitVarInsn(ALOAD, 0);
289 }
290
291
292 cv.visitMethodInsn(
293 INVOKESTATIC, joinPointClassName, INVOKE_METHOD_NAME,
294 TransformationUtil.getInvokeSignatureForHandlerJoinPoints(callerTypeName, exceptionTypeName)
295 );
296
297
298 m_ctx.addEmittedJoinPoint(
299 new EmittedJoinPoint(
300 JoinPointType.HANDLER_INT,
301 callerTypeName,
302 catchLabel.callerMember.getName(),
303 catchLabel.callerMember.getSignature(),
304 catchLabel.callerMember.getModifiers(),
305 exceptionTypeName,
306 "",
307 exceptionTypeDesc,
308 0,
309 joinPointHash,
310 joinPointClassName,
311 m_lastLabelForLineNumber
312 )
313 );
314 }
315 }
316
317 /***
318 * Filters out the catch clauses that are not eligible for transformation.
319 *
320 * @param definitions
321 * @param ctx
322 * @return boolean true if the catch clause should be filtered out
323 */
324 static boolean handlerFilter(final Set definitions, final ExpressionContext ctx) {
325 for (Iterator it = definitions.iterator(); it.hasNext();) {
326 if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
327 return false;
328 } else {
329 continue;
330 }
331 }
332 return true;
333 }
334
335 /***
336 * A struct to represent a catch clause.
337 * The index is class wide, and the exception class info is kept.
338 *
339 * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
340 */
341 private static class CatchLabelStruct {
342 int labelIndexInWholeClass = -1;
343 ClassInfo exception = null;
344 ClassInfo caller = null;
345 MemberInfo callerMember = null;
346
347 private CatchLabelStruct(int indexInClass, ClassInfo exception, ClassInfo caller, MemberInfo callerMember) {
348 labelIndexInWholeClass = indexInClass;
349 this.exception = exception;
350 this.caller = caller;
351 this.callerMember = callerMember;
352 }
353 }
354 }