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
198 ){super.visitMethodInsn(opcode, calleeClassName, calleeMethodName, calleeMethodDesc);
199 return;
200 }
201
202
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
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
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
252
253 if (Modifier.isStatic(m_callerMemberInfo.getModifiers())) {
254 visitInsn(ACONST_NULL);
255 } else {
256 visitVarInsn(ALOAD, 0);
257 }
258
259
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
271
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 }