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.lang.reflect.Modifier;
    11  import java.util.Iterator;
    12  import java.util.Set;
    13
    14  import org.objectweb.asm.*;
    15  import org.codehaus.aspectwerkz.transform.Context;
    16  import org.codehaus.aspectwerkz.transform.TransformationUtil;
    17  import org.codehaus.aspectwerkz.transform.TransformationConstants;
    18  import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
    19  import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
    20  import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
    21  import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
    22  import org.codehaus.aspectwerkz.definition.SystemDefinition;
    23  import org.codehaus.aspectwerkz.expression.ExpressionContext;
    24  import org.codehaus.aspectwerkz.expression.PointcutType;
    25  import org.codehaus.aspectwerkz.reflect.ClassInfo;
    26  import org.codehaus.aspectwerkz.reflect.MethodInfo;
    27
    28  /***
    29   * Adds a "proxy method" to the methods that matches an <tt>execution</tt> pointcut as well as prefixing the "original
    30   * method".
    31   * <br/>
    32   * The proxy method calls the JPClass.invoke static method. The signature of the invoke method depends if the
    33   * target method is static or not as follow:
    34   * <pre>
    35   *      invoke(callee, args.., caller) // non static
    36   *      invoke(args.., caller) // static
    37   * </pre>
    38   * (The reason why is that it simplifies call pointcut stack management)
    39   *
    40   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    41   * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
    42   */
    43  public class MethodExecutionVisitor extends ClassAdapter implements TransformationConstants {
    44
    45      private final ClassInfo m_classInfo;
    46      private final ContextImpl m_ctx;
    47      private String m_declaringTypeName;
    48      private final Set m_addedMethods;
    49
    50      /***
    51       * Creates a new class adapter.
    52       *
    53       * @param cv
    54       * @param classInfo
    55       * @param ctx
    56       * @param addedMethods
    57       */
    58      public MethodExecutionVisitor(final ClassVisitor cv,
    59                                    final ClassInfo classInfo,
    60                                    final Context ctx,
    61                                    final Set addedMethods) {
    62          super(cv);
    63          m_classInfo = classInfo;
    64          m_ctx = (ContextImpl) ctx;
    65          m_addedMethods = addedMethods;
    66      }
    67
    68      /***
    69       * Visits the class.
    70       *
    71       * @param access
    72       * @param name
    73       * @param superName
    74       * @param interfaces
    75       * @param sourceFile
    76       */
    77      public void visit(final int version,
    78                        final int access,
    79                        final String name,
    80                        final String superName,
    81                        final String[] interfaces,
    82                        final String sourceFile) {
    83          m_declaringTypeName = name;
    84          super.visit(version, access, name, superName, interfaces, sourceFile);
    85      }
    86
    87      /***
    88       * Visits the methods.
    89       *
    90       * @param access
    91       * @param name
    92       * @param desc
    93       * @param exceptions
    94       * @param attrs
    95       * @return
    96       */
    97      public CodeVisitor visitMethod(final int access,
    98                                     final String name,
    99                                     final String desc,
    100                                    final String[] exceptions,
    101                                    final Attribute attrs) {
    102
    103         if (INIT_METHOD_NAME.equals(name) ||
    104             CLINIT_METHOD_NAME.equals(name) ||
    105             name.startsWith(ASPECTWERKZ_PREFIX) ||
    106             name.startsWith(SYNTHETIC_MEMBER_PREFIX) ||
    107             name.startsWith(WRAPPER_METHOD_PREFIX)) {
    108             return cv.visitMethod(access, name, desc, exceptions, attrs);
    109         }
    110
    111         int hash = AsmHelper.calculateMethodHash(name, desc);
    112         MethodInfo methodInfo = m_classInfo.getMethod(hash);
    113         if (methodInfo == null) {
    114             System.err.println(
    115                     "AW::WARNING " +
    116                     "metadata structure could not be build for method ["
    117                     + m_classInfo.getName().replace('/', '.')
    118                     + '.' + name + ':' + desc + ']'
    119             );
    120             // bail out
    121             return cv.visitMethod(access, name, desc, exceptions, attrs);
    122         }
    123
    124         ExpressionContext ctx = new ExpressionContext(PointcutType.EXECUTION, methodInfo, methodInfo);
    125
    126         if (methodFilter(m_ctx.getDefinitions(), ctx, methodInfo)) {
    127             return cv.visitMethod(access, name, desc, exceptions, attrs);
    128         } else {
    129             String prefixedOriginalName = TransformationUtil.getPrefixedOriginalMethodName(name, m_declaringTypeName);
    130             if (m_addedMethods.contains(AlreadyAddedMethodAdapter.getMethodKey(prefixedOriginalName, desc))) {
    131                 return cv.visitMethod(access, name, desc, exceptions, attrs);
    132             }
    133
    134             m_ctx.markAsAdvised();
    135
    136             // create the proxy for the original method
    137             createProxyMethod(access, name, desc, exceptions, attrs, methodInfo);
    138
    139             int modifiers = ACC_SYNTHETIC;
    140             if (Modifier.isStatic(access)) {
    141                 modifiers |= ACC_STATIC;
    142             }
    143             // prefix the original method
    144             return cv.visitMethod(
    145                     modifiers,
    146                     prefixedOriginalName,
    147                     desc, exceptions, attrs
    148             );
    149         }
    150     }
    151
    152     /***
    153      * Creates the "proxy method", e.g. the method that has the same name and signature as the original method but a
    154      * completely other implementation.
    155      *
    156      * @param access
    157      * @param name
    158      * @param desc
    159      * @param exceptions
    160      * @param attrs
    161      */
    162     private void createProxyMethod(final int access,
    163                                    final String name,
    164                                    final String desc,
    165                                    final String[] exceptions,
    166                                    final Attribute attrs,
    167                                    final MethodInfo methodInfo) {
    168         CodeVisitor mv = cv.visitMethod(access, name, desc, exceptions, attrs);
    169
    170         // load "this" ie callee if target method is not static
    171         if (!Modifier.isStatic(access)) {
    172             mv.visitVarInsn(ALOAD, 0);
    173         }
    174         // load args
    175         AsmHelper.loadArgumentTypes(mv, Type.getArgumentTypes(desc), Modifier.isStatic(access));
    176         // load "this" ie caller or null if method is static
    177         if (Modifier.isStatic(access)) {
    178             mv.visitInsn(ACONST_NULL);
    179         } else {
    180             mv.visitVarInsn(ALOAD, 0);
    181         }
    182
    183         int joinPointHash = AsmHelper.calculateMethodHash(name, desc);
    184         String joinPointClassName = TransformationUtil.getJoinPointClassName(
    185                 m_declaringTypeName,
    186                 name,
    187                 desc,
    188                 m_declaringTypeName,
    189                 JoinPointType.METHOD_EXECUTION_INT,
    190                 joinPointHash
    191         );
    192
    193         // TODO: should we provide some sort of option to do JITgen when weaving instead of when loading ?
    194         // use case: offline full packaging and alike
    195
    196         mv.visitMethodInsn(
    197                 INVOKESTATIC,
    198                 joinPointClassName,
    199                 INVOKE_METHOD_NAME,
    200                 TransformationUtil.getInvokeSignatureForCodeJoinPoints(
    201                         access, desc, m_declaringTypeName, m_declaringTypeName
    202                 )
    203         );
    204
    205         AsmHelper.addReturnStatement(mv, Type.getReturnType(desc));
    206         mv.visitMaxs(0, 0);
    207
    208         // emit the joinpoint
    209         m_ctx.addEmittedJoinPoint(
    210                 new EmittedJoinPoint(
    211                         JoinPointType.METHOD_EXECUTION_INT,
    212                         m_declaringTypeName,
    213                         name,
    214                         desc,
    215                         access,
    216                         m_declaringTypeName,
    217                         name,
    218                         desc,
    219                         access,
    220                         joinPointHash,
    221                         joinPointClassName,
    222                         EmittedJoinPoint.NO_LINE_NUMBER
    223                 )
    224         );
    225     }
    226
    227     /***
    228      * Filters out the methods that are not eligible for transformation.
    229      *
    230      * @param definitions
    231      * @param ctx
    232      * @param methodInfo
    233      * @return boolean true if the method should be filtered out
    234      */
    235     public static boolean methodFilter(final Set definitions,
    236                                        final ExpressionContext ctx,
    237                                        final MethodInfo methodInfo) {
    238         if (Modifier.isAbstract(methodInfo.getModifiers())
    239             || Modifier.isNative(methodInfo.getModifiers())
    240             || methodInfo.getName().equals(INIT_METHOD_NAME)
    241             || methodInfo.getName().equals(CLINIT_METHOD_NAME)
    242             || methodInfo.getName().startsWith(ORIGINAL_METHOD_PREFIX)) {
    243             return true;
    244         }
    245         for (Iterator it = definitions.iterator(); it.hasNext();) {
    246             if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
    247                 return false;
    248             } else {
    249                 continue;
    250             }
    251         }
    252         return true;
    253     }
    254 }