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 }