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