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