debug 1.6.3 → 1.7.1
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/CONTRIBUTING.md +19 -7
- data/Gemfile +0 -0
- data/LICENSE.txt +0 -0
- data/README.md +37 -14
- data/Rakefile +0 -0
- data/TODO.md +8 -8
- data/debug.gemspec +1 -1
- data/exe/rdbg +1 -1
- data/ext/debug/debug.c +15 -1
- data/ext/debug/extconf.rb +0 -0
- data/ext/debug/iseq_collector.c +0 -0
- data/lib/debug/abbrev_command.rb +77 -0
- data/lib/debug/breakpoint.rb +17 -11
- data/lib/debug/client.rb +26 -9
- data/lib/debug/color.rb +0 -0
- data/lib/debug/config.rb +34 -16
- data/lib/debug/console.rb +0 -0
- data/lib/debug/frame_info.rb +0 -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 +1 -1
- data/lib/debug/server.rb +25 -21
- data/lib/debug/server_cdp.rb +292 -86
- data/lib/debug/server_dap.rb +132 -47
- data/lib/debug/session.rb +384 -204
- data/lib/debug/source_repository.rb +39 -19
- data/lib/debug/start.rb +1 -1
- data/lib/debug/thread_client.rb +196 -63
- data/lib/debug/tracer.rb +0 -0
- data/lib/debug/version.rb +1 -1
- data/lib/debug.rb +7 -3
- data/misc/README.md.erb +9 -3
- metadata +5 -4
data/lib/debug/server_cdp.rb
CHANGED
@@ -7,13 +7,18 @@ require 'securerandom'
|
|
7
7
|
require 'stringio'
|
8
8
|
require 'open3'
|
9
9
|
require 'tmpdir'
|
10
|
+
require 'tempfile'
|
11
|
+
require 'timeout'
|
10
12
|
|
11
13
|
module DEBUGGER__
|
12
14
|
module UI_CDP
|
13
15
|
SHOW_PROTOCOL = ENV['RUBY_DEBUG_CDP_SHOW_PROTOCOL'] == '1'
|
14
16
|
|
17
|
+
class UnsupportedError < StandardError; end
|
18
|
+
class NotFoundChromeEndpointError < StandardError; end
|
19
|
+
|
15
20
|
class << self
|
16
|
-
def setup_chrome addr
|
21
|
+
def setup_chrome addr, uuid
|
17
22
|
return if CONFIG[:chrome_path] == ''
|
18
23
|
|
19
24
|
port, path, pid = run_new_chrome
|
@@ -29,79 +34,218 @@ module DEBUGGER__
|
|
29
34
|
|
30
35
|
loop do
|
31
36
|
res = ws_client.extract_data
|
32
|
-
case
|
33
|
-
when
|
37
|
+
case res['id']
|
38
|
+
when 1
|
39
|
+
target_info = res.dig('result', 'targetInfos')
|
34
40
|
page = target_info.find{|t| t['type'] == 'page'}
|
35
41
|
ws_client.send id: 2, method: 'Target.attachToTarget',
|
36
42
|
params: {
|
37
43
|
targetId: page['targetId'],
|
38
44
|
flatten: true
|
39
45
|
}
|
40
|
-
when
|
46
|
+
when 2
|
41
47
|
s_id = res.dig('result', 'sessionId')
|
48
|
+
# TODO: change id
|
49
|
+
ws_client.send sessionId: s_id, id: 100, method: 'Network.enable'
|
42
50
|
ws_client.send sessionId: s_id, id: 3,
|
43
51
|
method: 'Page.enable'
|
44
|
-
when
|
52
|
+
when 3
|
45
53
|
s_id = res['sessionId']
|
46
54
|
ws_client.send sessionId: s_id, id: 4,
|
47
55
|
method: 'Page.getFrameTree'
|
48
|
-
when
|
56
|
+
when 4
|
49
57
|
s_id = res['sessionId']
|
50
58
|
f_id = res.dig('result', 'frameTree', 'frame', 'id')
|
51
59
|
ws_client.send sessionId: s_id, id: 5,
|
52
60
|
method: 'Page.navigate',
|
53
61
|
params: {
|
54
|
-
url: "devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=#{addr}/#{
|
62
|
+
url: "devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=#{addr}/#{uuid}",
|
55
63
|
frameId: f_id
|
56
64
|
}
|
57
|
-
when
|
65
|
+
when 101
|
58
66
|
break
|
67
|
+
else
|
68
|
+
if res['method'] == 'Network.webSocketWillSendHandshakeRequest'
|
69
|
+
s_id = res['sessionId']
|
70
|
+
# Display the console by entering ESC key
|
71
|
+
ws_client.send sessionId: s_id, id: 101, # TODO: change id
|
72
|
+
method:"Input.dispatchKeyEvent",
|
73
|
+
params: {
|
74
|
+
type:"keyDown",
|
75
|
+
windowsVirtualKeyCode:27 # ESC key
|
76
|
+
}
|
77
|
+
end
|
59
78
|
end
|
60
79
|
end
|
61
80
|
pid
|
62
|
-
rescue Errno::ENOENT
|
81
|
+
rescue Errno::ENOENT, UnsupportedError, NotFoundChromeEndpointError
|
63
82
|
nil
|
64
83
|
end
|
65
84
|
|
66
|
-
|
67
|
-
|
85
|
+
TIMEOUT_SEC = 5
|
86
|
+
|
87
|
+
def run_new_chrome
|
88
|
+
path = CONFIG[:chrome_path]
|
89
|
+
|
90
|
+
data = nil
|
91
|
+
port = nil
|
92
|
+
wait_thr = nil
|
68
93
|
|
69
94
|
# The process to check OS is based on `selenium` project.
|
70
95
|
case RbConfig::CONFIG['host_os']
|
71
96
|
when /mswin|msys|mingw|cygwin|emc/
|
72
|
-
|
97
|
+
if path.nil?
|
98
|
+
candidates = ['C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe', 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe']
|
99
|
+
path = get_chrome_path candidates
|
100
|
+
end
|
101
|
+
uuid = SecureRandom.uuid
|
102
|
+
# The path is based on https://github.com/sindresorhus/open/blob/v8.4.0/index.js#L128.
|
103
|
+
stdin, stdout, stderr, wait_thr = *Open3.popen3("#{ENV['SystemRoot']}\\System32\\WindowsPowerShell\\v1.0\\powershell")
|
104
|
+
tf = Tempfile.create(['debug-', '.txt'])
|
105
|
+
|
106
|
+
stdin.puts("Start-process '#{path}' -Argumentlist '--remote-debugging-port=0', '--no-first-run', '--no-default-browser-check', '--user-data-dir=C:\\temp' -Wait -RedirectStandardError #{tf.path}")
|
107
|
+
stdin.close
|
108
|
+
stdout.close
|
109
|
+
stderr.close
|
110
|
+
port, path = get_devtools_endpoint(tf.path)
|
111
|
+
|
112
|
+
at_exit{
|
113
|
+
DEBUGGER__.skip_all
|
114
|
+
|
115
|
+
stdin, stdout, stderr, wait_thr = *Open3.popen3("#{ENV['SystemRoot']}\\System32\\WindowsPowerShell\\v1.0\\powershell")
|
116
|
+
stdin.puts("Stop-process -Name chrome")
|
117
|
+
stdin.close
|
118
|
+
stdout.close
|
119
|
+
stderr.close
|
120
|
+
tf.close
|
121
|
+
begin
|
122
|
+
File.unlink(tf)
|
123
|
+
rescue Errno::EACCES
|
124
|
+
end
|
125
|
+
}
|
73
126
|
when /darwin|mac os/
|
74
|
-
'/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome'
|
127
|
+
path = path || '/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome'
|
128
|
+
dir = Dir.mktmpdir
|
129
|
+
# The command line flags are based on: https://developer.mozilla.org/en-US/docs/Tools/Remote_Debugging/Chrome_Desktop#connecting.
|
130
|
+
stdin, stdout, stderr, wait_thr = *Open3.popen3("#{path} --remote-debugging-port=0 --no-first-run --no-default-browser-check --user-data-dir=#{dir}")
|
131
|
+
stdin.close
|
132
|
+
stdout.close
|
133
|
+
data = stderr.readpartial 4096
|
134
|
+
stderr.close
|
135
|
+
if data.match /DevTools listening on ws:\/\/127.0.0.1:(\d+)(.*)/
|
136
|
+
port = $1
|
137
|
+
path = $2
|
138
|
+
end
|
139
|
+
|
140
|
+
at_exit{
|
141
|
+
DEBUGGER__.skip_all
|
142
|
+
FileUtils.rm_rf dir
|
143
|
+
}
|
75
144
|
when /linux/
|
76
|
-
'google-chrome'
|
145
|
+
path = path || 'google-chrome'
|
146
|
+
dir = Dir.mktmpdir
|
147
|
+
# The command line flags are based on: https://developer.mozilla.org/en-US/docs/Tools/Remote_Debugging/Chrome_Desktop#connecting.
|
148
|
+
stdin, stdout, stderr, wait_thr = *Open3.popen3("#{path} --remote-debugging-port=0 --no-first-run --no-default-browser-check --user-data-dir=#{dir}")
|
149
|
+
stdin.close
|
150
|
+
stdout.close
|
151
|
+
data = ''
|
152
|
+
begin
|
153
|
+
Timeout.timeout(TIMEOUT_SEC) do
|
154
|
+
until data.match?(/DevTools listening on ws:\/\/127.0.0.1:\d+.*/)
|
155
|
+
data = stderr.readpartial 4096
|
156
|
+
end
|
157
|
+
end
|
158
|
+
rescue Exception
|
159
|
+
raise NotFoundChromeEndpointError
|
160
|
+
end
|
161
|
+
stderr.close
|
162
|
+
if data.match /DevTools listening on ws:\/\/127.0.0.1:(\d+)(.*)/
|
163
|
+
port = $1
|
164
|
+
path = $2
|
165
|
+
end
|
166
|
+
|
167
|
+
at_exit{
|
168
|
+
DEBUGGER__.skip_all
|
169
|
+
FileUtils.rm_rf dir
|
170
|
+
}
|
77
171
|
else
|
78
|
-
raise
|
172
|
+
raise UnsupportedError
|
79
173
|
end
|
174
|
+
|
175
|
+
[port, path, wait_thr.pid]
|
80
176
|
end
|
81
177
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
178
|
+
def get_chrome_path candidates
|
179
|
+
candidates.each{|c|
|
180
|
+
if File.exist? c
|
181
|
+
return c
|
182
|
+
end
|
183
|
+
}
|
184
|
+
raise UnsupportedError
|
185
|
+
end
|
186
|
+
|
187
|
+
ITERATIONS = 50
|
188
|
+
|
189
|
+
def get_devtools_endpoint tf
|
190
|
+
i = 1
|
191
|
+
while i < ITERATIONS
|
192
|
+
i += 1
|
193
|
+
if File.exist?(tf) && data = File.read(tf)
|
194
|
+
if data.match /DevTools listening on ws:\/\/127.0.0.1:(\d+)(.*)/
|
195
|
+
port = $1
|
196
|
+
path = $2
|
197
|
+
return [port, path]
|
198
|
+
end
|
199
|
+
end
|
200
|
+
sleep 0.1
|
93
201
|
end
|
94
|
-
|
202
|
+
raise NotFoundChromeEndpointError
|
203
|
+
end
|
204
|
+
end
|
95
205
|
|
96
|
-
|
97
|
-
|
98
|
-
|
206
|
+
def send_chrome_response req
|
207
|
+
@repl = false
|
208
|
+
case req
|
209
|
+
when /^GET\s\/json\/version\sHTTP\/1.1/
|
210
|
+
body = {
|
211
|
+
Browser: "ruby/v#{RUBY_VERSION}",
|
212
|
+
'Protocol-Version': "1.1"
|
99
213
|
}
|
100
|
-
|
101
|
-
|
214
|
+
send_http_res body
|
215
|
+
raise UI_ServerBase::RetryConnection
|
216
|
+
|
217
|
+
when /^GET\s\/json\sHTTP\/1.1/
|
218
|
+
@uuid = @uuid || SecureRandom.uuid
|
219
|
+
addr = @local_addr.inspect_sockaddr
|
220
|
+
body = [{
|
221
|
+
description: "ruby instance",
|
222
|
+
devtoolsFrontendUrl: "devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=#{addr}/#{@uuid}",
|
223
|
+
id: @uuid,
|
224
|
+
title: $0,
|
225
|
+
type: "node",
|
226
|
+
url: "file://#{File.absolute_path($0)}",
|
227
|
+
webSocketDebuggerUrl: "ws://#{addr}/#{@uuid}"
|
228
|
+
}]
|
229
|
+
send_http_res body
|
230
|
+
raise UI_ServerBase::RetryConnection
|
231
|
+
|
232
|
+
when /^GET\s\/(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})\sHTTP\/1.1/
|
233
|
+
raise 'Incorrect uuid' unless $1 == @uuid
|
234
|
+
|
235
|
+
@need_pause_at_first = false
|
236
|
+
CONFIG.set_config no_color: true
|
237
|
+
|
238
|
+
@ws_server = WebSocketServer.new(@sock)
|
239
|
+
@ws_server.handshake
|
102
240
|
end
|
103
241
|
end
|
104
242
|
|
243
|
+
def send_http_res body
|
244
|
+
json = JSON.generate body
|
245
|
+
header = "HTTP/1.0 200 OK\r\nContent-Type: application/json; charset=UTF-8\r\nCache-Control: no-cache\r\nContent-Length: #{json.bytesize}\r\n\r\n"
|
246
|
+
@sock.puts "#{header}#{json}"
|
247
|
+
end
|
248
|
+
|
105
249
|
module WebSocketUtils
|
106
250
|
class Frame
|
107
251
|
attr_reader :b
|
@@ -528,12 +672,6 @@ module DEBUGGER__
|
|
528
672
|
|
529
673
|
## Called by the SESSION thread
|
530
674
|
|
531
|
-
def readline prompt
|
532
|
-
return 'c' unless @q_msg
|
533
|
-
|
534
|
-
@q_msg.pop || 'kill!'
|
535
|
-
end
|
536
|
-
|
537
675
|
def respond req, **result
|
538
676
|
send_response req, **result
|
539
677
|
end
|
@@ -561,6 +699,30 @@ module DEBUGGER__
|
|
561
699
|
end
|
562
700
|
|
563
701
|
class Session
|
702
|
+
include GlobalVariablesHelper
|
703
|
+
|
704
|
+
# FIXME: unify this method with ThreadClient#propertyDescriptor.
|
705
|
+
def get_type obj
|
706
|
+
case obj
|
707
|
+
when Array
|
708
|
+
['object', 'array']
|
709
|
+
when Hash
|
710
|
+
['object', 'map']
|
711
|
+
when String
|
712
|
+
['string']
|
713
|
+
when TrueClass, FalseClass
|
714
|
+
['boolean']
|
715
|
+
when Symbol
|
716
|
+
['symbol']
|
717
|
+
when Integer, Float
|
718
|
+
['number']
|
719
|
+
when Exception
|
720
|
+
['object', 'error']
|
721
|
+
else
|
722
|
+
['object']
|
723
|
+
end
|
724
|
+
end
|
725
|
+
|
564
726
|
def fail_response req, **result
|
565
727
|
@ui.respond_fail req, **result
|
566
728
|
return :retry
|
@@ -584,17 +746,38 @@ module DEBUGGER__
|
|
584
746
|
code: INVALID_PARAMS,
|
585
747
|
message: "'callFrameId' is an invalid"
|
586
748
|
end
|
587
|
-
when 'Runtime.getProperties'
|
588
|
-
oid = req.dig('params', 'objectId')
|
749
|
+
when 'Runtime.getProperties', 'Runtime.getExceptionDetails'
|
750
|
+
oid = req.dig('params', 'objectId') || req.dig('params', 'errorObjectId')
|
589
751
|
if ref = @obj_map[oid]
|
590
752
|
case ref[0]
|
591
753
|
when 'local'
|
592
754
|
frame_id = ref[1]
|
593
755
|
fid = @frame_map[frame_id]
|
594
756
|
request_tc [:cdp, :scope, req, fid]
|
757
|
+
when 'global'
|
758
|
+
vars = safe_global_variables.sort.map do |name|
|
759
|
+
gv = eval(name.to_s)
|
760
|
+
prop = {
|
761
|
+
name: name,
|
762
|
+
value: {
|
763
|
+
description: gv.inspect
|
764
|
+
},
|
765
|
+
configurable: true,
|
766
|
+
enumerable: true
|
767
|
+
}
|
768
|
+
type, subtype = get_type(gv)
|
769
|
+
prop[:value][:type] = type
|
770
|
+
prop[:value][:subtype] = subtype if subtype
|
771
|
+
prop
|
772
|
+
end
|
773
|
+
|
774
|
+
@ui.respond req, result: vars
|
775
|
+
return :retry
|
595
776
|
when 'properties'
|
596
777
|
request_tc [:cdp, :properties, req, oid]
|
597
|
-
when '
|
778
|
+
when 'exception'
|
779
|
+
request_tc [:cdp, :exception, req, oid]
|
780
|
+
when 'script'
|
598
781
|
# TODO: Support script and global types
|
599
782
|
@ui.respond req, result: []
|
600
783
|
return :retry
|
@@ -730,6 +913,9 @@ module DEBUGGER__
|
|
730
913
|
frame[:scriptId] = s_id
|
731
914
|
end
|
732
915
|
}
|
916
|
+
if oid = exc[:exception][:objectId]
|
917
|
+
@obj_map[oid] = ['exception']
|
918
|
+
end
|
733
919
|
end
|
734
920
|
rs = result.dig(:response, :result)
|
735
921
|
[rs].each{|obj|
|
@@ -767,6 +953,8 @@ module DEBUGGER__
|
|
767
953
|
}
|
768
954
|
}
|
769
955
|
@ui.respond req, **result
|
956
|
+
when :exception
|
957
|
+
@ui.respond req, **result
|
770
958
|
end
|
771
959
|
end
|
772
960
|
end
|
@@ -857,7 +1045,7 @@ module DEBUGGER__
|
|
857
1045
|
case expr
|
858
1046
|
# Chrome doesn't read instance variables
|
859
1047
|
when /\A\$\S/
|
860
|
-
|
1048
|
+
safe_global_variables.each{|gvar|
|
861
1049
|
if gvar.to_s == expr
|
862
1050
|
result = eval(gvar.to_s)
|
863
1051
|
break false
|
@@ -872,8 +1060,8 @@ module DEBUGGER__
|
|
872
1060
|
result = b.local_variable_get(expr)
|
873
1061
|
rescue NameError
|
874
1062
|
# try to check method
|
875
|
-
if b.receiver
|
876
|
-
result = b.receiver
|
1063
|
+
if M_RESPOND_TO_P.bind_call(b.receiver, expr, include_all: true)
|
1064
|
+
result = M_METHOD.bind_call(b.receiver, expr)
|
877
1065
|
else
|
878
1066
|
message = "Error: Can not evaluate: #{expr.inspect}"
|
879
1067
|
end
|
@@ -886,35 +1074,7 @@ module DEBUGGER__
|
|
886
1074
|
result = current_frame.binding.eval(expr.to_s, '(DEBUG CONSOLE)')
|
887
1075
|
rescue Exception => e
|
888
1076
|
result = e
|
889
|
-
|
890
|
-
frames = [
|
891
|
-
{
|
892
|
-
columnNumber: 0,
|
893
|
-
functionName: 'eval',
|
894
|
-
lineNumber: 0,
|
895
|
-
url: ''
|
896
|
-
}
|
897
|
-
]
|
898
|
-
e.backtrace_locations&.each do |loc|
|
899
|
-
break if loc.path == __FILE__
|
900
|
-
path = loc.absolute_path || loc.path
|
901
|
-
frames << {
|
902
|
-
columnNumber: 0,
|
903
|
-
functionName: loc.base_label,
|
904
|
-
lineNumber: loc.lineno - 1,
|
905
|
-
url: path
|
906
|
-
}
|
907
|
-
end
|
908
|
-
res[:exceptionDetails] = {
|
909
|
-
exceptionId: 1,
|
910
|
-
text: 'Uncaught',
|
911
|
-
lineNumber: 0,
|
912
|
-
columnNumber: 0,
|
913
|
-
exception: evaluate_result(result),
|
914
|
-
stackTrace: {
|
915
|
-
callFrames: frames
|
916
|
-
}
|
917
|
-
}
|
1077
|
+
res[:exceptionDetails] = exceptionDetails(e, 'Uncaught')
|
918
1078
|
ensure
|
919
1079
|
output = $stdout.string
|
920
1080
|
$stdout = orig_stdout
|
@@ -987,13 +1147,51 @@ module DEBUGGER__
|
|
987
1147
|
]
|
988
1148
|
end
|
989
1149
|
|
990
|
-
result += obj.
|
991
|
-
variable(iv,
|
1150
|
+
result += M_INSTANCE_VARIABLES.bind_call(obj).map{|iv|
|
1151
|
+
variable(iv, M_INSTANCE_VARIABLE_GET.bind_call(obj, iv))
|
992
1152
|
}
|
993
|
-
prop += [internalProperty('#class', obj
|
1153
|
+
prop += [internalProperty('#class', M_CLASS.bind_call(obj))]
|
994
1154
|
end
|
995
1155
|
event! :cdp_result, :properties, req, result: result, internalProperties: prop
|
1156
|
+
when :exception
|
1157
|
+
oid = args.shift
|
1158
|
+
exc = nil
|
1159
|
+
if obj = @obj_map[oid]
|
1160
|
+
exc = exceptionDetails obj, obj.to_s
|
1161
|
+
end
|
1162
|
+
event! :cdp_result, :exception, req, exceptionDetails: exc
|
1163
|
+
end
|
1164
|
+
end
|
1165
|
+
|
1166
|
+
def exceptionDetails exc, text
|
1167
|
+
frames = [
|
1168
|
+
{
|
1169
|
+
columnNumber: 0,
|
1170
|
+
functionName: 'eval',
|
1171
|
+
lineNumber: 0,
|
1172
|
+
url: ''
|
1173
|
+
}
|
1174
|
+
]
|
1175
|
+
exc.backtrace_locations&.each do |loc|
|
1176
|
+
break if loc.path == __FILE__
|
1177
|
+
path = loc.absolute_path || loc.path
|
1178
|
+
frames << {
|
1179
|
+
columnNumber: 0,
|
1180
|
+
functionName: loc.base_label,
|
1181
|
+
lineNumber: loc.lineno - 1,
|
1182
|
+
url: path
|
1183
|
+
}
|
996
1184
|
end
|
1185
|
+
{
|
1186
|
+
exceptionId: 1,
|
1187
|
+
text: text,
|
1188
|
+
lineNumber: 0,
|
1189
|
+
columnNumber: 0,
|
1190
|
+
exception: evaluate_result(exc),
|
1191
|
+
stackTrace: {
|
1192
|
+
callFrames: frames
|
1193
|
+
}
|
1194
|
+
}
|
997
1195
|
end
|
998
1196
|
|
999
1197
|
def search_const b, expr
|
@@ -1001,7 +1199,11 @@ module DEBUGGER__
|
|
1001
1199
|
[Object, *b.eval('::Module.nesting')].reverse_each{|mod|
|
1002
1200
|
if cs.all?{|c|
|
1003
1201
|
if mod.const_defined?(c)
|
1004
|
-
|
1202
|
+
begin
|
1203
|
+
mod = mod.const_get(c)
|
1204
|
+
rescue Exception
|
1205
|
+
false
|
1206
|
+
end
|
1005
1207
|
else
|
1006
1208
|
false
|
1007
1209
|
end
|
@@ -1045,25 +1247,29 @@ module DEBUGGER__
|
|
1045
1247
|
v = prop[:value]
|
1046
1248
|
v.delete :value
|
1047
1249
|
v[:subtype] = subtype if subtype
|
1048
|
-
v[:className] = obj.
|
1250
|
+
v[:className] = (klass = M_CLASS.bind_call(obj)).name || klass.to_s
|
1049
1251
|
end
|
1050
1252
|
prop
|
1051
1253
|
end
|
1052
1254
|
|
1053
1255
|
def preview_ value, hash, overflow
|
1256
|
+
# The reason for not using "map" method is to prevent the object overriding it from causing bugs.
|
1257
|
+
# https://github.com/ruby/debug/issues/781
|
1258
|
+
props = []
|
1259
|
+
hash.each{|k, v|
|
1260
|
+
pd = propertyDescriptor k, v
|
1261
|
+
props << {
|
1262
|
+
name: pd[:name],
|
1263
|
+
type: pd[:value][:type],
|
1264
|
+
value: pd[:value][:description]
|
1265
|
+
}
|
1266
|
+
}
|
1054
1267
|
{
|
1055
1268
|
type: value[:type],
|
1056
1269
|
subtype: value[:subtype],
|
1057
1270
|
description: value[:description],
|
1058
1271
|
overflow: overflow,
|
1059
|
-
properties:
|
1060
|
-
pd = propertyDescriptor k, v
|
1061
|
-
{
|
1062
|
-
name: pd[:name],
|
1063
|
-
type: pd[:value][:type],
|
1064
|
-
value: pd[:value][:description]
|
1065
|
-
}
|
1066
|
-
}
|
1272
|
+
properties: props
|
1067
1273
|
}
|
1068
1274
|
end
|
1069
1275
|
|