changes.
[IRC.git] / Robust / src / Analysis / SSJava / DefinitelyWrittenCheck.java
1 package Analysis.SSJava;
2
3 import java.util.HashSet;
4 import java.util.Hashtable;
5 import java.util.Iterator;
6 import java.util.LinkedList;
7 import java.util.Set;
8 import java.util.Stack;
9
10 import Analysis.CallGraph.CallGraph;
11 import Analysis.Loops.LoopFinder;
12 import IR.Descriptor;
13 import IR.FieldDescriptor;
14 import IR.MethodDescriptor;
15 import IR.Operation;
16 import IR.State;
17 import IR.TypeDescriptor;
18 import IR.Flat.FKind;
19 import IR.Flat.FlatCall;
20 import IR.Flat.FlatElementNode;
21 import IR.Flat.FlatFieldNode;
22 import IR.Flat.FlatLiteralNode;
23 import IR.Flat.FlatMethod;
24 import IR.Flat.FlatNode;
25 import IR.Flat.FlatOpNode;
26 import IR.Flat.FlatSetElementNode;
27 import IR.Flat.FlatSetFieldNode;
28 import IR.Flat.TempDescriptor;
29 import IR.Tree.Modifiers;
30 import Util.Pair;
31
32 public class DefinitelyWrittenCheck {
33
34   SSJavaAnalysis ssjava;
35   State state;
36   CallGraph callGraph;
37
38   // maps a descriptor to its known dependents: namely
39   // methods or tasks that call the descriptor's method
40   // AND are part of this analysis (reachable from main)
41   private Hashtable<Descriptor, Set<MethodDescriptor>> mapDescriptorToSetDependents;
42
43   // maps a flat node to its WrittenSet: this keeps all heap path overwritten
44   // previously.
45   private Hashtable<FlatNode, Set<NTuple<Descriptor>>> mapFlatNodeToWrittenSet;
46
47   // maps a temp descriptor to its heap path
48   // each temp descriptor has a unique heap path since we do not allow any
49   // alias.
50   private Hashtable<Descriptor, NTuple<Descriptor>> mapHeapPath;
51
52   // maps a flat method to the READ that is the set of heap path that is
53   // expected to be written before method invocation
54   private Hashtable<FlatMethod, Set<NTuple<Descriptor>>> mapFlatMethodToRead;
55
56   // maps a flat method to the OVERWRITE that is the set of heap path that is
57   // overwritten on every possible path during method invocation
58   private Hashtable<FlatMethod, Set<NTuple<Descriptor>>> mapFlatMethodToOverWrite;
59
60   // points to method containing SSJAVA Loop
61   private MethodDescriptor methodContainingSSJavaLoop;
62
63   // maps a flatnode to definitely written analysis mapping M
64   private Hashtable<FlatNode, Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>>> definitelyWrittenResults;
65
66   // maps a method descriptor to its current summary during the analysis
67   // then analysis reaches fixed-point, this mapping will have the final summary
68   // for each method descriptor
69   private Hashtable<MethodDescriptor, ClearingSummary> mapMethodDescriptorToCompleteClearingSummary;
70
71   // maps a method descriptor to the merged incoming caller's current
72   // overwritten status
73   private Hashtable<MethodDescriptor, ClearingSummary> mapMethodDescriptorToInitialClearingSummary;
74
75   // maps a flat node to current partial results
76   private Hashtable<FlatNode, ClearingSummary> mapFlatNodeToClearingSummary;
77
78   // maps shared location to the set of descriptors which belong to the shared
79   // location
80   private Hashtable<Location, Set<Descriptor>> mapSharedLocation2DescriptorSet;
81
82   // keep current descriptors to visit in fixed-point interprocedural analysis,
83   private Stack<MethodDescriptor> methodDescriptorsToVisitStack;
84
85   // when analyzing flatcall, need to re-schedule set of callee
86   private Set<MethodDescriptor> calleesToEnqueue;
87
88   public static final String arrayElementFieldName = "___element_";
89   static protected Hashtable<TypeDescriptor, FieldDescriptor> mapTypeToArrayField;
90
91   private Set<ClearingSummary> possibleCalleeCompleteSummarySetToCaller;
92
93   private LinkedList<MethodDescriptor> sortedDescriptors;
94
95   private FlatNode ssjavaLoopEntrance;
96   private LoopFinder ssjavaLoop;
97   private Set<FlatNode> loopIncElements;
98
99   private Set<NTuple<Descriptor>> calleeUnionBoundReadSet;
100   private Set<NTuple<Descriptor>> calleeIntersectBoundOverWriteSet;
101
102   private TempDescriptor LOCAL;
103
104   public DefinitelyWrittenCheck(SSJavaAnalysis ssjava, State state) {
105     this.state = state;
106     this.ssjava = ssjava;
107     this.callGraph = ssjava.getCallGraph();
108     this.mapFlatNodeToWrittenSet = new Hashtable<FlatNode, Set<NTuple<Descriptor>>>();
109     this.mapDescriptorToSetDependents = new Hashtable<Descriptor, Set<MethodDescriptor>>();
110     this.mapHeapPath = new Hashtable<Descriptor, NTuple<Descriptor>>();
111     this.mapFlatMethodToRead = new Hashtable<FlatMethod, Set<NTuple<Descriptor>>>();
112     this.mapFlatMethodToOverWrite = new Hashtable<FlatMethod, Set<NTuple<Descriptor>>>();
113     this.definitelyWrittenResults =
114         new Hashtable<FlatNode, Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>>>();
115     this.calleeUnionBoundReadSet = new HashSet<NTuple<Descriptor>>();
116     this.calleeIntersectBoundOverWriteSet = new HashSet<NTuple<Descriptor>>();
117
118     this.mapMethodDescriptorToCompleteClearingSummary =
119         new Hashtable<MethodDescriptor, ClearingSummary>();
120     this.mapMethodDescriptorToInitialClearingSummary =
121         new Hashtable<MethodDescriptor, ClearingSummary>();
122     this.mapSharedLocation2DescriptorSet = new Hashtable<Location, Set<Descriptor>>();
123     this.methodDescriptorsToVisitStack = new Stack<MethodDescriptor>();
124     this.calleesToEnqueue = new HashSet<MethodDescriptor>();
125     this.possibleCalleeCompleteSummarySetToCaller = new HashSet<ClearingSummary>();
126     this.mapTypeToArrayField = new Hashtable<TypeDescriptor, FieldDescriptor>();
127     this.LOCAL = new TempDescriptor("LOCAL");
128   }
129
130   public void definitelyWrittenCheck() {
131     if (!ssjava.getAnnotationRequireSet().isEmpty()) {
132       methodReadOverWriteAnalysis();
133       writtenAnalyis();
134       sharedLocationAnalysis();
135       checkSharedLocationResult();
136     }
137   }
138
139   private void checkSharedLocationResult() {
140
141     // mapping of method containing ssjava loop has the final result of
142     // shared location analysis
143     ClearingSummary result =
144         mapMethodDescriptorToCompleteClearingSummary.get(sortedDescriptors.peekFirst());
145
146     System.out.println("checkSharedLocationResult=" + result);
147
148     Set<NTuple<Descriptor>> hpKeySet = result.keySet();
149     for (Iterator iterator = hpKeySet.iterator(); iterator.hasNext();) {
150       NTuple<Descriptor> hpKey = (NTuple<Descriptor>) iterator.next();
151       SharedStatus state = result.get(hpKey);
152       Set<Location> locKeySet = state.getLocationSet();
153       for (Iterator iterator2 = locKeySet.iterator(); iterator2.hasNext();) {
154         Location locKey = (Location) iterator2.next();
155         if (!state.getFlag(locKey)) {
156           throw new Error(
157               "Some concrete locations of the shared abstract location are not cleared at the same time.");
158         }
159       }
160     }
161
162   }
163
164   private void sharedLocationAnalysis() {
165     // verify that all concrete locations of shared location are cleared out at
166     // the same time once per the out-most loop
167
168     computeReadSharedDescriptorSet();
169     System.out.println("Reading Shared Location=" + mapSharedLocation2DescriptorSet);
170
171     methodDescriptorsToVisitStack.clear();
172
173     methodDescriptorsToVisitStack.add(sortedDescriptors.peekFirst());
174
175     // analyze scheduled methods until there are no more to visit
176     while (!methodDescriptorsToVisitStack.isEmpty()) {
177       MethodDescriptor md = methodDescriptorsToVisitStack.pop();
178
179       ClearingSummary completeSummary =
180           sharedLocation_analyzeMethod(md, (md.equals(methodContainingSSJavaLoop)));
181
182       ClearingSummary prevCompleteSummary = mapMethodDescriptorToCompleteClearingSummary.get(md);
183
184       if (!completeSummary.equals(prevCompleteSummary)) {
185
186         mapMethodDescriptorToCompleteClearingSummary.put(md, completeSummary);
187
188         // results for callee changed, so enqueue dependents caller for
189         // further analysis
190         Iterator<MethodDescriptor> depsItr = getDependents(md).iterator();
191         while (depsItr.hasNext()) {
192           MethodDescriptor methodNext = depsItr.next();
193           if (!methodDescriptorsToVisitStack.contains(methodNext)) {
194             methodDescriptorsToVisitStack.add(methodNext);
195           }
196         }
197
198         // if there is set of callee to be analyzed,
199         // add this set into the top of stack
200         Iterator<MethodDescriptor> calleeIter = calleesToEnqueue.iterator();
201         while (calleeIter.hasNext()) {
202           MethodDescriptor mdNext = calleeIter.next();
203           if (!methodDescriptorsToVisitStack.contains(mdNext)) {
204             methodDescriptorsToVisitStack.add(mdNext);
205           }
206         }
207         calleesToEnqueue.clear();
208
209       }
210
211     }
212
213   }
214
215   private ClearingSummary sharedLocation_analyzeMethod(MethodDescriptor md,
216       boolean onlyVisitSSJavaLoop) {
217
218     if (state.SSJAVADEBUG) {
219       System.out.println("Definitely written for shared locations Analyzing: " + md + " "
220           + onlyVisitSSJavaLoop);
221     }
222
223     FlatMethod fm = state.getMethodFlat(md);
224
225     // intraprocedural analysis
226     Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
227
228     // start a new mapping of partial results for each flat node
229     mapFlatNodeToClearingSummary = new Hashtable<FlatNode, ClearingSummary>();
230
231     if (onlyVisitSSJavaLoop) {
232       flatNodesToVisit.add(ssjavaLoopEntrance);
233     } else {
234       flatNodesToVisit.add(fm);
235     }
236
237     Set<FlatNode> returnNodeSet = new HashSet<FlatNode>();
238
239     while (!flatNodesToVisit.isEmpty()) {
240       FlatNode fn = flatNodesToVisit.iterator().next();
241       flatNodesToVisit.remove(fn);
242
243       ClearingSummary curr = new ClearingSummary();
244
245       Set<ClearingSummary> prevSet = new HashSet<ClearingSummary>();
246       for (int i = 0; i < fn.numPrev(); i++) {
247         FlatNode prevFn = fn.getPrev(i);
248         ClearingSummary in = mapFlatNodeToClearingSummary.get(prevFn);
249         if (in != null) {
250           prevSet.add(in);
251         }
252       }
253       mergeSharedLocationAnaylsis(curr, prevSet);
254
255       sharedLocation_nodeActions(md, fn, curr, returnNodeSet, onlyVisitSSJavaLoop);
256       ClearingSummary clearingPrev = mapFlatNodeToClearingSummary.get(fn);
257
258       if (!curr.equals(clearingPrev)) {
259         mapFlatNodeToClearingSummary.put(fn, curr);
260
261         for (int i = 0; i < fn.numNext(); i++) {
262           FlatNode nn = fn.getNext(i);
263
264           if (!onlyVisitSSJavaLoop || (onlyVisitSSJavaLoop && loopIncElements.contains(nn))) {
265             flatNodesToVisit.add(nn);
266           }
267
268         }
269       }
270
271     }
272
273     ClearingSummary completeSummary = new ClearingSummary();
274     Set<ClearingSummary> summarySet = new HashSet<ClearingSummary>();
275
276     if (onlyVisitSSJavaLoop) {
277       // when analyzing ssjava loop,
278       // complete summary is merging of all previous nodes of ssjava loop
279       // entrance
280       for (int i = 0; i < ssjavaLoopEntrance.numPrev(); i++) {
281         ClearingSummary frnSummary =
282             mapFlatNodeToClearingSummary.get(ssjavaLoopEntrance.getPrev(i));
283         if (frnSummary != null) {
284           summarySet.add(frnSummary);
285         }
286       }
287     } else {
288       // merging all exit node summary into the complete summary
289       if (!returnNodeSet.isEmpty()) {
290         for (Iterator iterator = returnNodeSet.iterator(); iterator.hasNext();) {
291           FlatNode frn = (FlatNode) iterator.next();
292           ClearingSummary frnSummary = mapFlatNodeToClearingSummary.get(frn);
293           summarySet.add(frnSummary);
294         }
295       }
296     }
297     mergeSharedLocationAnaylsis(completeSummary, summarySet);
298     return completeSummary;
299   }
300
301   private void sharedLocation_nodeActions(MethodDescriptor caller, FlatNode fn,
302       ClearingSummary curr, Set<FlatNode> returnNodeSet, boolean isSSJavaLoop) {
303
304     TempDescriptor lhs;
305     TempDescriptor rhs;
306     FieldDescriptor fld;
307     switch (fn.kind()) {
308
309     case FKind.FlatMethod: {
310       FlatMethod fm = (FlatMethod) fn;
311
312       ClearingSummary summaryFromCaller =
313           mapMethodDescriptorToInitialClearingSummary.get(fm.getMethod());
314
315       Set<ClearingSummary> inSet = new HashSet<ClearingSummary>();
316       inSet.add(summaryFromCaller);
317       mergeSharedLocationAnaylsis(curr, inSet);
318
319     }
320       break;
321
322     case FKind.FlatOpNode: {
323       FlatOpNode fon = (FlatOpNode) fn;
324       lhs = fon.getDest();
325       rhs = fon.getLeft();
326
327       if (fon.getOp().getOp() == Operation.ASSIGN) {
328         if (rhs.getType().isImmutable() && isSSJavaLoop) {
329           // in ssjavaloop, we need to take care about reading local variables!
330           NTuple<Descriptor> rhsHeapPath = new NTuple<Descriptor>();
331           NTuple<Descriptor> lhsHeapPath = new NTuple<Descriptor>();
332           rhsHeapPath.add(LOCAL);
333           lhsHeapPath.add(LOCAL);
334           if (!lhs.getSymbol().startsWith("neverused")) {
335             readLocation(curr, rhsHeapPath, rhs);
336             writeLocation(curr, lhsHeapPath, lhs);
337           }
338         }
339       }
340
341     }
342       break;
343
344     case FKind.FlatFieldNode:
345     case FKind.FlatElementNode: {
346
347       FlatFieldNode ffn = (FlatFieldNode) fn;
348       lhs = ffn.getDst();
349       rhs = ffn.getSrc();
350       fld = ffn.getField();
351
352       // read field
353       NTuple<Descriptor> srcHeapPath = mapHeapPath.get(rhs);
354       NTuple<Descriptor> fldHeapPath = new NTuple<Descriptor>(srcHeapPath.getList());
355
356       if (fld.getType().isImmutable()) {
357         readLocation(curr, fldHeapPath, fld);
358       }
359
360     }
361       break;
362
363     case FKind.FlatSetFieldNode:
364     case FKind.FlatSetElementNode: {
365
366       FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
367       lhs = fsfn.getDst();
368       fld = fsfn.getField();
369
370       // write(field)
371       NTuple<Descriptor> lhsHeapPath = computePath(lhs);
372       NTuple<Descriptor> fldHeapPath = new NTuple<Descriptor>(lhsHeapPath.getList());
373       if (fld.getType().isImmutable()) {
374         writeLocation(curr, fldHeapPath, fld);
375       } else {
376         // updates reference field case:
377         // 2. if there exists a tuple t in sharing summary that starts with
378         // hp(x) then, set flag of tuple t to 'true'
379         fldHeapPath.add(fld);
380         Set<NTuple<Descriptor>> hpKeySet = curr.keySet();
381         for (Iterator iterator = hpKeySet.iterator(); iterator.hasNext();) {
382           NTuple<Descriptor> hpKey = (NTuple<Descriptor>) iterator.next();
383           if (hpKey.startsWith(fldHeapPath)) {
384             curr.get(hpKey).updateFlag(true);
385           }
386         }
387       }
388
389     }
390       break;
391
392     case FKind.FlatCall: {
393
394       FlatCall fc = (FlatCall) fn;
395
396       // find out the set of callees
397       MethodDescriptor mdCallee = fc.getMethod();
398       FlatMethod fmCallee = state.getMethodFlat(mdCallee);
399       Set<MethodDescriptor> setPossibleCallees = new HashSet<MethodDescriptor>();
400       TypeDescriptor typeDesc = fc.getThis().getType();
401       setPossibleCallees.addAll(callGraph.getMethods(mdCallee, typeDesc));
402
403       possibleCalleeCompleteSummarySetToCaller.clear();
404
405       for (Iterator iterator = setPossibleCallees.iterator(); iterator.hasNext();) {
406         MethodDescriptor mdPossibleCallee = (MethodDescriptor) iterator.next();
407         FlatMethod calleeFlatMethod = state.getMethodFlat(mdPossibleCallee);
408
409         addDependent(mdPossibleCallee, // callee
410             caller); // caller
411
412         calleesToEnqueue.add(mdPossibleCallee);
413
414         // updates possible callee's initial summary using caller's current
415         // writing status
416         ClearingSummary prevCalleeInitSummary =
417             mapMethodDescriptorToInitialClearingSummary.get(mdPossibleCallee);
418
419         ClearingSummary calleeInitSummary =
420             bindHeapPathOfCalleeCallerEffects(fc, calleeFlatMethod, curr);
421
422         // if changes, update the init summary
423         // and reschedule the callee for analysis
424         if (!calleeInitSummary.equals(prevCalleeInitSummary)) {
425
426           if (!methodDescriptorsToVisitStack.contains(mdPossibleCallee)) {
427             methodDescriptorsToVisitStack.add(mdPossibleCallee);
428           }
429           mapMethodDescriptorToInitialClearingSummary.put(mdPossibleCallee, calleeInitSummary);
430         }
431
432       }
433
434       // contribute callee's writing effects to the caller
435       mergeSharedLocationAnaylsis(curr, possibleCalleeCompleteSummarySetToCaller);
436
437     }
438       break;
439
440     case FKind.FlatReturnNode: {
441       returnNodeSet.add(fn);
442     }
443       break;
444
445     }
446
447   }
448
449   private ClearingSummary bindHeapPathOfCalleeCallerEffects(FlatCall fc,
450       FlatMethod calleeFlatMethod, ClearingSummary curr) {
451
452     ClearingSummary boundSet = new ClearingSummary();
453
454     // create mapping from arg idx to its heap paths
455     Hashtable<Integer, NTuple<Descriptor>> mapArgIdx2CallerArgHeapPath =
456         new Hashtable<Integer, NTuple<Descriptor>>();
457
458     // arg idx is starting from 'this' arg
459     NTuple<Descriptor> thisHeapPath = mapHeapPath.get(fc.getThis());
460     if (thisHeapPath == null) {
461       // method is called without creating new flat node representing 'this'
462       thisHeapPath = new NTuple<Descriptor>();
463       thisHeapPath.add(fc.getThis());
464     }
465
466     mapArgIdx2CallerArgHeapPath.put(Integer.valueOf(0), thisHeapPath);
467
468     for (int i = 0; i < fc.numArgs(); i++) {
469       TempDescriptor arg = fc.getArg(i);
470       NTuple<Descriptor> argHeapPath = computePath(arg);
471       mapArgIdx2CallerArgHeapPath.put(Integer.valueOf(i + 1), argHeapPath);
472     }
473
474     Hashtable<Integer, TempDescriptor> mapParamIdx2ParamTempDesc =
475         new Hashtable<Integer, TempDescriptor>();
476     for (int i = 0; i < calleeFlatMethod.numParameters(); i++) {
477       TempDescriptor param = calleeFlatMethod.getParameter(i);
478       mapParamIdx2ParamTempDesc.put(Integer.valueOf(i), param);
479     }
480
481     // binding caller's writing effects to callee's params
482     for (int i = 0; i < calleeFlatMethod.numParameters(); i++) {
483       NTuple<Descriptor> argHeapPath = mapArgIdx2CallerArgHeapPath.get(Integer.valueOf(i));
484       TempDescriptor calleeParamHeapPath = mapParamIdx2ParamTempDesc.get(Integer.valueOf(i));
485
486       // iterate over caller's writing effect set
487       Set<NTuple<Descriptor>> hpKeySet = curr.keySet();
488       for (Iterator iterator = hpKeySet.iterator(); iterator.hasNext();) {
489         NTuple<Descriptor> hpKey = (NTuple<Descriptor>) iterator.next();
490         // current element is reachable caller's arg
491         // so need to bind it to the caller's side and add it to the callee's
492         // init summary
493         if (hpKey.startsWith(argHeapPath)) {
494           NTuple<Descriptor> boundHeapPath = replace(hpKey, argHeapPath, calleeParamHeapPath);
495           boundSet.put(boundHeapPath, curr.get(hpKey).clone());
496         }
497
498       }
499
500     }
501
502     // contribute callee's complete summary into the caller's current summary
503     ClearingSummary calleeCompleteSummary =
504         mapMethodDescriptorToCompleteClearingSummary.get(calleeFlatMethod.getMethod());
505
506     if (calleeCompleteSummary != null) {
507       ClearingSummary boundCalleeEfffects = new ClearingSummary();
508       for (int i = 0; i < calleeFlatMethod.numParameters(); i++) {
509         NTuple<Descriptor> argHeapPath = mapArgIdx2CallerArgHeapPath.get(Integer.valueOf(i));
510         TempDescriptor calleeParamHeapPath = mapParamIdx2ParamTempDesc.get(Integer.valueOf(i));
511
512         // iterate over callee's writing effect set
513         Set<NTuple<Descriptor>> hpKeySet = calleeCompleteSummary.keySet();
514         for (Iterator iterator = hpKeySet.iterator(); iterator.hasNext();) {
515           NTuple<Descriptor> hpKey = (NTuple<Descriptor>) iterator.next();
516           // current element is reachable caller's arg
517           // so need to bind it to the caller's side and add it to the callee's
518           // init summary
519           if (hpKey.startsWith(calleeParamHeapPath)) {
520
521             NTuple<Descriptor> boundHeapPathForCaller = replace(hpKey, argHeapPath);
522
523             boundCalleeEfffects.put(boundHeapPathForCaller, calleeCompleteSummary.get(hpKey)
524                 .clone());
525
526           }
527         }
528       }
529       possibleCalleeCompleteSummarySetToCaller.add(boundCalleeEfffects);
530     }
531
532     return boundSet;
533   }
534
535   private NTuple<Descriptor> replace(NTuple<Descriptor> hpKey, NTuple<Descriptor> argHeapPath) {
536
537     // replace the head of heap path with caller's arg path
538     // for example, heap path 'param.a.b' in callee's side will be replaced with
539     // (corresponding arg heap path).a.b for caller's side
540
541     NTuple<Descriptor> bound = new NTuple<Descriptor>();
542
543     for (int i = 0; i < argHeapPath.size(); i++) {
544       bound.add(argHeapPath.get(i));
545     }
546
547     for (int i = 1; i < hpKey.size(); i++) {
548       bound.add(hpKey.get(i));
549     }
550
551     return bound;
552   }
553
554   private NTuple<Descriptor> replace(NTuple<Descriptor> effectHeapPath,
555       NTuple<Descriptor> argHeapPath, TempDescriptor calleeParamHeapPath) {
556     // replace the head of caller's heap path with callee's param heap path
557
558     NTuple<Descriptor> boundHeapPath = new NTuple<Descriptor>();
559     boundHeapPath.add(calleeParamHeapPath);
560
561     for (int i = argHeapPath.size(); i < effectHeapPath.size(); i++) {
562       boundHeapPath.add(effectHeapPath.get(i));
563     }
564
565     return boundHeapPath;
566   }
567
568   private void computeReadSharedDescriptorSet() {
569     Set<MethodDescriptor> methodDescriptorsToAnalyze = new HashSet<MethodDescriptor>();
570     methodDescriptorsToAnalyze.addAll(ssjava.getAnnotationRequireSet());
571
572     for (Iterator iterator = methodDescriptorsToAnalyze.iterator(); iterator.hasNext();) {
573       MethodDescriptor md = (MethodDescriptor) iterator.next();
574       FlatMethod fm = state.getMethodFlat(md);
575       computeReadSharedDescriptorSet_analyzeMethod(fm, md.equals(methodContainingSSJavaLoop));
576     }
577
578   }
579
580   private void computeReadSharedDescriptorSet_analyzeMethod(FlatMethod fm,
581       boolean onlyVisitSSJavaLoop) {
582
583     Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
584     Set<FlatNode> visited = new HashSet<FlatNode>();
585
586     if (onlyVisitSSJavaLoop) {
587       flatNodesToVisit.add(ssjavaLoopEntrance);
588     } else {
589       flatNodesToVisit.add(fm);
590     }
591
592     while (!flatNodesToVisit.isEmpty()) {
593       FlatNode fn = flatNodesToVisit.iterator().next();
594       flatNodesToVisit.remove(fn);
595       visited.add(fn);
596
597       computeReadSharedDescriptorSet_nodeActions(fn, onlyVisitSSJavaLoop);
598
599       for (int i = 0; i < fn.numNext(); i++) {
600         FlatNode nn = fn.getNext(i);
601         if (!visited.contains(nn)) {
602           if (!onlyVisitSSJavaLoop || (onlyVisitSSJavaLoop && loopIncElements.contains(nn))) {
603             flatNodesToVisit.add(nn);
604           }
605         }
606       }
607
608     }
609
610   }
611
612   private void computeReadSharedDescriptorSet_nodeActions(FlatNode fn, boolean isSSJavaLoop) {
613
614     TempDescriptor lhs;
615     TempDescriptor rhs;
616     FieldDescriptor fld;
617
618     switch (fn.kind()) {
619     case FKind.FlatOpNode: {
620       FlatOpNode fon = (FlatOpNode) fn;
621       lhs = fon.getDest();
622       rhs = fon.getLeft();
623
624       if (fon.getOp().getOp() == Operation.ASSIGN) {
625         if (rhs.getType().isImmutable() && isSSJavaLoop && (!rhs.getSymbol().startsWith("srctmp"))) {
626           // in ssjavaloop, we need to take care about reading local variables!
627           NTuple<Descriptor> rhsHeapPath = new NTuple<Descriptor>();
628           NTuple<Descriptor> lhsHeapPath = new NTuple<Descriptor>();
629           rhsHeapPath.add(LOCAL);
630           addReadDescriptor(rhsHeapPath, rhs);
631         }
632       }
633
634     }
635       break;
636
637     case FKind.FlatFieldNode:
638     case FKind.FlatElementNode: {
639
640       FlatFieldNode ffn = (FlatFieldNode) fn;
641       lhs = ffn.getDst();
642       rhs = ffn.getSrc();
643       fld = ffn.getField();
644
645       // read field
646       NTuple<Descriptor> srcHeapPath = mapHeapPath.get(rhs);
647       NTuple<Descriptor> fldHeapPath = new NTuple<Descriptor>(srcHeapPath.getList());
648       // fldHeapPath.add(fld);
649
650       if (fld.getType().isImmutable()) {
651         addReadDescriptor(fldHeapPath, fld);
652       }
653
654       // propagate rhs's heap path to the lhs
655       mapHeapPath.put(lhs, fldHeapPath);
656
657     }
658       break;
659
660     case FKind.FlatSetFieldNode:
661     case FKind.FlatSetElementNode: {
662
663       FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
664       lhs = fsfn.getDst();
665       fld = fsfn.getField();
666
667       // write(field)
668       NTuple<Descriptor> lhsHeapPath = computePath(lhs);
669       NTuple<Descriptor> fldHeapPath = new NTuple<Descriptor>(lhsHeapPath.getList());
670       // writeLocation(curr, fldHeapPath, fld, getLocation(fld));
671
672     }
673       break;
674
675     }
676   }
677
678   private boolean hasReadingEffectOnSharedLocation(NTuple<Descriptor> hp, Location loc, Descriptor d) {
679     if (!mapSharedLocation2DescriptorSet.containsKey(loc)) {
680       return false;
681     } else {
682       return mapSharedLocation2DescriptorSet.get(loc).contains(d);
683     }
684   }
685
686   private void addReadDescriptor(NTuple<Descriptor> hp, Descriptor d) {
687
688     Location loc = getLocation(d);
689
690     if (loc != null && ssjava.isSharedLocation(loc)) {
691
692       Set<Descriptor> set = mapSharedLocation2DescriptorSet.get(loc);
693       if (set == null) {
694         set = new HashSet<Descriptor>();
695         mapSharedLocation2DescriptorSet.put(loc, set);
696       }
697       set.add(d);
698     }
699
700   }
701
702   private Location getLocation(Descriptor d) {
703
704     if (d instanceof FieldDescriptor) {
705       return (Location) ((FieldDescriptor) d).getType().getExtension();
706     } else {
707       assert d instanceof TempDescriptor;
708       CompositeLocation comp = (CompositeLocation) ((TempDescriptor) d).getType().getExtension();
709       if (comp == null) {
710         return null;
711       } else {
712         return comp.get(comp.getSize() - 1);
713       }
714     }
715
716   }
717
718   private void writeLocation(ClearingSummary curr, NTuple<Descriptor> hp, Descriptor d) {
719     Location loc = getLocation(d);
720     if (loc != null && hasReadingEffectOnSharedLocation(hp, loc, d)) {
721
722       // 1. add field x to the clearing set
723       SharedStatus state = getState(curr, hp);
724       state.addVar(loc, d);
725
726       // 3. if the set v contains all of variables belonging to the shared
727       // location, set flag to true
728       Set<Descriptor> sharedVarSet = mapSharedLocation2DescriptorSet.get(loc);
729       if (state.getVarSet(loc).containsAll(sharedVarSet)) {
730         state.updateFlag(loc, true);
731       }
732     }
733   }
734
735   private void readLocation(ClearingSummary curr, NTuple<Descriptor> hp, Descriptor d) {
736     // remove reading var x from written set
737     Location loc = getLocation(d);
738     if (loc != null && hasReadingEffectOnSharedLocation(hp, loc, d)) {
739       SharedStatus state = getState(curr, hp);
740       state.removeVar(loc, d);
741     }
742   }
743
744   private SharedStatus getState(ClearingSummary curr, NTuple<Descriptor> hp) {
745     SharedStatus state = curr.get(hp);
746     if (state == null) {
747       state = new SharedStatus();
748       curr.put(hp, state);
749     }
750     return state;
751   }
752
753   private void writtenAnalyis() {
754     // perform second stage analysis: intraprocedural analysis ensure that
755     // all
756     // variables are definitely written in-between the same read
757
758     // First, identify ssjava loop entrace
759     FlatMethod fm = state.getMethodFlat(methodContainingSSJavaLoop);
760     Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
761     flatNodesToVisit.add(fm);
762
763     LoopFinder loopFinder = new LoopFinder(fm);
764
765     while (!flatNodesToVisit.isEmpty()) {
766       FlatNode fn = flatNodesToVisit.iterator().next();
767       flatNodesToVisit.remove(fn);
768
769       String label = (String) state.fn2labelMap.get(fn);
770       if (label != null) {
771
772         if (label.equals(ssjava.SSJAVA)) {
773           ssjavaLoopEntrance = fn;
774           break;
775         }
776       }
777
778       for (int i = 0; i < fn.numNext(); i++) {
779         FlatNode nn = fn.getNext(i);
780         flatNodesToVisit.add(nn);
781       }
782     }
783
784     assert ssjavaLoopEntrance != null;
785
786     // assume that ssjava loop is top-level loop in method, not nested loop
787     Set nestedLoop = loopFinder.nestedLoops();
788     for (Iterator loopIter = nestedLoop.iterator(); loopIter.hasNext();) {
789       LoopFinder lf = (LoopFinder) loopIter.next();
790       if (lf.loopEntrances().iterator().next().equals(ssjavaLoopEntrance)) {
791         ssjavaLoop = lf;
792       }
793     }
794
795     assert ssjavaLoop != null;
796
797     writtenAnalysis_analyzeLoop();
798
799   }
800
801   private void writtenAnalysis_analyzeLoop() {
802
803     Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
804     flatNodesToVisit.add(ssjavaLoopEntrance);
805
806     loopIncElements = (Set<FlatNode>) ssjavaLoop.loopIncElements();
807
808     while (!flatNodesToVisit.isEmpty()) {
809       FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next();
810       flatNodesToVisit.remove(fn);
811
812       Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>> prev =
813           definitelyWrittenResults.get(fn);
814
815       Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>> curr =
816           new Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>>();
817       for (int i = 0; i < fn.numPrev(); i++) {
818         FlatNode nn = fn.getPrev(i);
819         Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>> dwIn =
820             definitelyWrittenResults.get(nn);
821         if (dwIn != null) {
822           merge(curr, dwIn);
823         }
824       }
825
826       writtenAnalysis_nodeAction(fn, curr, ssjavaLoopEntrance);
827
828       // if a new result, schedule forward nodes for analysis
829       if (!curr.equals(prev)) {
830         definitelyWrittenResults.put(fn, curr);
831
832         for (int i = 0; i < fn.numNext(); i++) {
833           FlatNode nn = fn.getNext(i);
834           if (loopIncElements.contains(nn)) {
835             flatNodesToVisit.add(nn);
836           }
837
838         }
839       }
840     }
841   }
842
843   private void writtenAnalysis_nodeAction(FlatNode fn,
844       Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>> curr, FlatNode loopEntrance) {
845
846     if (fn.equals(loopEntrance)) {
847       // it reaches loop entrance: changes all flag to true
848       Set<NTuple<Descriptor>> keySet = curr.keySet();
849       for (Iterator iterator = keySet.iterator(); iterator.hasNext();) {
850         NTuple<Descriptor> key = (NTuple<Descriptor>) iterator.next();
851         Hashtable<FlatNode, Boolean> pair = curr.get(key);
852         if (pair != null) {
853           Set<FlatNode> pairKeySet = pair.keySet();
854           for (Iterator iterator2 = pairKeySet.iterator(); iterator2.hasNext();) {
855             FlatNode pairKey = (FlatNode) iterator2.next();
856             pair.put(pairKey, Boolean.TRUE);
857           }
858         }
859       }
860     } else {
861       TempDescriptor lhs;
862       TempDescriptor rhs;
863       FieldDescriptor fld;
864
865       switch (fn.kind()) {
866       case FKind.FlatOpNode: {
867         FlatOpNode fon = (FlatOpNode) fn;
868         lhs = fon.getDest();
869         rhs = fon.getLeft();
870
871         NTuple<Descriptor> rhsHeapPath = computePath(rhs);
872         if (!rhs.getType().isImmutable()) {
873           mapHeapPath.put(lhs, rhsHeapPath);
874         } else {
875           if (fon.getOp().getOp() == Operation.ASSIGN) {
876             // read(rhs)
877             readValue(fn, rhsHeapPath, curr);
878           }
879           // write(lhs)
880           NTuple<Descriptor> lhsHeapPath = computePath(lhs);
881           removeHeapPath(curr, lhsHeapPath);
882         }
883       }
884         break;
885
886       case FKind.FlatLiteralNode: {
887         FlatLiteralNode fln = (FlatLiteralNode) fn;
888         lhs = fln.getDst();
889
890         // write(lhs)
891         NTuple<Descriptor> lhsHeapPath = computePath(lhs);
892         removeHeapPath(curr, lhsHeapPath);
893
894       }
895         break;
896
897       case FKind.FlatFieldNode:
898       case FKind.FlatElementNode: {
899
900         if (fn.kind() == FKind.FlatFieldNode) {
901           FlatFieldNode ffn = (FlatFieldNode) fn;
902           lhs = ffn.getDst();
903           rhs = ffn.getSrc();
904           fld = ffn.getField();
905         } else {
906           FlatElementNode fen = (FlatElementNode) fn;
907           lhs = fen.getDst();
908           rhs = fen.getSrc();
909           TypeDescriptor td = rhs.getType().dereference();
910           fld = getArrayField(td);
911         }
912
913         if (fld.isFinal() /* && fld.isStatic() */) {
914           // if field is final and static, no need to check
915           break;
916         }
917
918         // read field
919         NTuple<Descriptor> srcHeapPath = mapHeapPath.get(rhs);
920         NTuple<Descriptor> fldHeapPath = new NTuple<Descriptor>(srcHeapPath.getList());
921         fldHeapPath.add(fld);
922
923         if (fld.getType().isImmutable()) {
924           readValue(fn, fldHeapPath, curr);
925         }
926
927         // propagate rhs's heap path to the lhs
928         mapHeapPath.put(lhs, fldHeapPath);
929
930       }
931         break;
932
933       case FKind.FlatSetFieldNode:
934       case FKind.FlatSetElementNode: {
935
936         if (fn.kind() == FKind.FlatSetFieldNode) {
937           FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
938           lhs = fsfn.getDst();
939           fld = fsfn.getField();
940         } else {
941           FlatSetElementNode fsen = (FlatSetElementNode) fn;
942           lhs = fsen.getDst();
943           rhs = fsen.getSrc();
944           TypeDescriptor td = lhs.getType().dereference();
945           fld = getArrayField(td);
946         }
947
948         // write(field)
949         NTuple<Descriptor> lhsHeapPath = computePath(lhs);
950         NTuple<Descriptor> fldHeapPath = new NTuple<Descriptor>(lhsHeapPath.getList());
951         fldHeapPath.add(fld);
952         removeHeapPath(curr, fldHeapPath);
953
954       }
955         break;
956
957       case FKind.FlatCall: {
958         FlatCall fc = (FlatCall) fn;
959         bindHeapPathCallerArgWithCaleeParam(fc);
960         // add <hp,statement,false> in which hp is an element of
961         // READ_bound set
962         // of callee: callee has 'read' requirement!
963         for (Iterator iterator = calleeUnionBoundReadSet.iterator(); iterator.hasNext();) {
964           NTuple<Descriptor> read = (NTuple<Descriptor>) iterator.next();
965
966           Hashtable<FlatNode, Boolean> gen = curr.get(read);
967           if (gen == null) {
968             gen = new Hashtable<FlatNode, Boolean>();
969             curr.put(read, gen);
970           }
971           Boolean currentStatus = gen.get(fn);
972           if (currentStatus == null) {
973             gen.put(fn, Boolean.FALSE);
974           } else {
975             checkFlag(currentStatus.booleanValue(), fn, read);
976           }
977         }
978
979         // removes <hp,statement,flag> if hp is an element of
980         // OVERWRITE_bound
981         // set of callee. it means that callee will overwrite it
982         for (Iterator iterator = calleeIntersectBoundOverWriteSet.iterator(); iterator.hasNext();) {
983           NTuple<Descriptor> write = (NTuple<Descriptor>) iterator.next();
984           removeHeapPath(curr, write);
985         }
986       }
987         break;
988
989       }
990     }
991
992   }
993
994   private void readValue(FlatNode fn, NTuple<Descriptor> hp,
995       Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>> curr) {
996     Hashtable<FlatNode, Boolean> gen = curr.get(hp);
997     if (gen == null) {
998       gen = new Hashtable<FlatNode, Boolean>();
999       curr.put(hp, gen);
1000     }
1001     Boolean currentStatus = gen.get(fn);
1002     if (currentStatus == null) {
1003       gen.put(fn, Boolean.FALSE);
1004     } else {
1005       checkFlag(currentStatus.booleanValue(), fn, hp);
1006     }
1007
1008   }
1009
1010   private void removeHeapPath(Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>> curr,
1011       NTuple<Descriptor> hp) {
1012
1013     // removes all of heap path that starts with prefix 'hp'
1014     // since any reference overwrite along heap path gives overwriting side
1015     // effects on the value
1016
1017     Set<NTuple<Descriptor>> keySet = curr.keySet();
1018     for (Iterator<NTuple<Descriptor>> iter = keySet.iterator(); iter.hasNext();) {
1019       NTuple<Descriptor> key = iter.next();
1020       if (key.startsWith(hp)) {
1021         curr.put(key, new Hashtable<FlatNode, Boolean>());
1022       }
1023     }
1024
1025   }
1026
1027   private void bindHeapPathCallerArgWithCaleeParam(FlatCall fc) {
1028     // compute all possible callee set
1029     // transform all READ/OVERWRITE set from the any possible
1030     // callees to the
1031     // caller
1032
1033     calleeUnionBoundReadSet.clear();
1034     calleeIntersectBoundOverWriteSet.clear();
1035
1036     MethodDescriptor mdCallee = fc.getMethod();
1037     FlatMethod fmCallee = state.getMethodFlat(mdCallee);
1038     Set<MethodDescriptor> setPossibleCallees = new HashSet<MethodDescriptor>();
1039     TypeDescriptor typeDesc = fc.getThis().getType();
1040     setPossibleCallees.addAll(callGraph.getMethods(mdCallee, typeDesc));
1041
1042     // create mapping from arg idx to its heap paths
1043     Hashtable<Integer, NTuple<Descriptor>> mapArgIdx2CallerArgHeapPath =
1044         new Hashtable<Integer, NTuple<Descriptor>>();
1045
1046     // arg idx is starting from 'this' arg
1047     NTuple<Descriptor> thisHeapPath = mapHeapPath.get(fc.getThis());
1048     if (thisHeapPath == null) {
1049       // method is called without creating new flat node representing 'this'
1050       thisHeapPath = new NTuple<Descriptor>();
1051       thisHeapPath.add(fc.getThis());
1052     }
1053
1054     mapArgIdx2CallerArgHeapPath.put(Integer.valueOf(0), thisHeapPath);
1055
1056     for (int i = 0; i < fc.numArgs(); i++) {
1057       TempDescriptor arg = fc.getArg(i);
1058       NTuple<Descriptor> argHeapPath = computePath(arg);
1059       mapArgIdx2CallerArgHeapPath.put(Integer.valueOf(i + 1), argHeapPath);
1060     }
1061
1062     for (Iterator iterator = setPossibleCallees.iterator(); iterator.hasNext();) {
1063       MethodDescriptor callee = (MethodDescriptor) iterator.next();
1064       FlatMethod calleeFlatMethod = state.getMethodFlat(callee);
1065
1066       // binding caller's args and callee's params
1067
1068       Set<NTuple<Descriptor>> calleeReadSet = mapFlatMethodToRead.get(calleeFlatMethod);
1069       if (calleeReadSet == null) {
1070         calleeReadSet = new HashSet<NTuple<Descriptor>>();
1071         mapFlatMethodToRead.put(calleeFlatMethod, calleeReadSet);
1072       }
1073       Set<NTuple<Descriptor>> calleeOverWriteSet = mapFlatMethodToOverWrite.get(calleeFlatMethod);
1074       if (calleeOverWriteSet == null) {
1075         calleeOverWriteSet = new HashSet<NTuple<Descriptor>>();
1076         mapFlatMethodToOverWrite.put(calleeFlatMethod, calleeOverWriteSet);
1077       }
1078
1079       Hashtable<Integer, TempDescriptor> mapParamIdx2ParamTempDesc =
1080           new Hashtable<Integer, TempDescriptor>();
1081       for (int i = 0; i < calleeFlatMethod.numParameters(); i++) {
1082         TempDescriptor param = calleeFlatMethod.getParameter(i);
1083         mapParamIdx2ParamTempDesc.put(Integer.valueOf(i), param);
1084       }
1085
1086       Set<NTuple<Descriptor>> calleeBoundReadSet =
1087           bindSet(calleeReadSet, mapParamIdx2ParamTempDesc, mapArgIdx2CallerArgHeapPath);
1088       // union of the current read set and the current callee's
1089       // read set
1090       calleeUnionBoundReadSet.addAll(calleeBoundReadSet);
1091       Set<NTuple<Descriptor>> calleeBoundWriteSet =
1092           bindSet(calleeOverWriteSet, mapParamIdx2ParamTempDesc, mapArgIdx2CallerArgHeapPath);
1093       // intersection of the current overwrite set and the current
1094       // callee's
1095       // overwrite set
1096       merge(calleeIntersectBoundOverWriteSet, calleeBoundWriteSet);
1097     }
1098
1099   }
1100
1101   private void checkFlag(boolean booleanValue, FlatNode fn, NTuple<Descriptor> hp) {
1102     if (booleanValue) {
1103       throw new Error(
1104           "There is a variable, which is reachable through references "
1105               + hp
1106               + ", who comes back to the same read statement without being overwritten at the out-most iteration at "
1107               + methodContainingSSJavaLoop.getClassDesc().getSourceFileName() + "::"
1108               + fn.getNumLine());
1109     }
1110   }
1111
1112   private void merge(Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>> curr,
1113       Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>> in) {
1114
1115     Set<NTuple<Descriptor>> inKeySet = in.keySet();
1116     for (Iterator iterator = inKeySet.iterator(); iterator.hasNext();) {
1117       NTuple<Descriptor> inKey = (NTuple<Descriptor>) iterator.next();
1118       Hashtable<FlatNode, Boolean> inPair = in.get(inKey);
1119
1120       Set<FlatNode> pairKeySet = inPair.keySet();
1121       for (Iterator iterator2 = pairKeySet.iterator(); iterator2.hasNext();) {
1122         FlatNode pairKey = (FlatNode) iterator2.next();
1123         Boolean inFlag = inPair.get(pairKey);
1124
1125         Hashtable<FlatNode, Boolean> currPair = curr.get(inKey);
1126         if (currPair == null) {
1127           currPair = new Hashtable<FlatNode, Boolean>();
1128           curr.put(inKey, currPair);
1129         }
1130
1131         Boolean currFlag = currPair.get(pairKey);
1132         // by default, flag is set by false
1133         if (currFlag == null) {
1134           currFlag = Boolean.FALSE;
1135         }
1136         currFlag = Boolean.valueOf(inFlag.booleanValue() | currFlag.booleanValue());
1137         currPair.put(pairKey, currFlag);
1138       }
1139
1140     }
1141
1142   }
1143
1144   private void methodReadOverWriteAnalysis() {
1145     // perform method READ/OVERWRITE analysis
1146     Set<MethodDescriptor> methodDescriptorsToAnalyze = new HashSet<MethodDescriptor>();
1147     methodDescriptorsToAnalyze.addAll(ssjava.getAnnotationRequireSet());
1148
1149     sortedDescriptors = topologicalSort(methodDescriptorsToAnalyze);
1150
1151     LinkedList<MethodDescriptor> descriptorListToAnalyze =
1152         (LinkedList<MethodDescriptor>) sortedDescriptors.clone();
1153
1154     // no need to analyze method having ssjava loop
1155     // methodContainingSSJavaLoop = descriptorListToAnalyze.removeFirst();
1156     methodContainingSSJavaLoop = ssjava.getMethodContainingSSJavaLoop();
1157
1158     // current descriptors to visit in fixed-point interprocedural analysis,
1159     // prioritized by
1160     // dependency in the call graph
1161     methodDescriptorsToVisitStack.clear();
1162
1163     Set<MethodDescriptor> methodDescriptorToVistSet = new HashSet<MethodDescriptor>();
1164     methodDescriptorToVistSet.addAll(descriptorListToAnalyze);
1165
1166     while (!descriptorListToAnalyze.isEmpty()) {
1167       MethodDescriptor md = descriptorListToAnalyze.removeFirst();
1168       methodDescriptorsToVisitStack.add(md);
1169     }
1170
1171     // analyze scheduled methods until there are no more to visit
1172     while (!methodDescriptorsToVisitStack.isEmpty()) {
1173       // start to analyze leaf node
1174       MethodDescriptor md = methodDescriptorsToVisitStack.pop();
1175       FlatMethod fm = state.getMethodFlat(md);
1176
1177       Set<NTuple<Descriptor>> readSet = new HashSet<NTuple<Descriptor>>();
1178       Set<NTuple<Descriptor>> overWriteSet = new HashSet<NTuple<Descriptor>>();
1179
1180       methodReadOverWrite_analyzeMethod(fm, readSet, overWriteSet);
1181
1182       Set<NTuple<Descriptor>> prevRead = mapFlatMethodToRead.get(fm);
1183       Set<NTuple<Descriptor>> prevOverWrite = mapFlatMethodToOverWrite.get(fm);
1184
1185       if (!(readSet.equals(prevRead) && overWriteSet.equals(prevOverWrite))) {
1186         mapFlatMethodToRead.put(fm, readSet);
1187         mapFlatMethodToOverWrite.put(fm, overWriteSet);
1188
1189         // results for callee changed, so enqueue dependents caller for
1190         // further
1191         // analysis
1192         Iterator<MethodDescriptor> depsItr = getDependents(md).iterator();
1193         while (depsItr.hasNext()) {
1194           MethodDescriptor methodNext = depsItr.next();
1195           if (!methodDescriptorsToVisitStack.contains(methodNext)
1196               && methodDescriptorToVistSet.contains(methodNext)) {
1197             methodDescriptorsToVisitStack.add(methodNext);
1198           }
1199
1200         }
1201
1202       }
1203
1204     }
1205
1206   }
1207
1208   private void methodReadOverWrite_analyzeMethod(FlatMethod fm, Set<NTuple<Descriptor>> readSet,
1209       Set<NTuple<Descriptor>> overWriteSet) {
1210     if (state.SSJAVADEBUG) {
1211       System.out.println("Definitely written Analyzing: " + fm);
1212     }
1213
1214     // intraprocedural analysis
1215     Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
1216     flatNodesToVisit.add(fm);
1217
1218     while (!flatNodesToVisit.isEmpty()) {
1219       FlatNode fn = flatNodesToVisit.iterator().next();
1220       flatNodesToVisit.remove(fn);
1221
1222       Set<NTuple<Descriptor>> curr = new HashSet<NTuple<Descriptor>>();
1223
1224       for (int i = 0; i < fn.numPrev(); i++) {
1225         FlatNode prevFn = fn.getPrev(i);
1226         Set<NTuple<Descriptor>> in = mapFlatNodeToWrittenSet.get(prevFn);
1227         if (in != null) {
1228           merge(curr, in);
1229         }
1230       }
1231
1232       methodReadOverWrite_nodeActions(fn, curr, readSet, overWriteSet);
1233
1234       Set<NTuple<Descriptor>> writtenSetPrev = mapFlatNodeToWrittenSet.get(fn);
1235       if (!curr.equals(writtenSetPrev)) {
1236         mapFlatNodeToWrittenSet.put(fn, curr);
1237         for (int i = 0; i < fn.numNext(); i++) {
1238           FlatNode nn = fn.getNext(i);
1239           flatNodesToVisit.add(nn);
1240         }
1241       }
1242
1243     }
1244
1245   }
1246
1247   private void methodReadOverWrite_nodeActions(FlatNode fn, Set<NTuple<Descriptor>> writtenSet,
1248       Set<NTuple<Descriptor>> readSet, Set<NTuple<Descriptor>> overWriteSet) {
1249     TempDescriptor lhs;
1250     TempDescriptor rhs;
1251     FieldDescriptor fld;
1252
1253     switch (fn.kind()) {
1254     case FKind.FlatMethod: {
1255
1256       // set up initial heap paths for method parameters
1257       FlatMethod fm = (FlatMethod) fn;
1258       for (int i = 0; i < fm.numParameters(); i++) {
1259         TempDescriptor param = fm.getParameter(i);
1260         NTuple<Descriptor> heapPath = new NTuple<Descriptor>();
1261         heapPath.add(param);
1262         mapHeapPath.put(param, heapPath);
1263       }
1264     }
1265       break;
1266
1267     case FKind.FlatOpNode: {
1268       FlatOpNode fon = (FlatOpNode) fn;
1269       // for a normal assign node, need to propagate lhs's heap path to
1270       // rhs
1271       if (fon.getOp().getOp() == Operation.ASSIGN) {
1272         rhs = fon.getLeft();
1273         lhs = fon.getDest();
1274
1275         NTuple<Descriptor> rhsHeapPath = mapHeapPath.get(rhs);
1276         if (rhsHeapPath != null) {
1277           mapHeapPath.put(lhs, mapHeapPath.get(rhs));
1278         }
1279
1280       }
1281     }
1282       break;
1283
1284     case FKind.FlatElementNode:
1285     case FKind.FlatFieldNode: {
1286
1287       // y=x.f;
1288
1289       if (fn.kind() == FKind.FlatFieldNode) {
1290         FlatFieldNode ffn = (FlatFieldNode) fn;
1291         lhs = ffn.getDst();
1292         rhs = ffn.getSrc();
1293         fld = ffn.getField();
1294       } else {
1295         FlatElementNode fen = (FlatElementNode) fn;
1296         lhs = fen.getDst();
1297         rhs = fen.getSrc();
1298         TypeDescriptor td = rhs.getType().dereference();
1299         fld = getArrayField(td);
1300       }
1301
1302       if (fld.isFinal() /* && fld.isStatic() */) {
1303         // if field is final and static, no need to check
1304         break;
1305       }
1306
1307       // set up heap path
1308       NTuple<Descriptor> srcHeapPath = mapHeapPath.get(rhs);
1309       if (srcHeapPath != null) {
1310         // if lhs srcHeapPath is null, it means that it is not reachable from
1311         // callee's parameters. so just ignore it
1312
1313         NTuple<Descriptor> readingHeapPath = new NTuple<Descriptor>(srcHeapPath.getList());
1314         readingHeapPath.add(fld);
1315         mapHeapPath.put(lhs, readingHeapPath);
1316
1317         // read (x.f)
1318         if (fld.getType().isImmutable()) {
1319           // if WT doesnot have hp(x.f), add hp(x.f) to READ
1320           if (!writtenSet.contains(readingHeapPath)) {
1321             readSet.add(readingHeapPath);
1322           }
1323         }
1324
1325         // need to kill hp(x.f) from WT
1326         writtenSet.remove(readingHeapPath);
1327       }
1328
1329     }
1330       break;
1331
1332     case FKind.FlatSetFieldNode:
1333     case FKind.FlatSetElementNode: {
1334
1335       // x.f=y;
1336
1337       if (fn.kind() == FKind.FlatSetFieldNode) {
1338         FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
1339         lhs = fsfn.getDst();
1340         fld = fsfn.getField();
1341         rhs = fsfn.getSrc();
1342       } else {
1343         FlatSetElementNode fsen = (FlatSetElementNode) fn;
1344         lhs = fsen.getDst();
1345         rhs = fsen.getSrc();
1346         TypeDescriptor td = lhs.getType().dereference();
1347         fld = getArrayField(td);
1348       }
1349
1350       // set up heap path
1351       NTuple<Descriptor> lhsHeapPath = mapHeapPath.get(lhs);
1352       if (lhsHeapPath != null) {
1353         // if lhs heap path is null, it means that it is not reachable from
1354         // callee's parameters. so just ignore it
1355         NTuple<Descriptor> newHeapPath = new NTuple<Descriptor>(lhsHeapPath.getList());
1356         newHeapPath.add(fld);
1357         mapHeapPath.put(fld, newHeapPath);
1358
1359         // write(x.f)
1360         // need to add hp(y) to WT
1361         writtenSet.add(newHeapPath);
1362       }
1363
1364     }
1365       break;
1366
1367     case FKind.FlatCall: {
1368
1369       FlatCall fc = (FlatCall) fn;
1370
1371       if (fc.getThis() != null) {
1372         bindHeapPathCallerArgWithCaleeParam(fc);
1373
1374         // add heap path, which is an element of READ_bound set and is not
1375         // an
1376         // element of WT set, to the caller's READ set
1377         for (Iterator iterator = calleeUnionBoundReadSet.iterator(); iterator.hasNext();) {
1378           NTuple<Descriptor> read = (NTuple<Descriptor>) iterator.next();
1379           if (!writtenSet.contains(read)) {
1380             readSet.add(read);
1381           }
1382         }
1383         writtenSet.removeAll(calleeUnionBoundReadSet);
1384
1385         // add heap path, which is an element of OVERWRITE_bound set, to the
1386         // caller's WT set
1387         for (Iterator iterator = calleeIntersectBoundOverWriteSet.iterator(); iterator.hasNext();) {
1388           NTuple<Descriptor> write = (NTuple<Descriptor>) iterator.next();
1389           writtenSet.add(write);
1390         }
1391       }
1392
1393     }
1394       break;
1395
1396     case FKind.FlatExit: {
1397       // merge the current written set with OVERWRITE set
1398       merge(overWriteSet, writtenSet);
1399     }
1400       break;
1401
1402     }
1403
1404   }
1405
1406   static public FieldDescriptor getArrayField(TypeDescriptor td) {
1407     FieldDescriptor fd = mapTypeToArrayField.get(td);
1408     if (fd == null) {
1409       fd =
1410           new FieldDescriptor(new Modifiers(Modifiers.PUBLIC), td, arrayElementFieldName, null,
1411               false);
1412       mapTypeToArrayField.put(td, fd);
1413     }
1414     return fd;
1415   }
1416
1417   private void mergeSharedLocationAnaylsis(ClearingSummary curr, Set<ClearingSummary> inSet) {
1418
1419     if (inSet.size() == 0) {
1420       return;
1421     }
1422
1423     Hashtable<Pair<NTuple<Descriptor>, Location>, Boolean> mapHeapPathLoc2Flag =
1424         new Hashtable<Pair<NTuple<Descriptor>, Location>, Boolean>();
1425
1426     for (Iterator inIterator = inSet.iterator(); inIterator.hasNext();) {
1427
1428       ClearingSummary inTable = (ClearingSummary) inIterator.next();
1429
1430       Set<NTuple<Descriptor>> keySet = inTable.keySet();
1431
1432       for (Iterator iterator = keySet.iterator(); iterator.hasNext();) {
1433         NTuple<Descriptor> hpKey = (NTuple<Descriptor>) iterator.next();
1434         SharedStatus inState = inTable.get(hpKey);
1435
1436         SharedStatus currState = curr.get(hpKey);
1437         if (currState == null) {
1438           currState = new SharedStatus();
1439           curr.put(hpKey, currState);
1440         }
1441         currState.merge(inState);
1442
1443         Set<Location> locSet = inState.getMap().keySet();
1444         for (Iterator iterator2 = locSet.iterator(); iterator2.hasNext();) {
1445           Location loc = (Location) iterator2.next();
1446           Pair<Set<Descriptor>, Boolean> pair = inState.getMap().get(loc);
1447           boolean inFlag = pair.getSecond().booleanValue();
1448
1449           Pair<NTuple<Descriptor>, Location> flagKey =
1450               new Pair<NTuple<Descriptor>, Location>(hpKey, loc);
1451           Boolean current = mapHeapPathLoc2Flag.get(flagKey);
1452           if (current == null) {
1453             current = new Boolean(true);
1454           }
1455           boolean newInFlag = current.booleanValue() & inFlag;
1456           mapHeapPathLoc2Flag.put(flagKey, Boolean.valueOf(newInFlag));
1457         }
1458
1459       }
1460
1461     }
1462
1463     // merge flag status
1464     Set<NTuple<Descriptor>> hpKeySet = curr.keySet();
1465     for (Iterator iterator = hpKeySet.iterator(); iterator.hasNext();) {
1466       NTuple<Descriptor> hpKey = (NTuple<Descriptor>) iterator.next();
1467       SharedStatus currState = curr.get(hpKey);
1468       Set<Location> locKeySet = currState.getMap().keySet();
1469       for (Iterator iterator2 = locKeySet.iterator(); iterator2.hasNext();) {
1470         Location locKey = (Location) iterator2.next();
1471         Pair<Set<Descriptor>, Boolean> pair = currState.getMap().get(locKey);
1472         boolean currentFlag = pair.getSecond().booleanValue();
1473         Boolean inFlag = mapHeapPathLoc2Flag.get(new Pair(hpKey, locKey));
1474         if (inFlag != null) {
1475           boolean newFlag = currentFlag | inFlag.booleanValue();
1476           if (currentFlag != newFlag) {
1477             currState.getMap().put(locKey, new Pair(pair.getFirst(), new Boolean(newFlag)));
1478           }
1479         }
1480       }
1481     }
1482
1483   }
1484
1485   private void merge(Set<NTuple<Descriptor>> curr, Set<NTuple<Descriptor>> in) {
1486     if (curr.isEmpty()) {
1487       // WrittenSet has a special initial value which covers all possible
1488       // elements
1489       // For the first time of intersection, we can take all previous set
1490       curr.addAll(in);
1491     } else {
1492       // otherwise, current set is the intersection of the two sets
1493       curr.retainAll(in);
1494     }
1495
1496   }
1497
1498   // combine two heap path
1499   private NTuple<Descriptor> combine(NTuple<Descriptor> callerIn, NTuple<Descriptor> calleeIn) {
1500     NTuple<Descriptor> combined = new NTuple<Descriptor>();
1501
1502     for (int i = 0; i < callerIn.size(); i++) {
1503       combined.add(callerIn.get(i));
1504     }
1505
1506     // the first element of callee's heap path represents parameter
1507     // so we skip the first one since it is already added from caller's heap
1508     // path
1509     for (int i = 1; i < calleeIn.size(); i++) {
1510       combined.add(calleeIn.get(i));
1511     }
1512
1513     return combined;
1514   }
1515
1516   private Set<NTuple<Descriptor>> bindSet(Set<NTuple<Descriptor>> calleeSet,
1517       Hashtable<Integer, TempDescriptor> mapParamIdx2ParamTempDesc,
1518       Hashtable<Integer, NTuple<Descriptor>> mapCallerArgIdx2HeapPath) {
1519
1520     Set<NTuple<Descriptor>> boundedCalleeSet = new HashSet<NTuple<Descriptor>>();
1521
1522     Set<Integer> keySet = mapCallerArgIdx2HeapPath.keySet();
1523     for (Iterator iterator = keySet.iterator(); iterator.hasNext();) {
1524       Integer idx = (Integer) iterator.next();
1525
1526       NTuple<Descriptor> callerArgHeapPath = mapCallerArgIdx2HeapPath.get(idx);
1527       TempDescriptor calleeParam = mapParamIdx2ParamTempDesc.get(idx);
1528
1529       for (Iterator iterator2 = calleeSet.iterator(); iterator2.hasNext();) {
1530         NTuple<Descriptor> element = (NTuple<Descriptor>) iterator2.next();
1531         if (element.startsWith(calleeParam)) {
1532           NTuple<Descriptor> boundElement = combine(callerArgHeapPath, element);
1533           boundedCalleeSet.add(boundElement);
1534         }
1535
1536       }
1537
1538     }
1539     return boundedCalleeSet;
1540
1541   }
1542
1543   // Borrowed it from disjoint analysis
1544   private LinkedList<MethodDescriptor> topologicalSort(Set<MethodDescriptor> toSort) {
1545
1546     Set<MethodDescriptor> discovered = new HashSet<MethodDescriptor>();
1547
1548     LinkedList<MethodDescriptor> sorted = new LinkedList<MethodDescriptor>();
1549
1550     Iterator<MethodDescriptor> itr = toSort.iterator();
1551     while (itr.hasNext()) {
1552       MethodDescriptor d = itr.next();
1553
1554       if (!discovered.contains(d)) {
1555         dfsVisit(d, toSort, sorted, discovered);
1556       }
1557     }
1558
1559     return sorted;
1560   }
1561
1562   // While we're doing DFS on call graph, remember
1563   // dependencies for efficient queuing of methods
1564   // during interprocedural analysis:
1565   //
1566   // a dependent of a method decriptor d for this analysis is:
1567   // 1) a method or task that invokes d
1568   // 2) in the descriptorsToAnalyze set
1569   private void dfsVisit(MethodDescriptor md, Set<MethodDescriptor> toSort,
1570       LinkedList<MethodDescriptor> sorted, Set<MethodDescriptor> discovered) {
1571
1572     discovered.add(md);
1573
1574     Iterator itr = callGraph.getCallerSet(md).iterator();
1575     while (itr.hasNext()) {
1576       MethodDescriptor dCaller = (MethodDescriptor) itr.next();
1577       // only consider callers in the original set to analyze
1578       if (!toSort.contains(dCaller)) {
1579         continue;
1580       }
1581       if (!discovered.contains(dCaller)) {
1582         addDependent(md, // callee
1583             dCaller // caller
1584         );
1585
1586         dfsVisit(dCaller, toSort, sorted, discovered);
1587       }
1588     }
1589
1590     // for leaf-nodes last now!
1591     sorted.addLast(md);
1592   }
1593
1594   // a dependent of a method decriptor d for this analysis is:
1595   // 1) a method or task that invokes d
1596   // 2) in the descriptorsToAnalyze set
1597   private void addDependent(MethodDescriptor callee, MethodDescriptor caller) {
1598     Set<MethodDescriptor> deps = mapDescriptorToSetDependents.get(callee);
1599     if (deps == null) {
1600       deps = new HashSet<MethodDescriptor>();
1601     }
1602     deps.add(caller);
1603     mapDescriptorToSetDependents.put(callee, deps);
1604   }
1605
1606   private Set<MethodDescriptor> getDependents(MethodDescriptor callee) {
1607     Set<MethodDescriptor> deps = mapDescriptorToSetDependents.get(callee);
1608     if (deps == null) {
1609       deps = new HashSet<MethodDescriptor>();
1610       mapDescriptorToSetDependents.put(callee, deps);
1611     }
1612     return deps;
1613   }
1614
1615   private NTuple<Descriptor> computePath(TempDescriptor td) {
1616     // generate proper path fot input td
1617     // if td is local variable, it just generate one element tuple path
1618     if (mapHeapPath.containsKey(td)) {
1619       return mapHeapPath.get(td);
1620     } else {
1621       NTuple<Descriptor> path = new NTuple<Descriptor>();
1622       path.add(td);
1623       return path;
1624     }
1625   }
1626
1627 }