From 8bf4c82a12f154a729cde4d93c43b1385fa43c26 Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Tue, 24 Jul 2007 05:06:10 +0000 Subject: [PATCH] Rework the main API"s that implement the calling convention. Provide utility methods to get arguments that were supplied, and provide return values. Add a VM interface to clarify the relationship between the VM, things that call the VM, and things that are called by the VM. Make the code more closely aligned with the C++ version. --- .../java/lua/addon/luacompat/LuaCompat.java | 81 ++-- .../lua/addon/luajava/CoerceLuaToJava.java | 18 +- src/addon/java/lua/addon/luajava/LuaJava.java | 242 +++++++--- src/main/java/lua/Builtin.java | 32 +- src/main/java/lua/CallFrame.java | 457 ------------------ src/main/java/lua/VM.java | 114 +++++ src/main/java/lua/io/Closure.java | 12 +- src/main/java/lua/io/LoadState.java | 221 +-------- src/main/java/lua/io/Proto.java | 4 +- src/main/java/lua/value/LFunction.java | 26 +- src/main/java/lua/value/LString.java | 5 - src/main/java/lua/value/LTable.java | 27 +- src/main/java/lua/value/LValue.java | 17 +- src/test/java/LuaJavaAppRunner.java | 7 +- src/test/java/LuacRunner.java | 13 +- src/test/java/lua/LuaJTest.java | 2 +- src/test/java/lua/StandardTest.java | 8 +- 17 files changed, 424 insertions(+), 862 deletions(-) delete mode 100644 src/main/java/lua/CallFrame.java create mode 100644 src/main/java/lua/VM.java diff --git a/src/addon/java/lua/addon/luacompat/LuaCompat.java b/src/addon/java/lua/addon/luacompat/LuaCompat.java index 06863509..35e421ed 100644 --- a/src/addon/java/lua/addon/luacompat/LuaCompat.java +++ b/src/addon/java/lua/addon/luacompat/LuaCompat.java @@ -3,15 +3,14 @@ package lua.addon.luacompat; import java.io.IOException; import java.io.InputStream; -import lua.CallFrame; +import lua.CallInfo; import lua.GlobalState; import lua.StackState; +import lua.VM; import lua.io.Closure; import lua.io.LoadState; import lua.io.Proto; -import lua.value.LDouble; import lua.value.LFunction; -import lua.value.LInteger; import lua.value.LNil; import lua.value.LNumber; import lua.value.LString; @@ -48,36 +47,33 @@ public class LuaCompat extends LFunction { this.id = id; } - public void luaStackCall( CallFrame call, int base, int top, int nresults ) { + public void luaStackCall( VM vm ) { switch ( id ) { case ASSERT: { - LValue v = call.stack[base+1]; - if ( !v.luaAsBoolean() ) { + if ( !vm.getArgAsBoolean(0) ) { String message; - if ( top > base+2 ) { - message = call.stack[base+2].luaAsString(); + if ( vm.getArgCount() > 1 ) { + message = vm.getArgAsString(1); } else { message = "assertion failed!"; } throw new RuntimeException(message); } - call.top = base; + vm.setResult(); } break; case COLLECTGARBAGE: System.gc(); - call.top = base; + vm.setResult(); break; case LOADFILE: - call.stack[base] = loadfile(call, ( top > base ) ? call.stack[base+1] : null); - call.top = base+1; + vm.setResult( loadfile(vm, vm.getArg(0)) ); break; case TONUMBER: - call.stack[base] = toNumber( call.stack, base+1, top ); - call.top = base+1; + vm.setResult( toNumber( vm ) ); break; case RAWGET: { - LValue t = call.stack[base+1]; - LValue k = call.stack[base+2]; + LValue t = vm.getArg(0);; + LValue k = vm.getArg(1); LValue result = LNil.NIL; if ( t instanceof LTable ) { LValue v = (LValue) ( (LTable) t ).m_hash.get( k ); @@ -85,39 +81,33 @@ public class LuaCompat extends LFunction { result = v; } } - call.stack[base] = result; - call.top = base+1; + vm.setResult( result ); } break; case SETFENV: - call.top = base + setfenv( call.stack, base, base+1, top, call.state ); + setfenv( (StackState) vm ); break; default: luaUnsupportedOperation(); } - if (nresults >= 0) - call.adjustTop(base + nresults); } - private LValue toNumber( LValue[] stack, int first, int top ) { - LValue result = LNil.NIL; - if ( first < top ) { - LValue input = stack[first]; - if ( input instanceof LNumber ) { - result = input; - } else if ( input instanceof LString ) { - int base = 10; - if ( first+1 < top ) { - base = stack[first+1].luaAsInt(); - } - return ( (LString) input ).luaToNumber( base ); + private LValue toNumber( VM vm ) { + LValue input = vm.getArg(0); + if ( input instanceof LNumber ) { + return input; + } else if ( input instanceof LString ) { + int base = 10; + if ( vm.getArgCount()>1 ) { + base = vm.getArgAsInt(1); } + return ( (LString) input ).luaToNumber( base ); } - return result; + return LNil.NIL; } - private int setfenv( LValue[] stack, int result, int argbase, int arglimit, StackState state ) { - LValue f = stack[argbase]; - LValue newenv = stack[argbase+1]; + private void setfenv( StackState state ) { + LValue f = state.getArg(0); + LValue newenv = state.getArg(1); Closure c = null; @@ -129,9 +119,9 @@ public class LuaCompat extends LFunction { } else { int callStackDepth = f.luaAsInt(); if ( callStackDepth > 0 ) { - CallFrame frame = state.getStackFrame( callStackDepth ); + CallInfo frame = state.getStackFrame( callStackDepth ); if ( frame != null ) { - c = frame.cl; + c = frame.closure; } } else { // This is supposed to set the environment of the current @@ -144,14 +134,15 @@ public class LuaCompat extends LFunction { if ( newenv instanceof LTable ) { c.env = (LTable) newenv; } - stack[ result ] = c; - return 1; + state.setResult( c ); + return; } - return 0; + state.setResult(); + return; } - private LValue loadfile( CallFrame call, LValue fileName ) { + private LValue loadfile( VM vm, LValue fileName ) { InputStream is; String script; @@ -165,8 +156,8 @@ public class LuaCompat extends LFunction { if ( is != null ) { try { - Proto p = LoadState.undump(call.state, is, script); - return new Closure(call.state, p); + Proto p = LoadState.undump(vm, is, script); + return new Closure( (StackState) vm, p); } catch (IOException e) { } finally { if ( is != System.in ) { diff --git a/src/addon/java/lua/addon/luajava/CoerceLuaToJava.java b/src/addon/java/lua/addon/luajava/CoerceLuaToJava.java index 69261391..7354bddd 100644 --- a/src/addon/java/lua/addon/luajava/CoerceLuaToJava.java +++ b/src/addon/java/lua/addon/luajava/CoerceLuaToJava.java @@ -3,8 +3,6 @@ package lua.addon.luajava; import java.util.HashMap; import java.util.Map; -import lua.CallFrame; -import lua.addon.luajava.LuaJava.LInstance; import lua.value.LBoolean; import lua.value.LDouble; import lua.value.LInteger; @@ -122,11 +120,12 @@ public class CoerceLuaToJava { return v; } - static Object[] coerceArgs(CallFrame call, int base, int nargs, Class[] parameterTypes) { + static Object[] coerceArgs(LValue[] suppliedArgs, Class[] parameterTypes) { + int nargs = suppliedArgs.length; int n = parameterTypes.length; Object[] args = new Object[n]; for ( int i=0; i nargs? 0x4000: 0x8000); for ( int i=0; i= 0) - call.adjustTop(base + nresults); + } + + public static class ParamsList { + public final LValue[] values; + public final Class[] classes; + public int hash; + ParamsList( VM vm ) { + int n = vm.getArgCount()-1; + values = new LValue[n]; + classes = new Class[n]; + for ( int i=0; i> consCache = + new HashMap>(); + + private static Map>> consIndex = + new HashMap>>(); + + private static Constructor resolveConstructor(Class clazz, ParamsList params ) { + + // get the cache + Map cache = consCache.get( clazz ); + if ( cache == null ) + consCache.put( clazz, cache = new HashMap() ); + + // look up in the cache + Constructor c = cache.get( params ); + if ( c != null ) + return c; + + // get index + Map> index = consIndex.get( clazz ); + if ( index == null ) { + consIndex.put( clazz, index = new HashMap>() ); + Constructor[] cons = clazz.getConstructors(); + for ( Constructor con : cons ) { + int n = con.getParameterTypes().length; + List list = index.get(n); + if ( list == null ) + index.put( n, list = new ArrayList() ); + list.add( con ); + } + } + + // figure out best list of arguments == supplied args + int n = params.classes.length; + List list = index.get(n); + if ( list == null ) + throw new IllegalArgumentException("no constructor with "+n+" args"); + + // find constructor with best score + int bests = Integer.MAX_VALUE; + int besti = 0; + for ( int i=0, size=list.size(); i>> methCache = + new HashMap>>(); + + private static Map>>> methIndex = + new HashMap>>>(); + + private static Method resolveMethod(Class clazz, String methodName, ParamsList params ) { + + // get the cache + Map> nameCache = methCache.get( clazz ); + if ( nameCache == null ) + methCache.put( clazz, nameCache = new HashMap>() ); + Map cache = nameCache.get( methodName ); + if ( cache == null ) + nameCache.put( methodName, cache = new HashMap() ); + + // look up in the cache + Method m = cache.get( params ); + if ( m != null ) + return m; + + // get index + Map>> index = methIndex.get( clazz ); + if ( index == null ) { + methIndex.put( clazz, index = new HashMap>>() ); + Method[] meths = clazz.getMethods(); + for ( Method meth : meths ) { + String s = meth.getName(); + int n = meth.getParameterTypes().length; + Map> map = index.get(s); + if ( map == null ) + index.put( s, map = new HashMap>() ); + List list = map.get(n); + if ( list == null ) + map.put( n, list = new ArrayList() ); + list.add( meth ); + } + } + + // figure out best list of arguments == supplied args + Map> map = index.get(methodName); + if ( map == null ) + throw new IllegalArgumentException("no method named '"+methodName+"'"); + int n = params.classes.length; + List list = map.get(n); + if ( list == null ) + throw new IllegalArgumentException("no method named '"+methodName+"' with "+n+" args"); + + // find constructor with best score + int bests = Integer.MAX_VALUE; + int besti = 0; + for ( int i=0, size=list.size(); i 0 ) + stdout.print( "\t" ); + stdout.print( vm.getArg(i).luaAsString() ); } stdout.println(); - call.top = base; + vm.setResult(); break; case PAIRS: - LValue value = call.stack[base+1].luaPairs(); - call.stack[base] = value; - call.top = base+1; + vm.setResult( vm.getArg(0).luaPairs() ); break; case GETMETATABLE: - call.stack[base] = call.stack[base+1].luaGetMetatable(); - call.top = base+1; + vm.setResult( vm.getArg(0).luaGetMetatable() ); break; case SETMETATABLE: - call.stack[base+1].luaSetMetatable(call.stack[base+2]); - call.stack[base] = call.stack[base+1]; - call.top = base+1; + LValue t = vm.getArg(0); + t.luaSetMetatable(vm.getArg(1)); + vm.setResult( t ); break; case TYPE: - call.stack[base] = call.stack[base+1].luaGetType(); - call.top = base+1; + vm.setResult( vm.getArg(0).luaGetType() ); break; default: luaUnsupportedOperation(); } - if (nresults >= 0) - call.adjustTop(base + nresults); } static void redirectOutput( OutputStream newStdOut ) { diff --git a/src/main/java/lua/CallFrame.java b/src/main/java/lua/CallFrame.java deleted file mode 100644 index 90b3ace9..00000000 --- a/src/main/java/lua/CallFrame.java +++ /dev/null @@ -1,457 +0,0 @@ -/** - * - */ -package lua; - -import lua.io.Closure; -import lua.io.Proto; -import lua.io.UpVal; -import lua.value.LBoolean; -import lua.value.LInteger; -import lua.value.LNil; -import lua.value.LString; -import lua.value.LTable; -import lua.value.LValue; - -public class CallFrame { - private final static boolean DEBUG = false; - - public final StackState state; - public final LValue[] stack; - public int base; - public int top; - public final Closure cl; - public final Proto p; - private final LValue[] k; - private final int nresults; - int pc = 0; - boolean done = false; - - CallFrame(StackState state, Closure c, int base, int nargs, int nresults) { - this.state = state; - this.stack = state.stack; - this.cl = c; - this.p = c.p; - this.k = p.k; - this.base = base; - this.nresults = nresults; - int nparams = p.numparams; - int nvalues = (p.is_vararg && nargs > nparams ? nargs : nparams); - for (int i = nargs; i < nvalues; i++) - this.state.stack[base + i] = LNil.NIL; - this.top = base + nvalues; - this.state.calls[++this.state.cc] = this; - this.state.avail = base + p.maxstacksize; - } - - private LValue RKBC(int bc) { - return StackState.ISK(bc) ? k[StackState.INDEXK(bc)] - : this.state.stack[base + bc]; - } - - private LValue GETARG_RKB(int i) { - return RKBC(StackState.GETARG_B(i)); - } - - private LValue GETARG_RKC(int i) { - return RKBC(StackState.GETARG_C(i)); - } - - public void adjustTop(int newTop) { - while (top < newTop) - this.stack[top++] = LNil.NIL; - top = newTop; - } - - public void exec() { - int i, a, b, c, o, n, cb; - LValue rkb, rkc, nvarargs, key, val; - StringBuffer sb; - LValue i0, step, idx, limit, init, table; - boolean back, body; - Proto proto; - Closure newClosure; - - // reload the current calling context - int[] code = p.code; - while (true) { - - if (DEBUG) - Print.printState(state, base, top, state.avail, cl, pc); - - i = code[pc++]; - - // TODO: implement debug hooks - // if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && - // (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { - // traceexec(L, pc); - // if (L->status == LUA_YIELD) { // did hook yield? - // L->savedpc = pc - 1; - // return; - // } - // base = L->base; - // } - - a = StackState.GETARG_A(i); - switch (StackState.GET_OPCODE(i)) { - case StackState.OP_MOVE: { - b = StackState.GETARG_B(i); - this.stack[base + a] = this.stack[base + b]; - continue; - } - case StackState.OP_LOADK: { - b = StackState.GETARG_Bx(i); - this.stack[base + a] = k[b]; - continue; - } - case StackState.OP_LOADBOOL: { - b = StackState.GETARG_B(i); - c = StackState.GETARG_C(i); - this.stack[base + a] = (b != 0 ? LBoolean.TRUE - : LBoolean.FALSE); - if (c != 0) - pc++; /* skip next instruction (if C) */ - continue; - } - case StackState.OP_LOADNIL: { - b = StackState.GETARG_B(i); - do { - this.stack[base + b] = LNil.NIL; - } while ((--b) >= a); - continue; - } - case StackState.OP_GETUPVAL: { - b = StackState.GETARG_B(i); - this.stack[base + a] = cl.upVals[b].getValue(); - continue; - } - case StackState.OP_GETGLOBAL: { - b = StackState.GETARG_Bx(i); - key = k[b]; - table = cl.env; - table.luaGetTable(this, base + a, table, key); - continue; - } - case StackState.OP_GETTABLE: { - b = StackState.GETARG_B(i); - key = GETARG_RKC(i); - table = this.stack[base + b]; - table.luaGetTable(this, base + a, table, key); - continue; - } - case StackState.OP_SETGLOBAL: { - b = StackState.GETARG_Bx(i); - key = k[b]; - val = this.stack[base + a]; - table = cl.env; - table.luaSetTable(this, this.state.avail, table, key, val); - continue; - } - case StackState.OP_SETUPVAL: { - b = StackState.GETARG_B(i); - cl.upVals[b].setValue( this.stack[base + a] ); - continue; - } - case StackState.OP_SETTABLE: { - key = GETARG_RKB(i); - val = GETARG_RKC(i); - table = this.stack[base + a]; - table.luaSetTable(this, state.avail, table, key, val); - continue; - } - case StackState.OP_NEWTABLE: { - b = StackState.GETARG_B(i); - c = StackState.GETARG_C(i); - this.stack[base + a] = new LTable(b, c); - continue; - } - case StackState.OP_SELF: { - rkb = GETARG_RKB(i); - rkc = GETARG_RKC(i); - this.stack[base + a + 1] = rkb; - rkb.luaGetTable(this, base + a, rkb, rkc); - // StkId rb = RB(i); - // setobjs2s(L, ra+1, rb); - // Protect(luaV_gettable(L, rb, RKC(i), ra)); - continue; - } - case StackState.OP_ADD: - case StackState.OP_SUB: - case StackState.OP_MUL: - case StackState.OP_DIV: - case StackState.OP_MOD: - case StackState.OP_POW: { - o = StackState.GET_OPCODE(i); - rkb = GETARG_RKB(i); - rkc = GETARG_RKC(i); - this.stack[base + a] = rkc.luaBinOpUnknown(o, rkb); - continue; - } - case StackState.OP_UNM: { - rkb = GETARG_RKB(i); - this.stack[base + a] = rkb.luaUnaryMinus(); - continue; - } - case StackState.OP_NOT: { - rkb = GETARG_RKB(i); - this.stack[base + a] = (!rkb.luaAsBoolean() ? LBoolean.TRUE - : LBoolean.FALSE); - continue; - } - case StackState.OP_LEN: { - rkb = GETARG_RKB(i); - this.stack[base + a] = rkb.luaLength(); - continue; - } - case StackState.OP_CONCAT: { - b = StackState.GETARG_B(i); - c = StackState.GETARG_C(i); - sb = new StringBuffer(); - for (int j = b; j <= c; j++) - sb.append(this.stack[base + j].luaAsString()); - this.stack[base + a] = new LString(sb.toString()); - continue; - } - case StackState.OP_JMP: { - pc += StackState.GETARG_sBx(i); - continue; - } - case StackState.OP_EQ: - case StackState.OP_LT: - case StackState.OP_LE: { - o = StackState.GET_OPCODE(i); - rkb = GETARG_RKB(i); - rkc = GETARG_RKC(i); - boolean test = rkc.luaBinCmpUnknown(o, rkb); - if (test == (a == 0)) - pc++; - continue; - } - case StackState.OP_TEST: { - c = StackState.GETARG_C(i); - if (this.stack[base + a].luaAsBoolean() != (c != 0)) - pc++; - continue; - } - case StackState.OP_TESTSET: { - rkb = GETARG_RKB(i); - c = StackState.GETARG_C(i); - if (rkb.luaAsBoolean() != (c != 0)) - pc++; - else - this.stack[base + a] = rkb; - continue; - } - case StackState.OP_CALL: { - /* ra is start of result location */ - b = StackState.GETARG_B(i); // number of stack spaces to reserve - // for - // closure plus args - c = StackState.GETARG_C(i); // num results plus 1 - if (b != 0) // else use previous instruction set top - top = base + a + b; - - // make or set up the call - this.stack[base + a].luaStackCall(this, base + a, top, c - 1); - - // force re-entry into current call - if (this.state.calls[this.state.cc] != this) - return; - - // adjustTop only for case when call was completed - if (c > 0) - adjustTop(base + a + c - 1); - - continue; - } - case StackState.OP_TAILCALL: { - b = StackState.GETARG_B(i); // number of stack spaces to reserve - // for - // closure plus args ?? - c = StackState.GETARG_C(i); // number of return args - must be - // LUA_MULTRET - if (b != 0) // else use previous instruction set top - top = base + a + b; - - close( base ); // Close open upvals - - // make or set up the call - this.stack[base + a].luaStackCall(this, base + a, top, c - 1); - - // adjustTop only for case when call was completed - if (this.state.calls[this.state.cc] != this) { - // was a vm call, or a Java call that re-entered the stack. - // copy down the stack variables and substitute the stack - // frame. - CallFrame ci = this.state.calls[this.state.cc]; - n = ci.top - ci.base; - System.arraycopy(this.stack, ci.base, - this.stack, base, n); - ci.base = base; - ci.top = base + n; - this.state.calls[this.state.cc - 1] = this.state.calls[this.state.cc]; - --this.state.cc; - - // force a reset of the calling context state - return; - } - continue; - } - case StackState.OP_RETURN: { - b = StackState.GETARG_B(i); // number of return vals - if (b != 0) // else use previous top - top = base + a + b - 1; - close( base ); // close open upvals - n = top - (base + a); // number to copy down - System.arraycopy(this.stack, base + a, this.stack, - base - 1, n); - top = base - 1 + n; - - // adjust results to what caller expected - if (nresults >= 0) - adjustTop(base + nresults); - - // pop the call stack - done = true; - if ( --state.cc >= 0 ) { - CallFrame call = state.calls[state.cc]; - call.top = top; - } - - // force a reload of the calling context - return; - } - case StackState.OP_FORLOOP: { - i0 = this.stack[base + a]; - step = this.stack[base + a + 2]; - idx = step.luaBinOpUnknown(Lua.OP_ADD, i0); - limit = this.stack[base + a + 1]; - back = step.luaBinCmpInteger(Lua.OP_LT, 0); - body = (back ? idx.luaBinCmpUnknown(Lua.OP_LE, limit) : limit - .luaBinCmpUnknown(Lua.OP_LE, idx)); - if (body) { - this.stack[base + a] = idx; - this.stack[base + a + 3] = idx; - pc += StackState.GETARG_sBx(i); - } - continue; - } - case StackState.OP_FORPREP: { - init = this.stack[base + a]; - step = this.stack[base + a + 2]; - this.stack[base + a] = step.luaBinOpUnknown(Lua.OP_SUB, - init); - b = StackState.GETARG_sBx(i); - pc += b; - continue; - } - case StackState.OP_TFORLOOP: { - cb = a + 3; /* call base */ - System.arraycopy(this.stack, base + a, this.stack, - base + cb, 3); - top = base + cb + 3; /* func. + 2 args (state and index) */ - - // call the iterator - c = StackState.GETARG_C(i); - this.stack[base + a].luaStackCall(this, base + cb, top, c - 1); - - // test for continuation - if (this.stack[base + cb] != LNil.NIL) { // continue? - this.stack[base + cb - 1] = this.stack[base - + cb]; // save control variable - } else { - pc++; // skip over jump - } - continue; - } - case StackState.OP_SETLIST: { - b = StackState.GETARG_B(i); - c = StackState.GETARG_C(i); - int listBase = base + a; - if (b == 0) { - b = top - listBase - 1; - } - if (c == 0) { - c = code[pc++]; - } - table = this.stack[base + a]; - for (int index = 1; index <= b; index++) { - val = this.stack[listBase + index]; - table.luaSetTable(this, this.state.avail, table, - new LInteger(index), val); - } - top = base + a - 1; - continue; - } - case StackState.OP_CLOSE: { - close( a ); // close upvals higher in the stack than position a - continue; - } - case StackState.OP_CLOSURE: { - b = StackState.GETARG_Bx(i); - proto = cl.p.p[b]; - newClosure = new Closure(this.state, proto); - for (int j = 0; j < newClosure.upVals.length; j++, pc++) { - i = code[pc]; - o = StackState.GET_OPCODE(i); - b = StackState.GETARG_B(i); - if (o == StackState.OP_GETUPVAL) { - newClosure.upVals[j] = cl.upVals[b]; - } else if (o == StackState.OP_MOVE) { - newClosure.upVals[j] = findUpVal( proto.upvalues[j], base + b ); - } else { - throw new java.lang.IllegalArgumentException( - "bad opcode: " + o); - } - } - this.stack[base + a] = newClosure; - continue; - } - case StackState.OP_VARARG: { - // figure out how many args to copy - b = StackState.GETARG_B(i) - 1; - nvarargs = this.stack[base - 1]; - n = nvarargs.luaAsInt(); - if (b == StackState.LUA_MULTRET) { - b = n; // use entire varargs supplied - } - - // copy args up to call stack area - for (int j = 0; j < b; j++) - this.stack[base + a + j] = (j < n ? this.stack[base - - n + j - 1] - : LNil.NIL); - top = base + a + b; - continue; - } - } - } - } - - private UpVal findUpVal( LString upValName, int target ) { - UpVal up; - int i; - for ( i = this.state.upvals.size() - 1; i >= 0; --i ) { - up = (UpVal) this.state.upvals.elementAt( i ); - if ( up.stack == this.stack && up.position == target ) { - return up; - } else if ( up.position < target ) { - break; - } - } - - up = new UpVal( upValName, this.stack, target ); - this.state.upvals.insertElementAt( up, i + 1 ); - return up; - } - - private void close( int limit ) { - while ( !state.upvals.empty() && ( (UpVal) this.state.upvals.lastElement() ).close( limit ) ) { - this.state.upvals.pop(); - } - } - - public void push(LValue value) { - stack[top++] = value; - } -} \ No newline at end of file diff --git a/src/main/java/lua/VM.java b/src/main/java/lua/VM.java new file mode 100644 index 00000000..d9e666f3 --- /dev/null +++ b/src/main/java/lua/VM.java @@ -0,0 +1,114 @@ +package lua; + +import lua.io.Closure; +import lua.value.LTable; +import lua.value.LValue; + +public interface VM { + + // ================ interfaces for performing calls + + /** Push an argument or return value onto the stack + */ + public void push( LValue value ); + + /** Push an int argument or return value onto the stack + */ + public void push( int value ); + + /** Push a double argument or return value onto the stack + */ + public void push( double value ); + + /** Push a boolean argument or return value onto the stack + */ + public void push( boolean value ); + + /** Push a String argument or return value onto the stack + */ + public void push( String value ); + + /** + * Perform a call that has been set up on the stack. + * The first value on the stack must be a Closure, + * and subsequent values are arguments to the closure. + */ + public void stackCall(); + + /** + * Execute bytecodes until the current call completes + * or the vm yields. + */ + public void exec(); + + /** + * Put the closure on the stack with arguments, + * then perform the call. Leave return values + * on the stack for later querying. + * + * @param c + * @param values + */ + public void doCall(Closure c, LValue[] values); + + // ================ interfaces for getting arguments when called + + /** + * Get the number of argumnets supplied in the call. + */ + public int getArgCount(); + + /** + * Get the index-th argument supplied, or NIL if fewer than index were supplied. + * @param index + * @return + */ + public LValue getArg(int index); + + /** + * Get the index-th argument as an int value, or 0 if fewer than index arguments were supplied. + * @param index + * @return + */ + public int getArgAsInt( int index ); + + /** + * Get the index-th argument as a double value, or 0 if fewer than index arguments were supplied. + * @param index + * @return + */ + public double getArgAsDouble( int index ); + + /** + * Get the index-th argument as a boolean value, or false if fewer than index arguments were supplied. + * @param index + * @return + */ + public boolean getArgAsBoolean( int index ); + + /** + * Get the index-th argument as a String value, or "" if fewer than index arguments were supplied. + * @param index + * @return + */ + public String getArgAsString( int index ); + + /** Set top to base in preparation for pushing return values. + * Can be used when returning no values. + * + * Once this is called, calls to getArg() are undefined. + * + * @see push() to push additional results once result is reset. + */ + public void setResult(); + + /** Convenience utility to set val to stack[base] and top to base + 1 + * in preparation for returning one value + * + * Once this is called, calls to getArg() are undefined. + * + * @param val value to provide as the only result. + */ + public void setResult(LValue val); + +} diff --git a/src/main/java/lua/io/Closure.java b/src/main/java/lua/io/Closure.java index 95bb76d5..cc07b692 100644 --- a/src/main/java/lua/io/Closure.java +++ b/src/main/java/lua/io/Closure.java @@ -1,7 +1,7 @@ package lua.io; -import lua.CallFrame; import lua.StackState; +import lua.VM; import lua.value.LFunction; import lua.value.LValue; @@ -9,14 +9,18 @@ public class Closure extends LFunction { public LValue env; public Proto p; public UpVal[] upVals; + + // TODO: change arg type to VM? public Closure(StackState state, Proto p) { this.env = state._G; this.p = p; upVals = new UpVal[p.nups]; } - // perform a lua call - public void luaStackCall(CallFrame call, int base, int top, int nresults) { - call.state.stackCall( this, base, top, nresults ); + // called by vm when there is an OP_CALL + // in this case, we are on the stack, + // and simply need to cue the VM to treat it as a stack call + public void luaStackCall(VM vm) { + vm.stackCall(); } } diff --git a/src/main/java/lua/io/LoadState.java b/src/main/java/lua/io/LoadState.java index b7cfb1fa..6a4ee22d 100644 --- a/src/main/java/lua/io/LoadState.java +++ b/src/main/java/lua/io/LoadState.java @@ -4,7 +4,7 @@ import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; -import lua.StackState; +import lua.VM; import lua.value.LBoolean; import lua.value.LDouble; import lua.value.LInteger; @@ -44,15 +44,14 @@ public class LoadState { private int luacSizeofLuaNumber; private boolean luacIsNumberIntegral; - /** The lua state that is loading the code */ - private StackState L; - /** input stream from which we are loading */ private DataInputStream is; /** Name of what is being loaded? */ String name; + /** The VM doing the loading */ + VM L; private static final int LUA_TNONE = (-1); @@ -65,76 +64,11 @@ public class LoadState { private static final int LUA_TFUNCTION = 6; private static final int LUA_TUSERDATA = 7; private static final int LUA_TTHREAD = 8; - -// /* -// ** $Id$ -// ** load precompiled Lua chunks -// ** See Copyright Notice in lua.h -// */ -// -// #include -// -// #define lundump_c -// #define LUA_CORE -// -// #include "lua.h" -// -// #include "ldebug.h" -// #include "ldo.h" -// #include "lfunc.h" -// #include "lmem.h" -// #include "lobject.h" -// #include "lstring.h" -// #include "lundump.h" -// #include "lzio.h" -// -// typedef struct { -// lua_State* L; -// ZIO* Z; -// Mbuffer* b; -// const char* name; -// } LoadState; -// -// #ifdef LUAC_TRUST_BINARIES -// #define IF(c,s) -// #else -// #define IF(c,s) if (c) error(S,s) -// -// static void error(LoadState* S, const char* why) -// { -// luaO_pushfstring(S->L,"%s: %s in precompiled chunk",S->name,why); -// luaD_throw(S->L,LUA_ERRSYNTAX); -// } -// #endif -// -// #define LoadMem(S,b,n,size) LoadBlock(S,b,(n)*(size)) -// #define LoadByte(S) (lu_byte)LoadChar(S) -// #define LoadVar(S,x) LoadMem(S,&x,1,sizeof(x)) -// #define LoadVector(S,b,n,size) LoadMem(S,b,n,size) -// -// static void LoadBlock(LoadState* S, void* b, size_t size) -// { -// size_t r=luaZ_read(S->Z,b,size); -// IF (r!=0, "unexpected end"); -// } -// -// static int LoadChar(LoadState* S) -// { -// char x; -// LoadVar(S,x); -// return x; -// } + int loadByte() throws IOException { return is.readUnsignedByte(); } -// -// static int LoadInt(LoadState* S) -// { -// int x; -// LoadVar(S,x); -// IF (x<0, "bad integer"); -// return x; -// } + int loadInt() throws IOException { if ( this.luacLittleEndian ) { int a = is.readUnsignedByte(); @@ -158,27 +92,7 @@ public class LoadState { } return (((long)b)<<32) | (((long)a)&0xffffffffL); } -// -// static lua_Number LoadNumber(LoadState* S) -// { -// lua_Number x; -// LoadVar(S,x); -// return x; -// } -// -// static TString* LoadString(LoadState* S) -// { -// size_t size; -// LoadVar(S,size); -// if (size==0) -// return NULL; -// else -// { -// char* s=luaZ_openspace(S->L,S->b,size); -// LoadBlock(S,s,size); -// return luaS_newlstr(S->L,s,size-1); /* remove trailing '\0' */ -// } -// } + LString loadString() throws IOException { int size = loadInt(); if ( size == 0 ) @@ -218,14 +132,7 @@ public class LoadState { return longBitsToLuaNumber( loadInt64() ); } } -// -// static void LoadCode(LoadState* S, Proto* f) -// { -// int n=LoadInt(S); -// f->code=luaM_newvector(S->L,n,Instruction); -// f->sizecode=n; -// LoadVector(S,f->code,n,sizeof(Instruction)); -// } + public void loadCode( Proto f ) throws IOException { int n = loadInt(); int[] code = new int[n]; @@ -233,45 +140,7 @@ public class LoadState { code[i] = loadInt(); f.code = code; } -// -// static Proto* LoadFunction(LoadState* S, TString* p); -// -// static void LoadConstants(LoadState* S, Proto* f) -// { -// int i,n; -// n=LoadInt(S); -// f->k=luaM_newvector(S->L,n,TValue); -// f->sizek=n; -// for (i=0; ik[i]); -// for (i=0; ik[i]; -// int t=LoadChar(S); -// switch (t) -// { -// case LUA_TNIL: -// setnilvalue(o); -// break; -// case LUA_TBOOLEAN: -// setbvalue(o,LoadChar(S)); -// break; -// case LUA_TNUMBER: -// setnvalue(o,LoadNumber(S)); -// break; -// case LUA_TSTRING: -// setsvalue2n(S->L,o,LoadString(S)); -// break; -// default: -// IF (1, "bad constant"); -// break; -// } -// } -// n=LoadInt(S); -// f->p=luaM_newvector(S->L,n,Proto*); -// f->sizep=n; -// for (i=0; ip[i]=NULL; -// for (i=0; ip[i]=LoadFunction(S,f->source); -// } + void loadConstants(Proto f) throws IOException { int n = loadInt(); LValue[] values = new LValue[n]; @@ -301,32 +170,7 @@ public class LoadState { protos[i] = loadFunction(f.source); f.p = protos; } -// -// static void LoadDebug(LoadState* S, Proto* f) -// { -// int i,n; -// n=LoadInt(S); -// f->lineinfo=luaM_newvector(S->L,n,int); -// f->sizelineinfo=n; -// LoadVector(S,f->lineinfo,n,sizeof(int)); - -// n=LoadInt(S); -// f->locvars=luaM_newvector(S->L,n,LocVar); -// f->sizelocvars=n; -// for (i=0; ilocvars[i].varname=NULL; -// for (i=0; ilocvars[i].varname=LoadString(S); -// f->locvars[i].startpc=LoadInt(S); -// f->locvars[i].endpc=LoadInt(S); -// } - -// n=LoadInt(S); -// f->upvalues=luaM_newvector(S->L,n,TString*); -// f->sizeupvalues=n; -// for (i=0; iupvalues[i]=NULL; -// for (i=0; iupvalues[i]=LoadString(S); -// } + void loadDebug( Proto f ) throws IOException { int n = loadInt(); f.lineinfo = new int[n]; @@ -348,27 +192,9 @@ public class LoadState { f.upvalues[i] = loadString(); } } -// -// static Proto* LoadFunction(LoadState* S, TString* p) -// { -// Proto* f=luaF_newproto(S->L); -// setptvalue2s(S->L,S->L->top,f); incr_top(S->L); -// f->source=LoadString(S); if (f->source==NULL) f->source=p; -// f->linedefined=LoadInt(S); -// f->lastlinedefined=LoadInt(S); -// f->nups=LoadByte(S); -// f->numparams=LoadByte(S); -// f->is_vararg=LoadByte(S); -// f->maxstacksize=LoadByte(S); -// LoadCode(S,f); -// LoadConstants(S,f); -// LoadDebug(S,f); -// IF (!luaG_checkcode(f), "bad code"); -// S->L->top--; -// return f; -// } + public Proto loadFunction(LString p) throws IOException { - Proto f = new Proto(this.L); + Proto f = new Proto(); // this.L.push(f); f.source = loadString(); f.linedefined = loadInt(); @@ -410,27 +236,8 @@ public class LoadState { if ( sig != LUAC_HEADER_SIGNATURE ) throw new IllegalArgumentException("bad signature"); } -// -// /* -// ** load precompiled chunk -// */ -// Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name) -// { -// LoadState S; -// if (*name=='@' || *name=='=') -// S.name=name+1; -// else if (*name==LUA_SIGNATURE[0]) -// S.name="binary string"; -// else -// S.name=name; -// S.L=L; -// S.Z=Z; -// S.b=buff; -// LoadHeader(&S); -// return LoadFunction(&S,luaS_newliteral(L,"=?")); -// } - public static Proto undump( StackState L, InputStream stream, String name ) throws IOException { + public static Proto undump( VM L, InputStream stream, String name ) throws IOException { String sname = name; if ( name.startsWith("@") || name.startsWith("=") ) sname = name.substring(1); @@ -438,12 +245,12 @@ public class LoadState { sname = "binary string"; LoadState s = new LoadState( L, stream, sname ); s.loadHeader(); - LString literal = new LString(L, "=?"); + LString literal = new LString("=?"); return s.loadFunction( literal ); } /** Private constructor for create a load state */ - private LoadState( StackState L, InputStream stream, String name ) { + private LoadState( VM L, InputStream stream, String name ) { this.L = L; this.name = name; this.is = new DataInputStream( stream ); diff --git a/src/main/java/lua/io/Proto.java b/src/main/java/lua/io/Proto.java index 23614dba..0d1447f4 100644 --- a/src/main/java/lua/io/Proto.java +++ b/src/main/java/lua/io/Proto.java @@ -8,9 +8,7 @@ import lua.value.LString; ** Function Prototypes */ public class Proto { - public Proto(StackState l) { - } - public Proto() { + public Proto() { } public LValue[] k; /* constants used by the function */ diff --git a/src/main/java/lua/value/LFunction.java b/src/main/java/lua/value/LFunction.java index 4d1b3b2d..81c987f9 100644 --- a/src/main/java/lua/value/LFunction.java +++ b/src/main/java/lua/value/LFunction.java @@ -1,6 +1,6 @@ package lua.value; -import lua.CallFrame; +import lua.VM; public class LFunction extends LValue { @@ -11,21 +11,19 @@ public class LFunction extends LValue { return "function: "+hashCode(); } - public void luaSetTable(CallFrame call, int base, LValue table, LValue key, LValue val) { - call.top = base; - call.push( this ); - call.push( table ); - call.push( key ); - call.push( val ); - this.luaStackCall(call, base, call.top, 1); + public void luaSetTable(VM vm, LValue table, LValue key, LValue val) { + vm.push( this ); + vm.push( table ); + vm.push( key ); + vm.push( val ); + this.luaStackCall(vm); } - public void luaGetTable(CallFrame call, int base, LValue table, LValue key) { - call.top = base; - call.push( this ); - call.push( table ); - call.push( key ); - this.luaStackCall(call, base, call.top, 1); + public void luaGetTable(VM vm, LValue table, LValue key) { + vm.push( this ); + vm.push( table ); + vm.push( key ); + this.luaStackCall(vm); } public LString luaGetType() { diff --git a/src/main/java/lua/value/LString.java b/src/main/java/lua/value/LString.java index 1de543b3..385fd145 100644 --- a/src/main/java/lua/value/LString.java +++ b/src/main/java/lua/value/LString.java @@ -27,11 +27,6 @@ public class LString extends LValue { return m_hash; } - // TODO: what to do with LuaState? - public LString(StackState l, String string) { - this(string); - } - public boolean luaBinCmpUnknown(int opcode, LValue lhs) { return lhs.luaBinCmpString(opcode, m_string); } diff --git a/src/main/java/lua/value/LTable.java b/src/main/java/lua/value/LTable.java index ea537d1c..7800c2a0 100644 --- a/src/main/java/lua/value/LTable.java +++ b/src/main/java/lua/value/LTable.java @@ -3,7 +3,7 @@ package lua.value; import java.util.Enumeration; import java.util.Hashtable; -import lua.CallFrame; +import lua.VM; public class LTable extends LValue { @@ -39,12 +39,12 @@ public class LTable extends LValue { return (LValue) m_hash.get( new LString(key) ); } - public void luaSetTable(CallFrame call, int base, LValue table, LValue key, LValue val) { + public void luaSetTable(VM vm, LValue table, LValue key, LValue val) { if ( m_metatable != null ) { if ( ! m_hash.containsKey(key) ) { LValue event = (LValue) m_metatable.m_hash.get( TM_NEWINDEX ); if ( event != null && event != LNil.NIL ) { - event.luaSetTable( call, base, table, key, val ); + event.luaSetTable( vm, table, key, val ); return; } } @@ -52,19 +52,21 @@ public class LTable extends LValue { m_hash.put( key, val ); } - public void luaGetTable(CallFrame call, int base, LValue table, LValue key) { + public void luaGetTable(VM vm, LValue table, LValue key) { LValue val = (LValue) m_hash.get(key); if ( val == null || val == LNil.NIL ) { if ( m_metatable != null ) { LValue event = (LValue) m_metatable.m_hash.get( TM_INDEX ); if ( event != null && event != LNil.NIL ) { - event.luaGetTable( call, base, table, key ); + event.luaGetTable( vm, table, key ); return; } } val = LNil.NIL; } - call.stack[base] = val; + + // stack.stack[base] = val; + vm.push(val); } public String toString() { @@ -107,19 +109,14 @@ public class LTable extends LValue { } // perform a lua call - public void luaStackCall(CallFrame call, int base, int top, int nresults) { + public void luaStackCall(VM vm) { if ( e.hasMoreElements() ) { LValue key = (LValue) e.nextElement(); LValue val = (LValue) t.m_hash.get(key); - call.stack[base] = key; - call.stack[base+1] = val; - call.top = base+2; - } else { - call.stack[base] = LNil.NIL; - call.top = base+1; + vm.setResult(); + vm.push( key ); + vm.push( val ); } - if ( nresults >= 0 ) - call.adjustTop(base + nresults); } } diff --git a/src/main/java/lua/value/LValue.java b/src/main/java/lua/value/LValue.java index 6389c8e4..ca2c3aa5 100644 --- a/src/main/java/lua/value/LValue.java +++ b/src/main/java/lua/value/LValue.java @@ -1,7 +1,7 @@ package lua.value; -import lua.CallFrame; import lua.Lua; +import lua.VM; abstract public class LValue { @@ -20,7 +20,7 @@ public class LValue { } // perform a lua call, return number of results actually produced - public void luaStackCall(CallFrame call, int base, int top, int nresults) { + public void luaStackCall(VM vm) { luaUnsupportedOperation(); } @@ -72,20 +72,21 @@ public class LValue { } /** set a value in a table - * @param call the stack state - * @param base the base of the stack, in case a function is put on the stack + * @param vm the calling vm * @param table the table to operate on * @param the key to set * @param the value to set */ - public void luaSetTable(CallFrame call, int base, LValue table, LValue key, LValue val) { + public void luaSetTable(VM vm, LValue table, LValue key, LValue val) { luaUnsupportedOperation(); } /** Get a value from a table - * @param base TODO - * @param table TODO*/ - public void luaGetTable(CallFrame call, int base, LValue table, LValue key) { + * @param vm the calling vm + * @param table the table from which to get the value + * @param key the key to look up + */ + public void luaGetTable(VM vm, LValue table, LValue key) { luaUnsupportedOperation(); } diff --git a/src/test/java/LuaJavaAppRunner.java b/src/test/java/LuaJavaAppRunner.java index 0df09d0c..519b36d4 100644 --- a/src/test/java/LuaJavaAppRunner.java +++ b/src/test/java/LuaJavaAppRunner.java @@ -30,18 +30,13 @@ public class LuaJavaAppRunner { // new lua state StackState state = new StackState(); - // convert args to lua - LValue[] vargs = new LValue[args.length]; - for ( int i=1; i0? args[0]: "/test1.luac"); + String script = (args.length>0? args[0]: "/test2.luac"); System.out.println("loading '"+script+"'"); // new lua state StackState state = new StackState(); + VM vm = state; - // convert args to lua - LValue[] vargs = new LValue[args.length]; - for ( int i=1; i