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.inlining.weaver;
9
10 import java.util.Set;
11 import java.util.Iterator;
12 import java.util.Collection;
13 import java.util.HashMap;
14
15 import org.objectweb.asm.*;
16 import org.codehaus.aspectwerkz.transform.Context;
17 import org.codehaus.aspectwerkz.transform.TransformationConstants;
18 import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
19 import org.codehaus.aspectwerkz.reflect.ClassInfo;
20 import org.codehaus.aspectwerkz.expression.ExpressionContext;
21 import org.codehaus.aspectwerkz.expression.PointcutType;
22 import org.codehaus.aspectwerkz.expression.ExpressionInfo;
23 import org.codehaus.aspectwerkz.definition.SystemDefinition;
24 import org.codehaus.aspectwerkz.definition.AdviceDefinition;
25 import org.codehaus.aspectwerkz.definition.DeploymentScope;
26 import org.codehaus.aspectwerkz.DeploymentModel;
27
28 /***
29 * Adds an instance level aspect management to the target class.
30 *
31 * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
32 */
33 public class InstanceLevelAspectVisitor extends ClassAdapter implements TransformationConstants {
34
35 private final ContextImpl m_ctx;
36 private final ClassInfo m_classInfo;
37 private boolean m_isAdvised = false;
38
39 /***
40 * Creates a new add interface class adapter.
41 *
42 * @param cv
43 * @param classInfo
44 * @param ctx
45 */
46 public InstanceLevelAspectVisitor(final ClassVisitor cv,
47 final ClassInfo classInfo,
48 final Context ctx) {
49 super(cv);
50 m_classInfo = classInfo;
51 m_ctx = (ContextImpl) ctx;
52 }
53
54 /***
55 * Visits the class.
56 *
57 * @param access
58 * @param name
59 * @param superName
60 * @param interfaces
61 * @param sourceFile
62 */
63 public void visit(final int version,
64 final int access,
65 final String name,
66 final String superName,
67 final String[] interfaces,
68 final String sourceFile) {
69
70 if (classFilter(m_classInfo, m_ctx.getDefinitions())) {
71 super.visit(version, access, name, superName, interfaces, sourceFile);
72 return;
73 }
74
75 for (int i = 0; i < interfaces.length; i++) {
76 String anInterface = interfaces[i];
77 if (anInterface.equals(HAS_INSTANCE_LEVEL_ASPECT_INTERFACE_NAME)) {
78 super.visit(version, access, name, superName, interfaces, sourceFile);
79 return;
80 }
81 }
82 String[] newInterfaceArray = new String[interfaces.length + 1];
83 for (int i = 0; i < interfaces.length; i++) {
84 newInterfaceArray[i] = interfaces[i];
85 }
86 newInterfaceArray[interfaces.length] = HAS_INSTANCE_LEVEL_ASPECT_INTERFACE_NAME;
87
88
89 super.visit(version, access, name, superName, newInterfaceArray, sourceFile);
90
91
92 addAspectMapField();
93
94
95 addGetAspectMethod(name);
96 }
97
98 /***
99 * Appends mixin instantiation to the clinit method and/or init method.
100 *
101 * @param access
102 * @param name
103 * @param desc
104 * @param exceptions
105 * @param attrs
106 * @return
107 */
108 public CodeVisitor visitMethod(final int access,
109 final String name,
110 final String desc,
111 final String[] exceptions,
112 final Attribute attrs) {
113 if (m_isAdvised) {
114 if (name.equals(INIT_METHOD_NAME)) {
115 CodeVisitor mv = new AppendToInitMethodCodeAdapter(
116 cv.visitMethod(access, name, desc, exceptions, attrs),
117 name
118 );
119 mv.visitMaxs(0, 0);
120 return mv;
121 }
122 }
123 return cv.visitMethod(access, name, desc, exceptions, attrs);
124 }
125
126 /***
127 * Adds the aspect map field to the target class.
128 */
129 private void addAspectMapField() {
130 super.visitField(
131 ACC_PRIVATE + ACC_SYNTHETIC + ACC_TRANSIENT,
132 INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
133 INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE,
134 null, null
135 );
136 }
137
138 /***
139 * Adds the getAspect(..) method to the target class.
140 *
141 * @param name the class name of the target class
142 */
143 private void addGetAspectMethod(final String name) {
144 CodeVisitor cv = super.visitMethod(
145 ACC_PUBLIC + ACC_SYNTHETIC,
146 GET_INSTANCE_LEVEL_ASPECT_METHOD_NAME,
147 GET_INSTANCE_LEVEL_ASPECT_METHOD_SIGNATURE,
148 null, null
149 );
150
151 cv.visitVarInsn(ALOAD, 0);
152 cv.visitFieldInsn(
153 GETFIELD,
154 name,
155 INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
156 INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
157 );
158
159
160 Label ifFieldNullNotLabel = new Label();
161 cv.visitJumpInsn(IFNONNULL, ifFieldNullNotLabel);
162 cv.visitVarInsn(ALOAD, 0);
163 cv.visitTypeInsn(NEW, HASH_MAP_CLASS_NAME);
164 cv.visitInsn(DUP);
165 cv.visitMethodInsn(
166 INVOKESPECIAL,
167 HASH_MAP_CLASS_NAME,
168 INIT_METHOD_NAME,
169 NO_PARAM_RETURN_VOID_SIGNATURE
170 );
171 cv.visitFieldInsn(
172 PUTFIELD,
173 m_classInfo.getName().replace('.', '/'),
174 INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
175 INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
176 );
177 cv.visitLabel(ifFieldNullNotLabel);
178
179 cv.visitVarInsn(ALOAD, 0);
180 cv.visitFieldInsn(
181 GETFIELD,
182 name,
183 INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
184 INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
185 );
186
187 cv.visitVarInsn(ALOAD, 2);
188 cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME, GET_METHOD_NAME, GET_METHOD_SIGNATURE);
189 cv.visitVarInsn(ASTORE, 4);
190 cv.visitVarInsn(ALOAD, 4);
191 Label ifNullNotLabel = new Label();
192 cv.visitJumpInsn(IFNONNULL, ifNullNotLabel);
193 cv.visitVarInsn(ALOAD, 2);
194 cv.visitVarInsn(ALOAD, 3);
195 cv.visitVarInsn(ALOAD, 0);
196 cv.visitMethodInsn(
197 INVOKESTATIC,
198 ASPECTS_CLASS_NAME,
199 ASPECT_OF_METHOD_NAME,
200 ASPECT_OF_PER_INSTANCE_METHOD_SIGNATURE
201 );
202 cv.visitVarInsn(ASTORE, 4);
203 cv.visitVarInsn(ALOAD, 0);
204 cv.visitFieldInsn(
205 GETFIELD,
206 name,
207 INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
208 INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
209 );
210 cv.visitVarInsn(ALOAD, 2);
211 cv.visitVarInsn(ALOAD, 4);
212 cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME, PUT_METHOD_NAME, PUT_METHOD_SIGNATURE);
213 cv.visitInsn(POP);
214 cv.visitLabel(ifNullNotLabel);
215 cv.visitVarInsn(ALOAD, 4);
216 cv.visitInsn(ARETURN);
217 cv.visitMaxs(0, 0);
218
219 m_ctx.markAsAdvised();
220 m_isAdvised = true;
221 }
222
223 /***
224 * Filters the classes to be transformed.
225 *
226 * @param classInfo the class to filter
227 * @param definitions a set with the definitions
228 * @return boolean true if the method should be filtered away
229 */
230 public static boolean classFilter(final ClassInfo classInfo, final Set definitions) {
231 if (classInfo.isInterface()) {
232 return true;
233 }
234
235 ExpressionContext ctx = new ExpressionContext(PointcutType.WITHIN, null, classInfo);
236
237 for (Iterator it = definitions.iterator(); it.hasNext();) {
238 SystemDefinition systemDef = (SystemDefinition) it.next();
239 String className = classInfo.getName().replace('/', '.');
240 if (systemDef.inExcludePackage(className)) {
241 return true;
242 }
243 if (!systemDef.inIncludePackage(className)) {
244 return true;
245 }
246
247
248 Collection adviceDefs = systemDef.getAdviceDefinitions();
249 for (Iterator defs = adviceDefs.iterator(); defs.hasNext();) {
250 AdviceDefinition adviceDef = (AdviceDefinition) defs.next();
251 ExpressionInfo expressionInfo = adviceDef.getExpressionInfo();
252 if (expressionInfo == null) {
253 continue;
254 }
255 if (adviceDef.getAspectDefinition().getDeploymentModel().equals(DeploymentModel.PER_INSTANCE)
256 && expressionInfo.getAdvisedClassFilterExpression().match(ctx)) {
257 return false;
258 }
259 }
260
261
262 Collection deploymentScopes = systemDef.getDeploymentScopes();
263 for (Iterator scopes = deploymentScopes.iterator(); scopes.hasNext();) {
264 DeploymentScope deploymentScope = (DeploymentScope) scopes.next();
265 ExpressionInfo expression = new ExpressionInfo(
266 deploymentScope.getExpression(),
267 systemDef.getUuid()
268 );
269 if (expression.getAdvisedClassFilterExpression().match(ctx)) {
270 return false;
271 }
272 }
273 }
274
275 return true;
276 }
277
278 /***
279 * Adds initialization of aspect map field to end of the init method.
280 *
281 * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
282 */
283 private class AppendToInitMethodCodeAdapter extends AfterObjectInitializationCodeAdapter {
284
285 private boolean m_done = false;
286
287 public AppendToInitMethodCodeAdapter(final CodeVisitor ca, String callerMemberName) {
288 super(ca, callerMemberName);
289 }
290
291 /***
292 * Inserts the init of the aspect field right after the call to super(..) of this(..).
293 *
294 * @param opcode
295 * @param owner
296 * @param name
297 * @param desc
298 */
299 public void visitMethodInsn(int opcode,
300 String owner,
301 String name,
302 String desc) {
303 super.visitMethodInsn(opcode, owner, name, desc);
304 if (opcode == INVOKESPECIAL && m_isObjectInitialized && !m_done) {
305 m_done = true;
306
307
308 cv.visitVarInsn(ALOAD, 0);
309 cv.visitTypeInsn(NEW, HASH_MAP_CLASS_NAME);
310 cv.visitInsn(DUP);
311 cv.visitMethodInsn(
312 INVOKESPECIAL,
313 HASH_MAP_CLASS_NAME,
314 INIT_METHOD_NAME,
315 NO_PARAM_RETURN_VOID_SIGNATURE
316 );
317 cv.visitFieldInsn(
318 PUTFIELD,
319 m_classInfo.getName().replace('.', '/'),
320 INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
321 INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
322 );
323 }
324 }
325 }
326 }