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.Constants;
    12  import org.objectweb.asm.ClassVisitor;
    13  import org.objectweb.asm.CodeVisitor;
    14  import org.objectweb.asm.Attribute;
    15  import org.objectweb.asm.Type;
    16  import org.objectweb.asm.CodeAdapter;
    17  import org.objectweb.asm.Label;
    18  import org.codehaus.aspectwerkz.reflect.ClassInfo;
    19  import org.codehaus.aspectwerkz.reflect.ConstructorInfo;
    20  import org.codehaus.aspectwerkz.transform.Context;
    21  import org.codehaus.aspectwerkz.transform.TransformationUtil;
    22  import org.codehaus.aspectwerkz.transform.TransformationConstants;
    23  import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
    24  import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
    25  import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
    26  import org.codehaus.aspectwerkz.expression.ExpressionContext;
    27  import org.codehaus.aspectwerkz.expression.PointcutType;
    28  import org.codehaus.aspectwerkz.definition.SystemDefinition;
    29  import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
    30
    31  import java.util.Iterator;
    32  import java.util.Set;
    33
    34  /***
    35   * Handles constructor execution weaving.
    36   * For each matching ctor, a static method is added with the same signature and with the extra thisClass parameter
    37   * prepended to the list. Then the orginal ctor body is changed to call the JP.invoke, only after call to this / super
    38   * initializers.
    39   * <p/>
    40   * TODO rename in ..execution..
    41   *
    42   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur</a>
    43   */
    44  public class ConstructorBodyVisitor extends ClassAdapter implements TransformationConstants {
    45
    46      private final ContextImpl m_ctx;
    47      private final ClassInfo m_calleeClassInfo;
    48      private String m_declaringTypeName;
    49      private Set m_addedMethods;
    50
    51      /***
    52       * Creates a new instance.
    53       *
    54       * @param cv
    55       * @param classInfo
    56       * @param ctx
    57       * @param addedMethods
    58       */
    59      public ConstructorBodyVisitor(final ClassVisitor cv,
    60                                    final ClassInfo classInfo,
    61                                    final Context ctx,
    62                                    final Set addedMethods) {
    63          super(cv);
    64          m_calleeClassInfo = classInfo;
    65          m_ctx = (ContextImpl) ctx;
    66          m_addedMethods = addedMethods;
    67      }
    68
    69      /***
    70       * Visits the class.
    71       *
    72       * @param access
    73       * @param name
    74       * @param superName
    75       * @param interfaces
    76       * @param sourceFile
    77       */
    78      public void visit(final int version,
    79                        final int access,
    80                        final String name,
    81                        final String superName,
    82                        final String[] interfaces,
    83                        final String sourceFile) {
    84          m_declaringTypeName = name;
    85          super.visit(version, access, name, superName, interfaces, sourceFile);
    86      }
    87
    88      /***
    89       * @param access
    90       * @param name
    91       * @param desc
    92       * @param exceptions
    93       * @param attrs
    94       * @return
    95       */
    96      public CodeVisitor visitMethod(int access,
    97                                     String name,
    98                                     String desc,
    99                                     String[] exceptions,
    100                                    Attribute attrs) {
    101         if (!INIT_METHOD_NAME.equals(name)) {
    102             return super.visitMethod(access, name, desc, exceptions, attrs);
    103         }
    104
    105         int hash = AsmHelper.calculateConstructorHash(desc);
    106         ConstructorInfo constructorInfo = m_calleeClassInfo.getConstructor(hash);
    107         if (constructorInfo == null) {
    108             System.err.println(
    109                     "AW::WARNING " +
    110                     "metadata structure could not be build for constructor ["
    111                     + m_calleeClassInfo.getName().replace('/', '.')
    112                     + ".<init>: " + desc + ']'
    113             );
    114             return cv.visitMethod(access, name, desc, exceptions, attrs);
    115         }
    116
    117         ExpressionContext ctx = new ExpressionContext(PointcutType.EXECUTION, constructorInfo, constructorInfo);
    118
    119         if (constructorFilter(m_ctx.getDefinitions(), ctx)) {
    120             return cv.visitMethod(access, name, desc, exceptions, attrs);
    121         } else {
    122             String wrapperName = TransformationUtil.getConstructorBodyMethodName(m_declaringTypeName);
    123             String wrapperDesc = TransformationUtil.getConstructorBodyMethodSignature(desc, m_declaringTypeName);
    124             if (m_addedMethods.contains(AlreadyAddedMethodAdapter.getMethodKey(wrapperName, wrapperDesc))) {
    125                 return cv.visitMethod(access, name, desc, exceptions, attrs);
    126             }
    127
    128             m_ctx.markAsAdvised();
    129
    130             // create the proxy constructor for the original constructor
    131             CodeVisitor proxyCtorCodeVisitor = cv.visitMethod(access, name, desc, exceptions, attrs);
    132             // create the ctorBodyMethod for the original constructor body
    133             int modifiers = ACC_SYNTHETIC | ACC_STATIC;
    134             CodeVisitor ctorBodyMethodCodeVisitor = cv.visitMethod(
    135                     modifiers, wrapperName, wrapperDesc, exceptions, attrs
    136             );
    137
    138             // return a dispatch Code Adapter in between the orginal one and both of them
    139             return new DispatchCtorBodyCodeAdapter(proxyCtorCodeVisitor, ctorBodyMethodCodeVisitor, access, desc);
    140         }
    141     }
    142
    143
    144     /***
    145      * Creates the "proxy constructor" join point invocation body
    146      *
    147      * @param ctorProxy
    148      * @param access
    149      * @param desc
    150      */
    151     private void insertJoinPointInvoke(final CodeVisitor ctorProxy,
    152                                        final int access,
    153                                        final String desc) {
    154         // load "this"
    155         ctorProxy.visitVarInsn(ALOAD, 0);// is too simple f.e. when DUP was used
    156         // load args
    157         AsmHelper.loadArgumentTypes(ctorProxy, Type.getArgumentTypes(desc), false);
    158
    159         // caller = callee
    160         ctorProxy.visitVarInsn(ALOAD, 0);
    161
    162         int joinPointHash = AsmHelper.calculateConstructorHash(desc);
    163         String joinPointClassName = TransformationUtil.getJoinPointClassName(
    164                 m_declaringTypeName,
    165                 INIT_METHOD_NAME,
    166                 desc,
    167                 m_declaringTypeName,
    168                 JoinPointType.CONSTRUCTOR_EXECUTION_INT,
    169                 joinPointHash
    170         );
    171
    172         ctorProxy.visitMethodInsn(
    173                 INVOKESTATIC,
    174                 joinPointClassName,
    175                 INVOKE_METHOD_NAME,
    176                 TransformationUtil.getInvokeSignatureForCodeJoinPoints(
    177                         access, desc, m_declaringTypeName, m_declaringTypeName
    178                 )
    179         );
    180
    181         // emit the joinpoint
    182         m_ctx.addEmittedJoinPoint(
    183                 new EmittedJoinPoint(
    184                         JoinPointType.CONSTRUCTOR_EXECUTION_INT,
    185                         m_declaringTypeName,
    186                         TransformationConstants.INIT_METHOD_NAME,
    187                         desc,
    188                         access,
    189                         m_declaringTypeName,
    190                         TransformationConstants.INIT_METHOD_NAME,
    191                         desc,
    192                         access,
    193                         joinPointHash,
    194                         joinPointClassName,
    195                         EmittedJoinPoint.NO_LINE_NUMBER
    196                 )
    197         );
    198     }
    199
    200     /***
    201      * Filters out the constructor that are not eligible for transformation.
    202      *
    203      * @param definitions
    204      * @param ctx
    205      * @return boolean true if the constructor should be filtered out
    206      */
    207     public static boolean constructorFilter(final Set definitions,
    208                                             final ExpressionContext ctx) {
    209         for (Iterator it = definitions.iterator(); it.hasNext();) {
    210             if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
    211                 return false;
    212             } else {
    213                 continue;
    214             }
    215         }
    216         return true;
    217     }
    218
    219     /***
    220      * A class that dispatch the ctor body instruction to any other given code visitor
    221      * </p>
    222      * The behavior is like this:
    223      * 1/ as long as the INVOKESPECIAL for the object initialization has not been reached, every bytecode
    224      * instruction is dispatched in the ctor code visitor. [note 1]
    225      * 2/ when this one is reached, it is only added in the ctor code visitor and a JP invoke is added
    226      * 3/ after that, only the other code visitor receives the instructions
    227      * </p>
    228      * [note 1] To support schemes like http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#9839
    229      * where the stack is like ALOAD_0 + DUP, we handle a special case.
    230      * f.e. CGlib proxy ctor are like that..
    231      * Don't know if some other info can be left on the stack (f.e. ILOAD 1, DUP ...)
    232      */
    233     private class DispatchCtorBodyCodeAdapter extends CodeAdapter {
    234         private CodeVisitor m_ctorBodyMethodCodeVisitor;
    235         private CodeVisitor m_proxyCtorCodeVisitor;
    236         private final int m_constructorAccess;
    237         private final String m_constructorDesc;
    238
    239         private int m_newCount = 0;
    240
    241         private boolean m_proxyCtorCodeDone = false;
    242         private boolean m_isALOADDUPHeuristic = false;
    243         private int m_index = -1;
    244
    245         public DispatchCtorBodyCodeAdapter(CodeVisitor proxyCtor, CodeVisitor ctorBodyMethod, final int access,
    246                                            final String desc) {
    247             super(proxyCtor);
    248             m_proxyCtorCodeVisitor = proxyCtor;
    249             m_ctorBodyMethodCodeVisitor = ctorBodyMethod;
    250             m_constructorAccess = access;
    251             m_constructorDesc = desc;
    252         }
    253
    254         public void visitInsn(int opcode) {
    255             super.visitInsn(opcode);
    256             if (!m_proxyCtorCodeDone && opcode == DUP && m_index == 0) {
    257                 // heuristic for ALOAD_0 + DUP confirmed
    258                 m_isALOADDUPHeuristic = true;
    259                 m_index++;
    260             }
    261         }
    262
    263         public void visitIntInsn(int i, int i1) {
    264             super.visitIntInsn(i, i1);
    265         }
    266
    267         public void visitVarInsn(int opcode, int i1) {
    268             super.visitVarInsn(opcode, i1);
    269             if (!m_proxyCtorCodeDone) {
    270                 if (opcode == ALOAD && i1 == 0) {
    271                     m_index++;
    272                 }
    273             }
    274         }
    275
    276         public void visitFieldInsn(int i, String s, String s1, String s2) {
    277             super.visitFieldInsn(i, s, s1, s2);
    278         }
    279
    280         public void visitLdcInsn(Object o) {
    281             super.visitLdcInsn(o);
    282         }
    283
    284         public void visitIincInsn(int i, int i1) {
    285             super.visitIincInsn(i, i1);
    286         }
    287
    288         public void visitMultiANewArrayInsn(String s, int i) {
    289             super.visitMultiANewArrayInsn(s, i);
    290         }
    291
    292         /***
    293          * Visit NEW type to ignore corresponding INVOKESPECIAL for those
    294          *
    295          * @param opcode
    296          * @param name
    297          */
    298         public void visitTypeInsn(int opcode, String name) {
    299             super.visitTypeInsn(opcode, name);
    300             if (opcode == NEW) {
    301                 m_newCount++;
    302             }
    303         }
    304
    305         public void visitMethodInsn(int opcode,
    306                                     String owner,
    307                                     String name,
    308                                     String desc) {
    309             if (!m_proxyCtorCodeDone) {
    310                 if (opcode == INVOKESPECIAL) {
    311                     if (m_newCount == 0) {
    312                         // first INVOKESPECIAL encountered to <init> for a NON new XXX()
    313                         m_proxyCtorCodeVisitor.visitMethodInsn(opcode, owner, name, desc);
    314                         // insert the JoinPoint invocation
    315                         insertJoinPointInvoke(m_proxyCtorCodeVisitor, m_constructorAccess, m_constructorDesc);
    316                         m_proxyCtorCodeVisitor.visitInsn(RETURN);
    317                         m_proxyCtorCodeVisitor.visitMaxs(0, 0);
    318                         m_proxyCtorCodeVisitor = null;
    319                         m_proxyCtorCodeDone = true;
    320                         cv = m_ctorBodyMethodCodeVisitor;
    321                         // load ALOAD 0 if under heuristic
    322                         if (m_isALOADDUPHeuristic) {
    323                             m_ctorBodyMethodCodeVisitor.visitVarInsn(ALOAD, 0);
    324                         }
    325                     } else {
    326                         m_newCount--;
    327                         cv.visitMethodInsn(opcode, owner, name, desc);
    328                     }
    329                 } else {
    330                     cv.visitMethodInsn(opcode, owner, name, desc);
    331                 }
    332             } else {
    333                 cv.visitMethodInsn(opcode, owner, name, desc);
    334             }
    335         }
    336
    337     }
    338 }