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.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      //AXprivate int m_joinPointIndex;
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          //AXm_joinPointIndex =
62          // TransformationUtil.getJoinPointIndex(klass.getCtClass()); //TODO
63          // thread safe reentrant
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                      // AW-228, super.callSomething(..) is not a valid join point
74                      if (methodCall.isSuper()) {
75                          return;
76                      }
77                      try {
78                          CtBehavior where;
79                          try {
80                              where = methodCall.where();
81                          } catch (RuntimeException e) {
82                              // <clinit> access leads to a bug in Javassist
83                              where = ctClass.getClassInitializer();
84                          }
85  
86                          // filter caller methods
87                          if (methodFilterCaller(where)) {
88                              return;
89                          }
90  
91                          // get the callee method name, signature and class name
92                          CtMethod calleeMethod = methodCall.getMethod();
93                          String calleeClassName = methodCall.getClassName();
94  
95                          // filter callee classes
96                          if (!definition.inIncludePackage(calleeClassName)) {
97                              return;
98                          }
99  
100                         // filter callee methods
101                         if (methodFilterCallee(calleeMethod)) {
102                             return;
103                         }
104                         JavassistClassInfoRepository classInfoRepository = JavassistClassInfoRepository
105                                 .getRepository(context.getLoader());
106 
107                         // TODO: callee side class info is NOT used, make use of it
108                         ClassInfo calleeSideClassInfo = classInfoRepository.getClassInfo(calleeClassName);
109                         if (calleeSideClassInfo == null) {
110                             calleeSideClassInfo = JavassistClassInfo.getClassInfo(ctClass.getClassPool().get(
111                                 calleeClassName), context.getLoader());
112                         }
113 
114                         // create the caller method info, used for 'within' and
115                         // 'withincode'
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                         // create the callee method info
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                             // check the callee class is not the same as target
135                             // class, if that is the case
136                             // then we have have class loaded and set in the
137                             // ___AW_clazz already
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                             // call the wrapper method instead of the callee
147                             // method
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                         // TODO: should we swallow this exception?
208                     }
209                 }
210             });
211         }
212 
213         //AxTransformationUtil.setJoinPointIndex(klass.getCtClass(),
214         // m_joinPointIndex);
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 }