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 }