1 /* 2 [The "BSD license"] 3 Copyright (c) 2005-2009 Terence Parr 4 All rights reserved. 5 6 Redistribution and use in source and binary forms, with or without 7 modification, are permitted provided that the following conditions 8 are met: 9 1. Redistributions of source code must retain the above copyright 10 notice, this list of conditions and the following disclaimer. 11 2. Redistributions in binary form must reproduce the above copyright 12 notice, this list of conditions and the following disclaimer in the 13 documentation and/or other materials provided with the distribution. 14 3. The name of the author may not be used to endorse or promote products 15 derived from this software without specific prior written permission. 16 17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 package org.antlr.runtime.debug; 29 30 import org.antlr.runtime.RecognitionException; 31 import org.antlr.runtime.Token; 32 import org.antlr.runtime.CharStream; 33 import org.antlr.runtime.tree.BaseTree; 34 import org.antlr.runtime.tree.Tree; 35 36 import java.io.*; 37 import java.net.ConnectException; 38 import java.net.Socket; 39 import java.util.StringTokenizer; 40 41 public class RemoteDebugEventSocketListener implements Runnable { 42 static final int MAX_EVENT_ELEMENTS = 8; 43 DebugEventListener listener; 44 String machine; 45 int port; 46 Socket channel = null; 47 PrintWriter out; 48 BufferedReader in; 49 String event; 50 /** Version of ANTLR (dictates events) */ 51 public String version; 52 public String grammarFileName; 53 /** Track the last token index we saw during a consume. If same, then 54 * set a flag that we have a problem. 55 */ 56 int previousTokenIndex = -1; 57 boolean tokenIndexesInvalid = false; 58 59 public static class ProxyToken implements Token { 60 int index; 61 int type; 62 int channel; 63 int line; 64 int charPos; 65 String text; ProxyToken(int index)66 public ProxyToken(int index) { this.index = index; } ProxyToken(int index, int type, int channel, int line, int charPos, String text)67 public ProxyToken(int index, int type, int channel, 68 int line, int charPos, String text) 69 { 70 this.index = index; 71 this.type = type; 72 this.channel = channel; 73 this.line = line; 74 this.charPos = charPos; 75 this.text = text; 76 } 77 78 @Override getText()79 public String getText() { 80 return text; 81 } 82 83 @Override setText(String text)84 public void setText(String text) { 85 this.text = text; 86 } 87 88 @Override getType()89 public int getType() { 90 return type; 91 } 92 93 @Override setType(int ttype)94 public void setType(int ttype) { 95 this.type = ttype; 96 } 97 98 @Override getLine()99 public int getLine() { 100 return line; 101 } 102 103 @Override setLine(int line)104 public void setLine(int line) { 105 this.line = line; 106 } 107 108 @Override getCharPositionInLine()109 public int getCharPositionInLine() { 110 return charPos; 111 } 112 113 @Override setCharPositionInLine(int pos)114 public void setCharPositionInLine(int pos) { 115 this.charPos = pos; 116 } 117 118 @Override getChannel()119 public int getChannel() { 120 return channel; 121 } 122 123 @Override setChannel(int channel)124 public void setChannel(int channel) { 125 this.channel = channel; 126 } 127 128 @Override getTokenIndex()129 public int getTokenIndex() { 130 return index; 131 } 132 133 @Override setTokenIndex(int index)134 public void setTokenIndex(int index) { 135 this.index = index; 136 } 137 138 @Override getInputStream()139 public CharStream getInputStream() { 140 return null; 141 } 142 143 @Override setInputStream(CharStream input)144 public void setInputStream(CharStream input) { 145 } 146 147 @Override toString()148 public String toString() { 149 String channelStr = ""; 150 if ( channel!=Token.DEFAULT_CHANNEL ) { 151 channelStr=",channel="+channel; 152 } 153 return "["+getText()+"/<"+type+">"+channelStr+","+line+":"+getCharPositionInLine()+",@"+index+"]"; 154 } 155 } 156 157 public static class ProxyTree extends BaseTree { 158 public int ID; 159 public int type; 160 public int line = 0; 161 public int charPos = -1; 162 public int tokenIndex = -1; 163 public String text; 164 ProxyTree(int ID, int type, int line, int charPos, int tokenIndex, String text)165 public ProxyTree(int ID, int type, int line, int charPos, int tokenIndex, String text) { 166 this.ID = ID; 167 this.type = type; 168 this.line = line; 169 this.charPos = charPos; 170 this.tokenIndex = tokenIndex; 171 this.text = text; 172 } 173 ProxyTree(int ID)174 public ProxyTree(int ID) { this.ID = ID; } 175 getTokenStartIndex()176 @Override public int getTokenStartIndex() { return tokenIndex; } setTokenStartIndex(int index)177 @Override public void setTokenStartIndex(int index) { } getTokenStopIndex()178 @Override public int getTokenStopIndex() { return 0; } setTokenStopIndex(int index)179 @Override public void setTokenStopIndex(int index) { } dupNode()180 @Override public Tree dupNode() { return null; } getType()181 @Override public int getType() { return type; } getText()182 @Override public String getText() { return text; } toString()183 @Override public String toString() { 184 return "fix this"; 185 } 186 } 187 RemoteDebugEventSocketListener(DebugEventListener listener, String machine, int port)188 public RemoteDebugEventSocketListener(DebugEventListener listener, 189 String machine, 190 int port) throws IOException 191 { 192 this.listener = listener; 193 this.machine = machine; 194 this.port = port; 195 196 if( !openConnection() ) { 197 throw new ConnectException(); 198 } 199 } 200 eventHandler()201 protected void eventHandler() { 202 try { 203 handshake(); 204 event = in.readLine(); 205 while ( event!=null ) { 206 dispatch(event); 207 ack(); 208 event = in.readLine(); 209 } 210 } 211 catch (Exception e) { 212 System.err.println(e); 213 e.printStackTrace(System.err); 214 } 215 finally { 216 closeConnection(); 217 } 218 } 219 openConnection()220 protected boolean openConnection() { 221 boolean success = false; 222 try { 223 channel = new Socket(machine, port); 224 channel.setTcpNoDelay(true); 225 OutputStream os = channel.getOutputStream(); 226 OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8"); 227 out = new PrintWriter(new BufferedWriter(osw)); 228 InputStream is = channel.getInputStream(); 229 InputStreamReader isr = new InputStreamReader(is, "UTF8"); 230 in = new BufferedReader(isr); 231 success = true; 232 } catch(Exception e) { 233 System.err.println(e); 234 } 235 return success; 236 } 237 closeConnection()238 protected void closeConnection() { 239 try { 240 in.close(); in = null; 241 out.close(); out = null; 242 channel.close(); channel=null; 243 } 244 catch (Exception e) { 245 System.err.println(e); 246 e.printStackTrace(System.err); 247 } 248 finally { 249 if ( in!=null ) { 250 try {in.close();} catch (IOException ioe) { 251 System.err.println(ioe); 252 } 253 } 254 if ( out!=null ) { 255 out.close(); 256 } 257 if ( channel!=null ) { 258 try {channel.close();} catch (IOException ioe) { 259 System.err.println(ioe); 260 } 261 } 262 } 263 264 } 265 handshake()266 protected void handshake() throws IOException { 267 String antlrLine = in.readLine(); 268 String[] antlrElements = getEventElements(antlrLine); 269 version = antlrElements[1]; 270 String grammarLine = in.readLine(); 271 String[] grammarElements = getEventElements(grammarLine); 272 grammarFileName = grammarElements[1]; 273 ack(); 274 listener.commence(); // inform listener after handshake 275 } 276 ack()277 protected void ack() { 278 out.println("ack"); 279 out.flush(); 280 } 281 dispatch(String line)282 protected void dispatch(String line) { 283 //System.out.println("event: "+line); 284 String[] elements = getEventElements(line); 285 if ( elements==null || elements[0]==null ) { 286 System.err.println("unknown debug event: "+line); 287 return; 288 } 289 if ( elements[0].equals("enterRule") ) { 290 listener.enterRule(elements[1], elements[2]); 291 } 292 else if ( elements[0].equals("exitRule") ) { 293 listener.exitRule(elements[1], elements[2]); 294 } 295 else if ( elements[0].equals("enterAlt") ) { 296 listener.enterAlt(Integer.parseInt(elements[1])); 297 } 298 else if ( elements[0].equals("enterSubRule") ) { 299 listener.enterSubRule(Integer.parseInt(elements[1])); 300 } 301 else if ( elements[0].equals("exitSubRule") ) { 302 listener.exitSubRule(Integer.parseInt(elements[1])); 303 } 304 else if ( elements[0].equals("enterDecision") ) { 305 listener.enterDecision(Integer.parseInt(elements[1]), elements[2].equals("true")); 306 } 307 else if ( elements[0].equals("exitDecision") ) { 308 listener.exitDecision(Integer.parseInt(elements[1])); 309 } 310 else if ( elements[0].equals("location") ) { 311 listener.location(Integer.parseInt(elements[1]), 312 Integer.parseInt(elements[2])); 313 } 314 else if ( elements[0].equals("consumeToken") ) { 315 ProxyToken t = deserializeToken(elements, 1); 316 if ( t.getTokenIndex() == previousTokenIndex ) { 317 tokenIndexesInvalid = true; 318 } 319 previousTokenIndex = t.getTokenIndex(); 320 listener.consumeToken(t); 321 } 322 else if ( elements[0].equals("consumeHiddenToken") ) { 323 ProxyToken t = deserializeToken(elements, 1); 324 if ( t.getTokenIndex() == previousTokenIndex ) { 325 tokenIndexesInvalid = true; 326 } 327 previousTokenIndex = t.getTokenIndex(); 328 listener.consumeHiddenToken(t); 329 } 330 else if ( elements[0].equals("LT") ) { 331 Token t = deserializeToken(elements, 2); 332 listener.LT(Integer.parseInt(elements[1]), t); 333 } 334 else if ( elements[0].equals("mark") ) { 335 listener.mark(Integer.parseInt(elements[1])); 336 } 337 else if ( elements[0].equals("rewind") ) { 338 if ( elements[1]!=null ) { 339 listener.rewind(Integer.parseInt(elements[1])); 340 } 341 else { 342 listener.rewind(); 343 } 344 } 345 else if ( elements[0].equals("beginBacktrack") ) { 346 listener.beginBacktrack(Integer.parseInt(elements[1])); 347 } 348 else if ( elements[0].equals("endBacktrack") ) { 349 int level = Integer.parseInt(elements[1]); 350 int successI = Integer.parseInt(elements[2]); 351 listener.endBacktrack(level, successI==DebugEventListener.TRUE); 352 } 353 else if ( elements[0].equals("exception") ) { 354 String excName = elements[1]; 355 String indexS = elements[2]; 356 String lineS = elements[3]; 357 String posS = elements[4]; 358 Class<? extends RecognitionException> excClass; 359 try { 360 excClass = Class.forName(excName).asSubclass(RecognitionException.class); 361 RecognitionException e = excClass.newInstance(); 362 e.index = Integer.parseInt(indexS); 363 e.line = Integer.parseInt(lineS); 364 e.charPositionInLine = Integer.parseInt(posS); 365 listener.recognitionException(e); 366 } 367 catch (ClassNotFoundException cnfe) { 368 System.err.println("can't find class "+cnfe); 369 cnfe.printStackTrace(System.err); 370 } 371 catch (InstantiationException ie) { 372 System.err.println("can't instantiate class "+ie); 373 ie.printStackTrace(System.err); 374 } 375 catch (IllegalAccessException iae) { 376 System.err.println("can't access class "+iae); 377 iae.printStackTrace(System.err); 378 } 379 } 380 else if ( elements[0].equals("beginResync") ) { 381 listener.beginResync(); 382 } 383 else if ( elements[0].equals("endResync") ) { 384 listener.endResync(); 385 } 386 else if ( elements[0].equals("terminate") ) { 387 listener.terminate(); 388 } 389 else if ( elements[0].equals("semanticPredicate") ) { 390 Boolean result = Boolean.valueOf(elements[1]); 391 String predicateText = elements[2]; 392 predicateText = unEscapeNewlines(predicateText); 393 listener.semanticPredicate(result, 394 predicateText); 395 } 396 else if ( elements[0].equals("consumeNode") ) { 397 ProxyTree node = deserializeNode(elements, 1); 398 listener.consumeNode(node); 399 } 400 else if ( elements[0].equals("LN") ) { 401 int i = Integer.parseInt(elements[1]); 402 ProxyTree node = deserializeNode(elements, 2); 403 listener.LT(i, node); 404 } 405 else if ( elements[0].equals("createNodeFromTokenElements") ) { 406 int ID = Integer.parseInt(elements[1]); 407 int type = Integer.parseInt(elements[2]); 408 String text = elements[3]; 409 text = unEscapeNewlines(text); 410 ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text); 411 listener.createNode(node); 412 } 413 else if ( elements[0].equals("createNode") ) { 414 int ID = Integer.parseInt(elements[1]); 415 int tokenIndex = Integer.parseInt(elements[2]); 416 // create dummy node/token filled with ID, tokenIndex 417 ProxyTree node = new ProxyTree(ID); 418 ProxyToken token = new ProxyToken(tokenIndex); 419 listener.createNode(node, token); 420 } 421 else if ( elements[0].equals("nilNode") ) { 422 int ID = Integer.parseInt(elements[1]); 423 ProxyTree node = new ProxyTree(ID); 424 listener.nilNode(node); 425 } 426 else if ( elements[0].equals("errorNode") ) { 427 // TODO: do we need a special tree here? 428 int ID = Integer.parseInt(elements[1]); 429 int type = Integer.parseInt(elements[2]); 430 String text = elements[3]; 431 text = unEscapeNewlines(text); 432 ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text); 433 listener.errorNode(node); 434 } 435 else if ( elements[0].equals("becomeRoot") ) { 436 int newRootID = Integer.parseInt(elements[1]); 437 int oldRootID = Integer.parseInt(elements[2]); 438 ProxyTree newRoot = new ProxyTree(newRootID); 439 ProxyTree oldRoot = new ProxyTree(oldRootID); 440 listener.becomeRoot(newRoot, oldRoot); 441 } 442 else if ( elements[0].equals("addChild") ) { 443 int rootID = Integer.parseInt(elements[1]); 444 int childID = Integer.parseInt(elements[2]); 445 ProxyTree root = new ProxyTree(rootID); 446 ProxyTree child = new ProxyTree(childID); 447 listener.addChild(root, child); 448 } 449 else if ( elements[0].equals("setTokenBoundaries") ) { 450 int ID = Integer.parseInt(elements[1]); 451 ProxyTree node = new ProxyTree(ID); 452 listener.setTokenBoundaries( 453 node, 454 Integer.parseInt(elements[2]), 455 Integer.parseInt(elements[3])); 456 } 457 else { 458 System.err.println("unknown debug event: "+line); 459 } 460 } 461 deserializeNode(String[] elements, int offset)462 protected ProxyTree deserializeNode(String[] elements, int offset) { 463 int ID = Integer.parseInt(elements[offset+0]); 464 int type = Integer.parseInt(elements[offset+1]); 465 int tokenLine = Integer.parseInt(elements[offset+2]); 466 int charPositionInLine = Integer.parseInt(elements[offset+3]); 467 int tokenIndex = Integer.parseInt(elements[offset+4]); 468 String text = elements[offset+5]; 469 text = unEscapeNewlines(text); 470 return new ProxyTree(ID, type, tokenLine, charPositionInLine, tokenIndex, text); 471 } 472 deserializeToken(String[] elements, int offset)473 protected ProxyToken deserializeToken(String[] elements, 474 int offset) 475 { 476 String indexS = elements[offset+0]; 477 String typeS = elements[offset+1]; 478 String channelS = elements[offset+2]; 479 String lineS = elements[offset+3]; 480 String posS = elements[offset+4]; 481 String text = elements[offset+5]; 482 text = unEscapeNewlines(text); 483 int index = Integer.parseInt(indexS); 484 ProxyToken t = 485 new ProxyToken(index, 486 Integer.parseInt(typeS), 487 Integer.parseInt(channelS), 488 Integer.parseInt(lineS), 489 Integer.parseInt(posS), 490 text); 491 return t; 492 } 493 494 /** Create a thread to listen to the remote running recognizer */ start()495 public void start() { 496 Thread t = new Thread(this); 497 t.start(); 498 } 499 500 @Override run()501 public void run() { 502 eventHandler(); 503 } 504 505 // M i s c 506 getEventElements(String event)507 public String[] getEventElements(String event) { 508 if ( event==null ) { 509 return null; 510 } 511 String[] elements = new String[MAX_EVENT_ELEMENTS]; 512 String str = null; // a string element if present (must be last) 513 try { 514 int firstQuoteIndex = event.indexOf('"'); 515 if ( firstQuoteIndex>=0 ) { 516 // treat specially; has a string argument like "a comment\n 517 // Note that the string is terminated by \n not end quote. 518 // Easier to parse that way. 519 String eventWithoutString = event.substring(0,firstQuoteIndex); 520 str = event.substring(firstQuoteIndex+1,event.length()); 521 event = eventWithoutString; 522 } 523 StringTokenizer st = new StringTokenizer(event, "\t", false); 524 int i = 0; 525 while ( st.hasMoreTokens() ) { 526 if ( i>=MAX_EVENT_ELEMENTS ) { 527 // ErrorManager.internalError("event has more than "+MAX_EVENT_ELEMENTS+" args: "+event); 528 return elements; 529 } 530 elements[i] = st.nextToken(); 531 i++; 532 } 533 if ( str!=null ) { 534 elements[i] = str; 535 } 536 } 537 catch (Exception e) { 538 e.printStackTrace(System.err); 539 } 540 return elements; 541 } 542 unEscapeNewlines(String txt)543 protected String unEscapeNewlines(String txt) { 544 // this unescape is slow but easy to understand 545 txt = txt.replaceAll("%0A","\n"); // unescape \n 546 txt = txt.replaceAll("%0D","\r"); // unescape \r 547 txt = txt.replaceAll("%25","%"); // undo escaped escape chars 548 return txt; 549 } 550 tokenIndexesAreInvalid()551 public boolean tokenIndexesAreInvalid() { 552 return false; 553 //return tokenIndexesInvalid; 554 } 555 556 } 557 558