/* Soot - a J*va Optimization Framework * Copyright (C) 2005 Nomair A. Naeem * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* * Maintained by: Nomair A. Naeem * Initial code taken from an existing implementation of the AbstractFlowSet in Soot */ /* * CHANGE LOG: * 16 nov, 2005: Adding <implicitTargets> feature to be able to store mappings of breaks * and continues implicitly targetting nodes * 21 Nov, 2005 * Reasoning that this implmentation is correct. Adding comments * * Refactored addIfNotDuplicate method since the same chunk of code was being used in * multiple places */ package soot.dava.toolkits.base.AST.structuredAnalysis; import soot.toolkits.scalar.*; import java.io.Serializable; import java.util.*; import soot.dava.internal.AST.*; import soot.dava.internal.SET.*; import soot.dava.internal.javaRep.*; import soot.dava.toolkits.base.AST.traversals.ClosestAbruptTargetFinder; public class DavaFlowSet extends AbstractFlowSet{ static final int DEFAULT_SIZE = 8; int numElements; int maxElements; public Object[] elements; /** * Whenever in a structured flow analysis a break or continue stmt is encountered the current DavaFlowSet * is stored in the break/continue list with the appropriate label for the target code. * This is how explicit breaks and continues are handled by the analysis framework */ HashMap<Serializable, List<DavaFlowSet>> breakList; HashMap<Serializable, List<DavaFlowSet>> continueList; /** * To handle implicit breaks and continues the following HashMaps store the DavaFlowSets as value * with the key being the targeted piece of code (an ASTNode) */ HashMap<Serializable, List<DavaFlowSet>> implicitBreaks; //map a node and all the dataflowsets due to implicit breaks targetting it HashMap<Serializable, List<DavaFlowSet>> implicitContinues; //map a node and all the dataflowsets due to implicit continues targetting it public DavaFlowSet(){ maxElements = DEFAULT_SIZE; elements = new Object[DEFAULT_SIZE]; numElements = 0; breakList = new HashMap<Serializable, List<DavaFlowSet>>(); continueList = new HashMap<Serializable, List<DavaFlowSet>>(); implicitBreaks = new HashMap<Serializable, List<DavaFlowSet>>(); implicitContinues = new HashMap<Serializable, List<DavaFlowSet>>(); } public DavaFlowSet(DavaFlowSet other){ numElements = other.numElements; maxElements = other.maxElements; elements = other.elements.clone(); breakList = (HashMap<Serializable, List<DavaFlowSet>>)other.breakList.clone(); continueList = (HashMap<Serializable, List<DavaFlowSet>>)other.continueList.clone(); implicitBreaks = (HashMap<Serializable, List<DavaFlowSet>>)other.implicitBreaks.clone(); implicitContinues = (HashMap<Serializable, List<DavaFlowSet>>)other.implicitContinues.clone(); } /** Returns true if flowSet is the same type of flow set as this. */ private boolean sameType(Object flowSet) { return (flowSet instanceof DavaFlowSet); } public DavaFlowSet clone() { return new DavaFlowSet(this); } public Object emptySet() { return new DavaFlowSet(); } public void clear() { numElements = 0; } public int size() { return numElements; } public boolean isEmpty() { return numElements == 0; } /** Returns a unbacked list of elements in this set. */ public List toList() { Object[] copiedElements = new Object[numElements]; System.arraycopy(elements, 0, copiedElements, 0, numElements); return Arrays.asList(copiedElements); } /* Expand array only when necessary, pointed out by Florian Loitsch * March 08, 2002 */ public void add(Object e){ /* Expand only if necessary! and removes one if too:) */ // Add element if(!contains(e)) { // Expand array if necessary if(numElements == maxElements) doubleCapacity(); elements[numElements++] = e; } } private void doubleCapacity() { int newSize = maxElements * 2; Object[] newElements = new Object[newSize]; System.arraycopy(elements, 0, newElements, 0, numElements); elements = newElements; maxElements = newSize; } public void remove(Object obj) { int i = 0; while (i < this.numElements) { if (elements[i].equals(obj)) { elements[i] = elements[--numElements]; return; } else i++; } } /** * Notice that the union method only merges the elements of the flow set * DavaFlowSet also contains information regarding abrupt control flow * This should also be merged using the copyInternalDataFrom method */ public void union(FlowSet otherFlow, FlowSet destFlow){ if (sameType(otherFlow) && sameType(destFlow)) { DavaFlowSet other = (DavaFlowSet) otherFlow; DavaFlowSet dest = (DavaFlowSet) destFlow; // For the special case that dest == other if(dest == other) { for(int i = 0; i < this.numElements; i++) dest.add(this.elements[i]); } // Else, force that dest starts with contents of this else { if(this != dest) copy(dest); for(int i = 0; i < other.numElements; i++) dest.add(other.elements[i]); } } else super.union(otherFlow, destFlow); } /** * Notice that the intersection method only merges the elements of the flow set * DavaFlowSet also contains information regarding abrupt control flow * This should also be merged using the copyInternalDataFrom method */ public void intersection(FlowSet otherFlow, FlowSet destFlow) { //System.out.println("DAVA FLOWSET INTERSECTION INVOKED!!!"); if (sameType(otherFlow) && sameType(destFlow)) { DavaFlowSet other = (DavaFlowSet) otherFlow; DavaFlowSet dest = (DavaFlowSet) destFlow; DavaFlowSet workingSet; if(dest == other || dest == this) workingSet = new DavaFlowSet(); else { workingSet = dest; workingSet.clear(); } for(int i = 0; i < this.numElements; i++) { if(other.contains(this.elements[i])) workingSet.add(this.elements[i]); } if(workingSet != dest) workingSet.copy(dest); } else super.intersection(otherFlow, destFlow); } public void difference(FlowSet otherFlow, FlowSet destFlow) { if (sameType(otherFlow) && sameType(destFlow)) { DavaFlowSet other = (DavaFlowSet) otherFlow; DavaFlowSet dest = (DavaFlowSet) destFlow; DavaFlowSet workingSet; if(dest == other || dest == this) workingSet = new DavaFlowSet(); else { workingSet = dest; workingSet.clear(); } for(int i = 0; i < this.numElements; i++) { if(!other.contains(this.elements[i])) workingSet.add(this.elements[i]); } if(workingSet != dest) workingSet.copy(dest); } else super.difference(otherFlow, destFlow); } public boolean contains(Object obj) { for(int i = 0; i < numElements; i++) if(elements[i].equals(obj)) return true; return false; } /** * Notice that the equals method only checks the equality of the elements of the flow set * DavaFlowSet also contains information regarding abrupt control flow * This should also be checked by invoking the internalDataMatchesTo method */ public boolean equals(Object otherFlow) { if (sameType(otherFlow)) { DavaFlowSet other = (DavaFlowSet) otherFlow; if(other.numElements != this.numElements) return false; int size = this.numElements; // Make sure that thisFlow is contained in otherFlow for(int i = 0; i < size; i++) if(!other.contains(this.elements[i])) return false; /* both arrays have the same size, no element appears twice in one * array, all elements of ThisFlow are in otherFlow -> they are * equal! we don't need to test again! // Make sure that otherFlow is contained in ThisFlow for(int i = 0; i < size; i++) if(!this.contains(other.elements[i])) return false; */ return true; } else return super.equals(otherFlow); } public void copy(FlowSet destFlow){ if (sameType(destFlow)) { DavaFlowSet dest = (DavaFlowSet) destFlow; while(dest.maxElements < this.maxElements) dest.doubleCapacity(); dest.numElements = this.numElements; System.arraycopy(this.elements, 0, dest.elements, 0, this.numElements); } else super.copy(destFlow); } /** * A private method used to add an element into a List if it is NOT a duplicate */ private List<DavaFlowSet> addIfNotDuplicate(List<DavaFlowSet> into, DavaFlowSet addThis){ //if set is not already present in the labelsBreakList then add it Iterator<DavaFlowSet> it = into.iterator(); boolean found=false; while(it.hasNext()){ DavaFlowSet temp = it.next(); if(temp.equals(addThis) && temp.internalDataMatchesTo(addThis)){ found=true; break; } } if(!found) into.add(addThis); return into; } /** * When an explicit break statement is encountered this method should be called * to store the current davaflowset */ public void addToBreakList(String labelBroken, DavaFlowSet set){ Object obj = breakList.get(labelBroken); if(obj == null){ List<DavaFlowSet> labelsBreakList = new ArrayList<DavaFlowSet>(); labelsBreakList.add(set); breakList.put(labelBroken,labelsBreakList); //System.out.println("ADDED"+labelBroken+" with"+set.toString()); } else{ List<DavaFlowSet> labelsBreakList = (List<DavaFlowSet>)obj; //add set into this list if its not a duplicate and update the hashMap breakList.put(labelBroken,addIfNotDuplicate(labelsBreakList,set)); } } /** * When an explicit continue statement is encountered this method should be called * to store the current davaflowset */ public void addToContinueList(String labelContinued, DavaFlowSet set){ Object obj = continueList.get(labelContinued); if(obj == null){ List<DavaFlowSet> labelsContinueList = new ArrayList<DavaFlowSet>(); labelsContinueList.add(set); continueList.put(labelContinued,labelsContinueList); } else{ List<DavaFlowSet> labelsContinueList = (List<DavaFlowSet>)obj; continueList.put(labelContinued,addIfNotDuplicate(labelsContinueList,set)); } } /** * Checks whether the input stmt is an implicit break/continue * A abrupt stmt is implicit if the SETLabelNode is null or the label.toString results in null */ private boolean checkImplicit(DAbruptStmt ab){ SETNodeLabel label = ab.getLabel(); if(label==null) return true; if(label.toString()==null) return true; return false; } /** * The next two methods take an abruptStmt as input along with a flowSet. * It should be only invoked for abrupt stmts which do not have explicit labels * The node being targeted by this implicit stmt should be found * Then the flow set should be added to the list within the appropriate hashmap */ public void addToImplicitBreaks(DAbruptStmt ab, DavaFlowSet set){ if(!checkImplicit(ab)) throw new RuntimeException("Tried to add explicit break statement in the implicit list in"); if(!ab.is_Break()) throw new RuntimeException("Tried to add continue statement in the break list in DavaFlowSet.addToImplicitBreaks"); //okkay so its an implicit break //get the targetted node, use the ClosestAbruptTargetFinder ASTNode node = ClosestAbruptTargetFinder.v().getTarget(ab); //get the list of flow sets already stored for this node Object list = implicitBreaks.get(node); ArrayList<DavaFlowSet> listSets = null; if(list == null){ listSets = new ArrayList<DavaFlowSet>(); } else{ //if not null listSets = (ArrayList<DavaFlowSet>)list; } //if set is not already present in listSets add it and update hashMap implicitBreaks.put(node,addIfNotDuplicate(listSets,set)); } public void addToImplicitContinues(DAbruptStmt ab, DavaFlowSet set){ if(!checkImplicit(ab)) throw new RuntimeException("Tried to add explicit continue statement in the implicit list "); if(!ab.is_Continue()) throw new RuntimeException("Tried to add break statement in the continue list"); //okkay so its an implicit continue //get the targetted node, use the ClosestAbruptTargetFinder ASTNode node = ClosestAbruptTargetFinder.v().getTarget(ab); //get the list of flow sets already stored for this node Object list = implicitContinues.get(node); ArrayList<DavaFlowSet> listSets = null; if(list == null){ listSets = new ArrayList<DavaFlowSet>(); } else{ //if not null listSets = (ArrayList<DavaFlowSet>)list; } //if set is not already present in listSets add it and update hashMap implicitContinues.put(node,addIfNotDuplicate(listSets,set)); } private HashMap<Serializable, List<DavaFlowSet>> getBreakList(){ return breakList; } private HashMap<Serializable, List<DavaFlowSet>> getContinueList(){ return continueList; } public HashMap<Serializable, List<DavaFlowSet>> getImplicitBreaks(){ return implicitBreaks; } public HashMap<Serializable, List<DavaFlowSet>> getImplicitContinues(){ return implicitContinues; } public List getImplicitlyBrokenSets(ASTNode node){ Object toReturn = implicitBreaks.get(node); if(toReturn != null) return (List)toReturn; return null; } public List getImplicitlyContinuedSets(ASTNode node){ Object toReturn = implicitContinues.get(node); if(toReturn != null) return (List)toReturn; return null; } /** * An internal method used to copy non-duplicate entries from the temp list * into the currentList */ private List<DavaFlowSet> copyDavaFlowSetList(List<DavaFlowSet> currentList, List<DavaFlowSet> temp){ Iterator<DavaFlowSet> tempIt = temp.iterator(); while(tempIt.hasNext()){ DavaFlowSet check = tempIt.next(); Iterator<DavaFlowSet> currentListIt = currentList.iterator(); boolean found=false; while(currentListIt.hasNext()){ //see if currentList has check DavaFlowSet currentSet = currentListIt.next(); if(check.equals(currentSet) && check.internalDataMatchesTo(currentSet)){ found=true; break; } } if(!found){ currentList.add(check); } } return currentList; } public void copyInternalDataFrom(Object fromThis){ if(!sameType(fromThis)) return; //copy elements of breaklist { HashMap<Serializable, List<DavaFlowSet>> fromThisBreakList = ((DavaFlowSet)fromThis).getBreakList(); Iterator<Serializable> keys = fromThisBreakList.keySet().iterator(); while(keys.hasNext()){ String labelBroken = (String)keys.next(); List<DavaFlowSet> temp = fromThisBreakList.get(labelBroken); Object list = breakList.get(labelBroken); if(list == null){ breakList.put(labelBroken,temp); } else{ List<DavaFlowSet> currentList = (List<DavaFlowSet>)list; List<DavaFlowSet> complete = copyDavaFlowSetList(currentList,temp); breakList.put(labelBroken,complete); } } } //copy elements of continuelist { HashMap<Serializable, List<DavaFlowSet>> fromThisContinueList = ((DavaFlowSet)fromThis).getContinueList(); Iterator<Serializable> keys = fromThisContinueList.keySet().iterator(); while(keys.hasNext()){ String labelContinued = (String)keys.next(); List<DavaFlowSet> temp = fromThisContinueList.get(labelContinued); Object list = continueList.get(labelContinued); if(list == null){ continueList.put(labelContinued,temp); } else{ List<DavaFlowSet> currentList = (List<DavaFlowSet>)list; List<DavaFlowSet> complete = copyDavaFlowSetList(currentList,temp); continueList.put(labelContinued,currentList); } } } //copy elements of implicitBreaks //this hashMap contains a mapping of ASTNodes to DavaFlowSets due to impicit breaks { HashMap<Serializable, List<DavaFlowSet>> copyThis = ((DavaFlowSet)fromThis).getImplicitBreaks(); Iterator<Serializable> it = copyThis.keySet().iterator(); while(it.hasNext()){ //going through all nodes in the other objects implicitBreaks hashMap //each is a node ASTNode node = (ASTNode)it.next(); //get list of dava flow sets targetting this node implicitly ArrayList<DavaFlowSet> fromDavaFlowSets = (ArrayList<DavaFlowSet>)copyThis.get(node); //Have copy non duplicates in this to the implicitbreak hashMap the current dava flow set has Object list = implicitBreaks.get(node); if(list==null){ //there was no dava flow set currently targetting this node implicitly //put the fromDavaFlowSets into the hashMap implicitBreaks.put(node,fromDavaFlowSets); } else{ ArrayList<DavaFlowSet> toDavaFlowSets = (ArrayList<DavaFlowSet>)list; List<DavaFlowSet> complete = copyDavaFlowSetList(toDavaFlowSets,fromDavaFlowSets); implicitBreaks.put(node,complete); } } } //copy elements of implicitContinues //this hashMap contains a mapping of ASTNodes to DavaFlowSets due to impicit continues { HashMap<Serializable, List<DavaFlowSet>> copyThis = ((DavaFlowSet)fromThis).getImplicitContinues(); Iterator<Serializable> it = copyThis.keySet().iterator(); while(it.hasNext()){ //going through all nodes in the other objects implicitcontinues hashMap //each is a node ASTNode node = (ASTNode)it.next(); //get list of dava flow sets targetting this node implicitly ArrayList<DavaFlowSet> fromDavaFlowSets = (ArrayList<DavaFlowSet>)copyThis.get(node); //Have copy non duplicates in this to the implicitContinue hashMap the current dava flow set has Object list = implicitContinues.get(node); if(list==null){ //there was no dava flow set currently targetting this node implicitly //put the fromDavaFlowSets into the hashMap implicitContinues.put(node,fromDavaFlowSets); } else{ ArrayList<DavaFlowSet> toDavaFlowSets = (ArrayList<DavaFlowSet>)list; List<DavaFlowSet> complete = copyDavaFlowSetList(toDavaFlowSets,fromDavaFlowSets); implicitContinues.put(node,complete); } } } } private boolean compareLists(Object One , Object Two){ if(One==null && Two == null) return true; if(One == null || Two == null) return false; List listOne = (List)One; List listTwo = (List)Two; //compare elements of the list if(listOne.size()!= listTwo.size()){ //size has to be same for lists to match return false; } Iterator listOneIt = listOne.iterator(); boolean found=false; while(listOneIt.hasNext()){ //going through the first list Object listOneObj = listOneIt.next(); Iterator listTwoIt = listTwo.iterator(); while(listTwoIt.hasNext()){ //find the object in the second list Object listTwoObj = listTwoIt.next(); if(listOneObj.equals(listTwoObj)){ //if object is found stop search found=true; break; } } if(!found){ //if didnt find object return false return false; } found=false; } return true; } public boolean internalDataMatchesTo(Object otherObj){ if(!(otherObj instanceof DavaFlowSet)) return false; DavaFlowSet other = (DavaFlowSet)otherObj; //check if same break list HashMap<Serializable, List<DavaFlowSet>> otherMap = other.getBreakList(); if( ! compareHashMaps(breakList,otherMap) ) return false; //check if same continue list otherMap = other.getContinueList(); if( ! compareHashMaps(continueList,otherMap) ) return false; //check implicitBreaks match otherMap = other.getImplicitBreaks(); if( ! compareHashMaps(implicitBreaks,otherMap) ) return false; //check implicitContinues match otherMap = other.getImplicitContinues(); if( ! compareHashMaps(implicitContinues,otherMap) ) return false; return true; } private boolean compareHashMaps(HashMap<Serializable, List<DavaFlowSet>> thisMap,HashMap<Serializable, List<DavaFlowSet>> otherMap){ List<String> otherKeyList = new ArrayList<String>(); Iterator<Serializable> keys = otherMap.keySet().iterator(); while(keys.hasNext()){ String otherKey = (String)keys.next(); otherKeyList.add(otherKey); Object listOther = otherMap.get(otherKey); Object listThis = thisMap.get(otherKey); //compare the two lists if(!compareLists(listOther,listThis)){ //if lists dont match internalData doesnt match return false; } } //have gone through otherMap //going through thisMap keys = thisMap.keySet().iterator(); while(keys.hasNext()){ String key = (String)keys.next(); Iterator<String> keyListIt = otherKeyList.iterator(); boolean alreadyDone=false; while(keyListIt.hasNext()){ String doneKey = keyListIt.next(); if(key.equals(doneKey)){ alreadyDone=true; break; } } if(!alreadyDone){ /* we have come across a label which was not done by the first hashmap meaning it was NOT in the first hashMap */ return false; } } return true; } public List getContinueSet(String label){ return continueList.remove(label); } public List getBreakSet(String label){ return breakList.remove(label); } public String toString(){ StringBuffer b = new StringBuffer(); b.append(" SET={"); for(int i = 0; i < this.numElements; i++){ if(i!=0) b.append(" , "); b.append(this.elements[i].toString()); } b.append(" }"); return b.toString(); } /* public String toString(){ StringBuffer b = new StringBuffer(); b.append("\nSETTTTT\n"); for(int i = 0; i < this.numElements; i++){ b.append("\t"+this.elements[i]+"\n"); } b.append("BREAK LIST\n"); b.append("\t"+breakList.toString()+"\n"); b.append("CONTINUE LIST\n"); b.append("\t"+continueList.toString()+"\n"); b.append("EXCEPTION LIST\n"); b.append("\t"+exceptionList.toString()+"\n"); return b.toString(); } */ }