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 org.objectweb.asm.ClassAdapter;
    11  import org.objectweb.asm.ClassVisitor;
    12  import org.objectweb.asm.CodeVisitor;
    13  import org.objectweb.asm.Attribute;
    14  import org.objectweb.asm.CodeAdapter;
    15  import org.objectweb.asm.Label;
    16  import org.codehaus.aspectwerkz.definition.SystemDefinition;
    17  import org.codehaus.aspectwerkz.expression.ExpressionContext;
    18  import org.codehaus.aspectwerkz.expression.PointcutType;
    19  import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
    20  import org.codehaus.aspectwerkz.reflect.ClassInfo;
    21  import org.codehaus.aspectwerkz.reflect.MethodInfo;
    22  import org.codehaus.aspectwerkz.reflect.MemberInfo;
    23  import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
    24  import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
    25  import org.codehaus.aspectwerkz.transform.Context;
    26  import org.codehaus.aspectwerkz.transform.TransformationUtil;
    27  import org.codehaus.aspectwerkz.transform.TransformationConstants;
    28  import org.codehaus.aspectwerkz.transform.inlining.compiler.AbstractJoinPointCompiler;
    29  import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
    30  import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
    31  import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
    32
    33  import java.lang.reflect.Modifier;
    34  import java.util.Iterator;
    35  import java.util.Set;
    36
    37  /***
    38   * Instruments method CALL join points by replacing INVOKEXXX instructions with invocations of the compiled join point.
    39   * <br/>
    40   * It calls the JPClass.invoke static method. The signature of the invoke method depends if the
    41   * target method is static or not as follow:
    42   * <pre>
    43   *      invoke(callee, args.., caller) // non static
    44   *      invoke(args.., caller) // static
    45   * </pre>
    46   * (The reason why is that it simplifies call pointcut stack management)
    47   *
    48   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    49   * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
    50   */
    51  public class MethodCallVisitor extends ClassAdapter implements TransformationConstants {
    52
    53      private final ContextImpl m_ctx;
    54      private final ClassLoader m_loader;
    55      private final ClassInfo m_callerClassInfo;
    56
    57      private Label m_lastLabelForLineNumber = EmittedJoinPoint.NO_LINE_NUMBER;
    58
    59      /***
    60       * Creates a new instance.
    61       *
    62       * @param cv
    63       * @param loader
    64       * @param classInfo
    65       * @param ctx
    66       */
    67      public MethodCallVisitor(final ClassVisitor cv,
    68                               final ClassLoader loader,
    69                               final ClassInfo classInfo,
    70                               final Context ctx) {
    71          super(cv);
    72          m_loader = loader;
    73          m_callerClassInfo = classInfo;
    74          m_ctx = (ContextImpl) ctx;
    75      }
    76
    77      /***
    78       * Visits the caller methods.
    79       *
    80       * @param access
    81       * @param name
    82       * @param desc
    83       * @param exceptions
    84       * @param attrs
    85       * @return
    86       */
    87      public CodeVisitor visitMethod(final int access,
    88                                     final String name,
    89                                     final String desc,
    90                                     final String[] exceptions,
    91                                     final Attribute attrs) {
    92
    93          if (name.startsWith(WRAPPER_METHOD_PREFIX) ||
    94              Modifier.isNative(access) ||
    95              Modifier.isAbstract(access)) {
    96              return super.visitMethod(access, name, desc, exceptions, attrs);
    97          }
    98
    99          CodeVisitor mv = cv.visitMethod(access, name, desc, exceptions, attrs);
    100         return mv == null ? null : new ReplaceInvokeInstructionCodeAdapter(
    101                 mv,
    102                 m_loader,
    103                 m_callerClassInfo,
    104                 m_ctx.getClassName(),
    105                 name,
    106                 desc
    107         );
    108     }
    109
    110     /***
    111      * Replaces 'INVOKEXXX' instructions with a call to the compiled JoinPoint instance.
    112      *
    113      * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
    114      * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
    115      */
    116     public class ReplaceInvokeInstructionCodeAdapter extends AfterObjectInitializationCodeAdapter {
    117
    118         private final ClassLoader m_loader;
    119         private final ClassInfo m_callerClassInfo;
    120         private final String m_callerClassName;
    121         private final String m_callerMethodName;
    122         private final String m_callerMethodDesc;
    123         private final MemberInfo m_callerMemberInfo;
    124
    125         /***
    126          * Creates a new instance.
    127          *
    128          * @param ca
    129          * @param loader
    130          * @param callerClassInfo
    131          * @param callerClassName
    132          * @param callerMethodName
    133          * @param callerMethodDesc
    134          */
    135         public ReplaceInvokeInstructionCodeAdapter(final CodeVisitor ca,
    136                                                    final ClassLoader loader,
    137                                                    final ClassInfo callerClassInfo,
    138                                                    final String callerClassName,
    139                                                    final String callerMethodName,
    140                                                    final String callerMethodDesc) {
    141             super(ca, callerMethodName);
    142             m_loader = loader;
    143             m_callerClassInfo = callerClassInfo;
    144             m_callerClassName = callerClassName;
    145             m_callerMethodName = callerMethodName;
    146             m_callerMethodDesc = callerMethodDesc;
    147
    148             if (CLINIT_METHOD_NAME.equals(callerMethodName)) {
    149                 m_callerMemberInfo = m_callerClassInfo.staticInitializer();
    150             } else if (INIT_METHOD_NAME.equals(callerMethodName)) {
    151                 int hash = AsmHelper.calculateConstructorHash(m_callerMethodDesc);
    152                 m_callerMemberInfo = m_callerClassInfo.getConstructor(hash);
    153             } else {
    154                 int hash = AsmHelper.calculateMethodHash(m_callerMethodName, m_callerMethodDesc);
    155                 m_callerMemberInfo = m_callerClassInfo.getMethod(hash);
    156             }
    157         }
    158
    159         /***
    160          * Label
    161          *
    162          * @param label
    163          */
    164         public void visitLabel(Label label) {
    165             m_lastLabelForLineNumber = label;
    166             super.visitLabel(label);
    167         }
    168
    169         /***
    170          * Visits 'INVOKEXXX' instructions.
    171          *
    172          * @param opcode
    173          * @param calleeClassName
    174          * @param calleeMethodName
    175          * @param calleeMethodDesc
    176          */
    177         public void visitMethodInsn(final int opcode,
    178                                     String calleeClassName,
    179                                     final String calleeMethodName,
    180                                     final String calleeMethodDesc) {
    181
    182             if (m_callerMemberInfo == null) {
    183                 System.err.println(
    184                         "AW::WARNING " +
    185                         "metadata structure could not be build for method ["
    186                         + m_callerClassInfo.getName().replace('/', '.')
    187                         + '.' + m_callerMethodName + ':' + m_callerMethodDesc + ']'
    188                 );
    189                 super.visitMethodInsn(opcode, calleeClassName, calleeMethodName, calleeMethodDesc);
    190                 return;
    191             }
    192
    193             if (INIT_METHOD_NAME.equals(calleeMethodName) ||
    194                 CLINIT_METHOD_NAME.equals(calleeMethodName) ||
    195                 calleeMethodName.startsWith(ASPECTWERKZ_PREFIX)
    196                 || calleeClassName.endsWith(JOIN_POINT_CLASS_SUFFIX)
    197                 //calleeClassName.startsWith("org/aopalliance/")) { // FIXME make generic fix by invoking all AspectModels (same problem in other visitors as well)
    198                     ){super.visitMethodInsn(opcode, calleeClassName, calleeMethodName, calleeMethodDesc);
    199                 return;
    200             }
    201
    202             // check if we have a super.sameMethod() call
    203             if (opcode == INVOKESPECIAL
    204                 && !calleeClassName.equals(m_callerClassName)
    205                 && ClassInfoHelper.extendsSuperClass(m_callerClassInfo, calleeClassName.replace('/', '.'))) {
    206                 super.visitMethodInsn(opcode, calleeClassName, calleeMethodName, calleeMethodDesc);
    207                 return;
    208             }
    209
    210             // check if object initialization has been reached
    211             if (!m_isObjectInitialized) {
    212                 super.visitMethodInsn(opcode, calleeClassName, calleeMethodName, calleeMethodDesc);
    213                 return;
    214             }
    215
    216             int joinPointHash = AsmHelper.calculateMethodHash(calleeMethodName, calleeMethodDesc);
    217
    218             ClassInfo classInfo = AsmClassInfo.getClassInfo(calleeClassName, m_loader);
    219             MethodInfo calleeMethodInfo = classInfo.getMethod(joinPointHash);
    220
    221             if (calleeMethodInfo == null) {
    222                 System.err.println(
    223                         "AW::WARNING " +
    224                         "metadata structure could not be build for method ["
    225                         + classInfo.getName().replace('/', '.')
    226                         + '.' + calleeMethodName + ':' + calleeMethodDesc
    227                         + "] when parsing method ["
    228                         + m_callerClassInfo.getName() + '.' + m_callerMethodName + "(..)]"
    229                 );
    230                 // bail out
    231                 super.visitMethodInsn(opcode, calleeClassName, calleeMethodName, calleeMethodDesc);
    232                 return;
    233             }
    234
    235             ExpressionContext ctx = new ExpressionContext(PointcutType.CALL, calleeMethodInfo, m_callerMemberInfo);
    236
    237             if (methodFilter(m_ctx.getDefinitions(), ctx, calleeMethodInfo)) {
    238                 super.visitMethodInsn(opcode, calleeClassName, calleeMethodName, calleeMethodDesc);
    239             } else {
    240                 m_ctx.markAsAdvised();
    241
    242                 String joinPointClassName = TransformationUtil.getJoinPointClassName(
    243                         m_callerClassName,
    244                         m_callerMethodName,
    245                         m_callerMethodDesc,
    246                         calleeClassName,
    247                         JoinPointType.METHOD_CALL_INT,
    248                         joinPointHash
    249                 );
    250
    251                 // load the caller instance (this), or null if in a static context
    252                 // note that callee instance [optional] and args are already on the stack
    253                 if (Modifier.isStatic(m_callerMemberInfo.getModifiers())) {
    254                     visitInsn(ACONST_NULL);
    255                 } else {
    256                     visitVarInsn(ALOAD, 0);
    257                 }
    258
    259                 // add the call to the join point
    260                 super.visitMethodInsn(
    261                         INVOKESTATIC,
    262                         joinPointClassName,
    263                         INVOKE_METHOD_NAME,
    264                         TransformationUtil.getInvokeSignatureForCodeJoinPoints(
    265                                 calleeMethodInfo.getModifiers(), calleeMethodDesc,
    266                                 m_callerClassName, calleeClassName
    267                         )
    268                 );
    269
    270                 // emit the joinpoint
    271                 //See AW-253 - we remember if we had an INVOKE INTERFACE opcode
    272                 int modifiers = calleeMethodInfo.getModifiers();
    273                 if (opcode == INVOKEINTERFACE) {
    274                     modifiers = modifiers | MODIFIER_INVOKEINTERFACE;
    275                 }
    276                 m_ctx.addEmittedJoinPoint(
    277                         new EmittedJoinPoint(
    278                                 JoinPointType.METHOD_CALL_INT,
    279                                 m_callerClassName,
    280                                 m_callerMethodName,
    281                                 m_callerMethodDesc,
    282                                 m_callerMemberInfo.getModifiers(),
    283                                 calleeClassName,
    284                                 calleeMethodName,
    285                                 calleeMethodDesc,
    286                                 modifiers,
    287                                 joinPointHash,
    288                                 joinPointClassName,
    289                                 m_lastLabelForLineNumber
    290                         )
    291                 );
    292             }
    293         }
    294
    295         /***
    296          * Filters out the methods that are not eligible for transformation.
    297          * Do not filter on abstract callee method - needed for interface declared method call
    298          * (invokeinterface instr.)
    299          *
    300          * @param definitions
    301          * @param ctx
    302          * @param calleeMethodInfo
    303          * @return boolean true if the method should be filtered out
    304          */
    305         public boolean methodFilter(final Set definitions,
    306                                     final ExpressionContext ctx,
    307                                     final MethodInfo calleeMethodInfo) {
    308             if (calleeMethodInfo.getName().equals(INIT_METHOD_NAME) ||
    309                 calleeMethodInfo.getName().equals(CLINIT_METHOD_NAME) ||
    310                 calleeMethodInfo.getName().startsWith(ORIGINAL_METHOD_PREFIX)) {
    311                 return true;
    312             }
    313             for (Iterator it = definitions.iterator(); it.hasNext();) {
    314                 if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
    315                     return false;
    316                 } else {
    317                     continue;
    318                 }
    319             }
    320             return true;
    321         }
    322     }
    323 }