byebug 3.5.1 → 4.0.0

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 (137) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/.rubocop.yml +18 -1
  4. data/.travis.yml +21 -1
  5. data/CHANGELOG.md +356 -308
  6. data/CONTRIBUTING.md +31 -15
  7. data/GUIDE.md +859 -475
  8. data/Gemfile +8 -10
  9. data/LICENSE +1 -1
  10. data/README.md +41 -45
  11. data/Rakefile +30 -28
  12. data/byebug.gemspec +18 -18
  13. data/ext/byebug/breakpoint.c +88 -75
  14. data/ext/byebug/byebug.c +253 -252
  15. data/ext/byebug/byebug.h +53 -53
  16. data/ext/byebug/context.c +188 -159
  17. data/ext/byebug/extconf.rb +9 -6
  18. data/ext/byebug/locker.c +53 -11
  19. data/ext/byebug/threads.c +137 -39
  20. data/lib/byebug/attacher.rb +7 -2
  21. data/lib/byebug/breakpoint.rb +30 -0
  22. data/lib/byebug/command.rb +36 -32
  23. data/lib/byebug/commands/break.rb +49 -48
  24. data/lib/byebug/commands/catch.rb +64 -0
  25. data/lib/byebug/commands/condition.rb +13 -9
  26. data/lib/byebug/commands/continue.rb +8 -4
  27. data/lib/byebug/commands/delete.rb +10 -4
  28. data/lib/byebug/commands/display.rb +33 -25
  29. data/lib/byebug/commands/edit.rb +18 -13
  30. data/lib/byebug/commands/enable_disable.rb +26 -24
  31. data/lib/byebug/commands/eval.rb +77 -35
  32. data/lib/byebug/commands/finish.rb +9 -5
  33. data/lib/byebug/commands/frame.rb +66 -125
  34. data/lib/byebug/commands/help.rb +14 -21
  35. data/lib/byebug/commands/history.rb +5 -1
  36. data/lib/byebug/commands/info.rb +41 -106
  37. data/lib/byebug/commands/interrupt.rb +6 -2
  38. data/lib/byebug/commands/irb.rb +5 -2
  39. data/lib/byebug/commands/kill.rb +6 -2
  40. data/lib/byebug/commands/list.rb +21 -14
  41. data/lib/byebug/commands/method.rb +17 -9
  42. data/lib/byebug/commands/pry.rb +13 -3
  43. data/lib/byebug/commands/quit.rb +10 -5
  44. data/lib/byebug/commands/restart.rb +12 -19
  45. data/lib/byebug/commands/save.rb +10 -6
  46. data/lib/byebug/commands/set.rb +15 -14
  47. data/lib/byebug/commands/show.rb +8 -8
  48. data/lib/byebug/commands/source.rb +14 -8
  49. data/lib/byebug/commands/stepping.rb +15 -29
  50. data/lib/byebug/commands/threads.rb +73 -49
  51. data/lib/byebug/commands/tracevar.rb +56 -0
  52. data/lib/byebug/commands/undisplay.rb +8 -4
  53. data/lib/byebug/commands/untracevar.rb +38 -0
  54. data/lib/byebug/commands/var.rb +107 -0
  55. data/lib/byebug/context.rb +78 -42
  56. data/lib/byebug/core.rb +78 -40
  57. data/lib/byebug/helper.rb +58 -42
  58. data/lib/byebug/history.rb +12 -1
  59. data/lib/byebug/interface.rb +91 -11
  60. data/lib/byebug/interfaces/local_interface.rb +12 -19
  61. data/lib/byebug/interfaces/remote_interface.rb +12 -15
  62. data/lib/byebug/interfaces/script_interface.rb +14 -18
  63. data/lib/byebug/interfaces/test_interface.rb +54 -0
  64. data/lib/byebug/printers/base.rb +64 -0
  65. data/lib/byebug/printers/plain.rb +53 -0
  66. data/lib/byebug/processor.rb +20 -1
  67. data/lib/byebug/processors/command_processor.rb +57 -172
  68. data/lib/byebug/processors/control_command_processor.rb +16 -43
  69. data/lib/byebug/remote.rb +13 -7
  70. data/lib/byebug/runner.rb +102 -54
  71. data/lib/byebug/setting.rb +45 -68
  72. data/lib/byebug/settings/autoeval.rb +2 -0
  73. data/lib/byebug/settings/autoirb.rb +3 -0
  74. data/lib/byebug/settings/autolist.rb +3 -0
  75. data/lib/byebug/settings/autosave.rb +2 -0
  76. data/lib/byebug/settings/basename.rb +2 -0
  77. data/lib/byebug/settings/callstyle.rb +2 -0
  78. data/lib/byebug/settings/fullpath.rb +2 -0
  79. data/lib/byebug/settings/histfile.rb +2 -0
  80. data/lib/byebug/settings/histsize.rb +2 -0
  81. data/lib/byebug/settings/linetrace.rb +2 -0
  82. data/lib/byebug/settings/listsize.rb +2 -0
  83. data/lib/byebug/settings/post_mortem.rb +7 -2
  84. data/lib/byebug/settings/stack_on_error.rb +2 -0
  85. data/lib/byebug/settings/verbose.rb +2 -0
  86. data/lib/byebug/settings/width.rb +2 -0
  87. data/lib/byebug/state.rb +12 -0
  88. data/lib/byebug/states/control_state.rb +26 -0
  89. data/lib/byebug/states/regular_state.rb +178 -0
  90. data/lib/byebug/version.rb +1 -1
  91. metadata +24 -109
  92. data/lib/byebug/commands/catchpoint.rb +0 -53
  93. data/lib/byebug/commands/reload.rb +0 -29
  94. data/lib/byebug/commands/trace.rb +0 -50
  95. data/lib/byebug/commands/variables.rb +0 -206
  96. data/lib/byebug/options.rb +0 -46
  97. data/lib/byebug/settings/autoreload.rb +0 -12
  98. data/lib/byebug/settings/forcestep.rb +0 -14
  99. data/lib/byebug/settings/testing.rb +0 -12
  100. data/lib/byebug/settings/tracing_plus.rb +0 -11
  101. data/test/commands/break_test.rb +0 -364
  102. data/test/commands/condition_test.rb +0 -85
  103. data/test/commands/continue_test.rb +0 -47
  104. data/test/commands/delete_test.rb +0 -26
  105. data/test/commands/display_test.rb +0 -37
  106. data/test/commands/edit_test.rb +0 -52
  107. data/test/commands/eval_test.rb +0 -89
  108. data/test/commands/finish_test.rb +0 -74
  109. data/test/commands/frame_test.rb +0 -223
  110. data/test/commands/help_test.rb +0 -66
  111. data/test/commands/history_test.rb +0 -61
  112. data/test/commands/info_test.rb +0 -238
  113. data/test/commands/interrupt_test.rb +0 -45
  114. data/test/commands/irb_test.rb +0 -28
  115. data/test/commands/kill_test.rb +0 -50
  116. data/test/commands/list_test.rb +0 -174
  117. data/test/commands/method_test.rb +0 -52
  118. data/test/commands/post_mortem_test.rb +0 -71
  119. data/test/commands/pry_test.rb +0 -26
  120. data/test/commands/quit_test.rb +0 -53
  121. data/test/commands/reload_test.rb +0 -39
  122. data/test/commands/restart_test.rb +0 -46
  123. data/test/commands/save_test.rb +0 -67
  124. data/test/commands/set_test.rb +0 -140
  125. data/test/commands/show_test.rb +0 -76
  126. data/test/commands/source_test.rb +0 -46
  127. data/test/commands/stepping_test.rb +0 -192
  128. data/test/commands/thread_test.rb +0 -164
  129. data/test/commands/trace_test.rb +0 -71
  130. data/test/commands/undisplay_test.rb +0 -75
  131. data/test/commands/variables_test.rb +0 -105
  132. data/test/debugger_alias_test.rb +0 -7
  133. data/test/runner_test.rb +0 -150
  134. data/test/support/matchers.rb +0 -65
  135. data/test/support/test_interface.rb +0 -59
  136. data/test/support/utils.rb +0 -122
  137. data/test/test_helper.rb +0 -58
@@ -1,105 +1,55 @@
1
+ require 'byebug/states/regular_state'
2
+
1
3
  module Byebug
2
4
  #
3
5
  # Processes commands in regular mode
4
6
  #
5
7
  class CommandProcessor < Processor
6
- attr_reader :display
8
+ attr_reader :display, :state
7
9
 
8
10
  def initialize(interface = LocalInterface.new)
9
11
  super(interface)
10
12
 
11
13
  @display = []
12
- @mutex = Mutex.new
13
- @last_cmd = nil # To allow empty (just <RET>) commands
14
- @last_file = nil # Filename the last time we stopped
15
- @last_line = nil # Line number the last time we stopped
14
+ @last_cmd = nil # To allow empty (just <RET>) commands
16
15
  @context_was_dead = false # Assume we haven't started.
17
16
  end
18
17
 
19
18
  def interface=(interface)
20
- @mutex.synchronize do
21
- @interface.close if @interface
22
- @interface = interface
23
- end
24
- end
25
-
26
- require 'pathname' # For cleanpath
27
-
28
- #
29
- # Regularize file name.
30
- #
31
- # This is also used as a common funnel place if basename is desired or if we
32
- # are working remotely and want to change the basename. Or we are eliding
33
- # filenames.
34
- def self.canonic_file(filename)
35
- return filename if ['(irb)', '-e'].include?(filename)
36
-
37
- # For now we want resolved filenames
38
- if Setting[:basename]
39
- File.basename(filename)
40
- else
41
- Pathname.new(filename).cleanpath.to_s
42
- end
19
+ @interface.close if @interface
20
+ @interface = interface
43
21
  end
44
22
 
45
- def self.protect(mname)
46
- alias_method "__#{mname}", mname
47
- module_eval <<-END, __FILE__, __LINE__ + 1
48
- def #{mname}(*args)
49
- @mutex.synchronize do
50
- return unless @interface
51
- __#{mname}(*args)
52
- end
53
- rescue IOError, SystemCallError
54
- @interface.close
55
- rescue SignalException
56
- raise
57
- rescue
58
- without_exceptions do
59
- puts "INTERNAL ERROR!!! #\{$!\}"
60
- puts $!.backtrace.map{|l| "\t#\{l\}"}.join("\n")
61
- end
62
- end
63
- END
64
- end
23
+ include FileFunctions
65
24
 
66
25
  def at_breakpoint(_context, breakpoint)
67
26
  n = Byebug.breakpoints.index(breakpoint) + 1
68
- file = self.class.canonic_file(breakpoint.source)
27
+ file = normalize(breakpoint.source)
69
28
  line = breakpoint.pos
29
+
70
30
  puts "Stopped by breakpoint #{n} at #{file}:#{line}"
71
31
  end
72
- protect :at_breakpoint
73
32
 
74
33
  def at_catchpoint(context, excpt)
75
- file = self.class.canonic_file(context.frame_file(0))
34
+ file = normalize(context.frame_file(0))
76
35
  line = context.frame_line(0)
36
+
77
37
  puts "Catchpoint at #{file}:#{line}: `#{excpt}' (#{excpt.class})"
78
38
  end
79
- protect :at_catchpoint
80
-
81
- include ParseFunctions
82
39
 
83
40
  def at_tracing(context, file, line)
84
- if file != @last_file || line != @last_line || Setting[:tracing_plus]
85
- path = self.class.canonic_file(file)
86
- @last_file, @last_line = file, line
87
- puts "Tracing: #{path}:#{line} #{get_line(file, line)}"
88
- end
41
+ puts "Tracing: #{normalize(file)}:#{line} #{get_line(file, line)}"
42
+
89
43
  always_run(context, file, line, 2)
90
44
  end
91
- protect :at_tracing
92
45
 
93
46
  def at_line(context, file, line)
94
- Byebug.source_reload if Setting[:autoreload]
95
47
  process_commands(context, file, line)
96
48
  end
97
- protect :at_line
98
49
 
99
50
  def at_return(context, file, line)
100
51
  process_commands(context, file, line)
101
52
  end
102
- protect :at_return
103
53
 
104
54
  private
105
55
 
@@ -107,51 +57,26 @@ module Byebug
107
57
  # Prompt shown before reading a command.
108
58
  #
109
59
  def prompt(context)
110
- "(byebug#{context.dead? ? ':post-mortem' : ''}) "
60
+ "(byebug#{context.dead? ? ':post-mortem' : ''}) "
111
61
  end
112
62
 
113
63
  #
114
64
  # Run commands everytime.
115
65
  #
116
- # For example display commands or possibly the list or irb in an
117
- # "autolist" or "autoirb".
66
+ # For example display commands or possibly the list or irb in an "autolist"
67
+ # or "autoirb".
118
68
  #
119
69
  # @return List of commands acceptable to run bound to the current state
120
70
  #
121
71
  def always_run(context, file, line, run_level)
122
- cmds = Command.commands
123
-
124
- state = State.new(cmds, context, @display, file, @interface, line)
72
+ @state = RegularState.new(context, @display, file, @interface, line)
125
73
 
126
74
  # Change default when in irb or code included in command line
127
75
  Setting[:autolist] = false if ['(irb)', '-e'].include?(file)
128
76
 
129
77
  # Bind commands to the current state.
130
- commands = cmds.map do |cmd_class|
131
- cmd = cmd_class.new(state)
132
- cmd.execute if cmd.class.always_run >= run_level
133
- cmd
134
- end
135
-
136
- [state, commands]
137
- end
138
-
139
- #
140
- # Splits a command line of the form "cmd1 ; cmd2 ; ... ; cmdN" into an
141
- # array of commands: [cmd1, cmd2, ..., cmdN]
142
- #
143
- def split_commands(cmd_line)
144
- cmd_line.split(/;/).each_with_object([]) do |v, m|
145
- if m.empty?
146
- m << v
147
- else
148
- if m.last[-1] == '\\'
149
- m.last[-1, 1] = ''
150
- m.last << ';' << v
151
- else
152
- m << v
153
- end
154
- end
78
+ Command.commands.each do |cmd|
79
+ cmd.new(state).execute if cmd.always_run >= run_level
155
80
  end
156
81
  end
157
82
 
@@ -159,55 +84,55 @@ module Byebug
159
84
  # Handle byebug commands.
160
85
  #
161
86
  def process_commands(context, file, line)
162
- state, commands = preloop(context, file, line)
87
+ always_run(context, file, line, 1)
88
+
89
+ puts 'The program finished.' if program_just_finished?(context)
90
+ puts(state.location) if Setting[:autolist] == 0
163
91
 
164
- repl(state, commands, context)
92
+ @interface.autorestore
165
93
 
166
- postloop
94
+ repl(context)
95
+ ensure
96
+ @interface.autosave
167
97
  end
168
98
 
169
99
  #
170
100
  # Main byebug's REPL
171
101
  #
172
- def repl(state, commands, context)
102
+ def repl(context)
173
103
  until state.proceed?
174
- input = if @interface.command_queue.empty?
175
- @interface.read_command(prompt(context))
176
- else
177
- @interface.command_queue.shift
178
- end
179
- return unless input
180
-
181
- if input == ''
182
- next unless @last_cmd
183
- input = @last_cmd
184
- else
185
- @last_cmd = input
186
- end
187
-
188
- split_commands(input).each do |cmd|
189
- one_cmd(commands, context, cmd)
190
- end
104
+ cmd = @interface.read_command(prompt(context))
105
+ return unless cmd
106
+
107
+ next if cmd == '' && @last_cmd.nil?
108
+
109
+ cmd.empty? ? cmd = @last_cmd : @last_cmd = cmd
110
+
111
+ one_cmd(context, cmd)
191
112
  end
192
113
  end
193
114
 
194
115
  #
195
116
  # Autoevals a single command
196
117
  #
197
- def one_unknown_cmd(commands, input)
118
+ def one_unknown_cmd(input)
198
119
  unless Setting[:autoeval]
199
120
  return errmsg("Unknown command: \"#{input}\". Try \"help\"")
200
121
  end
201
122
 
202
- commands.find { |c| c.is_a?(EvalCommand) }.execute
123
+ eval_cmd = EvalCommand.new(state)
124
+ eval_cmd.match(input)
125
+ eval_cmd.execute
203
126
  end
204
127
 
128
+ #
205
129
  #
206
130
  # Executes a single byebug command
207
131
  #
208
- def one_cmd(commands, context, input)
209
- cmd = commands.find { |c| c.match(input) }
210
- return one_unknown_cmd(commands, input) unless cmd
132
+ def one_cmd(context, input)
133
+ cmd = match_cmd(input)
134
+
135
+ return one_unknown_cmd(input) unless cmd
211
136
 
212
137
  if context.dead? && !cmd.class.allow_in_post_mortem
213
138
  return errmsg('Command unavailable in post mortem mode.')
@@ -217,65 +142,25 @@ module Byebug
217
142
  end
218
143
 
219
144
  #
220
- # Tasks to do before processor loop.
145
+ # Finds a matches the command matching the input
221
146
  #
222
- def preloop(context, file, line)
223
- state, commands = always_run(context, file, line, 1)
224
-
225
- if Setting[:testing]
226
- Thread.current.thread_variable_set('state', state)
227
- else
228
- Thread.current.thread_variable_set('state', nil)
147
+ def match_cmd(input)
148
+ Command.commands.each do |c|
149
+ cmd = c.new(state)
150
+ return cmd if cmd.match(input)
229
151
  end
230
152
 
231
- @context_was_dead = true if context.dead? && !@context_was_dead
232
- if @context_was_dead
233
- puts 'The program finished.'
234
- @context_was_dead = false
235
- end
236
-
237
- puts(state.location) if Setting[:autolist] == 0
238
-
239
- @interface.history.restore if Setting[:autosave]
240
-
241
- [state, commands]
153
+ nil
242
154
  end
243
155
 
244
156
  #
245
- # Tasks to do after processor loop.
157
+ # Returns true first time control is given to the user after program
158
+ # termination.
246
159
  #
247
- def postloop
248
- Setting[:autosave] ? @interface.history.save : @interface.history.clear
249
- end
250
-
251
- class State
252
- attr_accessor :commands, :context, :display, :file, :frame_pos,
253
- :interface, :line, :prev_line
254
-
255
- def initialize(commands, context, display, file, interface, line)
256
- @commands, @context, @display = commands, context, display
257
- @file, @frame_pos, @interface = file, 0, interface
258
- @line, @prev_line, @proceed = line, nil, false
259
- end
260
-
261
- extend Forwardable
262
- def_delegators :@interface, :errmsg, :puts, :confirm
263
-
264
- def proceed?
265
- @proceed
266
- end
267
-
268
- def proceed
269
- @proceed = true
270
- end
271
-
272
- def location
273
- path = self.class.canonic_file(@file)
274
- loc = "#{path} @ #{@line}\n"
275
- loc += "#{get_line(@file, @line)}\n" unless
276
- ['(irb)', '-e'].include? @file
277
- loc
278
- end
160
+ def program_just_finished?(context)
161
+ result = context.dead? && !@context_was_dead
162
+ @context_was_dead = false if result == true
163
+ result
279
164
  end
280
165
  end
281
166
  end
@@ -1,41 +1,41 @@
1
+ require 'byebug/states/control_state'
2
+
1
3
  module Byebug
2
4
  #
3
5
  # Processes commands in 'control' mode, when there's no program running
4
6
  #
5
7
  class ControlCommandProcessor < Processor
8
+ attr_reader :state
9
+
6
10
  def initialize(interface = LocalInterface.new)
7
11
  super(interface)
8
- @context_was_dead = false # Assume we haven't started.
9
12
  end
10
13
 
11
- def process_commands(verbose = false)
12
- control_cmds = Command.commands.select do |cmd|
13
- cmd.allow_in_control
14
- end
15
- state = State.new(@interface, control_cmds)
16
- commands = control_cmds.map { |cmd| cmd.new(state) }
14
+ def commands
15
+ Command.commands.select(&:allow_in_control).map { |cmd| cmd.new(state) }
16
+ end
17
17
 
18
- if @context_was_dead
19
- puts 'The program finished.'
20
- @context_was_dead = false
21
- end
18
+ def process_commands
19
+ @state = ControlState.new(interface)
22
20
 
23
21
  while (input = @interface.read_command(prompt(nil)))
24
- puts("+#{input}") if verbose
25
-
26
22
  cmd = commands.find { |c| c.match(input) }
27
- return errmsg('Unknown command') unless cmd
23
+ unless cmd
24
+ errmsg('Unknown command')
25
+ next
26
+ end
28
27
 
29
28
  cmd.execute
30
29
  end
30
+
31
+ @interface.close
31
32
  rescue IOError, SystemCallError
33
+ @interface.close
32
34
  rescue
33
35
  without_exceptions do
34
36
  puts "INTERNAL ERROR!!! #{$ERROR_INFO}"
35
37
  puts $ERROR_INFO.backtrace.map { |l| "\t#{l}" }.join("\n")
36
38
  end
37
- ensure
38
- @interface.close
39
39
  end
40
40
 
41
41
  #
@@ -44,32 +44,5 @@ module Byebug
44
44
  def prompt(_context)
45
45
  '(byebug:ctrl) '
46
46
  end
47
-
48
- class State
49
- attr_reader :commands, :interface
50
-
51
- def initialize(interface, commands)
52
- @interface = interface
53
- @commands = commands
54
- end
55
-
56
- def proceed
57
- end
58
-
59
- extend Forwardable
60
- def_delegators :@interface, :errmsg, :puts
61
-
62
- def confirm(*_args)
63
- 'y'
64
- end
65
-
66
- def context
67
- nil
68
- end
69
-
70
- def file
71
- errmsg 'No filename given.'
72
- end
73
- end
74
47
  end
75
48
  end
@@ -25,7 +25,7 @@ module Byebug
25
25
  def start_server(host = nil, port = PORT)
26
26
  return if @thread
27
27
 
28
- self.interface = nil
28
+ handler.interface = nil
29
29
  start
30
30
 
31
31
  start_control(host, port == 0 ? 0 : port + 1)
@@ -39,7 +39,7 @@ module Byebug
39
39
  self.actual_port = server.addr[1]
40
40
  @thread = DebugThread.new do
41
41
  while (session = server.accept)
42
- self.interface = RemoteInterface.new(session)
42
+ handler.interface = RemoteInterface.new(session)
43
43
  mutex.synchronize { proceed.signal } if wait_connection
44
44
  end
45
45
  end
@@ -53,8 +53,8 @@ module Byebug
53
53
  @actual_control_port = server.addr[1]
54
54
  @control_thread = DebugThread.new do
55
55
  while (session = server.accept)
56
- interface = RemoteInterface.new(session)
57
- ControlCommandProcessor.new(interface).process_commands
56
+ handler.interface = RemoteInterface.new(session)
57
+ ControlCommandProcessor.new(handler.interface).process_commands
58
58
  end
59
59
  end
60
60
  @actual_control_port
@@ -64,7 +64,8 @@ module Byebug
64
64
  # Connects to the remote byebug
65
65
  #
66
66
  def start_client(host = 'localhost', port = PORT)
67
- interface = LocalInterface.new
67
+ handler.interface = LocalInterface.new
68
+ puts 'Connecting to byebug server...'
68
69
  socket = TCPSocket.new(host, port)
69
70
  puts 'Connected.'
70
71
 
@@ -72,11 +73,11 @@ module Byebug
72
73
  while (line = socket.gets)
73
74
  case line
74
75
  when /^PROMPT (.*)$/
75
- input = interface.read_command(Regexp.last_match[1])
76
+ input = handler.interface.read_command(Regexp.last_match[1])
76
77
  throw :exit unless input
77
78
  socket.puts input
78
79
  when /^CONFIRM (.*)$/
79
- input = interface.confirm(Regexp.last_match[1])
80
+ input = handler.interface.confirm(Regexp.last_match[1])
80
81
  throw :exit unless input
81
82
  socket.puts input
82
83
  else
@@ -86,5 +87,10 @@ module Byebug
86
87
  end
87
88
  socket.close
88
89
  end
90
+
91
+ def parse_host_and_port(host_port_spec)
92
+ location = host_port_spec.split(':')
93
+ location[1] ? [location[0], location[1].to_i] : ['localhost', location[0]]
94
+ end
89
95
  end
90
96
  end