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.util.Set;
    11  import java.util.Iterator;
    12  import java.util.Collection;
    13  import java.util.HashMap;
    14
    15  import org.objectweb.asm.*;
    16  import org.codehaus.aspectwerkz.transform.Context;
    17  import org.codehaus.aspectwerkz.transform.TransformationConstants;
    18  import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
    19  import org.codehaus.aspectwerkz.reflect.ClassInfo;
    20  import org.codehaus.aspectwerkz.expression.ExpressionContext;
    21  import org.codehaus.aspectwerkz.expression.PointcutType;
    22  import org.codehaus.aspectwerkz.expression.ExpressionInfo;
    23  import org.codehaus.aspectwerkz.definition.SystemDefinition;
    24  import org.codehaus.aspectwerkz.definition.AdviceDefinition;
    25  import org.codehaus.aspectwerkz.definition.DeploymentScope;
    26  import org.codehaus.aspectwerkz.DeploymentModel;
    27
    28  /***
    29   * Adds an instance level aspect management to the target class.
    30   *
    31   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    32   */
    33  public class InstanceLevelAspectVisitor extends ClassAdapter implements TransformationConstants {
    34
    35      private final ContextImpl m_ctx;
    36      private final ClassInfo m_classInfo;
    37      private boolean m_isAdvised = false;
    38
    39      /***
    40       * Creates a new add interface class adapter.
    41       *
    42       * @param cv
    43       * @param classInfo
    44       * @param ctx
    45       */
    46      public InstanceLevelAspectVisitor(final ClassVisitor cv,
    47                                        final ClassInfo classInfo,
    48                                        final Context ctx) {
    49          super(cv);
    50          m_classInfo = classInfo;
    51          m_ctx = (ContextImpl) ctx;
    52      }
    53
    54      /***
    55       * Visits the class.
    56       *
    57       * @param access
    58       * @param name
    59       * @param superName
    60       * @param interfaces
    61       * @param sourceFile
    62       */
    63      public void visit(final int version,
    64                        final int access,
    65                        final String name,
    66                        final String superName,
    67                        final String[] interfaces,
    68                        final String sourceFile) {
    69
    70          if (classFilter(m_classInfo, m_ctx.getDefinitions())) {
    71              super.visit(version, access, name, superName, interfaces, sourceFile);
    72              return;
    73          }
    74
    75          for (int i = 0; i < interfaces.length; i++) {
    76              String anInterface = interfaces[i];
    77              if (anInterface.equals(HAS_INSTANCE_LEVEL_ASPECT_INTERFACE_NAME)) {
    78                  super.visit(version, access, name, superName, interfaces, sourceFile);
    79                  return;
    80              }
    81          }
    82          String[] newInterfaceArray = new String[interfaces.length + 1];
    83          for (int i = 0; i < interfaces.length; i++) {
    84              newInterfaceArray[i] = interfaces[i];
    85          }
    86          newInterfaceArray[interfaces.length] = HAS_INSTANCE_LEVEL_ASPECT_INTERFACE_NAME;
    87
    88          // add the interface
    89          super.visit(version, access, name, superName, newInterfaceArray, sourceFile);
    90
    91          // add the field with the aspect instance map
    92          addAspectMapField();
    93
    94          // add the getAspect(..) method
    95          addGetAspectMethod(name);
    96      }
    97
    98      /***
    99       * Appends mixin instantiation to the clinit method and/or init method.
    100      *
    101      * @param access
    102      * @param name
    103      * @param desc
    104      * @param exceptions
    105      * @param attrs
    106      * @return
    107      */
    108     public CodeVisitor visitMethod(final int access,
    109                                    final String name,
    110                                    final String desc,
    111                                    final String[] exceptions,
    112                                    final Attribute attrs) {
    113         if (m_isAdvised) {
    114             if (name.equals(INIT_METHOD_NAME)) {
    115                 CodeVisitor mv = new AppendToInitMethodCodeAdapter(
    116                         cv.visitMethod(access, name, desc, exceptions, attrs),
    117                         name
    118                 );
    119                 mv.visitMaxs(0, 0);
    120                 return mv;
    121             }
    122         }
    123         return cv.visitMethod(access, name, desc, exceptions, attrs);
    124     }
    125
    126     /***
    127      * Adds the aspect map field to the target class.
    128      */
    129     private void addAspectMapField() {
    130         super.visitField(
    131                 ACC_PRIVATE + ACC_SYNTHETIC + ACC_TRANSIENT,
    132                 INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
    133                 INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE,
    134                 null, null
    135         );
    136     }
    137
    138     /***
    139      * Adds the getAspect(..) method to the target class.
    140      *
    141      * @param name the class name of the target class
    142      */
    143     private void addGetAspectMethod(final String name) {
    144         CodeVisitor cv = super.visitMethod(
    145                 ACC_PUBLIC + ACC_SYNTHETIC,
    146                 GET_INSTANCE_LEVEL_ASPECT_METHOD_NAME,
    147                 GET_INSTANCE_LEVEL_ASPECT_METHOD_SIGNATURE,
    148                 null, null
    149         );
    150
    151         cv.visitVarInsn(ALOAD, 0);
    152         cv.visitFieldInsn(
    153                 GETFIELD,
    154                 name,
    155                 INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
    156                 INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
    157         );
    158
    159         // if == null, field = new HashMap()
    160         Label ifFieldNullNotLabel = new Label();
    161         cv.visitJumpInsn(IFNONNULL, ifFieldNullNotLabel);
    162         cv.visitVarInsn(ALOAD, 0);
    163         cv.visitTypeInsn(NEW, HASH_MAP_CLASS_NAME);
    164         cv.visitInsn(DUP);
    165         cv.visitMethodInsn(
    166                 INVOKESPECIAL,
    167                 HASH_MAP_CLASS_NAME,
    168                 INIT_METHOD_NAME,
    169                 NO_PARAM_RETURN_VOID_SIGNATURE
    170         );
    171         cv.visitFieldInsn(
    172                 PUTFIELD,
    173                 m_classInfo.getName().replace('.', '/'),
    174                 INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
    175                 INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
    176         );
    177         cv.visitLabel(ifFieldNullNotLabel);
    178
    179         cv.visitVarInsn(ALOAD, 0);
    180         cv.visitFieldInsn(
    181                 GETFIELD,
    182                 name,
    183                 INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
    184                 INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
    185         );
    186
    187         cv.visitVarInsn(ALOAD, 2);//qName
    188         cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME, GET_METHOD_NAME, GET_METHOD_SIGNATURE);
    189         cv.visitVarInsn(ASTORE, 4);
    190         cv.visitVarInsn(ALOAD, 4);
    191         Label ifNullNotLabel = new Label();
    192         cv.visitJumpInsn(IFNONNULL, ifNullNotLabel);
    193         cv.visitVarInsn(ALOAD, 2);//qName
    194         cv.visitVarInsn(ALOAD, 3);//containerClassName
    195         cv.visitVarInsn(ALOAD, 0);//this (perInstance)
    196         cv.visitMethodInsn(
    197                 INVOKESTATIC,
    198                 ASPECTS_CLASS_NAME,
    199                 ASPECT_OF_METHOD_NAME,
    200                 ASPECT_OF_PER_INSTANCE_METHOD_SIGNATURE
    201         );
    202         cv.visitVarInsn(ASTORE, 4);
    203         cv.visitVarInsn(ALOAD, 0);
    204         cv.visitFieldInsn(
    205                 GETFIELD,
    206                 name,
    207                 INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
    208                 INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
    209         );
    210         cv.visitVarInsn(ALOAD, 2);
    211         cv.visitVarInsn(ALOAD, 4);
    212         cv.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME, PUT_METHOD_NAME, PUT_METHOD_SIGNATURE);
    213         cv.visitInsn(POP);
    214         cv.visitLabel(ifNullNotLabel);
    215         cv.visitVarInsn(ALOAD, 4);
    216         cv.visitInsn(ARETURN);
    217         cv.visitMaxs(0, 0);
    218
    219         m_ctx.markAsAdvised();
    220         m_isAdvised = true;
    221     }
    222
    223     /***
    224      * Filters the classes to be transformed.
    225      *
    226      * @param classInfo   the class to filter
    227      * @param definitions a set with the definitions
    228      * @return boolean true if the method should be filtered away
    229      */
    230     public static boolean classFilter(final ClassInfo classInfo, final Set definitions) {
    231         if (classInfo.isInterface()) {
    232             return true;
    233         }
    234
    235         ExpressionContext ctx = new ExpressionContext(PointcutType.WITHIN, null, classInfo);
    236
    237         for (Iterator it = definitions.iterator(); it.hasNext();) {
    238             SystemDefinition systemDef = (SystemDefinition) it.next();
    239             String className = classInfo.getName().replace('/', '.');
    240             if (systemDef.inExcludePackage(className)) {
    241                 return true;
    242             }
    243             if (!systemDef.inIncludePackage(className)) {
    244                 return true;
    245             }
    246
    247             // match on perinstance deployed aspects
    248             Collection adviceDefs = systemDef.getAdviceDefinitions();
    249             for (Iterator defs = adviceDefs.iterator(); defs.hasNext();) {
    250                 AdviceDefinition adviceDef = (AdviceDefinition) defs.next();
    251                 ExpressionInfo expressionInfo = adviceDef.getExpressionInfo();
    252                 if (expressionInfo == null) {
    253                     continue;
    254                 }
    255                 if (adviceDef.getAspectDefinition().getDeploymentModel().equals(DeploymentModel.PER_INSTANCE)
    256                     && expressionInfo.getAdvisedClassFilterExpression().match(ctx)) {
    257                     return false;
    258                 }
    259             }
    260
    261             // match on deployment scopes, e.g. potential perinstance deployment aspects
    262             Collection deploymentScopes = systemDef.getDeploymentScopes();
    263             for (Iterator scopes = deploymentScopes.iterator(); scopes.hasNext();) {
    264                 DeploymentScope deploymentScope = (DeploymentScope) scopes.next();
    265                 ExpressionInfo expression = new ExpressionInfo(
    266                         deploymentScope.getExpression(),
    267                         systemDef.getUuid()
    268                 );
    269                 if (expression.getAdvisedClassFilterExpression().match(ctx)) {
    270                     return false;
    271                 }
    272             }
    273         }
    274
    275         return true;
    276     }
    277
    278     /***
    279      * Adds initialization of aspect map field to end of the init method.
    280      *
    281      * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    282      */
    283     private class AppendToInitMethodCodeAdapter extends AfterObjectInitializationCodeAdapter {
    284
    285         private boolean m_done = false;
    286
    287         public AppendToInitMethodCodeAdapter(final CodeVisitor ca, String callerMemberName) {
    288             super(ca, callerMemberName);
    289         }
    290
    291         /***
    292          * Inserts the init of the aspect field right after the call to super(..) of this(..).
    293          *
    294          * @param opcode
    295          * @param owner
    296          * @param name
    297          * @param desc
    298          */
    299         public void visitMethodInsn(int opcode,
    300                                     String owner,
    301                                     String name,
    302                                     String desc) {
    303             super.visitMethodInsn(opcode, owner, name, desc);
    304             if (opcode == INVOKESPECIAL && m_isObjectInitialized && !m_done) {
    305                 m_done = true;
    306
    307                 // initialize aspect map field
    308                 cv.visitVarInsn(ALOAD, 0);
    309                 cv.visitTypeInsn(NEW, HASH_MAP_CLASS_NAME);
    310                 cv.visitInsn(DUP);
    311                 cv.visitMethodInsn(
    312                         INVOKESPECIAL,
    313                         HASH_MAP_CLASS_NAME,
    314                         INIT_METHOD_NAME,
    315                         NO_PARAM_RETURN_VOID_SIGNATURE
    316                 );
    317                 cv.visitFieldInsn(
    318                         PUTFIELD,
    319                         m_classInfo.getName().replace('.', '/'),
    320                         INSTANCE_LEVEL_ASPECT_MAP_FIELD_NAME,
    321                         INSTANCE_LEVEL_ASPECT_MAP_FIELD_SIGNATURE
    322                 );
    323             }
    324         }
    325     }
    326 }