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