debug 1.6.1 → 1.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,7 +18,7 @@ module DEBUGGER__
18
18
  end
19
19
 
20
20
  at_exit do
21
- CONFIG.skip_all
21
+ DEBUGGER__.skip_all
22
22
  FileUtils.rm_rf dir if tempdir
23
23
  end
24
24
 
@@ -125,10 +125,12 @@ module DEBUGGER__
125
125
  def dap_setup bytes
126
126
  CONFIG.set_config no_color: true
127
127
  @seq = 0
128
+ @send_lock = Mutex.new
128
129
 
129
130
  case self
130
131
  when UI_UnixDomainServer
131
- UI_DAP.local_fs_map_set true
132
+ # If the user specified a mapping, respect it, otherwise, make sure that no mapping is used
133
+ UI_DAP.local_fs_map_set CONFIG[:local_fs_map] || true
132
134
  when UI_TcpServer
133
135
  # TODO: loopback address can be used to connect other FS env, like Docker containers
134
136
  # UI_DAP.local_fs_set if @local_addr.ipv4_loopback? || @local_addr.ipv6_loopback?
@@ -198,15 +200,26 @@ module DEBUGGER__
198
200
  # supportsInstructionBreakpoints:
199
201
  )
200
202
  send_event 'initialized'
203
+ puts <<~WELCOME
204
+ Ruby REPL: You can run any Ruby expression here.
205
+ Note that output to the STDOUT/ERR printed on the TERMINAL.
206
+ [experimental]
207
+ `,COMMAND` runs `COMMAND` debug command (ex: `,info`).
208
+ `,help` to list all debug commands.
209
+ WELCOME
201
210
  end
202
211
 
203
212
  def send **kw
204
213
  if sock = @sock
205
214
  kw[:seq] = @seq += 1
206
215
  str = JSON.dump(kw)
207
- sock.write "Content-Length: #{str.bytesize}\r\n\r\n#{str}"
216
+ @send_lock.synchronize do
217
+ sock.write "Content-Length: #{str.bytesize}\r\n\r\n#{str}"
218
+ end
208
219
  show_protocol '<', str
209
220
  end
221
+ rescue Errno::EPIPE => e
222
+ $stderr.puts "#{e.inspect} rescued during sending message"
210
223
  end
211
224
 
212
225
  def send_response req, success: true, message: nil, **kw
@@ -238,17 +251,17 @@ module DEBUGGER__
238
251
  end
239
252
 
240
253
  def recv_request
241
- r = IO.select([@sock])
254
+ IO.select([@sock])
242
255
 
243
256
  @session.process_group.sync do
244
257
  raise RetryBecauseCantRead unless IO.select([@sock], nil, nil, 0)
245
258
 
246
- case header = @sock.gets
259
+ case @sock.gets
247
260
  when /Content-Length: (\d+)/
248
261
  b = @sock.read(2)
249
262
  raise b.inspect unless b == "\r\n"
250
263
 
251
- l = @sock.read(s = $1.to_i)
264
+ l = @sock.read($1.to_i)
252
265
  show_protocol :>, l
253
266
  JSON.load(l)
254
267
  when nil
@@ -261,178 +274,225 @@ module DEBUGGER__
261
274
  retry
262
275
  end
263
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
+
264
296
  def process
265
297
  while req = recv_request
266
- raise "not a request: #{req.inpsect}" unless req['type'] == 'request'
267
- args = req.dig('arguments')
298
+ process_request(req)
299
+ end
300
+ ensure
301
+ send_event :terminated unless @sock.closed?
302
+ end
268
303
 
269
- case req['command']
304
+ def process_request req
305
+ raise "not a request: #{req.inspect}" unless req['type'] == 'request'
306
+ args = req.dig('arguments')
270
307
 
271
- ## boot/configuration
272
- when 'launch'
273
- send_response req
274
- UI_DAP.local_fs_map_set req.dig('arguments', 'localfs') || req.dig('arguments', 'localfsMap')
275
- @is_launch = true
308
+ case req['command']
276
309
 
277
- when 'attach'
278
- send_response req
279
- UI_DAP.local_fs_map_set req.dig('arguments', 'localfs') || req.dig('arguments', 'localfsMap')
280
- @is_launch = false
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
281
316
 
282
- when 'configurationDone'
283
- send_response req
317
+ load_extensions req
284
318
 
285
- if @is_launch
286
- @q_msg << 'continue'
287
- else
288
- if SESSION.in_subsession?
289
- send_event 'stopped', reason: 'pause',
290
- threadId: 1, # maybe ...
291
- allThreadsStopped: true
292
- end
293
- end
319
+ when 'attach'
320
+ send_response req
321
+ UI_DAP.local_fs_map_set req.dig('arguments', 'localfs') || req.dig('arguments', 'localfsMap')
294
322
 
295
- when 'setBreakpoints'
296
- req_path = args.dig('source', 'path')
297
- path = UI_DAP.local_to_remote_path(req_path)
323
+ if req.dig('arguments', 'nonstop') == true
324
+ @nonstop = true
325
+ else
326
+ @nonstop = false
327
+ end
298
328
 
299
- if path
300
- SESSION.clear_line_breakpoints path
329
+ load_extensions req
301
330
 
302
- bps = []
303
- args['breakpoints'].each{|bp|
304
- line = bp['line']
305
- if cond = bp['condition']
306
- bps << SESSION.add_line_breakpoint(path, line, cond: cond)
307
- else
308
- bps << SESSION.add_line_breakpoint(path, line)
309
- end
310
- }
311
- send_response req, breakpoints: (bps.map do |bp| {verified: true,} end)
312
- else
313
- send_response req, success: false, message: "#{req_path} is not available"
331
+ when 'configurationDone'
332
+ send_response req
333
+
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
314
341
  end
342
+ end
315
343
 
316
- when 'setFunctionBreakpoints'
317
- send_response req
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
318
363
 
319
- when 'setExceptionBreakpoints'
320
- process_filter = ->(filter_id, cond = nil) {
321
- bp =
322
- case filter_id
323
- when 'any'
324
- SESSION.add_catch_breakpoint 'Exception', cond: cond
325
- when 'RuntimeError'
326
- SESSION.add_catch_breakpoint 'RuntimeError', cond: cond
327
- else
328
- nil
329
- end
330
- {
331
- verified: !bp.nil?,
332
- message: bp.inspect,
333
- }
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,
334
381
  }
382
+ }
335
383
 
336
- SESSION.clear_catch_breakpoints 'Exception', 'RuntimeError'
337
-
338
- filters = args.fetch('filters').map {|filter_id|
339
- process_filter.call(filter_id)
340
- }
384
+ SESSION.clear_catch_breakpoints 'Exception', 'RuntimeError'
341
385
 
342
- filters += args.fetch('filterOptions', {}).map{|bp_info|
343
- process_filter.call(bp_info['filterId'], bp_info['condition'])
386
+ filters = args.fetch('filters').map {|filter_id|
387
+ process_filter.call(filter_id)
344
388
  }
345
389
 
346
- send_response req, breakpoints: filters
390
+ filters += args.fetch('filterOptions', {}).map{|bp_info|
391
+ process_filter.call(bp_info['filterId'], bp_info['condition'])
392
+ }
347
393
 
348
- when 'disconnect'
349
- terminate = args.fetch("terminateDebuggee", false)
394
+ send_response req, breakpoints: filters
350
395
 
351
- if SESSION.in_subsession?
352
- if terminate
353
- @q_msg << 'kill!'
354
- else
355
- @q_msg << 'continue'
356
- end
357
- else
358
- if terminate
359
- @q_msg << 'kill!'
360
- pause
361
- end
362
- end
396
+ when 'disconnect'
397
+ terminate = args.fetch("terminateDebuggee", false)
363
398
 
364
- SESSION.clear_all_breakpoints
365
- send_response req
399
+ SESSION.clear_all_breakpoints
400
+ send_response req
366
401
 
367
- ## control
368
- when 'continue'
369
- @q_msg << 'c'
370
- send_response req, allThreadsContinued: true
371
- when 'next'
372
- begin
373
- @session.check_postmortem
374
- @q_msg << 'n'
375
- send_response req
376
- rescue PostmortemError
377
- send_response req,
378
- success: false, message: 'postmortem mode',
379
- result: "'Next' is not supported while postmortem mode"
380
- end
381
- when 'stepIn'
382
- begin
383
- @session.check_postmortem
384
- @q_msg << 's'
385
- send_response req
386
- rescue PostmortemError
387
- send_response req,
388
- success: false, message: 'postmortem mode',
389
- 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'
390
407
  end
391
- when 'stepOut'
392
- begin
393
- @session.check_postmortem
394
- @q_msg << 'fin'
395
- send_response req
396
- rescue PostmortemError
397
- send_response req,
398
- success: false, message: 'postmortem mode',
399
- result: "'stepOut' is not supported while postmortem mode"
408
+ else
409
+ if terminate
410
+ @q_msg << 'kill!'
411
+ pause
400
412
  end
401
- 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'
402
423
  send_response req
403
- exit
404
- 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'
405
433
  send_response req
406
- Process.kill(UI_ServerBase::TRAP_SIGNAL, Process.pid)
407
- when 'reverseContinue'
434
+ rescue PostmortemError
408
435
  send_response req,
409
- success: false, message: 'cancelled',
410
- result: "Reverse Continue is not supported. Only \"Step back\" is supported."
411
- when 'stepBack'
412
- @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
413
461
 
414
- ## query
415
- when 'threads'
416
- send_response req, threads: SESSION.managed_thread_clients.map{|tc|
417
- { id: tc.id,
418
- name: tc.name,
419
- }
462
+ ## query
463
+ when 'threads'
464
+ send_response req, threads: SESSION.managed_thread_clients.map{|tc|
465
+ { id: tc.id,
466
+ name: tc.name,
420
467
  }
468
+ }
469
+
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 }
421
475
 
422
- when 'stackTrace',
423
- 'scopes',
424
- 'variables',
425
- 'evaluate',
426
- 'source',
427
- 'completions'
476
+ send_response req,
477
+ result: "(rdbg:command) #{dbg_expr}",
478
+ variablesReference: 0
479
+ else
428
480
  @q_msg << req
481
+ end
482
+ when 'stackTrace',
483
+ 'scopes',
484
+ 'variables',
485
+ 'source',
486
+ 'completions'
487
+ @q_msg << req
429
488
 
489
+ else
490
+ if respond_to? mid = "custom_dap_request_#{req['command']}"
491
+ __send__ mid, req
430
492
  else
431
493
  raise "Unknown request: #{req.inspect}"
432
494
  end
433
495
  end
434
- ensure
435
- send_event :terminated unless @sock.closed?
436
496
  end
437
497
 
438
498
  ## called by the SESSION thread
@@ -443,7 +503,11 @@ module DEBUGGER__
443
503
 
444
504
  def puts result
445
505
  # STDERR.puts "puts: #{result}"
446
- # send_event 'output', category: 'stderr', output: "PUTS!!: " + result.to_s
506
+ send_event 'output', category: 'console', output: "#{result&.chomp}\n"
507
+ end
508
+
509
+ def ignore_output_on_suspend?
510
+ true
447
511
  end
448
512
 
449
513
  def event type, *args
@@ -488,6 +552,8 @@ module DEBUGGER__
488
552
  end
489
553
 
490
554
  class Session
555
+ include GlobalVariablesHelper
556
+
491
557
  def find_waiting_tc id
492
558
  @th_clients.each{|th, tc|
493
559
  return tc if tc.id == id && tc.waiting?
@@ -512,7 +578,7 @@ module DEBUGGER__
512
578
  when 'stackTrace'
513
579
  tid = req.dig('arguments', 'threadId')
514
580
 
515
- if tc = find_waiting_tc(tid)
581
+ if find_waiting_tc(tid)
516
582
  request_tc [:dap, :backtrace, req]
517
583
  else
518
584
  fail_response req
@@ -521,7 +587,7 @@ module DEBUGGER__
521
587
  frame_id = req.dig('arguments', 'frameId')
522
588
  if @frame_map[frame_id]
523
589
  tid, fid = @frame_map[frame_id]
524
- if tc = find_waiting_tc(tid)
590
+ if find_waiting_tc(tid)
525
591
  request_tc [:dap, :scopes, req, fid]
526
592
  else
527
593
  fail_response req
@@ -534,8 +600,12 @@ module DEBUGGER__
534
600
  if ref = @var_map[varid]
535
601
  case ref[0]
536
602
  when :globals
537
- vars = global_variables.map do |name|
538
- gv = 'Not implemented yet...'
603
+ vars = safe_global_variables.sort.map do |name|
604
+ begin
605
+ gv = eval(name.to_s)
606
+ rescue Exception => e
607
+ gv = e.inspect
608
+ end
539
609
  {
540
610
  name: name,
541
611
  value: gv.inspect,
@@ -553,7 +623,7 @@ module DEBUGGER__
553
623
  frame_id = ref[1]
554
624
  tid, fid = @frame_map[frame_id]
555
625
 
556
- if tc = find_waiting_tc(tid)
626
+ if find_waiting_tc(tid)
557
627
  request_tc [:dap, :scope, req, fid]
558
628
  else
559
629
  fail_response req
@@ -562,7 +632,7 @@ module DEBUGGER__
562
632
  when :variable
563
633
  tid, vid = ref[1], ref[2]
564
634
 
565
- if tc = find_waiting_tc(tid)
635
+ if find_waiting_tc(tid)
566
636
  request_tc [:dap, :variable, req, vid]
567
637
  else
568
638
  fail_response req
@@ -580,7 +650,9 @@ module DEBUGGER__
580
650
  if @frame_map[frame_id]
581
651
  tid, fid = @frame_map[frame_id]
582
652
  expr = req.dig('arguments', 'expression')
583
- if tc = find_waiting_tc(tid)
653
+
654
+ if find_waiting_tc(tid)
655
+ restart_all_threads
584
656
  request_tc [:dap, :evaluate, req, fid, expr, context]
585
657
  else
586
658
  fail_response req
@@ -601,7 +673,7 @@ module DEBUGGER__
601
673
  frame_id = req.dig('arguments', 'frameId')
602
674
  tid, fid = @frame_map[frame_id]
603
675
 
604
- if tc = find_waiting_tc(tid)
676
+ if find_waiting_tc(tid)
605
677
  text = req.dig('arguments', 'text')
606
678
  line = req.dig('arguments', 'line')
607
679
  if col = req.dig('arguments', 'column')
@@ -612,11 +684,15 @@ module DEBUGGER__
612
684
  fail_response req
613
685
  end
614
686
  else
615
- raise "Unknown DAP request: #{req.inspect}"
687
+ if respond_to? mid = "custom_dap_request_#{req['command']}"
688
+ __send__ mid, req
689
+ else
690
+ raise "Unknown request: #{req.inspect}"
691
+ end
616
692
  end
617
693
  end
618
694
 
619
- def dap_event args
695
+ def process_protocol_result args
620
696
  # puts({dap_event: args}.inspect)
621
697
  type, req, result = args
622
698
 
@@ -653,6 +729,7 @@ module DEBUGGER__
653
729
  register_vars result[:variables], tid
654
730
  @ui.respond req, result
655
731
  when :evaluate
732
+ stop_all_threads
656
733
  message = result.delete :message
657
734
  if message
658
735
  @ui.respond req, success: false, message: message
@@ -664,7 +741,11 @@ module DEBUGGER__
664
741
  when :completions
665
742
  @ui.respond req, result
666
743
  else
667
- raise "unsupported: #{args.inspect}"
744
+ if respond_to? mid = "custom_dap_request_event_#{type}"
745
+ __send__ mid, req, result
746
+ else
747
+ raise "unsupported: #{args.inspect}"
748
+ end
668
749
  end
669
750
  end
670
751
 
@@ -684,10 +765,35 @@ module DEBUGGER__
684
765
  end
685
766
  end
686
767
 
768
+ class NaiveString
769
+ attr_reader :str
770
+ def initialize str
771
+ @str = str
772
+ end
773
+ end
774
+
687
775
  class ThreadClient
688
- def value_inspect obj
776
+ MAX_LENGTH = 180
777
+
778
+ def value_inspect obj, short: true
689
779
  # TODO: max length should be configuarable?
690
- DEBUGGER__.safe_inspect obj, short: true, max_length: 4 * 1024
780
+ str = DEBUGGER__.safe_inspect obj, short: short, max_length: MAX_LENGTH
781
+
782
+ if str.encoding == Encoding::UTF_8
783
+ str.scrub
784
+ else
785
+ str.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
786
+ end
787
+ end
788
+
789
+ def dap_eval b, expr, _context, prompt: '(repl_eval)'
790
+ begin
791
+ tp_allow_reentry do
792
+ b.eval(expr.to_s, prompt)
793
+ end
794
+ rescue Exception => e
795
+ e
796
+ end
691
797
  end
692
798
 
693
799
  def process_dap args
@@ -702,9 +808,10 @@ module DEBUGGER__
702
808
  frames = []
703
809
  @target_frames.each_with_index do |frame, i|
704
810
  next if i < start_frame
705
- break if (levels -= 1) < 0
706
811
 
707
812
  path = frame.realpath || frame.path
813
+ next if skip_path?(path) && !SESSION.stop_stepping?(path, frame.location.lineno)
814
+ break if (levels -= 1) < 0
708
815
  source_name = path ? File.basename(path) : frame.location.to_s
709
816
 
710
817
  if (path && File.exist?(path)) && (local_path = UI_DAP.remote_to_local_path(path))
@@ -726,7 +833,7 @@ module DEBUGGER__
726
833
  }
727
834
  end
728
835
 
729
- event! :dap_result, :backtrace, req, {
836
+ event! :protocol_result, :backtrace, req, {
730
837
  stackFrames: frames,
731
838
  totalFrames: @target_frames.size,
732
839
  }
@@ -743,7 +850,7 @@ module DEBUGGER__
743
850
  0
744
851
  end
745
852
 
746
- event! :dap_result, :scopes, req, scopes: [{
853
+ event! :protocol_result, :scopes, req, scopes: [{
747
854
  name: 'Local variables',
748
855
  presentationHint: 'locals',
749
856
  # variablesReference: N, # filled by SESSION
@@ -754,7 +861,7 @@ module DEBUGGER__
754
861
  name: 'Global variables',
755
862
  presentationHint: 'globals',
756
863
  variablesReference: 1, # GLOBAL
757
- namedVariables: global_variables.size,
864
+ namedVariables: safe_global_variables.size,
758
865
  indexedVariables: 0,
759
866
  expensive: false,
760
867
  }]
@@ -765,7 +872,7 @@ module DEBUGGER__
765
872
  variable(var, val)
766
873
  end
767
874
 
768
- event! :dap_result, :scope, req, variables: vars, tid: self.id
875
+ event! :protocol_result, :scope, req, variables: vars, tid: self.id
769
876
  when :variable
770
877
  vid = args.shift
771
878
  obj = @var_map[vid]
@@ -792,13 +899,12 @@ module DEBUGGER__
792
899
  when String
793
900
  vars = [
794
901
  variable('#length', obj.length),
795
- variable('#encoding', obj.encoding)
902
+ variable('#encoding', obj.encoding),
796
903
  ]
904
+ printed_str = value_inspect(obj)
905
+ vars << variable('#dump', NaiveString.new(obj)) if printed_str.end_with?('...')
797
906
  when Class, Module
798
- vars = obj.instance_variables.map{|iv|
799
- variable(iv, obj.instance_variable_get(iv))
800
- }
801
- vars.unshift variable('%ancestors', obj.ancestors[1..])
907
+ vars << variable('%ancestors', obj.ancestors[1..])
802
908
  when Range
803
909
  vars = [
804
910
  variable('#begin', obj.begin),
@@ -806,13 +912,15 @@ module DEBUGGER__
806
912
  ]
807
913
  end
808
914
 
809
- vars += M_INSTANCE_VARIABLES.bind_call(obj).map{|iv|
810
- variable(iv, M_INSTANCE_VARIABLE_GET.bind_call(obj, iv))
811
- }
812
- vars.unshift variable('#class', M_CLASS.bind_call(obj))
915
+ unless NaiveString === obj
916
+ vars += M_INSTANCE_VARIABLES.bind_call(obj).sort.map{|iv|
917
+ variable(iv, M_INSTANCE_VARIABLE_GET.bind_call(obj, iv))
918
+ }
919
+ vars.unshift variable('#class', M_CLASS.bind_call(obj))
920
+ end
813
921
  end
814
922
  end
815
- event! :dap_result, :variable, req, variables: (vars || []), tid: self.id
923
+ event! :protocol_result, :variable, req, variables: (vars || []), tid: self.id
816
924
 
817
925
  when :evaluate
818
926
  fid, expr, context = args
@@ -826,12 +934,7 @@ module DEBUGGER__
826
934
 
827
935
  case context
828
936
  when 'repl', 'watch'
829
- begin
830
- result = b.eval(expr.to_s, '(DEBUG CONSOLE)')
831
- rescue Exception => e
832
- result = e
833
- end
834
-
937
+ result = dap_eval b, expr, context, prompt: '(DEBUG CONSOLE)'
835
938
  when 'hover'
836
939
  case expr
837
940
  when /\A\@\S/
@@ -841,7 +944,7 @@ module DEBUGGER__
841
944
  message = "Error: Not defined instance variable: #{expr.inspect}"
842
945
  end
843
946
  when /\A\$\S/
844
- global_variables.each{|gvar|
947
+ safe_global_variables.each{|gvar|
845
948
  if gvar.to_s == expr
846
949
  result = eval(gvar.to_s)
847
950
  break false
@@ -872,7 +975,7 @@ module DEBUGGER__
872
975
  result = 'Error: Can not evaluate on this frame'
873
976
  end
874
977
 
875
- event! :dap_result, :evaluate, req, message: message, tid: self.id, **evaluate_result(result)
978
+ event! :protocol_result, :evaluate, req, message: message, tid: self.id, **evaluate_result(result)
876
979
 
877
980
  when :completions
878
981
  fid, text = args
@@ -882,7 +985,7 @@ module DEBUGGER__
882
985
  words = IRB::InputCompletor::retrieve_completion_data(word, bind: b).compact
883
986
  end
884
987
 
885
- event! :dap_result, :completions, req, targets: (words || []).map{|phrase|
988
+ event! :protocol_result, :completions, req, targets: (words || []).map{|phrase|
886
989
  detail = nil
887
990
 
888
991
  if /\b([_a-zA-Z]\w*[!\?]?)\z/ =~ phrase
@@ -905,16 +1008,24 @@ module DEBUGGER__
905
1008
  }
906
1009
 
907
1010
  else
908
- raise "Unknown req: #{args.inspect}"
1011
+ if respond_to? mid = "custom_dap_request_#{type}"
1012
+ __send__ mid, req
1013
+ else
1014
+ raise "Unknown request: #{args.inspect}"
1015
+ end
909
1016
  end
910
1017
  end
911
1018
 
912
1019
  def search_const b, expr
913
1020
  cs = expr.delete_prefix('::').split('::')
914
- [Object, *b.eval('Module.nesting')].reverse_each{|mod|
1021
+ [Object, *b.eval('::Module.nesting')].reverse_each{|mod|
915
1022
  if cs.all?{|c|
916
1023
  if mod.const_defined?(c)
917
- mod = mod.const_get(c)
1024
+ begin
1025
+ mod = mod.const_get(c)
1026
+ rescue Exception
1027
+ false
1028
+ end
918
1029
  else
919
1030
  false
920
1031
  end
@@ -927,11 +1038,17 @@ module DEBUGGER__
927
1038
  end
928
1039
 
929
1040
  def evaluate_result r
930
- v = variable nil, r
931
- v.delete :name
932
- v.delete :value
933
- v[:result] = value_inspect(r)
934
- v
1041
+ variable nil, r
1042
+ end
1043
+
1044
+ def type_name obj
1045
+ klass = M_CLASS.bind_call(obj)
1046
+
1047
+ begin
1048
+ M_NAME.bind_call(klass) || klass.to_s
1049
+ rescue Exception => e
1050
+ "<Error: #{e.message} (#{e.backtrace.first}>"
1051
+ end
935
1052
  end
936
1053
 
937
1054
  def variable_ name, obj, indexedVariables: 0, namedVariables: 0
@@ -942,15 +1059,31 @@ module DEBUGGER__
942
1059
  vid = 0
943
1060
  end
944
1061
 
945
- ivnum = M_INSTANCE_VARIABLES.bind_call(obj).size
1062
+ namedVariables += M_INSTANCE_VARIABLES.bind_call(obj).size
946
1063
 
947
- { name: name,
948
- value: value_inspect(obj),
949
- type: (klass = M_CLASS.bind_call(obj)).name || klass.to_s,
950
- variablesReference: vid,
951
- indexedVariables: indexedVariables,
952
- namedVariables: namedVariables + ivnum,
953
- }
1064
+ if NaiveString === obj
1065
+ str = obj.str.dump
1066
+ vid = indexedVariables = namedVariables = 0
1067
+ else
1068
+ str = value_inspect(obj)
1069
+ end
1070
+
1071
+ if name
1072
+ { name: name,
1073
+ value: str,
1074
+ type: type_name(obj),
1075
+ variablesReference: vid,
1076
+ indexedVariables: indexedVariables,
1077
+ namedVariables: namedVariables,
1078
+ }
1079
+ else
1080
+ { result: str,
1081
+ type: type_name(obj),
1082
+ variablesReference: vid,
1083
+ indexedVariables: indexedVariables,
1084
+ namedVariables: namedVariables,
1085
+ }
1086
+ end
954
1087
  end
955
1088
 
956
1089
  def variable name, obj
@@ -960,7 +1093,7 @@ module DEBUGGER__
960
1093
  when Hash
961
1094
  variable_ name, obj, namedVariables: obj.size
962
1095
  when String
963
- variable_ name, obj, namedVariables: 3 # #to_str, #length, #encoding
1096
+ variable_ name, obj, namedVariables: 3 # #length, #encoding, #to_str
964
1097
  when Struct
965
1098
  variable_ name, obj, namedVariables: obj.size
966
1099
  when Class, Module