View Javadoc

1   /*
2    * Copyright  2000-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   *
16   */
17  package org.apache.bcel.classfile;
18  
19  import java.io.DataInputStream;
20  import java.io.DataOutputStream;
21  import java.io.IOException;
22  import java.io.Serializable;
23  import org.apache.bcel.Constants;
24  
25  /***
26   * This class represents the constant pool, i.e., a table of constants, of
27   * a parsed classfile. It may contain null references, due to the JVM
28   * specification that skips an entry after an 8-byte constant (double,
29   * long) entry.  Those interested in generating constant pools
30   * programatically should see <a href="../generic/ConstantPoolGen.html">
31   * ConstantPoolGen</a>.
32  
33   * @version $Id: ConstantPool.java 386056 2006-03-15 11:31:56Z tcurdt $
34   * @see     Constant
35   * @see     org.apache.bcel.generic.ConstantPoolGen
36   * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
37   */
38  public class ConstantPool implements Cloneable, Node, Serializable {
39  
40      private int constant_pool_count;
41      private Constant[] constant_pool;
42  
43  
44      /***
45       * @param constant_pool Array of constants
46       */
47      public ConstantPool(Constant[] constant_pool) {
48          setConstantPool(constant_pool);
49      }
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          byte tag;
61          constant_pool_count = file.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(file);
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 == Constants.CONSTANT_Double) || (tag == Constants.CONSTANT_Long)) {
77                  i++;
78              }
79          }
80      }
81  
82  
83      /***
84       * Called by objects that are traversing the nodes of the tree implicitely
85       * defined by the contents of a Java class. I.e., the hierarchy of methods,
86       * fields, attributes, etc. spawns a tree of objects.
87       *
88       * @param v Visitor object
89       */
90      public void accept( 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         byte tag = c.getTag();
105         switch (tag) {
106             case Constants.CONSTANT_Class:
107                 i = ((ConstantClass) c).getNameIndex();
108                 c = getConstant(i, Constants.CONSTANT_Utf8);
109                 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
110                 break;
111             case Constants.CONSTANT_String:
112                 i = ((ConstantString) c).getStringIndex();
113                 c = getConstant(i, Constants.CONSTANT_Utf8);
114                 str = "\"" + escape(((ConstantUtf8) c).getBytes()) + "\"";
115                 break;
116             case Constants.CONSTANT_Utf8:
117                 str = ((ConstantUtf8) c).getBytes();
118                 break;
119             case Constants.CONSTANT_Double:
120                 str = "" + ((ConstantDouble) c).getBytes();
121                 break;
122             case Constants.CONSTANT_Float:
123                 str = "" + ((ConstantFloat) c).getBytes();
124                 break;
125             case Constants.CONSTANT_Long:
126                 str = "" + ((ConstantLong) c).getBytes();
127                 break;
128             case Constants.CONSTANT_Integer:
129                 str = "" + ((ConstantInteger) c).getBytes();
130                 break;
131             case Constants.CONSTANT_NameAndType:
132                 str = (constantToString(((ConstantNameAndType) c).getNameIndex(),
133                         Constants.CONSTANT_Utf8)
134                         + " " + constantToString(((ConstantNameAndType) c).getSignatureIndex(),
135                         Constants.CONSTANT_Utf8));
136                 break;
137             case Constants.CONSTANT_InterfaceMethodref:
138             case Constants.CONSTANT_Methodref:
139             case Constants.CONSTANT_Fieldref:
140                 str = (constantToString(((ConstantCP) c).getClassIndex(), Constants.CONSTANT_Class)
141                         + "." + constantToString(((ConstantCP) c).getNameAndTypeIndex(),
142                         Constants.CONSTANT_NameAndType));
143                 break;
144             default: // Never reached
145                 throw new RuntimeException("Unknown constant type " + tag);
146         }
147         return str;
148     }
149 
150 
151     private static final String escape( String str ) {
152         int len = str.length();
153         StringBuffer buf = new StringBuffer(len + 5);
154         char[] ch = str.toCharArray();
155         for (int i = 0; i < len; i++) {
156             switch (ch[i]) {
157                 case '\n':
158                     buf.append("//n");
159                     break;
160                 case '\r':
161                     buf.append("//r");
162                     break;
163                 case '\t':
164                     buf.append("//t");
165                     break;
166                 case '\b':
167                     buf.append("//b");
168                     break;
169                 case '"':
170                     buf.append("//\"");
171                     break;
172                 default:
173                     buf.append(ch[i]);
174             }
175         }
176         return buf.toString();
177     }
178 
179 
180     /***
181      * Retrieve constant at `index' from constant pool and resolve it to
182      * a string representation.
183      *
184      * @param  index of constant in constant pool
185      * @param  tag expected type
186      * @return String representation
187      */
188     public String constantToString( int index, byte tag ) throws ClassFormatException {
189         Constant c = getConstant(index, tag);
190         return constantToString(c);
191     }
192 
193 
194     /*** 
195      * Dump constant pool to file stream in binary format.
196      *
197      * @param file Output file stream
198      * @throws IOException
199      */
200     public void dump( DataOutputStream file ) throws IOException {
201         file.writeShort(constant_pool_count);
202         for (int i = 1; i < constant_pool_count; i++) {
203             if (constant_pool[i] != null) {
204                 constant_pool[i].dump(file);
205             }
206         }
207     }
208 
209 
210     /***
211      * Get constant from constant pool.
212      *
213      * @param  index Index in constant pool
214      * @return Constant value
215      * @see    Constant
216      */
217     public Constant getConstant( int index ) {
218         if (index >= constant_pool.length || index < 0) {
219             throw new ClassFormatException("Invalid constant pool reference: " + index
220                     + ". Constant pool size is: " + constant_pool.length);
221         }
222         return constant_pool[index];
223     }
224 
225 
226     /***
227      * Get constant from constant pool and check whether it has the
228      * expected type.
229      *
230      * @param  index Index in constant pool
231      * @param  tag Tag of expected constant, i.e., its type
232      * @return Constant value
233      * @see    Constant
234      * @throws  ClassFormatException
235      */
236     public Constant getConstant( int index, byte tag ) throws ClassFormatException {
237         Constant c;
238         c = getConstant(index);
239         if (c == null) {
240             throw new ClassFormatException("Constant pool at index " + index + " is null.");
241         }
242         if (c.getTag() != tag) {
243             throw new ClassFormatException("Expected class `" + Constants.CONSTANT_NAMES[tag]
244                     + "' at index " + index + " and got " + c);
245         }
246         return c;
247     }
248 
249 
250     /***
251      * @return Array of constants.
252      * @see    Constant
253      */
254     public Constant[] getConstantPool() {
255         return constant_pool;
256     }
257 
258 
259     /***
260      * Get string from constant pool and bypass the indirection of 
261      * `ConstantClass' and `ConstantString' objects. I.e. these classes have
262      * an index field that points to another entry of the constant pool of
263      * type `ConstantUtf8' which contains the real data.
264      *
265      * @param  index Index in constant pool
266      * @param  tag Tag of expected constant, either ConstantClass or ConstantString
267      * @return Contents of string reference
268      * @see    ConstantClass
269      * @see    ConstantString
270      * @throws  ClassFormatException
271      */
272     public String getConstantString( int index, byte tag ) throws ClassFormatException {
273         Constant c;
274         int i;
275         c = getConstant(index, tag);
276         /* This switch() is not that elegant, since the two classes have the
277          * same contents, they just differ in the name of the index
278          * field variable.
279          * But we want to stick to the JVM naming conventions closely though
280          * we could have solved these more elegantly by using the same
281          * variable name or by subclassing.
282          */
283         switch (tag) {
284             case Constants.CONSTANT_Class:
285                 i = ((ConstantClass) c).getNameIndex();
286                 break;
287             case Constants.CONSTANT_String:
288                 i = ((ConstantString) c).getStringIndex();
289                 break;
290             default:
291                 throw new RuntimeException("getConstantString called with illegal tag " + tag);
292         }
293         // Finally get the string from the constant pool
294         c = getConstant(i, Constants.CONSTANT_Utf8);
295         return ((ConstantUtf8) c).getBytes();
296     }
297 
298 
299     /***
300      * @return Length of constant pool.
301      */
302     public int getLength() {
303         return constant_pool_count;
304     }
305 
306 
307     /***
308      * @param constant Constant to set
309      */
310     public void setConstant( int index, Constant constant ) {
311         constant_pool[index] = constant;
312     }
313 
314 
315     /***
316      * @param constant_pool
317      */
318     public void setConstantPool( Constant[] constant_pool ) {
319         this.constant_pool = constant_pool;
320         constant_pool_count = (constant_pool == null) ? 0 : constant_pool.length;
321     }
322 
323 
324     /***
325      * @return String representation.
326      */
327     public String toString() {
328         StringBuffer buf = new StringBuffer();
329         for (int i = 1; i < constant_pool_count; i++) {
330             buf.append(i).append(")").append(constant_pool[i]).append("\n");
331         }
332         return buf.toString();
333     }
334 
335 
336     /***
337      * @return deep copy of this constant pool
338      */
339     public ConstantPool copy() {
340         ConstantPool c = null;
341         try {
342             c = (ConstantPool) clone();
343             c.constant_pool = new Constant[constant_pool_count];
344             for (int i = 1; i < constant_pool_count; i++) {
345                 if (constant_pool[i] != null) {
346                     c.constant_pool[i] = constant_pool[i].copy();
347                 }
348             }
349         } catch (CloneNotSupportedException e) {
350         }
351         return c;
352     }
353 }