4 import java.util.regex.*;
5 import java.nio.file.*;
6 import java.nio.file.attribute.*;
11 public class Compiler {
12 private final Map<Path, Module> modules = new HashMap<>();
14 public static class FilePart extends SimpleJavaFileObject {
15 public final Path file;
16 public final String clnm;
17 public final CharSequence src;
19 private static URI dummyuri(Path file, String clnm) {
20 String clp = clnm.replace('.', '/') + Kind.SOURCE.extension;
21 return(URI.create(file.toUri().toString() + "!/" + clp));
24 public FilePart(Path file, String clnm, CharSequence src) {
25 super(dummyuri(file, clnm), Kind.SOURCE);
31 public CharSequence getCharContent(boolean ice) {
35 private static final Pattern classpat = Pattern.compile("^((public|abstract)\\s+)*(class|interface)\\s+(\\S+)");
36 public static Collection<FilePart> split(Path file) throws IOException {
37 Collection<FilePart> ret = new ArrayList<>();
38 StringBuilder head = new StringBuilder();
39 StringBuilder cur = null;
41 try(BufferedReader fp = Files.newBufferedReader(file)) {
42 for(String ln = fp.readLine(); ln != null; ln = fp.readLine()) {
43 Matcher m = classpat.matcher(ln);
46 ret.add(new FilePart(file, clnm, cur));
48 cur = new StringBuilder();
52 cur.append(ln); cur.append('\n');
54 head.append(ln); head.append('\n');
58 ret.add(new FilePart(file, clnm, cur));
64 public static class ClassOutput extends SimpleJavaFileObject {
65 public final String name;
66 private final ByteArrayOutputStream buf = new ByteArrayOutputStream();
68 public ClassOutput(String name) {
69 super(URI.create("mem://" + name), Kind.CLASS);
73 public OutputStream openOutputStream() {
78 public static class FileContext extends ForwardingJavaFileManager<JavaFileManager> {
79 public final Collection<ClassOutput> output = new ArrayList<>();
81 public FileContext(JavaCompiler javac) {
82 super(javac.getStandardFileManager(null, null, null));
85 public JavaFileObject getJavaFileForOutput(Location location, String name, JavaFileObject.Kind kind, FileObject sibling) {
86 ClassOutput cl = new ClassOutput(name);
92 public static class CompilationException extends RuntimeException {
93 public final Path file;
94 private final List<Diagnostic<? extends JavaFileObject>> messages;
96 public CompilationException(Path file, List<Diagnostic<? extends JavaFileObject>> messages) {
98 this.messages = messages;
101 public String getMessage() {
102 return(file + ": compilation failed");
105 public String messages() {
106 StringBuilder buf = new StringBuilder();
107 for(Diagnostic msg : messages)
108 buf.append(msg.toString() + "\n");
109 return(buf.toString());
112 public void printStackTrace(PrintStream out) {
113 out.print(messages());
114 super.printStackTrace(out);
118 public static Collection<ClassOutput> compile(Path file) throws IOException {
119 List<String> opt = Arrays.asList();
120 JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
122 throw(new RuntimeException("no javac present"));
123 Collection<FilePart> files;
124 files = FilePart.split(file);
125 DiagnosticCollector<JavaFileObject> out = new DiagnosticCollector<>();
126 FileContext fs = new FileContext(javac);
127 JavaCompiler.CompilationTask job = javac.getTask(null, fs, out, opt, null, files);
129 throw(new CompilationException(file, out.getDiagnostics()));
133 public static class BufferedClassLoader extends ClassLoader {
134 public final Map<String, byte[]> contents;
136 public BufferedClassLoader(Collection<ClassOutput> contents) {
137 this.contents = new HashMap<>();
138 for(ClassOutput clc : contents)
139 this.contents.put(clc.name, clc.buf.toByteArray());
142 public Class<?> findClass(String name) throws ClassNotFoundException {
143 byte[] c = contents.get(name);
145 throw(new ClassNotFoundException(name));
146 return(defineClass(name, c, 0, c.length));
150 public static class Module {
151 public final Path file;
152 private FileTime mtime = null;
153 private ClassLoader code = null;
155 private Module(Path file) {
159 public void update() throws IOException {
161 FileTime mtime = Files.getLastModifiedTime(file);
162 if((this.mtime == null) || (this.mtime.compareTo(mtime) < 0)) {
163 code = new BufferedClassLoader(compile(file));
169 public ClassLoader code() {
171 throw(new RuntimeException("module has not yet been updated"));
176 public Module module(Path file) {
177 synchronized(modules) {
178 Module ret = modules.get(file);
180 modules.put(file, ret = new Module(file));
185 private static Compiler global = null;
186 public static Compiler get() {
188 synchronized(Compiler.class) {
190 global = new Compiler();