package kr.ac.snu.selab.soot.analyzer.sta; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import kr.ac.snu.selab.soot.analyzer.Analysis; import kr.ac.snu.selab.soot.analyzer.AnalysisResult; import kr.ac.snu.selab.soot.analyzer.MethodAnalysisResult; import kr.ac.snu.selab.soot.analyzer.MyField; import kr.ac.snu.selab.soot.analyzer.MyMethod; import kr.ac.snu.selab.soot.graph.Graph; import kr.ac.snu.selab.soot.graph.GraphPathCollector; import kr.ac.snu.selab.soot.graph.MyNode; import kr.ac.snu.selab.soot.graph.Path; import kr.ac.snu.selab.soot.graph.collectors.AllPathCollector; import kr.ac.snu.selab.soot.util.MyUtil; import kr.ac.snu.selab.soot.util.XMLWriter; import org.apache.log4j.Logger; import soot.Hierarchy; import soot.SootClass; import soot.SootField; import soot.SootMethod; import soot.Unit; import soot.Value; import soot.jimple.internal.JAssignStmt; import soot.jimple.internal.JInvokeStmt; public class StatePatternAnalysis extends Analysis { private static Logger logger = Logger.getLogger(StatePatternAnalysis.class); public StatePatternAnalysis(List<SootClass> aClassList, Hierarchy aHierarchy, boolean useSimpleCallGraph) { super(aClassList, aHierarchy, useSimpleCallGraph); } // private boolean isCreatorMethodOfType(SootMethod aMethod, SootClass // aType) { // SootClass receiverType = aMethod.getDeclaringClass(); // if (aMethod.getName().equals("<init>") // && isClassOfSubType(receiverType, aType)) { // return true; // } else { // return false; // } // } public Map<String, String> getWrittenFieldInformationOfThisStatement( Unit aUnit, SootClass aType) { Map<String, String> result = new HashMap<String, String>(); if (aUnit instanceof JAssignStmt) { JAssignStmt assignStmt = (JAssignStmt) aUnit; Value rightOp = assignStmt.getRightOp(); SootClass rightOpType = null; String rightOpTypeKey = rightOp.getType().toString(); if (!(rightOpTypeKey.startsWith("null"))) { if (classMap.containsKey(rightOpTypeKey)) { rightOpType = classMap.get(rightOpTypeKey); if ((rightOpType != null) && isClassOfSubType(rightOpType, aType)) { String leftSideString = assignStmt.getLeftOp() .toString(); if (leftSideString.startsWith("this.")) { leftSideString = leftSideString.substring(5); } // Only field variable if (leftSideString.startsWith("<")) { result.put(leftSideString, rightOp.toString()); } } } } } return result; } public List<String> getWrittenFieldStringListByThisMethod( SootMethod aMethod, SootClass aType) { List<String> fieldStringList = new ArrayList<String>(); Map<String, String> assignmentMap = new HashMap<String, String>(); for (Unit aUnit : getUnits(aMethod)) { if (aUnit instanceof JAssignStmt) { assignmentMap.put(((JAssignStmt) aUnit).getLeftOp().toString(), ((JAssignStmt) aUnit).getRightOp().toString()); } } for (Unit aUnit : getUnits(aMethod)) { Map<String, String> writtenFieldStringInformation = getWrittenFieldInformationOfThisStatement( aUnit, aType); if (!writtenFieldStringInformation.isEmpty()) { String writtenValue = writtenFieldStringInformation.values() .iterator().next(); if (assignmentMap.containsKey(writtenValue)) { if (!assignmentMap.get(writtenValue).equals("null")) { fieldStringList.add(writtenFieldStringInformation .keySet().iterator().next()); } } else if (writtenValue != "null") { fieldStringList.add(writtenFieldStringInformation.keySet() .iterator().next()); } } } return fieldStringList; } public AnalysisResult analyzeOverType(SootClass aType) { AnalysisResult anAnalysisResult = new StatePatternAnalysisResult(); List<MethodAnalysisResult> methodAnalysisResultList = new ArrayList<MethodAnalysisResult>(); HashMap<String, MyNode> nodeMap = new HashMap<String, MyNode>(); for (SootClass aClass : classList) { for (SootField aField : aClass.getFields()) { // We only consider fields of the abstract type. if (aField.getType().toString().equals(aType.toString())) { nodeMap.put(aField.toString(), new MyField(aField)); } } for (SootMethod aMethod : aClass.getMethods()) { nodeMap.put(aMethod.toString(), new MyMethod(aMethod)); } } anAnalysisResult.setAbstractType(aType); for (SootClass aClass : classList) { for (SootMethod aMethod : aClass.getMethods()) { methodAnalysisResultList.add(analyzeMethodOverType(aMethod, aType, nodeMap)); } } for (MethodAnalysisResult aResult : methodAnalysisResultList) { MyNode node = aResult.getSelf(); if (node.isCaller()) { anAnalysisResult.addCaller(node); } if (node.isCreator()) { anAnalysisResult.addCreator(node); } } Graph<MyNode> referenceFlowGraph = getGraphFromMethodAnalysisResultList(methodAnalysisResultList); for (MyNode callerNode : anAnalysisResult.getCallers()) { GraphPathCollector<MyNode> pathCollector = new AllPathCollector<MyNode>( callerNode, referenceFlowGraph); List<Path<MyNode>> pathList = pathCollector.run(); List<Path<MyNode>> pathIncludeStoreList = new ArrayList<Path<MyNode>>(); for (Path<MyNode> aPath : pathList) { boolean doesPathIncludeStore = false; for (MyNode aNode : aPath.getNodeList()) { if (aNode.isStore()) { doesPathIncludeStore = true; break; } } if (doesPathIncludeStore) { pathIncludeStoreList.add(aPath); } } if (!pathIncludeStoreList.isEmpty()) { if (pathIncludeStoreList.size() > 10) { anAnalysisResult.putReferenceFlowPath(callerNode.key(), pathIncludeStoreList.subList(0, 9)); } else { anAnalysisResult.putReferenceFlowPath(callerNode.key(), pathIncludeStoreList); } } } // Identify Layers for (MyNode callerNode : anAnalysisResult.getCallers()) { String callerKey = callerNode.toString(); if (!anAnalysisResult.containsReferenceFlowPath(callerKey)) continue; Iterable<Path<MyNode>> referenceFlowPathList = anAnalysisResult .getReferenceFlowPaths(callerKey); for (Path<MyNode> aPath : referenceFlowPathList) { Set<MyNode> tempSet = new HashSet<MyNode>(); for (MyNode aNode : aPath.getNodeList()) { if (!aNode.isStore()) { tempSet.add(aNode); } else { if (!tempSet.isEmpty()) { ((StatePatternAnalysisResult) anAnalysisResult) .putReusableNodeSet(tempSet); tempSet.clear(); ((StatePatternAnalysisResult) anAnalysisResult) .putReusableNode(aNode); } } } if (!tempSet.isEmpty()) { ((StatePatternAnalysisResult) anAnalysisResult) .putExntensionNodeSet(tempSet); } } } // Check whether a call chain from caller meets an object flow graph to // the caller for (MyNode callerNode : anAnalysisResult.getCallers()) { String callerKey = callerNode.toString(); if (!anAnalysisResult.containsReferenceFlowPath(callerKey)) continue; // Only collecting injectors Set<MyNode> injectorSet = new HashSet<MyNode>(); Iterable<Path<MyNode>> referenceFlowPathList = anAnalysisResult .getReferenceFlowPaths(callerKey); for (Path<MyNode> aPath : referenceFlowPathList) { boolean isNextNodeInjector = false; for (MyNode aNode : aPath.getNodeList()) { if ((isNextNodeInjector == true) && !(((SootMethod) (aNode.getElement())).getName() .equals("<init>"))) { // aNode.setIsInjector(true); injectorSet.add(aNode); break; } else if ((isNextNodeInjector == true) && (((SootMethod) (aNode.getElement())).getName() .equals("<init>"))) { break; } else if (aNode instanceof MyField) { isNextNodeInjector = true; } } } Set<MyNode> startNodeSet = new HashSet<MyNode>(); for (Unit aUnit : ((MyMethod) callerNode).getCallStatementList()) { Set<SootMethod> invokedMethodSet = new HashSet<SootMethod>(); if (aUnit instanceof JInvokeStmt) { SootMethod invokedMethod = ((JInvokeStmt) aUnit) .getInvokeExpr().getMethod(); invokedMethodSet.add(invokedMethod); // If there is a call to abstract type class, its // subclass' methods should be added to a call // graph. if (!((JInvokeStmt) aUnit).getInvokeExpr().toString() .startsWith("specialinvoke")) { List<SootMethod> overrideMethodList = getOverrideMethodsOf(invokedMethod); for (SootMethod overrideMethod : overrideMethodList) { invokedMethodSet.add(overrideMethod); } } } else if (aUnit instanceof JAssignStmt) { if (((JAssignStmt) aUnit).containsInvokeExpr()) { SootMethod invokedMethod = ((JAssignStmt) aUnit) .getInvokeExpr().getMethod(); invokedMethodSet.add(invokedMethod); // If there is a call to abstract type class, its // subclass' methods should be added to a call // graph. if (!((JAssignStmt) aUnit).getInvokeExpr().toString() .startsWith("specialinvoke")) { List<SootMethod> overrideMethodList = getOverrideMethodsOf(invokedMethod); for (SootMethod overrideMethod : overrideMethodList) { invokedMethodSet.add(overrideMethod); } } } } if (!invokedMethodSet.isEmpty()) { for (SootMethod invokedMethod : invokedMethodSet) { startNodeSet.add(nodeMap.get(invokedMethod.toString())); } } } Set<Path<MyNode>> pathSet = new HashSet<Path<MyNode>>(); for (MyNode aStartNode : startNodeSet) { if (!injectorSet.isEmpty()) { GraphPathCollector<MyNode> pathCollector = new TriggeringPathCollector( aStartNode, callGraph, injectorSet); List<Path<MyNode>> pathList = pathCollector.run(); if (pathList.size() > 10) { pathSet.addAll(pathList.subList(0, 9)); } else { pathSet.addAll(pathList); } } } if (!pathSet.isEmpty()) { ((StatePatternAnalysisResult) anAnalysisResult) .putTriggeringPath(callerKey, pathSet); } } return anAnalysisResult; } public void writeAnalysisResultOverAllAbstractTypes(String outputDirectory) { List<SootClass> abstractTypeList = getAbstractTypeClassList(); // String graphXML = "<GraphList>"; for (SootClass aType : abstractTypeList) { StatePatternAnalysisResult anAnalysisResult = null; anAnalysisResult = (StatePatternAnalysisResult) (analyzeOverType(aType)); if (anAnalysisResult == null) continue; if (((StatePatternAnalysisResult) anAnalysisResult) .isTriggeringPathMapEmpty()) continue; logger.debug("Writing output...."); String fileName = "StatePatternAnalysis_" + anAnalysisResult.getAbstractTypeName(); String outputPath = MyUtil.getPath(outputDirectory, fileName + ".xml"); XMLWriter writer = new XMLWriter(outputPath); anAnalysisResult.writeXML(writer); writer.close(); logger.debug("Writing output finished...."); } // graphXML = graphXML + "</GraphList>"; // MyUtil.stringToFile(graphXML, // "/Users/chanwoo/Documents/workspace/StatePatternExample2/output/StatePatternExample2_ReferenceFlowGraph.xml"); } private class TriggeringPathCollector extends kr.ac.snu.selab.soot.graph.collectors.TriggeringPathCollector<MyNode> { public TriggeringPathCollector(MyNode aStartNode, Graph<MyNode> aGraph, Set<MyNode> aDestinationSet) { super(aStartNode, aGraph, aDestinationSet); } } }