From: yeom Date: Fri, 1 Jul 2011 16:42:18 +0000 (+0000) Subject: fix indentation X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=8752fede9bde1aa186fa0606f5fddaa213b170a2;p=IRC.git fix indentation --- diff --git a/Robust/src/Analysis/SSJava/DefinitelyWrittenCheck.java b/Robust/src/Analysis/SSJava/DefinitelyWrittenCheck.java index dc2881d5..ec404bd9 100644 --- a/Robust/src/Analysis/SSJava/DefinitelyWrittenCheck.java +++ b/Robust/src/Analysis/SSJava/DefinitelyWrittenCheck.java @@ -26,811 +26,777 @@ import IR.Flat.TempDescriptor; public class DefinitelyWrittenCheck { - SSJavaAnalysis ssjava; - State state; - CallGraph callGraph; - - // maps a descriptor to its known dependents: namely - // methods or tasks that call the descriptor's method - // AND are part of this analysis (reachable from main) - private Hashtable> mapDescriptorToSetDependents; - - // maps a flat node to its WrittenSet: this keeps all heap path overwritten - // previously. - private Hashtable>> mapFlatNodeToWrittenSet; - - // maps a temp descriptor to its heap path - // each temp descriptor has a unique heap path since we do not allow any - // alias. - private Hashtable> mapHeapPath; - - // maps a flat method to the READ that is the set of heap path that is - // expected to be written before method invocation - private Hashtable>> mapFlatMethodToRead; - - // maps a flat method to the OVERWRITE that is the set of heap path that is - // overwritten on every possible path during method invocation - private Hashtable>> mapFlatMethodToOverWrite; - - // points to method containing SSJAVA Loop - private MethodDescriptor methodContainingSSJavaLoop; - - // maps a flatnode to definitely written analysis mapping M - private Hashtable, Hashtable>> definitelyWrittenResults; - - private Set> calleeUnionBoundReadSet; - private Set> calleeIntersectBoundOverWriteSet; - - public DefinitelyWrittenCheck(SSJavaAnalysis ssjava, State state) { - this.state = state; - this.ssjava = ssjava; - this.callGraph = ssjava.getCallGraph(); - this.mapFlatNodeToWrittenSet = new Hashtable>>(); - this.mapDescriptorToSetDependents = new Hashtable>(); - this.mapHeapPath = new Hashtable>(); - this.mapFlatMethodToRead = new Hashtable>>(); - this.mapFlatMethodToOverWrite = new Hashtable>>(); - this.definitelyWrittenResults = new Hashtable, Hashtable>>(); - this.calleeUnionBoundReadSet = new HashSet>(); - this.calleeIntersectBoundOverWriteSet = new HashSet>(); - } - - public void definitelyWrittenCheck() { - methodReadOverWriteAnalysis(); - writtenAnalyis(); - } - - private void writtenAnalyis() { - // perform second stage analysis: intraprocedural analysis ensure that - // all - // variables are definitely written in-between the same read - - // First, identify ssjava loop entrace - FlatMethod fm = state.getMethodFlat(methodContainingSSJavaLoop); - Set flatNodesToVisit = new HashSet(); - flatNodesToVisit.add(fm); - - FlatNode entrance = null; - - while (!flatNodesToVisit.isEmpty()) { - FlatNode fn = flatNodesToVisit.iterator().next(); - flatNodesToVisit.remove(fn); - - String label = (String) state.fn2labelMap.get(fn); - if (label != null) { - - if (label.equals(ssjava.SSJAVA)) { - entrance = fn; - break; - } - } - - for (int i = 0; i < fn.numNext(); i++) { - FlatNode nn = fn.getNext(i); - flatNodesToVisit.add(nn); - } - } - - assert entrance != null; - - writtenAnalysis_analyzeLoop(entrance); - - } - - private void writtenAnalysis_analyzeLoop(FlatNode entrance) { - - Set flatNodesToVisit = new HashSet(); - flatNodesToVisit.add(entrance); - - while (!flatNodesToVisit.isEmpty()) { - FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next(); - flatNodesToVisit.remove(fn); - - Hashtable, Hashtable> prev = definitelyWrittenResults - .get(fn); - - Hashtable, Hashtable> curr = new Hashtable, Hashtable>(); - for (int i = 0; i < fn.numPrev(); i++) { - FlatNode nn = fn.getPrev(i); - Hashtable, Hashtable> dwIn = definitelyWrittenResults - .get(nn); - if (dwIn != null) { - merge(curr, dwIn); - } - } - - writtenAnalysis_nodeAction(fn, curr, entrance); - - // if a new result, schedule forward nodes for analysis - if (!curr.equals(prev)) { - definitelyWrittenResults.put(fn, curr); - - for (int i = 0; i < fn.numNext(); i++) { - FlatNode nn = fn.getNext(i); - flatNodesToVisit.add(nn); - } - } - } - } - - private void writtenAnalysis_nodeAction(FlatNode fn, - Hashtable, Hashtable> curr, - FlatNode loopEntrance) { - if (fn.equals(loopEntrance)) { - // it reaches loop entrance: changes all flag to true - Set> keySet = curr.keySet(); - for (Iterator iterator = keySet.iterator(); iterator.hasNext();) { - NTuple key = (NTuple) iterator.next(); - Hashtable pair = curr.get(key); - if (pair != null) { - Set pairKeySet = pair.keySet(); - for (Iterator iterator2 = pairKeySet.iterator(); iterator2 - .hasNext();) { - FlatNode pairKey = (FlatNode) iterator2.next(); - pair.put(pairKey, Boolean.TRUE); - } - } - } - } else { - TempDescriptor lhs; - TempDescriptor rhs; - FieldDescriptor fld; - - switch (fn.kind()) { - case FKind.FlatOpNode: { - FlatOpNode fon = (FlatOpNode) fn; - lhs = fon.getDest(); - rhs = fon.getLeft(); - - NTuple rhsHeapPath = computePath(rhs); - if (!rhs.getType().isImmutable()) { - mapHeapPath.put(lhs, rhsHeapPath); - } - - if (fon.getOp().getOp() == Operation.ASSIGN) { - // read(rhs) - Hashtable gen = curr.get(rhsHeapPath); - - if (gen == null) { - gen = new Hashtable(); - curr.put(rhsHeapPath, gen); - } - Boolean currentStatus = gen.get(fn); - if (currentStatus == null) { - gen.put(fn, Boolean.FALSE); - } else { - if (!rhs.getType().isClass()) { - checkFlag(currentStatus.booleanValue(), fn); - } - } - - } - // write(lhs) - NTuple lhsHeapPath = computePath(lhs); - removeHeapPath(curr, lhsHeapPath); - // curr.put(lhsHeapPath, new Hashtable()); - } - break; - - case FKind.FlatLiteralNode: { - FlatLiteralNode fln = (FlatLiteralNode) fn; - lhs = fln.getDst(); - - // write(lhs) - NTuple lhsHeapPath = computePath(lhs); - removeHeapPath(curr, lhsHeapPath); - - } - break; - - case FKind.FlatFieldNode: - case FKind.FlatElementNode: { - - FlatFieldNode ffn = (FlatFieldNode) fn; - lhs = ffn.getSrc(); - fld = ffn.getField(); - - // read field - NTuple srcHeapPath = mapHeapPath.get(lhs); - NTuple fldHeapPath = new NTuple( - srcHeapPath.getList()); - fldHeapPath.add(fld); - Hashtable gen = curr.get(fldHeapPath); - - if (gen == null) { - gen = new Hashtable(); - curr.put(fldHeapPath, gen); - } - - Boolean currentStatus = gen.get(fn); - if (currentStatus == null) { - gen.put(fn, Boolean.FALSE); - } else { - checkFlag(currentStatus.booleanValue(), fn); - } - - } - break; - - case FKind.FlatSetFieldNode: - case FKind.FlatSetElementNode: { - - FlatSetFieldNode fsfn = (FlatSetFieldNode) fn; - lhs = fsfn.getDst(); - fld = fsfn.getField(); - - // write(field) - NTuple lhsHeapPath = mapHeapPath.get(lhs); - NTuple fldHeapPath = new NTuple( - lhsHeapPath.getList()); - fldHeapPath.add(fld); - removeHeapPath(curr, fldHeapPath); - // curr.put(fldHeapPath, new Hashtable()); - - } - break; - - case FKind.FlatCall: { - - FlatCall fc = (FlatCall) fn; - - bindHeapPathCallerArgWithCaleeParam(fc); - - // add in which hp is an element of - // READ_bound set - // of callee: callee has 'read' requirement! - for (Iterator iterator = calleeUnionBoundReadSet.iterator(); iterator - .hasNext();) { - NTuple read = (NTuple) iterator - .next(); - - Hashtable gen = curr.get(read); - if (gen == null) { - gen = new Hashtable(); - curr.put(read, gen); - } - Boolean currentStatus = gen.get(fn); - if (currentStatus == null) { - gen.put(fn, Boolean.FALSE); - } else { - checkFlag(currentStatus.booleanValue(), fn); - } - } - - // removes if hp is an element of - // OVERWRITE_bound - // set of callee. it means that callee will overwrite it - for (Iterator iterator = calleeIntersectBoundOverWriteSet - .iterator(); iterator.hasNext();) { - NTuple write = (NTuple) iterator - .next(); - removeHeapPath(curr, write); - // curr.put(write, new Hashtable()); - } - } - break; - - } - - } - - } - - private void removeHeapPath( - Hashtable, Hashtable> curr, - NTuple hp) { - - // removes all of heap path that starts with prefix 'hp' - // since any reference overwrite along heap path gives overwriting side - // effects on the value - - Set> keySet = curr.keySet(); - for (Iterator> iter = keySet.iterator(); iter - .hasNext();) { - NTuple key = iter.next(); - if (key.startsWith(hp)) { - curr.put(key, new Hashtable()); - } - } - - } - - private void bindHeapPathCallerArgWithCaleeParam(FlatCall fc) { - // compute all possible callee set - // transform all READ/OVERWRITE set from the any possible - // callees to the - // caller - MethodDescriptor mdCallee = fc.getMethod(); - FlatMethod fmCallee = state.getMethodFlat(mdCallee); - Set setPossibleCallees = new HashSet(); - TypeDescriptor typeDesc = fc.getThis().getType(); - setPossibleCallees.addAll(callGraph.getMethods(mdCallee, typeDesc)); - - // create mapping from arg idx to its heap paths - Hashtable> mapArgIdx2CallerArgHeapPath = new Hashtable>(); - - // arg idx is starting from 'this' arg - NTuple thisHeapPath = new NTuple(); - thisHeapPath.add(fc.getThis()); - mapArgIdx2CallerArgHeapPath.put(Integer.valueOf(0), thisHeapPath); - - for (int i = 0; i < fc.numArgs(); i++) { - TempDescriptor arg = fc.getArg(i); - NTuple argHeapPath = computePath(arg); - mapArgIdx2CallerArgHeapPath - .put(Integer.valueOf(i + 1), argHeapPath); - } - - for (Iterator iterator = setPossibleCallees.iterator(); iterator - .hasNext();) { - MethodDescriptor callee = (MethodDescriptor) iterator.next(); - FlatMethod calleeFlatMethod = state.getMethodFlat(callee); - - // binding caller's args and callee's params - Set> calleeReadSet = mapFlatMethodToRead - .get(calleeFlatMethod); - if (calleeReadSet == null) { - calleeReadSet = new HashSet>(); - mapFlatMethodToRead.put(calleeFlatMethod, calleeReadSet); - } - Set> calleeOverWriteSet = mapFlatMethodToOverWrite - .get(calleeFlatMethod); - if (calleeOverWriteSet == null) { - calleeOverWriteSet = new HashSet>(); - mapFlatMethodToOverWrite.put(calleeFlatMethod, - calleeOverWriteSet); - } - - Hashtable mapParamIdx2ParamTempDesc = new Hashtable(); - for (int i = 0; i < calleeFlatMethod.numParameters(); i++) { - TempDescriptor param = calleeFlatMethod.getParameter(i); - mapParamIdx2ParamTempDesc.put(Integer.valueOf(i), param); - } - - Set> calleeBoundReadSet = bindSet(calleeReadSet, - mapParamIdx2ParamTempDesc, mapArgIdx2CallerArgHeapPath); - // union of the current read set and the current callee's - // read set - calleeUnionBoundReadSet.addAll(calleeBoundReadSet); - Set> calleeBoundWriteSet = bindSet( - calleeOverWriteSet, mapParamIdx2ParamTempDesc, - mapArgIdx2CallerArgHeapPath); - // intersection of the current overwrite set and the current - // callee's - // overwrite set - merge(calleeIntersectBoundOverWriteSet, calleeBoundWriteSet); - } - - } - - private void checkFlag(boolean booleanValue, FlatNode fn) { - if (booleanValue) { - throw new Error( - "There is a variable who comes back to the same read statement at the out-most iteration at " - + methodContainingSSJavaLoop.getClassDesc() - .getSourceFileName() - + "::" - + fn.getNumLine()); - } - } - - private void merge( - Hashtable, Hashtable> curr, - Hashtable, Hashtable> in) { - - Set> inKeySet = in.keySet(); - for (Iterator iterator = inKeySet.iterator(); iterator.hasNext();) { - NTuple inKey = (NTuple) iterator.next(); - Hashtable inPair = in.get(inKey); - - Set pairKeySet = inPair.keySet(); - for (Iterator iterator2 = pairKeySet.iterator(); iterator2 - .hasNext();) { - FlatNode pairKey = (FlatNode) iterator2.next(); - Boolean inFlag = inPair.get(pairKey); - - Hashtable currPair = curr.get(inKey); - if (currPair == null) { - currPair = new Hashtable(); - curr.put(inKey, currPair); - } - - Boolean currFlag = currPair.get(pairKey); - // by default, flag is set by false - if (currFlag == null) { - currFlag = Boolean.FALSE; - } - currFlag = Boolean.valueOf(inFlag.booleanValue() - | currFlag.booleanValue()); - currPair.put(pairKey, currFlag); - } - - } - - } - - private void methodReadOverWriteAnalysis() { - // perform method READ/OVERWRITE analysis - Set methodDescriptorsToAnalyze = new HashSet(); - methodDescriptorsToAnalyze.addAll(ssjava.getAnnotationRequireSet()); - - LinkedList sortedDescriptors = topologicalSort(methodDescriptorsToAnalyze); - - // no need to analyze method having ssjava loop - methodContainingSSJavaLoop = sortedDescriptors.removeFirst(); - - // current descriptors to visit in fixed-point interprocedural analysis, - // prioritized by - // dependency in the call graph - Stack methodDescriptorsToVisitStack = new Stack(); - - Set methodDescriptorToVistSet = new HashSet(); - methodDescriptorToVistSet.addAll(sortedDescriptors); - - while (!sortedDescriptors.isEmpty()) { - MethodDescriptor md = sortedDescriptors.removeFirst(); - methodDescriptorsToVisitStack.add(md); - } - - // analyze scheduled methods until there are no more to visit - while (!methodDescriptorsToVisitStack.isEmpty()) { - // start to analyze leaf node - MethodDescriptor md = methodDescriptorsToVisitStack.pop(); - FlatMethod fm = state.getMethodFlat(md); - - Set> readSet = new HashSet>(); - Set> overWriteSet = new HashSet>(); - - methodReadOverWrite_analyzeMethod(fm, readSet, overWriteSet); - - Set> prevRead = mapFlatMethodToRead.get(fm); - Set> prevOverWrite = mapFlatMethodToOverWrite - .get(fm); - - if (!(readSet.equals(prevRead) && overWriteSet - .equals(prevOverWrite))) { - mapFlatMethodToRead.put(fm, readSet); - mapFlatMethodToOverWrite.put(fm, overWriteSet); - - // results for callee changed, so enqueue dependents caller for - // further - // analysis - Iterator depsItr = getDependents(md) - .iterator(); - while (depsItr.hasNext()) { - MethodDescriptor methodNext = depsItr.next(); - if (!methodDescriptorsToVisitStack.contains(methodNext) - && methodDescriptorToVistSet.contains(methodNext)) { - methodDescriptorsToVisitStack.add(methodNext); - } - - } - - } - - } - - } - - private void methodReadOverWrite_analyzeMethod(FlatMethod fm, - Set> readSet, - Set> overWriteSet) { - if (state.SSJAVADEBUG) { - System.out.println("Definitely written Analyzing: " + fm); - } - - // intraprocedural analysis - Set flatNodesToVisit = new HashSet(); - flatNodesToVisit.add(fm); - - while (!flatNodesToVisit.isEmpty()) { - FlatNode fn = flatNodesToVisit.iterator().next(); - flatNodesToVisit.remove(fn); - - Set> curr = new HashSet>(); - - for (int i = 0; i < fn.numPrev(); i++) { - FlatNode prevFn = fn.getPrev(i); - Set> in = mapFlatNodeToWrittenSet - .get(prevFn); - if (in != null) { - merge(curr, in); - } - } - - methodReadOverWrite_nodeActions(fn, curr, readSet, overWriteSet); - - mapFlatNodeToWrittenSet.put(fn, curr); - - for (int i = 0; i < fn.numNext(); i++) { - FlatNode nn = fn.getNext(i); - flatNodesToVisit.add(nn); - } - - } - - } - - private void methodReadOverWrite_nodeActions(FlatNode fn, - Set> writtenSet, - Set> readSet, - Set> overWriteSet) { - TempDescriptor lhs; - TempDescriptor rhs; - FieldDescriptor fld; - - switch (fn.kind()) { - case FKind.FlatMethod: { - - // set up initial heap paths for method parameters - FlatMethod fm = (FlatMethod) fn; - for (int i = 0; i < fm.numParameters(); i++) { - TempDescriptor param = fm.getParameter(i); - NTuple heapPath = new NTuple(); - heapPath.add(param); - mapHeapPath.put(param, heapPath); - } - } - break; - - case FKind.FlatOpNode: { - FlatOpNode fon = (FlatOpNode) fn; - // for a normal assign node, need to propagate lhs's heap path to - // rhs - if (fon.getOp().getOp() == Operation.ASSIGN) { - rhs = fon.getLeft(); - lhs = fon.getDest(); - - NTuple rhsHeapPath = mapHeapPath.get(rhs); - if (rhsHeapPath != null) { - mapHeapPath.put(lhs, mapHeapPath.get(rhs)); - } - - } - } - break; - - case FKind.FlatFieldNode: - case FKind.FlatElementNode: { - - // y=x.f; - - FlatFieldNode ffn = (FlatFieldNode) fn; - lhs = ffn.getDst(); - rhs = ffn.getSrc(); - fld = ffn.getField(); - - // set up heap path - NTuple srcHeapPath = mapHeapPath.get(rhs); - NTuple readingHeapPath = new NTuple( - srcHeapPath.getList()); - readingHeapPath.add(fld); - mapHeapPath.put(lhs, readingHeapPath); - - // read (x.f) - // if WT doesnot have hp(x.f), add hp(x.f) to READ - if (!writtenSet.contains(readingHeapPath)) { - readSet.add(readingHeapPath); - } - - // need to kill hp(x.f) from WT - writtenSet.remove(readingHeapPath); - - } - break; - - case FKind.FlatSetFieldNode: - case FKind.FlatSetElementNode: { - - // x.f=y; - FlatSetFieldNode fsfn = (FlatSetFieldNode) fn; - lhs = fsfn.getDst(); - fld = fsfn.getField(); - rhs = fsfn.getSrc(); - - // set up heap path - NTuple lhsHeapPath = mapHeapPath.get(lhs); - NTuple newHeapPath = new NTuple( - lhsHeapPath.getList()); - newHeapPath.add(fld); - mapHeapPath.put(fld, newHeapPath); - - // write(x.f) - // need to add hp(y) to WT - writtenSet.add(newHeapPath); - - } - break; - - case FKind.FlatCall: { - - FlatCall fc = (FlatCall) fn; - - bindHeapPathCallerArgWithCaleeParam(fc); - - // add heap path, which is an element of READ_bound set and is not - // an - // element of WT set, to the caller's READ set - for (Iterator iterator = calleeUnionBoundReadSet.iterator(); iterator - .hasNext();) { - NTuple read = (NTuple) iterator.next(); - if (!writtenSet.contains(read)) { - readSet.add(read); - } - } - writtenSet.removeAll(calleeUnionBoundReadSet); - - // add heap path, which is an element of OVERWRITE_bound set, to the - // caller's WT set - for (Iterator iterator = calleeIntersectBoundOverWriteSet - .iterator(); iterator.hasNext();) { - NTuple write = (NTuple) iterator.next(); - writtenSet.add(write); - } - - } - break; - - case FKind.FlatExit: { - // merge the current written set with OVERWRITE set - merge(overWriteSet, writtenSet); - } - break; - - } - - } - - private void merge(Set> curr, Set> in) { - - if (curr.isEmpty()) { - // WrittenSet has a special initial value which covers all possible - // elements - // For the first time of intersection, we can take all previous set - curr.addAll(in); - } else { - // otherwise, current set is the intersection of the two sets - curr.retainAll(in); - } - - } - - // combine two heap path - private NTuple combine(NTuple callerIn, - NTuple calleeIn) { - NTuple combined = new NTuple(); - - for (int i = 0; i < callerIn.size(); i++) { - combined.add(callerIn.get(i)); - } - - // the first element of callee's heap path represents parameter - // so we skip the first one since it is already added from caller's heap - // path - for (int i = 1; i < calleeIn.size(); i++) { - combined.add(calleeIn.get(i)); - } - - return combined; - } - - private Set> bindSet(Set> calleeSet, - Hashtable mapParamIdx2ParamTempDesc, - Hashtable> mapCallerArgIdx2HeapPath) { - - Set> boundedCalleeSet = new HashSet>(); - - Set keySet = mapCallerArgIdx2HeapPath.keySet(); - for (Iterator iterator = keySet.iterator(); iterator.hasNext();) { - Integer idx = (Integer) iterator.next(); - - NTuple callerArgHeapPath = mapCallerArgIdx2HeapPath - .get(idx); - TempDescriptor calleeParam = mapParamIdx2ParamTempDesc.get(idx); - - for (Iterator iterator2 = calleeSet.iterator(); iterator2.hasNext();) { - NTuple element = (NTuple) iterator2 - .next(); - if (element.startsWith(calleeParam)) { - NTuple boundElement = combine( - callerArgHeapPath, element); - boundedCalleeSet.add(boundElement); - } - - } - - } - return boundedCalleeSet; - - } - - // Borrowed it from disjoint analysis - private LinkedList topologicalSort( - Set toSort) { - - Set discovered = new HashSet(); - - LinkedList sorted = new LinkedList(); - - Iterator itr = toSort.iterator(); - while (itr.hasNext()) { - MethodDescriptor d = itr.next(); - - if (!discovered.contains(d)) { - dfsVisit(d, toSort, sorted, discovered); - } - } - - return sorted; - } - - // While we're doing DFS on call graph, remember - // dependencies for efficient queuing of methods - // during interprocedural analysis: - // - // a dependent of a method decriptor d for this analysis is: - // 1) a method or task that invokes d - // 2) in the descriptorsToAnalyze set - private void dfsVisit(MethodDescriptor md, Set toSort, - LinkedList sorted, - Set discovered) { - - discovered.add(md); - - // otherwise call graph guides DFS - Iterator itr = callGraph.getCallerSet(md).iterator(); - while (itr.hasNext()) { - MethodDescriptor dCaller = (MethodDescriptor) itr.next(); - - // only consider callers in the original set to analyze - if (!toSort.contains(dCaller)) { - continue; - } - - if (!discovered.contains(dCaller)) { - addDependent(md, // callee - dCaller // caller - ); - - dfsVisit(dCaller, toSort, sorted, discovered); - } - } - - // for leaf-nodes last now! - sorted.addLast(md); - } - - // a dependent of a method decriptor d for this analysis is: - // 1) a method or task that invokes d - // 2) in the descriptorsToAnalyze set - private void addDependent(MethodDescriptor callee, MethodDescriptor caller) { - Set deps = mapDescriptorToSetDependents.get(callee); - if (deps == null) { - deps = new HashSet(); - } - deps.add(caller); - mapDescriptorToSetDependents.put(callee, deps); - } - - private Set getDependents(MethodDescriptor callee) { - Set deps = mapDescriptorToSetDependents.get(callee); - if (deps == null) { - deps = new HashSet(); - mapDescriptorToSetDependents.put(callee, deps); - } - return deps; - } - - private NTuple computePath(TempDescriptor td) { - // generate proper path fot input td - // if td is local variable, it just generate one element tuple path - if (mapHeapPath.containsKey(td)) { - return mapHeapPath.get(td); - } else { - NTuple path = new NTuple(); - path.add(td); - return path; - } - } + SSJavaAnalysis ssjava; + State state; + CallGraph callGraph; + + // maps a descriptor to its known dependents: namely + // methods or tasks that call the descriptor's method + // AND are part of this analysis (reachable from main) + private Hashtable> mapDescriptorToSetDependents; + + // maps a flat node to its WrittenSet: this keeps all heap path overwritten + // previously. + private Hashtable>> mapFlatNodeToWrittenSet; + + // maps a temp descriptor to its heap path + // each temp descriptor has a unique heap path since we do not allow any + // alias. + private Hashtable> mapHeapPath; + + // maps a flat method to the READ that is the set of heap path that is + // expected to be written before method invocation + private Hashtable>> mapFlatMethodToRead; + + // maps a flat method to the OVERWRITE that is the set of heap path that is + // overwritten on every possible path during method invocation + private Hashtable>> mapFlatMethodToOverWrite; + + // points to method containing SSJAVA Loop + private MethodDescriptor methodContainingSSJavaLoop; + + // maps a flatnode to definitely written analysis mapping M + private Hashtable, Hashtable>> definitelyWrittenResults; + + private Set> calleeUnionBoundReadSet; + private Set> calleeIntersectBoundOverWriteSet; + + public DefinitelyWrittenCheck(SSJavaAnalysis ssjava, State state) { + this.state = state; + this.ssjava = ssjava; + this.callGraph = ssjava.getCallGraph(); + this.mapFlatNodeToWrittenSet = new Hashtable>>(); + this.mapDescriptorToSetDependents = new Hashtable>(); + this.mapHeapPath = new Hashtable>(); + this.mapFlatMethodToRead = new Hashtable>>(); + this.mapFlatMethodToOverWrite = new Hashtable>>(); + this.definitelyWrittenResults = + new Hashtable, Hashtable>>(); + this.calleeUnionBoundReadSet = new HashSet>(); + this.calleeIntersectBoundOverWriteSet = new HashSet>(); + } + + public void definitelyWrittenCheck() { + methodReadOverWriteAnalysis(); + writtenAnalyis(); + } + + private void writtenAnalyis() { + // perform second stage analysis: intraprocedural analysis ensure that + // all + // variables are definitely written in-between the same read + + // First, identify ssjava loop entrace + FlatMethod fm = state.getMethodFlat(methodContainingSSJavaLoop); + Set flatNodesToVisit = new HashSet(); + flatNodesToVisit.add(fm); + + FlatNode entrance = null; + + while (!flatNodesToVisit.isEmpty()) { + FlatNode fn = flatNodesToVisit.iterator().next(); + flatNodesToVisit.remove(fn); + + String label = (String) state.fn2labelMap.get(fn); + if (label != null) { + + if (label.equals(ssjava.SSJAVA)) { + entrance = fn; + break; + } + } + + for (int i = 0; i < fn.numNext(); i++) { + FlatNode nn = fn.getNext(i); + flatNodesToVisit.add(nn); + } + } + + assert entrance != null; + + writtenAnalysis_analyzeLoop(entrance); + + } + + private void writtenAnalysis_analyzeLoop(FlatNode entrance) { + + Set flatNodesToVisit = new HashSet(); + flatNodesToVisit.add(entrance); + + while (!flatNodesToVisit.isEmpty()) { + FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next(); + flatNodesToVisit.remove(fn); + + Hashtable, Hashtable> prev = + definitelyWrittenResults.get(fn); + + Hashtable, Hashtable> curr = + new Hashtable, Hashtable>(); + for (int i = 0; i < fn.numPrev(); i++) { + FlatNode nn = fn.getPrev(i); + Hashtable, Hashtable> dwIn = + definitelyWrittenResults.get(nn); + if (dwIn != null) { + merge(curr, dwIn); + } + } + + writtenAnalysis_nodeAction(fn, curr, entrance); + + // if a new result, schedule forward nodes for analysis + if (!curr.equals(prev)) { + definitelyWrittenResults.put(fn, curr); + + for (int i = 0; i < fn.numNext(); i++) { + FlatNode nn = fn.getNext(i); + flatNodesToVisit.add(nn); + } + } + } + } + + private void writtenAnalysis_nodeAction(FlatNode fn, + Hashtable, Hashtable> curr, FlatNode loopEntrance) { + if (fn.equals(loopEntrance)) { + // it reaches loop entrance: changes all flag to true + Set> keySet = curr.keySet(); + for (Iterator iterator = keySet.iterator(); iterator.hasNext();) { + NTuple key = (NTuple) iterator.next(); + Hashtable pair = curr.get(key); + if (pair != null) { + Set pairKeySet = pair.keySet(); + for (Iterator iterator2 = pairKeySet.iterator(); iterator2.hasNext();) { + FlatNode pairKey = (FlatNode) iterator2.next(); + pair.put(pairKey, Boolean.TRUE); + } + } + } + } else { + TempDescriptor lhs; + TempDescriptor rhs; + FieldDescriptor fld; + + switch (fn.kind()) { + case FKind.FlatOpNode: { + FlatOpNode fon = (FlatOpNode) fn; + lhs = fon.getDest(); + rhs = fon.getLeft(); + + NTuple rhsHeapPath = computePath(rhs); + if (!rhs.getType().isImmutable()) { + mapHeapPath.put(lhs, rhsHeapPath); + } + + if (fon.getOp().getOp() == Operation.ASSIGN) { + // read(rhs) + Hashtable gen = curr.get(rhsHeapPath); + + if (gen == null) { + gen = new Hashtable(); + curr.put(rhsHeapPath, gen); + } + Boolean currentStatus = gen.get(fn); + if (currentStatus == null) { + gen.put(fn, Boolean.FALSE); + } else { + if (!rhs.getType().isClass()) { + checkFlag(currentStatus.booleanValue(), fn); + } + } + + } + // write(lhs) + NTuple lhsHeapPath = computePath(lhs); + removeHeapPath(curr, lhsHeapPath); + // curr.put(lhsHeapPath, new Hashtable()); + } + break; + + case FKind.FlatLiteralNode: { + FlatLiteralNode fln = (FlatLiteralNode) fn; + lhs = fln.getDst(); + + // write(lhs) + NTuple lhsHeapPath = computePath(lhs); + removeHeapPath(curr, lhsHeapPath); + + } + break; + + case FKind.FlatFieldNode: + case FKind.FlatElementNode: { + + FlatFieldNode ffn = (FlatFieldNode) fn; + lhs = ffn.getSrc(); + fld = ffn.getField(); + + // read field + NTuple srcHeapPath = mapHeapPath.get(lhs); + NTuple fldHeapPath = new NTuple(srcHeapPath.getList()); + fldHeapPath.add(fld); + Hashtable gen = curr.get(fldHeapPath); + + if (gen == null) { + gen = new Hashtable(); + curr.put(fldHeapPath, gen); + } + + Boolean currentStatus = gen.get(fn); + if (currentStatus == null) { + gen.put(fn, Boolean.FALSE); + } else { + checkFlag(currentStatus.booleanValue(), fn); + } + + } + break; + + case FKind.FlatSetFieldNode: + case FKind.FlatSetElementNode: { + + FlatSetFieldNode fsfn = (FlatSetFieldNode) fn; + lhs = fsfn.getDst(); + fld = fsfn.getField(); + + // write(field) + NTuple lhsHeapPath = mapHeapPath.get(lhs); + NTuple fldHeapPath = new NTuple(lhsHeapPath.getList()); + fldHeapPath.add(fld); + removeHeapPath(curr, fldHeapPath); + // curr.put(fldHeapPath, new Hashtable()); + + } + break; + + case FKind.FlatCall: { + + FlatCall fc = (FlatCall) fn; + + bindHeapPathCallerArgWithCaleeParam(fc); + + // add in which hp is an element of + // READ_bound set + // of callee: callee has 'read' requirement! + for (Iterator iterator = calleeUnionBoundReadSet.iterator(); iterator.hasNext();) { + NTuple read = (NTuple) iterator.next(); + + Hashtable gen = curr.get(read); + if (gen == null) { + gen = new Hashtable(); + curr.put(read, gen); + } + Boolean currentStatus = gen.get(fn); + if (currentStatus == null) { + gen.put(fn, Boolean.FALSE); + } else { + checkFlag(currentStatus.booleanValue(), fn); + } + } + + // removes if hp is an element of + // OVERWRITE_bound + // set of callee. it means that callee will overwrite it + for (Iterator iterator = calleeIntersectBoundOverWriteSet.iterator(); iterator.hasNext();) { + NTuple write = (NTuple) iterator.next(); + removeHeapPath(curr, write); + // curr.put(write, new Hashtable()); + } + } + break; + + } + + } + + } + + private void removeHeapPath(Hashtable, Hashtable> curr, + NTuple hp) { + + // removes all of heap path that starts with prefix 'hp' + // since any reference overwrite along heap path gives overwriting side + // effects on the value + + Set> keySet = curr.keySet(); + for (Iterator> iter = keySet.iterator(); iter.hasNext();) { + NTuple key = iter.next(); + if (key.startsWith(hp)) { + curr.put(key, new Hashtable()); + } + } + + } + + private void bindHeapPathCallerArgWithCaleeParam(FlatCall fc) { + // compute all possible callee set + // transform all READ/OVERWRITE set from the any possible + // callees to the + // caller + MethodDescriptor mdCallee = fc.getMethod(); + FlatMethod fmCallee = state.getMethodFlat(mdCallee); + Set setPossibleCallees = new HashSet(); + TypeDescriptor typeDesc = fc.getThis().getType(); + setPossibleCallees.addAll(callGraph.getMethods(mdCallee, typeDesc)); + + // create mapping from arg idx to its heap paths + Hashtable> mapArgIdx2CallerArgHeapPath = + new Hashtable>(); + + // arg idx is starting from 'this' arg + NTuple thisHeapPath = new NTuple(); + thisHeapPath.add(fc.getThis()); + mapArgIdx2CallerArgHeapPath.put(Integer.valueOf(0), thisHeapPath); + + for (int i = 0; i < fc.numArgs(); i++) { + TempDescriptor arg = fc.getArg(i); + NTuple argHeapPath = computePath(arg); + mapArgIdx2CallerArgHeapPath.put(Integer.valueOf(i + 1), argHeapPath); + } + + for (Iterator iterator = setPossibleCallees.iterator(); iterator.hasNext();) { + MethodDescriptor callee = (MethodDescriptor) iterator.next(); + FlatMethod calleeFlatMethod = state.getMethodFlat(callee); + + // binding caller's args and callee's params + Set> calleeReadSet = mapFlatMethodToRead.get(calleeFlatMethod); + if (calleeReadSet == null) { + calleeReadSet = new HashSet>(); + mapFlatMethodToRead.put(calleeFlatMethod, calleeReadSet); + } + Set> calleeOverWriteSet = mapFlatMethodToOverWrite.get(calleeFlatMethod); + if (calleeOverWriteSet == null) { + calleeOverWriteSet = new HashSet>(); + mapFlatMethodToOverWrite.put(calleeFlatMethod, calleeOverWriteSet); + } + + Hashtable mapParamIdx2ParamTempDesc = + new Hashtable(); + for (int i = 0; i < calleeFlatMethod.numParameters(); i++) { + TempDescriptor param = calleeFlatMethod.getParameter(i); + mapParamIdx2ParamTempDesc.put(Integer.valueOf(i), param); + } + + Set> calleeBoundReadSet = + bindSet(calleeReadSet, mapParamIdx2ParamTempDesc, mapArgIdx2CallerArgHeapPath); + // union of the current read set and the current callee's + // read set + calleeUnionBoundReadSet.addAll(calleeBoundReadSet); + Set> calleeBoundWriteSet = + bindSet(calleeOverWriteSet, mapParamIdx2ParamTempDesc, mapArgIdx2CallerArgHeapPath); + // intersection of the current overwrite set and the current + // callee's + // overwrite set + merge(calleeIntersectBoundOverWriteSet, calleeBoundWriteSet); + } + + } + + private void checkFlag(boolean booleanValue, FlatNode fn) { + if (booleanValue) { + throw new Error( + "There is a variable who comes back to the same read statement at the out-most iteration at " + + methodContainingSSJavaLoop.getClassDesc().getSourceFileName() + "::" + + fn.getNumLine()); + } + } + + private void merge(Hashtable, Hashtable> curr, + Hashtable, Hashtable> in) { + + Set> inKeySet = in.keySet(); + for (Iterator iterator = inKeySet.iterator(); iterator.hasNext();) { + NTuple inKey = (NTuple) iterator.next(); + Hashtable inPair = in.get(inKey); + + Set pairKeySet = inPair.keySet(); + for (Iterator iterator2 = pairKeySet.iterator(); iterator2.hasNext();) { + FlatNode pairKey = (FlatNode) iterator2.next(); + Boolean inFlag = inPair.get(pairKey); + + Hashtable currPair = curr.get(inKey); + if (currPair == null) { + currPair = new Hashtable(); + curr.put(inKey, currPair); + } + + Boolean currFlag = currPair.get(pairKey); + // by default, flag is set by false + if (currFlag == null) { + currFlag = Boolean.FALSE; + } + currFlag = Boolean.valueOf(inFlag.booleanValue() | currFlag.booleanValue()); + currPair.put(pairKey, currFlag); + } + + } + + } + + private void methodReadOverWriteAnalysis() { + // perform method READ/OVERWRITE analysis + Set methodDescriptorsToAnalyze = new HashSet(); + methodDescriptorsToAnalyze.addAll(ssjava.getAnnotationRequireSet()); + + LinkedList sortedDescriptors = topologicalSort(methodDescriptorsToAnalyze); + + // no need to analyze method having ssjava loop + methodContainingSSJavaLoop = sortedDescriptors.removeFirst(); + + // current descriptors to visit in fixed-point interprocedural analysis, + // prioritized by + // dependency in the call graph + Stack methodDescriptorsToVisitStack = new Stack(); + + Set methodDescriptorToVistSet = new HashSet(); + methodDescriptorToVistSet.addAll(sortedDescriptors); + + while (!sortedDescriptors.isEmpty()) { + MethodDescriptor md = sortedDescriptors.removeFirst(); + methodDescriptorsToVisitStack.add(md); + } + + // analyze scheduled methods until there are no more to visit + while (!methodDescriptorsToVisitStack.isEmpty()) { + // start to analyze leaf node + MethodDescriptor md = methodDescriptorsToVisitStack.pop(); + FlatMethod fm = state.getMethodFlat(md); + + Set> readSet = new HashSet>(); + Set> overWriteSet = new HashSet>(); + + methodReadOverWrite_analyzeMethod(fm, readSet, overWriteSet); + + Set> prevRead = mapFlatMethodToRead.get(fm); + Set> prevOverWrite = mapFlatMethodToOverWrite.get(fm); + + if (!(readSet.equals(prevRead) && overWriteSet.equals(prevOverWrite))) { + mapFlatMethodToRead.put(fm, readSet); + mapFlatMethodToOverWrite.put(fm, overWriteSet); + + // results for callee changed, so enqueue dependents caller for + // further + // analysis + Iterator depsItr = getDependents(md).iterator(); + while (depsItr.hasNext()) { + MethodDescriptor methodNext = depsItr.next(); + if (!methodDescriptorsToVisitStack.contains(methodNext) + && methodDescriptorToVistSet.contains(methodNext)) { + methodDescriptorsToVisitStack.add(methodNext); + } + + } + + } + + } + + } + + private void methodReadOverWrite_analyzeMethod(FlatMethod fm, Set> readSet, + Set> overWriteSet) { + if (state.SSJAVADEBUG) { + System.out.println("Definitely written Analyzing: " + fm); + } + + // intraprocedural analysis + Set flatNodesToVisit = new HashSet(); + flatNodesToVisit.add(fm); + + while (!flatNodesToVisit.isEmpty()) { + FlatNode fn = flatNodesToVisit.iterator().next(); + flatNodesToVisit.remove(fn); + + Set> curr = new HashSet>(); + + for (int i = 0; i < fn.numPrev(); i++) { + FlatNode prevFn = fn.getPrev(i); + Set> in = mapFlatNodeToWrittenSet.get(prevFn); + if (in != null) { + merge(curr, in); + } + } + + methodReadOverWrite_nodeActions(fn, curr, readSet, overWriteSet); + + mapFlatNodeToWrittenSet.put(fn, curr); + + for (int i = 0; i < fn.numNext(); i++) { + FlatNode nn = fn.getNext(i); + flatNodesToVisit.add(nn); + } + + } + + } + + private void methodReadOverWrite_nodeActions(FlatNode fn, Set> writtenSet, + Set> readSet, Set> overWriteSet) { + TempDescriptor lhs; + TempDescriptor rhs; + FieldDescriptor fld; + + switch (fn.kind()) { + case FKind.FlatMethod: { + + // set up initial heap paths for method parameters + FlatMethod fm = (FlatMethod) fn; + for (int i = 0; i < fm.numParameters(); i++) { + TempDescriptor param = fm.getParameter(i); + NTuple heapPath = new NTuple(); + heapPath.add(param); + mapHeapPath.put(param, heapPath); + } + } + break; + + case FKind.FlatOpNode: { + FlatOpNode fon = (FlatOpNode) fn; + // for a normal assign node, need to propagate lhs's heap path to + // rhs + if (fon.getOp().getOp() == Operation.ASSIGN) { + rhs = fon.getLeft(); + lhs = fon.getDest(); + + NTuple rhsHeapPath = mapHeapPath.get(rhs); + if (rhsHeapPath != null) { + mapHeapPath.put(lhs, mapHeapPath.get(rhs)); + } + + } + } + break; + + case FKind.FlatFieldNode: + case FKind.FlatElementNode: { + + // y=x.f; + + FlatFieldNode ffn = (FlatFieldNode) fn; + lhs = ffn.getDst(); + rhs = ffn.getSrc(); + fld = ffn.getField(); + + // set up heap path + NTuple srcHeapPath = mapHeapPath.get(rhs); + NTuple readingHeapPath = new NTuple(srcHeapPath.getList()); + readingHeapPath.add(fld); + mapHeapPath.put(lhs, readingHeapPath); + + // read (x.f) + // if WT doesnot have hp(x.f), add hp(x.f) to READ + if (!writtenSet.contains(readingHeapPath)) { + readSet.add(readingHeapPath); + } + + // need to kill hp(x.f) from WT + writtenSet.remove(readingHeapPath); + + } + break; + + case FKind.FlatSetFieldNode: + case FKind.FlatSetElementNode: { + + // x.f=y; + FlatSetFieldNode fsfn = (FlatSetFieldNode) fn; + lhs = fsfn.getDst(); + fld = fsfn.getField(); + rhs = fsfn.getSrc(); + + // set up heap path + NTuple lhsHeapPath = mapHeapPath.get(lhs); + NTuple newHeapPath = new NTuple(lhsHeapPath.getList()); + newHeapPath.add(fld); + mapHeapPath.put(fld, newHeapPath); + + // write(x.f) + // need to add hp(y) to WT + writtenSet.add(newHeapPath); + + } + break; + + case FKind.FlatCall: { + + FlatCall fc = (FlatCall) fn; + + bindHeapPathCallerArgWithCaleeParam(fc); + + // add heap path, which is an element of READ_bound set and is not + // an + // element of WT set, to the caller's READ set + for (Iterator iterator = calleeUnionBoundReadSet.iterator(); iterator.hasNext();) { + NTuple read = (NTuple) iterator.next(); + if (!writtenSet.contains(read)) { + readSet.add(read); + } + } + writtenSet.removeAll(calleeUnionBoundReadSet); + + // add heap path, which is an element of OVERWRITE_bound set, to the + // caller's WT set + for (Iterator iterator = calleeIntersectBoundOverWriteSet.iterator(); iterator.hasNext();) { + NTuple write = (NTuple) iterator.next(); + writtenSet.add(write); + } + + } + break; + + case FKind.FlatExit: { + // merge the current written set with OVERWRITE set + merge(overWriteSet, writtenSet); + } + break; + + } + + } + + private void merge(Set> curr, Set> in) { + + if (curr.isEmpty()) { + // WrittenSet has a special initial value which covers all possible + // elements + // For the first time of intersection, we can take all previous set + curr.addAll(in); + } else { + // otherwise, current set is the intersection of the two sets + curr.retainAll(in); + } + + } + + // combine two heap path + private NTuple combine(NTuple callerIn, NTuple calleeIn) { + NTuple combined = new NTuple(); + + for (int i = 0; i < callerIn.size(); i++) { + combined.add(callerIn.get(i)); + } + + // the first element of callee's heap path represents parameter + // so we skip the first one since it is already added from caller's heap + // path + for (int i = 1; i < calleeIn.size(); i++) { + combined.add(calleeIn.get(i)); + } + + return combined; + } + + private Set> bindSet(Set> calleeSet, + Hashtable mapParamIdx2ParamTempDesc, + Hashtable> mapCallerArgIdx2HeapPath) { + + Set> boundedCalleeSet = new HashSet>(); + + Set keySet = mapCallerArgIdx2HeapPath.keySet(); + for (Iterator iterator = keySet.iterator(); iterator.hasNext();) { + Integer idx = (Integer) iterator.next(); + + NTuple callerArgHeapPath = mapCallerArgIdx2HeapPath.get(idx); + TempDescriptor calleeParam = mapParamIdx2ParamTempDesc.get(idx); + + for (Iterator iterator2 = calleeSet.iterator(); iterator2.hasNext();) { + NTuple element = (NTuple) iterator2.next(); + if (element.startsWith(calleeParam)) { + NTuple boundElement = combine(callerArgHeapPath, element); + boundedCalleeSet.add(boundElement); + } + + } + + } + return boundedCalleeSet; + + } + + // Borrowed it from disjoint analysis + private LinkedList topologicalSort(Set toSort) { + + Set discovered = new HashSet(); + + LinkedList sorted = new LinkedList(); + + Iterator itr = toSort.iterator(); + while (itr.hasNext()) { + MethodDescriptor d = itr.next(); + + if (!discovered.contains(d)) { + dfsVisit(d, toSort, sorted, discovered); + } + } + + return sorted; + } + + // While we're doing DFS on call graph, remember + // dependencies for efficient queuing of methods + // during interprocedural analysis: + // + // a dependent of a method decriptor d for this analysis is: + // 1) a method or task that invokes d + // 2) in the descriptorsToAnalyze set + private void dfsVisit(MethodDescriptor md, Set toSort, + LinkedList sorted, Set discovered) { + + discovered.add(md); + + // otherwise call graph guides DFS + Iterator itr = callGraph.getCallerSet(md).iterator(); + while (itr.hasNext()) { + MethodDescriptor dCaller = (MethodDescriptor) itr.next(); + + // only consider callers in the original set to analyze + if (!toSort.contains(dCaller)) { + continue; + } + + if (!discovered.contains(dCaller)) { + addDependent(md, // callee + dCaller // caller + ); + + dfsVisit(dCaller, toSort, sorted, discovered); + } + } + + // for leaf-nodes last now! + sorted.addLast(md); + } + + // a dependent of a method decriptor d for this analysis is: + // 1) a method or task that invokes d + // 2) in the descriptorsToAnalyze set + private void addDependent(MethodDescriptor callee, MethodDescriptor caller) { + Set deps = mapDescriptorToSetDependents.get(callee); + if (deps == null) { + deps = new HashSet(); + } + deps.add(caller); + mapDescriptorToSetDependents.put(callee, deps); + } + + private Set getDependents(MethodDescriptor callee) { + Set deps = mapDescriptorToSetDependents.get(callee); + if (deps == null) { + deps = new HashSet(); + mapDescriptorToSetDependents.put(callee, deps); + } + return deps; + } + + private NTuple computePath(TempDescriptor td) { + // generate proper path fot input td + // if td is local variable, it just generate one element tuple path + if (mapHeapPath.containsKey(td)) { + return mapHeapPath.get(td); + } else { + NTuple path = new NTuple(); + path.add(td); + return path; + } + } } \ No newline at end of file