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:
Diffstat (limited to 'main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeObjectSource.cs')
-rw-r--r--main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeObjectSource.cs410
1 files changed, 243 insertions, 167 deletions
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 3da1def7ef..a8a84c86fb 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,71 @@
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 static string GetActualTypeName (string type)
{
int startIndex;
@@ -51,17 +85,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 +114,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 +216,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);
-
- if (endIndex != -1) {
- // The value a bitwise-or'd set of enum values
- var expanded = new StringBuilder ();
- int index = 0;
+ } else if (IsEnum (displayType, value)) {
+ int endIndex = value.IndexOf (" | ", StringComparison.Ordinal);
- while (index < value.Length) {
- endIndex = value.IndexOf (" | ", index, StringComparison.Ordinal);
- string enumValue;
+ if (endIndex != -1) {
+ // The value is a bitwise-or'd set of enum values
+ var expanded = new StringBuilder ();
+ int index = 0;
- 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, 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);
}
}
}