1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.bcel.classfile;
20
21 import java.io.DataInput;
22 import java.io.DataOutputStream;
23 import java.io.IOException;
24 import java.util.Arrays;
25 import java.util.Iterator;
26
27 import org.apache.bcel.Const;
28
29
30
31
32
33
34
35
36
37 public class ConstantPool implements Cloneable, Node, Iterable<Constant> {
38
39 private static String escape(final String str) {
40 final int len = str.length();
41 final StringBuilder buf = new StringBuilder(len + 5);
42 final char[] ch = str.toCharArray();
43 for (int i = 0; i < len; i++) {
44 switch (ch[i]) {
45 case '\n':
46 buf.append("\\n");
47 break;
48 case '\r':
49 buf.append("\\r");
50 break;
51 case '\t':
52 buf.append("\\t");
53 break;
54 case '\b':
55 buf.append("\\b");
56 break;
57 case '"':
58 buf.append("\\\"");
59 break;
60 default:
61 buf.append(ch[i]);
62 }
63 }
64 return buf.toString();
65 }
66
67 private Constant[] constantPool;
68
69
70
71
72
73
74 public ConstantPool(final Constant... constantPool) {
75 setConstantPool(constantPool);
76 }
77
78
79
80
81
82
83
84 public ConstantPool(final DataInput input) throws IOException {
85 byte tag;
86 final int constantPoolCount = input.readUnsignedShort();
87 constantPool = new Constant[constantPoolCount];
88
89
90
91
92 for (int i = 1; i < constantPoolCount; i++) {
93 constantPool[i] = Constant.readConstant(input);
94
95
96
97
98
99
100 tag = constantPool[i].getTag();
101 if (tag == Const.CONSTANT_Double || tag == Const.CONSTANT_Long) {
102 i++;
103 }
104 }
105 }
106
107
108
109
110
111
112
113 @Override
114 public void accept(final Visitor v) {
115 v.visitConstantPool(this);
116 }
117
118
119
120
121
122
123
124
125 public String constantToString(Constant c) throws IllegalArgumentException {
126 final String str;
127 final int i;
128 final byte tag = c.getTag();
129 switch (tag) {
130 case Const.CONSTANT_Class:
131 i = ((ConstantClass) c).getNameIndex();
132 c = getConstantUtf8(i);
133 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
134 break;
135 case Const.CONSTANT_String:
136 i = ((ConstantString) c).getStringIndex();
137 c = getConstantUtf8(i);
138 str = "\"" + escape(((ConstantUtf8) c).getBytes()) + "\"";
139 break;
140 case Const.CONSTANT_Utf8:
141 str = ((ConstantUtf8) c).getBytes();
142 break;
143 case Const.CONSTANT_Double:
144 str = String.valueOf(((ConstantDouble) c).getBytes());
145 break;
146 case Const.CONSTANT_Float:
147 str = String.valueOf(((ConstantFloat) c).getBytes());
148 break;
149 case Const.CONSTANT_Long:
150 str = String.valueOf(((ConstantLong) c).getBytes());
151 break;
152 case Const.CONSTANT_Integer:
153 str = String.valueOf(((ConstantInteger) c).getBytes());
154 break;
155 case Const.CONSTANT_NameAndType:
156 str = constantToString(((ConstantNameAndType) c).getNameIndex(), Const.CONSTANT_Utf8) + " "
157 + constantToString(((ConstantNameAndType) c).getSignatureIndex(), Const.CONSTANT_Utf8);
158 break;
159 case Const.CONSTANT_InterfaceMethodref:
160 case Const.CONSTANT_Methodref:
161 case Const.CONSTANT_Fieldref:
162 str = constantToString(((ConstantCP) c).getClassIndex(), Const.CONSTANT_Class) + "."
163 + constantToString(((ConstantCP) c).getNameAndTypeIndex(), Const.CONSTANT_NameAndType);
164 break;
165 case Const.CONSTANT_MethodHandle:
166
167
168 final ConstantMethodHandle cmh = (ConstantMethodHandle) c;
169 str = Const.getMethodHandleName(cmh.getReferenceKind()) + " "
170 + constantToString(cmh.getReferenceIndex(), getConstant(cmh.getReferenceIndex()).getTag());
171 break;
172 case Const.CONSTANT_MethodType:
173 final ConstantMethodType cmt = (ConstantMethodType) c;
174 str = constantToString(cmt.getDescriptorIndex(), Const.CONSTANT_Utf8);
175 break;
176 case Const.CONSTANT_InvokeDynamic:
177 final ConstantInvokeDynamic cid = (ConstantInvokeDynamic) c;
178 str = cid.getBootstrapMethodAttrIndex() + ":" + constantToString(cid.getNameAndTypeIndex(), Const.CONSTANT_NameAndType);
179 break;
180 case Const.CONSTANT_Dynamic:
181 final ConstantDynamic cd = (ConstantDynamic) c;
182 str = cd.getBootstrapMethodAttrIndex() + ":" + constantToString(cd.getNameAndTypeIndex(), Const.CONSTANT_NameAndType);
183 break;
184 case Const.CONSTANT_Module:
185 i = ((ConstantModule) c).getNameIndex();
186 c = getConstantUtf8(i);
187 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
188 break;
189 case Const.CONSTANT_Package:
190 i = ((ConstantPackage) c).getNameIndex();
191 c = getConstantUtf8(i);
192 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
193 break;
194 default:
195 throw new IllegalArgumentException("Unknown constant type " + tag);
196 }
197 return str;
198 }
199
200
201
202
203
204
205
206
207 public String constantToString(final int index, final byte tag) {
208 return constantToString(getConstant(index, tag));
209 }
210
211
212
213
214
215
216 public ConstantPool copy() {
217 ConstantPool c = null;
218 try {
219 c = (ConstantPool) clone();
220 c.constantPool = new Constant[constantPool.length];
221 for (int i = 1; i < constantPool.length; i++) {
222 if (constantPool[i] != null) {
223 c.constantPool[i] = constantPool[i].copy();
224 }
225 }
226 } catch (final CloneNotSupportedException e) {
227
228 }
229 return c;
230 }
231
232
233
234
235
236
237
238 public void dump(final DataOutputStream file) throws IOException {
239
240
241
242
243 final int size = Math.min(constantPool.length, Const.MAX_CP_ENTRIES);
244
245 file.writeShort(size);
246 for (int i = 1; i < size; i++) {
247 if (constantPool[i] != null) {
248 constantPool[i].dump(file);
249 }
250 }
251 }
252
253
254
255
256
257
258
259
260
261
262 @SuppressWarnings("unchecked")
263 public <T extends Constant> T getConstant(final int index) throws ClassFormatException {
264 return (T) getConstant(index, Constant.class);
265 }
266
267
268
269
270
271
272
273
274
275
276
277 @SuppressWarnings("unchecked")
278 public <T extends Constant> T getConstant(final int index, final byte tag) throws ClassFormatException {
279 return (T) getConstant(index, tag, Constant.class);
280 }
281
282
283
284
285
286
287
288
289
290
291
292
293
294 public <T extends Constant> T getConstant(final int index, final byte tag, final Class<T> castTo) throws ClassFormatException {
295 final T c = getConstant(index);
296 if (c == null || c.getTag() != tag) {
297 throw new ClassFormatException("Expected class '" + Const.getConstantName(tag) + "' at index " + index + " and got " + c);
298 }
299 return c;
300 }
301
302
303
304
305
306
307
308
309
310
311
312
313 public <T extends Constant> T getConstant(final int index, final Class<T> castTo) throws ClassFormatException {
314 if (index >= constantPool.length || index < 1) {
315 throw new ClassFormatException("Invalid constant pool reference using index: " + index + ". Constant pool size is: " + constantPool.length);
316 }
317 if (constantPool[index] != null && !castTo.isAssignableFrom(constantPool[index].getClass())) {
318 throw new ClassFormatException("Invalid constant pool reference at index: " + index +
319 ". Expected " + castTo + " but was " + constantPool[index].getClass());
320 }
321 if (index > 1) {
322 final Constant prev = constantPool[index - 1];
323 if (prev != null && (prev.getTag() == Const.CONSTANT_Double || prev.getTag() == Const.CONSTANT_Long)) {
324 throw new ClassFormatException("Constant pool at index " + index + " is invalid. The index is unused due to the preceeding "
325 + Const.getConstantName(prev.getTag()) + ".");
326 }
327 }
328
329 final T c = castTo.cast(constantPool[index]);
330 if (c == null) {
331 throw new ClassFormatException("Constant pool at index " + index + " is null.");
332 }
333 return c;
334 }
335
336
337
338
339
340
341
342
343
344 public ConstantInteger getConstantInteger(final int index) {
345 return getConstant(index, Const.CONSTANT_Integer, ConstantInteger.class);
346 }
347
348
349
350
351
352
353
354 public Constant[] getConstantPool() {
355 return constantPool;
356 }
357
358
359
360
361
362
363
364
365
366
367
368
369 public String getConstantString(final int index, final byte tag) throws IllegalArgumentException {
370 final int i;
371
372
373
374
375
376 switch (tag) {
377 case Const.CONSTANT_Class:
378 i = getConstant(index, ConstantClass.class).getNameIndex();
379 break;
380 case Const.CONSTANT_String:
381 i = getConstant(index, ConstantString.class).getStringIndex();
382 break;
383 case Const.CONSTANT_Module:
384 i = getConstant(index, ConstantModule.class).getNameIndex();
385 break;
386 case Const.CONSTANT_Package:
387 i = getConstant(index, ConstantPackage.class).getNameIndex();
388 break;
389 case Const.CONSTANT_Utf8:
390 return getConstantUtf8(index).getBytes();
391 default:
392 throw new IllegalArgumentException("getConstantString called with illegal tag " + tag);
393 }
394
395 return getConstantUtf8(i).getBytes();
396 }
397
398
399
400
401
402
403
404
405
406 public ConstantUtf8 getConstantUtf8(final int index) throws ClassFormatException {
407 return getConstant(index, Const.CONSTANT_Utf8, ConstantUtf8.class);
408 }
409
410
411
412
413
414
415 public int getLength() {
416 return constantPool.length;
417 }
418
419 @Override
420 public Iterator<Constant> iterator() {
421 return Arrays.stream(constantPool).iterator();
422 }
423
424
425
426
427
428
429
430 public void setConstant(final int index, final Constant constant) {
431 constantPool[index] = constant;
432 }
433
434
435
436
437
438
439 public void setConstantPool(final Constant[] constantPool) {
440 this.constantPool = constantPool != null ? constantPool : Constant.EMPTY_ARRAY;
441 }
442
443
444
445
446 @Override
447 public String toString() {
448 final StringBuilder buf = new StringBuilder();
449 for (int i = 1; i < constantPool.length; i++) {
450 buf.append(i).append(")").append(constantPool[i]).append("\n");
451 }
452 return buf.toString();
453 }
454 }