newScript = script.ContinueWith(
string.Join("\n", replacer.variableDefinitions) + "\nreturn " + syntaxTree.ToString());
var state = await newScript.RunAsync(cancellationToken: token);
- return JObject.FromObject(ConvertCSharpToJSType(state.ReturnValue, state.ReturnValue?.GetType()));
+ return JObject.FromObject(ConvertCLRToJSType(state.ReturnValue));
}
catch (CompilationErrorException cee)
{
}
}
- private static readonly HashSet<Type> NumericTypes = new HashSet<Type>
+ private static JObject ConvertCLRToJSType(object v)
{
- typeof(decimal), typeof(byte), typeof(sbyte),
- typeof(short), typeof(ushort),
- typeof(int), typeof(uint),
- typeof(float), typeof(double)
- };
-
- private static object ConvertCSharpToJSType(object v, Type type)
- {
- if (v == null)
- return new { type = "object", subtype = "null", className = type?.ToString(), description = type?.ToString() };
- if (v is string s)
- return new { type = "string", value = s, description = s };
- if (v is char c)
- return new { type = "symbol", value = c, description = $"{(int)c} '{c}'" };
- if (NumericTypes.Contains(v.GetType()))
- return new { type = "number", value = v, description = Convert.ToDouble(v).ToString(CultureInfo.InvariantCulture) };
- if (v is bool)
- return new { type = "boolean", value = v, description = v.ToString().ToLowerInvariant(), className = type.ToString() };
- if (v is JObject)
- return v;
- return new { type = "object", value = v, description = v.ToString(), className = type.ToString() };
+ if (v is JObject jobj)
+ return jobj;
+
+ if (v is null)
+ return JObjectValueCreator.CreateNull("<unknown>")?["value"] as JObject;
+
+ string typeName = v.GetType().ToString();
+ jobj = JObjectValueCreator.CreateFromPrimitiveType(v);
+ return jobj is not null
+ ? jobj["value"] as JObject
+ : JObjectValueCreator.Create<object>(value: null,
+ type: "object",
+ description: v.ToString(),
+ className: typeName)?["value"] as JObject;
}
-
}
internal sealed class ReturnAsErrorException : Exception
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using BrowserDebugProxy;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json.Linq;
+
+namespace Microsoft.WebAssembly.Diagnostics;
+
+internal sealed class JObjectValueCreator
+{
+ private Dictionary<int, ValueTypeClass> _valueTypes = new();
+ private Dictionary<int, PointerValue> _pointerValues = new();
+ private readonly MonoSDBHelper _sdbAgent;
+ private readonly ILogger _logger;
+
+ public JObjectValueCreator(MonoSDBHelper sdbAgent, ILogger logger)
+ {
+ _sdbAgent = sdbAgent;
+ _logger = logger;
+ }
+
+ public static JObject Create<T>(T value,
+ string type,
+ string description,
+ string className = null,
+ string objectId = null,
+ string subtype = null,
+ bool writable = false,
+ bool isValueType = false,
+ bool isEnum = false)
+ {
+ var ret = JObject.FromObject(new
+ {
+ value = new
+ {
+ type,
+ value,
+ description
+ },
+ writable
+ });
+ if (className != null)
+ ret["value"]["className"] = className;
+ if (objectId != null)
+ ret["value"]["objectId"] = objectId;
+ if (subtype != null)
+ ret["value"]["subtype"] = subtype;
+ if (isValueType)
+ ret["value"]["isValueType"] = isValueType;
+ if (isEnum)
+ ret["value"]["isEnum"] = isEnum;
+ return ret;
+ }
+
+ public static JObject CreateFromPrimitiveType(object v)
+ => v switch
+ {
+ string s => Create(s, type: "string", description: s),
+ char c => CreateJObjectForChar(Convert.ToInt32(c)),
+ bool b => Create(b, type: "boolean", description: b ? "true" : "false", className: "System.Boolean"),
+
+ decimal or float or double or
+ byte or sbyte or
+ short or ushort or
+ int or uint or
+ long or ulong
+ => CreateJObjectForNumber(v),
+
+ _ => null
+ };
+
+ public static JObject CreateNull(string className!!)
+ => Create<object>(value: null,
+ type: "object",
+ description: className,
+ className: className,
+ subtype: "null");
+
+ public async Task<JObject> ReadAsVariableValue(
+ MonoBinaryReader retDebuggerCmdReader,
+ string name,
+ CancellationToken token,
+ bool isOwn = false,
+ int typeIdForObject = -1,
+ bool forDebuggerDisplayAttribute = false)
+ {
+ long initialPos = /*retDebuggerCmdReader == null ? 0 : */retDebuggerCmdReader.BaseStream.Position;
+ ElementType etype = (ElementType)retDebuggerCmdReader.ReadByte();
+ JObject ret = null;
+ switch (etype)
+ {
+ case ElementType.I:
+ case ElementType.U:
+ case ElementType.Void:
+ case (ElementType)ValueTypeId.VType:
+ case (ElementType)ValueTypeId.FixedArray:
+ ret = Create(value: "void", type: "void", description: "void");
+ break;
+ case ElementType.Boolean:
+ {
+ var value = retDebuggerCmdReader.ReadInt32();
+ ret = CreateFromPrimitiveType(value == 1);
+ break;
+ }
+ case ElementType.I1:
+ {
+ var value = retDebuggerCmdReader.ReadSByte();
+ ret = CreateJObjectForNumber<int>(value);
+ break;
+ }
+ case ElementType.I2:
+ case ElementType.I4:
+ {
+ var value = retDebuggerCmdReader.ReadInt32();
+ ret = CreateJObjectForNumber<int>(value);
+ break;
+ }
+ case ElementType.U1:
+ {
+ var value = retDebuggerCmdReader.ReadUByte();
+ ret = CreateJObjectForNumber<int>(value);
+ break;
+ }
+ case ElementType.U2:
+ {
+ var value = retDebuggerCmdReader.ReadUShort();
+ ret = CreateJObjectForNumber<int>(value);
+ break;
+ }
+ case ElementType.U4:
+ {
+ var value = retDebuggerCmdReader.ReadUInt32();
+ ret = CreateJObjectForNumber<uint>(value);
+ break;
+ }
+ case ElementType.R4:
+ {
+ float value = retDebuggerCmdReader.ReadSingle();
+ ret = CreateJObjectForNumber<float>(value);
+ break;
+ }
+ case ElementType.Char:
+ {
+ var value = retDebuggerCmdReader.ReadInt32();
+ ret = CreateJObjectForChar(value);
+ break;
+ }
+ case ElementType.I8:
+ {
+ long value = retDebuggerCmdReader.ReadInt64();
+ ret = CreateJObjectForNumber<long>(value);
+ break;
+ }
+ case ElementType.U8:
+ {
+ ulong value = retDebuggerCmdReader.ReadUInt64();
+ ret = CreateJObjectForNumber<ulong>(value);
+ break;
+ }
+ case ElementType.R8:
+ {
+ double value = retDebuggerCmdReader.ReadDouble();
+ ret = CreateJObjectForNumber<double>(value);
+ break;
+ }
+ case ElementType.FnPtr:
+ case ElementType.Ptr:
+ {
+ ret = await ReadAsPtrValue(etype, retDebuggerCmdReader, name, token);
+ break;
+ }
+ case ElementType.String:
+ {
+ var stringId = retDebuggerCmdReader.ReadInt32();
+ string value = await _sdbAgent.GetStringValue(stringId, token);
+ ret = CreateFromPrimitiveType(value);
+ break;
+ }
+ case ElementType.SzArray:
+ case ElementType.Array:
+ {
+ ret = await ReadAsArray(retDebuggerCmdReader, token);
+ break;
+ }
+ case ElementType.Class:
+ case ElementType.Object:
+ {
+ ret = await ReadAsObjectValue(retDebuggerCmdReader, typeIdForObject, forDebuggerDisplayAttribute, token);
+ break;
+ }
+ case ElementType.ValueType:
+ {
+ ret = await ReadAsValueType(retDebuggerCmdReader, name, initialPos, forDebuggerDisplayAttribute, token);
+ break;
+ }
+ case (ElementType)ValueTypeId.Null:
+ {
+ var className = await GetNullObjectClassName();
+ ret = CreateNull(className);
+ break;
+ }
+ case (ElementType)ValueTypeId.Type:
+ {
+ retDebuggerCmdReader.ReadInt32();
+ break;
+ }
+ default:
+ {
+ _logger.LogDebug($"Could not evaluate CreateJObjectForVariableValue invalid type {etype}");
+ break;
+ }
+ }
+ if (ret != null)
+ {
+ if (isOwn)
+ ret["isOwn"] = true;
+ ret["name"] = name;
+ }
+ return ret;
+
+ async Task<string> GetNullObjectClassName()
+ {
+ string className;
+ ElementType variableType = (ElementType)retDebuggerCmdReader.ReadByte();
+ switch (variableType)
+ {
+ case ElementType.String:
+ case ElementType.Class:
+ {
+ var type_id = retDebuggerCmdReader.ReadInt32();
+ className = await _sdbAgent.GetTypeName(type_id, token);
+ break;
+
+ }
+ case ElementType.SzArray:
+ case ElementType.Array:
+ {
+ ElementType byte_type = (ElementType)retDebuggerCmdReader.ReadByte();
+ retDebuggerCmdReader.ReadInt32(); // rank
+ if (byte_type == ElementType.Class)
+ {
+ retDebuggerCmdReader.ReadInt32(); // internal_type_id
+ }
+ var type_id = retDebuggerCmdReader.ReadInt32();
+ className = await _sdbAgent.GetTypeName(type_id, token);
+ break;
+ }
+ default:
+ {
+ var type_id = retDebuggerCmdReader.ReadInt32();
+ className = await _sdbAgent.GetTypeName(type_id, token);
+ break;
+ }
+ }
+ return className;
+ }
+ }
+
+ private async Task<JObject> ReadAsObjectValue(MonoBinaryReader retDebuggerCmdReader, int typeIdFromAttribute, bool forDebuggerDisplayAttribute, CancellationToken token)
+ {
+ var objectId = retDebuggerCmdReader.ReadInt32();
+ var type_id = await _sdbAgent.GetTypeIdsForObject(objectId, false, token);
+ string className = await _sdbAgent.GetTypeName(type_id[0], token);
+ string debuggerDisplayAttribute = null;
+ if (!forDebuggerDisplayAttribute)
+ debuggerDisplayAttribute = await _sdbAgent.GetValueFromDebuggerDisplayAttribute(
+ new DotnetObjectId("object", objectId), type_id[0], token);
+ var description = className.ToString();
+
+ if (debuggerDisplayAttribute != null)
+ description = debuggerDisplayAttribute;
+
+ if (await _sdbAgent.IsDelegate(objectId, token))
+ {
+ if (typeIdFromAttribute != -1)
+ {
+ className = await _sdbAgent.GetTypeName(typeIdFromAttribute, token);
+ }
+
+ description = await _sdbAgent.GetDelegateMethodDescription(objectId, token);
+ if (description == "")
+ {
+ return Create(value: className, type: "symbol", description: className);
+ }
+ }
+ return Create<object>(value: null, type: "object", description: description, className: className, objectId: $"dotnet:object:{objectId}");
+ }
+
+ public async Task<JObject> ReadAsValueType(
+ MonoBinaryReader retDebuggerCmdReader,
+ string name,
+ long initialPos,
+ bool forDebuggerDisplayAttribute,
+ CancellationToken token)
+ {
+ // FIXME: debugger proxy
+ var isEnum = retDebuggerCmdReader.ReadByte() == 1;
+ var isBoxed = retDebuggerCmdReader.ReadByte() == 1;
+ var typeId = retDebuggerCmdReader.ReadInt32();
+ var className = await _sdbAgent.GetTypeName(typeId, token);
+ var numValues = retDebuggerCmdReader.ReadInt32();
+
+ if (className.IndexOf("System.Nullable<", StringComparison.Ordinal) == 0) //should we call something on debugger-agent to check???
+ {
+ retDebuggerCmdReader.ReadByte(); //ignoring the boolean type
+ var isNull = retDebuggerCmdReader.ReadInt32();
+
+ // Read the value, even if isNull==true, to correctly advance the reader
+ var value = await ReadAsVariableValue(retDebuggerCmdReader, name, token);
+ if (isNull != 0)
+ return value;
+ else
+ return Create<object>(null, "object", className, className, subtype: "null", isValueType: true);
+ }
+ if (isBoxed && numValues == 1)
+ {
+ if (MonoSDBHelper.IsPrimitiveType(className))
+ {
+ return await ReadAsVariableValue(retDebuggerCmdReader, name: null, token);
+ }
+ }
+
+ ValueTypeClass valueType = await ValueTypeClass.CreateFromReader(
+ _sdbAgent,
+ retDebuggerCmdReader,
+ initialPos,
+ className,
+ typeId,
+ numValues,
+ isEnum,
+ token);
+ _valueTypes[valueType.Id.Value] = valueType;
+ return await valueType.ToJObject(_sdbAgent, forDebuggerDisplayAttribute, token);
+ }
+ public void ClearCache()
+ {
+ _valueTypes = new Dictionary<int, ValueTypeClass>();
+ _pointerValues = new Dictionary<int, PointerValue>();
+ }
+
+ public bool TryGetValueTypeById(int valueTypeId, out ValueTypeClass vt) => _valueTypes.TryGetValue(valueTypeId, out vt);
+ public PointerValue GetPointerValue(int pointerId) => _pointerValues.TryGetValue(pointerId, out PointerValue pv) ? pv : null;
+
+ private static JObject CreateJObjectForNumber<T>(T value) => Create(value, "number", value.ToString(), writable: true);
+
+ private static JObject CreateJObjectForChar(int value)
+ {
+ char charValue = Convert.ToChar(value);
+ var description = $"{value} '{charValue}'";
+ return Create(charValue, "symbol", description, writable: true);
+ }
+
+ private async Task<JObject> ReadAsPtrValue(ElementType etype, MonoBinaryReader retDebuggerCmdReader, string name, CancellationToken token)
+ {
+ string type;
+ string value;
+ long valueAddress = retDebuggerCmdReader.ReadInt64();
+ var typeId = retDebuggerCmdReader.ReadInt32();
+ string className;
+ if (etype == ElementType.FnPtr)
+ className = "(*())"; //to keep the old behavior
+ else
+ className = "(" + await _sdbAgent.GetTypeName(typeId, token) + ")";
+
+ int pointerId = 0;
+ if (valueAddress != 0 && className != "(void*)")
+ {
+ pointerId = MonoSDBHelper.GetNextDebuggerObjectId();
+ type = "object";
+ value = className;
+ _pointerValues[pointerId] = new PointerValue(valueAddress, typeId, name);
+ }
+ else
+ {
+ type = "symbol";
+ value = className + " " + valueAddress;
+ }
+ return Create(value: value, type: type, description: value, className: className, objectId: $"dotnet:pointer:{pointerId}", subtype: "pointer");
+ }
+
+ private async Task<JObject> ReadAsArray(MonoBinaryReader retDebuggerCmdReader, CancellationToken token)
+ {
+ var objectId = retDebuggerCmdReader.ReadInt32();
+ var className = await _sdbAgent.GetClassNameFromObject(objectId, token);
+ var arrayType = className.ToString();
+ var length = await _sdbAgent.GetArrayDimensions(objectId, token);
+ if (arrayType.LastIndexOf('[') > 0)
+ arrayType = arrayType.Insert(arrayType.LastIndexOf('[') + 1, length.ToString());
+ if (className.LastIndexOf('[') > 0)
+ className = className.Insert(arrayType.LastIndexOf('[') + 1, new string(',', length.Rank - 1));
+ return Create<object>(value: null,
+ type: "object",
+ description: arrayType,
+ className: className.ToString(),
+ objectId: "dotnet:array:" + objectId,
+ subtype: length.Rank == 1 ? "array" : null);
+ }
+}
private static async Task<JObject> ReadFieldValue(MonoSDBHelper sdbHelper, MonoBinaryReader reader, FieldTypeClass field, int objectId, TypeInfoWithDebugInformation typeInfo, int fieldValueType, bool isOwn, GetObjectCommandOptions getObjectOptions, CancellationToken token)
{
- var fieldValue = await sdbHelper.CreateJObjectForVariableValue(
+ var fieldValue = await sdbHelper.ValueCreator.ReadAsVariableValue(
reader,
field.Name,
token,
public static Task<GetMembersResult> GetValueTypeMemberValues(
MonoSDBHelper sdbHelper, int valueTypeId, GetObjectCommandOptions getCommandOptions, CancellationToken token, bool sortByAccessLevel = false, bool includeStatic = false)
{
- return sdbHelper.valueTypes.TryGetValue(valueTypeId, out ValueTypeClass valueType)
+ return sdbHelper.ValueCreator.TryGetValueTypeById(valueTypeId, out ValueTypeClass valueType)
? valueType.GetMemberValues(sdbHelper, getCommandOptions, sortByAccessLevel, includeStatic, token)
: throw new ArgumentException($"Could not find any valuetype with id: {valueTypeId}", nameof(valueTypeId));
}
bool isExtensionMethod = false;
try
{
- var typeIds = await context.SdbAgent.GetTypeIdsForObject(objectId.Value, true, token);
+ List<int> typeIds;
+ if (objectId.IsValueType)
+ {
+ if (!context.SdbAgent.ValueCreator.TryGetValueTypeById(objectId.Value, out ValueTypeClass valueType))
+ throw new Exception($"Could not find valuetype {objectId}");
+
+ typeIds = new List<int>(1) { valueType.TypeId };
+ }
+ else
+ {
+ typeIds = await context.SdbAgent.GetTypeIdsForObject(objectId.Value, true, token);
+ }
int methodId = await context.SdbAgent.GetMethodIdByName(typeIds[0], methodName, token);
var className = await context.SdbAgent.GetTypeNameOriginal(typeIds[0], token);
if (methodId == 0) //try to search on System.Linq.Enumerable
byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value<string>());
var retDebuggerCmdReader = new MonoBinaryReader(newBytes);
retDebuggerCmdReader.ReadByte(); //number of objects returned.
- var obj = await context.SdbAgent.CreateJObjectForVariableValue(retDebuggerCmdReader, "ret", token);
+ var obj = await context.SdbAgent.ValueCreator.ReadAsVariableValue(retDebuggerCmdReader, "ret", token);
/*JTokenType? res_value_type = res.Value?["result"]?["value"]?.Type;*/
res = Result.OkFromObject(new { result = obj["value"]});
SendResponse(id, res, token);
}
else if (objectId.Scheme == "valuetype")
{
- Write(SdbHelper.valueTypes[objectId.Value].Buffer);
+ if (SdbHelper.ValueCreator.TryGetValueTypeById(objectId.Value, out ValueTypeClass vt))
+ Write(vt.Buffer);
+ else
+ throw new ArgumentException($"Could not find any valuetype with id: {objectId.Value}", nameof(objectId.Value));
}
}
string displayVarName = varName;
if (int.TryParse(varName, out _))
displayVarName = $"[{varName}]";
- _value = await sdbHelper.CreateJObjectForVariableValue(retDebuggerCmdReader, "*" + displayVarName, token);
+ _value = await sdbHelper.ValueCreator.ReadAsVariableValue(retDebuggerCmdReader, "*" + displayVarName, token);
}
return _value;
private Dictionary<int, AssemblyInfo> assemblies;
private Dictionary<int, TypeInfoWithDebugInformation> types;
- internal Dictionary<int, ValueTypeClass> valueTypes = new Dictionary<int, ValueTypeClass>();
- internal Dictionary<int, PointerValue> pointerValues = new Dictionary<int, PointerValue>();
-
private MonoProxy proxy;
private DebugStore store;
private SessionId sessionId;
private readonly ILogger logger;
private Regex regexForAsyncLocals = new Regex(@"\<([^)]*)\>", RegexOptions.Singleline);
+ public JObjectValueCreator ValueCreator { get; init; }
public static int GetNewId() { return cmdId++; }
public static int GetNewObjectId() => Interlocked.Increment(ref debuggerObjectId);
this.proxy = proxy;
this.logger = logger;
this.sessionId = sessionId;
+ ValueCreator = new(this, logger);
ResetStore(null);
}
return types[typeId];
}
- public void ClearCache()
- {
- valueTypes = new Dictionary<int, ValueTypeClass>();
- pointerValues = new Dictionary<int, PointerValue>();
- }
+ public void ClearCache() => ValueCreator.ClearCache();
public async Task<bool> SetProtocolVersion(CancellationToken token)
{
commandParamsWriter.Write(fieldId);
using var retDebuggerCmdReader = await SendDebuggerAgentCommand(CmdType.GetValues, commandParamsWriter, token);
- return await CreateJObjectForVariableValue(retDebuggerCmdReader, "", token);
+ return await ValueCreator.ReadAsVariableValue(retDebuggerCmdReader, "", token);
}
public async Task<int> TypeIsInitialized(int typeId, CancellationToken token)
return retDebuggerCmdReader;
//reading buffer only to advance the reader to the next cattr
- for (int k = 0 ; k < 2; k++)
+ for (int k = 0; k < 2; k++)
{
var parmCount = retDebuggerCmdReader.ReadInt32();
for (int j = 0; j < parmCount; j++)
{
//to typed_args
- await CreateJObjectForVariableValue(retDebuggerCmdReader, "varName", token);
+ await ValueCreator.ReadAsVariableValue(retDebuggerCmdReader, "varName", token);
}
}
}
token);
JArray objectValues = new JArray(members.Flatten());
- var thisObj = CreateJObject<string>(value: "", type: "object", description: "", writable: false, objectId: dotnetObjectId.ToString());
+ var thisObj = JObjectValueCreator.Create(value: "", type: "object", description: "", writable: false, objectId: dotnetObjectId.ToString());
thisObj["name"] = "this";
objectValues.Add(thisObj);
commandParamsWriter.Write(0);
using var retDebuggerCmdReader = await SendDebuggerAgentCommand(CmdVM.InvokeMethod, commandParamsWriter, token);
retDebuggerCmdReader.ReadByte(); //number of objects returned.
- return await CreateJObjectForVariableValue(retDebuggerCmdReader, name, token);
+ return await ValueCreator.ReadAsVariableValue(retDebuggerCmdReader, name, token);
}
public Task<JObject> InvokeMethod(int objectId, int methodId, bool isValueType, CancellationToken token)
{
if (isValueType)
{
- return valueTypes.TryGetValue(objectId, out var valueType)
+ return ValueCreator.TryGetValueTypeById(objectId, out var valueType)
? InvokeMethod(valueType.Buffer, methodId, token)
: throw new ArgumentException($"Could not find valuetype with id {objectId}, for method id: {methodId}", nameof(objectId));
}
public async Task<JObject> GetPointerContent(int pointerId, CancellationToken token)
{
- if (!pointerValues.TryGetValue(pointerId, out PointerValue pointerValue))
+ using var commandParamsWriter = new MonoBinaryWriter();
+ PointerValue pointerValue = ValueCreator.GetPointerValue(pointerId);
+ if (pointerValue == null)
throw new ArgumentException($"Could not find any pointer with id: {pointerId}", nameof(pointerId));
-
return await pointerValue.GetValue(this, token);
}
- public static bool AutoExpandable(string className) {
- if (className == "System.DateTime" ||
- className == "System.DateTimeOffset" ||
- className == "System.TimeSpan")
- return true;
- return false;
- }
-
- private static bool AutoInvokeToString(string className) {
- if (className == "System.DateTime" ||
- className == "System.DateTimeOffset" ||
- className == "System.TimeSpan" ||
- className == "System.Decimal" ||
- className == "System.Guid")
- return true;
- return false;
- }
-
- public static JObject CreateJObject<T>(T value, string type, string description, bool writable, string className = null, string objectId = null, string __custom_type = null, string subtype = null, bool isValueType = false, bool expanded = false, bool isEnum = false)
- {
- var ret = JObject.FromObject(new {
- value = new
- {
- type,
- value,
- description
- },
- writable
- });
- if (__custom_type != null)
- ret["value"]["__custom_type"] = __custom_type;
- if (className != null)
- ret["value"]["className"] = className;
- if (objectId != null)
- ret["value"]["objectId"] = objectId;
- if (subtype != null)
- ret["value"]["subtype"] = subtype;
- if (isValueType)
- ret["value"]["isValueType"] = isValueType;
- if (expanded)
- ret["value"]["expanded"] = expanded;
- if (isEnum)
- ret["value"]["isEnum"] = isEnum;
- return ret;
-
- }
-
- private static JObject CreateJObjectForBoolean(int value)
- {
- return CreateJObject<bool>(value == 0 ? false : true, "boolean", value == 0 ? "false" : "true", true);
- }
-
- private static JObject CreateJObjectForNumber<T>(T value)
- {
- return CreateJObject<T>(value, "number", value.ToString(), true);
- }
-
- private static JObject CreateJObjectForChar(int value)
- {
- char charValue = Convert.ToChar(value);
- var description = $"{value} '{charValue}'";
- return CreateJObject<char>(charValue, "symbol", description, true);
- }
-
- public async Task<JObject> CreateJObjectForPtr(ElementType etype, MonoBinaryReader retDebuggerCmdReader, string name, CancellationToken token)
- {
- string type;
- string value;
- long valueAddress = retDebuggerCmdReader.ReadInt64();
- var typeId = retDebuggerCmdReader.ReadInt32();
- string className;
- if (etype == ElementType.FnPtr)
- className = "(*())"; //to keep the old behavior
- else
- className = "(" + await GetTypeName(typeId, token) + ")";
-
- int pointerId = -1;
- if (valueAddress != 0 && className != "(void*)")
- {
- pointerId = GetNewObjectId();
- type = "object";
- value = className;
- pointerValues[pointerId] = new PointerValue(valueAddress, typeId, name);
- }
- else
- {
- type = "symbol";
- value = className + " " + valueAddress;
- }
- return CreateJObject<string>(value, type, value, false, className, $"dotnet:pointer:{pointerId}", "pointer");
- }
-
- public async Task<JObject> CreateJObjectForString(MonoBinaryReader retDebuggerCmdReader, CancellationToken token)
- {
- var string_id = retDebuggerCmdReader.ReadInt32();
- var value = await GetStringValue(string_id, token);
- return CreateJObject<string>(value, "string", value, false);
- }
-
- public async Task<JObject> CreateJObjectForArray(MonoBinaryReader retDebuggerCmdReader, CancellationToken token)
- {
- var objectId = retDebuggerCmdReader.ReadInt32();
- var className = await GetClassNameFromObject(objectId, token);
- var arrayType = className.ToString();
- var length = await GetArrayDimensions(objectId, token);
- if (arrayType.LastIndexOf('[') > 0)
- arrayType = arrayType.Insert(arrayType.LastIndexOf('[')+1, length.ToString());
- if (className.LastIndexOf('[') > 0)
- className = className.Insert(arrayType.LastIndexOf('[')+1, new string(',', length.Rank-1));
- return CreateJObject<string>(null, "object", description : arrayType, writable : false, className.ToString(), "dotnet:array:" + objectId, null, subtype : length.Rank == 1 ? "array" : null);
- }
-
- public async Task<JObject> CreateJObjectForObject(MonoBinaryReader retDebuggerCmdReader, int typeIdFromAttribute, bool forDebuggerDisplayAttribute, CancellationToken token)
- {
- var objectId = retDebuggerCmdReader.ReadInt32();
- var type_id = await GetTypeIdsForObject(objectId, false, token);
- string className = await GetTypeName(type_id[0], token);
- string debuggerDisplayAttribute = null;
- if (!forDebuggerDisplayAttribute)
- debuggerDisplayAttribute = await GetValueFromDebuggerDisplayAttribute(new DotnetObjectId("object", objectId), type_id[0], token);
- var description = className.ToString();
-
- if (debuggerDisplayAttribute != null)
- description = debuggerDisplayAttribute;
-
- if (await IsDelegate(objectId, token))
- {
- if (typeIdFromAttribute != -1)
- className = await GetTypeName(typeIdFromAttribute, token);
-
- description = await GetDelegateMethodDescription(objectId, token);
- if (description == "")
- return CreateJObject<string>(className.ToString(), "symbol", className.ToString(), false);
- }
- return CreateJObject<string>(null, "object", description, false, className, $"dotnet:object:{objectId}");
- }
-
- private static readonly string[] s_primitiveTypeNames = new[]
- {
- "bool",
- "char",
- "string",
- "byte",
- "sbyte",
- "int",
- "uint",
- "long",
- "ulong",
- "short",
- "ushort",
- "float",
- "double",
- };
-
- public static bool IsPrimitiveType(string simplifiedClassName)
- => s_primitiveTypeNames.Contains(simplifiedClassName);
-
- public async Task<JObject> CreateJObjectForValueType(
- MonoBinaryReader retDebuggerCmdReader, string name, long initialPos, bool forDebuggerDisplayAttribute, CancellationToken token)
- {
- // FIXME: debugger proxy
- var isEnum = retDebuggerCmdReader.ReadByte() == 1;
- var isBoxed = retDebuggerCmdReader.ReadByte() == 1;
- var typeId = retDebuggerCmdReader.ReadInt32();
- var className = await GetTypeName(typeId, token);
- var numValues = retDebuggerCmdReader.ReadInt32();
-
- if (className.IndexOf("System.Nullable<", StringComparison.Ordinal) == 0) //should we call something on debugger-agent to check???
- {
- retDebuggerCmdReader.ReadByte(); //ignoring the boolean type
- var isNull = retDebuggerCmdReader.ReadInt32();
-
- // Read the value, even if isNull==true, to correctly advance the reader
- var value = await CreateJObjectForVariableValue(retDebuggerCmdReader, name, token);
- if (isNull != 0)
- return value;
- else
- return CreateJObject<string>(null, "object", className, false, className, null, null, "null", true);
- }
- if (isBoxed && numValues == 1)
- {
- if (IsPrimitiveType(className))
- {
- var value = await CreateJObjectForVariableValue(retDebuggerCmdReader, name: null, token);
- return value;
- }
- }
-
- ValueTypeClass valueType = await ValueTypeClass.CreateFromReader(
- this,
- retDebuggerCmdReader,
- initialPos,
- className,
- typeId,
- numValues,
- isEnum,
- token);
- valueTypes[valueType.Id.Value] = valueType;
- return await valueType.ToJObject(this, forDebuggerDisplayAttribute, token);
- }
-
- public async Task<JObject> CreateJObjectForNull(MonoBinaryReader retDebuggerCmdReader, CancellationToken token)
- {
- string className;
- ElementType variableType = (ElementType)retDebuggerCmdReader.ReadByte();
- switch (variableType)
- {
- case ElementType.String:
- case ElementType.Class:
- {
- var type_id = retDebuggerCmdReader.ReadInt32();
- className = await GetTypeName(type_id, token);
- break;
-
- }
- case ElementType.SzArray:
- case ElementType.Array:
- {
- ElementType byte_type = (ElementType)retDebuggerCmdReader.ReadByte();
- retDebuggerCmdReader.ReadInt32(); // rank
- if (byte_type == ElementType.Class) {
- retDebuggerCmdReader.ReadInt32(); // internal_type_id
- }
- var type_id = retDebuggerCmdReader.ReadInt32();
- className = await GetTypeName(type_id, token);
- break;
- }
- default:
- {
- var type_id = retDebuggerCmdReader.ReadInt32();
- className = await GetTypeName(type_id, token);
- break;
- }
- }
- return CreateJObject<string>(null, "object", className, false, className, null, null, "null");
- }
-
- public async Task<JObject> CreateJObjectForVariableValue(MonoBinaryReader retDebuggerCmdReader,
- string name,
- CancellationToken token,
- bool isOwn = false,
- int typeIdForObject = -1,
- bool forDebuggerDisplayAttribute = false)
- {
- long initialPos = /*retDebuggerCmdReader == null ? 0 : */retDebuggerCmdReader.BaseStream.Position;
- ElementType etype = (ElementType)retDebuggerCmdReader.ReadByte();
- JObject ret = null;
- switch (etype) {
- case ElementType.I:
- case ElementType.U:
- case ElementType.Void:
- case (ElementType)ValueTypeId.VType:
- case (ElementType)ValueTypeId.FixedArray:
- ret = JObject.FromObject(new {
- value = new
- {
- type = "void",
- value = "void",
- description = "void"
- }});
- break;
- case ElementType.Boolean:
- {
- var value = retDebuggerCmdReader.ReadInt32();
- ret = CreateJObjectForBoolean(value);
- break;
- }
- case ElementType.I1:
- {
- var value = retDebuggerCmdReader.ReadSByte();
- ret = CreateJObjectForNumber<int>(value);
- break;
- }
- case ElementType.I2:
- case ElementType.I4:
- {
- var value = retDebuggerCmdReader.ReadInt32();
- ret = CreateJObjectForNumber<int>(value);
- break;
- }
- case ElementType.U1:
- {
- var value = retDebuggerCmdReader.ReadUByte();
- ret = CreateJObjectForNumber<int>(value);
- break;
- }
- case ElementType.U2:
- {
- var value = retDebuggerCmdReader.ReadUShort();
- ret = CreateJObjectForNumber<int>(value);
- break;
- }
- case ElementType.U4:
- {
- var value = retDebuggerCmdReader.ReadUInt32();
- ret = CreateJObjectForNumber<uint>(value);
- break;
- }
- case ElementType.R4:
- {
- float value = retDebuggerCmdReader.ReadSingle();
- ret = CreateJObjectForNumber<float>(value);
- break;
- }
- case ElementType.Char:
- {
- var value = retDebuggerCmdReader.ReadInt32();
- ret = CreateJObjectForChar(value);
- break;
- }
- case ElementType.I8:
- {
- long value = retDebuggerCmdReader.ReadInt64();
- ret = CreateJObjectForNumber<long>(value);
- break;
- }
- case ElementType.U8:
- {
- ulong value = retDebuggerCmdReader.ReadUInt64();
- ret = CreateJObjectForNumber<ulong>(value);
- break;
- }
- case ElementType.R8:
- {
- double value = retDebuggerCmdReader.ReadDouble();
- ret = CreateJObjectForNumber<double>(value);
- break;
- }
- case ElementType.FnPtr:
- case ElementType.Ptr:
- {
- ret = await CreateJObjectForPtr(etype, retDebuggerCmdReader, name, token);
- break;
- }
- case ElementType.String:
- {
- ret = await CreateJObjectForString(retDebuggerCmdReader, token);
- break;
- }
- case ElementType.SzArray:
- case ElementType.Array:
- {
- ret = await CreateJObjectForArray(retDebuggerCmdReader, token);
- break;
- }
- case ElementType.Class:
- case ElementType.Object:
- {
- ret = await CreateJObjectForObject(retDebuggerCmdReader, typeIdForObject, forDebuggerDisplayAttribute, token);
- break;
- }
- case ElementType.ValueType:
- {
- ret = await CreateJObjectForValueType(retDebuggerCmdReader, name, initialPos, forDebuggerDisplayAttribute, token);
- break;
- }
- case (ElementType)ValueTypeId.Null:
- {
- ret = await CreateJObjectForNull(retDebuggerCmdReader, token);
- break;
- }
- case (ElementType)ValueTypeId.Type:
- {
- retDebuggerCmdReader.ReadInt32();
- break;
- }
- default:
- {
- logger.LogDebug($"Could not evaluate CreateJObjectForVariableValue invalid type {etype}");
- break;
- }
- }
- if (ret != null)
- {
- if (isOwn)
- ret["isOwn"] = true;
- if (!string.IsNullOrEmpty(name))
- ret["name"] = name;
- }
- return ret;
- }
+ public static int GetNextDebuggerObjectId() => Interlocked.Increment(ref debuggerObjectId);
public async Task<bool> IsAsyncMethod(int methodId, CancellationToken token)
{
{
try
{
- var var_json = await CreateJObjectForVariableValue(localsDebuggerCmdReader, var.Name, token);
+ var var_json = await ValueCreator.ReadAsVariableValue(localsDebuggerCmdReader, var.Name, token);
locals.Add(var_json);
}
catch (Exception ex)
if (!method.Info.IsStatic())
{
using var retDebuggerCmdReader = await SendDebuggerAgentCommand(CmdFrame.GetThis, commandParamsWriter, token);
- var var_json = await CreateJObjectForVariableValue(retDebuggerCmdReader, "this", token);
+ var var_json = await ValueCreator.ReadAsVariableValue(retDebuggerCmdReader, "this", token);
var_json.Add("fieldOffset", -1);
locals.Add(var_json);
}
}
- public ValueTypeClass GetValueTypeClass(int valueTypeId)
- {
- if (valueTypes.TryGetValue(valueTypeId, out ValueTypeClass value))
- return value;
- return null;
- }
-
public async Task<JArray> GetArrayValues(int arrayId, CancellationToken token)
{
var dimensions = await GetArrayDimensions(arrayId, token);
commandParamsWriter.Write(dimensions.TotalLength);
var retDebuggerCmdReader = await SendDebuggerAgentCommand(CmdArray.GetValues, commandParamsWriter, token);
JArray array = new JArray();
- for (int i = 0 ; i < dimensions.TotalLength; i++)
+ for (int i = 0; i < dimensions.TotalLength; i++)
{
- var var_json = await CreateJObjectForVariableValue(retDebuggerCmdReader, dimensions.GetArrayIndexString(i), token);
+ var var_json = await ValueCreator.ReadAsVariableValue(retDebuggerCmdReader, dimensions.GetArrayIndexString(i), token);
array.Add(var_json);
}
return array;
ctorArgsWriter.Write((byte)ValueTypeId.Null);
// FIXME: move method invocation to valueTypeclass?
- if (valueTypes.TryGetValue(objectId, out var valueType))
+ if (ValueCreator.TryGetValueTypeById(objectId, out var valueType))
{
//FIXME: Issue #68390
//ctorArgsWriter.Write((byte)0); //not used but needed
return -1;
}
+ public ValueTypeClass GetValueTypeClass(int valueTypeId)
+ {
+ if (ValueCreator.TryGetValueTypeById(valueTypeId, out ValueTypeClass vt))
+ return vt;
+ throw new ArgumentException($"Could not find any valuetype with id: {valueTypeId}", nameof(valueTypeId));
+ }
+
public Task<GetMembersResult> GetTypeMemberValues(DotnetObjectId dotnetObjectId, GetObjectCommandOptions getObjectOptions, CancellationToken token, bool sortByAccessLevel = false)
=> dotnetObjectId.IsValueType
? MemberObjectsExplorer.GetValueTypeMemberValues(this, dotnetObjectId.Value, getObjectOptions, token)
var typeIds = await GetTypeIdsForObject(objectId, true, token);
foreach (var typeId in typeIds)
{
- var retDebuggerCmdReader = await GetTypePropertiesReader(typeId, token);
+ var retDebuggerCmdReader = await GetTypePropertiesReader(typeId, token);
if (retDebuggerCmdReader == null)
return null;
await SendDebuggerAgentCommand(CmdModule.ApplyChanges, commandParamsWriter, token);
return true;
}
+
+ private static readonly string[] s_primitiveTypeNames = new[]
+ {
+ "bool",
+ "char",
+ "string",
+ "byte",
+ "sbyte",
+ "int",
+ "uint",
+ "long",
+ "ulong",
+ "short",
+ "ushort",
+ "float",
+ "double",
+ };
+
+ public static bool IsPrimitiveType(string simplifiedClassName)
+ => s_primitiveTypeNames.Contains(simplifiedClassName);
+
}
internal static class HelperExtensions
JArray fields = new();
foreach (var field in writableFields)
{
- var fieldValue = await sdbAgent.CreateJObjectForVariableValue(cmdReader, field.Name, token, true, field.TypeId, false);
+ var fieldValue = await sdbAgent.ValueCreator.ReadAsVariableValue(cmdReader, field.Name, token, true, field.TypeId, false);
fieldValue["__section"] = field.Attributes switch
{
if (displayString != null)
description = displayString;
}
- return MonoSDBHelper.CreateJObject(
+ return JObjectValueCreator.Create(
IsEnum ? fields[0]["value"] : null,
"object",
description,
- false,
className,
Id.ToString(),
- null, null, true, true,
- IsEnum);
+ isValueType: true,
+ isEnum: IsEnum);
}
public async Task<JArray> GetProxy(MonoSDBHelper sdbHelper, CancellationToken token)
);
var (_, res) = await EvaluateOnCallFrame(id, "test.GetDefaultAndRequiredParamMixedTypes(\"a\", 23, true, 1.23f)", expect_ok: false);
- Assert.Equal(
- "Unable to evaluate method 'GetDefaultAndRequiredParamMixedTypes'. Too many arguments passed.",
- res.Error["exceptionDetails"]["exception"]["description"]?.Value<string>());
+ AssertEqual("Unable to evaluate method 'GetDefaultAndRequiredParamMixedTypes'. Too many arguments passed.",
+ res.Error["result"]["description"]?.Value<string>(), "wrong error message");
});
[Fact]
await CheckPointerValue(props, "*cp", TChar('q'));
});
- [Theory]
+ [ConditionalTheory(nameof(RunningOnChrome))]
[MemberDataAttribute(nameof(PointersTestData))]
public async Task InspectLocalPointerArrays(string eval_fn, string type, string method, int line_offset, string bp_function_name, bool use_cfo) => await CheckInspectLocalsAtBreakpointSite(
type, method, line_offset, bp_function_name,
});
});
- [Theory]
+ [ConditionalTheory(nameof(RunningOnChrome))]
[MemberDataAttribute(nameof(PointersTestData))]
public async Task InspectLocalDoublePointerToPrimitiveTypeArrays(string eval_fn, string type, string method, int line_offset, string bp_function_name, bool use_cfo) => await CheckInspectLocalsAtBreakpointSite(
type, method, line_offset, bp_function_name,
}
});
- [Theory]
+ [ConditionalTheory(nameof(RunningOnChrome))]
[MemberDataAttribute(nameof(PointersTestData))]
public async Task InspectLocalPointersToValueTypes(string eval_fn, string type, string method, int line_offset, string bp_function_name, bool use_cfo) => await CheckInspectLocalsAtBreakpointSite(
type, method, line_offset, bp_function_name,
}
});
- [Theory]
+ [ConditionalTheory(nameof(RunningOnChrome))]
[MemberDataAttribute(nameof(PointersTestData))]
public async Task InspectLocalPointersToValueTypeArrays(string eval_fn, string type, string method, int line_offset, string bp_function_name, bool use_cfo) => await CheckInspectLocalsAtBreakpointSite(
type, method, line_offset, bp_function_name,
}
});
- [Theory]
+ [ConditionalTheory(nameof(RunningOnChrome))]
[MemberDataAttribute(nameof(PointersTestData))]
public async Task InspectLocalPointersToGenericValueTypeArrays(string eval_fn, string type, string method, int line_offset, string bp_function_name, bool use_cfo) => await CheckInspectLocalsAtBreakpointSite(
type, method, line_offset, bp_function_name,
}
});
- [Theory]
+ [ConditionalTheory(nameof(RunningOnChrome))]
[MemberDataAttribute(nameof(PointersTestData))]
public async Task InspectLocalDoublePointersToValueTypeArrays(string eval_fn, string type, string method, int line_offset, string bp_function_name, bool use_cfo) => await CheckInspectLocalsAtBreakpointSite(
type, method, line_offset, bp_function_name,
{ $"invoke_static_method ('[debugger-test] DebuggerTests.PointerTests:LocalPointers');", "DebuggerTests.PointerTests", "PointersAsArgsTest", 2, "PointersAsArgsTest", true },
};
- [Theory]
+ [ConditionalTheory(nameof(RunningOnChrome))]
[MemberDataAttribute(nameof(PointersAsMethodArgsTestData))]
public async Task InspectPrimitiveTypePointersAsMethodArgs(string eval_fn, string type, string method, int line_offset, string bp_function_name, bool use_cfo) => await CheckInspectLocalsAtBreakpointSite(
type, method, line_offset, bp_function_name,
}
});
- [Theory]
+ [ConditionalTheory(nameof(RunningOnChrome))]
[MemberDataAttribute(nameof(PointersAsMethodArgsTestData))]
public async Task InspectValueTypePointersAsMethodArgs(string eval_fn, string type, string method, int line_offset, string bp_function_name, bool use_cfo) => await CheckInspectLocalsAtBreakpointSite(
type, method, line_offset, bp_function_name,