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.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         // check if the JP is already loaded
103         // this can occurs if user packaged its JIT classes, or if we are using multiweaving
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         // create the callee info
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         // create the caller info
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         // get the compilation model
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                 //TODO - do we care about non bounded pointcut ?
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                         // compute the target method to advice method arguments map, and grab information about this
302                         // and target bindings
303                         expressionContext.resetRuntimeState();
304                         ArgsIndexVisitor.updateContextForRuntimeInformation(
305                                 expressionInfo,
306                                 expressionContext,
307                                 loader
308                         );
309                         // Note that the expressionContext dynamic information updated here should only be used
310                         // in the scope of this code block, since at the next iteration, the data will be
311                         // updated for another advice binding
312                         // [hence see setMethodArgumentIndexes below]
313 
314                         // create a lightweight representation of the bounded advices to pass to the compiler
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);//special case for "after only"
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                 // handles XML def where name is optional
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         // grab the parameters names
409         String[] adviceArgNames = getParameterNames(adviceInfo.getName());
410 
411         // map them from the ctx info
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                 // does not appears to be an argument of the advised target
420                 // It can be StaticJP / JP / This binding / Target binding
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         // support for old style advices in XML whose name does not contain the call signature
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 }