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
@@ -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
  #