Hooking up "Step Over" and "Step Out" buttons
[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
19 @implementation DebuggerConnection
20
21 /**
22 * Creates a new DebuggerConnection and initializes the socket from the given connection
23 * paramters.
24 */
25 - (id)initWithPort: (int)port session: (NSString *)session
26 {
27 if (self = [super init])
28 {
29 _port = port;
30 _session = [session retain];
31 _connected = NO;
32
33 _windowController = [[DebuggerWindowController alloc] initWithConnection: self];
34 [[_windowController window] makeKeyAndOrderFront: self];
35
36 // now that we have our host information, open the socket
37 _socket = [[SocketWrapper alloc] initWithPort: port];
38 [_socket setDelegate: self];
39 [_windowController setStatus: @"Connecting"];
40 [_socket connect];
41
42 // clean up after ourselves
43 [[NSNotificationCenter defaultCenter] addObserver: self
44 selector: @selector(applicationWillTerminate:)
45 name: NSApplicationWillTerminateNotification
46 object: NSApp];
47 }
48 return self;
49 }
50
51 /**
52 * Release ourselves when we're about to die
53 */
54 - (void)applicationWillTerminate: (NSNotification *)notif
55 {
56 [self release];
57 }
58
59 /**
60 * Releases all of the object's data members and closes the streams
61 */
62 - (void)dealloc
63 {
64 [_session release];
65 [_socket release];
66
67 [super dealloc];
68 }
69
70 /**
71 * Gets the port number
72 */
73 - (int)port
74 {
75 return _port;
76 }
77
78 /**
79 * Gets the session name
80 */
81 - (NSString *)session
82 {
83 return _session;
84 }
85
86 /**
87 * Returns the name of the remote host
88 */
89 - (NSString *)remoteHost
90 {
91 if (!_connected)
92 {
93 return @"(DISCONNECTED)";
94 }
95 return [_socket remoteHost];
96 }
97
98 /**
99 * SocketWrapper delegate method that is called whenever new data is received
100 */
101 - (void)dataReceived: (NSString *)response deliverTo: (SEL)selector
102 {
103 // if the caller of [_socket receive:] specified a deliverTo, just forward the message to them
104 if (selector != nil)
105 {
106 [self performSelector: selector withObject: response];
107 }
108 }
109
110 /**
111 * SocketWrapper delegate method that is called after data is sent. This really
112 * isn't useful for much.
113 */
114 - (void)dataSent: (NSString *)data
115 {
116 NSLog(@"send = %@", data);
117 }
118
119 /**
120 * Called by SocketWrapper after the connection is successful. This immediately calls
121 * -[SocketWrapper receive] to clear the way for communication
122 */
123 - (void)socketDidAccept
124 {
125 _connected = YES;
126 [_socket receive: @selector(_handshake:)];
127 }
128
129 /**
130 * Receives errors from the SocketWrapper and updates the display
131 */
132 - (void)errorEncountered: (NSError *)error
133 {
134 [_windowController setError: [error domain]];
135 }
136
137 /**
138 * The initial packet handshake. This allows us to set things like the title of the window
139 * and glean information about hte server we are debugging
140 */
141 - (void)_handshake: (NSData *)packet
142 {
143 [self refreshStatus];
144 }
145
146 /**
147 * Handler used by dataReceived:deliverTo: for anytime the status command is issued. It sets
148 * the window controller's status text
149 */
150 - (void)_updateStatus: (NSData *)packet
151 {
152 NSXMLDocument *doc = [[NSXMLDocument alloc] initWithData: packet options: NSXMLDocumentTidyXML error: nil];
153 [_windowController setStatus: [[[[doc rootElement] attributeForName: @"status"] stringValue] capitalizedString]];
154 [doc release];
155 }
156
157 /**
158 * Tells the debugger to continue running the script
159 */
160 - (void)run
161 {
162 [_socket send: [self _createCommand: @"run"]];
163 [self refreshStatus];
164 }
165
166 /**
167 * Method that runs tells the debugger to give us its status. This will call _updateStatus
168 * and will update the status text on the window
169 */
170 - (void)refreshStatus
171 {
172 [_socket send: [self _createCommand: @"status"]];
173 [_socket receive: @selector(_updateStatus:)];
174 }
175
176 /**
177 * Tells the debugger to step into the current command.
178 */
179 - (void)stepIn
180 {
181 [_socket send: [self _createCommand: @"step_into"]];
182 [_socket receive: nil];
183 [self refreshStatus];
184 [self updateStackTraceAndRegisters];
185 }
186
187 /**
188 * Tells the debugger to step out of the current context
189 */
190 - (void)stepOut
191 {
192 [_socket send: [self _createCommand: @"step_out"]];
193 [_socket receive: nil];
194 [self refreshStatus];
195 [self updateStackTraceAndRegisters];
196 }
197
198 /**
199 * Tells the debugger to step over the current function
200 */
201 - (void)stepOver
202 {
203 [_socket send: [self _createCommand: @"step_over"]];
204 [_socket receive: nil];
205 [self refreshStatus];
206 [self updateStackTraceAndRegisters];
207 }
208
209 /**
210 * This function queries the debug server for the current stacktrace and all the registers on
211 * level one. If a user then tries to expand past level one... TOOD: HOLY CRAP WHAT DO WE DO PAST LEVEL 1?
212 */
213 - (void)updateStackTraceAndRegisters
214 {
215 [_socket send: [self _createCommand: @"stack_get"]];
216 [_socket receive: @selector(_stackReceived:)];
217 }
218
219 /**
220 * Called by the dataReceived delivery delegate. This updates the window controller's data
221 * for the stack trace
222 */
223 - (void)_stackReceived: (NSData *)packet
224 {
225 NSXMLDocument *doc = [[NSXMLDocument alloc] initWithData: packet options: NSXMLDocumentTidyXML error: nil];
226 NSArray *children = [[doc rootElement] children];
227 NSMutableArray *stack = [NSMutableArray array];
228 NSMutableDictionary *dict = [NSMutableDictionary dictionary];
229 for (int i = 0; i < [children count]; i++)
230 {
231 NSArray *attrs = [[children objectAtIndex: i] attributes];
232 for (int j = 0; j < [attrs count]; j++)
233 {
234 [dict setValue: [[attrs objectAtIndex: j] stringValue] forKey: [[attrs objectAtIndex: j] name]];
235 }
236 [stack addObject: dict];
237 dict = [NSMutableDictionary dictionary];
238 }
239 [_windowController setStack: stack];
240 }
241
242 /**
243 * Helper method to create a string command with the -i <session> automatically tacked on
244 */
245 - (NSString *)_createCommand: (NSString *)cmd
246 {
247 return [NSString stringWithFormat: @"%@ -i %@", cmd, _session];
248 }
249
250 @end