diff --git a/src/main/java/run/myCode/compiler/InMemoryJavaFileObject.java b/src/main/java/run/myCode/compiler/InMemoryJavaFileObject.java index 022f8c0..08a4af5 100644 --- a/src/main/java/run/myCode/compiler/InMemoryJavaFileObject.java +++ b/src/main/java/run/myCode/compiler/InMemoryJavaFileObject.java @@ -34,7 +34,11 @@ public InMemoryJavaFileObject(String fileName, String contents) { // Using a non-file scheme is important because ECJ will otherwise try // to open the path returned by getName() from the filesystem which // causes "File ... is missing" errors when compiling in memory. - super(URI.create("mem:///" + fileName), Kind.SOURCE); + // Use the "string" URI scheme which the Eclipse compiler treats as an + // in-memory source and therefore does not attempt to resolve on the + // filesystem. Any other scheme will cause ECJ to verify the file on + // disk and emit a "File ... is missing" error. + super(URI.create("string:///" + fileName), Kind.SOURCE); // Save the file's contents this.contents = contents; @@ -57,15 +61,6 @@ public InputStream openInputStream() throws IOException { return new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)); } - @Override - public String getName() { - String path = toUri().getPath(); - if (path.startsWith("/")) { - return path.substring(1); - } - return path; - } - @Override public String toString() { String s = this.getName() + ":\n"; diff --git a/src/main/java/run/myCode/compiler/JavaCodeCompiler.java b/src/main/java/run/myCode/compiler/JavaCodeCompiler.java index f98295e..5e14b44 100644 --- a/src/main/java/run/myCode/compiler/JavaCodeCompiler.java +++ b/src/main/java/run/myCode/compiler/JavaCodeCompiler.java @@ -17,7 +17,7 @@ import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; -import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler; +import javax.tools.ToolProvider; public class JavaCodeCompiler { @@ -56,7 +56,10 @@ public static FromMemoryClassLoader compile(Iterable f final FromMemoryClassLoader classLoader = new FromMemoryClassLoader(urlcl); // get system compiler: - final JavaCompiler compiler = new EclipseCompiler(); + // Use the standard Java compiler provided by the JDK. This avoids ECJ + // attempting to resolve in-memory sources on disk which resulted in + // "File ... is missing" errors during tests. + final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); // create a diagnostic listener for compilation diagnostic message processing on // compilation WARNING/ERROR @@ -76,11 +79,11 @@ public static FromMemoryClassLoader compile(Iterable f options = new ArrayList<>(); } - // Build the classpath from the current folder and the system classpath - StringBuilder classpathBuilder = - new StringBuilder("." - + System.getProperty("path.separator") - + System.getProperty("java.class.path")); + // Build the classpath from the current folder and the system classpath + StringBuilder classpathBuilder = + new StringBuilder("." + + System.getProperty("path.separator") + + System.getProperty("java.class.path")); // Add any included jar files from the lib folder to the classpath (wildcard isn't working) try { diff --git a/src/main/java/zss/compiler/InMemoryJavaFileObject.java b/src/main/java/zss/compiler/InMemoryJavaFileObject.java index 4e21c15..838f98f 100644 --- a/src/main/java/zss/compiler/InMemoryJavaFileObject.java +++ b/src/main/java/zss/compiler/InMemoryJavaFileObject.java @@ -1,46 +1,56 @@ -package zss.compiler; - -import java.io.IOException; -import java.net.URI; - -import javax.tools.SimpleJavaFileObject; - -import javax.tools.JavaFileObject.Kind; - -/** - * java File Object represents an in-memory java source file so there is no need - * to put the source file on hard disk - */ -@SuppressWarnings("unused") -public class InMemoryJavaFileObject extends SimpleJavaFileObject { - - private String contents = null; - - /** - * Create a Java file object in memory with a name and text contents. - * - * @param fileName the name of the file, with extension - * @param contents the contents of the file as a single string object - * @throws Exception - */ - public InMemoryJavaFileObject(String fileName, String contents) { - // Create a file object with a classname instead of a filename by - // removing the file's extension and convert the . separators into slashes - super(URI.create("file:///" + fileName), Kind.SOURCE); - - // Save the file's contents - this.contents = contents; - } - - @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { - return contents; - } - - public String toString() { - String s = this.getName() + ":\n"; - s += this.contents; - - return s; - } -} +package zss.compiler; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.charset.StandardCharsets; + +import javax.tools.SimpleJavaFileObject; + +import javax.tools.JavaFileObject.Kind; + +/** + * java File Object represents an in-memory java source file so there is no need + * to put the source file on hard disk + */ +@SuppressWarnings("unused") +public class InMemoryJavaFileObject extends SimpleJavaFileObject { + + private String contents = null; + + /** + * Create a Java file object in memory with a name and text contents. + * + * @param fileName the name of the file, with extension + * @param contents the contents of the file as a single string object + */ + public InMemoryJavaFileObject(String fileName, String contents) { + // Use the "string" URI scheme so the compiler treats this source as an + // in-memory file and does not attempt to resolve it on disk. Using the + // "file" scheme caused ECJ to look for a physical file and fail with + // "File ... is missing" errors. + super(URI.create("string:///" + fileName), Kind.SOURCE); + + // Save the file's contents + this.contents = contents; + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + return contents; + } + + @Override + public InputStream openInputStream() throws IOException { + return new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public String toString() { + String s = this.getName() + ":\n"; + s += this.contents; + + return s; + } +} diff --git a/src/main/java/zss/compiler/MemoryCompiler.java b/src/main/java/zss/compiler/MemoryCompiler.java index 45ab749..5014082 100644 --- a/src/main/java/zss/compiler/MemoryCompiler.java +++ b/src/main/java/zss/compiler/MemoryCompiler.java @@ -12,13 +12,14 @@ import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; -import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler; +import javax.tools.ToolProvider; -/** - * An instance of the Eclipse Compiler for Java that compiles memory files into - * memory byte code - */ -public class MemoryCompiler { +/** + * Compiles in-memory source files into byte code using the standard JDK + * compiler. This avoids relying on the Eclipse compiler which attempted to + * resolve sources on disk. + */ +public class MemoryCompiler { /** * Compile Java source files into memory * @@ -36,8 +37,10 @@ public static FromMemoryClassLoader compile(Iterable f // classloader final FromMemoryClassLoader classLoader = new FromMemoryClassLoader(urlcl); - // get system compiler: - final JavaCompiler compiler = new EclipseCompiler(); + // Use the standard JDK compiler to avoid ECJ attempting to read + // temporary files from disk which caused "File ... is missing" + // errors when compiling in memory. + final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); // create a diagnostic listener for compilation diagnostic message processing on // compilation WARNING/ERROR @@ -48,10 +51,10 @@ public static FromMemoryClassLoader compile(Iterable f // specify options for compiler List options = new ArrayList<>(); - options.addAll( - Arrays.asList("-classpath", MemoryCompiler.class.getProtectionDomain().getCodeSource().getLocation() - + ":" + System.getProperty("java.class.path"))); - options.addAll(Arrays.asList("-1.8", "-nowarn")); + options.addAll( + Arrays.asList("-classpath", MemoryCompiler.class.getProtectionDomain().getCodeSource().getLocation() + + ":" + System.getProperty("java.class.path"))); + options.addAll(Arrays.asList("--release", "8", "-nowarn")); Writer out = new PrintWriter(System.out); JavaCompiler.CompilationTask task = compiler.getTask(out, fileManager, diag, options, null, files);