stdlib/strings: Moved Buffer to FlatBuffer, Buffer is now abstract.
[nit.git] / src / network_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 # Network debugger for a nit program, based on the original debugger
18 # Replaces access to stdin/stdout to send data on the network to the client concerned
19 module network_debugger
20
21 import socket
22 intrude import debugger
23
24 redef class ToolContext
25
26 var opt_debug_port: OptionInt = new OptionInt("Sets the debug port (Defaults to 22125) - Must be contained between 0 and 65535", 22125, "--port")
27
28 redef init
29 do
30 super
31 self.option_context.add_option(self.opt_debug_port)
32 end
33
34 end
35
36 redef class ModelBuilder
37 fun run_debugger_network_mode(mainmodule: MModule, arguments: Array[String], port: Int)
38 do
39 var time0 = get_time
40 self.toolcontext.info("*** START INTERPRETING ***", 1)
41
42 var interpreter = new NetworkDebugger(self, mainmodule, arguments, port)
43
44 init_naive_interpreter(interpreter, mainmodule)
45
46 var time1 = get_time
47 self.toolcontext.info("*** END INTERPRETING: {time1-time0} ***", 2)
48
49 interpreter.disconnect_routine
50
51 interpreter.ns.close
52 interpreter.socket.close
53 end
54 end
55
56 # Extends the debugger by adding new network capabilities for remote debugging
57 class NetworkDebugger
58 super Debugger
59
60 # Represents the connexion with a particular client (Actually the only one accepted at the moment)
61 private var ns: Socket
62
63 # Socket for the server, supposed to listen for incoming request from a client
64 private var socket: Socket
65
66 # Initializes the debugger, waits for a client to connect
67 # Then starts debugging as usual
68 init(modelbuilder: ModelBuilder, mainmodule: MModule, arguments: Array[String], port: Int)
69 do
70 print "Listening on port {port}"
71
72 socket = new Socket.stream_with_port(port)
73
74 socket.bind
75 socket.listen(1)
76
77 ns = socket.accept
78
79 socket.close
80
81 print "Client connected"
82
83 stdin.connection = ns
84
85 if stdout isa Stdout then
86 (stdout.as(Stdout)).connection = ns
87 else
88 ns.close
89 abort
90 end
91
92 super
93 end
94
95 # Provokes the disconnection of the client
96 # Used when debugging is over
97 fun disconnect_routine
98 do
99 print "DBG DONE WORK ON SELF"
100
101 var cliOverResp = ""
102
103 while cliOverResp != "CLIENT DBG DONE ACK" do
104 cliOverResp = gets
105 end
106 end
107
108 # Checks on every call if the client has sent a command before continuing debugging
109 redef fun stmt(n)
110 do
111 if stdin.poll_in then
112 var command = gets
113 if command == "stop" then
114 n.debug("Stopped by client")
115 while process_debug_command(gets) do end
116 else
117 process_debug_command(command)
118 end
119 end
120
121 super(n)
122 end
123
124 end
125
126 redef class ANode
127
128 # Breaks automatically when encountering an error
129 # Permits the injunction of commands before crashing
130 # Disconnects from the client before crashing
131 redef private fun fatal(v: NaiveInterpreter, message: String)
132 do
133 if v isa Debugger then
134 print "An error was encountered, the program will stop now."
135 self.debug(message)
136 while v.process_debug_command(gets) do end
137 end
138
139 if v isa NetworkDebugger then
140 v.disconnect_routine
141 stdin.connection.close
142 end
143
144 super
145 end
146 end
147
148 # Replaces Stdin to read on the network
149 redef class Stdin
150
151 # Represents the connection with a client
152 var connection: nullable Socket = null
153
154 # Used to store data that has been read from the connection
155 var buf: Buffer = new FlatBuffer
156 var buf_pos: Int = 0
157
158 # Checks if data is available for reading
159 redef fun poll_in
160 do
161 return connection.ready_to_read(0)
162 end
163
164 # Reads the whole content of the buffer
165 # Blocking if the buffer is empty
166 redef fun read_all
167 do
168 var loc_buf = new FlatBuffer
169 if connection.ready_to_read(0) then buf.append(connection.read)
170 for i in [buf_pos .. buf.length-1] do loc_buf.add(buf.chars[i])
171 buf.clear
172 buf_pos = 0
173 return loc_buf.to_s
174 end
175
176 # Reads a single char on the incoming buffer
177 # If the buffer is empty, the call is blocking
178 redef fun read_char
179 do
180 if connection.ready_to_read(0) then buf.append(connection.read)
181 if buf_pos >= buf.length then
182 buf.clear
183 buf_pos = 0
184 #Blocking call
185 buf.append(connection.read)
186 end
187 buf_pos += 1
188 return buf.chars[buf_pos-1].ascii
189 end
190
191 # Reads a line on the network if available
192 # Stops at the first encounter of a \n character
193 # If the buffer is empty, the read_line call is blocking
194 redef fun read_line
195 do
196 var line_buf = new FlatBuffer
197 if connection.ready_to_read(0) then buf.append(connection.read)
198 var has_found_eol: Bool = false
199 loop
200 if buf_pos >= buf.length then
201 buf.clear
202 buf_pos = 0
203 # Blocking call
204 buf.append(connection.read)
205 end
206 buf_pos += 1
207 if buf.chars[buf_pos-1] == '\n' then break
208 line_buf.add(buf.chars[buf_pos-1])
209 end
210 return line_buf.to_s
211 end
212 end
213
214 # Replaces Stdout to write on the network
215 redef class Stdout
216
217 # Connection with the client object
218 var connection: nullable Socket = null
219
220 # Writes a string on the network if available, else
221 # it is written in the standard output (Terminal)
222 redef fun write(s)
223 do
224 if connection != null then
225 connection.write(s)
226 else
227 super
228 end
229 end
230
231 end