001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.bcel.classfile; 020 021import java.io.DataInput; 022import java.io.DataOutputStream; 023import java.io.IOException; 024import java.util.Arrays; 025import java.util.Iterator; 026 027import org.apache.bcel.Const; 028 029/** 030 * This class represents the constant pool, that is, a table of constants, of a parsed classfile. It may contain null references, due to the JVM specification that 031 * skips an entry after an 8-byte constant (double, long) entry. Those interested in generating constant pools programmatically should see 032 * <a href="../generic/ConstantPoolGen.html"> ConstantPoolGen</a>. 033 * 034 * @see Constant 035 * @see org.apache.bcel.generic.ConstantPoolGen 036 */ 037public class ConstantPool implements Cloneable, Node, Iterable<Constant> { 038 039 private static String escape(final String str) { 040 final int len = str.length(); 041 final StringBuilder buf = new StringBuilder(len + 5); 042 final char[] ch = str.toCharArray(); 043 for (int i = 0; i < len; i++) { 044 switch (ch[i]) { 045 case '\n': 046 buf.append("\\n"); 047 break; 048 case '\r': 049 buf.append("\\r"); 050 break; 051 case '\t': 052 buf.append("\\t"); 053 break; 054 case '\b': 055 buf.append("\\b"); 056 break; 057 case '"': 058 buf.append("\\\""); 059 break; 060 default: 061 buf.append(ch[i]); 062 } 063 } 064 return buf.toString(); 065 } 066 067 private Constant[] constantPool; 068 069 /** 070 * Constructs a ConstantPool. 071 * 072 * @param constantPool Array of constants. 073 */ 074 public ConstantPool(final Constant... constantPool) { 075 setConstantPool(constantPool); 076 } 077 078 /** 079 * Reads constants from given input stream. 080 * 081 * @param input Input stream. 082 * @throws IOException if problem in readUnsignedShort or readConstant. 083 */ 084 public ConstantPool(final DataInput input) throws IOException { 085 byte tag; 086 final int constantPoolCount = input.readUnsignedShort(); 087 constantPool = new Constant[constantPoolCount]; 088 /* 089 * constantPool[0] is unused by the compiler and may be used freely by the implementation. 090 * constantPool[0] is currently unused by the implementation. 091 */ 092 for (int i = 1; i < constantPoolCount; i++) { 093 constantPool[i] = Constant.readConstant(input); 094 /* 095 * Quote from the JVM specification: "All eight byte constants take up two spots in the constant pool. If this is the n'th byte in the constant 096 * pool, then the next item will be numbered n+2" 097 * 098 * Thus we have to increment the index counter. 099 */ 100 tag = constantPool[i].getTag(); 101 if (tag == Const.CONSTANT_Double || tag == Const.CONSTANT_Long) { 102 i++; 103 } 104 } 105 } 106 107 /** 108 * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e., the hierarchy of methods, fields, 109 * attributes, etc. spawns a tree of objects. 110 * 111 * @param v Visitor object. 112 */ 113 @Override 114 public void accept(final Visitor v) { 115 v.visitConstantPool(this); 116 } 117 118 /** 119 * Resolves constant to a string representation. 120 * 121 * @param c Constant to be printed. 122 * @return String representation. 123 * @throws IllegalArgumentException if c is unknown constant type. 124 */ 125 public String constantToString(Constant c) throws IllegalArgumentException { 126 final String str; 127 final int i; 128 final byte tag = c.getTag(); 129 switch (tag) { 130 case Const.CONSTANT_Class: 131 i = ((ConstantClass) c).getNameIndex(); 132 c = getConstantUtf8(i); 133 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false); 134 break; 135 case Const.CONSTANT_String: 136 i = ((ConstantString) c).getStringIndex(); 137 c = getConstantUtf8(i); 138 str = "\"" + escape(((ConstantUtf8) c).getBytes()) + "\""; 139 break; 140 case Const.CONSTANT_Utf8: 141 str = ((ConstantUtf8) c).getBytes(); 142 break; 143 case Const.CONSTANT_Double: 144 str = String.valueOf(((ConstantDouble) c).getBytes()); 145 break; 146 case Const.CONSTANT_Float: 147 str = String.valueOf(((ConstantFloat) c).getBytes()); 148 break; 149 case Const.CONSTANT_Long: 150 str = String.valueOf(((ConstantLong) c).getBytes()); 151 break; 152 case Const.CONSTANT_Integer: 153 str = String.valueOf(((ConstantInteger) c).getBytes()); 154 break; 155 case Const.CONSTANT_NameAndType: 156 str = constantToString(((ConstantNameAndType) c).getNameIndex(), Const.CONSTANT_Utf8) + " " 157 + constantToString(((ConstantNameAndType) c).getSignatureIndex(), Const.CONSTANT_Utf8); 158 break; 159 case Const.CONSTANT_InterfaceMethodref: 160 case Const.CONSTANT_Methodref: 161 case Const.CONSTANT_Fieldref: 162 str = constantToString(((ConstantCP) c).getClassIndex(), Const.CONSTANT_Class) + "." 163 + constantToString(((ConstantCP) c).getNameAndTypeIndex(), Const.CONSTANT_NameAndType); 164 break; 165 case Const.CONSTANT_MethodHandle: 166 // Note that the ReferenceIndex may point to a Fieldref, Methodref or 167 // InterfaceMethodref - so we need to peek ahead to get the actual type. 168 final ConstantMethodHandle cmh = (ConstantMethodHandle) c; 169 str = Const.getMethodHandleName(cmh.getReferenceKind()) + " " 170 + constantToString(cmh.getReferenceIndex(), getConstant(cmh.getReferenceIndex()).getTag()); 171 break; 172 case Const.CONSTANT_MethodType: 173 final ConstantMethodType cmt = (ConstantMethodType) c; 174 str = constantToString(cmt.getDescriptorIndex(), Const.CONSTANT_Utf8); 175 break; 176 case Const.CONSTANT_InvokeDynamic: 177 final ConstantInvokeDynamic cid = (ConstantInvokeDynamic) c; 178 str = cid.getBootstrapMethodAttrIndex() + ":" + constantToString(cid.getNameAndTypeIndex(), Const.CONSTANT_NameAndType); 179 break; 180 case Const.CONSTANT_Dynamic: 181 final ConstantDynamic cd = (ConstantDynamic) c; 182 str = cd.getBootstrapMethodAttrIndex() + ":" + constantToString(cd.getNameAndTypeIndex(), Const.CONSTANT_NameAndType); 183 break; 184 case Const.CONSTANT_Module: 185 i = ((ConstantModule) c).getNameIndex(); 186 c = getConstantUtf8(i); 187 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false); 188 break; 189 case Const.CONSTANT_Package: 190 i = ((ConstantPackage) c).getNameIndex(); 191 c = getConstantUtf8(i); 192 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false); 193 break; 194 default: // Never reached 195 throw new IllegalArgumentException("Unknown constant type " + tag); 196 } 197 return str; 198 } 199 200 /** 201 * Retrieves constant at 'index' from constant pool and resolve it to a string representation. 202 * 203 * @param index of constant in constant pool. 204 * @param tag expected type. 205 * @return String representation. 206 */ 207 public String constantToString(final int index, final byte tag) { 208 return constantToString(getConstant(index, tag)); 209 } 210 211 /** 212 * Creates a deep copy of this constant pool. 213 * 214 * @return deep copy of this constant pool. 215 */ 216 public ConstantPool copy() { 217 ConstantPool c = null; 218 try { 219 c = (ConstantPool) clone(); 220 c.constantPool = new Constant[constantPool.length]; 221 for (int i = 1; i < constantPool.length; i++) { 222 if (constantPool[i] != null) { 223 c.constantPool[i] = constantPool[i].copy(); 224 } 225 } 226 } catch (final CloneNotSupportedException e) { 227 // TODO should this throw? 228 } 229 return c; 230 } 231 232 /** 233 * Dumps constant pool to file stream in binary format. 234 * 235 * @param file Output file stream. 236 * @throws IOException if problem in writeShort or dump 237 */ 238 public void dump(final DataOutputStream file) throws IOException { 239 /* 240 * Constants over the size of the constant pool shall not be written out. This is a redundant measure as the ConstantPoolGen should have already 241 * reported an error back in the situation. 242 */ 243 final int size = Math.min(constantPool.length, Const.MAX_CP_ENTRIES); 244 245 file.writeShort(size); 246 for (int i = 1; i < size; i++) { 247 if (constantPool[i] != null) { 248 constantPool[i].dump(file); 249 } 250 } 251 } 252 253 /** 254 * Gets constant from constant pool. 255 * 256 * @param <T> the type of the constant. 257 * @param index Index in constant pool. 258 * @return Constant value. 259 * @see Constant 260 * @throws ClassFormatException if index is invalid. 261 */ 262 @SuppressWarnings("unchecked") 263 public <T extends Constant> T getConstant(final int index) throws ClassFormatException { 264 return (T) getConstant(index, Constant.class); 265 } 266 267 /** 268 * Gets constant from constant pool and check whether it has the expected type. 269 * 270 * @param <T> the type of the constant. 271 * @param index Index in constant pool. 272 * @param tag Tag of expected constant, that is, its type. 273 * @return Constant value. 274 * @see Constant 275 * @throws ClassFormatException if constant type does not match tag. 276 */ 277 @SuppressWarnings("unchecked") 278 public <T extends Constant> T getConstant(final int index, final byte tag) throws ClassFormatException { 279 return (T) getConstant(index, tag, Constant.class); 280 } 281 282 /** 283 * Gets constant from constant pool and check whether it has the expected type. 284 * 285 * @param <T> the type of the constant. 286 * @param index Index in constant pool. 287 * @param tag Tag of expected constant, that is, its type. 288 * @param castTo the class to cast to. 289 * @return Constant value. 290 * @see Constant 291 * @throws ClassFormatException if constant type does not match tag. 292 * @since 6.6.0 293 */ 294 public <T extends Constant> T getConstant(final int index, final byte tag, final Class<T> castTo) throws ClassFormatException { 295 final T c = getConstant(index); 296 if (c == null || c.getTag() != tag) { 297 throw new ClassFormatException("Expected class '" + Const.getConstantName(tag) + "' at index " + index + " and got " + c); 298 } 299 return c; 300 } 301 302 /** 303 * Gets constant from constant pool. 304 * 305 * @param <T> A {@link Constant} subclass. 306 * @param index Index in constant pool. 307 * @param castTo The {@link Constant} subclass to cast to. 308 * @return Constant value. 309 * @throws ClassFormatException if index is invalid. 310 * @see Constant 311 * @since 6.6.0 312 */ 313 public <T extends Constant> T getConstant(final int index, final Class<T> castTo) throws ClassFormatException { 314 if (index >= constantPool.length || index < 1) { 315 throw new ClassFormatException("Invalid constant pool reference using index: " + index + ". Constant pool size is: " + constantPool.length); 316 } 317 if (constantPool[index] != null && !castTo.isAssignableFrom(constantPool[index].getClass())) { 318 throw new ClassFormatException("Invalid constant pool reference at index: " + index + 319 ". Expected " + castTo + " but was " + constantPool[index].getClass()); 320 } 321 if (index > 1) { 322 final Constant prev = constantPool[index - 1]; 323 if (prev != null && (prev.getTag() == Const.CONSTANT_Double || prev.getTag() == Const.CONSTANT_Long)) { 324 throw new ClassFormatException("Constant pool at index " + index + " is invalid. The index is unused due to the preceeding " 325 + Const.getConstantName(prev.getTag()) + "."); 326 } 327 } 328 // Previous check ensures this won't throw a ClassCastException 329 final T c = castTo.cast(constantPool[index]); 330 if (c == null) { 331 throw new ClassFormatException("Constant pool at index " + index + " is null."); 332 } 333 return c; 334 } 335 336 /** 337 * Gets constant from constant pool and check whether it has the expected type. 338 * 339 * @param index Index in constant pool. 340 * @return ConstantInteger value. 341 * @throws ClassFormatException if constant type does not match tag. 342 * @see ConstantInteger 343 */ 344 public ConstantInteger getConstantInteger(final int index) { 345 return getConstant(index, Const.CONSTANT_Integer, ConstantInteger.class); 346 } 347 348 /** 349 * Gets the array of constants. 350 * 351 * @return Array of constants. 352 * @see Constant 353 */ 354 public Constant[] getConstantPool() { 355 return constantPool; 356 } 357 358 /** 359 * Gets string from constant pool and bypass the indirection of 'ConstantClass' and 'ConstantString' objects. I.e. these classes have an index field that 360 * points to another entry of the constant pool of type 'ConstantUtf8' which contains the real data. 361 * 362 * @param index Index in constant pool. 363 * @param tag Tag of expected constant, either ConstantClass or ConstantString. 364 * @return Contents of string reference. 365 * @throws IllegalArgumentException if tag is invalid. 366 * @see ConstantClass 367 * @see ConstantString 368 */ 369 public String getConstantString(final int index, final byte tag) throws IllegalArgumentException { 370 final int i; 371 /* 372 * This switch() is not that elegant, since the four classes have the same contents, they just differ in the name of the index field variable. But we 373 * want to stick to the JVM naming conventions closely though we could have solved these more elegantly by using the same variable name or by 374 * subclassing. 375 */ 376 switch (tag) { 377 case Const.CONSTANT_Class: 378 i = getConstant(index, ConstantClass.class).getNameIndex(); 379 break; 380 case Const.CONSTANT_String: 381 i = getConstant(index, ConstantString.class).getStringIndex(); 382 break; 383 case Const.CONSTANT_Module: 384 i = getConstant(index, ConstantModule.class).getNameIndex(); 385 break; 386 case Const.CONSTANT_Package: 387 i = getConstant(index, ConstantPackage.class).getNameIndex(); 388 break; 389 case Const.CONSTANT_Utf8: 390 return getConstantUtf8(index).getBytes(); 391 default: 392 throw new IllegalArgumentException("getConstantString called with illegal tag " + tag); 393 } 394 // Finally get the string from the constant pool 395 return getConstantUtf8(i).getBytes(); 396 } 397 398 /** 399 * Gets constant from constant pool and check whether it has the expected type. 400 * 401 * @param index Index in constant pool. 402 * @return ConstantUtf8 value. 403 * @throws ClassFormatException if constant type does not match tag. 404 * @see ConstantUtf8 405 */ 406 public ConstantUtf8 getConstantUtf8(final int index) throws ClassFormatException { 407 return getConstant(index, Const.CONSTANT_Utf8, ConstantUtf8.class); 408 } 409 410 /** 411 * Gets the length of constant pool. 412 * 413 * @return Length of constant pool. 414 */ 415 public int getLength() { 416 return constantPool.length; 417 } 418 419 @Override 420 public Iterator<Constant> iterator() { 421 return Arrays.stream(constantPool).iterator(); 422 } 423 424 /** 425 * Sets a constant at the specified index. 426 * 427 * @param index the index in the constant pool. 428 * @param constant Constant to set. 429 */ 430 public void setConstant(final int index, final Constant constant) { 431 constantPool[index] = constant; 432 } 433 434 /** 435 * Sets the constant pool. 436 * 437 * @param constantPool the constant pool array. 438 */ 439 public void setConstantPool(final Constant[] constantPool) { 440 this.constantPool = constantPool != null ? constantPool : Constant.EMPTY_ARRAY; 441 } 442 443 /** 444 * @return String representation. 445 */ 446 @Override 447 public String toString() { 448 final StringBuilder buf = new StringBuilder(); 449 for (int i = 1; i < constantPool.length; i++) { 450 buf.append(i).append(")").append(constantPool[i]).append("\n"); 451 } 452 return buf.toString(); 453 } 454}