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[] constant_pool; 040 041 /** 042 * @param constant_pool Array of constants 043 */ 044 public ConstantPool(final Constant[] constant_pool) { 045 this.constant_pool = constant_pool; 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 constant_pool = new Constant[constant_pool_count]; 059 /* constant_pool[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 constant_pool[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 = constant_pool[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 RuntimeException("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 file.writeShort(constant_pool.length); 222 for (int i = 1; i < constant_pool.length; i++) { 223 if (constant_pool[i] != null) { 224 constant_pool[i].dump(file); 225 } 226 } 227 } 228 229 /** 230 * Gets constant from constant pool. 231 * 232 * @param index Index in constant pool 233 * @return Constant value 234 * @see Constant 235 */ 236 public Constant getConstant( final int index ) { 237 if (index >= constant_pool.length || index < 0) { 238 throw new ClassFormatException("Invalid constant pool reference: " + index 239 + ". Constant pool size is: " + constant_pool.length); 240 } 241 return constant_pool[index]; 242 } 243 244 /** 245 * Gets constant from constant pool and check whether it has the 246 * expected type. 247 * 248 * @param index Index in constant pool 249 * @param tag Tag of expected constant, i.e., its type 250 * @return Constant value 251 * @see Constant 252 * @throws ClassFormatException 253 */ 254 public Constant getConstant( final int index, final byte tag ) throws ClassFormatException { 255 Constant c; 256 c = getConstant(index); 257 if (c == null) { 258 throw new ClassFormatException("Constant pool at index " + index + " is null."); 259 } 260 if (c.getTag() != tag) { 261 throw new ClassFormatException("Expected class `" + Const.getConstantName(tag) 262 + "' at index " + index + " and got " + c); 263 } 264 return c; 265 } 266 267 /** 268 * @return Array of constants. 269 * @see Constant 270 */ 271 public Constant[] getConstantPool() { 272 return constant_pool; 273 } 274 275 /** 276 * Gets string from constant pool and bypass the indirection of 277 * `ConstantClass' and `ConstantString' objects. I.e. these classes have 278 * an index field that points to another entry of the constant pool of 279 * type `ConstantUtf8' which contains the real data. 280 * 281 * @param index Index in constant pool 282 * @param tag Tag of expected constant, either ConstantClass or ConstantString 283 * @return Contents of string reference 284 * @see ConstantClass 285 * @see ConstantString 286 * @throws ClassFormatException 287 */ 288 public String getConstantString( final int index, final byte tag ) throws ClassFormatException { 289 Constant c; 290 int i; 291 c = getConstant(index, tag); 292 /* This switch() is not that elegant, since the four classes have the 293 * same contents, they just differ in the name of the index 294 * field variable. 295 * But we want to stick to the JVM naming conventions closely though 296 * we could have solved these more elegantly by using the same 297 * variable name or by subclassing. 298 */ 299 switch (tag) { 300 case Const.CONSTANT_Class: 301 i = ((ConstantClass) c).getNameIndex(); 302 break; 303 case Const.CONSTANT_String: 304 i = ((ConstantString) c).getStringIndex(); 305 break; 306 case Const.CONSTANT_Module: 307 i = ((ConstantModule) c).getNameIndex(); 308 break; 309 case Const.CONSTANT_Package: 310 i = ((ConstantPackage) c).getNameIndex(); 311 break; 312 default: 313 throw new RuntimeException("getConstantString called with illegal tag " + tag); 314 } 315 // Finally get the string from the constant pool 316 c = getConstant(i, Const.CONSTANT_Utf8); 317 return ((ConstantUtf8) c).getBytes(); 318 } 319 320 321 /** 322 * @return Length of constant pool. 323 */ 324 public int getLength() { 325 return constant_pool == null ? 0 : constant_pool.length; 326 } 327 328 329 /** 330 * @param constant Constant to set 331 */ 332 public void setConstant( final int index, final Constant constant ) { 333 constant_pool[index] = constant; 334 } 335 336 337 /** 338 * @param constant_pool 339 */ 340 public void setConstantPool( final Constant[] constant_pool ) { 341 this.constant_pool = constant_pool; 342 } 343 344 345 /** 346 * @return String representation. 347 */ 348 @Override 349 public String toString() { 350 final StringBuilder buf = new StringBuilder(); 351 for (int i = 1; i < constant_pool.length; i++) { 352 buf.append(i).append(")").append(constant_pool[i]).append("\n"); 353 } 354 return buf.toString(); 355 } 356 357 358 /** 359 * @return deep copy of this constant pool 360 */ 361 public ConstantPool copy() { 362 ConstantPool c = null; 363 try { 364 c = (ConstantPool) clone(); 365 c.constant_pool = new Constant[constant_pool.length]; 366 for (int i = 1; i < constant_pool.length; i++) { 367 if (constant_pool[i] != null) { 368 c.constant_pool[i] = constant_pool[i].copy(); 369 } 370 } 371 } catch (final CloneNotSupportedException e) { 372 // TODO should this throw? 373 } 374 return c; 375 } 376}