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 12: // name
063 idx += 5;
064 break;
065 case 5: // long
066 case 6: // double
067 idx += 9;
068 i++; // wide entry
069 break;
070 default:
071 idx += 3;
072 }
073 }
074 return idx;
075 }
076
077 /**
078 * Read a byte value at the given offset into the given bytecode.
079 */
080 public static int readByte(byte[] b, int idx) {
081 return b[idx] & 0xFF;
082 }
083
084 /**
085 * Read an unsigned short value at the given offset into the given bytecode.
086 */
087 public static int readUnsignedShort(byte[] b, int idx) {
088 return (readByte(b, idx) << 8) | readByte(b, idx + 1);
089 }
090
091 /**
092 * Read an int value at the given offset into the given bytecode.
093 */
094 public static int readInt(byte[] b, int idx) {
095 return (readByte(b, idx) << 24) | (readByte(b, idx + 1) << 16)
096 | (readByte(b, idx + 2) << 8) | readByte(b, idx + 3);
097 }
098
099 /**
100 * Read a long value at the given offset into the given bytecode.
101 */
102 public static long readLong(byte[] b, int idx) {
103 return (readInt(b, idx) << 32) | readInt(b, idx + 4);
104 }
105
106 /**
107 * Read a UTF-8 string value at the given offset into the given bytecode.
108 */
109 public static String readString(byte[] b, int idx) {
110 int len = readUnsignedShort(b, idx);
111 try {
112 return new String(b, idx + 2, len, "UTF-8");
113 } catch (UnsupportedEncodingException uee) {
114 throw new ClassFormatError(uee.toString());
115 }
116 }
117
118 /**
119 * Read the contents of the given stream.
120 */
121 private static byte[] toByteArray(InputStream in) throws IOException {
122 ByteArrayOutputStream bout = new ByteArrayOutputStream();
123 byte[] buf = new byte[1024];
124 for (int r; (r = in.read(buf)) != -1; bout.write(buf, 0, r));
125 return bout.toByteArray();
126 }
127
128 /**
129 * Return the index into the bytecode of the end of the constant pool.
130 */
131 public int getEndIndex() {
132 return _idx;
133 }
134
135 /**
136 * Return the given table entry.
137 */
138 public int get(int idx) {
139 return _table[idx];
140 }
141
142 /**
143 * Read a byte value at the given offset.
144 */
145 public int readByte(int idx) {
146 return readByte(_bytecode, idx);
147 }
148
149 /**
150 * Read an unsigned short value at the given offset.
151 */
152 public int readUnsignedShort(int idx) {
153 return readUnsignedShort(_bytecode, idx);
154 }
155
156 /**
157 * Read an int value at the given offset.
158 */
159 public int readInt(int idx) {
160 return readInt(_bytecode, idx);
161 }
162
163 /**
164 * Read a long value at the given offset.
165 */
166 public long readLong(int idx) {
167 return readLong(_bytecode, idx);
168 }
169
170 /**
171 * Read a UTF-8 string value at the given offset.
172 */
173 public String readString(int idx) {
174 return readString(_bytecode, idx);
175 }
176 }