nitdbg: Added aliases
[nit.git] / src / debugger.nit
index e9e2d74..087b2d9 100644 (file)
@@ -17,6 +17,7 @@
 # Debugging of a nit program using the NaiveInterpreter
 module debugger
 
+import breakpoint
 intrude import naive_interpreter
 
 redef class ToolContext
@@ -69,6 +70,15 @@ class Debugger
        # if the instruction is a function call)
        var step_in_trigger = false
 
+       # HashMap containing the breakpoints bound to a file
+       var breakpoints = new HashMap[String, HashSet[Breakpoint]]
+
+       # Contains the current file
+       var curr_file = ""
+
+       # Aliases hashmap (maps an alias to a variable name)
+       var aliases = new HashMap[String, String]
+
        #######################################################################
        ##                  Execution of statement function                  ##
        #######################################################################
@@ -84,6 +94,8 @@ class Debugger
 
                steps_fun_call(n)
 
+               breakpoint_check(n)
+
                n.stmt(self)
                frame.current_node = old
        end
@@ -107,6 +119,29 @@ class Debugger
                end
        end
 
+       # Checks if a breakpoint is encountered, and launches the debugging prompt if true
+       private fun breakpoint_check(n: AExpr)
+       do
+               var currFileNameSplit = self.frame.current_node.location.file.filename.to_s.split_with("/")
+
+               self.curr_file = currFileNameSplit[currFileNameSplit.length-1]
+
+               var breakpoint = find_breakpoint(curr_file, n.location.line_start)
+
+               if breakpoints.keys.has(curr_file) and breakpoint != null then
+
+                       breakpoint.check_in
+
+                       if not breakpoint.is_valid
+                       then
+                               remove_breakpoint(curr_file, n.location.line_start)
+                       end
+
+                       n.debug("Execute stmt {n.to_s}")
+                       while process_debug_command(gets) do end
+               end
+       end
+
        #######################################################################
        ##                   Processing commands functions                   ##
        #######################################################################
@@ -142,6 +177,24 @@ class Debugger
                        # Shows the value of a variable in the current frame
                        if parts_of_command[0] == "p" or parts_of_command[0] == "print" then
                                print_command(parts_of_command)
+                       # Places a breakpoint on line x of file y
+                       else if parts_of_command[0] == "break" or parts_of_command[0] == "b"
+                       then
+                               process_place_break_fun(parts_of_command)
+                       # Places a temporary breakpoint on line x of file y
+                       else if parts_of_command[0] == "tbreak" and (parts_of_command.length == 2 or parts_of_command.length == 3)
+                       then
+                               process_place_tbreak_fun(parts_of_command)
+                       # Removes a breakpoint on line x of file y
+                       else if parts_of_command[0] == "d" or parts_of_command[0] == "delete" then
+                               process_remove_break_fun(parts_of_command)
+                       # Sets an alias for a variable
+                       else if parts_of_command.length == 3 and parts_of_command[1] == "as"
+                       then
+                               add_alias(parts_of_command[0], parts_of_command[2])
+                       # Lists all the commands available
+                       else
+                               list_commands
                        end
                end
                return true
@@ -207,15 +260,90 @@ class Debugger
 
                        print "\nEnd of current instruction \n"
                else
-                       var instance = seek_variable(parts_of_command[1], frame)
+                       var instance = seek_variable(get_real_variable_name(parts_of_command[1]), frame)
 
                        if instance != null
                        then
                                print_instance(instance)
+                       else
+                               print "Cannot find variable {parts_of_command[1]}"
                        end
                end
        end
 
+       # Processes the input string to know where to put a breakpoint
+       fun process_place_break_fun(parts_of_command: Array[String])
+       do
+               var bp = get_breakpoint_from_command(parts_of_command)
+               if bp != null then
+                       place_breakpoint(bp)
+               else
+                       list_commands
+               end
+       end
+
+       # Returns a breakpoint containing the informations stored in the command
+       fun get_breakpoint_from_command(parts_of_command: Array[String]): nullable Breakpoint
+       do
+               if parts_of_command[1].is_numeric then
+                       return new Breakpoint(parts_of_command[1].to_i, curr_file)
+               else if parts_of_command.length >= 3 and parts_of_command[2].is_numeric then
+                       return new Breakpoint(parts_of_command[2].to_i, parts_of_command[1])
+               else
+                       return null
+               end
+       end
+
+       # Processes the command of removing a breakpoint on specified line and file
+       fun process_remove_break_fun(parts_of_command: Array[String])
+       do
+               if parts_of_command[1].is_numeric then
+                       remove_breakpoint(self.curr_file, parts_of_command[1].to_i)
+               else if parts_of_command.length >= 3 and parts_of_command[2].is_numeric then
+                       remove_breakpoint(parts_of_command[1], parts_of_command[2].to_i)
+               else
+                       list_commands
+               end
+       end
+
+       #######################################################################
+       ##                    Alias management functions                     ##
+       #######################################################################
+
+       # Adds a new alias to the tables
+       fun add_alias(var_represented: String, alias: String)
+       do
+               self.aliases[alias] = var_represented
+       end
+
+       # Gets the real name of a variable hidden by an alias
+       fun get_variable_name_by_alias(alias: String): nullable String
+       do
+               if self.aliases.keys.has(alias) then
+                       return self.aliases[alias]
+               end
+
+               return null
+       end
+
+       # Gets the variable named by name, whether it is an alias or not
+       fun get_real_variable_name(name: String): String
+       do
+               var explode_string = name.split_with(".")
+               var final_string = new Buffer
+               for i in explode_string do
+                       var alias_resolved = get_variable_name_by_alias(i)
+                       if alias_resolved != null then
+                               final_string.append(get_real_variable_name(alias_resolved))
+                       else
+                               final_string.append(i)
+                       end
+                       final_string.append(".")
+               end
+
+               return final_string.substring(0,final_string.length-1).to_s
+       end
+
        #######################################################################
        ##                         Print functions                           ##
        #######################################################################
@@ -314,4 +442,98 @@ class Debugger
                end
        end
 
+       #######################################################################
+       ##                   Breakpoint placing functions                    ##
+       #######################################################################
+
+       # Places a breakpoint on line 'line_to_break' for file 'file_to_break'
+       fun place_breakpoint(breakpoint: Breakpoint)
+       do
+               if not self.breakpoints.keys.has(breakpoint.file) then
+                       self.breakpoints[breakpoint.file] = new HashSet[Breakpoint]
+               end
+               if find_breakpoint(breakpoint.file, breakpoint.line) == null then
+                       self.breakpoints[breakpoint.file].add(breakpoint)
+                       print "Breakpoint added on line {breakpoint.line} for file {breakpoint.file}"
+               else
+                       print "Breakpoint already present on line {breakpoint.line} for file {breakpoint.file}"
+               end
+       end
+
+       #Places a breakpoint that will trigger once and be destroyed afterwards
+       fun process_place_tbreak_fun(parts_of_command: Array[String])
+       do
+               var bp = get_breakpoint_from_command(parts_of_command)
+               if bp != null
+               then
+                       bp.set_max_breaks(1)
+                       place_breakpoint(bp)
+               else
+                       list_commands
+               end
+       end
+
+       #######################################################################
+       ##                  Breakpoint removing functions                    ##
+       #######################################################################
+
+       # Removes a breakpoint on line 'line_to_break' for file 'file_to_break'
+       fun remove_breakpoint(file_to_break: String, line_to_break: Int)
+       do
+               if self.breakpoints.keys.has(file_to_break) then
+                       var bp = find_breakpoint(file_to_break, line_to_break)
+
+                       if bp != null then
+                               self.breakpoints[file_to_break].remove(bp)
+                               print "Breakpoint removed on line {line_to_break} for file {file_to_break}"
+                               return
+                       end
+               end
+
+               print "No breakpoint existing on line {line_to_break} for file {file_to_break}"
+       end
+
+       #######################################################################
+       ##                  Breakpoint searching functions                   ##
+       #######################################################################
+
+       # Finds a breakpoint for 'file' and 'line' in the class HashMap
+       fun find_breakpoint(file: String, line: Int): nullable Breakpoint
+       do
+               if self.breakpoints.keys.has(file)
+               then
+                       for i in self.breakpoints[file]
+                       do
+                               if i.line == line
+                               then
+                                       return i
+                               end
+                       end
+               end
+
+               return null
+       end
+
+       #######################################################################
+       ##                     Command listing function                      ##
+       #######################################################################
+
+       # Lists the commands available when using the debugger
+       fun list_commands
+       do
+               print "\nCommand not recognized\n"
+               print "Commands accepted : \n"
+               print "[break/b] line : Adds a breakpoint on line *line_nb* of the current file\n"
+               print "[break/b] file_name line_nb : Adds a breakpoint on line *line_nb* of file *file_name* \n"
+               print "[p/print] variable : [p/print] * shows the status of all the variables\n"
+               print "s : steps in on the current function\n"
+               print "n : steps-over the current instruction\n"
+               print "finish : steps out of the current function\n"
+               print "variable as alias : Adds an alias called *alias* for the variable *variable*"
+               print "An alias can reference another alias\n"
+               print "[d/delete] line_nb : Removes a breakpoint on line *line_nb* of the current file \n"
+               print "[d/delete] file_name line_nb : Removes a breakpoint on line *line_nb* of file *file_name* \n"
+               print "kill : kills the current program (Exits with an error and stack trace)\n"
+       end
+
 end