Sat, 02 Mar 2013 15:05:29 +0100
First part of an attempt to fix the Ruby debugger backend.
--- a/DebugClients/Ruby/AsyncFile.rb Thu Feb 28 16:57:45 2013 +0100 +++ b/DebugClients/Ruby/AsyncFile.rb Sat Mar 02 15:05:29 2013 +0100 @@ -7,11 +7,6 @@ File implementing an asynchronous file like socket interface for the debugger. =end -if RUBY_VERSION < "1.9" - $KCODE = 'UTF8' - require 'jcode' -end - require 'socket' require 'DebugProtocol'
--- a/DebugClients/Ruby/AsyncIO.rb Thu Feb 28 16:57:45 2013 +0100 +++ b/DebugClients/Ruby/AsyncIO.rb Sat Mar 02 15:05:29 2013 +0100 @@ -7,11 +7,6 @@ File implementing an asynchronous interface for the debugger. =end -if RUBY_VERSION < "1.9" - $KCODE = 'UTF8' - require 'jcode' -end - module AsyncIO =begin edoc Module implementing asynchronous reading and writing.
--- a/DebugClients/Ruby/Completer.rb Thu Feb 28 16:57:45 2013 +0100 +++ b/DebugClients/Ruby/Completer.rb Sat Mar 02 15:05:29 2013 +0100 @@ -14,11 +14,6 @@ # From Original Idea of shugo@ruby-lang.org # -if RUBY_VERSION < "1.9" - $KCODE = 'UTF8' - require 'jcode' -end - class Completer =begin edoc Class implementing a command completer. @@ -60,12 +55,20 @@ @return list of possible completions (Array) =end case input + when /^((["'`]).*\2)\.([^.]*)$/ + # String + receiver = $1 + message = $3 + + candidates = String.instance_methods.collect{|m| m.to_s} + select_message(receiver, message, candidates) + when /^(\/[^\/]*\/)\.([^.]*)$/ # Regexp receiver = $1 message = Regexp.quote($2) - candidates = Regexp.instance_methods(true) + candidates = Regexp.instance_methods.collect{|m| m.to_s} select_message(receiver, message, candidates) when /^([^\]]*\])\.([^.]*)$/ @@ -73,7 +76,7 @@ receiver = $1 message = Regexp.quote($2) - candidates = Array.instance_methods(true) + candidates = Array.instance_methods.collect{|m| m.to_s} select_message(receiver, message, candidates) when /^([^\}]*\})\.([^.]*)$/ @@ -81,7 +84,8 @@ receiver = $1 message = Regexp.quote($2) - candidates = Proc.instance_methods(true) | Hash.instance_methods(true) + candidates = Proc.instance_methods.collect{|m| m.to_s} + candidates |= Hash.instance_methods.collect{|m| m.to_s} select_message(receiver, message, candidates) when /^(:[^:.]*)$/ @@ -97,61 +101,83 @@ when /^::([A-Z][^:\.\(]*)$/ # Absolute Constant or class methods receiver = $1 - candidates = Object.constants + candidates = Object.constants.collect{|m| m.to_s} candidates.grep(/^#{receiver}/).collect{|e| "::" + e} - when /^(((::)?[A-Z][^:.\(]*)+)::?([^:.]*)$/ + when /^([A-Z].*)::([^:.]*)$/ # Constant or class methods receiver = $1 message = Regexp.quote($4) begin - candidates = eval("#{receiver}.constants | #{receiver}.methods", @binding) + candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind) + candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind) rescue Exception candidates = [] end - candidates.grep(/^#{message}/).collect{|e| receiver + "::" + e} + select_message(receiver, message, candidates, "::") - when /^(:[^:.]+)\.([^.]*)$/ + when /^(:[^:.]+)(\.|::)([^.]*)$/ # Symbol receiver = $1 - message = Regexp.quote($2) + sep = $2 + message = Regexp.quote($3) - candidates = Symbol.instance_methods(true) - select_message(receiver, message, candidates) + candidates = Symbol.instance_methods.collect{|m| m.to_s} + select_message(receiver, message, candidates, sep) - when /^([0-9_]+(\.[0-9_]+)?(e[0-9]+)?)\.([^.]*)$/ + when /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)(\.|::)([^.]*)$/ # Numeric receiver = $1 - message = Regexp.quote($4) + sep = $5 + message = Regexp.quote($6) begin - candidates = eval(receiver, @binding).methods + candidates = eval(receiver, @binding).methods.collect{|m| m.to_s} rescue Exception candidates end - select_message(receiver, message, candidates) + select_message(receiver, message, candidates, sep) + + when /^(-?0x[0-9a-fA-F_]+)(\.|::)([^.]*)$/ + # Numeric(0xFFFF) + receiver = $1 + sep = $2 + message = Regexp.quote($3) + + begin + candidates = eval(receiver, bind).methods.collect{|m| m.to_s} + rescue Exception + candidates = [] + end + select_message(receiver, message, candidates, sep) when /^(\$[^.]*)$/ # Global variable candidates = global_variables.grep(Regexp.new(Regexp.quote($1))) -## when /^(\$?(\.?[^.]+)+)\.([^.]*)$/ - when /^((\.?[^.]+)+)\.([^.]*)$/ - # variable + when /^([^."].*)(\.|::)([^.]*)$/ + # variable.func or func.func receiver = $1 + sep = $2 message = Regexp.quote($3) - gv = eval("global_variables", @binding) - lv = eval("local_variables", @binding) - cv = eval("self.class.constants", @binding) + gv = eval("global_variables", @binding).collect{|m| m.to_s} + lv = eval("local_variables", @binding).collect{|m| m.to_s} + cv = eval("self.class.constants", @binding).collect{|m| m.to_s} - if (gv | lv | cv).include?(receiver) - # foo.func and foo is local var. - candidates = eval("#{receiver}.methods", @binding) - elsif /^[A-Z]/ =~ receiver and /\./ !~ receiver + if (gv | lv | cv).include?(receiver) or \ + /^[A-Z]/ =~ receiver && /\./ !~ receiver + # foo.func and foo is var. OR + # foo::func and foo is var. OR + # foo::Const and foo is var. OR # Foo::Bar.func begin - candidates = eval("#{receiver}.methods", @binding) + candidates = [] + rec = eval(receiver, bind) + if sep == "::" and rec.kind_of?(Module) + candidates = rec.constants.collect{|m| m.to_s} + end + candidates |= rec.methods.collect{|m| m.to_s} rescue Exception candidates = [] end @@ -159,14 +185,23 @@ # func1.func2 candidates = [] ObjectSpace.each_object(Module){|m| - next if m.name != "IRB::Context" and - /^(IRB|SLex|RubyLex|RubyToken)/ =~ m.name - candidates.concat m.instance_methods(false) + begin + name = m.name + rescue Exception + name = "" + end + begin + next if m.name != "IRB::Context" and + /^(IRB|SLex|RubyLex|RubyToken)/ =~ m.name + rescue Exception + next + end + candidates.concat m.instance_methods(false).collect{|x| x.to_s} } candidates.sort! candidates.uniq! end - select_message(receiver, message, candidates) + select_message(receiver, message, candidates, sep) when /^\.([^.]*)$/ # unknown(maybe String) @@ -174,11 +209,13 @@ receiver = "" message = Regexp.quote($1) - candidates = String.instance_methods(true) + candidates = String.instance_methods(true).collect{|m| m.to_s} select_message(receiver, message, candidates) else - candidates = eval("methods | private_methods | local_variables | self.class.constants", @binding) + candidates = eval( + "methods | private_methods | local_variables | self.class.constants", + @binding).collect{|m| m.to_s} (candidates|ReservedWords).grep(/^#{Regexp.quote(input)}/) end @@ -186,21 +223,22 @@ Operators = ["%", "&", "*", "**", "+", "-", "/", "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>", - "[]", "[]=", "^",] + "[]", "[]=", "^", "!", "!=", "!~"] - def select_message(receiver, message, candidates) + def select_message(receiver, message, candidates, sep = ".") =begin edoc Method used to pick completion candidates. @param receiver object receiving the message @param message message to be sent to object @param candidates possible completion candidates +@param sep separater string @return filtered list of candidates =end candidates.grep(/^#{message}/).collect do |e| case e when /^[a-zA-Z_]/ - receiver + "." + e + receiver + sep + e when /^[0-9]/ when *Operators #receiver + " " + e
--- a/DebugClients/Ruby/Config.rb Thu Feb 28 16:57:45 2013 +0100 +++ b/DebugClients/Ruby/Config.rb Sat Mar 02 15:05:29 2013 +0100 @@ -7,11 +7,6 @@ File defining the different Ruby types =end -if RUBY_VERSION < "1.9" - $KCODE = 'UTF8' - require 'jcode' -end - ConfigVarTypeStrings = ['__', 'NilClass', '_unused_', 'bool', 'Fixnum', 'Bignum', 'Float', 'Complex', 'String', 'String', '_unused_', 'Array',
--- a/DebugClients/Ruby/DebugClient.rb Thu Feb 28 16:57:45 2013 +0100 +++ b/DebugClients/Ruby/DebugClient.rb Sat Mar 02 15:05:29 2013 +0100 @@ -7,11 +7,6 @@ File implementing a debug client. =end -if RUBY_VERSION < "1.9" - $KCODE = 'UTF8' - require 'jcode' -end - # insert path to ourself in front of the search path $:.insert(0, File.dirname($0))
--- a/DebugClients/Ruby/DebugClientBaseModule.rb Thu Feb 28 16:57:45 2013 +0100 +++ b/DebugClients/Ruby/DebugClientBaseModule.rb Sat Mar 02 15:05:29 2013 +0100 @@ -7,11 +7,6 @@ File implementing a debug client base module. =end -if RUBY_VERSION < "1.9" - $KCODE = 'UTF8' - require 'jcode' -end - require 'socket' require 'DebugQuit' @@ -199,7 +194,7 @@ # Remove any newline if line[-1] == "\n" - line = line[1...-1] + line = line[0...-1] end ## STDOUT << line << "\n" ## debug @@ -209,7 +204,7 @@ if eoc and eoc >= 0 and line[0,1] == ">" # Get the command part and any argument cmd = line[0..eoc] - arg = line[eoc+1...-1] + arg = line[eoc+1..-1] case cmd when RequestOK @@ -304,6 +299,9 @@ end @running = fn command = "$0 = '%s'; require '%s'" % [fn, fn] + RubyVM::InstructionSequence.compile_option = { + trace_instruction: true + } set_trace_func proc { |event, file, line, id, binding_, klass, *rest| DEBUGGER__.context.trace_func(event, file, line, id, binding_, klass) }
--- a/DebugClients/Ruby/DebugClientCapabilities.rb Thu Feb 28 16:57:45 2013 +0100 +++ b/DebugClients/Ruby/DebugClientCapabilities.rb Sat Mar 02 15:05:29 2013 +0100 @@ -7,11 +7,6 @@ File defining the debug clients capabilities. =end -if RUBY_VERSION < "1.9" - $KCODE = 'UTF8' - require 'jcode' -end - HasDebugger = 0x0001 HasInterpreter = 0x0002 HasProfiler = 0x0004
--- a/DebugClients/Ruby/DebugProtocol.rb Thu Feb 28 16:57:45 2013 +0100 +++ b/DebugClients/Ruby/DebugProtocol.rb Sat Mar 02 15:05:29 2013 +0100 @@ -7,11 +7,6 @@ File defining the debug protocol tokens =end -if RUBY_VERSION < "1.9" - $KCODE = 'UTF8' - require 'jcode' -end - # The address used for debugger/client communications. DebugAddress = '127.0.0.1'
--- a/DebugClients/Ruby/Debuggee.rb Thu Feb 28 16:57:45 2013 +0100 +++ b/DebugClients/Ruby/Debuggee.rb Sat Mar 02 15:05:29 2013 +0100 @@ -17,6 +17,8 @@ File implementing the real debugger, which is connected to the IDE frontend. =end +require 'continuation' + require 'DebugQuit' require 'rbconfig' @@ -24,64 +26,6 @@ =begin edoc Class implementing the real debugger. =end - class Mutex -=begin edoc -Class implementing a mutex. -=end - def initialize -=begin edoc -Constructor -=end - @locker = nil - @waiting = [] - @locked = false; - end - - def locked? -=begin edoc -Method returning the locked state. - -@return flag indicating the locked state (boolean) -=end - @locked - end - - def lock -=begin edoc -Method to lock the mutex. - -@return the mutex -=end - return if @locker == Thread.current - while (Thread.critical = true; @locked) - @waiting.push Thread.current - Thread.stop - end - @locked = true - @locker = Thread.current - Thread.critical = false - self - end - - def unlock -=begin edoc -Method to unlock the mutex. - -@return the mutex -=end - return unless @locked - unless @locker == Thread.current - raise RuntimeError, "unlocked by other" - end - Thread.critical = true - t = @waiting.shift - @locked = false - @locker = nil - Thread.critical = false - t.run if t - self - end - end MUTEX = Mutex.new class Context @@ -186,12 +130,14 @@ =begin edoc Method to check the suspend state. =end - while (Thread.critical = true; @suspend_next) - DEBUGGER__.waiting.push Thread.current - @suspend_next = false - Thread.stop + while MUTEX.synchronize { + if @suspend_next + DEBUGGER__.waiting.push Thread.current + @suspend_next = false + true + end + } end - Thread.critical = false end def stdout @@ -315,7 +261,7 @@ # bp[7] special condition # bp[8] hash of special values return false if break_points.empty? - for b in break_points + break_points.each do |b| if b[0] if b[1] == 0 and b[2] == file and b[3] == pos # breakpoint # Evaluate condition @@ -441,7 +387,7 @@ @param pos line number of the breakpoint (int) @param enable flag indicating the new enabled state (boolean) =end - for bp in break_points + break_points.each do |bp| if (bp[1] == 0 and bp[2] == file and bp[3] == pos) bp[0] = enable break @@ -457,7 +403,7 @@ @param pos line number of the breakpoint (int) @param count ignore count to be set (int) =end - for bp in break_points + break_points.each do |bp| if (bp[2] == file and bp[3] == pos) bp[6] = count break @@ -508,7 +454,7 @@ @param cond expression of the watch expression (String) @param enable flag indicating the new enabled state (boolean) =end - for bp in break_points + break_points.each do |bp| if (bp[1] == 1 and bp[2] == cond) bp[0] = enable break @@ -523,7 +469,7 @@ @param cond expression of the watch expression (String) @param count ignore count to be set (int) =end - for bp in break_points + break_points.each do |bp| if (bp[1] == 1 and bp[2] == cond) bp[6] = count break @@ -576,8 +522,8 @@ end if not traceRuby? and - (file =~ /#{Config::CONFIG['sitelibdir']}/ or - file =~ /#{Config::CONFIG['rubylibdir']}/) + (file =~ /#{RbConfig::CONFIG['sitelibdir']}/ or + file =~ /#{RbConfig::CONFIG['rubylibdir']}/) return true end @@ -620,9 +566,6 @@ when 'return', 'end' @frames.shift - when 'end' - @frames.shift - when 'raise' excn_handle(file, line, id, binding_) @@ -662,20 +605,20 @@ when 'c-call' frame_set_pos(file, line) - if id == :require and klass == Kernel - @frames.unshift [binding_, file, line, id] - else - frame_set_pos(file, line) - end - - when 'c-return' - if id == :require and klass == Kernel - if @frames.size == @finish_pos - @stop_next = 1 - @finish_pos = 0 - end - @frames.shift - end +## if id == :require and klass == Kernel +## @frames.unshift [binding_, file, line, id] +## else +## frame_set_pos(file, line) +## end +## +## when 'c-return' +## if id == :require and klass == Kernel +## if @frames.size == @finish_pos +## @stop_next = 1 +## @finish_pos = 0 +## end +## @frames.shift +## end when 'class' @frames.unshift [binding_, file, line, id] @@ -687,9 +630,6 @@ end @frames.shift - when 'end' - @frames.shift - when 'raise' @no_step = nil @stop_next = 0 # break here before leaving... @@ -843,13 +783,13 @@ =begin edoc Method to suspend the program being debugged. =end - Thread.critical = true - make_thread_list - for th, in @thread_list - next if th == Thread.current - context(th).set_suspend + MUTEX.synchronize do + make_thread_list + for th, in @thread_list + next if th == Thread.current + context(th).set_suspend + end end - Thread.critical = false # Schedule other threads to suspend as soon as possible. Thread.pass end @@ -858,17 +798,17 @@ =begin edoc Method to resume the program being debugged. =end - Thread.critical = true - make_thread_list - for th, in @thread_list - next if th == Thread.current - context(th).clear_suspend + MUTEX.synchronize do + make_thread_list + for th, in @thread_list + next if th == Thread.current + context(th).clear_suspend + end + waiting.each do |th| + th.run + end + waiting.clear end - waiting.each do |th| - th.run - end - waiting.clear - Thread.critical = false # Schedule other threads to restart as soon as possible. Thread.pass end @@ -901,7 +841,7 @@ @param num thread number (int) @return thread with the requested number =end - th = @thread_list.index(num) + th = @thread_list.key(num) unless th @stdout.print "No thread ##{num}\n" throw :debug_error @@ -1205,4 +1145,4 @@ end Thread.main["name"] = 'Main' -end \ No newline at end of file +end