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.FieldInfo;
15  import org.codehaus.aspectwerkz.reflect.MemberInfo;
16  import org.codehaus.aspectwerkz.reflect.impl.javassist.JavassistClassInfo;
17  import org.codehaus.aspectwerkz.reflect.impl.javassist.JavassistFieldInfo;
18  import org.codehaus.aspectwerkz.reflect.impl.javassist.JavassistMethodInfo;
19  import org.codehaus.aspectwerkz.reflect.impl.javassist.JavassistConstructorInfo;
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.CtField;
33  import javassist.Modifier;
34  import javassist.NotFoundException;
35  import javassist.CtMethod;
36  import javassist.CtConstructor;
37  import javassist.expr.ExprEditor;
38  import javassist.expr.FieldAccess;
39  
40  /***
41   * Advises SET and GET 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 FieldSetGetTransformer 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          //m_joinPointIndex =
62          // TransformationUtil.getJoinPointIndex(klass.getCtClass()); //TODO
63          // thread safe and reentrant
64          // loop over all the definitions
65          for (Iterator it = definitions.iterator(); it.hasNext();) {
66              final SystemDefinition definition = (SystemDefinition) it.next();
67              final CtClass ctClass = klass.getCtClass();
68              final ClassInfo classInfo = JavassistClassInfo.getClassInfo(ctClass, context.getLoader());
69              if (classFilter(ctClass, new ExpressionContext(PointcutType.SET, classInfo, classInfo), definition)
70                  && classFilter(ctClass, new ExpressionContext(PointcutType.GET, classInfo, classInfo), definition)) {
71                  continue;
72              }
73              ctClass.instrument(new ExprEditor() {
74                  public void edit(FieldAccess fieldAccess) throws CannotCompileException {
75                      try {
76                          CtBehavior where = null;
77                          try {
78                              where = fieldAccess.where();
79                          } catch (RuntimeException e) {
80                              // <clinit> access leads to a bug in Javassist
81                              where = ctClass.getClassInitializer();
82                          }
83  
84                          // filter caller context
85                          if (methodFilter(where)) {
86                              return;
87                          }
88  
89                          // get field accessed information
90                          final String fieldName = fieldAccess.getFieldName();
91                          final String fieldSignature = fieldAccess.getField().getType().getName().replace('/', '.')
92                              + ' '
93                              + fieldName;
94                          FieldInfo fieldInfo = JavassistFieldInfo.getFieldInfo(fieldAccess.getField(), context
95                                  .getLoader());
96                          if (fieldInfo == null) {
97                              // when re-weaving is done, due to Javassist CtClass
98                              // behavior,
99                              // the fieldInfo for __AW_Clazz addded field can be
100                             // null
101                             // then we skip it silently
102                             return;
103                         }
104 
105                         // create the caller method info
106                         MemberInfo withinMethodInfo = null;
107                         if (where instanceof CtMethod) {
108                             withinMethodInfo = JavassistMethodInfo.getMethodInfo((CtMethod) where, context.getLoader());
109                         } else if (where instanceof CtConstructor) {
110                             withinMethodInfo = JavassistConstructorInfo.getConstructorInfo(
111                                 (CtConstructor) where,
112                                 context.getLoader());
113                         }
114 
115                         if (fieldAccess.isReader()
116                             && !getFieldFilter(
117                                 definition,
118                                 new ExpressionContext(PointcutType.GET, fieldInfo, withinMethodInfo),
119                                 fieldInfo)) {
120                             // check the declaring class for the field is not
121                             // the same as target class,
122                             // if that is the case then we have have class
123                             // loaded and set in the ___AW_clazz already
124                             String declaringClassFieldName = TransformationConstants.STATIC_CLASS_FIELD;
125                             CtClass declaringClass = fieldAccess.getField().getDeclaringClass();
126                             if (!declaringClass.getName().replace('/', '.').equals(
127                                 where.getDeclaringClass().getName().replace('/', '.'))) {
128                                 declaringClassFieldName = addFieldAccessDeclaringClassField(declaringClass, fieldAccess
129                                         .getField());
130                             }
131 
132                             //TODO ALEX might need to review since SET is not
133                             // handled gracefully that way
134                             StringBuffer body = new StringBuffer();
135                             StringBuffer callBody = new StringBuffer();
136                             callBody.append(TransformationConstants.JOIN_POINT_MANAGER_FIELD);
137                             callBody.append('.');
138                             callBody.append(TransformationConstants.PROCEED_WITH_GET_JOIN_POINT_METHOD);
139                             callBody.append('(');
140                             callBody.append(JavassistHelper.calculateHash(fieldAccess.getField()));
141                             callBody.append(',');
142                             callBody.append(klass.getJoinPointIndex());
143                             if (Modifier.isStatic(fieldAccess.getField().getModifiers())) {
144                                 callBody.append(", (Object)null, ");
145                             } else {
146                                 callBody.append(", $0, ");
147                             }
148                             callBody.append(declaringClassFieldName);
149                             callBody.append(",\"");
150                             callBody.append(fieldSignature);
151                             callBody.append("\");");
152 
153                             // handles advice returns null and field is
154                             // primitive type
155                             if (!fieldAccess.getField().getType().isPrimitive()) {
156                                 body.append("$_ = ($r)");
157                                 body.append(callBody.toString());
158                             } else {
159                                 String localResult = TransformationConstants.ASPECTWERKZ_PREFIX + "res";
160                                 body.append("{ Object ").append(localResult).append(" = ");
161                                 body.append(callBody.toString());
162                                 body.append("if (").append(localResult).append(" != null)");
163                                 body.append("$_ = ($r) ").append(localResult).append("; else ");
164                                 body.append("$_ = ");
165                                 body.append(JavassistHelper.getDefaultPrimitiveValue(fieldAccess.getField().getType()));
166                                 body.append("; }");
167                             }
168                             fieldAccess.replace(body.toString());
169                             context.markAsAdvised();
170                             klass.incrementJoinPointIndex();
171                         }
172                         if (fieldAccess.isWriter()
173                             && !setFieldFilter(
174                                 definition,
175                                 new ExpressionContext(PointcutType.SET, fieldInfo, withinMethodInfo),
176                                 fieldInfo)) {
177                             // check the declaring class for the field is not
178                             // the same as target class,
179                             // if that is the case then we have have class
180                             // loaded and set in the ___AW_clazz already
181                             String declaringClassFieldName = TransformationConstants.STATIC_CLASS_FIELD;
182                             CtClass declaringClass = fieldAccess.getField().getDeclaringClass();
183                             if (!declaringClass.getName().replace('/', '.').equals(
184                                 where.getDeclaringClass().getName().replace('/', '.'))) {
185                                 declaringClassFieldName = addFieldAccessDeclaringClassField(declaringClass, fieldAccess
186                                         .getField());
187                             }
188 
189                             //TODO ALEX think about null advice
190                             StringBuffer body = new StringBuffer();
191                             body.append(TransformationConstants.JOIN_POINT_MANAGER_FIELD);
192                             body.append('.');
193                             body.append(TransformationConstants.PROCEED_WITH_SET_JOIN_POINT_METHOD);
194                             body.append('(');
195                             body.append(JavassistHelper.calculateHash(fieldAccess.getField()));
196                             body.append(',');
197                             body.append(klass.getJoinPointIndex());
198                             if (Modifier.isStatic(fieldAccess.getField().getModifiers())) {
199                                 body.append(", $args, (Object)null, ");
200                             } else {
201                                 body.append(", $args, $0, ");
202                             }
203                             body.append(declaringClassFieldName);
204                             body.append(",\"");
205                             body.append(fieldSignature);
206                             body.append("\");");
207                             fieldAccess.replace(body.toString());
208                             context.markAsAdvised();
209                             klass.incrementJoinPointIndex();
210                         }
211                     } catch (NotFoundException nfe) {
212                         nfe.printStackTrace();
213                     }
214                 }
215             });
216         }
217 
218         //AXTransformationUtil.setJoinPointIndex(klass.getCtClass(),
219         // m_joinPointIndex);
220         klass.flushJoinPointIndex();
221     }
222 
223     /***
224      * Creates a new static class field, for the declaring class of the field that is accessed/modified.
225      * 
226      * @param ctClass the class
227      * @param ctField the field
228      * @return the name of the field
229      */
230     private String addFieldAccessDeclaringClassField(final CtClass ctClass, final CtField ctField) throws NotFoundException,
231             CannotCompileException {
232         String fieldName = TransformationConstants.STATIC_CLASS_FIELD
233             + TransformationConstants.DELIMITER
234             + "field"
235             + TransformationConstants.DELIMITER
236             + ctField.getDeclaringClass().getName().replace('.', '_');
237         boolean hasField = false;
238         CtField[] fields = ctClass.getDeclaredFields();
239         for (int i = 0; i < fields.length; i++) {
240             CtField field = fields[i];
241             if (field.getName().equals(fieldName)) {
242                 hasField = true;
243                 break;
244             }
245         }
246         if (!hasField) {
247             CtField field = new CtField(ctClass.getClassPool().get("java.lang.Class"), fieldName, ctClass);
248             field.setModifiers(Modifier.STATIC | Modifier.PRIVATE | Modifier.FINAL);
249             ctClass.addField(field, "java.lang.Class#forName(\""
250                 + ctField.getDeclaringClass().getName().replace('/', '.')
251                 + "\")");
252         }
253         return fieldName;
254     }
255 
256     /***
257      * Filters the classes to be transformed.
258      * 
259      * @param cg the class to filter
260      * @param ctx the context
261      * @param definition the definition
262      * @return boolean true if the method should be filtered away
263      */
264     public static boolean classFilter(final CtClass cg, final ExpressionContext ctx, final SystemDefinition definition) {
265         if (cg.isInterface()) {
266             return true;
267         }
268         String className = cg.getName().replace('/', '.');
269         if (definition.inExcludePackage(className)) {
270             return true;
271         }
272         if (!definition.inIncludePackage(className)) {
273             return true;
274         }
275         if (definition.isAdvised(ctx)) {
276             return false;
277         }
278         return true;
279     }
280 
281     /***
282      * Filters the methods.
283      * 
284      * @param method the method to filter
285      * @return boolean true if the method should be filtered away
286      */
287     public static boolean methodFilter(final CtBehavior method) {
288         return Modifier.isNative(method.getModifiers())
289             || Modifier.isAbstract(method.getModifiers())
290             || method.getName().startsWith(TransformationConstants.ASPECTWERKZ_PREFIX);
291     }
292 
293     /***
294      * Filters the PUTFIELD's to be transformed.
295      * 
296      * @param definition the definition
297      * @param ctx the context
298      * @param fieldInfo the field info
299      * @return
300      */
301     public static boolean setFieldFilter(
302         final SystemDefinition definition,
303         final ExpressionContext ctx,
304         final FieldInfo fieldInfo) {
305         if (fieldInfo.getName().startsWith(TransformationConstants.ASPECTWERKZ_PREFIX)) {
306             return true;
307         }
308         if (Modifier.isFinal(fieldInfo.getModifiers())) {
309             return true;
310         }
311         if (definition.hasPointcut(ctx)) {
312             return false;
313         }
314         return true;
315     }
316 
317     /***
318      * Filters the GETFIELD's to be transformed.
319      * 
320      * @param definition the definition
321      * @param ctx the context
322      * @param fieldInfo the field info
323      * @return
324      */
325     public static boolean getFieldFilter(
326         final SystemDefinition definition,
327         final ExpressionContext ctx,
328         final FieldInfo fieldInfo) {
329         if (fieldInfo.getName().startsWith(TransformationConstants.ASPECTWERKZ_PREFIX)) {
330             return true;
331         }
332         if (definition.hasPointcut(ctx)) {
333             return false;
334         }
335         return true;
336     }
337 }