debug 1.5.0 → 1.6.3

Sign up to get free protection for your applications and to get access to all the features.
data/lib/debug/session.rb CHANGED
@@ -19,6 +19,15 @@ 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.end_with?(added_opt)
26
+
27
+ ENV['RUBYOPT'] = rubyopt.delete_suffix(added_opt)
28
+ ENV['RUBY_DEBUG_ADDED_RUBYOPT'] = nil
29
+ end
30
+
22
31
  require_relative 'frame_info'
23
32
  require_relative 'config'
24
33
  require_relative 'thread_client'
@@ -32,6 +41,7 @@ $LOADED_FEATURES << File.expand_path(File.join(__dir__, '..', 'debug.rb'))
32
41
  require 'debug' # invalidate the $LOADED_FEATURE cache
33
42
 
34
43
  require 'json' if ENV['RUBY_DEBUG_TEST_UI'] == 'terminal'
44
+ require 'pp'
35
45
 
36
46
  class RubyVM::InstructionSequence
37
47
  def traceable_lines_norec lines
@@ -197,35 +207,44 @@ module DEBUGGER__
197
207
  deactivate
198
208
  end
199
209
 
210
+ def request_tc(req)
211
+ @tc << req
212
+ end
213
+
200
214
  def process_event evt
201
215
  # variable `@internal_info` is only used for test
202
216
  tc, output, ev, @internal_info, *ev_args = evt
203
- output.each{|str| @ui.puts str} if ev != :suspend
204
217
 
205
- case ev
218
+ output.each{|str| @ui.puts str} if ev != :suspend
206
219
 
207
- when :thread_begin # special event, tc is nil
220
+ # special event, tc is nil
221
+ # and we don't want to set @tc to the newly created thread's ThreadClient
222
+ if ev == :thread_begin
208
223
  th = ev_args.shift
209
224
  q = ev_args.shift
210
225
  on_thread_begin th
211
226
  q << true
212
227
 
228
+ return
229
+ end
230
+
231
+ @tc = tc
232
+
233
+ case ev
213
234
  when :init
214
235
  enter_subsession
215
- wait_command_loop tc
216
-
236
+ wait_command_loop
217
237
  when :load
218
238
  iseq, src = ev_args
219
239
  on_load iseq, src
220
- @ui.event :load
221
- tc << :continue
240
+ request_tc :continue
222
241
 
223
242
  when :trace
224
243
  trace_id, msg = ev_args
225
244
  if t = @tracers.values.find{|t| t.object_id == trace_id}
226
245
  t.puts msg
227
246
  end
228
- tc << :continue
247
+ request_tc :continue
229
248
 
230
249
  when :suspend
231
250
  enter_subsession if ev_args.first != :replay
@@ -235,24 +254,23 @@ module DEBUGGER__
235
254
  when :breakpoint
236
255
  bp, i = bp_index ev_args[1]
237
256
  clean_bps unless bp
238
- @ui.event :suspend_bp, i, bp, tc.id
257
+ @ui.event :suspend_bp, i, bp, @tc.id
239
258
  when :trap
240
- @ui.event :suspend_trap, sig = ev_args[1], tc.id
259
+ @ui.event :suspend_trap, sig = ev_args[1], @tc.id
241
260
 
242
261
  if sig == :SIGINT && (@intercepted_sigint_cmd.kind_of?(Proc) || @intercepted_sigint_cmd.kind_of?(String))
243
262
  @ui.puts "#{@intercepted_sigint_cmd.inspect} is registered as SIGINT handler."
244
263
  @ui.puts "`sigint` command execute it."
245
264
  end
246
265
  else
247
- @ui.event :suspended, tc.id
266
+ @ui.event :suspended, @tc.id
248
267
  end
249
268
 
250
269
  if @displays.empty?
251
- wait_command_loop tc
270
+ wait_command_loop
252
271
  else
253
- tc << [:eval, :display, @displays]
272
+ request_tc [:eval, :display, @displays]
254
273
  end
255
-
256
274
  when :result
257
275
  raise "[BUG] not in subsession" if @subsession_stack.empty?
258
276
 
@@ -283,14 +301,14 @@ module DEBUGGER__
283
301
  # ignore
284
302
  end
285
303
 
286
- wait_command_loop tc
304
+ wait_command_loop
287
305
 
288
306
  when :dap_result
289
307
  dap_event ev_args # server.rb
290
- wait_command_loop tc
308
+ wait_command_loop
291
309
  when :cdp_result
292
310
  cdp_event ev_args
293
- wait_command_loop tc
311
+ wait_command_loop
294
312
  end
295
313
  end
296
314
 
@@ -323,9 +341,7 @@ module DEBUGGER__
323
341
  "DEBUGGER__::SESSION"
324
342
  end
325
343
 
326
- def wait_command_loop tc
327
- @tc = tc
328
-
344
+ def wait_command_loop
329
345
  loop do
330
346
  case wait_command
331
347
  when :retry
@@ -529,32 +545,6 @@ module DEBUGGER__
529
545
  end
530
546
  end
531
547
 
532
- # skip
533
- when 'bv'
534
- check_postmortem
535
- require 'json'
536
-
537
- h = Hash.new{|h, k| h[k] = []}
538
- @bps.each_value{|bp|
539
- if LineBreakpoint === bp
540
- h[bp.path] << {lnum: bp.line}
541
- end
542
- }
543
- if h.empty?
544
- # TODO: clean?
545
- else
546
- open(".rdb_breakpoints.json", 'w'){|f| JSON.dump(h, f)}
547
- end
548
-
549
- vimsrc = File.join(__dir__, 'bp.vim')
550
- system("vim -R -S #{vimsrc} #{@tc.location.path}")
551
-
552
- if File.exist?(".rdb_breakpoints.json")
553
- pp JSON.load(File.read(".rdb_breakpoints.json"))
554
- end
555
-
556
- return :retry
557
-
558
548
  # * `catch <Error>`
559
549
  # * Set breakpoint on raising `<Error>`.
560
550
  # * `catch ... if: <expr>`
@@ -632,15 +622,15 @@ module DEBUGGER__
632
622
  when 'bt', 'backtrace'
633
623
  case arg
634
624
  when /\A(\d+)\z/
635
- @tc << [:show, :backtrace, arg.to_i, nil]
625
+ request_tc [:show, :backtrace, arg.to_i, nil]
636
626
  when /\A\/(.*)\/\z/
637
627
  pattern = $1
638
- @tc << [:show, :backtrace, nil, Regexp.compile(pattern)]
628
+ request_tc [:show, :backtrace, nil, Regexp.compile(pattern)]
639
629
  when /\A(\d+)\s+\/(.*)\/\z/
640
630
  max, pattern = $1, $2
641
- @tc << [:show, :backtrace, max.to_i, Regexp.compile(pattern)]
631
+ request_tc [:show, :backtrace, max.to_i, Regexp.compile(pattern)]
642
632
  else
643
- @tc << [:show, :backtrace, nil, nil]
633
+ request_tc [:show, :backtrace, nil, nil]
644
634
  end
645
635
 
646
636
  # * `l[ist]`
@@ -653,13 +643,13 @@ module DEBUGGER__
653
643
  when 'l', 'list'
654
644
  case arg ? arg.strip : nil
655
645
  when /\A(\d+)\z/
656
- @tc << [:show, :list, {start_line: arg.to_i - 1}]
646
+ request_tc [:show, :list, {start_line: arg.to_i - 1}]
657
647
  when /\A-\z/
658
- @tc << [:show, :list, {dir: -1}]
648
+ request_tc [:show, :list, {dir: -1}]
659
649
  when /\A(\d+)-(\d+)\z/
660
- @tc << [:show, :list, {start_line: $1.to_i - 1, end_line: $2.to_i}]
650
+ request_tc [:show, :list, {start_line: $1.to_i - 1, end_line: $2.to_i}]
661
651
  when nil
662
- @tc << [:show, :list]
652
+ request_tc [:show, :list]
663
653
  else
664
654
  @ui.puts "Can not handle list argument: #{arg}"
665
655
  return :retry
@@ -683,7 +673,7 @@ module DEBUGGER__
683
673
  return :retry
684
674
  end
685
675
 
686
- @tc << [:show, :edit, arg]
676
+ request_tc [:show, :edit, arg]
687
677
 
688
678
  # * `i[nfo]`
689
679
  # * Show information about current frame (local/instance variables and defined constants).
@@ -710,15 +700,15 @@ module DEBUGGER__
710
700
 
711
701
  case sub
712
702
  when nil
713
- @tc << [:show, :default, pat] # something useful
703
+ request_tc [:show, :default, pat] # something useful
714
704
  when 'l', /^locals?/
715
- @tc << [:show, :locals, pat]
705
+ request_tc [:show, :locals, pat]
716
706
  when 'i', /^ivars?/i, /^instance[_ ]variables?/i
717
- @tc << [:show, :ivars, pat]
707
+ request_tc [:show, :ivars, pat]
718
708
  when 'c', /^consts?/i, /^constants?/i
719
- @tc << [:show, :consts, pat]
709
+ request_tc [:show, :consts, pat]
720
710
  when 'g', /^globals?/i, /^global[_ ]variables?/i
721
- @tc << [:show, :globals, pat]
711
+ request_tc [:show, :globals, pat]
722
712
  when 'th', /threads?/
723
713
  thread_list
724
714
  return :retry
@@ -734,7 +724,7 @@ module DEBUGGER__
734
724
  # * Show you available methods and instance variables of the given object.
735
725
  # * If the object is a class/module, it also lists its constants.
736
726
  when 'outline', 'o', 'ls'
737
- @tc << [:show, :outline, arg]
727
+ request_tc [:show, :outline, arg]
738
728
 
739
729
  # * `display`
740
730
  # * Show display setting.
@@ -743,9 +733,9 @@ module DEBUGGER__
743
733
  when 'display'
744
734
  if arg && !arg.empty?
745
735
  @displays << arg
746
- @tc << [:eval, :try_display, @displays]
736
+ request_tc [:eval, :try_display, @displays]
747
737
  else
748
- @tc << [:eval, :display, @displays]
738
+ request_tc [:eval, :display, @displays]
749
739
  end
750
740
 
751
741
  # * `undisplay`
@@ -758,7 +748,7 @@ module DEBUGGER__
758
748
  if @displays[n = $1.to_i]
759
749
  @displays.delete_at n
760
750
  end
761
- @tc << [:eval, :display, @displays]
751
+ request_tc [:eval, :display, @displays]
762
752
  when nil
763
753
  if ask "clear all?", 'N'
764
754
  @displays.clear
@@ -773,29 +763,29 @@ module DEBUGGER__
773
763
  # * `f[rame] <framenum>`
774
764
  # * Specify a current frame. Evaluation are run on specified frame.
775
765
  when 'frame', 'f'
776
- @tc << [:frame, :set, arg]
766
+ request_tc [:frame, :set, arg]
777
767
 
778
768
  # * `up`
779
769
  # * Specify the upper frame.
780
770
  when 'up'
781
- @tc << [:frame, :up]
771
+ request_tc [:frame, :up]
782
772
 
783
773
  # * `down`
784
774
  # * Specify the lower frame.
785
775
  when 'down'
786
- @tc << [:frame, :down]
776
+ request_tc [:frame, :down]
787
777
 
788
778
  ### Evaluate
789
779
 
790
780
  # * `p <expr>`
791
781
  # * Evaluate like `p <expr>` on the current frame.
792
782
  when 'p'
793
- @tc << [:eval, :p, arg.to_s]
783
+ request_tc [:eval, :p, arg.to_s]
794
784
 
795
785
  # * `pp <expr>`
796
786
  # * Evaluate like `pp <expr>` on the current frame.
797
787
  when 'pp'
798
- @tc << [:eval, :pp, arg.to_s]
788
+ request_tc [:eval, :pp, arg.to_s]
799
789
 
800
790
  # * `eval <expr>`
801
791
  # * Evaluate `<expr>` on the current frame.
@@ -805,7 +795,7 @@ module DEBUGGER__
805
795
  @ui.puts "\nTo evaluate the variable `#{cmd}`, use `pp #{cmd}` instead."
806
796
  return :retry
807
797
  else
808
- @tc << [:eval, :call, arg]
798
+ request_tc [:eval, :call, arg]
809
799
  end
810
800
 
811
801
  # * `irb`
@@ -815,7 +805,7 @@ module DEBUGGER__
815
805
  @ui.puts "not supported on the remote console."
816
806
  return :retry
817
807
  end
818
- @tc << [:eval, :irb]
808
+ request_tc [:eval, :irb]
819
809
 
820
810
  # don't repeat irb command
821
811
  @repl_prev_line = nil
@@ -872,7 +862,7 @@ module DEBUGGER__
872
862
  return :retry
873
863
 
874
864
  when /\Aobject\s+(.+)/
875
- @tc << [:trace, :object, $1.strip, {pattern: pattern, into: into}]
865
+ request_tc [:trace, :object, $1.strip, {pattern: pattern, into: into}]
876
866
 
877
867
  when /\Aoff\s+(\d+)\z/
878
868
  if t = @tracers.values[$1.to_i]
@@ -910,7 +900,7 @@ module DEBUGGER__
910
900
  when 'record'
911
901
  case arg
912
902
  when nil, 'on', 'off'
913
- @tc << [:record, arg&.to_sym]
903
+ request_tc [:record, arg&.to_sym]
914
904
  else
915
905
  @ui.puts "unknown command: #{arg}"
916
906
  return :retry
@@ -1005,7 +995,7 @@ module DEBUGGER__
1005
995
 
1006
996
  ### END
1007
997
  else
1008
- @tc << [:eval, :pp, line]
998
+ request_tc [:eval, :pp, line]
1009
999
  =begin
1010
1000
  @repl_prev_line = nil
1011
1001
  @ui.puts "unknown command: #{line}"
@@ -1062,7 +1052,7 @@ module DEBUGGER__
1062
1052
  case arg
1063
1053
  when nil, /\A\d+\z/
1064
1054
  if type == :in && @tc.recorder&.replaying?
1065
- @tc << [:step, type, arg&.to_i]
1055
+ request_tc [:step, type, arg&.to_i]
1066
1056
  else
1067
1057
  leave_subsession [:step, type, arg&.to_i]
1068
1058
  end
@@ -1071,7 +1061,7 @@ module DEBUGGER__
1071
1061
  @ui.puts "only `step #{arg}` is supported."
1072
1062
  :retry
1073
1063
  else
1074
- @tc << [:step, arg.to_sym]
1064
+ request_tc [:step, arg.to_sym]
1075
1065
  end
1076
1066
  else
1077
1067
  @ui.puts "Unknown option: #{arg}"
@@ -1081,11 +1071,18 @@ module DEBUGGER__
1081
1071
 
1082
1072
  def config_show key
1083
1073
  key = key.to_sym
1084
- if CONFIG_SET[key]
1074
+ config_detail = CONFIG_SET[key]
1075
+
1076
+ if config_detail
1085
1077
  v = CONFIG[key]
1086
- kv = "#{key} = #{v.nil? ? '(default)' : v.inspect}"
1087
- desc = CONFIG_SET[key][1]
1088
- line = "%-30s \# %s" % [kv, desc]
1078
+ kv = "#{key} = #{v.inspect}"
1079
+ desc = config_detail[1]
1080
+
1081
+ if config_default = config_detail[3]
1082
+ desc += " (default: #{config_default})"
1083
+ end
1084
+
1085
+ line = "%-34s \# %s" % [kv, desc]
1089
1086
  if line.size > SESSION.width
1090
1087
  @ui.puts "\# #{desc}\n#{kv}"
1091
1088
  else
@@ -1135,7 +1132,7 @@ module DEBUGGER__
1135
1132
  config_set $1, $2, append: true
1136
1133
 
1137
1134
  when /\A\s*append\s+(\w+)\s+(.+)\z/
1138
- config_set $1, $2
1135
+ config_set $1, $2, append: true
1139
1136
 
1140
1137
  when /\A(\w+)\z/
1141
1138
  config_show $1
@@ -1261,9 +1258,12 @@ module DEBUGGER__
1261
1258
  @repl_prev_line = nil
1262
1259
 
1263
1260
  if @bps.has_key? bp.key
1264
- unless bp.duplicable?
1261
+ if bp.duplicable?
1262
+ bp
1263
+ else
1265
1264
  @ui.puts "duplicated breakpoint: #{bp}"
1266
1265
  bp.disable
1266
+ nil
1267
1267
  end
1268
1268
  else
1269
1269
  @bps[bp.key] = bp
@@ -1320,7 +1320,7 @@ module DEBUGGER__
1320
1320
  when /\A(.+)[:\s+](\d+)\z/
1321
1321
  add_line_breakpoint $1, $2.to_i, cond: cond, command: cmd
1322
1322
  when /\A(.+)([\.\#])(.+)\z/
1323
- @tc << [:breakpoint, :method, $1, $2, $3, cond, cmd, path]
1323
+ request_tc [:breakpoint, :method, $1, $2, $3, cond, cmd, path]
1324
1324
  return :noretry
1325
1325
  when nil
1326
1326
  add_check_breakpoint cond, path, cmd
@@ -1347,11 +1347,11 @@ module DEBUGGER__
1347
1347
  cmd = ['watch', expr[:pre], expr[:do]] if expr[:pre] || expr[:do]
1348
1348
  path = Regexp.compile(expr[:path]) if expr[:path]
1349
1349
 
1350
- @tc << [:breakpoint, :watch, expr[:sig], cond, cmd, path]
1350
+ request_tc [:breakpoint, :watch, expr[:sig], cond, cmd, path]
1351
1351
  end
1352
1352
 
1353
- def add_catch_breakpoint pat
1354
- bp = CatchBreakpoint.new(pat)
1353
+ def add_catch_breakpoint pat, cond: nil
1354
+ bp = CatchBreakpoint.new(pat, cond: cond)
1355
1355
  add_bp bp
1356
1356
  end
1357
1357
 
@@ -1369,15 +1369,34 @@ module DEBUGGER__
1369
1369
  @ui.puts e.message
1370
1370
  end
1371
1371
 
1372
- def clear_line_breakpoints path
1373
- path = resolve_path(path)
1372
+ def clear_breakpoints(&condition)
1374
1373
  @bps.delete_if do |k, bp|
1375
- if (Array === k) && DEBUGGER__.compare_path(k.first, path)
1374
+ if condition.call(k, bp)
1376
1375
  bp.delete
1376
+ true
1377
1377
  end
1378
1378
  end
1379
1379
  end
1380
1380
 
1381
+ def clear_line_breakpoints path
1382
+ path = resolve_path(path)
1383
+ clear_breakpoints do |k, bp|
1384
+ bp.is_a?(LineBreakpoint) && DEBUGGER__.compare_path(k.first, path)
1385
+ end
1386
+ rescue Errno::ENOENT
1387
+ # just ignore
1388
+ end
1389
+
1390
+ def clear_catch_breakpoints *exception_names
1391
+ clear_breakpoints do |k, bp|
1392
+ bp.is_a?(CatchBreakpoint) && exception_names.include?(k[1])
1393
+ end
1394
+ end
1395
+
1396
+ def clear_all_breakpoints
1397
+ clear_breakpoints{true}
1398
+ end
1399
+
1381
1400
  def add_iseq_breakpoint iseq, **kw
1382
1401
  bp = ISeqBreakpoint.new(iseq, [:line], **kw)
1383
1402
  add_bp bp
@@ -1568,7 +1587,7 @@ module DEBUGGER__
1568
1587
  DEBUGGER__.info "Leave subsession (nested #{@subsession_stack.size})"
1569
1588
  end
1570
1589
 
1571
- @tc << type if type
1590
+ request_tc type if type
1572
1591
  @tc = nil
1573
1592
  rescue Exception => e
1574
1593
  STDERR.puts PP.pp([e, e.backtrace], ''.dup)
@@ -1583,7 +1602,9 @@ module DEBUGGER__
1583
1602
 
1584
1603
  def on_load iseq, src
1585
1604
  DEBUGGER__.info "Load #{iseq.absolute_path || iseq.path}"
1586
- @sr.add iseq, src
1605
+
1606
+ file_path, reloaded = @sr.add(iseq, src)
1607
+ @ui.event :load, file_path, reloaded
1587
1608
 
1588
1609
  pending_line_breakpoints = @bps.find_all do |key, bp|
1589
1610
  LineBreakpoint === bp && !bp.iseq
@@ -1591,7 +1612,18 @@ module DEBUGGER__
1591
1612
 
1592
1613
  pending_line_breakpoints.each do |_key, bp|
1593
1614
  if DEBUGGER__.compare_path(bp.path, (iseq.absolute_path || iseq.path))
1594
- bp.try_activate
1615
+ bp.try_activate iseq
1616
+ end
1617
+ end
1618
+
1619
+ if reloaded
1620
+ @bps.find_all do |key, bp|
1621
+ LineBreakpoint === bp && DEBUGGER__.compare_path(bp.path, file_path)
1622
+ end.each do |_key, bp|
1623
+ @bps.delete bp.key # to allow duplicate
1624
+ if nbp = LineBreakpoint.copy(bp, iseq)
1625
+ add_bp nbp
1626
+ end
1595
1627
  end
1596
1628
  end
1597
1629
  end
@@ -1616,9 +1648,10 @@ module DEBUGGER__
1616
1648
 
1617
1649
  def method_added tp
1618
1650
  b = tp.binding
1651
+
1619
1652
  if var_name = b.local_variables.first
1620
1653
  mid = b.local_variable_get(var_name)
1621
- unresolved = false
1654
+ resolved = true
1622
1655
 
1623
1656
  @bps.each{|k, bp|
1624
1657
  case bp
@@ -1629,15 +1662,57 @@ module DEBUGGER__
1629
1662
  end
1630
1663
  end
1631
1664
 
1632
- unresolved = true unless bp.enabled?
1665
+ resolved = false if !bp.enabled?
1633
1666
  end
1634
1667
  }
1635
- unless unresolved
1636
- METHOD_ADDED_TRACKER.disable
1668
+
1669
+ if resolved
1670
+ Session.deactivate_method_added_trackers
1671
+ end
1672
+
1673
+ case mid
1674
+ when :method_added, :singleton_method_added
1675
+ Session.create_method_added_tracker(tp.self, mid)
1676
+ Session.activate_method_added_trackers unless resolved
1637
1677
  end
1638
1678
  end
1639
1679
  end
1640
1680
 
1681
+ class ::Module
1682
+ undef method_added
1683
+ def method_added mid; end
1684
+ end
1685
+
1686
+ class ::BasicObject
1687
+ undef singleton_method_added
1688
+ def singleton_method_added mid; end
1689
+ end
1690
+
1691
+ def self.create_method_added_tracker mod, method_added_id, method_accessor = :method
1692
+ m = mod.__send__(method_accessor, method_added_id)
1693
+ METHOD_ADDED_TRACKERS[m] = TracePoint.new(:call) do |tp|
1694
+ SESSION.method_added tp
1695
+ end
1696
+ end
1697
+
1698
+ def self.activate_method_added_trackers
1699
+ METHOD_ADDED_TRACKERS.each do |m, tp|
1700
+ tp.enable(target: m) unless tp.enabled?
1701
+ rescue ArgumentError
1702
+ DEBUGGER__.warn "Methods defined under #{m.owner} can not track by the debugger."
1703
+ end
1704
+ end
1705
+
1706
+ def self.deactivate_method_added_trackers
1707
+ METHOD_ADDED_TRACKERS.each do |m, tp|
1708
+ tp.disable if tp.enabled?
1709
+ end
1710
+ end
1711
+
1712
+ METHOD_ADDED_TRACKERS = Hash.new
1713
+ create_method_added_tracker Module, :method_added, :instance_method
1714
+ create_method_added_tracker BasicObject, :singleton_method_added, :instance_method
1715
+
1641
1716
  def width
1642
1717
  @ui.width
1643
1718
  end
@@ -1818,9 +1893,11 @@ module DEBUGGER__
1818
1893
 
1819
1894
  def after_fork child: true
1820
1895
  if child || !@lock_file
1821
- @m = Mutex.new
1822
- @lock_level = 0
1823
- @lock_file = open(@lock_tempfile.path, 'w')
1896
+ @m = Mutex.new unless @m
1897
+ @m.synchronize do
1898
+ @lock_level = 0
1899
+ @lock_file = open(@lock_tempfile.path, 'w')
1900
+ end
1824
1901
  end
1825
1902
  end
1826
1903
 
@@ -2051,34 +2128,53 @@ module DEBUGGER__
2051
2128
  end
2052
2129
  end
2053
2130
 
2054
- class ::Module
2055
- undef method_added
2056
- def method_added mid; end
2057
- def singleton_method_added mid; end
2058
- end
2131
+ # Inspector
2059
2132
 
2060
- def self.method_added tp
2061
- begin
2062
- SESSION.method_added tp
2063
- rescue Exception => e
2064
- p e
2133
+ SHORT_INSPECT_LENGTH = 40
2134
+
2135
+ class LimitedPP
2136
+ def self.pp(obj, max=80)
2137
+ out = self.new(max)
2138
+ catch out do
2139
+ PP.singleline_pp(obj, out)
2140
+ end
2141
+ out.buf
2065
2142
  end
2066
- end
2067
2143
 
2068
- METHOD_ADDED_TRACKER = self.create_method_added_tracker
2144
+ attr_reader :buf
2069
2145
 
2070
- SHORT_INSPECT_LENGTH = 40
2146
+ def initialize max
2147
+ @max = max
2148
+ @cnt = 0
2149
+ @buf = String.new
2150
+ end
2071
2151
 
2072
- def self.safe_inspect obj, max_length: SHORT_INSPECT_LENGTH, short: false
2073
- str = obj.inspect
2152
+ def <<(other)
2153
+ @buf << other
2154
+
2155
+ if @buf.size >= @max
2156
+ @buf = @buf[0..@max] + '...'
2157
+ throw self
2158
+ end
2159
+ end
2160
+ end
2074
2161
 
2075
- if short && str.length > max_length
2076
- str[0...max_length] + '...'
2162
+ def self.safe_inspect obj, max_length: SHORT_INSPECT_LENGTH, short: false
2163
+ if short
2164
+ LimitedPP.pp(obj, max_length)
2165
+ else
2166
+ obj.inspect
2167
+ end
2168
+ rescue NoMethodError => e
2169
+ klass, oid = M_CLASS.bind_call(obj), M_OBJECT_ID.bind_call(obj)
2170
+ if obj == (r = e.receiver)
2171
+ "<\##{klass.name}#{oid} does not have \#inspect>"
2077
2172
  else
2078
- str
2173
+ rklass, roid = M_CLASS.bind_call(r), M_OBJECT_ID.bind_call(r)
2174
+ "<\##{klass.name}:#{roid} contains <\##{rklass}:#{roid} and it does not have #inspect>"
2079
2175
  end
2080
2176
  rescue Exception => e
2081
- str = "<#inspect raises #{e.inspect}>"
2177
+ "<#inspect raises #{e.inspect}>"
2082
2178
  end
2083
2179
 
2084
2180
  def self.warn msg
@@ -2089,18 +2185,27 @@ module DEBUGGER__
2089
2185
  log :INFO, msg
2090
2186
  end
2091
2187
 
2092
- def self.log level, msg
2093
- @logfile = STDERR unless defined? @logfile
2094
-
2188
+ def self.check_loglevel level
2095
2189
  lv = LOG_LEVELS[level]
2096
- config_lv = LOG_LEVELS[CONFIG[:log_level] || :WARN]
2190
+ config_lv = LOG_LEVELS[CONFIG[:log_level]]
2191
+ lv <= config_lv
2192
+ end
2097
2193
 
2098
- if defined? SESSION
2099
- pi = SESSION.process_info
2100
- process_info = pi ? "[#{pi}]" : nil
2194
+ def self.debug(&b)
2195
+ if check_loglevel :DEBUG
2196
+ log :DEBUG, b.call
2101
2197
  end
2198
+ end
2199
+
2200
+ def self.log level, msg
2201
+ if check_loglevel level
2202
+ @logfile = STDERR unless defined? @logfile
2203
+
2204
+ if defined? SESSION
2205
+ pi = SESSION.process_info
2206
+ process_info = pi ? "[#{pi}]" : nil
2207
+ end
2102
2208
 
2103
- if lv <= config_lv
2104
2209
  if level == :WARN
2105
2210
  # :WARN on debugger is general information
2106
2211
  @logfile.puts "DEBUGGER#{process_info}: #{msg}"
@@ -2126,7 +2231,7 @@ module DEBUGGER__
2126
2231
  # depend on the file system. So this check is only roughly estimation.
2127
2232
 
2128
2233
  def self.compare_path(a, b)
2129
- a.downcase == b.downcase
2234
+ a&.downcase == b&.downcase
2130
2235
  end
2131
2236
  else
2132
2237
  def self.compare_path(a, b)
@@ -2137,7 +2242,7 @@ module DEBUGGER__
2137
2242
  module ForkInterceptor
2138
2243
  if Process.respond_to? :_fork
2139
2244
  def _fork
2140
- return yield unless defined?(SESSION) && SESSION.active?
2245
+ return super unless defined?(SESSION) && SESSION.active?
2141
2246
 
2142
2247
  parent_hook, child_hook = __fork_setup_for_debugger
2143
2248
 
@@ -2153,7 +2258,7 @@ module DEBUGGER__
2153
2258
  end
2154
2259
  else
2155
2260
  def fork(&given_block)
2156
- return yield unless defined?(SESSION) && SESSION.active?
2261
+ return super unless defined?(SESSION) && SESSION.active?
2157
2262
  parent_hook, child_hook = __fork_setup_for_debugger
2158
2263
 
2159
2264
  if given_block
@@ -2178,12 +2283,10 @@ module DEBUGGER__
2178
2283
  end
2179
2284
 
2180
2285
  private def __fork_setup_for_debugger
2181
- unless fork_mode = CONFIG[:fork_mode]
2182
- if CONFIG[:parent_on_fork]
2183
- fork_mode = :parent
2184
- else
2185
- fork_mode = :both
2186
- end
2286
+ fork_mode = CONFIG[:fork_mode]
2287
+
2288
+ if fork_mode == :both && CONFIG[:parent_on_fork]
2289
+ fork_mode = :parent
2187
2290
  end
2188
2291
 
2189
2292
  parent_pid = Process.pid
@@ -2310,3 +2413,12 @@ class Binding
2310
2413
  alias break debugger
2311
2414
  alias b debugger
2312
2415
  end
2416
+
2417
+ # for Ruby 2.6 compatibility
2418
+ unless method(:p).unbind.respond_to? :bind_call
2419
+ class UnboundMethod
2420
+ def bind_call(obj, *args)
2421
+ self.bind(obj).call(*args)
2422
+ end
2423
+ end
2424
+ end