debug 1.4.0 → 1.6.1

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.
data/lib/debug/session.rb CHANGED
@@ -7,7 +7,7 @@ return if ENV['RUBY_DEBUG_ENABLE'] == '0'
7
7
  if $0.end_with?('bin/bundle') && ARGV.first == 'exec'
8
8
  trace_var(:$0) do |file|
9
9
  trace_var(:$0, nil)
10
- if /-r (#{__dir__}\S+)/ =~ ENV['RUBYOPT']
10
+ if /-r (#{Regexp.escape(__dir__)}\S+)/ =~ ENV['RUBYOPT']
11
11
  lib = $1
12
12
  $LOADED_FEATURES.delete_if{|path| path.start_with?(__dir__)}
13
13
  ENV['RUBY_DEBUG_INITIAL_SUSPEND_PATH'] = file
@@ -19,6 +19,14 @@ if $0.end_with?('bin/bundle') && ARGV.first == 'exec'
19
19
  return
20
20
  end
21
21
 
22
+ # restore RUBYOPT
23
+ if (added_opt = ENV['RUBY_DEBUG_ADDED_RUBYOPT']) &&
24
+ (rubyopt = ENV['RUBYOPT']) &&
25
+ rubyopt.start_with?(added_opt)
26
+ ENV['RUBYOPT'] = rubyopt.delete_prefix(rubyopt)
27
+ ENV['RUBY_DEBUG_ADDED_RUBYOPT'] = nil
28
+ end
29
+
22
30
  require_relative 'frame_info'
23
31
  require_relative 'config'
24
32
  require_relative 'thread_client'
@@ -31,7 +39,8 @@ $LOADED_FEATURES << 'debug.rb'
31
39
  $LOADED_FEATURES << File.expand_path(File.join(__dir__, '..', 'debug.rb'))
32
40
  require 'debug' # invalidate the $LOADED_FEATURE cache
33
41
 
34
- require 'json' if ENV['RUBY_DEBUG_TEST_MODE']
42
+ require 'json' if ENV['RUBY_DEBUG_TEST_UI'] == 'terminal'
43
+ require 'pp'
35
44
 
36
45
  class RubyVM::InstructionSequence
37
46
  def traceable_lines_norec lines
@@ -56,23 +65,22 @@ class RubyVM::InstructionSequence
56
65
 
57
66
  def type
58
67
  self.to_a[9]
59
- end
68
+ end unless method_defined?(:type)
60
69
 
61
- def argc
62
- self.to_a[4][:arg_size]
63
- end
64
-
65
- def locals
66
- self.to_a[10]
67
- end
70
+ def parameters_symbols
71
+ ary = self.to_a
72
+ argc = ary[4][:arg_size]
73
+ locals = ary.to_a[10]
74
+ locals[0...argc]
75
+ end unless method_defined?(:parameters_symbols)
68
76
 
69
77
  def last_line
70
78
  self.to_a[4][:code_location][2]
71
- end
79
+ end unless method_defined?(:last_line)
72
80
 
73
81
  def first_line
74
82
  self.to_a[4][:code_location][0]
75
- end
83
+ end unless method_defined?(:first_line)
76
84
  end
77
85
 
78
86
  module DEBUGGER__
@@ -82,8 +90,10 @@ module DEBUGGER__
82
90
  class Session
83
91
  attr_reader :intercepted_sigint_cmd, :process_group
84
92
 
85
- def initialize ui
86
- @ui = ui
93
+ include Color
94
+
95
+ def initialize
96
+ @ui = nil
87
97
  @sr = SourceRepository.new
88
98
  @bps = {} # bp.key => bp
89
99
  # [file, line] => LineBreakpoint
@@ -104,13 +114,13 @@ module DEBUGGER__
104
114
  @intercept_trap_sigint = false
105
115
  @intercepted_sigint_cmd = 'DEFAULT'
106
116
  @process_group = ProcessGroup.new
107
- @subsession = nil
117
+ @subsession_stack = []
108
118
 
109
119
  @frame_map = {} # for DAP: {id => [threadId, frame_depth]} and CDP: {id => frame_depth}
110
120
  @var_map = {1 => [:globals], } # {id => ...} for DAP
111
121
  @src_map = {} # {id => src}
112
122
 
113
- @script_paths = [File.absolute_path($0)] # for CDP
123
+ @scr_id_map = {} # for CDP
114
124
  @obj_map = {} # { object_id => ... } for CDP
115
125
 
116
126
  @tp_thread_begin = nil
@@ -120,9 +130,6 @@ module DEBUGGER__
120
130
  @tp_load_script.enable
121
131
 
122
132
  @thread_stopper = thread_stopper
123
-
124
- activate
125
-
126
133
  self.postmortem = CONFIG[:postmortem]
127
134
  end
128
135
 
@@ -134,15 +141,13 @@ module DEBUGGER__
134
141
  @bps.has_key? [file, line]
135
142
  end
136
143
 
137
- def activate on_fork: false
144
+ def activate ui = nil, on_fork: false
145
+ @ui = ui if ui
146
+
138
147
  @tp_thread_begin&.disable
139
148
  @tp_thread_begin = nil
140
149
 
141
- if on_fork
142
- @ui.activate self, on_fork: true
143
- else
144
- @ui.activate self, on_fork: false
145
- end
150
+ @ui.activate self, on_fork: on_fork
146
151
 
147
152
  q = Queue.new
148
153
  @session_server = Thread.new do
@@ -201,9 +206,14 @@ module DEBUGGER__
201
206
  deactivate
202
207
  end
203
208
 
209
+ def request_tc(req)
210
+ @tc << req
211
+ end
212
+
204
213
  def process_event evt
205
214
  # variable `@internal_info` is only used for test
206
- tc, output, ev, @internal_info, *ev_args = evt
215
+ @tc, output, ev, @internal_info, *ev_args = evt
216
+
207
217
  output.each{|str| @ui.puts str} if ev != :suspend
208
218
 
209
219
  case ev
@@ -215,20 +225,19 @@ module DEBUGGER__
215
225
  q << true
216
226
 
217
227
  when :init
218
- wait_command_loop tc
219
-
228
+ enter_subsession
229
+ wait_command_loop
220
230
  when :load
221
231
  iseq, src = ev_args
222
232
  on_load iseq, src
223
- @ui.event :load
224
- tc << :continue
233
+ request_tc :continue
225
234
 
226
235
  when :trace
227
236
  trace_id, msg = ev_args
228
237
  if t = @tracers.values.find{|t| t.object_id == trace_id}
229
238
  t.puts msg
230
239
  end
231
- tc << :continue
240
+ request_tc :continue
232
241
 
233
242
  when :suspend
234
243
  enter_subsession if ev_args.first != :replay
@@ -238,26 +247,25 @@ module DEBUGGER__
238
247
  when :breakpoint
239
248
  bp, i = bp_index ev_args[1]
240
249
  clean_bps unless bp
241
- @ui.event :suspend_bp, i, bp, tc.id
250
+ @ui.event :suspend_bp, i, bp, @tc.id
242
251
  when :trap
243
- @ui.event :suspend_trap, sig = ev_args[1], tc.id
252
+ @ui.event :suspend_trap, sig = ev_args[1], @tc.id
244
253
 
245
254
  if sig == :SIGINT && (@intercepted_sigint_cmd.kind_of?(Proc) || @intercepted_sigint_cmd.kind_of?(String))
246
255
  @ui.puts "#{@intercepted_sigint_cmd.inspect} is registered as SIGINT handler."
247
256
  @ui.puts "`sigint` command execute it."
248
257
  end
249
258
  else
250
- @ui.event :suspended, tc.id
259
+ @ui.event :suspended, @tc.id
251
260
  end
252
261
 
253
262
  if @displays.empty?
254
- wait_command_loop tc
263
+ wait_command_loop
255
264
  else
256
- tc << [:eval, :display, @displays]
265
+ request_tc [:eval, :display, @displays]
257
266
  end
258
-
259
267
  when :result
260
- raise "[BUG] not in subsession" unless @subsession
268
+ raise "[BUG] not in subsession" if @subsession_stack.empty?
261
269
 
262
270
  case ev_args.first
263
271
  when :try_display
@@ -286,14 +294,14 @@ module DEBUGGER__
286
294
  # ignore
287
295
  end
288
296
 
289
- wait_command_loop tc
297
+ wait_command_loop
290
298
 
291
299
  when :dap_result
292
300
  dap_event ev_args # server.rb
293
- wait_command_loop tc
301
+ wait_command_loop
294
302
  when :cdp_result
295
303
  cdp_event ev_args
296
- wait_command_loop tc
304
+ wait_command_loop
297
305
  end
298
306
  end
299
307
 
@@ -326,9 +334,7 @@ module DEBUGGER__
326
334
  "DEBUGGER__::SESSION"
327
335
  end
328
336
 
329
- def wait_command_loop tc
330
- @tc = tc
331
-
337
+ def wait_command_loop
332
338
  loop do
333
339
  case wait_command
334
340
  when :retry
@@ -369,7 +375,7 @@ module DEBUGGER__
369
375
  @ui.puts "(rdbg:#{@preset_command.source}) #{line}"
370
376
  end
371
377
  else
372
- @ui.puts "INTERNAL_INFO: #{JSON.generate(@internal_info)}" if ENV['RUBY_DEBUG_TEST_MODE']
378
+ @ui.puts "INTERNAL_INFO: #{JSON.generate(@internal_info)}" if ENV['RUBY_DEBUG_TEST_UI'] == 'terminal'
373
379
  line = @ui.readline prompt
374
380
  end
375
381
 
@@ -510,8 +516,8 @@ module DEBUGGER__
510
516
  # * break and run `<command>` before stopping.
511
517
  # * `b[reak] ... do: <command>`
512
518
  # * break and run `<command>`, and continue.
513
- # * `b[reak] ... path: <path_regexp>`
514
- # * break if the triggering event's path matches <path_regexp>.
519
+ # * `b[reak] ... path: <path>`
520
+ # * break if the path matches to `<path>`. `<path>` can be a regexp with `/regexp/`.
515
521
  # * `b[reak] if: <expr>`
516
522
  # * break if: `<expr>` is true at any lines.
517
523
  # * Note that this feature is super slow.
@@ -532,32 +538,6 @@ module DEBUGGER__
532
538
  end
533
539
  end
534
540
 
535
- # skip
536
- when 'bv'
537
- check_postmortem
538
- require 'json'
539
-
540
- h = Hash.new{|h, k| h[k] = []}
541
- @bps.each_value{|bp|
542
- if LineBreakpoint === bp
543
- h[bp.path] << {lnum: bp.line}
544
- end
545
- }
546
- if h.empty?
547
- # TODO: clean?
548
- else
549
- open(".rdb_breakpoints.json", 'w'){|f| JSON.dump(h, f)}
550
- end
551
-
552
- vimsrc = File.join(__dir__, 'bp.vim')
553
- system("vim -R -S #{vimsrc} #{@tc.location.path}")
554
-
555
- if File.exist?(".rdb_breakpoints.json")
556
- pp JSON.load(File.read(".rdb_breakpoints.json"))
557
- end
558
-
559
- return :retry
560
-
561
541
  # * `catch <Error>`
562
542
  # * Set breakpoint on raising `<Error>`.
563
543
  # * `catch ... if: <expr>`
@@ -566,8 +546,8 @@ module DEBUGGER__
566
546
  # * runs `<command>` before stopping.
567
547
  # * `catch ... do: <command>`
568
548
  # * stops and run `<command>`, and continue.
569
- # * `catch ... path: <path_regexp>`
570
- # * stops if the exception is raised from a path that matches <path_regexp>.
549
+ # * `catch ... path: <path>`
550
+ # * stops if the exception is raised from a `<path>`. `<path>` can be a regexp with `/regexp/`.
571
551
  when 'catch'
572
552
  check_postmortem
573
553
 
@@ -588,8 +568,8 @@ module DEBUGGER__
588
568
  # * runs `<command>` before stopping.
589
569
  # * `watch ... do: <command>`
590
570
  # * stops and run `<command>`, and continue.
591
- # * `watch ... path: <path_regexp>`
592
- # * stops if the triggering event's path matches <path_regexp>.
571
+ # * `watch ... path: <path>`
572
+ # * stops if the path matches `<path>`. `<path>` can be a regexp with `/regexp/`.
593
573
  when 'wat', 'watch'
594
574
  check_postmortem
595
575
 
@@ -635,15 +615,15 @@ module DEBUGGER__
635
615
  when 'bt', 'backtrace'
636
616
  case arg
637
617
  when /\A(\d+)\z/
638
- @tc << [:show, :backtrace, arg.to_i, nil]
618
+ request_tc [:show, :backtrace, arg.to_i, nil]
639
619
  when /\A\/(.*)\/\z/
640
620
  pattern = $1
641
- @tc << [:show, :backtrace, nil, Regexp.compile(pattern)]
621
+ request_tc [:show, :backtrace, nil, Regexp.compile(pattern)]
642
622
  when /\A(\d+)\s+\/(.*)\/\z/
643
623
  max, pattern = $1, $2
644
- @tc << [:show, :backtrace, max.to_i, Regexp.compile(pattern)]
624
+ request_tc [:show, :backtrace, max.to_i, Regexp.compile(pattern)]
645
625
  else
646
- @tc << [:show, :backtrace, nil, nil]
626
+ request_tc [:show, :backtrace, nil, nil]
647
627
  end
648
628
 
649
629
  # * `l[ist]`
@@ -656,13 +636,13 @@ module DEBUGGER__
656
636
  when 'l', 'list'
657
637
  case arg ? arg.strip : nil
658
638
  when /\A(\d+)\z/
659
- @tc << [:show, :list, {start_line: arg.to_i - 1}]
639
+ request_tc [:show, :list, {start_line: arg.to_i - 1}]
660
640
  when /\A-\z/
661
- @tc << [:show, :list, {dir: -1}]
641
+ request_tc [:show, :list, {dir: -1}]
662
642
  when /\A(\d+)-(\d+)\z/
663
- @tc << [:show, :list, {start_line: $1.to_i - 1, end_line: $2.to_i}]
643
+ request_tc [:show, :list, {start_line: $1.to_i - 1, end_line: $2.to_i}]
664
644
  when nil
665
- @tc << [:show, :list]
645
+ request_tc [:show, :list]
666
646
  else
667
647
  @ui.puts "Can not handle list argument: #{arg}"
668
648
  return :retry
@@ -686,7 +666,7 @@ module DEBUGGER__
686
666
  return :retry
687
667
  end
688
668
 
689
- @tc << [:show, :edit, arg]
669
+ request_tc [:show, :edit, arg]
690
670
 
691
671
  # * `i[nfo]`
692
672
  # * Show information about current frame (local/instance variables and defined constants).
@@ -699,8 +679,8 @@ module DEBUGGER__
699
679
  # * Show information about accessible constants except toplevel constants.
700
680
  # * `i[nfo] g[lobal[s]]`
701
681
  # * Show information about global variables
702
- # * `i[nfo] ... </pattern/>`
703
- # * Filter the output with `</pattern/>`.
682
+ # * `i[nfo] ... /regexp/`
683
+ # * Filter the output with `/regexp/`.
704
684
  # * `i[nfo] th[read[s]]`
705
685
  # * Show all threads (same as `th[read]`).
706
686
  when 'i', 'info'
@@ -713,15 +693,15 @@ module DEBUGGER__
713
693
 
714
694
  case sub
715
695
  when nil
716
- @tc << [:show, :default, pat] # something useful
696
+ request_tc [:show, :default, pat] # something useful
717
697
  when 'l', /^locals?/
718
- @tc << [:show, :locals, pat]
698
+ request_tc [:show, :locals, pat]
719
699
  when 'i', /^ivars?/i, /^instance[_ ]variables?/i
720
- @tc << [:show, :ivars, pat]
700
+ request_tc [:show, :ivars, pat]
721
701
  when 'c', /^consts?/i, /^constants?/i
722
- @tc << [:show, :consts, pat]
702
+ request_tc [:show, :consts, pat]
723
703
  when 'g', /^globals?/i, /^global[_ ]variables?/i
724
- @tc << [:show, :globals, pat]
704
+ request_tc [:show, :globals, pat]
725
705
  when 'th', /threads?/
726
706
  thread_list
727
707
  return :retry
@@ -737,7 +717,7 @@ module DEBUGGER__
737
717
  # * Show you available methods and instance variables of the given object.
738
718
  # * If the object is a class/module, it also lists its constants.
739
719
  when 'outline', 'o', 'ls'
740
- @tc << [:show, :outline, arg]
720
+ request_tc [:show, :outline, arg]
741
721
 
742
722
  # * `display`
743
723
  # * Show display setting.
@@ -746,9 +726,9 @@ module DEBUGGER__
746
726
  when 'display'
747
727
  if arg && !arg.empty?
748
728
  @displays << arg
749
- @tc << [:eval, :try_display, @displays]
729
+ request_tc [:eval, :try_display, @displays]
750
730
  else
751
- @tc << [:eval, :display, @displays]
731
+ request_tc [:eval, :display, @displays]
752
732
  end
753
733
 
754
734
  # * `undisplay`
@@ -761,7 +741,7 @@ module DEBUGGER__
761
741
  if @displays[n = $1.to_i]
762
742
  @displays.delete_at n
763
743
  end
764
- @tc << [:eval, :display, @displays]
744
+ request_tc [:eval, :display, @displays]
765
745
  when nil
766
746
  if ask "clear all?", 'N'
767
747
  @displays.clear
@@ -776,29 +756,29 @@ module DEBUGGER__
776
756
  # * `f[rame] <framenum>`
777
757
  # * Specify a current frame. Evaluation are run on specified frame.
778
758
  when 'frame', 'f'
779
- @tc << [:frame, :set, arg]
759
+ request_tc [:frame, :set, arg]
780
760
 
781
761
  # * `up`
782
762
  # * Specify the upper frame.
783
763
  when 'up'
784
- @tc << [:frame, :up]
764
+ request_tc [:frame, :up]
785
765
 
786
766
  # * `down`
787
767
  # * Specify the lower frame.
788
768
  when 'down'
789
- @tc << [:frame, :down]
769
+ request_tc [:frame, :down]
790
770
 
791
771
  ### Evaluate
792
772
 
793
773
  # * `p <expr>`
794
774
  # * Evaluate like `p <expr>` on the current frame.
795
775
  when 'p'
796
- @tc << [:eval, :p, arg.to_s]
776
+ request_tc [:eval, :p, arg.to_s]
797
777
 
798
778
  # * `pp <expr>`
799
779
  # * Evaluate like `pp <expr>` on the current frame.
800
780
  when 'pp'
801
- @tc << [:eval, :pp, arg.to_s]
781
+ request_tc [:eval, :pp, arg.to_s]
802
782
 
803
783
  # * `eval <expr>`
804
784
  # * Evaluate `<expr>` on the current frame.
@@ -808,7 +788,7 @@ module DEBUGGER__
808
788
  @ui.puts "\nTo evaluate the variable `#{cmd}`, use `pp #{cmd}` instead."
809
789
  return :retry
810
790
  else
811
- @tc << [:eval, :call, arg]
791
+ request_tc [:eval, :call, arg]
812
792
  end
813
793
 
814
794
  # * `irb`
@@ -818,7 +798,7 @@ module DEBUGGER__
818
798
  @ui.puts "not supported on the remote console."
819
799
  return :retry
820
800
  end
821
- @tc << [:eval, :irb]
801
+ request_tc [:eval, :irb]
822
802
 
823
803
  # don't repeat irb command
824
804
  @repl_prev_line = nil
@@ -834,8 +814,8 @@ module DEBUGGER__
834
814
  # * Add an exception tracer. It indicates raising exceptions.
835
815
  # * `trace object <expr>`
836
816
  # * Add an object tracer. It indicates that an object by `<expr>` is passed as a parameter or a receiver on method call.
837
- # * `trace ... </pattern/>`
838
- # * Indicates only matched events to `</pattern/>` (RegExp).
817
+ # * `trace ... /regexp/`
818
+ # * Indicates only matched events to `/regexp/`.
839
819
  # * `trace ... into: <file>`
840
820
  # * Save trace information into: `<file>`.
841
821
  # * `trace off <num>`
@@ -875,7 +855,7 @@ module DEBUGGER__
875
855
  return :retry
876
856
 
877
857
  when /\Aobject\s+(.+)/
878
- @tc << [:trace, :object, $1.strip, {pattern: pattern, into: into}]
858
+ request_tc [:trace, :object, $1.strip, {pattern: pattern, into: into}]
879
859
 
880
860
  when /\Aoff\s+(\d+)\z/
881
861
  if t = @tracers.values[$1.to_i]
@@ -913,7 +893,7 @@ module DEBUGGER__
913
893
  when 'record'
914
894
  case arg
915
895
  when nil, 'on', 'off'
916
- @tc << [:record, arg&.to_sym]
896
+ request_tc [:record, arg&.to_sym]
917
897
  else
918
898
  @ui.puts "unknown command: #{arg}"
919
899
  return :retry
@@ -978,7 +958,7 @@ module DEBUGGER__
978
958
  when 'open'
979
959
  case arg&.downcase
980
960
  when '', nil
981
- repl_open_unix
961
+ repl_open
982
962
  when 'vscode'
983
963
  repl_open_vscode
984
964
  when /\A(.+):(\d+)\z/
@@ -1003,16 +983,12 @@ module DEBUGGER__
1003
983
  # * `h[elp] <command>`
1004
984
  # * Show help for the given command.
1005
985
  when 'h', 'help', '?'
1006
- if arg
1007
- show_help arg
1008
- else
1009
- @ui.puts DEBUGGER__.help
1010
- end
986
+ show_help arg
1011
987
  return :retry
1012
988
 
1013
989
  ### END
1014
990
  else
1015
- @tc << [:eval, :pp, line]
991
+ request_tc [:eval, :pp, line]
1016
992
  =begin
1017
993
  @repl_prev_line = nil
1018
994
  @ui.puts "unknown command: #{line}"
@@ -1055,21 +1031,21 @@ module DEBUGGER__
1055
1031
  repl_open_setup
1056
1032
  end
1057
1033
 
1058
- def repl_open_unix
1059
- DEBUGGER__.open_unix nonstop: true
1034
+ def repl_open
1035
+ DEBUGGER__.open nonstop: true
1060
1036
  repl_open_setup
1061
1037
  end
1062
1038
 
1063
1039
  def repl_open_vscode
1064
1040
  CONFIG[:open_frontend] = 'vscode'
1065
- repl_open_unix
1041
+ repl_open
1066
1042
  end
1067
1043
 
1068
1044
  def step_command type, arg
1069
1045
  case arg
1070
1046
  when nil, /\A\d+\z/
1071
1047
  if type == :in && @tc.recorder&.replaying?
1072
- @tc << [:step, type, arg&.to_i]
1048
+ request_tc [:step, type, arg&.to_i]
1073
1049
  else
1074
1050
  leave_subsession [:step, type, arg&.to_i]
1075
1051
  end
@@ -1078,7 +1054,7 @@ module DEBUGGER__
1078
1054
  @ui.puts "only `step #{arg}` is supported."
1079
1055
  :retry
1080
1056
  else
1081
- @tc << [:step, arg.to_sym]
1057
+ request_tc [:step, arg.to_sym]
1082
1058
  end
1083
1059
  else
1084
1060
  @ui.puts "Unknown option: #{arg}"
@@ -1088,11 +1064,18 @@ module DEBUGGER__
1088
1064
 
1089
1065
  def config_show key
1090
1066
  key = key.to_sym
1091
- if CONFIG_SET[key]
1067
+ config_detail = CONFIG_SET[key]
1068
+
1069
+ if config_detail
1092
1070
  v = CONFIG[key]
1093
- kv = "#{key} = #{v.nil? ? '(default)' : v.inspect}"
1094
- desc = CONFIG_SET[key][1]
1095
- line = "%-30s \# %s" % [kv, desc]
1071
+ kv = "#{key} = #{v.inspect}"
1072
+ desc = config_detail[1]
1073
+
1074
+ if config_default = config_detail[3]
1075
+ desc += " (default: #{config_default})"
1076
+ end
1077
+
1078
+ line = "%-34s \# %s" % [kv, desc]
1096
1079
  if line.size > SESSION.width
1097
1080
  @ui.puts "\# #{desc}\n#{kv}"
1098
1081
  else
@@ -1142,7 +1125,7 @@ module DEBUGGER__
1142
1125
  config_set $1, $2, append: true
1143
1126
 
1144
1127
  when /\A\s*append\s+(\w+)\s+(.+)\z/
1145
- config_set $1, $2
1128
+ config_set $1, $2, append: true
1146
1129
 
1147
1130
  when /\A(\w+)\z/
1148
1131
  config_show $1
@@ -1159,16 +1142,50 @@ module DEBUGGER__
1159
1142
  end
1160
1143
  end
1161
1144
 
1162
- def show_help arg
1163
- DEBUGGER__.helps.each{|cat, cs|
1164
- cs.each{|ws, desc|
1165
- if ws.include? arg
1166
- @ui.puts desc
1167
- return
1145
+ def show_help arg = nil
1146
+ instructions = (DEBUGGER__.commands.keys + DEBUGGER__.commands.values).uniq
1147
+ print_instructions = proc do |desc|
1148
+ desc.split("\n").each do |line|
1149
+ next if line.start_with?(" ") # workaround for step back
1150
+ formatted_line = line.gsub(/[\[\]\*]/, "").strip
1151
+ instructions.each do |inst|
1152
+ if formatted_line.start_with?("`#{inst}")
1153
+ desc.sub!(line, colorize(line, [:CYAN, :BOLD]))
1154
+ end
1155
+ end
1156
+ end
1157
+ @ui.puts desc
1158
+ end
1159
+
1160
+ print_category = proc do |cat|
1161
+ @ui.puts "\n"
1162
+ @ui.puts colorize("### #{cat}", [:GREEN, :BOLD])
1163
+ @ui.puts "\n"
1164
+ end
1165
+
1166
+ DEBUGGER__.helps.each { |cat, cs|
1167
+ # categories
1168
+ if arg.nil?
1169
+ print_category.call(cat)
1170
+ else
1171
+ cs.each { |ws, _|
1172
+ if ws.include?(arg)
1173
+ print_category.call(cat)
1174
+ break
1175
+ end
1176
+ }
1177
+ end
1178
+
1179
+ # instructions
1180
+ cs.each { |ws, desc|
1181
+ if arg.nil? || ws.include?(arg)
1182
+ print_instructions.call(desc.dup)
1183
+ return if arg
1168
1184
  end
1169
1185
  }
1170
1186
  }
1171
- @ui.puts "not found: #{arg}"
1187
+
1188
+ @ui.puts "not found: #{arg}" if arg
1172
1189
  end
1173
1190
 
1174
1191
  def ask msg, default = 'Y'
@@ -1234,9 +1251,12 @@ module DEBUGGER__
1234
1251
  @repl_prev_line = nil
1235
1252
 
1236
1253
  if @bps.has_key? bp.key
1237
- unless bp.duplicable?
1254
+ if bp.duplicable?
1255
+ bp
1256
+ else
1238
1257
  @ui.puts "duplicated breakpoint: #{bp}"
1239
1258
  bp.disable
1259
+ nil
1240
1260
  end
1241
1261
  else
1242
1262
  @bps[bp.key] = bp
@@ -1272,14 +1292,20 @@ module DEBUGGER__
1272
1292
  end
1273
1293
  }
1274
1294
  expr.default_proc = nil
1275
- expr.transform_values{|v| v.join(' ')}
1295
+ expr = expr.transform_values{|v| v.join(' ')}
1296
+
1297
+ if (path = expr[:path]) && path =~ /\A\/(.*)\/\z/
1298
+ expr[:path] = Regexp.compile($1)
1299
+ end
1300
+
1301
+ expr
1276
1302
  end
1277
1303
 
1278
1304
  def repl_add_breakpoint arg
1279
1305
  expr = parse_break arg.strip
1280
1306
  cond = expr[:if]
1281
1307
  cmd = ['break', expr[:pre], expr[:do]] if expr[:pre] || expr[:do]
1282
- path = Regexp.compile(expr[:path]) if expr[:path]
1308
+ path = expr[:path]
1283
1309
 
1284
1310
  case expr[:sig]
1285
1311
  when /\A(\d+)\z/
@@ -1287,10 +1313,10 @@ module DEBUGGER__
1287
1313
  when /\A(.+)[:\s+](\d+)\z/
1288
1314
  add_line_breakpoint $1, $2.to_i, cond: cond, command: cmd
1289
1315
  when /\A(.+)([\.\#])(.+)\z/
1290
- @tc << [:breakpoint, :method, $1, $2, $3, cond, cmd, path]
1316
+ request_tc [:breakpoint, :method, $1, $2, $3, cond, cmd, path]
1291
1317
  return :noretry
1292
1318
  when nil
1293
- add_check_breakpoint cond, path
1319
+ add_check_breakpoint cond, path, cmd
1294
1320
  else
1295
1321
  @ui.puts "Unknown breakpoint format: #{arg}"
1296
1322
  @ui.puts
@@ -1302,7 +1328,7 @@ module DEBUGGER__
1302
1328
  expr = parse_break arg.strip
1303
1329
  cond = expr[:if]
1304
1330
  cmd = ['catch', expr[:pre], expr[:do]] if expr[:pre] || expr[:do]
1305
- path = Regexp.compile(expr[:path]) if expr[:path]
1331
+ path = expr[:path]
1306
1332
 
1307
1333
  bp = CatchBreakpoint.new(expr[:sig], cond: cond, command: cmd, path: path)
1308
1334
  add_bp bp
@@ -1314,16 +1340,16 @@ module DEBUGGER__
1314
1340
  cmd = ['watch', expr[:pre], expr[:do]] if expr[:pre] || expr[:do]
1315
1341
  path = Regexp.compile(expr[:path]) if expr[:path]
1316
1342
 
1317
- @tc << [:breakpoint, :watch, expr[:sig], cond, cmd, path]
1343
+ request_tc [:breakpoint, :watch, expr[:sig], cond, cmd, path]
1318
1344
  end
1319
1345
 
1320
- def add_catch_breakpoint pat
1321
- bp = CatchBreakpoint.new(pat)
1346
+ def add_catch_breakpoint pat, cond: nil
1347
+ bp = CatchBreakpoint.new(pat, cond: cond)
1322
1348
  add_bp bp
1323
1349
  end
1324
1350
 
1325
- def add_check_breakpoint expr, path
1326
- bp = CheckBreakpoint.new(expr, path)
1351
+ def add_check_breakpoint cond, path, command
1352
+ bp = CheckBreakpoint.new(cond: cond, path: path, command: command)
1327
1353
  add_bp bp
1328
1354
  end
1329
1355
 
@@ -1336,6 +1362,34 @@ module DEBUGGER__
1336
1362
  @ui.puts e.message
1337
1363
  end
1338
1364
 
1365
+ def clear_breakpoints(&condition)
1366
+ @bps.delete_if do |k, bp|
1367
+ if condition.call(k, bp)
1368
+ bp.delete
1369
+ true
1370
+ end
1371
+ end
1372
+ end
1373
+
1374
+ def clear_line_breakpoints path
1375
+ path = resolve_path(path)
1376
+ clear_breakpoints do |k, bp|
1377
+ bp.is_a?(LineBreakpoint) && DEBUGGER__.compare_path(k.first, path)
1378
+ end
1379
+ rescue Errno::ENOENT
1380
+ # just ignore
1381
+ end
1382
+
1383
+ def clear_catch_breakpoints *exception_names
1384
+ clear_breakpoints do |k, bp|
1385
+ bp.is_a?(CatchBreakpoint) && exception_names.include?(k[1])
1386
+ end
1387
+ end
1388
+
1389
+ def clear_all_breakpoints
1390
+ clear_breakpoints{true}
1391
+ end
1392
+
1339
1393
  def add_iseq_breakpoint iseq, **kw
1340
1394
  bp = ISeqBreakpoint.new(iseq, [:line], **kw)
1341
1395
  add_bp bp
@@ -1503,42 +1557,66 @@ module DEBUGGER__
1503
1557
  end
1504
1558
 
1505
1559
  private def enter_subsession
1506
- raise "already in subsession" if @subsession
1507
- @subsession = true
1508
- stop_all_threads
1509
- @process_group.lock
1510
- DEBUGGER__.info "enter_subsession"
1560
+ if !@subsession_stack.empty?
1561
+ DEBUGGER__.info "Enter subsession (nested #{@subsession_stack.size})"
1562
+ else
1563
+ DEBUGGER__.info "Enter subsession"
1564
+ stop_all_threads
1565
+ @process_group.lock
1566
+ end
1567
+
1568
+ @subsession_stack << true
1511
1569
  end
1512
1570
 
1513
1571
  private def leave_subsession type
1514
- DEBUGGER__.info "leave_subsession"
1515
- @process_group.unlock
1516
- restart_all_threads
1517
- @tc << type if type
1572
+ raise '[BUG] leave_subsession: not entered' if @subsession_stack.empty?
1573
+ @subsession_stack.pop
1574
+
1575
+ if @subsession_stack.empty?
1576
+ DEBUGGER__.info "Leave subsession"
1577
+ @process_group.unlock
1578
+ restart_all_threads
1579
+ else
1580
+ DEBUGGER__.info "Leave subsession (nested #{@subsession_stack.size})"
1581
+ end
1582
+
1583
+ request_tc type if type
1518
1584
  @tc = nil
1519
- @subsession = false
1520
1585
  rescue Exception => e
1521
- STDERR.puts [e, e.backtrace].inspect
1586
+ STDERR.puts PP.pp([e, e.backtrace], ''.dup)
1522
1587
  raise
1523
1588
  end
1524
1589
 
1525
1590
  def in_subsession?
1526
- @subsession
1591
+ !@subsession_stack.empty?
1527
1592
  end
1528
1593
 
1529
1594
  ## event
1530
1595
 
1531
1596
  def on_load iseq, src
1532
1597
  DEBUGGER__.info "Load #{iseq.absolute_path || iseq.path}"
1533
- @sr.add iseq, src
1598
+
1599
+ file_path, reloaded = @sr.add(iseq, src)
1600
+ @ui.event :load, file_path, reloaded
1534
1601
 
1535
1602
  pending_line_breakpoints = @bps.find_all do |key, bp|
1536
1603
  LineBreakpoint === bp && !bp.iseq
1537
1604
  end
1538
1605
 
1539
1606
  pending_line_breakpoints.each do |_key, bp|
1540
- if bp.path == (iseq.absolute_path || iseq.path)
1541
- bp.try_activate
1607
+ if DEBUGGER__.compare_path(bp.path, (iseq.absolute_path || iseq.path))
1608
+ bp.try_activate iseq
1609
+ end
1610
+ end
1611
+
1612
+ if reloaded
1613
+ @bps.find_all do |key, bp|
1614
+ LineBreakpoint === bp && DEBUGGER__.compare_path(bp.path, file_path)
1615
+ end.each do |_key, bp|
1616
+ @bps.delete bp.key # to allow duplicate
1617
+ if nbp = LineBreakpoint.copy(bp, iseq)
1618
+ add_bp nbp
1619
+ end
1542
1620
  end
1543
1621
  end
1544
1622
  end
@@ -1563,9 +1641,10 @@ module DEBUGGER__
1563
1641
 
1564
1642
  def method_added tp
1565
1643
  b = tp.binding
1644
+
1566
1645
  if var_name = b.local_variables.first
1567
1646
  mid = b.local_variable_get(var_name)
1568
- unresolved = false
1647
+ resolved = true
1569
1648
 
1570
1649
  @bps.each{|k, bp|
1571
1650
  case bp
@@ -1576,15 +1655,57 @@ module DEBUGGER__
1576
1655
  end
1577
1656
  end
1578
1657
 
1579
- unresolved = true unless bp.enabled?
1658
+ resolved = false if !bp.enabled?
1580
1659
  end
1581
1660
  }
1582
- unless unresolved
1583
- METHOD_ADDED_TRACKER.disable
1661
+
1662
+ if resolved
1663
+ Session.deactivate_method_added_trackers
1584
1664
  end
1665
+
1666
+ case mid
1667
+ when :method_added, :singleton_method_added
1668
+ Session.create_method_added_tracker(tp.self, mid)
1669
+ Session.activate_method_added_trackers unless resolved
1670
+ end
1671
+ end
1672
+ end
1673
+
1674
+ class ::Module
1675
+ undef method_added
1676
+ def method_added mid; end
1677
+ end
1678
+
1679
+ class ::BasicObject
1680
+ undef singleton_method_added
1681
+ def singleton_method_added mid; end
1682
+ end
1683
+
1684
+ def self.create_method_added_tracker mod, method_added_id, method_accessor = :method
1685
+ m = mod.__send__(method_accessor, method_added_id)
1686
+ METHOD_ADDED_TRACKERS[m] = TracePoint.new(:call) do |tp|
1687
+ SESSION.method_added tp
1585
1688
  end
1586
1689
  end
1587
1690
 
1691
+ def self.activate_method_added_trackers
1692
+ METHOD_ADDED_TRACKERS.each do |m, tp|
1693
+ tp.enable(target: m) unless tp.enabled?
1694
+ rescue ArgumentError
1695
+ DEBUGGER__.warn "Methods defined under #{m.owner} can not track by the debugger."
1696
+ end
1697
+ end
1698
+
1699
+ def self.deactivate_method_added_trackers
1700
+ METHOD_ADDED_TRACKERS.each do |m, tp|
1701
+ tp.disable if tp.enabled?
1702
+ end
1703
+ end
1704
+
1705
+ METHOD_ADDED_TRACKERS = Hash.new
1706
+ create_method_added_tracker Module, :method_added, :instance_method
1707
+ create_method_added_tracker BasicObject, :singleton_method_added, :instance_method
1708
+
1588
1709
  def width
1589
1710
  @ui.width
1590
1711
  end
@@ -1860,6 +1981,9 @@ module DEBUGGER__
1860
1981
  puts "\nStop by #{args.first}"
1861
1982
  end
1862
1983
  end
1984
+
1985
+ def flush
1986
+ end
1863
1987
  end
1864
1988
 
1865
1989
  # manual configuration methods
@@ -1876,7 +2000,7 @@ module DEBUGGER__
1876
2000
  # nil for -r
1877
2001
  def self.require_location
1878
2002
  locs = caller_locations
1879
- dir_prefix = /#{__dir__}/
2003
+ dir_prefix = /#{Regexp.escape(__dir__)}/
1880
2004
 
1881
2005
  locs.each do |loc|
1882
2006
  case loc.absolute_path
@@ -1896,7 +2020,7 @@ module DEBUGGER__
1896
2020
 
1897
2021
  unless defined? SESSION
1898
2022
  require_relative 'local'
1899
- initialize_session UI_LocalConsole.new
2023
+ initialize_session{ UI_LocalConsole.new }
1900
2024
  end
1901
2025
 
1902
2026
  setup_initial_suspend unless nonstop
@@ -1904,8 +2028,9 @@ module DEBUGGER__
1904
2028
 
1905
2029
  def self.open host: nil, port: CONFIG[:port], sock_path: nil, sock_dir: nil, nonstop: false, **kw
1906
2030
  CONFIG.set_config(**kw)
2031
+ require_relative 'server'
1907
2032
 
1908
- if port || CONFIG[:open_frontend] == 'chrome'
2033
+ if port || CONFIG[:open_frontend] == 'chrome' || (!::Addrinfo.respond_to?(:unix))
1909
2034
  open_tcp host: host, port: (port || 0), nonstop: nonstop
1910
2035
  else
1911
2036
  open_unix sock_path: sock_path, sock_dir: sock_dir, nonstop: nonstop
@@ -1919,7 +2044,7 @@ module DEBUGGER__
1919
2044
  if defined? SESSION
1920
2045
  SESSION.reset_ui UI_TcpServer.new(host: host, port: port)
1921
2046
  else
1922
- initialize_session UI_TcpServer.new(host: host, port: port)
2047
+ initialize_session{ UI_TcpServer.new(host: host, port: port) }
1923
2048
  end
1924
2049
 
1925
2050
  setup_initial_suspend unless nonstop
@@ -1932,7 +2057,7 @@ module DEBUGGER__
1932
2057
  if defined? SESSION
1933
2058
  SESSION.reset_ui UI_UnixDomainServer.new(sock_dir: sock_dir, sock_path: sock_path)
1934
2059
  else
1935
- initialize_session UI_UnixDomainServer.new(sock_dir: sock_dir, sock_path: sock_path)
2060
+ initialize_session{ UI_UnixDomainServer.new(sock_dir: sock_dir, sock_path: sock_path) }
1936
2061
  end
1937
2062
 
1938
2063
  setup_initial_suspend unless nonstop
@@ -1959,9 +2084,10 @@ module DEBUGGER__
1959
2084
  end
1960
2085
 
1961
2086
  class << self
1962
- define_method :initialize_session do |ui|
2087
+ define_method :initialize_session do |&init_ui|
1963
2088
  DEBUGGER__.info "Session start (pid: #{Process.pid})"
1964
- ::DEBUGGER__.const_set(:SESSION, Session.new(ui))
2089
+ ::DEBUGGER__.const_set(:SESSION, Session.new)
2090
+ SESSION.activate init_ui.call
1965
2091
  load_rc
1966
2092
  end
1967
2093
  end
@@ -1993,34 +2119,53 @@ module DEBUGGER__
1993
2119
  end
1994
2120
  end
1995
2121
 
1996
- class ::Module
1997
- undef method_added
1998
- def method_added mid; end
1999
- def singleton_method_added mid; end
2000
- end
2122
+ # Inspector
2123
+
2124
+ SHORT_INSPECT_LENGTH = 40
2001
2125
 
2002
- def self.method_added tp
2003
- begin
2004
- SESSION.method_added tp
2005
- rescue Exception => e
2006
- p e
2126
+ class LimitedPP
2127
+ def self.pp(obj, max=80)
2128
+ out = self.new(max)
2129
+ catch out do
2130
+ PP.singleline_pp(obj, out)
2131
+ end
2132
+ out.buf
2007
2133
  end
2008
- end
2009
2134
 
2010
- METHOD_ADDED_TRACKER = self.create_method_added_tracker
2135
+ attr_reader :buf
2011
2136
 
2012
- SHORT_INSPECT_LENGTH = 40
2137
+ def initialize max
2138
+ @max = max
2139
+ @cnt = 0
2140
+ @buf = String.new
2141
+ end
2013
2142
 
2014
- def self.safe_inspect obj, max_length: SHORT_INSPECT_LENGTH, short: false
2015
- str = obj.inspect
2143
+ def <<(other)
2144
+ @buf << other
2145
+
2146
+ if @buf.size >= @max
2147
+ @buf = @buf[0..@max] + '...'
2148
+ throw self
2149
+ end
2150
+ end
2151
+ end
2016
2152
 
2017
- if short && str.length > max_length
2018
- str[0...max_length] + '...'
2153
+ def self.safe_inspect obj, max_length: SHORT_INSPECT_LENGTH, short: false
2154
+ if short
2155
+ LimitedPP.pp(obj, max_length)
2019
2156
  else
2020
- str
2157
+ obj.inspect
2158
+ end
2159
+ rescue NoMethodError => e
2160
+ klass, oid = M_CLASS.bind_call(obj), M_OBJECT_ID.bind_call(obj)
2161
+ if obj == (r = e.receiver)
2162
+ "<\##{klass.name}#{oid} does not have \#inspect>"
2163
+ else
2164
+ rklass, roid = M_CLASS.bind_call(r), M_OBJECT_ID.bind_call(r)
2165
+ "<\##{klass.name}:#{roid} contains <\##{rklass}:#{roid} and it does not have #inspect>"
2021
2166
  end
2022
2167
  rescue Exception => e
2023
- str = "<#inspect raises #{e.inspect}>"
2168
+ "<#inspect raises #{e.inspect}>"
2024
2169
  end
2025
2170
 
2026
2171
  def self.warn msg
@@ -2031,18 +2176,27 @@ module DEBUGGER__
2031
2176
  log :INFO, msg
2032
2177
  end
2033
2178
 
2034
- def self.log level, msg
2035
- @logfile = STDERR unless defined? @logfile
2036
-
2179
+ def self.check_loglevel level
2037
2180
  lv = LOG_LEVELS[level]
2038
- config_lv = LOG_LEVELS[CONFIG[:log_level] || :WARN]
2181
+ config_lv = LOG_LEVELS[CONFIG[:log_level]]
2182
+ lv <= config_lv
2183
+ end
2039
2184
 
2040
- if defined? SESSION
2041
- pi = SESSION.process_info
2042
- process_info = pi ? "[#{pi}]" : nil
2185
+ def self.debug(&b)
2186
+ if check_loglevel :DEBUG
2187
+ log :DEBUG, b.call
2043
2188
  end
2189
+ end
2190
+
2191
+ def self.log level, msg
2192
+ if check_loglevel level
2193
+ @logfile = STDERR unless defined? @logfile
2194
+
2195
+ if defined? SESSION
2196
+ pi = SESSION.process_info
2197
+ process_info = pi ? "[#{pi}]" : nil
2198
+ end
2044
2199
 
2045
- if lv <= config_lv
2046
2200
  if level == :WARN
2047
2201
  # :WARN on debugger is general information
2048
2202
  @logfile.puts "DEBUGGER#{process_info}: #{msg}"
@@ -2062,17 +2216,69 @@ module DEBUGGER__
2062
2216
  yield
2063
2217
  end
2064
2218
 
2219
+ if File.identical?(__FILE__.upcase, __FILE__.downcase)
2220
+ # For case insensitive file system (like Windows)
2221
+ # Note that this check is not enough because case sensitive/insensitive is
2222
+ # depend on the file system. So this check is only roughly estimation.
2223
+
2224
+ def self.compare_path(a, b)
2225
+ a.downcase == b.downcase
2226
+ end
2227
+ else
2228
+ def self.compare_path(a, b)
2229
+ a == b
2230
+ end
2231
+ end
2232
+
2065
2233
  module ForkInterceptor
2066
- def fork(&given_block)
2067
- return super unless defined?(SESSION) && SESSION.active?
2234
+ if Process.respond_to? :_fork
2235
+ def _fork
2236
+ return super unless defined?(SESSION) && SESSION.active?
2068
2237
 
2069
- unless fork_mode = CONFIG[:fork_mode]
2070
- if CONFIG[:parent_on_fork]
2071
- fork_mode = :parent
2238
+ parent_hook, child_hook = __fork_setup_for_debugger
2239
+
2240
+ super.tap do |pid|
2241
+ if pid != 0
2242
+ # after fork: parent
2243
+ parent_hook.call pid
2244
+ else
2245
+ # after fork: child
2246
+ child_hook.call
2247
+ end
2248
+ end
2249
+ end
2250
+ else
2251
+ def fork(&given_block)
2252
+ return super unless defined?(SESSION) && SESSION.active?
2253
+ parent_hook, child_hook = __fork_setup_for_debugger
2254
+
2255
+ if given_block
2256
+ new_block = proc {
2257
+ # after fork: child
2258
+ child_hook.call
2259
+ given_block.call
2260
+ }
2261
+ super(&new_block).tap{|pid| parent_hook.call(pid)}
2072
2262
  else
2073
- fork_mode = :both
2263
+ super.tap do |pid|
2264
+ if pid
2265
+ # after fork: parent
2266
+ parent_hook.call pid
2267
+ else
2268
+ # after fork: child
2269
+ child_hook.call
2270
+ end
2271
+ end
2074
2272
  end
2075
2273
  end
2274
+ end
2275
+
2276
+ private def __fork_setup_for_debugger
2277
+ fork_mode = CONFIG[:fork_mode]
2278
+
2279
+ if fork_mode == :both && CONFIG[:parent_on_fork]
2280
+ fork_mode = :parent
2281
+ end
2076
2282
 
2077
2283
  parent_pid = Process.pid
2078
2284
 
@@ -2112,26 +2318,7 @@ module DEBUGGER__
2112
2318
  }
2113
2319
  end
2114
2320
 
2115
- if given_block
2116
- new_block = proc {
2117
- # after fork: child
2118
- child_hook.call
2119
- given_block.call
2120
- }
2121
- pid = super(&new_block)
2122
- parent_hook.call(pid)
2123
- pid
2124
- else
2125
- if pid = super
2126
- # after fork: parent
2127
- parent_hook.call pid
2128
- else
2129
- # after fork: child
2130
- child_hook.call
2131
- end
2132
-
2133
- pid
2134
- end
2321
+ return parent_hook, child_hook
2135
2322
  end
2136
2323
  end
2137
2324
 
@@ -2148,28 +2335,46 @@ module DEBUGGER__
2148
2335
  end
2149
2336
  end
2150
2337
 
2151
- if RUBY_VERSION >= '3.0.0'
2338
+ if Process.respond_to? :_fork
2339
+ module ::Process
2340
+ class << self
2341
+ prepend ForkInterceptor
2342
+ end
2343
+ end
2344
+
2345
+ # trap
2152
2346
  module ::Kernel
2153
- prepend ForkInterceptor
2154
2347
  prepend TrapInterceptor
2155
2348
  end
2349
+ module ::Signal
2350
+ class << self
2351
+ prepend TrapInterceptor
2352
+ end
2353
+ end
2156
2354
  else
2157
- class ::Object
2158
- include ForkInterceptor
2159
- include TrapInterceptor
2355
+ if RUBY_VERSION >= '3.0.0'
2356
+ module ::Kernel
2357
+ prepend ForkInterceptor
2358
+ prepend TrapInterceptor
2359
+ end
2360
+ else
2361
+ class ::Object
2362
+ include ForkInterceptor
2363
+ include TrapInterceptor
2364
+ end
2160
2365
  end
2161
- end
2162
2366
 
2163
- module ::Kernel
2164
- class << self
2165
- prepend ForkInterceptor
2166
- prepend TrapInterceptor
2367
+ module ::Kernel
2368
+ class << self
2369
+ prepend ForkInterceptor
2370
+ prepend TrapInterceptor
2371
+ end
2167
2372
  end
2168
- end
2169
2373
 
2170
- module ::Process
2171
- class << self
2172
- prepend ForkInterceptor
2374
+ module ::Process
2375
+ class << self
2376
+ prepend ForkInterceptor
2377
+ end
2173
2378
  end
2174
2379
  end
2175
2380
 
@@ -2199,3 +2404,12 @@ class Binding
2199
2404
  alias break debugger
2200
2405
  alias b debugger
2201
2406
  end
2407
+
2408
+ # for Ruby 2.6 compatibility
2409
+ unless method(:p).unbind.respond_to? :bind_call
2410
+ class UnboundMethod
2411
+ def bind_call(obj, *args)
2412
+ self.bind(obj).call(*args)
2413
+ end
2414
+ end
2415
+ end