001package serp.bytecode.lowlevel;
002
003import java.io.*;
004import java.util.*;
005
006import serp.bytecode.visitor.*;
007import 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 */
018public 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}