View Javadoc

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         // rehash all the entries after the removed one with their new index
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 }