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.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             // create the proxy constructor for the original constructor
131             CodeVisitor proxyCtorCodeVisitor = cv.visitMethod(access, name, desc, exceptions, attrs);
132             // create the ctorBodyMethod for the original constructor body
133             int modifiers = ACC_SYNTHETIC | ACC_STATIC;
134             CodeVisitor ctorBodyMethodCodeVisitor = cv.visitMethod(
135                     modifiers, wrapperName, wrapperDesc, exceptions, attrs
136             );
137 
138             // return a dispatch Code Adapter in between the orginal one and both of them
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         // load "this"
155         ctorProxy.visitVarInsn(ALOAD, 0);// is too simple f.e. when DUP was used
156         // load args
157         AsmHelper.loadArgumentTypes(ctorProxy, Type.getArgumentTypes(desc), false);
158 
159         // caller = callee
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         // emit the joinpoint
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                 // heuristic for ALOAD_0 + DUP confirmed
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                         // first INVOKESPECIAL encountered to <init> for a NON new XXX()
313                         m_proxyCtorCodeVisitor.visitMethodInsn(opcode, owner, name, desc);
314                         // insert the JoinPoint invocation
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                         // load ALOAD 0 if under heuristic
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 }