0001: /*
0002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
0003: * notice. All rights reserved.
0004: */
0005: package com.tc.aspectwerkz.reflect.impl.asm;
0006:
0007: import com.tc.asm.ClassReader;
0008: import com.tc.asm.FieldVisitor;
0009: import com.tc.asm.Label;
0010: import com.tc.asm.MethodVisitor;
0011: import com.tc.asm.Type;
0012: import com.tc.aspectwerkz.exception.WrappedRuntimeException;
0013: import com.tc.aspectwerkz.reflect.ClassInfo;
0014: import com.tc.aspectwerkz.reflect.ConstructorInfo;
0015: import com.tc.aspectwerkz.reflect.FieldInfo;
0016: import com.tc.aspectwerkz.reflect.MethodInfo;
0017: import com.tc.aspectwerkz.reflect.NullClassInfo;
0018: import com.tc.aspectwerkz.reflect.StaticInitializationInfo;
0019: import com.tc.aspectwerkz.reflect.StaticInitializationInfoImpl;
0020: import com.tc.aspectwerkz.reflect.impl.java.JavaClassInfo;
0021: import com.tc.aspectwerkz.transform.TransformationConstants;
0022: import com.tc.aspectwerkz.transform.inlining.AsmHelper;
0023: import com.tc.aspectwerkz.transform.inlining.AsmNullAdapter;
0024: import com.tc.aspectwerkz.util.ContextClassLoader;
0025: import com.tc.backport175.bytecode.AnnotationElement;
0026: import com.tc.backport175.bytecode.AnnotationReader;
0027:
0028: import java.io.IOException;
0029: import java.io.InputStream;
0030: import java.lang.ref.WeakReference;
0031: import java.lang.reflect.Array;
0032: import java.lang.reflect.Modifier;
0033: import java.util.ArrayList;
0034: import java.util.HashMap;
0035: import java.util.List;
0036:
0037: /**
0038: * Implementation of the ClassInfo interface utilizing the ASM bytecode library for the info retriaval. <p/> Annotations
0039: * are lazily gathered, unless required to visit them at the same time as we visit methods and fields. <p/> This
0040: * implementation guarantees that the method, fields and constructors can be retrieved in the same order as they were in
0041: * the bytecode (it can depends of the compiler and might not be the order of the source code - f.e. IBM compiler)
0042: *
0043: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
0044: * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
0045: */
0046: public class AsmClassInfo implements ClassInfo {
0047:
0048: protected final static String[] EMPTY_STRING_ARRAY = new String[0];
0049:
0050: protected final static List EMPTY_LIST = new ArrayList();
0051:
0052: /**
0053: * The class loader wrapped in a weak ref.
0054: */
0055: private final WeakReference m_loaderRef;
0056:
0057: /**
0058: * The name of the class (with dots and not slashes)
0059: */
0060: private String m_name;
0061:
0062: /**
0063: * The signature of the class.
0064: */
0065: private String m_signature;
0066:
0067: /**
0068: * The internal generics signature for this class.
0069: */
0070: private String m_genericsSignature;
0071:
0072: /**
0073: * The modifiers.
0074: */
0075: private int m_modifiers;
0076:
0077: /**
0078: * Is the class an interface.
0079: */
0080: private boolean m_isInterface = false;
0081:
0082: /**
0083: * Is the class a primitive type.
0084: */
0085: private boolean m_isPrimitive = false;
0086:
0087: /**
0088: * Is the class of type array.
0089: */
0090: private boolean m_isArray = false;
0091:
0092: /**
0093: * Flag for the static initializer method.
0094: */
0095: private boolean m_hasStaticInitializer = false;
0096:
0097: /**
0098: * Lazy instance that represents the static initializer if present, else null
0099: */
0100: private StaticInitializationInfo m_staticInitializer = null;
0101:
0102: /**
0103: * A list with the <code>ConstructorInfo</code> instances. When visiting the bytecode, we keep track of the order of
0104: * the visit. The first time the getConstructors() gets called, we build an array and then reuse it directly.
0105: */
0106: private final HashMap m_constructors = new HashMap();
0107: private ArrayList m_sortedConstructorHashes = new ArrayList();
0108: private ConstructorInfo[] m_constructorsLazy = null;
0109:
0110: /**
0111: * A list with the <code>MethodInfo</code> instances. When visiting the bytecode, we keep track of the order of the
0112: * visit. The first time the getMethods() gets called, we build an array and then reuse it directly.
0113: */
0114: private final HashMap m_methods = new HashMap();
0115: private ArrayList m_sortedMethodHashes = new ArrayList();
0116: private MethodInfo[] m_methodsLazy = null;
0117:
0118: /**
0119: * A list with the <code>FieldInfo</code> instances. When visiting the bytecode, we keep track of the order of the
0120: * visit. The first time the getFields() gets called, we build an array and then reuse it directly.
0121: */
0122: private final HashMap m_fields = new HashMap();
0123: private ArrayList m_sortedFieldHashes = new ArrayList();
0124: private FieldInfo[] m_fieldsLazy = null;
0125:
0126: /**
0127: * A list with the interfaces class names.
0128: */
0129: private String[] m_interfaceClassNames = null;
0130:
0131: /**
0132: * A list with the interfaces.
0133: */
0134: private ClassInfo[] m_interfaces = null;
0135:
0136: /**
0137: * The super class name.
0138: */
0139: private String m_super ClassName = null;
0140:
0141: /**
0142: * The super class.
0143: */
0144: private ClassInfo m_super Class = null;
0145:
0146: /**
0147: * The annotation reader. Lazily instantiated from backport.
0148: */
0149: private AnnotationReader m_annotationReader = null;
0150:
0151: /**
0152: * The component type name if array type. Can be an array itself.
0153: */
0154: private String m_componentTypeName = null;
0155:
0156: /**
0157: * The component type if array type. Can be an array itself.
0158: */
0159: private ClassInfo m_componentType = null;
0160:
0161: /**
0162: * The class info repository.
0163: */
0164: private final AsmClassInfoRepository m_classInfoRepository;
0165:
0166: /**
0167: * Creates a new ClassInfo instance.
0168: *
0169: * @param bytecode
0170: * @param loader
0171: */
0172: AsmClassInfo(final byte[] bytecode, final ClassLoader loader) {
0173: if (bytecode == null) {
0174: throw new IllegalArgumentException(
0175: "bytecode can not be null");
0176: }
0177: m_loaderRef = new WeakReference(loader);
0178: m_classInfoRepository = AsmClassInfoRepository
0179: .getRepository(loader);
0180: try {
0181: ClassReader cr = new ClassReader(bytecode);
0182: ClassInfoClassAdapter visitor = new ClassInfoClassAdapter();
0183: cr.accept(visitor, ClassReader.SKIP_FRAMES);
0184: } catch (Throwable t) {
0185: t.printStackTrace();
0186: }
0187: m_classInfoRepository.addClassInfo(this );
0188: }
0189:
0190: /**
0191: * Creates a new ClassInfo instance.
0192: *
0193: * @param resourceStream
0194: * @param loader
0195: */
0196: AsmClassInfo(final InputStream resourceStream,
0197: final ClassLoader loader) {
0198: if (resourceStream == null) {
0199: throw new IllegalArgumentException(
0200: "resource stream can not be null");
0201: }
0202: m_loaderRef = new WeakReference(loader);
0203: m_classInfoRepository = AsmClassInfoRepository
0204: .getRepository(loader);
0205: try {
0206: ClassReader cr = new ClassReader(resourceStream);
0207: ClassInfoClassAdapter visitor = new ClassInfoClassAdapter();
0208: cr.accept(visitor, ClassReader.SKIP_FRAMES);
0209: } catch (Throwable t) {
0210: t.printStackTrace();
0211: }
0212: m_classInfoRepository.addClassInfo(this );
0213: }
0214:
0215: /**
0216: * Create a ClassInfo based on a component type and a given dimension Due to java.lang.reflect. behavior, the
0217: * ClassInfo is almost empty. It is not an interface, only subclass of java.lang.Object, no methods, fields, or
0218: * constructor, no annotation.
0219: *
0220: * @param className
0221: * @param loader
0222: * @param componentInfo
0223: */
0224: AsmClassInfo(final String className, final ClassLoader loader,
0225: final ClassInfo componentInfo) {
0226: m_loaderRef = new WeakReference(loader);
0227: m_name = className.replace('/', '.');
0228: m_classInfoRepository = AsmClassInfoRepository
0229: .getRepository(loader);
0230: m_isArray = true;
0231: m_componentType = componentInfo;
0232: m_componentTypeName = componentInfo.getName();
0233: m_modifiers = componentInfo.getModifiers() | Modifier.ABSTRACT
0234: | Modifier.FINAL;
0235: m_isInterface = false;// as in java.reflect
0236: m_super Class = JavaClassInfo.getClassInfo(Object.class);
0237: m_super ClassName = m_super Class.getName();
0238: m_interfaceClassNames = new String[0];
0239: m_interfaces = new ClassInfo[0];
0240: m_signature = AsmHelper.getClassDescriptor(this );
0241: m_classInfoRepository.addClassInfo(this );
0242: }
0243:
0244: /**
0245: * Returns a completely new class info for a specific class. Does not cache.
0246: *
0247: * @param bytecode
0248: * @param loader
0249: * @return the class info
0250: */
0251: public static ClassInfo newClassInfo(final byte[] bytecode,
0252: final ClassLoader loader) {
0253: final String className = AsmClassInfo
0254: .retrieveClassNameFromBytecode(bytecode);
0255: AsmClassInfoRepository repository = AsmClassInfoRepository
0256: .getRepository(loader);
0257: repository.removeClassInfo(className);
0258: return new AsmClassInfo(bytecode, loader);
0259: }
0260:
0261: /**
0262: * Returns the class info for a specific class.
0263: *
0264: * @param className
0265: * @param loader
0266: * @return the class info
0267: */
0268: public static ClassInfo getClassInfo(final String className,
0269: final ClassLoader loader) {
0270: final String javaClassName = getJavaClassName(className);
0271: AsmClassInfoRepository repository = AsmClassInfoRepository
0272: .getRepository(loader);
0273: ClassInfo classInfo = repository.getClassInfo(javaClassName);
0274: if (classInfo == null) {
0275: classInfo = createClassInfoFromStream(javaClassName, loader);
0276: }
0277: return classInfo;
0278: }
0279:
0280: /**
0281: * Returns the class info for a specific class.
0282: *
0283: * @param bytecode
0284: * @param loader
0285: * @return the class info
0286: */
0287: public static ClassInfo getClassInfo(final byte[] bytecode,
0288: final ClassLoader loader) {
0289: final String className = AsmClassInfo
0290: .retrieveClassNameFromBytecode(bytecode);
0291: AsmClassInfoRepository repository = AsmClassInfoRepository
0292: .getRepository(loader);
0293: ClassInfo classInfo = repository.getClassInfo(className);
0294: if (classInfo == null) {
0295: classInfo = new AsmClassInfo(bytecode, loader);
0296: }
0297: return classInfo;
0298: }
0299:
0300: /**
0301: * Returns the class info for a specific class.
0302: *
0303: * @param className
0304: * @param bytecode
0305: * @param loader
0306: * @return the class info
0307: */
0308: public static ClassInfo getClassInfo(final String className,
0309: final byte[] bytecode, final ClassLoader loader) {
0310: AsmClassInfoRepository repository = AsmClassInfoRepository
0311: .getRepository(loader);
0312: ClassInfo classInfo = repository.getClassInfo(className
0313: .replace('.', '/'));
0314: if (classInfo == null) {
0315: classInfo = new AsmClassInfo(bytecode, loader);
0316: }
0317: return classInfo;
0318: }
0319:
0320: /**
0321: * Returns the class info for a specific class.
0322: *
0323: * @param stream
0324: * @param loader
0325: * @return the class info
0326: */
0327: public static ClassInfo getClassInfo(final String name,
0328: final InputStream stream, final ClassLoader loader) {
0329: try {
0330: ClassReader cr = new ClassReader(stream);
0331: final String className = cr.getClassName()
0332: .replace('/', '.');
0333: AsmClassInfoRepository repository = AsmClassInfoRepository
0334: .getRepository(loader);
0335: ClassInfo classInfo = repository.getClassInfo(className);
0336: if (classInfo == null) {
0337: classInfo = new AsmClassInfo(cr.b, loader);
0338: }
0339: return classInfo;
0340: } catch (IOException e) {
0341: throw new WrappedRuntimeException(
0342: "Can't get ClassInfo for " + name, e);
0343: }
0344: }
0345:
0346: /**
0347: * Marks the class as dirty (since it has been modified and needs to be rebuild).
0348: *
0349: * @param className
0350: */
0351: public static void markDirty(final String className,
0352: final ClassLoader loader) {
0353: AsmClassInfoRepository.getRepository(loader).removeClassInfo(
0354: className);
0355: }
0356:
0357: /**
0358: * Retrieves the class name from the bytecode of a class.
0359: *
0360: * @param bytecode
0361: * @return the class name
0362: */
0363: public static String retrieveClassNameFromBytecode(
0364: final byte[] bytecode) {
0365: ClassReader cr = new ClassReader(bytecode);
0366: return cr.getClassName().replace('/', '.');
0367: }
0368:
0369: /**
0370: * Checks if the class is a of a primitive type, if so create and return the class for the type else return null.
0371: *
0372: * @param className
0373: * @return the class for the primitive type or null
0374: */
0375: public static Class getPrimitiveClass(final String className) {
0376: if (className.equals("void")) {
0377: return void.class;
0378: } else if (className.equals("long")) {
0379: return long.class;
0380: } else if (className.equals("int")) {
0381: return int.class;
0382: } else if (className.equals("short")) {
0383: return short.class;
0384: } else if (className.equals("double")) {
0385: return double.class;
0386: } else if (className.equals("float")) {
0387: return float.class;
0388: } else if (className.equals("byte")) {
0389: return byte.class;
0390: } else if (className.equals("boolean")) {
0391: return boolean.class;
0392: } else if (className.equals("char")) {
0393: return char.class;
0394: } else {
0395: return null;
0396: }
0397: }
0398:
0399: /**
0400: * Returns the annotations.
0401: *
0402: * @return the annotations
0403: */
0404: public AnnotationElement.Annotation[] getAnnotations() {
0405: return getAnnotationReader().getAnnotationElements();
0406: }
0407:
0408: /**
0409: * Returns the name of the class.
0410: *
0411: * @return the name of the class
0412: */
0413: public String getName() {
0414: return m_name;
0415: }
0416:
0417: /**
0418: * Returns the signature for the class.
0419: *
0420: * @return the signature for the class
0421: */
0422: public String getSignature() {
0423: return m_signature;
0424: }
0425:
0426: /**
0427: * Returns the internal generics signature for the element.
0428: *
0429: * @return the internal generics signature for the element
0430: */
0431: public String getGenericsSignature() {
0432: return m_genericsSignature;
0433: }
0434:
0435: /**
0436: * Returns the class modifiers.
0437: *
0438: * @return the class modifiers
0439: */
0440: public int getModifiers() {
0441: return m_modifiers;
0442: }
0443:
0444: /**
0445: * Returns the class loader that loaded this class.
0446: *
0447: * @return the class loader
0448: */
0449: public ClassLoader getClassLoader() {
0450: return (ClassLoader) m_loaderRef.get();
0451: }
0452:
0453: /**
0454: * Checks if the class has a static initalizer.
0455: *
0456: * @return
0457: */
0458: public boolean hasStaticInitializer() {
0459: return m_hasStaticInitializer;
0460: }
0461:
0462: /**
0463: * Return the static initializer info or null if not present
0464: *
0465: * @see org.codehaus.aspectwerkz.reflect.ClassInfo#staticInitializer()
0466: */
0467: public StaticInitializationInfo staticInitializer() {
0468: if (hasStaticInitializer() && m_staticInitializer == null) {
0469: m_staticInitializer = new StaticInitializationInfoImpl(this );
0470: }
0471: return m_staticInitializer;
0472: }
0473:
0474: /**
0475: * Returns a constructor info by its hash.
0476: *
0477: * @param hash
0478: * @return
0479: */
0480: public ConstructorInfo getConstructor(final int hash) {
0481: ConstructorInfo constructor = (ConstructorInfo) m_constructors
0482: .get(new Integer(hash));
0483: if (constructor == null && getSuperclass() != null) {
0484: constructor = getSuperclass().getConstructor(hash);
0485: }
0486: return constructor;
0487: }
0488:
0489: /**
0490: * Returns a list with all the constructors info.
0491: *
0492: * @return the constructors info
0493: */
0494: public synchronized ConstructorInfo[] getConstructors() {
0495: if (m_constructorsLazy == null) {
0496: ConstructorInfo[] constructorInfos = new ConstructorInfo[m_sortedConstructorHashes
0497: .size()];
0498: for (int i = 0; i < m_sortedConstructorHashes.size(); i++) {
0499: constructorInfos[i] = (ConstructorInfo) m_constructors
0500: .get(m_sortedConstructorHashes.get(i));
0501: }
0502: m_constructorsLazy = constructorInfos;
0503: }
0504: return m_constructorsLazy;
0505: }
0506:
0507: /**
0508: * Returns a method info by its hash.
0509: *
0510: * @param hash
0511: * @return
0512: */
0513: public MethodInfo getMethod(final int hash) {
0514: MethodInfo method = (MethodInfo) m_methods
0515: .get(new Integer(hash));
0516: if (method == null) {
0517: for (int i = 0; i < getInterfaces().length; i++) {
0518: method = getInterfaces()[i].getMethod(hash);
0519: if (method != null) {
0520: break;
0521: }
0522: }
0523: }
0524: if (method == null && getSuperclass() != null) {
0525: method = getSuperclass().getMethod(hash);
0526: }
0527: return method;
0528: }
0529:
0530: /**
0531: * Returns a list with all the methods info.
0532: *
0533: * @return the methods info
0534: */
0535: public synchronized MethodInfo[] getMethods() {
0536: if (m_methodsLazy == null) {
0537: MethodInfo[] methodInfos = new MethodInfo[m_sortedMethodHashes
0538: .size()];
0539: for (int i = 0; i < m_sortedMethodHashes.size(); i++) {
0540: methodInfos[i] = (MethodInfo) m_methods
0541: .get(m_sortedMethodHashes.get(i));
0542: }
0543: m_methodsLazy = methodInfos;
0544: }
0545: return m_methodsLazy;
0546: }
0547:
0548: /**
0549: * Returns a field info by its hash.
0550: *
0551: * @param hash
0552: * @return
0553: */
0554: public FieldInfo getField(final int hash) {
0555: FieldInfo field = (FieldInfo) m_fields.get(new Integer(hash));
0556: if (field == null && getSuperclass() != null) {
0557: field = getSuperclass().getField(hash);
0558: }
0559: if (field == null) {
0560: // Trying to find constants in Interfaces
0561: ClassInfo[] interfaces = getInterfaces();
0562: for (int i = 0; i < interfaces.length; i++) {
0563: ClassInfo ifc = interfaces[i];
0564: field = ifc.getField(hash);
0565: if (field != null)
0566: break;
0567: }
0568: }
0569: return field;
0570: }
0571:
0572: /**
0573: * Returns a list with all the field info.
0574: *
0575: * @return the field info
0576: */
0577: public FieldInfo[] getFields() {
0578: if (m_fieldsLazy == null) {
0579: FieldInfo[] fieldInfos = new FieldInfo[m_sortedFieldHashes
0580: .size()];
0581: for (int i = 0; i < m_sortedFieldHashes.size(); i++) {
0582: fieldInfos[i] = (FieldInfo) m_fields
0583: .get(m_sortedFieldHashes.get(i));
0584: }
0585: m_fieldsLazy = fieldInfos;
0586: }
0587: return m_fieldsLazy;
0588: }
0589:
0590: /**
0591: * Returns the interfaces.
0592: *
0593: * @return the interfaces
0594: */
0595: public synchronized ClassInfo[] getInterfaces() {
0596: if (m_interfaces == null) {
0597: m_interfaces = new ClassInfo[m_interfaceClassNames.length];
0598: for (int i = 0; i < m_interfaceClassNames.length; i++) {
0599: m_interfaces[i] = AsmClassInfo.getClassInfo(
0600: m_interfaceClassNames[i],
0601: (ClassLoader) m_loaderRef.get());
0602: }
0603: }
0604: return m_interfaces;
0605: }
0606:
0607: /**
0608: * Returns the super class.
0609: *
0610: * @return the super class
0611: */
0612: public ClassInfo getSuperclass() {
0613: if (m_super Class == null && m_super ClassName != null) {
0614: m_super Class = AsmClassInfo.getClassInfo(m_super ClassName,
0615: (ClassLoader) m_loaderRef.get());
0616: }
0617: return m_super Class;
0618: }
0619:
0620: /**
0621: * Returns the component type if array type else null.
0622: *
0623: * @return the component type
0624: */
0625: public ClassInfo getComponentType() {
0626: if (isArray() && (m_componentTypeName == null)) {
0627: m_componentType = AsmClassInfo.getClassInfo(
0628: m_componentTypeName, (ClassLoader) m_loaderRef
0629: .get());
0630: }
0631: return m_componentType;
0632: }
0633:
0634: /**
0635: * Is the class an interface.
0636: *
0637: * @return
0638: */
0639: public boolean isInterface() {
0640: return m_isInterface;
0641: }
0642:
0643: /**
0644: * Is the class a primitive type.
0645: *
0646: * @return
0647: */
0648: public boolean isPrimitive() {
0649: return m_isPrimitive;
0650: }
0651:
0652: /**
0653: * Is the class an array type.
0654: *
0655: * @return
0656: */
0657: public boolean isArray() {
0658: return m_isArray;
0659: }
0660:
0661: /**
0662: * @see java.lang.Object#equals(java.lang.Object)
0663: */
0664: public boolean equals(Object o) {
0665: if (this == o) {
0666: return true;
0667: }
0668: if (!(o instanceof ClassInfo)) {
0669: return false;
0670: }
0671: ClassInfo classInfo = (ClassInfo) o;
0672: return m_name.equals(classInfo.getName());
0673: }
0674:
0675: /**
0676: * @see java.lang.Object#hashCode()
0677: */
0678: public int hashCode() {
0679: return m_name.hashCode();
0680: }
0681:
0682: public String toString() {
0683: return m_name;
0684: }
0685:
0686: /**
0687: * Create a ClassInfo based on a component type which can be himself an array
0688: *
0689: * @param className
0690: * @param loader
0691: * @param componentClassInfo
0692: * @return
0693: */
0694: public static ClassInfo getArrayClassInfo(final String className,
0695: final ClassLoader loader, final ClassInfo componentClassInfo) {
0696: return new AsmClassInfo(className, loader, componentClassInfo);
0697: }
0698:
0699: /**
0700: * Creates a ClassInfo based on the stream retrieved from the class loader through <code>getResourceAsStream</code>.
0701: *
0702: * @param name java name as in source code
0703: * @param loader
0704: */
0705: private static ClassInfo createClassInfoFromStream(
0706: final String name, final ClassLoader loader) {
0707: final String className = name.replace('.', '/');
0708:
0709: // to handle primitive type we need to know the array dimension
0710: if (name.indexOf('/') < 0) {
0711: // it might be one
0712: // gets its non array component type and the dimension
0713: int dimension = 0;
0714: for (int i = className.indexOf('['); i > 0; i = className
0715: .indexOf('[', i + 1)) {
0716: dimension++;
0717: }
0718: String unidimComponentName = className;
0719: if (dimension > 0) {
0720: int unidimComponentTypeIndex = className.indexOf('[');
0721: unidimComponentName = className.substring(0,
0722: unidimComponentTypeIndex);
0723: }
0724: Class primitiveClass = AsmClassInfo
0725: .getPrimitiveClass(unidimComponentName);
0726: if (primitiveClass != null && primitiveClass.isPrimitive()) {
0727: if (dimension == 0) {
0728: return JavaClassInfo.getClassInfo(primitiveClass);
0729: } else {
0730: Class arrayClass = Array.newInstance(
0731: primitiveClass, new int[dimension])
0732: .getClass();
0733: return JavaClassInfo.getClassInfo(arrayClass);
0734: }
0735: }
0736: }
0737:
0738: // for non primitive, we need to chain component type ala java.lang.reflect
0739: // to support multi. dim. arrays
0740: int componentTypeIndex = className.lastIndexOf('[');
0741: String componentName = className;
0742: boolean isArray = false;
0743: if (componentTypeIndex > 0) {
0744: componentName = className.substring(0, componentTypeIndex);
0745: isArray = true;
0746: }
0747:
0748: ClassInfo componentInfo = null;
0749:
0750: // is component yet another array ie this name is a multi dim array ?
0751: if (componentName.indexOf('[') > 0) {
0752: componentInfo = getClassInfo(componentName, loader);
0753: } else {
0754: InputStream componentClassAsStream = null;
0755: if (loader != null) {
0756: componentClassAsStream = loader
0757: .getResourceAsStream(componentName + ".class");
0758: } else {
0759: // boot class loader, fall back to system classloader that will see it anyway
0760: componentClassAsStream = ClassLoader
0761: .getSystemClassLoader().getResourceAsStream(
0762: componentName + ".class");
0763: }
0764: if (componentClassAsStream == null) {
0765: // might be more than one dimension
0766: if (componentName.indexOf('[') > 0) {
0767: return getClassInfo(componentName, loader);
0768: }
0769:
0770: System.out
0771: .println("AW::WARNING - could not load class ["
0772: + componentName
0773: + "] as a resource in loader ["
0774: + loader + "]");
0775:
0776: NullClassInfo nullInfo = new NullClassInfo();
0777: nullInfo.setName(componentName.replace('/', '.'));
0778:
0779: // remember this missing resource so that later lookups will return early
0780: AsmClassInfoRepository.getRepository(loader)
0781: .addClassInfo(nullInfo);
0782: return nullInfo;
0783: }
0784:
0785: try {
0786: componentInfo = AsmClassInfo.getClassInfo(
0787: componentName, componentClassAsStream, loader);
0788: } finally {
0789: try {
0790: componentClassAsStream.close();
0791: } catch (Exception e) {
0792: // ignore
0793: }
0794: }
0795: }
0796:
0797: if (!isArray) {
0798: return componentInfo;
0799: } else {
0800: return AsmClassInfo.getArrayClassInfo(className, loader,
0801: componentInfo);
0802: }
0803: }
0804:
0805: // /**
0806: // * Creates a string with the annotation key value pairs.
0807: // *
0808: // * @param annotation
0809: // * @return the string
0810: // */
0811: // private static String createAnnotationKeyValueString(final Annotation annotation) {
0812: // List elementValues = annotation.elementValues;
0813: // StringBuffer annotationValues = new StringBuffer();
0814: // if (elementValues.size() != 0) {
0815: // int i = 0;
0816: // for (Iterator iterator = elementValues.iterator(); iterator.hasNext();) {
0817: // Object[] keyValuePair = (Object[]) iterator.next();
0818: // annotationValues.append((String) keyValuePair[0]);
0819: // annotationValues.append('=');
0820: // annotationValues.append(keyValuePair[1].toString());
0821: // if (i < elementValues.size() - 1) {
0822: // annotationValues.append(',');
0823: // }
0824: // }
0825: // }
0826: // return annotationValues.toString();
0827: // }
0828:
0829: /**
0830: * ASM bytecode visitor that gathers info about the class.
0831: */
0832: class ClassInfoClassAdapter extends AsmNullAdapter.NullClassAdapter {
0833:
0834: public void visit(final int version, final int access,
0835: final String name, final String signature,
0836: final String super Name, final String[] interfaces) {
0837:
0838: m_modifiers = access;
0839: m_name = name.replace('/', '.');
0840: m_genericsSignature = signature;
0841: m_isInterface = Modifier.isInterface(m_modifiers);
0842: // special case for java.lang.Object, which does not extend anything
0843: m_super ClassName = super Name == null ? null : super Name
0844: .replace('/', '.');
0845: m_interfaceClassNames = new String[interfaces.length];
0846: for (int i = 0; i < interfaces.length; i++) {
0847: m_interfaceClassNames[i] = interfaces[i].replace('/',
0848: '.');
0849: }
0850: // FIXME this algo for array types does most likely NOT WORK (since
0851: // I assume that ASM is handling arrays
0852: // using the internal desriptor format '[L' and the algo is using '[]')
0853: if (m_name.endsWith("[]")) {
0854: m_isArray = true;
0855: int index = m_name.indexOf('[');
0856: m_componentTypeName = m_name.substring(0, index);
0857: } else if (m_name.equals("long") || m_name.equals("int")
0858: || m_name.equals("short")
0859: || m_name.equals("double")
0860: || m_name.equals("float") || m_name.equals("byte")
0861: || m_name.equals("boolean")
0862: || m_name.equals("char")) {
0863: m_isPrimitive = true;
0864: }
0865: }
0866:
0867: public FieldVisitor visitField(final int access,
0868: final String name, final String desc,
0869: final String signature, final Object value) {
0870: final FieldStruct struct = new FieldStruct();
0871: struct.modifiers = access;
0872: struct.name = name;
0873: struct.desc = desc;
0874: struct.signature = signature;
0875: struct.value = value;
0876: AsmFieldInfo fieldInfo = new AsmFieldInfo(struct, m_name,
0877: (ClassLoader) m_loaderRef.get());
0878: Integer hash = new Integer(AsmHelper.calculateFieldHash(
0879: name, desc));
0880: m_fields.put(hash, fieldInfo);
0881: m_sortedFieldHashes.add(hash);
0882: return null;
0883: }
0884:
0885: public MethodVisitor visitMethod(final int access,
0886: final String name, final String desc,
0887: final String signature, final String[] exceptions) {
0888: final MethodStruct struct = new MethodStruct();
0889: struct.modifiers = access;
0890: struct.name = name;
0891: struct.desc = desc;
0892: struct.signature = signature;
0893: struct.exceptions = exceptions;
0894: Integer hash = new Integer(AsmHelper.calculateMethodHash(
0895: name, desc));
0896: // the methodInfo that should be updated when we will visit the method parameter names info if needed.
0897: AsmMethodInfo methodInfo = null;
0898: if (name.equals(TransformationConstants.CLINIT_METHOD_NAME)) {
0899: m_hasStaticInitializer = true;
0900: } else {
0901: AsmMemberInfo memberInfo = null;
0902: if (name
0903: .equals(TransformationConstants.INIT_METHOD_NAME)) {
0904: memberInfo = new AsmConstructorInfo(struct, m_name,
0905: (ClassLoader) m_loaderRef.get());
0906: m_constructors.put(hash, memberInfo);
0907: m_sortedConstructorHashes.add(hash);
0908: } else {
0909: memberInfo = new AsmMethodInfo(struct, m_name,
0910: (ClassLoader) m_loaderRef.get());
0911: m_methods.put(hash, memberInfo);
0912: m_sortedMethodHashes.add(hash);
0913: methodInfo = (AsmMethodInfo) memberInfo;
0914: }
0915: }
0916: if (methodInfo != null) {
0917: // visit the method to access the parameter names as required to support Aspect with bindings
0918: // TODO: should we make this optional - similar to m_lazyAttributes ?
0919: Type[] parameterTypes = Type.getArgumentTypes(desc);
0920: if (parameterTypes.length > 0) {
0921: MethodVisitor methodParameterNamesVisitor = new MethodParameterNamesCodeAdapter(
0922: Modifier.isStatic(access),
0923: parameterTypes.length, methodInfo);
0924: return methodParameterNamesVisitor;
0925: }
0926: methodInfo.m_parameterNames = EMPTY_STRING_ARRAY;
0927: }
0928: return null;
0929: }
0930:
0931: public void visitEnd() {
0932: m_signature = AsmHelper
0933: .getClassDescriptor(AsmClassInfo.this );
0934: }
0935: }
0936:
0937: /**
0938: * Extracts method parameter names as they appear in the source code from debug infos
0939: *
0940: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
0941: */
0942: static class MethodParameterNamesCodeAdapter extends
0943: AsmNullAdapter.NullMethodAdapter {
0944: private final boolean m_isStatic;
0945: private final int m_parameterCount;
0946: private AsmMethodInfo m_methodInfo;
0947: private int m_signatureParameterRegisterDepth = 0;
0948: private String[] m_parameterNames;
0949:
0950: public MethodParameterNamesCodeAdapter(boolean isStatic,
0951: int parameterCount, AsmMethodInfo methodInfo) {
0952: m_isStatic = isStatic;
0953: m_methodInfo = methodInfo;
0954: m_parameterCount = parameterCount;
0955: m_parameterNames = new String[parameterCount];
0956:
0957: // compute the max index of the arguments that appear in the method signature
0958: // including "this" on register 0 for non static methods
0959: // a long or double needs 2 registers
0960: if (!m_isStatic) {
0961: m_signatureParameterRegisterDepth++;// index 0 = this
0962: }
0963: m_signatureParameterRegisterDepth += AsmHelper
0964: .getRegisterDepth(Type
0965: .getArgumentTypes(m_methodInfo.m_member.desc));
0966: }
0967:
0968: /**
0969: * Do not assume to visit the local variable with index always increasing since it is a wrong assumption [ see f.e.
0970: * test.args.ArgsAspect.withArray advice ]
0971: */
0972: public void visitLocalVariable(String name, String desc,
0973: String sig, Label start, Label end, int index) {
0974: // skip local variables and "this"
0975: if (index >= m_signatureParameterRegisterDepth
0976: || (index == 0 && !m_isStatic)) {
0977: return; // local variable
0978: }
0979:
0980: int startIndex = m_isStatic ? index : index - 1;
0981:
0982: String[] typeNames = m_methodInfo.m_parameterTypeNames;
0983:
0984: // assume we have a stack starting at the first parameter
0985: Type[] parameters = Type.getArgumentTypes(m_methodInfo
0986: .getSignature());
0987: int typeIndex = AsmHelper.getTypeIndexOf(parameters,
0988: startIndex);
0989:
0990: if (typeIndex >= 0
0991: && typeIndex < m_parameterNames.length
0992: && parameters[typeIndex].getClassName().equals(
0993: typeNames[typeIndex])) {
0994: m_parameterNames[typeIndex] = name;
0995: }
0996: }
0997:
0998: public void visitEnd() {
0999: boolean resolved = true;
1000: for (int i = 0; i < m_parameterNames.length; i++) {
1001: String name = m_parameterNames[i];
1002: if (name == null) {
1003: resolved = false;
1004: }
1005: }
1006: if (resolved) {
1007: m_methodInfo.m_parameterNames = m_parameterNames;
1008: }
1009: }
1010:
1011: /**
1012: * Update the parameter name given the parameter information the index is the one from the register ie a long or
1013: * double will needs 2 register
1014: *
1015: * @param registerIndex
1016: * @param parameterName
1017: */
1018: // public void pushParameterNameFromRegister(int registerIndex, String parameterName) {
1019: // int registerStart = m_isStatic ? 0 : 1;
1020: //
1021: // // assume we have a stack starting at the first parameter
1022: // int registerIndexFrom0 = registerIndex - registerStart;
1023: // Type[] parameters = Type.getArgumentTypes(m_member.desc);
1024: // int typeIndex = AsmHelper.getTypeIndexOf(parameters, registerIndexFrom0);
1025: // if (typeIndex >= 0 && typeIndex < m_parameterNames.length) {
1026: // m_parameterNames[typeIndex] = parameterName;
1027: // } else {
1028: // throw new DefinitionException("Could not register parameter named " + parameterName + " from register "
1029: // + registerIndex + " for " + m_member.name + "." + m_member.desc);
1030: // }
1031: // }
1032: }
1033:
1034: /**
1035: * Converts the class name from VM type class name to Java type class name.
1036: *
1037: * @param className the VM type class name
1038: * @return the Java type class name
1039: */
1040: private static String getJavaClassName(final String className) {
1041: String javaClassName;
1042: if (className.startsWith("[")) {
1043: javaClassName = Type.getType(className).getClassName();
1044: } else {
1045: javaClassName = className.replace('/', '.');
1046: }
1047: return javaClassName;
1048: }
1049:
1050: /**
1051: * Returns the annotation reader
1052: *
1053: * @return
1054: */
1055: public AnnotationReader getAnnotationReader() {
1056: if (m_annotationReader == null) {
1057: ClassLoader loader = ContextClassLoader
1058: .getLoaderOrSystemLoader((ClassLoader) m_loaderRef
1059: .get());
1060: m_annotationReader = AnnotationReader.getReaderFor(m_name,
1061: loader);
1062: }
1063: return m_annotationReader;
1064: }
1065: }
|