1 // [The "BSD licence"] 2 // Copyright (c) 2006-2007 Kay Roepke 3 // All rights reserved. 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions 7 // are met: 8 // 1. Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // 2. Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // 3. The name of the author may not be used to endorse or promote products 14 // derived from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 // IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 27 #import "DebugEventSocketProxy.h" 28 #import "Token+DebuggerSupport.h" 29 #include <string.h> 30 31 static NSData *newlineData = nil; 32 static unsigned lengthOfUTF8Ack = 0; 33 34 @implementation DebugEventSocketProxy 35 36 + (void) initialize 37 { 38 if (!newlineData) newlineData = [@"\n" dataUsingEncoding:NSUTF8StringEncoding]; 39 if (!lengthOfUTF8Ack) lengthOfUTF8Ack = [[@"ack\n" dataUsingEncoding:NSUTF8StringEncoding] length]; 40 } 41 42 - (id) init 43 { 44 return [self initWithGrammarName:nil debuggerPort:DEFAULT_DEBUGGER_PORT]; 45 } 46 47 - (id) initWithGrammarName:(NSString *)aGrammarName debuggerPort:(NSInteger)aPort 48 { 49 self = [super init]; 50 if (self) { 51 serverSocket = -1; 52 [self setGrammarName:aGrammarName]; 53 if (aPort == -1) aPort = DEFAULT_DEBUGGER_PORT; 54 [self setDebuggerPort:aPort]; 55 } 56 return self; 57 } 58 59 - (void) dealloc 60 { 61 if (serverSocket != -1) 62 shutdown(serverSocket,SHUT_RDWR); 63 serverSocket = -1; 64 [debuggerFH release]; 65 [self setGrammarName:nil]; 66 [super dealloc]; 67 } 68 69 /* Java stuff 70 public void handshake() throws IOException { 71 if ( serverSocket==nil ) { 72 serverSocket = new ServerSocket(port); 73 socket = serverSocket.accept(); 74 socket.setTcpNoDelay(true); 75 OutputStream os = socket.getOutputStream(); 76 OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8"); 77 out = new PrintWriter(new BufferedWriter(osw)); 78 InputStream is = socket.getInputStream(); 79 InputStreamReader isr = new InputStreamReader(is, "UTF8"); 80 in = new BufferedReader(isr); 81 out.println("ANTLR "+ DebugEventListener.PROTOCOL_VERSION); 82 out.println("grammar \""+ grammarFileName); 83 out.flush(); 84 ack(); 85 } 86 } 87 88 - (void) commence 89 { 90 // don't bother sending event; listener will trigger upon connection 91 } 92 93 - (void) terminate 94 { 95 [self transmit:@"terminate"; 96 [out close]; 97 try { 98 [socket close]; 99 } 100 catch (IOException *ioe) { 101 ioe.printStackTrace(System.err); 102 } 103 } 104 105 - (void) ack 106 { 107 try { 108 in.readLine(); 109 } 110 catch (IOException ioe) { 111 ioe.printStackTrace(System.err); 112 } 113 } 114 115 protected void transmit(String event) { 116 out.println(event); 117 out.flush(); 118 ack(); 119 } 120 */ 121 122 - (void) waitForDebuggerConnection 123 { 124 if (serverSocket == -1) { 125 serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 126 127 NSAssert1(serverSocket != -1, @"Failed to create debugger socket. %s", strerror(errno)); 128 129 int yes = 1; 130 setsockopt(serverSocket, SOL_SOCKET, SO_KEEPALIVE|SO_REUSEPORT|SO_REUSEADDR|TCP_NODELAY, (void *)&yes, sizeof(NSInteger)); 131 132 struct sockaddr_in server_addr; 133 bzero(&server_addr, sizeof(struct sockaddr_in)); 134 server_addr.sin_family = AF_INET; 135 server_addr.sin_port = htons([self debuggerPort]); 136 server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 137 NSAssert1( bind(serverSocket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) != -1, @"bind(2) failed. %s", strerror(errno)); 138 139 NSAssert1(listen(serverSocket,50) == 0, @"listen(2) failed. %s", strerror(errno)); 140 141 NSLog(@"ANTLR waiting for debugger attach (grammar %@)", [self grammarName]); 142 143 debuggerSocket = accept(serverSocket, &debugger_sockaddr, &debugger_socklen); 144 NSAssert1( debuggerSocket != -1, @"accept(2) failed. %s", strerror(errno)); 145 146 debuggerFH = [[NSFileHandle alloc] initWithFileDescriptor:debuggerSocket]; 147 [self sendToDebugger:[NSString stringWithFormat:@"ANTLR %d", DebugProtocolVersion] waitForResponse:NO]; 148 [self sendToDebugger:[NSString stringWithFormat:@"grammar \"%@", [self grammarName]] waitForResponse:NO]; 149 } 150 } 151 152 - (void) waitForAck 153 { 154 NSString *response; 155 @try { 156 NSData *newLine = [debuggerFH readDataOfLength:lengthOfUTF8Ack]; 157 response = [[NSString alloc] initWithData:newLine encoding:NSUTF8StringEncoding]; 158 if (![response isEqualToString:@"ack\n"]) @throw [NSException exceptionWithName:@"DebugEventSocketProxy" reason:@"illegal response from debugger" userInfo:nil]; 159 } 160 @catch (NSException *e) { 161 NSLog(@"socket died or debugger misbehaved: %@ read <%@>", e, response); 162 } 163 @finally { 164 [response release]; 165 } 166 } 167 168 - (void) sendToDebugger:(NSString *)message 169 { 170 [self sendToDebugger:message waitForResponse:YES]; 171 } 172 173 - (void) sendToDebugger:(NSString *)message waitForResponse:(BOOL)wait 174 { 175 if (! debuggerFH ) return; 176 [debuggerFH writeData:[message dataUsingEncoding:NSUTF8StringEncoding]]; 177 [debuggerFH writeData:newlineData]; 178 if (wait) [self waitForAck]; 179 } 180 181 - (NSInteger) serverSocket 182 { 183 return serverSocket; 184 } 185 186 - (void) setServerSocket: (NSInteger) aServerSocket 187 { 188 serverSocket = aServerSocket; 189 } 190 191 - (NSInteger) debuggerSocket 192 { 193 return debuggerSocket; 194 } 195 196 - (void) setDebuggerSocket: (NSInteger) aDebuggerSocket 197 { 198 debuggerSocket = aDebuggerSocket; 199 } 200 201 - (NSString *) grammarName 202 { 203 return grammarName; 204 } 205 206 - (void) setGrammarName: (NSString *) aGrammarName 207 { 208 if (grammarName != aGrammarName) { 209 [aGrammarName retain]; 210 [grammarName release]; 211 grammarName = aGrammarName; 212 } 213 } 214 215 - (NSInteger) debuggerPort 216 { 217 return debuggerPort; 218 } 219 220 - (void) setDebuggerPort: (NSInteger) aDebuggerPort 221 { 222 debuggerPort = aDebuggerPort; 223 } 224 225 - (NSString *) escapeNewlines:(NSString *)aString 226 { 227 NSMutableString *escapedText; 228 if (aString) { 229 escapedText = [NSMutableString stringWithString:aString]; 230 NSRange wholeString = NSMakeRange(0,[escapedText length]); 231 [escapedText replaceOccurrencesOfString:@"%" withString:@"%25" options:0 range:wholeString]; 232 [escapedText replaceOccurrencesOfString:@"\n" withString:@"%0A" options:0 range:wholeString]; 233 [escapedText replaceOccurrencesOfString:@"\r" withString:@"%0D" options:0 range:wholeString]; 234 } else { 235 escapedText = [NSMutableString stringWithString:@""]; 236 } 237 return escapedText; 238 } 239 240 #pragma mark - 241 242 #pragma mark DebugEventListener Protocol 243 - (void) enterRule:(NSString *)ruleName 244 { 245 [self sendToDebugger:[NSString stringWithFormat:@"enterRule %@", ruleName]]; 246 } 247 248 - (void) enterAlt:(NSInteger)alt 249 { 250 [self sendToDebugger:[NSString stringWithFormat:@"enterAlt %d", alt]]; 251 } 252 253 - (void) exitRule:(NSString *)ruleName 254 { 255 [self sendToDebugger:[NSString stringWithFormat:@"exitRule %@", ruleName]]; 256 } 257 258 - (void) enterSubRule:(NSInteger)decisionNumber 259 { 260 [self sendToDebugger:[NSString stringWithFormat:@"enterSubRule %d", decisionNumber]]; 261 } 262 263 - (void) exitSubRule:(NSInteger)decisionNumber 264 { 265 [self sendToDebugger:[NSString stringWithFormat:@"exitSubRule %d", decisionNumber]]; 266 } 267 268 - (void) enterDecision:(NSInteger)decisionNumber 269 { 270 [self sendToDebugger:[NSString stringWithFormat:@"enterDecision %d", decisionNumber]]; 271 } 272 273 - (void) exitDecision:(NSInteger)decisionNumber 274 { 275 [self sendToDebugger:[NSString stringWithFormat:@"exitDecision %d", decisionNumber]]; 276 } 277 278 - (void) consumeToken:(id<Token>)t 279 { 280 [self sendToDebugger:[NSString stringWithFormat:@"consumeToken %@", [self escapeNewlines:[t description]]]]; 281 } 282 283 - (void) consumeHiddenToken:(id<Token>)t 284 { 285 [self sendToDebugger:[NSString stringWithFormat:@"consumeHiddenToken %@", [self escapeNewlines:[t description]]]]; 286 } 287 288 - (void) LT:(NSInteger)i foundToken:(id<Token>)t 289 { 290 [self sendToDebugger:[NSString stringWithFormat:@"LT %d %@", i, [self escapeNewlines:[t description]]]]; 291 } 292 293 - (void) mark:(NSInteger)marker 294 { 295 [self sendToDebugger:[NSString stringWithFormat:@"mark %d", marker]]; 296 } 297 - (void) rewind:(NSInteger)marker 298 { 299 [self sendToDebugger:[NSString stringWithFormat:@"rewind %d", marker]]; 300 } 301 302 - (void) rewind 303 { 304 [self sendToDebugger:@"rewind"]; 305 } 306 307 - (void) beginBacktrack:(NSInteger)level 308 { 309 [self sendToDebugger:[NSString stringWithFormat:@"beginBacktrack %d", level]]; 310 } 311 312 - (void) endBacktrack:(NSInteger)level wasSuccessful:(BOOL)successful 313 { 314 [self sendToDebugger:[NSString stringWithFormat:@"endBacktrack %d %d", level, successful ? 1 : 0]]; 315 } 316 317 - (void) locationLine:(NSInteger)line column:(NSInteger)pos 318 { 319 [self sendToDebugger:[NSString stringWithFormat:@"location %d %d", line, pos]]; 320 } 321 322 - (void) recognitionException:(RecognitionException *)e 323 { 324 #warning TODO: recognition exceptions 325 // these must use the names of the corresponding Java exception classes, because ANTLRWorks recreates the exception 326 // objects on the Java side. 327 // Write categories for Objective-C exceptions to provide those names 328 } 329 330 - (void) beginResync 331 { 332 [self sendToDebugger:@"beginResync"]; 333 } 334 335 - (void) endResync 336 { 337 [self sendToDebugger:@"endResync"]; 338 } 339 340 - (void) semanticPredicate:(NSString *)predicate matched:(BOOL)result 341 { 342 [self sendToDebugger:[NSString stringWithFormat:@"semanticPredicate %d %@", result?1:0, [self escapeNewlines:predicate]]]; 343 } 344 345 - (void) commence 346 { 347 // no need to send event 348 } 349 350 - (void) terminate 351 { 352 [self sendToDebugger:@"terminate"]; 353 @try { 354 [debuggerFH closeFile]; 355 } 356 @finally { 357 #warning TODO: make socket handling robust. too lazy now... 358 shutdown(serverSocket,SHUT_RDWR); 359 serverSocket = -1; 360 } 361 } 362 363 364 #pragma mark Tree Parsing 365 - (void) consumeNode:(unsigned)nodeHash ofType:(NSInteger)type text:(NSString *)text 366 { 367 [self sendToDebugger:[NSString stringWithFormat:@"consumeNode %u %d %@", 368 nodeHash, 369 type, 370 [self escapeNewlines:text] 371 ]]; 372 } 373 374 - (void) LT:(NSInteger)i foundNode:(unsigned)nodeHash ofType:(NSInteger)type text:(NSString *)text 375 { 376 [self sendToDebugger:[NSString stringWithFormat:@"LN %d %u %d %@", 377 i, 378 nodeHash, 379 type, 380 [self escapeNewlines:text] 381 ]]; 382 } 383 384 385 #pragma mark AST Events 386 387 - (void) createNilNode:(unsigned)hash 388 { 389 [self sendToDebugger:[NSString stringWithFormat:@"nilNode %u", hash]]; 390 } 391 392 - (void) createNode:(unsigned)hash text:(NSString *)text type:(NSInteger)type 393 { 394 [self sendToDebugger:[NSString stringWithFormat:@"createNodeFromToken %u %d %@", 395 hash, 396 type, 397 [self escapeNewlines:text] 398 ]]; 399 } 400 401 - (void) createNode:(unsigned)hash fromTokenAtIndex:(NSInteger)tokenIndex 402 { 403 [self sendToDebugger:[NSString stringWithFormat:@"createNode %u %d", hash, tokenIndex]]; 404 } 405 406 - (void) becomeRoot:(unsigned)newRootHash old:(unsigned)oldRootHash 407 { 408 [self sendToDebugger:[NSString stringWithFormat:@"becomeRoot %u %u", newRootHash, oldRootHash]]; 409 } 410 411 - (void) addChild:(unsigned)childHash toTree:(unsigned)treeHash 412 { 413 [self sendToDebugger:[NSString stringWithFormat:@"addChild %u %u", treeHash, childHash]]; 414 } 415 416 - (void) setTokenBoundariesForTree:(unsigned)nodeHash From:(NSInteger)tokenStartIndex To:(NSInteger)tokenStopIndex 417 { 418 [self sendToDebugger:[NSString stringWithFormat:@"setTokenBoundaries %u %d %d", nodeHash, tokenStartIndex, tokenStopIndex]]; 419 } 420 421 422 423 @end 424