ruby-debug 0.10.3 → 0.10.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. data/CHANGES +15 -0
  2. data/ChangeLog +461 -104
  3. data/Rakefile +69 -13
  4. data/VERSION +3 -0
  5. data/bin/rdebug +2 -1
  6. data/cli/ruby-debug.rb +8 -5
  7. data/cli/ruby-debug/command.rb +43 -4
  8. data/cli/ruby-debug/commands/breakpoints.rb +3 -1
  9. data/cli/ruby-debug/commands/catchpoint.rb +5 -3
  10. data/cli/ruby-debug/commands/{continue.RB.save → continue.RB} +0 -0
  11. data/cli/ruby-debug/commands/eval.rb +4 -1
  12. data/cli/ruby-debug/commands/frame.rb +11 -10
  13. data/cli/ruby-debug/commands/help.rb +5 -0
  14. data/cli/ruby-debug/commands/info.rb +6 -3
  15. data/cli/ruby-debug/commands/irb.rb +115 -12
  16. data/cli/ruby-debug/commands/kill.rb +50 -0
  17. data/cli/ruby-debug/commands/list.rb +3 -3
  18. data/cli/ruby-debug/commands/quit.rb +12 -6
  19. data/cli/ruby-debug/commands/raise.RB +41 -0
  20. data/cli/ruby-debug/commands/show.rb +26 -28
  21. data/cli/ruby-debug/helper.rb +5 -0
  22. data/cli/ruby-debug/interface.rb +53 -25
  23. data/cli/ruby-debug/processor.RB +484 -0
  24. data/cli/ruby-debug/processor.rb +140 -56
  25. data/rdbg.rb +0 -0
  26. data/runner.sh +7 -0
  27. data/test/base/base.rb +0 -0
  28. data/test/base/binding.rb +0 -0
  29. data/test/base/catchpoint.rb +0 -0
  30. data/test/base/reload_bug.rb +8 -0
  31. data/test/brkpt-class-bug.rb +8 -0
  32. data/test/cli/commands/unit/regexp.rb +11 -0
  33. data/test/config.yaml +8 -0
  34. data/test/data/annotate.cmd +1 -1
  35. data/test/data/annotate.right +3 -3
  36. data/test/data/break_bad.cmd +1 -1
  37. data/test/data/break_bad.right +1 -1
  38. data/test/data/break_loop_bug.right +1 -1
  39. data/test/data/breakpoints.cmd +1 -1
  40. data/test/data/breakpoints.right +3 -3
  41. data/test/data/brkpt-class-bug.cmd +9 -0
  42. data/test/data/brkpt-class-bug.right +18 -0
  43. data/test/data/catch.right +2 -0
  44. data/test/data/condition.cmd +1 -0
  45. data/test/data/condition.right +5 -3
  46. data/test/data/ctrl.right +2 -2
  47. data/test/data/display.right +1 -1
  48. data/test/data/emacs_basic.right +2 -2
  49. data/test/data/except-bug1.cmd +7 -0
  50. data/test/data/except-bug1.right +13 -0
  51. data/test/data/file-with-space.cmd +7 -0
  52. data/test/data/file-with-space.right +9 -0
  53. data/test/data/finish.right +1 -1
  54. data/test/data/frame.cmd +7 -1
  55. data/test/data/frame.right +12 -1
  56. data/test/data/info-var.right +1 -1
  57. data/test/data/info.cmd +1 -0
  58. data/test/data/info.right +21 -2
  59. data/test/data/list.right +1 -1
  60. data/test/data/method.right +1 -1
  61. data/test/data/post-mortem.right +5 -4
  62. data/test/data/save.right +1 -1
  63. data/test/data/setshow.cmd +0 -10
  64. data/test/data/setshow.right +0 -17
  65. data/test/dollar-0.rb +0 -0
  66. data/test/except-bug1.rb +4 -0
  67. data/test/file with space.rb +1 -0
  68. data/test/gcd-dbg.rb +0 -0
  69. data/test/helper.rb +7 -1
  70. data/test/pm-base.rb +0 -0
  71. data/test/pm.rb +1 -1
  72. data/test/raise.rb +0 -0
  73. data/test/rdebug-save.1 +7 -0
  74. data/test/tdebug.rb +2 -2
  75. data/test/test-annotate.rb +0 -0
  76. data/test/test-break-bad.rb +0 -0
  77. data/test/test-breakpoints.rb +0 -0
  78. data/test/test-brkpt-class-bug.rb +26 -0
  79. data/test/test-catch.rb +1 -1
  80. data/test/test-condition.rb +1 -1
  81. data/test/test-ctrl.rb +1 -0
  82. data/test/test-display.rb +0 -0
  83. data/test/test-dollar-0.rb +0 -0
  84. data/test/test-edit.rb +0 -0
  85. data/test/test-emacs-basic.rb +0 -0
  86. data/test/test-enable.rb +0 -0
  87. data/test/test-except-bug1.rb +31 -0
  88. data/test/test-file-with-space.rb +30 -0
  89. data/test/test-finish.rb +0 -0
  90. data/test/test-frame.rb +0 -0
  91. data/test/test-help.rb +0 -0
  92. data/test/test-hist.rb +0 -0
  93. data/test/test-info-thread.rb +0 -0
  94. data/test/test-info-var.rb +0 -0
  95. data/test/test-info.rb +0 -0
  96. data/test/test-init.rb +7 -1
  97. data/test/test-list.rb +0 -0
  98. data/test/test-method.rb +0 -0
  99. data/test/test-output.rb +0 -0
  100. data/test/test-pm.rb +0 -0
  101. data/test/test-quit.rb +0 -0
  102. data/test/test-raise.rb +1 -1
  103. data/test/test-save.rb +7 -1
  104. data/test/test-setshow.rb +0 -0
  105. data/test/test-source.rb +0 -0
  106. data/test/test-stepping.rb +0 -0
  107. data/test/test-trace.rb +0 -0
  108. metadata +211 -180
@@ -1,5 +1,10 @@
1
1
  module Debugger
2
2
 
3
+ def inside_emacs?
4
+ ENV['EMACS'] || ENV['INSIDE_EMACS']
5
+ end
6
+ module_function :inside_emacs?
7
+
3
8
  module ParseFunctions
4
9
  Position_regexp = '(?:(\d+)|(.+?)[:.#]([^.:\s]+))'
5
10
 
@@ -1,9 +1,22 @@
1
1
  module Debugger
2
- class Interface # :nodoc:
2
+
3
+ # An _Interface_ is the kind of input/output interaction that goes
4
+ # on between the user and the debugged program. It includes things
5
+ # like how one wants to show error messages, or read input. This is
6
+ # the base class. Subclasses inlcude Debugger::LocalInterface,
7
+ # Debugger::RemoteInterface and Debugger::ScriptInterface.
8
+ class Interface
3
9
  attr_writer :have_readline # true if Readline is available
4
10
 
5
11
  def initialize
6
- @have_readline = false
12
+ begin
13
+ require 'readline'
14
+ @have_readline = true
15
+ @history_save = true
16
+ rescue LoadError
17
+ @have_readline = false
18
+ @history_save = false
19
+ end
7
20
  end
8
21
 
9
22
  # Common routine for reporting debugger error messages.
@@ -28,9 +41,16 @@ module Debugger
28
41
  print afmt(msg)
29
42
  end
30
43
 
44
+ # Things we do before terminating.
45
+ def finalize
46
+ end
47
+
31
48
  end
32
49
 
33
- class LocalInterface < Interface # :nodoc:
50
+ # A _LocalInterface_ is the kind of I/O interactive performed when
51
+ # the user interface is in the same process as the debugged program.
52
+ # Compare with Debugger::RemoteInterface.
53
+ class LocalInterface < Interface
34
54
  attr_accessor :command_queue
35
55
  attr_accessor :histfile
36
56
  attr_accessor :history_save
@@ -43,19 +63,20 @@ module Debugger
43
63
  def initialize()
44
64
  super
45
65
  @command_queue = []
46
- @have_readline = false
47
- @history_save = true
48
- # take gdb's default
49
- @history_length = ENV["HISTSIZE"] ? ENV["HISTSIZE"].to_i : 256
50
- @histfile = File.join(ENV["HOME"]||ENV["HOMEPATH"]||".",
51
- FILE_HISTORY)
52
- open(@histfile, 'r') do |file|
53
- file.each do |line|
54
- line.chomp!
55
- Readline::HISTORY << line
56
- end
57
- end if File.exist?(@histfile)
58
66
  @restart_file = nil
67
+
68
+ if @have_readline
69
+ # take gdb's default
70
+ @history_length = ENV['HISTSIZE'] ? ENV['HISTSIZE'].to_i : 256
71
+ @histfile = File.join(ENV['HOME']||ENV['HOMEPATH']||'.',
72
+ FILE_HISTORY)
73
+ open(@histfile, 'r') do |file|
74
+ file.each do |line|
75
+ line.chomp!
76
+ Readline::HISTORY << line
77
+ end
78
+ end if File.exist?(@histfile)
79
+ end
59
80
  end
60
81
 
61
82
  def read_command(prompt)
@@ -94,7 +115,7 @@ module Debugger
94
115
  @have_readline = true
95
116
  define_method(:save_history) do
96
117
  iface = self.handler.interface
97
- iface.histfile ||= File.join(ENV["HOME"]||ENV["HOMEPATH"]||".",
118
+ iface.histfile ||= File.join(ENV['HOME']||ENV['HOMEPATH']||'.',
98
119
  FILE_HISTORY)
99
120
  open(iface.histfile, 'w') do |file|
100
121
  Readline::HISTORY.to_a.last(iface.history_length).each do |line|
@@ -125,7 +146,11 @@ module Debugger
125
146
  end
126
147
  end
127
148
 
128
- class RemoteInterface < Interface # :nodoc:
149
+ # A _RemoteInterface_ is the kind of I/O interactive performed when
150
+ # the user interface is in a different process (and possibly
151
+ # different computer) than the debugged program. Compare with
152
+ # Debugger::LocalInterface.
153
+ class RemoteInterface < Interface
129
154
  attr_accessor :command_queue
130
155
  attr_accessor :histfile
131
156
  attr_accessor :history_save
@@ -157,9 +182,6 @@ module Debugger
157
182
  send_command "CONFIRM #{prompt}"
158
183
  end
159
184
 
160
- def finalize
161
- end
162
-
163
185
  def read_command(prompt)
164
186
  send_command "PROMPT #{prompt}"
165
187
  end
@@ -182,7 +204,11 @@ module Debugger
182
204
  end
183
205
  end
184
206
 
185
- class ScriptInterface < Interface # :nodoc:
207
+ # A _ScriptInterface_ is used when we are reading debugger commands
208
+ # from a command-file rather than an interactive user. Command files
209
+ # appear in a users initialization script (e.g. .rdebugrc) and appear
210
+ # when running the debugger command _source_ (Debugger::SourceCommand).
211
+ class ScriptInterface < Interface
186
212
  attr_accessor :command_queue
187
213
  attr_accessor :histfile
188
214
  attr_accessor :history_save
@@ -198,10 +224,7 @@ module Debugger
198
224
  @history_length = 256 # take gdb default
199
225
  @histfile = ''
200
226
  end
201
-
202
- def finalize
203
- end
204
-
227
+
205
228
  def read_command(prompt)
206
229
  while result = @file.gets
207
230
  puts "# #{result}" if @verbose
@@ -213,10 +236,15 @@ module Debugger
213
236
  result.chomp!
214
237
  end
215
238
 
239
+ # Do we have ReadLine support? When running an debugger command
240
+ # script, we are not interactive so we just return _false_.
216
241
  def readline_support?
217
242
  false
218
243
  end
219
244
 
245
+ # _confirm_ is called before performing a dangerous action. In
246
+ # running a debugger script, we don't want to prompt, so we'll pretend
247
+ # the user has unconditionally said "yes" and return String "y".
220
248
  def confirm(prompt)
221
249
  'y'
222
250
  end
@@ -0,0 +1,484 @@
1
+ require 'ruby-debug/interface'
2
+ require 'ruby-debug/command'
3
+
4
+ module Debugger
5
+
6
+ # Should this be a mixin?
7
+ class Processor # :nodoc
8
+ attr_accessor :interface
9
+ attr_reader :processor
10
+ attr_reader :commands
11
+
12
+ # Format msg with gdb-style annotation header
13
+ def afmt(msg, newline="\n")
14
+ "\032\032#{msg}#{newline}"
15
+ end
16
+
17
+ def aprint(msg)
18
+ print afmt(msg) if Debugger.annotate.to_i > 2
19
+ end
20
+
21
+ # FIXME: use delegate?
22
+ def errmsg(*args)
23
+ @interface.errmsg(*args)
24
+ end
25
+
26
+ # Callers of this routine should make sure to use comma to
27
+ # separate format argments rather than %. Otherwise it seems that
28
+ # if the string you want to print has format specifier, which
29
+ # could happen if you are trying to show say a source-code line
30
+ # with "puts" or "print" in it, this print routine will give an
31
+ # error saying it is looking for more arguments.
32
+ def print(*args)
33
+ @interface.print(*args)
34
+ end
35
+
36
+ end
37
+
38
+ class CommandProcessor < Processor # :nodoc:
39
+ attr_reader :display
40
+
41
+ # FIXME: get from Command regexp method.
42
+ @@Show_breakpoints_postcmd = [
43
+ /^\s*b(?:reak)?/,
44
+ /^\s* cond(?:ition)? (?:\s+(\d+)\s*(.*))?$/ix,
45
+ /^\s*del(?:ete)?(?:\s+(.*))?$/ix,
46
+ /^\s* dis(?:able)? (?:\s+(.*))?$/ix,
47
+ /^\s* en(?:able)? (?:\s+(.*))?$/ix,
48
+ # "tbreak", "clear",
49
+ ]
50
+ @@Show_annotations_run = [
51
+ /^\s*c(?:ont(?:inue)?)?(?:\s+(.*))?$/,
52
+ /^\s*fin(?:ish)?$/,
53
+ /^\s*n(?:ext)?([+-])?(?:\s+(.*))?$/,
54
+ /^\s*s(?:tep)?([+-])?(?:\s+(.*))?$/
55
+ ]
56
+
57
+ @@Show_annotations_postcmd = [
58
+ /^\s* down (?:\s+(.*))? .*$/x,
59
+ /^\s* f(?:rame)? (?:\s+ (.*))? \s*$/x,
60
+ /^\s* u(?:p)? (?:\s+(.*))?$/x
61
+ ]
62
+
63
+ def initialize(interface = LocalInterface.new)
64
+ @interface = interface
65
+ @commands = []
66
+ @display = []
67
+
68
+ @mutex = Mutex.new
69
+ @last_cmd = nil
70
+ @last_file = nil # Filename the last time we stopped
71
+ @last_line = nil # line number the last time we stopped
72
+ @debugger_breakpoints_were_empty = false # Show breakpoints 1st time
73
+ @debugger_displays_were_empty = true # No display 1st time
74
+ @debugger_context_was_dead = true # Assume we haven't started.
75
+ end
76
+
77
+ def interface=(interface)
78
+ @mutex.synchronize do
79
+ @interface.close if @interface
80
+ @interface = interface
81
+ end
82
+ end
83
+
84
+ require 'pathname' # For cleanpath
85
+
86
+ # Regularize file name.
87
+ # This is also used as a common funnel place if basename is
88
+ # desired or if we are working remotely and want to change the
89
+ # basename. Or we are eliding filenames.
90
+ def self.canonic_file(filename)
91
+ # For now we want resolved filenames
92
+ if Command.settings[:basename]
93
+ File.basename(filename)
94
+ else
95
+ # Cache this?
96
+ Pathname.new(filename).cleanpath.to_s
97
+ end
98
+ end
99
+
100
+ def self.print_location_and_text(file, line)
101
+ file_line = "%s:%s\n%s" % [canonic_file(file), line,
102
+ Debugger.line_at(file, line)]
103
+ # FIXME: use annotations routines
104
+ if Debugger.annotate.to_i > 2
105
+ file_line = "\032\032source #{file_line}"
106
+ elsif ENV['EMACS']
107
+ file_line = "\032\032#{file_line}"
108
+ end
109
+ print file_line
110
+ end
111
+
112
+ def self.protect(mname)
113
+ alias_method "__#{mname}", mname
114
+ module_eval %{
115
+ def #{mname}(*args)
116
+ @mutex.synchronize do
117
+ return unless @interface
118
+ __#{mname}(*args)
119
+ end
120
+ rescue IOError, Errno::EPIPE
121
+ self.interface = nil
122
+ rescue SignalException
123
+ raise
124
+ rescue Exception => e
125
+ unless
126
+ print "INTERNAL ERROR!!! #\{$!\}\n" rescue nil
127
+ print $!.backtrace.map{|l| "\t#\{l\}"}.join("\n") rescue nil
128
+ end
129
+ }
130
+ end
131
+
132
+ def at_breakpoint(context, breakpoint)
133
+ aprint 'stopped' if Debugger.annotate.to_i > 2
134
+ n = Debugger.breakpoints.index(breakpoint) + 1
135
+ file = CommandProcessor.canonic_file(breakpoint.source)
136
+ line = breakpoint.pos
137
+ if Debugger.annotate.to_i > 2
138
+ print afmt("source #{file}:#{line}")
139
+ end
140
+ print "Breakpoint %d at %s:%s\n", n, file, line
141
+ end
142
+ protect :at_breakpoint
143
+
144
+ def at_catchpoint(context, excpt)
145
+ aprint 'stopped' if Debugger.annotate.to_i > 2
146
+ file = CommandProcessor.canonic_file(context.frame_file(0))
147
+ line = context.frame_line(0)
148
+ print afmt("%s:%d" % [file, line]) if ENV['EMACS']
149
+ print "Catchpoint at %s:%d: `%s' (%s)\n", file, line, excpt, excpt.class
150
+ fs = context.stack_size
151
+ tb = caller(0)[-fs..-1]
152
+ if tb
153
+ for i in tb
154
+ print "\tfrom %s\n", i
155
+ end
156
+ end
157
+ end
158
+ protect :at_catchpoint
159
+
160
+ def at_tracing(context, file, line)
161
+ return if defined?(Debugger::RDEBUG_FILE) &&
162
+ Debugger::RDEBUG_FILE == file # Don't trace ourself
163
+ @last_file = CommandProcessor.canonic_file(file)
164
+ file = CommandProcessor.canonic_file(file)
165
+ unless file == @last_file and @last_line == line and
166
+ Command.settings[:tracing_plus]
167
+ print "Tracing(%d):%s:%s %s",
168
+ context.thnum, file, line, Debugger.line_at(file, line)
169
+ @last_file = file
170
+ @last_line = line
171
+ end
172
+ always_run(context, file, line, 2)
173
+ end
174
+ protect :at_tracing
175
+
176
+ def at_line(context, file, line)
177
+ process_commands(context, file, line)
178
+ end
179
+ protect :at_line
180
+
181
+ def at_return(context, file, line)
182
+ context.stop_frame = -1
183
+ process_commands(context, file, line)
184
+ end
185
+
186
+ def one_cmd(commands, context, input)
187
+ if cmd = commands.find{ |c| c.match(input) }
188
+ if context.dead? && cmd.class.need_context
189
+ p cmd
190
+ print "Command is unavailable\n"
191
+ else
192
+ cmd.execute
193
+ end
194
+ else
195
+ unknown_cmd = commands.find{|cmd| cmd.class.unknown }
196
+ if unknown_cmd
197
+ unknown_cmd.execute
198
+ else
199
+ errmsg "Unknown command: \"#{input}\". Try \"help\".\n"
200
+ end
201
+ end
202
+ end
203
+
204
+ private
205
+
206
+ # The prompt shown before reading a command.
207
+ def prompt(context)
208
+ p = '(rdb:%s) ' % (context.dead? ? 'post-mortem' : context.thnum)
209
+ p = afmt("pre-prompt")+p+"\n"+afmt("prompt") if
210
+ Debugger.annotate.to_i > 2
211
+ return p
212
+ end
213
+
214
+ # Run these commands, for example display commands or possibly
215
+ # the list or irb in an "autolist" or "autoirb".
216
+ # We return a list of commands that are acceptable to run bound
217
+ # to the current state.
218
+ def always_run(context, file, line, run_level)
219
+ event_cmds = Command.commands.select{|cmd| cmd.event }
220
+
221
+ # Remove some commands in post-mortem
222
+ event_cmds = event_cmds.find_all do |cmd|
223
+ cmd.allow_in_post_mortem
224
+ end if context.dead?
225
+
226
+ state = State.new(self) do |s|
227
+ s.context = context
228
+ s.file = file
229
+ s.line = line
230
+ s.binding = context.frame_binding(0)
231
+ s.display = display
232
+ s.interface = interface
233
+ s.commands = event_cmds
234
+ end
235
+ @interface.state = state if @interface.respond_to?('state=')
236
+
237
+ # Bind commands to the current state.
238
+ commands = event_cmds.map{|cmd| cmd.new(state)}
239
+
240
+ commands.select do |cmd|
241
+ cmd.class.always_run >= run_level
242
+ end.each {|cmd| cmd.execute}
243
+ return state, commands
244
+ end
245
+
246
+ # Handle debugger commands
247
+ def process_commands(context, file, line)
248
+ state, @commands = always_run(context, file, line, 1)
249
+ $rdebug_state = state if Command.settings[:debuggertesting]
250
+ splitter = lambda do |str|
251
+ str.split(/;/).inject([]) do |m, v|
252
+ if m.empty?
253
+ m << v
254
+ else
255
+ if m.last[-1] == ?\\
256
+ m.last[-1,1] = ''
257
+ m.last << ';' << v
258
+ else
259
+ m << v
260
+ end
261
+ end
262
+ m
263
+ end
264
+ end
265
+
266
+ preloop(@commands, context)
267
+ CommandProcessor.print_location_and_text(file, line)
268
+ while !state.proceed?
269
+ input = if @interface.command_queue.empty?
270
+ @interface.read_command(prompt(context))
271
+ else
272
+ @interface.command_queue.shift
273
+ end
274
+ break unless input
275
+ catch(:debug_error) do
276
+ if input == ""
277
+ next unless @last_cmd
278
+ input = @last_cmd
279
+ else
280
+ @last_cmd = input
281
+ end
282
+ splitter[input].each do |cmd|
283
+ one_cmd(@commands, context, cmd)
284
+ postcmd(@commands, context, cmd)
285
+ end
286
+ end
287
+ end
288
+ postloop(@commands, context)
289
+ exception = state.exception
290
+ @exception = nil
291
+ puts "raising exception #{state.exception}"
292
+ raise exception if exception
293
+ end # process_commands
294
+
295
+ def preloop(commands, context)
296
+ aprint('stopped') if Debugger.annotate.to_i > 2
297
+ if context.dead?
298
+ unless @debugger_context_was_dead
299
+ if Debugger.annotate.to_i > 2
300
+ aprint('exited')
301
+ print "The program finished.\n"
302
+ end
303
+ @debugger_context_was_dead = true
304
+ end
305
+ end
306
+
307
+ if Debugger.annotate.to_i > 2
308
+ # if we are here, the stack frames have changed outside the
309
+ # command loop (e.g. after a "continue" command), so we show
310
+ # the annotations again
311
+ breakpoint_annotations(commands, context)
312
+ display_annotations(commands, context)
313
+ annotation('stack', commands, context, "where")
314
+ annotation('variables', commands, context, "info variables") unless
315
+ context.dead?
316
+ end
317
+ end
318
+
319
+ def postcmd(commands, context, cmd)
320
+ if Debugger.annotate.to_i > 0
321
+ cmd = @last_cmd unless cmd
322
+ breakpoint_annotations(commands, context) if
323
+ @@Show_breakpoints_postcmd.find{|pat| cmd =~ pat}
324
+ display_annotations(commands, context)
325
+ if @@Show_annotations_postcmd.find{|pat| cmd =~ pat}
326
+ annotation('stack', commands, context, "where") if
327
+ context.stack_size > 0
328
+ annotation('variables', commands, context, "info variables") unless
329
+ context.dead?
330
+ end
331
+ if not context.dead? and @@Show_annotations_run.find{|pat| cmd =~ pat}
332
+ aprint 'starting' if Debugger.annotate.to_i > 2
333
+
334
+ @debugger_context_was_dead = false
335
+ end
336
+ end
337
+ end
338
+
339
+ def postloop(commands, context)
340
+ end
341
+
342
+ def annotation(label, commands, context, cmd)
343
+ print afmt(label)
344
+ one_cmd(commands, context, cmd)
345
+ ### FIXME ANNOTATE: the following line should be deleted
346
+ print "\032\032\n"
347
+ end
348
+
349
+ def breakpoint_annotations(commands, context)
350
+ unless Debugger.breakpoints.empty? and @debugger_breakpoints_were_empty
351
+ annotation('breakpoints', commands, context, "info breakpoints")
352
+ @debugger_breakpoints_were_empty = Debugger.breakpoints.empty?
353
+ end
354
+ end
355
+
356
+ def display_annotations(commands, context)
357
+ return if display.empty?
358
+ # have_display = display.find{|d| d[0]}
359
+ # return unless have_display and @debugger_displays_were_empty
360
+ # @debugger_displays_were_empty = have_display
361
+ annotation('display', commands, context, "display")
362
+ end
363
+
364
+ class State # :nodoc:
365
+ attr_accessor :context, :file, :line, :binding
366
+ attr_accessor :frame_pos, :previous_line, :display
367
+ attr_accessor :interface, :commands, :processor
368
+ attr_accessor :exception
369
+
370
+ def initialize(processor=nil)
371
+ super()
372
+ @frame_pos = 0
373
+ @previous_line = nil
374
+ @proceed = false
375
+ @processor = processor
376
+ yield self
377
+ end
378
+
379
+ # FIXME: use delegate?
380
+ def errmsg(*args)
381
+ @interface.errmsg(*args)
382
+ end
383
+
384
+ def print(*args)
385
+ @interface.print(*args)
386
+ end
387
+
388
+ def confirm(*args)
389
+ @interface.confirm(*args)
390
+ end
391
+
392
+ def proceed?
393
+ @proceed
394
+ end
395
+
396
+ def proceed
397
+ @proceed = true
398
+ end
399
+ end
400
+ end
401
+
402
+ class ControlCommandProcessor < Processor # :nodoc:
403
+ def initialize(interface)
404
+ super()
405
+ @interface = interface
406
+ @debugger_context_was_dead = true # Assume we haven't started.
407
+ end
408
+
409
+ def process_commands(verbose=false)
410
+ control_cmds = Command.commands.select do |cmd|
411
+ cmd.allow_in_control
412
+ end
413
+ state = State.new(@interface, control_cmds)
414
+ @commands = control_cmds.map{|cmd| cmd.new(state) }
415
+
416
+ unless @debugger_context_was_dead
417
+ if Debugger.annotate.to_i > 2
418
+ aprint 'exited'
419
+ print "The program finished.\n"
420
+ end
421
+ @debugger_context_was_dead = true
422
+ end
423
+
424
+ while input = @interface.read_command(prompt(nil))
425
+ print "+#{input}" if verbose
426
+ catch(:debug_error) do
427
+ if cmd = @commands.find{|c| c.match(input) }
428
+ cmd.execute
429
+ else
430
+ errmsg "Unknown command\n"
431
+ end
432
+ end
433
+ end
434
+ rescue IOError, Errno::EPIPE
435
+ rescue Exception
436
+ print "INTERNAL ERROR!!! #{$!}\n" rescue nil
437
+ print $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
438
+ ensure
439
+ @interface.close
440
+ end
441
+
442
+ # The prompt shown before reading a command.
443
+ # Note: have an unused 'context' parameter to match the local interface.
444
+ def prompt(context)
445
+ p = '(rdb:ctrl) '
446
+ p = afmt("pre-prompt")+p+"\n"+afmt("prompt") if
447
+ Debugger.annotate.to_i > 2
448
+ return p
449
+ end
450
+
451
+ class State # :nodoc:
452
+ attr_reader :commands, :interface
453
+
454
+ def initialize(interface, commands)
455
+ @interface = interface
456
+ @commands = commands
457
+ end
458
+
459
+ def proceed
460
+ end
461
+
462
+ def errmsg(*args)
463
+ @interface.print(*args)
464
+ end
465
+
466
+ def print(*args)
467
+ @interface.print(*args)
468
+ end
469
+
470
+ def confirm(*args)
471
+ 'y'
472
+ end
473
+
474
+ def context
475
+ nil
476
+ end
477
+
478
+ def file
479
+ errmsg "No filename given.\n"
480
+ throw :debug_error
481
+ end
482
+ end # State
483
+ end
484
+ end