fixed warnings
This commit is contained in:
parent
5f593123de
commit
ffddfa21ec
@ -1,71 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio 15
|
|
||||||
VisualStudioVersion = 15.0.27019.1
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A2FCCAAC-BE90-4F7E-B95F-A72D46DDD6B3}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{59CA6310-4AA5-4093-95D4-472B94DC0CD4}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ben.Demystifier", "src\Ben.Demystifier\Ben.Demystifier.csproj", "{5410A056-89AB-4912-BD1E-A63616AD91D0}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ben.Demystifier.Test", "test\Ben.Demystifier.Test\Ben.Demystifier.Test.csproj", "{B9E150B0-AEEB-4D98-8BE1-92C1296699A2}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{455921D3-DD54-4355-85CF-F4009DF2AB70}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StackTrace", "sample\StackTrace\StackTrace.csproj", "{E161FC12-53C2-47CD-A5FC-3684B86723A9}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5937ACDF-0059-488E-9604-D84689C72933}"
|
|
||||||
ProjectSection(SolutionItems) = preProject
|
|
||||||
appveyor.yml = appveyor.yml
|
|
||||||
build.ps1 = build.ps1
|
|
||||||
Directory.Build.props = Directory.Build.props
|
|
||||||
README.md = README.md
|
|
||||||
version.json = version.json
|
|
||||||
EndProjectSection
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ben.Demystifier.Benchmarks", "test\Ben.Demystifier.Benchmarks\Ben.Demystifier.Benchmarks.csproj", "{EF5557DF-C48E-4999-846C-D99A92E86373}"
|
|
||||||
EndProject
|
|
||||||
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharpStackTrace", "sample\FSharpStackTrace\FSharpStackTrace.fsproj", "{D6B779D2-A678-47CC-A2F9-A312292EA7A2}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{5410A056-89AB-4912-BD1E-A63616AD91D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{5410A056-89AB-4912-BD1E-A63616AD91D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{5410A056-89AB-4912-BD1E-A63616AD91D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{5410A056-89AB-4912-BD1E-A63616AD91D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{B9E150B0-AEEB-4D98-8BE1-92C1296699A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{B9E150B0-AEEB-4D98-8BE1-92C1296699A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{B9E150B0-AEEB-4D98-8BE1-92C1296699A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{B9E150B0-AEEB-4D98-8BE1-92C1296699A2}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{E161FC12-53C2-47CD-A5FC-3684B86723A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{E161FC12-53C2-47CD-A5FC-3684B86723A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{E161FC12-53C2-47CD-A5FC-3684B86723A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{E161FC12-53C2-47CD-A5FC-3684B86723A9}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{EF5557DF-C48E-4999-846C-D99A92E86373}.Debug|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{EF5557DF-C48E-4999-846C-D99A92E86373}.Debug|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{EF5557DF-C48E-4999-846C-D99A92E86373}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{EF5557DF-C48E-4999-846C-D99A92E86373}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{D6B779D2-A678-47CC-A2F9-A312292EA7A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{D6B779D2-A678-47CC-A2F9-A312292EA7A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{D6B779D2-A678-47CC-A2F9-A312292EA7A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{D6B779D2-A678-47CC-A2F9-A312292EA7A2}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(NestedProjects) = preSolution
|
|
||||||
{5410A056-89AB-4912-BD1E-A63616AD91D0} = {A2FCCAAC-BE90-4F7E-B95F-A72D46DDD6B3}
|
|
||||||
{B9E150B0-AEEB-4D98-8BE1-92C1296699A2} = {59CA6310-4AA5-4093-95D4-472B94DC0CD4}
|
|
||||||
{E161FC12-53C2-47CD-A5FC-3684B86723A9} = {455921D3-DD54-4355-85CF-F4009DF2AB70}
|
|
||||||
{EF5557DF-C48E-4999-846C-D99A92E86373} = {59CA6310-4AA5-4093-95D4-472B94DC0CD4}
|
|
||||||
{D6B779D2-A678-47CC-A2F9-A312292EA7A2} = {455921D3-DD54-4355-85CF-F4009DF2AB70}
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {841B7D5F-E810-4F94-A529-002C7E075216}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@ -5,12 +5,12 @@
|
|||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
|
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
|
||||||
<TargetFrameworks>netstandard2.1;netstandard2.0;net45;net6.0</TargetFrameworks>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="System.Reflection.Metadata" Version="5.0.0" Condition="'$(TargetFramework)' != 'net6.0'"/>
|
<PackageReference Include="System.Reflection.Metadata" Version="5.0.0" Condition="'$(TargetFramework)' != 'net6.0'"/>
|
||||||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" Condition="'$(TargetFramework)' != 'netstandard2.1'" />
|
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" Condition="'$(TargetFramework)' != 'netstandard2.1'"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -1,17 +1,26 @@
|
|||||||
// Copyright (c) Ben A Adams. All rights reserved.
|
// Copyright (c) Ben A Adams. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Reflection;
|
namespace Ben.Demystifier;
|
||||||
|
|
||||||
namespace Ben.Demystifier
|
public class EnhancedStackFrame : StackFrame
|
||||||
{
|
{
|
||||||
public class EnhancedStackFrame : StackFrame
|
private readonly int _colNumber;
|
||||||
{
|
|
||||||
private readonly string? _fileName;
|
private readonly string? _fileName;
|
||||||
private readonly int _lineNumber;
|
private readonly int _lineNumber;
|
||||||
private readonly int _colNumber;
|
|
||||||
|
|
||||||
public StackFrame StackFrame { get; }
|
internal EnhancedStackFrame(StackFrame stackFrameBase, ResolvedMethod methodInfo, string? fileName, int lineNumber,
|
||||||
|
int colNumber)
|
||||||
|
: base(fileName, lineNumber, colNumber)
|
||||||
|
{
|
||||||
|
StackFrameBase = stackFrameBase;
|
||||||
|
MethodInfo = methodInfo;
|
||||||
|
_fileName = fileName;
|
||||||
|
_lineNumber = lineNumber;
|
||||||
|
_colNumber = colNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
private StackFrame StackFrameBase { get; }
|
||||||
|
|
||||||
public bool IsRecursive
|
public bool IsRecursive
|
||||||
{
|
{
|
||||||
@ -21,24 +30,11 @@ namespace Ben.Demystifier
|
|||||||
|
|
||||||
public ResolvedMethod MethodInfo { get; }
|
public ResolvedMethod MethodInfo { get; }
|
||||||
|
|
||||||
internal EnhancedStackFrame(StackFrame stackFrame, ResolvedMethod methodInfo, string? fileName, int lineNumber, int colNumber)
|
internal bool IsEquivalent(ResolvedMethod methodInfo, string? fileName, int lineNumber, int colNumber) =>
|
||||||
: base(fileName, lineNumber, colNumber)
|
_lineNumber == lineNumber &&
|
||||||
{
|
|
||||||
StackFrame = stackFrame;
|
|
||||||
MethodInfo = methodInfo;
|
|
||||||
|
|
||||||
_fileName = fileName;
|
|
||||||
_lineNumber = lineNumber;
|
|
||||||
_colNumber = colNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool IsEquivalent(ResolvedMethod methodInfo, string? fileName, int lineNumber, int colNumber)
|
|
||||||
{
|
|
||||||
return _lineNumber == lineNumber &&
|
|
||||||
_colNumber == colNumber &&
|
_colNumber == colNumber &&
|
||||||
_fileName == fileName &&
|
_fileName == fileName &&
|
||||||
MethodInfo.IsSequentialEquivalent(methodInfo);
|
MethodInfo.IsSequentialEquivalent(methodInfo);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the column number in the file that contains the code that is executing.
|
/// Gets the column number in the file that contains the code that is executing.
|
||||||
@ -68,13 +64,11 @@ namespace Ben.Demystifier
|
|||||||
/// code. The generation of this debugging information is controlled by the System.Diagnostics.DebuggableAttribute.
|
/// code. The generation of this debugging information is controlled by the System.Diagnostics.DebuggableAttribute.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The offset from the start of the MSIL code for the method that is executing.</returns>
|
/// <returns>The offset from the start of the MSIL code for the method that is executing.</returns>
|
||||||
public override int GetILOffset() => StackFrame.GetILOffset();
|
public override int GetILOffset() => StackFrameBase.GetILOffset();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Gets the method in which the frame is executing.</summary>
|
||||||
/// Gets the method in which the frame is executing.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The method in which the frame is executing.</returns>
|
/// <returns>The method in which the frame is executing.</returns>
|
||||||
public override MethodBase? GetMethod() => StackFrame.GetMethod();
|
public override MethodBase? GetMethod() => StackFrameBase.GetMethod();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the offset from the start of the native just-in-time (JIT)-compiled code
|
/// Gets the offset from the start of the native just-in-time (JIT)-compiled code
|
||||||
@ -82,12 +76,9 @@ namespace Ben.Demystifier
|
|||||||
/// is controlled by the System.Diagnostics.DebuggableAttribute class.
|
/// is controlled by the System.Diagnostics.DebuggableAttribute class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The offset from the start of the JIT-compiled code for the method that is being executed.</returns>
|
/// <returns>The offset from the start of the JIT-compiled code for the method that is being executed.</returns>
|
||||||
public override int GetNativeOffset() => StackFrame.GetNativeOffset();
|
public override int GetNativeOffset() => StackFrameBase.GetNativeOffset();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Builds a readable representation of the stack trace.</summary>
|
||||||
/// Builds a readable representation of the stack trace.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A readable representation of the stack trace.</returns>
|
/// <returns>A readable representation of the stack trace.</returns>
|
||||||
public override string ToString() => MethodInfo.ToString();
|
public override string ToString() => MethodInfo.ToString();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -3,28 +3,27 @@
|
|||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
global using System.Collections;
|
|
||||||
global using System.Collections.Generic;
|
|
||||||
using Ben.Demystifier.Enumerable;
|
|
||||||
using Ben.Demystifier.Internal;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.ExceptionServices;
|
using System.Runtime.ExceptionServices;
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Ben.Demystifier.Enumerable;
|
||||||
|
using Ben.Demystifier.Internal;
|
||||||
|
|
||||||
namespace Ben.Demystifier
|
namespace Ben.Demystifier
|
||||||
{
|
{
|
||||||
public partial class EnhancedStackTrace
|
public partial class EnhancedStackTrace
|
||||||
{
|
{
|
||||||
private static readonly Type? StackTraceHiddenAttributeType = Type.GetType("System.Diagnostics.StackTraceHiddenAttribute", false);
|
private static readonly Type? StackTraceHiddenAttributeType =
|
||||||
private static readonly Type? AsyncIteratorStateMachineAttributeType = Type.GetType("System.Runtime.CompilerServices.AsyncIteratorStateMachineAttribute", false);
|
Type.GetType("System.Diagnostics.StackTraceHiddenAttribute", false);
|
||||||
|
|
||||||
|
private static readonly Type? AsyncIteratorStateMachineAttributeType =
|
||||||
|
Type.GetType("System.Runtime.CompilerServices.AsyncIteratorStateMachineAttribute", false);
|
||||||
|
|
||||||
static EnhancedStackTrace()
|
static EnhancedStackTrace()
|
||||||
{
|
{
|
||||||
if (AsyncIteratorStateMachineAttributeType != null) return;
|
if (AsyncIteratorStateMachineAttributeType is not null) return;
|
||||||
|
|
||||||
Assembly mba;
|
Assembly mba;
|
||||||
try
|
try
|
||||||
@ -36,32 +35,15 @@ namespace Ben.Demystifier
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncIteratorStateMachineAttributeType = mba.GetType("System.Runtime.CompilerServices.AsyncIteratorStateMachineAttribute", false);
|
AsyncIteratorStateMachineAttributeType =
|
||||||
}
|
mba.GetType("System.Runtime.CompilerServices.AsyncIteratorStateMachineAttribute", false);
|
||||||
|
|
||||||
private static List<EnhancedStackFrame> GetFrames(Exception exception)
|
|
||||||
{
|
|
||||||
if (exception == null)
|
|
||||||
{
|
|
||||||
return new List<EnhancedStackFrame>();
|
|
||||||
}
|
|
||||||
|
|
||||||
var needFileInfo = true;
|
|
||||||
var stackTrace = new StackTrace(exception, needFileInfo);
|
|
||||||
|
|
||||||
return GetFrames(stackTrace);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<EnhancedStackFrame> GetFrames(StackTrace stackTrace)
|
public static List<EnhancedStackFrame> GetFrames(StackTrace stackTrace)
|
||||||
{
|
{
|
||||||
var frames = new List<EnhancedStackFrame>();
|
var enhancedFrames = new List<EnhancedStackFrame>();
|
||||||
var stackFrames = stackTrace.GetFrames();
|
var stackFrames = stackTrace.GetFrames();
|
||||||
|
|
||||||
if (stackFrames == null)
|
|
||||||
{
|
|
||||||
return frames;
|
|
||||||
}
|
|
||||||
|
|
||||||
EnhancedStackFrame? lastFrame = null;
|
EnhancedStackFrame? lastFrame = null;
|
||||||
PortablePdbReader? portablePdbReader = null;
|
PortablePdbReader? portablePdbReader = null;
|
||||||
try
|
try
|
||||||
@ -69,27 +51,22 @@ namespace Ben.Demystifier
|
|||||||
for (var i = 0; i < stackFrames.Length; i++)
|
for (var i = 0; i < stackFrames.Length; i++)
|
||||||
{
|
{
|
||||||
var frame = stackFrames[i];
|
var frame = stackFrames[i];
|
||||||
if (frame is null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var method = frame.GetMethod();
|
var method = frame.GetMethod();
|
||||||
|
|
||||||
// Always show last stackFrame
|
// Always show last stackFrame
|
||||||
if (method != null && !ShowInStackTrace(method) && i < stackFrames.Length - 1)
|
if (method is not null && !ShowInStackTrace(method) && i < stackFrames.Length - 1)
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
var fileName = frame.GetFileName();
|
var fileName = frame.GetFileName();
|
||||||
var row = frame.GetFileLineNumber();
|
var row = frame.GetFileLineNumber();
|
||||||
var column = frame.GetFileColumnNumber();
|
var column = frame.GetFileColumnNumber();
|
||||||
var ilOffset = frame.GetILOffset();
|
var ilOffset = frame.GetILOffset();
|
||||||
if (method != null && string.IsNullOrEmpty(fileName) && ilOffset >= 0)
|
if (method is not null && string.IsNullOrEmpty(fileName) && ilOffset >= 0)
|
||||||
{
|
{
|
||||||
// .NET Framework and older versions of mono don't support portable PDBs
|
// .NET Framework and older versions of mono don't support portable PDBs
|
||||||
// so we read it manually to get file name and line information
|
// so we read it manually to get file name and line information
|
||||||
(portablePdbReader ??= new PortablePdbReader()).PopulateStackFrame(frame, method, frame.GetILOffset(), out fileName, out row, out column);
|
(portablePdbReader ??= new PortablePdbReader()).PopulateStackFrame(frame, method,
|
||||||
|
frame.GetILOffset(), out fileName, out row, out column);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (method is null)
|
if (method is null)
|
||||||
@ -106,7 +83,7 @@ namespace Ben.Demystifier
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var stackFrame = new EnhancedStackFrame(frame, resolvedMethod, fileName, row, column);
|
var stackFrame = new EnhancedStackFrame(frame, resolvedMethod, fileName, row, column);
|
||||||
frames.Add(stackFrame);
|
enhancedFrames.Add(stackFrame);
|
||||||
lastFrame = stackFrame;
|
lastFrame = stackFrame;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,7 +93,7 @@ namespace Ben.Demystifier
|
|||||||
portablePdbReader?.Dispose();
|
portablePdbReader?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
return frames;
|
return enhancedFrames;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResolvedMethod GetMethodDisplayString(MethodBase originMethod)
|
public static ResolvedMethod GetMethodDisplayString(MethodBase originMethod)
|
||||||
@ -161,7 +138,8 @@ namespace Ben.Demystifier
|
|||||||
methodDisplayInfo.Name = methodName;
|
methodDisplayInfo.Name = methodName;
|
||||||
if (method.Name.IndexOf("<") >= 0)
|
if (method.Name.IndexOf("<") >= 0)
|
||||||
{
|
{
|
||||||
if (TryResolveGeneratedName(ref method, out type, out methodName, out subMethodName, out var kind, out var ordinal))
|
if (TryResolveGeneratedName(ref method, out type, out methodName, out subMethodName, out var kind,
|
||||||
|
out var ordinal))
|
||||||
{
|
{
|
||||||
methodName = method.Name;
|
methodName = method.Name;
|
||||||
methodDisplayInfo.MethodBase = method;
|
methodDisplayInfo.MethodBase = method;
|
||||||
@ -175,7 +153,7 @@ namespace Ben.Demystifier
|
|||||||
|
|
||||||
methodDisplayInfo.IsLambda = (kind == GeneratedNameKind.LambdaMethod);
|
methodDisplayInfo.IsLambda = (kind == GeneratedNameKind.LambdaMethod);
|
||||||
|
|
||||||
if (methodDisplayInfo.IsLambda && type != null)
|
if (methodDisplayInfo.IsLambda && type is not null)
|
||||||
{
|
{
|
||||||
if (methodName == ".cctor")
|
if (methodName == ".cctor")
|
||||||
{
|
{
|
||||||
@ -185,7 +163,8 @@ namespace Ben.Demystifier
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var fields = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
var fields =
|
||||||
|
type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
foreach (var field in fields)
|
foreach (var field in fields)
|
||||||
{
|
{
|
||||||
var value = field.GetValue(field);
|
var value = field.GetValue(field);
|
||||||
@ -212,33 +191,29 @@ namespace Ben.Demystifier
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ResolveStateMachineMethod may have set declaringType to null
|
// ResolveStateMachineMethod may have set declaringType to null
|
||||||
if (type != null)
|
if (type is not null)
|
||||||
{
|
{
|
||||||
methodDisplayInfo.DeclaringType = type;
|
methodDisplayInfo.DeclaringType = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (method is MethodInfo mi)
|
if (method is MethodInfo mi)
|
||||||
{
|
{
|
||||||
var returnParameter = mi.ReturnParameter;
|
if (mi.ReturnParameter is not null)
|
||||||
if (returnParameter != null)
|
|
||||||
{
|
|
||||||
methodDisplayInfo.ReturnParameter = GetParameter(mi.ReturnParameter);
|
methodDisplayInfo.ReturnParameter = GetParameter(mi.ReturnParameter);
|
||||||
}
|
else if (mi.ReturnType is not null)
|
||||||
else if (mi.ReturnType != null)
|
|
||||||
{
|
|
||||||
methodDisplayInfo.ReturnParameter = new ResolvedParameter(mi.ReturnType)
|
methodDisplayInfo.ReturnParameter = new ResolvedParameter(mi.ReturnType)
|
||||||
{
|
{
|
||||||
Prefix = "",
|
Prefix = "",
|
||||||
Name = "",
|
Name = ""
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (method.IsGenericMethod)
|
if (method.IsGenericMethod)
|
||||||
{
|
{
|
||||||
var genericArguments = method.GetGenericArguments();
|
var genericArguments = method.GetGenericArguments();
|
||||||
var genericArgumentsString = string.Join(", ", genericArguments
|
var genericArgumentsString = string.Join(", ", genericArguments
|
||||||
.Select(arg => TypeNameHelper.GetTypeDisplayName(arg, fullName: false, includeGenericParameterNames: true)));
|
.Select(arg =>
|
||||||
|
TypeNameHelper.GetTypeDisplayName(arg, fullName: false, includeGenericParameterNames: true)));
|
||||||
methodDisplayInfo.GenericArguments += "<" + genericArgumentsString + ">";
|
methodDisplayInfo.GenericArguments += "<" + genericArgumentsString + ">";
|
||||||
methodDisplayInfo.ResolvedGenericArguments = genericArguments;
|
methodDisplayInfo.ResolvedGenericArguments = genericArguments;
|
||||||
}
|
}
|
||||||
@ -260,7 +235,7 @@ namespace Ben.Demystifier
|
|||||||
{
|
{
|
||||||
methodDisplayInfo.SubMethodBase = null;
|
methodDisplayInfo.SubMethodBase = null;
|
||||||
}
|
}
|
||||||
else if (methodDisplayInfo.SubMethodBase != null)
|
else if (methodDisplayInfo.SubMethodBase is not null)
|
||||||
{
|
{
|
||||||
parameters = methodDisplayInfo.SubMethodBase.GetParameters();
|
parameters = methodDisplayInfo.SubMethodBase.GetParameters();
|
||||||
if (parameters.Length > 0)
|
if (parameters.Length > 0)
|
||||||
@ -295,7 +270,8 @@ namespace Ben.Demystifier
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryResolveGeneratedName(ref MethodBase method, out Type? type, out string methodName, out string? subMethodName, out GeneratedNameKind kind, out int? ordinal)
|
private static bool TryResolveGeneratedName(ref MethodBase method, out Type? type, out string methodName,
|
||||||
|
out string? subMethodName, out GeneratedNameKind kind, out int? ordinal)
|
||||||
{
|
{
|
||||||
kind = GeneratedNameKind.None;
|
kind = GeneratedNameKind.None;
|
||||||
type = method.DeclaringType;
|
type = method.DeclaringType;
|
||||||
@ -328,6 +304,7 @@ namespace Ben.Demystifier
|
|||||||
subMethodName = generatedName.Substring(localNameStart, localNameEnd - localNameStart);
|
subMethodName = generatedName.Substring(localNameStart, localNameEnd - localNameStart);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GeneratedNameKind.LambdaMethod:
|
case GeneratedNameKind.LambdaMethod:
|
||||||
@ -336,7 +313,7 @@ namespace Ben.Demystifier
|
|||||||
}
|
}
|
||||||
|
|
||||||
var dt = method.DeclaringType;
|
var dt = method.DeclaringType;
|
||||||
if (dt == null)
|
if (dt is null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -345,30 +322,42 @@ namespace Ben.Demystifier
|
|||||||
|
|
||||||
var matchName = methodName;
|
var matchName = methodName;
|
||||||
|
|
||||||
var candidateMethods = dt.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(m => m.Name == matchName);
|
var candidateMethods =
|
||||||
if (TryResolveSourceMethod(candidateMethods, kind, matchHint, ref method, ref type, out ordinal)) return true;
|
dt.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static |
|
||||||
|
BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(m => m.Name == matchName);
|
||||||
|
if (TryResolveSourceMethod(candidateMethods, kind, matchHint, ref method, ref type, out ordinal))
|
||||||
|
return true;
|
||||||
|
|
||||||
var candidateConstructors = dt.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(m => m.Name == matchName);
|
var candidateConstructors =
|
||||||
if (TryResolveSourceMethod(candidateConstructors, kind, matchHint, ref method, ref type, out ordinal)) return true;
|
dt.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static |
|
||||||
|
BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(m => m.Name == matchName);
|
||||||
|
if (TryResolveSourceMethod(candidateConstructors, kind, matchHint, ref method, ref type, out ordinal))
|
||||||
|
return true;
|
||||||
|
|
||||||
const int MaxResolveDepth = 10;
|
for (var i = 0; i < 10; i++)
|
||||||
for (var i = 0; i < MaxResolveDepth; i++)
|
|
||||||
{
|
{
|
||||||
dt = dt.DeclaringType;
|
dt = dt.DeclaringType;
|
||||||
if (dt == null)
|
if (dt is null)
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
candidateMethods = dt.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(m => m.Name == matchName);
|
candidateMethods =
|
||||||
if (TryResolveSourceMethod(candidateMethods, kind, matchHint, ref method, ref type, out ordinal)) return true;
|
dt.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static |
|
||||||
|
BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(m => m.Name == matchName);
|
||||||
|
if (TryResolveSourceMethod(candidateMethods, kind, matchHint, ref method, ref type, out ordinal))
|
||||||
|
return true;
|
||||||
|
|
||||||
candidateConstructors = dt.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(m => m.Name == matchName);
|
candidateConstructors =
|
||||||
if (TryResolveSourceMethod(candidateConstructors, kind, matchHint, ref method, ref type, out ordinal)) return true;
|
dt.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static |
|
||||||
|
BindingFlags.Instance | BindingFlags.DeclaredOnly)
|
||||||
|
.Where(m => m.Name == matchName);
|
||||||
|
if (TryResolveSourceMethod(candidateConstructors, kind, matchHint, ref method, ref type, out ordinal))
|
||||||
|
return true;
|
||||||
|
|
||||||
if (methodName == ".cctor")
|
if (methodName == ".cctor")
|
||||||
{
|
{
|
||||||
candidateConstructors = dt.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly).Where(m => m.Name == matchName);
|
candidateConstructors =
|
||||||
|
dt.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static |
|
||||||
|
BindingFlags.DeclaredOnly).Where(m => m.Name == matchName);
|
||||||
foreach (var cctor in candidateConstructors)
|
foreach (var cctor in candidateConstructors)
|
||||||
{
|
{
|
||||||
method = cctor;
|
method = cctor;
|
||||||
@ -381,7 +370,8 @@ namespace Ben.Demystifier
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryResolveSourceMethod(IEnumerable<MethodBase> candidateMethods, GeneratedNameKind kind, string? matchHint, ref MethodBase method, ref Type? type, out int? ordinal)
|
private static bool TryResolveSourceMethod(IEnumerable<MethodBase> candidateMethods, GeneratedNameKind kind,
|
||||||
|
string? matchHint, ref MethodBase method, ref Type? type, out int? ordinal)
|
||||||
{
|
{
|
||||||
ordinal = null;
|
ordinal = null;
|
||||||
foreach (var candidateMethod in candidateMethods)
|
foreach (var candidateMethod in candidateMethods)
|
||||||
@ -390,6 +380,7 @@ namespace Ben.Demystifier
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kind == GeneratedNameKind.LambdaMethod)
|
if (kind == GeneratedNameKind.LambdaMethod)
|
||||||
{
|
{
|
||||||
foreach (var v in EnumerableIList.Create(methodBody.LocalVariables))
|
foreach (var v in EnumerableIList.Create(methodBody.LocalVariables))
|
||||||
@ -397,8 +388,8 @@ namespace Ben.Demystifier
|
|||||||
if (v.LocalType == type)
|
if (v.LocalType == type)
|
||||||
{
|
{
|
||||||
GetOrdinal(method, ref ordinal);
|
GetOrdinal(method, ref ordinal);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
method = candidateMethod;
|
method = candidateMethod;
|
||||||
type = method.DeclaringType;
|
type = method.DeclaringType;
|
||||||
return true;
|
return true;
|
||||||
@ -412,12 +403,13 @@ namespace Ben.Demystifier
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var reader = new ILReader(rawIl);
|
var reader = new ILReader(rawIl);
|
||||||
while (reader.Read(candidateMethod))
|
while (reader.Read(candidateMethod))
|
||||||
{
|
{
|
||||||
if (reader.Operand is MethodBase mb)
|
if (reader.Operand is MethodBase mb)
|
||||||
{
|
{
|
||||||
if (method == mb || matchHint != null && method.Name.Contains(matchHint))
|
if (method == mb || matchHint is not null && method.Name.Contains(matchHint))
|
||||||
{
|
{
|
||||||
if (kind == GeneratedNameKind.LambdaMethod)
|
if (kind == GeneratedNameKind.LambdaMethod)
|
||||||
{
|
{
|
||||||
@ -460,10 +452,12 @@ namespace Ben.Demystifier
|
|||||||
|
|
||||||
ordinal = foundOrdinal;
|
ordinal = foundOrdinal;
|
||||||
|
|
||||||
var methods = method.DeclaringType?.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
var methods = method.DeclaringType?.GetMethods(BindingFlags.Public | BindingFlags.NonPublic |
|
||||||
|
BindingFlags.Static | BindingFlags.Instance |
|
||||||
|
BindingFlags.DeclaredOnly);
|
||||||
|
|
||||||
var count = 0;
|
var count = 0;
|
||||||
if (methods != null)
|
if (methods is not null)
|
||||||
{
|
{
|
||||||
var startName = method.Name.Substring(0, lamdaStart);
|
var startName = method.Name.Substring(0, lamdaStart);
|
||||||
foreach (var m in methods)
|
foreach (var m in methods)
|
||||||
@ -501,6 +495,7 @@ namespace Ben.Demystifier
|
|||||||
|
|
||||||
return methodName.Substring(start, end - start);
|
return methodName.Substring(start, end - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,13 +512,8 @@ namespace Ben.Demystifier
|
|||||||
{
|
{
|
||||||
openBracketOffset = -1;
|
openBracketOffset = -1;
|
||||||
if (name.StartsWith("CS$<", StringComparison.Ordinal))
|
if (name.StartsWith("CS$<", StringComparison.Ordinal))
|
||||||
{
|
|
||||||
openBracketOffset = 3;
|
openBracketOffset = 3;
|
||||||
}
|
else if (name.StartsWith("<", StringComparison.Ordinal)) openBracketOffset = 0;
|
||||||
else if (name.StartsWith("<", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
openBracketOffset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (openBracketOffset >= 0)
|
if (openBracketOffset >= 0)
|
||||||
{
|
{
|
||||||
@ -549,50 +539,32 @@ namespace Ben.Demystifier
|
|||||||
private static int IndexOfBalancedParenthesis(string str, int openingOffset, char closing)
|
private static int IndexOfBalancedParenthesis(string str, int openingOffset, char closing)
|
||||||
{
|
{
|
||||||
var opening = str[openingOffset];
|
var opening = str[openingOffset];
|
||||||
|
|
||||||
var depth = 1;
|
var depth = 1;
|
||||||
for (var i = openingOffset + 1; i < str.Length; i++)
|
for (var i = openingOffset + 1; i < str.Length; i++)
|
||||||
{
|
{
|
||||||
var c = str[i];
|
var c = str[i];
|
||||||
if (c == opening)
|
if (c == opening)
|
||||||
{
|
|
||||||
depth++;
|
depth++;
|
||||||
}
|
|
||||||
else if (c == closing)
|
else if (c == closing)
|
||||||
{
|
{
|
||||||
depth--;
|
depth--;
|
||||||
if (depth == 0)
|
if (depth == 0)
|
||||||
{
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetPrefix(ParameterInfo parameter)
|
private static string GetPrefix(ParameterInfo parameter)
|
||||||
{
|
{
|
||||||
if (Attribute.IsDefined(parameter, typeof(ParamArrayAttribute), false))
|
if (Attribute.IsDefined(parameter, typeof(ParamArrayAttribute), false))
|
||||||
{
|
|
||||||
return "params";
|
return "params";
|
||||||
}
|
|
||||||
|
|
||||||
if (parameter.IsOut)
|
if (parameter.IsOut)
|
||||||
{
|
|
||||||
return "out";
|
return "out";
|
||||||
}
|
|
||||||
|
|
||||||
if (parameter.IsIn)
|
if (parameter.IsIn)
|
||||||
{
|
|
||||||
return "in";
|
return "in";
|
||||||
}
|
|
||||||
|
|
||||||
if (parameter.ParameterType.IsByRef)
|
if (parameter.ParameterType.IsByRef)
|
||||||
{
|
|
||||||
return "ref";
|
return "ref";
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -605,20 +577,16 @@ namespace Ben.Demystifier
|
|||||||
{
|
{
|
||||||
var customAttribs = parameter.GetCustomAttributes(inherit: false);
|
var customAttribs = parameter.GetCustomAttributes(inherit: false);
|
||||||
|
|
||||||
var tupleNameAttribute = customAttribs.OfType<Attribute>().FirstOrDefault(a => a.IsTupleElementNameAttribute());
|
var tupleNameAttribute = customAttribs.OfType<Attribute>()
|
||||||
|
.FirstOrDefault(a => a.IsTupleElementNameAttribute());
|
||||||
|
|
||||||
var tupleNames = tupleNameAttribute?.GetTransformerNames();
|
var tupleNames = tupleNameAttribute?.GetTransformerNames();
|
||||||
|
|
||||||
if (tupleNames?.Count > 0)
|
if (tupleNames?.Count > 0)
|
||||||
{
|
|
||||||
return GetValueTupleParameter(tupleNames, prefix, parameter.Name, parameterType);
|
return GetValueTupleParameter(tupleNames, prefix, parameter.Name, parameterType);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (parameterType.IsByRef && parameterType.GetElementType() is {} elementType)
|
if (parameterType.IsByRef && parameterType.GetElementType() is { } elementType) parameterType = elementType;
|
||||||
{
|
|
||||||
parameterType = elementType;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ResolvedParameter(parameterType)
|
return new ResolvedParameter(parameterType)
|
||||||
{
|
{
|
||||||
@ -628,48 +596,16 @@ namespace Ben.Demystifier
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResolvedParameter GetValueTupleParameter(IList<string> tupleNames, string prefix, string? name, Type parameterType)
|
private static ResolvedParameter GetValueTupleParameter(IList<string> tupleNames, string prefix, string? name,
|
||||||
|
Type parameterType)
|
||||||
{
|
{
|
||||||
return new ValueTupleResolvedParameter(parameterType, tupleNames)
|
return new ValueTupleResolvedParameter(parameterType, tupleNames)
|
||||||
{
|
{
|
||||||
Prefix = prefix,
|
Prefix = prefix,
|
||||||
Name = name,
|
Name = name
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetValueTupleParameterName(IList<string> tupleNames, Type parameterType)
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
sb.Append("(");
|
|
||||||
var args = parameterType.GetGenericArguments();
|
|
||||||
for (var i = 0; i < args.Length; i++)
|
|
||||||
{
|
|
||||||
if (i > 0)
|
|
||||||
{
|
|
||||||
sb.Append(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append(TypeNameHelper.GetTypeDisplayName(args[i], fullName: false, includeGenericParameterNames: true));
|
|
||||||
|
|
||||||
if (i >= tupleNames.Count)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var argName = tupleNames[i];
|
|
||||||
if (argName == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append(" ");
|
|
||||||
sb.Append(argName);
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append(")");
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool ShowInStackTrace(MethodBase method)
|
private static bool ShowInStackTrace(MethodBase method)
|
||||||
{
|
{
|
||||||
// Since .NET 5:
|
// Since .NET 5:
|
||||||
@ -684,7 +620,7 @@ namespace Ben.Demystifier
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Since .NET Core 2:
|
// Since .NET Core 2:
|
||||||
if (StackTraceHiddenAttributeType != null)
|
if (StackTraceHiddenAttributeType is not null)
|
||||||
{
|
{
|
||||||
// Don't show any methods marked with the StackTraceHiddenAttribute
|
// Don't show any methods marked with the StackTraceHiddenAttribute
|
||||||
// https://github.com/dotnet/coreclr/pull/14652
|
// https://github.com/dotnet/coreclr/pull/14652
|
||||||
@ -696,13 +632,13 @@ namespace Ben.Demystifier
|
|||||||
|
|
||||||
var type = method.DeclaringType;
|
var type = method.DeclaringType;
|
||||||
|
|
||||||
if (type == null)
|
if (type is null)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since .NET Core 2:
|
// Since .NET Core 2:
|
||||||
if (StackTraceHiddenAttributeType != null)
|
if (StackTraceHiddenAttributeType is not null)
|
||||||
{
|
{
|
||||||
// Don't show any methods marked with the StackTraceHiddenAttribute
|
// Don't show any methods marked with the StackTraceHiddenAttribute
|
||||||
// https://github.com/dotnet/coreclr/pull/14652
|
// https://github.com/dotnet/coreclr/pull/14652
|
||||||
@ -716,18 +652,24 @@ namespace Ben.Demystifier
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == typeof(ValueTask<>) && method.Name == "get_Result")
|
if (type == typeof(ValueTask<>) && method.Name == "get_Result")
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (method.Name.StartsWith("System.Threading.Tasks.Sources.IValueTaskSource") && method.Name.EndsWith(".GetResult"))
|
|
||||||
|
if (method.Name.StartsWith("System.Threading.Tasks.Sources.IValueTaskSource") &&
|
||||||
|
method.Name.EndsWith(".GetResult"))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (method.Name == "GetResult" && method.DeclaringType?.FullName == "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore`1")
|
|
||||||
|
if (method.Name == "GetResult" && method.DeclaringType?.FullName ==
|
||||||
|
"System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore`1")
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == typeof(Task) || type.DeclaringType == typeof(Task))
|
if (type == typeof(Task) || type.DeclaringType == typeof(Task))
|
||||||
{
|
{
|
||||||
if (method.Name.Contains(".cctor"))
|
if (method.Name.Contains(".cctor"))
|
||||||
@ -747,12 +689,11 @@ namespace Ben.Demystifier
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == typeof(ExecutionContext))
|
if (type == typeof(ExecutionContext))
|
||||||
{
|
{
|
||||||
if (method.Name.Contains(".cctor"))
|
if (method.Name.Contains(".cctor"))
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
switch (method.Name)
|
switch (method.Name)
|
||||||
{
|
{
|
||||||
@ -821,9 +762,7 @@ namespace Ben.Demystifier
|
|||||||
private static bool IsStackTraceHidden(MemberInfo memberInfo)
|
private static bool IsStackTraceHidden(MemberInfo memberInfo)
|
||||||
{
|
{
|
||||||
if (StackTraceHiddenAttributeType is not null && !memberInfo.Module.Assembly.ReflectionOnly)
|
if (StackTraceHiddenAttributeType is not null && !memberInfo.Module.Assembly.ReflectionOnly)
|
||||||
{
|
|
||||||
return memberInfo.GetCustomAttributes(StackTraceHiddenAttributeType, false).Length != 0;
|
return memberInfo.GetCustomAttributes(StackTraceHiddenAttributeType, false).Length != 0;
|
||||||
}
|
|
||||||
|
|
||||||
EnumerableIList<CustomAttributeData> attributes;
|
EnumerableIList<CustomAttributeData> attributes;
|
||||||
try
|
try
|
||||||
@ -835,8 +774,9 @@ namespace Ben.Demystifier
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var attribute in attributes)
|
for (var i = 0; i < attributes.Count; i++)
|
||||||
{
|
{
|
||||||
|
var attribute = attributes[i];
|
||||||
// reflection-only attribute, match on name
|
// reflection-only attribute, match on name
|
||||||
if (attribute.AttributeType.FullName == StackTraceHiddenAttributeType?.FullName)
|
if (attribute.AttributeType.FullName == StackTraceHiddenAttributeType?.FullName)
|
||||||
{
|
{
|
||||||
@ -855,6 +795,7 @@ namespace Ben.Demystifier
|
|||||||
declaringType = null!;
|
declaringType = null!;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
declaringType = method.DeclaringType;
|
declaringType = method.DeclaringType;
|
||||||
|
|
||||||
var parentType = declaringType.DeclaringType;
|
var parentType = declaringType.DeclaringType;
|
||||||
@ -863,11 +804,9 @@ namespace Ben.Demystifier
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static MethodInfo[] GetDeclaredMethods(Type type) =>
|
var methods = parentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static |
|
||||||
type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||||
|
if (methods is null)
|
||||||
var methods = GetDeclaredMethods(parentType);
|
|
||||||
if (methods == null)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -888,7 +827,7 @@ namespace Ben.Demystifier
|
|||||||
{
|
{
|
||||||
foundAttribute = true;
|
foundAttribute = true;
|
||||||
foundIteratorAttribute |= asma is IteratorStateMachineAttribute
|
foundIteratorAttribute |= asma is IteratorStateMachineAttribute
|
||||||
|| AsyncIteratorStateMachineAttributeType != null
|
|| AsyncIteratorStateMachineAttributeType is not null
|
||||||
&& AsyncIteratorStateMachineAttributeType.IsInstanceOfType(asma);
|
&& AsyncIteratorStateMachineAttributeType.IsInstanceOfType(asma);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -903,6 +842,7 @@ namespace Ben.Demystifier
|
|||||||
return foundIteratorAttribute;
|
return foundIteratorAttribute;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -917,7 +857,9 @@ namespace Ben.Demystifier
|
|||||||
LambdaMethod = 'b',
|
LambdaMethod = 'b',
|
||||||
LambdaDisplayClass = 'c',
|
LambdaDisplayClass = 'c',
|
||||||
StateMachineType = 'd',
|
StateMachineType = 'd',
|
||||||
LocalFunction = 'g', // note collision with Deprecated_InitializerLocal, however this one is only used for method names
|
|
||||||
|
LocalFunction =
|
||||||
|
'g', // note collision with Deprecated_InitializerLocal, however this one is only used for method names
|
||||||
|
|
||||||
// Used by EnC:
|
// Used by EnC:
|
||||||
AwaiterField = 'u',
|
AwaiterField = 'u',
|
||||||
|
|||||||
@ -1,114 +1,70 @@
|
|||||||
// Copyright (c) Ben A Adams. All rights reserved.
|
// Copyright (c) Ben A Adams. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections;
|
namespace Ben.Demystifier;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Ben.Demystifier.Enumerable;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Ben.Demystifier
|
public partial class EnhancedStackTrace : StackTrace, IEnumerable<EnhancedStackFrame>
|
||||||
{
|
{
|
||||||
public partial class EnhancedStackTrace : StackTrace, IEnumerable<EnhancedStackFrame>
|
|
||||||
{
|
|
||||||
public static EnhancedStackTrace Current() => new EnhancedStackTrace(new StackTrace(1 /* skip this one frame */, true));
|
|
||||||
|
|
||||||
private readonly List<EnhancedStackFrame> _frames;
|
private readonly List<EnhancedStackFrame> _frames;
|
||||||
|
|
||||||
// Summary:
|
/// <summary>
|
||||||
// Initializes a new instance of the System.Diagnostics.StackTrace class using the
|
/// Initializes a new instance of the System.Diagnostics.StackTrace class using the
|
||||||
// provided exception object.
|
/// provided exception object
|
||||||
//
|
/// </summary>
|
||||||
// Parameters:
|
|
||||||
// e:
|
|
||||||
// The exception object from which to construct the stack trace.
|
|
||||||
//
|
|
||||||
// Exceptions:
|
|
||||||
// T:System.ArgumentNullException:
|
|
||||||
// The parameter e is null.
|
|
||||||
public EnhancedStackTrace(Exception e)
|
public EnhancedStackTrace(Exception e)
|
||||||
{
|
{
|
||||||
if (e == null)
|
_frames = GetFrames(new StackTrace(e, true));
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(e));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_frames = GetFrames(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public EnhancedStackTrace(StackTrace stackTrace)
|
public EnhancedStackTrace(StackTrace stackTrace)
|
||||||
{
|
{
|
||||||
if (stackTrace == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(stackTrace));
|
|
||||||
}
|
|
||||||
|
|
||||||
_frames = GetFrames(stackTrace);
|
_frames = GetFrames(stackTrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the number of frames in the stack trace.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The number of frames in the stack trace.</returns>
|
/// <returns>The number of frames in the stack trace.</returns>
|
||||||
public override int FrameCount => _frames.Count;
|
public override int FrameCount => _frames.Count;
|
||||||
|
|
||||||
/// <summary>
|
IEnumerator<EnhancedStackFrame> IEnumerable<EnhancedStackFrame>.GetEnumerator() => _frames.GetEnumerator();
|
||||||
/// Gets the specified stack frame.
|
|
||||||
/// </summary>
|
IEnumerator IEnumerable.GetEnumerator() => _frames.GetEnumerator();
|
||||||
|
|
||||||
|
public static EnhancedStackTrace Current()
|
||||||
|
=> new EnhancedStackTrace(new StackTrace(1 /* skip this one frame */, true));
|
||||||
|
|
||||||
/// <param name="index">The index of the stack frame requested.</param>
|
/// <param name="index">The index of the stack frame requested.</param>
|
||||||
/// <returns>The specified stack frame.</returns>
|
/// <returns>The specified stack frame.</returns>
|
||||||
public override StackFrame GetFrame(int index) => _frames[index];
|
public override StackFrame GetFrame(int index) => _frames[index];
|
||||||
|
|
||||||
/// <summary>
|
/// <returns>a copy of all stack frames in the current stack trace. </returns>
|
||||||
/// Returns a copy of all stack frames in the current stack trace.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// An array of type System.Diagnostics.StackFrame representing the function calls
|
|
||||||
/// in the stack trace.
|
|
||||||
/// </returns>
|
|
||||||
public override StackFrame[] GetFrames() => _frames.ToArray();
|
public override StackFrame[] GetFrames() => _frames.ToArray();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Builds a readable representation of the stack trace.
|
/// Builds a readable representation of the stack trace.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A readable representation of the stack trace.</returns>
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
if (_frames == null || _frames.Count == 0) return "";
|
if (_frames.Count == 0) return "";
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
|
AppendTo(sb);
|
||||||
Append(sb);
|
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AppendTo(StringBuilder sb)
|
||||||
internal void Append(StringBuilder sb)
|
|
||||||
{
|
{
|
||||||
var frames = _frames;
|
var count = _frames.Count;
|
||||||
var count = frames.Count;
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
if (i > 0)
|
if (i > 0) sb.Append(Environment.NewLine);
|
||||||
{
|
var frame = _frames[i];
|
||||||
sb.Append(Environment.NewLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
var frame = frames[i];
|
|
||||||
|
|
||||||
sb.Append(" at ");
|
sb.Append(" at ");
|
||||||
frame.MethodInfo.Append(sb);
|
frame.MethodInfo.AppendTo(sb);
|
||||||
|
|
||||||
if (frame.GetFileName() is {} fileName
|
var fileName = frame.GetFileName();
|
||||||
// IsNullOrEmpty alone wasn't enough to disable the null warning
|
if (!string.IsNullOrEmpty(fileName))
|
||||||
&& !string.IsNullOrEmpty(fileName))
|
|
||||||
{
|
{
|
||||||
sb.Append(" in ");
|
sb.Append(" in ");
|
||||||
sb.Append(TryGetFullPath(fileName));
|
sb.Append(TryGetFullPath(fileName));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var lineNo = frame.GetFileLineNumber();
|
var lineNo = frame.GetFileLineNumber();
|
||||||
@ -120,22 +76,14 @@ namespace Ben.Demystifier
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EnumerableIList<EnhancedStackFrame> GetEnumerator() => EnumerableIList.Create(_frames);
|
|
||||||
IEnumerator<EnhancedStackFrame> IEnumerable<EnhancedStackFrame>.GetEnumerator() => _frames.GetEnumerator();
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => _frames.GetEnumerator();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to convert a given <paramref name="filePath"/> to a full path.
|
/// Tries to convert a given <paramref name="filePath" /> to a full path.
|
||||||
/// Returns original value if the conversion isn't possible or a given path is relative.
|
/// Returns original value if the conversion isn't possible or a given path is relative.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string TryGetFullPath(string filePath)
|
public static string TryGetFullPath(string filePath)
|
||||||
{
|
{
|
||||||
if (Uri.TryCreate(filePath, UriKind.Absolute, out var uri) && uri.IsFile)
|
if (Uri.TryCreate(filePath, UriKind.Absolute, out var uri) && uri.IsFile)
|
||||||
{
|
|
||||||
return Uri.UnescapeDataString(uri.AbsolutePath);
|
return Uri.UnescapeDataString(uri.AbsolutePath);
|
||||||
}
|
|
||||||
|
|
||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,24 +1,27 @@
|
|||||||
// Copyright (c) Ben A Adams. All rights reserved.
|
// Copyright (c) Ben A Adams. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
namespace Ben.Demystifier.Enumerable
|
namespace Ben.Demystifier.Enumerable;
|
||||||
{
|
|
||||||
public static class EnumerableIList
|
|
||||||
{
|
|
||||||
public static EnumerableIList<T> Create<T>(IList<T> list) => new EnumerableIList<T>(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct EnumerableIList<T> : IEnumerableIList<T>, IList<T>
|
public static class EnumerableIList
|
||||||
|
{
|
||||||
|
public static EnumerableIList<T> Create<T>(IList<T> list)
|
||||||
{
|
{
|
||||||
|
return new(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct EnumerableIList<T> : IEnumerableIList<T>, IList<T>
|
||||||
|
{
|
||||||
private readonly IList<T> _list;
|
private readonly IList<T> _list;
|
||||||
|
|
||||||
public EnumerableIList(IList<T> list) => _list = list;
|
public EnumerableIList(IList<T> list) => _list = list;
|
||||||
|
|
||||||
public EnumeratorIList<T> GetEnumerator() => new EnumeratorIList<T>(_list);
|
public EnumeratorIList<T> GetEnumerator() => new(_list);
|
||||||
|
|
||||||
public static implicit operator EnumerableIList<T>(List<T> list) => new EnumerableIList<T>(list);
|
public static implicit operator EnumerableIList<T>(List<T> list) => new(list);
|
||||||
|
|
||||||
public static implicit operator EnumerableIList<T>(T[] array) => new EnumerableIList<T>(array);
|
public static implicit operator EnumerableIList<T>(T[] array) => new(array);
|
||||||
|
|
||||||
public static EnumerableIList<T> Empty = default;
|
public static EnumerableIList<T> Empty = default;
|
||||||
|
|
||||||
@ -26,7 +29,11 @@ namespace Ben.Demystifier.Enumerable
|
|||||||
// IList pass through
|
// IList pass through
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public T this[int index] { get => _list[index]; set => _list[index] = value; }
|
public T this[int index]
|
||||||
|
{
|
||||||
|
get => _list[index];
|
||||||
|
set => _list[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int Count => _list.Count;
|
public int Count => _list.Count;
|
||||||
@ -58,8 +65,13 @@ namespace Ben.Demystifier.Enumerable
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void RemoveAt(int index) => _list.RemoveAt(index);
|
public void RemoveAt(int index) => _list.RemoveAt(index);
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
|
IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,10 +1,10 @@
|
|||||||
// Copyright (c) Ben A Adams. All rights reserved.
|
// Copyright (c) Ben A Adams. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
namespace Ben.Demystifier.Enumerable
|
namespace Ben.Demystifier.Enumerable;
|
||||||
|
|
||||||
|
public struct EnumeratorIList<T> : IEnumerator<T>
|
||||||
{
|
{
|
||||||
public struct EnumeratorIList<T> : IEnumerator<T>
|
|
||||||
{
|
|
||||||
private readonly IList<T> _list;
|
private readonly IList<T> _list;
|
||||||
private int _index;
|
private int _index;
|
||||||
|
|
||||||
@ -23,8 +23,13 @@ namespace Ben.Demystifier.Enumerable
|
|||||||
return _index < (_list?.Count ?? 0);
|
return _index < (_list?.Count ?? 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() { }
|
public void Dispose()
|
||||||
|
{ }
|
||||||
|
|
||||||
object? IEnumerator.Current => Current;
|
object? IEnumerator.Current => Current;
|
||||||
public void Reset() => _index = -1;
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_index = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,10 +1,9 @@
|
|||||||
// Copyright (c) Ben A Adams. All rights reserved.
|
// Copyright (c) Ben A Adams. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
namespace Ben.Demystifier.Enumerable
|
namespace Ben.Demystifier.Enumerable;
|
||||||
|
|
||||||
|
internal interface IEnumerableIList<T> : IEnumerable<T>
|
||||||
{
|
{
|
||||||
interface IEnumerableIList<T> : IEnumerable<T>
|
|
||||||
{
|
|
||||||
new EnumeratorIList<T> GetEnumerator();
|
new EnumeratorIList<T> GetEnumerator();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,25 +1,31 @@
|
|||||||
// Copyright (c) Ben A Adams. All rights reserved.
|
// Copyright (c) Ben A Adams. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
global using System.Diagnostics;
|
|
||||||
global using System;
|
global using System;
|
||||||
|
global using System.Text;
|
||||||
|
global using System.Collections;
|
||||||
|
global using System.Collections.Generic;
|
||||||
|
global using System.Diagnostics;
|
||||||
|
global using System.Reflection;
|
||||||
|
global using System.Linq;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Ben.Demystifier.Enumerable;
|
using Ben.Demystifier.Enumerable;
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Ben.Demystifier
|
namespace Ben.Demystifier;
|
||||||
|
|
||||||
|
public static class ExceptionExtensions
|
||||||
{
|
{
|
||||||
public static class ExceptionExtensions
|
private static readonly FieldInfo? StackTraceString =
|
||||||
{
|
typeof(Exception).GetField("_stackTraceString", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
private static readonly FieldInfo? stackTraceString = typeof(Exception).GetField("_stackTraceString", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
||||||
|
|
||||||
private static void SetStackTracesString(this Exception exception, string value)
|
private static void SetStackTracesString(this Exception exception, string value)
|
||||||
=> stackTraceString?.SetValue(exception, value);
|
{
|
||||||
|
StackTraceString?.SetValue(exception, value);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Demystifies the given <paramref name="exception"/> and tracks the original stack traces for the whole exception tree.
|
/// Demystifies the given <paramref name="exception" /> and tracks the original stack traces for the whole exception
|
||||||
|
/// tree.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static T Demystify<T>(this T exception) where T : Exception
|
public static T Demystify<T>(this T exception) where T : Exception
|
||||||
{
|
{
|
||||||
@ -27,18 +33,11 @@ namespace Ben.Demystifier
|
|||||||
{
|
{
|
||||||
var stackTrace = new EnhancedStackTrace(exception);
|
var stackTrace = new EnhancedStackTrace(exception);
|
||||||
|
|
||||||
if (stackTrace.FrameCount > 0)
|
if (stackTrace.FrameCount > 0) exception.SetStackTracesString(stackTrace.ToString());
|
||||||
{
|
|
||||||
exception.SetStackTracesString(stackTrace.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exception is AggregateException aggEx)
|
if (exception is AggregateException aggEx)
|
||||||
{
|
|
||||||
foreach (var ex in EnumerableIList.Create(aggEx.InnerExceptions))
|
foreach (var ex in EnumerableIList.Create(aggEx.InnerExceptions))
|
||||||
{
|
|
||||||
ex.Demystify();
|
ex.Demystify();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exception.InnerException?.Demystify();
|
exception.InnerException?.Demystify();
|
||||||
}
|
}
|
||||||
@ -51,16 +50,17 @@ namespace Ben.Demystifier
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets demystified string representation of the <paramref name="exception"/>.
|
/// Gets demystified string representation of the <paramref name="exception" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// <see cref="Demystify{T}"/> method mutates the exception instance that can cause
|
/// <see cref="Demystify{T}" /> method mutates the exception instance that can cause
|
||||||
/// issues if a system relies on the stack trace be in the specific form.
|
/// issues if a system relies on the stack trace be in the specific form.
|
||||||
/// Unlike <see cref="Demystify{T}"/> this method is pure. It calls <see cref="Demystify{T}"/> first,
|
/// Unlike <see cref="Demystify{T}" /> this method is pure. It calls <see cref="Demystify{T}" /> first,
|
||||||
/// computes a demystified string representation and then restores the original state of the exception back.
|
/// computes a demystified string representation and then restores the original state of the exception back.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[Pure]
|
[Pure]
|
||||||
public static string ToStringDemystified(this Exception exception)
|
public static string ToStringDemystified(this Exception exception)
|
||||||
=> new StringBuilder().AppendDemystified(exception).ToString();
|
{
|
||||||
|
return new StringBuilder().AppendDemystified(exception).ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,18 +1,40 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
namespace Ben.Demystifier.Internal
|
namespace Ben.Demystifier.Internal;
|
||||||
|
|
||||||
|
internal class ILReader
|
||||||
{
|
{
|
||||||
internal class ILReader
|
private static readonly OpCode[] singleByteOpCode;
|
||||||
{
|
private static readonly OpCode[] doubleByteOpCode;
|
||||||
private static OpCode[] singleByteOpCode;
|
|
||||||
private static OpCode[] doubleByteOpCode;
|
|
||||||
|
|
||||||
private readonly byte[] _cil;
|
private readonly byte[] _cil;
|
||||||
private int ptr;
|
private int ptr;
|
||||||
|
|
||||||
|
static ILReader()
|
||||||
|
{
|
||||||
|
singleByteOpCode = new OpCode[225];
|
||||||
|
doubleByteOpCode = new OpCode[31];
|
||||||
|
|
||||||
public ILReader(byte[] cil) => _cil = cil;
|
var fields = GetOpCodeFields();
|
||||||
|
|
||||||
|
for (var i = 0; i < fields.Length; i++)
|
||||||
|
{
|
||||||
|
var code = (OpCode)fields[i].GetValue(null)!;
|
||||||
|
if (code.OpCodeType == OpCodeType.Nternal)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (code.Size == 1)
|
||||||
|
singleByteOpCode[code.Value] = code;
|
||||||
|
else
|
||||||
|
doubleByteOpCode[code.Value & 0xff] = code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ILReader(byte[] cil)
|
||||||
|
{
|
||||||
|
_cil = cil;
|
||||||
|
}
|
||||||
|
|
||||||
public OpCode OpCode { get; private set; }
|
public OpCode OpCode { get; private set; }
|
||||||
public int MetadataToken { get; private set; }
|
public int MetadataToken { get; private set; }
|
||||||
@ -26,19 +48,19 @@ namespace Ben.Demystifier.Internal
|
|||||||
Operand = ReadOperand(OpCode, methodInfo);
|
Operand = ReadOperand(OpCode, methodInfo);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
OpCode ReadOpCode()
|
private OpCode ReadOpCode()
|
||||||
{
|
{
|
||||||
var instruction = ReadByte();
|
var instruction = ReadByte();
|
||||||
if (instruction < 254)
|
if (instruction < 254)
|
||||||
return singleByteOpCode[instruction];
|
return singleByteOpCode[instruction];
|
||||||
else
|
|
||||||
return doubleByteOpCode[ReadByte()];
|
return doubleByteOpCode[ReadByte()];
|
||||||
}
|
}
|
||||||
|
|
||||||
MemberInfo? ReadOperand(OpCode code, MethodBase methodInfo)
|
private MemberInfo? ReadOperand(OpCode code, MethodBase methodInfo)
|
||||||
{
|
{
|
||||||
MetadataToken = 0;
|
MetadataToken = 0;
|
||||||
int inlineLength;
|
int inlineLength;
|
||||||
@ -47,15 +69,11 @@ namespace Ben.Demystifier.Internal
|
|||||||
case OperandType.InlineMethod:
|
case OperandType.InlineMethod:
|
||||||
MetadataToken = ReadInt();
|
MetadataToken = ReadInt();
|
||||||
Type[]? methodArgs = null;
|
Type[]? methodArgs = null;
|
||||||
if (methodInfo.GetType() != typeof(ConstructorInfo) && !methodInfo.GetType().IsSubclassOf(typeof(ConstructorInfo)))
|
if (methodInfo.GetType() != typeof(ConstructorInfo) &&
|
||||||
{
|
!methodInfo.GetType().IsSubclassOf(typeof(ConstructorInfo)))
|
||||||
methodArgs = methodInfo.GetGenericArguments();
|
methodArgs = methodInfo.GetGenericArguments();
|
||||||
}
|
|
||||||
Type[]? typeArgs = null;
|
Type[]? typeArgs = null;
|
||||||
if (methodInfo.DeclaringType != null)
|
if (methodInfo.DeclaringType is not null) typeArgs = methodInfo.DeclaringType.GetGenericArguments();
|
||||||
{
|
|
||||||
typeArgs = methodInfo.DeclaringType.GetGenericArguments();
|
|
||||||
}
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return methodInfo.Module.ResolveMember(MetadataToken, typeArgs, methodArgs);
|
return methodInfo.Module.ResolveMember(MetadataToken, typeArgs, methodArgs);
|
||||||
@ -101,45 +119,27 @@ namespace Ben.Demystifier.Internal
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < inlineLength; i++)
|
for (var i = 0; i < inlineLength; i++) ReadByte();
|
||||||
{
|
|
||||||
ReadByte();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte ReadByte() => _cil[ptr++];
|
private byte ReadByte()
|
||||||
|
{
|
||||||
|
return _cil[ptr++];
|
||||||
|
}
|
||||||
|
|
||||||
int ReadInt()
|
private int ReadInt()
|
||||||
{
|
{
|
||||||
var b1 = ReadByte();
|
var b1 = ReadByte();
|
||||||
var b2 = ReadByte();
|
var b2 = ReadByte();
|
||||||
var b3 = ReadByte();
|
var b3 = ReadByte();
|
||||||
var b4 = ReadByte();
|
var b4 = ReadByte();
|
||||||
return b1 | b2 << 8 | b3 << 16 | b4 << 24;
|
return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ILReader()
|
private static FieldInfo[] GetOpCodeFields()
|
||||||
{
|
{
|
||||||
singleByteOpCode = new OpCode[225];
|
return typeof(OpCodes).GetFields(BindingFlags.Public | BindingFlags.Static);
|
||||||
doubleByteOpCode = new OpCode[31];
|
|
||||||
|
|
||||||
var fields = GetOpCodeFields();
|
|
||||||
|
|
||||||
for (var i = 0; i < fields.Length; i++)
|
|
||||||
{
|
|
||||||
var code = (OpCode)fields[i].GetValue(null)!;
|
|
||||||
if (code.OpCodeType == OpCodeType.Nternal)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (code.Size == 1)
|
|
||||||
singleByteOpCode[code.Value] = code;
|
|
||||||
else
|
|
||||||
doubleByteOpCode[code.Value & 0xff] = code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static FieldInfo[] GetOpCodeFields() => typeof(OpCodes).GetFields(BindingFlags.Public | BindingFlags.Static);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,38 +1,37 @@
|
|||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
|
||||||
using System.Reflection.Metadata;
|
using System.Reflection.Metadata;
|
||||||
using System.Reflection.Metadata.Ecma335;
|
using System.Reflection.Metadata.Ecma335;
|
||||||
using System.Reflection.PortableExecutable;
|
using System.Reflection.PortableExecutable;
|
||||||
|
|
||||||
namespace Ben.Demystifier.Internal
|
namespace Ben.Demystifier.Internal;
|
||||||
{
|
|
||||||
// Adapted from https://github.com/aspnet/Common/blob/dev/shared/Microsoft.Extensions.StackTrace.Sources/StackFrame/PortablePdbReader.cs
|
|
||||||
internal class PortablePdbReader : IDisposable
|
|
||||||
{
|
|
||||||
private readonly Dictionary<string, MetadataReaderProvider> _cache =
|
|
||||||
new Dictionary<string, MetadataReaderProvider>(StringComparer.Ordinal);
|
|
||||||
|
|
||||||
public void PopulateStackFrame(StackFrame frameInfo, MethodBase method, int IlOffset, out string fileName, out int row, out int column)
|
// Adapted from https://github.com/aspnet/Common/blob/dev/shared/Microsoft.Extensions.StackTrace.Sources/StackFrame/PortablePdbReader.cs
|
||||||
|
internal class PortablePdbReader : IDisposable
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, MetadataReaderProvider> _cache = new(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (var entry in _cache) entry.Value.Dispose();
|
||||||
|
|
||||||
|
_cache.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopulateStackFrame(StackFrame frameInfo, MethodBase method, int IlOffset, out string fileName,
|
||||||
|
out int row, out int column)
|
||||||
{
|
{
|
||||||
fileName = "";
|
fileName = "";
|
||||||
row = 0;
|
row = 0;
|
||||||
column = 0;
|
column = 0;
|
||||||
|
|
||||||
if (method.Module.Assembly.IsDynamic)
|
if (method.Module.Assembly.IsDynamic) return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var metadataReader = GetMetadataReader(method.Module.Assembly.Location);
|
var metadataReader = GetMetadataReader(method.Module.Assembly.Location);
|
||||||
|
|
||||||
if (metadataReader == null)
|
if (metadataReader is null) return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var methodToken = MetadataTokens.Handle(method.MetadataToken);
|
var methodToken = MetadataTokens.Handle(method.MetadataToken);
|
||||||
|
|
||||||
@ -48,15 +47,9 @@ namespace Ben.Demystifier.Internal
|
|||||||
|
|
||||||
foreach (var point in sequencePoints)
|
foreach (var point in sequencePoints)
|
||||||
{
|
{
|
||||||
if (point.Offset > IlOffset)
|
if (point.Offset > IlOffset) break;
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (point.StartLine != SequencePoint.HiddenLine)
|
if (point.StartLine != SequencePoint.HiddenLine) bestPointSoFar = point;
|
||||||
{
|
|
||||||
bestPointSoFar = point;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bestPointSoFar.HasValue)
|
if (bestPointSoFar.HasValue)
|
||||||
@ -88,10 +81,7 @@ namespace Ben.Demystifier.Internal
|
|||||||
|
|
||||||
private static string? GetPdbPath(string assemblyPath)
|
private static string? GetPdbPath(string assemblyPath)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(assemblyPath))
|
if (string.IsNullOrEmpty(assemblyPath)) return null;
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (File.Exists(assemblyPath))
|
if (File.Exists(assemblyPath))
|
||||||
{
|
{
|
||||||
@ -99,7 +89,6 @@ namespace Ben.Demystifier.Internal
|
|||||||
|
|
||||||
using var peReader = new PEReader(peStream);
|
using var peReader = new PEReader(peStream);
|
||||||
foreach (var entry in peReader.ReadDebugDirectory())
|
foreach (var entry in peReader.ReadDebugDirectory())
|
||||||
{
|
|
||||||
if (entry.Type == DebugDirectoryEntryType.CodeView)
|
if (entry.Type == DebugDirectoryEntryType.CodeView)
|
||||||
{
|
{
|
||||||
var codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry);
|
var codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry);
|
||||||
@ -109,7 +98,6 @@ namespace Ben.Demystifier.Internal
|
|||||||
: Path.Combine(peDirectory, Path.GetFileName(codeViewData.Path));
|
: Path.Combine(peDirectory, Path.GetFileName(codeViewData.Path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -122,15 +110,4 @@ namespace Ben.Demystifier.Internal
|
|||||||
pdbStream.ReadByte() == 'J' &&
|
pdbStream.ReadByte() == 'J' &&
|
||||||
pdbStream.ReadByte() == 'B';
|
pdbStream.ReadByte() == 'B';
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
foreach (var entry in _cache)
|
|
||||||
{
|
|
||||||
entry.Value?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_cache.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,21 +1,19 @@
|
|||||||
// Copyright (c) Ben A Adams. All rights reserved.
|
// Copyright (c) Ben A Adams. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ben.Demystifier.Internal
|
namespace Ben.Demystifier.Internal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A helper class that contains utilities methods for dealing with reflection.
|
||||||
|
/// </summary>
|
||||||
|
public static class ReflectionHelper
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// A helper class that contains utilities methods for dealing with reflection.
|
|
||||||
/// </summary>
|
|
||||||
public static class ReflectionHelper
|
|
||||||
{
|
|
||||||
private static PropertyInfo? transformerNamesLazyPropertyInfo;
|
private static PropertyInfo? transformerNamesLazyPropertyInfo;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if the <paramref name="type"/> is a value tuple type.
|
/// Returns true if the <paramref name="type" /> is a value tuple type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool IsValueTuple(this Type type)
|
public static bool IsValueTuple(this Type type)
|
||||||
{
|
{
|
||||||
@ -23,11 +21,12 @@ namespace Ben.Demystifier.Internal
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if the given <paramref name="attribute"/> is of type <code>TupleElementNameAttribute</code>.
|
/// Returns true if the given <paramref name="attribute" /> is of type <code>TupleElementNameAttribute</code>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// To avoid compile-time dependency hell with System.ValueTuple, this method uses reflection and not checks statically that
|
/// To avoid compile-time dependency hell with System.ValueTuple, this method uses reflection and not checks statically
|
||||||
/// the given <paramref name="attribute"/> is <code>TupleElementNameAttribute</code>.
|
/// that
|
||||||
|
/// the given <paramref name="attribute" /> is <code>TupleElementNameAttribute</code>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static bool IsTupleElementNameAttribute(this Attribute attribute)
|
public static bool IsTupleElementNameAttribute(this Attribute attribute)
|
||||||
{
|
{
|
||||||
@ -37,7 +36,7 @@ namespace Ben.Demystifier.Internal
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns 'TransformNames' property value from a given <paramref name="attribute"/>.
|
/// Returns 'TransformNames' property value from a given <paramref name="attribute" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// To avoid compile-time dependency hell with System.ValueTuple, this method uses reflection
|
/// To avoid compile-time dependency hell with System.ValueTuple, this method uses reflection
|
||||||
@ -48,15 +47,14 @@ namespace Ben.Demystifier.Internal
|
|||||||
Debug.Assert(attribute.IsTupleElementNameAttribute());
|
Debug.Assert(attribute.IsTupleElementNameAttribute());
|
||||||
|
|
||||||
var propertyInfo = GetTransformNamesPropertyInfo(attribute.GetType());
|
var propertyInfo = GetTransformNamesPropertyInfo(attribute.GetType());
|
||||||
return propertyInfo?.GetValue(attribute) as IList<string>;
|
return propertyInfo.GetValue(attribute) as IList<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PropertyInfo? GetTransformNamesPropertyInfo(Type attributeType)
|
private static PropertyInfo GetTransformNamesPropertyInfo(Type attributeType)
|
||||||
{
|
{
|
||||||
#pragma warning disable 8634
|
#pragma warning disable 8634
|
||||||
return LazyInitializer.EnsureInitialized(ref transformerNamesLazyPropertyInfo,
|
return LazyInitializer.EnsureInitialized(ref transformerNamesLazyPropertyInfo,
|
||||||
#pragma warning restore 8634
|
#pragma warning restore 8634
|
||||||
() => attributeType.GetProperty("TransformNames", BindingFlags.Instance | BindingFlags.Public)!);
|
() => attributeType.GetProperty("TransformNames", BindingFlags.Instance | BindingFlags.Public)!);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -2,13 +2,11 @@
|
|||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Ben.Demystifier.Enumerable;
|
using Ben.Demystifier.Enumerable;
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Ben.Demystifier
|
namespace Ben.Demystifier;
|
||||||
|
|
||||||
|
public class ResolvedMethod
|
||||||
{
|
{
|
||||||
public class ResolvedMethod
|
|
||||||
{
|
|
||||||
public MethodBase? MethodBase { get; set; }
|
public MethodBase? MethodBase { get; set; }
|
||||||
|
|
||||||
public Type? DeclaringType { get; set; }
|
public Type? DeclaringType { get; set; }
|
||||||
@ -48,27 +46,20 @@ namespace Ben.Demystifier
|
|||||||
SubMethod == obj.SubMethod;
|
SubMethod == obj.SubMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => Append(new StringBuilder()).ToString();
|
public override string ToString() => AppendTo(new StringBuilder()).ToString();
|
||||||
|
|
||||||
public StringBuilder Append(StringBuilder builder)
|
public StringBuilder AppendTo(StringBuilder builder, bool fullName = true)
|
||||||
=> Append(builder, true);
|
|
||||||
|
|
||||||
public StringBuilder Append(StringBuilder builder, bool fullName)
|
|
||||||
{
|
{
|
||||||
if (IsAsync)
|
if (IsAsync) builder.Append("async ");
|
||||||
{
|
|
||||||
builder.Append("async ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ReturnParameter != null)
|
if (ReturnParameter is not null)
|
||||||
{
|
{
|
||||||
ReturnParameter.Append(builder);
|
ReturnParameter.Append(builder);
|
||||||
builder.Append(" ");
|
builder.Append(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DeclaringType != null)
|
if (DeclaringType is not null)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (Name == ".ctor")
|
if (Name == ".ctor")
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(SubMethod) && !IsLambda)
|
if (string.IsNullOrEmpty(SubMethod) && !IsLambda)
|
||||||
@ -84,65 +75,49 @@ namespace Ben.Demystifier
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
AppendDeclaringTypeName(builder, fullName)
|
AppendDeclaringTypeName(builder, fullName)
|
||||||
.Append(".")
|
.Append('.')
|
||||||
.Append(Name);
|
.Append(Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else builder.Append(Name);
|
||||||
{
|
|
||||||
builder.Append(Name);
|
|
||||||
}
|
|
||||||
builder.Append(GenericArguments);
|
builder.Append(GenericArguments);
|
||||||
|
|
||||||
builder.Append("(");
|
builder.Append('(');
|
||||||
if (MethodBase != null)
|
if (MethodBase is not null)
|
||||||
{
|
{
|
||||||
var isFirst = true;
|
var isFirst = true;
|
||||||
foreach(var param in Parameters)
|
foreach (var param in Parameters)
|
||||||
{
|
{
|
||||||
if (isFirst)
|
if (isFirst)
|
||||||
{
|
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
}
|
else builder.Append(", ");
|
||||||
else
|
|
||||||
{
|
|
||||||
builder.Append(", ");
|
|
||||||
}
|
|
||||||
param.Append(builder);
|
param.Append(builder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else builder.Append('?');
|
||||||
{
|
|
||||||
builder.Append("?");
|
builder.Append(')');
|
||||||
}
|
|
||||||
builder.Append(")");
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(SubMethod) || IsLambda)
|
if (!string.IsNullOrEmpty(SubMethod) || IsLambda)
|
||||||
{
|
{
|
||||||
builder.Append("+");
|
builder.Append('+');
|
||||||
builder.Append(SubMethod);
|
builder.Append(SubMethod);
|
||||||
builder.Append("(");
|
builder.Append('(');
|
||||||
if (SubMethodBase != null)
|
if (SubMethodBase is not null)
|
||||||
{
|
{
|
||||||
var isFirst = true;
|
var isFirst = true;
|
||||||
foreach (var param in SubMethodParameters)
|
foreach (var param in SubMethodParameters)
|
||||||
{
|
{
|
||||||
if (isFirst)
|
if (isFirst)
|
||||||
{
|
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
}
|
else builder.Append(", ");
|
||||||
else
|
|
||||||
{
|
|
||||||
builder.Append(", ");
|
|
||||||
}
|
|
||||||
param.Append(builder);
|
param.Append(builder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else builder.Append('?');
|
||||||
{
|
|
||||||
builder.Append("?");
|
builder.Append(')');
|
||||||
}
|
|
||||||
builder.Append(")");
|
|
||||||
if (IsLambda)
|
if (IsLambda)
|
||||||
{
|
{
|
||||||
builder.Append(" => { }");
|
builder.Append(" => { }");
|
||||||
@ -151,22 +126,18 @@ namespace Ben.Demystifier
|
|||||||
{
|
{
|
||||||
builder.Append(" [");
|
builder.Append(" [");
|
||||||
builder.Append(Ordinal);
|
builder.Append(Ordinal);
|
||||||
builder.Append("]");
|
builder.Append(']');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RecurseCount > 0)
|
if (RecurseCount > 0) builder.Append($" x {RecurseCount + 1:0}");
|
||||||
{
|
|
||||||
builder.Append($" x {RecurseCount + 1:0}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private StringBuilder AppendDeclaringTypeName(StringBuilder builder, bool fullName = true)
|
private StringBuilder AppendDeclaringTypeName(StringBuilder builder, bool fullName = true)
|
||||||
{
|
{
|
||||||
return DeclaringType != null ? builder.AppendTypeDisplayName(DeclaringType, fullName: fullName, includeGenericParameterNames: true) : builder;
|
return DeclaringType is not null ? builder.AppendTypeDisplayName(DeclaringType, fullName, true) : builder;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,12 +1,15 @@
|
|||||||
// Copyright (c) Ben A Adams. All rights reserved.
|
// Copyright (c) Ben A Adams. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Text;
|
namespace Ben.Demystifier;
|
||||||
|
|
||||||
namespace Ben.Demystifier
|
public class ResolvedParameter
|
||||||
{
|
{
|
||||||
public class ResolvedParameter
|
public ResolvedParameter(Type resolvedType)
|
||||||
{
|
{
|
||||||
|
ResolvedType = resolvedType;
|
||||||
|
}
|
||||||
|
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|
||||||
public Type ResolvedType { get; set; }
|
public Type ResolvedType { get; set; }
|
||||||
@ -14,9 +17,10 @@ namespace Ben.Demystifier
|
|||||||
public string? Prefix { get; set; }
|
public string? Prefix { get; set; }
|
||||||
public bool IsDynamicType { get; set; }
|
public bool IsDynamicType { get; set; }
|
||||||
|
|
||||||
public ResolvedParameter(Type resolvedType) => ResolvedType = resolvedType;
|
public override string ToString()
|
||||||
|
{
|
||||||
public override string ToString() => Append(new StringBuilder()).ToString();
|
return Append(new StringBuilder()).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
public StringBuilder Append(StringBuilder sb)
|
public StringBuilder Append(StringBuilder sb)
|
||||||
{
|
{
|
||||||
@ -24,36 +28,22 @@ namespace Ben.Demystifier
|
|||||||
return sb;
|
return sb;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Prefix))
|
if (!string.IsNullOrEmpty(Prefix))
|
||||||
{
|
|
||||||
sb.Append(Prefix)
|
sb.Append(Prefix)
|
||||||
.Append(" ");
|
.Append(' ');
|
||||||
}
|
|
||||||
|
|
||||||
if (IsDynamicType)
|
if (IsDynamicType)
|
||||||
{
|
|
||||||
sb.Append("dynamic");
|
sb.Append("dynamic");
|
||||||
}
|
else AppendTypeName(sb);
|
||||||
else if (ResolvedType != null)
|
|
||||||
{
|
|
||||||
AppendTypeName(sb);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sb.Append("?");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Name))
|
if (!string.IsNullOrEmpty(Name))
|
||||||
{
|
sb.Append(' ')
|
||||||
sb.Append(" ")
|
|
||||||
.Append(Name);
|
.Append(Name);
|
||||||
}
|
|
||||||
|
|
||||||
return sb;
|
return sb;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void AppendTypeName(StringBuilder sb)
|
protected virtual void AppendTypeName(StringBuilder sb)
|
||||||
{
|
{
|
||||||
sb.AppendTypeDisplayName(ResolvedType, fullName: false, includeGenericParameterNames: true);
|
sb.AppendTypeDisplayName(ResolvedType, false, true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,12 +2,11 @@
|
|||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Ben.Demystifier.Enumerable;
|
using Ben.Demystifier.Enumerable;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Ben.Demystifier
|
namespace Ben.Demystifier;
|
||||||
|
|
||||||
|
public static class StringBuilderExtentions
|
||||||
{
|
{
|
||||||
public static class StringBuilderExtentions
|
|
||||||
{
|
|
||||||
public static StringBuilder AppendDemystified(this StringBuilder builder, Exception exception)
|
public static StringBuilder AppendDemystified(this StringBuilder builder, Exception exception)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -15,29 +14,16 @@ namespace Ben.Demystifier
|
|||||||
var stackTrace = new EnhancedStackTrace(exception);
|
var stackTrace = new EnhancedStackTrace(exception);
|
||||||
|
|
||||||
builder.Append(exception.GetType());
|
builder.Append(exception.GetType());
|
||||||
if (!string.IsNullOrEmpty(exception.Message))
|
if (!string.IsNullOrEmpty(exception.Message)) builder.Append(": ").Append(exception.Message);
|
||||||
{
|
|
||||||
builder.Append(": ").Append(exception.Message);
|
|
||||||
}
|
|
||||||
builder.Append(Environment.NewLine);
|
builder.Append(Environment.NewLine);
|
||||||
|
|
||||||
if (stackTrace.FrameCount > 0)
|
if (stackTrace.FrameCount > 0) stackTrace.AppendTo(builder);
|
||||||
{
|
|
||||||
stackTrace.Append(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exception is AggregateException aggEx)
|
if (exception is AggregateException aggEx)
|
||||||
{
|
|
||||||
foreach (var ex in EnumerableIList.Create(aggEx.InnerExceptions))
|
foreach (var ex in EnumerableIList.Create(aggEx.InnerExceptions))
|
||||||
{
|
|
||||||
builder.AppendInnerException(ex);
|
builder.AppendInnerException(ex);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exception.InnerException != null)
|
if (exception.InnerException is not null) builder.AppendInnerException(exception.InnerException);
|
||||||
{
|
|
||||||
builder.AppendInnerException(exception.InnerException);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -48,7 +34,8 @@ namespace Ben.Demystifier
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void AppendInnerException(this StringBuilder builder, Exception exception)
|
private static void AppendInnerException(this StringBuilder builder, Exception exception)
|
||||||
=> builder.Append(" ---> ")
|
{
|
||||||
|
builder.Append(" ---> ")
|
||||||
.AppendDemystified(exception)
|
.AppendDemystified(exception)
|
||||||
.AppendLine()
|
.AppendLine()
|
||||||
.Append(" --- End of inner exception stack trace ---");
|
.Append(" --- End of inner exception stack trace ---");
|
||||||
|
|||||||
@ -1,15 +1,12 @@
|
|||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
namespace Ben.Demystifier;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Ben.Demystifier
|
// Adapted from https://github.com/aspnet/Common/blob/dev/shared/Microsoft.Extensions.TypeNameHelper.Sources/TypeNameHelper.cs
|
||||||
|
public static class TypeNameHelper
|
||||||
{
|
{
|
||||||
// Adapted from https://github.com/aspnet/Common/blob/dev/shared/Microsoft.Extensions.TypeNameHelper.Sources/TypeNameHelper.cs
|
public static readonly Dictionary<Type, string> BuiltInTypeNames = new()
|
||||||
public static class TypeNameHelper
|
|
||||||
{
|
|
||||||
public static readonly Dictionary<Type, string> BuiltInTypeNames = new Dictionary<Type, string>
|
|
||||||
{
|
{
|
||||||
{ typeof(void), "void" },
|
{ typeof(void), "void" },
|
||||||
{ typeof(bool), "bool" },
|
{ typeof(bool), "bool" },
|
||||||
@ -29,7 +26,7 @@ namespace Ben.Demystifier
|
|||||||
{ typeof(ushort), "ushort" }
|
{ typeof(ushort), "ushort" }
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly Dictionary<string, string> FSharpTypeNames = new Dictionary<string, string>
|
public static readonly Dictionary<string, string> FSharpTypeNames = new()
|
||||||
{
|
{
|
||||||
{ "Unit", "void" },
|
{ "Unit", "void" },
|
||||||
{ "FSharpOption", "Option" },
|
{ "FSharpOption", "Option" },
|
||||||
@ -41,7 +38,7 @@ namespace Ben.Demystifier
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pretty print a type name.
|
/// Pretty print a type name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">The <see cref="Type"/>.</param>
|
/// <param name="type">The <see cref="Type" />.</param>
|
||||||
/// <param name="fullName"><c>true</c> to print a fully qualified name.</param>
|
/// <param name="fullName"><c>true</c> to print a fully qualified name.</param>
|
||||||
/// <param name="includeGenericParameterNames"><c>true</c> to include generic parameter names.</param>
|
/// <param name="includeGenericParameterNames"><c>true</c> to include generic parameter names.</param>
|
||||||
/// <returns>The pretty printed type name.</returns>
|
/// <returns>The pretty printed type name.</returns>
|
||||||
@ -52,7 +49,8 @@ namespace Ben.Demystifier
|
|||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StringBuilder AppendTypeDisplayName(this StringBuilder builder, Type type, bool fullName = true, bool includeGenericParameterNames = false)
|
public static StringBuilder AppendTypeDisplayName(this StringBuilder builder, Type type, bool fullName = true,
|
||||||
|
bool includeGenericParameterNames = false)
|
||||||
{
|
{
|
||||||
ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames));
|
ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames));
|
||||||
return builder;
|
return builder;
|
||||||
@ -63,14 +61,11 @@ namespace Ben.Demystifier
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static string GetTypeNameForGenericType(Type type)
|
public static string GetTypeNameForGenericType(Type type)
|
||||||
{
|
{
|
||||||
if (!type.IsGenericType)
|
if (!type.IsGenericType) throw new ArgumentException("The given type should be generic", nameof(type));
|
||||||
{
|
|
||||||
throw new ArgumentException("The given type should be generic", nameof(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
var genericPartIndex = type.Name.IndexOf('`');
|
var genericPartIndex = type.Name.IndexOf('`');
|
||||||
|
|
||||||
return (genericPartIndex >= 0) ? type.Name.Substring(0, genericPartIndex) : type.Name;
|
return genericPartIndex >= 0 ? type.Name.Substring(0, genericPartIndex) : type.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ProcessType(StringBuilder builder, Type type, DisplayNameOptions options)
|
private static void ProcessType(StringBuilder builder, Type type, DisplayNameOptions options)
|
||||||
@ -78,7 +73,7 @@ namespace Ben.Demystifier
|
|||||||
if (type.IsGenericType)
|
if (type.IsGenericType)
|
||||||
{
|
{
|
||||||
var underlyingType = Nullable.GetUnderlyingType(type);
|
var underlyingType = Nullable.GetUnderlyingType(type);
|
||||||
if (underlyingType != null)
|
if (underlyingType is not null)
|
||||||
{
|
{
|
||||||
ProcessType(builder, underlyingType, options);
|
ProcessType(builder, underlyingType, options);
|
||||||
builder.Append('?');
|
builder.Append('?');
|
||||||
@ -108,10 +103,7 @@ namespace Ben.Demystifier
|
|||||||
}
|
}
|
||||||
else if (type.IsGenericParameter)
|
else if (type.IsGenericParameter)
|
||||||
{
|
{
|
||||||
if (options.IncludeGenericParameterNames)
|
if (options.IncludeGenericParameterNames) builder.Append(type.Name);
|
||||||
{
|
|
||||||
builder.Append(type.Name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -123,12 +115,8 @@ namespace Ben.Demystifier
|
|||||||
{
|
{
|
||||||
var innerType = type;
|
var innerType = type;
|
||||||
while (innerType.IsArray)
|
while (innerType.IsArray)
|
||||||
{
|
|
||||||
if (innerType.GetElementType() is { } inner)
|
if (innerType.GetElementType() is { } inner)
|
||||||
{
|
|
||||||
innerType = inner;
|
innerType = inner;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessType(builder, innerType, options);
|
ProcessType(builder, innerType, options);
|
||||||
|
|
||||||
@ -137,21 +125,16 @@ namespace Ben.Demystifier
|
|||||||
builder.Append('[');
|
builder.Append('[');
|
||||||
builder.Append(',', type.GetArrayRank() - 1);
|
builder.Append(',', type.GetArrayRank() - 1);
|
||||||
builder.Append(']');
|
builder.Append(']');
|
||||||
if (type.GetElementType() is not { } elementType)
|
if (type.GetElementType() is not { } elementType) break;
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
type = elementType;
|
type = elementType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ProcessGenericType(StringBuilder builder, Type type, Type[] genericArguments, int length, DisplayNameOptions options)
|
private static void ProcessGenericType(StringBuilder builder, Type type, Type[] genericArguments, int length,
|
||||||
|
DisplayNameOptions options)
|
||||||
{
|
{
|
||||||
var offset = 0;
|
var offset = 0;
|
||||||
if (type.IsNested && type.DeclaringType is not null)
|
if (type.IsNested && type.DeclaringType is not null) offset = type.DeclaringType.GetGenericArguments().Length;
|
||||||
{
|
|
||||||
offset = type.DeclaringType.GetGenericArguments().Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.FullName)
|
if (options.FullName)
|
||||||
{
|
{
|
||||||
@ -176,29 +159,21 @@ namespace Ben.Demystifier
|
|||||||
|
|
||||||
if (type.Assembly.ManifestModule.Name == "FSharp.Core.dll"
|
if (type.Assembly.ManifestModule.Name == "FSharp.Core.dll"
|
||||||
&& FSharpTypeNames.TryGetValue(type.Name, out var builtInName))
|
&& FSharpTypeNames.TryGetValue(type.Name, out var builtInName))
|
||||||
{
|
|
||||||
builder.Append(builtInName);
|
builder.Append(builtInName);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
builder.Append(type.Name, 0, genericPartIndex);
|
builder.Append(type.Name, 0, genericPartIndex);
|
||||||
}
|
|
||||||
|
|
||||||
builder.Append('<');
|
builder.Append('<');
|
||||||
for (var i = offset; i < length; i++)
|
for (var i = offset; i < length; i++)
|
||||||
{
|
{
|
||||||
ProcessType(builder, genericArguments[i], options);
|
ProcessType(builder, genericArguments[i], options);
|
||||||
if (i + 1 == length)
|
if (i + 1 == length) continue;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.Append(',');
|
builder.Append(',');
|
||||||
if (options.IncludeGenericParameterNames || !genericArguments[i + 1].IsGenericParameter)
|
if (options.IncludeGenericParameterNames || !genericArguments[i + 1].IsGenericParameter)
|
||||||
{
|
|
||||||
builder.Append(' ');
|
builder.Append(' ');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
builder.Append('>');
|
builder.Append('>');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,5 +189,4 @@ namespace Ben.Demystifier
|
|||||||
|
|
||||||
public bool IncludeGenericParameterNames { get; }
|
public bool IncludeGenericParameterNames { get; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,19 +1,19 @@
|
|||||||
// Copyright (c) Ben A Adams. All rights reserved.
|
// Copyright (c) Ben A Adams. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Ben.Demystifier.Internal;
|
using Ben.Demystifier.Internal;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Ben.Demystifier
|
namespace Ben.Demystifier;
|
||||||
|
|
||||||
|
public class ValueTupleResolvedParameter : ResolvedParameter
|
||||||
{
|
{
|
||||||
public class ValueTupleResolvedParameter : ResolvedParameter
|
|
||||||
{
|
|
||||||
public IList<string> TupleNames { get; }
|
|
||||||
|
|
||||||
public ValueTupleResolvedParameter(Type resolvedType, IList<string> tupleNames)
|
public ValueTupleResolvedParameter(Type resolvedType, IList<string> tupleNames)
|
||||||
: base(resolvedType)
|
: base(resolvedType)
|
||||||
=> TupleNames = tupleNames;
|
{
|
||||||
|
TupleNames = tupleNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IList<string> TupleNames { get; }
|
||||||
|
|
||||||
protected override void AppendTypeName(StringBuilder sb)
|
protected override void AppendTypeName(StringBuilder sb)
|
||||||
{
|
{
|
||||||
@ -36,33 +36,23 @@ namespace Ben.Demystifier
|
|||||||
|
|
||||||
private void AppendValueTupleParameterName(StringBuilder sb, Type parameterType)
|
private void AppendValueTupleParameterName(StringBuilder sb, Type parameterType)
|
||||||
{
|
{
|
||||||
sb.Append("(");
|
sb.Append('(');
|
||||||
var args = parameterType.GetGenericArguments();
|
var args = parameterType.GetGenericArguments();
|
||||||
for (var i = 0; i < args.Length; i++)
|
for (var i = 0; i < args.Length; i++)
|
||||||
{
|
{
|
||||||
if (i > 0)
|
if (i > 0) sb.Append(", ");
|
||||||
{
|
|
||||||
sb.Append(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.AppendTypeDisplayName(args[i], fullName: false, includeGenericParameterNames: true);
|
sb.AppendTypeDisplayName(args[i], false, true);
|
||||||
|
|
||||||
if (i >= TupleNames.Count)
|
if (i >= TupleNames.Count) continue;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var argName = TupleNames[i];
|
var argName = TupleNames[i];
|
||||||
if (argName == null)
|
if (argName is null) continue;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append(" ");
|
sb.Append(' ');
|
||||||
sb.Append(argName);
|
sb.Append(argName);
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.Append(")");
|
sb.Append(')');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user