+ #######################################################################
+ ## Array exploring functions ##
+ #######################################################################
+
+ # Gets the length of a collection
+ # Used by the debugger, else if we call Collection.length, it returns the capacity instead
+ fun get_collection_instance_real_length(collection: MutableInstance): nullable Int
+ 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
+ return primitive_length_instance.val
+ end
+ end
+
+ return null
+ end
+
+ # Processes the indexes of a print array call
+ # Returns an array containing all the indexes demanded
+ fun process_index(index_string: String): nullable Array[Int]
+ do
+ 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)
+ var index_to_string = index_string.substring_from(to_start_index+1)
+
+ if index_from_string.is_numeric and index_to_string.is_numeric then
+ var result_array = new Array[Int]
+
+ var index_from = index_from_string.to_i
+ var index_to = index_to_string.to_i
+
+ for i in [index_from..index_to] do
+ result_array.push(i)
+ end
+
+ return result_array
+ end
+ else
+ if index_string.is_numeric
+ then
+ var result_array = new Array[Int]
+
+ result_array.push(index_string.to_i)
+
+ return result_array
+ else
+ return null
+ end
+ end
+
+ return null
+ end
+
+ # Gets a collection in a MutableInstance
+ fun get_primary_collection(container: MutableInstance): nullable SequenceRead[Object]
+ do
+ var items_of_array = get_attribute_in_mutable_instance(container, "items")
+ if items_of_array != null then
+ var array = container.attributes[items_of_array]
+
+ if array isa PrimitiveInstance[Object] then
+ var sequenceRead_final = array.val
+ if sequenceRead_final isa SequenceRead[Object] then
+ return sequenceRead_final
+ end
+ end
+ end
+
+ return null
+ end
+
+ # Removes the braces '[' ']' in a print array command
+ # Returns an array containing their content
+ fun remove_braces(braces: String): nullable Array[String]
+ do
+ var buffer = new FlatBuffer
+
+ var result_array = new Array[String]
+
+ var number_of_opening_brackets = 0
+ var number_of_closing_brackets = 0
+
+ var last_was_opening_bracket = false
+
+ for i in braces.chars do
+ if i == '[' then
+ if last_was_opening_bracket then
+ return null
+ end
+
+ number_of_opening_brackets += 1
+ last_was_opening_bracket = true
+ else if i == ']' then
+ if not last_was_opening_bracket then
+ return null
+ end
+
+ result_array.push(buffer.to_s)
+ buffer.clear
+ number_of_closing_brackets += 1
+ last_was_opening_bracket = false
+ else if i.is_numeric or i == '.' then
+ buffer.append(i.to_s)
+ else if not i == ' ' then
+ return null
+ end
+ end
+
+ if number_of_opening_brackets != number_of_closing_brackets then
+ return null
+ end
+
+ return result_array
+ 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)
+ 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
+
+ #######################################################################
+ ## Runtime modification functions ##
+ #######################################################################
+
+ # Modifies the value of a variable contained in a frame
+ fun modify_in_frame(variable: Instance, value: String)
+ do
+ var new_variable = get_variable_of_type_with_value(variable.mtype.to_s, value)
+ if new_variable != null
+ then
+ var keys = frame.map.keys
+ for key in keys
+ do
+ if frame.map[key] == variable
+ then
+ frame.map[key] = new_variable
+ end
+ end
+ end
+ end
+
+ # Modifies the value of a variable contained in a MutableInstance
+ fun modify_argument_of_complex_type(papa: MutableInstance, attribute: MAttribute, value: String)
+ do
+ var final_variable = papa.attributes[attribute]
+ var type_of_variable = final_variable.mtype.to_s
+ var new_variable = get_variable_of_type_with_value(type_of_variable, value)
+ if new_variable != null
+ then
+ papa.attributes[attribute] = new_variable
+ end
+ end
+
+ #######################################################################
+ ## Variable generator functions ##
+ #######################################################################
+
+ # Returns a new variable of the type 'type_of_variable' with a value of 'value'
+ fun get_variable_of_type_with_value(type_of_variable: String, value: String): nullable Instance
+ do
+ if type_of_variable == "Int" then
+ return get_int(value)
+ else if type_of_variable == "Float" then
+ return get_float(value)
+ else if type_of_variable == "Bool" then
+ return get_bool(value)
+ else if type_of_variable == "Char" then
+ return get_char(value)
+ end
+
+ return null
+ end
+
+ # Returns a new int instance with value 'value'
+ fun get_int(value: String): nullable Instance
+ do
+ if value.is_numeric then
+ return int_instance(value.to_i)
+ else
+ return null
+ end
+ end
+
+ # Returns a new float instance with value 'value'
+ fun get_float(value:String): nullable Instance
+ do
+ if value.is_numeric then
+ return float_instance(value.to_f)
+ else
+ return null
+ end
+ end
+
+ # Returns a new char instance with value 'value'
+ fun get_char(value: String): nullable Instance
+ do
+ if value.length >= 1 then
+ return char_instance(value.chars[0])
+ else
+ return null
+ end
+ end
+
+ # Returns a new bool instance with value 'value'
+ fun get_bool(value: String): nullable Instance
+ do
+ if value.to_lower == "true" then
+ return self.true_instance
+ else if value.to_lower == "false" then
+ return self.false_instance
+ else
+ print "Invalid value, a boolean must be set at \"true\" or \"false\""
+ return null
+ end
+ end
+
+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
+private class TraceObject
+
+ # Map of the local names bound to a frame
+ var trace_map: 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
+ self.trace_map[frame] = variable_name
+ end
+
+ # Checks if the prompted variable is traced in the specified frame
+ fun is_variable_traced_in_frame(variable_name: String, frame: Frame): Bool
+ do
+ if self.trace_map.has_key(frame) then
+ if self.trace_map[frame] == variable_name then
+ return true
+ end
+ end
+
+ return false
+ end
+
+end
+
+redef class ANode
+
+ # Breaks automatically when encountering an error
+ # Permits the injunction of commands before crashing
+ redef private fun fatal(v: NaiveInterpreter, message: String)
+ do
+ if v isa Debugger then
+ print "An error was encountered, the program will stop now."
+ self.debug(message)
+ while v.process_debug_command(gets) do end
+ end
+
+ super
+ end