001: /*
002: * Copyright 2004 Brian S O'Neill
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.cojen.classfile;
018:
019: import java.util.Collections;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Map;
024: import java.util.Set;
025: import java.util.Vector;
026: import java.io.DataInput;
027: import java.io.DataOutput;
028: import java.io.IOException;
029: import org.cojen.classfile.constant.ConstantClassInfo;
030: import org.cojen.classfile.constant.ConstantDoubleInfo;
031: import org.cojen.classfile.constant.ConstantFieldInfo;
032: import org.cojen.classfile.constant.ConstantFloatInfo;
033: import org.cojen.classfile.constant.ConstantIntegerInfo;
034: import org.cojen.classfile.constant.ConstantInterfaceMethodInfo;
035: import org.cojen.classfile.constant.ConstantLongInfo;
036: import org.cojen.classfile.constant.ConstantMethodInfo;
037: import org.cojen.classfile.constant.ConstantNameAndTypeInfo;
038: import org.cojen.classfile.constant.ConstantStringInfo;
039: import org.cojen.classfile.constant.ConstantUTFInfo;
040:
041: /**
042: * This class corresponds to the constant_pool structure as defined in
043: * section 4.4 of <i>The Java Virtual Machine Specification</i>.
044: *
045: * <p>ConstantPool entries are not written out in the order in which they were
046: * added to it. Instead, their ordering is changed such that String, Integer
047: * and Float constants are written out first. This provides a slight
048: * optimization for referencing these constants from a code attribute.
049: * It means that Opcode.LDC will more likely be used (one-byte index) than
050: * Opcode.LDC_W (two-byte index).
051: *
052: * @author Brian S O'Neill
053: * @see Opcode
054: */
055: public class ConstantPool {
056: // A set of ConstantInfo objects.
057: private Map mConstants = new HashMap();
058: // Indexed list of constants.
059: private Vector mIndexedConstants;
060: private int mEntries;
061:
062: // Preserve the order only if the constant pool was read in.
063: private boolean mPreserveOrder;
064:
065: ConstantPool() {
066: }
067:
068: private ConstantPool(Vector indexedConstants) {
069: mIndexedConstants = indexedConstants;
070:
071: int size = indexedConstants.size();
072: for (int i = 1; i < size; i++) {
073: ConstantInfo ci = (ConstantInfo) indexedConstants.get(i);
074: if (ci != null) {
075: mConstants.put(ci, ci);
076: mEntries += ci.getEntryCount();
077: }
078: }
079:
080: mPreserveOrder = true;
081: }
082:
083: /**
084: * Returns a constant from the pool by index, or null if not found. If this
085: * constant pool has not yet been written or was not created by the read
086: * method, indexes are not assigned, and an IllegalStateException is
087: * thrown.
088: *
089: * @throws ArrayIndexOutOfBoundsException if index is out of range.
090: * @throws IllegalStateException if indexes are not assigned
091: */
092: public ConstantInfo getConstant(int index) {
093: if (mIndexedConstants == null) {
094: throw new IllegalStateException(
095: "Constant pool indexes have not been assigned");
096: }
097:
098: return (ConstantInfo) mIndexedConstants.get(index);
099: }
100:
101: /**
102: * Returns all the constants in the pool, in no particular order.
103: */
104: public Set getAllConstants() {
105: return Collections.unmodifiableSet(mConstants.keySet());
106: }
107:
108: /**
109: * Returns the number of constants in the pool.
110: */
111: public int getSize() {
112: return mEntries;
113: }
114:
115: /**
116: * Get or create a constant from the constant pool representing a class.
117: */
118: public ConstantClassInfo addConstantClass(String className) {
119: return (ConstantClassInfo) addConstant(new ConstantClassInfo(
120: this , className));
121: }
122:
123: /**
124: * Get or create a constant from the constant pool representing an array
125: * class.
126: *
127: * @param dim Number of array dimensions.
128: */
129: public ConstantClassInfo addConstantClass(String className, int dim) {
130: return (ConstantClassInfo) addConstant(new ConstantClassInfo(
131: this , className, dim));
132: }
133:
134: /**
135: * Get or create a constant from the constant pool representing a class.
136: */
137: public ConstantClassInfo addConstantClass(TypeDesc type) {
138: return (ConstantClassInfo) addConstant(new ConstantClassInfo(
139: this , type));
140: }
141:
142: /**
143: * Get or create a constant from the constant pool representing a field in
144: * any class.
145: */
146: public ConstantFieldInfo addConstantField(String className,
147: String fieldName, TypeDesc type) {
148: ConstantInfo ci = new ConstantFieldInfo(
149: addConstantClass(className), addConstantNameAndType(
150: fieldName, type));
151: return (ConstantFieldInfo) addConstant(ci);
152: }
153:
154: /**
155: * Get or create a constant from the constant pool representing a method
156: * in any class. If the method returns void, set ret to null.
157: */
158: public ConstantMethodInfo addConstantMethod(String className,
159: String methodName, TypeDesc ret, TypeDesc[] params) {
160:
161: MethodDesc md = MethodDesc.forArguments(ret, params);
162: ConstantInfo ci = new ConstantMethodInfo(
163: addConstantClass(className), addConstantNameAndType(
164: methodName, md));
165: return (ConstantMethodInfo) addConstant(ci);
166: }
167:
168: /**
169: * Get or create a constant from the constant pool representing an
170: * interface method in any interface.
171: */
172: public ConstantInterfaceMethodInfo addConstantInterfaceMethod(
173: String className, String methodName, TypeDesc ret,
174: TypeDesc[] params) {
175:
176: MethodDesc md = MethodDesc.forArguments(ret, params);
177: ConstantInfo ci = new ConstantInterfaceMethodInfo(
178: addConstantClass(className), addConstantNameAndType(
179: methodName, md));
180: return (ConstantInterfaceMethodInfo) addConstant(ci);
181: }
182:
183: /**
184: * Get or create a constant from the constant pool representing a
185: * constructor in any class.
186: */
187: public ConstantMethodInfo addConstantConstructor(String className,
188: TypeDesc[] params) {
189: return addConstantMethod(className, "<init>", null, params);
190: }
191:
192: /**
193: * Get or create a constant integer from the constant pool.
194: */
195: public ConstantIntegerInfo addConstantInteger(int value) {
196: return (ConstantIntegerInfo) addConstant(new ConstantIntegerInfo(
197: value));
198: }
199:
200: /**
201: * Get or create a constant long from the constant pool.
202: */
203: public ConstantLongInfo addConstantLong(long value) {
204: return (ConstantLongInfo) addConstant(new ConstantLongInfo(
205: value));
206: }
207:
208: /**
209: * Get or create a constant float from the constant pool.
210: */
211: public ConstantFloatInfo addConstantFloat(float value) {
212: return (ConstantFloatInfo) addConstant(new ConstantFloatInfo(
213: value));
214: }
215:
216: /**
217: * Get or create a constant double from the constant pool.
218: */
219: public ConstantDoubleInfo addConstantDouble(double value) {
220: return (ConstantDoubleInfo) addConstant(new ConstantDoubleInfo(
221: value));
222: }
223:
224: /**
225: * Get or create a constant string from the constant pool.
226: */
227: public ConstantStringInfo addConstantString(String str) {
228: return (ConstantStringInfo) addConstant(new ConstantStringInfo(
229: this , str));
230: }
231:
232: /**
233: * Get or create a constant UTF string from the constant pool.
234: */
235: public ConstantUTFInfo addConstantUTF(String str) {
236: return (ConstantUTFInfo) addConstant(new ConstantUTFInfo(str));
237: }
238:
239: /**
240: * Get or create a constant name and type structure from the constant pool.
241: */
242: public ConstantNameAndTypeInfo addConstantNameAndType(String name,
243: Descriptor type) {
244: return (ConstantNameAndTypeInfo) addConstant(new ConstantNameAndTypeInfo(
245: this , name, type));
246: }
247:
248: /**
249: * Get or create a constant name and type structure from the constant pool.
250: */
251: public ConstantNameAndTypeInfo addConstantNameAndType(
252: ConstantUTFInfo nameConstant, ConstantUTFInfo descConstant) {
253: return (ConstantNameAndTypeInfo) addConstant(new ConstantNameAndTypeInfo(
254: nameConstant, descConstant));
255: }
256:
257: /**
258: * Will only insert into the pool if the constant is not already in the
259: * pool.
260: *
261: * @return The actual constant in the pool.
262: */
263: public ConstantInfo addConstant(ConstantInfo constant) {
264: ConstantInfo info = (ConstantInfo) mConstants.get(constant);
265: if (info != null) {
266: return info;
267: }
268:
269: int entryCount = constant.getEntryCount();
270:
271: if (mIndexedConstants != null && mPreserveOrder) {
272: int size = mIndexedConstants.size();
273: mIndexedConstants.setSize(size + entryCount);
274: mIndexedConstants.set(size, constant);
275: constant.mIndex = size;
276: }
277:
278: mConstants.put(constant, constant);
279: mEntries += entryCount;
280:
281: return constant;
282: }
283:
284: public void writeTo(DataOutput dout) throws IOException {
285: // Write out the size (number of entries) of the constant pool.
286:
287: int size = getSize() + 1; // add one because constant 0 is reserved
288: if (size >= 65535) {
289: throw new IllegalStateException(
290: "Constant pool entry count cannot exceed 65535: "
291: + size);
292: }
293: dout.writeShort(size);
294:
295: if (mIndexedConstants == null || !mPreserveOrder) {
296: mIndexedConstants = new Vector(size);
297: mIndexedConstants.setSize(size);
298: int index = 1; // one-based constant pool index
299:
300: // First write constants of higher priority -- String, Integer,
301: // Float.
302: // This is a slight optimization. It means that Opcode.LDC will
303: // more likely be used (one-byte index) than Opcode.LDC_W (two-byte
304: // index).
305:
306: Iterator it = mConstants.keySet().iterator();
307: while (it.hasNext()) {
308: ConstantInfo constant = (ConstantInfo) it.next();
309: if (constant.hasPriority()) {
310: constant.mIndex = index;
311: mIndexedConstants.set(index, constant);
312: index += constant.getEntryCount();
313: }
314: }
315:
316: // Now write all non-priority constants.
317:
318: it = mConstants.keySet().iterator();
319: while (it.hasNext()) {
320: ConstantInfo constant = (ConstantInfo) it.next();
321: if (!constant.hasPriority()) {
322: constant.mIndex = index;
323: mIndexedConstants.set(index, constant);
324: index += constant.getEntryCount();
325: }
326: }
327: }
328:
329: // Now actually write out the constants since the indexes have been
330: // resolved.
331:
332: for (int i = 1; i < size; i++) {
333: Object obj = mIndexedConstants.get(i);
334: if (obj != null) {
335: ((ConstantInfo) obj).writeTo(dout);
336: }
337: }
338: }
339:
340: public static ConstantPool readFrom(DataInput din)
341: throws IOException {
342: int size = din.readUnsignedShort();
343: Vector constants = new Vector(size);
344: constants.setSize(size);
345:
346: int index = 1;
347: while (index < size) {
348: int tag = din.readByte();
349: int entryCount = 1;
350: Object constant;
351:
352: switch (tag) {
353: case ConstantInfo.TAG_UTF8:
354: constant = new ConstantUTFInfo(din.readUTF());
355: break;
356: case ConstantInfo.TAG_INTEGER:
357: constant = new ConstantIntegerInfo(din.readInt());
358: break;
359: case ConstantInfo.TAG_FLOAT:
360: constant = new ConstantFloatInfo(din.readFloat());
361: break;
362: case ConstantInfo.TAG_LONG:
363: constant = new ConstantLongInfo(din.readLong());
364: entryCount++;
365: break;
366: case ConstantInfo.TAG_DOUBLE:
367: constant = new ConstantDoubleInfo(din.readDouble());
368: entryCount++;
369: break;
370:
371: case ConstantInfo.TAG_CLASS:
372: case ConstantInfo.TAG_STRING:
373: constant = new TempEntry(tag, din.readUnsignedShort());
374: break;
375:
376: case ConstantInfo.TAG_FIELD:
377: case ConstantInfo.TAG_METHOD:
378: case ConstantInfo.TAG_INTERFACE_METHOD:
379: case ConstantInfo.TAG_NAME_AND_TYPE:
380: constant = new TempEntry(tag, (din.readShort() << 16)
381: | (din.readUnsignedShort()));
382: break;
383:
384: default:
385: throw new IOException("Invalid constant pool tag: "
386: + tag);
387: }
388:
389: if (constant instanceof ConstantInfo) {
390: ((ConstantInfo) constant).mIndex = index;
391: }
392:
393: constants.set(index, constant);
394: index += entryCount;
395: }
396:
397: for (index = 1; index < size; index++) {
398: resolve(constants, index);
399: }
400:
401: return new ConstantPool(constants);
402: }
403:
404: private static ConstantInfo resolve(List constants, int index) {
405: Object constant = constants.get(index);
406: if (constant == null) {
407: return null;
408: }
409:
410: if (constant instanceof ConstantInfo) {
411: return (ConstantInfo) constant;
412: }
413:
414: TempEntry entry = (TempEntry) constant;
415: int data = entry.mData;
416: int index1 = data & 0xffff;
417:
418: ConstantInfo ci1;
419: Object constant1 = constants.get(index1);
420:
421: if (constant1 instanceof ConstantInfo) {
422: ci1 = (ConstantInfo) constant1;
423: } else {
424: ci1 = resolve(constants, index1);
425: }
426:
427: ConstantInfo ci = null;
428:
429: switch (entry.mTag) {
430: case ConstantInfo.TAG_CLASS:
431: ci = new ConstantClassInfo((ConstantUTFInfo) ci1);
432: break;
433: case ConstantInfo.TAG_STRING:
434: ci = new ConstantStringInfo((ConstantUTFInfo) ci1);
435: break;
436:
437: case ConstantInfo.TAG_FIELD:
438: case ConstantInfo.TAG_METHOD:
439: case ConstantInfo.TAG_INTERFACE_METHOD:
440: case ConstantInfo.TAG_NAME_AND_TYPE:
441: int index2 = data >> 16;
442:
443: ConstantInfo ci2;
444: Object constant2 = constants.get(index2);
445:
446: if (constant2 instanceof ConstantInfo) {
447: ci2 = (ConstantInfo) constant2;
448: } else {
449: ci2 = resolve(constants, index2);
450: }
451:
452: switch (entry.mTag) {
453: case ConstantInfo.TAG_FIELD:
454: ci = new ConstantFieldInfo((ConstantClassInfo) ci2,
455: (ConstantNameAndTypeInfo) ci1);
456: break;
457: case ConstantInfo.TAG_METHOD:
458: ci = new ConstantMethodInfo((ConstantClassInfo) ci2,
459: (ConstantNameAndTypeInfo) ci1);
460: break;
461: case ConstantInfo.TAG_INTERFACE_METHOD:
462: ci = new ConstantInterfaceMethodInfo(
463: (ConstantClassInfo) ci2,
464: (ConstantNameAndTypeInfo) ci1);
465: break;
466: case ConstantInfo.TAG_NAME_AND_TYPE:
467: ci = new ConstantNameAndTypeInfo((ConstantUTFInfo) ci2,
468: (ConstantUTFInfo) ci1);
469: break;
470: }
471:
472: break;
473: }
474:
475: ci.mIndex = index;
476: constants.set(index, ci);
477:
478: return ci;
479: }
480:
481: private static class TempEntry {
482: public int mTag;
483: public int mData;
484:
485: public TempEntry(int tag, int data) {
486: mTag = tag;
487: mData = data;
488: }
489: }
490: }
|