org.aspectj.apache.bcel.classfile.ConstantPool.java Source code

Java tutorial

Introduction

Here is the source code for org.aspectj.apache.bcel.classfile.ConstantPool.java

Source

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *    "Apache BCEL" must not be used to endorse or promote products
 *    derived from this software without prior written permission. For
 *    written permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    "Apache BCEL", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
package org.aspectj.apache.bcel.classfile;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.generic.ArrayType;
import org.aspectj.apache.bcel.generic.ObjectType;

/**
 * This class represents the constant pool, i.e., a table of constants, of a parsed classfile. It may contain null references, due
 * to the JVM specification that skips an entry after an 8-byte constant (double, long) entry.
 */
public class ConstantPool implements Node {
    private Constant[] pool;
    private int poolSize; // number of entries in the pool (could be < pool.length as the array is resized in 'chunks')

    private Map<String, Integer> utf8Cache = new HashMap<String, Integer>();
    private Map<String, Integer> methodCache = new HashMap<String, Integer>();
    private Map<String, Integer> fieldCache = new HashMap<String, Integer>();

    public int getSize() {
        return poolSize;
    }

    public ConstantPool() {
        pool = new Constant[10];
        poolSize = 0;
    }

    public ConstantPool(Constant[] constants) {
        pool = constants;
        poolSize = (constants == null ? 0 : constants.length);
    }

    ConstantPool(DataInputStream file) throws IOException {
        byte tag;
        poolSize = file.readUnsignedShort();
        pool = new Constant[poolSize];
        // pool[0] is unused by the compiler and may be used freely by the implementation
        for (int i = 1; i < poolSize; i++) {
            pool[i] = Constant.readConstant(file);
            tag = pool[i].getTag();
            if ((tag == Constants.CONSTANT_Double) || (tag == Constants.CONSTANT_Long)) {
                i++;
            }
        }
    }

    public Constant getConstant(int index, byte tag) {
        Constant c = getConstant(index);
        // if (c == null) throw new ClassFormatException("Constant pool at index " + index + " is null.");
        if (c.tag == tag)
            return c;
        throw new ClassFormatException(
                "Expected class '" + Constants.CONSTANT_NAMES[tag] + "' at index " + index + " and found " + c);
    }

    public Constant getConstant(int index) {
        try {
            return pool[index];
        } catch (ArrayIndexOutOfBoundsException aioobe) {
            throw new ClassFormatException(
                    "Index " + index + " into constant pool (size:" + poolSize + ") is invalid");
        }
    }

    /**
     * @return deep copy of this constant pool
     */
    public ConstantPool copy() {
        Constant[] newConstants = new Constant[poolSize]; // use the correct size
        for (int i = 1; i < poolSize; i++) {
            if (pool[i] != null) {
                newConstants[i] = pool[i].copy();
            }
        }
        return new ConstantPool(newConstants);
    }

    /**
     * Get string from constant pool and bypass the indirection of `ConstantClass' and `ConstantString' objects. I.e. these classes
     * have an index field that points to another entry of the constant pool of type `ConstantUtf8' which contains the real data.
     * 
     * @param index Index in constant pool
     * @param tag Tag of expected constant, either ConstantClass or ConstantString
     * @return Contents of string reference
     * @see ConstantClass
     * @see ConstantString
     * @throws ClassFormatException
     */
    public String getConstantString(int index, byte tag) throws ClassFormatException {
        Constant c = getConstant(index, tag);
        int i;
        /*
         * This switch() is not that elegant, since the two classes have the same contents, they just differ in the name of the
         * index field variable. But we want to stick to the JVM naming conventions closely though we could have solved these more
         * elegantly by using the same variable name or by subclassing.
         */
        // OPTIMIZE remove the difference - use the an interface and same index methods for string ref id
        switch (tag) {
        case Constants.CONSTANT_Class:
            i = ((ConstantClass) c).getNameIndex();
            break;
        case Constants.CONSTANT_String:
            i = ((ConstantString) c).getStringIndex();
            break;
        default:
            throw new RuntimeException("getConstantString called with illegal tag " + tag);
        }
        // Finally get the string from the constant pool
        c = getConstant(i, Constants.CONSTANT_Utf8);
        return ((ConstantUtf8) c).getValue();
    }

    /**
     * Resolve constant to a string representation.
     */
    public String constantToString(Constant c) {
        String str;
        int i;

        switch (c.tag) {
        case Constants.CONSTANT_Class:
            i = ((ConstantClass) c).getNameIndex();
            c = getConstant(i, Constants.CONSTANT_Utf8);
            str = Utility.compactClassName(((ConstantUtf8) c).getValue(), false);
            break;

        case Constants.CONSTANT_String:
            i = ((ConstantString) c).getStringIndex();
            c = getConstant(i, Constants.CONSTANT_Utf8);
            str = "\"" + escape(((ConstantUtf8) c).getValue()) + "\"";
            break;

        case Constants.CONSTANT_Utf8:
        case Constants.CONSTANT_Double:
        case Constants.CONSTANT_Float:
        case Constants.CONSTANT_Long:
        case Constants.CONSTANT_Integer:
            str = ((SimpleConstant) c).getStringValue();
            break;

        case Constants.CONSTANT_NameAndType:
            str = (constantToString(((ConstantNameAndType) c).getNameIndex(), Constants.CONSTANT_Utf8) + " "
                    + constantToString(((ConstantNameAndType) c).getSignatureIndex(), Constants.CONSTANT_Utf8));
            break;

        case Constants.CONSTANT_InterfaceMethodref:
        case Constants.CONSTANT_Methodref:
        case Constants.CONSTANT_Fieldref:
            str = (constantToString(((ConstantCP) c).getClassIndex(), Constants.CONSTANT_Class) + "."
                    + constantToString(((ConstantCP) c).getNameAndTypeIndex(), Constants.CONSTANT_NameAndType));
            break;

        case Constants.CONSTANT_InvokeDynamic:
            ConstantInvokeDynamic cID = ((ConstantInvokeDynamic) c);
            return "#" + cID.getBootstrapMethodAttrIndex() + "."
                    + constantToString(cID.getNameAndTypeIndex(), Constants.CONSTANT_NameAndType);

        case Constants.CONSTANT_MethodHandle:
            ConstantMethodHandle cMH = ((ConstantMethodHandle) c);
            return cMH.getReferenceKind() + ":"
                    + constantToString(cMH.getReferenceIndex(), Constants.CONSTANT_Methodref);

        case Constants.CONSTANT_MethodType:
            ConstantMethodType cMT = (ConstantMethodType) c;
            return constantToString(cMT.getDescriptorIndex(), Constants.CONSTANT_Utf8);

        case Constants.CONSTANT_Module:
            ConstantModule cM = (ConstantModule) c;
            return "Module:" + constantToString(cM.getNameIndex(), Constants.CONSTANT_Utf8);

        case Constants.CONSTANT_Package:
            ConstantPackage cP = (ConstantPackage) c;
            return "Package:" + constantToString(cP.getNameIndex(), Constants.CONSTANT_Utf8);

        default: // Never reached
            throw new RuntimeException("Unknown constant type " + c.tag);
        }

        return str;
    }

    private static final String escape(String str) {
        int len = str.length();
        StringBuffer buf = new StringBuffer(len + 5);
        char[] ch = str.toCharArray();

        for (int i = 0; i < len; i++) {
            switch (ch[i]) {
            case '\n':
                buf.append("\\n");
                break;
            case '\r':
                buf.append("\\r");
                break;
            case '\t':
                buf.append("\\t");
                break;
            case '\b':
                buf.append("\\b");
                break;
            case '"':
                buf.append("\\\"");
                break;
            default:
                buf.append(ch[i]);
            }
        }

        return buf.toString();
    }

    public String constantToString(int index, byte tag) {
        Constant c = getConstant(index, tag);
        return constantToString(c);
    }

    public String constantToString(int index) {
        return constantToString(getConstant(index));
    }

    @Override
    public void accept(ClassVisitor v) {
        v.visitConstantPool(this);
    }

    public Constant[] getConstantPool() {
        return pool;
    } // TEMPORARY, DONT LIKE PASSING THIS DATA OUT!

    public void dump(DataOutputStream file) throws IOException {
        file.writeShort(poolSize);
        for (int i = 1; i < poolSize; i++)
            if (pool[i] != null)
                pool[i].dump(file);
    }

    public ConstantUtf8 getConstantUtf8(int index) {
        Constant c = getConstant(index);
        assert c != null;
        assert c.tag == Constants.CONSTANT_Utf8;
        return (ConstantUtf8) c;
    }

    public ConstantModule getConstantModule(int index) {
        Constant c = getConstant(index);
        assert c != null;
        assert c.tag == Constants.CONSTANT_Module;
        return (ConstantModule) c;
    }

    public ConstantPackage getConstantPackage(int index) {
        Constant c = getConstant(index);
        assert c != null;
        assert c.tag == Constants.CONSTANT_Package;
        return (ConstantPackage) c;
    }

    public String getConstantString_CONSTANTClass(int index) {
        ConstantClass c = (ConstantClass) getConstant(index, Constants.CONSTANT_Class);
        index = c.getNameIndex();
        return ((ConstantUtf8) getConstant(index, Constants.CONSTANT_Utf8)).getValue();
    }

    public int getLength() {
        return poolSize;
    }

    @Override
    public String toString() {
        StringBuffer buf = new StringBuffer();

        for (int i = 1; i < poolSize; i++)
            buf.append(i + ")" + pool[i] + "\n");

        return buf.toString();
    }

    public int lookupInteger(int n) {
        for (int i = 1; i < poolSize; i++) {
            if (pool[i] instanceof ConstantInteger) {
                ConstantInteger c = (ConstantInteger) pool[i];
                if (c.getValue() == n)
                    return i;
            }
        }
        return -1;
    }

    public int lookupUtf8(String string) {
        Integer pos = utf8Cache.get(string);
        if (pos != null) {
            return pos;
        }
        for (int i = 1; i < poolSize; i++) {
            Constant c = pool[i];
            if (c != null && c.tag == Constants.CONSTANT_Utf8) {
                if (((ConstantUtf8) c).getValue().equals(string)) {
                    utf8Cache.put(string, i);
                    return i;
                }
            }
        }
        return -1;
    }

    public int lookupClass(String classname) {
        for (int i = 1; i < poolSize; i++) {
            Constant c = pool[i];
            if (c != null && c.tag == Constants.CONSTANT_Class) {
                int cIndex = ((ConstantClass) c).getNameIndex();
                String cName = ((ConstantUtf8) pool[cIndex]).getValue();
                if (cName.equals(classname))
                    return i;
            }
        }
        return -1;
    }

    public int addUtf8(String n) {
        int ret = lookupUtf8(n);
        if (ret != -1)
            return ret;
        adjustSize();
        ret = poolSize;
        pool[poolSize++] = new ConstantUtf8(n);
        return ret;
    }

    public int addInteger(int n) {
        int ret = lookupInteger(n);
        if (ret != -1)
            return ret;
        adjustSize();
        ret = poolSize;
        pool[poolSize++] = new ConstantInteger(n);
        return ret;
    }

    public int addArrayClass(ArrayType type) {
        return addClass(type.getSignature());
    }

    public int addClass(ObjectType type) {
        return addClass(type.getClassName());
    }

    public int addClass(String classname) {
        String toAdd = classname.replace('.', '/');
        int ret = lookupClass(toAdd);
        if (ret != -1)
            return ret;
        adjustSize();
        ConstantClass c = new ConstantClass(addUtf8(toAdd));
        ret = poolSize;
        pool[poolSize++] = c;
        return ret;
    }

    private void adjustSize() {
        if (poolSize + 3 >= pool.length) {
            Constant[] cs = pool;
            pool = new Constant[cs.length + 8];
            System.arraycopy(cs, 0, pool, 0, cs.length);
        }
        if (poolSize == 0)
            poolSize = 1; // someone about to do something in here!
    }

    public int addFieldref(String class_name, String field_name, String signature) {
        int ret = lookupFieldref(class_name, field_name, signature);
        int class_index, name_and_type_index;

        if (ret != -1)
            return ret;

        adjustSize();

        class_index = addClass(class_name);
        name_and_type_index = addNameAndType(field_name, signature);
        ret = poolSize;
        pool[poolSize++] = new ConstantFieldref(class_index, name_and_type_index);

        return ret;
    }

    public int lookupFieldref(String searchClassname, String searchFieldname, String searchSignature) {
        searchClassname = searchClassname.replace('.', '/');
        String k = new StringBuffer().append(searchClassname).append(searchFieldname).append(searchSignature)
                .toString();
        Integer pos = fieldCache.get(k);
        if (pos != null)
            return pos.intValue();
        for (int i = 1; i < poolSize; i++) {
            Constant c = pool[i];
            if (c != null && c.tag == Constants.CONSTANT_Fieldref) {
                ConstantFieldref cfr = (ConstantFieldref) c;
                ConstantNameAndType cnat = (ConstantNameAndType) pool[cfr.getNameAndTypeIndex()];

                // check the class
                int cIndex = cfr.getClassIndex();
                ConstantClass cc = (ConstantClass) pool[cIndex];
                String cName = ((ConstantUtf8) pool[cc.getNameIndex()]).getValue();
                if (!cName.equals(searchClassname))
                    continue;

                // check the name and type
                String name = ((ConstantUtf8) pool[cnat.getNameIndex()]).getValue();
                if (!name.equals(searchFieldname))
                    continue; // not this one
                String typeSignature = ((ConstantUtf8) pool[cnat.getSignatureIndex()]).getValue();
                if (!typeSignature.equals(searchSignature))
                    continue;
                fieldCache.put(k, new Integer(i));
                return i;
            }
        }
        return -1;
    }

    public int addNameAndType(String name, String signature) {
        int ret = lookupNameAndType(name, signature);
        if (ret != -1)
            return ret;
        adjustSize();
        int name_index = addUtf8(name);
        int signature_index = addUtf8(signature);
        ret = poolSize;
        pool[poolSize++] = new ConstantNameAndType(name_index, signature_index);
        return ret;
    }

    public int lookupNameAndType(String searchName, String searchTypeSignature) {
        for (int i = 1; i < poolSize; i++) {
            Constant c = pool[i];
            if (c != null && c.tag == Constants.CONSTANT_NameAndType) {
                ConstantNameAndType cnat = (ConstantNameAndType) c;
                String name = ((ConstantUtf8) pool[cnat.getNameIndex()]).getValue();
                if (!name.equals(searchName))
                    continue; // not this one
                String typeSignature = ((ConstantUtf8) pool[cnat.getSignatureIndex()]).getValue();
                if (!typeSignature.equals(searchTypeSignature))
                    continue;
                return i;
            }
        }
        return -1;
    }

    public int addFloat(float f) {
        int ret = lookupFloat(f);
        if (ret != -1)
            return ret;
        adjustSize();
        ret = poolSize;
        pool[poolSize++] = new ConstantFloat(f);
        return ret;
    }

    public int lookupFloat(float f) {
        int bits = Float.floatToIntBits(f);
        for (int i = 1; i < poolSize; i++) {
            Constant c = pool[i];
            if (c != null && c.tag == Constants.CONSTANT_Float) {
                ConstantFloat cf = (ConstantFloat) c;
                if (Float.floatToIntBits(cf.getValue()) == bits)
                    return i;
            }
        }
        return -1;
    }

    public int addDouble(double d) {
        int ret = lookupDouble(d);
        if (ret != -1)
            return ret;
        adjustSize();
        ret = poolSize;
        pool[poolSize] = new ConstantDouble(d);
        poolSize += 2;
        return ret;
    }

    public int lookupDouble(double d) {
        long bits = Double.doubleToLongBits(d);
        for (int i = 1; i < poolSize; i++) {
            Constant c = pool[i];
            if (c != null && c.tag == Constants.CONSTANT_Double) {
                ConstantDouble cf = (ConstantDouble) c;
                if (Double.doubleToLongBits(cf.getValue()) == bits)
                    return i;
            }
        }
        return -1;
    }

    public int addLong(long l) {
        int ret = lookupLong(l);
        if (ret != -1)
            return ret;
        adjustSize();
        ret = poolSize;
        pool[poolSize] = new ConstantLong(l);
        poolSize += 2;
        return ret;
    }

    public int lookupString(String s) {
        for (int i = 1; i < poolSize; i++) {
            Constant c = pool[i];
            if (c != null && c.tag == Constants.CONSTANT_String) {
                ConstantString cs = (ConstantString) c;
                ConstantUtf8 cu8 = (ConstantUtf8) pool[cs.getStringIndex()];
                if (cu8.getValue().equals(s))
                    return i;
            }
        }
        return -1;
    }

    public int addString(String str) {
        int ret = lookupString(str);
        if (ret != -1)
            return ret;
        int utf8 = addUtf8(str);
        adjustSize();
        ConstantString s = new ConstantString(utf8);
        ret = poolSize;
        pool[poolSize++] = s;
        return ret;
    }

    public int lookupLong(long l) {
        for (int i = 1; i < poolSize; i++) {
            Constant c = pool[i];
            if (c != null && c.tag == Constants.CONSTANT_Long) {
                ConstantLong cf = (ConstantLong) c;
                if (cf.getValue() == l)
                    return i;
            }
        }
        return -1;
    }

    public int addConstant(Constant c, ConstantPool cp) {
        Constant[] constants = cp.getConstantPool();
        switch (c.getTag()) {

        case Constants.CONSTANT_String: {
            ConstantString s = (ConstantString) c;
            ConstantUtf8 u8 = (ConstantUtf8) constants[s.getStringIndex()];

            return addString(u8.getValue());
        }

        case Constants.CONSTANT_Class: {
            ConstantClass s = (ConstantClass) c;
            ConstantUtf8 u8 = (ConstantUtf8) constants[s.getNameIndex()];

            return addClass(u8.getValue());
        }

        case Constants.CONSTANT_NameAndType: {
            ConstantNameAndType n = (ConstantNameAndType) c;
            ConstantUtf8 u8 = (ConstantUtf8) constants[n.getNameIndex()];
            ConstantUtf8 u8_2 = (ConstantUtf8) constants[n.getSignatureIndex()];

            return addNameAndType(u8.getValue(), u8_2.getValue());
        }

        case Constants.CONSTANT_InvokeDynamic: {
            ConstantInvokeDynamic cid = (ConstantInvokeDynamic) c;
            int index1 = cid.getBootstrapMethodAttrIndex();
            ConstantNameAndType cnat = (ConstantNameAndType) constants[cid.getNameAndTypeIndex()];
            ConstantUtf8 name = (ConstantUtf8) constants[cnat.getNameIndex()];
            ConstantUtf8 signature = (ConstantUtf8) constants[cnat.getSignatureIndex()];
            int index2 = addNameAndType(name.getValue(), signature.getValue());
            return addInvokeDynamic(index1, index2);
        }

        case Constants.CONSTANT_MethodHandle:
            ConstantMethodHandle cmh = (ConstantMethodHandle) c;
            return addMethodHandle(cmh.getReferenceKind(), addConstant(constants[cmh.getReferenceIndex()], cp));

        case Constants.CONSTANT_Utf8:
            return addUtf8(((ConstantUtf8) c).getValue());

        case Constants.CONSTANT_Double:
            return addDouble(((ConstantDouble) c).getValue());

        case Constants.CONSTANT_Float:
            return addFloat(((ConstantFloat) c).getValue());

        case Constants.CONSTANT_Long:
            return addLong(((ConstantLong) c).getValue());

        case Constants.CONSTANT_Integer:
            return addInteger(((ConstantInteger) c).getValue());

        case Constants.CONSTANT_MethodType:
            ConstantMethodType cmt = (ConstantMethodType) c;
            return addMethodType(addConstant(constants[cmt.getDescriptorIndex()], cp));

        case Constants.CONSTANT_InterfaceMethodref:
        case Constants.CONSTANT_Methodref:
        case Constants.CONSTANT_Fieldref: {
            ConstantCP m = (ConstantCP) c;
            ConstantClass clazz = (ConstantClass) constants[m.getClassIndex()];
            ConstantNameAndType n = (ConstantNameAndType) constants[m.getNameAndTypeIndex()];
            ConstantUtf8 u8 = (ConstantUtf8) constants[clazz.getNameIndex()];
            String class_name = u8.getValue().replace('/', '.');

            u8 = (ConstantUtf8) constants[n.getNameIndex()];
            String name = u8.getValue();

            u8 = (ConstantUtf8) constants[n.getSignatureIndex()];
            String signature = u8.getValue();

            switch (c.getTag()) {
            case Constants.CONSTANT_InterfaceMethodref:
                return addInterfaceMethodref(class_name, name, signature);

            case Constants.CONSTANT_Methodref:
                return addMethodref(class_name, name, signature); // OPTIMIZE indicate it should be cached!

            case Constants.CONSTANT_Fieldref:
                return addFieldref(class_name, name, signature);

            default: // Never reached
                throw new RuntimeException("Unknown constant type " + c);
            }
        }

        default: // Never reached
            throw new RuntimeException("Unknown constant type " + c);
        }
    }

    public int addMethodHandle(byte referenceKind, int referenceIndex) {
        adjustSize();
        int ret = poolSize;
        pool[poolSize++] = new ConstantMethodHandle(referenceKind, referenceIndex);
        return ret;
    }

    public int addMethodType(int descriptorIndex) {
        adjustSize();
        int ret = poolSize;
        pool[poolSize++] = new ConstantMethodType(descriptorIndex);
        return ret;
    }

    // OPTIMIZE should put it in the cache now
    public int addMethodref(String class_name, String method_name, String signature) {
        int ret, class_index, name_and_type_index;
        if ((ret = lookupMethodref(class_name, method_name, signature)) != -1)
            return ret; // Already in CP

        adjustSize();

        name_and_type_index = addNameAndType(method_name, signature);
        class_index = addClass(class_name);
        ret = poolSize;
        pool[poolSize++] = new ConstantMethodref(class_index, name_and_type_index);
        return ret;
    }

    public int addInvokeDynamic(int bootstrapMethodIndex, int constantNameAndTypeIndex) {
        adjustSize();
        int ret = poolSize;
        pool[poolSize++] = new ConstantInvokeDynamic(bootstrapMethodIndex, constantNameAndTypeIndex);
        return ret;
    }

    public int addInterfaceMethodref(String class_name, String method_name, String signature) {
        int ret = lookupInterfaceMethodref(class_name, method_name, signature);
        int class_index, name_and_type_index;

        if (ret != -1)
            return ret;
        adjustSize();

        class_index = addClass(class_name);
        name_and_type_index = addNameAndType(method_name, signature);
        ret = poolSize;
        pool[poolSize++] = new ConstantInterfaceMethodref(class_index, name_and_type_index);
        return ret;
    }

    public int lookupInterfaceMethodref(String searchClassname, String searchMethodName, String searchSignature) {
        searchClassname = searchClassname.replace('.', '/');
        for (int i = 1; i < poolSize; i++) {
            Constant c = pool[i];
            if (c != null && c.tag == Constants.CONSTANT_InterfaceMethodref) {
                ConstantInterfaceMethodref cfr = (ConstantInterfaceMethodref) c;

                ConstantClass cc = (ConstantClass) pool[cfr.getClassIndex()];
                String cName = ((ConstantUtf8) pool[cc.getNameIndex()]).getValue();
                if (!cName.equals(searchClassname))
                    continue;

                // check the name and type
                ConstantNameAndType cnat = (ConstantNameAndType) pool[cfr.getNameAndTypeIndex()];
                String name = ((ConstantUtf8) pool[cnat.getNameIndex()]).getValue();
                if (!name.equals(searchMethodName))
                    continue; // not this one
                String typeSignature = ((ConstantUtf8) pool[cnat.getSignatureIndex()]).getValue();
                if (!typeSignature.equals(searchSignature))
                    continue;
                return i;
            }
        }
        return -1;
    }

    public int lookupMethodref(String searchClassname, String searchMethodName, String searchSignature) {
        String key = new StringBuffer().append(searchClassname).append(searchMethodName).append(searchSignature)
                .toString();
        Integer cached = methodCache.get(key);
        if (cached != null)
            return cached.intValue();
        searchClassname = searchClassname.replace('.', '/');
        for (int i = 1; i < poolSize; i++) {
            Constant c = pool[i];
            if (c != null && c.tag == Constants.CONSTANT_Methodref) {
                ConstantMethodref cfr = (ConstantMethodref) c;
                ConstantNameAndType cnat = (ConstantNameAndType) pool[cfr.getNameAndTypeIndex()];

                // check the class
                int cIndex = cfr.getClassIndex();
                ConstantClass cc = (ConstantClass) pool[cIndex];
                String cName = ((ConstantUtf8) pool[cc.getNameIndex()]).getValue();
                if (!cName.equals(searchClassname))
                    continue;

                // check the name and type
                String name = ((ConstantUtf8) pool[cnat.getNameIndex()]).getValue();
                if (!name.equals(searchMethodName))
                    continue; // not this one
                String typeSignature = ((ConstantUtf8) pool[cnat.getSignatureIndex()]).getValue();
                if (!typeSignature.equals(searchSignature))
                    continue;
                methodCache.put(key, new Integer(i));
                return i;
            }
        }
        return -1;
    }

    public ConstantPool getFinalConstantPool() {
        Constant[] cs = new Constant[poolSize]; // create it the exact size we need
        System.arraycopy(pool, 0, cs, 0, poolSize);
        return new ConstantPool(cs);
    }

    public String getModuleName(int moduleIndex) {
        return getConstantModule(moduleIndex).getModuleName(this);
    }

    public String getPackageName(int packageIndex) {
        return getConstantPackage(packageIndex).getPackageName(this);
    }
}