blob: 1a325e6b01b9856133c80a63177e2bd5db4bd2fc [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
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000254 @Override
Stuart McCullochbb014372012-06-07 21:57:32 +0000255 public String getName() {
256 return name;
257 }
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000258
Stuart McCullochbb014372012-06-07 21:57:32 +0000259
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000260 @Override
Stuart McCullochbb014372012-06-07 21:57:32 +0000261 public TypeRef getType() {
262 return descriptor.getType();
263 }
264
265 public TypeRef getContainingClass() {
266 return getClassName();
267 }
268
269 public Descriptor getDescriptor() {
270 return descriptor;
271 }
272
273 public void setConstant(Object o) {
274 this.constant = o;
275 }
276
277 public Object getConstant() {
278 return this.constant;
279 }
280
281 // TODO change to use proper generics
282 public String getGenericReturnType() {
283 String use = descriptor.toString();
284 if (signature != null)
285 use = signature;
286
287 Matcher m = METHOD_DESCRIPTOR.matcher(use);
288 if (!m.matches())
289 throw new IllegalArgumentException("Not a valid method descriptor: " + descriptor);
290
291 String returnType = m.group(2);
292 return objectDescriptorToFQN(returnType);
293 }
294
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000295 @Override
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000296 public TypeRef[] getPrototype() {
297 return null;
298 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000299 public String getSignature() {
300 return signature;
301 }
302
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000303 @Override
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000304 public String toString() {
305 return name;
306 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000307 }
308
309 public class MethodDef extends FieldDef {
310 public MethodDef(int access, String method, String descriptor) {
311 super(access, method, descriptor);
312 }
313
314 public boolean isConstructor() {
315 return name.equals("<init>") || name.equals("<clinit>");
316 }
317
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000318 @Override
Stuart McCullochbb014372012-06-07 21:57:32 +0000319 public TypeRef[] getPrototype() {
320 return descriptor.getPrototype();
321 }
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000322 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000323
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000324 public class TypeDef extends Def {
325 TypeRef type;
326 boolean interf;
327
328 public TypeDef(TypeRef type, boolean interf) {
329 super(Modifier.PUBLIC);
330 this.type = type;
331 this.interf = interf;
332 }
333
334 public TypeRef getReference() {
335 return type;
336 }
337
338 public boolean getImplements() {
339 return interf;
340 }
341
342
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000343 @Override
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000344 public String getName() {
345 if (interf)
346 return "<implements>";
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000347 return "<extends>";
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000348 }
349
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000350 @Override
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000351 public TypeRef getType() {
352 return type;
353 }
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +0000354 @Override
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000355 public TypeRef[] getPrototype() {
356 return null;
357 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000358 }
359
360 final static byte SkipTable[] = { //
Stuart McCulloch2286f232012-06-15 13:27:53 +0000361 0, // 0 non existent
Stuart McCullochbb014372012-06-07 21:57:32 +0000362 -1, // 1 CONSTANT_utf8 UTF 8, handled in
363 // method
364 -1, // 2
365 4, // 3 CONSTANT_Integer
366 4, // 4 CONSTANT_Float
367 8, // 5 CONSTANT_Long (index +=2!)
368 8, // 6 CONSTANT_Double (index +=2!)
369 -1, // 7 CONSTANT_Class
370 2, // 8 CONSTANT_String
371 4, // 9 CONSTANT_FieldRef
372 4, // 10 CONSTANT_MethodRef
373 4, // 11 CONSTANT_InterfaceMethodRef
374 4, // 12 CONSTANT_NameAndType
375 -1, // 13 Not defined
376 -1, // 14 Not defined
377 3, // 15 CONSTANT_MethodHandle
378 2, // 16 CONSTANT_MethodType
379 -1, // 17 Not defined
380 4, // 18 CONSTANT_InvokeDynamic
381 };
382
383 boolean hasRuntimeAnnotations;
384 boolean hasClassAnnotations;
385
386 TypeRef className;
387 Object pool[];
388 int intPool[];
389 Set<PackageRef> imports = Create.set();
390 String path;
391 int minor = 0;
392 int major = 0;
393 int innerAccess = -1;
394 int accessx = 0;
395 String sourceFile;
396 Set<TypeRef> xref;
Stuart McCullochbb014372012-06-07 21:57:32 +0000397 Set<TypeRef> annotations;
398 int forName = 0;
399 int class$ = 0;
400 TypeRef[] interfaces;
401 TypeRef zuper;
402 ClassDataCollector cd = null;
403 Resource resource;
404 FieldDef last = null;
405 boolean deprecated;
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000406 Set<PackageRef> api;
Stuart McCullochbb014372012-06-07 21:57:32 +0000407 final Analyzer analyzer;
408
409 public Clazz(Analyzer analyzer, String path, Resource resource) {
410 this.path = path;
411 this.resource = resource;
412 this.analyzer = analyzer;
413 }
414
415 public Set<TypeRef> parseClassFile() throws Exception {
416 return parseClassFileWithCollector(null);
417 }
418
419 public Set<TypeRef> parseClassFile(InputStream in) throws Exception {
420 return parseClassFile(in, null);
421 }
422
423 public Set<TypeRef> parseClassFileWithCollector(ClassDataCollector cd) throws Exception {
424 InputStream in = resource.openInputStream();
425 try {
426 return parseClassFile(in, cd);
Stuart McCulloch2286f232012-06-15 13:27:53 +0000427 }
428 finally {
Stuart McCullochbb014372012-06-07 21:57:32 +0000429 in.close();
430 }
431 }
432
433 public Set<TypeRef> parseClassFile(InputStream in, ClassDataCollector cd) throws Exception {
434 DataInputStream din = new DataInputStream(in);
435 try {
436 this.cd = cd;
437 return parseClassFile(din);
Stuart McCulloch2286f232012-06-15 13:27:53 +0000438 }
439 finally {
Stuart McCullochbb014372012-06-07 21:57:32 +0000440 cd = null;
441 din.close();
442 }
443 }
444
445 Set<TypeRef> parseClassFile(DataInputStream in) throws Exception {
446 xref = new HashSet<TypeRef>();
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000447
Stuart McCullochbb014372012-06-07 21:57:32 +0000448 boolean crawl = cd != null; // Crawl the byte code if we have a
449 // collector
450 int magic = in.readInt();
451 if (magic != 0xCAFEBABE)
452 throw new IOException("Not a valid class file (no CAFEBABE header)");
453
454 minor = in.readUnsignedShort(); // minor version
455 major = in.readUnsignedShort(); // major version
456 if (cd != null)
457 cd.version(minor, major);
458 int count = in.readUnsignedShort();
459 pool = new Object[count];
460 intPool = new int[count];
461
462 process: for (int poolIndex = 1; poolIndex < count; poolIndex++) {
463 byte tag = in.readByte();
464 switch (tag) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000465 case 0 :
466 break process;
467 case 1 :
468 constantUtf8(in, poolIndex);
469 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000470
Stuart McCulloch2286f232012-06-15 13:27:53 +0000471 case 3 :
472 constantInteger(in, poolIndex);
473 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000474
Stuart McCulloch2286f232012-06-15 13:27:53 +0000475 case 4 :
476 constantFloat(in, poolIndex);
477 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000478
Stuart McCulloch2286f232012-06-15 13:27:53 +0000479 // For some insane optimization reason are
480 // the long and the double two entries in the
481 // constant pool. See 4.4.5
482 case 5 :
483 constantLong(in, poolIndex);
484 poolIndex++;
485 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000486
Stuart McCulloch2286f232012-06-15 13:27:53 +0000487 case 6 :
488 constantDouble(in, poolIndex);
489 poolIndex++;
490 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000491
Stuart McCulloch2286f232012-06-15 13:27:53 +0000492 case 7 :
493 constantClass(in, poolIndex);
494 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000495
Stuart McCulloch2286f232012-06-15 13:27:53 +0000496 case 8 :
497 constantString(in, poolIndex);
498 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000499
Stuart McCulloch2286f232012-06-15 13:27:53 +0000500 case 10 : // Method ref
501 case 11 : // Interface Method ref
502 methodRef(in, poolIndex);
503 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000504
Stuart McCulloch2286f232012-06-15 13:27:53 +0000505 // Name and Type
506 case 12 :
507 nameAndType(in, poolIndex, tag);
508 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000509
Stuart McCulloch2286f232012-06-15 13:27:53 +0000510 // We get the skip count for each record type
511 // from the SkipTable. This will also automatically
512 // abort when
513 default :
514 if (tag == 2)
515 throw new IOException("Invalid tag " + tag);
516 in.skipBytes(SkipTable[tag]);
517 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000518 }
519 }
520
521 pool(pool, intPool);
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000522
523 // All name& type and class constant records contain descriptors we must
524 // treat
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000525 // as references, though not API
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000526
527 for (Object o : pool) {
528 if (o == null)
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000529 continue;
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000530
531 if (o instanceof Assoc && ((Assoc) o).tag == 12) {
532 referTo(((Assoc) o).b, 0); // Descriptor
533 } else if (o instanceof ClassConstant) {
534 String binaryClassName = (String) pool[((ClassConstant) o).cname];
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000535 TypeRef typeRef = analyzer.getTypeRef(binaryClassName);
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000536 referTo(typeRef, 0);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000537 }
538 }
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000539
Stuart McCullochbb014372012-06-07 21:57:32 +0000540 /*
541 * Parse after the constant pool, code thanks to Hans Christian
542 * Falkenberg
543 */
544
545 accessx = in.readUnsignedShort(); // access
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000546 if (Modifier.isPublic(accessx))
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000547 api = new HashSet<PackageRef>();
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000548
Stuart McCullochbb014372012-06-07 21:57:32 +0000549 int this_class = in.readUnsignedShort();
550 className = analyzer.getTypeRef((String) pool[intPool[this_class]]);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000551 referTo(className, Modifier.PUBLIC);
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000552
Stuart McCullochbb014372012-06-07 21:57:32 +0000553 try {
554
555 if (cd != null) {
556 if (!cd.classStart(accessx, className))
557 return null;
558 }
559
560 int super_class = in.readUnsignedShort();
561 String superName = (String) pool[intPool[super_class]];
562 if (superName != null) {
563 zuper = analyzer.getTypeRef(superName);
564 }
565
566 if (zuper != null) {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000567 referTo(zuper, accessx);
Stuart McCullochbb014372012-06-07 21:57:32 +0000568 if (cd != null)
569 cd.extendsClass(zuper);
570 }
571
572 int interfacesCount = in.readUnsignedShort();
573 if (interfacesCount > 0) {
574 interfaces = new TypeRef[interfacesCount];
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000575 for (int i = 0; i < interfacesCount; i++) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000576 interfaces[i] = analyzer.getTypeRef((String) pool[intPool[in.readUnsignedShort()]]);
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000577 referTo(interfaces[i], accessx);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000578 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000579 if (cd != null)
580 cd.implementsInterfaces(interfaces);
581 }
582
583 int fieldsCount = in.readUnsignedShort();
584 for (int i = 0; i < fieldsCount; i++) {
585 int access_flags = in.readUnsignedShort(); // skip access flags
586 int name_index = in.readUnsignedShort();
587 int descriptor_index = in.readUnsignedShort();
588
589 // Java prior to 1.5 used a weird
590 // static variable to hold the com.X.class
591 // result construct. If it did not find it
592 // it would create a variable class$com$X
593 // that would be used to hold the class
594 // object gotten with Class.forName ...
595 // Stupidly, they did not actively use the
596 // class name for the field type, so bnd
597 // would not see a reference. We detect
598 // this case and add an artificial descriptor
599 String name = pool[name_index].toString(); // name_index
600 if (name.startsWith("class$")) {
601 crawl = true;
602 }
603 if (cd != null)
Stuart McCulloch2286f232012-06-15 13:27:53 +0000604 cd.field(last = new FieldDef(access_flags, name, pool[descriptor_index].toString()));
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000605
606 referTo(descriptor_index, access_flags);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000607 doAttributes(in, ElementType.FIELD, false, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000608 }
609
610 //
611 // Check if we have to crawl the code to find
612 // the ldc(_w) <string constant> invokestatic Class.forName
613 // if so, calculate the method ref index so we
614 // can do this efficiently
615 //
616 if (crawl) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000617 forName = findMethodReference("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
618 class$ = findMethodReference(className.getBinary(), "class$", "(Ljava/lang/String;)Ljava/lang/Class;");
619 } else if (major == 48) {
620 forName = findMethodReference("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
Stuart McCullochbb014372012-06-07 21:57:32 +0000621 if (forName > 0) {
622 crawl = true;
623 class$ = findMethodReference(className.getBinary(), "class$",
624 "(Ljava/lang/String;)Ljava/lang/Class;");
625 }
626 }
Stuart McCulloch2286f232012-06-15 13:27:53 +0000627
Stuart McCullochbb014372012-06-07 21:57:32 +0000628 // There are some serious changes in the
629 // class file format. So we do not do any crawling
630 // it has also become less important
Stuart McCulloch2286f232012-06-15 13:27:53 +0000631 if (major >= JAVA.OpenJDK7.major)
Stuart McCullochbb014372012-06-07 21:57:32 +0000632 crawl = false;
633
634 //
635 // Handle the methods
636 //
637 int methodCount = in.readUnsignedShort();
638 for (int i = 0; i < methodCount; i++) {
639 int access_flags = in.readUnsignedShort();
640 int name_index = in.readUnsignedShort();
641 int descriptor_index = in.readUnsignedShort();
Stuart McCullochbb014372012-06-07 21:57:32 +0000642 String name = pool[name_index].toString();
643 String descriptor = pool[descriptor_index].toString();
644 if (cd != null) {
645 MethodDef mdef = new MethodDef(access_flags, name, descriptor);
646 last = mdef;
647 cd.method(mdef);
648 }
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000649 referTo(descriptor_index, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000650
651 if ("<init>".equals(name)) {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000652 doAttributes(in, ElementType.CONSTRUCTOR, crawl, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000653 } else {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000654 doAttributes(in, ElementType.METHOD, crawl, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000655 }
656 }
657 if (cd != null)
658 cd.memberEnd();
659
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000660 doAttributes(in, ElementType.TYPE, false, accessx);
Stuart McCullochbb014372012-06-07 21:57:32 +0000661
662 //
663 // Parse all the descriptors we found
664 //
665
Stuart McCullochbb014372012-06-07 21:57:32 +0000666 Set<TypeRef> xref = this.xref;
667 reset();
668 return xref;
Stuart McCulloch2286f232012-06-15 13:27:53 +0000669 }
670 finally {
Stuart McCullochbb014372012-06-07 21:57:32 +0000671 if (cd != null)
672 cd.classEnd();
673 }
674 }
675
676 private void constantFloat(DataInputStream in, int poolIndex) throws IOException {
677 if (cd != null)
678 pool[poolIndex] = in.readFloat(); // ALU
679 else
680 in.skipBytes(4);
681 }
682
683 private void constantInteger(DataInputStream in, int poolIndex) throws IOException {
684 intPool[poolIndex] = in.readInt();
685 if (cd != null)
686 pool[poolIndex] = intPool[poolIndex];
687 }
688
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000689 protected void pool(@SuppressWarnings("unused")
690 Object[] pool, @SuppressWarnings("unused")
691 int[] intPool) {}
Stuart McCullochbb014372012-06-07 21:57:32 +0000692
693 /**
694 * @param in
695 * @param poolIndex
696 * @param tag
697 * @throws IOException
698 */
699 protected void nameAndType(DataInputStream in, int poolIndex, byte tag) throws IOException {
700 int name_index = in.readUnsignedShort();
701 int descriptor_index = in.readUnsignedShort();
Stuart McCullochbb014372012-06-07 21:57:32 +0000702 pool[poolIndex] = new Assoc(tag, name_index, descriptor_index);
703 }
704
705 /**
706 * @param in
707 * @param poolIndex
708 * @param tag
709 * @throws IOException
710 */
711 private void methodRef(DataInputStream in, int poolIndex) throws IOException {
712 int class_index = in.readUnsignedShort();
713 int name_and_type_index = in.readUnsignedShort();
714 pool[poolIndex] = new Assoc((byte) 10, class_index, name_and_type_index);
715 }
716
717 /**
718 * @param in
719 * @param poolIndex
720 * @throws IOException
721 */
722 private void constantString(DataInputStream in, int poolIndex) throws IOException {
723 int string_index = in.readUnsignedShort();
724 intPool[poolIndex] = string_index;
725 }
726
727 /**
728 * @param in
729 * @param poolIndex
730 * @throws IOException
731 */
732 protected void constantClass(DataInputStream in, int poolIndex) throws IOException {
733 int class_index = in.readUnsignedShort();
Stuart McCullochbb014372012-06-07 21:57:32 +0000734 intPool[poolIndex] = class_index;
735 ClassConstant c = new ClassConstant(class_index);
736 pool[poolIndex] = c;
737 }
738
739 /**
740 * @param in
741 * @throws IOException
742 */
743 protected void constantDouble(DataInputStream in, int poolIndex) throws IOException {
744 if (cd != null)
745 pool[poolIndex] = in.readDouble();
746 else
747 in.skipBytes(8);
748 }
749
750 /**
751 * @param in
752 * @throws IOException
753 */
754 protected void constantLong(DataInputStream in, int poolIndex) throws IOException {
755 if (cd != null) {
756 pool[poolIndex] = in.readLong();
757 } else
758 in.skipBytes(8);
759 }
760
761 /**
762 * @param in
763 * @param poolIndex
764 * @throws IOException
765 */
766 protected void constantUtf8(DataInputStream in, int poolIndex) throws IOException {
767 // CONSTANT_Utf8
768
769 String name = in.readUTF();
770 pool[poolIndex] = name;
771 }
772
773 /**
774 * Find a method reference in the pool that points to the given class,
775 * methodname and descriptor.
776 *
777 * @param clazz
778 * @param methodname
779 * @param descriptor
780 * @return index in constant pool
781 */
782 private int findMethodReference(String clazz, String methodname, String descriptor) {
783 for (int i = 1; i < pool.length; i++) {
784 if (pool[i] instanceof Assoc) {
785 Assoc methodref = (Assoc) pool[i];
786 if (methodref.tag == 10) {
787 // Method ref
788 int class_index = methodref.a;
789 int class_name_index = intPool[class_index];
790 if (clazz.equals(pool[class_name_index])) {
791 int name_and_type_index = methodref.b;
792 Assoc name_and_type = (Assoc) pool[name_and_type_index];
793 if (name_and_type.tag == 12) {
794 // Name and Type
795 int name_index = name_and_type.a;
796 int type_index = name_and_type.b;
797 if (methodname.equals(pool[name_index])) {
798 if (descriptor.equals(pool[type_index])) {
799 return i;
800 }
801 }
802 }
803 }
804 }
805 }
806 }
807 return -1;
808 }
809
810 /**
811 * Called for each attribute in the class, field, or method.
812 *
813 * @param in
814 * The stream
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000815 * @param access_flags
Stuart McCullochbb014372012-06-07 21:57:32 +0000816 * @throws Exception
817 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000818 private void doAttributes(DataInputStream in, ElementType member, boolean crawl, int access_flags) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +0000819 int attributesCount = in.readUnsignedShort();
820 for (int j = 0; j < attributesCount; j++) {
821 // skip name CONSTANT_Utf8 pointer
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000822 doAttribute(in, member, crawl, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000823 }
824 }
825
826 /**
827 * Process a single attribute, if not recognized, skip it.
828 *
829 * @param in
830 * the data stream
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000831 * @param access_flags
Stuart McCullochbb014372012-06-07 21:57:32 +0000832 * @throws Exception
833 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000834 private void doAttribute(DataInputStream in, ElementType member, boolean crawl, int access_flags) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +0000835 int attribute_name_index = in.readUnsignedShort();
836 String attributeName = (String) pool[attribute_name_index];
837 long attribute_length = in.readInt();
838 attribute_length &= 0xFFFFFFFF;
839 if ("Deprecated".equals(attributeName)) {
840 if (cd != null)
841 cd.deprecated();
842 } else if ("RuntimeVisibleAnnotations".equals(attributeName))
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +0000843 doAnnotations(in, member, RetentionPolicy.RUNTIME, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000844 else if ("RuntimeVisibleParameterAnnotations".equals(attributeName))
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +0000845 doParameterAnnotations(in, member, RetentionPolicy.RUNTIME, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000846 else if ("RuntimeInvisibleAnnotations".equals(attributeName))
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +0000847 doAnnotations(in, member, RetentionPolicy.CLASS, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000848 else if ("RuntimeInvisibleParameterAnnotations".equals(attributeName))
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +0000849 doParameterAnnotations(in, member, RetentionPolicy.CLASS, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000850 else if ("InnerClasses".equals(attributeName))
851 doInnerClasses(in);
852 else if ("EnclosingMethod".equals(attributeName))
853 doEnclosingMethod(in);
854 else if ("SourceFile".equals(attributeName))
855 doSourceFile(in);
856 else if ("Code".equals(attributeName) && crawl)
857 doCode(in);
858 else if ("Signature".equals(attributeName))
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000859 doSignature(in, member, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000860 else if ("ConstantValue".equals(attributeName))
861 doConstantValue(in);
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +0000862 else if ("Exceptions".equals(attributeName))
863 doExceptions(in, access_flags);
864 else {
Stuart McCullochbb014372012-06-07 21:57:32 +0000865 if (attribute_length > 0x7FFFFFFF) {
866 throw new IllegalArgumentException("Attribute > 2Gb");
867 }
868 in.skipBytes((int) attribute_length);
869 }
870 }
871
872 /**
873 * <pre>
874 * EnclosingMethod_attribute {
875 * u2 attribute_name_index;
876 * u4 attribute_length;
877 * u2 class_index
878 * u2 method_index;
879 * }
880 * </pre>
881 *
Stuart McCullochbb014372012-06-07 21:57:32 +0000882 * @param in
883 * @throws IOException
884 */
885 private void doEnclosingMethod(DataInputStream in) throws IOException {
886 int cIndex = in.readShort();
887 int mIndex = in.readShort();
888
889 if (cd != null) {
890 int nameIndex = intPool[cIndex];
891 TypeRef cName = analyzer.getTypeRef((String) pool[nameIndex]);
892
893 String mName = null;
894 String mDescriptor = null;
895
896 if (mIndex != 0) {
897 Assoc nameAndType = (Assoc) pool[mIndex];
898 mName = (String) pool[nameAndType.a];
899 mDescriptor = (String) pool[nameAndType.b];
900 }
901 cd.enclosingMethod(cName, mName, mDescriptor);
902 }
903 }
904
905 /**
906 * <pre>
907 * InnerClasses_attribute {
908 * u2 attribute_name_index;
909 * u4 attribute_length;
910 * u2 number_of_classes; {
911 * u2 inner_class_info_index;
912 * u2 outer_class_info_index;
913 * u2 inner_name_index;
914 * u2 inner_class_access_flags;
915 * } classes[number_of_classes];
916 * }
917 * </pre>
918 *
919 * @param in
920 * @throws Exception
921 */
922 private void doInnerClasses(DataInputStream in) throws Exception {
923 int number_of_classes = in.readShort();
924 for (int i = 0; i < number_of_classes; i++) {
925 int inner_class_info_index = in.readShort();
926 int outer_class_info_index = in.readShort();
927 int inner_name_index = in.readShort();
928 int inner_class_access_flags = in.readShort() & 0xFFFF;
929
930 if (cd != null) {
931 TypeRef innerClass = null;
932 TypeRef outerClass = null;
933 String innerName = null;
934
935 if (inner_class_info_index != 0) {
936 int nameIndex = intPool[inner_class_info_index];
937 innerClass = analyzer.getTypeRef((String) pool[nameIndex]);
938 }
939
940 if (outer_class_info_index != 0) {
941 int nameIndex = intPool[outer_class_info_index];
942 outerClass = analyzer.getTypeRef((String) pool[nameIndex]);
943 }
944
945 if (inner_name_index != 0)
946 innerName = (String) pool[inner_name_index];
947
948 cd.innerClass(innerClass, outerClass, innerName, inner_class_access_flags);
949 }
950 }
951 }
952
953 /**
954 * Handle a signature
955 *
956 * <pre>
957 * Signature_attribute {
958 * u2 attribute_name_index;
959 * u4 attribute_length;
960 * u2 signature_index;
961 * }
962 * </pre>
963 *
964 * @param member
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +0000965 * @param access_flags
Stuart McCullochbb014372012-06-07 21:57:32 +0000966 */
967
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000968 void doSignature(DataInputStream in, ElementType member, int access_flags) throws IOException {
Stuart McCullochbb014372012-06-07 21:57:32 +0000969 int signature_index = in.readUnsignedShort();
970 String signature = (String) pool[signature_index];
971
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +0000972 parseDescriptor(signature, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +0000973
974 if (last != null)
975 last.signature = signature;
976
977 if (cd != null)
978 cd.signature(signature);
979 }
980
981 /**
982 * Handle a constant value call the data collector with it
983 */
984 void doConstantValue(DataInputStream in) throws IOException {
985 int constantValue_index = in.readUnsignedShort();
986 if (cd == null)
987 return;
988
989 Object object = pool[constantValue_index];
990 if (object == null)
991 object = pool[intPool[constantValue_index]];
992
993 last.constant = object;
994 cd.constant(object);
995 }
996
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +0000997 void doExceptions(DataInputStream in, int access_flags) throws IOException {
998 int exception_count = in.readUnsignedShort();
999 for (int i = 0; i < exception_count; i++) {
1000 int index = in.readUnsignedShort();
1001 if (api != null && (Modifier.isPublic(access_flags) || Modifier.isProtected(access_flags))) {
1002 ClassConstant cc = (ClassConstant) pool[index];
1003 String descr = (String) pool[cc.cname];
1004
1005 TypeRef clazz = analyzer.getTypeRef(descr);
1006 referTo(clazz, access_flags);
1007 }
1008 }
1009 }
1010
Stuart McCullochbb014372012-06-07 21:57:32 +00001011 /**
1012 * <pre>
1013 * Code_attribute {
1014 * u2 attribute_name_index;
1015 * u4 attribute_length;
1016 * u2 max_stack;
1017 * u2 max_locals;
1018 * u4 code_length;
1019 * u1 code[code_length];
1020 * u2 exception_table_length;
1021 * { u2 start_pc;
1022 * u2 end_pc;
1023 * u2 handler_pc;
1024 * u2 catch_type;
1025 * } exception_table[exception_table_length];
1026 * u2 attributes_count;
1027 * attribute_info attributes[attributes_count];
1028 * }
1029 * </pre>
1030 *
1031 * @param in
1032 * @param pool
1033 * @throws Exception
1034 */
1035 private void doCode(DataInputStream in) throws Exception {
1036 /* int max_stack = */in.readUnsignedShort();
1037 /* int max_locals = */in.readUnsignedShort();
1038 int code_length = in.readInt();
1039 byte code[] = new byte[code_length];
1040 in.readFully(code);
1041 crawl(code);
1042 int exception_table_length = in.readUnsignedShort();
1043 in.skipBytes(exception_table_length * 8);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001044 doAttributes(in, ElementType.METHOD, false, 0);
Stuart McCullochbb014372012-06-07 21:57:32 +00001045 }
1046
1047 /**
1048 * We must find Class.forName references ...
1049 *
1050 * @param code
1051 */
1052 protected void crawl(byte[] code) {
1053 ByteBuffer bb = ByteBuffer.wrap(code);
1054 bb.order(ByteOrder.BIG_ENDIAN);
1055 int lastReference = -1;
1056
1057 while (bb.remaining() > 0) {
1058 int instruction = 0xFF & bb.get();
1059 switch (instruction) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001060 case OpCodes.ldc :
1061 lastReference = 0xFF & bb.get();
1062 break;
Stuart McCullochbb014372012-06-07 21:57:32 +00001063
Stuart McCulloch2286f232012-06-15 13:27:53 +00001064 case OpCodes.ldc_w :
1065 lastReference = 0xFFFF & bb.getShort();
1066 break;
Stuart McCullochbb014372012-06-07 21:57:32 +00001067
Stuart McCulloch2286f232012-06-15 13:27:53 +00001068 case OpCodes.invokespecial : {
1069 int mref = 0xFFFF & bb.getShort();
1070 if (cd != null)
1071 getMethodDef(0, mref);
1072 break;
1073 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001074
Stuart McCulloch2286f232012-06-15 13:27:53 +00001075 case OpCodes.invokevirtual : {
1076 int mref = 0xFFFF & bb.getShort();
1077 if (cd != null)
1078 getMethodDef(0, mref);
1079 break;
1080 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001081
Stuart McCulloch2286f232012-06-15 13:27:53 +00001082 case OpCodes.invokeinterface : {
1083 int mref = 0xFFFF & bb.getShort();
1084 if (cd != null)
1085 getMethodDef(0, mref);
1086 break;
1087 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001088
Stuart McCulloch2286f232012-06-15 13:27:53 +00001089 case OpCodes.invokestatic : {
1090 int methodref = 0xFFFF & bb.getShort();
1091 if (cd != null)
1092 getMethodDef(0, methodref);
Stuart McCullochbb014372012-06-07 21:57:32 +00001093
Stuart McCulloch2286f232012-06-15 13:27:53 +00001094 if ((methodref == forName || methodref == class$) && lastReference != -1
1095 && pool[intPool[lastReference]] instanceof String) {
1096 String fqn = (String) pool[intPool[lastReference]];
1097 if (!fqn.equals("class") && fqn.indexOf('.') > 0) {
1098 TypeRef clazz = analyzer.getTypeRefFromFQN(fqn);
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001099 referTo(clazz, 0);
Stuart McCulloch2286f232012-06-15 13:27:53 +00001100 }
1101 lastReference = -1;
1102 }
1103 break;
1104 }
1105
1106 case OpCodes.tableswitch :
1107 // Skip to place divisible by 4
1108 while ((bb.position() & 0x3) != 0)
1109 bb.get();
1110 /* int deflt = */
1111 bb.getInt();
1112 int low = bb.getInt();
1113 int high = bb.getInt();
1114 try {
1115 bb.position(bb.position() + (high - low + 1) * 4);
1116 }
1117 catch (Exception e) {
1118 // TODO Auto-generated catch block
1119 e.printStackTrace();
Stuart McCullochbb014372012-06-07 21:57:32 +00001120 }
1121 lastReference = -1;
Stuart McCulloch2286f232012-06-15 13:27:53 +00001122 break;
Stuart McCullochbb014372012-06-07 21:57:32 +00001123
Stuart McCulloch2286f232012-06-15 13:27:53 +00001124 case OpCodes.lookupswitch :
1125 // Skip to place divisible by 4
1126 while ((bb.position() & 0x3) != 0)
1127 bb.get();
1128 /* deflt = */
1129 bb.getInt();
1130 int npairs = bb.getInt();
1131 bb.position(bb.position() + npairs * 8);
1132 lastReference = -1;
1133 break;
Stuart McCullochbb014372012-06-07 21:57:32 +00001134
Stuart McCulloch2286f232012-06-15 13:27:53 +00001135 default :
1136 lastReference = -1;
1137 bb.position(bb.position() + OpCodes.OFFSETS[instruction]);
Stuart McCullochbb014372012-06-07 21:57:32 +00001138 }
1139 }
1140 }
1141
1142 private void doSourceFile(DataInputStream in) throws IOException {
1143 int sourcefile_index = in.readUnsignedShort();
1144 this.sourceFile = pool[sourcefile_index].toString();
1145 }
1146
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001147 private void doParameterAnnotations(DataInputStream in, ElementType member, RetentionPolicy policy, int access_flags)
Stuart McCulloch2286f232012-06-15 13:27:53 +00001148 throws IOException {
Stuart McCullochbb014372012-06-07 21:57:32 +00001149 int num_parameters = in.readUnsignedByte();
1150 for (int p = 0; p < num_parameters; p++) {
1151 if (cd != null)
1152 cd.parameter(p);
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001153 doAnnotations(in, member, policy, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +00001154 }
1155 }
1156
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001157 private void doAnnotations(DataInputStream in, ElementType member, RetentionPolicy policy, int access_flags) throws IOException {
Stuart McCullochbb014372012-06-07 21:57:32 +00001158 int num_annotations = in.readUnsignedShort(); // # of annotations
1159 for (int a = 0; a < num_annotations; a++) {
1160 if (cd == null)
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001161 doAnnotation(in, member, policy, false, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +00001162 else {
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001163 Annotation annotion = doAnnotation(in, member, policy, true, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +00001164 cd.annotation(annotion);
1165 }
1166 }
1167 }
1168
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001169 private Annotation doAnnotation(DataInputStream in, ElementType member, RetentionPolicy policy, boolean collect, int access_flags)
Stuart McCulloch2286f232012-06-15 13:27:53 +00001170 throws IOException {
Stuart McCullochbb014372012-06-07 21:57:32 +00001171 int type_index = in.readUnsignedShort();
1172 if (annotations == null)
1173 annotations = new HashSet<TypeRef>();
1174
1175 TypeRef tr = analyzer.getTypeRef(pool[type_index].toString());
1176 annotations.add(tr);
1177
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001178 TypeRef name = analyzer.getTypeRef((String) pool[type_index]);
Stuart McCullochbb014372012-06-07 21:57:32 +00001179 if (policy == RetentionPolicy.RUNTIME) {
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001180 referTo(type_index, 0);
Stuart McCullochbb014372012-06-07 21:57:32 +00001181 hasRuntimeAnnotations = true;
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001182 if (api != null && (Modifier.isPublic(access_flags) || Modifier.isProtected(access_flags)))
1183 api.add(name.getPackageRef());
Stuart McCullochbb014372012-06-07 21:57:32 +00001184 } else {
1185 hasClassAnnotations = true;
1186 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001187 int num_element_value_pairs = in.readUnsignedShort();
Stuart McCulloch2286f232012-06-15 13:27:53 +00001188 Map<String,Object> elements = null;
Stuart McCullochbb014372012-06-07 21:57:32 +00001189 for (int v = 0; v < num_element_value_pairs; v++) {
1190 int element_name_index = in.readUnsignedShort();
1191 String element = (String) pool[element_name_index];
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001192 Object value = doElementValue(in, member, policy, collect, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +00001193 if (collect) {
1194 if (elements == null)
Stuart McCulloch2286f232012-06-15 13:27:53 +00001195 elements = new LinkedHashMap<String,Object>();
Stuart McCullochbb014372012-06-07 21:57:32 +00001196 elements.put(element, value);
1197 }
1198 }
1199 if (collect)
1200 return new Annotation(name, elements, member, policy);
Stuart McCullochd4826102012-06-26 16:34:24 +00001201 return null;
Stuart McCullochbb014372012-06-07 21:57:32 +00001202 }
1203
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001204 private Object doElementValue(DataInputStream in, ElementType member, RetentionPolicy policy, boolean collect, int access_flags)
Stuart McCulloch2286f232012-06-15 13:27:53 +00001205 throws IOException {
Stuart McCullochbb014372012-06-07 21:57:32 +00001206 char tag = (char) in.readUnsignedByte();
1207 switch (tag) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001208 case 'B' : // Byte
1209 case 'C' : // Character
1210 case 'I' : // Integer
1211 case 'S' : // Short
1212 int const_value_index = in.readUnsignedShort();
1213 return intPool[const_value_index];
Stuart McCullochbb014372012-06-07 21:57:32 +00001214
Stuart McCulloch2286f232012-06-15 13:27:53 +00001215 case 'D' : // Double
1216 case 'F' : // Float
1217 case 's' : // String
1218 case 'J' : // Long
1219 const_value_index = in.readUnsignedShort();
1220 return pool[const_value_index];
Stuart McCullochbb014372012-06-07 21:57:32 +00001221
Stuart McCulloch2286f232012-06-15 13:27:53 +00001222 case 'Z' : // Boolean
1223 const_value_index = in.readUnsignedShort();
1224 return pool[const_value_index] == null || pool[const_value_index].equals(0) ? false : true;
Stuart McCullochbb014372012-06-07 21:57:32 +00001225
Stuart McCulloch2286f232012-06-15 13:27:53 +00001226 case 'e' : // enum constant
1227 int type_name_index = in.readUnsignedShort();
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001228 if (policy == RetentionPolicy.RUNTIME) {
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001229 referTo(type_name_index, 0);
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001230 if (api != null && (Modifier.isPublic(access_flags) || Modifier.isProtected(access_flags))) {
1231 TypeRef name = analyzer.getTypeRef((String) pool[type_name_index]);
1232 api.add(name.getPackageRef());
1233 }
1234 }
Stuart McCulloch2286f232012-06-15 13:27:53 +00001235 int const_name_index = in.readUnsignedShort();
1236 return pool[const_name_index];
Stuart McCullochbb014372012-06-07 21:57:32 +00001237
Stuart McCulloch2286f232012-06-15 13:27:53 +00001238 case 'c' : // Class
1239 int class_info_index = in.readUnsignedShort();
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001240 if (policy == RetentionPolicy.RUNTIME) {
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001241 referTo(class_info_index, 0);
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001242 if (api != null && (Modifier.isPublic(access_flags) || Modifier.isProtected(access_flags))) {
1243 TypeRef name = analyzer.getTypeRef((String) pool[class_info_index]);
1244 api.add(name.getPackageRef());
1245 }
1246 }
Stuart McCulloch2286f232012-06-15 13:27:53 +00001247 return pool[class_info_index];
Stuart McCullochbb014372012-06-07 21:57:32 +00001248
Stuart McCulloch2286f232012-06-15 13:27:53 +00001249 case '@' : // Annotation type
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001250 return doAnnotation(in, member, policy, collect, access_flags);
Stuart McCullochbb014372012-06-07 21:57:32 +00001251
Stuart McCulloch2286f232012-06-15 13:27:53 +00001252 case '[' : // Array
1253 int num_values = in.readUnsignedShort();
1254 Object[] result = new Object[num_values];
1255 for (int i = 0; i < num_values; i++) {
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001256 result[i] = doElementValue(in, member, policy, collect, access_flags);
Stuart McCulloch2286f232012-06-15 13:27:53 +00001257 }
1258 return result;
Stuart McCullochbb014372012-06-07 21:57:32 +00001259
Stuart McCulloch2286f232012-06-15 13:27:53 +00001260 default :
1261 throw new IllegalArgumentException("Invalid value for Annotation ElementValue tag " + tag);
Stuart McCullochbb014372012-06-07 21:57:32 +00001262 }
1263 }
1264
1265 /**
1266 * Add a new package reference.
1267 *
1268 * @param packageRef
1269 * A '.' delimited package name
1270 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001271 void referTo(TypeRef typeRef, int modifiers) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001272 if (xref != null)
1273 xref.add(typeRef);
1274 if (typeRef.isPrimitive())
1275 return;
1276
1277 PackageRef packageRef = typeRef.getPackageRef();
1278 if (packageRef.isPrimitivePackage())
1279 return;
1280
1281 imports.add(packageRef);
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001282
1283 if (api != null && (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)))
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001284 api.add(packageRef);
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001285
1286 if (cd != null)
1287 cd.referTo(typeRef, modifiers);
1288
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001289 }
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001290
1291 void referTo(int index, int modifiers) {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001292 String descriptor = (String) pool[index];
1293 parseDescriptor(descriptor, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001294 }
1295
1296 /**
1297 * This method parses a descriptor and adds the package of the descriptor to
Stuart McCulloch2286f232012-06-15 13:27:53 +00001298 * the referenced packages. The syntax of the descriptor is:
Stuart McCullochbb014372012-06-07 21:57:32 +00001299 *
1300 * <pre>
1301 * descriptor ::= ( '(' reference * ')' )? reference
1302 * reference ::= 'L' classname ( '&lt;' references '&gt;' )? ';' | 'B' | 'Z' | ... | '+' | '-' | '['
1303 * </pre>
1304 *
1305 * This methods uses heavy recursion to parse the descriptor and a roving
1306 * pointer to limit the creation of string objects.
1307 *
1308 * @param descriptor
1309 * The to be parsed descriptor
1310 * @param rover
1311 * The pointer to start at
1312 */
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001313
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001314 public void parseDescriptor(String descriptor, int modifiers) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001315 // Some descriptors are weird, they start with a generic
1316 // declaration that contains ':', not sure what they mean ...
1317 int rover = 0;
1318 if (descriptor.charAt(0) == '<') {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001319 rover = parseFormalTypeParameters(descriptor, rover, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001320 }
1321
1322 if (descriptor.charAt(rover) == '(') {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001323 rover = parseReferences(descriptor, rover + 1, ')', modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001324 rover++;
1325 }
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001326 parseReferences(descriptor, rover, (char) 0, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001327 }
1328
1329 /**
1330 * Parse a sequence of references. A sequence ends with a given character or
1331 * when the string ends.
1332 *
1333 * @param descriptor
1334 * The whole descriptor.
1335 * @param rover
1336 * The index in the descriptor
1337 * @param delimiter
1338 * The end character or 0
1339 * @return the last index processed, one character after the delimeter
1340 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001341 int parseReferences(String descriptor, int rover, char delimiter, int modifiers) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001342 int r = rover;
1343 while (r < descriptor.length() && descriptor.charAt(r) != delimiter) {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001344 r = parseReference(descriptor, r, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001345 }
1346 return r;
1347 }
1348
1349 /**
1350 * Parse a single reference. This can be a single character or an object
1351 * reference when it starts with 'L'.
1352 *
1353 * @param descriptor
1354 * The descriptor
1355 * @param rover
1356 * The place to start
1357 * @return The return index after the reference
1358 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001359 int parseReference(String descriptor, int rover, int modifiers) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001360 int r = rover;
1361 char c = descriptor.charAt(r);
1362 while (c == '[')
1363 c = descriptor.charAt(++r);
1364
1365 if (c == '<') {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001366 r = parseReferences(descriptor, r + 1, '>', modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001367 } else if (c == 'T') {
1368 // Type variable name
1369 r++;
1370 while (descriptor.charAt(r) != ';')
1371 r++;
1372 } else if (c == 'L') {
1373 StringBuilder sb = new StringBuilder();
1374 r++;
1375 while ((c = descriptor.charAt(r)) != ';') {
1376 if (c == '<') {
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001377 r = parseReferences(descriptor, r + 1, '>', modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001378 } else
1379 sb.append(c);
1380 r++;
1381 }
1382 TypeRef ref = analyzer.getTypeRef(sb.toString());
1383 if (cd != null)
1384 cd.addReference(ref);
1385
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001386 referTo(ref, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001387 } else {
1388 if ("+-*BCDFIJSZV".indexOf(c) < 0)
1389 ;// System.err.println("Should not skip: " + c);
1390 }
1391
1392 // this skips a lot of characters
1393 // [, *, +, -, B, etc.
1394
1395 return r + 1;
1396 }
1397
1398 /**
1399 * FormalTypeParameters
1400 *
1401 * @param descriptor
1402 * @param index
1403 * @return
1404 */
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001405 private int parseFormalTypeParameters(String descriptor, int index, int modifiers) {
Stuart McCullochbb014372012-06-07 21:57:32 +00001406 index++;
1407 while (descriptor.charAt(index) != '>') {
1408 // Skip IDENTIFIER
1409 index = descriptor.indexOf(':', index) + 1;
1410 if (index == 0)
1411 throw new IllegalArgumentException("Expected IDENTIFIER: " + descriptor);
1412
1413 // ClassBound? InterfaceBounds
1414
1415 char c = descriptor.charAt(index);
1416
1417 // Class Bound?
1418 if (c == 'L' || c == 'T') {
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001419 index = parseReference(descriptor, index, modifiers); // class
1420 // reference
Stuart McCullochbb014372012-06-07 21:57:32 +00001421 c = descriptor.charAt(index);
1422 }
1423
1424 // Interface Bounds
1425 while (c == ':') {
1426 index++;
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001427 index = parseReference(descriptor, index, modifiers);
Stuart McCullochbb014372012-06-07 21:57:32 +00001428 c = descriptor.charAt(index);
1429 } // for each interface
1430
1431 } // for each formal parameter
1432 return index + 1; // skip >
1433 }
1434
1435 public Set<PackageRef> getReferred() {
1436 return imports;
1437 }
1438
1439 public String getAbsolutePath() {
1440 return path;
1441 }
1442
1443 public String getSourceFile() {
1444 return sourceFile;
1445 }
1446
1447 /**
Stuart McCulloch2286f232012-06-15 13:27:53 +00001448 * .class construct for different compilers sun 1.1 Detect static variable
1449 * class$com$acme$MyClass 1.2 " 1.3 " 1.4 " 1.5 ldc_w (class) 1.6 " eclipse
1450 * 1.1 class$0, ldc (string), invokestatic Class.forName 1.2 " 1.3 " 1.5 ldc
1451 * (class) 1.6 " 1.5 and later is not an issue, sun pre 1.5 is easy to
1452 * detect the static variable that decodes the class name. For eclipse, the
1453 * class$0 gives away we have a reference encoded in a string.
Stuart McCullochbb014372012-06-07 21:57:32 +00001454 * compilerversions/compilerversions.jar contains test versions of all
1455 * versions/compilers.
1456 */
1457
1458 public void reset() {
1459 pool = null;
1460 intPool = null;
1461 xref = null;
Stuart McCullochbb014372012-06-07 21:57:32 +00001462 }
1463
1464 public boolean is(QUERY query, Instruction instr, Analyzer analyzer) throws Exception {
1465 switch (query) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001466 case ANY :
1467 return true;
Stuart McCullochbb014372012-06-07 21:57:32 +00001468
Stuart McCulloch2286f232012-06-15 13:27:53 +00001469 case NAMED :
1470 if (instr.matches(getClassName().getDottedOnly()))
Stuart McCullochbb014372012-06-07 21:57:32 +00001471 return !instr.isNegated();
Stuart McCullochbb014372012-06-07 21:57:32 +00001472 return false;
1473
Stuart McCulloch2286f232012-06-15 13:27:53 +00001474 case VERSION :
1475 String v = major + "." + minor;
1476 if (instr.matches(v))
1477 return !instr.isNegated();
Stuart McCullochbb014372012-06-07 21:57:32 +00001478 return false;
1479
Stuart McCulloch2286f232012-06-15 13:27:53 +00001480 case IMPLEMENTS :
1481 for (int i = 0; interfaces != null && i < interfaces.length; i++) {
1482 if (instr.matches(interfaces[i].getDottedOnly()))
1483 return !instr.isNegated();
1484 }
1485 break;
1486
1487 case EXTENDS :
1488 if (zuper == null)
1489 return false;
1490
1491 if (instr.matches(zuper.getDottedOnly()))
Stuart McCullochbb014372012-06-07 21:57:32 +00001492 return !instr.isNegated();
Stuart McCulloch2286f232012-06-15 13:27:53 +00001493 break;
Stuart McCullochbb014372012-06-07 21:57:32 +00001494
Stuart McCulloch2286f232012-06-15 13:27:53 +00001495 case PUBLIC :
1496 return Modifier.isPublic(accessx);
Stuart McCullochbb014372012-06-07 21:57:32 +00001497
Stuart McCulloch2286f232012-06-15 13:27:53 +00001498 case CONCRETE :
1499 return !Modifier.isAbstract(accessx);
Stuart McCullochbb014372012-06-07 21:57:32 +00001500
Stuart McCulloch2286f232012-06-15 13:27:53 +00001501 case ANNOTATED :
1502 if (annotations == null)
1503 return false;
Stuart McCullochbb014372012-06-07 21:57:32 +00001504
Stuart McCulloch2286f232012-06-15 13:27:53 +00001505 for (TypeRef annotation : annotations) {
1506 if (instr.matches(annotation.getFQN()))
1507 return !instr.isNegated();
1508 }
1509
1510 return false;
1511
1512 case RUNTIMEANNOTATIONS :
Stuart McCulloch4b9de8e2012-07-22 00:19:13 +00001513 return hasRuntimeAnnotations;
Stuart McCulloch2286f232012-06-15 13:27:53 +00001514 case CLASSANNOTATIONS :
1515 return hasClassAnnotations;
1516
1517 case ABSTRACT :
1518 return Modifier.isAbstract(accessx);
1519
1520 case IMPORTS :
1521 for (PackageRef imp : imports) {
1522 if (instr.matches(imp.getFQN()))
1523 return !instr.isNegated();
1524 }
Stuart McCullochbb014372012-06-07 21:57:32 +00001525 }
1526
1527 if (zuper == null)
1528 return false;
1529
1530 Clazz clazz = analyzer.findClass(zuper);
1531 if (clazz == null)
1532 return false;
1533
1534 return clazz.is(query, instr, analyzer);
1535 }
1536
Stuart McCulloch55d4dfe2012-08-07 10:57:21 +00001537 @Override
Stuart McCullochbb014372012-06-07 21:57:32 +00001538 public String toString() {
1539 return className.getFQN();
1540 }
1541
1542 /**
1543 * Called when crawling the byte code and a method reference is found
Stuart McCullochbb014372012-06-07 21:57:32 +00001544 */
1545 void getMethodDef(int access, int methodRefPoolIndex) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001546 if (methodRefPoolIndex == 0)
Stuart McCullochbb014372012-06-07 21:57:32 +00001547 return;
Stuart McCulloch2286f232012-06-15 13:27:53 +00001548
Stuart McCullochbb014372012-06-07 21:57:32 +00001549 Object o = pool[methodRefPoolIndex];
1550 if (o != null && o instanceof Assoc) {
1551 Assoc assoc = (Assoc) o;
1552 if (assoc.tag == 10) {
1553 int string_index = intPool[assoc.a];
1554 TypeRef className = analyzer.getTypeRef((String) pool[string_index]);
1555 int name_and_type_index = assoc.b;
1556 Assoc name_and_type = (Assoc) pool[name_and_type_index];
1557 if (name_and_type.tag == 12) {
1558 // Name and Type
1559 int name_index = name_and_type.a;
1560 int type_index = name_and_type.b;
1561 String method = (String) pool[name_index];
1562 String descriptor = (String) pool[type_index];
1563 cd.referenceMethod(access, className, method, descriptor);
1564 } else
1565 throw new IllegalArgumentException(
1566 "Invalid class file (or parsing is wrong), assoc is not type + name (12)");
1567 } else
1568 throw new IllegalArgumentException(
1569 "Invalid class file (or parsing is wrong), Assoc is not method ref! (10)");
1570 } else
Stuart McCulloch2286f232012-06-15 13:27:53 +00001571 throw new IllegalArgumentException("Invalid class file (or parsing is wrong), Not an assoc at a method ref");
Stuart McCullochbb014372012-06-07 21:57:32 +00001572 }
1573
1574 public boolean isPublic() {
1575 return Modifier.isPublic(accessx);
1576 }
1577
1578 public boolean isProtected() {
1579 return Modifier.isProtected(accessx);
1580 }
1581
1582 public boolean isEnum() {
1583 return zuper != null && zuper.getBinary().equals("java/lang/Enum");
1584 }
1585
1586 public JAVA getFormat() {
1587 return JAVA.format(major);
1588
1589 }
1590
1591 public static String objectDescriptorToFQN(String string) {
1592 if (string.startsWith("L") && string.endsWith(";"))
1593 return string.substring(1, string.length() - 1).replace('/', '.');
1594
1595 switch (string.charAt(0)) {
Stuart McCulloch2286f232012-06-15 13:27:53 +00001596 case 'V' :
1597 return "void";
1598 case 'B' :
1599 return "byte";
1600 case 'C' :
1601 return "char";
1602 case 'I' :
1603 return "int";
1604 case 'S' :
1605 return "short";
1606 case 'D' :
1607 return "double";
1608 case 'F' :
1609 return "float";
1610 case 'J' :
1611 return "long";
1612 case 'Z' :
1613 return "boolean";
1614 case '[' : // Array
1615 return objectDescriptorToFQN(string.substring(1)) + "[]";
Stuart McCullochbb014372012-06-07 21:57:32 +00001616 }
1617 throw new IllegalArgumentException("Invalid type character in descriptor " + string);
1618 }
1619
1620 public static String unCamel(String id) {
1621 StringBuilder out = new StringBuilder();
1622 for (int i = 0; i < id.length(); i++) {
1623 char c = id.charAt(i);
1624 if (c == '_' || c == '$' || c == '.') {
1625 if (out.length() > 0 && !Character.isWhitespace(out.charAt(out.length() - 1)))
1626 out.append(' ');
1627 continue;
1628 }
1629
1630 int n = i;
1631 while (n < id.length() && Character.isUpperCase(id.charAt(n))) {
1632 n++;
1633 }
1634 if (n == i)
1635 out.append(id.charAt(i));
1636 else {
1637 boolean tolower = (n - i) == 1;
1638 if (i > 0 && !Character.isWhitespace(out.charAt(out.length() - 1)))
1639 out.append(' ');
1640
1641 for (; i < n;) {
1642 if (tolower)
1643 out.append(Character.toLowerCase(id.charAt(i)));
1644 else
1645 out.append(id.charAt(i));
1646 i++;
1647 }
1648 i--;
1649 }
1650 }
1651 if (id.startsWith("."))
1652 out.append(" *");
1653 out.replace(0, 1, Character.toUpperCase(out.charAt(0)) + "");
1654 return out.toString();
1655 }
1656
1657 public boolean isInterface() {
1658 return Modifier.isInterface(accessx);
1659 }
1660
1661 public boolean isAbstract() {
1662 return Modifier.isAbstract(accessx);
1663 }
1664
1665 public int getAccess() {
1666 if (innerAccess == -1)
1667 return accessx;
Stuart McCullochd4826102012-06-26 16:34:24 +00001668 return innerAccess;
Stuart McCullochbb014372012-06-07 21:57:32 +00001669 }
1670
1671 public TypeRef getClassName() {
1672 return className;
1673 }
1674
1675 /**
1676 * To provide an enclosing instance
1677 *
1678 * @param access
1679 * @param name
1680 * @param descriptor
1681 * @return
1682 */
1683 public MethodDef getMethodDef(int access, String name, String descriptor) {
1684 return new MethodDef(access, name, descriptor);
1685 }
1686
1687 public TypeRef getSuper() {
1688 return zuper;
1689 }
1690
1691 public String getFQN() {
1692 return className.getFQN();
1693 }
1694
1695 public TypeRef[] getInterfaces() {
1696 return interfaces;
1697 }
1698
1699 public void setInnerAccess(int access) {
1700 innerAccess = access;
1701 }
1702
1703 public boolean isFinal() {
1704 return Modifier.isFinal(accessx);
1705 }
1706
1707 public void setDeprecated(boolean b) {
1708 deprecated = b;
1709 }
1710
1711 public boolean isDeprecated() {
1712 return deprecated;
1713 }
1714
1715 public boolean isAnnotation() {
1716 return (accessx & ACC_ANNOTATION) != 0;
1717 }
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001718
1719 public Set<PackageRef> getAPIUses() {
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001720 if (api == null)
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001721 return Collections.emptySet();
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001722 return api;
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +00001723 }
Stuart McCulloch3b7e6af2012-07-16 14:10:57 +00001724
1725 public Clazz.TypeDef getExtends(TypeRef type) {
1726 return new TypeDef(type, false);
1727 }
1728
1729 public Clazz.TypeDef getImplements(TypeRef type) {
1730 return new TypeDef(type, true);
1731 }
1732
Stuart McCullochbb014372012-06-07 21:57:32 +00001733}