View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.mule.config.spring.util;
21  
22  import java.io.ByteArrayInputStream;
23  import java.io.ByteArrayOutputStream;
24  import java.io.EOFException;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.lang.reflect.Constructor;
28  import java.lang.reflect.Field;
29  import java.lang.reflect.InvocationTargetException;
30  import java.lang.reflect.Member;
31  import java.lang.reflect.Method;
32  import java.util.HashMap;
33  import java.util.Map;
34  
35  /**
36   * This is the class file reader for obtaining the parameter names for declared
37   * methods in a class. The class must have debugging attributes for us to obtain
38   * this information.
39   * <p>
40   * This does not work for inherited methods. To obtain parameter names for
41   * inherited methods, you must use a paramReader for the class that originally
42   * declared the method.
43   * <p>
44   * don't get tricky, it's the bare minimum. Instances of this class are not
45   * threadsafe -- don't share them.
46   * <p>
47   * 
48   * @author Edwin Smith, Macromedia
49   */
50  public class ClassReader extends ByteArrayInputStream {
51      // constants values that appear in java class files,
52      // from jvm spec 2nd ed, section 4.4, pp 103
53      private static final int CONSTANT_CLASS = 7;
54      private static final int CONSTANT_FIELDREF = 9;
55      private static final int CONSTANT_METHODREF = 10;
56      private static final int CONSTANT_INTERFACE_METHOD_REF = 11;
57      private static final int CONSTANT_STRING = 8;
58      private static final int CONSTANT_INTEGER = 3;
59      private static final int CONSTANT_FLOAT = 4;
60      private static final int CONSTANT_LONG = 5;
61      private static final int CONSTANT_DOUBLE = 6;
62      private static final int CONSTANT_NAME_AND_TYPE = 12;
63      private static final int CONSTANT_UTF_8 = 1;
64      /**
65       * the constant pool. constant pool indices in the class file directly index
66       * into this array. The value stored in this array is the position in the
67       * class file where that constant begins.
68       */
69      private int[] cpoolIndex;
70      private Object[] cpool;
71  
72      private Map<String, Method> attrMethods;
73  
74      protected ClassReader(byte buf[], Map<String, Method> attrMethods) {
75          super(buf);
76  
77          this.attrMethods = attrMethods;
78      }
79      
80      /**
81       * load the bytecode for a given class, by using the class's defining
82       * classloader and assuming that for a class named P.C, the bytecodes are in
83       * a resource named /P/C.class.
84       * 
85       * @param c the class of interest
86       * @return a byte array containing the bytecode
87       * @throws IOException
88       */
89      protected static byte[] getBytes(Class c) throws IOException {
90          InputStream fin = c.getResourceAsStream('/' + c.getName().replace('.', '/') + ".class");
91          if (fin == null) {
92              throw new IOException();
93          }
94          try {
95              ByteArrayOutputStream out = new ByteArrayOutputStream();
96              byte[] buf = new byte[1024];
97              int actual;
98              do {
99                  actual = fin.read(buf);
100                 if (actual > 0) {
101                     out.write(buf, 0, actual);
102                 }
103             } while (actual > 0);
104             return out.toByteArray();
105         } finally {
106             fin.close();
107         }
108     }
109 
110     static String classDescriptorToName(String desc) {
111         return desc.replace('/', '.');
112     }
113 
114     protected static Map<String, Method> findAttributeReaders(Class c) {
115         Map<String, Method> map = new HashMap<String, Method>();
116         Method[] methods = c.getMethods();
117 
118         for (int i = 0; i < methods.length; i++) {
119             String name = methods[i].getName();
120             if (name.startsWith("read") && methods[i].getReturnType() == void.class) {
121                 map.put(name.substring(4), methods[i]);
122             }
123         }
124 
125         return map;
126     }
127 
128     protected static String getSignature(Member method, Class[] paramTypes) {
129         // compute the method descriptor
130 
131         StringBuffer b = new StringBuffer((method instanceof Method) ? method.getName() : "<init>");
132         b.append('(');
133 
134         for (int i = 0; i < paramTypes.length; i++) {
135             addDescriptor(b, paramTypes[i]);
136         }
137 
138         b.append(')');
139         if (method instanceof Method) {
140             addDescriptor(b, ((Method)method).getReturnType());
141         } else if (method instanceof Constructor) {
142             addDescriptor(b, void.class);
143         }
144 
145         return b.toString();
146     }
147 
148     private static void addDescriptor(StringBuffer b, Class c) {
149         if (c.isPrimitive()) {
150             if (c == void.class) {
151                 b.append('V');
152             } else if (c == int.class) {
153                 b.append('I');
154             } else if (c == boolean.class) {
155                 b.append('Z');
156             } else if (c == byte.class) {
157                 b.append('B');
158             } else if (c == short.class) {
159                 b.append('S');
160             } else if (c == long.class) {
161                 b.append('J');
162             } else if (c == char.class) {
163                 b.append('C');
164             } else if (c == float.class) {
165                 b.append('F');
166             } else if (c == double.class) {
167                 b.append('D');
168             }
169         } else if (c.isArray()) {
170             b.append('[');
171             addDescriptor(b, c.getComponentType());
172         } else {
173             b.append('L').append(c.getName().replace('.', '/')).append(';');
174         }
175     }
176 
177     /**
178      * @return the next unsigned 16 bit value
179      */
180     protected final int readShort() {
181         return (read() << 8) | read();
182     }
183 
184     /**
185      * @return the next signed 32 bit value
186      */
187     protected final int readInt() {
188         return (read() << 24) | (read() << 16) | (read() << 8) | read();
189     }
190 
191     /**
192      * skip n bytes in the input stream.
193      */
194     protected void skipFully(int n) throws IOException {
195         while (n > 0) {
196             int c = (int)skip(n);
197             if (c <= 0) {
198                 throw new EOFException();
199             }
200 
201             n -= c;
202         }
203     }
204 
205     protected final Member resolveMethod(int index) throws IOException, ClassNotFoundException,
206         NoSuchMethodException {
207         int oldPos = pos;
208         try {
209             Member m = (Member)cpool[index];
210             if (m == null) {
211                 pos = cpoolIndex[index];
212                 Class owner = resolveClass(readShort());
213                 NameAndType nt = resolveNameAndType(readShort());
214                 String signature = nt.name + nt.type;
215                 if ("<init>".equals(nt.name)) {
216                     Constructor[] ctors = owner.getConstructors();
217                     for (int i = 0; i < ctors.length; i++) {
218                         String sig = getSignature(ctors[i], ctors[i].getParameterTypes());
219                         if (sig.equals(signature)) {
220                             cpool[index] = ctors[i];
221                             m = ctors[i];
222                             return m;
223                         }
224                     }
225                 } else {
226                     Method[] methods = owner.getDeclaredMethods();
227                     for (int i = 0; i < methods.length; i++) {
228                         String sig = getSignature(methods[i], methods[i].getParameterTypes());
229                         if (sig.equals(signature)) {
230                             cpool[index] = methods[i];
231                             m = methods[i];
232                             return m;
233                         }
234                     }
235                 }
236                 throw new NoSuchMethodException(signature);
237             }
238             return m;
239         } finally {
240             pos = oldPos;
241         }
242 
243     }
244 
245     protected final Field resolveField(int i) throws IOException, ClassNotFoundException,
246         NoSuchFieldException {
247         int oldPos = pos;
248         try {
249             Field f = (Field)cpool[i];
250             if (f == null) {
251                 pos = cpoolIndex[i];
252                 Class owner = resolveClass(readShort());
253                 NameAndType nt = resolveNameAndType(readShort());
254                 cpool[i] = owner.getDeclaredField(nt.name);
255                 f = owner.getDeclaredField(nt.name);
256             }
257             return f;
258         } finally {
259             pos = oldPos;
260         }
261     }
262 
263     protected final NameAndType resolveNameAndType(int i) throws IOException {
264         int oldPos = pos;
265         try {
266             NameAndType nt = (NameAndType)cpool[i];
267             if (nt == null) {
268                 pos = cpoolIndex[i];
269                 String name = resolveUtf8(readShort());
270                 String type = resolveUtf8(readShort());
271                 cpool[i] = new NameAndType(name, type);
272                 nt = new NameAndType(name, type);
273             }
274             return nt;
275         } finally {
276             pos = oldPos;
277         }
278     }
279 
280     protected final Class resolveClass(int i) throws IOException, ClassNotFoundException {
281         int oldPos = pos;
282         try {
283             Class c = (Class)cpool[i];
284             if (c == null) {
285                 pos = cpoolIndex[i];
286                 String name = resolveUtf8(readShort());
287                 cpool[i] = Class.forName(classDescriptorToName(name));
288                 c = Class.forName(classDescriptorToName(name));
289             }
290             return c;
291         } finally {
292             pos = oldPos;
293         }
294     }
295 
296     protected final String resolveUtf8(int i) throws IOException {
297         int oldPos = pos;
298         try {
299             String s = (String)cpool[i];
300             if (s == null) {
301                 pos = cpoolIndex[i];
302                 int len = readShort();
303                 skipFully(len);
304                 cpool[i] = new String(buf, pos - len, len, "utf-8");
305                 s = new String(buf, pos - len, len, "utf-8");
306             }
307             return s;
308         } finally {
309             pos = oldPos;
310         }
311     }
312 
313     @SuppressWarnings("fallthrough")
314     protected final void readCpool() throws IOException {
315         int count = readShort(); // cpool count
316         cpoolIndex = new int[count];
317         cpool = new Object[count];
318         for (int i = 1; i < count; i++) {
319             int c = read();
320             cpoolIndex[i] = super.pos;
321             // constant pool tag
322             switch (c) {
323             case CONSTANT_FIELDREF:
324             case CONSTANT_METHODREF:
325             case CONSTANT_INTERFACE_METHOD_REF:
326             case CONSTANT_NAME_AND_TYPE:
327 
328                 readShort(); // class index or (12) name index
329                 // fall through
330 
331             case CONSTANT_CLASS:
332             case CONSTANT_STRING:
333 
334                 readShort(); // string index or class index
335                 break;
336 
337             case CONSTANT_LONG:
338             case CONSTANT_DOUBLE:
339 
340                 readInt(); // hi-value
341 
342                 // see jvm spec section 4.4.5 - double and long cpool
343                 // entries occupy two "slots" in the cpool table.
344                 i++;
345                 // fall through
346 
347             case CONSTANT_INTEGER:
348             case CONSTANT_FLOAT:
349 
350                 readInt(); // value
351                 break;
352 
353             case CONSTANT_UTF_8:
354 
355                 int len = readShort();
356                 skipFully(len);
357                 break;
358 
359             default:
360                 // corrupt class file
361                 throw new IllegalStateException();
362             }
363         }
364     }
365 
366     protected final void skipAttributes() throws IOException {
367         int count = readShort();
368         for (int i = 0; i < count; i++) {
369             readShort(); // name index
370             skipFully(readInt());
371         }
372     }
373 
374     /**
375      * read an attributes array. the elements of a class file that can contain
376      * attributes are: fields, methods, the class itself, and some other types
377      * of attributes.
378      */
379     protected final void readAttributes() throws IOException {
380         int count = readShort();
381         for (int i = 0; i < count; i++) {
382             int nameIndex = readShort(); // name index
383             int attrLen = readInt();
384             int curPos = pos;
385 
386             String attrName = resolveUtf8(nameIndex);
387 
388             Method m = attrMethods.get(attrName);
389 
390             if (m != null) {
391                 try {
392                     m.invoke(this, new Object[] {});
393                 } catch (IllegalAccessException e) {
394                     pos = curPos;
395                     skipFully(attrLen);
396                 } catch (InvocationTargetException e) {
397                     try {
398                         throw e.getTargetException();
399                     } catch (Error ex) {
400                         throw ex;
401                     } catch (RuntimeException ex) {
402                         throw ex;
403                     } catch (IOException ex) {
404                         throw ex;
405                     } catch (Throwable ex) {
406                         pos = curPos;
407                         skipFully(attrLen);
408                     }
409                 }
410             } else {
411                 // don't care what attribute this is
412                 skipFully(attrLen);
413             }
414         }
415     }
416 
417     /**
418      * read a code attribute
419      * 
420      * @throws IOException
421      */
422     public void readCode() throws IOException {
423         readShort(); // max stack
424         readShort(); // max locals
425         skipFully(readInt()); // code
426         skipFully(8 * readShort()); // exception table
427 
428         // read the code attributes (recursive). This is where
429         // we will find the LocalVariableTable attribute.
430         readAttributes();
431     }
432 
433     private static class NameAndType {
434         String name;
435         String type;
436 
437         public NameAndType(String name, String type) {
438             this.name = name;
439             this.type = type;
440         }
441     }
442 }