blob: dc8784c93bb6c92ac68a54b2418d11fd5182947c [file] [log] [blame]
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001package aQute.bnd.osgi;
Stuart McCullochbb014372012-06-07 21:57:32 +00002
3import java.io.*;
4import java.lang.annotation.*;
5import java.lang.reflect.*;
6import java.nio.*;
7import java.util.*;
8import java.util.regex.*;
9
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +000010import aQute.bnd.osgi.Descriptors.Descriptor;
11import aQute.bnd.osgi.Descriptors.PackageRef;
12import aQute.bnd.osgi.Descriptors.TypeRef;
Stuart McCullochbb014372012-06-07 21:57:32 +000013import 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 McCullochd4826102012-06-26 16:34:24 +000072 public static JAVA getJava(int major, @SuppressWarnings("unused") int minor) {
Stuart McCullochbb014372012-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 McCulloch2286f232012-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 McCullochbb014372012-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 McCulloch2286f232012-06-15 13:27:53 +0000107 final static int ACC_PUBLIC = 0x0001; // Declared
Stuart McCullochbb014372012-06-07 21:57:32 +0000108 // public;
109 // may
110 // be
111 // accessed
112 // from outside its package.
Stuart McCulloch2286f232012-06-15 13:27:53 +0000113 final static int ACC_FINAL = 0x0010; // Declared
Stuart McCullochbb014372012-06-07 21:57:32 +0000114 // final;
115 // no
116 // subclasses
117 // allowed.
Stuart McCulloch2286f232012-06-15 13:27:53 +0000118 final static int ACC_SUPER = 0x0020; // Treat
Stuart McCullochbb014372012-06-07 21:57:32 +0000119 // superclass
120 // methods
121 // specially when invoked by the
122 // invokespecial instruction.
Stuart McCulloch2286f232012-06-15 13:27:53 +0000123 final static int ACC_INTERFACE = 0x0200; // Is
Stuart McCullochbb014372012-06-07 21:57:32 +0000124 // an
125 // interface,
126 // not
127 // a
128 // classs
Stuart McCulloch2286f232012-06-15 13:27:53 +0000129 final static int ACC_ABSTRACT = 0x0400; // Declared
Stuart McCullochbb014372012-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 McCulloch2286f232012-06-15 13:27:53 +0000304 0, // 0 non existent
Stuart McCullochbb014372012-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;
Stuart McCullochbb014372012-06-07 21:57:32 +0000340 Set<TypeRef> annotations;
341 int forName = 0;
342 int class$ = 0;
343 TypeRef[] interfaces;
344 TypeRef zuper;
345 ClassDataCollector cd = null;
346 Resource resource;
347 FieldDef last = null;
348 boolean deprecated;
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000349 Set<PackageRef> api;
Stuart McCullochbb014372012-06-07 21:57:32 +0000350 final Analyzer analyzer;
351
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000352
Stuart McCullochbb014372012-06-07 21:57:32 +0000353 public Clazz(Analyzer analyzer, String path, Resource resource) {
354 this.path = path;
355 this.resource = resource;
356 this.analyzer = analyzer;
357 }
358
359 public Set<TypeRef> parseClassFile() throws Exception {
360 return parseClassFileWithCollector(null);
361 }
362
363 public Set<TypeRef> parseClassFile(InputStream in) throws Exception {
364 return parseClassFile(in, null);
365 }
366
367 public Set<TypeRef> parseClassFileWithCollector(ClassDataCollector cd) throws Exception {
368 InputStream in = resource.openInputStream();
369 try {
370 return parseClassFile(in, cd);
Stuart McCulloch2286f232012-06-15 13:27:53 +0000371 }
372 finally {
Stuart McCullochbb014372012-06-07 21:57:32 +0000373 in.close();
374 }
375 }
376
377 public Set<TypeRef> parseClassFile(InputStream in, ClassDataCollector cd) throws Exception {
378 DataInputStream din = new DataInputStream(in);
379 try {
380 this.cd = cd;
381 return parseClassFile(din);
Stuart McCulloch2286f232012-06-15 13:27:53 +0000382 }
383 finally {
Stuart McCullochbb014372012-06-07 21:57:32 +0000384 cd = null;
385 din.close();
386 }
387 }
388
389 Set<TypeRef> parseClassFile(DataInputStream in) throws Exception {
390 xref = new HashSet<TypeRef>();
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000391
Stuart McCullochbb014372012-06-07 21:57:32 +0000392 boolean crawl = cd != null; // Crawl the byte code if we have a
393 // collector
394 int magic = in.readInt();
395 if (magic != 0xCAFEBABE)
396 throw new IOException("Not a valid class file (no CAFEBABE header)");
397
398 minor = in.readUnsignedShort(); // minor version
399 major = in.readUnsignedShort(); // major version
400 if (cd != null)
401 cd.version(minor, major);
402 int count = in.readUnsignedShort();
403 pool = new Object[count];
404 intPool = new int[count];
405
406 process: for (int poolIndex = 1; poolIndex < count; poolIndex++) {
407 byte tag = in.readByte();
408 switch (tag) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000409 case 0 :
410 break process;
411 case 1 :
412 constantUtf8(in, poolIndex);
413 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000414
Stuart McCulloch2286f232012-06-15 13:27:53 +0000415 case 3 :
416 constantInteger(in, poolIndex);
417 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000418
Stuart McCulloch2286f232012-06-15 13:27:53 +0000419 case 4 :
420 constantFloat(in, poolIndex);
421 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000422
Stuart McCulloch2286f232012-06-15 13:27:53 +0000423 // For some insane optimization reason are
424 // the long and the double two entries in the
425 // constant pool. See 4.4.5
426 case 5 :
427 constantLong(in, poolIndex);
428 poolIndex++;
429 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000430
Stuart McCulloch2286f232012-06-15 13:27:53 +0000431 case 6 :
432 constantDouble(in, poolIndex);
433 poolIndex++;
434 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000435
Stuart McCulloch2286f232012-06-15 13:27:53 +0000436 case 7 :
437 constantClass(in, poolIndex);
438 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000439
Stuart McCulloch2286f232012-06-15 13:27:53 +0000440 case 8 :
441 constantString(in, poolIndex);
442 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000443
Stuart McCulloch2286f232012-06-15 13:27:53 +0000444 case 10 : // Method ref
445 case 11 : // Interface Method ref
446 methodRef(in, poolIndex);
447 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000448
Stuart McCulloch2286f232012-06-15 13:27:53 +0000449 // Name and Type
450 case 12 :
451 nameAndType(in, poolIndex, tag);
452 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000453
Stuart McCulloch2286f232012-06-15 13:27:53 +0000454 // We get the skip count for each record type
455 // from the SkipTable. This will also automatically
456 // abort when
457 default :
458 if (tag == 2)
459 throw new IOException("Invalid tag " + tag);
460 in.skipBytes(SkipTable[tag]);
461 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000462 }
463 }
464
465 pool(pool, intPool);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000466
467 // All name& type and class constant records contain descriptors we must treat
468 // as references, though not API
469
470 for ( Object o : pool ) {
471 if ( o == null)
472 continue;
473
474 if (o instanceof Assoc && ((Assoc)o).tag ==12 ) {
475 referTo( ((Assoc)o).b, 0); // Descriptor
476 } else if ( o instanceof ClassConstant) {
477 String binaryClassName = (String) pool[((ClassConstant)o).cname];
478 TypeRef typeRef = analyzer.getTypeRef(binaryClassName);
479 referTo( typeRef, 0);
480 }
481 }
482
Stuart McCullochbb014372012-06-07 21:57:32 +0000483 /*
484 * Parse after the constant pool, code thanks to Hans Christian
485 * Falkenberg
486 */
487
488 accessx = in.readUnsignedShort(); // access
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000489 if ( Modifier.isPublic(accessx))
490 api = new HashSet<PackageRef>();
491
Stuart McCullochbb014372012-06-07 21:57:32 +0000492 int this_class = in.readUnsignedShort();
493 className = analyzer.getTypeRef((String) pool[intPool[this_class]]);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000494 referTo(className, Modifier.PUBLIC);
495
Stuart McCullochbb014372012-06-07 21:57:32 +0000496 try {
497
498 if (cd != null) {
499 if (!cd.classStart(accessx, className))
500 return null;
501 }
502
503 int super_class = in.readUnsignedShort();
504 String superName = (String) pool[intPool[super_class]];
505 if (superName != null) {
506 zuper = analyzer.getTypeRef(superName);
507 }
508
509 if (zuper != null) {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000510 referTo(zuper, accessx);
Stuart McCullochbb014372012-06-07 21:57:32 +0000511 if (cd != null)
512 cd.extendsClass(zuper);
513 }
514
515 int interfacesCount = in.readUnsignedShort();
516 if (interfacesCount > 0) {
517 interfaces = new TypeRef[interfacesCount];
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000518 for (int i = 0; i < interfacesCount; i++) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000519 interfaces[i] = analyzer.getTypeRef((String) pool[intPool[in.readUnsignedShort()]]);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000520 referTo(interfaces[i],accessx);
521 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000522 if (cd != null)
523 cd.implementsInterfaces(interfaces);
524 }
525
526 int fieldsCount = in.readUnsignedShort();
527 for (int i = 0; i < fieldsCount; i++) {
528 int access_flags = in.readUnsignedShort(); // skip access flags
529 int name_index = in.readUnsignedShort();
530 int descriptor_index = in.readUnsignedShort();
531
532 // Java prior to 1.5 used a weird
533 // static variable to hold the com.X.class
534 // result construct. If it did not find it
535 // it would create a variable class$com$X
536 // that would be used to hold the class
537 // object gotten with Class.forName ...
538 // Stupidly, they did not actively use the
539 // class name for the field type, so bnd
540 // would not see a reference. We detect
541 // this case and add an artificial descriptor
542 String name = pool[name_index].toString(); // name_index
543 if (name.startsWith("class$")) {
544 crawl = true;
545 }
546 if (cd != null)
Stuart McCulloch2286f232012-06-15 13:27:53 +0000547 cd.field(last = new FieldDef(access_flags, name, pool[descriptor_index].toString()));
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000548
549 referTo( descriptor_index, access_flags);
550 doAttributes(in, ElementType.FIELD, false, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000551 }
552
553 //
554 // Check if we have to crawl the code to find
555 // the ldc(_w) <string constant> invokestatic Class.forName
556 // if so, calculate the method ref index so we
557 // can do this efficiently
558 //
559 if (crawl) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000560 forName = findMethodReference("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
561 class$ = findMethodReference(className.getBinary(), "class$", "(Ljava/lang/String;)Ljava/lang/Class;");
562 } else if (major == 48) {
563 forName = findMethodReference("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
Stuart McCullochbb014372012-06-07 21:57:32 +0000564 if (forName > 0) {
565 crawl = true;
566 class$ = findMethodReference(className.getBinary(), "class$",
567 "(Ljava/lang/String;)Ljava/lang/Class;");
568 }
569 }
Stuart McCulloch2286f232012-06-15 13:27:53 +0000570
Stuart McCullochbb014372012-06-07 21:57:32 +0000571 // There are some serious changes in the
572 // class file format. So we do not do any crawling
573 // it has also become less important
Stuart McCulloch2286f232012-06-15 13:27:53 +0000574 if (major >= JAVA.OpenJDK7.major)
Stuart McCullochbb014372012-06-07 21:57:32 +0000575 crawl = false;
576
577 //
578 // Handle the methods
579 //
580 int methodCount = in.readUnsignedShort();
581 for (int i = 0; i < methodCount; i++) {
582 int access_flags = in.readUnsignedShort();
583 int name_index = in.readUnsignedShort();
584 int descriptor_index = in.readUnsignedShort();
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000585 referTo(descriptor_index, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000586 String name = pool[name_index].toString();
587 String descriptor = pool[descriptor_index].toString();
588 if (cd != null) {
589 MethodDef mdef = new MethodDef(access_flags, name, descriptor);
590 last = mdef;
591 cd.method(mdef);
592 }
593
594 if ("<init>".equals(name)) {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000595 doAttributes(in, ElementType.CONSTRUCTOR, crawl, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000596 } else {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000597 doAttributes(in, ElementType.METHOD, crawl, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000598 }
599 }
600 if (cd != null)
601 cd.memberEnd();
602
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000603 doAttributes(in, ElementType.TYPE, false, accessx);
Stuart McCullochbb014372012-06-07 21:57:32 +0000604
605 //
606 // Parse all the descriptors we found
607 //
608
Stuart McCullochbb014372012-06-07 21:57:32 +0000609 Set<TypeRef> xref = this.xref;
610 reset();
611 return xref;
Stuart McCulloch2286f232012-06-15 13:27:53 +0000612 }
613 finally {
Stuart McCullochbb014372012-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 McCullochd4826102012-06-26 16:34:24 +0000632 protected void pool(@SuppressWarnings("unused") Object[] pool, @SuppressWarnings("unused") int[] intPool) {}
Stuart McCullochbb014372012-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();
Stuart McCullochbb014372012-06-07 21:57:32 +0000643 pool[poolIndex] = new Assoc(tag, name_index, descriptor_index);
644 }
645
646 /**
647 * @param in
648 * @param poolIndex
649 * @param tag
650 * @throws IOException
651 */
652 private void methodRef(DataInputStream in, int poolIndex) throws IOException {
653 int class_index = in.readUnsignedShort();
654 int name_and_type_index = in.readUnsignedShort();
655 pool[poolIndex] = new Assoc((byte) 10, class_index, name_and_type_index);
656 }
657
658 /**
659 * @param in
660 * @param poolIndex
661 * @throws IOException
662 */
663 private void constantString(DataInputStream in, int poolIndex) throws IOException {
664 int string_index = in.readUnsignedShort();
665 intPool[poolIndex] = string_index;
666 }
667
668 /**
669 * @param in
670 * @param poolIndex
671 * @throws IOException
672 */
673 protected void constantClass(DataInputStream in, int poolIndex) throws IOException {
674 int class_index = in.readUnsignedShort();
Stuart McCullochbb014372012-06-07 21:57:32 +0000675 intPool[poolIndex] = class_index;
676 ClassConstant c = new ClassConstant(class_index);
677 pool[poolIndex] = c;
678 }
679
680 /**
681 * @param in
682 * @throws IOException
683 */
684 protected void constantDouble(DataInputStream in, int poolIndex) throws IOException {
685 if (cd != null)
686 pool[poolIndex] = in.readDouble();
687 else
688 in.skipBytes(8);
689 }
690
691 /**
692 * @param in
693 * @throws IOException
694 */
695 protected void constantLong(DataInputStream in, int poolIndex) throws IOException {
696 if (cd != null) {
697 pool[poolIndex] = in.readLong();
698 } else
699 in.skipBytes(8);
700 }
701
702 /**
703 * @param in
704 * @param poolIndex
705 * @throws IOException
706 */
707 protected void constantUtf8(DataInputStream in, int poolIndex) throws IOException {
708 // CONSTANT_Utf8
709
710 String name = in.readUTF();
711 pool[poolIndex] = name;
712 }
713
714 /**
715 * Find a method reference in the pool that points to the given class,
716 * methodname and descriptor.
717 *
718 * @param clazz
719 * @param methodname
720 * @param descriptor
721 * @return index in constant pool
722 */
723 private int findMethodReference(String clazz, String methodname, String descriptor) {
724 for (int i = 1; i < pool.length; i++) {
725 if (pool[i] instanceof Assoc) {
726 Assoc methodref = (Assoc) pool[i];
727 if (methodref.tag == 10) {
728 // Method ref
729 int class_index = methodref.a;
730 int class_name_index = intPool[class_index];
731 if (clazz.equals(pool[class_name_index])) {
732 int name_and_type_index = methodref.b;
733 Assoc name_and_type = (Assoc) pool[name_and_type_index];
734 if (name_and_type.tag == 12) {
735 // Name and Type
736 int name_index = name_and_type.a;
737 int type_index = name_and_type.b;
738 if (methodname.equals(pool[name_index])) {
739 if (descriptor.equals(pool[type_index])) {
740 return i;
741 }
742 }
743 }
744 }
745 }
746 }
747 }
748 return -1;
749 }
750
751 /**
752 * Called for each attribute in the class, field, or method.
753 *
754 * @param in
755 * The stream
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000756 * @param access_flags
Stuart McCullochbb014372012-06-07 21:57:32 +0000757 * @throws Exception
758 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000759 private void doAttributes(DataInputStream in, ElementType member, boolean crawl, int access_flags) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +0000760 int attributesCount = in.readUnsignedShort();
761 for (int j = 0; j < attributesCount; j++) {
762 // skip name CONSTANT_Utf8 pointer
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000763 doAttribute(in, member, crawl, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000764 }
765 }
766
767 /**
768 * Process a single attribute, if not recognized, skip it.
769 *
770 * @param in
771 * the data stream
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000772 * @param access_flags
Stuart McCullochbb014372012-06-07 21:57:32 +0000773 * @throws Exception
774 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000775 private void doAttribute(DataInputStream in, ElementType member, boolean crawl, int access_flags) throws Exception {
Stuart McCullochbb014372012-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))
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000800 doSignature(in, member, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000801 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 McCullochbb014372012-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
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000904 * @param access_flags
Stuart McCullochbb014372012-06-07 21:57:32 +0000905 */
906
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000907 void doSignature(DataInputStream in, ElementType member, int access_flags) throws IOException {
Stuart McCullochbb014372012-06-07 21:57:32 +0000908 int signature_index = in.readUnsignedShort();
909 String signature = (String) pool[signature_index];
910
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000911 parseDescriptor(signature, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000912
913 if (last != null)
914 last.signature = signature;
915
916 if (cd != null)
917 cd.signature(signature);
918 }
919
920 /**
921 * Handle a constant value call the data collector with it
922 */
923 void doConstantValue(DataInputStream in) throws IOException {
924 int constantValue_index = in.readUnsignedShort();
925 if (cd == null)
926 return;
927
928 Object object = pool[constantValue_index];
929 if (object == null)
930 object = pool[intPool[constantValue_index]];
931
932 last.constant = object;
933 cd.constant(object);
934 }
935
936 /**
937 * <pre>
938 * Code_attribute {
939 * u2 attribute_name_index;
940 * u4 attribute_length;
941 * u2 max_stack;
942 * u2 max_locals;
943 * u4 code_length;
944 * u1 code[code_length];
945 * u2 exception_table_length;
946 * { u2 start_pc;
947 * u2 end_pc;
948 * u2 handler_pc;
949 * u2 catch_type;
950 * } exception_table[exception_table_length];
951 * u2 attributes_count;
952 * attribute_info attributes[attributes_count];
953 * }
954 * </pre>
955 *
956 * @param in
957 * @param pool
958 * @throws Exception
959 */
960 private void doCode(DataInputStream in) throws Exception {
961 /* int max_stack = */in.readUnsignedShort();
962 /* int max_locals = */in.readUnsignedShort();
963 int code_length = in.readInt();
964 byte code[] = new byte[code_length];
965 in.readFully(code);
966 crawl(code);
967 int exception_table_length = in.readUnsignedShort();
968 in.skipBytes(exception_table_length * 8);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000969 doAttributes(in, ElementType.METHOD, false, 0);
Stuart McCullochbb014372012-06-07 21:57:32 +0000970 }
971
972 /**
973 * We must find Class.forName references ...
974 *
975 * @param code
976 */
977 protected void crawl(byte[] code) {
978 ByteBuffer bb = ByteBuffer.wrap(code);
979 bb.order(ByteOrder.BIG_ENDIAN);
980 int lastReference = -1;
981
982 while (bb.remaining() > 0) {
983 int instruction = 0xFF & bb.get();
984 switch (instruction) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000985 case OpCodes.ldc :
986 lastReference = 0xFF & bb.get();
987 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000988
Stuart McCulloch2286f232012-06-15 13:27:53 +0000989 case OpCodes.ldc_w :
990 lastReference = 0xFFFF & bb.getShort();
991 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000992
Stuart McCulloch2286f232012-06-15 13:27:53 +0000993 case OpCodes.invokespecial : {
994 int mref = 0xFFFF & bb.getShort();
995 if (cd != null)
996 getMethodDef(0, mref);
997 break;
998 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000999
Stuart McCulloch2286f232012-06-15 13:27:53 +00001000 case OpCodes.invokevirtual : {
1001 int mref = 0xFFFF & bb.getShort();
1002 if (cd != null)
1003 getMethodDef(0, mref);
1004 break;
1005 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001006
Stuart McCulloch2286f232012-06-15 13:27:53 +00001007 case OpCodes.invokeinterface : {
1008 int mref = 0xFFFF & bb.getShort();
1009 if (cd != null)
1010 getMethodDef(0, mref);
1011 break;
1012 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001013
Stuart McCulloch2286f232012-06-15 13:27:53 +00001014 case OpCodes.invokestatic : {
1015 int methodref = 0xFFFF & bb.getShort();
1016 if (cd != null)
1017 getMethodDef(0, methodref);
Stuart McCullochbb014372012-06-07 21:57:32 +00001018
Stuart McCulloch2286f232012-06-15 13:27:53 +00001019 if ((methodref == forName || methodref == class$) && lastReference != -1
1020 && pool[intPool[lastReference]] instanceof String) {
1021 String fqn = (String) pool[intPool[lastReference]];
1022 if (!fqn.equals("class") && fqn.indexOf('.') > 0) {
1023 TypeRef clazz = analyzer.getTypeRefFromFQN(fqn);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001024 referTo(clazz, 0);
Stuart McCulloch2286f232012-06-15 13:27:53 +00001025 }
1026 lastReference = -1;
1027 }
1028 break;
1029 }
1030
1031 case OpCodes.tableswitch :
1032 // Skip to place divisible by 4
1033 while ((bb.position() & 0x3) != 0)
1034 bb.get();
1035 /* int deflt = */
1036 bb.getInt();
1037 int low = bb.getInt();
1038 int high = bb.getInt();
1039 try {
1040 bb.position(bb.position() + (high - low + 1) * 4);
1041 }
1042 catch (Exception e) {
1043 // TODO Auto-generated catch block
1044 e.printStackTrace();
Stuart McCullochbb014372012-06-07 21:57:32 +00001045 }
1046 lastReference = -1;
Stuart McCulloch2286f232012-06-15 13:27:53 +00001047 break;
Stuart McCullochbb014372012-06-07 21:57:32 +00001048
Stuart McCulloch2286f232012-06-15 13:27:53 +00001049 case OpCodes.lookupswitch :
1050 // Skip to place divisible by 4
1051 while ((bb.position() & 0x3) != 0)
1052 bb.get();
1053 /* deflt = */
1054 bb.getInt();
1055 int npairs = bb.getInt();
1056 bb.position(bb.position() + npairs * 8);
1057 lastReference = -1;
1058 break;
Stuart McCullochbb014372012-06-07 21:57:32 +00001059
Stuart McCulloch2286f232012-06-15 13:27:53 +00001060 default :
1061 lastReference = -1;
1062 bb.position(bb.position() + OpCodes.OFFSETS[instruction]);
Stuart McCullochbb014372012-06-07 21:57:32 +00001063 }
1064 }
1065 }
1066
1067 private void doSourceFile(DataInputStream in) throws IOException {
1068 int sourcefile_index = in.readUnsignedShort();
1069 this.sourceFile = pool[sourcefile_index].toString();
1070 }
1071
Stuart McCulloch2286f232012-06-15 13:27:53 +00001072 private void doParameterAnnotations(DataInputStream in, ElementType member, RetentionPolicy policy)
1073 throws IOException {
Stuart McCullochbb014372012-06-07 21:57:32 +00001074 int num_parameters = in.readUnsignedByte();
1075 for (int p = 0; p < num_parameters; p++) {
1076 if (cd != null)
1077 cd.parameter(p);
1078 doAnnotations(in, member, policy);
1079 }
1080 }
1081
Stuart McCulloch2286f232012-06-15 13:27:53 +00001082 private void doAnnotations(DataInputStream in, ElementType member, RetentionPolicy policy) throws IOException {
Stuart McCullochbb014372012-06-07 21:57:32 +00001083 int num_annotations = in.readUnsignedShort(); // # of annotations
1084 for (int a = 0; a < num_annotations; a++) {
1085 if (cd == null)
1086 doAnnotation(in, member, policy, false);
1087 else {
1088 Annotation annotion = doAnnotation(in, member, policy, true);
1089 cd.annotation(annotion);
1090 }
1091 }
1092 }
1093
Stuart McCulloch2286f232012-06-15 13:27:53 +00001094 private Annotation doAnnotation(DataInputStream in, ElementType member, RetentionPolicy policy, boolean collect)
1095 throws IOException {
Stuart McCullochbb014372012-06-07 21:57:32 +00001096 int type_index = in.readUnsignedShort();
1097 if (annotations == null)
1098 annotations = new HashSet<TypeRef>();
1099
1100 TypeRef tr = analyzer.getTypeRef(pool[type_index].toString());
1101 annotations.add(tr);
1102
1103 if (policy == RetentionPolicy.RUNTIME) {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001104 referTo(type_index,0);
Stuart McCullochbb014372012-06-07 21:57:32 +00001105 hasRuntimeAnnotations = true;
1106 } else {
1107 hasClassAnnotations = true;
1108 }
1109 TypeRef name = analyzer.getTypeRef((String) pool[type_index]);
1110 int num_element_value_pairs = in.readUnsignedShort();
Stuart McCulloch2286f232012-06-15 13:27:53 +00001111 Map<String,Object> elements = null;
Stuart McCullochbb014372012-06-07 21:57:32 +00001112 for (int v = 0; v < num_element_value_pairs; v++) {
1113 int element_name_index = in.readUnsignedShort();
1114 String element = (String) pool[element_name_index];
1115 Object value = doElementValue(in, member, policy, collect);
1116 if (collect) {
1117 if (elements == null)
Stuart McCulloch2286f232012-06-15 13:27:53 +00001118 elements = new LinkedHashMap<String,Object>();
Stuart McCullochbb014372012-06-07 21:57:32 +00001119 elements.put(element, value);
1120 }
1121 }
1122 if (collect)
1123 return new Annotation(name, elements, member, policy);
Stuart McCullochd4826102012-06-26 16:34:24 +00001124 return null;
Stuart McCullochbb014372012-06-07 21:57:32 +00001125 }
1126
Stuart McCulloch2286f232012-06-15 13:27:53 +00001127 private Object doElementValue(DataInputStream in, ElementType member, RetentionPolicy policy, boolean collect)
1128 throws IOException {
Stuart McCullochbb014372012-06-07 21:57:32 +00001129 char tag = (char) in.readUnsignedByte();
1130 switch (tag) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001131 case 'B' : // Byte
1132 case 'C' : // Character
1133 case 'I' : // Integer
1134 case 'S' : // Short
1135 int const_value_index = in.readUnsignedShort();
1136 return intPool[const_value_index];
Stuart McCullochbb014372012-06-07 21:57:32 +00001137
Stuart McCulloch2286f232012-06-15 13:27:53 +00001138 case 'D' : // Double
1139 case 'F' : // Float
1140 case 's' : // String
1141 case 'J' : // Long
1142 const_value_index = in.readUnsignedShort();
1143 return pool[const_value_index];
Stuart McCullochbb014372012-06-07 21:57:32 +00001144
Stuart McCulloch2286f232012-06-15 13:27:53 +00001145 case 'Z' : // Boolean
1146 const_value_index = in.readUnsignedShort();
1147 return pool[const_value_index] == null || pool[const_value_index].equals(0) ? false : true;
Stuart McCullochbb014372012-06-07 21:57:32 +00001148
Stuart McCulloch2286f232012-06-15 13:27:53 +00001149 case 'e' : // enum constant
1150 int type_name_index = in.readUnsignedShort();
1151 if (policy == RetentionPolicy.RUNTIME)
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001152 referTo(type_name_index,0);
Stuart McCulloch2286f232012-06-15 13:27:53 +00001153 int const_name_index = in.readUnsignedShort();
1154 return pool[const_name_index];
Stuart McCullochbb014372012-06-07 21:57:32 +00001155
Stuart McCulloch2286f232012-06-15 13:27:53 +00001156 case 'c' : // Class
1157 int class_info_index = in.readUnsignedShort();
1158 if (policy == RetentionPolicy.RUNTIME)
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001159 referTo(class_info_index,0);
Stuart McCulloch2286f232012-06-15 13:27:53 +00001160 return pool[class_info_index];
Stuart McCullochbb014372012-06-07 21:57:32 +00001161
Stuart McCulloch2286f232012-06-15 13:27:53 +00001162 case '@' : // Annotation type
1163 return doAnnotation(in, member, policy, collect);
Stuart McCullochbb014372012-06-07 21:57:32 +00001164
Stuart McCulloch2286f232012-06-15 13:27:53 +00001165 case '[' : // Array
1166 int num_values = in.readUnsignedShort();
1167 Object[] result = new Object[num_values];
1168 for (int i = 0; i < num_values; i++) {
1169 result[i] = doElementValue(in, member, policy, collect);
1170 }
1171 return result;
Stuart McCullochbb014372012-06-07 21:57:32 +00001172
Stuart McCulloch2286f232012-06-15 13:27:53 +00001173 default :
1174 throw new IllegalArgumentException("Invalid value for Annotation ElementValue tag " + tag);
Stuart McCullochbb014372012-06-07 21:57:32 +00001175 }
1176 }
1177
1178 /**
1179 * Add a new package reference.
1180 *
1181 * @param packageRef
1182 * A '.' delimited package name
1183 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001184 void referTo(TypeRef typeRef, int modifiers) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001185 if (xref != null)
1186 xref.add(typeRef);
1187 if (typeRef.isPrimitive())
1188 return;
1189
1190 PackageRef packageRef = typeRef.getPackageRef();
1191 if (packageRef.isPrimitivePackage())
1192 return;
1193
1194 imports.add(packageRef);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001195
1196 if ( api != null && (Modifier.isPublic(modifiers)||Modifier.isProtected(modifiers)))
1197 api.add(packageRef);
1198 }
1199
1200 void referTo( int index, int modifiers) {
1201 String descriptor = (String) pool[index];
1202 parseDescriptor(descriptor, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001203 }
1204
1205 /**
1206 * This method parses a descriptor and adds the package of the descriptor to
Stuart McCulloch2286f232012-06-15 13:27:53 +00001207 * the referenced packages. The syntax of the descriptor is:
Stuart McCullochbb014372012-06-07 21:57:32 +00001208 *
1209 * <pre>
1210 * descriptor ::= ( '(' reference * ')' )? reference
1211 * reference ::= 'L' classname ( '&lt;' references '&gt;' )? ';' | 'B' | 'Z' | ... | '+' | '-' | '['
1212 * </pre>
1213 *
1214 * This methods uses heavy recursion to parse the descriptor and a roving
1215 * pointer to limit the creation of string objects.
1216 *
1217 * @param descriptor
1218 * The to be parsed descriptor
1219 * @param rover
1220 * The pointer to start at
1221 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001222
1223 public void parseDescriptor(String descriptor, int modifiers) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001224 // Some descriptors are weird, they start with a generic
1225 // declaration that contains ':', not sure what they mean ...
1226 int rover = 0;
1227 if (descriptor.charAt(0) == '<') {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001228 rover = parseFormalTypeParameters(descriptor, rover, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001229 }
1230
1231 if (descriptor.charAt(rover) == '(') {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001232 rover = parseReferences(descriptor, rover + 1, ')', modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001233 rover++;
1234 }
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001235 parseReferences(descriptor, rover, (char) 0, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001236 }
1237
1238 /**
1239 * Parse a sequence of references. A sequence ends with a given character or
1240 * when the string ends.
1241 *
1242 * @param descriptor
1243 * The whole descriptor.
1244 * @param rover
1245 * The index in the descriptor
1246 * @param delimiter
1247 * The end character or 0
1248 * @return the last index processed, one character after the delimeter
1249 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001250 int parseReferences(String descriptor, int rover, char delimiter, int modifiers) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001251 int r = rover;
1252 while (r < descriptor.length() && descriptor.charAt(r) != delimiter) {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001253 r = parseReference(descriptor, r, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001254 }
1255 return r;
1256 }
1257
1258 /**
1259 * Parse a single reference. This can be a single character or an object
1260 * reference when it starts with 'L'.
1261 *
1262 * @param descriptor
1263 * The descriptor
1264 * @param rover
1265 * The place to start
1266 * @return The return index after the reference
1267 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001268 int parseReference(String descriptor, int rover, int modifiers) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001269 int r = rover;
1270 char c = descriptor.charAt(r);
1271 while (c == '[')
1272 c = descriptor.charAt(++r);
1273
1274 if (c == '<') {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001275 r = parseReferences(descriptor, r + 1, '>', modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001276 } else if (c == 'T') {
1277 // Type variable name
1278 r++;
1279 while (descriptor.charAt(r) != ';')
1280 r++;
1281 } else if (c == 'L') {
1282 StringBuilder sb = new StringBuilder();
1283 r++;
1284 while ((c = descriptor.charAt(r)) != ';') {
1285 if (c == '<') {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001286 r = parseReferences(descriptor, r + 1, '>', modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001287 } else
1288 sb.append(c);
1289 r++;
1290 }
1291 TypeRef ref = analyzer.getTypeRef(sb.toString());
1292 if (cd != null)
1293 cd.addReference(ref);
1294
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001295 referTo(ref, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001296 } else {
1297 if ("+-*BCDFIJSZV".indexOf(c) < 0)
1298 ;// System.err.println("Should not skip: " + c);
1299 }
1300
1301 // this skips a lot of characters
1302 // [, *, +, -, B, etc.
1303
1304 return r + 1;
1305 }
1306
1307 /**
1308 * FormalTypeParameters
1309 *
1310 * @param descriptor
1311 * @param index
1312 * @return
1313 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001314 private int parseFormalTypeParameters(String descriptor, int index, int modifiers) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001315 index++;
1316 while (descriptor.charAt(index) != '>') {
1317 // Skip IDENTIFIER
1318 index = descriptor.indexOf(':', index) + 1;
1319 if (index == 0)
1320 throw new IllegalArgumentException("Expected IDENTIFIER: " + descriptor);
1321
1322 // ClassBound? InterfaceBounds
1323
1324 char c = descriptor.charAt(index);
1325
1326 // Class Bound?
1327 if (c == 'L' || c == 'T') {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001328 index = parseReference(descriptor, index, modifiers); // class reference
Stuart McCullochbb014372012-06-07 21:57:32 +00001329 c = descriptor.charAt(index);
1330 }
1331
1332 // Interface Bounds
1333 while (c == ':') {
1334 index++;
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001335 index = parseReference(descriptor, index, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001336 c = descriptor.charAt(index);
1337 } // for each interface
1338
1339 } // for each formal parameter
1340 return index + 1; // skip >
1341 }
1342
1343 public Set<PackageRef> getReferred() {
1344 return imports;
1345 }
1346
1347 public String getAbsolutePath() {
1348 return path;
1349 }
1350
1351 public String getSourceFile() {
1352 return sourceFile;
1353 }
1354
1355 /**
Stuart McCulloch2286f232012-06-15 13:27:53 +00001356 * .class construct for different compilers sun 1.1 Detect static variable
1357 * class$com$acme$MyClass 1.2 " 1.3 " 1.4 " 1.5 ldc_w (class) 1.6 " eclipse
1358 * 1.1 class$0, ldc (string), invokestatic Class.forName 1.2 " 1.3 " 1.5 ldc
1359 * (class) 1.6 " 1.5 and later is not an issue, sun pre 1.5 is easy to
1360 * detect the static variable that decodes the class name. For eclipse, the
1361 * class$0 gives away we have a reference encoded in a string.
Stuart McCullochbb014372012-06-07 21:57:32 +00001362 * compilerversions/compilerversions.jar contains test versions of all
1363 * versions/compilers.
1364 */
1365
1366 public void reset() {
1367 pool = null;
1368 intPool = null;
1369 xref = null;
Stuart McCullochbb014372012-06-07 21:57:32 +00001370 }
1371
1372 public boolean is(QUERY query, Instruction instr, Analyzer analyzer) throws Exception {
1373 switch (query) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001374 case ANY :
1375 return true;
Stuart McCullochbb014372012-06-07 21:57:32 +00001376
Stuart McCulloch2286f232012-06-15 13:27:53 +00001377 case NAMED :
1378 if (instr.matches(getClassName().getDottedOnly()))
Stuart McCullochbb014372012-06-07 21:57:32 +00001379 return !instr.isNegated();
Stuart McCullochbb014372012-06-07 21:57:32 +00001380 return false;
1381
Stuart McCulloch2286f232012-06-15 13:27:53 +00001382 case VERSION :
1383 String v = major + "." + minor;
1384 if (instr.matches(v))
1385 return !instr.isNegated();
Stuart McCullochbb014372012-06-07 21:57:32 +00001386 return false;
1387
Stuart McCulloch2286f232012-06-15 13:27:53 +00001388 case IMPLEMENTS :
1389 for (int i = 0; interfaces != null && i < interfaces.length; i++) {
1390 if (instr.matches(interfaces[i].getDottedOnly()))
1391 return !instr.isNegated();
1392 }
1393 break;
1394
1395 case EXTENDS :
1396 if (zuper == null)
1397 return false;
1398
1399 if (instr.matches(zuper.getDottedOnly()))
Stuart McCullochbb014372012-06-07 21:57:32 +00001400 return !instr.isNegated();
Stuart McCulloch2286f232012-06-15 13:27:53 +00001401 break;
Stuart McCullochbb014372012-06-07 21:57:32 +00001402
Stuart McCulloch2286f232012-06-15 13:27:53 +00001403 case PUBLIC :
1404 return Modifier.isPublic(accessx);
Stuart McCullochbb014372012-06-07 21:57:32 +00001405
Stuart McCulloch2286f232012-06-15 13:27:53 +00001406 case CONCRETE :
1407 return !Modifier.isAbstract(accessx);
Stuart McCullochbb014372012-06-07 21:57:32 +00001408
Stuart McCulloch2286f232012-06-15 13:27:53 +00001409 case ANNOTATED :
1410 if (annotations == null)
1411 return false;
Stuart McCullochbb014372012-06-07 21:57:32 +00001412
Stuart McCulloch2286f232012-06-15 13:27:53 +00001413 for (TypeRef annotation : annotations) {
1414 if (instr.matches(annotation.getFQN()))
1415 return !instr.isNegated();
1416 }
1417
1418 return false;
1419
1420 case RUNTIMEANNOTATIONS :
1421 return hasClassAnnotations;
1422 case CLASSANNOTATIONS :
1423 return hasClassAnnotations;
1424
1425 case ABSTRACT :
1426 return Modifier.isAbstract(accessx);
1427
1428 case IMPORTS :
1429 for (PackageRef imp : imports) {
1430 if (instr.matches(imp.getFQN()))
1431 return !instr.isNegated();
1432 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001433 }
1434
1435 if (zuper == null)
1436 return false;
1437
1438 Clazz clazz = analyzer.findClass(zuper);
1439 if (clazz == null)
1440 return false;
1441
1442 return clazz.is(query, instr, analyzer);
1443 }
1444
1445 public String toString() {
1446 return className.getFQN();
1447 }
1448
1449 /**
1450 * Called when crawling the byte code and a method reference is found
Stuart McCullochbb014372012-06-07 21:57:32 +00001451 */
1452 void getMethodDef(int access, int methodRefPoolIndex) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001453 if (methodRefPoolIndex == 0)
Stuart McCullochbb014372012-06-07 21:57:32 +00001454 return;
Stuart McCulloch2286f232012-06-15 13:27:53 +00001455
Stuart McCullochbb014372012-06-07 21:57:32 +00001456 Object o = pool[methodRefPoolIndex];
1457 if (o != null && o instanceof Assoc) {
1458 Assoc assoc = (Assoc) o;
1459 if (assoc.tag == 10) {
1460 int string_index = intPool[assoc.a];
1461 TypeRef className = analyzer.getTypeRef((String) pool[string_index]);
1462 int name_and_type_index = assoc.b;
1463 Assoc name_and_type = (Assoc) pool[name_and_type_index];
1464 if (name_and_type.tag == 12) {
1465 // Name and Type
1466 int name_index = name_and_type.a;
1467 int type_index = name_and_type.b;
1468 String method = (String) pool[name_index];
1469 String descriptor = (String) pool[type_index];
1470 cd.referenceMethod(access, className, method, descriptor);
1471 } else
1472 throw new IllegalArgumentException(
1473 "Invalid class file (or parsing is wrong), assoc is not type + name (12)");
1474 } else
1475 throw new IllegalArgumentException(
1476 "Invalid class file (or parsing is wrong), Assoc is not method ref! (10)");
1477 } else
Stuart McCulloch2286f232012-06-15 13:27:53 +00001478 throw new IllegalArgumentException("Invalid class file (or parsing is wrong), Not an assoc at a method ref");
Stuart McCullochbb014372012-06-07 21:57:32 +00001479 }
1480
1481 public boolean isPublic() {
1482 return Modifier.isPublic(accessx);
1483 }
1484
1485 public boolean isProtected() {
1486 return Modifier.isProtected(accessx);
1487 }
1488
1489 public boolean isEnum() {
1490 return zuper != null && zuper.getBinary().equals("java/lang/Enum");
1491 }
1492
1493 public JAVA getFormat() {
1494 return JAVA.format(major);
1495
1496 }
1497
1498 public static String objectDescriptorToFQN(String string) {
1499 if (string.startsWith("L") && string.endsWith(";"))
1500 return string.substring(1, string.length() - 1).replace('/', '.');
1501
1502 switch (string.charAt(0)) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001503 case 'V' :
1504 return "void";
1505 case 'B' :
1506 return "byte";
1507 case 'C' :
1508 return "char";
1509 case 'I' :
1510 return "int";
1511 case 'S' :
1512 return "short";
1513 case 'D' :
1514 return "double";
1515 case 'F' :
1516 return "float";
1517 case 'J' :
1518 return "long";
1519 case 'Z' :
1520 return "boolean";
1521 case '[' : // Array
1522 return objectDescriptorToFQN(string.substring(1)) + "[]";
Stuart McCullochbb014372012-06-07 21:57:32 +00001523 }
1524 throw new IllegalArgumentException("Invalid type character in descriptor " + string);
1525 }
1526
1527 public static String unCamel(String id) {
1528 StringBuilder out = new StringBuilder();
1529 for (int i = 0; i < id.length(); i++) {
1530 char c = id.charAt(i);
1531 if (c == '_' || c == '$' || c == '.') {
1532 if (out.length() > 0 && !Character.isWhitespace(out.charAt(out.length() - 1)))
1533 out.append(' ');
1534 continue;
1535 }
1536
1537 int n = i;
1538 while (n < id.length() && Character.isUpperCase(id.charAt(n))) {
1539 n++;
1540 }
1541 if (n == i)
1542 out.append(id.charAt(i));
1543 else {
1544 boolean tolower = (n - i) == 1;
1545 if (i > 0 && !Character.isWhitespace(out.charAt(out.length() - 1)))
1546 out.append(' ');
1547
1548 for (; i < n;) {
1549 if (tolower)
1550 out.append(Character.toLowerCase(id.charAt(i)));
1551 else
1552 out.append(id.charAt(i));
1553 i++;
1554 }
1555 i--;
1556 }
1557 }
1558 if (id.startsWith("."))
1559 out.append(" *");
1560 out.replace(0, 1, Character.toUpperCase(out.charAt(0)) + "");
1561 return out.toString();
1562 }
1563
1564 public boolean isInterface() {
1565 return Modifier.isInterface(accessx);
1566 }
1567
1568 public boolean isAbstract() {
1569 return Modifier.isAbstract(accessx);
1570 }
1571
1572 public int getAccess() {
1573 if (innerAccess == -1)
1574 return accessx;
Stuart McCullochd4826102012-06-26 16:34:24 +00001575 return innerAccess;
Stuart McCullochbb014372012-06-07 21:57:32 +00001576 }
1577
1578 public TypeRef getClassName() {
1579 return className;
1580 }
1581
1582 /**
1583 * To provide an enclosing instance
1584 *
1585 * @param access
1586 * @param name
1587 * @param descriptor
1588 * @return
1589 */
1590 public MethodDef getMethodDef(int access, String name, String descriptor) {
1591 return new MethodDef(access, name, descriptor);
1592 }
1593
1594 public TypeRef getSuper() {
1595 return zuper;
1596 }
1597
1598 public String getFQN() {
1599 return className.getFQN();
1600 }
1601
1602 public TypeRef[] getInterfaces() {
1603 return interfaces;
1604 }
1605
1606 public void setInnerAccess(int access) {
1607 innerAccess = access;
1608 }
1609
1610 public boolean isFinal() {
1611 return Modifier.isFinal(accessx);
1612 }
1613
1614 public void setDeprecated(boolean b) {
1615 deprecated = b;
1616 }
1617
1618 public boolean isDeprecated() {
1619 return deprecated;
1620 }
1621
1622 public boolean isAnnotation() {
1623 return (accessx & ACC_ANNOTATION) != 0;
1624 }
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001625
1626 public Set<PackageRef> getAPIUses() {
1627 if ( api == null)
1628 return Collections.emptySet();
1629 return api;
1630 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001631}