    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 *  Unless required by applicable law or agreed to in writing, software
    012 *  distributed under the License is distributed on an "AS IS" BASIS,
    013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 *  See the License for the specific language governing permissions and
    015 *  limitations under the License.
    016 *
    017 */
    018package org.apache.bcel.classfile;
    019
    020import java.io.DataInput;
    021import java.io.DataOutputStream;
    022import java.io.IOException;
    023
    024import org.apache.bcel.Const;
    025
    026/**
    027 * This class represents the constant pool, i.e., a table of constants, of
    028 * a parsed classfile. It may contain null references, due to the JVM
    029 * specification that skips an entry after an 8-byte constant (double,
    030 * long) entry.  Those interested in generating constant pools
    031 * programatically should see <a href="../generic/ConstantPoolGen.html">
    032 * ConstantPoolGen</a>.
    033
    034 * @see     Constant
    035 * @see     org.apache.bcel.generic.ConstantPoolGen
    036 */
    037public class ConstantPool implements Cloneable, Node {
    038
    039    private Constant[] constantPool;
    040
    041    /**
    042     * @param constantPool Array of constants
    043     */
    044    public ConstantPool(final Constant[] constantPool) {
    045        this.constantPool = constantPool;
    046    }
    047
    048    /**
    049     * Reads constants from given input stream.
    050     *
    051     * @param input Input stream
    052     * @throws IOException
    053     * @throws ClassFormatException
    054     */
    055    public ConstantPool(final DataInput input) throws IOException, ClassFormatException {
    056        byte tag;
    057        final int constant_pool_count = input.readUnsignedShort();
    058        constantPool = new Constant[constant_pool_count];
    059        /* constantPool[0] is unused by the compiler and may be used freely
    060         * by the implementation.
    061         */
    062        for (int i = 1; i < constant_pool_count; i++) {
    063            constantPool[i] = Constant.readConstant(input);
    064            /* Quote from the JVM specification:
    065             * "All eight byte constants take up two spots in the constant pool.
    066             * If this is the n'th byte in the constant pool, then the next item
    067             * will be numbered n+2"
    068             *
    069             * Thus we have to increment the index counter.
    070             */
    071            tag = constantPool[i].getTag();
    072            if ((tag == Const.CONSTANT_Double) || (tag == Const.CONSTANT_Long)) {
    073                i++;
    074            }
    075        }
    076    }
    077
    078    /**
    079     * Called by objects that are traversing the nodes of the tree implicitely
    080     * defined by the contents of a Java class. I.e., the hierarchy of methods,
    081     * fields, attributes, etc. spawns a tree of objects.
    082     *
    083     * @param v Visitor object
    084     */
    085    @Override
    086    public void accept( final Visitor v ) {
    087        v.visitConstantPool(this);
    088    }
    089
    090    /**
    091     * Resolves constant to a string representation.
    092     *
    093     * @param  c Constant to be printed
    094     * @return String representation
    095     */
    096    public String constantToString( Constant c ) throws ClassFormatException {
    097        String str;
    098        int i;
    099        final byte tag = c.getTag();
    100        switch (tag) {
    101            case Const.CONSTANT_Class:
    102                i = ((ConstantClass) c).getNameIndex();
    103                c = getConstant(i, Const.CONSTANT_Utf8);
    104                str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
    105                break;
    106            case Const.CONSTANT_String:
    107                i = ((ConstantString) c).getStringIndex();
    108                c = getConstant(i, Const.CONSTANT_Utf8);
    109                str = "\"" + escape(((ConstantUtf8) c).getBytes()) + "\"";
    110                break;
    111            case Const.CONSTANT_Utf8:
    112                str = ((ConstantUtf8) c).getBytes();
    113                break;
    114            case Const.CONSTANT_Double:
    115                str = String.valueOf(((ConstantDouble) c).getBytes());
    116                break;
    117            case Const.CONSTANT_Float:
    118                str = String.valueOf(((ConstantFloat) c).getBytes());
    119                break;
    120            case Const.CONSTANT_Long:
    121                str = String.valueOf(((ConstantLong) c).getBytes());
    122                break;
    123            case Const.CONSTANT_Integer:
    124                str = String.valueOf(((ConstantInteger) c).getBytes());
    125                break;
    126            case Const.CONSTANT_NameAndType:
    127                str = constantToString(((ConstantNameAndType) c).getNameIndex(),
    128                        Const.CONSTANT_Utf8)
    129                        + " " + constantToString(((ConstantNameAndType) c).getSignatureIndex(),
    130                        Const.CONSTANT_Utf8);
    131                break;
    132            case Const.CONSTANT_InterfaceMethodref:
    133            case Const.CONSTANT_Methodref:
    134            case Const.CONSTANT_Fieldref:
    135                str = constantToString(((ConstantCP) c).getClassIndex(), Const.CONSTANT_Class)
    136                        + "." + constantToString(((ConstantCP) c).getNameAndTypeIndex(),
    137                        Const.CONSTANT_NameAndType);
    138                break;
    139            case Const.CONSTANT_MethodHandle:
    140                // Note that the ReferenceIndex may point to a Fieldref, Methodref or
    141                // InterfaceMethodref - so we need to peek ahead to get the actual type.
    142                final ConstantMethodHandle cmh = (ConstantMethodHandle) c;
    143                str = Const.getMethodHandleName(cmh.getReferenceKind())
    144                        + " " + constantToString(cmh.getReferenceIndex(),
    145                        getConstant(cmh.getReferenceIndex()).getTag());
    146                break;
    147            case Const.CONSTANT_MethodType:
    148                final ConstantMethodType cmt = (ConstantMethodType) c;
    149                str = constantToString(cmt.getDescriptorIndex(), Const.CONSTANT_Utf8);
    150                break;
    151            case Const.CONSTANT_InvokeDynamic:
    152                final ConstantInvokeDynamic cid = (ConstantInvokeDynamic) c;
    153                str = cid.getBootstrapMethodAttrIndex()
    154                        + ":" + constantToString(cid.getNameAndTypeIndex(),
    155                        Const.CONSTANT_NameAndType);
    156                break;
    157            case Const.CONSTANT_Module:
    158                i = ((ConstantModule) c).getNameIndex();
    159                c = getConstant(i, Const.CONSTANT_Utf8);
    160                str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
    161                break;
    162            case Const.CONSTANT_Package:
    163                i = ((ConstantPackage) c).getNameIndex();
    164                c = getConstant(i, Const.CONSTANT_Utf8);
    165                str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
    166                break;
    167            default: // Never reached
    168                throw new IllegalArgumentException("Unknown constant type " + tag);
    169        }
    170        return str;
    171    }
    172
    173    private static String escape( final String str ) {
    174        final int len = str.length();
    175        final StringBuilder buf = new StringBuilder(len + 5);
    176        final char[] ch = str.toCharArray();
    177        for (int i = 0; i < len; i++) {
    178            switch (ch[i]) {
    179                case '\n':
    180                    buf.append("\\n");
    181                    break;
    182                case '\r':
    183                    buf.append("\\r");
    184                    break;
    185                case '\t':
    186                    buf.append("\\t");
    187                    break;
    188                case '\b':
    189                    buf.append("\\b");
    190                    break;
    191                case '"':
    192                    buf.append("\\\"");
    193                    break;
    194                default:
    195                    buf.append(ch[i]);
    196            }
    197        }
    198        return buf.toString();
    199    }
    200
    201    /**
    202     * Retrieves constant at `index' from constant pool and resolve it to
    203     * a string representation.
    204     *
    205     * @param  index of constant in constant pool
    206     * @param  tag expected type
    207     * @return String representation
    208     */
    209    public String constantToString( final int index, final byte tag ) throws ClassFormatException {
    210        final Constant c = getConstant(index, tag);
    211        return constantToString(c);
    212    }
    213
    214    /**
    215     * Dump constant pool to file stream in binary format.
    216     *
    217     * @param file Output file stream
    218     * @throws IOException
    219     */
    220    public void dump( final DataOutputStream file ) throws IOException {
    221        file.writeShort(constantPool.length);
    222        for (int i = 1; i < constantPool.length; i++) {
    223            if (constantPool[i] != null) {
    224                constantPool[i].dump(file);
    225            }
    226        }
    227    }
    228
    229    /**
    230     * Gets constant from constant pool.
    231     *
    232     * @param  index Index in constant pool
    233     * @return Constant value
    234     * @see    Constant
    235     */
    236    public Constant getConstant( final int index ) {
    237        if (index >= constantPool.length || index < 0) {
    238            throw new ClassFormatException("Invalid constant pool reference: " + index
    239                    + ". Constant pool size is: " + constantPool.length);
    240        }
    241        return constantPool[index];
    242    }
    243
    244    /**
    245     * Gets constant from constant pool and check whether it has the
    246     * expected type.
    247     *
    248     * @param  index Index in constant pool
    249     * @param  tag Tag of expected constant, i.e., its type
    250     * @return Constant value
    251     * @see    Constant
    252     * @throws  ClassFormatException
    253     */
    254    public Constant getConstant( final int index, final byte tag ) throws ClassFormatException {
    255        Constant c;
    256        c = getConstant(index);
    257        if (c == null) {
    258            throw new ClassFormatException("Constant pool at index " + index + " is null.");
    259        }
    260        if (c.getTag() != tag) {
    261            throw new ClassFormatException("Expected class `" + Const.getConstantName(tag)
    262                    + "' at index " + index + " and got " + c);
    263        }
    264        return c;
    265    }
    266
    267    /**
    268     * @return Array of constants.
    269     * @see    Constant
    270     */
    271    public Constant[] getConstantPool() {
    272        return constantPool;
    273    }
    274
    275    /**
    276     * Gets string from constant pool and bypass the indirection of
    277     * `ConstantClass' and `ConstantString' objects. I.e. these classes have
    278     * an index field that points to another entry of the constant pool of
    279     * type `ConstantUtf8' which contains the real data.
    280     *
    281     * @param  index Index in constant pool
    282     * @param  tag Tag of expected constant, either ConstantClass or ConstantString
    283     * @return Contents of string reference
    284     * @see    ConstantClass
    285     * @see    ConstantString
    286     * @throws  ClassFormatException
    287     */
    288    public String getConstantString( final int index, final byte tag ) throws ClassFormatException {
    289        Constant c;
    290        int i;
    291        c = getConstant(index, tag);
    292        /* This switch() is not that elegant, since the four classes have the
    293         * same contents, they just differ in the name of the index
    294         * field variable.
    295         * But we want to stick to the JVM naming conventions closely though
    296         * we could have solved these more elegantly by using the same
    297         * variable name or by subclassing.
    298         */
    299        switch (tag) {
    300            case Const.CONSTANT_Class:
    301                i = ((ConstantClass) c).getNameIndex();
    302                break;
    303            case Const.CONSTANT_String:
    304                i = ((ConstantString) c).getStringIndex();
    305                break;
    306            case Const.CONSTANT_Module:
    307                i = ((ConstantModule) c).getNameIndex();
    308                break;
    309            case Const.CONSTANT_Package:
    310                i = ((ConstantPackage) c).getNameIndex();
    311                break;
    312            default:
    313                throw new IllegalArgumentException("getConstantString called with illegal tag " + tag);
    314        }
    315        // Finally get the string from the constant pool
    316        c = getConstant(i, Const.CONSTANT_Utf8);
    317        return ((ConstantUtf8) c).getBytes();
    318    }
    319
    320
    321    /**
    322     * @return Length of constant pool.
    323     */
    324    public int getLength() {
    325        return constantPool == null ? 0 : constantPool.length;
    326    }
    327
    328
    329    /**
    330     * @param constant Constant to set
    331     */
    332    public void setConstant( final int index, final Constant constant ) {
    333        constantPool[index] = constant;
    334    }
    335
    336
    337    /**
    338     * @param constantPool
    339     */
    340    public void setConstantPool( final Constant[] constantPool ) {
    341        this.constantPool = constantPool;
    342    }
    343
    344
    345    /**
    346     * @return String representation.
    347     */
    348    @Override
    349    public String toString() {
    350        final StringBuilder buf = new StringBuilder();
    351        for (int i = 1; i < constantPool.length; i++) {
    352            buf.append(i).append(")").append(constantPool[i]).append("\n");
    353        }
    354        return buf.toString();
    355    }
    356
    357
    358    /**
    359     * @return deep copy of this constant pool
    360     */
    361    public ConstantPool copy() {
    362        ConstantPool c = null;
    363        try {
    364            c = (ConstantPool) clone();
    365            c.constantPool = new Constant[constantPool.length];
    366            for (int i = 1; i < constantPool.length; i++) {
    367                if (constantPool[i] != null) {
    368                    c.constantPool[i] = constantPool[i].copy();
    369                }
    370            }
    371        } catch (final CloneNotSupportedException e) {
    372            // TODO should this throw?
    373        }
    374        return c;
    375    }
    376}