1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.bcel.internal.classfile;
  23 
  24 import java.io.DataInput;
  25 import java.io.DataOutputStream;
  26 import java.io.IOException;
  27 
  28 import com.sun.org.apache.bcel.internal.Const;
  29 
  30 /**
  31  * This class represents the constant pool, i.e., a table of constants, of
  32  * a parsed classfile. It may contain null references, due to the JVM
  33  * specification that skips an entry after an 8-byte constant (double,
  34  * long) entry.  Those interested in generating constant pools
  35  * programatically should see <a href="../generic/ConstantPoolGen.html">
  36  * ConstantPoolGen</a>.
  37 
  38  * @see     Constant
  39  * @see     com.sun.org.apache.bcel.internal.generic.ConstantPoolGen
  40  */
  41 public class ConstantPool implements Cloneable, Node {
  42 
  43     private Constant[] constant_pool;
  44 
  45     /**
  46      * @param constant_pool Array of constants
  47      */
  48     public ConstantPool(final Constant[] constant_pool) {
  49         this.constant_pool = constant_pool;
  50     }
  51 
  52     /**
  53      * Reads constants from given input stream.
  54      *
  55      * @param input Input stream
  56      * @throws IOException
  57      * @throws ClassFormatException
  58      */
  59     public ConstantPool(final DataInput input) throws IOException, ClassFormatException {
  60         byte tag;
  61         final int constant_pool_count = input.readUnsignedShort();
  62         constant_pool = new Constant[constant_pool_count];
  63         /* constant_pool[0] is unused by the compiler and may be used freely
  64          * by the implementation.
  65          */
  66         for (int i = 1; i < constant_pool_count; i++) {
  67             constant_pool[i] = Constant.readConstant(input);
  68             /* Quote from the JVM specification:
  69              * "All eight byte constants take up two spots in the constant pool.
  70              * If this is the n'th byte in the constant pool, then the next item
  71              * will be numbered n+2"
  72              *
  73              * Thus we have to increment the index counter.
  74              */
  75             tag = constant_pool[i].getTag();
  76             if ((tag == Const.CONSTANT_Double) || (tag == Const.CONSTANT_Long)) {
  77                 i++;
  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      * Resolves constant to a string representation.
  96      *
  97      * @param  c Constant to be printed
  98      * @return String representation
  99      */
 100     public String constantToString( Constant c ) throws ClassFormatException {
 101         String str;
 102         int i;
 103         final byte tag = c.getTag();
 104         switch (tag) {
 105             case Const.CONSTANT_Class:
 106                 i = ((ConstantClass) c).getNameIndex();
 107                 c = getConstant(i, Const.CONSTANT_Utf8);
 108                 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
 109                 break;
 110             case Const.CONSTANT_String:
 111                 i = ((ConstantString) c).getStringIndex();
 112                 c = getConstant(i, Const.CONSTANT_Utf8);
 113                 str = "\"" + escape(((ConstantUtf8) c).getBytes()) + "\"";
 114                 break;
 115             case Const.CONSTANT_Utf8:
 116                 str = ((ConstantUtf8) c).getBytes();
 117                 break;
 118             case Const.CONSTANT_Double:
 119                 str = String.valueOf(((ConstantDouble) c).getBytes());
 120                 break;
 121             case Const.CONSTANT_Float:
 122                 str = String.valueOf(((ConstantFloat) c).getBytes());
 123                 break;
 124             case Const.CONSTANT_Long:
 125                 str = String.valueOf(((ConstantLong) c).getBytes());
 126                 break;
 127             case Const.CONSTANT_Integer:
 128                 str = String.valueOf(((ConstantInteger) c).getBytes());
 129                 break;
 130             case Const.CONSTANT_NameAndType:
 131                 str = constantToString(((ConstantNameAndType) c).getNameIndex(),
 132                         Const.CONSTANT_Utf8)
 133                         + " " + constantToString(((ConstantNameAndType) c).getSignatureIndex(),
 134                         Const.CONSTANT_Utf8);
 135                 break;
 136             case Const.CONSTANT_InterfaceMethodref:
 137             case Const.CONSTANT_Methodref:
 138             case Const.CONSTANT_Fieldref:
 139                 str = constantToString(((ConstantCP) c).getClassIndex(), Const.CONSTANT_Class)
 140                         + "." + constantToString(((ConstantCP) c).getNameAndTypeIndex(),
 141                         Const.CONSTANT_NameAndType);
 142                 break;
 143             case Const.CONSTANT_MethodHandle:
 144                 // Note that the ReferenceIndex may point to a Fieldref, Methodref or
 145                 // InterfaceMethodref - so we need to peek ahead to get the actual type.
 146                 final ConstantMethodHandle cmh = (ConstantMethodHandle) c;
 147                 str = Const.getMethodHandleName(cmh.getReferenceKind())
 148                         + " " + constantToString(cmh.getReferenceIndex(),
 149                         getConstant(cmh.getReferenceIndex()).getTag());
 150                 break;
 151             case Const.CONSTANT_MethodType:
 152                 final ConstantMethodType cmt = (ConstantMethodType) c;
 153                 str = constantToString(cmt.getDescriptorIndex(), Const.CONSTANT_Utf8);
 154                 break;
 155             case Const.CONSTANT_InvokeDynamic:
 156                 final ConstantInvokeDynamic cid = (ConstantInvokeDynamic) c;
 157                 str = cid.getBootstrapMethodAttrIndex()
 158                         + ":" + constantToString(cid.getNameAndTypeIndex(),
 159                         Const.CONSTANT_NameAndType);
 160                 break;
 161             case Const.CONSTANT_Module:
 162                 i = ((ConstantModule) c).getNameIndex();
 163                 c = getConstant(i, Const.CONSTANT_Utf8);
 164                 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
 165                 break;
 166             case Const.CONSTANT_Package:
 167                 i = ((ConstantPackage) c).getNameIndex();
 168                 c = getConstant(i, Const.CONSTANT_Utf8);
 169                 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
 170                 break;
 171             default: // Never reached
 172                 throw new RuntimeException("Unknown constant type " + tag);
 173         }
 174         return str;
 175     }
 176 
 177     private static String escape( final String str ) {
 178         final int len = str.length();
 179         final StringBuilder buf = new StringBuilder(len + 5);
 180         final char[] ch = str.toCharArray();
 181         for (int i = 0; i < len; i++) {
 182             switch (ch[i]) {
 183                 case '\n':
 184                     buf.append("\\n");
 185                     break;
 186                 case '\r':
 187                     buf.append("\\r");
 188                     break;
 189                 case '\t':
 190                     buf.append("\\t");
 191                     break;
 192                 case '\b':
 193                     buf.append("\\b");
 194                     break;
 195                 case '"':
 196                     buf.append("\\\"");
 197                     break;
 198                 default:
 199                     buf.append(ch[i]);
 200             }
 201         }
 202         return buf.toString();
 203     }
 204 
 205     /**
 206      * Retrieves constant at `index' from constant pool and resolve it to
 207      * a string representation.
 208      *
 209      * @param  index of constant in constant pool
 210      * @param  tag expected type
 211      * @return String representation
 212      */
 213     public String constantToString( final int index, final byte tag ) throws ClassFormatException {
 214         final Constant c = getConstant(index, tag);
 215         return constantToString(c);
 216     }
 217 
 218     /**
 219      * Dump constant pool to file stream in binary format.
 220      *
 221      * @param file Output file stream
 222      * @throws IOException
 223      */
 224     public void dump( final DataOutputStream file ) throws IOException {
 225         file.writeShort(constant_pool.length);
 226         for (int i = 1; i < constant_pool.length; i++) {
 227             if (constant_pool[i] != null) {
 228                 constant_pool[i].dump(file);
 229             }
 230         }
 231     }
 232 
 233     /**
 234      * Gets constant from constant pool.
 235      *
 236      * @param  index Index in constant pool
 237      * @return Constant value
 238      * @see    Constant
 239      */
 240     public Constant getConstant( final int index ) {
 241         if (index >= constant_pool.length || index < 0) {
 242             throw new ClassFormatException("Invalid constant pool reference: " + index
 243                     + ". Constant pool size is: " + constant_pool.length);
 244         }
 245         return constant_pool[index];
 246     }
 247 
 248     /**
 249      * Gets constant from constant pool and check whether it has the
 250      * expected type.
 251      *
 252      * @param  index Index in constant pool
 253      * @param  tag Tag of expected constant, i.e., its type
 254      * @return Constant value
 255      * @see    Constant
 256      * @throws  ClassFormatException
 257      */
 258     public Constant getConstant( final int index, final byte tag ) throws ClassFormatException {
 259         Constant c;
 260         c = getConstant(index);
 261         if (c == null) {
 262             throw new ClassFormatException("Constant pool at index " + index + " is null.");
 263         }
 264         if (c.getTag() != tag) {
 265             throw new ClassFormatException("Expected class `" + Const.getConstantName(tag)
 266                     + "' at index " + index + " and got " + c);
 267         }
 268         return c;
 269     }
 270 
 271     /**
 272      * @return Array of constants.
 273      * @see    Constant
 274      */
 275     public Constant[] getConstantPool() {
 276         return constant_pool;
 277     }
 278 
 279     /**
 280      * Gets string from constant pool and bypass the indirection of
 281      * `ConstantClass' and `ConstantString' objects. I.e. these classes have
 282      * an index field that points to another entry of the constant pool of
 283      * type `ConstantUtf8' which contains the real data.
 284      *
 285      * @param  index Index in constant pool
 286      * @param  tag Tag of expected constant, either ConstantClass or ConstantString
 287      * @return Contents of string reference
 288      * @see    ConstantClass
 289      * @see    ConstantString
 290      * @throws  ClassFormatException
 291      */
 292     public String getConstantString( final int index, final byte tag ) throws ClassFormatException {
 293         Constant c;
 294         int i;
 295         c = getConstant(index, tag);
 296         /* This switch() is not that elegant, since the four classes have the
 297          * same contents, they just differ in the name of the index
 298          * field variable.
 299          * But we want to stick to the JVM naming conventions closely though
 300          * we could have solved these more elegantly by using the same
 301          * variable name or by subclassing.
 302          */
 303         switch (tag) {
 304             case Const.CONSTANT_Class:
 305                 i = ((ConstantClass) c).getNameIndex();
 306                 break;
 307             case Const.CONSTANT_String:
 308                 i = ((ConstantString) c).getStringIndex();
 309                 break;
 310             case Const.CONSTANT_Module:
 311                 i = ((ConstantModule) c).getNameIndex();
 312                 break;
 313             case Const.CONSTANT_Package:
 314                 i = ((ConstantPackage) c).getNameIndex();
 315                 break;
 316             default:
 317                 throw new RuntimeException("getConstantString called with illegal tag " + tag);
 318         }
 319         // Finally get the string from the constant pool
 320         c = getConstant(i, Const.CONSTANT_Utf8);
 321         return ((ConstantUtf8) c).getBytes();
 322     }
 323 
 324 
 325     /**
 326      * @return Length of constant pool.
 327      */
 328     public int getLength() {
 329         return constant_pool == null ? 0 : constant_pool.length;
 330     }
 331 
 332 
 333     /**
 334      * @param constant Constant to set
 335      */
 336     public void setConstant( final int index, final Constant constant ) {
 337         constant_pool[index] = constant;
 338     }
 339 
 340 
 341     /**
 342      * @param constant_pool
 343      */
 344     public void setConstantPool( final Constant[] constant_pool ) {
 345         this.constant_pool = constant_pool;
 346     }
 347 
 348 
 349     /**
 350      * @return String representation.
 351      */
 352     @Override
 353     public String toString() {
 354         final StringBuilder buf = new StringBuilder();
 355         for (int i = 1; i < constant_pool.length; i++) {
 356             buf.append(i).append(")").append(constant_pool[i]).append("\n");
 357         }
 358         return buf.toString();
 359     }
 360 
 361 
 362     /**
 363      * @return deep copy of this constant pool
 364      */
 365     public ConstantPool copy() {
 366         ConstantPool c = null;
 367         try {
 368             c = (ConstantPool) clone();
 369             c.constant_pool = new Constant[constant_pool.length];
 370             for (int i = 1; i < constant_pool.length; i++) {
 371                 if (constant_pool[i] != null) {
 372                     c.constant_pool[i] = constant_pool[i].copy();
 373                 }
 374             }
 375         } catch (final CloneNotSupportedException e) {
 376             // TODO should this throw?
 377         }
 378         return c;
 379     }
 380 }