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.compiler;
9   
10  import java.util.Map;
11  import java.util.Iterator;
12  import java.util.WeakHashMap;
13  import java.util.HashSet;
14  import java.util.Set;
15  
16  import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
17  import org.codehaus.aspectwerkz.expression.ExpressionContext;
18  import org.codehaus.aspectwerkz.expression.PointcutType;
19  import org.codehaus.aspectwerkz.expression.ExpressionInfo;
20  import org.codehaus.aspectwerkz.reflect.MethodInfo;
21  import org.codehaus.aspectwerkz.reflect.ClassInfo;
22  import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
23  import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
24  import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
25  
26  /***
27   * TODO is factory a good name, now that it does so much more?
28   * <p/>
29   * Factory for the different join point implementations.
30   * Compiles a new join point on the fly and loads the class.
31   *
32   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
33   */
34  public class JoinPointFactory {
35  
36      /***
37       * Stores the compilation infos - mapped to the last compiled join point class based on this compilation info.
38       */
39      private static final Map COMPILATION_INFO_REPOSITORY = new WeakHashMap();
40  
41      /***
42       * Compiles and loades a join point class, one specific class for each distinct join point.
43       *
44       * @param model  the model for the compilation
45       * @param loader the class loader that the compiled join point should live in
46       * @return the compiled join point class
47       */
48      public static Class compileJoinPointAndAttachToClassLoader(final CompilationInfo.Model model,
49                                                                 final ClassLoader loader) {
50          return attachToClassLoader(model.getJoinPointClassName(), loader, compileJoinPoint(model));
51      }
52  
53      /***
54       * Loads a join point class, one specific class for each distinct join point.
55       *
56       * @param joinpointClassName
57       * @param loader the class loader that the compiled join point should live in
58       * @param bytecode of the joinpoint
59       * @return the compiled join point class
60       */
61      public static Class attachToClassLoader(final String joinpointClassName,
62                                              final ClassLoader loader,
63                                              final byte[] bytecode) {
64          return AsmHelper.defineClass(loader, bytecode, joinpointClassName);
65      }
66  
67      /***
68       * Adds or updates a compilation info. The class key is always the first compiled join point class.
69       *
70       * @param clazz
71       * @param compilationInfo
72       */
73      public static void addCompilationInfo(final Class clazz, final CompilationInfo compilationInfo) {
74          COMPILATION_INFO_REPOSITORY.put(clazz, compilationInfo);
75      }
76  
77      /***
78       * Compiles a join point class, one specific class for each distinct join point.
79       *
80       * @param model the model for the compilation
81       * @return the compiled join point bytecode
82       */
83      public static byte[] compileJoinPoint(final CompilationInfo.Model model) {
84          switch (model.getEmittedJoinPoint().getJoinPointType()) {
85              case JoinPointType.METHOD_EXECUTION_INT:
86                  return new MethodExecutionJoinPointCompiler(model).compile();
87              case JoinPointType.METHOD_CALL_INT:
88                  return new MethodCallJoinPointCompiler(model).compile();
89              case JoinPointType.CONSTRUCTOR_EXECUTION_INT:
90                  return new ConstructorExecutionJoinPointCompiler(model).compile();
91              case JoinPointType.CONSTRUCTOR_CALL_INT:
92                  return new ConstructorCallJoinPointCompiler(model).compile();
93              case JoinPointType.FIELD_SET_INT:
94                  return new FieldSetJoinPointCompiler(model).compile();
95              case JoinPointType.FIELD_GET_INT:
96                  return new FieldGetJoinPointCompiler(model).compile();
97              case JoinPointType.HANDLER_INT:
98                  return new HandlerJoinPointCompiler(model).compile();
99              case JoinPointType.STATIC_INITIALIZATION_INT:
100                 return new StaticInitializationJoinPointCompiler(model).compile();
101             default:
102                 throw new UnsupportedOperationException(
103                         "join point type is not supported: " + model.getEmittedJoinPoint().getJoinPointType()
104                 );
105         }
106     }
107 
108     /***
109      * Redefines the originally compiled join point.
110      *
111      * @param compilationInfo the model for the compilation
112      * @return the compiled join point bytecode
113      */
114     public static byte[] redefineJoinPoint(final CompilationInfo compilationInfo) {
115         switch (compilationInfo.getInitialModel().getEmittedJoinPoint().getJoinPointType()) {
116             case JoinPointType.METHOD_EXECUTION_INT:
117                 return new MethodExecutionJoinPointRedefiner(compilationInfo).compile();
118             case JoinPointType.METHOD_CALL_INT:
119                 return new MethodCallJoinPointRedefiner(compilationInfo).compile();
120             case JoinPointType.CONSTRUCTOR_EXECUTION_INT:
121                 return new ConstructorExecutionJoinPointRedefiner(compilationInfo).compile();
122             case JoinPointType.CONSTRUCTOR_CALL_INT:
123                 return new ConstructorCallJoinPointRedefiner(compilationInfo).compile();
124             case JoinPointType.FIELD_SET_INT:
125                 return new FieldSetJoinPointRedefiner(compilationInfo).compile();
126             case JoinPointType.FIELD_GET_INT:
127                 return new FieldGetJoinPointRedefiner(compilationInfo).compile();
128             case JoinPointType.HANDLER_INT:
129                 return new HandlerJoinPointRedefiner(compilationInfo).compile();
130             default:
131                 throw new UnsupportedOperationException(
132                         "join point type is not supported: " +
133                         compilationInfo.getInitialModel().getEmittedJoinPoint().getJoinPointType()
134                 );
135         }
136     }
137 
138     /***
139      * Returns a list with all the join point compilers that matches a specific pointcut expression.
140      * <p/>
141      * To be used for redefinition of the join point compilers only. This since the compilers must have been created
142      * in advance to exist in the repository (which is done when the target class is loaded).
143      *
144      * @param expression the pointcut expression
145      * @return a set with the matching emitted join point
146      */
147     public static Set getJoinPointsMatching(final ExpressionInfo expression) {
148         final Set matchingJoinPointInfos = new HashSet();
149         for (Iterator it = COMPILATION_INFO_REPOSITORY.entrySet().iterator(); it.hasNext();) {
150             final Map.Entry entry = (Map.Entry) it.next();
151 
152             final Class clazz = (Class) entry.getKey();
153             final CompilationInfo compilationInfo = (CompilationInfo) entry.getValue();
154             final EmittedJoinPoint joinPoint = (EmittedJoinPoint) compilationInfo.
155                     getInitialModel().getEmittedJoinPoint();
156             final ClassLoader loader = clazz.getClassLoader();
157 
158             final ClassInfo calleeClassInfo = AsmClassInfo.getClassInfo(joinPoint.getCalleeClassName(), loader);
159             final ClassInfo callerClassInfo = AsmClassInfo.getClassInfo(joinPoint.getCallerClassName(), loader);
160             final MethodInfo callerMethodInfo = getCallerMethodInfo(callerClassInfo, joinPoint);
161 
162             ExpressionContext ctx = null;
163             switch (joinPoint.getJoinPointType()) {
164                 case JoinPointType.METHOD_EXECUTION_INT:
165                     ctx = new ExpressionContext(
166                             PointcutType.EXECUTION,
167                             calleeClassInfo.getMethod(joinPoint.getJoinPointHash()),
168                             callerMethodInfo
169                     );
170                     break;
171                 case JoinPointType.METHOD_CALL_INT:
172                     ctx = new ExpressionContext(
173                             PointcutType.CALL,
174                             calleeClassInfo.getMethod(joinPoint.getJoinPointHash()),
175                             callerMethodInfo
176                     );
177                     break;
178                 case JoinPointType.CONSTRUCTOR_EXECUTION_INT:
179                     ctx = new ExpressionContext(
180                             PointcutType.EXECUTION,
181                             calleeClassInfo.getConstructor(joinPoint.getJoinPointHash()),
182                             callerMethodInfo
183                     );
184                     break;
185                 case JoinPointType.CONSTRUCTOR_CALL_INT:
186                     ctx = new ExpressionContext(
187                             PointcutType.CALL,
188                             calleeClassInfo.getConstructor(joinPoint.getJoinPointHash()),
189                             callerMethodInfo
190                     );
191                     break;
192                 case JoinPointType.FIELD_SET_INT:
193                     ctx = new ExpressionContext(
194                             PointcutType.SET,
195                             calleeClassInfo.getField(joinPoint.getJoinPointHash()),
196                             callerMethodInfo
197                     );
198                     break;
199                 case JoinPointType.FIELD_GET_INT:
200                     ctx = new ExpressionContext(
201                             PointcutType.GET,
202                             calleeClassInfo.getField(joinPoint.getJoinPointHash()),
203                             callerMethodInfo
204                     );
205                     break;
206                 case JoinPointType.HANDLER_INT:
207                     ctx = new ExpressionContext(
208                             PointcutType.HANDLER,
209                             AsmClassInfo.getClassInfo(joinPoint.getCalleeClassName(), loader),
210                             callerMethodInfo
211                     );
212                     break;
213                 case JoinPointType.STATIC_INITIALIZATION_INT:
214                 	ctx = new ExpressionContext(
215                 	        PointcutType.STATIC_INITIALIZATION,
216                 	        calleeClassInfo.staticInitializer(),
217                 	        calleeClassInfo
218                 	);
219             }
220             if (expression.getExpression().match(ctx)) {
221                 matchingJoinPointInfos.add(new MatchingJoinPointInfo(clazz, compilationInfo, ctx));
222             }
223         }
224         return matchingJoinPointInfos;
225     }
226 
227     /***
228      * Returns the emitted join point structure for a specific JIT generated join point class.
229      *
230      * @param clazz the join point class
231      * @return the emitted join point structure
232      */
233     public static EmittedJoinPoint getEmittedJoinPoint(final Class clazz) {
234         return (EmittedJoinPoint) COMPILATION_INFO_REPOSITORY.get(clazz);
235     }
236 
237     /***
238      * Grabs the caller method info.
239      *
240      * @param callerClassInfo
241      * @param emittedJoinPoint
242      * @return
243      */
244     private static MethodInfo getCallerMethodInfo(final ClassInfo callerClassInfo,
245                                                   final EmittedJoinPoint emittedJoinPoint) {
246         MethodInfo callerMethodInfo = null;
247         MethodInfo[] callerMethods = callerClassInfo.getMethods();
248         for (int i = 0; i < callerMethods.length; i++) {
249             MethodInfo method = callerMethods[i];
250             if (method.getName().equals(emittedJoinPoint.getCallerMethodName()) &&
251                 method.getSignature().equals(emittedJoinPoint.getCallerMethodDesc())) {
252                 callerMethodInfo = method;
253                 break;
254             }
255         }
256         return callerMethodInfo;
257     }
258 }