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 java.lang.reflect.Modifier;
11  import java.util.Iterator;
12  import java.util.Set;
13  
14  import org.objectweb.asm.*;
15  import org.codehaus.aspectwerkz.transform.Context;
16  import org.codehaus.aspectwerkz.transform.TransformationUtil;
17  import org.codehaus.aspectwerkz.transform.TransformationConstants;
18  import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
19  import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
20  import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
21  import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
22  import org.codehaus.aspectwerkz.definition.SystemDefinition;
23  import org.codehaus.aspectwerkz.expression.ExpressionContext;
24  import org.codehaus.aspectwerkz.expression.PointcutType;
25  import org.codehaus.aspectwerkz.reflect.ClassInfo;
26  import org.codehaus.aspectwerkz.reflect.MethodInfo;
27  
28  /***
29   * Adds a "proxy method" to the methods that matches an <tt>execution</tt> pointcut as well as prefixing the "original
30   * method".
31   * <br/>
32   * The proxy method calls the JPClass.invoke static method. The signature of the invoke method depends if the
33   * target method is static or not as follow:
34   * <pre>
35   *      invoke(callee, args.., caller) // non static
36   *      invoke(args.., caller) // static
37   * </pre>
38   * (The reason why is that it simplifies call pointcut stack management)
39   *
40   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
41   * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
42   */
43  public class MethodExecutionVisitor extends ClassAdapter implements TransformationConstants {
44  
45      private final ClassInfo m_classInfo;
46      private final ContextImpl m_ctx;
47      private String m_declaringTypeName;
48      private final Set m_addedMethods;
49  
50      /***
51       * Creates a new class adapter.
52       *
53       * @param cv
54       * @param classInfo
55       * @param ctx
56       * @param addedMethods
57       */
58      public MethodExecutionVisitor(final ClassVisitor cv,
59                                    final ClassInfo classInfo,
60                                    final Context ctx,
61                                    final Set addedMethods) {
62          super(cv);
63          m_classInfo = classInfo;
64          m_ctx = (ContextImpl) ctx;
65          m_addedMethods = addedMethods;
66      }
67  
68      /***
69       * Visits the class.
70       *
71       * @param access
72       * @param name
73       * @param superName
74       * @param interfaces
75       * @param sourceFile
76       */
77      public void visit(final int version,
78                        final int access,
79                        final String name,
80                        final String superName,
81                        final String[] interfaces,
82                        final String sourceFile) {
83          m_declaringTypeName = name;
84          super.visit(version, access, name, superName, interfaces, sourceFile);
85      }
86  
87      /***
88       * Visits the methods.
89       *
90       * @param access
91       * @param name
92       * @param desc
93       * @param exceptions
94       * @param attrs
95       * @return
96       */
97      public CodeVisitor visitMethod(final int access,
98                                     final String name,
99                                     final String desc,
100                                    final String[] exceptions,
101                                    final Attribute attrs) {
102 
103         if (INIT_METHOD_NAME.equals(name) ||
104             CLINIT_METHOD_NAME.equals(name) ||
105             name.startsWith(ASPECTWERKZ_PREFIX) ||
106             name.startsWith(SYNTHETIC_MEMBER_PREFIX) ||
107             name.startsWith(WRAPPER_METHOD_PREFIX)) {
108             return cv.visitMethod(access, name, desc, exceptions, attrs);
109         }
110 
111         int hash = AsmHelper.calculateMethodHash(name, desc);
112         MethodInfo methodInfo = m_classInfo.getMethod(hash);
113         if (methodInfo == null) {
114             System.err.println(
115                     "AW::WARNING " +
116                     "metadata structure could not be build for method ["
117                     + m_classInfo.getName().replace('/', '.')
118                     + '.' + name + ':' + desc + ']'
119             );
120             // bail out
121             return cv.visitMethod(access, name, desc, exceptions, attrs);
122         }
123 
124         ExpressionContext ctx = new ExpressionContext(PointcutType.EXECUTION, methodInfo, methodInfo);
125 
126         if (methodFilter(m_ctx.getDefinitions(), ctx, methodInfo)) {
127             return cv.visitMethod(access, name, desc, exceptions, attrs);
128         } else {
129             String prefixedOriginalName = TransformationUtil.getPrefixedOriginalMethodName(name, m_declaringTypeName);
130             if (m_addedMethods.contains(AlreadyAddedMethodAdapter.getMethodKey(prefixedOriginalName, desc))) {
131                 return cv.visitMethod(access, name, desc, exceptions, attrs);
132             }
133 
134             m_ctx.markAsAdvised();
135 
136             // create the proxy for the original method
137             createProxyMethod(access, name, desc, exceptions, attrs, methodInfo);
138 
139             int modifiers = ACC_SYNTHETIC;
140             if (Modifier.isStatic(access)) {
141                 modifiers |= ACC_STATIC;
142             }
143             // prefix the original method
144             return cv.visitMethod(
145                     modifiers,
146                     prefixedOriginalName,
147                     desc, exceptions, attrs
148             );
149         }
150     }
151 
152     /***
153      * Creates the "proxy method", e.g. the method that has the same name and signature as the original method but a
154      * completely other implementation.
155      *
156      * @param access
157      * @param name
158      * @param desc
159      * @param exceptions
160      * @param attrs
161      */
162     private void createProxyMethod(final int access,
163                                    final String name,
164                                    final String desc,
165                                    final String[] exceptions,
166                                    final Attribute attrs,
167                                    final MethodInfo methodInfo) {
168         CodeVisitor mv = cv.visitMethod(access, name, desc, exceptions, attrs);
169 
170         // load "this" ie callee if target method is not static
171         if (!Modifier.isStatic(access)) {
172             mv.visitVarInsn(ALOAD, 0);
173         }
174         // load args
175         AsmHelper.loadArgumentTypes(mv, Type.getArgumentTypes(desc), Modifier.isStatic(access));
176         // load "this" ie caller or null if method is static
177         if (Modifier.isStatic(access)) {
178             mv.visitInsn(ACONST_NULL);
179         } else {
180             mv.visitVarInsn(ALOAD, 0);
181         }
182 
183         int joinPointHash = AsmHelper.calculateMethodHash(name, desc);
184         String joinPointClassName = TransformationUtil.getJoinPointClassName(
185                 m_declaringTypeName,
186                 name,
187                 desc,
188                 m_declaringTypeName,
189                 JoinPointType.METHOD_EXECUTION_INT,
190                 joinPointHash
191         );
192 
193         // TODO: should we provide some sort of option to do JITgen when weaving instead of when loading ?
194         // use case: offline full packaging and alike
195 
196         mv.visitMethodInsn(
197                 INVOKESTATIC,
198                 joinPointClassName,
199                 INVOKE_METHOD_NAME,
200                 TransformationUtil.getInvokeSignatureForCodeJoinPoints(
201                         access, desc, m_declaringTypeName, m_declaringTypeName
202                 )
203         );
204 
205         AsmHelper.addReturnStatement(mv, Type.getReturnType(desc));
206         mv.visitMaxs(0, 0);
207 
208         // emit the joinpoint
209         m_ctx.addEmittedJoinPoint(
210                 new EmittedJoinPoint(
211                         JoinPointType.METHOD_EXECUTION_INT,
212                         m_declaringTypeName,
213                         name,
214                         desc,
215                         access,
216                         m_declaringTypeName,
217                         name,
218                         desc,
219                         access,
220                         joinPointHash,
221                         joinPointClassName,
222                         EmittedJoinPoint.NO_LINE_NUMBER
223                 )
224         );
225     }
226 
227     /***
228      * Filters out the methods that are not eligible for transformation.
229      *
230      * @param definitions
231      * @param ctx
232      * @param methodInfo
233      * @return boolean true if the method should be filtered out
234      */
235     public static boolean methodFilter(final Set definitions,
236                                        final ExpressionContext ctx,
237                                        final MethodInfo methodInfo) {
238         if (Modifier.isAbstract(methodInfo.getModifiers())
239             || Modifier.isNative(methodInfo.getModifiers())
240             || methodInfo.getName().equals(INIT_METHOD_NAME)
241             || methodInfo.getName().equals(CLINIT_METHOD_NAME)
242             || methodInfo.getName().startsWith(ORIGINAL_METHOD_PREFIX)) {
243             return true;
244         }
245         for (Iterator it = definitions.iterator(); it.hasNext();) {
246             if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
247                 return false;
248             } else {
249                 continue;
250             }
251         }
252         return true;
253     }
254 }