package soot.spl.ifds; import heros.EdgeFunction; import heros.EdgeFunctions; import heros.FlowFunction; import heros.FlowFunctions; import heros.IFDSTabulationProblem; import heros.JoinLattice; import heros.ZeroedFlowFunctions; import heros.edgefunc.EdgeIdentity; import heros.solver.IDESolver; import heros.template.DefaultIDETabulationProblem; import java.util.BitSet; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import soot.SootMethod; import soot.Unit; import soot.tagkit.Host; import br.ufal.cideei.soot.instrument.FeatureTag; public class SPLIFDSSolver<D> extends IDESolver<Unit,D,SootMethod,Constraint<String>,ExtendedInterproceduralCFG> { protected final Constraint<String> FULL_FM_CONSTRAINT; /** * Creates a solver for the given problem. The solver must then be started by calling * {@link #solve()}. * @param fullFmConstraint * @param alloyFilePath * @param numFeaturesPresent */ public SPLIFDSSolver(final IFDSTabulationProblem<Unit,D,SootMethod,ExtendedInterproceduralCFG> ifdsProblem, final FeatureModelContext fmContext, final boolean useFMInEdgeComputations) { super(new DefaultIDETabulationProblem<Unit,D,SootMethod,Constraint<String>,ExtendedInterproceduralCFG>(new ExtendedInterproceduralCFG(ifdsProblem.interproceduralCFG())) { public FlowFunctions<Unit,D,SootMethod> createFlowFunctionsFactory() { return new FlowFunctions<Unit,D,SootMethod>() { @Override public FlowFunction<D> getNormalFlowFunction(Unit curr, Unit succ) { FlowFunction<D> original = ifdsProblem.flowFunctions().getNormalFlowFunction(curr, succ); if(hasFeatureAnnotation(curr) && interproceduralCFG().isFallThroughSuccessor(curr, succ)) { return new WrappedFlowFunction<D>(original); } else { return original; } } @Override public FlowFunction<D> getCallFlowFunction(Unit callStmt, SootMethod destinationMethod) { return ifdsProblem.flowFunctions().getCallFlowFunction(callStmt, destinationMethod); } @Override public FlowFunction<D> getReturnFlowFunction(Unit callSite, SootMethod calleeMethod, Unit exitStmt, Unit returnSite) { return ifdsProblem.flowFunctions().getReturnFlowFunction(callSite, calleeMethod, exitStmt, returnSite); } @Override public FlowFunction<D> getCallToReturnFlowFunction(Unit callSite, Unit returnSite) { FlowFunction<D> original = ifdsProblem.flowFunctions().getCallToReturnFlowFunction(callSite, returnSite); if(hasFeatureAnnotation(callSite)) { return new WrappedFlowFunction<D>(original); } else { return original; } } }; } public Set<Unit> initialSeeds() { return ifdsProblem.initialSeeds(); } public D createZeroValue() { return ifdsProblem.zeroValue(); } public EdgeFunctions<Unit,D,SootMethod,Constraint<String>> createEdgeFunctionsFactory() { return new IFDSEdgeFunctions(interproceduralCFG()); } public JoinLattice<Constraint<String>> createJoinLattice() { return new JoinLattice<Constraint<String>>() { public Constraint<String> topElement() { return Constraint.falseValue(); } public Constraint<String> bottomElement() { return Constraint.trueValue(); } public Constraint<String> join(Constraint<String> left, Constraint<String> right) { return left.or(right); } }; } class IFDSEdgeFunctions implements EdgeFunctions<Unit,D,SootMethod,Constraint<String>> { private final FlowFunctions<Unit, D, SootMethod> zeroedFlowFunctions; private final ExtendedInterproceduralCFG icfg; public IFDSEdgeFunctions(ExtendedInterproceduralCFG icfg) { this.icfg = icfg; zeroedFlowFunctions = new ZeroedFlowFunctions<Unit, D, SootMethod>(ifdsProblem.flowFunctions(),ifdsProblem.zeroValue()); } public EdgeFunction<Constraint<String>> getNormalEdgeFunction(Unit currStmt, D currNode, Unit succStmt, D succNode) { return buildFlowFunction(currStmt, succStmt, currNode, succNode, zeroedFlowFunctions.getNormalFlowFunction(currStmt, succStmt), false); } public EdgeFunction<Constraint<String>> getCallEdgeFunction(Unit callStmt, D srcNode, SootMethod destinationMethod,D destNode) { return buildFlowFunction(callStmt, destinationMethod, srcNode, destNode, zeroedFlowFunctions.getCallFlowFunction(callStmt, destinationMethod), true); } public EdgeFunction<Constraint<String>> getReturnEdgeFunction(Unit callSite, SootMethod calleeMethod, Unit exitStmt,D exitNode, Unit returnSite,D retNode) { return buildFlowFunction(exitStmt, returnSite, exitNode, retNode, zeroedFlowFunctions.getReturnFlowFunction(callSite, calleeMethod, exitStmt, returnSite), false); } public EdgeFunction<Constraint<String>> getCallToReturnEdgeFunction(Unit callSite, D callNode, Unit returnSite, D returnSideNode) { return buildFlowFunction(callSite, returnSite, callNode, returnSideNode, zeroedFlowFunctions.getCallToReturnFlowFunction(callSite, returnSite), false); } private EdgeFunction<Constraint<String>> buildFlowFunction(Unit src, Host successor, D srcNode, D tgtNode, FlowFunction<D> originalFlowFunction, boolean isCall) { boolean srcAnnotated = hasFeatureAnnotation(src); boolean succAnnotated = hasFeatureAnnotation(successor); if(!srcAnnotated && !(isCall && succAnnotated)) return EdgeIdentity.v(); BitSet features = new BitSet(); if(srcAnnotated) features.or(features(src)); if(isCall && succAnnotated) features.or(features(successor)); boolean isFallThroughEdge = false; if(successor instanceof Unit) { Unit succUnit = (Unit) successor; isFallThroughEdge = icfg.isFallThroughSuccessor(src,succUnit); } boolean canFallThrough = !isCall && src.fallsThrough(); Constraint<String> pos = originalFlowFunction.computeTargets(srcNode).contains(tgtNode) && !(isFallThroughEdge && !canFallThrough) ? Constraint.<String>make(features,true) : Constraint.<String>falseValue(); Constraint<String> neg = srcNode==tgtNode && isFallThroughEdge ? Constraint.<String>make(features,false): Constraint.<String>falseValue(); Constraint<String> lifted = pos.or(neg); if(useFMInEdgeComputations) { lifted = lifted.and(fmContext.getSimplifiedFMConstraint()); } return new SPLFeatureFunction(lifted, fmContext); } private BitSet features(Host successor) { FeatureTag tag = (FeatureTag) successor.getTag(FeatureTag.FEAT_TAG_NAME); if(tag==null) { return new BitSet(); } else { BitSet features = tag.getFeatureRep(); return features; } } } public EdgeFunction<Constraint<String>> createAllTopFunction() { return new SPLFeatureFunction(Constraint.<String>falseValue(), fmContext); } }); this.FULL_FM_CONSTRAINT = fmContext.getFullFMConstraint(); } private static boolean hasFeatureAnnotation(Host host) { return host.hasTag(FeatureTag.FEAT_TAG_NAME); } /** * Returns the set of facts that hold at the given statement. */ public Set<D> ifdsResultsAt(Unit statement) { return resultsAt(statement).keySet(); } public Constraint<String> resultAt(Unit stmt, D value) { return super.resultAt(stmt, value).and(FULL_FM_CONSTRAINT); } @Override public Map<D, Constraint<String>> resultsAt(Unit stmt) { Map<D, Constraint<String>> resultsAt = super.resultsAt(stmt); Map<D, Constraint<String>> res = new HashMap<D, Constraint<String>>(); for(Entry<D,Constraint<String>> entry: resultsAt.entrySet()) { res.put(entry.getKey(), entry.getValue().and(FULL_FM_CONSTRAINT)); } return res; } static class WrappedFlowFunction<D> implements FlowFunction<D> { private FlowFunction<D> del; private WrappedFlowFunction(FlowFunction<D> del) { this.del = del; } @Override public Set<D> computeTargets(D source) { Set<D> targets = new HashSet<D>(del.computeTargets(source)); targets.add(source); return targets; } } }