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    }