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