byebug 5.0.0 → 6.0.0

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