001    package serp.bytecode.lowlevel;
002    
003    import java.io.*;
004    import java.util.*;
005    
006    import serp.bytecode.visitor.*;
007    import serp.util.*;
008    
009    /**
010     * A bytecode constant pool, containing entries for all strings,
011     * constants, classes, etc referenced in the class structure and method
012     * opcodes. In keeping with the low-level bytecode representation, all pool
013     * indexes are 1-based and {@link LongEntry}s and {@link DoubleEntry}s each
014     * occupy two indexes in the pool.
015     *
016     * @author Abe White
017     */
018    public class ConstantPool implements VisitAcceptor {
019        private List _entries = new ArrayList(50);
020        private Map _lookup = new HashMap(50);
021    
022        /**
023         * Default constructor.
024         */
025        public ConstantPool() {
026        }
027    
028        /**
029         * Return all the entries in the pool.
030         */
031        public Entry[] getEntries() {
032            List entries = new ArrayList(_entries.size());
033            Entry entry;
034            for (Iterator itr = _entries.iterator(); itr.hasNext();) {
035                entry = (Entry) itr.next();
036                if (entry != null)
037                    entries.add(entry);
038            }
039            return (Entry[]) entries.toArray(new Entry[entries.size()]);
040        }
041    
042        /**
043         * Retrieve the entry at the specified 1-based index.
044         *
045         * @throws IndexOutOfBoundsException if index is invalid,
046         * including the case that it points to the second slot of a
047         * long or double entry
048         */
049        public Entry getEntry(int index) {
050            Entry entry = (Entry) _entries.get(index - 1);
051            if (entry == null)
052                throw new IndexOutOfBoundsException("index = " + index);
053            return entry;
054        }
055    
056        /**
057         * Return the index of the given entry, or 0 if it is not in the pool.
058         */
059        public int indexOf(Entry entry) {
060            if (entry == null || entry.getPool() != this)
061                return 0;
062            return entry.getIndex();
063        }
064    
065        /**
066         * Add an entry to the pool.
067         *
068         * @return the index at which the entry was added
069         */
070        public int addEntry(Entry entry) {
071            if (entry.getPool() != this)
072                addEntry(getKey(entry), entry);
073            return entry.getIndex();
074        }
075    
076        /**
077         * Add an entry to the pool using the given key.
078         */
079        private int addEntry(Object key, Entry entry) {
080            entry.setPool(this);
081            _entries.add(entry);
082            entry.setIndex(_entries.size());
083            _lookup.put(key, entry);
084            if (entry.isWide())
085                _entries.add(null);
086            return entry.getIndex();
087        }
088    
089        /**
090         * Remove the given entry from the pool.
091         *
092         * @return false if the entry is not in the pool, true otherwise
093         */
094        public boolean removeEntry(Entry entry) {
095            if (entry == null || entry.getPool() != this)
096                return false;
097    
098            int index = entry.getIndex() - 1;
099            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    }