byebug 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. data/.gitignore +10 -0
  2. data/.travis.yml +8 -0
  3. data/AUTHORS +10 -0
  4. data/CHANGELOG.md +2 -0
  5. data/CONTRIBUTING.md +1 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +20 -0
  8. data/README.md +5 -0
  9. data/Rakefile +28 -0
  10. data/bin/byebug +395 -0
  11. data/byebug.gemspec +29 -0
  12. data/doc/hanoi.rb +35 -0
  13. data/doc/primes.rb +28 -0
  14. data/doc/rdebug-emacs.texi +1030 -0
  15. data/doc/test-tri2.rb +18 -0
  16. data/doc/tri3.rb +8 -0
  17. data/doc/triangle.rb +12 -0
  18. data/ext/byebug/breakpoint.c +476 -0
  19. data/ext/byebug/byebug.c +512 -0
  20. data/ext/byebug/byebug.h +131 -0
  21. data/ext/byebug/context.c +424 -0
  22. data/ext/byebug/extconf.rb +21 -0
  23. data/ext/byebug/locker.c +53 -0
  24. data/lib/byebug.rb +404 -0
  25. data/lib/byebug/command.rb +232 -0
  26. data/lib/byebug/commands/breakpoints.rb +153 -0
  27. data/lib/byebug/commands/catchpoint.rb +56 -0
  28. data/lib/byebug/commands/condition.rb +49 -0
  29. data/lib/byebug/commands/continue.rb +38 -0
  30. data/lib/byebug/commands/control.rb +110 -0
  31. data/lib/byebug/commands/display.rb +122 -0
  32. data/lib/byebug/commands/edit.rb +48 -0
  33. data/lib/byebug/commands/enable.rb +202 -0
  34. data/lib/byebug/commands/eval.rb +176 -0
  35. data/lib/byebug/commands/finish.rb +43 -0
  36. data/lib/byebug/commands/frame.rb +303 -0
  37. data/lib/byebug/commands/help.rb +56 -0
  38. data/lib/byebug/commands/info.rb +462 -0
  39. data/lib/byebug/commands/irb.rb +123 -0
  40. data/lib/byebug/commands/jump.rb +66 -0
  41. data/lib/byebug/commands/kill.rb +51 -0
  42. data/lib/byebug/commands/list.rb +94 -0
  43. data/lib/byebug/commands/method.rb +84 -0
  44. data/lib/byebug/commands/quit.rb +39 -0
  45. data/lib/byebug/commands/reload.rb +40 -0
  46. data/lib/byebug/commands/save.rb +90 -0
  47. data/lib/byebug/commands/set.rb +210 -0
  48. data/lib/byebug/commands/show.rb +246 -0
  49. data/lib/byebug/commands/skip.rb +35 -0
  50. data/lib/byebug/commands/source.rb +36 -0
  51. data/lib/byebug/commands/stepping.rb +83 -0
  52. data/lib/byebug/commands/threads.rb +189 -0
  53. data/lib/byebug/commands/tmate.rb +36 -0
  54. data/lib/byebug/commands/trace.rb +56 -0
  55. data/lib/byebug/commands/variables.rb +199 -0
  56. data/lib/byebug/context.rb +58 -0
  57. data/lib/byebug/helper.rb +69 -0
  58. data/lib/byebug/interface.rb +223 -0
  59. data/lib/byebug/processor.rb +468 -0
  60. data/lib/byebug/version.rb +3 -0
  61. data/man/rdebug.1 +241 -0
  62. data/test/breakpoints_test.rb +357 -0
  63. data/test/conditions_test.rb +77 -0
  64. data/test/continue_test.rb +44 -0
  65. data/test/display_test.rb +141 -0
  66. data/test/edit_test.rb +56 -0
  67. data/test/eval_test.rb +92 -0
  68. data/test/examples/breakpoint1.rb +15 -0
  69. data/test/examples/breakpoint2.rb +7 -0
  70. data/test/examples/conditions.rb +4 -0
  71. data/test/examples/continue.rb +4 -0
  72. data/test/examples/display.rb +5 -0
  73. data/test/examples/edit.rb +3 -0
  74. data/test/examples/edit2.rb +3 -0
  75. data/test/examples/eval.rb +4 -0
  76. data/test/examples/finish.rb +20 -0
  77. data/test/examples/frame.rb +20 -0
  78. data/test/examples/frame_threads.rb +31 -0
  79. data/test/examples/help.rb +2 -0
  80. data/test/examples/info.rb +38 -0
  81. data/test/examples/info2.rb +3 -0
  82. data/test/examples/info_threads.rb +48 -0
  83. data/test/examples/irb.rb +6 -0
  84. data/test/examples/jump.rb +14 -0
  85. data/test/examples/kill.rb +2 -0
  86. data/test/examples/list.rb +12 -0
  87. data/test/examples/method.rb +15 -0
  88. data/test/examples/post_mortem.rb +19 -0
  89. data/test/examples/quit.rb +2 -0
  90. data/test/examples/reload.rb +6 -0
  91. data/test/examples/restart.rb +6 -0
  92. data/test/examples/save.rb +3 -0
  93. data/test/examples/set.rb +3 -0
  94. data/test/examples/set_annotate.rb +12 -0
  95. data/test/examples/settings.rb +1 -0
  96. data/test/examples/show.rb +2 -0
  97. data/test/examples/source.rb +3 -0
  98. data/test/examples/stepping.rb +21 -0
  99. data/test/examples/thread.rb +32 -0
  100. data/test/examples/tmate.rb +10 -0
  101. data/test/examples/trace.rb +7 -0
  102. data/test/examples/trace_threads.rb +20 -0
  103. data/test/examples/variables.rb +26 -0
  104. data/test/finish_test.rb +48 -0
  105. data/test/frame_test.rb +143 -0
  106. data/test/help_test.rb +50 -0
  107. data/test/info_test.rb +313 -0
  108. data/test/irb_test.rb +81 -0
  109. data/test/jump_test.rb +70 -0
  110. data/test/kill_test.rb +48 -0
  111. data/test/list_test.rb +145 -0
  112. data/test/method_test.rb +70 -0
  113. data/test/post_mortem_test.rb +27 -0
  114. data/test/quit_test.rb +56 -0
  115. data/test/reload_test.rb +44 -0
  116. data/test/restart_test.rb +164 -0
  117. data/test/save_test.rb +92 -0
  118. data/test/set_test.rb +177 -0
  119. data/test/show_test.rb +293 -0
  120. data/test/source_test.rb +45 -0
  121. data/test/stepping_test.rb +130 -0
  122. data/test/support/breakpoint.rb +13 -0
  123. data/test/support/context.rb +14 -0
  124. data/test/support/matchers.rb +67 -0
  125. data/test/support/mocha_extensions.rb +72 -0
  126. data/test/support/processor.rb +7 -0
  127. data/test/support/test_dsl.rb +206 -0
  128. data/test/support/test_interface.rb +68 -0
  129. data/test/test_helper.rb +10 -0
  130. data/test/tmate_test.rb +44 -0
  131. data/test/trace_test.rb +159 -0
  132. data/test/variables_test.rb +119 -0
  133. metadata +265 -0
@@ -0,0 +1,58 @@
1
+ module Byebug
2
+
3
+ class << self
4
+
5
+ # interface modules provide +handler+ object
6
+ attr_accessor :handler
7
+
8
+ end
9
+
10
+ class Context
11
+
12
+ def frame_locals(frame_no=0)
13
+ result = {}
14
+ binding = frame_binding(frame_no)
15
+ locals = eval("local_variables", binding)
16
+ locals.each {|local| result[local.to_s] = eval(local.to_s, binding)}
17
+ result
18
+ end
19
+
20
+ def frame_class(frame_no=0)
21
+ frame_self(frame_no).class
22
+ end
23
+
24
+ def frame_args_info(frame_no=0)
25
+ nil
26
+ end
27
+
28
+ def interrupt
29
+ self.stop_next = 1
30
+ end
31
+
32
+ def handler
33
+ Byebug.handler or raise 'No interface loaded'
34
+ end
35
+
36
+ def at_breakpoint(breakpoint)
37
+ handler.at_breakpoint(self, breakpoint)
38
+ end
39
+
40
+ def at_catchpoint(excpt)
41
+ handler.at_catchpoint(self, excpt)
42
+ end
43
+
44
+ def at_tracing(file, line)
45
+ @tracing_started = File.identical?(file, Byebug::PROG_SCRIPT)
46
+ handler.at_tracing(self, file, line) if @tracing_started
47
+ end
48
+
49
+ def at_line(file, line)
50
+ handler.at_line(self, file, line)
51
+ end
52
+
53
+ def at_return(file, line)
54
+ handler.at_return(self, file, line)
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,69 @@
1
+ module Byebug
2
+
3
+ module ParseFunctions
4
+ Position_regexp = '(?:(\d+)|(.+?)[:.#]([^.:\s]+))'
5
+
6
+ # Parse 'str' of command 'cmd' as an integer between
7
+ # min and max. If either min or max is nil, that
8
+ # value has no bound.
9
+ def get_int(str, cmd, min=nil, max=nil, default=1)
10
+ return default unless str
11
+ begin
12
+ int = Integer(str)
13
+ if min and int < min
14
+ print "%s argument '%s' needs to at least %s.\n" % [cmd, str, min]
15
+ return nil
16
+ elsif max and int > max
17
+ print "%s argument '%s' needs to at most %s.\n" % [cmd, str, max]
18
+ return nil
19
+ end
20
+ return int
21
+ rescue
22
+ print "%s argument '%s' needs to be a number.\n" % [cmd, str]
23
+ return nil
24
+ end
25
+ end
26
+
27
+ # Return true if arg is 'on' or 1 and false arg is 'off' or 0.
28
+ # Any other value raises RuntimeError.
29
+ def get_onoff(arg, default=nil, print_error=true)
30
+ if arg.nil? or arg == ''
31
+ if default.nil?
32
+ if print_error
33
+ print "Expecting 'on', 1, 'off', or 0. Got nothing.\n"
34
+ raise RuntimeError
35
+ end
36
+ return default
37
+ end
38
+ end
39
+ case arg.downcase
40
+ when '1', 'on'
41
+ return true
42
+ when '0', 'off'
43
+ return false
44
+ else
45
+ if print_error
46
+ print "Expecting 'on', 1, 'off', or 0. Got: %s.\n" % arg.to_s
47
+ raise RuntimeError
48
+ end
49
+ end
50
+ end
51
+
52
+ # Return 'on' or 'off' for supplied parameter. The parmeter should
53
+ # be true, false or nil.
54
+ def show_onoff(bool)
55
+ if not [TrueClass, FalseClass, NilClass].member?(bool.class)
56
+ return "??"
57
+ end
58
+ return bool ? 'on' : 'off'
59
+ end
60
+
61
+ # Return true if code is syntactically correct for Ruby.
62
+ def syntax_valid?(code)
63
+ eval("BEGIN {return true}\n#{code}", nil, "", 0)
64
+ rescue Exception
65
+ false
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,223 @@
1
+ module Byebug
2
+
3
+ class Interface # :nodoc:
4
+ attr_writer :have_readline
5
+
6
+ def initialize
7
+ @have_readline = false
8
+ end
9
+
10
+ # Common routine for reporting byebug error messages.
11
+ # Derived classed may want to override this to capture output.
12
+ def errmsg(*args)
13
+ if Byebug.annotate.to_i > 2
14
+ aprint 'error-begin'
15
+ print(*args)
16
+ aprint ''
17
+ else
18
+ print '*** '
19
+ print(*args)
20
+ end
21
+ end
22
+
23
+ # Format msg with gdb-style annotation header
24
+ def afmt(msg, newline="\n")
25
+ "\032\032#{msg}#{newline}"
26
+ end
27
+
28
+ def aprint(msg)
29
+ print afmt(msg)
30
+ end
31
+
32
+ end
33
+
34
+ class LocalInterface < Interface # :nodoc:
35
+ attr_accessor :command_queue, :history_length, :history_save, :histfile
36
+ attr_accessor :restart_file
37
+
38
+ unless defined?(FILE_HISTORY)
39
+ FILE_HISTORY = ".rdebug_hist"
40
+ end
41
+ def initialize()
42
+ super
43
+ @command_queue = []
44
+ @have_readline = false
45
+ @history_save = true
46
+ # take gdb's default
47
+ @history_length = ENV["HISTSIZE"] ? ENV["HISTSIZE"].to_i : 256
48
+ @histfile = File.join(ENV["HOME"]||ENV["HOMEPATH"]||".",
49
+ FILE_HISTORY)
50
+ open(@histfile, 'r') do |file|
51
+ file.each do |line|
52
+ line.chomp!
53
+ Readline::HISTORY << line
54
+ end
55
+ end if File.exist?(@histfile)
56
+ @restart_file = nil
57
+ end
58
+
59
+ def read_command(prompt)
60
+ readline(prompt, true)
61
+ end
62
+
63
+ def confirm(prompt)
64
+ readline(prompt, false)
65
+ end
66
+
67
+ def print(*args)
68
+ STDOUT.printf(*args)
69
+ end
70
+
71
+ def close
72
+ end
73
+
74
+ # Things to do before quitting
75
+ def finalize
76
+ if Byebug.method_defined?("annotate") and Byebug.annotate.to_i > 2
77
+ print "\032\032exited\n\n"
78
+ end
79
+ if Byebug.respond_to?(:save_history)
80
+ Byebug.save_history
81
+ end
82
+ end
83
+
84
+ def readline_support?
85
+ @have_readline
86
+ end
87
+
88
+ private
89
+
90
+ begin
91
+ require 'readline'
92
+ class << Byebug
93
+ @have_readline = true
94
+ define_method(:save_history) do
95
+ iface = self.handler.interface
96
+ iface.histfile ||= File.join(ENV["HOME"]||ENV["HOMEPATH"]||".",
97
+ FILE_HISTORY)
98
+ open(iface.histfile, 'w') do |file|
99
+ Readline::HISTORY.to_a.last(iface.history_length).each do |line|
100
+ file.puts line unless line.strip.empty?
101
+ end if defined?(iface.history_save) and iface.history_save
102
+ end rescue nil
103
+ end
104
+ public :save_history
105
+ end
106
+
107
+ def readline(prompt, hist)
108
+ Readline::readline(prompt, hist)
109
+ end
110
+ rescue LoadError
111
+ def readline(prompt, hist)
112
+ @histfile = ''
113
+ @hist_save = false
114
+ STDOUT.print prompt
115
+ STDOUT.flush
116
+ line = STDIN.gets
117
+ exit unless line
118
+ line.chomp!
119
+ line
120
+ end
121
+ end
122
+ end
123
+
124
+ class RemoteInterface < Interface # :nodoc:
125
+ attr_accessor :command_queue, :history_length, :history_save, :histfile
126
+ attr_accessor :restart_file
127
+
128
+ def initialize(socket)
129
+ @command_queue = []
130
+ @socket = socket
131
+ @history_save = false
132
+ @history_length = 256
133
+ @histfile = ''
134
+ # Do we read the histfile?
135
+ # open(@histfile, 'r') do |file|
136
+ # file.each do |line|
137
+ # line.chomp!
138
+ # Readline::HISTORY << line
139
+ # end
140
+ # end if File.exist?(@histfile)
141
+ @restart_file = nil
142
+ end
143
+
144
+ def close
145
+ @socket.close
146
+ rescue Exception
147
+ end
148
+
149
+ def confirm(prompt)
150
+ send_command "CONFIRM #{prompt}"
151
+ end
152
+
153
+ def finalize
154
+ end
155
+
156
+ def read_command(prompt)
157
+ send_command "PROMPT #{prompt}"
158
+ end
159
+
160
+ def readline_support?
161
+ false
162
+ end
163
+
164
+ def print(*args)
165
+ @socket.printf(*args)
166
+ end
167
+
168
+ private
169
+
170
+ def send_command(msg)
171
+ @socket.puts msg
172
+ result = @socket.gets
173
+ raise IOError unless result
174
+ result.chomp
175
+ end
176
+ end
177
+
178
+ class ScriptInterface < Interface # :nodoc:
179
+ attr_accessor :command_queue, :history_length, :history_save, :histfile
180
+ attr_accessor :restart_file
181
+
182
+ def initialize(file, out, verbose=false)
183
+ super()
184
+ @command_queue = []
185
+ @file = file.respond_to?(:gets) ? file : open(file)
186
+ @out = out
187
+ @verbose = verbose
188
+ @history_save = false
189
+ @history_length = 256 # take gdb default
190
+ @histfile = ''
191
+ end
192
+
193
+ def finalize
194
+ end
195
+
196
+ def read_command(prompt)
197
+ while result = @file.gets
198
+ puts "# #{result}" if @verbose
199
+ next if result =~ /^\s*#/
200
+ next if result.strip.empty?
201
+ break
202
+ end
203
+ raise IOError unless result
204
+ result.chomp!
205
+ end
206
+
207
+ def readline_support?
208
+ false
209
+ end
210
+
211
+ def confirm(prompt)
212
+ 'y'
213
+ end
214
+
215
+ def print(*args)
216
+ @out.printf(*args)
217
+ end
218
+
219
+ def close
220
+ @file.close
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,468 @@
1
+ require_relative 'interface'
2
+ require_relative 'command'
3
+
4
+ module Byebug
5
+
6
+ # Should this be a mixin?
7
+ class Processor # :nodoc
8
+ attr_accessor :interface
9
+
10
+ # Format msg with gdb-style annotation header
11
+ def afmt(msg, newline="\n")
12
+ "\032\032#{msg}#{newline}"
13
+ end
14
+
15
+ def aprint(msg)
16
+ print afmt(msg) if Byebug.annotate.to_i > 2
17
+ end
18
+
19
+ # FIXME: use delegate?
20
+ def errmsg(*args)
21
+ @interface.errmsg(*args)
22
+ end
23
+
24
+ # Callers of this routine should make sure to use comma to separate format
25
+ # argments rather than %. Otherwise it seems that if the string you want to
26
+ # print has format specifier, which could happen if you are trying to show
27
+ # say a source-code line with "puts" or "print" in it, this print routine
28
+ # will give an error saying it is looking for more arguments.
29
+ def print(*args)
30
+ @interface.print(*args)
31
+ end
32
+
33
+ end
34
+
35
+ class CommandProcessor < Processor # :nodoc:
36
+ attr_reader :display
37
+
38
+ # FIXME: get from Command regexp method.
39
+ @@Show_breakpoints_postcmd = [
40
+ /^\s*b(?:reak)?/,
41
+ /^\s* cond(?:ition)? (?:\s+(\d+)\s*(.*))?$/ix,
42
+ /^\s*del(?:ete)?(?:\s+(.*))?$/ix,
43
+ /^\s* dis(?:able)? (?:\s+(.*))?$/ix,
44
+ /^\s* en(?:able)? (?:\s+(.*))?$/ix
45
+ # "tbreak", "clear",
46
+ ]
47
+ @@Show_annotations_run = [
48
+ /^\s*c(?:ont(?:inue)?)?(?:\s+(.*))?$/,
49
+ /^\s*fin(?:ish)?$/,
50
+ /^\s*n(?:ext)?([+-])?(?:\s+(.*))?$/,
51
+ /^\s*s(?:tep)?([+-])?(?:\s+(.*))?$/
52
+ ]
53
+
54
+ @@Show_annotations_postcmd = [
55
+ /^\s* down (?:\s+(.*))? .*$/x,
56
+ /^\s* f(?:rame)? (?:\s+ (.*))? \s*$/x,
57
+ /^\s* u(?:p)? (?:\s+(.*))?$/x
58
+ ]
59
+
60
+ def initialize(interface = LocalInterface.new)
61
+ @interface = interface
62
+ @display = []
63
+
64
+ @mutex = Mutex.new
65
+ @last_cmd = nil
66
+ @last_file = nil # Filename the last time we stopped
67
+ @last_line = nil # line number the last time we stopped
68
+ @byebug_breakpoints_were_empty = false # Show breakpoints 1st time
69
+ @byebug_displays_were_empty = true # No display 1st time
70
+ @byebug_context_was_dead = true # Assume we haven't started.
71
+ end
72
+
73
+ def interface=(interface)
74
+ @mutex.synchronize do
75
+ @interface.close if @interface
76
+ @interface = interface
77
+ end
78
+ end
79
+
80
+ require 'pathname' # For cleanpath
81
+
82
+ # Regularize file name.
83
+ # This is also used as a common funnel place if basename is
84
+ # desired or if we are working remotely and want to change the
85
+ # basename. Or we are eliding filenames.
86
+ def self.canonic_file(filename)
87
+ # For now we want resolved filenames
88
+ if Command.settings[:basename]
89
+ File.basename(filename)
90
+ else
91
+ # Cache this?
92
+ Pathname.new(filename).cleanpath.to_s
93
+ end
94
+ end
95
+
96
+ def self.print_location_and_text(file, line)
97
+ file_line = "%s:%s\n%s" % [canonic_file(file), line,
98
+ Byebug.line_at(file, line)]
99
+ # FIXME: use annotations routines
100
+ if Byebug.annotate.to_i > 2
101
+ file_line = "\032\032source #{file_line}"
102
+ elsif ENV['EMACS']
103
+ file_line = "\032\032#{file_line}"
104
+ end
105
+ print file_line
106
+ end
107
+
108
+ def self.protect(mname)
109
+ alias_method "__#{mname}", mname
110
+ module_eval %{
111
+ def #{mname}(*args)
112
+ @mutex.synchronize do
113
+ return unless @interface
114
+ __#{mname}(*args)
115
+ end
116
+ rescue IOError, Errno::EPIPE
117
+ self.interface = nil
118
+ rescue SignalException
119
+ raise
120
+ rescue Exception
121
+ print "INTERNAL ERROR!!! #\{$!\}\n" rescue nil
122
+ print $!.backtrace.map{|l| "\t#\{l\}"}.join("\n") rescue nil
123
+ end
124
+ }
125
+ end
126
+
127
+ def at_breakpoint(context, breakpoint)
128
+ aprint 'stopped' if Byebug.annotate.to_i > 2
129
+ n = Byebug.breakpoints.index(breakpoint) + 1
130
+ file = CommandProcessor.canonic_file(breakpoint.source)
131
+ line = breakpoint.pos
132
+ if Byebug.annotate.to_i > 2
133
+ print afmt("source #{file}:#{line}")
134
+ end
135
+ print "Breakpoint %d at %s:%s\n", n, file, line
136
+ end
137
+ protect :at_breakpoint
138
+
139
+ def at_catchpoint(context, excpt)
140
+ aprint 'stopped' if Byebug.annotate.to_i > 2
141
+ file = CommandProcessor.canonic_file(context.frame_file(0))
142
+ line = context.frame_line(0)
143
+ print afmt("%s:%d" % [file, line]) if ENV['EMACS']
144
+ print "Catchpoint at %s:%d: `%s' (%s)\n", file, line, excpt, excpt.class
145
+ fs = context.stack_size
146
+ tb = caller(0)[-fs..-1]
147
+ if tb
148
+ for i in tb
149
+ print "\tfrom %s\n", i
150
+ end
151
+ end
152
+ end
153
+ protect :at_catchpoint
154
+
155
+ def at_tracing(context, file, line)
156
+ return if defined?(Byebug::RDEBUG_FILE) &&
157
+ Byebug::RDEBUG_FILE == file # Don't trace ourself
158
+ @last_file = CommandProcessor.canonic_file(file)
159
+ file = CommandProcessor.canonic_file(file)
160
+ unless file == @last_file and @last_line == line and
161
+ Command.settings[:tracing_plus]
162
+ print "Tracing:%s:%s %s", file, line, Byebug.line_at(file, line)
163
+ @last_file = file
164
+ @last_line = line
165
+ end
166
+ always_run(context, file, line, 2)
167
+ end
168
+ protect :at_tracing
169
+
170
+ def at_line(context, file, line)
171
+ process_commands(context, file, line)
172
+ end
173
+ protect :at_line
174
+
175
+ def at_return(context, file, line)
176
+ context.stop_frame = -1
177
+ process_commands(context, file, line)
178
+ end
179
+
180
+ private
181
+
182
+ # The prompt shown before reading a command.
183
+ def prompt(context)
184
+ p = '(rdb:%s) ' % (context.dead? ? 'post-mortem' : context.thnum)
185
+ p = afmt("pre-prompt")+p+"\n"+afmt("prompt") if Byebug.annotate.to_i > 2
186
+ return p
187
+ end
188
+
189
+ # Run these commands, for example display commands or possibly the list or
190
+ # irb in an "autolist" or "autoirb". We return a list of commands that are
191
+ # acceptable to run bound to the current state.
192
+ def always_run(context, file, line, run_level)
193
+ event_cmds = Command.commands.select{|cmd| cmd.event }
194
+
195
+ # Remove some commands in post-mortem
196
+ event_cmds = event_cmds.find_all do |cmd|
197
+ cmd.allow_in_post_mortem
198
+ end if context.dead?
199
+
200
+ state = State.new do |s|
201
+ s.context = context
202
+ s.file = file
203
+ s.line = line
204
+ s.binding = context.frame_binding(0)
205
+ s.display = display
206
+ s.interface = interface
207
+ s.commands = event_cmds
208
+ end
209
+ @interface.state = state if @interface.respond_to?('state=')
210
+
211
+ # Bind commands to the current state.
212
+ commands = event_cmds.map{|cmd| cmd.new(state)}
213
+
214
+ commands.select do |cmd|
215
+ cmd.class.always_run >= run_level
216
+ end.each {|cmd| cmd.execute}
217
+ return state, commands
218
+ end
219
+
220
+ # Handle byebug commands
221
+ def process_commands(context, file, line)
222
+ state, commands = always_run(context, file, line, 1)
223
+ $rdebug_state = state if Command.settings[:byebugtesting]
224
+ splitter = lambda do |str|
225
+ str.split(/;/).inject([]) do |m, v|
226
+ if m.empty?
227
+ m << v
228
+ else
229
+ if m.last[-1] == ?\\
230
+ m.last[-1,1] = ''
231
+ m.last << ';' << v
232
+ else
233
+ m << v
234
+ end
235
+ end
236
+ m
237
+ end
238
+ end
239
+
240
+ preloop(commands, context)
241
+ CommandProcessor.print_location_and_text(file, line)
242
+ while !state.proceed?
243
+ input = if @interface.command_queue.empty?
244
+ @interface.read_command(prompt(context))
245
+ else
246
+ @interface.command_queue.shift
247
+ end
248
+ break unless input
249
+ catch(:debug_error) do
250
+ if input == ""
251
+ next unless @last_cmd
252
+ input = @last_cmd
253
+ else
254
+ @last_cmd = input
255
+ end
256
+ splitter[input].each do |cmd|
257
+ one_cmd(commands, context, cmd)
258
+ postcmd(commands, context, cmd)
259
+ end
260
+ end
261
+ end
262
+ postloop(commands, context)
263
+ end # process_commands
264
+
265
+ def one_cmd(commands, context, input)
266
+ if cmd = commands.find{ |c| c.match(input) }
267
+ if context.dead? && cmd.class.need_context
268
+ p cmd
269
+ print "Command is unavailable\n"
270
+ else
271
+ cmd.execute
272
+ end
273
+ else
274
+ unknown_cmd = commands.find{ |c| c.class.unknown }
275
+ if unknown_cmd
276
+ unknown_cmd.execute
277
+ else
278
+ errmsg "Unknown command: \"#{input}\". Try \"help\".\n"
279
+ end
280
+ end
281
+ end
282
+
283
+ def preloop(commands, context)
284
+ aprint('stopped') if Byebug.annotate.to_i > 2
285
+ if context.dead?
286
+ unless @byebug_context_was_dead
287
+ if Byebug.annotate.to_i > 2
288
+ aprint('exited')
289
+ print "The program finished.\n"
290
+ end
291
+ @byebug_context_was_dead = true
292
+ end
293
+ end
294
+
295
+ if Byebug.annotate.to_i > 2
296
+ # if we are here, the stack frames have changed outside the command
297
+ # loop (e.g. after a "continue" command), so we show the annotations
298
+ # again
299
+ breakpoint_annotations(commands, context)
300
+ display_annotations(commands, context)
301
+ annotation('stack', commands, context, "where")
302
+ annotation('variables', commands, context, "info variables") unless
303
+ context.dead?
304
+ end
305
+ end
306
+
307
+ def postcmd(commands, context, cmd)
308
+ if Byebug.annotate.to_i > 2
309
+ cmd = @last_cmd unless cmd
310
+ breakpoint_annotations(commands, context) if
311
+ @@Show_breakpoints_postcmd.find{|pat| cmd =~ pat}
312
+ display_annotations(commands, context)
313
+ if @@Show_annotations_postcmd.find{|pat| cmd =~ pat}
314
+ annotation('stack', commands, context, "where") if
315
+ context.stack_size > 0
316
+ annotation('variables', commands, context, "info variables") unless
317
+ context.dead?
318
+ end
319
+ if not context.dead? and @@Show_annotations_run.find{|pat| cmd =~ pat}
320
+ aprint 'starting' if Byebug.annotate.to_i > 2
321
+
322
+ @byebug_context_was_dead = false
323
+ end
324
+ end
325
+ end
326
+
327
+ def postloop(commands, context)
328
+ end
329
+
330
+ def annotation(label, commands, context, cmd)
331
+ print afmt(label)
332
+ one_cmd(commands, context, cmd)
333
+ end
334
+
335
+ def breakpoint_annotations(commands, context)
336
+ unless Byebug.breakpoints.empty? and @byebug_breakpoints_were_empty
337
+ annotation('breakpoints', commands, context, "info breakpoints")
338
+ @byebug_breakpoints_were_empty = Byebug.breakpoints.empty?
339
+ end
340
+ end
341
+
342
+ def display_annotations(commands, context)
343
+ return if display.empty?
344
+ # have_display = display.find{|d| d[0]}
345
+ # return unless have_display and @byebug_displays_were_empty
346
+ # @byebug_displays_were_empty = have_display
347
+ annotation('display', commands, context, "display")
348
+ end
349
+
350
+ class State # :nodoc:
351
+ attr_accessor :binding, :commands, :context, :display, :file, :frame_pos
352
+ attr_accessor :interface, :line, :previous_line
353
+
354
+ def initialize
355
+ super()
356
+ @frame_pos = 0
357
+ @previous_line = nil
358
+ @proceed = false
359
+ yield self
360
+ end
361
+
362
+ # FIXME: use delegate?
363
+ def errmsg(*args)
364
+ @interface.errmsg(*args)
365
+ end
366
+
367
+ def print(*args)
368
+ @interface.print(*args)
369
+ end
370
+
371
+ def confirm(*args)
372
+ @interface.confirm(*args)
373
+ end
374
+
375
+ def proceed?
376
+ @proceed
377
+ end
378
+
379
+ def proceed
380
+ @proceed = true
381
+ end
382
+ end
383
+
384
+ end # end class CommandProcessor
385
+
386
+ class ControlCommandProcessor < Processor # :nodoc:
387
+ def initialize(interface)
388
+ super()
389
+ @interface = interface
390
+ @byebug_context_was_dead = true # Assume we haven't started.
391
+ end
392
+
393
+ def process_commands(verbose=false)
394
+ control_cmds = Command.commands.select do |cmd|
395
+ cmd.allow_in_control
396
+ end
397
+ state = State.new(@interface, control_cmds)
398
+ commands = control_cmds.map{|cmd| cmd.new(state) }
399
+
400
+ unless @byebug_context_was_dead
401
+ if Byebug.annotate.to_i > 2
402
+ aprint 'exited'
403
+ print "The program finished.\n"
404
+ end
405
+ @byebug_context_was_dead = true
406
+ end
407
+
408
+ while input = @interface.read_command(prompt(nil))
409
+ print "+#{input}" if verbose
410
+ catch(:debug_error) do
411
+ if cmd = commands.find{|c| c.match(input) }
412
+ cmd.execute
413
+ else
414
+ errmsg "Unknown command\n"
415
+ end
416
+ end
417
+ end
418
+ rescue IOError, Errno::EPIPE
419
+ rescue Exception
420
+ print "INTERNAL ERROR!!! #{$!}\n" rescue nil
421
+ print $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
422
+ ensure
423
+ @interface.close
424
+ end
425
+
426
+ # The prompt shown before reading a command.
427
+ # Note: have an unused 'context' parameter to match the local interface.
428
+ def prompt(context)
429
+ p = '(rdb:ctrl) '
430
+ p = afmt("pre-prompt")+p+"\n"+afmt("prompt") if
431
+ Byebug.annotate.to_i > 2
432
+ return p
433
+ end
434
+
435
+ class State # :nodoc:
436
+ attr_reader :commands, :interface
437
+
438
+ def initialize(interface, commands)
439
+ @interface = interface
440
+ @commands = commands
441
+ end
442
+
443
+ def proceed
444
+ end
445
+
446
+ def errmsg(*args)
447
+ @interface.print(*args)
448
+ end
449
+
450
+ def print(*args)
451
+ @interface.print(*args)
452
+ end
453
+
454
+ def confirm(*args)
455
+ 'y'
456
+ end
457
+
458
+ def context
459
+ nil
460
+ end
461
+
462
+ def file
463
+ errmsg "No filename given.\n"
464
+ throw :debug_error
465
+ end
466
+ end # State
467
+ end
468
+ end