1
2
3
4 package joeq.Compiler.BytecodeAnalysis;
5
6 import java.util.AbstractSet;
7 import java.util.Collections;
8 import java.util.Iterator;
9 import java.util.LinkedHashSet;
10 import java.util.Set;
11 import java.util.Stack;
12 import joeq.Class.PrimordialClassLoader;
13 import joeq.Class.jq_Class;
14 import joeq.Class.jq_InstanceMethod;
15 import joeq.Class.jq_Method;
16 import joeq.Class.jq_Reference;
17 import joeq.Class.jq_StaticMethod;
18 import joeq.Compiler.CompilationConstants;
19 import joeq.Compiler.CompilationState;
20 import joeq.Runtime.TypeCheck;
21 import jwutil.collections.HashCodeComparator;
22 import jwutil.collections.SortedArraySet;
23 import jwutil.util.Assert;
24
25
26
27
28
29 public abstract class CallTargets extends AbstractSet implements CompilationConstants {
30
31 public static final boolean TRACE = false;
32 public static final boolean VerifyAssertions = false;
33
34 public static CallTargets getTargets(jq_Class callingClass,
35 jq_Method method,
36 byte type,
37 Set possibleReceiverTypes,
38 boolean exact,
39 boolean loadClasses)
40 {
41 if (type == BytecodeVisitor.INVOKE_STATIC) return getStaticTargets((jq_StaticMethod)method);
42 jq_InstanceMethod imethod = (jq_InstanceMethod)method;
43 if (type == BytecodeVisitor.INVOKE_SPECIAL) return getSpecialTargets(callingClass, imethod, loadClasses);
44 if (VerifyAssertions)
45 Assert._assert(type == BytecodeVisitor.INVOKE_VIRTUAL || type == BytecodeVisitor.INVOKE_INTERFACE, ""+type);
46
47 if (TRACE) System.out.println("Getting call targets of "+method+" type "+type+" receiver types "+possibleReceiverTypes+" exact="+exact);
48 if (!exact) {
49
50
51 Set c = new LinkedHashSet();
52 Iterator i = possibleReceiverTypes.iterator();
53 boolean complete = true;
54 while (i.hasNext()) {
55 CallTargets ct = getTargets(callingClass, method, type, (jq_Reference)i.next(), false, loadClasses);
56 c.addAll(ct);
57 if (!ct.isComplete()) complete = false;
58 }
59 return new MultipleCallTargets(c, complete);
60 }
61
62 Set c = SortedArraySet.FACTORY.makeSet(HashCodeComparator.INSTANCE);
63 boolean complete = true;
64
65 if (type == BytecodeVisitor.INVOKE_VIRTUAL &&
66 imethod.getDeclaringClass().isLoaded() &&
67 imethod.getDeclaringClass().isInterface()) {
68 type = BytecodeVisitor.INVOKE_INTERFACE;
69 }
70
71 if ((type == BytecodeVisitor.INVOKE_VIRTUAL) && imethod.getDeclaringClass().isPrepared()) {
72
73 if (VerifyAssertions)
74 Assert._assert(!imethod.getDeclaringClass().isInterface(), imethod.toString());
75 int offset = imethod.getOffset() >>> 2;
76 if (TRACE) System.out.println("Fast search using vtable offset "+offset+" for method "+imethod);
77 if (VerifyAssertions)
78 Assert._assert(offset >= 1);
79 Iterator i = possibleReceiverTypes.iterator();
80 while (i.hasNext()) {
81 jq_Reference rtype = (jq_Reference)i.next();
82 if (rtype.isClassType()) {
83 jq_Class rclass = (jq_Class)rtype;
84
85 if (!rclass.isPrepared()) {
86 jq_InstanceMethod target;
87 for (;;) {
88 if (TRACE) System.out.println("visiting "+rclass);
89 if (loadClasses) rclass.load();
90 if (!rclass.isLoaded()) {
91 if (TRACE) System.out.println(rclass+" isn't loaded: conservative.");
92 complete = false;
93 break;
94 }
95 jq_Method mtarget = (jq_Method)rclass.getDeclaredMember(imethod.getNameAndDesc());
96 if (mtarget instanceof jq_StaticMethod) break;
97 target = (jq_InstanceMethod)mtarget;
98 if (target != null) {
99 if (VerifyAssertions)
100 Assert._assert(imethod.getNameAndDesc().equals(target.getNameAndDesc()), imethod+" != "+target);
101 if (!target.isAbstract())
102 c.add(target);
103 break;
104 }
105 rclass = rclass.getSuperclass();
106 if (rclass == null) {
107
108 break;
109 }
110 }
111 continue;
112 }
113 if (offset > rclass.getVirtualMethods().length)
114 continue;
115 jq_InstanceMethod target = rclass.getVirtualMethods()[offset-1];
116 if (!imethod.getNameAndDesc().equals(target.getNameAndDesc()))
117 continue;
118 if (VerifyAssertions)
119 Assert._assert(!target.isAbstract());
120 c.add(target);
121 } else {
122 if (VerifyAssertions)
123 Assert._assert(rtype.isArrayType(), rtype.toString());
124 if (imethod.getDeclaringClass() != PrimordialClassLoader.getJavaLangObject())
125 continue;
126 if (VerifyAssertions)
127 Assert._assert(!imethod.isAbstract(), imethod.toString());
128 c.add(imethod);
129 }
130 }
131 } else {
132
133 Iterator i = possibleReceiverTypes.iterator();
134 while (i.hasNext()) {
135 jq_Reference rtype = (jq_Reference)i.next();
136 if (rtype.isClassType()) {
137 jq_Class rclass = (jq_Class)rtype;
138 jq_InstanceMethod target;
139 for (;;) {
140 if (TRACE) System.out.println("visiting "+rclass);
141 if (loadClasses) rclass.load();
142 if (!rclass.isLoaded()) {
143 complete = false;
144 break;
145 }
146 jq_Method mtarget = (jq_Method)rclass.getDeclaredMember(imethod.getNameAndDesc());
147 if (mtarget instanceof jq_StaticMethod) break;
148 target = (jq_InstanceMethod)mtarget;
149 if (target != null) {
150 if (VerifyAssertions)
151 Assert._assert(imethod.getNameAndDesc().equals(target.getNameAndDesc()), imethod+" != "+target);
152 if (!target.isAbstract())
153 c.add(target);
154 break;
155 }
156 rclass = rclass.getSuperclass();
157 if (rclass == null) {
158
159 break;
160 }
161 }
162 continue;
163 } else {
164 if (VerifyAssertions)
165 Assert._assert(rtype.isArrayType(), rtype.toString());
166 if (imethod.getDeclaringClass() != PrimordialClassLoader.getJavaLangObject())
167 continue;
168 if (VerifyAssertions)
169 Assert._assert(!imethod.isAbstract());
170 c.add(imethod);
171 }
172 }
173 }
174 if (TRACE) System.out.println("final result: "+c+" complete: "+complete);
175 return new MultipleCallTargets(c, complete);
176 }
177
178 public static CallTargets getTargets(jq_Class callingClass,
179 jq_Method method,
180 byte type,
181 jq_Reference receiverType,
182 boolean exact,
183 boolean loadClasses)
184 {
185 if (type == BytecodeVisitor.INVOKE_STATIC) return getStaticTargets((jq_StaticMethod)method);
186 jq_InstanceMethod imethod = (jq_InstanceMethod)method;
187 if (type == BytecodeVisitor.INVOKE_SPECIAL) return getSpecialTargets(callingClass, imethod, loadClasses);
188 if (VerifyAssertions)
189 Assert._assert(type == BytecodeVisitor.INVOKE_VIRTUAL || type == BytecodeVisitor.INVOKE_INTERFACE, ""+type);
190
191 if (receiverType.isArrayType()) {
192 if (imethod.getDeclaringClass() != PrimordialClassLoader.getJavaLangObject())
193 return NoCallTarget.INSTANCE;
194 return new SingleCallTarget(imethod, true);
195 }
196
197 jq_Class rclass = (jq_Class)receiverType;
198
199 if (TRACE) System.out.println("Class "+rclass+" has "+rclass.getSubClasses().length+" subclasses");
200
201 if (TypeCheck.isSuperclassOf(rclass, imethod.getDeclaringClass(), loadClasses) == YES) {
202
203 receiverType = rclass = imethod.getDeclaringClass();
204 }
205
206 if (exact) {
207 if (loadClasses) rclass.load();
208 if (!rclass.isLoaded()) return NoCallTarget.INSTANCE;
209 for (;;) {
210 jq_Method mtarget = (jq_Method)rclass.getDeclaredMember(imethod.getNameAndDesc());
211 if (mtarget instanceof jq_StaticMethod) return NoCallTarget.INSTANCE;
212 jq_InstanceMethod target = (jq_InstanceMethod)mtarget;
213 if (target != null) return new SingleCallTarget(target, true);
214 if (VerifyAssertions)
215 Assert._assert(rclass != imethod.getDeclaringClass(), rclass+" != "+imethod.getDeclaringClass());
216 if (loadClasses) rclass.load();
217 if (!rclass.isLoaded()) return NoCallTarget.INSTANCE;
218 rclass = rclass.getSuperclass();
219 if (rclass == null) {
220
221 return NoCallTarget.INSTANCE;
222 }
223 }
224 }
225
226
227
228 if (loadClasses) {
229 rclass.load();
230 if (rclass.isInterface() && type == BytecodeVisitor.INVOKE_VIRTUAL) {
231 if (!imethod.getDeclaringClass().isInterface())
232 receiverType = rclass = imethod.getDeclaringClass();
233 }
234 }
235
236
237
238 if (type == BytecodeVisitor.INVOKE_VIRTUAL &&
239 imethod.getDeclaringClass().isLoaded() &&
240 imethod.getDeclaringClass().isInterface()) {
241 type = BytecodeVisitor.INVOKE_INTERFACE;
242 }
243
244 Set c = SortedArraySet.FACTORY.makeSet(HashCodeComparator.INSTANCE);
245 boolean complete = true;
246 if (type == BytecodeVisitor.INVOKE_VIRTUAL) {
247 if (imethod.getDeclaringClass().isPrepared()) {
248
249 if (VerifyAssertions)
250 Assert._assert(!imethod.getDeclaringClass().isInterface());
251 int offset = imethod.getOffset() >>> 2;
252 if (VerifyAssertions)
253 Assert._assert(offset >= 1);
254 if (!rclass.isPrepared()) {
255 for (;;) {
256 if (loadClasses) rclass.load();
257 if (!rclass.isLoaded()) {
258 complete = false;
259 break;
260 }
261 jq_Method target = (jq_Method)rclass.getDeclaredMember(imethod.getNameAndDesc());
262 if (target != null) {
263 if (!target.isStatic() && !target.isAbstract())
264 c.add(target);
265 break;
266 }
267 if (VerifyAssertions)
268 Assert._assert(rclass != imethod.getDeclaringClass());
269 rclass = rclass.getSuperclass();
270 if (rclass == null) {
271
272 break;
273 }
274 }
275 }
276 jq_InstanceMethod target;
277 Stack subclass = new Stack();
278 subclass.push(receiverType);
279 while (!subclass.empty()) {
280 rclass = (jq_Class)subclass.pop();
281 if (loadClasses) rclass.load();
282 if (!rclass.isLoaded()) {
283 complete = false;
284 continue;
285 }
286 if (TRACE) System.out.println("Class "+rclass+" has "+rclass.getSubClasses().length+" subclasses");
287 if (!rclass.isPrepared()) {
288 jq_Method mtarget = (jq_Method)rclass.getDeclaredMember(imethod.getNameAndDesc());
289 if (mtarget instanceof jq_StaticMethod) continue;
290 target = (jq_InstanceMethod)mtarget;
291 if (TRACE) System.out.println("Class "+rclass+" target: "+target);
292 if ((target != null) && !target.isAbstract()) c.add(target);
293 } else {
294 if (offset > rclass.getVirtualMethods().length)
295 continue;
296 target = rclass.getVirtualMethods()[offset-1];
297 if (TRACE) System.out.println("Class "+rclass+" target: "+target);
298 if (!imethod.getNameAndDesc().equals(target.getNameAndDesc()))
299 continue;
300 if (!target.isAbstract()) {
301 if (TRACE) System.out.println("Target added to result: "+target);
302 c.add(target);
303 }
304 }
305 if (target != null) {
306 if (!target.isFinal() && !target.isPrivate()) {
307 if (!rclass.isFinal()) {
308 complete = false;
309 }
310 jq_Class[] subclasses = rclass.getSubClasses();
311 for (int i=0; i<subclasses.length; ++i) {
312 subclass.push(subclasses[i]);
313 }
314 }
315 } else {
316 if (!rclass.isFinal()) {
317 complete = false;
318 }
319 jq_Class[] subclasses = rclass.getSubClasses();
320 for (int i=0; i<subclasses.length; ++i) subclass.push(subclasses[i]);
321 }
322 }
323 if (TRACE) System.out.println("final result: "+c+" complete: "+complete);
324 return new MultipleCallTargets(c, complete);
325 }
326 }
327
328 if (type == BytecodeVisitor.INVOKE_INTERFACE) {
329 if (loadClasses) rclass.load();
330 if (!rclass.isLoaded() || ((jq_Class)rclass).isInterface()) {
331
332
333
334
335 return getTargets(callingClass, method, type, loadClasses);
336 }
337 }
338
339
340 for (;;) {
341 if (loadClasses) rclass.load();
342 if (!rclass.isLoaded()) {
343 complete = false;
344 break;
345 }
346 jq_Method mtarget = (jq_Method)rclass.getDeclaredMember(imethod.getNameAndDesc());
347 if (mtarget instanceof jq_StaticMethod) break;
348 jq_InstanceMethod target = (jq_InstanceMethod)mtarget;
349 if (target != null) {
350 if (!target.isAbstract()) c.add(target);
351 break;
352 }
353
354
355 rclass = rclass.getSuperclass();
356 if (rclass == null) {
357
358 break;
359 }
360 }
361 Stack subclass = new Stack();
362 subclass.push(receiverType);
363 while (!subclass.empty()) {
364 rclass = (jq_Class)subclass.pop();
365 if (loadClasses) rclass.load();
366 if (TRACE) System.out.println("Class "+rclass+" has "+rclass.getSubClasses().length+" subclasses");
367 if (!rclass.isLoaded()) {
368 complete = false;
369 continue;
370 }
371 jq_Method mtarget = (jq_Method)rclass.getDeclaredMember(imethod.getNameAndDesc());
372 if (mtarget instanceof jq_StaticMethod) continue;
373 jq_InstanceMethod target = (jq_InstanceMethod)mtarget;
374 if (target != null) {
375 if (TRACE) System.out.println("Class "+rclass+" target: "+target);
376 if (!target.isAbstract()) {
377 c.add(target);
378 if (TRACE) System.out.println("target added to result: "+target);
379 }
380 if (!target.isFinal() && !target.isPrivate()) {
381 if (!rclass.isFinal()) {
382 complete = false;
383 }
384 jq_Class[] subclasses = rclass.getSubClasses();
385 for (int i=0; i<subclasses.length; ++i) subclass.push(subclasses[i]);
386 }
387 } else {
388 if (!rclass.isFinal()) {
389 complete = false;
390 }
391 jq_Class[] subclasses = rclass.getSubClasses();
392 for (int i=0; i<subclasses.length; ++i) subclass.push(subclasses[i]);
393 }
394 }
395 if (TRACE) System.out.println("final result: "+c+" complete: "+complete);
396 return new MultipleCallTargets(c, complete);
397 }
398
399 public static void addAllSubclasses(jq_Class cl, Set s, boolean loadClasses) {
400 Stack worklist = new Stack();
401 for (;;) {
402 s.add(cl);
403 if (loadClasses) cl.load();
404 if (cl.isLoaded()) {
405 jq_Class[] subclasses = cl.getSubClasses();
406 for (int i=0; i<subclasses.length; ++i) worklist.push(subclasses[i]);
407 }
408 if (worklist.empty()) break;
409 cl = (jq_Class)worklist.pop();
410 }
411 }
412
413 public static CallTargets getTargets(jq_Class callingClass,
414 jq_Method method,
415 byte type,
416 boolean loadClasses)
417 {
418 if (type == BytecodeVisitor.INVOKE_STATIC) return getStaticTargets((jq_StaticMethod)method);
419 jq_InstanceMethod imethod = (jq_InstanceMethod)method;
420 if (type == BytecodeVisitor.INVOKE_SPECIAL) return getSpecialTargets(callingClass, imethod, loadClasses);
421 if (type == BytecodeVisitor.INVOKE_VIRTUAL)
422 return getTargets(callingClass, imethod, type, imethod.getDeclaringClass(), false, loadClasses);
423 if (VerifyAssertions)
424 Assert._assert(type == BytecodeVisitor.INVOKE_INTERFACE);
425 if (VerifyAssertions)
426 Assert._assert(!imethod.getDeclaringClass().isLoaded() ||
427 imethod.getDeclaringClass().isInterface());
428
429
430 jq_Class interf = method.getDeclaringClass();
431 Set interfaces = SortedArraySet.FACTORY.makeSet(HashCodeComparator.INSTANCE);
432 interfaces.add(interf);
433 addAllSubclasses(interf, interfaces, loadClasses);
434 boolean again;
435 do {
436 again = false;
437 jq_Class rclass = PrimordialClassLoader.getJavaLangObject();
438 Stack worklist = new Stack();
439 worklist.push(rclass);
440 while (!worklist.empty()) {
441 rclass = (jq_Class)worklist.pop();
442 if (loadClasses) rclass.load();
443 if (!rclass.isLoaded()) continue;
444 if (!rclass.isInterface()) continue;
445 if (CompilationState.DEFAULT.declaresInterface(rclass, interfaces) != NO) {
446 again = true;
447
448 addAllSubclasses(rclass, interfaces, loadClasses);
449 } else {
450 jq_Class[] subclasses = rclass.getSubClasses();
451 for (int i=0; i<subclasses.length; ++i) worklist.push(subclasses[i]);
452 }
453 }
454 } while (again);
455
456
457 Stack worklist = new Stack();
458 Stack implementers = new Stack();
459 jq_Class rclass = PrimordialClassLoader.getJavaLangObject();
460 if (VerifyAssertions)
461 Assert._assert(rclass.isLoaded());
462 if (rclass.implementsInterface(interf)) implementers.push(rclass);
463 else {
464 worklist.push(rclass);
465 while (!worklist.empty()) {
466 rclass = (jq_Class)worklist.pop();
467 if (loadClasses) rclass.load();
468 if (!rclass.isLoaded()) continue;
469 if (rclass.isInterface()) continue;
470 if (CompilationState.DEFAULT.declaresInterface(rclass, interfaces) != NO) {
471 implementers.push(rclass);
472 } else {
473 jq_Class[] subclasses = rclass.getSubClasses();
474 for (int i=0; i<subclasses.length; ++i) worklist.push(subclasses[i]);
475 }
476 }
477 }
478
479 LinkedHashSet c = new LinkedHashSet();
480 while (!implementers.empty()) {
481 rclass = (jq_Class)implementers.pop();
482 if (loadClasses) rclass.load();
483 if (!rclass.isLoaded()) continue;
484 if (VerifyAssertions)
485 Assert._assert(!rclass.isInterface());
486 jq_Method mtarget = (jq_Method)rclass.getDeclaredMember(imethod.getNameAndDesc());
487 if (mtarget instanceof jq_StaticMethod) continue;
488 jq_InstanceMethod target = (jq_InstanceMethod)mtarget;
489 if (target != null) {
490 if (!target.isAbstract()) c.add(target);
491 if (target.isFinal() || target.isPrivate()) continue;
492 }
493 jq_Class[] subclasses = rclass.getSubClasses();
494 for (int i=0; i<subclasses.length; ++i) implementers.push(subclasses[i]);
495 }
496 return new MultipleCallTargets(c, false);
497 }
498
499 public static SingleCallTarget getStaticTargets(jq_StaticMethod method)
500 {
501
502 return new SingleCallTarget(method, true);
503 }
504
505 public static CallTargets getSpecialTargets(jq_Class callingClass,
506 jq_InstanceMethod method,
507 boolean loadClasses)
508 {
509
510 if (!method.getDeclaringClass().isLoaded()) {
511 if (method.isInitializer()) return new SingleCallTarget(method, true);
512 if (callingClass.isLoaded() && !callingClass.isSpecial())
513 return new SingleCallTarget(method, true);
514 if (!loadClasses) return NoCallTarget.INSTANCE;
515 method.getDeclaringClass().load();
516 }
517 jq_InstanceMethod target = jq_Class.getInvokespecialTarget(callingClass, method);
518 return new SingleCallTarget(target, true);
519 }
520
521 public abstract Iterator iterator();
522 public abstract boolean isComplete();
523 public abstract CallTargets union(CallTargets s);
524 public abstract int size();
525
526 public static class NoCallTarget extends CallTargets
527 {
528 public Iterator iterator() { return Collections.EMPTY_SET.iterator(); }
529 public boolean isComplete() { return false; }
530 public CallTargets union(CallTargets s) { return s; }
531 public int size() { return 0; }
532 public static final NoCallTarget INSTANCE = new NoCallTarget();
533 private NoCallTarget() {}
534 public String toString() { return "{}"; }
535 }
536
537 public static class SingleCallTarget extends CallTargets
538 {
539 final jq_Method method; final boolean complete;
540 public SingleCallTarget(jq_Method m, boolean c) { method = m; complete = c; }
541 public Iterator iterator() { return Collections.singleton(method).iterator(); }
542 public boolean isComplete() { return complete; }
543 public CallTargets union(CallTargets s) {
544 if (s == NoCallTarget.INSTANCE) return this;
545 Set result = SortedArraySet.FACTORY.makeSet(HashCodeComparator.INSTANCE);
546 boolean is_complete = this.complete;
547 result.add(this.method);
548 if (!s.isComplete()) is_complete = false;
549 for (Iterator i = s.iterator(); i.hasNext(); ) {
550 result.add(i.next());
551 }
552 return new MultipleCallTargets(result, is_complete);
553 }
554 public int size() { return 1; }
555 public String toString() {
556 if (complete) return "{ "+method.toString()+" } (complete)";
557 else return "{ "+method.toString()+" }";
558 }
559 }
560
561 static class MultipleCallTargets extends CallTargets
562 {
563 final Set set; boolean complete;
564 MultipleCallTargets(Set s, boolean c) { set = s; complete = c; }
565 public Iterator iterator() { return set.iterator(); }
566 public boolean isComplete() { return complete; }
567 public CallTargets union(CallTargets s) {
568 if (s == NoCallTarget.INSTANCE) return this;
569 if (s instanceof SingleCallTarget) {
570 SingleCallTarget sct = (SingleCallTarget)s;
571 this.set.add(sct.method);
572 if (!sct.isComplete()) this.complete = false;
573 } else {
574 if (VerifyAssertions)
575 Assert._assert(s instanceof MultipleCallTargets);
576 this.set.addAll(((MultipleCallTargets)s).set);
577 }
578 return this;
579 }
580 public int size() { return set.size(); }
581 public String toString() {
582 if (complete) return set.toString()+" (complete)";
583 else return set.toString();
584 }
585 }
586
587 }