[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
JavaCard support in Soot
Hi,
When jimplifying a JavaCard applet in J2ME mode soot gives the following
message:
java.lang.RuntimeException: tried to get nonexistent method: <java.lang.Class:
java.lang.Object newInstance()> !
at soot.Scene.getMethod(Scene.java:334)
at soot.jimple.toolkits.invoke.MethodCallGraph.isReachable
(MethodCallGraph.java:220)
at soot.jimple.toolkits.invoke.MethodCallGraph.initialize
(MethodCallGraph.java:178)
..........
This is since the class java.lang.Class is excluded from the java.lang package
in JavaCard's specification.
Attached a fix to MethodCallGraph.java to avoid above exception.
Also, it seems that J2ME is not "officially" supported in the sense that there
is no command line argument to specify a J2ME mode.
This is convenient (at least for us) in an environment where J2SE and
J2ME/JavaCard applications are analyzed.
Attached support for the command line argument --j2me in Main.java.
Cheers,
Ran.
/* Soot - a J*va Optimization Framework
* Copyright (C) 2000 Felix Kwok
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library 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)
*/
/* Reference Version: $SootVersion: 1.2.3.dev.4 $ */
package soot.jimple.toolkits.invoke;
import java.util.*;
import soot.*;
import soot.util.*;
import soot.toolkits.graph.*;
import soot.jimple.toolkits.invoke.*;
import soot.jimple.*;
/** A directed graph whose nodes are methods and whose edges are call edges. It also knows
* about default entry points, and more entry points can be added using the <code>addEntryPoint</code>
* method.
*/
public class MethodCallGraph extends MemoryEfficientGraph {
private Set methodContained;
private InvokeGraph ig;
private HashSet reachableMethods;
private LinkedList allStartNodes;
private HashSet entryPoints; // Additional entry points specified by the caller.
/** Sets the underlying invoke graph to <code>ig</code>. Does not trigger
* recomputation or reachable methods.
*/
public void setInvokeGraph(InvokeGraph ig) {
this.ig = ig;
}
/** Constructs a MethodCallGraph based on the invoke graph <code>ig</code>, with
* default entry points.
*/
public MethodCallGraph(InvokeGraph ig) {
methodContained = new HashSet();
this.ig = ig;
reachableMethods = new HashSet();
entryPoints = (ig.mcg == null) ? null : ig.mcg.entryPoints;
Date start = new Date();
initialize();
Date finish = new Date();
long runtime = finish.getTime() - start.getTime();
// System.out.println("Computing reachable methods took "+(runtime/60000)+" min. "+ ((runtime%60000)/1000)+" sec.");
}
/** Constructs a MethodCallGraph based on the invoke graph <code>ig</code>, with
* entry points contained in <code>methods</code>.
*/
public MethodCallGraph(InvokeGraph ig, Collection methods) {
methodContained = new HashSet();
this.ig = ig;
reachableMethods = new HashSet();
entryPoints = new HashSet(methods);
Date start = new Date();
initialize();
Date finish = new Date();
long runtime = finish.getTime() - start.getTime();
// System.out.println("Computing reachable methods took "+(runtime/60000)+" min. "+ ((runtime%60000)/1000)+" sec.");
}
/** Recomputes the call graph, based on the entry points specified by the current set
* of entry points.
*/
public void refresh() {
clearAll();
methodContained = new HashSet();
reachableMethods = new HashSet();
Date start = new Date();
initialize();
Date finish = new Date();
long runtime = finish.getTime() - start.getTime();
// System.out.println("Computing reachable methods took "+(runtime/60000)+" min. "+ ((runtime%60000)/1000)+" sec.");
}
/** Adds an entry point to the current set of entry points. */
public void addEntryPoint(SootMethod m) {
if (entryPoints==null)
entryPoints = new HashSet(0);
entryPoints.add(m);
}
/** Clear the current set of entry points. */
public void clearEntryPoints() {
entryPoints = null;
}
/** Returns the current set of entry points. */
public List getEntryPoints() {
return new LinkedList(entryPoints);
}
/* Sets up entry points and computes reachable methods. */
private void initialize() {
LinkedList st = new LinkedList();
LinkedList inits = new LinkedList();
// If entryPoints == null, we use the default entry points, PLUS all "void <init>()" if newInstance is reachable.
// If entryPoints is non-null, the entry points are the defaults plus "entryPoints" (but not all inits).
// Gather all methods belonging to application or library classes.
HashSet appAndLibClasses = new HashSet();
appAndLibClasses.addAll(Scene.v().getApplicationClasses());
appAndLibClasses.addAll(Scene.v().getLibraryClasses());
HashSet methodSet = new HashSet();
Iterator classesIt = appAndLibClasses.iterator();
while (classesIt.hasNext()) {
SootClass c = (SootClass)classesIt.next();
Iterator methodsIt = c.getMethods().iterator();
while (methodsIt.hasNext()) {
SootMethod m = (SootMethod)methodsIt.next();
String sig = m.getSubSignature();
// Set up default entry points.
if (sig.equals("void main(java.lang.String[])"))
st.addLast(m);
else if (sig.equals("void start()"))
st.addLast(m);
else if (sig.equals("void run()"))
st.addLast(m);
else if (sig.equals("void finalize()"))
st.addLast(m);
else if (sig.equals("void <clinit>()"))
st.addLast(m);
else if (sig.equals("void exit()"))
st.addLast(m);
else if (sig.equals("java.lang.Class loadClass(java.lang.String)"))
st.addLast(m);
else if (sig.equals("void <init>()") && entryPoints==null)
inits.addLast(m);
}
}
// More default entry points.
try {
st.addLast(Scene.v().getMainClass().getMethod("void main(java.lang.String[])"));
st.addLast(Scene.v().getMethod("<java.lang.System: void initializeSystemClass()>"));
st.addLast(Scene.v().getMethod("<java.lang.ThreadGroup: void <init>()>"));
st.addLast(Scene.v().getMethod
("<java.lang.ThreadGroup: void uncaughtException(java.lang.Thread,java.lang.Throwable)>"));
st.addLast(Scene.v().getMethod("<java.lang.System: void loadLibrary(java.lang.String)>"));
}
catch (RuntimeException e) { }
allStartNodes = new LinkedList(st);
if (entryPoints!=null)
allStartNodes.addAll(entryPoints);
for (Iterator it = allStartNodes.iterator(); it.hasNext(); )
addEdges((SootMethod)it.next());
try {
if (entryPoints == null) {
if (isReachable("<java.lang.Class: java.lang.Object newInstance()>"))
for (Iterator it = inits.iterator(); it.hasNext(); )
addEdges((SootMethod)it.next());
}
}
// hack for a JavaCard environment, as java.lang.Class is not included in
// JavaCard's JDK
catch (RuntimeException e) { }
}
/** Returns a list of methods reachable from a method listed in methods. */
public List getMethodsReachableFrom(Collection methods) {
return new LinkedList(getMethodsReachableFrom0(methods));
}
private HashSet getMethodsReachableFrom0(Collection methods) {
LinkedList st = new LinkedList(methods);
HashSet greyNodes = new HashSet();
HashSet retVals = new HashSet();
while (!st.isEmpty()) {
Object o = st.getLast();
if (!containsNode(o)) {
st.removeLast();
continue;
}
if (!greyNodes.contains(o)) {
greyNodes.add(o);
for (Iterator succsIt = ((List)getSuccsOf(o)).iterator(); succsIt.hasNext(); ) {
Object child = succsIt.next();
if (!greyNodes.contains(child))
st.addLast(child);
}
}
else {
retVals.add(o);
st.removeLast();
}
}
return retVals;
}
/** Returns true if the method specified in <code>signature</code> is reachable. */
public boolean isReachable(String signature) {
return containsNode(Scene.v().getMethod(signature));
}
/** Returns true if the method <code>m</code> is reachable. */
public boolean isReachable(SootMethod m) {
return containsNode(m);
}
/** Returns a list of reachable methods. */
public List getReachableMethods() {
return getNodes();
}
// A non-recursive implementation of addEdges (for speed)
// John pointed out to replace getNodes.contains() by containsNode.
private void addEdges(SootMethod meth) {
if (methodContained.contains(meth))
return;
methodContained.add(meth);
if (!containsNode(meth))
addNode(meth);
LinkedList st = new LinkedList();
st.addLast(meth);
while (!st.isEmpty()) {
SootMethod m = (SootMethod)st.removeLast();
for (Iterator targetsIt = ig.getTargetsOf(m).iterator(); targetsIt.hasNext(); ) {
SootMethod target = (SootMethod)targetsIt.next();
if (!containsNode(target))
addNode(target);
addEdge(m, target);
if (methodContained.contains(target))
continue;
methodContained.add(target);
st.addLast(target);
}
}
}
}
/* 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 Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library 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)
*/
/* Reference Version: $SootVersion: 1.2.3.dev.4 $ */
package soot;
import soot.util.*;
import soot.gui.*;
import java.util.*;
import soot.jimple.*;
import soot.grimp.*;
import soot.baf.*;
import soot.jimple.toolkits.invoke.*;
import soot.baf.toolkits.base.*;
import soot.toolkits.scalar.*;
import soot.dava.*;
import soot.jimple.toolkits.annotation.arraycheck.*;
import soot.jimple.toolkits.annotation.nullcheck.*;
import soot.jimple.toolkits.annotation.profiling.*;
import soot.jimple.toolkits.annotation.tags.*;
import soot.jimple.toolkits.invoke.*;
import soot.tagkit.*;
import soot.dava.toolkits.base.misc.*;
import java.io.*;
import java.text.*;
import gnu.getopt.*;
/** Main class for Soot; provides Soot's command-line user interface. */
public class Main implements Runnable
{
public Date start;
public Date finish;
//------> this used to be in Main
// DEBUG
static boolean isAnalyzingLibraries = false;
public static boolean keepLineNumberAttribute = false;
private static List compilationListeners = new ArrayList(1);
public static void addCompilationListener(ICompilationListener l)
{
compilationListeners.add(l);
}
public static final int COMPILATION_ABORTED = 0;
public static final int COMPILATION_SUCCEDED = 1;
static List dynamicPackages = new ArrayList();
// The following lists are paired. false is exclude in the first list.
static List packageInclusionFlags = new ArrayList();
static List packageInclusionMasks = new ArrayList();
static List dynamicClasses = new ArrayList();
static List processClasses = new ArrayList();
static Chain cmdLineClasses = new HashChain();
// <-------------
// for POINTS-TO analysis
public static final int NO_OUTPUT = -1;
public static final int BAF = 0;
public static final int B = 1;
public static final int JIMPLE = 2;
public static final int JIMP = 3;
public static final int NJIMPLE = 4;
public static final int GRIMP = 5;
public static final int GRIMPLE = 6;
public static final int CLASS = 7;
public static final int DAVA = 8;
public static final int JASMIN = 9;
public static final int XML = 10;
public static String getExtensionFor(int rep)
{
String str = null;
switch(rep) {
case BAF:
str = ".baf";
break;
case B:
str = ".b";
break;
case JIMPLE:
str = ".jimple";
break;
case JIMP:
str = ".jimp";
break;
case NJIMPLE:
str = ".njimple";
break;
case GRIMP:
str = ".grimp";
break;
case GRIMPLE:
str = ".grimple";
break;
case CLASS:
str = ".class";
break;
case DAVA:
str = ".java";
break;
case JASMIN:
str = ".jasmin";
break;
case XML:
str = ".xml";
break;
default:
throw new RuntimeException();
}
return str;
}
public static String getFileNameFor( SootClass c, int rep)
{
// add an option for no output
if (rep == NO_OUTPUT) return null;
StringBuffer b = new StringBuffer();
if (outputDir != null)
b.append( outputDir);
if ((b.length() > 0) && (b.charAt( b.length() - 1) != fileSeparator))
b.append( fileSeparator);
if (rep != DAVA) {
b.append( c.getName());
b.append( getExtensionFor( rep));
return b.toString();
}
b.append( "dava");
b.append( fileSeparator);
{
String classPath = b.toString() + "classes";
File dir = new File( classPath);
if (!dir.exists())
try {
dir.mkdirs();
}
catch( SecurityException se) {
System.err.println( "Unable to create " + classPath);
System.exit(0);
}
}
b.append( "src");
b.append( fileSeparator);
String fixedPackageName = c.getJavaPackageName();
if (fixedPackageName.equals( "") == false) {
b.append( fixedPackageName.replace( '.', fileSeparator));
b.append( fileSeparator);
}
{
String path = b.toString();
File dir = new File( path);
if (!dir.exists())
try {
dir.mkdirs();
}
catch( SecurityException se) {
System.err.println( "Unable to create " + path);
System.exit(0);
}
}
b.append( c.getShortJavaStyleName());
b.append( ".java");
return b.toString();
}
private static char fileSeparator = System.getProperty("file.separator").charAt(0);
//FD: Unused variables.
// static boolean naiveJimplification;
// static boolean onlyJimpleOutput;
// static boolean onlyJasminOutput;
// static public boolean oldTyping;
// static public boolean usePackedLive;
// static public boolean usePackedDefs = true;
// static boolean isTestingPerformance;
public static boolean isVerbose;
public static boolean isProfilingOptimization;
public static boolean isInDebugMode;
static boolean isSubtractingGC;
static private int targetExtension = CLASS;
static private boolean withCache = false;
static private String cacheDir = null;
static private boolean useJavaStyle = false;
static public int totalFlowNodes, totalFlowComputations;
static boolean doArrayBoundsCheck = false;
static boolean doNullPointerCheck = false;
static public Timer copiesTimer = new Timer("copies"),
defsTimer = new Timer("defs"),
usesTimer = new Timer("uses"),
liveTimer = new Timer("live"),
splitTimer = new Timer("split"),
packTimer = new Timer("pack"),
cleanup1Timer = new Timer("cleanup1"),
cleanup2Timer = new Timer("cleanup2"),
conversionTimer = new Timer("conversion"),
cleanupAlgorithmTimer = new Timer("cleanupAlgorithm"),
graphTimer = new Timer("graphTimer"),
assignTimer = new Timer("assignTimer"),
resolveTimer = new Timer("resolveTimer"),
totalTimer = new Timer("totalTimer"),
splitPhase1Timer = new Timer("splitPhase1"),
splitPhase2Timer = new Timer("splitPhase2"),
usePhase1Timer = new Timer("usePhase1"),
usePhase2Timer = new Timer("usePhase2"),
usePhase3Timer = new Timer("usePhase3"),
defsSetupTimer = new Timer("defsSetup"),
defsAnalysisTimer = new Timer("defsAnalysis"),
defsPostTimer = new Timer("defsPost"),
liveSetupTimer = new Timer("liveSetup"),
liveAnalysisTimer = new Timer("liveAnalysis"),
livePostTimer = new Timer("livePost"),
aggregationTimer = new Timer("aggregation"),
grimpAggregationTimer = new Timer("grimpAggregation"),
deadCodeTimer = new Timer("deadCode"),
propagatorTimer = new Timer("propagator"),
buildJasminTimer = new Timer("buildjasmin"),
assembleJasminTimer = new Timer("assembling jasmin");
static public Timer
resolverTimer = new Timer("resolver");
static public int conversionLocalCount,
cleanup1LocalCount,
splitLocalCount,
assignLocalCount,
packLocalCount,
cleanup2LocalCount;
static public int conversionStmtCount,
cleanup1StmtCount,
splitStmtCount,
assignStmtCount,
packStmtCount,
cleanup2StmtCount;
static private String outputDir = "";
static private boolean isOptimizing;
static private boolean isOptimizingWhole;
static private boolean isUsingVTA;
static private boolean isUsingRTA;
static private boolean isApplication = false;
// hack for J2ME, patch provided by Stephen Chen
// by default, this is set as false, to use SOOT with J2ME library
// flag isJ2ME true. Type system works around Clonable, Serializeable.
// see changes in:
// soot/jimple/toolkits/typing/ClassHierarchy.java
// soot/jimple/toolkits/typing/TypeResolver.java
// soot/jimple/toolkits/typing/TypeVariable.java
// soot/jimple/toolkits/typing/TypeNode.java
static private boolean isJ2ME = false;
// In application mode, we can choose lazy invocation mode
// and also choose no output, this is only used for
// our point-to analysis right now.
static private boolean isLazyInvocation = false;
static private SootClass mainClass = null;
static public long stmtCount;
static int finalRep = BAF;
// The final rep to be used is Baf; conclusion of our CC2000 paper!
private static List sTagFileList = new ArrayList();
private static List getClassesUnder(String aPath)
{
File file = new File(aPath);
List fileNames = new ArrayList();
File[] files = file.listFiles();
if (files == null)
{
files = new File[1]; files[0] = file;
}
for(int i = 0; i < files.length; i++) {
if(files[i].isDirectory()) {
List l = getClassesUnder( aPath + File.separator + files[i].getName());
Iterator it = l.iterator();
while(it.hasNext()) {
String s = (String) it.next();
fileNames.add(files[i].getName() + "." + s);
}
} else {
String fileName = files[i].getName();
if (fileName.endsWith(".class"))
{
int index = fileName.lastIndexOf(".class");
fileNames.add(fileName.substring(0, index));
}
if (fileName.endsWith(".jimple"))
{
int index = fileName.lastIndexOf(".jimple");
fileNames.add(fileName.substring(0, index));
}
}
}
return fileNames;
}
public static void setTargetRep(int rep)
{
targetExtension = rep;
}
public static int getTargetRep()
{
return targetExtension;
}
public static void setOptimizing(boolean val)
{
isOptimizing = val;
}
public static boolean isOptimizing()
{
return isOptimizing;
}
public static void setOptimizingWhole(boolean val)
throws CompilationDeathException
{
if (!isApplication && val){
throw new CompilationDeathException(COMPILATION_ABORTED, "Can only whole-program optimize in application mode!");
}
isOptimizingWhole = val;
isOptimizing = val;
}
public static boolean isOptimizingWhole()
{
return isOptimizingWhole;
}
public static void setProfiling(boolean val)
{
isProfilingOptimization = val;
}
public static boolean isProfiling()
{
return isProfilingOptimization;
}
public static void setVerbose(boolean val)
{
isVerbose = val;
}
public static boolean isVerbose()
{
return isVerbose;
}
public static void setWithCache(boolean val)
{
withCache = val;
}
public static boolean getWithCache()
{
return withCache;
}
public static void setCacheDir( String s)
{
cacheDir = s;
setWithCache( true);
}
public static String getCacheDir()
{
return cacheDir;
}
public static void setAppMode(boolean val)
{
isApplication = val;
}
public static boolean isAppMode()
{
return isApplication;
}
public static void setJ2ME(boolean val)
{
isJ2ME = val;
}
/* hack for J2ME */
public static boolean isJ2ME(){
return isJ2ME;
}
/* for POINTs-TO analysis */
public static void setLazyInvocation(boolean val) {
isLazyInvocation = val;
if (val) {
targetExtension = NO_OUTPUT;
}
}
public static void setJavaStyle( boolean val)
{
useJavaStyle = val;
}
public static boolean getJavaStyle()
{
return useJavaStyle;
}
public static void addExclude(String str)
throws CompilationDeathException
{
if (!isApplication) {
throw new CompilationDeathException(COMPILATION_ABORTED, "Exclude flag only valid in application mode!");
}
packageInclusionFlags.add(new Boolean(false));
packageInclusionMasks.add(str);
}
public static void addInclude(String str)
throws CompilationDeathException
{
if (!isApplication) {
throw new CompilationDeathException(COMPILATION_ABORTED, "Include flag only valid in application mode!");
}
packageInclusionFlags.add(new Boolean(true));
packageInclusionMasks.add(str);
}
public static void addDynamicPath(String path)
throws CompilationDeathException
{
if (!isApplication)
{
throw new CompilationDeathException(COMPILATION_ABORTED, "Dynamic-path flag only valid in application mode!");
}
StringTokenizer tokenizer = new StringTokenizer(path, ":");
while(tokenizer.hasMoreTokens())
dynamicClasses.addAll(getClassesUnder(tokenizer.nextToken()));
}
public static void addDynamicPackage(String str)
throws CompilationDeathException
{
if (!isApplication) {
throw new CompilationDeathException(COMPILATION_ABORTED, "Dynamic-package flag only valid in application mode!");
}
StringTokenizer tokenizer = new StringTokenizer(str, ",");
while(tokenizer.hasMoreTokens())
dynamicPackages.add(tokenizer.nextToken());
}
/* This is called after sootClassPath has been defined. */
public static void markPackageAsDynamic(String str)
{
StringTokenizer strtok = new StringTokenizer(Scene.v().getSootClassPath(), ":");
while(strtok.hasMoreTokens()) {
HashSet set = new HashSet(0);
String path = strtok.nextToken();
// For jimple files
List l = getClassesUnder(path);
for (Iterator it = l.iterator(); it.hasNext(); ) {
String filename = (String)it.next();
if (filename.startsWith(str))
set.add(filename);
}
// For class files;
path = path + "/";
StringTokenizer tokenizer = new StringTokenizer(str, ".");
while(tokenizer.hasMoreTokens()) {
path = path + tokenizer.nextToken();
if (tokenizer.hasMoreTokens())
path = path + "/";
}
l = getClassesUnder(path);
for (Iterator it = l.iterator(); it.hasNext(); )
set.add(str+"."+((String)it.next()));
dynamicClasses.addAll(set);
}
}
public static void addProcessPath(String path)
throws CompilationDeathException
{
if (isApplication)
{
throw new CompilationDeathException(COMPILATION_ABORTED, "Process-path flag only valid in single-file mode!");
}
StringTokenizer tokenizer = new StringTokenizer(path, ":");
while(tokenizer.hasMoreTokens())
processClasses.addAll(getClassesUnder(tokenizer.nextToken()));
}
public static void setDebug(boolean val)
{
isInDebugMode = val;
}
public static boolean isDebug()
{
return isInDebugMode;
}
public static void setOutputDir(String dir)
{
outputDir = dir;
}
public static String getOutputDir()
{
return outputDir;
}
public static void setSrcPrecedence(String prec)
throws CompilationDeathException
{
if(prec.equals("jimple"))
SourceLocator.setSrcPrecedence(SourceLocator.PRECEDENCE_JIMPLE);
else if(prec.equals("class"))
SourceLocator.setSrcPrecedence(SourceLocator.PRECEDENCE_CLASS);
else {
throw new CompilationDeathException(COMPILATION_ABORTED,
"Illegal --src-prec arg: "
+ prec + ". Valid args are:"
+ " \"jimple\" or \"class\"");
}
}
public static void setFinalRep(String rep)
throws CompilationDeathException
{
if(rep.equals("jimple"))
finalRep = JIMPLE;
else if(rep.equals("grimp"))
finalRep = GRIMP;
else if(rep.equals("baf"))
finalRep = BAF;
else {
throw new CompilationDeathException(COMPILATION_ABORTED,
"Illegal argument \"" + rep + "\" for final-rep option"
+ "\nvalid args are: [baf|grimp|jimple]" );
}
}
public static int getFinalRep()
{
return finalRep;
}
public static void setAnalyzingLibraries(boolean val)
{
isAnalyzingLibraries = val;
}
public static boolean isAnalyzingLibraries()
{
return isAnalyzingLibraries;
}
public static void setSubstractingGC(boolean val)
{
isSubtractingGC = val;
}
public static boolean isSubstractingGC()
{
return isSubtractingGC;
}
public static void setAnnotationPhases(String opt)
{
if (opt.equals("both"))
{
doNullPointerCheck = true;
doArrayBoundsCheck = true;
}
else if (opt.equals("arraybounds"))
{
doArrayBoundsCheck = true;
}
else if (opt.equals("nullpointer"))
{
doNullPointerCheck = true;
}
else if (opt.equals("LineNumber"))
{
soot.Main.keepLineNumberAttribute = true;
CodeAttributeGenerator.v().registerAggregator(new LineNumberTagAggregator(true));
}
else
System.out.println("Annotation phase \"" + opt + "\" is not valid.");
// put null pointer check before bounds check for profiling purpose
if (doNullPointerCheck)
{
Scene.v().getPack("jtp").add(new Transform("jtp.npc", NullPointerChecker.v()));
}
if (doArrayBoundsCheck)
{
Scene.v().getPack("wjtp2").add(new Transform("wjtp2.ra", RectangularArrayFinder.v()));
Scene.v().getPack("jtp").add(new Transform("jtp.abc", ArrayBoundsChecker.v()));
}
if (doNullPointerCheck || doArrayBoundsCheck) {
Scene.v().getPack("jtp").add(new Transform("jtp.profiling", ProfilingGenerator.v()));
// turn on the tag aggregator
CodeAttributeGenerator.v().registerAggregator(new ArrayNullTagAggregator(true));
}
}
private static void printVersion()
{
// $Format: " System.out.println(\"Soot version 1.2.3 (build $ProjectVersion$)\");"$
System.out.println("Soot version 1.2.3 (build 1.2.3.dev.4)");
System.out.println("Copyright (C) 1997-2003 Raja Vallee-Rai (rvalleerai@sable.mcgill.ca).");
System.out.println("All rights reserved.");
System.out.println("");
System.out.println("Contributions are copyright (C) 1997-2003 by their respective contributors.");
System.out.println("See individual source files for details.");
System.out.println("");
System.out.println("Soot comes with ABSOLUTELY NO WARRANTY. Soot is free software,");
System.out.println("and you are welcome to redistribute it under certain conditions.");
System.out.println("See the accompanying file 'license.html' for details.");
System.out.println();
System.out.println("Visit the Soot website:");
System.out.println(" http://www.sable.mcgill.ca/soot/");
}
private static void printHelp()
{
System.out.println("Syntax:");
System.out.println(" (single-file mode) soot [option]* classname ... ");
System.out.println(" (application mode) soot --app [option]* mainClassName");
System.out.println("");
System.out.println("General options:");
System.out.println(" --version output version information and exit");
System.out.println(" -h, --help display this help and exit");
System.out.println("");
System.out.println("Output options:");
System.out.println(" -b, --b produce .b (abbreviated .baf) files");
System.out.println(" -B, --baf produce .baf code");
System.out.println(" -j, --jimp produce .jimp (abbreviated .jimple) files");
System.out.println(" -J, --jimple produce .jimple code");
System.out.println(" -g, --grimp produce .grimp (abbreviated .grimple) files");
System.out.println(" -G, --grimple produce .grimple files");
System.out.println(" -s, --jasmin produce .jasmin files");
System.out.println(" -c, --class produce .class files");
System.out.println(" -X, --xml produce .xml files");
System.out.println(" -d PATH store produced files in PATH");
System.out.println("");
System.out.println("Application mode options:");
System.out.println(" -x, --exclude PACKAGE marks classfiles in PACKAGE (e.g. java.)");
System.out.println(" as context classes");
System.out.println(" -i, --include PACKAGE marks classfiles in PACKAGE (e.g. java.util.)");
System.out.println(" as application classes");
System.out.println(" -a, --analyze-context label context classes as library");
System.out.println(" --dynamic-path PATH marks all class files in PATH as ");
System.out.println(" potentially dynamic classes");
System.out.println(" --dynamic-packages PACKAGES marks classfiles in PACKAGES (separated by");
System.out.println(" commas) as potentially dynamic classes");
System.out.println("");
System.out.println("Single-file mode options:");
System.out.println(" --process-path PATH process all classes on the PATH");
System.out.println("");
System.out.println("Construction options:");
System.out.println(" --final-rep REP produce classfile/jasmin from REP ");
System.out.println(" (jimple, grimp, or baf)");
System.out.println("");
System.out.println("Optimization options:");
System.out.println(" -O --optimize perform scalar optimizations on the classfiles");
System.out.println(" -W --whole-optimize perform whole program optimizations on the ");
System.out.println(" classfiles");
System.out.println("");
System.out.println("Miscellaneous options:");
System.out.println(" --soot-classpath PATH uses PATH as the classpath for finding classes");
System.out.println(" --src-prec [jimple|class] sets the source precedence for Soot");
System.out.println(" -t, --time print out time statistics about tranformations");
System.out.println(" --subtract-gc attempt to subtract the gc from the time stats");
System.out.println(" --j2me J2ME mode");
System.out.println(" -v, --verbose verbose mode");
System.out.println(" --debug avoid catching exceptions");
System.out.println(" -p, --phase-option PHASE-NAME KEY1[:VALUE1],KEY2[:VALUE2],...,KEYn[:VALUEn]");
System.out.println(" set run-time option KEY to VALUE for PHASE-NAME");
System.out.println(" (default for VALUE is true)");
System.out.println(" -A --annotation [both|nullpointer|arraybounds]");
System.out.println(" turn on the annotation for null pointer and/or ");
System.out.println(" array bounds check. ");
System.out.println(" more options are in the document. ");
System.out.println(" -A LineNumber keep line number tables.");
System.out.println("");
System.out.println("Examples:");
System.out.println("");
System.out.println(" soot --app -d newClasses Simulator");
System.out.println(" Transforms all classes starting with Simulator, ");
System.out.println(" and stores them in newClasses. ");
}
private static void processCmdLine(String[] args)
throws CompilationDeathException
{
// check --new-cmdline-parser option
for(int i = 0; i < args.length; i++) {
String arg = args[i];
if(arg.equals("--use-CommandLine")) {
processCmdLine_CommandLine(args);
return;
}
if(arg.equals("--use-Getopt")) {
processCmdLine_Getopt(args);
return;
}
}
processCmdLine_classic(args);
}
private static void processCmdLine_CommandLine(String[] args)
throws CompilationDeathException
{
if(args.length == 0) {
printHelp();
throw new CompilationDeathException(COMPILATION_ABORTED, "don't know what to do!");
}
CommandLine cl = new CommandLine(args);
// handle --app option first
while (cl.contains("app")) {
setAppMode(true);
}
// Handle all the options
while (cl.contains("j") || cl.contains("jimp"))
setTargetRep(JIMP);
while (cl.contains("njimple"))
setTargetRep(NJIMPLE);
while (cl.contains("s") || cl.contains("jasmin"))
setTargetRep(JASMIN);
while (cl.contains("J") || cl.contains("jimple"))
setTargetRep(JIMPLE);
while (cl.contains("B") || cl.contains("baf"))
setTargetRep(BAF);
while (cl.contains("b"))
setTargetRep(B);
while (cl.contains("g") || cl.contains("grimp"))
setTargetRep(GRIMP);
while (cl.contains("G") || cl.contains("grimple"))
setTargetRep(GRIMPLE);
while (cl.contains("c") || cl.contains("class"))
setTargetRep(CLASS);
while (cl.contains("dava")) {
Scene.v().setJimpleStmtPrinter( soot.dava.DavaStmtPrinter.v());
Scene.v().setLocalPrinter( soot.dava.DavaLocalPrinter.v());
setTargetRep(DAVA);
}
while (cl.contains("X") || cl.contains("xml")) {
Scene.v().setJimpleStmtPrinter( soot.jimple.XMLStmtPrinter.v());
setTargetRep(XML);
}
while (cl.contains("O") || cl.contains("optimize"))
setOptimizing(true);
while (cl.contains("W") || cl.contains("whole-optimize"))
setOptimizingWhole(true);
while (cl.contains("t") || cl.contains("time"))
setProfiling(true);
while (cl.contains("subtract-gc"))
setSubstractingGC(true);
while (cl.contains( "with-cache"))
setWithCache( true);
while (cl.contains( "k") || cl.contains( "cache-dir")) {
String s = cl.getValue();
if (s.equals( "")) {
System.err.println( "Warning: -k option without argument");
System.err.println( " Using default cache directory");
setWithCache( true);
}
else
setCacheDir( s);
}
while (cl.contains("J2ME"))
setJ2ME(true);
while (cl.contains("v") || cl.contains("verbose"))
setVerbose(true);
while (cl.contains("soot-class-path")
|| cl.contains("soot-classpath")) {
Scene.v().setSootClassPath(cl.getValue());
}
while (cl.contains("d")) {
String s = cl.getValueOf("d");
if (s.equals("")) {
System.err.println ("Warning: -d option used without argument");
System.err.println (" Using default output directory");
}
outputDir = s;
}
while (cl.contains("x") || cl.contains("exclude")) {
String s = cl.getValue();
if (s.equals("")) {
System.err.println ("Warning: exclude-package option used without argument");
} else {
addExclude(s);
}
}
while (cl.contains("i") || cl.contains("include")) {
String s = cl.getValue();
if (s.equals("")) {
System.err.println ("Warning: include-package option used without argument");
} else {
addInclude(s);
}
}
while (cl.contains("a") || cl.contains("analyze-context")) {
setAnalyzingLibraries(true);
}
while (cl.contains("final-rep")) {
String s = cl.getValueOf("final-rep");
if (s.equals("")) {
throw new CompilationDeathException(COMPILATION_ABORTED,
"final-rep requires an argument\n"
+ "valid args are: [baf|grimp|jimple]");
} else {
setFinalRep(s);
}
}
while (cl.contains("p") || cl.contains("phase-option")) {
String s = cl.getValue();
if (s.equals("")) {
System.err.println ("Warning: phase-option option used without argument");
} else {
processPhaseOptions(s);
}
}
while (cl.contains("debug"))
setDebug(true);
while (cl.contains("dynamic-path")) {
addDynamicPath(cl.getValueOf("dynamic-path"));
}
while (cl.contains("dynamic-packages")) {
addDynamicPackage(cl.getValueOf("dynamic-packages"));
}
while (cl.contains("process-path")) {
addProcessPath(cl.getValueOf("process-path"));
}
while (cl.contains("src-prec")) {
setSrcPrecedence(cl.getValueOf("src-prec"));
}
while (cl.contains("tag-file")) {
sTagFileList.add(cl.getValueOf("tag-file"));
}
while (cl.contains("A") || cl.contains("annotation")) {
setAnnotationPhases(cl.getValue());
}
while (cl.contains("version")) {
printVersion();
throw new CompilationDeathException(COMPILATION_SUCCEDED);
}
while (cl.contains("h") || cl.contains("help")) {
printHelp();
throw new CompilationDeathException(COMPILATION_SUCCEDED);
}
while (cl.contains("use-Getopt") || cl.contains("use-CommandLine")
|| cl.contains("classic")) {
// already handled: ignore
}
cl.completeOptionsCheck();
Iterator argIt = cl.getNonOptionArguments().iterator();
while (argIt.hasNext())
cmdLineClasses.add((String)argIt.next());
postCmdLineCheck();
}
private static void processCmdLine_Getopt(String[] args)
throws CompilationDeathException
{
if(args.length == 0)
{
printHelp();
throw new CompilationDeathException(COMPILATION_ABORTED, "don't know what to do!");
}
// handle --app option first
for(int i = 0; i < args.length; i++) {
String arg = args[i];
if(arg.equals("--app")) {
setAppMode(true);
}
}
// Initialize the options for getOpt
addGetoptOption('j', "jimp", LongOpt.NO_ARGUMENT);
addGetoptOption(-10, "njimple", LongOpt.NO_ARGUMENT);
addGetoptOption('s', "jasmin", LongOpt.NO_ARGUMENT);
addGetoptOption('J', "jimple", LongOpt.NO_ARGUMENT);
addGetoptOption('B', "baf", LongOpt.NO_ARGUMENT);
addGetoptOption('b', "b", LongOpt.NO_ARGUMENT);
addGetoptOption('g', "grimp", LongOpt.NO_ARGUMENT);
addGetoptOption('G', "grimple", LongOpt.NO_ARGUMENT);
addGetoptOption('c', "class", LongOpt.NO_ARGUMENT);
addGetoptOption(-11, "dava", LongOpt.NO_ARGUMENT);
addGetoptOption('X', "xml", LongOpt.NO_ARGUMENT);
addGetoptOption('O', "optimize", LongOpt.NO_ARGUMENT);
addGetoptOption('W', "whole-optimize", LongOpt.NO_ARGUMENT);
addGetoptOption('t', "time", LongOpt.NO_ARGUMENT);
addGetoptOption(-12, "substract-gc", LongOpt.NO_ARGUMENT);
addGetoptOption(-9, "j2me", LongOpt.NO_ARGUMENT);
addGetoptOption('v', "verbose", LongOpt.NO_ARGUMENT);
addGetoptOption(-13, "soot-class-path", LongOpt.REQUIRED_ARGUMENT);
addGetoptOption(-13, "soot-classpath", LongOpt.REQUIRED_ARGUMENT);
addGetoptOption('d', "output-dir", LongOpt.REQUIRED_ARGUMENT);
addGetoptOption('x', "exclude", LongOpt.REQUIRED_ARGUMENT);
addGetoptOption('i', "include", LongOpt.REQUIRED_ARGUMENT);
addGetoptOption('a', "analyze-context", LongOpt.NO_ARGUMENT);
addGetoptOption(-14, "final-rep", LongOpt.REQUIRED_ARGUMENT);
addGetoptOption('p', "phase-option", LongOpt.REQUIRED_ARGUMENT);
addGetoptOption(-15, "debug", LongOpt.NO_ARGUMENT);
addGetoptOption(-16, "dynamic-path", LongOpt.REQUIRED_ARGUMENT);
addGetoptOption(-17, "dynamic-packages", LongOpt.REQUIRED_ARGUMENT);
addGetoptOption(-18, "process-path", LongOpt.REQUIRED_ARGUMENT);
addGetoptOption(-19, "src-prec", LongOpt.REQUIRED_ARGUMENT);
addGetoptOption(-20, "tag-file", LongOpt.REQUIRED_ARGUMENT);
addGetoptOption('A', "annotation", LongOpt.REQUIRED_ARGUMENT);
addGetoptOption(-21, "version", LongOpt.NO_ARGUMENT);
addGetoptOption('h', "help", LongOpt.NO_ARGUMENT);
addGetoptOption(-22, "with-cache", LongOpt.NO_ARGUMENT);
addGetoptOption('k', "cache-dir", LongOpt.REQUIRED_ARGUMENT);
// options handled elsewhere
addGetoptOption('-', "use-Getopt", LongOpt.NO_ARGUMENT);
addGetoptOption('-', "use-CommandLine", LongOpt.NO_ARGUMENT);
addGetoptOption('-', "classic", LongOpt.NO_ARGUMENT);
// initialize getopt
Object[] objArray = longOpts.toArray();
LongOpt[] longOptsArray = new LongOpt[objArray.length];
for (int i=0; i<objArray.length; i++) {
longOptsArray[i] = (LongOpt)objArray[i];
}
// I sometimes have ClassCastException with the following line
// so I use the ugly thing above, which seems to work
// LongOpt[] longOptsArray = (LongOpt[])longOpts.toArray();
Getopt g = new Getopt("Soot", args, shortOpts, longOptsArray);
g.setOpterr(false); // We'll do our own error handling
// Handle all the options
int c;
while ((c = g.getopt()) != -1) {
switch (c) {
case 0:
// should never reached that point, due to the way
// the LongOption are created
System.err.println("000000000000");
break;
case 1:
// non-option argument
// behaviour triggered by the - at the beginning of shortOpts
cmdLineClasses.add(g.getOptarg());
break;
case 'j':
setTargetRep(JIMP);
break;
case -10:
setTargetRep(NJIMPLE);
break;
case 's':
setTargetRep(JASMIN);
break;
case 'J':
setTargetRep(JIMPLE);
break;
case 'B':
setTargetRep(BAF);
break;
case 'b':
setTargetRep(B);
break;
case 'g':
setTargetRep(GRIMP);
break;
case 'G':
setTargetRep(GRIMPLE);
break;
case 'c':
setTargetRep(CLASS);
break;
case -11:
Scene.v().setJimpleStmtPrinter( soot.dava.DavaStmtPrinter.v());
Scene.v().setLocalPrinter( soot.dava.DavaLocalPrinter.v());
setTargetRep(DAVA);
break;
case 'X':
Scene.v().setJimpleStmtPrinter( soot.jimple.XMLStmtPrinter.v());
setTargetRep(XML);
break;
case 'O':
setOptimizing(true);
break;
case 'W':
setOptimizingWhole(true);
break;
case 't':
setProfiling(true);
break;
case -12:
setSubstractingGC(true);
break;
case -9:
setJ2ME(true);
break;
case 'v':
setVerbose(true);
break;
case -13:
Scene.v().setSootClassPath(g.getOptarg());
break;
case 'd':
outputDir = g.getOptarg();
break;
case 'x':
addExclude(g.getOptarg());
break;
case 'i':
addInclude(g.getOptarg());
break;
case 'a':
setAnalyzingLibraries(true);
break;
case -14:
setFinalRep(g.getOptarg());
break;
case 'p':
processPhaseOptions(g.getOptarg());
break;
case -15:
setDebug(true);
break;
case -16:
addDynamicPath(g.getOptarg());
break;
case -17:
addDynamicPackage(g.getOptarg());
break;
case -18:
addProcessPath(g.getOptarg());
break;
case -19:
setSrcPrecedence(g.getOptarg());
break;
case -20:
sTagFileList.add(g.getOptarg());
case 'A':
setAnnotationPhases(g.getOptarg());
break;
case -21:
printVersion();
throw new CompilationDeathException(COMPILATION_SUCCEDED);
case -22:
setWithCache( true);
break;
case 'k':
setCacheDir( g.getOptarg());
break;
case 'h':
printHelp();
throw new CompilationDeathException(COMPILATION_SUCCEDED);
case ':':
// option that needs an argument and did not get one
// this behaviour is triggered by the : in shortOpts[1]
String optName;
// This technic is the best to get the value of the option
// (which is difficult when you use a long option because
// getopt will by default give you the id of the option.
// It fails when the user uses the option with different
// syntaxes in succession.
if (g.getOptopt() < 0
|| longOptsArray[g.getLongind()].getVal() != g.getOptopt() ) {
// this is definitely a short option
optName = String.valueOf((char)g.getOptopt());
} else {
optName = longOptsArray[g.getLongind()].getName();
}
throw new CompilationDeathException(COMPILATION_ABORTED,
"Option " + optName
+ "requires an argument!");
case '?':
// invalid option
throw new
CompilationDeathException(COMPILATION_ABORTED,
"Option "
+ (g.getOptopt() == 0 ?
args[g.getOptind()-1].substring(2)
: String.valueOf((char)g.getOptopt()) )
+ " is not valid");
default:
// we should only get here for the unhandled options
}
}
// get trailing non options ( after '--' )
for (int i = g.getOptind(); i < args.length ; i++)
cmdLineClasses.add(args[i]);
postCmdLineCheck();
}
private static String shortOpts = "-:";
private static ArrayList longOpts = new ArrayList();
private static void addGetoptOption(int id, String name, int has_arg) {
if (id > 0 && shortOpts.indexOf((char)id) == -1) {
StringBuffer opts = new StringBuffer(shortOpts);
opts.append((char)id);
if (has_arg == LongOpt.REQUIRED_ARGUMENT)
opts.append(':');
// supported but discouraged
if (has_arg == LongOpt.OPTIONAL_ARGUMENT)
opts.append("::");
shortOpts = opts.toString();
}
longOpts.add(new LongOpt(name, has_arg, null, id));
}
private static void processCmdLine_classic(String[] args)
throws CompilationDeathException
{
if(args.length == 0) {
printHelp();
throw new CompilationDeathException(COMPILATION_ABORTED, "don't know what to do!");
}
// handle --app option first
for(int i = 0; i < args.length; i++) {
String arg = args[i];
if(arg.equals("--app")) {
setAppMode(true);
}
}
// Handle all the options
for(int i = 0; i < args.length; i++) {
String arg = args[i];
if(arg.equals("--app"))
continue; // ignore
else if(arg.equals("--lazy"))
setLazyInvocation(true);
else if(arg.equals("--nooutput"))
setTargetRep(NO_OUTPUT);
else if(arg.equals("-j") || arg.equals("--jimp"))
setTargetRep(JIMP);
else if(arg.equals("--njimple"))
setTargetRep(NJIMPLE);
else if(arg.equals("-s") || arg.equals("--jasmin"))
setTargetRep(JASMIN);
else if(arg.equals("-J") || arg.equals("--jimple"))
setTargetRep(JIMPLE);
else if(arg.equals("-B") || arg.equals("--baf"))
setTargetRep(BAF);
else if(arg.equals("-b") || arg.equals("--b"))
setTargetRep(B);
else if(arg.equals("-g") || arg.equals("--grimp"))
setTargetRep(GRIMP);
else if(arg.equals("-G") || arg.equals("--grimple"))
setTargetRep(GRIMPLE);
else if(arg.equals("-c") || arg.equals("--class"))
setTargetRep(CLASS);
else if(arg.equals("--dava")) {
Scene.v().setJimpleStmtPrinter( soot.dava.DavaStmtPrinter.v());
Scene.v().setLocalPrinter( soot.dava.DavaLocalPrinter.v());
setTargetRep(DAVA);
}
else if(arg.equals("-X") || arg.equals("--xml")) {
Scene.v().setJimpleStmtPrinter( soot.jimple.XMLStmtPrinter.v());
setTargetRep(XML);
}
else if(arg.equals("-O") || arg.equals("--optimize"))
setOptimizing(true);
else if(arg.equals("-W") || arg.equals("--whole-optimize"))
setOptimizingWhole(true);
else if(arg.equals("-t") || arg.equals("--time"))
setProfiling(true);
else if (arg.equals( "--with-cache"))
setWithCache( true);
else if ((arg.equals( "--cache-dir")) || (arg.equals( "-k"))) {
if (++i < args.length)
setCacheDir( args[i]);
}
else if(arg.equals("--subtract-gc"))
setSubstractingGC(true);
else if(arg.equals("--j2me"))
setJ2ME(true);
else if(arg.equals("-v") || arg.equals("--verbose"))
setVerbose(true);
else if(arg.equals("--soot-class-path")
|| arg.equals("--soot-classpath")) {
if(++i < args.length)
Scene.v().setSootClassPath(args[i]);
}
else if(arg.equals("-d")) {
if(++i < args.length)
outputDir = args[i];
}
else if(arg.equals("-x") || arg.equals("--exclude")) {
if(++i < args.length)
addExclude(args[i]);
}
else if(arg.equals("-i") || arg.equals("--include")) {
if(++i < args.length)
addInclude(args[i]);
}
else if(arg.equals("-a") || arg.equals("--analyze-context"))
setAnalyzingLibraries(true);
else if(arg.equals("--final-rep")) {
if(++i < args.length)
setFinalRep(args[i]);
}
else if (arg.equals("-p") || arg.equals("--phase-option")) {
if(i+2 < args.length)
processPhaseOptions(args[++i], args[++i]);
//syntax -p phase-name:phase-options
//if(++i < args.length)
// processPhaseOption(args[i]);
}
else if (arg.equals("--debug"))
setDebug(true);
else if (arg.equals("--dynamic-path")) {
if(++i < args.length)
addDynamicPath(args[i]);
}
else if (arg.equals("--dynamic-packages")) {
if(++i < args.length)
addDynamicPackage(args[i]);
}
else if (arg.equals("--process-path")) {
if(++i < args.length)
addProcessPath(args[i]);
}
else if(arg.equals("--src-prec")) {
if(++i < args.length)
setSrcPrecedence(args[i]);
}
else if(arg.equals("--tag-file")) {
if(++i < args.length)
sTagFileList.add(args[i]);
}
else if(arg.equals("-A") || arg.equals("--annotation")) {
if (++i < args.length)
setAnnotationPhases(args[i]);
}
else if(arg.equals("--version")) {
printVersion();
throw new CompilationDeathException(COMPILATION_SUCCEDED);
}
else if(arg.equals("-h") || arg.equals("--help")) {
printHelp();
throw new CompilationDeathException(COMPILATION_SUCCEDED);
}
else if (arg.equals("--classic") || arg.equals("--use-Getop")
|| arg.equals("--use-CommandLine")) {
// handled elsewhere
}
else if(arg.startsWith("-")) {
System.out.println("Unrecognized option: " + arg);
printHelp();
throw new CompilationDeathException(COMPILATION_ABORTED);
}
else if(arg.startsWith("@")) {
try {
File fn = new File(arg.substring(1));
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fn)));
List argsList = new LinkedList();
while (br.ready())
argsList.add(br.readLine());
br.close();
processCmdLine_classic((String[])argsList.toArray(new String[argsList.size()]));
} catch (IOException e) {
throw new CompilationDeathException(COMPILATION_ABORTED,
"Error reading file "+arg.substring(1));
}
}
else {
cmdLineClasses.add(arg);
}
}
postCmdLineCheck();
}
private static void exitCompilation(int status)
{
exitCompilation(status, "") ;
}
private static void exitCompilation(int status, String msg)
{
Scene.v().reset();
Iterator it = compilationListeners.iterator();
while(it.hasNext())
((ICompilationListener)it.next()).compilationTerminated(status, msg);
}
// called by the new command-line parser
private static void processPhaseOptions(String phaseOptions) {
int idx = phaseOptions.indexOf(':');
if (idx == -1) {
throw new CompilationDeathException(COMPILATION_ABORTED,
"Invalid phase option: "
+ phaseOptions);
}
String phaseName = phaseOptions.substring(0, idx);
StringTokenizer st = new StringTokenizer(phaseOptions.substring(idx+1), ",");
while (st.hasMoreTokens()) {
processPhaseOption(phaseName, st.nextToken(), '=');
}
}
// called by the "classic" command-line parser
private static void processPhaseOptions(String phaseName, String option) {
StringTokenizer st = new StringTokenizer(option, ",");
while (st.hasMoreTokens()) {
processPhaseOption(phaseName, st.nextToken(), ':');
}
}
private static void processPhaseOption(String phaseName, String option,
char delimiter)
{
int delimLoc = option.indexOf(delimiter);
String key = null, value = null;
if (delimLoc == -1)
{
key = option;
value = "true";
}
else
{
key = option.substring(0, delimLoc);
value = option.substring(delimLoc+1);
}
Scene.v().getPhaseOptions(phaseName).put(key, value);
}
private static void postCmdLineCheck()
throws CompilationDeathException
{
if(cmdLineClasses.isEmpty() && processClasses.isEmpty())
{
throw new CompilationDeathException(COMPILATION_ABORTED, "Nothing to do!");
}
// Command line classes
if (isApplication && cmdLineClasses.size() > 1)
{
throw new CompilationDeathException(COMPILATION_ABORTED,
"Can only specify one class in application mode!\n" +
"The transitive closure of the specified class gets loaded.\n" +
"(Did you mean to use single-file mode?)");
}
}
/** Initializes various Soot data and calls the PackAdjuster.
* Must be called! */
public static void initApp()
{
packageInclusionFlags.add(new Boolean(false));
packageInclusionMasks.add("java.");
packageInclusionFlags.add(new Boolean(false));
packageInclusionMasks.add("sun.");
packageInclusionFlags.add(new Boolean(false));
packageInclusionMasks.add("javax.");
}
private static String[] cmdLineArgs;
public static void setCmdLineArgs(String[] args)
{
cmdLineArgs = args;
}
/**
* Entry point for cmd line invocation of soot.
*/
public static void main(String[] args)
{
setReservedNames();
setCmdLineArgs(args);
Main m = new Main();
ConsoleCompilationListener ccl = new ConsoleCompilationListener();
addCompilationListener(ccl);
(new Thread(m)).start();
}
public static void setReservedNames()
{
Set rn = Scene.v().getReservedNames();
rn.add("newarray");
rn.add("newmultiarray");
rn.add("nop");
rn.add("ret");
rn.add("specialinvoke");
rn.add("staticinvoke");
rn.add("tableswitch");
rn.add("virtualinvoke");
rn.add("null_type");
rn.add("unknown");
rn.add("cmp");
rn.add("cmpg");
rn.add("cmpl");
rn.add("entermonitor");
rn.add("exitmonitor");
rn.add("interfaceinvoke");
rn.add("lengthof");
rn.add("lookupswitch");
rn.add("neg");
rn.add("if");
rn.add("abstract");
rn.add("boolean");
rn.add("break");
rn.add("byte");
rn.add("case");
rn.add("catch");
rn.add("char");
rn.add("class");
rn.add("final");
rn.add("native");
rn.add("public");
rn.add("protected");
rn.add("private");
rn.add("static");
rn.add("synchronized");
rn.add("transient");
rn.add("volatile");
rn.add("interface");
rn.add("void");
rn.add("short");
rn.add("int");
rn.add("long");
rn.add("float");
rn.add("double");
rn.add("extends");
rn.add("implements");
rn.add("breakpoint");
rn.add("default");
rn.add("goto");
rn.add("instanceof");
rn.add("new");
rn.add("return");
rn.add("throw");
rn.add("throws");
rn.add("null");
rn.add("from");
rn.add("to");
}
/**
* Entry point to the soot's compilation process. Be sure to call
* setCmdLineArgs before invoking this method.
*
* @see #setCmdLineArgs
*/
public void run() {
start = new Date();
try {
totalTimer.start();
cmdLineClasses = new HashChain();
initApp();
processCmdLine(cmdLineArgs);
System.out.println("Soot started on "+start);
loadNecessaryClasses();
prepareClasses();
/* process all reachable methods and generate jimple body */
if (isLazyInvocation) {
lazyPreprocessClasses();
}
processTagFiles();
// Run the whole-program packs.
Scene.v().getPack("wjtp").apply();
if(isOptimizingWhole)
Scene.v().getPack("wjop").apply();
// Give one more chance
Scene.v().getPack("wjtp2").apply();
// System.gc();
preProcessDAVA();
if (isLazyInvocation) {
lazyProcessClasses();
} else {
processClasses();
}
postProcessDAVA();
totalTimer.end();
// Print out time stats.
if(isProfilingOptimization)
printProfilingInformation();
} catch (CompilationDeathException e) {
totalTimer.end();
exitCompilation(e.getStatus(), e.getMessage());
return;
}
finish = new Date();
System.out.println("Soot finished on "+finish);
long runtime = finish.getTime() - start.getTime();
System.out.println("Soot has run for "
+(runtime/60000)+" min. "
+((runtime%60000)/1000)+" sec.");
exitCompilation(COMPILATION_SUCCEDED);
}
/* preprocess classes for DAVA */
private static void preProcessDAVA() {
if ((targetExtension == DAVA) || (finalRep == DAVA)) {
ThrowFinder.v().find();
PackageNamer.v().fixNames();
System.out.println();
}
}
/* lazily preprocess classes, based on invoke graph generated by CHA */
private static void lazyPreprocessClasses() {
Date start = new Date();
// if (Main.isVerbose)
{
System.out.println("[] Start building the invoke graph ... ");
}
InvokeGraph invokeGraph =
ClassHierarchyAnalysis.newPreciseInvokeGraph(true);
Scene.v().setActiveInvokeGraph(invokeGraph);
Date finish = new Date();
// if (Main.isVerbose)
{
System.out.println("[] Finished building the invoke graph ...");
long runtime = finish.getTime() - start.getTime();
System.out.println("[] Building invoke graph takes "
+(runtime/60000)+" min. "
+((runtime%60000)/1000)+" sec.");
}
}
/* lazily process classes */
private static void lazyProcessClasses() {
Iterator classIt = Scene.v().getApplicationClasses().iterator();
while (classIt.hasNext()) {
SootClass s = (SootClass)classIt.next();
System.out.println(" Transforming " + s.getName() + "...");
if (!isInDebugMode) {
try {
lazyHandleClass(s);
} catch (RuntimeException e) {
e.printStackTrace();
}
} else {
lazyHandleClass(s);
}
}
}
/* process classes */
private static void processClasses() {
Iterator classIt = Scene.v().getApplicationClasses().iterator();
// process each class
while(classIt.hasNext()) {
SootClass s = (SootClass) classIt.next();
if ((targetExtension == DAVA) || (finalRep == DAVA))
System.out.print( "Decompiling ");
else
System.out.print( "Transforming ");
System.out.print( s.getName() + "... " );
System.out.flush();
if(!isInDebugMode) {
try {
handleClass(s);
} catch(RuntimeException e) {
e.printStackTrace();
}
} else {
handleClass(s);
}
System.out.println();
}
}
/* post process for DAVA */
private static void postProcessDAVA() {
if ((targetExtension == DAVA) || (finalRep == DAVA)) {
// ThrowFinder.v().find();
// PackageNamer.v().fixNames();
System.out.println();
setJavaStyle( true);
Iterator classIt = Scene.v().getApplicationClasses().iterator();
while (classIt.hasNext()) {
SootClass s = (SootClass) classIt.next();
FileOutputStream streamOut = null;
PrintWriter writerOut = null;
String fileName = getFileNameFor( s, targetExtension);
try {
streamOut = new FileOutputStream(fileName);
writerOut = new PrintWriter(new OutputStreamWriter(streamOut));
} catch (IOException e) {
System.out.println("Cannot output file " + fileName);
}
System.out.print( "Generating " + fileName + "... ");
System.out.flush();
if (!isInDebugMode) {
try {
s.printTo(writerOut, PrintGrimpBodyOption.USE_ABBREVIATIONS);
} catch (RuntimeException e) {
e.printStackTrace();
}
} else {
s.printTo(writerOut, PrintGrimpBodyOption.USE_ABBREVIATIONS);
}
System.out.println();
System.out.flush();
{
try {
writerOut.flush();
streamOut.close();
} catch(IOException e) {
System.out.println("Cannot close output file " + fileName);
}
}
{
Iterator methodIt = s.getMethods().iterator();
while(methodIt.hasNext()) {
SootMethod m = (SootMethod) methodIt.next();
if(m.hasActiveBody())
m.releaseActiveBody();
}
}
}
System.out.println();
setJavaStyle( false);
}
}
/* load necessary classes
*/
private static void loadNecessaryClasses() {
Iterator it = cmdLineClasses.iterator();
while(it.hasNext()) {
String name = (String) it.next();
SootClass c;
c = Scene.v().loadClassAndSupport(name);
if(mainClass == null) {
mainClass = c;
Scene.v().setMainClass(c);
}
c.setApplicationClass();
}
// Dynamic packages
it = dynamicPackages.iterator();
while(it.hasNext())
markPackageAsDynamic((String)it.next());
// Dynamic & process classes
it = dynamicClasses.iterator();
while(it.hasNext())
Scene.v().loadClassAndSupport((String) it.next());
it = processClasses.iterator();
while(it.hasNext()) {
String s = (String)it.next();
Scene.v().loadClassAndSupport(s);
Scene.v().getSootClass(s).setApplicationClass();
}
}
/* Generate classes to process, adding or removing package marked by
* command line options.
*/
private static void prepareClasses() {
if(isApplication) {
Iterator contextClassesIt =
Scene.v().getContextClasses().snapshotIterator();
while (contextClassesIt.hasNext())
((SootClass)contextClassesIt.next()).setApplicationClass();
}
// Remove/add all classes from packageInclusionMask as per piFlag
List applicationPlusContextClasses = new ArrayList();
applicationPlusContextClasses.addAll(Scene.v().getApplicationClasses());
applicationPlusContextClasses.addAll(Scene.v().getContextClasses());
Iterator classIt = applicationPlusContextClasses.iterator();
while(classIt.hasNext()) {
SootClass s = (SootClass) classIt.next();
if(cmdLineClasses.contains(s.getName()))
continue;
Iterator packageCmdIt = packageInclusionFlags.iterator();
Iterator packageMaskIt = packageInclusionMasks.iterator();
while(packageCmdIt.hasNext()) {
boolean pkgFlag =
((Boolean) packageCmdIt.next()).booleanValue();
String pkgMask = (String) packageMaskIt.next();
//RS@ s.setApplicationClass();
if (pkgFlag) {
if (s.isContextClass()
&& s.getPackageName().startsWith(pkgMask))
s.setApplicationClass();
} else {
if (s.isApplicationClass()
&& s.getPackageName().startsWith(pkgMask))
s.setContextClass();
}
}
}
if (isAnalyzingLibraries) {
Iterator contextClassesIt =
Scene.v().getContextClasses().snapshotIterator();
while (contextClassesIt.hasNext())
((SootClass)contextClassesIt.next()).setLibraryClass();
}
}
/* read in the tag files
* Who created this? It calls retrieve jimple body
*/
private static void processTagFiles() {
Iterator it = sTagFileList.iterator();
while(it.hasNext()) {
try {
File f = new File((String)it.next());
BufferedReader reader =
new BufferedReader(new InputStreamReader(new FileInputStream(f)));
for(String line = reader.readLine();
line != null;
line = reader.readLine()) {
if(line.startsWith("<") ) {
String signature = line.substring(0,line.indexOf('+'));
int offset = Integer.parseInt(line.substring(line.indexOf('+')
+ 1, line.indexOf('/')));
String name = line.substring(line.indexOf('/')+1,
line.lastIndexOf(':'));
String value = line.substring(line.lastIndexOf(':')+1);
SootMethod m = Scene.v().getMethod(signature);
JimpleBody body = (JimpleBody) m.retrieveActiveBody();
List unitList = new ArrayList(body.getUnits());
Unit u = (Unit) unitList.get(offset);
if(Long.valueOf(value) == null)
System.out.println(value);
}
}
} catch (IOException e) {
}
}
}
private static void printProfilingInformation()
{
long totalTime = totalTimer.getTime();
System.out.println("Time measurements");
System.out.println();
System.out.println(" Building graphs: " + toTimeString(graphTimer, totalTime));
System.out.println(" Computing LocalDefs: " + toTimeString(defsTimer, totalTime));
// System.out.println(" setup: " + toTimeString(defsSetupTimer, totalTime));
// System.out.println(" analysis: " + toTimeString(defsAnalysisTimer, totalTime));
// System.out.println(" post: " + toTimeString(defsPostTimer, totalTime));
System.out.println(" Computing LocalUses: " + toTimeString(usesTimer, totalTime));
// System.out.println(" Use phase1: " + toTimeString(usePhase1Timer, totalTime));
// System.out.println(" Use phase2: " + toTimeString(usePhase2Timer, totalTime));
// System.out.println(" Use phase3: " + toTimeString(usePhase3Timer, totalTime));
System.out.println(" Cleaning up code: " + toTimeString(cleanupAlgorithmTimer, totalTime));
System.out.println("Computing LocalCopies: " + toTimeString(copiesTimer, totalTime));
System.out.println(" Computing LiveLocals: " + toTimeString(liveTimer, totalTime));
// System.out.println(" setup: " + toTimeString(liveSetupTimer, totalTime));
// System.out.println(" analysis: " + toTimeString(liveAnalysisTimer, totalTime));
// System.out.println(" post: " + toTimeString(livePostTimer, totalTime));
System.out.println("Coading coffi structs: " + toTimeString(resolveTimer, totalTime));
System.out.println();
// Print out time stats.
{
float timeInSecs;
System.out.println(" Resolving classfiles: " + toTimeString(resolverTimer, totalTime));
System.out.println(" Bytecode -> jimple (naive): " + toTimeString(conversionTimer, totalTime));
System.out.println(" Splitting variables: " + toTimeString(splitTimer, totalTime));
System.out.println(" Assigning types: " + toTimeString(assignTimer, totalTime));
System.out.println(" Propagating copies & csts: " + toTimeString(propagatorTimer, totalTime));
System.out.println(" Eliminating dead code: " + toTimeString(deadCodeTimer, totalTime));
System.out.println(" Aggregation: " + toTimeString(aggregationTimer, totalTime));
System.out.println(" Coloring locals: " + toTimeString(packTimer, totalTime));
System.out.println(" Generating jasmin code: " + toTimeString(buildJasminTimer, totalTime));
System.out.println(" .jasmin -> .class: " + toTimeString(assembleJasminTimer, totalTime));
// System.out.println(" Cleaning up code: " + toTimeString(cleanup1Timer, totalTime) +
// "\t" + cleanup1LocalCount + " locals " + cleanup1StmtCount + " stmts");
// System.out.println(" Split phase1: " + toTimeString(splitPhase1Timer, totalTime));
// System.out.println(" Split phase2: " + toTimeString(splitPhase2Timer, totalTime));
/*
System.out.println("cleanup2Timer: " + cleanup2Time +
"(" + (cleanup2Time * 100 / totalTime) + "%) " +
cleanup2LocalCount + " locals " + cleanup2StmtCount + " stmts");
*/
timeInSecs = (float) totalTime / 1000.0f;
float memoryUsed = (float) (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1000.0f;
System.out.println("totalTime:" + toTimeString(totalTimer, totalTime));
if(isSubtractingGC)
{
System.out.println("Garbage collection was subtracted from these numbers.");
System.out.println(" forcedGC:" +
toTimeString(Timer.forcedGarbageCollectionTimer, totalTime));
}
System.out.println("stmtCount: " + stmtCount + "(" + toFormattedString(stmtCount / timeInSecs) + " stmt/s)");
System.out.println("totalFlowNodes: " + totalFlowNodes +
" totalFlowComputations: " + totalFlowComputations + " avg: " +
truncatedOf((double) totalFlowComputations / totalFlowNodes, 2));
}
}
private static String toTimeString(Timer timer, long totalTime)
{
DecimalFormat format = new DecimalFormat("00.0");
DecimalFormat percFormat = new DecimalFormat("00.0");
long time = timer.getTime();
String timeString = format.format(time / 1000.0); // paddedLeftOf(new Double(truncatedOf(time / 1000.0, 1)).toString(), 5);
return (timeString + "s" + " (" + percFormat.format(time * 100.0 / totalTime) + "%" + ")");
}
private static String toFormattedString(double value)
{
return paddedLeftOf(new Double(truncatedOf(value, 2)).toString(), 5);
}
/** Attach JimpleBodies to the methods of c. */
private static void attachJimpleBodiesFor(SootClass c) {
Iterator methodIt = c.getMethods().iterator();
while(methodIt.hasNext()) {
SootMethod m = (SootMethod) methodIt.next();
if(!m.isConcrete())
continue;
if(!m.hasActiveBody()) {
m.setActiveBody(m.getBodyFromMethodSource("jb"));
Scene.v().getPack("jtp").apply(m.getActiveBody());
if(isOptimizing)
Scene.v().getPack("jop").apply(m.getActiveBody());
}
}
}
/* lazyHandleClass only processes methods reachable from the entry points
* by the call graph, it does not have any output.
*/
private static void lazyHandleClass(SootClass c) {
Iterator methodIt = c.getMethods().iterator();
while(methodIt.hasNext()) {
SootMethod m = (SootMethod) methodIt.next();
if(!m.isConcrete())
continue;
if (m.hasActiveBody()) {
JimpleBody body = (JimpleBody) m.getActiveBody();
Scene.v().getPack("jtp").apply(body);
if(isOptimizing)
Scene.v().getPack("jop").apply(body);
m.releaseActiveBody();
}
}
}
/* normal approach to handle each class by analyzing every method.
*/
private static void handleClass(SootClass c)
{
FileOutputStream streamOut = null;
PrintWriter writerOut = null;
boolean
produceBaf = false,
produceGrimp = false,
produceDava = false;
switch( targetExtension) {
case NO_OUTPUT:
break;
case JIMPLE:
case NJIMPLE:
case JIMP:
break;
case DAVA:
produceDava = true;
case GRIMP:
case GRIMPLE:
produceGrimp = true;
break;
case BAF:
case B:
produceBaf = true;
break;
case XML:
break;
default:
switch( finalRep) {
case DAVA:
produceDava = true;
case GRIMP:
case GRIMPLE:
produceGrimp = true;
break;
case BAF:
produceBaf = true;
default:
}
}
String fileName = getFileNameFor( c, targetExtension);
// add an option for no output
if ((targetExtension != NO_OUTPUT) && (targetExtension != CLASS)) {
try {
streamOut = new FileOutputStream(fileName);
writerOut = new PrintWriter(new OutputStreamWriter(streamOut));
} catch (IOException e) {
System.out.println("Cannot output file " + fileName);
}
}
HashChain newMethods = new HashChain();
// Build all necessary bodies
{
Iterator methodIt = c.getMethods().iterator();
while(methodIt.hasNext()) {
SootMethod m = (SootMethod) methodIt.next();
if(!m.isConcrete())
continue;
// Build Jimple body and transform it.
{
boolean wasOptimizing = isOptimizing;
if (produceDava)
isOptimizing = true;
JimpleBody body = (JimpleBody) m.retrieveActiveBody();
Scene.v().getPack("jtp").apply(body);
if(isOptimizing)
Scene.v().getPack("jop").apply(body);
isOptimizing = wasOptimizing;
}
if(produceGrimp) {
boolean wasOptimizing = isOptimizing;
if (produceDava)
isOptimizing = true;
if(isOptimizing)
m.setActiveBody(Grimp.v().newBody(m.getActiveBody(), "gb", "aggregate-all-locals"));
else
m.setActiveBody(Grimp.v().newBody(m.getActiveBody(), "gb"));
if(isOptimizing)
Scene.v().getPack("gop").apply(m.getActiveBody());
isOptimizing = wasOptimizing;
} else if(produceBaf) {
m.setActiveBody(Baf.v().newBody((JimpleBody) m.getActiveBody()));
if(isOptimizing)
Scene.v().getPack("bop").apply(m.getActiveBody());
}
}
if (produceDava) {
methodIt = c.getMethods().iterator();
while (methodIt.hasNext()) {
SootMethod m = (SootMethod) methodIt.next();
if (!m.isConcrete())
continue;
m.setActiveBody( Dava.v().newBody( m.getActiveBody(), "db"));
}
}
}
switch(targetExtension) {
// add an option for no output
case NO_OUTPUT:
break;
case JASMIN:
if(c.containsBafBody())
new soot.baf.JasminClass(c).print(writerOut);
else
new soot.jimple.JasminClass(c).print(writerOut);
break;
case JIMP:
c.printTo(writerOut, PrintJimpleBodyOption.USE_ABBREVIATIONS);
break;
case NJIMPLE:
c.printTo(writerOut, PrintJimpleBodyOption.NUMBERED);
break;
case B:
c.printTo(writerOut, soot.baf.PrintBafBodyOption.USE_ABBREVIATIONS);
break;
case BAF:
case JIMPLE:
case GRIMPLE:
writerOut =
new PrintWriter(new EscapedWriter(new OutputStreamWriter(streamOut)));
c.printJimpleStyleTo(writerOut, 0);
break;
case DAVA:
break;
case GRIMP:
c.printTo(writerOut, PrintGrimpBodyOption.USE_ABBREVIATIONS);
break;
case CLASS:
c.write(outputDir);
break;
case XML:
writerOut =
new PrintWriter(new EscapedWriter(new OutputStreamWriter(streamOut)));
c.printJimpleStyleTo(writerOut, PrintJimpleBodyOption.XML_OUTPUT);
break;
default:
throw new RuntimeException();
}
if ((targetExtension != NO_OUTPUT) && (targetExtension != CLASS)) {
try {
writerOut.flush();
streamOut.close();
} catch(IOException e) {
System.out.println("Cannot close output file " + fileName);
}
}
// Release bodies
if (!produceDava) {
Iterator methodIt = c.getMethods().iterator();
while(methodIt.hasNext()) {
SootMethod m = (SootMethod) methodIt.next();
if(m.hasActiveBody())
m.releaseActiveBody();
}
}
}
public static double truncatedOf(double d, int numDigits)
{
double multiplier = 1;
for(int i = 0; i < numDigits; i++)
multiplier *= 10;
return ((long) (d * multiplier)) / multiplier;
}
public static String paddedLeftOf(String s, int length)
{
if(s.length() >= length)
return s;
else {
int diff = length - s.length();
char[] padding = new char[diff];
for(int i = 0; i < diff; i++)
padding[i] = ' ';
return new String(padding) + s;
}
}
}