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 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          // add the interface
89          super.visit(version, access, name, superName, newInterfaceArray, sourceFile);
90  
91          // add the field with the aspect instance map
92          addAspectMapField();
93  
94          // add the getAspect(..) method
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         // if == null, field = new HashMap()
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);//qName
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);//qName
194         cv.visitVarInsn(ALOAD, 3);//containerClassName
195         cv.visitVarInsn(ALOAD, 0);//this (perInstance)
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             // match on perinstance deployed aspects
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             // match on deployment scopes, e.g. potential perinstance deployment aspects
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                 // initialize aspect map field
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 }