Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Munn <gregm@microsoft.com>2020-01-15 22:15:28 +0300
committerGitHub <noreply@github.com>2020-01-15 22:15:28 +0300
commitc942e40c22cd72f63ce08d30a1c719652f110c56 (patch)
tree007eb2825ed0a738ca82da9e498e7e5e8ad5651c
parent3aa56f3ac55338ea18f82b5a0ea4a1fbaf2d6fec (diff)
parent7ebc7323317450151d9eab728ff271d76c6fe5cd (diff)
Merge pull request #9522 from mono/jstedfast-vscodedebugger-fixes-8.4
[release-8.4] [VsCodeDebugger] various fixes
-rw-r--r--main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol.csproj2
-rw-r--r--main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeDebuggerSession.cs46
-rw-r--r--main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeEvaluationSource.cs117
-rw-r--r--main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeVariableSource.cs76
-rw-r--r--main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeBacktrace.cs150
-rw-r--r--main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeObjectSource.cs468
6 files changed, 628 insertions, 231 deletions
diff --git a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol.csproj b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol.csproj
index abfc017abb..cffd4ddd46 100644
--- a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol.csproj
+++ b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol.csproj
@@ -48,6 +48,8 @@
<Compile Include="VsCodeStackFrame.cs" />
<Compile Include="VsCodeObjectSource.cs" />
<Compile Include="VsCodeBacktrace.cs" />
+ <Compile Include="VSCodeVariableSource.cs" />
+ <Compile Include="VSCodeEvaluationSource.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
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 5da7eda093..15d16030d7 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
@@ -23,20 +23,23 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+
using System;
-using Mono.Debugging.Client;
-using System.Diagnostics;
-using Mono.Debugging.Backend;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
using System.IO;
+using System.Linq;
using System.Text;
-using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages;
+using System.Diagnostics;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+using Mono.Debugging.Client;
+
using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol;
-using System.Threading;
+using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages;
+
using MonoDevelop.Core;
using MonoDevelop.Core.Execution;
+
using MonoFunctionBreakpoint = Mono.Debugging.Client.FunctionBreakpoint;
using VsCodeFunctionBreakpoint = Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages.FunctionBreakpoint;
@@ -44,8 +47,23 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol
{
public abstract class VSCodeDebuggerSession : DebuggerSession
{
+ readonly Dictionary<string, bool> enumTypes = new Dictionary<string, bool> ();
int currentThreadId;
+ internal bool IsEnum (string type, int frameId)
+ {
+ if (enumTypes.TryGetValue (type, out var isEnum))
+ return isEnum;
+
+ var request = new EvaluateRequest ($"typeof ({type}).IsEnum") { FrameId = frameId };
+ var response = protocolClient.SendRequestSync (request);
+
+ isEnum = response.Result.Equals ("true", StringComparison.OrdinalIgnoreCase);
+ enumTypes.Add (type, isEnum);
+
+ return isEnum;
+ }
+
protected override void OnContinue ()
{
protocolClient.SendRequestSync (new ContinueRequest (currentThreadId));
@@ -227,7 +245,7 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol
startInfo.StandardOutputEncoding = Encoding.UTF8;
startInfo.StandardOutputEncoding = Encoding.UTF8;
startInfo.UseShellExecute = false;
- if (!MonoDevelop.Core.Platform.IsWindows)
+ if (!Platform.IsWindows)
startInfo.EnvironmentVariables ["PATH"] = Environment.GetEnvironmentVariable ("PATH") + ":/usr/local/share/dotnet/";
debugAgentProcess = Process.Start (startInfo);
debugAgentProcess.EnableRaisingEvents = true;
@@ -236,7 +254,7 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol
protocolClient.RequestReceived += OnDebugAdaptorRequestReceived;
protocolClient.Run ();
protocolClient.EventReceived += HandleEvent;
- InitializeRequest initRequest = CreateInitRequest ();
+ var initRequest = CreateInitRequest ();
Capabilities = protocolClient.SendRequestSync (initRequest);
}
@@ -265,7 +283,7 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol
{
pauseWhenFinished = !startInfo.CloseExternalConsoleOnExit;
StartDebugAgent ();
- LaunchRequest launchRequest = CreateLaunchRequest (startInfo);
+ var launchRequest = CreateLaunchRequest (startInfo);
protocolClient.SendRequestSync (launchRequest);
protocolClient.SendRequestSync (new ConfigurationDoneRequest ());
}
@@ -316,7 +334,7 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol
bool? EvaluateCondition (int frameId, string exp)
{
- var response = protocolClient.SendRequestSync (new EvaluateRequest (exp, frameId)).Result;
+ var response = protocolClient.SendRequestSync (new EvaluateRequest (exp) { FrameId = frameId }).Result;
if (bool.TryParse (response, out var result))
return result;
@@ -455,7 +473,7 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol
}
break;
}
- });
+ }).Ignore ();
}
List<string> pathsWithBreakpoints = new List<string> ();
@@ -516,7 +534,7 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol
if (obj.Breakpoints [i].Line != sourceFile.ElementAt (i).OriginalLine)
breakpoints [sourceFile.ElementAt (i)].AdjustBreakpointLocation (obj.Breakpoints [i].Line, obj.Breakpoints [i].Column ?? 1);
}
- });
+ }).Ignore ();
});
}
diff --git a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeEvaluationSource.cs b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeEvaluationSource.cs
new file mode 100644
index 0000000000..235b7adaa2
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeEvaluationSource.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Globalization;
+
+using Mono.Debugging.Client;
+using Mono.Debugging.Evaluation;
+
+using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages;
+
+namespace MonoDevelop.Debugger.VsCodeDebugProtocol
+{
+ class VSCodeEvaluationSource : VSCodeObjectSource
+ {
+ readonly EvaluateResponse response;
+ readonly string expression;
+ readonly string display;
+ readonly string value;
+ readonly string name;
+ readonly string type;
+
+ public VSCodeEvaluationSource (VSCodeDebuggerSession session, string expression, EvaluateResponse response, int frameId) : base (session, 0, frameId)
+ {
+ this.expression = expression;
+ this.response = response;
+
+ // FIXME: can we use PresentationHint.Attributes == VariablePresentationHint.AttributesValue.FailedEvaluation instead?
+ if (response.Type == null) {
+ if (IsCSError (118, "is a namespace but is used like a variable", response.Result, out string ns)) {
+ Flags = ObjectValueFlags.Namespace;
+ display = name = value = ns;
+ type = "<namespace>";
+ return;
+ }
+
+ if (IsCSError (119, "is a type, which is not valid in the given context", response.Result, out string vtype)) {
+ if (expression.StartsWith ("global::", StringComparison.Ordinal))
+ vtype = expression.Substring ("global::".Length);
+
+ display = name = value = ObjectValueAdaptor.GetCSharpTypeName (vtype);
+ Flags = ObjectValueFlags.Type;
+ type = "<type>";
+ return;
+ }
+ }
+
+ var actualType = GetActualTypeName (response.Type);
+
+ Flags = GetFlags (response.PresentationHint);
+ type = actualType.Replace (", ", ",");
+ name = expression;
+
+ if (actualType != "void")
+ value = GetFixedValue (response.Result, type, actualType);
+ else
+ value = "No return value.";
+ display = response.Result;
+
+ if (name[0] == '[')
+ Flags |= ObjectValueFlags.ArrayElement;
+
+ if (type == null || value == $"'{name}' threw an exception of type '{type}'")
+ Flags = ObjectValueFlags.Error;
+ }
+
+ protected override string Display {
+ get { return display; }
+ }
+
+ protected override string Expression {
+ get { return expression; }
+ }
+
+ protected override string Name {
+ get { return name; }
+ }
+
+ protected override string Type {
+ get { return type; }
+ }
+
+ protected override string Value {
+ get { return value; }
+ }
+
+ protected override int VariablesReference {
+ get { return response.VariablesReference; }
+ }
+
+ 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;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeVariableSource.cs b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeVariableSource.cs
new file mode 100644
index 0000000000..789e4ef0c4
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeVariableSource.cs
@@ -0,0 +1,76 @@
+using System;
+
+using Mono.Debugging.Client;
+
+using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages;
+
+namespace MonoDevelop.Debugger.VsCodeDebugProtocol
+{
+ class VSCodeVariableSource : VSCodeObjectSource
+ {
+ readonly Variable variable;
+ readonly string display;
+ readonly string value;
+ readonly string name;
+ readonly string type;
+
+ public VSCodeVariableSource (VSCodeDebuggerSession session, Variable variable, int parentVariablesReference, int frameId) : base (session, parentVariablesReference, frameId)
+ {
+ this.variable = variable;
+
+ var actualType = GetActualTypeName (variable.Type);
+
+ Flags = parentVariablesReference > 0 ? ObjectValueFlags.None : ObjectValueFlags.ReadOnly;
+ Flags |= GetFlags (variable.PresentationHint);
+ name = GetFixedVariableName (variable.Name);
+ type = actualType.Replace (", ", ",");
+
+ if (actualType != "void")
+ value = GetFixedValue (variable.Value, type, actualType);
+ else
+ value = "No return value.";
+ display = variable.Value;
+
+ if (name[0] == '[')
+ Flags |= ObjectValueFlags.ArrayElement;
+
+ if (type == null || value == $"'{name}' threw an exception of type '{type}'")
+ Flags = ObjectValueFlags.Error;
+ }
+
+ protected override string Display {
+ get { return display; }
+ }
+
+ protected override string Expression {
+ get { return variable.EvaluateName; }
+ }
+
+ protected override string Name {
+ get { return name; }
+ }
+
+ protected override string Type {
+ get { return type; }
+ }
+
+ protected override string Value {
+ get { return value; }
+ }
+
+ protected override int VariablesReference {
+ get { return variable.VariablesReference; }
+ }
+
+ 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;
+ }
+ }
+}
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 9a7c611876..7fe678a1fe 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
@@ -2,39 +2,45 @@ using System;
using System.Linq;
using System.Collections.Generic;
+using Mono.Debugging.Client;
+using Mono.Debugging.Backend;
+
using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages;
-using Mono.Debugging.Backend;
-using Mono.Debugging.Client;
+using MonoDevelop.Core;
-using VsFormat = Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages.StackFrameFormat;
+using StackFrame = Mono.Debugging.Client.StackFrame;
+using VsStackFrame = Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages.StackFrame;
+using VsFrameFormat = Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages.StackFrameFormat;
namespace MonoDevelop.Debugger.VsCodeDebugProtocol
{
class VSCodeDebuggerBacktrace : IBacktrace
{
+ readonly VSCodeDebuggerSession session;
+ readonly VsStackFrame[] frames;
+ readonly List<Scope>[] scopes;
+ readonly VsFrameFormat format;
readonly int threadId;
- VSCodeDebuggerSession vsCodeDebuggerSession;
- int totalFramesCount;
- Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages.StackFrame [] frames;
- VsFormat frame0Format;
- public VSCodeDebuggerBacktrace (VSCodeDebuggerSession vsCodeDebuggerSession, int threadId)
+ public VSCodeDebuggerBacktrace (VSCodeDebuggerSession session, int threadId)
{
+ this.session = session;
this.threadId = threadId;
- this.vsCodeDebuggerSession = vsCodeDebuggerSession;
- frame0Format = VsCodeStackFrame.GetStackFrameFormat (vsCodeDebuggerSession.EvaluationOptions);
- var body = vsCodeDebuggerSession.protocolClient.SendRequestSync (new StackTraceRequest (threadId) { StartFrame = 0, Levels = 1, Format = frame0Format });
- totalFramesCount = body.TotalFrames ?? 0;
- frames = new Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages.StackFrame [totalFramesCount];
- if (totalFramesCount > 0 && body.StackFrames.Count > 0)
- frames [0] = body.StackFrames [0];
+
+ format = VsCodeStackFrame.GetStackFrameFormat (session.EvaluationOptions);
+
+ var response = session.protocolClient.SendRequestSync (new StackTraceRequest (threadId) { StartFrame = 0, Levels = 1, Format = format });
+
+ FrameCount = response.TotalFrames ?? 0;
+ frames = new VsStackFrame[FrameCount];
+ scopes = new List<Scope>[FrameCount];
+ if (FrameCount > 0 && response.StackFrames.Count > 0)
+ frames[0] = response.StackFrames[0];
}
public int FrameCount {
- get {
- return totalFramesCount;
- }
+ get; private set;
}
public AssemblyLine [] Disassemble (int frameIndex, int firstLine, int count)
@@ -42,19 +48,42 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol
throw new NotImplementedException ();
}
+ List<Scope> GetScopes (int frameIndex)
+ {
+ if (scopes[frameIndex] == null) {
+ var response = session.protocolClient.SendRequestSync (new ScopesRequest (frames[frameIndex].Id));
+ scopes[frameIndex] = response.Scopes;
+ }
+
+ return scopes[frameIndex];
+ }
+
public ObjectValue [] GetAllLocals (int frameIndex, EvaluationOptions options)
{
- List<ObjectValue> results = new List<ObjectValue> ();
- var scopeBody = vsCodeDebuggerSession.protocolClient.SendRequestSync (new ScopesRequest (frames [frameIndex].Id));
- foreach (var variablesGroup in scopeBody.Scopes) {
- using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) {
- var varibles = vsCodeDebuggerSession.protocolClient.SendRequestSync (new VariablesRequest (variablesGroup.VariablesReference));
- foreach (var variable in varibles.Variables) {
- results.Add (VsCodeVariableToObjectValue (vsCodeDebuggerSession, variable.Name, variable.EvaluateName, variable.Type, variable.Value, variable.VariablesReference, variablesGroup.VariablesReference, frames [frameIndex].Id));
+ var results = new List<ObjectValue> ();
+ var frame = frames[frameIndex];
+
+ foreach (var scope in GetScopes (frameIndex)) {
+ using (var timer = session.EvaluationStats.StartTimer ()) {
+ VariablesResponse response;
+
+ try {
+ response = session.protocolClient.SendRequestSync (new VariablesRequest (scope.VariablesReference));
+ } catch (Exception ex) {
+ LoggingService.LogError ($"[VsCodeDebugger] Failed to get local variables for the scope: {scope.Name}", ex);
+ timer.Success = false;
+ continue;
+ }
+
+ foreach (var variable in response.Variables) {
+ var source = new VSCodeVariableSource (session, variable, scope.VariablesReference, frame.Id);
+ results.Add (source.GetValue (default (ObjectPath), null));
}
+
timer.Success = true;
}
}
+
return results.ToArray ();
}
@@ -71,54 +100,75 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol
public ObjectValue [] GetExpressionValues (int frameIndex, string [] expressions, EvaluationOptions options)
{
var results = new List<ObjectValue> ();
+ var frame = frames[frameIndex];
+
foreach (var expr in expressions) {
- using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) {
- var responseBody = vsCodeDebuggerSession.protocolClient.SendRequestSync (new EvaluateRequest (expr) { FrameId = frames[frameIndex].Id });
- results.Add (VsCodeVariableToObjectValue (vsCodeDebuggerSession, expr, expr, responseBody.Type, responseBody.Result, responseBody.VariablesReference, 0, frames [frameIndex].Id));
+ using (var timer = session.EvaluationStats.StartTimer ()) {
+ var response = session.protocolClient.SendRequestSync (new EvaluateRequest (expr) { FrameId = frame.Id });
+ var source = new VSCodeEvaluationSource (session, expr, response, frame.Id);
+
+ results.Add (source.GetValue (default (ObjectPath), null));
timer.Success = true;
}
}
+
return results.ToArray ();
}
- internal static ObjectValue VsCodeVariableToObjectValue (VSCodeDebuggerSession vsCodeDebuggerSession, string name, string evalName, string type, string value, int variablesReference, int parentVariablesReference, int frameId)
+ ObjectValue[] GetVariables (int frameIndex, string scopeName)
{
- return new VSCodeObjectSource (vsCodeDebuggerSession, variablesReference, parentVariablesReference, name, type, evalName, frameId, value).GetValue (default (ObjectPath), null);
- }
+ var results = new List<ObjectValue> ();
+ var frame = frames[frameIndex];
- public ObjectValue [] GetLocalVariables (int frameIndex, EvaluationOptions options)
- {
- List<ObjectValue> results = new List<ObjectValue> ();
- var scopeBody = vsCodeDebuggerSession.protocolClient.SendRequestSync (new ScopesRequest (frames [frameIndex].Id));
- foreach (var variablesGroup in scopeBody.Scopes) {
- using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) {
- var varibles = vsCodeDebuggerSession.protocolClient.SendRequestSync (new VariablesRequest (variablesGroup.VariablesReference));
- foreach (var variable in varibles.Variables) {
- results.Add (ObjectValue.CreatePrimitive (null, new ObjectPath (variable.Name), variable.Type ?? "<unknown>", new EvaluationResult (variable.Value), ObjectValueFlags.None));
+ foreach (var scope in GetScopes (frameIndex)) {
+ if (!scope.Name.Equals (scopeName, StringComparison.Ordinal))
+ continue;
+
+ using (var timer = session.EvaluationStats.StartTimer ()) {
+ VariablesResponse response;
+
+ try {
+ response = session.protocolClient.SendRequestSync (new VariablesRequest (scope.VariablesReference));
+ } catch (Exception ex) {
+ LoggingService.LogError ($"[VsCodeDebugger] Failed to get local variables for the scope: {scope.Name}", ex);
+ timer.Success = false;
+ continue;
+ }
+
+ foreach (var variable in response.Variables) {
+ var source = new VSCodeVariableSource (session, variable, scope.VariablesReference, frame.Id);
+ results.Add (source.GetValue (default (ObjectPath), null));
}
+
timer.Success = true;
}
}
+
return results.ToArray ();
}
+ public ObjectValue [] GetLocalVariables (int frameIndex, EvaluationOptions options)
+ {
+ return GetVariables (frameIndex, "Locals");
+ }
+
public ObjectValue [] GetParameters (int frameIndex, EvaluationOptions options)
{
- return new ObjectValue [0];//TODO: Find out how to seperate Params from other Locals
+ return GetVariables (frameIndex, "Arguments");
}
- public Mono.Debugging.Client.StackFrame [] GetStackFrames (int firstIndex, int lastIndex)
+ public StackFrame [] GetStackFrames (int firstIndex, int lastIndex)
{
//Optimisation for getting 1st frame of thread(used for ThreadPad)
- if (firstIndex == 0 && lastIndex == 1 && totalFramesCount > 0) {
- return new Mono.Debugging.Client.StackFrame [] { new VsCodeStackFrame (frame0Format, threadId, 0, frames [0]) };
- }
- var stackFrames = new Mono.Debugging.Client.StackFrame [Math.Min (lastIndex - firstIndex, totalFramesCount - firstIndex)];
- var format = VsCodeStackFrame.GetStackFrameFormat (vsCodeDebuggerSession.EvaluationOptions);
- var body = vsCodeDebuggerSession.protocolClient.SendRequestSync (new StackTraceRequest (threadId) { StartFrame = firstIndex, Levels = stackFrames.Length, Format = format });
+ if (firstIndex == 0 && lastIndex == 1 && FrameCount > 0)
+ return new StackFrame[] { new VsCodeStackFrame (this.format, threadId, 0, frames[0]) };
+
+ var stackFrames = new StackFrame [Math.Min (lastIndex - firstIndex, FrameCount - firstIndex)];
+ var format = VsCodeStackFrame.GetStackFrameFormat (session.EvaluationOptions);
+ var body = session.protocolClient.SendRequestSync (new StackTraceRequest (threadId) { StartFrame = firstIndex, Levels = stackFrames.Length, Format = format });
for (int i = 0; i < stackFrames.Length; i++) {
- frames [i + firstIndex] = body.StackFrames [i];
- stackFrames [i] = new VsCodeStackFrame (format, threadId, i, body.StackFrames [i]);
+ frames[i + firstIndex] = body.StackFrames [i];
+ stackFrames[i] = new VsCodeStackFrame (format, threadId, i, body.StackFrames [i]);
}
return stackFrames;
}
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 19c8c4fa77..bae1cf7637 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,37 +1,129 @@
using System;
-using System.Linq;
using System.Text;
using System.Globalization;
+using System.Collections.Generic;
-using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages;
-
-using Mono.Debugging.Backend;
using Mono.Debugging.Client;
-using Mono.Debugging.Evaluation;
+using Mono.Debugging.Backend;
+
+using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages;
namespace MonoDevelop.Debugger.VsCodeDebugProtocol
{
- class VSCodeObjectSource : IObjectValueSource
+ abstract 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 = { '<', '[' };
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)
+ protected VSCodeObjectSource (VSCodeDebuggerSession session, int parentVariablesReference, int frameId)
+ {
+ ParentVariablesReference = parentVariablesReference;
+ Session = session;
+ FrameId = frameId;
+ }
+
+ protected abstract string Display {
+ get;
+ }
+
+ protected abstract string Expression {
+ get;
+ }
+
+ protected ObjectValueFlags Flags {
+ get; set;
+ }
+
+ protected int FrameId {
+ get; private set;
+ }
+
+ protected abstract string Name {
+ get;
+ }
+
+ protected int ParentVariablesReference {
+ get;
+ }
+
+ protected VSCodeDebuggerSession Session {
+ get; private set;
+ }
+
+ protected abstract string Type {
+ get;
+ }
+
+ protected abstract string Value {
+ get;
+ }
+
+ protected abstract int VariablesReference {
+ get;
+ }
+
+ protected ObjectValueFlags GetFlags (VariablePresentationHint hint)
+ {
+ var flags = ObjectValueFlags.None;
+
+ if (hint != null) {
+ if (hint.Attributes.HasValue) {
+ var attributes = hint.Attributes.Value;
+
+ if ((attributes & VariablePresentationHint.AttributesValue.FailedEvaluation) != 0)
+ return ObjectValueFlags.Error;
+ if ((attributes & VariablePresentationHint.AttributesValue.Constant) != 0)
+ flags |= ObjectValueFlags.Literal;
+ if ((attributes & VariablePresentationHint.AttributesValue.ReadOnly) != 0)
+ flags |= ObjectValueFlags.ReadOnly;
+ if ((attributes & VariablePresentationHint.AttributesValue.Static) != 0)
+ flags |= ObjectValueFlags.ReadOnly;
+ if ((attributes & VariablePresentationHint.AttributesValue.CanHaveObjectId) != 0)
+ flags |= ObjectValueFlags.Primitive;
+ if ((attributes & VariablePresentationHint.AttributesValue.HasObjectId) != 0)
+ flags |= ObjectValueFlags.Object;
+ }
+
+ if (hint.Kind.HasValue) {
+ var kind = hint.Kind.Value;
+
+ if ((kind & VariablePresentationHint.KindValue.Property) != 0)
+ flags |= ObjectValueFlags.Property;
+ if ((kind & VariablePresentationHint.KindValue.BaseClass) != 0)
+ flags |= ObjectValueFlags.Type;
+ if ((kind & VariablePresentationHint.KindValue.Class) != 0)
+ flags |= ObjectValueFlags.Type;
+ if ((kind & VariablePresentationHint.KindValue.InnerClass) != 0)
+ flags |= ObjectValueFlags.Type;
+ if ((kind & VariablePresentationHint.KindValue.Interface) != 0)
+ flags |= ObjectValueFlags.Type;
+ if ((kind & VariablePresentationHint.KindValue.MostDerivedClass) != 0)
+ flags |= ObjectValueFlags.Type;
+ if ((kind & VariablePresentationHint.KindValue.Data) != 0)
+ flags |= ObjectValueFlags.Variable;
+ }
+
+ if (hint.Visibility.HasValue) {
+ var visibility = hint.Visibility.Value;
+
+ if ((visibility & VariablePresentationHint.VisibilityValue.Protected) != 0)
+ flags |= ObjectValueFlags.Protected;
+ if ((visibility & VariablePresentationHint.VisibilityValue.Internal) != 0)
+ flags |= ObjectValueFlags.Internal;
+ if ((visibility & VariablePresentationHint.VisibilityValue.Private) != 0)
+ flags |= ObjectValueFlags.Private;
+ if ((visibility & VariablePresentationHint.VisibilityValue.Public) != 0)
+ flags |= ObjectValueFlags.Public;
+ }
+ }
+
+ return flags;
+ }
+
+ protected static string GetActualTypeName (string type)
{
int startIndex;
@@ -51,17 +143,6 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol
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);
@@ -91,11 +172,56 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol
return index < type.Length && type[index] == ',';
}
+ bool IsPotentialEnumValue (string value)
+ {
+ if (string.IsNullOrEmpty (value))
+ return false;
+
+ // Note: An enum value must begin with a letter
+ if (!char.IsLetter (value[0]))
+ return false;
+
+ // Note: if the value has a '|', it's probably an enum (can it be anything else?)
+ if (value.IndexOf ('|') != -1)
+ return true;
+
+ for (int i = 1; i < value.Length; i++) {
+ if (char.IsLetterOrDigit (value[i]) || value[i] == '_')
+ continue;
+
+ return false;
+ }
+
+ return true;
+ }
+
+ bool IsEnum (string displayType, string value)
+ {
+ if (string.IsNullOrEmpty (displayType))
+ return false;
+
+ // Note: generic types cannot be enums
+ if (displayType[displayType.Length - 1] == '>')
+ return false;
+
+ // Note: true and false look like enum values but aren't
+ if (displayType.Equals ("bool", StringComparison.Ordinal))
+ return false;
+
+ if (!IsPotentialEnumValue (value))
+ return false;
+
+ return Session.IsEnum (displayType, FrameId);
+ }
+
// Note: displayType will often have spaces after commas
- string GetFixedValue (string value, string canonType, string displayType)
+ protected string GetFixedValue (string value, string canonType, string displayType)
{
int arrayIndex;
+ if (value.Equals ("null", StringComparison.Ordinal))
+ return value;
+
if (IsMultiDimensionalArray (displayType, out arrayIndex)) {
var arrayType = displayType.Substring (0, arrayIndex);
var prefix = $"{{{arrayType}[";
@@ -148,212 +274,220 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol
return compacted.ToString ();
}
- } else if (canonType == "char") {
+ } else if (canonType.Equals ("char", StringComparison.Ordinal)) {
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);
+ } else if (IsEnum (displayType, value)) {
+ 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;
+ if (endIndex != -1) {
+ // The value is 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;
+ while (index < value.Length) {
+ endIndex = value.IndexOf (" | ", index, StringComparison.Ordinal);
+ string enumValue;
- expanded.Append (canonType).Append ('.').Append (enumValue);
+ if (endIndex != -1)
+ enumValue = value.Substring (index, endIndex - index);
+ else if (index > 0)
+ enumValue = value.Substring (index);
+ else
+ enumValue = value;
- if (endIndex == -1)
- break;
+ expanded.Append (canonType).Append ('.').Append (enumValue);
- expanded.Append (" | ");
- index = endIndex + 3;
- }
+ if (endIndex == -1)
+ break;
- return expanded.ToString ();
+ expanded.Append (" | ");
+ index = endIndex + 3;
}
- return canonType + "." + value;
+ return expanded.ToString ();
}
+
+ return canonType + "." + value;
}
return value;
}
- static bool IsCSError (int code, string message, string value, out string newValue)
+ public ObjectValue[] GetChildren (ObjectPath path, int index, int count, EvaluationOptions options)
{
- 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 (objValChildren == null) {
+ if (VariablesReference > 0) {
+ using (var timer = Session.EvaluationStats.StartTimer ()) {
+ var response = Session.protocolClient.SendRequestSync (new VariablesRequest (VariablesReference));
+ var children = new List<ObjectValue> ();
+
+ foreach (var variable in response.Variables) {
+ var source = new VSCodeVariableSource (Session, variable, VariablesReference, FrameId);
+ children.Add (source.GetValue (default (ObjectPath), null));
+ }
- if (index + message.Length != value.Length)
- return false;
+ objValChildren = children.ToArray ();
+ timer.Success = true;
+ }
+ } else {
+ objValChildren = new ObjectValue[0];
+ }
+ }
- return string.CompareOrdinal (value, index, message, 0, message.Length) == 0;
+ return objValChildren;
}
- public VSCodeObjectSource (VSCodeDebuggerSession vsCodeDebuggerSession, int variablesReference, int parentVariablesReference, string name, string type, string evalName, int frameId, string val)
+ static string Quote (string text)
{
- this.vsCodeDebuggerSession = vsCodeDebuggerSession;
- this.parentVariablesReference = parentVariablesReference;
- 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 quoted = new StringBuilder (text.Length + 2);
+
+ quoted.Append ('"');
+ for (int i = 0; i < text.Length; i++) {
+ char c = text [i];
+
+ switch (c) {
+ case '\0': quoted.Append ("\\0"); break;
+ case '\a': quoted.Append ("\\a"); break;
+ case '\b': quoted.Append ("\\b"); break;
+ case '\n': quoted.Append ("\\n"); break;
+ case '\r': quoted.Append ("\\r"); break;
+ case '\t': quoted.Append ("\\t"); break;
+ case '\v': quoted.Append ("\\v"); break;
+ case '"': quoted.Append ("\\\""); break;
+ case '\\': quoted.Append ("\\\\"); break;
+ default:
+ if (c < ' ') {
+ quoted.AppendFormat (CultureInfo.InvariantCulture, "\\x{0:2}", c);
+ } else {
+ quoted.Append (c);
+ }
+ break;
}
}
+ quoted.Append ('"');
- 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;
+ return quoted.ToString ();
}
- public ObjectValue[] GetChildren (ObjectPath path, int index, int count, EvaluationOptions options)
+ static string Unquote (string text)
{
- if (objValChildren == null) {
- if (variablesReference <= 0) {
- objValChildren = new ObjectValue[0];
- } else {
- using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) {
- var children = vsCodeDebuggerSession.protocolClient.SendRequestSync (new VariablesRequest (
- variablesReference
- )).Variables;
- objValChildren = children.Select (c => VSCodeDebuggerBacktrace.VsCodeVariableToObjectValue (vsCodeDebuggerSession, c.Name, c.EvaluateName, c.Type, c.Value, c.VariablesReference, variablesReference, frameId)).ToArray ();
- timer.Success = true;
- }
+ var unquoted = new char [text.Length - 2];
+ bool escaped = false;
+ int count = 0;
+
+ for (int i = 1; i < text.Length - 1; i++) {
+ char c = text [i];
+
+ switch (c) {
+ case '\\':
+ if (escaped)
+ unquoted [count++] = '\\';
+ escaped = !escaped;
+ break;
+ case '0':
+ unquoted [count++] = escaped ? '\0' : c;
+ escaped = false;
+ break;
+ case 'a':
+ unquoted [count++] = escaped ? '\a' : c;
+ escaped = false;
+ break;
+ case 'b':
+ unquoted [count++] = escaped ? '\b' : c;
+ escaped = false;
+ break;
+ case 'n':
+ unquoted [count++] = escaped ? '\n' : c;
+ escaped = false;
+ break;
+ case 'r':
+ unquoted [count++] = escaped ? '\r' : c;
+ escaped = false;
+ break;
+ case 't':
+ unquoted [count++] = escaped ? '\t' : c;
+ escaped = false;
+ break;
+ case 'v':
+ unquoted [count++] = escaped ? '\v' : c;
+ escaped = false;
+ break;
+ default:
+ unquoted [count++] = c;
+ escaped = false;
+ break;
}
}
- return objValChildren;
+
+ return new string (unquoted, 0, count);
}
class RawString : IRawValueString
{
- string val;
-
- public RawString (string val)
+ public RawString (string value)
{
- this.val = val.Remove (val.Length - 1).Remove (0, 1);
+ Value = value;
}
public int Length {
- get {
- return val.Length;
- }
+ get { return Value.Length; }
}
public string Value {
- get {
- return val;
- }
+ get; private set;
}
public string Substring (int index, int length)
{
- return val.Substring (index, length);
+ return Value.Substring (index, length);
}
}
public object GetRawValue (ObjectPath path, EvaluationOptions options)
{
- string rawValue = null;
+ // Note: If the type is a string, then we already have the full value
+ if (Type.Equals ("string", StringComparison.Ordinal)) {
+ var rawValue = Unquote (Value);
- using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) {
- rawValue = vsCodeDebuggerSession.protocolClient.SendRequestSync (new EvaluateRequest (evalName) { FrameId = frameId }).Result;
- timer.Success = true;
+ return new RawValueString (new RawString (rawValue));
}
- if (rawValue.StartsWith ("\"", StringComparison.Ordinal)) {
- if (options.ChunkRawStrings)
- return new RawValueString (new RawString (rawValue));
-
- return rawValue.Substring (1, rawValue.Length - 2);
- }
+ //using (var timer = Session.EvaluationStats.StartTimer ()) {
+ // var response = Session.protocolClient.SendRequestSync (new EvaluateRequest (Expression) { FrameId = FrameId });
+ // var rawValue = response.Result;
+ // timer.Success = true;
+ //}
throw new NotImplementedException ();
}
public ObjectValue GetValue (ObjectPath path, EvaluationOptions options)
{
- if (val == "null")
- 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, display), flags);
- return ObjectValue.CreateObject (this, new ObjectPath (name), type, new EvaluationResult (val, display), flags, null);
+ if (Value == "null")
+ 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 (Value, Display), Flags);
+
+ return ObjectValue.CreateObject (this, new ObjectPath (Name), Type, new EvaluationResult (Value, Display), Flags, null);
}
public void SetRawValue (ObjectPath path, object value, EvaluationOptions options)
{
var v = value.ToString ();
- if (type == "string")
- v = $"\"{v}\"";
- vsCodeDebuggerSession.protocolClient.SendRequestSync (new SetVariableRequest (parentVariablesReference, name, v));
+
+ if (Type == "string")
+ v = Quote (v);
+
+ Session.protocolClient.SendRequestSync (new SetVariableRequest (ParentVariablesReference, Name, v));
}
public EvaluationResult SetValue (ObjectPath path, string value, EvaluationOptions options)
{
- return new EvaluationResult (vsCodeDebuggerSession.protocolClient.SendRequestSync (new SetVariableRequest (parentVariablesReference, name, value)).Value);
+ return new EvaluationResult (Session.protocolClient.SendRequestSync (new SetVariableRequest (ParentVariablesReference, Name, value)).Value);
}
}
}