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
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
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
81 where = ctClass.getClassInitializer();
82 }
83
84
85 if (methodFilter(where)) {
86 return;
87 }
88
89
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
98
99
100
101
102 return;
103 }
104
105
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
121
122
123
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
133
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
154
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
178
179
180
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
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
219
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 }