debug 1.0.0.beta4 → 1.0.0.beta5
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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +34 -0
- data/.gitignore +1 -0
- data/CONTRIBUTING.md +48 -0
- data/Gemfile +1 -1
- data/README.md +72 -37
- data/debug.gemspec +2 -0
- data/exe/rdbg +8 -1
- data/ext/debug/debug.c +9 -8
- data/lib/debug/breakpoint.rb +71 -24
- data/lib/debug/client.rb +49 -6
- data/lib/debug/color.rb +70 -0
- data/lib/debug/config.rb +39 -5
- data/lib/debug/console.rb +8 -1
- data/lib/debug/frame_info.rb +61 -30
- data/lib/debug/open.rb +2 -0
- data/lib/debug/run.rb +2 -0
- data/lib/debug/server.rb +72 -19
- data/lib/debug/server_dap.rb +605 -0
- data/lib/debug/session.rb +181 -86
- data/lib/debug/source_repository.rb +53 -33
- data/lib/debug/test_console.rb +0 -0
- data/lib/debug/thread_client.rb +91 -23
- data/lib/debug/version.rb +1 -1
- data/misc/README.md.erb +51 -28
- metadata +21 -3
@@ -0,0 +1,605 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module DEBUGGER__
|
4
|
+
module UI_DAP
|
5
|
+
SHOW_PROTOCOL = ENV['RUBY_DEBUG_DAP_SHOW_PROTOCOL'] == '1'
|
6
|
+
|
7
|
+
def dap_setup bytes
|
8
|
+
DEBUGGER__.set_config(use_colorize: false)
|
9
|
+
@seq = 0
|
10
|
+
|
11
|
+
$stderr.puts '[>]' + bytes if SHOW_PROTOCOL
|
12
|
+
req = JSON.load(bytes)
|
13
|
+
|
14
|
+
# capability
|
15
|
+
send_response(req,
|
16
|
+
## Supported
|
17
|
+
supportsConfigurationDoneRequest: true,
|
18
|
+
supportsFunctionBreakpoints: true,
|
19
|
+
supportsConditionalBreakpoints: true,
|
20
|
+
supportTerminateDebuggee: true,
|
21
|
+
supportsTerminateRequest: true,
|
22
|
+
exceptionBreakpointFilters: [
|
23
|
+
{
|
24
|
+
filter: 'any',
|
25
|
+
label: 'rescue any exception',
|
26
|
+
#supportsCondition: true,
|
27
|
+
#conditionDescription: '',
|
28
|
+
},
|
29
|
+
{
|
30
|
+
filter: 'RuntimeError',
|
31
|
+
label: 'rescue RuntimeError',
|
32
|
+
default: true,
|
33
|
+
#supportsCondition: true,
|
34
|
+
#conditionDescription: '',
|
35
|
+
},
|
36
|
+
],
|
37
|
+
supportsExceptionFilterOptions: true,
|
38
|
+
|
39
|
+
## Will be supported
|
40
|
+
# supportsExceptionOptions: true,
|
41
|
+
# supportsHitConditionalBreakpoints:
|
42
|
+
# supportsEvaluateForHovers:
|
43
|
+
# supportsSetVariable: true,
|
44
|
+
# supportSuspendDebuggee:
|
45
|
+
# supportsLogPoints:
|
46
|
+
# supportsLoadedSourcesRequest:
|
47
|
+
# supportsDataBreakpoints:
|
48
|
+
# supportsBreakpointLocationsRequest:
|
49
|
+
|
50
|
+
## Possible?
|
51
|
+
# supportsStepBack:
|
52
|
+
# supportsRestartFrame:
|
53
|
+
# supportsCompletionsRequest:
|
54
|
+
# completionTriggerCharacters:
|
55
|
+
# supportsModulesRequest:
|
56
|
+
# additionalModuleColumns:
|
57
|
+
# supportedChecksumAlgorithms:
|
58
|
+
# supportsRestartRequest:
|
59
|
+
# supportsValueFormattingOptions:
|
60
|
+
# supportsExceptionInfoRequest:
|
61
|
+
# supportsDelayedStackTraceLoading:
|
62
|
+
# supportsTerminateThreadsRequest:
|
63
|
+
# supportsSetExpression:
|
64
|
+
# supportsClipboardContext:
|
65
|
+
|
66
|
+
## Never
|
67
|
+
# supportsGotoTargetsRequest:
|
68
|
+
# supportsStepInTargetsRequest:
|
69
|
+
# supportsReadMemoryRequest:
|
70
|
+
# supportsDisassembleRequest:
|
71
|
+
# supportsCancelRequest:
|
72
|
+
# supportsSteppingGranularity:
|
73
|
+
# supportsInstructionBreakpoints:
|
74
|
+
)
|
75
|
+
send_event 'initialized'
|
76
|
+
end
|
77
|
+
|
78
|
+
def send **kw
|
79
|
+
kw[:seq] = @seq += 1
|
80
|
+
str = JSON.dump(kw)
|
81
|
+
$stderr.puts "[<] #{str}" if SHOW_PROTOCOL
|
82
|
+
# STDERR.puts "[STDERR] [<] #{str}"
|
83
|
+
@sock.print header = "Content-Length: #{str.size}\r\n\r\n"
|
84
|
+
@sock.write str
|
85
|
+
end
|
86
|
+
|
87
|
+
def send_response req, success: true, **kw
|
88
|
+
if kw.empty?
|
89
|
+
send type: 'response',
|
90
|
+
command: req['command'],
|
91
|
+
request_seq: req['seq'],
|
92
|
+
success: success,
|
93
|
+
message: success ? 'Success' : 'Failed'
|
94
|
+
else
|
95
|
+
send type: 'response',
|
96
|
+
command: req['command'],
|
97
|
+
request_seq: req['seq'],
|
98
|
+
success: success,
|
99
|
+
message: success ? 'Success' : 'Failed',
|
100
|
+
body: kw
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def send_event name, **kw
|
105
|
+
if kw.empty?
|
106
|
+
send type: 'event', event: name
|
107
|
+
else
|
108
|
+
send type: 'event', event: name, body: kw
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def recv_request
|
113
|
+
case header = @sock.gets
|
114
|
+
when /Content-Length: (\d+)/
|
115
|
+
b = @sock.read(2)
|
116
|
+
raise b.inspect unless b == "\r\n"
|
117
|
+
|
118
|
+
l = @sock.read(s = $1.to_i)
|
119
|
+
$stderr.puts "[>] #{l}" if SHOW_PROTOCOL
|
120
|
+
JSON.load(l)
|
121
|
+
when nil
|
122
|
+
nil
|
123
|
+
else
|
124
|
+
raise "unrecognized line: #{l} (#{l.size} bytes)"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def process
|
129
|
+
while req = recv_request
|
130
|
+
raise "not a request: #{req.inpsect}" unless req['type'] == 'request'
|
131
|
+
args = req.dig('arguments')
|
132
|
+
|
133
|
+
case req['command']
|
134
|
+
|
135
|
+
## boot/configuration
|
136
|
+
when 'launch'
|
137
|
+
send_response req
|
138
|
+
when 'setBreakpoints'
|
139
|
+
path = args.dig('source', 'path')
|
140
|
+
bp_args = args['breakpoints']
|
141
|
+
bps = []
|
142
|
+
bp_args.each{|bp|
|
143
|
+
line = bp['line']
|
144
|
+
if cond = bp['condition']
|
145
|
+
bps << SESSION.add_line_breakpoint(path, line, cond: cond)
|
146
|
+
else
|
147
|
+
bps << SESSION.add_line_breakpoint(path, line)
|
148
|
+
end
|
149
|
+
}
|
150
|
+
send_response req, breakpoints: (bps.map do |bp| {verified: true,} end)
|
151
|
+
when 'setFunctionBreakpoints'
|
152
|
+
send_response req
|
153
|
+
when 'setExceptionBreakpoints'
|
154
|
+
filters = args.dig('filterOptions').map{|bp_info|
|
155
|
+
case bp_info.dig('filterId')
|
156
|
+
when 'any'
|
157
|
+
bp = SESSION.add_catch_breakpoint 'Exception'
|
158
|
+
when 'RuntimeError'
|
159
|
+
bp = SESSION.add_catch_breakpoint 'RuntimeError'
|
160
|
+
else
|
161
|
+
bp = nil
|
162
|
+
end
|
163
|
+
{
|
164
|
+
verifiled: bp ? true : false,
|
165
|
+
message: bp.inspect,
|
166
|
+
}
|
167
|
+
}
|
168
|
+
send_response req, breakpoints: filters
|
169
|
+
when 'configurationDone'
|
170
|
+
send_response req
|
171
|
+
@q_msg << 'continue'
|
172
|
+
when 'attach'
|
173
|
+
send_response req
|
174
|
+
Process.kill(:SIGINT, Process.pid)
|
175
|
+
when 'disconnect'
|
176
|
+
send_response req
|
177
|
+
@q_msg << 'continue'
|
178
|
+
|
179
|
+
## control
|
180
|
+
when 'continue'
|
181
|
+
@q_msg << 'c'
|
182
|
+
send_response req, allThreadsContinued: true
|
183
|
+
when 'next'
|
184
|
+
@q_msg << 'n'
|
185
|
+
send_response req
|
186
|
+
when 'stepIn'
|
187
|
+
@q_msg << 's'
|
188
|
+
send_response req
|
189
|
+
when 'stepOut'
|
190
|
+
@q_msg << 'fin'
|
191
|
+
send_response req
|
192
|
+
when 'terminate'
|
193
|
+
send_response req
|
194
|
+
exit
|
195
|
+
when 'pause'
|
196
|
+
send_response req
|
197
|
+
Process.kill(:SIGINT, Process.pid)
|
198
|
+
|
199
|
+
## query
|
200
|
+
when 'threads'
|
201
|
+
send_response req, threads: SESSION.managed_thread_clients.map{|tc|
|
202
|
+
{ id: tc.id,
|
203
|
+
name: tc.name,
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
when 'stackTrace',
|
208
|
+
'scopes',
|
209
|
+
'variables',
|
210
|
+
'evaluate',
|
211
|
+
'source'
|
212
|
+
@q_msg << req
|
213
|
+
else
|
214
|
+
raise "Unknown request: #{req.inspect}"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
## called by the SESSION thread
|
220
|
+
|
221
|
+
def readline
|
222
|
+
@q_msg.pop || 'kill!'
|
223
|
+
end
|
224
|
+
|
225
|
+
def sock skip: false
|
226
|
+
yield $stderr
|
227
|
+
end
|
228
|
+
|
229
|
+
def respond req, res
|
230
|
+
send_response(req, **res)
|
231
|
+
end
|
232
|
+
|
233
|
+
def puts result
|
234
|
+
# STDERR.puts "puts: #{result}"
|
235
|
+
# send_event 'output', category: 'stderr', output: "PUTS!!: " + result.to_s
|
236
|
+
end
|
237
|
+
|
238
|
+
def event type, *args
|
239
|
+
case type
|
240
|
+
when :suspend_bp
|
241
|
+
_i, bp = *args
|
242
|
+
if bp.kind_of?(CatchBreakpoint)
|
243
|
+
reason = 'exception'
|
244
|
+
text = bp.description
|
245
|
+
else
|
246
|
+
reason = 'breakpoint'
|
247
|
+
text = bp ? bp.description : 'temporary bp'
|
248
|
+
end
|
249
|
+
|
250
|
+
send_event 'stopped', reason: reason,
|
251
|
+
description: text,
|
252
|
+
text: text,
|
253
|
+
threadId: 1,
|
254
|
+
allThreadsStopped: true
|
255
|
+
when :suspend_trap
|
256
|
+
send_event 'stopped', reason: 'pause',
|
257
|
+
threadId: 1,
|
258
|
+
allThreadsStopped: true
|
259
|
+
when :suspended
|
260
|
+
send_event 'stopped', reason: 'step',
|
261
|
+
threadId: 1,
|
262
|
+
allThreadsStopped: true
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
class Session
|
268
|
+
def find_tc id
|
269
|
+
@th_clients.each{|th, tc|
|
270
|
+
return tc if tc.id == id
|
271
|
+
}
|
272
|
+
return nil
|
273
|
+
end
|
274
|
+
|
275
|
+
def fail_response req, **kw
|
276
|
+
@ui.respond req, success: false, **kw
|
277
|
+
return :retry
|
278
|
+
end
|
279
|
+
|
280
|
+
def process_dap_request req
|
281
|
+
case req['command']
|
282
|
+
when 'stackTrace'
|
283
|
+
tid = req.dig('arguments', 'threadId')
|
284
|
+
if tc = find_tc(tid)
|
285
|
+
tc << [:dap, :backtrace, req]
|
286
|
+
else
|
287
|
+
fail_response req
|
288
|
+
end
|
289
|
+
when 'scopes'
|
290
|
+
frame_id = req.dig('arguments', 'frameId')
|
291
|
+
if @frame_map[frame_id]
|
292
|
+
tid, fid = @frame_map[frame_id]
|
293
|
+
if tc = find_tc(tid)
|
294
|
+
tc << [:dap, :scopes, req, fid]
|
295
|
+
else
|
296
|
+
fail_response req
|
297
|
+
end
|
298
|
+
else
|
299
|
+
fail_response req
|
300
|
+
end
|
301
|
+
when 'variables'
|
302
|
+
varid = req.dig('arguments', 'variablesReference')
|
303
|
+
if ref = @var_map[varid]
|
304
|
+
case ref[0]
|
305
|
+
when :globals
|
306
|
+
vars = global_variables.map do |name|
|
307
|
+
File.write('/tmp/x', "#{name}\n")
|
308
|
+
gv = 'Not implemented yet...'
|
309
|
+
{
|
310
|
+
name: name,
|
311
|
+
value: gv.inspect,
|
312
|
+
type: (gv.class.name || gv.class.to_s),
|
313
|
+
variablesReference: 0,
|
314
|
+
}
|
315
|
+
end
|
316
|
+
|
317
|
+
@ui.respond req, {
|
318
|
+
variables: vars,
|
319
|
+
}
|
320
|
+
return :retry
|
321
|
+
|
322
|
+
when :scope
|
323
|
+
frame_id = ref[1]
|
324
|
+
tid, fid = @frame_map[frame_id]
|
325
|
+
|
326
|
+
if tc = find_tc(tid)
|
327
|
+
tc << [:dap, :scope, req, fid]
|
328
|
+
else
|
329
|
+
fail_response req
|
330
|
+
end
|
331
|
+
|
332
|
+
when :variable
|
333
|
+
tid, vid = ref[1], ref[2]
|
334
|
+
|
335
|
+
if tc = find_tc(tid)
|
336
|
+
tc << [:dap, :variable, req, vid]
|
337
|
+
else
|
338
|
+
fail_response req
|
339
|
+
end
|
340
|
+
else
|
341
|
+
raise "Uknown type: #{ref.inspect}"
|
342
|
+
end
|
343
|
+
else
|
344
|
+
fail_response req
|
345
|
+
end
|
346
|
+
when 'evaluate'
|
347
|
+
frame_id = req.dig('arguments', 'frameId')
|
348
|
+
if @frame_map[frame_id]
|
349
|
+
tid, fid = @frame_map[frame_id]
|
350
|
+
expr = req.dig('arguments', 'expression')
|
351
|
+
if tc = find_tc(tid)
|
352
|
+
tc << [:dap, :evaluate, req, fid, expr]
|
353
|
+
else
|
354
|
+
fail_response req
|
355
|
+
end
|
356
|
+
else
|
357
|
+
fail_response req, result: "can't evaluate"
|
358
|
+
end
|
359
|
+
when 'source'
|
360
|
+
ref = req.dig('arguments', 'sourceReference')
|
361
|
+
if src = @src_map[ref]
|
362
|
+
@ui.respond req, content: src.join
|
363
|
+
else
|
364
|
+
fail_response req, message: 'not found...'
|
365
|
+
end
|
366
|
+
|
367
|
+
return :retry
|
368
|
+
else
|
369
|
+
raise "Unknown DAP request: #{req.inspect}"
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
def dap_event args
|
374
|
+
# puts({dap_event: args}.inspect)
|
375
|
+
type, req, result = args
|
376
|
+
|
377
|
+
case type
|
378
|
+
when :backtrace
|
379
|
+
result[:stackFrames].each.with_index{|fi, i|
|
380
|
+
fi[:id] = id = @frame_map.size + 1
|
381
|
+
@frame_map[id] = [req.dig('arguments', 'threadId'), i]
|
382
|
+
if fi[:source] && src = fi[:source][:sourceReference]
|
383
|
+
src_id = @src_map.size + 1
|
384
|
+
@src_map[src_id] = src
|
385
|
+
fi[:source][:sourceReference] = src_id
|
386
|
+
end
|
387
|
+
}
|
388
|
+
@ui.respond req, result
|
389
|
+
when :scopes
|
390
|
+
frame_id = req.dig('arguments', 'frameId')
|
391
|
+
local_scope = result[:scopes].first
|
392
|
+
local_scope[:variablesReference] = id = @var_map.size + 1
|
393
|
+
|
394
|
+
@var_map[id] = [:scope, frame_id]
|
395
|
+
@ui.respond req, result
|
396
|
+
when :scope
|
397
|
+
tid = result.delete :tid
|
398
|
+
register_vars result[:variables], tid
|
399
|
+
@ui.respond req, result
|
400
|
+
when :variable
|
401
|
+
tid = result.delete :tid
|
402
|
+
register_vars result[:variables], tid
|
403
|
+
@ui.respond req, result
|
404
|
+
when :evaluate
|
405
|
+
tid = result.delete :tid
|
406
|
+
register_var result, tid
|
407
|
+
@ui.respond req, result
|
408
|
+
else
|
409
|
+
raise "unsupported: #{args.inspect}"
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
def register_var v, tid
|
414
|
+
if (tl_vid = v[:variablesReference]) > 0
|
415
|
+
vid = @var_map.size + 1
|
416
|
+
@var_map[vid] = [:variable, tid, tl_vid]
|
417
|
+
v[:variablesReference] = vid
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
def register_vars vars, tid
|
422
|
+
raise tid.inspect unless tid.kind_of?(Integer)
|
423
|
+
vars.each{|v|
|
424
|
+
register_var v, tid
|
425
|
+
}
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
class ThreadClient
|
430
|
+
def process_dap args
|
431
|
+
# pp tc: self, args: args
|
432
|
+
type = args.shift
|
433
|
+
req = args.shift
|
434
|
+
|
435
|
+
case type
|
436
|
+
when :backtrace
|
437
|
+
event! :dap_result, :backtrace, req, {
|
438
|
+
stackFrames: @target_frames.map.with_index{|frame, i|
|
439
|
+
path = frame.path
|
440
|
+
ref = frame.file_lines unless File.exist?(path)
|
441
|
+
|
442
|
+
{
|
443
|
+
# id: ??? # filled by SESSION
|
444
|
+
name: frame.name,
|
445
|
+
line: frame.location.lineno,
|
446
|
+
column: 1,
|
447
|
+
source: {
|
448
|
+
name: File.basename(frame.path),
|
449
|
+
path: path,
|
450
|
+
sourceReference: ref,
|
451
|
+
},
|
452
|
+
}
|
453
|
+
}
|
454
|
+
}
|
455
|
+
when :scopes
|
456
|
+
fid = args.shift
|
457
|
+
frame = @target_frames[fid]
|
458
|
+
lnum = frame.binding ? frame.binding.local_variables.size : 0
|
459
|
+
|
460
|
+
event! :dap_result, :scopes, req, scopes: [{
|
461
|
+
name: 'Local variables',
|
462
|
+
presentationHint: 'locals',
|
463
|
+
# variablesReference: N, # filled by SESSION
|
464
|
+
namedVariables: lnum,
|
465
|
+
indexedVariables: 0,
|
466
|
+
expensive: false,
|
467
|
+
}, {
|
468
|
+
name: 'Global variables',
|
469
|
+
presentationHint: 'globals',
|
470
|
+
variablesReference: 1, # GLOBAL
|
471
|
+
namedVariables: global_variables.size,
|
472
|
+
indexedVariables: 0,
|
473
|
+
expensive: false,
|
474
|
+
}]
|
475
|
+
when :scope
|
476
|
+
fid = args.shift
|
477
|
+
frame = @target_frames[fid]
|
478
|
+
if b = frame.binding
|
479
|
+
vars = b.local_variables.map{|name|
|
480
|
+
v = b.local_variable_get(name)
|
481
|
+
variable(name, v)
|
482
|
+
}
|
483
|
+
vars.unshift variable('%raised', frame.raised_exception) if frame.has_raised_exception
|
484
|
+
vars.unshift variable('%return', frame.return_value) if frame.has_return_value
|
485
|
+
vars.unshift variable('%self', b.receiver)
|
486
|
+
else
|
487
|
+
vars = [variable('%self', frame.self)]
|
488
|
+
vars.push variable('%raised', frame.raised_exception) if frame.has_raised_exception
|
489
|
+
vars.push variable('%return', frame.return_value) if frame.has_return_value
|
490
|
+
end
|
491
|
+
event! :dap_result, :scope, req, variables: vars, tid: self.id
|
492
|
+
|
493
|
+
when :variable
|
494
|
+
vid = args.shift
|
495
|
+
obj = @var_map[vid]
|
496
|
+
if obj
|
497
|
+
case req.dig('arguments', 'filter')
|
498
|
+
when 'indexed'
|
499
|
+
start = req.dig('arguments', 'start') || 0
|
500
|
+
count = req.dig('arguments', 'count') || obj.size
|
501
|
+
vars = (start ... (start + count)).map{|i|
|
502
|
+
variable(i.to_s, obj[i])
|
503
|
+
}
|
504
|
+
else
|
505
|
+
vars = []
|
506
|
+
|
507
|
+
case obj
|
508
|
+
when Hash
|
509
|
+
vars = obj.map{|k, v|
|
510
|
+
variable(DEBUGGER__.short_inspect(k), v)
|
511
|
+
}
|
512
|
+
when Struct
|
513
|
+
vars = obj.members.map{|m|
|
514
|
+
variable(m, obj[m])
|
515
|
+
}
|
516
|
+
when String
|
517
|
+
vars = [
|
518
|
+
variable('#length', obj.length),
|
519
|
+
variable('#encoding', obj.encoding)
|
520
|
+
]
|
521
|
+
when Class, Module
|
522
|
+
vars = obj.instance_variables.map{|iv|
|
523
|
+
variable(iv, obj.instance_variable_get(iv))
|
524
|
+
}
|
525
|
+
vars.unshift variable('%ancestors', obj.ancestors[1..])
|
526
|
+
when Range
|
527
|
+
vars = [
|
528
|
+
variable('#begin', obj.begin),
|
529
|
+
variable('#end', obj.end),
|
530
|
+
]
|
531
|
+
end
|
532
|
+
|
533
|
+
vars += obj.instance_variables.map{|iv|
|
534
|
+
variable(iv, obj.instance_variable_get(iv))
|
535
|
+
}
|
536
|
+
vars.unshift variable('#class', obj.class)
|
537
|
+
end
|
538
|
+
end
|
539
|
+
event! :dap_result, :variable, req, variables: (vars || []), tid: self.id
|
540
|
+
|
541
|
+
when :evaluate
|
542
|
+
fid, expr = args
|
543
|
+
frame = @target_frames[fid]
|
544
|
+
|
545
|
+
if frame && (b = frame.binding)
|
546
|
+
begin
|
547
|
+
result = b.eval(expr.to_s, '(DEBUG CONSOLE)')
|
548
|
+
rescue Exception => e
|
549
|
+
result = e
|
550
|
+
end
|
551
|
+
else
|
552
|
+
result = 'can not evaluate on this frame...'
|
553
|
+
end
|
554
|
+
event! :dap_result, :evaluate, req, tid: self.id, **evaluate_result(result)
|
555
|
+
else
|
556
|
+
raise "Unkown req: #{args.inspect}"
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
def evaluate_result r
|
561
|
+
v = variable nil, r
|
562
|
+
v.delete(:name)
|
563
|
+
v[:result] = DEBUGGER__.short_inspect(r)
|
564
|
+
v
|
565
|
+
end
|
566
|
+
|
567
|
+
def variable_ name, obj, indexedVariables: 0, namedVariables: 0, use_short: true
|
568
|
+
if indexedVariables > 0 || namedVariables > 0
|
569
|
+
vid = @var_map.size + 1
|
570
|
+
@var_map[vid] = obj
|
571
|
+
else
|
572
|
+
vid = 0
|
573
|
+
end
|
574
|
+
|
575
|
+
ivnum = obj.instance_variables.size
|
576
|
+
|
577
|
+
{ name: name,
|
578
|
+
value: DEBUGGER__.short_inspect(obj, use_short),
|
579
|
+
type: obj.class.name || obj.class.to_s,
|
580
|
+
variablesReference: vid,
|
581
|
+
indexedVariables: indexedVariables,
|
582
|
+
namedVariables: namedVariables + ivnum,
|
583
|
+
}
|
584
|
+
end
|
585
|
+
|
586
|
+
def variable name, obj
|
587
|
+
case obj
|
588
|
+
when Array
|
589
|
+
variable_ name, obj, indexedVariables: obj.size
|
590
|
+
when Hash
|
591
|
+
variable_ name, obj, namedVariables: obj.size
|
592
|
+
when String
|
593
|
+
variable_ name, obj, use_short: false, namedVariables: 3 # #to_str, #length, #encoding
|
594
|
+
when Struct
|
595
|
+
variable_ name, obj, namedVariables: obj.size
|
596
|
+
when Class, Module
|
597
|
+
variable_ name, obj, namedVariables: 1 # %ancestors (#ancestors without self)
|
598
|
+
when Range
|
599
|
+
variable_ name, obj, namedVariables: 2 # #begin, #end
|
600
|
+
else
|
601
|
+
variable_ name, obj, namedVariables: 1 # #class
|
602
|
+
end
|
603
|
+
end
|
604
|
+
end
|
605
|
+
end
|