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