Skip to content
Snippets Groups Projects
Commit 1b271d44 authored by Sven Robertz's avatar Sven Robertz
Browse files

started working on dynamic generation and compilation

parent 984ddf79
Branches
Tags
No related merge requests found
...@@ -195,6 +195,36 @@ aspect Java_Void { ...@@ -195,6 +195,36 @@ aspect Java_Void {
aspect Java_CodeGen { aspect Java_CodeGen {
public void Program.J_gen(PrintStream ps, String pack) throws IOException {
Java_env env;
/*
// Registration class
env = new Java_env(ps);
if (pack != null && pack.length() > 0) {
env.println("package " + pack + ";");
}
env.println("public class LabCommRegister {");
env.println();
env.indent();
Java_emitTypeRegister(env);
env.unindent();
env.println();
env.println("}");
// env.close();
*/
env = new Java_env(ps);
for (int i = 0; i < getNumDecl(); i++) {
Decl d = getDecl(i);
try {
d.Java_emitClass(env, pack);
} catch (Error e) {
System.err.println(d.getName());
throw e;
}
}
env.close();
}
public void Program.J_gen(String dir, String pack) throws IOException { public void Program.J_gen(String dir, String pack) throws IOException {
Java_env env; Java_env env;
/* /*
...@@ -225,6 +255,29 @@ aspect Java_CodeGen { ...@@ -225,6 +255,29 @@ aspect Java_CodeGen {
} }
} }
/** Experimental method for generating code to a map <classname, source>
*/
public void Program.J_gen(Map<String,String> src, String pack) throws IOException {
Java_env env;
/*
// Registration class was commented out, so got removed in this copy
*/
for (int i = 0; i < getNumDecl(); i++) {
Decl d = getDecl(i);
try {
ByteArrayOutputStream bs = new ByteArrayOutputStream();
PrintStream out = new PrintStream(bs);
env = new Java_env(out);
d.Java_emitClass(env, pack);
env.close();
src.put(d.getName(), bs.toString());
} catch (Error e) {
System.err.println(d.getName());
throw e;
}
}
}
} }
aspect Java_Register { aspect Java_Register {
......
public class DynamicPart {
};
import java.lang.reflect.InvocationTargetException;
public interface InRAMCompiler {
/**
* Delete a class from the RAM storage, and destroy its ClassLoader, to
* allow it to be unloaded (and later replaced).
* @param className - the class to be unloaded. pkgName (as given to the constructor) is added to the name.
*/
public void deleteClass(String className);
public void compile(String name, String srcStr)
throws ClassNotFoundException, IllegalArgumentException,
SecurityException, IllegalAccessException,
InvocationTargetException, NoSuchMethodException;
/**
* Load className.class out of cache
* @param className
* @return the class object
* @throws ClassNotFoundException
*/
public Class<?> load(String className) throws ClassNotFoundException;
}
\ No newline at end of file
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
/**
* On-the-fly, incremental, all-in-RAM compilation (no disk files used).
* Based on an example by Piotr Kobzda at
* http://groups.google.com/group/pl.co...7010d1cce043d0
*
* Each class is compiled in its own compilation unit, and newer
* classes can call or reference older classes.
*
* Written and debugged against Java 1.6.0 Beta 2 build 86, in Eclipse
* 3.2 Jim Goodwin July 25 2006
*
* Adapted to jdk 1.6.0_11 and adapted with more class loader delegation for
* working with custom class loaders (e.g., OSGi) by Sven Robertz
*/
public class InRAMCompilerJavax implements InRAMCompiler {
// Source for both test classes. They go in package
// "just.generated"
private ClassLoader loader;
private DiagnosticCollector<JavaFileObject> diagnostics;
private JavaCompiler compiler;
private RAMFileManager jfm;
private Map<String, JavaFileObject> output;
private ClassLoader providedClassLoader;
private String pkgName; //package for the generated classes
// private String getClassPath()
// {
// ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// URL[] urls = ((URLClassLoader) classLoader).getURLs();
// StringBuilder buf = new StringBuilder(1000);
// buf.append(".");
// String separator = System.getProperty("path.separator");
// for (URL url : urls) {
// buf.append(separator).append(url.getFile());
// }
// return buf.toString();
// }
public static void main(String[] args) throws Exception {
InRAMCompiler irc = new InRAMCompilerJavax("just.generated", null);
final String SRC_HelloIf = "package just.generated;\n"
+ "public class HelloIf {\n"
+ " public static class Foo {\n"
+ " public interface Bar {\n"
+ " public String foo();\n"
+ " }\n"
+ " }\n"
+ "}\n";
final String SRC_Hello1 = "package just.generated;\n"
+ "public class Hello1 {\n"
+ " public static void main(String... args) {\n"
+ " System.out.println(new Hello2()); \n" + "}}\n";
final String SRC_Hello2 = "package just.generated;\n"
+ "public class Hello2 implements HelloIf.Foo.Bar{\n"
+ " public String foo() { return \"foo\";}\n"
+ " public String toString() {\n"
+ " return \"hello!\"+foo();\n}}\n";
irc.compile("HelloIf", SRC_HelloIf);
irc.compile("Hello2", SRC_Hello2);
irc.compile("Hello1", SRC_Hello1);
Class<?> c = irc.load("Hello1");
// Run the 'main' method of the Hello class.
c.getMethod("main", String[].class).invoke(null,
new Object[] { new String[0] });
}
/**
*
* @param pkgName - The package for the generated classes. The source code must only contain classes in this package.
* @param cl - An optional (i.e., may be null) classloader that is also searched
*/
public InRAMCompilerJavax(String pkgName, ClassLoader cl) {
this.pkgName = pkgName;
providedClassLoader = cl;
compiler = ToolProvider.getSystemJavaCompiler();
// A map of from class names to the RAMJavaFileObject that holds
// the compiled-code for that class. This is the cache of
// compiled classes.
output = new HashMap<String, JavaFileObject>();
createNewClassLoader(providedClassLoader);
}
/* (non-Javadoc)
* @see se.lth.cs.sven.rosettaTest.cc.InRAMCompiler#deleteClass(java.lang.String)
*/
public void deleteClass(String className) {
output.remove(pkgName+"."+className);
// System.out.println("\nstored classes: " + output.keySet());
createNewClassLoader(providedClassLoader);
}
/**
* Create a new class loader, possibly replacing the existing one.
* This method is called by the constructor, and from deleteClass, to
* allow unloading of generated classes.
* @param cl
*/
private void createNewClassLoader(ClassLoader cl) {
// A loader that searches our cache first.
loader = new RAMClassLoader(output, cl);
diagnostics = new DiagnosticCollector<JavaFileObject>();
// Create a JavaFileManager which uses our DiagnosticCollector,
// and creates a new RAMJavaFileObject for the class, and
// registers it in our cache
StandardJavaFileManager sjfm = compiler.getStandardFileManager(diagnostics, null, null);
jfm = new RAMFileManager(sjfm, output, loader);
}
/*
* Help routine to convert a string to a URI.
*/
static URI toURI(String name) {
try {
return new URI(name);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
/* (non-Javadoc)
* @see se.lth.cs.sven.rosettaTest.cc.InRAMCompiler#compile(java.lang.String, java.lang.String)
*/
public void compile(String name, String srcStr) throws ClassNotFoundException, IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
// TODO Auto-generated method stub
// Create source file objects
SourceJavaFileObject src = new SourceJavaFileObject(name,
srcStr);
// Compile source code. call() causes it to run.
// Compiler options
// String myCP = getClassPath()+":/home/sven/eclipse/workspace_sven_test_3.4/RosettaOSGi-API/bin";
// System.out.println("classpath: "+myCP);
List<String> options = new ArrayList<String>();
// options.add("-classpath");
// options.add(myCP);
// options.add(getClassPath());
CompilationTask task = compiler.getTask(null, jfm,
diagnostics, options, null, Arrays.asList(src));
jfm.isCompiling(true);
if (!task.call()) {
for (Diagnostic<?> dm : diagnostics.getDiagnostics())
System.err.println(dm);
throw new RuntimeException("Compilation of task "+name+" failed");
}
jfm.isCompiling(false);
// Traces the classes now found in the cache
System.out.println("\nInRAMCompiler: generated classes = " + output.keySet());
}
/* (non-Javadoc)
* @see se.lth.cs.sven.rosettaTest.cc.InRAMCompiler#load(java.lang.String)
*/
public Class<?> load(String className) throws ClassNotFoundException {
jfm.isCompiling(false);
Class<?> c = Class.forName(pkgName+"."+className, false,
loader);
return c;
}
/*
* A JavaFileObject class for source code, that just uses a String for
* the source code.
*/
private class SourceJavaFileObject extends SimpleJavaFileObject {
private final String classText;
SourceJavaFileObject(String className, final String classText) {
super(InRAMCompilerJavax.toURI(className + ".java"),
Kind.SOURCE);
this.classText = classText;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors)
throws IOException, IllegalStateException,
UnsupportedOperationException {
return classText;
}
}
/*
* A JavaFileManager that presents the contents of the cache as a file
* system to the compiler. To do this, it must do four things:
*
* It remembers our special loader and returns it from getClassLoader()
*
* It maintains our cache, adding class "files" to it when the compiler
* calls getJavaFileForOutput
*
* It implements list() to add the classes in our cache to the result
* when the compiler is asking for the classPath. This is the key
trick:
* it is what makes it possible for the second compilation task to
* compile a call to a class from the first task.
*
* It implements inferBinaryName to give the right answer for cached
* classes.
*/
private class RAMFileManager extends
ForwardingJavaFileManager<StandardJavaFileManager> {
private final Map<String, JavaFileObject> output;
private final ClassLoader ldr;
public RAMFileManager(StandardJavaFileManager sjfm,
Map<String, JavaFileObject> output, ClassLoader ldr) {
super(sjfm);
this.output = output;
this.ldr = ldr;
}
public JavaFileObject getJavaFileForOutput(Location location,
String name, Kind kind, FileObject sibling)
throws IOException {
JavaFileObject jfo = new RAMJavaFileObject(name, kind);
output.put(name, jfo);
return jfo;
}
public ClassLoader getClassLoader(JavaFileManager.Location
location) {
return ldr;
}
@Override
public String inferBinaryName(Location loc, JavaFileObject jfo) {
// System.out.println("RAMFileManager.inferBinaryName:"+loc+", "+jfo);
String result;
if (loc == StandardLocation.CLASS_PATH
&& jfo instanceof RAMJavaFileObject)
result = jfo.getName();
else
result = super.inferBinaryName(loc, jfo);
return result;
}
private boolean restrictPackage=true;
public void isCompiling(boolean b) {
restrictPackage = !b;
}
@Override
public Iterable<JavaFileObject> list(Location loc, String pkg,
Set<Kind> kind, boolean recurse) throws IOException {
Iterable<JavaFileObject> result = super.list(loc, pkg, kind,
recurse);
if (loc == StandardLocation.CLASS_PATH
&& (!restrictPackage || pkg.equals("just.generated"))
&& kind.contains(JavaFileObject.Kind.CLASS)) {
ArrayList<JavaFileObject> temp = new ArrayList<JavaFileObject>(
3);
for (JavaFileObject jfo : result)
temp.add(jfo);
for (Entry<String, JavaFileObject> entry : output
.entrySet()) {
temp.add(entry.getValue());
}
result = temp;
} else {
result = super.list(loc, pkg, kind, recurse);
}
return result;
}
}
/**
* A JavaFileObject that uses RAM instead of disk to store the file. It
* gets written to by the compiler, and read from by the loader.
*/
private class RAMJavaFileObject extends SimpleJavaFileObject {
ByteArrayOutputStream baos;
RAMJavaFileObject(String name, Kind kind) {
super(InRAMCompilerJavax.toURI(name), kind);
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors)
throws IOException, IllegalStateException,
UnsupportedOperationException {
throw new UnsupportedOperationException();
}
@Override
public InputStream openInputStream() throws IOException,
IllegalStateException, UnsupportedOperationException {
return new ByteArrayInputStream(baos.toByteArray());
}
@Override
public OutputStream openOutputStream() throws IOException,
IllegalStateException, UnsupportedOperationException {
return baos = new ByteArrayOutputStream();
}
}
/**
* A class loader that loads what's in the cache by preference, and if
* it can't find the class there, loads from the standard parent.
*
* It is important that everything in the demo use the same loader, so
* we pass this to the JavaFileManager as well as calling it directly.
*/
private final class RAMClassLoader extends ClassLoader {
private final Map<String, JavaFileObject> output;
private ClassLoader classLoader;
RAMClassLoader(Map<String, JavaFileObject> output, ClassLoader cl) {
this.output = output;
this.classLoader = cl;
testGetResources();
}
private void testGetResources() {
Package[] ps = getPackages();
for (Package package1 : ps) {
System.out.println(package1.getName());
}
}
@Override
protected Class<?> findClass(String name)
throws ClassNotFoundException {
JavaFileObject jfo = output.get(name);
try {
//XXX This is a hack! Look in "parent" class loader first, to find correct Service instances.
if(classLoader != null) {
System.out.println("RAMClassLoader.findClass: getResource (package): "+classLoader.getResource("se/lth/cs/sven/rosettaTest"));
System.out.println("RAMClassLoader.findClass: getResource: "+classLoader.getResource("se/lth/cs/sven/rosettaTest/Constraint.class"));
try {
Class<?> c = classLoader.loadClass(name);
if(c != null) {
return c;
}
} catch( ClassNotFoundException ex) {
System.out.println(ex.getMessage());
}
}
if (jfo != null) {
byte[] bytes = ((RAMJavaFileObject) jfo).baos.toByteArray();
return defineClass(name, bytes, 0, bytes.length);
}
} catch(Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
}
}
This directory contains an embryo of an example of how to
generate and compile a labcomm endpoint on the fly.
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import se.lth.control.labcomm.LabCommEncoderChannel;
import java.io.FileInputStream;
import java.io.InputStream;
import se.lth.control.labcomm.LabCommDecoderChannel;
public class StaticPart {
/**
* Simple encoder
*/
public class Encoder {
LabCommEncoderChannel encoder;
public Encoder(OutputStream out)
throws Exception
{
encoder = new LabCommEncoderChannel(out);
theTwoInts.register(encoder);
IntString.register(encoder);
TwoArrays.register(encoder);
}
public void doEncode() throws java.io.IOException {
TwoInts x = new TwoInts();
x.a = 17;
x.b = 42;
IntString y = new IntString();
y.x = 37;
y.s = "Testing, testing";
System.out.println("Encoding theTwoInts, a="+x.a+", b="+x.b);
theTwoInts.encode(encoder, x);
System.out.println("Encoding IntString, x="+y.x+", s="+y.s);
IntString.encode(encoder, y);
}
}
public class Decoder
implements theTwoInts.Handler, anotherTwoInts.Handler, IntString.Handler
{
LabCommDecoderChannel decoder;
public Decoder(InputStream in)
throws Exception
{
decoder = new LabCommDecoderChannel(in);
theTwoInts.register(decoder, this);
anotherTwoInts.register(decoder, this);
IntString.register(decoder, this);
TwoArrays.register(decoder, this);
TwoFixedArrays.register(decoder, this);
try {
System.out.println("Running decoder.");
decoder.run();
} catch (java.io.EOFException e) {
System.out.println("Decoder reached end of file.");
}
}
public void printTwoInts(TwoInts d) throws java.io.IOException {
System.out.println("a="+d.a+", b="+d.b);
}
public void handle_theTwoInts(TwoInts d) throws java.io.IOException {
System.out.print("Got theTwoInts: ");
printTwoInts(d);
}
public void handle_anotherTwoInts(TwoInts d) throws java.io.IOException {
System.out.print("Got anotherheTwoInts: ");
printTwoInts(d);
}
public void handle_IntString(IntString d) throws java.io.IOException {
System.out.println("Got IntString, x="+d.x+", s="+d.s);
}
}
public static void main(String[] arg) throws Exception {
FileOutputStream fos = new FileOutputStream(arg[0]);
FileInputStream fis = new FileInputStream(new File(arg[0]))
Encoder example = new Encoder(fos);
Decoder example = new Decoder(fis);
example.doEncode();
fos.close();
fis.close();
}
}
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import AST.LabCommParser;
import AST.LabCommScanner;
import AST.Program;
import beaver.Parser.Exception;
public class TestLabCommCompiler {
/**
* @param args
*/
public static void main(String[] args) {
/* input data: */
String prg ="sample struct { int x; int y; int z;}foo; sample int bar;";
HashMap <String, String> handlers = new HashMap<String, String>();
handlers.put("foo", "public void handle_foo(foo value) {\nSystem.out.println(value.x);\nSystem.out.println(value.y);\nSystem.out.println(value.z);}");
handlers.put("bar", "public void handle_bar(int value) {System.out.println(value);}");
generateCode(prg, handlers);
}
public static void generateCode(String prg, HashMap<String, String> handlers) {
Program ast = null;
InputStream in = new ByteArrayInputStream(prg.getBytes());
LabCommScanner scanner = new LabCommScanner(in);
LabCommParser parser = new LabCommParser();
Collection errors = new LinkedList();
try {
Program p = (Program)parser.parse(scanner);
p.errorCheck(errors);
if (errors.isEmpty()) {
ast = p;
} else {
System.out.println("*** Errors:");
for (Iterator iter = errors.iterator(); iter.hasNext(); ) {
String s = (String)iter.next();
System.out.println(s);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
if (ast != null) {
InRAMCompiler irc = handleAst(ast, handlers);
dummyTest(irc);
} else {
System.err.println("compilation failed");
}
}
/* dummy test creating instances of sample and handler, and calling handle*/
private static void dummyTest(InRAMCompiler irc) {
try {
Class hc = irc.load("gen_fooHandler");
Object h = hc.newInstance();
Class fc = irc.load("foo");
Object f = fc.newInstance();
Field x = fc.getDeclaredField("x");
Field y = fc.getDeclaredField("y");
Field z = fc.getDeclaredField("z");
x.setInt(f, 10);
y.setInt(f, 11);
z.setInt(f, 12);
Method m;
try {
m = hc.getDeclaredMethod("handle_foo", fc);
m.invoke(h, f);
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static InRAMCompiler handleAst(Program ast, HashMap<String, String> handlers) {
Map<String, String> foo = new HashMap<String, String>();
try {
ast.J_gen(foo, "labcomm.generated");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Generated labcomm code:");
InRAMCompiler irc = new InRAMCompilerJavax("labcomm.generated", null);
Iterator<String> i = foo.keySet().iterator();
while(i.hasNext()){
final String name = i.next();
final String src = foo.get(name);
System.out.println("***"+name+"\n"+src);
StringBuilder sb = new StringBuilder();
sb.append("package labcomm.generated;\n");
sb.append("public class gen_"+name+"Handler implements "+name+".Handler {\n");
sb.append(handlers.get(name));
sb.append("}\n");
System.out.println("-------------------------------------");
System.out.println(sb.toString());
try {
irc.compile(name, src);
irc.compile("gen_"+name+"Handler", sb.toString());
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
System.out.println("================================");
}
return irc;
}
}
#dummy script to test the on-the-fly compilation
javac -cp .:../../compiler/labComm.jar:../../lib/java/labcomm.jar:../../lib/tools/beaver.jar:../../lib/tools/beaver-rt.jar:../../lib/tools/jastadd2.jar:../../lib/tools/JFlex.jar:../../lib/tools/proj.jar TestLabCommCompiler.java
java -cp .:../../compiler/labComm.jar:../../lib/java/labcomm.jar:../../lib/tools/beaver.jar:../../lib/tools/beaver-rt.jar:../../lib/tools/jastadd2.jar:../../lib/tools/JFlex.jar:../../lib/tools/proj.jar TestLabCommCompiler
typedef struct {
int a;
int b;
} TwoInts;
sample TwoInts theTwoInts;
sample TwoInts anotherTwoInts;
sample struct {
int x;
string s;
} IntString;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment