1
2
3 package com.atlassian.plugin.osgi.util;
4
5 import java.io.*;
6 import java.nio.*;
7 import java.util.*;
8
9
10
11
12 public class Clazz {
13
14 static protected class Assoc {
15 Assoc(byte tag, int a, int b) {
16 this.tag = tag;
17 this.a = a;
18 this.b = b;
19 }
20
21 byte tag;
22 int a;
23 int b;
24 }
25
26 final static byte SkipTable[] = { 0,
27 -1,
28
29 -1,
30 4,
31 4,
32 8,
33 8,
34 -1,
35 2,
36 4,
37 4,
38 4,
39 4,
40 };
41
42 String className;
43 Object pool[];
44 int intPool[];
45 Map packageImports = new HashMap();
46 Set classImports = new HashSet();
47 String path;
48
49
50
51
52 int minor = 0;
53 int major = 0;
54
55 String sourceFile;
56 Set xref;
57 Set classes;
58 Set descriptors;
59 int forName = 0;
60 int class$ = 0;
61
62 public Clazz(String path) {
63 this.path = path;
64 }
65
66 public Clazz(String path, InputStream in) throws IOException {
67 this.path = path;
68 DataInputStream din = new DataInputStream(in);
69 parseClassFile(din);
70 din.close();
71 }
72
73 Set parseClassFile(DataInputStream in) throws IOException {
74 xref = new HashSet();
75 classes = new HashSet();
76 descriptors = new HashSet();
77
78 boolean crawl = false;
79 int magic = in.readInt();
80 if (magic != 0xCAFEBABE)
81 throw new IOException("Not a valid class file (no CAFEBABE header)");
82
83 minor = in.readUnsignedShort();
84 major = in.readUnsignedShort();
85 int count = in.readUnsignedShort();
86 pool = new Object[count];
87 intPool = new int[count];
88
89 process: for (int poolIndex = 1; poolIndex < count; poolIndex++) {
90 byte tag = in.readByte();
91 switch (tag) {
92 case 0:
93 break process;
94 case 1:
95 constantUtf8(in, poolIndex);
96 break;
97
98
99
100
101 case 5:
102 constantLong(in, poolIndex);
103 poolIndex++;
104 break;
105
106 case 6:
107 constantDouble(in, poolIndex);
108 poolIndex++;
109 break;
110
111 case 7:
112 constantClass(in, poolIndex);
113 break;
114
115 case 8:
116 constantString(in, poolIndex);
117 break;
118
119 case 10:
120 methodRef(in, poolIndex);
121 break;
122
123
124 case 12:
125 nameAndType(in, poolIndex, tag);
126 break;
127
128
129
130
131 default:
132 if (tag == 2)
133 throw new IOException("Invalid tag " + tag);
134 in.skipBytes(SkipTable[tag]);
135 break;
136 }
137 }
138
139 pool(pool, intPool);
140
141
142
143
144
145 int access_flags = in.readUnsignedShort();
146 int this_class = in.readUnsignedShort();
147 int super_class = in.readUnsignedShort();
148 String supr =(String) pool[intPool[super_class]];
149 if ( supr != null )
150 addReference(supr);
151
152 className = (String) pool[intPool[this_class]];
153
154 int interfacesCount = in.readUnsignedShort();
155 in.skipBytes(interfacesCount * 2);
156
157 int fieldsCount = in.readUnsignedShort();
158 for (int i = 0; i < fieldsCount; i++) {
159 access_flags = in.readUnsignedShort();
160 int name_index = in.readUnsignedShort();
161 int descriptor_index = in.readUnsignedShort();
162
163
164
165
166
167
168
169
170
171
172
173 String name = pool[name_index].toString();
174 if (name.startsWith("class$")) {
175 crawl = true;
176 }
177
178 descriptors.add(new Integer(descriptor_index));
179 doAttributes(in, false);
180 }
181
182
183
184
185
186
187
188 if (crawl) {
189 forName = findMethod("java/lang/Class", "forName",
190 "(Ljava/lang/String;)Ljava/lang/Class;");
191 class$ = findMethod(className, "class$",
192 "(Ljava/lang/String;)Ljava/lang/Class;");
193 }
194
195
196
197
198 int methodCount = in.readUnsignedShort();
199 for (int i = 0; i < methodCount; i++) {
200 access_flags = in.readUnsignedShort();
201 int name_index = in.readUnsignedShort();
202 int descriptor_index = in.readUnsignedShort();
203 String s = (String) pool[name_index];
204 descriptors.add(new Integer(descriptor_index));
205 doAttributes(in, crawl);
206 }
207
208 doAttributes(in, false);
209
210
211
212
213
214
215 for (Iterator e = classes.iterator(); e.hasNext();) {
216 int class_index = ((Integer) e.next()).shortValue();
217 doClassReference((String) pool[class_index]);
218 }
219
220
221
222
223
224 for (Iterator e = descriptors.iterator(); e.hasNext();) {
225 Integer index = (Integer) e.next();
226 String prototype = (String) pool[index.intValue()];
227 if (prototype != null)
228 parseDescriptor(prototype);
229 else
230 System.err.println("Unrecognized descriptor: " + index);
231 }
232 Set xref = this.xref;
233 reset();
234 return xref;
235 }
236
237 protected void pool(Object[] pool, int[] intPool) {
238 }
239
240
241
242
243
244
245
246 protected void nameAndType(DataInputStream in, int poolIndex, byte tag)
247 throws IOException {
248 int name_index = in.readUnsignedShort();
249 int descriptor_index = in.readUnsignedShort();
250 descriptors.add(new Integer(descriptor_index));
251 pool[poolIndex] = new Assoc(tag, name_index, descriptor_index);
252 }
253
254
255
256
257
258
259
260 private void methodRef(DataInputStream in, int poolIndex)
261 throws IOException {
262 int class_index = in.readUnsignedShort();
263 int name_and_type_index = in.readUnsignedShort();
264 pool[poolIndex] = new Assoc((byte) 10, class_index, name_and_type_index);
265 }
266
267
268
269
270
271
272 private void constantString(DataInputStream in, int poolIndex)
273 throws IOException {
274 int string_index = in.readUnsignedShort();
275 intPool[poolIndex] = string_index;
276 }
277
278
279
280
281
282
283 protected void constantClass(DataInputStream in, int poolIndex)
284 throws IOException {
285 int class_index = in.readUnsignedShort();
286 classes.add(new Integer(class_index));
287 intPool[poolIndex] = class_index;
288 }
289
290
291
292
293
294 protected void constantDouble(DataInputStream in, int poolIndex)
295 throws IOException {
296 in.skipBytes(8);
297 }
298
299
300
301
302
303 protected void constantLong(DataInputStream in, int poolIndex)
304 throws IOException {
305 in.skipBytes(8);
306 }
307
308
309
310
311
312
313 protected void constantUtf8(DataInputStream in, int poolIndex)
314 throws IOException {
315
316 String name = in.readUTF();
317 xref.add(name);
318 pool[poolIndex] = name;
319 }
320
321
322
323
324
325
326
327
328
329
330 private int findMethod(String clazz, String methodname, String descriptor) {
331 for (int i = 1; i < pool.length; i++) {
332 if (pool[i] instanceof Assoc) {
333 Assoc methodref = (Assoc) pool[i];
334 if (methodref.tag == 10) {
335
336 int class_index = methodref.a;
337 int class_name_index = intPool[class_index];
338 if (clazz.equals(pool[class_name_index])) {
339 int name_and_type_index = methodref.b;
340 Assoc name_and_type = (Assoc) pool[name_and_type_index];
341 if (name_and_type.tag == 12) {
342
343 int name_index = name_and_type.a;
344 int type_index = name_and_type.b;
345 if (methodname.equals(pool[name_index])) {
346 if (descriptor.equals(pool[type_index])) {
347 return i;
348 }
349 }
350 }
351 }
352 }
353 }
354 }
355 return -1;
356 }
357
358 private void doClassReference(String next) {
359 if (next != null) {
360 String normalized = normalize(next);
361 if (normalized != null) {
362 classReference(normalized);
363 }
364 } else
365 throw new IllegalArgumentException("Invalid class, parent=");
366 }
367
368
369
370
371
372
373
374
375 private void doAttributes(DataInputStream in, boolean crawl)
376 throws IOException {
377 int attributesCount = in.readUnsignedShort();
378 for (int j = 0; j < attributesCount; j++) {
379
380 doAttribute(in, crawl);
381 }
382 }
383
384
385
386
387
388
389
390
391 private void doAttribute(DataInputStream in, boolean crawl)
392 throws IOException {
393 int attribute_name_index = in.readUnsignedShort();
394 String attributeName = (String) pool[attribute_name_index];
395 long attribute_length = in.readInt();
396 attribute_length &= 0xFFFF;
397 if ("RuntimeVisibleAnnotations".equals(attributeName))
398 doAnnotations(in);
399 else if ("RuntimeVisibleParameterAnnotations".equals(attributeName))
400 doParameterAnnotations(in);
401 else if ("SourceFile".equals(attributeName))
402 doSourceFile(in);
403 else if ("Code".equals(attributeName) && crawl)
404 doCode(in);
405 else {
406 if (attribute_length > 0x7FFFFFFF) {
407 throw new IllegalArgumentException("Attribute > 2Gb");
408 }
409 in.skipBytes((int) attribute_length);
410 }
411 }
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437 private void doCode(DataInputStream in) throws IOException {
438
439
440 int code_length = in.readInt();
441 byte code[] = new byte[code_length];
442 in.readFully(code);
443 crawl(code);
444 int exception_table_length = in.readUnsignedShort();
445 in.skipBytes(exception_table_length * 8);
446 doAttributes(in, false);
447 }
448
449
450
451
452
453
454 protected void crawl(byte[] code) {
455 ByteBuffer bb = ByteBuffer.wrap(code);
456 bb.order(ByteOrder.BIG_ENDIAN);
457 int lastReference = -1;
458
459 while (bb.remaining() > 0) {
460 int instruction = 0xFF & bb.get();
461 switch (instruction) {
462 case OpCodes.ldc:
463 lastReference = 0xFF & bb.get();
464 break;
465
466 case OpCodes.ldc_w:
467 lastReference = 0xFFFF & bb.getShort();
468 break;
469
470 case OpCodes.invokestatic:
471 int methodref = 0xFFFF & bb.getShort();
472 if ((methodref == forName || methodref == class$) &&
473 lastReference != -1 &&
474 pool[intPool[lastReference]] instanceof String) {
475 String clazz = (String) pool[intPool[lastReference]];
476 doClassReference(clazz.replace('.', '/'));
477 }
478 break;
479
480 case OpCodes.tableswitch:
481
482 while ((bb.position() & 0x3) != 0)
483 bb.get();
484 int deflt = bb.getInt();
485 int low = bb.getInt();
486 int high = bb.getInt();
487 bb.position(bb.position() + (high - low + 1) * 4);
488 lastReference = -1;
489 break;
490
491 case OpCodes.lookupswitch:
492
493 while ((bb.position() & 0x3) != 0)
494 bb.get();
495 deflt = bb.getInt();
496 int npairs = bb.getInt();
497 bb.position(bb.position() + npairs * 8);
498 lastReference = -1;
499 break;
500
501 default:
502 lastReference = -1;
503 bb.position(bb.position() + OpCodes.OFFSETS[instruction]);
504 }
505 }
506 }
507
508 private void doSourceFile(DataInputStream in) throws IOException {
509 int sourcefile_index = in.readUnsignedShort();
510 this.sourceFile = pool[sourcefile_index].toString();
511 }
512
513 private void doParameterAnnotations(DataInputStream in) throws IOException {
514 int num_parameters = in.readUnsignedByte();
515 for (int p = 0; p < num_parameters; p++) {
516 int num_annotations = in.readUnsignedShort();
517 for (int a = 0; a < num_annotations; a++) {
518 doAnnotation(in);
519 }
520 }
521 }
522
523 private void doAnnotations(DataInputStream in) throws IOException {
524 int num_annotations = in.readUnsignedShort();
525 for (int a = 0; a < num_annotations; a++) {
526 doAnnotation(in);
527 }
528 }
529
530 private void doAnnotation(DataInputStream in) throws IOException {
531 int type_index = in.readUnsignedShort();
532 descriptors.add(new Integer(type_index));
533 int num_element_value_pairs = in.readUnsignedShort();
534 for (int v = 0; v < num_element_value_pairs; v++) {
535
536 doElementValue(in);
537 }
538 }
539
540 private void doElementValue(DataInputStream in) throws IOException {
541 int tag = in.readUnsignedByte();
542 switch (tag) {
543 case 'B':
544 case 'C':
545 case 'D':
546 case 'F':
547 case 'I':
548 case 'J':
549 case 'S':
550 case 'Z':
551 case 's':
552
553 in.readUnsignedShort();
554 break;
555
556 case 'e':
557 int type_name_index = in.readUnsignedShort();
558 descriptors.add(new Integer(type_name_index));
559
560 in.readUnsignedShort();
561 break;
562
563 case 'c':
564 int class_info_index = in.readUnsignedShort();
565 descriptors.add(new Integer(class_info_index));
566 break;
567
568 case '@':
569 doAnnotation(in);
570 break;
571
572 case '[':
573 int num_values = in.readUnsignedShort();
574 for (int i = 0; i < num_values; i++) {
575 doElementValue(in);
576 }
577 break;
578
579 default:
580 throw new IllegalArgumentException(
581 "Invalid value for Annotation ElementValue tag " + tag);
582 }
583 }
584
585 void classReference(String clazz) {
586 String pack = getPackage(clazz);
587 packageReference(pack);
588 classImports.add(clazz);
589 }
590
591 void packageReference(String pack) {
592 if (pack.indexOf('<') >= 0)
593 System.out.println("Oops: " + pack);
594 if (!packageImports.containsKey(pack))
595 packageImports.put(pack, new HashMap());
596 }
597
598 void parseDescriptor(String prototype) {
599 addReference(prototype);
600 StringTokenizer st = new StringTokenizer(prototype, "(;)", true);
601 while (st.hasMoreTokens()) {
602 if (st.nextToken().equals("(")) {
603 String token = st.nextToken();
604 while (!token.equals(")")) {
605 addReference(token);
606 token = st.nextToken();
607 }
608 token = st.nextToken();
609 addReference(token);
610 }
611 }
612 }
613
614 private void addReference(String token) {
615 while (token.startsWith("["))
616 token = token.substring(1);
617
618 if (token.startsWith("L")) {
619 String clazz = normalize(token.substring(1));
620 if (clazz.startsWith("java/"))
621 return;
622 classReference(clazz);
623 }
624 }
625
626 static String normalize(String s) {
627 if (s.startsWith("[L"))
628 return normalize(s.substring(2));
629 if (s.startsWith("["))
630 if (s.length() == 2)
631 return null;
632 else
633 return normalize(s.substring(1));
634 if (s.endsWith(";"))
635 return normalize(s.substring(0, s.length() - 1));
636 return s + ".class";
637 }
638
639 public static String getPackage(String clazz) {
640 int n = clazz.lastIndexOf('/');
641 if (n < 0)
642 return ".";
643 return clazz.substring(0, n).replace('/', '.');
644 }
645
646 public Map getReferred() {
647 return packageImports;
648 }
649
650 public Set getReferredClasses() {
651 return classImports;
652 }
653
654 String getClassName() {
655 return className;
656 }
657
658 public String getPath() {
659 return path;
660 }
661
662 public Set xref(InputStream in) throws IOException {
663 DataInputStream din = new DataInputStream(in);
664 Set set = parseClassFile(din);
665 din.close();
666 return set;
667 }
668
669 public String getSourceFile() {
670 return sourceFile;
671 }
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689 public void reset() {
690 pool = null;
691 intPool = null;
692 xref = null;
693 classes = null;
694 descriptors = null;
695 }
696
697 }