byebug 5.0.0 → 6.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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -1
  3. data/CONTRIBUTING.md +35 -13
  4. data/GUIDE.md +256 -198
  5. data/README.md +5 -11
  6. data/ext/byebug/byebug.c +5 -43
  7. data/ext/byebug/byebug.h +6 -1
  8. data/ext/byebug/context.c +4 -5
  9. data/lib/byebug/command.rb +64 -64
  10. data/lib/byebug/command_list.rb +32 -0
  11. data/lib/byebug/commands.rb +37 -0
  12. data/lib/byebug/commands/break.rb +45 -37
  13. data/lib/byebug/commands/catch.rb +52 -28
  14. data/lib/byebug/commands/condition.rb +19 -13
  15. data/lib/byebug/commands/continue.rb +15 -11
  16. data/lib/byebug/commands/delete.rb +18 -12
  17. data/lib/byebug/commands/disable.rb +9 -10
  18. data/lib/byebug/commands/disable/breakpoints.rb +13 -11
  19. data/lib/byebug/commands/disable/display.rb +13 -11
  20. data/lib/byebug/commands/display.rb +32 -24
  21. data/lib/byebug/commands/down.rb +18 -14
  22. data/lib/byebug/commands/edit.rb +42 -26
  23. data/lib/byebug/commands/enable.rb +9 -3
  24. data/lib/byebug/commands/enable/breakpoints.rb +13 -11
  25. data/lib/byebug/commands/enable/display.rb +13 -11
  26. data/lib/byebug/commands/finish.rb +23 -14
  27. data/lib/byebug/commands/frame.rb +21 -18
  28. data/lib/byebug/commands/help.rb +39 -16
  29. data/lib/byebug/commands/history.rb +16 -10
  30. data/lib/byebug/commands/info.rb +8 -5
  31. data/lib/byebug/commands/info/breakpoints.rb +16 -14
  32. data/lib/byebug/commands/info/display.rb +18 -18
  33. data/lib/byebug/commands/info/file.rb +22 -22
  34. data/lib/byebug/commands/info/line.rb +13 -11
  35. data/lib/byebug/commands/info/program.rb +13 -17
  36. data/lib/byebug/commands/interrupt.rb +13 -11
  37. data/lib/byebug/commands/irb.rb +16 -10
  38. data/lib/byebug/commands/kill.rb +19 -13
  39. data/lib/byebug/commands/list.rb +35 -24
  40. data/lib/byebug/commands/method.rb +25 -15
  41. data/lib/byebug/commands/next.rb +15 -13
  42. data/lib/byebug/commands/pry.rb +18 -11
  43. data/lib/byebug/commands/ps.rb +21 -23
  44. data/lib/byebug/commands/quit.rb +17 -11
  45. data/lib/byebug/commands/restart.rb +28 -24
  46. data/lib/byebug/commands/save.rb +23 -15
  47. data/lib/byebug/commands/set.rb +26 -19
  48. data/lib/byebug/commands/show.rb +20 -14
  49. data/lib/byebug/commands/source.rb +15 -14
  50. data/lib/byebug/commands/step.rb +15 -13
  51. data/lib/byebug/commands/thread.rb +8 -4
  52. data/lib/byebug/commands/thread/current.rb +11 -11
  53. data/lib/byebug/commands/thread/list.rb +14 -14
  54. data/lib/byebug/commands/thread/resume.rb +14 -14
  55. data/lib/byebug/commands/thread/stop.rb +14 -14
  56. data/lib/byebug/commands/thread/switch.rb +15 -14
  57. data/lib/byebug/commands/tracevar.rb +20 -16
  58. data/lib/byebug/commands/undisplay.rb +22 -18
  59. data/lib/byebug/commands/untracevar.rb +13 -11
  60. data/lib/byebug/commands/up.rb +18 -14
  61. data/lib/byebug/commands/var.rb +10 -3
  62. data/lib/byebug/commands/var/all.rb +15 -13
  63. data/lib/byebug/commands/var/args.rb +37 -0
  64. data/lib/byebug/commands/var/const.rb +25 -14
  65. data/lib/byebug/commands/var/global.rb +13 -11
  66. data/lib/byebug/commands/var/instance.rb +13 -11
  67. data/lib/byebug/commands/var/local.rb +13 -11
  68. data/lib/byebug/commands/where.rb +15 -11
  69. data/lib/byebug/context.rb +71 -73
  70. data/lib/byebug/core.rb +45 -26
  71. data/lib/byebug/errors.rb +27 -0
  72. data/lib/byebug/frame.rb +181 -0
  73. data/lib/byebug/helpers/eval.rb +67 -26
  74. data/lib/byebug/helpers/file.rb +18 -3
  75. data/lib/byebug/helpers/frame.rb +36 -39
  76. data/lib/byebug/helpers/parse.rb +15 -13
  77. data/lib/byebug/helpers/path.rb +21 -0
  78. data/lib/byebug/helpers/reflection.rb +17 -0
  79. data/lib/byebug/helpers/thread.rb +20 -14
  80. data/lib/byebug/helpers/toggle.rb +10 -5
  81. data/lib/byebug/helpers/var.rb +36 -15
  82. data/lib/byebug/interface.rb +27 -9
  83. data/lib/byebug/option_setter.rb +93 -0
  84. data/lib/byebug/printers/base.rb +3 -0
  85. data/lib/byebug/printers/plain.rb +4 -14
  86. data/lib/byebug/printers/texts/base.yml +2 -7
  87. data/lib/byebug/processors/command_processor.rb +101 -102
  88. data/lib/byebug/processors/control_processor.rb +20 -0
  89. data/lib/byebug/processors/post_mortem_processor.rb +16 -0
  90. data/lib/byebug/processors/script_processor.rb +49 -0
  91. data/lib/byebug/remote.rb +13 -7
  92. data/lib/byebug/runner.rb +39 -65
  93. data/lib/byebug/setting.rb +4 -1
  94. data/lib/byebug/settings/post_mortem.rb +0 -16
  95. data/lib/byebug/settings/savefile.rb +1 -4
  96. data/lib/byebug/subcommands.rb +27 -29
  97. data/lib/byebug/version.rb +4 -1
  98. metadata +14 -29
  99. data/lib/byebug/commands/eval.rb +0 -43
  100. data/lib/byebug/commands/info/args.rb +0 -39
  101. data/lib/byebug/commands/info/catch.rb +0 -39
  102. data/lib/byebug/commands/pp.rb +0 -41
  103. data/lib/byebug/commands/putl.rb +0 -43
  104. data/lib/byebug/processor.rb +0 -43
  105. data/lib/byebug/processors/control_command_processor.rb +0 -48
  106. data/lib/byebug/settings/verbose.rb +0 -20
  107. data/lib/byebug/state.rb +0 -12
  108. data/lib/byebug/states/control_state.rb +0 -26
  109. data/lib/byebug/states/regular_state.rb +0 -187
  110. data/lib/byebug/subcommand_list.rb +0 -33
@@ -1,46 +1,87 @@
1
1
  module Byebug
2
2
  module Helpers
3
3
  #
4
- # Utilities used by the eval command
4
+ # Utilities to assist evaluation of code strings
5
5
  #
6
6
  module EvalHelper
7
7
  #
8
- # Run block temporarily ignoring all TracePoint events.
8
+ # Evaluates +expression+ that might manipulate threads
9
9
  #
10
- # Used to evaluate stuff within Byebug's prompt. Otherwise, any code
11
- # creating new threads won't be properly evaluated because new threads
12
- # will get blocked by byebug's main thread.
10
+ # @param expression [String] Expression to evaluate
13
11
  #
14
- def allowing_other_threads
15
- Byebug.unlock
16
- res = yield
17
- Byebug.lock
18
- res
12
+ def thread_safe_eval(expression)
13
+ allowing_other_threads { single_thread_eval(expression) }
14
+ end
15
+
16
+ #
17
+ # Evaluates an +expression+ that doesn't deal with threads
18
+ #
19
+ # @param expression [String] Expression to evaluate
20
+ #
21
+ def single_thread_eval(expression)
22
+ return error_eval(expression) if Setting[:stack_on_error]
23
+
24
+ warning_eval(expression)
25
+ end
26
+
27
+ #
28
+ # Evaluates a string containing Ruby code in a specific binding,
29
+ # returning nil in an error happens.
30
+ #
31
+ def silent_eval(str, binding = frame._binding)
32
+ binding.eval(str)
33
+ rescue StandardError, ScriptError
34
+ nil
35
+ end
36
+
37
+ #
38
+ # Evaluates a string containing Ruby code in a specific binding,
39
+ # handling the errors at an error level.
40
+ #
41
+ def error_eval(str, binding = frame._binding)
42
+ safe_eval(str, binding) { |e| error_msg(e) }
19
43
  end
20
44
 
21
45
  #
22
- # Get current binding and yield it to the given block
46
+ # Evaluates a string containing Ruby code in a specific binding,
47
+ # handling the errors at a warning level.
23
48
  #
24
- def run_with_binding
25
- binding = get_binding
26
- yield binding
49
+ def warning_eval(str, binding = frame._binding)
50
+ safe_eval(str, binding) { |e| warning_msg(e) }
51
+ end
52
+
53
+ private
54
+
55
+ def safe_eval(str, binding)
56
+ binding.eval(str)
57
+ rescue StandardError, ScriptError => e
58
+ raise(e, yield(e))
59
+ end
60
+
61
+ def error_msg(e)
62
+ at = e.backtrace
63
+ locations = ["#{at.shift}: #{e.class} Exception(#{e.message})"]
64
+ locations += at.map { |path| "\tfrom #{path}" }
65
+
66
+ pr('eval.exception', text_message: locations.join("\n"))
67
+ end
68
+
69
+ def warning_msg(e)
70
+ pr('eval.exception', text_message: "#{e.class} Exception: #{e.message}")
27
71
  end
28
72
 
29
73
  #
30
- # Evaluate +expression+ using +binding+
74
+ # Run block temporarily ignoring all TracePoint events.
31
75
  #
32
- # @param binding [Binding] Context where to evaluate the expression
33
- # @param expression [String] Expression to evaluation
34
- # @param stack_on_error [Boolean] Whether to show a stack trace on error.
76
+ # Used to evaluate stuff within Byebug's prompt. Otherwise, any code
77
+ # creating new threads won't be properly evaluated because new threads
78
+ # will get blocked by byebug's main thread.
35
79
  #
36
- def eval_with_setting(binding, expression, stack_on_error)
37
- allowing_other_threads do
38
- if stack_on_error
39
- bb_eval(expression, binding)
40
- else
41
- bb_warning_eval(expression, binding)
42
- end
43
- end
80
+ def allowing_other_threads
81
+ Byebug.unlock
82
+ res = yield
83
+ Byebug.lock
84
+ res
44
85
  end
45
86
  end
46
87
  end
@@ -33,13 +33,28 @@ module Byebug
33
33
  # Regularize file name.
34
34
  #
35
35
  def normalize(filename)
36
- return filename if ['(irb)', '-e'].include?(filename)
36
+ return filename if virtual_file?(filename)
37
37
 
38
38
  return File.basename(filename) if Setting[:basename]
39
39
 
40
- path = File.expand_path(filename)
40
+ File.exist?(filename) ? File.realpath(filename) : filename
41
+ end
42
+
43
+ #
44
+ # A short version of a long path
45
+ #
46
+ def shortpath(fullpath)
47
+ components = Pathname(fullpath).each_filename.to_a
48
+ return fullpath if components.size <= 2
41
49
 
42
- File.exist?(path) ? File.realpath(path) : filename
50
+ File.join('...', components[-3..-1])
51
+ end
52
+
53
+ #
54
+ # True for special files like -e, false otherwise
55
+ #
56
+ def virtual_file?(name)
57
+ ['(irb)', '-e'].include?(name)
43
58
  end
44
59
  end
45
60
  end
@@ -4,21 +4,36 @@ module Byebug
4
4
  # Utilities to assist frame navigation
5
5
  #
6
6
  module FrameHelper
7
- def switch_to_frame(frame_no)
8
- frame_no >= 0 ? frame_no : @state.context.stack_size + frame_no
7
+ def switch_to_frame(frame)
8
+ new_frame = index_from_start(frame)
9
+ return frame_err('c_frame') if Frame.new(context, new_frame).c_frame?
10
+
11
+ adjust_frame(new_frame)
9
12
  end
10
13
 
11
- def navigate_to_frame(jump_no)
12
- return if jump_no == 0
14
+ def jump_frames(steps)
15
+ adjust_frame(navigate_to_frame(steps))
16
+ end
17
+
18
+ private
19
+
20
+ def adjust_frame(new_frame)
21
+ return frame_err('too_low') if new_frame >= context.stack_size
22
+ return frame_err('too_high') if new_frame < 0
13
23
 
24
+ context.frame = new_frame
25
+ processor.prev_line = nil
26
+ end
27
+
28
+ def navigate_to_frame(jump_no)
14
29
  current_jumps = 0
15
- current_pos = @state.frame
30
+ current_pos = context.frame.pos
16
31
 
17
32
  loop do
18
33
  current_pos += direction(jump_no)
19
- break if current_pos < 0 || current_pos >= @state.context.stack_size
34
+ break if out_of_bounds?(current_pos)
20
35
 
21
- next if @state.c_frame?(current_pos)
36
+ next if Frame.new(context, current_pos).c_frame?
22
37
 
23
38
  current_jumps += 1
24
39
  break if current_jumps == jump_no.abs
@@ -27,42 +42,14 @@ module Byebug
27
42
  current_pos
28
43
  end
29
44
 
30
- def adjust_frame(frame, absolute)
31
- if absolute
32
- abs_frame = switch_to_frame(frame)
33
- if @state.c_frame?(abs_frame)
34
- return errmsg(pr('frame.errors.c_frame'))
35
- end
36
- else
37
- abs_frame = navigate_to_frame(frame)
38
- end
39
-
40
- if abs_frame >= @state.context.stack_size
41
- return errmsg(pr('frame.errors.too_low'))
42
- elsif abs_frame < 0
43
- return errmsg(pr('frame.errors.too_high'))
44
- end
45
-
46
- @state.frame = abs_frame
47
- @state.file = @state.context.frame_file(@state.frame)
48
- @state.line = @state.context.frame_line(@state.frame)
49
- @state.prev_line = nil
45
+ def out_of_bounds?(pos)
46
+ !(0...context.stack_size).include?(pos)
50
47
  end
51
48
 
52
- def get_pr_arguments(frame_no)
53
- file = @state.frame_file(frame_no)
54
- full_path = File.expand_path(file)
55
- line = @state.frame_line(frame_no)
56
- call = @state.frame_call(frame_no)
57
- mark = @state.frame_mark(frame_no)
58
- pos = @state.frame_pos(frame_no)
59
-
60
- { mark: mark, pos: pos, call: call, file: file, line: line,
61
- full_path: full_path }
49
+ def frame_err(msg)
50
+ errmsg(pr("frame.errors.#{msg}"))
62
51
  end
63
52
 
64
- private
65
-
66
53
  #
67
54
  # @param [Integer] A positive or negative integer
68
55
  #
@@ -71,6 +58,16 @@ module Byebug
71
58
  def direction(step)
72
59
  step / step.abs
73
60
  end
61
+
62
+ #
63
+ # Convert a possibly negative index to a positive index from the start
64
+ # of the callstack. -1 is the last position in the stack and so on.
65
+ #
66
+ # @param [Integer] Integer to be converted in a proper positive index.
67
+ #
68
+ def index_from_start(i)
69
+ i >= 0 ? i : context.stack_size + i
70
+ end
74
71
  end
75
72
  end
76
73
  end
@@ -9,7 +9,7 @@ module Byebug
9
9
  #
10
10
  # If either +min+ or +max+ is nil, that value has no bound.
11
11
  #
12
- # TODO: Remove the `cmd` parameter. It has nothing to do with the methods
12
+ # TODO: Remove the `cmd` parameter. It has nothing to do with the method's
13
13
  # purpose.
14
14
  #
15
15
  def get_int(str, cmd, min = nil, max = nil)
@@ -46,18 +46,6 @@ module Byebug
46
46
  end
47
47
  end
48
48
 
49
- #
50
- # Temporarily disable output to $stderr
51
- #
52
- def without_stderr
53
- stderr = $stderr
54
- $stderr.reopen(IO::NULL)
55
-
56
- yield
57
- ensure
58
- $stderr.reopen(stderr)
59
- end
60
-
61
49
  #
62
50
  # @return +str+ as an integer or 1 if +str+ is empty.
63
51
  #
@@ -69,6 +57,20 @@ module Byebug
69
57
 
70
58
  steps
71
59
  end
60
+
61
+ private
62
+
63
+ #
64
+ # Temporarily disable output to $stderr
65
+ #
66
+ def without_stderr
67
+ stderr = $stderr
68
+ $stderr.reopen(IO::NULL)
69
+
70
+ yield
71
+ ensure
72
+ $stderr.reopen(stderr)
73
+ end
72
74
  end
73
75
  end
74
76
  end
@@ -0,0 +1,21 @@
1
+ module Byebug
2
+ module Helpers
3
+ #
4
+ # Utilities for managing gem paths
5
+ #
6
+ module PathHelper
7
+ def bin_file
8
+ @bin_file ||= Gem.bin_path('byebug', 'byebug')
9
+ end
10
+
11
+ def lib_files
12
+ @lib_files ||= Dir.glob(File.expand_path('../../../**/*.rb', __FILE__))
13
+ end
14
+
15
+ def all_files
16
+ @all_files ||=
17
+ Dir.glob(File.expand_path('../../../../**/*.rb', __FILE__))
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module Byebug
2
+ module Helpers
3
+ #
4
+ # Reflection utilitie
5
+ #
6
+ module ReflectionHelper
7
+ #
8
+ # List of "command" classes in the including module
9
+ #
10
+ def commands
11
+ constants(false)
12
+ .map { |const| const_get(const, false) }
13
+ .select { |c| c.is_a?(Class) && c.name =~ /[a-z]Command$/ }
14
+ end
15
+ end
16
+ end
17
+ end
@@ -4,22 +4,24 @@ module Byebug
4
4
  # Utilities for thread subcommands
5
5
  #
6
6
  module ThreadHelper
7
- def display_context(context)
8
- puts pr('thread.context', thread_arguments(context))
7
+ def display_context(ctx)
8
+ puts pr('thread.context', thread_arguments(ctx))
9
9
  end
10
10
 
11
- def thread_arguments(context)
12
- status_flag = if context.suspended?
11
+ def thread_arguments(ctx)
12
+ status_flag = if ctx.suspended?
13
13
  '$'
14
14
  else
15
- context.thread == Thread.current ? '+' : ' '
15
+ current_thread?(ctx) ? '+' : ' '
16
16
  end
17
- debug_flag = context.ignored? ? '!' : ' '
18
17
 
19
- if context == Byebug.current_context
20
- file_line = "#{@state.file}:#{@state.line}"
18
+ debug_flag = ctx.ignored? ? '!' : ' '
19
+
20
+ # Check whether it is Byebug.current_context or context
21
+ if ctx == Byebug.current_context
22
+ file_line = context.location
21
23
  else
22
- backtrace = context.thread.backtrace_locations
24
+ backtrace = ctx.thread.backtrace_locations
23
25
  if backtrace && backtrace[0]
24
26
  file_line = "#{backtrace[0].path}:#{backtrace[0].lineno}"
25
27
  end
@@ -28,21 +30,25 @@ module Byebug
28
30
  {
29
31
  status_flag: status_flag,
30
32
  debug_flag: debug_flag,
31
- id: context.thnum,
32
- thread: context.thread.inspect,
33
+ id: ctx.thnum,
34
+ thread: ctx.thread.inspect,
33
35
  file_line: file_line || '',
34
36
  pid: Process.pid,
35
- status: context.thread.status,
36
- current: (context.thread == Thread.current)
37
+ status: ctx.thread.status,
38
+ current: current_thread?(ctx)
37
39
  }
38
40
  end
39
41
 
42
+ def current_thread?(ctx)
43
+ ctx.thread == Thread.current
44
+ end
45
+
40
46
  def context_from_thread(thnum)
41
47
  ctx = Byebug.contexts.find { |c| c.thnum.to_s == thnum }
42
48
 
43
49
  err = case
44
50
  when ctx.nil? then pr('thread.errors.no_thread')
45
- when ctx == @state.context then pr('thread.errors.current_thread')
51
+ when ctx == context then pr('thread.errors.current_thread')
46
52
  when ctx.ignored? then pr('thread.errors.ignored', arg: thnum)
47
53
  end
48
54
 
@@ -39,18 +39,23 @@ module Byebug
39
39
  end
40
40
 
41
41
  def enable_disable_display(is_enable, args)
42
- display = @state.display
43
- return errmsg(pr('toggle.errors.no_display')) if 0 == display.size
42
+ return errmsg(pr('toggle.errors.no_display')) if 0 == n_displays
44
43
 
45
- selected_displays = args.nil? ? [1..display.size + 1] : args.split(/ +/)
44
+ selected_displays = args ? args.split(/ +/) : [1..n_displays + 1]
46
45
 
47
46
  selected_displays.each do |pos|
48
- pos, err = get_int(pos, "#{is_enable} display", 1, display.size)
47
+ pos, err = get_int(pos, "#{is_enable} display", 1, n_displays)
49
48
  return errmsg(err) unless err.nil?
50
49
 
51
- display[pos - 1][0] = ('enable' == is_enable)
50
+ Byebug.displays[pos - 1][0] = ('enable' == is_enable)
52
51
  end
53
52
  end
53
+
54
+ private
55
+
56
+ def n_displays
57
+ Byebug.displays.size
58
+ end
54
59
  end
55
60
  end
56
61
  end
@@ -1,22 +1,18 @@
1
+ require 'byebug/helpers/eval'
2
+
1
3
  module Byebug
2
4
  module Helpers
3
5
  #
4
6
  # Utilities for variable subcommands
5
7
  #
6
8
  module VarHelper
7
- def var_list(ary, b = get_binding)
8
- vars = ary.sort.map do |v|
9
- s = begin
10
- b.eval(v.to_s).inspect
11
- rescue
12
- begin
13
- b.eval(v.to_s).to_s
14
- rescue
15
- '*Error in evaluation*'
16
- end
17
- end
18
- [v, s]
9
+ include EvalHelper
10
+
11
+ def var_list(ary, binding = context.frame._binding)
12
+ vars = ary.sort.map do |name|
13
+ [name, safe_inspect(silent_eval(name.to_s, binding))]
19
14
  end
15
+
20
16
  puts prv(vars, 'instance')
21
17
  end
22
18
 
@@ -29,17 +25,42 @@ module Byebug
29
25
  end
30
26
 
31
27
  def var_instance(str)
32
- obj = bb_warning_eval(str || 'self')
28
+ obj = single_thread_eval(str || 'self')
33
29
 
34
30
  var_list(obj.instance_variables, obj.instance_eval { binding })
35
31
  end
36
32
 
37
33
  def var_local
38
- locals = @state.context.frame_locals
39
- cur_self = @state.context.frame_self(@state.frame)
34
+ locals = context.frame.locals
35
+ cur_self = context.frame._self
40
36
  locals[:self] = cur_self unless cur_self.to_s == 'main'
41
37
  puts prv(locals.keys.sort.map { |k| [k, locals[k]] }, 'instance')
42
38
  end
39
+
40
+ def var_args
41
+ args = context.frame.args
42
+ return if args == [[:rest]]
43
+
44
+ all_locals = context.frame.locals
45
+ arg_values = args.map { |arg| arg[1] }
46
+
47
+ locals = all_locals.select { |k, _| arg_values.include?(k) }
48
+ puts prv(locals.keys.sort.map { |k| [k, locals[k]] }, 'instance')
49
+ end
50
+
51
+ private
52
+
53
+ def safe_inspect(var)
54
+ var.inspect
55
+ rescue
56
+ safe_to_s(var)
57
+ end
58
+
59
+ def safe_to_s(var)
60
+ var.to_s
61
+ rescue
62
+ '*Error in evaluation*'
63
+ end
43
64
  end
44
65
  end
45
66
  end