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