byebug 3.5.1 → 4.0.0

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