Mega Code Archive

 
Categories / Java / Reflection
 

Class file reader for obtaining the parameter names for declared methods in a class

/**  * Licensed to the Apache Software Foundation (ASF) under one  * or more contributor license agreements. See the NOTICE file  * distributed with this work for additional information  * regarding copyright ownership. The ASF licenses this file  * to you under the Apache License, Version 2.0 (the  * "License"); you may not use this file except in compliance  * with the License. You may obtain a copy of the License at  *  * http://www.apache.org/licenses/LICENSE-2.0  *  * Unless required by applicable law or agreed to in writing,  * software distributed under the License is distributed on an  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY  * KIND, either express or implied. See the License for the  * specific language governing permissions and limitations  * under the License.  */ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; /**  * This is the class file reader for obtaining the parameter names for declared  * methods in a class. The class must have debugging attributes for us to obtain  * this information.  * <p>  * This does not work for inherited methods. To obtain parameter names for  * inherited methods, you must use a paramReader for the class that originally  * declared the method.  * <p>  * don't get tricky, it's the bare minimum. Instances of this class are not  * threadsafe -- don't share them.  * <p>  *   * @author Edwin Smith, Macromedia  */ public class ClassReader extends ByteArrayInputStream {     // constants values that appear in java class files,     // from jvm spec 2nd ed, section 4.4, pp 103     private static final int CONSTANT_CLASS = 7;     private static final int CONSTANT_FIELDREF = 9;     private static final int CONSTANT_METHODREF = 10;     private static final int CONSTANT_INTERFACE_METHOD_REF = 11;     private static final int CONSTANT_STRING = 8;     private static final int CONSTANT_INTEGER = 3;     private static final int CONSTANT_FLOAT = 4;     private static final int CONSTANT_LONG = 5;     private static final int CONSTANT_DOUBLE = 6;     private static final int CONSTANT_NAME_AND_TYPE = 12;     private static final int CONSTANT_UTF_8 = 1;     /**      * the constant pool. constant pool indices in the class file directly index      * into this array. The value stored in this array is the position in the      * class file where that constant begins.      */     private int[] cpoolIndex;     private Object[] cpool;     private Map<String, Method> attrMethods;     protected ClassReader(byte buf[], Map<String, Method> attrMethods) {         super(buf);         this.attrMethods = attrMethods;     }          /**      * load the bytecode for a given class, by using the class's defining      * classloader and assuming that for a class named P.C, the bytecodes are in      * a resource named /P/C.class.      *       * @param c the class of interest      * @return a byte array containing the bytecode      * @throws IOException      */     protected static byte[] getBytes(Class c) throws IOException {         InputStream fin = c.getResourceAsStream('/' + c.getName().replace('.', '/') + ".class");         if (fin == null) {             throw new IOException();         }         try {             ByteArrayOutputStream out = new ByteArrayOutputStream();             byte[] buf = new byte[1024];             int actual;             do {                 actual = fin.read(buf);                 if (actual > 0) {                     out.write(buf, 0, actual);                 }             } while (actual > 0);             return out.toByteArray();         } finally {             fin.close();         }     }     static String classDescriptorToName(String desc) {         return desc.replace('/', '.');     }     protected static Map<String, Method> findAttributeReaders(Class c) {         Map<String, Method> map = new HashMap<String, Method>();         Method[] methods = c.getMethods();         for (int i = 0; i < methods.length; i++) {             String name = methods[i].getName();             if (name.startsWith("read") && methods[i].getReturnType() == void.class) {                 map.put(name.substring(4), methods[i]);             }         }         return map;     }     protected static String getSignature(Member method, Class[] paramTypes) {         // compute the method descriptor         StringBuffer b = new StringBuffer((method instanceof Method) ? method.getName() : "<init>");         b.append('(');         for (int i = 0; i < paramTypes.length; i++) {             addDescriptor(b, paramTypes[i]);         }         b.append(')');         if (method instanceof Method) {             addDescriptor(b, ((Method)method).getReturnType());         } else if (method instanceof Constructor) {             addDescriptor(b, void.class);         }         return b.toString();     }     private static void addDescriptor(StringBuffer b, Class c) {         if (c.isPrimitive()) {             if (c == void.class) {                 b.append('V');             } else if (c == int.class) {                 b.append('I');             } else if (c == boolean.class) {                 b.append('Z');             } else if (c == byte.class) {                 b.append('B');             } else if (c == short.class) {                 b.append('S');             } else if (c == long.class) {                 b.append('J');             } else if (c == char.class) {                 b.append('C');             } else if (c == float.class) {                 b.append('F');             } else if (c == double.class) {                 b.append('D');             }         } else if (c.isArray()) {             b.append('[');             addDescriptor(b, c.getComponentType());         } else {             b.append('L').append(c.getName().replace('.', '/')).append(';');         }     }     /**      * @return the next unsigned 16 bit value      */     protected final int readShort() {         return (read() << 8) | read();     }     /**      * @return the next signed 32 bit value      */     protected final int readInt() {         return (read() << 24) | (read() << 16) | (read() << 8) | read();     }     /**      * skip n bytes in the input stream.      */     protected void skipFully(int n) throws IOException {         while (n > 0) {             int c = (int)skip(n);             if (c <= 0) {                 throw new EOFException();             }             n -= c;         }     }     protected final Member resolveMethod(int index) throws IOException, ClassNotFoundException,         NoSuchMethodException {         int oldPos = pos;         try {             Member m = (Member)cpool[index];             if (m == null) {                 pos = cpoolIndex[index];                 Class owner = resolveClass(readShort());                 NameAndType nt = resolveNameAndType(readShort());                 String signature = nt.name + nt.type;                 if ("<init>".equals(nt.name)) {                     Constructor[] ctors = owner.getConstructors();                     for (int i = 0; i < ctors.length; i++) {                         String sig = getSignature(ctors[i], ctors[i].getParameterTypes());                         if (sig.equals(signature)) {                             cpool[index] = ctors[i];                             m = ctors[i];                             return m;                         }                     }                 } else {                     Method[] methods = owner.getDeclaredMethods();                     for (int i = 0; i < methods.length; i++) {                         String sig = getSignature(methods[i], methods[i].getParameterTypes());                         if (sig.equals(signature)) {                             cpool[index] = methods[i];                             m = methods[i];                             return m;                         }                     }                 }                 throw new NoSuchMethodException(signature);             }             return m;         } finally {             pos = oldPos;         }     }     protected final Field resolveField(int i) throws IOException, ClassNotFoundException,         NoSuchFieldException {         int oldPos = pos;         try {             Field f = (Field)cpool[i];             if (f == null) {                 pos = cpoolIndex[i];                 Class owner = resolveClass(readShort());                 NameAndType nt = resolveNameAndType(readShort());                 cpool[i] = owner.getDeclaredField(nt.name);                 f = owner.getDeclaredField(nt.name);             }             return f;         } finally {             pos = oldPos;         }     }     protected final NameAndType resolveNameAndType(int i) throws IOException {         int oldPos = pos;         try {             NameAndType nt = (NameAndType)cpool[i];             if (nt == null) {                 pos = cpoolIndex[i];                 String name = resolveUtf8(readShort());                 String type = resolveUtf8(readShort());                 cpool[i] = new NameAndType(name, type);                 nt = new NameAndType(name, type);             }             return nt;         } finally {             pos = oldPos;         }     }     protected final Class resolveClass(int i) throws IOException, ClassNotFoundException {         int oldPos = pos;         try {             Class c = (Class)cpool[i];             if (c == null) {                 pos = cpoolIndex[i];                 String name = resolveUtf8(readShort());                 cpool[i] = Class.forName(classDescriptorToName(name));                 c = Class.forName(classDescriptorToName(name));             }             return c;         } finally {             pos = oldPos;         }     }     protected final String resolveUtf8(int i) throws IOException {         int oldPos = pos;         try {             String s = (String)cpool[i];             if (s == null) {                 pos = cpoolIndex[i];                 int len = readShort();                 skipFully(len);                 cpool[i] = new String(buf, pos - len, len, "utf-8");                 s = new String(buf, pos - len, len, "utf-8");             }             return s;         } finally {             pos = oldPos;         }     }     @SuppressWarnings("fallthrough")     protected final void readCpool() throws IOException {         int count = readShort(); // cpool count         cpoolIndex = new int[count];         cpool = new Object[count];         for (int i = 1; i < count; i++) {             int c = read();             cpoolIndex[i] = super.pos;             // constant pool tag             switch (c) {             case CONSTANT_FIELDREF:             case CONSTANT_METHODREF:             case CONSTANT_INTERFACE_METHOD_REF:             case CONSTANT_NAME_AND_TYPE:                 readShort(); // class index or (12) name index                 // fall through             case CONSTANT_CLASS:             case CONSTANT_STRING:                 readShort(); // string index or class index                 break;             case CONSTANT_LONG:             case CONSTANT_DOUBLE:                 readInt(); // hi-value                 // see jvm spec section 4.4.5 - double and long cpool                 // entries occupy two "slots" in the cpool table.                 i++;                 // fall through             case CONSTANT_INTEGER:             case CONSTANT_FLOAT:                 readInt(); // value                 break;             case CONSTANT_UTF_8:                 int len = readShort();                 skipFully(len);                 break;             default:                 // corrupt class file                 throw new IllegalStateException();             }         }     }     protected final void skipAttributes() throws IOException {         int count = readShort();         for (int i = 0; i < count; i++) {             readShort(); // name index             skipFully(readInt());         }     }     /**      * read an attributes array. the elements of a class file that can contain      * attributes are: fields, methods, the class itself, and some other types      * of attributes.      */     protected final void readAttributes() throws IOException {         int count = readShort();         for (int i = 0; i < count; i++) {             int nameIndex = readShort(); // name index             int attrLen = readInt();             int curPos = pos;             String attrName = resolveUtf8(nameIndex);             Method m = attrMethods.get(attrName);             if (m != null) {                 try {                     m.invoke(this, new Object[] {});                 } catch (IllegalAccessException e) {                     pos = curPos;                     skipFully(attrLen);                 } catch (InvocationTargetException e) {                     try {                         throw e.getTargetException();                     } catch (Error ex) {                         throw ex;                     } catch (RuntimeException ex) {                         throw ex;                     } catch (IOException ex) {                         throw ex;                     } catch (Throwable ex) {                         pos = curPos;                         skipFully(attrLen);                     }                 }             } else {                 // don't care what attribute this is                 skipFully(attrLen);             }         }     }     /**      * read a code attribute      *       * @throws IOException      */     public void readCode() throws IOException {         readShort(); // max stack         readShort(); // max locals         skipFully(readInt()); // code         skipFully(8 * readShort()); // exception table         // read the code attributes (recursive). This is where         // we will find the LocalVariableTable attribute.         readAttributes();     }     private static class NameAndType {         String name;         String type;         public NameAndType(String name, String type) {             this.name = name;             this.type = type;         }     } }