c0fa9b3f5bceaef18dbc2420c86e180ac5d497cb
[IRC.git] / Robust / src / Analysis / SSJava / SSJavaAnalysis.java
1 package Analysis.SSJava;
2
3 import java.io.BufferedWriter;
4 import java.io.FileWriter;
5 import java.io.IOException;
6 import java.util.ArrayList;
7 import java.util.Collections;
8 import java.util.Comparator;
9 import java.util.HashSet;
10 import java.util.Hashtable;
11 import java.util.Iterator;
12 import java.util.LinkedList;
13 import java.util.List;
14 import java.util.Set;
15 import java.util.StringTokenizer;
16 import java.util.Vector;
17
18 import Analysis.CallGraph.CallGraph;
19 import Analysis.Loops.GlobalFieldType;
20 import Analysis.Loops.LoopFinder;
21 import Analysis.Loops.LoopOptimize;
22 import Analysis.Loops.LoopTerminate;
23 import IR.AnnotationDescriptor;
24 import IR.ClassDescriptor;
25 import IR.Descriptor;
26 import IR.FieldDescriptor;
27 import IR.MethodDescriptor;
28 import IR.State;
29 import IR.SymbolTable;
30 import IR.TypeUtil;
31 import IR.Flat.BuildFlat;
32 import IR.Flat.FlatMethod;
33 import IR.Flat.FlatNode;
34 import Util.Pair;
35
36 public class SSJavaAnalysis {
37
38   public static final String SSJAVA = "SSJAVA";
39   public static final String LATTICE = "LATTICE";
40   public static final String METHODDEFAULT = "METHODDEFAULT";
41   public static final String THISLOC = "THISLOC";
42   public static final String GLOBALLOC = "GLOBALLOC";
43   public static final String RETURNLOC = "RETURNLOC";
44   public static final String LOC = "LOC";
45   public static final String DELTA = "DELTA";
46   public static final String TERMINATE = "TERMINATE";
47   public static final String DELEGATE = "DELEGATE";
48   public static final String DELEGATETHIS = "DELEGATETHIS";
49   public static final String TRUST = "TRUST";
50
51   public static final String TOP = "_top_";
52   public static final String BOTTOM = "_bottom_";
53
54   State state;
55   TypeUtil tu;
56   FlowDownCheck flowDownChecker;
57   MethodAnnotationCheck methodAnnotationChecker;
58   BuildFlat bf;
59
60   // set containing method requires to be annoated
61   Set<MethodDescriptor> annotationRequireSet;
62
63   // class -> field lattice
64   Hashtable<ClassDescriptor, SSJavaLattice<String>> cd2lattice;
65
66   // class -> default local variable lattice
67   Hashtable<ClassDescriptor, MethodLattice<String>> cd2methodDefault;
68
69   // method -> local variable lattice
70   Hashtable<MethodDescriptor, MethodLattice<String>> md2lattice;
71
72   // method set that does not want to have loop termination analysis
73   Hashtable<MethodDescriptor, Integer> skipLoopTerminate;
74
75   // map shared location to its descriptors
76   Hashtable<Location, Set<Descriptor>> mapSharedLocation2DescriptorSet;
77
78   // set containing a class that has at least one annoated method
79   Set<ClassDescriptor> annotationRequireClassSet;
80
81   // the set of method descriptor required to check the linear type property
82   Set<MethodDescriptor> linearTypeCheckMethodSet;
83
84   // the set of method descriptors annotated as "TRUST"
85   Set<MethodDescriptor> trustWorthyMDSet;
86
87   // points to method containing SSJAVA Loop
88   private MethodDescriptor methodContainingSSJavaLoop;
89
90   private FlatNode ssjavaLoopEntrance;
91
92   // keep the field ownership from the linear type checking
93   Hashtable<MethodDescriptor, Set<FieldDescriptor>> mapMethodToOwnedFieldSet;
94
95   Set<FlatNode> sameHeightWriteFlatNodeSet;
96
97   CallGraph callgraph;
98
99   LinearTypeCheck checker;
100
101   // maps a descriptor to its known dependents: namely
102   // methods or tasks that call the descriptor's method
103   // AND are part of this analysis (reachable from main)
104   private Hashtable<Descriptor, Set<MethodDescriptor>> mapDescriptorToSetDependents;
105
106   private LinkedList<MethodDescriptor> sortedDescriptors;
107
108   public SSJavaAnalysis(State state, TypeUtil tu, BuildFlat bf, CallGraph callgraph) {
109     this.state = state;
110     this.tu = tu;
111     this.callgraph = callgraph;
112     this.cd2lattice = new Hashtable<ClassDescriptor, SSJavaLattice<String>>();
113     this.cd2methodDefault = new Hashtable<ClassDescriptor, MethodLattice<String>>();
114     this.md2lattice = new Hashtable<MethodDescriptor, MethodLattice<String>>();
115     this.annotationRequireSet = new HashSet<MethodDescriptor>();
116     this.annotationRequireClassSet = new HashSet<ClassDescriptor>();
117     this.skipLoopTerminate = new Hashtable<MethodDescriptor, Integer>();
118     this.mapSharedLocation2DescriptorSet = new Hashtable<Location, Set<Descriptor>>();
119     this.linearTypeCheckMethodSet = new HashSet<MethodDescriptor>();
120     this.bf = bf;
121     this.trustWorthyMDSet = new HashSet<MethodDescriptor>();
122     this.mapMethodToOwnedFieldSet = new Hashtable<MethodDescriptor, Set<FieldDescriptor>>();
123     this.sameHeightWriteFlatNodeSet = new HashSet<FlatNode>();
124     this.mapDescriptorToSetDependents = new Hashtable<Descriptor, Set<MethodDescriptor>>();
125     this.sortedDescriptors = new LinkedList<MethodDescriptor>();
126   }
127
128   public void doCheck() {
129     doMethodAnnotationCheck();
130     computeLinearTypeCheckMethodSet();
131     doLinearTypeCheck();
132
133     init();
134
135     if (state.SSJAVADEBUG) {
136       // debugPrint();
137     }
138     if (state.SSJAVAINFER) {
139       inference();
140     } else {
141       parseLocationAnnotation();
142       doFlowDownCheck();
143       doDefinitelyWrittenCheck();
144       doLoopCheck();
145     }
146   }
147
148   private void init() {
149     // perform topological sort over the set of methods accessed by the main
150     // event loop
151     Set<MethodDescriptor> methodDescriptorsToAnalyze = new HashSet<MethodDescriptor>();
152     methodDescriptorsToAnalyze.addAll(getAnnotationRequireSet());
153     sortedDescriptors = topologicalSort(methodDescriptorsToAnalyze);
154   }
155
156   public LinkedList<MethodDescriptor> getSortedDescriptors() {
157     return (LinkedList<MethodDescriptor>) sortedDescriptors.clone();
158   }
159
160   private void inference() {
161     LocationInference inferEngine = new LocationInference(this, state);
162     inferEngine.inference();
163   }
164
165   private void doLoopCheck() {
166     GlobalFieldType gft = new GlobalFieldType(callgraph, state, tu.getMain());
167     LoopOptimize lo = new LoopOptimize(gft, tu);
168
169     SymbolTable classtable = state.getClassSymbolTable();
170
171     List<ClassDescriptor> toanalyzeList = new ArrayList<ClassDescriptor>();
172     List<MethodDescriptor> toanalyzeMethodList = new ArrayList<MethodDescriptor>();
173
174     toanalyzeList.addAll(classtable.getValueSet());
175     Collections.sort(toanalyzeList, new Comparator<ClassDescriptor>() {
176       public int compare(ClassDescriptor o1, ClassDescriptor o2) {
177         return o1.getClassName().compareTo(o2.getClassName());
178       }
179     });
180
181     for (int i = 0; i < toanalyzeList.size(); i++) {
182       ClassDescriptor cd = toanalyzeList.get(i);
183
184       SymbolTable methodtable = cd.getMethodTable();
185       toanalyzeMethodList.clear();
186       toanalyzeMethodList.addAll(methodtable.getValueSet());
187       Collections.sort(toanalyzeMethodList, new Comparator<MethodDescriptor>() {
188         public int compare(MethodDescriptor o1, MethodDescriptor o2) {
189           return o1.getSymbol().compareTo(o2.getSymbol());
190         }
191       });
192
193       for (int mdIdx = 0; mdIdx < toanalyzeMethodList.size(); mdIdx++) {
194         MethodDescriptor md = toanalyzeMethodList.get(mdIdx);
195         if (needTobeAnnotated(md)) {
196           lo.analyze(state.getMethodFlat(md));
197           doLoopTerminationCheck(lo, state.getMethodFlat(md));
198         }
199       }
200
201     }
202
203   }
204
205   public void addTrustMethod(MethodDescriptor md) {
206     trustWorthyMDSet.add(md);
207   }
208
209   public boolean isTrustMethod(MethodDescriptor md) {
210     return trustWorthyMDSet.contains(md);
211   }
212
213   private void computeLinearTypeCheckMethodSet() {
214
215     Set<MethodDescriptor> allCalledSet = callgraph.getMethodCalls(tu.getMain());
216     linearTypeCheckMethodSet.addAll(allCalledSet);
217
218     Set<MethodDescriptor> trustedSet = new HashSet<MethodDescriptor>();
219
220     for (Iterator iterator = trustWorthyMDSet.iterator(); iterator.hasNext();) {
221       MethodDescriptor trustMethod = (MethodDescriptor) iterator.next();
222       Set<MethodDescriptor> calledFromTrustMethodSet = callgraph.getMethodCalls(trustMethod);
223       trustedSet.add(trustMethod);
224       trustedSet.addAll(calledFromTrustMethodSet);
225     }
226
227     linearTypeCheckMethodSet.removeAll(trustedSet);
228
229     // if a method is called only by trusted method, no need to check linear
230     // type & flow down rule
231     for (Iterator iterator = trustedSet.iterator(); iterator.hasNext();) {
232       MethodDescriptor md = (MethodDescriptor) iterator.next();
233       Set<MethodDescriptor> callerSet = callgraph.getCallerSet(md);
234       if (!trustedSet.containsAll(callerSet) && !trustWorthyMDSet.contains(md)) {
235         linearTypeCheckMethodSet.add(md);
236       }
237     }
238
239   }
240
241   private void doLinearTypeCheck() {
242     LinearTypeCheck checker = new LinearTypeCheck(this, state);
243     checker.linearTypeCheck();
244   }
245
246   public void debugPrint() {
247     System.out.println("SSJAVA: SSJava is checking the following methods:");
248     for (Iterator<MethodDescriptor> iterator = annotationRequireSet.iterator(); iterator.hasNext();) {
249       MethodDescriptor md = iterator.next();
250       System.out.print(" " + md);
251     }
252     System.out.println();
253   }
254
255   private void doMethodAnnotationCheck() {
256     methodAnnotationChecker = new MethodAnnotationCheck(this, state, tu);
257     methodAnnotationChecker.methodAnnoatationCheck();
258     methodAnnotationChecker.methodAnnoataionInheritanceCheck();
259     state.setAnnotationRequireSet(annotationRequireSet);
260   }
261
262   public void doFlowDownCheck() {
263     flowDownChecker = new FlowDownCheck(this, state);
264     flowDownChecker.flowDownCheck();
265   }
266
267   public void doDefinitelyWrittenCheck() {
268     DefinitelyWrittenCheck checker = new DefinitelyWrittenCheck(this, state);
269     checker.definitelyWrittenCheck();
270   }
271
272   private void parseLocationAnnotation() {
273     Iterator it = state.getClassSymbolTable().getDescriptorsIterator();
274     while (it.hasNext()) {
275       ClassDescriptor cd = (ClassDescriptor) it.next();
276       // parsing location hierarchy declaration for the class
277       Vector<AnnotationDescriptor> classAnnotations = cd.getModifier().getAnnotations();
278       for (int i = 0; i < classAnnotations.size(); i++) {
279         AnnotationDescriptor an = classAnnotations.elementAt(i);
280         String marker = an.getMarker();
281         if (marker.equals(LATTICE)) {
282           SSJavaLattice<String> locOrder =
283               new SSJavaLattice<String>(SSJavaAnalysis.TOP, SSJavaAnalysis.BOTTOM);
284           cd2lattice.put(cd, locOrder);
285           parseClassLatticeDefinition(cd, an.getValue(), locOrder);
286
287           if (state.SSJAVADEBUG) {
288             // generate lattice dot file
289             writeLatticeDotFile(cd, null, locOrder);
290           }
291
292         } else if (marker.equals(METHODDEFAULT)) {
293           MethodLattice<String> locOrder =
294               new MethodLattice<String>(SSJavaAnalysis.TOP, SSJavaAnalysis.BOTTOM);
295           cd2methodDefault.put(cd, locOrder);
296           parseMethodDefaultLatticeDefinition(cd, an.getValue(), locOrder);
297         }
298       }
299
300       for (Iterator method_it = cd.getMethods(); method_it.hasNext();) {
301         MethodDescriptor md = (MethodDescriptor) method_it.next();
302         // parsing location hierarchy declaration for the method
303
304         if (needTobeAnnotated(md)) {
305           Vector<AnnotationDescriptor> methodAnnotations = md.getModifiers().getAnnotations();
306           if (methodAnnotations != null) {
307             for (int i = 0; i < methodAnnotations.size(); i++) {
308               AnnotationDescriptor an = methodAnnotations.elementAt(i);
309               if (an.getMarker().equals(LATTICE)) {
310                 // developer explicitly defines method lattice
311                 MethodLattice<String> locOrder =
312                     new MethodLattice<String>(SSJavaAnalysis.TOP, SSJavaAnalysis.BOTTOM);
313                 md2lattice.put(md, locOrder);
314                 parseMethodDefaultLatticeDefinition(cd, an.getValue(), locOrder);
315               } else if (an.getMarker().equals(TERMINATE)) {
316                 // developer explicitly wants to skip loop termination analysis
317                 String value = an.getValue();
318                 int maxIteration = 0;
319                 if (value != null) {
320                   maxIteration = Integer.parseInt(value);
321                 }
322                 skipLoopTerminate.put(md, new Integer(maxIteration));
323               }
324             }
325           }
326         }
327
328       }
329
330     }
331   }
332
333   public <T> void writeLatticeDotFile(ClassDescriptor cd, MethodDescriptor md,
334       SSJavaLattice<T> locOrder) {
335
336     String fileName = "lattice_";
337     if (md != null) {
338       fileName +=
339           cd.getSymbol().replaceAll("[\\W_]", "") + "_" + md.getSymbol().replaceAll("[\\W_]", "");
340     } else {
341       fileName += cd.getSymbol().replaceAll("[\\W_]", "");
342     }
343
344     Set<Pair<T, T>> pairSet = locOrder.getOrderingPairSet();
345
346     if (pairSet.size() > 0) {
347       try {
348         BufferedWriter bw = new BufferedWriter(new FileWriter(fileName + ".dot"));
349
350         bw.write("digraph " + fileName + " {\n");
351
352         for (Iterator iterator = pairSet.iterator(); iterator.hasNext();) {
353           // pair is in the form of <higher, lower>
354           Pair<T, T> pair = (Pair<T, T>) iterator.next();
355
356           T highLocId = pair.getFirst();
357           String highLocStr, lowLocStr;
358           if (locOrder.isSharedLoc(highLocId)) {
359             highLocStr = "\"" + highLocId + "*\"";
360           } else {
361             highLocStr = highLocId.toString();
362           }
363           T lowLocId = pair.getSecond();
364           if (locOrder.isSharedLoc(lowLocId)) {
365             lowLocStr = "\"" + lowLocId + "*\"";
366           } else {
367             lowLocStr = lowLocId.toString();
368           }
369           bw.write(highLocId + " -> " + lowLocId + ";\n");
370         }
371         bw.write("}\n");
372         bw.close();
373
374       } catch (IOException e) {
375         e.printStackTrace();
376       }
377
378     }
379
380   }
381
382   private void parseMethodDefaultLatticeDefinition(ClassDescriptor cd, String value,
383       MethodLattice<String> locOrder) {
384
385     value = value.replaceAll(" ", ""); // remove all blank spaces
386
387     StringTokenizer tokenizer = new StringTokenizer(value, ",");
388
389     while (tokenizer.hasMoreTokens()) {
390       String orderElement = tokenizer.nextToken();
391       int idx = orderElement.indexOf("<");
392       if (idx > 0) {// relative order element
393         String lowerLoc = orderElement.substring(0, idx);
394         String higherLoc = orderElement.substring(idx + 1);
395         locOrder.put(higherLoc, lowerLoc);
396         if (locOrder.isIntroducingCycle(higherLoc)) {
397           throw new Error("Error: the order relation " + lowerLoc + " < " + higherLoc
398               + " introduces a cycle.");
399         }
400       } else if (orderElement.startsWith(THISLOC + "=")) {
401         String thisLoc = orderElement.substring(8);
402         locOrder.setThisLoc(thisLoc);
403       } else if (orderElement.startsWith(GLOBALLOC + "=")) {
404         String globalLoc = orderElement.substring(10);
405         locOrder.setGlobalLoc(globalLoc);
406       } else if (orderElement.startsWith(RETURNLOC + "=")) {
407         String returnLoc = orderElement.substring(10);
408         locOrder.setReturnLoc(returnLoc);
409       } else if (orderElement.endsWith("*")) {
410         // spin loc definition
411         locOrder.addSharedLoc(orderElement.substring(0, orderElement.length() - 1));
412       } else {
413         // single element
414         locOrder.put(orderElement);
415       }
416     }
417
418     // sanity checks
419     if (locOrder.getThisLoc() != null && !locOrder.containsKey(locOrder.getThisLoc())) {
420       throw new Error("Variable 'this' location '" + locOrder.getThisLoc()
421           + "' is not defined in the local variable lattice at " + cd.getSourceFileName());
422     }
423
424     if (locOrder.getGlobalLoc() != null && !locOrder.containsKey(locOrder.getGlobalLoc())) {
425       throw new Error("Variable global location '" + locOrder.getGlobalLoc()
426           + "' is not defined in the local variable lattice at " + cd.getSourceFileName());
427     }
428   }
429
430   private void parseClassLatticeDefinition(ClassDescriptor cd, String value,
431       SSJavaLattice<String> locOrder) {
432
433     value = value.replaceAll(" ", ""); // remove all blank spaces
434
435     StringTokenizer tokenizer = new StringTokenizer(value, ",");
436
437     while (tokenizer.hasMoreTokens()) {
438       String orderElement = tokenizer.nextToken();
439       int idx = orderElement.indexOf("<");
440
441       if (idx > 0) {// relative order element
442         String lowerLoc = orderElement.substring(0, idx);
443         String higherLoc = orderElement.substring(idx + 1);
444         locOrder.put(higherLoc, lowerLoc);
445         if (locOrder.isIntroducingCycle(higherLoc)) {
446           throw new Error("Error: the order relation " + lowerLoc + " < " + higherLoc
447               + " introduces a cycle.");
448         }
449       } else if (orderElement.contains("*")) {
450         // spin loc definition
451         locOrder.addSharedLoc(orderElement.substring(0, orderElement.length() - 1));
452       } else {
453         // single element
454         locOrder.put(orderElement);
455       }
456     }
457
458     // sanity check
459     Set<String> spinLocSet = locOrder.getSharedLocSet();
460     for (Iterator iterator = spinLocSet.iterator(); iterator.hasNext();) {
461       String spinLoc = (String) iterator.next();
462       if (!locOrder.containsKey(spinLoc)) {
463         throw new Error("Spin location '" + spinLoc
464             + "' is not defined in the default local variable lattice at " + cd.getSourceFileName());
465       }
466     }
467   }
468
469   public Hashtable<ClassDescriptor, SSJavaLattice<String>> getCd2lattice() {
470     return cd2lattice;
471   }
472
473   public Hashtable<ClassDescriptor, MethodLattice<String>> getCd2methodDefault() {
474     return cd2methodDefault;
475   }
476
477   public Hashtable<MethodDescriptor, MethodLattice<String>> getMd2lattice() {
478     return md2lattice;
479   }
480
481   public SSJavaLattice<String> getClassLattice(ClassDescriptor cd) {
482     return cd2lattice.get(cd);
483   }
484
485   public MethodLattice<String> getMethodDefaultLattice(ClassDescriptor cd) {
486     return cd2methodDefault.get(cd);
487   }
488
489   public MethodLattice<String> getMethodLattice(MethodDescriptor md) {
490     if (md2lattice.containsKey(md)) {
491       return md2lattice.get(md);
492     } else {
493
494       if (cd2methodDefault.containsKey(md.getClassDesc())) {
495         return cd2methodDefault.get(md.getClassDesc());
496       } else {
497         throw new Error("Method Lattice of " + md + " is not defined.");
498       }
499
500     }
501   }
502
503   public boolean needToCheckLinearType(MethodDescriptor md) {
504     return linearTypeCheckMethodSet.contains(md);
505   }
506
507   public boolean needTobeAnnotated(MethodDescriptor md) {
508     return annotationRequireSet.contains(md);
509   }
510
511   public boolean needToBeAnnoated(ClassDescriptor cd) {
512     return annotationRequireClassSet.contains(cd);
513   }
514
515   public void addAnnotationRequire(ClassDescriptor cd) {
516     annotationRequireClassSet.add(cd);
517   }
518
519   public void addAnnotationRequire(MethodDescriptor md) {
520
521     ClassDescriptor cd = md.getClassDesc();
522     // if a method requires to be annotated, class containg that method also
523     // requires to be annotated
524     if (!isSSJavaUtil(cd)) {
525       annotationRequireClassSet.add(cd);
526       annotationRequireSet.add(md);
527     }
528   }
529
530   public Set<MethodDescriptor> getAnnotationRequireSet() {
531     return annotationRequireSet;
532   }
533
534   public void doLoopTerminationCheck(LoopOptimize lo, FlatMethod fm) {
535     LoopTerminate lt = new LoopTerminate(this, state);
536     if (needTobeAnnotated(fm.getMethod())) {
537       lt.terminateAnalysis(fm, lo.getLoopInvariant(fm));
538     }
539   }
540
541   public CallGraph getCallGraph() {
542     return callgraph;
543   }
544
545   public SSJavaLattice<String> getLattice(Descriptor d) {
546
547     if (d instanceof MethodDescriptor) {
548       return getMethodLattice((MethodDescriptor) d);
549     } else {
550       return getClassLattice((ClassDescriptor) d);
551     }
552
553   }
554
555   public boolean isSharedLocation(Location loc) {
556     SSJavaLattice<String> lattice = getLattice(loc.getDescriptor());
557     return lattice.getSharedLocSet().contains(loc.getLocIdentifier());
558   }
559
560   public void mapSharedLocation2Descriptor(Location loc, Descriptor d) {
561     Set<Descriptor> set = mapSharedLocation2DescriptorSet.get(loc);
562     if (set == null) {
563       set = new HashSet<Descriptor>();
564       mapSharedLocation2DescriptorSet.put(loc, set);
565     }
566     set.add(d);
567   }
568
569   public BuildFlat getBuildFlat() {
570     return bf;
571   }
572
573   public MethodDescriptor getMethodContainingSSJavaLoop() {
574     return methodContainingSSJavaLoop;
575   }
576
577   public void setMethodContainingSSJavaLoop(MethodDescriptor methodContainingSSJavaLoop) {
578     this.methodContainingSSJavaLoop = methodContainingSSJavaLoop;
579   }
580
581   public boolean isSSJavaUtil(ClassDescriptor cd) {
582     if (cd.getSymbol().equals("SSJAVA")) {
583       return true;
584     }
585     return false;
586   }
587
588   public void setFieldOnwership(MethodDescriptor md, FieldDescriptor field) {
589
590     Set<FieldDescriptor> fieldSet = mapMethodToOwnedFieldSet.get(md);
591     if (fieldSet == null) {
592       fieldSet = new HashSet<FieldDescriptor>();
593       mapMethodToOwnedFieldSet.put(md, fieldSet);
594     }
595     fieldSet.add(field);
596   }
597
598   public boolean isOwnedByMethod(MethodDescriptor md, FieldDescriptor field) {
599     Set<FieldDescriptor> fieldSet = mapMethodToOwnedFieldSet.get(md);
600     if (fieldSet != null) {
601       return fieldSet.contains(field);
602     }
603     return false;
604   }
605
606   public FlatNode getSSJavaLoopEntrance() {
607     return ssjavaLoopEntrance;
608   }
609
610   public void setSSJavaLoopEntrance(FlatNode ssjavaLoopEntrance) {
611     this.ssjavaLoopEntrance = ssjavaLoopEntrance;
612   }
613
614   public void addSameHeightWriteFlatNode(FlatNode fn) {
615     this.sameHeightWriteFlatNodeSet.add(fn);
616   }
617
618   public boolean isSameHeightWrite(FlatNode fn) {
619     return this.sameHeightWriteFlatNodeSet.contains(fn);
620   }
621
622   public LinkedList<MethodDescriptor> topologicalSort(Set<MethodDescriptor> toSort) {
623
624     Set<MethodDescriptor> discovered = new HashSet<MethodDescriptor>();
625
626     LinkedList<MethodDescriptor> sorted = new LinkedList<MethodDescriptor>();
627
628     Iterator<MethodDescriptor> itr = toSort.iterator();
629     while (itr.hasNext()) {
630       MethodDescriptor d = itr.next();
631
632       if (!discovered.contains(d)) {
633         dfsVisit(d, toSort, sorted, discovered);
634       }
635     }
636
637     return sorted;
638   }
639
640   // While we're doing DFS on call graph, remember
641   // dependencies for efficient queuing of methods
642   // during interprocedural analysis:
643   //
644   // a dependent of a method decriptor d for this analysis is:
645   // 1) a method or task that invokes d
646   // 2) in the descriptorsToAnalyze set
647   private void dfsVisit(MethodDescriptor md, Set<MethodDescriptor> toSort,
648       LinkedList<MethodDescriptor> sorted, Set<MethodDescriptor> discovered) {
649
650     discovered.add(md);
651
652     Iterator itr2 = callgraph.getCalleeSet(md).iterator();
653     while (itr2.hasNext()) {
654       MethodDescriptor dCallee = (MethodDescriptor) itr2.next();
655       addDependent(dCallee, md);
656     }
657
658     Iterator itr = callgraph.getCallerSet(md).iterator();
659     while (itr.hasNext()) {
660       MethodDescriptor dCaller = (MethodDescriptor) itr.next();
661       // only consider callers in the original set to analyze
662       if (!toSort.contains(dCaller)) {
663         continue;
664       }
665       if (!discovered.contains(dCaller)) {
666         addDependent(md, // callee
667             dCaller // caller
668         );
669
670         dfsVisit(dCaller, toSort, sorted, discovered);
671       }
672     }
673
674     // for leaf-nodes last now!
675     sorted.addLast(md);
676   }
677
678   public void addDependent(MethodDescriptor callee, MethodDescriptor caller) {
679     Set<MethodDescriptor> deps = mapDescriptorToSetDependents.get(callee);
680     if (deps == null) {
681       deps = new HashSet<MethodDescriptor>();
682     }
683     deps.add(caller);
684     mapDescriptorToSetDependents.put(callee, deps);
685   }
686
687   public Set<MethodDescriptor> getDependents(MethodDescriptor callee) {
688     Set<MethodDescriptor> deps = mapDescriptorToSetDependents.get(callee);
689     if (deps == null) {
690       deps = new HashSet<MethodDescriptor>();
691       mapDescriptorToSetDependents.put(callee, deps);
692     }
693     return deps;
694   }
695
696 }