fixed warnings

This commit is contained in:
timerix 2022-09-26 17:12:57 +06:00
parent 5f593123de
commit ffddfa21ec
17 changed files with 1092 additions and 1379 deletions

View File

@ -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

View File

@ -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>

View File

@ -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();
}
} }

View File

@ -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',

View File

@ -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;
} }
}
} }

View File

@ -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();
} }
} }

View File

@ -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;
} }
} }

View File

@ -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();
}
} }

View File

@ -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();
} }
} }

View File

@ -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);
} }
} }

View File

@ -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();
}
}
} }

View File

@ -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)!);
} }
}
} }

View File

@ -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;
}
} }
} }

View File

@ -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);
}
} }
} }

View File

@ -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 ---");

View File

@ -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; }
} }
}
} }

View File

@ -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(')');
}
} }
} }