|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using Microsoft.VisualStudio.Debugger.Evaluation;
using Microsoft.VisualStudio.Debugger.Evaluation.ClrCompilation;
using System.Collections.ObjectModel;
using System.Diagnostics;
namespace Microsoft.CodeAnalysis.ExpressionEvaluator
{
/// <summary>
/// Debugger type proxy expansion.
/// </summary>
/// <remarks>
/// May include <see cref="System.Collections.IEnumerable"/> and
/// <see cref="System.Collections.Generic.IEnumerable{T}"/> as special cases.
/// (The proxy is not declared by an attribute, but is known to debugger.)
/// </remarks>
internal sealed class DebuggerTypeProxyExpansion : Expansion
{
internal static Expansion CreateExpansion(
ResultProvider resultProvider,
DkmInspectionContext inspectionContext,
string name,
TypeAndCustomInfo typeDeclaringMemberAndInfoOpt,
TypeAndCustomInfo declaredTypeAndInfo,
DkmClrValue value,
bool childShouldParenthesize,
string fullName,
string childFullNamePrefix,
ReadOnlyCollection<string> formatSpecifiers,
DkmEvaluationResultFlags flags,
string editableValue)
{
Debug.Assert((inspectionContext.EvaluationFlags & DkmEvaluationFlags.NoExpansion) == 0);
// Note: The native EE uses the proxy type, even for
// null instances, so statics on the proxy type are
// displayed. That case is not supported currently.
if (!value.IsNull)
{
var proxyType = value.Type.GetProxyType();
if (proxyType != null)
{
if ((inspectionContext.EvaluationFlags & DkmEvaluationFlags.ShowValueRaw) != 0)
{
var rawView = CreateRawView(resultProvider, inspectionContext, declaredTypeAndInfo, value);
Debug.Assert(rawView != null);
return rawView;
}
DkmClrValue proxyValue;
try
{
proxyValue = value.InstantiateProxyType(inspectionContext, proxyType);
}
catch
{
proxyValue = null;
}
if (proxyValue != null)
{
return new DebuggerTypeProxyExpansion(
inspectionContext,
proxyValue,
name,
typeDeclaringMemberAndInfoOpt,
declaredTypeAndInfo,
value,
childShouldParenthesize,
fullName,
childFullNamePrefix,
formatSpecifiers,
flags,
editableValue,
resultProvider);
}
}
}
return null;
}
private readonly EvalResult _proxyItem;
private readonly string _name;
private readonly TypeAndCustomInfo _typeDeclaringMemberAndInfoOpt;
private readonly TypeAndCustomInfo _declaredTypeAndInfo;
private readonly DkmClrValue _value;
private readonly bool _childShouldParenthesize;
private readonly string _fullName;
private readonly string _childFullNamePrefix;
private readonly ReadOnlyCollection<string> _formatSpecifiers;
private readonly DkmEvaluationResultFlags _flags;
private readonly string _editableValue;
private DebuggerTypeProxyExpansion(
DkmInspectionContext inspectionContext,
DkmClrValue proxyValue,
string name,
TypeAndCustomInfo typeDeclaringMemberAndInfoOpt,
TypeAndCustomInfo declaredTypeAndInfo,
DkmClrValue value,
bool childShouldParenthesize,
string fullName,
string childFullNamePrefix,
ReadOnlyCollection<string> formatSpecifiers,
DkmEvaluationResultFlags flags,
string editableValue,
ResultProvider resultProvider)
{
Debug.Assert(proxyValue != null);
var proxyType = proxyValue.Type;
var proxyTypeAndInfo = new TypeAndCustomInfo(proxyType);
var proxyMembers = MemberExpansion.CreateExpansion(
inspectionContext,
proxyTypeAndInfo,
proxyValue,
ExpansionFlags.IncludeBaseMembers,
TypeHelpers.IsPublic,
resultProvider,
isProxyType: true,
supportsFavorites: false);
if (proxyMembers != null)
{
string proxyMemberFullNamePrefix = null;
if (childFullNamePrefix != null)
{
proxyMemberFullNamePrefix = resultProvider.FullNameProvider.GetClrObjectCreationExpression(
inspectionContext,
proxyTypeAndInfo.ClrType,
proxyTypeAndInfo.Info,
[childFullNamePrefix]);
}
_proxyItem = new EvalResult(
ExpansionKind.Default,
name: string.Empty,
typeDeclaringMemberAndInfo: default(TypeAndCustomInfo),
declaredTypeAndInfo: proxyTypeAndInfo,
useDebuggerDisplay: false,
value: proxyValue,
displayValue: null,
expansion: proxyMembers,
childShouldParenthesize: false,
fullName: null,
childFullNamePrefixOpt: proxyMemberFullNamePrefix,
formatSpecifiers: Formatter.NoFormatSpecifiers,
category: default(DkmEvaluationResultCategory),
flags: default(DkmEvaluationResultFlags),
editableValue: null,
inspectionContext: inspectionContext);
}
_name = name;
_typeDeclaringMemberAndInfoOpt = typeDeclaringMemberAndInfoOpt;
_declaredTypeAndInfo = declaredTypeAndInfo;
_value = value;
_childShouldParenthesize = childShouldParenthesize;
_fullName = fullName;
_childFullNamePrefix = childFullNamePrefix;
_formatSpecifiers = formatSpecifiers;
_flags = flags;
_editableValue = editableValue;
}
internal override void GetRows(
ResultProvider resultProvider,
ArrayBuilder<EvalResult> rows,
DkmInspectionContext inspectionContext,
EvalResultDataItem parent,
DkmClrValue value,
int startIndex,
int count,
bool visitAll,
ref int index)
{
_proxyItem?.Expansion.GetRows(resultProvider, rows, inspectionContext, _proxyItem.ToDataItem(), _proxyItem.Value, startIndex, count, visitAll, ref index);
if (InRange(startIndex, count, index))
{
rows.Add(this.CreateRawViewRow(resultProvider, inspectionContext));
}
index++;
}
private EvalResult CreateRawViewRow(
ResultProvider resultProvider,
DkmInspectionContext inspectionContext)
{
return new EvalResult(
ExpansionKind.RawView,
_name,
_typeDeclaringMemberAndInfoOpt,
_declaredTypeAndInfo,
useDebuggerDisplay: false,
value: _value,
displayValue: null,
expansion: CreateRawView(resultProvider, inspectionContext, _declaredTypeAndInfo, _value),
childShouldParenthesize: _childShouldParenthesize,
fullName: _fullName,
childFullNamePrefixOpt: _childFullNamePrefix,
formatSpecifiers: Formatter.AddFormatSpecifier(_formatSpecifiers, "raw"),
category: DkmEvaluationResultCategory.Data,
flags: _flags | DkmEvaluationResultFlags.ReadOnly,
editableValue: _editableValue,
inspectionContext: inspectionContext);
}
private static Expansion CreateRawView(
ResultProvider resultProvider,
DkmInspectionContext inspectionContext,
TypeAndCustomInfo declaredTypeAndInfo,
DkmClrValue value)
{
return resultProvider.GetTypeExpansion(inspectionContext, declaredTypeAndInfo, value, ExpansionFlags.IncludeBaseMembers, supportsFavorites: false);
}
}
}
|