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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +75 -0
  3. data/ChangeLog.archive +1073 -0
  4. data/ChangeLog.md +594 -0
  5. data/Gemfile +38 -0
  6. data/MIT-LICENSE +24 -0
  7. data/Rakefile +93 -0
  8. data/bin/gdb_wrapper +96 -0
  9. data/bin/rdebug-ide +200 -0
  10. data/ext/mkrf_conf.rb +44 -0
  11. data/lib/ruby-debug-ide/attach/debugger_loader.rb +20 -0
  12. data/lib/ruby-debug-ide/attach/gdb.rb +73 -0
  13. data/lib/ruby-debug-ide/attach/lldb.rb +71 -0
  14. data/lib/ruby-debug-ide/attach/native_debugger.rb +133 -0
  15. data/lib/ruby-debug-ide/attach/process_thread.rb +54 -0
  16. data/lib/ruby-debug-ide/attach/util.rb +115 -0
  17. data/lib/ruby-debug-ide/command.rb +187 -0
  18. data/lib/ruby-debug-ide/commands/breakpoints.rb +128 -0
  19. data/lib/ruby-debug-ide/commands/catchpoint.rb +64 -0
  20. data/lib/ruby-debug-ide/commands/condition.rb +51 -0
  21. data/lib/ruby-debug-ide/commands/control.rb +158 -0
  22. data/lib/ruby-debug-ide/commands/enable.rb +203 -0
  23. data/lib/ruby-debug-ide/commands/eval.rb +64 -0
  24. data/lib/ruby-debug-ide/commands/expression_info.rb +71 -0
  25. data/lib/ruby-debug-ide/commands/file_filtering.rb +107 -0
  26. data/lib/ruby-debug-ide/commands/frame.rb +155 -0
  27. data/lib/ruby-debug-ide/commands/inspect.rb +25 -0
  28. data/lib/ruby-debug-ide/commands/jump.rb +73 -0
  29. data/lib/ruby-debug-ide/commands/load.rb +18 -0
  30. data/lib/ruby-debug-ide/commands/pause.rb +33 -0
  31. data/lib/ruby-debug-ide/commands/set_type.rb +47 -0
  32. data/lib/ruby-debug-ide/commands/stepping.rb +108 -0
  33. data/lib/ruby-debug-ide/commands/threads.rb +178 -0
  34. data/lib/ruby-debug-ide/commands/variables.rb +154 -0
  35. data/lib/ruby-debug-ide/event_processor.rb +71 -0
  36. data/lib/ruby-debug-ide/greeter.rb +42 -0
  37. data/lib/ruby-debug-ide/helper.rb +33 -0
  38. data/lib/ruby-debug-ide/ide_processor.rb +155 -0
  39. data/lib/ruby-debug-ide/interface.rb +45 -0
  40. data/lib/ruby-debug-ide/multiprocess/monkey.rb +47 -0
  41. data/lib/ruby-debug-ide/multiprocess/pre_child.rb +59 -0
  42. data/lib/ruby-debug-ide/multiprocess/starter.rb +11 -0
  43. data/lib/ruby-debug-ide/multiprocess/unmonkey.rb +31 -0
  44. data/lib/ruby-debug-ide/multiprocess.rb +23 -0
  45. data/lib/ruby-debug-ide/thread_alias.rb +27 -0
  46. data/lib/ruby-debug-ide/version.rb +3 -0
  47. data/lib/ruby-debug-ide/xml_printer.rb +571 -0
  48. data/lib/ruby-debug-ide.rb +228 -0
  49. data/ruby-debug-ide.gemspec +47 -0
  50. metadata +110 -0
@@ -0,0 +1,3 @@
1
+ module Debugger
2
+ IDE_VERSION='0.7.4'
3
+ end
@@ -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