/*
* 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.jbco.IJbcoTransform;
import soot.jbco.util.Rand;
import soot.jbco.jimpleTransformations.*;
import soot.jimple.*;
import soot.toolkits.graph.BriefUnitGraph;
import soot.util.Chain;
import soot.baf.*;
public class TryCatchCombiner extends BodyTransformer implements IJbcoTransform {
int totalcount = 0;
int changedcount = 0;
public static String dependancies[] = new String[] {"bb.jbco_j2bl", "bb.jbco_ctbcb", "bb.jbco_ful", "bb.lp" };
public String[] getDependancies() {
return dependancies;
}
public static String name = "bb.jbco_ctbcb";
public String getName() {
return name;
}
public void outputSummary() {
out.println("Total try blocks found: " + totalcount);
out.println("Combined TryCatches: " + changedcount);
}
protected void internalTransform(Body b, String phaseName, Map options) {
int weight = soot.jbco.Main.getWeight(phaseName, b.getMethod().getSignature());
if (weight == 0) return;
int trapCount = 0;
PatchingChain units = b.getUnits();
ArrayList<Unit> headList = new ArrayList<Unit>();
ArrayList<Trap> trapList = new ArrayList<Trap>();
Iterator traps = b.getTraps().iterator();
// build list of heads and corresponding traps
while (traps.hasNext()) {
Trap t = (Trap) traps.next();
totalcount++;
// skip runtime exceptions
if (!isRewritable(t))
continue;
headList.add(t.getBeginUnit());
trapList.add(t);
trapCount++;
}
if (trapCount == 0)
return;
// check if any traps have same head, if so insert dumby NOP to disambiguate
for (int i = 0; i < headList.size(); i++) {
for (int j = 0; j < headList.size(); j++) {
if (i == j)
continue;
if (headList.get(i) == headList.get(j)) {
Trap t = trapList.get(i);
Unit nop = Baf.v().newNopInst();
units.insertBeforeNoRedirect(nop, headList.get(i));
headList.set(i, nop);
t.setBeginUnit(nop);
}
}
}
Unit first = null;
Iterator uit = units.iterator();
while (uit.hasNext()) {
Unit unit = (Unit)uit.next();
if (!(unit instanceof IdentityInst))
break;
first = unit;
}
if (first == null) {
first = Baf.v().newNopInst();
units.insertBefore(first,units.getFirst());
} else {
first = (Unit)units.getSuccOf(first);
}
Chain locs = b.getLocals();
HashMap stackHeightsBefore = null;
HashMap bafToJLocals = soot.jbco.Main.methods2Baf2JLocals.get(b.getMethod());
int varCount = trapCount + 1;
traps = b.getTraps().snapshotIterator();
while (traps.hasNext()) {
Trap t = (Trap) traps.next();
Unit begUnit = t.getBeginUnit();
if (!isRewritable(t) || Rand.getInt(10) > weight)
continue;
stackHeightsBefore = StackTypeHeightCalculator.calculateStackHeights(b,bafToJLocals);
boolean badType = false;
Stack s = (Stack)((Stack)stackHeightsBefore.get(begUnit)).clone();
if (s.size() > 0) {
for (int i = 0; i < s.size(); i++) {
if (s.pop() instanceof StmtAddressType) {
badType = true;
break;
}
}
}
if (badType) continue;
// local to hold control flow flag (0=try, 1=catch)
Local controlLocal = Baf.v().newLocal("controlLocal_tccomb" + trapCount,
IntType.v());
locs.add(controlLocal);
// initialize local to 0=try
Unit pushZero = Baf.v().newPushInst(IntConstant.v(0));
Unit storZero = Baf.v().newStoreInst(IntType.v(), controlLocal);
// this is necessary even though it seems like it shouldn't be
units.insertBeforeNoRedirect((Unit)pushZero.clone(), first);
units.insertBeforeNoRedirect((Unit)storZero.clone(), first);
BriefUnitGraph graph = new BriefUnitGraph(b);
List l = graph.getPredsOf(begUnit);
// add initializer seq for try - sets local to zero and loads null exc
units.add(pushZero);
units.add(storZero);
Stack varsToLoad = new Stack();
s = (Stack)stackHeightsBefore.get(begUnit);
if (s.size() > 0) {
for (int i = 0; i < s.size(); i++) {
Type type = (Type)s.pop();
Local varLocal = Baf.v().newLocal("varLocal_tccomb" + varCount++, type);
locs.add(varLocal);
varsToLoad.push(varLocal);
units.add(Baf.v().newStoreInst(type,varLocal));
units.insertBeforeNoRedirect(FixUndefinedLocals.getPushInitializer(varLocal, type), first);
units.insertBeforeNoRedirect(Baf.v().newStoreInst(type, varLocal), first);
}
}
units.add(Baf.v().newPushInst(NullConstant.v()));
units.add(Baf.v().newGotoInst(begUnit));
// for each pred of the beginUnit of the try, we must insert goto initializer
for (int i = 0; i < l.size(); i++) {
Unit pred = (Unit) l.get(i);
if (isIf(pred)) {
TargetArgInst ifPred = ((TargetArgInst) pred);
if (ifPred.getTarget() == begUnit) {
ifPred.setTarget(pushZero);
}
Unit succ = (Unit) units.getSuccOf(ifPred);
if (succ == begUnit) {
units.insertAfter(Baf.v().newGotoInst(pushZero), ifPred);
}
} else if (pred instanceof GotoInst
&& ((GotoInst) pred).getTarget() == begUnit) {
((GotoInst) pred).setTarget(pushZero);
} else {
units.insertAfter(Baf.v().newGotoInst(pushZero), pred);
}
}
Unit handlerUnit = t.getHandlerUnit();
Unit newBeginUnit = Baf.v().newLoadInst(IntType.v(), controlLocal);
units.insertBefore(newBeginUnit, begUnit);
units.insertBefore(Baf.v().newIfNeInst(handlerUnit), begUnit);
units.insertBefore(Baf.v().newPopInst(RefType.v()), begUnit);
while (varsToLoad.size() > 0) {
Local varLocal = (Local)varsToLoad.pop();
units.insertBefore(Baf.v().newLoadInst(varLocal.getType(),varLocal), begUnit);
}
try {
SootField f[] = FieldRenamer.getRandomOpaques();
if (f[0] != null && f[1] != null) {
loadBooleanValue(units,f[0],begUnit);
loadBooleanValue(units,f[1],begUnit);
units.insertBeforeNoRedirect(Baf.v().newIfCmpEqInst(BooleanType.v(),begUnit),begUnit);
}
} catch (NullPointerException npe){}
// randomize the increment - sometimes store one, sometimes just set to 1
if (Rand.getInt() % 2 == 0) {
units.insertBeforeNoRedirect(Baf.v().newPushInst(IntConstant.v(Rand.getInt(3)+1)), begUnit);
units.insertBeforeNoRedirect(Baf.v().newStoreInst(IntType.v(), controlLocal),
begUnit);
} else {
units.insertBeforeNoRedirect(Baf.v().newIncInst(controlLocal,IntConstant.v(Rand.getInt(3)+1)), begUnit);
}
trapCount--;
t.setBeginUnit(newBeginUnit);
t.setHandlerUnit(newBeginUnit);
changedcount++;
if (debug)
StackTypeHeightCalculator.printStack(units,StackTypeHeightCalculator.calculateStackHeights(b),false);
}
}
private void loadBooleanValue(PatchingChain units, SootField f, Unit insert)
{
units.insertBefore(Baf.v().newStaticGetInst(f.makeRef()),insert);
if (f.getType() instanceof RefType) {
SootMethod boolInit = ((RefType)f.getType()).getSootClass().getMethod("boolean booleanValue()");
units.insertBefore(Baf.v().newVirtualInvokeInst(boolInit.makeRef()),insert);
}
}
private boolean isIf(Unit u) {
// TODO: will a RET statement be a TargetArgInst?
return (u instanceof TargetArgInst) && !(u instanceof GotoInst)
&& !(u instanceof JSRInst);
}
private boolean isRewritable(Trap t) {
// ignore traps that already catch their own begin unit
if (t.getBeginUnit() == t.getHandlerUnit()) return false;
//ignore runtime try blocks - these may have weird side-effects do to asynchronous exceptions
SootClass exc = t.getException();
if (exc.getName().equals("java.lang.Throwable"))
return false;
do {
if (exc.getName().equals("java.lang.RuntimeException"))
return false;
} while (exc.hasSuperclass() && (exc = exc.getSuperclass()) != null);
return true;
}
}