Source code: com/techtrader/modules/tools/bytecode/lowlevel/ConstantPool.java
1 package com.techtrader.modules.tools.bytecode.lowlevel;
2
3
4 import java.util.List;
5 import java.util.LinkedList;
6 import java.util.Iterator;
7 import java.util.Map;
8 import java.util.HashMap;
9 import java.io.IOException;
10 import java.io.DataInput;
11 import java.io.DataOutput;
12
13 import com.techtrader.modules.tools.bytecode.BCClass;
14 import com.techtrader.modules.tools.bytecode.visitor.BCVisitor;
15 import com.techtrader.modules.tools.bytecode.visitor.VisitAcceptor;
16
17
18 /**
19 * Represents a class constant pool, containing entries for all strings,
20 * constants, classes, etc referenced in the class structure and opcodes.
21 * In keeping with the low-level bytecode representation, all pool indexes
22 * are 1-based.
23 * <p>
24 * NOTE: Entries are not meant to be manipulated manually. If you change
25 * entries by hand, make sure to call the {@link #rehash} method of the
26 * ConstantPool so that the entry is hashed correctly for quick lookups and
27 * to avoid duplicates.
28 * <p>
29 * NOTE: LongEntries and DoubleEntries are always followed by a
30 * PlaceHolderEntry in the pool, as they take up 2 pool indeces.
31 * When manually adding a new entry of these types, it is <b>not</b>
32 * necessary to insert the PlaceHolder as well; the system will do this
33 * automatically.
34 *
35 * @author Abe White
36 */
37 public class ConstantPool
38 implements LowLevelConstants, VisitAcceptor
39 {
40 // map constant types to codes
41 private static final Map _consts = new HashMap ();
42 static
43 {
44 _consts.put (Integer.class, new Integer (ENTRY_INT));
45 _consts.put (Long.class, new Integer (ENTRY_LONG));
46 _consts.put (Double.class, new Integer (ENTRY_DOUBLE));
47 _consts.put (Float.class, new Integer (ENTRY_FLOAT));
48 _consts.put (String.class, new Integer (ENTRY_STRING));
49 }
50
51 private List _entries = new LinkedList ();
52 private Map _lookup = new HashMap ();
53 private BCClass _owner = null;
54
55
56 /**
57 * Create an Entry based on its one-byte code: one of the constants in
58 * the LowLevelConstants class.
59 */
60 public static Entry createEntry (int type)
61 {
62 switch (type)
63 {
64 case ENTRY_PLACEHOLDER:
65 return new PlaceHolderEntry ();
66 case ENTRY_CLASS:
67 return new ClassEntry ();
68 case ENTRY_FIELD:
69 return new FieldEntry ();
70 case ENTRY_METHOD:
71 return new MethodEntry ();
72 case ENTRY_INTMETHOD:
73 return new InterfaceMethodEntry ();
74 case ENTRY_STRING:
75 return new StringEntry ();
76 case ENTRY_INT:
77 return new IntEntry ();
78 case ENTRY_FLOAT:
79 return new FloatEntry ();
80 case ENTRY_LONG:
81 return new LongEntry ();
82 case ENTRY_DOUBLE:
83 return new DoubleEntry ();
84 case ENTRY_NAME_AND_TYPE:
85 return new NameAndTypeEntry ();
86 case ENTRY_UTF8:
87 return new UTF8Entry ();
88 default:
89 return null;
90 }
91 }
92
93
94 public ConstantPool (BCClass owner)
95 {
96 _owner = owner;
97 }
98
99
100 /**
101 * Get the entries in the pool.
102 */
103 public Entry[] getEntries ()
104 {
105 return (Entry[]) _entries.toArray (new Entry[_entries.size ()]);
106 }
107
108
109 /**
110 * Retrieve the entry at the specified 1-based index.
111 *
112 * @return the specified entry, or null if invalid index
113 */
114 public Entry getEntry (int index)
115 {
116 if (index < 1 || index > _entries.size ())
117 return null;
118
119 return (Entry) _entries.get (index - 1);
120 }
121
122
123 /**
124 * Set the entry at the given 1-based index.
125 *
126 * @return the entry that was replaced
127 */
128 public Entry setEntry (int index, Entry entry)
129 {
130 if (entry == null)
131 throw new NullPointerException ();
132
133 Entry old = (Entry) _entries.set (index - 1, entry);
134 removeHash (old);
135
136 // remove placeholders
137 if (old != null && (old.getType () == ENTRY_LONG
138 || old.getType () == ENTRY_DOUBLE))
139 _entries.remove (index);
140
141 // add placeholder
142 if (entry.getType () == ENTRY_LONG || entry.getType () == ENTRY_DOUBLE)
143 _entries.add (index, new PlaceHolderEntry ());
144
145 hash (entry, index);
146
147 return old;
148 }
149
150
151 /**
152 * Set the entry at the given 1-based index.
153 */
154 public void addEntry (int index, Entry entry)
155 {
156 if (entry == null)
157 throw new NullPointerException ();
158
159 _entries.add (index - 1, entry);
160 hash (entry, index);
161
162 // add placeholder
163 if (entry.getType () == ENTRY_LONG || entry.getType () == ENTRY_DOUBLE)
164 _entries.add (index, new PlaceHolderEntry ());
165 }
166
167
168 /**
169 * Add an entry to the pool.
170 *
171 * @return the index at which the entry was added
172 */
173 public int addEntry (Entry entry)
174 {
175 if (entry == null)
176 throw new NullPointerException ();
177
178 _entries.add (entry);
179 int index = _entries.size ();
180 hash (entry, index);
181
182 // add placeholder
183 if (entry.getType () == ENTRY_LONG || entry.getType () == ENTRY_DOUBLE)
184 _entries.add (new PlaceHolderEntry ());
185
186 return index;
187 }
188
189
190 /**
191 * Remove the entry at the given index.
192 *
193 * @return the removed entry, or null if none
194 */
195 public Entry removeEntry (int index)
196 {
197 if (index < 1 || index > _entries.size ())
198 return null;
199
200 Entry old = (Entry) _entries.remove (index - 1);
201 removeHash (old);
202
203 // remove placeholders
204 if (old != null && (old.getType () == ENTRY_LONG
205 || old.getType () == ENTRY_DOUBLE))
206 _entries.remove (index - 1);
207
208 return old;
209 }
210
211
212 /**
213 * Remove the given entry from the pool.
214 *
215 * @return false if the entry is not in the pool, true otherwise
216 */
217 public boolean removeEntry (Entry entry)
218 {
219 if (entry == null)
220 return false;
221
222 int index = _entries.indexOf (entry);
223 if (index != -1)
224 {
225 _entries.remove (index);
226 removeHash (entry);
227
228 // remove placeholders
229 if (entry.getType () == ENTRY_LONG
230 || entry.getType () == ENTRY_DOUBLE)
231 _entries.remove (index);
232
233 return true;
234 }
235 return false;
236 }
237
238
239 /**
240 * Return the number of entries in the pool, including placeholder
241 * entries.
242 */
243 public int size ()
244 {
245 return _entries.size ();
246 }
247
248
249 /**
250 * Get the constant pool index of the entry for the given UTF value.
251 *
252 * @return the index of the matching entry, or 0 if no match
253 */
254 public int getUTFIndex (String name)
255 {
256 if (name == null)
257 name = "";
258
259 return find (ENTRY_UTF8 + "|" + name);
260 }
261
262
263 /**
264 * Get the value of the entry at the given index.
265 *
266 * @return the value of the given entry, or empty string if
267 * the entry does not exist
268 */
269 public String getUTF (int index)
270 {
271 UTF8Entry entry = (UTF8Entry) getEntry (index);
272 if (entry == null)
273 return "";
274
275 return entry.getValue ();
276 }
277
278
279 /**
280 * Set the entry at the given index; if the given index is <= 0,
281 * a search will be performed for an entry with the given value, and, if
282 * it fails, a new entry will be added to the pool.
283 *
284 * @return the index of the entry with the given value, whether an
285 * existing one was found/modified or a new one was added
286 */
287 public int setUTF (int index, String name)
288 {
289 if (name == null)
290 name = "";
291
292 if (index <= 0)
293 {
294 index = getUTFIndex (name);
295 if (index > 0)
296 return index;
297 }
298
299 UTF8Entry entry = (UTF8Entry) getEntry (index);
300 if (entry == null)
301 {
302 entry = new UTF8Entry ();
303 entry.setValue (name);
304 return addEntry (entry);
305 }
306
307 entry.setValue (name);
308 rehash (entry, index);
309
310 return index;
311 }
312
313
314 /**
315 * Get the constant pool index of the entry for the given class name.
316 *
317 * @return the index of the matching entry, or 0 if no match
318 */
319 public int getClassIndex (String name)
320 {
321 int nameIdx = getUTFIndex (name);
322 if (nameIdx == 0)
323 return 0;
324
325 return find (ENTRY_CLASS + "|" + nameIdx);
326 }
327
328
329 /**
330 * Get the value of the entry at the given index.
331 *
332 * @return the value of the given entry, or empty string if
333 * the entry does not exist
334 */
335 public String getClassName (int index)
336 {
337 ClassEntry entry = (ClassEntry) getEntry (index);
338 if (entry == null)
339 return "";
340
341 return getUTF (entry.getNameIndex ());
342 }
343
344
345 /**
346 * Set the entry at the given index; if the given index is <= 0,
347 * a search will be performed for an entry with the given value, and, if
348 * it fails, a new entry will be added to the pool.
349 *
350 * @return the index of the entry with the given value, whether an
351 * existing one was found/modified or a new one was added
352 */
353 public int setClassName (int index, String name)
354 {
355 if (index <= 0)
356 {
357 index = getClassIndex (name);
358 if (index > 0)
359 return index;
360 }
361
362 ClassEntry entry = (ClassEntry) getEntry (index);
363 if (entry == null)
364 {
365 entry = new ClassEntry ();
366 entry.setNameIndex (setUTF (0, name));
367 return addEntry (entry);
368 }
369
370 entry.setNameIndex (setUTF (0, name));
371 rehash (entry, index);
372
373 return index;
374 }
375
376
377 /**
378 * Get the constant pool index of the entry for the given name+type.
379 *
380 * @return the index of the matching entry, or 0 if no match
381 */
382 public int getNameAndTypeIndex (String name, String desc)
383 {
384 int nameIdx = getUTFIndex (name);
385 if (nameIdx == 0)
386 return 0;
387
388 int descIdx = getUTFIndex (desc);
389 if (descIdx == 0)
390 return 0;
391
392 return find (ENTRY_NAME_AND_TYPE + "|" + nameIdx + "|" + descIdx);
393 }
394
395
396 /**
397 * Set the entry at the given index; if the given index is <= 0,
398 * a search will be performed for an entry with the given value, and, if
399 * it fails, a new entry will be added to the pool.
400 *
401 * @return the index of the entry with the given value, whether an
402 * existing one was found/modified or a new one was added
403 */
404 public int setNameAndType (int index, String name, String desc)
405 {
406 if (index <= 0)
407 {
408 index = getNameAndTypeIndex (name, desc);
409 if (index > 0)
410 return index;
411 }
412
413 NameAndTypeEntry entry = (NameAndTypeEntry) getEntry (index);
414 if (entry == null)
415 {
416 entry = new NameAndTypeEntry ();
417 entry.setNameIndex (setUTF (0, name));
418 entry.setDescriptorIndex (setUTF (0, desc));
419 return addEntry (entry);
420 }
421
422 entry.setNameIndex (setUTF (0, name));
423 entry.setDescriptorIndex (setUTF (0, desc));
424 rehash (entry, index);
425
426 return index;
427 }
428
429
430 /**
431 * Get the constant pool index of the entry for the given complex entry.
432 *
433 * @return the index of the matching entry, or 0 if no match
434 */
435 public int getComplexIndex (String name, String desc, String owner,
436 Class type)
437 {
438 int classIdx = getClassIndex (owner);
439 if (classIdx == 0)
440 return 0;
441
442 int descIdx = getNameAndTypeIndex (name, desc);
443 if (descIdx == 0)
444 return 0;
445
446 int code;
447 if (type.equals (FieldEntry.class))
448 code = ENTRY_FIELD;
449 else if (type.equals (MethodEntry.class))
450 code = ENTRY_METHOD;
451 else
452 code = ENTRY_INTMETHOD;
453
454 return find (code + "|" + classIdx + "|" + descIdx);
455 }
456
457
458 /**
459 * Set the entry at the given index; if the given index is <= 0,
460 * a search will be performed for an entry with the given value, and, if
461 * it fails, a new entry will be added to the pool.
462 *
463 * @return the index of the entry with the given value, whether an
464 * existing one was found/modified or a new one was added
465 */
466 public int setComplex (int index, String name, String desc,
467 String owner, Class entryType)
468 {
469 if (index <= 0)
470 {
471 index = getComplexIndex (name, desc, owner, entryType);
472 if (index > 0)
473 return index;
474 }
475
476 ComplexEntry entry = (ComplexEntry) getEntry (index);
477 if (entry == null)
478 {
479 try
480 {
481 entry = (ComplexEntry) entryType.newInstance ();
482 }
483 catch (Throwable t)
484 {
485 throw new RuntimeException (t.getMessage ());
486 }
487
488 entry.setClassIndex (setClassName (0, owner));
489 entry.setNameAndTypeIndex (setNameAndType (0, name, desc));
490 return addEntry (entry);
491 }
492
493 entry.setClassIndex (setClassName (0, owner));
494 entry.setNameAndTypeIndex (setNameAndType (0, name, desc));
495 rehash (entry, index);
496
497 return index;
498 }
499
500
501 /**
502 * Get the value of the entry at the given index.
503 *
504 * @return the value of the given entry, or empty string if
505 * the entry does not exist
506 */
507 public String getComplexName (int index)
508 {
509 ComplexEntry complex = (ComplexEntry) getEntry (index);
510 if (complex == null)
511 return "";
512
513 NameAndTypeEntry nt = (NameAndTypeEntry) getEntry
514 (complex.getNameAndTypeIndex ());
515 if (nt == null)
516 return "";
517
518 return getUTF (nt.getNameIndex ());
519 }
520
521
522 /**
523 * Get the value of the entry at the given index.
524 *
525 * @return the value of the given entry, or empty string if
526 * the entry does not exist
527 */
528 public String getComplexTypeName (int index)
529 {
530 ComplexEntry complex = (ComplexEntry) getEntry (index);
531 if (complex == null)
532 return "";
533
534 NameAndTypeEntry nt = (NameAndTypeEntry) getEntry
535 (complex.getNameAndTypeIndex ());
536 if (nt == null)
537 return "";
538
539 return getUTF (nt.getDescriptorIndex ());
540 }
541
542
543 /**
544 * Get the value of the entry at the given index.
545 *
546 * @return the value of the given entry, or empty string if
547 * the entry does not exist
548 */
549 public String getComplexOwnerTypeName (int index)
550 {
551 ComplexEntry complex = (ComplexEntry) getEntry (index);
552 if (complex == null)
553 return "";
554
555 return getClassName (complex.getClassIndex ());
556 }
557
558
559 /**
560 * Get the constant pool index of the entry for the given constant value.
561 *
562 * @return the index of the matching entry, or 0 if no match
563 */
564 public int getConstantIndex (Object value)
565 {
566 if (value == null)
567 return 0;
568
569 // get the constant code for the given type
570 int code = ((Integer) _consts.get (value.getClass ())).intValue ();
571 if (code == ENTRY_STRING)
572 return find (code + "|" + getUTFIndex (value.toString ()));
573
574 return find (code + "|" + value);
575 }
576
577
578 /**
579 * Get the value of the entry at the given index.
580 *
581 * @return the value of the given entry, or null if the entry
582 * does not exist
583 */
584 public Object getConstant (int index)
585 {
586 ConstantEntry entry = (ConstantEntry) getEntry (index);
587 if (entry == null)
588 return null;
589
590 if (entry instanceof StringEntry)
591 return getUTF (((StringEntry) entry).getStringIndex ());
592
593 return ((ConstantEntry) entry).getConstantValue ();
594 }
595
596
597 /**
598 * Set the entry at the given index; if the given index is <= 0,
599 * a search will be performed for an entry with the given value, and, if
600 * it fails, a new entry will be added to the pool.
601 *
602 * @return the index of the entry with the given value, whether an
603 * existing one was found/modified or a new one was added
604 */
605 public int setConstant (int index, Object value)
606 {
607 if (index <= 0)
608 {
609 index = getConstantIndex (value);
610 if (index > 0)
611 return index;
612 }
613
614 ConstantEntry entry = (ConstantEntry) getEntry (index);
615 if (entry == null)
616 {
617 int code = ((Integer) _consts.get (value.getClass ())).intValue ();
618 entry = (ConstantEntry) createEntry (code);
619 if (entry instanceof StringEntry)
620 ((StringEntry) entry).setStringIndex
621 (setUTF (0, value.toString ()));
622 else
623 entry.setConstantValue (value);
624 return addEntry (entry);
625 }
626
627 if (entry instanceof StringEntry)
628 ((StringEntry) entry).setStringIndex (setUTF(0, value.toString ()));
629 else
630 entry.setConstantValue (value);
631 rehash (entry, index);
632
633 return index;
634 }
635
636
637 /**
638 * Rehash the given entry after modification; this allows for quick
639 * lookups and guarantees that entries won't be repeated in the pool.
640 */
641 public void rehash (Entry entry, int poolIndex)
642 {
643 if (entry != null)
644 {
645 // remove the modified entry from the map
646 for (Iterator i = _lookup.values ().iterator (); i.hasNext ();)
647 if (((HashedEntry) i.next ()).entry.equals (entry))
648 i.remove ();
649
650 // rehash with its new values
651 hash (entry, poolIndex);
652 }
653 }
654
655
656 public void readData (DataInput in)
657 throws IOException
658 {
659 _entries.clear ();
660 _lookup.clear ();
661
662 int entryCount = in.readUnsignedShort ();
663 Entry entry;
664 for (int i = 1; i < entryCount; i++)
665 {
666 entry = createEntry (in.readUnsignedByte ());
667 entry.readData (in);
668 addEntry (entry);
669
670 // bytecode 'feature': long and double entries
671 // are counted twice in the entryCount
672 if (entry.getType () == ENTRY_LONG
673 || entry.getType () == ENTRY_DOUBLE)
674 i++;
675 }
676 }
677
678
679 public void writeData (DataOutput out)
680 throws IOException
681 {
682 out.writeShort (_entries.size () + 1);
683 Entry entry;
684 for (Iterator i = _entries.iterator (); i.hasNext ();)
685 {
686 entry = (Entry) i.next ();
687
688 // compensate for weird long/double entry behavior
689 if (entry.getType () == ENTRY_PLACEHOLDER)
690 continue;
691
692 out.writeByte (entry.getType ());
693 entry.writeData (out);
694 }
695 }
696
697
698 private int find (String key)
699 {
700 HashedEntry he = (HashedEntry) _lookup.get (key);
701 if (he == null)
702 return 0;
703
704 return he.index;
705 }
706
707
708 private void hash (Entry entry, int index)
709 {
710 if (entry != null)
711 _lookup.put (entry.getKey (), new HashedEntry (entry, index));
712 }
713
714
715 private void removeHash (Entry entry)
716 {
717 if (entry != null)
718 _lookup.remove (entry.getKey ());
719 }
720
721
722 public void acceptVisit (BCVisitor visit)
723 {
724 visit.enterConstantPool (this);
725
726 Entry next;
727 for (Iterator i = _entries.iterator (); i.hasNext ();)
728 {
729 next = (Entry) i.next ();
730
731 visit.enterEntry (next);
732 next.acceptVisit (visit);
733 visit.exitEntry (next);
734 }
735
736 visit.exitConstantPool (this);
737 }
738
739
740 private static class HashedEntry
741 {
742 public Entry entry = null;
743 public int index = 0;
744
745
746 public HashedEntry (Entry entry, int index)
747 {
748 this.entry = entry;
749 this.index = index;
750 }
751 }
752 }
753