diff --git a/src/main/java/org/mdkt/compiler/ExtendedStandardJavaFileManager.java b/src/main/java/org/mdkt/compiler/ExtendedStandardJavaFileManager.java index 1dc8a41..7f0e61f 100644 --- a/src/main/java/org/mdkt/compiler/ExtendedStandardJavaFileManager.java +++ b/src/main/java/org/mdkt/compiler/ExtendedStandardJavaFileManager.java @@ -1,15 +1,14 @@ package org.mdkt.compiler; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import javax.tools.FileObject; import javax.tools.ForwardingJavaFileManager; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; +import java.io.IOException; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; /** * Created by trung on 5/3/15. Edited by turpid-monkey on 9/25/15, completed @@ -18,20 +17,20 @@ public class ExtendedStandardJavaFileManager extends ForwardingJavaFileManager { - private List compiledCode = new ArrayList(); + private Map compiledCode; private DynamicClassLoader cl; /** * Creates a new instance of ForwardingJavaFileManager. * - * @param fileManager - * delegate to this file manager + * @param fileManager delegate to this file manager * @param cl */ protected ExtendedStandardJavaFileManager(JavaFileManager fileManager, - DynamicClassLoader cl) { + DynamicClassLoader cl, CompiledCode[] compiledCode) { super(fileManager); this.cl = cl; + this.compiledCode = Arrays.stream(compiledCode).collect(Collectors.toMap(CompiledCode::getClassName, c -> c)); } @Override @@ -40,8 +39,11 @@ public JavaFileObject getJavaFileForOutput( JavaFileObject.Kind kind, FileObject sibling) throws IOException { try { - CompiledCode innerClass = new CompiledCode(className); - compiledCode.add(innerClass); + CompiledCode innerClass = compiledCode.get(className); + if (innerClass == null) { + innerClass = new CompiledCode(className); + compiledCode.put(className, innerClass); + } cl.addCode(innerClass); return innerClass; } catch (Exception e) { diff --git a/src/main/java/org/mdkt/compiler/InMemoryJavaCompiler.java b/src/main/java/org/mdkt/compiler/InMemoryJavaCompiler.java index b853590..61a9c3d 100644 --- a/src/main/java/org/mdkt/compiler/InMemoryJavaCompiler.java +++ b/src/main/java/org/mdkt/compiler/InMemoryJavaCompiler.java @@ -1,8 +1,7 @@ package org.mdkt.compiler; -import java.util.*; - import javax.tools.*; +import java.util.*; /** * Compile Java sources in-memory @@ -65,38 +64,62 @@ public InMemoryJavaCompiler ignoreWarnings() { * @throws Exception */ public Map> compileAll() throws Exception { + Map compiled = compileAllToBytes(); + return loadCompiledBytes(compiled); + } + + public Class loadCompiledBytes(String className, byte[] compiledClasses) throws ClassNotFoundException { + Map map = new HashMap<>(); + map.put(className, compiledClasses); + return loadCompiledBytes(map).get(className); + } + + public Map> loadCompiledBytes(Map compiledClasses) throws ClassNotFoundException { + ClassLoader classLoader = new ClassLoader() { + @Override + protected Class findClass(String name) { + byte[] b = compiledClasses.get(name); + return defineClass(name, b, 0, b.length); + } + }; + Map> classes = new HashMap<>(); + for (String className : compiledClasses.keySet()) { + classes.put(className, classLoader.loadClass(className)); + } + return classes; + } + + public Map compileAllToBytes() throws Exception { if (sourceCodes.size() == 0) { throw new CompilationException("No source code to compile"); } Collection compilationUnits = sourceCodes.values(); - CompiledCode[] code; - - code = new CompiledCode[compilationUnits.size()]; + CompiledCode[] code = new CompiledCode[compilationUnits.size()]; Iterator iter = compilationUnits.iterator(); for (int i = 0; i < code.length; i++) { code[i] = new CompiledCode(iter.next().getClassName()); } DiagnosticCollector collector = new DiagnosticCollector<>(); - ExtendedStandardJavaFileManager fileManager = new ExtendedStandardJavaFileManager(javac.getStandardFileManager(null, null, null), classLoader); + ExtendedStandardJavaFileManager fileManager = new ExtendedStandardJavaFileManager(javac.getStandardFileManager(null, null, null), classLoader, code); JavaCompiler.CompilationTask task = javac.getTask(null, fileManager, collector, options, null, compilationUnits); boolean result = task.call(); if (!result || collector.getDiagnostics().size() > 0) { - StringBuffer exceptionMsg = new StringBuffer(); + StringBuilder exceptionMsg = new StringBuilder(); exceptionMsg.append("Unable to compile the source"); boolean hasWarnings = false; boolean hasErrors = false; for (Diagnostic d : collector.getDiagnostics()) { switch (d.getKind()) { - case NOTE: - case MANDATORY_WARNING: - case WARNING: - hasWarnings = true; - break; - case OTHER: - case ERROR: - default: - hasErrors = true; - break; + case NOTE: + case MANDATORY_WARNING: + case WARNING: + hasWarnings = true; + break; + case OTHER: + case ERROR: + default: + hasErrors = true; + break; } exceptionMsg.append("\n").append("[kind=").append(d.getKind()); exceptionMsg.append(", ").append("line=").append(d.getLineNumber()); @@ -107,9 +130,9 @@ public Map> compileAll() throws Exception { } } - Map> classes = new HashMap>(); - for (String className : sourceCodes.keySet()) { - classes.put(className, classLoader.loadClass(className)); + Map classes = new HashMap<>(); + for (CompiledCode aCode : code) { + classes.put(aCode.getClassName(), aCode.getByteCode()); } return classes; } @@ -139,4 +162,6 @@ public InMemoryJavaCompiler addSource(String className, String sourceCode) throw sourceCodes.put(className, new SourceCode(className, sourceCode)); return this; } + + } diff --git a/src/test/java/org/mdkt/compiler/InMemoryJavaCompilerTest.java b/src/test/java/org/mdkt/compiler/InMemoryJavaCompilerTest.java index c3c16e8..c6f0047 100644 --- a/src/test/java/org/mdkt/compiler/InMemoryJavaCompilerTest.java +++ b/src/test/java/org/mdkt/compiler/InMemoryJavaCompilerTest.java @@ -1,8 +1,5 @@ package org.mdkt.compiler; -import java.util.List; -import java.util.Map; - import org.junit.Assert; import org.junit.Rule; import org.junit.Test; @@ -10,6 +7,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.List; +import java.util.Map; + public class InMemoryJavaCompilerTest { private static final Logger logger = LoggerFactory.getLogger(InMemoryJavaCompilerTest.class); @@ -114,4 +114,25 @@ public void compile_WhenWarningsAndErrors() throws Exception { throw e; } } + + @Test + public void compile_save_run() throws Exception { + StringBuffer sourceCode = new StringBuffer(); + + sourceCode.append("package org.mdkt;\n"); + sourceCode.append("public class HelloClass {\n"); + sourceCode.append(" public java.util.List hello() { return new java.util.ArrayList(); }"); + sourceCode.append("}"); + + + InMemoryJavaCompiler compiler = InMemoryJavaCompiler.newInstance().ignoreWarnings().addSource("org.mdkt.HelloClass", sourceCode.toString()); + Map compiled = compiler.compileAllToBytes(); + byte[] helloClassCode = compiled.get("org.mdkt.HelloClass"); + //compiled code is in helloClassCode + + + Class helloClass = InMemoryJavaCompiler.newInstance().loadCompiledBytes("org.mdkt.HelloClass", helloClassCode); + List res = (List) helloClass.getMethod("hello").invoke(helloClass.newInstance()); + Assert.assertEquals(0, res.size()); + } }