blob: 87ce8a566aef302b1bc35a39da6c89dc575e06fb [file] [log] [blame]
Stuart McCullochf3173222012-06-07 21:57:32 +00001package aQute.lib.osgi;
2
3import java.io.*;
4import java.lang.annotation.*;
5import java.lang.reflect.*;
6import java.nio.*;
7import java.util.*;
8import java.util.regex.*;
9
10import aQute.lib.osgi.Descriptors.Descriptor;
11import aQute.lib.osgi.Descriptors.PackageRef;
12import aQute.lib.osgi.Descriptors.TypeRef;
13import aQute.libg.generics.*;
14
15public class Clazz {
16
17 static Pattern METHOD_DESCRIPTOR = Pattern.compile("\\((.*)\\)(.+)");
18
19 public class ClassConstant {
20 int cname;
21
22 public ClassConstant(int class_index) {
23 this.cname = class_index;
24 }
25
26 public String getName() {
27 return (String) pool[cname];
28 }
29 }
30
31 public static enum JAVA {
32 JDK1_1(45, "JRE-1.1"), JDK1_2(46, "J2SE-1.2"), //
33 JDK1_3(47, "J2SE-1.3"), //
34 JDK1_4(48, "J2SE-1.4"), //
35 J2SE5(49, "J2SE-1.5"), //
36 J2SE6(50, "JavaSE-1.6"), //
37 OpenJDK7(51, "JavaSE-1.7"), //
38 UNKNOWN(Integer.MAX_VALUE, "<>")//
39 ;
40
41 final int major;
42 final String ee;
43
44 JAVA(int major, String ee) {
45 this.major = major;
46 this.ee = ee;
47 }
48
49 static JAVA format(int n) {
50 for (JAVA e : JAVA.values())
51 if (e.major == n)
52 return e;
53 return UNKNOWN;
54 }
55
56 public int getMajor() {
57 return major;
58 }
59
60 public boolean hasAnnotations() {
61 return major >= J2SE5.major;
62 }
63
64 public boolean hasGenerics() {
65 return major >= J2SE5.major;
66 }
67
68 public boolean hasEnums() {
69 return major >= J2SE5.major;
70 }
71
Stuart McCulloch669423b2012-06-26 16:34:24 +000072 public static JAVA getJava(int major, @SuppressWarnings("unused") int minor) {
Stuart McCullochf3173222012-06-07 21:57:32 +000073 for (JAVA j : JAVA.values()) {
74 if (j.major == major)
75 return j;
76 }
77 return UNKNOWN;
78 }
79
80 public String getEE() {
81 return ee;
82 }
83 }
84
85 public static enum QUERY {
86 IMPLEMENTS, EXTENDS, IMPORTS, NAMED, ANY, VERSION, CONCRETE, ABSTRACT, PUBLIC, ANNOTATED, RUNTIMEANNOTATIONS, CLASSANNOTATIONS;
87
88 }
89
Stuart McCulloch4482c702012-06-15 13:27:53 +000090 public final static EnumSet<QUERY> HAS_ARGUMENT = EnumSet.of(QUERY.IMPLEMENTS, QUERY.EXTENDS, QUERY.IMPORTS,
91 QUERY.NAMED, QUERY.VERSION, QUERY.ANNOTATED);
Stuart McCullochf3173222012-06-07 21:57:32 +000092
93 /**
94 * <pre>
95 * ACC_PUBLIC 0x0001 Declared public; may be accessed from outside its
96 * package.
97 * ACC_FINAL 0x0010 Declared final; no subclasses allowed.
98 * ACC_SUPER 0x0020 Treat superclass methods specially when invoked by the
99 * invokespecial instruction.
100 * ACC_INTERFACE 0x0200 Is an interface, not a
101 * class.
102 * ACC_ABSTRACT 0x0400 Declared abstract; may not be instantiated.
103 * </pre>
104 *
105 * @param mod
106 */
Stuart McCulloch4482c702012-06-15 13:27:53 +0000107 final static int ACC_PUBLIC = 0x0001; // Declared
Stuart McCullochf3173222012-06-07 21:57:32 +0000108 // public;
109 // may
110 // be
111 // accessed
112 // from outside its package.
Stuart McCulloch4482c702012-06-15 13:27:53 +0000113 final static int ACC_FINAL = 0x0010; // Declared
Stuart McCullochf3173222012-06-07 21:57:32 +0000114 // final;
115 // no
116 // subclasses
117 // allowed.
Stuart McCulloch4482c702012-06-15 13:27:53 +0000118 final static int ACC_SUPER = 0x0020; // Treat
Stuart McCullochf3173222012-06-07 21:57:32 +0000119 // superclass
120 // methods
121 // specially when invoked by the
122 // invokespecial instruction.
Stuart McCulloch4482c702012-06-15 13:27:53 +0000123 final static int ACC_INTERFACE = 0x0200; // Is
Stuart McCullochf3173222012-06-07 21:57:32 +0000124 // an
125 // interface,
126 // not
127 // a
128 // classs
Stuart McCulloch4482c702012-06-15 13:27:53 +0000129 final static int ACC_ABSTRACT = 0x0400; // Declared
Stuart McCullochf3173222012-06-07 21:57:32 +0000130
131 // a thing not in the source code
132 final static int ACC_SYNTHETIC = 0x1000;
133 final static int ACC_ANNOTATION = 0x2000;
134 final static int ACC_ENUM = 0x4000;
135
136 static protected class Assoc {
137 Assoc(byte tag, int a, int b) {
138 this.tag = tag;
139 this.a = a;
140 this.b = b;
141 }
142
143 byte tag;
144 int a;
145 int b;
146 }
147
148 public class Def {
149 final int access;
150 Set<TypeRef> annotations;
151
152 public Def(int access) {
153 this.access = access;
154 }
155
156 public int getAccess() {
157 return access;
158 }
159
160 public boolean isEnum() {
161 return (access & ACC_ENUM) != 0;
162 }
163
164 public boolean isPublic() {
165 return Modifier.isPublic(access);
166 }
167
168 public boolean isAbstract() {
169 return Modifier.isAbstract(access);
170 }
171
172 public boolean isProtected() {
173 return Modifier.isProtected(access);
174 }
175
176 public boolean isFinal() {
177 return Modifier.isFinal(access) || Clazz.this.isFinal();
178 }
179
180 public boolean isStatic() {
181 return Modifier.isStatic(access);
182 }
183
184 public boolean isPrivate() {
185 return Modifier.isPrivate(access);
186 }
187
188 public boolean isNative() {
189 return Modifier.isNative(access);
190 }
191
192 public boolean isTransient() {
193 return Modifier.isTransient(access);
194 }
195
196 public boolean isVolatile() {
197 return Modifier.isVolatile(access);
198 }
199
200 public boolean isInterface() {
201 return Modifier.isInterface(access);
202 }
203
204 public boolean isSynthetic() {
205 return (access & ACC_SYNTHETIC) != 0;
206 }
207
208 void addAnnotation(Annotation a) {
209 if (annotations == null)
210 annotations = Create.set();
211 annotations.add(analyzer.getTypeRef(a.name.getBinary()));
212 }
213
214 public Collection<TypeRef> getAnnotations() {
215 return annotations;
216 }
217 }
218
219 public class FieldDef extends Def {
220 final String name;
221 final Descriptor descriptor;
222 String signature;
223 Object constant;
224 boolean deprecated;
225
226 public boolean isDeprecated() {
227 return deprecated;
228 }
229
230 public void setDeprecated(boolean deprecated) {
231 this.deprecated = deprecated;
232 }
233
234 public FieldDef(int access, String name, String descriptor) {
235 super(access);
236 this.name = name;
237 this.descriptor = analyzer.getDescriptor(descriptor);
238 }
239
240 public String getName() {
241 return name;
242 }
243
244 public String toString() {
245 return getName();
246 }
247
248 public TypeRef getType() {
249 return descriptor.getType();
250 }
251
252 public TypeRef getContainingClass() {
253 return getClassName();
254 }
255
256 public Descriptor getDescriptor() {
257 return descriptor;
258 }
259
260 public void setConstant(Object o) {
261 this.constant = o;
262 }
263
264 public Object getConstant() {
265 return this.constant;
266 }
267
268 // TODO change to use proper generics
269 public String getGenericReturnType() {
270 String use = descriptor.toString();
271 if (signature != null)
272 use = signature;
273
274 Matcher m = METHOD_DESCRIPTOR.matcher(use);
275 if (!m.matches())
276 throw new IllegalArgumentException("Not a valid method descriptor: " + descriptor);
277
278 String returnType = m.group(2);
279 return objectDescriptorToFQN(returnType);
280 }
281
282 public String getSignature() {
283 return signature;
284 }
285
286 }
287
288 public class MethodDef extends FieldDef {
289 public MethodDef(int access, String method, String descriptor) {
290 super(access, method, descriptor);
291 }
292
293 public boolean isConstructor() {
294 return name.equals("<init>") || name.equals("<clinit>");
295 }
296
297 public TypeRef[] getPrototype() {
298 return descriptor.getPrototype();
299 }
300
301 }
302
303 final static byte SkipTable[] = { //
Stuart McCulloch4482c702012-06-15 13:27:53 +0000304 0, // 0 non existent
Stuart McCullochf3173222012-06-07 21:57:32 +0000305 -1, // 1 CONSTANT_utf8 UTF 8, handled in
306 // method
307 -1, // 2
308 4, // 3 CONSTANT_Integer
309 4, // 4 CONSTANT_Float
310 8, // 5 CONSTANT_Long (index +=2!)
311 8, // 6 CONSTANT_Double (index +=2!)
312 -1, // 7 CONSTANT_Class
313 2, // 8 CONSTANT_String
314 4, // 9 CONSTANT_FieldRef
315 4, // 10 CONSTANT_MethodRef
316 4, // 11 CONSTANT_InterfaceMethodRef
317 4, // 12 CONSTANT_NameAndType
318 -1, // 13 Not defined
319 -1, // 14 Not defined
320 3, // 15 CONSTANT_MethodHandle
321 2, // 16 CONSTANT_MethodType
322 -1, // 17 Not defined
323 4, // 18 CONSTANT_InvokeDynamic
324 };
325
326 boolean hasRuntimeAnnotations;
327 boolean hasClassAnnotations;
328
329 TypeRef className;
330 Object pool[];
331 int intPool[];
332 Set<PackageRef> imports = Create.set();
333 String path;
334 int minor = 0;
335 int major = 0;
336 int innerAccess = -1;
337 int accessx = 0;
338 String sourceFile;
339 Set<TypeRef> xref;
340 Set<Integer> classes;
341 Set<Integer> descriptors;
342 Set<TypeRef> annotations;
343 int forName = 0;
344 int class$ = 0;
345 TypeRef[] interfaces;
346 TypeRef zuper;
347 ClassDataCollector cd = null;
348 Resource resource;
349 FieldDef last = null;
350 boolean deprecated;
351
352 final Analyzer analyzer;
353
354 public Clazz(Analyzer analyzer, String path, Resource resource) {
355 this.path = path;
356 this.resource = resource;
357 this.analyzer = analyzer;
358 }
359
360 public Set<TypeRef> parseClassFile() throws Exception {
361 return parseClassFileWithCollector(null);
362 }
363
364 public Set<TypeRef> parseClassFile(InputStream in) throws Exception {
365 return parseClassFile(in, null);
366 }
367
368 public Set<TypeRef> parseClassFileWithCollector(ClassDataCollector cd) throws Exception {
369 InputStream in = resource.openInputStream();
370 try {
371 return parseClassFile(in, cd);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000372 }
373 finally {
Stuart McCullochf3173222012-06-07 21:57:32 +0000374 in.close();
375 }
376 }
377
378 public Set<TypeRef> parseClassFile(InputStream in, ClassDataCollector cd) throws Exception {
379 DataInputStream din = new DataInputStream(in);
380 try {
381 this.cd = cd;
382 return parseClassFile(din);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000383 }
384 finally {
Stuart McCullochf3173222012-06-07 21:57:32 +0000385 cd = null;
386 din.close();
387 }
388 }
389
390 Set<TypeRef> parseClassFile(DataInputStream in) throws Exception {
391 xref = new HashSet<TypeRef>();
392 classes = new HashSet<Integer>();
393 descriptors = new HashSet<Integer>();
394
395 boolean crawl = cd != null; // Crawl the byte code if we have a
396 // collector
397 int magic = in.readInt();
398 if (magic != 0xCAFEBABE)
399 throw new IOException("Not a valid class file (no CAFEBABE header)");
400
401 minor = in.readUnsignedShort(); // minor version
402 major = in.readUnsignedShort(); // major version
403 if (cd != null)
404 cd.version(minor, major);
405 int count = in.readUnsignedShort();
406 pool = new Object[count];
407 intPool = new int[count];
408
409 process: for (int poolIndex = 1; poolIndex < count; poolIndex++) {
410 byte tag = in.readByte();
411 switch (tag) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000412 case 0 :
413 break process;
414 case 1 :
415 constantUtf8(in, poolIndex);
416 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000417
Stuart McCulloch4482c702012-06-15 13:27:53 +0000418 case 3 :
419 constantInteger(in, poolIndex);
420 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000421
Stuart McCulloch4482c702012-06-15 13:27:53 +0000422 case 4 :
423 constantFloat(in, poolIndex);
424 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000425
Stuart McCulloch4482c702012-06-15 13:27:53 +0000426 // For some insane optimization reason are
427 // the long and the double two entries in the
428 // constant pool. See 4.4.5
429 case 5 :
430 constantLong(in, poolIndex);
431 poolIndex++;
432 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000433
Stuart McCulloch4482c702012-06-15 13:27:53 +0000434 case 6 :
435 constantDouble(in, poolIndex);
436 poolIndex++;
437 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000438
Stuart McCulloch4482c702012-06-15 13:27:53 +0000439 case 7 :
440 constantClass(in, poolIndex);
441 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000442
Stuart McCulloch4482c702012-06-15 13:27:53 +0000443 case 8 :
444 constantString(in, poolIndex);
445 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000446
Stuart McCulloch4482c702012-06-15 13:27:53 +0000447 case 10 : // Method ref
448 case 11 : // Interface Method ref
449 methodRef(in, poolIndex);
450 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000451
Stuart McCulloch4482c702012-06-15 13:27:53 +0000452 // Name and Type
453 case 12 :
454 nameAndType(in, poolIndex, tag);
455 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000456
Stuart McCulloch4482c702012-06-15 13:27:53 +0000457 // We get the skip count for each record type
458 // from the SkipTable. This will also automatically
459 // abort when
460 default :
461 if (tag == 2)
462 throw new IOException("Invalid tag " + tag);
463 in.skipBytes(SkipTable[tag]);
464 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000465 }
466 }
467
468 pool(pool, intPool);
469 /*
470 * Parse after the constant pool, code thanks to Hans Christian
471 * Falkenberg
472 */
473
474 accessx = in.readUnsignedShort(); // access
475
476 int this_class = in.readUnsignedShort();
477 className = analyzer.getTypeRef((String) pool[intPool[this_class]]);
478
479 try {
480
481 if (cd != null) {
482 if (!cd.classStart(accessx, className))
483 return null;
484 }
485
486 int super_class = in.readUnsignedShort();
487 String superName = (String) pool[intPool[super_class]];
488 if (superName != null) {
489 zuper = analyzer.getTypeRef(superName);
490 }
491
492 if (zuper != null) {
493 referTo(zuper);
494 if (cd != null)
495 cd.extendsClass(zuper);
496 }
497
498 int interfacesCount = in.readUnsignedShort();
499 if (interfacesCount > 0) {
500 interfaces = new TypeRef[interfacesCount];
501 for (int i = 0; i < interfacesCount; i++)
Stuart McCulloch4482c702012-06-15 13:27:53 +0000502 interfaces[i] = analyzer.getTypeRef((String) pool[intPool[in.readUnsignedShort()]]);
Stuart McCullochf3173222012-06-07 21:57:32 +0000503 if (cd != null)
504 cd.implementsInterfaces(interfaces);
505 }
506
507 int fieldsCount = in.readUnsignedShort();
508 for (int i = 0; i < fieldsCount; i++) {
509 int access_flags = in.readUnsignedShort(); // skip access flags
510 int name_index = in.readUnsignedShort();
511 int descriptor_index = in.readUnsignedShort();
512
513 // Java prior to 1.5 used a weird
514 // static variable to hold the com.X.class
515 // result construct. If it did not find it
516 // it would create a variable class$com$X
517 // that would be used to hold the class
518 // object gotten with Class.forName ...
519 // Stupidly, they did not actively use the
520 // class name for the field type, so bnd
521 // would not see a reference. We detect
522 // this case and add an artificial descriptor
523 String name = pool[name_index].toString(); // name_index
524 if (name.startsWith("class$")) {
525 crawl = true;
526 }
527 if (cd != null)
Stuart McCulloch4482c702012-06-15 13:27:53 +0000528 cd.field(last = new FieldDef(access_flags, name, pool[descriptor_index].toString()));
Stuart McCullochf3173222012-06-07 21:57:32 +0000529 descriptors.add(Integer.valueOf(descriptor_index));
530 doAttributes(in, ElementType.FIELD, false);
531 }
532
533 //
534 // Check if we have to crawl the code to find
535 // the ldc(_w) <string constant> invokestatic Class.forName
536 // if so, calculate the method ref index so we
537 // can do this efficiently
538 //
539 if (crawl) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000540 forName = findMethodReference("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
541 class$ = findMethodReference(className.getBinary(), "class$", "(Ljava/lang/String;)Ljava/lang/Class;");
542 } else if (major == 48) {
543 forName = findMethodReference("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
Stuart McCullochf3173222012-06-07 21:57:32 +0000544 if (forName > 0) {
545 crawl = true;
546 class$ = findMethodReference(className.getBinary(), "class$",
547 "(Ljava/lang/String;)Ljava/lang/Class;");
548 }
549 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000550
Stuart McCullochf3173222012-06-07 21:57:32 +0000551 // There are some serious changes in the
552 // class file format. So we do not do any crawling
553 // it has also become less important
Stuart McCulloch4482c702012-06-15 13:27:53 +0000554 if (major >= JAVA.OpenJDK7.major)
Stuart McCullochf3173222012-06-07 21:57:32 +0000555 crawl = false;
556
557 //
558 // Handle the methods
559 //
560 int methodCount = in.readUnsignedShort();
561 for (int i = 0; i < methodCount; i++) {
562 int access_flags = in.readUnsignedShort();
563 int name_index = in.readUnsignedShort();
564 int descriptor_index = in.readUnsignedShort();
565 descriptors.add(Integer.valueOf(descriptor_index));
566 String name = pool[name_index].toString();
567 String descriptor = pool[descriptor_index].toString();
568 if (cd != null) {
569 MethodDef mdef = new MethodDef(access_flags, name, descriptor);
570 last = mdef;
571 cd.method(mdef);
572 }
573
574 if ("<init>".equals(name)) {
575 doAttributes(in, ElementType.CONSTRUCTOR, crawl);
576 } else {
577 doAttributes(in, ElementType.METHOD, crawl);
578 }
579 }
580 if (cd != null)
581 cd.memberEnd();
582
583 doAttributes(in, ElementType.TYPE, false);
584
585 //
586 // Now iterate over all classes we found and
587 // parse those as well. We skip duplicates
588 //
589
590 for (int n : classes) {
591 String descr = (String) pool[n];
592
593 TypeRef clazz = analyzer.getTypeRef(descr);
594 referTo(clazz);
595 }
596
597 //
598 // Parse all the descriptors we found
599 //
600
601 for (Iterator<Integer> e = descriptors.iterator(); e.hasNext();) {
602 Integer index = e.next();
603 String prototype = (String) pool[index.intValue()];
604 if (prototype != null)
605 parseDescriptor(prototype);
606 else
607 System.err.println("Unrecognized descriptor: " + index);
608 }
609 Set<TypeRef> xref = this.xref;
610 reset();
611 return xref;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000612 }
613 finally {
Stuart McCullochf3173222012-06-07 21:57:32 +0000614 if (cd != null)
615 cd.classEnd();
616 }
617 }
618
619 private void constantFloat(DataInputStream in, int poolIndex) throws IOException {
620 if (cd != null)
621 pool[poolIndex] = in.readFloat(); // ALU
622 else
623 in.skipBytes(4);
624 }
625
626 private void constantInteger(DataInputStream in, int poolIndex) throws IOException {
627 intPool[poolIndex] = in.readInt();
628 if (cd != null)
629 pool[poolIndex] = intPool[poolIndex];
630 }
631
Stuart McCulloch669423b2012-06-26 16:34:24 +0000632 protected void pool(@SuppressWarnings("unused") Object[] pool, @SuppressWarnings("unused") int[] intPool) {}
Stuart McCullochf3173222012-06-07 21:57:32 +0000633
634 /**
635 * @param in
636 * @param poolIndex
637 * @param tag
638 * @throws IOException
639 */
640 protected void nameAndType(DataInputStream in, int poolIndex, byte tag) throws IOException {
641 int name_index = in.readUnsignedShort();
642 int descriptor_index = in.readUnsignedShort();
643 descriptors.add(Integer.valueOf(descriptor_index));
644 pool[poolIndex] = new Assoc(tag, name_index, descriptor_index);
645 }
646
647 /**
648 * @param in
649 * @param poolIndex
650 * @param tag
651 * @throws IOException
652 */
653 private void methodRef(DataInputStream in, int poolIndex) throws IOException {
654 int class_index = in.readUnsignedShort();
655 int name_and_type_index = in.readUnsignedShort();
656 pool[poolIndex] = new Assoc((byte) 10, class_index, name_and_type_index);
657 }
658
659 /**
660 * @param in
661 * @param poolIndex
662 * @throws IOException
663 */
664 private void constantString(DataInputStream in, int poolIndex) throws IOException {
665 int string_index = in.readUnsignedShort();
666 intPool[poolIndex] = string_index;
667 }
668
669 /**
670 * @param in
671 * @param poolIndex
672 * @throws IOException
673 */
674 protected void constantClass(DataInputStream in, int poolIndex) throws IOException {
675 int class_index = in.readUnsignedShort();
676 classes.add(Integer.valueOf(class_index));
677 intPool[poolIndex] = class_index;
678 ClassConstant c = new ClassConstant(class_index);
679 pool[poolIndex] = c;
680 }
681
682 /**
683 * @param in
684 * @throws IOException
685 */
686 protected void constantDouble(DataInputStream in, int poolIndex) throws IOException {
687 if (cd != null)
688 pool[poolIndex] = in.readDouble();
689 else
690 in.skipBytes(8);
691 }
692
693 /**
694 * @param in
695 * @throws IOException
696 */
697 protected void constantLong(DataInputStream in, int poolIndex) throws IOException {
698 if (cd != null) {
699 pool[poolIndex] = in.readLong();
700 } else
701 in.skipBytes(8);
702 }
703
704 /**
705 * @param in
706 * @param poolIndex
707 * @throws IOException
708 */
709 protected void constantUtf8(DataInputStream in, int poolIndex) throws IOException {
710 // CONSTANT_Utf8
711
712 String name = in.readUTF();
713 pool[poolIndex] = name;
714 }
715
716 /**
717 * Find a method reference in the pool that points to the given class,
718 * methodname and descriptor.
719 *
720 * @param clazz
721 * @param methodname
722 * @param descriptor
723 * @return index in constant pool
724 */
725 private int findMethodReference(String clazz, String methodname, String descriptor) {
726 for (int i = 1; i < pool.length; i++) {
727 if (pool[i] instanceof Assoc) {
728 Assoc methodref = (Assoc) pool[i];
729 if (methodref.tag == 10) {
730 // Method ref
731 int class_index = methodref.a;
732 int class_name_index = intPool[class_index];
733 if (clazz.equals(pool[class_name_index])) {
734 int name_and_type_index = methodref.b;
735 Assoc name_and_type = (Assoc) pool[name_and_type_index];
736 if (name_and_type.tag == 12) {
737 // Name and Type
738 int name_index = name_and_type.a;
739 int type_index = name_and_type.b;
740 if (methodname.equals(pool[name_index])) {
741 if (descriptor.equals(pool[type_index])) {
742 return i;
743 }
744 }
745 }
746 }
747 }
748 }
749 }
750 return -1;
751 }
752
753 /**
754 * Called for each attribute in the class, field, or method.
755 *
756 * @param in
757 * The stream
758 * @throws Exception
759 */
Stuart McCulloch4482c702012-06-15 13:27:53 +0000760 private void doAttributes(DataInputStream in, ElementType member, boolean crawl) throws Exception {
Stuart McCullochf3173222012-06-07 21:57:32 +0000761 int attributesCount = in.readUnsignedShort();
762 for (int j = 0; j < attributesCount; j++) {
763 // skip name CONSTANT_Utf8 pointer
764 doAttribute(in, member, crawl);
765 }
766 }
767
768 /**
769 * Process a single attribute, if not recognized, skip it.
770 *
771 * @param in
772 * the data stream
773 * @throws Exception
774 */
Stuart McCulloch4482c702012-06-15 13:27:53 +0000775 private void doAttribute(DataInputStream in, ElementType member, boolean crawl) throws Exception {
Stuart McCullochf3173222012-06-07 21:57:32 +0000776 int attribute_name_index = in.readUnsignedShort();
777 String attributeName = (String) pool[attribute_name_index];
778 long attribute_length = in.readInt();
779 attribute_length &= 0xFFFFFFFF;
780 if ("Deprecated".equals(attributeName)) {
781 if (cd != null)
782 cd.deprecated();
783 } else if ("RuntimeVisibleAnnotations".equals(attributeName))
784 doAnnotations(in, member, RetentionPolicy.RUNTIME);
785 else if ("RuntimeVisibleParameterAnnotations".equals(attributeName))
786 doParameterAnnotations(in, member, RetentionPolicy.RUNTIME);
787 else if ("RuntimeInvisibleAnnotations".equals(attributeName))
788 doAnnotations(in, member, RetentionPolicy.CLASS);
789 else if ("RuntimeInvisibleParameterAnnotations".equals(attributeName))
790 doParameterAnnotations(in, member, RetentionPolicy.CLASS);
791 else if ("InnerClasses".equals(attributeName))
792 doInnerClasses(in);
793 else if ("EnclosingMethod".equals(attributeName))
794 doEnclosingMethod(in);
795 else if ("SourceFile".equals(attributeName))
796 doSourceFile(in);
797 else if ("Code".equals(attributeName) && crawl)
798 doCode(in);
799 else if ("Signature".equals(attributeName))
800 doSignature(in, member);
801 else if ("ConstantValue".equals(attributeName))
802 doConstantValue(in);
803 else {
804 if (attribute_length > 0x7FFFFFFF) {
805 throw new IllegalArgumentException("Attribute > 2Gb");
806 }
807 in.skipBytes((int) attribute_length);
808 }
809 }
810
811 /**
812 * <pre>
813 * EnclosingMethod_attribute {
814 * u2 attribute_name_index;
815 * u4 attribute_length;
816 * u2 class_index
817 * u2 method_index;
818 * }
819 * </pre>
820 *
Stuart McCullochf3173222012-06-07 21:57:32 +0000821 * @param in
822 * @throws IOException
823 */
824 private void doEnclosingMethod(DataInputStream in) throws IOException {
825 int cIndex = in.readShort();
826 int mIndex = in.readShort();
827
828 if (cd != null) {
829 int nameIndex = intPool[cIndex];
830 TypeRef cName = analyzer.getTypeRef((String) pool[nameIndex]);
831
832 String mName = null;
833 String mDescriptor = null;
834
835 if (mIndex != 0) {
836 Assoc nameAndType = (Assoc) pool[mIndex];
837 mName = (String) pool[nameAndType.a];
838 mDescriptor = (String) pool[nameAndType.b];
839 }
840 cd.enclosingMethod(cName, mName, mDescriptor);
841 }
842 }
843
844 /**
845 * <pre>
846 * InnerClasses_attribute {
847 * u2 attribute_name_index;
848 * u4 attribute_length;
849 * u2 number_of_classes; {
850 * u2 inner_class_info_index;
851 * u2 outer_class_info_index;
852 * u2 inner_name_index;
853 * u2 inner_class_access_flags;
854 * } classes[number_of_classes];
855 * }
856 * </pre>
857 *
858 * @param in
859 * @throws Exception
860 */
861 private void doInnerClasses(DataInputStream in) throws Exception {
862 int number_of_classes = in.readShort();
863 for (int i = 0; i < number_of_classes; i++) {
864 int inner_class_info_index = in.readShort();
865 int outer_class_info_index = in.readShort();
866 int inner_name_index = in.readShort();
867 int inner_class_access_flags = in.readShort() & 0xFFFF;
868
869 if (cd != null) {
870 TypeRef innerClass = null;
871 TypeRef outerClass = null;
872 String innerName = null;
873
874 if (inner_class_info_index != 0) {
875 int nameIndex = intPool[inner_class_info_index];
876 innerClass = analyzer.getTypeRef((String) pool[nameIndex]);
877 }
878
879 if (outer_class_info_index != 0) {
880 int nameIndex = intPool[outer_class_info_index];
881 outerClass = analyzer.getTypeRef((String) pool[nameIndex]);
882 }
883
884 if (inner_name_index != 0)
885 innerName = (String) pool[inner_name_index];
886
887 cd.innerClass(innerClass, outerClass, innerName, inner_class_access_flags);
888 }
889 }
890 }
891
892 /**
893 * Handle a signature
894 *
895 * <pre>
896 * Signature_attribute {
897 * u2 attribute_name_index;
898 * u4 attribute_length;
899 * u2 signature_index;
900 * }
901 * </pre>
902 *
903 * @param member
904 */
905
906 void doSignature(DataInputStream in, ElementType member) throws IOException {
907 int signature_index = in.readUnsignedShort();
908 String signature = (String) pool[signature_index];
909
910 // s.println("Signature " + signature );
911
912 // // The type signature is kind of weird,
913 // // lets skip it for now. Seems to be some kind of
914 // // type variable name index but it does not seem to
915 // // conform to the language specification.
916 // if (member != ElementType.TYPE)
917 parseDescriptor(signature);
918
919 if (last != null)
920 last.signature = signature;
921
922 if (cd != null)
923 cd.signature(signature);
924 }
925
926 /**
927 * Handle a constant value call the data collector with it
928 */
929 void doConstantValue(DataInputStream in) throws IOException {
930 int constantValue_index = in.readUnsignedShort();
931 if (cd == null)
932 return;
933
934 Object object = pool[constantValue_index];
935 if (object == null)
936 object = pool[intPool[constantValue_index]];
937
938 last.constant = object;
939 cd.constant(object);
940 }
941
942 /**
943 * <pre>
944 * Code_attribute {
945 * u2 attribute_name_index;
946 * u4 attribute_length;
947 * u2 max_stack;
948 * u2 max_locals;
949 * u4 code_length;
950 * u1 code[code_length];
951 * u2 exception_table_length;
952 * { u2 start_pc;
953 * u2 end_pc;
954 * u2 handler_pc;
955 * u2 catch_type;
956 * } exception_table[exception_table_length];
957 * u2 attributes_count;
958 * attribute_info attributes[attributes_count];
959 * }
960 * </pre>
961 *
962 * @param in
963 * @param pool
964 * @throws Exception
965 */
966 private void doCode(DataInputStream in) throws Exception {
967 /* int max_stack = */in.readUnsignedShort();
968 /* int max_locals = */in.readUnsignedShort();
969 int code_length = in.readInt();
970 byte code[] = new byte[code_length];
971 in.readFully(code);
972 crawl(code);
973 int exception_table_length = in.readUnsignedShort();
974 in.skipBytes(exception_table_length * 8);
975 doAttributes(in, ElementType.METHOD, false);
976 }
977
978 /**
979 * We must find Class.forName references ...
980 *
981 * @param code
982 */
983 protected void crawl(byte[] code) {
984 ByteBuffer bb = ByteBuffer.wrap(code);
985 bb.order(ByteOrder.BIG_ENDIAN);
986 int lastReference = -1;
987
988 while (bb.remaining() > 0) {
989 int instruction = 0xFF & bb.get();
990 switch (instruction) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000991 case OpCodes.ldc :
992 lastReference = 0xFF & bb.get();
993 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000994
Stuart McCulloch4482c702012-06-15 13:27:53 +0000995 case OpCodes.ldc_w :
996 lastReference = 0xFFFF & bb.getShort();
997 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000998
Stuart McCulloch4482c702012-06-15 13:27:53 +0000999 case OpCodes.invokespecial : {
1000 int mref = 0xFFFF & bb.getShort();
1001 if (cd != null)
1002 getMethodDef(0, mref);
1003 break;
1004 }
Stuart McCullochf3173222012-06-07 21:57:32 +00001005
Stuart McCulloch4482c702012-06-15 13:27:53 +00001006 case OpCodes.invokevirtual : {
1007 int mref = 0xFFFF & bb.getShort();
1008 if (cd != null)
1009 getMethodDef(0, mref);
1010 break;
1011 }
Stuart McCullochf3173222012-06-07 21:57:32 +00001012
Stuart McCulloch4482c702012-06-15 13:27:53 +00001013 case OpCodes.invokeinterface : {
1014 int mref = 0xFFFF & bb.getShort();
1015 if (cd != null)
1016 getMethodDef(0, mref);
1017 break;
1018 }
Stuart McCullochf3173222012-06-07 21:57:32 +00001019
Stuart McCulloch4482c702012-06-15 13:27:53 +00001020 case OpCodes.invokestatic : {
1021 int methodref = 0xFFFF & bb.getShort();
1022 if (cd != null)
1023 getMethodDef(0, methodref);
Stuart McCullochf3173222012-06-07 21:57:32 +00001024
Stuart McCulloch4482c702012-06-15 13:27:53 +00001025 if ((methodref == forName || methodref == class$) && lastReference != -1
1026 && pool[intPool[lastReference]] instanceof String) {
1027 String fqn = (String) pool[intPool[lastReference]];
1028 if (!fqn.equals("class") && fqn.indexOf('.') > 0) {
1029 TypeRef clazz = analyzer.getTypeRefFromFQN(fqn);
1030 referTo(clazz);
1031 }
1032 lastReference = -1;
1033 }
1034 break;
1035 }
1036
1037 case OpCodes.tableswitch :
1038 // Skip to place divisible by 4
1039 while ((bb.position() & 0x3) != 0)
1040 bb.get();
1041 /* int deflt = */
1042 bb.getInt();
1043 int low = bb.getInt();
1044 int high = bb.getInt();
1045 try {
1046 bb.position(bb.position() + (high - low + 1) * 4);
1047 }
1048 catch (Exception e) {
1049 // TODO Auto-generated catch block
1050 e.printStackTrace();
Stuart McCullochf3173222012-06-07 21:57:32 +00001051 }
1052 lastReference = -1;
Stuart McCulloch4482c702012-06-15 13:27:53 +00001053 break;
Stuart McCullochf3173222012-06-07 21:57:32 +00001054
Stuart McCulloch4482c702012-06-15 13:27:53 +00001055 case OpCodes.lookupswitch :
1056 // Skip to place divisible by 4
1057 while ((bb.position() & 0x3) != 0)
1058 bb.get();
1059 /* deflt = */
1060 bb.getInt();
1061 int npairs = bb.getInt();
1062 bb.position(bb.position() + npairs * 8);
1063 lastReference = -1;
1064 break;
Stuart McCullochf3173222012-06-07 21:57:32 +00001065
Stuart McCulloch4482c702012-06-15 13:27:53 +00001066 default :
1067 lastReference = -1;
1068 bb.position(bb.position() + OpCodes.OFFSETS[instruction]);
Stuart McCullochf3173222012-06-07 21:57:32 +00001069 }
1070 }
1071 }
1072
1073 private void doSourceFile(DataInputStream in) throws IOException {
1074 int sourcefile_index = in.readUnsignedShort();
1075 this.sourceFile = pool[sourcefile_index].toString();
1076 }
1077
Stuart McCulloch4482c702012-06-15 13:27:53 +00001078 private void doParameterAnnotations(DataInputStream in, ElementType member, RetentionPolicy policy)
1079 throws IOException {
Stuart McCullochf3173222012-06-07 21:57:32 +00001080 int num_parameters = in.readUnsignedByte();
1081 for (int p = 0; p < num_parameters; p++) {
1082 if (cd != null)
1083 cd.parameter(p);
1084 doAnnotations(in, member, policy);
1085 }
1086 }
1087
Stuart McCulloch4482c702012-06-15 13:27:53 +00001088 private void doAnnotations(DataInputStream in, ElementType member, RetentionPolicy policy) throws IOException {
Stuart McCullochf3173222012-06-07 21:57:32 +00001089 int num_annotations = in.readUnsignedShort(); // # of annotations
1090 for (int a = 0; a < num_annotations; a++) {
1091 if (cd == null)
1092 doAnnotation(in, member, policy, false);
1093 else {
1094 Annotation annotion = doAnnotation(in, member, policy, true);
1095 cd.annotation(annotion);
1096 }
1097 }
1098 }
1099
Stuart McCulloch4482c702012-06-15 13:27:53 +00001100 private Annotation doAnnotation(DataInputStream in, ElementType member, RetentionPolicy policy, boolean collect)
1101 throws IOException {
Stuart McCullochf3173222012-06-07 21:57:32 +00001102 int type_index = in.readUnsignedShort();
1103 if (annotations == null)
1104 annotations = new HashSet<TypeRef>();
1105
1106 TypeRef tr = analyzer.getTypeRef(pool[type_index].toString());
1107 annotations.add(tr);
1108
1109 if (policy == RetentionPolicy.RUNTIME) {
1110 descriptors.add(Integer.valueOf(type_index));
1111 hasRuntimeAnnotations = true;
1112 } else {
1113 hasClassAnnotations = true;
1114 }
1115 TypeRef name = analyzer.getTypeRef((String) pool[type_index]);
1116 int num_element_value_pairs = in.readUnsignedShort();
Stuart McCulloch4482c702012-06-15 13:27:53 +00001117 Map<String,Object> elements = null;
Stuart McCullochf3173222012-06-07 21:57:32 +00001118 for (int v = 0; v < num_element_value_pairs; v++) {
1119 int element_name_index = in.readUnsignedShort();
1120 String element = (String) pool[element_name_index];
1121 Object value = doElementValue(in, member, policy, collect);
1122 if (collect) {
1123 if (elements == null)
Stuart McCulloch4482c702012-06-15 13:27:53 +00001124 elements = new LinkedHashMap<String,Object>();
Stuart McCullochf3173222012-06-07 21:57:32 +00001125 elements.put(element, value);
1126 }
1127 }
1128 if (collect)
1129 return new Annotation(name, elements, member, policy);
Stuart McCulloch669423b2012-06-26 16:34:24 +00001130 return null;
Stuart McCullochf3173222012-06-07 21:57:32 +00001131 }
1132
Stuart McCulloch4482c702012-06-15 13:27:53 +00001133 private Object doElementValue(DataInputStream in, ElementType member, RetentionPolicy policy, boolean collect)
1134 throws IOException {
Stuart McCullochf3173222012-06-07 21:57:32 +00001135 char tag = (char) in.readUnsignedByte();
1136 switch (tag) {
Stuart McCulloch4482c702012-06-15 13:27:53 +00001137 case 'B' : // Byte
1138 case 'C' : // Character
1139 case 'I' : // Integer
1140 case 'S' : // Short
1141 int const_value_index = in.readUnsignedShort();
1142 return intPool[const_value_index];
Stuart McCullochf3173222012-06-07 21:57:32 +00001143
Stuart McCulloch4482c702012-06-15 13:27:53 +00001144 case 'D' : // Double
1145 case 'F' : // Float
1146 case 's' : // String
1147 case 'J' : // Long
1148 const_value_index = in.readUnsignedShort();
1149 return pool[const_value_index];
Stuart McCullochf3173222012-06-07 21:57:32 +00001150
Stuart McCulloch4482c702012-06-15 13:27:53 +00001151 case 'Z' : // Boolean
1152 const_value_index = in.readUnsignedShort();
1153 return pool[const_value_index] == null || pool[const_value_index].equals(0) ? false : true;
Stuart McCullochf3173222012-06-07 21:57:32 +00001154
Stuart McCulloch4482c702012-06-15 13:27:53 +00001155 case 'e' : // enum constant
1156 int type_name_index = in.readUnsignedShort();
1157 if (policy == RetentionPolicy.RUNTIME)
1158 descriptors.add(Integer.valueOf(type_name_index));
1159 int const_name_index = in.readUnsignedShort();
1160 return pool[const_name_index];
Stuart McCullochf3173222012-06-07 21:57:32 +00001161
Stuart McCulloch4482c702012-06-15 13:27:53 +00001162 case 'c' : // Class
1163 int class_info_index = in.readUnsignedShort();
1164 if (policy == RetentionPolicy.RUNTIME)
1165 descriptors.add(Integer.valueOf(class_info_index));
1166 return pool[class_info_index];
Stuart McCullochf3173222012-06-07 21:57:32 +00001167
Stuart McCulloch4482c702012-06-15 13:27:53 +00001168 case '@' : // Annotation type
1169 return doAnnotation(in, member, policy, collect);
Stuart McCullochf3173222012-06-07 21:57:32 +00001170
Stuart McCulloch4482c702012-06-15 13:27:53 +00001171 case '[' : // Array
1172 int num_values = in.readUnsignedShort();
1173 Object[] result = new Object[num_values];
1174 for (int i = 0; i < num_values; i++) {
1175 result[i] = doElementValue(in, member, policy, collect);
1176 }
1177 return result;
Stuart McCullochf3173222012-06-07 21:57:32 +00001178
Stuart McCulloch4482c702012-06-15 13:27:53 +00001179 default :
1180 throw new IllegalArgumentException("Invalid value for Annotation ElementValue tag " + tag);
Stuart McCullochf3173222012-06-07 21:57:32 +00001181 }
1182 }
1183
1184 /**
1185 * Add a new package reference.
1186 *
1187 * @param packageRef
1188 * A '.' delimited package name
1189 */
1190 void referTo(TypeRef typeRef) {
1191 if (xref != null)
1192 xref.add(typeRef);
1193 if (typeRef.isPrimitive())
1194 return;
1195
1196 PackageRef packageRef = typeRef.getPackageRef();
1197 if (packageRef.isPrimitivePackage())
1198 return;
1199
1200 imports.add(packageRef);
1201 }
1202
1203 /**
1204 * This method parses a descriptor and adds the package of the descriptor to
Stuart McCulloch4482c702012-06-15 13:27:53 +00001205 * the referenced packages. The syntax of the descriptor is:
Stuart McCullochf3173222012-06-07 21:57:32 +00001206 *
1207 * <pre>
1208 * descriptor ::= ( '(' reference * ')' )? reference
1209 * reference ::= 'L' classname ( '&lt;' references '&gt;' )? ';' | 'B' | 'Z' | ... | '+' | '-' | '['
1210 * </pre>
1211 *
1212 * This methods uses heavy recursion to parse the descriptor and a roving
1213 * pointer to limit the creation of string objects.
1214 *
1215 * @param descriptor
1216 * The to be parsed descriptor
1217 * @param rover
1218 * The pointer to start at
1219 */
1220 public void parseDescriptor(String descriptor) {
1221 // Some descriptors are weird, they start with a generic
1222 // declaration that contains ':', not sure what they mean ...
1223 int rover = 0;
1224 if (descriptor.charAt(0) == '<') {
1225 rover = parseFormalTypeParameters(descriptor, rover);
1226 }
1227
1228 if (descriptor.charAt(rover) == '(') {
1229 rover = parseReferences(descriptor, rover + 1, ')');
1230 rover++;
1231 }
1232 parseReferences(descriptor, rover, (char) 0);
1233 }
1234
1235 /**
1236 * Parse a sequence of references. A sequence ends with a given character or
1237 * when the string ends.
1238 *
1239 * @param descriptor
1240 * The whole descriptor.
1241 * @param rover
1242 * The index in the descriptor
1243 * @param delimiter
1244 * The end character or 0
1245 * @return the last index processed, one character after the delimeter
1246 */
1247 int parseReferences(String descriptor, int rover, char delimiter) {
1248 int r = rover;
1249 while (r < descriptor.length() && descriptor.charAt(r) != delimiter) {
1250 r = parseReference(descriptor, r);
1251 }
1252 return r;
1253 }
1254
1255 /**
1256 * Parse a single reference. This can be a single character or an object
1257 * reference when it starts with 'L'.
1258 *
1259 * @param descriptor
1260 * The descriptor
1261 * @param rover
1262 * The place to start
1263 * @return The return index after the reference
1264 */
1265 int parseReference(String descriptor, int rover) {
1266 int r = rover;
1267 char c = descriptor.charAt(r);
1268 while (c == '[')
1269 c = descriptor.charAt(++r);
1270
1271 if (c == '<') {
1272 r = parseReferences(descriptor, r + 1, '>');
1273 } else if (c == 'T') {
1274 // Type variable name
1275 r++;
1276 while (descriptor.charAt(r) != ';')
1277 r++;
1278 } else if (c == 'L') {
1279 StringBuilder sb = new StringBuilder();
1280 r++;
1281 while ((c = descriptor.charAt(r)) != ';') {
1282 if (c == '<') {
1283 r = parseReferences(descriptor, r + 1, '>');
1284 } else
1285 sb.append(c);
1286 r++;
1287 }
1288 TypeRef ref = analyzer.getTypeRef(sb.toString());
1289 if (cd != null)
1290 cd.addReference(ref);
1291
1292 referTo(ref);
1293 } else {
1294 if ("+-*BCDFIJSZV".indexOf(c) < 0)
1295 ;// System.err.println("Should not skip: " + c);
1296 }
1297
1298 // this skips a lot of characters
1299 // [, *, +, -, B, etc.
1300
1301 return r + 1;
1302 }
1303
1304 /**
1305 * FormalTypeParameters
1306 *
1307 * @param descriptor
1308 * @param index
1309 * @return
1310 */
1311 private int parseFormalTypeParameters(String descriptor, int index) {
1312 index++;
1313 while (descriptor.charAt(index) != '>') {
1314 // Skip IDENTIFIER
1315 index = descriptor.indexOf(':', index) + 1;
1316 if (index == 0)
1317 throw new IllegalArgumentException("Expected IDENTIFIER: " + descriptor);
1318
1319 // ClassBound? InterfaceBounds
1320
1321 char c = descriptor.charAt(index);
1322
1323 // Class Bound?
1324 if (c == 'L' || c == 'T') {
1325 index = parseReference(descriptor, index); // class reference
1326 c = descriptor.charAt(index);
1327 }
1328
1329 // Interface Bounds
1330 while (c == ':') {
1331 index++;
1332 index = parseReference(descriptor, index);
1333 c = descriptor.charAt(index);
1334 } // for each interface
1335
1336 } // for each formal parameter
1337 return index + 1; // skip >
1338 }
1339
1340 public Set<PackageRef> getReferred() {
1341 return imports;
1342 }
1343
1344 public String getAbsolutePath() {
1345 return path;
1346 }
1347
1348 public String getSourceFile() {
1349 return sourceFile;
1350 }
1351
1352 /**
Stuart McCulloch4482c702012-06-15 13:27:53 +00001353 * .class construct for different compilers sun 1.1 Detect static variable
1354 * class$com$acme$MyClass 1.2 " 1.3 " 1.4 " 1.5 ldc_w (class) 1.6 " eclipse
1355 * 1.1 class$0, ldc (string), invokestatic Class.forName 1.2 " 1.3 " 1.5 ldc
1356 * (class) 1.6 " 1.5 and later is not an issue, sun pre 1.5 is easy to
1357 * detect the static variable that decodes the class name. For eclipse, the
1358 * class$0 gives away we have a reference encoded in a string.
Stuart McCullochf3173222012-06-07 21:57:32 +00001359 * compilerversions/compilerversions.jar contains test versions of all
1360 * versions/compilers.
1361 */
1362
1363 public void reset() {
1364 pool = null;
1365 intPool = null;
1366 xref = null;
1367 classes = null;
1368 descriptors = null;
1369 }
1370
1371 public boolean is(QUERY query, Instruction instr, Analyzer analyzer) throws Exception {
1372 switch (query) {
Stuart McCulloch4482c702012-06-15 13:27:53 +00001373 case ANY :
1374 return true;
Stuart McCullochf3173222012-06-07 21:57:32 +00001375
Stuart McCulloch4482c702012-06-15 13:27:53 +00001376 case NAMED :
1377 if (instr.matches(getClassName().getDottedOnly()))
Stuart McCullochf3173222012-06-07 21:57:32 +00001378 return !instr.isNegated();
Stuart McCullochf3173222012-06-07 21:57:32 +00001379 return false;
1380
Stuart McCulloch4482c702012-06-15 13:27:53 +00001381 case VERSION :
1382 String v = major + "." + minor;
1383 if (instr.matches(v))
1384 return !instr.isNegated();
Stuart McCullochf3173222012-06-07 21:57:32 +00001385 return false;
1386
Stuart McCulloch4482c702012-06-15 13:27:53 +00001387 case IMPLEMENTS :
1388 for (int i = 0; interfaces != null && i < interfaces.length; i++) {
1389 if (instr.matches(interfaces[i].getDottedOnly()))
1390 return !instr.isNegated();
1391 }
1392 break;
1393
1394 case EXTENDS :
1395 if (zuper == null)
1396 return false;
1397
1398 if (instr.matches(zuper.getDottedOnly()))
Stuart McCullochf3173222012-06-07 21:57:32 +00001399 return !instr.isNegated();
Stuart McCulloch4482c702012-06-15 13:27:53 +00001400 break;
Stuart McCullochf3173222012-06-07 21:57:32 +00001401
Stuart McCulloch4482c702012-06-15 13:27:53 +00001402 case PUBLIC :
1403 return Modifier.isPublic(accessx);
Stuart McCullochf3173222012-06-07 21:57:32 +00001404
Stuart McCulloch4482c702012-06-15 13:27:53 +00001405 case CONCRETE :
1406 return !Modifier.isAbstract(accessx);
Stuart McCullochf3173222012-06-07 21:57:32 +00001407
Stuart McCulloch4482c702012-06-15 13:27:53 +00001408 case ANNOTATED :
1409 if (annotations == null)
1410 return false;
Stuart McCullochf3173222012-06-07 21:57:32 +00001411
Stuart McCulloch4482c702012-06-15 13:27:53 +00001412 for (TypeRef annotation : annotations) {
1413 if (instr.matches(annotation.getFQN()))
1414 return !instr.isNegated();
1415 }
1416
1417 return false;
1418
1419 case RUNTIMEANNOTATIONS :
1420 return hasClassAnnotations;
1421 case CLASSANNOTATIONS :
1422 return hasClassAnnotations;
1423
1424 case ABSTRACT :
1425 return Modifier.isAbstract(accessx);
1426
1427 case IMPORTS :
1428 for (PackageRef imp : imports) {
1429 if (instr.matches(imp.getFQN()))
1430 return !instr.isNegated();
1431 }
Stuart McCullochf3173222012-06-07 21:57:32 +00001432 }
1433
1434 if (zuper == null)
1435 return false;
1436
1437 Clazz clazz = analyzer.findClass(zuper);
1438 if (clazz == null)
1439 return false;
1440
1441 return clazz.is(query, instr, analyzer);
1442 }
1443
1444 public String toString() {
1445 return className.getFQN();
1446 }
1447
1448 /**
1449 * Called when crawling the byte code and a method reference is found
Stuart McCullochf3173222012-06-07 21:57:32 +00001450 */
1451 void getMethodDef(int access, int methodRefPoolIndex) {
Stuart McCulloch4482c702012-06-15 13:27:53 +00001452 if (methodRefPoolIndex == 0)
Stuart McCullochf3173222012-06-07 21:57:32 +00001453 return;
Stuart McCulloch4482c702012-06-15 13:27:53 +00001454
Stuart McCullochf3173222012-06-07 21:57:32 +00001455 Object o = pool[methodRefPoolIndex];
1456 if (o != null && o instanceof Assoc) {
1457 Assoc assoc = (Assoc) o;
1458 if (assoc.tag == 10) {
1459 int string_index = intPool[assoc.a];
1460 TypeRef className = analyzer.getTypeRef((String) pool[string_index]);
1461 int name_and_type_index = assoc.b;
1462 Assoc name_and_type = (Assoc) pool[name_and_type_index];
1463 if (name_and_type.tag == 12) {
1464 // Name and Type
1465 int name_index = name_and_type.a;
1466 int type_index = name_and_type.b;
1467 String method = (String) pool[name_index];
1468 String descriptor = (String) pool[type_index];
1469 cd.referenceMethod(access, className, method, descriptor);
1470 } else
1471 throw new IllegalArgumentException(
1472 "Invalid class file (or parsing is wrong), assoc is not type + name (12)");
1473 } else
1474 throw new IllegalArgumentException(
1475 "Invalid class file (or parsing is wrong), Assoc is not method ref! (10)");
1476 } else
Stuart McCulloch4482c702012-06-15 13:27:53 +00001477 throw new IllegalArgumentException("Invalid class file (or parsing is wrong), Not an assoc at a method ref");
Stuart McCullochf3173222012-06-07 21:57:32 +00001478 }
1479
1480 public boolean isPublic() {
1481 return Modifier.isPublic(accessx);
1482 }
1483
1484 public boolean isProtected() {
1485 return Modifier.isProtected(accessx);
1486 }
1487
1488 public boolean isEnum() {
1489 return zuper != null && zuper.getBinary().equals("java/lang/Enum");
1490 }
1491
1492 public JAVA getFormat() {
1493 return JAVA.format(major);
1494
1495 }
1496
1497 public static String objectDescriptorToFQN(String string) {
1498 if (string.startsWith("L") && string.endsWith(";"))
1499 return string.substring(1, string.length() - 1).replace('/', '.');
1500
1501 switch (string.charAt(0)) {
Stuart McCulloch4482c702012-06-15 13:27:53 +00001502 case 'V' :
1503 return "void";
1504 case 'B' :
1505 return "byte";
1506 case 'C' :
1507 return "char";
1508 case 'I' :
1509 return "int";
1510 case 'S' :
1511 return "short";
1512 case 'D' :
1513 return "double";
1514 case 'F' :
1515 return "float";
1516 case 'J' :
1517 return "long";
1518 case 'Z' :
1519 return "boolean";
1520 case '[' : // Array
1521 return objectDescriptorToFQN(string.substring(1)) + "[]";
Stuart McCullochf3173222012-06-07 21:57:32 +00001522 }
1523 throw new IllegalArgumentException("Invalid type character in descriptor " + string);
1524 }
1525
1526 public static String unCamel(String id) {
1527 StringBuilder out = new StringBuilder();
1528 for (int i = 0; i < id.length(); i++) {
1529 char c = id.charAt(i);
1530 if (c == '_' || c == '$' || c == '.') {
1531 if (out.length() > 0 && !Character.isWhitespace(out.charAt(out.length() - 1)))
1532 out.append(' ');
1533 continue;
1534 }
1535
1536 int n = i;
1537 while (n < id.length() && Character.isUpperCase(id.charAt(n))) {
1538 n++;
1539 }
1540 if (n == i)
1541 out.append(id.charAt(i));
1542 else {
1543 boolean tolower = (n - i) == 1;
1544 if (i > 0 && !Character.isWhitespace(out.charAt(out.length() - 1)))
1545 out.append(' ');
1546
1547 for (; i < n;) {
1548 if (tolower)
1549 out.append(Character.toLowerCase(id.charAt(i)));
1550 else
1551 out.append(id.charAt(i));
1552 i++;
1553 }
1554 i--;
1555 }
1556 }
1557 if (id.startsWith("."))
1558 out.append(" *");
1559 out.replace(0, 1, Character.toUpperCase(out.charAt(0)) + "");
1560 return out.toString();
1561 }
1562
1563 public boolean isInterface() {
1564 return Modifier.isInterface(accessx);
1565 }
1566
1567 public boolean isAbstract() {
1568 return Modifier.isAbstract(accessx);
1569 }
1570
1571 public int getAccess() {
1572 if (innerAccess == -1)
1573 return accessx;
Stuart McCulloch669423b2012-06-26 16:34:24 +00001574 return innerAccess;
Stuart McCullochf3173222012-06-07 21:57:32 +00001575 }
1576
1577 public TypeRef getClassName() {
1578 return className;
1579 }
1580
1581 /**
1582 * To provide an enclosing instance
1583 *
1584 * @param access
1585 * @param name
1586 * @param descriptor
1587 * @return
1588 */
1589 public MethodDef getMethodDef(int access, String name, String descriptor) {
1590 return new MethodDef(access, name, descriptor);
1591 }
1592
1593 public TypeRef getSuper() {
1594 return zuper;
1595 }
1596
1597 public String getFQN() {
1598 return className.getFQN();
1599 }
1600
1601 public TypeRef[] getInterfaces() {
1602 return interfaces;
1603 }
1604
1605 public void setInnerAccess(int access) {
1606 innerAccess = access;
1607 }
1608
1609 public boolean isFinal() {
1610 return Modifier.isFinal(accessx);
1611 }
1612
1613 public void setDeprecated(boolean b) {
1614 deprecated = b;
1615 }
1616
1617 public boolean isDeprecated() {
1618 return deprecated;
1619 }
1620
1621 public boolean isAnnotation() {
1622 return (accessx & ACC_ANNOTATION) != 0;
1623 }
1624}