001 package serp.bytecode.lowlevel;
002
003 import java.io.*;
004
005 /**
006 * Efficient representation of the constant pool as a table. This class
007 * can be used to parse out bits of information from bytecode without
008 * instantiating a full {@link serp.bytecode.BCClass}.
009 *
010 * @author Abe White
011 */
012 public class ConstantPoolTable {
013 private byte[] _bytecode = null;
014 private int[] _table = null;
015 private int _idx = 0;
016
017 /**
018 * Constructor; supply class bytecode.
019 */
020 public ConstantPoolTable(byte[] b) {
021 _bytecode = b;
022 _table = new int[readUnsignedShort(b, 8)];
023 _idx = parse(b, _table);
024 }
025
026 /**
027 * Constructor; supply input stream to bytecode.
028 */
029 public ConstantPoolTable(InputStream in) throws IOException {
030 this(toByteArray(in));
031 }
032
033 /**
034 * Allows static computation of the byte index after the constant
035 * pool without caching constant pool information.
036 */
037 public static int getEndIndex(byte[] b) {
038 return parse(b, null);
039 }
040
041 /**
042 * Parse class bytecode, returning end index of pool.
043 */
044 private static int parse(byte[] b, int[] table) {
045 // each entry is the index in the byte array of the data for a const
046 // pool entry
047 int entries = (table == null) ? readUnsignedShort(b, 8) : table.length;
048 int idx = 10;
049 for (int i = 1; i < entries; i++) {
050 if (table != null)
051 table[i] = idx + 1; // skip entry type
052
053 switch (b[idx]) {
054 case 1: // utf8
055 idx += (3 + readUnsignedShort(b, idx + 1));
056 break;
057 case 3: // integer
058 case 4: // float
059 case 9: // field
060 case 10: // method
061 case 11: // interface method
062 case 18: // invoke dynamic
063 case 12: // name
064 idx += 5;
065 break;
066 case 5: // long
067 case 6: // double
068 idx += 9;
069 i++; // wide entry
070 break;
071 case 15: // method handle
072 idx += 4;
073 break;
074 case 16: // method type
075 default:
076 idx += 3;
077 }
078 }
079 return idx;
080 }
081
082 /**
083 * Read a byte value at the given offset into the given bytecode.
084 */
085 public static int readByte(byte[] b, int idx) {
086 return b[idx] & 0xFF;
087 }
088
089 /**
090 * Read an unsigned short value at the given offset into the given bytecode.
091 */
092 public static int readUnsignedShort(byte[] b, int idx) {
093 return (readByte(b, idx) << 8) | readByte(b, idx + 1);
094 }
095
096 /**
097 * Read an int value at the given offset into the given bytecode.
098 */
099 public static int readInt(byte[] b, int idx) {
100 return (readByte(b, idx) << 24) | (readByte(b, idx + 1) << 16)
101 | (readByte(b, idx + 2) << 8) | readByte(b, idx + 3);
102 }
103
104 /**
105 * Read a long value at the given offset into the given bytecode.
106 */
107 public static long readLong(byte[] b, int idx) {
108 return (readInt(b, idx) << 32) | readInt(b, idx + 4);
109 }
110
111 /**
112 * Read a UTF-8 string value at the given offset into the given bytecode.
113 */
114 public static String readString(byte[] b, int idx) {
115 int len = readUnsignedShort(b, idx);
116 try {
117 return new String(b, idx + 2, len, "UTF-8");
118 } catch (UnsupportedEncodingException uee) {
119 throw new ClassFormatError(uee.toString());
120 }
121 }
122
123 /**
124 * Read the contents of the given stream.
125 */
126 private static byte[] toByteArray(InputStream in) throws IOException {
127 ByteArrayOutputStream bout = new ByteArrayOutputStream();
128 byte[] buf = new byte[1024];
129 for (int r; (r = in.read(buf)) != -1; bout.write(buf, 0, r));
130 return bout.toByteArray();
131 }
132
133 /**
134 * Return the index into the bytecode of the end of the constant pool.
135 */
136 public int getEndIndex() {
137 return _idx;
138 }
139
140 /**
141 * Return the given table entry.
142 */
143 public int get(int idx) {
144 return _table[idx];
145 }
146
147 /**
148 * Read a byte value at the given offset.
149 */
150 public int readByte(int idx) {
151 return readByte(_bytecode, idx);
152 }
153
154 /**
155 * Read an unsigned short value at the given offset.
156 */
157 public int readUnsignedShort(int idx) {
158 return readUnsignedShort(_bytecode, idx);
159 }
160
161 /**
162 * Read an int value at the given offset.
163 */
164 public int readInt(int idx) {
165 return readInt(_bytecode, idx);
166 }
167
168 /**
169 * Read a long value at the given offset.
170 */
171 public long readLong(int idx) {
172 return readLong(_bytecode, idx);
173 }
174
175 /**
176 * Read a UTF-8 string value at the given offset.
177 */
178 public String readString(int idx) {
179 return readString(_bytecode, idx);
180 }
181 }