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 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}