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 final byte referenceTag = getConstant(cmh.getReferenceIndex()).getTag();
170
171
172
173
174 if (referenceTag != Const.CONSTANT_Fieldref && referenceTag != Const.CONSTANT_Methodref && referenceTag != Const.CONSTANT_InterfaceMethodref) {
175 throw new ClassFormatException(
176 "Constant pool at index " + cmh.getReferenceIndex() + " has an invalid tag " + referenceTag + " for a CONSTANT_MethodHandle reference");
177 }
178 str = Const.getMethodHandleName(cmh.getReferenceKind()) + " " + constantToString(cmh.getReferenceIndex(), referenceTag);
179 break;
180 case Const.CONSTANT_MethodType:
181 final ConstantMethodType cmt = (ConstantMethodType) c;
182 str = constantToString(cmt.getDescriptorIndex(), Const.CONSTANT_Utf8);
183 break;
184 case Const.CONSTANT_InvokeDynamic:
185 final ConstantInvokeDynamic cid = (ConstantInvokeDynamic) c;
186 str = cid.getBootstrapMethodAttrIndex() + ":" + constantToString(cid.getNameAndTypeIndex(), Const.CONSTANT_NameAndType);
187 break;
188 case Const.CONSTANT_Dynamic:
189 final ConstantDynamic cd = (ConstantDynamic) c;
190 str = cd.getBootstrapMethodAttrIndex() + ":" + constantToString(cd.getNameAndTypeIndex(), Const.CONSTANT_NameAndType);
191 break;
192 case Const.CONSTANT_Module:
193 i = ((ConstantModule) c).getNameIndex();
194 c = getConstantUtf8(i);
195 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
196 break;
197 case Const.CONSTANT_Package:
198 i = ((ConstantPackage) c).getNameIndex();
199 c = getConstantUtf8(i);
200 str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
201 break;
202 default:
203 throw new IllegalArgumentException("Unknown constant type " + tag);
204 }
205 return str;
206 }
207
208
209
210
211
212
213
214
215 public String constantToString(final int index, final byte tag) {
216 return constantToString(getConstant(index, tag));
217 }
218
219
220
221
222
223
224 public ConstantPool copy() {
225 ConstantPool c = null;
226 try {
227 c = (ConstantPool) clone();
228 c.constantPool = new Constant[constantPool.length];
229 for (int i = 1; i < constantPool.length; i++) {
230 if (constantPool[i] != null) {
231 c.constantPool[i] = constantPool[i].copy();
232 }
233 }
234 } catch (final CloneNotSupportedException e) {
235
236 }
237 return c;
238 }
239
240
241
242
243
244
245
246 public void dump(final DataOutputStream file) throws IOException {
247
248
249
250
251
252 if (constantPool.length > Const.MAX_CP_ENTRIES) {
253 throw new ClassFormatException("Constant pool size " + constantPool.length + " exceeds the u2 maximum of " + Const.MAX_CP_ENTRIES);
254 }
255 file.writeShort(constantPool.length);
256 for (int i = 1; i < constantPool.length; i++) {
257 if (constantPool[i] != null) {
258 constantPool[i].dump(file);
259 }
260 }
261 }
262
263
264
265
266
267
268
269
270
271
272 @SuppressWarnings("unchecked")
273 public <T extends Constant> T getConstant(final int index) throws ClassFormatException {
274 return (T) getConstant(index, Constant.class);
275 }
276
277
278
279
280
281
282
283
284
285
286
287 @SuppressWarnings("unchecked")
288 public <T extends Constant> T getConstant(final int index, final byte tag) throws ClassFormatException {
289 return (T) getConstant(index, tag, Constant.class);
290 }
291
292
293
294
295
296
297
298
299
300
301
302
303
304 public <T extends Constant> T getConstant(final int index, final byte tag, final Class<T> castTo) throws ClassFormatException {
305 final T c = getConstant(index);
306 if (c == null || c.getTag() != tag) {
307 throw new ClassFormatException("Expected class '" + Const.getConstantName(tag) + "' at index " + index + " and got " + c);
308 }
309 return c;
310 }
311
312
313
314
315
316
317
318
319
320
321
322
323 public <T extends Constant> T getConstant(final int index, final Class<T> castTo) throws ClassFormatException {
324 if (index >= constantPool.length || index < 1) {
325 throw new ClassFormatException("Invalid constant pool reference using index: " + index + ". Constant pool size is: " + constantPool.length);
326 }
327 if (constantPool[index] != null && !castTo.isAssignableFrom(constantPool[index].getClass())) {
328 throw new ClassFormatException("Invalid constant pool reference at index: " + index +
329 ". Expected " + castTo + " but was " + constantPool[index].getClass());
330 }
331 if (index > 1) {
332 final Constant prev = constantPool[index - 1];
333 if (prev != null && (prev.getTag() == Const.CONSTANT_Double || prev.getTag() == Const.CONSTANT_Long)) {
334 throw new ClassFormatException("Constant pool at index " + index + " is invalid. The index is unused due to the preceeding "
335 + Const.getConstantName(prev.getTag()) + ".");
336 }
337 }
338
339 final T c = castTo.cast(constantPool[index]);
340 if (c == null) {
341 throw new ClassFormatException("Constant pool at index " + index + " is null.");
342 }
343 return c;
344 }
345
346
347
348
349
350
351
352
353
354 public ConstantInteger getConstantInteger(final int index) {
355 return getConstant(index, Const.CONSTANT_Integer, ConstantInteger.class);
356 }
357
358
359
360
361
362
363
364 public Constant[] getConstantPool() {
365 return constantPool;
366 }
367
368
369
370
371
372
373
374
375
376
377
378
379 public String getConstantString(final int index, final byte tag) throws IllegalArgumentException {
380 final int i;
381
382
383
384
385
386 switch (tag) {
387 case Const.CONSTANT_Class:
388 i = getConstant(index, ConstantClass.class).getNameIndex();
389 break;
390 case Const.CONSTANT_String:
391 i = getConstant(index, ConstantString.class).getStringIndex();
392 break;
393 case Const.CONSTANT_Module:
394 i = getConstant(index, ConstantModule.class).getNameIndex();
395 break;
396 case Const.CONSTANT_Package:
397 i = getConstant(index, ConstantPackage.class).getNameIndex();
398 break;
399 case Const.CONSTANT_Utf8:
400 return getConstantUtf8(index).getBytes();
401 default:
402 throw new IllegalArgumentException("getConstantString called with illegal tag " + tag);
403 }
404
405 return getConstantUtf8(i).getBytes();
406 }
407
408
409
410
411
412
413
414
415
416 public ConstantUtf8 getConstantUtf8(final int index) throws ClassFormatException {
417 return getConstant(index, Const.CONSTANT_Utf8, ConstantUtf8.class);
418 }
419
420
421
422
423
424
425 public int getLength() {
426 return constantPool.length;
427 }
428
429 @Override
430 public Iterator<Constant> iterator() {
431 return Arrays.stream(constantPool).iterator();
432 }
433
434
435
436
437
438
439
440 public void setConstant(final int index, final Constant constant) {
441 constantPool[index] = constant;
442 }
443
444
445
446
447
448
449 public void setConstantPool(final Constant[] constantPool) {
450 this.constantPool = constantPool != null ? constantPool : Constant.EMPTY_ARRAY;
451 }
452
453
454
455
456 @Override
457 public String toString() {
458 final StringBuilder buf = new StringBuilder();
459 for (int i = 1; i < constantPool.length; i++) {
460 buf.append(i).append(")").append(constantPool[i]).append("\n");
461 }
462 return buf.toString();
463 }
464 }