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, i.e., 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 * @param constantPool Array of constants 071 */ 072 public ConstantPool(final Constant[] constantPool) { 073 setConstantPool(constantPool); 074 } 075 076 /** 077 * Reads constants from given input stream. 078 * 079 * @param input Input stream 080 * @throws IOException if problem in readUnsignedShort or readConstant 081 */ 082 public ConstantPool(final DataInput input) throws IOException { 083 byte tag; 084 final int constantPoolCount = input.readUnsignedShort(); 085 constantPool = new Constant[constantPoolCount]; 086 /* 087 * constantPool[0] is unused by the compiler and may be used freely by the implementation. 088 * constantPool[0] is currently unused by the implementation. 089 */ 090 for (int i = 1; i < constantPoolCount; i++) { 091 constantPool[i] = Constant.readConstant(input); 092 /* 093 * 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 094 * pool, then the next item will be numbered n+2" 095 * 096 * Thus we have to increment the index counter. 097 */ 098 tag = constantPool[i].getTag(); 099 if (tag == Const.CONSTANT_Double || tag == Const.CONSTANT_Long) { 100 i++; 101 } 102 } 103 } 104 105 /** 106 * 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, 107 * attributes, etc. spawns a tree of objects. 108 * 109 * @param v Visitor object 110 */ 111 @Override 112 public void accept(final Visitor v) { 113 v.visitConstantPool(this); 114 } 115 116 /** 117 * Resolves constant to a string representation. 118 * 119 * @param c Constant to be printed 120 * @return String representation 121 * @throws IllegalArgumentException if c is unknown constant type 122 */ 123 public String constantToString(Constant c) throws IllegalArgumentException { 124 final String str; 125 final int i; 126 final byte tag = c.getTag(); 127 switch (tag) { 128 case Const.CONSTANT_Class: 129 i = ((ConstantClass) c).getNameIndex(); 130 c = getConstantUtf8(i); 131 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false); 132 break; 133 case Const.CONSTANT_String: 134 i = ((ConstantString) c).getStringIndex(); 135 c = getConstantUtf8(i); 136 str = "\"" + escape(((ConstantUtf8) c).getBytes()) + "\""; 137 break; 138 case Const.CONSTANT_Utf8: 139 str = ((ConstantUtf8) c).getBytes(); 140 break; 141 case Const.CONSTANT_Double: 142 str = String.valueOf(((ConstantDouble) c).getBytes()); 143 break; 144 case Const.CONSTANT_Float: 145 str = String.valueOf(((ConstantFloat) c).getBytes()); 146 break; 147 case Const.CONSTANT_Long: 148 str = String.valueOf(((ConstantLong) c).getBytes()); 149 break; 150 case Const.CONSTANT_Integer: 151 str = String.valueOf(((ConstantInteger) c).getBytes()); 152 break; 153 case Const.CONSTANT_NameAndType: 154 str = constantToString(((ConstantNameAndType) c).getNameIndex(), Const.CONSTANT_Utf8) + " " 155 + constantToString(((ConstantNameAndType) c).getSignatureIndex(), Const.CONSTANT_Utf8); 156 break; 157 case Const.CONSTANT_InterfaceMethodref: 158 case Const.CONSTANT_Methodref: 159 case Const.CONSTANT_Fieldref: 160 str = constantToString(((ConstantCP) c).getClassIndex(), Const.CONSTANT_Class) + "." 161 + constantToString(((ConstantCP) c).getNameAndTypeIndex(), Const.CONSTANT_NameAndType); 162 break; 163 case Const.CONSTANT_MethodHandle: 164 // Note that the ReferenceIndex may point to a Fieldref, Methodref or 165 // InterfaceMethodref - so we need to peek ahead to get the actual type. 166 final ConstantMethodHandle cmh = (ConstantMethodHandle) c; 167 str = Const.getMethodHandleName(cmh.getReferenceKind()) + " " 168 + constantToString(cmh.getReferenceIndex(), getConstant(cmh.getReferenceIndex()).getTag()); 169 break; 170 case Const.CONSTANT_MethodType: 171 final ConstantMethodType cmt = (ConstantMethodType) c; 172 str = constantToString(cmt.getDescriptorIndex(), Const.CONSTANT_Utf8); 173 break; 174 case Const.CONSTANT_InvokeDynamic: 175 final ConstantInvokeDynamic cid = (ConstantInvokeDynamic) c; 176 str = cid.getBootstrapMethodAttrIndex() + ":" + constantToString(cid.getNameAndTypeIndex(), Const.CONSTANT_NameAndType); 177 break; 178 case Const.CONSTANT_Dynamic: 179 final ConstantDynamic cd = (ConstantDynamic) c; 180 str = cd.getBootstrapMethodAttrIndex() + ":" + constantToString(cd.getNameAndTypeIndex(), Const.CONSTANT_NameAndType); 181 break; 182 case Const.CONSTANT_Module: 183 i = ((ConstantModule) c).getNameIndex(); 184 c = getConstantUtf8(i); 185 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false); 186 break; 187 case Const.CONSTANT_Package: 188 i = ((ConstantPackage) c).getNameIndex(); 189 c = getConstantUtf8(i); 190 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false); 191 break; 192 default: // Never reached 193 throw new IllegalArgumentException("Unknown constant type " + tag); 194 } 195 return str; 196 } 197 198 /** 199 * Retrieves constant at 'index' from constant pool and resolve it to a string representation. 200 * 201 * @param index of constant in constant pool 202 * @param tag expected type 203 * @return String representation 204 */ 205 public String constantToString(final int index, final byte tag) { 206 return constantToString(getConstant(index, tag)); 207 } 208 209 /** 210 * @return deep copy of this constant pool 211 */ 212 public ConstantPool copy() { 213 ConstantPool c = null; 214 try { 215 c = (ConstantPool) clone(); 216 c.constantPool = new Constant[constantPool.length]; 217 for (int i = 1; i < constantPool.length; i++) { 218 if (constantPool[i] != null) { 219 c.constantPool[i] = constantPool[i].copy(); 220 } 221 } 222 } catch (final CloneNotSupportedException e) { 223 // TODO should this throw? 224 } 225 return c; 226 } 227 228 /** 229 * Dump constant pool to file stream in binary format. 230 * 231 * @param file Output file stream 232 * @throws IOException if problem in writeShort or dump 233 */ 234 public void dump(final DataOutputStream file) throws IOException { 235 /* 236 * Constants over the size of the constant pool shall not be written out. This is a redundant measure as the ConstantPoolGen should have already 237 * reported an error back in the situation. 238 */ 239 final int size = Math.min(constantPool.length, Const.MAX_CP_ENTRIES); 240 241 file.writeShort(size); 242 for (int i = 1; i < size; i++) { 243 if (constantPool[i] != null) { 244 constantPool[i].dump(file); 245 } 246 } 247 } 248 249 /** 250 * Gets constant from constant pool. 251 * 252 * @param index Index in constant pool 253 * @return Constant value 254 * @see Constant 255 * @throws ClassFormatException if index is invalid 256 */ 257 @SuppressWarnings("unchecked") 258 public <T extends Constant> T getConstant(final int index) throws ClassFormatException { 259 return (T) getConstant(index, Constant.class); 260 } 261 262 /** 263 * Gets constant from constant pool and check whether it has the expected type. 264 * 265 * @param index Index in constant pool 266 * @param tag Tag of expected constant, i.e., its type 267 * @return Constant value 268 * @see Constant 269 * @throws ClassFormatException if constant type does not match tag 270 */ 271 @SuppressWarnings("unchecked") 272 public <T extends Constant> T getConstant(final int index, final byte tag) throws ClassFormatException { 273 return (T) getConstant(index, tag, Constant.class); 274 } 275 276 /** 277 * Gets constant from constant pool and check whether it has the expected type. 278 * 279 * @param index Index in constant pool 280 * @param tag Tag of expected constant, i.e., its type 281 * @return Constant value 282 * @see Constant 283 * @throws ClassFormatException if constant type does not match tag 284 * @since 6.6.0 285 */ 286 public <T extends Constant> T getConstant(final int index, final byte tag, final Class<T> castTo) throws ClassFormatException { 287 final T c = getConstant(index); 288 if (c == null || c.getTag() != tag) { 289 throw new ClassFormatException("Expected class '" + Const.getConstantName(tag) + "' at index " + index + " and got " + c); 290 } 291 return c; 292 } 293 294 /** 295 * Gets constant from constant pool. 296 * 297 * @param <T> A {@link Constant} subclass 298 * @param index Index in constant pool 299 * @param castTo The {@link Constant} subclass to cast to. 300 * @return Constant value 301 * @see Constant 302 * @throws ClassFormatException if index is invalid 303 * @since 6.6.0 304 */ 305 public <T extends Constant> T getConstant(final int index, final Class<T> castTo) throws ClassFormatException { 306 if (index >= constantPool.length || index < 1) { 307 throw new ClassFormatException("Invalid constant pool reference using index: " + index + ". Constant pool size is: " + constantPool.length); 308 } 309 if (constantPool[index] != null && !castTo.isAssignableFrom(constantPool[index].getClass())) { 310 throw new ClassFormatException("Invalid constant pool reference at index: " + index + 311 ". Expected " + castTo + " but was " + constantPool[index].getClass()); 312 } 313 if (index > 1) { 314 final Constant prev = constantPool[index - 1]; 315 if (prev != null && (prev.getTag() == Const.CONSTANT_Double || prev.getTag() == Const.CONSTANT_Long)) { 316 throw new ClassFormatException("Constant pool at index " + index + " is invalid. The index is unused due to the preceeding " 317 + Const.getConstantName(prev.getTag()) + "."); 318 } 319 } 320 // Previous check ensures this won't throw a ClassCastException 321 final T c = castTo.cast(constantPool[index]); 322 if (c == null) { 323 throw new ClassFormatException("Constant pool at index " + index + " is null."); 324 } 325 return c; 326 } 327 328 /** 329 * Gets constant from constant pool and check whether it has the expected type. 330 * 331 * @param index Index in constant pool 332 * @return ConstantInteger value 333 * @see ConstantInteger 334 * @throws ClassFormatException if constant type does not match tag 335 */ 336 public ConstantInteger getConstantInteger(final int index) { 337 return getConstant(index, Const.CONSTANT_Integer, ConstantInteger.class); 338 } 339 340 /** 341 * @return Array of constants. 342 * @see Constant 343 */ 344 public Constant[] getConstantPool() { 345 return constantPool; 346 } 347 348 /** 349 * Gets string from constant pool and bypass the indirection of 'ConstantClass' and 'ConstantString' objects. I.e. these classes have an index field that 350 * points to another entry of the constant pool of type 'ConstantUtf8' which contains the real data. 351 * 352 * @param index Index in constant pool 353 * @param tag Tag of expected constant, either ConstantClass or ConstantString 354 * @return Contents of string reference 355 * @see ConstantClass 356 * @see ConstantString 357 * @throws IllegalArgumentException if tag is invalid 358 */ 359 public String getConstantString(final int index, final byte tag) throws IllegalArgumentException { 360 final int i; 361 /* 362 * 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 363 * 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 364 * subclassing. 365 */ 366 switch (tag) { 367 case Const.CONSTANT_Class: 368 i = getConstant(index, ConstantClass.class).getNameIndex(); 369 break; 370 case Const.CONSTANT_String: 371 i = getConstant(index, ConstantString.class).getStringIndex(); 372 break; 373 case Const.CONSTANT_Module: 374 i = getConstant(index, ConstantModule.class).getNameIndex(); 375 break; 376 case Const.CONSTANT_Package: 377 i = getConstant(index, ConstantPackage.class).getNameIndex(); 378 break; 379 case Const.CONSTANT_Utf8: 380 return getConstantUtf8(index).getBytes(); 381 default: 382 throw new IllegalArgumentException("getConstantString called with illegal tag " + tag); 383 } 384 // Finally get the string from the constant pool 385 return getConstantUtf8(i).getBytes(); 386 } 387 388 /** 389 * Gets constant from constant pool and check whether it has the expected type. 390 * 391 * @param index Index in constant pool 392 * @return ConstantUtf8 value 393 * @see ConstantUtf8 394 * @throws ClassFormatException if constant type does not match tag 395 */ 396 public ConstantUtf8 getConstantUtf8(final int index) throws ClassFormatException { 397 return getConstant(index, Const.CONSTANT_Utf8, ConstantUtf8.class); 398 } 399 400 /** 401 * @return Length of constant pool. 402 */ 403 public int getLength() { 404 return constantPool.length; 405 } 406 407 @Override 408 public Iterator<Constant> iterator() { 409 return Arrays.stream(constantPool).iterator(); 410 } 411 412 /** 413 * @param constant Constant to set 414 */ 415 public void setConstant(final int index, final Constant constant) { 416 constantPool[index] = constant; 417 } 418 419 /** 420 * @param constantPool 421 */ 422 public void setConstantPool(final Constant[] constantPool) { 423 this.constantPool = constantPool != null ? constantPool : Constant.EMPTY_ARRAY; 424 } 425 426 /** 427 * @return String representation. 428 */ 429 @Override 430 public String toString() { 431 final StringBuilder buf = new StringBuilder(); 432 for (int i = 1; i < constantPool.length; i++) { 433 buf.append(i).append(")").append(constantPool[i]).append("\n"); 434 } 435 return buf.toString(); 436 } 437}