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    }