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