/*******************************************************************************
* Copyright (c) 2014 Imperial College London
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Raul Castro Fernandez - initial API and implementation
******************************************************************************/
package uk.ac.imperial.lsds.java2sdg;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import soot.CompilationDeathException;
import soot.PhaseOptions;
import soot.Scene;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;
import soot.options.Options;
import soot.toolkits.graph.UnitGraph;
import soot.util.Chain;
import uk.ac.imperial.lsds.java2sdg.bricks.InternalStateRepr;
import uk.ac.imperial.lsds.java2sdg.bricks.TaskElement.TaskElementBuilder;
import uk.ac.imperial.lsds.java2sdg.bricks.SDG.OperatorBlock;
import uk.ac.imperial.lsds.java2sdg.bricks.SDG.PartialSDGBuilder;
import uk.ac.imperial.lsds.java2sdg.bricks.SDG.SDGBuilder;
import uk.ac.imperial.lsds.java2sdg.codegenerator.CodeGenerator;
import uk.ac.imperial.lsds.java2sdg.flowanalysis.LiveVariableAnalysis;
import uk.ac.imperial.lsds.java2sdg.flowanalysis.TEBoundaryAnalysis;
import uk.ac.imperial.lsds.java2sdg.input.SourceCodeHandler;
import uk.ac.imperial.lsds.java2sdg.output.DOTExporter;
import uk.ac.imperial.lsds.java2sdg.output.GEXFExporter;
public class Main {
private final static Logger LOG = LoggerFactory.getLogger(Main.class.getSimpleName());
public static void main(String args[]) {
/** Parse input parameters **/
//FIXME: ra. Get rid of this options library and move to joptsimple
//FIXME: ra. integrate *all* options as properties
// Define options
org.apache.commons.cli.Options options = new org.apache.commons.cli.Options();
options.addOption("h", "help", false, "print this message");
options.addOption("t", "target", true, "desired target. dot for DOT file OR gexf for GEXF files OR seepjar for SEEP runnable jar");
options.addOption("o", "output", true, "desired output file name");
options.addOption("i", "input", true, "the name of the input program");
options.addOption("cp", "classpath", true, "the path to additional libraries and code used by the input program");
// generate helper
HelpFormatter formatter = new HelpFormatter();
// Parse arguments
CommandLineParser parser = new BasicParser();
CommandLine cmd = null;
try {
cmd = parser.parse(options, args);
}
catch (ParseException e) {
// TODO Auto-generated catch block
System.err.println("Parsing failed. Reason: " + e.getMessage());
}
// Get values
String className = null;
String pathToSeepJar = "../seep-common/build/libs/seep-common-0.1.jar";
String pathToDriverFile = null;
String outputTarget = null;
String outputFileName = null;
if (cmd.hasOption("i")) {
className = cmd.getOptionValue("i");
if (className == null) {
formatter.printHelp("java2sdg", options);
System.exit(0);
}
}
else {
formatter.printHelp("java2sdg", options);
System.exit(0);
}
if (cmd.hasOption("cp")) {
pathToDriverFile = cmd.getOptionValue("cp");
if (pathToDriverFile == null) {
System.err.println("cp parameter cannot be empty. Please specify the path to your classpath");
System.exit(0);
}
}
else {
pathToDriverFile = ".";
}
if (cmd.hasOption("o")) {
outputFileName = cmd.getOptionValue("o");
if (outputFileName == null) {
System.err.println("o parameter cannot be empty. Please specify a name for the output file");
System.exit(0);
}
}
else {
formatter.printHelp("java2sdg", options);
System.exit(0);
}
if (cmd.hasOption("t")) {
outputTarget = cmd.getOptionValue("t");
if (outputTarget == null) {
System.err.println("t parameter cannot be empty. Please specify a target, dot/seepjar");
System.exit(0);
}
if (!(outputTarget.equals("dot") || outputTarget.equals("seepjar") || outputTarget.equals("gexf"))) {
System.err.println("Invalid target option");
formatter.printHelp("java2sdg", options);
System.exit(0);
}
}
else {
outputTarget = "dot";
}
/** Set up SOOT for compilation and java manipulation **/
// Get java.home to access rt.jar, required by soot
String javaHome = System.getProperty("java.home");
String sootClassPath = javaHome + "/lib/rt.jar:" + pathToSeepJar + ":"
+ "../seep-worker/build/libs/seep-worker-0.1.jar" + ":./"; //FIXME: Ra. It should not need seep-worker anymore
String pathToSourceCode = pathToDriverFile + "/" + className;
/**
* Parse input program source code. This stage performs operations at source code only
*
**/
// Parse original source code
LOG.info("Parsing source code...");
SourceCodeHandler sch = SourceCodeHandler.getInstance(pathToSourceCode);
sch.printLineAnnotation();
LOG.info("Parsing source code...OK");
/** Initialise soot and load input program **/
// With the class loaded, we can then get the SootClass wrapper to work
LOG.info("Setting soot classpath: " + sootClassPath);
Scene.v().setSootClassPath(sootClassPath);
Options.v().setPhaseOption("jb", "preserve-source-annotations");
LOG.info("Loading class: " + className);
SootClass c = null;
try {
System.out.println();
c = Scene.v().loadClassAndSupport(className);
c.setApplicationClass();
}
catch (CompilationDeathException cde) {
System.out.println();
LOG.error(cde.getMessage());
System.exit(1);
}
LOG.info("Loading class...OK");
/** Extract fields and workflows **/
PhaseOptions.v().setPhaseOption("tag.ln", "on"); // tell compiler to include line numbers
// List fields and indicate which one is state
LOG.info("Extracting state information...");
Chain<SootField> fields = c.getFields();
Iterator<SootField> fieldsIterator = fields.iterator();
Map<String, InternalStateRepr> stateElements = Util.extractStateInformation(fieldsIterator);
LOG.info("Extracting state information...OK");
// List relevant methods (the ones that need to be analyzed)
LOG.info("Extracting workflows...");
Iterator<SootMethod> methodIterator = c.methodIterator();
DriverProgramAnalyzer driverProgramAnalyzer = new DriverProgramAnalyzer();
driverProgramAnalyzer.extractWorkflows(methodIterator, c);
List<String> workflows = DriverProgramAnalyzer.getWorkflowNames(); //TODO: will return WorkflowRepr objects instead of strings
LOG.info("Extracting workflows...OK");
/** Build partialSDGs, one per workflow **/
SDGBuilder sdgBuilder = new SDGBuilder();
int workflowId = 0;
// Analyse and extract a partial SDG per workflow
for (String methodName : workflows) {
// Build CFG
LOG.info("Building partialSDG for workflow: " + methodName);
UnitGraph cfg = Util.getCFGForMethod(methodName, c); // get cfg
// Perform live variable analysis
LiveVariableAnalysis lva = LiveVariableAnalysis.getInstance(cfg); // compute livevariables
// Perform TE boundary analysis
TEBoundaryAnalysis oba = TEBoundaryAnalysis.getBoundaryAnalyzer(cfg, stateElements, sch, lva);
List<TaskElementBuilder> sequentialTEList = oba.performTEAnalysis();
// TODO: this partialSDG will contain also a source and optionally a sink, depending on the info of the WorkflowRepr object
List<OperatorBlock> partialSDG = PartialSDGBuilder.buildPartialSDG(sequentialTEList, workflowId);
// for(OperatorBlock ob : partialSDG){
// System.out.println(ob);
// }
workflowId++;
sdgBuilder.addPartialSDG(partialSDG);
}
// TODO: Validate partialSDGs here.
// TODO: we should come up with a reasonable collection of unit tests to try all type of workflows at this point
// TODO: and observe whether they are correctly constructed or not (including sources and sinks)
/** Build SDG from partialSDGs **/
LOG.info("Building SDG from " + sdgBuilder.getNumberOfPartialSDGs()
+ " partialSDGs...");
List<OperatorBlock> sdg = sdgBuilder.synthetizeSDG();
// for(OperatorBlock ob : sdg){
// System.out.println(ob);
// }
LOG.info("Building SDG from partialSDGs...OK");
/** Ouput SDG in a given format **/
// Output
if (outputTarget.equals("dot")) { // dot output
// Export SDG to dot
LOG.info("Exporting SDG to DOT file...");
DOTExporter exporter = DOTExporter.getInstance();
exporter.export(sdg, outputFileName);
LOG.info("Exporting SDG to DOT file...OK");
} else if (outputTarget.equals("gexf")) {
LOG.info("Exporting GEXF to DOT file...");
GEXFExporter exporter = GEXFExporter.getInstance();
exporter.export(sdg, outputFileName);
LOG.info("Exporting GEXF to DOT file...OK");
} else if (outputTarget.equals("seepjar")) {
LOG.info("Exporting SEEP runnable query...");
List<OperatorBlock> assembledCode = CodeGenerator.assemble(sdg);
for (OperatorBlock ob : assembledCode) {
System.out.println("---->");
System.out.println("");
System.out.println("");
System.out.println("");
System.out.println("");
System.out.println(ob.getCode());
System.out.println("");
System.out.println("");
System.out.println("");
System.out.println("");
}
// Set<TaskElement> sdg = SDGAssembler.getSDG(oba, lva, sch);
//
// // SDGAssembler sdgAssembler = new SDGAssembler();
// // Set<OperatorBlock> sdg =
// sdgAssembler.getFakeLinearPipelineOfStatelessOperators(1);
//
// QueryBuilder qBuilder = new QueryBuilder();
// String q = qBuilder.generateQueryPlanDriver(sdg);
// System.out.println("QueryPlan: "+q);
// qBuilder.buildAndPackageQuery();
}
}
}