001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019:
020: package org.apache.axis2.description.java2wsdl.bytecode;
021:
022: import java.io.ByteArrayInputStream;
023: import java.io.ByteArrayOutputStream;
024: import java.io.EOFException;
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.lang.reflect.Constructor;
028: import java.lang.reflect.Field;
029: import java.lang.reflect.InvocationTargetException;
030: import java.lang.reflect.Member;
031: import java.lang.reflect.Method;
032: import java.util.HashMap;
033: import java.util.Map;
034:
035: /**
036: * This is the class file reader for obtaining the parameter names
037: * for declared methods in a class. The class must have debugging
038: * attributes for us to obtain this information. <p>
039: * <p/>
040: * This does not work for inherited methods. To obtain parameter
041: * names for inherited methods, you must use a paramReader for the
042: * class that originally declared the method. <p>
043: * <p/>
044: * don't get tricky, it's the bare minimum. Instances of this class
045: * are not threadsafe -- don't share them. <p>
046: */
047: public class ClassReader extends ByteArrayInputStream {
048: // constants values that appear in java class files,
049: // from jvm spec 2nd ed, section 4.4, pp 103
050: private final int CONSTANT_Class = 7;
051: private final int CONSTANT_Fieldref = 9;
052: private final int CONSTANT_Methodref = 10;
053: private final int CONSTANT_InterfaceMethodref = 11;
054: private final int CONSTANT_String = 8;
055: private final int CONSTANT_Integer = 3;
056: private final int CONSTANT_Float = 4;
057: private final int CONSTANT_Long = 5;
058: private final int CONSTANT_Double = 6;
059: private final int CONSTANT_NameAndType = 12;
060: private final int CONSTANT_Utf8 = 1;
061: /**
062: * the constant pool. constant pool indices in the class file
063: * directly index into this array. The value stored in this array
064: * is the position in the class file where that constant begins.
065: */
066: private int[] cpoolIndex;
067: private Object[] cpool;
068:
069: private Map attrMethods;
070:
071: /**
072: * Loads the bytecode for a given class, by using the class's defining
073: * classloader and assuming that for a class named P.C, the bytecodes are
074: * in a resource named /P/C.class.
075: *
076: * @param c the class of interest
077: * @return Returns a byte array containing the bytecode
078: * @throws IOException
079: */
080: protected static byte[] getBytes(Class c) throws IOException {
081: InputStream fin = c.getResourceAsStream('/'
082: + c.getName().replace('.', '/') + ".class");
083: if (fin == null) {
084: throw new IOException("Unable to load bytecode for class "
085: + c.getName());
086: }
087: try {
088: ByteArrayOutputStream out = new ByteArrayOutputStream();
089: byte[] buf = new byte[1024];
090: int actual;
091: do {
092: actual = fin.read(buf);
093: if (actual > 0) {
094: out.write(buf, 0, actual);
095: }
096: } while (actual > 0);
097: return out.toByteArray();
098: } finally {
099: fin.close();
100: }
101: }
102:
103: static String classDescriptorToName(String desc) {
104: return desc.replace('/', '.');
105: }
106:
107: protected static Map findAttributeReaders(Class c) {
108: HashMap map = new HashMap();
109: Method[] methods = c.getMethods();
110:
111: for (int i = 0; i < methods.length; i++) {
112: String name = methods[i].getName();
113: if (name.startsWith("read")
114: && methods[i].getReturnType() == void.class) {
115: map.put(name.substring(4), methods[i]);
116: }
117: }
118:
119: return map;
120: }
121:
122: protected static String getSignature(Member method,
123: Class[] paramTypes) {
124: // compute the method descriptor
125:
126: StringBuffer b = new StringBuffer(
127: (method instanceof Method) ? method.getName()
128: : "<init>");
129: b.append('(');
130:
131: for (int i = 0; i < paramTypes.length; i++) {
132: addDescriptor(b, paramTypes[i]);
133: }
134:
135: b.append(')');
136: if (method instanceof Method) {
137: addDescriptor(b, ((Method) method).getReturnType());
138: } else if (method instanceof Constructor) {
139: addDescriptor(b, void.class);
140: }
141:
142: return b.toString();
143: }
144:
145: private static void addDescriptor(StringBuffer b, Class c) {
146: if (c.isPrimitive()) {
147: if (c == void.class)
148: b.append('V');
149: else if (c == int.class)
150: b.append('I');
151: else if (c == boolean.class)
152: b.append('Z');
153: else if (c == byte.class)
154: b.append('B');
155: else if (c == short.class)
156: b.append('S');
157: else if (c == long.class)
158: b.append('J');
159: else if (c == char.class)
160: b.append('C');
161: else if (c == float.class)
162: b.append('F');
163: else if (c == double.class)
164: b.append('D');
165: } else if (c.isArray()) {
166: b.append('[');
167: addDescriptor(b, c.getComponentType());
168: } else {
169: b.append('L').append(c.getName().replace('.', '/')).append(
170: ';');
171: }
172: }
173:
174: /**
175: * @return Returns the next unsigned 16 bit value.
176: */
177: protected final int readShort() {
178: return (read() << 8) | read();
179: }
180:
181: /**
182: * @return Returns the next signed 32 bit value.
183: */
184: protected final int readInt() {
185: return (read() << 24) | (read() << 16) | (read() << 8) | read();
186: }
187:
188: /**
189: * Skips n bytes in the input stream.
190: */
191: protected void skipFully(int n) throws IOException {
192: while (n > 0) {
193: int c = (int) skip(n);
194: if (c <= 0)
195: throw new EOFException(
196: "Error looking for paramter names in bytecode: unexpected end of file");
197: n -= c;
198: }
199: }
200:
201: protected final Member resolveMethod(int index) throws IOException,
202: ClassNotFoundException, NoSuchMethodException {
203: int oldPos = pos;
204: try {
205: Member m = (Member) cpool[index];
206: if (m == null) {
207: pos = cpoolIndex[index];
208: Class owner = resolveClass(readShort());
209: NameAndType nt = resolveNameAndType(readShort());
210: String signature = nt.name + nt.type;
211: if (nt.name.equals("<init>")) {
212: Constructor[] ctors = owner.getConstructors();
213: for (int i = 0; i < ctors.length; i++) {
214: String sig = getSignature(ctors[i], ctors[i]
215: .getParameterTypes());
216: if (sig.equals(signature)) {
217: cpool[index] = m = ctors[i];
218: return m;
219: }
220: }
221: } else {
222: Method[] methods = owner.getDeclaredMethods();
223: for (int i = 0; i < methods.length; i++) {
224: String sig = getSignature(methods[i],
225: methods[i].getParameterTypes());
226: if (sig.equals(signature)) {
227: cpool[index] = m = methods[i];
228: return m;
229: }
230: }
231: }
232: throw new NoSuchMethodException(signature);
233: }
234: return m;
235: } finally {
236: pos = oldPos;
237: }
238:
239: }
240:
241: protected final Field resolveField(int i) throws IOException,
242: ClassNotFoundException, NoSuchFieldException {
243: int oldPos = pos;
244: try {
245: Field f = (Field) cpool[i];
246: if (f == null) {
247: pos = cpoolIndex[i];
248: Class owner = resolveClass(readShort());
249: NameAndType nt = resolveNameAndType(readShort());
250: cpool[i] = f = owner.getDeclaredField(nt.name);
251: }
252: return f;
253: } finally {
254: pos = oldPos;
255: }
256: }
257:
258: private static class NameAndType {
259: String name;
260: String type;
261:
262: public NameAndType(String name, String type) {
263: this .name = name;
264: this .type = type;
265: }
266: }
267:
268: protected final NameAndType resolveNameAndType(int i)
269: throws IOException {
270: int oldPos = pos;
271: try {
272: NameAndType nt = (NameAndType) cpool[i];
273: if (nt == null) {
274: pos = cpoolIndex[i];
275: String name = resolveUtf8(readShort());
276: String type = resolveUtf8(readShort());
277: cpool[i] = nt = new NameAndType(name, type);
278: }
279: return nt;
280: } finally {
281: pos = oldPos;
282: }
283: }
284:
285: protected final Class resolveClass(int i) throws IOException,
286: ClassNotFoundException {
287: int oldPos = pos;
288: try {
289: Class c = (Class) cpool[i];
290: if (c == null) {
291: pos = cpoolIndex[i];
292: String name = resolveUtf8(readShort());
293: cpool[i] = c = Class
294: .forName(classDescriptorToName(name));
295: }
296: return c;
297: } finally {
298: pos = oldPos;
299: }
300: }
301:
302: protected final String resolveUtf8(int i) throws IOException {
303: int oldPos = pos;
304: try {
305: String s = (String) cpool[i];
306: if (s == null) {
307: pos = cpoolIndex[i];
308: int len = readShort();
309: skipFully(len);
310: cpool[i] = s = new String(buf, pos - len, len, "utf-8");
311: }
312: return s;
313: } finally {
314: pos = oldPos;
315: }
316: }
317:
318: protected final void readCpool() throws IOException {
319: int count = readShort(); // cpool count
320: cpoolIndex = new int[count];
321: cpool = new Object[count];
322: for (int i = 1; i < count; i++) {
323: int c = read();
324: cpoolIndex[i] = super .pos;
325: switch (c) // constant pool tag
326: {
327: case CONSTANT_Fieldref:
328: case CONSTANT_Methodref:
329: case CONSTANT_InterfaceMethodref:
330: case CONSTANT_NameAndType:
331:
332: readShort(); // class index or (12) name index
333: // fall through
334:
335: case CONSTANT_Class:
336: case CONSTANT_String:
337:
338: readShort(); // string index or class index
339: break;
340:
341: case CONSTANT_Long:
342: case CONSTANT_Double:
343:
344: readInt(); // hi-value
345:
346: // see jvm spec section 4.4.5 - double and long cpool
347: // entries occupy two "slots" in the cpool table.
348: i++;
349: // fall through
350:
351: case CONSTANT_Integer:
352: case CONSTANT_Float:
353:
354: readInt(); // value
355: break;
356:
357: case CONSTANT_Utf8:
358:
359: int len = readShort();
360: skipFully(len);
361: break;
362:
363: default:
364: // corrupt class file
365: throw new IllegalStateException(
366: "Error looking for paramter names in bytecode: unexpected bytes in file");
367: }
368: }
369: }
370:
371: protected final void skipAttributes() throws IOException {
372: int count = readShort();
373: for (int i = 0; i < count; i++) {
374: readShort(); // name index
375: skipFully(readInt());
376: }
377: }
378:
379: /**
380: * Reads an attributes array. The elements of a class file that
381: * can contain attributes are: fields, methods, the class itself,
382: * and some other types of attributes.
383: */
384: protected final void readAttributes() throws IOException {
385: int count = readShort();
386: for (int i = 0; i < count; i++) {
387: int nameIndex = readShort(); // name index
388: int attrLen = readInt();
389: int curPos = pos;
390:
391: String attrName = resolveUtf8(nameIndex);
392:
393: Method m = (Method) attrMethods.get(attrName);
394:
395: if (m != null) {
396: try {
397: m.invoke(this , new Object[] {});
398: } catch (IllegalAccessException e) {
399: pos = curPos;
400: skipFully(attrLen);
401: } catch (InvocationTargetException e) {
402: try {
403: throw e.getTargetException();
404: } catch (Error ex) {
405: throw ex;
406: } catch (RuntimeException ex) {
407: throw ex;
408: } catch (IOException ex) {
409: throw ex;
410: } catch (Throwable ex) {
411: pos = curPos;
412: skipFully(attrLen);
413: }
414: }
415: } else {
416: // don't care what attribute this is
417: skipFully(attrLen);
418: }
419: }
420: }
421:
422: /**
423: * Reads a code attribute.
424: *
425: * @throws IOException
426: */
427: public void readCode() throws IOException {
428: readShort(); // max stack
429: readShort(); // max locals
430: skipFully(readInt()); // code
431: skipFully(8 * readShort()); // exception table
432:
433: // read the code attributes (recursive). This is where
434: // we will find the LocalVariableTable attribute.
435: readAttributes();
436: }
437:
438: protected ClassReader(byte buf[], Map attrMethods) {
439: super(buf);
440:
441: this.attrMethods = attrMethods;
442: }
443: }
|