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
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
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
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
171 if (!Modifier.isStatic(access)) {
172 mv.visitVarInsn(ALOAD, 0);
173 }
174
175 AsmHelper.loadArgumentTypes(mv, Type.getArgumentTypes(desc), Modifier.isStatic(access));
176
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
194
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
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 }