1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2013 Lucas Bajolet <lucas.bajolet@gmail.com>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Debugging of a nit program using the NaiveInterpreter
21 intrude import naive_interpreter
23 redef class ToolContext
25 var opt_debugger_mode
: OptionBool = new OptionBool("Launches the target program with the debugger attached to it", "-d")
27 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")
32 self.option_context
.add_option
(self.opt_debugger_mode
)
33 self.option_context
.add_option
(self.opt_debugger_autorun
)
37 redef class ModelBuilder
38 # Execute the program from the entry point (Sys::main) of the `mainmodule`
39 # `arguments` are the command-line arguments in order
41 # 1. the AST is fully loaded.
42 # 2. the model is fully built.
43 # 3. the instructions are fully analysed.
44 fun run_debugger
(mainmodule
: MModule, arguments
: Array[String])
47 self.toolcontext
.info
("*** START INTERPRETING ***", 1)
49 var interpreter
= new Debugger(self, mainmodule
, arguments
)
51 init_naive_interpreter
(interpreter
, mainmodule
)
54 self.toolcontext
.info
("*** END INTERPRETING: {time1-time0} ***", 2)
57 fun run_debugger_autorun
(mainmodule
: MModule, arguments
: Array[String])
60 self.toolcontext
.info
("*** START INTERPRETING ***", 1)
62 var interpreter
= new Debugger(self, mainmodule
, arguments
)
63 interpreter
.autocontinue
= true
65 init_naive_interpreter
(interpreter
, mainmodule
)
68 self.toolcontext
.info
("*** END INTERPRETING: {time1-time0} ***", 2)
72 # The class extending `NaiveInterpreter` by adding debugging methods
74 super NaiveInterpreter
76 # Keeps the frame count in memory to find when to stop
77 # and launch the command prompt after a step out call
78 var step_stack_count
= 1
80 # Triggers a step over an instruction in a nit program
81 var stop_after_step_over_trigger
= true
83 # Triggers a step out of an instruction
84 var stop_after_step_out_trigger
= false
86 # Triggers a step in a instruction (enters a function
87 # if the instruction is a function call)
88 var step_in_trigger
= false
90 # HashMap containing the breakpoints bound to a file
91 var breakpoints
= new HashMap[String, HashSet[Breakpoint]]
93 # Contains the current file
96 # Aliases hashmap (maps an alias to a variable name)
97 var aliases
= new HashMap[String, String]
99 # Set containing all the traced variables and their related frame
100 private var traces
= new HashSet[TraceObject]
102 # Map containing all the positions for the positions of the arguments traced
104 private var fun_call_arguments_positions
= new HashMap[Int, TraceObject]
106 # Triggers the remapping of a trace object in the local context after a function call
107 var aftermath
= false
109 # Used to prevent the case when the body of the function called is empty
110 # If it is not, then, the remapping won't be happening
111 var frame_count_aftermath
= 1
113 # Auto continues the execution until the end or until an error is encountered
114 var autocontinue
= false
116 #######################################################################
117 ## Execution of statement function ##
118 #######################################################################
120 # Main loop, every call to a debug command is done here
121 redef fun stmt
(n
: nullable AExpr)
123 if n
== null then return
125 var frame
= self.frame
126 var old
= frame
.current_node
127 frame
.current_node
= n
129 if not self.autocontinue
then
130 if not n
isa ABlockExpr then
135 check_funcall_and_traced_args
(n
)
139 check_if_vars_are_traced
(n
)
144 frame
.current_node
= old
147 # Encpasulates the behaviour for step over/out
148 private fun steps_fun_call
(n
: AExpr)
150 if self.stop_after_step_over_trigger
then
151 if self.frames
.length
<= self.step_stack_count
then
152 n
.debug
("Execute stmt {n.to_s}")
153 while process_debug_command
(gets
) do end
155 else if self.stop_after_step_out_trigger
then
156 if frames
.length
< self.step_stack_count
then
157 n
.debug
("Execute stmt {n.to_s}")
158 while process_debug_command
(gets
) do end
160 else if step_in_trigger
then
161 n
.debug
("Execute stmt {n.to_s}")
162 while process_debug_command
(gets
) do end
166 # Checks if a breakpoint is encountered, and launches the debugging prompt if true
167 private fun breakpoint_check
(n
: AExpr)
169 var currFileNameSplit
= self.frame
.current_node
.location
.file
.filename
.to_s
.split_with
("/")
171 self.curr_file
= currFileNameSplit
[currFileNameSplit
.length-1
]
173 var breakpoint
= find_breakpoint
(curr_file
, n
.location
.line_start
)
175 if breakpoints
.keys
.has
(curr_file
) and breakpoint
!= null then
179 if not breakpoint
.is_valid
181 remove_breakpoint
(curr_file
, n
.location
.line_start
)
184 n
.debug
("Execute stmt {n.to_s}")
185 while process_debug_command
(gets
) do end
189 # Check if a variable of current expression is traced
190 # Then prints and/or breaks for command prompt
191 private fun check_if_vars_are_traced
(n
: AExpr)
193 var identifiers_in_instruction
= get_identifiers_in_current_instruction
(n
.location
.text
)
195 for i
in identifiers_in_instruction
do
196 var variable
= seek_variable
(i
, frame
)
197 for j
in self.traces
do
198 if j
.is_variable_traced_in_frame
(i
, frame
) then
199 n
.debug
("Traced variable {i} used")
200 if j
.break_on_encounter
then while process_debug_command
(gets
) do end
207 # Function remapping all the traced objects to match their name in the local context
208 private fun remap
(n
: AExpr)
210 if self.aftermath
then
212 # Trace every argument variable pre-specified
213 if self.frame_count_aftermath
< frames
.length
and fun_call_arguments_positions
.length
> 0 then
215 var ids_in_fun_def
= get_identifiers_in_current_instruction
(get_function_arguments
(frame
.mpropdef
.location
.text
))
217 for i
in fun_call_arguments_positions
.keys
do
218 self.fun_call_arguments_positions
[i
].add_frame_variable
(frame
, ids_in_fun_def
[i
])
222 self.aftermath
= false
226 # If the current instruction is a function call
227 # We analyse its signature and the position of traced arguments if the call
228 # For future remapping when inside the function
229 private fun check_funcall_and_traced_args
(n
: AExpr) do
230 # If we have a function call, we need to see if any of the arguments is traced (including the caller)
231 # if it is, next time we face an instruction, we'll trace the local version on the traced variable in the next frame
232 if n
isa ACallExpr then
233 self.aftermath
= true
234 self.frame_count_aftermath
= frames
.length
235 fun_call_arguments_positions
.clear
236 var fun_arguments
= get_identifiers_in_current_instruction
(get_function_arguments
(n
.location
.text
))
238 for i
in self.traces
do
239 for j
in [0 .. fun_arguments
.length
- 1] do
240 if i
.is_variable_traced_in_frame
(fun_arguments
[j
],frame
) then
241 fun_call_arguments_positions
[j
] = i
248 #######################################################################
249 ## Processing commands functions ##
250 #######################################################################
252 # Takes a user command as a parameter
254 # Returns a boolean value, representing whether or not to
255 # continue reading commands from the console input
256 fun process_debug_command
(command
:String): Bool
261 # Kills the current program
262 if command
== "kill" then
265 else if command
== "finish"
269 else if command
== "s"
273 else if command
== "n" then
275 # Continues execution until the end
276 else if command
== "c" then
279 var parts_of_command
= command
.split_with
(' ')
280 # Shows the value of a variable in the current frame
281 if parts_of_command
[0] == "p" or parts_of_command
[0] == "print" then
282 print_command
(parts_of_command
)
283 # Places a breakpoint on line x of file y
284 else if parts_of_command
[0] == "break" or parts_of_command
[0] == "b"
286 process_place_break_fun
(parts_of_command
)
287 # Places a temporary breakpoint on line x of file y
288 else if parts_of_command
[0] == "tbreak" and (parts_of_command
.length
== 2 or parts_of_command
.length
== 3)
290 process_place_tbreak_fun
(parts_of_command
)
291 # Removes a breakpoint on line x of file y
292 else if parts_of_command
[0] == "d" or parts_of_command
[0] == "delete" then
293 process_remove_break_fun
(parts_of_command
)
294 # Sets an alias for a variable
295 else if parts_of_command
.length
== 3 and parts_of_command
[1] == "as"
297 add_alias
(parts_of_command
[0], parts_of_command
[2])
298 # Modifies the value of a variable in the current frame
299 else if parts_of_command
.length
>= 3 and parts_of_command
[1] == "=" then
300 process_mod_function
(parts_of_command
)
301 # Traces the modifications on a variable
302 else if parts_of_command
.length
>= 2 and parts_of_command
[0] == "trace" then
303 process_trace_command
(parts_of_command
)
304 # Untraces the modifications on a variable
305 else if parts_of_command
.length
== 2 and parts_of_command
[0] == "untrace" then
306 process_untrace_command
(parts_of_command
)
307 # Lists all the commands available
315 #######################################################################
316 ## Processing specific command functions ##
317 #######################################################################
319 # Sets the flags to step-over an instruction in the current file
322 self.step_stack_count
= frames
.length
323 self.stop_after_step_over_trigger
= true
324 self.stop_after_step_out_trigger
= false
325 self.step_in_trigger
= false
329 #Sets the flags to step-out of a function
332 self.stop_after_step_over_trigger
= false
333 self.stop_after_step_out_trigger
= true
334 self.step_in_trigger
= false
335 self.step_stack_count
= frames
.length
339 # Sets the flags to step-in an instruction
342 self.step_in_trigger
= true
343 self.stop_after_step_over_trigger
= false
344 self.stop_after_step_out_trigger
= false
348 # Sets the flags to continue execution
349 fun continue_exec
: Bool
351 self.stop_after_step_over_trigger
= false
352 self.stop_after_step_out_trigger
= false
353 self.step_in_trigger
= false
357 # Prints the demanded variable in the command
359 # The name of the variable in in position 1 of the array 'parts_of_command'
360 fun print_command
(parts_of_command
: Array[String])
362 if parts_of_command
[1] == "*" then
363 var map_of_instances
= frame
.map
365 var keys
= map_of_instances
.iterator
367 print
"Variables collection : \n"
369 for instance
in map_of_instances
.keys
do
370 print
"Variable {instance.to_s}, Instance {map_of_instances[instance].to_s}"
373 print
"\nEnd of current instruction \n"
374 else if parts_of_command
[1] == "stack" then
375 print
self.stack_trace
376 else if parts_of_command
[1].has
('[') and parts_of_command
[1].has
(']') then
377 process_array_command
(parts_of_command
)
379 var instance
= seek_variable
(get_real_variable_name
(parts_of_command
[1]), frame
)
383 print_instance
(instance
)
385 print
"Cannot find variable {parts_of_command[1]}"
390 # Processes the input string to know where to put a breakpoint
391 fun process_place_break_fun
(parts_of_command
: Array[String])
393 var bp
= get_breakpoint_from_command
(parts_of_command
)
401 # Returns a breakpoint containing the informations stored in the command
402 fun get_breakpoint_from_command
(parts_of_command
: Array[String]): nullable Breakpoint
404 if parts_of_command
[1].is_numeric
then
405 return new Breakpoint(parts_of_command
[1].to_i
, curr_file
)
406 else if parts_of_command
.length
>= 3 and parts_of_command
[2].is_numeric
then
407 return new Breakpoint(parts_of_command
[2].to_i
, parts_of_command
[1])
413 # Processes the command of removing a breakpoint on specified line and file
414 fun process_remove_break_fun
(parts_of_command
: Array[String])
416 if parts_of_command
[1].is_numeric
then
417 remove_breakpoint
(self.curr_file
, parts_of_command
[1].to_i
)
418 else if parts_of_command
.length
>= 3 and parts_of_command
[2].is_numeric
then
419 remove_breakpoint
(parts_of_command
[1], parts_of_command
[2].to_i
)
425 # Processes an array print command
426 fun process_array_command
(parts_of_command
: Array[String])
428 var index_of_first_brace
= parts_of_command
[1].index_of
('[')
429 var variable_name
= get_real_variable_name
(parts_of_command
[1].substring
(0,index_of_first_brace
))
430 var braces
= parts_of_command
[1].substring_from
(index_of_first_brace
)
432 var indexes
= remove_braces
(braces
)
434 var index_array
= new Array[Array[Int]]
436 if indexes
!= null then
437 for index
in indexes
do
438 var temp_indexes_array
= process_index
(index
)
439 if temp_indexes_array
!= null then
440 index_array
.push
(temp_indexes_array
)
441 #print index_array.last
446 var instance
= seek_variable
(variable_name
, frame
)
448 if instance
!= null then
449 print_nested_collection
(instance
, index_array
, 0, variable_name
, "")
451 print
"Cannot find variable {variable_name}"
455 # Processes the modification function to modify a variable dynamically
457 # Command of type variable = value
458 fun process_mod_function
(parts_of_command
: Array[String])
460 parts_of_command
[0] = get_real_variable_name
(parts_of_command
[0])
461 var parts_of_variable
= parts_of_command
[0].split_with
(".")
463 if parts_of_variable
.length
> 1 then
464 var last_part
= parts_of_variable
.pop
465 var first_part
= parts_of_command
[0].substring
(0,parts_of_command
[0].length
- last_part
.length
- 1)
466 var papa
= seek_variable
(first_part
, frame
)
468 if papa
!= null and papa
isa MutableInstance then
469 var attribute
= get_attribute_in_mutable_instance
(papa
, last_part
)
471 if attribute
!= null then
472 modify_argument_of_complex_type
(papa
, attribute
, parts_of_command
[2])
476 var target
= seek_variable
(parts_of_variable
[0], frame
)
477 if target
!= null then
478 modify_in_frame
(target
, parts_of_command
[2])
483 # Processes the untrace variable command
485 # Command pattern : "untrace variable"
486 fun process_untrace_command
(parts_of_command
: Array[String])
488 var variable_name
= get_real_variable_name
(parts_of_command
[1])
489 if untrace_variable
(variable_name
) then
490 print
"Untraced variable {parts_of_command[1]}"
492 print
"{parts_of_command[1]} is not traced"
496 # Processes the trace variable command
498 # Command pattern : "trace variable [break/print]"
499 fun process_trace_command
(parts_of_command
: Array[String])
501 var variable_name
= get_real_variable_name
(parts_of_command
[1])
504 if seek_variable
(variable_name
, frame
) == null then
505 print
"Cannot find a variable called {parts_of_command[1]}"
509 if parts_of_command
.length
== 3 then
510 if parts_of_command
[2] == "break" then
519 trace_variable
(variable_name
, breaker
)
521 print
"Successfully tracing {parts_of_command[1]}"
524 #######################################################################
525 ## Trace Management functions ##
526 #######################################################################
528 # Effectively untraces the variable called *variable_name*
530 # Returns true if the variable exists, false otherwise
531 private fun untrace_variable
(variable_name
: String): Bool
533 var to_remove
: nullable TraceObject = null
534 for i
in self.traces
do
535 if i
.is_variable_traced_in_frame
(variable_name
, frame
) then
540 if to_remove
!= null then
541 self.traces
.remove
(to_remove
)
548 # Effectively traces the variable *variable_name* either in print or break mode depending on the value of breaker (break if true, print if false)
550 private fun trace_variable
(variable_name
: String, breaker
: Bool)
552 for i
in self.traces
do
553 if i
.is_variable_traced_in_frame
(variable_name
, frame
) then
554 print
"This variable is already traced"
559 var trace_object
: TraceObject
562 trace_object
= new TraceObject(true)
564 trace_object
= new TraceObject(false)
567 # We trace the current variable found for the current frame
568 trace_object
.add_frame_variable
(self.frame
, variable_name
)
570 var position_of_variable_in_arguments
= get_position_of_variable_in_arguments
(frame
, variable_name
)
572 # Start parsing the frames starting with the parent of the current one, until the highest
573 # When the variable traced is declared locally, the loop stops
574 for i
in [1 .. frames
.length-1
] do
576 # If the variable was reported to be an argument of the previous frame
577 if position_of_variable_in_arguments
!= -1 then
579 var local_name
= get_identifiers_in_current_instruction
(get_function_arguments
(frames
[i
].current_node
.location
.text
))[position_of_variable_in_arguments
]
581 position_of_variable_in_arguments
= get_position_of_variable_in_arguments
(frames
[i
], local_name
)
583 trace_object
.add_frame_variable
(frames
[i
], local_name
)
589 self.traces
.add
(trace_object
)
592 # If the variable *variable_name* is an argument of the function being executed in the frame *frame*
593 # The function returns its position in the arguments
594 # Else, it returns -1
595 private fun get_position_of_variable_in_arguments
(frame
: Frame, variable_name
: String): Int
597 var identifiers
= get_identifiers_in_current_instruction
(get_function_arguments
(frame
.mpropdef
.location
.text
))
598 for i
in [0 .. identifiers
.length-1
] do
599 # If the current traced variable is an argument of the current function, we trace its parent (at least)
600 if identifiers
[i
] == variable_name
then return i
605 # Gets all the identifiers of an instruction (uses the rules of Nit as of Mar 05 2013)
607 fun get_identifiers_in_current_instruction
(instruction
: AbstractString): Array[String]
609 var result_array
= new Array[String]
610 var instruction_buffer
= new Buffer
612 var trigger_char_escape
= false
613 var trigger_string_escape
= false
614 var trigger_concat_in_string
= false
616 for i
in instruction
do
617 if trigger_char_escape
then
618 if i
== '\'' then trigger_char_escape = false
619 else if trigger_string_escape then
621 trigger_concat_in_string = true
622 trigger_string_escape = false
623 else if i == '\
"' then trigger_string_escape = false
625 if i.is_alphanumeric or i == '_' then
626 instruction_buffer.add(i)
627 else if i == '.' then
628 if instruction_buffer.is_numeric or (instruction_buffer[0] >= 'A' and instruction_buffer[0] <= 'Z') then
629 instruction_buffer.clear
631 result_array.push(instruction_buffer.to_s)
632 instruction_buffer.add(i)
634 else if i == '\'' then
635 trigger_char_escape = true
636 else if i == '\"' then
637 trigger_string_escape = true
638 else if i == '}' then
639 trigger_concat_in_string = false
640 trigger_string_escape = true
642 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)
643 instruction_buffer.clear
648 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)
653 # Takes a function call or declaration and strips all but the arguments
655 fun get_function_arguments(function: AbstractString): String
658 var trigger_copy = false
661 if i == ')' then break
662 if trigger_copy then buf.add(i)
663 if i == '(' then trigger_copy = true
669 #######################################################################
670 ## Alias management functions ##
671 #######################################################################
673 # Adds a new alias to the tables
674 fun add_alias(var_represented: String, alias: String)
676 self.aliases[alias] = var_represented
679 # Gets the real name of a variable hidden by an alias
680 fun get_variable_name_by_alias(alias: String): nullable String
682 if self.aliases.keys.has(alias) then
683 return self.aliases[alias]
689 # Gets the variable named by name, whether it is an alias or not
690 fun get_real_variable_name(name: String): String
692 var explode_string = name.split_with(".")
693 var final_string = new Buffer
694 for i in explode_string do
695 var alias_resolved = get_variable_name_by_alias(i)
696 if alias_resolved != null then
697 final_string.append(get_real_variable_name(alias_resolved))
699 final_string.append(i)
701 final_string.append(".")
704 return final_string.substring(0,final_string.length-1).to_s
707 #######################################################################
708 ## Print functions ##
709 #######################################################################
711 # Prints an object instance and its attributes if it has some
713 # If it is a primitive type, its value is directly printed
714 fun print_instance(instance: Instance)
716 print "Printing innards of a variable"
718 if instance isa MutableInstance then
719 var attributes = instance.attributes
720 print "Object : {instance}"
722 for current_attribute in attributes.keys do
723 print "Attribute : {current_attribute.to_s} \nValeur : {attributes[current_attribute].to_s}"
726 print "Found variable {instance}"
729 print "Stopping printing innards of a variable"
732 # Prints the attributes demanded in a SequenceRead
733 # Used recursively to print nested collections
734 fun print_nested_collection(instance: Instance, indexes: Array[Array[Int]], depth: Int, variable_name: String, depth_string: String)
736 var collection: nullable SequenceRead[Object] = null
737 var real_collection_length: nullable Int = null
739 if instance isa MutableInstance then
740 real_collection_length = get_collection_instance_real_length(instance)
741 collection = get_primary_collection(instance)
744 if collection != null and real_collection_length != null then
745 for i in indexes[depth] do
746 if i >= 0 and i < real_collection_length then
747 if depth == indexes.length-1 then
748 print "{variable_name}{depth_string}[{i}] = {collection[i]}"
750 var item_i = collection[i]
752 if item_i isa MutableInstance then
753 print_nested_collection(item_i, indexes, depth+1, variable_name, depth_string+"[{i}]")
755 print "The item at {variable_name}{depth_string}[{i}] is not a collection"
760 print "Out of bounds exception : i = {i} and collection_length = {real_collection_length.to_s}"
764 else if i >= real_collection_length then
770 if collection == null then
771 print "Cannot find {variable_name}{depth_string}"
772 else if real_collection_length != null then
773 print "Cannot find attribute length in {instance}"
775 print "Unknown error."
781 #######################################################################
782 ## Variable Exploration functions ##
783 #######################################################################
785 # Seeks a variable from the current frame called 'variable_path
', can introspect complex objects using function get_variable_in_mutable_instance
786 private fun seek_variable(variable_path: String, frame: Frame): nullable Instance
788 var full_variable = variable_path.split_with(".")
790 var full_variable_iterator = full_variable.iterator
792 var first_instance = get_variable_in_frame(full_variable_iterator.item, frame)
794 if first_instance == null then return null
796 if full_variable.length <= 1 then return first_instance
798 full_variable_iterator.next
800 if not (first_instance isa MutableInstance and full_variable_iterator.is_ok) then return null
802 return get_variable_in_mutable_instance(first_instance, full_variable_iterator)
805 # Gets a variable 'variable_name
' contained in the frame 'frame
'
806 private fun get_variable_in_frame(variable_name: String, frame: Frame): nullable Instance
808 if variable_name == "self" then
809 if frame.arguments.length >= 1 then return frame.arguments.first
812 var map_of_instances = frame.map
814 for key in map_of_instances.keys do
815 if key.to_s == variable_name then
816 return map_of_instances[key]
823 # Gets an attribute 'attribute_name
' contained in variable 'variable
'
824 fun get_attribute_in_mutable_instance(variable: MutableInstance, attribute_name: String): nullable MAttribute
826 var map_of_attributes = variable.attributes
828 for key in map_of_attributes.keys do
829 if key.to_s.substring_from(1) == attribute_name then
837 # Recursive function, returns the variable described by 'total_chain
'
838 fun get_variable_in_mutable_instance(variable: MutableInstance, iterator: Iterator[String]): nullable Instance
840 var attribute = get_attribute_in_mutable_instance(variable, iterator.item)
842 if attribute == null then return null
846 if iterator.is_ok then
847 var new_variable = variable.attributes[attribute]
848 if new_variable isa MutableInstance then
849 return get_variable_in_mutable_instance(new_variable, iterator)
854 return variable.attributes[attribute]
858 #######################################################################
859 ## Array exploring functions ##
860 #######################################################################
862 # Gets the length of a collection
863 # Used by the debugger, else if we call Collection.length, it returns the capacity instead
864 fun get_collection_instance_real_length(collection: MutableInstance): nullable Int
866 var collection_length_attribute = get_attribute_in_mutable_instance(collection, "length")
868 var real_collection_length: nullable Int = null
870 if collection_length_attribute != null then
871 var primitive_length_instance = collection.attributes[collection_length_attribute]
872 if primitive_length_instance isa PrimitiveInstance[Int] then
873 return primitive_length_instance.val
880 # Processes the indexes of a print array call
881 # Returns an array containing all the indexes demanded
882 fun process_index(index_string: String): nullable Array[Int]
884 var from_end_index = index_string.index_of('.')
885 var to_start_index = index_string.last_index_of('.')
887 if from_end_index != -1 and to_start_index != -1 then
888 var index_from_string = index_string.substring(0,from_end_index)
889 var index_to_string = index_string.substring_from(to_start_index+1)
891 if index_from_string.is_numeric and index_to_string.is_numeric then
892 var result_array = new Array[Int]
894 var index_from = index_from_string.to_i
895 var index_to = index_to_string.to_i
897 for i in [index_from..index_to] do
904 if index_string.is_numeric
906 var result_array = new Array[Int]
908 result_array.push(index_string.to_i)
919 # Gets a collection in a MutableInstance
920 fun get_primary_collection(container: MutableInstance): nullable SequenceRead[Object]
922 var items_of_array = get_attribute_in_mutable_instance(container, "items")
923 if items_of_array != null then
924 var array = container.attributes[items_of_array]
926 if array isa PrimitiveInstance[Object] then
927 var sequenceRead_final = array.val
928 if sequenceRead_final isa SequenceRead[Object] then
929 return sequenceRead_final
937 # Removes the braces '[' ']' in a print array command
938 # Returns an array containing their content
939 fun remove_braces(braces: String): nullable Array[String]
941 var buffer = new Buffer
943 var result_array = new Array[String]
945 var number_of_opening_brackets = 0
946 var number_of_closing_brackets = 0
948 var last_was_opening_bracket = false
952 if last_was_opening_bracket then
956 number_of_opening_brackets += 1
957 last_was_opening_bracket = true
958 else if i == ']' then
959 if not last_was_opening_bracket then
963 result_array.push(buffer.to_s)
965 number_of_closing_brackets += 1
966 last_was_opening_bracket = false
967 else if i.is_numeric or i == '.' then
968 buffer.append(i.to_s)
969 else if not i == ' ' then
974 if number_of_opening_brackets != number_of_closing_brackets then
981 #######################################################################
982 ## Breakpoint placing functions ##
983 #######################################################################
985 # Places a breakpoint on line 'line_to_break
' for file 'file_to_break
'
986 fun place_breakpoint(breakpoint: Breakpoint)
988 if not self.breakpoints.keys.has(breakpoint.file) then
989 self.breakpoints[breakpoint.file] = new HashSet[Breakpoint]
991 if find_breakpoint(breakpoint.file, breakpoint.line) == null then
992 self.breakpoints[breakpoint.file].add(breakpoint)
993 print "Breakpoint added on line {breakpoint.line} for file {breakpoint.file}"
995 print "Breakpoint already present on line {breakpoint.line} for file {breakpoint.file}"
999 #Places a breakpoint that will trigger once and be destroyed afterwards
1000 fun process_place_tbreak_fun(parts_of_command: Array[String])
1002 var bp = get_breakpoint_from_command(parts_of_command)
1005 bp.set_max_breaks(1)
1006 place_breakpoint(bp)
1012 #######################################################################
1013 ## Breakpoint removing functions ##
1014 #######################################################################
1016 # Removes a breakpoint on line 'line_to_break
' for file 'file_to_break
'
1017 fun remove_breakpoint(file_to_break: String, line_to_break: Int)
1019 if self.breakpoints.keys.has(file_to_break) then
1020 var bp = find_breakpoint(file_to_break, line_to_break)
1023 self.breakpoints[file_to_break].remove(bp)
1024 print "Breakpoint removed on line {line_to_break} for file {file_to_break}"
1029 print "No breakpoint existing on line {line_to_break} for file {file_to_break}"
1032 #######################################################################
1033 ## Breakpoint searching functions ##
1034 #######################################################################
1036 # Finds a breakpoint for 'file
' and 'line
' in the class HashMap
1037 fun find_breakpoint(file: String, line: Int): nullable Breakpoint
1039 if self.breakpoints.keys.has(file)
1041 for i in self.breakpoints[file]
1053 #######################################################################
1054 ## Runtime modification functions ##
1055 #######################################################################
1057 # Modifies the value of a variable contained in a frame
1058 fun modify_in_frame(variable: Instance, value: String)
1060 var new_variable = get_variable_of_type_with_value(variable.mtype.to_s, value)
1061 if new_variable != null
1063 var keys = frame.map.keys
1066 if frame.map[key] == variable
1068 frame.map[key] = new_variable
1074 # Modifies the value of a variable contained in a MutableInstance
1075 fun modify_argument_of_complex_type(papa: MutableInstance, attribute: MAttribute, value: String)
1077 var final_variable = papa.attributes[attribute]
1078 var type_of_variable = final_variable.mtype.to_s
1079 var new_variable = get_variable_of_type_with_value(type_of_variable, value)
1080 if new_variable != null
1082 papa.attributes[attribute] = new_variable
1086 #######################################################################
1087 ## Variable generator functions ##
1088 #######################################################################
1090 # Returns a new variable of the type 'type_of_variable
' with a value of 'value
'
1091 fun get_variable_of_type_with_value(type_of_variable: String, value: String): nullable Instance
1093 if type_of_variable == "Int" then
1094 return get_int(value)
1095 else if type_of_variable == "Float" then
1096 return get_float(value)
1097 else if type_of_variable == "Bool" then
1098 return get_bool(value)
1099 else if type_of_variable == "Char" then
1100 return get_char(value)
1106 # Returns a new int instance with value 'value
'
1107 fun get_int(value: String): nullable Instance
1109 if value.is_numeric then
1110 return int_instance(value.to_i)
1116 # Returns a new float instance with value 'value
'
1117 fun get_float(value:String): nullable Instance
1119 if value.is_numeric then
1120 return float_instance(value.to_f)
1126 # Returns a new char instance with value 'value
'
1127 fun get_char(value: String): nullable Instance
1129 if value.length >= 1 then
1130 return char_instance(value[0])
1136 # Returns a new bool instance with value 'value
'
1137 fun get_bool(value: String): nullable Instance
1139 if value.to_lower == "true" then
1140 return self.true_instance
1141 else if value.to_lower == "false" then
1142 return self.false_instance
1144 print "Invalid value, a boolean must be set at \"true\" or \"false\""
1149 #######################################################################
1150 ## Command listing function ##
1151 #######################################################################
1153 # Lists the commands available when using the debugger
1156 print "\nCommand not recognized\n"
1157 print "Commands accepted : \n"
1158 print "[break/b] line : Adds a breakpoint on line *line_nb* of the current file\n"
1159 print "[break/b] file_name line_nb : Adds a breakpoint on line *line_nb* of file *file_name* \n"
1160 print "[p/print] variable : [p/print] * shows the status of all the variables\n"
1161 print "[p/print] variable[i] : Prints the value of the variable contained at position *i* in SequenceRead collection *variable*\n"
1162 print "[p/print] variable[i..j]: Prints the value of all the variables contained between positions *i* and *j* in SequenceRead collection *variable*\n"
1163 print "[p/print] stack: Prints a stack trace at current instruction\n"
1164 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"
1165 print "s : steps in on the current function\n"
1166 print "n : steps-over the current instruction\n"
1167 print "finish : steps out of the current function\n"
1168 print "variable as alias : Adds an alias called *alias* for the variable *variable*"
1169 print "An alias can reference another alias\n"
1170 print "variable = value : Sets the value of *variable* to *value*\n"
1171 print "[d/delete] line_nb : Removes a breakpoint on line *line_nb* of the current file \n"
1172 print "[d/delete] file_name line_nb : Removes a breakpoint on line *line_nb* of file *file_name* \n"
1173 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."
1174 print "untrace variable_name : Removes the trace on the variable you chose to trace earlier in the program"
1175 print "kill : kills the current program (Exits with an error and stack trace)\n"
1180 # Traces the modifications of an object linked to a certain frame
1181 private class TraceObject
1183 # Map of the local names bound to a frame
1184 var trace_map: HashMap[Frame, String]
1185 # Decides if breaking or printing statement when the variable is encountered
1186 var break_on_encounter: Bool
1188 init(break_on_encounter: Bool)
1190 trace_map = new HashMap[Frame, String]
1191 self.break_on_encounter = break_on_encounter
1194 # Adds the local alias for a variable and the frame bound to it
1195 fun add_frame_variable(frame: Frame, variable_name: String)
1197 self.trace_map[frame] = variable_name
1200 # Checks if the prompted variable is traced in the specified frame
1201 fun is_variable_traced_in_frame(variable_name: String, frame: Frame): Bool
1203 if self.trace_map.has_key(frame) then
1204 if self.trace_map[frame] == variable_name then
1216 # Breaks automatically when encountering an error
1217 # Permits the injunction of commands before crashing
1218 redef private fun fatal(v: NaiveInterpreter, message: String)
1220 if v isa Debugger then
1221 print "An error was encountered, the program will stop now."
1223 while v.process_debug_command(gets) do end