1 package Analysis.SSJava;
3 import java.util.HashSet;
4 import java.util.Hashtable;
5 import java.util.Iterator;
6 import java.util.LinkedList;
8 import java.util.Stack;
10 import Analysis.CallGraph.CallGraph;
11 import Analysis.Loops.LoopFinder;
13 import IR.FieldDescriptor;
14 import IR.MethodDescriptor;
17 import IR.TypeDescriptor;
19 import IR.Flat.FlatCall;
20 import IR.Flat.FlatFieldNode;
21 import IR.Flat.FlatLiteralNode;
22 import IR.Flat.FlatMethod;
23 import IR.Flat.FlatNode;
24 import IR.Flat.FlatOpNode;
25 import IR.Flat.FlatSetFieldNode;
26 import IR.Flat.TempDescriptor;
29 public class DefinitelyWrittenCheck {
31 SSJavaAnalysis ssjava;
35 // maps a descriptor to its known dependents: namely
36 // methods or tasks that call the descriptor's method
37 // AND are part of this analysis (reachable from main)
38 private Hashtable<Descriptor, Set<MethodDescriptor>> mapDescriptorToSetDependents;
40 // maps a flat node to its WrittenSet: this keeps all heap path overwritten
42 private Hashtable<FlatNode, Set<NTuple<Descriptor>>> mapFlatNodeToWrittenSet;
44 // maps a temp descriptor to its heap path
45 // each temp descriptor has a unique heap path since we do not allow any
47 private Hashtable<Descriptor, NTuple<Descriptor>> mapHeapPath;
49 // maps a flat method to the READ that is the set of heap path that is
50 // expected to be written before method invocation
51 private Hashtable<FlatMethod, Set<NTuple<Descriptor>>> mapFlatMethodToRead;
53 // maps a flat method to the OVERWRITE that is the set of heap path that is
54 // overwritten on every possible path during method invocation
55 private Hashtable<FlatMethod, Set<NTuple<Descriptor>>> mapFlatMethodToOverWrite;
57 // points to method containing SSJAVA Loop
58 private MethodDescriptor methodContainingSSJavaLoop;
60 // maps a flatnode to definitely written analysis mapping M
61 private Hashtable<FlatNode, Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>>> definitelyWrittenResults;
63 // maps a method descriptor to its current summary during the analysis
64 // then analysis reaches fixed-point, this mapping will have the final summary
65 // for each method descriptor
66 private Hashtable<MethodDescriptor, Hashtable<NTuple<Descriptor>, SharedLocState>> mapMethodDescriptorToCompleteClearingSummary;
68 // maps a method descriptor to the merged incoming caller's current
70 private Hashtable<MethodDescriptor, Hashtable<NTuple<Descriptor>, SharedLocState>> mapMethodDescriptorToInitialClearingSummary;
72 // maps a flat node to current partial results
73 private Hashtable<FlatNode, Hashtable<NTuple<Descriptor>, SharedLocState>> mapFlatNodeToClearingSummary;
75 // maps shared location to the set of descriptors which belong to the shared
77 private Hashtable<Pair<NTuple<Descriptor>, Location>, Set<Descriptor>> mapSharedLocation2DescriptorSet;
79 // data structure for merging current state
80 private Hashtable<Pair<NTuple<Descriptor>, Location>, Boolean> mapHeapPathLocation2Flag;
82 private FlatNode ssjavaLoopEntrance;
83 private LoopFinder ssjavaLoop;
84 private Set<FlatNode> loopIncElements;
86 private Set<NTuple<Descriptor>> calleeUnionBoundReadSet;
87 private Set<NTuple<Descriptor>> calleeIntersectBoundOverWriteSet;
89 private TempDescriptor LOCAL;
91 public DefinitelyWrittenCheck(SSJavaAnalysis ssjava, State state) {
94 this.callGraph = ssjava.getCallGraph();
95 this.mapFlatNodeToWrittenSet = new Hashtable<FlatNode, Set<NTuple<Descriptor>>>();
96 this.mapDescriptorToSetDependents = new Hashtable<Descriptor, Set<MethodDescriptor>>();
97 this.mapHeapPath = new Hashtable<Descriptor, NTuple<Descriptor>>();
98 this.mapFlatMethodToRead = new Hashtable<FlatMethod, Set<NTuple<Descriptor>>>();
99 this.mapFlatMethodToOverWrite = new Hashtable<FlatMethod, Set<NTuple<Descriptor>>>();
100 this.definitelyWrittenResults =
101 new Hashtable<FlatNode, Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>>>();
102 this.calleeUnionBoundReadSet = new HashSet<NTuple<Descriptor>>();
103 this.calleeIntersectBoundOverWriteSet = new HashSet<NTuple<Descriptor>>();
105 this.mapMethodDescriptorToCompleteClearingSummary =
106 new Hashtable<MethodDescriptor, Hashtable<NTuple<Descriptor>, SharedLocState>>();
107 this.mapMethodDescriptorToInitialClearingSummary =
108 new Hashtable<MethodDescriptor, Hashtable<NTuple<Descriptor>, SharedLocState>>();
109 this.mapFlatNodeToClearingSummary =
110 new Hashtable<FlatNode, Hashtable<NTuple<Descriptor>, SharedLocState>>();
111 this.mapSharedLocation2DescriptorSet =
112 new Hashtable<Pair<NTuple<Descriptor>, Location>, Set<Descriptor>>();
113 this.mapHeapPathLocation2Flag = new Hashtable<Pair<NTuple<Descriptor>, Location>, Boolean>();
114 this.LOCAL = new TempDescriptor("LOCAL");
117 public void definitelyWrittenCheck() {
118 if (!ssjava.getAnnotationRequireSet().isEmpty()) {
119 methodReadOverWriteAnalysis();
121 sharedLocationAnalysis();
122 checkSharedLocationResult();
126 private void checkSharedLocationResult() {
128 Hashtable<NTuple<Descriptor>, SharedLocState> result =
129 mapFlatNodeToClearingSummary.get(ssjavaLoopEntrance);
130 System.out.println("checkSharedLocationResult=" + result);
131 Set<NTuple<Descriptor>> hpKeySet = result.keySet();
132 for (Iterator iterator = hpKeySet.iterator(); iterator.hasNext();) {
133 NTuple<Descriptor> hpKey = (NTuple<Descriptor>) iterator.next();
134 SharedLocState state = result.get(hpKey);
135 Set<Location> locKeySet = state.getLocationSet();
136 for (Iterator iterator2 = locKeySet.iterator(); iterator2.hasNext();) {
137 Location locKey = (Location) iterator2.next();
138 if (!state.getFlag(locKey)) {
140 "Some concrete locations of the shared abstract location are not cleared at the same time.");
147 private void sharedLocationAnalysis() {
148 // verify that all concrete locations of shared location are cleared out at
149 // the same time once per the out-most loop
151 computeReadSharedDescriptorSet();
153 Set<MethodDescriptor> methodDescriptorsToAnalyze = new HashSet<MethodDescriptor>();
154 methodDescriptorsToAnalyze.addAll(ssjava.getAnnotationRequireSet());
156 LinkedList<MethodDescriptor> sortedDescriptors = topologicalSort(methodDescriptorsToAnalyze);
158 Stack<MethodDescriptor> methodDescriptorsToVisitStack = new Stack<MethodDescriptor>();
159 Set<MethodDescriptor> methodDescriptorToVistSet = new HashSet<MethodDescriptor>();
160 methodDescriptorToVistSet.addAll(sortedDescriptors);
162 while (!sortedDescriptors.isEmpty()) {
163 MethodDescriptor md = sortedDescriptors.removeLast();
164 methodDescriptorsToVisitStack.add(md);
167 // analyze scheduled methods until there are no more to visit
168 while (!methodDescriptorsToVisitStack.isEmpty()) {
169 MethodDescriptor md = methodDescriptorsToVisitStack.pop();
170 FlatMethod fm = state.getMethodFlat(md);
172 sharedLocation_analyzeMethod(fm, (md.equals(methodContainingSSJavaLoop)));
176 // results for callee changed, so enqueue dependents caller for
178 Iterator<MethodDescriptor> depsItr = getDependents(md).iterator();
179 while (depsItr.hasNext()) {
180 MethodDescriptor methodNext = depsItr.next();
181 if (!methodDescriptorsToVisitStack.contains(methodNext)
182 && methodDescriptorToVistSet.contains(methodNext)) {
183 methodDescriptorsToVisitStack.add(methodNext);
192 Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
193 flatNodesToVisit.add(ssjavaLoopEntrance);
197 private void sharedLocation_analyzeMethod(FlatMethod fm, boolean onlyVisitSSJavaLoop) {
199 if (state.SSJAVADEBUG) {
200 System.out.println("Definitely written for shared locations Analyzing: " + fm + " "
201 + onlyVisitSSJavaLoop);
204 // intraprocedural analysis
205 Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
207 if (onlyVisitSSJavaLoop) {
208 flatNodesToVisit.add(ssjavaLoopEntrance);
210 flatNodesToVisit.add(fm);
213 while (!flatNodesToVisit.isEmpty()) {
214 FlatNode fn = flatNodesToVisit.iterator().next();
215 flatNodesToVisit.remove(fn);
217 Hashtable<NTuple<Descriptor>, SharedLocState> curr =
218 new Hashtable<NTuple<Descriptor>, SharedLocState>();
220 mapHeapPathLocation2Flag.clear();
221 for (int i = 0; i < fn.numPrev(); i++) {
222 FlatNode prevFn = fn.getPrev(i);
223 Hashtable<NTuple<Descriptor>, SharedLocState> in = mapFlatNodeToClearingSummary.get(prevFn);
225 mergeSharedAnaylsis(curr, in);
229 Set<NTuple<Descriptor>> hpKeySet = curr.keySet();
230 for (Iterator iterator = hpKeySet.iterator(); iterator.hasNext();) {
231 NTuple<Descriptor> hpKey = (NTuple<Descriptor>) iterator.next();
232 SharedLocState currState = curr.get(hpKey);
233 Set<Location> locKeySet = currState.getMap().keySet();
234 for (Iterator iterator2 = locKeySet.iterator(); iterator2.hasNext();) {
235 Location locKey = (Location) iterator2.next();
236 Pair<Set<Descriptor>, Boolean> pair = currState.getMap().get(locKey);
237 boolean currentFlag = pair.getSecond().booleanValue();
238 Boolean inFlag = mapHeapPathLocation2Flag.get(new Pair(hpKey, locKey));
239 boolean newFlag = currentFlag | inFlag.booleanValue();
240 if (currentFlag != newFlag) {
241 currState.getMap().put(locKey, new Pair(pair.getFirst(), new Boolean(newFlag)));
246 sharedLocation_nodeActions(fn, curr, onlyVisitSSJavaLoop);
248 Hashtable<NTuple<Descriptor>, SharedLocState> clearingPrev =
249 mapFlatNodeToClearingSummary.get(fn);
251 if (!curr.equals(clearingPrev)) {
252 mapFlatNodeToClearingSummary.put(fn, curr);
254 for (int i = 0; i < fn.numNext(); i++) {
255 FlatNode nn = fn.getNext(i);
257 if (!onlyVisitSSJavaLoop || (onlyVisitSSJavaLoop && loopIncElements.contains(nn))) {
258 flatNodesToVisit.add(nn);
268 private void sharedLocation_nodeActions(FlatNode fn,
269 Hashtable<NTuple<Descriptor>, SharedLocState> curr, boolean isSSJavaLoop) {
276 case FKind.FlatOpNode: {
277 FlatOpNode fon = (FlatOpNode) fn;
281 if (fon.getOp().getOp() == Operation.ASSIGN) {
282 if (rhs.getType().isImmutable() && isSSJavaLoop) {
283 // in ssjavaloop, we need to take care about reading local variables!
284 NTuple<Descriptor> rhsHeapPath = new NTuple<Descriptor>();
285 NTuple<Descriptor> lhsHeapPath = new NTuple<Descriptor>();
286 rhsHeapPath.add(LOCAL);
287 lhsHeapPath.add(LOCAL);
288 if (!lhs.getSymbol().startsWith("neverused")) {
289 readLocation(curr, rhsHeapPath, rhs);
290 writeLocation(curr, lhsHeapPath, lhs);
298 case FKind.FlatFieldNode:
299 case FKind.FlatElementNode: {
301 FlatFieldNode ffn = (FlatFieldNode) fn;
304 fld = ffn.getField();
307 NTuple<Descriptor> srcHeapPath = mapHeapPath.get(rhs);
308 NTuple<Descriptor> fldHeapPath = new NTuple<Descriptor>(srcHeapPath.getList());
310 if (fld.getType().isImmutable()) {
311 readLocation(curr, fldHeapPath, fld);
317 case FKind.FlatSetFieldNode:
318 case FKind.FlatSetElementNode: {
320 FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
322 fld = fsfn.getField();
325 NTuple<Descriptor> lhsHeapPath = computePath(lhs);
326 NTuple<Descriptor> fldHeapPath = new NTuple<Descriptor>(lhsHeapPath.getList());
328 if (fld.getType().isImmutable()) {
329 writeLocation(curr, fldHeapPath, fld);
335 case FKind.FlatCall: {
343 private void computeReadSharedDescriptorSet() {
344 Set<MethodDescriptor> methodDescriptorsToAnalyze = new HashSet<MethodDescriptor>();
345 methodDescriptorsToAnalyze.addAll(ssjava.getAnnotationRequireSet());
347 for (Iterator iterator = methodDescriptorsToAnalyze.iterator(); iterator.hasNext();) {
348 MethodDescriptor md = (MethodDescriptor) iterator.next();
349 FlatMethod fm = state.getMethodFlat(md);
350 computeReadSharedDescriptorSet_analyzeMethod(fm, md.equals(methodContainingSSJavaLoop));
355 private void computeReadSharedDescriptorSet_analyzeMethod(FlatMethod fm,
356 boolean onlyVisitSSJavaLoop) {
358 Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
359 Set<FlatNode> visited = new HashSet<FlatNode>();
361 if (onlyVisitSSJavaLoop) {
362 flatNodesToVisit.add(ssjavaLoopEntrance);
364 flatNodesToVisit.add(fm);
367 while (!flatNodesToVisit.isEmpty()) {
368 FlatNode fn = flatNodesToVisit.iterator().next();
369 flatNodesToVisit.remove(fn);
372 computeReadSharedDescriptorSet_nodeActions(fn, onlyVisitSSJavaLoop);
374 for (int i = 0; i < fn.numNext(); i++) {
375 FlatNode nn = fn.getNext(i);
376 if (!visited.contains(nn)) {
377 if (!onlyVisitSSJavaLoop || (onlyVisitSSJavaLoop && loopIncElements.contains(nn))) {
378 flatNodesToVisit.add(nn);
387 private void computeReadSharedDescriptorSet_nodeActions(FlatNode fn, boolean isSSJavaLoop) {
394 case FKind.FlatOpNode: {
395 FlatOpNode fon = (FlatOpNode) fn;
399 if (fon.getOp().getOp() == Operation.ASSIGN) {
400 if (rhs.getType().isImmutable() && isSSJavaLoop && (!rhs.getSymbol().startsWith("srctmp"))) {
401 // in ssjavaloop, we need to take care about reading local variables!
402 NTuple<Descriptor> rhsHeapPath = new NTuple<Descriptor>();
403 NTuple<Descriptor> lhsHeapPath = new NTuple<Descriptor>();
404 rhsHeapPath.add(LOCAL);
405 addReadDescriptor(rhsHeapPath, rhs);
412 case FKind.FlatFieldNode:
413 case FKind.FlatElementNode: {
415 FlatFieldNode ffn = (FlatFieldNode) fn;
418 fld = ffn.getField();
421 NTuple<Descriptor> srcHeapPath = mapHeapPath.get(rhs);
422 NTuple<Descriptor> fldHeapPath = new NTuple<Descriptor>(srcHeapPath.getList());
423 // fldHeapPath.add(fld);
425 if (fld.getType().isImmutable()) {
426 addReadDescriptor(fldHeapPath, fld);
429 // propagate rhs's heap path to the lhs
430 mapHeapPath.put(lhs, fldHeapPath);
435 case FKind.FlatSetFieldNode:
436 case FKind.FlatSetElementNode: {
438 FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
440 fld = fsfn.getField();
443 NTuple<Descriptor> lhsHeapPath = computePath(lhs);
444 NTuple<Descriptor> fldHeapPath = new NTuple<Descriptor>(lhsHeapPath.getList());
445 // writeLocation(curr, fldHeapPath, fld, getLocation(fld));
453 private void addReadDescriptor(NTuple<Descriptor> hp, Descriptor d) {
455 Location loc = getLocation(d);
457 if (loc != null && ssjava.isSharedLocation(loc)) {
459 Pair key = new Pair(hp, loc);
460 Set<Descriptor> set = mapSharedLocation2DescriptorSet.get(key);
462 set = new HashSet<Descriptor>();
463 mapSharedLocation2DescriptorSet.put(key, set);
470 private Location getLocation(Descriptor d) {
472 if (d instanceof FieldDescriptor) {
473 return (Location) ((FieldDescriptor) d).getType().getExtension();
475 assert d instanceof TempDescriptor;
476 CompositeLocation comp = (CompositeLocation) ((TempDescriptor) d).getType().getExtension();
480 return comp.get(comp.getSize() - 1);
486 private void writeLocation(Hashtable<NTuple<Descriptor>, SharedLocState> curr,
487 NTuple<Descriptor> hp, Descriptor d) {
488 Location loc = getLocation(d);
489 if (loc != null && ssjava.isSharedLocation(loc)) {
490 SharedLocState state = getState(curr, hp);
491 state.addVar(loc, d);
493 // if the set v contains all of variables belonging to the shared
494 // location, set flag to true
496 Set<Descriptor> sharedVarSet =
497 mapSharedLocation2DescriptorSet.get(new Pair<NTuple<Descriptor>, Location>(hp, loc));
499 if (state.getVarSet(loc).containsAll(sharedVarSet)) {
500 state.updateFlag(loc, true);
505 private void readLocation(Hashtable<NTuple<Descriptor>, SharedLocState> curr,
506 NTuple<Descriptor> hp, Descriptor d) {
507 // remove reading var x from written set
508 Location loc = getLocation(d);
509 if (loc != null && ssjava.isSharedLocation(loc)) {
510 SharedLocState state = getState(curr, hp);
511 state.removeVar(loc, d);
515 private SharedLocState getState(Hashtable<NTuple<Descriptor>, SharedLocState> curr,
516 NTuple<Descriptor> hp) {
517 SharedLocState state = curr.get(hp);
519 state = new SharedLocState();
525 private void writtenAnalyis() {
526 // perform second stage analysis: intraprocedural analysis ensure that
528 // variables are definitely written in-between the same read
530 // First, identify ssjava loop entrace
531 FlatMethod fm = state.getMethodFlat(methodContainingSSJavaLoop);
532 Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
533 flatNodesToVisit.add(fm);
535 LoopFinder loopFinder = new LoopFinder(fm);
537 while (!flatNodesToVisit.isEmpty()) {
538 FlatNode fn = flatNodesToVisit.iterator().next();
539 flatNodesToVisit.remove(fn);
541 String label = (String) state.fn2labelMap.get(fn);
544 if (label.equals(ssjava.SSJAVA)) {
545 ssjavaLoopEntrance = fn;
550 for (int i = 0; i < fn.numNext(); i++) {
551 FlatNode nn = fn.getNext(i);
552 flatNodesToVisit.add(nn);
556 assert ssjavaLoopEntrance != null;
558 // assume that ssjava loop is top-level loop in method, not nested loop
559 Set nestedLoop = loopFinder.nestedLoops();
560 for (Iterator loopIter = nestedLoop.iterator(); loopIter.hasNext();) {
561 LoopFinder lf = (LoopFinder) loopIter.next();
562 if (lf.loopEntrances().iterator().next().equals(ssjavaLoopEntrance)) {
567 assert ssjavaLoop != null;
569 writtenAnalysis_analyzeLoop();
573 private void writtenAnalysis_analyzeLoop() {
575 Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
576 flatNodesToVisit.add(ssjavaLoopEntrance);
578 loopIncElements = (Set<FlatNode>) ssjavaLoop.loopIncElements();
580 while (!flatNodesToVisit.isEmpty()) {
581 FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next();
582 flatNodesToVisit.remove(fn);
584 Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>> prev =
585 definitelyWrittenResults.get(fn);
587 Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>> curr =
588 new Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>>();
589 for (int i = 0; i < fn.numPrev(); i++) {
590 FlatNode nn = fn.getPrev(i);
591 Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>> dwIn =
592 definitelyWrittenResults.get(nn);
598 writtenAnalysis_nodeAction(fn, curr, ssjavaLoopEntrance);
600 // if a new result, schedule forward nodes for analysis
601 if (!curr.equals(prev)) {
602 definitelyWrittenResults.put(fn, curr);
604 for (int i = 0; i < fn.numNext(); i++) {
605 FlatNode nn = fn.getNext(i);
606 if (loopIncElements.contains(nn)) {
607 flatNodesToVisit.add(nn);
615 private void writtenAnalysis_nodeAction(FlatNode fn,
616 Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>> curr, FlatNode loopEntrance) {
618 if (fn.equals(loopEntrance)) {
619 // it reaches loop entrance: changes all flag to true
620 Set<NTuple<Descriptor>> keySet = curr.keySet();
621 for (Iterator iterator = keySet.iterator(); iterator.hasNext();) {
622 NTuple<Descriptor> key = (NTuple<Descriptor>) iterator.next();
623 Hashtable<FlatNode, Boolean> pair = curr.get(key);
625 Set<FlatNode> pairKeySet = pair.keySet();
626 for (Iterator iterator2 = pairKeySet.iterator(); iterator2.hasNext();) {
627 FlatNode pairKey = (FlatNode) iterator2.next();
628 pair.put(pairKey, Boolean.TRUE);
638 case FKind.FlatOpNode: {
639 FlatOpNode fon = (FlatOpNode) fn;
643 NTuple<Descriptor> rhsHeapPath = computePath(rhs);
644 if (!rhs.getType().isImmutable()) {
645 mapHeapPath.put(lhs, rhsHeapPath);
647 if (fon.getOp().getOp() == Operation.ASSIGN) {
649 readValue(fn, rhsHeapPath, curr);
652 NTuple<Descriptor> lhsHeapPath = computePath(lhs);
653 removeHeapPath(curr, lhsHeapPath);
658 case FKind.FlatLiteralNode: {
659 FlatLiteralNode fln = (FlatLiteralNode) fn;
663 NTuple<Descriptor> lhsHeapPath = computePath(lhs);
664 removeHeapPath(curr, lhsHeapPath);
669 case FKind.FlatFieldNode:
670 case FKind.FlatElementNode: {
672 FlatFieldNode ffn = (FlatFieldNode) fn;
675 fld = ffn.getField();
678 NTuple<Descriptor> srcHeapPath = mapHeapPath.get(rhs);
679 NTuple<Descriptor> fldHeapPath = new NTuple<Descriptor>(srcHeapPath.getList());
680 fldHeapPath.add(fld);
682 if (fld.getType().isImmutable()) {
683 readValue(fn, fldHeapPath, curr);
686 // propagate rhs's heap path to the lhs
687 mapHeapPath.put(lhs, fldHeapPath);
692 case FKind.FlatSetFieldNode:
693 case FKind.FlatSetElementNode: {
695 FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
697 fld = fsfn.getField();
700 NTuple<Descriptor> lhsHeapPath = computePath(lhs);
701 NTuple<Descriptor> fldHeapPath = new NTuple<Descriptor>(lhsHeapPath.getList());
702 fldHeapPath.add(fld);
703 removeHeapPath(curr, fldHeapPath);
708 case FKind.FlatCall: {
709 FlatCall fc = (FlatCall) fn;
710 bindHeapPathCallerArgWithCaleeParam(fc);
712 // add <hp,statement,false> in which hp is an element of
714 // of callee: callee has 'read' requirement!
715 for (Iterator iterator = calleeUnionBoundReadSet.iterator(); iterator.hasNext();) {
716 NTuple<Descriptor> read = (NTuple<Descriptor>) iterator.next();
718 Hashtable<FlatNode, Boolean> gen = curr.get(read);
720 gen = new Hashtable<FlatNode, Boolean>();
723 Boolean currentStatus = gen.get(fn);
724 if (currentStatus == null) {
725 gen.put(fn, Boolean.FALSE);
727 checkFlag(currentStatus.booleanValue(), fn, read);
731 // removes <hp,statement,flag> if hp is an element of
733 // set of callee. it means that callee will overwrite it
734 for (Iterator iterator = calleeIntersectBoundOverWriteSet.iterator(); iterator.hasNext();) {
735 NTuple<Descriptor> write = (NTuple<Descriptor>) iterator.next();
736 removeHeapPath(curr, write);
746 private void readValue(FlatNode fn, NTuple<Descriptor> hp,
747 Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>> curr) {
748 Hashtable<FlatNode, Boolean> gen = curr.get(hp);
750 gen = new Hashtable<FlatNode, Boolean>();
753 Boolean currentStatus = gen.get(fn);
754 if (currentStatus == null) {
755 gen.put(fn, Boolean.FALSE);
757 checkFlag(currentStatus.booleanValue(), fn, hp);
762 private void removeHeapPath(Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>> curr,
763 NTuple<Descriptor> hp) {
765 // removes all of heap path that starts with prefix 'hp'
766 // since any reference overwrite along heap path gives overwriting side
767 // effects on the value
769 Set<NTuple<Descriptor>> keySet = curr.keySet();
770 for (Iterator<NTuple<Descriptor>> iter = keySet.iterator(); iter.hasNext();) {
771 NTuple<Descriptor> key = iter.next();
772 if (key.startsWith(hp)) {
773 curr.put(key, new Hashtable<FlatNode, Boolean>());
779 private void bindHeapPathCallerArgWithCaleeParam(FlatCall fc) {
780 // compute all possible callee set
781 // transform all READ/OVERWRITE set from the any possible
785 calleeUnionBoundReadSet.clear();
786 calleeIntersectBoundOverWriteSet.clear();
788 MethodDescriptor mdCallee = fc.getMethod();
789 FlatMethod fmCallee = state.getMethodFlat(mdCallee);
790 Set<MethodDescriptor> setPossibleCallees = new HashSet<MethodDescriptor>();
791 TypeDescriptor typeDesc = fc.getThis().getType();
792 setPossibleCallees.addAll(callGraph.getMethods(mdCallee, typeDesc));
794 // create mapping from arg idx to its heap paths
795 Hashtable<Integer, NTuple<Descriptor>> mapArgIdx2CallerArgHeapPath =
796 new Hashtable<Integer, NTuple<Descriptor>>();
798 // arg idx is starting from 'this' arg
799 NTuple<Descriptor> thisHeapPath = mapHeapPath.get(fc.getThis());
800 if (thisHeapPath == null) {
801 // method is called without creating new flat node representing 'this'
802 thisHeapPath = new NTuple<Descriptor>();
803 thisHeapPath.add(fc.getThis());
806 mapArgIdx2CallerArgHeapPath.put(Integer.valueOf(0), thisHeapPath);
808 for (int i = 0; i < fc.numArgs(); i++) {
809 TempDescriptor arg = fc.getArg(i);
810 NTuple<Descriptor> argHeapPath = computePath(arg);
811 mapArgIdx2CallerArgHeapPath.put(Integer.valueOf(i + 1), argHeapPath);
814 for (Iterator iterator = setPossibleCallees.iterator(); iterator.hasNext();) {
815 MethodDescriptor callee = (MethodDescriptor) iterator.next();
816 FlatMethod calleeFlatMethod = state.getMethodFlat(callee);
818 // binding caller's args and callee's params
820 Set<NTuple<Descriptor>> calleeReadSet = mapFlatMethodToRead.get(calleeFlatMethod);
821 if (calleeReadSet == null) {
822 calleeReadSet = new HashSet<NTuple<Descriptor>>();
823 mapFlatMethodToRead.put(calleeFlatMethod, calleeReadSet);
825 Set<NTuple<Descriptor>> calleeOverWriteSet = mapFlatMethodToOverWrite.get(calleeFlatMethod);
826 if (calleeOverWriteSet == null) {
827 calleeOverWriteSet = new HashSet<NTuple<Descriptor>>();
828 mapFlatMethodToOverWrite.put(calleeFlatMethod, calleeOverWriteSet);
831 Hashtable<Integer, TempDescriptor> mapParamIdx2ParamTempDesc =
832 new Hashtable<Integer, TempDescriptor>();
833 for (int i = 0; i < calleeFlatMethod.numParameters(); i++) {
834 TempDescriptor param = calleeFlatMethod.getParameter(i);
835 mapParamIdx2ParamTempDesc.put(Integer.valueOf(i), param);
838 Set<NTuple<Descriptor>> calleeBoundReadSet =
839 bindSet(calleeReadSet, mapParamIdx2ParamTempDesc, mapArgIdx2CallerArgHeapPath);
840 // union of the current read set and the current callee's
842 calleeUnionBoundReadSet.addAll(calleeBoundReadSet);
843 Set<NTuple<Descriptor>> calleeBoundWriteSet =
844 bindSet(calleeOverWriteSet, mapParamIdx2ParamTempDesc, mapArgIdx2CallerArgHeapPath);
845 // intersection of the current overwrite set and the current
848 merge(calleeIntersectBoundOverWriteSet, calleeBoundWriteSet);
853 private void checkFlag(boolean booleanValue, FlatNode fn, NTuple<Descriptor> hp) {
856 "There is a variable, which is reachable through references "
858 + ", who comes back to the same read statement without being overwritten at the out-most iteration at "
859 + methodContainingSSJavaLoop.getClassDesc().getSourceFileName() + "::"
864 private void merge(Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>> curr,
865 Hashtable<NTuple<Descriptor>, Hashtable<FlatNode, Boolean>> in) {
867 Set<NTuple<Descriptor>> inKeySet = in.keySet();
868 for (Iterator iterator = inKeySet.iterator(); iterator.hasNext();) {
869 NTuple<Descriptor> inKey = (NTuple<Descriptor>) iterator.next();
870 Hashtable<FlatNode, Boolean> inPair = in.get(inKey);
872 Set<FlatNode> pairKeySet = inPair.keySet();
873 for (Iterator iterator2 = pairKeySet.iterator(); iterator2.hasNext();) {
874 FlatNode pairKey = (FlatNode) iterator2.next();
875 Boolean inFlag = inPair.get(pairKey);
877 Hashtable<FlatNode, Boolean> currPair = curr.get(inKey);
878 if (currPair == null) {
879 currPair = new Hashtable<FlatNode, Boolean>();
880 curr.put(inKey, currPair);
883 Boolean currFlag = currPair.get(pairKey);
884 // by default, flag is set by false
885 if (currFlag == null) {
886 currFlag = Boolean.FALSE;
888 currFlag = Boolean.valueOf(inFlag.booleanValue() | currFlag.booleanValue());
889 currPair.put(pairKey, currFlag);
896 private void methodReadOverWriteAnalysis() {
897 // perform method READ/OVERWRITE analysis
898 Set<MethodDescriptor> methodDescriptorsToAnalyze = new HashSet<MethodDescriptor>();
899 methodDescriptorsToAnalyze.addAll(ssjava.getAnnotationRequireSet());
901 LinkedList<MethodDescriptor> sortedDescriptors = topologicalSort(methodDescriptorsToAnalyze);
903 // no need to analyze method having ssjava loop
904 methodContainingSSJavaLoop = sortedDescriptors.removeFirst();
906 // current descriptors to visit in fixed-point interprocedural analysis,
908 // dependency in the call graph
909 Stack<MethodDescriptor> methodDescriptorsToVisitStack = new Stack<MethodDescriptor>();
911 Set<MethodDescriptor> methodDescriptorToVistSet = new HashSet<MethodDescriptor>();
912 methodDescriptorToVistSet.addAll(sortedDescriptors);
914 while (!sortedDescriptors.isEmpty()) {
915 MethodDescriptor md = sortedDescriptors.removeFirst();
916 methodDescriptorsToVisitStack.add(md);
919 // analyze scheduled methods until there are no more to visit
920 while (!methodDescriptorsToVisitStack.isEmpty()) {
921 // start to analyze leaf node
922 MethodDescriptor md = methodDescriptorsToVisitStack.pop();
923 FlatMethod fm = state.getMethodFlat(md);
925 Set<NTuple<Descriptor>> readSet = new HashSet<NTuple<Descriptor>>();
926 Set<NTuple<Descriptor>> overWriteSet = new HashSet<NTuple<Descriptor>>();
928 methodReadOverWrite_analyzeMethod(fm, readSet, overWriteSet);
930 Set<NTuple<Descriptor>> prevRead = mapFlatMethodToRead.get(fm);
931 Set<NTuple<Descriptor>> prevOverWrite = mapFlatMethodToOverWrite.get(fm);
933 if (!(readSet.equals(prevRead) && overWriteSet.equals(prevOverWrite))) {
934 mapFlatMethodToRead.put(fm, readSet);
935 mapFlatMethodToOverWrite.put(fm, overWriteSet);
937 // results for callee changed, so enqueue dependents caller for
940 Iterator<MethodDescriptor> depsItr = getDependents(md).iterator();
941 while (depsItr.hasNext()) {
942 MethodDescriptor methodNext = depsItr.next();
943 if (!methodDescriptorsToVisitStack.contains(methodNext)
944 && methodDescriptorToVistSet.contains(methodNext)) {
945 methodDescriptorsToVisitStack.add(methodNext);
956 private void methodReadOverWrite_analyzeMethod(FlatMethod fm, Set<NTuple<Descriptor>> readSet,
957 Set<NTuple<Descriptor>> overWriteSet) {
958 if (state.SSJAVADEBUG) {
959 System.out.println("Definitely written Analyzing: " + fm);
962 // intraprocedural analysis
963 Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
964 flatNodesToVisit.add(fm);
966 while (!flatNodesToVisit.isEmpty()) {
967 FlatNode fn = flatNodesToVisit.iterator().next();
968 flatNodesToVisit.remove(fn);
970 Set<NTuple<Descriptor>> curr = new HashSet<NTuple<Descriptor>>();
972 for (int i = 0; i < fn.numPrev(); i++) {
973 FlatNode prevFn = fn.getPrev(i);
974 Set<NTuple<Descriptor>> in = mapFlatNodeToWrittenSet.get(prevFn);
980 methodReadOverWrite_nodeActions(fn, curr, readSet, overWriteSet);
982 Set<NTuple<Descriptor>> writtenSetPrev = mapFlatNodeToWrittenSet.get(fn);
983 if (!curr.equals(writtenSetPrev)) {
984 mapFlatNodeToWrittenSet.put(fn, curr);
985 for (int i = 0; i < fn.numNext(); i++) {
986 FlatNode nn = fn.getNext(i);
987 flatNodesToVisit.add(nn);
995 private void methodReadOverWrite_nodeActions(FlatNode fn, Set<NTuple<Descriptor>> writtenSet,
996 Set<NTuple<Descriptor>> readSet, Set<NTuple<Descriptor>> overWriteSet) {
1001 switch (fn.kind()) {
1002 case FKind.FlatMethod: {
1004 // set up initial heap paths for method parameters
1005 FlatMethod fm = (FlatMethod) fn;
1006 for (int i = 0; i < fm.numParameters(); i++) {
1007 TempDescriptor param = fm.getParameter(i);
1008 NTuple<Descriptor> heapPath = new NTuple<Descriptor>();
1009 heapPath.add(param);
1010 mapHeapPath.put(param, heapPath);
1015 case FKind.FlatOpNode: {
1016 FlatOpNode fon = (FlatOpNode) fn;
1017 // for a normal assign node, need to propagate lhs's heap path to
1019 if (fon.getOp().getOp() == Operation.ASSIGN) {
1020 rhs = fon.getLeft();
1021 lhs = fon.getDest();
1023 NTuple<Descriptor> rhsHeapPath = mapHeapPath.get(rhs);
1024 if (rhsHeapPath != null) {
1025 mapHeapPath.put(lhs, mapHeapPath.get(rhs));
1032 case FKind.FlatFieldNode:
1033 case FKind.FlatElementNode: {
1037 FlatFieldNode ffn = (FlatFieldNode) fn;
1040 fld = ffn.getField();
1043 NTuple<Descriptor> srcHeapPath = mapHeapPath.get(rhs);
1044 if (srcHeapPath != null) {
1045 // if lhs srcHeapPath is null, it means that it is not reachable from
1046 // callee's parameters. so just ignore it
1048 NTuple<Descriptor> readingHeapPath = new NTuple<Descriptor>(srcHeapPath.getList());
1049 readingHeapPath.add(fld);
1050 mapHeapPath.put(lhs, readingHeapPath);
1053 if (fld.getType().isImmutable()) {
1054 // if WT doesnot have hp(x.f), add hp(x.f) to READ
1055 if (!writtenSet.contains(readingHeapPath)) {
1056 readSet.add(readingHeapPath);
1060 // need to kill hp(x.f) from WT
1061 writtenSet.remove(readingHeapPath);
1067 case FKind.FlatSetFieldNode:
1068 case FKind.FlatSetElementNode: {
1071 FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
1072 lhs = fsfn.getDst();
1073 fld = fsfn.getField();
1074 rhs = fsfn.getSrc();
1077 NTuple<Descriptor> lhsHeapPath = mapHeapPath.get(lhs);
1078 if (lhsHeapPath != null) {
1079 // if lhs heap path is null, it means that it is not reachable from
1080 // callee's parameters. so just ignore it
1081 NTuple<Descriptor> newHeapPath = new NTuple<Descriptor>(lhsHeapPath.getList());
1082 newHeapPath.add(fld);
1083 mapHeapPath.put(fld, newHeapPath);
1086 // need to add hp(y) to WT
1087 writtenSet.add(newHeapPath);
1093 case FKind.FlatCall: {
1095 FlatCall fc = (FlatCall) fn;
1097 bindHeapPathCallerArgWithCaleeParam(fc);
1099 // add heap path, which is an element of READ_bound set and is not
1101 // element of WT set, to the caller's READ set
1102 for (Iterator iterator = calleeUnionBoundReadSet.iterator(); iterator.hasNext();) {
1103 NTuple<Descriptor> read = (NTuple<Descriptor>) iterator.next();
1104 if (!writtenSet.contains(read)) {
1108 writtenSet.removeAll(calleeUnionBoundReadSet);
1110 // add heap path, which is an element of OVERWRITE_bound set, to the
1112 for (Iterator iterator = calleeIntersectBoundOverWriteSet.iterator(); iterator.hasNext();) {
1113 NTuple<Descriptor> write = (NTuple<Descriptor>) iterator.next();
1114 writtenSet.add(write);
1120 case FKind.FlatExit: {
1121 // merge the current written set with OVERWRITE set
1122 merge(overWriteSet, writtenSet);
1130 private void mergeSharedAnaylsis(Hashtable<NTuple<Descriptor>, SharedLocState> curr,
1131 Hashtable<NTuple<Descriptor>, SharedLocState> in) {
1133 Set<NTuple<Descriptor>> keySet = in.keySet();
1134 for (Iterator iterator = keySet.iterator(); iterator.hasNext();) {
1135 NTuple<Descriptor> hpKey = (NTuple<Descriptor>) iterator.next();
1136 SharedLocState inState = in.get(hpKey);
1138 SharedLocState currState = curr.get(hpKey);
1139 if (currState == null) {
1140 currState = new SharedLocState();
1141 curr.put(hpKey, currState);
1143 currState.merge(inState);
1145 Set<Location> locSet = inState.getMap().keySet();
1146 for (Iterator iterator2 = locSet.iterator(); iterator2.hasNext();) {
1147 Location loc = (Location) iterator2.next();
1148 Pair<Set<Descriptor>, Boolean> pair = inState.getMap().get(loc);
1149 boolean inFlag = pair.getSecond().booleanValue();
1151 Pair<NTuple<Descriptor>, Location> flagKey =
1152 new Pair<NTuple<Descriptor>, Location>(hpKey, loc);
1153 Boolean current = mapHeapPathLocation2Flag.get(flagKey);
1154 if (current == null) {
1155 current = new Boolean(true);
1157 boolean newInFlag = current.booleanValue() & inFlag;
1158 mapHeapPathLocation2Flag.put(flagKey, Boolean.valueOf(newInFlag));
1165 private void merge(Set<NTuple<Descriptor>> curr, Set<NTuple<Descriptor>> in) {
1166 if (curr.isEmpty()) {
1167 // WrittenSet has a special initial value which covers all possible
1169 // For the first time of intersection, we can take all previous set
1172 // otherwise, current set is the intersection of the two sets
1178 // combine two heap path
1179 private NTuple<Descriptor> combine(NTuple<Descriptor> callerIn, NTuple<Descriptor> calleeIn) {
1180 NTuple<Descriptor> combined = new NTuple<Descriptor>();
1182 for (int i = 0; i < callerIn.size(); i++) {
1183 combined.add(callerIn.get(i));
1186 // the first element of callee's heap path represents parameter
1187 // so we skip the first one since it is already added from caller's heap
1189 for (int i = 1; i < calleeIn.size(); i++) {
1190 combined.add(calleeIn.get(i));
1196 private Set<NTuple<Descriptor>> bindSet(Set<NTuple<Descriptor>> calleeSet,
1197 Hashtable<Integer, TempDescriptor> mapParamIdx2ParamTempDesc,
1198 Hashtable<Integer, NTuple<Descriptor>> mapCallerArgIdx2HeapPath) {
1200 Set<NTuple<Descriptor>> boundedCalleeSet = new HashSet<NTuple<Descriptor>>();
1202 Set<Integer> keySet = mapCallerArgIdx2HeapPath.keySet();
1203 for (Iterator iterator = keySet.iterator(); iterator.hasNext();) {
1204 Integer idx = (Integer) iterator.next();
1206 NTuple<Descriptor> callerArgHeapPath = mapCallerArgIdx2HeapPath.get(idx);
1207 TempDescriptor calleeParam = mapParamIdx2ParamTempDesc.get(idx);
1209 for (Iterator iterator2 = calleeSet.iterator(); iterator2.hasNext();) {
1210 NTuple<Descriptor> element = (NTuple<Descriptor>) iterator2.next();
1211 if (element.startsWith(calleeParam)) {
1212 NTuple<Descriptor> boundElement = combine(callerArgHeapPath, element);
1213 boundedCalleeSet.add(boundElement);
1219 return boundedCalleeSet;
1223 // Borrowed it from disjoint analysis
1224 private LinkedList<MethodDescriptor> topologicalSort(Set<MethodDescriptor> toSort) {
1226 Set<MethodDescriptor> discovered = new HashSet<MethodDescriptor>();
1228 LinkedList<MethodDescriptor> sorted = new LinkedList<MethodDescriptor>();
1230 Iterator<MethodDescriptor> itr = toSort.iterator();
1231 while (itr.hasNext()) {
1232 MethodDescriptor d = itr.next();
1234 if (!discovered.contains(d)) {
1235 dfsVisit(d, toSort, sorted, discovered);
1242 // While we're doing DFS on call graph, remember
1243 // dependencies for efficient queuing of methods
1244 // during interprocedural analysis:
1246 // a dependent of a method decriptor d for this analysis is:
1247 // 1) a method or task that invokes d
1248 // 2) in the descriptorsToAnalyze set
1249 private void dfsVisit(MethodDescriptor md, Set<MethodDescriptor> toSort,
1250 LinkedList<MethodDescriptor> sorted, Set<MethodDescriptor> discovered) {
1254 // otherwise call graph guides DFS
1255 Iterator itr = callGraph.getCallerSet(md).iterator();
1256 while (itr.hasNext()) {
1257 MethodDescriptor dCaller = (MethodDescriptor) itr.next();
1259 // only consider callers in the original set to analyze
1260 if (!toSort.contains(dCaller)) {
1264 if (!discovered.contains(dCaller)) {
1265 addDependent(md, // callee
1269 dfsVisit(dCaller, toSort, sorted, discovered);
1273 // for leaf-nodes last now!
1277 // a dependent of a method decriptor d for this analysis is:
1278 // 1) a method or task that invokes d
1279 // 2) in the descriptorsToAnalyze set
1280 private void addDependent(MethodDescriptor callee, MethodDescriptor caller) {
1281 Set<MethodDescriptor> deps = mapDescriptorToSetDependents.get(callee);
1283 deps = new HashSet<MethodDescriptor>();
1286 mapDescriptorToSetDependents.put(callee, deps);
1289 private Set<MethodDescriptor> getDependents(MethodDescriptor callee) {
1290 Set<MethodDescriptor> deps = mapDescriptorToSetDependents.get(callee);
1292 deps = new HashSet<MethodDescriptor>();
1293 mapDescriptorToSetDependents.put(callee, deps);
1298 private NTuple<Descriptor> computePath(TempDescriptor td) {
1299 // generate proper path fot input td
1300 // if td is local variable, it just generate one element tuple path
1301 if (mapHeapPath.containsKey(td)) {
1302 return mapHeapPath.get(td);
1304 NTuple<Descriptor> path = new NTuple<Descriptor>();