nitdbg: Added function to process commands
[nit.git] / src / debugger.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2013 Lucas Bajolet <lucas.bajolet@gmail.com>
4 #
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
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
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.
16
17 # Debugging of a nit program using the NaiveInterpreter
18 module debugger
19
20 intrude import naive_interpreter
21
22 redef class ToolContext
23 # -d
24 var opt_debugger_mode: OptionBool = new OptionBool("Launches the target program with the debugger attached to it", "-d")
25
26 redef init
27 do
28 super
29 self.option_context.add_option(self.opt_debugger_mode)
30 end
31 end
32
33 redef class ModelBuilder
34 # Execute the program from the entry point (Sys::main) of the `mainmodule'
35 # `arguments' are the command-line arguments in order
36 # REQUIRE that:
37 # 1. the AST is fully loaded.
38 # 2. the model is fully built.
39 # 3. the instructions are fully analysed.
40 fun run_debugger(mainmodule: MModule, arguments: Array[String])
41 do
42 var time0 = get_time
43 self.toolcontext.info("*** START INTERPRETING ***", 1)
44
45 var interpreter = new Debugger(self, mainmodule, arguments)
46
47 init_naive_interpreter(interpreter, mainmodule)
48
49 var time1 = get_time
50 self.toolcontext.info("*** END INTERPRETING: {time1-time0} ***", 2)
51 end
52 end
53
54 # The class extending NaiveInterpreter by adding debugging methods
55 class Debugger
56 super NaiveInterpreter
57
58 # Main loop, every call to a debug command is done here
59 redef fun stmt(n: nullable AExpr)
60 do
61 if n == null then return
62
63 var frame = self.frame
64 var old = frame.current_node
65 frame.current_node = n
66 n.stmt(self)
67 frame.current_node = old
68 end
69
70 #######################################################################
71 ## Processing commands functions ##
72 #######################################################################
73
74 # Takes a user command as a parameter
75 #
76 # Returns a boolean value, representing whether or not to
77 # continue reading commands from the console input
78 fun process_debug_command(command:String): Bool
79 do
80 # For lisibility
81 print "\n"
82
83 # Kills the current program
84 if command == "kill" then
85 abort
86 else
87 var parts_of_command = command.split_with(' ')
88 # Shows the value of a variable in the current frame
89 if parts_of_command[0] == "p" or parts_of_command[0] == "print" then
90 print_command(parts_of_command)
91 end
92 end
93 return true
94 end
95
96 # Prints the demanded variable in the command
97 #
98 # The name of the variable in in position 1 of the array 'parts_of_command'
99 fun print_command(parts_of_command: Array[String])
100 do
101 if parts_of_command[1] == "*" then
102 var map_of_instances = frame.map
103
104 var keys = map_of_instances.iterator
105
106 print "Variables collection : \n"
107
108 for instance in map_of_instances.keys do
109 print "Variable {instance.to_s}, Instance {map_of_instances[instance].to_s}"
110 end
111
112 print "\nEnd of current instruction \n"
113 else
114 var instance = seek_variable(parts_of_command[1], frame)
115
116 if instance != null
117 then
118 print_instance(instance)
119 end
120 end
121 end
122
123 #######################################################################
124 ## Print functions ##
125 #######################################################################
126
127 # Prints an object instance and its attributes if it has some
128 #
129 # If it is a primitive type, its value is directly printed
130 fun print_instance(instance: Instance)
131 do
132 if instance isa MutableInstance then
133 var attributes = instance.attributes
134 print "Object : {instance}"
135
136 for current_attribute in attributes.keys do
137 print "Attribute : {current_attribute.to_s} \nValeur : {attributes[current_attribute].to_s}"
138 end
139 else
140 print "Found variable {instance}"
141 end
142 end
143
144 #######################################################################
145 ## Variable Exploration functions ##
146 #######################################################################
147
148 # Seeks a variable from the current frame called 'variable_path', can introspect complex objects using function get_variable_in_mutable_instance
149 private fun seek_variable(variable_path: String, frame: Frame): nullable Instance
150 do
151 var full_variable = variable_path.split_with(".")
152
153 var full_variable_iterator = full_variable.iterator
154
155 var first_instance = get_variable_in_frame(full_variable_iterator.item, frame)
156
157 if first_instance == null then return null
158
159 if full_variable.length <= 1 then return first_instance
160
161 full_variable_iterator.next
162
163 if not (first_instance isa MutableInstance and full_variable_iterator.is_ok) then return null
164
165 return get_variable_in_mutable_instance(first_instance, full_variable_iterator)
166 end
167
168 # Gets a variable 'variable_name' contained in the frame 'frame'
169 private fun get_variable_in_frame(variable_name: String, frame: Frame): nullable Instance
170 do
171 if variable_name == "self" then
172 if frame.arguments.length >= 1 then return frame.arguments.first
173 end
174
175 var map_of_instances = frame.map
176
177 for key in map_of_instances.keys do
178 if key.to_s == variable_name then
179 return map_of_instances[key]
180 end
181 end
182
183 return null
184 end
185
186 # Gets an attribute 'attribute_name' contained in variable 'variable'
187 fun get_attribute_in_mutable_instance(variable: MutableInstance, attribute_name: String): nullable MAttribute
188 do
189 var map_of_attributes = variable.attributes
190
191 for key in map_of_attributes.keys do
192 if key.to_s.substring_from(1) == attribute_name then
193 return key
194 end
195 end
196
197 return null
198 end
199
200 # Recursive function, returns the variable described by 'total_chain'
201 fun get_variable_in_mutable_instance(variable: MutableInstance, iterator: Iterator[String]): nullable Instance
202 do
203 var attribute = get_attribute_in_mutable_instance(variable, iterator.item)
204
205 if attribute == null then return null
206
207 iterator.next
208
209 if iterator.is_ok then
210 var new_variable = variable.attributes[attribute]
211 if new_variable isa MutableInstance then
212 return get_variable_in_mutable_instance(new_variable, iterator)
213 else
214 return null
215 end
216 else
217 return variable.attributes[attribute]
218 end
219 end
220
221 end