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.objectweb.asm.Type;
    17  import org.codehaus.aspectwerkz.definition.SystemDefinition;
    18  import org.codehaus.aspectwerkz.expression.ExpressionContext;
    19  import org.codehaus.aspectwerkz.expression.PointcutType;
    20  import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
    21  import org.codehaus.aspectwerkz.reflect.ClassInfo;
    22  import org.codehaus.aspectwerkz.reflect.MemberInfo;
    23  import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
    24  import org.codehaus.aspectwerkz.transform.Context;
    25  import org.codehaus.aspectwerkz.transform.TransformationUtil;
    26  import org.codehaus.aspectwerkz.transform.TransformationConstants;
    27  import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
    28  import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
    29  import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
    30
    31  import java.util.Iterator;
    32  import java.util.Set;
    33  import java.util.ArrayList;
    34  import java.util.List;
    35  import java.util.Map;
    36  import java.util.HashMap;
    37  import java.lang.reflect.Modifier;
    38
    39  /***
    40   * Advises catch clauses by inserting a call to the join point as the first thing in the catch block.
    41   *
    42   * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
    43   */
    44  public class HandlerVisitor extends ClassAdapter implements TransformationConstants {
    45
    46      /***
    47       * A visitor that looks for all catch clause and keep track of them
    48       * providing that they match
    49       *
    50       * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
    51       */
    52      public static class LookaheadCatchLabelsClassAdapter extends ClassAdapter {
    53          /***
    54           * list of CatchLabelStruct that matches
    55           */
    56          List m_catchLabels = new ArrayList();
    57
    58          /***
    59           * map of Integer(index in whole class)-->asm.Label for all the visited labels
    60           */
    61          private final Map m_labelIndexes = new HashMap();
    62
    63          /***
    64           * current label index in whole class, from 0 to N
    65           */
    66          private int m_labelIndex = -1;
    67
    68          private final ContextImpl m_ctx;
    69          private final ClassLoader m_loader;
    70          private final ClassInfo m_callerClassInfo;
    71
    72          /***
    73           * Visit the class
    74           *
    75           * @param cv
    76           * @param loader
    77           * @param callerClassInfo
    78           * @param ctx
    79           * @param catchLabels
    80           */
    81          public LookaheadCatchLabelsClassAdapter(ClassVisitor cv, ClassLoader loader, ClassInfo callerClassInfo,
    82                                                  Context ctx, List catchLabels) {
    83              super(cv);
    84              m_catchLabels = catchLabels;
    85              m_loader = loader;
    86              m_callerClassInfo = callerClassInfo;
    87              m_ctx = (ContextImpl) ctx;
    88          }
    89
    90          /***
    91           * Visit method bodies
    92           *
    93           * @param access
    94           * @param callerMethodName
    95           * @param callerMethodDesc
    96           * @param exceptions
    97           * @param attrs
    98           * @return
    99           */
    100         public CodeVisitor visitMethod(final int access,
    101                                        final String callerMethodName,
    102                                        final String callerMethodDesc,
    103                                        final String[] exceptions,
    104                                        final Attribute attrs) {
    105             if (callerMethodName.startsWith(WRAPPER_METHOD_PREFIX)) {
    106                 return super.visitMethod(access, callerMethodName, callerMethodDesc, exceptions, attrs);
    107             }
    108
    109             CodeVisitor mv = cv.visitMethod(access, callerMethodName, callerMethodDesc, exceptions, attrs);
    110             if (mv == null) {
    111                 return mv;
    112             }
    113
    114             final MemberInfo callerMemberInfo;
    115             if (CLINIT_METHOD_NAME.equals(callerMethodName)) {
    116                 callerMemberInfo = m_callerClassInfo.staticInitializer();
    117             } else if (INIT_METHOD_NAME.equals(callerMethodName)) {
    118                 int hash = AsmHelper.calculateConstructorHash(callerMethodDesc);
    119                 callerMemberInfo = m_callerClassInfo.getConstructor(hash);
    120             } else {
    121                 int hash = AsmHelper.calculateMethodHash(callerMethodName, callerMethodDesc);
    122                 callerMemberInfo = m_callerClassInfo.getMethod(hash);
    123             }
    124             if (callerMemberInfo == null) {
    125                 System.err.println(
    126                         "AW::WARNING " +
    127                         "metadata structure could not be build for method ["
    128                         + m_callerClassInfo.getName().replace('/', '.')
    129                         + '.' + callerMethodName + ':' + callerMethodDesc + ']'
    130                 );
    131                 return mv;
    132             }
    133
    134             /***
    135              * Visit the method, and keep track of all labels so that when visittryCatch is reached
    136              * we can remember the index
    137              *
    138              * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
    139              */
    140             return new CodeAdapter(mv) {
    141                 public void visitLabel(Label label) {
    142                     m_labelIndexes.put(label, new Integer(++m_labelIndex));
    143                     super.visitLabel(label);
    144                 }
    145
    146                 public void visitTryCatchBlock(Label startLabel, Label endLabel, Label handlerLabel,
    147                                                String exceptionTypeName) {
    148                     if (exceptionTypeName == null) {
    149                         // finally block
    150                         super.visitTryCatchBlock(startLabel, endLabel, handlerLabel, exceptionTypeName);
    151                         return;
    152                     }
    153                     final ClassInfo exceptionClassInfo = AsmClassInfo.getClassInfo(exceptionTypeName, m_loader);
    154                     final ExpressionContext ctx = new ExpressionContext(
    155                             PointcutType.HANDLER, exceptionClassInfo, callerMemberInfo
    156                     );
    157                     if (!handlerFilter(m_ctx.getDefinitions(), ctx)) {
    158                         // remember its index and the exception exceptionClassInfo
    159                         Integer index = (Integer) m_labelIndexes.get(handlerLabel);
    160                         if (index != null) {
    161                             m_catchLabels.add(
    162                                     new CatchLabelStruct(
    163                                             index.intValue(),
    164                                             exceptionClassInfo,
    165                                             m_callerClassInfo,
    166                                             callerMemberInfo
    167                                     )
    168                             );
    169                         }
    170                     }
    171                     super.visitTryCatchBlock(startLabel, endLabel, handlerLabel, exceptionTypeName);
    172                 }
    173             };
    174         }
    175     }
    176
    177     //---- non lookahead visitor
    178
    179     private final ContextImpl m_ctx;
    180
    181     /***
    182      * List of matching catch clause
    183      */
    184     private final List m_catchLabels;
    185
    186     /***
    187      * catch clause index in whole class
    188      */
    189     private int m_labelIndex = -1;
    190
    191     private Label m_lastLabelForLineNumber = EmittedJoinPoint.NO_LINE_NUMBER;
    192
    193
    194     /***
    195      * Creates a new instance.
    196      *
    197      * @param cv
    198      * @param ctx
    199      */
    200     public HandlerVisitor(final ClassVisitor cv,
    201                           final Context ctx,
    202                           final List catchLabels) {
    203         super(cv);
    204         m_ctx = (ContextImpl) ctx;
    205         m_catchLabels = catchLabels;
    206     }
    207
    208     /***
    209      * Visits the methods bodies to weave in JP calls at catch clauses
    210      *
    211      * @param access
    212      * @param name
    213      * @param desc
    214      * @param exceptions
    215      * @param attrs
    216      * @return
    217      */
    218     public CodeVisitor visitMethod(final int access,
    219                                    final String name,
    220                                    final String desc,
    221                                    final String[] exceptions,
    222                                    final Attribute attrs) {
    223         if (name.startsWith(WRAPPER_METHOD_PREFIX)) {
    224             return super.visitMethod(access, name, desc, exceptions, attrs);
    225         }
    226
    227         CodeVisitor mv = cv.visitMethod(access, name, desc, exceptions, attrs);
    228         return mv == null ? null : new CatchClauseCodeAdapter(mv);
    229     }
    230
    231     /***
    232      * Advises catch clauses by inserting a call to the join point as the first thing in the catch block.
    233      *
    234      * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
    235      */
    236     public class CatchClauseCodeAdapter extends CodeAdapter {
    237
    238         /***
    239          * Creates a new instance.
    240          *
    241          * @param ca
    242          */
    243         public CatchClauseCodeAdapter(final CodeVisitor ca) {
    244             super(ca);
    245         }
    246
    247         public void visitLabel(Label label) {
    248             m_lastLabelForLineNumber = label;
    249             super.visitLabel(label);
    250
    251             // check if it is a catch label
    252             int index = ++m_labelIndex;
    253             CatchLabelStruct catchLabel = null;
    254             for (Iterator iterator = m_catchLabels.iterator(); iterator.hasNext();) {
    255                 CatchLabelStruct aCatchLabel = (CatchLabelStruct) iterator.next();
    256                 if (aCatchLabel.labelIndexInWholeClass == index) {
    257                     catchLabel = aCatchLabel;
    258                     break;
    259                 }
    260             }
    261             if (catchLabel == null) {
    262                 return;
    263             }
    264             // matched
    265             m_ctx.markAsAdvised();
    266             final String callerTypeName = catchLabel.caller.getName().replace('.', '/');
    267             final String exceptionTypeDesc = catchLabel.exception.getSignature();
    268             final String exceptionTypeName = Type.getType(exceptionTypeDesc).getInternalName();
    269             final int joinPointHash = AsmHelper.calculateClassHash(exceptionTypeDesc);
    270             final String joinPointClassName = TransformationUtil.getJoinPointClassName(
    271                     callerTypeName,
    272                     catchLabel.callerMember.getName(),
    273                     catchLabel.callerMember.getSignature(),
    274                     exceptionTypeName,
    275                     JoinPointType.HANDLER_INT,
    276                     joinPointHash
    277             );
    278
    279             // add the call to the join point
    280             // exception instance is on the stack
    281             // dup it for ARG0
    282             cv.visitInsn(DUP);
    283
    284             // load caller instance if any
    285             if (Modifier.isStatic(catchLabel.callerMember.getModifiers())) {
    286                 cv.visitInsn(ACONST_NULL);
    287             } else {
    288                 cv.visitVarInsn(ALOAD, 0);
    289             }
    290             //TODO for now we pass the exception as both CALLEE and ARG0 - may be callee must be NULL
    291             //? check in AJ RTTI
    292             cv.visitMethodInsn(
    293                     INVOKESTATIC, joinPointClassName, INVOKE_METHOD_NAME,
    294                     TransformationUtil.getInvokeSignatureForHandlerJoinPoints(callerTypeName, exceptionTypeName)
    295             );
    296
    297             // emit the joinpoint
    298             m_ctx.addEmittedJoinPoint(
    299                     new EmittedJoinPoint(
    300                             JoinPointType.HANDLER_INT,
    301                             callerTypeName,
    302                             catchLabel.callerMember.getName(),
    303                             catchLabel.callerMember.getSignature(),
    304                             catchLabel.callerMember.getModifiers(),
    305                             exceptionTypeName,
    306                             "",
    307                             exceptionTypeDesc,
    308                             0, // a bit meaningless but must not be static
    309                             joinPointHash,
    310                             joinPointClassName,
    311                             m_lastLabelForLineNumber
    312                     )
    313             );
    314         }
    315     }
    316
    317     /***
    318      * Filters out the catch clauses that are not eligible for transformation.
    319      *
    320      * @param definitions
    321      * @param ctx
    322      * @return boolean true if the catch clause should be filtered out
    323      */
    324     static boolean handlerFilter(final Set definitions, final ExpressionContext ctx) {
    325         for (Iterator it = definitions.iterator(); it.hasNext();) {
    326             if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
    327                 return false;
    328             } else {
    329                 continue;
    330             }
    331         }
    332         return true;
    333     }
    334
    335     /***
    336      * A struct to represent a catch clause.
    337      * The index is class wide, and the exception class info is kept.
    338      *
    339      * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
    340      */
    341     private static class CatchLabelStruct {
    342         int labelIndexInWholeClass = -1;
    343         ClassInfo exception = null;
    344         ClassInfo caller = null;
    345         MemberInfo callerMember = null;
    346
    347         private CatchLabelStruct(int indexInClass, ClassInfo exception, ClassInfo caller, MemberInfo callerMember) {
    348             labelIndexInWholeClass = indexInClass;
    349             this.exception = exception;
    350             this.caller = caller;
    351             this.callerMember = callerMember;
    352         }
    353     }
    354 }