/* Soot - a J*va Optimization Framework
* Copyright (C) 1999 Patrick Lam, 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.
*/
/*
* Modified by the Sable Research Group and others 1997-1999.
* See the 'credits' file distributed with Soot for the complete list of
* contributors. (Soot is distributed at http://www.sable.mcgill.ca/soot)
*/
package soot.jimple.toolkits.invoke;
import soot.options.*;
import soot.*;
import soot.jimple.*;
import soot.jimple.toolkits.callgraph.*;
import soot.tagkit.Host;
import java.util.*;
/** Uses the Scene's currently-active InvokeGraph to inline monomorphic call sites. */
public class StaticInliner extends SceneTransformer
{
public StaticInliner( Singletons.Global g ) {}
public static StaticInliner v() { return G.v().soot_jimple_toolkits_invoke_StaticInliner(); }
protected void internalTransform(String phaseName, Map options)
{
Filter explicitInvokesFilter = new Filter( new ExplicitEdgesPred() );
if(Options.v().verbose())
G.v().out.println("[] Inlining methods...");
boolean enableNullPointerCheckInsertion = PhaseOptions.getBoolean(options, "insert-null-checks");
boolean enableRedundantCastInsertion = PhaseOptions.getBoolean(options, "insert-redundant-casts");
String modifierOptions = PhaseOptions.getString(options, "allowed-modifier-changes");
float expansionFactor = PhaseOptions.getFloat(options, "expansion-factor");
int maxContainerSize = PhaseOptions.getInt(options, "max-container-size");
int maxInlineeSize = PhaseOptions.getInt(options, "max-inlinee-size");
boolean rerunJb = PhaseOptions.getBoolean(options, "rerun-jb");
HashMap instanceToStaticMap = new HashMap();
CallGraph cg = Scene.v().getCallGraph();
Hierarchy hierarchy = Scene.v().getActiveHierarchy();
ArrayList<List<Host>> sitesToInline = new ArrayList<List<Host>>();
computeAverageMethodSizeAndSaveOriginalSizes();
// Visit each potential site in reverse pseudo topological order.
{
TopologicalOrderer orderer = new TopologicalOrderer(cg);
orderer.go();
List<SootMethod> order = orderer.order();
ListIterator<SootMethod> it = order.listIterator(order.size());
while (it.hasPrevious())
{
SootMethod container = it.previous();
if( methodToOriginalSize.get(container) == null ) continue;
if (!container.isConcrete())
continue;
if (!explicitInvokesFilter.wrap( cg.edgesOutOf(container) ).hasNext())
continue;
JimpleBody b = (JimpleBody)container.retrieveActiveBody();
List<Unit> unitList = new ArrayList<Unit>(); unitList.addAll(b.getUnits());
Iterator<Unit> unitIt = unitList.iterator();
while (unitIt.hasNext())
{
Stmt s = (Stmt)unitIt.next();
if (!s.containsInvokeExpr())
continue;
Iterator targets = new Targets(
explicitInvokesFilter.wrap( cg.edgesOutOf(s) ) );
if( !targets.hasNext() ) continue;
SootMethod target = (SootMethod)targets.next();
if( targets.hasNext() ) continue;
if (!target.getDeclaringClass().isApplicationClass() || !target.isConcrete())
continue;
if(!InlinerSafetyManager.ensureInlinability(target, s, container, modifierOptions))
continue;
List<Host> l = new ArrayList<Host>();
l.add(target); l.add(s); l.add(container);
sitesToInline.add(l);
}
}
}
// Proceed to inline the sites, one at a time, keeping track of
// expansion rates.
{
Iterator<List<Host>> sitesIt = sitesToInline.iterator();
while (sitesIt.hasNext())
{
List l = sitesIt.next();
SootMethod inlinee = (SootMethod)l.get(0);
int inlineeSize = ((JimpleBody)(inlinee.retrieveActiveBody())).getUnits().size();
Stmt invokeStmt = (Stmt)l.get(1);
SootMethod container = (SootMethod)l.get(2);
int containerSize = ((JimpleBody)(container.retrieveActiveBody())).getUnits().size();
if (inlineeSize + containerSize > maxContainerSize)
continue;
if (inlineeSize > maxInlineeSize)
continue;
if (inlineeSize + containerSize >
expansionFactor * methodToOriginalSize.get(container).intValue())
continue;
if(InlinerSafetyManager.ensureInlinability(inlinee, invokeStmt, container, modifierOptions))
{
// Not that it is important to check right before inlining if the site is still valid.
SiteInliner.inlineSite(inlinee, invokeStmt, container, options);
if( rerunJb ) {
PackManager.v().getPack("jb").apply(container.getActiveBody());
}
}
}
}
}
private final HashMap<SootMethod, Integer> methodToOriginalSize = new HashMap<SootMethod, Integer>();
private void computeAverageMethodSizeAndSaveOriginalSizes()
{
long sum = 0, count = 0;
Iterator classesIt = Scene.v().getApplicationClasses().iterator();
while (classesIt.hasNext())
{
SootClass c = (SootClass) classesIt.next();
Iterator methodsIt = c.methodIterator();
while (methodsIt.hasNext())
{
SootMethod m = (SootMethod) methodsIt.next();
if (m.isConcrete())
{
int size = ((JimpleBody)m.retrieveActiveBody()).getUnits().size();
sum += size;
methodToOriginalSize.put(m, new Integer(size));
count++;
}
}
}
if (count == 0)
return;
}
}