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
@@ -5,59 +5,66 @@ module Byebug
5
5
  # at_breakpoint, at_catchpoint, at_tracing, at_line and at_return callbacks
6
6
  #
7
7
  class Context
8
- class << self
9
- def stack_size(byebug_frames = false)
10
- backtrace = Thread.current.backtrace_locations(0)
11
- return 0 unless backtrace
8
+ #
9
+ # List of files byebug will ignore while debugging
10
+ #
11
+ def self.ignored_files
12
+ Byebug.mode == :standalone ? lib_files + [bin_file] : lib_files
13
+ end
14
+
15
+ def self.bin_file
16
+ @bin_file ||= Gem.bin_path('byebug', 'byebug')
17
+ end
12
18
 
13
- unless byebug_frames
14
- backtrace = backtrace.drop_while { |l| !ignored(l.path) }
15
- .drop_while { |l| ignored(l.path) }
16
- .take_while { |l| !ignored(l.path) }
17
- end
19
+ def self.lib_files
20
+ @lib_files ||= Dir.glob(File.expand_path('../../**/*.rb', __FILE__))
21
+ end
22
+
23
+ #
24
+ # Tells whether a file is ignored by the debugger.
25
+ #
26
+ # @param path [String] filename to be checked.
27
+ #
28
+ def ignored_file?(path)
29
+ self.class.ignored_files.include?(path)
30
+ end
18
31
 
19
- backtrace.size
20
- end
32
+ def stack_size
33
+ return 0 unless backtrace
21
34
 
22
- def ignored(path)
23
- IGNORED_FILES.include?(path)
24
- end
25
- private :ignored
35
+ backtrace.drop_while { |l| ignored_file?(l.first.path) }
36
+ .take_while { |l| !ignored_file?(l.first.path) }
37
+ .size
26
38
  end
27
39
 
28
40
  def interrupt
29
41
  step_into 1
30
42
  end
31
43
 
44
+ #
45
+ # Gets local variables for a frame.
46
+ #
47
+ # @param frame_no Frame index in the backtrace. Defaults to 0.
48
+ #
49
+ # TODO: Use brand new local_variable_{get,set,defined?} for rubies >= 2.1
50
+ #
32
51
  def frame_locals(frame_no = 0)
33
- bind = frame_binding frame_no
34
- eval 'local_variables.inject({}){|h, v| h[v] = eval(v.to_s); h}', bind
35
- end
36
-
37
- def c_frame_args(frame_no)
38
- myself = frame_self frame_no
39
- return [] unless myself.to_s != 'main'
40
- myself.send(:method, frame_method(frame_no)).parameters
41
- end
52
+ bind = frame_binding(frame_no)
53
+ return [] unless bind
42
54
 
43
- def ruby_frame_args(bind)
44
- return [] unless eval '__method__', bind
45
- begin
46
- eval 'self.method(__method__).parameters', bind
47
- rescue NameError => e
48
- puts "WARNING: Got exception #{e.class}: \"#{e.message}\" " \
49
- 'while retreving parameters from frame'
50
- return []
51
- end
55
+ bind.eval('local_variables.inject({}){|h, v| h[v] = eval(v.to_s); h}')
52
56
  end
53
57
 
58
+ #
59
+ # Gets current method arguments for a frame.
60
+ #
61
+ # @param frame_no Frame index in the backtrace. Defaults to 0.
62
+ #
54
63
  def frame_args(frame_no = 0)
55
- bind = frame_binding frame_no
56
- if bind.nil?
57
- c_frame_args frame_no
58
- else
59
- ruby_frame_args bind
60
- end
64
+ bind = frame_binding(frame_no)
65
+ return c_frame_args(frame_no) unless bind
66
+
67
+ ruby_frame_args(bind)
61
68
  end
62
69
 
63
70
  def handler
@@ -73,15 +80,44 @@ module Byebug
73
80
  end
74
81
 
75
82
  def at_tracing(file, line)
76
- handler.at_tracing(self, file, line)
83
+ handler.at_tracing(self, file, line) unless ignored_file?(file)
77
84
  end
78
85
 
79
86
  def at_line(file, line)
80
- handler.at_line(self, file, line) unless IGNORED_FILES.include?(file)
87
+ handler.at_line(self, file, line) unless ignored_file?(file)
81
88
  end
82
89
 
83
90
  def at_return(file, line)
84
- handler.at_return(self, file, line)
91
+ handler.at_return(self, file, line) unless ignored_file?(file)
92
+ end
93
+
94
+ private
95
+
96
+ #
97
+ # Gets method arguments for a c-frame.
98
+ #
99
+ # @param frame_no Frame index in the backtrace.
100
+ #
101
+ def c_frame_args(frame_no)
102
+ myself = frame_self(frame_no)
103
+ return [] unless myself.to_s != 'main'
104
+
105
+ myself.method(frame_method(frame_no)).parameters
106
+ end
107
+
108
+ #
109
+ # Gets method arguments for a ruby-frame.
110
+ #
111
+ # @param bind Binding for the ruby-frame.
112
+ #
113
+ def ruby_frame_args(bind)
114
+ return [] unless bind.eval('__method__')
115
+
116
+ bind.eval('method(__method__).parameters')
117
+ rescue NameError => e
118
+ Byebug.errmsg \
119
+ "Exception #{e.class} (#{e.message}) while retreving frame params"
120
+ []
85
121
  end
86
122
  end
87
123
  end
@@ -4,72 +4,110 @@ require 'byebug/context'
4
4
  require 'byebug/breakpoint'
5
5
  require 'byebug/interface'
6
6
  require 'byebug/processor'
7
- require 'byebug/setting'
8
7
  require 'byebug/remote'
9
-
10
- require 'stringio'
11
- require 'tracer'
12
- require 'linecache19'
8
+ require 'byebug/printers/plain'
13
9
 
14
10
  module Byebug
15
- #
16
- # List of files byebug will ignore while debugging
17
- #
18
- IGNORED_FILES = Dir.glob(File.expand_path('../**/*.rb', __FILE__))
11
+ extend self
12
+
13
+ class NoScript < StandardError
14
+ end
15
+
16
+ class NonExistentScript < StandardError
17
+ end
19
18
 
20
19
  #
21
20
  # Configuration file used for startup commands. Default value is .byebugrc
22
21
  #
23
22
  INIT_FILE = '.byebugrc' unless defined?(INIT_FILE)
24
23
 
25
- class << self
26
- attr_accessor :handler, :debugged_program
27
-
28
- extend Forwardable
29
- def_delegators :handler, :errmsg, :puts
30
- end
24
+ #
25
+ # Main debugger's processor
26
+ #
27
+ attr_accessor :handler
28
+ self.handler = CommandProcessor.new
31
29
 
32
- Byebug.handler = CommandProcessor.new
30
+ #
31
+ # Main debugger's printer
32
+ #
33
+ attr_accessor :printer
34
+ self.printer = Printers::Plain.new
33
35
 
34
- def self.source_reload
35
- hsh = 'SCRIPT_LINES__'
36
- Object.send(:remove_const, hsh) if Object.const_defined?(hsh)
37
- Object.const_set(hsh, {})
38
- end
36
+ extend Forwardable
37
+ def_delegators :handler, :errmsg, :puts
39
38
 
40
39
  #
41
- # Byebug's interface is its handler's interface
40
+ # Running mode of the debugger. Can be either:
42
41
  #
43
- def self.interface=(value)
44
- handler.interface = value
45
- end
42
+ # * :attached => Attached to a running program through the `byebug` method.
43
+ # * :standalone => Started through `bin/byebug` script.
44
+ #
45
+ attr_accessor :mode
46
46
 
47
47
  #
48
48
  # Runs normal byebug initialization scripts.
49
49
  #
50
50
  # Reads and executes the commands from init file (if any) in the current
51
- # working directory. This is only done if the current directory is
52
- # different from your home directory. Thus, you can have more than one init
53
- # file, one generic in your home directory, and another, specific to the
54
- # program you are debugging, in the directory where you invoke byebug.
55
- #
56
- def self.run_init_script(out = handler.interface)
57
- cwd_script = File.expand_path(File.join('.', INIT_FILE))
58
- run_script(cwd_script, out, true) if File.exist?(cwd_script)
59
-
60
- home_script = File.expand_path(File.join(ENV['HOME'].to_s, INIT_FILE))
61
- if File.exist?(home_script) && cwd_script != home_script
62
- run_script(home_script, out, true)
51
+ # working directory. This is only done if the current directory is different
52
+ # from your home directory. Thus, you can have more than one init file, one
53
+ # generic in your home directory, and another, specific to the program you
54
+ # are debugging, in the directory where you invoke byebug.
55
+ #
56
+ def run_init_script
57
+ home_rc = File.expand_path(File.join(ENV['HOME'].to_s, INIT_FILE))
58
+ run_script(home_rc) if File.exist?(home_rc)
59
+
60
+ cwd_rc = File.expand_path(File.join('.', INIT_FILE))
61
+ run_script(cwd_rc) if File.exist?(cwd_rc) && cwd_rc != home_rc
62
+ end
63
+
64
+ #
65
+ # Extracts debugged program from command line args
66
+ #
67
+ def setup_cmd_line_args
68
+ unless $PROGRAM_NAME.include?('bin/byebug')
69
+ self.mode = :attached
70
+ return
63
71
  end
72
+
73
+ self.mode = :standalone
74
+
75
+ fail(NoScript, 'You must specify a program to debug...') if $ARGV.empty?
76
+
77
+ program = which($ARGV.shift)
78
+ program = which($ARGV.shift) if program == which('ruby')
79
+ fail(NonExistentScript, "The script doesn't exist") unless program
80
+
81
+ $PROGRAM_NAME = program
64
82
  end
65
83
 
84
+ private
85
+
66
86
  #
67
87
  # Runs a script file
68
88
  #
69
- def self.run_script(file, out = handler.interface, verbose = false)
70
- interface = ScriptInterface.new(File.expand_path(file), out)
89
+ def run_script(file, verbose = false)
90
+ interface = ScriptInterface.new(file, verbose)
71
91
  processor = ControlCommandProcessor.new(interface)
72
- processor.process_commands(verbose)
92
+ processor.process_commands
93
+ end
94
+
95
+ #
96
+ # Cross-platform way of finding an executable in the $PATH.
97
+ # Borrowed from: http://stackoverflow.com/questions/2108727
98
+ #
99
+ def which(cmd)
100
+ return File.expand_path(cmd) if File.exist?(cmd)
101
+
102
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
103
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
104
+ exts.each do |ext|
105
+ exe = File.join(path, "#{cmd}#{ext}")
106
+ return exe if File.executable?(exe) && !File.directory?(exe)
107
+ end
108
+ end
109
+
110
+ nil
73
111
  end
74
112
  end
75
113
 
@@ -1,24 +1,65 @@
1
1
  module Byebug
2
2
  #
3
- # Miscelaneous Utilities
3
+ # Utilities for interaction with files
4
4
  #
5
- module MiscUtils
5
+ module FileFunctions
6
6
  #
7
- # Cross-platform way of finding an executable in the $PATH.
8
- # Borrowed from: http://stackoverflow.com/questions/2108727
7
+ # Reads lines of source file +filename+ into an array
9
8
  #
10
- def which(cmd)
11
- return File.expand_path(cmd) if File.exist?(cmd)
9
+ def get_lines(filename)
10
+ File.foreach(filename).reduce([]) { |a, e| a << e.chomp }
11
+ end
12
12
 
13
- exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
14
- ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
15
- exts.each do |ext|
16
- exe = File.join(path, "#{cmd}#{ext}")
17
- return exe if File.executable?(exe) && !File.directory?(exe)
18
- end
13
+ #
14
+ # Reads line number +lineno+ from file named +filename+
15
+ #
16
+ def get_line(filename, lineno)
17
+ File.open(filename) do |f|
18
+ f.gets until f.lineno == lineno - 1
19
+ f.gets
19
20
  end
21
+ end
22
+
23
+ #
24
+ # Returns the number of lines in file +filename+ in a portable,
25
+ # one-line-at-a-time way.
26
+ #
27
+ def n_lines(filename)
28
+ File.foreach(filename).reduce(0) { |a, _e| a + 1 }
29
+ end
30
+
31
+ #
32
+ # Regularize file name.
33
+ #
34
+ def normalize(filename)
35
+ return filename if ['(irb)', '-e'].include?(filename)
36
+
37
+ return File.basename(filename) if Setting[:basename]
38
+
39
+ path = File.expand_path(filename)
40
+
41
+ File.exist?(path) ? File.realpath(path) : filename
42
+ end
43
+ end
44
+
45
+ #
46
+ # Utilities for interaction with files
47
+ #
48
+ module StringFunctions
49
+ #
50
+ # Converts +str+ from an_underscored-or-dasherized_string to
51
+ # ACamelizedString.
52
+ #
53
+ def camelize(str)
54
+ str.dup.split(/[_-]/).map(&:capitalize).join('')
55
+ end
20
56
 
21
- nil
57
+ #
58
+ # Improves indentation and spacing in +str+ for readability in Byebug's
59
+ # command prompt.
60
+ #
61
+ def prettify(str)
62
+ "\n" + str.gsub(/^ {8}/, '') + "\n"
22
63
  end
23
64
  end
24
65
 
@@ -32,45 +73,20 @@ module Byebug
32
73
  #
33
74
  def get_int(str, cmd, min = nil, max = nil)
34
75
  if str !~ /\A[0-9]+\z/
35
- return nil, "\"#{cmd}\" argument \"#{str}\" needs to be a number"
76
+ return nil, pr('parse.errors.int.not_number', cmd: cmd, str: str)
36
77
  end
37
78
 
38
79
  int = str.to_i
39
80
  if min && int < min
40
- return min, "\"#{cmd}\" argument \"#{str}\" needs to be at least #{min}"
81
+ return min, pr('parse.errors.int.too_low', cmd: cmd, str: str, min: min)
41
82
  elsif max && int > max
42
- return max, "\"#{cmd}\" argument \"#{str}\" needs to be at most #{max}"
83
+ return max, pr('parse.errors.int.too_high',
84
+ cmd: cmd, str: str, max: max)
43
85
  end
44
86
 
45
87
  int
46
88
  end
47
89
 
48
- #
49
- # Fills SCRIPT_LINES__ entry for <filename> if not already filled.
50
- #
51
- def lines(filename)
52
- SCRIPT_LINES__[filename] ||= File.readlines(filename)
53
- end
54
-
55
- #
56
- # Gets all lines in a source code file
57
- #
58
- def get_lines(filename)
59
- return nil unless File.exist?(filename)
60
-
61
- lines(filename)
62
- end
63
-
64
- #
65
- # Gets a single line in a source code file
66
- #
67
- def get_line(filename, lineno)
68
- lines = get_lines(filename)
69
- return nil unless lines
70
-
71
- lines[lineno - 1]
72
- end
73
-
74
90
  #
75
91
  # Returns true if code is syntactically correct for Ruby.
76
92
  #
@@ -44,6 +44,8 @@ module Byebug
44
44
  # Adds a new command to Readline's history.
45
45
  #
46
46
  def push(cmd)
47
+ return if ignore?(cmd)
48
+
47
49
  self.size += 1
48
50
  Readline::HISTORY.push(cmd)
49
51
  end
@@ -64,11 +66,20 @@ module Byebug
64
66
 
65
67
  commands = Readline::HISTORY.to_a.last(show_size)
66
68
 
67
- (self.size - show_size + 1..self.size).to_a.zip(commands).map do |l|
69
+ last_ids(show_size).zip(commands).map do |l|
68
70
  format('%5d %s', l[0], l[1])
69
71
  end.join("\n") + "\n"
70
72
  end
71
73
 
74
+ #
75
+ # Array of ids of the last n commands.
76
+ #
77
+ def last_ids(n)
78
+ from, to = 1 + self.size - n, self.size
79
+
80
+ (from..to).to_a
81
+ end
82
+
72
83
  #
73
84
  # Max number of commands to be displayed when no size has been specified.
74
85
  #