package jayhorn.hornify; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import com.google.common.base.Verify; import jayhorn.Log; import jayhorn.solver.Prover; import jayhorn.utils.GhostRegister; import soottocfg.cfg.Program; import soottocfg.cfg.method.Method; import soottocfg.cfg.type.IntType; import soottocfg.cfg.type.ReferenceType; import soottocfg.cfg.type.Type; import soottocfg.cfg.util.GraphUtil; import soottocfg.cfg.variable.ClassVariable; import soottocfg.cfg.variable.Variable; import soottocfg.soot.transformers.ArrayTransformer; /** * Holds the context of the current Horn clause construction: * - mapping from ClassVariables to Invariant Predicates * - mapping from ClassVariables to unique numbers. * * This class assigns a unique number to every ClassType * in a program. This is used to implement instanceof checks: * for "a instanceof b" we obtain the list of IDs of all * subtypes of "b" using getSubtypeIDs and check if any of * these is equal to the ID of "a" that we obtain with * "getTypeID". * * Make sure that there exists no more than one instance of this per * Prover. * @author schaef * */ public class HornEncoderContext { private final Program program; private final Prover p; private Map<ClassVariable, Integer> typeIds = new LinkedHashMap<ClassVariable, Integer>(); private Map<ClassVariable, Map<Long, HornPredicate>> invariantPredicates = new HashMap<ClassVariable, Map<Long, HornPredicate>>(); private Map<Method, MethodContract> methodContracts = new LinkedHashMap<Method, MethodContract>(); public HornEncoderContext(Prover p, Program prog) { this.program = prog; this.p = p; for (ClassVariable var : program.getTypeGraph().vertexSet()) { //add +1 to make sure that no type is the //same number as the null constant typeIds.put(var, typeIds.size()+1); } mkMethodContract(program, p); } /** * Get the invariant predicates for each class. This is for debugging only. * @return */ public Map<ClassVariable, Map<Long, HornPredicate>> getInvariantPredicates() { return this.invariantPredicates; } /** * Creates method contracts for all methods in the current scene. * @param program * @param p */ private void mkMethodContract(Program program, Prover p) { for (Method method : program.getMethods()) { final List<Variable> inParams = new ArrayList<Variable>(method.getInParams()); final List<Variable> postParams = new ArrayList<Variable>(); postParams.addAll(inParams); if (!method.getOutParams().isEmpty()) { Verify.verify(method.getOutParams().size()==method.getReturnType().size(), method.getOutParams().size()+"!="+method.getReturnType().size()); postParams.addAll(method.getOutParams()); } else if (!method.getReturnType().isEmpty()) { int ctr = 0; for (Type tp : method.getReturnType()) { postParams.add(new Variable("resultVar" + (ctr++), tp)); } } Log.debug("method: " + method.getMethodName()); Log.debug("pre: " + inParams); Log.debug("post: " + postParams); final HornPredicate pre = new HornPredicate(p, method.getMethodName() + "_pre", inParams); final HornPredicate post = new HornPredicate(p, method.getMethodName() + "_post", postParams); methodContracts.put(method, new MethodContract(method, pre, post)); } } /** * Return the method contract * * @param methodName * @return */ public MethodContract getMethodContract(Method method) { return methodContracts.get(method); } /** * Find or create the HornPredicate that encodes the class * invariant for the class represented by "sig". * @param sig * @return */ public HornPredicate lookupInvariantPredicate(ClassVariable sig, long pushId) { if (!invariantPredicates.containsKey(sig)) invariantPredicates.put (sig, new HashMap<Long, HornPredicate>()); final Map<Long, HornPredicate> subMap = invariantPredicates.get(sig); if (!subMap.containsKey(pushId)) { List<Variable> args = new ArrayList<Variable>(); args.add(new Variable("ref", new ReferenceType(sig))); //add variables for the ghost fields //used by pull and push. for (Entry<String, Type> entry : GhostRegister.v().ghostVariableMap.entrySet()) { args.add(new Variable(entry.getKey(), entry.getValue())); } if (soottocfg.Options.v().arrayInv() && (sig.getName().contains(ArrayTransformer.arrayTypeName))) { args.add(new Variable("array_index", IntType.instance())); } for (Variable v : sig.getAssociatedFields()) { args.add(v); } String name = "inv_" + sig.getName(); if (pushId >= 0) name = name + "_" + pushId; subMap.put(pushId, new HornPredicate(p, name, args)); } return subMap.get(pushId); } /** * Maps a ClassVariable to a unique integer. * @param var * @return */ public Integer getTypeID(ClassVariable var) { return typeIds.get(var); } /** * Returns the unique IDs of all possible subtypes * of var. * @param var * @return */ public Set<Integer> getSubtypeIDs(ClassVariable var) { final Set<Integer> result = new HashSet<Integer>(); for (ClassVariable v : GraphUtil.getForwardReachableVertices(program.getTypeGraph(), var)) { result.add(getTypeID(v)); } return result; } public Program getProgram() { return this.program; } /** * Gets a map from ClassVariable to unique ID. * @return */ public Map<ClassVariable, Integer> getTypeIds() { return typeIds; } }