We no longer use DebuggerConnection to manage the DebuggerWindowController, but now...
[macgdbp.git] / Source / DebuggerConnection.m
1 /*
2 * MacGDBp
3 * Copyright (c) 2002 - 2007, Blue Static <http://www.bluestatic.org>
4 *
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17 #import "DebuggerConnection.h"
18 #import "AppDelegate.h"
19
20 @interface DebuggerConnection (Private)
21
22 - (NSString *)createCommand:(NSString *)cmd;
23
24 @end
25
26 @implementation DebuggerConnection
27
28 /**
29 * Creates a new DebuggerConnection and initializes the socket from the given connection
30 * paramters.
31 */
32 - (id)initWithWindowController:(DebuggerWindowController *)wc port:(int)aPort session:(NSString *)aSession;
33 {
34 if (self = [super init])
35 {
36 port = aPort;
37 session = [aSession retain];
38 connected = NO;
39
40 windowController = [wc retain];
41
42 // now that we have our host information, open the socket
43 socket = [[SocketWrapper alloc] initWithPort:port];
44 [socket setDelegate:self];
45 [windowController setStatus:@"Connecting"];
46 [socket connect];
47 }
48 return self;
49 }
50
51 /**
52 * Releases all of the object's data members and closes the streams
53 */
54 - (void)dealloc
55 {
56 [session release];
57 [socket release];
58 [windowController release];
59
60 [super dealloc];
61 }
62
63 /**
64 * Gets the port number
65 */
66 - (int)port
67 {
68 return port;
69 }
70
71 /**
72 * Gets the session name
73 */
74 - (NSString *)session
75 {
76 return session;
77 }
78
79 /**
80 * Returns the name of the remote host
81 */
82 - (NSString *)remoteHost
83 {
84 if (!connected)
85 {
86 return @"(DISCONNECTED)";
87 }
88 return [socket remoteHost];
89 }
90
91 /**
92 * Returns whether or not we have an active connection
93 */
94 - (BOOL)isConnected
95 {
96 return connected;
97 }
98
99 /**
100 * SocketWrapper delegate method that is called whenever new data is received
101 */
102 - (void)dataReceived:(NSData *)response deliverTo:(SEL)selector
103 {
104 NSXMLDocument *doc = [[NSXMLDocument alloc] initWithData:response options:NSXMLDocumentTidyXML error:nil];
105
106 // check and see if there's an error
107 NSArray *error = [[doc rootElement] elementsForName:@"error"];
108 if ([error count] > 0)
109 {
110 [windowController setError:[[[[error objectAtIndex:0] children] objectAtIndex:0] stringValue]];
111 return;
112 }
113
114 // if the caller of [_socket receive:] specified a deliverTo, just forward the message to them
115 if (selector != nil)
116 {
117 [self performSelector:selector withObject:doc];
118 }
119
120 [doc release];
121 }
122
123 /**
124 * SocketWrapper delegate method that is called after data is sent. This really
125 * isn't useful for much.
126 */
127 - (void)dataSent:(NSString *)data
128 {}
129
130 /**
131 * Called by SocketWrapper after the connection is successful. This immediately calls
132 * -[SocketWrapper receive] to clear the way for communication
133 */
134 - (void)socketDidAccept
135 {
136 connected = YES;
137 [socket receive:@selector(handshake:)];
138 }
139
140 /**
141 * Receives errors from the SocketWrapper and updates the display
142 */
143 - (void)errorEncountered:(NSError *)error
144 {
145 [windowController setError:[error domain]];
146 }
147
148 /**
149 * The initial packet handshake. This allows us to set things like the title of the window
150 * and glean information about hte server we are debugging
151 */
152 - (void)handshake:(NSXMLDocument *)doc
153 {
154 [self refreshStatus];
155 }
156
157 /**
158 * Handler used by dataReceived:deliverTo: for anytime the status command is issued. It sets
159 * the window controller's status text
160 */
161 - (void)updateStatus:(NSXMLDocument *)doc
162 {
163 NSString *status = [[[doc rootElement] attributeForName:@"status"] stringValue];
164 [windowController setStatus:[status capitalizedString]];
165
166 if ([status isEqualToString:@"break"])
167 {
168 [self updateStackTraceAndRegisters];
169 }
170 }
171
172 /**
173 * Tells the debugger to continue running the script
174 */
175 - (void)run
176 {
177 [socket send:[self createCommand:@"run"]];
178 [self refreshStatus];
179 }
180
181 /**
182 * Method that runs tells the debugger to give us its status. This will call _updateStatus
183 * and will update the status text on the window
184 */
185 - (void)refreshStatus
186 {
187 [socket send:[self createCommand:@"status"]];
188 [socket receive:@selector(updateStatus:)];
189 }
190
191 /**
192 * Tells the debugger to step into the current command.
193 */
194 - (void)stepIn
195 {
196 [socket send:[self createCommand:@"step_into"]];
197 [socket receive:nil];
198 [self refreshStatus];
199 }
200
201 /**
202 * Tells the debugger to step out of the current context
203 */
204 - (void)stepOut
205 {
206 [socket send:[self createCommand:@"step_out"]];
207 [socket receive:nil];
208 [self refreshStatus];
209 }
210
211 /**
212 * Tells the debugger to step over the current function
213 */
214 - (void)stepOver
215 {
216 [socket send:[self createCommand:@"step_over"]];
217 [socket receive:nil];
218 [self refreshStatus];
219 }
220
221 /**
222 * This function queries the debug server for the current stacktrace and all the registers on
223 * level one. If a user then tries to expand past level one... TOOD: HOLY CRAP WHAT DO WE DO PAST LEVEL 1?
224 */
225 - (void)updateStackTraceAndRegisters
226 {
227 [socket send:[self createCommand:@"stack_get"]];
228 [socket receive:@selector(stackReceived:)];
229
230 [socket send:[self createCommand:@"context_get"]];
231 [socket receive:@selector(registerReceived:)];
232 }
233
234 /**
235 * Called by the dataReceived delivery delegate. This updates the window controller's data
236 * for the stack trace
237 */
238 - (void)stackReceived:(NSXMLDocument *)doc
239 {
240 NSArray *children = [[doc rootElement] children];
241 NSMutableArray *stack = [NSMutableArray array];
242 NSMutableDictionary *dict = [NSMutableDictionary dictionary];
243 for (int i = 0; i < [children count]; i++)
244 {
245 NSArray *attrs = [[children objectAtIndex:i] attributes];
246 for (int j = 0; j < [attrs count]; j++)
247 {
248 [dict setValue:[[attrs objectAtIndex:j] stringValue] forKey:[[attrs objectAtIndex:j] name]];
249 }
250 [stack addObject:dict];
251 dict = [NSMutableDictionary dictionary];
252 }
253 [windowController setStack:stack];
254 }
255
256 /**
257 * Called when we have a new register to display
258 */
259 - (void)registerReceived:(NSXMLDocument *)doc
260 {
261 [windowController setRegister:doc];
262 }
263
264 /**
265 * Tells the debugger engine to get a specifc property. This also takes in the NSXMLElement
266 * that requested it so that the child can be attached in the delivery.
267 */
268 - (void)getProperty:(NSString *)property forElement:(NSXMLElement *)elm
269 {
270 [socket send:[self createCommand:[NSString stringWithFormat:@"property_get -n \"%@\"", property]]];
271 depthFetchElement = elm;
272 [socket receive:@selector(propertyReceived:)];
273 }
274
275 /**
276 * Called when a property is received. This then adds the result as children to the passed object
277 */
278 - (void)propertyReceived:(NSXMLDocument *)doc
279 {
280 /*
281 <response>
282 <property> <!-- this is the one we requested -->
283 <property ... /> <!-- these are what we want -->
284 </property>
285 </repsonse>
286 */
287
288 // we now have to detach all the children so we can insert them into another document
289 NSXMLElement *parent = (NSXMLElement *)[[doc rootElement] childAtIndex:0];
290 NSArray *children = [parent children];
291 [parent setChildren:nil];
292 [windowController addChildren:children toNode:depthFetchElement];
293 depthFetchElement = nil;
294 }
295
296 /**
297 * Helper method to create a string command with the -i <session> automatically tacked on
298 */
299 - (NSString *)createCommand:(NSString *)cmd
300 {
301 return [NSString stringWithFormat:@"%@ -i %@", cmd, session];
302 }
303
304 @end