001package serp.bytecode.lowlevel;
002
003import 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 */
012public 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}