KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > aspectwerkz > transform > inlining > weaver > ConstructorCallVisitor


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
3  */

4 package com.tc.aspectwerkz.transform.inlining.weaver;
5
6
7 import java.lang.reflect.Modifier JavaDoc;
8 import java.util.Collections JavaDoc;
9 import java.util.HashMap JavaDoc;
10 import java.util.Iterator JavaDoc;
11 import java.util.Map JavaDoc;
12 import java.util.Set JavaDoc;
13 import java.util.Stack JavaDoc;
14
15 import com.tc.asm.ClassAdapter;
16 import com.tc.asm.ClassVisitor;
17 import com.tc.asm.MethodAdapter;
18 import com.tc.asm.MethodVisitor;
19 import com.tc.asm.Label;
20
21 import com.tc.aspectwerkz.definition.SystemDefinition;
22 import com.tc.aspectwerkz.joinpoint.management.JoinPointType;
23 import com.tc.aspectwerkz.reflect.impl.asm.AsmClassInfo;
24 import com.tc.aspectwerkz.reflect.ClassInfo;
25 import com.tc.aspectwerkz.reflect.MemberInfo;
26 import com.tc.aspectwerkz.reflect.ConstructorInfo;
27 import com.tc.aspectwerkz.transform.InstrumentationContext;
28 import com.tc.aspectwerkz.transform.TransformationConstants;
29 import com.tc.aspectwerkz.transform.TransformationUtil;
30 import com.tc.aspectwerkz.transform.inlining.AsmHelper;
31 import com.tc.aspectwerkz.transform.inlining.AsmNullAdapter;
32 import com.tc.aspectwerkz.transform.inlining.EmittedJoinPoint;
33 import com.tc.aspectwerkz.expression.ExpressionContext;
34 import com.tc.aspectwerkz.expression.PointcutType;
35
36
37 /**
38  * Instruments ctor CALL join points by replacing INVOKEXXX instructions with invocations of the compiled join point.
39  * <br/>
40  * It calls the JPClass.invoke static method. The signature of the invoke method is:
41  * <pre>
42  * invoke(args.., caller) - note: no callee as arg0
43  * </pre>
44  * (The reason why is that it simplifies call pointcut stack management)
45  * <p/>
46  * <p/>
47  * Note: The Eclipse compiler is generating "catch(exception) NEW DUP_X1 SWAP getMessage newError(..)"
48  * hence NEW DUP_X1 is a valid sequence as well, and DUP_X1 is replaced by DUP to preserved the SWAP.
49  * Other more complex schemes (DUP_X2) are not implemented (no real test so far)
50  *
51  * @author <a HREF="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
52  */

53 public class ConstructorCallVisitor extends ClassAdapter implements TransformationConstants {
54
55   private final static Map JavaDoc EMPTY_INTHASHMAP = Collections.EMPTY_MAP;
56
57   private final InstrumentationContext m_ctx;
58   private final ClassLoader JavaDoc m_loader;
59   private final ClassInfo m_callerClassInfo;
60
61   /**
62    * Map of NEW instructions.
63    * The key is the method (withincode) hash
64    * The value is a TLongObjectHashMap whose key is index of NEW instructions and value instance of NewInvocationStruct
65    */

66   private final Map JavaDoc m_newInvocationsByCallerMemberHash;
67
68   private Label m_lastLabelForLineNumber = EmittedJoinPoint.NO_LINE_NUMBER;
69
70   /**
71    * Creates a new instance.
72    *
73    * @param cv
74    * @param loader
75    * @param classInfo
76    * @param ctx
77    */

78   public ConstructorCallVisitor(final ClassVisitor cv,
79                                 final ClassLoader JavaDoc loader,
80                                 final ClassInfo classInfo,
81                                 final InstrumentationContext ctx,
82                                 final Map JavaDoc newInvocationsByCallerMemberHash) {
83     super(cv);
84     m_loader = loader;
85     m_callerClassInfo = classInfo;
86     m_ctx = ctx;
87     m_newInvocationsByCallerMemberHash = newInvocationsByCallerMemberHash;
88   }
89
90   /**
91    * Visits the caller methods.
92    *
93    * @param access
94    * @param name
95    * @param desc
96    * @param signature
97    * @param exceptions
98    * @return
99    */

100   public MethodVisitor visitMethod(final int access,
101                                    final String JavaDoc name,
102                                    final String JavaDoc desc,
103                                    final String JavaDoc signature,
104                                    final String JavaDoc[] exceptions) {
105
106     if (name.startsWith(WRAPPER_METHOD_PREFIX) ||
107             Modifier.isNative(access) ||
108             Modifier.isAbstract(access)) {
109       return super.visitMethod(access, name, desc, signature, exceptions);
110     }
111
112     MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
113     return mv == null ? null : new ReplaceNewInstructionCodeAdapter(
114             mv,
115             m_loader,
116             m_callerClassInfo,
117             m_ctx.getClassName(),
118             name,
119             desc,
120             (Map JavaDoc) m_newInvocationsByCallerMemberHash.get(getMemberHash(name, desc))
121     );
122   }
123
124
125   /**
126    * Replaces 'new' instructions with a call to the compiled JoinPoint instance.
127    * <br/>
128    * It does the following:
129    * - remove NEW <class> when we know (from first visit) that it matchs
130    * - remove DUP that follows NEW <class>
131    * - replace INVOKESPECIAL <ctor signature> with call to JP
132    *
133    * @author <a HREF="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
134    */

135   public class ReplaceNewInstructionCodeAdapter extends MethodAdapter {
136
137     private final ClassLoader JavaDoc m_loader;
138     private final ClassInfo m_callerClassInfo;
139     private final String JavaDoc m_callerClassName;
140     private final String JavaDoc m_callerMethodName;
141     private final String JavaDoc m_callerMethodDesc;
142     private final MemberInfo m_callerMemberInfo;
143
144     /**
145      * Map of NewInvocationStruct indexed by NEW indexes (incremented thru the visit) for the visited member code body
146      */

147     private final Map JavaDoc m_newInvocations;
148
149     /**
150      * Index of NEW instr. in the scope of the visited member code body
151      */

152     private int m_newInvocationIndex = -1;
153
154     /**
155      * Stack of NewInovationStruct, which mirrors the corresponding INVOKESPECIAL <init> when a NEW has been visited.
156      * If the entry is NULL, it means that this ctor call does not match.
157      * This allow to compute the match only once when the NEW is visited (since we have data from the first visit)
158      * while supporting nested interception like new Foo(new Bar("s"))
159      */

160     private final Stack JavaDoc m_newInvocationStructStack = new Stack JavaDoc();
161
162     /**
163      * Flag set to true just after a NEW that match has been visited
164      */

165     private boolean m_skipNextDup = false;
166
167     /**
168      * Creates a new instance.
169      *
170      * @param ca
171      * @param loader
172      * @param callerClassInfo
173      * @param callerClassName
174      * @param callerMethodName
175      * @param callerMethodDesc
176      */

177     public ReplaceNewInstructionCodeAdapter(final MethodVisitor ca,
178                                             final ClassLoader JavaDoc loader,
179                                             final ClassInfo callerClassInfo,
180                                             final String JavaDoc callerClassName,
181                                             final String JavaDoc callerMethodName,
182                                             final String JavaDoc callerMethodDesc,
183                                             final Map JavaDoc newInvocations) {
184       super(ca);
185       m_loader = loader;
186       m_callerClassInfo = callerClassInfo;
187       m_callerClassName = callerClassName;
188       m_callerMethodName = callerMethodName;
189       m_callerMethodDesc = callerMethodDesc;
190       m_newInvocations = (newInvocations != null) ? newInvocations : EMPTY_INTHASHMAP;
191
192       if (CLINIT_METHOD_NAME.equals(m_callerMethodName)) {
193         m_callerMemberInfo = m_callerClassInfo.staticInitializer();
194       } else if (INIT_METHOD_NAME.equals(m_callerMethodName)) {
195         final int hash = AsmHelper.calculateConstructorHash(m_callerMethodDesc);
196         m_callerMemberInfo = m_callerClassInfo.getConstructor(hash);
197       } else {
198         final int hash = AsmHelper.calculateMethodHash(m_callerMethodName, m_callerMethodDesc);
199         m_callerMemberInfo = m_callerClassInfo.getMethod(hash);
200       }
201       if (m_callerMemberInfo == null) {
202         System.err.println(
203                 "AW::WARNING " +
204                         "metadata structure could not be build for method ["
205                         + m_callerClassInfo.getName().replace('/', '.')
206                         + '.' + m_callerMethodName + ':' + m_callerMethodDesc + ']'
207         );
208       }
209     }
210
211     /**
212      * Label
213      *
214      * @param label
215      */

216     public void visitLabel(Label label) {
217       m_lastLabelForLineNumber = label;
218       super.visitLabel(label);
219     }
220
221     /**
222      * Removes the NEW when we know that the corresponding INVOKE SPECIAL <init> is advised.
223      *
224      * @param opcode
225      * @param desc
226      */

227     public void visitTypeInsn(int opcode, String JavaDoc desc) {
228       if (m_callerMemberInfo == null) {
229         return;
230       }
231
232       if (opcode == NEW) {
233         m_newInvocationIndex++;
234         // build the callee ConstructorInfo and check for a match
235
NewInvocationStruct newInvocationStruct = (NewInvocationStruct) m_newInvocations.get(new Integer JavaDoc(m_newInvocationIndex));
236         if (newInvocationStruct == null) {
237           super.visitTypeInsn(opcode, desc);//we failed
238
return;
239         }
240         String JavaDoc calleeClassName = newInvocationStruct.className;
241         String JavaDoc calleeMethodName = INIT_METHOD_NAME;
242         String JavaDoc calleeMethodDesc = newInvocationStruct.ctorDesc;
243         int joinPointHash = AsmHelper.calculateMethodHash(calleeMethodName, calleeMethodDesc);
244         ClassInfo classInfo = AsmClassInfo.getClassInfo(calleeClassName, m_loader);
245         ConstructorInfo calleeConstructorInfo = classInfo.getConstructor(joinPointHash);
246         if (calleeConstructorInfo == null) {
247           super.visitTypeInsn(opcode, desc);//we failed
248
System.err.println(
249                   "AW::WARNING " +
250                           "metadata structure could not be build for method ["
251                           + classInfo.getName().replace('/', '.')
252                           + '.' + calleeMethodName + ':' + calleeMethodDesc + ']'
253           );
254           return;
255         }
256
257         // do we have a match - if so, skip the NEW and the DUP
258
ExpressionContext ctx = new ExpressionContext(
259                 PointcutType.CALL, calleeConstructorInfo, m_callerMemberInfo
260         );
261         if (constructorFilter(m_ctx.getDefinitions(), ctx, calleeConstructorInfo)) {
262           // push NULL as a struct (means no match)
263
m_newInvocationStructStack.push(null);
264           super.visitTypeInsn(opcode, desc);
265         } else {
266           // keep track of the ConstructorInfo so that we don't compute it again in visitMethodInsn <init>
267
newInvocationStruct.constructorInfo = calleeConstructorInfo;
268           newInvocationStruct.joinPointHash = joinPointHash;
269           m_newInvocationStructStack.push(newInvocationStruct);
270           // skip NEW instr and flag to skip next DUP
271
m_skipNextDup = true;
272           //System.out.println("RECORD " + calleeClassName + calleeMethodDesc);
273
}
274       } else {
275         // is not a NEW instr
276
super.visitTypeInsn(opcode, desc);
277       }
278     }
279
280     /**
281      * Remove the DUP instruction if we know that those were for a NEW ... INVOKESPECIAL that match.
282      *
283      * @param opcode
284      */

285     public void visitInsn(int opcode) {
286       if ((opcode == DUP || opcode == DUP_X1) && m_skipNextDup) {
287         //System.out.println("SKIP dup");
288
// skip the DUP
289
if (opcode == DUP_X1)
290           super.visitInsn(DUP);
291       } else {
292         super.visitInsn(opcode);
293       }
294       m_skipNextDup = false;
295     }
296
297     /**
298      * Visits INVOKESPECIAL <init> instructions and replace them with a call to the join point when matched.
299      *
300      * @param opcode
301      * @param calleeClassName
302      * @param calleeConstructorName
303      * @param calleeConstructorDesc
304      */

305     public void visitMethodInsn(final int opcode,
306                                 final String JavaDoc calleeClassName,
307                                 final String JavaDoc calleeConstructorName,
308                                 final String JavaDoc calleeConstructorDesc) {
309
310       if (m_callerMemberInfo == null) {
311         super.visitMethodInsn(opcode, calleeClassName, calleeConstructorName, calleeConstructorDesc);
312         return;
313       }
314
315       if (!INIT_METHOD_NAME.equals(calleeConstructorName) ||
316               calleeClassName.endsWith(TransformationConstants.JOIN_POINT_CLASS_SUFFIX)) {
317         super.visitMethodInsn(opcode, calleeClassName, calleeConstructorName, calleeConstructorDesc);
318         return;
319       }
320
321       // getDefault the info from the invocation stack since all the matching has already been done
322
if (m_newInvocationStructStack.isEmpty()) {
323         // nothing to weave
324
super.visitMethodInsn(opcode, calleeClassName, calleeConstructorName, calleeConstructorDesc);
325         return;
326       }
327
328       NewInvocationStruct struct = (NewInvocationStruct) m_newInvocationStructStack.pop();
329       if (struct == null) {
330         // not matched
331
super.visitMethodInsn(opcode, calleeClassName, calleeConstructorName, calleeConstructorDesc);
332       } else {
333         m_ctx.markAsAdvised();
334
335         String JavaDoc joinPointClassName = TransformationUtil.getJoinPointClassName(
336                 m_callerClassName,
337                 m_callerMethodName,
338                 m_callerMethodDesc,
339                 calleeClassName,
340                 JoinPointType.CONSTRUCTOR_CALL_INT,
341                 struct.joinPointHash
342         );
343
344         // load the caller instance (this), or null if in a static context
345
// note that callee instance [mandatory since ctor] and args are already on the stack
346
if (Modifier.isStatic(m_callerMemberInfo.getModifiers())) {
347           visitInsn(ACONST_NULL);
348         } else {
349           visitVarInsn(ALOAD, 0);
350         }
351
352         // add the call to the join point
353
super.visitMethodInsn(
354                 INVOKESTATIC,
355                 joinPointClassName,
356                 INVOKE_METHOD_NAME,
357                 TransformationUtil.getInvokeSignatureForConstructorCallJoinPoints(
358                         calleeConstructorDesc,
359                         m_callerClassName,
360                         calleeClassName
361                 )
362         );
363
364         // emit the joinpoint
365
m_ctx.addEmittedJoinPoint(
366                 new EmittedJoinPoint(
367                         JoinPointType.CONSTRUCTOR_CALL_INT,
368                         m_callerClassName,
369                         m_callerMethodName,
370                         m_callerMethodDesc,
371                         m_callerMemberInfo.getModifiers(),
372                         calleeClassName,
373                         calleeConstructorName,
374                         calleeConstructorDesc,
375                         struct.constructorInfo.getModifiers(),
376                         struct.joinPointHash,
377                         joinPointClassName,
378                         m_lastLabelForLineNumber
379                 )
380         );
381       }
382     }
383
384     /**
385      * Filters out the ctor that are not eligible for transformation.
386      *
387      * @param definitions
388      * @param ctx
389      * @param calleeConstructorInfo
390      * @return boolean true if the method should be filtered out
391      */

392     public boolean constructorFilter(final Set JavaDoc definitions,
393                                      final ExpressionContext ctx,
394                                      final ConstructorInfo calleeConstructorInfo) {
395       for (Iterator JavaDoc it = definitions.iterator(); it.hasNext();) {
396         if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
397           return false;
398         } else {
399           continue;
400         }
401       }
402       return true;
403     }
404   }
405
406   private static Integer JavaDoc getMemberHash(String JavaDoc name, String JavaDoc desc) {
407     int hash = 29;
408     hash = (29 * hash) + name.hashCode();
409     hash = (29 * hash) + desc.hashCode();
410     return new Integer JavaDoc(hash);
411   }
412
413   /**
414    * Lookahead index of NEW instruction for NEW + DUP + INVOKESPECIAL instructions
415    * Remember the NEW instruction index
416    * <p/>
417    * Special case when withincode ctor of called ctor:
418    * <pre>public Foo() { super(new Foo()); }</pre>
419    * In such a case, it is not possible to intercept the call to new Foo() since this cannot be
420    * referenced as long as this(..) or super(..) has not been called.
421    *
422    * @author <a HREF="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
423    */

424   public static class LookaheadNewDupInvokeSpecialInstructionClassAdapter
425           extends AsmNullAdapter.NullClassAdapter {
426
427     private String JavaDoc m_callerMemberName;
428
429     // list of new invocations by caller member hash
430
public Map JavaDoc m_newInvocationsByCallerMemberHash;
431
432     public LookaheadNewDupInvokeSpecialInstructionClassAdapter(Map JavaDoc newInvocations) {
433       m_newInvocationsByCallerMemberHash = newInvocations;
434     }
435
436     public MethodVisitor visitMethod(final int access,
437                                      final String JavaDoc name,
438                                      final String JavaDoc desc,
439                                      final String JavaDoc signature,
440                                      final String JavaDoc[] exceptions) {
441       if (name.startsWith(WRAPPER_METHOD_PREFIX) ||
442               Modifier.isNative(access) ||
443               Modifier.isAbstract(access)) {
444         //ignore
445
}
446
447       m_callerMemberName = name;
448
449       Map JavaDoc newInvocations = new HashMap JavaDoc(5);
450       m_newInvocationsByCallerMemberHash.put(getMemberHash(name, desc), newInvocations);
451       return new LookaheadNewDupInvokeSpecialInstructionCodeAdapter(
452               super.visitMethod(access, name, desc, signature, exceptions),
453               newInvocations,
454               m_callerMemberName
455       );
456     }
457   }
458
459   public static class LookaheadNewDupInvokeSpecialInstructionCodeAdapter
460           extends AfterObjectInitializationCodeAdapter {
461
462     private Map JavaDoc m_newInvocations;
463
464     private Stack JavaDoc m_newIndexStack = new Stack JavaDoc();
465     private int m_newIndex = -1;
466
467     /**
468      * Creates a new instance.
469      */

470     public LookaheadNewDupInvokeSpecialInstructionCodeAdapter(MethodVisitor cv, Map JavaDoc newInvocations,
471                                                               final String JavaDoc callerMemberName) {
472       super(cv, callerMemberName);
473       m_newInvocations = newInvocations;
474     }
475
476     public void visitTypeInsn(int opcode, String JavaDoc desc) {
477       // make sure to call super first to compute post object initialization flag
478
super.visitTypeInsn(opcode, desc);
479       if (opcode == NEW) {
480         m_newIndex++;
481         m_newIndexStack.push(new Integer JavaDoc(m_newIndex));
482       }
483     }
484
485     public void visitMethodInsn(final int opcode,
486                                 final String JavaDoc calleeClassName,
487                                 final String JavaDoc calleeMethodName,
488                                 final String JavaDoc calleeMethodDesc) {
489       // make sure to call super first to compute post object initialization flag
490
super.visitMethodInsn(opcode, calleeClassName, calleeMethodName, calleeMethodDesc);
491
492       if (INIT_METHOD_NAME.equals(calleeMethodName) && opcode == INVOKESPECIAL) {
493         if (!m_isObjectInitialized) {
494           // skip - remove the NEW index from the stack
495
if (!m_newIndexStack.isEmpty()) {
496             m_newIndexStack.pop();
497           }
498         } else {
499           if (!m_newIndexStack.isEmpty()) {
500             Object JavaDoc index = m_newIndexStack.pop();
501             NewInvocationStruct newInvocationStruct = new NewInvocationStruct();
502             newInvocationStruct.className = calleeClassName;
503             newInvocationStruct.ctorDesc = calleeMethodDesc;
504             // constructorInfo and matching will be done at weave time and not at lookahead time
505
m_newInvocations.put(index, newInvocationStruct);
506           }
507         }
508       }
509     }
510   }
511
512   private static class NewInvocationStruct {
513     public String JavaDoc className;
514     public String JavaDoc ctorDesc;
515     public ConstructorInfo constructorInfo = null;
516     public int joinPointHash = -1;
517   }
518
519 }
Popular Tags