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 org.objectweb.asm.ClassAdapter;
11 import org.objectweb.asm.Constants;
12 import org.objectweb.asm.ClassVisitor;
13 import org.objectweb.asm.CodeVisitor;
14 import org.objectweb.asm.Attribute;
15 import org.objectweb.asm.Type;
16 import org.objectweb.asm.CodeAdapter;
17 import org.objectweb.asm.Label;
18 import org.codehaus.aspectwerkz.reflect.ClassInfo;
19 import org.codehaus.aspectwerkz.reflect.ConstructorInfo;
20 import org.codehaus.aspectwerkz.transform.Context;
21 import org.codehaus.aspectwerkz.transform.TransformationUtil;
22 import org.codehaus.aspectwerkz.transform.TransformationConstants;
23 import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
24 import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
25 import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
26 import org.codehaus.aspectwerkz.expression.ExpressionContext;
27 import org.codehaus.aspectwerkz.expression.PointcutType;
28 import org.codehaus.aspectwerkz.definition.SystemDefinition;
29 import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
30
31 import java.util.Iterator;
32 import java.util.Set;
33
34 /***
35 * Handles constructor execution weaving.
36 * For each matching ctor, a static method is added with the same signature and with the extra thisClass parameter
37 * prepended to the list. Then the orginal ctor body is changed to call the JP.invoke, only after call to this / super
38 * initializers.
39 * <p/>
40 * TODO rename in ..execution..
41 *
42 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur</a>
43 */
44 public class ConstructorBodyVisitor extends ClassAdapter implements TransformationConstants {
45
46 private final ContextImpl m_ctx;
47 private final ClassInfo m_calleeClassInfo;
48 private String m_declaringTypeName;
49 private Set m_addedMethods;
50
51 /***
52 * Creates a new instance.
53 *
54 * @param cv
55 * @param classInfo
56 * @param ctx
57 * @param addedMethods
58 */
59 public ConstructorBodyVisitor(final ClassVisitor cv,
60 final ClassInfo classInfo,
61 final Context ctx,
62 final Set addedMethods) {
63 super(cv);
64 m_calleeClassInfo = classInfo;
65 m_ctx = (ContextImpl) ctx;
66 m_addedMethods = addedMethods;
67 }
68
69 /***
70 * Visits the class.
71 *
72 * @param access
73 * @param name
74 * @param superName
75 * @param interfaces
76 * @param sourceFile
77 */
78 public void visit(final int version,
79 final int access,
80 final String name,
81 final String superName,
82 final String[] interfaces,
83 final String sourceFile) {
84 m_declaringTypeName = name;
85 super.visit(version, access, name, superName, interfaces, sourceFile);
86 }
87
88 /***
89 * @param access
90 * @param name
91 * @param desc
92 * @param exceptions
93 * @param attrs
94 * @return
95 */
96 public CodeVisitor visitMethod(int access,
97 String name,
98 String desc,
99 String[] exceptions,
100 Attribute attrs) {
101 if (!INIT_METHOD_NAME.equals(name)) {
102 return super.visitMethod(access, name, desc, exceptions, attrs);
103 }
104
105 int hash = AsmHelper.calculateConstructorHash(desc);
106 ConstructorInfo constructorInfo = m_calleeClassInfo.getConstructor(hash);
107 if (constructorInfo == null) {
108 System.err.println(
109 "AW::WARNING " +
110 "metadata structure could not be build for constructor ["
111 + m_calleeClassInfo.getName().replace('/', '.')
112 + ".<init>: " + desc + ']'
113 );
114 return cv.visitMethod(access, name, desc, exceptions, attrs);
115 }
116
117 ExpressionContext ctx = new ExpressionContext(PointcutType.EXECUTION, constructorInfo, constructorInfo);
118
119 if (constructorFilter(m_ctx.getDefinitions(), ctx)) {
120 return cv.visitMethod(access, name, desc, exceptions, attrs);
121 } else {
122 String wrapperName = TransformationUtil.getConstructorBodyMethodName(m_declaringTypeName);
123 String wrapperDesc = TransformationUtil.getConstructorBodyMethodSignature(desc, m_declaringTypeName);
124 if (m_addedMethods.contains(AlreadyAddedMethodAdapter.getMethodKey(wrapperName, wrapperDesc))) {
125 return cv.visitMethod(access, name, desc, exceptions, attrs);
126 }
127
128 m_ctx.markAsAdvised();
129
130
131 CodeVisitor proxyCtorCodeVisitor = cv.visitMethod(access, name, desc, exceptions, attrs);
132
133 int modifiers = ACC_SYNTHETIC | ACC_STATIC;
134 CodeVisitor ctorBodyMethodCodeVisitor = cv.visitMethod(
135 modifiers, wrapperName, wrapperDesc, exceptions, attrs
136 );
137
138
139 return new DispatchCtorBodyCodeAdapter(proxyCtorCodeVisitor, ctorBodyMethodCodeVisitor, access, desc);
140 }
141 }
142
143
144 /***
145 * Creates the "proxy constructor" join point invocation body
146 *
147 * @param ctorProxy
148 * @param access
149 * @param desc
150 */
151 private void insertJoinPointInvoke(final CodeVisitor ctorProxy,
152 final int access,
153 final String desc) {
154
155 ctorProxy.visitVarInsn(ALOAD, 0);
156
157 AsmHelper.loadArgumentTypes(ctorProxy, Type.getArgumentTypes(desc), false);
158
159
160 ctorProxy.visitVarInsn(ALOAD, 0);
161
162 int joinPointHash = AsmHelper.calculateConstructorHash(desc);
163 String joinPointClassName = TransformationUtil.getJoinPointClassName(
164 m_declaringTypeName,
165 INIT_METHOD_NAME,
166 desc,
167 m_declaringTypeName,
168 JoinPointType.CONSTRUCTOR_EXECUTION_INT,
169 joinPointHash
170 );
171
172 ctorProxy.visitMethodInsn(
173 INVOKESTATIC,
174 joinPointClassName,
175 INVOKE_METHOD_NAME,
176 TransformationUtil.getInvokeSignatureForCodeJoinPoints(
177 access, desc, m_declaringTypeName, m_declaringTypeName
178 )
179 );
180
181
182 m_ctx.addEmittedJoinPoint(
183 new EmittedJoinPoint(
184 JoinPointType.CONSTRUCTOR_EXECUTION_INT,
185 m_declaringTypeName,
186 TransformationConstants.INIT_METHOD_NAME,
187 desc,
188 access,
189 m_declaringTypeName,
190 TransformationConstants.INIT_METHOD_NAME,
191 desc,
192 access,
193 joinPointHash,
194 joinPointClassName,
195 EmittedJoinPoint.NO_LINE_NUMBER
196 )
197 );
198 }
199
200 /***
201 * Filters out the constructor that are not eligible for transformation.
202 *
203 * @param definitions
204 * @param ctx
205 * @return boolean true if the constructor should be filtered out
206 */
207 public static boolean constructorFilter(final Set definitions,
208 final ExpressionContext ctx) {
209 for (Iterator it = definitions.iterator(); it.hasNext();) {
210 if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
211 return false;
212 } else {
213 continue;
214 }
215 }
216 return true;
217 }
218
219 /***
220 * A class that dispatch the ctor body instruction to any other given code visitor
221 * </p>
222 * The behavior is like this:
223 * 1/ as long as the INVOKESPECIAL for the object initialization has not been reached, every bytecode
224 * instruction is dispatched in the ctor code visitor. [note 1]
225 * 2/ when this one is reached, it is only added in the ctor code visitor and a JP invoke is added
226 * 3/ after that, only the other code visitor receives the instructions
227 * </p>
228 * [note 1] To support schemes like http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#9839
229 * where the stack is like ALOAD_0 + DUP, we handle a special case.
230 * f.e. CGlib proxy ctor are like that..
231 * Don't know if some other info can be left on the stack (f.e. ILOAD 1, DUP ...)
232 */
233 private class DispatchCtorBodyCodeAdapter extends CodeAdapter {
234 private CodeVisitor m_ctorBodyMethodCodeVisitor;
235 private CodeVisitor m_proxyCtorCodeVisitor;
236 private final int m_constructorAccess;
237 private final String m_constructorDesc;
238
239 private int m_newCount = 0;
240
241 private boolean m_proxyCtorCodeDone = false;
242 private boolean m_isALOADDUPHeuristic = false;
243 private int m_index = -1;
244
245 public DispatchCtorBodyCodeAdapter(CodeVisitor proxyCtor, CodeVisitor ctorBodyMethod, final int access,
246 final String desc) {
247 super(proxyCtor);
248 m_proxyCtorCodeVisitor = proxyCtor;
249 m_ctorBodyMethodCodeVisitor = ctorBodyMethod;
250 m_constructorAccess = access;
251 m_constructorDesc = desc;
252 }
253
254 public void visitInsn(int opcode) {
255 super.visitInsn(opcode);
256 if (!m_proxyCtorCodeDone && opcode == DUP && m_index == 0) {
257
258 m_isALOADDUPHeuristic = true;
259 m_index++;
260 }
261 }
262
263 public void visitIntInsn(int i, int i1) {
264 super.visitIntInsn(i, i1);
265 }
266
267 public void visitVarInsn(int opcode, int i1) {
268 super.visitVarInsn(opcode, i1);
269 if (!m_proxyCtorCodeDone) {
270 if (opcode == ALOAD && i1 == 0) {
271 m_index++;
272 }
273 }
274 }
275
276 public void visitFieldInsn(int i, String s, String s1, String s2) {
277 super.visitFieldInsn(i, s, s1, s2);
278 }
279
280 public void visitLdcInsn(Object o) {
281 super.visitLdcInsn(o);
282 }
283
284 public void visitIincInsn(int i, int i1) {
285 super.visitIincInsn(i, i1);
286 }
287
288 public void visitMultiANewArrayInsn(String s, int i) {
289 super.visitMultiANewArrayInsn(s, i);
290 }
291
292 /***
293 * Visit NEW type to ignore corresponding INVOKESPECIAL for those
294 *
295 * @param opcode
296 * @param name
297 */
298 public void visitTypeInsn(int opcode, String name) {
299 super.visitTypeInsn(opcode, name);
300 if (opcode == NEW) {
301 m_newCount++;
302 }
303 }
304
305 public void visitMethodInsn(int opcode,
306 String owner,
307 String name,
308 String desc) {
309 if (!m_proxyCtorCodeDone) {
310 if (opcode == INVOKESPECIAL) {
311 if (m_newCount == 0) {
312
313 m_proxyCtorCodeVisitor.visitMethodInsn(opcode, owner, name, desc);
314
315 insertJoinPointInvoke(m_proxyCtorCodeVisitor, m_constructorAccess, m_constructorDesc);
316 m_proxyCtorCodeVisitor.visitInsn(RETURN);
317 m_proxyCtorCodeVisitor.visitMaxs(0, 0);
318 m_proxyCtorCodeVisitor = null;
319 m_proxyCtorCodeDone = true;
320 cv = m_ctorBodyMethodCodeVisitor;
321
322 if (m_isALOADDUPHeuristic) {
323 m_ctorBodyMethodCodeVisitor.visitVarInsn(ALOAD, 0);
324 }
325 } else {
326 m_newCount--;
327 cv.visitMethodInsn(opcode, owner, name, desc);
328 }
329 } else {
330 cv.visitMethodInsn(opcode, owner, name, desc);
331 }
332 } else {
333 cv.visitMethodInsn(opcode, owner, name, desc);
334 }
335 }
336
337 }
338 }