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