1 package serp.bytecode.lowlevel;
2
3 import java.io.*;
4 import java.util.*;
5
6 import serp.bytecode.visitor.*;
7 import serp.util.*;
8
9 /***
10 * A bytecode constant pool, containing entries for all strings,
11 * constants, classes, etc referenced in the class structure and method
12 * opcodes. In keeping with the low-level bytecode representation, all pool
13 * indexes are 1-based and {@link LongEntry}s and {@link DoubleEntry}s each
14 * occupy two indexes in the pool.
15 *
16 * @author Abe White
17 */
18 public class ConstantPool implements VisitAcceptor {
19 private List _entries = new ArrayList(50);
20 private Map _lookup = new HashMap(50);
21
22 /***
23 * Default constructor.
24 */
25 public ConstantPool() {
26 }
27
28 /***
29 * Return all the entries in the pool.
30 */
31 public Entry[] getEntries() {
32 List entries = new ArrayList(_entries.size());
33 Entry entry;
34 for (Iterator itr = _entries.iterator(); itr.hasNext();) {
35 entry = (Entry) itr.next();
36 if (entry != null)
37 entries.add(entry);
38 }
39 return (Entry[]) entries.toArray(new Entry[entries.size()]);
40 }
41
42 /***
43 * Retrieve the entry at the specified 1-based index.
44 *
45 * @throws IndexOutOfBoundsException if index is invalid,
46 * including the case that it points to the second slot of a
47 * long or double entry
48 */
49 public Entry getEntry(int index) {
50 Entry entry = (Entry) _entries.get(index - 1);
51 if (entry == null)
52 throw new IndexOutOfBoundsException("index = " + index);
53 return entry;
54 }
55
56 /***
57 * Return the index of the given entry, or 0 if it is not in the pool.
58 */
59 public int indexOf(Entry entry) {
60 if (entry == null || entry.getPool() != this)
61 return 0;
62 return entry.getIndex();
63 }
64
65 /***
66 * Add an entry to the pool.
67 *
68 * @return the index at which the entry was added
69 */
70 public int addEntry(Entry entry) {
71 if (entry.getPool() != this)
72 addEntry(getKey(entry), entry);
73 return entry.getIndex();
74 }
75
76 /***
77 * Add an entry to the pool using the given key.
78 */
79 private int addEntry(Object key, Entry entry) {
80 entry.setPool(this);
81 _entries.add(entry);
82 entry.setIndex(_entries.size());
83 _lookup.put(key, entry);
84 if (entry.isWide())
85 _entries.add(null);
86 return entry.getIndex();
87 }
88
89 /***
90 * Remove the given entry from the pool.
91 *
92 * @return false if the entry is not in the pool, true otherwise
93 */
94 public boolean removeEntry(Entry entry) {
95 if (entry == null || entry.getPool() != this)
96 return false;
97
98 int index = entry.getIndex() - 1;
99 entry.setPool(null);
100 entry.setIndex(0);
101 _entries.remove(index);
102 if (entry.isWide())
103 _entries.remove(index);
104 _lookup.remove(getKey(entry));
105
106
107 Object key;
108 for (int i = index; i < _entries.size(); i++) {
109 entry = (Entry) _entries.get(i);
110 if (entry != null) {
111 key = getKey(entry);
112 _lookup.remove(key);
113 entry.setIndex(i + 1);
114 _lookup.put(key, entry);
115 }
116 }
117 return true;
118 }
119
120 /***
121 * Clear all entries from the pool.
122 */
123 public void clear() {
124 Entry entry;
125 for (Iterator itr = _entries.iterator(); itr.hasNext();) {
126 entry = (Entry) itr.next();
127 if (entry != null) {
128 entry.setPool(null);
129 entry.setIndex(0);
130 }
131 }
132 _entries.clear();
133 _lookup.clear();
134 }
135
136 /***
137 * Return the number of places occupied in the pool, including the fact
138 * that long and double entries occupy two places.
139 */
140 public int size() {
141 return _entries.size();
142 }
143
144 /***
145 * Return the index of the {@link UTF8Entry} with the given value, or
146 * 0 if it does not exist.
147 *
148 * @param add if true, the entry will be added if it does not
149 * already exist, and the new entry's index returned
150 */
151 public int findUTF8Entry(String value, boolean add) {
152 if (value == null) {
153 if (add)
154 throw new NullPointerException("value = null");
155 return 0;
156 }
157
158 int index = find(value);
159 if (!add || index > 0)
160 return index;
161 return addEntry(value, new UTF8Entry(value));
162 }
163
164 /***
165 * Return the constant pool index of the {@link DoubleEntry} for the given
166 * value, or 0 if it does not exist.
167 *
168 * @param value the value to find
169 * @param add if true, the entry will be added if it does not
170 * already exist, and the new entry's index returned
171 */
172 public int findDoubleEntry(double value, boolean add) {
173 Double key = new Double(value);
174 int index = find(key);
175 if (!add || (index > 0))
176 return index;
177 return addEntry(key, new DoubleEntry(value));
178 }
179
180 /***
181 * Return the constant pool index of the {@link FloatEntry} for the given
182 * value, or 0 if it does not exist.
183 *
184 * @param value the value to find
185 * @param add if true, the entry will be added if it does not
186 * already exist, and the new entry's index returned
187 */
188 public int findFloatEntry(float value, boolean add) {
189 Float key = new Float(value);
190 int index = find(key);
191 if (!add || index > 0)
192 return index;
193 return addEntry(key, new FloatEntry(value));
194 }
195
196 /***
197 * Return the constant pool index of the {@link IntEntry} for the given
198 * value, or 0 if it does not exist.
199 *
200 * @param value the value to find
201 * @param add if true, the entry will be added if it does not
202 * already exist, and the new entry's index returned
203 */
204 public int findIntEntry(int value, boolean add) {
205 Integer key = Numbers.valueOf(value);
206 int index = find(key);
207 if (!add || index > 0)
208 return index;
209 return addEntry(key, new IntEntry(value));
210 }
211
212 /***
213 * Return the constant pool index of the {@link LongEntry} for the given
214 * value, or 0 if it does not exist.
215 *
216 * @param value the value to find
217 * @param add if true, the entry will be added if it does not
218 * already exist, and the new entry's index returned
219 */
220 public int findLongEntry(long value, boolean add) {
221 Long key = Numbers.valueOf(value);
222 int index = find(key);
223 if (!add || index > 0)
224 return index;
225 return addEntry(key, new LongEntry(value));
226 }
227
228 /***
229 * Return the constant pool index of the {@link StringEntry} for the given
230 * string value, or 0 if it does not exist.
231 *
232 * @param value the value to find
233 * @param add if true, the entry will be added if it does not
234 * already exist, and the new entry's index returned
235 */
236 public int findStringEntry(String value, boolean add) {
237 int valueIndex = findUTF8Entry(value, add);
238 if (valueIndex == 0)
239 return 0;
240
241 StringKey key = new StringKey(valueIndex);
242 int index = find(key);
243 if (!add || index > 0)
244 return index;
245 return addEntry(key, new StringEntry(valueIndex));
246 }
247
248 /***
249 * Return the constant pool index of the {@link ClassEntry} for the given
250 * class name, or 0 if it does not exist.
251 *
252 * @param name the class name in internal form
253 * @param add if true, the entry will be added if it does not
254 * already exist, and the new entry's index returned
255 */
256 public int findClassEntry(String name, boolean add) {
257 int nameIndex = findUTF8Entry(name, add);
258 if (nameIndex == 0)
259 return 0;
260
261 ClassKey key = new ClassKey(nameIndex);
262 int index = find(key);
263 if (!add || index > 0)
264 return index;
265 return addEntry(key, new ClassEntry(nameIndex));
266 }
267
268 /***
269 * Return the constant pool index of the {@link NameAndTypeEntry} for the
270 * given name and descriptor, or 0 if it does not exist.
271 *
272 * @param name the name of the entity
273 * @param desc the descriptor of the entity in internal form
274 * @param add if true, the entry will be added if it does not
275 * already exist, and the new entry's index returned
276 */
277 public int findNameAndTypeEntry(String name, String desc, boolean add) {
278 int nameIndex = findUTF8Entry(name, add);
279 if (nameIndex == 0)
280 return 0;
281 int descIndex = findUTF8Entry(desc, add);
282 if (descIndex == 0)
283 return 0;
284
285 NameAndTypeKey key = new NameAndTypeKey(nameIndex, descIndex);
286 int index = find(key);
287 if (!add || index > 0)
288 return index;
289 return addEntry(key, new NameAndTypeEntry(nameIndex, descIndex));
290 }
291
292 /***
293 * Return the constant pool index of the {@link FieldEntry} for the
294 * given name, descriptor, and owner class name.
295 *
296 * @param owner the name of the field's owning class in internal form
297 * @param name the name of the field
298 * @param desc the descriptor of the field in internal form
299 * @param add if true, the entry will be added if it does not
300 * already exist, and the new entry's index returned
301 */
302 public int findFieldEntry(String owner, String name, String desc,
303 boolean add) {
304 return findComplexEntry(owner, name, desc, Entry.FIELD, add);
305 }
306
307 /***
308 * Return the constant pool index of the {@link MethodEntry} for the
309 * given name, descriptor, and owner class name.
310 *
311 * @param owner the name of the method's owning class in internal form
312 * @param name the name of the method
313 * @param desc the descriptor of the method in internal form
314 * @param add if true, the entry will be added if it does not
315 * already exist, and the new entry's index returned
316 */
317 public int findMethodEntry(String owner, String name, String desc,
318 boolean add) {
319 return findComplexEntry(owner, name, desc, Entry.METHOD, add);
320 }
321
322 /***
323 * Return the constant pool index of the {@link InterfaceMethodEntry} for
324 * the given name, descriptor, and owner class name.
325 *
326 * @param owner the name of the method's owning class in internal form
327 * @param name the name of the method
328 * @param desc the descriptor of the method in internal form
329 * @param add if true, the entry will be added if it does not
330 * already exist, and the new entry's index returned
331 */
332 public int findInterfaceMethodEntry(String owner, String name, String desc,
333 boolean add) {
334 return findComplexEntry(owner, name, desc, Entry.INTERFACEMETHOD, add);
335 }
336
337 /***
338 * Return the constant pool index of the {@link ComplexEntry} for the
339 * given name, descriptor, and owner class name.
340 *
341 * @param owner the name of the owning class in internal form
342 * @param name the name of the entity
343 * @param desc the descriptor of the entity in internal form
344 * @param type the type of entry: field, method, interface method
345 * @param add if true, the entry will be added if it does not
346 * already exist, and the new entry's index returned
347 */
348 private int findComplexEntry(String owner, String name, String desc,
349 int type, boolean add) {
350 int classIndex = findClassEntry(owner, add);
351 if (classIndex == 0)
352 return 0;
353 int descIndex = findNameAndTypeEntry(name, desc, add);
354 if (descIndex == 0)
355 return 0;
356
357 Object key = null;
358 switch (type) {
359 case Entry.FIELD:
360 key = new FieldKey(classIndex, descIndex);
361 break;
362 case Entry.METHOD:
363 key = new MethodKey(classIndex, descIndex);
364 break;
365 case Entry.INTERFACEMETHOD:
366 key = new InterfaceMethodKey(classIndex, descIndex);
367 break;
368 }
369 int index = find(key);
370 if (!add || index > 0)
371 return index;
372
373 Entry entry = null;
374 switch (type) {
375 case Entry.FIELD:
376 entry = new FieldEntry(classIndex, descIndex);
377 break;
378 case Entry.METHOD:
379 entry = new MethodEntry(classIndex, descIndex);
380 break;
381 case Entry.INTERFACEMETHOD:
382 entry = new InterfaceMethodEntry(classIndex, descIndex);
383 break;
384 }
385 return addEntry(key, entry);
386 }
387
388 public void acceptVisit(BCVisitor visit) {
389 visit.enterConstantPool(this);
390
391 Entry entry;
392 for (Iterator itr = _entries.iterator(); itr.hasNext();) {
393 entry = (Entry) itr.next();
394 if (entry == null)
395 continue;
396 visit.enterEntry(entry);
397 entry.acceptVisit(visit);
398 visit.exitEntry(entry);
399 }
400 visit.exitConstantPool(this);
401 }
402
403 /***
404 * Fill the constant pool from the given bytecode stream.
405 */
406 public void read(DataInput in) throws IOException {
407 clear();
408
409 int entryCount = in.readUnsignedShort();
410 Entry entry;
411 for (int i = 1; i < entryCount; i++) {
412 entry = Entry.read(in);
413 addEntry(entry);
414 if (entry.isWide())
415 i++;
416 }
417 }
418
419 /***
420 * Write the constant pool to the given bytecode stream.
421 */
422 public void write(DataOutput out) throws IOException {
423 out.writeShort(_entries.size() + 1);
424
425 Entry entry;
426 for (Iterator itr = _entries.iterator(); itr.hasNext();) {
427 entry = (Entry) itr.next();
428 if (entry != null)
429 Entry.write(entry, out);
430 }
431 }
432
433 /***
434 * Called by constant pool entries when they are mutated.
435 */
436 void modifyEntry(Object origKey, Entry entry) {
437 _lookup.remove(origKey);
438 _lookup.put(getKey(entry), entry);
439 }
440
441 /***
442 * Returns the constant pool index of the entry with the given key.
443 */
444 private int find(Object key) {
445 Entry entry = (Entry) _lookup.get(key);
446 if (entry == null)
447 return 0;
448 return entry.getIndex();
449 }
450
451 /***
452 * Return the hash key used for the specified entry.
453 */
454 static Object getKey(Entry entry) {
455 switch (entry.getType()) {
456 case Entry.CLASS:
457 return new ClassKey(((ClassEntry) entry).getNameIndex());
458 case Entry.FIELD:
459 FieldEntry fe = (FieldEntry) entry;
460 return new FieldKey(fe.getClassIndex(), fe.getNameAndTypeIndex());
461 case Entry.METHOD:
462 MethodEntry me = (MethodEntry) entry;
463 return new MethodKey(me.getClassIndex(), me.getNameAndTypeIndex());
464 case Entry.INTERFACEMETHOD:
465 InterfaceMethodEntry ime = (InterfaceMethodEntry) entry;
466 return new InterfaceMethodKey(ime.getClassIndex(),
467 ime.getNameAndTypeIndex());
468 case Entry.STRING:
469 return new StringKey(((StringEntry) entry).getStringIndex());
470 case Entry.INT:
471 case Entry.FLOAT:
472 case Entry.LONG:
473 case Entry.DOUBLE:
474 case Entry.UTF8:
475 return ((ConstantEntry) entry).getConstant();
476 case Entry.NAMEANDTYPE:
477 NameAndTypeEntry nte = (NameAndTypeEntry) entry;
478 return new NameAndTypeKey(nte.getNameIndex(),
479 nte.getDescriptorIndex());
480 default:
481 return null;
482 }
483 }
484
485 /***
486 * Base class key for entries with one ptr to another entry.
487 */
488 private static abstract class PtrKey {
489 private final int _index;
490
491 public PtrKey(int index) {
492 _index = index;
493 }
494
495 public int hashCode() {
496 return _index;
497 }
498
499 public boolean equals(Object other) {
500 if (other == this)
501 return true;
502 if (other.getClass() != getClass())
503 return false;
504 return ((PtrKey) other)._index == _index;
505 }
506 }
507
508 /***
509 * Key for string entries.
510 */
511 private static class StringKey extends PtrKey {
512 public StringKey(int index) {
513 super(index);
514 }
515 }
516
517 /***
518 * Key for class entries.
519 */
520 private static class ClassKey extends PtrKey {
521 public ClassKey(int index) {
522 super(index);
523 }
524 }
525
526 /***
527 * Base class key for entries with two ptr to other entries.
528 */
529 private static abstract class DoublePtrKey {
530 private final int _index1;
531 private final int _index2;
532
533 public DoublePtrKey(int index1, int index2) {
534 _index1 = index1;
535 _index2 = index2;
536 }
537
538 public int hashCode() {
539 return _index1 ^ _index2;
540 }
541
542 public boolean equals(Object other) {
543 if (other == this)
544 return true;
545 if (other.getClass() != getClass())
546 return false;
547 DoublePtrKey key = (DoublePtrKey) other;
548 return key._index1 == _index1 && key._index2 == _index2;
549 }
550 }
551
552 /***
553 * Key for name and type entries.
554 */
555 private static class NameAndTypeKey extends DoublePtrKey {
556 public NameAndTypeKey(int index1, int index2) {
557 super(index1, index2);
558 }
559 }
560
561 /***
562 * Key for field entries.
563 */
564 private static class FieldKey extends DoublePtrKey {
565 public FieldKey(int index1, int index2) {
566 super(index1, index2);
567 }
568 }
569
570 /***
571 * Key for method entries.
572 */
573 private static class MethodKey extends DoublePtrKey {
574 public MethodKey(int index1, int index2) {
575 super(index1, index2);
576 }
577 }
578
579 /***
580 * Key for interface method entries.
581 */
582 private static class InterfaceMethodKey extends DoublePtrKey {
583 public InterfaceMethodKey(int index1, int index2) {
584 super(index1, index2);
585 }
586 }
587 }