debug 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/debug/session.rb CHANGED
@@ -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'
@@ -32,6 +40,7 @@ $LOADED_FEATURES << File.expand_path(File.join(__dir__, '..', 'debug.rb'))
32
40
  require 'debug' # invalidate the $LOADED_FEATURE cache
33
41
 
34
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
@@ -197,9 +206,14 @@ module DEBUGGER__
197
206
  deactivate
198
207
  end
199
208
 
209
+ def request_tc(req)
210
+ @tc << req
211
+ end
212
+
200
213
  def process_event evt
201
214
  # variable `@internal_info` is only used for test
202
- tc, output, ev, @internal_info, *ev_args = evt
215
+ @tc, output, ev, @internal_info, *ev_args = evt
216
+
203
217
  output.each{|str| @ui.puts str} if ev != :suspend
204
218
 
205
219
  case ev
@@ -212,20 +226,18 @@ module DEBUGGER__
212
226
 
213
227
  when :init
214
228
  enter_subsession
215
- wait_command_loop tc
216
-
229
+ wait_command_loop
217
230
  when :load
218
231
  iseq, src = ev_args
219
232
  on_load iseq, src
220
- @ui.event :load
221
- tc << :continue
233
+ request_tc :continue
222
234
 
223
235
  when :trace
224
236
  trace_id, msg = ev_args
225
237
  if t = @tracers.values.find{|t| t.object_id == trace_id}
226
238
  t.puts msg
227
239
  end
228
- tc << :continue
240
+ request_tc :continue
229
241
 
230
242
  when :suspend
231
243
  enter_subsession if ev_args.first != :replay
@@ -235,24 +247,23 @@ module DEBUGGER__
235
247
  when :breakpoint
236
248
  bp, i = bp_index ev_args[1]
237
249
  clean_bps unless bp
238
- @ui.event :suspend_bp, i, bp, tc.id
250
+ @ui.event :suspend_bp, i, bp, @tc.id
239
251
  when :trap
240
- @ui.event :suspend_trap, sig = ev_args[1], tc.id
252
+ @ui.event :suspend_trap, sig = ev_args[1], @tc.id
241
253
 
242
254
  if sig == :SIGINT && (@intercepted_sigint_cmd.kind_of?(Proc) || @intercepted_sigint_cmd.kind_of?(String))
243
255
  @ui.puts "#{@intercepted_sigint_cmd.inspect} is registered as SIGINT handler."
244
256
  @ui.puts "`sigint` command execute it."
245
257
  end
246
258
  else
247
- @ui.event :suspended, tc.id
259
+ @ui.event :suspended, @tc.id
248
260
  end
249
261
 
250
262
  if @displays.empty?
251
- wait_command_loop tc
263
+ wait_command_loop
252
264
  else
253
- tc << [:eval, :display, @displays]
265
+ request_tc [:eval, :display, @displays]
254
266
  end
255
-
256
267
  when :result
257
268
  raise "[BUG] not in subsession" if @subsession_stack.empty?
258
269
 
@@ -283,14 +294,14 @@ module DEBUGGER__
283
294
  # ignore
284
295
  end
285
296
 
286
- wait_command_loop tc
297
+ wait_command_loop
287
298
 
288
299
  when :dap_result
289
300
  dap_event ev_args # server.rb
290
- wait_command_loop tc
301
+ wait_command_loop
291
302
  when :cdp_result
292
303
  cdp_event ev_args
293
- wait_command_loop tc
304
+ wait_command_loop
294
305
  end
295
306
  end
296
307
 
@@ -323,9 +334,7 @@ module DEBUGGER__
323
334
  "DEBUGGER__::SESSION"
324
335
  end
325
336
 
326
- def wait_command_loop tc
327
- @tc = tc
328
-
337
+ def wait_command_loop
329
338
  loop do
330
339
  case wait_command
331
340
  when :retry
@@ -529,32 +538,6 @@ module DEBUGGER__
529
538
  end
530
539
  end
531
540
 
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
541
  # * `catch <Error>`
559
542
  # * Set breakpoint on raising `<Error>`.
560
543
  # * `catch ... if: <expr>`
@@ -632,15 +615,15 @@ module DEBUGGER__
632
615
  when 'bt', 'backtrace'
633
616
  case arg
634
617
  when /\A(\d+)\z/
635
- @tc << [:show, :backtrace, arg.to_i, nil]
618
+ request_tc [:show, :backtrace, arg.to_i, nil]
636
619
  when /\A\/(.*)\/\z/
637
620
  pattern = $1
638
- @tc << [:show, :backtrace, nil, Regexp.compile(pattern)]
621
+ request_tc [:show, :backtrace, nil, Regexp.compile(pattern)]
639
622
  when /\A(\d+)\s+\/(.*)\/\z/
640
623
  max, pattern = $1, $2
641
- @tc << [:show, :backtrace, max.to_i, Regexp.compile(pattern)]
624
+ request_tc [:show, :backtrace, max.to_i, Regexp.compile(pattern)]
642
625
  else
643
- @tc << [:show, :backtrace, nil, nil]
626
+ request_tc [:show, :backtrace, nil, nil]
644
627
  end
645
628
 
646
629
  # * `l[ist]`
@@ -653,13 +636,13 @@ module DEBUGGER__
653
636
  when 'l', 'list'
654
637
  case arg ? arg.strip : nil
655
638
  when /\A(\d+)\z/
656
- @tc << [:show, :list, {start_line: arg.to_i - 1}]
639
+ request_tc [:show, :list, {start_line: arg.to_i - 1}]
657
640
  when /\A-\z/
658
- @tc << [:show, :list, {dir: -1}]
641
+ request_tc [:show, :list, {dir: -1}]
659
642
  when /\A(\d+)-(\d+)\z/
660
- @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}]
661
644
  when nil
662
- @tc << [:show, :list]
645
+ request_tc [:show, :list]
663
646
  else
664
647
  @ui.puts "Can not handle list argument: #{arg}"
665
648
  return :retry
@@ -683,7 +666,7 @@ module DEBUGGER__
683
666
  return :retry
684
667
  end
685
668
 
686
- @tc << [:show, :edit, arg]
669
+ request_tc [:show, :edit, arg]
687
670
 
688
671
  # * `i[nfo]`
689
672
  # * Show information about current frame (local/instance variables and defined constants).
@@ -710,15 +693,15 @@ module DEBUGGER__
710
693
 
711
694
  case sub
712
695
  when nil
713
- @tc << [:show, :default, pat] # something useful
696
+ request_tc [:show, :default, pat] # something useful
714
697
  when 'l', /^locals?/
715
- @tc << [:show, :locals, pat]
698
+ request_tc [:show, :locals, pat]
716
699
  when 'i', /^ivars?/i, /^instance[_ ]variables?/i
717
- @tc << [:show, :ivars, pat]
700
+ request_tc [:show, :ivars, pat]
718
701
  when 'c', /^consts?/i, /^constants?/i
719
- @tc << [:show, :consts, pat]
702
+ request_tc [:show, :consts, pat]
720
703
  when 'g', /^globals?/i, /^global[_ ]variables?/i
721
- @tc << [:show, :globals, pat]
704
+ request_tc [:show, :globals, pat]
722
705
  when 'th', /threads?/
723
706
  thread_list
724
707
  return :retry
@@ -734,7 +717,7 @@ module DEBUGGER__
734
717
  # * Show you available methods and instance variables of the given object.
735
718
  # * If the object is a class/module, it also lists its constants.
736
719
  when 'outline', 'o', 'ls'
737
- @tc << [:show, :outline, arg]
720
+ request_tc [:show, :outline, arg]
738
721
 
739
722
  # * `display`
740
723
  # * Show display setting.
@@ -743,9 +726,9 @@ module DEBUGGER__
743
726
  when 'display'
744
727
  if arg && !arg.empty?
745
728
  @displays << arg
746
- @tc << [:eval, :try_display, @displays]
729
+ request_tc [:eval, :try_display, @displays]
747
730
  else
748
- @tc << [:eval, :display, @displays]
731
+ request_tc [:eval, :display, @displays]
749
732
  end
750
733
 
751
734
  # * `undisplay`
@@ -758,7 +741,7 @@ module DEBUGGER__
758
741
  if @displays[n = $1.to_i]
759
742
  @displays.delete_at n
760
743
  end
761
- @tc << [:eval, :display, @displays]
744
+ request_tc [:eval, :display, @displays]
762
745
  when nil
763
746
  if ask "clear all?", 'N'
764
747
  @displays.clear
@@ -773,29 +756,29 @@ module DEBUGGER__
773
756
  # * `f[rame] <framenum>`
774
757
  # * Specify a current frame. Evaluation are run on specified frame.
775
758
  when 'frame', 'f'
776
- @tc << [:frame, :set, arg]
759
+ request_tc [:frame, :set, arg]
777
760
 
778
761
  # * `up`
779
762
  # * Specify the upper frame.
780
763
  when 'up'
781
- @tc << [:frame, :up]
764
+ request_tc [:frame, :up]
782
765
 
783
766
  # * `down`
784
767
  # * Specify the lower frame.
785
768
  when 'down'
786
- @tc << [:frame, :down]
769
+ request_tc [:frame, :down]
787
770
 
788
771
  ### Evaluate
789
772
 
790
773
  # * `p <expr>`
791
774
  # * Evaluate like `p <expr>` on the current frame.
792
775
  when 'p'
793
- @tc << [:eval, :p, arg.to_s]
776
+ request_tc [:eval, :p, arg.to_s]
794
777
 
795
778
  # * `pp <expr>`
796
779
  # * Evaluate like `pp <expr>` on the current frame.
797
780
  when 'pp'
798
- @tc << [:eval, :pp, arg.to_s]
781
+ request_tc [:eval, :pp, arg.to_s]
799
782
 
800
783
  # * `eval <expr>`
801
784
  # * Evaluate `<expr>` on the current frame.
@@ -805,7 +788,7 @@ module DEBUGGER__
805
788
  @ui.puts "\nTo evaluate the variable `#{cmd}`, use `pp #{cmd}` instead."
806
789
  return :retry
807
790
  else
808
- @tc << [:eval, :call, arg]
791
+ request_tc [:eval, :call, arg]
809
792
  end
810
793
 
811
794
  # * `irb`
@@ -815,7 +798,7 @@ module DEBUGGER__
815
798
  @ui.puts "not supported on the remote console."
816
799
  return :retry
817
800
  end
818
- @tc << [:eval, :irb]
801
+ request_tc [:eval, :irb]
819
802
 
820
803
  # don't repeat irb command
821
804
  @repl_prev_line = nil
@@ -872,7 +855,7 @@ module DEBUGGER__
872
855
  return :retry
873
856
 
874
857
  when /\Aobject\s+(.+)/
875
- @tc << [:trace, :object, $1.strip, {pattern: pattern, into: into}]
858
+ request_tc [:trace, :object, $1.strip, {pattern: pattern, into: into}]
876
859
 
877
860
  when /\Aoff\s+(\d+)\z/
878
861
  if t = @tracers.values[$1.to_i]
@@ -910,7 +893,7 @@ module DEBUGGER__
910
893
  when 'record'
911
894
  case arg
912
895
  when nil, 'on', 'off'
913
- @tc << [:record, arg&.to_sym]
896
+ request_tc [:record, arg&.to_sym]
914
897
  else
915
898
  @ui.puts "unknown command: #{arg}"
916
899
  return :retry
@@ -1005,7 +988,7 @@ module DEBUGGER__
1005
988
 
1006
989
  ### END
1007
990
  else
1008
- @tc << [:eval, :pp, line]
991
+ request_tc [:eval, :pp, line]
1009
992
  =begin
1010
993
  @repl_prev_line = nil
1011
994
  @ui.puts "unknown command: #{line}"
@@ -1062,7 +1045,7 @@ module DEBUGGER__
1062
1045
  case arg
1063
1046
  when nil, /\A\d+\z/
1064
1047
  if type == :in && @tc.recorder&.replaying?
1065
- @tc << [:step, type, arg&.to_i]
1048
+ request_tc [:step, type, arg&.to_i]
1066
1049
  else
1067
1050
  leave_subsession [:step, type, arg&.to_i]
1068
1051
  end
@@ -1071,7 +1054,7 @@ module DEBUGGER__
1071
1054
  @ui.puts "only `step #{arg}` is supported."
1072
1055
  :retry
1073
1056
  else
1074
- @tc << [:step, arg.to_sym]
1057
+ request_tc [:step, arg.to_sym]
1075
1058
  end
1076
1059
  else
1077
1060
  @ui.puts "Unknown option: #{arg}"
@@ -1081,11 +1064,18 @@ module DEBUGGER__
1081
1064
 
1082
1065
  def config_show key
1083
1066
  key = key.to_sym
1084
- if CONFIG_SET[key]
1067
+ config_detail = CONFIG_SET[key]
1068
+
1069
+ if config_detail
1085
1070
  v = CONFIG[key]
1086
- kv = "#{key} = #{v.nil? ? '(default)' : v.inspect}"
1087
- desc = CONFIG_SET[key][1]
1088
- 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]
1089
1079
  if line.size > SESSION.width
1090
1080
  @ui.puts "\# #{desc}\n#{kv}"
1091
1081
  else
@@ -1135,7 +1125,7 @@ module DEBUGGER__
1135
1125
  config_set $1, $2, append: true
1136
1126
 
1137
1127
  when /\A\s*append\s+(\w+)\s+(.+)\z/
1138
- config_set $1, $2
1128
+ config_set $1, $2, append: true
1139
1129
 
1140
1130
  when /\A(\w+)\z/
1141
1131
  config_show $1
@@ -1261,9 +1251,12 @@ module DEBUGGER__
1261
1251
  @repl_prev_line = nil
1262
1252
 
1263
1253
  if @bps.has_key? bp.key
1264
- unless bp.duplicable?
1254
+ if bp.duplicable?
1255
+ bp
1256
+ else
1265
1257
  @ui.puts "duplicated breakpoint: #{bp}"
1266
1258
  bp.disable
1259
+ nil
1267
1260
  end
1268
1261
  else
1269
1262
  @bps[bp.key] = bp
@@ -1320,7 +1313,7 @@ module DEBUGGER__
1320
1313
  when /\A(.+)[:\s+](\d+)\z/
1321
1314
  add_line_breakpoint $1, $2.to_i, cond: cond, command: cmd
1322
1315
  when /\A(.+)([\.\#])(.+)\z/
1323
- @tc << [:breakpoint, :method, $1, $2, $3, cond, cmd, path]
1316
+ request_tc [:breakpoint, :method, $1, $2, $3, cond, cmd, path]
1324
1317
  return :noretry
1325
1318
  when nil
1326
1319
  add_check_breakpoint cond, path, cmd
@@ -1347,11 +1340,11 @@ module DEBUGGER__
1347
1340
  cmd = ['watch', expr[:pre], expr[:do]] if expr[:pre] || expr[:do]
1348
1341
  path = Regexp.compile(expr[:path]) if expr[:path]
1349
1342
 
1350
- @tc << [:breakpoint, :watch, expr[:sig], cond, cmd, path]
1343
+ request_tc [:breakpoint, :watch, expr[:sig], cond, cmd, path]
1351
1344
  end
1352
1345
 
1353
- def add_catch_breakpoint pat
1354
- bp = CatchBreakpoint.new(pat)
1346
+ def add_catch_breakpoint pat, cond: nil
1347
+ bp = CatchBreakpoint.new(pat, cond: cond)
1355
1348
  add_bp bp
1356
1349
  end
1357
1350
 
@@ -1369,15 +1362,34 @@ module DEBUGGER__
1369
1362
  @ui.puts e.message
1370
1363
  end
1371
1364
 
1372
- def clear_line_breakpoints path
1373
- path = resolve_path(path)
1365
+ def clear_breakpoints(&condition)
1374
1366
  @bps.delete_if do |k, bp|
1375
- if (Array === k) && DEBUGGER__.compare_path(k.first, path)
1367
+ if condition.call(k, bp)
1376
1368
  bp.delete
1369
+ true
1377
1370
  end
1378
1371
  end
1379
1372
  end
1380
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
+
1381
1393
  def add_iseq_breakpoint iseq, **kw
1382
1394
  bp = ISeqBreakpoint.new(iseq, [:line], **kw)
1383
1395
  add_bp bp
@@ -1568,7 +1580,7 @@ module DEBUGGER__
1568
1580
  DEBUGGER__.info "Leave subsession (nested #{@subsession_stack.size})"
1569
1581
  end
1570
1582
 
1571
- @tc << type if type
1583
+ request_tc type if type
1572
1584
  @tc = nil
1573
1585
  rescue Exception => e
1574
1586
  STDERR.puts PP.pp([e, e.backtrace], ''.dup)
@@ -1583,7 +1595,9 @@ module DEBUGGER__
1583
1595
 
1584
1596
  def on_load iseq, src
1585
1597
  DEBUGGER__.info "Load #{iseq.absolute_path || iseq.path}"
1586
- @sr.add iseq, src
1598
+
1599
+ file_path, reloaded = @sr.add(iseq, src)
1600
+ @ui.event :load, file_path, reloaded
1587
1601
 
1588
1602
  pending_line_breakpoints = @bps.find_all do |key, bp|
1589
1603
  LineBreakpoint === bp && !bp.iseq
@@ -1591,7 +1605,18 @@ module DEBUGGER__
1591
1605
 
1592
1606
  pending_line_breakpoints.each do |_key, bp|
1593
1607
  if DEBUGGER__.compare_path(bp.path, (iseq.absolute_path || iseq.path))
1594
- bp.try_activate
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
1595
1620
  end
1596
1621
  end
1597
1622
  end
@@ -1618,7 +1643,7 @@ module DEBUGGER__
1618
1643
  b = tp.binding
1619
1644
  if var_name = b.local_variables.first
1620
1645
  mid = b.local_variable_get(var_name)
1621
- unresolved = false
1646
+ resolved = true
1622
1647
 
1623
1648
  @bps.each{|k, bp|
1624
1649
  case bp
@@ -1629,15 +1654,53 @@ module DEBUGGER__
1629
1654
  end
1630
1655
  end
1631
1656
 
1632
- unresolved = true unless bp.enabled?
1657
+ resolved = false if !bp.enabled?
1633
1658
  end
1634
1659
  }
1635
- unless unresolved
1636
- METHOD_ADDED_TRACKER.disable
1660
+
1661
+ if resolved
1662
+ Session.deactivate_method_added_trackers
1663
+ end
1664
+
1665
+ case mid
1666
+ when :method_added, :singleton_method_added
1667
+ Session.create_method_added_tracker(tp.self, mid)
1668
+ Session.create_method_added_tracker unless resolved
1637
1669
  end
1638
1670
  end
1639
1671
  end
1640
1672
 
1673
+ class ::Module
1674
+ undef method_added
1675
+ def method_added mid; end
1676
+ def singleton_method_added mid; end
1677
+ end
1678
+
1679
+ def self.create_method_added_tracker mod, method_added_id, method_accessor = :method
1680
+ m = mod.__send__(method_accessor, method_added_id)
1681
+ METHOD_ADDED_TRACKERS[m] = TracePoint.new(:call) do |tp|
1682
+ SESSION.method_added tp
1683
+ end
1684
+ end
1685
+
1686
+ def self.activate_method_added_trackers
1687
+ METHOD_ADDED_TRACKERS.each do |m, tp|
1688
+ tp.enable(target: m) unless tp.enabled?
1689
+ rescue ArgumentError
1690
+ DEBUGGER__.warn "Methods defined under #{m.owner} can not track by the debugger."
1691
+ end
1692
+ end
1693
+
1694
+ def self.deactivate_method_added_trackers
1695
+ METHOD_ADDED_TRACKERS.each do |m, tp|
1696
+ tp.disable if tp.enabled?
1697
+ end
1698
+ end
1699
+
1700
+ METHOD_ADDED_TRACKERS = Hash.new
1701
+ create_method_added_tracker Module, :method_added, :instance_method
1702
+ create_method_added_tracker Module, :singleton_method_added, :instance_method
1703
+
1641
1704
  def width
1642
1705
  @ui.width
1643
1706
  end
@@ -2051,34 +2114,53 @@ module DEBUGGER__
2051
2114
  end
2052
2115
  end
2053
2116
 
2054
- class ::Module
2055
- undef method_added
2056
- def method_added mid; end
2057
- def singleton_method_added mid; end
2058
- end
2117
+ # Inspector
2118
+
2119
+ SHORT_INSPECT_LENGTH = 40
2059
2120
 
2060
- def self.method_added tp
2061
- begin
2062
- SESSION.method_added tp
2063
- rescue Exception => e
2064
- p e
2121
+ class LimitedPP
2122
+ def self.pp(obj, max=80)
2123
+ out = self.new(max)
2124
+ catch out do
2125
+ PP.singleline_pp(obj, out)
2126
+ end
2127
+ out.buf
2065
2128
  end
2066
- end
2067
2129
 
2068
- METHOD_ADDED_TRACKER = self.create_method_added_tracker
2130
+ attr_reader :buf
2069
2131
 
2070
- SHORT_INSPECT_LENGTH = 40
2132
+ def initialize max
2133
+ @max = max
2134
+ @cnt = 0
2135
+ @buf = String.new
2136
+ end
2071
2137
 
2072
- def self.safe_inspect obj, max_length: SHORT_INSPECT_LENGTH, short: false
2073
- str = obj.inspect
2138
+ def <<(other)
2139
+ @buf << other
2074
2140
 
2075
- if short && str.length > max_length
2076
- str[0...max_length] + '...'
2141
+ if @buf.size >= @max
2142
+ @buf = @buf[0..@max] + '...'
2143
+ throw self
2144
+ end
2145
+ end
2146
+ end
2147
+
2148
+ def self.safe_inspect obj, max_length: SHORT_INSPECT_LENGTH, short: false
2149
+ if short
2150
+ LimitedPP.pp(obj, max_length)
2151
+ else
2152
+ obj.inspect
2153
+ end
2154
+ rescue NoMethodError => e
2155
+ klass, oid = M_CLASS.bind_call(obj), M_OBJECT_ID.bind_call(obj)
2156
+ if obj == (r = e.receiver)
2157
+ "<\##{klass.name}#{oid} does not have \#inspect>"
2077
2158
  else
2078
- str
2159
+ rklass, roid = M_CLASS.bind_call(r), M_OBJECT_ID.bind_call(r)
2160
+ "<\##{klass.name}:#{roid} contains <\##{rklass}:#{roid} and it does not have #inspect>"
2079
2161
  end
2080
2162
  rescue Exception => e
2081
- str = "<#inspect raises #{e.inspect}>"
2163
+ "<#inspect raises #{e.inspect}>"
2082
2164
  end
2083
2165
 
2084
2166
  def self.warn msg
@@ -2089,18 +2171,27 @@ module DEBUGGER__
2089
2171
  log :INFO, msg
2090
2172
  end
2091
2173
 
2092
- def self.log level, msg
2093
- @logfile = STDERR unless defined? @logfile
2094
-
2174
+ def self.check_loglevel level
2095
2175
  lv = LOG_LEVELS[level]
2096
- config_lv = LOG_LEVELS[CONFIG[:log_level] || :WARN]
2176
+ config_lv = LOG_LEVELS[CONFIG[:log_level]]
2177
+ lv <= config_lv
2178
+ end
2097
2179
 
2098
- if defined? SESSION
2099
- pi = SESSION.process_info
2100
- process_info = pi ? "[#{pi}]" : nil
2180
+ def self.debug(&b)
2181
+ if check_loglevel :DEBUG
2182
+ log :DEBUG, b.call
2101
2183
  end
2184
+ end
2185
+
2186
+ def self.log level, msg
2187
+ if check_loglevel level
2188
+ @logfile = STDERR unless defined? @logfile
2189
+
2190
+ if defined? SESSION
2191
+ pi = SESSION.process_info
2192
+ process_info = pi ? "[#{pi}]" : nil
2193
+ end
2102
2194
 
2103
- if lv <= config_lv
2104
2195
  if level == :WARN
2105
2196
  # :WARN on debugger is general information
2106
2197
  @logfile.puts "DEBUGGER#{process_info}: #{msg}"
@@ -2137,7 +2228,7 @@ module DEBUGGER__
2137
2228
  module ForkInterceptor
2138
2229
  if Process.respond_to? :_fork
2139
2230
  def _fork
2140
- return yield unless defined?(SESSION) && SESSION.active?
2231
+ return super unless defined?(SESSION) && SESSION.active?
2141
2232
 
2142
2233
  parent_hook, child_hook = __fork_setup_for_debugger
2143
2234
 
@@ -2153,7 +2244,7 @@ module DEBUGGER__
2153
2244
  end
2154
2245
  else
2155
2246
  def fork(&given_block)
2156
- return yield unless defined?(SESSION) && SESSION.active?
2247
+ return super unless defined?(SESSION) && SESSION.active?
2157
2248
  parent_hook, child_hook = __fork_setup_for_debugger
2158
2249
 
2159
2250
  if given_block
@@ -2178,12 +2269,10 @@ module DEBUGGER__
2178
2269
  end
2179
2270
 
2180
2271
  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
2272
+ fork_mode = CONFIG[:fork_mode]
2273
+
2274
+ if fork_mode == :both && CONFIG[:parent_on_fork]
2275
+ fork_mode = :parent
2187
2276
  end
2188
2277
 
2189
2278
  parent_pid = Process.pid
@@ -2310,3 +2399,12 @@ class Binding
2310
2399
  alias break debugger
2311
2400
  alias b debugger
2312
2401
  end
2402
+
2403
+ # for Ruby 2.6 compatibility
2404
+ unless method(:p).unbind.respond_to? :bind_call
2405
+ class UnboundMethod
2406
+ def bind_call(obj, *args)
2407
+ self.bind(obj).call(*args)
2408
+ end
2409
+ end
2410
+ end