View Javadoc
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  package org.apache.bcel.data;
18  
19  /*
20   * This file is a modified copy of org.apache.bcel.classfile.ConstantPool.java.
21   * It was chosen as it generates both tableswitch and lookupswitch byte codes.
22   * It has been modified to compile stand alone within the BCEL test environment.
23   * The code is not executed but the classfile is used as input to a BCEL test case.
24   */
25  
26  import java.io.DataOutputStream;
27  import java.io.IOException;
28  
29  import org.apache.bcel.Const;
30  import org.apache.bcel.classfile.ClassFormatException;
31  import org.apache.bcel.classfile.Constant;
32  import org.apache.bcel.classfile.ConstantCP;
33  import org.apache.bcel.classfile.ConstantClass;
34  import org.apache.bcel.classfile.ConstantDouble;
35  import org.apache.bcel.classfile.ConstantFloat;
36  import org.apache.bcel.classfile.ConstantInteger;
37  import org.apache.bcel.classfile.ConstantInvokeDynamic;
38  import org.apache.bcel.classfile.ConstantLong;
39  import org.apache.bcel.classfile.ConstantMethodHandle;
40  import org.apache.bcel.classfile.ConstantMethodType;
41  import org.apache.bcel.classfile.ConstantModule;
42  import org.apache.bcel.classfile.ConstantNameAndType;
43  import org.apache.bcel.classfile.ConstantPackage;
44  import org.apache.bcel.classfile.ConstantString;
45  import org.apache.bcel.classfile.ConstantUtf8;
46  import org.apache.bcel.classfile.Node;
47  import org.apache.bcel.classfile.Utility;
48  
49  /**
50   * This class represents the constant pool, i.e., a table of constants, of a parsed classfile. It may contain null
51   * references, due to the JVM specification that skips an entry after an 8-byte constant (double, long) entry. Those
52   * interested in generating constant pools programatically should see <a href="../generic/ConstantPoolGen.html">
53   * ConstantPoolGen</a>.
54   *
55   * @see Constant
56   * @see org.apache.bcel.generic.ConstantPoolGen
57   */
58  public abstract class ConstantPoolX implements Cloneable, Node {
59  
60      private static String escape(final String str) {
61          final int len = str.length();
62          final StringBuilder buf = new StringBuilder(len + 5);
63          final char[] ch = str.toCharArray();
64          for (int i = 0; i < len; i++) {
65              switch (ch[i]) {
66              case '\n':
67                  buf.append("\\n");
68                  break;
69              case '\r':
70                  buf.append("\\r");
71                  break;
72              case '\t':
73                  buf.append("\\t");
74                  break;
75              case '\b':
76                  buf.append("\\b");
77                  break;
78              case '"':
79                  buf.append("\\\"");
80                  break;
81              default:
82                  buf.append(ch[i]);
83              }
84          }
85          return buf.toString();
86      }
87  
88      private Constant[] constantPool;
89  
90      /**
91       * Resolves constant to a string representation.
92       *
93       * @param c Constant to be printed
94       * @return String representation
95       */
96      public String constantToString(Constant c) throws ClassFormatException {
97          String str;
98          int i;
99          final byte tag = c.getTag();
100         switch (tag) {
101         case Const.CONSTANT_Class:
102             i = ((ConstantClass) c).getNameIndex();
103             c = getConstant(i, Const.CONSTANT_Utf8);
104             str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
105             break;
106         case Const.CONSTANT_String:
107             i = ((ConstantString) c).getStringIndex();
108             c = getConstant(i, Const.CONSTANT_Utf8);
109             str = "\"" + escape(((ConstantUtf8) c).getBytes()) + "\"";
110             break;
111         case Const.CONSTANT_Utf8:
112             str = ((ConstantUtf8) c).getBytes();
113             break;
114         case Const.CONSTANT_Double:
115             str = String.valueOf(((ConstantDouble) c).getBytes());
116             break;
117         case Const.CONSTANT_Float:
118             str = String.valueOf(((ConstantFloat) c).getBytes());
119             break;
120         case Const.CONSTANT_Long:
121             str = String.valueOf(((ConstantLong) c).getBytes());
122             break;
123         case Const.CONSTANT_Integer:
124             str = String.valueOf(((ConstantInteger) c).getBytes());
125             break;
126         case Const.CONSTANT_NameAndType:
127             str = constantToString(((ConstantNameAndType) c).getNameIndex(), Const.CONSTANT_Utf8) + " "
128                 + constantToString(((ConstantNameAndType) c).getSignatureIndex(), Const.CONSTANT_Utf8);
129             break;
130         case Const.CONSTANT_InterfaceMethodref:
131         case Const.CONSTANT_Methodref:
132         case Const.CONSTANT_Fieldref:
133             str = constantToString(((ConstantCP) c).getClassIndex(), Const.CONSTANT_Class) + "."
134                 + constantToString(((ConstantCP) c).getNameAndTypeIndex(), Const.CONSTANT_NameAndType);
135             break;
136         case Const.CONSTANT_MethodHandle:
137             // Note that the ReferenceIndex may point to a Fieldref, Methodref or
138             // InterfaceMethodref - so we need to peek ahead to get the actual type.
139             final ConstantMethodHandle cmh = (ConstantMethodHandle) c;
140             str = Const.getMethodHandleName(cmh.getReferenceKind()) + " "
141                 + constantToString(cmh.getReferenceIndex(), getConstant(cmh.getReferenceIndex()).getTag());
142             break;
143         case Const.CONSTANT_MethodType:
144             final ConstantMethodType cmt = (ConstantMethodType) c;
145             str = constantToString(cmt.getDescriptorIndex(), Const.CONSTANT_Utf8);
146             break;
147         case Const.CONSTANT_InvokeDynamic:
148             final ConstantInvokeDynamic cid = (ConstantInvokeDynamic) c;
149             str = cid.getBootstrapMethodAttrIndex() + ":" + constantToString(cid.getNameAndTypeIndex(), Const.CONSTANT_NameAndType);
150             break;
151         case Const.CONSTANT_Module:
152             i = ((ConstantModule) c).getNameIndex();
153             c = getConstant(i, Const.CONSTANT_Utf8);
154             str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
155             break;
156         case Const.CONSTANT_Package:
157             i = ((ConstantPackage) c).getNameIndex();
158             c = getConstant(i, Const.CONSTANT_Utf8);
159             str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
160             break;
161         default: // Never reached
162             throw new IllegalArgumentException("Unknown constant type " + tag);
163         }
164         return str;
165     }
166 
167     /**
168      * Retrieves constant at 'index' from constant pool and resolve it to a string representation.
169      *
170      * @param index of constant in constant pool
171      * @param tag expected type
172      * @return String representation
173      */
174     public String constantToString(final int index, final byte tag) throws ClassFormatException {
175         final Constant c = getConstant(index, tag);
176         return constantToString(c);
177     }
178 
179     /**
180      * Dump constant pool to file stream in binary format.
181      *
182      * @param file Output file stream
183      * @throws IOException
184      */
185     public void dump(final DataOutputStream file) throws IOException {
186         file.writeShort(constantPool.length);
187         for (int i = 1; i < constantPool.length; i++) {
188             if (constantPool[i] != null) {
189                 constantPool[i].dump(file);
190             }
191         }
192     }
193 
194     /**
195      * Gets constant from constant pool.
196      *
197      * @param index Index in constant pool
198      * @return Constant value
199      * @see Constant
200      */
201     public Constant getConstant(final int index) {
202         if (index >= constantPool.length || index < 0) {
203             throw new ClassFormatException("Invalid constant pool reference: " + index + ". Constant pool size is: " + constantPool.length);
204         }
205         return constantPool[index];
206     }
207 
208     /**
209      * Gets constant from constant pool and check whether it has the expected type.
210      *
211      * @param index Index in constant pool
212      * @param tag Tag of expected constant, i.e., its type
213      * @return Constant value
214      * @see Constant
215      * @throws ClassFormatException
216      */
217     public Constant getConstant(final int index, final byte tag) throws ClassFormatException {
218         Constant c;
219         c = getConstant(index);
220         if (c == null) {
221             throw new ClassFormatException("Constant pool at index " + index + " is null.");
222         }
223         if (c.getTag() != tag) {
224             throw new ClassFormatException("Expected class '" + Const.getConstantName(tag) + "' at index " + index + " and got " + c);
225         }
226         return c;
227     }
228 
229     /**
230      * @return Array of constants.
231      * @see Constant
232      */
233     public Constant[] getConstantPool() {
234         return constantPool;
235     }
236 
237     /**
238      * Gets string from constant pool and bypass the indirection of 'ConstantClass' and 'ConstantString' objects. I.e. these
239      * classes have an index field that points to another entry of the constant pool of type 'ConstantUtf8' which contains
240      * the real data.
241      *
242      * @param index Index in constant pool
243      * @param tag Tag of expected constant, either ConstantClass or ConstantString
244      * @return Contents of string reference
245      * @see ConstantClass
246      * @see ConstantString
247      * @throws ClassFormatException
248      */
249     public String getConstantString(final int index, final byte tag) throws ClassFormatException {
250         Constant c;
251         int i;
252         c = getConstant(index, tag);
253         /*
254          * This switch() is not that elegant, since the four classes have the same contents, they just differ in the name of the
255          * index field variable. But we want to stick to the JVM naming conventions closely though we could have solved these
256          * more elegantly by using the same variable name or by subclassing.
257          */
258         switch (tag) {
259         case Const.CONSTANT_Class:
260             i = ((ConstantClass) c).getNameIndex();
261             break;
262         case Const.CONSTANT_String:
263             i = ((ConstantString) c).getStringIndex();
264             break;
265         case Const.CONSTANT_Module:
266             i = ((ConstantModule) c).getNameIndex();
267             break;
268         case Const.CONSTANT_Package:
269             i = ((ConstantPackage) c).getNameIndex();
270             break;
271         default:
272             throw new IllegalArgumentException("getConstantString called with illegal tag " + tag);
273         }
274         // Finally get the string from the constant pool
275         c = getConstant(i, Const.CONSTANT_Utf8);
276         return ((ConstantUtf8) c).getBytes();
277     }
278 
279     /**
280      * @return Length of constant pool.
281      */
282     public int getLength() {
283         return constantPool == null ? 0 : constantPool.length;
284     }
285 
286     /**
287      * @param constant Constant to set
288      */
289     public void setConstant(final int index, final Constant constant) {
290         constantPool[index] = constant;
291     }
292 
293     /**
294      * @param constantPool
295      */
296     public void setConstantPool(final Constant[] constantPool) {
297         this.constantPool = constantPool;
298     }
299 
300     /**
301      * @return String representation.
302      */
303     @Override
304     public String toString() {
305         final StringBuilder buf = new StringBuilder();
306         for (int i = 1; i < constantPool.length; i++) {
307             buf.append(i).append(")").append(constantPool[i]).append("\n");
308         }
309         return buf.toString();
310     }
311 
312 }