View Javadoc

1   package serp.bytecode.lowlevel;
2   
3   import java.io.*;
4   
5   /**
6    * Efficient representation of the constant pool as a table. This class
7    * can be used to parse out bits of information from bytecode without
8    * instantiating a full {@link serp.bytecode.BCClass}.
9    *
10   * @author Abe White
11   */
12  public class ConstantPoolTable {
13      private byte[] _bytecode = null;
14      private int[] _table = null;
15      private int _idx = 0;
16  
17      /**
18       * Constructor; supply class bytecode.
19       */
20      public ConstantPoolTable(byte[] b) {
21          _bytecode = b;
22          _table = new int[readUnsignedShort(b, 8)];
23          _idx = parse(b, _table);
24      }
25  
26      /**
27       * Constructor; supply input stream to bytecode.
28       */
29      public ConstantPoolTable(InputStream in) throws IOException {
30          this(toByteArray(in));
31      }
32  
33      /**
34       * Allows static computation of the byte index after the constant
35       * pool without caching constant pool information.
36       */
37      public static int getEndIndex(byte[] b) {
38          return parse(b, null);
39      }
40  
41      /**
42       * Parse class bytecode, returning end index of pool.
43       */
44      private static int parse(byte[] b, int[] table) {
45          // each entry is the index in the byte array of the data for a const
46          // pool entry
47          int entries = (table == null) ? readUnsignedShort(b, 8) : table.length;
48          int idx = 10;
49          for (int i = 1; i < entries; i++) {
50              if (table != null)
51                  table[i] = idx + 1; // skip entry type
52  
53              switch (b[idx]) {
54              case 1: // utf8
55                  idx += (3 + readUnsignedShort(b, idx + 1));
56                  break;
57              case 3: // integer
58              case 4: // float
59              case 9: // field
60              case 10: // method
61              case 11: // interface method
62              case 12: // name
63                  idx += 5;
64                  break;
65              case 5: // long
66              case 6: // double
67                  idx += 9;
68                  i++; // wide entry
69                  break;
70              default:
71                  idx += 3;
72              }
73          }
74          return idx;
75      }
76  
77      /**
78       * Read a byte value at the given offset into the given bytecode.
79       */
80      public static int readByte(byte[] b, int idx) {
81          return b[idx] & 0xFF;
82      }
83  
84      /**
85       * Read an unsigned short value at the given offset into the given bytecode.
86       */
87      public static int readUnsignedShort(byte[] b, int idx) {
88          return (readByte(b, idx) << 8) | readByte(b, idx + 1);
89      }
90  
91      /**
92       * Read an int value at the given offset into the given bytecode.
93       */
94      public static int readInt(byte[] b, int idx) {
95          return (readByte(b, idx) << 24) | (readByte(b, idx + 1) << 16) 
96              | (readByte(b, idx + 2) << 8) | readByte(b, idx + 3);
97      }
98  
99      /**
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 }