001: /**************************************************************************************
002: * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
003: * http://aspectwerkz.codehaus.org *
004: * ---------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of the LGPL license *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: **************************************************************************************/package org.codehaus.aspectwerkz.transform.inlining.weaver;
008:
009: import org.objectweb.asm.ClassAdapter;
010: import org.objectweb.asm.ClassVisitor;
011: import org.objectweb.asm.CodeVisitor;
012: import org.objectweb.asm.Attribute;
013: import org.objectweb.asm.CodeAdapter;
014: import org.objectweb.asm.Label;
015: import org.objectweb.asm.Type;
016: import org.codehaus.aspectwerkz.definition.SystemDefinition;
017: import org.codehaus.aspectwerkz.expression.ExpressionContext;
018: import org.codehaus.aspectwerkz.expression.PointcutType;
019: import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
020: import org.codehaus.aspectwerkz.reflect.ClassInfo;
021: import org.codehaus.aspectwerkz.reflect.MemberInfo;
022: import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
023: import org.codehaus.aspectwerkz.transform.Context;
024: import org.codehaus.aspectwerkz.transform.TransformationUtil;
025: import org.codehaus.aspectwerkz.transform.TransformationConstants;
026: import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
027: import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
028: import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
029:
030: import java.util.Iterator;
031: import java.util.Set;
032: import java.util.ArrayList;
033: import java.util.List;
034: import java.util.Map;
035: import java.util.HashMap;
036: import java.lang.reflect.Modifier;
037:
038: /**
039: * Advises catch clauses by inserting a call to the join point as the first thing in the catch block.
040: *
041: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
042: */
043: public class HandlerVisitor extends ClassAdapter implements
044: TransformationConstants {
045:
046: /**
047: * A visitor that looks for all catch clause and keep track of them
048: * providing that they match
049: *
050: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
051: */
052: public static class LookaheadCatchLabelsClassAdapter extends
053: ClassAdapter {
054: /**
055: * list of CatchLabelStruct that matches
056: */
057: List m_catchLabels = new ArrayList();
058:
059: /**
060: * map of Integer(index in whole class)-->asm.Label for all the visited labels
061: */
062: private final Map m_labelIndexes = new HashMap();
063:
064: /**
065: * current label index in whole class, from 0 to N
066: */
067: private int m_labelIndex = -1;
068:
069: private final ContextImpl m_ctx;
070: private final ClassLoader m_loader;
071: private final ClassInfo m_callerClassInfo;
072:
073: /**
074: * Visit the class
075: *
076: * @param cv
077: * @param loader
078: * @param callerClassInfo
079: * @param ctx
080: * @param catchLabels
081: */
082: public LookaheadCatchLabelsClassAdapter(ClassVisitor cv,
083: ClassLoader loader, ClassInfo callerClassInfo,
084: Context ctx, List catchLabels) {
085: super (cv);
086: m_catchLabels = catchLabels;
087: m_loader = loader;
088: m_callerClassInfo = callerClassInfo;
089: m_ctx = (ContextImpl) ctx;
090: }
091:
092: /**
093: * Visit method bodies
094: *
095: * @param access
096: * @param callerMethodName
097: * @param callerMethodDesc
098: * @param exceptions
099: * @param attrs
100: * @return
101: */
102: public CodeVisitor visitMethod(final int access,
103: final String callerMethodName,
104: final String callerMethodDesc,
105: final String[] exceptions, final Attribute attrs) {
106: if (callerMethodName.startsWith(WRAPPER_METHOD_PREFIX)) {
107: return super .visitMethod(access, callerMethodName,
108: callerMethodDesc, exceptions, attrs);
109: }
110:
111: CodeVisitor mv = cv.visitMethod(access, callerMethodName,
112: callerMethodDesc, exceptions, attrs);
113: if (mv == null) {
114: return mv;
115: }
116:
117: final MemberInfo callerMemberInfo;
118: if (CLINIT_METHOD_NAME.equals(callerMethodName)) {
119: callerMemberInfo = m_callerClassInfo
120: .staticInitializer();
121: } else if (INIT_METHOD_NAME.equals(callerMethodName)) {
122: int hash = AsmHelper
123: .calculateConstructorHash(callerMethodDesc);
124: callerMemberInfo = m_callerClassInfo
125: .getConstructor(hash);
126: } else {
127: int hash = AsmHelper.calculateMethodHash(
128: callerMethodName, callerMethodDesc);
129: callerMemberInfo = m_callerClassInfo.getMethod(hash);
130: }
131: if (callerMemberInfo == null) {
132: System.err
133: .println("AW::WARNING "
134: + "metadata structure could not be build for method ["
135: + m_callerClassInfo.getName().replace(
136: '/', '.') + '.'
137: + callerMethodName + ':'
138: + callerMethodDesc + ']');
139: return mv;
140: }
141:
142: /**
143: * Visit the method, and keep track of all labels so that when visittryCatch is reached
144: * we can remember the index
145: *
146: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
147: */
148: return new CodeAdapter(mv) {
149: public void visitLabel(Label label) {
150: m_labelIndexes.put(label, new Integer(
151: ++m_labelIndex));
152: super .visitLabel(label);
153: }
154:
155: public void visitTryCatchBlock(Label startLabel,
156: Label endLabel, Label handlerLabel,
157: String exceptionTypeName) {
158: if (exceptionTypeName == null) {
159: // finally block
160: super .visitTryCatchBlock(startLabel, endLabel,
161: handlerLabel, exceptionTypeName);
162: return;
163: }
164: final ClassInfo exceptionClassInfo = AsmClassInfo
165: .getClassInfo(exceptionTypeName, m_loader);
166: final ExpressionContext ctx = new ExpressionContext(
167: PointcutType.HANDLER, exceptionClassInfo,
168: callerMemberInfo);
169: if (!handlerFilter(m_ctx.getDefinitions(), ctx)) {
170: // remember its index and the exception exceptionClassInfo
171: Integer index = (Integer) m_labelIndexes
172: .get(handlerLabel);
173: if (index != null) {
174: m_catchLabels
175: .add(new CatchLabelStruct(index
176: .intValue(),
177: exceptionClassInfo,
178: m_callerClassInfo,
179: callerMemberInfo));
180: }
181: }
182: super .visitTryCatchBlock(startLabel, endLabel,
183: handlerLabel, exceptionTypeName);
184: }
185: };
186: }
187: }
188:
189: //---- non lookahead visitor
190:
191: private final ContextImpl m_ctx;
192:
193: /**
194: * List of matching catch clause
195: */
196: private final List m_catchLabels;
197:
198: /**
199: * catch clause index in whole class
200: */
201: private int m_labelIndex = -1;
202:
203: private Label m_lastLabelForLineNumber = EmittedJoinPoint.NO_LINE_NUMBER;
204:
205: /**
206: * Creates a new instance.
207: *
208: * @param cv
209: * @param ctx
210: */
211: public HandlerVisitor(final ClassVisitor cv, final Context ctx,
212: final List catchLabels) {
213: super (cv);
214: m_ctx = (ContextImpl) ctx;
215: m_catchLabels = catchLabels;
216: }
217:
218: /**
219: * Visits the methods bodies to weave in JP calls at catch clauses
220: *
221: * @param access
222: * @param name
223: * @param desc
224: * @param exceptions
225: * @param attrs
226: * @return
227: */
228: public CodeVisitor visitMethod(final int access, final String name,
229: final String desc, final String[] exceptions,
230: final Attribute attrs) {
231: if (name.startsWith(WRAPPER_METHOD_PREFIX)) {
232: return super .visitMethod(access, name, desc, exceptions,
233: attrs);
234: }
235:
236: CodeVisitor mv = cv.visitMethod(access, name, desc, exceptions,
237: attrs);
238: return mv == null ? null : new CatchClauseCodeAdapter(mv);
239: }
240:
241: /**
242: * Advises catch clauses by inserting a call to the join point as the first thing in the catch block.
243: *
244: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
245: */
246: public class CatchClauseCodeAdapter extends CodeAdapter {
247:
248: /**
249: * Creates a new instance.
250: *
251: * @param ca
252: */
253: public CatchClauseCodeAdapter(final CodeVisitor ca) {
254: super (ca);
255: }
256:
257: public void visitLabel(Label label) {
258: m_lastLabelForLineNumber = label;
259: super .visitLabel(label);
260:
261: // check if it is a catch label
262: int index = ++m_labelIndex;
263: CatchLabelStruct catchLabel = null;
264: for (Iterator iterator = m_catchLabels.iterator(); iterator
265: .hasNext();) {
266: CatchLabelStruct aCatchLabel = (CatchLabelStruct) iterator
267: .next();
268: if (aCatchLabel.labelIndexInWholeClass == index) {
269: catchLabel = aCatchLabel;
270: break;
271: }
272: }
273: if (catchLabel == null) {
274: return;
275: }
276: // matched
277: m_ctx.markAsAdvised();
278: final String callerTypeName = catchLabel.caller.getName()
279: .replace('.', '/');
280: final String exceptionTypeDesc = catchLabel.exception
281: .getSignature();
282: final String exceptionTypeName = Type.getType(
283: exceptionTypeDesc).getInternalName();
284: final int joinPointHash = AsmHelper
285: .calculateClassHash(exceptionTypeDesc);
286: final String joinPointClassName = TransformationUtil
287: .getJoinPointClassName(callerTypeName,
288: catchLabel.callerMember.getName(),
289: catchLabel.callerMember.getSignature(),
290: exceptionTypeName,
291: JoinPointType.HANDLER_INT, joinPointHash);
292:
293: // add the call to the join point
294: // exception instance is on the stack
295: // dup it for ARG0
296: cv.visitInsn(DUP);
297:
298: // load caller instance if any
299: if (Modifier.isStatic(catchLabel.callerMember
300: .getModifiers())) {
301: cv.visitInsn(ACONST_NULL);
302: } else {
303: cv.visitVarInsn(ALOAD, 0);
304: }
305: //TODO for now we pass the exception as both CALLEE and ARG0 - may be callee must be NULL
306: //? check in AJ RTTI
307: cv.visitMethodInsn(INVOKESTATIC, joinPointClassName,
308: INVOKE_METHOD_NAME, TransformationUtil
309: .getInvokeSignatureForHandlerJoinPoints(
310: callerTypeName, exceptionTypeName));
311:
312: // emit the joinpoint
313: m_ctx.addEmittedJoinPoint(new EmittedJoinPoint(
314: JoinPointType.HANDLER_INT, callerTypeName,
315: catchLabel.callerMember.getName(),
316: catchLabel.callerMember.getSignature(),
317: catchLabel.callerMember.getModifiers(),
318: exceptionTypeName, "", exceptionTypeDesc,
319: 0, // a bit meaningless but must not be static
320: joinPointHash, joinPointClassName,
321: m_lastLabelForLineNumber));
322: }
323: }
324:
325: /**
326: * Filters out the catch clauses that are not eligible for transformation.
327: *
328: * @param definitions
329: * @param ctx
330: * @return boolean true if the catch clause should be filtered out
331: */
332: static boolean handlerFilter(final Set definitions,
333: final ExpressionContext ctx) {
334: for (Iterator it = definitions.iterator(); it.hasNext();) {
335: if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
336: return false;
337: } else {
338: continue;
339: }
340: }
341: return true;
342: }
343:
344: /**
345: * A struct to represent a catch clause.
346: * The index is class wide, and the exception class info is kept.
347: *
348: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
349: */
350: private static class CatchLabelStruct {
351: int labelIndexInWholeClass = -1;
352: ClassInfo exception = null;
353: ClassInfo caller = null;
354: MemberInfo callerMember = null;
355:
356: private CatchLabelStruct(int indexInClass, ClassInfo exception,
357: ClassInfo caller, MemberInfo callerMember) {
358: labelIndexInWholeClass = indexInClass;
359: this.exception = exception;
360: this.caller = caller;
361: this.callerMember = callerMember;
362: }
363: }
364: }
|