debug 1.7.2 → 1.9.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.
@@ -274,198 +274,225 @@ module DEBUGGER__
274
274
  retry
275
275
  end
276
276
 
277
+ def load_extensions req
278
+ if exts = req.dig('arguments', 'rdbgExtensions')
279
+ exts.each{|ext|
280
+ require_relative "dap_custom/#{File.basename(ext)}"
281
+ }
282
+ end
283
+
284
+ if scripts = req.dig('arguments', 'rdbgInitialScripts')
285
+ scripts.each do |script|
286
+ begin
287
+ eval(script)
288
+ rescue Exception => e
289
+ puts e.message
290
+ puts e.backtrace.inspect
291
+ end
292
+ end
293
+ end
294
+ end
295
+
277
296
  def process
278
297
  while req = recv_request
279
- raise "not a request: #{req.inspect}" unless req['type'] == 'request'
280
- args = req.dig('arguments')
298
+ process_request(req)
299
+ end
300
+ ensure
301
+ send_event :terminated unless @sock.closed?
302
+ end
281
303
 
282
- case req['command']
304
+ def process_request req
305
+ raise "not a request: #{req.inspect}" unless req['type'] == 'request'
306
+ args = req.dig('arguments')
283
307
 
284
- ## boot/configuration
285
- when 'launch'
286
- send_response req
287
- # `launch` runs on debuggee on the same file system
288
- UI_DAP.local_fs_map_set req.dig('arguments', 'localfs') || req.dig('arguments', 'localfsMap') || true
289
- @nonstop = true
308
+ case req['command']
290
309
 
291
- when 'attach'
292
- send_response req
293
- UI_DAP.local_fs_map_set req.dig('arguments', 'localfs') || req.dig('arguments', 'localfsMap')
310
+ ## boot/configuration
311
+ when 'launch'
312
+ send_response req
313
+ # `launch` runs on debuggee on the same file system
314
+ UI_DAP.local_fs_map_set req.dig('arguments', 'localfs') || req.dig('arguments', 'localfsMap') || true
315
+ @nonstop = true
294
316
 
295
- if req.dig('arguments', 'nonstop') == true
296
- @nonstop = true
297
- else
298
- @nonstop = false
299
- end
317
+ load_extensions req
300
318
 
301
- when 'configurationDone'
302
- send_response req
319
+ when 'attach'
320
+ send_response req
321
+ UI_DAP.local_fs_map_set req.dig('arguments', 'localfs') || req.dig('arguments', 'localfsMap')
303
322
 
304
- if @nonstop
305
- @q_msg << 'continue'
306
- else
307
- if SESSION.in_subsession?
308
- send_event 'stopped', reason: 'pause',
309
- threadId: 1, # maybe ...
310
- allThreadsStopped: true
311
- end
312
- end
323
+ if req.dig('arguments', 'nonstop') == true
324
+ @nonstop = true
325
+ else
326
+ @nonstop = false
327
+ end
313
328
 
314
- when 'setBreakpoints'
315
- req_path = args.dig('source', 'path')
316
- path = UI_DAP.local_to_remote_path(req_path)
317
- if path
318
- SESSION.clear_line_breakpoints path
319
-
320
- bps = []
321
- args['breakpoints'].each{|bp|
322
- line = bp['line']
323
- if cond = bp['condition']
324
- bps << SESSION.add_line_breakpoint(path, line, cond: cond)
325
- else
326
- bps << SESSION.add_line_breakpoint(path, line)
327
- end
328
- }
329
- send_response req, breakpoints: (bps.map do |bp| {verified: true,} end)
330
- else
331
- send_response req, success: false, message: "#{req_path} is not available"
332
- end
329
+ load_extensions req
333
330
 
334
- when 'setFunctionBreakpoints'
335
- send_response req
331
+ when 'configurationDone'
332
+ send_response req
336
333
 
337
- when 'setExceptionBreakpoints'
338
- process_filter = ->(filter_id, cond = nil) {
339
- bp =
340
- case filter_id
341
- when 'any'
342
- SESSION.add_catch_breakpoint 'Exception', cond: cond
343
- when 'RuntimeError'
344
- SESSION.add_catch_breakpoint 'RuntimeError', cond: cond
345
- else
346
- nil
347
- end
348
- {
349
- verified: !bp.nil?,
350
- message: bp.inspect,
351
- }
352
- }
334
+ if @nonstop
335
+ @q_msg << 'continue'
336
+ else
337
+ if SESSION.in_subsession?
338
+ send_event 'stopped', reason: 'pause',
339
+ threadId: 1, # maybe ...
340
+ allThreadsStopped: true
341
+ end
342
+ end
353
343
 
354
- SESSION.clear_catch_breakpoints 'Exception', 'RuntimeError'
344
+ when 'setBreakpoints'
345
+ req_path = args.dig('source', 'path')
346
+ path = UI_DAP.local_to_remote_path(req_path)
347
+ if path
348
+ SESSION.clear_line_breakpoints path
349
+
350
+ bps = []
351
+ args['breakpoints'].each{|bp|
352
+ line = bp['line']
353
+ if cond = bp['condition']
354
+ bps << SESSION.add_line_breakpoint(path, line, cond: cond)
355
+ else
356
+ bps << SESSION.add_line_breakpoint(path, line)
357
+ end
358
+ }
359
+ send_response req, breakpoints: (bps.map do |bp| {verified: true,} end)
360
+ else
361
+ send_response req, success: false, message: "#{req_path} is not available"
362
+ end
355
363
 
356
- filters = args.fetch('filters').map {|filter_id|
357
- process_filter.call(filter_id)
364
+ when 'setFunctionBreakpoints'
365
+ send_response req
366
+
367
+ when 'setExceptionBreakpoints'
368
+ process_filter = ->(filter_id, cond = nil) {
369
+ bp =
370
+ case filter_id
371
+ when 'any'
372
+ SESSION.add_catch_breakpoint 'Exception', cond: cond
373
+ when 'RuntimeError'
374
+ SESSION.add_catch_breakpoint 'RuntimeError', cond: cond
375
+ else
376
+ nil
377
+ end
378
+ {
379
+ verified: !bp.nil?,
380
+ message: bp.inspect,
358
381
  }
382
+ }
383
+
384
+ SESSION.clear_catch_breakpoints 'Exception', 'RuntimeError'
359
385
 
360
- filters += args.fetch('filterOptions', {}).map{|bp_info|
361
- process_filter.call(bp_info['filterId'], bp_info['condition'])
386
+ filters = args.fetch('filters').map {|filter_id|
387
+ process_filter.call(filter_id)
362
388
  }
363
389
 
364
- send_response req, breakpoints: filters
390
+ filters += args.fetch('filterOptions', {}).map{|bp_info|
391
+ process_filter.call(bp_info['filterId'], bp_info['condition'])
392
+ }
365
393
 
366
- when 'disconnect'
367
- terminate = args.fetch("terminateDebuggee", false)
394
+ send_response req, breakpoints: filters
368
395
 
369
- SESSION.clear_all_breakpoints
370
- send_response req
396
+ when 'disconnect'
397
+ terminate = args.fetch("terminateDebuggee", false)
371
398
 
372
- if SESSION.in_subsession?
373
- if terminate
374
- @q_msg << 'kill!'
375
- else
376
- @q_msg << 'continue'
377
- end
378
- else
379
- if terminate
380
- @q_msg << 'kill!'
381
- pause
382
- end
383
- end
399
+ SESSION.clear_all_breakpoints
400
+ send_response req
384
401
 
385
- ## control
386
- when 'continue'
387
- @q_msg << 'c'
388
- send_response req, allThreadsContinued: true
389
- when 'next'
390
- begin
391
- @session.check_postmortem
392
- @q_msg << 'n'
393
- send_response req
394
- rescue PostmortemError
395
- send_response req,
396
- success: false, message: 'postmortem mode',
397
- result: "'Next' is not supported while postmortem mode"
398
- end
399
- when 'stepIn'
400
- begin
401
- @session.check_postmortem
402
- @q_msg << 's'
403
- send_response req
404
- rescue PostmortemError
405
- send_response req,
406
- success: false, message: 'postmortem mode',
407
- result: "'stepIn' is not supported while postmortem mode"
402
+ if SESSION.in_subsession?
403
+ if terminate
404
+ @q_msg << 'kill!'
405
+ else
406
+ @q_msg << 'continue'
408
407
  end
409
- when 'stepOut'
410
- begin
411
- @session.check_postmortem
412
- @q_msg << 'fin'
413
- send_response req
414
- rescue PostmortemError
415
- send_response req,
416
- success: false, message: 'postmortem mode',
417
- result: "'stepOut' is not supported while postmortem mode"
408
+ else
409
+ if terminate
410
+ @q_msg << 'kill!'
411
+ pause
418
412
  end
419
- when 'terminate'
413
+ end
414
+
415
+ ## control
416
+ when 'continue'
417
+ @q_msg << 'c'
418
+ send_response req, allThreadsContinued: true
419
+ when 'next'
420
+ begin
421
+ @session.check_postmortem
422
+ @q_msg << 'n'
420
423
  send_response req
421
- exit
422
- when 'pause'
424
+ rescue PostmortemError
425
+ send_response req,
426
+ success: false, message: 'postmortem mode',
427
+ result: "'Next' is not supported while postmortem mode"
428
+ end
429
+ when 'stepIn'
430
+ begin
431
+ @session.check_postmortem
432
+ @q_msg << 's'
423
433
  send_response req
424
- Process.kill(UI_ServerBase::TRAP_SIGNAL, Process.pid)
425
- when 'reverseContinue'
434
+ rescue PostmortemError
426
435
  send_response req,
427
- success: false, message: 'cancelled',
428
- result: "Reverse Continue is not supported. Only \"Step back\" is supported."
429
- when 'stepBack'
430
- @q_msg << req
436
+ success: false, message: 'postmortem mode',
437
+ result: "'stepIn' is not supported while postmortem mode"
438
+ end
439
+ when 'stepOut'
440
+ begin
441
+ @session.check_postmortem
442
+ @q_msg << 'fin'
443
+ send_response req
444
+ rescue PostmortemError
445
+ send_response req,
446
+ success: false, message: 'postmortem mode',
447
+ result: "'stepOut' is not supported while postmortem mode"
448
+ end
449
+ when 'terminate'
450
+ send_response req
451
+ exit
452
+ when 'pause'
453
+ send_response req
454
+ Process.kill(UI_ServerBase::TRAP_SIGNAL, Process.pid)
455
+ when 'reverseContinue'
456
+ send_response req,
457
+ success: false, message: 'cancelled',
458
+ result: "Reverse Continue is not supported. Only \"Step back\" is supported."
459
+ when 'stepBack'
460
+ @q_msg << req
431
461
 
432
- ## query
433
- when 'threads'
434
- send_response req, threads: SESSION.managed_thread_clients.map{|tc|
435
- { id: tc.id,
436
- name: tc.name,
437
- }
462
+ ## query
463
+ when 'threads'
464
+ send_response req, threads: SESSION.managed_thread_clients.map{|tc|
465
+ { id: tc.id,
466
+ name: tc.name,
438
467
  }
468
+ }
439
469
 
440
- when 'evaluate'
441
- expr = req.dig('arguments', 'expression')
442
- if /\A\s*,(.+)\z/ =~ expr
443
- dbg_expr = $1.strip
444
- dbg_expr.split(';;') { |cmd| @q_msg << cmd }
470
+ when 'evaluate'
471
+ expr = req.dig('arguments', 'expression')
472
+ if /\A\s*,(.+)\z/ =~ expr
473
+ dbg_expr = $1.strip
474
+ dbg_expr.split(';;') { |cmd| @q_msg << cmd }
445
475
 
446
- send_response req,
447
- result: "(rdbg:command) #{dbg_expr}",
448
- variablesReference: 0
449
- else
450
- @q_msg << req
451
- end
452
- when 'stackTrace',
453
- 'scopes',
454
- 'variables',
455
- 'source',
456
- 'completions'
476
+ send_response req,
477
+ result: "(rdbg:command) #{dbg_expr}",
478
+ variablesReference: 0
479
+ else
457
480
  @q_msg << req
481
+ end
482
+ when 'stackTrace',
483
+ 'scopes',
484
+ 'variables',
485
+ 'source',
486
+ 'completions'
487
+ @q_msg << req
458
488
 
489
+ else
490
+ if respond_to? mid = "custom_dap_request_#{req['command']}"
491
+ __send__ mid, req
459
492
  else
460
- if respond_to? mid = "custom_dap_request_#{req['command']}"
461
- __send__ mid, req
462
- else
463
- raise "Unknown request: #{req.inspect}"
464
- end
493
+ raise "Unknown request: #{req.inspect}"
465
494
  end
466
495
  end
467
- ensure
468
- send_event :terminated unless @sock.closed?
469
496
  end
470
497
 
471
498
  ## called by the SESSION thread
@@ -625,6 +652,7 @@ module DEBUGGER__
625
652
  expr = req.dig('arguments', 'expression')
626
653
 
627
654
  if find_waiting_tc(tid)
655
+ restart_all_threads
628
656
  request_tc [:dap, :evaluate, req, fid, expr, context]
629
657
  else
630
658
  fail_response req
@@ -701,6 +729,7 @@ module DEBUGGER__
701
729
  register_vars result[:variables], tid
702
730
  @ui.respond req, result
703
731
  when :evaluate
732
+ stop_all_threads
704
733
  message = result.delete :message
705
734
  if message
706
735
  @ui.respond req, success: false, message: message
@@ -759,7 +788,9 @@ module DEBUGGER__
759
788
 
760
789
  def dap_eval b, expr, _context, prompt: '(repl_eval)'
761
790
  begin
762
- b.eval(expr.to_s, prompt)
791
+ tp_allow_reentry do
792
+ b.eval(expr.to_s, prompt)
793
+ end
763
794
  rescue Exception => e
764
795
  e
765
796
  end
@@ -867,7 +898,7 @@ module DEBUGGER__
867
898
  }
868
899
  when String
869
900
  vars = [
870
- variable('#lengthddsfsd', obj.length),
901
+ variable('#length', obj.length),
871
902
  variable('#encoding', obj.encoding),
872
903
  ]
873
904
  printed_str = value_inspect(obj)
@@ -1014,7 +1045,7 @@ module DEBUGGER__
1014
1045
  klass = M_CLASS.bind_call(obj)
1015
1046
 
1016
1047
  begin
1017
- klass.name || klass.to_s
1048
+ M_NAME.bind_call(klass) || klass.to_s
1018
1049
  rescue Exception => e
1019
1050
  "<Error: #{e.message} (#{e.backtrace.first}>"
1020
1051
  end
data/lib/debug/session.rb CHANGED
@@ -82,7 +82,7 @@ class RubyVM::InstructionSequence
82
82
  def first_line
83
83
  self.to_a[4][:code_location][0]
84
84
  end unless method_defined?(:first_line)
85
- end
85
+ end if defined?(RubyVM::InstructionSequence)
86
86
 
87
87
  module DEBUGGER__
88
88
  PresetCommands = Struct.new(:commands, :source, :auto_continue)
@@ -133,7 +133,7 @@ module DEBUGGER__
133
133
  @commands = {}
134
134
  @unsafe_context = false
135
135
 
136
- @has_keep_script_lines = RubyVM.respond_to? :keep_script_lines
136
+ @has_keep_script_lines = defined?(RubyVM.keep_script_lines)
137
137
 
138
138
  @tp_load_script = TracePoint.new(:script_compiled){|tp|
139
139
  eval_script = tp.eval_script unless @has_keep_script_lines
@@ -202,6 +202,11 @@ module DEBUGGER__
202
202
  end
203
203
  @tp_thread_end.enable
204
204
 
205
+ if CONFIG[:irb_console] && !CONFIG[:open]
206
+ require_relative "irb_integration"
207
+ thc.activate_irb_integration
208
+ end
209
+
205
210
  # session start
206
211
  q << true
207
212
  session_server_main
@@ -256,6 +261,15 @@ module DEBUGGER__
256
261
  @tc << req
257
262
  end
258
263
 
264
+ def request_tc_with_restarted_threads(req)
265
+ restart_all_threads
266
+ request_tc(req)
267
+ end
268
+
269
+ def request_eval type, src
270
+ request_tc_with_restarted_threads [:eval, type, src]
271
+ end
272
+
259
273
  def process_event evt
260
274
  # variable `@internal_info` is only used for test
261
275
  tc, output, ev, @internal_info, *ev_args = evt
@@ -314,7 +328,7 @@ module DEBUGGER__
314
328
  if @displays.empty?
315
329
  wait_command_loop
316
330
  else
317
- request_tc [:eval, :display, @displays]
331
+ request_eval :display, @displays
318
332
  end
319
333
  when :result
320
334
  raise "[BUG] not in subsession" if @subsession_stack.empty?
@@ -329,6 +343,7 @@ module DEBUGGER__
329
343
  end
330
344
  end
331
345
 
346
+ stop_all_threads
332
347
  when :method_breakpoint, :watch_breakpoint
333
348
  bp = ev_args[1]
334
349
  if bp
@@ -342,6 +357,7 @@ module DEBUGGER__
342
357
  obj_inspect = ev_args[2]
343
358
  opt = ev_args[3]
344
359
  add_tracer ObjectTracer.new(@ui, obj_id, obj_inspect, **opt)
360
+ stop_all_threads
345
361
  else
346
362
  stop_all_threads
347
363
  end
@@ -685,15 +701,15 @@ module DEBUGGER__
685
701
  register_command 'bt', 'backtrace', unsafe: false do |arg|
686
702
  case arg
687
703
  when /\A(\d+)\z/
688
- request_tc [:show, :backtrace, arg.to_i, nil]
704
+ request_tc_with_restarted_threads [:show, :backtrace, arg.to_i, nil]
689
705
  when /\A\/(.*)\/\z/
690
706
  pattern = $1
691
- request_tc [:show, :backtrace, nil, Regexp.compile(pattern)]
707
+ request_tc_with_restarted_threads [:show, :backtrace, nil, Regexp.compile(pattern)]
692
708
  when /\A(\d+)\s+\/(.*)\/\z/
693
709
  max, pattern = $1, $2
694
- request_tc [:show, :backtrace, max.to_i, Regexp.compile(pattern)]
710
+ request_tc_with_restarted_threads [:show, :backtrace, max.to_i, Regexp.compile(pattern)]
695
711
  else
696
- request_tc [:show, :backtrace, nil, nil]
712
+ request_tc_with_restarted_threads [:show, :backtrace, nil, nil]
697
713
  end
698
714
  end
699
715
 
@@ -810,15 +826,15 @@ module DEBUGGER__
810
826
 
811
827
  case sub
812
828
  when nil
813
- request_tc [:show, :default, pat] # something useful
829
+ request_tc_with_restarted_threads [:show, :default, pat] # something useful
814
830
  when :locals
815
- request_tc [:show, :locals, pat]
831
+ request_tc_with_restarted_threads [:show, :locals, pat]
816
832
  when :ivars
817
- request_tc [:show, :ivars, pat, opt]
833
+ request_tc_with_restarted_threads [:show, :ivars, pat, opt]
818
834
  when :consts
819
- request_tc [:show, :consts, pat, opt]
835
+ request_tc_with_restarted_threads [:show, :consts, pat, opt]
820
836
  when :globals
821
- request_tc [:show, :globals, pat]
837
+ request_tc_with_restarted_threads [:show, :globals, pat]
822
838
  when :threads
823
839
  thread_list
824
840
  :retry
@@ -838,7 +854,7 @@ module DEBUGGER__
838
854
  # * Show you available methods and instance variables of the given object.
839
855
  # * If the object is a class/module, it also lists its constants.
840
856
  register_command 'outline', 'o', 'ls', unsafe: false do |arg|
841
- request_tc [:show, :outline, arg]
857
+ request_tc_with_restarted_threads [:show, :outline, arg]
842
858
  end
843
859
 
844
860
  # * `display`
@@ -848,9 +864,9 @@ module DEBUGGER__
848
864
  register_command 'display', postmortem: false do |arg|
849
865
  if arg && !arg.empty?
850
866
  @displays << arg
851
- request_tc [:eval, :try_display, @displays]
867
+ request_eval :try_display, @displays
852
868
  else
853
- request_tc [:eval, :display, @displays]
869
+ request_eval :display, @displays
854
870
  end
855
871
  end
856
872
 
@@ -864,7 +880,7 @@ module DEBUGGER__
864
880
  if @displays[n = $1.to_i]
865
881
  @displays.delete_at n
866
882
  end
867
- request_tc [:eval, :display, @displays]
883
+ request_eval :display, @displays
868
884
  when nil
869
885
  if ask "clear all?", 'N'
870
886
  @displays.clear
@@ -925,10 +941,11 @@ module DEBUGGER__
925
941
  # * Invoke `irb` on the current frame.
926
942
  register_command 'irb' do |arg|
927
943
  if @ui.remote?
928
- @ui.puts "not supported on the remote console."
944
+ @ui.puts "\nIRB is not supported on the remote console."
929
945
  :retry
946
+ else
947
+ request_eval :irb, nil
930
948
  end
931
- request_eval :irb, nil
932
949
  end
933
950
 
934
951
  ### Trace
@@ -983,7 +1000,7 @@ module DEBUGGER__
983
1000
  :retry
984
1001
 
985
1002
  when /\Aobject\s+(.+)/
986
- request_tc [:trace, :object, $1.strip, {pattern: pattern, into: into}]
1003
+ request_tc_with_restarted_threads [:trace, :object, $1.strip, {pattern: pattern, into: into}]
987
1004
 
988
1005
  when /\Aoff\s+(\d+)\z/
989
1006
  if t = @tracers.values[$1.to_i]
@@ -1164,11 +1181,6 @@ module DEBUGGER__
1164
1181
  return :retry
1165
1182
  end
1166
1183
 
1167
- def request_eval type, src
1168
- restart_all_threads
1169
- request_tc [:eval, type, src]
1170
- end
1171
-
1172
1184
  def step_command type, arg
1173
1185
  if type == :until
1174
1186
  leave_subsession [:step, type, arg]
@@ -1739,15 +1751,19 @@ module DEBUGGER__
1739
1751
  # check breakpoints
1740
1752
  if file_path
1741
1753
  @bps.find_all do |_key, bp|
1742
- LineBreakpoint === bp && bp.path_is?(file_path)
1754
+ LineBreakpoint === bp && bp.path_is?(file_path) && (iseq.first_lineno..iseq.last_line).cover?(bp.line)
1743
1755
  end.each do |_key, bp|
1744
1756
  if !bp.iseq
1745
1757
  bp.try_activate iseq
1746
1758
  elsif reloaded
1747
1759
  @bps.delete bp.key # to allow duplicate
1748
- if nbp = LineBreakpoint.copy(bp, iseq)
1749
- add_bp nbp
1750
- end
1760
+
1761
+ # When we delete a breakpoint from the @bps hash, we also need to deactivate it or else its tracepoint event
1762
+ # will continue to be enabled and we'll suspend on ghost breakpoints
1763
+ bp.delete
1764
+
1765
+ nbp = LineBreakpoint.copy(bp, iseq)
1766
+ add_bp nbp
1751
1767
  end
1752
1768
  end
1753
1769
  else # !file_path => file_path is not existing
@@ -1993,6 +2009,13 @@ module DEBUGGER__
1993
2009
  def after_fork_parent
1994
2010
  @ui.after_fork_parent
1995
2011
  end
2012
+
2013
+ # experimental API
2014
+ def extend_feature session: nil, thread_client: nil, ui: nil
2015
+ Session.include session if session
2016
+ ThreadClient.include thread_client if thread_client
2017
+ @ui.extend ui if ui
2018
+ end
1996
2019
  end
1997
2020
 
1998
2021
  class ProcessGroup
@@ -2167,6 +2190,7 @@ module DEBUGGER__
2167
2190
  case loc.absolute_path
2168
2191
  when dir_prefix
2169
2192
  when %r{rubygems/core_ext/kernel_require\.rb}
2193
+ when %r{bundled_gems\.rb}
2170
2194
  else
2171
2195
  return loc if loc.absolute_path
2172
2196
  end
@@ -2517,7 +2541,17 @@ module DEBUGGER__
2517
2541
 
2518
2542
  module TrapInterceptor
2519
2543
  def trap sig, *command, &command_proc
2520
- case sig&.to_sym
2544
+ sym =
2545
+ case sig
2546
+ when String
2547
+ sig.to_sym
2548
+ when Integer
2549
+ Signal.signame(sig)&.to_sym
2550
+ else
2551
+ sig
2552
+ end
2553
+
2554
+ case sym
2521
2555
  when :INT, :SIGINT
2522
2556
  if defined?(SESSION) && SESSION.active? && SESSION.intercept_trap_sigint?
2523
2557
  return SESSION.save_int_trap(command.empty? ? command_proc : command.first)
@@ -22,7 +22,7 @@ module DEBUGGER__
22
22
  end
23
23
  end
24
24
 
25
- if RubyVM.respond_to? :keep_script_lines
25
+ if defined?(RubyVM.keep_script_lines)
26
26
  # Ruby 3.1 and later
27
27
  RubyVM.keep_script_lines = true
28
28
  require 'objspace'