/** * */ package soottocfg.soot.transformers; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import com.google.common.base.Preconditions; import soot.ArrayType; import soot.Local; import soot.Modifier; import soot.RefType; import soot.Scene; import soot.SootClass; import soot.SootField; import soot.SootMethod; import soot.Type; import soot.Unit; import soot.ValueBox; import soot.VoidType; import soot.jimple.Jimple; import soot.jimple.JimpleBody; import soot.jimple.StaticFieldRef; import soottocfg.soot.util.SootTranslationHelpers; /** * @author schaef * */ public class StaticInitializerTransformer extends AbstractSceneTransformer { private final Set<SootMethod> staticInitializers = new LinkedHashSet<SootMethod>(); /** * */ public StaticInitializerTransformer() { // TODO Auto-generated constructor stub } @Override public void applyTransformation() { for (SootClass sc : new LinkedList<SootClass>(Scene.v().getClasses())) { if ((sc.resolvingLevel() >= SootClass.SIGNATURES && sc.isApplicationClass()) || sc == SootTranslationHelpers.v().getAssertionClass()) { initializeStaticFields(sc); } } addStaticInitializerCallsToMain(); } private void addStaticInitializerCallsToMain() { Preconditions.checkNotNull(Scene.v().getMainMethod()); SootMethod entry = Scene.v().getMainMethod(); for (SootMethod initializer : this.madeUpSort()) { Unit initCall = Jimple.v().newInvokeStmt(Jimple.v().newStaticInvokeExpr(initializer.makeRef())); JimpleBody jb = (JimpleBody) entry.getActiveBody(); jb.getUnits().insertBefore(initCall, jb.getFirstNonIdentityStmt()); } } private void initializeStaticFields(SootClass containingClass) { // find all static fields of the class. Set<SootField> staticFields = new LinkedHashSet<SootField>(); for (SootField f : containingClass.getFields()) { if (f.isStatic()) { staticFields.add(f); } } if (staticFields.isEmpty()) { return; // nothing to do. } SootMethod staticInit = null; for (SootMethod m : containingClass.getMethods()) { if (m.isStaticInitializer()) { staticInit = m; break; } } if (staticInit == null) { // TODO: super hacky! staticInit = new SootMethod(SootMethod.staticInitializerName, new LinkedList<soot.Type>(), VoidType.v(), Modifier.STATIC | Modifier.PUBLIC); JimpleBody body = Jimple.v().newBody(staticInit); body.getUnits().add(Jimple.v().newReturnVoidStmt()); staticInit.setActiveBody(body); containingClass.addMethod(staticInit); } staticInitializers.add(staticInit); for (ValueBox vb : staticInit.retrieveActiveBody().getDefBoxes()) { if (vb.getValue() instanceof StaticFieldRef) { staticFields.remove(((StaticFieldRef) vb.getValue()).getField()); } } for (SootField f : staticFields) { Unit init = Jimple.v().newAssignStmt(Jimple.v().newStaticFieldRef(f.makeRef()), SootTranslationHelpers.v().getDefaultValue(f.getType())); staticInit.getActiveBody().getUnits().addFirst(init); } } /** TODO: * These are just a few random rules to sort static initializers to prevent * some unsound cases. This is NOT THE RIGHT WAY DO THING. If we want to be sound, * we have to call static initializers the first time a class is accessed. This * requires some real analysis. * @return */ private List<SootMethod> madeUpSort() { List<SootMethod> orderedInitializers = new LinkedList<SootMethod>(staticInitializers); Collections.sort(orderedInitializers, new Comparator<SootMethod>() { @Override public int compare(SootMethod o1, SootMethod o2) { SootClass c1 = o1.getDeclaringClass(); SootClass c2 = o2.getDeclaringClass(); for (SootField sf : c1.getFields()) { if (hasBaseType(sf.getType(), c2)) { return -1; } } for (SootField sf : c2.getFields()) { if (hasBaseType(sf.getType(), c1)) { return 1; } } if (o1.hasActiveBody()) { for (Local l : o1.getActiveBody().getLocals()) { if (hasBaseType(l.getType(), c2)) { return -1; } } } if (o2.hasActiveBody()) { for (Local l : o2.getActiveBody().getLocals()) { if (hasBaseType(l.getType(), c1)) { return 1; } } } // if (c1.hasOuterClass() && c1.getOuterClass().equals(c2)) { // return -1; // } else if (c2.hasOuterClass() && c2.getOuterClass().equals(c1)) { // return 1; // } //otherwise we can do it last. return -1; } private boolean hasBaseType(Type t, SootClass c) { if (t instanceof RefType && ((RefType)t).getSootClass().equals(c)) { return true; } else if (t instanceof ArrayType) { return hasBaseType( ((ArrayType)t).baseType, c); } return false; } }); return orderedInitializers; } }