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