<commit_before>﻿/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Linq;
using System.Net;
using Mono.Debugger.Client;
using Mono.Debugging.Client;
using System.Collections.Concurrent;


namespace OpenDebug
{
	public class SDBDebugSession : DebugSession, IDebugSession
	{
		private const string MONO = "mono";
		private readonly string[] MONO_EXTENSIONS = new String[] {
			".cs",
			".fs", ".fsi", ".ml", ".mli", ".fsx", ".fsscript"
		};
		private const int MAX_CHILDREN = 100;

		private Handles<ObjectValue[]> _variableHandles;
		private Handles<Mono.Debugging.Client.StackFrame> _frameHandles;
		private ObjectValue _exception;
		private Dictionary<int, Thread> _seenThreads = new Dictionary<int, Thread>();
		private bool _attachMode = false;


		public SDBDebugSession(Action<DebugEvent> callback) : base(true)
		{
			_variableHandles = new Handles<ObjectValue[]>();
			_frameHandles = new Handles<Mono.Debugging.Client.StackFrame>();
			_seenThreads = new Dictionary<int, Thread>();

			Configuration.Current.MaxConnectionAttempts = 10;
			Configuration.Current.ConnectionAttemptInterval = 500;

			Debugger.Callback = (type, sourceLocation, threadinfo) => {
				int tid;
				switch (type) {
				case "TargetStopped":
					Stopped();
					callback.Invoke(CreateStoppedEvent("step", sourceLocation, threadinfo));
					break;

				case "TargetHitBreakpoint":
					Stopped();
					callback.Invoke(CreateStoppedEvent("breakpoint", sourceLocation, threadinfo));
					break;

				case "TargetExceptionThrown":
				case "TargetUnhandledException":
					Stopped();
					ExceptionInfo ex = Debugger.ActiveException;
					if (ex != null) {
						_exception = ex.Instance;
					}
					callback.Invoke(CreateStoppedEvent("exception", sourceLocation, threadinfo, Debugger.ActiveException.Message));
					break;

				case "TargetExited":
					callback.Invoke(new TerminatedEvent());
					break;

				case "TargetThreadStarted":
					tid = (int)threadinfo.Id;
					lock (_seenThreads) {
						_seenThreads[tid] = new Thread(tid, threadinfo.Name);
					}
					callback.Invoke(new ThreadEvent("started", tid));
					break;

				case "TargetThreadStopped":
					tid = (int)threadinfo.Id;
					lock (_seenThreads) {
						_seenThreads.Remove(tid);
					}
					callback.Invoke(new ThreadEvent("exited", tid));
					break;

				default:
					callback.Invoke(new DebugEvent(type));
					break;
				}
			};

			// the Soft Debugger is ready to accept breakpoints immediately (so it doesn't have to wait until the target is known)
			callback.Invoke(new InitializedEvent());
		}

		public override Task<DebugResult> Launch(dynamic args)
		{
			_attachMode = false;

			// validate argument 'program'
			string programPath = getString(args, "program");
			if (programPath == null) {
				return Task.FromResult(new DebugResult(1001, "launch: property 'program' is missing or empty"));
			}
			programPath = ConvertClientPathToDebugger(programPath);
			if (!File.Exists(programPath) && !Directory.Exists(programPath)) {
				return Task.FromResult(new DebugResult(1002, "launch: program '{path}' does not exist", new { path = programPath }));
			}

			// validate argument 'args'
			string[] arguments = null;
			if (args.args != null) {
				arguments = args.args.ToObject<string[]>();
				if (arguments != null && arguments.Length == 0) {
					arguments = null;
				}
			}

			// validate argument 'cwd'
			var workingDirectory = (string)args.cwd;
			if (workingDirectory != null) {
				workingDirectory = workingDirectory.Trim();
				if (workingDirectory.Length == 0) {
					return Task.FromResult(new DebugResult(1003, "launch: property 'workingDirectory' is empty"));
				}
				workingDirectory = ConvertClientPathToDebugger(workingDirectory);
				if (!Directory.Exists(workingDirectory)) {
					return Task.FromResult(new DebugResult(1004, "launch: workingDirectory '{path}' does not exist", new { path = workingDirectory }));
				}
			}
			workingDirectory = null;	// TODO@AW Why?

			// validate argument 'runtimeExecutable'
			var runtimeExecutable = (string)args.runtimeExecutable;
			if (runtimeExecutable != null) {
				runtimeExecutable = runtimeExecutable.Trim();
				if (runtimeExecutable.Length == 0) {
					return Task.FromResult(new DebugResult(1005, "launch: property 'runtimeExecutable' is empty"));
				}
				runtimeExecutable = ConvertClientPathToDebugger(runtimeExecutable);
				if (!File.Exists(runtimeExecutable)) {
					return Task.FromResult(new DebugResult(1006, "launch: runtimeExecutable '{path}' does not exist", new { path = runtimeExecutable }));
				}
			}

			// validate argument 'runtimeArgs'
			string[] runtimeArguments = null;
			if (args.runtimeArgs != null) {
				runtimeArguments = args.runtimeArgs.ToObject<string[]>();
				if (runtimeArguments != null && runtimeArguments.Length == 0) {
					runtimeArguments = null;
				}
			}

			// validate argument 'env'
			Dictionary<string, string> env = null;
			var environmentVariables = args.env;
			if (environmentVariables != null) {
				env = new Dictionary<string, string>();
				foreach (var entry in environmentVariables) {
					env.Add((string)entry.Name, (string)entry.Value);
				}
				if (env.Count == 0) {
					env = null;
				}
			}

			if (Utilities.IsOSX() || Utilities.IsLinux()) {
				const string host = "127.0.0.1";
				int port = Utilities.FindFreePort(55555);

				string mono_path = runtimeExecutable;
				if (mono_path == null) {
					if (!Terminal.IsOnPath(MONO)) {
						return Task.FromResult(new DebugResult(3001, "launch: can't find runtime '{_runtime}' on PATH", new { _runtime = MONO }));
					}<commit_msg>add 'externalConsole' launch config flag<commit_after>﻿/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Linq;
using System.Net;
using Mono.Debugger.Client;
using Mono.Debugging.Client;
using System.Collections.Concurrent;
using Microsoft.CSharp.RuntimeBinder;


namespace OpenDebug
{
	public class SDBDebugSession : DebugSession, IDebugSession
	{
		private const string MONO = "mono";
		private readonly string[] MONO_EXTENSIONS = new String[] {
			".cs",
			".fs", ".fsi", ".ml", ".mli", ".fsx", ".fsscript"
		};
		private const int MAX_CHILDREN = 100;

		private Handles<ObjectValue[]> _variableHandles;
		private Handles<Mono.Debugging.Client.StackFrame> _frameHandles;
		private ObjectValue _exception;
		private Dictionary<int, Thread> _seenThreads = new Dictionary<int, Thread>();
		private bool _attachMode = false;


		public SDBDebugSession(Action<DebugEvent> callback) : base(true)
		{
			_variableHandles = new Handles<ObjectValue[]>();
			_frameHandles = new Handles<Mono.Debugging.Client.StackFrame>();
			_seenThreads = new Dictionary<int, Thread>();

			Configuration.Current.MaxConnectionAttempts = 10;
			Configuration.Current.ConnectionAttemptInterval = 500;

			Debugger.Callback = (type, sourceLocation, threadinfo) => {
				int tid;
				switch (type) {
				case "TargetStopped":
					Stopped();
					callback.Invoke(CreateStoppedEvent("step", sourceLocation, threadinfo));
					break;

				case "TargetHitBreakpoint":
					Stopped();
					callback.Invoke(CreateStoppedEvent("breakpoint", sourceLocation, threadinfo));
					break;

				case "TargetExceptionThrown":
				case "TargetUnhandledException":
					Stopped();
					ExceptionInfo ex = Debugger.ActiveException;
					if (ex != null) {
						_exception = ex.Instance;
					}
					callback.Invoke(CreateStoppedEvent("exception", sourceLocation, threadinfo, Debugger.ActiveException.Message));
					break;

				case "TargetExited":
					callback.Invoke(new TerminatedEvent());
					break;

				case "TargetThreadStarted":
					tid = (int)threadinfo.Id;
					lock (_seenThreads) {
						_seenThreads[tid] = new Thread(tid, threadinfo.Name);
					}
					callback.Invoke(new ThreadEvent("started", tid));
					break;

				case "TargetThreadStopped":
					tid = (int)threadinfo.Id;
					lock (_seenThreads) {
						_seenThreads.Remove(tid);
					}
					callback.Invoke(new ThreadEvent("exited", tid));
					break;

				default:
					callback.Invoke(new DebugEvent(type));
					break;
				}
			};

			// the Soft Debugger is ready to accept breakpoints immediately (so it doesn't have to wait until the target is known)
			callback.Invoke(new InitializedEvent());
		}

		public override Task<DebugResult> Launch(dynamic args)
		{
			_attachMode = false;

			// validate argument 'program'
			string programPath = getString(args, "program");
			if (programPath == null) {
				return Task.FromResult(new DebugResult(1001, "launch: property 'program' is missing or empty"));
			}
			programPath = ConvertClientPathToDebugger(programPath);
			if (!File.Exists(programPath) && !Directory.Exists(programPath)) {
				return Task.FromResult(new DebugResult(1002, "launch: program '{path}' does not exist", new { path = programPath }));
			}

			// validate argument 'args'
			string[] arguments = null;
			if (args.args != null) {
				arguments = args.args.ToObject<string[]>();
				if (arguments != null && arguments.Length == 0) {
					arguments = null;
				}
			}

			// validate argument 'cwd'
			var workingDirectory = (string)args.cwd;
			if (workingDirectory != null) {
				workingDirectory = workingDirectory.Trim();
				if (workingDirectory.Length == 0) {
					return Task.FromResult(new DebugResult(1003, "launch: property 'workingDirectory' is empty"));
				}
				workingDirectory = ConvertClientPathToDebugger(workingDirectory);
				if (!Directory.Exists(workingDirectory)) {
					return Task.FromResult(new DebugResult(1004, "launch: workingDirectory '{path}' does not exist", new { path = workingDirectory }));
				}
			}
			workingDirectory = null;	// TODO@AW Why?

			// validate argument 'runtimeExecutable'
			var runtimeExecutable = (string)args.runtimeExecutable;
			if (runtimeExecutable != null) {
				runtimeExecutable = runtimeExecutable.Trim();
				if (runtimeExecutable.Length == 0) {
					return Task.FromResult(new DebugResult(1005, "launch: property 'runtimeExecutable' is empty"));
				}
				runtimeExecutable = ConvertClientPathToDebugger(runtimeExecutable);
				if (!File.Exists(runtimeExecutable)) {
					return Task.FromResult(new DebugResult(1006, "launch: runtimeExecutable '{path}' does not exist", new { path = runtimeExecutable }));
				}
			}

			// validate argument 'runtimeArgs'
			string[] runtimeArguments = null;
			if (args.runtimeArgs != null) {
				runtimeArguments = args.runtimeArgs.ToObject<string[]>();
				if (runtimeArguments != null && runtimeArguments.Length == 0) {
					runtimeArguments = null;
				}
			}

			// validate argument 'env'
			Dictionary<string, string> env = null;
			var environmentVariables = args.env;
			if (environmentVariables != null) {
				env = new Dictionary<string, string>();
				foreach (var entry in environmentVariables) {
					env.Add((string)entry.Name, (string)entry.Value);
				}
				if (env.Count == 0) {
					env = null;
				}
			}

			bool externalConsole = true;
			try {
				externalConsole = (bool)args["externalConsole"];
			}
			catch (RuntimeBinderException) {
				// ignore
			}
				
			if (externalConsole && (Utilities.IsOSX() || Utilities.IsLinux())) {
				const string host = "127.0.0.1";
				int port = Utilities.FindFreePort(55555);

				string mono_path = runtimeExecutable;
				if (mono_path == null) {
					if (!Terminal.IsOnPath(MONO)) {
						return Task.FromResult(new DebugResult(3001, "launch: can't find runtime '{_runtime}' on PATH", new { _runtime = MONO }));
					}
{'fraction_non_alphanumeric': 0.14914346025817976, 'fraction_numerical': 0.008443614516521881, 'mean_word_length': 3.308499475341028, 'pattern_counts': {'":': 14, '<': 29, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, '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': '2451672', 'n_tokens_mistral': 4346, 'n_tokens_neox': 3441, 'n_words': 868}