/** * * * @author Ju Qian {jqian@live.com} * @date 2011-8-8 * @version */ package jqian.sootex.sideeffect; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import soot.Body; import soot.Local; import soot.RefLikeType; import soot.SootMethod; import soot.Unit; import soot.Value; import soot.jimple.AnyNewExpr; import soot.jimple.CastExpr; import soot.jimple.ConcreteRef; import soot.jimple.Constant; import soot.jimple.DefinitionStmt; import soot.jimple.IdentityStmt; import soot.jimple.InstanceInvokeExpr; import soot.jimple.InvokeExpr; import soot.jimple.InvokeStmt; import soot.jimple.ReturnStmt; import soot.jimple.ThrowStmt; import soot.jimple.toolkits.callgraph.CallGraph; import soot.shimple.PhiExpr; import soot.toolkits.graph.MutableDirectedGraph; import jqian.Global; import jqian.sootex.Cache; import jqian.sootex.util.SootUtils; import jqian.sootex.util.callgraph.Callees; import jqian.sootex.util.graph.GraphHelper; import jqian.util.Utils; /** * Implement the fresh method and variable analysis provided by Gay@CC 2000 A * fresh method return a newly created objects (not exist before method call). * NOTE that the object may already bean escaped or also return from parameters. * */ @SuppressWarnings({ "unchecked", "rawtypes" }) public class FastEscapeAnalysis extends FreshAnalysis { // ͨ��ջ�ϵĴ��ݣ����ɷ���ֵ©���ı��� protected Set<Local>[] _returned; // ���ڲ���throw���������򡢾�̬������©���ı��� protected Set<Local>[] _vescaped; protected boolean[] _mescaped; public FastEscapeAnalysis(CallGraph cg) { super(cg); } /** add constrains caused by dependences from out to in parameters. */ private void addParamInOutConstraints(MutableDirectedGraph<Object> varConn, SootMethod tgt, Set<Local> returned, Value left, Value right) { if (returned.isEmpty()) { return; } Body body = tgt.retrieveActiveBody(); if (!tgt.isStatic()) { Local thiz = body.getThisLocal(); if (returned.contains(thiz)) { varConn.addEdge(left, ((InstanceInvokeExpr) right).getBase()); } } int pc = tgt.getParameterCount(); InvokeExpr invoke = (InvokeExpr) right; for (int i = 0; i < pc; i++) { Local p = body.getParameterLocal(i); if (returned.contains(p)) { Value lv = invoke.getArg(i); if (lv instanceof Local) { varConn.addEdge(left, lv); } } } } /** return����ֻҪ����ReturnStmt����ȡһ�±����ص�Value�Ϳ�����ô��ΪʲôҪ����DefinitionStmt����� */ protected void returnedAnalysis(SootMethod m) { // build a variable connection graph MutableDirectedGraph<Object> varConn = initVarConnectionGraph(m); Body body = m.retrieveActiveBody(); for (Unit u : body.getUnits()) { if (u instanceof ReturnStmt) { Value v = ((ReturnStmt) u).getOp(); if (v.getType() instanceof RefLikeType && v instanceof Local) { varConn.addEdge(Boolean.TRUE, v); } } else if (u instanceof DefinitionStmt) { DefinitionStmt d = (DefinitionStmt) u; Value left = d.getLeftOp(); Value right = d.getRightOp(); // assignment on non-reference variables/** if (!(left.getType() instanceof RefLikeType)) { } else if (left instanceof Local) { if (right instanceof Local) { varConn.addEdge(left, right); } else if (right instanceof CastExpr) { CastExpr cast = (CastExpr) right; Value op = cast.getOp(); if (op instanceof Local) { varConn.addEdge(left, op); }//2012.2.15 tao }else if (right instanceof PhiExpr) { List<Value> arg=((PhiExpr) right).getValues(); for(Value value:arg){ varConn.addEdge(left, value); } //throw new RuntimeException("PhiExpr processing has not implemented"); } else if (right instanceof InvokeExpr) { Callees callees = new Callees(_cg, u); for (SootMethod tgt : callees.explicits()) { Set<Local> returned = _returned[tgt.getNumber()]; // if called method not analyzed (recursion), use worst assumption if (returned == null) { /** �����static�أ��к����� **/ if (!tgt.isStatic()) { varConn.addEdge(left, ((InstanceInvokeExpr) right).getBase()); } int pc = tgt.getParameterCount(); InvokeExpr invoke = (InvokeExpr) right; for (int i = 0; i < pc; i++) { Value lv = invoke.getArg(i); if (lv instanceof Local && lv.getType() instanceof RefLikeType) { varConn.addEdge(left, lv); } } break; } else { addParamInOutConstraints(varConn, tgt, returned, left, right); } } } } } } // solve the constraints Set returned = GraphHelper.getReachables(varConn, Boolean.TRUE); _returned[m.getNumber()] = returned; } private void handleMethodCallForEscape( MutableDirectedGraph<Object> varConn, Value left, Value right, Unit call) { Callees callees = new Callees(_cg, call); for (SootMethod tgt : callees.explicits()) { int tgtId = tgt.getNumber(); Set<Local> escaped = _vescaped[tgtId]; // if callee not analyzed yet, use worst assumptions if (escaped == null) { // return value itself if (left != null) { varConn.addEdge(Boolean.TRUE, left); } // receiver if (!tgt.isStatic()) { varConn.addEdge(Boolean.TRUE,((InstanceInvokeExpr) right).getBase()); } // arguments int pc = tgt.getParameterCount(); InvokeExpr invoke = (InvokeExpr) right; for (int i = 0; i < pc; i++) { Value lv = invoke.getArg(i); if (lv instanceof Local && lv.getType() instanceof RefLikeType) { varConn.addEdge(Boolean.TRUE, lv); } } break; } else { // if returned value escapes, then the source will also escapes if (left != null) { Set<Local> returned = _returned[tgtId]; addParamInOutConstraints(varConn, tgt, returned, left, right); } // return value already escaped in the callee if (left != null && _mescaped[tgtId]) { varConn.addEdge(Boolean.TRUE, left); } // receiver value already escaped in the callee Body body = tgt.retrieveActiveBody(); if (!tgt.isStatic()) { Local thiz = body.getThisLocal(); if (escaped.contains(thiz)) { varConn.addEdge(Boolean.TRUE, ((InstanceInvokeExpr) right).getBase()); } } // arguments int pc = tgt.getParameterCount(); InvokeExpr invoke = (InvokeExpr) right; for (int i = 0; i < pc; i++) { Local p = body.getParameterLocal(i); if (escaped.contains(p)) { Value lv = invoke.getArg(i); if (lv instanceof Local && lv.getType() instanceof RefLikeType) { varConn.addEdge(Boolean.TRUE, lv); } } } } } // TODO: Ignore implicit method calls here } protected void escapedAnalysis(SootMethod m) { Set<Local> returns = new HashSet<Local>(); // build a constraint graph MutableDirectedGraph<Object> varConn = initVarConnectionGraph(m); Body body = m.retrieveActiveBody(); for (Unit u : body.getUnits()) { if (u instanceof ReturnStmt) { Value v = ((ReturnStmt) u).getOp(); if (v.getType() instanceof RefLikeType && v instanceof Local) { returns.add((Local) v); } } else if (u instanceof ThrowStmt) { Value t = ((ThrowStmt) u).getOp(); varConn.addEdge(Boolean.TRUE, t); } else if (u instanceof DefinitionStmt) { DefinitionStmt d = (DefinitionStmt) u; Value left = d.getLeftOp(); Value right = d.getRightOp(); // assignment on non-reference variables if (!(left.getType() instanceof RefLikeType)) { } else if (left instanceof Local) { // 1. l = @param, l = @this if (d instanceof IdentityStmt) { } // 2. "l = new C", "l = constant" else if (right instanceof AnyNewExpr || right instanceof Constant) { } // 3. l = r else if (right instanceof Local) { varConn.addEdge(left, right); } // 4. l = (cast)r else if (right instanceof CastExpr) { CastExpr cast = (CastExpr) right; Value op = cast.getOp(); if (op instanceof Local) { varConn.addEdge(left, op); } } // 5. l = r.f, l = r[], l = g else if (right instanceof ConcreteRef) { } else if (right instanceof InvokeExpr) { handleMethodCallForEscape(varConn, left, right, u); } // phiExpr else if (right instanceof PhiExpr) { List<Value> arg=((PhiExpr) right).getValues(); for(Value value:arg){ varConn.addEdge(left, value); } //throw new RuntimeException("PhiExpr processing has not implemented"); } else { // 2012.2.11 tao modified // throw new RuntimeException(); } } // 6. g = r, l.f = r, l[] = r else if (left instanceof ConcreteRef) { if (right instanceof Local) { varConn.addEdge(Boolean.TRUE, right); } } // others else { throw new RuntimeException(); } } else if (u instanceof InvokeStmt) { handleMethodCallForEscape(varConn, null, ((InvokeStmt) u).getInvokeExpr(), u); } } // solve constraints Set escaped = GraphHelper.getReachables(varConn, Boolean.TRUE); _vescaped[m.getNumber()] = escaped; boolean esc = false; for (Local r : returns) { if (escaped.contains(r)) { esc = true; break; } } _mescaped[m.getNumber()] = esc; } public void build() { Date startTime = new Date(); _mfresh = new boolean[SootUtils.getMethodCount()]; _vnfresh = new Set[SootUtils.getMethodCount()]; _returned = new Set[SootUtils.getMethodCount()]; _vescaped = new Set[SootUtils.getMethodCount()]; _mescaped = new boolean[SootUtils.getMethodCount()]; List<?> rm = Cache.v().getTopologicalOrder(); for (Iterator<?> it = rm.iterator(); it.hasNext();) { SootMethod m = (SootMethod) it.next(); if (m.isConcrete()) { boolean hasRefReturn = m.getReturnType() instanceof RefLikeType; if (hasRefReturn) { returnedAnalysis(m); } else { _returned[m.getNumber()] = Collections.EMPTY_SET; } escapedAnalysis(m); freshAnalysis(m); } } _cg = null; Date endTime = new Date(); Global.v().out.println("[FastEscape] " + rm.size() + " methods analyszed in " + Utils.getTimeConsumed(startTime, endTime)); } public Set<Local> getReturnedLocals(SootMethod m) { return _returned[m.getNumber()]; } public Set<Local> getEscapedLocals(SootMethod m) { return _vescaped[m.getNumber()]; } public boolean isReturnValueEscaped(SootMethod m) { return _mescaped[m.getNumber()]; } public boolean isRefTgtLocal(SootMethod m, Local v) { int mId = m.getNumber(); Set<Local> escaped = _vescaped[mId]; Set<Local> returned = _returned[mId]; Set<Local> nonfresh = _vnfresh[mId]; if (escaped == null || escaped.contains(v)) { return false; } if (returned == null || returned.contains(v)) { return false; } if (nonfresh.contains(v)) { return false; } return true; } }