/*
* Copyright 2012 Phil Pratt-Szeliga and other contributors
* http://chirrup.org/
*
* See the file LICENSE for copying permission.
*/
package org.trifort.rootbeer.entry;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.*;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.trifort.rootbeer.compiler.*;
import org.trifort.rootbeer.configuration.Configuration;
import org.trifort.rootbeer.configuration.RootbeerPaths;
import org.trifort.rootbeer.generate.opencl.tweaks.CudaTweaks;
import org.trifort.rootbeer.generate.opencl.tweaks.NativeCpuTweaks;
import org.trifort.rootbeer.generate.opencl.tweaks.Tweaks;
import org.trifort.rootbeer.util.*;
import pack.Pack;
import soot.*;
import soot.options.Options;
import soot.rbclassload.DfsInfo;
import soot.rbclassload.ListClassTester;
import soot.rbclassload.ListMethodTester;
import soot.rbclassload.MethodTester;
import soot.rbclassload.RootbeerClassLoader;
import soot.util.JasminOutputStream;
public class RootbeerCompiler {
private String m_classOutputFolder;
private String m_jimpleOutputFolder;
private String m_provider;
private boolean m_enableClassRemapping;
private MethodTester m_entryDetector;
private Set<String> m_runtimePackages;
public RootbeerCompiler(){
clearOutputFolders();
m_classOutputFolder = RootbeerPaths.v().getOutputClassFolder();
m_jimpleOutputFolder = RootbeerPaths.v().getOutputJimpleFolder();
if(Configuration.compilerInstance().getMode() == Configuration.MODE_GPU){
Tweaks.setInstance(new CudaTweaks());
} else {
Tweaks.setInstance(new NativeCpuTweaks());
}
m_enableClassRemapping = true;
m_runtimePackages = new HashSet<String>();
addRuntimePackages();
}
private void addRuntimePackages(){
m_runtimePackages.add("com.lmax.disruptor.");
m_runtimePackages.add("org.trifort.rootbeer.compiler.");
m_runtimePackages.add("org.trifort.rootbeer.configuration.");
m_runtimePackages.add("org.trifort.rootbeer.entry.");
m_runtimePackages.add("org.trifort.rootbeer.generate.");
m_runtimePackages.add("org.trifort.rootbeer.runtime.");
m_runtimePackages.add("org.trifort.rootbeer.runtime2.");
m_runtimePackages.add("org.trifort.rootbeer.test.");
m_runtimePackages.add("org.trifort.rootbeer.util.");
}
public void disableClassRemapping(){
m_enableClassRemapping = false;
}
public void compile(String main_jar, List<String> lib_jars, List<String> dirs, String dest_jar) {
}
private void setupSoot(String jar_filename, String rootbeer_jar, boolean runtests){
RootbeerClassLoader.v().setUserJar(jar_filename);
//extractJar(jar_filename);
List<String> proc_dir = new ArrayList<String>();
proc_dir.add(jar_filename);
Options.v().set_allow_phantom_refs(true);
Options.v().set_rbclassload(true);
Options.v().set_prepend_classpath(true);
Options.v().set_process_dir(proc_dir);
if(m_enableClassRemapping){
Options.v().set_rbclassload_buildcg(true);
}
if(rootbeer_jar.equals("") == false){
Options.v().set_soot_classpath(rootbeer_jar);
}
//Options.v().set_rbcl_remap_all(Configuration.compilerInstance().getRemapAll());
Options.v().set_rbcl_remap_all(false);
Options.v().set_rbcl_remap_prefix("org.trifort.rootbeer.runtime.remap.");
RootbeerClassLoader.v().addEntryMethodTester(m_entryDetector);
ListClassTester ignore_packages = new ListClassTester();
ignore_packages.addPackage("com.lmax.disruptor.");
ignore_packages.addPackage("org.trifort.rootbeer.compressor.");
ignore_packages.addPackage("org.trifort.rootbeer.deadmethods.");
ignore_packages.addPackage("org.trifort.rootbeer.compiler.");
ignore_packages.addPackage("org.trifort.rootbeer.configuration.");
ignore_packages.addPackage("org.trifort.rootbeer.entry.");
ignore_packages.addPackage("org.trifort.rootbeer.generate.");
ignore_packages.addPackage("org.trifort.rootbeer.test.");
if(!runtests){
ignore_packages.addPackage("org.trifort.rootbeer.testcases.");
}
ignore_packages.addPackage("org.trifort.rootbeer.util.");
ignore_packages.addPackage("pack.");
ignore_packages.addPackage("jasmin.");
ignore_packages.addPackage("soot.");
ignore_packages.addPackage("beaver.");
ignore_packages.addPackage("polyglot.");
ignore_packages.addPackage("org.antlr.");
ignore_packages.addPackage("java_cup.");
ignore_packages.addPackage("ppg.");
ignore_packages.addPackage("antlr.");
ignore_packages.addPackage("jas.");
ignore_packages.addPackage("scm.");
ignore_packages.addPackage("org.xmlpull.v1.");
ignore_packages.addPackage("android.util.");
ignore_packages.addPackage("android.content.res.");
ignore_packages.addPackage("org.apache.commons.codec.");
RootbeerClassLoader.v().addDontFollowClassTester(ignore_packages);
ListClassTester keep_packages = new ListClassTester();
for(String runtime_class : m_runtimePackages){
keep_packages.addPackage(runtime_class);
}
RootbeerClassLoader.v().addToSignaturesClassTester(keep_packages);
RootbeerClassLoader.v().addNewInvoke("java.lang.StringBuilder");
ListMethodTester follow_tester = new ListMethodTester();
follow_tester.addSignature("<java.lang.String: void <init>()>");
follow_tester.addSignature("<java.lang.String: void <init>(char[])>");
follow_tester.addSignature("<java.lang.StringBuilder: void <init>()>");
follow_tester.addSignature("<java.lang.Boolean: java.lang.String toString(boolean)>");
follow_tester.addSignature("<java.lang.Character: java.lang.String toString(char)>");
follow_tester.addSignature("<java.lang.Double: java.lang.String toString(double)>");
follow_tester.addSignature("<java.lang.Float: java.lang.String toString(float)>");
follow_tester.addSignature("<java.lang.Integer: java.lang.String toString(int)>");
follow_tester.addSignature("<java.lang.Long: java.lang.String toString(long)>");
follow_tester.addSignature("<org.trifort.rootbeer.runtime.Sentinal: void <init>()>");
follow_tester.addSignature("<org.trifort.rootbeer.runtimegpu.GpuException: void <init>()>");
follow_tester.addSignature("<org.trifort.rootbeer.runtimegpu.GpuException: org.trifort.rootbeer.runtimegpu.GpuException arrayOutOfBounds(int,int,int)>");
follow_tester.addSignature("<org.trifort.rootbeer.runtime.Serializer: void <init>(org.trifort.rootbeer.runtime.Memory,org.trifort.rootbeer.runtime.Memory)>");
follow_tester.addSignature("<org.trifort.rootbeer.testcases.rootbeertest.serialization.CovarientTest: void <init>()>");
RootbeerClassLoader.v().addFollowMethodTester(follow_tester);
if(runtests){
RootbeerClassLoader.v().addFollowClassTester(new TestCaseFollowTester());
}
if(Configuration.compilerInstance().getKeepMains()){
MainTester main_tester = new MainTester();
RootbeerClassLoader.v().addFollowMethodTester(main_tester);
}
ListMethodTester dont_dfs_tester = new ListMethodTester();
CompilerSetup setup = new CompilerSetup();
for(String no_dfs : setup.getDontDfs()){
dont_dfs_tester.addSignature(no_dfs);
}
RootbeerClassLoader.v().addDontFollowMethodTester(dont_dfs_tester);
ForcedFields forced_fields = new ForcedFields();
for(String field_sig : forced_fields.get()){
RootbeerClassLoader.v().loadField(field_sig);
}
ListMethodTester to_sig_methods = new ListMethodTester();
to_sig_methods.addSignature("<java.lang.Object: int hashCode()>");
to_sig_methods.addSignature("<java.io.PrintStream: void println(java.lang.String)>");
to_sig_methods.addSignature("<java.io.PrintStream: void println(int)>");
to_sig_methods.addSignature("<java.io.PrintStream: void println(long)>");
RootbeerClassLoader.v().addToSignaturesMethodTester(to_sig_methods);
RootbeerClassLoader.v().addClassRemapping("java.util.concurrent.atomic.AtomicLong", "org.trifort.rootbeer.remap.GpuAtomicLong");
RootbeerClassLoader.v().addClassRemapping("org.trifort.rootbeer.testcases.rootbeertest.remaptest.CallsPrivateMethod", "org.trifort.rootbeer.remap.DoesntCallPrivateMethod");
RootbeerClassLoader.v().loadNecessaryClasses();
}
public void compile(String jar_filename, String outname, String test_case) throws Exception {
TestCaseEntryPointDetector detector = new TestCaseEntryPointDetector(test_case);
m_entryDetector = detector;
CurrJarName jar_name = new CurrJarName();
setupSoot(jar_filename, jar_name.get(), true);
m_provider = detector.getProvider();
List<SootMethod> kernel_methods = RootbeerClassLoader.v().getEntryPoints();
compileForKernels(outname, kernel_methods, jar_filename);
}
public void compile(String jar_filename, String outname) throws Exception {
compile(jar_filename, outname, false);
}
public void compile(String jar_filename, String outname, boolean run_tests) throws Exception {
m_entryDetector = new KernelEntryPointDetector(run_tests);
CurrJarName jar_name = new CurrJarName();
setupSoot(jar_filename, jar_name.get(), run_tests);
List<SootMethod> kernel_methods = RootbeerClassLoader.v().getEntryPoints();
compileForKernels(outname, kernel_methods, jar_filename);
}
private void compileForKernels(String outname, List<SootMethod> kernel_methods, String jar_filename) throws Exception {
if(kernel_methods.isEmpty()){
System.out.println("There are no kernel classes. Please implement the following interface to use rootbeer:");
System.out.println("org.trifort.runtime.Kernel");
System.exit(0);
}
Transform2 transform2 = new Transform2();
for(SootMethod kernel_method : kernel_methods){
System.out.println("running transform2 on: "+kernel_method.getSignature()+"...");
RootbeerClassLoader.v().loadDfsInfo(kernel_method);
DfsInfo dfs_info = RootbeerClassLoader.v().getDfsInfo();
RootbeerDfs rootbeer_dfs = new RootbeerDfs();
rootbeer_dfs.run(dfs_info);
dfs_info.expandArrayTypes();
dfs_info.finalizeTypes();
SootClass soot_class = kernel_method.getDeclaringClass();
transform2.run(soot_class.getName());
}
System.out.println("writing classes out...");
Iterator<SootClass> iter = Scene.v().getClasses().iterator();
while(iter.hasNext()){
SootClass soot_class = iter.next();
if(soot_class.isLibraryClass()){
continue;
}
String class_name = soot_class.getName();
boolean write = true;
for(String runtime_class : m_runtimePackages){
if(class_name.startsWith(runtime_class)){
write = false;
break;
}
}
Iterator<SootClass> ifaces = soot_class.getInterfaces().iterator();
while(ifaces.hasNext()){
SootClass iface = ifaces.next();
if(iface.getName().startsWith("org.trifort.rootbeer.test.")){
write = false;
break;
}
}
if(write){
writeJimpleFile(class_name);
writeClassFile(class_name);
}
}
makeOutJar(jar_filename);
pack(outname);
}
public void pack(String outjar_name) throws Exception {
Pack p = new Pack();
String main_jar = RootbeerPaths.v().getOutputJarFolder() + File.separator + "partial-ret.jar";
List<String> lib_jars = new ArrayList<String>();
CurrJarName jar_name = new CurrJarName();
lib_jars.add(jar_name.get());
p.run(main_jar, lib_jars, outjar_name);
}
public void makeOutJar(String jar_filename) throws Exception {
JarEntryHelp.mkdir(RootbeerPaths.v().getOutputJarFolder() + File.separator);
String outfile = RootbeerPaths.v().getOutputJarFolder() + File.separator + "partial-ret.jar";
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outfile));
addJarInputManifestFiles(zos, jar_filename);
addOutputClassFiles(zos);
addConfigurationFile(zos);
zos.flush();
zos.close();
}
private void addJarInputManifestFiles(ZipOutputStream zos, String jar_filename) throws Exception {
ZipInputStream jin = new ZipInputStream(new FileInputStream(jar_filename));
while(true){
ZipEntry jar_entry = jin.getNextEntry();
if(jar_entry == null){
break;
}
if(jar_entry.getName().contains("META-INF")){
writeFileToOutput(jin, jar_entry, zos);
}
}
jin.close();
}
private void addOutputClassFiles(ZipOutputStream zos) throws Exception {
List<File> output_class_files = getFiles(RootbeerPaths.v().getOutputClassFolder());
for(File f : output_class_files){
writeFileToOutput(f, zos, RootbeerPaths.v().getOutputClassFolder());
}
}
private List<File> getFiles(String path) {
File f = new File(path);
List<File> ret = new ArrayList<File>();
getFiles(ret, f);
return ret;
}
private void getFiles(List<File> total_files, File dir){
File[] files = dir.listFiles();
for(File f : files){
if(f.isDirectory()){
getFiles(total_files, f);
} else {
total_files.add(f);
}
}
}
private String makeJarFileName(File f, String folder) {
try {
String abs_path = f.getAbsolutePath();
if(f.isDirectory()){
abs_path += File.separator;
}
folder += File.separator;
folder = folder.replace("\\", "\\\\");
String[] tokens = abs_path.split(folder);
String ret = tokens[1];
if(File.separator.equals("\\")){
ret = ret.replace("\\", "/");
}
return ret;
} catch(Exception ex){
throw new RuntimeException(ex);
}
}
private void addConfigurationFile(ZipOutputStream zos) throws IOException {
String folder_name = "org/trifort/rootbeer/runtime/";
String name = folder_name + "config.txt";
ZipEntry entry = new ZipEntry(name);
entry.setSize(1);
byte[] contents = new byte[2];
contents[0] = (byte) Configuration.compilerInstance().getMode();
if(Configuration.compilerInstance().getExceptions()){
contents[1] = (byte) 1;
} else {
contents[1] = (byte) 0;
}
entry.setCrc(calcCrc32(contents));
zos.putNextEntry(entry);
zos.write(contents);
zos.flush();
File file = new File(RootbeerPaths.v().getOutputClassFolder()+File.separator+folder_name);
if(file.exists() == false){
file.mkdirs();
}
FileOutputStream fout = new FileOutputStream(RootbeerPaths.v().getOutputClassFolder()+File.separator+name);
fout.write(contents);
fout.flush();
fout.close();
}
private void writeFileToOutput(ZipInputStream jin, ZipEntry jar_entry, ZipOutputStream zos) throws Exception {
if(jar_entry.isDirectory() == false){
List<byte[]> buffered = new ArrayList<byte[]>();
int total_size = 0;
while(true){
byte[] buffer = new byte[4096];
int len = jin.read(buffer);
if(len == -1){
break;
}
total_size += len;
byte[] truncated = new byte[len];
for(int i = 0; i < len; ++i){
truncated[i] = buffer[i];
}
buffered.add(truncated);
}
ZipEntry entry = new ZipEntry(jar_entry.getName());
entry.setSize(total_size);
entry.setCrc(jar_entry.getCrc());
zos.putNextEntry(entry);
for(byte[] buffer : buffered){
zos.write(buffer);
}
zos.flush();
} else {
zos.putNextEntry(jar_entry);
}
}
private void writeFileToOutput(File f, ZipOutputStream zos, String folder) throws Exception {
String name = makeJarFileName(f, folder);
ZipEntry entry = new ZipEntry(name);
byte[] contents = readFile(f);
entry.setSize(contents.length);
entry.setCrc(calcCrc32(contents));
zos.putNextEntry(entry);
int wrote_len = 0;
int total_len = contents.length;
while(wrote_len < total_len){
int len = 4096;
int len_left = total_len - wrote_len;
if(len > len_left)
len = len_left;
zos.write(contents, wrote_len, len);
wrote_len += len;
}
zos.flush();
}
private long calcCrc32(byte[] buffer){
CRC32 crc = new CRC32();
crc.update(buffer);
return crc.getValue();
}
private byte[] readFile(File f) throws Exception {
List<Byte> contents = new ArrayList<Byte>();
byte[] buffer = new byte[4096];
FileInputStream fin = new FileInputStream(f);
while(true){
int len = fin.read(buffer);
if(len == -1)
break;
for(int i = 0; i < len; ++i){
contents.add(buffer[i]);
}
}
fin.close();
byte[] ret = new byte[contents.size()];
for(int i = 0; i < contents.size(); ++i)
ret[i] = contents.get(i);
return ret;
}
private void writeJimpleFile(String cls){
try {
SootClass c = Scene.v().getSootClass(cls);
JimpleWriter writer = new JimpleWriter();
writer.write(classNameToFileName(cls, true), c);
} catch(Exception ex){
System.out.println("Error writing .jimple: "+cls);
}
}
private void writeClassFile(String cls, String filename){
FileOutputStream fos = null;
OutputStream out1 = null;
PrintWriter writer = null;
SootClass c = Scene.v().getSootClass(cls);
List<String> before_sigs = getMethodSignatures(c);
try {
fos = new FileOutputStream(filename);
out1 = new JasminOutputStream(fos);
writer = new PrintWriter(new OutputStreamWriter(out1));
new soot.jimple.JasminClass(c).print(writer);
} catch(Exception ex){
System.out.println("Error writing .class: "+cls);
ex.printStackTrace(System.out);
List<String> after_sigs = getMethodSignatures(c);
System.out.println("Before sigs: ");
printMethodSigs(before_sigs);
System.out.println("After sigs: ");
printMethodSigs(after_sigs);
} finally {
try {
writer.flush();
writer.close();
out1.close();
fos.close();
} catch(Exception ex){
ex.printStackTrace();
}
}
}
private List<String> getMethodSignatures(SootClass c){
List<String> ret = new ArrayList<String>();
List<SootMethod> methods = c.getMethods();
for(SootMethod method : methods){
ret.add(method.getSignature());
}
return ret;
}
private void printMethodSigs(List<String> sigs){
for(String sig : sigs){
System.out.println(" "+sig);
}
}
private void writeClassFile(String cls) {
writeClassFile(cls, classNameToFileName(cls, false));
}
private String classNameToFileName(String cls, boolean jimple){
File f;
if(jimple)
f = new File(m_jimpleOutputFolder);
else
f = new File(m_classOutputFolder);
cls = cls.replace(".", File.separator);
if(jimple)
cls += ".jimple";
else
cls += ".class";
cls = f.getAbsolutePath()+File.separator + cls;
File f2 = new File(cls);
String folder = f2.getParent();
new File(folder).mkdirs();
return cls;
}
private void clearOutputFolders() {
DeleteFolder deleter = new DeleteFolder();
deleter.delete(RootbeerPaths.v().getOutputJarFolder());
deleter.delete(RootbeerPaths.v().getOutputClassFolder());
deleter.delete(RootbeerPaths.v().getOutputShimpleFolder());
deleter.delete(RootbeerPaths.v().getJarContentsFolder());
}
public String getProvider() {
return m_provider;
}
}