From e489e94dc529b280b3b53d43f0b7af5a6dd4a3de Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Thu, 25 Nov 2021 11:26:06 +0100 Subject: [PATCH 01/45] Description of DebuggerBrowsable behavior. --- docs/design/mono/debugger.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 docs/design/mono/debugger.md diff --git a/docs/design/mono/debugger.md b/docs/design/mono/debugger.md new file mode 100644 index 00000000000000..64fac6ea0d2fab --- /dev/null +++ b/docs/design/mono/debugger.md @@ -0,0 +1,19 @@ +__System.Diagnostics.DebuggerBrowsable__ + +DebuggerBrowsableState: + +Evaluation of an object with properties decorated with DebuggerBrowsable: + - Collapsed - it is displayed normally, it means: + - Simple type is displayed as: + + object_name > property_name, + + - Collection / Array is displayed as: + + object_name > property_name > property_idx(s), propery_value(s). + - RootHidden: + - Simple type - it is not displayed in the debugger window. + - Collection / Array - its root is not displayed, so the values of a collection are appearing in a flat view. + - Never - it is not displayed in the debugger window. + +DebuggerBrowsable does not affect direct evaluation of an object propoerty, e.g. calling myObject.neverBrowsableProperty, decorated with *[DebuggerBrowsable(DebuggerBrowsableState.Never)]* will result in displaying the value regardless of the decorator. \ No newline at end of file From 2423830dd19049bf13570d13213f72455592203f Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Thu, 25 Nov 2021 15:59:16 +0100 Subject: [PATCH 02/45] Added test for browse attributes. --- .../EvaluateOnCallFrameTests.cs | 38 +++++++++++++++++ .../debugger-test/debugger-evaluate-test.cs | 42 +++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index b33adf6f944ffc..9cfbd8fdf4d3f6 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -841,6 +841,44 @@ await EvaluateOnCallFrameFail(id, ); }); + [Fact] + public async Task EvaluateBrowsableProperties() => await CheckInspectLocalsAtBreakpointSite( + "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 4, "Evaluate", + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateBrowsableProperties:Evaluate'); 1 })", + wait_for_event_fn: async (pause_location) => + { + var id = pause_location["callFrames"][0]["callFrameId"].Value(); + + var (testNever, _) = await EvaluateOnCallFrame(id, "testNever"); + await CheckValue(testNever, TObject("DebuggerTests.EvaluateBrowsableProperties.TestEvaluateNever"), nameof(testNever)); + var testNeverProps = await GetProperties(testNever["objectId"]?.Value()); + await CheckProps(testNeverProps, new + { + list = TObject("System.Collections.Generic.List", description: "Count = 2"), + text = TString("text") + }, "testNeverProps#1"); + + var (testCollapsed, _) = await EvaluateOnCallFrame(id, "testCollapsed"); + await CheckValue(testCollapsed, TObject("DebuggerTests.EvaluateBrowsableProperties.TestEvaluateCollapsed"), nameof(testCollapsed)); + var testCollapsedProps = await GetProperties(testCollapsed["objectId"]?.Value()); + await CheckProps(testCollapsedProps, new + { + list = TObject("System.Collections.Generic.List", description: "Count = 2"), + text = TString("text"), + listCollapsed = TObject("System.Collections.Generic.List", description: "Count = 2"), + textCollapsed = TString("text") + }, "testCollapsedProps#1"); + + var (testRootHidden, _) = await EvaluateOnCallFrame(id, "testRootHidden"); + await CheckValue(testRootHidden, TObject("DebuggerTests.EvaluateBrowsableProperties.TestEvaluateRootHidden"), nameof(testRootHidden)); + var testRootHiddenProps = await GetProperties(testRootHidden["objectId"]?.Value()); + var (refList, _) = await EvaluateOnCallFrame(id, "testNever.list"); + var refListProp = await GetProperties(refList["objectId"]?.Value()); + var refListElementsProp = await GetProperties(refListProp[0]["value"]["objectId"]?.Value()); + Assert.Equal(refListElementsProp, testRootHiddenProps); + + + }); } } diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs index 6a9d87ba3f9e3d..cf7e8df215ae5f 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs @@ -496,6 +496,48 @@ public static void EvaluateLocals() } } + public static class EvaluateBrowsableProperties + { + public class TestEvaluateNever + { + public List list = new List() { 1, 2 }; + public string text = "text"; + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + public List listNever = new List() { 1, 2 }; + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + public string textNever = "textNever"; + } + + public class TestEvaluateCollapsed + { + public List list = new List() { 1, 2 }; + public string text = "text"; + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] + public List listCollapsed = new List() { 1, 2 }; + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] + public string textCollapsed = "textCollapsed"; + } + + public class TestEvaluateRootHidden + { + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] + public List listRootHidden = new List() { 1, 2 }; + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] + public string textRootHidden = "textRootHidden"; + } + + public static void Evaluate() + { + var testNever = new TestEvaluateNever(); + var testCollapsed = new TestEvaluateCollapsed(); + var testRootHidden = new TestEvaluateRootHidden(); + } + } } namespace DebuggerTestsV2 From f5ede4a937b262801118fda70f03d411271b13f1 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Thu, 25 Nov 2021 15:59:44 +0100 Subject: [PATCH 03/45] Corrected typos in the doc. --- docs/design/mono/debugger.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/design/mono/debugger.md b/docs/design/mono/debugger.md index 64fac6ea0d2fab..3e6130a5283f69 100644 --- a/docs/design/mono/debugger.md +++ b/docs/design/mono/debugger.md @@ -7,7 +7,7 @@ Evaluation of an object with properties decorated with DebuggerBrowsable: - Simple type is displayed as: object_name > property_name, - + - Collection / Array is displayed as: object_name > property_name > property_idx(s), propery_value(s). @@ -16,4 +16,4 @@ Evaluation of an object with properties decorated with DebuggerBrowsable: - Collection / Array - its root is not displayed, so the values of a collection are appearing in a flat view. - Never - it is not displayed in the debugger window. -DebuggerBrowsable does not affect direct evaluation of an object propoerty, e.g. calling myObject.neverBrowsableProperty, decorated with *[DebuggerBrowsable(DebuggerBrowsableState.Never)]* will result in displaying the value regardless of the decorator. \ No newline at end of file +DebuggerBrowsable does not affect direct evaluation of an object property, e.g. calling myObject.neverBrowsableProperty, decorated with *[DebuggerBrowsable(DebuggerBrowsableState.Never)]* will result in displaying the value regardless of the decorator. \ No newline at end of file From 326ac82debe2359ea65d3c87737f0dc9afbf5e41 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 29 Nov 2021 16:11:40 +0100 Subject: [PATCH 04/45] Added Browse Never feature. Corrected Collapse test. ToDo: RootHidden. --- .../debugger/BrowserDebugProxy/DebugStore.cs | 69 ++++++++++++++++++- .../BrowserDebugProxy/MonoSDBHelper.cs | 45 ++++++++++-- .../EvaluateOnCallFrameTests.cs | 18 +++-- 3 files changed, 114 insertions(+), 18 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index 148fd03d37c84e..435749611cdb70 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -20,6 +20,7 @@ using Microsoft.CodeAnalysis.Debugging; using System.IO.Compression; using System.Reflection; +using System.Diagnostics; namespace Microsoft.WebAssembly.Diagnostics { @@ -331,6 +332,8 @@ internal class MethodInfo public bool IsStatic() => (methodDef.Attributes & MethodAttributes.Static) != 0; public int IsAsync { get; set; } public bool IsHiddenFromDebugger { get; } + public bool IsBrowsable { get; } + public Enum BrowsableState { get; } public TypeInfo TypeInfo { get; } public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, int token, SourceFile source, TypeInfo type, MetadataReader asmMetadataReader, MetadataReader pdbMetadataReader) @@ -480,6 +483,8 @@ internal class TypeInfo internal int Token { get; } internal string Namespace { get; } + public Dictionary DebuggerBrowsableFields = new Dictionary(); + public TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDefinition type) { this.assembly = assembly; @@ -499,6 +504,35 @@ public TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDefi FullName = Namespace + "." + Name; else FullName = Name; + + foreach (FieldDefinitionHandle field in type.GetFields()) + { + var fieldDefinition = metadataReader.GetFieldDefinition(field); + var fieldName = metadataReader.GetString(fieldDefinition.Name); + var hasBrowsableAttribute = false; + foreach (var cattr in fieldDefinition.GetCustomAttributes()) + { + if (hasBrowsableAttribute) + break; + + var ctorHandle = metadataReader.GetCustomAttribute(cattr).Constructor; + if (ctorHandle.Kind == HandleKind.MemberReference) + { + var container = metadataReader.GetMemberReference((MemberReferenceHandle)ctorHandle).Parent; + var value = metadataReader.GetBlobBytes(metadataReader.GetCustomAttribute(cattr).Value); + var attributeName = metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)container).Name); + if (attributeName == "DebuggerBrowsableAttribute") + { + var state = (DebuggerBrowsableState)value[2]; + if (Enum.IsDefined(typeof(DebuggerBrowsableState), state)) + { + DebuggerBrowsableFields.Add(fieldName, state); + hasBrowsableAttribute = true; + } + } + } + } + } } public TypeInfo(AssemblyInfo assembly, string name) @@ -514,7 +548,6 @@ public TypeInfo(AssemblyInfo assembly, string name) public override string ToString() => "TypeInfo('" + FullName + "')"; } - internal class AssemblyInfo { private static int next_id; @@ -648,7 +681,41 @@ SourceFile FindSource(DocumentHandle doc, int rowid, string documentName) } } } + } + public DebuggerBrowsableState? GetBrowsableAttributeState(string fieldname) + { + foreach (TypeDefinitionHandle type in asmMetadataReader.TypeDefinitions) + { + var typeDefinition = asmMetadataReader.GetTypeDefinition(type); + foreach (FieldDefinitionHandle field in typeDefinition.GetFields()) + { + var fieldDefinition = asmMetadataReader.GetFieldDefinition(field); + var name = asmMetadataReader.GetString(fieldDefinition.Name); + if (fieldname == name) + { + foreach (var cattr in fieldDefinition.GetCustomAttributes()) + { + var ctorHandle = asmMetadataReader.GetCustomAttribute(cattr).Constructor; + if (ctorHandle.Kind == HandleKind.MemberReference) + { + var container = asmMetadataReader.GetMemberReference((MemberReferenceHandle)ctorHandle).Parent; + var value = asmMetadataReader.GetBlobBytes(asmMetadataReader.GetCustomAttribute(cattr).Value); + var attributeName = asmMetadataReader.GetString(asmMetadataReader.GetTypeReference((TypeReferenceHandle)container).Name); + if (attributeName == "DebuggerBrowsableAttribute") + { + var state = (DebuggerBrowsableState)value[2]; + if (Enum.IsDefined(typeof(DebuggerBrowsableState), state)) + { + return state; + } + } + } + } + } + } + } + return null; } private void ProcessSourceLink() diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 4ad3f6a45cb71d..c7d8732707ec0d 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -16,6 +16,7 @@ using System.Reflection; using System.Text; using System.Runtime.CompilerServices; +using System.Diagnostics; namespace Microsoft.WebAssembly.Diagnostics { @@ -2339,21 +2340,23 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions { className = await GetTypeName(typeId[i], token); var fields = await GetTypeFields(typeId[i], token); + var filteredFields = await FilterFieldsByDebuggerBrowsable(fields, typeId[i], token); + if (getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute)) - fields = fields.Where(field => field.IsPublic).ToList(); + filteredFields = filteredFields.Where(field => field.IsPublic).ToList(); JArray objectFields = new JArray(); using var commandParamsWriter = new MonoBinaryWriter(); commandParamsWriter.Write(objectId); - commandParamsWriter.Write(fields.Count); - foreach (var field in fields) + commandParamsWriter.Write(filteredFields.Count); + foreach (var field in filteredFields) { commandParamsWriter.Write(field.Id); } using var retDebuggerCmdReader = await SendDebuggerAgentCommand(CmdObject.RefGetValues, commandParamsWriter, token); - foreach (var field in fields) + foreach (var field in filteredFields) { long initialPos = retDebuggerCmdReader.BaseStream.Position; int valtype = retDebuggerCmdReader.ReadByte(); @@ -2405,16 +2408,18 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions for (int i = 0; i < typeId.Count; i++) { var fields = await GetTypeFields(typeId[i], token); - allFields.Add(fields); + var filteredFields = await FilterFieldsByDebuggerBrowsable(fields, typeId[i], token); + allFields.Add(filteredFields); } foreach (var item in ret) { bool foundField = false; - for (int j = 0 ; j < allFields.Count; j++) + for (int j = 0; j < allFields.Count; j++) { foreach (var field in allFields[j]) { - if (field.Name.Equals(item["name"].Value())) { + if (field.Name.Equals(item["name"].Value())) + { if (item["isOwn"] == null || (item["isOwn"].Value() && j == 0) || !item["isOwn"].Value()) foundField = true; break; @@ -2430,6 +2435,32 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions ret = retAfterRemove; } return ret; + + async Task> FilterFieldsByDebuggerBrowsable(List fields, int typeId, CancellationToken token) + { + var typeInfo = await GetTypeInfo(typeId, token); + var typeFieldsBrowsableInfo = typeInfo?.Info?.DebuggerBrowsableFields; + if (typeFieldsBrowsableInfo == null) + return fields; + + var filteredFields = new List(); + foreach (var field in fields) + { + typeFieldsBrowsableInfo.TryGetValue(field.Name, out DebuggerBrowsableState? state); + switch (state) + { + case DebuggerBrowsableState.Never: + break; + case DebuggerBrowsableState.RootHidden: + break; + //null and DebuggerBrowsableState.Collapsed have the same result + default: + filteredFields.Add(field); + break; + } + } + return filteredFields; + } } public async Task GetObjectProxy(int objectId, CancellationToken token) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index 9cfbd8fdf4d3f6..f5390c39123187 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -866,18 +866,16 @@ public async Task EvaluateBrowsableProperties() => await CheckInspectLocalsAtBre list = TObject("System.Collections.Generic.List", description: "Count = 2"), text = TString("text"), listCollapsed = TObject("System.Collections.Generic.List", description: "Count = 2"), - textCollapsed = TString("text") + textCollapsed = TString("textCollapsed") }, "testCollapsedProps#1"); - var (testRootHidden, _) = await EvaluateOnCallFrame(id, "testRootHidden"); - await CheckValue(testRootHidden, TObject("DebuggerTests.EvaluateBrowsableProperties.TestEvaluateRootHidden"), nameof(testRootHidden)); - var testRootHiddenProps = await GetProperties(testRootHidden["objectId"]?.Value()); - var (refList, _) = await EvaluateOnCallFrame(id, "testNever.list"); - var refListProp = await GetProperties(refList["objectId"]?.Value()); - var refListElementsProp = await GetProperties(refListProp[0]["value"]["objectId"]?.Value()); - Assert.Equal(refListElementsProp, testRootHiddenProps); - - + // var (testRootHidden, _) = await EvaluateOnCallFrame(id, "testRootHidden"); + // await CheckValue(testRootHidden, TObject("DebuggerTests.EvaluateBrowsableProperties.TestEvaluateRootHidden"), nameof(testRootHidden)); + // var testRootHiddenProps = await GetProperties(testRootHidden["objectId"]?.Value()); + // var (refList, _) = await EvaluateOnCallFrame(id, "testNever.list"); + // var refListProp = await GetProperties(refList["objectId"]?.Value()); + // var refListElementsProp = await GetProperties(refListProp[0]["value"]["objectId"]?.Value()); + // Assert.Equal(refListElementsProp, testRootHiddenProps); }); } From abb60a5cd93492ad7c6935d8f7e1ca4cab7638b4 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Wed, 1 Dec 2021 13:24:18 +0100 Subject: [PATCH 05/45] Draft of RootHidden solution. --- .../debugger/BrowserDebugProxy/DebugStore.cs | 35 ---- .../BrowserDebugProxy/MonoSDBHelper.cs | 149 ++++++++++++------ 2 files changed, 100 insertions(+), 84 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index 435749611cdb70..ba5de83f156924 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -683,41 +683,6 @@ SourceFile FindSource(DocumentHandle doc, int rowid, string documentName) } } - public DebuggerBrowsableState? GetBrowsableAttributeState(string fieldname) - { - foreach (TypeDefinitionHandle type in asmMetadataReader.TypeDefinitions) - { - var typeDefinition = asmMetadataReader.GetTypeDefinition(type); - foreach (FieldDefinitionHandle field in typeDefinition.GetFields()) - { - var fieldDefinition = asmMetadataReader.GetFieldDefinition(field); - var name = asmMetadataReader.GetString(fieldDefinition.Name); - if (fieldname == name) - { - foreach (var cattr in fieldDefinition.GetCustomAttributes()) - { - var ctorHandle = asmMetadataReader.GetCustomAttribute(cattr).Constructor; - if (ctorHandle.Kind == HandleKind.MemberReference) - { - var container = asmMetadataReader.GetMemberReference((MemberReferenceHandle)ctorHandle).Parent; - var value = asmMetadataReader.GetBlobBytes(asmMetadataReader.GetCustomAttribute(cattr).Value); - var attributeName = asmMetadataReader.GetString(asmMetadataReader.GetTypeReference((TypeReferenceHandle)container).Name); - if (attributeName == "DebuggerBrowsableAttribute") - { - var state = (DebuggerBrowsableState)value[2]; - if (Enum.IsDefined(typeof(DebuggerBrowsableState), state)) - { - return state; - } - } - } - } - } - } - } - return null; - } - private void ProcessSourceLink() { var sourceLinkDebugInfo = diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index c7d8732707ec0d..8a6d59e45f4106 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2340,50 +2340,15 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions { className = await GetTypeName(typeId[i], token); var fields = await GetTypeFields(typeId[i], token); - var filteredFields = await FilterFieldsByDebuggerBrowsable(fields, typeId[i], token); + var (regularFields, rootHiddenCollections, rootHiddenArrays) = await FilterFieldsByDebuggerBrowsable(fields, typeId[i], token); - if (getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute)) - filteredFields = filteredFields.Where(field => field.IsPublic).ToList(); - JArray objectFields = new JArray(); + var regularObjects = await GetFieldsValue(regularFields, i == 0); + var rootHiddenCollectionObjects = await GetFieldsValue(rootHiddenCollections, i == 0, true, false); + var rootHiddenArrayObjects = await GetFieldsValue(rootHiddenArrays, i == 0, true, true); - using var commandParamsWriter = new MonoBinaryWriter(); - commandParamsWriter.Write(objectId); - commandParamsWriter.Write(filteredFields.Count); - foreach (var field in filteredFields) - { - commandParamsWriter.Write(field.Id); - } - - using var retDebuggerCmdReader = await SendDebuggerAgentCommand(CmdObject.RefGetValues, commandParamsWriter, token); - - foreach (var field in filteredFields) - { - long initialPos = retDebuggerCmdReader.BaseStream.Position; - int valtype = retDebuggerCmdReader.ReadByte(); - retDebuggerCmdReader.BaseStream.Position = initialPos; - var fieldValue = await CreateJObjectForVariableValue(retDebuggerCmdReader, field.Name, i == 0, field.TypeId, getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerDisplayAttribute), token); - if (ret.Where(attribute => attribute["name"].Value().Equals(fieldValue["name"].Value())).Any()) { - continue; - } - if (getCommandType.HasFlag(GetObjectCommandOptions.WithSetter)) - { - var command_params_writer_to_set = new MonoBinaryWriter(); - command_params_writer_to_set.Write(objectId); - command_params_writer_to_set.Write(1); - command_params_writer_to_set.Write(field.Id); - var (data, length) = command_params_writer_to_set.ToBase64(); - - fieldValue.Add("set", JObject.FromObject(new { - commandSet = CommandSet.ObjectRef, - command = CmdObject.RefSetValues, - buffer = data, - valtype, - length = length - })); - } - objectFields.Add(fieldValue); - } - ret = new JArray(ret.Union(objectFields)); + ret = new JArray(ret.Union(regularObjects)); + ret = new JArray(ret.Union(rootHiddenCollectionObjects)); + ret = new JArray(ret.Union(rootHiddenArrayObjects)); } if (!getCommandType.HasFlag(GetObjectCommandOptions.WithProperties)) return ret; @@ -2408,8 +2373,8 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions for (int i = 0; i < typeId.Count; i++) { var fields = await GetTypeFields(typeId[i], token); - var filteredFields = await FilterFieldsByDebuggerBrowsable(fields, typeId[i], token); - allFields.Add(filteredFields); + var (regularFields, _, _) = await FilterFieldsByDebuggerBrowsable(fields, typeId[i], token); + allFields.Add(regularFields); } foreach (var item in ret) { @@ -2436,14 +2401,89 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions } return ret; - async Task> FilterFieldsByDebuggerBrowsable(List fields, int typeId, CancellationToken token) + async Task GetFieldsValue(List fields, bool isFirstElement, bool isRootHidden = false, bool isArray = false) + { + if (getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute)) + { + fields = fields.Where(field => field.IsPublic).ToList(); + } + + using var commandParamsWriter = new MonoBinaryWriter(); + commandParamsWriter.Write(objectId); + commandParamsWriter.Write(fields.Count); + foreach (var field in fields) + { + commandParamsWriter.Write(field.Id); + } + + var retDebuggerCmdReader = await SendDebuggerAgentCommand(CmdObject.RefGetValues, commandParamsWriter, token); + + JArray objFields = new JArray(); + foreach (var field in fields) + { + long initialPos = retDebuggerCmdReader.BaseStream.Position; + int valtype = retDebuggerCmdReader.ReadByte(); + retDebuggerCmdReader.BaseStream.Position = initialPos; + var fieldValue = await CreateJObjectForVariableValue(retDebuggerCmdReader, field.Name, isFirstElement, field.TypeId, getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerDisplayAttribute), token); + if (ret.Where(attribute => attribute["name"].Value().Equals(fieldValue["name"].Value())).Any()) + { + return null; + } + if (getCommandType.HasFlag(GetObjectCommandOptions.WithSetter)) + { + var command_params_writer_to_set = new MonoBinaryWriter(); + command_params_writer_to_set.Write(objectId); + command_params_writer_to_set.Write(1); + command_params_writer_to_set.Write(field.Id); + var (data, length) = command_params_writer_to_set.ToBase64(); + + fieldValue.Add("set", JObject.FromObject(new + { + commandSet = CommandSet.ObjectRef, + command = CmdObject.RefSetValues, + buffer = data, + valtype, + length = length + })); + } + if (isRootHidden) + { + DotnetObjectId.TryParse(fieldValue?["value"]?["objectId"]?.Value(), out DotnetObjectId objectId); + if (int.TryParse(objectId.Value, out int objectIdToGetInfo)) + { + var resultValue = new JArray(); + if (!isArray) + { + resultValue = await GetObjectValues(objectIdToGetInfo, getCommandType, token); + DotnetObjectId.TryParse(resultValue[0]?["value"]?["objectId"]?.Value(), out DotnetObjectId objectId2); + int.TryParse(objectId2.Value, out objectIdToGetInfo); + } + resultValue = await GetArrayValues(objectIdToGetInfo, token); + foreach (var item in resultValue) + { + item["name"] = string.Concat("[", item["name"], "]"); + objFields.Add(item); + } + } + } + else + { + objFields.Add(fieldValue); + } + } + return objFields; + } + + async Task<(List, List, List)> FilterFieldsByDebuggerBrowsable(List fields, int typeId, CancellationToken token) { var typeInfo = await GetTypeInfo(typeId, token); var typeFieldsBrowsableInfo = typeInfo?.Info?.DebuggerBrowsableFields; if (typeFieldsBrowsableInfo == null) - return fields; + return (fields, new List(), new List()); - var filteredFields = new List(); + var regularFields = new List(); + var rootHiddenCollections = new List(); + var rootHiddenArrays = new List(); foreach (var field in fields) { typeFieldsBrowsableInfo.TryGetValue(field.Name, out DebuggerBrowsableState? state); @@ -2452,14 +2492,25 @@ async Task> FilterFieldsByDebuggerBrowsable(List Date: Wed, 1 Dec 2021 14:10:22 +0100 Subject: [PATCH 06/45] Added Array to test cases as it behaves differently than Collection. --- .../EvaluateOnCallFrameTests.cs | 25 +++++++++++++------ .../debugger-test/debugger-evaluate-test.cs | 11 ++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index f5390c39123187..b6369e82a7f426 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -855,6 +855,7 @@ public async Task EvaluateBrowsableProperties() => await CheckInspectLocalsAtBre await CheckProps(testNeverProps, new { list = TObject("System.Collections.Generic.List", description: "Count = 2"), + array = TObject("int[]", description: "int[2]"), text = TString("text") }, "testNeverProps#1"); @@ -864,18 +865,28 @@ public async Task EvaluateBrowsableProperties() => await CheckInspectLocalsAtBre await CheckProps(testCollapsedProps, new { list = TObject("System.Collections.Generic.List", description: "Count = 2"), + array = TObject("int[]", description: "int[2]"), text = TString("text"), listCollapsed = TObject("System.Collections.Generic.List", description: "Count = 2"), + arrayCollapsed = TObject("int[]", description: "int[2]"), textCollapsed = TString("textCollapsed") }, "testCollapsedProps#1"); - // var (testRootHidden, _) = await EvaluateOnCallFrame(id, "testRootHidden"); - // await CheckValue(testRootHidden, TObject("DebuggerTests.EvaluateBrowsableProperties.TestEvaluateRootHidden"), nameof(testRootHidden)); - // var testRootHiddenProps = await GetProperties(testRootHidden["objectId"]?.Value()); - // var (refList, _) = await EvaluateOnCallFrame(id, "testNever.list"); - // var refListProp = await GetProperties(refList["objectId"]?.Value()); - // var refListElementsProp = await GetProperties(refListProp[0]["value"]["objectId"]?.Value()); - // Assert.Equal(refListElementsProp, testRootHiddenProps); + var (testRootHidden, _) = await EvaluateOnCallFrame(id, "testRootHidden"); + await CheckValue(testRootHidden, TObject("DebuggerTests.EvaluateBrowsableProperties.TestEvaluateRootHidden"), nameof(testRootHidden)); + var testRootHiddenProps = await GetProperties(testRootHidden["objectId"]?.Value()); + var (refList, _) = await EvaluateOnCallFrame(id, "testNever.list"); + var refListProp = await GetProperties(refList["objectId"]?.Value()); + var refListElementsProp = await GetProperties(refListProp[0]["value"]["objectId"]?.Value()); + var (refArray, _) = await EvaluateOnCallFrame(id, "testNever.array"); + var refArrayProp = await GetProperties(refArray["objectId"]?.Value()); + var mergedRefItems = new JArray(refListElementsProp.Union(refArrayProp)); + //in Console App names are in [] + foreach (var item in mergedRefItems) + { + item["name"] = string.Concat("[", item["name"], "]"); + } + Assert.Equal(mergedRefItems, testRootHiddenProps); }); } diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs index cf7e8df215ae5f..3b5b887c0949b7 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs @@ -501,11 +501,15 @@ public static class EvaluateBrowsableProperties public class TestEvaluateNever { public List list = new List() { 1, 2 }; + public int[] array = new int[] { 11, 22 }; public string text = "text"; [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] public List listNever = new List() { 1, 2 }; + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + public int[] arrayNever = new int[] { 11, 22 }; + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] public string textNever = "textNever"; } @@ -513,11 +517,15 @@ public class TestEvaluateNever public class TestEvaluateCollapsed { public List list = new List() { 1, 2 }; + public int[] array = new int[] { 11, 22 }; public string text = "text"; [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] public List listCollapsed = new List() { 1, 2 }; + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] + public int[] arrayCollapsed = new int[] { 11, 22 }; + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] public string textCollapsed = "textCollapsed"; } @@ -527,6 +535,9 @@ public class TestEvaluateRootHidden [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] public List listRootHidden = new List() { 1, 2 }; + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] + public int[] arrayRootHidden = new int[] { 11, 22 }; + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] public string textRootHidden = "textRootHidden"; } From 5c82b466a485f569d59ed1d5b3f367cb3157c3df Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Wed, 1 Dec 2021 16:46:25 +0100 Subject: [PATCH 07/45] Added name concatenation to make array/list elemetns in debug window unique. --- .../wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs | 2 +- .../DebuggerTestSuite/EvaluateOnCallFrameTests.cs | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 8a6d59e45f4106..a5f2e59e9d41d5 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2461,7 +2461,7 @@ async Task GetFieldsValue(List fields, bool isFirstEleme resultValue = await GetArrayValues(objectIdToGetInfo, token); foreach (var item in resultValue) { - item["name"] = string.Concat("[", item["name"], "]"); + item["name"] = string.Concat(fieldValue["name"], "[", item["name"], "]"); objFields.Add(item); } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index b6369e82a7f426..6bfe3c763cb1ee 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -880,12 +880,17 @@ public async Task EvaluateBrowsableProperties() => await CheckInspectLocalsAtBre var refListElementsProp = await GetProperties(refListProp[0]["value"]["objectId"]?.Value()); var (refArray, _) = await EvaluateOnCallFrame(id, "testNever.array"); var refArrayProp = await GetProperties(refArray["objectId"]?.Value()); - var mergedRefItems = new JArray(refListElementsProp.Union(refArrayProp)); //in Console App names are in [] - foreach (var item in mergedRefItems) + //adding variable name to make elements unique + foreach (var item in refArrayProp) { - item["name"] = string.Concat("[", item["name"], "]"); + item["name"] = string.Concat("arrayRootHidden[", item["name"], "]"); } + foreach (var item in refListElementsProp) + { + item["name"] = string.Concat("listRootHidden[", item["name"], "]"); + } + var mergedRefItems = new JArray(refListElementsProp.Union(refArrayProp)); Assert.Equal(mergedRefItems, testRootHiddenProps); }); } From 973bd3a7984e1a019e9390391ecaa1f6fcec8b40 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Thu, 2 Dec 2021 09:31:55 +0100 Subject: [PATCH 08/45] Update docs/design/mono/debugger.md Co-authored-by: Ankit Jain --- docs/design/mono/debugger.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/mono/debugger.md b/docs/design/mono/debugger.md index 7ab0597f8b2453..675ec89e483259 100644 --- a/docs/design/mono/debugger.md +++ b/docs/design/mono/debugger.md @@ -22,7 +22,7 @@ Web Assembly Debugger supports usage of following attributes: - Collapsed - displayed normally. - RootHidden: - Simple type - not displayed in the debugger window. - - Collection / Array - the values of a collection are appearing in a flat view, using naming convention: *rootName[idx]*. + - Collection / Array - the values of a collection are displayed in a flat view, using the naming convention: *rootName[idx]*. - Never - not displayed in the debugger window. From 0d37c773ded1d73e2dc9f093a7dddedc171cc2c8 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Thu, 2 Dec 2021 11:34:11 +0100 Subject: [PATCH 09/45] Applied PR review suggestions. --- .../debugger/BrowserDebugProxy/DebugStore.cs | 27 +++---- .../BrowserDebugProxy/MonoSDBHelper.cs | 75 +++++++++++-------- 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index ba5de83f156924..7318dea74c7df6 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -483,7 +483,7 @@ internal class TypeInfo internal int Token { get; } internal string Namespace { get; } - public Dictionary DebuggerBrowsableFields = new Dictionary(); + public Dictionary DebuggerBrowsableFields = new(); public TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDefinition type) { @@ -509,26 +509,21 @@ public TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDefi { var fieldDefinition = metadataReader.GetFieldDefinition(field); var fieldName = metadataReader.GetString(fieldDefinition.Name); - var hasBrowsableAttribute = false; foreach (var cattr in fieldDefinition.GetCustomAttributes()) { - if (hasBrowsableAttribute) - break; - var ctorHandle = metadataReader.GetCustomAttribute(cattr).Constructor; - if (ctorHandle.Kind == HandleKind.MemberReference) + if (ctorHandle.Kind != HandleKind.MemberReference) + continue; + var container = metadataReader.GetMemberReference((MemberReferenceHandle)ctorHandle).Parent; + var value = metadataReader.GetBlobBytes(metadataReader.GetCustomAttribute(cattr).Value); + var attributeName = metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)container).Name); + if (attributeName == "DebuggerBrowsableAttribute") { - var container = metadataReader.GetMemberReference((MemberReferenceHandle)ctorHandle).Parent; - var value = metadataReader.GetBlobBytes(metadataReader.GetCustomAttribute(cattr).Value); - var attributeName = metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)container).Name); - if (attributeName == "DebuggerBrowsableAttribute") + var state = (DebuggerBrowsableState)value[2]; + if (Enum.IsDefined(typeof(DebuggerBrowsableState), state)) { - var state = (DebuggerBrowsableState)value[2]; - if (Enum.IsDefined(typeof(DebuggerBrowsableState), state)) - { - DebuggerBrowsableFields.Add(fieldName, state); - hasBrowsableAttribute = true; - } + DebuggerBrowsableFields.Add(fieldName, state); + break; } } } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 8c5c0da0f3b102..ff7bfbf045f05c 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2317,7 +2317,7 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions return debuggerProxy; } var className = await GetTypeName(typeId[0], token); - JArray ret = new JArray(); + JArray objectValues = new JArray(); if (await IsDelegate(objectId, token)) { var description = await GetDelegateMethodDescription(objectId, token); @@ -2331,8 +2331,8 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions }, name = "Target" }); - ret.Add(obj); - return ret; + objectValues.Add(obj); + return objectValues; } for (int i = 0; i < typeId.Count; i++) { @@ -2346,16 +2346,23 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions var rootHiddenCollectionObjects = await GetFieldsValue(rootHiddenCollections, i == 0, true, false); var rootHiddenArrayObjects = await GetFieldsValue(rootHiddenArrays, i == 0, true, true); - ret = new JArray(ret.Union(regularObjects)); - ret = new JArray(ret.Union(rootHiddenCollectionObjects)); - ret = new JArray(ret.Union(rootHiddenArrayObjects)); + objectValues = new JArray(objectValues.Union(regularObjects)); + objectValues = new JArray(objectValues.Union(rootHiddenCollectionObjects)); + objectValues = new JArray(objectValues.Union(rootHiddenArrayObjects)); } if (!getCommandType.HasFlag(GetObjectCommandOptions.WithProperties)) - return ret; + return objectValues; using var commandParamsObjWriter = new MonoBinaryWriter(); commandParamsObjWriter.WriteObj(new DotnetObjectId("object", $"{objectId}"), this); - var props = await CreateJArrayForProperties(typeId[i], commandParamsObjWriter.GetParameterBuffer(), ret, getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute), $"dotnet:object:{objectId}", i == 0, token); - ret = new JArray(ret.Union(props)); + var props = await CreateJArrayForProperties( + typeId[i], + commandParamsObjWriter.GetParameterBuffer(), + objectValues, + getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute), + $"dotnet:object:{objectId}", + i == 0, + token); + objectValues = new JArray(objectValues.Union(props)); // ownProperties // Note: ownProperties should mean that we return members of the klass itself, @@ -2376,7 +2383,7 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions var (regularFields, _, _) = await FilterFieldsByDebuggerBrowsable(fields, typeId[i], token); allFields.Add(regularFields); } - foreach (var item in ret) + foreach (var item in objectValues) { bool foundField = false; for (int j = 0; j < allFields.Count; j++) @@ -2397,9 +2404,9 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions retAfterRemove.Add(item); } } - ret = retAfterRemove; + objectValues = retAfterRemove; } - return ret; + return objectValues; async Task GetFieldsValue(List fields, bool isFirstElement, bool isRootHidden = false, bool isArray = false) { @@ -2425,7 +2432,7 @@ async Task GetFieldsValue(List fields, bool isFirstEleme int valtype = retDebuggerCmdReader.ReadByte(); retDebuggerCmdReader.BaseStream.Position = initialPos; var fieldValue = await CreateJObjectForVariableValue(retDebuggerCmdReader, field.Name, isFirstElement, field.TypeId, getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerDisplayAttribute), token); - if (ret.Where(attribute => attribute["name"].Value().Equals(fieldValue["name"].Value())).Any()) + if (objectValues.Where(attribute => attribute["name"].Value().Equals(fieldValue["name"].Value())).Any()) { return null; } @@ -2446,29 +2453,31 @@ async Task GetFieldsValue(List fields, bool isFirstEleme length = length })); } - if (isRootHidden) + if (!isRootHidden) { - DotnetObjectId.TryParse(fieldValue?["value"]?["objectId"]?.Value(), out DotnetObjectId objectId); - if (int.TryParse(objectId.Value, out int objectIdToGetInfo)) - { - var resultValue = new JArray(); - if (!isArray) - { - resultValue = await GetObjectValues(objectIdToGetInfo, getCommandType, token); - DotnetObjectId.TryParse(resultValue[0]?["value"]?["objectId"]?.Value(), out DotnetObjectId objectId2); - int.TryParse(objectId2.Value, out objectIdToGetInfo); - } - resultValue = await GetArrayValues(objectIdToGetInfo, token); - foreach (var item in resultValue) - { - item["name"] = string.Concat(fieldValue["name"], "[", item["name"], "]"); - objFields.Add(item); - } - } + objFields.Add(fieldValue); + continue; } - else + if (!DotnetObjectId.TryParse(fieldValue?["value"]?["objectId"]?.Value(), out DotnetObjectId rootHiddenObjectId)) + continue; + if (!int.TryParse(rootHiddenObjectId?.Value, out int rootHiddenObjectIdInt)) + continue; + + var resultValue = new JArray(); + // collections require extracting items first; items are of array type + if (!isArray) { - objFields.Add(fieldValue); + resultValue = await GetObjectValues(rootHiddenObjectIdInt, getCommandType, token); + DotnetObjectId.TryParse(resultValue[0]?["value"]?["objectId"]?.Value(), out DotnetObjectId objectId2); + int.TryParse(objectId2.Value, out rootHiddenObjectIdInt); + } + resultValue = await GetArrayValues(rootHiddenObjectIdInt, token); + + // root hidden item name has to be unique, so we concatenate the root's name to it + foreach (var item in resultValue) + { + item["name"] = string.Concat(fieldValue["name"], "[", item["name"], "]"); + objFields.Add(item); } } return objFields; From 8353e33493f338225f0adb1d4900ea4971179a2b Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Thu, 2 Dec 2021 11:37:11 +0100 Subject: [PATCH 10/45] Added a reference to regular Browsable attribute behavior in .net. --- docs/design/mono/debugger.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/mono/debugger.md b/docs/design/mono/debugger.md index 675ec89e483259..2268d81bf1047b 100644 --- a/docs/design/mono/debugger.md +++ b/docs/design/mono/debugger.md @@ -18,7 +18,7 @@ Web Assembly Debugger supports usage of following attributes: - Stepping In/Over: results in an additional stepping need to proceed to the next line.

- __System.Diagnostics.DebuggerDisplay__ - __System.Diagnostics.DebuggerTypeProxy__ -- __System.Diagnostics.DebuggerBrowsable__ +- __System.Diagnostics.DebuggerBrowsable__ ([doc](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.debuggerbrowsableattribute?view=net-6.0)) - Collapsed - displayed normally. - RootHidden: - Simple type - not displayed in the debugger window. From 8fbdff99a7dfc59cf47d2d2391e72e278b8c6a5a Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Thu, 2 Dec 2021 13:12:11 +0100 Subject: [PATCH 11/45] Applied most of review suggestions. --- .../debugger/BrowserDebugProxy/DebugStore.cs | 50 +++++++++++----- .../BrowserDebugProxy/MonoSDBHelper.cs | 51 ++++++++-------- .../EvaluateOnCallFrameTests.cs | 58 ++++++++++++++----- .../debugger-test/debugger-evaluate-test.cs | 10 ++-- 4 files changed, 110 insertions(+), 59 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index 7318dea74c7df6..8964d2114dd622 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -21,6 +21,7 @@ using System.IO.Compression; using System.Reflection; using System.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.WebAssembly.Diagnostics { @@ -505,28 +506,47 @@ public TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDefi else FullName = Name; - foreach (FieldDefinitionHandle field in type.GetFields()) + DebuggerBrowsableFields = GetDebuggerBrowsableFields(); + + Dictionary GetDebuggerBrowsableFields() { - var fieldDefinition = metadataReader.GetFieldDefinition(field); - var fieldName = metadataReader.GetString(fieldDefinition.Name); - foreach (var cattr in fieldDefinition.GetCustomAttributes()) + var fields = new Dictionary(); + foreach (FieldDefinitionHandle field in type.GetFields()) { - var ctorHandle = metadataReader.GetCustomAttribute(cattr).Constructor; - if (ctorHandle.Kind != HandleKind.MemberReference) - continue; - var container = metadataReader.GetMemberReference((MemberReferenceHandle)ctorHandle).Parent; - var value = metadataReader.GetBlobBytes(metadataReader.GetCustomAttribute(cattr).Value); - var attributeName = metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)container).Name); - if (attributeName == "DebuggerBrowsableAttribute") + try { - var state = (DebuggerBrowsableState)value[2]; - if (Enum.IsDefined(typeof(DebuggerBrowsableState), state)) + var fieldDefinition = metadataReader.GetFieldDefinition(field); + var fieldName = metadataReader.GetString(fieldDefinition.Name); + foreach (var cattr in fieldDefinition.GetCustomAttributes()) { - DebuggerBrowsableFields.Add(fieldName, state); - break; + try + { + var ctorHandle = metadataReader.GetCustomAttribute(cattr).Constructor; + if (ctorHandle.Kind != HandleKind.MemberReference) + continue; + var container = metadataReader.GetMemberReference((MemberReferenceHandle)ctorHandle).Parent; + var valueBytes = metadataReader.GetBlobBytes(metadataReader.GetCustomAttribute(cattr).Value); + var attributeName = metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)container).Name); + if (attributeName != "DebuggerBrowsableAttribute") + continue; + var state = (DebuggerBrowsableState)valueBytes[2]; + if (!Enum.IsDefined(typeof(DebuggerBrowsableState), state)) + continue; + fields.Add(fieldName, state); + break; + } + catch + { + continue; + } } } + catch + { + continue; + } } + return fields; } } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index ff7bfbf045f05c..34e0bb53f5d7be 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2309,14 +2309,14 @@ public async Task GetValuesFromDebuggerProxyAttribute(int objectId, int public async Task GetObjectValues(int objectId, GetObjectCommandOptions getCommandType, CancellationToken token) { - var typeId = await GetTypeIdFromObject(objectId, true, token); + var typeIdsIncludingParent = await GetTypeIdFromObject(objectId, true, token); if (!getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerDisplayAttribute)) { - var debuggerProxy = await GetValuesFromDebuggerProxyAttribute(objectId, typeId[0], token); + var debuggerProxy = await GetValuesFromDebuggerProxyAttribute(objectId, typeIdsIncludingParent[0], token); if (debuggerProxy != null) return debuggerProxy; } - var className = await GetTypeName(typeId[0], token); + var className = await GetTypeName(typeIdsIncludingParent[0], token); JArray objectValues = new JArray(); if (await IsDelegate(objectId, token)) { @@ -2334,17 +2334,17 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions objectValues.Add(obj); return objectValues; } - for (int i = 0; i < typeId.Count; i++) + for (int i = 0; i < typeIdsIncludingParent.Count; i++) { if (!getCommandType.HasFlag(GetObjectCommandOptions.AccessorPropertiesOnly)) { - className = await GetTypeName(typeId[i], token); - var fields = await GetTypeFields(typeId[i], token); - var (regularFields, rootHiddenCollections, rootHiddenArrays) = await FilterFieldsByDebuggerBrowsable(fields, typeId[i], token); + className = await GetTypeName(typeIdsIncludingParent[i], token); + var fields = await GetTypeFields(typeIdsIncludingParent[i], token); + var (regularFields, rootHiddenCollections, rootHiddenArrays) = await FilterFieldsByDebuggerBrowsable(fields, typeIdsIncludingParent[i], token); - var regularObjects = await GetFieldsValue(regularFields, i == 0); - var rootHiddenCollectionObjects = await GetFieldsValue(rootHiddenCollections, i == 0, true, false); - var rootHiddenArrayObjects = await GetFieldsValue(rootHiddenArrays, i == 0, true, true); + var regularObjects = await GetFieldsValues(regularFields, i == 0); + var rootHiddenCollectionObjects = await GetFieldsValues(rootHiddenCollections, i == 0, true, false); + var rootHiddenArrayObjects = await GetFieldsValues(rootHiddenArrays, i == 0, true, true); objectValues = new JArray(objectValues.Union(regularObjects)); objectValues = new JArray(objectValues.Union(rootHiddenCollectionObjects)); @@ -2355,7 +2355,7 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions using var commandParamsObjWriter = new MonoBinaryWriter(); commandParamsObjWriter.WriteObj(new DotnetObjectId("object", $"{objectId}"), this); var props = await CreateJArrayForProperties( - typeId[i], + typeIdsIncludingParent[i], commandParamsObjWriter.GetParameterBuffer(), objectValues, getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute), @@ -2377,10 +2377,10 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions { var retAfterRemove = new JArray(); List> allFields = new List>(); - for (int i = 0; i < typeId.Count; i++) + for (int i = 0; i < typeIdsIncludingParent.Count; i++) { - var fields = await GetTypeFields(typeId[i], token); - var (regularFields, _, _) = await FilterFieldsByDebuggerBrowsable(fields, typeId[i], token); + var fields = await GetTypeFields(typeIdsIncludingParent[i], token); + var (regularFields, _, _) = await FilterFieldsByDebuggerBrowsable(fields, typeIdsIncludingParent[i], token); allFields.Add(regularFields); } foreach (var item in objectValues) @@ -2408,20 +2408,16 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions } return objectValues; - async Task GetFieldsValue(List fields, bool isFirstElement, bool isRootHidden = false, bool isArray = false) + async Task GetFieldsValues(List fields, bool isParent, bool isRootHidden = false, bool isArray = false) { if (getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute)) - { fields = fields.Where(field => field.IsPublic).ToList(); - } using var commandParamsWriter = new MonoBinaryWriter(); commandParamsWriter.Write(objectId); commandParamsWriter.Write(fields.Count); foreach (var field in fields) - { commandParamsWriter.Write(field.Id); - } var retDebuggerCmdReader = await SendDebuggerAgentCommand(CmdObject.RefGetValues, commandParamsWriter, token); @@ -2431,11 +2427,9 @@ async Task GetFieldsValue(List fields, bool isFirstEleme long initialPos = retDebuggerCmdReader.BaseStream.Position; int valtype = retDebuggerCmdReader.ReadByte(); retDebuggerCmdReader.BaseStream.Position = initialPos; - var fieldValue = await CreateJObjectForVariableValue(retDebuggerCmdReader, field.Name, isFirstElement, field.TypeId, getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerDisplayAttribute), token); + var fieldValue = await CreateJObjectForVariableValue(retDebuggerCmdReader, field.Name, isOwn: isParent, field.TypeId, getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerDisplayAttribute), token); if (objectValues.Where(attribute => attribute["name"].Value().Equals(fieldValue["name"].Value())).Any()) - { return null; - } if (getCommandType.HasFlag(GetObjectCommandOptions.WithSetter)) { var command_params_writer_to_set = new MonoBinaryWriter(); @@ -2485,9 +2479,12 @@ async Task GetFieldsValue(List fields, bool isFirstEleme async Task<(List, List, List)> FilterFieldsByDebuggerBrowsable(List fields, int typeId, CancellationToken token) { + if (fields.Count == 0) + return (fields, new List(), new List()); + var typeInfo = await GetTypeInfo(typeId, token); var typeFieldsBrowsableInfo = typeInfo?.Info?.DebuggerBrowsableFields; - if (typeFieldsBrowsableInfo == null) + if (typeFieldsBrowsableInfo == null || typeFieldsBrowsableInfo.Count == 0) return (fields, new List(), new List()); var regularFields = new List(); @@ -2495,7 +2492,8 @@ async Task GetFieldsValue(List fields, bool isFirstEleme var rootHiddenArrays = new List(); foreach (var field in fields) { - typeFieldsBrowsableInfo.TryGetValue(field.Name, out DebuggerBrowsableState? state); + if (!typeFieldsBrowsableInfo.TryGetValue(field.Name, out DebuggerBrowsableState? state)) + continue; switch (state) { case DebuggerBrowsableState.Never: @@ -2513,10 +2511,11 @@ async Task GetFieldsValue(List fields, bool isFirstEleme break; } break; - //null and DebuggerBrowsableState.Collapsed have the same result - default: + case DebuggerBrowsableState.Collapsed: regularFields.Add(field); break; + default: + throw new NotImplementedException($"DebuggerBrowsableState: {state}"); } } return (regularFields, rootHiddenCollections, rootHiddenArrays); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index 6841adbc5d3f63..d23c44e10f4b3b 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -854,13 +854,32 @@ await RuntimeEvaluateAndCheck( ("\"15\"\n//comment as vs does\n", TString("15")), ("\"15\"", TString("15"))); }); + + [Fact] + public async Task EvaluateBrowsableFieldsNone() => await CheckInspectLocalsAtBreakpointSite( + "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 5, "Evaluate", + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateBrowsableProperties:Evaluate'); 1 })", + wait_for_event_fn: async (pause_location) => + { + var id = pause_location["callFrames"][0]["callFrameId"].Value(); + + var (testNone, _) = await EvaluateOnCallFrame(id, "testNone"); + await CheckValue(testNone, TObject("DebuggerTests.EvaluateBrowsableProperties.TestEvaluateNone"), nameof(testNone)); + var testNoneProps = await GetProperties(testNone["objectId"]?.Value()); + await CheckProps(testNoneProps, new + { + list = TObject("System.Collections.Generic.List", description: "Count = 2"), + array = TObject("int[]", description: "int[2]"), + text = TString("text") + }, "testNoneProps#1"); + }); [Fact] - public async Task EvaluateBrowsableProperties() => await CheckInspectLocalsAtBreakpointSite( - "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 4, "Evaluate", + public async Task EvaluateBrowsableFieldsNever() => await CheckInspectLocalsAtBreakpointSite( + "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 5, "Evaluate", "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateBrowsableProperties:Evaluate'); 1 })", wait_for_event_fn: async (pause_location) => - { + { var id = pause_location["callFrames"][0]["callFrameId"].Value(); var (testNever, _) = await EvaluateOnCallFrame(id, "testNever"); @@ -868,32 +887,45 @@ public async Task EvaluateBrowsableProperties() => await CheckInspectLocalsAtBre var testNeverProps = await GetProperties(testNever["objectId"]?.Value()); await CheckProps(testNeverProps, new { - list = TObject("System.Collections.Generic.List", description: "Count = 2"), - array = TObject("int[]", description: "int[2]"), - text = TString("text") }, "testNeverProps#1"); + }); + + [Fact] + public async Task EvaluateBrowsableFieldsCollapsed() => await CheckInspectLocalsAtBreakpointSite( + "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 5, "Evaluate", + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateBrowsableProperties:Evaluate'); 1 })", + wait_for_event_fn: async (pause_location) => + { + var id = pause_location["callFrames"][0]["callFrameId"].Value(); var (testCollapsed, _) = await EvaluateOnCallFrame(id, "testCollapsed"); await CheckValue(testCollapsed, TObject("DebuggerTests.EvaluateBrowsableProperties.TestEvaluateCollapsed"), nameof(testCollapsed)); var testCollapsedProps = await GetProperties(testCollapsed["objectId"]?.Value()); await CheckProps(testCollapsedProps, new { - list = TObject("System.Collections.Generic.List", description: "Count = 2"), - array = TObject("int[]", description: "int[2]"), - text = TString("text"), listCollapsed = TObject("System.Collections.Generic.List", description: "Count = 2"), arrayCollapsed = TObject("int[]", description: "int[2]"), textCollapsed = TString("textCollapsed") - }, "testCollapsedProps#1"); + }, "testCollapsedProps#1"); + }); + + [Fact] + public async Task EvaluateBrowsableFieldsRootHidden() => await CheckInspectLocalsAtBreakpointSite( + "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 5, "Evaluate", + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateBrowsableProperties:Evaluate'); 1 })", + wait_for_event_fn: async (pause_location) => + { + var id = pause_location["callFrames"][0]["callFrameId"].Value(); var (testRootHidden, _) = await EvaluateOnCallFrame(id, "testRootHidden"); await CheckValue(testRootHidden, TObject("DebuggerTests.EvaluateBrowsableProperties.TestEvaluateRootHidden"), nameof(testRootHidden)); var testRootHiddenProps = await GetProperties(testRootHidden["objectId"]?.Value()); - var (refList, _) = await EvaluateOnCallFrame(id, "testNever.list"); + var (refList, _) = await EvaluateOnCallFrame(id, "testNone.list"); var refListProp = await GetProperties(refList["objectId"]?.Value()); var refListElementsProp = await GetProperties(refListProp[0]["value"]["objectId"]?.Value()); - var (refArray, _) = await EvaluateOnCallFrame(id, "testNever.array"); + var (refArray, _) = await EvaluateOnCallFrame(id, "testNone.array"); var refArrayProp = await GetProperties(refArray["objectId"]?.Value()); + //in Console App names are in [] //adding variable name to make elements unique foreach (var item in refArrayProp) @@ -904,7 +936,7 @@ public async Task EvaluateBrowsableProperties() => await CheckInspectLocalsAtBre { item["name"] = string.Concat("listRootHidden[", item["name"], "]"); } - var mergedRefItems = new JArray(refListElementsProp.Union(refArrayProp)); + var mergedRefItems = new JArray(refListElementsProp.Union(refArrayProp)); Assert.Equal(mergedRefItems, testRootHiddenProps); }); } diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs index 3b5b887c0949b7..c7da7403d5463a 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs @@ -498,12 +498,15 @@ public static void EvaluateLocals() public static class EvaluateBrowsableProperties { - public class TestEvaluateNever + public class TestEvaluateNone { public List list = new List() { 1, 2 }; public int[] array = new int[] { 11, 22 }; public string text = "text"; + } + public class TestEvaluateNever + { [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] public List listNever = new List() { 1, 2 }; @@ -516,10 +519,6 @@ public class TestEvaluateNever public class TestEvaluateCollapsed { - public List list = new List() { 1, 2 }; - public int[] array = new int[] { 11, 22 }; - public string text = "text"; - [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] public List listCollapsed = new List() { 1, 2 }; @@ -544,6 +543,7 @@ public class TestEvaluateRootHidden public static void Evaluate() { + var testNone = new TestEvaluateNone(); var testNever = new TestEvaluateNever(); var testCollapsed = new TestEvaluateCollapsed(); var testRootHidden = new TestEvaluateRootHidden(); From 1a06a96cd10fa9adb534776fc5d6ea8a761abbd0 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Thu, 2 Dec 2021 14:07:49 +0100 Subject: [PATCH 12/45] Stopping GetFieldsValue early. --- src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 34e0bb53f5d7be..1b54b58ef0ba9d 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2410,6 +2410,10 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions async Task GetFieldsValues(List fields, bool isParent, bool isRootHidden = false, bool isArray = false) { + JArray objFields = new JArray(); + if (fields.Count == 0) + return objFields; + if (getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute)) fields = fields.Where(field => field.IsPublic).ToList(); @@ -2418,10 +2422,8 @@ async Task GetFieldsValues(List fields, bool isParent, b commandParamsWriter.Write(fields.Count); foreach (var field in fields) commandParamsWriter.Write(field.Id); - var retDebuggerCmdReader = await SendDebuggerAgentCommand(CmdObject.RefGetValues, commandParamsWriter, token); - JArray objFields = new JArray(); foreach (var field in fields) { long initialPos = retDebuggerCmdReader.BaseStream.Position; From 3c9a02e88a9ab5a3c045c76ac2fdf9901a4ad3eb Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Thu, 2 Dec 2021 15:32:03 +0100 Subject: [PATCH 13/45] Remove unintentional change to the original code. --- src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 1b54b58ef0ba9d..f7aa47ca382269 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2431,7 +2431,7 @@ async Task GetFieldsValues(List fields, bool isParent, b retDebuggerCmdReader.BaseStream.Position = initialPos; var fieldValue = await CreateJObjectForVariableValue(retDebuggerCmdReader, field.Name, isOwn: isParent, field.TypeId, getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerDisplayAttribute), token); if (objectValues.Where(attribute => attribute["name"].Value().Equals(fieldValue["name"].Value())).Any()) - return null; + continue; if (getCommandType.HasFlag(GetObjectCommandOptions.WithSetter)) { var command_params_writer_to_set = new MonoBinaryWriter(); From 9ee0fe999767103ca3a420932b7fa274283a709b Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 3 Dec 2021 11:20:53 +0100 Subject: [PATCH 14/45] Do not skip fields that don't have browsable attributes. --- src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index f7aa47ca382269..1eb0c96ec5e5b5 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2495,7 +2495,10 @@ async Task GetFieldsValues(List fields, bool isParent, b foreach (var field in fields) { if (!typeFieldsBrowsableInfo.TryGetValue(field.Name, out DebuggerBrowsableState? state)) + { + regularFields.Add(field); continue; + } switch (state) { case DebuggerBrowsableState.Never: From 1ec30f77000fee12445c3427e934ef03f18d88c2 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 3 Dec 2021 13:19:39 +0100 Subject: [PATCH 15/45] Changing the expected behavior to match Console Application. EventHandlers are Browsable.Never by default. --- src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs index d6b260375c1f0e..5b2818b510eb01 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs @@ -651,7 +651,6 @@ public async Task MulticastDelegateTest() => await CheckInspectLocalsAtBreakpoin var this_props = await GetObjectOnLocals(frame_locals, "this"); await CheckProps(this_props, new { - TestEvent = TSymbol("System.EventHandler"), Delegate = TSymbol("System.MulticastDelegate") }, "this_props"); }); From 98c82bb90e5e6a82e0ae1545f3271c1f1b01ec9f Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 3 Dec 2021 17:10:14 +0100 Subject: [PATCH 16/45] Changed the place of checking if objetc is an array. --- .../BrowserDebugProxy/MonoSDBHelper.cs | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 1eb0c96ec5e5b5..ef735fc444c8d8 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2340,15 +2340,13 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions { className = await GetTypeName(typeIdsIncludingParent[i], token); var fields = await GetTypeFields(typeIdsIncludingParent[i], token); - var (regularFields, rootHiddenCollections, rootHiddenArrays) = await FilterFieldsByDebuggerBrowsable(fields, typeIdsIncludingParent[i], token); + var (regularFields, rootHiddenFields) = await FilterFieldsByDebuggerBrowsable(fields, typeIdsIncludingParent[i], token); var regularObjects = await GetFieldsValues(regularFields, i == 0); - var rootHiddenCollectionObjects = await GetFieldsValues(rootHiddenCollections, i == 0, true, false); - var rootHiddenArrayObjects = await GetFieldsValues(rootHiddenArrays, i == 0, true, true); + var rootHiddenObjects = await GetFieldsValues(rootHiddenFields, i == 0, true); objectValues = new JArray(objectValues.Union(regularObjects)); - objectValues = new JArray(objectValues.Union(rootHiddenCollectionObjects)); - objectValues = new JArray(objectValues.Union(rootHiddenArrayObjects)); + objectValues = new JArray(objectValues.Union(rootHiddenObjects)); } if (!getCommandType.HasFlag(GetObjectCommandOptions.WithProperties)) return objectValues; @@ -2380,7 +2378,7 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions for (int i = 0; i < typeIdsIncludingParent.Count; i++) { var fields = await GetTypeFields(typeIdsIncludingParent[i], token); - var (regularFields, _, _) = await FilterFieldsByDebuggerBrowsable(fields, typeIdsIncludingParent[i], token); + var (regularFields, _) = await FilterFieldsByDebuggerBrowsable(fields, typeIdsIncludingParent[i], token); allFields.Add(regularFields); } foreach (var item in objectValues) @@ -2408,7 +2406,7 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions } return objectValues; - async Task GetFieldsValues(List fields, bool isParent, bool isRootHidden = false, bool isArray = false) + async Task GetFieldsValues(List fields, bool isParent, bool isRootHidden = false) { JArray objFields = new JArray(); if (fields.Count == 0) @@ -2460,8 +2458,10 @@ async Task GetFieldsValues(List fields, bool isParent, b continue; var resultValue = new JArray(); - // collections require extracting items first; items are of array type - if (!isArray) + // collections require extracting items to get inner values; items are of array type + // arrays have "subtype": "array" field, collections don't + var subtype = fieldValue?["value"]?["subtype"]; + if (subtype == null || subtype?.Value() != "array") { resultValue = await GetObjectValues(rootHiddenObjectIdInt, getCommandType, token); DotnetObjectId.TryParse(resultValue[0]?["value"]?["objectId"]?.Value(), out DotnetObjectId objectId2); @@ -2479,19 +2479,18 @@ async Task GetFieldsValues(List fields, bool isParent, b return objFields; } - async Task<(List, List, List)> FilterFieldsByDebuggerBrowsable(List fields, int typeId, CancellationToken token) + async Task<(List, List)> FilterFieldsByDebuggerBrowsable(List fields, int typeId, CancellationToken token) { if (fields.Count == 0) - return (fields, new List(), new List()); + return (fields, new List()); var typeInfo = await GetTypeInfo(typeId, token); var typeFieldsBrowsableInfo = typeInfo?.Info?.DebuggerBrowsableFields; if (typeFieldsBrowsableInfo == null || typeFieldsBrowsableInfo.Count == 0) - return (fields, new List(), new List()); + return (fields, new List()); var regularFields = new List(); - var rootHiddenCollections = new List(); - var rootHiddenArrays = new List(); + var rootHiddenFields = new List(); foreach (var field in fields) { if (!typeFieldsBrowsableInfo.TryGetValue(field.Name, out DebuggerBrowsableState? state)) @@ -2505,14 +2504,15 @@ async Task GetFieldsValues(List fields, bool isParent, b break; case DebuggerBrowsableState.RootHidden: var typeName = await GetTypeName(field.TypeId, token); + //var info = await GetTypeInfo(field.TypeId, token); if (typeName.StartsWith("System.Collections.Generic")) { - rootHiddenCollections.Add(field); + rootHiddenFields.Add(field); break; } if (typeName.EndsWith("[]")) { - rootHiddenArrays.Add(field); + rootHiddenFields.Add(field); break; } break; @@ -2523,7 +2523,7 @@ async Task GetFieldsValues(List fields, bool isParent, b throw new NotImplementedException($"DebuggerBrowsableState: {state}"); } } - return (regularFields, rootHiddenCollections, rootHiddenArrays); + return (regularFields, rootHiddenFields); } } From 00e58cbbb68d3cf95708af7e1b9c39999068515b Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Mon, 6 Dec 2021 09:06:10 +0100 Subject: [PATCH 17/45] Update src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs Co-authored-by: Ankit Jain --- .../wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index d23c44e10f4b3b..92702e9f57c756 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -874,7 +874,7 @@ public async Task EvaluateBrowsableFieldsNone() => await CheckInspectLocalsAtBre }, "testNoneProps#1"); }); - [Fact] + [Fact] public async Task EvaluateBrowsableFieldsNever() => await CheckInspectLocalsAtBreakpointSite( "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 5, "Evaluate", "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateBrowsableProperties:Evaluate'); 1 })", From 8f954f4273a79ce3fc22822913d096423c37c652 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 6 Dec 2021 09:12:42 +0100 Subject: [PATCH 18/45] Removed unused variables. --- src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index 8964d2114dd622..364e1177809e07 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -333,8 +333,6 @@ internal class MethodInfo public bool IsStatic() => (methodDef.Attributes & MethodAttributes.Static) != 0; public int IsAsync { get; set; } public bool IsHiddenFromDebugger { get; } - public bool IsBrowsable { get; } - public Enum BrowsableState { get; } public TypeInfo TypeInfo { get; } public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, int token, SourceFile source, TypeInfo type, MetadataReader asmMetadataReader, MetadataReader pdbMetadataReader) From 314dac7cec5aa994ff1be0e318d85f7cc1773aab Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 6 Dec 2021 09:16:19 +0100 Subject: [PATCH 19/45] Removing space and unused import. --- src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs | 1 - .../wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index 364e1177809e07..b27ad2dcb36d17 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -21,7 +21,6 @@ using System.IO.Compression; using System.Reflection; using System.Diagnostics; -using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.WebAssembly.Diagnostics { diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index 92702e9f57c756..d23c44e10f4b3b 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -874,7 +874,7 @@ public async Task EvaluateBrowsableFieldsNone() => await CheckInspectLocalsAtBre }, "testNoneProps#1"); }); - [Fact] + [Fact] public async Task EvaluateBrowsableFieldsNever() => await CheckInspectLocalsAtBreakpointSite( "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 5, "Evaluate", "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateBrowsableProperties:Evaluate'); 1 })", From 4f4f7031c047882efc68cdd426536bc4f979982f Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 10 Dec 2021 10:46:35 +0100 Subject: [PATCH 20/45] Partially addressed @radical comments. --- .../BrowserDebugProxy/MonoSDBHelper.cs | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index ef735fc444c8d8..29a98add553369 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2342,8 +2342,8 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions var fields = await GetTypeFields(typeIdsIncludingParent[i], token); var (regularFields, rootHiddenFields) = await FilterFieldsByDebuggerBrowsable(fields, typeIdsIncludingParent[i], token); - var regularObjects = await GetFieldsValues(regularFields, i == 0); - var rootHiddenObjects = await GetFieldsValues(rootHiddenFields, i == 0, true); + var regularObjects = await GetFieldsValues(regularFields, isOwn: i == 0); + var rootHiddenObjects = await GetFieldsValues(rootHiddenFields, isOwn: i == 0, isRootHidden: true); objectValues = new JArray(objectValues.Union(regularObjects)); objectValues = new JArray(objectValues.Union(rootHiddenObjects)); @@ -2373,14 +2373,15 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions } if (getCommandType.HasFlag(GetObjectCommandOptions.AccessorPropertiesOnly)) { - var retAfterRemove = new JArray(); List> allFields = new List>(); for (int i = 0; i < typeIdsIncludingParent.Count; i++) { var fields = await GetTypeFields(typeIdsIncludingParent[i], token); - var (regularFields, _) = await FilterFieldsByDebuggerBrowsable(fields, typeIdsIncludingParent[i], token); + var (regularFields, rootHiddenFields) = await FilterFieldsByDebuggerBrowsable(fields, typeIdsIncludingParent[i], token); allFields.Add(regularFields); + allFields.Add(rootHiddenFields); } + var retAfterRemove = new JArray(); foreach (var item in objectValues) { bool foundField = false; @@ -2398,7 +2399,8 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions if (foundField) break; } - if (!foundField) { + if (!foundField) + { retAfterRemove.Add(item); } } @@ -2406,7 +2408,7 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions } return objectValues; - async Task GetFieldsValues(List fields, bool isParent, bool isRootHidden = false) + async Task GetFieldsValues(List fields, bool isOwn, bool isRootHidden = false) { JArray objFields = new JArray(); if (fields.Count == 0) @@ -2427,7 +2429,7 @@ async Task GetFieldsValues(List fields, bool isParent, b long initialPos = retDebuggerCmdReader.BaseStream.Position; int valtype = retDebuggerCmdReader.ReadByte(); retDebuggerCmdReader.BaseStream.Position = initialPos; - var fieldValue = await CreateJObjectForVariableValue(retDebuggerCmdReader, field.Name, isOwn: isParent, field.TypeId, getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerDisplayAttribute), token); + var fieldValue = await CreateJObjectForVariableValue(retDebuggerCmdReader, field.Name, isOwn: isOwn, field.TypeId, getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerDisplayAttribute), token); if (objectValues.Where(attribute => attribute["name"].Value().Equals(fieldValue["name"].Value())).Any()) continue; if (getCommandType.HasFlag(GetObjectCommandOptions.WithSetter)) @@ -2504,17 +2506,9 @@ async Task GetFieldsValues(List fields, bool isParent, b break; case DebuggerBrowsableState.RootHidden: var typeName = await GetTypeName(field.TypeId, token); - //var info = await GetTypeInfo(field.TypeId, token); - if (typeName.StartsWith("System.Collections.Generic")) - { + if (typeName.StartsWith("System.Collections.Generic") || + typeName.EndsWith("[]")) rootHiddenFields.Add(field); - break; - } - if (typeName.EndsWith("[]")) - { - rootHiddenFields.Add(field); - break; - } break; case DebuggerBrowsableState.Collapsed: regularFields.Add(field); From ab933298b6933ece1db4358fb46e2fcde3c5c40f Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 10 Dec 2021 11:45:30 +0100 Subject: [PATCH 21/45] Addressed the comment about extension instead of Union. --- .../debugger/BrowserDebugProxy/MonoSDBHelper.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 29a98add553369..e227b631338ba4 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2345,8 +2345,8 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions var regularObjects = await GetFieldsValues(regularFields, isOwn: i == 0); var rootHiddenObjects = await GetFieldsValues(rootHiddenFields, isOwn: i == 0, isRootHidden: true); - objectValues = new JArray(objectValues.Union(regularObjects)); - objectValues = new JArray(objectValues.Union(rootHiddenObjects)); + objectValues.AddRange(regularObjects); + objectValues.AddRange(rootHiddenObjects); } if (!getCommandType.HasFlag(GetObjectCommandOptions.WithProperties)) return objectValues; @@ -2360,7 +2360,7 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions $"dotnet:object:{objectId}", i == 0, token); - objectValues = new JArray(objectValues.Union(props)); + objectValues.AddRange(props); // ownProperties // Note: ownProperties should mean that we return members of the klass itself, @@ -2605,4 +2605,13 @@ public async Task SetVariableValue(int thread_id, int frame_id, int varId, return true; } } + + internal static class HelperExtensions + { + public static void AddRange(this JArray arr, JArray addedArr) + { + foreach (var item in addedArr) + arr.Add(item); + } + } } From 88682abba6964acf0bef8de434c0ba21c0724d8d Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 10 Dec 2021 11:49:27 +0100 Subject: [PATCH 22/45] Removed string cultural vunerability. --- src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index e227b631338ba4..69ce7e0f722880 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2506,8 +2506,8 @@ async Task GetFieldsValues(List fields, bool isOwn, bool break; case DebuggerBrowsableState.RootHidden: var typeName = await GetTypeName(field.TypeId, token); - if (typeName.StartsWith("System.Collections.Generic") || - typeName.EndsWith("[]")) + if (typeName.StartsWith("System.Collections.Generic", StringComparison.Ordinal) || + typeName.EndsWith("[]", StringComparison.Ordinal)) rootHiddenFields.Add(field); break; case DebuggerBrowsableState.Collapsed: From 8629a992ad2484769861131e995dd6c6f011cbca Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 10 Dec 2021 12:24:35 +0100 Subject: [PATCH 23/45] Added Properties dictionary, the same as for fields. --- src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index a28a922bec4b4f..e1149c26b7dc01 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -22,6 +22,7 @@ using System.Reflection; using System.Collections.Immutable; using System.Diagnostics; +using Microsoft.VisualBasic; namespace Microsoft.WebAssembly.Diagnostics { @@ -483,6 +484,7 @@ internal class TypeInfo internal string Namespace { get; } public Dictionary DebuggerBrowsableFields = new(); + public Dictionary DebuggerBrowsableProperties = new(); public TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDefinition type) { @@ -504,12 +506,13 @@ public TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDefi else FullName = Name; - DebuggerBrowsableFields = GetDebuggerBrowsableFields(); + DebuggerBrowsableFields = GetDebuggerBrowsable(type.GetFields()); + DebuggerBrowsableProperties = GetDebuggerBrowsable(type.GetProperties()); - Dictionary GetDebuggerBrowsableFields() + Dictionary GetDebuggerBrowsable(dynamic typeCollection) { var fields = new Dictionary(); - foreach (FieldDefinitionHandle field in type.GetFields()) + foreach (FieldDefinitionHandle field in typeCollection) { try { From 53b525a96cd6af7bdb18b7b047200dc36017e132 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 10 Dec 2021 13:48:08 +0100 Subject: [PATCH 24/45] Fixed the bug I made by using dynamc. --- .../debugger/BrowserDebugProxy/DebugStore.cs | 67 ++++++++++--------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index e1149c26b7dc01..0c38e82dccb82d 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -506,48 +506,53 @@ public TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDefi else FullName = Name; - DebuggerBrowsableFields = GetDebuggerBrowsable(type.GetFields()); - DebuggerBrowsableProperties = GetDebuggerBrowsable(type.GetProperties()); + foreach (var field in type.GetFields()) + { + try + { + var fieldDefinition = metadataReader.GetFieldDefinition(field); + var fieldName = metadataReader.GetString(fieldDefinition.Name); + AppendToBrowsable(DebuggerBrowsableFields, fieldDefinition.GetCustomAttributes(), fieldName); + } + catch { continue; } + } + + foreach (var prop in type.GetProperties()) + { + try + { + var propDefinition = metadataReader.GetPropertyDefinition(prop); + var propdName = metadataReader.GetString(propDefinition.Name); + AppendToBrowsable(DebuggerBrowsableProperties, propDefinition.GetCustomAttributes(), propdName); + } + catch { continue; } + } - Dictionary GetDebuggerBrowsable(dynamic typeCollection) + void AppendToBrowsable(Dictionary dict, CustomAttributeHandleCollection customAttrs, string fieldName) { - var fields = new Dictionary(); - foreach (FieldDefinitionHandle field in typeCollection) + foreach (var cattr in customAttrs) { try { - var fieldDefinition = metadataReader.GetFieldDefinition(field); - var fieldName = metadataReader.GetString(fieldDefinition.Name); - foreach (var cattr in fieldDefinition.GetCustomAttributes()) - { - try - { - var ctorHandle = metadataReader.GetCustomAttribute(cattr).Constructor; - if (ctorHandle.Kind != HandleKind.MemberReference) - continue; - var container = metadataReader.GetMemberReference((MemberReferenceHandle)ctorHandle).Parent; - var valueBytes = metadataReader.GetBlobBytes(metadataReader.GetCustomAttribute(cattr).Value); - var attributeName = metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)container).Name); - if (attributeName != "DebuggerBrowsableAttribute") - continue; - var state = (DebuggerBrowsableState)valueBytes[2]; - if (!Enum.IsDefined(typeof(DebuggerBrowsableState), state)) - continue; - fields.Add(fieldName, state); - break; - } - catch - { - continue; - } - } + var ctorHandle = metadataReader.GetCustomAttribute(cattr).Constructor; + if (ctorHandle.Kind != HandleKind.MemberReference) + continue; + var container = metadataReader.GetMemberReference((MemberReferenceHandle)ctorHandle).Parent; + var valueBytes = metadataReader.GetBlobBytes(metadataReader.GetCustomAttribute(cattr).Value); + var attributeName = metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)container).Name); + if (attributeName != "DebuggerBrowsableAttribute") + continue; + var state = (DebuggerBrowsableState)valueBytes[2]; + if (!Enum.IsDefined(typeof(DebuggerBrowsableState), state)) + continue; + dict.Add(fieldName, state); + break; } catch { continue; } } - return fields; } } From 458a78bd8f71e090c94e7accc4706764062806b6 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 10 Dec 2021 13:48:32 +0100 Subject: [PATCH 25/45] Applying @radical comments about refactoring. --- .../debugger/BrowserDebugProxy/MonoSDBHelper.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 69ce7e0f722880..39a14364674508 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2316,7 +2316,6 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions if (debuggerProxy != null) return debuggerProxy; } - var className = await GetTypeName(typeIdsIncludingParent[0], token); JArray objectValues = new JArray(); if (await IsDelegate(objectId, token)) { @@ -2336,17 +2335,17 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions } for (int i = 0; i < typeIdsIncludingParent.Count; i++) { + int typeId = typeIdsIncludingParent[i]; + // 0th id is for the object itself, and then its parents + bool isOwn = i == 0; + if (!getCommandType.HasFlag(GetObjectCommandOptions.AccessorPropertiesOnly)) { - className = await GetTypeName(typeIdsIncludingParent[i], token); var fields = await GetTypeFields(typeIdsIncludingParent[i], token); var (regularFields, rootHiddenFields) = await FilterFieldsByDebuggerBrowsable(fields, typeIdsIncludingParent[i], token); - var regularObjects = await GetFieldsValues(regularFields, isOwn: i == 0); - var rootHiddenObjects = await GetFieldsValues(rootHiddenFields, isOwn: i == 0, isRootHidden: true); - - objectValues.AddRange(regularObjects); - objectValues.AddRange(rootHiddenObjects); + objectValues.AddRange(await GetFieldsValues(regularFields, isOwn)); + objectValues.AddRange(await GetFieldsValues(rootHiddenFields, isOwn, isRootHidden: true)); } if (!getCommandType.HasFlag(GetObjectCommandOptions.WithProperties)) return objectValues; From feafa66d614644ae4957b6508bd4bda3e5ef3f5e Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Tue, 14 Dec 2021 10:41:01 +0100 Subject: [PATCH 26/45] Corrected typo. --- src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index 0c38e82dccb82d..100967ed94a655 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -522,8 +522,8 @@ public TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDefi try { var propDefinition = metadataReader.GetPropertyDefinition(prop); - var propdName = metadataReader.GetString(propDefinition.Name); - AppendToBrowsable(DebuggerBrowsableProperties, propDefinition.GetCustomAttributes(), propdName); + var propName = metadataReader.GetString(propDefinition.Name); + AppendToBrowsable(DebuggerBrowsableProperties, propDefinition.GetCustomAttributes(), propName); } catch { continue; } } From 0ad80a4834cc491b838d235c68f503025536e4f0 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Tue, 14 Dec 2021 10:41:18 +0100 Subject: [PATCH 27/45] Added tests for properties. --- .../EvaluateOnCallFrameTests.cs | 53 ++++++----- .../debugger-test/debugger-evaluate-test.cs | 92 +++++++++++++++++-- 2 files changed, 115 insertions(+), 30 deletions(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index d23c44e10f4b3b..426b78f8f64d75 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -855,16 +855,18 @@ await RuntimeEvaluateAndCheck( ("\"15\"", TString("15"))); }); - [Fact] - public async Task EvaluateBrowsableFieldsNone() => await CheckInspectLocalsAtBreakpointSite( - "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 5, "Evaluate", + [Theory] + [InlineData("TestEvaluateFieldsNone", "testFieldsNone")] + [InlineData("TestEvaluatePropertiesNone", "testPropertiesNone")] + public async Task EvaluateBrowsableNone(string className, string localVarName) => await CheckInspectLocalsAtBreakpointSite( + "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 10, "Evaluate", "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateBrowsableProperties:Evaluate'); 1 })", wait_for_event_fn: async (pause_location) => { var id = pause_location["callFrames"][0]["callFrameId"].Value(); - var (testNone, _) = await EvaluateOnCallFrame(id, "testNone"); - await CheckValue(testNone, TObject("DebuggerTests.EvaluateBrowsableProperties.TestEvaluateNone"), nameof(testNone)); + var (testNone, _) = await EvaluateOnCallFrame(id, localVarName); + await CheckValue(testNone, TObject($"DebuggerTests.EvaluateBrowsableProperties.{className}"), nameof(testNone)); var testNoneProps = await GetProperties(testNone["objectId"]?.Value()); await CheckProps(testNoneProps, new { @@ -874,32 +876,36 @@ public async Task EvaluateBrowsableFieldsNone() => await CheckInspectLocalsAtBre }, "testNoneProps#1"); }); - [Fact] - public async Task EvaluateBrowsableFieldsNever() => await CheckInspectLocalsAtBreakpointSite( - "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 5, "Evaluate", + [Theory] + [InlineData("TestEvaluateFieldsNever", "testFieldsNever")] + [InlineData("TestEvaluatePropertiesNever", "testPropertiesNever")] + public async Task EvaluateBrowsableNever(string className, string localVarName) => await CheckInspectLocalsAtBreakpointSite( + "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 10, "Evaluate", "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateBrowsableProperties:Evaluate'); 1 })", wait_for_event_fn: async (pause_location) => { var id = pause_location["callFrames"][0]["callFrameId"].Value(); - var (testNever, _) = await EvaluateOnCallFrame(id, "testNever"); - await CheckValue(testNever, TObject("DebuggerTests.EvaluateBrowsableProperties.TestEvaluateNever"), nameof(testNever)); + var (testNever, _) = await EvaluateOnCallFrame(id, localVarName); + await CheckValue(testNever, TObject($"DebuggerTests.EvaluateBrowsableProperties.{className}"), nameof(testNever)); var testNeverProps = await GetProperties(testNever["objectId"]?.Value()); await CheckProps(testNeverProps, new { }, "testNeverProps#1"); }); - [Fact] - public async Task EvaluateBrowsableFieldsCollapsed() => await CheckInspectLocalsAtBreakpointSite( - "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 5, "Evaluate", + [Theory] + [InlineData("TestEvaluateFieldsCollapsed", "testFieldsCollapsed")] + [InlineData("TestEvaluatePropertiesCollapsed", "testPropertiesCollapsed")] + public async Task EvaluateBrowsableCollapsed(string className, string localVarName) => await CheckInspectLocalsAtBreakpointSite( + "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 10, "Evaluate", "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateBrowsableProperties:Evaluate'); 1 })", wait_for_event_fn: async (pause_location) => { var id = pause_location["callFrames"][0]["callFrameId"].Value(); - var (testCollapsed, _) = await EvaluateOnCallFrame(id, "testCollapsed"); - await CheckValue(testCollapsed, TObject("DebuggerTests.EvaluateBrowsableProperties.TestEvaluateCollapsed"), nameof(testCollapsed)); + var (testCollapsed, _) = await EvaluateOnCallFrame(id, localVarName); + await CheckValue(testCollapsed, TObject($"DebuggerTests.EvaluateBrowsableProperties.{className}"), nameof(testCollapsed)); var testCollapsedProps = await GetProperties(testCollapsed["objectId"]?.Value()); await CheckProps(testCollapsedProps, new { @@ -909,21 +915,24 @@ public async Task EvaluateBrowsableFieldsCollapsed() => await CheckInspectLocals }, "testCollapsedProps#1"); }); - [Fact] - public async Task EvaluateBrowsableFieldsRootHidden() => await CheckInspectLocalsAtBreakpointSite( - "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 5, "Evaluate", + //PROPS FAILING + [Theory] + [InlineData("TestEvaluateFieldsRootHidden", "testFieldsRootHidden")] + [InlineData("TestEvaluatePropertiesRootHidden", "testPropertiesRootHidden")] + public async Task EvaluateBrowsableRootHidden(string className, string localVarName) => await CheckInspectLocalsAtBreakpointSite( + "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 10, "Evaluate", "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateBrowsableProperties:Evaluate'); 1 })", wait_for_event_fn: async (pause_location) => { var id = pause_location["callFrames"][0]["callFrameId"].Value(); - var (testRootHidden, _) = await EvaluateOnCallFrame(id, "testRootHidden"); - await CheckValue(testRootHidden, TObject("DebuggerTests.EvaluateBrowsableProperties.TestEvaluateRootHidden"), nameof(testRootHidden)); + var (testRootHidden, _) = await EvaluateOnCallFrame(id, localVarName); + await CheckValue(testRootHidden, TObject($"DebuggerTests.EvaluateBrowsableProperties.{className}"), nameof(testRootHidden)); var testRootHiddenProps = await GetProperties(testRootHidden["objectId"]?.Value()); - var (refList, _) = await EvaluateOnCallFrame(id, "testNone.list"); + var (refList, _) = await EvaluateOnCallFrame(id, "testFieldsNone.list"); var refListProp = await GetProperties(refList["objectId"]?.Value()); var refListElementsProp = await GetProperties(refListProp[0]["value"]["objectId"]?.Value()); - var (refArray, _) = await EvaluateOnCallFrame(id, "testNone.array"); + var (refArray, _) = await EvaluateOnCallFrame(id, "testFieldsNone.array"); var refArrayProp = await GetProperties(refArray["objectId"]?.Value()); //in Console App names are in [] diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs index c7da7403d5463a..a213186b55fdce 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs @@ -498,14 +498,28 @@ public static void EvaluateLocals() public static class EvaluateBrowsableProperties { - public class TestEvaluateNone + public class TestEvaluateFieldsNone { public List list = new List() { 1, 2 }; public int[] array = new int[] { 11, 22 }; public string text = "text"; } - public class TestEvaluateNever + public class TestEvaluatePropertiesNone + { + public List list { get; set; } + public int[] array { get; set; } + public string text { get; set; } + + public TestEvaluatePropertiesNone() + { + list = new List() { 1, 2 }; + array = new int[] { 11, 22 }; + text = "text"; + } + } + + public class TestEvaluateFieldsNever { [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] public List listNever = new List() { 1, 2 }; @@ -517,7 +531,26 @@ public class TestEvaluateNever public string textNever = "textNever"; } - public class TestEvaluateCollapsed + public class TestEvaluatePropertiesNever + { + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + public List listNever { get; set; } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + public int[] arrayNever { get; set; } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + public string textNever { get; set; } + + public TestEvaluatePropertiesNever() + { + listNever = new List() { 1, 2 }; + arrayNever = new int[] { 11, 22 }; + textNever = "textNever"; + } + } + + public class TestEvaluateFieldsCollapsed { [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] public List listCollapsed = new List() { 1, 2 }; @@ -529,7 +562,26 @@ public class TestEvaluateCollapsed public string textCollapsed = "textCollapsed"; } - public class TestEvaluateRootHidden + public class TestEvaluatePropertiesCollapsed + { + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] + public List listCollapsed { get; set; } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] + public int[] arrayCollapsed { get; set; } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] + public string textCollapsed { get; set; } + + public TestEvaluatePropertiesCollapsed() + { + listCollapsed = new List() { 1, 2 }; + arrayCollapsed = new int[] { 11, 22 }; + textCollapsed = "textCollapsed"; + } + } + + public class TestEvaluateFieldsRootHidden { [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] public List listRootHidden = new List() { 1, 2 }; @@ -541,12 +593,36 @@ public class TestEvaluateRootHidden public string textRootHidden = "textRootHidden"; } + public class TestEvaluatePropertiesRootHidden + { + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] + public List listRootHidden { get; set; } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] + public int[] arrayRootHidden { get; set; } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] + public string textRootHidden { get; set; } + + public TestEvaluatePropertiesRootHidden() + { + listRootHidden = new List() { 1, 2 }; + arrayRootHidden = new int[] { 11, 22 }; + textRootHidden = "textRootHidden"; + } + } + public static void Evaluate() { - var testNone = new TestEvaluateNone(); - var testNever = new TestEvaluateNever(); - var testCollapsed = new TestEvaluateCollapsed(); - var testRootHidden = new TestEvaluateRootHidden(); + var testFieldsNone = new TestEvaluateFieldsNone(); + var testFieldsNever = new TestEvaluateFieldsNever(); + var testFieldsCollapsed = new TestEvaluateFieldsCollapsed(); + var testFieldsRootHidden = new TestEvaluateFieldsRootHidden(); + + var testPropertiesNone = new TestEvaluatePropertiesNone(); + var testPropertiesNever = new TestEvaluatePropertiesNever(); + var testPropertiesCollapsed = new TestEvaluatePropertiesCollapsed(); + var testPropertiesRootHidden = new TestEvaluatePropertiesRootHidden(); } } } From cd7f9da6eef87b79f7e7abf5dac86c395fbd6eaa Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Tue, 14 Dec 2021 10:42:13 +0100 Subject: [PATCH 28/45] Draft of changes for properties handling - never and root hidden failing. --- .../debugger/BrowserDebugProxy/MonoSDBHelper.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 39a14364674508..10e54ff79e7a5b 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2341,8 +2341,8 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions if (!getCommandType.HasFlag(GetObjectCommandOptions.AccessorPropertiesOnly)) { - var fields = await GetTypeFields(typeIdsIncludingParent[i], token); - var (regularFields, rootHiddenFields) = await FilterFieldsByDebuggerBrowsable(fields, typeIdsIncludingParent[i], token); + var fields = await GetTypeFields(typeId, token); + var (regularFields, rootHiddenFields) = await FilterFieldsByDebuggerBrowsable(fields, typeId, token); objectValues.AddRange(await GetFieldsValues(regularFields, isOwn)); objectValues.AddRange(await GetFieldsValues(rootHiddenFields, isOwn, isRootHidden: true)); @@ -2352,7 +2352,7 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions using var commandParamsObjWriter = new MonoBinaryWriter(); commandParamsObjWriter.WriteObj(new DotnetObjectId("object", $"{objectId}"), this); var props = await CreateJArrayForProperties( - typeIdsIncludingParent[i], + typeId, commandParamsObjWriter.GetParameterBuffer(), objectValues, getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute), @@ -2366,7 +2366,7 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions // 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;*/ } @@ -2487,6 +2487,7 @@ async Task GetFieldsValues(List fields, bool isOwn, bool var typeInfo = await GetTypeInfo(typeId, token); var typeFieldsBrowsableInfo = typeInfo?.Info?.DebuggerBrowsableFields; + var typeProperitesBrowsableInfo = typeInfo?.Info?.DebuggerBrowsableProperties; if (typeFieldsBrowsableInfo == null || typeFieldsBrowsableInfo.Count == 0) return (fields, new List()); @@ -2496,8 +2497,12 @@ async Task GetFieldsValues(List fields, bool isOwn, bool { if (!typeFieldsBrowsableInfo.TryGetValue(field.Name, out DebuggerBrowsableState? state)) { - regularFields.Add(field); - continue; + if (!typeProperitesBrowsableInfo.TryGetValue(field.Name, out DebuggerBrowsableState? propState)) + { + regularFields.Add(field); + continue; + } + state = propState; } switch (state) { From b6902bd152ab63c62479041b3544a0f021d140f0 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Tue, 14 Dec 2021 12:49:24 +0100 Subject: [PATCH 29/45] Fix for RootHidden properties. --- .../BrowserDebugProxy/MonoSDBHelper.cs | 30 ++++++++++++++++++- .../EvaluateOnCallFrameTests.cs | 1 - 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 10e54ff79e7a5b..5529b3fafc0dfe 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2359,7 +2359,7 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions $"dotnet:object:{objectId}", i == 0, token); - objectValues.AddRange(props); + objectValues.AddRange(await GetRegularProperties(props, typeId, token)); // ownProperties // Note: ownProperties should mean that we return members of the klass itself, @@ -2523,6 +2523,34 @@ async Task GetFieldsValues(List fields, bool isOwn, bool } return (regularFields, rootHiddenFields); } + + async Task GetRegularProperties(JArray props, 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(), out DebuggerBrowsableState? state)) + { + regularProps.Add(p); + continue; + } + switch (state) + { + case DebuggerBrowsableState.Never: + break; + case DebuggerBrowsableState.RootHidden: + break; + case DebuggerBrowsableState.Collapsed: + regularProps.Add(p); + break; + default: + throw new NotImplementedException($"DebuggerBrowsableState: {state}"); + } + } + return regularProps; + } } public async Task GetObjectProxy(int objectId, CancellationToken token) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index 426b78f8f64d75..97491748dda925 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -915,7 +915,6 @@ public async Task EvaluateBrowsableCollapsed(string className, string localVarNa }, "testCollapsedProps#1"); }); - //PROPS FAILING [Theory] [InlineData("TestEvaluateFieldsRootHidden", "testFieldsRootHidden")] [InlineData("TestEvaluatePropertiesRootHidden", "testPropertiesRootHidden")] From 8945dbed68c0375806b90c1cf1ee45ea86519ca5 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Tue, 14 Dec 2021 13:26:43 +0100 Subject: [PATCH 30/45] Added tests for static fields decorated with Browsable. --- .../EvaluateOnCallFrameTests.cs | 42 +++--- .../debugger-test/debugger-evaluate-test.cs | 130 ++++++++++++++++++ 2 files changed, 154 insertions(+), 18 deletions(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index 97491748dda925..a143c4b3f7f8d8 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -877,17 +877,19 @@ public async Task EvaluateBrowsableNone(string className, string localVarName) = }); [Theory] - [InlineData("TestEvaluateFieldsNever", "testFieldsNever")] - [InlineData("TestEvaluatePropertiesNever", "testPropertiesNever")] - public async Task EvaluateBrowsableNever(string className, string localVarName) => await CheckInspectLocalsAtBreakpointSite( - "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 10, "Evaluate", - "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateBrowsableProperties:Evaluate'); 1 })", + [InlineData("EvaluateBrowsableProperties", "TestEvaluateFieldsNever", "testFieldsNever")] + [InlineData("EvaluateBrowsableProperties", "TestEvaluatePropertiesNever", "testPropertiesNever")] + [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluateFieldsNever", "testFieldsNever")] + [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluatePropertiesNever", "testPropertiesNever")] + public async Task EvaluateBrowsableNever(string outerClassName, string className, string localVarName) => await CheckInspectLocalsAtBreakpointSite( + $"DebuggerTests.{outerClassName}", "Evaluate", 10, "Evaluate", + $"window.setTimeout(function() {{ invoke_static_method ('[debugger-test] DebuggerTests.{outerClassName}:Evaluate'); 1 }})", wait_for_event_fn: async (pause_location) => { var id = pause_location["callFrames"][0]["callFrameId"].Value(); var (testNever, _) = await EvaluateOnCallFrame(id, localVarName); - await CheckValue(testNever, TObject($"DebuggerTests.EvaluateBrowsableProperties.{className}"), nameof(testNever)); + await CheckValue(testNever, TObject($"DebuggerTests.{outerClassName}.{className}"), nameof(testNever)); var testNeverProps = await GetProperties(testNever["objectId"]?.Value()); await CheckProps(testNeverProps, new { @@ -895,17 +897,19 @@ public async Task EvaluateBrowsableNever(string className, string localVarName) }); [Theory] - [InlineData("TestEvaluateFieldsCollapsed", "testFieldsCollapsed")] - [InlineData("TestEvaluatePropertiesCollapsed", "testPropertiesCollapsed")] - public async Task EvaluateBrowsableCollapsed(string className, string localVarName) => await CheckInspectLocalsAtBreakpointSite( - "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 10, "Evaluate", - "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateBrowsableProperties:Evaluate'); 1 })", + [InlineData("EvaluateBrowsableProperties", "TestEvaluateFieldsCollapsed", "testFieldsCollapsed")] + [InlineData("EvaluateBrowsableProperties", "TestEvaluatePropertiesCollapsed", "testPropertiesCollapsed")] + [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluateFieldsCollapsed", "testFieldsCollapsed")] + [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluatePropertiesCollapsed", "testPropertiesCollapsed")] + public async Task EvaluateBrowsableCollapsed(string outerClassName, string className, string localVarName) => await CheckInspectLocalsAtBreakpointSite( + $"DebuggerTests.{outerClassName}", "Evaluate", 10, "Evaluate", + $"window.setTimeout(function() {{ invoke_static_method ('[debugger-test] DebuggerTests.{outerClassName}:Evaluate'); 1 }})", wait_for_event_fn: async (pause_location) => { var id = pause_location["callFrames"][0]["callFrameId"].Value(); var (testCollapsed, _) = await EvaluateOnCallFrame(id, localVarName); - await CheckValue(testCollapsed, TObject($"DebuggerTests.EvaluateBrowsableProperties.{className}"), nameof(testCollapsed)); + await CheckValue(testCollapsed, TObject($"DebuggerTests.{outerClassName}.{className}"), nameof(testCollapsed)); var testCollapsedProps = await GetProperties(testCollapsed["objectId"]?.Value()); await CheckProps(testCollapsedProps, new { @@ -916,17 +920,19 @@ public async Task EvaluateBrowsableCollapsed(string className, string localVarNa }); [Theory] - [InlineData("TestEvaluateFieldsRootHidden", "testFieldsRootHidden")] - [InlineData("TestEvaluatePropertiesRootHidden", "testPropertiesRootHidden")] - public async Task EvaluateBrowsableRootHidden(string className, string localVarName) => await CheckInspectLocalsAtBreakpointSite( - "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 10, "Evaluate", - "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateBrowsableProperties:Evaluate'); 1 })", + [InlineData("EvaluateBrowsableProperties", "TestEvaluateFieldsRootHidden", "testFieldsRootHidden")] + [InlineData("EvaluateBrowsableProperties", "TestEvaluatePropertiesRootHidden", "testPropertiesRootHidden")] + [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluateFieldsRootHidden", "testFieldsRootHidden")] + [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluatePropertiesRootHidden", "testPropertiesRootHidden")] + public async Task EvaluateBrowsableRootHidden(string outerClassName, string className, string localVarName) => await CheckInspectLocalsAtBreakpointSite( + $"DebuggerTests.{outerClassName}", "Evaluate", 10, "Evaluate", + $"window.setTimeout(function() {{ invoke_static_method ('[debugger-test] DebuggerTests.{outerClassName}:Evaluate'); 1 }})", wait_for_event_fn: async (pause_location) => { var id = pause_location["callFrames"][0]["callFrameId"].Value(); var (testRootHidden, _) = await EvaluateOnCallFrame(id, localVarName); - await CheckValue(testRootHidden, TObject($"DebuggerTests.EvaluateBrowsableProperties.{className}"), nameof(testRootHidden)); + await CheckValue(testRootHidden, TObject($"DebuggerTests.{outerClassName}.{className}"), nameof(testRootHidden)); var testRootHiddenProps = await GetProperties(testRootHidden["objectId"]?.Value()); var (refList, _) = await EvaluateOnCallFrame(id, "testFieldsNone.list"); var refListProp = await GetProperties(refList["objectId"]?.Value()); diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs index a213186b55fdce..c91babdf18a48b 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs @@ -625,6 +625,136 @@ public static void Evaluate() var testPropertiesRootHidden = new TestEvaluatePropertiesRootHidden(); } } + + public static class EvaluateBrowsableStaticProperties + { + public class TestEvaluateFieldsNone + { + public static List list = new List() { 1, 2 }; + public static int[] array = new int[] { 11, 22 }; + public static string text = "text"; + } + + public class TestEvaluatePropertiesNone + { + public static List list { get; set; } + public static int[] array { get; set; } + public static string text { get; set; } + + public TestEvaluatePropertiesNone() + { + list = new List() { 1, 2 }; + array = new int[] { 11, 22 }; + text = "text"; + } + } + + public class TestEvaluateFieldsNever + { + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + public static List listNever = new List() { 1, 2 }; + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + public static int[] arrayNever = new int[] { 11, 22 }; + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + public static string textNever = "textNever"; + } + + public class TestEvaluatePropertiesNever + { + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + public static List listNever { get; set; } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + public static int[] arrayNever { get; set; } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + public static string textNever { get; set; } + + public TestEvaluatePropertiesNever() + { + listNever = new List() { 1, 2 }; + arrayNever = new int[] { 11, 22 }; + textNever = "textNever"; + } + } + + public class TestEvaluateFieldsCollapsed + { + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] + public static List listCollapsed = new List() { 1, 2 }; + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] + public static int[] arrayCollapsed = new int[] { 11, 22 }; + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] + public static string textCollapsed = "textCollapsed"; + } + + public class TestEvaluatePropertiesCollapsed + { + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] + public static List listCollapsed { get; set; } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] + public static int[] arrayCollapsed { get; set; } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] + public static string textCollapsed { get; set; } + + public TestEvaluatePropertiesCollapsed() + { + listCollapsed = new List() { 1, 2 }; + arrayCollapsed = new int[] { 11, 22 }; + textCollapsed = "textCollapsed"; + } + } + + public class TestEvaluateFieldsRootHidden + { + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] + public static List listRootHidden = new List() { 1, 2 }; + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] + public static int[] arrayRootHidden = new int[] { 11, 22 }; + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] + public static string textRootHidden = "textRootHidden"; + } + + public class TestEvaluatePropertiesRootHidden + { + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] + public static List listRootHidden { get; set; } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] + public static int[] arrayRootHidden { get; set; } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] + public static string textRootHidden { get; set; } + + public TestEvaluatePropertiesRootHidden() + { + listRootHidden = new List() { 1, 2 }; + arrayRootHidden = new int[] { 11, 22 }; + textRootHidden = "textRootHidden"; + } + } + + public static void Evaluate() + { + var testFieldsNone = new TestEvaluateFieldsNone(); + var testFieldsNever = new TestEvaluateFieldsNever(); + var testFieldsCollapsed = new TestEvaluateFieldsCollapsed(); + var testFieldsRootHidden = new TestEvaluateFieldsRootHidden(); + + var testPropertiesNone = new TestEvaluatePropertiesNone(); + var testPropertiesNever = new TestEvaluatePropertiesNever(); + var testPropertiesCollapsed = new TestEvaluatePropertiesCollapsed(); + var testPropertiesRootHidden = new TestEvaluatePropertiesRootHidden(); + } + } } namespace DebuggerTestsV2 From 2c59af7aa87d90f9d2a6bf3513b08a5fa34e14b3 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Tue, 14 Dec 2021 13:35:41 +0100 Subject: [PATCH 31/45] Correct a typo. --- .../debugger/BrowserDebugProxy/MonoSDBHelper.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 5529b3fafc0dfe..442eb9417ee353 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2309,10 +2309,10 @@ public async Task GetValuesFromDebuggerProxyAttribute(int objectId, int public async Task GetObjectValues(int objectId, GetObjectCommandOptions getCommandType, CancellationToken token) { - var typeIdsIncludingParent = await GetTypeIdFromObject(objectId, true, token); + var typeIdsIncludingParents = await GetTypeIdFromObject(objectId, true, token); if (!getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerDisplayAttribute)) { - var debuggerProxy = await GetValuesFromDebuggerProxyAttribute(objectId, typeIdsIncludingParent[0], token); + var debuggerProxy = await GetValuesFromDebuggerProxyAttribute(objectId, typeIdsIncludingParents[0], token); if (debuggerProxy != null) return debuggerProxy; } @@ -2333,9 +2333,9 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions objectValues.Add(obj); return objectValues; } - for (int i = 0; i < typeIdsIncludingParent.Count; i++) + for (int i = 0; i < typeIdsIncludingParents.Count; i++) { - int typeId = typeIdsIncludingParent[i]; + int typeId = typeIdsIncludingParents[i]; // 0th id is for the object itself, and then its parents bool isOwn = i == 0; @@ -2373,10 +2373,10 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions if (getCommandType.HasFlag(GetObjectCommandOptions.AccessorPropertiesOnly)) { List> allFields = new List>(); - for (int i = 0; i < typeIdsIncludingParent.Count; i++) + for (int i = 0; i < typeIdsIncludingParents.Count; i++) { - var fields = await GetTypeFields(typeIdsIncludingParent[i], token); - var (regularFields, rootHiddenFields) = await FilterFieldsByDebuggerBrowsable(fields, typeIdsIncludingParent[i], token); + var fields = await GetTypeFields(typeIdsIncludingParents[i], token); + var (regularFields, rootHiddenFields) = await FilterFieldsByDebuggerBrowsable(fields, typeIdsIncludingParents[i], token); allFields.Add(regularFields); allFields.Add(rootHiddenFields); } From bdf671b0bcd949caab23c156b6d07b03ccab771f Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 17 Dec 2021 09:51:28 +0100 Subject: [PATCH 32/45] Undo merge unintentional changes. --- src/coreclr/pal/src/libunwind/README.md | 1 - src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/pal/src/libunwind/README.md b/src/coreclr/pal/src/libunwind/README.md index e845566c06f9bf..e69de29bb2d1d6 120000 --- a/src/coreclr/pal/src/libunwind/README.md +++ b/src/coreclr/pal/src/libunwind/README.md @@ -1 +0,0 @@ -README diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs index 22b8b165159885..398d640c0a01c8 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs @@ -654,6 +654,7 @@ public async Task MulticastDelegateTest() => await CheckInspectLocalsAtBreakpoin var this_props = await GetObjectOnLocals(frame_locals, "this"); await CheckProps(this_props, new { + TestEvent = TSymbol("System.EventHandler"), Delegate = TSymbol("System.MulticastDelegate") }, "this_props"); }); From 85928edbcac042553fa5646b71c05a35a76f570c Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 17 Dec 2021 13:12:38 +0100 Subject: [PATCH 33/45] Changing expected behavior for MulticastDelegateTest - in Console Application EventHandler is Browsable.Never by default so we should not expect it to be visible in the debug window. --- src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs index 398d640c0a01c8..22b8b165159885 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs @@ -654,7 +654,6 @@ public async Task MulticastDelegateTest() => await CheckInspectLocalsAtBreakpoin var this_props = await GetObjectOnLocals(frame_locals, "this"); await CheckProps(this_props, new { - TestEvent = TSymbol("System.EventHandler"), Delegate = TSymbol("System.MulticastDelegate") }, "this_props"); }); From b1acf8b546d1b95bea101290c25ec7f15cb78799 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 17 Dec 2021 13:15:45 +0100 Subject: [PATCH 34/45] Removing not relevant changes created after merge with main. --- src/coreclr/pal/src/libunwind/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/pal/src/libunwind/README.md b/src/coreclr/pal/src/libunwind/README.md index e69de29bb2d1d6..100b93820ade4c 120000 --- a/src/coreclr/pal/src/libunwind/README.md +++ b/src/coreclr/pal/src/libunwind/README.md @@ -0,0 +1 @@ +README \ No newline at end of file From 153f41a9727e4ac385c4a0de404827557209278a Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 17 Dec 2021 13:18:00 +0100 Subject: [PATCH 35/45] Remove file added in merge with main. --- .../external/brotli/fuzz/test_fuzzer.sh | 33 ------------------- 1 file changed, 33 deletions(-) delete mode 100644 src/native/external/brotli/fuzz/test_fuzzer.sh diff --git a/src/native/external/brotli/fuzz/test_fuzzer.sh b/src/native/external/brotli/fuzz/test_fuzzer.sh deleted file mode 100644 index 9985194a192544..00000000000000 --- a/src/native/external/brotli/fuzz/test_fuzzer.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash -set -e - -export CC=${CC:-cc} - -BROTLI="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd )" -SRC=$BROTLI/c - -cd $BROTLI - -rm -rf bin -mkdir bin -cd bin - -cmake $BROTLI -DCMAKE_C_COMPILER="$CC" \ - -DBUILD_TESTING=OFF -DENABLE_SANITIZER=address -make -j$(nproc) brotlidec-static - -${CC} -o run_decode_fuzzer -std=c99 -fsanitize=address -I$SRC/include \ - $SRC/fuzz/decode_fuzzer.c $SRC/fuzz/run_decode_fuzzer.c \ - ./libbrotlidec-static.a ./libbrotlicommon-static.a - -mkdir decode_corpora -unzip $BROTLI/java/org/brotli/integration/fuzz_data.zip -d decode_corpora - -for f in `ls decode_corpora` -do - echo "Testing $f" - ./run_decode_fuzzer decode_corpora/$f -done - -cd $BROTLI -rm -rf bin From 1276724ebb857537fc532cf74a05c7c5f8d6e774 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 17 Dec 2021 13:18:57 +0100 Subject: [PATCH 36/45] Revert "Removing not relevant changes created after merge with main." This reverts commit b1acf8b546d1b95bea101290c25ec7f15cb78799. --- src/coreclr/pal/src/libunwind/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/pal/src/libunwind/README.md b/src/coreclr/pal/src/libunwind/README.md index 100b93820ade4c..e69de29bb2d1d6 120000 --- a/src/coreclr/pal/src/libunwind/README.md +++ b/src/coreclr/pal/src/libunwind/README.md @@ -1 +0,0 @@ -README \ No newline at end of file From f943552198c62091bd688c86155f442e798104a2 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 17 Dec 2021 13:20:44 +0100 Subject: [PATCH 37/45] Revert. --- .../external/brotli/fuzz/test_fuzzer.sh | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/native/external/brotli/fuzz/test_fuzzer.sh diff --git a/src/native/external/brotli/fuzz/test_fuzzer.sh b/src/native/external/brotli/fuzz/test_fuzzer.sh new file mode 100644 index 00000000000000..9985194a192544 --- /dev/null +++ b/src/native/external/brotli/fuzz/test_fuzzer.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -e + +export CC=${CC:-cc} + +BROTLI="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd )" +SRC=$BROTLI/c + +cd $BROTLI + +rm -rf bin +mkdir bin +cd bin + +cmake $BROTLI -DCMAKE_C_COMPILER="$CC" \ + -DBUILD_TESTING=OFF -DENABLE_SANITIZER=address +make -j$(nproc) brotlidec-static + +${CC} -o run_decode_fuzzer -std=c99 -fsanitize=address -I$SRC/include \ + $SRC/fuzz/decode_fuzzer.c $SRC/fuzz/run_decode_fuzzer.c \ + ./libbrotlidec-static.a ./libbrotlicommon-static.a + +mkdir decode_corpora +unzip $BROTLI/java/org/brotli/integration/fuzz_data.zip -d decode_corpora + +for f in `ls decode_corpora` +do + echo "Testing $f" + ./run_decode_fuzzer decode_corpora/$f +done + +cd $BROTLI +rm -rf bin From 7c7c6201fe36c636ec0b88f0a3f65878d454a525 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 17 Dec 2021 13:42:23 +0100 Subject: [PATCH 38/45] Revert revert. --- src/coreclr/pal/src/libunwind/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/pal/src/libunwind/README.md b/src/coreclr/pal/src/libunwind/README.md index e69de29bb2d1d6..100b93820ade4c 120000 --- a/src/coreclr/pal/src/libunwind/README.md +++ b/src/coreclr/pal/src/libunwind/README.md @@ -0,0 +1 @@ +README \ No newline at end of file From e148b2de7797191b40b8b8d2af10fd5cb5094985 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 20 Dec 2021 14:41:49 +0100 Subject: [PATCH 39/45] One broken test for custom getter. --- .../BrowserDebugProxy/DevToolsHelper.cs | 12 ++- .../MemberReferenceResolver.cs | 4 +- .../debugger/BrowserDebugProxy/MonoProxy.cs | 7 ++ .../BrowserDebugProxy/MonoSDBHelper.cs | 65 +++++++----- .../DebuggerTestSuite/DebuggerTestBase.cs | 13 ++- .../EvaluateOnCallFrameTests.cs | 98 ++++++++++++------- .../DebuggerTestSuite/GetPropertiesTests.cs | 6 +- .../debugger-test/debugger-evaluate-test.cs | 54 ++++++++++ 8 files changed, 183 insertions(+), 76 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs index ee7def9514c54c..082249e4344d80 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs @@ -61,6 +61,8 @@ internal class DotnetObjectId { public string Scheme { get; } public string Value { get; } + public string SubScheme { get; } + public string SubValue { get; } public static bool TryParse(JToken jToken, out DotnetObjectId objectId) => TryParse(jToken?.Value(), out objectId); @@ -73,20 +75,22 @@ public static bool TryParse(string id, out DotnetObjectId objectId) if (!id.StartsWith("dotnet:")) return false; - string[] parts = id.Split(":", 3); + string[] parts = id.Split(":", 5); - if (parts.Length < 3) + if (parts.Length < 3 || parts.Length == 4) return false; - objectId = new DotnetObjectId(parts[1], parts[2]); + objectId = new DotnetObjectId(parts[1], parts[2], parts.Length > 3 ? parts[3] : "", parts.Length > 3 ? parts[4] : ""); return true; } - public DotnetObjectId(string scheme, string value) + public DotnetObjectId(string scheme, string value, string subscheme = "", string subvalue = "") { Scheme = scheme; Value = value; + SubScheme = subscheme; + SubValue = subvalue; } public override string ToString() => $"dotnet:{Scheme}:{Value}"; diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs index 8516b349f4cf23..4927c49da28eaf 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs @@ -347,9 +347,7 @@ public async Task Resolve(ElementAccessExpressionSyntax elementAccess, case "object": var typeIds = await context.SdbAgent.GetTypeIdFromObject(int.Parse(objectId.Value), true, token); int methodId = await context.SdbAgent.GetMethodIdByName(typeIds[0], "ToArray", token); - var commandParamsObjWriter = new MonoBinaryWriter(); - commandParamsObjWriter.WriteObj(objectId, context.SdbAgent); - var toArrayRetMethod = await context.SdbAgent.InvokeMethod(commandParamsObjWriter.GetParameterBuffer(), methodId, elementAccess.Expression.ToString(), token); + var toArrayRetMethod = await context.SdbAgent.InvokeMethodInObject(int.Parse(objectId.Value), methodId, elementAccess.Expression.ToString(), token); rootObject = await GetValueFromObject(toArrayRetMethod, token); DotnetObjectId.TryParse(rootObject?["objectId"]?.Value(), out DotnetObjectId arrayObjectId); rootObject["value"] = await context.SdbAgent.GetArrayValues(int.Parse(arrayObjectId.Value), token); diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index e0f796e09c5ad2..8766f9a41b65b1 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -688,7 +688,14 @@ internal async Task RuntimeGetPropertiesInternal(SessionId id, DotnetObj case "array": return await context.SdbAgent.GetArrayValues(int.Parse(objectId.Value), token); case "object": + { + if (objectId.SubScheme == "methodId") //getter + { + var objRet = await context.SdbAgent.InvokeMethodInObject(int.Parse(objectId.Value), int.Parse(objectId.SubValue), "", token); + return new JArray(objRet); + } return await context.SdbAgent.GetObjectValues(int.Parse(objectId.Value), objectValuesOpt, token); + } case "pointer": return new JArray{await context.SdbAgent.GetPointerContent(int.Parse(objectId.Value), token)}; case "cfo_res": diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index a242bf02a6dce9..1b330c6161d7ab 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -503,7 +503,7 @@ protected unsafe void WriteBigEndian(T val) where T : struct base.Write(data); } - private void Write(ElementType type, T value) where T : struct => Write((byte)type, value); + internal void Write(ElementType type, T value) where T : struct => Write((byte)type, value); private void Write(T1 type, T2 value) where T1 : struct where T2 : struct { @@ -1538,6 +1538,13 @@ public async Task InvokeMethod(ArraySegment valueTypeBuffer, int return await CreateJObjectForVariableValue(retDebuggerCmdReader, varName, false, -1, false, token); } + public async Task InvokeMethodInObject(int objectId, int methodId, string varName, CancellationToken token) + { + using var commandParamsObjWriter = new MonoBinaryWriter(); + commandParamsObjWriter.Write(ElementType.Class, objectId); + return await InvokeMethod(commandParamsObjWriter.GetParameterBuffer(), methodId, varName, token); + } + public async Task GetPropertyMethodIdByName(int typeId, string propertyName, CancellationToken token) { using var retDebuggerCmdReader = await GetTypePropertiesReader(typeId, token); @@ -2407,6 +2414,33 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions } return objectValues; + async Task AppendRootHiddenChildren(JObject root, JArray expandedCollection) + { + if (!DotnetObjectId.TryParse(root?["value"]?["objectId"]?.Value(), out DotnetObjectId rootHiddenObjectId)) + return; + if (!int.TryParse(rootHiddenObjectId?.Value, out int rootHiddenObjectIdInt)) + return; + + var resultValue = new JArray(); + // collections require extracting items to get inner values; items are of array type + // arrays have "subtype": "array" field, collections don't + var subtype = root?["value"]?["subtype"]; + if (subtype == null || subtype?.Value() != "array") + { + resultValue = await GetObjectValues(rootHiddenObjectIdInt, getCommandType, token); + DotnetObjectId.TryParse(resultValue[0]?["value"]?["objectId"]?.Value(), out DotnetObjectId objectId2); + int.TryParse(objectId2.Value, out rootHiddenObjectIdInt); + } + resultValue = await GetArrayValues(rootHiddenObjectIdInt, token); + + // root hidden item name has to be unique, so we concatenate the root's name to it + foreach (var item in resultValue) + { + item["name"] = string.Concat(root["name"], "[", item["name"], "]"); + expandedCollection.Add(item); + } + } + async Task GetFieldsValues(List fields, bool isOwn, bool isRootHidden = false) { JArray objFields = new JArray(); @@ -2453,29 +2487,7 @@ async Task GetFieldsValues(List fields, bool isOwn, bool objFields.Add(fieldValue); continue; } - if (!DotnetObjectId.TryParse(fieldValue?["value"]?["objectId"]?.Value(), out DotnetObjectId rootHiddenObjectId)) - continue; - if (!int.TryParse(rootHiddenObjectId?.Value, out int rootHiddenObjectIdInt)) - continue; - - var resultValue = new JArray(); - // collections require extracting items to get inner values; items are of array type - // arrays have "subtype": "array" field, collections don't - var subtype = fieldValue?["value"]?["subtype"]; - if (subtype == null || subtype?.Value() != "array") - { - resultValue = await GetObjectValues(rootHiddenObjectIdInt, getCommandType, token); - DotnetObjectId.TryParse(resultValue[0]?["value"]?["objectId"]?.Value(), out DotnetObjectId objectId2); - int.TryParse(objectId2.Value, out rootHiddenObjectIdInt); - } - resultValue = await GetArrayValues(rootHiddenObjectIdInt, token); - - // root hidden item name has to be unique, so we concatenate the root's name to it - foreach (var item in resultValue) - { - item["name"] = string.Concat(fieldValue["name"], "[", item["name"], "]"); - objFields.Add(item); - } + await AppendRootHiddenChildren(fieldValue, objFields); } return objFields; } @@ -2541,6 +2553,11 @@ async Task GetRegularProperties(JArray props, int typeId, CancellationTo case DebuggerBrowsableState.Never: break; case DebuggerBrowsableState.RootHidden: + DotnetObjectId rootObjId; + DotnetObjectId.TryParse(p["get"]["objectId"].Value(), out rootObjId); + var rootObject = await InvokeMethodInObject(int.Parse(rootObjId.Value), int.Parse(rootObjId.SubValue), p["name"].Value(), token); + Console.WriteLine(p); + await AppendRootHiddenChildren(rootObject, regularProps); break; case DebuggerBrowsableState.Collapsed: regularProps.Add(p); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index fbc176dc2ca5e3..9bbe39a44c7837 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -623,7 +623,12 @@ internal async Task CheckCustomType(JToken actual_val, JToken exp_val, string la AssertEqual("Function", get["className"]?.Value(), $"{label}-className"); AssertStartsWith($"get {exp_val["type_name"]?.Value()} ()", get["description"]?.Value(), $"{label}-description"); AssertEqual("function", get["type"]?.Value(), $"{label}-type"); - + var expectedValue = exp_val["value"]; + if (expectedValue.Type != JTokenType.Null) + { + var valueAfterRunGet = await GetProperties(get["objectId"]?.Value()); + await CheckValue(valueAfterRunGet[0]?["value"], expectedValue, exp_val["type_name"]?.Value()); + } break; } @@ -668,7 +673,7 @@ internal async Task CheckProps(JToken actual, object exp_o, string label, int nu return; } - + // Not an array var exp = exp_o as JObject; if (exp == null) @@ -797,7 +802,7 @@ internal async Task GetObjectOnLocals(JToken locals, string name) /* @fn_args is for use with `Runtime.callFunctionOn` only */ internal async Task GetProperties(string id, JToken fn_args = null, bool? own_properties = null, bool? accessors_only = null, bool expect_ok = true) { - if (UseCallFunctionOnBeforeGetProperties && !id.StartsWith("dotnet:scope:")) + if (UseCallFunctionOnBeforeGetProperties && !id.StartsWith("dotnet:scope:")) //FALSE { var fn_decl = "function () { return this; }"; var cfo_args = JObject.FromObject(new @@ -1059,7 +1064,7 @@ internal static JObject TDelegate(string className, string target) => JObject.Fr internal static JObject TIgnore() => JObject.FromObject(new { __custom_type = "ignore_me" }); - internal static JObject TGetter(string type) => JObject.FromObject(new { __custom_type = "getter", type_name = type }); + internal static JObject TGetter(string type, JObject value = null) => JObject.FromObject(new { __custom_type = "getter", type_name = type, value = value}); internal static JObject TDateTime(DateTime dt) => JObject.FromObject(new { diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index 9e55cf7f47b81c..97f042be385a26 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -856,33 +856,44 @@ await RuntimeEvaluateAndCheck( }); [Theory] - [InlineData("TestEvaluateFieldsNone", "testFieldsNone")] - [InlineData("TestEvaluatePropertiesNone", "testPropertiesNone")] - public async Task EvaluateBrowsableNone(string className, string localVarName) => await CheckInspectLocalsAtBreakpointSite( - "DebuggerTests.EvaluateBrowsableProperties", "Evaluate", 10, "Evaluate", - "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateBrowsableProperties:Evaluate'); 1 })", + [InlineData("EvaluateBrowsableProperties", "TestEvaluateFieldsNone", "testFieldsNone", 10)] + [InlineData("EvaluateBrowsableProperties", "TestEvaluatePropertiesNone", "testPropertiesNone", 10)] + [InlineData("EvaluateBrowsableCustomProperties", "TestEvaluatePropertiesNone", "testPropertiesNone", 5, true)] + public async Task EvaluateBrowsableNone(string outerClassName, string className, string localVarName, int breakLine, bool isCustomGetter = false) => await CheckInspectLocalsAtBreakpointSite( + $"DebuggerTests.{outerClassName}", "Evaluate", breakLine, "Evaluate", + $"window.setTimeout(function() {{ invoke_static_method ('[debugger-test] DebuggerTests.{outerClassName}:Evaluate'); 1 }})", wait_for_event_fn: async (pause_location) => { var id = pause_location["callFrames"][0]["callFrameId"].Value(); var (testNone, _) = await EvaluateOnCallFrame(id, localVarName); - await CheckValue(testNone, TObject($"DebuggerTests.EvaluateBrowsableProperties.{className}"), nameof(testNone)); + await CheckValue(testNone, TObject($"DebuggerTests.{outerClassName}.{className}"), nameof(testNone)); var testNoneProps = await GetProperties(testNone["objectId"]?.Value()); - await CheckProps(testNoneProps, new - { - list = TObject("System.Collections.Generic.List", description: "Count = 2"), - array = TObject("int[]", description: "int[2]"), - text = TString("text") - }, "testNoneProps#1"); + + if (isCustomGetter) + await CheckProps(testNoneProps, new + { + list = TGetter("list", TObject("System.Collections.Generic.List", description: "Count = 2")), + array = TGetter("array", TObject("int[]", description: "int[2]")), + text = TGetter("text", TString("text")) + }, "testNoneProps#1"); + else + await CheckProps(testNoneProps, new + { + list = TObject("System.Collections.Generic.List", description: "Count = 2"), + array = TObject("int[]", description: "int[2]"), + text = TString("text") + }, "testNoneProps#1"); }); [Theory] - [InlineData("EvaluateBrowsableProperties", "TestEvaluateFieldsNever", "testFieldsNever")] - [InlineData("EvaluateBrowsableProperties", "TestEvaluatePropertiesNever", "testPropertiesNever")] - [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluateFieldsNever", "testFieldsNever")] - [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluatePropertiesNever", "testPropertiesNever")] - public async Task EvaluateBrowsableNever(string outerClassName, string className, string localVarName) => await CheckInspectLocalsAtBreakpointSite( - $"DebuggerTests.{outerClassName}", "Evaluate", 10, "Evaluate", + [InlineData("EvaluateBrowsableProperties", "TestEvaluateFieldsNever", "testFieldsNever", 10)] + [InlineData("EvaluateBrowsableProperties", "TestEvaluatePropertiesNever", "testPropertiesNever", 10)] + [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluateFieldsNever", "testFieldsNever", 10)] + [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluatePropertiesNever", "testPropertiesNever", 10)] + [InlineData("EvaluateBrowsableCustomProperties", "TestEvaluatePropertiesNever", "testPropertiesNever", 5)] + public async Task EvaluateBrowsableNever(string outerClassName, string className, string localVarName, int breakLine) => await CheckInspectLocalsAtBreakpointSite( + $"DebuggerTests.{outerClassName}", "Evaluate", breakLine, "Evaluate", $"window.setTimeout(function() {{ invoke_static_method ('[debugger-test] DebuggerTests.{outerClassName}:Evaluate'); 1 }})", wait_for_event_fn: async (pause_location) => { @@ -897,12 +908,13 @@ public async Task EvaluateBrowsableNever(string outerClassName, string className }); [Theory] - [InlineData("EvaluateBrowsableProperties", "TestEvaluateFieldsCollapsed", "testFieldsCollapsed")] - [InlineData("EvaluateBrowsableProperties", "TestEvaluatePropertiesCollapsed", "testPropertiesCollapsed")] - [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluateFieldsCollapsed", "testFieldsCollapsed")] - [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluatePropertiesCollapsed", "testPropertiesCollapsed")] - public async Task EvaluateBrowsableCollapsed(string outerClassName, string className, string localVarName) => await CheckInspectLocalsAtBreakpointSite( - $"DebuggerTests.{outerClassName}", "Evaluate", 10, "Evaluate", + [InlineData("EvaluateBrowsableProperties", "TestEvaluateFieldsCollapsed", "testFieldsCollapsed", 10)] + [InlineData("EvaluateBrowsableProperties", "TestEvaluatePropertiesCollapsed", "testPropertiesCollapsed", 10)] + [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluateFieldsCollapsed", "testFieldsCollapsed", 10)] + [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluatePropertiesCollapsed", "testPropertiesCollapsed", 10)] + [InlineData("EvaluateBrowsableCustomProperties", "TestEvaluatePropertiesCollapsed", "testPropertiesCollapsed", 5, true)] + public async Task EvaluateBrowsableCollapsed(string outerClassName, string className, string localVarName, int breakLine, bool isCustomGetter = false) => await CheckInspectLocalsAtBreakpointSite( + $"DebuggerTests.{outerClassName}", "Evaluate", breakLine, "Evaluate", $"window.setTimeout(function() {{ invoke_static_method ('[debugger-test] DebuggerTests.{outerClassName}:Evaluate'); 1 }})", wait_for_event_fn: async (pause_location) => { @@ -911,21 +923,30 @@ public async Task EvaluateBrowsableCollapsed(string outerClassName, string class var (testCollapsed, _) = await EvaluateOnCallFrame(id, localVarName); await CheckValue(testCollapsed, TObject($"DebuggerTests.{outerClassName}.{className}"), nameof(testCollapsed)); var testCollapsedProps = await GetProperties(testCollapsed["objectId"]?.Value()); - await CheckProps(testCollapsedProps, new - { - listCollapsed = TObject("System.Collections.Generic.List", description: "Count = 2"), - arrayCollapsed = TObject("int[]", description: "int[2]"), - textCollapsed = TString("textCollapsed") - }, "testCollapsedProps#1"); + if (isCustomGetter) + await CheckProps(testCollapsedProps, new + { + listCollapsed = TGetter("listCollapsed", TObject("System.Collections.Generic.List", description: "Count = 2")), + arrayCollapsed = TGetter("arrayCollapsed", TObject("int[]", description: "int[2]")), + textCollapsed = TGetter("textCollapsed", TString("textCollapsed")) + }, "testCollapsedProps#1"); + else + await CheckProps(testCollapsedProps, new + { + listCollapsed = TObject("System.Collections.Generic.List", description: "Count = 2"), + arrayCollapsed = TObject("int[]", description: "int[2]"), + textCollapsed = TString("textCollapsed") + }, "testCollapsedProps#1"); }); [Theory] - [InlineData("EvaluateBrowsableProperties", "TestEvaluateFieldsRootHidden", "testFieldsRootHidden")] - [InlineData("EvaluateBrowsableProperties", "TestEvaluatePropertiesRootHidden", "testPropertiesRootHidden")] - [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluateFieldsRootHidden", "testFieldsRootHidden")] - [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluatePropertiesRootHidden", "testPropertiesRootHidden")] - public async Task EvaluateBrowsableRootHidden(string outerClassName, string className, string localVarName) => await CheckInspectLocalsAtBreakpointSite( - $"DebuggerTests.{outerClassName}", "Evaluate", 10, "Evaluate", + [InlineData("EvaluateBrowsableProperties", "TestEvaluateFieldsRootHidden", "testFieldsRootHidden", 10)] + [InlineData("EvaluateBrowsableProperties", "TestEvaluatePropertiesRootHidden", "testPropertiesRootHidden", 10)] + [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluateFieldsRootHidden", "testFieldsRootHidden", 10)] + [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluatePropertiesRootHidden", "testPropertiesRootHidden", 10)] + [InlineData("EvaluateBrowsableCustomProperties", "TestEvaluatePropertiesRootHidden", "testPropertiesRootHidden", 5, true)] //FAILING + public async Task EvaluateBrowsableRootHidden(string outerClassName, string className, string localVarName, int breakLine, bool isCustomGetter = false) => await CheckInspectLocalsAtBreakpointSite( + $"DebuggerTests.{outerClassName}", "Evaluate", breakLine, "Evaluate", $"window.setTimeout(function() {{ invoke_static_method ('[debugger-test] DebuggerTests.{outerClassName}:Evaluate'); 1 }})", wait_for_event_fn: async (pause_location) => { @@ -934,10 +955,10 @@ public async Task EvaluateBrowsableRootHidden(string outerClassName, string clas var (testRootHidden, _) = await EvaluateOnCallFrame(id, localVarName); await CheckValue(testRootHidden, TObject($"DebuggerTests.{outerClassName}.{className}"), nameof(testRootHidden)); var testRootHiddenProps = await GetProperties(testRootHidden["objectId"]?.Value()); - var (refList, _) = await EvaluateOnCallFrame(id, "testFieldsNone.list"); + var (refList, _) = await EvaluateOnCallFrame(id, "testPropertiesNone.list"); var refListProp = await GetProperties(refList["objectId"]?.Value()); var refListElementsProp = await GetProperties(refListProp[0]["value"]["objectId"]?.Value()); - var (refArray, _) = await EvaluateOnCallFrame(id, "testFieldsNone.array"); + var (refArray, _) = await EvaluateOnCallFrame(id, "testPropertiesNone.array"); var refArrayProp = await GetProperties(refArray["objectId"]?.Value()); //in Console App names are in [] @@ -950,6 +971,7 @@ public async Task EvaluateBrowsableRootHidden(string outerClassName, string clas { item["name"] = string.Concat("listRootHidden[", item["name"], "]"); } + // Console.WriteLine(testRootHiddenProps); var mergedRefItems = new JArray(refListElementsProp.Union(refArrayProp)); Assert.Equal(mergedRefItems, testRootHiddenProps); }); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs index ca5afa742cfb3f..e16e8b38da898e 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs @@ -350,9 +350,9 @@ public async Task GetObjectValueWithInheritance() j = TNumber(20), i = TNumber(50), k = TNumber(30), - GetJ = TGetter("GetJ"), - GetI = TGetter("GetI"), - GetK = TGetter("GetK") + GetJ = TGetter("GetJ", TNumber(20)), + GetI = TGetter("GetI", TNumber(50)), + GetK = TGetter("GetK", TNumber(30)) }, "test_props"); await EvaluateOnCallFrameAndCheck(frame_id, ($"test.GetJ", TNumber(20)), diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs index 8d0ee36f92f589..b0ff3f95ba3de3 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs @@ -763,6 +763,60 @@ public static void Evaluate() var testPropertiesRootHidden = new TestEvaluatePropertiesRootHidden(); } } + + public static class EvaluateBrowsableCustomProperties + { + public class TestEvaluatePropertiesNone + { + public List list { get { return new List() { 1, 2 }; } } + public int[] array { get { return new int[] { 11, 22 }; } } + public string text { get { return "text"; } } + } + + public class TestEvaluatePropertiesNever + { + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + public List listNever { get { return new List() { 1, 2 }; } } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + public int[] arrayNever { get { return new int[] { 11, 22 }; } } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] + public string textNever { get { return "textNever"; } } + } + + public class TestEvaluatePropertiesCollapsed + { + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] + public List listCollapsed { get { return new List() { 1, 2 }; } } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] + public int[] arrayCollapsed { get { return new int[] { 11, 22 }; } } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Collapsed)] + public string textCollapsed { get { return "textCollapsed"; } } + } + + public class TestEvaluatePropertiesRootHidden + { + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] + public List listRootHidden { get { return new List() { 1, 2 }; } } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] + public int[] arrayRootHidden { get { return new int[] { 11, 22 }; } } + + [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)] + public string textRootHidden { get { return "textRootHidden"; } } + } + + public static void Evaluate() + { + var testPropertiesNone = new TestEvaluatePropertiesNone(); + var testPropertiesNever = new TestEvaluatePropertiesNever(); + var testPropertiesCollapsed = new TestEvaluatePropertiesCollapsed(); + var testPropertiesRootHidden = new TestEvaluatePropertiesRootHidden(); + } + } } namespace DebuggerTestsV2 From 28ef9b0ced2d68494dda8957755d93d96422ee24 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 20 Dec 2021 16:44:03 +0100 Subject: [PATCH 40/45] Ugly fix to make all the tests work. --- .../wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 1b330c6161d7ab..fc63d3640cd765 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2366,7 +2366,13 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions $"dotnet:object:{objectId}", i == 0, token); - objectValues.AddRange(await GetRegularProperties(props, typeId, token)); + var properties = await GetRegularProperties(props, typeId, token); + foreach (var p in properties) + { + if (objectValues.Any(x => x["name"].Value().Equals(p["name"].Value()))) + continue; + objectValues.Add(p); + } // ownProperties // Note: ownProperties should mean that we return members of the klass itself, @@ -2556,7 +2562,6 @@ async Task GetRegularProperties(JArray props, int typeId, CancellationTo DotnetObjectId rootObjId; DotnetObjectId.TryParse(p["get"]["objectId"].Value(), out rootObjId); var rootObject = await InvokeMethodInObject(int.Parse(rootObjId.Value), int.Parse(rootObjId.SubValue), p["name"].Value(), token); - Console.WriteLine(p); await AppendRootHiddenChildren(rootObject, regularProps); break; case DebuggerBrowsableState.Collapsed: From 8cf81dc8aed3365f4e7e22c071f873ef3a982031 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Tue, 21 Dec 2021 12:32:59 +0100 Subject: [PATCH 41/45] Refactored JArray aggregation to Dictionary. --- .../BrowserDebugProxy/MonoSDBHelper.cs | 115 +++++++++--------- 1 file changed, 57 insertions(+), 58 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index fc63d3640cd765..1b5f9291a98195 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2323,23 +2323,23 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions if (debuggerProxy != null) return debuggerProxy; } - JArray objectValues = new JArray(); if (await IsDelegate(objectId, token)) { var description = await GetDelegateMethodDescription(objectId, token); - - var obj = JObject.FromObject(new { - value = new - { - type = "symbol", - value = description, - description - }, - name = "Target" - }); - objectValues.Add(obj); - return objectValues; + return new JArray( + JObject.FromObject(new + { + value = new + { + type = "symbol", + value = description, + description + }, + name = "Target" + })); } + + var objects = new Dictionary(); for (int i = 0; i < typeIdsIncludingParents.Count; i++) { int typeId = typeIdsIncludingParents[i]; @@ -2349,30 +2349,28 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions if (!getCommandType.HasFlag(GetObjectCommandOptions.AccessorPropertiesOnly)) { var fields = await GetTypeFields(typeId, token); - var (regularFields, rootHiddenFields) = await FilterFieldsByDebuggerBrowsable(fields, typeId, token); + var (collapsedFields, rootHiddenFields) = await FilterFieldsByDebuggerBrowsable(fields, typeId, token); + + var collapsedFieldsValues = await GetFieldsValues(collapsedFields, isOwn); + var hiddenFieldsValues = await GetFieldsValues(rootHiddenFields, isOwn, isRootHidden: true); - objectValues.AddRange(await GetFieldsValues(regularFields, isOwn)); - objectValues.AddRange(await GetFieldsValues(rootHiddenFields, isOwn, isRootHidden: true)); + objects.TryAddRange(collapsedFieldsValues); + objects.TryAddRange(hiddenFieldsValues); } if (!getCommandType.HasFlag(GetObjectCommandOptions.WithProperties)) - return objectValues; + return new JArray(objects.Values); using var commandParamsObjWriter = new MonoBinaryWriter(); commandParamsObjWriter.WriteObj(new DotnetObjectId("object", $"{objectId}"), this); var props = await CreateJArrayForProperties( typeId, commandParamsObjWriter.GetParameterBuffer(), - objectValues, + new JArray(objects.Values), getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute), $"dotnet:object:{objectId}", i == 0, token); var properties = await GetRegularProperties(props, typeId, token); - foreach (var p in properties) - { - if (objectValues.Any(x => x["name"].Value().Equals(p["name"].Value()))) - continue; - objectValues.Add(p); - } + objects.TryAddRange(properties); // ownProperties // Note: ownProperties should mean that we return members of the klass itself, @@ -2385,40 +2383,30 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions } if (getCommandType.HasFlag(GetObjectCommandOptions.AccessorPropertiesOnly)) { - List> allFields = new List>(); - for (int i = 0; i < typeIdsIncludingParents.Count; i++) - { - var fields = await GetTypeFields(typeIdsIncludingParents[i], token); - var (regularFields, rootHiddenFields) = await FilterFieldsByDebuggerBrowsable(fields, typeIdsIncludingParents[i], token); - allFields.Add(regularFields); - allFields.Add(rootHiddenFields); - } - var retAfterRemove = new JArray(); - foreach (var item in objectValues) - { - bool foundField = false; - for (int j = 0; j < allFields.Count; j++) - { - foreach (var field in allFields[j]) - { - if (field.Name.Equals(item["name"].Value())) - { - if (item["isOwn"] == null || (item["isOwn"].Value() && j == 0) || !item["isOwn"].Value()) - foundField = true; - break; - } - } - if (foundField) - break; - } - if (!foundField) - { - retAfterRemove.Add(item); - } - } - objectValues = retAfterRemove; + var ownId = typeIdsIncludingParents[0]; + var ownFields = await GetTypeFields(ownId, token); + + var parentFields = new List(); + 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) && + (obj.Value["isOwn"] == null || + obj.Value["isOwn"].Value() || + !obj.Value["isOwn"].Value()))); + + var parentDuplicatedFields = parentFields.Where(field => objects.Any(obj => + field.Name.Equals(obj.Key) && + (obj.Value["isOwn"] == null || + !obj.Value["isOwn"].Value()))); + + foreach (var d in ownDuplicatedFields) + objects.Remove(d.Name); + foreach (var d in parentDuplicatedFields) + objects.Remove(d.Name); } - return objectValues; + return new JArray(objects.Values); async Task AppendRootHiddenChildren(JObject root, JArray expandedCollection) { @@ -2469,7 +2457,7 @@ async Task GetFieldsValues(List fields, bool isOwn, bool int valtype = retDebuggerCmdReader.ReadByte(); retDebuggerCmdReader.BaseStream.Position = initialPos; var fieldValue = await CreateJObjectForVariableValue(retDebuggerCmdReader, field.Name, isOwn: isOwn, field.TypeId, getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerDisplayAttribute), token); - if (objectValues.Where(attribute => attribute["name"].Value().Equals(fieldValue["name"].Value())).Any()) + if (objects.Where((k, v) => k.Equals(fieldValue["name"].Value())).Any()) continue; if (getCommandType.HasFlag(GetObjectCommandOptions.WithSetter)) { @@ -2667,5 +2655,16 @@ public static void AddRange(this JArray arr, JArray addedArr) foreach (var item in addedArr) arr.Add(item); } + + public static void TryAddRange(this Dictionary dict, JArray addedArr) + { + foreach (var item in addedArr) + { + var key = item["name"]?.Value(); + if (key == null) + continue; + dict.TryAdd(key, item); + } + } } } From ae8ebb4259d8d5e11ec2977fc44380f1b98bac98 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Tue, 21 Dec 2021 12:39:16 +0100 Subject: [PATCH 42/45] Better naming. --- .../BrowserDebugProxy/MonoSDBHelper.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 1b5f9291a98195..210c2eee3b2aa2 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2369,7 +2369,7 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions $"dotnet:object:{objectId}", i == 0, token); - var properties = await GetRegularProperties(props, typeId, token); + var properties = await GetProperties(props, typeId, token); objects.TryAddRange(properties); // ownProperties @@ -2401,10 +2401,10 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions (obj.Value["isOwn"] == null || !obj.Value["isOwn"].Value()))); - foreach (var d in ownDuplicatedFields) - objects.Remove(d.Name); - foreach (var d in parentDuplicatedFields) - objects.Remove(d.Name); + foreach (var duplicate in ownDuplicatedFields) + objects.Remove(duplicate.Name); + foreach (var duplicate in parentDuplicatedFields) + objects.Remove(duplicate.Name); } return new JArray(objects.Values); @@ -2497,7 +2497,7 @@ async Task GetFieldsValues(List fields, bool isOwn, bool if (typeFieldsBrowsableInfo == null || typeFieldsBrowsableInfo.Count == 0) return (fields, new List()); - var regularFields = new List(); + var collapsedFields = new List(); var rootHiddenFields = new List(); foreach (var field in fields) { @@ -2505,7 +2505,7 @@ async Task GetFieldsValues(List fields, bool isOwn, bool { if (!typeProperitesBrowsableInfo.TryGetValue(field.Name, out DebuggerBrowsableState? propState)) { - regularFields.Add(field); + collapsedFields.Add(field); continue; } state = propState; @@ -2521,16 +2521,16 @@ async Task GetFieldsValues(List fields, bool isOwn, bool rootHiddenFields.Add(field); break; case DebuggerBrowsableState.Collapsed: - regularFields.Add(field); + collapsedFields.Add(field); break; default: throw new NotImplementedException($"DebuggerBrowsableState: {state}"); } } - return (regularFields, rootHiddenFields); + return (collapsedFields, rootHiddenFields); } - async Task GetRegularProperties(JArray props, int typeId, CancellationToken token) + async Task GetProperties(JArray props, int typeId, CancellationToken token) { var typeInfo = await GetTypeInfo(typeId, token); var typeProperitesBrowsableInfo = typeInfo?.Info?.DebuggerBrowsableProperties; From 098e3f1281bd5097499f1e43ea87714214c5d7e8 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 3 Jan 2022 09:49:36 +0100 Subject: [PATCH 43/45] Remove not connected to PR file. --- src/native/external/brotli/fuzz/test_fuzzer.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 src/native/external/brotli/fuzz/test_fuzzer.sh diff --git a/src/native/external/brotli/fuzz/test_fuzzer.sh b/src/native/external/brotli/fuzz/test_fuzzer.sh old mode 100644 new mode 100755 From bb4cb8d3b1e843f98589055a0d031f401b90a29b Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 3 Jan 2022 10:36:03 +0100 Subject: [PATCH 44/45] Applied @thaystg suggestions. --- .../debugger/BrowserDebugProxy/DebugStore.cs | 18 ++++++++++++++---- .../BrowserDebugProxy/MonoSDBHelper.cs | 5 +---- .../DebuggerTestSuite/DebuggerTestBase.cs | 1 - 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index a1574bea09321b..a50f486e052f6c 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -477,6 +477,7 @@ public VarInfo[] GetLiveVarsAt(int offset) internal class TypeInfo { + private readonly ILogger logger; internal AssemblyInfo assembly; private TypeDefinition type; private List methods; @@ -486,8 +487,9 @@ internal class TypeInfo public Dictionary DebuggerBrowsableFields = new(); public Dictionary DebuggerBrowsableProperties = new(); - public TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDefinition type) + public TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDefinition type, ILogger logger) { + this.logger = logger; this.assembly = assembly; var metadataReader = assembly.asmMetadataReader; Token = MetadataTokens.GetToken(metadataReader, typeHandle); @@ -514,7 +516,11 @@ public TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDefi var fieldName = metadataReader.GetString(fieldDefinition.Name); AppendToBrowsable(DebuggerBrowsableFields, fieldDefinition.GetCustomAttributes(), fieldName); } - catch { continue; } + catch (Exception ex) + { + logger.LogDebug($"Failed to read browsable attributes of a field. ({ex.Message})"); + continue; + } } foreach (var prop in type.GetProperties()) @@ -525,7 +531,11 @@ public TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDefi var propName = metadataReader.GetString(propDefinition.Name); AppendToBrowsable(DebuggerBrowsableProperties, propDefinition.GetCustomAttributes(), propName); } - catch { continue; } + catch (Exception ex) + { + logger.LogDebug($"Failed to read browsable attributes of a property. ({ex.Message})"); + continue; + } } void AppendToBrowsable(Dictionary dict, CustomAttributeHandleCollection customAttrs, string fieldName) @@ -705,7 +715,7 @@ SourceFile FindSource(DocumentHandle doc, int rowid, string documentName) { var typeDefinition = asmMetadataReader.GetTypeDefinition(type); - var typeInfo = new TypeInfo(this, type, typeDefinition); + var typeInfo = new TypeInfo(this, type, typeDefinition, logger); TypesByName[typeInfo.FullName] = typeInfo; TypesByToken[typeInfo.Token] = typeInfo; if (pdbMetadataReader != null) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 715d2b2309740a..80eaed9daf6948 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2392,10 +2392,7 @@ public async Task GetObjectValues(int objectId, GetObjectCommandOptions parentFields.AddRange(await GetTypeFields(typeIdsIncludingParents[i], token)); var ownDuplicatedFields = ownFields.Where(field => objects.Any(obj => - field.Name.Equals(obj.Key) && - (obj.Value["isOwn"] == null || - obj.Value["isOwn"].Value() || - !obj.Value["isOwn"].Value()))); + field.Name.Equals(obj.Key))); var parentDuplicatedFields = parentFields.Where(field => objects.Any(obj => field.Name.Equals(obj.Key) && diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index 9bbe39a44c7837..1113a3e626084a 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -670,7 +670,6 @@ internal async Task CheckProps(JToken actual, object exp_o, string label, int nu if (exp_i != null) await CheckValue(act_i["value"], exp_i, $"{label}-{i}th value"); } - return; } From 44145e4990357f88e275851e04dbc355612dc867 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 3 Jan 2022 10:39:44 +0100 Subject: [PATCH 45/45] Removed comments. --- src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs | 3 +-- .../debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index 1113a3e626084a..f78f013e8a68e6 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -672,7 +672,6 @@ internal async Task CheckProps(JToken actual, object exp_o, string label, int nu } return; } - // Not an array var exp = exp_o as JObject; if (exp == null) @@ -801,7 +800,7 @@ internal async Task GetObjectOnLocals(JToken locals, string name) /* @fn_args is for use with `Runtime.callFunctionOn` only */ internal async Task GetProperties(string id, JToken fn_args = null, bool? own_properties = null, bool? accessors_only = null, bool expect_ok = true) { - if (UseCallFunctionOnBeforeGetProperties && !id.StartsWith("dotnet:scope:")) //FALSE + if (UseCallFunctionOnBeforeGetProperties && !id.StartsWith("dotnet:scope:")) { var fn_decl = "function () { return this; }"; var cfo_args = JObject.FromObject(new diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index 97f042be385a26..14a786c040b4e3 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -944,7 +944,7 @@ public async Task EvaluateBrowsableCollapsed(string outerClassName, string class [InlineData("EvaluateBrowsableProperties", "TestEvaluatePropertiesRootHidden", "testPropertiesRootHidden", 10)] [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluateFieldsRootHidden", "testFieldsRootHidden", 10)] [InlineData("EvaluateBrowsableStaticProperties", "TestEvaluatePropertiesRootHidden", "testPropertiesRootHidden", 10)] - [InlineData("EvaluateBrowsableCustomProperties", "TestEvaluatePropertiesRootHidden", "testPropertiesRootHidden", 5, true)] //FAILING + [InlineData("EvaluateBrowsableCustomProperties", "TestEvaluatePropertiesRootHidden", "testPropertiesRootHidden", 5, true)] public async Task EvaluateBrowsableRootHidden(string outerClassName, string className, string localVarName, int breakLine, bool isCustomGetter = false) => await CheckInspectLocalsAtBreakpointSite( $"DebuggerTests.{outerClassName}", "Evaluate", breakLine, "Evaluate", $"window.setTimeout(function() {{ invoke_static_method ('[debugger-test] DebuggerTests.{outerClassName}:Evaluate'); 1 }})",