/** * */ package soottocfg.soot.transformers; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import soot.Local; import soot.RefType; import soot.Scene; import soot.SootClass; import soot.SootField; import soot.SootMethod; import soot.Type; import soot.Unit; import soot.jimple.FieldRef; import soot.jimple.InvokeExpr; import soot.jimple.JimpleBody; import soot.jimple.Stmt; import soottocfg.spec.ListSimpleSpec; /** * @author schaef * This transformation takes a map from SootClass to SootClass * that specifies which types to replace by stubs. For example, * this could contain an entry java.util.List -> jayhorn.ListStub * which indicates that all uses of List should be replaced by * ListStub. * This requires that ListStub extends List. * * !!IMPORTANT!!: This assumes that Arrays have been removed already! */ public class SpecClassTransformer extends AbstractSceneTransformer { private final Map<SootClass, SootClass> replacementMap = new HashMap<SootClass, SootClass>(); private final Map<String, SootField> fieldSubstitutionMap = new HashMap<String, SootField>(); private final Map<String, SootMethod> methodSubstitutionMap = new HashMap<String, SootMethod>(); public SpecClassTransformer() { SootClass listSpec = Scene.v().loadClass(ListSimpleSpec.class.getName(), SootClass.SIGNATURES); replacementMap.put(Scene.v().getSootClass(java.util.LinkedList.class.getName()), listSpec); replacementMap.put(Scene.v().getSootClass(java.util.List.class.getName()), listSpec); } /* * (non-Javadoc) * * @see * soottocfg.soot.transformers.AbstractSceneTransformer#applyTransformation( * ) */ @Override public void applyTransformation() { List<SootClass> classes = new LinkedList<SootClass>(Scene.v().getClasses()); List<JimpleBody> bodies = new LinkedList<JimpleBody>(); // List<SootMethod> entryPoints = new LinkedList<SootMethod>(Scene.v().getEntryPoints()); // Don't transform the classes that we replace anyway. classes.removeAll(replacementMap.keySet()); for (SootClass sc : classes) { if (sc.resolvingLevel() >= SootClass.SIGNATURES) { // === change the type of all fields if they are in // replacementMap ===. Set<String> changedFieldSignatures = new HashSet<String>(); for (SootField f : sc.getFields()) { Type newType = updateType(f.getType()); if (!newType.equals(f.getType())) { changedFieldSignatures.add(f.getSignature()); final String oldSig = f.getSignature(); f.setType(newType); fieldSubstitutionMap.put(oldSig, f); } } // === remove original version of changed fields ===. for (String s : changedFieldSignatures) { if (sc.declaresField(s)) { sc.removeField(sc.getFieldByName(s)); } } // === now update methods ===. for (SootMethod sm : sc.getMethods()) { final String oldSignature = sm.getSignature(); // we also have to update the refs in the EntryPoint list. // boolean wasMain = sm.isEntryMethod(); // if (wasMain) { // entryPoints.remove(sm); // } if (sc.resolvingLevel() >= SootClass.BODIES && sm.isConcrete() && !sc.isLibraryClass() && !sc.isJavaLibraryClass()) { // record all methods for which we found a body. bodies.add((JimpleBody) sm.retrieveActiveBody()); } // update return type sm.setReturnType(updateType(sm.getReturnType())); // update parameter types List<Type> newParamTypes = new LinkedList<Type>(); for (Type t : sm.getParameterTypes()) { newParamTypes.add(updateType(t)); } sm.setParameterTypes(newParamTypes); // if (wasMain) { // entryPoints.add(sm); // } methodSubstitutionMap.put(oldSignature, sm); } for (JimpleBody body : bodies) { for (Local local : body.getLocals()) { local.setType(updateType(local.getType())); } for (Unit u : new LinkedList<Unit>(body.getUnits())) { Stmt st = (Stmt) u; if (st.containsFieldRef()) { FieldRef fr = st.getFieldRef(); if (fieldSubstitutionMap.containsKey(fr.getField().getSignature())) { fr.setFieldRef(fieldSubstitutionMap.get(fr.getField().getSignature()).makeRef()); } } if (st.containsInvokeExpr()) { InvokeExpr ivk = st.getInvokeExpr(); if (replacementMap.containsKey(ivk.getMethod().getDeclaringClass())) { SootClass replClass = replacementMap.get(ivk.getMethod().getDeclaringClass()); List<Type> replTypes = new LinkedList<Type>(); for (Type t : ivk.getMethod().getParameterTypes()) { // TODO: it might be better to only replace // this one type. // instead of every type in the map. replTypes.add(updateType(t)); } SootMethod replMethod = replClass.getMethod(ivk.getMethod().getName(), replTypes); ivk.setMethodRef(replMethod.makeRef()); } else if (methodSubstitutionMap.containsKey(ivk.getMethod().getSignature())) { ivk.setMethodRef(methodSubstitutionMap.get(ivk.getMethod().getSignature()).makeRef()); } } } } } } } private Type updateType(Type t) { if (t instanceof RefType) { SootClass sc = ((RefType) t).getSootClass(); if (replacementMap.containsKey(sc)) { return RefType.v(replacementMap.get(sc)); } } return t; } }