From be4a320bf97b32cffbc97a4e25f7c7cadf54c332 Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Fri, 21 Sep 2012 05:24:58 +0000 Subject: [PATCH] Improve compatibility with lua 5.2. --- examples/jse/SampleJseMain.java | 5 +- examples/jse/ScriptEngineSample.java | 44 +++++-- src/jse/org/luaj/vm2/luajc/JavaBuilder.java | 40 +++++-- src/jse/org/luaj/vm2/luajc/JavaGen.java | 12 +- src/jse/org/luaj/vm2/luajc/LuaJC.java | 4 +- .../org/luaj/vm2/script/LuaScriptEngine.java | 109 +++++++++--------- test/java/org/luaj/luajc/TestLuaJC.java | 57 ++++----- 7 files changed, 154 insertions(+), 117 deletions(-) diff --git a/examples/jse/SampleJseMain.java b/examples/jse/SampleJseMain.java index 95610081..f706ce2d 100644 --- a/examples/jse/SampleJseMain.java +++ b/examples/jse/SampleJseMain.java @@ -1,5 +1,6 @@ +import org.luaj.vm2.Globals; import org.luaj.vm2.LuaValue; import org.luaj.vm2.lib.jse.JsePlatform; @@ -10,8 +11,8 @@ public class SampleJseMain { String script = "examples/lua/hello.lua"; // create an environment to run in - LuaValue _G = JsePlatform.standardGlobals(); - _G.get("dofile").call( LuaValue.valueOf(script) ); + Globals _G = JsePlatform.standardGlobals(); + _G.loadFile(script).arg1().call( LuaValue.valueOf(script) ); } diff --git a/examples/jse/ScriptEngineSample.java b/examples/jse/ScriptEngineSample.java index e1518d64..1583f79e 100644 --- a/examples/jse/ScriptEngineSample.java +++ b/examples/jse/ScriptEngineSample.java @@ -10,16 +10,22 @@ import javax.script.ScriptException; import javax.script.SimpleBindings; import org.luaj.vm2.LuaValue; +import org.luaj.vm2.lib.OneArgFunction; public class ScriptEngineSample { public static void main(String [] args) { + // Set the property 'luaj.debug' before getting the engine to get + // the debug libraries, which will be slower, but may provide stack traces + // when luaJC is not used. + // System.setProperty("luaj.debug", "true"); + ScriptEngineManager sem = new ScriptEngineManager(); ScriptEngine e = sem.getEngineByExtension(".lua"); ScriptEngineFactory f = e.getFactory(); // uncomment to enable the lua-to-java bytecode compiler - // (require bcel library in class path) + // (requires bcel library in class path) // org.luaj.vm2.luajc.LuaJC.install(); System.out.println( "Engine name: " +f.getEngineName() ); @@ -29,6 +35,7 @@ public class ScriptEngineSample { String statement = f.getOutputStatement("\"hello, world\""); System.out.println(statement); try { + // Simple evaluations. Each tiny script is compiled then evaluated. e.eval(statement); e.put("x", 25); @@ -38,22 +45,37 @@ public class ScriptEngineSample { e.put("x", 2); e.eval("y = math.sqrt(x)"); System.out.println( "y="+e.get("y") ); - + + e.put("f", new OneArgFunction() { + public LuaValue call(LuaValue arg) { + System.out.println("arg "+arg.tojstring()); + return LuaValue.valueOf(123); + } + }); + System.out.println("eval: "+e.eval("return f('abc')")); + + // Example of compiled script tha can be reused once compiled. CompiledScript cs = ((Compilable)e).compile("y = math.sqrt(x); return y"); - Bindings b = e.createBindings(); - b.put("x", 3); - System.out.println( "eval: "+cs.eval(b) ); - System.out.println( "y="+b.get("y") ); - SimpleBindings sb = new SimpleBindings(); - sb.put("x", 144); + Bindings b1 = e.createBindings(); + Bindings b2 = e.createBindings(); + b1.put("x", 3); + b2.put("x", 144); + System.out.println( "eval: "+cs.eval(b1) ); + System.out.println( "eval: "+cs.eval(b2) ); + System.out.println( "y="+b1.get("y") ); + System.out.println( "y="+b2.get("y") ); + + // Bindings that use a client-specified Bindings type. + Bindings sb = new SimpleBindings(); + sb.put("x", 2); System.out.println( "eval: "+cs.eval(sb) ); - System.out.println( "y="+sb.get("y") ); + // Example of how exceptions are generated. try { - e.eval("\n\nbogus example\n\n"); + e.eval("\n\nbuggy lua code\n\n"); } catch ( ScriptException se ) { - System.out.println("script threw ScriptException as expected, message is '"+se.getMessage()+"'"); + System.out.println("script exception thrown for buggy script, message: '"+se.getMessage()+"'"); } testEngineBindings(e); diff --git a/src/jse/org/luaj/vm2/luajc/JavaBuilder.java b/src/jse/org/luaj/vm2/luajc/JavaBuilder.java index 18c9c4ee..127d5267 100644 --- a/src/jse/org/luaj/vm2/luajc/JavaBuilder.java +++ b/src/jse/org/luaj/vm2/luajc/JavaBuilder.java @@ -53,7 +53,6 @@ import org.apache.bcel.generic.Type; import org.luaj.vm2.Buffer; import org.luaj.vm2.Lua; import org.luaj.vm2.LuaBoolean; -import org.luaj.vm2.LuaDouble; import org.luaj.vm2.LuaInteger; import org.luaj.vm2.LuaNumber; import org.luaj.vm2.LuaString; @@ -73,7 +72,6 @@ public class JavaBuilder { private static final String STR_LUAVALUE = LuaValue.class.getName(); private static final String STR_LUASTRING = LuaString.class.getName(); private static final String STR_LUAINTEGER = LuaInteger.class.getName(); - private static final String STR_LUADOUBLE = LuaDouble.class.getName(); private static final String STR_LUANUMBER = LuaNumber.class.getName(); private static final String STR_LUABOOLEAN = LuaBoolean.class.getName(); private static final String STR_LUATABLE = LuaTable.class.getName(); @@ -84,7 +82,6 @@ public class JavaBuilder { private static final ObjectType TYPE_LUAVALUE = new ObjectType(STR_LUAVALUE); private static final ObjectType TYPE_LUASTRING = new ObjectType(STR_LUASTRING); private static final ObjectType TYPE_LUAINTEGER = new ObjectType(STR_LUAINTEGER); - private static final ObjectType TYPE_LUADOUBLE = new ObjectType(STR_LUADOUBLE); private static final ObjectType TYPE_LUANUMBER = new ObjectType(STR_LUANUMBER); private static final ObjectType TYPE_LUABOOLEAN = new ObjectType(STR_LUABOOLEAN); private static final ObjectType TYPE_LUATABLE = new ObjectType(STR_LUATABLE); @@ -92,9 +89,8 @@ public class JavaBuilder { private static final ArrayType TYPE_LOCALUPVALUE = new ArrayType( TYPE_LUAVALUE, 1 ); private static final ArrayType TYPE_CHARARRAY = new ArrayType( Type.CHAR, 1 ); + private static final ArrayType TYPE_LUAVALUEARRAY = new ArrayType( TYPE_LUAVALUE, 1 ); - - private static final Class[] NO_INNER_CLASSES = {}; private static final String STR_FUNCV = VarArgFunction.class.getName(); private static final String STR_FUNC0 = ZeroArgFunction.class.getName(); @@ -108,7 +104,6 @@ public class JavaBuilder { private static final Type[] ARG_TYPES_DOUBLE = { Type.DOUBLE }; private static final Type[] ARG_TYPES_STRING = { Type.STRING }; private static final Type[] ARG_TYPES_CHARARRAY = { TYPE_CHARARRAY }; - private static final Type[] ARG_TYPES_VARARGS_INT = { TYPE_VARARGS, Type.INT }; private static final Type[] ARG_TYPES_INT_LUAVALUE = { Type.INT, TYPE_LUAVALUE }; private static final Type[] ARG_TYPES_INT_VARARGS = { Type.INT, TYPE_VARARGS }; private static final Type[] ARG_TYPES_LUAVALUE_VARARGS = { TYPE_LUAVALUE, TYPE_VARARGS }; @@ -161,10 +156,12 @@ public class JavaBuilder { private final int[] targets; private final BranchInstruction[] branches; private final InstructionHandle[] branchDestHandles; + private final InstructionHandle[] lastInstrHandles; private InstructionHandle beginningOfLuaInstruction; // hold vararg result private LocalVariableGen varresult = null; + private int prev_line = -1; public JavaBuilder(ProtoInfo pi, String classname, String filename) { this.pi = pi; @@ -220,6 +217,7 @@ public class JavaBuilder { targets = new int[nc]; branches = new BranchInstruction[nc]; branchDestHandles = new InstructionHandle[nc]; + lastInstrHandles = new InstructionHandle[nc]; } public void initializeSlots() { @@ -339,6 +337,7 @@ public class JavaBuilder { private Map plainSlotVars = new HashMap(); private Map upvalueSlotVars = new HashMap(); + private Map localVarGenBySlot = new HashMap(); private int findSlot( int slot, Map map, String prefix, Type type ) { Integer islot = Integer.valueOf(slot); if ( map.containsKey(islot) ) @@ -347,6 +346,7 @@ public class JavaBuilder { LocalVariableGen local = mg.addLocalVariable(name, type, null, null); int index = local.getIndex(); map.put(islot, Integer.valueOf(index)); + localVarGenBySlot.put(slot, local); return index; } private int findSlotIndex( int slot, boolean isupvalue ) { @@ -748,11 +748,25 @@ public class JavaBuilder { beginningOfLuaInstruction = ih; } - public void onEndOfLuaInstruction(int pc) { + public void onEndOfLuaInstruction(int pc, int line) { branchDestHandles[pc] = beginningOfLuaInstruction; + lastInstrHandles[pc] = main.getEnd(); + if (line != prev_line) + mg.addLineNumber(beginningOfLuaInstruction, prev_line = line); beginningOfLuaInstruction = null; } + public void setVarStartEnd(int slot, int start_pc, int end_pc, String name) { + if (localVarGenBySlot.containsKey(slot)) { + name = name.replaceAll("[^a-zA-Z0-9]", "_"); + LocalVariableGen l = localVarGenBySlot.get(slot); + l.setEnd(lastInstrHandles[end_pc-1]); + if (start_pc > 1) + l.setStart(lastInstrHandles[start_pc-2]); + l.setName(name); + } + } + private void resolveBranches() { int nc = p.code.length; for (int pc = 0; pc < nc; pc++) { @@ -798,7 +812,17 @@ public class JavaBuilder { append(factory.createInvoke(STR_BUFFER, "value", TYPE_LUAVALUE, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); } - public void closeUpvalue(int pc, int i) { + public void closeUpvalue(int pc, int upindex) { // TODO: assign the upvalue location the value null; + /* + boolean isrw = pi.isReadWriteUpvalue( pi.upvals[upindex] ); + append(InstructionConstants.THIS); + append(InstructionConstants.ACONST_NULL); + if ( isrw ) { + append(factory.createFieldAccess(classname, upvalueName(upindex), TYPE_LUAVALUEARRAY, Constants.PUTFIELD)); + } else { + append(factory.createFieldAccess(classname, upvalueName(upindex), TYPE_LUAVALUE, Constants.PUTFIELD)); + } + */ } } diff --git a/src/jse/org/luaj/vm2/luajc/JavaGen.java b/src/jse/org/luaj/vm2/luajc/JavaGen.java index 27ca98e8..ab493348 100644 --- a/src/jse/org/luaj/vm2/luajc/JavaGen.java +++ b/src/jse/org/luaj/vm2/luajc/JavaGen.java @@ -21,6 +21,7 @@ ******************************************************************************/ package org.luaj.vm2.luajc; +import org.luaj.vm2.LocVars; import org.luaj.vm2.Lua; import org.luaj.vm2.Prototype; import org.luaj.vm2.Upvaldesc; @@ -46,6 +47,10 @@ public class JavaGen { // build this class JavaBuilder builder = new JavaBuilder(pi, classname, filename); scanInstructions(pi, classname, builder); + for (int i = 0; i < pi.prototype.locvars.length; ++i) { + LocVars l = pi.prototype.locvars[i]; + builder.setVarStartEnd(i, l.startpc, l.endpc, l.varname.tojstring()); + } this.bytecode = builder.completeClass(); // build sub-prototypes @@ -80,8 +85,9 @@ public class JavaGen { for ( int pc=b0.pc0; pc<=b0.pc1; pc++ ) { - int pc0 = pc; // closure changes pc - int ins = p.code[pc]; + final int pc0 = pc; // closure changes pc + final int ins = p.code[pc]; + final int line = pc < p.lineinfo.length? p.lineinfo[pc]: -1; final int o = Lua.GET_OPCODE(ins); int a = Lua.GETARG_A(ins); int b = Lua.GETARG_B(ins); @@ -420,7 +426,7 @@ public class JavaGen { } // let builder process branch instructions - builder.onEndOfLuaInstruction( pc0 ); + builder.onEndOfLuaInstruction( pc0, line ); } } } diff --git a/src/jse/org/luaj/vm2/luajc/LuaJC.java b/src/jse/org/luaj/vm2/luajc/LuaJC.java index fa6f82f4..e12d60ea 100644 --- a/src/jse/org/luaj/vm2/luajc/LuaJC.java +++ b/src/jse/org/luaj/vm2/luajc/LuaJC.java @@ -97,8 +97,8 @@ public class LuaJC implements LuaCompiler { public LuaFunction load(InputStream stream, String name, LuaValue env) throws IOException { Prototype p = LuaC.instance.compile(stream, name); - String classname = toStandardJavaClassName( name ); String luaname = toStandardLuaFileName( name ); + String classname = toStandardJavaClassName( luaname ); JavaLoader loader = new JavaLoader(); return loader.load(p, classname, luaname, env); } @@ -116,7 +116,7 @@ public class LuaJC implements LuaCompiler { private static String toStandardLuaFileName( String luachunkname ) { String stub = toStub( luachunkname ); String filename = stub.replace('.','/')+".lua"; - return filename; + return filename.startsWith("@")? filename.substring(1): filename; } private static String toStub( String s ) { diff --git a/src/jse/org/luaj/vm2/script/LuaScriptEngine.java b/src/jse/org/luaj/vm2/script/LuaScriptEngine.java index 650a3f57..d48d360f 100644 --- a/src/jse/org/luaj/vm2/script/LuaScriptEngine.java +++ b/src/jse/org/luaj/vm2/script/LuaScriptEngine.java @@ -25,7 +25,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; -import java.util.Iterator; +import java.util.Map.Entry; import javax.script.Bindings; import javax.script.Compilable; @@ -37,6 +37,7 @@ import javax.script.ScriptException; import javax.script.SimpleBindings; import javax.script.SimpleScriptContext; +import org.luaj.vm2.Globals; import org.luaj.vm2.LoadState; import org.luaj.vm2.Lua; import org.luaj.vm2.LuaClosure; @@ -59,7 +60,7 @@ public class LuaScriptEngine implements ScriptEngine, Compilable { private static final String __NAME__ = "Luaj"; private static final String __SHORT_NAME__ = "Luaj"; private static final String __LANGUAGE__ = "lua"; - private static final String __LANGUAGE_VERSION__ = "5.1"; + private static final String __LANGUAGE_VERSION__ = "5.2"; private static final String __ARGV__ = "arg"; private static final String __FILENAME__ = "?"; @@ -67,12 +68,14 @@ public class LuaScriptEngine implements ScriptEngine, Compilable { private ScriptContext defaultContext; - private final LuaValue _G; + private final Globals _G; public LuaScriptEngine() { // create globals - _G = JsePlatform.standardGlobals(); + _G = "true".equals(System.getProperty("luaj.debug"))? + JsePlatform.debugGlobals(): + JsePlatform.standardGlobals(); // set up context ScriptContext ctx = new SimpleScriptContext(); @@ -88,9 +91,11 @@ public class LuaScriptEngine implements ScriptEngine, Compilable { put(FILENAME, __FILENAME__); put(NAME, __SHORT_NAME__); put("THREADING", null); + + // Let globals act as an index metatable + _G.set(LuaValue.INDEX, _G); } - public Object eval(String script) throws ScriptException { return eval(new StringReader(script)); } @@ -162,22 +167,24 @@ public class LuaScriptEngine implements ScriptEngine, Compilable { try { InputStream ris = new Utf8Encoder(reader); try { - final LuaFunction f = LoadState.load(ris, "script", "bt", null); + final LuaFunction f = LoadState.load(ris, "script", "bt", _G); if ( f.isclosure() ) { // most compiled functions are closures with prototypes final Prototype p = f.checkclosure().p; return new CompiledScriptImpl() { - protected LuaFunction newFunctionInstance() { - return new LuaClosure( p, null ); + protected LuaFunction newFunctionInstance(LuaTable env) { + return new LuaClosure( p, env ); } }; } else { // when luajc is used, functions are java class instances final Class c = f.getClass(); return new CompiledScriptImpl() { - protected LuaFunction newFunctionInstance() throws ScriptException { + protected LuaFunction newFunctionInstance(LuaTable env) throws ScriptException { try { - return (LuaFunction) c.newInstance(); + LuaFunction f = (LuaFunction) c.newInstance(); + f.initupvalue1( env ); + return f; } catch (Exception e) { throw new ScriptException("instantiation failed: "+e.toString()); } @@ -195,63 +202,51 @@ public class LuaScriptEngine implements ScriptEngine, Compilable { } abstract protected class CompiledScriptImpl extends CompiledScript { - abstract protected LuaFunction newFunctionInstance() throws ScriptException; + abstract protected LuaFunction newFunctionInstance(LuaTable env) throws ScriptException; public ScriptEngine getEngine() { return LuaScriptEngine.this; } public Object eval(ScriptContext context) throws ScriptException { Bindings b = context.getBindings(ScriptContext.ENGINE_SCOPE); - LuaFunction f = newFunctionInstance(); - ClientBindings cb = new ClientBindings(b); - f.initupvalue1(cb.env); - Varargs result = f.invoke(LuaValue.NONE); - cb.copyGlobalsToBindings(); - return result; + BindingsGlobals env = new BindingsGlobals(b); + LuaFunction f = newFunctionInstance(env); + try { + return f.invoke(LuaValue.NONE); + } finally { + env.copyout(); + } } } - - public class ClientBindings { - public final Bindings b; - public final LuaTable env; - public ClientBindings( Bindings b ) { + + class BindingsGlobals extends Globals { + final Bindings b; + BindingsGlobals(Bindings b) { this.b = b; - this.env = new LuaTable(); - env.setmetatable(LuaTable.tableOf(new LuaValue[] { LuaValue.INDEX, _G })); - this.copyBindingsToGlobals(); + this.setmetatable(_G); + this.debuglib = _G.debuglib; + for (Entry e : b.entrySet()) + rawset(toLua(e.getKey()), toLua(e.getValue())); } - public void copyBindingsToGlobals() { - for ( Iterator i = b.keySet().iterator(); i.hasNext(); ) { - String key = (String) i.next(); - Object val = b.get(key); - LuaValue luakey = toLua(key); - LuaValue luaval = toLua(val); - env.set(luakey, luaval); - i.remove(); - } + void copyout() { + b.clear(); + for (Varargs v = next(LuaValue.NIL); !v.arg1().isnil(); v = next(v.arg1())) + b.put(toJava(v.arg1()).toString(), toJava(v.arg(2))); } - private LuaValue toLua(Object javaValue) { - return javaValue == null? LuaValue.NIL: - javaValue instanceof LuaValue? (LuaValue) javaValue: - CoerceJavaToLua.coerce(javaValue); - } - public void copyGlobalsToBindings() { - LuaValue[] keys = env.keys(); - for ( int i=0; i0 && args[0].equals("nocompile")) ) { - chunk = LuaJC.getInstance().load(new ByteArrayInputStream(bytes), "script", _G); - } else { - chunk = (LuaValue) Class.forName("script").newInstance(); + LuaJC.install(); + f = _G.loadFile(filename).arg1(); } // call with arguments - LuaValue[] vargs = new LuaValue[args.length]; - for ( int i=0; i