2
-- This lets you pause execution of the app and step through it line by line.
3
-- This adds a function, debugger.breakpt(), that triggers this intrument.
4
-- Until this is called, the instrument remains hidden.
6
DebugStepper = DebugInstrument:extend
13
onNew = function (self)
14
self.stepIntoButton = self:add(DebugInstrumentButton:new
17
onMouseUp = function (self)
18
debugger._stepPaused = false
19
debugger._stepFilter = nil
23
self.stepOverButton = self:add(DebugInstrumentButton:new
26
onMouseUp = function (self)
27
debugger._stepPaused = false
28
local prevStack = debugger._stepStack()
30
debugger._stepFilter = function (stack)
31
for i = 1, #stack - #prevStack do
34
for j = 1, #prevStack do
35
if stack[i + j] ~= prevStack[j] then
41
-- we are now executing a sub-call;
42
-- temporarily disable our line hook until
43
-- we return to the previous function
46
debug.sethook(function()
47
local state = debug.getinfo(3, 'f')
49
if state.func == prevStack[1] then
50
-- we're at least on our old function, but is
51
-- the stack depth the same?
56
state = debug.getinfo(3 + depth, 'f')
57
if not state then break end
61
if depth == #prevStack then
62
debug.sethook(debugger._stepLine, 'l')
75
self.stepOutButton = self:add(DebugInstrumentButton:new
78
onMouseUp = function (self)
79
debugger._stepPaused = false
80
local prevStack = debugger._stepStack()
82
-- disable our line hook until the current function returns
84
debug.sethook(function()
89
local state = debug.getinfo(depth, 'f')
94
elseif state.func ~= prevStack[depth - 1] then
102
debug.sethook(debugger._stepLine, 'l')
108
self.continueButton = self:add(DebugInstrumentButton:new
111
onMouseUp = function (self)
112
debugger._stepPaused = false
113
debugger.endBreakpt()
117
self.lineHighlight = self:add(Fill:new{ fill = {64, 64, 64}, height = 0, width = 0 })
119
self.sourceLines = self:add(Text:new
127
self.sourceView = self:add(Text:new{ font = self.font, wordWrap = false })
128
self.lineHeight = self.sourceView._fontObj:getHeight()
130
self.title.text = 'Source'
131
self.contentHeight = self.lineHeight * (self.lineContext + 1) * 2 + self.spacing * 3 +
132
DebugInstrumentButton.height
134
debugger.breakpt = function()
135
local print = debugger.unsourcedPrint or print
136
local caller = debug.getinfo(2, 'S')
138
debugger.showConsole()
141
print('\n' .. string.rep('=', 40))
142
print('Breakpoint, ' .. caller.short_src .. ', ' .. caller.linedefined)
143
print(string.rep('=', 40))
144
debug.sethook(debugger._stepLine, 'l')
147
debugger.endBreakpt = function()
149
if debugger.hideStack then debugger.hideStack() end
150
if debugger.hideLocals then debugger.hideLocals() end
151
debugger.hideConsole()
155
-- hook to handle stepping over source
157
debugger._stepLine = function (_, line)
158
local state = debug.getinfo(2, 'Sl')
160
if string.find(state.source, 'zoetrope/debug') or
161
(debugger._stepFilter and not debugger._stepFilter(debugger._stepStack())) then
162
--print('skipping', state.source, state.currentline, #debugger._stepStack())
166
if debugger.showStack then debugger.showStack(4) end
167
if debugger.showLocals then debugger.showLocals(4) end
169
local file = string.match(state.source, '^@(.*)')
170
self:showLine(file, line)
172
debugger._stepPaused = true
175
while debugger._stepPaused and not quit do
176
quit = debugger._miniEventLoop()
185
-- returns a table representing the call stack during a source step
187
debugger._stepStack = function()
191
local afterHook = false
194
info = debug.getinfo(level, 'f')
195
if not info then break end
198
table.insert(result, info.func)
199
elseif info.func == debugger._stepLine then
210
onResize = function (self, x, y, width, height)
211
self.sourceLines.x = x + self.spacing
212
self.sourceLines.y = y + self.spacing
213
self.sourceLines.height = height - self.sourceLines.y - self.spacing
215
self.sourceView.x = self.sourceLines.x + self.sourceLines.width + self.spacing
216
self.sourceView.y = self.sourceLines.y
217
self.sourceView.width = width - self.sourceView.x - self.spacing * 2
218
self.sourceView.height = self.sourceLines.height
220
self.lineHighlight.x = x + self.spacing
221
self.lineHighlight.y = self.sourceLines.y + self.lineHeight * self.lineContext
222
self.lineHighlight.width = width - self.spacing * 2
223
self.lineHighlight.height = self.lineHeight
225
self.stepIntoButton.x = self.sourceLines.x
226
self.stepIntoButton.y = self.sourceView.y + self.sourceView.height + self.spacing
228
self.stepOverButton.x = self.stepIntoButton.x + self.stepIntoButton.width + self.spacing
229
self.stepOverButton.y = self.stepIntoButton.y
231
self.stepOutButton.x = self.stepOverButton.x + self.stepOverButton.width + self.spacing
232
self.stepOutButton.y = self.stepOverButton.y
234
self.continueButton.x = width - self.stepOverButton.width
235
self.continueButton.y = self.stepOverButton.y
238
showLine = function (self, file, line)
240
self.sourceLines.text = ''
241
self.sourceView.text = ''
243
for i = line - self.lineContext, line + self.lineContext + 1 do
244
local source = debugger.sourceLine(file, i)
247
self.sourceLines.text = self.sourceLines.text .. i .. '\n'
248
self.sourceView.text = self.sourceView.text .. string.gsub(debugger.sourceLine(file, i), '\t', string.rep(' ', 4)) .. '\n'
252
self.title.text = file .. ':' .. line
254
self.title.text = 'Source'
255
self.sourceView.text = '(source not available)'