From 0f40feb6258a8f1fad9ffdb03a20c46a8e13ee32 Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Tue, 24 Mar 2009 14:27:59 +0000 Subject: [PATCH] Add optional debug library. --- src/core/org/luaj/lib/DebugLib.java | 372 ++++++++++++++++++++ src/core/org/luaj/vm/LThread.java | 36 +- src/core/org/luaj/vm/LuaErrorException.java | 2 +- src/core/org/luaj/vm/LuaState.java | 8 +- 4 files changed, 397 insertions(+), 21 deletions(-) create mode 100644 src/core/org/luaj/lib/DebugLib.java diff --git a/src/core/org/luaj/lib/DebugLib.java b/src/core/org/luaj/lib/DebugLib.java new file mode 100644 index 00000000..ca916a9c --- /dev/null +++ b/src/core/org/luaj/lib/DebugLib.java @@ -0,0 +1,372 @@ +/******************************************************************************* +* Copyright (c) 2009 LuaJ. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.lib; + + +import org.luaj.vm.CallInfo; +import org.luaj.vm.LClosure; +import org.luaj.vm.LFunction; +import org.luaj.vm.LInteger; +import org.luaj.vm.LNil; +import org.luaj.vm.LString; +import org.luaj.vm.LTable; +import org.luaj.vm.LValue; +import org.luaj.vm.LocVars; +import org.luaj.vm.LuaState; + +public class DebugLib extends LFunction { + + private static final String[] NAMES = { + "debug", + "getfenv", + "gethook", + "getinfo", + "getlocal", + "getmetatable", + "getregistry", + "getupvalue", + "setfenv", + "sethook", + "setlocal", + "setmetatable", + "setupvalue", + "traceback", + }; + + private static final int INSTALL = 0; + private static final int DEBUG = 1; + private static final int GETFENV = 2; + private static final int GETHOOK = 3; + private static final int GETHOOKCOUNT = 4; + private static final int GETHOOKMASK = 5; + private static final int GETINFO = 6; + private static final int GETLOCAL = 7; + private static final int GETMETATABLE = 8; + private static final int GETREGISTRY = 9; + private static final int GETUPVALUE = 10; + private static final int SETFENV = 11; + private static final int SETHOOK = 12; + private static final int SETLOCAL = 13; + private static final int SETMETATABLE = 14; + private static final int SETUPVALUE = 15; + private static final int TRACEBACK = 16; + + public static void install( LTable globals ) { + LTable debug = new LTable(); + for (int i = 0; i < NAMES.length; i++) + debug.put(NAMES[i], new DebugLib(i + 1)); + globals.put("debug", debug); + PackageLib.setIsLoaded("debug", debug); + } + + private final int id; + + private DebugLib( int id ) { + this.id = id; + } + + public String toString() { + return NAMES[id]+"()"; + } + + public boolean luaStackCall( LuaState vm ) { + switch ( id ) { + case INSTALL: + install(vm._G); + break; + case DEBUG: + debug(vm); + break; + case GETFENV: + getfenv(vm); + break; + case GETHOOK: + gethook(vm); + break; + case GETHOOKCOUNT: + gethookcount(vm); + break; + case GETHOOKMASK: + gethookmask(vm); + break; + case GETINFO: + getinfo(vm); + break; + case GETLOCAL: + getlocal(vm); + break; + case GETMETATABLE: + getmetatable(vm); + break; + case GETREGISTRY: + getregistry(vm); + break; + case GETUPVALUE: + getupvalue(vm); + break; + case SETFENV: + setfenv(vm); + break; + case SETHOOK: + sethook(vm); + break; + case SETLOCAL: + setlocal(vm); + break; + case SETMETATABLE: + setmetatable(vm); + break; + case SETUPVALUE: + setupvalue(vm); + break; + case TRACEBACK: + traceback(vm); + break; + default: + LuaState.vmerror( "bad package id" ); + } + return false; + } + + private void debug(LuaState vm) { + // TODO Auto-generated method stub + vm.resettop(); + } + + private void gethook(LuaState vm) { + // TODO Auto-generated method stub + vm.resettop(); + } + + private void gethookcount(LuaState vm) { + // TODO Auto-generated method stub + vm.resettop(); + vm.pushinteger(0); + } + + private void gethookmask(LuaState vm) { + // TODO Auto-generated method stub + vm.resettop(); + vm.pushinteger(0); + } + + private void sethook(LuaState vm) { + // TODO Auto-generated method stub + vm.resettop(); + } + + private void getfenv(LuaState vm) { + LValue object = vm.topointer(2); + LValue env = object.luaGetEnv(null); + vm.pushlvalue(env!=null? env: LNil.NIL); + } + + private void setfenv(LuaState vm) { + LValue object = vm.topointer(2); + LTable table = vm.checktable(3); + object.luaSetEnv(table); + } + + private void getinfo(LuaState vm) { + LuaState threadVm = vm; + CallInfo ci = null; + LFunction func = null; + LClosure closure = null; + String what = ""; + if ( vm.gettop() >= 4 ) { + threadVm = vm.checkthread(2).vm; + vm.remove(2); + } + if ( vm.gettop() >= 3 ) { + what = vm.tostring(3); + } + if ( vm.isnumber(2) ) { + ci = this.getcallinfo(vm, threadVm, vm.tointeger(2)); + closure = ci.closure; + } else { + func = vm.checkfunction(2); + if ( func instanceof LClosure ) + closure = (LClosure) func; + } + vm.resettop(); + LTable info = new LTable(); + vm.pushlvalue(info); + for (int i = 0, n = what.length(); i < n; i++) { + switch (what.charAt(i)) { + case 'S': { + info.put("source", (closure!=null? closure.p.source: new LString("?"))); + info.put("linedefined", (closure!=null? closure.p.linedefined: 0)); + info.put("lastlinedefined", (closure!=null? closure.p.lastlinedefined: 0)); + info.put("what", new LString(what)); + break; + } + case 'l': { + info.put( "currentline", (ci!=null? ci.pc: 0) ); + break; + } + case 'u': { + info.put("nups", (closure!=null? closure.p.nups: 0)); + info.put("what", new LString(what)); + break; + } + case 'n': { + // TODO: name + info.put("name", new LString("?")); + info.put("namewhat", new LString("?")); + break; + } + case 'f': { + vm.pushlvalue( func!=null? func: LNil.NIL ); + break; + } + case 'L': { + LTable lines = new LTable(); + vm.pushlvalue(lines); + if ( closure != null ) + for ( int j=0, k=1; j= 4 ) { + threadVm = vm.checkthread(2).vm; + vm.remove(2); + } + int level = vm.checkint(2); + int local = vm.checkint(3); + CallInfo ci = getcallinfo(vm, threadVm, level); + LValue value = LNil.NIL; + LValue name = LNil.NIL; + if ( local >= 0 && local < ci.top-ci.base ) { + value = threadVm.stack[ ci.base + local ]; + LocVars[] vars = ci.closure.p.locvars; + if ( vars != null && local >= 0 && local < vars.length ) + name = vars[local].varname; + } + vm.resettop(); + vm.pushlvalue( name ); + vm.pushlvalue( value ); + } + + private void setlocal(LuaState vm) { + LuaState threadVm = vm; + if ( vm.gettop() >= 4 ) { + threadVm = vm.checkthread(2).vm; + vm.remove(2); + } + int level = vm.checkint(2); + int local = vm.checkint(3); + LValue value = vm.topointer(4); + CallInfo ci = getcallinfo(vm, threadVm, level); + LValue name = LNil.NIL; + if ( local >= 0 && local < ci.top-ci.base ) { + threadVm.stack[ ci.base + local ] = value; + LocVars[] vars = ci.closure.p.locvars; + if ( vars != null && local >= 0 && local < vars.length ) + name = vars[local].varname; + } + vm.resettop(); + vm.pushlvalue( name ); + } + + private CallInfo getcallinfo(LuaState vm, LuaState threadVm, int level) { + if ( level <= 0 || level > threadVm.cc ) + vm.error("level out of range"); + int cc = threadVm.cc-(level-1); + return threadVm.calls[cc]; + } + + private void getmetatable(LuaState vm) { + LValue object = vm.topointer(2); + vm.resettop(); + vm.pushlvalue( object.luaGetMetatable() ); + } + + private void setmetatable(LuaState vm) { + LValue object = vm.topointer(2); + LValue table = vm.totable(3); + object.luaSetMetatable(table); + vm.resettop(); + vm.pushlvalue( object ); + } + + private void getregistry(LuaState vm) { + vm.resettop(); + vm.pushlvalue( new LTable() ); + } + + private void getupvalue(LuaState vm) { + LFunction func = vm.checkfunction(2); + int up = vm.checkint(3); + vm.resettop(); + if ( func instanceof LClosure ) { + LClosure c = (LClosure) func; + if ( c.upVals != null && up > 0 && up < c.upVals.length ) { + vm.pushlvalue(c.upVals[up].getValue()); + return; + } + } + vm.pushnil(); + } + + private void setupvalue(LuaState vm) { + LFunction func = vm.checkfunction(2); + int up = vm.checkint(3); + LValue value = vm.topointer(4); + vm.resettop(); + if ( func instanceof LClosure ) { + LClosure c = (LClosure) func; + if ( c.upVals != null && up > 0 && up < c.upVals.length ) { + c.upVals[up].setValue(value); + if ( c.p.upvalues != null && up < c.p.upvalues.length ) + vm.pushlvalue( c.p.upvalues[up] ); + else + vm.pushstring( "."+up+"" ); + return; + } + } + vm.pushnil(); + } + + private void traceback(LuaState vm) { + LuaState threadVm = vm; + int level = 1; + String message = ""; + StringBuffer sb = new StringBuffer(); + if ( vm.gettop() >= 4 ) { + threadVm = vm.checkthread(2).vm; + vm.remove(2); + } + if ( vm.gettop() >= 3 ) + level = vm.optint(3,1); + if ( vm.gettop() >= 2 ) + message = vm.tostring(2)+"\n"; + message += threadVm.getStackTrace(level); + vm.resettop(); + vm.pushstring(sb.toString()); + } +} diff --git a/src/core/org/luaj/vm/LThread.java b/src/core/org/luaj/vm/LThread.java index 94130c37..8e19d712 100644 --- a/src/core/org/luaj/vm/LThread.java +++ b/src/core/org/luaj/vm/LThread.java @@ -42,13 +42,13 @@ public class LThread extends LValue implements Runnable { private int status = STATUS_SUSPENDED; - LuaState threadVm; + public final LuaState vm; private Thread thread; static LThread running; public LThread(LFunction c, LTable env) { - threadVm = new LuaState(env); - threadVm.pushlvalue(c); + vm = new LuaState(env); + vm.pushlvalue(c); } public int luaGetType() { @@ -61,7 +61,7 @@ public class LThread extends LValue implements Runnable { // Set the environment if a thread, or closure, and return 1, otherwise return 0 public boolean luaSetEnv(LTable t) { - threadVm._G = t; + vm._G = t; return true; } @@ -76,7 +76,7 @@ public class LThread extends LValue implements Runnable { public void run() { synchronized ( this ) { try { - threadVm.execute(); + vm.execute(); } finally { status = STATUS_DEAD; this.notify(); @@ -87,7 +87,7 @@ public class LThread extends LValue implements Runnable { public boolean yield() { synchronized ( this ) { if ( status != STATUS_RUNNING ) - threadVm.error(this+" not running"); + vm.error(this+" not running"); status = STATUS_SUSPENDED; if ( USE_JAVA_THREADS ) { this.notify(); @@ -96,7 +96,7 @@ public class LThread extends LValue implements Runnable { status = STATUS_RUNNING; } catch ( InterruptedException e ) { status = STATUS_DEAD; - threadVm.error(this+" "+e); + vm.error(this+" "+e); } } return false; @@ -128,12 +128,12 @@ public class LThread extends LValue implements Runnable { status = STATUS_RUNNING; // copy args in - if (threadVm.cc < 0) { - vm.xmove(threadVm, nargs); - threadVm.prepStackCall(); + if (this.vm.cc < 0) { + vm.xmove(this.vm, nargs); + this.vm.prepStackCall(); } else { - threadVm.resettop(); - vm.xmove(threadVm, nargs); + this.vm.resettop(); + vm.xmove(this.vm, nargs); } // execute in the other thread @@ -149,19 +149,19 @@ public class LThread extends LValue implements Runnable { this.wait(); } else { // run this vm until it yields - while (threadVm.cc >= 0 && status == STATUS_RUNNING) - threadVm.exec(); + while (this.vm.cc >= 0 && status == STATUS_RUNNING) + this.vm.exec(); } // copy return values from yielding stack state vm.resettop(); - if ( threadVm.cc >= 0 ) { + if ( this.vm.cc >= 0 ) { vm.pushboolean(status != STATUS_DEAD); - threadVm.xmove(vm, threadVm.gettop() - 1); + this.vm.xmove(vm, this.vm.gettop() - 1); } else { vm.pushboolean(true); - threadVm.base = 0; - threadVm.xmove(vm, threadVm.gettop()); + this.vm.base = 0; + this.vm.xmove(vm, this.vm.gettop()); } } catch ( Throwable t ) { diff --git a/src/core/org/luaj/vm/LuaErrorException.java b/src/core/org/luaj/vm/LuaErrorException.java index 609aee61..18d611b2 100644 --- a/src/core/org/luaj/vm/LuaErrorException.java +++ b/src/core/org/luaj/vm/LuaErrorException.java @@ -78,7 +78,7 @@ public class LuaErrorException extends RuntimeException { return message; if ( vm == null ) { if ( LThread.running != null ) - vm = LThread.running.threadVm; + vm = LThread.running.vm; else vm = LuaState.mainState; } diff --git a/src/core/org/luaj/vm/LuaState.java b/src/core/org/luaj/vm/LuaState.java index ddc6b540..e934eaa6 100644 --- a/src/core/org/luaj/vm/LuaState.java +++ b/src/core/org/luaj/vm/LuaState.java @@ -1062,8 +1062,12 @@ public class LuaState extends Lua { } public String getStackTrace() { - StringBuffer buffer = new StringBuffer("Stack Trace:\n"); - for (int i = cc; i >= 0; i--) { + return "Stack Trace:\n"+getStackTrace(0); + } + + public String getStackTrace(int level) { + StringBuffer buffer = new StringBuffer(); + for (int i = cc-level; i >= 0; i--) { buffer.append(" "); buffer.append(getFileLine(i)); buffer.append("\n");