From 250fde7859ba6a1e7907c2dc6282f15afc4f0971 Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Tue, 7 Apr 2009 23:45:29 +0000 Subject: [PATCH] Improve debug library getinfo, traceback, getupvalue, and getlocal functions. --- src/core/org/luaj/lib/DebugLib.java | 635 +++++++++++++++----- src/core/org/luaj/vm/CallInfo.java | 14 + src/core/org/luaj/vm/LPrototype.java | 1 + src/core/org/luaj/vm/LoadState.java | 2 +- src/core/org/luaj/vm/LuaErrorException.java | 2 +- src/core/org/luaj/vm/LuaState.java | 89 +-- 6 files changed, 516 insertions(+), 227 deletions(-) diff --git a/src/core/org/luaj/lib/DebugLib.java b/src/core/org/luaj/lib/DebugLib.java index 3aa05e4c..52419344 100644 --- a/src/core/org/luaj/lib/DebugLib.java +++ b/src/core/org/luaj/lib/DebugLib.java @@ -70,8 +70,23 @@ public class DebugLib extends LFunction { private static final int SETMETATABLE = 12; private static final int SETUPVALUE = 13; private static final int TRACEBACK = 14; + + /* maximum stack for a Lua function */ + private static final int MAXSTACK = 250; - private static final LString TEMPORARY = new LString("(*temporary)"); + private static final LString LUA = new LString("Lua"); + private static final LString JAVA = new LString("Java"); + private static final LString JAVASRC = new LString("[Java]"); + private static final LString QMARK = new LString("?"); + private static final LString GLOBAL = new LString("global"); + private static final LString LOCAL = new LString("local"); + private static final LString METHOD = new LString("method"); + private static final LString UPVALUE = new LString("upvalue"); + private static final LString FIELD = new LString("field"); + private static final LString MAINCHUNK = new LString("main chunk"); + private static final LString NOSTRING = new LString(""); + + public static void install( LuaState vm ) { LTable debug = new LTable(); @@ -148,12 +163,13 @@ public class DebugLib extends LFunction { return false; } - private void debug(LuaState vm) { - // TODO: interactive console impl + // j2se subclass may wish to override and provide actual console here. + // j2me platform has not System.in to provide console. + protected void debug(LuaState vm) { vm.resettop(); } - private void gethook(LuaState vm) { + protected void gethook(LuaState vm) { LuaState threadVm = vm; if ( vm.gettop() >= 2 ) threadVm = vm.checkthread(2).vm; @@ -163,12 +179,16 @@ public class DebugLib extends LFunction { vm.pushinteger(threadVm.gethookcount()); } - private void sethook(LuaState vm) { - LuaState threadVm = vm; - if ( vm.gettop() >= 4 ) { - threadVm = vm.checkthread(2).vm; - vm.remove(2); - } + protected LuaState optthreadvm(LuaState vm, int index) { + if ( ! vm.isthread(2) ) + return vm; + LuaState threadVm = vm.checkthread(2).vm; + vm.remove(2); + return threadVm; + } + + protected void sethook(LuaState vm) { + LuaState threadVm = optthreadvm(vm, 2); LFunction func = vm.isnoneornil(2)? null: vm.checkfunction(2); String str = vm.optstring(3,""); int count = vm.optint(4,0); @@ -183,206 +203,133 @@ public class DebugLib extends LFunction { vm.resettop(); } - private void getfenv(LuaState vm) { + protected void getfenv(LuaState vm) { LValue object = vm.topointer(2); LValue env = object.luaGetEnv(null); vm.resettop(); vm.pushlvalue(env!=null? env: LNil.NIL); } - private void setfenv(LuaState vm) { + protected void setfenv(LuaState vm) { LValue object = vm.topointer(2); LTable table = vm.checktable(3); object.luaSetEnv(table); vm.settop(1); } - - private void getinfo(LuaState vm) { - LuaState threadVm = vm; - CallInfo ci = null; - LFunction func = null; - LClosure closure = null; - if ( vm.gettop() >= 4 ) { - threadVm = vm.checkthread(2).vm; - vm.remove(2); - } + + protected void getinfo(LuaState vm) { + LuaState threadVm = optthreadvm(vm, 2); String what = vm.optstring(3, "nSluf"); - // find the closure or function + // find the stack info + StackInfo si; if ( vm.isnumber(2) ) { - Object o = getcallinfoorfunction(threadVm, vm.tointeger(2)); - if ( o == null ) { + int level = vm.tointeger(2); + si = getstackinfo(threadVm, level, 1)[0]; + if ( si == null ) { vm.resettop(); return; } - if ( o instanceof CallInfo ) { - ci = (CallInfo) o; - closure = ci.closure; - } else { - func = (LFunction) o; - } - } else { - func = vm.checkfunction(2); - if ( func instanceof LClosure ) - closure = (LClosure) func; + } else { + LFunction func = vm.checkfunction(2); + si = findstackinfo(threadVm, func); } vm.resettop(); - + // look up info LTable info = new LTable(); vm.pushlvalue(info); for (int i = 0, n = what.length(); i < n; i++) { switch (what.charAt(i)) { case 'S': { - if ( closure != null ) { - String s = closure.p.source.toJavaString(); - info.put("what", new LString("Lua")); - info.put("source", new LString(s.replace('@','='))); - info.put("short_src", new LString(s.substring(1))); - info.put("linedefined", closure.p.linedefined); - info.put("lastlinedefined", closure.p.lastlinedefined); + if ( si.luainfo != null ) { + LClosure c = si.luainfo.closure; + info.put("what", LUA); + info.put("source", c.p.source); + info.put("short_src", new LString(si.luainfo.sourcename())); + info.put("linedefined", c.p.linedefined); + info.put("lastlinedefined", c.p.lastlinedefined); } else { - info.put("what", new LString("Java")); - info.put("source", new LString("[Java]")); - info.put("short_src", new LString("[Java]")); + info.put("what", JAVA); + info.put("source", JAVASRC); + info.put("short_src", JAVASRC); info.put("linedefined", -1); + info.put("lastlinedefined", -1); } break; } case 'l': { - info.put( "currentline", currentline(threadVm, ci, func) ); + int line = si.currentline(); + info.put( "currentline", line ); break; } case 'u': { - info.put("nups", (closure!=null? closure.p.nups: 0)); + info.put("nups", (si.luainfo!=null? si.luainfo.closure.p.nups: 0)); break; } case 'n': { - // TODO: name - info.put("name", (new LString(closure!=null? "?": func.toString()))); - info.put("namewhat", new LString("")); + LString[] kind = si.getfunckind(); + info.put("name", kind!=null? kind[0]: QMARK); + info.put("namewhat", kind!=null? kind[1]: NOSTRING); break; } case 'f': { - info.put( "func", closure ); + info.put( "func", si.luainfo!=null? si.luainfo.closure: si.javafunc ); break; } case 'L': { LTable lines = new LTable(); info.put("activelines", lines); - for ( int j=threadVm.cc, k=1; j>=0; --j ) - if ( threadVm.calls[j].closure == func ) { - int line = threadVm.debugGetLineNumber(ci); - if ( line >= 0 ) - lines.put(k++, LInteger.valueOf(line)); - } + if ( si.luainfo != null ) { + int line = threadVm.debugGetLineNumber(si.luainfo); + if ( line >= 0 ) + lines.put(1, LInteger.valueOf(line)); + } break; } } } } - - private int currentline(LuaState vm, CallInfo ci, LFunction func) { - if ( ci == null ) { - ci = findcallinfo(vm, func); - if ( ci == null ) - return -1; - } - return vm.debugGetLineNumber(ci); - } - - private CallInfo findcallinfo(LuaState vm, LFunction func) { - for ( int i=vm.cc; i>=0; --i ) - if ( vm.calls[i].closure == func ) - return vm.calls[i]; - return null; - } - - private LString findlocal(LuaState vm, int cc, int n) { - CallInfo ci = vm.calls[cc]; - LString name; - LPrototype fp = ci.closure.p; - if ( fp!=null && (name = fp.getlocalname(n, ci.pc-1)) != null) - return name; - return null; - } - - /** pushes the value onto the stack, returns the name or null */ - private LString getlocal(LuaState vm, int cc, int n) { - LString name = findlocal(vm, cc, n); - if ( name != null ) - vm.pushlvalue( vm.stack[vm.calls[cc].base+(n-1)] ); - return name; - } - - /** pops the value onto the stack, sets it to the local, return name or null */ - private LString setlocal(LuaState vm, int cc, int n) { - LString name = findlocal(vm, cc, n); - if ( name != null ) - vm.stack[vm.calls[cc].base+(n-1)] = vm.poplvalue(); - return name; - } - private void getlocal(LuaState vm) { - LuaState threadVm = vm; - if ( vm.gettop() >= 4 ) { - threadVm = vm.checkthread(2).vm; - vm.remove(2); - } + protected void getlocal(LuaState vm) { + LuaState threadVm = optthreadvm(vm, 2); int level = vm.checkint(2); int local = vm.checkint(3); - LString name = getlocal(threadVm, threadVm.cc-(level-1), local); + StackInfo si = getstackinfo(threadVm, level, 1)[0]; + CallInfo ci = (si!=null? si.luainfo: null); + LPrototype p = (ci!=null? ci.closure.p: null); + LString name = (p!=null? p.getlocalname(local, ci.pc>0? ci.pc-1: 0): null); if ( name != null ) { - LValue value = vm.poplvalue(); + LValue value = threadVm.stack[ci.base+(local-1)]; vm.resettop(); - vm.pushlvalue(name); - vm.pushlvalue(value); + vm.pushlvalue( name ); + vm.pushlvalue( value ); } else { vm.resettop(); vm.pushnil(); } } - private void setlocal(LuaState vm) { - LuaState threadVm = vm; - if ( vm.gettop() >= 5 ) { - threadVm = vm.checkthread(2).vm; - vm.remove(2); - } + protected void setlocal(LuaState vm) { + LuaState threadVm = optthreadvm(vm, 2); int level = vm.checkint(2); int local = vm.checkint(3); - vm.settop(4); - LString name = setlocal(threadVm, threadVm.cc-(level-1), local); - vm.resettop(); + LValue value = vm.topointer(4); + StackInfo si = getstackinfo(threadVm, level, 1)[0]; + CallInfo ci = (si!=null? si.luainfo: null); + LPrototype p = (ci!=null? ci.closure.p: null); + LString name = (p!=null? p.getlocalname(local, ci.pc>0? ci.pc-1: 0): null); if ( name != null ) { + threadVm.stack[ci.base+(local-1)] = value; + vm.resettop(); vm.pushlvalue(name); } else { + vm.resettop(); vm.pushnil(); } } - // return callinfo if level is a lua call, LFunction if a java call - private Object getcallinfoorfunction(LuaState vm, int level) { - if ( level < 0 ) - return null; - for ( int i=vm.cc; i>=0; --i ) { - CallInfo ci = vm.calls[i]; - int pc = ci.pc>0? ci.pc-1: 0; - int instr = ci.closure.p.code[pc]; - if ( Lua.GET_OPCODE(instr) == Lua.OP_CALL ) { - LValue f = vm.stack[ci.base + Lua.GETARG_A(instr)]; - if ( f.isFunction() && ! (f instanceof LClosure) ) { - if ( (level--) <= 0 ) - return f; - } - } - if ( (level--) <= 0 ) - return ci; - } - return null; - } - - private void getmetatable(LuaState vm) { + protected void getmetatable(LuaState vm) { LValue object = vm.topointer(2); vm.resettop(); LValue mt = object.luaGetMetatable(); @@ -392,7 +339,7 @@ public class DebugLib extends LFunction { vm.pushnil(); } - private void setmetatable(LuaState vm) { + protected void setmetatable(LuaState vm) { LValue object = vm.topointer(2); try { if ( ! vm.isnoneornil(3) ) @@ -408,7 +355,7 @@ public class DebugLib extends LFunction { } } - private void getregistry(LuaState vm) { + protected void getregistry(LuaState vm) { vm.resettop(); vm.pushlvalue( new LTable() ); } @@ -423,7 +370,7 @@ public class DebugLib extends LFunction { return null; } - private void getupvalue(LuaState vm) { + protected void getupvalue(LuaState vm) { LFunction func = vm.checkfunction(2); int up = vm.checkint(3); vm.resettop(); @@ -439,7 +386,7 @@ public class DebugLib extends LFunction { vm.pushnil(); } - private void setupvalue(LuaState vm) { + protected void setupvalue(LuaState vm) { LFunction func = vm.checkfunction(2); int up = vm.checkint(3); LValue value = vm.topointer(4); @@ -456,20 +403,396 @@ public class DebugLib extends LFunction { vm.pushnil(); } - private void traceback(LuaState vm) { - LuaState threadVm = vm; - int level = 1; + protected void traceback(LuaState vm) { + LuaState threadVm = optthreadvm(vm, 2); String message = ""; - if ( vm.gettop() >= 4 ) { - threadVm = vm.checkthread(2).vm; - vm.remove(2); + int level = vm.optint(3,1); + if ( ! vm.isnoneornil(2) ) + message = vm.checkstring(2)+"\n"; + StackInfo[] s = getstackinfo(threadVm, level, 10); + StringBuffer sb = new StringBuffer("stack traceback:"); + for ( int i=0; i= 3 ) - level = vm.optint(3,1); - if ( vm.gettop() >= 2 ) - message = vm.tostring(2)+"\n"; - String trace = threadVm.getStackTrace(level); vm.resettop(); - vm.pushstring(message+trace); + vm.pushstring(message+sb); } + + // ======================================================= + + private static void lua_assert(boolean x) { + if (!x) throw new RuntimeException("lua_assert failed"); + } + + + private static class StackInfo { + private LuaState vm; + private CallInfo caller; // or null if first item on stack + private int stackpos; // offset into stack + private CallInfo luainfo; // or null if a java function + private LValue javafunc; // or null if a lua call + public StackInfo(LuaState vm, CallInfo caller, int stackpos, CallInfo luainfo, LValue javafunc) { + this.vm = vm; + this.caller = caller; + this.stackpos = stackpos; + this.luainfo = luainfo; + this.javafunc = javafunc; + } + public String sourceline() { + if ( luainfo != null ) { + String s = luainfo.closure.p.source.toJavaString(); + int line = currentline(); + return (s.startsWith("@")||s.startsWith("=")? s.substring(1): s) + ":" + line; + } else { + return "[Java]"; + } + } + public LString[] getfunckind() { + return (caller!=null && stackpos>=0? getobjname(vm, caller, stackpos): null); + } + public int currentline() { + return luainfo!=null? luainfo.currentline(): -1; + } + public String tracename() { + if ( caller == null ) + return "main chunk"; + if ( javafunc != null ) + return javafunc.toString(); + LString[] kind = getfunckind(); + if ( kind == null ) + return "function ?"; + return "function "+kind[0].toJavaString(); + } + } + + + /** + * @param level first level to report + * @return array StackInfo with countlevels items, some may be null! + */ + private static StackInfo[] getstackinfo(LuaState vm, int level, int countlevels) { + StackInfo[] si = new StackInfo[countlevels]; + int i = 0; + for (int j=vm.cc; j>=0; --j) { + + CallInfo ci = vm.calls[j]; + int instr = ci.closure.p.code[ci.pc>0? ci.pc-1: 0]; + + // java function? + if ( Lua.GET_OPCODE(instr) == Lua.OP_CALL ) { + int a = Lua.GETARG_A(instr); + LValue f = vm.stack[ci.base + a]; + if ( f.isFunction() ) { + + // add the lua closure + if ( j < vm.cc ) { + if ( (level--) <= 0 ) { + CallInfo ci1 = vm.calls[j+1]; + LValue f1 = vm.calls[j+1].closure; + if ( f != f1 ) + a = ci1.resultbase-ci.base; + si[i++] = new StackInfo( vm, ci, a, ci1, null); + if ( i >= countlevels ) + return si; + } + } + + // is there also a java call? + if ( ! f.isClosure() ) { + if ( (level--) <= 0 ) { + si[i++] = new StackInfo( vm, ci, a, null, f); + if ( i >= countlevels ) + return si; + } + } + + // TODO: tail calls, for loops + } + } + } + + // first call is a plain call with no enclosing frame + if ( (level--) <= 0 ) + si[i++] = new StackInfo(vm, null, -1, vm.calls[0], null); + + return si; + } + + // look up a function in the stack, if it exists + private static StackInfo findstackinfo(LuaState vm, LFunction func) { + for (int j=vm.cc; j>=0; --j) { + CallInfo ci = vm.calls[j]; + int instr = ci.closure.p.code[ci.pc>0? ci.pc-1: 0]; + if ( Lua.GET_OPCODE(instr) == Lua.OP_CALL ) { + int a = Lua.GETARG_A(instr); + if ( func == vm.stack[ci.base + a] ) + return new StackInfo(vm, ci, a, null, func); + if ( func == ci.closure ) + return new StackInfo(vm, (j>0? vm.calls[j-1]: null), 0, ci, null); + } + } + return new StackInfo(vm, null, -1, null, func); + } + + // return LString[] { name, namewhat } if found, null if not + private static LString[] getobjname(LuaState L, CallInfo ci, int stackpos) { + LString name; + if (ci.isLua()) { /* a Lua function? */ + LPrototype p = ci.closure.p; + int pc = (ci.pc > 0 ? ci.pc - 1 : 0); // currentpc(L, ci); + int i;// Instruction i; + name = p.getlocalname(stackpos + 1, pc); + if (name != null) /* is a local? */ + return new LString[] { name, LOCAL }; + i = symbexec(p, pc, stackpos); /* try symbolic execution */ + lua_assert(pc != -1); + switch (Lua.GET_OPCODE(i)) { + case Lua.OP_GETGLOBAL: { + int g = Lua.GETARG_Bx(i); /* global index */ + // lua_assert(p.k[g].isString()); + return new LString[] { p.k[g].luaAsString(), GLOBAL }; + } + case Lua.OP_MOVE: { + int a = Lua.GETARG_A(i); + int b = Lua.GETARG_B(i); /* move from `b' to `a' */ + if (b < a) + return getobjname(L, ci, b); /* get name for `b' */ + break; + } + case Lua.OP_GETTABLE: { + int k = Lua.GETARG_C(i); /* key index */ + name = kname(p, k); + return new LString[] { name, FIELD }; + } + case Lua.OP_GETUPVAL: { + int u = Lua.GETARG_B(i); /* upvalue index */ + name = u < p.upvalues.length ? p.upvalues[u] : QMARK; + return new LString[] { name, UPVALUE }; + } + case Lua.OP_SELF: { + int k = Lua.GETARG_C(i); /* key index */ + name = kname(p, k); + return new LString[] { name, METHOD }; + } + default: + break; + } + } + return null; /* no useful name found */ + } + + private static LString kname(LPrototype p, int c) { + if (Lua.ISK(c) && p.k[Lua.INDEXK(c)].isString()) + return p.k[Lua.INDEXK(c)].luaAsString(); + else + return QMARK; + } + + private static boolean checkreg(LPrototype pt,int reg) { + return (reg < pt.maxstacksize); + } + + private static boolean precheck(LPrototype pt) { + if (!(pt.maxstacksize <= MAXSTACK)) return false; + lua_assert(pt.numparams + (pt.is_vararg & Lua.VARARG_HASARG) <= pt.maxstacksize); + lua_assert((pt.is_vararg & Lua.VARARG_NEEDSARG) == 0 + || (pt.is_vararg & Lua.VARARG_HASARG) != 0); + if (!(pt.upvalues.length <= pt.nups)) return false; + if (!(pt.lineinfo.length == pt.code.length || pt.lineinfo.length == 0)) return false; + if (!(Lua.GET_OPCODE(pt.code[pt.code.length - 1]) == Lua.OP_RETURN)) return false; + return true; + } + + private static boolean checkopenop(LPrototype pt,int pc) { + int i = pt.code[(pc)+1]; + switch (Lua.GET_OPCODE(i)) { + case Lua.OP_CALL: + case Lua.OP_TAILCALL: + case Lua.OP_RETURN: + case Lua.OP_SETLIST: { + if (!(Lua.GETARG_B(i) == 0)) return false; + return true; + } + default: + return false; /* invalid instruction after an open call */ + } + } + + //static int checkArgMode (LPrototype pt, int r, enum OpArgMask mode) { + private static boolean checkArgMode (LPrototype pt, int r, int mode) { + switch (mode) { + case Lua.OpArgN: if (!(r == 0)) return false; break; + case Lua.OpArgU: break; + case Lua.OpArgR: checkreg(pt, r); break; + case Lua.OpArgK: + if (!(Lua.ISK(r) ? Lua.INDEXK(r) < pt.k.length : r < pt.maxstacksize)) return false; + break; + } + return true; + } + + + // return last instruction, or 0 if error + private static int symbexec(LPrototype pt, int lastpc, int reg) { + int pc; + int last; /* stores position of last instruction that changed `reg' */ + last = pt.code.length - 1; /* + * points to final return (a `neutral' + * instruction) + */ + if (!(precheck(pt))) return 0; + for (pc = 0; pc < lastpc; pc++) { + int i = pt.code[pc]; + int op = Lua.GET_OPCODE(i); + int a = Lua.GETARG_A(i); + int b = 0; + int c = 0; + if (!(op < Lua.NUM_OPCODES)) return 0; + if (!checkreg(pt, a)) return 0; + switch (Lua.getOpMode(op)) { + case Lua.iABC: { + b = Lua.GETARG_B(i); + c = Lua.GETARG_C(i); + if (!(checkArgMode(pt, b, Lua.getBMode(op)))) return 0; + if (!(checkArgMode(pt, c, Lua.getCMode(op)))) return 0; + break; + } + case Lua.iABx: { + b = Lua.GETARG_Bx(i); + if (Lua.getBMode(op) == Lua.OpArgK) + if (!(b < pt.k.length)) return 0; + break; + } + case Lua.iAsBx: { + b = Lua.GETARG_sBx(i); + if (Lua.getBMode(op) == Lua.OpArgR) { + int dest = pc + 1 + b; + if (!(0 <= dest && dest < pt.code.length)) return 0; + if (dest > 0) { + /* cannot jump to a setlist count */ + int d = pt.code[dest - 1]; + if ((Lua.GET_OPCODE(d) == Lua.OP_SETLIST && Lua.GETARG_C(d) == 0)) return 0; + } + } + break; + } + } + if (Lua.testAMode(op)) { + if (a == reg) + last = pc; /* change register `a' */ + } + if (Lua.testTMode(op)) { + if (!(pc + 2 < pt.code.length)) return 0; /* check skip */ + if (!(Lua.GET_OPCODE(pt.code[pc + 1]) == Lua.OP_JMP)) return 0; + } + switch (op) { + case Lua.OP_LOADBOOL: { + if (!(c == 0 || pc + 2 < pt.code.length)) return 0; /* check its jump */ + break; + } + case Lua.OP_LOADNIL: { + if (a <= reg && reg <= b) + last = pc; /* set registers from `a' to `b' */ + break; + } + case Lua.OP_GETUPVAL: + case Lua.OP_SETUPVAL: { + if (!(b < pt.nups)) return 0; + break; + } + case Lua.OP_GETGLOBAL: + case Lua.OP_SETGLOBAL: { + if (!(pt.k[b].isString())) return 0; + break; + } + case Lua.OP_SELF: { + if (!checkreg(pt, a + 1)) return 0; + if (reg == a + 1) + last = pc; + break; + } + case Lua.OP_CONCAT: { + if (!(b < c)) return 0; /* at least two operands */ + break; + } + case Lua.OP_TFORLOOP: { + if (!(c >= 1)) return 0; /* at least one result (control variable) */ + if (!checkreg(pt, a + 2 + c)) return 0; /* space for results */ + if (reg >= a + 2) + last = pc; /* affect all regs above its base */ + break; + } + case Lua.OP_FORLOOP: + case Lua.OP_FORPREP: + if (!checkreg(pt, a + 3)) return 0; + /* go through */ + case Lua.OP_JMP: { + int dest = pc + 1 + b; + /* not full check and jump is forward and do not skip `lastpc'? */ + if (reg != Lua.NO_REG && pc < dest && dest <= lastpc) + pc += b; /* do the jump */ + break; + } + case Lua.OP_CALL: + case Lua.OP_TAILCALL: { + if (b != 0) { + if (!checkreg(pt, a + b - 1)) return 0; + } + c--; /* c = num. returns */ + if (c == Lua.LUA_MULTRET) { + if (!(checkopenop(pt, pc))) return 0; + } else if (c != 0) + if (!checkreg(pt, a + c - 1)) return 0; + if (reg >= a) + last = pc; /* affect all registers above base */ + break; + } + case Lua.OP_RETURN: { + b--; /* b = num. returns */ + if (b > 0) + if (!checkreg(pt, a + b - 1)) return 0; + break; + } + case Lua.OP_SETLIST: { + if (b > 0) + if (!checkreg(pt, a + b)) return 0; + if (c == 0) + pc++; + break; + } + case Lua.OP_CLOSURE: { + int nup, j; + if (!(b < pt.p.length)) return 0; + nup = pt.p[b].nups; + if (!(pc + nup < pt.code.length)) return 0; + for (j = 1; j <= nup; j++) { + int op1 = Lua.GET_OPCODE(pt.code[pc + j]); + if (!(op1 == Lua.OP_GETUPVAL || op1 == Lua.OP_MOVE)) return 0; + } + if (reg != Lua.NO_REG) /* tracing? */ + pc += nup; /* do not 'execute' these pseudo-instructions */ + break; + } + case Lua.OP_VARARG: { + if (!((pt.is_vararg & Lua.VARARG_ISVARARG) != 0 + && (pt.is_vararg & Lua.VARARG_NEEDSARG) == 0)) return 0; + b--; + if (b == Lua.LUA_MULTRET) + if (!(checkopenop(pt, pc))) return 0; + if (!checkreg(pt, a + b - 1)) return 0; + break; + } + default: + break; + } + } + return pt.code[last]; + } + } diff --git a/src/core/org/luaj/vm/CallInfo.java b/src/core/org/luaj/vm/CallInfo.java index 91907872..99194f91 100644 --- a/src/core/org/luaj/vm/CallInfo.java +++ b/src/core/org/luaj/vm/CallInfo.java @@ -44,4 +44,18 @@ public class CallInfo { return true; } + /** + * @return current line number, or -1 if no line info found + */ + public int currentline() { + int[] li = closure.p.lineinfo; + if ( li != null && pc <= li.length ) + return li[pc>0? pc-1: pc]; + return -1; + } + + public String sourcename() { + return LoadState.getSourceName(closure.p.source.toJavaString()); + } + } diff --git a/src/core/org/luaj/vm/LPrototype.java b/src/core/org/luaj/vm/LPrototype.java index b86849a2..0c27e14f 100644 --- a/src/core/org/luaj/vm/LPrototype.java +++ b/src/core/org/luaj/vm/LPrototype.java @@ -69,4 +69,5 @@ public class LPrototype { } return null; /* not found */ } + } diff --git a/src/core/org/luaj/vm/LoadState.java b/src/core/org/luaj/vm/LoadState.java index 1304a404..3667fabb 100644 --- a/src/core/org/luaj/vm/LoadState.java +++ b/src/core/org/luaj/vm/LoadState.java @@ -44,7 +44,7 @@ public class LoadState { public static LuaCompiler compiler = null; /** Signature byte indicating the file is a compiled binary chunk */ - private static final byte[] LUA_SIGNATURE = "\033Lua".getBytes(); + private static final byte[] LUA_SIGNATURE = { '\033', 'L', 'u', 'a' }; /** Name for compiled chunks */ public static final String SOURCE_BINARY_STRING = "binary string"; diff --git a/src/core/org/luaj/vm/LuaErrorException.java b/src/core/org/luaj/vm/LuaErrorException.java index 18d611b2..3c02d88d 100644 --- a/src/core/org/luaj/vm/LuaErrorException.java +++ b/src/core/org/luaj/vm/LuaErrorException.java @@ -82,6 +82,6 @@ public class LuaErrorException extends RuntimeException { else vm = LuaState.mainState; } - return vm != null? vm.getFileLine(vm.cc + 1 - level) + ": " + message: message; + return vm != null? vm.getFileLine(level) + ": " + message: message; } } diff --git a/src/core/org/luaj/vm/LuaState.java b/src/core/org/luaj/vm/LuaState.java index be41c312..cb229253 100644 --- a/src/core/org/luaj/vm/LuaState.java +++ b/src/core/org/luaj/vm/LuaState.java @@ -1077,80 +1077,32 @@ public class LuaState extends Lua { // Lua Java API //=============================================================== - /** - * Returns the current program counter for the given call frame. - * @param ci -- A call frame - * @return the current program counter for the given call frame. - */ - protected int getCurrentPc(CallInfo ci) { - int pc = ci.pc; - return pc > 0 ? pc - 1 : 0; - } - protected String getSourceFileName(LString source) { - String sourceStr = LoadState.getSourceName(source.toJavaString()); - return getSourceFileName(sourceStr); - } - - protected String getSourceFileName(String sourceStr) { - if (!LoadState.SOURCE_BINARY_STRING.equals(sourceStr)) { - sourceStr = sourceStr.replace('\\', '/'); - } - - int index = sourceStr.lastIndexOf('/'); - if (index != -1) { - sourceStr = sourceStr.substring(index + 1); - } - - return sourceStr; - } - /** * Get the file line number info for a particular call frame. * @param cindex index into call stack * @return */ - protected String getFileLine(int cindex) { - String source = "?"; - String line = ""; - if (cindex >= 0 && cindex <= cc) { - CallInfo call = this.calls[cindex]; - LPrototype p = call.closure.p; - if (p != null && p.source != null) - source = getSourceFileName(p.source); - int pc = getCurrentPc(call); - if (p.lineinfo != null && p.lineinfo.length > pc) - line = ":" + String.valueOf(p.lineinfo[pc]); - } - return source + line; - } - - public String getStackTrace() { - return "Stack Trace:\n"+getStackTrace(0); - } - - public String getStackTrace(int level) { - StringBuffer buffer = new StringBuffer(); - String between = " "; - for (int i=cc; i>=0; --i) { - CallInfo ci = calls[i]; - int pc = ci.pc>0? ci.pc-1: 0; - int instr = ci.closure.p.code[pc]; + protected String getFileLine(int level) { + for (int j=cc; j>=0; --j) { + CallInfo ci = calls[j]; + int instr = ci.closure.p.code[ci.pc>0? ci.pc-1: 0]; if ( Lua.GET_OPCODE(instr) == Lua.OP_CALL ) { - LValue f = stack[ci.base + Lua.GETARG_A(instr)]; - if ( f.isFunction() && ! (f instanceof LClosure) ) { + int a = Lua.GETARG_A(instr); + LValue f = stack[ci.base + a]; + if ( f.isFunction() ) { + if ( ! f.isClosure() ) { + if ( (level--) <= 0 ) { + return "[Java]: "+f.toString(); + } + } if ( (level--) <= 0 ) { - buffer.append(between+"[Java]: in function "+f.toString()); - between = "\n "; + return ci.sourcename()+":"+ci.currentline(); } } } - if ( (level--) <= 0 ) { - buffer.append(between+getFileLine(i)); - between = "\n "; - } - } - return buffer.toString(); + } + return ""; } /** @@ -2490,12 +2442,11 @@ public class LuaState extends Lua { } } - public int debugGetLineNumber(CallInfo ci) { - int[] li = ci.closure.p.lineinfo; - int pc = ci.pc>0? ci.pc-1: 0; - if ( li != null && pc < li.length ) - return li[pc]; - return -1; + /** + * @deprecated use CallInfo.currentline() instead + */ + public static int debugGetLineNumber(CallInfo ci) { + return ci.currentline(); } private void debugCallHook(int mask, int line) {