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 Thrown 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 Thrown 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 final byte referenceTag = getConstant(cmh.getReferenceIndex()).getTag(); 170 // JVMS 4.4.8: the reference_index of a CONSTANT_MethodHandle must point to a CONSTANT_Fieldref, 171 // CONSTANT_Methodref or CONSTANT_InterfaceMethodref entry. Anything else is malformed; in particular, a 172 // CONSTANT_MethodHandle referencing another CONSTANT_MethodHandle (such as itself) would otherwise make 173 // this method recurse without bound until a StackOverflowError. 174 if (referenceTag != Const.CONSTANT_Fieldref && referenceTag != Const.CONSTANT_Methodref && referenceTag != Const.CONSTANT_InterfaceMethodref) { 175 throw new ClassFormatException( 176 "Constant pool at index " + cmh.getReferenceIndex() + " has an invalid tag " + referenceTag + " for a CONSTANT_MethodHandle reference"); 177 } 178 str = Const.getMethodHandleName(cmh.getReferenceKind()) + " " + constantToString(cmh.getReferenceIndex(), referenceTag); 179 break; 180 case Const.CONSTANT_MethodType: 181 final ConstantMethodType cmt = (ConstantMethodType) c; 182 str = constantToString(cmt.getDescriptorIndex(), Const.CONSTANT_Utf8); 183 break; 184 case Const.CONSTANT_InvokeDynamic: 185 final ConstantInvokeDynamic cid = (ConstantInvokeDynamic) c; 186 str = cid.getBootstrapMethodAttrIndex() + ":" + constantToString(cid.getNameAndTypeIndex(), Const.CONSTANT_NameAndType); 187 break; 188 case Const.CONSTANT_Dynamic: 189 final ConstantDynamic cd = (ConstantDynamic) c; 190 str = cd.getBootstrapMethodAttrIndex() + ":" + constantToString(cd.getNameAndTypeIndex(), Const.CONSTANT_NameAndType); 191 break; 192 case Const.CONSTANT_Module: 193 i = ((ConstantModule) c).getNameIndex(); 194 c = getConstantUtf8(i); 195 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false); 196 break; 197 case Const.CONSTANT_Package: 198 i = ((ConstantPackage) c).getNameIndex(); 199 c = getConstantUtf8(i); 200 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false); 201 break; 202 default: // Never reached 203 throw new IllegalArgumentException("Unknown constant type " + tag); 204 } 205 return str; 206 } 207 208 /** 209 * Retrieves constant at 'index' from constant pool and resolve it to a string representation. 210 * 211 * @param index of constant in constant pool. 212 * @param tag expected type. 213 * @return String representation. 214 */ 215 public String constantToString(final int index, final byte tag) { 216 return constantToString(getConstant(index, tag)); 217 } 218 219 /** 220 * Creates a deep copy of this constant pool. 221 * 222 * @return deep copy of this constant pool. 223 */ 224 public ConstantPool copy() { 225 ConstantPool c = null; 226 try { 227 c = (ConstantPool) clone(); 228 c.constantPool = new Constant[constantPool.length]; 229 for (int i = 1; i < constantPool.length; i++) { 230 if (constantPool[i] != null) { 231 c.constantPool[i] = constantPool[i].copy(); 232 } 233 } 234 } catch (final CloneNotSupportedException e) { 235 // TODO should this throw? 236 } 237 return c; 238 } 239 240 /** 241 * Dumps constant pool to file stream in binary format. 242 * 243 * @param file Output file stream. 244 * @throws IOException Thrown if problem in writeShort or dump 245 */ 246 public void dump(final DataOutputStream file) throws IOException { 247 /* 248 * A constant pool larger than the u2 count field can represent must fail loudly instead of being silently 249 * truncated: class structures may still reference the dropped entries, and a wrapped count with extra bodies 250 * desynchronizes any consumer that reparses the emitted bytes (the CVE-2022-42920 writer-overflow shape). 251 */ 252 if (constantPool.length > Const.MAX_CP_ENTRIES) { 253 throw new ClassFormatException("Constant pool size " + constantPool.length + " exceeds the u2 maximum of " + Const.MAX_CP_ENTRIES); 254 } 255 file.writeShort(constantPool.length); 256 for (int i = 1; i < constantPool.length; i++) { 257 if (constantPool[i] != null) { 258 constantPool[i].dump(file); 259 } 260 } 261 } 262 263 /** 264 * Gets constant from constant pool. 265 * 266 * @param <T> The type of the constant. 267 * @param index Index in constant pool. 268 * @return Constant value. 269 * @see Constant 270 * @throws ClassFormatException Thrown if index is invalid. 271 */ 272 @SuppressWarnings("unchecked") 273 public <T extends Constant> T getConstant(final int index) throws ClassFormatException { 274 return (T) getConstant(index, Constant.class); 275 } 276 277 /** 278 * Gets constant from constant pool and check whether it has the expected type. 279 * 280 * @param <T> The type of the constant. 281 * @param index Index in constant pool. 282 * @param tag Tag of expected constant, that is, its type. 283 * @return Constant value. 284 * @see Constant 285 * @throws ClassFormatException Thrown if constant type does not match tag. 286 */ 287 @SuppressWarnings("unchecked") 288 public <T extends Constant> T getConstant(final int index, final byte tag) throws ClassFormatException { 289 return (T) getConstant(index, tag, Constant.class); 290 } 291 292 /** 293 * Gets constant from constant pool and check whether it has the expected type. 294 * 295 * @param <T> The type of the constant. 296 * @param index Index in constant pool. 297 * @param tag Tag of expected constant, that is, its type. 298 * @param castTo The class to cast to. 299 * @return Constant value. 300 * @see Constant 301 * @throws ClassFormatException Thrown if constant type does not match tag. 302 * @since 6.6.0 303 */ 304 public <T extends Constant> T getConstant(final int index, final byte tag, final Class<T> castTo) throws ClassFormatException { 305 final T c = getConstant(index); 306 if (c == null || c.getTag() != tag) { 307 throw new ClassFormatException("Expected class '" + Const.getConstantName(tag) + "' at index " + index + " and got " + c); 308 } 309 return c; 310 } 311 312 /** 313 * Gets constant from constant pool. 314 * 315 * @param <T> A {@link Constant} subclass. 316 * @param index Index in constant pool. 317 * @param castTo The {@link Constant} subclass to cast to. 318 * @return Constant value. 319 * @throws ClassFormatException Thrown if index is invalid. 320 * @see Constant 321 * @since 6.6.0 322 */ 323 public <T extends Constant> T getConstant(final int index, final Class<T> castTo) throws ClassFormatException { 324 if (index >= constantPool.length || index < 1) { 325 throw new ClassFormatException("Invalid constant pool reference using index: " + index + ". Constant pool size is: " + constantPool.length); 326 } 327 if (constantPool[index] != null && !castTo.isAssignableFrom(constantPool[index].getClass())) { 328 throw new ClassFormatException("Invalid constant pool reference at index: " + index + 329 ". Expected " + castTo + " but was " + constantPool[index].getClass()); 330 } 331 if (index > 1) { 332 final Constant prev = constantPool[index - 1]; 333 if (prev != null && (prev.getTag() == Const.CONSTANT_Double || prev.getTag() == Const.CONSTANT_Long)) { 334 throw new ClassFormatException("Constant pool at index " + index + " is invalid. The index is unused due to the preceeding " 335 + Const.getConstantName(prev.getTag()) + "."); 336 } 337 } 338 // Previous check ensures this won't throw a ClassCastException 339 final T c = castTo.cast(constantPool[index]); 340 if (c == null) { 341 throw new ClassFormatException("Constant pool at index " + index + " is null."); 342 } 343 return c; 344 } 345 346 /** 347 * Gets constant from constant pool and check whether it has the expected type. 348 * 349 * @param index Index in constant pool. 350 * @return ConstantInteger value. 351 * @throws ClassFormatException Thrown if constant type does not match tag. 352 * @see ConstantInteger 353 */ 354 public ConstantInteger getConstantInteger(final int index) { 355 return getConstant(index, Const.CONSTANT_Integer, ConstantInteger.class); 356 } 357 358 /** 359 * Gets the array of constants. 360 * 361 * @return Array of constants. 362 * @see Constant 363 */ 364 public Constant[] getConstantPool() { 365 return constantPool; 366 } 367 368 /** 369 * Gets string from constant pool and bypass the indirection of 'ConstantClass' and 'ConstantString' objects. I.e. these classes have an index field that 370 * points to another entry of the constant pool of type 'ConstantUtf8' which contains the real data. 371 * 372 * @param index Index in constant pool. 373 * @param tag Tag of expected constant, either ConstantClass or ConstantString. 374 * @return Contents of string reference. 375 * @throws IllegalArgumentException Thrown if tag is invalid. 376 * @see ConstantClass 377 * @see ConstantString 378 */ 379 public String getConstantString(final int index, final byte tag) throws IllegalArgumentException { 380 final int i; 381 /* 382 * 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 383 * 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 384 * subclassing. 385 */ 386 switch (tag) { 387 case Const.CONSTANT_Class: 388 i = getConstant(index, ConstantClass.class).getNameIndex(); 389 break; 390 case Const.CONSTANT_String: 391 i = getConstant(index, ConstantString.class).getStringIndex(); 392 break; 393 case Const.CONSTANT_Module: 394 i = getConstant(index, ConstantModule.class).getNameIndex(); 395 break; 396 case Const.CONSTANT_Package: 397 i = getConstant(index, ConstantPackage.class).getNameIndex(); 398 break; 399 case Const.CONSTANT_Utf8: 400 return getConstantUtf8(index).getBytes(); 401 default: 402 throw new IllegalArgumentException("getConstantString called with illegal tag " + tag); 403 } 404 // Finally get the string from the constant pool 405 return getConstantUtf8(i).getBytes(); 406 } 407 408 /** 409 * Gets constant from constant pool and check whether it has the expected type. 410 * 411 * @param index Index in constant pool. 412 * @return ConstantUtf8 value. 413 * @throws ClassFormatException Thrown if constant type does not match tag. 414 * @see ConstantUtf8 415 */ 416 public ConstantUtf8 getConstantUtf8(final int index) throws ClassFormatException { 417 return getConstant(index, Const.CONSTANT_Utf8, ConstantUtf8.class); 418 } 419 420 /** 421 * Gets the length of constant pool. 422 * 423 * @return Length of constant pool. 424 */ 425 public int getLength() { 426 return constantPool.length; 427 } 428 429 @Override 430 public Iterator<Constant> iterator() { 431 return Arrays.stream(constantPool).iterator(); 432 } 433 434 /** 435 * Sets a constant at the specified index. 436 * 437 * @param index The index in the constant pool. 438 * @param constant Constant to set. 439 */ 440 public void setConstant(final int index, final Constant constant) { 441 constantPool[index] = constant; 442 } 443 444 /** 445 * Sets the constant pool. 446 * 447 * @param constantPool The constant pool array. 448 */ 449 public void setConstantPool(final Constant[] constantPool) { 450 this.constantPool = constantPool != null ? constantPool : Constant.EMPTY_ARRAY; 451 } 452 453 /** 454 * @return String representation. 455 */ 456 @Override 457 public String toString() { 458 final StringBuilder buf = new StringBuilder(); 459 for (int i = 1; i < constantPool.length; i++) { 460 buf.append(i).append(")").append(constantPool[i]).append("\n"); 461 } 462 return buf.toString(); 463 } 464}