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 gnu.trove.TIntObjectHashMap;
    11  import gnu.trove.TIntArrayList;
    12  import org.codehaus.aspectwerkz.annotation.instrumentation.asm.AsmAnnotationHelper;
    13  import org.codehaus.aspectwerkz.annotation.AnnotationInfo;
    14  import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
    15  import org.codehaus.aspectwerkz.reflect.ClassInfo;
    16  import org.codehaus.aspectwerkz.reflect.ConstructorInfo;
    17  import org.codehaus.aspectwerkz.reflect.FieldInfo;
    18  import org.codehaus.aspectwerkz.reflect.MethodInfo;
    19  import org.codehaus.aspectwerkz.reflect.StaticInitializationInfo;
    20  import org.codehaus.aspectwerkz.reflect.StaticInitializationInfoImpl;
    21  import org.codehaus.aspectwerkz.reflect.impl.java.JavaClassInfo;
    22  import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
    23  import org.codehaus.aspectwerkz.transform.TransformationConstants;
    24  import org.codehaus.aspectwerkz.proxy.ProxyCompiler;
    25  import org.objectweb.asm.Attribute;
    26  import org.objectweb.asm.ClassReader;
    27  import org.objectweb.asm.CodeVisitor;
    28  import org.objectweb.asm.Label;
    29  import org.objectweb.asm.Type;
    30  import org.objectweb.asm.attrs.Annotation;
    31  import org.objectweb.asm.attrs.Attributes;
    32
    33  import java.io.IOException;
    34  import java.io.InputStream;
    35  import java.lang.ref.WeakReference;
    36  import java.lang.reflect.Array;
    37  import java.lang.reflect.Modifier;
    38  import java.util.ArrayList;
    39  import java.util.List;
    40  import java.util.Iterator;
    41
    42  /***
    43   * Implementation of the ClassInfo interface utilizing the ASM bytecode library for the info retriaval.
    44   * <p/>
    45   * Annotations are lazily gathered, unless required to visit them at the same time as we visit methods and fields.
    46   * <p/>
    47   * This implementation guarantees that the method, fields and constructors can be retrieved in the same order as they were in the bytecode
    48   * (it can depends of the compiler and might not be the order of the source code - f.e. IBM compiler)
    49   *
    50   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    51   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
    52   */
    53  public class AsmClassInfo implements ClassInfo {
    54
    55      protected final static String[] EMPTY_STRING_ARRAY = new String[0];
    56
    57      protected final static List EMPTY_LIST = new ArrayList();
    58
    59      private final static Attribute[] NO_ATTRIBUTES = new Attribute[0];
    60
    61      /***
    62       * The class loader wrapped in a weak ref.
    63       */
    64      private final WeakReference m_loaderRef;
    65
    66      /***
    67       * The name of the class (with dots and not slashes)
    68       */
    69      private String m_name;
    70
    71      /***
    72       * The signature of the class.
    73       */
    74      private String m_signature;
    75
    76      /***
    77       * The modifiers.
    78       */
    79      private int m_modifiers;
    80
    81      /***
    82       * Is the class an interface.
    83       */
    84      private boolean m_isInterface = false;
    85
    86      /***
    87       * Is the class a primitive type.
    88       */
    89      private boolean m_isPrimitive = false;
    90
    91      /***
    92       * Is the class of type array.
    93       */
    94      private boolean m_isArray = false;
    95
    96      /***
    97       * Flag for the static initializer method.
    98       */
    99      private boolean m_hasStaticInitializer = false;
    100
    101     /***
    102      * Lazy instance that represents the static initializer if present, else null
    103      */
    104     private StaticInitializationInfo m_staticInitializer = null;
    105
    106     /***
    107      * A list with the <code>ConstructorInfo</code> instances.
    108      * When visiting the bytecode, we keep track of the order of the visit.
    109      * The first time the getConstructors() gets called, we build an array and then reuse it directly.
    110      */
    111     private final TIntObjectHashMap m_constructors = new TIntObjectHashMap();
    112     private TIntArrayList m_sortedConstructorHashes = new TIntArrayList();
    113     private ConstructorInfo[] m_constructorsLazy = null;
    114
    115
    116     /***
    117      * A list with the <code>MethodInfo</code> instances.
    118      * When visiting the bytecode, we keep track of the order of the visit.
    119      * The first time the getMethods() gets called, we build an array and then reuse it directly.
    120      */
    121     private final TIntObjectHashMap m_methods = new TIntObjectHashMap();
    122     private TIntArrayList m_sortedMethodHashes = new TIntArrayList();
    123     private MethodInfo[] m_methodsLazy = null;
    124
    125     /***
    126      * A list with the <code>FieldInfo</code> instances.
    127      * When visiting the bytecode, we keep track of the order of the visit.
    128      * The first time the getFields() gets called, we build an array and then reuse it directly.
    129      */
    130     private final TIntObjectHashMap m_fields = new TIntObjectHashMap();
    131     private TIntArrayList m_sortedFieldHashes = new TIntArrayList();
    132     private FieldInfo[] m_fieldsLazy = null;
    133
    134     /***
    135      * A list with the interfaces class names.
    136      */
    137     private String[] m_interfaceClassNames = null;
    138
    139     /***
    140      * A list with the interfaces.
    141      */
    142     private ClassInfo[] m_interfaces = null;
    143
    144     /***
    145      * The super class name.
    146      */
    147     private String m_superClassName = null;
    148
    149     /***
    150      * The super class.
    151      */
    152     private ClassInfo m_superClass = null;
    153
    154     /***
    155      * The annotations.
    156      * Lazily populated.
    157      */
    158     private List m_annotations = null;
    159
    160     /***
    161      * The component type name if array type. Can be an array itself.
    162      */
    163     private String m_componentTypeName = null;
    164
    165     /***
    166      * The component type if array type. Can be an array itself.
    167      */
    168     private ClassInfo m_componentType = null;
    169
    170     /***
    171      * The class info repository.
    172      */
    173     private final AsmClassInfoRepository m_classInfoRepository;
    174
    175     /***
    176      * Creates a new ClassInfo instance.
    177      *
    178      * @param bytecode
    179      * @param loader
    180      */
    181     AsmClassInfo(final byte[] bytecode, final ClassLoader loader, boolean lazyAttributes) {
    182         if (bytecode == null) {
    183             throw new IllegalArgumentException("bytecode can not be null");
    184         }
    185         m_loaderRef = new WeakReference(loader);
    186         m_classInfoRepository = AsmClassInfoRepository.getRepository(loader);
    187         try {
    188             ClassReader cr = new ClassReader(bytecode);
    189             ClassInfoClassAdapter visitor = new ClassInfoClassAdapter(lazyAttributes);
    190             cr.accept(visitor, lazyAttributes ? NO_ATTRIBUTES : Attributes.getDefaultAttributes(), false);
    191         } catch (Throwable t) {
    192             t.printStackTrace();
    193         }
    194         m_classInfoRepository.addClassInfo(this);
    195     }
    196
    197     /***
    198      * Creates a new ClassInfo instance.
    199      *
    200      * @param resourceStream
    201      * @param loader
    202      */
    203     AsmClassInfo(final InputStream resourceStream, final ClassLoader loader) {
    204         if (resourceStream == null) {
    205             throw new IllegalArgumentException("resource stream can not be null");
    206         }
    207         m_loaderRef = new WeakReference(loader);
    208         m_classInfoRepository = AsmClassInfoRepository.getRepository(loader);
    209         try {
    210             ClassReader cr = new ClassReader(resourceStream);
    211             ClassInfoClassAdapter visitor = new ClassInfoClassAdapter(true);
    212             cr.accept(visitor, NO_ATTRIBUTES, false);
    213         } catch (Throwable t) {
    214             t.printStackTrace();
    215         }
    216         m_classInfoRepository.addClassInfo(this);
    217     }
    218
    219     /***
    220      * Create a ClassInfo based on a component type and a given dimension Due to java.lang.reflect. behavior, the
    221      * ClassInfo is almost empty. It is not an interface, only subclass of java.lang.Object, no methods, fields, or
    222      * constructor, no annotation.
    223      *
    224      * @param className
    225      * @param loader
    226      * @param componentInfo
    227      */
    228     AsmClassInfo(final String className,
    229                  final ClassLoader loader,
    230                  final ClassInfo componentInfo) {
    231         m_loaderRef = new WeakReference(loader);
    232         m_name = className.replace('/', '.');
    233         m_classInfoRepository = AsmClassInfoRepository.getRepository(loader);
    234         m_isArray = true;
    235         m_componentType = componentInfo;
    236         m_componentTypeName = componentInfo.getName();
    237         m_modifiers = componentInfo.getModifiers() | Modifier.ABSTRACT | Modifier.FINAL;
    238         m_isInterface = false;//as in java.reflect
    239         m_superClass = JavaClassInfo.getClassInfo(Object.class);
    240         m_superClassName = m_superClass.getName();
    241         m_interfaceClassNames = new String[0];
    242         m_interfaces = new ClassInfo[0];
    243         m_signature = AsmHelper.getClassDescriptor(this);
    244         m_classInfoRepository.addClassInfo(this);
    245     }
    246
    247     /***
    248      * Returns a completely new class info for a specific class. Does not cache.
    249      *
    250      * @param bytecode
    251      * @param loader
    252      * @return the class info
    253      */
    254     public static ClassInfo newClassInfo(final byte[] bytecode, final ClassLoader loader) {
    255         final String className = AsmClassInfo.retrieveClassNameFromBytecode(bytecode);
    256         AsmClassInfoRepository repository = AsmClassInfoRepository.getRepository(loader);
    257         repository.removeClassInfo(className);
    258         return new AsmClassInfo(bytecode, loader, true);
    259     }
    260
    261     /***
    262      * Returns the class info for a specific class.
    263      *
    264      * @param className
    265      * @param loader
    266      * @return the class info
    267      */
    268     public static ClassInfo getClassInfo(final String className, final ClassLoader loader) {
    269         final String javaClassName = getJavaClassName(className);
    270         AsmClassInfoRepository repository = AsmClassInfoRepository.getRepository(loader);
    271         ClassInfo classInfo = repository.getClassInfo(javaClassName);
    272         if (classInfo == null) {
    273             classInfo = createClassInfoFromStream(javaClassName, loader, true);
    274         }
    275         return classInfo;
    276     }
    277
    278     /***
    279      * Returns the class info for a specific class.
    280      *
    281      * @param bytecode
    282      * @param loader
    283      * @return the class info
    284      */
    285     public static ClassInfo getClassInfo(final byte[] bytecode, final ClassLoader loader) {
    286         final String className = AsmClassInfo.retrieveClassNameFromBytecode(bytecode);
    287         AsmClassInfoRepository repository = AsmClassInfoRepository.getRepository(loader);
    288         ClassInfo classInfo = repository.getClassInfo(className);
    289         if (classInfo == null) {
    290             classInfo = new AsmClassInfo(bytecode, loader, true);
    291         }
    292         return classInfo;
    293     }
    294
    295     /***
    296      * Returns the class info for a specific class.
    297      *
    298      * @param stream
    299      * @param loader
    300      * @return the class info
    301      */
    302     public static ClassInfo getClassInfo(final InputStream stream, final ClassLoader loader) {
    303         try {
    304             ClassReader cr = new ClassReader(stream);
    305             // keep a copy of the bytecode, since me way want to "reuse the stream"
    306             byte[] bytes = cr.b;
    307             ClassNameRetrievalClassAdapter visitor = new ClassNameRetrievalClassAdapter();
    308             cr.accept(visitor, NO_ATTRIBUTES, true);
    309             final String className = visitor.getClassName();
    310             AsmClassInfoRepository repository = AsmClassInfoRepository.getRepository(loader);
    311             ClassInfo classInfo = repository.getClassInfo(className);
    312             if (classInfo == null) {
    313                 classInfo = new AsmClassInfo(bytes, loader, true);
    314             }
    315             return classInfo;
    316         } catch (IOException e) {
    317             throw new WrappedRuntimeException(e);
    318         }
    319     }
    320
    321     /***
    322      * Returns the class info for a specific class.
    323      *
    324      * @param stream
    325      * @param loader
    326      * @param lazyAttributes
    327      * @return the class info
    328      */
    329     public static ClassInfo getClassInfo(final InputStream stream, final ClassLoader loader, boolean lazyAttributes) {
    330         if (lazyAttributes) {
    331             return getClassInfo(stream, loader);
    332         }
    333         try {
    334             ClassReader cr = new ClassReader(stream);
    335             // keep a copy of the bytecode, since me way want to "reuse the stream"
    336             byte[] bytes = cr.b;
    337             ClassNameRetrievalClassAdapter visitor = new ClassNameRetrievalClassAdapter();
    338             cr.accept(visitor, NO_ATTRIBUTES, true);
    339             String className = visitor.getClassName();
    340             AsmClassInfoRepository repository = AsmClassInfoRepository.getRepository(loader);
    341             ClassInfo classInfo = repository.getClassInfo(className);
    342             if (classInfo == null) {
    343                 classInfo = new AsmClassInfo(bytes, loader, lazyAttributes);
    344             }
    345             return classInfo;
    346         } catch (IOException e) {
    347             throw new WrappedRuntimeException(e);
    348         }
    349     }
    350
    351     /***
    352      * Marks the class as dirty (since it has been modified and needs to be rebuild).
    353      *
    354      * @param className
    355      */
    356     public static void markDirty(final String className, final ClassLoader loader) {
    357         AsmClassInfoRepository.getRepository(loader).removeClassInfo(className);
    358     }
    359
    360     /***
    361      * Retrieves the class name from the bytecode of a class.
    362      *
    363      * @param bytecode
    364      * @return the class name
    365      */
    366     public static String retrieveClassNameFromBytecode(final byte[] bytecode) {
    367         ClassReader cr = new ClassReader(bytecode);
    368         ClassNameRetrievalClassAdapter visitor = new ClassNameRetrievalClassAdapter();
    369         cr.accept(visitor, NO_ATTRIBUTES, true);
    370         return visitor.getClassName();
    371     }
    372
    373     /***
    374      * Checks if the class is a of a primitive type, if so create and return the class for the type else return null.
    375      *
    376      * @param className
    377      * @return the class for the primitive type or null
    378      */
    379     public static Class getPrimitiveClass(final String className) {
    380         if (className.equals("void")) {
    381             return void.class;
    382         } else if (className.equals("long")) {
    383             return long.class;
    384         } else if (className.equals("int")) {
    385             return int.class;
    386         } else if (className.equals("short")) {
    387             return short.class;
    388         } else if (className.equals("double")) {
    389             return double.class;
    390         } else if (className.equals("float")) {
    391             return float.class;
    392         } else if (className.equals("byte")) {
    393             return byte.class;
    394         } else if (className.equals("boolean")) {
    395             return boolean.class;
    396         } else if (className.equals("char")) {
    397             return char.class;
    398         } else {
    399             return null;
    400         }
    401     }
    402
    403     /***
    404      * Returns the annotations infos.
    405      *
    406      * @return the annotations infos
    407      */
    408     public List getAnnotations() {
    409         if (m_annotations == null) {
    410             if (isPrimitive() || isArray()) {
    411                 m_annotations = EMPTY_LIST;
    412             } else {
    413                 try {
    414                     InputStream in = null;
    415                     ClassReader cr = null;
    416                     try {
    417                         if ((ClassLoader) m_loaderRef.get() != null) {
    418                             in =
    419                             ((ClassLoader) m_loaderRef.get()).getResourceAsStream(m_name.replace('.', '/') + ".class");
    420                         } else {
    421                             in = ClassLoader.getSystemClassLoader().getResourceAsStream(m_name.replace('.', '/') + ".class");
    422                         }
    423                         if (in == null) {
    424                             in = ProxyCompiler.getProxyResourceAsStream((ClassLoader) m_loaderRef.get(), m_name);
    425                         }
    426                         cr = new ClassReader(in);
    427                     } finally {
    428                         try {
    429                             in.close();
    430                         } catch (Exception e) {
    431                             ;
    432                         }
    433                     }
    434                     List annotations = new ArrayList();
    435                     cr.accept(
    436                             new AsmAnnotationHelper.ClassAnnotationExtractor(
    437                                     annotations, (ClassLoader) m_loaderRef.get()
    438                             ),
    439                             Attributes.getDefaultAttributes(),
    440                             true
    441                     );
    442                     m_annotations = annotations;
    443                 } catch (IOException e) {
    444                     // unlikely to occur since ClassInfo relies on getResourceAsStream
    445                     System.err.println(
    446                             "AW::WARNING  could not load " + m_name + " as a resource to retrieve annotations"
    447                     );
    448                     m_annotations = EMPTY_LIST;
    449                 }
    450             }
    451         }
    452         return m_annotations;
    453     }
    454
    455     /***
    456      * Returns the name of the class.
    457      *
    458      * @return the name of the class
    459      */
    460     public String getName() {
    461         return m_name;
    462     }
    463
    464     /***
    465      * Returns the signature for the class.
    466      *
    467      * @return the signature for the class
    468      */
    469     public String getSignature() {
    470         return m_signature;
    471     }
    472
    473     /***
    474      * Returns the class modifiers.
    475      *
    476      * @return the class modifiers
    477      */
    478     public int getModifiers() {
    479         return m_modifiers;
    480     }
    481
    482     /***
    483      * Returns the class loader that loaded this class.
    484      *
    485      * @return the class loader
    486      */
    487     public ClassLoader getClassLoader() {
    488         return (ClassLoader) m_loaderRef.get();
    489     }
    490
    491     /***
    492      * Checks if the class has a static initalizer.
    493      *
    494      * @return
    495      */
    496     public boolean hasStaticInitializer() {
    497         return m_hasStaticInitializer;
    498     }
    499
    500 	/***
    501      * Return the static initializer info or null if not present
    502      *
    503 	 * @see org.codehaus.aspectwerkz.reflect.ClassInfo#staticInitializer()
    504 	 */
    505 	public StaticInitializationInfo staticInitializer() {
    506 		if(hasStaticInitializer() && m_staticInitializer == null) {
    507 			m_staticInitializer = new StaticInitializationInfoImpl(this);
    508 		}
    509 		return m_staticInitializer;
    510 	}
    511
    512     /***
    513      * Returns a constructor info by its hash.
    514      *
    515      * @param hash
    516      * @return
    517      */
    518     public ConstructorInfo getConstructor(final int hash) {
    519         ConstructorInfo constructor = (ConstructorInfo) m_constructors.get(hash);
    520         if (constructor == null && getSuperclass() != null) {
    521             constructor = getSuperclass().getConstructor(hash);
    522         }
    523         return constructor;
    524     }
    525
    526     /***
    527      * Returns a list with all the constructors info.
    528      *
    529      * @return the constructors info
    530      */
    531     public ConstructorInfo[] getConstructors() {
    532         if (m_constructorsLazy == null) {
    533             ConstructorInfo[] constructorInfos = new ConstructorInfo[m_sortedConstructorHashes.size()];
    534             for (int i = 0; i < m_sortedConstructorHashes.size(); i++) {
    535                 constructorInfos[i] = (ConstructorInfo) m_constructors.get(m_sortedConstructorHashes.get(i));
    536             }
    537             m_constructorsLazy = constructorInfos;
    538         }
    539         return m_constructorsLazy;
    540     }
    541
    542     /***
    543      * Returns a method info by its hash.
    544      *
    545      * @param hash
    546      * @return
    547      */
    548     public MethodInfo getMethod(final int hash) {
    549         MethodInfo method = (MethodInfo) m_methods.get(hash);
    550         if (method == null) {
    551             for (int i = 0; i < getInterfaces().length; i++) {
    552                 method = getInterfaces()[i].getMethod(hash);
    553                 if (method != null) {
    554                     break;
    555                 }
    556             }
    557         }
    558         if (method == null && getSuperclass() != null) {
    559             method = getSuperclass().getMethod(hash);
    560         }
    561         return method;
    562     }
    563
    564     /***
    565      * Returns a list with all the methods info.
    566      *
    567      * @return the methods info
    568      */
    569     public MethodInfo[] getMethods() {
    570         if (m_methodsLazy == null) {
    571             MethodInfo[] methodInfos = new MethodInfo[m_sortedMethodHashes.size()];
    572             for (int i = 0; i < m_sortedMethodHashes.size(); i++) {
    573                 methodInfos[i] = (MethodInfo) m_methods.get(m_sortedMethodHashes.get(i));
    574             }
    575             m_methodsLazy = methodInfos;
    576         }
    577         return m_methodsLazy;
    578     }
    579
    580     /***
    581      * Returns a field info by its hash.
    582      *
    583      * @param hash
    584      * @return
    585      */
    586     public FieldInfo getField(final int hash) {
    587         FieldInfo field = (FieldInfo) m_fields.get(hash);
    588         if (field == null && getSuperclass() != null) {
    589             field = getSuperclass().getField(hash);
    590         }
    591         return field;
    592     }
    593
    594     /***
    595      * Returns a list with all the field info.
    596      *
    597      * @return the field info
    598      */
    599     public FieldInfo[] getFields() {
    600         if (m_fieldsLazy == null) {
    601             FieldInfo[] fieldInfos = new FieldInfo[m_sortedFieldHashes.size()];
    602             for (int i = 0; i < m_sortedFieldHashes.size(); i++) {
    603                 fieldInfos[i] = (FieldInfo) m_fields.get(m_sortedFieldHashes.get(i));
    604             }
    605             m_fieldsLazy = fieldInfos;
    606         }
    607         return m_fieldsLazy;
    608     }
    609
    610     /***
    611      * Returns the interfaces.
    612      *
    613      * @return the interfaces
    614      */
    615     public ClassInfo[] getInterfaces() {
    616         if (m_interfaces == null) {
    617             m_interfaces = new ClassInfo[m_interfaceClassNames.length];
    618             for (int i = 0; i < m_interfaceClassNames.length; i++) {
    619                 m_interfaces[i] = AsmClassInfo.getClassInfo(m_interfaceClassNames[i], (ClassLoader) m_loaderRef.get());
    620             }
    621         }
    622         return m_interfaces;
    623     }
    624
    625     /***
    626      * Returns the super class.
    627      *
    628      * @return the super class
    629      */
    630     public ClassInfo getSuperclass() {
    631         if (m_superClass == null && m_superClassName != null) {
    632             m_superClass = AsmClassInfo.getClassInfo(m_superClassName, (ClassLoader) m_loaderRef.get());
    633         }
    634         return m_superClass;
    635     }
    636
    637     /***
    638      * Returns the component type if array type else null.
    639      *
    640      * @return the component type
    641      */
    642     public ClassInfo getComponentType() {
    643         if (isArray() && (m_componentTypeName == null)) {
    644             m_componentType = AsmClassInfo.getClassInfo(m_componentTypeName, (ClassLoader) m_loaderRef.get());
    645         }
    646         return m_componentType;
    647     }
    648
    649     /***
    650      * Is the class an interface.
    651      *
    652      * @return
    653      */
    654     public boolean isInterface() {
    655         return m_isInterface;
    656     }
    657
    658     /***
    659      * Is the class a primitive type.
    660      *
    661      * @return
    662      */
    663     public boolean isPrimitive() {
    664         return m_isPrimitive;
    665     }
    666
    667     /***
    668      * Is the class an array type.
    669      *
    670      * @return
    671      */
    672     public boolean isArray() {
    673         return m_isArray;
    674     }
    675
    676     /***
    677      * @see java.lang.Object#equals(java.lang.Object)
    678      */
    679     public boolean equals(Object o) {
    680         if (this == o) {
    681             return true;
    682         }
    683         if (!(o instanceof ClassInfo)) {
    684             return false;
    685         }
    686         ClassInfo classInfo = (ClassInfo) o;
    687         return m_name.equals(classInfo.getName());
    688     }
    689
    690     /***
    691      * @see java.lang.Object#hashCode()
    692      */
    693     public int hashCode() {
    694         return m_name.hashCode();
    695     }
    696
    697     public String toString() {
    698         return m_name;
    699     }
    700
    701     /***
    702      * Create a ClassInfo based on a component type which can be himself an array
    703      *
    704      * @param className
    705      * @param loader
    706      * @param componentClassInfo
    707      * @return
    708      */
    709     public static ClassInfo getArrayClassInfo(final String className,
    710                                               final ClassLoader loader,
    711                                               final ClassInfo componentClassInfo) {
    712         return new AsmClassInfo(className, loader, componentClassInfo);
    713     }
    714
    715     /***
    716      * Creates a ClassInfo based on the stream retrieved from the class loader through
    717      * <code>getResourceAsStream</code>.
    718      *
    719      * @param name java name as in source code
    720      * @param loader
    721      * @param lazyAttributes
    722      */
    723     private static ClassInfo createClassInfoFromStream(final String name,
    724                                                        final ClassLoader loader,
    725                                                        boolean lazyAttributes) {
    726         final String className = name.replace('.', '/');
    727
    728         // to handle primitive type we need to know the array dimension
    729         if (name.indexOf('/') < 0) {
    730             // it might be one
    731             // gets its non array component type and the dimension
    732             int dimension = 0;
    733             for (int i = className.indexOf('['); i > 0; i = className.indexOf('[', i+1)) {
    734                 dimension++;
    735             }
    736             String unidimComponentName = className;
    737             if (dimension > 0) {
    738                 int unidimComponentTypeIndex = className.indexOf('[');
    739                 unidimComponentName = className.substring(0, unidimComponentTypeIndex);
    740             }
    741             Class primitiveClass = AsmClassInfo.getPrimitiveClass(unidimComponentName);
    742             if (primitiveClass != null && primitiveClass.isPrimitive()) {
    743                 if (dimension == 0) {
    744                     return JavaClassInfo.getClassInfo(primitiveClass);
    745                 } else {
    746                     Class arrayClass = Array.newInstance(primitiveClass, new int[dimension]).getClass();
    747                     return JavaClassInfo.getClassInfo(arrayClass);
    748                 }
    749             }
    750         }
    751
    752         // for non primitive, we need to chain component type ala java.lang.reflect
    753         // to support multi. dim. arrays
    754         int componentTypeIndex = className.lastIndexOf('[');
    755         String componentName = className;
    756         boolean isArray = false;
    757         if (componentTypeIndex > 0) {
    758             componentName = className.substring(0, componentTypeIndex);
    759             isArray = true;
    760         }
    761
    762         ClassInfo componentInfo = null;
    763
    764         // is component yet another array ie this name is a multi dim array ?
    765         if (componentName.indexOf('[') > 0) {
    766             componentInfo = getClassInfo(componentName, loader);
    767         } else {
    768             InputStream componentClassAsStream = null;
    769             if (loader != null) {
    770                 componentClassAsStream = loader.getResourceAsStream(componentName + ".class");
    771             } else {
    772                 // boot class loader, fall back to system classloader that will see it anyway
    773                 componentClassAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream(componentName + ".class");
    774             }
    775             if (componentClassAsStream == null) {
    776                 // might be more than one dimension
    777                 if (componentName.indexOf('[') > 0) {
    778                     return getClassInfo(componentName, loader);
    779                 }
    780                 System.out.println(
    781                         "AW::WARNING - could not load class ["
    782                         + componentName
    783                         + "] as a resource in loader ["
    784                         + loader
    785                         + "]"
    786                 );
    787                 componentInfo = new ClassInfo.NullClassInfo();
    788             }
    789             try {
    790                 componentInfo = AsmClassInfo.getClassInfo(componentClassAsStream, loader, lazyAttributes);
    791             } finally {
    792                 try {
    793                     componentClassAsStream.close();
    794                 } catch (Exception e) {
    795                     ;
    796                 }
    797             }
    798         }
    799
    800         if (!isArray) {
    801             return componentInfo;
    802         } else {
    803             return AsmClassInfo.getArrayClassInfo(className, loader, componentInfo);
    804         }
    805     }
    806
    807 //    /***
    808 //     * Creates a string with the annotation key value pairs.
    809 //     *
    810 //     * @param annotation
    811 //     * @return the string
    812 //     */
    813 //    private static String createAnnotationKeyValueString(final Annotation annotation) {
    814 //        List elementValues = annotation.elementValues;
    815 //        StringBuffer annotationValues = new StringBuffer();
    816 //        if (elementValues.size() != 0) {
    817 //            int i = 0;
    818 //            for (Iterator iterator = elementValues.iterator(); iterator.hasNext();) {
    819 //                Object[] keyValuePair = (Object[]) iterator.next();
    820 //                annotationValues.append((String) keyValuePair[0]);
    821 //                annotationValues.append('=');
    822 //                annotationValues.append(keyValuePair[1].toString());
    823 //                if (i < elementValues.size() - 1) {
    824 //                    annotationValues.append(',');
    825 //                }
    826 //            }
    827 //        }
    828 //        return annotationValues.toString();
    829 //    }
    830
    831     /***
    832      * ASM bytecode visitor that retrieves the class name from the bytecode.
    833      *
    834      * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    835      */
    836     public static class ClassNameRetrievalClassAdapter extends AsmAnnotationHelper.NullClassAdapter {
    837
    838         private String m_className;
    839
    840         public void visit(final int version,
    841                           final int access,
    842                           final String name,
    843                           final String superName,
    844                           final String[] interfaces,
    845                           final String sourceFile) {
    846             m_className = name.replace('/', '.');
    847         }
    848
    849         public String getClassName() {
    850             return m_className;
    851         }
    852     }
    853
    854     /***
    855      * ASM bytecode visitor that gathers info about the class.
    856      *
    857      * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    858      */
    859     private class ClassInfoClassAdapter extends AsmAnnotationHelper.NullClassAdapter {
    860
    861         public boolean m_lazyAttributes = true;
    862
    863         public ClassInfoClassAdapter(boolean lazyAttributes) {
    864             m_lazyAttributes = lazyAttributes;
    865         }
    866
    867         public void visit(final int version,
    868                           final int access,
    869                           final String name,
    870                           final String superName,
    871                           final String[] interfaces,
    872                           final String sourceFile) {
    873
    874             m_name = name.replace('/', '.');
    875             m_modifiers = access;
    876             m_isInterface = Modifier.isInterface(m_modifiers);
    877             // special case for java.lang.Object, which does not extend anything
    878             m_superClassName = superName == null ? null : superName.replace('/', '.');
    879             m_interfaceClassNames = new String[interfaces.length];
    880             for (int i = 0; i < interfaces.length; i++) {
    881                 m_interfaceClassNames[i] = interfaces[i].replace('/', '.');
    882             }
    883             // FIXME this algo for array types does most likely NOT WORK (since
    884             // I assume that ASM is handling arrays
    885             // using the internal desriptor format '[L' and the algo is using '[]')
    886             if (m_name.endsWith("[]")) {
    887                 m_isArray = true;
    888                 int index = m_name.indexOf('[');
    889                 m_componentTypeName = m_name.substring(0, index);
    890             } else if (m_name.equals("long")
    891                        || m_name.equals("int")
    892                        || m_name.equals("short")
    893                        || m_name.equals("double")
    894                        || m_name.equals("float")
    895                        || m_name.equals("byte")
    896                        || m_name.equals("boolean")
    897                        || m_name.equals("char")) {
    898                 m_isPrimitive = true;
    899             }
    900         }
    901
    902         public void visitAttribute(final Attribute attribute) {
    903             // attributes
    904             if (!m_lazyAttributes) {
    905                 List annotations = new ArrayList();
    906                 annotations =
    907                 AsmAnnotationHelper.extractAnnotations(annotations, attribute, (ClassLoader) m_loaderRef.get());
    908                 m_annotations = annotations;
    909             }
    910         }
    911
    912         public void visitField(final int access,
    913                                final String name,
    914                                final String desc,
    915                                final Object value,
    916                                final Attribute attrs) {
    917             final FieldStruct struct = new FieldStruct();
    918             struct.modifiers = access;
    919             struct.name = name;
    920             struct.desc = desc;
    921             struct.value = value;
    922             AsmFieldInfo fieldInfo = new AsmFieldInfo(struct, m_name, (ClassLoader) m_loaderRef.get());
    923             // attributes
    924             if (!m_lazyAttributes) {
    925                 List annotations = new ArrayList();
    926                 annotations =
    927                 AsmAnnotationHelper.extractAnnotations(annotations, attrs, (ClassLoader) m_loaderRef.get());
    928                 fieldInfo.m_annotations = annotations;
    929             }
    930             int hash = AsmHelper.calculateFieldHash(name, desc);
    931             m_fields.put(hash, fieldInfo);
    932             m_sortedFieldHashes.add(hash);
    933         }
    934
    935         public CodeVisitor visitMethod(final int access,
    936                                        final String name,
    937                                        final String desc,
    938                                        final String[] exceptions,
    939                                        final Attribute attrs) {
    940             final MethodStruct struct = new MethodStruct();
    941             struct.modifiers = access;
    942             struct.name = name;
    943             struct.desc = desc;
    944             struct.exceptions = exceptions;
    945             int hash = AsmHelper.calculateMethodHash(name, desc);
    946             // the methodInfo that should be updated when we will visit the method parameter names info if needed.
    947             AsmMethodInfo methodInfo = null;
    948             if (name.equals(TransformationConstants.CLINIT_METHOD_NAME)) {
    949                 m_hasStaticInitializer = true;
    950             } else {
    951                 AsmMemberInfo memberInfo = null;
    952                 if (name.equals(TransformationConstants.INIT_METHOD_NAME)) {
    953                     memberInfo = new AsmConstructorInfo(struct, m_name, (ClassLoader) m_loaderRef.get());
    954                     m_constructors.put(hash, memberInfo);
    955                     m_sortedConstructorHashes.add(hash);
    956                 } else {
    957                     memberInfo = new AsmMethodInfo(struct, m_name, (ClassLoader) m_loaderRef.get());
    958                     m_methods.put(hash, memberInfo);
    959                     m_sortedMethodHashes.add(hash);
    960                     methodInfo = (AsmMethodInfo) memberInfo;
    961                 }
    962                 // attributes
    963                 if (!m_lazyAttributes) {
    964                     List annotations = new ArrayList();
    965                     annotations =
    966                     AsmAnnotationHelper.extractAnnotations(annotations, attrs, (ClassLoader) m_loaderRef.get());
    967                     memberInfo.m_annotations = annotations;
    968                 }
    969             }
    970             if (methodInfo != null) {
    971                 // visit the method to access the parameter names as required to support Aspect with bindings
    972                 // TODO: should we make this optional - similar to m_lazyAttributes ?
    973                 Type[] parameterTypes = Type.getArgumentTypes(desc);
    974                 if (parameterTypes.length > 0) {
    975                     CodeVisitor methodParameterNamesVisitor = new MethodParameterNamesCodeAdapter(
    976                             Modifier.isStatic(access),
    977                             parameterTypes.length, methodInfo
    978                     );
    979                     return methodParameterNamesVisitor;
    980                 } else {
    981                     methodInfo.m_parameterNames = EMPTY_STRING_ARRAY;
    982                 }
    983             }
    984             return AsmAnnotationHelper.NullCodeAdapter.NULL_CODE_ADAPTER;
    985         }
    986
    987         public void visitEnd() {
    988             m_signature = AsmHelper.getClassDescriptor(AsmClassInfo.this);
    989         }
    990     }
    991
    992     /***
    993      * Extracts method parameter names as they appear in the source code from debug infos
    994      *
    995      * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
    996      */
    997     static class MethodParameterNamesCodeAdapter extends AsmAnnotationHelper.NullCodeAdapter {
    998         private final boolean m_isStatic;
    999         private final int m_parameterCount;
    1000         private AsmMethodInfo m_methodInfo;
    1001         private int m_signatureParameterRegisterDepth = 0;
    1002
    1003         public MethodParameterNamesCodeAdapter(boolean isStatic, int parameterCount, AsmMethodInfo methodInfo) {
    1004             m_isStatic = isStatic;
    1005             m_parameterCount = parameterCount;
    1006             m_methodInfo = methodInfo;
    1007             m_methodInfo.m_parameterNames = new String[m_parameterCount];
    1008
    1009             // compute the max index of the arguments that appear in the method signature
    1010             // including "this" on register 0 for non static methods
    1011             // a long or double needs 2 registers
    1012             if (!m_isStatic) {
    1013                 m_signatureParameterRegisterDepth++;// index 0 = this
    1014             }
    1015             m_signatureParameterRegisterDepth += AsmHelper.getRegisterDepth(Type.getArgumentTypes(m_methodInfo.m_member.desc));
    1016         }
    1017
    1018         /***
    1019          * Do not assume to visit the local variable with index always increasing since it is a wrong assumption
    1020          * [ see f.e. test.args.ArgsAspect.withArray advice ]
    1021          * @param name
    1022          * @param desc
    1023          * @param start
    1024          * @param end
    1025          * @param index the index of the argument on the stack
    1026          */
    1027         public void visitLocalVariable(String name, String desc, Label start, Label end, int index) {
    1028             if (index < m_signatureParameterRegisterDepth) {
    1029                 // this is not a local variable
    1030                 if (index == 0) {
    1031                     if (!m_isStatic) {
    1032                         ;//skip this
    1033                     } else {
    1034                         m_methodInfo.pushParameterNameFromRegister(index, name);
    1035                     }
    1036                 } else {
    1037                     m_methodInfo.pushParameterNameFromRegister(index, name);
    1038                 }
    1039             } else {
    1040                 ;// skip code block locals
    1041             }
    1042         }
    1043     }
    1044
    1045     /***
    1046      * Converts the class name from VM type class name to Java type class name.
    1047      *
    1048      * @param className the VM type class name
    1049      * @return the Java type class name
    1050      */
    1051     private static String getJavaClassName(final String className) {
    1052         String javaClassName;
    1053         if (className.startsWith("[")) {
    1054             javaClassName = Type.getType(className).getClassName();
    1055         } else {
    1056             javaClassName = className.replace('/', '.');
    1057         }
    1058         return javaClassName;
    1059     }
    1060 }