First part of an attempt to fix the Ruby debugger backend.

Sat, 02 Mar 2013 15:05:29 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 02 Mar 2013 15:05:29 +0100
changeset 2438
61bfcff921d8
parent 2436
f75dbdd22959
child 2439
aa87a4232a32

First part of an attempt to fix the Ruby debugger backend.

DebugClients/Ruby/AsyncFile.rb file | annotate | diff | comparison | revisions
DebugClients/Ruby/AsyncIO.rb file | annotate | diff | comparison | revisions
DebugClients/Ruby/Completer.rb file | annotate | diff | comparison | revisions
DebugClients/Ruby/Config.rb file | annotate | diff | comparison | revisions
DebugClients/Ruby/DebugClient.rb file | annotate | diff | comparison | revisions
DebugClients/Ruby/DebugClientBaseModule.rb file | annotate | diff | comparison | revisions
DebugClients/Ruby/DebugClientCapabilities.rb file | annotate | diff | comparison | revisions
DebugClients/Ruby/DebugProtocol.rb file | annotate | diff | comparison | revisions
DebugClients/Ruby/Debuggee.rb file | annotate | diff | comparison | revisions
--- 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

eric ide

mercurial