• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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