ruby-debug-ide22 0.7.4
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 +7 -0
- data/CHANGES +75 -0
- data/ChangeLog.archive +1073 -0
- data/ChangeLog.md +594 -0
- data/Gemfile +38 -0
- data/MIT-LICENSE +24 -0
- data/Rakefile +93 -0
- data/bin/gdb_wrapper +96 -0
- data/bin/rdebug-ide +200 -0
- data/ext/mkrf_conf.rb +44 -0
- data/lib/ruby-debug-ide/attach/debugger_loader.rb +20 -0
- data/lib/ruby-debug-ide/attach/gdb.rb +73 -0
- data/lib/ruby-debug-ide/attach/lldb.rb +71 -0
- data/lib/ruby-debug-ide/attach/native_debugger.rb +133 -0
- data/lib/ruby-debug-ide/attach/process_thread.rb +54 -0
- data/lib/ruby-debug-ide/attach/util.rb +115 -0
- data/lib/ruby-debug-ide/command.rb +187 -0
- data/lib/ruby-debug-ide/commands/breakpoints.rb +128 -0
- data/lib/ruby-debug-ide/commands/catchpoint.rb +64 -0
- data/lib/ruby-debug-ide/commands/condition.rb +51 -0
- data/lib/ruby-debug-ide/commands/control.rb +158 -0
- data/lib/ruby-debug-ide/commands/enable.rb +203 -0
- data/lib/ruby-debug-ide/commands/eval.rb +64 -0
- data/lib/ruby-debug-ide/commands/expression_info.rb +71 -0
- data/lib/ruby-debug-ide/commands/file_filtering.rb +107 -0
- data/lib/ruby-debug-ide/commands/frame.rb +155 -0
- data/lib/ruby-debug-ide/commands/inspect.rb +25 -0
- data/lib/ruby-debug-ide/commands/jump.rb +73 -0
- data/lib/ruby-debug-ide/commands/load.rb +18 -0
- data/lib/ruby-debug-ide/commands/pause.rb +33 -0
- data/lib/ruby-debug-ide/commands/set_type.rb +47 -0
- data/lib/ruby-debug-ide/commands/stepping.rb +108 -0
- data/lib/ruby-debug-ide/commands/threads.rb +178 -0
- data/lib/ruby-debug-ide/commands/variables.rb +154 -0
- data/lib/ruby-debug-ide/event_processor.rb +71 -0
- data/lib/ruby-debug-ide/greeter.rb +42 -0
- data/lib/ruby-debug-ide/helper.rb +33 -0
- data/lib/ruby-debug-ide/ide_processor.rb +155 -0
- data/lib/ruby-debug-ide/interface.rb +45 -0
- data/lib/ruby-debug-ide/multiprocess/monkey.rb +47 -0
- data/lib/ruby-debug-ide/multiprocess/pre_child.rb +59 -0
- data/lib/ruby-debug-ide/multiprocess/starter.rb +11 -0
- data/lib/ruby-debug-ide/multiprocess/unmonkey.rb +31 -0
- data/lib/ruby-debug-ide/multiprocess.rb +23 -0
- data/lib/ruby-debug-ide/thread_alias.rb +27 -0
- data/lib/ruby-debug-ide/version.rb +3 -0
- data/lib/ruby-debug-ide/xml_printer.rb +571 -0
- data/lib/ruby-debug-ide.rb +228 -0
- data/ruby-debug-ide.gemspec +47 -0
- metadata +110 -0
@@ -0,0 +1,571 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'cgi'
|
3
|
+
require 'monitor'
|
4
|
+
|
5
|
+
module Debugger
|
6
|
+
|
7
|
+
module OverflowMessageType
|
8
|
+
NIL_MESSAGE = lambda {|e| nil}
|
9
|
+
EXCEPTION_MESSAGE = lambda {|e| e.message}
|
10
|
+
SPECIAL_SYMBOL_MESSAGE = lambda {|e| '<?>'}
|
11
|
+
end
|
12
|
+
|
13
|
+
class ExecError
|
14
|
+
attr_reader :message
|
15
|
+
attr_reader :backtrace
|
16
|
+
|
17
|
+
def initialize(message, backtrace = [])
|
18
|
+
@message = message
|
19
|
+
@backtrace = backtrace
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class SimpleTimeLimitError < StandardError
|
24
|
+
attr_reader :message
|
25
|
+
|
26
|
+
def initialize(message)
|
27
|
+
@message = message
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class MemoryLimitError < ExecError;
|
32
|
+
end
|
33
|
+
|
34
|
+
class TimeLimitError < ExecError;
|
35
|
+
end
|
36
|
+
|
37
|
+
class XmlPrinter # :nodoc:
|
38
|
+
class ExceptionProxy
|
39
|
+
instance_methods.each {|m| undef_method m unless m =~ /(^__|^send$|^object_id$|^instance_variables$|^instance_eval$)/}
|
40
|
+
|
41
|
+
def initialize(exception)
|
42
|
+
@exception = exception
|
43
|
+
@message = exception.message
|
44
|
+
@backtrace = Debugger.cleanup_backtrace(exception.backtrace)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def method_missing(called, *args, &block)
|
49
|
+
@exception.__send__(called, *args, &block)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.protect(mname)
|
54
|
+
return if instance_methods.include?("__#{mname}")
|
55
|
+
alias_method "__#{mname}", mname
|
56
|
+
class_eval %{
|
57
|
+
def #{mname}(*args, &block)
|
58
|
+
@@monitor.synchronize do
|
59
|
+
return unless @interface
|
60
|
+
__#{mname}(*args, &block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
@@monitor = Monitor.new
|
67
|
+
attr_accessor :interface
|
68
|
+
|
69
|
+
def initialize(interface)
|
70
|
+
@interface = interface
|
71
|
+
end
|
72
|
+
|
73
|
+
def print_msg(*args)
|
74
|
+
msg, *args = args
|
75
|
+
xml_message = CGI.escapeHTML(msg % args)
|
76
|
+
print "<message>#{xml_message}</message>"
|
77
|
+
end
|
78
|
+
|
79
|
+
# Sends debug message to the frontend if XML debug logging flag (--xml-debug) is on.
|
80
|
+
def print_debug(*args)
|
81
|
+
Debugger.print_debug(*args)
|
82
|
+
if Debugger.xml_debug
|
83
|
+
msg, *args = args
|
84
|
+
xml_message = CGI.escapeHTML(msg % args)
|
85
|
+
@interface.print("<message debug='true'>#{xml_message}</message>")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def print_error(*args)
|
90
|
+
print_element("error") do
|
91
|
+
msg, *args = args
|
92
|
+
print CGI.escapeHTML(msg % args)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def print_frames(context, current_frame_id)
|
97
|
+
print_element("frames") do
|
98
|
+
(0...context.stack_size).each do |id|
|
99
|
+
print_frame(context, id, current_frame_id)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def print_current_frame(frame_pos)
|
105
|
+
print_debug "Selected frame no #{frame_pos}"
|
106
|
+
end
|
107
|
+
|
108
|
+
def print_frame(context, frame_id, current_frame_id)
|
109
|
+
# idx + 1: one-based numbering as classic-debugger
|
110
|
+
file = context.frame_file(frame_id)
|
111
|
+
print "<frame no=\"%s\" file=\"%s\" line=\"%s\" #{"current='true' " if frame_id == current_frame_id}/>",
|
112
|
+
frame_id + 1, CGI.escapeHTML(File.expand_path(file)), context.frame_line(frame_id)
|
113
|
+
end
|
114
|
+
|
115
|
+
def print_contexts(contexts)
|
116
|
+
print_element("threads") do
|
117
|
+
contexts.each do |c|
|
118
|
+
print_context(c) unless c.ignored?
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def print_context(context)
|
124
|
+
print "<thread id=\"%s\" status=\"%s\" pid=\"%s\" #{current_thread_attr(context)}/>", context.thnum, context.thread.status, Process.pid
|
125
|
+
end
|
126
|
+
|
127
|
+
def print_variables(vars, kind)
|
128
|
+
print_element("variables") do
|
129
|
+
# print self at top position
|
130
|
+
print_variable('self', yield('self'), kind) if vars.include?('self')
|
131
|
+
vars.sort.each do |v|
|
132
|
+
print_variable(v, yield(v), kind) unless v == 'self'
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def print_array(array)
|
138
|
+
print_element("variables") do
|
139
|
+
index = 0
|
140
|
+
array.each {|e|
|
141
|
+
print_variable('[' + index.to_s + ']', e, 'instance')
|
142
|
+
index += 1
|
143
|
+
}
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def do_print_hash_key_value(hash)
|
148
|
+
print_element("variables", {:type => 'hashItem'}) do
|
149
|
+
hash.each {|(k, v)|
|
150
|
+
print_variable('key', k, 'instance')
|
151
|
+
print_variable('value', v, 'instance')
|
152
|
+
}
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def do_print_hash(hash)
|
157
|
+
print_element("variables") do
|
158
|
+
hash.each {|(k, v)|
|
159
|
+
if k.class.name == "String"
|
160
|
+
name = '\'' + k + '\''
|
161
|
+
else
|
162
|
+
name = exec_with_allocation_control(k, :to_s, OverflowMessageType::EXCEPTION_MESSAGE)
|
163
|
+
end
|
164
|
+
|
165
|
+
if k.nil?
|
166
|
+
name = 'nil'
|
167
|
+
end
|
168
|
+
|
169
|
+
print_variable(name, v, 'instance')
|
170
|
+
}
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def print_hash(hash)
|
175
|
+
if Debugger.key_value_mode
|
176
|
+
do_print_hash_key_value(hash)
|
177
|
+
else
|
178
|
+
do_print_hash(hash)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def print_string(string)
|
183
|
+
print_element("variables") do
|
184
|
+
if string.respond_to?('bytes')
|
185
|
+
bytes = string.bytes.to_a
|
186
|
+
InspectCommand.reference_result(bytes)
|
187
|
+
print_variable('bytes', bytes, 'instance')
|
188
|
+
end
|
189
|
+
print_variable('encoding', string.encoding, 'instance') if string.respond_to?('encoding')
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def exec_with_timeout(sec, error_message)
|
194
|
+
return yield if sec == nil or sec.zero?
|
195
|
+
if Thread.respond_to?(:critical) and Thread.critical
|
196
|
+
raise ThreadError, "timeout within critical session"
|
197
|
+
end
|
198
|
+
begin
|
199
|
+
x = Thread.current
|
200
|
+
y = DebugThread.start {
|
201
|
+
sleep sec
|
202
|
+
x.raise SimpleTimeLimitError.new(error_message) if x.alive?
|
203
|
+
}
|
204
|
+
yield sec
|
205
|
+
ensure
|
206
|
+
y.kill if y and y.alive?
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def exec_with_allocation_control(value, exec_method, overflow_message_type)
|
211
|
+
return value.__send__ exec_method unless Debugger.trace_to_s
|
212
|
+
|
213
|
+
memory_limit = Debugger.debugger_memory_limit
|
214
|
+
time_limit = Debugger.inspect_time_limit
|
215
|
+
|
216
|
+
if defined?(JRUBY_VERSION) || RUBY_VERSION < '2.0' || memory_limit <= 0
|
217
|
+
return exec_with_timeout(time_limit * 1e-3, "Timeout: evaluation of #{exec_method} took longer than #{time_limit}ms.") { value.__send__ exec_method }
|
218
|
+
end
|
219
|
+
|
220
|
+
require 'objspace'
|
221
|
+
trace_queue = Queue.new
|
222
|
+
|
223
|
+
inspect_thread = DebugThread.start do
|
224
|
+
start_alloc_size = ObjectSpace.memsize_of_all
|
225
|
+
start_time = Time.now.to_f
|
226
|
+
|
227
|
+
trace_point = TracePoint.new(:c_call, :call) do |tp|
|
228
|
+
curr_time = Time.now.to_f
|
229
|
+
|
230
|
+
if (curr_time - start_time) * 1e3 > time_limit
|
231
|
+
trace_queue << TimeLimitError.new("Timeout: evaluation of #{exec_method} took longer than #{time_limit}ms.", caller.to_a)
|
232
|
+
trace_point.disable
|
233
|
+
inspect_thread.kill
|
234
|
+
end
|
235
|
+
|
236
|
+
next unless rand > 0.75
|
237
|
+
|
238
|
+
curr_alloc_size = ObjectSpace.memsize_of_all
|
239
|
+
start_alloc_size = curr_alloc_size if curr_alloc_size < start_alloc_size
|
240
|
+
|
241
|
+
if curr_alloc_size - start_alloc_size > 1e6 * memory_limit
|
242
|
+
trace_queue << MemoryLimitError.new("Out of memory: evaluation of #{exec_method} took more than #{memory_limit}mb.", caller.to_a)
|
243
|
+
trace_point.disable
|
244
|
+
inspect_thread.kill
|
245
|
+
end
|
246
|
+
end
|
247
|
+
trace_point.enable
|
248
|
+
result = value.__send__ exec_method
|
249
|
+
trace_queue << result
|
250
|
+
trace_point.disable
|
251
|
+
end
|
252
|
+
|
253
|
+
while(mes = trace_queue.pop)
|
254
|
+
if(mes.is_a? TimeLimitError or mes.is_a? MemoryLimitError)
|
255
|
+
print_debug(mes.message + "\n" + mes.backtrace.map {|l| "\t#{l}"}.join("\n"))
|
256
|
+
return overflow_message_type.call(mes)
|
257
|
+
else
|
258
|
+
return mes
|
259
|
+
end
|
260
|
+
end
|
261
|
+
rescue SimpleTimeLimitError => e
|
262
|
+
print_debug(e.message)
|
263
|
+
return overflow_message_type.call(e)
|
264
|
+
end
|
265
|
+
|
266
|
+
def print_variable(name, value, kind)
|
267
|
+
name = name.to_s
|
268
|
+
|
269
|
+
if value.nil?
|
270
|
+
print("<variable name=\"%s\" kind=\"%s\"/>", CGI.escapeHTML(name), kind)
|
271
|
+
return
|
272
|
+
end
|
273
|
+
if value.is_a?(Array) || value.is_a?(Hash)
|
274
|
+
has_children = !value.empty?
|
275
|
+
if has_children
|
276
|
+
size = value.size
|
277
|
+
value_str = "#{value.class} (#{value.size} element#{size > 1 ? "s" : "" })"
|
278
|
+
else
|
279
|
+
value_str = "Empty #{value.class}"
|
280
|
+
end
|
281
|
+
elsif value.is_a?(String)
|
282
|
+
has_children = value.respond_to?('bytes') || value.respond_to?('encoding')
|
283
|
+
value_str = value
|
284
|
+
else
|
285
|
+
has_children = !value.instance_variables.empty? || !value.class.class_variables.empty?
|
286
|
+
|
287
|
+
value_str = exec_with_allocation_control(value, :to_s, OverflowMessageType::EXCEPTION_MESSAGE) || 'nil' rescue "<#to_s method raised exception: #{$!}>"
|
288
|
+
unless value_str.is_a?(String)
|
289
|
+
value_str = "ERROR: #{value.class}.to_s method returns #{value_str.class}. Should return String."
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
if value_str.respond_to?('encode')
|
294
|
+
# noinspection RubyEmptyRescueBlockInspection
|
295
|
+
begin
|
296
|
+
value_str = value_str.encode("UTF-8")
|
297
|
+
rescue
|
298
|
+
end
|
299
|
+
end
|
300
|
+
value_str = handle_binary_data(value_str)
|
301
|
+
escaped_value_str = CGI.escapeHTML(value_str)
|
302
|
+
print("<variable name=\"%s\" %s kind=\"%s\" %s type=\"%s\" hasChildren=\"%s\" objectId=\"%#+x\">",
|
303
|
+
CGI.escapeHTML(name), build_compact_value_attr(value, value_str), kind,
|
304
|
+
build_value_attr(escaped_value_str), value.class,
|
305
|
+
has_children, value.object_id)
|
306
|
+
|
307
|
+
print("<value><![CDATA[%s]]></value>", escaped_value_str) if Debugger.value_as_nested_element
|
308
|
+
print('</variable>')
|
309
|
+
rescue StandardError => e
|
310
|
+
print_debug "Unexpected exception \"%s\"\n%s", e.to_s, e.backtrace.join("\n")
|
311
|
+
print("<variable name=\"%s\" kind=\"%s\" value=\"%s\"/>",
|
312
|
+
CGI.escapeHTML(name), kind, CGI.escapeHTML(safe_to_string(value)))
|
313
|
+
end
|
314
|
+
|
315
|
+
def print_file_included(file)
|
316
|
+
print("<fileIncluded file=\"%s\"/>", file)
|
317
|
+
end
|
318
|
+
|
319
|
+
def print_file_excluded(file)
|
320
|
+
print("<fileExcluded file=\"%s\"/>", file)
|
321
|
+
end
|
322
|
+
|
323
|
+
def print_file_filter_status(status)
|
324
|
+
print("<fileFilter status=\"%s\"/>", status)
|
325
|
+
end
|
326
|
+
|
327
|
+
def print_breakpoints(breakpoints)
|
328
|
+
print_element 'breakpoints' do
|
329
|
+
breakpoints.sort_by {|b| b.id}.each do |b|
|
330
|
+
print "<breakpoint n=\"%d\" file=\"%s\" line=\"%s\" />", b.id, CGI.escapeHTML(b.source), b.pos.to_s
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def print_breakpoint_added(b)
|
336
|
+
print "<breakpointAdded no=\"%s\" location=\"%s:%s\"/>", b.id, CGI.escapeHTML(b.source), b.pos
|
337
|
+
end
|
338
|
+
|
339
|
+
def print_breakpoint_deleted(b)
|
340
|
+
print "<breakpointDeleted no=\"%s\"/>", b.id
|
341
|
+
end
|
342
|
+
|
343
|
+
def print_breakpoint_enabled(b)
|
344
|
+
print "<breakpointEnabled bp_id=\"%s\"/>", b.id
|
345
|
+
end
|
346
|
+
|
347
|
+
def print_breakpoint_disabled(b)
|
348
|
+
print "<breakpointDisabled bp_id=\"%s\"/>", b.id
|
349
|
+
end
|
350
|
+
|
351
|
+
def print_contdition_set(bp_id)
|
352
|
+
print "<conditionSet bp_id=\"%d\"/>", bp_id
|
353
|
+
end
|
354
|
+
|
355
|
+
def print_catchpoint_set(exception_class_name)
|
356
|
+
print "<catchpointSet exception=\"%s\"/>", exception_class_name
|
357
|
+
end
|
358
|
+
|
359
|
+
def print_catchpoint_deleted(exception_class_name)
|
360
|
+
if Debugger.catchpoint_deleted_event
|
361
|
+
print "<catchpointDeleted exception=\"%s\"/>", exception_class_name
|
362
|
+
else
|
363
|
+
print_catchpoint_set(exception_class_name)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
def print_expressions(exps)
|
368
|
+
print_element "expressions" do
|
369
|
+
exps.each_with_index do |(exp, value), idx|
|
370
|
+
print_expression(exp, value, idx + 1)
|
371
|
+
end
|
372
|
+
end unless exps.empty?
|
373
|
+
end
|
374
|
+
|
375
|
+
def print_expression(exp, value, idx)
|
376
|
+
print "<dispay name=\"%s\" value=\"%s\" no=\"%d\" />", exp, value, idx
|
377
|
+
end
|
378
|
+
|
379
|
+
def print_expression_info(incomplete, prompt, indent)
|
380
|
+
print "<expressionInfo incomplete=\"%s\" prompt=\"%s\" indent=\"%s\"></expressionInfo>",
|
381
|
+
incomplete, CGI.escapeHTML(prompt), indent
|
382
|
+
end
|
383
|
+
|
384
|
+
def print_eval(exp, value)
|
385
|
+
print "<eval expression=\"%s\" value=\"%s\" />", CGI.escapeHTML(exp), value
|
386
|
+
end
|
387
|
+
|
388
|
+
def print_pp(value)
|
389
|
+
print value
|
390
|
+
end
|
391
|
+
|
392
|
+
def print_list(b, e, file, line)
|
393
|
+
print "[%d, %d] in %s\n", b, e, file
|
394
|
+
if (lines = Debugger.source_for(file))
|
395
|
+
b.upto(e) do |n|
|
396
|
+
if n > 0 && lines[n - 1]
|
397
|
+
if n == line
|
398
|
+
print "=> %d %s\n", n, lines[n - 1].chomp
|
399
|
+
else
|
400
|
+
print " %d %s\n", n, lines[n - 1].chomp
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
else
|
405
|
+
print "No source-file available for %s\n", file
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
def print_methods(methods)
|
410
|
+
print_element "methods" do
|
411
|
+
methods.each do |method|
|
412
|
+
print "<method name=\"%s\" />", method
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
# Events
|
418
|
+
|
419
|
+
def print_breakpoint(_, breakpoint)
|
420
|
+
print("<breakpoint file=\"%s\" line=\"%s\" threadId=\"%d\"/>",
|
421
|
+
CGI.escapeHTML(breakpoint.source), breakpoint.pos, Debugger.current_context.thnum)
|
422
|
+
end
|
423
|
+
|
424
|
+
def print_catchpoint(exception)
|
425
|
+
context = Debugger.current_context
|
426
|
+
print("<exception file=\"%s\" line=\"%s\" type=\"%s\" message=\"%s\" threadId=\"%d\"/>",
|
427
|
+
CGI.escapeHTML(context.frame_file(0)), context.frame_line(0), exception.class, CGI.escapeHTML(exception.to_s), context.thnum)
|
428
|
+
end
|
429
|
+
|
430
|
+
def print_trace(context, file, line)
|
431
|
+
Debugger::print_debug "trace: location=\"%s:%s\", threadId=%d", file, line, context.thnum
|
432
|
+
# TBD: do we want to clog fronend with the <trace> elements? There are tons of them.
|
433
|
+
# print "<trace file=\"%s\" line=\"%s\" threadId=\"%d\" />", file, line, context.thnum
|
434
|
+
end
|
435
|
+
|
436
|
+
def print_at_line(context, file, line)
|
437
|
+
print "<suspended file=\"%s\" line=\"%s\" threadId=\"%d\" frames=\"%d\"/>",
|
438
|
+
CGI.escapeHTML(File.expand_path(file)), line, context.thnum, context.stack_size
|
439
|
+
end
|
440
|
+
|
441
|
+
def print_exception(exception, _)
|
442
|
+
print_element("variables") do
|
443
|
+
proxy = ExceptionProxy.new(exception)
|
444
|
+
InspectCommand.reference_result(proxy)
|
445
|
+
print_variable('error', proxy, 'exception')
|
446
|
+
end
|
447
|
+
rescue Exception
|
448
|
+
print "<processingException type=\"%s\" message=\"%s\"/>",
|
449
|
+
exception.class, CGI.escapeHTML(exception.to_s)
|
450
|
+
end
|
451
|
+
|
452
|
+
def print_inspect(eval_result)
|
453
|
+
print_element("variables") do
|
454
|
+
print_variable("eval_result", eval_result, 'local')
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
def print_load_result(file, exception = nil)
|
459
|
+
if exception
|
460
|
+
print("<loadResult file=\"%s\" exceptionType=\"%s\" exceptionMessage=\"%s\"/>", file, exception.class, CGI.escapeHTML(exception.to_s))
|
461
|
+
else
|
462
|
+
print("<loadResult file=\"%s\" status=\"OK\"/>", file)
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
def print_element(name, additional_tags = nil)
|
467
|
+
additional_tags_presentation = additional_tags.nil? ? '' : additional_tags.map {|tag, value| " #{tag}=\"#{value}\""}.reduce(:+)
|
468
|
+
|
469
|
+
print("<#{name}#{additional_tags_presentation}>")
|
470
|
+
begin
|
471
|
+
yield if block_given?
|
472
|
+
ensure
|
473
|
+
print("</#{name}>")
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
private
|
478
|
+
|
479
|
+
def print(*params)
|
480
|
+
Debugger::print_debug(*params)
|
481
|
+
@interface.print(*params)
|
482
|
+
end
|
483
|
+
|
484
|
+
def handle_binary_data(value)
|
485
|
+
return '[Binary Data]' if (value.respond_to?('is_binary_data?') && value.is_binary_data?)
|
486
|
+
return '[Invalid encoding]' if (value.respond_to?('valid_encoding?') && !value.valid_encoding?)
|
487
|
+
value
|
488
|
+
end
|
489
|
+
|
490
|
+
def current_thread_attr(context)
|
491
|
+
if context.thread == Thread.current
|
492
|
+
'current="yes"'
|
493
|
+
else
|
494
|
+
''
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
def build_compact_name(value, value_str)
|
499
|
+
return compact_array_str(value) if value.is_a?(Array)
|
500
|
+
return compact_hash_str(value) if value.is_a?(Hash)
|
501
|
+
return value_str[0..max_compact_name_size - 3] + '...' if value_str.size > max_compact_name_size
|
502
|
+
nil
|
503
|
+
rescue ::Exception => e
|
504
|
+
print_debug(e)
|
505
|
+
nil
|
506
|
+
end
|
507
|
+
|
508
|
+
def max_compact_name_size
|
509
|
+
# todo: do we want to configure it?
|
510
|
+
50
|
511
|
+
end
|
512
|
+
|
513
|
+
def compact_array_str(value)
|
514
|
+
slice = value[0..10]
|
515
|
+
|
516
|
+
compact = exec_with_allocation_control(slice, :inspect, OverflowMessageType::NIL_MESSAGE)
|
517
|
+
|
518
|
+
if compact && value.size != slice.size
|
519
|
+
compact[0..compact.size - 2] + ", ...]"
|
520
|
+
end
|
521
|
+
compact
|
522
|
+
end
|
523
|
+
|
524
|
+
def compact_hash_str(value)
|
525
|
+
keys_strings = Hash.new
|
526
|
+
|
527
|
+
slice = value.sort_by do |k, _|
|
528
|
+
keys_string = exec_with_allocation_control(k, :to_s, OverflowMessageType::SPECIAL_SYMBOL_MESSAGE)
|
529
|
+
keys_strings[k] = keys_string
|
530
|
+
keys_string
|
531
|
+
end[0..5]
|
532
|
+
|
533
|
+
compact = slice.map do |kv|
|
534
|
+
key_string = keys_strings[kv[0]]
|
535
|
+
value_string = exec_with_allocation_control(kv[1], :to_s, OverflowMessageType::SPECIAL_SYMBOL_MESSAGE)
|
536
|
+
"#{key_string}: #{handle_binary_data(value_string)}"
|
537
|
+
end.join(", ")
|
538
|
+
"{" + compact + (slice.size != value.size ? ", ..." : "") + "}"
|
539
|
+
end
|
540
|
+
|
541
|
+
def build_compact_value_attr(value, value_str)
|
542
|
+
compact_value_str = build_compact_name(value, value_str)
|
543
|
+
compact_value_str.nil? ? '' : "compactValue=\"#{CGI.escapeHTML(compact_value_str)}\""
|
544
|
+
end
|
545
|
+
|
546
|
+
def safe_to_string(value)
|
547
|
+
begin
|
548
|
+
str = value.to_s
|
549
|
+
rescue NoMethodError
|
550
|
+
str = "(Object doesn't support #to_s)"
|
551
|
+
end
|
552
|
+
return str unless str.nil?
|
553
|
+
|
554
|
+
string_io = StringIO.new
|
555
|
+
string_io.write(value)
|
556
|
+
string_io.string
|
557
|
+
end
|
558
|
+
|
559
|
+
def build_value_attr(escaped_value_str)
|
560
|
+
Debugger.value_as_nested_element ? '' : "value=\"#{escaped_value_str}\""
|
561
|
+
end
|
562
|
+
|
563
|
+
instance_methods.each do |m|
|
564
|
+
if m.to_s.index('print_') == 0
|
565
|
+
protect m
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
end
|
570
|
+
|
571
|
+
end
|