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 * @see Constant 39 * @see com.sun.org.apache.bcel.internal.generic.ConstantPoolGen 40 */ 41 public class ConstantPool implements Cloneable, Node { 42 43 private Constant[] constant_pool; 44 45 /** 46 * @param constant_pool Array of constants 47 */ 48 public ConstantPool(final Constant[] constant_pool) { 49 this.constant_pool = constant_pool; 50 } 51 52 /** 53 * Reads constants from given input stream. 54 * 55 * @param input Input stream 56 * @throws IOException 57 * @throws ClassFormatException 58 */ 59 public ConstantPool(final DataInput input) throws IOException, ClassFormatException { 60 byte tag; 61 final int constant_pool_count = input.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(input); 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 == Const.CONSTANT_Double) || (tag == Const.CONSTANT_Long)) { 77 i++; 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 * Resolves constant to a string representation. 96 * 97 * @param c Constant to be printed 98 * @return String representation 99 */ 100 public String constantToString( Constant c ) throws ClassFormatException { 101 String str; 102 int i; 103 final byte tag = c.getTag(); 104 switch (tag) { 105 case Const.CONSTANT_Class: 106 i = ((ConstantClass) c).getNameIndex(); 107 c = getConstant(i, Const.CONSTANT_Utf8); 108 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false); 109 break; 110 case Const.CONSTANT_String: 111 i = ((ConstantString) c).getStringIndex(); 112 c = getConstant(i, Const.CONSTANT_Utf8); 113 str = "\"" + escape(((ConstantUtf8) c).getBytes()) + "\""; 114 break; 115 case Const.CONSTANT_Utf8: 116 str = ((ConstantUtf8) c).getBytes(); 117 break; 118 case Const.CONSTANT_Double: 119 str = String.valueOf(((ConstantDouble) c).getBytes()); 120 break; 121 case Const.CONSTANT_Float: 122 str = String.valueOf(((ConstantFloat) c).getBytes()); 123 break; 124 case Const.CONSTANT_Long: 125 str = String.valueOf(((ConstantLong) c).getBytes()); 126 break; 127 case Const.CONSTANT_Integer: 128 str = String.valueOf(((ConstantInteger) c).getBytes()); 129 break; 130 case Const.CONSTANT_NameAndType: 131 str = constantToString(((ConstantNameAndType) c).getNameIndex(), 132 Const.CONSTANT_Utf8) 133 + " " + constantToString(((ConstantNameAndType) c).getSignatureIndex(), 134 Const.CONSTANT_Utf8); 135 break; 136 case Const.CONSTANT_InterfaceMethodref: 137 case Const.CONSTANT_Methodref: 138 case Const.CONSTANT_Fieldref: 139 str = constantToString(((ConstantCP) c).getClassIndex(), Const.CONSTANT_Class) 140 + "." + constantToString(((ConstantCP) c).getNameAndTypeIndex(), 141 Const.CONSTANT_NameAndType); 142 break; 143 case Const.CONSTANT_MethodHandle: 144 // Note that the ReferenceIndex may point to a Fieldref, Methodref or 145 // InterfaceMethodref - so we need to peek ahead to get the actual type. 146 final ConstantMethodHandle cmh = (ConstantMethodHandle) c; 147 str = Const.getMethodHandleName(cmh.getReferenceKind()) 148 + " " + constantToString(cmh.getReferenceIndex(), 149 getConstant(cmh.getReferenceIndex()).getTag()); 150 break; 151 case Const.CONSTANT_MethodType: 152 final ConstantMethodType cmt = (ConstantMethodType) c; 153 str = constantToString(cmt.getDescriptorIndex(), Const.CONSTANT_Utf8); 154 break; 155 case Const.CONSTANT_InvokeDynamic: 156 final ConstantInvokeDynamic cid = (ConstantInvokeDynamic) c; 157 str = cid.getBootstrapMethodAttrIndex() 158 + ":" + constantToString(cid.getNameAndTypeIndex(), 159 Const.CONSTANT_NameAndType); 160 break; 161 case Const.CONSTANT_Module: 162 i = ((ConstantModule) c).getNameIndex(); 163 c = getConstant(i, Const.CONSTANT_Utf8); 164 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false); 165 break; 166 case Const.CONSTANT_Package: 167 i = ((ConstantPackage) c).getNameIndex(); 168 c = getConstant(i, Const.CONSTANT_Utf8); 169 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false); 170 break; 171 default: // Never reached 172 throw new RuntimeException("Unknown constant type " + tag); 173 } 174 return str; 175 } 176 177 private static String escape( final String str ) { 178 final int len = str.length(); 179 final StringBuilder buf = new StringBuilder(len + 5); 180 final char[] ch = str.toCharArray(); 181 for (int i = 0; i < len; i++) { 182 switch (ch[i]) { 183 case '\n': 184 buf.append("\\n"); 185 break; 186 case '\r': 187 buf.append("\\r"); 188 break; 189 case '\t': 190 buf.append("\\t"); 191 break; 192 case '\b': 193 buf.append("\\b"); 194 break; 195 case '"': 196 buf.append("\\\""); 197 break; 198 default: 199 buf.append(ch[i]); 200 } 201 } 202 return buf.toString(); 203 } 204 205 /** 206 * Retrieves constant at `index' from constant pool and resolve it to 207 * a string representation. 208 * 209 * @param index of constant in constant pool 210 * @param tag expected type 211 * @return String representation 212 */ 213 public String constantToString( final int index, final byte tag ) throws ClassFormatException { 214 final Constant c = getConstant(index, tag); 215 return constantToString(c); 216 } 217 218 /** 219 * Dump constant pool to file stream in binary format. 220 * 221 * @param file Output file stream 222 * @throws IOException 223 */ 224 public void dump( final DataOutputStream file ) throws IOException { 225 file.writeShort(constant_pool.length); 226 for (int i = 1; i < constant_pool.length; i++) { 227 if (constant_pool[i] != null) { 228 constant_pool[i].dump(file); 229 } 230 } 231 } 232 233 /** 234 * Gets constant from constant pool. 235 * 236 * @param index Index in constant pool 237 * @return Constant value 238 * @see Constant 239 */ 240 public Constant getConstant( final int index ) { 241 if (index >= constant_pool.length || index < 0) { 242 throw new ClassFormatException("Invalid constant pool reference: " + index 243 + ". Constant pool size is: " + constant_pool.length); 244 } 245 return constant_pool[index]; 246 } 247 248 /** 249 * Gets 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 * @return Array of constants. 273 * @see Constant 274 */ 275 public Constant[] getConstantPool() { 276 return constant_pool; 277 } 278 279 /** 280 * Gets string from constant pool and bypass the indirection of 281 * `ConstantClass' and `ConstantString' objects. I.e. these classes have 282 * an index field that points to another entry of the constant pool of 283 * type `ConstantUtf8' which contains the real data. 284 * 285 * @param index Index in constant pool 286 * @param tag Tag of expected constant, either ConstantClass or ConstantString 287 * @return Contents of string reference 288 * @see ConstantClass 289 * @see ConstantString 290 * @throws ClassFormatException 291 */ 292 public String getConstantString( final int index, final byte tag ) throws ClassFormatException { 293 Constant c; 294 int i; 295 c = getConstant(index, tag); 296 /* This switch() is not that elegant, since the four classes have the 297 * same contents, they just differ in the name of the index 298 * field variable. 299 * But we want to stick to the JVM naming conventions closely though 300 * we could have solved these more elegantly by using the same 301 * variable name or by subclassing. 302 */ 303 switch (tag) { 304 case Const.CONSTANT_Class: 305 i = ((ConstantClass) c).getNameIndex(); 306 break; 307 case Const.CONSTANT_String: 308 i = ((ConstantString) c).getStringIndex(); 309 break; 310 case Const.CONSTANT_Module: 311 i = ((ConstantModule) c).getNameIndex(); 312 break; 313 case Const.CONSTANT_Package: 314 i = ((ConstantPackage) c).getNameIndex(); 315 break; 316 default: 317 throw new RuntimeException("getConstantString called with illegal tag " + tag); 318 } 319 // Finally get the string from the constant pool 320 c = getConstant(i, Const.CONSTANT_Utf8); 321 return ((ConstantUtf8) c).getBytes(); 322 } 323 324 325 /** 326 * @return Length of constant pool. 327 */ 328 public int getLength() { 329 return constant_pool == null ? 0 : constant_pool.length; 330 } 331 332 333 /** 334 * @param constant Constant to set 335 */ 336 public void setConstant( final int index, final Constant constant ) { 337 constant_pool[index] = constant; 338 } 339 340 341 /** 342 * @param constant_pool 343 */ 344 public void setConstantPool( final Constant[] constant_pool ) { 345 this.constant_pool = constant_pool; 346 } 347 348 349 /** 350 * @return String representation. 351 */ 352 @Override 353 public String toString() { 354 final StringBuilder buf = new StringBuilder(); 355 for (int i = 1; i < constant_pool.length; i++) { 356 buf.append(i).append(")").append(constant_pool[i]).append("\n"); 357 } 358 return buf.toString(); 359 } 360 361 362 /** 363 * @return deep copy of this constant pool 364 */ 365 public ConstantPool copy() { 366 ConstantPool c = null; 367 try { 368 c = (ConstantPool) clone(); 369 c.constant_pool = new Constant[constant_pool.length]; 370 for (int i = 1; i < constant_pool.length; i++) { 371 if (constant_pool[i] != null) { 372 c.constant_pool[i] = constant_pool[i].copy(); 373 } 374 } 375 } catch (final CloneNotSupportedException e) { 376 // TODO should this throw? 377 } 378 return c; 379 } 380 }