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.reflect.impl.asm;
    9
    10  import org.codehaus.aspectwerkz.reflect.ClassInfo;
    11  import org.codehaus.aspectwerkz.reflect.MethodInfo;
    12  import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
    13  import org.codehaus.aspectwerkz.annotation.instrumentation.asm.AsmAnnotationHelper;
    14  import org.codehaus.aspectwerkz.exception.DefinitionException;
    15  import org.codehaus.aspectwerkz.proxy.ProxyCompiler;
    16  import org.objectweb.asm.Type;
    17  import org.objectweb.asm.ClassReader;
    18  import org.objectweb.asm.attrs.Attributes;
    19
    20  import java.util.List;
    21  import java.util.ArrayList;
    22  import java.io.IOException;
    23  import java.io.InputStream;
    24  import java.lang.reflect.Modifier;
    25
    26  /***
    27   * ASM implementation of the MethodInfo interface.
    28   *
    29   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    30   */
    31  public class AsmMethodInfo extends AsmMemberInfo implements MethodInfo {
    32
    33      /***
    34       * The return type name.
    35       */
    36      private String m_returnTypeName = null;
    37
    38      /***
    39       * A list with the parameter names as they appear in the source code.
    40       * This information may not be available.
    41       */
    42      protected String[] m_parameterNames = null;
    43
    44      /***
    45       * A list with the parameter type names.
    46       */
    47      private String[] m_parameterTypeNames = null;
    48
    49      /***
    50       * A list with the exception type names.
    51       */
    52      private String[] m_exceptionTypeNames = null;
    53
    54      /***
    55       * The return type.
    56       */
    57      private ClassInfo m_returnType = null;
    58
    59      /***
    60       * A list with the parameter types.
    61       */
    62      private ClassInfo[] m_parameterTypes = null;
    63
    64      /***
    65       * A list with the exception types.
    66       */
    67      private ClassInfo[] m_exceptionTypes = null;
    68
    69      /***
    70       * Creates a new method info instance.
    71       *
    72       * @param method
    73       * @param declaringType
    74       * @param loader
    75       */
    76      AsmMethodInfo(final MethodStruct method, final String declaringType, final ClassLoader loader) {
    77          super(method, declaringType, loader);
    78
    79          m_returnTypeName = Type.getReturnType(method.desc).getClassName();
    80          Type[] argTypes = Type.getArgumentTypes(method.desc);
    81          m_parameterTypeNames = new String[argTypes.length];
    82          for (int i = 0; i < argTypes.length; i++) {
    83              m_parameterTypeNames[i] = argTypes[i].getClassName();
    84          }
    85          // FIXME: how to do exceptions? needed?
    86          m_exceptionTypeNames = new String[]{};
    87      }
    88
    89      /***
    90       * Returns the method info for the method specified.
    91       *
    92       * @param methodName
    93       * @param methodDesc
    94       * @param bytecode
    95       * @param loader
    96       * @return the method info
    97       */
    98      public static MethodInfo getMethodInfo(final String methodName,
    99                                             final String methodDesc,
    100                                            final byte[] bytecode,
    101                                            final ClassLoader loader) {
    102         String className = AsmClassInfo.retrieveClassNameFromBytecode(bytecode);
    103         AsmClassInfoRepository repository = AsmClassInfoRepository.getRepository(loader);
    104         ClassInfo classInfo = repository.getClassInfo(className);
    105         if (classInfo == null) {
    106             classInfo = AsmClassInfo.getClassInfo(bytecode, loader);
    107         }
    108         return classInfo.getMethod(AsmHelper.calculateMethodHash(methodName, methodDesc));
    109     }
    110
    111     /***
    112      * Returns the signature for the element.
    113      *
    114      * @return the signature for the element
    115      */
    116     public String getSignature() {
    117         return AsmHelper.getMethodDescriptor(this);
    118     }
    119
    120     /***
    121      * Returns the return type.
    122      *
    123      * @return the return type
    124      */
    125     public ClassInfo getReturnType() {
    126         if (m_returnType == null) {
    127             m_returnType = AsmClassInfo.getClassInfo(m_returnTypeName, (ClassLoader) m_loaderRef.get());
    128         }
    129         return m_returnType;
    130     }
    131
    132     /***
    133      * Returns the parameter types.
    134      *
    135      * @return the parameter types
    136      */
    137     public ClassInfo[] getParameterTypes() {
    138         if (m_parameterTypes == null) {
    139             m_parameterTypes = new ClassInfo[m_parameterTypeNames.length];
    140             for (int i = 0; i < m_parameterTypeNames.length; i++) {
    141                 m_parameterTypes[i] = AsmClassInfo.getClassInfo(
    142                         m_parameterTypeNames[i],
    143                         (ClassLoader) m_loaderRef.get()
    144                 );
    145             }
    146         }
    147         return m_parameterTypes;
    148     }
    149
    150     /***
    151      * Returns the parameter names as they appear in the source code.
    152      * This information is available only when class are compiled with javac -g (debug info), but is required
    153      * for Aspect that are using args() and target()/this() bindings.
    154      * <p/>
    155      * It returns null if not available.
    156      *
    157      * @return
    158      */
    159     public String[] getParameterNames() {
    160         return m_parameterNames;
    161     }
    162
    163     /***
    164      * Returns the exception types.
    165      *
    166      * @return the exception types
    167      */
    168     public ClassInfo[] getExceptionTypes() {
    169         if (m_exceptionTypes == null) {
    170             m_exceptionTypes = new ClassInfo[m_exceptionTypeNames.length];
    171             for (int i = 0; i < m_exceptionTypeNames.length; i++) {
    172                 m_exceptionTypes[i] = AsmClassInfo.getClassInfo(
    173                         m_exceptionTypeNames[i],
    174                         (ClassLoader) m_loaderRef.get()
    175                 );
    176             }
    177         }
    178         return m_exceptionTypes;
    179     }
    180
    181     /***
    182      * Returns the annotations.
    183      *
    184      * @return the annotations
    185      */
    186     public List getAnnotations() {
    187         if (m_annotations == null) {
    188             try {
    189                 InputStream in = null;
    190                 ClassReader cr = null;
    191                 try {
    192                     if ((ClassLoader) m_loaderRef.get() != null) {
    193                         in = ((ClassLoader) m_loaderRef.get()).getResourceAsStream(
    194                                 m_declaringTypeName.replace('.', '/') + ".class"
    195                         );
    196                     } else {
    197                         in = ClassLoader.getSystemClassLoader().getResourceAsStream(
    198                                 m_declaringTypeName.replace('.', '/') + ".class"
    199                         );
    200                     }
    201                     if (in == null) {
    202                         in = ProxyCompiler.getProxyResourceAsStream((ClassLoader) m_loaderRef.get(), m_declaringTypeName);
    203                     }
    204                     cr = new ClassReader(in);
    205                 } finally {
    206                     try {
    207                         in.close();
    208                     } catch (Exception e) {
    209                         ;
    210                     }
    211                 }
    212                 List annotations = new ArrayList();
    213                 cr.accept(
    214                         new AsmAnnotationHelper.MethodAnnotationExtractor(
    215                                 annotations, m_member.name, m_member.desc, (ClassLoader) m_loaderRef.get()
    216                         ),
    217                         Attributes.getDefaultAttributes(),
    218                         true
    219                 );
    220                 m_annotations = annotations;
    221             } catch (IOException e) {
    222                 // unlikely to occur since ClassInfo relies on getResourceAsStream
    223                 System.err.println(
    224                         "WARN - could not load " + m_declaringTypeName + " as a resource to retrieve annotations"
    225                 );
    226                 m_annotations = AsmClassInfo.EMPTY_LIST;
    227             }
    228         }
    229         return m_annotations;
    230     }
    231
    232     public boolean equals(Object o) {
    233         if (this == o) {
    234             return true;
    235         }
    236         if (!(o instanceof MethodInfo)) {
    237             return false;
    238         }
    239         MethodInfo methodInfo = (MethodInfo) o;
    240         if (!m_declaringTypeName.equals(methodInfo.getDeclaringType().getName())) {
    241             return false;
    242         }
    243         if (!m_member.name.equals(methodInfo.getName())) {
    244             return false;
    245         }
    246         ClassInfo[] parameterTypes = methodInfo.getParameterTypes();
    247         if (m_parameterTypeNames.length != parameterTypes.length) {//check on names length for lazyness optim
    248             return false;
    249         }
    250         for (int i = 0; i < m_parameterTypeNames.length; i++) {
    251             if (!m_parameterTypeNames[i].equals(parameterTypes[i].getName())) {
    252                 return false;
    253             }
    254         }
    255         return true;
    256     }
    257
    258     public int hashCode() {
    259         int result = 29;
    260         result = (29 * result) + m_declaringTypeName.hashCode();
    261         result = (29 * result) + m_member.name.hashCode();
    262         for (int i = 0; i < m_parameterTypeNames.length; i++) {
    263             result = (29 * result) + m_parameterTypeNames[i].hashCode();
    264         }
    265         return result;
    266     }
    267
    268     public String toString() {
    269         StringBuffer sb = new StringBuffer(m_declaringTypeName);
    270         sb.append('.').append(m_member.name);
    271         sb.append(m_member.desc);
    272         return sb.toString();
    273     }
    274
    275     /***
    276      * Update the parameter name given the parameter information
    277      * the index is the one from the register ie a long or double will needs 2 register
    278      *
    279      * @param registerIndex
    280      * @param parameterName
    281      */
    282     public void pushParameterNameFromRegister(int registerIndex, String parameterName) {
    283         int registerStart = 1;
    284         if (Modifier.isStatic(m_member.modifiers)) {
    285             registerStart = 0;
    286         }
    287         // assume we have a stack starting at the first parameter
    288         int registerIndexFrom0 = registerIndex - registerStart;
    289         Type[] parameters = Type.getArgumentTypes(m_member.desc);
    290         int typeIndex = AsmHelper.getTypeIndexOf(parameters, registerIndexFrom0);
    291         if (typeIndex >= 0 && typeIndex < m_parameterNames.length) {
    292             m_parameterNames[typeIndex] = parameterName;
    293         } else {
    294             throw new DefinitionException("Could not register parameter named " + parameterName
    295                 + " from register " + registerIndex + " for " + m_member.name + "." + m_member.desc);
    296         }
    297     }
    298 }