View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.bcel.data;
20  
21  /*
22   * This file is a modified copy of org.apache.bcel.classfile.ConstantPool.java.
23   * It was chosen as it generates both tableswitch and lookupswitch byte codes.
24   * It has been modified to compile stand alone within the BCEL test environment.
25   * The code is not executed but the classfile is used as input to a BCEL test case.
26   */
27  
28  import java.io.DataOutputStream;
29  import java.io.IOException;
30  
31  import org.apache.bcel.Const;
32  import org.apache.bcel.classfile.ClassFormatException;
33  import org.apache.bcel.classfile.Constant;
34  import org.apache.bcel.classfile.ConstantCP;
35  import org.apache.bcel.classfile.ConstantClass;
36  import org.apache.bcel.classfile.ConstantDouble;
37  import org.apache.bcel.classfile.ConstantFloat;
38  import org.apache.bcel.classfile.ConstantInteger;
39  import org.apache.bcel.classfile.ConstantInvokeDynamic;
40  import org.apache.bcel.classfile.ConstantLong;
41  import org.apache.bcel.classfile.ConstantMethodHandle;
42  import org.apache.bcel.classfile.ConstantMethodType;
43  import org.apache.bcel.classfile.ConstantModule;
44  import org.apache.bcel.classfile.ConstantNameAndType;
45  import org.apache.bcel.classfile.ConstantPackage;
46  import org.apache.bcel.classfile.ConstantString;
47  import org.apache.bcel.classfile.ConstantUtf8;
48  import org.apache.bcel.classfile.Node;
49  import org.apache.bcel.classfile.Utility;
50  
51  /**
52   * This class represents the constant pool, that is, a table of constants, of a parsed classfile. It may contain null
53   * references, due to the JVM specification that skips an entry after an 8-byte constant (double, long) entry. Those
54   * interested in generating constant pools programatically should see <a href="../generic/ConstantPoolGen.html">
55   * ConstantPoolGen</a>.
56   *
57   * @see Constant
58   * @see org.apache.bcel.generic.ConstantPoolGen
59   */
60  public abstract class ConstantPoolX implements Cloneable, Node {
61  
62      private static String escape(final String str) {
63          final int len = str.length();
64          final StringBuilder buf = new StringBuilder(len + 5);
65          final char[] ch = str.toCharArray();
66          for (int i = 0; i < len; i++) {
67              switch (ch[i]) {
68              case '\n':
69                  buf.append("\\n");
70                  break;
71              case '\r':
72                  buf.append("\\r");
73                  break;
74              case '\t':
75                  buf.append("\\t");
76                  break;
77              case '\b':
78                  buf.append("\\b");
79                  break;
80              case '"':
81                  buf.append("\\\"");
82                  break;
83              default:
84                  buf.append(ch[i]);
85              }
86          }
87          return buf.toString();
88      }
89  
90      private Constant[] constantPool;
91  
92      /**
93       * Resolves constant to a string representation.
94       *
95       * @param c Constant to be printed.
96       * @return String representation.
97       */
98      public String constantToString(Constant c) throws ClassFormatException {
99          final String str;
100         final int i;
101         final byte tag = c.getTag();
102         switch (tag) {
103         case Const.CONSTANT_Class:
104             i = ((ConstantClass) c).getNameIndex();
105             c = getConstant(i, Const.CONSTANT_Utf8);
106             str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
107             break;
108         case Const.CONSTANT_String:
109             i = ((ConstantString) c).getStringIndex();
110             c = getConstant(i, Const.CONSTANT_Utf8);
111             str = "\"" + escape(((ConstantUtf8) c).getBytes()) + "\"";
112             break;
113         case Const.CONSTANT_Utf8:
114             str = ((ConstantUtf8) c).getBytes();
115             break;
116         case Const.CONSTANT_Double:
117             str = String.valueOf(((ConstantDouble) c).getBytes());
118             break;
119         case Const.CONSTANT_Float:
120             str = String.valueOf(((ConstantFloat) c).getBytes());
121             break;
122         case Const.CONSTANT_Long:
123             str = String.valueOf(((ConstantLong) c).getBytes());
124             break;
125         case Const.CONSTANT_Integer:
126             str = String.valueOf(((ConstantInteger) c).getBytes());
127             break;
128         case Const.CONSTANT_NameAndType:
129             str = constantToString(((ConstantNameAndType) c).getNameIndex(), Const.CONSTANT_Utf8) + " "
130                 + constantToString(((ConstantNameAndType) c).getSignatureIndex(), Const.CONSTANT_Utf8);
131             break;
132         case Const.CONSTANT_InterfaceMethodref:
133         case Const.CONSTANT_Methodref:
134         case Const.CONSTANT_Fieldref:
135             str = constantToString(((ConstantCP) c).getClassIndex(), Const.CONSTANT_Class) + "."
136                 + constantToString(((ConstantCP) c).getNameAndTypeIndex(), Const.CONSTANT_NameAndType);
137             break;
138         case Const.CONSTANT_MethodHandle:
139             // Note that the ReferenceIndex may point to a Fieldref, Methodref or
140             // InterfaceMethodref - so we need to peek ahead to get the actual type.
141             final ConstantMethodHandle cmh = (ConstantMethodHandle) c;
142             str = Const.getMethodHandleName(cmh.getReferenceKind()) + " "
143                 + constantToString(cmh.getReferenceIndex(), getConstant(cmh.getReferenceIndex()).getTag());
144             break;
145         case Const.CONSTANT_MethodType:
146             final ConstantMethodType cmt = (ConstantMethodType) c;
147             str = constantToString(cmt.getDescriptorIndex(), Const.CONSTANT_Utf8);
148             break;
149         case Const.CONSTANT_InvokeDynamic:
150             final ConstantInvokeDynamic cid = (ConstantInvokeDynamic) c;
151             str = cid.getBootstrapMethodAttrIndex() + ":" + constantToString(cid.getNameAndTypeIndex(), Const.CONSTANT_NameAndType);
152             break;
153         case Const.CONSTANT_Module:
154             i = ((ConstantModule) c).getNameIndex();
155             c = getConstant(i, Const.CONSTANT_Utf8);
156             str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
157             break;
158         case Const.CONSTANT_Package:
159             i = ((ConstantPackage) c).getNameIndex();
160             c = getConstant(i, Const.CONSTANT_Utf8);
161             str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
162             break;
163         default: // Never reached
164             throw new IllegalArgumentException("Unknown constant type " + tag);
165         }
166         return str;
167     }
168 
169     /**
170      * Retrieves constant at 'index' from constant pool and resolve it to a string representation.
171      *
172      * @param index of constant in constant pool.
173      * @param tag expected type.
174      * @return String representation.
175      */
176     public String constantToString(final int index, final byte tag) throws ClassFormatException {
177         final Constant c = getConstant(index, tag);
178         return constantToString(c);
179     }
180 
181     /**
182      * Dumps constant pool to file stream in binary format.
183      *
184      * @param file Output file stream.
185      * @throws IOException if an I/O error occurs.
186      */
187     public void dump(final DataOutputStream file) throws IOException {
188         file.writeShort(constantPool.length);
189         for (int i = 1; i < constantPool.length; i++) {
190             if (constantPool[i] != null) {
191                 constantPool[i].dump(file);
192             }
193         }
194     }
195 
196     /**
197      * Gets constant from constant pool.
198      *
199      * @param index Index in constant pool.
200      * @return Constant value.
201      * @see Constant
202      */
203     public Constant getConstant(final int index) {
204         if (index >= constantPool.length || index < 0) {
205             throw new ClassFormatException("Invalid constant pool reference: " + index + ". Constant pool size is: " + constantPool.length);
206         }
207         return constantPool[index];
208     }
209 
210     /**
211      * Gets constant from constant pool and check whether it has the expected type.
212      *
213      * @param index Index in constant pool.
214      * @param tag Tag of expected constant, that is, its type.
215      * @return Constant value.
216      * @see Constant
217      * @throws ClassFormatException
218      */
219     public Constant getConstant(final int index, final byte tag) throws ClassFormatException {
220         final Constant c = getConstant(index);
221         if (c == null) {
222             throw new ClassFormatException("Constant pool at index " + index + " is null.");
223         }
224         if (c.getTag() != tag) {
225             throw new ClassFormatException("Expected class '" + Const.getConstantName(tag) + "' at index " + index + " and got " + c);
226         }
227         return c;
228     }
229 
230     /**
231      * @return Array of constants.
232      * @see Constant
233      */
234     public Constant[] getConstantPool() {
235         return constantPool;
236     }
237 
238     /**
239      * Gets string from constant pool and bypass the indirection of 'ConstantClass' and 'ConstantString' objects. I.e. these
240      * classes have an index field that points to another entry of the constant pool of type 'ConstantUtf8' which contains
241      * the real data.
242      *
243      * @param index Index in constant pool.
244      * @param tag Tag of expected constant, either ConstantClass or ConstantString.
245      * @return Contents of string reference.
246      * @see ConstantClass
247      * @see ConstantString
248      * @throws ClassFormatException
249      */
250     public String getConstantString(final int index, final byte tag) throws ClassFormatException {
251         final int i;
252         Constant 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 }