enforce strict monotonicity for initial method contexts and back edges as defined...
[IRC.git] / Robust / src / Analysis / Disjoint / DisjointAnalysis.java
1 package Analysis.Disjoint;
2
3 import Analysis.CallGraph.*;
4 import Analysis.Liveness;
5 import Analysis.ArrayReferencees;
6 import IR.*;
7 import IR.Flat.*;
8 import IR.Tree.Modifiers;
9 import java.util.*;
10 import java.io.*;
11
12
13 public class DisjointAnalysis {
14         
15           ///////////////////////////////////////////
16           //
17           //  Public interface to discover possible
18           //  aliases in the program under analysis
19           //
20           ///////////////////////////////////////////
21         
22           public HashSet<AllocSite>
23           getFlaggedAllocationSitesReachableFromTask(TaskDescriptor td) {
24             checkAnalysisComplete();
25             return getFlaggedAllocationSitesReachableFromTaskPRIVATE(td);
26           }
27           
28           public AllocSite getAllocationSiteFromFlatNew(FlatNew fn) {
29                     checkAnalysisComplete();
30                     return getAllocSiteFromFlatNewPRIVATE(fn);
31            }      
32           
33           public AllocSite getAllocationSiteFromHeapRegionNodeID(Integer id) {
34                     checkAnalysisComplete();
35                     return mapHrnIdToAllocSite.get(id);
36           }
37           
38           public Set<HeapRegionNode> hasPotentialSharing(Descriptor taskOrMethod,
39               int paramIndex1,
40               int paramIndex2) {
41                   checkAnalysisComplete();
42                   ReachGraph rg=mapDescriptorToCompleteReachGraph.get(taskOrMethod);
43                   FlatMethod fm=state.getMethodFlat(taskOrMethod);
44                   assert(rg != null);
45                   return rg.mayReachSharedObjects(fm, paramIndex1, paramIndex2);
46           }
47           
48         public Set<HeapRegionNode> hasPotentialSharing(Descriptor taskOrMethod,
49                         int paramIndex, AllocSite alloc) {
50                 checkAnalysisComplete();
51                 ReachGraph rg = mapDescriptorToCompleteReachGraph.get(taskOrMethod);
52             FlatMethod fm=state.getMethodFlat(taskOrMethod);
53                 assert (rg != null);
54                 return rg.mayReachSharedObjects(fm, paramIndex, alloc);
55         }
56
57         public Set<HeapRegionNode> hasPotentialSharing(Descriptor taskOrMethod,
58                         AllocSite alloc, int paramIndex) {
59                 checkAnalysisComplete();
60                 ReachGraph rg  = mapDescriptorToCompleteReachGraph.get(taskOrMethod);
61                 FlatMethod fm=state.getMethodFlat(taskOrMethod);
62                 assert (rg != null);
63                 return rg.mayReachSharedObjects(fm, paramIndex, alloc);
64         }
65
66         public Set<HeapRegionNode> hasPotentialSharing(Descriptor taskOrMethod,
67                         AllocSite alloc1, AllocSite alloc2) {
68                 checkAnalysisComplete();
69                 ReachGraph rg  = mapDescriptorToCompleteReachGraph.get(taskOrMethod);
70                 assert (rg != null);
71                 return rg.mayReachSharedObjects(alloc1, alloc2);
72         }
73         
74         public String prettyPrintNodeSet(Set<HeapRegionNode> s) {
75                 checkAnalysisComplete();
76
77                 String out = "{\n";
78
79                 Iterator<HeapRegionNode> i = s.iterator();
80                 while (i.hasNext()) {
81                         HeapRegionNode n = i.next();
82
83                         AllocSite as = n.getAllocSite();
84                         if (as == null) {
85                                 out += "  " + n.toString() + ",\n";
86                         } else {
87                                 out += "  " + n.toString() + ": " + as.toStringVerbose()
88                                                 + ",\n";
89                         }
90                 }
91
92                 out += "}\n";
93                 return out;
94         }
95         
96   // use the methods given above to check every possible alias
97   // between task parameters and flagged allocation sites reachable
98   // from the task
99   public void writeAllAliases(String outputFile, 
100                               String timeReport,
101                               String justTime,
102                               boolean tabularOutput,
103                               int numLines
104                               )
105     throws java.io.IOException {
106     checkAnalysisComplete();
107
108     BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile));
109
110     if (!tabularOutput) {
111       bw.write("Conducting ownership analysis with allocation depth = "
112                + allocationDepth + "\n");
113       bw.write(timeReport + "\n");
114     }
115
116     int numAlias = 0;
117
118     // look through every task for potential aliases
119     Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
120     while (taskItr.hasNext()) {
121       TaskDescriptor td = (TaskDescriptor) taskItr.next();
122
123       if (!tabularOutput) {
124         bw.write("\n---------" + td + "--------\n");
125       }
126
127       HashSet<AllocSite> allocSites = getFlaggedAllocationSitesReachableFromTask(td);
128
129       Set<HeapRegionNode> common;
130
131       // for each task parameter, check for aliases with
132       // other task parameters and every allocation site
133       // reachable from this task
134       boolean foundSomeAlias = false;
135
136       FlatMethod fm = state.getMethodFlat(td);
137       for (int i = 0; i < fm.numParameters(); ++i) {
138
139         // skip parameters with types that cannot reference
140         // into the heap
141         if( !shouldAnalysisTrack( fm.getParameter( i ).getType() ) ) {
142           continue;
143         }
144                           
145         // for the ith parameter check for aliases to all
146         // higher numbered parameters
147         for (int j = i + 1; j < fm.numParameters(); ++j) {
148
149           // skip parameters with types that cannot reference
150           // into the heap
151           if( !shouldAnalysisTrack( fm.getParameter( j ).getType() ) ) {
152             continue;
153           }
154
155
156           common = hasPotentialSharing(td, i, j);
157           if (!common.isEmpty()) {
158             foundSomeAlias = true;
159             if (!tabularOutput) {
160               bw.write("Potential alias between parameters " + i
161                        + " and " + j + ".\n");
162               bw.write(prettyPrintNodeSet(common) + "\n");
163             } else {
164               ++numAlias;
165             }
166           }
167         }
168
169         // for the ith parameter, check for aliases against
170         // the set of allocation sites reachable from this
171         // task context
172         Iterator allocItr = allocSites.iterator();
173         while (allocItr.hasNext()) {
174           AllocSite as = (AllocSite) allocItr.next();
175           common = hasPotentialSharing(td, i, as);
176           if (!common.isEmpty()) {
177             foundSomeAlias = true;
178             if (!tabularOutput) {
179               bw.write("Potential alias between parameter " + i
180                        + " and " + as.getFlatNew() + ".\n");
181               bw.write(prettyPrintNodeSet(common) + "\n");
182             } else {
183               ++numAlias;
184             }
185           }
186         }
187       }
188
189       // for each allocation site check for aliases with
190       // other allocation sites in the context of execution
191       // of this task
192       HashSet<AllocSite> outerChecked = new HashSet<AllocSite>();
193       Iterator allocItr1 = allocSites.iterator();
194       while (allocItr1.hasNext()) {
195         AllocSite as1 = (AllocSite) allocItr1.next();
196
197         Iterator allocItr2 = allocSites.iterator();
198         while (allocItr2.hasNext()) {
199           AllocSite as2 = (AllocSite) allocItr2.next();
200
201           if (!outerChecked.contains(as2)) {
202             common = hasPotentialSharing(td, as1, as2);
203
204             if (!common.isEmpty()) {
205               foundSomeAlias = true;
206               if (!tabularOutput) {
207                 bw.write("Potential alias between "
208                          + as1.getFlatNew() + " and "
209                          + as2.getFlatNew() + ".\n");
210                 bw.write(prettyPrintNodeSet(common) + "\n");
211               } else {
212                 ++numAlias;
213               }
214             }
215           }
216         }
217
218         outerChecked.add(as1);
219       }
220
221       if (!foundSomeAlias) {
222         if (!tabularOutput) {
223           bw.write("No aliases between flagged objects in Task " + td
224                    + ".\n");
225         }
226       }
227     }
228
229                 
230     if (tabularOutput) {
231       bw.write(" & " + numAlias + " & " + justTime + " & " + numLines
232                + " & " + numMethodsAnalyzed() + " \\\\\n");
233     }           
234
235     bw.close();
236   }
237         
238   // this version of writeAllAliases is for Java programs that have no tasks
239   public void writeAllAliasesJava(String outputFile, 
240                                   String timeReport,
241                                   String justTime,
242                                   boolean tabularOutput,
243                                   int numLines
244                                   )
245     throws java.io.IOException {
246     checkAnalysisComplete();
247
248     assert !state.TASK;
249
250     BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile));
251     
252     bw.write("Conducting disjoint reachability analysis with allocation depth = "
253              + allocationDepth + "\n");
254     bw.write(timeReport + "\n\n");
255
256     boolean foundSomeAlias = false;
257
258     Descriptor d = typeUtil.getMain();
259     HashSet<AllocSite> allocSites = getFlaggedAllocationSites(d);
260
261     // for each allocation site check for aliases with
262     // other allocation sites in the context of execution
263     // of this task
264     HashSet<AllocSite> outerChecked = new HashSet<AllocSite>();
265     Iterator allocItr1 = allocSites.iterator();
266     while (allocItr1.hasNext()) {
267       AllocSite as1 = (AllocSite) allocItr1.next();
268
269       Iterator allocItr2 = allocSites.iterator();
270       while (allocItr2.hasNext()) {
271         AllocSite as2 = (AllocSite) allocItr2.next();
272
273         if (!outerChecked.contains(as2)) {
274           Set<HeapRegionNode> common = hasPotentialSharing(d,
275                                                            as1, as2);
276
277           if (!common.isEmpty()) {
278             foundSomeAlias = true;
279             bw.write("Potential alias between "
280                      + as1.getDisjointAnalysisId() + " and "
281                      + as2.getDisjointAnalysisId() + ".\n");
282             bw.write(prettyPrintNodeSet(common) + "\n");
283           }
284         }
285       }
286
287       outerChecked.add(as1);
288     }
289
290     if (!foundSomeAlias) {
291       bw.write("No aliases between flagged objects found.\n");
292     }
293
294     bw.write("Number of methods analyzed: "+numMethodsAnalyzed()+"\n");
295
296     bw.close();
297   }
298           
299   ///////////////////////////////////////////
300   //
301   // end public interface
302   //
303   ///////////////////////////////////////////
304
305   protected void checkAnalysisComplete() {
306     if( !analysisComplete ) {
307       throw new Error("Warning: public interface method called while analysis is running.");
308     }
309   } 
310
311
312   // run in faster mode, only when bugs wrung out!
313   public static boolean releaseMode;
314
315   // data from the compiler
316   public State            state;
317   public CallGraph        callGraph;
318   public Liveness         liveness;
319   public ArrayReferencees arrayReferencees;
320   public TypeUtil         typeUtil;
321   public int              allocationDepth;
322   
323   // data structure for public interface
324   private Hashtable<Descriptor,    HashSet<AllocSite> > mapDescriptorToAllocSiteSet;
325
326   
327   // for public interface methods to warn that they
328   // are grabbing results during analysis
329   private boolean analysisComplete;
330
331
332   // used to identify HeapRegionNode objects
333   // A unique ID equates an object in one
334   // ownership graph with an object in another
335   // graph that logically represents the same
336   // heap region
337   // start at 10 and increment to reserve some
338   // IDs for special purposes
339   static protected int uniqueIDcount = 10;
340
341
342   // An out-of-scope method created by the
343   // analysis that has no parameters, and
344   // appears to allocate the command line
345   // arguments, then invoke the source code's
346   // main method.  The purpose of this is to
347   // provide the analysis with an explicit
348   // top-level context with no parameters
349   protected MethodDescriptor mdAnalysisEntry;
350   protected FlatMethod       fmAnalysisEntry;
351
352   // main method defined by source program
353   protected MethodDescriptor mdSourceEntry;
354
355   // the set of task and/or method descriptors
356   // reachable in call graph
357   protected Set<Descriptor> 
358     descriptorsToAnalyze;
359
360   // current descriptors to visit in fixed-point
361   // interprocedural analysis, prioritized by
362   // dependency in the call graph
363   protected Stack<DescriptorQWrapper>
364     descriptorsToVisitStack;
365   protected PriorityQueue<DescriptorQWrapper> 
366     descriptorsToVisitQ;
367   
368   // a duplication of the above structure, but
369   // for efficient testing of inclusion
370   protected HashSet<Descriptor> 
371     descriptorsToVisitSet;
372
373   // storage for priorities (doesn't make sense)
374   // to add it to the Descriptor class, just in
375   // this analysis
376   protected Hashtable<Descriptor, Integer> 
377     mapDescriptorToPriority;
378
379
380   // maps a descriptor to its current partial result
381   // from the intraprocedural fixed-point analysis--
382   // then the interprocedural analysis settles, this
383   // mapping will have the final results for each
384   // method descriptor
385   protected Hashtable<Descriptor, ReachGraph> 
386     mapDescriptorToCompleteReachGraph;
387
388   // maps a descriptor to its known dependents: namely
389   // methods or tasks that call the descriptor's method
390   // AND are part of this analysis (reachable from main)
391   protected Hashtable< Descriptor, Set<Descriptor> >
392     mapDescriptorToSetDependents;
393
394   // maps each flat new to one analysis abstraction
395   // allocate site object, these exist outside reach graphs
396   protected Hashtable<FlatNew, AllocSite>
397     mapFlatNewToAllocSite;
398
399   // maps intergraph heap region IDs to intergraph
400   // allocation sites that created them, a redundant
401   // structure for efficiency in some operations
402   protected Hashtable<Integer, AllocSite>
403     mapHrnIdToAllocSite;
404
405   // maps a method to its initial heap model (IHM) that
406   // is the set of reachability graphs from every caller
407   // site, all merged together.  The reason that we keep
408   // them separate is that any one call site's contribution
409   // to the IHM may changed along the path to the fixed point
410   protected Hashtable< Descriptor, Hashtable< FlatCall, ReachGraph > >
411     mapDescriptorToIHMcontributions;
412
413   // additionally, keep a mapping from descriptors to the
414   // merged in-coming initial context, because we want this
415   // initial context to be STRICTLY MONOTONIC
416   protected Hashtable<Descriptor, ReachGraph>
417     mapDescriptorToInitialContext;
418
419   // make the result for back edges analysis-wide STRICTLY
420   // MONOTONIC as well, but notice we use FlatNode as the
421   // key for this map: in case we want to consider other
422   // nodes as back edge's in future implementations
423   protected Hashtable<FlatNode, ReachGraph>
424     mapBackEdgeToMonotone;
425   
426
427   public static final String arrayElementFieldName = "___element_";
428   static protected Hashtable<TypeDescriptor, FieldDescriptor>
429     mapTypeToArrayField;
430
431   // for controlling DOT file output
432   protected boolean writeFinalDOTs;
433   protected boolean writeAllIncrementalDOTs;
434
435   // supporting DOT output--when we want to write every
436   // partial method result, keep a tally for generating
437   // unique filenames
438   protected Hashtable<Descriptor, Integer>
439     mapDescriptorToNumUpdates;
440   
441   //map task descriptor to initial task parameter 
442   protected Hashtable<Descriptor, ReachGraph>
443     mapDescriptorToReachGraph;
444
445   protected PointerMethod pm;
446
447   static protected Hashtable<FlatNode, ReachGraph> fn2rg =
448     new Hashtable<FlatNode, ReachGraph>();
449
450
451   // allocate various structures that are not local
452   // to a single class method--should be done once
453   protected void allocateStructures() {    
454     descriptorsToAnalyze = new HashSet<Descriptor>();
455
456     mapDescriptorToCompleteReachGraph =
457       new Hashtable<Descriptor, ReachGraph>();
458
459     mapDescriptorToNumUpdates =
460       new Hashtable<Descriptor, Integer>();
461
462     mapDescriptorToSetDependents =
463       new Hashtable< Descriptor, Set<Descriptor> >();
464
465     mapFlatNewToAllocSite = 
466       new Hashtable<FlatNew, AllocSite>();
467
468     mapDescriptorToIHMcontributions =
469       new Hashtable< Descriptor, Hashtable< FlatCall, ReachGraph > >();
470
471     mapDescriptorToInitialContext =
472       new Hashtable<Descriptor, ReachGraph>();    
473
474     mapBackEdgeToMonotone =
475       new Hashtable<FlatNode, ReachGraph>();
476
477     mapHrnIdToAllocSite =
478       new Hashtable<Integer, AllocSite>();
479
480     mapTypeToArrayField = 
481       new Hashtable <TypeDescriptor, FieldDescriptor>();
482
483     if( state.DISJOINTDVISITSTACK ) {
484       descriptorsToVisitStack =
485         new Stack<DescriptorQWrapper>();
486     }
487
488     if( state.DISJOINTDVISITPQUE ) {
489       descriptorsToVisitQ =
490         new PriorityQueue<DescriptorQWrapper>();
491     }
492
493     descriptorsToVisitSet =
494       new HashSet<Descriptor>();
495
496     mapDescriptorToPriority =
497       new Hashtable<Descriptor, Integer>();
498     
499     mapDescriptorToAllocSiteSet =
500         new Hashtable<Descriptor,    HashSet<AllocSite> >();
501     
502     mapDescriptorToReachGraph = 
503         new Hashtable<Descriptor, ReachGraph>();
504   }
505
506
507
508   // this analysis generates a disjoint reachability
509   // graph for every reachable method in the program
510   public DisjointAnalysis( State            s,
511                            TypeUtil         tu,
512                            CallGraph        cg,
513                            Liveness         l,
514                            ArrayReferencees ar
515                            ) throws java.io.IOException {
516     init( s, tu, cg, l, ar );
517   }
518   
519   protected void init( State            state,
520                        TypeUtil         typeUtil,
521                        CallGraph        callGraph,
522                        Liveness         liveness,
523                        ArrayReferencees arrayReferencees
524                        ) throws java.io.IOException {
525           
526     analysisComplete = false;
527     
528     this.state                   = state;
529     this.typeUtil                = typeUtil;
530     this.callGraph               = callGraph;
531     this.liveness                = liveness;
532     this.arrayReferencees        = arrayReferencees;
533     this.allocationDepth         = state.DISJOINTALLOCDEPTH;
534     this.releaseMode             = state.DISJOINTRELEASEMODE;
535
536     this.writeFinalDOTs          = state.DISJOINTWRITEDOTS && !state.DISJOINTWRITEALL;
537     this.writeAllIncrementalDOTs = state.DISJOINTWRITEDOTS &&  state.DISJOINTWRITEALL;
538
539     this.takeDebugSnapshots      = state.DISJOINTSNAPSYMBOL != null;
540     this.descSymbolDebug         = state.DISJOINTSNAPSYMBOL;
541     this.visitStartCapture       = state.DISJOINTSNAPVISITTOSTART;
542     this.numVisitsToCapture      = state.DISJOINTSNAPNUMVISITS;
543     this.stopAfterCapture        = state.DISJOINTSNAPSTOPAFTER;
544     this.snapVisitCounter        = 1; // count visits from 1 (user will write 1, means 1st visit)
545     this.snapNodeCounter         = 0; // count nodes from 0
546     this.pm=new PointerMethod();
547
548     assert state.DISJOINTDVISITSTACK || state.DISJOINTDVISITPQUE;
549     assert !(state.DISJOINTDVISITSTACK && state.DISJOINTDVISITPQUE);
550             
551     // set some static configuration for ReachGraphs
552     ReachGraph.allocationDepth = allocationDepth;
553     ReachGraph.typeUtil        = typeUtil;
554
555     ReachGraph.debugCallSiteVisitsUntilExit = state.DISJOINTDEBUGCALLCOUNT;
556
557     allocateStructures();
558
559     double timeStartAnalysis = (double) System.nanoTime();
560
561     // start interprocedural fixed-point computation
562     analyzeMethods();
563     analysisComplete=true;
564
565     double timeEndAnalysis = (double) System.nanoTime();
566     double dt = (timeEndAnalysis - timeStartAnalysis)/(Math.pow( 10.0, 9.0 ) );
567     String treport = String.format( "The reachability analysis took %.3f sec.", dt );
568     String justtime = String.format( "%.2f", dt );
569     System.out.println( treport );
570
571     if( writeFinalDOTs && !writeAllIncrementalDOTs ) {
572       writeFinalGraphs();      
573     }
574
575     if( state.DISJOINTWRITEIHMS ) {
576       writeFinalIHMs();
577     }
578
579     if( state.DISJOINTALIASFILE != null ) {
580       if( state.TASK ) {
581         writeAllAliases(state.DISJOINTALIASFILE, treport, justtime, state.DISJOINTALIASTAB, state.lines);
582       } else {
583         writeAllAliasesJava(state.DISJOINTALIASFILE, 
584                             treport, 
585                             justtime, 
586                             state.DISJOINTALIASTAB, 
587                             state.lines
588                             );
589       }
590     }
591   }
592
593
594   protected boolean moreDescriptorsToVisit() {
595     if( state.DISJOINTDVISITSTACK ) {
596       return !descriptorsToVisitStack.isEmpty();
597
598     } else if( state.DISJOINTDVISITPQUE ) {
599       return !descriptorsToVisitQ.isEmpty();
600     }
601
602     throw new Error( "Neither descriptor visiting mode set" );
603   }
604
605
606   // fixed-point computation over the call graph--when a
607   // method's callees are updated, it must be reanalyzed
608   protected void analyzeMethods() throws java.io.IOException {  
609
610     if( state.TASK ) {
611       // This analysis does not support Bamboo at the moment,
612       // but if it does in the future we would initialize the
613       // set of descriptors to analyze as the program-reachable
614       // tasks and the methods callable by them.  For Java,
615       // just methods reachable from the main method.
616       System.out.println( "Bamboo..." );
617       Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
618       
619       while (taskItr.hasNext()) {
620           TaskDescriptor td = (TaskDescriptor) taskItr.next();
621           if (!descriptorsToAnalyze.contains(td)) {           
622               descriptorsToAnalyze.add(td);
623               descriptorsToAnalyze.addAll(callGraph.getAllMethods(td));
624           }       
625       }
626
627     } else {
628       // add all methods transitively reachable from the
629       // source's main to set for analysis
630       mdSourceEntry = typeUtil.getMain();
631       descriptorsToAnalyze.add( mdSourceEntry );
632       descriptorsToAnalyze.addAll( 
633         callGraph.getAllMethods( mdSourceEntry ) 
634                                    );
635
636       // fabricate an empty calling context that will call
637       // the source's main, but call graph doesn't know
638       // about it, so explicitly add it
639       makeAnalysisEntryMethod( mdSourceEntry );
640       descriptorsToAnalyze.add( mdAnalysisEntry );
641     }
642
643     // topologically sort according to the call graph so 
644     // leaf calls are ordered first, smarter analysis order
645     // CHANGED: order leaf calls last!!
646     LinkedList<Descriptor> sortedDescriptors = 
647       topologicalSort( descriptorsToAnalyze );
648
649     // add sorted descriptors to priority queue, and duplicate
650     // the queue as a set for efficiently testing whether some
651     // method is marked for analysis
652     int p = 0;
653     Iterator<Descriptor> dItr = sortedDescriptors.iterator();
654     while( dItr.hasNext() ) {
655       Descriptor d = dItr.next();
656
657       mapDescriptorToPriority.put( d, new Integer( p ) );
658
659       if( state.DISJOINTDVISITSTACK ) {
660         descriptorsToVisitStack.add( new DescriptorQWrapper( p, d ) );
661
662       } else if( state.DISJOINTDVISITPQUE ) {
663         descriptorsToVisitQ.add( new DescriptorQWrapper( p, d ) );
664       }
665
666       descriptorsToVisitSet.add( d );
667       ++p;
668     }
669
670     // analyze methods from the priority queue until it is empty
671     while( moreDescriptorsToVisit() ) {
672       Descriptor d = null;
673
674       if( state.DISJOINTDVISITSTACK ) {
675         d = descriptorsToVisitStack.pop().getDescriptor();
676
677       } else if( state.DISJOINTDVISITPQUE ) {
678         d = descriptorsToVisitQ.poll().getDescriptor();
679       }
680
681       assert descriptorsToVisitSet.contains( d );
682       descriptorsToVisitSet.remove( d );
683
684       // because the task or method descriptor just extracted
685       // was in the "to visit" set it either hasn't been analyzed
686       // yet, or some method that it depends on has been
687       // updated.  Recompute a complete reachability graph for
688       // this task/method and compare it to any previous result.
689       // If there is a change detected, add any methods/tasks
690       // that depend on this one to the "to visit" set.
691
692       System.out.println( "Analyzing " + d );
693
694       ReachGraph rg     = analyzeMethod( d );
695       ReachGraph rgPrev = getPartial( d );
696       
697       if( !rg.equals( rgPrev ) ) {
698         setPartial( d, rg );
699         
700         // results for d changed, so enqueue dependents
701         // of d for further analysis
702         Iterator<Descriptor> depsItr = getDependents( d ).iterator();
703         while( depsItr.hasNext() ) {
704           Descriptor dNext = depsItr.next();
705           enqueue( dNext );
706         }
707       }      
708     }
709   }
710
711   protected ReachGraph analyzeMethod( Descriptor d ) 
712     throws java.io.IOException {
713
714     // get the flat code for this descriptor
715     FlatMethod fm;
716     if( d == mdAnalysisEntry ) {
717       fm = fmAnalysisEntry;
718     } else {
719       fm = state.getMethodFlat( d );
720     }
721     pm.analyzeMethod( fm );
722
723     // intraprocedural work set
724     Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
725     flatNodesToVisit.add( fm );
726
727     Set<FlatNode> debugVisited = new HashSet<FlatNode>();
728     
729     // mapping of current partial results
730     Hashtable<FlatNode, ReachGraph> mapFlatNodeToReachGraph =
731       new Hashtable<FlatNode, ReachGraph>();
732
733     // the set of return nodes partial results that will be combined as
734     // the final, conservative approximation of the entire method
735     HashSet<FlatReturnNode> setReturns = new HashSet<FlatReturnNode>();
736
737     while( !flatNodesToVisit.isEmpty() ) {
738       FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next();
739       flatNodesToVisit.remove( fn );
740
741       debugVisited.add( fn );
742
743       // effect transfer function defined by this node,
744       // then compare it to the old graph at this node
745       // to see if anything was updated.
746
747       ReachGraph rg = new ReachGraph();
748       TaskDescriptor taskDesc;
749       if(fn instanceof FlatMethod && (taskDesc=((FlatMethod)fn).getTask())!=null){
750           if(mapDescriptorToReachGraph.containsKey(taskDesc)){
751                   // retrieve existing reach graph if it is not first time
752                   rg=mapDescriptorToReachGraph.get(taskDesc);
753           }else{
754                   // create initial reach graph for a task
755                   rg=createInitialTaskReachGraph((FlatMethod)fn);
756                   rg.globalSweep();
757                   mapDescriptorToReachGraph.put(taskDesc, rg);
758           }
759       }
760
761       // start by merging all node's parents' graphs
762       for( int i = 0; i < pm.numPrev(fn); ++i ) {
763         FlatNode pn = pm.getPrev(fn,i);
764         if( mapFlatNodeToReachGraph.containsKey( pn ) ) {
765           ReachGraph rgParent = mapFlatNodeToReachGraph.get( pn );
766           rg.merge( rgParent );
767         }
768       }
769
770
771       if( takeDebugSnapshots && 
772           d.getSymbol().equals( descSymbolDebug ) 
773           ) {
774         debugSnapshot( rg, fn, true );
775       }
776
777
778       // modify rg with appropriate transfer function
779       rg = analyzeFlatNode( d, fm, fn, setReturns, rg );
780
781
782       if( takeDebugSnapshots && 
783           d.getSymbol().equals( descSymbolDebug ) 
784           ) {
785         debugSnapshot( rg, fn, false );
786         ++snapNodeCounter;
787       }
788           
789
790       // if the results of the new graph are different from
791       // the current graph at this node, replace the graph
792       // with the update and enqueue the children
793       ReachGraph rgPrev = mapFlatNodeToReachGraph.get( fn );
794       if( !rg.equals( rgPrev ) ) {
795         mapFlatNodeToReachGraph.put( fn, rg );
796
797         for( int i = 0; i < pm.numNext(fn); i++ ) {
798           FlatNode nn = pm.getNext(fn, i);
799           flatNodesToVisit.add( nn );
800         }
801       }
802     }
803
804
805     // assert that the fixed-point results for each
806     // node in the method is no smaller than the last
807     // time this method was analyzed (monotonicity)
808     /*
809     Iterator<FlatNode> nItr = fm.getNodeSet().iterator();
810     while( nItr.hasNext() ) {
811       FlatNode   fn     = nItr.next();      
812       ReachGraph last   = fn2rg.get( fn );
813       ReachGraph newest = mapFlatNodeToReachGraph.get( fn );
814
815       if( newest == null ) {
816         System.out.println( "**********\nfn null result: "+fn+
817                             "\nnum visited="+debugVisited.size()+", num in set="+fm.getNodeSet().size()+
818                             "\nvisited:"+debugVisited );
819       }
820
821       assert newest != null;
822       if( last != null ) {
823         if( !ReachGraph.isNoSmallerThan( last, newest ) ) {
824           last.writeGraph( "last", true, false, false, true, true );
825           newest.writeGraph( "newest", true, false, false, true, true );
826           throw new Error( "transfer func for "+fn+" was not monotic" );
827         }
828       }
829       fn2rg.put( fn, newest );
830     }
831     */
832
833     // end by merging all return nodes into a complete
834     // reach graph that represents all possible heap
835     // states after the flat method returns
836     ReachGraph completeGraph = new ReachGraph();
837
838     assert !setReturns.isEmpty();
839     Iterator retItr = setReturns.iterator();
840     while( retItr.hasNext() ) {
841       FlatReturnNode frn = (FlatReturnNode) retItr.next();
842
843       assert mapFlatNodeToReachGraph.containsKey( frn );
844       ReachGraph rgRet = mapFlatNodeToReachGraph.get( frn );
845
846       completeGraph.merge( rgRet );
847     }
848
849
850     if( takeDebugSnapshots && 
851         d.getSymbol().equals( descSymbolDebug ) 
852         ) {
853       // increment that we've visited the debug snap
854       // method, and reset the node counter
855       System.out.println( "    @@@ debug snap at visit "+snapVisitCounter );
856       ++snapVisitCounter;
857       snapNodeCounter = 0;
858
859       if( snapVisitCounter == visitStartCapture + numVisitsToCapture && 
860           stopAfterCapture 
861           ) {
862         System.out.println( "!!! Stopping analysis after debug snap captures. !!!" );
863         System.exit( 0 );
864       }
865     }
866
867
868     return completeGraph;
869   }
870
871   
872   protected ReachGraph
873     analyzeFlatNode( Descriptor              d,
874                      FlatMethod              fmContaining,
875                      FlatNode                fn,
876                      HashSet<FlatReturnNode> setRetNodes,
877                      ReachGraph              rg
878                      ) throws java.io.IOException {
879
880     
881     // any variables that are no longer live should be
882     // nullified in the graph to reduce edges
883     //rg.nullifyDeadVars( liveness.getLiveInTemps( fmContaining, fn ) );
884
885           
886     TempDescriptor  lhs;
887     TempDescriptor  rhs;
888     FieldDescriptor fld;
889
890     // use node type to decide what transfer function
891     // to apply to the reachability graph
892     switch( fn.kind() ) {
893
894     case FKind.FlatMethod: {
895       // construct this method's initial heap model (IHM)
896       // since we're working on the FlatMethod, we know
897       // the incoming ReachGraph 'rg' is empty
898
899       Hashtable<FlatCall, ReachGraph> heapsFromCallers = 
900         getIHMcontributions( d );
901
902       Set entrySet = heapsFromCallers.entrySet();
903       Iterator itr = entrySet.iterator();
904       while( itr.hasNext() ) {
905         Map.Entry  me        = (Map.Entry)  itr.next();
906         FlatCall   fc        = (FlatCall)   me.getKey();
907         ReachGraph rgContrib = (ReachGraph) me.getValue();
908
909         assert fc.getMethod().equals( d );
910
911         rg.merge( rgContrib );
912       }
913
914       // additionally, we are enforcing STRICT MONOTONICITY for the
915       // method's initial context, so grow the context by whatever
916       // the previously computed context was, and put the most
917       // up-to-date context back in the map
918       ReachGraph rgPrevContext = mapDescriptorToInitialContext.get( d );
919       rg.merge( rgPrevContext );      
920       mapDescriptorToInitialContext.put( d, rg );
921
922     } break;
923       
924     case FKind.FlatOpNode:
925       FlatOpNode fon = (FlatOpNode) fn;
926       if( fon.getOp().getOp() == Operation.ASSIGN ) {
927         lhs = fon.getDest();
928         rhs = fon.getLeft();
929         rg.assignTempXEqualToTempY( lhs, rhs );
930       }
931       break;
932
933     case FKind.FlatCastNode:
934       FlatCastNode fcn = (FlatCastNode) fn;
935       lhs = fcn.getDst();
936       rhs = fcn.getSrc();
937
938       TypeDescriptor td = fcn.getType();
939       assert td != null;
940       
941       rg.assignTempXEqualToCastedTempY( lhs, rhs, td );
942       break;
943
944     case FKind.FlatFieldNode:
945       FlatFieldNode ffn = (FlatFieldNode) fn;
946       lhs = ffn.getDst();
947       rhs = ffn.getSrc();
948       fld = ffn.getField();
949       if( shouldAnalysisTrack( fld.getType() ) ) {
950         rg.assignTempXEqualToTempYFieldF( lhs, rhs, fld );
951       }          
952       break;
953
954     case FKind.FlatSetFieldNode:
955       FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
956       lhs = fsfn.getDst();
957       fld = fsfn.getField();
958       rhs = fsfn.getSrc();
959       if( shouldAnalysisTrack( fld.getType() ) ) {
960         rg.assignTempXFieldFEqualToTempY( lhs, fld, rhs );
961       }           
962       break;
963
964     case FKind.FlatElementNode:
965       FlatElementNode fen = (FlatElementNode) fn;
966       lhs = fen.getDst();
967       rhs = fen.getSrc();
968       if( shouldAnalysisTrack( lhs.getType() ) ) {
969
970         assert rhs.getType() != null;
971         assert rhs.getType().isArray();
972         
973         TypeDescriptor  tdElement = rhs.getType().dereference();
974         FieldDescriptor fdElement = getArrayField( tdElement );
975   
976         rg.assignTempXEqualToTempYFieldF( lhs, rhs, fdElement );
977       }
978       break;
979
980     case FKind.FlatSetElementNode:
981       FlatSetElementNode fsen = (FlatSetElementNode) fn;
982
983       if( arrayReferencees.doesNotCreateNewReaching( fsen ) ) {
984         // skip this node if it cannot create new reachability paths
985         break;
986       }
987
988       lhs = fsen.getDst();
989       rhs = fsen.getSrc();
990       if( shouldAnalysisTrack( rhs.getType() ) ) {
991
992         assert lhs.getType() != null;
993         assert lhs.getType().isArray();
994         
995         TypeDescriptor  tdElement = lhs.getType().dereference();
996         FieldDescriptor fdElement = getArrayField( tdElement );
997
998         rg.assignTempXFieldFEqualToTempY( lhs, fdElement, rhs );
999       }
1000       break;
1001       
1002     case FKind.FlatNew:
1003       FlatNew fnn = (FlatNew) fn;
1004       lhs = fnn.getDst();
1005       if( shouldAnalysisTrack( lhs.getType() ) ) {
1006         AllocSite as = getAllocSiteFromFlatNewPRIVATE( fnn );   
1007         rg.assignTempEqualToNewAlloc( lhs, as );
1008       }
1009       break;
1010
1011     case FKind.FlatCall: {
1012       //TODO: temporal fix for task descriptor case
1013       //MethodDescriptor mdCaller = fmContaining.getMethod();
1014       Descriptor mdCaller;
1015       if(fmContaining.getMethod()!=null){
1016           mdCaller  = fmContaining.getMethod();
1017       }else{
1018           mdCaller = fmContaining.getTask();
1019       }      
1020       FlatCall         fc       = (FlatCall) fn;
1021       MethodDescriptor mdCallee = fc.getMethod();
1022       FlatMethod       fmCallee = state.getMethodFlat( mdCallee );
1023
1024       boolean writeDebugDOTs = 
1025         mdCaller.getSymbol().equals( state.DISJOINTDEBUGCALLER ) &&
1026         mdCallee.getSymbol().equals( state.DISJOINTDEBUGCALLEE );      
1027
1028
1029       // calculate the heap this call site can reach--note this is
1030       // not used for the current call site transform, we are
1031       // grabbing this heap model for future analysis of the callees,
1032       // so if different results emerge we will return to this site
1033       ReachGraph heapForThisCall_old = 
1034         getIHMcontribution( mdCallee, fc );
1035
1036       // the computation of the callee-reachable heap
1037       // is useful for making the callee starting point
1038       // and for applying the call site transfer function
1039       Set<Integer> callerNodeIDsCopiedToCallee = 
1040         new HashSet<Integer>();
1041
1042       ReachGraph heapForThisCall_cur = 
1043         rg.makeCalleeView( fc, 
1044                            fmCallee,
1045                            callerNodeIDsCopiedToCallee,
1046                            writeDebugDOTs
1047                            );
1048
1049       if( !heapForThisCall_cur.equals( heapForThisCall_old ) ) {        
1050         // if heap at call site changed, update the contribution,
1051         // and reschedule the callee for analysis
1052         addIHMcontribution( mdCallee, fc, heapForThisCall_cur );        
1053         enqueue( mdCallee );
1054       }
1055
1056
1057
1058
1059       // the transformation for a call site should update the
1060       // current heap abstraction with any effects from the callee,
1061       // or if the method is virtual, the effects from any possible
1062       // callees, so find the set of callees...
1063       Set<MethodDescriptor> setPossibleCallees =
1064         new HashSet<MethodDescriptor>();
1065
1066       if( mdCallee.isStatic() ) {        
1067         setPossibleCallees.add( mdCallee );
1068       } else {
1069         TypeDescriptor typeDesc = fc.getThis().getType();
1070         setPossibleCallees.addAll( callGraph.getMethods( mdCallee, 
1071                                                          typeDesc )
1072                                    );
1073       }
1074
1075       ReachGraph rgMergeOfEffects = new ReachGraph();
1076
1077       Iterator<MethodDescriptor> mdItr = setPossibleCallees.iterator();
1078       while( mdItr.hasNext() ) {
1079         MethodDescriptor mdPossible = mdItr.next();
1080         FlatMethod       fmPossible = state.getMethodFlat( mdPossible );
1081
1082         addDependent( mdPossible, // callee
1083                       d );        // caller
1084
1085         // don't alter the working graph (rg) until we compute a 
1086         // result for every possible callee, merge them all together,
1087         // then set rg to that
1088         ReachGraph rgCopy = new ReachGraph();
1089         rgCopy.merge( rg );             
1090                 
1091         ReachGraph rgEffect = getPartial( mdPossible );
1092
1093         if( rgEffect == null ) {
1094           // if this method has never been analyzed just schedule it 
1095           // for analysis and skip over this call site for now
1096           enqueue( mdPossible );
1097         } else {
1098           rgCopy.resolveMethodCall( fc, 
1099                                     fmPossible, 
1100                                     rgEffect,
1101                                     callerNodeIDsCopiedToCallee,
1102                                     writeDebugDOTs
1103                                     );
1104         }
1105         
1106         rgMergeOfEffects.merge( rgCopy );
1107       }
1108
1109
1110       // now that we've taken care of building heap models for
1111       // callee analysis, finish this transformation
1112       rg = rgMergeOfEffects;
1113     } break;
1114       
1115
1116     case FKind.FlatReturnNode:
1117       FlatReturnNode frn = (FlatReturnNode) fn;
1118       rhs = frn.getReturnTemp();
1119       if( rhs != null && shouldAnalysisTrack( rhs.getType() ) ) {
1120         rg.assignReturnEqualToTemp( rhs );
1121       }
1122       setRetNodes.add( frn );
1123       break;
1124
1125     } // end switch
1126
1127     
1128     // dead variables were removed before the above transfer function
1129     // was applied, so eliminate heap regions and edges that are no
1130     // longer part of the abstractly-live heap graph, and sweep up
1131     // and reachability effects that are altered by the reduction
1132     //rg.abstractGarbageCollect();
1133     //rg.globalSweep();
1134
1135
1136     // back edges are strictly monotonic
1137     if( pm.isBackEdge( fn ) ) {
1138       ReachGraph rgPrevResult = mapBackEdgeToMonotone.get( fn );
1139       rg.merge( rgPrevResult );
1140       mapBackEdgeToMonotone.put( fn, rg );
1141     }
1142     
1143     // at this point rg should be the correct update
1144     // by an above transfer function, or untouched if
1145     // the flat node type doesn't affect the heap
1146     return rg;
1147   }
1148
1149
1150   
1151   // this method should generate integers strictly greater than zero!
1152   // special "shadow" regions are made from a heap region by negating
1153   // the ID
1154   static public Integer generateUniqueHeapRegionNodeID() {
1155     ++uniqueIDcount;
1156     return new Integer( uniqueIDcount );
1157   }
1158
1159
1160   
1161   static public FieldDescriptor getArrayField( TypeDescriptor tdElement ) {
1162     FieldDescriptor fdElement = mapTypeToArrayField.get( tdElement );
1163     if( fdElement == null ) {
1164       fdElement = new FieldDescriptor( new Modifiers( Modifiers.PUBLIC ),
1165                                        tdElement,
1166                                        arrayElementFieldName,
1167                                        null,
1168                                        false );
1169       mapTypeToArrayField.put( tdElement, fdElement );
1170     }
1171     return fdElement;
1172   }
1173
1174   
1175   
1176   private void writeFinalGraphs() {
1177     Set entrySet = mapDescriptorToCompleteReachGraph.entrySet();
1178     Iterator itr = entrySet.iterator();
1179     while( itr.hasNext() ) {
1180       Map.Entry  me = (Map.Entry)  itr.next();
1181       Descriptor  d = (Descriptor) me.getKey();
1182       ReachGraph rg = (ReachGraph) me.getValue();
1183
1184       rg.writeGraph( "COMPLETE"+d,
1185                      true,   // write labels (variables)                
1186                      true,   // selectively hide intermediate temp vars 
1187                      true,   // prune unreachable heap regions          
1188                      false,  // hide subset reachability states         
1189                      true ); // hide edge taints                        
1190     }
1191   }
1192
1193   private void writeFinalIHMs() {
1194     Iterator d2IHMsItr = mapDescriptorToIHMcontributions.entrySet().iterator();
1195     while( d2IHMsItr.hasNext() ) {
1196       Map.Entry                        me1 = (Map.Entry)                       d2IHMsItr.next();
1197       Descriptor                         d = (Descriptor)                      me1.getKey();
1198       Hashtable<FlatCall, ReachGraph> IHMs = (Hashtable<FlatCall, ReachGraph>) me1.getValue();
1199
1200       Iterator fc2rgItr = IHMs.entrySet().iterator();
1201       while( fc2rgItr.hasNext() ) {
1202         Map.Entry  me2 = (Map.Entry)  fc2rgItr.next();
1203         FlatCall   fc  = (FlatCall)   me2.getKey();
1204         ReachGraph rg  = (ReachGraph) me2.getValue();
1205                 
1206         rg.writeGraph( "IHMPARTFOR"+d+"FROM"+fc,
1207                        true,   // write labels (variables)
1208                        true,   // selectively hide intermediate temp vars
1209                        true,   // prune unreachable heap regions
1210                        false,  // hide subset reachability states
1211                        true ); // hide edge taints
1212       }
1213     }
1214   }
1215    
1216
1217   protected ReachGraph getPartial( Descriptor d ) {
1218     return mapDescriptorToCompleteReachGraph.get( d );
1219   }
1220
1221   protected void setPartial( Descriptor d, ReachGraph rg ) {
1222     mapDescriptorToCompleteReachGraph.put( d, rg );
1223
1224     // when the flag for writing out every partial
1225     // result is set, we should spit out the graph,
1226     // but in order to give it a unique name we need
1227     // to track how many partial results for this
1228     // descriptor we've already written out
1229     if( writeAllIncrementalDOTs ) {
1230       if( !mapDescriptorToNumUpdates.containsKey( d ) ) {
1231         mapDescriptorToNumUpdates.put( d, new Integer( 0 ) );
1232       }
1233       Integer n = mapDescriptorToNumUpdates.get( d );
1234       
1235       rg.writeGraph( d+"COMPLETE"+String.format( "%05d", n ),
1236                      true,   // write labels (variables)
1237                      true,   // selectively hide intermediate temp vars
1238                      true,   // prune unreachable heap regions
1239                      false,  // hide subset reachability states
1240                      true ); // hide edge taints
1241       
1242       mapDescriptorToNumUpdates.put( d, n + 1 );
1243     }
1244   }
1245
1246
1247
1248   // return just the allocation site associated with one FlatNew node
1249   protected AllocSite getAllocSiteFromFlatNewPRIVATE( FlatNew fnew ) {
1250
1251     if( !mapFlatNewToAllocSite.containsKey( fnew ) ) {
1252       AllocSite as = 
1253         (AllocSite) Canonical.makeCanonical( new AllocSite( allocationDepth, 
1254                                                             fnew, 
1255                                                             fnew.getDisjointId() 
1256                                                             )
1257                                              );
1258
1259       // the newest nodes are single objects
1260       for( int i = 0; i < allocationDepth; ++i ) {
1261         Integer id = generateUniqueHeapRegionNodeID();
1262         as.setIthOldest( i, id );
1263         mapHrnIdToAllocSite.put( id, as );
1264       }
1265
1266       // the oldest node is a summary node
1267       as.setSummary( generateUniqueHeapRegionNodeID() );
1268
1269       mapFlatNewToAllocSite.put( fnew, as );
1270     }
1271
1272     return mapFlatNewToAllocSite.get( fnew );
1273   }
1274
1275
1276   public static boolean shouldAnalysisTrack( TypeDescriptor type ) {
1277     // don't track primitive types, but an array
1278     // of primitives is heap memory
1279     if( type.isImmutable() ) {
1280       return type.isArray();
1281     }
1282
1283     // everything else is an object
1284     return true;
1285   }
1286
1287   protected int numMethodsAnalyzed() {    
1288     return descriptorsToAnalyze.size();
1289   }
1290   
1291
1292   
1293   
1294   
1295   // Take in source entry which is the program's compiled entry and
1296   // create a new analysis entry, a method that takes no parameters
1297   // and appears to allocate the command line arguments and call the
1298   // source entry with them.  The purpose of this analysis entry is
1299   // to provide a top-level method context with no parameters left.
1300   protected void makeAnalysisEntryMethod( MethodDescriptor mdSourceEntry ) {
1301
1302     Modifiers mods = new Modifiers();
1303     mods.addModifier( Modifiers.PUBLIC );
1304     mods.addModifier( Modifiers.STATIC );
1305
1306     TypeDescriptor returnType = 
1307       new TypeDescriptor( TypeDescriptor.VOID );
1308
1309     this.mdAnalysisEntry = 
1310       new MethodDescriptor( mods,
1311                             returnType,
1312                             "analysisEntryMethod"
1313                             );
1314
1315     TempDescriptor cmdLineArgs = 
1316       new TempDescriptor( "args",
1317                           mdSourceEntry.getParamType( 0 )
1318                           );
1319
1320     FlatNew fn = 
1321       new FlatNew( mdSourceEntry.getParamType( 0 ),
1322                    cmdLineArgs,
1323                    false // is global 
1324                    );
1325     
1326     TempDescriptor[] sourceEntryArgs = new TempDescriptor[1];
1327     sourceEntryArgs[0] = cmdLineArgs;
1328     
1329     FlatCall fc = 
1330       new FlatCall( mdSourceEntry,
1331                     null, // dst temp
1332                     null, // this temp
1333                     sourceEntryArgs
1334                     );
1335
1336     FlatReturnNode frn = new FlatReturnNode( null );
1337
1338     FlatExit fe = new FlatExit();
1339
1340     this.fmAnalysisEntry = 
1341       new FlatMethod( mdAnalysisEntry, 
1342                       fe
1343                       );
1344
1345     this.fmAnalysisEntry.addNext( fn );
1346     fn.addNext( fc );
1347     fc.addNext( frn );
1348     frn.addNext( fe );
1349   }
1350
1351
1352   protected LinkedList<Descriptor> topologicalSort( Set<Descriptor> toSort ) {
1353
1354     Set       <Descriptor> discovered = new HashSet   <Descriptor>();
1355     LinkedList<Descriptor> sorted     = new LinkedList<Descriptor>();
1356   
1357     Iterator<Descriptor> itr = toSort.iterator();
1358     while( itr.hasNext() ) {
1359       Descriptor d = itr.next();
1360           
1361       if( !discovered.contains( d ) ) {
1362         dfsVisit( d, toSort, sorted, discovered );
1363       }
1364     }
1365     
1366     return sorted;
1367   }
1368   
1369   // While we're doing DFS on call graph, remember
1370   // dependencies for efficient queuing of methods
1371   // during interprocedural analysis:
1372   //
1373   // a dependent of a method decriptor d for this analysis is:
1374   //  1) a method or task that invokes d
1375   //  2) in the descriptorsToAnalyze set
1376   protected void dfsVisit( Descriptor             d,
1377                            Set       <Descriptor> toSort,                        
1378                            LinkedList<Descriptor> sorted,
1379                            Set       <Descriptor> discovered ) {
1380     discovered.add( d );
1381     
1382     // only methods have callers, tasks never do
1383     if( d instanceof MethodDescriptor ) {
1384
1385       MethodDescriptor md = (MethodDescriptor) d;
1386
1387       // the call graph is not aware that we have a fabricated
1388       // analysis entry that calls the program source's entry
1389       if( md == mdSourceEntry ) {
1390         if( !discovered.contains( mdAnalysisEntry ) ) {
1391           addDependent( mdSourceEntry,  // callee
1392                         mdAnalysisEntry // caller
1393                         );
1394           dfsVisit( mdAnalysisEntry, toSort, sorted, discovered );
1395         }
1396       }
1397
1398       // otherwise call graph guides DFS
1399       Iterator itr = callGraph.getCallerSet( md ).iterator();
1400       while( itr.hasNext() ) {
1401         Descriptor dCaller = (Descriptor) itr.next();
1402         
1403         // only consider callers in the original set to analyze
1404         if( !toSort.contains( dCaller ) ) {
1405           continue;
1406         }
1407           
1408         if( !discovered.contains( dCaller ) ) {
1409           addDependent( md,     // callee
1410                         dCaller // caller
1411                         );
1412
1413           dfsVisit( dCaller, toSort, sorted, discovered );
1414         }
1415       }
1416     }
1417     
1418     // for leaf-nodes last now!
1419     sorted.addLast( d );
1420   }
1421
1422
1423   protected void enqueue( Descriptor d ) {
1424     if( !descriptorsToVisitSet.contains( d ) ) {
1425       Integer priority = mapDescriptorToPriority.get( d );
1426
1427       if( state.DISJOINTDVISITSTACK ) {
1428         descriptorsToVisitStack.add( new DescriptorQWrapper( priority, 
1429                                                              d ) 
1430                                      );
1431
1432       } else if( state.DISJOINTDVISITPQUE ) {
1433         descriptorsToVisitQ.add( new DescriptorQWrapper( priority, 
1434                                                          d ) 
1435                                  );
1436       }
1437
1438       descriptorsToVisitSet.add( d );
1439     }
1440   }
1441
1442
1443   // a dependent of a method decriptor d for this analysis is:
1444   //  1) a method or task that invokes d
1445   //  2) in the descriptorsToAnalyze set
1446   protected void addDependent( Descriptor callee, Descriptor caller ) {
1447     Set<Descriptor> deps = mapDescriptorToSetDependents.get( callee );
1448     if( deps == null ) {
1449       deps = new HashSet<Descriptor>();
1450     }
1451     deps.add( caller );
1452     mapDescriptorToSetDependents.put( callee, deps );
1453   }
1454   
1455   protected Set<Descriptor> getDependents( Descriptor callee ) {
1456     Set<Descriptor> deps = mapDescriptorToSetDependents.get( callee );
1457     if( deps == null ) {
1458       deps = new HashSet<Descriptor>();
1459       mapDescriptorToSetDependents.put( callee, deps );
1460     }
1461     return deps;
1462   }
1463
1464   
1465   public Hashtable<FlatCall, ReachGraph> getIHMcontributions( Descriptor d ) {
1466
1467     Hashtable<FlatCall, ReachGraph> heapsFromCallers = 
1468       mapDescriptorToIHMcontributions.get( d );
1469     
1470     if( heapsFromCallers == null ) {
1471       heapsFromCallers = new Hashtable<FlatCall, ReachGraph>();
1472       mapDescriptorToIHMcontributions.put( d, heapsFromCallers );
1473     }
1474     
1475     return heapsFromCallers;
1476   }
1477
1478   public ReachGraph getIHMcontribution( Descriptor d, 
1479                                         FlatCall   fc
1480                                         ) {
1481     Hashtable<FlatCall, ReachGraph> heapsFromCallers = 
1482       getIHMcontributions( d );
1483
1484     if( !heapsFromCallers.containsKey( fc ) ) {
1485       heapsFromCallers.put( fc, new ReachGraph() );
1486     }
1487
1488     return heapsFromCallers.get( fc );
1489   }
1490
1491   public void addIHMcontribution( Descriptor d,
1492                                   FlatCall   fc,
1493                                   ReachGraph rg
1494                                   ) {
1495     Hashtable<FlatCall, ReachGraph> heapsFromCallers = 
1496       getIHMcontributions( d );
1497
1498     heapsFromCallers.put( fc, rg );
1499   }
1500
1501 private AllocSite createParameterAllocSite(ReachGraph rg, TempDescriptor tempDesc) {
1502     
1503     // create temp descriptor for each parameter variable
1504     FlatNew flatNew = new FlatNew(tempDesc.getType(), tempDesc, false);
1505     // create allocation site
1506     AllocSite as = (AllocSite) Canonical.makeCanonical(new AllocSite( allocationDepth, flatNew, flatNew.getDisjointId()));
1507     for (int i = 0; i < allocationDepth; ++i) {
1508         Integer id = generateUniqueHeapRegionNodeID();
1509         as.setIthOldest(i, id);
1510         mapHrnIdToAllocSite.put(id, as);
1511     }
1512     // the oldest node is a summary node
1513     as.setSummary( generateUniqueHeapRegionNodeID() );
1514     
1515     rg.age(as);
1516     
1517     return as;
1518     
1519 }
1520
1521 private Set<FieldDescriptor> getFieldSetTobeAnalyzed(TypeDescriptor typeDesc){
1522         
1523         Set<FieldDescriptor> fieldSet=new HashSet<FieldDescriptor>();
1524     if(!typeDesc.isImmutable()){
1525             ClassDescriptor classDesc = typeDesc.getClassDesc();                    
1526             for (Iterator it = classDesc.getFields(); it.hasNext();) {
1527                     FieldDescriptor field = (FieldDescriptor) it.next();
1528                     TypeDescriptor fieldType = field.getType();
1529                     if (shouldAnalysisTrack( fieldType )) {
1530                         fieldSet.add(field);                    
1531                     }
1532             }
1533     }
1534     return fieldSet;
1535         
1536 }
1537
1538   private HeapRegionNode createMultiDeimensionalArrayHRN(ReachGraph rg, AllocSite alloc, HeapRegionNode srcHRN, FieldDescriptor fd, Hashtable<HeapRegionNode, HeapRegionNode> map, Hashtable<TypeDescriptor, HeapRegionNode> mapToExistingNode, ReachSet alpha ){
1539
1540         int dimCount=fd.getType().getArrayCount();
1541         HeapRegionNode prevNode=null;
1542         HeapRegionNode arrayEntryNode=null;
1543         for(int i=dimCount;i>0;i--){
1544                 TypeDescriptor typeDesc=fd.getType().dereference();//hack to get instance of type desc
1545                 typeDesc.setArrayCount(i);
1546                 TempDescriptor tempDesc=new TempDescriptor(typeDesc.getSymbol(),typeDesc);
1547                 HeapRegionNode hrnSummary ;
1548                 if(!mapToExistingNode.containsKey(typeDesc)){
1549                         AllocSite as;
1550                         if(i==dimCount){
1551                                 as = alloc;
1552                         }else{
1553                                 as = createParameterAllocSite(rg, tempDesc);
1554                         }
1555                         // make a new reference to allocated node
1556                     hrnSummary = 
1557                                 rg.createNewHeapRegionNode(as.getSummary(), // id or null to generate a new one
1558                                                            false, // single object?
1559                                                            true, // summary?
1560                                                            false, // flagged?
1561                                                            false, // out-of-context?
1562                                                            as.getType(), // type
1563                                                            as, // allocation site
1564                                                            alpha, // inherent reach
1565                                                            alpha, // current reach
1566                                                            ExistPredSet.factory(rg.predTrue), // predicates
1567                                                            tempDesc.toString() // description
1568                                                            );
1569                     rg.id2hrn.put(as.getSummary(),hrnSummary);
1570                     
1571                     mapToExistingNode.put(typeDesc, hrnSummary);
1572                 }else{
1573                         hrnSummary=mapToExistingNode.get(typeDesc);
1574                 }
1575             
1576             if(prevNode==null){
1577                     // make a new reference between new summary node and source
1578                     RefEdge edgeToSummary = new RefEdge(srcHRN, // source
1579                                                         hrnSummary, // dest
1580                                                         typeDesc, // type
1581                                                         fd.getSymbol(), // field name
1582                                                         alpha, // beta
1583                                                         ExistPredSet.factory(rg.predTrue) // predicates
1584                                                         );
1585                     
1586                     rg.addRefEdge(srcHRN, hrnSummary, edgeToSummary);
1587                     prevNode=hrnSummary;
1588                     arrayEntryNode=hrnSummary;
1589             }else{
1590                     // make a new reference between summary nodes of array
1591                     RefEdge edgeToSummary = new RefEdge(prevNode, // source
1592                                                         hrnSummary, // dest
1593                                                         typeDesc, // type
1594                                                         arrayElementFieldName, // field name
1595                                                         alpha, // beta
1596                                                         ExistPredSet.factory(rg.predTrue) // predicates
1597                                                         );
1598                     
1599                     rg.addRefEdge(prevNode, hrnSummary, edgeToSummary);
1600                     prevNode=hrnSummary;
1601             }
1602             
1603         }
1604         
1605         // create a new obj node if obj has at least one non-primitive field
1606         TypeDescriptor type=fd.getType();
1607     if(getFieldSetTobeAnalyzed(type).size()>0){
1608         TypeDescriptor typeDesc=type.dereference();
1609         typeDesc.setArrayCount(0);
1610         if(!mapToExistingNode.containsKey(typeDesc)){
1611                 TempDescriptor tempDesc=new TempDescriptor(type.getSymbol(),typeDesc);
1612                 AllocSite as = createParameterAllocSite(rg, tempDesc);
1613                 // make a new reference to allocated node
1614                     HeapRegionNode hrnSummary = 
1615                                 rg.createNewHeapRegionNode(as.getSummary(), // id or null to generate a new one
1616                                                            false, // single object?
1617                                                            true, // summary?
1618                                                            false, // flagged?
1619                                                            false, // out-of-context?
1620                                                            typeDesc, // type
1621                                                            as, // allocation site
1622                                                            alpha, // inherent reach
1623                                                            alpha, // current reach
1624                                                            ExistPredSet.factory(rg.predTrue), // predicates
1625                                                            tempDesc.toString() // description
1626                                                            );
1627                     rg.id2hrn.put(as.getSummary(),hrnSummary);
1628                     mapToExistingNode.put(typeDesc, hrnSummary);
1629                     RefEdge edgeToSummary = new RefEdge(prevNode, // source
1630                                         hrnSummary, // dest
1631                                         typeDesc, // type
1632                                         arrayElementFieldName, // field name
1633                                         alpha, // beta
1634                                         ExistPredSet.factory(rg.predTrue) // predicates
1635                                         );
1636                     rg.addRefEdge(prevNode, hrnSummary, edgeToSummary);
1637                     prevNode=hrnSummary;
1638         }else{
1639                 HeapRegionNode hrnSummary=mapToExistingNode.get(typeDesc);
1640                 if(prevNode.getReferenceTo(hrnSummary, typeDesc, arrayElementFieldName)==null){
1641                         RefEdge edgeToSummary = new RefEdge(prevNode, // source
1642                                         hrnSummary, // dest
1643                                         typeDesc, // type
1644                                         arrayElementFieldName, // field name
1645                                         alpha, // beta
1646                                         ExistPredSet.factory(rg.predTrue) // predicates
1647                                         );
1648                     rg.addRefEdge(prevNode, hrnSummary, edgeToSummary);
1649                 }
1650                  prevNode=hrnSummary;
1651         }
1652     }
1653         
1654         map.put(arrayEntryNode, prevNode);
1655         return arrayEntryNode;
1656 }
1657
1658 private ReachGraph createInitialTaskReachGraph(FlatMethod fm) {
1659     ReachGraph rg = new ReachGraph();
1660     TaskDescriptor taskDesc = fm.getTask();
1661     
1662     for (int idx = 0; idx < taskDesc.numParameters(); idx++) {
1663         Descriptor paramDesc = taskDesc.getParameter(idx);
1664         TypeDescriptor paramTypeDesc = taskDesc.getParamType(idx);
1665         
1666         // setup data structure
1667         Set<HashMap<HeapRegionNode, FieldDescriptor>> workSet = 
1668             new HashSet<HashMap<HeapRegionNode, FieldDescriptor>>();
1669         Hashtable<TypeDescriptor, HeapRegionNode> mapTypeToExistingSummaryNode = 
1670             new Hashtable<TypeDescriptor, HeapRegionNode>();
1671         Hashtable<HeapRegionNode, HeapRegionNode> mapToFirstDimensionArrayNode = 
1672             new Hashtable<HeapRegionNode, HeapRegionNode>();
1673         Set<String> doneSet = new HashSet<String>();
1674         
1675         TempDescriptor tempDesc = fm.getParameter(idx);
1676         
1677         AllocSite as = createParameterAllocSite(rg, tempDesc);
1678         VariableNode lnX = rg.getVariableNodeFromTemp(tempDesc);
1679         Integer idNewest = as.getIthOldest(0);
1680         HeapRegionNode hrnNewest = rg.id2hrn.get(idNewest);
1681
1682         // make a new reference to allocated node
1683         RefEdge edgeNew = new RefEdge(lnX, // source
1684                                       hrnNewest, // dest
1685                                       taskDesc.getParamType(idx), // type
1686                                       null, // field name
1687                                       hrnNewest.getAlpha(), // beta
1688                                       ExistPredSet.factory(rg.predTrue) // predicates
1689                                       );
1690         rg.addRefEdge(lnX, hrnNewest, edgeNew);
1691
1692         // set-up a work set for class field
1693         ClassDescriptor classDesc = paramTypeDesc.getClassDesc();
1694         for (Iterator it = classDesc.getFields(); it.hasNext();) {
1695             FieldDescriptor fd = (FieldDescriptor) it.next();
1696             TypeDescriptor fieldType = fd.getType();
1697             if (shouldAnalysisTrack( fieldType )) {
1698                 HashMap<HeapRegionNode, FieldDescriptor> newMap = new HashMap<HeapRegionNode, FieldDescriptor>();
1699                 newMap.put(hrnNewest, fd);
1700                 workSet.add(newMap);
1701             }
1702         }
1703         
1704         int uniqueIdentifier = 0;
1705         while (!workSet.isEmpty()) {
1706             HashMap<HeapRegionNode, FieldDescriptor> map = workSet
1707                 .iterator().next();
1708             workSet.remove(map);
1709             
1710             Set<HeapRegionNode> key = map.keySet();
1711             HeapRegionNode srcHRN = key.iterator().next();
1712             FieldDescriptor fd = map.get(srcHRN);
1713             TypeDescriptor type = fd.getType();
1714             String doneSetIdentifier = srcHRN.getIDString() + "_" + fd;
1715             
1716             if (!doneSet.contains(doneSetIdentifier)) {
1717                 doneSet.add(doneSetIdentifier);
1718                 if (!mapTypeToExistingSummaryNode.containsKey(type)) {
1719                     // create new summary Node
1720                     TempDescriptor td = new TempDescriptor("temp"
1721                                                            + uniqueIdentifier, type);
1722                     
1723                     AllocSite allocSite;
1724                     if(type.equals(paramTypeDesc)){
1725                     //corresponding allocsite has already been created for a parameter variable.
1726                         allocSite=as;
1727                     }else{
1728                         allocSite = createParameterAllocSite(rg, td);
1729                     }
1730                     String strDesc = allocSite.toStringForDOT()
1731                         + "\\nsummary";
1732                     TypeDescriptor allocType=allocSite.getType();
1733                     
1734                     HeapRegionNode      hrnSummary;
1735                     if(allocType.isArray() && allocType.getArrayCount()>0){
1736                       hrnSummary=createMultiDeimensionalArrayHRN(rg,allocSite,srcHRN,fd,mapToFirstDimensionArrayNode,mapTypeToExistingSummaryNode,hrnNewest.getAlpha());
1737                     }else{                  
1738                         hrnSummary = 
1739                                         rg.createNewHeapRegionNode(allocSite.getSummary(), // id or null to generate a new one
1740                                                                    false, // single object?
1741                                                                    true, // summary?
1742                                                                    false, // flagged?
1743                                                                    false, // out-of-context?
1744                                                                    allocSite.getType(), // type
1745                                                                    allocSite, // allocation site
1746                                                                    hrnNewest.getAlpha(), // inherent reach
1747                                                                    hrnNewest.getAlpha(), // current reach
1748                                                                    ExistPredSet.factory(rg.predTrue), // predicates
1749                                                                    strDesc // description
1750                                                                    );
1751                                     rg.id2hrn.put(allocSite.getSummary(),hrnSummary);
1752                     
1753                     // make a new reference to summary node
1754                     RefEdge edgeToSummary = new RefEdge(srcHRN, // source
1755                                                         hrnSummary, // dest
1756                                                         type, // type
1757                                                         fd.getSymbol(), // field name
1758                                                         hrnNewest.getAlpha(), // beta
1759                                                         ExistPredSet.factory(rg.predTrue) // predicates
1760                                                         );
1761                     
1762                     rg.addRefEdge(srcHRN, hrnSummary, edgeToSummary);
1763                     }               
1764                     uniqueIdentifier++;
1765                     
1766                     mapTypeToExistingSummaryNode.put(type, hrnSummary);
1767                     
1768                     // set-up a work set for  fields of the class
1769                     Set<FieldDescriptor> fieldTobeAnalyzed=getFieldSetTobeAnalyzed(type);
1770                     for (Iterator iterator = fieldTobeAnalyzed.iterator(); iterator
1771                                         .hasNext();) {
1772                                 FieldDescriptor fieldDescriptor = (FieldDescriptor) iterator
1773                                                 .next();
1774                                 HeapRegionNode newDstHRN;
1775                                 if(mapToFirstDimensionArrayNode.containsKey(hrnSummary)){
1776                                         //related heap region node is already exsited.
1777                                         newDstHRN=mapToFirstDimensionArrayNode.get(hrnSummary);
1778                                 }else{
1779                                         newDstHRN=hrnSummary;
1780                                 }
1781                                  doneSetIdentifier = newDstHRN.getIDString() + "_" + fieldDescriptor;                                                            
1782                                  if(!doneSet.contains(doneSetIdentifier)){
1783                                  // add new work item
1784                                          HashMap<HeapRegionNode, FieldDescriptor> newMap = 
1785                                             new HashMap<HeapRegionNode, FieldDescriptor>();
1786                                          newMap.put(newDstHRN, fieldDescriptor);
1787                                          workSet.add(newMap);
1788                                   }                             
1789                         }
1790                     
1791                 }else{
1792                     // if there exists corresponding summary node
1793                     HeapRegionNode hrnDst=mapTypeToExistingSummaryNode.get(type);
1794                     
1795                     RefEdge edgeToSummary = new RefEdge(srcHRN, // source
1796                                                         hrnDst, // dest
1797                                                         fd.getType(), // type
1798                                                         fd.getSymbol(), // field name
1799                                                         srcHRN.getAlpha(), // beta
1800                                                         ExistPredSet.factory(rg.predTrue) // predicates
1801                                                         );
1802                     rg.addRefEdge(srcHRN, hrnDst, edgeToSummary);
1803                     
1804                 }               
1805             }       
1806         }           
1807     }   
1808 //    debugSnapshot(rg, fm, true);
1809     return rg;
1810 }
1811
1812 // return all allocation sites in the method (there is one allocation
1813 // site per FlatNew node in a method)
1814 private HashSet<AllocSite> getAllocationSiteSet(Descriptor d) {
1815   if( !mapDescriptorToAllocSiteSet.containsKey(d) ) {
1816     buildAllocationSiteSet(d);
1817   }
1818
1819   return mapDescriptorToAllocSiteSet.get(d);
1820
1821 }
1822
1823 private void buildAllocationSiteSet(Descriptor d) {
1824     HashSet<AllocSite> s = new HashSet<AllocSite>();
1825
1826     FlatMethod fm;
1827     if( d instanceof MethodDescriptor ) {
1828       fm = state.getMethodFlat( (MethodDescriptor) d);
1829     } else {
1830       assert d instanceof TaskDescriptor;
1831       fm = state.getMethodFlat( (TaskDescriptor) d);
1832     }
1833     pm.analyzeMethod(fm);
1834
1835     // visit every node in this FlatMethod's IR graph
1836     // and make a set of the allocation sites from the
1837     // FlatNew node's visited
1838     HashSet<FlatNode> visited = new HashSet<FlatNode>();
1839     HashSet<FlatNode> toVisit = new HashSet<FlatNode>();
1840     toVisit.add(fm);
1841
1842     while( !toVisit.isEmpty() ) {
1843       FlatNode n = toVisit.iterator().next();
1844
1845       if( n instanceof FlatNew ) {
1846         s.add(getAllocSiteFromFlatNewPRIVATE( (FlatNew) n) );
1847       }
1848
1849       toVisit.remove(n);
1850       visited.add(n);
1851
1852       for( int i = 0; i < pm.numNext(n); ++i ) {
1853         FlatNode child = pm.getNext(n, i);
1854         if( !visited.contains(child) ) {
1855           toVisit.add(child);
1856         }
1857       }
1858     }
1859
1860     mapDescriptorToAllocSiteSet.put(d, s);
1861   }
1862
1863         private HashSet<AllocSite> getFlaggedAllocationSites(Descriptor dIn) {
1864
1865                 HashSet<AllocSite> out = new HashSet<AllocSite>();
1866                 HashSet<Descriptor> toVisit = new HashSet<Descriptor>();
1867                 HashSet<Descriptor> visited = new HashSet<Descriptor>();
1868
1869                 toVisit.add(dIn);
1870
1871                 while (!toVisit.isEmpty()) {
1872                         Descriptor d = toVisit.iterator().next();
1873                         toVisit.remove(d);
1874                         visited.add(d);
1875
1876                         HashSet<AllocSite> asSet = getAllocationSiteSet(d);
1877                         Iterator asItr = asSet.iterator();
1878                         while (asItr.hasNext()) {
1879                                 AllocSite as = (AllocSite) asItr.next();
1880                                 if (as.getDisjointAnalysisId() != null) {
1881                                         out.add(as);
1882                                 }
1883                         }
1884
1885                         // enqueue callees of this method to be searched for
1886                         // allocation sites also
1887                         Set callees = callGraph.getCalleeSet(d);
1888                         if (callees != null) {
1889                                 Iterator methItr = callees.iterator();
1890                                 while (methItr.hasNext()) {
1891                                         MethodDescriptor md = (MethodDescriptor) methItr.next();
1892
1893                                         if (!visited.contains(md)) {
1894                                                 toVisit.add(md);
1895                                         }
1896                                 }
1897                         }
1898                 }
1899
1900                 return out;
1901         }
1902  
1903     
1904 private HashSet<AllocSite>
1905 getFlaggedAllocationSitesReachableFromTaskPRIVATE(TaskDescriptor td) {
1906
1907   HashSet<AllocSite> asSetTotal = new HashSet<AllocSite>();
1908   HashSet<Descriptor>     toVisit    = new HashSet<Descriptor>();
1909   HashSet<Descriptor>     visited    = new HashSet<Descriptor>();
1910
1911   toVisit.add(td);
1912
1913   // traverse this task and all methods reachable from this task
1914   while( !toVisit.isEmpty() ) {
1915     Descriptor d = toVisit.iterator().next();
1916     toVisit.remove(d);
1917     visited.add(d);
1918
1919     HashSet<AllocSite> asSet = getAllocationSiteSet(d);
1920     Iterator asItr = asSet.iterator();
1921     while( asItr.hasNext() ) {
1922         AllocSite as = (AllocSite) asItr.next();
1923         TypeDescriptor typed = as.getType();
1924         if( typed != null ) {
1925           ClassDescriptor cd = typed.getClassDesc();
1926           if( cd != null && cd.hasFlags() ) {
1927             asSetTotal.add(as);
1928           }
1929         }
1930     }
1931
1932     // enqueue callees of this method to be searched for
1933     // allocation sites also
1934     Set callees = callGraph.getCalleeSet(d);
1935     if( callees != null ) {
1936         Iterator methItr = callees.iterator();
1937         while( methItr.hasNext() ) {
1938           MethodDescriptor md = (MethodDescriptor) methItr.next();
1939
1940           if( !visited.contains(md) ) {
1941             toVisit.add(md);
1942           }
1943         }
1944     }
1945   }
1946
1947   return asSetTotal;
1948 }
1949
1950
1951   
1952   
1953   // get successive captures of the analysis state, use compiler
1954   // flags to control
1955   boolean takeDebugSnapshots = false;
1956   String  descSymbolDebug    = null;
1957   boolean stopAfterCapture   = false;
1958   int     snapVisitCounter   = 0;
1959   int     snapNodeCounter    = 0;
1960   int     visitStartCapture  = 0;
1961   int     numVisitsToCapture = 0;
1962
1963
1964   void debugSnapshot( ReachGraph rg, FlatNode fn, boolean in ) {
1965     if( snapVisitCounter > visitStartCapture + numVisitsToCapture ) {
1966       return;
1967     }
1968
1969     if( in ) {
1970
1971     }
1972
1973     if( snapVisitCounter >= visitStartCapture ) {
1974       System.out.println( "    @@@ snapping visit="+snapVisitCounter+
1975                           ", node="+snapNodeCounter+
1976                           " @@@" );
1977       String graphName;
1978       if( in ) {
1979         graphName = String.format( "snap%02d_%04din",
1980                                    snapVisitCounter,
1981                                    snapNodeCounter );
1982       } else {
1983         graphName = String.format( "snap%02d_%04dout",
1984                                    snapVisitCounter,
1985                                    snapNodeCounter );
1986       }
1987       if( fn != null ) {
1988         graphName = graphName + fn;
1989       }
1990       rg.writeGraph( graphName,
1991                      true,  // write labels (variables)
1992                      true,  // selectively hide intermediate temp vars
1993                      true,  // prune unreachable heap regions
1994                      false, // hide subset reachability states
1995                      true );// hide edge taints
1996     }
1997   }
1998
1999 }