require 'cgi' require 'yaml' require 'monitor' module Debugger class XmlPrinter # :nodoc: class ExceptionProxy instance_methods.each { |m| undef_method m unless m =~ /(^__|^send$|^object_id$|^instance_variables$|^instance_eval$)/ } def initialize(exception) @exception = exception @message = exception.message @backtrace = cleanup_backtrace(exception.backtrace) end private def method_missing(called, *args, &block) @exception.__send__(called, *args, &block) end def cleanup_backtrace(backtrace) cleared = [] return cleared unless backtrace backtrace.each do |line| if line.index(File.expand_path(File.dirname(__FILE__))) == 0 break end cleared << line end cleared end end def self.protect(mname) return if instance_methods.include?("__#{mname}") alias_method "__#{mname}", mname class_eval %{ def #{mname}(*args, &block) @@monitor.synchronize do return unless @interface __#{mname}(*args, &block) end end } end @@monitor = Monitor.new attr_accessor :interface def initialize(interface) @interface = interface end def print_msg(*args) msg, *args = args xml_message = CGI.escapeHTML(msg % args) print "#{xml_message}" end # Sends debug message to the frontend if XML debug logging flag (--xml-debug) is on. def print_debug(*args) Debugger.print_debug(*args) if Debugger.xml_debug msg, *args = args xml_message = CGI.escapeHTML(msg % args) @interface.print("#{xml_message}") end end def print_error(*args) print_element("error") do msg, *args = args print CGI.escapeHTML(msg % args) end end def print_frames(context, current_frame_id) print_element("frames") do (0...context.stack_size).each do |id| print_frame(context, id, current_frame_id) end end end def print_current_frame(frame_pos) print_debug "Selected frame no #{frame_pos}" end def print_frame(context, frame_id, current_frame_id) # idx + 1: one-based numbering as classic-debugger file = context.frame_file(frame_id) print "", frame_id + 1, File.expand_path(file), context.frame_line(frame_id) end def print_contexts(contexts) print_element("threads") do contexts.each do |c| print_context(c) unless c.ignored? end end end def print_context(context) current = 'current="yes"' if context.thread == Thread.current print "", context.thnum, context.thread.status end def print_variables(vars, kind) print_element("variables") do # print self at top position print_variable('self', yield('self'), kind) if vars.include?('self') vars.sort.each do |v| print_variable(v, yield(v), kind) unless v == 'self' end end end def print_array(array) print_element("variables") do index = 0 array.each { |e| print_variable('[' + index.to_s + ']', e, 'instance') index += 1 } end end def print_hash(hash) print_element("variables") do hash.keys.each { | k | if k.class.name == "String" name = '\'' + k + '\'' else name = k.to_s end print_variable(name, hash[k], 'instance') } end end def print_variable(name, value, kind) name = name.to_s if value.nil? print("", CGI.escapeHTML(name), kind) return end if value.is_a?(Array) || value.is_a?(Hash) has_children = !value.empty? unless has_children value_str = "Empty #{value.class}" else value_str = "#{value.class} (#{value.size} element(s))" end else has_children = !value.instance_variables.empty? || !value.class.class_variables.empty? value_str = value.to_s || 'nil' rescue "<#to_s method raised exception: #$!>" unless value_str.is_a?(String) value_str = "ERROR: #{value.class}.to_s method returns #{value_str.class}. Should return String." end if value_str =~ /^\"(.*)"$/ value_str = $1 end end value_str = "[Binary Data]" if (value_str.respond_to?('is_binary_data?') && value_str.is_binary_data?) print("", CGI.escapeHTML(name), kind, CGI.escapeHTML(value_str), value.class, has_children, value.respond_to?(:object_id) ? value.object_id : value.id) end def print_breakpoints(breakpoints) print_element 'breakpoints' do breakpoints.sort_by{|b| b.id }.each do |b| print "", b.id, b.source, b.pos.to_s end end end def print_breakpoint_added(b) print "", b.id, b.source, b.pos end def print_breakpoint_deleted(b) print "", b.id end def print_breakpoint_enabled(b) print "", b.id end def print_breakpoint_disabled(b) print "", b.id end def print_contdition_set(bp_id) print "", bp_id end def print_catchpoint_set(exception_class_name) print "", exception_class_name end def print_expressions(exps) print_element "expressions" do exps.each_with_index do |(exp, value), idx| print_expression(exp, value, idx+1) end end unless exps.empty? end def print_expression(exp, value, idx) print "", exp, value, idx end def print_eval(exp, value) print "", CGI.escapeHTML(exp), value end def print_pp(value) print value end def print_list(b, e, file, line) print "[%d, %d] in %s\n", b, e, file if lines = Debugger.source_for(file) b.upto(e) do |n| if n > 0 && lines[n-1] if n == line print "=> %d %s\n", n, lines[n-1].chomp else print " %d %s\n", n, lines[n-1].chomp end end end else print "No sourcefile available for %s\n", file end end def print_methods(methods) print_element "methods" do methods.each do |method| print "", method end end end # Events def print_breakpoint(n, breakpoint) print("", breakpoint.source, breakpoint.pos, Debugger.current_context.thnum) end def print_catchpoint(exception) context = Debugger.current_context print("", context.frame_file(0), context.frame_line(0), exception.class, CGI.escapeHTML(exception.to_s), context.thnum) end def print_trace(context, file, line) Debugger::print_debug "trace: location=\"%s:%s\", threadId=%d", file, line, context.thnum # TBD: do we want to clog fronend with the elements? There are tons of them. # print "", file, line, context.thnum end def print_at_line(context, file, line) print "", File.expand_path(file), line, context.thnum, context.stack_size end def print_exception(exception, binding) print_variables(%w(error), 'exception') do |var| ExceptionProxy.new(exception) end rescue print "", exception.class, CGI.escapeHTML(exception.to_s) end def print_inspect(eval_result) print_element("variables") do print_variable("eval_result", eval_result, 'local') end end def print_load_result(file, exception=nil) if exception then print("", file, exception.class, CGI.escapeHTML(exception.to_s)) else print("", file) end end def print_element(name) print("<#{name}>") begin yield ensure print("") end end private def print(*params) Debugger::print_debug(*params) @interface.print(*params) end instance_methods.each do |m| if m.to_s.index('print_') == 0 protect m end end end end