Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.mdkt.compiler</groupId>
Expand Down Expand Up @@ -35,8 +36,8 @@
<url>https://github.com/trung/InMemoryJavaCompiler</url>
<connection>scm:git:git://github.com/trung/InMemoryJavaCompiler</connection>
<developerConnection>scm:git:git@github.com:trung/InMemoryJavaCompiler.git</developerConnection>
<tag>HEAD</tag>
</scm>
<tag>HEAD</tag>
</scm>

<distributionManagement>
<snapshotRepository>
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/org/mdkt/compiler/DynamicClassLoader.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.mdkt.compiler;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
Expand All @@ -14,7 +15,13 @@ public DynamicClassLoader(ClassLoader parent) {
super(parent);
}

public void setCode(CompiledCode cc) {
public void addCodes(List<CompiledCode> compiledCodes) {
for (CompiledCode cc : compiledCodes) {
customCompiledCode.put(cc.getName(), cc);
}
}

public void addCode(CompiledCode cc) {
customCompiledCode.put(cc.getName(), cc);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,76 @@
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.util.List;

/**
* Created by trung on 5/3/15.
*/
public class ExtendedStandardJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {

private CompiledCode compiledCode;
private DynamicClassLoader cl;
private DynamicClassLoader classLoader;
private List<CompiledCode> compiledCodes;

/**
* Creates a new instance of ForwardingJavaFileManager.
*
* @param fileManager delegate to this file manager
* @param cl
* @param classLoader
*/
protected ExtendedStandardJavaFileManager(JavaFileManager fileManager, CompiledCode compiledCode, DynamicClassLoader cl) {
protected ExtendedStandardJavaFileManager(JavaFileManager fileManager, CompiledCode compiledCode, DynamicClassLoader classLoader) {
super(fileManager);
this.compiledCode = compiledCode;
this.cl = cl;
this.cl.setCode(compiledCode);
this.classLoader = classLoader;
this.classLoader.addCode(compiledCode);
}

/**
* Creates a new instance of ForwardingJavaFileManager.
*
* @param fileManager delegate to this file manager
* @param classLoader
*/
protected ExtendedStandardJavaFileManager(JavaFileManager fileManager, List<CompiledCode> compiledCodes, DynamicClassLoader classLoader) {
super(fileManager);
this.compiledCodes = compiledCodes;
this.classLoader = classLoader;
this.classLoader.addCodes(compiledCodes);
}

@Override
public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
return compiledCode;

if (compiledCode != null) {
return compiledCode;
}

return getCompiledCode(className);
}


private CompiledCode getCompiledCode(String className) {
for (CompiledCode compiledCode : compiledCodes) {
if (compiledCode.getName().equals(className)) {
return compiledCode;
}
}

try {
CompiledCode innerClass = new CompiledCode(className);
compiledCodes.add(innerClass);
classLoader.addCode(innerClass);
return innerClass;
} catch (Exception e) {
e.printStackTrace();
}


return null;
}

@Override
public ClassLoader getClassLoader(JavaFileManager.Location location) {
return cl;
return classLoader;
}
}
47 changes: 47 additions & 0 deletions src/main/java/org/mdkt/compiler/InMemoryCompilerException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.mdkt.compiler;

import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.util.*;

/**************************************************
* @author Ryan Rowe (1531352)
* 2/15/16
* InMemoryJavaCompiler
**************************************************/

public class InMemoryCompilerException extends Exception {

private List<Diagnostic<? extends JavaFileObject>> diags;
private int line;

/**
* Constructs a new exception with {@code null} as its detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to {@link #initCause}.
*/
public InMemoryCompilerException(List<Diagnostic<? extends JavaFileObject>> diags) {
this.diags = diags;
line = -1;
}

public List<Map<String, Object>> getErrorList() {
List<Map<String, Object>> list = new ArrayList<>();

for (Diagnostic diag : diags) {
Map<String, Object> diagnostic = new HashMap<>();

diagnostic.put("kind", diag.getKind());
diagnostic.put("line", diag.getLineNumber() - line + 1);
diagnostic.put("message", diag.getMessage(Locale.US));

list.add(diagnostic);
}

return list;
}

public void setInsertLine(int line) {
this.line = line;
}
}
73 changes: 60 additions & 13 deletions src/main/java/org/mdkt/compiler/InMemoryJavaCompiler.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,71 @@
package org.mdkt.compiler;

import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;
import java.util.Arrays;
import java.util.*;

/**
* Created by trung on 5/3/15.
*/
public class InMemoryJavaCompiler {
static JavaCompiler javac = ToolProvider.getSystemJavaCompiler();

public static Class<?> compile(String className, String sourceCodeInText) throws Exception {
SourceCode sourceCode = new SourceCode(className, sourceCodeInText);
CompiledCode compiledCode = new CompiledCode(className);
Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(sourceCode);
DynamicClassLoader cl = new DynamicClassLoader(ClassLoader.getSystemClassLoader());
ExtendedStandardJavaFileManager fileManager = new ExtendedStandardJavaFileManager(javac.getStandardFileManager(null, null, null), compiledCode, cl);
JavaCompiler.CompilationTask task = javac.getTask(null, fileManager, null, null, null, compilationUnits);
boolean result = task.call();
return cl.loadClass(className);
private static final Iterable<String> options = Collections.singletonList("-Xlint:unchecked");
private static JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
DynamicClassLoader classLoader = new DynamicClassLoader(ClassLoader.getSystemClassLoader());
private DiagnosticCollector<JavaFileObject> collector;

public InMemoryJavaCompiler() {
this.collector = new DiagnosticCollector<>();
}
}

Map<String, SourceCode> clazzCode = new HashMap<>();

public void addSource(String className, String sourceCodeInText)
throws Exception {
clazzCode.put(className, new SourceCode(className, sourceCodeInText));
}

public Map<String, Class<?>> compileAll() throws Exception {

Collection<SourceCode> compilationUnits = clazzCode.values();
List<CompiledCode> compiledCodes = new ArrayList<>();
Iterator<SourceCode> iterator = compilationUnits.iterator();
for (SourceCode sourceCode : compilationUnits) {
compiledCodes.add(new CompiledCode(sourceCode.getClassName()));
}

ExtendedStandardJavaFileManager fileManager = new ExtendedStandardJavaFileManager(javac.getStandardFileManager(null, null, null), compiledCodes, classLoader);

JavaCompiler.CompilationTask task = javac.getTask(null, fileManager, collector,
options, null, compilationUnits);


try {
boolean result = task.call();

if (!result || collector.getDiagnostics().size() > 0) {
throw new InMemoryCompilerException(collector.getDiagnostics());
}

Map<String, Class<?>> classes = new HashMap<String, Class<?>>();
for (String className : clazzCode.keySet()) {
classes.put(className, classLoader.loadClass(className));
}
return classes;
} catch (ClassFormatError e) {
throw new InMemoryCompilerException(collector.getDiagnostics());
}

}


public Class<?> compile(String className, String sourceCodeInText) throws Exception {
addSource(className, sourceCodeInText);
Map<String, Class<?>> compiled = compileAll();
Class<?> compiledClass = compiled.get(className);
return compiledClass;
}


}
7 changes: 7 additions & 0 deletions src/main/java/org/mdkt/compiler/SourceCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,20 @@
*/
public class SourceCode extends SimpleJavaFileObject {
private String contents = null;
private String className;

public SourceCode(String className, String contents) throws Exception {
super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
this.contents = contents;
this.className = className;
}

@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return contents;
}

public String getClassName() {
return className;
}
}
41 changes: 40 additions & 1 deletion src/test/java/org/mdkt/compiler/InMemoryJavaCompilerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,60 @@
import org.junit.Assert;
import org.junit.Test;

import java.util.Map;

/**
* Created by trung on 5/3/15.
*/
public class InMemoryJavaCompilerTest {

@Test
public void compile_whenTypical() throws Exception {
StringBuilder sourceCode = new StringBuilder();

sourceCode.append("package org.mdkt;\n");
sourceCode.append("public class HelloClass {\n");
sourceCode.append(" public String hello() { return \"hello\"; }");
sourceCode.append("}");

InMemoryJavaCompiler inMemoryJavaCompiler = new InMemoryJavaCompiler();
Class<?> helloClass = inMemoryJavaCompiler.compile("org.mdkt.HelloClass", sourceCode.toString());
Assert.assertNotNull(helloClass);
Assert.assertEquals(1, helloClass.getDeclaredMethods().length);
}


@Test
public void compile_severalFiles() throws Exception {
String cls1 = "public class A{ public B b() { return new B(); }}";
String cls2 = "public class B{ public String toString() { return \"B!\"; }}";

InMemoryJavaCompiler compiler = new InMemoryJavaCompiler();
compiler.addSource("A", cls1);
compiler.addSource("B", cls2);
Map<String, Class<?>> compiled = compiler.compileAll();
;
Assert.assertNotNull(compiled.get("A"));
Assert.assertNotNull(compiled.get("B"));

Class<?> aClass = compiled.get("A");
Object a = aClass.newInstance();
Assert.assertEquals("B!", aClass.getMethod("b").invoke(a).toString());
}


@Test
public void compile_filesWithInnerClasses() throws Exception {
StringBuffer sourceCode = new StringBuffer();

sourceCode.append("package org.mdkt;\n");
sourceCode.append("public class HelloClass {\n");
sourceCode.append(" private static class InnerHelloWorld { int inner; }\n");
sourceCode.append(" public String hello() { return \"hello\"; }");
sourceCode.append("}");

Class<?> helloClass = InMemoryJavaCompiler.compile("org.mdkt.HelloClass", sourceCode.toString());
InMemoryJavaCompiler inMemoryJavaCompiler = new InMemoryJavaCompiler();
Class<?> helloClass = inMemoryJavaCompiler.compile("org.mdkt.HelloClass", sourceCode.toString());
Assert.assertNotNull(helloClass);
Assert.assertEquals(1, helloClass.getDeclaredMethods().length);
}
Expand Down