/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using dnSpy.Contracts.Debugger; using dnSpy.Contracts.Debugger.DotNet.Evaluation; using dnSpy.Contracts.Debugger.DotNet.Text; using dnSpy.Contracts.Debugger.Evaluation; using dnSpy.Contracts.Debugger.Text; using dnSpy.Debugger.DotNet.Metadata; using dnSpy.Roslyn.Properties; namespace dnSpy.Roslyn.Debugger.ValueNodes { abstract class DbgDotNetValueNodeProviderFactory { [Flags] enum TypeStateFlags { None = 0, IsNullable = 1, IsTupleType = 2, IsDynamicViewType = 4, } sealed class TypeState { public readonly DmdType Type; public readonly DmdType? EnumerableType; public readonly TypeStateFlags Flags; public readonly string TypeExpression; public readonly bool HasNoChildren; public readonly MemberValueNodeInfoCollection InstanceMembers; public readonly MemberValueNodeInfoCollection StaticMembers; // Only if it's a tuple public readonly TupleField[] TupleFields; public bool IsNullable => (Flags & TypeStateFlags.IsNullable) != 0; public bool IsTupleType => (Flags & TypeStateFlags.IsTupleType) != 0; public bool IsDynamicViewType => (Flags & TypeStateFlags.IsDynamicViewType) != 0; public DbgValueNodeEvaluationOptions CachedEvalOptions; public MemberValueNodeInfoCollection CachedInstanceMembers; public MemberValueNodeInfoCollection CachedStaticMembers; public DbgValueNodeEvaluationOptions CachedRawViewEvalOptions; public MemberValueNodeInfoCollection CachedRawViewInstanceMembers; public MemberValueNodeInfoCollection CachedRawViewStaticMembers; public TypeState(DmdType type, string typeExpression) { Type = type; Flags = TypeStateFlags.None; TypeExpression = typeExpression; HasNoChildren = true; InstanceMembers = MemberValueNodeInfoCollection.Empty; StaticMembers = MemberValueNodeInfoCollection.Empty; TupleFields = Array.Empty(); } public TypeState(DmdType type, string typeExpression, MemberValueNodeInfoCollection instanceMembers, MemberValueNodeInfoCollection staticMembers, TupleField[] tupleFields) { Type = type; EnumerableType = GetEnumerableType(type); Flags = GetFlags(type, tupleFields); TypeExpression = typeExpression; HasNoChildren = false; InstanceMembers = instanceMembers; StaticMembers = staticMembers; TupleFields = tupleFields; } static DmdType? GetEnumerableType(DmdType type) { if (type.IsArray || type == type.AppDomain.System_String) return null; bool foundEnumerableType = false; var enumerableType = type.AppDomain.System_Collections_IEnumerable; var enumerableOfTType = type.AppDomain.System_Collections_Generic_IEnumerable_T; switch (GetEnumerableTypeKind(type, enumerableType, enumerableOfTType)) { case EnumerableTypeKind.Enumerable: foundEnumerableType = true; break; case EnumerableTypeKind.EnumerableOfT: return type; } foreach (var iface in type.GetInterfaces()) { switch (GetEnumerableTypeKind(iface, enumerableType, enumerableOfTType)) { case EnumerableTypeKind.Enumerable: foundEnumerableType = true; break; case EnumerableTypeKind.EnumerableOfT: return iface; } } return foundEnumerableType ? enumerableType : null; } enum EnumerableTypeKind { None, Enumerable, EnumerableOfT, } static EnumerableTypeKind GetEnumerableTypeKind(DmdType type, DmdType enumerableType, DmdType enumerableOfTType) { if (type == enumerableType) return EnumerableTypeKind.Enumerable; if (type.IsConstructedGenericType && type.GetGenericArguments().Count == 1) { if (type.GetGenericTypeDefinition() == enumerableOfTType) return EnumerableTypeKind.EnumerableOfT; } return EnumerableTypeKind.None; } static TypeStateFlags GetFlags(DmdType type, TupleField[] tupleFields) { var res = TypeStateFlags.None; if (type.IsNullable) res |= TypeStateFlags.IsNullable; if (tupleFields.Length != 0) res |= TypeStateFlags.IsTupleType; if (CheckIsDynamicViewType(type)) res |= TypeStateFlags.IsDynamicViewType; return res; } static bool CheckIsDynamicViewType(DmdType type) { // Windows Runtime types aren't supported if (type.IsWindowsRuntime) return false; // Microsoft.CSharp.RuntimeBinder.DynamicMetaObjectProviderDebugView supports COM objects if (type.CanCastTo(type.AppDomain.GetWellKnownType(DmdWellKnownType.System___ComObject, isOptional: true))) return true; // Microsoft.CSharp.RuntimeBinder.DynamicMetaObjectProviderDebugView supports IDynamicMetaObjectProvider. // This type is defined in Microsoft.CSharp which isn't always loaded. That's the reason we don't // use a DmdWellKnownType, since when searching for one, the code will check every loaded assembly // until the type is found, forcing all lazy-loaded metadata to be loaded. Most of the time it would // fail, and thus load all metadata. It's a problem when debugging programs with 100+ loaded assemblies. foreach (var iface in type.GetInterfaces()) { if (iface.DeclaringType is null && iface.MetadataNamespace == "System.Dynamic" && iface.MetadataName == "IDynamicMetaObjectProvider") return true; } return false; } } readonly LanguageValueNodeFactory valueNodeFactory; readonly StringComparer stringComparer; protected DbgDotNetValueNodeProviderFactory(LanguageValueNodeFactory valueNodeFactory, bool isCaseSensitive) { this.valueNodeFactory = valueNodeFactory ?? throw new ArgumentNullException(nameof(valueNodeFactory)); stringComparer = isCaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; } /// /// Returns true if is a primitive type that doesn't show any members, /// eg. integers, booleans, floating point numbers, strings /// /// Type to check /// protected abstract bool HasNoChildren(DmdType type); protected abstract DbgDotNetText InstanceMembersName { get; } protected abstract DbgDotNetText StaticMembersName { get; } protected abstract void FormatTypeName(IDbgTextWriter output, DmdType type); protected abstract void FormatFieldName(IDbgTextWriter output, DmdFieldInfo field); protected abstract void FormatPropertyName(IDbgTextWriter output, DmdPropertyInfo property); public abstract void FormatArrayName(IDbgTextWriter output, int index); public abstract void FormatArrayName(IDbgTextWriter output, int[] indexes); public abstract string GetNewObjectExpression(DmdConstructorInfo ctor, string argumentExpression, DmdType expectedType); public abstract string GetCallExpression(DmdMethodBase method, string instanceExpression); public abstract string GetDereferenceExpression(string instanceExpression); public abstract ref readonly DbgDotNetText GetDereferencedName(); internal void FormatTypeName2(IDbgTextWriter output, DmdType type) => FormatTypeName(output, type); [Flags] enum CreationOptions { None = 0, NoNullable = 1, NoProxy = 2, NoExtraRawView = 4, } public DbgDotNetValueNodeProviderResult Create(DbgEvaluationInfo evalInfo, bool addParens, DmdType slotType, DbgDotNetValueNodeInfo nodeInfo, DbgValueNodeEvaluationOptions options) { var providers = new List(2); Create(evalInfo, providers, addParens, slotType, nodeInfo, options, CreationOptions.None); return new DbgDotNetValueNodeProviderResult(DbgDotNetValueNodeProvider.Create(providers)); } public DbgDotNetValueNodeProviderResult CreateDynamicView(DbgEvaluationInfo evalInfo, bool addParens, DmdType slotType, DbgDotNetValueNodeInfo nodeInfo, DbgValueNodeEvaluationOptions options) { var state = GetTypeState(nodeInfo); var provider = TryCreateDynamicView(state, nodeInfo.Expression, nodeInfo.Value, slotType, options); if (provider is not null) return new DbgDotNetValueNodeProviderResult(provider); return new DbgDotNetValueNodeProviderResult(dnSpy_Roslyn_Resources.DynamicView_MustBeDynamicOrComType); } public DbgDotNetValueNodeProviderResult CreateResultsView(DbgEvaluationInfo evalInfo, bool addParens, DmdType slotType, DbgDotNetValueNodeInfo nodeInfo, DbgValueNodeEvaluationOptions options) { var state = GetTypeState(nodeInfo); var provider = TryCreateResultsView(state, nodeInfo.Expression, nodeInfo.Value, slotType, options); if (provider is not null) return new DbgDotNetValueNodeProviderResult(provider); return new DbgDotNetValueNodeProviderResult(dnSpy_Roslyn_Resources.ResultsView_MustBeEnumerableType); } TypeState GetTypeState(DbgDotNetValueNodeInfo nodeInfo) { var type = nodeInfo.Value.Type; if (type.IsByRef) type = type.GetElementType()!; return GetOrCreateTypeState(type); } void Create(DbgEvaluationInfo evalInfo, List providers, bool addParens, DmdType slotType, DbgDotNetValueNodeInfo nodeInfo, DbgValueNodeEvaluationOptions options, CreationOptions creationOptions) => CreateCore(evalInfo, providers, addParens, slotType, nodeInfo, GetTypeState(nodeInfo), options, creationOptions); TypeState GetOrCreateTypeState(DmdType type) { var state = StateWithKey.TryGet(type, this); if (state is not null) return state; return CreateTypeState(type); TypeState CreateTypeState(DmdType type2) { var state2 = CreateTypeStateCore(type2); return StateWithKey.GetOrCreate(type2, this, () => state2); } } sealed class MemberValueNodeInfoEqualityComparer : IComparer { public static readonly MemberValueNodeInfoEqualityComparer Instance = new MemberValueNodeInfoEqualityComparer(); MemberValueNodeInfoEqualityComparer() { } public int Compare([AllowNull] MemberValueNodeInfo x, [AllowNull] MemberValueNodeInfo y) { int c = GetOrder(x.Member.MemberType) - GetOrder(y.Member.MemberType); if (c != 0) return c; c = StringComparer.OrdinalIgnoreCase.Compare(x.Member.Name, y.Member.Name); if (c != 0) return c; c = y.InheritanceLevel.CompareTo(x.InheritanceLevel); if (c != 0) return c; return x.Member.MetadataToken - y.Member.MetadataToken; } static int GetOrder(DmdMemberTypes memberType) { if (memberType == DmdMemberTypes.Property) return 0; if (memberType == DmdMemberTypes.Field) return 1; throw new InvalidOperationException(); } } string GetTypeExpression(DmdType type) { var output = new DbgStringBuilderTextWriter(); FormatTypeName(output, type); return output.ToString(); } TupleField[]? TryCreateTupleFields(DmdType type) { var tupleArity = Formatters.TypeFormatterUtils.GetTupleArity(type); if (tupleArity <= 0) return null; var tupleFields = new TupleField[tupleArity]; foreach (var info in Formatters.TupleTypeUtils.GetTupleFields(type, tupleArity)) { if (info.tupleIndex < 0) return null; var defaultName = GetDefaultTupleName(info.tupleIndex); tupleFields[info.tupleIndex] = new TupleField(defaultName, info.fields!.ToArray()); } return tupleFields; } static string GetDefaultTupleName(int tupleIndex) => "Item" + (tupleIndex + 1).ToString(); TypeState CreateTypeStateCore(DmdType type) { var typeExpression = GetTypeExpression(type); if (HasNoChildren(type) || type.IsFunctionPointer) return new TypeState(type, typeExpression); MemberValueNodeInfoCollection instanceMembers, staticMembers; TupleField[] tupleFields; Debug.Assert(!type.IsByRef); if (type.TypeSignatureKind == DmdTypeSignatureKind.Type || type.TypeSignatureKind == DmdTypeSignatureKind.GenericInstance) { tupleFields = TryCreateTupleFields(type) ?? Array.Empty(); var instanceMembersList = new List(); var staticMembersList = new List(); bool instanceHasHideRoot = false; bool staticHasHideRoot = false; bool instanceHasShowNever = false; bool staticHasShowNever = false; byte inheritanceLevel; DmdType currentType; inheritanceLevel = 0; currentType = type; foreach (var field in type.Fields) { var declType = field.DeclaringType; while (declType != currentType) { Debug2.Assert(currentType.BaseType is not null); currentType = currentType.BaseType; if (inheritanceLevel != byte.MaxValue) inheritanceLevel++; } var nodeInfo = new MemberValueNodeInfo(field, inheritanceLevel); if (field.IsStatic) { staticHasHideRoot |= nodeInfo.HasDebuggerBrowsableState_RootHidden; staticHasShowNever |= nodeInfo.HasDebuggerBrowsableState_Never; staticMembersList.Add(nodeInfo); } else { instanceHasHideRoot |= nodeInfo.HasDebuggerBrowsableState_RootHidden; instanceHasShowNever |= nodeInfo.HasDebuggerBrowsableState_Never; instanceMembersList.Add(nodeInfo); } } inheritanceLevel = 0; currentType = type; foreach (var property in type.Properties) { if (property.GetMethodSignature().GetParameterTypes().Count != 0) continue; var declType = property.DeclaringType; while (declType != currentType) { Debug2.Assert(currentType.BaseType is not null); currentType = currentType.BaseType; if (inheritanceLevel != byte.MaxValue) inheritanceLevel++; } var getter = property.GetGetMethod(DmdGetAccessorOptions.All); if (getter is null || getter.GetMethodSignature().GetParameterTypes().Count != 0) continue; var nodeInfo = new MemberValueNodeInfo(property, inheritanceLevel); if (getter.IsStatic) { staticHasHideRoot |= nodeInfo.HasDebuggerBrowsableState_RootHidden; staticHasShowNever |= nodeInfo.HasDebuggerBrowsableState_Never; staticMembersList.Add(nodeInfo); } else { instanceHasHideRoot |= nodeInfo.HasDebuggerBrowsableState_RootHidden; instanceHasShowNever |= nodeInfo.HasDebuggerBrowsableState_Never; instanceMembersList.Add(nodeInfo); } } var instanceMembersArray = InitializeOverloadedMembers(instanceMembersList.ToArray()); var staticMembersArray = InitializeOverloadedMembers(staticMembersList.ToArray()); instanceMembers = instanceMembersList.Count == 0 ? MemberValueNodeInfoCollection.Empty : new MemberValueNodeInfoCollection(instanceMembersArray, instanceHasHideRoot, instanceHasShowNever); staticMembers = staticMembersList.Count == 0 ? MemberValueNodeInfoCollection.Empty : new MemberValueNodeInfoCollection(staticMembersArray, staticHasHideRoot, staticHasShowNever); Array.Sort(instanceMembers.Members, MemberValueNodeInfoEqualityComparer.Instance); Array.Sort(staticMembers.Members, MemberValueNodeInfoEqualityComparer.Instance); var output = ObjectCache.AllocDotNetTextOutput(); UpdateNames(instanceMembers.Members, output); UpdateNames(staticMembers.Members, output); ObjectCache.Free(ref output); } else { staticMembers = instanceMembers = MemberValueNodeInfoCollection.Empty; tupleFields = Array.Empty(); } return new TypeState(type, typeExpression, instanceMembers, staticMembers, tupleFields); } MemberValueNodeInfo[] InitializeOverloadedMembers(MemberValueNodeInfo[] memberInfos) { var dict = new Dictionary(stringComparer); for (int i = 0; i < memberInfos.Length; i++) { ref var info = ref memberInfos[i]; if (dict.TryGetValue(info.Member.Name, out var value)) { List list; if (value is int) { list = new List(2); list.Add((int)value); dict[info.Member.Name] = list; } else list = (List)value; list.Add(i); } else dict[info.Member.Name] = i; } var memberInfosTmp = memberInfos; foreach (var kv in dict) { var list = kv.Value as List; if (list is null) continue; list.Sort((a, b) => { ref var ai = ref memberInfosTmp[a]; ref var bi = ref memberInfosTmp[b]; return ai.InheritanceLevel - bi.InheritanceLevel; }); var firstType = memberInfos[list[0]].Member.DeclaringType; for (int i = 1; i < list.Count; i++) { ref var info = ref memberInfos[list[i]]; if (info.Member.DeclaringType != firstType) info.SetNeedCastAndNeedTypeName(); } } return memberInfos; } void UpdateNames(MemberValueNodeInfo[] infos, DbgDotNetTextOutput output) { if (infos.Length == 0) return; for (int i = 0; i < infos.Length; i++) { ref var info = ref infos[i]; FormatName(output, info.Member); if (info.NeedTypeName) { output.Write(DbgTextColor.Text, " "); output.Write(DbgTextColor.Punctuation, "("); FormatTypeName(output, info.Member.DeclaringType!); output.Write(DbgTextColor.Punctuation, ")"); } info.Name = output.CreateAndReset(); } } void FormatName(DbgDotNetTextOutput output, DmdMemberInfo member) { if (member.MemberType == DmdMemberTypes.Field) FormatFieldName(output, (DmdFieldInfo)member); else { Debug.Assert(member.MemberType == DmdMemberTypes.Property); FormatPropertyName(output, (DmdPropertyInfo)member); } } bool TryCreateNullable(DbgEvaluationInfo evalInfo, List providers, bool addParens, DmdType slotType, DbgDotNetValueNodeInfo nodeInfo, TypeState state, DbgValueNodeEvaluationOptions evalOptions, CreationOptions creationOptions) { Debug.Assert((creationOptions & CreationOptions.NoNullable) == 0); if (!state.IsNullable) return false; var fields = Formatters.NullableTypeUtils.TryGetNullableFields(state.Type); Debug2.Assert(fields.hasValueField is not null); if (fields.hasValueField is null) return false; var runtime = evalInfo.Runtime.GetDotNetRuntime(); bool disposeFieldValue = true; var fieldValue = runtime.LoadField(evalInfo, nodeInfo.Value, fields.hasValueField); try { if (fieldValue.HasError || fieldValue.ValueIsException) return false; var rawValue = fieldValue.Value!.GetRawValue(); if (rawValue.ValueType != DbgSimpleValueType.Boolean) return false; if (!(bool)rawValue.RawValue!) { nodeInfo.SetDisplayValue(new SyntheticNullValue(fields.valueField!.FieldType)); return true; } fieldValue.Value?.Dispose(); fieldValue = default; fieldValue = runtime.LoadField(evalInfo, nodeInfo.Value, fields.valueField!); if (fieldValue.HasError || fieldValue.ValueIsException) return false; nodeInfo.SetDisplayValue(fieldValue.Value!); Create(evalInfo, providers, addParens, slotType, nodeInfo, evalOptions, creationOptions | CreationOptions.NoNullable); disposeFieldValue = false; return true; } finally { if (disposeFieldValue) fieldValue.Value?.Dispose(); } } void CreateCore(DbgEvaluationInfo evalInfo, List providers, bool addParens, DmdType slotType, DbgDotNetValueNodeInfo nodeInfo, TypeState state, DbgValueNodeEvaluationOptions evalOptions, CreationOptions creationOptions) { evalInfo.CancellationToken.ThrowIfCancellationRequested(); if (state.HasNoChildren) return; if ((creationOptions & CreationOptions.NoNullable) == 0 && state.IsNullable) { if (TryCreateNullable(evalInfo, providers, addParens, slotType, nodeInfo, state, evalOptions, creationOptions)) return; } if (state.Type.IsArray && !nodeInfo.Value.IsNull) { providers.Add(new ArrayValueNodeProvider(this, addParens, slotType, nodeInfo)); return; } bool forceRawView = (evalOptions & DbgValueNodeEvaluationOptions.RawView) != 0; bool funcEval = (evalOptions & DbgValueNodeEvaluationOptions.NoFuncEval) == 0; if (state.IsTupleType && !forceRawView) { providers.Add(new TupleValueNodeProvider(addParens, slotType, nodeInfo, state.TupleFields)); AddProvidersOneChildNode(providers, state, nodeInfo.Expression, addParens, slotType, nodeInfo.Value, evalOptions, isRawView: true); return; } if (!forceRawView && (creationOptions & CreationOptions.NoProxy) == 0 && funcEval && !nodeInfo.Value.IsNull) { var proxyCtor = DebuggerTypeProxyFinder.GetDebuggerTypeProxyConstructor(state.Type); if (proxyCtor is not null) { var runtime = evalInfo.Runtime.GetDotNetRuntime(); var proxyTypeResult = runtime.CreateInstance(evalInfo, proxyCtor, new[] { nodeInfo.Value }, DbgDotNetInvokeOptions.None); // Use the result even if the constructor threw an exception if (!proxyTypeResult.HasError) { var value = nodeInfo.Value; var origExpression = nodeInfo.Expression; nodeInfo.Expression = GetNewObjectExpression(proxyCtor, nodeInfo.Expression, slotType); nodeInfo.SetProxyValue(proxyTypeResult.Value!); Create(evalInfo, providers, false, slotType, nodeInfo, evalOptions | DbgValueNodeEvaluationOptions.PublicMembers, creationOptions | CreationOptions.NoProxy | CreationOptions.NoExtraRawView); AddProvidersOneChildNode(providers, state, origExpression, addParens, slotType, value, evalOptions, isRawView: true); return; } } } if (!forceRawView && (creationOptions & CreationOptions.NoExtraRawView) == 0) { GetMemberCollections(state, evalOptions, out var instanceMembersInfos, out var staticMembersInfos); if (instanceMembersInfos.HasHideRoot || instanceMembersInfos.HasShowNever) { AddProviders(providers, state, nodeInfo.Expression, addParens, slotType, nodeInfo.Value, evalOptions, isRawView: false); AddProvidersOneChildNode(providers, state, nodeInfo.Expression, addParens, slotType, nodeInfo.Value, evalOptions, isRawView: true); return; } } AddProviders(providers, state, nodeInfo.Expression, addParens, slotType, nodeInfo.Value, evalOptions, forceRawView); } DbgDotNetValueNodeProvider? TryCreateDynamicView(TypeState state, string expression, DbgDotNetValue value, DmdType expectedType, DbgValueNodeEvaluationOptions evalOptions) { if (state.IsDynamicViewType && !value.IsNull) return new DynamicViewMembersValueNodeProvider(this, valueNodeFactory, value, expectedType, expression, state.Type.AppDomain, evalOptions); return null; } DbgDotNetValueNodeProvider? TryCreateResultsView(TypeState state, string expression, DbgDotNetValue value, DmdType expectedType, DbgValueNodeEvaluationOptions evalOptions) { if (state.EnumerableType is not null && !value.IsNull) return new ResultsViewMembersValueNodeProvider(this, valueNodeFactory, state.EnumerableType, value, expectedType, expression, evalOptions); return null; } void AddProvidersOneChildNode(List providers, TypeState state, string expression, bool addParens, DmdType slotType, DbgDotNetValue value, DbgValueNodeEvaluationOptions evalOptions, bool isRawView) { var tmpProviders = new List(2); AddProviders(tmpProviders, state, expression, addParens, slotType, value, evalOptions, isRawView); if (tmpProviders.Count > 0) providers.Add(DbgDotNetValueNodeProvider.Create(tmpProviders)!); } internal void GetMemberCollections(DmdType type, DbgValueNodeEvaluationOptions evalOptions, out MemberValueNodeInfoCollection instanceMembersInfos, out MemberValueNodeInfoCollection staticMembersInfos) { var state = GetOrCreateTypeState(type); GetMemberCollections(state, evalOptions, out instanceMembersInfos, out staticMembersInfos); } void GetMemberCollections(TypeState state, DbgValueNodeEvaluationOptions evalOptions, out MemberValueNodeInfoCollection instanceMembersInfos, out MemberValueNodeInfoCollection staticMembersInfos) { lock (state) { if ((evalOptions & DbgValueNodeEvaluationOptions.RawView) == 0) { if (state.CachedEvalOptions != evalOptions || state.CachedInstanceMembers.Members is null) { state.CachedEvalOptions = evalOptions; state.CachedInstanceMembers = Filter(state.InstanceMembers, evalOptions); state.CachedStaticMembers = Filter(state.StaticMembers, evalOptions); } instanceMembersInfos = state.CachedInstanceMembers; staticMembersInfos = state.CachedStaticMembers; } else { if (state.CachedRawViewEvalOptions != evalOptions || state.CachedRawViewInstanceMembers.Members is null) { state.CachedRawViewEvalOptions = evalOptions; state.CachedRawViewInstanceMembers = Filter(state.InstanceMembers, evalOptions); state.CachedRawViewStaticMembers = Filter(state.StaticMembers, evalOptions); } instanceMembersInfos = state.CachedRawViewInstanceMembers; staticMembersInfos = state.CachedRawViewStaticMembers; } } } void AddProviders(List providers, TypeState state, string expression, bool addParens, DmdType slotType, DbgDotNetValue value, DbgValueNodeEvaluationOptions evalOptions, bool isRawView) { GetMemberCollections(state, evalOptions | (isRawView ? DbgValueNodeEvaluationOptions.RawView : 0), out var instanceMembersInfos, out var staticMembersInfos); var membersEvalOptions = evalOptions; if (isRawView) membersEvalOptions |= DbgValueNodeEvaluationOptions.RawView; if (value.IsNull) instanceMembersInfos = MemberValueNodeInfoCollection.Empty; if (PointerValueNodeProvider.IsSupported(value)) providers.Add(new PointerValueNodeProvider(this, expression, value)); else { providers.Add(new InstanceMembersValueNodeProvider(valueNodeFactory, isRawView ? rawViewName : InstanceMembersName, expression, addParens, slotType, value, instanceMembersInfos, membersEvalOptions, isRawView ? PredefinedDbgValueNodeImageNames.RawView : PredefinedDbgValueNodeImageNames.InstanceMembers)); } if (staticMembersInfos.Members.Length != 0) providers.Add(new StaticMembersValueNodeProvider(this, valueNodeFactory, StaticMembersName, state.TypeExpression, staticMembersInfos, membersEvalOptions)); var provider = TryCreateResultsView(state, expression, value, slotType, evalOptions); if (provider is not null) providers.Add(provider); provider = TryCreateDynamicView(state, expression, value, slotType, evalOptions); if (provider is not null) providers.Add(provider); } static readonly DbgDotNetText rawViewName = new DbgDotNetText(new DbgDotNetTextPart(DbgTextColor.Text, dnSpy_Roslyn_Resources.DebuggerVarsWindow_RawView)); static MemberValueNodeInfoCollection Filter(MemberValueNodeInfoCollection infos, DbgValueNodeEvaluationOptions evalOptions) { bool isRawView = (evalOptions & DbgValueNodeEvaluationOptions.RawView) != 0; bool hideCompilerGeneratedMembers = (evalOptions & DbgValueNodeEvaluationOptions.HideCompilerGeneratedMembers) != 0; bool respectHideMemberAttributes = (evalOptions & DbgValueNodeEvaluationOptions.RespectHideMemberAttributes) != 0; bool publicMembers = (evalOptions & DbgValueNodeEvaluationOptions.PublicMembers) != 0; bool hideDeprecatedError = (evalOptions & DbgValueNodeEvaluationOptions.HideDeprecatedError) != 0; if (!hideCompilerGeneratedMembers && (isRawView || !respectHideMemberAttributes) && !publicMembers && !hideDeprecatedError) return infos; return Filter(infos, isRawView, hideCompilerGeneratedMembers, respectHideMemberAttributes, publicMembers, hideDeprecatedError); } static MemberValueNodeInfoCollection Filter(MemberValueNodeInfoCollection infos, bool isRawView, bool hideCompilerGeneratedMembers, bool respectHideMemberAttributes, bool publicMembers, bool hideDeprecatedError) { bool hasHideRoot = false; bool hasShowNever = false; var members = infos.Members.Where(a => { Debug.Assert(a.Member.MemberType == DmdMemberTypes.Field || a.Member.MemberType == DmdMemberTypes.Property); if (publicMembers && !a.IsPublic) return false; if (!isRawView && respectHideMemberAttributes && a.HasDebuggerBrowsableState_Never) return false; if (hideCompilerGeneratedMembers && a.IsCompilerGenerated) return false; if (hideDeprecatedError && a.DeprecatedError) return false; hasHideRoot |= a.HasDebuggerBrowsableState_RootHidden; hasShowNever |= a.HasDebuggerBrowsableState_Never; return true; }).ToArray(); return new MemberValueNodeInfoCollection(members, hasHideRoot, hasShowNever); } } readonly struct DbgDotNetValueNodeProviderResult { public string? ErrorMessage { get; } public DbgDotNetValueNodeProvider? Provider { get; } public DbgDotNetValueNodeProviderResult(DbgDotNetValueNodeProvider? provider) { ErrorMessage = null; Provider = provider; } public DbgDotNetValueNodeProviderResult(string errorMessage) { ErrorMessage = errorMessage ?? throw new ArgumentNullException(nameof(errorMessage)); Provider = null; } } }