[Orxonox-commit 1946] r6663 - code/branches/gamestates2/data/lua
rgrieder at orxonox.net
rgrieder at orxonox.net
Wed Mar 31 01:06:55 CEST 2010
Author: rgrieder
Date: 2010-03-31 01:06:55 +0200 (Wed, 31 Mar 2010)
New Revision: 6663
Modified:
code/branches/gamestates2/data/lua/Debugger.lua
code/branches/gamestates2/data/lua/Strict.lua
Log:
svn:eol-style "native" for all text based files.
Modified: code/branches/gamestates2/data/lua/Debugger.lua
===================================================================
--- code/branches/gamestates2/data/lua/Debugger.lua 2010-03-30 23:05:28 UTC (rev 6662)
+++ code/branches/gamestates2/data/lua/Debugger.lua 2010-03-30 23:06:55 UTC (rev 6663)
@@ -1,1358 +1,1358 @@
-
---{{{ history
-
---28/03/10 ORX Adjusted show() to work with the Orxonox resource system
---15/03/06 DCN Created based on RemDebug
---28/04/06 DCN Update for Lua 5.1
---01/06/06 DCN Fix command argument parsing
--- Add step/over N facility
--- Add trace lines facility
---05/06/06 DCN Add trace call/return facility
---06/06/06 DCN Make it behave when stepping through the creation of a coroutine
---06/06/06 DCN Integrate the simple debugger into the main one
---07/06/06 DCN Provide facility to step into coroutines
---13/06/06 DCN Fix bug that caused the function environment to get corrupted with the global one
---14/06/06 DCN Allow 'sloppy' file names when setting breakpoints
---04/08/06 DCN Allow for no space after command name
---11/08/06 DCN Use io.write not print
---30/08/06 DCN Allow access to array elements in 'dump'
---10/10/06 DCN Default to breakfile for all commands that require a filename and give '-'
---06/12/06 DCN Allow for punctuation characters in DUMP variable names
---03/01/07 DCN Add pause on/off facility
---19/06/07 DCN Allow for duff commands being typed in the debugger (thanks to Michael.Bringmann at lsi.com)
--- Allow for case sensitive file systems (thanks to Michael.Bringmann at lsi.com)
-
---}}}
---{{{ description
-
---A simple command line debug system for Lua written by Dave Nichols of
---Match-IT Limited. Its public domain software. Do with it as you wish.
-
---This debugger was inspired by:
--- RemDebug 1.0 Beta
--- Copyright Kepler Project 2005 (http://www.keplerproject.org/remdebug)
-
---Usage:
--- require('debugger') --load the debug library
--- pause(message) --start/resume a debug session
-
---An assert() failure will also invoke the debugger.
-
---}}}
-
-local IsWindows = string.find(string.lower(os.getenv('OS') or ''),'^windows')
-
-local coro_debugger
-local events = { BREAK = 1, WATCH = 2, STEP = 3, SET = 4 }
-local breakpoints = {}
-local watches = {}
-local step_into = false
-local step_over = false
-local step_lines = 0
-local step_level = {main=0}
-local stack_level = {main=0}
-local trace_level = {main=0}
-local trace_calls = false
-local trace_returns = false
-local trace_lines = false
-local ret_file, ret_line, ret_name
-local current_thread = 'main'
-local started = false
-local pause_off = false
-local _g = _G
-local cocreate, cowrap = coroutine.create, coroutine.wrap
-local pausemsg = 'pause'
-
---{{{ local hints -- command help
---The format in here is name=summary|description
-local hints = {
-
-pause = [[
-pause(msg) -- start/resume a debugger session|
-
-This can only be used in your code or from the console as a means to
-start/resume a debug session.
-If msg is given that is shown when the session starts/resumes. Useful to
-give a context if you've instrumented your code with pause() statements.
-]],
-
-poff = [[
-poff -- turn off pause() command|
-
-This causes all pause() commands to be ignored. This is useful if you have
-instrumented your code in a busy loop and want to continue normal execution
-with no further interruption.
-]],
-
-pon = [[
-pon -- turn on pause() command|
-
-This re-instates honouring the pause() commands you may have instrumented
-your code with.
-]],
-
-setb = [[
-setb [line file] -- set a breakpoint to line/file|
-
-If file is omitted or is "-" the breakpoint is set at the file for the
-currently set level (see "set"). Execution pauses when this line is about
-to be executed and the debugger session is re-activated.
-
-The file can be given as the fully qualified name, partially qualified or
-just the file name. E.g. if file is set as "myfile.lua", then whenever
-execution reaches any file that ends with "myfile.lua" it will pause.
-]],
-
-delb = [[
-delb [line file] -- removes a breakpoint|
-
-If file is omitted or is "-" the breakpoint is removed for the file of the
-currently set level (see "set").
-]],
-
-delallb = [[
-delallb -- removes all breakpoints|
-]],
-
-setw = [[
-setw <exp> -- adds a new watch expression|
-
-The expression is evaluated before each line is executed. If the expression
-yields true then execution is paused and the debugger session re-activated.
-The expression is executed in the context of the line about to be executed.
-]],
-
-delw = [[
-delw <index> -- removes the watch expression at index|
-
-The index is that returned when the watch expression was set by setw.
-]],
-
-delallw = [[
-delallw -- removes all watch expressions|
-]],
-
-run = [[
-run -- run until next breakpoint or watch expression|
-]],
-
-step = [[
-step [N] -- run next N lines, stepping into function calls|
-
-If N is omitted, use 1.
-]],
-
-over = [[
-over [N] -- run next N lines, stepping over function calls|
-
-If N is omitted, use 1.
-]],
-
-out = [[
-out [N] -- run lines until stepped out of N functions|
-
-If N is omitted, use 1.
-If you are inside a function, using "out 1" will run until you return
-from that function to the caller.
-]],
-
-goto = [[
-goto <line> -- step to line number <line> in the current file|
-
-The line and current file are those in the currently set context level.
-]],
-
-listb = [[
-listb -- lists breakpoints|
-]],
-
-listw = [[
-listw -- lists watch expressions|
-]],
-
-set = [[
-set [level] -- set context to stack level, omitted=show|
-
-If level is omitted it just prints the current level set.
-This sets the current context to the level given. This affects the
-context used for several other functions (e.g. vars). The possible
-levels are those shown by trace.
-]],
-
-vars = [[
-vars [depth] -- list context locals to depth, omitted=1|
-
-If depth is omitted then uses 1.
-Use a depth of 0 for the maximum.
-Lists all non-nil local variables and all non-nil upvalues in the
-currently set context. For variables that are tables, lists all fields
-to the given depth.
-]],
-
-fenv = [[
-fenv [depth] -- list context function env to depth, omitted=1|
-
-If depth is omitted then uses 1.
-Use a depth of 0 for the maximum.
-Lists all function environment variables in the currently set context.
-For variables that are tables, lists all fields to the given depth.
-]],
-
-glob = [[
-glob [depth] -- list globals to depth, omitted=1|
-
-If depth is omitted then uses 1.
-Use a depth of 0 for the maximum.
-Lists all global variables.
-For variables that are tables, lists all fields to the given depth.
-]],
-
-ups = [[
-ups -- list all the upvalue names|
-
-These names will also be in the "vars" list unless their value is nil.
-This provides a means to identify which vars are upvalues and which are
-locals. If a name is both an upvalue and a local, the local value takes
-precedance.
-]],
-
-locs = [[
-locs -- list all the locals names|
-
-These names will also be in the "vars" list unless their value is nil.
-This provides a means to identify which vars are upvalues and which are
-locals. If a name is both an upvalue and a local, the local value takes
-precedance.
-]],
-
-dump = [[
-dump <var> [depth] -- dump all fields of variable to depth|
-
-If depth is omitted then uses 1.
-Use a depth of 0 for the maximum.
-Prints the value of <var> in the currently set context level. If <var>
-is a table, lists all fields to the given depth. <var> can be just a
-name, or name.field or name.# to any depth, e.g. t.1.f accesses field
-'f' in array element 1 in table 't'.
-
-Can also be called from a script as dump(var,depth).
-]],
-
-tron = [[
-tron [crl] -- turn trace on for (c)alls, (r)etuns, (l)lines|
-
-If no parameter is given then tracing is turned off.
-When tracing is turned on a line is printed to the console for each
-debug 'event' selected. c=function calls, r=function returns, l=lines.
-]],
-
-trace = [[
-trace -- dumps a stack trace|
-
-Format is [level] = file,line,name
-The level is a candidate for use by the 'set' command.
-]],
-
-info = [[
-info -- dumps the complete debug info captured|
-
-Only useful as a diagnostic aid for the debugger itself. This information
-can be HUGE as it dumps all variables to the maximum depth, so be careful.
-]],
-
-show = [[
-show line file X Y -- show X lines before and Y after line in file|
-
-If line is omitted or is '-' then the current set context line is used.
-If file is omitted or is '-' then the current set context file is used.
-If file is not fully qualified and cannot be opened as specified, then
-a search for the file in the package[path] is performed using the usual
-"require" searching rules. If no file extension is given, .lua is used.
-Prints the lines from the source file around the given line.
-]],
-
-exit = [[
-exit -- exits debugger, re-start it using pause()|
-]],
-
-help = [[
-help [command] -- show this list or help for command|
-]],
-
-["<statement>"] = [[
-<statement> -- execute a statement in the current context|
-
-The statement can be anything that is legal in the context, including
-assignments. Such assignments affect the context and will be in force
-immediately. Any results returned are printed. Use '=' as a short-hand
-for 'return', e.g. "=func(arg)" will call 'func' with 'arg' and print
-the results, and "=var" will just print the value of 'var'.
-]],
-
-what = [[
-what <func> -- show where <func> is defined (if known)|
-]],
-
-}
---}}}
-
---{{{ local function getinfo(level,field)
-
---like debug.getinfo but copes with no activation record at the given level
---and knows how to get 'field'. 'field' can be the name of any of the
---activation record fields or any of the 'what' names or nil for everything.
---only valid when using the stack level to get info, not a function name.
-
-local function getinfo(level,field)
- level = level + 1 --to get to the same relative level as the caller
- if not field then return debug.getinfo(level) end
- local what
- if field == 'name' or field == 'namewhat' then
- what = 'n'
- elseif field == 'what' or field == 'source' or field == 'linedefined' or field == 'lastlinedefined' or field == 'short_src' then
- what = 'S'
- elseif field == 'currentline' then
- what = 'l'
- elseif field == 'nups' then
- what = 'u'
- elseif field == 'func' then
- what = 'f'
- else
- return debug.getinfo(level,field)
- end
- local ar = debug.getinfo(level,what)
- if ar then return ar[field] else return nil end
-end
-
---}}}
---{{{ local function indented( level, ... )
-
-local function indented( level, ... )
- io.write( string.rep(' ',level), table.concat({...}), '\n' )
-end
-
---}}}
---{{{ local function dumpval( level, name, value, limit )
-
-local dumpvisited
-
-local function dumpval( level, name, value, limit )
- local index
- if type(name) == 'number' then
- index = string.format('[%d] = ',name)
- elseif type(name) == 'string'
- and (name == '__VARSLEVEL__' or name == '__ENVIRONMENT__' or name == '__GLOBALS__' or name == '__UPVALUES__' or name == '__LOCALS__') then
- --ignore these, they are debugger generated
- return
- elseif type(name) == 'string' and string.find(name,'^[_%a][_.%w]*$') then
- index = name ..' = '
- else
- index = string.format('[%q] = ',tostring(name))
- end
- if type(value) == 'table' then
- if dumpvisited[value] then
- indented( level, index, string.format('ref%q;',dumpvisited[value]) )
- else
- dumpvisited[value] = tostring(value)
- if (limit or 0) > 0 and level+1 >= limit then
- indented( level, index, dumpvisited[value] )
- else
- indented( level, index, '{ -- ', dumpvisited[value] )
- for n,v in pairs(value) do
- dumpval( level+1, n, v, limit )
- end
- indented( level, '};' )
- end
- end
- else
- if type(value) == 'string' then
- if string.len(value) > 40 then
- indented( level, index, '[[', value, ']];' )
- else
- indented( level, index, string.format('%q',value), ';' )
- end
- else
- indented( level, index, tostring(value), ';' )
- end
- end
-end
-
---}}}
---{{{ local function dumpvar( value, limit, name )
-
-local function dumpvar( value, limit, name )
- dumpvisited = {}
- dumpval( 0, name or tostring(value), value, limit )
-end
-
---}}}
---{{{ local function show(file,line,before,after)
-
---show +/-N lines of a file around line M
-
-local function show(file,line,before,after)
-
- line = tonumber(line or 1)
- before = tonumber(before or 10)
- after = tonumber(after or before)
-
- -- Try to find the file in the Orxonox resources
- local text = luaState:getSourceCode(file)
-
- if text == "" then
- if not string.find(file,'%.') then file = file..'.lua' end
-
- local f = io.open(file,'r')
- if not f then
- --{{{ try to find the file in the path
-
- --
- -- looks for a file in the package path
- --
- local path = package.path or LUA_PATH or ''
- for c in string.gmatch (path, "[^;]+") do
- local c = string.gsub (c, "%?%.lua", file)
- f = io.open (c,'r')
- if f then
- break
- end
- end
-
- --}}}
-
- if f then
- -- Read file into 'text'
- text = f:read("*a")
- f:close()
- else
- io.write('Cannot find '..file..'\n')
- return
- end
- end
- end
-
- -- Transform line endings to \n
- text:gsub("\r\n", "\n") -- Windows to Unix
- text:gsub("\r", "\n") -- Mac to Unix
- if text[-1] ~= "\n" then
- text = text.."\n"
- end
- -- Print requested lines
- local i = 0
- for l in text:gmatch("[^\n]*[\n]") do
- i = i + 1
- if i >= (line-before) then
- if i > (line+after) then break end
- if i == line then
- io.write(i..'***\t'..l)
- else
- io.write(i..'\t'..l)
- end
- end
- end
-end
-
---}}}
---{{{ local function tracestack(l)
-
-local function gi( i )
- return function() i=i+1 return debug.getinfo(i),i end
-end
-
-local function gl( level, j )
- return function() j=j+1 return debug.getlocal( level, j ) end
-end
-
-local function gu( func, k )
- return function() k=k+1 return debug.getupvalue( func, k ) end
-end
-
-local traceinfo
-
-local function tracestack(l)
- local l = l + 1 --NB: +1 to get level relative to caller
- traceinfo = {}
- traceinfo.pausemsg = pausemsg
- for ar,i in gi(l) do
- table.insert( traceinfo, ar )
- local names = {}
- local values = {}
- for n,v in gl(i,0) do
- if string.sub(n,1,1) ~= '(' then --ignore internal control variables
- table.insert( names, n )
- table.insert( values, v )
- end
- end
- if #names > 0 then
- ar.lnames = names
- ar.lvalues = values
- end
- if ar.func then
- local names = {}
- local values = {}
- for n,v in gu(ar.func,0) do
- if string.sub(n,1,1) ~= '(' then --ignore internal control variables
- table.insert( names, n )
- table.insert( values, v )
- end
- end
- if #names > 0 then
- ar.unames = names
- ar.uvalues = values
- end
- end
- end
-end
-
---}}}
---{{{ local function trace()
-
-local function trace(set)
- local mark
- for level,ar in ipairs(traceinfo) do
- if level == set then
- mark = '***'
- else
- mark = ''
- end
- io.write('['..level..']'..mark..'\t'..(ar.name or ar.what)..' in '..ar.short_src..':'..ar.currentline..'\n')
- end
-end
-
---}}}
---{{{ local function info()
-
-local function info() dumpvar( traceinfo, 0, 'traceinfo' ) end
-
---}}}
-
---{{{ local function set_breakpoint(file, line)
-
-local function set_breakpoint(file, line)
- if not breakpoints[line] then
- breakpoints[line] = {}
- end
- breakpoints[line][file] = true
-end
-
---}}}
---{{{ local function remove_breakpoint(file, line)
-
-local function remove_breakpoint(file, line)
- if breakpoints[line] then
- breakpoints[line][file] = nil
- end
-end
-
---}}}
---{{{ local function has_breakpoint(file, line)
-
---allow for 'sloppy' file names
---search for file and all variations walking up its directory hierachy
---ditto for the file with no extension
-
-local function has_breakpoint(file, line)
- if not breakpoints[line] then return false end
- local noext = string.gsub(file,"(%..-)$",'',1)
- if noext == file then noext = nil end
- while file do
- if breakpoints[line][file] then return true end
- file = string.match(file,"[:/\](.+)$")
- end
- while noext do
- if breakpoints[line][noext] then return true end
- noext = string.match(noext,"[:/\](.+)$")
- end
- return false
-end
-
---}}}
---{{{ local function capture_vars(ref,level,line)
-
-local function capture_vars(ref,level,line)
- --get vars, file and line for the given level relative to debug_hook offset by ref
-
- local lvl = ref + level --NB: This includes an offset of +1 for the call to here
-
- --{{{ capture variables
-
- local ar = debug.getinfo(lvl, "f")
- if not ar then return {},'?',0 end
-
- local vars = {__UPVALUES__={}, __LOCALS__={}}
- local i
-
- local func = ar.func
- if func then
- i = 1
- while true do
- local name, value = debug.getupvalue(func, i)
- if not name then break end
- if string.sub(name,1,1) ~= '(' then --NB: ignoring internal control variables
- vars[name] = value
- vars.__UPVALUES__[i] = name
- end
- i = i + 1
- end
- vars.__ENVIRONMENT__ = getfenv(func)
- end
-
- vars.__GLOBALS__ = getfenv(0)
-
- i = 1
- while true do
- local name, value = debug.getlocal(lvl, i)
- if not name then break end
- if string.sub(name,1,1) ~= '(' then --NB: ignoring internal control variables
- vars[name] = value
- vars.__LOCALS__[i] = name
- end
- i = i + 1
- end
-
- vars.__VARSLEVEL__ = level
-
- if func then
- --NB: Do not do this until finished filling the vars table
- setmetatable(vars, { __index = getfenv(func), __newindex = getfenv(func) })
- end
-
- --NB: Do not read or write the vars table anymore else the metatable functions will get invoked!
-
- --}}}
-
- local file = getinfo(lvl, "source")
- if string.find(file, "@") == 1 then
- file = string.sub(file, 2)
- end
- -- Orxonox changes: Our resource system is case sensisive, even on Windows
- --if IsWindows then file = string.lower(file) end
-
- if not line then
- line = getinfo(lvl, "currentline")
- end
-
- return vars,file,line
-
-end
-
---}}}
---{{{ local function restore_vars(ref,vars)
-
-local function restore_vars(ref,vars)
-
- if type(vars) ~= 'table' then return end
-
- local level = vars.__VARSLEVEL__ --NB: This level is relative to debug_hook offset by ref
- if not level then return end
-
- level = level + ref --NB: This includes an offset of +1 for the call to here
-
- local i
- local written_vars = {}
-
- i = 1
- while true do
- local name, value = debug.getlocal(level, i)
- if not name then break end
- if vars[name] and string.sub(name,1,1) ~= '(' then --NB: ignoring internal control variables
- debug.setlocal(level, i, vars[name])
- written_vars[name] = true
- end
- i = i + 1
- end
-
- local ar = debug.getinfo(level, "f")
- if not ar then return end
-
- local func = ar.func
- if func then
-
- i = 1
- while true do
- local name, value = debug.getupvalue(func, i)
- if not name then break end
- if vars[name] and string.sub(name,1,1) ~= '(' then --NB: ignoring internal control variables
- if not written_vars[name] then
- debug.setupvalue(func, i, vars[name])
- end
- written_vars[name] = true
- end
- i = i + 1
- end
-
- end
-
-end
-
---}}}
---{{{ local function trace_event(event, line, level)
-
-local function print_trace(level,depth,event,file,line,name)
-
- --NB: level here is relative to the caller of trace_event, so offset by 2 to get to there
- level = level + 2
-
- local file = file or getinfo(level,'short_src')
- local line = line or getinfo(level,'currentline')
- local name = name or getinfo(level,'name')
-
- local prefix = ''
- if current_thread ~= 'main' then prefix = '['..tostring(current_thread)..'] ' end
-
- io.write(prefix..
- string.format('%08.2f:%02i.',os.clock(),depth)..
- string.rep('.',depth%32)..
- (file or '')..' ('..(line or '')..') '..
- (name or '')..
- ' ('..event..')\n')
-
-end
-
-local function trace_event(event, line, level)
-
- if event == 'return' and trace_returns then
- --note the line info for later
- ret_file = getinfo(level+1,'short_src')
- ret_line = getinfo(level+1,'currentline')
- ret_name = getinfo(level+1,'name')
- end
-
- if event ~= 'line' then return end
-
- local slevel = stack_level[current_thread]
- local tlevel = trace_level[current_thread]
-
- if trace_calls and slevel > tlevel then
- --we are now in the function called, so look back 1 level further to find the calling file and line
- print_trace(level+1,slevel-1,'c',nil,nil,getinfo(level+1,'name'))
- end
-
- if trace_returns and slevel < tlevel then
- print_trace(level,slevel,'r',ret_file,ret_line,ret_name)
- end
-
- if trace_lines then
- print_trace(level,slevel,'l')
- end
-
- trace_level[current_thread] = stack_level[current_thread]
-
-end
-
---}}}
---{{{ local function debug_hook(event, line, level, thread)
-
-local function debug_hook(event, line, level, thread)
- if not started then debug.sethook() return end
- current_thread = thread or 'main'
- local level = level or 2
- trace_event(event,line,level)
- if event == "call" then
- stack_level[current_thread] = stack_level[current_thread] + 1
- elseif event == "return" then
- stack_level[current_thread] = stack_level[current_thread] - 1
- if stack_level[current_thread] < 0 then stack_level[current_thread] = 0 end
- else
- local vars,file,line = capture_vars(level,1,line)
- local stop, ev, idx = false, events.STEP, 0
- while true do
- for index, value in pairs(watches) do
- setfenv(value.func, vars)
- local status, res = pcall(value.func)
- if status and res then
- ev, idx = events.WATCH, index
- stop = true
- break
- end
- end
- if stop then break end
- if (step_into)
- or (step_over and (stack_level[current_thread] <= step_level[current_thread] or stack_level[current_thread] == 0)) then
- step_lines = step_lines - 1
- if step_lines < 1 then
- ev, idx = events.STEP, 0
- break
- end
- end
- if has_breakpoint(file, line) then
- ev, idx = events.BREAK, 0
- break
- end
- return
- end
- tracestack(level)
- local last_next = 1
- local err, next = assert(coroutine.resume(coro_debugger, ev, vars, file, line, idx))
- while true do
- if next == 'cont' then
- return
- elseif next == 'stop' then
- started = false
- debug.sethook()
- return
- elseif tonumber(next) then --get vars for given level or last level
- next = tonumber(next)
- if next == 0 then next = last_next end
- last_next = next
- restore_vars(level,vars)
- vars, file, line = capture_vars(level,next)
- err, next = assert(coroutine.resume(coro_debugger, events.SET, vars, file, line, idx))
- else
- io.write('Unknown command from debugger_loop: '..tostring(next)..'\n')
- io.write('Stopping debugger\n')
- next = 'stop'
- end
- end
- end
-end
-
---}}}
---{{{ local function report(ev, vars, file, line, idx_watch)
-
-local function report(ev, vars, file, line, idx_watch)
- local vars = vars or {}
- local file = file or '?'
- local line = line or 0
- local prefix = ''
- if current_thread ~= 'main' then prefix = '['..tostring(current_thread)..'] ' end
- if ev == events.STEP then
- io.write(prefix.."Paused at file "..file.." line "..line..' ('..stack_level[current_thread]..')\n')
- elseif ev == events.BREAK then
- io.write(prefix.."Paused at file "..file.." line "..line..' ('..stack_level[current_thread]..') (breakpoint)\n')
- elseif ev == events.WATCH then
- io.write(prefix.."Paused at file "..file.." line "..line..' ('..stack_level[current_thread]..')'.." (watch expression "..idx_watch.. ": ["..watches[idx_watch].exp.."])\n")
- elseif ev == events.SET then
- --do nothing
- else
- io.write(prefix.."Error in application: "..file.." line "..line.."\n")
- end
- if ev ~= events.SET then
- if pausemsg and pausemsg ~= '' then io.write('Message: '..pausemsg..'\n') end
- pausemsg = ''
- end
- return vars, file, line
-end
-
---}}}
-
---{{{ local function debugger_loop(server)
-
-local function debugger_loop(ev, vars, file, line, idx_watch)
-
- io.write("Lua Debugger\n")
- local eval_env, breakfile, breakline = report(ev, vars, file, line, idx_watch)
- io.write("Type 'help' for commands\n")
-
- local command, args
-
- --{{{ local function getargs(spec)
-
- --get command arguments according to the given spec from the args string
- --the spec has a single character for each argument, arguments are separated
- --by white space, the spec characters can be one of:
- -- F for a filename (defaults to breakfile if - given in args)
- -- L for a line number (defaults to breakline if - given in args)
- -- N for a number
- -- V for a variable name
- -- S for a string
-
- local function getargs(spec)
- local res={}
- local char,arg
- local ptr=1
- for i=1,string.len(spec) do
- char = string.sub(spec,i,i)
- if char == 'F' then
- _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
- if not arg or arg == '' then arg = '-' end
- if arg == '-' then arg = breakfile end
- elseif char == 'L' then
- _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
- if not arg or arg == '' then arg = '-' end
- if arg == '-' then arg = breakline end
- arg = tonumber(arg) or 0
- elseif char == 'N' then
- _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
- if not arg or arg == '' then arg = '0' end
- arg = tonumber(arg) or 0
- elseif char == 'V' then
- _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
- if not arg or arg == '' then arg = '' end
- elseif char == 'S' then
- _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
- if not arg or arg == '' then arg = '' end
- else
- arg = ''
- end
- table.insert(res,arg or '')
- end
- return unpack(res)
- end
-
- --}}}
-
- while true do
- io.write("[DEBUG]> ")
- local line = io.read("*line")
- if line == nil then io.write('\n'); line = 'exit' end
-
- if string.find(line, "^[a-z]+") then
- command = string.sub(line, string.find(line, "^[a-z]+"))
- args = string.gsub(line,"^[a-z]+%s*",'',1) --strip command off line
- else
- command = ''
- end
-
- if command == "setb" then
- --{{{ set breakpoint
-
- local line, filename = getargs('LF')
- if filename ~= '' and line ~= '' then
- set_breakpoint(filename,line)
- io.write("Breakpoint set in file "..filename..' line '..line..'\n')
- else
- io.write("Bad request\n")
- end
-
- --}}}
-
- elseif command == "delb" then
- --{{{ delete breakpoint
-
- local line, filename = getargs('LF')
- if filename ~= '' and line ~= '' then
- remove_breakpoint(filename, line)
- io.write("Breakpoint deleted from file "..filename..' line '..line.."\n")
- else
- io.write("Bad request\n")
- end
-
- --}}}
-
- elseif command == "delallb" then
- --{{{ delete all breakpoints
- breakpoints = {}
- io.write('All breakpoints deleted\n')
- --}}}
-
- elseif command == "listb" then
- --{{{ list breakpoints
- for i, v in pairs(breakpoints) do
- for ii, vv in pairs(v) do
- io.write("Break at: "..i..' in '..ii..'\n')
- end
- end
- --}}}
-
- elseif command == "setw" then
- --{{{ set watch expression
-
- if args and args ~= '' then
- local func = loadstring("return(" .. args .. ")")
- local newidx = #watches + 1
- watches[newidx] = {func = func, exp = args}
- io.write("Set watch exp no. " .. newidx..'\n')
- else
- io.write("Bad request\n")
- end
-
- --}}}
-
- elseif command == "delw" then
- --{{{ delete watch expression
-
- local index = tonumber(args)
- if index then
- watches[index] = nil
- io.write("Watch expression deleted\n")
- else
- io.write("Bad request\n")
- end
-
- --}}}
-
- elseif command == "delallw" then
- --{{{ delete all watch expressions
- watches = {}
- io.write('All watch expressions deleted\n')
- --}}}
-
- elseif command == "listw" then
- --{{{ list watch expressions
- for i, v in pairs(watches) do
- io.write("Watch exp. " .. i .. ": " .. v.exp..'\n')
- end
- --}}}
-
- elseif command == "run" then
- --{{{ run until breakpoint
- step_into = false
- step_over = false
- eval_env, breakfile, breakline = report(coroutine.yield('cont'))
- --}}}
-
- elseif command == "step" then
- --{{{ step N lines (into functions)
- local N = tonumber(args) or 1
- step_over = false
- step_into = true
- step_lines = tonumber(N or 1)
- eval_env, breakfile, breakline = report(coroutine.yield('cont'))
- --}}}
-
- elseif command == "over" then
- --{{{ step N lines (over functions)
- local N = tonumber(args) or 1
- step_into = false
- step_over = true
- step_lines = tonumber(N or 1)
- step_level[current_thread] = stack_level[current_thread]
- eval_env, breakfile, breakline = report(coroutine.yield('cont'))
- --}}}
-
- elseif command == "out" then
- --{{{ step N lines (out of functions)
- local N = tonumber(args) or 1
- step_into = false
- step_over = true
- step_lines = 1
- step_level[current_thread] = stack_level[current_thread] - tonumber(N or 1)
- eval_env, breakfile, breakline = report(coroutine.yield('cont'))
- --}}}
-
- elseif command == "goto" then
- --{{{ step until reach line
- local N = tonumber(args)
- if N then
- step_over = false
- step_into = false
- if has_breakpoint(breakfile,N) then
- eval_env, breakfile, breakline = report(coroutine.yield('cont'))
- else
- local bf = breakfile
- set_breakpoint(breakfile,N)
- eval_env, breakfile, breakline = report(coroutine.yield('cont'))
- if breakfile == bf and breakline == N then remove_breakpoint(breakfile,N) end
- end
- else
- io.write("Bad request\n")
- end
- --}}}
-
- elseif command == "set" then
- --{{{ set/show context level
- local level = args
- if level and level == '' then level = nil end
- if level then
- eval_env, breakfile, breakline = report(coroutine.yield(level))
- end
- if eval_env.__VARSLEVEL__ then
- io.write('Level: '..eval_env.__VARSLEVEL__..'\n')
- else
- io.write('No level set\n')
- end
- --}}}
-
- elseif command == "vars" then
- --{{{ list context variables
- local depth = args
- if depth and depth == '' then depth = nil end
- depth = tonumber(depth) or 1
- dumpvar(eval_env, depth+1, 'variables')
- --}}}
-
- elseif command == "glob" then
- --{{{ list global variables
- local depth = args
- if depth and depth == '' then depth = nil end
- depth = tonumber(depth) or 1
- dumpvar(eval_env.__GLOBALS__,depth+1,'globals')
- --}}}
-
- elseif command == "fenv" then
- --{{{ list function environment variables
- local depth = args
- if depth and depth == '' then depth = nil end
- depth = tonumber(depth) or 1
- dumpvar(eval_env.__ENVIRONMENT__,depth+1,'environment')
- --}}}
-
- elseif command == "ups" then
- --{{{ list upvalue names
- dumpvar(eval_env.__UPVALUES__,2,'upvalues')
- --}}}
-
- elseif command == "locs" then
- --{{{ list locals names
- dumpvar(eval_env.__LOCALS__,2,'upvalues')
- --}}}
-
- elseif command == "what" then
- --{{{ show where a function is defined
- if args and args ~= '' then
- local v = eval_env
- local n = nil
- for w in string.gmatch(args,"[%w_]+") do
- v = v[w]
- if n then n = n..'.'..w else n = w end
- if not v then break end
- end
- if type(v) == 'function' then
- local def = debug.getinfo(v,'S')
- if def then
- io.write(def.what..' in '..def.short_src..' '..def.linedefined..'..'..def.lastlinedefined..'\n')
- else
- io.write('Cannot get info for '..v..'\n')
- end
- else
- io.write(v..' is not a function\n')
- end
- else
- io.write("Bad request\n")
- end
- --}}}
-
- elseif command == "dump" then
- --{{{ dump a variable
- local name, depth = getargs('VN')
- if name ~= '' then
- if depth == '' or depth == 0 then depth = nil end
- depth = tonumber(depth or 1)
- local v = eval_env
- local n = nil
- for w in string.gmatch(name,"[^%.]+") do --get everything between dots
- if tonumber(w) then
- v = v[tonumber(w)]
- else
- v = v[w]
- end
- if n then n = n..'.'..w else n = w end
- if not v then break end
- end
- dumpvar(v,depth+1,n)
- else
- io.write("Bad request\n")
- end
- --}}}
-
- elseif command == "show" then
- --{{{ show file around a line or the current breakpoint
-
- local line, file, before, after = getargs('LFNN')
- if before == 0 then before = 10 end
- if after == 0 then after = before end
-
- if file ~= '' and file ~= "=stdin" then
- show(file,line,before,after)
- else
- io.write('Nothing to show\n')
- end
-
- --}}}
-
- elseif command == "poff" then
- --{{{ turn pause command off
- pause_off = true
- --}}}
-
- elseif command == "pon" then
- --{{{ turn pause command on
- pause_off = false
- --}}}
-
- elseif command == "tron" then
- --{{{ turn tracing on/off
- local option = getargs('S')
- trace_calls = false
- trace_returns = false
- trace_lines = false
- if string.find(option,'c') then trace_calls = true end
- if string.find(option,'r') then trace_returns = true end
- if string.find(option,'l') then trace_lines = true end
- --}}}
-
- elseif command == "trace" then
- --{{{ dump a stack trace
- trace(eval_env.__VARSLEVEL__)
- --}}}
-
- elseif command == "info" then
- --{{{ dump all debug info captured
- info()
- --}}}
-
- elseif command == "pause" then
- --{{{ not allowed in here
- io.write('pause() should only be used in the script you are debugging\n')
- --}}}
-
- elseif command == "help" then
- --{{{ help
- local command = getargs('S')
- if command ~= '' and hints[command] then
- io.write(hints[command]..'\n')
- else
- for _,v in pairs(hints) do
- local _,_,h = string.find(v,"(.+)|")
- io.write(h..'\n')
- end
- end
- --}}}
-
- elseif command == "exit" then
- --{{{ exit debugger
- return 'stop'
- --}}}
-
- elseif line ~= '' then
- --{{{ just execute whatever it is in the current context
-
- --map line starting with "=..." to "return ..."
- if string.sub(line,1,1) == '=' then line = string.gsub(line,'=','return ',1) end
-
- local ok, func = pcall(loadstring,line)
- if func == nil then --Michael.Bringmann at lsi.com
- io.write("Compile error: "..line..'\n')
- elseif not ok then
- io.write("Compile error: "..func..'\n')
- else
- setfenv(func, eval_env)
- local res = {pcall(func)}
- if res[1] then
- if res[2] then
- table.remove(res,1)
- for _,v in ipairs(res) do
- io.write(tostring(v))
- io.write('\t')
- end
- io.write('\n')
- end
- --update in the context
- eval_env, breakfile, breakline = report(coroutine.yield(0))
- else
- io.write("Run error: "..res[2]..'\n')
- end
- end
-
- --}}}
- end
- end
-
-end
-
---}}}
-
---{{{ coroutine.create
-
---This function overrides the built-in for the purposes of propagating
---the debug hook settings from the creator into the created coroutine.
-
-_G.coroutine.create = function(f)
- local thread
- local hook, mask, count = debug.gethook()
- if hook then
- local function thread_hook(event,line)
- hook(event,line,3,thread)
- end
- thread = cocreate(function(...)
- stack_level[thread] = 0
- trace_level[thread] = 0
- step_level [thread] = 0
- debug.sethook(thread_hook,mask,count)
- return f(...)
- end)
- return thread
- else
- return cocreate(f)
- end
-end
-
---}}}
---{{{ coroutine.wrap
-
---This function overrides the built-in for the purposes of propagating
---the debug hook settings from the creator into the created coroutine.
-
-_G.coroutine.wrap = function(f)
- local thread
- local hook, mask, count = debug.gethook()
- if hook then
- local function thread_hook(event,line)
- hook(event,line,3,thread)
- end
- thread = cowrap(function(...)
- stack_level[thread] = 0
- trace_level[thread] = 0
- step_level [thread] = 0
- debug.sethook(thread_hook,mask,count)
- return f(...)
- end)
- return thread
- else
- return cowrap(f)
- end
-end
-
---}}}
-
---{{{ function pause()
-
---
--- Starts/resumes a debug session
---
-
-function pause(x)
- if pause_off then return end --being told to ignore pauses
- pausemsg = x or 'pause'
- local lines
- local src = getinfo(2,'short_src')
- if src == "stdin" then
- lines = 1 --if in a console session, stop now
- else
- lines = 2 --if in a script, stop when get out of pause()
- end
- if started then
- --we'll stop now 'cos the existing debug hook will grab us
- step_lines = lines
- step_into = true
- else
- coro_debugger = cocreate(debugger_loop) --NB: Use original coroutune.create
- --set to stop when get out of pause()
- trace_level[current_thread] = 0
- step_level [current_thread] = 0
- stack_level[current_thread] = 1
- step_lines = lines
- step_into = true
- started = true
- debug.sethook(debug_hook, "crl") --NB: this will cause an immediate entry to the debugger_loop
- end
-end
-
---}}}
---{{{ function dump()
-
---shows the value of the given variable, only really useful
---when the variable is a table
---see dump debug command hints for full semantics
-
-function dump(v,depth)
- dumpvar(v,(depth or 1)+1,tostring(v))
-end
-
---}}}
---{{{ function debug.traceback(x)
-
-local _traceback = debug.traceback --note original function
-
---override standard function
-debug.traceback = function(x)
- local assertmsg = _traceback(x) --do original function
- pause(x) --let user have a look at stuff
- return assertmsg --carry on
-end
-
-_TRACEBACK = debug.traceback --Lua 5.0 function
-
---}}}
-
+
+--{{{ history
+
+--28/03/10 ORX Adjusted show() to work with the Orxonox resource system
+--15/03/06 DCN Created based on RemDebug
+--28/04/06 DCN Update for Lua 5.1
+--01/06/06 DCN Fix command argument parsing
+-- Add step/over N facility
+-- Add trace lines facility
+--05/06/06 DCN Add trace call/return facility
+--06/06/06 DCN Make it behave when stepping through the creation of a coroutine
+--06/06/06 DCN Integrate the simple debugger into the main one
+--07/06/06 DCN Provide facility to step into coroutines
+--13/06/06 DCN Fix bug that caused the function environment to get corrupted with the global one
+--14/06/06 DCN Allow 'sloppy' file names when setting breakpoints
+--04/08/06 DCN Allow for no space after command name
+--11/08/06 DCN Use io.write not print
+--30/08/06 DCN Allow access to array elements in 'dump'
+--10/10/06 DCN Default to breakfile for all commands that require a filename and give '-'
+--06/12/06 DCN Allow for punctuation characters in DUMP variable names
+--03/01/07 DCN Add pause on/off facility
+--19/06/07 DCN Allow for duff commands being typed in the debugger (thanks to Michael.Bringmann at lsi.com)
+-- Allow for case sensitive file systems (thanks to Michael.Bringmann at lsi.com)
+
+--}}}
+--{{{ description
+
+--A simple command line debug system for Lua written by Dave Nichols of
+--Match-IT Limited. Its public domain software. Do with it as you wish.
+
+--This debugger was inspired by:
+-- RemDebug 1.0 Beta
+-- Copyright Kepler Project 2005 (http://www.keplerproject.org/remdebug)
+
+--Usage:
+-- require('debugger') --load the debug library
+-- pause(message) --start/resume a debug session
+
+--An assert() failure will also invoke the debugger.
+
+--}}}
+
+local IsWindows = string.find(string.lower(os.getenv('OS') or ''),'^windows')
+
+local coro_debugger
+local events = { BREAK = 1, WATCH = 2, STEP = 3, SET = 4 }
+local breakpoints = {}
+local watches = {}
+local step_into = false
+local step_over = false
+local step_lines = 0
+local step_level = {main=0}
+local stack_level = {main=0}
+local trace_level = {main=0}
+local trace_calls = false
+local trace_returns = false
+local trace_lines = false
+local ret_file, ret_line, ret_name
+local current_thread = 'main'
+local started = false
+local pause_off = false
+local _g = _G
+local cocreate, cowrap = coroutine.create, coroutine.wrap
+local pausemsg = 'pause'
+
+--{{{ local hints -- command help
+--The format in here is name=summary|description
+local hints = {
+
+pause = [[
+pause(msg) -- start/resume a debugger session|
+
+This can only be used in your code or from the console as a means to
+start/resume a debug session.
+If msg is given that is shown when the session starts/resumes. Useful to
+give a context if you've instrumented your code with pause() statements.
+]],
+
+poff = [[
+poff -- turn off pause() command|
+
+This causes all pause() commands to be ignored. This is useful if you have
+instrumented your code in a busy loop and want to continue normal execution
+with no further interruption.
+]],
+
+pon = [[
+pon -- turn on pause() command|
+
+This re-instates honouring the pause() commands you may have instrumented
+your code with.
+]],
+
+setb = [[
+setb [line file] -- set a breakpoint to line/file|
+
+If file is omitted or is "-" the breakpoint is set at the file for the
+currently set level (see "set"). Execution pauses when this line is about
+to be executed and the debugger session is re-activated.
+
+The file can be given as the fully qualified name, partially qualified or
+just the file name. E.g. if file is set as "myfile.lua", then whenever
+execution reaches any file that ends with "myfile.lua" it will pause.
+]],
+
+delb = [[
+delb [line file] -- removes a breakpoint|
+
+If file is omitted or is "-" the breakpoint is removed for the file of the
+currently set level (see "set").
+]],
+
+delallb = [[
+delallb -- removes all breakpoints|
+]],
+
+setw = [[
+setw <exp> -- adds a new watch expression|
+
+The expression is evaluated before each line is executed. If the expression
+yields true then execution is paused and the debugger session re-activated.
+The expression is executed in the context of the line about to be executed.
+]],
+
+delw = [[
+delw <index> -- removes the watch expression at index|
+
+The index is that returned when the watch expression was set by setw.
+]],
+
+delallw = [[
+delallw -- removes all watch expressions|
+]],
+
+run = [[
+run -- run until next breakpoint or watch expression|
+]],
+
+step = [[
+step [N] -- run next N lines, stepping into function calls|
+
+If N is omitted, use 1.
+]],
+
+over = [[
+over [N] -- run next N lines, stepping over function calls|
+
+If N is omitted, use 1.
+]],
+
+out = [[
+out [N] -- run lines until stepped out of N functions|
+
+If N is omitted, use 1.
+If you are inside a function, using "out 1" will run until you return
+from that function to the caller.
+]],
+
+goto = [[
+goto <line> -- step to line number <line> in the current file|
+
+The line and current file are those in the currently set context level.
+]],
+
+listb = [[
+listb -- lists breakpoints|
+]],
+
+listw = [[
+listw -- lists watch expressions|
+]],
+
+set = [[
+set [level] -- set context to stack level, omitted=show|
+
+If level is omitted it just prints the current level set.
+This sets the current context to the level given. This affects the
+context used for several other functions (e.g. vars). The possible
+levels are those shown by trace.
+]],
+
+vars = [[
+vars [depth] -- list context locals to depth, omitted=1|
+
+If depth is omitted then uses 1.
+Use a depth of 0 for the maximum.
+Lists all non-nil local variables and all non-nil upvalues in the
+currently set context. For variables that are tables, lists all fields
+to the given depth.
+]],
+
+fenv = [[
+fenv [depth] -- list context function env to depth, omitted=1|
+
+If depth is omitted then uses 1.
+Use a depth of 0 for the maximum.
+Lists all function environment variables in the currently set context.
+For variables that are tables, lists all fields to the given depth.
+]],
+
+glob = [[
+glob [depth] -- list globals to depth, omitted=1|
+
+If depth is omitted then uses 1.
+Use a depth of 0 for the maximum.
+Lists all global variables.
+For variables that are tables, lists all fields to the given depth.
+]],
+
+ups = [[
+ups -- list all the upvalue names|
+
+These names will also be in the "vars" list unless their value is nil.
+This provides a means to identify which vars are upvalues and which are
+locals. If a name is both an upvalue and a local, the local value takes
+precedance.
+]],
+
+locs = [[
+locs -- list all the locals names|
+
+These names will also be in the "vars" list unless their value is nil.
+This provides a means to identify which vars are upvalues and which are
+locals. If a name is both an upvalue and a local, the local value takes
+precedance.
+]],
+
+dump = [[
+dump <var> [depth] -- dump all fields of variable to depth|
+
+If depth is omitted then uses 1.
+Use a depth of 0 for the maximum.
+Prints the value of <var> in the currently set context level. If <var>
+is a table, lists all fields to the given depth. <var> can be just a
+name, or name.field or name.# to any depth, e.g. t.1.f accesses field
+'f' in array element 1 in table 't'.
+
+Can also be called from a script as dump(var,depth).
+]],
+
+tron = [[
+tron [crl] -- turn trace on for (c)alls, (r)etuns, (l)lines|
+
+If no parameter is given then tracing is turned off.
+When tracing is turned on a line is printed to the console for each
+debug 'event' selected. c=function calls, r=function returns, l=lines.
+]],
+
+trace = [[
+trace -- dumps a stack trace|
+
+Format is [level] = file,line,name
+The level is a candidate for use by the 'set' command.
+]],
+
+info = [[
+info -- dumps the complete debug info captured|
+
+Only useful as a diagnostic aid for the debugger itself. This information
+can be HUGE as it dumps all variables to the maximum depth, so be careful.
+]],
+
+show = [[
+show line file X Y -- show X lines before and Y after line in file|
+
+If line is omitted or is '-' then the current set context line is used.
+If file is omitted or is '-' then the current set context file is used.
+If file is not fully qualified and cannot be opened as specified, then
+a search for the file in the package[path] is performed using the usual
+"require" searching rules. If no file extension is given, .lua is used.
+Prints the lines from the source file around the given line.
+]],
+
+exit = [[
+exit -- exits debugger, re-start it using pause()|
+]],
+
+help = [[
+help [command] -- show this list or help for command|
+]],
+
+["<statement>"] = [[
+<statement> -- execute a statement in the current context|
+
+The statement can be anything that is legal in the context, including
+assignments. Such assignments affect the context and will be in force
+immediately. Any results returned are printed. Use '=' as a short-hand
+for 'return', e.g. "=func(arg)" will call 'func' with 'arg' and print
+the results, and "=var" will just print the value of 'var'.
+]],
+
+what = [[
+what <func> -- show where <func> is defined (if known)|
+]],
+
+}
+--}}}
+
+--{{{ local function getinfo(level,field)
+
+--like debug.getinfo but copes with no activation record at the given level
+--and knows how to get 'field'. 'field' can be the name of any of the
+--activation record fields or any of the 'what' names or nil for everything.
+--only valid when using the stack level to get info, not a function name.
+
+local function getinfo(level,field)
+ level = level + 1 --to get to the same relative level as the caller
+ if not field then return debug.getinfo(level) end
+ local what
+ if field == 'name' or field == 'namewhat' then
+ what = 'n'
+ elseif field == 'what' or field == 'source' or field == 'linedefined' or field == 'lastlinedefined' or field == 'short_src' then
+ what = 'S'
+ elseif field == 'currentline' then
+ what = 'l'
+ elseif field == 'nups' then
+ what = 'u'
+ elseif field == 'func' then
+ what = 'f'
+ else
+ return debug.getinfo(level,field)
+ end
+ local ar = debug.getinfo(level,what)
+ if ar then return ar[field] else return nil end
+end
+
+--}}}
+--{{{ local function indented( level, ... )
+
+local function indented( level, ... )
+ io.write( string.rep(' ',level), table.concat({...}), '\n' )
+end
+
+--}}}
+--{{{ local function dumpval( level, name, value, limit )
+
+local dumpvisited
+
+local function dumpval( level, name, value, limit )
+ local index
+ if type(name) == 'number' then
+ index = string.format('[%d] = ',name)
+ elseif type(name) == 'string'
+ and (name == '__VARSLEVEL__' or name == '__ENVIRONMENT__' or name == '__GLOBALS__' or name == '__UPVALUES__' or name == '__LOCALS__') then
+ --ignore these, they are debugger generated
+ return
+ elseif type(name) == 'string' and string.find(name,'^[_%a][_.%w]*$') then
+ index = name ..' = '
+ else
+ index = string.format('[%q] = ',tostring(name))
+ end
+ if type(value) == 'table' then
+ if dumpvisited[value] then
+ indented( level, index, string.format('ref%q;',dumpvisited[value]) )
+ else
+ dumpvisited[value] = tostring(value)
+ if (limit or 0) > 0 and level+1 >= limit then
+ indented( level, index, dumpvisited[value] )
+ else
+ indented( level, index, '{ -- ', dumpvisited[value] )
+ for n,v in pairs(value) do
+ dumpval( level+1, n, v, limit )
+ end
+ indented( level, '};' )
+ end
+ end
+ else
+ if type(value) == 'string' then
+ if string.len(value) > 40 then
+ indented( level, index, '[[', value, ']];' )
+ else
+ indented( level, index, string.format('%q',value), ';' )
+ end
+ else
+ indented( level, index, tostring(value), ';' )
+ end
+ end
+end
+
+--}}}
+--{{{ local function dumpvar( value, limit, name )
+
+local function dumpvar( value, limit, name )
+ dumpvisited = {}
+ dumpval( 0, name or tostring(value), value, limit )
+end
+
+--}}}
+--{{{ local function show(file,line,before,after)
+
+--show +/-N lines of a file around line M
+
+local function show(file,line,before,after)
+
+ line = tonumber(line or 1)
+ before = tonumber(before or 10)
+ after = tonumber(after or before)
+
+ -- Try to find the file in the Orxonox resources
+ local text = luaState:getSourceCode(file)
+
+ if text == "" then
+ if not string.find(file,'%.') then file = file..'.lua' end
+
+ local f = io.open(file,'r')
+ if not f then
+ --{{{ try to find the file in the path
+
+ --
+ -- looks for a file in the package path
+ --
+ local path = package.path or LUA_PATH or ''
+ for c in string.gmatch (path, "[^;]+") do
+ local c = string.gsub (c, "%?%.lua", file)
+ f = io.open (c,'r')
+ if f then
+ break
+ end
+ end
+
+ --}}}
+
+ if f then
+ -- Read file into 'text'
+ text = f:read("*a")
+ f:close()
+ else
+ io.write('Cannot find '..file..'\n')
+ return
+ end
+ end
+ end
+
+ -- Transform line endings to \n
+ text:gsub("\r\n", "\n") -- Windows to Unix
+ text:gsub("\r", "\n") -- Mac to Unix
+ if text[-1] ~= "\n" then
+ text = text.."\n"
+ end
+ -- Print requested lines
+ local i = 0
+ for l in text:gmatch("[^\n]*[\n]") do
+ i = i + 1
+ if i >= (line-before) then
+ if i > (line+after) then break end
+ if i == line then
+ io.write(i..'***\t'..l)
+ else
+ io.write(i..'\t'..l)
+ end
+ end
+ end
+end
+
+--}}}
+--{{{ local function tracestack(l)
+
+local function gi( i )
+ return function() i=i+1 return debug.getinfo(i),i end
+end
+
+local function gl( level, j )
+ return function() j=j+1 return debug.getlocal( level, j ) end
+end
+
+local function gu( func, k )
+ return function() k=k+1 return debug.getupvalue( func, k ) end
+end
+
+local traceinfo
+
+local function tracestack(l)
+ local l = l + 1 --NB: +1 to get level relative to caller
+ traceinfo = {}
+ traceinfo.pausemsg = pausemsg
+ for ar,i in gi(l) do
+ table.insert( traceinfo, ar )
+ local names = {}
+ local values = {}
+ for n,v in gl(i,0) do
+ if string.sub(n,1,1) ~= '(' then --ignore internal control variables
+ table.insert( names, n )
+ table.insert( values, v )
+ end
+ end
+ if #names > 0 then
+ ar.lnames = names
+ ar.lvalues = values
+ end
+ if ar.func then
+ local names = {}
+ local values = {}
+ for n,v in gu(ar.func,0) do
+ if string.sub(n,1,1) ~= '(' then --ignore internal control variables
+ table.insert( names, n )
+ table.insert( values, v )
+ end
+ end
+ if #names > 0 then
+ ar.unames = names
+ ar.uvalues = values
+ end
+ end
+ end
+end
+
+--}}}
+--{{{ local function trace()
+
+local function trace(set)
+ local mark
+ for level,ar in ipairs(traceinfo) do
+ if level == set then
+ mark = '***'
+ else
+ mark = ''
+ end
+ io.write('['..level..']'..mark..'\t'..(ar.name or ar.what)..' in '..ar.short_src..':'..ar.currentline..'\n')
+ end
+end
+
+--}}}
+--{{{ local function info()
+
+local function info() dumpvar( traceinfo, 0, 'traceinfo' ) end
+
+--}}}
+
+--{{{ local function set_breakpoint(file, line)
+
+local function set_breakpoint(file, line)
+ if not breakpoints[line] then
+ breakpoints[line] = {}
+ end
+ breakpoints[line][file] = true
+end
+
+--}}}
+--{{{ local function remove_breakpoint(file, line)
+
+local function remove_breakpoint(file, line)
+ if breakpoints[line] then
+ breakpoints[line][file] = nil
+ end
+end
+
+--}}}
+--{{{ local function has_breakpoint(file, line)
+
+--allow for 'sloppy' file names
+--search for file and all variations walking up its directory hierachy
+--ditto for the file with no extension
+
+local function has_breakpoint(file, line)
+ if not breakpoints[line] then return false end
+ local noext = string.gsub(file,"(%..-)$",'',1)
+ if noext == file then noext = nil end
+ while file do
+ if breakpoints[line][file] then return true end
+ file = string.match(file,"[:/\](.+)$")
+ end
+ while noext do
+ if breakpoints[line][noext] then return true end
+ noext = string.match(noext,"[:/\](.+)$")
+ end
+ return false
+end
+
+--}}}
+--{{{ local function capture_vars(ref,level,line)
+
+local function capture_vars(ref,level,line)
+ --get vars, file and line for the given level relative to debug_hook offset by ref
+
+ local lvl = ref + level --NB: This includes an offset of +1 for the call to here
+
+ --{{{ capture variables
+
+ local ar = debug.getinfo(lvl, "f")
+ if not ar then return {},'?',0 end
+
+ local vars = {__UPVALUES__={}, __LOCALS__={}}
+ local i
+
+ local func = ar.func
+ if func then
+ i = 1
+ while true do
+ local name, value = debug.getupvalue(func, i)
+ if not name then break end
+ if string.sub(name,1,1) ~= '(' then --NB: ignoring internal control variables
+ vars[name] = value
+ vars.__UPVALUES__[i] = name
+ end
+ i = i + 1
+ end
+ vars.__ENVIRONMENT__ = getfenv(func)
+ end
+
+ vars.__GLOBALS__ = getfenv(0)
+
+ i = 1
+ while true do
+ local name, value = debug.getlocal(lvl, i)
+ if not name then break end
+ if string.sub(name,1,1) ~= '(' then --NB: ignoring internal control variables
+ vars[name] = value
+ vars.__LOCALS__[i] = name
+ end
+ i = i + 1
+ end
+
+ vars.__VARSLEVEL__ = level
+
+ if func then
+ --NB: Do not do this until finished filling the vars table
+ setmetatable(vars, { __index = getfenv(func), __newindex = getfenv(func) })
+ end
+
+ --NB: Do not read or write the vars table anymore else the metatable functions will get invoked!
+
+ --}}}
+
+ local file = getinfo(lvl, "source")
+ if string.find(file, "@") == 1 then
+ file = string.sub(file, 2)
+ end
+ -- Orxonox changes: Our resource system is case sensisive, even on Windows
+ --if IsWindows then file = string.lower(file) end
+
+ if not line then
+ line = getinfo(lvl, "currentline")
+ end
+
+ return vars,file,line
+
+end
+
+--}}}
+--{{{ local function restore_vars(ref,vars)
+
+local function restore_vars(ref,vars)
+
+ if type(vars) ~= 'table' then return end
+
+ local level = vars.__VARSLEVEL__ --NB: This level is relative to debug_hook offset by ref
+ if not level then return end
+
+ level = level + ref --NB: This includes an offset of +1 for the call to here
+
+ local i
+ local written_vars = {}
+
+ i = 1
+ while true do
+ local name, value = debug.getlocal(level, i)
+ if not name then break end
+ if vars[name] and string.sub(name,1,1) ~= '(' then --NB: ignoring internal control variables
+ debug.setlocal(level, i, vars[name])
+ written_vars[name] = true
+ end
+ i = i + 1
+ end
+
+ local ar = debug.getinfo(level, "f")
+ if not ar then return end
+
+ local func = ar.func
+ if func then
+
+ i = 1
+ while true do
+ local name, value = debug.getupvalue(func, i)
+ if not name then break end
+ if vars[name] and string.sub(name,1,1) ~= '(' then --NB: ignoring internal control variables
+ if not written_vars[name] then
+ debug.setupvalue(func, i, vars[name])
+ end
+ written_vars[name] = true
+ end
+ i = i + 1
+ end
+
+ end
+
+end
+
+--}}}
+--{{{ local function trace_event(event, line, level)
+
+local function print_trace(level,depth,event,file,line,name)
+
+ --NB: level here is relative to the caller of trace_event, so offset by 2 to get to there
+ level = level + 2
+
+ local file = file or getinfo(level,'short_src')
+ local line = line or getinfo(level,'currentline')
+ local name = name or getinfo(level,'name')
+
+ local prefix = ''
+ if current_thread ~= 'main' then prefix = '['..tostring(current_thread)..'] ' end
+
+ io.write(prefix..
+ string.format('%08.2f:%02i.',os.clock(),depth)..
+ string.rep('.',depth%32)..
+ (file or '')..' ('..(line or '')..') '..
+ (name or '')..
+ ' ('..event..')\n')
+
+end
+
+local function trace_event(event, line, level)
+
+ if event == 'return' and trace_returns then
+ --note the line info for later
+ ret_file = getinfo(level+1,'short_src')
+ ret_line = getinfo(level+1,'currentline')
+ ret_name = getinfo(level+1,'name')
+ end
+
+ if event ~= 'line' then return end
+
+ local slevel = stack_level[current_thread]
+ local tlevel = trace_level[current_thread]
+
+ if trace_calls and slevel > tlevel then
+ --we are now in the function called, so look back 1 level further to find the calling file and line
+ print_trace(level+1,slevel-1,'c',nil,nil,getinfo(level+1,'name'))
+ end
+
+ if trace_returns and slevel < tlevel then
+ print_trace(level,slevel,'r',ret_file,ret_line,ret_name)
+ end
+
+ if trace_lines then
+ print_trace(level,slevel,'l')
+ end
+
+ trace_level[current_thread] = stack_level[current_thread]
+
+end
+
+--}}}
+--{{{ local function debug_hook(event, line, level, thread)
+
+local function debug_hook(event, line, level, thread)
+ if not started then debug.sethook() return end
+ current_thread = thread or 'main'
+ local level = level or 2
+ trace_event(event,line,level)
+ if event == "call" then
+ stack_level[current_thread] = stack_level[current_thread] + 1
+ elseif event == "return" then
+ stack_level[current_thread] = stack_level[current_thread] - 1
+ if stack_level[current_thread] < 0 then stack_level[current_thread] = 0 end
+ else
+ local vars,file,line = capture_vars(level,1,line)
+ local stop, ev, idx = false, events.STEP, 0
+ while true do
+ for index, value in pairs(watches) do
+ setfenv(value.func, vars)
+ local status, res = pcall(value.func)
+ if status and res then
+ ev, idx = events.WATCH, index
+ stop = true
+ break
+ end
+ end
+ if stop then break end
+ if (step_into)
+ or (step_over and (stack_level[current_thread] <= step_level[current_thread] or stack_level[current_thread] == 0)) then
+ step_lines = step_lines - 1
+ if step_lines < 1 then
+ ev, idx = events.STEP, 0
+ break
+ end
+ end
+ if has_breakpoint(file, line) then
+ ev, idx = events.BREAK, 0
+ break
+ end
+ return
+ end
+ tracestack(level)
+ local last_next = 1
+ local err, next = assert(coroutine.resume(coro_debugger, ev, vars, file, line, idx))
+ while true do
+ if next == 'cont' then
+ return
+ elseif next == 'stop' then
+ started = false
+ debug.sethook()
+ return
+ elseif tonumber(next) then --get vars for given level or last level
+ next = tonumber(next)
+ if next == 0 then next = last_next end
+ last_next = next
+ restore_vars(level,vars)
+ vars, file, line = capture_vars(level,next)
+ err, next = assert(coroutine.resume(coro_debugger, events.SET, vars, file, line, idx))
+ else
+ io.write('Unknown command from debugger_loop: '..tostring(next)..'\n')
+ io.write('Stopping debugger\n')
+ next = 'stop'
+ end
+ end
+ end
+end
+
+--}}}
+--{{{ local function report(ev, vars, file, line, idx_watch)
+
+local function report(ev, vars, file, line, idx_watch)
+ local vars = vars or {}
+ local file = file or '?'
+ local line = line or 0
+ local prefix = ''
+ if current_thread ~= 'main' then prefix = '['..tostring(current_thread)..'] ' end
+ if ev == events.STEP then
+ io.write(prefix.."Paused at file "..file.." line "..line..' ('..stack_level[current_thread]..')\n')
+ elseif ev == events.BREAK then
+ io.write(prefix.."Paused at file "..file.." line "..line..' ('..stack_level[current_thread]..') (breakpoint)\n')
+ elseif ev == events.WATCH then
+ io.write(prefix.."Paused at file "..file.." line "..line..' ('..stack_level[current_thread]..')'.." (watch expression "..idx_watch.. ": ["..watches[idx_watch].exp.."])\n")
+ elseif ev == events.SET then
+ --do nothing
+ else
+ io.write(prefix.."Error in application: "..file.." line "..line.."\n")
+ end
+ if ev ~= events.SET then
+ if pausemsg and pausemsg ~= '' then io.write('Message: '..pausemsg..'\n') end
+ pausemsg = ''
+ end
+ return vars, file, line
+end
+
+--}}}
+
+--{{{ local function debugger_loop(server)
+
+local function debugger_loop(ev, vars, file, line, idx_watch)
+
+ io.write("Lua Debugger\n")
+ local eval_env, breakfile, breakline = report(ev, vars, file, line, idx_watch)
+ io.write("Type 'help' for commands\n")
+
+ local command, args
+
+ --{{{ local function getargs(spec)
+
+ --get command arguments according to the given spec from the args string
+ --the spec has a single character for each argument, arguments are separated
+ --by white space, the spec characters can be one of:
+ -- F for a filename (defaults to breakfile if - given in args)
+ -- L for a line number (defaults to breakline if - given in args)
+ -- N for a number
+ -- V for a variable name
+ -- S for a string
+
+ local function getargs(spec)
+ local res={}
+ local char,arg
+ local ptr=1
+ for i=1,string.len(spec) do
+ char = string.sub(spec,i,i)
+ if char == 'F' then
+ _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
+ if not arg or arg == '' then arg = '-' end
+ if arg == '-' then arg = breakfile end
+ elseif char == 'L' then
+ _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
+ if not arg or arg == '' then arg = '-' end
+ if arg == '-' then arg = breakline end
+ arg = tonumber(arg) or 0
+ elseif char == 'N' then
+ _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
+ if not arg or arg == '' then arg = '0' end
+ arg = tonumber(arg) or 0
+ elseif char == 'V' then
+ _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
+ if not arg or arg == '' then arg = '' end
+ elseif char == 'S' then
+ _,ptr,arg = string.find(args..' ',"%s*([%w%p]*)%s*",ptr)
+ if not arg or arg == '' then arg = '' end
+ else
+ arg = ''
+ end
+ table.insert(res,arg or '')
+ end
+ return unpack(res)
+ end
+
+ --}}}
+
+ while true do
+ io.write("[DEBUG]> ")
+ local line = io.read("*line")
+ if line == nil then io.write('\n'); line = 'exit' end
+
+ if string.find(line, "^[a-z]+") then
+ command = string.sub(line, string.find(line, "^[a-z]+"))
+ args = string.gsub(line,"^[a-z]+%s*",'',1) --strip command off line
+ else
+ command = ''
+ end
+
+ if command == "setb" then
+ --{{{ set breakpoint
+
+ local line, filename = getargs('LF')
+ if filename ~= '' and line ~= '' then
+ set_breakpoint(filename,line)
+ io.write("Breakpoint set in file "..filename..' line '..line..'\n')
+ else
+ io.write("Bad request\n")
+ end
+
+ --}}}
+
+ elseif command == "delb" then
+ --{{{ delete breakpoint
+
+ local line, filename = getargs('LF')
+ if filename ~= '' and line ~= '' then
+ remove_breakpoint(filename, line)
+ io.write("Breakpoint deleted from file "..filename..' line '..line.."\n")
+ else
+ io.write("Bad request\n")
+ end
+
+ --}}}
+
+ elseif command == "delallb" then
+ --{{{ delete all breakpoints
+ breakpoints = {}
+ io.write('All breakpoints deleted\n')
+ --}}}
+
+ elseif command == "listb" then
+ --{{{ list breakpoints
+ for i, v in pairs(breakpoints) do
+ for ii, vv in pairs(v) do
+ io.write("Break at: "..i..' in '..ii..'\n')
+ end
+ end
+ --}}}
+
+ elseif command == "setw" then
+ --{{{ set watch expression
+
+ if args and args ~= '' then
+ local func = loadstring("return(" .. args .. ")")
+ local newidx = #watches + 1
+ watches[newidx] = {func = func, exp = args}
+ io.write("Set watch exp no. " .. newidx..'\n')
+ else
+ io.write("Bad request\n")
+ end
+
+ --}}}
+
+ elseif command == "delw" then
+ --{{{ delete watch expression
+
+ local index = tonumber(args)
+ if index then
+ watches[index] = nil
+ io.write("Watch expression deleted\n")
+ else
+ io.write("Bad request\n")
+ end
+
+ --}}}
+
+ elseif command == "delallw" then
+ --{{{ delete all watch expressions
+ watches = {}
+ io.write('All watch expressions deleted\n')
+ --}}}
+
+ elseif command == "listw" then
+ --{{{ list watch expressions
+ for i, v in pairs(watches) do
+ io.write("Watch exp. " .. i .. ": " .. v.exp..'\n')
+ end
+ --}}}
+
+ elseif command == "run" then
+ --{{{ run until breakpoint
+ step_into = false
+ step_over = false
+ eval_env, breakfile, breakline = report(coroutine.yield('cont'))
+ --}}}
+
+ elseif command == "step" then
+ --{{{ step N lines (into functions)
+ local N = tonumber(args) or 1
+ step_over = false
+ step_into = true
+ step_lines = tonumber(N or 1)
+ eval_env, breakfile, breakline = report(coroutine.yield('cont'))
+ --}}}
+
+ elseif command == "over" then
+ --{{{ step N lines (over functions)
+ local N = tonumber(args) or 1
+ step_into = false
+ step_over = true
+ step_lines = tonumber(N or 1)
+ step_level[current_thread] = stack_level[current_thread]
+ eval_env, breakfile, breakline = report(coroutine.yield('cont'))
+ --}}}
+
+ elseif command == "out" then
+ --{{{ step N lines (out of functions)
+ local N = tonumber(args) or 1
+ step_into = false
+ step_over = true
+ step_lines = 1
+ step_level[current_thread] = stack_level[current_thread] - tonumber(N or 1)
+ eval_env, breakfile, breakline = report(coroutine.yield('cont'))
+ --}}}
+
+ elseif command == "goto" then
+ --{{{ step until reach line
+ local N = tonumber(args)
+ if N then
+ step_over = false
+ step_into = false
+ if has_breakpoint(breakfile,N) then
+ eval_env, breakfile, breakline = report(coroutine.yield('cont'))
+ else
+ local bf = breakfile
+ set_breakpoint(breakfile,N)
+ eval_env, breakfile, breakline = report(coroutine.yield('cont'))
+ if breakfile == bf and breakline == N then remove_breakpoint(breakfile,N) end
+ end
+ else
+ io.write("Bad request\n")
+ end
+ --}}}
+
+ elseif command == "set" then
+ --{{{ set/show context level
+ local level = args
+ if level and level == '' then level = nil end
+ if level then
+ eval_env, breakfile, breakline = report(coroutine.yield(level))
+ end
+ if eval_env.__VARSLEVEL__ then
+ io.write('Level: '..eval_env.__VARSLEVEL__..'\n')
+ else
+ io.write('No level set\n')
+ end
+ --}}}
+
+ elseif command == "vars" then
+ --{{{ list context variables
+ local depth = args
+ if depth and depth == '' then depth = nil end
+ depth = tonumber(depth) or 1
+ dumpvar(eval_env, depth+1, 'variables')
+ --}}}
+
+ elseif command == "glob" then
+ --{{{ list global variables
+ local depth = args
+ if depth and depth == '' then depth = nil end
+ depth = tonumber(depth) or 1
+ dumpvar(eval_env.__GLOBALS__,depth+1,'globals')
+ --}}}
+
+ elseif command == "fenv" then
+ --{{{ list function environment variables
+ local depth = args
+ if depth and depth == '' then depth = nil end
+ depth = tonumber(depth) or 1
+ dumpvar(eval_env.__ENVIRONMENT__,depth+1,'environment')
+ --}}}
+
+ elseif command == "ups" then
+ --{{{ list upvalue names
+ dumpvar(eval_env.__UPVALUES__,2,'upvalues')
+ --}}}
+
+ elseif command == "locs" then
+ --{{{ list locals names
+ dumpvar(eval_env.__LOCALS__,2,'upvalues')
+ --}}}
+
+ elseif command == "what" then
+ --{{{ show where a function is defined
+ if args and args ~= '' then
+ local v = eval_env
+ local n = nil
+ for w in string.gmatch(args,"[%w_]+") do
+ v = v[w]
+ if n then n = n..'.'..w else n = w end
+ if not v then break end
+ end
+ if type(v) == 'function' then
+ local def = debug.getinfo(v,'S')
+ if def then
+ io.write(def.what..' in '..def.short_src..' '..def.linedefined..'..'..def.lastlinedefined..'\n')
+ else
+ io.write('Cannot get info for '..v..'\n')
+ end
+ else
+ io.write(v..' is not a function\n')
+ end
+ else
+ io.write("Bad request\n")
+ end
+ --}}}
+
+ elseif command == "dump" then
+ --{{{ dump a variable
+ local name, depth = getargs('VN')
+ if name ~= '' then
+ if depth == '' or depth == 0 then depth = nil end
+ depth = tonumber(depth or 1)
+ local v = eval_env
+ local n = nil
+ for w in string.gmatch(name,"[^%.]+") do --get everything between dots
+ if tonumber(w) then
+ v = v[tonumber(w)]
+ else
+ v = v[w]
+ end
+ if n then n = n..'.'..w else n = w end
+ if not v then break end
+ end
+ dumpvar(v,depth+1,n)
+ else
+ io.write("Bad request\n")
+ end
+ --}}}
+
+ elseif command == "show" then
+ --{{{ show file around a line or the current breakpoint
+
+ local line, file, before, after = getargs('LFNN')
+ if before == 0 then before = 10 end
+ if after == 0 then after = before end
+
+ if file ~= '' and file ~= "=stdin" then
+ show(file,line,before,after)
+ else
+ io.write('Nothing to show\n')
+ end
+
+ --}}}
+
+ elseif command == "poff" then
+ --{{{ turn pause command off
+ pause_off = true
+ --}}}
+
+ elseif command == "pon" then
+ --{{{ turn pause command on
+ pause_off = false
+ --}}}
+
+ elseif command == "tron" then
+ --{{{ turn tracing on/off
+ local option = getargs('S')
+ trace_calls = false
+ trace_returns = false
+ trace_lines = false
+ if string.find(option,'c') then trace_calls = true end
+ if string.find(option,'r') then trace_returns = true end
+ if string.find(option,'l') then trace_lines = true end
+ --}}}
+
+ elseif command == "trace" then
+ --{{{ dump a stack trace
+ trace(eval_env.__VARSLEVEL__)
+ --}}}
+
+ elseif command == "info" then
+ --{{{ dump all debug info captured
+ info()
+ --}}}
+
+ elseif command == "pause" then
+ --{{{ not allowed in here
+ io.write('pause() should only be used in the script you are debugging\n')
+ --}}}
+
+ elseif command == "help" then
+ --{{{ help
+ local command = getargs('S')
+ if command ~= '' and hints[command] then
+ io.write(hints[command]..'\n')
+ else
+ for _,v in pairs(hints) do
+ local _,_,h = string.find(v,"(.+)|")
+ io.write(h..'\n')
+ end
+ end
+ --}}}
+
+ elseif command == "exit" then
+ --{{{ exit debugger
+ return 'stop'
+ --}}}
+
+ elseif line ~= '' then
+ --{{{ just execute whatever it is in the current context
+
+ --map line starting with "=..." to "return ..."
+ if string.sub(line,1,1) == '=' then line = string.gsub(line,'=','return ',1) end
+
+ local ok, func = pcall(loadstring,line)
+ if func == nil then --Michael.Bringmann at lsi.com
+ io.write("Compile error: "..line..'\n')
+ elseif not ok then
+ io.write("Compile error: "..func..'\n')
+ else
+ setfenv(func, eval_env)
+ local res = {pcall(func)}
+ if res[1] then
+ if res[2] then
+ table.remove(res,1)
+ for _,v in ipairs(res) do
+ io.write(tostring(v))
+ io.write('\t')
+ end
+ io.write('\n')
+ end
+ --update in the context
+ eval_env, breakfile, breakline = report(coroutine.yield(0))
+ else
+ io.write("Run error: "..res[2]..'\n')
+ end
+ end
+
+ --}}}
+ end
+ end
+
+end
+
+--}}}
+
+--{{{ coroutine.create
+
+--This function overrides the built-in for the purposes of propagating
+--the debug hook settings from the creator into the created coroutine.
+
+_G.coroutine.create = function(f)
+ local thread
+ local hook, mask, count = debug.gethook()
+ if hook then
+ local function thread_hook(event,line)
+ hook(event,line,3,thread)
+ end
+ thread = cocreate(function(...)
+ stack_level[thread] = 0
+ trace_level[thread] = 0
+ step_level [thread] = 0
+ debug.sethook(thread_hook,mask,count)
+ return f(...)
+ end)
+ return thread
+ else
+ return cocreate(f)
+ end
+end
+
+--}}}
+--{{{ coroutine.wrap
+
+--This function overrides the built-in for the purposes of propagating
+--the debug hook settings from the creator into the created coroutine.
+
+_G.coroutine.wrap = function(f)
+ local thread
+ local hook, mask, count = debug.gethook()
+ if hook then
+ local function thread_hook(event,line)
+ hook(event,line,3,thread)
+ end
+ thread = cowrap(function(...)
+ stack_level[thread] = 0
+ trace_level[thread] = 0
+ step_level [thread] = 0
+ debug.sethook(thread_hook,mask,count)
+ return f(...)
+ end)
+ return thread
+ else
+ return cowrap(f)
+ end
+end
+
+--}}}
+
+--{{{ function pause()
+
+--
+-- Starts/resumes a debug session
+--
+
+function pause(x)
+ if pause_off then return end --being told to ignore pauses
+ pausemsg = x or 'pause'
+ local lines
+ local src = getinfo(2,'short_src')
+ if src == "stdin" then
+ lines = 1 --if in a console session, stop now
+ else
+ lines = 2 --if in a script, stop when get out of pause()
+ end
+ if started then
+ --we'll stop now 'cos the existing debug hook will grab us
+ step_lines = lines
+ step_into = true
+ else
+ coro_debugger = cocreate(debugger_loop) --NB: Use original coroutune.create
+ --set to stop when get out of pause()
+ trace_level[current_thread] = 0
+ step_level [current_thread] = 0
+ stack_level[current_thread] = 1
+ step_lines = lines
+ step_into = true
+ started = true
+ debug.sethook(debug_hook, "crl") --NB: this will cause an immediate entry to the debugger_loop
+ end
+end
+
+--}}}
+--{{{ function dump()
+
+--shows the value of the given variable, only really useful
+--when the variable is a table
+--see dump debug command hints for full semantics
+
+function dump(v,depth)
+ dumpvar(v,(depth or 1)+1,tostring(v))
+end
+
+--}}}
+--{{{ function debug.traceback(x)
+
+local _traceback = debug.traceback --note original function
+
+--override standard function
+debug.traceback = function(x)
+ local assertmsg = _traceback(x) --do original function
+ pause(x) --let user have a look at stuff
+ return assertmsg --carry on
+end
+
+_TRACEBACK = debug.traceback --Lua 5.0 function
+
+--}}}
+
Property changes on: code/branches/gamestates2/data/lua/Debugger.lua
___________________________________________________________________
Added: svn:eol-style
+ native
Modified: code/branches/gamestates2/data/lua/Strict.lua
===================================================================
--- code/branches/gamestates2/data/lua/Strict.lua 2010-03-30 23:05:28 UTC (rev 6662)
+++ code/branches/gamestates2/data/lua/Strict.lua 2010-03-30 23:06:55 UTC (rev 6663)
@@ -1,39 +1,39 @@
---
--- strict.lua
--- checks uses of undeclared global variables
--- All global variables must be 'declared' through a regular assignment
--- (even assigning nil will do) in a main chunk before being used
--- anywhere or assigned to inside a function.
---
-
-local mt = getmetatable(_G)
-if mt == nil then
- mt = {}
- setmetatable(_G, mt)
-end
-
-__STRICT = false
-mt.__declared = {}
-
-mt.__newindex = function (t, n, v)
- if __STRICT and not mt.__declared[n] then
- local d = debug.getinfo(2, "S")
- local w = d and d.what or "C"
- if w ~= "main" and w ~= "C" then
- error("assign to undeclared variable '"..n.."'", 2)
- end
- mt.__declared[n] = true
- end
- rawset(t, n, v)
-end
-
-mt.__index = function (t, n)
- if not mt.__declared[n] and debug.getinfo(2, "S").what ~= "C" then
- error("variable '"..n.."' is not declared", 2)
- end
- return rawget(t, n)
-end
-
-function global(...)
- for _, v in ipairs{...} do mt.__declared[v] = true end
-end
+--
+-- strict.lua
+-- checks uses of undeclared global variables
+-- All global variables must be 'declared' through a regular assignment
+-- (even assigning nil will do) in a main chunk before being used
+-- anywhere or assigned to inside a function.
+--
+
+local mt = getmetatable(_G)
+if mt == nil then
+ mt = {}
+ setmetatable(_G, mt)
+end
+
+__STRICT = false
+mt.__declared = {}
+
+mt.__newindex = function (t, n, v)
+ if __STRICT and not mt.__declared[n] then
+ local d = debug.getinfo(2, "S")
+ local w = d and d.what or "C"
+ if w ~= "main" and w ~= "C" then
+ error("assign to undeclared variable '"..n.."'", 2)
+ end
+ mt.__declared[n] = true
+ end
+ rawset(t, n, v)
+end
+
+mt.__index = function (t, n)
+ if not mt.__declared[n] and debug.getinfo(2, "S").what ~= "C" then
+ error("variable '"..n.."' is not declared", 2)
+ end
+ return rawget(t, n)
+end
+
+function global(...)
+ for _, v in ipairs{...} do mt.__declared[v] = true end
+end
Property changes on: code/branches/gamestates2/data/lua/Strict.lua
___________________________________________________________________
Added: svn:eol-style
+ native
More information about the Orxonox-commit
mailing list