X-Git-Url: http://nitlanguage.org diff --git a/src/interpreter/debugger.nit b/src/interpreter/debugger.nit index 8b52f5a..58a49d1 100644 --- a/src/interpreter/debugger.nit +++ b/src/interpreter/debugger.nit @@ -65,7 +65,7 @@ end redef class ScopeVisitor - redef init(toolcontext) + redef init do super if toolcontext.dbg != null then @@ -103,7 +103,7 @@ redef class ToolContext redef fun check_errors do if dbg == null then - super + return super else if messages.length > 0 then message_sorter.sort(messages) @@ -116,12 +116,13 @@ redef class ToolContext messages.clear end + return not had_error end # -d - var opt_debugger_mode: OptionBool = new OptionBool("Launches the target program with the debugger attached to it", "-d") + var opt_debugger_mode = new OptionBool("Launches the target program with the debugger attached to it", "-d") # -c - var opt_debugger_autorun: OptionBool = new OptionBool("Launches the target program with the interpreter, such as when the program fails, the debugging prompt is summoned", "-c") + var opt_debugger_autorun = new OptionBool("Launches the target program with the interpreter, such as when the program fails, the debugging prompt is summoned", "-c") redef init do @@ -270,19 +271,8 @@ class Debugger end # Same as a regular call but for a runtime injected module - # fun rt_call(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance do - args = call_commons(mpropdef, args) - return rt_call_without_varargs(mpropdef, args) - end - - # Common code to call and this function - # - # Call only executes the variadic part, this avoids - # double encapsulation of variadic parameters into an Array - fun rt_call_without_varargs(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance - do if self.modelbuilder.toolcontext.opt_discover_call_trace.value and not self.discover_call_trace.has(mpropdef) then self.discover_call_trace.add mpropdef self.debug("Discovered {mpropdef}") @@ -290,20 +280,13 @@ class Debugger assert args.length == mpropdef.msignature.arity + 1 else debug("Invalid arity for {mpropdef}. {args.length} arguments given.") # Look for the AST node that implements the property - var mproperty = mpropdef.mproperty - if self.modelbuilder.mpropdef2npropdef.has_key(mpropdef) then - var npropdef = self.modelbuilder.mpropdef2npropdef[mpropdef] - self.parameter_check(npropdef, mpropdef, args) - if npropdef isa AMethPropdef then - return npropdef.rt_call(self, mpropdef, args) - else - print "Error, invalid propdef to call at runtime !" - return null - end - else if mproperty.name == "init" then - var nclassdef = self.modelbuilder.mclassdef2nclassdef[mpropdef.mclassdef] - self.parameter_check(nclassdef, mpropdef, args) - return nclassdef.call(self, mpropdef, args) + var node = modelbuilder.mpropdef2node(mpropdef) + if node isa AMethPropdef then + self.parameter_check(node, mpropdef, args) + return node.rt_call(self, mpropdef, args) + else if node isa AClassdef then + self.parameter_check(node, mpropdef, args) + return node.call(self, mpropdef, args) else fatal("Fatal Error: method {mpropdef} not found in the AST") abort @@ -337,7 +320,6 @@ class Debugger var mmod = e.mmodule if mmod != null then self.mainmodule = mmod - var local_classdefs = mmod.mclassdefs var sys_type = mmod.sys_type if sys_type == null then print "Fatal error, cannot find Class Sys !\nAborting" @@ -404,7 +386,6 @@ class Debugger var identifiers_in_instruction = get_identifiers_in_current_instruction(n.location.text) for i in identifiers_in_instruction do - var variable = seek_variable(i, frame) for j in self.traces do if j.is_variable_traced_in_frame(i, frame) then n.debug("Traced variable {i} used") @@ -470,7 +451,7 @@ class Debugger # # Returns a boolean value, representing whether or not to # continue reading commands from the console input - fun process_debug_command(command:String): Bool + fun process_debug_command(command: String): Bool do # Step-out command if command == "finish" @@ -483,10 +464,17 @@ class Debugger # Step-over command else if command == "n" then return step_over + # Shows help + else if command == "help" then + help + return true # Opens a new NitIndex prompt on current model else if command == "nitx" then new NitIndex.with_infos(modelbuilder, self.mainmodule).prompt return true + else if command == "bt" or command == "backtrack" then + print stack_trace + return true # Continues execution until the end else if command == "c" then return continue_exec @@ -508,37 +496,72 @@ class Debugger print stack_trace exit(0) else - var parts_of_command = command.split_with(' ') + var parts = command.split_with(' ') + var cname = parts.first # 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) + if cname == "p" or cname == "print" then + print_command(parts) # 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) + else if cname == "break" or cname == "b" then + process_place_break_fun(parts) # 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) + else if cname == "d" or cname == "delete" then + process_remove_break_fun(parts) # 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]) + else if parts.length == 2 and parts[1] == "as" then + process_alias(parts) # Modifies the value of a variable in the current frame - else if parts_of_command.length >= 3 and parts_of_command[1] == "=" then - process_mod_function(parts_of_command) + else if parts.length == 3 and parts[1] == "=" then + process_mod_function(parts) # Traces the modifications on a variable - else if parts_of_command.length >= 2 and parts_of_command[0] == "trace" then - process_trace_command(parts_of_command) + else if cname == "trace" then + process_trace_command(parts) # Untraces the modifications on a variable - else if parts_of_command.length == 2 and parts_of_command[0] == "untrace" then - process_untrace_command(parts_of_command) + else if cname == "untrace" then + process_untrace_command(parts) else - print "Unknown command \"{command}\"" + bad_command(command) end end return true end + # Produces help for the commands of the debugger + fun help do + print "" + print "Help :" + print "-----------------------------------" + print "" + print "Variables" + print " * Modification: var_name = value (Warning: var_name must be primitive)" + print " * Alias: var_name as alias" + print "" + print "Printing" + print " * Variables: p(rint) var_name (Use * to print all local variables)" + print " * Collections: p(rint) var_name '[' start_index (.. end_index) ']'" + print "" + print "Breakpoints" + print " * File/line: b(reak) file_name line_number" + print " * Remove: d(elete) id" + print "" + print "Tracepoints" + print " * Variable: trace var_name break/print" + print " * Untrace variable: untrace var_name" + print "" + print "Flow control" + print " * Next instruction (same-level): n" + print " * Next instruction: s" + print " * Finish current method: finish" + print " * Continue until next breakpoint or end: c" + print "" + print "General commands" + print " * quit: Quits the debugger" + print " * abort: Aborts the interpretation, prints the stack trace before leaving" + print " * nitx: Ask questions to the model about its entities (classes, methods, etc.)" + print " * nit: Inject dynamic code for interpretation" + print "" + end + ####################################################################### ## Processing specific command functions ## ####################################################################### @@ -581,75 +604,96 @@ class Debugger return false end + fun bad_command(cmd: String) do + print "Unrecognized command {cmd}. Use 'help' to show help." + end + # Prints the demanded variable in the command # # The name of the variable in in position 1 of the array 'parts_of_command' - fun print_command(parts_of_command: Array[String]) + fun print_command(parts: Array[String]) do - if parts_of_command[1] == "*" then + if parts.length != 2 then + bad_command(parts.join(" ")) + return + end + if parts[1] == "*" then var map_of_instances = frame.map - var keys = map_of_instances.iterator - var self_var = seek_variable("self", frame) print "self: {self_var.to_s}" for instance in map_of_instances.keys do print "{instance.to_s}: {map_of_instances[instance].to_s}" end - else if parts_of_command[1] == "stack" then - print self.stack_trace - else if parts_of_command[1].chars.has('[') and parts_of_command[1].chars.has(']') then - process_array_command(parts_of_command) + else if parts[1].chars.has('[') and parts[1].chars.has(']') then + process_array_command(parts) else - var instance = seek_variable(get_real_variable_name(parts_of_command[1]), frame) + var instance = seek_variable(get_real_variable_name(parts[1]), frame) if instance != null then print_instance(instance) else - print "Cannot find variable {parts_of_command[1]}" + print "Cannot find variable {parts[1]}" end end end + # Process the input command to set an alias for a variable + fun process_alias(parts: Array[String]) do + if parts.length != 3 then + bad_command(parts.join(" ")) + return + end + add_alias(parts.first, parts.last) + end + # Processes the input string to know where to put a breakpoint - fun process_place_break_fun(parts_of_command: Array[String]) + fun process_place_break_fun(parts: Array[String]) do - var bp = get_breakpoint_from_command(parts_of_command) + if parts.length != 3 then + bad_command(parts.join(" ")) + return + end + var bp = get_breakpoint_from_command(parts) if bp != null then place_breakpoint(bp) end end # Returns a breakpoint containing the informations stored in the command - fun get_breakpoint_from_command(parts_of_command: Array[String]): nullable Breakpoint + fun get_breakpoint_from_command(parts: 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]) + if parts[1].is_numeric then + return new Breakpoint(parts[1].to_i, curr_file) + else if parts.length >= 3 and parts[2].is_numeric then + return new Breakpoint(parts[2].to_i, parts[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]) + fun process_remove_break_fun(parts: 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) + if parts.length != 2 then + bad_command(parts.join(" ")) + return + end + if parts[1].is_numeric then + remove_breakpoint(self.curr_file, parts[1].to_i) + else if parts.length >= 3 and parts[2].is_numeric then + remove_breakpoint(parts[1], parts[2].to_i) end end # Processes an array print command - fun process_array_command(parts_of_command: Array[String]) + fun process_array_command(parts: Array[String]) do - var index_of_first_brace = parts_of_command[1].chars.index_of('[') - var variable_name = get_real_variable_name(parts_of_command[1].substring(0,index_of_first_brace)) - var braces = parts_of_command[1].substring_from(index_of_first_brace) + var index_of_first_brace = parts[1].chars.index_of('[') + var variable_name = get_real_variable_name(parts[1].substring(0,index_of_first_brace)) + var braces = parts[1].substring_from(index_of_first_brace) var indexes = remove_braces(braces) @@ -677,27 +721,32 @@ class Debugger # Processes the modification function to modify a variable dynamically # # Command of type variable = value - fun process_mod_function(parts_of_command: Array[String]) + fun process_mod_function(parts: Array[String]) do - parts_of_command[0] = get_real_variable_name(parts_of_command[0]) - var parts_of_variable = parts_of_command[0].split_with(".") + if parts.length != 3 then + bad_command(parts.join(" ")) + return + end + var p0 = parts[0] + p0 = get_real_variable_name(p0) + var parts_of_variable = p0.split_with(".") if parts_of_variable.length > 1 then var last_part = parts_of_variable.pop - var first_part = parts_of_command[0].substring(0,parts_of_command[0].length - last_part.length - 1) + var first_part = p0.substring(0,p0.length - last_part.length - 1) var papa = seek_variable(first_part, frame) if papa != null and papa isa MutableInstance then var attribute = get_attribute_in_mutable_instance(papa, last_part) if attribute != null then - modify_argument_of_complex_type(papa, attribute, parts_of_command[2]) + modify_argument_of_complex_type(papa, attribute, parts[2]) end end else var target = seek_variable(parts_of_variable[0], frame) if target != null then - modify_in_frame(target, parts_of_command[2]) + modify_in_frame(target, parts[2]) end end end @@ -705,42 +754,46 @@ class Debugger # Processes the untrace variable command # # Command pattern : "untrace variable" - fun process_untrace_command(parts_of_command: Array[String]) + fun process_untrace_command(parts: Array[String]) do - var variable_name = get_real_variable_name(parts_of_command[1]) + if parts.length != 2 then + bad_command(parts.join(" ")) + return + end + var variable_name = get_real_variable_name(parts[1]) if untrace_variable(variable_name) then - print "Untraced variable {parts_of_command[1]}" + print "Untraced variable {parts[1]}" else - print "{parts_of_command[1]} is not traced" + print "{parts[1]} is not traced" end end # Processes the trace variable command # # Command pattern : "trace variable [break/print]" - fun process_trace_command(parts_of_command: Array[String]) + fun process_trace_command(parts: Array[String]) do - var variable_name = get_real_variable_name(parts_of_command[1]) + if parts.length != 3 then + bad_command(parts.join(" ")) + return + end + var variable_name = get_real_variable_name(parts[1]) var breaker:Bool if seek_variable(variable_name, frame) == null then - print "Cannot find a variable called {parts_of_command[1]}" + print "Cannot find a variable called {parts[1]}" return end - if parts_of_command.length == 3 then - if parts_of_command[2] == "break" then - breaker = true - else - breaker = false - end + if parts[2] == "break" then + breaker = true else breaker = false end trace_variable(variable_name, breaker) - print "Successfully tracing {parts_of_command[1]}" + print "Successfully tracing {parts[1]}" end ####################################################################### @@ -833,14 +886,12 @@ class Debugger var trigger_char_escape = false var trigger_string_escape = false - var trigger_concat_in_string = false for i in instruction.chars do if trigger_char_escape then if i == '\'' then trigger_char_escape = false else if trigger_string_escape then if i == '{' then - trigger_concat_in_string = true trigger_string_escape = false else if i == '\"' then trigger_string_escape = false else @@ -858,7 +909,6 @@ class Debugger else if i == '\"' then trigger_string_escape = true else if i == '}' then - trigger_concat_in_string = false trigger_string_escape = true else if instruction_buffer.length > 0 and not instruction_buffer.is_numeric and not (instruction_buffer.chars[0] >= 'A' and instruction_buffer.chars[0] <= 'Z') then result_array.push(instruction_buffer.to_s) @@ -1085,8 +1135,6 @@ class Debugger do var collection_length_attribute = get_attribute_in_mutable_instance(collection, "length") - var real_collection_length: nullable Int = null - if collection_length_attribute != null then var primitive_length_instance = collection.attributes[collection_length_attribute] if primitive_length_instance isa PrimitiveInstance[Int] then @@ -1388,16 +1436,11 @@ end private class TraceObject # Map of the local names bound to a frame - var trace_map: HashMap[Frame, String] + var trace_map = new HashMap[Frame, String] + # Decides if breaking or printing statement when the variable is encountered var break_on_encounter: Bool - init(break_on_encounter: Bool) - do - trace_map = new HashMap[Frame, String] - self.break_on_encounter = break_on_encounter - end - # Adds the local alias for a variable and the frame bound to it fun add_frame_variable(frame: Frame, variable_name: String) do