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.delegation;
9
10 import org.codehaus.aspectwerkz.definition.SystemDefinition;
11 import org.codehaus.aspectwerkz.expression.ExpressionContext;
12 import org.codehaus.aspectwerkz.expression.PointcutType;
13 import org.codehaus.aspectwerkz.reflect.ClassInfo;
14 import org.codehaus.aspectwerkz.reflect.MemberInfo;
15 import org.codehaus.aspectwerkz.reflect.MethodInfo;
16 import org.codehaus.aspectwerkz.reflect.impl.javassist.JavassistClassInfo;
17 import org.codehaus.aspectwerkz.reflect.impl.javassist.JavassistClassInfoRepository;
18 import org.codehaus.aspectwerkz.reflect.impl.javassist.JavassistConstructorInfo;
19 import org.codehaus.aspectwerkz.reflect.impl.javassist.JavassistMethodInfo;
20 import org.codehaus.aspectwerkz.transform.Context;
21 import org.codehaus.aspectwerkz.transform.TransformationUtil;
22 import org.codehaus.aspectwerkz.transform.Transformer;
23 import org.codehaus.aspectwerkz.transform.TransformationConstants;
24 import org.codehaus.aspectwerkz.transform.TransformationConstants;
25
26 import java.util.Iterator;
27 import java.util.List;
28
29 import javassist.CannotCompileException;
30 import javassist.CtBehavior;
31 import javassist.CtClass;
32 import javassist.CtConstructor;
33 import javassist.CtField;
34 import javassist.CtMethod;
35 import javassist.Modifier;
36 import javassist.NotFoundException;
37 import javassist.expr.ExprEditor;
38 import javassist.expr.MethodCall;
39
40 /***
41 * Advises method CALL join points.
42 *
43 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
44 * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
45 */
46 public class MethodCallTransformer implements Transformer {
47 /***
48 * The join point index.
49 */
50
51
52 /***
53 * Transforms the call side pointcuts.
54 *
55 * @param context the transformation context
56 * @param klass the class set.
57 */
58 public void transform(final Context context, final Klass klass) throws NotFoundException, CannotCompileException {
59 List definitions = context.getDefinitions();
60
61
62
63
64 for (Iterator it = definitions.iterator(); it.hasNext();) {
65 final SystemDefinition definition = (SystemDefinition) it.next();
66 final CtClass ctClass = klass.getCtClass();
67 ClassInfo classInfo = JavassistClassInfo.getClassInfo(ctClass, context.getLoader());
68 if (classFilter(definition, new ExpressionContext(PointcutType.CALL, null, classInfo), ctClass)) {
69 continue;
70 }
71 ctClass.instrument(new ExprEditor() {
72 public void edit(MethodCall methodCall) throws CannotCompileException {
73
74 if (methodCall.isSuper()) {
75 return;
76 }
77 try {
78 CtBehavior where;
79 try {
80 where = methodCall.where();
81 } catch (RuntimeException e) {
82
83 where = ctClass.getClassInitializer();
84 }
85
86
87 if (methodFilterCaller(where)) {
88 return;
89 }
90
91
92 CtMethod calleeMethod = methodCall.getMethod();
93 String calleeClassName = methodCall.getClassName();
94
95
96 if (!definition.inIncludePackage(calleeClassName)) {
97 return;
98 }
99
100
101 if (methodFilterCallee(calleeMethod)) {
102 return;
103 }
104 JavassistClassInfoRepository classInfoRepository = JavassistClassInfoRepository
105 .getRepository(context.getLoader());
106
107
108 ClassInfo calleeSideClassInfo = classInfoRepository.getClassInfo(calleeClassName);
109 if (calleeSideClassInfo == null) {
110 calleeSideClassInfo = JavassistClassInfo.getClassInfo(ctClass.getClassPool().get(
111 calleeClassName), context.getLoader());
112 }
113
114
115
116 MemberInfo withinMemberInfo = null;
117 if (where instanceof CtMethod) {
118 withinMemberInfo = JavassistMethodInfo.getMethodInfo((CtMethod) where, context.getLoader());
119 } else if (where instanceof CtConstructor) {
120 withinMemberInfo = JavassistConstructorInfo.getConstructorInfo(
121 (CtConstructor) where,
122 context.getLoader());
123 }
124
125
126 MethodInfo calleeSideMethodInfo = JavassistMethodInfo.getMethodInfo(
127 methodCall.getMethod(),
128 context.getLoader());
129 ExpressionContext ctx = new ExpressionContext(
130 PointcutType.CALL,
131 calleeSideMethodInfo,
132 withinMemberInfo);
133 if (definition.hasPointcut(ctx) || definition.hasCflowPointcut(ctx)) {
134
135
136
137
138 String declaringClassMethodName = TransformationConstants.STATIC_CLASS_FIELD;
139 CtMethod method = methodCall.getMethod();
140 CtClass declaringClass = method.getDeclaringClass();
141 if (!declaringClass.getName().replace('/', '.').equals(
142 where.getDeclaringClass().getName().replace('/', '.'))) {
143 declaringClassMethodName = addCalleeMethodDeclaringClassField(ctClass, method);
144 }
145
146
147
148 StringBuffer body = new StringBuffer();
149 StringBuffer callBody = new StringBuffer();
150 callBody.append(TransformationConstants.JOIN_POINT_MANAGER_FIELD);
151 callBody.append('.');
152 callBody.append(TransformationConstants.PROCEED_WITH_CALL_JOIN_POINT_METHOD);
153 callBody.append('(');
154 callBody.append(JavassistHelper.calculateHash(method));
155 callBody.append(',');
156 callBody.append(klass.getJoinPointIndex());
157 callBody.append(", args, ");
158 callBody.append(TransformationConstants.STATIC_CLASS_FIELD);
159 if (Modifier.isStatic(where.getModifiers())) {
160 callBody.append(", nullObject, ");
161 } else {
162 callBody.append(", this, ");
163 }
164 callBody.append("declaringClass, $0, \"");
165 callBody.append(where.getName());
166 callBody.append("\",\"");
167 callBody.append(where.getSignature());
168 callBody.append("\",");
169 callBody.append(TransformationConstants.JOIN_POINT_TYPE_METHOD_CALL);
170 callBody.append(");");
171 body.append('{');
172 if (method.getParameterTypes().length > 0) {
173 body.append("Object[] args = $args; ");
174 } else {
175 body.append("Object[] args = null; ");
176 }
177 body.append("Class declaringClass = ");
178 body.append(declaringClassMethodName);
179 body.append("; ");
180 if (Modifier.isStatic(where.getModifiers())) {
181 body.append("Object nullObject = null;");
182 }
183 if (methodCall.getMethod().getReturnType() == CtClass.voidType) {
184 body.append("$_ = ").append(callBody.toString()).append("}");
185 } else if (!methodCall.getMethod().getReturnType().isPrimitive()) {
186 body.append("$_ = ($r)");
187 body.append(callBody.toString());
188 body.append("}");
189 } else {
190 String localResult = TransformationConstants.ASPECTWERKZ_PREFIX + "res";
191 body.append("Object ").append(localResult).append(" = ");
192 body.append(callBody.toString());
193 body.append("if (").append(localResult).append(" != null)");
194 body.append("$_ = ($r) ").append(localResult).append("; else ");
195 body.append("$_ = ");
196 body.append(JavassistHelper.getDefaultPrimitiveValue(methodCall.getMethod()
197 .getReturnType()));
198 body.append("; }");
199 }
200 methodCall.replace(body.toString());
201 context.markAsAdvised();
202 klass.incrementJoinPointIndex();
203 }
204 } catch (NotFoundException nfe) {
205 nfe.printStackTrace();
206
207
208 }
209 }
210 });
211 }
212
213
214
215 klass.flushJoinPointIndex();
216 }
217
218 /***
219 * Creates a new static class field, for the declaring class of the callee method.
220 *
221 * @param ctClass the class
222 * @param ctMethod the method
223 * @return the name of the field
224 */
225 private String addCalleeMethodDeclaringClassField(final CtClass ctClass, final CtMethod ctMethod) throws NotFoundException,
226 CannotCompileException {
227 String fieldName = TransformationConstants.STATIC_CLASS_FIELD
228 + TransformationConstants.DELIMITER
229 + "method"
230 + TransformationConstants.DELIMITER
231 + ctMethod.getDeclaringClass().getName().replace('.', '_');
232 boolean hasField = false;
233 CtField[] fields = ctClass.getDeclaredFields();
234 for (int i = 0; i < fields.length; i++) {
235 CtField field = fields[i];
236 if (field.getName().equals(fieldName)) {
237 hasField = true;
238 break;
239 }
240 }
241 if (!hasField) {
242 CtField field = new CtField(ctClass.getClassPool().get("java.lang.Class"), fieldName, ctClass);
243 field.setModifiers(Modifier.STATIC | Modifier.PRIVATE | Modifier.FINAL);
244 ctClass.addField(field, "java.lang.Class#forName(\""
245 + ctMethod.getDeclaringClass().getName().replace('/', '.')
246 + "\")");
247 }
248 return fieldName;
249 }
250
251 /***
252 * Filters the classes to be transformed.
253 *
254 * @param definition the definition
255 * @param ctx the context
256 * @param cg the class to filter
257 * @return boolean true if the method should be filtered away
258 */
259 public static boolean classFilter(final SystemDefinition definition, final ExpressionContext ctx, final CtClass cg) {
260 if (cg.isInterface()) {
261 return true;
262 }
263 String className = cg.getName().replace('/', '.');
264 if (definition.inExcludePackage(className)) {
265 return true;
266 }
267 if (!definition.inIncludePackage(className)) {
268 return true;
269 }
270 if (definition.isAdvised(ctx)) {
271 return false;
272 }
273 return true;
274 }
275
276 /***
277 * Filters the caller methods.
278 *
279 * @param method the method to filter
280 * @return boolean true if the method should be filtered away
281 */
282 public static boolean methodFilterCaller(final CtBehavior method) {
283 if (Modifier.isNative(method.getModifiers())
284 || Modifier.isInterface(method.getModifiers())
285 || method.getName().equals(TransformationConstants.CLASS_LOOKUP_METHOD)) {
286 return true;
287 } else {
288 return false;
289 }
290 }
291
292 /***
293 * Filters the callee methods.
294 *
295 * @param method the name of method to filter
296 * @return boolean true if the method should be filtered away
297 * @TODO: create metadata instance and check with the system
298 */
299 public static boolean methodFilterCallee(final CtMethod method) {
300 if (method.getName().equals("<init>")
301 || method.getName().equals("<clinit>")
302 || method.getName().startsWith(TransformationConstants.ORIGINAL_METHOD_PREFIX)
303 || method.getName().equals(TransformationConstants.CLASS_LOOKUP_METHOD)) {
304 return true;
305 } else {
306 return false;
307 }
308 }
309 }