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