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.joinpoint.management;
9
10 import java.util.ArrayList;
11 import java.util.Collection;
12 import java.util.Iterator;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Set;
16 import java.util.WeakHashMap;
17
18 import org.codehaus.aspectwerkz.aspect.AdviceInfo;
19 import org.codehaus.aspectwerkz.aspect.AdviceType;
20 import org.codehaus.aspectwerkz.definition.AdviceDefinition;
21 import org.codehaus.aspectwerkz.definition.AspectDefinition;
22 import org.codehaus.aspectwerkz.definition.SystemDefinition;
23 import org.codehaus.aspectwerkz.definition.SystemDefinitionContainer;
24 import org.codehaus.aspectwerkz.definition.Virtual;
25 import org.codehaus.aspectwerkz.expression.ArgsIndexVisitor;
26 import org.codehaus.aspectwerkz.expression.ExpressionContext;
27 import org.codehaus.aspectwerkz.expression.ExpressionInfo;
28 import org.codehaus.aspectwerkz.expression.PointcutType;
29 import org.codehaus.aspectwerkz.joinpoint.ConstructorSignature;
30 import org.codehaus.aspectwerkz.joinpoint.EnclosingStaticJoinPoint;
31 import org.codehaus.aspectwerkz.joinpoint.JoinPoint;
32 import org.codehaus.aspectwerkz.joinpoint.MethodSignature;
33 import org.codehaus.aspectwerkz.joinpoint.Signature;
34 import org.codehaus.aspectwerkz.joinpoint.StaticJoinPoint;
35 import org.codehaus.aspectwerkz.joinpoint.impl.EnclosingStaticJoinPointImpl;
36 import org.codehaus.aspectwerkz.reflect.ClassInfo;
37 import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
38 import org.codehaus.aspectwerkz.reflect.MethodInfo;
39 import org.codehaus.aspectwerkz.reflect.ReflectionInfo;
40 import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
41 import org.codehaus.aspectwerkz.reflect.impl.java.JavaClassInfo;
42 import org.codehaus.aspectwerkz.transform.TransformationConstants;
43 import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
44 import org.codehaus.aspectwerkz.transform.inlining.AspectModelManager;
45 import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
46 import org.codehaus.aspectwerkz.transform.inlining.compiler.CompilationInfo;
47 import org.codehaus.aspectwerkz.transform.inlining.compiler.JoinPointFactory;
48 import org.codehaus.aspectwerkz.transform.inlining.spi.AspectModel;
49 import org.codehaus.aspectwerkz.util.ContextClassLoader;
50 import org.codehaus.aspectwerkz.util.Strings;
51 import org.objectweb.asm.Type;
52
53 /***
54 * Manages the join point compilation, loading and instantiation for the target classes.
55 * This implementation relies on the SystemDefinitionContainer.
56 *
57 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
58 * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
59 * @author <a href="mailto:the_mindstorm@evolva.ro">Alex Popescu</a>
60 */
61 public class JoinPointManager {
62 /***
63 * Ensures that the specific joinPoint class for the given target class and joinPoint info is generated. This call
64 * is added to the weaved class as a "clinit" block
65 *
66 * @param joinPointType
67 * @param callerClass
68 * @param callerMethodName
69 * @param callerMethodDesc
70 * @param callerMethodModifiers
71 * @param calleeClassName
72 * @param calleeMemberName
73 * @param calleeMemberDesc
74 * @param calleeMemberModifiers
75 * @param joinPointHash
76 * @param joinPointClassName
77 */
78 public static void loadJoinPoint(final int joinPointType,
79 final Class callerClass,
80 final String callerMethodName,
81 final String callerMethodDesc,
82 final int callerMethodModifiers,
83 final String calleeClassName,
84 final String calleeMemberName,
85 final String calleeMemberDesc,
86 final int calleeMemberModifiers,
87 final int joinPointHash,
88 final String joinPointClassName) {
89 Class calleeClass = null;
90 try {
91 if (calleeClassName != null) {
92 calleeClass = Class.forName(calleeClassName.replace('/', '.'), false, callerClass.getClassLoader());
93 }
94 } catch (ClassNotFoundException calleeNotFound) {
95 throw new RuntimeException(
96 "callee class [" + calleeClassName + "] can not be found in class loader [" +
97 callerClass.getClassLoader() +
98 "]"
99 );
100 }
101
102
103
104 final ClassLoader classLoader = callerClass.getClassLoader();
105 boolean generateJoinPoint = false;
106 try {
107 if (calleeClass == null) {
108 throw new RuntimeException("callee class [" + calleeClassName + "] is NULL");
109 }
110 ContextClassLoader.forName(classLoader, joinPointClassName.replace('/', '.'));
111 } catch (ClassNotFoundException e) {
112 generateJoinPoint = true;
113 }
114 if (!generateJoinPoint) {
115 return;
116 }
117
118 final CompiledJoinPoint compiledJoinPoint = compileJoinPoint(
119 joinPointType,
120 callerClass,
121 callerMethodName,
122 callerMethodDesc,
123 callerMethodModifiers,
124 calleeClassName,
125 calleeMemberName,
126 calleeMemberDesc,
127 calleeMemberModifiers,
128 joinPointHash,
129 joinPointClassName,
130 calleeClass,
131 classLoader
132 );
133
134 Class jpClass = JoinPointFactory.attachToClassLoader(
135 joinPointClassName, classLoader, compiledJoinPoint.bytecode
136 );
137 JoinPointFactory.addCompilationInfo(jpClass, compiledJoinPoint.compilationInfo);
138 }
139
140 /***
141 * Compile a new joinpoint
142 *
143 * @param joinPointType
144 * @param callerClass
145 * @param callerMethodName
146 * @param callerMethodDesc
147 * @param callerMethodModifiers
148 * @param calleeClassName
149 * @param calleeMemberName
150 * @param calleeMemberDesc
151 * @param calleeMemberModifiers
152 * @param joinPointHash
153 * @param joinPointClassName
154 * @param calleeClass
155 * @param loader the loader that hosts the definitions, and from where caller, callee and aspect are visible.
156 * At runtime it is exactly callerClass.getClassLoader() but in offline mode and genjp, it can happen to be
157 * different when weaved class also exists in the compilation classpath.
158 * @return
159 */
160 public static CompiledJoinPoint compileJoinPoint(final int joinPointType,
161 final Class callerClass,
162 final String callerMethodName,
163 final String callerMethodDesc,
164 final int callerMethodModifiers,
165 final String calleeClassName,
166 final String calleeMemberName,
167 final String calleeMemberDesc,
168 final int calleeMemberModifiers,
169 final int joinPointHash,
170 final String joinPointClassName,
171 final Class calleeClass,
172 final ClassLoader loader) {
173
174 ClassInfo calleeClassInfo = JavaClassInfo.getClassInfo(calleeClass);
175
176
177 final ReflectionInfo reflectionInfo;
178 final PointcutType pointcutType;
179 switch (joinPointType) {
180 case JoinPointType.STATIC_INITIALIZATION_INT:
181 reflectionInfo = calleeClassInfo.staticInitializer();
182 pointcutType = PointcutType.STATIC_INITIALIZATION;
183 break;
184 case JoinPointType.METHOD_EXECUTION_INT:
185 reflectionInfo = calleeClassInfo.getMethod(joinPointHash);
186 pointcutType = PointcutType.EXECUTION;
187 break;
188 case JoinPointType.METHOD_CALL_INT:
189 reflectionInfo = calleeClassInfo.getMethod(joinPointHash);
190 pointcutType = PointcutType.CALL;
191 break;
192 case JoinPointType.FIELD_GET_INT:
193 reflectionInfo = calleeClassInfo.getField(joinPointHash);
194 pointcutType = PointcutType.GET;
195 break;
196 case JoinPointType.FIELD_SET_INT:
197 reflectionInfo = calleeClassInfo.getField(joinPointHash);
198 pointcutType = PointcutType.SET;
199 break;
200 case JoinPointType.CONSTRUCTOR_EXECUTION_INT:
201 reflectionInfo = calleeClassInfo.getConstructor(joinPointHash);
202 pointcutType = PointcutType.EXECUTION;
203 break;
204 case JoinPointType.CONSTRUCTOR_CALL_INT:
205 reflectionInfo = calleeClassInfo.getConstructor(joinPointHash);
206 pointcutType = PointcutType.CALL;
207 break;
208 case JoinPointType.HANDLER_INT:
209 reflectionInfo = calleeClassInfo;
210 pointcutType = PointcutType.HANDLER;
211 break;
212 default:
213 throw new RuntimeException("Joinpoint type not supported: " + joinPointType);
214 }
215
216
217 final ClassInfo callerClassInfo = JavaClassInfo.getClassInfo(callerClass);
218 final ReflectionInfo withinInfo;
219 if (TransformationConstants.CLINIT_METHOD_NAME.equals(callerMethodName)) {
220 withinInfo = callerClassInfo.staticInitializer();
221 } else if (TransformationConstants.INIT_METHOD_NAME.equals(callerMethodName)) {
222 withinInfo = callerClassInfo.getConstructor(AsmHelper.calculateConstructorHash(callerMethodDesc));
223 } else {
224 withinInfo =
225 callerClassInfo.getMethod(AsmHelper.calculateMethodHash(callerMethodName, callerMethodDesc));
226 }
227
228
229 final ExpressionContext ctx = new ExpressionContext(pointcutType, reflectionInfo, withinInfo);
230 final AdviceInfoContainer adviceContainer = getAdviceInfoContainerForJoinPoint(
231 ctx, loader
232 );
233 final EmittedJoinPoint emittedJoinPoint = new EmittedJoinPoint(
234 joinPointType,
235 callerClass.getName(),
236 callerMethodName,
237 callerMethodDesc,
238 callerMethodModifiers,
239 calleeClass.getName(),
240 calleeMemberName,
241 calleeMemberDesc,
242 calleeMemberModifiers,
243 joinPointHash,
244 joinPointClassName,
245 EmittedJoinPoint.NO_LINE_NUMBER
246 );
247 final CompilationInfo.Model compilationModel = new CompilationInfo.Model(
248 emittedJoinPoint, adviceContainer, callerClassInfo
249 );
250
251 return new CompiledJoinPoint(compilationModel);
252 }
253
254 /***
255 * A compiled joinpoint is tied to a compilation model at a given time
256 */
257 public static class CompiledJoinPoint {
258 public byte[] bytecode;
259 public CompilationInfo compilationInfo;
260
261 public CompiledJoinPoint(CompilationInfo.Model model) {
262 bytecode = JoinPointFactory.compileJoinPoint(model);
263 compilationInfo = new CompilationInfo(model);
264 }
265 }
266
267 /***
268 * Retrieves the advice info wrapped up in a struct.
269 *
270 * @param expressionContext
271 * @param loader
272 * @return the advice info
273 */
274 public static AdviceInfoContainer getAdviceInfoContainerForJoinPoint(final ExpressionContext expressionContext,
275 final ClassLoader loader) {
276 final List beforeAdvices = new ArrayList();
277 final List aroundAdvices = new ArrayList();
278 final List afterFinallyAdvices = new ArrayList();
279 final List afterReturningAdvices = new ArrayList();
280 final List afterThrowingAdvices = new ArrayList();
281
282 final Set systemDefinitions = SystemDefinitionContainer.getDefinitionsFor(loader);
283
284 for (Iterator iterator = systemDefinitions.iterator(); iterator.hasNext();) {
285 SystemDefinition systemDefinition = (SystemDefinition) iterator.next();
286 Collection aspects = systemDefinition.getAspectDefinitions();
287 for (Iterator iterator1 = aspects.iterator(); iterator1.hasNext();) {
288 AspectDefinition aspectDefinition = (AspectDefinition) iterator1.next();
289 if (aspectDefinition.getName().equals(Virtual.class.getName())) {
290 continue;
291 }
292
293
294 for (Iterator iterator2 = aspectDefinition.getAdviceDefinitions().iterator(); iterator2.hasNext();) {
295 AdviceDefinition adviceDefinition = (AdviceDefinition) iterator2.next();
296 final ExpressionInfo expressionInfo = adviceDefinition.getExpressionInfo();
297 if (expressionInfo == null) {
298 continue;
299 }
300 if (expressionInfo.getExpression().match(expressionContext)) {
301
302
303 expressionContext.resetRuntimeState();
304 ArgsIndexVisitor.updateContextForRuntimeInformation(
305 expressionInfo,
306 expressionContext,
307 loader
308 );
309
310
311
312
313
314
315 final MethodInfo adviceMethodInfo = adviceDefinition.getMethodInfo();
316 final AdviceInfo adviceInfo = new AdviceInfo(
317 aspectDefinition.getQualifiedName(),
318 aspectDefinition.getClassName(),
319 aspectDefinition.getDeploymentModel(),
320 adviceMethodInfo.getName(),
321 AsmHelper.getMethodDescriptor(adviceMethodInfo),
322 AsmHelper.getArgumentTypes(adviceMethodInfo),
323 adviceDefinition.getType(),
324 adviceDefinition.getSpecialArgumentType(),
325 adviceDefinition.getName(),
326 expressionContext.m_targetWithRuntimeCheck,
327 expressionInfo,
328 expressionContext,
329 adviceDefinition
330 );
331
332 setMethodArgumentIndexes(expressionInfo, expressionContext, adviceInfo, loader);
333
334 if (AdviceType.BEFORE.equals(adviceDefinition.getType())) {
335 beforeAdvices.add(adviceInfo);
336 } else if (AdviceType.AROUND.equals(adviceDefinition.getType())) {
337 aroundAdvices.add(adviceInfo);
338 } else if (AdviceType.AFTER_FINALLY.equals(adviceDefinition.getType())) {
339 afterFinallyAdvices.add(adviceInfo);
340 } else if (AdviceType.AFTER_RETURNING.equals(adviceDefinition.getType())) {
341 afterReturningAdvices.add(adviceInfo);
342 } else if (AdviceType.AFTER_THROWING.equals(adviceDefinition.getType())) {
343 afterThrowingAdvices.add(adviceInfo);
344 } else if (AdviceType.AFTER.equals(adviceDefinition.getType())) {
345 afterFinallyAdvices.add(adviceInfo);
346 }
347 }
348 }
349 }
350 }
351
352 final AdviceInfoContainer adviceInfoContainer = new AdviceInfoContainer(
353 aroundAdvices,
354 beforeAdvices,
355 afterFinallyAdvices,
356 afterReturningAdvices,
357 afterThrowingAdvices
358 );
359 return adviceInfoContainer;
360 }
361
362 /***
363 * Get the parameter names from a "method declaration" signature like pc(type a, type2 b) => 0:a, 1:b
364 *
365 * @param adviceName
366 * @return the parameter names
367 */
368 public static String[] getParameterNames(final String adviceName) {
369 int paren = adviceName.indexOf('(');
370 List paramNames = new ArrayList();
371 if (paren > 0) {
372 String params = adviceName.substring(paren + 1, adviceName.lastIndexOf(')')).trim();
373 String[] javaParameters = Strings.splitString(params, ",");
374 for (int i = 0; i < javaParameters.length; i++) {
375 String javaParameter = Strings.replaceSubString(javaParameters[i], " ", " ").trim();
376 String[] paramInfo = Strings.splitString(javaParameter, " ");
377
378 if (paramInfo.length == 2) {
379 paramNames.add(paramInfo[1]);
380 } else {
381 paramNames.add("anonymous_" + i);
382 }
383 }
384 }
385 String[] paramNamesArray = new String[paramNames.size()];
386 int index = 0;
387 for (Iterator it = paramNames.iterator(); it.hasNext(); index++) {
388 paramNamesArray[index] = (String) it.next();
389 }
390 return paramNamesArray;
391 }
392
393 /***
394 * Sets the advice argument indexes map.
395 * <p/>
396 * Each advice arg is mapped to wether a system entity like StaticJoinPoint, Rtti, this, target etc thru a specific index
397 * (see AdviceInfo), or to one of the advised member arguments (thru args(..) binding).
398 *
399 * @param expressionInfo
400 * @param ctx
401 * @param adviceInfo
402 * @param loader
403 */
404 private static void setMethodArgumentIndexes(final ExpressionInfo expressionInfo,
405 final ExpressionContext ctx,
406 final AdviceInfo adviceInfo,
407 final ClassLoader loader) {
408
409 String[] adviceArgNames = getParameterNames(adviceInfo.getName());
410
411
412 int[] adviceToTargetArgs = new int[adviceInfo.getMethodParameterTypes().length];
413 for (int k = 0; k < adviceArgNames.length; k++) {
414 String adviceArgName = adviceArgNames[k];
415 int exprArgIndex = expressionInfo.getArgumentIndex(adviceArgName);
416 if (exprArgIndex >= 0 && ctx.m_exprIndexToTargetIndex.containsKey(adviceArgName)) {
417 adviceToTargetArgs[k] = ctx.m_exprIndexToTargetIndex.get(adviceArgName);
418 } else {
419
420
421 final Type type = adviceInfo.getMethodParameterTypes()[k];
422 if (isJoinPoint(type)) {
423 adviceToTargetArgs[k] = AdviceInfo.JOINPOINT_ARG;
424 } else if (isStaticJoinPoint(type)) {
425 adviceToTargetArgs[k] = AdviceInfo.STATIC_JOINPOINT_ARG;
426 } else if (isTarget(adviceArgName, ctx)) {
427 adviceToTargetArgs[k] = AdviceInfo.TARGET_ARG;
428 } else if (isThis(adviceArgName, ctx)) {
429 adviceToTargetArgs[k] = AdviceInfo.THIS_ARG;
430 } else if (isSpecialArgument(adviceArgName, expressionInfo)) {
431 adviceToTargetArgs[k] = AdviceInfo.SPECIAL_ARGUMENT;
432 } else if (isCustomJointPoint(type, loader)) {
433 adviceToTargetArgs[k] = AdviceInfo.CUSTOM_JOIN_POINT_ARG;
434 } else {
435 throw new Error(
436 "Unbound advice parameter at index " + k +
437 " in " + adviceInfo.getMethodName() +
438 adviceInfo.getMethodSignature() +
439 " named " +
440 adviceArgName
441 );
442 }
443 }
444 }
445
446
447 if (adviceArgNames.length == 0) {
448 AspectDefinition aspectDef = adviceInfo.getAdviceDefinition().getAspectDefinition();
449 Type[] adviceArgTypes = adviceInfo.getMethodParameterTypes();
450 for (int i = 0; i < adviceArgTypes.length; i++) {
451
452 if (aspectDef.isAspectWerkzAspect()) {
453 if (isJoinPoint(adviceArgTypes[i])) {
454 adviceToTargetArgs[i] = AdviceInfo.JOINPOINT_ARG;
455 } else if (isStaticJoinPoint(adviceArgTypes[i])) {
456 adviceToTargetArgs[i] = AdviceInfo.STATIC_JOINPOINT_ARG;
457 } else {
458 throw new Error(
459 "Unbound unnamed advice parameter at index " + i +
460 " in " + adviceInfo.getMethodSignature()
461 );
462 }
463 } else {
464 final AspectModel aspectModel = AspectModelManager.getModelFor(aspectDef.getAspectModel());
465 final String superClassName = aspectModel.getAroundClosureClassInfo().getSuperClassName();
466 final String[] interfaces = aspectModel.getAroundClosureClassInfo().getInterfaceNames();
467 final String[] classNames = new String[interfaces.length + 1];
468 classNames[0] = superClassName;
469 for (int j = 1; j < interfaces.length + 1; j++) {
470 classNames[j] = interfaces[j - 1];
471 }
472
473 final Type argType = adviceArgTypes[i];
474 if (isValidAroundClosureType(argType, classNames)) {
475 adviceToTargetArgs[i] = AdviceInfo.VALID_NON_AW_AROUND_CLOSURE_TYPE;
476 } else if (isSpecialArgumentType(argType, adviceInfo)) {
477 adviceToTargetArgs[i] = AdviceInfo.SPECIAL_ARGUMENT;
478 } else {
479 }
480 }
481 }
482 }
483
484 adviceInfo.setMethodToArgIndexes(adviceToTargetArgs);
485 }
486
487 private static boolean isSpecialArgumentType(final Type argType, final AdviceInfo adviceInfo) {
488 final String specialArgumentTypeDesc = adviceInfo.getSpecialArgumentTypeDesc();
489 return specialArgumentTypeDesc != null && specialArgumentTypeDesc.equals(argType.getDescriptor());
490 }
491
492 private static boolean isValidAroundClosureType(final Type argType, final String[] closureTypeNames) {
493 for (int i = 0; i < closureTypeNames.length; i++) {
494 final String closureTypeName = closureTypeNames[i];
495 if (closureTypeName != null && closureTypeName.equals(argType.getInternalName())) {
496 return true;
497 }
498 }
499 return false;
500 }
501
502 private static boolean isJoinPoint(final Type type) {
503 return Type.getType(JoinPoint.class).getDescriptor().equals(type.getDescriptor());
504 }
505
506 private static boolean isStaticJoinPoint(final Type type) {
507 return Type.getType(StaticJoinPoint.class).getDescriptor().equals(type.getDescriptor());
508 }
509
510 private static boolean isTarget(final String adviceArgName, final ExpressionContext ctx) {
511 return adviceArgName.equals(ctx.m_targetBoundedName);
512 }
513
514 private static boolean isThis(final String adviceArgName, final ExpressionContext ctx) {
515 return adviceArgName.equals(ctx.m_thisBoundedName);
516 }
517
518 private static boolean isSpecialArgument(final String adviceArgName, final ExpressionInfo expressionInfo) {
519 return adviceArgName.equals(expressionInfo.getSpecialArgumentName());
520 }
521
522 private static boolean isCustomJointPoint(final Type type, final ClassLoader loader) {
523 ClassInfo classInfo = AsmClassInfo.getClassInfo(type.getClassName(), loader);
524 return ClassInfoHelper.implementsInterface(classInfo, ExpressionInfo.JOINPOINT_CLASS_NAME) ||
525 ClassInfoHelper.implementsInterface(classInfo, ExpressionInfo.STATIC_JOINPOINT_CLASS_NAME);
526 }
527 }