/* Soot - a J*va Optimization Framework * Copyright (C) 2007 Manu Sridharan * * 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. */ package soot.jimple.spark.ondemand.pautil; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import soot.G; import soot.SootMethod; import soot.jimple.InvokeExpr; import soot.jimple.spark.ondemand.genericutil.ArraySet; import soot.jimple.spark.ondemand.genericutil.ArraySetMultiMap; import soot.jimple.spark.pag.GlobalVarNode; import soot.jimple.spark.pag.LocalVarNode; import soot.jimple.spark.pag.Node; import soot.jimple.spark.pag.PAG; import soot.jimple.spark.pag.VarNode; import soot.toolkits.scalar.Pair; import soot.util.HashMultiMap; /** * Information for a context-sensitive analysis, eg. for call sites * * @author manu */ public class ContextSensitiveInfo { private static final boolean SKIP_STRING_NODES = false; private static final boolean SKIP_EXCEPTION_NODES = false; private static final boolean SKIP_THREAD_GLOBALS = false; private static final boolean PRINT_CALL_SITE_INFO = false; /** * assignment edges, but properly handling multiple calls to a method * VarNode -> ArraySet[AssignEdge] */ private final ArraySetMultiMap<VarNode, AssignEdge> contextSensitiveAssignEdges = new ArraySetMultiMap<VarNode, AssignEdge>(); private final ArraySetMultiMap<VarNode, AssignEdge> contextSensitiveAssignBarEdges = new ArraySetMultiMap<VarNode, AssignEdge>(); /** * nodes in each method */ private final ArraySetMultiMap<SootMethod, VarNode> methodToNodes = new ArraySetMultiMap<SootMethod, VarNode>(); private final ArraySetMultiMap<SootMethod, VarNode> methodToOutPorts = new ArraySetMultiMap<SootMethod, VarNode>(); private final ArraySetMultiMap<SootMethod, VarNode> methodToInPorts = new ArraySetMultiMap<SootMethod, VarNode>(); private final ArraySetMultiMap<SootMethod, Integer> callSitesInMethod = new ArraySetMultiMap<SootMethod, Integer>(); private final ArraySetMultiMap<SootMethod, Integer> callSitesInvokingMethod = new ArraySetMultiMap<SootMethod, Integer>(); private final ArraySetMultiMap<Integer, SootMethod> callSiteToTargets = new ArraySetMultiMap<Integer, SootMethod>(); private final ArraySetMultiMap<Integer, AssignEdge> callSiteToEdges = new ArraySetMultiMap<Integer, AssignEdge>(); private final Map<Integer, LocalVarNode> virtCallSiteToReceiver = new HashMap<Integer, LocalVarNode>(); private final Map<Integer, SootMethod> callSiteToInvokedMethod = new HashMap<Integer, SootMethod>(); private final Map<Integer, SootMethod> callSiteToInvokingMethod = new HashMap<Integer, SootMethod>(); private final ArraySetMultiMap<LocalVarNode, Integer> receiverToVirtCallSites = new ArraySetMultiMap<LocalVarNode, Integer>(); /** * */ public ContextSensitiveInfo(PAG pag) { // set up method to node map for (Iterator iter = pag.getVarNodeNumberer().iterator(); iter .hasNext();) { VarNode varNode = (VarNode) iter.next(); if (varNode instanceof LocalVarNode) { LocalVarNode local = (LocalVarNode) varNode; SootMethod method = local.getMethod(); assert method != null : local; methodToNodes.put(method, local); if (SootUtil.isRetNode(local)) { methodToOutPorts.put(method, local); } if (SootUtil.isParamNode(local)) { methodToInPorts.put(method, local); } } } int callSiteNum = 0; // first, add regular assigns Set assignSources = pag.simpleSources(); for (Iterator iter = assignSources.iterator(); iter.hasNext();) { VarNode assignSource = (VarNode) iter.next(); if (skipNode(assignSource)) { continue; } boolean sourceGlobal = assignSource instanceof GlobalVarNode; Node[] assignTargets = pag.simpleLookup(assignSource); for (int i = 0; i < assignTargets.length; i++) { VarNode assignTarget = (VarNode) assignTargets[i]; if (skipNode(assignTarget)) continue; boolean isFinalizerNode = false; if (assignTarget instanceof LocalVarNode) { LocalVarNode local = (LocalVarNode) assignTarget; SootMethod method = local.getMethod(); if (method.toString().indexOf("finalize()") != -1 && SootUtil.isThisNode(local)) { isFinalizerNode = true; } } boolean targetGlobal = assignTarget instanceof GlobalVarNode; AssignEdge assignEdge = new AssignEdge(assignSource, assignTarget); // handle weird finalizers if (isFinalizerNode) { assignEdge.setParamEdge(); Integer callSite = new Integer(callSiteNum++); assignEdge.setCallSite(callSite); } addAssignEdge(assignEdge); if (sourceGlobal) { if (targetGlobal) { // System.err.println("G2G " + assignSource + " --> " // + assignTarget); } else { SootMethod method = ((LocalVarNode) assignTarget) .getMethod(); // don't want to include things assigned something that // is already an in port if (!methodToInPorts.get(method).contains(assignTarget)) { methodToInPorts.put(method, assignSource); } } } else { if (targetGlobal) { SootMethod method = ((LocalVarNode) assignSource) .getMethod(); // don't want to include things assigned from something // that // is already an out port if (!methodToOutPorts.get(method) .contains(assignSource)) { methodToOutPorts.put(method, assignTarget); } } } } } // now handle calls HashMultiMap callAssigns = pag.callAssigns; PrintWriter callSiteWriter = null; if (PRINT_CALL_SITE_INFO) { try { callSiteWriter = new PrintWriter( new FileWriter("callSiteInfo"), true); } catch (IOException e) { e.printStackTrace(); } } for (Iterator iter = callAssigns.keySet().iterator(); iter.hasNext();) { InvokeExpr ie = (InvokeExpr) iter.next(); Integer callSite = new Integer(callSiteNum++); callSiteToInvokedMethod.put(callSite, ie.getMethod()); SootMethod invokingMethod = pag.callToMethod.get(ie); callSiteToInvokingMethod.put(callSite, invokingMethod); if (PRINT_CALL_SITE_INFO) { callSiteWriter.println(callSite + " " + callSiteToInvokingMethod.get(callSite) + " " + ie); } if (pag.virtualCallsToReceivers.containsKey(ie)) { LocalVarNode receiver = (LocalVarNode) pag.virtualCallsToReceivers .get(ie); assert receiver != null; virtCallSiteToReceiver.put(callSite, receiver); receiverToVirtCallSites.put(receiver, callSite); } Set curEdges = callAssigns.get(ie); for (Iterator iterator = curEdges.iterator(); iterator.hasNext();) { Pair callAssign = (Pair) iterator.next(); //for reflective calls, the "O1" value can actually be a FieldRefNode //we simply ignore such cases here (appears to be sound) if(!(callAssign.getO1() instanceof VarNode)) continue; VarNode src = (VarNode) callAssign.getO1(); VarNode dst = (VarNode) callAssign.getO2(); if (skipNode(src)) { continue; } ArraySet edges = getAssignBarEdges(src); AssignEdge edge = null; for (int i = 0; i < edges.size() && edge == null; i++) { AssignEdge curEdge = (AssignEdge) edges.get(i); if (curEdge.getDst() == dst) { edge = curEdge; } } assert edge != null : "no edge from " + src + " to " + dst; boolean edgeFromOtherCallSite = edge.isCallEdge(); if (edgeFromOtherCallSite) { edge = new AssignEdge(src, dst); } edge.setCallSite(callSite); callSiteToEdges.put(callSite, edge); if (SootUtil.isParamNode(dst)) { // assert src instanceof LocalVarNode : src + " " + dst; edge.setParamEdge(); SootMethod invokedMethod = ((LocalVarNode) dst).getMethod(); callSiteToTargets.put(callSite, invokedMethod); callSitesInvokingMethod.put(invokedMethod, callSite); // assert src instanceof LocalVarNode : src + " NOT LOCAL"; if (src instanceof LocalVarNode) { callSitesInMethod.put(((LocalVarNode) src).getMethod(), callSite); } } else if (SootUtil.isRetNode(src)) { edge.setReturnEdge(); SootMethod invokedMethod = ((LocalVarNode) src).getMethod(); callSiteToTargets.put(callSite, invokedMethod); callSitesInvokingMethod.put(invokedMethod, callSite); if (dst instanceof LocalVarNode) { callSitesInMethod.put(((LocalVarNode) dst).getMethod(), callSite); } } else { assert false : "weird call edge " + callAssign; } if (edgeFromOtherCallSite) { addAssignEdge(edge); } } } // System.err.println(callSiteNum + " call sites"); assert callEdgesReasonable(); if (PRINT_CALL_SITE_INFO) { callSiteWriter.close(); } // assert assignEdgesWellFormed(pag) == null : // assignEdgesWellFormed(pag); } private boolean callEdgesReasonable() { Set<VarNode> vars = contextSensitiveAssignEdges.keySet(); for (VarNode node : vars) { ArraySet<AssignEdge> assigns = contextSensitiveAssignEdges .get(node); for (AssignEdge edge : assigns) { if (edge.isCallEdge()) { if (edge.getCallSite() == null) { G.v().out.println(edge + " is weird!!"); return false; } } } } return true; } @SuppressWarnings("unused") private String assignEdgesWellFormed(PAG pag) { for (Iterator iter = pag.getVarNodeNumberer().iterator(); iter .hasNext();) { VarNode v = (VarNode) iter.next(); Set<AssignEdge> outgoingAssigns = getAssignBarEdges(v); for (AssignEdge edge : outgoingAssigns) { if (edge.getSrc() != v) return edge + " src should be " + v; } Set<AssignEdge> incomingAssigns = getAssignEdges(v); for (AssignEdge edge : incomingAssigns) { if (edge.getDst() != v) return edge + " dst should be " + v; } } return null; } /** * @param node * @return */ private boolean skipNode(VarNode node) { return (SKIP_STRING_NODES && SootUtil.isStringNode(node)) || (SKIP_EXCEPTION_NODES && SootUtil.isExceptionNode(node)) || (SKIP_THREAD_GLOBALS && SootUtil.isThreadGlobal(node)); } /** * @param assignSource * @param assignTarget */ private void addAssignEdge(AssignEdge assignEdge) { contextSensitiveAssignEdges.put(assignEdge.getSrc(), assignEdge); contextSensitiveAssignBarEdges.put(assignEdge.getDst(), assignEdge); } public ArraySet<AssignEdge> getAssignBarEdges(VarNode node) { return contextSensitiveAssignEdges.get(node); } /** * * @param node * @return edges capturing assign flow <em>into</em> node */ public ArraySet<AssignEdge> getAssignEdges(VarNode node) { return contextSensitiveAssignBarEdges.get(node); } public Set<SootMethod> methods() { return methodToNodes.keySet(); } public ArraySet<VarNode> getNodesForMethod(SootMethod method) { return methodToNodes.get(method); } public ArraySet<VarNode> getInPortsForMethod(SootMethod method) { return methodToInPorts.get(method); } public ArraySet<VarNode> getOutPortsForMethod(SootMethod method) { return methodToOutPorts.get(method); } /** * @param method * @return */ public ArraySet<Integer> getCallSitesInMethod(SootMethod method) { return callSitesInMethod.get(method); } public Set<Integer> getCallSitesInvokingMethod(SootMethod method) { return callSitesInvokingMethod.get(method); } public ArraySet<AssignEdge> getCallSiteEdges(Integer callSite) { return callSiteToEdges.get(callSite); } public ArraySet<SootMethod> getCallSiteTargets(Integer callSite) { return callSiteToTargets.get(callSite); } public LocalVarNode getReceiverForVirtCallSite(Integer callSite) { LocalVarNode ret = virtCallSiteToReceiver.get(callSite); assert ret != null; return ret; } public Set<Integer> getVirtCallSitesForReceiver(LocalVarNode receiver) { return receiverToVirtCallSites.get(receiver); } public SootMethod getInvokedMethod(Integer callSite) { return callSiteToInvokedMethod.get(callSite); } public SootMethod getInvokingMethod(Integer callSite) { return callSiteToInvokingMethod.get(callSite); } public boolean isVirtCall(Integer callSite) { return virtCallSiteToReceiver.containsKey(callSite); } }