/* Soot - a J*va Optimization Framework
* Copyright (C) 1997-1999 Raja Vallee-Rai
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
package soot.jbco.bafTransformations;
import java.util.*;
import soot.*;
import soot.baf.*;
import soot.jbco.IJbcoTransform;
import soot.jbco.util.*;
/**
* @author Michael Batchelder
*
* Created on 2-May-2006
*
* This transformer takes a portion of gotos/ifs and moves them into a TRY/CATCH block
*/
public class IndirectIfJumpsToCaughtGotos extends BodyTransformer implements IJbcoTransform {
int count = 0;
public static String dependancies[] = new String[] {"bb.jbco_iii", "bb.jbco_ful", "bb.lp"};
public String[] getDependancies() {
return dependancies;
}
public static String name = "bb.jbco_iii";
public String getName() {
return name;
}
public void outputSummary() {
out.println("Indirected Ifs through Traps: "+count);
}
protected void internalTransform(Body b, String phaseName, Map options) {
int weight = soot.jbco.Main.getWeight(phaseName, b.getMethod().getSignature());
if (weight == 0) return;
PatchingChain units = b.getUnits();
Unit nonTrap = findNonTrappedUnit(units,b.getTraps());
if (nonTrap == null) {
Unit last = null;
nonTrap = Baf.v().newNopInst();
for (Iterator it = units.iterator(); it.hasNext(); ) {
Unit u = (Unit)it.next();
if (u instanceof IdentityInst && ((IdentityInst)u).getLeftOp() instanceof Local) {
last = u;
continue;
} else {
if (last!=null)
units.insertAfter(nonTrap,last);
else
units.addFirst(nonTrap);
break;
}
}
}
Stack<Type> stack = null;
try {
stack = StackTypeHeightCalculator.getAfterStack(b,nonTrap);
} catch (Exception exc) {
out.println(exc);
Debugger.printUnits(b, b.getMethod().getSignature());
System.exit(1);
}
ArrayList<Unit> addedUnits = new ArrayList<Unit>();
Iterator it = units.snapshotIterator();
while (it.hasNext()) {
Unit u = (Unit)it.next();
if (isIf(u) && Rand.getInt(10) <= weight) {
TargetArgInst ifu = (TargetArgInst)u;
Unit newTarg = Baf.v().newGotoInst(ifu.getTarget());
units.add(newTarg);
ifu.setTarget(newTarg);
addedUnits.add(newTarg);
}
}
if (addedUnits.size()<=0) return;
Unit nop = Baf.v().newNopInst();
units.add(nop);
ArrayList<Unit> toinsert = new ArrayList<Unit>();
SootField field = null;
try {
field = soot.jbco.jimpleTransformations.FieldRenamer.getRandomOpaques()[Rand.getInt(2)];
} catch (NullPointerException npe) {}
if (field != null && Rand.getInt(3) > 0) {
toinsert.add(Baf.v().newStaticGetInst(field.makeRef()));
if (field.getType() instanceof IntegerType) {
toinsert.add(Baf.v().newIfGeInst((Unit)units.getSuccOf(nonTrap)));
} else {
SootMethod boolInit = ((RefType)field.getType()).getSootClass().getMethod("boolean booleanValue()");
toinsert.add(Baf.v().newVirtualInvokeInst(boolInit.makeRef()));
toinsert.add(Baf.v().newIfGeInst((Unit)units.getSuccOf(nonTrap)));
}
} else {
toinsert.add(Baf.v().newPushInst(soot.jimple.IntConstant.v(BodyBuilder.getIntegerNine())));
toinsert.add(Baf.v().newPrimitiveCastInst(IntType.v(),ByteType.v()));
toinsert.add(Baf.v().newPushInst(soot.jimple.IntConstant.v(Rand.getInt() % 2 == 0 ? 9 : 3)));
toinsert.add(Baf.v().newRemInst(ByteType.v()));
/*toinsert.add(Baf.v().newDup1Inst(ByteType.v()));
toinsert.add(Baf.v().newPrimitiveCastInst(ByteType.v(),IntType.v()));
toinsert.add(Baf.v().newStaticGetInst(sys.getFieldByName("out").makeRef()));
toinsert.add(Baf.v().newSwapInst(IntType.v(),RefType.v()));
ArrayList parms = new ArrayList();
parms.add(IntType.v());
toinsert.add(Baf.v().newVirtualInvokeInst(out.getMethod("println",parms).makeRef()));
*/
toinsert.add(Baf.v().newIfEqInst((Unit)units.getSuccOf(nonTrap)));
}
ArrayList<Unit> toinserttry = new ArrayList<Unit>();
while (stack.size()>0)
toinserttry.add(Baf.v().newPopInst(stack.pop()));
toinserttry.add(Baf.v().newPushInst(soot.jimple.NullConstant.v()));
Unit handler = Baf.v().newThrowInst();
int rand = Rand.getInt(toinserttry.size());
while (rand++ < toinserttry.size()) {
toinsert.add(toinserttry.get(0));
toinserttry.remove(0);
}
if (toinserttry.size()>0) {
toinserttry.add(Baf.v().newGotoInst(handler));
toinsert.add(Baf.v().newGotoInst(toinserttry.get(0)));
units.insertBefore(toinserttry,nop);
}
toinsert.add(handler);
units.insertAfter(toinsert,nonTrap);
b.getTraps().add(Baf.v().newTrap(ThrowSet.getRandomThrowable(),addedUnits.get(0),nop,handler));
count+=addedUnits.size();
if (addedUnits.size() > 0 && debug) {
StackTypeHeightCalculator.calculateStackHeights(b);
//StackTypeHeightCalculator.printStack(units, StackTypeHeightCalculator.calculateStackHeights(b), false);
}
}
private Unit findNonTrappedUnit(PatchingChain units, soot.util.Chain traps) {
int intrap = 0;
ArrayList<Unit> untrapped = new ArrayList<Unit>();
Iterator it = units.snapshotIterator();
while (it.hasNext()) {
Unit u = (Unit)it.next();
Iterator tit = traps.iterator();
while (tit.hasNext()) {
Trap t = (Trap)tit.next();
if (u == t.getBeginUnit()) intrap++;
if (u == t.getEndUnit()) intrap--;
}
if (intrap == 0) untrapped.add(u);
}
Unit result = null;
if (untrapped.size() > 0) {
int count = 0;
while (result == null && count <10) {
count++;
result = untrapped.get(Rand.getInt(999999) % untrapped.size());
if (!result.fallsThrough() || units.getSuccOf(result) == null || units.getSuccOf(result) instanceof ThrowInst)
result = null;
}
}
return result;
}
private boolean isIf(Unit u) {
// TODO: will a RET statement be a TargetArgInst?
return (u instanceof TargetArgInst) && !(u instanceof GotoInst)
&& !(u instanceof JSRInst);
}
}