From 71f539ab2d3aae773993f588b0f176e40059186a Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Wed, 8 Apr 2009 14:40:52 +0000 Subject: [PATCH] Improve handling of tail calls in debug info --- src/core/org/luaj/lib/DebugLib.java | 56 ++++++++------------- src/core/org/luaj/vm/CallInfo.java | 26 ++++++++++ src/core/org/luaj/vm/LuaErrorException.java | 6 +-- src/core/org/luaj/vm/LuaState.java | 28 +++++------ 4 files changed, 63 insertions(+), 53 deletions(-) diff --git a/src/core/org/luaj/lib/DebugLib.java b/src/core/org/luaj/lib/DebugLib.java index 50ae6c40..2b633b7f 100644 --- a/src/core/org/luaj/lib/DebugLib.java +++ b/src/core/org/luaj/lib/DebugLib.java @@ -483,47 +483,35 @@ public class DebugLib extends LFunction { private static StackInfo[] getstackinfo(LuaState vm, int level, int countlevels) { StackInfo[] si = new StackInfo[countlevels]; int i = 0; + LClosure prevclosure = null; 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() ) { + LFunction f = ci.currentfunc(vm); - // 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, (LFunction) f); - if ( i >= countlevels ) - return si; - } - } - - // TODO: tail calls, for loops + // java, or tailcall? + if ( f != null && (! f.isClosure() || f!=prevclosure) ) { + if ( (level--) <= 0 ) { + si[i++] = new StackInfo( vm, ci, ci.currentfunca(vm), null, f); + if ( i >= countlevels ) + return si; } } + + // add the lua closure + if ( (level--) <= 0 ) { + if (j>0 && vm.calls[j-1].currentfunc(vm) == ci.closure) { + CallInfo caller = vm.calls[j-1]; + int callera = caller.currentfunca(vm); + si[i++] = new StackInfo( vm, caller, callera, ci, ci.closure); + } else { + si[i++] = new StackInfo( vm, null, -1, ci, ci.closure); + } + if ( i >= countlevels ) + return si; + } + prevclosure = ci.closure; } - - // 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; } diff --git a/src/core/org/luaj/vm/CallInfo.java b/src/core/org/luaj/vm/CallInfo.java index bbf2d8bf..60cd9e6a 100644 --- a/src/core/org/luaj/vm/CallInfo.java +++ b/src/core/org/luaj/vm/CallInfo.java @@ -54,4 +54,30 @@ public class CallInfo { return -1; } + /** + * @param vm + * @return current function executing, or null + */ + public LFunction currentfunc(LuaState vm) { + int a = currentfunca(vm); + if ( a >= 0 ) { + LValue v = vm.stack[base + a]; + if ( v.isFunction() ) + return (LFunction) v; + } + return null; + } + + /** + * @param vm + * @return register of the current function executing, or null + */ + public int currentfunca(LuaState vm) { + int i = closure.p.code[pc>0? pc-1: 0]; + int op = Lua.GET_OPCODE(i); + if ( op == Lua.OP_CALL || op == Lua.OP_TAILCALL ) + return Lua.GETARG_A(i); + return -1; + } + } diff --git a/src/core/org/luaj/vm/LuaErrorException.java b/src/core/org/luaj/vm/LuaErrorException.java index 3c02d88d..5039aff2 100644 --- a/src/core/org/luaj/vm/LuaErrorException.java +++ b/src/core/org/luaj/vm/LuaErrorException.java @@ -53,14 +53,14 @@ public class LuaErrorException extends RuntimeException { * @param message message to supply */ public LuaErrorException(String message) { - this(null, message, 1); + this(null, message, -1); } /** * Construct the message around a specific vm and with a particular level of debug info * @param vm * @param message - * @param level + * @param level 0 for no message, >=1 for current call or above, -1 for most recent lua call */ public LuaErrorException(LuaState vm, String message, int level) { super( addLineInfo( vm, message, level ) ); @@ -74,7 +74,7 @@ public class LuaErrorException extends RuntimeException { * @return */ private static String addLineInfo(LuaState vm, String message, int level) { - if ( level < 1 || message == null ) + if ( level == 0 || message == null ) return message; if ( vm == null ) { if ( LThread.running != null ) diff --git a/src/core/org/luaj/vm/LuaState.java b/src/core/org/luaj/vm/LuaState.java index a4b2e8f7..f27cf16b 100644 --- a/src/core/org/luaj/vm/LuaState.java +++ b/src/core/org/luaj/vm/LuaState.java @@ -1080,27 +1080,23 @@ public class LuaState extends Lua { /** * Get the file line number info for a particular call frame. - * @param cindex index into call stack + * @param cindex index into call stack, or -1 to get first lua location * @return */ protected String getFileLine(int level) { + LClosure c = null; 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 ) { - 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 ) { - return ci.closure.p.sourceshort()+":"+ci.currentline(); - } + LFunction f = ci.currentfunc(this); + if ( f != null && (!f.isClosure() || f!=c) ) { + if ( level != -1 && (level--) <= 0 ) { + return "[Java]: "+f.toString(); } } + c = ci.closure; + if ( (level--) <= 0 ) { + return c.p.sourceshort()+":"+ci.currentline(); + } } return ""; } @@ -1121,7 +1117,7 @@ public class LuaState extends Lua { * Raises an error with the default level. */ public void error(String message) { - throw new LuaErrorException( this, message, 1 ); + throw new LuaErrorException( this, message, -1 ); } /** @@ -2462,7 +2458,7 @@ public class LuaState extends Lua { case LUA_HOOKCOUNT: this.pushstring("count"); break; case LUA_HOOKCALL: this.pushstring("call"); break; case LUA_HOOKRET: this.pushstring("return"); break; - case LUA_HOOKTAILRET: this.pushstring("return"); break; + case LUA_HOOKTAILRET: this.pushstring("tail return"); break; default: lineval = LInteger.valueOf(line); this.pushstring("line");