debug 1.5.0 → 1.6.0

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,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