changes on the SJava inference
[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, 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, SSJavaLattice<String> locOrder) {
331
332     String className = cd.getSymbol().replaceAll("[\\W_]", "");
333
334     Set<Pair<String, String>> pairSet = locOrder.getOrderingPairSet();
335
336     try {
337       BufferedWriter bw = new BufferedWriter(new FileWriter(className + ".dot"));
338
339       bw.write("digraph " + className + " {\n");
340
341       for (Iterator iterator = pairSet.iterator(); iterator.hasNext();) {
342         // pair is in the form of <higher, lower>
343         Pair<String, String> pair = (Pair<String, String>) iterator.next();
344
345         String highLocId = pair.getFirst();
346         if (locOrder.isSharedLoc(highLocId)) {
347           highLocId = "\"" + highLocId + "*\"";
348         }
349         String lowLocId = pair.getSecond();
350         if (locOrder.isSharedLoc(lowLocId)) {
351           lowLocId = "\"" + lowLocId + "*\"";
352         }
353         bw.write(highLocId + " -> " + lowLocId + ";\n");
354       }
355       bw.write("}\n");
356       bw.close();
357
358     } catch (IOException e) {
359       e.printStackTrace();
360     }
361
362   }
363
364   private void parseMethodDefaultLatticeDefinition(ClassDescriptor cd, String value,
365       MethodLattice<String> locOrder) {
366
367     value = value.replaceAll(" ", ""); // remove all blank spaces
368
369     StringTokenizer tokenizer = new StringTokenizer(value, ",");
370
371     while (tokenizer.hasMoreTokens()) {
372       String orderElement = tokenizer.nextToken();
373       int idx = orderElement.indexOf("<");
374       if (idx > 0) {// relative order element
375         String lowerLoc = orderElement.substring(0, idx);
376         String higherLoc = orderElement.substring(idx + 1);
377         locOrder.put(higherLoc, lowerLoc);
378         if (locOrder.isIntroducingCycle(higherLoc)) {
379           throw new Error("Error: the order relation " + lowerLoc + " < " + higherLoc
380               + " introduces a cycle.");
381         }
382       } else if (orderElement.startsWith(THISLOC + "=")) {
383         String thisLoc = orderElement.substring(8);
384         locOrder.setThisLoc(thisLoc);
385       } else if (orderElement.startsWith(GLOBALLOC + "=")) {
386         String globalLoc = orderElement.substring(10);
387         locOrder.setGlobalLoc(globalLoc);
388       } else if (orderElement.startsWith(RETURNLOC + "=")) {
389         String returnLoc = orderElement.substring(10);
390         locOrder.setReturnLoc(returnLoc);
391       } else if (orderElement.endsWith("*")) {
392         // spin loc definition
393         locOrder.addSharedLoc(orderElement.substring(0, orderElement.length() - 1));
394       } else {
395         // single element
396         locOrder.put(orderElement);
397       }
398     }
399
400     // sanity checks
401     if (locOrder.getThisLoc() != null && !locOrder.containsKey(locOrder.getThisLoc())) {
402       throw new Error("Variable 'this' location '" + locOrder.getThisLoc()
403           + "' is not defined in the local variable lattice at " + cd.getSourceFileName());
404     }
405
406     if (locOrder.getGlobalLoc() != null && !locOrder.containsKey(locOrder.getGlobalLoc())) {
407       throw new Error("Variable global location '" + locOrder.getGlobalLoc()
408           + "' is not defined in the local variable lattice at " + cd.getSourceFileName());
409     }
410   }
411
412   private void parseClassLatticeDefinition(ClassDescriptor cd, String value,
413       SSJavaLattice<String> locOrder) {
414
415     value = value.replaceAll(" ", ""); // remove all blank spaces
416
417     StringTokenizer tokenizer = new StringTokenizer(value, ",");
418
419     while (tokenizer.hasMoreTokens()) {
420       String orderElement = tokenizer.nextToken();
421       int idx = orderElement.indexOf("<");
422
423       if (idx > 0) {// relative order element
424         String lowerLoc = orderElement.substring(0, idx);
425         String higherLoc = orderElement.substring(idx + 1);
426         locOrder.put(higherLoc, lowerLoc);
427         if (locOrder.isIntroducingCycle(higherLoc)) {
428           throw new Error("Error: the order relation " + lowerLoc + " < " + higherLoc
429               + " introduces a cycle.");
430         }
431       } else if (orderElement.contains("*")) {
432         // spin loc definition
433         locOrder.addSharedLoc(orderElement.substring(0, orderElement.length() - 1));
434       } else {
435         // single element
436         locOrder.put(orderElement);
437       }
438     }
439
440     // sanity check
441     Set<String> spinLocSet = locOrder.getSharedLocSet();
442     for (Iterator iterator = spinLocSet.iterator(); iterator.hasNext();) {
443       String spinLoc = (String) iterator.next();
444       if (!locOrder.containsKey(spinLoc)) {
445         throw new Error("Spin location '" + spinLoc
446             + "' is not defined in the default local variable lattice at " + cd.getSourceFileName());
447       }
448     }
449   }
450
451   public Hashtable<ClassDescriptor, SSJavaLattice<String>> getCd2lattice() {
452     return cd2lattice;
453   }
454
455   public Hashtable<ClassDescriptor, MethodLattice<String>> getCd2methodDefault() {
456     return cd2methodDefault;
457   }
458
459   public Hashtable<MethodDescriptor, MethodLattice<String>> getMd2lattice() {
460     return md2lattice;
461   }
462
463   public SSJavaLattice<String> getClassLattice(ClassDescriptor cd) {
464     return cd2lattice.get(cd);
465   }
466
467   public MethodLattice<String> getMethodDefaultLattice(ClassDescriptor cd) {
468     return cd2methodDefault.get(cd);
469   }
470
471   public MethodLattice<String> getMethodLattice(MethodDescriptor md) {
472     if (md2lattice.containsKey(md)) {
473       return md2lattice.get(md);
474     } else {
475
476       if (cd2methodDefault.containsKey(md.getClassDesc())) {
477         return cd2methodDefault.get(md.getClassDesc());
478       } else {
479         throw new Error("Method Lattice of " + md + " is not defined.");
480       }
481
482     }
483   }
484
485   public boolean needToCheckLinearType(MethodDescriptor md) {
486     return linearTypeCheckMethodSet.contains(md);
487   }
488
489   public boolean needTobeAnnotated(MethodDescriptor md) {
490     return annotationRequireSet.contains(md);
491   }
492
493   public boolean needToBeAnnoated(ClassDescriptor cd) {
494     return annotationRequireClassSet.contains(cd);
495   }
496
497   public void addAnnotationRequire(ClassDescriptor cd) {
498     annotationRequireClassSet.add(cd);
499   }
500
501   public void addAnnotationRequire(MethodDescriptor md) {
502
503     ClassDescriptor cd = md.getClassDesc();
504     // if a method requires to be annotated, class containg that method also
505     // requires to be annotated
506     if (!isSSJavaUtil(cd)) {
507       annotationRequireClassSet.add(cd);
508       annotationRequireSet.add(md);
509     }
510   }
511
512   public Set<MethodDescriptor> getAnnotationRequireSet() {
513     return annotationRequireSet;
514   }
515
516   public void doLoopTerminationCheck(LoopOptimize lo, FlatMethod fm) {
517     LoopTerminate lt = new LoopTerminate(this, state);
518     if (needTobeAnnotated(fm.getMethod())) {
519       lt.terminateAnalysis(fm, lo.getLoopInvariant(fm));
520     }
521   }
522
523   public CallGraph getCallGraph() {
524     return callgraph;
525   }
526
527   public SSJavaLattice<String> getLattice(Descriptor d) {
528
529     if (d instanceof MethodDescriptor) {
530       return getMethodLattice((MethodDescriptor) d);
531     } else {
532       return getClassLattice((ClassDescriptor) d);
533     }
534
535   }
536
537   public boolean isSharedLocation(Location loc) {
538     SSJavaLattice<String> lattice = getLattice(loc.getDescriptor());
539     return lattice.getSharedLocSet().contains(loc.getLocIdentifier());
540   }
541
542   public void mapSharedLocation2Descriptor(Location loc, Descriptor d) {
543     Set<Descriptor> set = mapSharedLocation2DescriptorSet.get(loc);
544     if (set == null) {
545       set = new HashSet<Descriptor>();
546       mapSharedLocation2DescriptorSet.put(loc, set);
547     }
548     set.add(d);
549   }
550
551   public BuildFlat getBuildFlat() {
552     return bf;
553   }
554
555   public MethodDescriptor getMethodContainingSSJavaLoop() {
556     return methodContainingSSJavaLoop;
557   }
558
559   public void setMethodContainingSSJavaLoop(MethodDescriptor methodContainingSSJavaLoop) {
560     this.methodContainingSSJavaLoop = methodContainingSSJavaLoop;
561   }
562
563   public boolean isSSJavaUtil(ClassDescriptor cd) {
564     if (cd.getSymbol().equals("SSJAVA")) {
565       return true;
566     }
567     return false;
568   }
569
570   public void setFieldOnwership(MethodDescriptor md, FieldDescriptor field) {
571
572     Set<FieldDescriptor> fieldSet = mapMethodToOwnedFieldSet.get(md);
573     if (fieldSet == null) {
574       fieldSet = new HashSet<FieldDescriptor>();
575       mapMethodToOwnedFieldSet.put(md, fieldSet);
576     }
577     fieldSet.add(field);
578   }
579
580   public boolean isOwnedByMethod(MethodDescriptor md, FieldDescriptor field) {
581     Set<FieldDescriptor> fieldSet = mapMethodToOwnedFieldSet.get(md);
582     if (fieldSet != null) {
583       return fieldSet.contains(field);
584     }
585     return false;
586   }
587
588   public FlatNode getSSJavaLoopEntrance() {
589     return ssjavaLoopEntrance;
590   }
591
592   public void setSSJavaLoopEntrance(FlatNode ssjavaLoopEntrance) {
593     this.ssjavaLoopEntrance = ssjavaLoopEntrance;
594   }
595
596   public void addSameHeightWriteFlatNode(FlatNode fn) {
597     this.sameHeightWriteFlatNodeSet.add(fn);
598   }
599
600   public boolean isSameHeightWrite(FlatNode fn) {
601     return this.sameHeightWriteFlatNodeSet.contains(fn);
602   }
603
604   public LinkedList<MethodDescriptor> topologicalSort(Set<MethodDescriptor> toSort) {
605
606     Set<MethodDescriptor> discovered = new HashSet<MethodDescriptor>();
607
608     LinkedList<MethodDescriptor> sorted = new LinkedList<MethodDescriptor>();
609
610     Iterator<MethodDescriptor> itr = toSort.iterator();
611     while (itr.hasNext()) {
612       MethodDescriptor d = itr.next();
613
614       if (!discovered.contains(d)) {
615         dfsVisit(d, toSort, sorted, discovered);
616       }
617     }
618
619     return sorted;
620   }
621
622   // While we're doing DFS on call graph, remember
623   // dependencies for efficient queuing of methods
624   // during interprocedural analysis:
625   //
626   // a dependent of a method decriptor d for this analysis is:
627   // 1) a method or task that invokes d
628   // 2) in the descriptorsToAnalyze set
629   private void dfsVisit(MethodDescriptor md, Set<MethodDescriptor> toSort,
630       LinkedList<MethodDescriptor> sorted, Set<MethodDescriptor> discovered) {
631
632     discovered.add(md);
633
634     Iterator itr = callgraph.getCallerSet(md).iterator();
635     while (itr.hasNext()) {
636       MethodDescriptor dCaller = (MethodDescriptor) itr.next();
637       // only consider callers in the original set to analyze
638       if (!toSort.contains(dCaller)) {
639         continue;
640       }
641       if (!discovered.contains(dCaller)) {
642         addDependent(md, // callee
643             dCaller // caller
644         );
645
646         dfsVisit(dCaller, toSort, sorted, discovered);
647       }
648     }
649
650     // for leaf-nodes last now!
651     sorted.addLast(md);
652   }
653
654   public void addDependent(MethodDescriptor callee, MethodDescriptor caller) {
655     Set<MethodDescriptor> deps = mapDescriptorToSetDependents.get(callee);
656     if (deps == null) {
657       deps = new HashSet<MethodDescriptor>();
658     }
659     deps.add(caller);
660     mapDescriptorToSetDependents.put(callee, deps);
661   }
662
663   public Set<MethodDescriptor> getDependents(MethodDescriptor callee) {
664     Set<MethodDescriptor> deps = mapDescriptorToSetDependents.get(callee);
665     if (deps == null) {
666       deps = new HashSet<MethodDescriptor>();
667       mapDescriptorToSetDependents.put(callee, deps);
668     }
669     return deps;
670   }
671
672 }