From c0ebfb887349e5a9f72a56d02f125cf900cfd45a Mon Sep 17 00:00:00 2001 From: Viuginov Nickolay Date: Wed, 2 Aug 2017 13:48:58 +0300 Subject: [PATCH] autoformat xml_printer and fix hash with long-computable `inspect` --- lib/ruby-debug-ide/xml_printer.rb | 229 ++++++++++++++++-------------- 1 file changed, 126 insertions(+), 103 deletions(-) diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index 0b0e607..447d5fa 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -5,7 +5,13 @@ module Debugger - class MemoryLimitError < StandardError + module OverflovMessageType + EXEPTION_MESSAGE = 1 + NIL_MESSAGE = 2 + SPECIAL_SYMBOL_MESSAGE = 3 + end + + class MemoryLimitError < StandardError attr_reader :message attr_reader :backtrace @@ -13,9 +19,9 @@ def initialize(message, backtrace = '') @message = message @backtrace = backtrace end - end + end - class TimeLimitError < StandardError + class TimeLimitError < StandardError attr_reader :message attr_reader :backtrace @@ -23,11 +29,11 @@ def initialize(message, backtrace = '') @message = message @backtrace = backtrace end - end + end class XmlPrinter # :nodoc: class ExceptionProxy - instance_methods.each { |m| undef_method m unless m =~ /(^__|^send$|^object_id$|^instance_variables$|^instance_eval$)/ } + instance_methods.each {|m| undef_method m unless m =~ /(^__|^send$|^object_id$|^instance_variables$|^instance_eval$)/} def initialize(exception) @exception = exception @@ -35,9 +41,9 @@ def initialize(exception) @backtrace = Debugger.cleanup_backtrace(exception.backtrace) end - private - def method_missing(called, *args, &block) - @exception.__send__(called, *args, &block) + private + def method_missing(called, *args, &block) + @exception.__send__(called, *args, &block) end end @@ -52,15 +58,15 @@ def #{mname}(*args, &block) end 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) @@ -83,7 +89,7 @@ def print_error(*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| @@ -91,18 +97,18 @@ def print_frames(context, 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, CGI.escapeHTML(File.expand_path(file)), context.frame_line(frame_id) + frame_id + 1, CGI.escapeHTML(File.expand_path(file)), context.frame_line(frame_id) end - + def print_contexts(contexts) print_element("threads") do contexts.each do |c| @@ -110,11 +116,11 @@ def print_contexts(contexts) end end end - + def print_context(context) print "", context.thnum, context.thread.status, Process.pid end - + def print_variables(vars, kind) print_element("variables") do # print self at top position @@ -124,26 +130,26 @@ def print_variables(vars, kind) 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 + 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 | + hash.keys.each {|k| if k.class.name == "String" name = '\'' + k + '\'' else - name = k.to_s + name = exec_with_allocation_control(k, ENV['DEBUGGER_MEMORY_LIMIT'].to_i, ENV['INSPECT_TIME_LIMIT'].to_i, :to_s, OverflovMessageType::EXEPTION_MESSAGE) end - print_variable(name, hash[k], 'instance') + print_variable(name, hash[k], 'instance') } end end @@ -155,32 +161,36 @@ def print_string(string) InspectCommand.reference_result(bytes) print_variable('bytes', bytes, 'instance') end - print_variable('encoding', string.encoding, 'instance') if string.respond_to?('encoding') + print_variable('encoding', string.encoding, 'instance') if string.respond_to?('encoding') end end - def exec_with_allocation_control(value, memory_limit, time_limit, exec_method, return_message_if_overflow) + def exec_with_allocation_control(value, memory_limit, time_limit, exec_method, overflov_message_type) + if (defined?(JRUBY_VERSION) || ENV['DEBUGGER_MEMORY_LIMIT'].to_i <= 0) + return value.send exec_method + end + curr_thread = Thread.current result = nil inspect_thread = DebugThread.start { start_alloc_size = ObjectSpace.memsize_of_all start_time = Time.now.to_f - + trace = TracePoint.new(:c_call, :call) do |tp| - - if(rand > 0.75) + + if (rand > 0.75) curr_alloc_size = ObjectSpace.memsize_of_all curr_time = Time.now.to_f - - if((curr_time - start_time) * 1e3 > time_limit) - curr_thread.raise TimeLimitError.new("Timeout: evaluation of #{exec_method} took longer than #{time_limit}ms.", "#{caller.map{|l| "\t#{l}"}.join("\n")}") + + if ((curr_time - start_time) * 1e3 > time_limit) + curr_thread.raise TimeLimitError.new("Timeout: evaluation of #{exec_method} took longer than #{time_limit}ms.", "#{caller.map {|l| "\t#{l}"}.join("\n")}") inspect_thread.kill end start_alloc_size = curr_alloc_size if (curr_alloc_size < start_alloc_size) - - if(curr_alloc_size - start_alloc_size > 1e6 * memory_limit) - curr_thread.raise MemoryLimitError.new("Out of memory: evaluation of #{exec_method} took more than #{memory_limit}mb.", "#{caller.map{|l| "\t#{l}"}.join("\n")}") + + if (curr_alloc_size - start_alloc_size > 1e6 * memory_limit) + curr_thread.raise MemoryLimitError.new("Out of memory: evaluation of #{exec_method} took more than #{memory_limit}mb.", "#{caller.map {|l| "\t#{l}"}.join("\n")}") inspect_thread.kill end end @@ -193,8 +203,15 @@ def exec_with_allocation_control(value, memory_limit, time_limit, exec_method, r return result rescue MemoryLimitError, TimeLimitError => e print_debug(e.message + "\n" + e.backtrace) - - return return_message_if_overflow ? e.message : nil + + return case overflov_message_type + when OverflovMessageType::NIL_MESSAGE + nil + when OverflovMessageType::EXEPTION_MESSAGE + e.message + when OverflovMessageType::SPECIAL_SYMBOL_MESSAGE + '' + end end def print_variable(name, value, kind) @@ -206,7 +223,7 @@ def print_variable(name, value, kind) if value.is_a?(Array) || value.is_a?(Hash) has_children = !value.empty? if has_children - size = value.size + size = value.size value_str = "#{value.class} (#{value.size} element#{size > 1 ? "s" : "" })" else value_str = "Empty #{value.class}" @@ -214,33 +231,29 @@ def print_variable(name, value, kind) elsif value.is_a?(String) has_children = value.respond_to?('bytes') || value.respond_to?('encoding') value_str = value - else + else has_children = !value.instance_variables.empty? || !value.class.class_variables.empty? - - value_str = if (defined?(JRUBY_VERSION) || ENV['DEBUGGER_MEMORY_LIMIT'].to_i <= 0) - value.to_s || 'nil' rescue "<#to_s method raised exception: #{$!}>" - else - exec_with_allocation_control(value, ENV['DEBUGGER_MEMORY_LIMIT'].to_i, ENV['INSPECT_TIME_LIMIT'].to_i, :to_s, false) || 'nil' rescue "<#to_s method raised exception: #{$!}>" - end - + + value_str = exec_with_allocation_control(value, ENV['DEBUGGER_MEMORY_LIMIT'].to_i, ENV['INSPECT_TIME_LIMIT'].to_i, :to_s, OverflovMessageType::EXEPTION_MESSAGE) || '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." + value_str = "ERROR: #{value.class}.to_s method returns #{value_str.class}. Should return String." end end if value_str.respond_to?('encode') # noinspection RubyEmptyRescueBlockInspection begin - value_str = value_str.encode("UTF-8") + value_str = value_str.encode("UTF-8") rescue end end value_str = handle_binary_data(value_str) escaped_value_str = CGI.escapeHTML(value_str) print("", - CGI.escapeHTML(name), build_compact_value_attr(value, value_str), kind, - build_value_attr(escaped_value_str), value.class, - has_children, value.respond_to?(:object_id) ? value.object_id : value.id) + CGI.escapeHTML(name), build_compact_value_attr(value, value_str), kind, + build_value_attr(escaped_value_str), value.class, + has_children, value.respond_to?(:object_id) ? value.object_id : value.id) print("", escaped_value_str) if Debugger.value_as_nested_element print('') rescue StandardError => e @@ -263,28 +276,28 @@ def print_file_filter_status(status) def print_breakpoints(breakpoints) print_element 'breakpoints' do - breakpoints.sort_by{|b| b.id }.each do |b| + breakpoints.sort_by {|b| b.id}.each do |b| print "", b.id, CGI.escapeHTML(b.source), b.pos.to_s end end end - + def print_breakpoint_added(b) print "", b.id, CGI.escapeHTML(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 @@ -304,37 +317,37 @@ def print_catchpoint_deleted(exception_class_name) def print_expressions(exps) print_element "expressions" do exps.each_with_index do |(exp, value), idx| - print_expression(exp, value, idx+1) + 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_expression_info(incomplete, prompt, indent) print "", - incomplete, CGI.escapeHTML(prompt), indent + incomplete, CGI.escapeHTML(prompt), indent end - + def print_eval(exp, value) - print "", CGI.escapeHTML(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 > 0 && lines[n - 1] if n == line - print "=> %d %s\n", n, lines[n-1].chomp + print "=> %d %s\n", n, lines[n - 1].chomp else - print " %d %s\n", n, lines[n-1].chomp + print " %d %s\n", n, lines[n - 1].chomp end end end @@ -342,7 +355,7 @@ def print_list(b, e, file, line) print "No source-file available for %s\n", file end end - + def print_methods(methods) print_element "methods" do methods.each do |method| @@ -350,31 +363,31 @@ def print_methods(methods) end end end - + # Events - + def print_breakpoint(_, breakpoint) print("", - CGI.escapeHTML(breakpoint.source), breakpoint.pos, Debugger.current_context.thnum) + CGI.escapeHTML(breakpoint.source), breakpoint.pos, Debugger.current_context.thnum) end - + def print_catchpoint(exception) context = Debugger.current_context - print("", - CGI.escapeHTML(context.frame_file(0)), context.frame_line(0), exception.class, CGI.escapeHTML(exception.to_s), context.thnum) + print("", + CGI.escapeHTML(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 "", CGI.escapeHTML(File.expand_path(file)), line, context.thnum, context.stack_size end - + def print_exception(exception, _) print_element("variables") do proxy = ExceptionProxy.new(exception) @@ -382,21 +395,21 @@ def print_exception(exception, _) print_variable('error', proxy, 'exception') end rescue Exception - print "", - exception.class, CGI.escapeHTML(exception.to_s) + print "", + exception.class, CGI.escapeHTML(exception.to_s) end - + def print_inspect(eval_result) - print_element("variables") do + print_element("variables") do print_variable("eval_result", eval_result, 'local') end end - - def print_load_result(file, exception=nil) + + def print_load_result(file, exception = nil) if exception - print("", file, exception.class, CGI.escapeHTML(exception.to_s)) + print("", file, exception.class, CGI.escapeHTML(exception.to_s)) else - print("", file) + print("", file) end end @@ -410,7 +423,7 @@ def print_element(name) end private - + def print(*params) Debugger::print_debug(*params) @interface.print(*params) @@ -446,28 +459,38 @@ def max_compact_name_size end def compact_array_str(value) - slice = value[0..10] - - compact = if (defined?(JRUBY_VERSION) || ENV['DEBUGGER_MEMORY_LIMIT'].to_i <= 0) - slice.inspect - else - exec_with_allocation_control(slice, ENV['DEBUGGER_MEMORY_LIMIT'].to_i, ENV['INSPECT_TIME_LIMIT'].to_i, :inspect, true) - end - + slice = value[0..10] + + compact = exec_with_allocation_control(slice, ENV['DEBUGGER_MEMORY_LIMIT'].to_i, ENV['INSPECT_TIME_LIMIT'].to_i, :inspect, OverflovMessageType::NIL_MESSAGE) + if compact && value.size != slice.size - compact[0..compact.size-2] + ", ...]" + compact[0..compact.size - 2] + ", ...]" end - compact + compact end def compact_hash_str(value) - slice = value.sort_by { |k, _| k.to_s }[0..5] - compact = slice.map { |kv| "#{kv[0]}: #{handle_binary_data(kv[1])}" }.join(", ") + compact = '' + + key_strings = Hash.new + slice = value.sort_by do |k, _| + key_str = exec_with_allocation_control(k, ENV['DEBUGGER_MEMORY_LIMIT'].to_i, ENV['INSPECT_TIME_LIMIT'].to_i, :to_s, OverflovMessageType::SPECIAL_SYMBOL_MESSAGE) + key_strings[k] = key_str + key_str + end[0..5] + + compact = slice.map {|kv| + kv_name = key_strings[kv[0]] + kv_value = exec_with_allocation_control(kv[1], ENV['DEBUGGER_MEMORY_LIMIT'].to_i, ENV['INSPECT_TIME_LIMIT'].to_i, :to_s, OverflovMessageType::SPECIAL_SYMBOL_MESSAGE) + "#{kv_name}: #{handle_binary_data(kv_value)}" + }.join(", ") + + "{" + compact + (slice.size != value.size ? ", ..." : "") + "}" end def build_compact_value_attr(value, value_str) - compact_value_str = build_compact_name(value, value_str) + compact_value_str = build_compact_name(value, value_str) compact_value_str.nil? ? '' : "compactValue=\"#{CGI.escapeHTML(compact_value_str)}\"" end @@ -493,7 +516,7 @@ def build_value_attr(escaped_value_str) protect m end end - + end end