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 
  25 import  com.sun.org.apache.bcel.internal.Constants;
  26 import  java.io.*;
  27 
  28 /**
  29  * This class represents the constant pool, i.e., a table of constants, of
  30  * a parsed classfile. It may contain null references, due to the JVM
  31  * specification that skips an entry after an 8-byte constant (double,
  32  * long) entry.  Those interested in generating constant pools
  33  * programatically should see <a href="../generic/ConstantPoolGen.html">
  34  * ConstantPoolGen</a>.
  35 
  36  * @see     Constant
  37  * @see     com.sun.org.apache.bcel.internal.generic.ConstantPoolGen
  38  * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
  39  */
  40 public class ConstantPool implements Cloneable, Node, Serializable {
  41   private int        constant_pool_count;
  42   private Constant[] constant_pool;
  43 
  44   /**
  45    * @param constant_pool Array of constants
  46    */
  47   public ConstantPool(Constant[] constant_pool)
  48   {
  49     setConstantPool(constant_pool);
  50   }
  51 
  52   /**
  53    * Read constants from given file stream.
  54    *
  55    * @param file Input stream
  56    * @throws IOException
  57    * @throws ClassFormatException
  58    */
  59   ConstantPool(DataInputStream file) throws IOException, ClassFormatException
  60   {
  61     byte tag;
  62 
  63     constant_pool_count = file.readUnsignedShort();
  64     constant_pool       = new Constant[constant_pool_count];
  65 
  66     /* constant_pool[0] is unused by the compiler and may be used freely
  67      * by the implementation.
  68      */
  69     for(int i=1; i < constant_pool_count; i++) {
  70       constant_pool[i] = Constant.readConstant(file);
  71 
  72       /* Quote from the JVM specification:
  73        * "All eight byte constants take up two spots in the constant pool.
  74        * If this is the n'th byte in the constant pool, then the next item
  75        * will be numbered n+2"
  76        *
  77        * Thus we have to increment the index counter.
  78        */
  79       tag = constant_pool[i].getTag();
  80       if((tag == Constants.CONSTANT_Double) || (tag == Constants.CONSTANT_Long))
  81         i++;
  82     }
  83   }
  84 
  85   /**
  86    * Called by objects that are traversing the nodes of the tree implicitely
  87    * defined by the contents of a Java class. I.e., the hierarchy of methods,
  88    * fields, attributes, etc. spawns a tree of objects.
  89    *
  90    * @param v Visitor object
  91    */
  92   public void accept(Visitor v) {
  93     v.visitConstantPool(this);
  94   }
  95 
  96   /**
  97    * Resolve constant to a string representation.
  98    *
  99    * @param  constant Constant to be printed
 100    * @return String representation
 101    */
 102   public String constantToString(Constant c)
 103        throws ClassFormatException
 104   {
 105     String   str;
 106     int      i;
 107     byte     tag = c.getTag();
 108 
 109     switch(tag) {
 110     case Constants.CONSTANT_Class:
 111       i   = ((ConstantClass)c).getNameIndex();
 112       c   = getConstant(i, Constants.CONSTANT_Utf8);
 113       str = Utility.compactClassName(((ConstantUtf8)c).getBytes(), false);
 114       break;
 115 
 116     case Constants.CONSTANT_String:
 117       i   = ((ConstantString)c).getStringIndex();
 118       c   = getConstant(i, Constants.CONSTANT_Utf8);
 119       str = "\"" + escape(((ConstantUtf8)c).getBytes()) + "\"";
 120       break;
 121 
 122     case Constants.CONSTANT_Utf8:    str = ((ConstantUtf8)c).getBytes();         break;
 123     case Constants.CONSTANT_Double:  str = "" + ((ConstantDouble)c).getBytes();  break;
 124     case Constants.CONSTANT_Float:   str = "" + ((ConstantFloat)c).getBytes();   break;
 125     case Constants.CONSTANT_Long:    str = "" + ((ConstantLong)c).getBytes();    break;
 126     case Constants.CONSTANT_Integer: str = "" + ((ConstantInteger)c).getBytes(); break;
 127 
 128     case Constants.CONSTANT_NameAndType:
 129       str = (constantToString(((ConstantNameAndType)c).getNameIndex(),
 130                               Constants.CONSTANT_Utf8) + " " +
 131              constantToString(((ConstantNameAndType)c).getSignatureIndex(),
 132                               Constants.CONSTANT_Utf8));
 133       break;
 134 
 135     case Constants.CONSTANT_InterfaceMethodref: case Constants.CONSTANT_Methodref:
 136     case Constants.CONSTANT_Fieldref:
 137       str = (constantToString(((ConstantCP)c).getClassIndex(),
 138                               Constants.CONSTANT_Class) + "." +
 139              constantToString(((ConstantCP)c).getNameAndTypeIndex(),
 140                               Constants.CONSTANT_NameAndType));
 141       break;
 142 
 143     default: // Never reached
 144       throw new RuntimeException("Unknown constant type " + tag);
 145     }
 146 
 147     return str;
 148   }
 149 
 150   private static final String escape(String str) {
 151     int          len = str.length();
 152     StringBuffer buf = new StringBuffer(len + 5);
 153     char[]       ch  = str.toCharArray();
 154 
 155     for(int i=0; i < len; i++) {
 156       switch(ch[i]) {
 157       case '\n' : buf.append("\\n"); break;
 158       case '\r' : buf.append("\\r"); break;
 159       case '\t' : buf.append("\\t"); break;
 160       case '\b' : buf.append("\\b"); break;
 161       case '"'  : buf.append("\\\""); break;
 162       default: buf.append(ch[i]);
 163       }
 164     }
 165 
 166     return buf.toString();
 167   }
 168 
 169 
 170   /**
 171    * Retrieve constant at `index' from constant pool and resolve it to
 172    * a string representation.
 173    *
 174    * @param  index of constant in constant pool
 175    * @param  tag expected type
 176    * @return String representation
 177    */
 178   public String constantToString(int index, byte tag)
 179        throws ClassFormatException
 180   {
 181     Constant c = getConstant(index, tag);
 182     return constantToString(c);
 183   }
 184 
 185   /**
 186    * Dump constant pool to file stream in binary format.
 187    *
 188    * @param file Output file stream
 189    * @throws IOException
 190    */
 191   public void dump(DataOutputStream file) throws IOException
 192   {
 193     file.writeShort(constant_pool_count);
 194 
 195     for(int i=1; i < constant_pool_count; i++)
 196       if(constant_pool[i] != null)
 197         constant_pool[i].dump(file);
 198   }
 199 
 200   /**
 201    * Get constant from constant pool.
 202    *
 203    * @param  index Index in constant pool
 204    * @return Constant value
 205    * @see    Constant
 206    */
 207   public Constant getConstant(int index) {
 208     if (index >= constant_pool.length || index < 0)
 209       throw new ClassFormatException("Invalid constant pool reference: " +
 210                                  index + ". Constant pool size is: " +
 211                                  constant_pool.length);
 212     return constant_pool[index];
 213   }
 214 
 215   /**
 216    * Get constant from constant pool and check whether it has the
 217    * expected type.
 218    *
 219    * @param  index Index in constant pool
 220    * @param  tag Tag of expected constant, i.e., its type
 221    * @return Constant value
 222    * @see    Constant
 223    * @throws  ClassFormatException
 224    */
 225   public Constant getConstant(int index, byte tag)
 226        throws ClassFormatException
 227   {
 228     Constant c;
 229 
 230     c = getConstant(index);
 231 
 232     if(c == null)
 233       throw new ClassFormatException("Constant pool at index " + index + " is null.");
 234 
 235     if(c.getTag() == tag)
 236       return c;
 237     else
 238       throw new ClassFormatException("Expected class `" + Constants.CONSTANT_NAMES[tag] +
 239                                  "' at index " + index + " and got " + c);
 240   }
 241 
 242   /**
 243    * @return Array of constants.
 244    * @see    Constant
 245    */
 246   public Constant[] getConstantPool() { return constant_pool;  }
 247   /**
 248    * Get string from constant pool and bypass the indirection of
 249    * `ConstantClass' and `ConstantString' objects. I.e. these classes have
 250    * an index field that points to another entry of the constant pool of
 251    * type `ConstantUtf8' which contains the real data.
 252    *
 253    * @param  index Index in constant pool
 254    * @param  tag Tag of expected constant, either ConstantClass or ConstantString
 255    * @return Contents of string reference
 256    * @see    ConstantClass
 257    * @see    ConstantString
 258    * @throws  ClassFormatException
 259    */
 260   public String getConstantString(int index, byte tag)
 261        throws ClassFormatException
 262   {
 263     Constant c;
 264     int    i;
 265 
 266     c = getConstant(index, tag);
 267 
 268     /* This switch() is not that elegant, since the two classes have the
 269      * same contents, they just differ in the name of the index
 270      * field variable.
 271      * But we want to stick to the JVM naming conventions closely though
 272      * we could have solved these more elegantly by using the same
 273      * variable name or by subclassing.
 274      */
 275     switch(tag) {
 276     case Constants.CONSTANT_Class:  i = ((ConstantClass)c).getNameIndex();    break;
 277     case Constants.CONSTANT_String: i = ((ConstantString)c).getStringIndex(); break;
 278     default:
 279       throw new RuntimeException("getConstantString called with illegal tag " + tag);
 280     }
 281 
 282     // Finally get the string from the constant pool
 283     c = getConstant(i, Constants.CONSTANT_Utf8);
 284     return ((ConstantUtf8)c).getBytes();
 285   }
 286   /**
 287    * @return Length of constant pool.
 288    */
 289   public int getLength()
 290   {
 291     return constant_pool_count;
 292   }
 293 
 294   /**
 295    * @param constant Constant to set
 296    */
 297   public void setConstant(int index, Constant constant) {
 298     constant_pool[index] = constant;
 299   }
 300 
 301   /**
 302    * @param constant_pool
 303    */
 304   public void setConstantPool(Constant[] constant_pool) {
 305     this.constant_pool = constant_pool;
 306     constant_pool_count = (constant_pool == null)? 0 : constant_pool.length;
 307   }
 308   /**
 309    * @return String representation.
 310    */
 311   public String toString() {
 312     StringBuffer buf = new StringBuffer();
 313 
 314     for(int i=1; i < constant_pool_count; i++)
 315       buf.append(i + ")" + constant_pool[i] + "\n");
 316 
 317     return buf.toString();
 318   }
 319 
 320   /**
 321    * @return deep copy of this constant pool
 322    */
 323   public ConstantPool copy() {
 324     ConstantPool c = null;
 325 
 326     try {
 327       c = (ConstantPool)clone();
 328     } catch(CloneNotSupportedException e) {}
 329 
 330     c.constant_pool = new Constant[constant_pool_count];
 331 
 332     for(int i=1; i < constant_pool_count; i++) {
 333       if(constant_pool[i] != null)
 334         c.constant_pool[i] = constant_pool[i].copy();
 335     }
 336 
 337     return c;
 338   }
 339 }