debug 1.5.0 → 1.6.2

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
@@ -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
@@ -2051,34 +2126,53 @@ module DEBUGGER__
2051
2126
  end
2052
2127
  end
2053
2128
 
2054
- class ::Module
2055
- undef method_added
2056
- def method_added mid; end
2057
- def singleton_method_added mid; end
2058
- end
2129
+ # Inspector
2059
2130
 
2060
- def self.method_added tp
2061
- begin
2062
- SESSION.method_added tp
2063
- rescue Exception => e
2064
- p e
2131
+ SHORT_INSPECT_LENGTH = 40
2132
+
2133
+ class LimitedPP
2134
+ def self.pp(obj, max=80)
2135
+ out = self.new(max)
2136
+ catch out do
2137
+ PP.singleline_pp(obj, out)
2138
+ end
2139
+ out.buf
2065
2140
  end
2066
- end
2067
2141
 
2068
- METHOD_ADDED_TRACKER = self.create_method_added_tracker
2142
+ attr_reader :buf
2069
2143
 
2070
- SHORT_INSPECT_LENGTH = 40
2144
+ def initialize max
2145
+ @max = max
2146
+ @cnt = 0
2147
+ @buf = String.new
2148
+ end
2071
2149
 
2072
- def self.safe_inspect obj, max_length: SHORT_INSPECT_LENGTH, short: false
2073
- str = obj.inspect
2150
+ def <<(other)
2151
+ @buf << other
2152
+
2153
+ if @buf.size >= @max
2154
+ @buf = @buf[0..@max] + '...'
2155
+ throw self
2156
+ end
2157
+ end
2158
+ end
2074
2159
 
2075
- if short && str.length > max_length
2076
- str[0...max_length] + '...'
2160
+ def self.safe_inspect obj, max_length: SHORT_INSPECT_LENGTH, short: false
2161
+ if short
2162
+ LimitedPP.pp(obj, max_length)
2163
+ else
2164
+ obj.inspect
2165
+ end
2166
+ rescue NoMethodError => e
2167
+ klass, oid = M_CLASS.bind_call(obj), M_OBJECT_ID.bind_call(obj)
2168
+ if obj == (r = e.receiver)
2169
+ "<\##{klass.name}#{oid} does not have \#inspect>"
2077
2170
  else
2078
- str
2171
+ rklass, roid = M_CLASS.bind_call(r), M_OBJECT_ID.bind_call(r)
2172
+ "<\##{klass.name}:#{roid} contains <\##{rklass}:#{roid} and it does not have #inspect>"
2079
2173
  end
2080
2174
  rescue Exception => e
2081
- str = "<#inspect raises #{e.inspect}>"
2175
+ "<#inspect raises #{e.inspect}>"
2082
2176
  end
2083
2177
 
2084
2178
  def self.warn msg
@@ -2089,18 +2183,27 @@ module DEBUGGER__
2089
2183
  log :INFO, msg
2090
2184
  end
2091
2185
 
2092
- def self.log level, msg
2093
- @logfile = STDERR unless defined? @logfile
2094
-
2186
+ def self.check_loglevel level
2095
2187
  lv = LOG_LEVELS[level]
2096
- config_lv = LOG_LEVELS[CONFIG[:log_level] || :WARN]
2188
+ config_lv = LOG_LEVELS[CONFIG[:log_level]]
2189
+ lv <= config_lv
2190
+ end
2097
2191
 
2098
- if defined? SESSION
2099
- pi = SESSION.process_info
2100
- process_info = pi ? "[#{pi}]" : nil
2192
+ def self.debug(&b)
2193
+ if check_loglevel :DEBUG
2194
+ log :DEBUG, b.call
2101
2195
  end
2196
+ end
2197
+
2198
+ def self.log level, msg
2199
+ if check_loglevel level
2200
+ @logfile = STDERR unless defined? @logfile
2201
+
2202
+ if defined? SESSION
2203
+ pi = SESSION.process_info
2204
+ process_info = pi ? "[#{pi}]" : nil
2205
+ end
2102
2206
 
2103
- if lv <= config_lv
2104
2207
  if level == :WARN
2105
2208
  # :WARN on debugger is general information
2106
2209
  @logfile.puts "DEBUGGER#{process_info}: #{msg}"
@@ -2137,7 +2240,7 @@ module DEBUGGER__
2137
2240
  module ForkInterceptor
2138
2241
  if Process.respond_to? :_fork
2139
2242
  def _fork
2140
- return yield unless defined?(SESSION) && SESSION.active?
2243
+ return super unless defined?(SESSION) && SESSION.active?
2141
2244
 
2142
2245
  parent_hook, child_hook = __fork_setup_for_debugger
2143
2246
 
@@ -2153,7 +2256,7 @@ module DEBUGGER__
2153
2256
  end
2154
2257
  else
2155
2258
  def fork(&given_block)
2156
- return yield unless defined?(SESSION) && SESSION.active?
2259
+ return super unless defined?(SESSION) && SESSION.active?
2157
2260
  parent_hook, child_hook = __fork_setup_for_debugger
2158
2261
 
2159
2262
  if given_block
@@ -2178,12 +2281,10 @@ module DEBUGGER__
2178
2281
  end
2179
2282
 
2180
2283
  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
2284
+ fork_mode = CONFIG[:fork_mode]
2285
+
2286
+ if fork_mode == :both && CONFIG[:parent_on_fork]
2287
+ fork_mode = :parent
2187
2288
  end
2188
2289
 
2189
2290
  parent_pid = Process.pid
@@ -2310,3 +2411,12 @@ class Binding
2310
2411
  alias break debugger
2311
2412
  alias b debugger
2312
2413
  end
2414
+
2415
+ # for Ruby 2.6 compatibility
2416
+ unless method(:p).unbind.respond_to? :bind_call
2417
+ class UnboundMethod
2418
+ def bind_call(obj, *args)
2419
+ self.bind(obj).call(*args)
2420
+ end
2421
+ end
2422
+ end