/**
* Copyright (C) 2010 Hal Hildebrand. All rights reserved.
*
* This file is part of the Prime Mover Event Driven Simulation Framework.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program 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 Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.hellblazer.primeMover.soot;
import static com.hellblazer.primeMover.soot.util.Utils.isEntity;
import static java.util.Arrays.asList;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import soot.PackManager;
import soot.PhaseOptions;
import soot.Scene;
import soot.SceneTransformer;
import soot.SootClass;
import soot.SootMethod;
import soot.Transform;
import soot.baf.Baf;
import soot.options.Options;
/**
* The class that sets up the entire chain of transformations for the Prime
* Mover event driven simulation framework.
* <p>
*
* @author <a href="mailto:hal.hildebrand@gmail.com">Hal Hildebrand</a>
*
*/
public class SimulationTransform {
private static Logger log = Logger.getLogger(SimulationTransform.class.getCanonicalName());
public static String getBootClasspath() {
String cp = System.getProperty("sun.boot.class.path");
if (cp != null) {
log.info(String.format("Using[1] boot class path: %s", cp));
return cp;
}
cp = System.getProperty("java.boot.class.path");
if (cp != null) {
log.info(String.format("Using[2] boot class path: %s", cp));
return cp;
}
Enumeration<?> i = System.getProperties().propertyNames();
String name = null;
while (i.hasMoreElements()) {
String temp = (String) i.nextElement();
if (temp.indexOf(".boot.class.path") != -1) {
if (name == null) {
name = temp;
} else {
throw new IllegalStateException(
"Cannot auto-detect boot class path "
+ System.getProperty("java.version"));
}
}
}
if (name == null) {
String version = System.getProperty("java.version");
if (version.startsWith("1.1.")) {
// by default, the current directory is added to the classpath
// we therefore need to strip that out
cp = System.getProperty("java.class.path");
cp = removeAll(cp, ".");
cp = removeAll(cp, new File(".").getAbsolutePath());
try {
cp = removeAll(cp, new File(".").getCanonicalPath());
} catch (IOException e) {
// ignore
}
cp = removeAll(cp,
new File(".").getAbsolutePath()
+ System.getProperty("file.separator"));
try {
cp = removeAll(cp, new File(".").getCanonicalPath()
+ System.getProperty("file.separator"));
} catch (IOException e) {
// ignore
}
log.info(String.format("Using[3] boot class path: %s", cp));
return cp;
}
log.severe("Cannot auto-detect boot class path "
+ System.getProperty("java.version") + " "
+ System.getProperty("java.class.path"));
throw new IllegalStateException(
"Cannot auto-detect boot class path ");
}
log.info(String.format("Using[4] boot class path: %s",
System.getProperty(name)));
return System.getProperty(name);
}
public static boolean isWindows() {
return System.getProperty("os.name").toUpperCase().startsWith("WINDOWS");
}
public static void main(String[] args) {
try {
new SimulationTransform().execute(args);
} catch (RuntimeException e) {
e.printStackTrace();
}
}
public static String removeAll(String cp, String prefix) {
String pathSeparator = System.getProperty("path.separator");
if (cp.startsWith(prefix + pathSeparator)) {
cp = cp.substring(prefix.length() + pathSeparator.length());
}
int j;
while (-1 != (j = cp.indexOf(pathSeparator + prefix + pathSeparator))) {
cp = cp.substring(0, j)
+ cp.substring(j + prefix.length() + pathSeparator.length());
}
if (cp.endsWith(pathSeparator + prefix)) {
cp = cp.substring(0,
cp.length() - prefix.length()
+ pathSeparator.length());
}
if (isWindows()) {
// we might have the prefix or the classpath case differing
if (cp.toUpperCase().startsWith((prefix + pathSeparator).toUpperCase())) {
cp = cp.substring(prefix.length() + pathSeparator.length());
}
while (-1 != (j = cp.toUpperCase().indexOf((pathSeparator + prefix + pathSeparator).toUpperCase()))) {
cp = cp.substring(0, j)
+ cp.substring(j + prefix.length()
+ pathSeparator.length());
}
if (cp.toUpperCase().endsWith((pathSeparator + prefix).toUpperCase())) {
cp = cp.substring(0, cp.length() - prefix.length()
+ pathSeparator.length());
}
}
return cp;
}
public static void setStandardClassPath() {
Options.v().set_soot_classpath(String.format("%s:%s",
getBootClasspath(),
System.getProperty("java.class.path")));
}
private String generatedDirectory;
public void execute(String[] args) {
if (args == null) {
args = new String[] {};
}
Options.v().set_unfriendly_mode(true);
final List<SootClass> generated = new ArrayList<SootClass>();
PackManager.v().getPack("wjtp").add(new Transform(
"wjtp.continuation.analysis",
new ContinuationAnalysis()));
PackManager.v().getPack("wjtp").add(new Transform(
"wjtp.entity.generation",
new SceneTransformer() {
@Override
protected void internalTransform(String phaseName,
@SuppressWarnings("rawtypes") Map options) {
Iterator<SootClass> applicationClasses = Scene.v().getApplicationClasses().snapshotIterator();
while (applicationClasses.hasNext()) {
SootClass applicationClass = applicationClasses.next();
if (isEntity(applicationClass)) {
new EntityGenerator(
generated,
applicationClass).generateEntity();
}
}
}
}));
PackManager.v().getPack("jtp").add(new Transform("jtp.api.transform",
new ApiTransformer()));
PackManager.v().getPack("jtp").add(new Transform(
"jtp.continuation.transform",
new ContinuationTransformer(
generated)));
PackManager.v().getPack("jtp").add(new Transform(
"jtp.entity.creation.transform",
new EntityConstructionTransformer()));
Options.v().set_keep_line_number(true);
PhaseOptions.v().setPhaseOption("tag.ln", "on");
Options.v().set_exclude(asList("com.hellblazer.primeMover",
"com.hellblazer.primeMover.controllers",
"com.hellblazer.primeMover.runtime",
"com.hellblazer.primeMover.soot"));
Options.v().set_no_bodies_for_excluded(true);
Options.v().set_whole_program(true);
// Options.v().set_app(true);
soot.Main.main(args);
if (generatedDirectory != null) {
Options.v().set_output_dir(generatedDirectory);
}
for (SootClass generatedClass : generated) {
for (SootMethod method : generatedClass.getMethods()) {
method.setActiveBody(Baf.v().newBody(method.getActiveBody()));
}
PackManager.v().writeClass(generatedClass);
}
}
public String getGeneratedDirectory() {
return generatedDirectory;
}
public void setGeneratedDirectory(String generatedDirectory) {
this.generatedDirectory = generatedDirectory;
}
}