001 package serp.bytecode.lowlevel; 002 003 import java.io.*; 004 import java.util.*; 005 006 import serp.bytecode.visitor.*; 007 import serp.util.*; 008 009 /** 010 * A bytecode constant pool, containing entries for all strings, 011 * constants, classes, etc referenced in the class structure and method 012 * opcodes. In keeping with the low-level bytecode representation, all pool 013 * indexes are 1-based and {@link LongEntry}s and {@link DoubleEntry}s each 014 * occupy two indexes in the pool. 015 * 016 * @author Abe White 017 */ 018 public class ConstantPool implements VisitAcceptor { 019 private List _entries = new ArrayList(50); 020 private Map _lookup = new HashMap(50); 021 022 /** 023 * Default constructor. 024 */ 025 public ConstantPool() { 026 } 027 028 /** 029 * Return all the entries in the pool. 030 */ 031 public Entry[] getEntries() { 032 List entries = new ArrayList(_entries.size()); 033 Entry entry; 034 for (Iterator itr = _entries.iterator(); itr.hasNext();) { 035 entry = (Entry) itr.next(); 036 if (entry != null) 037 entries.add(entry); 038 } 039 return (Entry[]) entries.toArray(new Entry[entries.size()]); 040 } 041 042 /** 043 * Retrieve the entry at the specified 1-based index. 044 * 045 * @throws IndexOutOfBoundsException if index is invalid, 046 * including the case that it points to the second slot of a 047 * long or double entry 048 */ 049 public Entry getEntry(int index) { 050 Entry entry = (Entry) _entries.get(index - 1); 051 if (entry == null) 052 throw new IndexOutOfBoundsException("index = " + index); 053 return entry; 054 } 055 056 /** 057 * Return the index of the given entry, or 0 if it is not in the pool. 058 */ 059 public int indexOf(Entry entry) { 060 if (entry == null || entry.getPool() != this) 061 return 0; 062 return entry.getIndex(); 063 } 064 065 /** 066 * Add an entry to the pool. 067 * 068 * @return the index at which the entry was added 069 */ 070 public int addEntry(Entry entry) { 071 if (entry.getPool() != this) 072 addEntry(getKey(entry), entry); 073 return entry.getIndex(); 074 } 075 076 /** 077 * Add an entry to the pool using the given key. 078 */ 079 private int addEntry(Object key, Entry entry) { 080 entry.setPool(this); 081 _entries.add(entry); 082 entry.setIndex(_entries.size()); 083 _lookup.put(key, entry); 084 if (entry.isWide()) 085 _entries.add(null); 086 return entry.getIndex(); 087 } 088 089 /** 090 * Remove the given entry from the pool. 091 * 092 * @return false if the entry is not in the pool, true otherwise 093 */ 094 public boolean removeEntry(Entry entry) { 095 if (entry == null || entry.getPool() != this) 096 return false; 097 098 int index = entry.getIndex() - 1; 099 entry.setPool(null); 100 entry.setIndex(0); 101 _entries.remove(index); 102 if (entry.isWide()) 103 _entries.remove(index); 104 _lookup.remove(getKey(entry)); 105 106 // rehash all the entries after the removed one with their new index 107 Object key; 108 for (int i = index; i < _entries.size(); i++) { 109 entry = (Entry) _entries.get(i); 110 if (entry != null) { 111 key = getKey(entry); 112 _lookup.remove(key); 113 entry.setIndex(i + 1); 114 _lookup.put(key, entry); 115 } 116 } 117 return true; 118 } 119 120 /** 121 * Clear all entries from the pool. 122 */ 123 public void clear() { 124 Entry entry; 125 for (Iterator itr = _entries.iterator(); itr.hasNext();) { 126 entry = (Entry) itr.next(); 127 if (entry != null) { 128 entry.setPool(null); 129 entry.setIndex(0); 130 } 131 } 132 _entries.clear(); 133 _lookup.clear(); 134 } 135 136 /** 137 * Return the number of places occupied in the pool, including the fact 138 * that long and double entries occupy two places. 139 */ 140 public int size() { 141 return _entries.size(); 142 } 143 144 /** 145 * Return the index of the {@link UTF8Entry} with the given value, or 146 * 0 if it does not exist. 147 * 148 * @param add if true, the entry will be added if it does not 149 * already exist, and the new entry's index returned 150 */ 151 public int findUTF8Entry(String value, boolean add) { 152 if (value == null) { 153 if (add) 154 throw new NullPointerException("value = null"); 155 return 0; 156 } 157 158 int index = find(value); 159 if (!add || index > 0) 160 return index; 161 return addEntry(value, new UTF8Entry(value)); 162 } 163 164 /** 165 * Return the constant pool index of the {@link DoubleEntry} for the given 166 * value, or 0 if it does not exist. 167 * 168 * @param value the value to find 169 * @param add if true, the entry will be added if it does not 170 * already exist, and the new entry's index returned 171 */ 172 public int findDoubleEntry(double value, boolean add) { 173 Double key = new Double(value); 174 int index = find(key); 175 if (!add || (index > 0)) 176 return index; 177 return addEntry(key, new DoubleEntry(value)); 178 } 179 180 /** 181 * Return the constant pool index of the {@link FloatEntry} for the given 182 * value, or 0 if it does not exist. 183 * 184 * @param value the value to find 185 * @param add if true, the entry will be added if it does not 186 * already exist, and the new entry's index returned 187 */ 188 public int findFloatEntry(float value, boolean add) { 189 Float key = new Float(value); 190 int index = find(key); 191 if (!add || index > 0) 192 return index; 193 return addEntry(key, new FloatEntry(value)); 194 } 195 196 /** 197 * Return the constant pool index of the {@link IntEntry} for the given 198 * value, or 0 if it does not exist. 199 * 200 * @param value the value to find 201 * @param add if true, the entry will be added if it does not 202 * already exist, and the new entry's index returned 203 */ 204 public int findIntEntry(int value, boolean add) { 205 Integer key = Numbers.valueOf(value); 206 int index = find(key); 207 if (!add || index > 0) 208 return index; 209 return addEntry(key, new IntEntry(value)); 210 } 211 212 /** 213 * Return the constant pool index of the {@link LongEntry} for the given 214 * value, or 0 if it does not exist. 215 * 216 * @param value the value to find 217 * @param add if true, the entry will be added if it does not 218 * already exist, and the new entry's index returned 219 */ 220 public int findLongEntry(long value, boolean add) { 221 Long key = Numbers.valueOf(value); 222 int index = find(key); 223 if (!add || index > 0) 224 return index; 225 return addEntry(key, new LongEntry(value)); 226 } 227 228 /** 229 * Return the constant pool index of the {@link StringEntry} for the given 230 * string value, or 0 if it does not exist. 231 * 232 * @param value the value to find 233 * @param add if true, the entry will be added if it does not 234 * already exist, and the new entry's index returned 235 */ 236 public int findStringEntry(String value, boolean add) { 237 int valueIndex = findUTF8Entry(value, add); 238 if (valueIndex == 0) 239 return 0; 240 241 StringKey key = new StringKey(valueIndex); 242 int index = find(key); 243 if (!add || index > 0) 244 return index; 245 return addEntry(key, new StringEntry(valueIndex)); 246 } 247 248 /** 249 * Return the constant pool index of the {@link ClassEntry} for the given 250 * class name, or 0 if it does not exist. 251 * 252 * @param name the class name in internal form 253 * @param add if true, the entry will be added if it does not 254 * already exist, and the new entry's index returned 255 */ 256 public int findClassEntry(String name, boolean add) { 257 int nameIndex = findUTF8Entry(name, add); 258 if (nameIndex == 0) 259 return 0; 260 261 ClassKey key = new ClassKey(nameIndex); 262 int index = find(key); 263 if (!add || index > 0) 264 return index; 265 return addEntry(key, new ClassEntry(nameIndex)); 266 } 267 268 /** 269 * Return the constant pool index of the {@link NameAndTypeEntry} for the 270 * given name and descriptor, or 0 if it does not exist. 271 * 272 * @param name the name of the entity 273 * @param desc the descriptor of the entity in internal form 274 * @param add if true, the entry will be added if it does not 275 * already exist, and the new entry's index returned 276 */ 277 public int findNameAndTypeEntry(String name, String desc, boolean add) { 278 int nameIndex = findUTF8Entry(name, add); 279 if (nameIndex == 0) 280 return 0; 281 int descIndex = findUTF8Entry(desc, add); 282 if (descIndex == 0) 283 return 0; 284 285 NameAndTypeKey key = new NameAndTypeKey(nameIndex, descIndex); 286 int index = find(key); 287 if (!add || index > 0) 288 return index; 289 return addEntry(key, new NameAndTypeEntry(nameIndex, descIndex)); 290 } 291 292 /** 293 * Return the constant pool index of the {@link FieldEntry} for the 294 * given name, descriptor, and owner class name. 295 * 296 * @param owner the name of the field's owning class in internal form 297 * @param name the name of the field 298 * @param desc the descriptor of the field in internal form 299 * @param add if true, the entry will be added if it does not 300 * already exist, and the new entry's index returned 301 */ 302 public int findFieldEntry(String owner, String name, String desc, 303 boolean add) { 304 return findComplexEntry(owner, name, desc, Entry.FIELD, add); 305 } 306 307 /** 308 * Return the constant pool index of the {@link MethodEntry} for the 309 * given name, descriptor, and owner class name. 310 * 311 * @param owner the name of the method's owning class in internal form 312 * @param name the name of the method 313 * @param desc the descriptor of the method in internal form 314 * @param add if true, the entry will be added if it does not 315 * already exist, and the new entry's index returned 316 */ 317 public int findMethodEntry(String owner, String name, String desc, 318 boolean add) { 319 return findComplexEntry(owner, name, desc, Entry.METHOD, add); 320 } 321 322 /** 323 * Return the constant pool index of the {@link InterfaceMethodEntry} for 324 * the given name, descriptor, and owner class name. 325 * 326 * @param owner the name of the method's owning class in internal form 327 * @param name the name of the method 328 * @param desc the descriptor of the method in internal form 329 * @param add if true, the entry will be added if it does not 330 * already exist, and the new entry's index returned 331 */ 332 public int findInterfaceMethodEntry(String owner, String name, String desc, 333 boolean add) { 334 return findComplexEntry(owner, name, desc, Entry.INTERFACEMETHOD, add); 335 } 336 337 /** 338 * Return the constant pool index of the {@link ComplexEntry} for the 339 * given name, descriptor, and owner class name. 340 * 341 * @param owner the name of the owning class in internal form 342 * @param name the name of the entity 343 * @param desc the descriptor of the entity in internal form 344 * @param type the type of entry: field, method, interface method 345 * @param add if true, the entry will be added if it does not 346 * already exist, and the new entry's index returned 347 */ 348 private int findComplexEntry(String owner, String name, String desc, 349 int type, boolean add) { 350 int classIndex = findClassEntry(owner, add); 351 if (classIndex == 0) 352 return 0; 353 int descIndex = findNameAndTypeEntry(name, desc, add); 354 if (descIndex == 0) 355 return 0; 356 357 Object key = null; 358 switch (type) { 359 case Entry.FIELD: 360 key = new FieldKey(classIndex, descIndex); 361 break; 362 case Entry.METHOD: 363 key = new MethodKey(classIndex, descIndex); 364 break; 365 case Entry.INTERFACEMETHOD: 366 key = new InterfaceMethodKey(classIndex, descIndex); 367 break; 368 } 369 int index = find(key); 370 if (!add || index > 0) 371 return index; 372 373 Entry entry = null; 374 switch (type) { 375 case Entry.FIELD: 376 entry = new FieldEntry(classIndex, descIndex); 377 break; 378 case Entry.METHOD: 379 entry = new MethodEntry(classIndex, descIndex); 380 break; 381 case Entry.INTERFACEMETHOD: 382 entry = new InterfaceMethodEntry(classIndex, descIndex); 383 break; 384 } 385 return addEntry(key, entry); 386 } 387 388 public void acceptVisit(BCVisitor visit) { 389 visit.enterConstantPool(this); 390 391 Entry entry; 392 for (Iterator itr = _entries.iterator(); itr.hasNext();) { 393 entry = (Entry) itr.next(); 394 if (entry == null) 395 continue; 396 visit.enterEntry(entry); 397 entry.acceptVisit(visit); 398 visit.exitEntry(entry); 399 } 400 visit.exitConstantPool(this); 401 } 402 403 /** 404 * Fill the constant pool from the given bytecode stream. 405 */ 406 public void read(DataInput in) throws IOException { 407 clear(); 408 409 int entryCount = in.readUnsignedShort(); 410 Entry entry; 411 for (int i = 1; i < entryCount; i++) { 412 entry = Entry.read(in); 413 addEntry(entry); 414 if (entry.isWide()) 415 i++; 416 } 417 } 418 419 /** 420 * Write the constant pool to the given bytecode stream. 421 */ 422 public void write(DataOutput out) throws IOException { 423 out.writeShort(_entries.size() + 1); 424 425 Entry entry; 426 for (Iterator itr = _entries.iterator(); itr.hasNext();) { 427 entry = (Entry) itr.next(); 428 if (entry != null) 429 Entry.write(entry, out); 430 } 431 } 432 433 /** 434 * Called by constant pool entries when they are mutated. 435 */ 436 void modifyEntry(Object origKey, Entry entry) { 437 _lookup.remove(origKey); 438 _lookup.put(getKey(entry), entry); 439 } 440 441 /** 442 * Returns the constant pool index of the entry with the given key. 443 */ 444 private int find(Object key) { 445 Entry entry = (Entry) _lookup.get(key); 446 if (entry == null) 447 return 0; 448 return entry.getIndex(); 449 } 450 451 /** 452 * Return the hash key used for the specified entry. 453 */ 454 static Object getKey(Entry entry) { 455 switch (entry.getType()) { 456 case Entry.CLASS: 457 return new ClassKey(((ClassEntry) entry).getNameIndex()); 458 case Entry.FIELD: 459 FieldEntry fe = (FieldEntry) entry; 460 return new FieldKey(fe.getClassIndex(), fe.getNameAndTypeIndex()); 461 case Entry.METHOD: 462 MethodEntry me = (MethodEntry) entry; 463 return new MethodKey(me.getClassIndex(), me.getNameAndTypeIndex()); 464 case Entry.INTERFACEMETHOD: 465 InterfaceMethodEntry ime = (InterfaceMethodEntry) entry; 466 return new InterfaceMethodKey(ime.getClassIndex(), 467 ime.getNameAndTypeIndex()); 468 case Entry.STRING: 469 return new StringKey(((StringEntry) entry).getStringIndex()); 470 case Entry.INT: 471 case Entry.FLOAT: 472 case Entry.LONG: 473 case Entry.DOUBLE: 474 case Entry.UTF8: 475 return ((ConstantEntry) entry).getConstant(); 476 case Entry.NAMEANDTYPE: 477 NameAndTypeEntry nte = (NameAndTypeEntry) entry; 478 return new NameAndTypeKey(nte.getNameIndex(), 479 nte.getDescriptorIndex()); 480 default: 481 return null; 482 } 483 } 484 485 /** 486 * Base class key for entries with one ptr to another entry. 487 */ 488 private static abstract class PtrKey { 489 private final int _index; 490 491 public PtrKey(int index) { 492 _index = index; 493 } 494 495 public int hashCode() { 496 return _index; 497 } 498 499 public boolean equals(Object other) { 500 if (other == this) 501 return true; 502 if (other.getClass() != getClass()) 503 return false; 504 return ((PtrKey) other)._index == _index; 505 } 506 } 507 508 /** 509 * Key for string entries. 510 */ 511 private static class StringKey extends PtrKey { 512 public StringKey(int index) { 513 super(index); 514 } 515 } 516 517 /** 518 * Key for class entries. 519 */ 520 private static class ClassKey extends PtrKey { 521 public ClassKey(int index) { 522 super(index); 523 } 524 } 525 526 /** 527 * Base class key for entries with two ptr to other entries. 528 */ 529 private static abstract class DoublePtrKey { 530 private final int _index1; 531 private final int _index2; 532 533 public DoublePtrKey(int index1, int index2) { 534 _index1 = index1; 535 _index2 = index2; 536 } 537 538 public int hashCode() { 539 return _index1 ^ _index2; 540 } 541 542 public boolean equals(Object other) { 543 if (other == this) 544 return true; 545 if (other.getClass() != getClass()) 546 return false; 547 DoublePtrKey key = (DoublePtrKey) other; 548 return key._index1 == _index1 && key._index2 == _index2; 549 } 550 } 551 552 /** 553 * Key for name and type entries. 554 */ 555 private static class NameAndTypeKey extends DoublePtrKey { 556 public NameAndTypeKey(int index1, int index2) { 557 super(index1, index2); 558 } 559 } 560 561 /** 562 * Key for field entries. 563 */ 564 private static class FieldKey extends DoublePtrKey { 565 public FieldKey(int index1, int index2) { 566 super(index1, index2); 567 } 568 } 569 570 /** 571 * Key for method entries. 572 */ 573 private static class MethodKey extends DoublePtrKey { 574 public MethodKey(int index1, int index2) { 575 super(index1, index2); 576 } 577 } 578 579 /** 580 * Key for interface method entries. 581 */ 582 private static class InterfaceMethodKey extends DoublePtrKey { 583 public InterfaceMethodKey(int index1, int index2) { 584 super(index1, index2); 585 } 586 } 587 }