Made the Debugger's lookup of local variables not precede the ability to send messages.
[cslatevm.git] / src / core / debugger.slate
blob67bc0334ddb021caa04c837aa46f966a80575599
1 "DebugConsole writeStream collectionLimit := 512.
2 DebugConsole writeStream nestingLimit := 3.
3 Stream PrettyPrinter collectionLimit := 512.
4 Stream PrettyPrinter nestingLimit := 3."
6 Stream PrettyPrinter collectionLimit := 256.
7 Stream PrettyPrinter nestingLimit := 2.
9 conditions define: #Debugger &parents: {Cloneable}
10   &slots: {#condition -> Nil.
11            #restarts -> ExtensibleArray new.
12            #interpreter -> bootstrapInterpreter.
13            #resource -> DebugConsole.
14            #parser -> Parser.
15            #printer -> DebugConsole writer.
16            #namespace -> Nil.
17            #frames -> ExtensibleArray new.
18            #currentFrame -> Nil.
19            #baseFramePointer -> Nil.
20            #maxBacktraceSize -> 40.
21            #basePrompt -> '> '.
22            #isSaved -> False. "is the stack live or is it saved?"
23            #quitFlag -> False}.
25 Debugger traits define: #Namespace &parents: {Namespace} &slots: {}.
27 ns@(Debugger Namespace traits) new [Debugger Namespace clone].
29 d@(Debugger traits) refreshWorkspace
31   d namespace := d namespace newSisterSpace.
32   ns@(d namespace) debugger [d].
35 Debugger atSlotNamed: #namespace put: (Debugger Namespace newDelegatingTo: lobby).
37 d@(Debugger traits) on: resource
39   d resource := resource.
40   d parser := d parser newOn: resource reader.
41   d printer := resource writer.
42   d printer collectionLimit := Stream PrettyPrinter collectionLimit.
43   d printer nestingLimit := Stream PrettyPrinter nestingLimit.
44   d
47 d@(Debugger traits) newFor: c &interpreter: interp &console: resource &topMethod: symbol &save: isSaved
49   interp `defaultsTo: d interpreter.
50   resource `defaultsTo: d resource.
51   isSaved `defaultsTo: False.
52   symbol `defaultsTo: #invokeDebugger.
53   d clone `>>
54     [| :newD |
55      on: resource.
56      isSaved := isSaved.
57      condition := c.
58      restarts := d restarts new.
59      namespace := d namespace clone.
60      parser currentScope := nodes Namespace for: newD namespace.
61      parser parseInteractively:
62        ([repl parser parseInteractively]
63          on: SeriousCondition do: #(return: False) `er).
64      baseFramePointer := d interpreter framePointerOf: symbol.
65      buildFrames. ]
68 c@(Condition traits) invokeDebugger
69 "The hook for any Condition to start the debugger and enter its loop."
70 [(Debugger newFor: c) enter].
72 d@(Debugger traits) findRestarts
73 "Rebuilds the list of applicable restarts."
75   d restarts clear.
76   conditionStack
77     reverseDo:
78       [| :context |
79        (context is: Restart) /\ [context appliesTo: d condition]
80          ifTrue: [d restarts addLast: context]].
81   d restarts
84 d@(Debugger traits) describeRestartAt: index
85 [| restart |
86   restart := d restarts at: index.
87   d printer `cache ; 'restart: ' ; index printString ; '\t'.
88   restart describeOn: d printer.
91 d@(Debugger traits) describeRestarts
92 "Describes the restarts available in the context, if any."
94   d printer `cache ; 'The following condition was signaled:\n'.
95   d condition describeOn: d printer.
96   d printer ; '\nAvailable Restarts:\n'.
97   d restarts isEmpty
98     ifTrue: [d printer ; '(none available)']
99     ifFalse: [d restarts keysDo: #(d describeRestartAt: _) `er].
102 d@(Debugger traits) stackAt: x
104   d interpreter stack at: x
107 d@(Debugger traits) stackAt: x put: val
109   d interpreter stack at: x put: val
112 Debugger traits define: #Frame &parents: {Cloneable}
113   &slots: #{#debugger. #framePointer. #nextFramePointer}.
115 Debugger traits define: #SavedFrame &parents: {Debugger Frame}
116   &slots: #{#lexicalContext. #method. #instructionPointer. #resultStackLocation}.
118 f@(Debugger Frame traits) isSaved [False].
119 f@(Debugger SavedFrame traits) isSaved [True].
121 f@(Debugger Frame traits) sizeOnStack [6].
123 f@(Debugger Frame traits) newOn: d@(Debugger traits) at: fp from: nextFp
125   fp > f sizeOnStack ifTrue:
126     [f cloneSettingSlots: #{#debugger. #framePointer. #nextFramePointer} to: {d. fp. nextFp}]
129 f@(Debugger Frame traits) newAtTopOf: d@(Debugger traits)
131   f cloneSettingSlots: #{#debugger. #framePointer} to: {d. d baseFramePointer}
134 f@(Debugger Frame traits) nextFrame
136   f newOn: f debugger at: f callerFrameIndex from: f framePointer
139 f@(Debugger Frame traits) stackAtOffset: offset
140 [f debugger stackAt: f framePointer + offset].
142 f@(Debugger Frame traits) stackAtOffset: offset put: value
143 [f debugger stackAt: f framePointer + offset put: value].
145 f@(Debugger Frame traits) localForOffset: offset
147   f method method sourceTree
148     ifNotNilDo:
149       [| :src |
150        (offset < src inputVariables size
151           ifTrue: [src inputVariables]
152           ifFalse: [src localVariables]) at: offset ifAbsent: [Nil]]
155 f@(Debugger Frame traits) stackOffsetForLocal: name
157   f method method sourceTree
158     ifNil: [warn: 'No source information available for the current frame.'. ]
159     ifNotNilDo:
160       [| :src |
161        (src inputVariables indexOfFirstSatisfying: [| :var | var name =~ name])
162          ifNil:
163            [src localVariables indexOfFirstSatisfying: [| :var | var name =~ name]]]
166 f@(Debugger Frame traits) callerFrameIndex
167 [f stackAtOffset: -1].
169 f@(Debugger Frame traits) lexicalContext
170 [f stackAtOffset: -2].
172 f@(Debugger Frame traits) method
173 [f stackAtOffset: -3].
175 f@(Debugger Frame traits) callerInstructionPointer
176 [f stackAtOffset: -4].
178 f@(Debugger Frame traits) instructionPointer
179 [f debugger stackAt: f nextFramePointer - 4].
181 f@(Debugger Frame traits) resultStackLocation
182 [f stackAtOffset: -5].
184 f@(Debugger Frame traits) previousStackPointer
185 [f stackAtOffset: -6].
187 f@(Debugger Frame traits) callerFrameIndex: fp
188 [f stackAtOffset: -1 put: fp].
190 f@(Debugger Frame traits) lexicalContext: lc
191 [f stackAtOffset: -2 put: lc].
193 f@(Debugger Frame traits) method: m
194 [f stackAtOffset: -3 put: m].
196 f@(Debugger Frame traits) callerInstructionPointer: ip
197 [f stackAtOffset: -4 put: ip].
199 f@(Debugger Frame traits) instructionPointer: ip
200 [f debugger stackAt: f nextFramePointer - 4 put: ip].
202 f@(Debugger Frame traits) resultStackLocation: rp
203 [f stackAtOffset: -5 put: rp].
205 f@(Debugger Frame traits) previousStackPointer: sp
206 [f stackAtOffset: -6 put: sp].
208 d@(Debugger traits) saveFrame: f@(Debugger Frame traits)
209 [Debugger SavedFrame cloneSettingSlots: #{#lexicalContext. #method. #instructionPointer. #resultStackLocation. #debugger}
210                      to: {f lexicalContext. f method. f instructionPointer. f resultStackLocation. d}].
212 f@(Debugger Frame traits) describeOn: out
214   f method method `cacheAs: #method.
215   out
216     ; 'frame: ' ; (f debugger frames indexOf: f) printString ; '\n'
217     ; '  FP: ' ; f framePointer printString  ; '\n'
218     ; '  IP: ' ; f instructionPointer printString ; '\n'
219     ; 'method: ' ; method printString ; ' @ ' ; method definitionLocation ; '\n'
220     ; '  #calls: ' ; method callCount printString ; '\n'
221     ; '  #registers: ' ; method registerCount printString ; '\n'
222     ; '  code size: ' ; method code size printString ; '\n'
223     ; '  source: ' ; method sourceTree printString ; '\n'.
224   out ; 'Inputs:\n'.
225   0 below: method inputVariables do:
226     [| :i | out
227        ; ' arg: ' ; i printString
228        ; '\t' ; ((f localForOffset: i) ifNil: ['?'] ifNotNilDo: [| :var | var name as: ''])
229        ; ' := ' ; (f isSaved ifTrue: ['(not live)'] ifFalse: [(f stackAtOffset: i) printString])
230        ; '\n'].
231   out ; 'Locals:\n'.
232   method inputVariables below: method inputVariables + method localVariables do:
233     [| :i | out
234        ; ' arg: ' ; i printString
235        ; '\t' ; ((f localForOffset: i) ifNil: ['?'] ifNotNilDo: [| :var | var name as: ''])
236        ; ' := ' ; (f isSaved ifTrue: ['(not live)'] ifFalse: [(f stackAtOffset: i) printString])
237        ; '\n'].
238   out ; 'Registers:\n'.
239   method inputVariables + method localVariables below: method registerCount do:
240     [| :i | out
241        ; ' arg: ' ; i printString
242        ; '\t' ; (f isSaved ifTrue: ['(not live)'] ifFalse: [(f stackAtOffset: i) printString])
243        ; '\n'].
244   out ; 'Decompiled:\n'.
245   [method printInstructionsOn: out &condensed: True]
246     on: SeriousCondition do: [| :c | out ; '(instructions unavailable)\n'. c return: Nil].
249 f@(Debugger Frame traits) reset
251   f instructionPointer := 0.
254 f@(Debugger Frame traits) restart
255 "Proceed execution from the start of this frame."
257   f reset.
258   f debugger quitFlag := True.
261 d@(Debugger traits) topFrame
262 [d Frame newAtTopOf: d].
264 d@(Debugger traits) buildFrames
265 [| frame |
266   d baseFramePointer ifNil:
267     [error: 'Base frame pointer is nil. Cannot build stack frames.\n'. ^ Nil].
268   d frames := d frames new `>> [add: (frame := d topFrame). ].
269   [(frame := frame nextFrame) isNotNil] whileTrue:
270     [d frames add:
271        (d isSaved
272           ifTrue: [d saveFrame: frame]
273           ifFalse: [frame])].
276 d@(Debugger traits) prompt
278   [| :out |
279    out ; 'slate-debug'.
280    d restarts isEmpty
281      ifFalse: [out ; '[0'.
282               d restarts size > 1 ifTrue:
283                 [out ; '..' ; d restarts indexLast printString].
284               out ; ']'].
285    d currentFrame ifNotNilDo: [| :frame | out ; '[frame: ' ; ((d frames indexOf: frame) as: String) ; ']'].
286    out ; d basePrompt] writingAs: ''
289 d@(Debugger traits) signalRestartAt: index
290 "Takes the restart option number from the queryRestart and invokes it."
292   (d restarts acceptsKey: index)
293     ifTrue: [((d restarts at: index) newCondition: d condition)
294               `>> [queryFrom: d. signal]]
295     ifFalse: [d resource ; index printString ; ' is not a valid restart.\n']
298 d@(Debugger traits) printFrameLineAt: index &stream: out &showLocation: showLocation
300   out `defaultsTo: d printer.
301   showLocation `defaultsTo: False.
302   (d frames at: index) `cacheAs: #frame.
303   out ; 'frame: ' ; index printString ; (d currentFrame = frame ifTrue: [' *'] ifFalse: ['']) ; '\t' ; frame method printString.
304   showLocation ifTrue: [out ; ' @ ' ; frame method method definitionLocation].
305   out ; '\n'.
308 d@(Debugger traits) printBacktrace &fromFrame: frameIndex &limit: limit &stream: out &showLocation: showLocation
310   limit `defaultsTo: d maxBacktraceSize.
311   frameIndex `defaultsTo: ((d frames identityIndexOf: d currentFrame) - (limit // 2) max: 0).
312   out `defaultsTo: d printer.
313   showLocation `defaultsTo: True.
314   out ; 'Backtrace (method @ source): \n'.
315   frameIndex below: (d frames length min: frameIndex + limit) do:
316     [| :index | d printFrameLineAt: index &showLocation: showLocation].
319 d@(Debugger traits) frame: index
320 "Selects the frame at the given index."
322   d currentFrame := d frames at: index ifAbsent: [Nil]
325 d@(Debugger traits) top
327   d currentFrame := d frames first
330 d@(Debugger traits) bottom
332   d currentFrame := d frames last
335 d@(Debugger traits) up
337   (d frames before: d currentFrame)
338     ifNil: [error: 'You are at the top of the stack and cannot go up a frame']
339     ifNotNilDo: [| :frame | d currentFrame := frame]
342 d@(Debugger traits) down
344   (d frames after: d currentFrame)
345     ifNil: [error: 'You are at the bottom of the stack and cannot go down a frame']
346     ifNotNilDo: [| :frame | d currentFrame := frame]
349 d@(Debugger traits) currentFrameAt: n@(Integer traits)
351   d currentFrame isSaved ifTrue: [error: 'Cannot get value in saved frame'].
352   d currentFrame
353     ifNil: [^ (d printer ; 'No frame currently selected. Use "frame: N"')]
354     ifNotNilDo:
355       [| :frame |
356        frame method method `cacheAs: #method.
357        d printer ; 'register[' ; n printString ; ']@' ; (frame stackAtOffset: n) printName
358         ; '\t' ; (frame stackAtOffset: n) printString ; '\n'.
359        frame stackAtOffset: n]
362 d@(Debugger traits) currentFrameAt: n@(Integer traits) put: val
364   d currentFrame isSaved ifTrue: [error: 'Cannot set value in saved frame'].
365   d currentFrame stackAtOffset: n put: val
368 d@(Debugger traits) currentFrameAt: name
370   (d currentFrame stackOffsetForLocal: name)
371     ifNil: [error: 'Input or Local Variable not found']
372     ifNotNilDo: #(d currentFrameAt: _) `er
375 d@(Debugger traits) currentFrameAt: name put: val
377   (d currentFrame stackOffsetForLocal: name)
378     ifNil: [error: 'Input or Local Variable not found']
379     ifNotNilDo: #(d currentFrameAt: _ put: val) `er
382 d@(Debugger traits) printFrame
384   d currentFrame
385     ifNil: [^ (d printer ; 'No frame currently selected. Use "frame: N"')]
386     ifNotNilDo: [| :frame | frame describeOn: d printer]
389 ns@(Debugger Namespace traits) frame
390 [ns debugger printFrame].
391 ns@(Debugger Namespace traits) True
392 [True].
393 ns@(Debugger Namespace traits) False
394 [False].
395 ns@(Debugger Namespace traits) frame
396 [ns debugger currentFrame].
397 ns@(Debugger Namespace traits) frame: n
398 [ns debugger `>> [frame := n. printFrame]].
399 ns@(Debugger Namespace traits) top
400 [ns debugger `>> [top. printFrame]].
401 ns@(Debugger Namespace traits) up
402 [ns debugger `>> [up. printFrame]].
403 ns@(Debugger Namespace traits) down
404 [ns debugger `>> [down. printFrame]].
405 ns@(Debugger Namespace traits) arg: n
406 [ns debugger currentFrameAt: n].
407 ns@(Debugger Namespace traits) arg: n put: newVal
408 [ns debugger currentFrameAt: n put: newVal].
409 ns@(Debugger Namespace traits) frames &fromFrame: frameIndex &limit: n &showLocation: showLocation
410 [ns debugger printBacktrace &fromFrame: frameIndex &limit: n &showLocation: showLocation].
411 ns@(Debugger Namespace traits) backtrace &fromFrame: frameIndex &limit: n &showLocation: showLocation
412 [ns debugger printBacktrace &fromFrame: frameIndex &limit: n &showLocation: showLocation].
413 ns@(Debugger Namespace traits) bt &fromFrame: frameIndex &limit: n &showLocation: showLocation
414 [ns debugger printBacktrace &fromFrame: frameIndex &limit: n &showLocation: showLocation].
415 ns@(Debugger Namespace traits) restarts
416 [ns debugger describeRestarts].
417 ns@(Debugger Namespace traits) restart: index
418 [ns debugger signalRestartAt: index].
419 ns@(Debugger Namespace traits) : index
420 [ns debugger signalRestartAt: index].
421 ns@(Debugger Namespace traits) quit
422 [ns debugger quitFlag := True].
423 ns@(Debugger Namespace traits) return: value
424 [ns debugger return: value].
426 ns@(Debugger Namespace traits) help
428   ns debugger printer
429    ; 'The Debugger is a specialized REPL that evaluates expressions within the method currently being debugged. Certain messages are provided for changing the state of the debugger, as follows:\n'
430    ; ': index. (or restart: index.)\n\tSignal the restart at the given index\n'
431    ; 'backtrace (or "bt")\n\tPrint the backtrace\n'
432    ; 'frame\n\tPrint the current frame\n'
433    ; 'frame: N\n\tSet the current frame\n'
434    ; 'frame restart\n\tResume execution from the start of the current frame\n'
435    ; 'down\n\tMove down one frame (to the caller)\n'
436    ; 'up\n\tMove up one frame (in the callee direction)\n'
437    ; 'arg: N\n\tDisplays the frame\'s register at index N\n'
438    ; 'arg: N put:\n\tSets the frame\'s register at index N\n'
439    ; 'restarts\n\tPrint the currently available restarts\n'
440    ; 'help\n\tPrint this help listing\n'.
443 ns@(Debugger Namespace traits) ? [ns help].
445 ns@(Debugger Namespace traits) ? obj [ns helpFor: obj].
447 ns@(Debugger Namespace traits) didNotUnderstand: message at: position
449   position > 0
450    ifTrue: [resend]
451    ifFalse:
452      [| selector |
453       selector := message selector.
454       "Handle an accessor."
455       selector isUnarySelector
456         ifTrue: [^ [ns arg: selector]].
457       "Handle a mutator."
458       selector isKeywordSelector /\ [selector arity = 2]
459         ifTrue: [^ [ns arg: selector keywords first put: message arguments second]].
460       resend]
463 d@(Debugger traits) enter
464 "The main interactive debugger loop."
466   d printer `cache ; 'Debugging: ' ; d condition printName ; '\n'.
467   d refreshWorkspace.
468   d findRestarts.
469   d top.
470   "d printer ; d frames printString."
471   "d printBacktrace."
472   d describeRestarts.
473   d printer ; '\nEnter \'help.\' for instructions.\n'.
474   [
475   "Print out the restarting options with a prompt and numeric labels, and read
476 in the selected option number, returning it if valid."
477   d printer ; d prompt.
478   d printer flush.
479   [| expr result restartNumeric |
480    [expr := d parser next] on: Stream Exhaustion do:
481      [| :c | c stream == d resource reader ifTrue: [lobby exit: 1]].
482    result := expr evaluateIn: d namespace.
483    restartNumeric := (expr is: nodes Literal) /\ [result is: Integer].
484    "Exclude selections of restarts and other commands from printing."
485    restartNumeric \/
486      [(expr is: nodes Message)
487         /\ [#{#:. #restart:. #frame. #frame:. #restarts. #help. #bt. #backtrace} includes: expr selector]]
488       ifFalse:
489         [[result printOn: d printer]
490            on: Error do: [| :c | d printer ; '<Printing failed>'].
491          d printer newLine.
492          d printer flush].
493    restartNumeric ifTrue: [d signalRestartAt: result].
494    ] on: SeriousCondition
495     do: [| :c |
496       d printer ; '\nThe following condition was signaled from within the debugger:\n'.
497       c describeOn: d printer.
498       (c is: Stream Exhaustion) /\ [c stream resource handle = 0]
499         ifTrue: [d printer ; 'End of input, exiting...\n'. lobby exit: 1].
500       c exit].
501   d quitFlag] whileFalse.