Home | History | Annotate | Download | only in debug
      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;
     66 		public ProxyToken(int index) { this.index = index; }
     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 		public String getText() {
     78 			return text;
     79 		}
     80 		public void setText(String text) {
     81 			this.text = text;
     82 		}
     83 		public int getType() {
     84 			return type;
     85 		}
     86 		public void setType(int ttype) {
     87 			this.type = ttype;
     88 		}
     89 		public int getLine() {
     90 			return line;
     91 		}
     92 		public void setLine(int line) {
     93 			this.line = line;
     94 		}
     95 		public int getCharPositionInLine() {
     96 			return charPos;
     97 		}
     98 		public void setCharPositionInLine(int pos) {
     99 			this.charPos = pos;
    100 		}
    101 		public int getChannel() {
    102 			return channel;
    103 		}
    104 		public void setChannel(int channel) {
    105 			this.channel = channel;
    106 		}
    107 		public int getTokenIndex() {
    108 			return index;
    109 		}
    110 		public void setTokenIndex(int index) {
    111 			this.index = index;
    112 		}
    113 		public CharStream getInputStream() {
    114 			return null;
    115 		}
    116 		public void setInputStream(CharStream input) {
    117 		}
    118 		public String toString() {
    119 			String channelStr = "";
    120 			if ( channel!=Token.DEFAULT_CHANNEL ) {
    121 				channelStr=",channel="+channel;
    122 			}
    123 			return "["+getText()+"/<"+type+">"+channelStr+","+line+":"+getCharPositionInLine()+",@"+index+"]";
    124 		}
    125 	}
    126 
    127 	public static class ProxyTree extends BaseTree {
    128 		public int ID;
    129 		public int type;
    130 		public int line = 0;
    131 		public int charPos = -1;
    132 		public int tokenIndex = -1;
    133 		public String text;
    134 
    135 		public ProxyTree(int ID, int type, int line, int charPos, int tokenIndex, String text) {
    136 			this.ID = ID;
    137 			this.type = type;
    138 			this.line = line;
    139 			this.charPos = charPos;
    140 			this.tokenIndex = tokenIndex;
    141 			this.text = text;
    142 		}
    143 
    144 		public ProxyTree(int ID) { this.ID = ID; }
    145 
    146 		public int getTokenStartIndex() { return tokenIndex; }
    147 		public void setTokenStartIndex(int index) {	}
    148 		public int getTokenStopIndex() { return 0; }
    149 		public void setTokenStopIndex(int index) { }
    150 		public Tree dupNode() {	return null; }
    151 		public int getType() { return type; }
    152 		public String getText() { return text; }
    153 		public String toString() {
    154 			return "fix this";
    155 		}
    156 	}
    157 
    158 	public RemoteDebugEventSocketListener(DebugEventListener listener,
    159 										  String machine,
    160 										  int port) throws IOException
    161 	{
    162 		this.listener = listener;
    163 		this.machine = machine;
    164 		this.port = port;
    165 
    166         if( !openConnection() ) {
    167             throw new ConnectException();
    168         }
    169 	}
    170 
    171 	protected void eventHandler() {
    172 		try {
    173 			handshake();
    174 			event = in.readLine();
    175 			while ( event!=null ) {
    176 				dispatch(event);
    177 				ack();
    178 				event = in.readLine();
    179 			}
    180 		}
    181 		catch (Exception e) {
    182 			System.err.println(e);
    183 			e.printStackTrace(System.err);
    184 		}
    185 		finally {
    186             closeConnection();
    187 		}
    188 	}
    189 
    190     protected boolean openConnection() {
    191         boolean success = false;
    192         try {
    193             channel = new Socket(machine, port);
    194             channel.setTcpNoDelay(true);
    195 			OutputStream os = channel.getOutputStream();
    196 			OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8");
    197 			out = new PrintWriter(new BufferedWriter(osw));
    198 			InputStream is = channel.getInputStream();
    199 			InputStreamReader isr = new InputStreamReader(is, "UTF8");
    200 			in = new BufferedReader(isr);
    201             success = true;
    202         } catch(Exception e) {
    203             System.err.println(e);
    204         }
    205         return success;
    206     }
    207 
    208     protected void closeConnection() {
    209         try {
    210             in.close(); in = null;
    211             out.close(); out = null;
    212             channel.close(); channel=null;
    213         }
    214         catch (Exception e) {
    215             System.err.println(e);
    216             e.printStackTrace(System.err);
    217         }
    218         finally {
    219             if ( in!=null ) {
    220                 try {in.close();} catch (IOException ioe) {
    221                     System.err.println(ioe);
    222                 }
    223             }
    224             if ( out!=null ) {
    225                 out.close();
    226             }
    227             if ( channel!=null ) {
    228                 try {channel.close();} catch (IOException ioe) {
    229                     System.err.println(ioe);
    230                 }
    231             }
    232         }
    233 
    234     }
    235 
    236 	protected void handshake() throws IOException {
    237 		String antlrLine = in.readLine();
    238 		String[] antlrElements = getEventElements(antlrLine);
    239 		version = antlrElements[1];
    240 		String grammarLine = in.readLine();
    241 		String[] grammarElements = getEventElements(grammarLine);
    242 		grammarFileName = grammarElements[1];
    243 		ack();
    244 		listener.commence(); // inform listener after handshake
    245 	}
    246 
    247 	protected void ack() {
    248         out.println("ack");
    249 		out.flush();
    250 	}
    251 
    252 	protected void dispatch(String line) {
    253         //System.out.println("event: "+line);
    254         String[] elements = getEventElements(line);
    255 		if ( elements==null || elements[0]==null ) {
    256 			System.err.println("unknown debug event: "+line);
    257 			return;
    258 		}
    259 		if ( elements[0].equals("enterRule") ) {
    260 			listener.enterRule(elements[1], elements[2]);
    261 		}
    262 		else if ( elements[0].equals("exitRule") ) {
    263 			listener.exitRule(elements[1], elements[2]);
    264 		}
    265 		else if ( elements[0].equals("enterAlt") ) {
    266 			listener.enterAlt(Integer.parseInt(elements[1]));
    267 		}
    268 		else if ( elements[0].equals("enterSubRule") ) {
    269 			listener.enterSubRule(Integer.parseInt(elements[1]));
    270 		}
    271 		else if ( elements[0].equals("exitSubRule") ) {
    272 			listener.exitSubRule(Integer.parseInt(elements[1]));
    273 		}
    274 		else if ( elements[0].equals("enterDecision") ) {
    275 			listener.enterDecision(Integer.parseInt(elements[1]), elements[2].equals("true"));
    276 		}
    277 		else if ( elements[0].equals("exitDecision") ) {
    278 			listener.exitDecision(Integer.parseInt(elements[1]));
    279 		}
    280 		else if ( elements[0].equals("location") ) {
    281 			listener.location(Integer.parseInt(elements[1]),
    282 							  Integer.parseInt(elements[2]));
    283 		}
    284 		else if ( elements[0].equals("consumeToken") ) {
    285 			ProxyToken t = deserializeToken(elements, 1);
    286 			if ( t.getTokenIndex() == previousTokenIndex ) {
    287 				tokenIndexesInvalid = true;
    288 			}
    289 			previousTokenIndex = t.getTokenIndex();
    290 			listener.consumeToken(t);
    291 		}
    292 		else if ( elements[0].equals("consumeHiddenToken") ) {
    293 			ProxyToken t = deserializeToken(elements, 1);
    294 			if ( t.getTokenIndex() == previousTokenIndex ) {
    295 				tokenIndexesInvalid = true;
    296 			}
    297 			previousTokenIndex = t.getTokenIndex();
    298 			listener.consumeHiddenToken(t);
    299 		}
    300 		else if ( elements[0].equals("LT") ) {
    301 			Token t = deserializeToken(elements, 2);
    302 			listener.LT(Integer.parseInt(elements[1]), t);
    303 		}
    304 		else if ( elements[0].equals("mark") ) {
    305 			listener.mark(Integer.parseInt(elements[1]));
    306 		}
    307 		else if ( elements[0].equals("rewind") ) {
    308 			if ( elements[1]!=null ) {
    309 				listener.rewind(Integer.parseInt(elements[1]));
    310 			}
    311 			else {
    312 				listener.rewind();
    313 			}
    314 		}
    315 		else if ( elements[0].equals("beginBacktrack") ) {
    316 			listener.beginBacktrack(Integer.parseInt(elements[1]));
    317 		}
    318 		else if ( elements[0].equals("endBacktrack") ) {
    319 			int level = Integer.parseInt(elements[1]);
    320 			int successI = Integer.parseInt(elements[2]);
    321 			listener.endBacktrack(level, successI==DebugEventListener.TRUE);
    322 		}
    323 		else if ( elements[0].equals("exception") ) {
    324 			String excName = elements[1];
    325 			String indexS = elements[2];
    326 			String lineS = elements[3];
    327 			String posS = elements[4];
    328 			Class excClass = null;
    329 			try {
    330 				excClass = Class.forName(excName);
    331 				RecognitionException e =
    332 					(RecognitionException)excClass.newInstance();
    333 				e.index = Integer.parseInt(indexS);
    334 				e.line = Integer.parseInt(lineS);
    335 				e.charPositionInLine = Integer.parseInt(posS);
    336 				listener.recognitionException(e);
    337 			}
    338 			catch (ClassNotFoundException cnfe) {
    339 				System.err.println("can't find class "+cnfe);
    340 				cnfe.printStackTrace(System.err);
    341 			}
    342 			catch (InstantiationException ie) {
    343 				System.err.println("can't instantiate class "+ie);
    344 				ie.printStackTrace(System.err);
    345 			}
    346 			catch (IllegalAccessException iae) {
    347 				System.err.println("can't access class "+iae);
    348 				iae.printStackTrace(System.err);
    349 			}
    350 		}
    351 		else if ( elements[0].equals("beginResync") ) {
    352 			listener.beginResync();
    353 		}
    354 		else if ( elements[0].equals("endResync") ) {
    355 			listener.endResync();
    356 		}
    357 		else if ( elements[0].equals("terminate") ) {
    358 			listener.terminate();
    359 		}
    360 		else if ( elements[0].equals("semanticPredicate") ) {
    361 			Boolean result = Boolean.valueOf(elements[1]);
    362 			String predicateText = elements[2];
    363 			predicateText = unEscapeNewlines(predicateText);
    364 			listener.semanticPredicate(result.booleanValue(),
    365 									   predicateText);
    366 		}
    367 		else if ( elements[0].equals("consumeNode") ) {
    368 			ProxyTree node = deserializeNode(elements, 1);
    369 			listener.consumeNode(node);
    370 		}
    371 		else if ( elements[0].equals("LN") ) {
    372 			int i = Integer.parseInt(elements[1]);
    373 			ProxyTree node = deserializeNode(elements, 2);
    374 			listener.LT(i, node);
    375 		}
    376 		else if ( elements[0].equals("createNodeFromTokenElements") ) {
    377 			int ID = Integer.parseInt(elements[1]);
    378 			int type = Integer.parseInt(elements[2]);
    379 			String text = elements[3];
    380 			text = unEscapeNewlines(text);
    381 			ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text);
    382 			listener.createNode(node);
    383 		}
    384 		else if ( elements[0].equals("createNode") ) {
    385 			int ID = Integer.parseInt(elements[1]);
    386 			int tokenIndex = Integer.parseInt(elements[2]);
    387 			// create dummy node/token filled with ID, tokenIndex
    388 			ProxyTree node = new ProxyTree(ID);
    389 			ProxyToken token = new ProxyToken(tokenIndex);
    390 			listener.createNode(node, token);
    391 		}
    392 		else if ( elements[0].equals("nilNode") ) {
    393 			int ID = Integer.parseInt(elements[1]);
    394 			ProxyTree node = new ProxyTree(ID);
    395 			listener.nilNode(node);
    396 		}
    397 		else if ( elements[0].equals("errorNode") ) {
    398 			// TODO: do we need a special tree here?
    399 			int ID = Integer.parseInt(elements[1]);
    400 			int type = Integer.parseInt(elements[2]);
    401 			String text = elements[3];
    402 			text = unEscapeNewlines(text);
    403 			ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text);
    404 			listener.errorNode(node);
    405 		}
    406 		else if ( elements[0].equals("becomeRoot") ) {
    407 			int newRootID = Integer.parseInt(elements[1]);
    408 			int oldRootID = Integer.parseInt(elements[2]);
    409 			ProxyTree newRoot = new ProxyTree(newRootID);
    410 			ProxyTree oldRoot = new ProxyTree(oldRootID);
    411 			listener.becomeRoot(newRoot, oldRoot);
    412 		}
    413 		else if ( elements[0].equals("addChild") ) {
    414 			int rootID = Integer.parseInt(elements[1]);
    415 			int childID = Integer.parseInt(elements[2]);
    416 			ProxyTree root = new ProxyTree(rootID);
    417 			ProxyTree child = new ProxyTree(childID);
    418 			listener.addChild(root, child);
    419 		}
    420 		else if ( elements[0].equals("setTokenBoundaries") ) {
    421 			int ID = Integer.parseInt(elements[1]);
    422 			ProxyTree node = new ProxyTree(ID);
    423 			listener.setTokenBoundaries(
    424 				node,
    425 				Integer.parseInt(elements[2]),
    426 				Integer.parseInt(elements[3]));
    427 		}
    428 		else {
    429 			System.err.println("unknown debug event: "+line);
    430 		}
    431 	}
    432 
    433 	protected ProxyTree deserializeNode(String[] elements, int offset) {
    434 		int ID = Integer.parseInt(elements[offset+0]);
    435 		int type = Integer.parseInt(elements[offset+1]);
    436 		int tokenLine = Integer.parseInt(elements[offset+2]);
    437 		int charPositionInLine = Integer.parseInt(elements[offset+3]);
    438 		int tokenIndex = Integer.parseInt(elements[offset+4]);
    439 		String text = elements[offset+5];
    440 		text = unEscapeNewlines(text);
    441 		return new ProxyTree(ID, type, tokenLine, charPositionInLine, tokenIndex, text);
    442 	}
    443 
    444 	protected ProxyToken deserializeToken(String[] elements,
    445 										  int offset)
    446 	{
    447 		String indexS = elements[offset+0];
    448 		String typeS = elements[offset+1];
    449 		String channelS = elements[offset+2];
    450 		String lineS = elements[offset+3];
    451 		String posS = elements[offset+4];
    452 		String text = elements[offset+5];
    453 		text = unEscapeNewlines(text);
    454 		int index = Integer.parseInt(indexS);
    455 		ProxyToken t =
    456 			new ProxyToken(index,
    457 						   Integer.parseInt(typeS),
    458 						   Integer.parseInt(channelS),
    459 						   Integer.parseInt(lineS),
    460 						   Integer.parseInt(posS),
    461 						   text);
    462 		return t;
    463 	}
    464 
    465 	/** Create a thread to listen to the remote running recognizer */
    466 	public void start() {
    467 		Thread t = new Thread(this);
    468 		t.start();
    469 	}
    470 
    471 	public void run() {
    472 		eventHandler();
    473 	}
    474 
    475 	// M i s c
    476 
    477 	public String[] getEventElements(String event) {
    478 		if ( event==null ) {
    479 			return null;
    480 		}
    481 		String[] elements = new String[MAX_EVENT_ELEMENTS];
    482 		String str = null; // a string element if present (must be last)
    483 		try {
    484 			int firstQuoteIndex = event.indexOf('"');
    485 			if ( firstQuoteIndex>=0 ) {
    486 				// treat specially; has a string argument like "a comment\n
    487 				// Note that the string is terminated by \n not end quote.
    488 				// Easier to parse that way.
    489 				String eventWithoutString = event.substring(0,firstQuoteIndex);
    490 				str = event.substring(firstQuoteIndex+1,event.length());
    491 				event = eventWithoutString;
    492 			}
    493 			StringTokenizer st = new StringTokenizer(event, "\t", false);
    494 			int i = 0;
    495 			while ( st.hasMoreTokens() ) {
    496 				if ( i>=MAX_EVENT_ELEMENTS ) {
    497 					// ErrorManager.internalError("event has more than "+MAX_EVENT_ELEMENTS+" args: "+event);
    498 					return elements;
    499 				}
    500 				elements[i] = st.nextToken();
    501 				i++;
    502 			}
    503 			if ( str!=null ) {
    504 				elements[i] = str;
    505 			}
    506 		}
    507 		catch (Exception e) {
    508 			e.printStackTrace(System.err);
    509 		}
    510 		return elements;
    511 	}
    512 
    513 	protected String unEscapeNewlines(String txt) {
    514 		// this unescape is slow but easy to understand
    515 		txt = txt.replaceAll("%0A","\n");  // unescape \n
    516 		txt = txt.replaceAll("%0D","\r");  // unescape \r
    517 		txt = txt.replaceAll("%25","%");   // undo escaped escape chars
    518 		return txt;
    519 	}
    520 
    521 	public boolean tokenIndexesAreInvalid() {
    522 		return false;
    523 		//return tokenIndexesInvalid;
    524 	}
    525 
    526 }
    527 
    528