Home | History | Annotate | Download | only in classfile
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  *
     17  */
     18 package org.apache.bcel.classfile;
     19 
     20 import java.io.DataInput;
     21 import java.io.DataOutputStream;
     22 import java.io.IOException;
     23 
     24 import org.apache.bcel.Const;
     25 
     26 /**
     27  * This class represents the constant pool, i.e., a table of constants, of
     28  * a parsed classfile. It may contain null references, due to the JVM
     29  * specification that skips an entry after an 8-byte constant (double,
     30  * long) entry.  Those interested in generating constant pools
     31  * programatically should see <a href="../generic/ConstantPoolGen.html">
     32  * ConstantPoolGen</a>.
     33 
     34  * @version $Id$
     35  * @see     Constant
     36  * @see     org.apache.bcel.generic.ConstantPoolGen
     37  */
     38 public class ConstantPool implements Cloneable, Node {
     39 
     40     private Constant[] constant_pool;
     41 
     42 
     43     /**
     44      * @param constant_pool Array of constants
     45      */
     46     public ConstantPool(final Constant[] constant_pool) {
     47         this.constant_pool = constant_pool;
     48     }
     49 
     50 
     51     /**
     52      * Read constants from given input stream.
     53      *
     54      * @param input Input stream
     55      * @throws IOException
     56      * @throws ClassFormatException
     57      */
     58     public ConstantPool(final DataInput input) throws IOException, ClassFormatException {
     59         byte tag;
     60         final int constant_pool_count = input.readUnsignedShort();
     61         constant_pool = new Constant[constant_pool_count];
     62         /* constant_pool[0] is unused by the compiler and may be used freely
     63          * by the implementation.
     64          */
     65         for (int i = 1; i < constant_pool_count; i++) {
     66             constant_pool[i] = Constant.readConstant(input);
     67             /* Quote from the JVM specification:
     68              * "All eight byte constants take up two spots in the constant pool.
     69              * If this is the n'th byte in the constant pool, then the next item
     70              * will be numbered n+2"
     71              *
     72              * Thus we have to increment the index counter.
     73              */
     74             tag = constant_pool[i].getTag();
     75             if ((tag == Const.CONSTANT_Double) || (tag == Const.CONSTANT_Long)) {
     76                 i++;
     77             }
     78         }
     79     }
     80 
     81 
     82     /**
     83      * Called by objects that are traversing the nodes of the tree implicitely
     84      * defined by the contents of a Java class. I.e., the hierarchy of methods,
     85      * fields, attributes, etc. spawns a tree of objects.
     86      *
     87      * @param v Visitor object
     88      */
     89     @Override
     90     public void accept( final Visitor v ) {
     91         v.visitConstantPool(this);
     92     }
     93 
     94 
     95     /**
     96      * Resolve constant to a string representation.
     97      *
     98      * @param  c Constant to be printed
     99      * @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 }
    373