Merge branch 'refactoring' into leopard
[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 [socket connect];
46 }
47 return self;
48 }
49
50 /**
51 * Releases all of the object's data members and closes the streams
52 */
53 - (void)dealloc
54 {
55 [session release];
56 [socket release];
57 [windowController release];
58
59 [super dealloc];
60 }
61
62 /**
63 * Gets the port number
64 */
65 - (int)port
66 {
67 return port;
68 }
69
70 /**
71 * Gets the session name
72 */
73 - (NSString *)session
74 {
75 return session;
76 }
77
78 /**
79 * Returns the name of the remote host
80 */
81 - (NSString *)remoteHost
82 {
83 if (!connected)
84 {
85 return @"(DISCONNECTED)";
86 }
87 return [socket remoteHost];
88 }
89
90 /**
91 * Returns whether or not we have an active connection
92 */
93 - (BOOL)isConnected
94 {
95 return connected;
96 }
97
98 /**
99 * SocketWrapper delegate method that is called whenever new data is received
100 */
101 - (void)dataReceived:(NSData *)response deliverTo:(SEL)selector
102 {
103 NSXMLDocument *doc = [[NSXMLDocument alloc] initWithData:response options:NSXMLDocumentTidyXML error:nil];
104
105 // check and see if there's an error
106 NSArray *error = [[doc rootElement] elementsForName:@"error"];
107 if ([error count] > 0)
108 {
109 [windowController setError:[[[[error objectAtIndex:0] children] objectAtIndex:0] stringValue]];
110 return;
111 }
112
113 // if the caller of [_socket receive:] specified a deliverTo, just forward the message to them
114 if (selector != nil)
115 {
116 [self performSelector:selector withObject:doc];
117 }
118
119 [doc release];
120 }
121
122 /**
123 * SocketWrapper delegate method that is called after data is sent. This really
124 * isn't useful for much.
125 */
126 - (void)dataSent:(NSString *)data
127 {}
128
129 /**
130 * Called by SocketWrapper after the connection is successful. This immediately calls
131 * -[SocketWrapper receive] to clear the way for communication
132 */
133 - (void)socketDidAccept
134 {
135 connected = YES;
136 [socket receive:@selector(handshake:)];
137 }
138
139 /**
140 * Receives errors from the SocketWrapper and updates the display
141 */
142 - (void)errorEncountered:(NSError *)error
143 {
144 [windowController setError:[error domain]];
145 }
146
147 /**
148 * The initial packet handshake. This allows us to set things like the title of the window
149 * and glean information about hte server we are debugging
150 */
151 - (void)handshake:(NSXMLDocument *)doc
152 {
153 [self refreshStatus];
154 }
155
156 /**
157 * Handler used by dataReceived:deliverTo: for anytime the status command is issued. It sets
158 * the window controller's status text
159 */
160 - (void)updateStatus:(NSXMLDocument *)doc
161 {
162 NSString *status = [[[doc rootElement] attributeForName:@"status"] stringValue];
163 [windowController setStatus:[status capitalizedString]];
164
165 if ([status isEqualToString:@"break"])
166 {
167 [self updateStackTraceAndRegisters];
168 }
169 }
170
171 /**
172 * Tells the debugger to continue running the script
173 */
174 - (void)run
175 {
176 [socket send:[self createCommand:@"run"]];
177 [self refreshStatus];
178 }
179
180 /**
181 * Method that runs tells the debugger to give us its status. This will call _updateStatus
182 * and will update the status text on the window
183 */
184 - (void)refreshStatus
185 {
186 [socket send:[self createCommand:@"status"]];
187 [socket receive:@selector(updateStatus:)];
188 }
189
190 /**
191 * Tells the debugger to step into the current command.
192 */
193 - (void)stepIn
194 {
195 [socket send:[self createCommand:@"step_into"]];
196 [socket receive:nil];
197 [self refreshStatus];
198 }
199
200 /**
201 * Tells the debugger to step out of the current context
202 */
203 - (void)stepOut
204 {
205 [socket send:[self createCommand:@"step_out"]];
206 [socket receive:nil];
207 [self refreshStatus];
208 }
209
210 /**
211 * Tells the debugger to step over the current function
212 */
213 - (void)stepOver
214 {
215 [socket send:[self createCommand:@"step_over"]];
216 [socket receive:nil];
217 [self refreshStatus];
218 }
219
220 /**
221 * This function queries the debug server for the current stacktrace and all the registers on
222 * level one. If a user then tries to expand past level one... TOOD: HOLY CRAP WHAT DO WE DO PAST LEVEL 1?
223 */
224 - (void)updateStackTraceAndRegisters
225 {
226 [socket send:[self createCommand:@"stack_get"]];
227 [socket receive:@selector(stackReceived:)];
228
229 [socket send:[self createCommand:@"context_get"]];
230 [socket receive:@selector(registerReceived:)];
231 }
232
233 /**
234 * Called by the dataReceived delivery delegate. This updates the window controller's data
235 * for the stack trace
236 */
237 - (void)stackReceived:(NSXMLDocument *)doc
238 {
239 NSArray *children = [[doc rootElement] children];
240 NSMutableArray *stack = [NSMutableArray array];
241 NSMutableDictionary *dict = [NSMutableDictionary dictionary];
242 for (int i = 0; i < [children count]; i++)
243 {
244 NSArray *attrs = [[children objectAtIndex:i] attributes];
245 for (int j = 0; j < [attrs count]; j++)
246 {
247 [dict setValue:[[attrs objectAtIndex:j] stringValue] forKey:[[attrs objectAtIndex:j] name]];
248 }
249 [stack addObject:dict];
250 dict = [NSMutableDictionary dictionary];
251 }
252 [windowController setStack:stack];
253 }
254
255 /**
256 * Called when we have a new register to display
257 */
258 - (void)registerReceived:(NSXMLDocument *)doc
259 {
260 [windowController setRegister:doc];
261 }
262
263 /**
264 * Tells the debugger engine to get a specifc property. This also takes in the NSXMLElement
265 * that requested it so that the child can be attached in the delivery.
266 */
267 - (void)getProperty:(NSString *)property forNode:(NSTreeNode *)node
268 {
269 [socket send:[self createCommand:[NSString stringWithFormat:@"property_get -n \"%@\"", property]]];
270 depthFetchNode = node;
271 [socket receive:@selector(propertyReceived:)];
272 }
273
274 /**
275 * Called when a property is received. This then adds the result as children to the passed object
276 */
277 - (void)propertyReceived:(NSXMLDocument *)doc
278 {
279 /*
280 <response>
281 <property> <!-- this is the one we requested -->
282 <property ... /> <!-- these are what we want -->
283 </property>
284 </repsonse>
285 */
286
287 // we now have to detach all the children so we can insert them into another document
288 NSXMLElement *parent = (NSXMLElement *)[[doc rootElement] childAtIndex:0];
289 NSArray *children = [parent children];
290 [parent setChildren:nil];
291 [windowController addChildren:children toNode:depthFetchNode];
292 depthFetchNode = nil;
293 }
294
295 /**
296 * Helper method to create a string command with the -i <session> automatically tacked on
297 */
298 - (NSString *)createCommand:(NSString *)cmd
299 {
300 return [NSString stringWithFormat:@"%@ -i %@", cmd, session];
301 }
302
303 @end