simplify LuaCompiler interface and add Lua2Java utility class.

This commit is contained in:
James Roseborough
2010-07-25 22:31:43 +00:00
parent 97b4162423
commit 69bbae70a1
14 changed files with 238 additions and 110 deletions

View File

@@ -56,15 +56,9 @@ public class LoadState {
/** Interface for the compiler, if it is installed. */ /** Interface for the compiler, if it is installed. */
public interface LuaCompiler { public interface LuaCompiler {
/** Compile into a prototype, without taking the additional step of create a LuaFunction or LuaClosure */ /** Load into a Closure or LuaFunction from a Stream and initializes the environment
public Prototype compile(int firstByte, InputStream stream, String name) throws IOException; * @throws IOException */
public LuaFunction load(InputStream stream, String filename, LuaValue env) throws IOException;
/** Load into a Closure or LuaFunction, with the supplied initial environment */
public LuaFunction load(int firstByte, InputStream stream, String name, LuaValue env) throws IOException;
/** Load into a LuaFunction given a prototype. May compile into a class, or return a LuaClosure
* @param filename TODO*/
public LuaFunction load(Prototype p, String filename, LuaValue env);
} }
/** Compiler instance, if installed */ /** Compiler instance, if installed */
@@ -275,26 +269,25 @@ public class LoadState {
} }
public static LuaFunction load( InputStream stream, String name, LuaValue env ) throws IOException { public static LuaFunction load( InputStream stream, String name, LuaValue env ) throws IOException {
Prototype p = compile( stream, name );
if ( compiler != null ) if ( compiler != null )
return compiler.load(p, name, env); return compiler.load(stream, name, env);
else else {
int firstByte = stream.read();
if ( firstByte != LUA_SIGNATURE[0] )
throw new LuaError("no compiler");
Prototype p = loadBinaryChunk( firstByte, stream, name );
return new LuaClosure( p, env ); return new LuaClosure( p, env );
}
} }
public static Prototype compile( InputStream stream, String name ) throws IOException { public static Prototype loadBinaryChunk( int firstByte, InputStream stream, String name ) throws IOException {
int c = stream.read();
if ( c != LUA_SIGNATURE[0] ) {
if ( compiler != null )
return compiler.compile(c, stream, name);
throw new LuaError("no compiler");
}
// check rest of signature // check rest of signature
for ( int i=1; i<4; i++ ) { if ( firstByte != LUA_SIGNATURE[0]
if ( stream.read() != LUA_SIGNATURE[i] ) || stream.read() != LUA_SIGNATURE[1]
throw new IllegalArgumentException("bad signature"); || stream.read() != LUA_SIGNATURE[2]
} || stream.read() != LUA_SIGNATURE[3] )
throw new IllegalArgumentException("bad signature");
// load file as a compiled chunk // load file as a compiled chunk
String sname = getSourceName(name); String sname = getSourceName(name);

View File

@@ -25,6 +25,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Hashtable; import java.util.Hashtable;
import org.luaj.vm2.LoadState;
import org.luaj.vm2.LocVars; import org.luaj.vm2.LocVars;
import org.luaj.vm2.Lua; import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaClosure; import org.luaj.vm2.LuaClosure;
@@ -41,12 +42,14 @@ import org.luaj.vm2.LoadState.LuaCompiler;
*/ */
public class LuaC extends Lua implements LuaCompiler { public class LuaC extends Lua implements LuaCompiler {
public static final LuaC instance = new LuaC();
/** Install the compiler so that LoadState will first /** Install the compiler so that LoadState will first
* try to use it when handed bytes that are * try to use it when handed bytes that are
* not already a compiled lua chunk. * not already a compiled lua chunk.
*/ */
public static void install() { public static void install() {
org.luaj.vm2.LoadState.compiler = new LuaC(); org.luaj.vm2.LoadState.compiler = instance;
} }
protected static void _assert(boolean b) { protected static void _assert(boolean b) {
@@ -156,43 +159,26 @@ public class LuaC extends Lua implements LuaCompiler {
} }
public int nCcalls; public int nCcalls;
Hashtable strings = new Hashtable(); Hashtable strings;
/** Utility method to invoke the compiler for an input stream protected LuaC() {}
*/
public static Prototype compile(InputStream is, String name) throws IOException { private LuaC(Hashtable strings) {
return new LuaC().compile(is.read(), is, name); this.strings = strings;
} }
/** Load into a Closure or LuaFunction, with the supplied initial environment */ /** Load into a Closure or LuaFunction, with the supplied initial environment */
public static LuaFunction load(InputStream is, String name, LuaValue env) throws IOException { public LuaFunction load(InputStream stream, String name, LuaValue env) throws IOException {
return new LuaC().load(is.read(), is, name, env); Prototype p = compile( stream, name );
}
/** Load into a Closure or LuaFunction, with the supplied initial environment */
public LuaFunction load(int firstByte, InputStream stream, String name, LuaValue env) throws IOException {
Prototype p = compile(firstByte, stream, name);
return new LuaClosure( p, env ); return new LuaClosure( p, env );
} }
/** Compile source bytes into a LPrototype. /** Compile a prototype or load as a binary chunk */
* public static Prototype compile(InputStream stream, String name) throws IOException {
* Try to compile the file, and return the Prototype on success, int firstByte = stream.read();
* or throw LuaErrorException on syntax error or I/O Exception return ( firstByte == '\033' )?
* LoadState.loadBinaryChunk(firstByte, stream, name):
* @param firstByte the first byte from the InputStream. (new LuaC(new Hashtable())).luaY_parser(firstByte, stream, name);
* This can be read by the client and tested to see if it is already a binary chunk.
* @param stream InputStream to read from.
* @param name Name of the chunk
* @return null if the first byte indicates it is a binary chunk,
* a LPrototype instance if it can be compiled,
* or an exception is thrown if there is an error.
* @throws IOException if an I/O exception occurs
* @throws LuaError if there is a syntax error.
*/
public Prototype compile(int firstByte, InputStream stream, String name) throws IOException {
LuaC compiler = new LuaC();
return compiler.luaY_parser(firstByte, stream, name);
} }
/** Parse the input */ /** Parse the input */

View File

@@ -159,7 +159,7 @@ public class luac {
private void processScript( InputStream script, String chunkname, OutputStream out ) throws IOException { private void processScript( InputStream script, String chunkname, OutputStream out ) throws IOException {
try { try {
// create the chunk // create the chunk
Prototype chunk = LuaC.compile(script, chunkname); Prototype chunk = LuaC.instance.compile(script, chunkname);
// list the chunk // list the chunk
if (list) if (list)

View File

@@ -0,0 +1,168 @@
package org.luaj.vm2.lua2java;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Arrays;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
import org.luaj.vm2.LoadState;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.LoadState.LuaCompiler;
import org.luaj.vm2.ast.Chunk;
import org.luaj.vm2.compiler.LuaC;
import org.luaj.vm2.parser.LuaParser;
public class Lua2Java implements LuaCompiler {
public static final Lua2Java instance = new Lua2Java();
public static final void install() {
LoadState.compiler = instance;
}
private Lua2Java() {
}
public LuaFunction load(InputStream stream, String filename, LuaValue env) throws IOException {
// get first byte
if ( ! stream.markSupported() )
stream = new BufferedInputStream( stream );
stream.mark( 1 );
int firstByte = stream.read();
stream.reset();
// we can only sompile sources
if ( firstByte != '\033' ) {
final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
if (compiler == null)
LuaValue.error("no java compiler");
// break into package and class
if ( filename.endsWith( ".lua") )
filename = filename.substring(0, filename.length()-4);
String s = filename.replace('\\', '/').replace('/','.').replaceAll("[^\\w]", "_");
int p = s.lastIndexOf('.');
final String packageName = p>=0? s.substring(0,p): null;
final String className = toClassname( s.substring(p+1) );
// open output file
final String pkgSubdir = (packageName!=null? packageName.replace('.','/'): "");
final String srcDirRoot = "lua2java/src";
final String binDirRoot = "lua2java/classes";
final String srcDirname = srcDirRoot+"/"+pkgSubdir;
final String binDirname = binDirRoot+"/"+pkgSubdir;
final String srcFilename = srcDirname + "/" + className + ".java";
// make directories
new File(srcDirname).mkdirs();
new File(binDirname).mkdirs();
// generate java source
try {
LuaParser parser = new LuaParser(stream,"ISO8859-1");
Chunk chunk = parser.Chunk();
File source = new File(srcFilename);
Writer writer = new OutputStreamWriter( new FileOutputStream(source) );
new JavaCodeGen(chunk,writer,packageName,className);
writer.close();
// set up output location
Iterable<? extends File> dest = Arrays.asList(new File[] { new File(binDirRoot) });
StandardJavaFileManager fm = compiler.getStandardFileManager( null, null, null);
fm.setLocation(StandardLocation.CLASS_OUTPUT, dest);
// compile the file
Iterable<? extends JavaFileObject> compilationUnits = fm.getJavaFileObjects(source);
CompilationTask task = compiler.getTask(null, fm, null, null, null, compilationUnits);
boolean success = task.call();
// instantiate, config and return
if (success) {
// create instance
ClassLoader cl = new ClassLoader() {
public Class findClass(String classname) throws ClassNotFoundException {
if ( classname.startsWith(className) ) {
File f = new File( binDirname+"/"+classname+".class");
long n = f.length();
byte[] b = new byte[(int) n];
try {
DataInputStream dis = new DataInputStream( new FileInputStream(f) );
dis.readFully(b);
} catch ( Exception e ) {
throw new RuntimeException("failed to read class bytes: "+e );
}
return defineClass(classname, b, 0, b.length);
}
return super.findClass(classname);
}
};
Class clazz = cl.loadClass(className);
Object instance = clazz.newInstance();
LuaFunction value = (LuaFunction) instance;
value.setfenv( env );
return value;
} else {
}
} catch ( Exception e ) {
LuaValue.error("compile task failed: "+e);
}
// report compilation error
LuaValue.error("compile task failed:");
return null;
}
// fall back to plain compiler
return LuaC.instance.load( stream, filename, env);
}
/** Convert lua filename to valid class name */
public static final String toClassname( String filename ) {
int n=filename.length();
int j=n;
if ( filename.endsWith(".lua") )
j -= 4;
for ( int k=0; k<j; k++ ) {
char c = filename.charAt(k);
if ( (!isClassnamePart(c)) || (c=='/') || (c=='\\') ) {
StringBuffer sb = new StringBuffer(j);
for ( int i=0; i<j; i++ ) {
c = filename.charAt(i);
sb.append(
(isClassnamePart(c))? c:
((c=='/') || (c=='\\'))? '.': '_' );
}
return sb.toString();
}
}
return n==j? filename: filename.substring(0,j);
}
private static final boolean isClassnamePart(char c) {
if ( (c>='a'&&c<='z') || (c>='A'&&c<='Z') || (c>='0'&&c<='9') )
return true;
switch ( c ) {
case '.':
case '$':
case '_':
return true;
default:
return false;
}
}
}

View File

@@ -1,13 +1,11 @@
package org.luaj.vm2.luajc; package org.luaj.vm2.luajc;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Prototype; import org.luaj.vm2.Prototype;
import org.luaj.vm2.compiler.LuaC;
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2010 Luaj.org. All rights reserved. * Copyright (c) 2010 Luaj.org. All rights reserved.
@@ -40,25 +38,20 @@ public class JavaLoader extends ClassLoader {
this.env = env; this.env = env;
} }
public LuaValue load( InputStream is, String classname, String filename ) throws IOException { public LuaFunction load( Prototype p, String classname, String filename ) {
Prototype p = LuaC.compile(is, classname);
return load( p, classname, filename );
}
public LuaValue load( Prototype p, String classname, String filename ) {
JavaGen jg = new JavaGen( p, classname, filename ); JavaGen jg = new JavaGen( p, classname, filename );
return load( jg ); return load( jg );
} }
public LuaValue load( JavaGen jg ) { public LuaFunction load( JavaGen jg ) {
include( jg ); include( jg );
return load( jg.classname ); return load( jg.classname );
} }
public LuaValue load(String classname) { public LuaFunction load(String classname) {
try { try {
Class c = loadClass( classname ); Class c = loadClass( classname );
LuaValue v = (LuaValue) c.newInstance(); LuaFunction v = (LuaFunction) c.newInstance();
v.setfenv(env); v.setfenv(env);
return v; return v;
} catch ( Exception e ) { } catch ( Exception e ) {

View File

@@ -37,7 +37,6 @@ public class LuaJC implements LuaCompiler {
private static final String NON_IDENTIFIER = "[^a-zA-Z0-9_$]"; private static final String NON_IDENTIFIER = "[^a-zA-Z0-9_$]";
private static LuaJC instance; private static LuaJC instance;
private LuaC luac;
public static LuaJC getInstance() { public static LuaJC getInstance() {
if ( instance == null ) if ( instance == null )
@@ -54,12 +53,11 @@ public class LuaJC implements LuaCompiler {
} }
public LuaJC() { public LuaJC() {
luac = new LuaC();
} }
public Hashtable compileAll(InputStream script, String classname, String filename) throws IOException { public Hashtable compileAll(InputStream script, String classname, String filename) throws IOException {
Hashtable h = new Hashtable(); Hashtable h = new Hashtable();
Prototype p = luac.compile(script.read(), script, classname); Prototype p = LuaC.instance.compile(script, classname);
JavaGen gen = new JavaGen(p, classname, filename); JavaGen gen = new JavaGen(p, classname, filename);
insert( h, gen ); insert( h, gen );
return h; return h;
@@ -71,25 +69,11 @@ public class LuaJC implements LuaCompiler {
insert(h, gen.inners[i]); insert(h, gen.inners[i]);
} }
public Prototype compile(int firstByte, InputStream stream, String name) throws IOException { public LuaFunction load(InputStream stream, String name, LuaValue env) throws IOException {
return luac.compile(firstByte, stream, name); Prototype p = LuaC.instance.compile(stream, name);
} String classname = name.endsWith(".lua")? name.substring(0,name.length()-4): name;
public LuaFunction load(int firstByte, InputStream stream, String name, LuaValue env) throws IOException {
return load( compile(firstByte, stream, name), name, env );
}
public LuaFunction load(Prototype p, String filename, LuaValue env) {
String classname = filename.endsWith(".lua")? filename.substring(0,filename.length()-4): filename;
classname = classname.replaceAll(NON_IDENTIFIER, "_"); classname = classname.replaceAll(NON_IDENTIFIER, "_");
JavaGen gen = new JavaGen(p, classname, filename);
JavaLoader loader = new JavaLoader(env); JavaLoader loader = new JavaLoader(env);
loader.include(gen); return loader.load(p, classname, name);
return (LuaFunction) loader.load(p, classname, filename);
} }
public LuaValue load(InputStream stream, String name, LuaValue env) throws IOException {
return load(stream.read(), stream, name, env);
}
} }

View File

@@ -56,9 +56,7 @@ public class TestLuaJ {
// compile into a chunk, or load as a class // compile into a chunk, or load as a class
InputStream is = new ByteArrayInputStream( script.getBytes() ); InputStream is = new ByteArrayInputStream( script.getBytes() );
Prototype p = LuaC.compile(is, "script"); LuaValue chunk = LuaC.instance.load(is, "script",_G);
print( p );
LuaValue chunk = new LuaClosure(p,_G);
chunk.call(); chunk.call();
} }

View File

@@ -24,6 +24,7 @@ package org.luaj.vm2;
import junit.framework.Test; import junit.framework.Test;
import junit.framework.TestSuite; import junit.framework.TestSuite;
import org.luaj.vm2.lua2java.Lua2Java;
import org.luaj.vm2.luajc.LuaJC; import org.luaj.vm2.luajc.LuaJC;
/** /**
@@ -62,9 +63,21 @@ public class CompatibiltyTest {
TestSuite suite = new TestSuite("Compatibility Tests"); TestSuite suite = new TestSuite("Compatibility Tests");
suite.addTest( new TestSuite( JseCompatibilityTest.class, "JSE Tests" ) ); suite.addTest( new TestSuite( JseCompatibilityTest.class, "JSE Tests" ) );
suite.addTest( new TestSuite( JmeCompatibilityTest.class, "JME Tests" ) ); suite.addTest( new TestSuite( JmeCompatibilityTest.class, "JME Tests" ) );
suite.addTest( new TestSuite( Lua2JavaTest.class, "Lua2Java Tests" ) );
suite.addTest( new TestSuite( JseBytecodeTest.class, "JSE Bytecode Tests" ) ); suite.addTest( new TestSuite( JseBytecodeTest.class, "JSE Bytecode Tests" ) );
return suite; return suite;
} }
public static class Lua2JavaTest extends CompatibiltyTestSuite {
public Lua2JavaTest() {
super(ScriptDrivenTest.PlatformType.LUA2JAVA);
}
protected void setUp() throws Exception {
super.setUp();
System.setProperty("Lua2Java", "false");
Lua2Java.install();
}
}
public static class JmeCompatibilityTest extends CompatibiltyTestSuite { public static class JmeCompatibilityTest extends CompatibiltyTestSuite {
public JmeCompatibilityTest() { public JmeCompatibilityTest() {

View File

@@ -45,7 +45,7 @@ public class FragmentsTest extends TestCase {
if ( true ) { if ( true ) {
chunk = LuaJC.getInstance().load(is,name,_G); chunk = LuaJC.getInstance().load(is,name,_G);
} else { } else {
chunk = (new LuaC()).load( is, name, _G ); chunk = LuaC.instance.load( is, name, _G );
} }
Varargs actual = chunk.invoke(); Varargs actual = chunk.invoke();
assertEquals( expected.narg(), actual.narg() ); assertEquals( expected.narg(), actual.narg() );

View File

@@ -181,7 +181,7 @@ public class LuaOperationsTest extends TestCase {
try { try {
LuaTable _G = org.luaj.vm2.lib.JsePlatform.standardGlobals(); LuaTable _G = org.luaj.vm2.lib.JsePlatform.standardGlobals();
InputStream is = new ByteArrayInputStream(script.getBytes("UTF-8")); InputStream is = new ByteArrayInputStream(script.getBytes("UTF-8"));
return LuaC.compile(is, name); return LuaC.instance.compile(is, name);
} catch (Exception e) { } catch (Exception e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();

View File

@@ -39,7 +39,7 @@ public class ScriptDrivenTest extends TestCase {
public static final boolean nocompile = "true".equals(System.getProperty("nocompile")); public static final boolean nocompile = "true".equals(System.getProperty("nocompile"));
public enum PlatformType { public enum PlatformType {
JME, JSE, LUAJIT, JME, JSE, LUAJIT, LUA2JAVA,
} }
private final PlatformType platform; private final PlatformType platform;
@@ -57,6 +57,7 @@ public class ScriptDrivenTest extends TestCase {
default: default:
case JSE: case JSE:
case LUAJIT: case LUAJIT:
case LUA2JAVA:
_G = org.luaj.vm2.lib.JsePlatform.debugGlobals(); _G = org.luaj.vm2.lib.JsePlatform.debugGlobals();
break; break;
case JME: case JME:

View File

@@ -54,7 +54,7 @@ abstract public class AbstractUnitTests extends TestCase {
// compile in memory // compile in memory
InputStream is = new ByteArrayInputStream(lua); InputStream is = new ByteArrayInputStream(lua);
Prototype p = LuaC.compile(is, "@" + dir + "/" + file); Prototype p = LuaC.instance.compile(is, "@" + dir + "/" + file);
String actual = protoToString(p); String actual = protoToString(p);
// load expected value from jar // load expected value from jar
@@ -97,7 +97,7 @@ abstract public class AbstractUnitTests extends TestCase {
protected Prototype loadFromBytes(byte[] bytes, String script) protected Prototype loadFromBytes(byte[] bytes, String script)
throws IOException { throws IOException {
InputStream is = new ByteArrayInputStream(bytes); InputStream is = new ByteArrayInputStream(bytes);
return LoadState.compile(is, script); return LoadState.loadBinaryChunk(is.read(), is, script);
} }
protected String protoToString(Prototype p) { protected String protoToString(Prototype p) {

View File

@@ -11,6 +11,7 @@ import junit.framework.TestCase;
import org.luaj.vm2.LoadState; import org.luaj.vm2.LoadState;
import org.luaj.vm2.LuaClosure; import org.luaj.vm2.LuaClosure;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Prototype; import org.luaj.vm2.Prototype;
@@ -83,10 +84,10 @@ public class DumpLoadEndianIntTest extends TestCase {
// compile into prototype // compile into prototype
InputStream is = new ByteArrayInputStream(script.getBytes()); InputStream is = new ByteArrayInputStream(script.getBytes());
Prototype p = LuaC.compile(is, "script"); Prototype p = LuaC.instance.compile(is, "script");
// double check script result before dumping // double check script result before dumping
LuaClosure f = new LuaClosure(p, _G); LuaFunction f = new LuaClosure(p, _G);
LuaValue r = f.call(); LuaValue r = f.call();
String actual = r.tojstring(); String actual = r.tojstring();
assertEquals( expectedPriorDump, actual ); assertEquals( expectedPriorDump, actual );
@@ -107,8 +108,7 @@ public class DumpLoadEndianIntTest extends TestCase {
// load again using compiler // load again using compiler
is = new ByteArrayInputStream(dumped); is = new ByteArrayInputStream(dumped);
p = LoadState.compile(is, "dumped"); f = LoadState.load(is, "dumped", _G);
f = new LuaClosure(p, _G);
r = f.call(); r = f.call();
actual = r.tojstring(); actual = r.tojstring();
assertEquals( expectedPostDump, actual ); assertEquals( expectedPostDump, actual );

View File

@@ -5,14 +5,11 @@ import java.io.InputStream;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.luaj.vm2.LuaClosure;
import org.luaj.vm2.LuaDouble; import org.luaj.vm2.LuaDouble;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaInteger; import org.luaj.vm2.LuaInteger;
import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Print;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.lib.BaseLib;
import org.luaj.vm2.lib.JsePlatform; import org.luaj.vm2.lib.JsePlatform;
public class SimpleTests extends TestCase { public class SimpleTests extends TestCase {
@@ -27,12 +24,7 @@ public class SimpleTests extends TestCase {
private void doTest( String script ) { private void doTest( String script ) {
try { try {
InputStream is = new ByteArrayInputStream( script.getBytes("UTF8") ); InputStream is = new ByteArrayInputStream( script.getBytes("UTF8") );
Prototype p = LuaC.compile( is, "script" ); LuaFunction c = LuaC.instance.load( is, "script", _G );
assertNotNull( p );
Print.printCode( p );
// try running the code!
LuaClosure c = new LuaClosure( p, _G );
c.call(); c.call();
} catch ( Exception e ) { } catch ( Exception e ) {
fail("i/o exception: "+e ); fail("i/o exception: "+e );