From f0dea3464d471042a59e9c0000a5e65e8c9bc46b Mon Sep 17 00:00:00 2001 From: James Newton-King <james@newtonking.com> Date: Fri, 15 Sep 2023 22:06:15 +0800 Subject: [PATCH] Add .NET debugging attributes --- csharp/src/Google.Protobuf/ByteString.cs | 16 ++++++++++++++++ .../Google.Protobuf/Collections/MapField.cs | 16 ++++++++++++++++ .../Collections/RepeatedField.cs | 17 +++++++++++++++++ .../Reflection/CustomOptions.cs | 19 +++++++++++++++++++ .../Reflection/DescriptorBase.cs | 2 ++ .../Reflection/ExtensionCollection.cs | 16 ++++++++++++++++ .../Reflection/GeneratedClrTypeInfo.cs | 2 ++ .../Reflection/MessageDescriptor.cs | 16 ++++++++++++++++ .../Reflection/TypeRegistry.cs | 16 ++++++++++++++++ csharp/src/Google.Protobuf/UnknownFieldSet.cs | 19 ++++++++++++++++++- 10 files changed, 138 insertions(+), 1 deletion(-) diff --git a/csharp/src/Google.Protobuf/ByteString.cs b/csharp/src/Google.Protobuf/ByteString.cs index e361dd11fe..fc5d925fa7 100644 --- a/csharp/src/Google.Protobuf/ByteString.cs +++ b/csharp/src/Google.Protobuf/ByteString.cs @@ -10,6 +10,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Security; @@ -23,6 +24,8 @@ namespace Google.Protobuf /// Immutable array of bytes. /// </summary> [SecuritySafeCritical] + [DebuggerDisplay("Length = {Length}")] + [DebuggerTypeProxy(typeof(ByteStringDebugView))] public sealed class ByteString : IEnumerable<byte>, IEquatable<ByteString> { private static readonly ByteString empty = new ByteString(new byte[0]); @@ -400,5 +403,18 @@ namespace Google.Protobuf outputStream.Write(array, 0, array.Length); } } + + private sealed class ByteStringDebugView + { + private readonly ByteString data; + + public ByteStringDebugView(ByteString data) + { + this.data = data; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public byte[] Items => data.bytes.ToArray(); + } } } \ No newline at end of file diff --git a/csharp/src/Google.Protobuf/Collections/MapField.cs b/csharp/src/Google.Protobuf/Collections/MapField.cs index 2e18976578..f0be958303 100644 --- a/csharp/src/Google.Protobuf/Collections/MapField.cs +++ b/csharp/src/Google.Protobuf/Collections/MapField.cs @@ -11,6 +11,7 @@ using Google.Protobuf.Compatibility; using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Security; @@ -43,6 +44,8 @@ namespace Google.Protobuf.Collections /// in future versions. /// </para> /// </remarks> + [DebuggerDisplay("Count = {Count}")] + [DebuggerTypeProxy(typeof(MapField<,>.MapFieldDebugView))] public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDictionary, IReadOnlyDictionary<TKey, TValue> { private static readonly EqualityComparer<TValue> ValueEqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<TValue>(); @@ -683,5 +686,18 @@ namespace Google.Protobuf.Collections } } } + + private sealed class MapFieldDebugView + { + private readonly MapField<TKey, TValue> map; + + public MapFieldDebugView(MapField<TKey, TValue> map) + { + this.map = map; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public KeyValuePair<TKey, TValue>[] Items => map.list.ToArray(); + } } } diff --git a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs index 38e70156c9..8bf410aa85 100644 --- a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs +++ b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs @@ -10,7 +10,9 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.IO; +using System.Linq; using System.Security; namespace Google.Protobuf.Collections @@ -24,6 +26,8 @@ namespace Google.Protobuf.Collections /// supported by Protocol Buffers but nor does it guarantee that all operations will work in such cases. /// </remarks> /// <typeparam name="T">The element type of the repeated field.</typeparam> + [DebuggerDisplay("Count = {Count}")] + [DebuggerTypeProxy(typeof(RepeatedField<>.RepeatedFieldDebugView))] public sealed class RepeatedField<T> : IList<T>, IList, IDeepCloneable<RepeatedField<T>>, IEquatable<RepeatedField<T>>, IReadOnlyList<T> { private static readonly EqualityComparer<T> EqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<T>(); @@ -642,5 +646,18 @@ namespace Google.Protobuf.Collections } } #endregion + + private sealed class RepeatedFieldDebugView + { + private readonly RepeatedField<T> list; + + public RepeatedFieldDebugView(RepeatedField<T> list) + { + this.list = list; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Items => list.ToArray(); + } } } diff --git a/csharp/src/Google.Protobuf/Reflection/CustomOptions.cs b/csharp/src/Google.Protobuf/Reflection/CustomOptions.cs index 21ec57a386..43b5a4c5ab 100644 --- a/csharp/src/Google.Protobuf/Reflection/CustomOptions.cs +++ b/csharp/src/Google.Protobuf/Reflection/CustomOptions.cs @@ -8,8 +8,10 @@ #endregion using Google.Protobuf.Collections; +using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; @@ -38,6 +40,8 @@ namespace Google.Protobuf.Reflection /// all the set values are merged together. /// </para> /// </remarks> + [DebuggerDisplay("Count = {DebugCount}")] + [DebuggerTypeProxy(typeof(CustomOptionsDebugView))] public sealed class CustomOptions { private const string UnreferencedCodeMessage = "CustomOptions is incompatible with trimming."; @@ -290,5 +294,20 @@ namespace Google.Protobuf.Reflection value = default; return false; } + + private int DebugCount => values?.Count ?? 0; + + private sealed class CustomOptionsDebugView + { + private readonly CustomOptions customOptions; + + public CustomOptionsDebugView(CustomOptions customOptions) + { + this.customOptions = customOptions; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public KeyValuePair<int, IExtensionValue>[] Items => customOptions.values?.ToArray() ?? new KeyValuePair<int, IExtensionValue>[0]; + } } } diff --git a/csharp/src/Google.Protobuf/Reflection/DescriptorBase.cs b/csharp/src/Google.Protobuf/Reflection/DescriptorBase.cs index 34a73d6abe..e70b23ad1f 100644 --- a/csharp/src/Google.Protobuf/Reflection/DescriptorBase.cs +++ b/csharp/src/Google.Protobuf/Reflection/DescriptorBase.cs @@ -8,12 +8,14 @@ #endregion using System.Collections.Generic; +using System.Diagnostics; namespace Google.Protobuf.Reflection { /// <summary> /// Base class for nearly all descriptors, providing common functionality. /// </summary> + [DebuggerDisplay("Type = {GetType().Name,nq}, FullName = {FullName}")] public abstract class DescriptorBase : IDescriptor { internal DescriptorBase(FileDescriptor file, string fullName, int index) diff --git a/csharp/src/Google.Protobuf/Reflection/ExtensionCollection.cs b/csharp/src/Google.Protobuf/Reflection/ExtensionCollection.cs index 9fcad8942f..5ce2cfe50f 100644 --- a/csharp/src/Google.Protobuf/Reflection/ExtensionCollection.cs +++ b/csharp/src/Google.Protobuf/Reflection/ExtensionCollection.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics; using System.Linq; namespace Google.Protobuf.Reflection @@ -16,6 +17,8 @@ namespace Google.Protobuf.Reflection /// <summary> /// A collection to simplify retrieving the descriptors of extensions in a descriptor for a message /// </summary> + [DebuggerDisplay("Count = {UnorderedExtensions.Count}")] + [DebuggerTypeProxy(typeof(ExtensionCollectionDebugView))] public sealed class ExtensionCollection { private IDictionary<MessageDescriptor, IList<FieldDescriptor>> extensionsByTypeInDeclarationOrder; @@ -98,5 +101,18 @@ namespace Google.Protobuf.Reflection extensionsByTypeInNumberOrder = declarationOrder .ToDictionary(kvp => kvp.Key, kvp => (IList<FieldDescriptor>)new ReadOnlyCollection<FieldDescriptor>(kvp.Value.OrderBy(field => field.FieldNumber).ToArray())); } + + private sealed class ExtensionCollectionDebugView + { + private readonly ExtensionCollection list; + + public ExtensionCollectionDebugView(ExtensionCollection list) + { + this.list = list; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public FieldDescriptor[] Items => list.UnorderedExtensions.ToArray(); + } } } diff --git a/csharp/src/Google.Protobuf/Reflection/GeneratedClrTypeInfo.cs b/csharp/src/Google.Protobuf/Reflection/GeneratedClrTypeInfo.cs index f5ef62c9b8..a4748c53db 100644 --- a/csharp/src/Google.Protobuf/Reflection/GeneratedClrTypeInfo.cs +++ b/csharp/src/Google.Protobuf/Reflection/GeneratedClrTypeInfo.cs @@ -8,6 +8,7 @@ #endregion using System; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace Google.Protobuf.Reflection @@ -17,6 +18,7 @@ namespace Google.Protobuf.Reflection /// These are constructed as required, and are not long-lived. Hand-written code should /// never need to use this type. /// </summary> + [DebuggerDisplay("ClrType = {ClrType}")] public sealed class GeneratedClrTypeInfo { private static readonly string[] EmptyNames = new string[0]; diff --git a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs index dfa63e47cf..37ca0f152e 100644 --- a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; @@ -326,6 +327,8 @@ namespace Google.Protobuf.Reflection /// <summary> /// A collection to simplify retrieving the field accessor for a particular field. /// </summary> + [DebuggerDisplay("Count = {InFieldNumberOrder().Count}")] + [DebuggerTypeProxy(typeof(FieldCollectionDebugView))] public sealed class FieldCollection { private readonly MessageDescriptor messageDescriptor; @@ -398,6 +401,19 @@ namespace Google.Protobuf.Reflection return fieldDescriptor; } } + + private sealed class FieldCollectionDebugView + { + private readonly FieldCollection collection; + + public FieldCollectionDebugView(FieldCollection collection) + { + this.collection = collection; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public FieldDescriptor[] Items => collection.InFieldNumberOrder().ToArray(); + } } } } diff --git a/csharp/src/Google.Protobuf/Reflection/TypeRegistry.cs b/csharp/src/Google.Protobuf/Reflection/TypeRegistry.cs index b1bf30b714..f0ab81cbe7 100644 --- a/csharp/src/Google.Protobuf/Reflection/TypeRegistry.cs +++ b/csharp/src/Google.Protobuf/Reflection/TypeRegistry.cs @@ -8,6 +8,7 @@ #endregion using System.Collections.Generic; +using System.Diagnostics; using System.Linq; namespace Google.Protobuf.Reflection @@ -15,6 +16,8 @@ namespace Google.Protobuf.Reflection /// <summary> /// An immutable registry of types which can be looked up by their full name. /// </summary> + [DebuggerDisplay("Count = {fullNameToMessageMap.Count}")] + [DebuggerTypeProxy(typeof(TypeRegistryDebugView))] public sealed class TypeRegistry { /// <summary> @@ -156,5 +159,18 @@ namespace Google.Protobuf.Reflection return new TypeRegistry(types); } } + + private sealed class TypeRegistryDebugView + { + private readonly TypeRegistry list; + + public TypeRegistryDebugView(TypeRegistry list) + { + this.list = list; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public KeyValuePair<string, MessageDescriptor>[] Items => list.fullNameToMessageMap.ToArray(); + } } } diff --git a/csharp/src/Google.Protobuf/UnknownFieldSet.cs b/csharp/src/Google.Protobuf/UnknownFieldSet.cs index 4fab6156dc..d0963d257d 100644 --- a/csharp/src/Google.Protobuf/UnknownFieldSet.cs +++ b/csharp/src/Google.Protobuf/UnknownFieldSet.cs @@ -9,6 +9,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using System.Security; namespace Google.Protobuf @@ -22,6 +24,8 @@ namespace Google.Protobuf /// /// Most users will never need to use this class directly. /// </summary> + [DebuggerDisplay("Count = {fields.Count}")] + [DebuggerTypeProxy(typeof(UnknownFieldSetDebugView))] public sealed partial class UnknownFieldSet { private readonly IDictionary<int, UnknownField> fields = new Dictionary<int, UnknownField>(); @@ -93,7 +97,7 @@ namespace Google.Protobuf } UnknownFieldSet otherSet = other as UnknownFieldSet; IDictionary<int, UnknownField> otherFields = otherSet.fields; - if (fields.Count != otherFields.Count) + if (fields.Count != otherFields.Count) { return false; } @@ -360,6 +364,19 @@ namespace Google.Protobuf unknownFields.MergeFrom(other); return unknownFields; } + + private sealed class UnknownFieldSetDebugView + { + private readonly UnknownFieldSet set; + + public UnknownFieldSetDebugView(UnknownFieldSet set) + { + this.set = set; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public KeyValuePair<int, UnknownField>[] Items => set.fields.ToArray(); + } } }