001package serp.bytecode.lowlevel; 002 003import java.io.*; 004import java.util.*; 005 006import serp.bytecode.visitor.*; 007import 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 */ 018public 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 public int findInvokeDynamicEntry(int bootstrapMethodIndex, String name, String desc, boolean add) { 338 int descIndex = findNameAndTypeEntry(name, desc, add); 339 if (descIndex == 0) 340 return 0; 341 342 Object key = new InvokeDynamicKey(bootstrapMethodIndex, descIndex); 343 int index = find(key); 344 if (!add || index > 0) 345 return index; 346 347 Entry entry = new InvokeDynamicEntry(bootstrapMethodIndex, descIndex); 348 return addEntry(key, entry); 349 } 350 351 /** 352 * Return the constant pool index of the {@link ComplexEntry} for the 353 * given name, descriptor, and owner class name. 354 * 355 * @param owner the name of the owning class in internal form 356 * @param name the name of the entity 357 * @param desc the descriptor of the entity in internal form 358 * @param type the type of entry: field, method, interface method 359 * @param add if true, the entry will be added if it does not 360 * already exist, and the new entry's index returned 361 */ 362 private int findComplexEntry(String owner, String name, String desc, 363 int type, boolean add) { 364 int classIndex = findClassEntry(owner, add); 365 if (classIndex == 0) 366 return 0; 367 int descIndex = findNameAndTypeEntry(name, desc, add); 368 if (descIndex == 0) 369 return 0; 370 371 Object key = null; 372 switch (type) { 373 case Entry.FIELD: 374 key = new FieldKey(classIndex, descIndex); 375 break; 376 case Entry.METHOD: 377 key = new MethodKey(classIndex, descIndex); 378 break; 379 case Entry.INTERFACEMETHOD: 380 key = new InterfaceMethodKey(classIndex, descIndex); 381 break; 382 } 383 int index = find(key); 384 if (!add || index > 0) 385 return index; 386 387 Entry entry = null; 388 switch (type) { 389 case Entry.FIELD: 390 entry = new FieldEntry(classIndex, descIndex); 391 break; 392 case Entry.METHOD: 393 entry = new MethodEntry(classIndex, descIndex); 394 break; 395 case Entry.INTERFACEMETHOD: 396 entry = new InterfaceMethodEntry(classIndex, descIndex); 397 break; 398 } 399 return addEntry(key, entry); 400 } 401 402 public void acceptVisit(BCVisitor visit) { 403 visit.enterConstantPool(this); 404 405 Entry entry; 406 for (Iterator itr = _entries.iterator(); itr.hasNext();) { 407 entry = (Entry) itr.next(); 408 if (entry == null) 409 continue; 410 visit.enterEntry(entry); 411 entry.acceptVisit(visit); 412 visit.exitEntry(entry); 413 } 414 visit.exitConstantPool(this); 415 } 416 417 /** 418 * Fill the constant pool from the given bytecode stream. 419 */ 420 public void read(DataInput in) throws IOException { 421 clear(); 422 423 int entryCount = in.readUnsignedShort(); 424 Entry entry; 425 for (int i = 1; i < entryCount; i++) { 426 entry = Entry.read(in); 427 addEntry(entry); 428 if (entry.isWide()) 429 i++; 430 } 431 } 432 433 /** 434 * Write the constant pool to the given bytecode stream. 435 */ 436 public void write(DataOutput out) throws IOException { 437 out.writeShort(_entries.size() + 1); 438 439 Entry entry; 440 for (Iterator itr = _entries.iterator(); itr.hasNext();) { 441 entry = (Entry) itr.next(); 442 if (entry != null) 443 Entry.write(entry, out); 444 } 445 } 446 447 /** 448 * Called by constant pool entries when they are mutated. 449 */ 450 void modifyEntry(Object origKey, Entry entry) { 451 _lookup.remove(origKey); 452 _lookup.put(getKey(entry), entry); 453 } 454 455 /** 456 * Returns the constant pool index of the entry with the given key. 457 */ 458 private int find(Object key) { 459 Entry entry = (Entry) _lookup.get(key); 460 if (entry == null) 461 return 0; 462 return entry.getIndex(); 463 } 464 465 /** 466 * Return the hash key used for the specified entry. 467 */ 468 static Object getKey(Entry entry) { 469 switch (entry.getType()) { 470 case Entry.CLASS: 471 return new ClassKey(((ClassEntry) entry).getNameIndex()); 472 case Entry.FIELD: 473 FieldEntry fe = (FieldEntry) entry; 474 return new FieldKey(fe.getClassIndex(), fe.getNameAndTypeIndex()); 475 case Entry.METHOD: 476 MethodEntry me = (MethodEntry) entry; 477 return new MethodKey(me.getClassIndex(), me.getNameAndTypeIndex()); 478 case Entry.INTERFACEMETHOD: 479 InterfaceMethodEntry ime = (InterfaceMethodEntry) entry; 480 return new InterfaceMethodKey(ime.getClassIndex(), 481 ime.getNameAndTypeIndex()); 482 case Entry.INVOKEDYNAMIC: 483 InvokeDynamicEntry ide = (InvokeDynamicEntry) entry; 484 return new InvokeDynamicKey(ide.getBootstrapMethodAttrIndex(), ide.getNameAndTypeIndex()); 485 case Entry.STRING: 486 return new StringKey(((StringEntry) entry).getStringIndex()); 487 case Entry.INT: 488 case Entry.FLOAT: 489 case Entry.LONG: 490 case Entry.DOUBLE: 491 case Entry.UTF8: 492 return ((ConstantEntry) entry).getConstant(); 493 case Entry.NAMEANDTYPE: 494 NameAndTypeEntry nte = (NameAndTypeEntry) entry; 495 return new NameAndTypeKey(nte.getNameIndex(), 496 nte.getDescriptorIndex()); 497 default: 498 return null; 499 } 500 } 501 502 /** 503 * Base class key for entries with one ptr to another entry. 504 */ 505 private static abstract class PtrKey { 506 private final int _index; 507 508 public PtrKey(int index) { 509 _index = index; 510 } 511 512 public int hashCode() { 513 return _index; 514 } 515 516 public boolean equals(Object other) { 517 if (other == this) 518 return true; 519 if (other.getClass() != getClass()) 520 return false; 521 return ((PtrKey) other)._index == _index; 522 } 523 } 524 525 /** 526 * Key for string entries. 527 */ 528 private static class StringKey extends PtrKey { 529 public StringKey(int index) { 530 super(index); 531 } 532 } 533 534 /** 535 * Key for class entries. 536 */ 537 private static class ClassKey extends PtrKey { 538 public ClassKey(int index) { 539 super(index); 540 } 541 } 542 543 /** 544 * Base class key for entries with two ptr to other entries. 545 */ 546 private static abstract class DoublePtrKey { 547 private final int _index1; 548 private final int _index2; 549 550 public DoublePtrKey(int index1, int index2) { 551 _index1 = index1; 552 _index2 = index2; 553 } 554 555 public int hashCode() { 556 return _index1 ^ _index2; 557 } 558 559 public boolean equals(Object other) { 560 if (other == this) 561 return true; 562 if (other.getClass() != getClass()) 563 return false; 564 DoublePtrKey key = (DoublePtrKey) other; 565 return key._index1 == _index1 && key._index2 == _index2; 566 } 567 } 568 569 /** 570 * Key for name and type entries. 571 */ 572 private static class NameAndTypeKey extends DoublePtrKey { 573 public NameAndTypeKey(int index1, int index2) { 574 super(index1, index2); 575 } 576 } 577 578 /** 579 * Key for field entries. 580 */ 581 private static class FieldKey extends DoublePtrKey { 582 public FieldKey(int index1, int index2) { 583 super(index1, index2); 584 } 585 } 586 587 /** 588 * Key for method entries. 589 */ 590 private static class MethodKey extends DoublePtrKey { 591 public MethodKey(int index1, int index2) { 592 super(index1, index2); 593 } 594 } 595 596 /** 597 * Key for interface method entries. 598 */ 599 private static class InterfaceMethodKey extends DoublePtrKey { 600 public InterfaceMethodKey(int index1, int index2) { 601 super(index1, index2); 602 } 603 } 604 605 /** 606 * Key for method entries. 607 */ 608 private static class InvokeDynamicKey extends DoublePtrKey { 609 public InvokeDynamicKey(int index1, int index2) { 610 super(index1, index2); 611 } 612 } 613}