use of soot.options.Options in project soot by Sable.
the class DeadAssignmentEliminator method internalTransform.
/**
* Eliminates dead code in a linear fashion. Complexity is linear
* with respect to the statements.
*
* Does not work on grimp code because of the check on the right hand
* side for side effects.
*/
@Override
protected void internalTransform(Body b, String phaseName, Map<String, String> options) {
boolean eliminateOnlyStackLocals = PhaseOptions.getBoolean(options, "only-stack-locals");
final Options soptions = Options.v();
if (soptions.verbose()) {
logger.debug("[" + b.getMethod().getName() + "] Eliminating dead code...");
}
if (soptions.time()) {
Timers.v().deadCodeTimer.start();
}
Chain<Unit> units = b.getUnits();
Deque<Unit> q = new ArrayDeque<Unit>(units.size());
// Make a first pass through the statements, noting
// the statements we must absolutely keep.
boolean isStatic = b.getMethod().isStatic();
boolean allEssential = true;
boolean checkInvoke = false;
Local thisLocal = null;
for (Iterator<Unit> it = units.iterator(); it.hasNext(); ) {
Unit s = it.next();
boolean isEssential = true;
if (s instanceof NopStmt) {
// Hack: do not remove nop if is is used for a Trap
// which is at the very end of the code.
boolean removeNop = it.hasNext();
if (!removeNop) {
removeNop = true;
for (Trap t : b.getTraps()) {
if (t.getEndUnit() == s) {
removeNop = false;
break;
}
}
}
if (removeNop) {
it.remove();
continue;
}
} else if (s instanceof AssignStmt) {
AssignStmt as = (AssignStmt) s;
Value lhs = as.getLeftOp();
Value rhs = as.getRightOp();
// Stmt is of the form a = a which is useless
if (lhs == rhs && lhs instanceof Local) {
it.remove();
continue;
}
if (lhs instanceof Local && (!eliminateOnlyStackLocals || ((Local) lhs).getName().startsWith("$") || lhs.getType() instanceof NullType)) {
isEssential = false;
if (!checkInvoke) {
checkInvoke = as.containsInvokeExpr();
}
if (rhs instanceof CastExpr) {
// CastExpr : can trigger ClassCastException, but null-casts never fail
CastExpr ce = (CastExpr) rhs;
Type t = ce.getCastType();
Value v = ce.getOp();
isEssential = !(v instanceof NullConstant && t instanceof RefType);
} else if (rhs instanceof InvokeExpr || rhs instanceof ArrayRef || rhs instanceof NewExpr || rhs instanceof NewArrayExpr || rhs instanceof NewMultiArrayExpr) {
// ArrayRef : can have side effects (like throwing a null pointer exception)
// InvokeExpr : can have side effects (like throwing a null pointer exception)
// NewArrayExpr : can throw exception
// NewMultiArrayExpr : can throw exception
// NewExpr : can trigger class initialization
isEssential = true;
} else if (rhs instanceof FieldRef) {
// Can trigger class initialization
isEssential = true;
if (rhs instanceof InstanceFieldRef) {
InstanceFieldRef ifr = (InstanceFieldRef) rhs;
if (!isStatic && thisLocal == null) {
thisLocal = b.getThisLocal();
}
// Any InstanceFieldRef may have side effects,
// unless the base is reading from 'this'
// in a non-static method
isEssential = (isStatic || thisLocal != ifr.getBase());
}
} else if (rhs instanceof DivExpr || rhs instanceof RemExpr) {
BinopExpr expr = (BinopExpr) rhs;
Type t1 = expr.getOp1().getType();
Type t2 = expr.getOp2().getType();
// Can trigger a division by zero
boolean t2Int = t2 instanceof IntType;
isEssential = t2Int || t1 instanceof IntType || t1 instanceof LongType || t2 instanceof LongType || t1 instanceof UnknownType || t2 instanceof UnknownType;
if (isEssential && t2Int) {
Value v = expr.getOp2();
if (v instanceof IntConstant) {
IntConstant i = (IntConstant) v;
isEssential = (i.value == 0);
} else
// could be 0, we don't know
isEssential = true;
}
if (isEssential && t2 instanceof LongType) {
Value v = expr.getOp2();
if (v instanceof LongConstant) {
LongConstant l = (LongConstant) v;
isEssential = (l.value == 0);
} else
// could be 0, we don't know
isEssential = true;
}
}
}
}
if (isEssential) {
q.addFirst(s);
}
allEssential &= isEssential;
}
if (checkInvoke || !allEssential) {
// Add all the statements which are used to compute values
// for the essential statements, recursively
final LocalDefs localDefs = LocalDefs.Factory.newLocalDefs(b);
if (!allEssential) {
Set<Unit> essential = new HashSet<Unit>(b.getUnits().size());
while (!q.isEmpty()) {
Unit s = q.removeFirst();
if (essential.add(s)) {
for (ValueBox box : s.getUseBoxes()) {
Value v = box.getValue();
if (v instanceof Local) {
Local l = (Local) v;
List<Unit> defs = localDefs.getDefsOfAt(l, s);
if (defs != null)
q.addAll(defs);
}
}
}
}
// Remove the dead statements
units.retainAll(essential);
}
if (checkInvoke) {
final LocalUses localUses = LocalUses.Factory.newLocalUses(b, localDefs);
// Eliminate dead assignments from invokes such as x = f(), where
// x is no longer used
List<AssignStmt> postProcess = new ArrayList<AssignStmt>();
for (Unit u : units) {
if (u instanceof AssignStmt) {
AssignStmt s = (AssignStmt) u;
if (s.containsInvokeExpr()) {
// Just find one use of l which is essential
boolean deadAssignment = true;
for (UnitValueBoxPair pair : localUses.getUsesOf(s)) {
if (units.contains(pair.unit)) {
deadAssignment = false;
break;
}
}
if (deadAssignment) {
postProcess.add(s);
}
}
}
}
final Jimple jimple = Jimple.v();
for (AssignStmt s : postProcess) {
// Transform it into a simple invoke.
Stmt newInvoke = jimple.newInvokeStmt(s.getInvokeExpr());
newInvoke.addAllTagsOf(s);
units.swapWith(s, newInvoke);
// If we have a callgraph, we need to fix it
if (Scene.v().hasCallGraph())
Scene.v().getCallGraph().swapEdgesOutOf(s, newInvoke);
}
}
}
if (soptions.time()) {
Timers.v().deadCodeTimer.end();
}
}
use of soot.options.Options in project soot by Sable.
the class DexlibAbstractInstruction method addTags.
/**
* Tag the passed host with: - this instructions line number (if one is set)
* - the original bytecode offset
*
* @param host
* the host to tag
*/
protected void addTags(Host host) {
Options options = Options.v();
if (options.keep_line_number() && lineNumber != -1) {
host.addTag(new LineNumberTag(lineNumber));
host.addTag(new SourceLineNumberTag(lineNumber));
}
if (options.keep_offset())
host.addTag(new BytecodeOffsetTag(codeAddress));
}
use of soot.options.Options in project soot by Sable.
the class DexBody method jimplify.
/**
* Return the jimple equivalent of this body.
*
* @param m
* the SootMethod that contains this body
*/
public Body jimplify(Body b, SootMethod m) {
final Jimple jimple = Jimple.v();
final UnknownType unknownType = UnknownType.v();
final NullConstant nullConstant = NullConstant.v();
final Options options = Options.v();
/*
* Timer t_whole_jimplification = new Timer(); Timer t_num = new
* Timer(); Timer t_null = new Timer();
*
* t_whole_jimplification.start();
*/
jBody = (JimpleBody) b;
deferredInstructions = new ArrayList<DeferableInstruction>();
instructionsToRetype = new HashSet<RetypeableInstruction>();
if (IDalvikTyper.ENABLE_DVKTYPER) {
DalvikTyper.v().clear();
}
// process method parameters and generate Jimple locals from Dalvik
// registers
List<Local> paramLocals = new LinkedList<Local>();
if (!isStatic) {
int thisRegister = numRegisters - numParameterRegisters - 1;
// generateLocal(UnknownType.v());
Local thisLocal = jimple.newLocal("$u" + thisRegister, unknownType);
jBody.getLocals().add(thisLocal);
registerLocals[thisRegister] = thisLocal;
JIdentityStmt idStmt = (JIdentityStmt) jimple.newIdentityStmt(thisLocal, jimple.newThisRef(declaringClassType));
add(idStmt);
paramLocals.add(thisLocal);
if (IDalvikTyper.ENABLE_DVKTYPER) {
DalvikTyper.v().setType(idStmt.leftBox, jBody.getMethod().getDeclaringClass().getType(), false);
}
}
{
// index of parameter type
int i = 0;
// index
int parameterRegister = numRegisters - numParameterRegisters;
// register
for (Type t : parameterTypes) {
// may
Local gen = jimple.newLocal("$u" + parameterRegister, unknownType);
// only
// use
// UnknownType
// here
// because
// the
// local
// may
// be
// reused
// with
// a
// different
// type
// later
// (before
// splitting)
jBody.getLocals().add(gen);
registerLocals[parameterRegister] = gen;
JIdentityStmt idStmt = (JIdentityStmt) jimple.newIdentityStmt(gen, jimple.newParameterRef(t, i++));
add(idStmt);
paramLocals.add(gen);
if (IDalvikTyper.ENABLE_DVKTYPER) {
DalvikTyper.v().setType(idStmt.leftBox, t, false);
}
// used later in the Dalvik bytecode
if (t instanceof LongType || t instanceof DoubleType) {
parameterRegister++;
// may
Local g = jimple.newLocal("$u" + parameterRegister, unknownType);
// only
// use
// UnknownType
// here
// because
// the
// local
// may
// be
// reused
// with
// a
// different
// type
// later
// (before
// splitting)
jBody.getLocals().add(g);
registerLocals[parameterRegister] = g;
}
parameterRegister++;
}
}
for (int i = 0; i < (numRegisters - numParameterRegisters - (isStatic ? 0 : 1)); i++) {
registerLocals[i] = jimple.newLocal("$u" + i, unknownType);
jBody.getLocals().add(registerLocals[i]);
}
// add local to store intermediate results
storeResultLocal = jimple.newLocal("$u-1", unknownType);
jBody.getLocals().add(storeResultLocal);
// process bytecode instructions
final boolean isOdex = dexFile instanceof DexBackedDexFile ? ((DexBackedDexFile) dexFile).isOdexFile() : false;
ClassPath cp = null;
if (isOdex) {
String[] sootClasspath = options.soot_classpath().split(File.pathSeparator);
List<String> classpathList = new ArrayList<String>();
for (String str : sootClasspath) classpathList.add(str);
try {
ClassPathResolver resolver = new ClassPathResolver(classpathList, classpathList, classpathList, dexFile);
cp = new ClassPath(resolver.getResolvedClassProviders().toArray(new ClassProvider[0]));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
int prevLineNumber = -1;
for (DexlibAbstractInstruction instruction : instructions) {
if (isOdex && instruction instanceof OdexInstruction)
((OdexInstruction) instruction).deOdex(dexFile, method, cp);
if (dangling != null) {
dangling.finalize(this, instruction);
dangling = null;
}
instruction.jimplify(this);
if (instruction.getLineNumber() > 0)
prevLineNumber = instruction.getLineNumber();
else {
instruction.setLineNumber(prevLineNumber);
}
}
for (DeferableInstruction instruction : deferredInstructions) {
instruction.deferredJimplify(this);
}
if (tries != null)
addTraps();
int prevLn = -1;
final boolean keepLineNumber = options.keep_line_number();
for (DexlibAbstractInstruction instruction : instructions) {
Unit unit = instruction.getUnit();
int lineNumber = unit.getJavaSourceStartLineNumber();
if (keepLineNumber && lineNumber < 0) {
if (prevLn >= 0) {
unit.addTag(new LineNumberTag(prevLn));
unit.addTag(new SourceLineNumberTag(prevLn));
}
} else {
prevLn = lineNumber;
}
}
// At this point Jimple code is generated
// Cleaning...
instructions = null;
// registerLocals = null;
// storeResultLocal = null;
instructionAtAddress.clear();
// localGenerator = null;
deferredInstructions = null;
// instructionsToRetype = null;
dangling = null;
tries = null;
/*
* We eliminate dead code. Dead code has been shown to occur under the
* following circumstances.
*
* 0006ec: 0d00 |00a2: move-exception v0 ... 0006f2: 0d00 |00a5:
* move-exception v0 ... 0x0041 - 0x008a Ljava/lang/Throwable; -> 0x00a5
* <any> -> 0x00a2
*
* Here there are two traps both over the same region. But the same
* always fires, hence rendering the code at a2 unreachable. Dead code
* yields problems during local splitting because locals within dead
* code will not be split. Hence we remove all dead code here.
*/
// Fix traps that do not catch exceptions
DexTrapStackFixer.v().transform(jBody);
// Sort out jump chains
DexJumpChainShortener.v().transform(jBody);
// Make sure that we don't have any overlapping uses due to returns
DexReturnInliner.v().transform(jBody);
// Shortcut: Reduce array initializations
DexArrayInitReducer.v().transform(jBody);
// split first to find undefined uses
getLocalSplitter().transform(jBody);
// Remove dead code and the corresponding locals before assigning types
getUnreachableCodeEliminator().transform(jBody);
DeadAssignmentEliminator.v().transform(jBody);
UnusedLocalEliminator.v().transform(jBody);
for (RetypeableInstruction i : instructionsToRetype) i.retype(jBody);
if (IDalvikTyper.ENABLE_DVKTYPER) {
DexReturnValuePropagator.v().transform(jBody);
getCopyPopagator().transform(jBody);
DexNullThrowTransformer.v().transform(jBody);
DalvikTyper.v().typeUntypedConstrantInDiv(jBody);
DeadAssignmentEliminator.v().transform(jBody);
UnusedLocalEliminator.v().transform(jBody);
DalvikTyper.v().assignType(jBody);
// jBody.validate();
jBody.validateUses();
jBody.validateValueBoxes();
// jBody.checkInit();
// Validate.validateArrays(jBody);
// jBody.checkTypes();
// jBody.checkLocals();
} else {
// t_num.start();
DexNumTransformer.v().transform(jBody);
// t_num.end();
DexReturnValuePropagator.v().transform(jBody);
getCopyPopagator().transform(jBody);
DexNullThrowTransformer.v().transform(jBody);
// t_null.start();
DexNullTransformer.v().transform(jBody);
// t_null.end();
DexIfTransformer.v().transform(jBody);
DeadAssignmentEliminator.v().transform(jBody);
UnusedLocalEliminator.v().transform(jBody);
// DexRefsChecker.v().transform(jBody);
DexNullArrayRefTransformer.v().transform(jBody);
}
if (IDalvikTyper.ENABLE_DVKTYPER) {
for (Local l : jBody.getLocals()) {
l.setType(unknownType);
}
}
// Remove "instanceof" checks on the null constant
DexNullInstanceofTransformer.v().transform(jBody);
TypeAssigner.v().transform(jBody);
final RefType objectType = RefType.v("java.lang.Object");
if (IDalvikTyper.ENABLE_DVKTYPER) {
for (Unit u : jBody.getUnits()) {
if (u instanceof IfStmt) {
ConditionExpr expr = (ConditionExpr) ((IfStmt) u).getCondition();
if (((expr instanceof EqExpr) || (expr instanceof NeExpr))) {
Value op1 = expr.getOp1();
Value op2 = expr.getOp2();
if (op1 instanceof Constant && op2 instanceof Local) {
Local l = (Local) op2;
Type ltype = l.getType();
if (ltype instanceof PrimType)
continue;
if (// by default
!(op1 instanceof IntConstant))
// in Dalvik
continue;
IntConstant icst = (IntConstant) op1;
int val = icst.value;
if (val != 0)
continue;
expr.setOp1(nullConstant);
} else if (op1 instanceof Local && op2 instanceof Constant) {
Local l = (Local) op1;
Type ltype = l.getType();
if (ltype instanceof PrimType)
continue;
if (// by default
!(op2 instanceof IntConstant))
// in Dalvik
continue;
IntConstant icst = (IntConstant) op2;
int val = icst.value;
if (val != 0)
continue;
expr.setOp2(nullConstant);
} else if (op1 instanceof Local && op2 instanceof Local) {
// nothing to do
} else if (op1 instanceof Constant && op2 instanceof Constant) {
if (op1 instanceof NullConstant && op2 instanceof NumericConstant) {
IntConstant nc = (IntConstant) op2;
if (nc.value != 0)
throw new RuntimeException("expected value 0 for int constant. Got " + expr);
expr.setOp2(NullConstant.v());
} else if (op2 instanceof NullConstant && op1 instanceof NumericConstant) {
IntConstant nc = (IntConstant) op1;
if (nc.value != 0)
throw new RuntimeException("expected value 0 for int constant. Got " + expr);
expr.setOp1(nullConstant);
}
} else {
throw new RuntimeException("error: do not handle if: " + u);
}
}
}
}
// For null_type locals: replace their use by NullConstant()
List<ValueBox> uses = jBody.getUseBoxes();
// List<ValueBox> defs = jBody.getDefBoxes();
List<ValueBox> toNullConstantify = new ArrayList<ValueBox>();
List<Local> toRemove = new ArrayList<Local>();
for (Local l : jBody.getLocals()) {
if (l.getType() instanceof NullType) {
toRemove.add(l);
for (ValueBox vb : uses) {
Value v = vb.getValue();
if (v == l)
toNullConstantify.add(vb);
}
}
}
for (ValueBox vb : toNullConstantify) {
System.out.println("replace valuebox '" + vb + " with null constant");
vb.setValue(nullConstant);
}
for (Local l : toRemove) {
System.out.println("removing null_type local " + l);
l.setType(objectType);
}
}
// We pack locals that are not used in overlapping regions. This may
// again lead to unused locals which we have to remove.
LocalPacker.v().transform(jBody);
UnusedLocalEliminator.v().transform(jBody);
LocalNameStandardizer.v().transform(jBody);
// on the fly.
if (options.wrong_staticness() == Options.wrong_staticness_fix) {
FieldStaticnessCorrector.v().transform(jBody);
MethodStaticnessCorrector.v().transform(jBody);
}
// Inline PackManager.v().getPack("jb").apply(jBody);
// Keep only transformations that have not been done
// at this point.
TrapTightener.v().transform(jBody);
TrapMinimizer.v().transform(jBody);
// LocalSplitter.v().transform(jBody);
Aggregator.v().transform(jBody);
// UnusedLocalEliminator.v().transform(jBody);
// TypeAssigner.v().transform(jBody);
// LocalPacker.v().transform(jBody);
// LocalNameStandardizer.v().transform(jBody);
// Remove if (null == null) goto x else <madness>. We can only do this
// after we have run the constant propagation as we might not be able
// to statically decide the conditions earlier.
ConditionalBranchFolder.v().transform(jBody);
// Remove unnecessary typecasts
ConstantCastEliminator.v().transform(jBody);
IdentityCastEliminator.v().transform(jBody);
// Remove unnecessary logic operations
IdentityOperationEliminator.v().transform(jBody);
// We need to run this transformer since the conditional branch folder
// might have rendered some code unreachable (well, it was unreachable
// before as well, but we didn't know).
UnreachableCodeEliminator.v().transform(jBody);
// Not sure whether we need this even though we do it earlier on as
// the earlier pass does not have type information
// CopyPropagator.v().transform(jBody);
// we might have gotten new dead assignments and unused locals through
// copy propagation and unreachable code elimination, so we have to do
// this again
DeadAssignmentEliminator.v().transform(jBody);
UnusedLocalEliminator.v().transform(jBody);
NopEliminator.v().transform(jBody);
// Remove unnecessary chains of return statements
DexReturnPacker.v().transform(jBody);
for (Unit u : jBody.getUnits()) {
if (u instanceof AssignStmt) {
AssignStmt ass = (AssignStmt) u;
if (ass.getRightOp() instanceof CastExpr) {
CastExpr c = (CastExpr) ass.getRightOp();
if (c.getType() instanceof NullType) {
ass.setRightOp(nullConstant);
}
}
}
if (u instanceof DefinitionStmt) {
DefinitionStmt def = (DefinitionStmt) u;
// we must manually fix the hierarchy
if (def.getLeftOp() instanceof Local && def.getRightOp() instanceof CaughtExceptionRef) {
Type t = def.getLeftOp().getType();
if (t instanceof RefType) {
RefType rt = (RefType) t;
if (rt.getSootClass().isPhantom() && !rt.getSootClass().hasSuperclass() && !rt.getSootClass().getName().equals("java.lang.Throwable"))
rt.getSootClass().setSuperclass(Scene.v().getSootClass("java.lang.Throwable"));
}
}
}
}
//
for (Local l : jBody.getLocals()) {
Type t = l.getType();
if (t instanceof NullType) {
l.setType(objectType);
}
}
return jBody;
}
Aggregations