import breakpoint
intrude import naive_interpreter
-intrude import nitx
-
-redef class NitIndex
-
- # New constructor to use the pre-calculated model when interpreting a module
- init with_infos(model: Model, mbuilder: ModelBuilder, mmodule: MModule, toolctx: ToolContext) do
-
- self.model = model
- self.mbuilder = mbuilder
+import nitx
+intrude import local_var_init
+intrude import scope
+intrude import toolcontext
+
+redef class Model
+ # Cleans the model to remove a module and what it defines when semantic analysis fails on injected code
+ private fun try_remove_module(m: MModule): Bool
+ do
+ var index = -1
+ for i in [0 .. mmodules.length[ do
+ if mmodules[i] == m then
+ index = i
+ break
+ end
+ end
+ if index == -1 then return false
+ var mmodule = mmodules[index]
+ mmodules.remove_at(index)
+ for classdef in mmodule.mclassdefs do
+ var mclass = classdef.mclass
+ for i in [0 .. mclass.mclassdefs.length[ do
+ if mclass.mclassdefs[i] == classdef then
+ index = i
+ break
+ end
+ end
+ mclass.mclassdefs.remove_at(index)
+ var propdefs = classdef.mpropdefs
+ for propdef in propdefs do
+ var prop = propdef.mproperty
+ for i in [0..prop.mpropdefs.length[ do
+ if prop.mpropdefs[i] == propdef then
+ index = i
+ break
+ end
+ end
+ prop.mpropdefs.remove_at(index)
+ end
+ end
+ return true
+ end
+end
- self.mainmodule = mmodule
- self.toolcontext = toolctx
- self.arguments = toolctx.option_context.rest
+redef class ScopeVisitor
- renderer = new PagerMatchesRenderer(self)
+ redef init(toolcontext)
+ do
+ super
+ if toolcontext.dbg != null then
+ var localvars = toolcontext.dbg.frame.map
+ for i in localvars.keys do
+ scopes.first.variables[i.to_s] = i
+ end
+ end
end
- redef fun search(s)
+end
+
+redef class LocalVarInitVisitor
+ redef fun mark_is_unset(node: AExpr, variable: nullable Variable)
do
- if s == ":q" then return
super
+ if toolcontext.dbg != null then
+ var varname = variable.to_s
+ var instmap = toolcontext.dbg.frame.map
+ for i in instmap.keys do
+ if i.to_s == varname then
+ mark_is_set(node, variable)
+ end
+ end
+ end
end
end
redef class ToolContext
+ private var dbg: nullable Debugger = null
+
+ private var had_error: Bool = false
+
+ redef fun check_errors
+ do
+ if dbg == null then
+ super
+ else
+ if messages.length > 0 then
+ message_sorter.sort(messages)
+
+ for m in messages do
+ if m.text.search("Warning") == null then had_error = true
+ sys.stderr.write("{m.to_color_string}\n")
+ end
+ end
+
+ messages.clear
+ end
+ end
+
# -d
var opt_debugger_mode: OptionBool = new OptionBool("Launches the target program with the debugger attached to it", "-d")
# -c
var old = frame.current_node
frame.current_node = n
+ if sys.stdin.poll_in then process_debug_command(gets)
+
if not self.autocontinue then
if not n isa ABlockExpr then
steps_fun_call(n)
frame.current_node = old
end
+ # Does the same as an usual send, except it will modify the call chain on the first call when injecting code at Runtime using the debugger.
+ # Instead of creating a pristine Frame, it will copy the actual values of the frame, and re-inject them after execution in the current context.
+ fun rt_send(mproperty: MMethod, args: Array[Instance]): nullable Instance
+ do
+ var recv = args.first
+ var mtype = recv.mtype
+ var ret = send_commons(mproperty, args, mtype)
+ if ret != null then return ret
+ var propdef = mproperty.lookup_first_definition(self.mainmodule, mtype)
+ return self.rt_call(propdef, args)
+ 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}")
+ end
+ 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)
+ else
+ fatal("Fatal Error: method {mpropdef} not found in the AST")
+ abort
+ end
+ end
+
+ # Evaluates dynamically a snippet of Nit code
+ # `nit_code` : Nit code to be executed
+ fun eval(nit_code: String)
+ do
+ var local_toolctx = modelbuilder.toolcontext
+ local_toolctx.dbg = self
+ var e = local_toolctx.parse_something(nit_code)
+ if e isa ABlockExpr then
+ nit_code = "module rt_module\n" + nit_code
+ e = local_toolctx.parse_something(nit_code)
+ end
+ if e isa AExpr then
+ nit_code = "module rt_module\nprint " + nit_code
+ e = local_toolctx.parse_something(nit_code)
+ end
+ if e isa AModule then
+ local_toolctx.had_error = false
+ modelbuilder.load_rt_module(self.mainmodule, e, "rt_module")
+ local_toolctx.run_phases([e])
+ if local_toolctx.had_error then
+ modelbuilder.model.try_remove_module(e.mmodule.as(not null))
+ local_toolctx.dbg = null
+ return
+ end
+ 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"
+ abort
+ end
+ var mobj = new MutableInstance(sys_type)
+ init_instance(mobj)
+ var initprop = mmod.try_get_primitive_method("init", sys_type.mclass)
+ if initprop != null then
+ self.send(initprop, [mobj])
+ end
+ var mainprop = mmod.try_get_primitive_method("main", sys_type.mclass)
+ if mainprop != null then
+ self.rt_send(mainprop, [mobj])
+ end
+ else
+ print "Error while loading_rt_module"
+ end
+ else
+ print "Error when parsing, e = {e.class_name}"
+ end
+ local_toolctx.dbg = null
+ end
+
# Encpasulates the behaviour for step over/out
private fun steps_fun_call(n: AExpr)
do
if self.stop_after_step_over_trigger then
if self.frames.length <= self.step_stack_count then
n.debug("Execute stmt {n.to_s}")
- while process_debug_command(gets) do end
+ while read_cmd do end
end
else if self.stop_after_step_out_trigger then
if frames.length < self.step_stack_count then
n.debug("Execute stmt {n.to_s}")
- while process_debug_command(gets) do end
+ while read_cmd do end
end
else if step_in_trigger then
n.debug("Execute stmt {n.to_s}")
- while process_debug_command(gets) do end
+ while read_cmd do end
end
end
end
n.debug("Execute stmt {n.to_s}")
- while process_debug_command(gets) do end
+ while read_cmd do end
end
end
for j in self.traces do
if j.is_variable_traced_in_frame(i, frame) then
n.debug("Traced variable {i} used")
- if j.break_on_encounter then while process_debug_command(gets) do end
+ if j.break_on_encounter then while read_cmd do end
break
end
end
## Processing commands functions ##
#######################################################################
+ fun read_cmd: Bool
+ do
+ printn "> "
+ return process_debug_command(gets)
+ end
+
# Takes a user command as a parameter
#
# Returns a boolean value, representing whether or not to
# continue reading commands from the console input
fun process_debug_command(command:String): Bool
do
- # For lisibility
- print "\n"
-
- # Kills the current program
- if command == "kill" then
- abort
# Step-out command
- else if command == "finish"
+ if command == "finish"
then
return step_out
# Step-in command
return step_over
# Opens a new NitIndex prompt on current model
else if command == "nitx" then
- new NitIndex.with_infos(modelbuilder.model, modelbuilder, self.mainmodule, self.modelbuilder.toolcontext).prompt
+ new NitIndex.with_infos(modelbuilder, self.mainmodule).prompt
return true
# Continues execution until the end
else if command == "c" then
return continue_exec
+ else if command == "nit" then
+ printn "$~> "
+ command = gets
+ var nit_buf = new FlatBuffer
+ while not command == ":q" do
+ nit_buf.append(command)
+ nit_buf.append("\n")
+ printn "$~> "
+ command = gets
+ end
+ step_in
+ eval(nit_buf.to_s)
+ else if command == "quit" then
+ exit(0)
+ else if command == "abort" then
+ print stack_trace
+ exit(0)
else
var parts_of_command = command.split_with(' ')
# Shows the value of a variable in the current frame
# 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)
- # Lists all the commands available
else
- list_commands
+ print "Unknown command \"{command}\""
end
end
return true
var keys = map_of_instances.iterator
- print "Variables collection : \n"
+ var self_var = seek_variable("self", frame)
+ print "self: {self_var.to_s}"
for instance in map_of_instances.keys do
- print "Variable {instance.to_s}, Instance {map_of_instances[instance].to_s}"
+ print "{instance.to_s}: {map_of_instances[instance].to_s}"
end
-
- print "\nEnd of current instruction \n"
else if parts_of_command[1] == "stack" then
print self.stack_trace
- else if parts_of_command[1].has('[') and parts_of_command[1].has(']') then
+ else if parts_of_command[1].chars.has('[') and parts_of_command[1].chars.has(']') then
process_array_command(parts_of_command)
else
var instance = seek_variable(get_real_variable_name(parts_of_command[1]), frame)
var bp = get_breakpoint_from_command(parts_of_command)
if bp != null then
place_breakpoint(bp)
- else
- list_commands
end
end
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
# Processes an array print command
fun process_array_command(parts_of_command: Array[String])
do
- var index_of_first_brace = parts_of_command[1].index_of('[')
+ 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)
# Gets all the identifiers of an instruction (uses the rules of Nit as of Mar 05 2013)
#
- fun get_identifiers_in_current_instruction(instruction: AbstractString): Array[String]
+ fun get_identifiers_in_current_instruction(instruction: Text): Array[String]
do
var result_array = new Array[String]
- var instruction_buffer = new Buffer
+ var instruction_buffer = new FlatBuffer
var trigger_char_escape = false
var trigger_string_escape = false
var trigger_concat_in_string = false
- for i in instruction do
+ 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.is_alphanumeric or i == '_' then
instruction_buffer.add(i)
else if i == '.' then
- if instruction_buffer.is_numeric or (instruction_buffer[0] >= 'A' and instruction_buffer[0] <= 'Z') then
+ if instruction_buffer.is_numeric or (instruction_buffer.chars[0] >= 'A' and instruction_buffer.chars[0] <= 'Z') then
instruction_buffer.clear
else
result_array.push(instruction_buffer.to_s)
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[0] >= 'A' and instruction_buffer[0] <= 'Z') then result_array.push(instruction_buffer.to_s)
+ 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)
instruction_buffer.clear
end
end
end
- if instruction_buffer.length > 0 and not instruction_buffer.is_numeric and not (instruction_buffer[0] >= 'A' and instruction_buffer[0] <= 'Z') then result_array.push(instruction_buffer.to_s)
+ 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)
return result_array
end
# Takes a function call or declaration and strips all but the arguments
#
- fun get_function_arguments(function: AbstractString): String
+ fun get_function_arguments(function: Text): String
do
- var buf = new Buffer
+ var buf = new FlatBuffer
var trigger_copy = false
- for i in function do
+ for i in function.chars do
if i == ')' then break
if trigger_copy then buf.add(i)
if i == '(' then trigger_copy = true
fun get_real_variable_name(name: String): String
do
var explode_string = name.split_with(".")
- var final_string = new Buffer
+ var final_string = new FlatBuffer
for i in explode_string do
var alias_resolved = get_variable_name_by_alias(i)
if alias_resolved != null then
# If it is a primitive type, its value is directly printed
fun print_instance(instance: Instance)
do
- print "Printing innards of a variable"
-
if instance isa MutableInstance then
- var attributes = instance.attributes
- print "Object : {instance}"
+ print "\{"
+ print "\ttype : {instance},"
- for current_attribute in attributes.keys do
- print "Attribute : {current_attribute.to_s} \nValeur : {attributes[current_attribute].to_s}"
- end
+ printn("\t")
+
+ print instance.attributes.join(",\n\t"," : ")
+
+ print "\}"
else
- print "Found variable {instance}"
+ print "{instance}"
end
-
- print "Stopping printing innards of a variable"
end
# Prints the attributes demanded in a SequenceRead
# Returns an array containing all the indexes demanded
fun process_index(index_string: String): nullable Array[Int]
do
- var from_end_index = index_string.index_of('.')
- var to_start_index = index_string.last_index_of('.')
+ var from_end_index = index_string.chars.index_of('.')
+ var to_start_index = index_string.chars.last_index_of('.')
if from_end_index != -1 and to_start_index != -1 then
var index_from_string = index_string.substring(0,from_end_index)
# Returns an array containing their content
fun remove_braces(braces: String): nullable Array[String]
do
- var buffer = new Buffer
+ var buffer = new FlatBuffer
var result_array = new Array[String]
var last_was_opening_bracket = false
- for i in braces do
+ for i in braces.chars do
if i == '[' then
if last_was_opening_bracket then
return null
then
bp.set_max_breaks(1)
place_breakpoint(bp)
- else
- list_commands
end
end
fun get_char(value: String): nullable Instance
do
if value.length >= 1 then
- return char_instance(value[0])
+ return char_instance(value.chars[0])
else
return null
end
end
end
- #######################################################################
- ## Command listing function ##
- #######################################################################
+end
- # 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 "[p/print] variable[i] : Prints the value of the variable contained at position *i* in SequenceRead collection *variable*\n"
- print "[p/print] variable[i..j]: Prints the value of all the variables contained between positions *i* and *j* in SequenceRead collection *variable*\n"
- print "[p/print] stack: Prints a stack trace at current instruction\n"
- print "Note : The arrays can be multi-dimensional (Ex : variable[i..j][k] will print all the values at position *k* of all the SequenceRead collections contained between positions *i* and *j* in SequenceRead collection *variable*)\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 "variable = value : Sets the value of *variable* to *value*\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 "trace variable_name [break/print] : Traces the uses of the variable you chose to trace by printing the statement it appears in or by breaking on each use."
- print "untrace variable_name : Removes the trace on the variable you chose to trace earlier in the program"
- print "kill : kills the current program (Exits with an error and stack trace)\n"
- end
+redef class AMethPropdef
+
+ # Same as call except it will copy local variables of the parent frame to the frame defined in this call.
+ # Not supposed to be used by anyone else than the Debugger.
+ private fun rt_call(v: Debugger, mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
+ do
+ var f = new Frame(self, self.mpropdef.as(not null), args)
+ var curr_instances = v.frame.map
+ for i in curr_instances.keys do
+ f.map[i] = curr_instances[i]
+ end
+ call_commons(v,mpropdef,args,f)
+ var currFra = v.frames.shift
+ for i in curr_instances.keys do
+ if currFra.map.keys.has(i) then
+ curr_instances[i] = currFra.map[i]
+ end
+ end
+ if v.returnmark == f then
+ v.returnmark = null
+ var res = v.escapevalue
+ v.escapevalue = null
+ return res
+ end
+ return null
+ end
end
# Traces the modifications of an object linked to a certain frame