View Javadoc

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.codehaus.aspectwerkz.definition.SystemDefinition;
17  import org.codehaus.aspectwerkz.expression.ExpressionContext;
18  import org.codehaus.aspectwerkz.expression.PointcutType;
19  import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
20  import org.codehaus.aspectwerkz.reflect.ClassInfo;
21  import org.codehaus.aspectwerkz.reflect.MethodInfo;
22  import org.codehaus.aspectwerkz.reflect.MemberInfo;
23  import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
24  import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
25  import org.codehaus.aspectwerkz.transform.Context;
26  import org.codehaus.aspectwerkz.transform.TransformationUtil;
27  import org.codehaus.aspectwerkz.transform.TransformationConstants;
28  import org.codehaus.aspectwerkz.transform.inlining.compiler.AbstractJoinPointCompiler;
29  import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
30  import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
31  import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
32  
33  import java.lang.reflect.Modifier;
34  import java.util.Iterator;
35  import java.util.Set;
36  
37  /***
38   * Instruments method 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 depends if the
41   * target method is static or not as follow:
42   * <pre>
43   *      invoke(callee, args.., caller) // non static
44   *      invoke(args.., caller) // static
45   * </pre>
46   * (The reason why is that it simplifies call pointcut stack management)
47   *
48   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
49   * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
50   */
51  public class MethodCallVisitor extends ClassAdapter implements TransformationConstants {
52  
53      private final ContextImpl m_ctx;
54      private final ClassLoader m_loader;
55      private final ClassInfo m_callerClassInfo;
56  
57      private Label m_lastLabelForLineNumber = EmittedJoinPoint.NO_LINE_NUMBER;
58  
59      /***
60       * Creates a new instance.
61       *
62       * @param cv
63       * @param loader
64       * @param classInfo
65       * @param ctx
66       */
67      public MethodCallVisitor(final ClassVisitor cv,
68                               final ClassLoader loader,
69                               final ClassInfo classInfo,
70                               final Context ctx) {
71          super(cv);
72          m_loader = loader;
73          m_callerClassInfo = classInfo;
74          m_ctx = (ContextImpl) ctx;
75      }
76  
77      /***
78       * Visits the caller methods.
79       *
80       * @param access
81       * @param name
82       * @param desc
83       * @param exceptions
84       * @param attrs
85       * @return
86       */
87      public CodeVisitor visitMethod(final int access,
88                                     final String name,
89                                     final String desc,
90                                     final String[] exceptions,
91                                     final Attribute attrs) {
92  
93          if (name.startsWith(WRAPPER_METHOD_PREFIX) ||
94              Modifier.isNative(access) ||
95              Modifier.isAbstract(access)) {
96              return super.visitMethod(access, name, desc, exceptions, attrs);
97          }
98  
99          CodeVisitor mv = cv.visitMethod(access, name, desc, exceptions, attrs);
100         return mv == null ? null : new ReplaceInvokeInstructionCodeAdapter(
101                 mv,
102                 m_loader,
103                 m_callerClassInfo,
104                 m_ctx.getClassName(),
105                 name,
106                 desc
107         );
108     }
109 
110     /***
111      * Replaces 'INVOKEXXX' instructions with a call to the compiled JoinPoint instance.
112      *
113      * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
114      * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
115      */
116     public class ReplaceInvokeInstructionCodeAdapter extends AfterObjectInitializationCodeAdapter {
117 
118         private final ClassLoader m_loader;
119         private final ClassInfo m_callerClassInfo;
120         private final String m_callerClassName;
121         private final String m_callerMethodName;
122         private final String m_callerMethodDesc;
123         private final MemberInfo m_callerMemberInfo;
124 
125         /***
126          * Creates a new instance.
127          *
128          * @param ca
129          * @param loader
130          * @param callerClassInfo
131          * @param callerClassName
132          * @param callerMethodName
133          * @param callerMethodDesc
134          */
135         public ReplaceInvokeInstructionCodeAdapter(final CodeVisitor ca,
136                                                    final ClassLoader loader,
137                                                    final ClassInfo callerClassInfo,
138                                                    final String callerClassName,
139                                                    final String callerMethodName,
140                                                    final String callerMethodDesc) {
141             super(ca, callerMethodName);
142             m_loader = loader;
143             m_callerClassInfo = callerClassInfo;
144             m_callerClassName = callerClassName;
145             m_callerMethodName = callerMethodName;
146             m_callerMethodDesc = callerMethodDesc;
147 
148             if (CLINIT_METHOD_NAME.equals(callerMethodName)) {
149                 m_callerMemberInfo = m_callerClassInfo.staticInitializer();
150             } else if (INIT_METHOD_NAME.equals(callerMethodName)) {
151                 int hash = AsmHelper.calculateConstructorHash(m_callerMethodDesc);
152                 m_callerMemberInfo = m_callerClassInfo.getConstructor(hash);
153             } else {
154                 int hash = AsmHelper.calculateMethodHash(m_callerMethodName, m_callerMethodDesc);
155                 m_callerMemberInfo = m_callerClassInfo.getMethod(hash);
156             }
157         }
158 
159         /***
160          * Label
161          *
162          * @param label
163          */
164         public void visitLabel(Label label) {
165             m_lastLabelForLineNumber = label;
166             super.visitLabel(label);
167         }
168 
169         /***
170          * Visits 'INVOKEXXX' instructions.
171          *
172          * @param opcode
173          * @param calleeClassName
174          * @param calleeMethodName
175          * @param calleeMethodDesc
176          */
177         public void visitMethodInsn(final int opcode,
178                                     String calleeClassName,
179                                     final String calleeMethodName,
180                                     final String calleeMethodDesc) {
181 
182             if (m_callerMemberInfo == null) {
183                 System.err.println(
184                         "AW::WARNING " +
185                         "metadata structure could not be build for method ["
186                         + m_callerClassInfo.getName().replace('/', '.')
187                         + '.' + m_callerMethodName + ':' + m_callerMethodDesc + ']'
188                 );
189                 super.visitMethodInsn(opcode, calleeClassName, calleeMethodName, calleeMethodDesc);
190                 return;
191             }
192 
193             if (INIT_METHOD_NAME.equals(calleeMethodName) ||
194                 CLINIT_METHOD_NAME.equals(calleeMethodName) ||
195                 calleeMethodName.startsWith(ASPECTWERKZ_PREFIX)
196                 || calleeClassName.endsWith(JOIN_POINT_CLASS_SUFFIX)
197                 //calleeClassName.startsWith("org/aopalliance/")) { // FIXME make generic fix by invoking all AspectModels (same problem in other visitors as well)
198                     ){super.visitMethodInsn(opcode, calleeClassName, calleeMethodName, calleeMethodDesc);
199                 return;
200             }
201 
202             // check if we have a super.sameMethod() call
203             if (opcode == INVOKESPECIAL
204                 && !calleeClassName.equals(m_callerClassName)
205                 && ClassInfoHelper.extendsSuperClass(m_callerClassInfo, calleeClassName.replace('/', '.'))) {
206                 super.visitMethodInsn(opcode, calleeClassName, calleeMethodName, calleeMethodDesc);
207                 return;
208             }
209 
210             // check if object initialization has been reached
211             if (!m_isObjectInitialized) {
212                 super.visitMethodInsn(opcode, calleeClassName, calleeMethodName, calleeMethodDesc);
213                 return;
214             }
215 
216             int joinPointHash = AsmHelper.calculateMethodHash(calleeMethodName, calleeMethodDesc);
217 
218             ClassInfo classInfo = AsmClassInfo.getClassInfo(calleeClassName, m_loader);
219             MethodInfo calleeMethodInfo = classInfo.getMethod(joinPointHash);
220 
221             if (calleeMethodInfo == null) {
222                 System.err.println(
223                         "AW::WARNING " +
224                         "metadata structure could not be build for method ["
225                         + classInfo.getName().replace('/', '.')
226                         + '.' + calleeMethodName + ':' + calleeMethodDesc
227                         + "] when parsing method ["
228                         + m_callerClassInfo.getName() + '.' + m_callerMethodName + "(..)]"
229                 );
230                 // bail out
231                 super.visitMethodInsn(opcode, calleeClassName, calleeMethodName, calleeMethodDesc);
232                 return;
233             }
234 
235             ExpressionContext ctx = new ExpressionContext(PointcutType.CALL, calleeMethodInfo, m_callerMemberInfo);
236 
237             if (methodFilter(m_ctx.getDefinitions(), ctx, calleeMethodInfo)) {
238                 super.visitMethodInsn(opcode, calleeClassName, calleeMethodName, calleeMethodDesc);
239             } else {
240                 m_ctx.markAsAdvised();
241 
242                 String joinPointClassName = TransformationUtil.getJoinPointClassName(
243                         m_callerClassName,
244                         m_callerMethodName,
245                         m_callerMethodDesc,
246                         calleeClassName,
247                         JoinPointType.METHOD_CALL_INT,
248                         joinPointHash
249                 );
250 
251                 // load the caller instance (this), or null if in a static context
252                 // note that callee instance [optional] and args are already on the stack
253                 if (Modifier.isStatic(m_callerMemberInfo.getModifiers())) {
254                     visitInsn(ACONST_NULL);
255                 } else {
256                     visitVarInsn(ALOAD, 0);
257                 }
258 
259                 // add the call to the join point
260                 super.visitMethodInsn(
261                         INVOKESTATIC,
262                         joinPointClassName,
263                         INVOKE_METHOD_NAME,
264                         TransformationUtil.getInvokeSignatureForCodeJoinPoints(
265                                 calleeMethodInfo.getModifiers(), calleeMethodDesc,
266                                 m_callerClassName, calleeClassName
267                         )
268                 );
269 
270                 // emit the joinpoint
271                 //See AW-253 - we remember if we had an INVOKE INTERFACE opcode
272                 int modifiers = calleeMethodInfo.getModifiers();
273                 if (opcode == INVOKEINTERFACE) {
274                     modifiers = modifiers | MODIFIER_INVOKEINTERFACE;
275                 }
276                 m_ctx.addEmittedJoinPoint(
277                         new EmittedJoinPoint(
278                                 JoinPointType.METHOD_CALL_INT,
279                                 m_callerClassName,
280                                 m_callerMethodName,
281                                 m_callerMethodDesc,
282                                 m_callerMemberInfo.getModifiers(),
283                                 calleeClassName,
284                                 calleeMethodName,
285                                 calleeMethodDesc,
286                                 modifiers,
287                                 joinPointHash,
288                                 joinPointClassName,
289                                 m_lastLabelForLineNumber
290                         )
291                 );
292             }
293         }
294 
295         /***
296          * Filters out the methods that are not eligible for transformation.
297          * Do not filter on abstract callee method - needed for interface declared method call
298          * (invokeinterface instr.)
299          *
300          * @param definitions
301          * @param ctx
302          * @param calleeMethodInfo
303          * @return boolean true if the method should be filtered out
304          */
305         public boolean methodFilter(final Set definitions,
306                                     final ExpressionContext ctx,
307                                     final MethodInfo calleeMethodInfo) {
308             if (calleeMethodInfo.getName().equals(INIT_METHOD_NAME) ||
309                 calleeMethodInfo.getName().equals(CLINIT_METHOD_NAME) ||
310                 calleeMethodInfo.getName().startsWith(ORIGINAL_METHOD_PREFIX)) {
311                 return true;
312             }
313             for (Iterator it = definitions.iterator(); it.hasNext();) {
314                 if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
315                     return false;
316                 } else {
317                     continue;
318                 }
319             }
320             return true;
321         }
322     }
323 }