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