/serp/src/main/java/serp/bytecode/lowlevel/ConstantPool.java

# · Java · 587 lines · 342 code · 55 blank · 190 comment · 81 complexity · 78a11f2257107f89cad119cc7cf6e241 MD5 · raw file

  1. package serp.bytecode.lowlevel;
  2. import java.io.*;
  3. import java.util.*;
  4. import serp.bytecode.visitor.*;
  5. import serp.util.*;
  6. /**
  7. * A bytecode constant pool, containing entries for all strings,
  8. * constants, classes, etc referenced in the class structure and method
  9. * opcodes. In keeping with the low-level bytecode representation, all pool
  10. * indexes are 1-based and {@link LongEntry}s and {@link DoubleEntry}s each
  11. * occupy two indexes in the pool.
  12. *
  13. * @author Abe White
  14. */
  15. public class ConstantPool implements VisitAcceptor {
  16. private List _entries = new ArrayList(50);
  17. private Map _lookup = new HashMap(50);
  18. /**
  19. * Default constructor.
  20. */
  21. public ConstantPool() {
  22. }
  23. /**
  24. * Return all the entries in the pool.
  25. */
  26. public Entry[] getEntries() {
  27. List entries = new ArrayList(_entries.size());
  28. Entry entry;
  29. for (Iterator itr = _entries.iterator(); itr.hasNext();) {
  30. entry = (Entry) itr.next();
  31. if (entry != null)
  32. entries.add(entry);
  33. }
  34. return (Entry[]) entries.toArray(new Entry[entries.size()]);
  35. }
  36. /**
  37. * Retrieve the entry at the specified 1-based index.
  38. *
  39. * @throws IndexOutOfBoundsException if index is invalid,
  40. * including the case that it points to the second slot of a
  41. * long or double entry
  42. */
  43. public Entry getEntry(int index) {
  44. Entry entry = (Entry) _entries.get(index - 1);
  45. if (entry == null)
  46. throw new IndexOutOfBoundsException("index = " + index);
  47. return entry;
  48. }
  49. /**
  50. * Return the index of the given entry, or 0 if it is not in the pool.
  51. */
  52. public int indexOf(Entry entry) {
  53. if (entry == null || entry.getPool() != this)
  54. return 0;
  55. return entry.getIndex();
  56. }
  57. /**
  58. * Add an entry to the pool.
  59. *
  60. * @return the index at which the entry was added
  61. */
  62. public int addEntry(Entry entry) {
  63. if (entry.getPool() != this)
  64. addEntry(getKey(entry), entry);
  65. return entry.getIndex();
  66. }
  67. /**
  68. * Add an entry to the pool using the given key.
  69. */
  70. private int addEntry(Object key, Entry entry) {
  71. entry.setPool(this);
  72. _entries.add(entry);
  73. entry.setIndex(_entries.size());
  74. _lookup.put(key, entry);
  75. if (entry.isWide())
  76. _entries.add(null);
  77. return entry.getIndex();
  78. }
  79. /**
  80. * Remove the given entry from the pool.
  81. *
  82. * @return false if the entry is not in the pool, true otherwise
  83. */
  84. public boolean removeEntry(Entry entry) {
  85. if (entry == null || entry.getPool() != this)
  86. return false;
  87. int index = entry.getIndex() - 1;
  88. entry.setPool(null);
  89. entry.setIndex(0);
  90. _entries.remove(index);
  91. if (entry.isWide())
  92. _entries.remove(index);
  93. _lookup.remove(getKey(entry));
  94. // rehash all the entries after the removed one with their new index
  95. Object key;
  96. for (int i = index; i < _entries.size(); i++) {
  97. entry = (Entry) _entries.get(i);
  98. if (entry != null) {
  99. key = getKey(entry);
  100. _lookup.remove(key);
  101. entry.setIndex(i + 1);
  102. _lookup.put(key, entry);
  103. }
  104. }
  105. return true;
  106. }
  107. /**
  108. * Clear all entries from the pool.
  109. */
  110. public void clear() {
  111. Entry entry;
  112. for (Iterator itr = _entries.iterator(); itr.hasNext();) {
  113. entry = (Entry) itr.next();
  114. if (entry != null) {
  115. entry.setPool(null);
  116. entry.setIndex(0);
  117. }
  118. }
  119. _entries.clear();
  120. _lookup.clear();
  121. }
  122. /**
  123. * Return the number of places occupied in the pool, including the fact
  124. * that long and double entries occupy two places.
  125. */
  126. public int size() {
  127. return _entries.size();
  128. }
  129. /**
  130. * Return the index of the {@link UTF8Entry} with the given value, or
  131. * 0 if it does not exist.
  132. *
  133. * @param add if true, the entry will be added if it does not
  134. * already exist, and the new entry's index returned
  135. */
  136. public int findUTF8Entry(String value, boolean add) {
  137. if (value == null) {
  138. if (add)
  139. throw new NullPointerException("value = null");
  140. return 0;
  141. }
  142. int index = find(value);
  143. if (!add || index > 0)
  144. return index;
  145. return addEntry(value, new UTF8Entry(value));
  146. }
  147. /**
  148. * Return the constant pool index of the {@link DoubleEntry} for the given
  149. * value, or 0 if it does not exist.
  150. *
  151. * @param value the value to find
  152. * @param add if true, the entry will be added if it does not
  153. * already exist, and the new entry's index returned
  154. */
  155. public int findDoubleEntry(double value, boolean add) {
  156. Double key = new Double(value);
  157. int index = find(key);
  158. if (!add || (index > 0))
  159. return index;
  160. return addEntry(key, new DoubleEntry(value));
  161. }
  162. /**
  163. * Return the constant pool index of the {@link FloatEntry} for the given
  164. * value, or 0 if it does not exist.
  165. *
  166. * @param value the value to find
  167. * @param add if true, the entry will be added if it does not
  168. * already exist, and the new entry's index returned
  169. */
  170. public int findFloatEntry(float value, boolean add) {
  171. Float key = new Float(value);
  172. int index = find(key);
  173. if (!add || index > 0)
  174. return index;
  175. return addEntry(key, new FloatEntry(value));
  176. }
  177. /**
  178. * Return the constant pool index of the {@link IntEntry} for the given
  179. * value, or 0 if it does not exist.
  180. *
  181. * @param value the value to find
  182. * @param add if true, the entry will be added if it does not
  183. * already exist, and the new entry's index returned
  184. */
  185. public int findIntEntry(int value, boolean add) {
  186. Integer key = Numbers.valueOf(value);
  187. int index = find(key);
  188. if (!add || index > 0)
  189. return index;
  190. return addEntry(key, new IntEntry(value));
  191. }
  192. /**
  193. * Return the constant pool index of the {@link LongEntry} for the given
  194. * value, or 0 if it does not exist.
  195. *
  196. * @param value the value to find
  197. * @param add if true, the entry will be added if it does not
  198. * already exist, and the new entry's index returned
  199. */
  200. public int findLongEntry(long value, boolean add) {
  201. Long key = Numbers.valueOf(value);
  202. int index = find(key);
  203. if (!add || index > 0)
  204. return index;
  205. return addEntry(key, new LongEntry(value));
  206. }
  207. /**
  208. * Return the constant pool index of the {@link StringEntry} for the given
  209. * string value, or 0 if it does not exist.
  210. *
  211. * @param value the value to find
  212. * @param add if true, the entry will be added if it does not
  213. * already exist, and the new entry's index returned
  214. */
  215. public int findStringEntry(String value, boolean add) {
  216. int valueIndex = findUTF8Entry(value, add);
  217. if (valueIndex == 0)
  218. return 0;
  219. StringKey key = new StringKey(valueIndex);
  220. int index = find(key);
  221. if (!add || index > 0)
  222. return index;
  223. return addEntry(key, new StringEntry(valueIndex));
  224. }
  225. /**
  226. * Return the constant pool index of the {@link ClassEntry} for the given
  227. * class name, or 0 if it does not exist.
  228. *
  229. * @param name the class name in internal form
  230. * @param add if true, the entry will be added if it does not
  231. * already exist, and the new entry's index returned
  232. */
  233. public int findClassEntry(String name, boolean add) {
  234. int nameIndex = findUTF8Entry(name, add);
  235. if (nameIndex == 0)
  236. return 0;
  237. ClassKey key = new ClassKey(nameIndex);
  238. int index = find(key);
  239. if (!add || index > 0)
  240. return index;
  241. return addEntry(key, new ClassEntry(nameIndex));
  242. }
  243. /**
  244. * Return the constant pool index of the {@link NameAndTypeEntry} for the
  245. * given name and descriptor, or 0 if it does not exist.
  246. *
  247. * @param name the name of the entity
  248. * @param desc the descriptor of the entity in internal form
  249. * @param add if true, the entry will be added if it does not
  250. * already exist, and the new entry's index returned
  251. */
  252. public int findNameAndTypeEntry(String name, String desc, boolean add) {
  253. int nameIndex = findUTF8Entry(name, add);
  254. if (nameIndex == 0)
  255. return 0;
  256. int descIndex = findUTF8Entry(desc, add);
  257. if (descIndex == 0)
  258. return 0;
  259. NameAndTypeKey key = new NameAndTypeKey(nameIndex, descIndex);
  260. int index = find(key);
  261. if (!add || index > 0)
  262. return index;
  263. return addEntry(key, new NameAndTypeEntry(nameIndex, descIndex));
  264. }
  265. /**
  266. * Return the constant pool index of the {@link FieldEntry} for the
  267. * given name, descriptor, and owner class name.
  268. *
  269. * @param owner the name of the field's owning class in internal form
  270. * @param name the name of the field
  271. * @param desc the descriptor of the field in internal form
  272. * @param add if true, the entry will be added if it does not
  273. * already exist, and the new entry's index returned
  274. */
  275. public int findFieldEntry(String owner, String name, String desc,
  276. boolean add) {
  277. return findComplexEntry(owner, name, desc, Entry.FIELD, add);
  278. }
  279. /**
  280. * Return the constant pool index of the {@link MethodEntry} for the
  281. * given name, descriptor, and owner class name.
  282. *
  283. * @param owner the name of the method's owning class in internal form
  284. * @param name the name of the method
  285. * @param desc the descriptor of the method in internal form
  286. * @param add if true, the entry will be added if it does not
  287. * already exist, and the new entry's index returned
  288. */
  289. public int findMethodEntry(String owner, String name, String desc,
  290. boolean add) {
  291. return findComplexEntry(owner, name, desc, Entry.METHOD, add);
  292. }
  293. /**
  294. * Return the constant pool index of the {@link InterfaceMethodEntry} for
  295. * the given name, descriptor, and owner class name.
  296. *
  297. * @param owner the name of the method's owning class in internal form
  298. * @param name the name of the method
  299. * @param desc the descriptor of the method in internal form
  300. * @param add if true, the entry will be added if it does not
  301. * already exist, and the new entry's index returned
  302. */
  303. public int findInterfaceMethodEntry(String owner, String name, String desc,
  304. boolean add) {
  305. return findComplexEntry(owner, name, desc, Entry.INTERFACEMETHOD, add);
  306. }
  307. /**
  308. * Return the constant pool index of the {@link ComplexEntry} for the
  309. * given name, descriptor, and owner class name.
  310. *
  311. * @param owner the name of the owning class in internal form
  312. * @param name the name of the entity
  313. * @param desc the descriptor of the entity in internal form
  314. * @param type the type of entry: field, method, interface method
  315. * @param add if true, the entry will be added if it does not
  316. * already exist, and the new entry's index returned
  317. */
  318. private int findComplexEntry(String owner, String name, String desc,
  319. int type, boolean add) {
  320. int classIndex = findClassEntry(owner, add);
  321. if (classIndex == 0)
  322. return 0;
  323. int descIndex = findNameAndTypeEntry(name, desc, add);
  324. if (descIndex == 0)
  325. return 0;
  326. Object key = null;
  327. switch (type) {
  328. case Entry.FIELD:
  329. key = new FieldKey(classIndex, descIndex);
  330. break;
  331. case Entry.METHOD:
  332. key = new MethodKey(classIndex, descIndex);
  333. break;
  334. case Entry.INTERFACEMETHOD:
  335. key = new InterfaceMethodKey(classIndex, descIndex);
  336. break;
  337. }
  338. int index = find(key);
  339. if (!add || index > 0)
  340. return index;
  341. Entry entry = null;
  342. switch (type) {
  343. case Entry.FIELD:
  344. entry = new FieldEntry(classIndex, descIndex);
  345. break;
  346. case Entry.METHOD:
  347. entry = new MethodEntry(classIndex, descIndex);
  348. break;
  349. case Entry.INTERFACEMETHOD:
  350. entry = new InterfaceMethodEntry(classIndex, descIndex);
  351. break;
  352. }
  353. return addEntry(key, entry);
  354. }
  355. public void acceptVisit(BCVisitor visit) {
  356. visit.enterConstantPool(this);
  357. Entry entry;
  358. for (Iterator itr = _entries.iterator(); itr.hasNext();) {
  359. entry = (Entry) itr.next();
  360. if (entry == null)
  361. continue;
  362. visit.enterEntry(entry);
  363. entry.acceptVisit(visit);
  364. visit.exitEntry(entry);
  365. }
  366. visit.exitConstantPool(this);
  367. }
  368. /**
  369. * Fill the constant pool from the given bytecode stream.
  370. */
  371. public void read(DataInput in) throws IOException {
  372. clear();
  373. int entryCount = in.readUnsignedShort();
  374. Entry entry;
  375. for (int i = 1; i < entryCount; i++) {
  376. entry = Entry.read(in);
  377. addEntry(entry);
  378. if (entry.isWide())
  379. i++;
  380. }
  381. }
  382. /**
  383. * Write the constant pool to the given bytecode stream.
  384. */
  385. public void write(DataOutput out) throws IOException {
  386. out.writeShort(_entries.size() + 1);
  387. Entry entry;
  388. for (Iterator itr = _entries.iterator(); itr.hasNext();) {
  389. entry = (Entry) itr.next();
  390. if (entry != null)
  391. Entry.write(entry, out);
  392. }
  393. }
  394. /**
  395. * Called by constant pool entries when they are mutated.
  396. */
  397. void modifyEntry(Object origKey, Entry entry) {
  398. _lookup.remove(origKey);
  399. _lookup.put(getKey(entry), entry);
  400. }
  401. /**
  402. * Returns the constant pool index of the entry with the given key.
  403. */
  404. private int find(Object key) {
  405. Entry entry = (Entry) _lookup.get(key);
  406. if (entry == null)
  407. return 0;
  408. return entry.getIndex();
  409. }
  410. /**
  411. * Return the hash key used for the specified entry.
  412. */
  413. static Object getKey(Entry entry) {
  414. switch (entry.getType()) {
  415. case Entry.CLASS:
  416. return new ClassKey(((ClassEntry) entry).getNameIndex());
  417. case Entry.FIELD:
  418. FieldEntry fe = (FieldEntry) entry;
  419. return new FieldKey(fe.getClassIndex(), fe.getNameAndTypeIndex());
  420. case Entry.METHOD:
  421. MethodEntry me = (MethodEntry) entry;
  422. return new MethodKey(me.getClassIndex(), me.getNameAndTypeIndex());
  423. case Entry.INTERFACEMETHOD:
  424. InterfaceMethodEntry ime = (InterfaceMethodEntry) entry;
  425. return new InterfaceMethodKey(ime.getClassIndex(),
  426. ime.getNameAndTypeIndex());
  427. case Entry.STRING:
  428. return new StringKey(((StringEntry) entry).getStringIndex());
  429. case Entry.INT:
  430. case Entry.FLOAT:
  431. case Entry.LONG:
  432. case Entry.DOUBLE:
  433. case Entry.UTF8:
  434. return ((ConstantEntry) entry).getConstant();
  435. case Entry.NAMEANDTYPE:
  436. NameAndTypeEntry nte = (NameAndTypeEntry) entry;
  437. return new NameAndTypeKey(nte.getNameIndex(),
  438. nte.getDescriptorIndex());
  439. default:
  440. return null;
  441. }
  442. }
  443. /**
  444. * Base class key for entries with one ptr to another entry.
  445. */
  446. private static abstract class PtrKey {
  447. private final int _index;
  448. public PtrKey(int index) {
  449. _index = index;
  450. }
  451. public int hashCode() {
  452. return _index;
  453. }
  454. public boolean equals(Object other) {
  455. if (other == this)
  456. return true;
  457. if (other.getClass() != getClass())
  458. return false;
  459. return ((PtrKey) other)._index == _index;
  460. }
  461. }
  462. /**
  463. * Key for string entries.
  464. */
  465. private static class StringKey extends PtrKey {
  466. public StringKey(int index) {
  467. super(index);
  468. }
  469. }
  470. /**
  471. * Key for class entries.
  472. */
  473. private static class ClassKey extends PtrKey {
  474. public ClassKey(int index) {
  475. super(index);
  476. }
  477. }
  478. /**
  479. * Base class key for entries with two ptr to other entries.
  480. */
  481. private static abstract class DoublePtrKey {
  482. private final int _index1;
  483. private final int _index2;
  484. public DoublePtrKey(int index1, int index2) {
  485. _index1 = index1;
  486. _index2 = index2;
  487. }
  488. public int hashCode() {
  489. return _index1 ^ _index2;
  490. }
  491. public boolean equals(Object other) {
  492. if (other == this)
  493. return true;
  494. if (other.getClass() != getClass())
  495. return false;
  496. DoublePtrKey key = (DoublePtrKey) other;
  497. return key._index1 == _index1 && key._index2 == _index2;
  498. }
  499. }
  500. /**
  501. * Key for name and type entries.
  502. */
  503. private static class NameAndTypeKey extends DoublePtrKey {
  504. public NameAndTypeKey(int index1, int index2) {
  505. super(index1, index2);
  506. }
  507. }
  508. /**
  509. * Key for field entries.
  510. */
  511. private static class FieldKey extends DoublePtrKey {
  512. public FieldKey(int index1, int index2) {
  513. super(index1, index2);
  514. }
  515. }
  516. /**
  517. * Key for method entries.
  518. */
  519. private static class MethodKey extends DoublePtrKey {
  520. public MethodKey(int index1, int index2) {
  521. super(index1, index2);
  522. }
  523. }
  524. /**
  525. * Key for interface method entries.
  526. */
  527. private static class InterfaceMethodKey extends DoublePtrKey {
  528. public InterfaceMethodKey(int index1, int index2) {
  529. super(index1, index2);
  530. }
  531. }
  532. }