iup-stack/iup/srclua5/scripter/debugger.lua

1764 lines
50 KiB
Lua
Raw Normal View History

2023-02-20 16:44:45 +00:00
local DEBUG_INACTIVE = 1 -- debug is inactive, hooks are not set
local DEBUG_ACTIVE = 2 -- debug should be active and running, until we found a breakpoint or the program ends
local DEBUG_STOPPED = 3 -- debug should be active and running, we are waiting to stop in the next opportunity, we have to abort
local DEBUG_STEP_INTO = 4 -- debug should be active and running, we are waiting until its steps into the function to pause, or we found a breakpoint
local DEBUG_STEP_OVER = 5 -- debug should be active and running, we are waiting until its steps over the function to pause, or we found a breakpoint
local DEBUG_STEP_OUT = 6 -- debug should be active and running, we are waiting until its steps out of the function to pause, or we found a breakpoint
local DEBUG_PAUSED = 7 -- debug should be active, but paused
local DEBUG_ERRORPAUSED = 8 -- debug should be active, but paused in an error
local FUNC_STATE_INSIDE = 1
local FUNC_STATE_OUTSIDE = 2
local debugger = {
debugState = DEBUG_INACTIVE,
currentFuncLevel = 0,
stepFuncLevel = 0,
stepFuncState = 0,
startFile = nil,
currentLine = nil,
currentFile = nil,
breakpoints = {},
main_dialog = nil,
panel_tabs = nil,
multitext_tabs = nil,
console_pos = 0,
debug_pos = 0,
}
function iup.DebuggerInit(panelTabs, multitextTabs)
debugger.main_dialog = iup.GetDialog(panelTabs)
debugger.multitext_tabs = multitextTabs
debugger.panel_tabs = panelTabs
debugger.console_pos = panelTabs.count - 4
debugger.debug_pos = panelTabs.count - 2
debugger.panel_tabs.valuepos = debugger.console_pos -- show console tab
end
function iup.DebuggerShowConsoleTab()
debugger.panel_tabs.valuepos = debugger.console_pos -- show console tab
end
function iup.DebuggerShowDebugTab()
debugger.panel_tabs.valuepos = debugger.debug_pos -- show debug tab
end
function iup.DebuggerGetCurrentMultitext()
return debugger.multitext_tabs.value_handle
end
----------- IupScintillaDlg Usage ---------------------
function iup.DebuggerNewFile()
debugger.main_dialog.newfile = nil
end
function iup.DebuggerOpenFile(filename)
debugger.main_dialog.openfile = filename
end
function iup.DebuggerForceCloseFile(pos)
debugger.main_dialog.forceclosefile = pos
end
function iup.DebuggerUpdateTitle()
debugger.main_dialog.updatetitle = "Yes"
end
function iup.DebuggerSaveFile()
debugger.main_dialog.savefile = nil
end
function iup.DebuggerToggleMarker(lin)
debugger.main_dialog["togglemarker"..lin] = 2 -- margin=2
end
-----------------------------------------__________----
function iup.DebuggerFindMultitext(filename)
local multitext = iup.GetChild(debugger.multitext_tabs, 0)
while multitext do
if multitext.filename == filename then
return multitext
end
multitext = iup.GetBrother(multitext)
end
end
function iup.DebuggerRestoreBreakpoints(multitext, m_filename)
local list_break = iup.GetDialogChild(debugger.main_dialog, "DEBUG_LIST_BREAK")
local index = 1
local filename = list_break["FILENAME" .. index]
while filename do
if filename == m_filename then
local line = tonumber(list_break["LINE" .. index])
multitext["MARKERADD".. (line - 1)] = 1 -- (margin=1)
end
index = index + 1
filename = list_break["FILENAME" .. index]
end
end
function iup.DebuggerOpenMultitext(filename, source)
local old_count = tonumber(debugger.multitext_tabs.count)
local s = string.sub(filename, 1, 6)
if s == "string" then
iup.DebuggerNewFile()
else
iup.DebuggerOpenFile(filename)
end
local count = tonumber(debugger.multitext_tabs.count)
if count > old_count then
local multitext = iup.GetChild(debugger.multitext_tabs, old_count)
if s == "string" then
multitext.filename = filename
multitext.value = source
multitext.temporary = "Yes"
iup.DebuggerRestoreBreakpoints(multitext, filename)
end
if iup.DebuggerIsActive() then
multitext.readonly = "Yes"
end
return multitext
end
end
function iup.DebuggerCloseTemporary()
local multitext = iup.GetChild(debugger.multitext_tabs, 0)
local pos = 0
while multitext do
if multitext.temporary == "Yes" then
local temp = multitext
multitext = iup.GetBrother(multitext)
iup.DebuggerForceCloseFile(pos)
else
multitext = iup.GetBrother(multitext)
pos = pos + 1
end
end
end
function iup.DebuggerSetAttribAllMultitext(name, value)
local multitext = iup.GetChild(debugger.multitext_tabs, 0)
while multitext do
multitext[name] = value
multitext = iup.GetBrother(multitext)
end
end
function iup.DebuggerSetDialogChildAttrib(child_name, name, value)
iup.SetAttribute(iup.GetDialogChild(debugger.main_dialog, child_name), name, value)
end
local function setparent_param_cb(param_dialog, param_index)
if param_index == iup.GETPARAM_MAP then
param_dialog.parentdialog = debugger.main_dialog
end
return 1
end
------------------------------------- User Interface State -------------------------------------
function iup.DebuggerSetStateString(state)
local map_state = { -- only the ones used in C
DEBUG_ACTIVE = DEBUG_ACTIVE,
DEBUG_STOPPED = DEBUG_STOPPED,
DEBUG_STEP_INTO = DEBUG_STEP_INTO,
DEBUG_STEP_OVER = DEBUG_STEP_OVER,
DEBUG_STEP_OUT = DEBUG_STEP_OUT,
DEBUG_PAUSED = DEBUG_PAUSED,
}
iup.DebuggerSetState(map_state[state])
end
function iup.DebuggerIsActive()
if debugger.debugState ~= DEBUG_INACTIVE then
return true
else
return false
end
end
function iup.DebuggerSetState(st)
local stop, step, pause, run, dbg, curline
if debugger.debugState == st then
return
end
iup.DebuggerSetAttribAllMultitext("READONLY", "Yes")
if st == DEBUG_STOPPED then
stop = "NO"
step = "NO"
run = "NO"
pause = "NO"
dbg = "NO"
curline = "NO"
elseif st == DEBUG_ACTIVE or st == DEBUG_STEP_INTO or st == DEBUG_STEP_OVER or st == DEBUG_STEP_OUT then
stop = "YES"
step = "NO"
run = "NO"
pause = "YES"
dbg = "NO"
curline = "NO"
if st == DEBUG_STEP_OUT then
debugger.stepFuncLevel = debugger.currentFuncLevel
debugger.stepFuncState = FUNC_STATE_INSIDE
else
debugger.stepFuncLevel = 0
debugger.stepFuncState = FUNC_STATE_OUTSIDE
end
elseif st == DEBUG_PAUSED then
stop = "YES"
step = "YES"
run = "NO"
pause = "NO"
dbg = "YES"
curline = "Yes"
elseif st == DEBUG_ERRORPAUSED then
stop = "YES"
step = "NO"
run = "NO"
pause = "NO"
dbg = "NO"
curline = "Yes"
else -- st == DEBUG_INACTIVE
stop = "NO"
step = "NO"
run = "YES"
pause = "NO"
dbg = "YES"
curline = "NO"
iup.DebuggerSetAttribAllMultitext("READONLY", "No")
iup.DebuggerClearLocalVariablesTree()
iup.DebuggerClearStackList()
iup.DebuggerInitGlobalsTree()
end
debugger.debugState = st
iup.DebuggerSetAttribAllMultitext("MARKERDELETEALL", "2") -- clear all line highlight (margin=2)
iup.DebuggerSetAttribAllMultitext("MARKERDELETEALL", "3") -- clear all line arrow (margin=3)
iup.DebuggerSetDialogChildAttrib("DEBUG_ITM_RUN", "ACTIVE", run)
iup.DebuggerSetDialogChildAttrib("DEBUG_ITM_STOP", "ACTIVE", stop)
iup.DebuggerSetDialogChildAttrib("DEBUG_ITM_PAUSE", "ACTIVE", pause)
iup.DebuggerSetDialogChildAttrib("DEBUG_ITM_DEBUG", "ACTIVE", dbg)
iup.DebuggerSetDialogChildAttrib("DEBUG_ITM_STEPINTO", "ACTIVE", step)
iup.DebuggerSetDialogChildAttrib("DEBUG_ITM_STEPOVER", "ACTIVE", step)
iup.DebuggerSetDialogChildAttrib("DEBUG_ITM_STEPOUT", "ACTIVE", step)
iup.DebuggerSetDialogChildAttrib("DEBUG_ITM_CURRENTLINE", "ACTIVE", curline)
iup.DebuggerSetDialogChildAttrib("DEBUG_BTN_RUN", "ACTIVE", run)
iup.DebuggerSetDialogChildAttrib("DEBUG_BTN_STOP", "ACTIVE", stop)
iup.DebuggerSetDialogChildAttrib("DEBUG_BTN_PAUSE", "ACTIVE", pause)
iup.DebuggerSetDialogChildAttrib("DEBUG_BTN_DEBUG", "ACTIVE", dbg)
iup.DebuggerSetDialogChildAttrib("DEBUG_BTN_STEPINTO", "ACTIVE", step)
iup.DebuggerSetDialogChildAttrib("DEBUG_BTN_STEPOVER", "ACTIVE", step)
iup.DebuggerSetDialogChildAttrib("DEBUG_BTN_STEPOUT", "ACTIVE", step)
iup.DebuggerSetDialogChildAttrib("DEBUG_BTN_CURRENTLINE", "ACTIVE", curline)
end
function iup.DebuggerHighlightLine(filename, line, source)
if line == nil or line <= 0 then
return
end
debugger.currentLine = line
debugger.currentFile = filename
local multitext = iup.DebuggerGetMultitext(filename, true, true, source) -- find and open if necessary
if not multitext then
return
end
local pos = iup.TextConvertLinColToPos(multitext, line-1, 0) -- line here starts at 0
multitext.caretpos = pos
-- highlight
multitext["MARKERADD"..(line-1)] = 2 -- (margin=2)
-- arrow
multitext["MARKERADD"..(line-1)] = 3 -- (margin=3)
end
function iup.DebuggerShowCurrentLine()
if debugger.currentLine and debugger.currentFile then
iup.DebuggerSelectLine(debugger.currentFile, debugger.currentLine, true) -- find
end
end
function iup.DebuggerGetMultitext(filename, find, open, source)
local multitext = iup.DebuggerGetCurrentMultitext()
if filename ~= multitext.filename then
if find then
multitext = iup.DebuggerFindMultitext(filename)
if open then
debugger.multitext_tabs.value = multitext
iup.DebuggerUpdateTitle()
if not multitext then
multitext = iup.DebuggerOpenMultitext(filename, source)
if not multitext then
return
end
end
end
else
return
end
end
return multitext
end
function iup.DebuggerSelectLine(filename, line, find, source)
if line == nil or line <= 0 then
return
end
local multitext = iup.DebuggerGetMultitext(filename, find, true, source) -- find and always open if necessary
if not multitext then
return
end
local pos = iup.TextConvertLinColToPos(multitext, line-1, 0) -- line here starts at 0
multitext.caretpos = pos
-- select
multitext.selection = line-1 .. ",0:" .. line-1 .. ",9999"
end
------------------------------------- Breakpoints -------------------------------------
function iup.DebuggerHasBreakpoint(filename, line)
local file_breaks = debugger.breakpoints[filename]
if file_breaks and file_breaks[line] then
return true
end
return false
end
function iup.DebuggerRestoreLastListValue(list, last_value)
-- maintain visual position of the selection on the list, not necessarily the same item
last_value = tonumber(last_value)
local count = tonumber(list.count)
if last_value > count then
list.value = count
else
list.value = last_value
end
end
function table.count(t)
local count = 0
for i, v in pairs(t) do
count = count + 1
end
return count
end
function iup.DebuggerUpdateBreakpointsListLines(list_break, m_filename, m_line, new_line)
local count = tonumber(list_break.count)
for i = 1, count do
local filename = list_break["FILENAME" .. i]
local line = tonumber(list_break["LINE" .. i])
if m_filename == filename and m_line == line then
-- update list (just title, index remains the same)
list_break[i] = "Line " .. new_line .. " of \"" .. filename .. "\""
-- update FILENAME#LINE
list_break["LINE" .. i] = new_line
end
end
end
function iup.DebuggerRemoveBreakpointsListItem(list_break, m_filename, m_line)
local count = tonumber(list_break.count)
for i = 1, count do
local filename = list_break["FILENAME" .. i]
local line = tonumber(list_break["LINE" .. i])
if m_filename == filename and m_line == line then
iup.DebuggerRemoveBreakpointsListIndex(list_break, i)
end
end
end
function iup.DebuggerRemoveBreakpointsListIndex(list_break, index)
-- update list
list_break.removeitem = index
local count = tonumber(list_break.count)
-- update FILENAME#LINE
for i = index, count do
list_break["FILENAME"..i] = list_break["FILENAME"..(i+1)]
list_break["LINE"..i] = list_break["LINE"..(i+1)]
end
list_break["FILENAME" .. count+1] = nil
list_break["LINE" .. count+1] = nil
end
function iup.DebuggerRemoveBreakpoint(list_break, index)
local last_value = list_break.value
local line = tonumber(list_break["LINE" .. index])
local filename = list_break["FILENAME" .. index]
local count = tonumber(list_break.count)
-- update multitext
local multitext = iup.DebuggerGetMultitext(filename, true, false) -- find but do NOT open if not found
if multitext then
multitext["MARKERDELETE" .. (line - 1)] = 1 -- margin=1
end
-- update breakpoints table
local file_breaks = debugger.breakpoints[filename]
if file_breaks then
file_breaks[line] = nil
if table.count(file_breaks) == 0 then
debugger.breakpoints[filename] = nil
end
end
-- update list and FILENAME#LINE
iup.DebuggerRemoveBreakpointsListIndex(list_break, index)
if count == 1 then
-- update buttons, it is now empty
iup.DebuggerSetDialogChildAttrib("DEBUG_REMOVE_BREAK", "ACTIVE", "NO")
else
iup.DebuggerRestoreLastListValue(list_break, last_value)
end
end
function iup.DebuggerAddBreakpoint(list_break, filename, line)
local last_value = list_break.value
-- update multitext
local multitext = iup.DebuggerGetMultitext(filename, true, false) -- find but do NOT open if not found
if multitext then
multitext["MARKERADD" .. (line - 1)] = 1 -- margin=1
end
-- update breakpoints table
local file_breaks = debugger.breakpoints[filename]
if not file_breaks then
file_breaks = {}
debugger.breakpoints[filename] = file_breaks
end
file_breaks[line] = { } -- other breakpoint info can be saved here
-- update list
list_break.appenditem = "Line " .. line .. " of \"" .. filename .. "\""
local count = tonumber(list_break.count)
-- update FILENAME#LINE
list_break["FILENAME"..count] = filename
list_break["LINE"..count] = line
if count == 1 then
-- update buttons, it was empty
iup.DebuggerSetDialogChildAttrib("DEBUG_REMOVE_BREAK", "ACTIVE", "Yes")
end
-- select the new item
list_break.value = count
end
function table_compare_lines(elem1, elem2)
if (elem1.line < elem2.line) then
return true
else
return false
end
end
function iup.DebuggerBreakpointsChanged(multitext, start_lin, num_lin)
local filename = multitext.filename
local list_break = iup.GetDialogChild(debugger.main_dialog, "DEBUG_LIST_BREAK")
local last_value = list_break.value
local file_breaks = debugger.breakpoints[filename]
if not file_breaks then
return
end
local new_file_breaks = {}
for line, bp in pairs(file_breaks) do
if line >= start_lin then
if num_lin < 0 and line < start_lin - num_lin then
-- removed lines and removed breakpoint
-- update multitext
multitext["MARKERDELETE" .. (start_lin - 1)] = 1 -- margin=1 -- all breakpoints inside the region are collapsed to the start_lin
-- update breakpoints table
-- simply don't copy to new_file_breaks
-- update list and FILENAME#LINE
iup.DebuggerRemoveBreakpointsListItem(list_break, filename, line)
else
-- added or removed lines, just change line in breakpoints
-- update breakpoints table
-- copy breakpoint at the new position
new_file_breaks[line + num_lin] = bp
-- update list and FILENAME#LINE
iup.DebuggerUpdateBreakpointsListLines(list_break, filename, line, line + num_lin)
end
else
-- no changes, just copy
new_file_breaks[line] = bp
end
end
-- update breakpoints table
if table.count(new_file_breaks) == 0 then
debugger.breakpoints[filename] = nil
else
debugger.breakpoints[filename] = new_file_breaks
end
local count = tonumber(list_break.count)
if count == 0 then
-- update buttons, it is now empty
iup.DebuggerSetDialogChildAttrib("DEBUG_REMOVE_BREAK", "ACTIVE", "NO")
else
iup.DebuggerRestoreLastListValue(list_break, last_value)
end
end
function iup.DebuggerRemoveAllBreakpoints()
local list_break = iup.GetDialogChild(debugger.main_dialog, "DEBUG_LIST_BREAK")
-- update all multitext
iup.DebuggerSetAttribAllMultitext("MARKERDELETEALL", "1") -- margin=1
-- update list
list_break.removeitem = "ALL"
-- update breakpoints table
debugger.breakpoints = {}
-- update FILENAME#LINE
local index = 1
local filename = list_break["FILENAME" .. index]
while filename do
list_break["FILENAME" .. index] = nil
list_break["LINE" .. index] = nil
index = index + 1
filename = list_break["FILENAME" .. index]
end
-- update buttons
iup.DebuggerSetDialogChildAttrib("DEBUG_REMOVE_BREAK", "ACTIVE", "NO")
end
function iup.DebuggerInitBreakpointsList(list_break)
-- From FILENAME#LINE, initialize list and breakpoints table
-- each multitext will be updated when created/loaded, see restoremarkers_cb
list_break.removeitem = "ALL"
debugger.breakpoints = {}
local index = 1
local filename = list_break["FILENAME" .. index]
while filename do
local line = tonumber(list_break["LINE" .. index])
-- update list
list_break[index] = "Line " .. line .. " of \"" .. filename .. "\""
-- update breakpoints table
local file_breaks = debugger.breakpoints[filename]
if not file_breaks then
file_breaks = {}
debugger.breakpoints[filename] = file_breaks
end
file_breaks[line] = { } -- other breakpoint info can be saved here
index = index + 1
filename = list_break["FILENAME" .. index]
end
-- update buttons
if index > 1 then
iup.DebuggerSetDialogChildAttrib("DEBUG_REMOVE_BREAK", "ACTIVE", "Yes")
list_break.value = 1 -- select first item on list
else
iup.DebuggerSetDialogChildAttrib("DEBUG_REMOVE_BREAK", "ACTIVE", "NO")
end
end
function iup.DebuggerAddBreakpointList()
local multitext = iup.DebuggerGetCurrentMultitext()
local suggest_filename = multitext.filename
if (not suggest_filename) then suggest_filename = "" end
local suggest_lin, suggest_col = iup.TextConvertPosToLinCol(multitext, multitext.caretpos)
local status, filename, line = iup.GetParam("Add Breakpoint", setparent_param_cb, "Filename: %s\nLine: %i[1,]\n", suggest_filename, suggest_lin + 1)
if (status) then
local list_break = iup.GetDialogChild(debugger.main_dialog, "DEBUG_LIST_BREAK")
if iup.DebuggerHasBreakpoint(filename, line) then
iup.MessageError(debugger.main_dialog, "Breakpoint already exists.")
return
end
iup.DebuggerAddBreakpoint(list_break, filename, line)
end
end
function iup.DebuggerBreaksListAction(list_break, index)
local filename = list_break["FILENAME" .. index]
local line = tonumber(list_break["LINE" .. index])
iup.DebuggerSelectLine(filename, line, false) -- do not find
end
function iup.DebuggerBreaksListActivate(list_break, index)
local filename = list_break["FILENAME" .. index]
local line = tonumber(list_break["LINE" .. index])
iup.DebuggerSelectLine(filename, line, true) -- find
end
------------------------------------- Locals -------------------------------------
function iup.DebuggerClearLocalVariablesTree()
iup.DebuggerSetDialogChildAttrib("DEBUG_PRINT_LOCAL", "ACTIVE", "NO")
iup.DebuggerSetDialogChildAttrib("DEBUG_SET_LOCAL", "ACTIVE", "NO")
iup.DebuggerSetDialogChildAttrib("DEBUG_TREE_LOCAL", "DELNODE", "ALL")
end
function iup.DebuggerSetLocalVariable()
local tree_local = iup.GetDialogChild(debugger.main_dialog, "DEBUG_TREE_LOCAL")
local id = tree_local.value
if (not id or tonumber(id) < 0) then
iup.MessageError(debugger.main_dialog, "Select a variable on the list.")
return
end
local userdata = iup.TreeGetUserId(tree_local, id)
local name = userdata.name
local value = userdata.value
if (value == nil) then value = "nil" end
local valueType = type(value)
if valueType ~= "string" and valueType ~= "number" and valueType ~= "boolean" then
iup.MessageError(debugger.main_dialog, "Can edit only strings, numbers and booleans.")
return
end
local status, newValue = iup.GetParam("Set Local", setparent_param_cb, name.." = ".."%s{true, false and nil are translated to Lua values.}\n", tostring(value))
if (status) then
local bol = string.lower(newValue)
if bol == "true" then
newValue = true
elseif bol == "false" then
newValue = false
elseif bol == "nil" then
newValue = nil
else
local num = tonumber(newValue)
if num then
newValue = num
end
end
local list_stack = iup.GetDialogChild(debugger.main_dialog, "DEBUG_LIST_STACK")
local index = tonumber(list_stack.value)
-- here there are 4 levels on top of the stack:
-- 1-DebuggerSetLocalVariable,
-- 2-LoopStep
-- 3-DebuggerLineHook,
-- 4-DebuggerHookFunction
local startLevel = 5
local level = index - 1 + startLevel -- this is the level of the function
iup.DebuggerSetLocalValue(tree_local, level, id, newValue)
iup.DebuggerSetLocalTreeItem(tree_local, id, name, newValue)
end
end
function iup.DebuggerPrintLocalVariable()
local tree_local = iup.GetDialogChild(debugger.main_dialog, "DEBUG_TREE_LOCAL")
local id = tree_local.value
iup.DebuggerShowConsoleTab()
local userdata = iup.TreeGetUserId(tree_local, id)
local pos = userdata.pos
local value = userdata.value
iup.ConsolePrint(tree_local["TITLE"..id] .. " (pos="..pos..")")
iup.ConsolePrintValue(value)
end
function iup.DebuggerPrintAllLocalVariables()
local tree_local = iup.GetDialogChild(debugger.main_dialog, "DEBUG_TREE_LOCAL")
local count = tonumber(tree_local.rootcount) -- print all at root only
iup.DebuggerShowConsoleTab()
local id = 0
for i = 0, count-1 do
local userdata = iup.TreeGetUserId(tree_local, id)
local pos = userdata.pos
local value = userdata.value
iup.ConsolePrint(tree_local["TITLE"..id] .. " (pos="..pos..")")
iup.ConsolePrintValue(value)
id = tree_local["NEXT"..id]
end
end
function iup.DebuggerSetTreeTableValue(tree, name, id, newValue)
local parentId = tree["parent"..id]
local parentUdata = iup.TreeGetUserId(tree, parentId)
local parent = parentUdata.value
parent[name] = newValue
end
function iup.DebuggerSetLocalValue(tree_local, level, id, newValue)
local userdata = iup.TreeGetUserId(tree_local, id)
local depth = tonumber(tree_local["depth"..id])
if depth > 0 then
iup.DebuggerSetTreeTableValue(tree_local, userdata.name, id, newValue)
else
local pos = userdata.pos
local s = string.sub(userdata.name, 1, 3)
if s == ":: " then
debug.setupvalue(tree_local.func, pos, newValue)
else
level = level + 1 -- this is the level inside this function
debug.setlocal(level, pos, newValue)
end
end
userdata.value = newValue
end
--[[ UNUSED
function iup.DebuggerGetLocal(tree_local, level, id)
local name, value
local userdata = iup.TreeGetUserId(tree_local, id)
local pos = userdata.pos
local tree_value = tree_local["TITLE"..id]
local s = string.sub(tree_value, 1, 3)
if s == ":: " then
name, value = debug.getupvalue(tree_local.func, pos)
else
level = level + 1 -- this is the level inside this function
name, value = debug.getlocal(level, pos)
end
return name, value
end
]]
function iup.DebuggerLocalVariablesTreeAction(tree_local, id)
local userdata = iup.TreeGetUserId(tree_local, id)
local valueType = type(userdata.value)
if valueType == "string" or valueType == "number" or valueType == "boolean" then
iup.DebuggerSetDialogChildAttrib("DEBUG_SET_LOCAL", "ACTIVE", "Yes")
else
iup.DebuggerSetDialogChildAttrib("DEBUG_SET_LOCAL", "ACTIVE", "No")
end
end
function iup.DebuggerLocalVariablesBranchOpenAction(tree_local, id)
local userdata = iup.TreeGetUserId(tree_local, id)
local tableValue = userdata.value
local pos = userdata.pos
if type(tableValue) ~= "table" or userdata.expanded then
return
end
local dummy_id = id + 1
userdata.expanded = true
id = dummy_id
-- the same algorithm used in ConsolePrintTable
local name
local tmp = {}
for name, value in ipairs(tableValue) do
id = iup.DebuggerAddLocalTreeItem(tree_local, id, name, value, pos)
tmp[name] = true
end
for name, value in pairs(tableValue) do
if (not tmp[name]) then
id = iup.DebuggerAddLocalTreeItem(tree_local, id, name, value, pos)
end
end
iup.DebuggerSetDialogChildAttrib("DEBUG_TREE_LOCAL", "DELNODE"..dummy_id, "SELECTED")
end
function iup.DebuggerGetTreeTitle(tree, id, name, value)
local depth = tonumber(tree["depth"..id])
local pref = ""
local suff = ""
if depth and depth > 0 then
pref = "["
suff = "]"
end
local title = pref .. name .. suff .. " = "
if type(value) == "string" then
title = title .. "\"" .. value .. "\""
else
title = title .. tostring(value)
end
title = title .. " <" .. type(value) .. ">"
return title
end
function iup.DebuggerSetLocalTreeItem(tree_local, id, name, value)
tree_local["TITLE"..id] = iup.DebuggerGetTreeTitle(tree_local, id, name, value)
end
function iup.DebuggerAddLocalTreeItem(tree_local, id, name, value, pos)
local userdata = {}
local lastAddedNode
if type(value) == "table" then
tree_local["INSERTBRANCH"..id] = iup.DebuggerGetTreeTitle(tree_local, id, name, value)
lastAddedNode = tree_local.lastaddnode
tree_local["ADDLEAF"..lastAddedNode] = "dummy"
userdata.expanded = false
else
tree_local["INSERTLEAF"..id] = iup.DebuggerGetTreeTitle(tree_local, id, name, value)
lastAddedNode = tree_local.lastaddnode
end
userdata.name = name
userdata.value = value
userdata.pos = pos
iup.TreeSetUserId(tree_local, lastAddedNode, userdata)
return tonumber(lastAddedNode)
end
function iup.DebuggerUpdateLocalVariablesTree(level)
local name, value
local pos
local id = -1
level = level + 1 -- this is the level inside this function
iup.DebuggerClearLocalVariablesTree()
local tree_local = iup.GetDialogChild(debugger.main_dialog, "DEBUG_TREE_LOCAL")
pos = 1
name, value = debug.getlocal(level, pos)
while name ~= nil do
if string.sub(name, 1, 1) ~= "(" then -- do not include internal variables (loop control variables, temporaries, etc).
id = iup.DebuggerAddLocalTreeItem(tree_local, id, name, value, pos)
end
pos = pos + 1
name, value = debug.getlocal(level, pos)
end
-- vararg (only for Lua >= 5.2, ignored in Lua 5.1)
pos = -1
name, value = debug.getlocal(level, pos)
while name ~= nil do
name = "vararg[" .. -pos .. "]"
id = iup.DebuggerAddLocalTreeItem(tree_local, id, name, value, pos)
pos = pos - 1
name, value = debug.getlocal(level, pos)
end
local call = debug.getinfo(level, "uf")
if call.nups > 0 then
pos = 1
tree_local.func = call.func
name, value = debug.getupvalue(call.func, pos)
while name ~= nil do
name = ":: " .. name
id = iup.DebuggerAddLocalTreeItem(tree_local, id, name, value, pos)
pos = pos + 1
name, value = debug.getupvalue(call.func, pos)
end
else
tree_local.func = nil
end
if (id > 0) then
iup.DebuggerSetDialogChildAttrib("DEBUG_PRINT_LOCAL", "ACTIVE", "Yes")
iup.DebuggerSetDialogChildAttrib("DEBUG_SET_LOCAL", "ACTIVE", "Yes")
tree_local.value = 1 -- select first item on list
end
end
function iup.DebuggerShowTip(word, line)
if debugger.debugState ~= DEBUG_PAUSED and debugger.debugState ~= DEBUG_ERRORPAUSED then
return
end
local list_stack = iup.GetDialogChild(debugger.main_dialog, "DEBUG_LIST_STACK")
local index = tonumber(list_stack.value)
-- here there are 4 levels on top of the stack:
-- 1-DebuggerShowTip,
-- 2-LoopStep
-- 3-DebuggerLineHook,
-- 4-DebuggerHookFunction
local startLevel = 5
local level = index - 1 + startLevel -- this is the level of the function
-- check if inside the current function
local info = debug.getinfo(level, "SlL") -- linedefined, lastlinedefined, activelines
if info.linedefined == 0 and info.lastlinedefined == 0 then
if not info.activelines[line] then
return
end
elseif line < info.linedefined or line > info.lastlinedefined then
return
end
-- check for local
local pos = 1
local name, value = debug.getlocal(level, pos)
while name ~= nil do
if name == word then
return tostring(value)
end
pos = pos + 1
name, value = debug.getlocal(level, pos)
end
-- check for upvalues
local call = debug.getinfo(level, "uf")
if call.nups > 0 then
pos = 1
name, value = debug.getupvalue(call.func, pos)
while name ~= nil do
if name == word then
return tostring(value)
end
pos = pos + 1
name, value = debug.getupvalue(call.func, pos)
end
end
-- check for global
if _G[word] then
return tostring(_G[word])
end
end
------------------------------------- Stack -------------------------------------
function iup.DebuggerStackListAction(list_stack, index)
-- here there are 4 levels on top of the stack:
-- 1-DebuggerStackListAction,
-- 2-LoopStep
-- 3-DebuggerLineHook,
-- 4-DebuggerHookFunction
local startLevel = 5
local level = index - 1 + startLevel -- this is the level of the function
iup.DebuggerUpdateLocalVariablesTree(level)
local filename = list_stack["FILENAME" .. index]
local line = list_stack["LINE" .. index]
iup.DebuggerSelectLine(filename, tonumber(line), false) -- do not find
end
function iup.DebuggerStackListActivate(list_stack, index)
local startLevel = 5
local level = index - 1 + startLevel -- this is the level of the function
iup.DebuggerUpdateLocalVariablesTree(level)
local filename = list_stack["FILENAME" .. index]
local line = list_stack["LINE" .. index]
local source = list_stack["SOURCE" .. index]
iup.DebuggerSelectLine(filename, tonumber(line), true, source) -- find
end
function iup.DebuggerClearStackList()
iup.DebuggerSetDialogChildAttrib("DEBUG_PRINT_LEVEL", "ACTIVE", "NO")
iup.DebuggerSetDialogChildAttrib("DEBUG_LIST_STACK", "REMOVEITEM", "ALL")
end
function iup.DebuggerPrintStackLevel()
local list_stack = iup.GetDialogChild(debugger.main_dialog, "DEBUG_LIST_STACK")
local index = list_stack.value
iup.DebuggerShowConsoleTab()
local defined = list_stack["DEFINED" .. index]
iup.ConsolePrint(list_stack[index] .. " (level=" .. index..")")
iup.ConsolePrint(defined)
end
function iup.DebuggerPrintAllStackLevel()
local list_stack = iup.GetDialogChild(debugger.main_dialog, "DEBUG_LIST_STACK")
local count = tonumber(list_stack.count)
iup.DebuggerShowConsoleTab()
for index = 1, count do
local defined = list_stack["DEFINED" .. index]
iup.ConsolePrint(list_stack[index] .. " (level=" .. index..")")
iup.ConsolePrint(defined)
end
end
function iup.DebuggerUpdateStackList()
local info, desc, defined
-- here there are 3 levels on top of the stack:
-- 1-DebuggerUpdateStackList,
-- 2-DebuggerLineHook,
-- 3-DebuggerHookFunction
local startLevel = 4
local level = startLevel
iup.DebuggerClearStackList()
local list_stack = iup.GetDialogChild(debugger.main_dialog, "DEBUG_LIST_STACK")
info = debug.getinfo(level)--, "Snl") -- source, name, namewhat, what, currentline, linedefined
while info ~= nil do
if info.what == "main" then
desc = "<main>"
elseif info.name and info.name ~= "" then
desc = info.name
else
desc = "<noname>"
end
if _VERSION ~= "Lua 5.1" then
local call = debug.getinfo(level, "uf")
local params = ""
local pos = 1
local name, value = debug.getlocal(call.func, pos)
while name ~= nil do
params = params .. name .. ", "
pos = pos + 1
name, value = debug.getlocal(call.func, pos)
end
if params ~= "" then
if call.isvararg then
params = params .. "..."
else
params = string.sub(params, 1, -3) -- remove last ", "
end
desc = desc .. "(" .. params .. ")"
end
end
if info.namewhat ~= "" then
desc = desc .. " <".. info.namewhat .. ">"
end
if info.currentline > 0 then
desc = desc .. " at line " .. info.currentline
end
local filename
if info.what == "C" then
defined = " [Defined in C"
else
local s = string.sub(info.source, 1, 1)
if s == "@" then
filename = string.sub(info.source, 2)
defined = " [Defined in the file: \"" .. filename .. "\""
else
filename = string.sub(info.short_src, 2, -2)
defined = " [Defined in a " .. filename
end
end
if info.linedefined > 0 then
defined = defined .. ", line " .. info.linedefined .. "]"
else
defined = defined .. "]"
end
local index = (level - startLevel) + 1
list_stack[index] = desc
list_stack["DEFINED" .. index] = defined
list_stack["FILENAME" .. index] = filename
list_stack["SOURCE" .. index] = info.source
list_stack["LINE" .. index] = info.currentline
level = level + 1
info = debug.getinfo(level)--, "Snl") -- source, name, namewhat, what, currentline, linedefined
end
if level > startLevel then
iup.DebuggerSetDialogChildAttrib("DEBUG_PRINT_LEVEL", "ACTIVE", "YES")
list_stack.value = 1 -- select first item on list (startLevel)
iup.DebuggerUpdateLocalVariablesTree(startLevel)
end
end
---------------------------- Globals --------------------------
function iup.DebuggerIsExpression(name)
if string.find(name, "[^%w_]") then -- finds any non alphanumeric character, but allows underscore
return true
else
return false
end
end
function iup.DebuggerGetGlobalNameFromTree(tree_global, id)
local userdata = iup.TreeGetUserId(tree_global, id)
return userdata.globalname
end
function iup.DebuggerGetGlobalValue(name)
local value
-- used only for root nodes, and only when debuger is active
if iup.DebuggerIsExpression(name) then
local cmd, msg = loadstring("return " .. name)
if not cmd then
value = "<Invalid Expression> " .. msg
else
local result = {pcall(cmd)}
if result[1] then
value = result[2]
else
value = "<Invalid Expression>" .. result[2]
end
end
else
value = _G[name]
end
return value
end
function iup.DebuggerGlobalsTreeAction(tree_global, id)
local userdata = iup.TreeGetUserId(tree_global, id)
local name = userdata.globalname
if iup.DebuggerIsExpression(name) then
iup.DebuggerSetDialogChildAttrib("DEBUG_SET_GLOBAL", "ACTIVE", "NO")
else
iup.DebuggerSetDialogChildAttrib("DEBUG_SET_GLOBAL", "ACTIVE", "YES")
end
end
function iup.DebuggerGlobalVariablesBranchOpenAction(tree_global, id)
-- can not expand when debugger is not active
if not iup.DebuggerIsActive() then
return iup.IGNORE
end
local userdata = iup.TreeGetUserId(tree_global, id)
local tableValue = userdata.value
if type(tableValue) ~= "table" or userdata.expanded then
return
end
local dummy_id = id + 1
userdata.expanded = true
id = dummy_id
-- the same algorithm used in ConsolePrintTable
local name
local tmp = {}
for name, value in ipairs(tableValue) do
id = iup.DebuggerAddGlobalsTreeItem(tree_global, id, name, value)
tmp[name] = true
end
for name, value in pairs(tableValue) do
if (not tmp[name]) then
id = iup.DebuggerAddGlobalsTreeItem(tree_global, id, name, value)
end
end
iup.DebuggerSetDialogChildAttrib("DEBUG_TREE_GLOBAL", "DELNODE"..dummy_id, "SELECTED")
end
function iup.DebuggerInitGlobalsTree(tree_global)
if not tree_global then
tree_global = iup.GetDialogChild(debugger.main_dialog, "DEBUG_TREE_GLOBAL")
end
local count = tonumber(tree_global.rootcount)
local id = 0
for i = 0, count-1 do
local userdata = iup.TreeGetUserId(tree_global, id)
local name = userdata.globalname
tree_global["TITLE"..id] = name
tree_global["IMAGE"..id] = "IUP_TreeEmpty"
tree_global["IMAGEEXPANDED"..id] = "IUP_TreeEmpty"
tree_global["DELNODE"..id] = "CHILDREN"
id = tree_global["NEXT"..id]
end
tree_global.expandall = "NO"
local count = tonumber(tree_global.rootcount)
if (count > 0) then
iup.DebuggerSetDialogChildAttrib("DEBUG_PRINT_GLOBAL", "ACTIVE", "Yes")
iup.DebuggerSetDialogChildAttrib("DEBUG_SET_GLOBAL", "ACTIVE", "Yes")
iup.DebuggerSetDialogChildAttrib("DEBUG_REMOVE_GLOBAL", "ACTIVE", "Yes")
tree_global.value = 1 -- select first item on list
iup.DebuggerGlobalsTreeAction(tree_global, tree_global.value)
else
iup.DebuggerSetDialogChildAttrib("DEBUG_PRINT_GLOBAL", "ACTIVE", "NO")
iup.DebuggerSetDialogChildAttrib("DEBUG_SET_GLOBAL", "ACTIVE", "NO")
iup.DebuggerSetDialogChildAttrib("DEBUG_REMOVE_GLOBAL", "ACTIVE", "NO")
end
end
function iup.DebuggerUpdateGlobalsTree()
-- this is called only during debug
local tree_global = iup.GetDialogChild(debugger.main_dialog, "DEBUG_TREE_GLOBAL")
local count = tonumber(tree_global.rootcount) -- print all at root only
-- clear all secondary branches (including "dummy")
local id = 0
for i = 0, count-1 do
tree_global["DELNODE"..id] = "CHILDREN"
id = tree_global["NEXT"..id]
end
id = 0
for i = 0, count-1 do
local userdata = iup.TreeGetUserId(tree_global, id)
local name = userdata.globalname
local value = iup.DebuggerGetGlobalValue(name)
userdata.value = value
userdata.expanded = false
iup.DebuggerSetGlobalsTreeItem(tree_global, id, name, value)
id = tree_global["NEXT"..id]
end
tree_global.expandall = "NO"
end
function iup.DebuggerSetGlobalsTreeItem(tree_global, id, name, value)
tree_global["TITLE"..id] = iup.DebuggerGetTreeTitle(tree_global, id, name, value)
if type(value) == "table" then
tree_global["IMAGE"..id] = "IUP_TreePlus"
tree_global["IMAGEEXPANDED"..id] = "IUP_TreeMinus"
-- restore "dummy"
tree_global["ADDLEAF"..id] = "dummy"
else
tree_global["IMAGE"..id] = "IUP_TreeEmpty"
tree_global["IMAGEEXPANDED"..id] = "IUP_TreeEmpty"
end
end
function iup.DebuggerAddGlobalsTreeItem(tree_global, id, name, value)
local userdata = {}
local lastAddedNode
if type(value) == "table" then
tree_global["INSERTBRANCH"..id] = iup.DebuggerGetTreeTitle(tree_global, id, name, value)
lastAddedNode = tree_global.lastaddnode
tree_global["IMAGE"..lastAddedNode] = "IUP_TreePlus"
tree_global["IMAGEEXPANDED"..lastAddedNode] = "IUP_TreeMinus"
tree_global["ADDLEAF"..lastAddedNode] = "dummy"
else
tree_global["INSERTBRANCH"..id] = iup.DebuggerGetTreeTitle(tree_global, id, name, value)
lastAddedNode = tree_global.lastaddnode
tree_global["IMAGE"..lastAddedNode] = "IUP_TreeEmpty"
tree_global["IMAGEEXPANDED"..lastAddedNode] = "IUP_TreeEmpty"
end
userdata.globalname = name
userdata.expanded = false
userdata.value = value
iup.TreeSetUserId(tree_global, lastAddedNode, userdata)
return lastAddedNode
end
function iup.DebuggerAddGlobal(tree_global, name)
local id = tonumber(tree_global.last0)
if not id then id = -1 end
if iup.DebuggerIsActive() then
local value = iup.DebuggerGetGlobalValue(name)
return iup.DebuggerAddGlobalsTreeItem(tree_global, id, name, value)
else
local userdata = {}
userdata.globalname = name
userdata.expanded = false
tree_global["INSERTBRANCH"..id] = name
local lastAddedNode = tree_global.lastaddnode
tree_global["IMAGE"..lastAddedNode] = "IUP_TreeEmpty"
tree_global["IMAGEEXPANDED"..lastAddedNode] = "IUP_TreeEmpty"
iup.TreeSetUserId(tree_global, lastAddedNode, userdata)
return lastAddedNode
end
end
function iup.DebuggerAddGlobalVariable()
local status, newName = iup.GetParam("Add Global", setparent_param_cb, "Name = ".."%s\n", "")
if (status) then
local tree_global = iup.GetDialogChild(debugger.main_dialog, "DEBUG_TREE_GLOBAL")
local count = tonumber(tree_global.rootcount)
local lastAddedNode = iup.DebuggerAddGlobal(tree_global, newName)
if (count == 0) then
iup.DebuggerSetDialogChildAttrib("DEBUG_PRINT_GLOBAL", "ACTIVE", "Yes")
iup.DebuggerSetDialogChildAttrib("DEBUG_SET_GLOBAL", "ACTIVE", "Yes")
iup.DebuggerSetDialogChildAttrib("DEBUG_REMOVE_GLOBAL", "ACTIVE", "Yes")
end
iup.DebuggerGlobalsTreeAction(tree_global, lastAddedNode)
end
end
function iup.DebuggerRemoveGlobalVariable()
local tree_global = iup.GetDialogChild(debugger.main_dialog, "DEBUG_TREE_GLOBAL")
local id = tree_global.value
if (not id or tonumber(id) < 0) then
iup.MessageError(debugger.main_dialog, "Select a variable or expression on the list.")
return
end
-- get the root item
local parentId = tree_global["parent"..id]
while parentId do
id = parentId
parentId = tree_global["parent"..id]
end
-- select the next item
local next_id = tree_global["next"..id]
if next_id then
tree_global.value = next_id
else
local previous_id = tree_global["previous"..id]
if previous_id then
tree_global.value = previous_id
end
end
tree_global["DELNODE"..id] = "SELECTED"
local count = tonumber(tree_global.rootcount)
if (count == 0) then
iup.DebuggerSetDialogChildAttrib("DEBUG_PRINT_GLOBAL", "ACTIVE", "No")
iup.DebuggerSetDialogChildAttrib("DEBUG_SET_GLOBAL", "ACTIVE", "No")
iup.DebuggerSetDialogChildAttrib("DEBUG_REMOVE_GLOBAL", "ACTIVE", "No")
else
iup.DebuggerGlobalsTreeAction(tree_global, tree_global.value)
end
end
function iup.DebuggerRemoveAllGlobalVariable()
local tree_global = iup.GetDialogChild(debugger.main_dialog, "DEBUG_TREE_GLOBAL")
tree_global["DELNODE"] = "ALL"
iup.DebuggerSetDialogChildAttrib("DEBUG_PRINT_GLOBAL", "ACTIVE", "No")
iup.DebuggerSetDialogChildAttrib("DEBUG_SET_GLOBAL", "ACTIVE", "No")
iup.DebuggerSetDialogChildAttrib("DEBUG_REMOVE_GLOBAL", "ACTIVE", "No")
end
function iup.DebuggerSetGlobalVariable()
local tree_global = iup.GetDialogChild(debugger.main_dialog, "DEBUG_TREE_GLOBAL")
local id = tree_global.value
if (not id or tonumber(id) < 0) then
iup.MessageError(debugger.main_dialog, "Select a variable on the list.")
return
end
local userdata = iup.TreeGetUserId(tree_global, id)
local name = userdata.globalname
if iup.DebuggerIsExpression(name) then
iup.MessageError(debugger.main_dialog, "Can not change expressions values.")
return
end
local value
local depth = tonumber(tree_global["depth"..id])
if depth > 0 then
value = userdata.value
else
value = iup.DebuggerGetGlobalValue(name)
end
if (value == nil) then value = "nil" end
local valueType = type(value)
if valueType ~= "string" and valueType ~= "number" and valueType ~= "boolean" then
iup.MessageError(debugger.main_dialog, "Can edit only strings, numbers and booleans.")
return
end
local status, newValue = iup.GetParam("Set Global", setparent_param_cb, name.." = ".."%s{true, false and nil are translated to Lua values.}\n", tostring(value))
if (status) then
local bol = string.lower(newValue)
if bol == "true" then
newValue = true
elseif bol == "false" then
newValue = false
elseif bol == "nil" then
newValue = nil
else
local num = tonumber(newValue)
if num then
newValue = num
end
end
if depth > 0 then
iup.DebuggerSetTreeTableValue(tree_global, name, id, newValue)
else
_G[name] = newValue
end
if iup.DebuggerIsActive() then
iup.DebuggerSetGlobalsTreeItem(tree_global, id, name, newValue)
else
tree_global["TITLE"..id] = name
tree_global["IMAGE"..id] = "IUP_TreeEmpty"
tree_global["IMAGEEXPANDED"..id] = "IUP_TreeEmpty"
end
end
end
function iup.DebuggerPrintGlobalVariable()
local tree_global = iup.GetDialogChild(debugger.main_dialog, "DEBUG_TREE_GLOBAL")
local id = tree_global.value
iup.DebuggerShowConsoleTab()
local userdata = iup.TreeGetUserId(tree_global, id)
local value
local depth = tonumber(tree_global["depth"..id])
if depth > 0 then
value = userdata.value
else
value = iup.DebuggerGetGlobalValue(userdata.globalname)
end
iup.ConsolePrint(tree_global["TITLE"..id])
iup.ConsolePrintValue(value)
end
function iup.DebuggerPrintAllGlobalVariables()
local tree_global = iup.GetDialogChild(debugger.main_dialog, "DEBUG_TREE_GLOBAL")
local count = tonumber(tree_global.rootcount) -- print all at root only
iup.DebuggerShowConsoleTab()
local id = 0
for i = 0, count-1 do
local userdata = iup.TreeGetUserId(tree_global, id)
local value = iup.DebuggerGetGlobalValue(userdata.globalname)
iup.ConsolePrint(tree_global["TITLE"..id])
iup.ConsolePrintValue(value)
id = tree_global["NEXT"..id]
end
end
---------------------------- Debug State --------------------------
function iup.DebuggerGetFuncLevel()
-- level 0 is the current function (getinfo itself);
-- level 1 is the function that called getinfo (DebuggerGetFuncLevel)
local func_level = 1
repeat
func_level = func_level + 1
until debug.getinfo(func_level, "l") == nil -- only current line, default is all info
return func_level - 1
end
function iup.DebuggerUpdateState(filename, line)
if debugger.debugState == DEBUG_STEP_OUT then
if debugger.stepFuncState == FUNC_STATE_OUTSIDE then
iup.DebuggerSetState(DEBUG_PAUSED)
debugger.stepFuncState = FUNC_STATE_OUTSIDE
debugger.stepFuncLevel = debugger.currentFuncLevel
end
elseif debugger.debugState == DEBUG_STEP_INTO or
(debugger.debugState == DEBUG_STEP_OVER and debugger.stepFuncState == FUNC_STATE_OUTSIDE) or
(debugger.debugState ~= DEBUG_PAUSED and iup.DebuggerHasBreakpoint(filename, line)) then
iup.DebuggerSetState(DEBUG_PAUSED)
end
end
function iup.DebuggerErrorMessage(message)
local dlg = iup.messagedlg
{
parentdialog = debugger.main_dialog,
dialogtype = "ERROR",
buttons = "OKCANCEL",
title = "Lua Error!",
}
dlg.value = message .. "\n\nWould you like to debug at error location?"
dlg:popup(iup.CENTERPARENT, iup.CENTERPARENT)
local ret = dlg.buttonresponse
dlg:destroy()
if ret == "1" then
return true
else
return false
end
end
function iup.DebuggerForcePause(filename, currentline, source)
iup.DebuggerSetState(DEBUG_ERRORPAUSED)
iup.DebuggerHighlightLine(filename, currentline, source)
iup.DebuggerUpdateGlobalsTree()
iup.DebuggerUpdateStackList()
while debugger.debugState == DEBUG_ERRORPAUSED do
local ret = iup.LoopStep()
if ret == iup.CLOSE then
iup.ExitLoop() -- repost to MainLoop
end
end
-- in any change of state, simply stop the debugger
iup.DebuggerSetState(DEBUG_INACTIVE)
iup.DebuggerCloseTemporary()
end
function iup.DebuggerTraceBack(msg)
if not msg then
return
end
-- hook is always at level 2 when called
local info = debug.getinfo(2, "Sl") -- what, source, currentline
local s = string.sub(info.source, 1, 1)
local filename
if s == "@" then
filename = string.sub(info.source, 2)
else
filename = string.sub(info.short_src, 2, -2)
end
iup._TRACEBACK = nil
debug.sethook() -- turns off the hook
-- NOT necessary
-- msg = debug.traceback(msg)
iup.ConsolePrint("Lua Error:")
iup.ConsolePrint(msg)
if iup.DebuggerErrorMessage(msg) then
iup.DebuggerForcePause(filename, info.currentline, info.source)
end
end
function iup.DebuggerLineHook(filename, line, source)
debugger.currentFuncLevel = iup.DebuggerGetFuncLevel()
iup.DebuggerUpdateState(filename, line)
if debugger.debugState == DEBUG_PAUSED then
iup.DebuggerHighlightLine(filename, line, source)
iup.DebuggerUpdateGlobalsTree()
iup.DebuggerUpdateStackList()
debug.sethook() -- turns off the hook
while debugger.debugState == DEBUG_PAUSED do
local ret = iup.LoopStep()
if ret == iup.CLOSE then
iup.ExitLoop() -- repost to MainLoop
end
end
if iup.DebuggerIsActive() then
debug.sethook(iup.DebuggerHookFunction, "lcr") -- restore the hook if still active
end
end
if debugger.debugState == DEBUG_STOPPED then
iup.DebuggerEndDebug(true)
end
end
function iup.DebuggerCallHook()
if debugger.debugState == DEBUG_STEP_OVER then
if debugger.stepFuncLevel == 0 then
local func_level = iup.DebuggerGetFuncLevel()
debugger.stepFuncState = FUNC_STATE_INSIDE
debugger.stepFuncLevel = func_level
end
end
end
function iup.DebuggerReturnHook(filename, what)
if filename == debugger.startFile and what == "main" then
iup.DebuggerSetState(DEBUG_INACTIVE)
elseif debugger.debugState == DEBUG_STEP_OUT or debugger.debugState == DEBUG_STEP_OVER then
local func_level = iup.DebuggerGetFuncLevel()
if debugger.stepFuncLevel == func_level then
debugger.stepFuncState = FUNC_STATE_OUTSIDE
debugger.stepFuncLevel = 0
end
end
end
function iup.DebuggerHookFunction(event, line)
-- how many levels we have before the hook was invoked?
-- hook is always at level 2 when called
-- Inside a hook, you can call getinfo with level 2 to get more information about the running function
local info = debug.getinfo(2, "S") -- what, source
local s = string.sub(info.source, 1, 1)
local filename
if s == "@" then
filename = string.sub(info.source, 2)
else
filename = string.sub(info.short_src, 2, -2)
end
if iup.DebuggerIsActive() then
if event == "call" then
iup.DebuggerCallHook()
elseif event == "return" then
iup.DebuggerReturnHook(filename, info.what)
elseif event == "line" then
iup.DebuggerLineHook(filename, line, info.source)
end
end
local ret = iup.LoopStep()
if ret == iup.CLOSE then
iup.ExitLoop() -- repost to MainLoop
end
end
function iup.DebuggerStartDebug(filename)
debugger.startFile = filename
iup.DebuggerShowDebugTab()
iup.ConsolePrint("-- Debug start")
iup.DebuggerSetState(DEBUG_ACTIVE)
debug.sethook(iup.DebuggerHookFunction, "lcr")
iup._TRACEBACK = iup.DebuggerTraceBack
end
function iup.DebuggerEndDebug(stop)
iup._TRACEBACK = nil
debug.sethook() -- turns off the hook
iup.DebuggerSetState(DEBUG_INACTIVE)
iup.DebuggerShowConsoleTab()
if stop then
iup.ConsolePrint("-- Debug stop!")
error() -- abort processing, no error message
else
iup.ConsolePrint("-- Debug finish")
end
iup.DebuggerCloseTemporary()
end
function iup.DebuggerExit()
if iup.DebuggerIsActive() then
iup.DebuggerEndDebug(true) -- make a stop
end
end