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.Type;
    16  import org.objectweb.asm.Label;
    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.FieldInfo;
    23  import org.codehaus.aspectwerkz.reflect.MemberInfo;
    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 SET and GET join points by replacing PUTFIELD and GETFIELD instructions with invocations
    39   * of the compiled join point.
    40   *
    41   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    42   */
    43  public class FieldSetFieldGetVisitor extends ClassAdapter implements TransformationConstants {
    44
    45      private final ContextImpl m_ctx;
    46      private final ClassLoader m_loader;
    47      private final ClassInfo m_callerClassInfo;
    48
    49      private Label m_lastLabelForLineNumber = EmittedJoinPoint.NO_LINE_NUMBER;
    50
    51      /***
    52       * Creates a new instance.
    53       *
    54       * @param cv
    55       * @param loader
    56       * @param classInfo
    57       * @param ctx
    58       */
    59      public FieldSetFieldGetVisitor(final ClassVisitor cv,
    60                                     final ClassLoader loader,
    61                                     final ClassInfo classInfo,
    62                                     final Context ctx) {
    63          super(cv);
    64          m_loader = loader;
    65          m_callerClassInfo = classInfo;
    66          m_ctx = (ContextImpl) ctx;
    67      }
    68
    69      /***
    70       * Visits the caller methods.
    71       *
    72       * @param access
    73       * @param name
    74       * @param desc
    75       * @param exceptions
    76       * @param attrs
    77       * @return
    78       */
    79      public CodeVisitor visitMethod(final int access,
    80                                     final String name,
    81                                     final String desc,
    82                                     final String[] exceptions,
    83                                     final Attribute attrs) {
    84
    85          if (name.startsWith(WRAPPER_METHOD_PREFIX)) {
    86              return super.visitMethod(access, name, desc, exceptions, attrs);
    87          }
    88
    89          CodeVisitor mv = cv.visitMethod(access, name, desc, exceptions, attrs);
    90          return mv == null ? null : new ReplacePutFieldAndGetFieldInstructionCodeAdapter(
    91                  mv,
    92                  m_loader,
    93                  m_callerClassInfo,
    94                  m_ctx.getClassName(),
    95                  name,
    96                  desc
    97          );
    98      }
    99
    100     /***
    101      * Replaces PUTFIELD and GETFIELD instructions with a call to the compiled JoinPoint instance.
    102      *
    103      * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
    104      * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
    105      */
    106     public class ReplacePutFieldAndGetFieldInstructionCodeAdapter extends AfterObjectInitializationCodeAdapter {
    107
    108         private final ClassLoader m_loader;
    109         private final ClassInfo m_callerClassInfo;
    110         private final String m_callerClassName;
    111         private final String m_callerMethodName;
    112         private final String m_callerMethodDesc;
    113         private final MemberInfo m_callerMemberInfo;
    114
    115         /***
    116          * Creates a new instance.
    117          *
    118          * @param ca
    119          * @param loader
    120          * @param callerClassInfo
    121          * @param callerClassName
    122          * @param callerMethodName
    123          * @param callerMethodDesc
    124          */
    125         public ReplacePutFieldAndGetFieldInstructionCodeAdapter(final CodeVisitor ca,
    126                                                                 final ClassLoader loader,
    127                                                                 final ClassInfo callerClassInfo,
    128                                                                 final String callerClassName,
    129                                                                 final String callerMethodName,
    130                                                                 final String callerMethodDesc) {
    131             super(ca, callerMethodName);
    132             m_loader = loader;
    133             m_callerClassInfo = callerClassInfo;
    134             m_callerClassName = callerClassName;
    135             m_callerMethodName = callerMethodName;
    136             m_callerMethodDesc = callerMethodDesc;
    137
    138             if (CLINIT_METHOD_NAME.equals(m_callerMethodName)) {
    139                 m_callerMemberInfo = m_callerClassInfo.staticInitializer();
    140             } else if (INIT_METHOD_NAME.equals(m_callerMethodName)) {
    141                 int hash = AsmHelper.calculateConstructorHash(m_callerMethodDesc);
    142                 m_callerMemberInfo = m_callerClassInfo.getConstructor(hash);
    143             } else {
    144                 int hash = AsmHelper.calculateMethodHash(m_callerMethodName, m_callerMethodDesc);
    145                 m_callerMemberInfo = m_callerClassInfo.getMethod(hash);
    146             }
    147             if (m_callerMemberInfo == null) {
    148                 System.err.println(
    149                         "AW::WARNING " +
    150                         "metadata structure could not be build for method ["
    151                         + m_callerClassInfo.getName().replace('/', '.')
    152                         + '.' + m_callerMethodName + ':' + m_callerMethodDesc + ']'
    153                 );
    154             }
    155         }
    156
    157         /***
    158          * Label
    159          *
    160          * @param label
    161          */
    162         public void visitLabel(Label label) {
    163             m_lastLabelForLineNumber = label;
    164             super.visitLabel(label);
    165         }
    166
    167         /***
    168          * Visits PUTFIELD and GETFIELD instructions.
    169          *
    170          * @param opcode
    171          * @param className
    172          * @param fieldName
    173          * @param fieldDesc
    174          */
    175         public void visitFieldInsn(final int opcode,
    176                                    final String className,
    177                                    final String fieldName,
    178                                    final String fieldDesc) {
    179
    180             if (className.endsWith(AbstractJoinPointCompiler.JOIN_POINT_CLASS_SUFFIX) ||
    181                 fieldName.startsWith(ASPECTWERKZ_PREFIX) ||
    182                 fieldName.startsWith(SYNTHETIC_MEMBER_PREFIX) || // synthetic field
    183                 fieldName.equals(SERIAL_VERSION_UID_FIELD_NAME) // can have been added by the weaver (not safe)
    184             ) {
    185                 super.visitFieldInsn(opcode, className, fieldName, fieldDesc);
    186                 return;
    187             }
    188
    189             // if within ctor, make sure object initialization has been reached
    190             if (!m_isObjectInitialized) {
    191                 super.visitFieldInsn(opcode, className, fieldName, fieldDesc);
    192                 return;
    193             }
    194
    195
    196             final Type fieldType = Type.getType(fieldDesc);
    197             final int joinPointHash = AsmHelper.calculateFieldHash(fieldName, fieldDesc);
    198             final ClassInfo classInfo = AsmClassInfo.getClassInfo(className, m_loader);
    199             final FieldInfo fieldInfo = getFieldInfo(classInfo, className, fieldName, fieldDesc, joinPointHash);
    200
    201             if (opcode == PUTFIELD || opcode == PUTSTATIC) {
    202                 handleFieldModification(fieldInfo, opcode, className, fieldName, fieldDesc, joinPointHash);
    203             } else if (opcode == GETFIELD || opcode == GETSTATIC) {
    204                 handleFieldAccess(fieldInfo, opcode, className, fieldName, fieldDesc, joinPointHash, fieldType);
    205             } else {
    206                 super.visitFieldInsn(opcode, className, fieldName, fieldDesc);
    207             }
    208         }
    209
    210         /***
    211          * Handles field access.
    212          *
    213          * @param fieldInfo
    214          * @param opcode
    215          * @param className
    216          * @param fieldName
    217          * @param fieldDesc
    218          * @param joinPointHash
    219          * @param fieldType
    220          */
    221         private void handleFieldAccess(final FieldInfo fieldInfo,
    222                                        final int opcode,
    223                                        final String className,
    224                                        final String fieldName,
    225                                        final String fieldDesc,
    226                                        int joinPointHash,
    227                                        final Type fieldType) {
    228             if (m_callerMemberInfo == null) {
    229                 super.visitFieldInsn(opcode, className, fieldName, fieldDesc);
    230                 return;
    231             }
    232
    233             ExpressionContext ctx = new ExpressionContext(PointcutType.GET, fieldInfo, m_callerMemberInfo);
    234
    235             if (fieldFilter(m_ctx.getDefinitions(), ctx, fieldInfo)) {
    236                 super.visitFieldInsn(opcode, className, fieldName, fieldDesc);
    237             } else {
    238                 m_ctx.markAsAdvised();
    239
    240                 String joinPointClassName = TransformationUtil.getJoinPointClassName(
    241                         m_callerClassName,
    242                         m_callerMethodName,
    243                         m_callerMethodDesc,
    244                         className,
    245                         JoinPointType.FIELD_GET_INT,
    246                         joinPointHash
    247                 );
    248
    249                 // no param to field, so pass a default value to the invoke method
    250                 AsmHelper.addDefaultValue(this, fieldType);
    251
    252                 // if static context load NULL else 'this'
    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.getInvokeSignatureForFieldJoinPoints(
    265                                 fieldInfo.getModifiers(), fieldDesc, m_callerClassName, className
    266                         )
    267                 );
    268
    269                 // emit the joinpoint
    270                 m_ctx.addEmittedJoinPoint(
    271                         new EmittedJoinPoint(
    272                                 JoinPointType.FIELD_GET_INT,
    273                                 m_callerClassName,
    274                                 m_callerMethodName,
    275                                 m_callerMethodDesc,
    276                                 m_callerMemberInfo.getModifiers(),
    277                                 className,
    278                                 fieldName,
    279                                 fieldDesc,
    280                                 fieldInfo.getModifiers(),
    281                                 joinPointHash,
    282                                 joinPointClassName,
    283                                 m_lastLabelForLineNumber
    284                         )
    285                 );
    286             }
    287         }
    288
    289         /***
    290          * Handles field modification.
    291          *
    292          * @param fieldInfo
    293          * @param opcode
    294          * @param className
    295          * @param fieldName
    296          * @param fieldDesc
    297          * @param joinPointHash
    298          */
    299         private void handleFieldModification(final FieldInfo fieldInfo,
    300                                              final int opcode,
    301                                              final String className,
    302                                              final String fieldName,
    303                                              final String fieldDesc,
    304                                              final int joinPointHash) {
    305             if (m_callerMemberInfo == null) {
    306                 super.visitFieldInsn(opcode, className, fieldName, fieldDesc);
    307                 return;
    308             }
    309
    310             ExpressionContext ctx = new ExpressionContext(PointcutType.SET, fieldInfo, m_callerMemberInfo);
    311
    312             if (fieldFilter(m_ctx.getDefinitions(), ctx, fieldInfo)) {
    313                 super.visitFieldInsn(opcode, className, fieldName, fieldDesc);
    314             } else {
    315                 m_ctx.markAsAdvised();
    316
    317                 String joinPointClassName = TransformationUtil.getJoinPointClassName(
    318                         m_callerClassName,
    319                         m_callerMethodName,
    320                         m_callerMethodDesc,
    321                         className,
    322                         JoinPointType.FIELD_SET_INT,
    323                         joinPointHash
    324                 );
    325
    326                 // load the caller instance (this), or null if in a static context
    327                 // note that callee instance [optional] and args are already on the stack
    328                 if (Modifier.isStatic(m_callerMemberInfo.getModifiers())) {
    329                     visitInsn(ACONST_NULL);
    330                 } else {
    331                     visitVarInsn(ALOAD, 0);
    332                 }
    333
    334                 // add the call to the join point
    335                 super.visitMethodInsn(
    336                         INVOKESTATIC,
    337                         joinPointClassName,
    338                         INVOKE_METHOD_NAME,
    339                         TransformationUtil.getInvokeSignatureForFieldJoinPoints(
    340                                 fieldInfo.getModifiers(), fieldDesc, m_callerClassName, className
    341                         )
    342                 );
    343
    344                 final int sort = Type.getType(fieldDesc).getSort();
    345                 if (sort != Type.LONG && sort != Type.DOUBLE) {
    346                     super.visitInsn(POP);
    347                 } else {
    348                     //AW-437
    349                     super.visitInsn(POP2);
    350                 }
    351
    352                 // emit the joinpoint
    353                 m_ctx.addEmittedJoinPoint(
    354                         new EmittedJoinPoint(
    355                                 JoinPointType.FIELD_SET_INT,
    356                                 m_callerClassName,
    357                                 m_callerMethodName,
    358                                 m_callerMethodDesc,
    359                                 m_callerMemberInfo.getModifiers(),
    360                                 className,
    361                                 fieldName,
    362                                 fieldDesc,
    363                                 fieldInfo.getModifiers(),
    364                                 joinPointHash,
    365                                 joinPointClassName,
    366                                 m_lastLabelForLineNumber
    367                         )
    368                 );
    369             }
    370         }
    371
    372         /***
    373          * Returns the field info.
    374          *
    375          * @param classInfo
    376          * @param className
    377          * @param fieldName
    378          * @param fieldDesc
    379          * @param joinPointHash
    380          * @return
    381          */
    382         private FieldInfo getFieldInfo(final ClassInfo classInfo,
    383                                        final String className,
    384                                        final String fieldName,
    385                                        final String fieldDesc,
    386                                        final int joinPointHash) {
    387             FieldInfo fieldInfo = classInfo.getField(joinPointHash);
    388             if (fieldInfo == null) {
    389                 throw new Error(
    390                         "field info metadata structure could not be build for field: "
    391                         + className
    392                         + '.'
    393                         + fieldName
    394                         + ':'
    395                         + fieldDesc
    396                 );
    397             }
    398             return fieldInfo;
    399         }
    400
    401         /***
    402          * Filters out the fields that are not eligible for transformation.
    403          *
    404          * @param definitions
    405          * @param ctx
    406          * @param fieldInfo
    407          * @return boolean true if the field should be filtered out
    408          */
    409         public boolean fieldFilter(final Set definitions,
    410                                    final ExpressionContext ctx,
    411                                    final FieldInfo fieldInfo) {
    412             if (fieldInfo.getName().startsWith(ORIGINAL_METHOD_PREFIX)) {
    413                 return true;
    414             }
    415             for (Iterator it = definitions.iterator(); it.hasNext();) {
    416                 if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
    417                     return false;
    418                 } else {
    419                     continue;
    420                 }
    421             }
    422             return true;
    423         }
    424     }
    425 }