<commit_before>﻿//*********************************************************//
//    Copyright (c) Microsoft. All rights reserved.
//    
//    Apache 2.0 License
//    
//    You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//    
//    Unless required by applicable law or agreed to in writing, software 
//    distributed under the License is distributed on an "AS IS" BASIS, 
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
//    implied. See the License for the specific language governing 
//    permissions and limitations under the License.
//
//*********************************************************//

using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.NodejsTools.Debugger.Commands;
using Microsoft.NodejsTools.Debugger.Events;
using Microsoft.VisualStudioTools.Project;
using Newtonsoft.Json.Linq;
using SR = Microsoft.NodejsTools.Project.SR;

namespace Microsoft.NodejsTools.Debugger.Communication {
    sealed class DebuggerClient : IDebuggerClient {
        private readonly IDebuggerConnection _connection;

        private ConcurrentDictionary<int, TaskCompletionSource<JObject>> _messages =
            new ConcurrentDictionary<int, TaskCompletionSource<JObject>>();

        public DebuggerClient(IDebuggerConnection connection) {
            Utilities.ArgumentNotNull("connection", connection);

            _connection = connection;
            _connection.OutputMessage += OnOutputMessage;
            _connection.ConnectionClosed += OnConnectionClosed;
        }

        /// <summary>
        /// Send a command to debugger.
        /// </summary>
        /// <param name="command">Command.</param>
        /// <param name="cancellationToken">Cancellation token.</param>
        public async Task SendRequestAsync(DebuggerCommand command, CancellationToken cancellationToken = new CancellationToken()) {
            cancellationToken.ThrowIfCancellationRequested();

            try {
                TaskCompletionSource<JObject> promise = _messages.GetOrAdd(command.Id, i => new TaskCompletionSource<JObject>());
                _connection.SendMessage(command.ToString());
                cancellationToken.ThrowIfCancellationRequested();

                JObject response = await promise.Task.ConfigureAwait(false);
                cancellationToken.ThrowIfCancellationRequested();

                command.ProcessResponse(response);
            } finally {
                TaskCompletionSource<JObject> promise;
                _messages.TryRemove(command.Id, out promise);
            }
        }

        /// <summary>
        /// Execudes the provided code, and catches any expected exceptions that may arise from direct or indirect use of <see cref="DebuggerClient.SendRequestAsync"/>.
        /// (in particular, when the connection is shut down, or is forcibly dropped from the other end).
        /// </summary>
        /// <remarks>
        /// This is intended to be used primarily with fire-and-forget async void methods that run on threadpool threads and cannot leak those exceptions
        /// without crashing the process.
        /// </remarks>
        public static async void RunWithRequestExceptionsHandled(Func<Task> action) {
            try {
                await action().ConfigureAwait(false);
            } catch (IOException) {
            } catch (OperationCanceledException) {
            }
        }

        /// <summary>
        /// Break point event handler.
        /// </summary>
        public event EventHandler<BreakpointEventArgs> BreakpointEvent;

        /// <summary>
        /// Compile script event handler.
        /// </summary>
        public event EventHandler<CompileScriptEventArgs> CompileScriptEvent;

        /// <summary>
        /// Exception event handler.
        /// </summary>
        public event EventHandler<ExceptionEventArgs> ExceptionEvent;

        /// <summary>
        /// Handles disconnect from debugger.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event arguments.</param>
        private void OnConnectionClosed(object sender, EventArgs e) {
            ConcurrentDictionary<int, TaskCompletionSource<JObject>> messages = Interlocked.Exchange(ref _messages, new ConcurrentDictionary<int, TaskCompletionSource<JObject>>());
            foreach (var kv in messages) {
                var exception = new IOException(SR.GetString(SR.DebuggerConnectionClosed));
                kv.Value.SetException(exception);
            }

            messages.Clear();
        }

        /// <summary>
        /// Process message from debugger connection.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="args">Event arguments.</param>
        private void OnOutputMessage(object sender, MessageEventArgs args) {
            JObject message = JObject.Parse(args.Message);
            var messageType = (string)message["type"];

            switch (messageType) {
                case "event":
                    HandleEventMessage(message);
                    break;

                case "response":
                    HandleResponseMessage(message);
                    break;

                default:
                    Debug.Fail(string.Format("Unrecognized type '{0}' in message: {1}", messageType, message));
                    break;
            }
        }

        /// <summary>
        /// Handles event message.
        /// </summary>
        /// <param name="message">Message.</param>
        private void HandleEventMessage(JObject message) {
            var eventType = (string)message["event"];
            switch (eventType) {
                case "afterCompile":
                    EventHandler<CompileScriptEventArgs> compileScriptHandler = CompileScriptEvent;
                    if (compileScriptHandler != null) {
                        var compileScriptEvent = new CompileScriptEvent(message);
                        compileScriptHandler(this, new CompileScriptEventArgs(compileScriptEvent));<commit_msg>Fix Debugger display of JS strings that contain Date strings<commit_after>﻿//*********************************************************//
//    Copyright (c) Microsoft. All rights reserved.
//    
//    Apache 2.0 License
//    
//    You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//    
//    Unless required by applicable law or agreed to in writing, software 
//    distributed under the License is distributed on an "AS IS" BASIS, 
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
//    implied. See the License for the specific language governing 
//    permissions and limitations under the License.
//
//*********************************************************//

using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.NodejsTools.Debugger.Commands;
using Microsoft.NodejsTools.Debugger.Events;
using Microsoft.VisualStudioTools.Project;
using Newtonsoft.Json.Linq;
using SR = Microsoft.NodejsTools.Project.SR;

namespace Microsoft.NodejsTools.Debugger.Communication {
    sealed class DebuggerClient : IDebuggerClient {
        private readonly IDebuggerConnection _connection;

        private ConcurrentDictionary<int, TaskCompletionSource<JObject>> _messages =
            new ConcurrentDictionary<int, TaskCompletionSource<JObject>>();

        private readonly static Newtonsoft.Json.JsonSerializerSettings jsonSettings = new Newtonsoft.Json.JsonSerializerSettings() {
            DateParseHandling = Newtonsoft.Json.DateParseHandling.None
        };

        public DebuggerClient(IDebuggerConnection connection) {
            Utilities.ArgumentNotNull("connection", connection);

            _connection = connection;
            _connection.OutputMessage += OnOutputMessage;
            _connection.ConnectionClosed += OnConnectionClosed;
        }

        /// <summary>
        /// Send a command to debugger.
        /// </summary>
        /// <param name="command">Command.</param>
        /// <param name="cancellationToken">Cancellation token.</param>
        public async Task SendRequestAsync(DebuggerCommand command, CancellationToken cancellationToken = new CancellationToken()) {
            cancellationToken.ThrowIfCancellationRequested();

            try {
                TaskCompletionSource<JObject> promise = _messages.GetOrAdd(command.Id, i => new TaskCompletionSource<JObject>());
                _connection.SendMessage(command.ToString());
                cancellationToken.ThrowIfCancellationRequested();

                JObject response = await promise.Task.ConfigureAwait(false);
                cancellationToken.ThrowIfCancellationRequested();

                command.ProcessResponse(response);
            } finally {
                TaskCompletionSource<JObject> promise;
                _messages.TryRemove(command.Id, out promise);
            }
        }

        /// <summary>
        /// Execudes the provided code, and catches any expected exceptions that may arise from direct or indirect use of <see cref="DebuggerClient.SendRequestAsync"/>.
        /// (in particular, when the connection is shut down, or is forcibly dropped from the other end).
        /// </summary>
        /// <remarks>
        /// This is intended to be used primarily with fire-and-forget async void methods that run on threadpool threads and cannot leak those exceptions
        /// without crashing the process.
        /// </remarks>
        public static async void RunWithRequestExceptionsHandled(Func<Task> action) {
            try {
                await action().ConfigureAwait(false);
            } catch (IOException) {
            } catch (OperationCanceledException) {
            }
        }

        /// <summary>
        /// Break point event handler.
        /// </summary>
        public event EventHandler<BreakpointEventArgs> BreakpointEvent;

        /// <summary>
        /// Compile script event handler.
        /// </summary>
        public event EventHandler<CompileScriptEventArgs> CompileScriptEvent;

        /// <summary>
        /// Exception event handler.
        /// </summary>
        public event EventHandler<ExceptionEventArgs> ExceptionEvent;

        /// <summary>
        /// Handles disconnect from debugger.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event arguments.</param>
        private void OnConnectionClosed(object sender, EventArgs e) {
            ConcurrentDictionary<int, TaskCompletionSource<JObject>> messages = Interlocked.Exchange(ref _messages, new ConcurrentDictionary<int, TaskCompletionSource<JObject>>());
            foreach (var kv in messages) {
                var exception = new IOException(SR.GetString(SR.DebuggerConnectionClosed));
                kv.Value.SetException(exception);
            }

            messages.Clear();
        }

        /// <summary>
        /// Process message from debugger connection.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="args">Event arguments.</param>
        private void OnOutputMessage(object sender, MessageEventArgs args) {
            var message = Newtonsoft.Json.JsonConvert.DeserializeObject<JObject>(args.Message, jsonSettings);
            var messageType = (string)message["type"];

            switch (messageType) {
                case "event":
                    HandleEventMessage(message);
                    break;

                case "response":
                    HandleResponseMessage(message);
                    break;

                default:
                    Debug.Fail(string.Format("Unrecognized type '{0}' in message: {1}", messageType, message));
                    break;
            }
        }

        /// <summary>
        /// Handles event message.
        /// </summary>
        /// <param name="message">Message.</param>
        private void HandleEventMessage(JObject message) {
            var eventType = (string)message["event"];
            switch (eventType) {
                case "afterCompile":
                    EventHandler<CompileScriptEventArgs> compileScriptHandler = CompileScriptEvent;
                    if (compileScriptHandler != null) {
                        var compileScriptEvent = new CompileScriptEvent(message);
                        compileScriptHandler(this, new CompileScriptEventArgs(compileScriptEvent));
{'fraction_non_alphanumeric': 0.1147373387857817, 'fraction_numerical': 0.0009436929852154766, 'mean_word_length': 2.2524296675191815, 'pattern_counts': {'":': 6, '<': 102, '<?xml version=': 0, '>': 104, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6646153', 'n_tokens_mistral': 2944, 'n_tokens_neox': 2884, 'n_words': 780}