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