Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
e489e94
Description of DebuggerBrowsable behavior.
ilonatommy Nov 25, 2021
2423830
Added test for browse attributes.
ilonatommy Nov 25, 2021
f5ede4a
Corrected typos in the doc.
ilonatommy Nov 25, 2021
326ac82
Added Browse Never feature. Corrected Collapse test. ToDo: RootHidden.
ilonatommy Nov 29, 2021
abb60a5
Draft of RootHidden solution.
ilonatommy Dec 1, 2021
609161b
Added Array to test cases as it behaves differently than Collection.
ilonatommy Dec 1, 2021
5c82b46
Added name concatenation to make array/list elemetns in debug window …
ilonatommy Dec 1, 2021
890acbb
Merge branch 'main' into debugger-browse-attribute
ilonatommy Dec 1, 2021
973bd3a
Update docs/design/mono/debugger.md
ilonatommy Dec 2, 2021
0d37c77
Applied PR review suggestions.
ilonatommy Dec 2, 2021
8353e33
Added a reference to regular Browsable attribute behavior in .net.
ilonatommy Dec 2, 2021
8fbdff9
Applied most of review suggestions.
ilonatommy Dec 2, 2021
1a06a96
Stopping GetFieldsValue early.
ilonatommy Dec 2, 2021
3c9a02e
Remove unintentional change to the original code.
ilonatommy Dec 2, 2021
01d7e84
Merge branch 'main' into debugger-browse-attribute
ilonatommy Dec 2, 2021
8a322ab
Merge branch 'main' into debugger-browse-attribute
ilonatommy Dec 3, 2021
9ee0fe9
Do not skip fields that don't have browsable attributes.
ilonatommy Dec 3, 2021
1ec30f7
Changing the expected behavior to match Console Application. EventHan…
ilonatommy Dec 3, 2021
98c82bb
Changed the place of checking if objetc is an array.
ilonatommy Dec 3, 2021
00e58cb
Update src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTe…
ilonatommy Dec 6, 2021
8f954f4
Removed unused variables.
ilonatommy Dec 6, 2021
ef135fb
Merge branch 'debugger-browse-attribute' of https://github.com/ilonat…
ilonatommy Dec 6, 2021
314dac7
Removing space and unused import.
ilonatommy Dec 6, 2021
991ba6b
Draft - parameter for choosing sorted and unsorted returned value ver…
ilonatommy Dec 7, 2021
1b78abb
Working version.
ilonatommy Dec 7, 2021
36a6219
Added "protected internal" and "private protected" cases. Changed fie…
ilonatommy Dec 8, 2021
e068d0e
Merge with main.
ilonatommy Dec 9, 2021
09c1c9c
Changed approach: placed sorting at the end of the function, not in t…
ilonatommy Dec 9, 2021
b2d50c9
Fix for tests that use GetProperties.
ilonatommy Dec 9, 2021
1f398a0
Fixed most tests (31 failing).
ilonatommy Dec 9, 2021
e135547
Removed whitespaces.
ilonatommy Dec 9, 2021
bf11957
Fixed CheckAccessorsOnObjectsWithCFO tests.
ilonatommy Dec 9, 2021
79d9ff7
Fix for InspectTypeInheritedMembers.
ilonatommy Dec 10, 2021
2225d2a
Revert unnecessary indent change.
ilonatommy Dec 10, 2021
4f4f703
Partially addressed @radical comments.
ilonatommy Dec 10, 2021
ab93329
Addressed the comment about extension instead of Union.
ilonatommy Dec 10, 2021
88682ab
Removed string cultural vunerability.
ilonatommy Dec 10, 2021
8629a99
Added Properties dictionary, the same as for fields.
ilonatommy Dec 10, 2021
53b525a
Fixed the bug I made by using dynamc.
ilonatommy Dec 10, 2021
458a78b
Applying @radical comments about refactoring.
ilonatommy Dec 10, 2021
feafa66
Corrected typo.
ilonatommy Dec 14, 2021
0ad80a4
Added tests for properties.
ilonatommy Dec 14, 2021
cd7f9da
Draft of changes for properties handling - never and root hidden fail…
ilonatommy Dec 14, 2021
b6902bd
Fix for RootHidden properties.
ilonatommy Dec 14, 2021
8945dbe
Added tests for static fields decorated with Browsable.
ilonatommy Dec 14, 2021
2c59af7
Correct a typo.
ilonatommy Dec 14, 2021
f6ad020
Merge with main.
ilonatommy Dec 17, 2021
bdf671b
Undo merge unintentional changes.
ilonatommy Dec 17, 2021
85928ed
Changing expected behavior for MulticastDelegateTest - in Console App…
ilonatommy Dec 17, 2021
b1acf8b
Removing not relevant changes created after merge with main.
ilonatommy Dec 17, 2021
153f41a
Remove file added in merge with main.
ilonatommy Dec 17, 2021
1276724
Revert "Removing not relevant changes created after merge with main."
ilonatommy Dec 17, 2021
f943552
Revert.
ilonatommy Dec 17, 2021
7c7c620
Revert revert.
ilonatommy Dec 17, 2021
e148b2d
One broken test for custom getter.
ilonatommy Dec 20, 2021
28ef9b0
Ugly fix to make all the tests work.
ilonatommy Dec 20, 2021
8cf81dc
Refactored JArray aggregation to Dictionary.
ilonatommy Dec 21, 2021
ae8ebb4
Better naming.
ilonatommy Dec 21, 2021
f7ee76b
Merge with main.
ilonatommy Dec 21, 2021
494251d
Merge with main, regression in browsables - to be fixed.
ilonatommy Dec 21, 2021
98696f8
Add skipped change from merge.
ilonatommy Dec 22, 2021
6aa584f
Fix getters testing.
ilonatommy Dec 22, 2021
a1b7b62
Removed unnecessary doubled execution.
ilonatommy Dec 22, 2021
7afb623
Decreased complexity by replacing a loop with data aggregation.
ilonatommy Dec 22, 2021
eb65384
Replaced O(nm) Linq with O(n) loop through dictionary.
ilonatommy Dec 22, 2021
1ad1b83
Remove comment, flip conditions.
ilonatommy Dec 22, 2021
80247a2
Merge branch 'main' into get-properties-internal-private
ilonatommy Jan 3, 2022
e9a560c
Removed not intended comments and changes.
ilonatommy Jan 3, 2022
66e6d52
Missing removal for previous commit.
ilonatommy Jan 3, 2022
bcdf72e
Merge with main.
ilonatommy Jan 3, 2022
6e25627
Added tests for protection levels.
ilonatommy Jan 3, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
using System.Reflection;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.VisualBasic;

namespace Microsoft.WebAssembly.Diagnostics
{
Expand Down
41 changes: 28 additions & 13 deletions src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -461,12 +461,12 @@ protected override async Task<bool> AcceptCommand(MessageId id, string method, J
if (!DotnetObjectId.TryParse(args?["objectId"], out DotnetObjectId objectId))
break;

var ret = await RuntimeGetPropertiesInternal(id, objectId, args, token);
var ret = await RuntimeGetPropertiesInternal(id, objectId, args, token, true);
if (ret == null) {
SendResponse(id, Result.Err($"Unable to RuntimeGetProperties '{objectId}'"), token);
}
else
SendResponse(id, Result.OkFromObject(new { result = ret }), token);
SendResponse(id, Result.OkFromObject(ret), token);
return true;
}

Expand Down Expand Up @@ -658,7 +658,7 @@ private async Task<bool> OnSetVariableValue(MessageId id, int scopeId, string va
return true;
}

internal async Task<JToken> RuntimeGetPropertiesInternal(SessionId id, DotnetObjectId objectId, JToken args, CancellationToken token)
internal async Task<JToken> RuntimeGetPropertiesInternal(SessionId id, DotnetObjectId objectId, JToken args, CancellationToken token, bool sortByAccessLevel = false)
{
var context = GetContext(id);
var accessorPropertiesOnly = false;
Expand All @@ -675,33 +675,48 @@ internal async Task<JToken> RuntimeGetPropertiesInternal(SessionId id, DotnetObj
objectValuesOpt |= GetObjectCommandOptions.OwnProperties;
}
}
//Console.WriteLine($"RuntimeGetProperties - {args}");
try {
switch (objectId.Scheme)
{
case "scope":
{
var res = await GetScopeProperties(id, objectId.Value, token);
return res.Value?["result"];
var resScope = await GetScopeProperties(id, objectId.Value, token);
if (sortByAccessLevel)
return resScope.Value;
return resScope.Value?["result"];
}
case "valuetype":
return await context.SdbAgent.GetValueTypeValues(objectId.Value, accessorPropertiesOnly, token);
{
var resValType = await context.SdbAgent.GetValueTypeValues(objectId.Value, accessorPropertiesOnly, token);
return sortByAccessLevel ? JObject.FromObject(new { result = resValType }) : resValType;
}
case "array":
return await context.SdbAgent.GetArrayValues(objectId.Value, token);
{
var resArr = await context.SdbAgent.GetArrayValues(objectId.Value, token);
return sortByAccessLevel ? JObject.FromObject(new { result = resArr }) : resArr;
}
case "methodId":
{
var objRet = await context.SdbAgent.InvokeMethodInObject(objectId.Value, objectId.SubValue, "", token);
return new JArray(objRet);
var resMethod = await context.SdbAgent.InvokeMethodInObject(objectId.Value, objectId.SubValue, "", token);
return sortByAccessLevel ? JObject.FromObject(new { result = resMethod }) : new JArray(resMethod);
}
case "object":
return await context.SdbAgent.GetObjectValues(objectId.Value, objectValuesOpt, token);
{
var resObj = (await context.SdbAgent.GetObjectValues(objectId.Value, objectValuesOpt, token, sortByAccessLevel));
return sortByAccessLevel ? resObj[0] : resObj;
}
case "pointer":
return new JArray{await context.SdbAgent.GetPointerContent(objectId.Value, token)};
{
var resPointer = new JArray { await context.SdbAgent.GetPointerContent(objectId.Value, token) };
return sortByAccessLevel ? JObject.FromObject(new { result = resPointer }) : resPointer;
}
case "cfo_res":
{
Result res = await SendMonoCommand(id, MonoCommands.GetDetails(RuntimeId, objectId.Value, args), token);
string value_json_str = res.Value["result"]?["value"]?["__value_as_json_string__"]?.Value<string>();
return value_json_str != null ? JArray.Parse(value_json_str) : null;
return value_json_str != null ?
(sortByAccessLevel ? JObject.FromObject(new { result = JArray.Parse(value_json_str) }) : JArray.Parse(value_json_str)) :
null;
}
default:
return null;
Expand Down
142 changes: 94 additions & 48 deletions src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -646,13 +646,15 @@ internal class FieldTypeClass
public int Id { get; }
public string Name { get; }
public int TypeId { get; }
public bool IsPublic { get; }
public FieldTypeClass(int id, string name, int typeId, bool isPublic)
public bool IsNotPrivate { get; }
public FieldAttributes ProtectionLevel { get; }
public FieldTypeClass(int id, string name, int typeId, bool isNotPrivate, FieldAttributes protectionLevel)
{
Id = id;
Name = name;
TypeId = typeId;
IsPublic = isPublic;
IsNotPrivate = isNotPrivate;
ProtectionLevel = protectionLevel;
}
}
internal class ValueTypeClass
Expand Down Expand Up @@ -1235,14 +1237,14 @@ public async Task<List<FieldTypeClass>> GetTypeFields(int typeId, CancellationTo

for (int i = 0 ; i < nFields; i++)
{
bool isPublic = false;
bool isNotPrivate = false;
int fieldId = retDebuggerCmdReader.ReadInt32(); //fieldId
string fieldNameStr = retDebuggerCmdReader.ReadString();
int fieldTypeId = retDebuggerCmdReader.ReadInt32(); //typeId
int attrs = retDebuggerCmdReader.ReadInt32(); //attrs
int isSpecialStatic = retDebuggerCmdReader.ReadInt32(); //is_special_static
if (((attrs & (int)MethodAttributes.Public) != 0))
isPublic = true;
isNotPrivate = true;
if (isSpecialStatic == 1)
continue;
if (fieldNameStr.Contains("k__BackingField"))
Expand All @@ -1251,7 +1253,7 @@ public async Task<List<FieldTypeClass>> GetTypeFields(int typeId, CancellationTo
fieldNameStr = fieldNameStr.Replace("<", "");
fieldNameStr = fieldNameStr.Replace(">", "");
}
ret.Add(new FieldTypeClass(fieldId, fieldNameStr, fieldTypeId, isPublic));
ret.Add(new FieldTypeClass(fieldId, fieldNameStr, fieldTypeId, isNotPrivate, (FieldAttributes)((attrs) & (int)FieldAttributes.FieldAccessMask)));
}
typeInfo.FieldsList = ret;
return ret;
Expand Down Expand Up @@ -2315,41 +2317,46 @@ public async Task<JArray> GetValuesFromDebuggerProxyAttribute(int objectId, int
return null;
}

public async Task<JArray> GetObjectValues(int objectId, GetObjectCommandOptions getCommandType, CancellationToken token)
public async Task<JArray> GetObjectValues(int objectId, GetObjectCommandOptions getCommandType, CancellationToken token, bool sortByAccessLevel = false)
{
var typeIdsIncludingParents = await GetTypeIdFromObject(objectId, true, token);
if (!getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerDisplayAttribute))
{
var debuggerProxy = await GetValuesFromDebuggerProxyAttribute(objectId, typeIdsIncludingParents[0], token);
if (debuggerProxy != null)
return debuggerProxy;
return sortByAccessLevel ?
new JArray(JObject.FromObject( new { result = debuggerProxy, internalProperties = new JArray(), privateProperties = new JArray() } )) :
debuggerProxy;
}
if (await IsDelegate(objectId, token))
{
var description = await GetDelegateMethodDescription(objectId, token);
return new JArray(
JObject.FromObject(new
var objValues = new JArray(JObject.FromObject(new
{
value = new
{
value = new
{
type = "symbol",
value = description,
description
},
name = "Target"
}));
type = "symbol",
value = description,
description
},
name = "Target"
}));
return sortByAccessLevel ?
new JArray(JObject.FromObject(new { result = objValues, internalProperties = new JArray(), privateProperties = new JArray() })) :
objValues;
}

var allFields = new List<FieldTypeClass>();
var objects = new Dictionary<string, JToken>();
for (int i = 0; i < typeIdsIncludingParents.Count; i++)
{
int typeId = typeIdsIncludingParents[i];
// 0th id is for the object itself, and then its parents
bool isOwn = i == 0;
var fields = await GetTypeFields(typeId, token);
allFields.AddRange(fields);

if (!getCommandType.HasFlag(GetObjectCommandOptions.AccessorPropertiesOnly))
{
var fields = await GetTypeFields(typeId, token);
var (collapsedFields, rootHiddenFields) = await FilterFieldsByDebuggerBrowsable(fields, typeId, token);

var collapsedFieldsValues = await GetFieldsValues(collapsedFields, isOwn);
Expand All @@ -2358,8 +2365,12 @@ public async Task<JArray> GetObjectValues(int objectId, GetObjectCommandOptions
objects.TryAddRange(collapsedFieldsValues);
objects.TryAddRange(hiddenFieldsValues);
}

if (!getCommandType.HasFlag(GetObjectCommandOptions.WithProperties))
return new JArray(objects.Values);
return sortByAccessLevel ?
SegregatePropertiesByAccessLevel(allFields, objects, token) :
new JArray(objects.Values);

using var commandParamsObjWriter = new MonoBinaryWriter();
commandParamsObjWriter.WriteObj(new DotnetObjectId("object", objectId), this);
var props = await CreateJArrayForProperties(
Expand All @@ -2370,41 +2381,60 @@ public async Task<JArray> GetObjectValues(int objectId, GetObjectCommandOptions
$"dotnet:object:{objectId}",
i == 0,
token);
var properties = await GetProperties(props, typeId, token);
var properties = await GetProperties(props, allFields, typeId, token);
objects.TryAddRange(properties);

// ownProperties
// Note: ownProperties should mean that we return members of the klass itself,
// but we are going to ignore that here, because otherwise vscode/chrome don't
// seem to ask for inherited fields at all.
//if (ownProperties)
//break;
//break;
/*if (accessorPropertiesOnly)
break;*/
}
if (getCommandType.HasFlag(GetObjectCommandOptions.AccessorPropertiesOnly))
{
var ownId = typeIdsIncludingParents[0];
var ownFields = await GetTypeFields(ownId, token);

var parentFields = new List<FieldTypeClass>();
for (int i = 1; i < typeIdsIncludingParents.Count; i++)
parentFields.AddRange(await GetTypeFields(typeIdsIncludingParents[i], token));

var ownDuplicatedFields = ownFields.Where(field => objects.Any(obj =>
field.Name.Equals(obj.Key)));
return sortByAccessLevel ?
SegregatePropertiesByAccessLevel(allFields, objects, token) :
new JArray(objects.Values);

var parentDuplicatedFields = parentFields.Where(field => objects.Any(obj =>
field.Name.Equals(obj.Key) &&
(obj.Value["isOwn"] == null ||
!obj.Value["isOwn"].Value<bool>())));

foreach (var duplicate in ownDuplicatedFields)
objects.Remove(duplicate.Name);
foreach (var duplicate in parentDuplicatedFields)
objects.Remove(duplicate.Name);
JArray SegregatePropertiesByAccessLevel(List<FieldTypeClass> fields, Dictionary<string, JToken> objectsDict, CancellationToken token)
{
if (fields.Count == 0)
return new JArray(JObject.FromObject(new { result = new JArray(objectsDict.Values) }));

var pubNames = fields.Where(field => field.ProtectionLevel == FieldAttributes.Public)
.Select(field => field.Name).ToList();
var privNames = fields.Where(field =>
(field.ProtectionLevel == FieldAttributes.Private ||
field.ProtectionLevel == FieldAttributes.FamANDAssem) &&
// when field is inherited it is listed both in Private and Public, to avoid duplicates:
pubNames.All(pubFieldName => pubFieldName != field.Name))
.Select(field => field.Name).ToList();
//protected == family, internal == assembly
var protectedAndInternalNames = fields.Where(field =>
field.ProtectionLevel == FieldAttributes.Family ||
field.ProtectionLevel == FieldAttributes.Assembly ||
field.ProtectionLevel == FieldAttributes.FamORAssem)
.Select(field => field.Name).ToList();
var accessorProperties = objectsDict
.Where(obj => (
pubNames.All(name => name != obj.Key) &&
privNames.All(name => name != obj.Key) &&
protectedAndInternalNames.All(name => name != obj.Key)))
.Select((k, v) => k.Value);

var pubProperties = objectsDict.GetValuesByKeys(pubNames);
var privProperties = objectsDict.GetValuesByKeys(privNames);
var protAndIntProperties = objectsDict.GetValuesByKeys(protectedAndInternalNames);
pubProperties.AddRange(new JArray(accessorProperties));

return new JArray(JObject.FromObject(new
{
result = pubProperties,
internalProperties = protAndIntProperties,
privateProperties = privProperties
}));
}
return new JArray(objects.Values);

async Task AppendRootHiddenChildren(JObject root, JArray expandedCollection)
{
Expand Down Expand Up @@ -2439,7 +2469,7 @@ async Task<JArray> GetFieldsValues(List<FieldTypeClass> fields, bool isOwn, bool
return objFields;

if (getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute))
fields = fields.Where(field => field.IsPublic).ToList();
fields = fields.Where(field => field.IsNotPrivate).ToList();

using var commandParamsWriter = new MonoBinaryWriter();
commandParamsWriter.Write(objectId);
Expand Down Expand Up @@ -2527,14 +2557,18 @@ async Task<JArray> GetFieldsValues(List<FieldTypeClass> fields, bool isOwn, bool
return (collapsedFields, rootHiddenFields);
}

async Task<JArray> GetProperties(JArray props, int typeId, CancellationToken token)
async Task<JArray> GetProperties(JArray props, List<FieldTypeClass> fields, int typeId, CancellationToken token)
{
var typeInfo = await GetTypeInfo(typeId, token);
var typeProperitesBrowsableInfo = typeInfo?.Info?.DebuggerBrowsableProperties;
var regularProps = new JArray();
foreach (var p in props)
{
if (!typeProperitesBrowsableInfo.TryGetValue(p["name"].Value<string>(), out DebuggerBrowsableState? state))
var propName = p["name"].Value<string>();
// if property has a backing field - avoid adding a duplicate
if (fields.Any(field => field.Name == propName))
continue;
if (!typeProperitesBrowsableInfo.TryGetValue(propName, out DebuggerBrowsableState? state))
{
regularProps.Add(p);
continue;
Expand All @@ -2546,7 +2580,7 @@ async Task<JArray> GetProperties(JArray props, int typeId, CancellationToken tok
case DebuggerBrowsableState.RootHidden:
DotnetObjectId rootObjId;
DotnetObjectId.TryParse(p["get"]["objectId"].Value<string>(), out rootObjId);
var rootObject = await InvokeMethodInObject(rootObjId.Value, rootObjId.SubValue, p["name"].Value<string>(), token);
var rootObject = await InvokeMethodInObject(rootObjId.Value, rootObjId.SubValue, propName, token);
await AppendRootHiddenChildren(rootObject, regularProps);
break;
case DebuggerBrowsableState.Collapsed:
Expand Down Expand Up @@ -2663,5 +2697,17 @@ public static void TryAddRange(this Dictionary<string, JToken> dict, JArray adde
dict.TryAdd(key, item);
}
}

public static JArray GetValuesByKeys(this Dictionary<string, JToken> dict, List<string> keys)
{
var result = new JArray();
foreach (var name in keys)
{
if (!dict.TryGetValue(name, out var obj))
continue;
result.Add(obj);
}
return result;
}
}
}
Loading