debug 1.6.1 → 1.9.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +22 -10
- data/Gemfile +0 -0
- data/LICENSE.txt +0 -0
- data/README.md +84 -55
- data/Rakefile +8 -3
- data/TODO.md +8 -8
- data/debug.gemspec +3 -3
- data/exe/rdbg +19 -4
- data/ext/debug/debug.c +33 -5
- data/ext/debug/extconf.rb +1 -0
- data/ext/debug/iseq_collector.c +2 -0
- data/lib/debug/abbrev_command.rb +77 -0
- data/lib/debug/breakpoint.rb +15 -11
- data/lib/debug/client.rb +26 -8
- data/lib/debug/color.rb +0 -0
- data/lib/debug/config.rb +69 -23
- data/lib/debug/console.rb +8 -29
- data/lib/debug/dap_custom/traceInspector.rb +336 -0
- data/lib/debug/frame_info.rb +9 -0
- data/lib/debug/irb_integration.rb +27 -0
- data/lib/debug/local.rb +16 -10
- data/lib/debug/open.rb +0 -0
- data/lib/debug/open_nonstop.rb +0 -0
- data/lib/debug/prelude.rb +2 -1
- data/lib/debug/server.rb +32 -27
- data/lib/debug/server_cdp.rb +360 -155
- data/lib/debug/server_dap.rb +330 -197
- data/lib/debug/session.rb +494 -258
- data/lib/debug/source_repository.rb +41 -21
- data/lib/debug/start.rb +1 -1
- data/lib/debug/thread_client.rb +241 -82
- data/lib/debug/tracer.rb +4 -5
- data/lib/debug/version.rb +1 -1
- data/lib/debug.rb +7 -2
- data/misc/README.md.erb +50 -44
- metadata +13 -10
data/lib/debug/server_dap.rb
CHANGED
@@ -18,7 +18,7 @@ module DEBUGGER__
|
|
18
18
|
end
|
19
19
|
|
20
20
|
at_exit do
|
21
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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(
|
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
|
-
|
267
|
-
|
298
|
+
process_request(req)
|
299
|
+
end
|
300
|
+
ensure
|
301
|
+
send_event :terminated unless @sock.closed?
|
302
|
+
end
|
268
303
|
|
269
|
-
|
304
|
+
def process_request req
|
305
|
+
raise "not a request: #{req.inspect}" unless req['type'] == 'request'
|
306
|
+
args = req.dig('arguments')
|
270
307
|
|
271
|
-
|
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
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
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
|
-
|
283
|
-
send_response req
|
317
|
+
load_extensions req
|
284
318
|
|
285
|
-
|
286
|
-
|
287
|
-
|
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
|
-
|
296
|
-
|
297
|
-
|
323
|
+
if req.dig('arguments', 'nonstop') == true
|
324
|
+
@nonstop = true
|
325
|
+
else
|
326
|
+
@nonstop = false
|
327
|
+
end
|
298
328
|
|
299
|
-
|
300
|
-
SESSION.clear_line_breakpoints path
|
329
|
+
load_extensions req
|
301
330
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
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
|
-
|
317
|
-
|
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
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
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
|
-
|
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
|
-
|
343
|
-
process_filter.call(
|
386
|
+
filters = args.fetch('filters').map {|filter_id|
|
387
|
+
process_filter.call(filter_id)
|
344
388
|
}
|
345
389
|
|
346
|
-
|
390
|
+
filters += args.fetch('filterOptions', {}).map{|bp_info|
|
391
|
+
process_filter.call(bp_info['filterId'], bp_info['condition'])
|
392
|
+
}
|
347
393
|
|
348
|
-
|
349
|
-
terminate = args.fetch("terminateDebuggee", false)
|
394
|
+
send_response req, breakpoints: filters
|
350
395
|
|
351
|
-
|
352
|
-
|
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
|
-
|
365
|
-
|
399
|
+
SESSION.clear_all_breakpoints
|
400
|
+
send_response req
|
366
401
|
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
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
|
-
|
392
|
-
|
393
|
-
@
|
394
|
-
|
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
|
-
|
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
|
-
|
404
|
-
|
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
|
-
|
407
|
-
when 'reverseContinue'
|
434
|
+
rescue PostmortemError
|
408
435
|
send_response req,
|
409
|
-
success: false, message: '
|
410
|
-
result: "
|
411
|
-
|
412
|
-
|
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
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
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
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
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
|
-
|
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
|
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
|
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 =
|
538
|
-
|
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
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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:
|
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! :
|
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! :
|
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:
|
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! :
|
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
|
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
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
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! :
|
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
|
-
|
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
|
-
|
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! :
|
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! :
|
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
|
-
|
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
|
-
|
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
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
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
|
-
|
1062
|
+
namedVariables += M_INSTANCE_VARIABLES.bind_call(obj).size
|
946
1063
|
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
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 # #
|
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
|