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    /**
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}