Package org.cojen.classfile

Source Code of org.cojen.classfile.ConstantPool

/*
*  Copyright 2004-2010 Brian S O'Neill
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

package org.cojen.classfile;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.cojen.classfile.constant.ConstantClassInfo;
import org.cojen.classfile.constant.ConstantDoubleInfo;
import org.cojen.classfile.constant.ConstantFieldInfo;
import org.cojen.classfile.constant.ConstantFloatInfo;
import org.cojen.classfile.constant.ConstantIntegerInfo;
import org.cojen.classfile.constant.ConstantInterfaceMethodInfo;
import org.cojen.classfile.constant.ConstantLongInfo;
import org.cojen.classfile.constant.ConstantMethodInfo;
import org.cojen.classfile.constant.ConstantNameAndTypeInfo;
import org.cojen.classfile.constant.ConstantStringInfo;
import org.cojen.classfile.constant.ConstantUTFInfo;

/**
* This class corresponds to the constant_pool structure as defined in <i>The
* Java Virtual Machine Specification</i>.
*
* <p>ConstantPool entries are not written out in the order in which they were
* added to it. Instead, their ordering is changed such that String, Integer
* and Float constants are written out first. This provides a slight
* optimization for referencing these constants from a code attribute.
* It means that Opcode.LDC will more likely be used (one-byte index) than
* Opcode.LDC_W (two-byte index).
*
* @author Brian S O'Neill
* @see Opcode
*/
public class ConstantPool {
    // A set of ConstantInfo objects.
    private Map<ConstantInfo, ConstantInfo> mConstants = new HashMap<ConstantInfo, ConstantInfo>();
    // Indexed list of constants.
    private Vector<ConstantInfo> mIndexedConstants;
    private int mEntries;

    // Preserve the order only if the constant pool was read in.
    private boolean mPreserveOrder;

    ConstantPool() {
    }

    private ConstantPool(Vector<ConstantInfo> indexedConstants) {
        mIndexedConstants = indexedConstants;

        int size = indexedConstants.size();
        for (int i=1; i<size; i++) {
            ConstantInfo ci = indexedConstants.get(i);
            if (ci != null) {
                mConstants.put(ci, ci);
                mEntries += ci.getEntryCount();
            }
        }

        mPreserveOrder = true;
    }

    /**
     * Returns a constant from the pool by index, or null if not found. If this
     * constant pool has not yet been written or was not created by the read
     * method, indexes are not assigned, and an IllegalStateException is
     * thrown.
     *
     * @throws ArrayIndexOutOfBoundsException if index is out of range.
     * @throws IllegalStateException if indexes are not assigned
     */
    public ConstantInfo getConstant(int index) {
        if (mIndexedConstants == null) {
            throw new IllegalStateException
                ("Constant pool indexes have not been assigned");
        }

        return mIndexedConstants.get(index);
    }

    /**
     * Returns all the constants in the pool, in no particular order.
     */
    public Set<ConstantInfo> getAllConstants() {
        return Collections.unmodifiableSet(mConstants.keySet());
    }

    /**
     * Returns the number of constants in the pool.
     */   
    public int getSize() {
        return mEntries;
    }

    /**
     * Get or create a constant from the constant pool representing a class.
     */
    public ConstantClassInfo addConstantClass(String className) {
        return (ConstantClassInfo)addConstant(new ConstantClassInfo(this, className));
    }

    /**
     * Get or create a constant from the constant pool representing an array
     * class.
     *
     * @param dim Number of array dimensions.
     */
    public ConstantClassInfo addConstantClass(String className, int dim) {
        return (ConstantClassInfo)addConstant(new ConstantClassInfo(this, className, dim));
    }
   
    /**
     * Get or create a constant from the constant pool representing a class.
     */
    public ConstantClassInfo addConstantClass(TypeDesc type) {
        return (ConstantClassInfo)addConstant(new ConstantClassInfo(this, type));
    }

    /**
     * Get or create a constant from the constant pool representing a field in
     * any class.
     */
    public ConstantFieldInfo addConstantField(String className,
                                              String fieldName,
                                              TypeDesc type) {
        ConstantInfo ci = new ConstantFieldInfo
            (addConstantClass(className), addConstantNameAndType(fieldName, type));
        return (ConstantFieldInfo)addConstant(ci);
    }

    /**
     * Get or create a constant from the constant pool representing a method
     * in any class. If the method returns void, set ret to null.
     */
    public ConstantMethodInfo addConstantMethod(String className,
                                                String methodName,
                                                TypeDesc ret,
                                                TypeDesc[] params) {
       
        MethodDesc md = MethodDesc.forArguments(ret, params);
        ConstantInfo ci = new ConstantMethodInfo
            (addConstantClass(className), addConstantNameAndType(methodName, md));
        return (ConstantMethodInfo)addConstant(ci);
    }
   
    /**
     * Get or create a constant from the constant pool representing an
     * interface method in any interface.
     */
    public ConstantInterfaceMethodInfo addConstantInterfaceMethod(String className,
                                                                  String methodName,
                                                                  TypeDesc ret,
                                                                  TypeDesc[] params) {
       
        MethodDesc md = MethodDesc.forArguments(ret, params);
        ConstantInfo ci = new ConstantInterfaceMethodInfo
            (addConstantClass(className), addConstantNameAndType(methodName, md));
        return (ConstantInterfaceMethodInfo)addConstant(ci);
    }

    /**
     * Get or create a constant from the constant pool representing a
     * constructor in any class.
     */
    public ConstantMethodInfo addConstantConstructor(String className,
                                                     TypeDesc[] params) {
        return addConstantMethod(className, "<init>", null, params);
    }

    /**
     * Get or create a constant integer from the constant pool.
     */
    public ConstantIntegerInfo addConstantInteger(int value) {
        return (ConstantIntegerInfo)addConstant(new ConstantIntegerInfo(value));
    }

    /**
     * Get or create a constant long from the constant pool.
     */
    public ConstantLongInfo addConstantLong(long value) {
        return (ConstantLongInfo)addConstant(new ConstantLongInfo(value));
    }

    /**
     * Get or create a constant float from the constant pool.
     */
    public ConstantFloatInfo addConstantFloat(float value) {
        return (ConstantFloatInfo)addConstant(new ConstantFloatInfo(value));
    }

    /**
     * Get or create a constant double from the constant pool.
     */
    public ConstantDoubleInfo addConstantDouble(double value) {
        return (ConstantDoubleInfo)addConstant(new ConstantDoubleInfo(value));
    }

    /**
     * Get or create a constant string from the constant pool.
     */
    public ConstantStringInfo addConstantString(String str) {
        return (ConstantStringInfo)addConstant(new ConstantStringInfo(this, str));
    }

    /**
     * Get or create a constant UTF string from the constant pool.
     */
    public ConstantUTFInfo addConstantUTF(String str) {
        return (ConstantUTFInfo)addConstant(new ConstantUTFInfo(str));
    }

    /**
     * Get or create a constant name and type structure from the constant pool.
     */
    public ConstantNameAndTypeInfo addConstantNameAndType(String name,
                                                          Descriptor type) {
        return (ConstantNameAndTypeInfo)addConstant(new ConstantNameAndTypeInfo(this, name, type));
    }

    /**
     * Get or create a constant name and type structure from the constant pool.
     */
    public ConstantNameAndTypeInfo addConstantNameAndType(ConstantUTFInfo nameConstant,
                                                          ConstantUTFInfo descConstant) {
        return (ConstantNameAndTypeInfo)addConstant
            (new ConstantNameAndTypeInfo(nameConstant, descConstant));
    }


    /**
     * Will only insert into the pool if the constant is not already in the
     * pool.
     *
     * @return The actual constant in the pool.
     */
    public ConstantInfo addConstant(ConstantInfo constant) {
        ConstantInfo info = mConstants.get(constant);
        if (info != null) {
            return info;
        }
       
        int entryCount = constant.getEntryCount();

        if (mIndexedConstants != null && mPreserveOrder) {
            int size = mIndexedConstants.size();
            mIndexedConstants.setSize(size + entryCount);
            mIndexedConstants.set(size, constant);
            constant.mIndex = size;
        }

        mConstants.put(constant, constant);
        mEntries += entryCount;

        return constant;
    }

    public void writeTo(DataOutput dout) throws IOException {
        // Write out the size (number of entries) of the constant pool.

        int size = getSize() + 1; // add one because constant 0 is reserved
        if (size >= 65535) {
            throw new IllegalStateException
                ("Constant pool entry count cannot exceed 65535: " + size);
        }
        dout.writeShort(size);

        if (mIndexedConstants == null || !mPreserveOrder) {
            mIndexedConstants = new Vector<ConstantInfo>(size);
            mIndexedConstants.setSize(size);
            int index = 1; // one-based constant pool index
           
            // First write constants of higher priority -- String, Integer,
            // Float.
            // This is a slight optimization. It means that Opcode.LDC will
            // more likely be used (one-byte index) than Opcode.LDC_W (two-byte
            // index).
           
            Iterator it = mConstants.keySet().iterator();
            while (it.hasNext()) {
                ConstantInfo constant = (ConstantInfo)it.next();
                if (constant.hasPriority()) {
                    constant.mIndex = index;
                    mIndexedConstants.set(index, constant);
                    index += constant.getEntryCount();
                }
            }
           
            // Now write all non-priority constants.
           
            it = mConstants.keySet().iterator();
            while (it.hasNext()) {
                ConstantInfo constant = (ConstantInfo)it.next();
                if (!constant.hasPriority()) {
                    constant.mIndex = index;
                    mIndexedConstants.set(index, constant);
                    index += constant.getEntryCount();
                }
            }
        }

        // Now actually write out the constants since the indexes have been
        // resolved.

        for (int i=1; i<size; i++) {
            Object obj = mIndexedConstants.get(i);
            if (obj != null) {
                ((ConstantInfo)obj).writeTo(dout);
            }
        }
    }

    public static ConstantPool readFrom(DataInput din) throws IOException {
        int size = din.readUnsignedShort();
        Vector<ConstantInfo> constants = new Vector<ConstantInfo>(size);
        constants.setSize(size);

        int index = 1;
        while (index < size) {
            int tag = din.readByte();
            int entryCount = 1;
            ConstantInfo constant;

            switch (tag) {
            case ConstantInfo.TAG_UTF8:
                constant = new ConstantUTFInfo(din.readUTF());
                break;
            case ConstantInfo.TAG_INTEGER:
                constant = new ConstantIntegerInfo(din.readInt());
                break;
            case ConstantInfo.TAG_FLOAT:
                constant = new ConstantFloatInfo(din.readFloat());
                break;
            case ConstantInfo.TAG_LONG:
                constant = new ConstantLongInfo(din.readLong());
                entryCount++;
                break;
            case ConstantInfo.TAG_DOUBLE:
                constant = new ConstantDoubleInfo(din.readDouble());
                entryCount++;
                break;

            case ConstantInfo.TAG_CLASS:
            case ConstantInfo.TAG_STRING:
                constant = new TempEntry(tag, din.readUnsignedShort());
                break;

            case ConstantInfo.TAG_FIELD:
            case ConstantInfo.TAG_METHOD:
            case ConstantInfo.TAG_INTERFACE_METHOD:
            case ConstantInfo.TAG_NAME_AND_TYPE:
                constant = new TempEntry
                    (tag, (din.readShort() << 16) | (din.readUnsignedShort()));
                break;

            default:
                throw new IOException("Invalid constant pool tag: " + tag);
            }

            constant.mIndex = index;
            constants.set(index, constant);
            index += entryCount;
        }

        for (index = 1; index < size; index++) {
            resolve(constants, index);
        }

        return new ConstantPool(constants);
    }

    private static ConstantInfo resolve(List<ConstantInfo> constants, int index) {
        ConstantInfo constant = constants.get(index);
        if (constant == null) {
            return null;
        }
       
        if (!(constant instanceof TempEntry)) {
            return constant;
        }

        TempEntry entry = (TempEntry)constant;
        int data = entry.mData;
        int index1 = data & 0xffff;

        ConstantInfo ci1 = constants.get(index1);

        if (ci1 instanceof TempEntry) {
            ci1 = resolve(constants, index1);
        }

        ConstantInfo ci = null;

        switch (entry.mTag) {
        case ConstantInfo.TAG_CLASS:
            ci = new ConstantClassInfo((ConstantUTFInfo)ci1);
            break;
        case ConstantInfo.TAG_STRING:
            ci = new ConstantStringInfo((ConstantUTFInfo)ci1);
            break;

        case ConstantInfo.TAG_FIELD:
        case ConstantInfo.TAG_METHOD:
        case ConstantInfo.TAG_INTERFACE_METHOD:
        case ConstantInfo.TAG_NAME_AND_TYPE:
            int index2 = data >>> 16;
           
            ConstantInfo ci2 = constants.get(index2);
           
            if (ci2 instanceof TempEntry) {
                ci2 = resolve(constants, index2);
            }

            switch (entry.mTag) {
            case ConstantInfo.TAG_FIELD:
                ci = new ConstantFieldInfo
                    ((ConstantClassInfo)ci2, (ConstantNameAndTypeInfo)ci1);
                break;
            case ConstantInfo.TAG_METHOD:
                ci = new ConstantMethodInfo
                    ((ConstantClassInfo)ci2, (ConstantNameAndTypeInfo)ci1);
                break;
            case ConstantInfo.TAG_INTERFACE_METHOD:
                ci = new ConstantInterfaceMethodInfo
                    ((ConstantClassInfo)ci2, (ConstantNameAndTypeInfo)ci1);
                break;
            case ConstantInfo.TAG_NAME_AND_TYPE:
                ci = new ConstantNameAndTypeInfo
                    ((ConstantUTFInfo)ci2, (ConstantUTFInfo)ci1);
                break;
            }

            break;
        }

        ci.mIndex = index;
        constants.set(index, ci);

        return ci;
    }

    private static class TempEntry extends ConstantInfo {
        public int mTag;
        public int mData;

        public TempEntry(int tag, int data) {
            super(-1);
            mTag = tag;
            mData = data;
        }
    }
}
TOP

Related Classes of org.cojen.classfile.ConstantPool

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.