diff --git a/src/main/java/lua/CallInfo.java b/src/main/java/lua/CallInfo.java
new file mode 100644
index 00000000..b98396ef
--- /dev/null
+++ b/src/main/java/lua/CallInfo.java
@@ -0,0 +1,22 @@
+package lua;
+
+import lua.io.Closure;
+
+public class CallInfo {
+
+ public Closure closure;
+ public int base;
+ public int top;
+ public int pc;
+ public int resultbase;
+ public int nresults;
+
+ public CallInfo(Closure c, int base, int resultoff, int nresults) {
+ this.closure = c;
+ this.base = base;
+ this.resultbase = resultoff;
+ this.nresults = nresults;
+ this.pc = 0;
+ }
+
+}
diff --git a/src/main/java/lua/Print.java b/src/main/java/lua/Print.java
new file mode 100644
index 00000000..3373b7a1
--- /dev/null
+++ b/src/main/java/lua/Print.java
@@ -0,0 +1,308 @@
+package lua;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import lua.io.Closure;
+import lua.io.Proto;
+import lua.value.LDouble;
+import lua.value.LFunction;
+import lua.value.LInteger;
+import lua.value.LString;
+import lua.value.LValue;
+
+public class Print extends Lua {
+
+ private static final String STRING_FOR_NULL = "null";
+ public static PrintStream ps = System.out;
+
+ static void printString(String s) {
+ char[] chars = s.toCharArray();
+ ps.print('"');
+ for (int i = 0, n = chars.length; i < n; i++) {
+ char c = chars[i];
+ if ( c >= ' ' && c <= '~' && c != '\"' && c != '\\' )
+ ps.print(c);
+ else {
+ switch (c) {
+ case '"':
+ ps.print("\\\"");
+ break;
+ case '\\':
+ ps.print("\\\\");
+ break;
+ case 0x0007: /* bell */
+ ps.print("\\a");
+ break;
+ case '\b': /* backspace */
+ ps.print("\\f");
+ break;
+ case '\f': /* form feed */
+ ps.print("\\f");
+ break;
+ case '\r': /* carriage return */
+ ps.print("\\r");
+ break;
+ case '\n': /* newline */
+ ps.print("\\n");
+ break;
+ case 0x000B: /* vertical tab */
+ ps.print("\\v");
+ break;
+ default:
+ ps.print("\\u");
+ ps.print(Integer.toHexString(0x10000 + (int) c)
+ .substring(1));
+ break;
+ }
+ }
+ }
+ ps.print('"');
+ }
+
+ static void printValue( LValue v ) {
+ if ( v instanceof LString )
+ printString(v.toString());
+ else if ( v instanceof LInteger ) {
+ ps.print( v.toJavaInt() );
+ } else if ( v instanceof LDouble ) {
+ double d = v.toJavaDouble();
+ if ( d == ((int)d) )
+ ps.print( (int) d );
+ else
+ ps.print( d );
+ } else if ( v instanceof LFunction ) {
+ ps.print(v.getClass().getName());
+ } else {
+ ps.print( String.valueOf(v) );
+ }
+ }
+
+ static void printConstant(Proto f, int i) {
+ printValue( f.k[i] );
+ }
+
+ public static void printCode(Proto f) {
+ int[] code = f.code;
+ int pc, n = code.length;
+ for (pc = 0; pc < n; pc++) {
+ printOpCode(f, pc);
+ ps.println();
+ }
+ }
+
+ static void printOpCode(Proto f, int pc) {
+ int[] code = f.code;
+ int i = code[pc];
+ int o = GET_OPCODE(i);
+ int a = GETARG_A(i);
+ int b = GETARG_B(i);
+ int c = GETARG_C(i);
+ int bx = GETARG_Bx(i);
+ int sbx = GETARG_sBx(i);
+ int line = getline(f, pc);
+ ps.print(" " + (pc + 1) + " ");
+ if (line > 0)
+ ps.print("[" + line + "] ");
+ else
+ ps.print("[-] ");
+ ps.print(luaP_opnames[o] + " ");
+ switch (getOpMode(o)) {
+ case iABC:
+ ps.print( a );
+ if (getBMode(o) != OpArgN)
+ ps.print(" "+(ISK(b) ? (-1 - INDEXK(b)) : b));
+ if (getCMode(o) != OpArgN)
+ ps.print(" "+(ISK(c) ? (-1 - INDEXK(c)) : c));
+ break;
+ case iABx:
+ if (getBMode(o) == OpArgK) {
+ ps.print(a + " " + (-1 - bx));
+ } else {
+ ps.print(a + " " + (bx));
+ }
+ break;
+ case iAsBx:
+ if (o == OP_JMP)
+ ps.print( sbx );
+ else
+ ps.print(a + " " + sbx);
+ break;
+ }
+ switch (o) {
+ case OP_LOADK:
+ printString(" ; ");
+ printConstant(f, bx);
+ break;
+ case OP_GETUPVAL:
+ case OP_SETUPVAL:
+ ps.print(" ; ");
+ if ( f.upvalues.length > b )
+ printValue(f.upvalues[b]);
+ else
+ ps.print( "-" );
+ break;
+ case OP_GETGLOBAL:
+ case OP_SETGLOBAL:
+ ps.print(" ; ");
+ printConstant( f, bx );
+ break;
+ case OP_GETTABLE:
+ case OP_SELF:
+ if (ISK(c)) {
+ ps.print(" ; ");
+ printConstant(f, INDEXK(c));
+ }
+ break;
+ case OP_SETTABLE:
+ case OP_ADD:
+ case OP_SUB:
+ case OP_MUL:
+ case OP_DIV:
+ case OP_POW:
+ case OP_EQ:
+ case OP_LT:
+ case OP_LE:
+ if (ISK(b) || ISK(c)) {
+ ps.print(" ; ");
+ if (ISK(b))
+ printConstant(f, INDEXK(b));
+ else
+ ps.print("-");
+ ps.print(" ");
+ if (ISK(c))
+ printConstant(f, INDEXK(c));
+ else
+ ps.print("-");
+ }
+ break;
+ case OP_JMP:
+ case OP_FORLOOP:
+ case OP_FORPREP:
+ ps.print(" ; to " + (sbx + pc + 2));
+ break;
+ case OP_CLOSURE:
+ ps.print(" ; " + f.p[bx].getClass().getName());
+ break;
+ case OP_SETLIST:
+ if (c == 0)
+ ps.print(" ; " + ((int) code[++pc]));
+ else
+ ps.print(" ; " + ((int) c));
+ break;
+ default:
+ break;
+ }
+ }
+
+ private static int getline(Proto f, int pc) {
+ return f.lineinfo[pc];
+ }
+
+ static void printHeader(Proto f) {
+ String s = String.valueOf(f.source);
+ if (s.startsWith("@") || s.startsWith("="))
+ s = s.substring(1);
+ else if ("\033Lua".equals(s))
+ s = "(bstring)";
+ else
+ s = "(string)";
+ String a = (f.linedefined == 0) ? "main" : "function";
+ ps.print("\n%" + a + " <" + s + ":" + f.linedefined + ","
+ + f.lastlinedefined + "> (" + f.code.length + " instructions, "
+ + f.code.length * 4 + " bytes at " + id(f) + ")\n");
+ ps.print(f.numparams + " param, " + f.maxstacksize + " slot, "
+ + f.upvalues.length + " upvalue, ");
+ ps.print(f.locvars.length + " local, " + f.k.length
+ + " constant, " + f.p.length + " function\n");
+ }
+
+ static void printConstants(Proto f) {
+ int i, n = f.k.length;
+ ps.print("constants (" + n + ") for " + id(f) + ":\n");
+ for (i = 0; i < n; i++) {
+ ps.print(" " + (i + 1) + " ");
+ printValue( f.k[i] );
+ ps.print( "\n");
+ }
+ }
+
+ static void printLocals(Proto f) {
+ int i, n = f.locvars.length;
+ ps.print("locals (" + n + ") for " + id(f) + ":\n");
+ for (i = 0; i < n; i++) {
+ ps.println(" "+i+" "+f.locvars[i].varname+" "+(f.locvars[i].startpc+1)+" "+(f.locvars[i].endpc+1));
+ }
+ }
+
+ static void printUpValues(Proto f) {
+ int i, n = f.upvalues.length;
+ ps.print("upvalues (" + n + ") for " + id(f) + ":\n");
+ for (i = 0; i < n; i++) {
+ ps.print(" " + i + " " + f.upvalues[i] + "\n");
+ }
+ }
+
+ public void printFunction(Proto f, boolean full) {
+ int i, n = f.p.length;
+ printHeader(f);
+ printCode(f);
+ if (full) {
+ printConstants(f);
+ printLocals(f);
+ printUpValues(f);
+ }
+ for (i = 0; i < n; i++)
+ printFunction(f.p[i], full);
+ }
+
+ private static void format( String s, int maxcols ) {
+ int n = s.length();
+ if ( n > maxcols )
+ ps.print( s.substring(0,maxcols) );
+ else {
+ ps.print( s );
+ for ( int i=maxcols-n; --i>=0; )
+ ps.print( ' ' );
+ }
+ }
+
+ public static void printState(StackState state, int base, int top, int max,
+ Closure cl, int pc) {
+
+ // print opcode into buffer
+ PrintStream previous = ps;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ps = new PrintStream( baos );
+ printOpCode( cl.p, pc );
+ ps.flush();
+ ps.close();
+ ps = previous;
+ format( baos.toString(), 40 );
+
+ ps.print( " b,t=(" );
+ format( String.valueOf(base), 3 );
+ ps.print( "," );
+ format( String.valueOf(top), 3 );
+ ps.print( ") " );
+
+ // print stack
+ int i=0;
+ for ( ; i[-0, +0,
+ * -]
+ *
+ *
+ * Returns NULL if cannot create the state (due to lack of
+ * memory). The argument f is the allocator function; Lua
+ * does all memory allocation for this state through this function. The
+ * second argument, ud, is an opaque pointer that Lua simply
+ * passes to the allocator in every call.
+ */
+ public StackState() {
+ }
+
+ // ================ interfaces for performing calls
+
+ public void push( int value ) {
+ stack[top++] = LInteger.valueOf(value);
+ }
+
+ public void push( String value ) {
+ stack[top++] = new LString(value);
+ }
+
+ public void push( double value ) {
+ stack[top++] = new LDouble(value);
+ }
+
+ public void push( boolean value ) {
+ stack[top++] = value? LBoolean.TRUE: LBoolean.FALSE;
+ }
+
+ public void push( LValue value ) {
+ stack[top++] = value;
+ }
+
+ public void prepStackCall() {
+ Closure c = (Closure) stack[base];
+ int resultbase = base;
+ // Expand the stack if necessary
+ checkstack( c.p.maxstacksize );
+ if ( ! c.p.is_vararg ) {
+ base += 1;
+ adjustTop( base+c.p.numparams );
+ } else {
+ /* vararg function */
+ int npar = c.p.numparams;
+ int narg = Math.max(0, top - base - 1);
+ int nfix = Math.min(narg, npar);
+ int nvar = Math.max(0, narg-nfix);
+
+ // must copy args into position, add number parameter
+ stack[top] = LInteger.valueOf(nvar);
+ System.arraycopy(stack, base+1, stack, top+1, nfix);
+ base = top + 1;
+ top = base + nfix;
+ adjustTop( base + npar );
+ }
+ final int newcc = cc + 1;
+ if ( newcc >= calls.length ) {
+ CallInfo[] newcalls = new CallInfo[ calls.length * 2 ];
+ System.arraycopy( calls, 0, newcalls, 0, cc+1 );
+ calls = newcalls;
+ }
+ calls[newcc] = new CallInfo(c, base, resultbase, nresults);
+ cc = newcc;
+ }
+
+ public void execute() {
+ for ( int cb=cc; cc>=cb; )
+ exec();
+ }
+
+ public void doCall( Closure c, LValue[] args ) {
+ setResult( c );
+ for ( int i=0, n=(args!=null? args.length: 0); i=0? calls[cc].base: 0);
+ }
+
+ public void setExpectedResultCount( int nresults ) {
+ this.nresults = nresults;
+ }
+
+ public int getExpectedResultCount() {
+ return this.nresults;
+ }
+
+ public void invokeJavaFunction(JavaFunction javaFunction) {
+ int resultbase = base;
+ int resultsneeded = nresults;
+ ++base;
+ int nactual = javaFunction.invoke(this);
+ debugAssert(nactual>=0);
+ debugAssert(top-nactual>=base);
+ base = resultbase;
+ System.arraycopy(stack, top-nactual, stack, base, nactual);
+ settop( nactual );
+ if ( resultsneeded >= 0 )
+ settop( resultsneeded );
+ }
+
+ // ================== error processing =================
+
+ // return 0 for success, non-zero for error condition
+ public void call( int nargs, int nreturns ) {
+ // save stack state
+ int oldbase = base;
+
+ // rb is base of new call frame
+ int rb = this.base = top - 1 - nargs;
+
+ // make or set up the call
+ this.nresults = nreturns;
+ if (this.stack[base].luaStackCall(this)) {
+ // call was set up on the stack,
+ // we still have to execute it
+ execute();
+ }
+
+ // adjustTop only for case when call was completed
+ // and number of args > 0. If call completed but
+ // c == 0, leave top to point to end of results
+ if (nreturns >= 0)
+ adjustTop(rb + nreturns);
+
+ // restore base
+ this.base = oldbase;
+ }
+
+ // return 0 for success, non-zero for error condition
+ public int pcall( int nargs, int nreturns, int errfunc ) {
+ // save stack state
+ int oldtop = top;
+ int oldbase = base;
+ int oldcc = cc;
+ try {
+ // rb is base of new call frame
+ int rb = this.base = top - 1 - nargs;
+
+ // make or set up the call
+ this.nresults = nreturns;
+ if (this.stack[base].luaStackCall(this)) {
+ // call was set up on the stack,
+ // we still have to execute it
+ execute();
+ }
+
+ // adjustTop only for case when call was completed
+ // and number of args > 0. If call completed but
+ // c == 0, leave top to point to end of results
+ if (nreturns >= 0)
+ adjustTop(rb + nreturns);
+
+ // restore base
+ this.base = oldbase;
+
+ return 0;
+ } catch ( Throwable t ) {
+ debugPcallError( t );
+ this.base = oldbase;
+ this.cc = oldcc;
+ close(oldtop); /* close eventual pending closures */
+ String s = t.getMessage();
+ setResult( s!=null? s: t.toString() );
+ return (t instanceof OutOfMemoryError? LUA_ERRMEM: LUA_ERRRUN);
+ }
+ }
+
+ // loader
+ public int load( InputStream is, String chunkname ) {
+ try {
+ Proto p = LoadState.undump(this, is, chunkname );
+ push( new Closure( this, p ) );
+ return 0;
+ } catch ( Throwable t ) {
+ setResult( t.getMessage() );
+ return (t instanceof OutOfMemoryError? LUA_ERRMEM: LUA_ERRSYNTAX);
+ }
+ }
+
+ // ================ interfaces for being called
+
+ public int getArgCount() {
+ return top - (base + 1);
+ }
+
+ public LValue getArg(int index) {
+ int off = (base + 1) + index;
+ return off < top? stack[off]: LNil.NIL;
+ }
+
+ public int getArgAsInt( int index ) {
+ return getArg(index).toJavaInt();
+ }
+
+ public double getArgAsDouble( int index ) {
+ return getArg(index).toJavaDouble();
+ }
+
+ public boolean getArgAsBoolean( int index ) {
+ return getArg(index).toJavaBoolean();
+ }
+
+ public LString getArgAsLuaString( int index ) {
+ return getArg(index).luaAsString();
+ }
+
+ public String getArgAsString( int index ) {
+ return getArgAsLuaString(index).toJavaString();
+ }
+
+ public void setResult() {
+ top = base;
+ }
+
+ public void setResult( LValue value ) {
+ setResult();
+ push( value );
+ }
+
+ public void setResult( String value ) {
+ setResult();
+ push( value );
+ }
+
+ public void setErrorResult(LValue value, String message) {
+ setResult();
+ push( value );
+ push( message );
+ }
+
+ public void adjustResults() {
+ if ( nresults != LUA_MULTRET ) {
+ adjustTop( base + nresults );
+ }
+ // Copy base back from value in call frame
+ base = (cc >= 0) ? calls[cc].base : 0;
+ }
+
+ // ================ execute instructions
+ private LValue RKBC(LValue[] k, int bc) {
+ return StackState.ISK(bc) ?
+ k[StackState.INDEXK(bc)]:
+ stack[base + bc];
+ }
+
+ private LValue GETARG_RKB(LValue[] k, int i) {
+ return RKBC(k, GETARG_B(i));
+ }
+
+ private LValue GETARG_RKC(LValue[] k, int i) {
+ return RKBC(k, GETARG_C(i));
+ }
+
+ private void adjustTop(int newTop) {
+ while (top < newTop)
+ this.stack[top++] = LNil.NIL;
+ top = newTop;
+ }
+
+ /** execute instructions up to a yield, return, or call */
+ public void exec() {
+ if ( cc < 0 )
+ return;
+
+ int i, a, b, c, o, n, cb;
+ LValue rkb, rkc, nvarargs, key, val;
+ LValue i0, step, idx, limit, init, table;
+ boolean back, body;
+ Proto proto;
+ Closure newClosure;
+
+ // reload values from the current call frame
+ // into local variables
+ CallInfo ci = calls[cc];
+ Closure cl = ci.closure;
+ Proto p = cl.p;
+ int[] code = p.code;
+ LValue[] k = p.k;
+
+ this.base = ci.base;
+
+ // loop until a return instruction is processed,
+ // or the vm yields
+ while (true) {
+ debugAssert( ci == calls[cc] );
+
+ if (TRACE)
+ Print.printState(this, base, top, base+p.maxstacksize, cl, ci.pc);
+
+ // allow debug hooks a chance to operate
+ debugHooks( ci.pc );
+
+ // advance program counter
+ i = code[ci.pc++];
+
+ // get first opcode arg
+ 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)
+ ci.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;
+ top = base + a;
+ table.luaGetTable(this, table, key);
+ continue;
+ }
+ case StackState.OP_GETTABLE: {
+ b = StackState.GETARG_B(i);
+ key = GETARG_RKC(k, i);
+ table = this.stack[base + b];
+ top = base + a;
+ table.luaGetTable(this, 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, 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(k, i);
+ val = GETARG_RKC(k, i);
+ table = this.stack[base + a];
+ table.luaSetTable(this, 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(k, i);
+ rkc = GETARG_RKC(k, i);
+ top = base + a;
+ rkb.luaGetTable(this, rkb, rkc);
+ this.stack[base + a + 1] = rkb;
+ // 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(k, i);
+ rkc = GETARG_RKC(k, i);
+ this.stack[base + a] = rkc.luaBinOpUnknown(o, rkb);
+ continue;
+ }
+ case StackState.OP_UNM: {
+ rkb = GETARG_RKB(k, i);
+ this.stack[base + a] = rkb.luaUnaryMinus();
+ continue;
+ }
+ case StackState.OP_NOT: {
+ rkb = GETARG_RKB(k, i);
+ this.stack[base + a] = (!rkb.toJavaBoolean() ? LBoolean.TRUE
+ : LBoolean.FALSE);
+ continue;
+ }
+ case StackState.OP_LEN: {
+ rkb = GETARG_RKB(k, i);
+ this.stack[base + a] = LInteger.valueOf( rkb.luaLength() );
+ continue;
+ }
+ case StackState.OP_CONCAT: {
+ b = StackState.GETARG_B(i);
+ c = StackState.GETARG_C(i);
+ int numValues = c - b + 1;
+ LString[] strings = new LString[numValues];
+
+ for (int j = b, l = 0; j <= c; j++, l++) {
+ LString s = this.stack[base + j].luaAsString();
+ strings[l] = s;
+ }
+ this.stack[base + a] = LString.concat( strings );
+ continue;
+ }
+ case StackState.OP_JMP: {
+ ci.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(k, i);
+ rkc = GETARG_RKC(k, i);
+ boolean test = rkc.luaBinCmpUnknown(o, rkb);
+ if (test == (a == 0))
+ ci.pc++;
+ continue;
+ }
+ case StackState.OP_TEST: {
+ c = StackState.GETARG_C(i);
+ if (this.stack[base + a].toJavaBoolean() != (c != 0))
+ ci.pc++;
+ continue;
+ }
+ case StackState.OP_TESTSET: {
+ rkb = GETARG_RKB(k, i);
+ c = StackState.GETARG_C(i);
+ if (rkb.toJavaBoolean() != (c != 0))
+ ci.pc++;
+ else
+ this.stack[base + a] = rkb;
+ continue;
+ }
+ case StackState.OP_CALL: {
+
+ // ra is base of new call frame
+ this.base += a;
+
+ // number of args
+ b = StackState.GETARG_B(i);
+ if (b != 0) // else use previous instruction set top
+ top = base + b;
+
+ // number of return values we need
+ c = StackState.GETARG_C(i);
+
+ // make or set up the call
+ this.nresults = c - 1;
+ if (this.stack[base].luaStackCall(this))
+ return;
+
+ // adjustTop only for case when call was completed
+ // and number of args > 0. If call completed but
+ // c == 0, leave top to point to end of results
+ if (c > 0)
+ adjustTop(base + c - 1);
+
+ // restore base
+ base = ci.base;
+
+ continue;
+ }
+
+ case StackState.OP_TAILCALL: {
+ close(base);
+
+ // copy down the frame before calling!
+
+ // number of args (including the function)
+ b = StackState.GETARG_B(i);
+ if (b == 0)
+ b = top - (base + a);
+
+ // copy call + all args, discard current frame
+ System.arraycopy(stack, base + a, stack, ci.resultbase, b);
+ this.base = ci.resultbase;
+ this.top = base + b;
+ this.nresults = ci.nresults;
+ --cc;
+
+ // make or set up the call
+ if (this.stack[base].luaStackCall(this))
+ return;
+
+ // adjustTop only for case when call was completed
+ // and number of args > 0. If call completed but
+ // c == 0, leave top to point to end of results
+ if (this.nresults >= 0)
+ adjustTop(base + nresults);
+
+ // force restore of base, etc.
+ return;
+ }
+
+ case StackState.OP_RETURN: {
+ // number of return vals to return
+ b = StackState.GETARG_B(i) - 1;
+ if (b == -1)
+ b = top - (base + a);
+
+ // close open upvals
+ close( base );
+
+ // number to copy down
+ System.arraycopy(stack, base + a, stack, ci.resultbase, b);
+ top = ci.resultbase + b;
+
+ // adjust results to what caller expected
+ if (ci.nresults >= 0)
+ adjustTop(ci.resultbase + ci.nresults);
+
+ // pop the call stack
+ --cc;
+
+ // 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;
+ ci.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);
+ ci.pc += b;
+ continue;
+ }
+ case StackState.OP_TFORLOOP: {
+ cb = a + 3; /* call base */
+ System.arraycopy(this.stack, base + a, this.stack,
+ base + cb, 3);
+ base += cb;
+ try {
+ top = base + 3; /* func. + 2 args (state and index) */
+
+ // call the iterator
+ c = StackState.GETARG_C(i);
+ if (this.stack[base].luaStackCall(this))
+ execute();
+ adjustTop( base + c - 1 );
+
+ // test for continuation
+ if (this.stack[base] != LNil.NIL) { // continue?
+ this.stack[base - 1] = this.stack[base]; // save control variable
+ } else {
+ ci.pc++; // skip over jump
+ }
+ } finally {
+ base -= cb;
+ }
+ 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[ci.pc++];
+ }
+ table = this.stack[base + a];
+ for (int index = 1; index <= b; index++) {
+ val = this.stack[listBase + index];
+ table.luaSetTable(this, table, LInteger.valueOf(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, proto);
+ for (int j = 0; j < newClosure.upVals.length; j++, ci.pc++) {
+ i = code[ci.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.toJavaInt();
+ if (b == StackState.LUA_MULTRET) {
+ b = n; // use entire varargs supplied
+ }
+
+ // copy args up to call stack area
+ checkstack(a+b);
+ 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;
+ }
+ }
+ ci.top = top;
+ }
+ }
+
+ private UpVal findUpVal( LString upValName, int target ) {
+ UpVal up;
+ int i;
+ for ( i = this.upvals.size() - 1; i >= 0; --i ) {
+ up = (UpVal) this.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.upvals.insertElementAt( up, i + 1 );
+ return up;
+ }
+
+ private void close( int limit ) {
+ while ( !upvals.empty() && ( (UpVal) this.upvals.lastElement() ).close( limit ) ) {
+ this.upvals.pop();
+ }
+ }
+
+ public CallInfo getStackFrame(int callStackDepth) {
+ return calls[cc-callStackDepth];
+ }
+
+
+ //===============================================================
+ // Lua Java API
+ //===============================================================
+
+ private void notImplemented() {
+ throw new java.lang.RuntimeException("AbstractStack: not yet implemented");
+ }
+
+ public JavaFunction atpanic(JavaFunction panicf) {
+ JavaFunction f = panic;
+ panic = panicf;
+ return f;
+ }
+
+ public void error(String message, int level) {
+ error( message );
+ }
+
+ public void error(String message) {
+ throw new RuntimeException( message );
+ }
+
+ public void error() {
+ String message = tostring(-1);
+ pop(1);
+ error( message );
+ }
+
+ public void dump() {
+ notImplemented();
+ }
+
+ public void pushclosure(JavaFunction fn, int n) {
+ notImplemented();
+ }
+
+ public int javapcall(JavaFunction func, Object ud) {
+ this.pushjavafunction(func);
+ this.pushlightuserdata(ud);
+ return this.pcall(1, 0, 0);
+ }
+
+ public String pushfstring(String fmt, Object[] args) {
+ notImplemented();
+ return null;
+ }
+ public void pushvfstring(String format, Object[] args) {
+ notImplemented();
+ }
+
+ public void rawequal(int index1, int index2) {
+ notImplemented();
+ }
+
+ public void getfenv(int index) {
+ notImplemented();
+ }
+
+ public int setfenv(int index) {
+ notImplemented();
+ return 0;
+ }
+
+
+ public void checkstack(int extra) {
+ if ( top + extra > stack.length ) {
+ int n = Math.max( top + extra + LUA_MINSTACK, stack.length * 2 );
+ LValue[] s = new LValue[n];
+ System.arraycopy(stack, 0, s, 0, stack.length);
+ stack = s;
+ }
+ }
+
+ public void close() {
+ stack = new LValue[20];
+ base = top = 0;
+ }
+
+ public void concat(int n) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for ( int i=-n; i<0; i++ ) {
+ LString ls = tolstring(i);
+ baos.write(ls.m_bytes, ls.m_offset, ls.m_length);
+ }
+ pop(n);
+ pushlvalue( new LString(baos.toByteArray()));
+ }
+
+ public void createtable(int narr, int nrec) {
+ stack[top++] = new LTable(narr, nrec);
+ }
+
+ public boolean equal(int index1, int index2) {
+ return topointer(index2).luaBinOpUnknown(Lua.OP_EQ,
+ topointer(index1)).toJavaBoolean();
+ }
+
+ public void gc(int what, int data) {
+ notImplemented();
+ }
+
+ public void getfield(int index, String k) {
+ LTable t = totable(index);
+ // TODO: what if this triggers metatable ops
+ // pushlvalue( t.luaGetTable(this, t, new LString(k)) );
+ t.luaGetTable(this, t, new LString(k));
+ }
+
+ public void getglobal(String s) {
+ LTable t = this._G;
+ // TODO: what if this triggers metatable ops
+ // pushlvalue( t.luaGetTable(this, t, new LString(s)));
+ t.luaGetTable(this, t, new LString(s));
+ }
+
+ public int getmetatable(int index) {
+ LTable mt = topointer(index).luaGetMetatable();
+ if ( mt != null ) {
+ pushlvalue( mt );
+ return 1;
+ }
+ return 0;
+ }
+
+ public void gettable(int index) {
+ LValue t = totable(index);
+ LValue k = poplvalue();
+ // todo: what if this triggers metatable ops
+ // pushlvalue( t.luaGetTable(this, t, k) );
+ t.luaGetTable(this, t, k);
+ }
+
+ public void insert(int index) {
+ int ai = index2adr(index);
+ LValue v = stack[top-1];
+ System.arraycopy(stack, ai, stack, ai+1, top-ai-1);
+ stack[ai] = v;
+ }
+
+ public boolean isboolean(int index) {
+ return type(index) == Lua.LUA_TBOOLEAN;
+ }
+
+ public boolean isfunction(int index) {
+ return type(index) == Lua.LUA_TFUNCTION;
+ }
+
+ public boolean isjavafunction(int index) {
+ return topointer(index) instanceof JavaFunction;
+ }
+
+ public boolean islightuserdata(int index) {
+ return false;
+ }
+
+ public boolean isnil(int index) {
+ return topointer(index) == LNil.NIL;
+ }
+
+ public boolean isnone(int index) {
+ return topointer(index) == null;
+ }
+
+ public boolean isnoneornil(int index) {
+ Object v = topointer(index);
+ return v == null || v == LNil.NIL;
+ }
+
+ public boolean isnumber(int index) {
+ return type(index) == Lua.LUA_TNUMBER;
+ }
+
+ public boolean isstring(int index) {
+ return type(index) == Lua.LUA_TSTRING;
+ }
+
+ public boolean istable(int index) {
+ return type(index) == Lua.LUA_TTABLE;
+ }
+
+ public boolean isthread(int index) {
+ return type(index) == Lua.LUA_TTHREAD;
+ }
+
+ public boolean isuserdata(int index) {
+ return type(index) == Lua.LUA_TUSERDATA;
+ }
+
+ public boolean lessthan(int index1, int index2) {
+ return topointer(index2).luaBinOpUnknown(Lua.OP_LT,
+ topointer(index1)).toJavaBoolean();
+ }
+
+ public void newtable() {
+ stack[top++] = new LTable();
+ }
+
+ public void newthread() {
+ notImplemented();
+ }
+
+ public void newuserdata(Object o) {
+ stack[top++] = new LUserData(o);
+ }
+
+ public int next(int index) {
+ notImplemented();
+ return 0;
+ }
+
+ public int objlen(int index) {
+ return tostring(index).length();
+ }
+
+ public void pop(int n) {
+ for ( int i=0; i=0? base+nt: top+nt;
+ if ( ant < base )
+ throw new IllegalArgumentException("index out of bounds: "+ant );
+ while ( top < ant )
+ stack[top++] = LNil.NIL;
+ while ( top > ant )
+ stack[--top] = null;
+ }
+
+ private int index2adr(int index) {
+ // TODO: upvalues? globals? environment?
+ int ai = index>0? base+index-1: top+index;
+ if ( ai < base )
+ throw new IllegalArgumentException("index out of bounds: "+ai );
+ return ai;
+ }
+
+ public LValue topointer(int index) {
+ int ai = index2adr(index);
+ if ( ai >= top )
+ return LNil.NIL;
+ return stack[ai];
+ }
+
+ public String tostring(int index) {
+ return topointer(index).toJavaString();
+ }
+
+ public LTable totable(int index) {
+ return (LTable) topointer(index);
+ }
+
+ public Object touserdata(int index) {
+ LValue v = topointer(index);
+ if ( v.luaGetType() != Lua.LUA_TUSERDATA )
+ return null;
+ return ((LUserData)v).m_instance;
+ }
+
+ public int type(int index) {
+ return topointer(index).luaGetType();
+ }
+
+ public String typename(int index) {
+ return topointer(index).luaGetTypeName().toJavaString();
+ }
+
+ public void xmove(VM to, int n) {
+ if ( n > 0 ) {
+ to.checkstack(n);
+ StackState ss = (StackState)to;
+ System.arraycopy(stack, top-n, ss.stack, ss.top, n);
+ ss.top += n;
+ }
+ }
+
+ public void yield(int nresults) {
+ notImplemented();
+ }
+
+ // ============================= conversion to and from Java boxed types ====================
+
+ public void pushboolean(Boolean b) {
+ if ( b == null )
+ pushnil();
+ else
+ pushboolean( b.booleanValue() );
+ }
+
+ public void pushinteger(Byte b) {
+ if ( b == null )
+ pushnil();
+ else
+ pushinteger( b.byteValue() );
+ }
+
+ public void pushinteger(Character c) {
+ if ( c == null )
+ pushnil();
+ else
+ pushinteger( c.charValue() );
+ }
+
+ public void pushnumber(Double d) {
+ if ( d == null )
+ pushnil();
+ else
+ pushnumber( d.doubleValue() );
+ }
+
+ public void pushnumber(Float f) {
+ if ( f == null )
+ pushnil();
+ else
+ pushnumber( f.doubleValue() );
+ }
+
+ public void pushinteger(Integer i) {
+ if ( i == null )
+ pushnil();
+ else
+ pushinteger( i.intValue() );
+ }
+
+ public void pushinteger(Short s) {
+ if ( s == null )
+ pushnil();
+ else
+ pushinteger( s.shortValue() );
+ }
+
+ public void pushnumber(Long l) {
+ if ( l == null )
+ pushnil();
+ else
+ pushnumber( l.doubleValue() );
+ }
+
+ public void pushuserdata( Object o ) {
+ if ( o == null )
+ pushnil();
+ else
+ newuserdata( o );
+ }
+
+ public Boolean toboxedboolean(int index) {
+ return topointer(index).toJavaBoxedBoolean();
+ }
+
+ public Byte toboxedbyte(int index) {
+ return topointer(index).toJavaBoxedByte();
+ }
+
+ public Double toboxeddouble(int index) {
+ return topointer(index).toJavaBoxedDouble();
+ }
+
+ public Float toboxedfloat(int index) {
+ return topointer(index).toJavaBoxedFloat();
+ }
+
+ public Integer toboxedinteger(int index) {
+ return topointer(index).toJavaBoxedInteger();
+ }
+
+ public Long toboxedlong(int index) {
+ return topointer(index).toJavaBoxedLong();
+ }
+
+}