blob: 0a72c77d0c486d7ccf934a4a86d205503dc3f21f [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 McCulloch3b7e6af2012-07-16 14:10:57 +000072 public static JAVA getJava(int major, @SuppressWarnings("unused")
73 int minor) {
Stuart McCullochbb014372012-06-07 21:57:32 +000074 for (JAVA j : JAVA.values()) {
75 if (j.major == major)
76 return j;
77 }
78 return UNKNOWN;
79 }
80
81 public String getEE() {
82 return ee;
83 }
84 }
85
86 public static enum QUERY {
87 IMPLEMENTS, EXTENDS, IMPORTS, NAMED, ANY, VERSION, CONCRETE, ABSTRACT, PUBLIC, ANNOTATED, RUNTIMEANNOTATIONS, CLASSANNOTATIONS;
88
89 }
90
Stuart McCulloch2286f232012-06-15 13:27:53 +000091 public final static EnumSet<QUERY> HAS_ARGUMENT = EnumSet.of(QUERY.IMPLEMENTS, QUERY.EXTENDS, QUERY.IMPORTS,
92 QUERY.NAMED, QUERY.VERSION, QUERY.ANNOTATED);
Stuart McCullochbb014372012-06-07 21:57:32 +000093
94 /**
95 * <pre>
96 * ACC_PUBLIC 0x0001 Declared public; may be accessed from outside its
97 * package.
98 * ACC_FINAL 0x0010 Declared final; no subclasses allowed.
99 * ACC_SUPER 0x0020 Treat superclass methods specially when invoked by the
100 * invokespecial instruction.
101 * ACC_INTERFACE 0x0200 Is an interface, not a
102 * class.
103 * ACC_ABSTRACT 0x0400 Declared abstract; may not be instantiated.
104 * </pre>
105 *
106 * @param mod
107 */
Stuart McCulloch2286f232012-06-15 13:27:53 +0000108 final static int ACC_PUBLIC = 0x0001; // Declared
Stuart McCullochbb014372012-06-07 21:57:32 +0000109 // public;
110 // may
111 // be
112 // accessed
113 // from outside its package.
Stuart McCulloch2286f232012-06-15 13:27:53 +0000114 final static int ACC_FINAL = 0x0010; // Declared
Stuart McCullochbb014372012-06-07 21:57:32 +0000115 // final;
116 // no
117 // subclasses
118 // allowed.
Stuart McCulloch2286f232012-06-15 13:27:53 +0000119 final static int ACC_SUPER = 0x0020; // Treat
Stuart McCullochbb014372012-06-07 21:57:32 +0000120 // superclass
121 // methods
122 // specially when invoked by the
123 // invokespecial instruction.
Stuart McCulloch2286f232012-06-15 13:27:53 +0000124 final static int ACC_INTERFACE = 0x0200; // Is
Stuart McCullochbb014372012-06-07 21:57:32 +0000125 // an
126 // interface,
127 // not
128 // a
129 // classs
Stuart McCulloch2286f232012-06-15 13:27:53 +0000130 final static int ACC_ABSTRACT = 0x0400; // Declared
Stuart McCullochbb014372012-06-07 21:57:32 +0000131
132 // a thing not in the source code
133 final static int ACC_SYNTHETIC = 0x1000;
134 final static int ACC_ANNOTATION = 0x2000;
135 final static int ACC_ENUM = 0x4000;
136
137 static protected class Assoc {
138 Assoc(byte tag, int a, int b) {
139 this.tag = tag;
140 this.a = a;
141 this.b = b;
142 }
143
144 byte tag;
145 int a;
146 int b;
147 }
148
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000149 public abstract class Def {
150
Stuart McCullochbb014372012-06-07 21:57:32 +0000151 final int access;
152 Set<TypeRef> annotations;
153
154 public Def(int access) {
155 this.access = access;
156 }
157
158 public int getAccess() {
159 return access;
160 }
161
162 public boolean isEnum() {
163 return (access & ACC_ENUM) != 0;
164 }
165
166 public boolean isPublic() {
167 return Modifier.isPublic(access);
168 }
169
170 public boolean isAbstract() {
171 return Modifier.isAbstract(access);
172 }
173
174 public boolean isProtected() {
175 return Modifier.isProtected(access);
176 }
177
178 public boolean isFinal() {
179 return Modifier.isFinal(access) || Clazz.this.isFinal();
180 }
181
182 public boolean isStatic() {
183 return Modifier.isStatic(access);
184 }
185
186 public boolean isPrivate() {
187 return Modifier.isPrivate(access);
188 }
189
190 public boolean isNative() {
191 return Modifier.isNative(access);
192 }
193
194 public boolean isTransient() {
195 return Modifier.isTransient(access);
196 }
197
198 public boolean isVolatile() {
199 return Modifier.isVolatile(access);
200 }
201
202 public boolean isInterface() {
203 return Modifier.isInterface(access);
204 }
205
206 public boolean isSynthetic() {
207 return (access & ACC_SYNTHETIC) != 0;
208 }
209
210 void addAnnotation(Annotation a) {
211 if (annotations == null)
212 annotations = Create.set();
213 annotations.add(analyzer.getTypeRef(a.name.getBinary()));
214 }
215
216 public Collection<TypeRef> getAnnotations() {
217 return annotations;
218 }
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000219
220 public TypeRef getOwnerType() {
221 return className;
222 }
223
224 public abstract String getName();
225 public abstract TypeRef getType();
226 public abstract TypeRef[] getPrototype();
227
228 public Object getClazz() {
229 return Clazz.this;
230 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000231 }
232
233 public class FieldDef extends Def {
234 final String name;
235 final Descriptor descriptor;
236 String signature;
237 Object constant;
238 boolean deprecated;
239
240 public boolean isDeprecated() {
241 return deprecated;
242 }
243
244 public void setDeprecated(boolean deprecated) {
245 this.deprecated = deprecated;
246 }
247
248 public FieldDef(int access, String name, String descriptor) {
249 super(access);
250 this.name = name;
251 this.descriptor = analyzer.getDescriptor(descriptor);
252 }
253
254 public String getName() {
255 return name;
256 }
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000257
Stuart McCullochbb014372012-06-07 21:57:32 +0000258
259 public TypeRef getType() {
260 return descriptor.getType();
261 }
262
263 public TypeRef getContainingClass() {
264 return getClassName();
265 }
266
267 public Descriptor getDescriptor() {
268 return descriptor;
269 }
270
271 public void setConstant(Object o) {
272 this.constant = o;
273 }
274
275 public Object getConstant() {
276 return this.constant;
277 }
278
279 // TODO change to use proper generics
280 public String getGenericReturnType() {
281 String use = descriptor.toString();
282 if (signature != null)
283 use = signature;
284
285 Matcher m = METHOD_DESCRIPTOR.matcher(use);
286 if (!m.matches())
287 throw new IllegalArgumentException("Not a valid method descriptor: " + descriptor);
288
289 String returnType = m.group(2);
290 return objectDescriptorToFQN(returnType);
291 }
292
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000293 public TypeRef[] getPrototype() {
294 return null;
295 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000296 public String getSignature() {
297 return signature;
298 }
299
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000300 public String toString() {
301 return name;
302 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000303 }
304
305 public class MethodDef extends FieldDef {
306 public MethodDef(int access, String method, String descriptor) {
307 super(access, method, descriptor);
308 }
309
310 public boolean isConstructor() {
311 return name.equals("<init>") || name.equals("<clinit>");
312 }
313
314 public TypeRef[] getPrototype() {
315 return descriptor.getPrototype();
316 }
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000317 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000318
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000319 public class TypeDef extends Def {
320 TypeRef type;
321 boolean interf;
322
323 public TypeDef(TypeRef type, boolean interf) {
324 super(Modifier.PUBLIC);
325 this.type = type;
326 this.interf = interf;
327 }
328
329 public TypeRef getReference() {
330 return type;
331 }
332
333 public boolean getImplements() {
334 return interf;
335 }
336
337
338 public String getName() {
339 if (interf)
340 return "<implements>";
341 else
342 return "<extends>";
343 }
344
345 public TypeRef getType() {
346 return type;
347 }
348 public TypeRef[] getPrototype() {
349 return null;
350 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000351 }
352
353 final static byte SkipTable[] = { //
Stuart McCulloch2286f232012-06-15 13:27:53 +0000354 0, // 0 non existent
Stuart McCullochbb014372012-06-07 21:57:32 +0000355 -1, // 1 CONSTANT_utf8 UTF 8, handled in
356 // method
357 -1, // 2
358 4, // 3 CONSTANT_Integer
359 4, // 4 CONSTANT_Float
360 8, // 5 CONSTANT_Long (index +=2!)
361 8, // 6 CONSTANT_Double (index +=2!)
362 -1, // 7 CONSTANT_Class
363 2, // 8 CONSTANT_String
364 4, // 9 CONSTANT_FieldRef
365 4, // 10 CONSTANT_MethodRef
366 4, // 11 CONSTANT_InterfaceMethodRef
367 4, // 12 CONSTANT_NameAndType
368 -1, // 13 Not defined
369 -1, // 14 Not defined
370 3, // 15 CONSTANT_MethodHandle
371 2, // 16 CONSTANT_MethodType
372 -1, // 17 Not defined
373 4, // 18 CONSTANT_InvokeDynamic
374 };
375
376 boolean hasRuntimeAnnotations;
377 boolean hasClassAnnotations;
378
379 TypeRef className;
380 Object pool[];
381 int intPool[];
382 Set<PackageRef> imports = Create.set();
383 String path;
384 int minor = 0;
385 int major = 0;
386 int innerAccess = -1;
387 int accessx = 0;
388 String sourceFile;
389 Set<TypeRef> xref;
Stuart McCullochbb014372012-06-07 21:57:32 +0000390 Set<TypeRef> annotations;
391 int forName = 0;
392 int class$ = 0;
393 TypeRef[] interfaces;
394 TypeRef zuper;
395 ClassDataCollector cd = null;
396 Resource resource;
397 FieldDef last = null;
398 boolean deprecated;
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000399 Set<PackageRef> api;
Stuart McCullochbb014372012-06-07 21:57:32 +0000400 final Analyzer analyzer;
401
402 public Clazz(Analyzer analyzer, String path, Resource resource) {
403 this.path = path;
404 this.resource = resource;
405 this.analyzer = analyzer;
406 }
407
408 public Set<TypeRef> parseClassFile() throws Exception {
409 return parseClassFileWithCollector(null);
410 }
411
412 public Set<TypeRef> parseClassFile(InputStream in) throws Exception {
413 return parseClassFile(in, null);
414 }
415
416 public Set<TypeRef> parseClassFileWithCollector(ClassDataCollector cd) throws Exception {
417 InputStream in = resource.openInputStream();
418 try {
419 return parseClassFile(in, cd);
Stuart McCulloch2286f232012-06-15 13:27:53 +0000420 }
421 finally {
Stuart McCullochbb014372012-06-07 21:57:32 +0000422 in.close();
423 }
424 }
425
426 public Set<TypeRef> parseClassFile(InputStream in, ClassDataCollector cd) throws Exception {
427 DataInputStream din = new DataInputStream(in);
428 try {
429 this.cd = cd;
430 return parseClassFile(din);
Stuart McCulloch2286f232012-06-15 13:27:53 +0000431 }
432 finally {
Stuart McCullochbb014372012-06-07 21:57:32 +0000433 cd = null;
434 din.close();
435 }
436 }
437
438 Set<TypeRef> parseClassFile(DataInputStream in) throws Exception {
439 xref = new HashSet<TypeRef>();
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000440
Stuart McCullochbb014372012-06-07 21:57:32 +0000441 boolean crawl = cd != null; // Crawl the byte code if we have a
442 // collector
443 int magic = in.readInt();
444 if (magic != 0xCAFEBABE)
445 throw new IOException("Not a valid class file (no CAFEBABE header)");
446
447 minor = in.readUnsignedShort(); // minor version
448 major = in.readUnsignedShort(); // major version
449 if (cd != null)
450 cd.version(minor, major);
451 int count = in.readUnsignedShort();
452 pool = new Object[count];
453 intPool = new int[count];
454
455 process: for (int poolIndex = 1; poolIndex < count; poolIndex++) {
456 byte tag = in.readByte();
457 switch (tag) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000458 case 0 :
459 break process;
460 case 1 :
461 constantUtf8(in, poolIndex);
462 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000463
Stuart McCulloch2286f232012-06-15 13:27:53 +0000464 case 3 :
465 constantInteger(in, poolIndex);
466 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000467
Stuart McCulloch2286f232012-06-15 13:27:53 +0000468 case 4 :
469 constantFloat(in, poolIndex);
470 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000471
Stuart McCulloch2286f232012-06-15 13:27:53 +0000472 // For some insane optimization reason are
473 // the long and the double two entries in the
474 // constant pool. See 4.4.5
475 case 5 :
476 constantLong(in, poolIndex);
477 poolIndex++;
478 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000479
Stuart McCulloch2286f232012-06-15 13:27:53 +0000480 case 6 :
481 constantDouble(in, poolIndex);
482 poolIndex++;
483 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000484
Stuart McCulloch2286f232012-06-15 13:27:53 +0000485 case 7 :
486 constantClass(in, poolIndex);
487 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000488
Stuart McCulloch2286f232012-06-15 13:27:53 +0000489 case 8 :
490 constantString(in, poolIndex);
491 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000492
Stuart McCulloch2286f232012-06-15 13:27:53 +0000493 case 10 : // Method ref
494 case 11 : // Interface Method ref
495 methodRef(in, poolIndex);
496 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000497
Stuart McCulloch2286f232012-06-15 13:27:53 +0000498 // Name and Type
499 case 12 :
500 nameAndType(in, poolIndex, tag);
501 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000502
Stuart McCulloch2286f232012-06-15 13:27:53 +0000503 // We get the skip count for each record type
504 // from the SkipTable. This will also automatically
505 // abort when
506 default :
507 if (tag == 2)
508 throw new IOException("Invalid tag " + tag);
509 in.skipBytes(SkipTable[tag]);
510 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000511 }
512 }
513
514 pool(pool, intPool);
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000515
516 // All name& type and class constant records contain descriptors we must
517 // treat
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000518 // as references, though not API
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000519
520 for (Object o : pool) {
521 if (o == null)
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000522 continue;
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000523
524 if (o instanceof Assoc && ((Assoc) o).tag == 12) {
525 referTo(((Assoc) o).b, 0); // Descriptor
526 } else if (o instanceof ClassConstant) {
527 String binaryClassName = (String) pool[((ClassConstant) o).cname];
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000528 TypeRef typeRef = analyzer.getTypeRef(binaryClassName);
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000529 referTo(typeRef, 0);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000530 }
531 }
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000532
Stuart McCullochbb014372012-06-07 21:57:32 +0000533 /*
534 * Parse after the constant pool, code thanks to Hans Christian
535 * Falkenberg
536 */
537
538 accessx = in.readUnsignedShort(); // access
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000539 if (Modifier.isPublic(accessx))
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000540 api = new HashSet<PackageRef>();
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000541
Stuart McCullochbb014372012-06-07 21:57:32 +0000542 int this_class = in.readUnsignedShort();
543 className = analyzer.getTypeRef((String) pool[intPool[this_class]]);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000544 referTo(className, Modifier.PUBLIC);
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000545
Stuart McCullochbb014372012-06-07 21:57:32 +0000546 try {
547
548 if (cd != null) {
549 if (!cd.classStart(accessx, className))
550 return null;
551 }
552
553 int super_class = in.readUnsignedShort();
554 String superName = (String) pool[intPool[super_class]];
555 if (superName != null) {
556 zuper = analyzer.getTypeRef(superName);
557 }
558
559 if (zuper != null) {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000560 referTo(zuper, accessx);
Stuart McCullochbb014372012-06-07 21:57:32 +0000561 if (cd != null)
562 cd.extendsClass(zuper);
563 }
564
565 int interfacesCount = in.readUnsignedShort();
566 if (interfacesCount > 0) {
567 interfaces = new TypeRef[interfacesCount];
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000568 for (int i = 0; i < interfacesCount; i++) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000569 interfaces[i] = analyzer.getTypeRef((String) pool[intPool[in.readUnsignedShort()]]);
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000570 referTo(interfaces[i], accessx);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000571 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000572 if (cd != null)
573 cd.implementsInterfaces(interfaces);
574 }
575
576 int fieldsCount = in.readUnsignedShort();
577 for (int i = 0; i < fieldsCount; i++) {
578 int access_flags = in.readUnsignedShort(); // skip access flags
579 int name_index = in.readUnsignedShort();
580 int descriptor_index = in.readUnsignedShort();
581
582 // Java prior to 1.5 used a weird
583 // static variable to hold the com.X.class
584 // result construct. If it did not find it
585 // it would create a variable class$com$X
586 // that would be used to hold the class
587 // object gotten with Class.forName ...
588 // Stupidly, they did not actively use the
589 // class name for the field type, so bnd
590 // would not see a reference. We detect
591 // this case and add an artificial descriptor
592 String name = pool[name_index].toString(); // name_index
593 if (name.startsWith("class$")) {
594 crawl = true;
595 }
596 if (cd != null)
Stuart McCulloch2286f232012-06-15 13:27:53 +0000597 cd.field(last = new FieldDef(access_flags, name, pool[descriptor_index].toString()));
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000598
599 referTo(descriptor_index, access_flags);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000600 doAttributes(in, ElementType.FIELD, false, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000601 }
602
603 //
604 // Check if we have to crawl the code to find
605 // the ldc(_w) <string constant> invokestatic Class.forName
606 // if so, calculate the method ref index so we
607 // can do this efficiently
608 //
609 if (crawl) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000610 forName = findMethodReference("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
611 class$ = findMethodReference(className.getBinary(), "class$", "(Ljava/lang/String;)Ljava/lang/Class;");
612 } else if (major == 48) {
613 forName = findMethodReference("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
Stuart McCullochbb014372012-06-07 21:57:32 +0000614 if (forName > 0) {
615 crawl = true;
616 class$ = findMethodReference(className.getBinary(), "class$",
617 "(Ljava/lang/String;)Ljava/lang/Class;");
618 }
619 }
Stuart McCulloch2286f232012-06-15 13:27:53 +0000620
Stuart McCullochbb014372012-06-07 21:57:32 +0000621 // There are some serious changes in the
622 // class file format. So we do not do any crawling
623 // it has also become less important
Stuart McCulloch2286f232012-06-15 13:27:53 +0000624 if (major >= JAVA.OpenJDK7.major)
Stuart McCullochbb014372012-06-07 21:57:32 +0000625 crawl = false;
626
627 //
628 // Handle the methods
629 //
630 int methodCount = in.readUnsignedShort();
631 for (int i = 0; i < methodCount; i++) {
632 int access_flags = in.readUnsignedShort();
633 int name_index = in.readUnsignedShort();
634 int descriptor_index = in.readUnsignedShort();
Stuart McCullochbb014372012-06-07 21:57:32 +0000635 String name = pool[name_index].toString();
636 String descriptor = pool[descriptor_index].toString();
637 if (cd != null) {
638 MethodDef mdef = new MethodDef(access_flags, name, descriptor);
639 last = mdef;
640 cd.method(mdef);
641 }
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000642 referTo(descriptor_index, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000643
644 if ("<init>".equals(name)) {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000645 doAttributes(in, ElementType.CONSTRUCTOR, crawl, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000646 } else {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000647 doAttributes(in, ElementType.METHOD, crawl, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000648 }
649 }
650 if (cd != null)
651 cd.memberEnd();
652
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000653 doAttributes(in, ElementType.TYPE, false, accessx);
Stuart McCullochbb014372012-06-07 21:57:32 +0000654
655 //
656 // Parse all the descriptors we found
657 //
658
Stuart McCullochbb014372012-06-07 21:57:32 +0000659 Set<TypeRef> xref = this.xref;
660 reset();
661 return xref;
Stuart McCulloch2286f232012-06-15 13:27:53 +0000662 }
663 finally {
Stuart McCullochbb014372012-06-07 21:57:32 +0000664 if (cd != null)
665 cd.classEnd();
666 }
667 }
668
669 private void constantFloat(DataInputStream in, int poolIndex) throws IOException {
670 if (cd != null)
671 pool[poolIndex] = in.readFloat(); // ALU
672 else
673 in.skipBytes(4);
674 }
675
676 private void constantInteger(DataInputStream in, int poolIndex) throws IOException {
677 intPool[poolIndex] = in.readInt();
678 if (cd != null)
679 pool[poolIndex] = intPool[poolIndex];
680 }
681
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000682 protected void pool(@SuppressWarnings("unused")
683 Object[] pool, @SuppressWarnings("unused")
684 int[] intPool) {}
Stuart McCullochbb014372012-06-07 21:57:32 +0000685
686 /**
687 * @param in
688 * @param poolIndex
689 * @param tag
690 * @throws IOException
691 */
692 protected void nameAndType(DataInputStream in, int poolIndex, byte tag) throws IOException {
693 int name_index = in.readUnsignedShort();
694 int descriptor_index = in.readUnsignedShort();
Stuart McCullochbb014372012-06-07 21:57:32 +0000695 pool[poolIndex] = new Assoc(tag, name_index, descriptor_index);
696 }
697
698 /**
699 * @param in
700 * @param poolIndex
701 * @param tag
702 * @throws IOException
703 */
704 private void methodRef(DataInputStream in, int poolIndex) throws IOException {
705 int class_index = in.readUnsignedShort();
706 int name_and_type_index = in.readUnsignedShort();
707 pool[poolIndex] = new Assoc((byte) 10, class_index, name_and_type_index);
708 }
709
710 /**
711 * @param in
712 * @param poolIndex
713 * @throws IOException
714 */
715 private void constantString(DataInputStream in, int poolIndex) throws IOException {
716 int string_index = in.readUnsignedShort();
717 intPool[poolIndex] = string_index;
718 }
719
720 /**
721 * @param in
722 * @param poolIndex
723 * @throws IOException
724 */
725 protected void constantClass(DataInputStream in, int poolIndex) throws IOException {
726 int class_index = in.readUnsignedShort();
Stuart McCullochbb014372012-06-07 21:57:32 +0000727 intPool[poolIndex] = class_index;
728 ClassConstant c = new ClassConstant(class_index);
729 pool[poolIndex] = c;
730 }
731
732 /**
733 * @param in
734 * @throws IOException
735 */
736 protected void constantDouble(DataInputStream in, int poolIndex) throws IOException {
737 if (cd != null)
738 pool[poolIndex] = in.readDouble();
739 else
740 in.skipBytes(8);
741 }
742
743 /**
744 * @param in
745 * @throws IOException
746 */
747 protected void constantLong(DataInputStream in, int poolIndex) throws IOException {
748 if (cd != null) {
749 pool[poolIndex] = in.readLong();
750 } else
751 in.skipBytes(8);
752 }
753
754 /**
755 * @param in
756 * @param poolIndex
757 * @throws IOException
758 */
759 protected void constantUtf8(DataInputStream in, int poolIndex) throws IOException {
760 // CONSTANT_Utf8
761
762 String name = in.readUTF();
763 pool[poolIndex] = name;
764 }
765
766 /**
767 * Find a method reference in the pool that points to the given class,
768 * methodname and descriptor.
769 *
770 * @param clazz
771 * @param methodname
772 * @param descriptor
773 * @return index in constant pool
774 */
775 private int findMethodReference(String clazz, String methodname, String descriptor) {
776 for (int i = 1; i < pool.length; i++) {
777 if (pool[i] instanceof Assoc) {
778 Assoc methodref = (Assoc) pool[i];
779 if (methodref.tag == 10) {
780 // Method ref
781 int class_index = methodref.a;
782 int class_name_index = intPool[class_index];
783 if (clazz.equals(pool[class_name_index])) {
784 int name_and_type_index = methodref.b;
785 Assoc name_and_type = (Assoc) pool[name_and_type_index];
786 if (name_and_type.tag == 12) {
787 // Name and Type
788 int name_index = name_and_type.a;
789 int type_index = name_and_type.b;
790 if (methodname.equals(pool[name_index])) {
791 if (descriptor.equals(pool[type_index])) {
792 return i;
793 }
794 }
795 }
796 }
797 }
798 }
799 }
800 return -1;
801 }
802
803 /**
804 * Called for each attribute in the class, field, or method.
805 *
806 * @param in
807 * The stream
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000808 * @param access_flags
Stuart McCullochbb014372012-06-07 21:57:32 +0000809 * @throws Exception
810 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000811 private void doAttributes(DataInputStream in, ElementType member, boolean crawl, int access_flags) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +0000812 int attributesCount = in.readUnsignedShort();
813 for (int j = 0; j < attributesCount; j++) {
814 // skip name CONSTANT_Utf8 pointer
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000815 doAttribute(in, member, crawl, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000816 }
817 }
818
819 /**
820 * Process a single attribute, if not recognized, skip it.
821 *
822 * @param in
823 * the data stream
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000824 * @param access_flags
Stuart McCullochbb014372012-06-07 21:57:32 +0000825 * @throws Exception
826 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000827 private void doAttribute(DataInputStream in, ElementType member, boolean crawl, int access_flags) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +0000828 int attribute_name_index = in.readUnsignedShort();
829 String attributeName = (String) pool[attribute_name_index];
830 long attribute_length = in.readInt();
831 attribute_length &= 0xFFFFFFFF;
832 if ("Deprecated".equals(attributeName)) {
833 if (cd != null)
834 cd.deprecated();
835 } else if ("RuntimeVisibleAnnotations".equals(attributeName))
836 doAnnotations(in, member, RetentionPolicy.RUNTIME);
837 else if ("RuntimeVisibleParameterAnnotations".equals(attributeName))
838 doParameterAnnotations(in, member, RetentionPolicy.RUNTIME);
839 else if ("RuntimeInvisibleAnnotations".equals(attributeName))
840 doAnnotations(in, member, RetentionPolicy.CLASS);
841 else if ("RuntimeInvisibleParameterAnnotations".equals(attributeName))
842 doParameterAnnotations(in, member, RetentionPolicy.CLASS);
843 else if ("InnerClasses".equals(attributeName))
844 doInnerClasses(in);
845 else if ("EnclosingMethod".equals(attributeName))
846 doEnclosingMethod(in);
847 else if ("SourceFile".equals(attributeName))
848 doSourceFile(in);
849 else if ("Code".equals(attributeName) && crawl)
850 doCode(in);
851 else if ("Signature".equals(attributeName))
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000852 doSignature(in, member, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000853 else if ("ConstantValue".equals(attributeName))
854 doConstantValue(in);
855 else {
856 if (attribute_length > 0x7FFFFFFF) {
857 throw new IllegalArgumentException("Attribute > 2Gb");
858 }
859 in.skipBytes((int) attribute_length);
860 }
861 }
862
863 /**
864 * <pre>
865 * EnclosingMethod_attribute {
866 * u2 attribute_name_index;
867 * u4 attribute_length;
868 * u2 class_index
869 * u2 method_index;
870 * }
871 * </pre>
872 *
Stuart McCullochbb014372012-06-07 21:57:32 +0000873 * @param in
874 * @throws IOException
875 */
876 private void doEnclosingMethod(DataInputStream in) throws IOException {
877 int cIndex = in.readShort();
878 int mIndex = in.readShort();
879
880 if (cd != null) {
881 int nameIndex = intPool[cIndex];
882 TypeRef cName = analyzer.getTypeRef((String) pool[nameIndex]);
883
884 String mName = null;
885 String mDescriptor = null;
886
887 if (mIndex != 0) {
888 Assoc nameAndType = (Assoc) pool[mIndex];
889 mName = (String) pool[nameAndType.a];
890 mDescriptor = (String) pool[nameAndType.b];
891 }
892 cd.enclosingMethod(cName, mName, mDescriptor);
893 }
894 }
895
896 /**
897 * <pre>
898 * InnerClasses_attribute {
899 * u2 attribute_name_index;
900 * u4 attribute_length;
901 * u2 number_of_classes; {
902 * u2 inner_class_info_index;
903 * u2 outer_class_info_index;
904 * u2 inner_name_index;
905 * u2 inner_class_access_flags;
906 * } classes[number_of_classes];
907 * }
908 * </pre>
909 *
910 * @param in
911 * @throws Exception
912 */
913 private void doInnerClasses(DataInputStream in) throws Exception {
914 int number_of_classes = in.readShort();
915 for (int i = 0; i < number_of_classes; i++) {
916 int inner_class_info_index = in.readShort();
917 int outer_class_info_index = in.readShort();
918 int inner_name_index = in.readShort();
919 int inner_class_access_flags = in.readShort() & 0xFFFF;
920
921 if (cd != null) {
922 TypeRef innerClass = null;
923 TypeRef outerClass = null;
924 String innerName = null;
925
926 if (inner_class_info_index != 0) {
927 int nameIndex = intPool[inner_class_info_index];
928 innerClass = analyzer.getTypeRef((String) pool[nameIndex]);
929 }
930
931 if (outer_class_info_index != 0) {
932 int nameIndex = intPool[outer_class_info_index];
933 outerClass = analyzer.getTypeRef((String) pool[nameIndex]);
934 }
935
936 if (inner_name_index != 0)
937 innerName = (String) pool[inner_name_index];
938
939 cd.innerClass(innerClass, outerClass, innerName, inner_class_access_flags);
940 }
941 }
942 }
943
944 /**
945 * Handle a signature
946 *
947 * <pre>
948 * Signature_attribute {
949 * u2 attribute_name_index;
950 * u4 attribute_length;
951 * u2 signature_index;
952 * }
953 * </pre>
954 *
955 * @param member
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000956 * @param access_flags
Stuart McCullochbb014372012-06-07 21:57:32 +0000957 */
958
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000959 void doSignature(DataInputStream in, ElementType member, int access_flags) throws IOException {
Stuart McCullochbb014372012-06-07 21:57:32 +0000960 int signature_index = in.readUnsignedShort();
961 String signature = (String) pool[signature_index];
962
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000963 parseDescriptor(signature, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000964
965 if (last != null)
966 last.signature = signature;
967
968 if (cd != null)
969 cd.signature(signature);
970 }
971
972 /**
973 * Handle a constant value call the data collector with it
974 */
975 void doConstantValue(DataInputStream in) throws IOException {
976 int constantValue_index = in.readUnsignedShort();
977 if (cd == null)
978 return;
979
980 Object object = pool[constantValue_index];
981 if (object == null)
982 object = pool[intPool[constantValue_index]];
983
984 last.constant = object;
985 cd.constant(object);
986 }
987
988 /**
989 * <pre>
990 * Code_attribute {
991 * u2 attribute_name_index;
992 * u4 attribute_length;
993 * u2 max_stack;
994 * u2 max_locals;
995 * u4 code_length;
996 * u1 code[code_length];
997 * u2 exception_table_length;
998 * { u2 start_pc;
999 * u2 end_pc;
1000 * u2 handler_pc;
1001 * u2 catch_type;
1002 * } exception_table[exception_table_length];
1003 * u2 attributes_count;
1004 * attribute_info attributes[attributes_count];
1005 * }
1006 * </pre>
1007 *
1008 * @param in
1009 * @param pool
1010 * @throws Exception
1011 */
1012 private void doCode(DataInputStream in) throws Exception {
1013 /* int max_stack = */in.readUnsignedShort();
1014 /* int max_locals = */in.readUnsignedShort();
1015 int code_length = in.readInt();
1016 byte code[] = new byte[code_length];
1017 in.readFully(code);
1018 crawl(code);
1019 int exception_table_length = in.readUnsignedShort();
1020 in.skipBytes(exception_table_length * 8);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001021 doAttributes(in, ElementType.METHOD, false, 0);
Stuart McCullochbb014372012-06-07 21:57:32 +00001022 }
1023
1024 /**
1025 * We must find Class.forName references ...
1026 *
1027 * @param code
1028 */
1029 protected void crawl(byte[] code) {
1030 ByteBuffer bb = ByteBuffer.wrap(code);
1031 bb.order(ByteOrder.BIG_ENDIAN);
1032 int lastReference = -1;
1033
1034 while (bb.remaining() > 0) {
1035 int instruction = 0xFF & bb.get();
1036 switch (instruction) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001037 case OpCodes.ldc :
1038 lastReference = 0xFF & bb.get();
1039 break;
Stuart McCullochbb014372012-06-07 21:57:32 +00001040
Stuart McCulloch2286f232012-06-15 13:27:53 +00001041 case OpCodes.ldc_w :
1042 lastReference = 0xFFFF & bb.getShort();
1043 break;
Stuart McCullochbb014372012-06-07 21:57:32 +00001044
Stuart McCulloch2286f232012-06-15 13:27:53 +00001045 case OpCodes.invokespecial : {
1046 int mref = 0xFFFF & bb.getShort();
1047 if (cd != null)
1048 getMethodDef(0, mref);
1049 break;
1050 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001051
Stuart McCulloch2286f232012-06-15 13:27:53 +00001052 case OpCodes.invokevirtual : {
1053 int mref = 0xFFFF & bb.getShort();
1054 if (cd != null)
1055 getMethodDef(0, mref);
1056 break;
1057 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001058
Stuart McCulloch2286f232012-06-15 13:27:53 +00001059 case OpCodes.invokeinterface : {
1060 int mref = 0xFFFF & bb.getShort();
1061 if (cd != null)
1062 getMethodDef(0, mref);
1063 break;
1064 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001065
Stuart McCulloch2286f232012-06-15 13:27:53 +00001066 case OpCodes.invokestatic : {
1067 int methodref = 0xFFFF & bb.getShort();
1068 if (cd != null)
1069 getMethodDef(0, methodref);
Stuart McCullochbb014372012-06-07 21:57:32 +00001070
Stuart McCulloch2286f232012-06-15 13:27:53 +00001071 if ((methodref == forName || methodref == class$) && lastReference != -1
1072 && pool[intPool[lastReference]] instanceof String) {
1073 String fqn = (String) pool[intPool[lastReference]];
1074 if (!fqn.equals("class") && fqn.indexOf('.') > 0) {
1075 TypeRef clazz = analyzer.getTypeRefFromFQN(fqn);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001076 referTo(clazz, 0);
Stuart McCulloch2286f232012-06-15 13:27:53 +00001077 }
1078 lastReference = -1;
1079 }
1080 break;
1081 }
1082
1083 case OpCodes.tableswitch :
1084 // Skip to place divisible by 4
1085 while ((bb.position() & 0x3) != 0)
1086 bb.get();
1087 /* int deflt = */
1088 bb.getInt();
1089 int low = bb.getInt();
1090 int high = bb.getInt();
1091 try {
1092 bb.position(bb.position() + (high - low + 1) * 4);
1093 }
1094 catch (Exception e) {
1095 // TODO Auto-generated catch block
1096 e.printStackTrace();
Stuart McCullochbb014372012-06-07 21:57:32 +00001097 }
1098 lastReference = -1;
Stuart McCulloch2286f232012-06-15 13:27:53 +00001099 break;
Stuart McCullochbb014372012-06-07 21:57:32 +00001100
Stuart McCulloch2286f232012-06-15 13:27:53 +00001101 case OpCodes.lookupswitch :
1102 // Skip to place divisible by 4
1103 while ((bb.position() & 0x3) != 0)
1104 bb.get();
1105 /* deflt = */
1106 bb.getInt();
1107 int npairs = bb.getInt();
1108 bb.position(bb.position() + npairs * 8);
1109 lastReference = -1;
1110 break;
Stuart McCullochbb014372012-06-07 21:57:32 +00001111
Stuart McCulloch2286f232012-06-15 13:27:53 +00001112 default :
1113 lastReference = -1;
1114 bb.position(bb.position() + OpCodes.OFFSETS[instruction]);
Stuart McCullochbb014372012-06-07 21:57:32 +00001115 }
1116 }
1117 }
1118
1119 private void doSourceFile(DataInputStream in) throws IOException {
1120 int sourcefile_index = in.readUnsignedShort();
1121 this.sourceFile = pool[sourcefile_index].toString();
1122 }
1123
Stuart McCulloch2286f232012-06-15 13:27:53 +00001124 private void doParameterAnnotations(DataInputStream in, ElementType member, RetentionPolicy policy)
1125 throws IOException {
Stuart McCullochbb014372012-06-07 21:57:32 +00001126 int num_parameters = in.readUnsignedByte();
1127 for (int p = 0; p < num_parameters; p++) {
1128 if (cd != null)
1129 cd.parameter(p);
1130 doAnnotations(in, member, policy);
1131 }
1132 }
1133
Stuart McCulloch2286f232012-06-15 13:27:53 +00001134 private void doAnnotations(DataInputStream in, ElementType member, RetentionPolicy policy) throws IOException {
Stuart McCullochbb014372012-06-07 21:57:32 +00001135 int num_annotations = in.readUnsignedShort(); // # of annotations
1136 for (int a = 0; a < num_annotations; a++) {
1137 if (cd == null)
1138 doAnnotation(in, member, policy, false);
1139 else {
1140 Annotation annotion = doAnnotation(in, member, policy, true);
1141 cd.annotation(annotion);
1142 }
1143 }
1144 }
1145
Stuart McCulloch2286f232012-06-15 13:27:53 +00001146 private Annotation doAnnotation(DataInputStream in, ElementType member, RetentionPolicy policy, boolean collect)
1147 throws IOException {
Stuart McCullochbb014372012-06-07 21:57:32 +00001148 int type_index = in.readUnsignedShort();
1149 if (annotations == null)
1150 annotations = new HashSet<TypeRef>();
1151
1152 TypeRef tr = analyzer.getTypeRef(pool[type_index].toString());
1153 annotations.add(tr);
1154
1155 if (policy == RetentionPolicy.RUNTIME) {
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001156 referTo(type_index, 0);
Stuart McCullochbb014372012-06-07 21:57:32 +00001157 hasRuntimeAnnotations = true;
1158 } else {
1159 hasClassAnnotations = true;
1160 }
1161 TypeRef name = analyzer.getTypeRef((String) pool[type_index]);
1162 int num_element_value_pairs = in.readUnsignedShort();
Stuart McCulloch2286f232012-06-15 13:27:53 +00001163 Map<String,Object> elements = null;
Stuart McCullochbb014372012-06-07 21:57:32 +00001164 for (int v = 0; v < num_element_value_pairs; v++) {
1165 int element_name_index = in.readUnsignedShort();
1166 String element = (String) pool[element_name_index];
1167 Object value = doElementValue(in, member, policy, collect);
1168 if (collect) {
1169 if (elements == null)
Stuart McCulloch2286f232012-06-15 13:27:53 +00001170 elements = new LinkedHashMap<String,Object>();
Stuart McCullochbb014372012-06-07 21:57:32 +00001171 elements.put(element, value);
1172 }
1173 }
1174 if (collect)
1175 return new Annotation(name, elements, member, policy);
Stuart McCullochd4826102012-06-26 16:34:24 +00001176 return null;
Stuart McCullochbb014372012-06-07 21:57:32 +00001177 }
1178
Stuart McCulloch2286f232012-06-15 13:27:53 +00001179 private Object doElementValue(DataInputStream in, ElementType member, RetentionPolicy policy, boolean collect)
1180 throws IOException {
Stuart McCullochbb014372012-06-07 21:57:32 +00001181 char tag = (char) in.readUnsignedByte();
1182 switch (tag) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001183 case 'B' : // Byte
1184 case 'C' : // Character
1185 case 'I' : // Integer
1186 case 'S' : // Short
1187 int const_value_index = in.readUnsignedShort();
1188 return intPool[const_value_index];
Stuart McCullochbb014372012-06-07 21:57:32 +00001189
Stuart McCulloch2286f232012-06-15 13:27:53 +00001190 case 'D' : // Double
1191 case 'F' : // Float
1192 case 's' : // String
1193 case 'J' : // Long
1194 const_value_index = in.readUnsignedShort();
1195 return pool[const_value_index];
Stuart McCullochbb014372012-06-07 21:57:32 +00001196
Stuart McCulloch2286f232012-06-15 13:27:53 +00001197 case 'Z' : // Boolean
1198 const_value_index = in.readUnsignedShort();
1199 return pool[const_value_index] == null || pool[const_value_index].equals(0) ? false : true;
Stuart McCullochbb014372012-06-07 21:57:32 +00001200
Stuart McCulloch2286f232012-06-15 13:27:53 +00001201 case 'e' : // enum constant
1202 int type_name_index = in.readUnsignedShort();
1203 if (policy == RetentionPolicy.RUNTIME)
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001204 referTo(type_name_index, 0);
Stuart McCulloch2286f232012-06-15 13:27:53 +00001205 int const_name_index = in.readUnsignedShort();
1206 return pool[const_name_index];
Stuart McCullochbb014372012-06-07 21:57:32 +00001207
Stuart McCulloch2286f232012-06-15 13:27:53 +00001208 case 'c' : // Class
1209 int class_info_index = in.readUnsignedShort();
1210 if (policy == RetentionPolicy.RUNTIME)
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001211 referTo(class_info_index, 0);
Stuart McCulloch2286f232012-06-15 13:27:53 +00001212 return pool[class_info_index];
Stuart McCullochbb014372012-06-07 21:57:32 +00001213
Stuart McCulloch2286f232012-06-15 13:27:53 +00001214 case '@' : // Annotation type
1215 return doAnnotation(in, member, policy, collect);
Stuart McCullochbb014372012-06-07 21:57:32 +00001216
Stuart McCulloch2286f232012-06-15 13:27:53 +00001217 case '[' : // Array
1218 int num_values = in.readUnsignedShort();
1219 Object[] result = new Object[num_values];
1220 for (int i = 0; i < num_values; i++) {
1221 result[i] = doElementValue(in, member, policy, collect);
1222 }
1223 return result;
Stuart McCullochbb014372012-06-07 21:57:32 +00001224
Stuart McCulloch2286f232012-06-15 13:27:53 +00001225 default :
1226 throw new IllegalArgumentException("Invalid value for Annotation ElementValue tag " + tag);
Stuart McCullochbb014372012-06-07 21:57:32 +00001227 }
1228 }
1229
1230 /**
1231 * Add a new package reference.
1232 *
1233 * @param packageRef
1234 * A '.' delimited package name
1235 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001236 void referTo(TypeRef typeRef, int modifiers) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001237 if (xref != null)
1238 xref.add(typeRef);
1239 if (typeRef.isPrimitive())
1240 return;
1241
1242 PackageRef packageRef = typeRef.getPackageRef();
1243 if (packageRef.isPrimitivePackage())
1244 return;
1245
1246 imports.add(packageRef);
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001247
1248 if (api != null && (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)))
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001249 api.add(packageRef);
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001250
1251 if (cd != null)
1252 cd.referTo(typeRef, modifiers);
1253
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001254 }
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001255
1256 void referTo(int index, int modifiers) {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001257 String descriptor = (String) pool[index];
1258 parseDescriptor(descriptor, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001259 }
1260
1261 /**
1262 * This method parses a descriptor and adds the package of the descriptor to
Stuart McCulloch2286f232012-06-15 13:27:53 +00001263 * the referenced packages. The syntax of the descriptor is:
Stuart McCullochbb014372012-06-07 21:57:32 +00001264 *
1265 * <pre>
1266 * descriptor ::= ( '(' reference * ')' )? reference
1267 * reference ::= 'L' classname ( '&lt;' references '&gt;' )? ';' | 'B' | 'Z' | ... | '+' | '-' | '['
1268 * </pre>
1269 *
1270 * This methods uses heavy recursion to parse the descriptor and a roving
1271 * pointer to limit the creation of string objects.
1272 *
1273 * @param descriptor
1274 * The to be parsed descriptor
1275 * @param rover
1276 * The pointer to start at
1277 */
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001278
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001279 public void parseDescriptor(String descriptor, int modifiers) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001280 // Some descriptors are weird, they start with a generic
1281 // declaration that contains ':', not sure what they mean ...
1282 int rover = 0;
1283 if (descriptor.charAt(0) == '<') {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001284 rover = parseFormalTypeParameters(descriptor, rover, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001285 }
1286
1287 if (descriptor.charAt(rover) == '(') {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001288 rover = parseReferences(descriptor, rover + 1, ')', modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001289 rover++;
1290 }
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001291 parseReferences(descriptor, rover, (char) 0, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001292 }
1293
1294 /**
1295 * Parse a sequence of references. A sequence ends with a given character or
1296 * when the string ends.
1297 *
1298 * @param descriptor
1299 * The whole descriptor.
1300 * @param rover
1301 * The index in the descriptor
1302 * @param delimiter
1303 * The end character or 0
1304 * @return the last index processed, one character after the delimeter
1305 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001306 int parseReferences(String descriptor, int rover, char delimiter, int modifiers) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001307 int r = rover;
1308 while (r < descriptor.length() && descriptor.charAt(r) != delimiter) {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001309 r = parseReference(descriptor, r, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001310 }
1311 return r;
1312 }
1313
1314 /**
1315 * Parse a single reference. This can be a single character or an object
1316 * reference when it starts with 'L'.
1317 *
1318 * @param descriptor
1319 * The descriptor
1320 * @param rover
1321 * The place to start
1322 * @return The return index after the reference
1323 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001324 int parseReference(String descriptor, int rover, int modifiers) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001325 int r = rover;
1326 char c = descriptor.charAt(r);
1327 while (c == '[')
1328 c = descriptor.charAt(++r);
1329
1330 if (c == '<') {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001331 r = parseReferences(descriptor, r + 1, '>', modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001332 } else if (c == 'T') {
1333 // Type variable name
1334 r++;
1335 while (descriptor.charAt(r) != ';')
1336 r++;
1337 } else if (c == 'L') {
1338 StringBuilder sb = new StringBuilder();
1339 r++;
1340 while ((c = descriptor.charAt(r)) != ';') {
1341 if (c == '<') {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001342 r = parseReferences(descriptor, r + 1, '>', modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001343 } else
1344 sb.append(c);
1345 r++;
1346 }
1347 TypeRef ref = analyzer.getTypeRef(sb.toString());
1348 if (cd != null)
1349 cd.addReference(ref);
1350
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001351 referTo(ref, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001352 } else {
1353 if ("+-*BCDFIJSZV".indexOf(c) < 0)
1354 ;// System.err.println("Should not skip: " + c);
1355 }
1356
1357 // this skips a lot of characters
1358 // [, *, +, -, B, etc.
1359
1360 return r + 1;
1361 }
1362
1363 /**
1364 * FormalTypeParameters
1365 *
1366 * @param descriptor
1367 * @param index
1368 * @return
1369 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001370 private int parseFormalTypeParameters(String descriptor, int index, int modifiers) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001371 index++;
1372 while (descriptor.charAt(index) != '>') {
1373 // Skip IDENTIFIER
1374 index = descriptor.indexOf(':', index) + 1;
1375 if (index == 0)
1376 throw new IllegalArgumentException("Expected IDENTIFIER: " + descriptor);
1377
1378 // ClassBound? InterfaceBounds
1379
1380 char c = descriptor.charAt(index);
1381
1382 // Class Bound?
1383 if (c == 'L' || c == 'T') {
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001384 index = parseReference(descriptor, index, modifiers); // class
1385 // reference
Stuart McCullochbb014372012-06-07 21:57:32 +00001386 c = descriptor.charAt(index);
1387 }
1388
1389 // Interface Bounds
1390 while (c == ':') {
1391 index++;
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001392 index = parseReference(descriptor, index, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001393 c = descriptor.charAt(index);
1394 } // for each interface
1395
1396 } // for each formal parameter
1397 return index + 1; // skip >
1398 }
1399
1400 public Set<PackageRef> getReferred() {
1401 return imports;
1402 }
1403
1404 public String getAbsolutePath() {
1405 return path;
1406 }
1407
1408 public String getSourceFile() {
1409 return sourceFile;
1410 }
1411
1412 /**
Stuart McCulloch2286f232012-06-15 13:27:53 +00001413 * .class construct for different compilers sun 1.1 Detect static variable
1414 * class$com$acme$MyClass 1.2 " 1.3 " 1.4 " 1.5 ldc_w (class) 1.6 " eclipse
1415 * 1.1 class$0, ldc (string), invokestatic Class.forName 1.2 " 1.3 " 1.5 ldc
1416 * (class) 1.6 " 1.5 and later is not an issue, sun pre 1.5 is easy to
1417 * detect the static variable that decodes the class name. For eclipse, the
1418 * class$0 gives away we have a reference encoded in a string.
Stuart McCullochbb014372012-06-07 21:57:32 +00001419 * compilerversions/compilerversions.jar contains test versions of all
1420 * versions/compilers.
1421 */
1422
1423 public void reset() {
1424 pool = null;
1425 intPool = null;
1426 xref = null;
Stuart McCullochbb014372012-06-07 21:57:32 +00001427 }
1428
1429 public boolean is(QUERY query, Instruction instr, Analyzer analyzer) throws Exception {
1430 switch (query) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001431 case ANY :
1432 return true;
Stuart McCullochbb014372012-06-07 21:57:32 +00001433
Stuart McCulloch2286f232012-06-15 13:27:53 +00001434 case NAMED :
1435 if (instr.matches(getClassName().getDottedOnly()))
Stuart McCullochbb014372012-06-07 21:57:32 +00001436 return !instr.isNegated();
Stuart McCullochbb014372012-06-07 21:57:32 +00001437 return false;
1438
Stuart McCulloch2286f232012-06-15 13:27:53 +00001439 case VERSION :
1440 String v = major + "." + minor;
1441 if (instr.matches(v))
1442 return !instr.isNegated();
Stuart McCullochbb014372012-06-07 21:57:32 +00001443 return false;
1444
Stuart McCulloch2286f232012-06-15 13:27:53 +00001445 case IMPLEMENTS :
1446 for (int i = 0; interfaces != null && i < interfaces.length; i++) {
1447 if (instr.matches(interfaces[i].getDottedOnly()))
1448 return !instr.isNegated();
1449 }
1450 break;
1451
1452 case EXTENDS :
1453 if (zuper == null)
1454 return false;
1455
1456 if (instr.matches(zuper.getDottedOnly()))
Stuart McCullochbb014372012-06-07 21:57:32 +00001457 return !instr.isNegated();
Stuart McCulloch2286f232012-06-15 13:27:53 +00001458 break;
Stuart McCullochbb014372012-06-07 21:57:32 +00001459
Stuart McCulloch2286f232012-06-15 13:27:53 +00001460 case PUBLIC :
1461 return Modifier.isPublic(accessx);
Stuart McCullochbb014372012-06-07 21:57:32 +00001462
Stuart McCulloch2286f232012-06-15 13:27:53 +00001463 case CONCRETE :
1464 return !Modifier.isAbstract(accessx);
Stuart McCullochbb014372012-06-07 21:57:32 +00001465
Stuart McCulloch2286f232012-06-15 13:27:53 +00001466 case ANNOTATED :
1467 if (annotations == null)
1468 return false;
Stuart McCullochbb014372012-06-07 21:57:32 +00001469
Stuart McCulloch2286f232012-06-15 13:27:53 +00001470 for (TypeRef annotation : annotations) {
1471 if (instr.matches(annotation.getFQN()))
1472 return !instr.isNegated();
1473 }
1474
1475 return false;
1476
1477 case RUNTIMEANNOTATIONS :
1478 return hasClassAnnotations;
1479 case CLASSANNOTATIONS :
1480 return hasClassAnnotations;
1481
1482 case ABSTRACT :
1483 return Modifier.isAbstract(accessx);
1484
1485 case IMPORTS :
1486 for (PackageRef imp : imports) {
1487 if (instr.matches(imp.getFQN()))
1488 return !instr.isNegated();
1489 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001490 }
1491
1492 if (zuper == null)
1493 return false;
1494
1495 Clazz clazz = analyzer.findClass(zuper);
1496 if (clazz == null)
1497 return false;
1498
1499 return clazz.is(query, instr, analyzer);
1500 }
1501
1502 public String toString() {
1503 return className.getFQN();
1504 }
1505
1506 /**
1507 * Called when crawling the byte code and a method reference is found
Stuart McCullochbb014372012-06-07 21:57:32 +00001508 */
1509 void getMethodDef(int access, int methodRefPoolIndex) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001510 if (methodRefPoolIndex == 0)
Stuart McCullochbb014372012-06-07 21:57:32 +00001511 return;
Stuart McCulloch2286f232012-06-15 13:27:53 +00001512
Stuart McCullochbb014372012-06-07 21:57:32 +00001513 Object o = pool[methodRefPoolIndex];
1514 if (o != null && o instanceof Assoc) {
1515 Assoc assoc = (Assoc) o;
1516 if (assoc.tag == 10) {
1517 int string_index = intPool[assoc.a];
1518 TypeRef className = analyzer.getTypeRef((String) pool[string_index]);
1519 int name_and_type_index = assoc.b;
1520 Assoc name_and_type = (Assoc) pool[name_and_type_index];
1521 if (name_and_type.tag == 12) {
1522 // Name and Type
1523 int name_index = name_and_type.a;
1524 int type_index = name_and_type.b;
1525 String method = (String) pool[name_index];
1526 String descriptor = (String) pool[type_index];
1527 cd.referenceMethod(access, className, method, descriptor);
1528 } else
1529 throw new IllegalArgumentException(
1530 "Invalid class file (or parsing is wrong), assoc is not type + name (12)");
1531 } else
1532 throw new IllegalArgumentException(
1533 "Invalid class file (or parsing is wrong), Assoc is not method ref! (10)");
1534 } else
Stuart McCulloch2286f232012-06-15 13:27:53 +00001535 throw new IllegalArgumentException("Invalid class file (or parsing is wrong), Not an assoc at a method ref");
Stuart McCullochbb014372012-06-07 21:57:32 +00001536 }
1537
1538 public boolean isPublic() {
1539 return Modifier.isPublic(accessx);
1540 }
1541
1542 public boolean isProtected() {
1543 return Modifier.isProtected(accessx);
1544 }
1545
1546 public boolean isEnum() {
1547 return zuper != null && zuper.getBinary().equals("java/lang/Enum");
1548 }
1549
1550 public JAVA getFormat() {
1551 return JAVA.format(major);
1552
1553 }
1554
1555 public static String objectDescriptorToFQN(String string) {
1556 if (string.startsWith("L") && string.endsWith(";"))
1557 return string.substring(1, string.length() - 1).replace('/', '.');
1558
1559 switch (string.charAt(0)) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001560 case 'V' :
1561 return "void";
1562 case 'B' :
1563 return "byte";
1564 case 'C' :
1565 return "char";
1566 case 'I' :
1567 return "int";
1568 case 'S' :
1569 return "short";
1570 case 'D' :
1571 return "double";
1572 case 'F' :
1573 return "float";
1574 case 'J' :
1575 return "long";
1576 case 'Z' :
1577 return "boolean";
1578 case '[' : // Array
1579 return objectDescriptorToFQN(string.substring(1)) + "[]";
Stuart McCullochbb014372012-06-07 21:57:32 +00001580 }
1581 throw new IllegalArgumentException("Invalid type character in descriptor " + string);
1582 }
1583
1584 public static String unCamel(String id) {
1585 StringBuilder out = new StringBuilder();
1586 for (int i = 0; i < id.length(); i++) {
1587 char c = id.charAt(i);
1588 if (c == '_' || c == '$' || c == '.') {
1589 if (out.length() > 0 && !Character.isWhitespace(out.charAt(out.length() - 1)))
1590 out.append(' ');
1591 continue;
1592 }
1593
1594 int n = i;
1595 while (n < id.length() && Character.isUpperCase(id.charAt(n))) {
1596 n++;
1597 }
1598 if (n == i)
1599 out.append(id.charAt(i));
1600 else {
1601 boolean tolower = (n - i) == 1;
1602 if (i > 0 && !Character.isWhitespace(out.charAt(out.length() - 1)))
1603 out.append(' ');
1604
1605 for (; i < n;) {
1606 if (tolower)
1607 out.append(Character.toLowerCase(id.charAt(i)));
1608 else
1609 out.append(id.charAt(i));
1610 i++;
1611 }
1612 i--;
1613 }
1614 }
1615 if (id.startsWith("."))
1616 out.append(" *");
1617 out.replace(0, 1, Character.toUpperCase(out.charAt(0)) + "");
1618 return out.toString();
1619 }
1620
1621 public boolean isInterface() {
1622 return Modifier.isInterface(accessx);
1623 }
1624
1625 public boolean isAbstract() {
1626 return Modifier.isAbstract(accessx);
1627 }
1628
1629 public int getAccess() {
1630 if (innerAccess == -1)
1631 return accessx;
Stuart McCullochd4826102012-06-26 16:34:24 +00001632 return innerAccess;
Stuart McCullochbb014372012-06-07 21:57:32 +00001633 }
1634
1635 public TypeRef getClassName() {
1636 return className;
1637 }
1638
1639 /**
1640 * To provide an enclosing instance
1641 *
1642 * @param access
1643 * @param name
1644 * @param descriptor
1645 * @return
1646 */
1647 public MethodDef getMethodDef(int access, String name, String descriptor) {
1648 return new MethodDef(access, name, descriptor);
1649 }
1650
1651 public TypeRef getSuper() {
1652 return zuper;
1653 }
1654
1655 public String getFQN() {
1656 return className.getFQN();
1657 }
1658
1659 public TypeRef[] getInterfaces() {
1660 return interfaces;
1661 }
1662
1663 public void setInnerAccess(int access) {
1664 innerAccess = access;
1665 }
1666
1667 public boolean isFinal() {
1668 return Modifier.isFinal(accessx);
1669 }
1670
1671 public void setDeprecated(boolean b) {
1672 deprecated = b;
1673 }
1674
1675 public boolean isDeprecated() {
1676 return deprecated;
1677 }
1678
1679 public boolean isAnnotation() {
1680 return (accessx & ACC_ANNOTATION) != 0;
1681 }
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001682
1683 public Set<PackageRef> getAPIUses() {
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001684 if (api == null)
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001685 return Collections.emptySet();
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001686 return api;
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001687 }
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001688
1689 public Clazz.TypeDef getExtends(TypeRef type) {
1690 return new TypeDef(type, false);
1691 }
1692
1693 public Clazz.TypeDef getImplements(TypeRef type) {
1694 return new TypeDef(type, true);
1695 }
1696
Stuart McCullochbb014372012-06-07 21:57:32 +00001697}