diff options
author | Jeffrey Stedfast <jestedfa@microsoft.com> | 2019-05-14 19:27:45 +0300 |
---|---|---|
committer | Jeffrey Stedfast <jestedfa@microsoft.com> | 2019-05-20 20:14:28 +0300 |
commit | 22e9afa19bbd85d24588876363b6f293760ee570 (patch) | |
tree | 914adc9f65d0e4a600c74d4e0e60b618cf5155ba /main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol | |
parent | 70ac70a3596b1fc5972510133fa700edecc49dd6 (diff) |
[VSCodeDebugProtocol] Fix up the values used when creating ObjectValues to match Sdb/Cor backends
Diffstat (limited to 'main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol')
4 files changed, 296 insertions, 33 deletions
diff --git a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeDebuggerSession.cs b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeDebuggerSession.cs index 4d92b280ba..be1189696e 100644 --- a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeDebuggerSession.cs +++ b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeDebuggerSession.cs @@ -109,10 +109,23 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol var source = new Source { Name = Path.GetFileName (fileName), Path = fileName }; var request = new GotoTargetsRequest (source, line) { Column = column }; var response = protocolClient.SendRequestSync (request); - var target = response.Targets.FirstOrDefault (x => x.Line <= line && x.EndLine >= line && x.Column <= column && x.EndColumn >= column); + GotoTarget target = null; + + foreach (var location in response.Targets) { + if (location.Line <= line && location.EndLine >= line && location.Column <= column && location.EndColumn >= column) { + // exact match for location + target = location; + break; + } + + if (target == null) { + // closest match so far... + target = location; + } + } if (target == null) - throw new NotSupportedException (); + throw new NotImplementedException (); protocolClient.SendRequestSync (new GotoRequest ((int) threadId, target.Id)); RaiseStopEvent (); diff --git a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeBacktrace.cs b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeBacktrace.cs index 28344c4949..9a7c611876 100644 --- a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeBacktrace.cs +++ b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeBacktrace.cs @@ -1,9 +1,12 @@ using System; -using System.Collections.Generic; using System.Linq; +using System.Collections.Generic; + using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages; + using Mono.Debugging.Backend; using Mono.Debugging.Client; + using VsFormat = Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages.StackFrameFormat; namespace MonoDevelop.Debugger.VsCodeDebugProtocol diff --git a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeObjectSource.cs b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeObjectSource.cs index d98418ec98..19c8c4fa77 100644 --- a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeObjectSource.cs +++ b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeObjectSource.cs @@ -1,45 +1,280 @@ using System; using System.Linq; +using System.Text; +using System.Globalization; + using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages; + using Mono.Debugging.Backend; using Mono.Debugging.Client; +using Mono.Debugging.Evaluation; namespace MonoDevelop.Debugger.VsCodeDebugProtocol { class VSCodeObjectSource : IObjectValueSource { + const VariablePresentationHint.AttributesValue ConstantReadOnlyStatic = VariablePresentationHint.AttributesValue.Constant | VariablePresentationHint.AttributesValue.ReadOnly | VariablePresentationHint.AttributesValue.Static; + static readonly char[] CommaDotOrSquareEndBracket = { ',', '.', ']' }; + static readonly char[] CommaOrSquareEndBracket = { ',', ']' }; + static readonly char[] LessThanOrSquareBracket = { '<', '[' }; - int parentVariablesReference; - int variablesReference; - int frameId; - VSCodeDebuggerSession vsCodeDebuggerSession; - ObjectValue [] objValChildren; - readonly string name; + ObjectValue[] objValChildren; + + readonly VSCodeDebuggerSession vsCodeDebuggerSession; + readonly int parentVariablesReference; + readonly ObjectValueFlags flags; + readonly int variablesReference; + readonly int frameId; readonly string evalName; + readonly string display; + readonly string name; readonly string type; readonly string val; + static string GetActualTypeName (string type) + { + int startIndex; + + if (type == null) + return string.Empty; + + if ((startIndex = type.IndexOf (" {", StringComparison.Ordinal)) != -1) { + // The type is boxed. The string between the {}'s is the actual type name. + int endIndex = type.LastIndexOf ('}'); + + startIndex += 2; + + if (endIndex > startIndex) + return type.Substring (startIndex, endIndex - startIndex); + } + + return type; + } + + static string GetFixedVariableName (string name) + { + // Check for a type attribute and strip it off. + var index = name.LastIndexOf (" [", StringComparison.Ordinal); + + if (index != -1) + return name.Remove (index); + + return name; + } + + static bool IsMultiDimensionalArray (string type, out int arrayIndexer) + { + int index = type.IndexOfAny (LessThanOrSquareBracket); + + arrayIndexer = -1; + + if (index == -1) + return false; + + if (type[index] == '<') { + int depth = 1; + + index++; + while (index < type.Length && depth > 0) { + switch (type[index++]) { + case '<': depth++; break; + case '>': depth--; break; + } + } + + if (index >= type.Length || type[index] != '[') + return false; + } + + arrayIndexer = index++; + + return index < type.Length && type[index] == ','; + } + + // Note: displayType will often have spaces after commas + string GetFixedValue (string value, string canonType, string displayType) + { + int arrayIndex; + + if (IsMultiDimensionalArray (displayType, out arrayIndex)) { + var arrayType = displayType.Substring (0, arrayIndex); + var prefix = $"{{{arrayType}["; + + if (value.StartsWith (prefix, StringComparison.Ordinal)) { + var compacted = new StringBuilder (prefix.Replace (", ", ",")); + int index = prefix.Length; + + while (index < value.Length) { + int endIndex = value.IndexOfAny (CommaDotOrSquareEndBracket, index); + string number; + + if (endIndex == -1) + return value; + + if (endIndex + 1 < value.Length && value[endIndex] == '.' && value[endIndex + 1] == '.') { + int min, max; + + number = value.Substring (index, endIndex - index); + + if (!int.TryParse (number, NumberStyles.Integer, CultureInfo.InvariantCulture, out min)) + return value; + + index = endIndex + 2; + + if ((endIndex = value.IndexOfAny (CommaOrSquareEndBracket, index)) == -1) + return value; + + number = value.Substring (index, endIndex - index); + + if (!int.TryParse (number, NumberStyles.Integer, CultureInfo.InvariantCulture, out max)) + return value; + + compacted.Append (((max - min) + 1).ToString (CultureInfo.InvariantCulture)); + } else { + compacted.Append (value, index, endIndex - index); + } + + compacted.Append (value[endIndex]); + index = endIndex + 1; + + if (value[endIndex] == ']') + break; + + if (index < value.Length && value[index] == ' ') + index++; + } + + compacted.Append ('}'); + + return compacted.ToString (); + } + } else if (canonType == "char") { + int startIndex = value.IndexOf ('\''); + + if (startIndex != -1) + return value.Substring (startIndex); + } else { + var request = new EvaluateRequest ($"typeof ({displayType}).IsEnum") { FrameId = frameId }; + var result = vsCodeDebuggerSession.protocolClient.SendRequestSync (request); + + if (result.Result.Equals ("true", StringComparison.OrdinalIgnoreCase)) { + int endIndex = value.IndexOf (" | ", StringComparison.Ordinal); + + if (endIndex != -1) { + // The value a bitwise-or'd set of enum values + var expanded = new StringBuilder (); + int index = 0; + + while (index < value.Length) { + endIndex = value.IndexOf (" | ", index, StringComparison.Ordinal); + string enumValue; + + if (endIndex != -1) + enumValue = value.Substring (index, endIndex - index); + else if (index > 0) + enumValue = value.Substring (index); + else + enumValue = value; + + expanded.Append (canonType).Append ('.').Append (enumValue); + + if (endIndex == -1) + break; + + expanded.Append (" | "); + index = endIndex + 3; + } + + return expanded.ToString (); + } + + return canonType + "." + value; + } + } + + return value; + } + + static bool IsCSError (int code, string message, string value, out string newValue) + { + var prefix = string.Format (CultureInfo.InvariantCulture, "error CS{0:D4}: '", code); + + newValue = null; + + if (value == null || !value.StartsWith (prefix, StringComparison.Ordinal)) + return false; + + int startIndex = prefix.Length; + int index = startIndex; + + while (index < value.Length && value[index] != '\'') + index++; + + newValue = value.Substring (startIndex, index - startIndex); + index++; + + if (index >= value.Length || value[index] != ' ') + return false; + + index++; + + if (index + message.Length != value.Length) + return false; + + return string.CompareOrdinal (value, index, message, 0, message.Length) == 0; + } public VSCodeObjectSource (VSCodeDebuggerSession vsCodeDebuggerSession, int variablesReference, int parentVariablesReference, string name, string type, string evalName, int frameId, string val) { - this.type = type ?? string.Empty; - this.frameId = frameId; - this.evalName = evalName; - var indexOfType = name.LastIndexOf (" [", StringComparison.Ordinal); - if (indexOfType != -1) - name = name.Remove (indexOfType); - this.name = name; this.vsCodeDebuggerSession = vsCodeDebuggerSession; - this.variablesReference = variablesReference; this.parentVariablesReference = parentVariablesReference; - this.val = val; + this.variablesReference = variablesReference; + this.evalName = evalName; + this.frameId = frameId; + + if (type == null) { + if (IsCSError (118, "is a namespace but is used like a variable", val, out string ns)) { + this.display = this.name = this.val = ns; + this.flags = ObjectValueFlags.Namespace; + this.type = "<namespace>"; + return; + } + + if (IsCSError (119, "is a type, which is not valid in the given context", val, out string vtype)) { + if (name.StartsWith ("global::", StringComparison.Ordinal)) + vtype = name.Substring ("global::".Length); + + this.display = this.name = this.val = ObjectValueAdaptor.GetCSharpTypeName (vtype); + this.flags = ObjectValueFlags.Type; + this.type = "<type>"; + return; + } + } + + var actualType = GetActualTypeName (type); + + this.flags = parentVariablesReference > 0 ? ObjectValueFlags.None : ObjectValueFlags.ReadOnly; + this.type = actualType.Replace (", ", ","); + this.name = GetFixedVariableName (name); + + if (actualType != "void") + this.val = GetFixedValue (val, this.type, actualType); + else + this.val = "No return value."; + this.display = val; + + if (this.name[0] == '[') + flags |= ObjectValueFlags.ArrayElement; + + if (type == null || val == $"'{this.name}' threw an exception of type '{this.type}'") + flags |= ObjectValueFlags.Error; } - public ObjectValue [] GetChildren (ObjectPath path, int index, int count, EvaluationOptions options) + public ObjectValue[] GetChildren (ObjectPath path, int index, int count, EvaluationOptions options) { if (objValChildren == null) { if (variablesReference <= 0) { - objValChildren = new ObjectValue [0]; + objValChildren = new ObjectValue[0]; } else { using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) { var children = vsCodeDebuggerSession.protocolClient.SendRequestSync (new VariablesRequest ( @@ -82,27 +317,30 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol public object GetRawValue (ObjectPath path, EvaluationOptions options) { - string val = null; + string rawValue = null; + using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) { - val = vsCodeDebuggerSession.protocolClient.SendRequestSync (new EvaluateRequest (evalName) { FrameId = frameId }).Result; + rawValue = vsCodeDebuggerSession.protocolClient.SendRequestSync (new EvaluateRequest (evalName) { FrameId = frameId }).Result; timer.Success = true; } - if (val.StartsWith ("\"", StringComparison.Ordinal)) + + if (rawValue.StartsWith ("\"", StringComparison.Ordinal)) { if (options.ChunkRawStrings) - return new RawValueString (new RawString (val)); - else - return val.Remove (val.Length - 1).Remove (0, 1); - else - throw new NotImplementedException (); + return new RawValueString (new RawString (rawValue)); + + return rawValue.Substring (1, rawValue.Length - 2); + } + + throw new NotImplementedException (); } public ObjectValue GetValue (ObjectPath path, EvaluationOptions options) { if (val == "null") - return ObjectValue.CreateNullObject (this, name, type, parentVariablesReference > 0 ? ObjectValueFlags.None : ObjectValueFlags.ReadOnly); + return ObjectValue.CreateNullObject (this, name, type, flags); if (variablesReference == 0)//This is some kind of primitive... - return ObjectValue.CreatePrimitive (this, new ObjectPath (name), type, new EvaluationResult (val), parentVariablesReference > 0 ? ObjectValueFlags.None : ObjectValueFlags.ReadOnly); - return ObjectValue.CreateObject (this, new ObjectPath (name), type, new EvaluationResult (val), parentVariablesReference > 0 ? ObjectValueFlags.None : ObjectValueFlags.ReadOnly, null); + return ObjectValue.CreatePrimitive (this, new ObjectPath (name), type, new EvaluationResult (val, display), flags); + return ObjectValue.CreateObject (this, new ObjectPath (name), type, new EvaluationResult (val, display), flags, null); } public void SetRawValue (ObjectPath path, object value, EvaluationOptions options) diff --git a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeStackFrame.cs b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeStackFrame.cs index 833ab92c89..09930da9ee 100644 --- a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeStackFrame.cs +++ b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeStackFrame.cs @@ -1,7 +1,11 @@ using System; using System.Linq; + using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages; + using Mono.Debugging.Client; + +using VsStackFrame = Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages.StackFrame; using VsFormat = Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages.StackFrameFormat; namespace MonoDevelop.Debugger.VsCodeDebugProtocol @@ -33,14 +37,19 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol return null; } + static SourceLocation GetSourceLocation (VsStackFrame frame) + { + return new SourceLocation (frame.Name, frame.Source?.Path, frame.Line, frame.Column, frame.EndLine ?? -1, frame.EndColumn ?? -1, GetHashBytes (frame.Source)); + } + VsFormat format; readonly int threadId; readonly int frameIndex; internal readonly int frameId; string fullStackframeText; - public VsCodeStackFrame (VsFormat format, int threadId, int frameIndex, Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages.StackFrame frame) - : base (0, new SourceLocation (frame.Name, frame.Source?.Path, frame.Line, frame.Column, frame.EndLine ?? -1, frame.EndColumn ?? -1, GetHashBytes (frame.Source)), GetLanguage (frame.Source?.Path)) + public VsCodeStackFrame (VsFormat format, int threadId, int frameIndex, VsStackFrame frame) + : base (0, GetSourceLocation (frame), GetLanguage (frame.Source?.Path)) { this.format = format; this.threadId = threadId; |