RemoteDebugEventSocketListener.cs revision 324c4644fee44b9898524c09511bd33c3f12e2df
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
35namespace 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;
74            public ProxyToken(int index) {
75                this.index = index;
76            }
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
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
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
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
229            public override ITree DupNode() {
230                return null;
231            }
232
233            public override string ToString() {
234                return "fix this";
235            }
236        }
237
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
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
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
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
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
330        protected virtual void Ack() {
331            @out.WriteLine("ack");
332            @out.Flush();
333        }
334
335        protected virtual void Dispatch(string line) {
336            //JSystem.@out.println( "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
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
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> */
526        public virtual void Start() {
527            System.Threading.Thread t = new System.Threading.Thread(Run);
528            t.Start();
529        }
530
531        public virtual void Run() {
532            EventHandler();
533        }
534
535        #region Misc
536
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
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
580        public virtual bool TokenIndexesAreInvalid() {
581            return false;
582            //return tokenIndexesInvalid;
583        }
584
585        #endregion
586
587    }
588}
589