view core/src/luan/impl/LuanJavaCompiler.java @ 665:41f8fdbc3a0a

compile modules
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 07 Apr 2016 17:06:22 -0600
parents e038905512d3
children
line wrap: on
line source

package luan.impl;

import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.io.StringWriter;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.Map;
import java.util.HashMap;
import javax.tools.FileObject;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import javax.tools.JavaFileManager;
import javax.tools.StandardJavaFileManager;
import javax.tools.ForwardingJavaFileManager;


public final class LuanJavaCompiler {
	private LuanJavaCompiler() {}  // never

	private static class MyJavaFileObject extends SimpleJavaFileObject {
		final ByteArrayOutputStream baos = new ByteArrayOutputStream();

		MyJavaFileObject() {
			super(URI.create("whatever"),JavaFileObject.Kind.CLASS);
		}

		@Override public OutputStream openOutputStream() {
			return baos;
		}

		byte[] byteCode(String sourceName) {
			byte[] byteCode = baos.toByteArray();
			final int len = sourceName.length();
			int max = byteCode.length-len-3;
			outer:
			for( int i=0; true; i++ ) {
				if( i > max )
					throw new RuntimeException("len="+len);
				if( byteCode[i]==1 && (byteCode[i+1] << 8 | 0xFF & byteCode[i+2]) == len ) {
					for( int j=i+3; j<i+3+len; j++ ) {
						if( byteCode[j] != '$' )
							continue outer;
					}
					System.arraycopy(sourceName.getBytes(),0,byteCode,i+3,len);
					break;
				}
			}
			return byteCode;
		}
	}

	public static Class compile(final String className,final String sourceName,final String code) throws ClassNotFoundException {
		final int len = sourceName.length();
		StringBuilder sb = new StringBuilder(sourceName);
		for( int i=0; i<len; i++ )
			sb.setCharAt(i,'$');
		JavaFileObject sourceFile = new SimpleJavaFileObject(URI.create(sb.toString()),JavaFileObject.Kind.SOURCE) {
			@Override public CharSequence getCharContent(boolean ignoreEncodingErrors) {
				return code;
			}
			@Override public String getName() {
				return sourceName;
			}
			@Override public boolean isNameCompatible(String simpleName,JavaFileObject.Kind kind) {
				return true;
			}
		};
		final Map<String,MyJavaFileObject> map = new HashMap<String,MyJavaFileObject>();
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager sjfm = compiler.getStandardFileManager(null,null,null);
		ForwardingJavaFileManager fjfm = new ForwardingJavaFileManager(sjfm) {
			@Override public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
				if( map.containsKey(className) )
					throw new RuntimeException(className);
				MyJavaFileObject classFile = new MyJavaFileObject();
				map.put(className,classFile);
				return classFile;
			}
		};
		StringWriter out = new StringWriter();
		boolean b = compiler.getTask(out, fjfm, null, null, null, Collections.singletonList(sourceFile)).call();
		if( !b )
			throw new RuntimeException("\n"+out+"\ncode:\n"+code+"\n");
		ClassLoader cl = new ClassLoader() {
			@Override protected Class<?> findClass(String name) throws ClassNotFoundException {
				MyJavaFileObject jfo = map.get(name);
				if( jfo != null ) {
					byte[] byteCode = jfo.byteCode(sourceName);
					return defineClass(name, byteCode, 0, byteCode.length);
				}
				return super.findClass(name);
			}
		};
		return cl.loadClass(className);
	}
}