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 31static NSData *newlineData = nil; 32static 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 70public 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 115protected 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