ruby-debug 0.10.3 → 0.10.4

Sign up to get free protection for your applications and to get access to all the features.
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