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        public int findInvokeDynamicEntry(int bootstrapMethodIndex, String name, String desc, boolean add) {       
338            int descIndex = findNameAndTypeEntry(name, desc, add);
339            if (descIndex == 0)
340                return 0;
341            
342            Object key = new InvokeDynamicKey(bootstrapMethodIndex, descIndex);
343            int index = find(key);
344            if (!add || index > 0)
345                return index;
346            
347            Entry entry = new InvokeDynamicEntry(bootstrapMethodIndex, descIndex);
348            return addEntry(key, entry);
349        }
350        
351        /**
352         * Return the constant pool index of the {@link ComplexEntry} for the
353         * given name, descriptor, and owner class name.
354         *
355         * @param owner the name of the owning class in internal form
356         * @param name the name of the entity
357         * @param desc the descriptor of the entity in internal form
358         * @param type the type of entry: field, method, interface method
359         * @param add if true, the entry will be added if it does not
360         * already exist, and the new entry's index returned
361         */
362        private int findComplexEntry(String owner, String name, String desc,
363            int type, boolean add) {
364            int classIndex = findClassEntry(owner, add);
365            if (classIndex == 0)
366                return 0;
367            int descIndex = findNameAndTypeEntry(name, desc, add);
368            if (descIndex == 0)
369                return 0;
370    
371            Object key = null;
372            switch (type) {
373            case Entry.FIELD:
374                key = new FieldKey(classIndex, descIndex);
375                break;
376            case Entry.METHOD:
377                key = new MethodKey(classIndex, descIndex);
378                break;
379            case Entry.INTERFACEMETHOD:
380                key = new InterfaceMethodKey(classIndex, descIndex);
381                break;
382            }
383            int index = find(key);
384            if (!add || index > 0)
385                return index;
386    
387            Entry entry = null;
388            switch (type) {
389            case Entry.FIELD:
390                entry = new FieldEntry(classIndex, descIndex);
391                break;
392            case Entry.METHOD:
393                entry = new MethodEntry(classIndex, descIndex);
394                break;
395            case Entry.INTERFACEMETHOD:
396                entry = new InterfaceMethodEntry(classIndex, descIndex);
397                break;
398            }
399            return addEntry(key, entry);
400        }
401    
402        public void acceptVisit(BCVisitor visit) {
403            visit.enterConstantPool(this);
404    
405            Entry entry;
406            for (Iterator itr = _entries.iterator(); itr.hasNext();) {
407                entry = (Entry) itr.next();
408                if (entry == null)
409                    continue;
410                visit.enterEntry(entry);
411                entry.acceptVisit(visit);
412                visit.exitEntry(entry);
413            }
414            visit.exitConstantPool(this);
415        }
416    
417        /**
418         * Fill the constant pool from the given bytecode stream.
419         */
420        public void read(DataInput in) throws IOException {
421            clear();
422    
423            int entryCount = in.readUnsignedShort();
424            Entry entry;
425            for (int i = 1; i < entryCount; i++) {
426                entry = Entry.read(in);
427                addEntry(entry);
428                if (entry.isWide())
429                    i++;
430            }
431        }
432    
433        /**
434         * Write the constant pool to the given bytecode stream.
435         */
436        public void write(DataOutput out) throws IOException {
437            out.writeShort(_entries.size() + 1);
438    
439            Entry entry;
440            for (Iterator itr = _entries.iterator(); itr.hasNext();) {
441                entry = (Entry) itr.next();
442                if (entry != null)
443                    Entry.write(entry, out);
444            }
445        }
446    
447        /**
448         * Called by constant pool entries when they are mutated.
449         */
450        void modifyEntry(Object origKey, Entry entry) {
451            _lookup.remove(origKey);
452            _lookup.put(getKey(entry), entry);
453        }
454    
455        /**
456         * Returns the constant pool index of the entry with the given key.
457         */
458        private int find(Object key) {
459            Entry entry = (Entry) _lookup.get(key);
460            if (entry == null)
461                return 0;
462            return entry.getIndex();
463        }
464    
465        /**
466         * Return the hash key used for the specified entry.
467         */
468        static Object getKey(Entry entry) {
469            switch (entry.getType()) {
470            case Entry.CLASS:
471                return new ClassKey(((ClassEntry) entry).getNameIndex());
472            case Entry.FIELD:
473                FieldEntry fe = (FieldEntry) entry;
474                return new FieldKey(fe.getClassIndex(), fe.getNameAndTypeIndex());
475            case Entry.METHOD:
476                MethodEntry me = (MethodEntry) entry;
477                return new MethodKey(me.getClassIndex(), me.getNameAndTypeIndex());
478            case Entry.INTERFACEMETHOD:
479                InterfaceMethodEntry ime = (InterfaceMethodEntry) entry;
480                return new InterfaceMethodKey(ime.getClassIndex(),
481                    ime.getNameAndTypeIndex());
482            case Entry.INVOKEDYNAMIC:
483                InvokeDynamicEntry ide = (InvokeDynamicEntry) entry;
484                return new InvokeDynamicKey(ide.getBootstrapMethodAttrIndex(), ide.getNameAndTypeIndex());
485            case Entry.STRING:
486                return new StringKey(((StringEntry) entry).getStringIndex());
487            case Entry.INT:
488            case Entry.FLOAT:
489            case Entry.LONG:
490            case Entry.DOUBLE:
491            case Entry.UTF8:
492                return ((ConstantEntry) entry).getConstant();
493            case Entry.NAMEANDTYPE:
494                NameAndTypeEntry nte = (NameAndTypeEntry) entry;
495                return new NameAndTypeKey(nte.getNameIndex(),
496                    nte.getDescriptorIndex());
497            default:
498                return null;
499            }
500        }
501    
502        /**
503         * Base class key for entries with one ptr to another entry.
504         */
505        private static abstract class PtrKey {
506            private final int _index;
507    
508            public PtrKey(int index) {
509                _index = index;
510            }
511    
512            public int hashCode() {
513                return _index;
514            }
515    
516            public boolean equals(Object other) {
517                if (other == this)
518                    return true;
519                if (other.getClass() != getClass())
520                    return false;
521                return ((PtrKey) other)._index == _index;
522            }
523        }
524    
525        /**
526         * Key for string entries.
527         */
528        private static class StringKey extends PtrKey {
529            public StringKey(int index) {
530                super(index);
531            }
532        }
533    
534        /**
535         * Key for class entries.
536         */
537        private static class ClassKey extends PtrKey {
538            public ClassKey(int index) {
539                super(index);
540            }
541        }
542    
543        /**
544         * Base class key for entries with two ptr to other entries.
545         */
546        private static abstract class DoublePtrKey {
547            private final int _index1;
548            private final int _index2;
549    
550            public DoublePtrKey(int index1, int index2) {
551                _index1 = index1;
552                _index2 = index2;
553            }
554    
555            public int hashCode() {
556                return _index1 ^ _index2;
557            }
558    
559            public boolean equals(Object other) {
560                if (other == this)
561                    return true;
562                if (other.getClass() != getClass())
563                    return false;
564                DoublePtrKey key = (DoublePtrKey) other;
565                return key._index1 == _index1 && key._index2 == _index2;
566            }
567        }
568    
569        /**
570         * Key for name and type entries.
571         */
572        private static class NameAndTypeKey extends DoublePtrKey {
573            public NameAndTypeKey(int index1, int index2) {
574                super(index1, index2);
575            }
576        }
577    
578        /**
579         * Key for field entries.
580         */
581        private static class FieldKey extends DoublePtrKey {
582            public FieldKey(int index1, int index2) {
583                super(index1, index2);
584            }
585        }
586    
587        /**
588         * Key for method entries.
589         */
590        private static class MethodKey extends DoublePtrKey {
591            public MethodKey(int index1, int index2) {
592                super(index1, index2);
593            }
594        }
595    
596        /**
597         * Key for interface method entries.
598         */
599        private static class InterfaceMethodKey extends DoublePtrKey {
600            public InterfaceMethodKey(int index1, int index2) {
601                super(index1, index2);
602            }
603        }
604        
605        /**
606         * Key for method entries.
607         */
608        private static class InvokeDynamicKey extends DoublePtrKey {
609            public InvokeDynamicKey(int index1, int index2) {
610                super(index1, index2);
611            }
612        }
613    }