updating the global sweep and some related code--this currently bombs, only update...
[IRC.git] / Robust / src / Analysis / Disjoint / ReachGraph.java
1 package Analysis.Disjoint;
2
3 import IR.*;
4 import IR.Flat.*;
5 import Util.UtilAlgorithms;
6 import java.util.*;
7 import java.io.*;
8
9 public class ReachGraph {
10
11   // use to disable improvements for comparison
12   protected static final boolean DISABLE_STRONG_UPDATES = false;
13   protected static final boolean DISABLE_GLOBAL_SWEEP   = false;
14                    
15   // a special out-of-scope temp
16   protected static final TempDescriptor tdReturn = new TempDescriptor( "_Return___" );
17                    
18   // some frequently used reachability constants
19   protected static final ReachState rstateEmpty        = ReachState.factory();
20   protected static final ReachSet   rsetEmpty          = ReachSet.factory();
21   protected static final ReachSet   rsetWithEmptyState = ReachSet.factory( rstateEmpty );
22
23   // predicate constants
24   protected static final ExistPred    predTrue   = ExistPred.factory(); // if no args, true
25   protected static final ExistPredSet predsEmpty = ExistPredSet.factory();
26   protected static final ExistPredSet predsTrue  = ExistPredSet.factory( predTrue );
27
28
29   // from DisjointAnalysis for convenience
30   protected static int      allocationDepth   = -1;
31   protected static TypeUtil typeUtil          = null;
32
33
34   // variable and heap region nodes indexed by unique ID
35   public Hashtable<Integer,        HeapRegionNode> id2hrn;
36   public Hashtable<TempDescriptor, VariableNode  > td2vn;
37
38   // convenient set of alloc sites for all heap regions
39   // present in the graph without having to search
40   public HashSet<AllocSite> allocSites;  
41
42   public ReachGraph() {
43     id2hrn     = new Hashtable<Integer,        HeapRegionNode>();
44     td2vn      = new Hashtable<TempDescriptor, VariableNode  >();
45     allocSites = new HashSet<AllocSite>();
46   }
47
48   
49   // temp descriptors are globally unique and map to
50   // exactly one variable node, easy
51   protected VariableNode getVariableNodeFromTemp( TempDescriptor td ) {
52     assert td != null;
53
54     if( !td2vn.containsKey( td ) ) {
55       td2vn.put( td, new VariableNode( td ) );
56     }
57
58     return td2vn.get( td );
59   }
60
61   public boolean hasVariable( TempDescriptor td ) {
62     return td2vn.containsKey( td );
63   }
64
65
66   // this suite of methods can be used to assert a
67   // very important property of ReachGraph objects:
68   // some element, HeapRegionNode, RefEdge etc.
69   // should be referenced by at most ONE ReachGraph!!
70   // If a heap region or edge or variable should be
71   // in another graph, make a new object with
72   // equivalent properties for a new graph
73   public boolean belongsToThis( RefSrcNode rsn ) {
74     if( rsn instanceof VariableNode ) {
75       VariableNode vn = (VariableNode) rsn;
76       return this.td2vn.get( vn.getTempDescriptor() ) == vn;
77     }
78     HeapRegionNode hrn = (HeapRegionNode) rsn;
79     return this.id2hrn.get( hrn.getID() ) == hrn;
80   }
81
82
83
84   // the reason for this method is to have the option
85   // of creating new heap regions with specific IDs, or
86   // duplicating heap regions with specific IDs (especially
87   // in the merge() operation) or to create new heap
88   // regions with a new unique ID
89   protected HeapRegionNode
90     createNewHeapRegionNode( Integer        id,
91                              boolean        isSingleObject,
92                              boolean        isNewSummary,
93                              boolean        isFlagged,
94                              boolean        isOutOfContext,
95                              TypeDescriptor type,
96                              AllocSite      allocSite,
97                              ReachSet       inherent,
98                              ReachSet       alpha,
99                              ExistPredSet   preds,
100                              String         description
101                              ) {
102
103     boolean markForAnalysis = isFlagged;
104
105     TypeDescriptor typeToUse = null;
106     if( allocSite != null ) {
107       typeToUse = allocSite.getType();
108       allocSites.add( allocSite );
109     } else {
110       typeToUse = type;
111     }
112
113     if( allocSite != null && allocSite.getDisjointAnalysisId() != null ) {
114       markForAnalysis = true;
115     }
116
117     if( id == null ) {
118       id = DisjointAnalysis.generateUniqueHeapRegionNodeID();
119     }
120
121     if( inherent == null ) {
122       if( markForAnalysis ) {
123         inherent = 
124           ReachSet.factory(
125                            ReachState.factory(
126                                               ReachTuple.factory( id,
127                                                                   !isSingleObject,
128                                                                   ReachTuple.ARITY_ONE,
129                                                                   false // out-of-context
130                                                                   )
131                                               )
132                            );
133       } else {
134         inherent = rsetWithEmptyState;
135       }
136     }
137
138     if( alpha == null ) {
139       alpha = inherent;
140     }
141
142     if( preds == null ) {
143       // TODO: do this right?  For out-of-context nodes?
144       preds = ExistPredSet.factory();
145     }
146     
147     HeapRegionNode hrn = new HeapRegionNode( id,
148                                              isSingleObject,
149                                              markForAnalysis,
150                                              isNewSummary,
151                                              isOutOfContext,
152                                              typeToUse,
153                                              allocSite,
154                                              inherent,
155                                              alpha,
156                                              preds,
157                                              description );
158     id2hrn.put( id, hrn );
159     return hrn;
160   }
161
162
163
164   ////////////////////////////////////////////////
165   //
166   //  Low-level referencee and referencer methods
167   //
168   //  These methods provide the lowest level for
169   //  creating references between reachability nodes
170   //  and handling the details of maintaining both
171   //  list of referencers and referencees.
172   //
173   ////////////////////////////////////////////////
174   protected void addRefEdge( RefSrcNode     referencer,
175                              HeapRegionNode referencee,
176                              RefEdge        edge ) {
177     assert referencer != null;
178     assert referencee != null;
179     assert edge       != null;
180     assert edge.getSrc() == referencer;
181     assert edge.getDst() == referencee;
182     assert belongsToThis( referencer );
183     assert belongsToThis( referencee );
184
185     // edges are getting added twice to graphs now, the
186     // kind that should have abstract facts merged--use
187     // this check to prevent that
188     assert referencer.getReferenceTo( referencee,
189                                       edge.getType(),
190                                       edge.getField()
191                                       ) == null;
192
193     referencer.addReferencee( edge );
194     referencee.addReferencer( edge );
195   }
196
197   protected void removeRefEdge( RefEdge e ) {
198     removeRefEdge( e.getSrc(),
199                    e.getDst(),
200                    e.getType(),
201                    e.getField() );
202   }
203
204   protected void removeRefEdge( RefSrcNode     referencer,
205                                 HeapRegionNode referencee,
206                                 TypeDescriptor type,
207                                 String         field ) {
208     assert referencer != null;
209     assert referencee != null;
210     
211     RefEdge edge = referencer.getReferenceTo( referencee,
212                                               type,
213                                               field );
214     assert edge != null;
215     assert edge == referencee.getReferenceFrom( referencer,
216                                                 type,
217                                                 field );
218        
219     referencer.removeReferencee( edge );
220     referencee.removeReferencer( edge );
221   }
222
223   protected void clearRefEdgesFrom( RefSrcNode     referencer,
224                                     TypeDescriptor type,
225                                     String         field,
226                                     boolean        removeAll ) {
227     assert referencer != null;
228
229     // get a copy of the set to iterate over, otherwise
230     // we will be trying to take apart the set as we
231     // are iterating over it, which won't work
232     Iterator<RefEdge> i = referencer.iteratorToReferenceesClone();
233     while( i.hasNext() ) {
234       RefEdge edge = i.next();
235
236       if( removeAll                                          || 
237           (edge.typeEquals( type ) && edge.fieldEquals( field ))
238         ){
239
240         HeapRegionNode referencee = edge.getDst();
241         
242         removeRefEdge( referencer,
243                        referencee,
244                        edge.getType(),
245                        edge.getField() );
246       }
247     }
248   }
249
250   protected void clearRefEdgesTo( HeapRegionNode referencee,
251                                   TypeDescriptor type,
252                                   String         field,
253                                   boolean        removeAll ) {
254     assert referencee != null;
255
256     // get a copy of the set to iterate over, otherwise
257     // we will be trying to take apart the set as we
258     // are iterating over it, which won't work
259     Iterator<RefEdge> i = referencee.iteratorToReferencersClone();
260     while( i.hasNext() ) {
261       RefEdge edge = i.next();
262
263       if( removeAll                                          || 
264           (edge.typeEquals( type ) && edge.fieldEquals( field ))
265         ){
266
267         RefSrcNode referencer = edge.getSrc();
268
269         removeRefEdge( referencer,
270                        referencee,
271                        edge.getType(),
272                        edge.getField() );
273       }
274     }
275   }
276
277   protected void clearNonVarRefEdgesTo( HeapRegionNode referencee ) {
278     assert referencee != null;
279
280     // get a copy of the set to iterate over, otherwise
281     // we will be trying to take apart the set as we
282     // are iterating over it, which won't work
283     Iterator<RefEdge> i = referencee.iteratorToReferencersClone();
284     while( i.hasNext() ) {
285       RefEdge edge = i.next();
286       RefSrcNode referencer = edge.getSrc();
287       if( !(referencer instanceof VariableNode) ) {
288         removeRefEdge( referencer,
289                        referencee,
290                        edge.getType(),
291                        edge.getField() );
292       }
293     }
294   }
295
296
297   ////////////////////////////////////////////////////
298   //
299   //  Assignment Operation Methods
300   //
301   //  These methods are high-level operations for
302   //  modeling program assignment statements using
303   //  the low-level reference create/remove methods
304   //  above.
305   //
306   ////////////////////////////////////////////////////
307
308   public void assignTempXEqualToTempY( TempDescriptor x,
309                                        TempDescriptor y ) {
310     assignTempXEqualToCastedTempY( x, y, null );
311   }
312
313   public void assignTempXEqualToCastedTempY( TempDescriptor x,
314                                              TempDescriptor y,
315                                              TypeDescriptor tdCast ) {
316
317     VariableNode lnX = getVariableNodeFromTemp( x );
318     VariableNode lnY = getVariableNodeFromTemp( y );
319     
320     clearRefEdgesFrom( lnX, null, null, true );
321
322     // note it is possible that the types of temps in the
323     // flat node to analyze will reveal that some typed
324     // edges in the reachability graph are impossible
325     Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
326
327     Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
328     while( itrYhrn.hasNext() ) {
329       RefEdge        edgeY      = itrYhrn.next();
330       HeapRegionNode referencee = edgeY.getDst();
331       RefEdge        edgeNew    = edgeY.copy();
332
333       if( !isSuperiorType( x.getType(), edgeY.getType() ) ) {
334         impossibleEdges.add( edgeY );
335         continue;
336       }
337
338       edgeNew.setSrc( lnX );
339       
340       if( tdCast == null ) {
341         edgeNew.setType( mostSpecificType( y.getType(),                           
342                                            edgeY.getType(), 
343                                            referencee.getType() 
344                                            ) 
345                          );
346       } else {
347         edgeNew.setType( mostSpecificType( y.getType(),
348                                            edgeY.getType(), 
349                                            referencee.getType(),
350                                            tdCast
351                                            ) 
352                          );
353       }
354
355       edgeNew.setField( null );
356
357       addRefEdge( lnX, referencee, edgeNew );
358     }
359
360     Iterator<RefEdge> itrImp = impossibleEdges.iterator();
361     while( itrImp.hasNext() ) {
362       RefEdge edgeImp = itrImp.next();
363       removeRefEdge( edgeImp );
364     }
365   }
366
367
368   public void assignTempXEqualToTempYFieldF( TempDescriptor  x,
369                                              TempDescriptor  y,
370                                              FieldDescriptor f ) {
371     VariableNode lnX = getVariableNodeFromTemp( x );
372     VariableNode lnY = getVariableNodeFromTemp( y );
373
374     clearRefEdgesFrom( lnX, null, null, true );
375
376     // note it is possible that the types of temps in the
377     // flat node to analyze will reveal that some typed
378     // edges in the reachability graph are impossible
379     Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
380
381     Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
382     while( itrYhrn.hasNext() ) {
383       RefEdge        edgeY = itrYhrn.next();
384       HeapRegionNode hrnY  = edgeY.getDst();
385       ReachSet       betaY = edgeY.getBeta();
386
387       Iterator<RefEdge> itrHrnFhrn = hrnY.iteratorToReferencees();
388       while( itrHrnFhrn.hasNext() ) {
389         RefEdge        edgeHrn = itrHrnFhrn.next();
390         HeapRegionNode hrnHrn  = edgeHrn.getDst();
391         ReachSet       betaHrn = edgeHrn.getBeta();
392
393         // prune edges that are not a matching field
394         if( edgeHrn.getType() != null &&                    
395             !edgeHrn.getField().equals( f.getSymbol() )     
396             ) {
397           continue;
398         }
399
400         // check for impossible edges
401         if( !isSuperiorType( x.getType(), edgeHrn.getType() ) ) {
402           impossibleEdges.add( edgeHrn );
403           continue;
404         }
405
406         TypeDescriptor tdNewEdge =
407           mostSpecificType( edgeHrn.getType(), 
408                             hrnHrn.getType() 
409                             );
410           
411         RefEdge edgeNew = new RefEdge( lnX,
412                                        hrnHrn,
413                                        tdNewEdge,
414                                        null,
415                                        Canonical.intersection( betaY, betaHrn ),
416                                        predsTrue
417                                        );
418         
419         addRefEdge( lnX, hrnHrn, edgeNew );
420       }
421     }
422
423     Iterator<RefEdge> itrImp = impossibleEdges.iterator();
424     while( itrImp.hasNext() ) {
425       RefEdge edgeImp = itrImp.next();
426       removeRefEdge( edgeImp );
427     }
428
429     // anytime you might remove edges between heap regions
430     // you must global sweep to clean up broken reachability    
431     if( !impossibleEdges.isEmpty() ) {
432       if( !DISABLE_GLOBAL_SWEEP ) {
433         globalSweep();
434       }
435     }    
436   }
437
438
439   public void assignTempXFieldFEqualToTempY( TempDescriptor  x,
440                                              FieldDescriptor f,
441                                              TempDescriptor  y ) {
442
443     VariableNode lnX = getVariableNodeFromTemp( x );
444     VariableNode lnY = getVariableNodeFromTemp( y );
445
446     HashSet<HeapRegionNode> nodesWithNewAlpha = new HashSet<HeapRegionNode>();
447     HashSet<RefEdge>        edgesWithNewBeta  = new HashSet<RefEdge>();
448
449     // note it is possible that the types of temps in the
450     // flat node to analyze will reveal that some typed
451     // edges in the reachability graph are impossible
452     Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
453
454     // first look for possible strong updates and remove those edges
455     boolean strongUpdate = false;
456
457     Iterator<RefEdge> itrXhrn = lnX.iteratorToReferencees();
458     while( itrXhrn.hasNext() ) {
459       RefEdge        edgeX = itrXhrn.next();
460       HeapRegionNode hrnX  = edgeX.getDst();
461
462       // we can do a strong update here if one of two cases holds       
463       if( f != null &&
464           f != DisjointAnalysis.getArrayField( f.getType() ) &&     
465           (   (hrnX.getNumReferencers()                         == 1) || // case 1
466               (hrnX.isSingleObject() && lnX.getNumReferencees() == 1)    // case 2
467               )
468           ) {
469         if( !DISABLE_STRONG_UPDATES ) {
470           strongUpdate = true;
471           clearRefEdgesFrom( hrnX, f.getType(), f.getSymbol(), false );
472         }
473       }
474     }
475     
476     // then do all token propagation
477     itrXhrn = lnX.iteratorToReferencees();
478     while( itrXhrn.hasNext() ) {
479       RefEdge        edgeX = itrXhrn.next();
480       HeapRegionNode hrnX  = edgeX.getDst();
481       ReachSet       betaX = edgeX.getBeta();
482       ReachSet       R     = Canonical.intersection( hrnX.getAlpha(),
483                                                      edgeX.getBeta() 
484                                                      );
485
486       Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
487       while( itrYhrn.hasNext() ) {
488         RefEdge        edgeY = itrYhrn.next();
489         HeapRegionNode hrnY  = edgeY.getDst();
490         ReachSet       O     = edgeY.getBeta();
491
492         // check for impossible edges
493         if( !isSuperiorType( f.getType(), edgeY.getType() ) ) {
494           impossibleEdges.add( edgeY );
495           continue;
496         }
497
498         // propagate tokens over nodes starting from hrnSrc, and it will
499         // take care of propagating back up edges from any touched nodes
500         ChangeSet Cy = Canonical.unionUpArityToChangeSet( O, R );
501         propagateTokensOverNodes( hrnY, Cy, nodesWithNewAlpha, edgesWithNewBeta );
502
503         // then propagate back just up the edges from hrn
504         ChangeSet Cx = Canonical.unionUpArityToChangeSet( R, O );
505         HashSet<RefEdge> todoEdges = new HashSet<RefEdge>();
506
507         Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
508           new Hashtable<RefEdge, ChangeSet>();
509
510         Iterator<RefEdge> referItr = hrnX.iteratorToReferencers();
511         while( referItr.hasNext() ) {
512           RefEdge edgeUpstream = referItr.next();
513           todoEdges.add( edgeUpstream );
514           edgePlannedChanges.put( edgeUpstream, Cx );
515         }
516
517         propagateTokensOverEdges( todoEdges,
518                                   edgePlannedChanges,
519                                   edgesWithNewBeta );
520       }
521     }
522
523
524     // apply the updates to reachability
525     Iterator<HeapRegionNode> nodeItr = nodesWithNewAlpha.iterator();
526     while( nodeItr.hasNext() ) {
527       nodeItr.next().applyAlphaNew();
528     }
529
530     Iterator<RefEdge> edgeItr = edgesWithNewBeta.iterator();
531     while( edgeItr.hasNext() ) {
532       edgeItr.next().applyBetaNew();
533     }
534
535
536     // then go back through and add the new edges
537     itrXhrn = lnX.iteratorToReferencees();
538     while( itrXhrn.hasNext() ) {
539       RefEdge        edgeX = itrXhrn.next();
540       HeapRegionNode hrnX  = edgeX.getDst();
541       
542       Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
543       while( itrYhrn.hasNext() ) {
544         RefEdge        edgeY = itrYhrn.next();
545         HeapRegionNode hrnY  = edgeY.getDst();
546
547         // skip impossible edges here, we already marked them
548         // when computing reachability propagations above
549         if( !isSuperiorType( f.getType(), edgeY.getType() ) ) {
550           continue;
551         }
552         
553         // prepare the new reference edge hrnX.f -> hrnY
554         TypeDescriptor tdNewEdge =      
555           mostSpecificType( y.getType(),
556                             edgeY.getType(), 
557                             hrnY.getType()
558                             );  
559
560         RefEdge edgeNew = new RefEdge( hrnX,
561                                        hrnY,
562                                        tdNewEdge,
563                                        f.getSymbol(),
564                                        Canonical.pruneBy( edgeY.getBeta(),
565                                                           hrnX.getAlpha() 
566                                                           ),
567                                        predsTrue
568                                        );
569
570         // look to see if an edge with same field exists
571         // and merge with it, otherwise just add the edge
572         RefEdge edgeExisting = hrnX.getReferenceTo( hrnY, 
573                                                     tdNewEdge,
574                                                     f.getSymbol() );
575         
576         if( edgeExisting != null ) {
577           edgeExisting.setBeta(
578                                Canonical.union( edgeExisting.getBeta(),
579                                                 edgeNew.getBeta()
580                                                 )
581                                );
582           edgeExisting.setPreds(
583                                 Canonical.join( edgeExisting.getPreds(),
584                                                 edgeNew.getPreds()
585                                                 )
586                                 );
587         
588         } else {                          
589           addRefEdge( hrnX, hrnY, edgeNew );
590         }
591       }
592     }
593
594     Iterator<RefEdge> itrImp = impossibleEdges.iterator();
595     while( itrImp.hasNext() ) {
596       RefEdge edgeImp = itrImp.next();
597       removeRefEdge( edgeImp );
598     }
599
600     // if there was a strong update, make sure to improve
601     // reachability with a global sweep    
602     if( strongUpdate || !impossibleEdges.isEmpty() ) {    
603       if( !DISABLE_GLOBAL_SWEEP ) {
604         globalSweep();
605       }
606     }    
607   }
608
609
610   public void assignReturnEqualToTemp( TempDescriptor x ) {
611
612     VariableNode lnR = getVariableNodeFromTemp( tdReturn );
613     VariableNode lnX = getVariableNodeFromTemp( x );
614
615     clearRefEdgesFrom( lnR, null, null, true );
616
617     Iterator<RefEdge> itrXhrn = lnX.iteratorToReferencees();
618     while( itrXhrn.hasNext() ) {
619       RefEdge        edgeX      = itrXhrn.next();
620       HeapRegionNode referencee = edgeX.getDst();
621       RefEdge        edgeNew    = edgeX.copy();
622       edgeNew.setSrc( lnR );
623
624       addRefEdge( lnR, referencee, edgeNew );
625     }
626   }
627
628
629   public void assignTempEqualToNewAlloc( TempDescriptor x,
630                                          AllocSite      as ) {
631     assert x  != null;
632     assert as != null;
633
634     age( as );
635
636     // after the age operation the newest (or zero-ith oldest)
637     // node associated with the allocation site should have
638     // no references to it as if it were a newly allocated
639     // heap region
640     Integer        idNewest   = as.getIthOldest( 0 );
641     HeapRegionNode hrnNewest  = id2hrn.get( idNewest );
642     assert         hrnNewest != null;
643
644     VariableNode lnX = getVariableNodeFromTemp( x );
645     clearRefEdgesFrom( lnX, null, null, true );
646
647     // make a new reference to allocated node
648     TypeDescriptor type = as.getType();
649
650     RefEdge edgeNew =
651       new RefEdge( lnX,                  // source
652                    hrnNewest,            // dest
653                    type,                 // type
654                    null,                 // field name
655                    hrnNewest.getAlpha(), // beta
656                    predsTrue             // predicates
657                    );
658
659     addRefEdge( lnX, hrnNewest, edgeNew );
660   }
661
662
663   // use the allocation site (unique to entire analysis) to
664   // locate the heap region nodes in this reachability graph
665   // that should be aged.  The process models the allocation
666   // of new objects and collects all the oldest allocations
667   // in a summary node to allow for a finite analysis
668   //
669   // There is an additional property of this method.  After
670   // running it on a particular reachability graph (many graphs
671   // may have heap regions related to the same allocation site)
672   // the heap region node objects in this reachability graph will be
673   // allocated.  Therefore, after aging a graph for an allocation
674   // site, attempts to retrieve the heap region nodes using the
675   // integer id's contained in the allocation site should always
676   // return non-null heap regions.
677   public void age( AllocSite as ) {
678
679     // keep track of allocation sites that are represented 
680     // in this graph for efficiency with other operations
681     allocSites.add( as );
682
683     // if there is a k-th oldest node, it merges into
684     // the summary node
685     Integer idK = as.getOldest();
686     if( id2hrn.containsKey( idK ) ) {
687       HeapRegionNode hrnK = id2hrn.get( idK );
688
689       // retrieve the summary node, or make it
690       // from scratch
691       HeapRegionNode hrnSummary = getSummaryNode( as, false );      
692       
693       mergeIntoSummary( hrnK, hrnSummary );
694     }
695
696     // move down the line of heap region nodes
697     // clobbering the ith and transferring all references
698     // to and from i-1 to node i.
699     for( int i = allocationDepth - 1; i > 0; --i ) {
700
701       // only do the transfer if the i-1 node exists
702       Integer idImin1th = as.getIthOldest( i - 1 );
703       if( id2hrn.containsKey( idImin1th ) ) {
704         HeapRegionNode hrnImin1 = id2hrn.get( idImin1th );
705         if( hrnImin1.isWiped() ) {
706           // there is no info on this node, just skip
707           continue;
708         }
709
710         // either retrieve or make target of transfer
711         HeapRegionNode hrnI = getIthNode( as, i, false );
712
713         transferOnto( hrnImin1, hrnI );
714       }
715
716     }
717
718     // as stated above, the newest node should have had its
719     // references moved over to the second oldest, so we wipe newest
720     // in preparation for being the new object to assign something to
721     HeapRegionNode hrn0 = getIthNode( as, 0, false );
722     wipeOut( hrn0, true );
723
724     // now tokens in reachability sets need to "age" also
725     Iterator itrAllVariableNodes = td2vn.entrySet().iterator();
726     while( itrAllVariableNodes.hasNext() ) {
727       Map.Entry    me = (Map.Entry)    itrAllVariableNodes.next();
728       VariableNode ln = (VariableNode) me.getValue();
729
730       Iterator<RefEdge> itrEdges = ln.iteratorToReferencees();
731       while( itrEdges.hasNext() ) {
732         ageTuplesFrom( as, itrEdges.next() );
733       }
734     }
735
736     Iterator itrAllHRNodes = id2hrn.entrySet().iterator();
737     while( itrAllHRNodes.hasNext() ) {
738       Map.Entry      me       = (Map.Entry)      itrAllHRNodes.next();
739       HeapRegionNode hrnToAge = (HeapRegionNode) me.getValue();
740
741       ageTuplesFrom( as, hrnToAge );
742
743       Iterator<RefEdge> itrEdges = hrnToAge.iteratorToReferencees();
744       while( itrEdges.hasNext() ) {
745         ageTuplesFrom( as, itrEdges.next() );
746       }
747     }
748
749
750     // after tokens have been aged, reset newest node's reachability
751     // and a brand new node has a "true" predicate
752     hrn0.setAlpha( hrn0.getInherent() );
753     hrn0.setPreds( predsTrue );
754   }
755
756
757   // either retrieve or create the needed heap region node
758   protected HeapRegionNode getSummaryNode( AllocSite as, 
759                                            boolean   shadow ) {
760
761     Integer idSummary;
762     if( shadow ) {
763       idSummary = as.getSummaryShadow();
764     } else {
765       idSummary = as.getSummary();
766     }
767
768     HeapRegionNode hrnSummary = id2hrn.get( idSummary );
769
770     if( hrnSummary == null ) {
771
772       boolean hasFlags = false;
773       if( as.getType().isClass() ) {
774         hasFlags = as.getType().getClassDesc().hasFlags();
775       }
776       
777       if( as.getFlag() ){
778         hasFlags = as.getFlag();
779       }
780
781       String strDesc = as.toStringForDOT()+"\\nsummary";
782       if( shadow ) {
783         strDesc += " shadow";
784       }
785
786       hrnSummary = 
787         createNewHeapRegionNode( idSummary,    // id or null to generate a new one 
788                                  false,        // single object?                 
789                                  true,         // summary?       
790                                  hasFlags,     // flagged?
791                                  false,        // out-of-context?
792                                  as.getType(), // type                           
793                                  as,           // allocation site                        
794                                  null,         // inherent reach
795                                  null,         // current reach                 
796                                  predsEmpty,   // predicates
797                                  strDesc       // description
798                                  );                                
799     }
800   
801     return hrnSummary;
802   }
803
804   // either retrieve or create the needed heap region node
805   protected HeapRegionNode getIthNode( AllocSite as, 
806                                        Integer   i, 
807                                        boolean   shadow ) {
808
809     Integer idIth;
810     if( shadow ) {
811       idIth = as.getIthOldestShadow( i );
812     } else {
813       idIth = as.getIthOldest( i );
814     }
815     
816     HeapRegionNode hrnIth = id2hrn.get( idIth );
817     
818     if( hrnIth == null ) {
819
820       boolean hasFlags = false;
821       if( as.getType().isClass() ) {
822         hasFlags = as.getType().getClassDesc().hasFlags();
823       }
824       
825       if( as.getFlag() ){
826         hasFlags = as.getFlag();
827       }
828
829       String strDesc = as.toStringForDOT()+"\\n"+i+" oldest";
830       if( shadow ) {
831         strDesc += " shadow";
832       }
833
834       hrnIth = createNewHeapRegionNode( idIth,        // id or null to generate a new one 
835                                         true,         // single object?                  
836                                         false,        // summary?                        
837                                         hasFlags,     // flagged?                        
838                                         false,        // out-of-context?
839                                         as.getType(), // type                            
840                                         as,           // allocation site                         
841                                         null,         // inherent reach
842                                         null,         // current reach
843                                         predsEmpty,   // predicates
844                                         strDesc       // description
845                                         );
846     }
847
848     return hrnIth;
849   }
850
851
852   protected void mergeIntoSummary( HeapRegionNode hrn, 
853                                    HeapRegionNode hrnSummary ) {
854     assert hrnSummary.isNewSummary();
855
856     // assert that these nodes belong to THIS graph
857     assert belongsToThis( hrn );
858     assert belongsToThis( hrnSummary );
859
860     assert hrn != hrnSummary;
861
862     // transfer references _from_ hrn over to hrnSummary
863     Iterator<RefEdge> itrReferencee = hrn.iteratorToReferencees();
864     while( itrReferencee.hasNext() ) {
865       RefEdge edge       = itrReferencee.next();
866       RefEdge edgeMerged = edge.copy();
867       edgeMerged.setSrc( hrnSummary );
868
869       HeapRegionNode hrnReferencee = edge.getDst();
870       RefEdge        edgeSummary   = 
871         hrnSummary.getReferenceTo( hrnReferencee, 
872                                    edge.getType(),
873                                    edge.getField() 
874                                    );
875       
876       if( edgeSummary == null ) {
877         // the merge is trivial, nothing to be done
878         addRefEdge( hrnSummary, hrnReferencee, edgeMerged );
879
880       } else {
881         // otherwise an edge from the referencer to hrnSummary exists already
882         // and the edge referencer->hrn should be merged with it
883         edgeSummary.setBeta( 
884                             Canonical.union( edgeMerged.getBeta(),
885                                              edgeSummary.getBeta() 
886                                              ) 
887                              );
888         edgeSummary.setPreds( 
889                              Canonical.join( edgeMerged.getPreds(),
890                                              edgeSummary.getPreds() 
891                                              )
892                               );
893       }
894     }
895
896     // next transfer references _to_ hrn over to hrnSummary
897     Iterator<RefEdge> itrReferencer = hrn.iteratorToReferencers();
898     while( itrReferencer.hasNext() ) {
899       RefEdge edge         = itrReferencer.next();
900       RefEdge edgeMerged   = edge.copy();
901       edgeMerged.setDst( hrnSummary );
902
903       RefSrcNode onReferencer = edge.getSrc();
904       RefEdge    edgeSummary  =
905         onReferencer.getReferenceTo( hrnSummary, 
906                                      edge.getType(),
907                                      edge.getField() 
908                                      );
909
910       if( edgeSummary == null ) {
911         // the merge is trivial, nothing to be done
912         addRefEdge( onReferencer, hrnSummary, edgeMerged );
913
914       } else {
915         // otherwise an edge from the referencer to alpha_S exists already
916         // and the edge referencer->alpha_K should be merged with it
917         edgeSummary.setBeta( 
918                             Canonical.union( edgeMerged.getBeta(),
919                                              edgeSummary.getBeta() 
920                                              ) 
921                              );
922         edgeSummary.setPreds( 
923                              Canonical.join( edgeMerged.getPreds(),
924                                              edgeSummary.getPreds() 
925                                              )
926                               );
927       }
928     }
929
930     // then merge hrn reachability into hrnSummary
931     hrnSummary.setAlpha( 
932                         Canonical.union( hrnSummary.getAlpha(),
933                                          hrn.getAlpha() 
934                                          )
935                          );
936
937     hrnSummary.setPreds(
938                         Canonical.join( hrnSummary.getPreds(),
939                                         hrn.getPreds()
940                                         )
941                         );
942     
943     // and afterward, this node is gone
944     wipeOut( hrn, true );
945   }
946
947
948   protected void transferOnto( HeapRegionNode hrnA, 
949                                HeapRegionNode hrnB ) {
950
951     assert belongsToThis( hrnA );
952     assert belongsToThis( hrnB );
953     assert hrnA != hrnB;
954
955     // clear references in and out of node b?
956     assert hrnB.isWiped();
957
958     // copy each: (edge in and out of A) to B
959     Iterator<RefEdge> itrReferencee = hrnA.iteratorToReferencees();
960     while( itrReferencee.hasNext() ) {
961       RefEdge        edge          = itrReferencee.next();
962       HeapRegionNode hrnReferencee = edge.getDst();
963       RefEdge        edgeNew       = edge.copy();
964       edgeNew.setSrc( hrnB );
965       edgeNew.setDst( hrnReferencee );
966
967       addRefEdge( hrnB, hrnReferencee, edgeNew );
968     }
969
970     Iterator<RefEdge> itrReferencer = hrnA.iteratorToReferencers();
971     while( itrReferencer.hasNext() ) {
972       RefEdge    edge          = itrReferencer.next();
973       RefSrcNode rsnReferencer = edge.getSrc();
974       RefEdge    edgeNew       = edge.copy();
975       edgeNew.setSrc( rsnReferencer );
976       edgeNew.setDst( hrnB );
977
978       addRefEdge( rsnReferencer, hrnB, edgeNew );
979     }
980
981     // replace hrnB reachability and preds with hrnA's
982     hrnB.setAlpha( hrnA.getAlpha() );
983     hrnB.setPreds( hrnA.getPreds() );
984
985     // after transfer, wipe out source
986     wipeOut( hrnA, true );
987   }
988
989
990   // the purpose of this method is to conceptually "wipe out"
991   // a heap region from the graph--purposefully not called REMOVE
992   // because the node is still hanging around in the graph, just
993   // not mechanically connected or have any reach or predicate
994   // information on it anymore--lots of ops can use this
995   protected void wipeOut( HeapRegionNode hrn,
996                           boolean        wipeVariableReferences ) {
997
998     assert belongsToThis( hrn );
999
1000     clearRefEdgesFrom( hrn, null, null, true );
1001
1002     if( wipeVariableReferences ) {
1003       clearRefEdgesTo( hrn, null, null, true );
1004     } else {
1005       clearNonVarRefEdgesTo( hrn );
1006     }
1007
1008     hrn.setAlpha( rsetEmpty );
1009     hrn.setPreds( predsEmpty );
1010   }
1011
1012
1013   protected void ageTuplesFrom( AllocSite as, RefEdge edge ) {
1014     edge.setBeta( 
1015                  Canonical.ageTuplesFrom( edge.getBeta(),
1016                                           as
1017                                           )
1018                   );
1019   }
1020
1021   protected void ageTuplesFrom( AllocSite as, HeapRegionNode hrn ) {
1022     hrn.setAlpha( 
1023                  Canonical.ageTuplesFrom( hrn.getAlpha(),
1024                                           as
1025                                           )
1026                   );
1027   }
1028
1029
1030
1031   protected void propagateTokensOverNodes( HeapRegionNode          nPrime,
1032                                            ChangeSet               c0,
1033                                            HashSet<HeapRegionNode> nodesWithNewAlpha,
1034                                            HashSet<RefEdge>        edgesWithNewBeta ) {
1035
1036     HashSet<HeapRegionNode> todoNodes
1037       = new HashSet<HeapRegionNode>();
1038     todoNodes.add( nPrime );
1039     
1040     HashSet<RefEdge> todoEdges
1041       = new HashSet<RefEdge>();
1042     
1043     Hashtable<HeapRegionNode, ChangeSet> nodePlannedChanges
1044       = new Hashtable<HeapRegionNode, ChangeSet>();
1045     nodePlannedChanges.put( nPrime, c0 );
1046
1047     Hashtable<RefEdge, ChangeSet> edgePlannedChanges
1048       = new Hashtable<RefEdge, ChangeSet>();
1049
1050     // first propagate change sets everywhere they can go
1051     while( !todoNodes.isEmpty() ) {
1052       HeapRegionNode n = todoNodes.iterator().next();
1053       ChangeSet      C = nodePlannedChanges.get( n );
1054
1055       Iterator<RefEdge> referItr = n.iteratorToReferencers();
1056       while( referItr.hasNext() ) {
1057         RefEdge edge = referItr.next();
1058         todoEdges.add( edge );
1059
1060         if( !edgePlannedChanges.containsKey( edge ) ) {
1061           edgePlannedChanges.put( edge, 
1062                                   ChangeSet.factory()
1063                                   );
1064         }
1065
1066         edgePlannedChanges.put( edge, 
1067                                 Canonical.union( edgePlannedChanges.get( edge ),
1068                                                  C
1069                                                  )
1070                                 );
1071       }
1072
1073       Iterator<RefEdge> refeeItr = n.iteratorToReferencees();
1074       while( refeeItr.hasNext() ) {
1075         RefEdge        edgeF = refeeItr.next();
1076         HeapRegionNode m     = edgeF.getDst();
1077
1078         ChangeSet changesToPass = ChangeSet.factory();
1079
1080         Iterator<ChangeTuple> itrCprime = C.iterator();
1081         while( itrCprime.hasNext() ) {
1082           ChangeTuple c = itrCprime.next();
1083           if( edgeF.getBeta().contains( c.getSetToMatch() ) ) {
1084             changesToPass = Canonical.union( changesToPass, c );
1085           }
1086         }
1087
1088         if( !changesToPass.isEmpty() ) {
1089           if( !nodePlannedChanges.containsKey( m ) ) {
1090             nodePlannedChanges.put( m, ChangeSet.factory() );
1091           }
1092
1093           ChangeSet currentChanges = nodePlannedChanges.get( m );
1094
1095           if( !changesToPass.isSubset( currentChanges ) ) {
1096
1097             nodePlannedChanges.put( m, 
1098                                     Canonical.union( currentChanges,
1099                                                      changesToPass
1100                                                      )
1101                                     );
1102             todoNodes.add( m );
1103           }
1104         }
1105       }
1106
1107       todoNodes.remove( n );
1108     }
1109
1110     // then apply all of the changes for each node at once
1111     Iterator itrMap = nodePlannedChanges.entrySet().iterator();
1112     while( itrMap.hasNext() ) {
1113       Map.Entry      me = (Map.Entry)      itrMap.next();
1114       HeapRegionNode n  = (HeapRegionNode) me.getKey();
1115       ChangeSet      C  = (ChangeSet)      me.getValue();
1116
1117       // this propagation step is with respect to one change,
1118       // so we capture the full change from the old alpha:
1119       ReachSet localDelta = Canonical.applyChangeSet( n.getAlpha(),
1120                                                       C,
1121                                                       true 
1122                                                       );
1123       // but this propagation may be only one of many concurrent
1124       // possible changes, so keep a running union with the node's
1125       // partially updated new alpha set
1126       n.setAlphaNew( Canonical.union( n.getAlphaNew(),
1127                                       localDelta 
1128                                       )
1129                      );
1130
1131       nodesWithNewAlpha.add( n );
1132     }
1133
1134     propagateTokensOverEdges( todoEdges, 
1135                               edgePlannedChanges, 
1136                               edgesWithNewBeta
1137                               );
1138   }
1139
1140
1141   protected void propagateTokensOverEdges( HashSet  <RefEdge>            todoEdges,
1142                                            Hashtable<RefEdge, ChangeSet> edgePlannedChanges,
1143                                            HashSet  <RefEdge>            edgesWithNewBeta ) {
1144     
1145     // first propagate all change tuples everywhere they can go
1146     while( !todoEdges.isEmpty() ) {
1147       RefEdge edgeE = todoEdges.iterator().next();
1148       todoEdges.remove( edgeE );
1149
1150       if( !edgePlannedChanges.containsKey( edgeE ) ) {
1151         edgePlannedChanges.put( edgeE, 
1152                                 ChangeSet.factory()
1153                                 );
1154       }
1155
1156       ChangeSet C = edgePlannedChanges.get( edgeE );
1157
1158       ChangeSet changesToPass = ChangeSet.factory();
1159
1160       Iterator<ChangeTuple> itrC = C.iterator();
1161       while( itrC.hasNext() ) {
1162         ChangeTuple c = itrC.next();
1163         if( edgeE.getBeta().contains( c.getSetToMatch() ) ) {
1164           changesToPass = Canonical.union( changesToPass, c );
1165         }
1166       }
1167
1168       RefSrcNode rsn = edgeE.getSrc();
1169
1170       if( !changesToPass.isEmpty() && rsn instanceof HeapRegionNode ) {
1171         HeapRegionNode n = (HeapRegionNode) rsn;
1172
1173         Iterator<RefEdge> referItr = n.iteratorToReferencers();
1174         while( referItr.hasNext() ) {
1175           RefEdge edgeF = referItr.next();
1176
1177           if( !edgePlannedChanges.containsKey( edgeF ) ) {
1178             edgePlannedChanges.put( edgeF,
1179                                     ChangeSet.factory()
1180                                     );
1181           }
1182
1183           ChangeSet currentChanges = edgePlannedChanges.get( edgeF );
1184
1185           if( !changesToPass.isSubset( currentChanges ) ) {
1186             todoEdges.add( edgeF );
1187             edgePlannedChanges.put( edgeF,
1188                                     Canonical.union( currentChanges,
1189                                                      changesToPass
1190                                                      )
1191                                     );
1192           }
1193         }
1194       }
1195     }
1196
1197     // then apply all of the changes for each edge at once
1198     Iterator itrMap = edgePlannedChanges.entrySet().iterator();
1199     while( itrMap.hasNext() ) {
1200       Map.Entry me = (Map.Entry) itrMap.next();
1201       RefEdge   e  = (RefEdge)   me.getKey();
1202       ChangeSet C  = (ChangeSet) me.getValue();
1203
1204       // this propagation step is with respect to one change,
1205       // so we capture the full change from the old beta:
1206       ReachSet localDelta =
1207         Canonical.applyChangeSet( e.getBeta(),
1208                                   C,
1209                                   true 
1210                                   );
1211
1212       // but this propagation may be only one of many concurrent
1213       // possible changes, so keep a running union with the edge's
1214       // partially updated new beta set
1215       e.setBetaNew( Canonical.union( e.getBetaNew(),
1216                                      localDelta  
1217                                      )
1218                     );
1219       
1220       edgesWithNewBeta.add( e );
1221     }
1222   }
1223
1224
1225   // used in makeCalleeView below to decide if there is
1226   // already an appropriate out-of-context edge in a callee
1227   // view graph for merging, or null if a new one will be added
1228   protected RefEdge
1229     getOutOfContextReferenceTo( HeapRegionNode hrn,
1230                                 TypeDescriptor srcType,
1231                                 TypeDescriptor refType,
1232                                 String         refField ) {
1233     assert belongsToThis( hrn );
1234
1235     HeapRegionNode hrnInContext = id2hrn.get( hrn.getID() );
1236     if( hrnInContext == null ) {
1237       return null;
1238     }
1239
1240     Iterator<RefEdge> refItr = hrnInContext.iteratorToReferencers();
1241     while( refItr.hasNext() ) {
1242       RefEdge re = refItr.next();
1243
1244       assert belongsToThis( re.getSrc() );
1245       assert belongsToThis( re.getDst() );
1246
1247       if( !(re.getSrc() instanceof HeapRegionNode) ) {
1248         continue;
1249       }
1250
1251       HeapRegionNode hrnSrc = (HeapRegionNode) re.getSrc();
1252       if( !hrnSrc.isOutOfContext() ) {
1253         continue;
1254       }
1255       
1256       if( srcType == null ) {
1257         if( hrnSrc.getType() != null ) {
1258           continue;
1259         }
1260       } else {
1261         if( !srcType.equals( hrnSrc.getType() ) ) {
1262           continue;
1263         }
1264       }
1265
1266       if( !re.typeEquals( refType ) ) {
1267         continue;
1268       }
1269
1270       if( !re.fieldEquals( refField ) ) {
1271         continue;
1272       }
1273
1274       // tada!  We found it!
1275       return re;
1276     }
1277     
1278     return null;
1279   }
1280
1281   // used below to convert a ReachSet to its callee-context
1282   // equivalent with respect to allocation sites in this graph
1283   protected ReachSet toCalleeContext( ReachSet       rs,
1284                                       Integer        hrnID,
1285                                       TempDescriptor tdSrc,
1286                                       Integer        hrnSrcID,
1287                                       Integer        hrnDstID,
1288                                       TypeDescriptor type,
1289                                       String         field,
1290                                       boolean        outOfContext
1291                                       ) {
1292     ReachSet out = ReachSet.factory();
1293    
1294     Iterator<ReachState> itr = rs.iterator();
1295     while( itr.hasNext() ) {
1296       ReachState stateCaller = itr.next();
1297     
1298       ReachState stateCallee = stateCaller;
1299
1300       Iterator<AllocSite> asItr = allocSites.iterator();
1301       while( asItr.hasNext() ) {
1302         AllocSite as = asItr.next();
1303
1304         stateCallee = Canonical.toCalleeContext( stateCallee, as );
1305       }
1306
1307       ExistPredSet preds;
1308
1309       if( outOfContext ) {
1310         preds = predsEmpty;
1311       } else {
1312         ExistPred pred;
1313         if( hrnID != null ) {
1314           assert tdSrc    == null;
1315           assert hrnSrcID == null;
1316           assert hrnDstID == null;
1317           pred = ExistPred.factory( hrnID, 
1318                                     stateCaller );
1319         } else {
1320           assert tdSrc != null || hrnSrcID != null;
1321           assert hrnDstID != null;
1322           pred = ExistPred.factory( tdSrc,
1323                                     hrnSrcID,
1324                                     hrnDstID,
1325                                     type,
1326                                     field,
1327                                     stateCaller,
1328                                     false );
1329         }
1330         preds = ExistPredSet.factory( pred );
1331       }
1332       
1333       stateCallee = Canonical.attach( stateCallee,
1334                                       preds );
1335
1336       out = Canonical.add( out,
1337                            stateCallee
1338                            );
1339
1340     }
1341     assert out.isCanonical();
1342     return out;
1343   }
1344
1345   // used below to convert a ReachSet to its caller-context
1346   // equivalent with respect to allocation sites in this graph
1347   protected ReachSet 
1348     toCallerContext( ReachSet                            rs,
1349                      Hashtable<ReachState, ExistPredSet> calleeStatesSatisfied 
1350                      ) {
1351     ReachSet out = ReachSet.factory();
1352
1353     Iterator<ReachState> itr = rs.iterator();
1354     while( itr.hasNext() ) {
1355       ReachState stateCallee = itr.next();
1356
1357       if( calleeStatesSatisfied.containsKey( stateCallee ) ) {
1358
1359         // starting from one callee state...
1360         ReachSet rsCaller = ReachSet.factory( stateCallee );
1361
1362         // possibly branch it into many states, which any
1363         // allocation site might do, so lots of derived states
1364         Iterator<AllocSite> asItr = allocSites.iterator();
1365         while( asItr.hasNext() ) {
1366           AllocSite as = asItr.next();
1367           rsCaller = Canonical.toCallerContext( rs, as );
1368         }
1369         
1370         // then before adding each derived, now caller-context
1371         // states to the output, attach the appropriate pred
1372         // based on the source callee state
1373         Iterator<ReachState> stateItr = rsCaller.iterator();
1374         while( stateItr.hasNext() ) {
1375           ReachState stateCaller = stateItr.next();
1376           stateCaller = Canonical.attach( stateCaller,
1377                                           calleeStatesSatisfied.get( stateCallee )
1378                                           );        
1379           out = Canonical.union( out,
1380                                  stateCaller
1381                                  );
1382         }
1383       }
1384     }
1385     
1386     assert out.isCanonical();
1387     return out;
1388   }
1389
1390   // used below to convert a ReachSet to an equivalent
1391   // version with shadow IDs merged into unshadowed IDs
1392   protected ReachSet unshadow( ReachSet rs ) {
1393     ReachSet out = rs;
1394     Iterator<AllocSite> asItr = allocSites.iterator();
1395     while( asItr.hasNext() ) {
1396       AllocSite as = asItr.next();
1397       out = Canonical.unshadow( out, as );
1398     }
1399     assert out.isCanonical();
1400     return out;
1401   }
1402
1403
1404   // use this method to make a new reach graph that is
1405   // what heap the FlatMethod callee from the FlatCall 
1406   // would start with reaching from its arguments in
1407   // this reach graph
1408   public ReachGraph 
1409     makeCalleeView( FlatCall     fc,
1410                     FlatMethod   fmCallee,
1411                     Set<Integer> callerNodeIDsCopiedToCallee,
1412                     boolean      writeDebugDOTs
1413                     ) {
1414
1415     // the callee view is a new graph: DON'T MODIFY
1416     // *THIS* graph
1417     ReachGraph rg = new ReachGraph();
1418
1419     // track what parts of this graph have already been
1420     // added to callee view, variables not needed.
1421     // Note that we need this because when we traverse
1422     // this caller graph for each parameter we may find
1423     // nodes and edges more than once (which the per-param
1424     // "visit" sets won't show) and we only want to create
1425     // an element in the new callee view one time
1426
1427
1428     // a conservative starting point is to take the 
1429     // mechanically-reachable-from-arguments graph
1430     // as opposed to using reachability information
1431     // to prune the graph further
1432     for( int i = 0; i < fmCallee.numParameters(); ++i ) {
1433
1434       // for each parameter index, get the symbol in the
1435       // caller view and callee view
1436       
1437       // argument defined here is the symbol in the caller
1438       TempDescriptor tdArg = fc.getArgMatchingParamIndex( fmCallee, i );
1439
1440       // parameter defined here is the symbol in the callee
1441       TempDescriptor tdParam = fmCallee.getParameter( i );
1442
1443       // use these two VariableNode objects to translate
1444       // between caller and callee--its easy to compare
1445       // a HeapRegionNode across callee and caller because
1446       // they will have the same heap region ID
1447       VariableNode vnCaller = this.getVariableNodeFromTemp( tdArg );
1448       VariableNode vnCallee = rg.getVariableNodeFromTemp( tdParam );
1449       
1450       // now traverse the calleR view using the argument to
1451       // build the calleE view which has the parameter symbol
1452       Set<RefSrcNode> toVisitInCaller = new HashSet<RefSrcNode>();
1453       Set<RefSrcNode> visitedInCaller = new HashSet<RefSrcNode>();
1454       toVisitInCaller.add( vnCaller );
1455
1456       while( !toVisitInCaller.isEmpty() ) {
1457         RefSrcNode rsnCaller = toVisitInCaller.iterator().next();
1458         RefSrcNode rsnCallee;
1459
1460         toVisitInCaller.remove( rsnCaller );
1461         visitedInCaller.add( rsnCaller );
1462         
1463         // FIRST - setup the source end of an edge, and
1464         // remember the identifying info of the source
1465         // to build predicates
1466         TempDescriptor tdSrc    = null;
1467         Integer        hrnSrcID = null;
1468
1469         if( rsnCaller == vnCaller ) {
1470           // if the caller node is the param symbol, we
1471           // have to do this translation for the callee
1472           rsnCallee = vnCallee;
1473           tdSrc     = tdArg;
1474
1475         } else {
1476           // otherwise the callee-view node is a heap
1477           // region with the same ID, that may or may
1478           // not have been created already
1479           assert rsnCaller instanceof HeapRegionNode;          
1480
1481           HeapRegionNode hrnSrcCaller = (HeapRegionNode) rsnCaller;
1482           hrnSrcID = hrnSrcCaller.getID(); 
1483
1484           if( !callerNodeIDsCopiedToCallee.contains( hrnSrcID ) ) {
1485             
1486             ExistPred pred = 
1487               ExistPred.factory( hrnSrcID, null );
1488
1489             ExistPredSet preds = 
1490               ExistPredSet.factory( pred );
1491
1492             rsnCallee = 
1493               rg.createNewHeapRegionNode( hrnSrcCaller.getID(),
1494                                           hrnSrcCaller.isSingleObject(),
1495                                           hrnSrcCaller.isNewSummary(),
1496                                           hrnSrcCaller.isFlagged(),
1497                                           false, // out-of-context?
1498                                           hrnSrcCaller.getType(),
1499                                           hrnSrcCaller.getAllocSite(),
1500                                           toCalleeContext( hrnSrcCaller.getInherent(),   // in state
1501                                                            hrnSrcCaller.getID(),         // node pred
1502                                                            null, null, null, null, null, // edge pred
1503                                                            false ),                      // ooc pred
1504                                           toCalleeContext( hrnSrcCaller.getAlpha(),      // in state
1505                                                            hrnSrcCaller.getID(),         // node pred
1506                                                            null, null, null, null, null, // edge pred
1507                                                            false ),                      // ooc pred
1508                                           preds,
1509                                           hrnSrcCaller.getDescription()
1510                                           );
1511
1512             callerNodeIDsCopiedToCallee.add( hrnSrcID );
1513
1514           } else {
1515             rsnCallee = rg.id2hrn.get( hrnSrcID );
1516           }
1517         }
1518
1519         // SECOND - go over all edges from that source
1520
1521         Iterator<RefEdge> itrRefEdges = rsnCaller.iteratorToReferencees();
1522         while( itrRefEdges.hasNext() ) {
1523           RefEdge        reCaller  = itrRefEdges.next();
1524           HeapRegionNode hrnCaller = reCaller.getDst();
1525           HeapRegionNode hrnCallee;
1526
1527           // THIRD - setup destination ends of edges
1528           Integer hrnDstID = hrnCaller.getID(); 
1529
1530           if( !callerNodeIDsCopiedToCallee.contains( hrnDstID ) ) {
1531
1532             ExistPred pred = 
1533               ExistPred.factory( hrnDstID, null );
1534
1535             ExistPredSet preds = 
1536               ExistPredSet.factory( pred );
1537             
1538             hrnCallee = 
1539               rg.createNewHeapRegionNode( hrnDstID,
1540                                           hrnCaller.isSingleObject(),
1541                                           hrnCaller.isNewSummary(),
1542                                           hrnCaller.isFlagged(),
1543                                           false, // out-of-context?
1544                                           hrnCaller.getType(),
1545                                           hrnCaller.getAllocSite(),
1546                                           toCalleeContext( hrnCaller.getInherent(),      // in state
1547                                                            hrnDstID,                     // node pred
1548                                                            null, null, null, null, null, // edge pred
1549                                                            false ),                      // ooc pred
1550                                           toCalleeContext( hrnCaller.getAlpha(),         // in state
1551                                                            hrnDstID,                     // node pred
1552                                                            null, null, null, null, null, // edge pred
1553                                                            false ),                      // ooc pred
1554                                           preds,
1555                                           hrnCaller.getDescription()
1556                                           );
1557
1558             callerNodeIDsCopiedToCallee.add( hrnDstID );
1559
1560           } else {
1561             hrnCallee = rg.id2hrn.get( hrnDstID );
1562           }
1563
1564           // FOURTH - copy edge over if needed
1565           RefEdge reCallee = rsnCallee.getReferenceTo( hrnCallee,
1566                                                        reCaller.getType(),
1567                                                        reCaller.getField()
1568                                                        );
1569           if( reCallee == null ) {
1570
1571             ExistPred pred =
1572               ExistPred.factory( tdSrc, 
1573                                  hrnSrcID, 
1574                                  hrnDstID,
1575                                  reCaller.getType(),
1576                                  reCaller.getField(),
1577                                  null,
1578                                  rsnCaller instanceof VariableNode ); // out-of-context
1579
1580             ExistPredSet preds = 
1581               ExistPredSet.factory( pred );
1582
1583             rg.addRefEdge( rsnCallee,
1584                            hrnCallee,
1585                            new RefEdge( rsnCallee,
1586                                         hrnCallee,
1587                                         reCaller.getType(),
1588                                         reCaller.getField(),
1589                                         toCalleeContext( reCaller.getBeta(),  // in state
1590                                                          null,                // node pred
1591                                                          tdSrc,               // edge pred
1592                                                          hrnSrcID,            // edge pred
1593                                                          hrnDstID,            // edge pred
1594                                                          reCaller.getType(),  // edge pred
1595                                                          reCaller.getField(), // edge pred
1596                                                          false ),             // ooc pred
1597                                         preds
1598                                         )
1599                            );              
1600           }
1601           
1602           // keep traversing nodes reachable from param i
1603           // that we haven't visited yet
1604           if( !visitedInCaller.contains( hrnCaller ) ) {
1605             toVisitInCaller.add( hrnCaller );
1606           }
1607           
1608         } // end edge iteration        
1609       } // end visiting heap nodes in caller
1610     } // end iterating over parameters as starting points
1611
1612
1613     // find the set of edges in this graph with source
1614     // out-of-context (not in nodes copied) and have a
1615     // destination in context (one of nodes copied) as
1616     // a starting point for building out-of-context nodes
1617     Iterator<Integer> itrInContext =
1618       callerNodeIDsCopiedToCallee.iterator();
1619     while( itrInContext.hasNext() ) {
1620       Integer        hrnID                 = itrInContext.next();
1621       HeapRegionNode hrnCallerAndInContext = id2hrn.get( hrnID );
1622       
1623       Iterator<RefEdge> itrMightCross =
1624         hrnCallerAndInContext.iteratorToReferencers();
1625       while( itrMightCross.hasNext() ) {
1626         RefEdge edgeMightCross = itrMightCross.next();        
1627
1628         RefSrcNode rsnCallerAndOutContext =
1629           edgeMightCross.getSrc();
1630         
1631         TypeDescriptor oocNodeType;
1632         ReachSet       oocReach;
1633
1634         TempDescriptor oocPredSrcTemp = null;
1635         Integer        oocPredSrcID   = null;
1636
1637         if( rsnCallerAndOutContext instanceof VariableNode ) {
1638           // variables are always out-of-context
1639           oocNodeType = null;
1640           oocReach    = rsetEmpty;
1641           oocPredSrcTemp = 
1642             ((VariableNode)rsnCallerAndOutContext).getTempDescriptor();
1643
1644         } else {
1645           
1646           HeapRegionNode hrnCallerAndOutContext = 
1647             (HeapRegionNode) rsnCallerAndOutContext;
1648
1649           // is this source node out-of-context?
1650           if( callerNodeIDsCopiedToCallee.contains( hrnCallerAndOutContext.getID() ) ) {
1651             // no, skip this edge
1652             continue;
1653           }
1654
1655           oocNodeType  = hrnCallerAndOutContext.getType();
1656           oocReach     = hrnCallerAndOutContext.getAlpha(); 
1657           oocPredSrcID = hrnCallerAndOutContext.getID();
1658         }        
1659
1660         // if we're here we've found an out-of-context edge
1661
1662         ExistPred pred =
1663           ExistPred.factory( oocPredSrcTemp, 
1664                              oocPredSrcID, 
1665                              hrnID,
1666                              edgeMightCross.getType(),
1667                              edgeMightCross.getField(),
1668                              null,
1669                              true ); // out-of-context
1670
1671         ExistPredSet preds = 
1672           ExistPredSet.factory( pred );
1673
1674
1675         HeapRegionNode hrnCalleeAndInContext = 
1676           rg.id2hrn.get( hrnCallerAndInContext.getID() );
1677         
1678         RefEdge oocEdgeExisting =
1679           rg.getOutOfContextReferenceTo( hrnCalleeAndInContext,
1680                                          oocNodeType,
1681                                          edgeMightCross.getType(),
1682                                          edgeMightCross.getField()
1683                                          );
1684
1685         if( oocEdgeExisting == null ) {
1686           // we found a reference that crosses from out-of-context
1687           // to in-context, so build a special out-of-context node
1688           // for the callee IHM and its reference edge
1689           
1690           // for consistency, map one out-of-context "identifier"
1691           // to one heap region node id, otherwise no convergence
1692           String oocid = "oocid"+
1693             fmCallee+
1694             hrnCalleeAndInContext.getIDString()+
1695             oocNodeType+
1696             edgeMightCross.getType()+
1697             edgeMightCross.getField();
1698           
1699           Integer oocHrnID = oocid2hrnid.get( oocid );
1700
1701           HeapRegionNode hrnCalleeAndOutContext;
1702
1703           if( oocHrnID == null ) {
1704
1705             hrnCalleeAndOutContext =
1706               rg.createNewHeapRegionNode( null,  // ID
1707                                           false, // single object?
1708                                           false, // new summary?
1709                                           false, // flagged?
1710                                           true,  // out-of-context?
1711                                           oocNodeType,
1712                                           null,  // alloc site, shouldn't be used
1713                                           toCalleeContext( oocReach,                     // in state
1714                                                            null,                         // node pred
1715                                                            null, null, null, null, null, // edge pred
1716                                                            true                          // ooc pred
1717                                                            ), // inherent
1718                                           toCalleeContext( oocReach,                     // in state
1719                                                            null,                         // node pred
1720                                                            null, null, null, null, null, // edge pred
1721                                                            true                          // ooc pred
1722                                                            ), // alpha
1723                                           preds,
1724                                           "out-of-context"
1725                                           );       
1726
1727             oocid2hrnid.put( oocid, hrnCalleeAndOutContext.getID() );
1728
1729           } else {
1730
1731             // the mapping already exists, so see if node is there
1732             hrnCalleeAndOutContext = rg.id2hrn.get( oocHrnID );
1733
1734             if( hrnCalleeAndOutContext == null ) {
1735               // nope, make it
1736               hrnCalleeAndOutContext =
1737                 rg.createNewHeapRegionNode( oocHrnID,  // ID
1738                                             false, // single object?
1739                                             false, // new summary?
1740                                             false, // flagged?
1741                                             true,  // out-of-context?
1742                                             oocNodeType,
1743                                             null,  // alloc site, shouldn't be used
1744                                             toCalleeContext( oocReach,                     // in state
1745                                                              null,                         // node pred
1746                                                              null, null, null, null, null, // edge pred
1747                                                              true                          // ooc pred
1748                                                              ), // inherent
1749                                             toCalleeContext( oocReach,                     // in state
1750                                                              null,                         // node pred
1751                                                              null, null, null, null, null, // edge pred
1752                                                              true                          // ooc pred
1753                                                              ), // alpha
1754                                             preds,
1755                                             "out-of-context"
1756                                             );       
1757             }
1758           }
1759
1760           rg.addRefEdge( hrnCalleeAndOutContext,
1761                          hrnCalleeAndInContext,
1762                          new RefEdge( hrnCalleeAndOutContext,
1763                                       hrnCalleeAndInContext,
1764                                       edgeMightCross.getType(),
1765                                       edgeMightCross.getField(),
1766                                       toCalleeContext( edgeMightCross.getBeta(),      // in state
1767                                                        null,                          // node pred
1768                                                        oocPredSrcTemp,                // edge pred
1769                                                        oocPredSrcID,                  // edge pred
1770                                                        hrnCallerAndInContext.getID(), // edge pred
1771                                                        edgeMightCross.getType(),      // edge pred
1772                                                        edgeMightCross.getField(),     // edge pred
1773                                                        false                          // ooc pred
1774                                                        ),
1775                                       preds
1776                                       )
1777                          );              
1778
1779         } else {
1780           // the out-of-context edge already exists
1781           oocEdgeExisting.setBeta( Canonical.union( oocEdgeExisting.getBeta(),
1782                                                     toCalleeContext( edgeMightCross.getBeta(),      // in state
1783                                                                      null,                          // node pred
1784                                                                      oocPredSrcTemp,                // edge pred
1785                                                                      oocPredSrcID,                  // edge pred
1786                                                                      hrnCallerAndInContext.getID(), // edge pred
1787                                                                      edgeMightCross.getType(),      // edge pred
1788                                                                      edgeMightCross.getField(),     // edge pred
1789                                                                      false                          // ooc pred
1790                                                                      )
1791                                                     )
1792                                    );         
1793           
1794           oocEdgeExisting.setPreds( Canonical.join( oocEdgeExisting.getPreds(),
1795                                                     edgeMightCross.getPreds()
1796                                                     )
1797                                     );          
1798           
1799         }                
1800       }
1801     }    
1802
1803     if( writeDebugDOTs ) {    
1804       try {
1805         rg.writeGraph( "calleeview", true, false, false, false, true, true );
1806       } catch( IOException e ) {}
1807     }
1808
1809     return rg;
1810   }  
1811
1812   private static Hashtable<String, Integer> oocid2hrnid = 
1813     new Hashtable<String, Integer>();
1814
1815
1816
1817   public void 
1818     resolveMethodCall( FlatCall     fc,        
1819                        FlatMethod   fmCallee,        
1820                        ReachGraph   rgCallee,
1821                        Set<Integer> callerNodeIDsCopiedToCallee,
1822                        boolean      writeDebugDOTs
1823                        ) {
1824
1825
1826     if( writeDebugDOTs ) {
1827       try {
1828         rgCallee.writeGraph( "callee", 
1829                              true, false, false, false, true, true );
1830         writeGraph( "caller00In", 
1831                     true, false, false, false, true, true, 
1832                     callerNodeIDsCopiedToCallee );
1833       } catch( IOException e ) {}
1834     }
1835
1836
1837     // method call transfer function steps:
1838     // 1. Use current callee-reachable heap (CRH) to test callee 
1839     //    predicates and mark what will be coming in.
1840     // 2. Wipe CRH out of caller.
1841     // 3. Transplant marked callee parts in:
1842     //    a) bring in nodes
1843     //    b) bring in callee -> callee edges
1844     //    c) resolve out-of-context -> callee edges
1845     //    d) assign return value
1846     // 4. Collapse shadow nodes down
1847     // 5. Global sweep it.
1848
1849
1850
1851     // 1. mark what callee elements have satisfied predicates
1852     Hashtable<HeapRegionNode, ExistPredSet> calleeNodesSatisfied =
1853       new Hashtable<HeapRegionNode, ExistPredSet>();
1854     
1855     Hashtable<RefEdge, ExistPredSet> calleeEdgesSatisfied =
1856       new Hashtable<RefEdge, ExistPredSet>();
1857
1858     Hashtable<ReachState, ExistPredSet> calleeStatesSatisfied =
1859       new Hashtable<ReachState, ExistPredSet>();
1860
1861     Hashtable< RefEdge, Set<RefSrcNode> > calleeEdges2oocCallerSrcMatches =
1862       new Hashtable< RefEdge, Set<RefSrcNode> >();
1863
1864     Iterator meItr = rgCallee.id2hrn.entrySet().iterator();
1865     while( meItr.hasNext() ) {
1866       Map.Entry      me        = (Map.Entry)      meItr.next();
1867       Integer        id        = (Integer)        me.getKey();
1868       HeapRegionNode hrnCallee = (HeapRegionNode) me.getValue();
1869
1870       // if a callee element's predicates are satisfied then a set
1871       // of CALLER predicates is returned: they are the predicates
1872       // that the callee element moved into the caller context
1873       // should have, and it is inefficient to find this again later
1874       ExistPredSet predsIfSatis = 
1875         hrnCallee.getPreds().isSatisfiedBy( this,
1876                                             callerNodeIDsCopiedToCallee
1877                                             );
1878       if( predsIfSatis != null ) {
1879         calleeNodesSatisfied.put( hrnCallee, predsIfSatis );
1880       } else {
1881         // otherwise don't bother looking at edges to this node
1882         continue;
1883       }
1884       
1885       // since the node is coming over, find out which reach
1886       // states on it should come over, too
1887       Iterator<ReachState> stateItr = hrnCallee.getAlpha().iterator();
1888       while( stateItr.hasNext() ) {
1889         ReachState stateCallee = stateItr.next();
1890
1891         predsIfSatis = 
1892           stateCallee.getPreds().isSatisfiedBy( this,
1893                                                 callerNodeIDsCopiedToCallee
1894                                                 );
1895         if( predsIfSatis != null ) {
1896           calleeStatesSatisfied.put( stateCallee, predsIfSatis );
1897         } 
1898       }
1899
1900       // then look at edges to the node
1901       Iterator<RefEdge> reItr = hrnCallee.iteratorToReferencers();
1902       while( reItr.hasNext() ) {
1903         RefEdge    reCallee  = reItr.next();
1904         RefSrcNode rsnCallee = reCallee.getSrc();
1905
1906         // (caller local variables to in-context heap regions)
1907         // have an (out-of-context heap region -> in-context heap region)
1908         // abstraction in the callEE, so its true we never need to
1909         // look at a (var node -> heap region) edge in callee to bring
1910         // those over for the call site transfer.  What about (param var->heap region)
1911         // edges in callee? They are dealt with below this loop.
1912         // So, yes, at this point skip (var->region) edges in callee
1913         if( rsnCallee instanceof VariableNode ) {
1914           continue;
1915         }        
1916
1917         // first see if the source is out-of-context, and only
1918         // proceed with this edge if we find some caller-context
1919         // matches
1920         HeapRegionNode hrnSrcCallee = (HeapRegionNode) rsnCallee;
1921         boolean matchedOutOfContext = false;
1922
1923         if( hrnSrcCallee.isOutOfContext() ) {          
1924
1925           assert !calleeEdges2oocCallerSrcMatches.containsKey( reCallee );
1926           Set<RefSrcNode> rsnCallers = new HashSet<RefSrcNode>();            
1927
1928           HeapRegionNode hrnDstCaller = this.id2hrn.get( hrnCallee.getID() );
1929           Iterator<RefEdge> reDstItr = hrnDstCaller.iteratorToReferencers();
1930           while( reDstItr.hasNext() ) {
1931             // the edge and field (either possibly null) must match
1932             RefEdge reCaller = reDstItr.next();
1933
1934             if( !reCaller.typeEquals ( reCallee.getType()  ) ||
1935                 !reCaller.fieldEquals( reCallee.getField() ) 
1936                 ) {
1937               continue;
1938             }
1939             
1940             RefSrcNode rsnCaller = reCaller.getSrc();
1941             if( rsnCaller instanceof VariableNode ) {
1942               // a variable node matches an OOC region with null type
1943               if( hrnSrcCallee.getType() != null ) {
1944                 continue;
1945               }
1946
1947             } else {
1948               // otherwise types should match
1949               HeapRegionNode hrnCallerSrc = (HeapRegionNode) rsnCaller;
1950               if( hrnSrcCallee.getType() == null ) {
1951                 if( hrnCallerSrc.getType() != null ) {
1952                   continue;
1953                 }
1954               } else {
1955                 if( !hrnSrcCallee.getType().equals( hrnCallerSrc.getType() ) ) {
1956                   continue;
1957                 }
1958               }
1959             }
1960
1961             rsnCallers.add( rsnCaller );
1962             matchedOutOfContext = true;
1963           }
1964
1965           if( !rsnCallers.isEmpty() ) {
1966             calleeEdges2oocCallerSrcMatches.put( reCallee, rsnCallers );
1967           }
1968         }
1969
1970         if( hrnSrcCallee.isOutOfContext() &&
1971             !matchedOutOfContext ) {
1972           continue;
1973         }
1974         
1975         predsIfSatis = 
1976           reCallee.getPreds().isSatisfiedBy( this,
1977                                              callerNodeIDsCopiedToCallee
1978                                              );
1979         if( predsIfSatis != null ) {
1980           calleeEdgesSatisfied.put( reCallee, predsIfSatis );
1981
1982           // since the edge is coming over, find out which reach
1983           // states on it should come over, too
1984           stateItr = reCallee.getBeta().iterator();
1985           while( stateItr.hasNext() ) {
1986             ReachState stateCallee = stateItr.next();
1987             
1988             predsIfSatis = 
1989               stateCallee.getPreds().isSatisfiedBy( this,
1990                                                     callerNodeIDsCopiedToCallee
1991                                                     );
1992             if( predsIfSatis != null ) {
1993               calleeStatesSatisfied.put( stateCallee, predsIfSatis );
1994             } 
1995           }
1996
1997         }        
1998
1999       }
2000     }
2001
2002     // test param -> HRN edges, also
2003     for( int i = 0; i < fmCallee.numParameters(); ++i ) {
2004
2005       // parameter defined here is the symbol in the callee
2006       TempDescriptor tdParam  = fmCallee.getParameter( i );
2007       VariableNode   vnCallee = rgCallee.getVariableNodeFromTemp( tdParam );
2008
2009       Iterator<RefEdge> reItr = vnCallee.iteratorToReferencees();
2010       while( reItr.hasNext() ) {
2011         RefEdge reCallee = reItr.next();
2012         
2013         ExistPredSet ifDst = 
2014           reCallee.getDst().getPreds().isSatisfiedBy( this,
2015                                                       callerNodeIDsCopiedToCallee
2016                                                       );
2017         if( ifDst == null ) {
2018           continue;
2019         }
2020         
2021         ExistPredSet predsIfSatis = 
2022           reCallee.getPreds().isSatisfiedBy( this,
2023                                              callerNodeIDsCopiedToCallee
2024                                              );
2025         if( predsIfSatis != null ) {
2026           calleeEdgesSatisfied.put( reCallee, predsIfSatis );
2027
2028           // since the edge is coming over, find out which reach
2029           // states on it should come over, too
2030           Iterator<ReachState> stateItr = reCallee.getBeta().iterator();
2031           while( stateItr.hasNext() ) {
2032             ReachState stateCallee = stateItr.next();
2033             
2034             predsIfSatis = 
2035               stateCallee.getPreds().isSatisfiedBy( this,
2036                                                     callerNodeIDsCopiedToCallee
2037                                                     );
2038             if( predsIfSatis != null ) {
2039               calleeStatesSatisfied.put( stateCallee, predsIfSatis );
2040             } 
2041           }
2042
2043         }        
2044       }
2045     }
2046
2047
2048
2049
2050     if( writeDebugDOTs ) {
2051       try {
2052         writeGraph( "caller20BeforeWipe", 
2053                     true, false, false, false, true, true );
2054       } catch( IOException e ) {}
2055     }
2056
2057
2058     // 2. predicates tested, ok to wipe out caller part
2059     Iterator<Integer> hrnItr = callerNodeIDsCopiedToCallee.iterator();
2060     while( hrnItr.hasNext() ) {
2061       Integer        hrnID     = hrnItr.next();
2062       HeapRegionNode hrnCaller = id2hrn.get( hrnID );
2063       assert hrnCaller != null;
2064
2065       // when clearing off nodes, also eliminate variable
2066       // references
2067       wipeOut( hrnCaller, true );
2068     }
2069
2070
2071
2072     if( writeDebugDOTs ) {
2073       try {
2074         writeGraph( "caller30BeforeAddingNodes", 
2075                     true, false, false, false, true, true );
2076       } catch( IOException e ) {}
2077     }
2078
2079
2080     // 3. callee elements with satisfied preds come in, note that
2081     //    the mapping of elements satisfied to preds is like this:
2082     //    A callee element EE has preds EEp that are satisfied by
2083     //    some caller element ER.  We bring EE into the caller
2084     //    context as ERee with the preds of ER, namely ERp, which
2085     //    in the following algorithm is the value in the mapping
2086
2087     // 3.a) nodes
2088     Iterator satisItr = calleeNodesSatisfied.entrySet().iterator();
2089     while( satisItr.hasNext() ) {
2090       Map.Entry      me        = (Map.Entry)      satisItr.next();
2091       HeapRegionNode hrnCallee = (HeapRegionNode) me.getKey();
2092       ExistPredSet   preds     = (ExistPredSet)   me.getValue();
2093
2094       // TODO: I think its true that the current implementation uses
2095       // the type of the OOC region and the predicates OF THE EDGE from
2096       // it to link everything up in caller context, so that's why we're
2097       // skipping this... maybe that's a sillier way to do it?
2098       if( hrnCallee.isOutOfContext() ) {
2099         continue;
2100       }
2101
2102       AllocSite as = hrnCallee.getAllocSite();  
2103       allocSites.add( as );
2104
2105       Integer hrnIDshadow = as.getShadowIDfromID( hrnCallee.getID() );
2106
2107       HeapRegionNode hrnCaller = id2hrn.get( hrnIDshadow );
2108       if( hrnCaller == null ) {
2109         hrnCaller =
2110           createNewHeapRegionNode( hrnIDshadow,                // id or null to generate a new one 
2111                                    hrnCallee.isSingleObject(), // single object?                 
2112                                    hrnCallee.isNewSummary(),   // summary?       
2113                                    hrnCallee.isFlagged(),      // flagged?
2114                                    false,                      // out-of-context?
2115                                    hrnCallee.getType(),        // type                           
2116                                    hrnCallee.getAllocSite(),   // allocation site                        
2117                                    toCallerContext( hrnCallee.getInherent(),
2118                                                     calleeStatesSatisfied  ),    // inherent reach
2119                                    null,                       // current reach                 
2120                                    predsEmpty,                 // predicates
2121                                    hrnCallee.getDescription()  // description
2122                                    );                                        
2123       } else {
2124         assert hrnCaller.isWiped();
2125       }
2126
2127       hrnCaller.setAlpha( toCallerContext( hrnCallee.getAlpha(),
2128                                            calleeStatesSatisfied 
2129                                            )
2130                           );
2131
2132       hrnCaller.setPreds( preds );
2133     }
2134
2135
2136
2137     if( writeDebugDOTs ) {
2138       try {
2139         writeGraph( "caller31BeforeAddingEdges", 
2140                     true, false, false, false, true, true );
2141       } catch( IOException e ) {}
2142     }
2143
2144
2145     // 3.b) callee -> callee edges AND out-of-context -> callee
2146     satisItr = calleeEdgesSatisfied.entrySet().iterator();
2147     while( satisItr.hasNext() ) {
2148       Map.Entry    me       = (Map.Entry)    satisItr.next();
2149       RefEdge      reCallee = (RefEdge)      me.getKey();
2150       ExistPredSet preds    = (ExistPredSet) me.getValue();
2151
2152       HeapRegionNode hrnDstCallee = reCallee.getDst();
2153       AllocSite      asDst        = hrnDstCallee.getAllocSite();
2154       allocSites.add( asDst );
2155
2156       Integer hrnIDDstShadow = 
2157         asDst.getShadowIDfromID( hrnDstCallee.getID() );
2158       
2159       HeapRegionNode hrnDstCaller = id2hrn.get( hrnIDDstShadow );
2160       assert hrnDstCaller != null;
2161       
2162       
2163       RefSrcNode rsnCallee = reCallee.getSrc();
2164
2165       Set<RefSrcNode> rsnCallers =
2166         new HashSet<RefSrcNode>();
2167       
2168       Set<RefSrcNode> oocCallers = 
2169         calleeEdges2oocCallerSrcMatches.get( reCallee );
2170       
2171       if( oocCallers == null ) {
2172         // there are no out-of-context matches, so it's
2173         // either a param/arg var or one in-context heap region
2174         if( rsnCallee instanceof VariableNode ) {
2175           // variable -> node in the callee should only
2176           // come into the caller if its from a param var
2177           VariableNode   vnCallee = (VariableNode) rsnCallee;
2178           TempDescriptor tdParam  = vnCallee.getTempDescriptor();
2179           TempDescriptor tdArg    = fc.getArgMatchingParam( fmCallee,
2180                                                             tdParam );
2181           if( tdArg == null ) {
2182             // this means the variable isn't a parameter, its local
2183             // to the callee so we ignore it in call site transfer
2184             // shouldn't this NEVER HAPPEN?
2185             assert false;
2186             //continue;
2187           }
2188           rsnCallers.add( this.getVariableNodeFromTemp( tdArg ) );
2189
2190         } else {
2191           // otherwise source is in context, one region
2192           HeapRegionNode hrnSrcCallee = (HeapRegionNode) rsnCallee;
2193
2194           // translate an in-context node to shadow
2195           AllocSite asSrc = hrnSrcCallee.getAllocSite();
2196           allocSites.add( asSrc );
2197           
2198           Integer hrnIDSrcShadow = 
2199             asSrc.getShadowIDfromID( hrnSrcCallee.getID() );
2200
2201           HeapRegionNode hrnSrcCallerShadow =
2202             this.id2hrn.get( hrnIDSrcShadow );
2203           
2204           if( hrnSrcCallerShadow == null ) {
2205             hrnSrcCallerShadow =
2206               createNewHeapRegionNode( hrnIDSrcShadow,                // id or null to generate a new one 
2207                                        hrnSrcCallee.isSingleObject(), // single object?          
2208                                        hrnSrcCallee.isNewSummary(),   // summary?        
2209                                        hrnSrcCallee.isFlagged(),      // flagged?
2210                                        false,                         // out-of-context?
2211                                        hrnSrcCallee.getType(),        // type                            
2212                                        hrnSrcCallee.getAllocSite(),   // allocation site                         
2213                                        toCallerContext( hrnSrcCallee.getInherent(),
2214                                                         calleeStatesSatisfied ),    // inherent reach
2215                                        toCallerContext( hrnSrcCallee.getAlpha(),
2216                                                         calleeStatesSatisfied ),       // current reach                 
2217                                        predsEmpty,                    // predicates
2218                                        hrnSrcCallee.getDescription()  // description
2219                                        );                                        
2220           }
2221           
2222           rsnCallers.add( hrnSrcCallerShadow );
2223         }
2224
2225       } else {
2226         // otherwise we have a set of out-of-context srcs
2227         // that should NOT be translated to shadow nodes
2228         assert !oocCallers.isEmpty();
2229         rsnCallers.addAll( oocCallers );
2230       }
2231
2232       // now make all caller edges we've identified from
2233       // this callee edge with a satisfied predicate
2234       assert !rsnCallers.isEmpty();
2235       Iterator<RefSrcNode> rsnItr = rsnCallers.iterator();
2236       while( rsnItr.hasNext() ) {
2237         RefSrcNode rsnCaller = rsnItr.next();
2238         
2239         // TODO: beta rewrites
2240         RefEdge reCaller = new RefEdge( rsnCaller,
2241                                         hrnDstCaller,
2242                                         reCallee.getType(),
2243                                         reCallee.getField(),
2244                                         toCallerContext( reCallee.getBeta(),
2245                                                          calleeStatesSatisfied ),
2246                                         preds
2247                                         );
2248         
2249         // look to see if an edge with same field exists
2250         // and merge with it, otherwise just add the edge
2251         RefEdge edgeExisting = rsnCaller.getReferenceTo( hrnDstCaller,
2252                                                          reCallee.getType(),
2253                                                          reCallee.getField()
2254                                                          );     
2255         if( edgeExisting != null ) {
2256           edgeExisting.setBeta(
2257                                Canonical.union( edgeExisting.getBeta(),
2258                                                 reCaller.getBeta()
2259                                                 )
2260                                );
2261           edgeExisting.setPreds(
2262                                 Canonical.join( edgeExisting.getPreds(),
2263                                                 reCaller.getPreds()
2264                                                 )
2265                                 );
2266           
2267         } else {                          
2268           addRefEdge( rsnCaller, hrnDstCaller, reCaller );      
2269         }
2270       }
2271     }
2272
2273
2274
2275
2276
2277     if( writeDebugDOTs ) {
2278       try {
2279         writeGraph( "caller35BeforeAssignReturnValue", 
2280                     true, false, false, false, true, true );
2281       } catch( IOException e ) {}
2282     }
2283
2284
2285
2286     // TODO: WAIT! THIS SHOULD BE MERGED INTO OTHER PARTS, BECAUSE
2287     // AS IT IS WE'RE NOT VERIFYING PREDICATES OF RETURN VALUE
2288     // EDGES, JUST BRINGING THEM ALL!  It'll work for now, over approximation
2289     
2290     // 3.d) handle return value assignment if needed
2291     TempDescriptor returnTemp = fc.getReturnTemp();
2292     if( returnTemp != null && !returnTemp.getType().isImmutable() ) {
2293
2294       VariableNode vnLhsCaller = getVariableNodeFromTemp( returnTemp );
2295       clearRefEdgesFrom( vnLhsCaller, null, null, true );
2296
2297       VariableNode vnReturnCallee = rgCallee.getVariableNodeFromTemp( tdReturn );
2298       Iterator<RefEdge> reCalleeItr = vnReturnCallee.iteratorToReferencees();
2299       while( reCalleeItr.hasNext() ) {
2300         RefEdge        reCallee     = reCalleeItr.next();
2301         HeapRegionNode hrnDstCallee = reCallee.getDst();
2302
2303         // some edge types are not possible return values when we can
2304         // see what type variable we are assigning it to
2305         if( !isSuperiorType( returnTemp.getType(), reCallee.getType() ) ) {
2306           System.out.println( "*** NOT EXPECTING TO SEE THIS: Throwing out "+
2307                               reCallee+" for return temp "+returnTemp );
2308           // prune
2309           continue;
2310         }       
2311
2312         AllocSite asDst = hrnDstCallee.getAllocSite();
2313         allocSites.add( asDst );
2314
2315         Integer hrnIDDstShadow = asDst.getShadowIDfromID( hrnDstCallee.getID() );
2316
2317         HeapRegionNode hrnDstCaller = id2hrn.get( hrnIDDstShadow );
2318         if( hrnDstCaller == null ) {
2319           hrnDstCaller =
2320             createNewHeapRegionNode( hrnIDDstShadow,                // id or null to generate a new one 
2321                                      hrnDstCallee.isSingleObject(), // single object?            
2322                                      hrnDstCallee.isNewSummary(),   // summary?  
2323                                      hrnDstCallee.isFlagged(),      // flagged?
2324                                      false,                         // out-of-context?
2325                                      hrnDstCallee.getType(),        // type                              
2326                                      hrnDstCallee.getAllocSite(),   // allocation site                   
2327                                      toCallerContext( hrnDstCallee.getInherent(),
2328                                                       calleeStatesSatisfied  ),    // inherent reach
2329                                      toCallerContext( hrnDstCallee.getAlpha(),
2330                                                       calleeStatesSatisfied  ),    // current reach                 
2331                                      predsTrue,                     // predicates
2332                                      hrnDstCallee.getDescription()  // description
2333                                      );                                        
2334         } else {
2335           assert hrnDstCaller.isWiped();
2336         }
2337
2338         TypeDescriptor tdNewEdge =
2339           mostSpecificType( reCallee.getType(),
2340                             hrnDstCallee.getType(),
2341                             hrnDstCaller.getType()
2342                             );        
2343
2344         RefEdge reCaller = new RefEdge( vnLhsCaller,
2345                                         hrnDstCaller,
2346                                         tdNewEdge,
2347                                         null,
2348                                         toCallerContext( reCallee.getBeta(),
2349                                                          calleeStatesSatisfied ),
2350                                         predsTrue
2351                                         );
2352
2353         addRefEdge( vnLhsCaller, hrnDstCaller, reCaller );
2354       }
2355     }
2356
2357
2358
2359     if( writeDebugDOTs ) {
2360       try {
2361         writeGraph( "caller40BeforeShadowMerge", 
2362                     true, false, false, false, true, true );
2363       } catch( IOException e ) {}
2364     }
2365     
2366
2367     // 4) merge shadow nodes so alloc sites are back to k
2368     Iterator<AllocSite> asItr = rgCallee.allocSites.iterator();
2369     while( asItr.hasNext() ) {
2370       // for each allocation site do the following to merge
2371       // shadow nodes (newest from callee) with any existing
2372       // look for the newest normal and newest shadow "slot"
2373       // not being used, transfer normal to shadow.  Keep
2374       // doing this until there are no more normal nodes, or
2375       // no empty shadow slots: then merge all remaining normal
2376       // nodes into the shadow summary.  Finally, convert all
2377       // shadow to their normal versions.
2378       AllocSite as = asItr.next();
2379       int ageNorm = 0;
2380       int ageShad = 0;
2381       while( ageNorm < allocationDepth &&
2382              ageShad < allocationDepth ) {
2383
2384         // first, are there any normal nodes left?
2385         Integer        idNorm  = as.getIthOldest( ageNorm );
2386         HeapRegionNode hrnNorm = id2hrn.get( idNorm );
2387         if( hrnNorm == null ) {
2388           // no, this age of normal node not in the caller graph
2389           ageNorm++;
2390           continue;
2391         }
2392
2393         // yes, a normal node exists, is there an empty shadow
2394         // "slot" to transfer it onto?
2395         HeapRegionNode hrnShad = getIthNode( as, ageShad, true );        
2396         if( !hrnShad.isWiped() ) {
2397           // no, this age of shadow node is not empty
2398           ageShad++;
2399           continue;
2400         }
2401  
2402         // yes, this shadow node is empty
2403         transferOnto( hrnNorm, hrnShad );
2404         ageNorm++;
2405         ageShad++;
2406       }
2407
2408       // now, while there are still normal nodes but no shadow
2409       // slots, merge normal nodes into the shadow summary
2410       while( ageNorm < allocationDepth ) {
2411
2412         // first, are there any normal nodes left?
2413         Integer        idNorm  = as.getIthOldest( ageNorm );
2414         HeapRegionNode hrnNorm = id2hrn.get( idNorm );
2415         if( hrnNorm == null ) {
2416           // no, this age of normal node not in the caller graph
2417           ageNorm++;
2418           continue;
2419         }
2420
2421         // yes, a normal node exists, so get the shadow summary
2422         HeapRegionNode summShad = getSummaryNode( as, true );
2423         mergeIntoSummary( hrnNorm, summShad );
2424         ageNorm++;
2425       }
2426
2427       // if there is a normal summary, merge it into shadow summary
2428       Integer        idNorm   = as.getSummary();
2429       HeapRegionNode summNorm = id2hrn.get( idNorm );
2430       if( summNorm != null ) {
2431         HeapRegionNode summShad = getSummaryNode( as, true );
2432         mergeIntoSummary( summNorm, summShad );
2433       }
2434       
2435       // finally, flip all existing shadow nodes onto the normal
2436       for( int i = 0; i < allocationDepth; ++i ) {
2437         Integer        idShad  = as.getIthOldestShadow( i );
2438         HeapRegionNode hrnShad = id2hrn.get( idShad );
2439         if( hrnShad != null ) {
2440           // flip it
2441           HeapRegionNode hrnNorm = getIthNode( as, i, false );
2442           assert hrnNorm.isWiped();
2443           transferOnto( hrnShad, hrnNorm );
2444         }
2445       }
2446       
2447       Integer        idShad   = as.getSummaryShadow();
2448       HeapRegionNode summShad = id2hrn.get( idShad );
2449       if( summShad != null ) {
2450         summNorm = getSummaryNode( as, false );
2451         transferOnto( summShad, summNorm );
2452       }      
2453     }
2454
2455
2456     if( writeDebugDOTs ) {
2457       try {
2458         writeGraph( "caller45BeforeUnshadow", 
2459                     true, false, false, false, true, true );
2460       } catch( IOException e ) {}
2461     }
2462     
2463     
2464     Iterator itrAllHRNodes = id2hrn.entrySet().iterator();
2465     while( itrAllHRNodes.hasNext() ) {
2466       Map.Entry      me  = (Map.Entry)      itrAllHRNodes.next();
2467       HeapRegionNode hrn = (HeapRegionNode) me.getValue();
2468       
2469       hrn.setAlpha( unshadow( hrn.getAlpha() ) );
2470       
2471       Iterator<RefEdge> itrEdges = hrn.iteratorToReferencers();
2472       while( itrEdges.hasNext() ) {
2473         RefEdge re = itrEdges.next();
2474         re.setBeta( unshadow( re.getBeta() ) );
2475       }
2476     }
2477     
2478
2479
2480     if( writeDebugDOTs ) {
2481       try {
2482         writeGraph( "caller50BeforeGlobalSweep", 
2483                     true, false, false, false, true, true );
2484       } catch( IOException e ) {}
2485     }
2486
2487
2488     // 5.
2489     if( !DISABLE_GLOBAL_SWEEP ) {
2490       globalSweep();
2491     }
2492     
2493
2494
2495     if( writeDebugDOTs ) {
2496       try {
2497         writeGraph( "caller90AfterTransfer", 
2498                     true, false, false, false, true, true );
2499       } catch( IOException e ) {}
2500     }
2501   } 
2502
2503   
2504
2505   ////////////////////////////////////////////////////
2506   //
2507   //  Abstract garbage collection simply removes
2508   //  heap region nodes that are not mechanically
2509   //  reachable from a root set.  This step is
2510   //  essential for testing node and edge existence
2511   //  predicates efficiently
2512   //
2513   ////////////////////////////////////////////////////
2514   public void abstractGarbageCollect( Set<TempDescriptor> liveSet ) {
2515
2516     // calculate a root set, will be different for Java
2517     // version of analysis versus Bamboo version
2518     Set<RefSrcNode> toVisit = new HashSet<RefSrcNode>();
2519
2520     // visit every variable in graph while building root
2521     // set, and do iterating on a copy, so we can remove
2522     // dead variables while we're at this
2523     Iterator makeCopyItr = td2vn.entrySet().iterator();
2524     Set      entrysCopy  = new HashSet();
2525     while( makeCopyItr.hasNext() ) {
2526       entrysCopy.add( makeCopyItr.next() );
2527     }
2528     
2529     Iterator eItr = entrysCopy.iterator();
2530     while( eItr.hasNext() ) {
2531       Map.Entry      me = (Map.Entry)      eItr.next();
2532       TempDescriptor td = (TempDescriptor) me.getKey();
2533       VariableNode   vn = (VariableNode)   me.getValue();
2534
2535       if( liveSet.contains( td ) ) {
2536         toVisit.add( vn );
2537
2538       } else {
2539         // dead var, remove completely from graph
2540         td2vn.remove( td );
2541         clearRefEdgesFrom( vn, null, null, true );
2542       }
2543     }
2544
2545     // everything visited in a traversal is
2546     // considered abstractly live
2547     Set<RefSrcNode> visited = new HashSet<RefSrcNode>();
2548     
2549     while( !toVisit.isEmpty() ) {
2550       RefSrcNode rsn = toVisit.iterator().next();
2551       toVisit.remove( rsn );
2552       visited.add( rsn );
2553       
2554       Iterator<RefEdge> hrnItr = rsn.iteratorToReferencees();
2555       while( hrnItr.hasNext() ) {
2556         RefEdge        edge = hrnItr.next();
2557         HeapRegionNode hrn  = edge.getDst();
2558         
2559         if( !visited.contains( hrn ) ) {
2560           toVisit.add( hrn );
2561         }
2562       }
2563     }
2564
2565     // get a copy of the set to iterate over because
2566     // we're going to monkey with the graph when we
2567     // identify a garbage node
2568     Set<HeapRegionNode> hrnAllPrior = new HashSet<HeapRegionNode>();
2569     Iterator<HeapRegionNode> hrnItr = id2hrn.values().iterator();
2570     while( hrnItr.hasNext() ) {
2571       hrnAllPrior.add( hrnItr.next() );
2572     }
2573
2574     Iterator<HeapRegionNode> hrnAllItr = hrnAllPrior.iterator();
2575     while( hrnAllItr.hasNext() ) {
2576       HeapRegionNode hrn = hrnAllItr.next();
2577
2578       if( !visited.contains( hrn ) ) {
2579
2580         // heap region nodes are compared across ReachGraph
2581         // objects by their integer ID, so when discarding
2582         // garbage nodes we must also discard entries in
2583         // the ID -> heap region hashtable.
2584         id2hrn.remove( hrn.getID() );
2585
2586         // RefEdge objects are two-way linked between
2587         // nodes, so when a node is identified as garbage,
2588         // actively clear references to and from it so
2589         // live nodes won't have dangling RefEdge's
2590         wipeOut( hrn, true );
2591
2592         // if we just removed the last node from an allocation
2593         // site, it should be taken out of the ReachGraph's list
2594         AllocSite as = hrn.getAllocSite();
2595         if( !hasNodesOf( as ) ) {
2596           allocSites.remove( as );
2597         }
2598       }
2599     }
2600   }
2601
2602   protected boolean hasNodesOf( AllocSite as ) {
2603     if( id2hrn.containsKey( as.getSummary() ) ) {
2604       return true;
2605     }
2606
2607     for( int i = 0; i < allocationDepth; ++i ) {
2608       if( id2hrn.containsKey( as.getIthOldest( i ) ) ) {
2609         return true;
2610       }      
2611     }
2612     return false;
2613   }
2614
2615
2616   ////////////////////////////////////////////////////
2617   //
2618   //  This global sweep is an optional step to prune
2619   //  reachability sets that are not internally
2620   //  consistent with the global graph.  It should be
2621   //  invoked after strong updates or method calls.
2622   //
2623   ////////////////////////////////////////////////////
2624   public void globalSweep() {
2625
2626     // boldB is part of the phase 1 sweep
2627     // it has an in-context table and an out-of-context table
2628     Hashtable< Integer, Hashtable<RefEdge, ReachSet> > boldBic =
2629       new Hashtable< Integer, Hashtable<RefEdge, ReachSet> >();    
2630
2631     Hashtable< Integer, Hashtable<RefEdge, ReachSet> > boldBooc =
2632       new Hashtable< Integer, Hashtable<RefEdge, ReachSet> >();    
2633
2634     // visit every heap region to initialize alphaNew and betaNew,
2635     // and make a map of every hrnID to the source nodes it should
2636     // propagate forward from.  In-context flagged hrnID's propagate
2637     // from only the in-context node they name, but out-of-context
2638     // ID's may propagate from several out-of-context nodes
2639     Hashtable< Integer, Set<HeapRegionNode> > icID2srcs = 
2640       new Hashtable< Integer, Set<HeapRegionNode> >();
2641
2642     Hashtable< Integer, Set<HeapRegionNode> > oocID2srcs =
2643       new Hashtable< Integer, Set<HeapRegionNode> >();
2644
2645
2646     Iterator itrHrns = id2hrn.entrySet().iterator();
2647     while( itrHrns.hasNext() ) {
2648       Map.Entry      me    = (Map.Entry)      itrHrns.next();
2649       Integer        hrnID = (Integer)        me.getKey();
2650       HeapRegionNode hrn   = (HeapRegionNode) me.getValue();
2651     
2652       // assert that this node and incoming edges have clean alphaNew
2653       // and betaNew sets, respectively
2654       assert rsetEmpty.equals( hrn.getAlphaNew() );
2655
2656       Iterator<RefEdge> itrRers = hrn.iteratorToReferencers();
2657       while( itrRers.hasNext() ) {
2658         RefEdge edge = itrRers.next();
2659         assert rsetEmpty.equals( edge.getBetaNew() );
2660       }      
2661
2662       // calculate boldB for this flagged node, or out-of-context node
2663       if( hrn.isFlagged() ) {
2664         assert !hrn.isOutOfContext();
2665         assert !icID2srcs.containsKey( hrn.getID() );
2666         Set<HeapRegionNode> srcs = new HashSet<HeapRegionNode>();
2667         srcs.add( hrn );
2668         icID2srcs.put( hrn.getID(), srcs );
2669       }
2670
2671       if( hrn.isOutOfContext() ) {
2672         assert !hrn.isFlagged();
2673
2674         Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
2675         while( stateItr.hasNext() ) {
2676           ReachState state = stateItr.next();
2677
2678           Iterator<ReachTuple> rtItr = state.iterator();
2679           while( rtItr.hasNext() ) {
2680             ReachTuple rt = rtItr.next();
2681             assert rt.isOutOfContext();
2682
2683             Set<HeapRegionNode> srcs = oocID2srcs.get( rt.getHrnID() );
2684             if( srcs == null ) {
2685               srcs = new HashSet<HeapRegionNode>();
2686             }
2687             srcs.add( hrn );
2688             oocID2srcs.put( rt.getHrnID(), srcs );
2689           }
2690         }
2691       }
2692     }
2693
2694     // calculate boldB for all hrnIDs identified by the above
2695     // node traversal, propagating from every source
2696     while( !icID2srcs.isEmpty() || !oocID2srcs.isEmpty() ) {
2697
2698       Integer             hrnID;
2699       Set<HeapRegionNode> srcs;
2700       boolean             inContext;
2701
2702       if( !icID2srcs.isEmpty() ) {
2703         Map.Entry me = (Map.Entry) icID2srcs.entrySet().iterator().next();
2704         hrnID = (Integer)             me.getKey();
2705         srcs  = (Set<HeapRegionNode>) me.getValue();
2706         inContext = true;
2707         icID2srcs.remove( hrnID );
2708
2709       } else {
2710         assert !oocID2srcs.isEmpty();
2711
2712         Map.Entry me = (Map.Entry) oocID2srcs.entrySet().iterator().next();
2713         hrnID = (Integer)             me.getKey();
2714         srcs  = (Set<HeapRegionNode>) me.getValue();
2715         inContext = false;
2716         oocID2srcs.remove( hrnID );
2717       }
2718
2719
2720       Hashtable<RefEdge, ReachSet> boldB_f =
2721         new Hashtable<RefEdge, ReachSet>();
2722         
2723       Set<RefEdge> workSetEdges = new HashSet<RefEdge>();
2724
2725       Iterator<HeapRegionNode> hrnItr = srcs.iterator();
2726       while( hrnItr.hasNext() ) {
2727         HeapRegionNode hrn = hrnItr.next();
2728
2729         assert workSetEdges.isEmpty();
2730         
2731         // initial boldB_f constraints
2732         Iterator<RefEdge> itrRees = hrn.iteratorToReferencees();
2733         while( itrRees.hasNext() ) {
2734           RefEdge edge = itrRees.next();
2735           
2736           assert !boldB_f.containsKey( edge );
2737           boldB_f.put( edge, edge.getBeta() );
2738           
2739           assert !workSetEdges.contains( edge );
2740           workSetEdges.add( edge );
2741         }       
2742       
2743         // enforce the boldB_f constraint at edges until we reach a fixed point
2744         while( !workSetEdges.isEmpty() ) {
2745           RefEdge edge = workSetEdges.iterator().next();
2746           workSetEdges.remove( edge );   
2747           
2748           Iterator<RefEdge> itrPrime = edge.getDst().iteratorToReferencees();
2749           while( itrPrime.hasNext() ) {
2750             RefEdge edgePrime = itrPrime.next();            
2751           
2752             ReachSet prevResult   = boldB_f.get( edgePrime );
2753             ReachSet intersection = Canonical.intersection( boldB_f.get( edge ),
2754                                                             edgePrime.getBeta()
2755                                                             );
2756           
2757             if( prevResult == null || 
2758                 Canonical.union( prevResult,
2759                                  intersection ).size() > prevResult.size() ) {
2760             
2761               if( prevResult == null ) {
2762                 boldB_f.put( edgePrime, 
2763                              Canonical.union( edgePrime.getBeta(),
2764                                               intersection 
2765                                               )
2766                              );
2767               } else {
2768                 boldB_f.put( edgePrime, 
2769                              Canonical.union( prevResult,
2770                                               intersection 
2771                                               )
2772                              );
2773               }
2774               workSetEdges.add( edgePrime );    
2775             }
2776           }
2777         }
2778       }
2779       
2780       if( inContext ) {
2781         boldBic.put( hrnID, boldB_f );
2782       } else {
2783         boldBooc.put( hrnID, boldB_f );
2784       }
2785     }
2786
2787
2788     // use boldB to prune hrnIDs from alpha states that are impossible
2789     // and propagate the differences backwards across edges
2790     HashSet<RefEdge> edgesForPropagation = new HashSet<RefEdge>();
2791
2792     Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
2793       new Hashtable<RefEdge, ChangeSet>();
2794
2795
2796     itrHrns = id2hrn.entrySet().iterator();
2797     while( itrHrns.hasNext() ) {
2798       Map.Entry      me    = (Map.Entry)      itrHrns.next();
2799       Integer        hrnID = (Integer)        me.getKey();
2800       HeapRegionNode hrn   = (HeapRegionNode) me.getValue();
2801       
2802       // out-of-context nodes don't participate in the 
2803       // global sweep, they serve as sources for the pass
2804       // performed above
2805       if( hrn.isOutOfContext() ) {
2806         continue;
2807       }
2808
2809       // the inherent states of a region are the exception
2810       // to removal as the global sweep prunes
2811       ReachTuple rtException = ReachTuple.factory( hrnID,
2812                                                    !hrn.isSingleObject(),    
2813                                                    ReachTuple.ARITY_ONE,
2814                                                    false // out-of-context
2815                                                    );
2816
2817       ChangeSet cts = ChangeSet.factory();
2818
2819       // mark hrnIDs for removal
2820       Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
2821       while( stateItr.hasNext() ) {
2822         ReachState stateOld = stateItr.next();
2823
2824         ReachState markedHrnIDs = ReachState.factory();
2825
2826         Iterator<ReachTuple> rtItr = stateOld.iterator();
2827         while( rtItr.hasNext() ) {
2828           ReachTuple rtOld = rtItr.next();
2829
2830           // never remove the inherent hrnID from a flagged region
2831           // because it is trivially satisfied
2832           if( hrn.isFlagged() ) {       
2833             if( rtOld == rtException ) {
2834               continue;
2835             }
2836           }
2837
2838           // does boldB allow this hrnID?
2839           boolean foundState = false;
2840           Iterator<RefEdge> incidentEdgeItr = hrn.iteratorToReferencers();
2841           while( incidentEdgeItr.hasNext() ) {
2842             RefEdge incidentEdge = incidentEdgeItr.next();
2843
2844             Hashtable<RefEdge, ReachSet> B; 
2845             if( rtOld.isOutOfContext() ) {
2846               B = boldBooc.get( rtOld.getHrnID() ); 
2847             } else {
2848               assert id2hrn.containsKey( rtOld.getHrnID() );
2849               B = boldBic.get( rtOld.getHrnID() ); 
2850             }
2851               
2852             ReachSet boldB_rtOld_incident = B.get( incidentEdge );
2853             if( boldB_rtOld_incident != null &&
2854                 boldB_rtOld_incident.contains( stateOld ) ) {
2855               foundState = true;
2856             }
2857           }
2858           
2859           if( !foundState ) {
2860             markedHrnIDs = Canonical.add( markedHrnIDs, rtOld );          
2861           }
2862         }
2863
2864         // if there is nothing marked, just move on
2865         if( markedHrnIDs.isEmpty() ) {
2866           hrn.setAlphaNew( Canonical.union( hrn.getAlphaNew(),
2867                                             stateOld
2868                                             )
2869                            );
2870           continue;
2871         }
2872
2873         // remove all marked hrnIDs and establish a change set that should
2874         // propagate backwards over edges from this node
2875         ReachState statePruned = ReachState.factory();
2876         rtItr = stateOld.iterator();
2877         while( rtItr.hasNext() ) {
2878           ReachTuple rtOld = rtItr.next();
2879
2880           if( !markedHrnIDs.containsTuple( rtOld ) ) {
2881             statePruned = Canonical.union( statePruned, rtOld );
2882           }
2883         }
2884         assert !stateOld.equals( statePruned );
2885
2886         hrn.setAlphaNew( Canonical.union( hrn.getAlphaNew(),
2887                                           statePruned
2888                                           )
2889                          );
2890         ChangeTuple ct = ChangeTuple.factory( stateOld,
2891                                               statePruned
2892                                               );
2893         cts = Canonical.union( cts, ct );
2894       }
2895
2896       // throw change tuple set on all incident edges
2897       if( !cts.isEmpty() ) {
2898         Iterator<RefEdge> incidentEdgeItr = hrn.iteratorToReferencers();
2899         while( incidentEdgeItr.hasNext() ) {
2900           RefEdge incidentEdge = incidentEdgeItr.next();
2901                   
2902           edgesForPropagation.add( incidentEdge );
2903
2904           if( edgePlannedChanges.get( incidentEdge ) == null ) {
2905             edgePlannedChanges.put( incidentEdge, cts );
2906           } else {          
2907             edgePlannedChanges.put( 
2908                                    incidentEdge, 
2909                                    Canonical.union( edgePlannedChanges.get( incidentEdge ),
2910                                                     cts
2911                                                     ) 
2912                                     );
2913           }
2914         }
2915       }
2916     }
2917     
2918     HashSet<RefEdge> edgesUpdated = new HashSet<RefEdge>();
2919
2920     propagateTokensOverEdges( edgesForPropagation,
2921                               edgePlannedChanges,
2922                               edgesUpdated );
2923
2924     // at the end of the 1st phase reference edges have
2925     // beta, betaNew that correspond to beta and betaR
2926     //
2927     // commit beta<-betaNew, so beta=betaR and betaNew
2928     // will represent the beta' calculation in 2nd phase
2929     //
2930     // commit alpha<-alphaNew because it won't change
2931     HashSet<RefEdge> res = new HashSet<RefEdge>();
2932
2933     Iterator<HeapRegionNode> nodeItr = id2hrn.values().iterator();
2934     while( nodeItr.hasNext() ) {
2935       HeapRegionNode hrn = nodeItr.next();
2936       hrn.applyAlphaNew();
2937       Iterator<RefEdge> itrRes = hrn.iteratorToReferencers();
2938       while( itrRes.hasNext() ) {
2939         res.add( itrRes.next() );
2940       }
2941     }
2942
2943
2944     // 2nd phase    
2945     Iterator<RefEdge> edgeItr = res.iterator();
2946     while( edgeItr.hasNext() ) {
2947       RefEdge        edge = edgeItr.next();
2948       HeapRegionNode hrn  = edge.getDst();
2949
2950       // commit results of last phase
2951       if( edgesUpdated.contains( edge ) ) {
2952         edge.applyBetaNew();
2953       }
2954
2955       // compute intial condition of 2nd phase
2956       edge.setBetaNew( Canonical.intersection( edge.getBeta(),
2957                                                hrn.getAlpha() 
2958                                                )
2959                        );
2960     }
2961         
2962     // every edge in the graph is the initial workset
2963     Set<RefEdge> edgeWorkSet = (Set) res.clone();
2964     while( !edgeWorkSet.isEmpty() ) {
2965       RefEdge edgePrime = edgeWorkSet.iterator().next();
2966       edgeWorkSet.remove( edgePrime );
2967
2968       RefSrcNode rsn = edgePrime.getSrc();
2969       if( !(rsn instanceof HeapRegionNode) ) {
2970         continue;
2971       }
2972       HeapRegionNode hrn = (HeapRegionNode) rsn;
2973
2974       Iterator<RefEdge> itrEdge = hrn.iteratorToReferencers();
2975       while( itrEdge.hasNext() ) {
2976         RefEdge edge = itrEdge.next();      
2977
2978         ReachSet prevResult = edge.getBetaNew();
2979         assert prevResult != null;
2980
2981         ReachSet intersection = 
2982           Canonical.intersection( edge.getBeta(),
2983                                   edgePrime.getBetaNew() 
2984                                   );
2985                     
2986         if( Canonical.union( prevResult,
2987                              intersection
2988                              ).size() > prevResult.size() ) {
2989           edge.setBetaNew( 
2990                           Canonical.union( prevResult,
2991                                            intersection 
2992                                            )
2993                            );
2994           edgeWorkSet.add( edge );
2995         }       
2996       }      
2997     }
2998
2999     // commit beta' (beta<-betaNew)
3000     edgeItr = res.iterator();
3001     while( edgeItr.hasNext() ) {
3002       edgeItr.next().applyBetaNew();
3003     } 
3004   }  
3005
3006
3007
3008   ////////////////////////////////////////////////////
3009   // high-level merge operations
3010   ////////////////////////////////////////////////////
3011   public void merge_sameMethodContext( ReachGraph rg ) {
3012     // when merging two graphs that abstract the heap
3013     // of the same method context, we just call the
3014     // basic merge operation
3015     merge( rg );
3016   }
3017
3018   public void merge_diffMethodContext( ReachGraph rg ) {
3019     // when merging graphs for abstract heaps in
3020     // different method contexts we should:
3021     // 1) age the allocation sites?
3022     merge( rg );
3023   }
3024
3025   ////////////////////////////////////////////////////
3026   // in merge() and equals() methods the suffix A
3027   // represents the passed in graph and the suffix
3028   // B refers to the graph in this object
3029   // Merging means to take the incoming graph A and
3030   // merge it into B, so after the operation graph B
3031   // is the final result.
3032   ////////////////////////////////////////////////////
3033   protected void merge( ReachGraph rg ) {
3034
3035     if( rg == null ) {
3036       return;
3037     }
3038
3039     mergeNodes     ( rg );
3040     mergeRefEdges  ( rg );
3041     mergeAllocSites( rg );
3042   }
3043   
3044   protected void mergeNodes( ReachGraph rg ) {
3045
3046     // start with heap region nodes
3047     Set      sA = rg.id2hrn.entrySet();
3048     Iterator iA = sA.iterator();
3049     while( iA.hasNext() ) {
3050       Map.Entry      meA  = (Map.Entry)      iA.next();
3051       Integer        idA  = (Integer)        meA.getKey();
3052       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3053
3054       // if this graph doesn't have a node the
3055       // incoming graph has, allocate it
3056       if( !id2hrn.containsKey( idA ) ) {
3057         HeapRegionNode hrnB = hrnA.copy();
3058         id2hrn.put( idA, hrnB );
3059
3060       } else {
3061         // otherwise this is a node present in both graphs
3062         // so make the new reachability set a union of the
3063         // nodes' reachability sets
3064         HeapRegionNode hrnB = id2hrn.get( idA );
3065         hrnB.setAlpha( Canonical.union( hrnB.getAlpha(),
3066                                         hrnA.getAlpha() 
3067                                         )
3068                        );
3069
3070         // if hrnB is already dirty or hrnA is dirty,
3071         // the hrnB should end up dirty: TODO
3072         /*
3073         if( !hrnA.isClean() ) {
3074           hrnB.setIsClean( false );
3075         }
3076         */
3077       }
3078     }
3079
3080     // now add any variable nodes that are in graph B but
3081     // not in A
3082     sA = rg.td2vn.entrySet();
3083     iA = sA.iterator();
3084     while( iA.hasNext() ) {
3085       Map.Entry      meA = (Map.Entry)      iA.next();
3086       TempDescriptor tdA = (TempDescriptor) meA.getKey();
3087       VariableNode   lnA = (VariableNode)   meA.getValue();
3088
3089       // if the variable doesn't exist in B, allocate and add it
3090       VariableNode lnB = getVariableNodeFromTemp( tdA );
3091     }
3092   }
3093
3094   protected void mergeRefEdges( ReachGraph rg ) {
3095
3096     // between heap regions
3097     Set      sA = rg.id2hrn.entrySet();
3098     Iterator iA = sA.iterator();
3099     while( iA.hasNext() ) {
3100       Map.Entry      meA  = (Map.Entry)      iA.next();
3101       Integer        idA  = (Integer)        meA.getKey();
3102       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3103
3104       Iterator<RefEdge> heapRegionsItrA = hrnA.iteratorToReferencees();
3105       while( heapRegionsItrA.hasNext() ) {
3106         RefEdge        edgeA     = heapRegionsItrA.next();
3107         HeapRegionNode hrnChildA = edgeA.getDst();
3108         Integer        idChildA  = hrnChildA.getID();
3109
3110         // at this point we know an edge in graph A exists
3111         // idA -> idChildA, does this exist in B?
3112         assert id2hrn.containsKey( idA );
3113         HeapRegionNode hrnB        = id2hrn.get( idA );
3114         RefEdge        edgeToMerge = null;
3115
3116         Iterator<RefEdge> heapRegionsItrB = hrnB.iteratorToReferencees();
3117         while( heapRegionsItrB.hasNext() &&
3118                edgeToMerge == null          ) {
3119
3120           RefEdge        edgeB     = heapRegionsItrB.next();
3121           HeapRegionNode hrnChildB = edgeB.getDst();
3122           Integer        idChildB  = hrnChildB.getID();
3123
3124           // don't use the RefEdge.equals() here because
3125           // we're talking about existence between graphs,
3126           // not intragraph equal
3127           if( idChildB.equals( idChildA ) &&
3128               edgeB.typeAndFieldEquals( edgeA ) ) {
3129
3130             edgeToMerge = edgeB;
3131           }
3132         }
3133
3134         // if the edge from A was not found in B,
3135         // add it to B.
3136         if( edgeToMerge == null ) {
3137           assert id2hrn.containsKey( idChildA );
3138           HeapRegionNode hrnChildB = id2hrn.get( idChildA );
3139           edgeToMerge = edgeA.copy();
3140           edgeToMerge.setSrc( hrnB );
3141           edgeToMerge.setDst( hrnChildB );
3142           addRefEdge( hrnB, hrnChildB, edgeToMerge );
3143         }
3144         // otherwise, the edge already existed in both graphs
3145         // so merge their reachability sets
3146         else {
3147           // just replace this beta set with the union
3148           assert edgeToMerge != null;
3149           edgeToMerge.setBeta(
3150                               Canonical.union( edgeToMerge.getBeta(),
3151                                                edgeA.getBeta() 
3152                                                )
3153                               );
3154           // TODO: what?
3155           /*
3156           if( !edgeA.isClean() ) {
3157             edgeToMerge.setIsClean( false );
3158           }
3159           */
3160         }
3161       }
3162     }
3163
3164     // and then again from variable nodes
3165     sA = rg.td2vn.entrySet();
3166     iA = sA.iterator();
3167     while( iA.hasNext() ) {
3168       Map.Entry      meA = (Map.Entry)      iA.next();
3169       TempDescriptor tdA = (TempDescriptor) meA.getKey();
3170       VariableNode   vnA = (VariableNode)   meA.getValue();
3171
3172       Iterator<RefEdge> heapRegionsItrA = vnA.iteratorToReferencees();
3173       while( heapRegionsItrA.hasNext() ) {
3174         RefEdge        edgeA     = heapRegionsItrA.next();
3175         HeapRegionNode hrnChildA = edgeA.getDst();
3176         Integer        idChildA  = hrnChildA.getID();
3177
3178         // at this point we know an edge in graph A exists
3179         // tdA -> idChildA, does this exist in B?
3180         assert td2vn.containsKey( tdA );
3181         VariableNode vnB         = td2vn.get( tdA );
3182         RefEdge      edgeToMerge = null;
3183
3184         Iterator<RefEdge> heapRegionsItrB = vnB.iteratorToReferencees();
3185         while( heapRegionsItrB.hasNext() &&
3186                edgeToMerge == null          ) {
3187
3188           RefEdge        edgeB     = heapRegionsItrB.next();
3189           HeapRegionNode hrnChildB = edgeB.getDst();
3190           Integer        idChildB  = hrnChildB.getID();
3191
3192           // don't use the RefEdge.equals() here because
3193           // we're talking about existence between graphs
3194           if( idChildB.equals( idChildA ) &&
3195               edgeB.typeAndFieldEquals( edgeA ) ) {
3196
3197             edgeToMerge = edgeB;
3198           }
3199         }
3200
3201         // if the edge from A was not found in B,
3202         // add it to B.
3203         if( edgeToMerge == null ) {
3204           assert id2hrn.containsKey( idChildA );
3205           HeapRegionNode hrnChildB = id2hrn.get( idChildA );
3206           edgeToMerge = edgeA.copy();
3207           edgeToMerge.setSrc( vnB );
3208           edgeToMerge.setDst( hrnChildB );
3209           addRefEdge( vnB, hrnChildB, edgeToMerge );
3210         }
3211         // otherwise, the edge already existed in both graphs
3212         // so merge their reachability sets
3213         else {
3214           // just replace this beta set with the union
3215           edgeToMerge.setBeta( Canonical.union( edgeToMerge.getBeta(),
3216                                                 edgeA.getBeta()
3217                                                 )
3218                                );
3219           // TODO: what?
3220           /*
3221           if( !edgeA.isClean() ) {
3222             edgeToMerge.setIsClean( false );
3223           }
3224           */
3225         }
3226       }
3227     }
3228   }
3229
3230   protected void mergeAllocSites( ReachGraph rg ) {
3231     allocSites.addAll( rg.allocSites );
3232   }
3233
3234
3235   // it is necessary in the equals() member functions
3236   // to "check both ways" when comparing the data
3237   // structures of two graphs.  For instance, if all
3238   // edges between heap region nodes in graph A are
3239   // present and equal in graph B it is not sufficient
3240   // to say the graphs are equal.  Consider that there
3241   // may be edges in graph B that are not in graph A.
3242   // the only way to know that all edges in both graphs
3243   // are equally present is to iterate over both data
3244   // structures and compare against the other graph.
3245   public boolean equals( ReachGraph rg ) {
3246
3247     if( rg == null ) {
3248       return false;
3249     }
3250     
3251     if( !areHeapRegionNodesEqual( rg ) ) {
3252       return false;
3253     }
3254
3255     if( !areVariableNodesEqual( rg ) ) {
3256       return false;
3257     }
3258
3259     if( !areRefEdgesEqual( rg ) ) {
3260       return false;
3261     }
3262
3263     // if everything is equal up to this point,
3264     // assert that allocSites is also equal--
3265     // this data is redundant but kept for efficiency
3266     assert allocSites.equals( rg.allocSites );
3267
3268     return true;
3269   }
3270
3271   
3272   protected boolean areHeapRegionNodesEqual( ReachGraph rg ) {
3273
3274     if( !areallHRNinAalsoinBandequal( this, rg ) ) {
3275       return false;
3276     }
3277
3278     if( !areallHRNinAalsoinBandequal( rg, this ) ) {
3279       return false;
3280     }
3281
3282     return true;
3283   }
3284
3285   static protected boolean areallHRNinAalsoinBandequal( ReachGraph rgA,
3286                                                         ReachGraph rgB ) {
3287     Set      sA = rgA.id2hrn.entrySet();
3288     Iterator iA = sA.iterator();
3289     while( iA.hasNext() ) {
3290       Map.Entry      meA  = (Map.Entry)      iA.next();
3291       Integer        idA  = (Integer)        meA.getKey();
3292       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3293
3294       if( !rgB.id2hrn.containsKey( idA ) ) {
3295         return false;
3296       }
3297
3298       HeapRegionNode hrnB = rgB.id2hrn.get( idA );
3299       if( !hrnA.equalsIncludingAlphaAndPreds( hrnB ) ) {
3300         return false;
3301       }
3302     }
3303     
3304     return true;
3305   }
3306   
3307
3308   protected boolean areVariableNodesEqual( ReachGraph rg ) {
3309
3310     if( !areallVNinAalsoinBandequal( this, rg ) ) {
3311       return false;
3312     }
3313
3314     if( !areallVNinAalsoinBandequal( rg, this ) ) {
3315       return false;
3316     }
3317
3318     return true;
3319   }
3320
3321   static protected boolean areallVNinAalsoinBandequal( ReachGraph rgA,
3322                                                        ReachGraph rgB ) {
3323     Set      sA = rgA.td2vn.entrySet();
3324     Iterator iA = sA.iterator();
3325     while( iA.hasNext() ) {
3326       Map.Entry      meA = (Map.Entry)      iA.next();
3327       TempDescriptor tdA = (TempDescriptor) meA.getKey();
3328
3329       if( !rgB.td2vn.containsKey( tdA ) ) {
3330         return false;
3331       }
3332     }
3333
3334     return true;
3335   }
3336
3337
3338   protected boolean areRefEdgesEqual( ReachGraph rg ) {
3339     if( !areallREinAandBequal( this, rg ) ) {
3340       return false;
3341     }
3342
3343     return true;
3344   }
3345
3346   static protected boolean areallREinAandBequal( ReachGraph rgA,
3347                                                  ReachGraph rgB ) {
3348
3349     // check all the heap region->heap region edges
3350     Set      sA = rgA.id2hrn.entrySet();
3351     Iterator iA = sA.iterator();
3352     while( iA.hasNext() ) {
3353       Map.Entry      meA  = (Map.Entry)      iA.next();
3354       Integer        idA  = (Integer)        meA.getKey();
3355       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3356
3357       // we should have already checked that the same
3358       // heap regions exist in both graphs
3359       assert rgB.id2hrn.containsKey( idA );
3360
3361       if( !areallREfromAequaltoB( rgA, hrnA, rgB ) ) {
3362         return false;
3363       }
3364
3365       // then check every edge in B for presence in A, starting
3366       // from the same parent HeapRegionNode
3367       HeapRegionNode hrnB = rgB.id2hrn.get( idA );
3368
3369       if( !areallREfromAequaltoB( rgB, hrnB, rgA ) ) {
3370         return false;
3371       }
3372     }
3373
3374     // then check all the variable->heap region edges
3375     sA = rgA.td2vn.entrySet();
3376     iA = sA.iterator();
3377     while( iA.hasNext() ) {
3378       Map.Entry      meA = (Map.Entry)      iA.next();
3379       TempDescriptor tdA = (TempDescriptor) meA.getKey();
3380       VariableNode   vnA = (VariableNode)   meA.getValue();
3381
3382       // we should have already checked that the same
3383       // label nodes exist in both graphs
3384       assert rgB.td2vn.containsKey( tdA );
3385
3386       if( !areallREfromAequaltoB( rgA, vnA, rgB ) ) {
3387         return false;
3388       }
3389
3390       // then check every edge in B for presence in A, starting
3391       // from the same parent VariableNode
3392       VariableNode vnB = rgB.td2vn.get( tdA );
3393
3394       if( !areallREfromAequaltoB( rgB, vnB, rgA ) ) {
3395         return false;
3396       }
3397     }
3398
3399     return true;
3400   }
3401
3402
3403   static protected boolean areallREfromAequaltoB( ReachGraph rgA,
3404                                                   RefSrcNode rnA,
3405                                                   ReachGraph rgB ) {
3406
3407     Iterator<RefEdge> itrA = rnA.iteratorToReferencees();
3408     while( itrA.hasNext() ) {
3409       RefEdge        edgeA     = itrA.next();
3410       HeapRegionNode hrnChildA = edgeA.getDst();
3411       Integer        idChildA  = hrnChildA.getID();
3412
3413       assert rgB.id2hrn.containsKey( idChildA );
3414
3415       // at this point we know an edge in graph A exists
3416       // rnA -> idChildA, does this exact edge exist in B?
3417       boolean edgeFound = false;
3418
3419       RefSrcNode rnB = null;
3420       if( rnA instanceof HeapRegionNode ) {
3421         HeapRegionNode hrnA = (HeapRegionNode) rnA;
3422         rnB = rgB.id2hrn.get( hrnA.getID() );
3423       } else {
3424         VariableNode vnA = (VariableNode) rnA;
3425         rnB = rgB.td2vn.get( vnA.getTempDescriptor() );
3426       }
3427
3428       Iterator<RefEdge> itrB = rnB.iteratorToReferencees();
3429       while( itrB.hasNext() ) {
3430         RefEdge        edgeB     = itrB.next();
3431         HeapRegionNode hrnChildB = edgeB.getDst();
3432         Integer        idChildB  = hrnChildB.getID();
3433
3434         if( idChildA.equals( idChildB ) &&
3435             edgeA.typeAndFieldEquals( edgeB ) ) {
3436
3437           // there is an edge in the right place with the right field,
3438           // but do they have the same attributes?
3439           if( edgeA.getBeta().equals( edgeB.getBeta() ) &&
3440               edgeA.equalsPreds( edgeB )
3441               ) {
3442             edgeFound = true;
3443           }
3444         }
3445       }
3446       
3447       if( !edgeFound ) {
3448         return false;
3449       }
3450     }
3451
3452     return true;
3453   }
3454
3455
3456
3457   // this analysis no longer has the "match anything"
3458   // type which was represented by null
3459   protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
3460                                              TypeDescriptor td2 ) {
3461     assert td1 != null;
3462     assert td2 != null;
3463
3464     if( td1.isNull() ) {
3465       return td2;
3466     }
3467     if( td2.isNull() ) {
3468       return td1;
3469     }
3470     return typeUtil.mostSpecific( td1, td2 );
3471   }
3472   
3473   protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
3474                                              TypeDescriptor td2,
3475                                              TypeDescriptor td3 ) {
3476     
3477     return mostSpecificType( td1, 
3478                              mostSpecificType( td2, td3 )
3479                              );
3480   }  
3481   
3482   protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
3483                                              TypeDescriptor td2,
3484                                              TypeDescriptor td3,
3485                                              TypeDescriptor td4 ) {
3486     
3487     return mostSpecificType( mostSpecificType( td1, td2 ), 
3488                              mostSpecificType( td3, td4 )
3489                              );
3490   }  
3491
3492   protected boolean isSuperiorType( TypeDescriptor possibleSuper,
3493                                     TypeDescriptor possibleChild ) {
3494     assert possibleSuper != null;
3495     assert possibleChild != null;
3496     
3497     if( possibleSuper.isNull() ||
3498         possibleChild.isNull() ) {
3499       return true;
3500     }
3501
3502     return typeUtil.isSuperorType( possibleSuper, possibleChild );
3503   }
3504
3505
3506   protected boolean hasMatchingField( HeapRegionNode src, 
3507                                       RefEdge        edge ) {
3508
3509     TypeDescriptor tdSrc = src.getType();    
3510     assert tdSrc != null;
3511
3512     if( tdSrc.isArray() ) {
3513       TypeDescriptor td = edge.getType();
3514       assert td != null;
3515
3516       TypeDescriptor tdSrcDeref = tdSrc.dereference();
3517       assert tdSrcDeref != null;
3518
3519       if( !typeUtil.isSuperorType( tdSrcDeref, td ) ) {
3520         return false;
3521       }
3522
3523       return edge.getField().equals( DisjointAnalysis.arrayElementFieldName );
3524     }
3525
3526     // if it's not a class, it doesn't have any fields to match
3527     if( !tdSrc.isClass() ) {
3528       return false;
3529     }
3530
3531     ClassDescriptor cd = tdSrc.getClassDesc();
3532     while( cd != null ) {      
3533       Iterator fieldItr = cd.getFields();
3534
3535       while( fieldItr.hasNext() ) {     
3536         FieldDescriptor fd = (FieldDescriptor) fieldItr.next();
3537
3538         if( fd.getType().equals( edge.getType() ) &&
3539             fd.getSymbol().equals( edge.getField() ) ) {
3540           return true;
3541         }
3542       }
3543       
3544       cd = cd.getSuperDesc();
3545     }
3546     
3547     // otherwise it is a class with fields
3548     // but we didn't find a match
3549     return false;
3550   }
3551
3552   protected boolean hasMatchingType( RefEdge        edge, 
3553                                      HeapRegionNode dst  ) {
3554     
3555     // if the region has no type, matches everything
3556     TypeDescriptor tdDst = dst.getType();
3557     assert tdDst != null;
3558  
3559     // if the type is not a class or an array, don't
3560     // match because primitives are copied, no aliases
3561     ClassDescriptor cdDst = tdDst.getClassDesc();
3562     if( cdDst == null && !tdDst.isArray() ) {
3563       return false;
3564     }
3565  
3566     // if the edge type is null, it matches everything
3567     TypeDescriptor tdEdge = edge.getType();
3568     assert tdEdge != null;
3569  
3570     return typeUtil.isSuperorType( tdEdge, tdDst );
3571   }
3572   
3573
3574
3575   public void writeGraph( String  graphName,
3576                           boolean writeLabels,
3577                           boolean labelSelect,
3578                           boolean pruneGarbage,
3579                           boolean writeReferencers,
3580                           boolean hideSubsetReachability,
3581                           boolean hideEdgeTaints
3582                           ) throws java.io.IOException {
3583     writeGraph( graphName,
3584                 writeLabels,
3585                 labelSelect,
3586                 pruneGarbage,
3587                 writeReferencers,
3588                 hideSubsetReachability,
3589                 hideEdgeTaints,
3590                 null );
3591   }
3592
3593   public void writeGraph( String       graphName,
3594                           boolean      writeLabels,
3595                           boolean      labelSelect,
3596                           boolean      pruneGarbage,
3597                           boolean      writeReferencers,
3598                           boolean      hideSubsetReachability,
3599                           boolean      hideEdgeTaints,
3600                           Set<Integer> callerNodeIDsCopiedToCallee
3601                           ) throws java.io.IOException {
3602     
3603     // remove all non-word characters from the graph name so
3604     // the filename and identifier in dot don't cause errors
3605     graphName = graphName.replaceAll( "[\\W]", "" );
3606
3607     BufferedWriter bw = 
3608       new BufferedWriter( new FileWriter( graphName+".dot" ) );
3609
3610     bw.write( "digraph "+graphName+" {\n" );
3611
3612
3613     // this is an optional step to form the callee-reachable
3614     // "cut-out" into a DOT cluster for visualization
3615     if( callerNodeIDsCopiedToCallee != null ) {
3616
3617       bw.write( "  subgraph cluster0 {\n" );
3618       bw.write( "    color=blue;\n" );
3619
3620       Iterator i = id2hrn.entrySet().iterator();
3621       while( i.hasNext() ) {
3622         Map.Entry      me  = (Map.Entry)      i.next();
3623         HeapRegionNode hrn = (HeapRegionNode) me.getValue();      
3624         
3625         if( callerNodeIDsCopiedToCallee.contains( hrn.getID() ) ) {
3626           bw.write( "    "+hrn.toString()+
3627                     hrn.toStringDOT( hideSubsetReachability )+
3628                     ";\n" );
3629           
3630         }
3631       }
3632
3633       bw.write( "  }\n" );
3634     }
3635
3636
3637     Set<HeapRegionNode> visited = new HashSet<HeapRegionNode>();
3638
3639     // then visit every heap region node    
3640     Iterator i = id2hrn.entrySet().iterator();
3641     while( i.hasNext() ) {
3642       Map.Entry      me  = (Map.Entry)      i.next();
3643       HeapRegionNode hrn = (HeapRegionNode) me.getValue();      
3644
3645       // only visit nodes worth writing out--for instance
3646       // not every node at an allocation is referenced
3647       // (think of it as garbage-collected), etc.
3648       if( !pruneGarbage                              ||
3649           (hrn.isFlagged() && hrn.getID() > 0)       ||
3650           hrn.getDescription().startsWith( "param" ) ||
3651           hrn.isOutOfContext()
3652           ) {
3653
3654         if( !visited.contains( hrn ) ) {
3655           traverseHeapRegionNodes( hrn,
3656                                    bw,
3657                                    null,
3658                                    visited,
3659                                    writeReferencers,
3660                                    hideSubsetReachability,
3661                                    hideEdgeTaints,
3662                                    callerNodeIDsCopiedToCallee );
3663         }
3664       }
3665     }
3666
3667     bw.write( "  graphTitle[label=\""+graphName+"\",shape=box];\n" );
3668   
3669
3670     // then visit every label node, useful for debugging
3671     if( writeLabels ) {
3672       i = td2vn.entrySet().iterator();
3673       while( i.hasNext() ) {
3674         Map.Entry    me = (Map.Entry)    i.next();
3675         VariableNode vn = (VariableNode) me.getValue();
3676         
3677         if( labelSelect ) {
3678           String labelStr = vn.getTempDescriptorString();
3679           if( labelStr.startsWith("___temp") ||
3680               labelStr.startsWith("___dst") ||
3681               labelStr.startsWith("___srctmp") ||
3682               labelStr.startsWith("___neverused")
3683               ) {
3684             continue;
3685           }
3686         }
3687         
3688         Iterator<RefEdge> heapRegionsItr = vn.iteratorToReferencees();
3689         while( heapRegionsItr.hasNext() ) {
3690           RefEdge        edge = heapRegionsItr.next();
3691           HeapRegionNode hrn  = edge.getDst();
3692           
3693           if( pruneGarbage && !visited.contains( hrn ) ) {
3694             traverseHeapRegionNodes( hrn,
3695                                      bw,
3696                                      null,
3697                                      visited,
3698                                      writeReferencers,
3699                                      hideSubsetReachability,
3700                                      hideEdgeTaints,
3701                                      callerNodeIDsCopiedToCallee );
3702           }
3703           
3704           bw.write( "  "+vn.toString()+
3705                     " -> "+hrn.toString()+
3706                     edge.toStringDOT( hideSubsetReachability, "" )+
3707                     ";\n" );
3708         }
3709       }
3710     }
3711     
3712     bw.write( "}\n" );
3713     bw.close();
3714   }
3715
3716   protected void traverseHeapRegionNodes( HeapRegionNode      hrn,
3717                                           BufferedWriter      bw,
3718                                           TempDescriptor      td,
3719                                           Set<HeapRegionNode> visited,
3720                                           boolean             writeReferencers,
3721                                           boolean             hideSubsetReachability,
3722                                           boolean             hideEdgeTaints,
3723                                           Set<Integer>        callerNodeIDsCopiedToCallee
3724                                           ) throws java.io.IOException {
3725
3726     if( visited.contains( hrn ) ) {
3727       return;
3728     }
3729     visited.add( hrn );
3730
3731     // if we're drawing the callee-view subgraph, only
3732     // write out the node info if it hasn't already been
3733     // written
3734     if( callerNodeIDsCopiedToCallee == null ||
3735         !callerNodeIDsCopiedToCallee.contains( hrn.getID() ) 
3736         ) {
3737       bw.write( "  "+hrn.toString()+
3738                 hrn.toStringDOT( hideSubsetReachability )+
3739                 ";\n" );
3740     }
3741
3742     Iterator<RefEdge> childRegionsItr = hrn.iteratorToReferencees();
3743     while( childRegionsItr.hasNext() ) {
3744       RefEdge        edge     = childRegionsItr.next();
3745       HeapRegionNode hrnChild = edge.getDst();
3746
3747       if( callerNodeIDsCopiedToCallee != null &&
3748           (edge.getSrc() instanceof HeapRegionNode) ) {
3749         HeapRegionNode hrnSrc = (HeapRegionNode) edge.getSrc();
3750         if( callerNodeIDsCopiedToCallee.contains( hrnSrc.getID()        ) &&
3751             callerNodeIDsCopiedToCallee.contains( edge.getDst().getID() )
3752             ) {
3753           bw.write( "  "+hrn.toString()+
3754                     " -> "+hrnChild.toString()+
3755                     edge.toStringDOT( hideSubsetReachability, ",color=blue" )+
3756                     ";\n");
3757         } else if( !callerNodeIDsCopiedToCallee.contains( hrnSrc.getID()       ) &&
3758                    callerNodeIDsCopiedToCallee.contains( edge.getDst().getID() )
3759                    ) {
3760           bw.write( "  "+hrn.toString()+
3761                     " -> "+hrnChild.toString()+
3762                     edge.toStringDOT( hideSubsetReachability, ",color=blue,style=dashed" )+
3763                     ";\n");
3764         } else {
3765           bw.write( "  "+hrn.toString()+
3766                     " -> "+hrnChild.toString()+
3767                     edge.toStringDOT( hideSubsetReachability, "" )+
3768                     ";\n");
3769         }
3770       } else {
3771         bw.write( "  "+hrn.toString()+
3772                   " -> "+hrnChild.toString()+
3773                   edge.toStringDOT( hideSubsetReachability, "" )+
3774                   ";\n");
3775       }
3776       
3777       traverseHeapRegionNodes( hrnChild,
3778                                bw,
3779                                td,
3780                                visited,
3781                                writeReferencers,
3782                                hideSubsetReachability,
3783                                hideEdgeTaints,
3784                                callerNodeIDsCopiedToCallee );
3785     }
3786   }  
3787
3788 }