    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 * @version $Id$
    035 * @see     Constant
    036 * @see     org.apache.bcel.generic.ConstantPoolGen
    037 */
    038public class ConstantPool implements Cloneable, Node {
    039
    040    private Constant[] constant_pool;
    041
    042
    043    /**
    044     * @param constant_pool Array of constants
    045     */
    046    public ConstantPool(final Constant[] constant_pool) {
    047        this.constant_pool = constant_pool;
    048    }
    049
    050
    051    /**
    052     * Read constants from given input stream.
    053     *
    054     * @param input Input stream
    055     * @throws IOException
    056     * @throws ClassFormatException
    057     */
    058    public ConstantPool(final DataInput input) throws IOException, ClassFormatException {
    059        byte tag;
    060        final int constant_pool_count = input.readUnsignedShort();
    061        constant_pool = new Constant[constant_pool_count];
    062        /* constant_pool[0] is unused by the compiler and may be used freely
    063         * by the implementation.
    064         */
    065        for (int i = 1; i < constant_pool_count; i++) {
    066            constant_pool[i] = Constant.readConstant(input);
    067            /* Quote from the JVM specification:
    068             * "All eight byte constants take up two spots in the constant pool.
    069             * If this is the n'th byte in the constant pool, then the next item
    070             * will be numbered n+2"
    071             *
    072             * Thus we have to increment the index counter.
    073             */
    074            tag = constant_pool[i].getTag();
    075            if ((tag == Const.CONSTANT_Double) || (tag == Const.CONSTANT_Long)) {
    076                i++;
    077            }
    078        }
    079    }
    080
    081
    082    /**
    083     * Called by objects that are traversing the nodes of the tree implicitely
    084     * defined by the contents of a Java class. I.e., the hierarchy of methods,
    085     * fields, attributes, etc. spawns a tree of objects.
    086     *
    087     * @param v Visitor object
    088     */
    089    @Override
    090    public void accept( final Visitor v ) {
    091        v.visitConstantPool(this);
    092    }
    093
    094
    095    /**
    096     * Resolve constant to a string representation.
    097     *
    098     * @param  c Constant to be printed
    099     * @return String representation
    100     */
    101    public String constantToString( Constant c ) throws ClassFormatException {
    102        String str;
    103        int i;
    104        final byte tag = c.getTag();
    105        switch (tag) {
    106            case Const.CONSTANT_Class:
    107                i = ((ConstantClass) c).getNameIndex();
    108                c = getConstant(i, Const.CONSTANT_Utf8);
    109                str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
    110                break;
    111            case Const.CONSTANT_String:
    112                i = ((ConstantString) c).getStringIndex();
    113                c = getConstant(i, Const.CONSTANT_Utf8);
    114                str = "\"" + escape(((ConstantUtf8) c).getBytes()) + "\"";
    115                break;
    116            case Const.CONSTANT_Utf8:
    117                str = ((ConstantUtf8) c).getBytes();
    118                break;
    119            case Const.CONSTANT_Double:
    120                str = String.valueOf(((ConstantDouble) c).getBytes());
    121                break;
    122            case Const.CONSTANT_Float:
    123                str = String.valueOf(((ConstantFloat) c).getBytes());
    124                break;
    125            case Const.CONSTANT_Long:
    126                str = String.valueOf(((ConstantLong) c).getBytes());
    127                break;
    128            case Const.CONSTANT_Integer:
    129                str = String.valueOf(((ConstantInteger) c).getBytes());
    130                break;
    131            case Const.CONSTANT_NameAndType:
    132                str = constantToString(((ConstantNameAndType) c).getNameIndex(),
    133                        Const.CONSTANT_Utf8)
    134                        + " " + constantToString(((ConstantNameAndType) c).getSignatureIndex(),
    135                        Const.CONSTANT_Utf8);
    136                break;
    137            case Const.CONSTANT_InterfaceMethodref:
    138            case Const.CONSTANT_Methodref:
    139            case Const.CONSTANT_Fieldref:
    140                str = constantToString(((ConstantCP) c).getClassIndex(), Const.CONSTANT_Class)
    141                        + "." + constantToString(((ConstantCP) c).getNameAndTypeIndex(),
    142                        Const.CONSTANT_NameAndType);
    143                break;
    144            case Const.CONSTANT_MethodHandle:
    145                // Note that the ReferenceIndex may point to a Fieldref, Methodref or
    146                // InterfaceMethodref - so we need to peek ahead to get the actual type.
    147                final ConstantMethodHandle cmh = (ConstantMethodHandle) c;
    148                str = Const.getMethodHandleName(cmh.getReferenceKind())
    149                        + " " + constantToString(cmh.getReferenceIndex(),
    150                        getConstant(cmh.getReferenceIndex()).getTag());
    151                break;
    152            case Const.CONSTANT_MethodType:
    153                final ConstantMethodType cmt = (ConstantMethodType) c;
    154                str = constantToString(cmt.getDescriptorIndex(), Const.CONSTANT_Utf8);
    155                break;
    156            case Const.CONSTANT_InvokeDynamic:
    157                final ConstantInvokeDynamic cid = (ConstantInvokeDynamic) c;
    158                str = cid.getBootstrapMethodAttrIndex()
    159                        + ":" + constantToString(cid.getNameAndTypeIndex(),
    160                        Const.CONSTANT_NameAndType);
    161                break;
    162            default: // Never reached
    163                throw new RuntimeException("Unknown constant type " + tag);
    164        }
    165        return str;
    166    }
    167
    168
    169    private static String escape( final String str ) {
    170        final int len = str.length();
    171        final StringBuilder buf = new StringBuilder(len + 5);
    172        final char[] ch = str.toCharArray();
    173        for (int i = 0; i < len; i++) {
    174            switch (ch[i]) {
    175                case '\n':
    176                    buf.append("\\n");
    177                    break;
    178                case '\r':
    179                    buf.append("\\r");
    180                    break;
    181                case '\t':
    182                    buf.append("\\t");
    183                    break;
    184                case '\b':
    185                    buf.append("\\b");
    186                    break;
    187                case '"':
    188                    buf.append("\\\"");
    189                    break;
    190                default:
    191                    buf.append(ch[i]);
    192            }
    193        }
    194        return buf.toString();
    195    }
    196
    197
    198    /**
    199     * Retrieve constant at `index' from constant pool and resolve it to
    200     * a string representation.
    201     *
    202     * @param  index of constant in constant pool
    203     * @param  tag expected type
    204     * @return String representation
    205     */
    206    public String constantToString( final int index, final byte tag ) throws ClassFormatException {
    207        final Constant c = getConstant(index, tag);
    208        return constantToString(c);
    209    }
    210
    211
    212    /**
    213     * Dump constant pool to file stream in binary format.
    214     *
    215     * @param file Output file stream
    216     * @throws IOException
    217     */
    218    public void dump( final DataOutputStream file ) throws IOException {
    219        file.writeShort(constant_pool.length);
    220        for (int i = 1; i < constant_pool.length; i++) {
    221            if (constant_pool[i] != null) {
    222                constant_pool[i].dump(file);
    223            }
    224        }
    225    }
    226
    227
    228    /**
    229     * Get constant from constant pool.
    230     *
    231     * @param  index Index in constant pool
    232     * @return Constant value
    233     * @see    Constant
    234     */
    235    public Constant getConstant( final int index ) {
    236        if (index >= constant_pool.length || index < 0) {
    237            throw new ClassFormatException("Invalid constant pool reference: " + index
    238                    + ". Constant pool size is: " + constant_pool.length);
    239        }
    240        return constant_pool[index];
    241    }
    242
    243
    244    /**
    245     * Get 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    /**
    269     * @return Array of constants.
    270     * @see    Constant
    271     */
    272    public Constant[] getConstantPool() {
    273        return constant_pool;
    274    }
    275
    276
    277    /**
    278     * Get string from constant pool and bypass the indirection of
    279     * `ConstantClass' and `ConstantString' objects. I.e. these classes have
    280     * an index field that points to another entry of the constant pool of
    281     * type `ConstantUtf8' which contains the real data.
    282     *
    283     * @param  index Index in constant pool
    284     * @param  tag Tag of expected constant, either ConstantClass or ConstantString
    285     * @return Contents of string reference
    286     * @see    ConstantClass
    287     * @see    ConstantString
    288     * @throws  ClassFormatException
    289     */
    290    public String getConstantString( final int index, final byte tag ) throws ClassFormatException {
    291        Constant c;
    292        int i;
    293        c = getConstant(index, tag);
    294        /* This switch() is not that elegant, since the two classes have the
    295         * same contents, they just differ in the name of the index
    296         * field variable.
    297         * But we want to stick to the JVM naming conventions closely though
    298         * we could have solved these more elegantly by using the same
    299         * variable name or by subclassing.
    300         */
    301        switch (tag) {
    302            case Const.CONSTANT_Class:
    303                i = ((ConstantClass) c).getNameIndex();
    304                break;
    305            case Const.CONSTANT_String:
    306                i = ((ConstantString) c).getStringIndex();
    307                break;
    308            default:
    309                throw new RuntimeException("getConstantString called with illegal tag " + tag);
    310        }
    311        // Finally get the string from the constant pool
    312        c = getConstant(i, Const.CONSTANT_Utf8);
    313        return ((ConstantUtf8) c).getBytes();
    314    }
    315
    316
    317    /**
    318     * @return Length of constant pool.
    319     */
    320    public int getLength() {
    321        return constant_pool == null ? 0 : constant_pool.length;
    322    }
    323
    324
    325    /**
    326     * @param constant Constant to set
    327     */
    328    public void setConstant( final int index, final Constant constant ) {
    329        constant_pool[index] = constant;
    330    }
    331
    332
    333    /**
    334     * @param constant_pool
    335     */
    336    public void setConstantPool( final Constant[] constant_pool ) {
    337        this.constant_pool = constant_pool;
    338    }
    339
    340
    341    /**
    342     * @return String representation.
    343     */
    344    @Override
    345    public String toString() {
    346        final StringBuilder buf = new StringBuilder();
    347        for (int i = 1; i < constant_pool.length; i++) {
    348            buf.append(i).append(")").append(constant_pool[i]).append("\n");
    349        }
    350        return buf.toString();
    351    }
    352
    353
    354    /**
    355     * @return deep copy of this constant pool
    356     */
    357    public ConstantPool copy() {
    358        ConstantPool c = null;
    359        try {
    360            c = (ConstantPool) clone();
    361            c.constant_pool = new Constant[constant_pool.length];
    362            for (int i = 1; i < constant_pool.length; i++) {
    363                if (constant_pool[i] != null) {
    364                    c.constant_pool[i] = constant_pool[i].copy();
    365                }
    366            }
    367        } catch (final CloneNotSupportedException e) {
    368            // TODO should this throw?
    369        }
    370        return c;
    371    }
    372}