View Javadoc

1   // CallTargets.java, created Fri Jan 11 16:49:00 2002 by joewhaley
2   // Copyright (C) 2001-3 John Whaley <jwhaley@alum.mit.edu>
3   // Licensed under the terms of the GNU LGPL; see COPYING for details.
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   * @author  John Whaley <jwhaley@alum.mit.edu>
27   * @version $Id: CallTargets.java 1931 2004-09-22 22:17:47Z joewhaley $
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              // temporary hack, until we rewrite the code below to take into account
50              // non-exact sets.
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          // HACK: some broken class files have invokevirtual on interface methods.
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              // fast search using vtable
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                      //jq.Assert(!rclass.isLoaded() || !rclass.isAbstract());
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; // conservative.
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                                 // method doesn't exist in this class or any of its superclasses.
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             // slow search.
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; // conservative.
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                             // method doesn't exist in this class or any of its superclasses.
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             // errr... rclass is a supertype of the method's class!
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                     // method doesn't exist in this class or any of its superclasses.
221                     return NoCallTarget.INSTANCE;
222                 }
223             }
224         }
225 
226         // TEMPORARY HACK: sometimes casts from interface types are
227         // lost in the type analysis, leading to virtual calls on interfaces.
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         // TEMPORARY HACK: some class files are messed up and use
237         // invokevirtual instead of invokeinterface.
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                 // fast search.
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; // conservative.
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                             // method doesn't exist in this class or any of its superclasses.
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; // conservative.
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; // conservative.
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; // conservative.
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                 // not the true receiver type, or we don't know anything about the receiver type because
332                 // it isn't loaded, so we fall back to the case where we don't consider the receiver
333                 // (calls to java.lang.Object methods on interfaces should have been caught above
334                 //  because they are virtual calls, and java.lang.Object is always prepared)
335                 return getTargets(callingClass, method, type, loadClasses);
336             }
337         }
338         
339         // slow search, interface or virtual call.  instance method is not prepared.
340         for (;;) {
341             if (loadClasses) rclass.load();
342             if (!rclass.isLoaded()) {
343                 complete = false; // conservative.
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             // assert commented out- imethod might have the wrong 
354             //jq.Assert(rclass != imethod.getDeclaringClass());
355             rclass = rclass.getSuperclass();
356             if (rclass == null) {
357                 // method doesn't exist in this class or any of its superclasses.
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; // conservative.
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; // conservative.
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; // conservative.
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         // find the set of equivalent interfaces
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; // must repeat to catch any interfaces that implement this one
447                     // add subtree from here.
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         // find the set of classes that implement these interfaces.
457         Stack worklist = new Stack();     // unchecked classes
458         Stack implementers = new Stack(); // do/may implement
459         jq_Class rclass = PrimordialClassLoader.getJavaLangObject();
460         if (VerifyAssertions)
461             Assert._assert(rclass.isLoaded()); // java.lang.Object had better be loaded!
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(); // use a HashSet because it is going to be large
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         // static method. the declaring class might not have been loaded.
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         // special, non-virtual invocation.
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;  // no idea!
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 }