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,7 +5,7 @@
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<Nullable>enable</Nullable>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<TargetFrameworks>netstandard2.1;netstandard2.0;net45;net6.0</TargetFrameworks>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>

View File

@ -1,17 +1,26 @@
// 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.
using System.Reflection;
namespace Ben.Demystifier;
namespace Ben.Demystifier
{
public class EnhancedStackFrame : StackFrame
{
private readonly int _colNumber;
private readonly string? _fileName;
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
{
@ -21,24 +30,11 @@ namespace Ben.Demystifier
public ResolvedMethod MethodInfo { get; }
internal EnhancedStackFrame(StackFrame stackFrame, ResolvedMethod methodInfo, string? fileName, int lineNumber, int colNumber)
: base(fileName, lineNumber, colNumber)
{
StackFrame = stackFrame;
MethodInfo = methodInfo;
_fileName = fileName;
_lineNumber = lineNumber;
_colNumber = colNumber;
}
internal bool IsEquivalent(ResolvedMethod methodInfo, string? fileName, int lineNumber, int colNumber)
{
return _lineNumber == lineNumber &&
internal bool IsEquivalent(ResolvedMethod methodInfo, string? fileName, int lineNumber, int colNumber) =>
_lineNumber == lineNumber &&
_colNumber == colNumber &&
_fileName == fileName &&
MethodInfo.IsSequentialEquivalent(methodInfo);
}
/// <summary>
/// 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.
/// </summary>
/// <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>
/// Gets the method in which the frame is executing.
/// </summary>
/// <summary>Gets the method in which the frame is executing.</summary>
/// <returns>The method in which the frame is executing.</returns>
public override MethodBase? GetMethod() => StackFrame.GetMethod();
public override MethodBase? GetMethod() => StackFrameBase.GetMethod();
/// <summary>
/// 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.
/// </summary>
/// <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>
/// Builds a readable representation of the stack trace.
/// </summary>
/// <summary>Builds a readable representation of the stack trace.</summary>
/// <returns>A readable representation of the stack trace.</returns>
public override string ToString() => MethodInfo.ToString();
}
}

View File

@ -3,28 +3,27 @@
// 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.
global using System.Collections;
global using System.Collections.Generic;
using Ben.Demystifier.Enumerable;
using Ben.Demystifier.Internal;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Ben.Demystifier.Enumerable;
using Ben.Demystifier.Internal;
namespace Ben.Demystifier
{
public partial class EnhancedStackTrace
{
private static readonly Type? StackTraceHiddenAttributeType = Type.GetType("System.Diagnostics.StackTraceHiddenAttribute", false);
private static readonly Type? AsyncIteratorStateMachineAttributeType = Type.GetType("System.Runtime.CompilerServices.AsyncIteratorStateMachineAttribute", false);
private static readonly Type? StackTraceHiddenAttributeType =
Type.GetType("System.Diagnostics.StackTraceHiddenAttribute", false);
private static readonly Type? AsyncIteratorStateMachineAttributeType =
Type.GetType("System.Runtime.CompilerServices.AsyncIteratorStateMachineAttribute", false);
static EnhancedStackTrace()
{
if (AsyncIteratorStateMachineAttributeType != null) return;
if (AsyncIteratorStateMachineAttributeType is not null) return;
Assembly mba;
try
@ -36,32 +35,15 @@ namespace Ben.Demystifier
return;
}
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);
AsyncIteratorStateMachineAttributeType =
mba.GetType("System.Runtime.CompilerServices.AsyncIteratorStateMachineAttribute", false);
}
public static List<EnhancedStackFrame> GetFrames(StackTrace stackTrace)
{
var frames = new List<EnhancedStackFrame>();
var enhancedFrames = new List<EnhancedStackFrame>();
var stackFrames = stackTrace.GetFrames();
if (stackFrames == null)
{
return frames;
}
EnhancedStackFrame? lastFrame = null;
PortablePdbReader? portablePdbReader = null;
try
@ -69,27 +51,22 @@ namespace Ben.Demystifier
for (var i = 0; i < stackFrames.Length; i++)
{
var frame = stackFrames[i];
if (frame is null)
{
continue;
}
var method = frame.GetMethod();
// 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;
}
var fileName = frame.GetFileName();
var row = frame.GetFileLineNumber();
var column = frame.GetFileColumnNumber();
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
// 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)
@ -106,7 +83,7 @@ namespace Ben.Demystifier
else
{
var stackFrame = new EnhancedStackFrame(frame, resolvedMethod, fileName, row, column);
frames.Add(stackFrame);
enhancedFrames.Add(stackFrame);
lastFrame = stackFrame;
}
}
@ -116,7 +93,7 @@ namespace Ben.Demystifier
portablePdbReader?.Dispose();
}
return frames;
return enhancedFrames;
}
public static ResolvedMethod GetMethodDisplayString(MethodBase originMethod)
@ -161,7 +138,8 @@ namespace Ben.Demystifier
methodDisplayInfo.Name = methodName;
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;
methodDisplayInfo.MethodBase = method;
@ -175,7 +153,7 @@ namespace Ben.Demystifier
methodDisplayInfo.IsLambda = (kind == GeneratedNameKind.LambdaMethod);
if (methodDisplayInfo.IsLambda && type != null)
if (methodDisplayInfo.IsLambda && type is not null)
{
if (methodName == ".cctor")
{
@ -185,7 +163,8 @@ namespace Ben.Demystifier
}
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)
{
var value = field.GetValue(field);
@ -212,33 +191,29 @@ namespace Ben.Demystifier
}
// ResolveStateMachineMethod may have set declaringType to null
if (type != null)
if (type is not null)
{
methodDisplayInfo.DeclaringType = type;
}
if (method is MethodInfo mi)
{
var returnParameter = mi.ReturnParameter;
if (returnParameter != null)
{
if (mi.ReturnParameter is not null)
methodDisplayInfo.ReturnParameter = GetParameter(mi.ReturnParameter);
}
else if (mi.ReturnType != null)
{
else if (mi.ReturnType is not null)
methodDisplayInfo.ReturnParameter = new ResolvedParameter(mi.ReturnType)
{
Prefix = "",
Name = "",
Name = ""
};
}
}
if (method.IsGenericMethod)
{
var genericArguments = method.GetGenericArguments();
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.ResolvedGenericArguments = genericArguments;
}
@ -260,7 +235,7 @@ namespace Ben.Demystifier
{
methodDisplayInfo.SubMethodBase = null;
}
else if (methodDisplayInfo.SubMethodBase != null)
else if (methodDisplayInfo.SubMethodBase is not null)
{
parameters = methodDisplayInfo.SubMethodBase.GetParameters();
if (parameters.Length > 0)
@ -295,7 +270,8 @@ namespace Ben.Demystifier
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;
type = method.DeclaringType;
@ -328,6 +304,7 @@ namespace Ben.Demystifier
subMethodName = generatedName.Substring(localNameStart, localNameEnd - localNameStart);
}
}
break;
}
case GeneratedNameKind.LambdaMethod:
@ -336,7 +313,7 @@ namespace Ben.Demystifier
}
var dt = method.DeclaringType;
if (dt == null)
if (dt is null)
{
return false;
}
@ -345,30 +322,42 @@ namespace Ben.Demystifier
var matchName = methodName;
var candidateMethods = 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 candidateMethods =
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);
if (TryResolveSourceMethod(candidateConstructors, 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);
if (TryResolveSourceMethod(candidateConstructors, kind, matchHint, ref method, ref type, out ordinal))
return true;
const int MaxResolveDepth = 10;
for (var i = 0; i < MaxResolveDepth; i++)
for (var i = 0; i < 10; i++)
{
dt = dt.DeclaringType;
if (dt == null)
{
if (dt is null)
return false;
}
candidateMethods = 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;
candidateMethods =
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);
if (TryResolveSourceMethod(candidateConstructors, 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);
if (TryResolveSourceMethod(candidateConstructors, kind, matchHint, ref method, ref type, out ordinal))
return true;
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)
{
method = cctor;
@ -381,7 +370,8 @@ namespace Ben.Demystifier
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;
foreach (var candidateMethod in candidateMethods)
@ -390,6 +380,7 @@ namespace Ben.Demystifier
{
continue;
}
if (kind == GeneratedNameKind.LambdaMethod)
{
foreach (var v in EnumerableIList.Create(methodBody.LocalVariables))
@ -397,8 +388,8 @@ namespace Ben.Demystifier
if (v.LocalType == type)
{
GetOrdinal(method, ref ordinal);
}
method = candidateMethod;
type = method.DeclaringType;
return true;
@ -412,12 +403,13 @@ namespace Ben.Demystifier
{
continue;
}
var reader = new ILReader(rawIl);
while (reader.Read(candidateMethod))
{
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)
{
@ -460,10 +452,12 @@ namespace Ben.Demystifier
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;
if (methods != null)
if (methods is not null)
{
var startName = method.Name.Substring(0, lamdaStart);
foreach (var m in methods)
@ -501,6 +495,7 @@ namespace Ben.Demystifier
return methodName.Substring(start, end - start);
}
return null;
}
@ -517,13 +512,8 @@ namespace Ben.Demystifier
{
openBracketOffset = -1;
if (name.StartsWith("CS$<", StringComparison.Ordinal))
{
openBracketOffset = 3;
}
else if (name.StartsWith("<", StringComparison.Ordinal))
{
openBracketOffset = 0;
}
else if (name.StartsWith("<", StringComparison.Ordinal)) openBracketOffset = 0;
if (openBracketOffset >= 0)
{
@ -549,50 +539,32 @@ namespace Ben.Demystifier
private static int IndexOfBalancedParenthesis(string str, int openingOffset, char closing)
{
var opening = str[openingOffset];
var depth = 1;
for (var i = openingOffset + 1; i < str.Length; i++)
{
var c = str[i];
if (c == opening)
{
depth++;
}
else if (c == closing)
{
depth--;
if (depth == 0)
{
return i;
}
}
}
return -1;
}
private static string GetPrefix(ParameterInfo parameter)
{
if (Attribute.IsDefined(parameter, typeof(ParamArrayAttribute), false))
{
return "params";
}
if (parameter.IsOut)
{
return "out";
}
if (parameter.IsIn)
{
return "in";
}
if (parameter.ParameterType.IsByRef)
{
return "ref";
}
return string.Empty;
}
@ -605,20 +577,16 @@ namespace Ben.Demystifier
{
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();
if (tupleNames?.Count > 0)
{
return GetValueTupleParameter(tupleNames, prefix, parameter.Name, parameterType);
}
}
if (parameterType.IsByRef && parameterType.GetElementType() is {} elementType)
{
parameterType = elementType;
}
if (parameterType.IsByRef && parameterType.GetElementType() is { } elementType) parameterType = elementType;
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)
{
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)
{
// Since .NET 5:
@ -684,7 +620,7 @@ namespace Ben.Demystifier
}
// Since .NET Core 2:
if (StackTraceHiddenAttributeType != null)
if (StackTraceHiddenAttributeType is not null)
{
// Don't show any methods marked with the StackTraceHiddenAttribute
// https://github.com/dotnet/coreclr/pull/14652
@ -696,13 +632,13 @@ namespace Ben.Demystifier
var type = method.DeclaringType;
if (type == null)
if (type is null)
{
return true;
}
// Since .NET Core 2:
if (StackTraceHiddenAttributeType != null)
if (StackTraceHiddenAttributeType is not null)
{
// Don't show any methods marked with the StackTraceHiddenAttribute
// https://github.com/dotnet/coreclr/pull/14652
@ -716,18 +652,24 @@ namespace Ben.Demystifier
{
return false;
}
if (type == typeof(ValueTask<>) && method.Name == "get_Result")
{
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;
}
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;
}
if (type == typeof(Task) || type.DeclaringType == typeof(Task))
{
if (method.Name.Contains(".cctor"))
@ -747,12 +689,11 @@ namespace Ben.Demystifier
return false;
}
}
if (type == typeof(ExecutionContext))
{
if (method.Name.Contains(".cctor"))
{
return false;
}
switch (method.Name)
{
@ -821,9 +762,7 @@ namespace Ben.Demystifier
private static bool IsStackTraceHidden(MemberInfo memberInfo)
{
if (StackTraceHiddenAttributeType is not null && !memberInfo.Module.Assembly.ReflectionOnly)
{
return memberInfo.GetCustomAttributes(StackTraceHiddenAttributeType, false).Length != 0;
}
EnumerableIList<CustomAttributeData> attributes;
try
@ -835,8 +774,9 @@ namespace Ben.Demystifier
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
if (attribute.AttributeType.FullName == StackTraceHiddenAttributeType?.FullName)
{
@ -855,6 +795,7 @@ namespace Ben.Demystifier
declaringType = null!;
return false;
}
declaringType = method.DeclaringType;
var parentType = declaringType.DeclaringType;
@ -863,11 +804,9 @@ namespace Ben.Demystifier
return false;
}
static MethodInfo[] GetDeclaredMethods(Type type) =>
type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);
var methods = GetDeclaredMethods(parentType);
if (methods == null)
var methods = parentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static |
BindingFlags.Instance | BindingFlags.DeclaredOnly);
if (methods is null)
{
return false;
}
@ -888,7 +827,7 @@ namespace Ben.Demystifier
{
foundAttribute = true;
foundIteratorAttribute |= asma is IteratorStateMachineAttribute
|| AsyncIteratorStateMachineAttributeType != null
|| AsyncIteratorStateMachineAttributeType is not null
&& AsyncIteratorStateMachineAttributeType.IsInstanceOfType(asma);
}
}
@ -903,6 +842,7 @@ namespace Ben.Demystifier
return foundIteratorAttribute;
}
}
return false;
}
@ -917,7 +857,9 @@ namespace Ben.Demystifier
LambdaMethod = 'b',
LambdaDisplayClass = 'c',
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:
AwaiterField = 'u',

View File

@ -1,114 +1,70 @@
// 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.
using System.Collections;
using System.Collections.Generic;
using Ben.Demystifier.Enumerable;
using System.IO;
using System.Text;
namespace Ben.Demystifier;
namespace Ben.Demystifier
{
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;
// Summary:
// Initializes a new instance of the System.Diagnostics.StackTrace class using the
// provided exception object.
//
// Parameters:
// e:
// The exception object from which to construct the stack trace.
//
// Exceptions:
// T:System.ArgumentNullException:
// The parameter e is null.
/// <summary>
/// Initializes a new instance of the System.Diagnostics.StackTrace class using the
/// provided exception object
/// </summary>
public EnhancedStackTrace(Exception e)
{
if (e == null)
{
throw new ArgumentNullException(nameof(e));
_frames = GetFrames(new StackTrace(e, true));
}
_frames = GetFrames(e);
}
public EnhancedStackTrace(StackTrace stackTrace)
{
if (stackTrace == null)
{
throw new ArgumentNullException(nameof(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>
public override int FrameCount => _frames.Count;
/// <summary>
/// Gets the specified stack frame.
/// </summary>
IEnumerator<EnhancedStackFrame> IEnumerable<EnhancedStackFrame>.GetEnumerator() => _frames.GetEnumerator();
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>
/// <returns>The specified stack frame.</returns>
public override StackFrame GetFrame(int index) => _frames[index];
/// <summary>
/// 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>
/// <returns>a copy of all stack frames in the current stack trace. </returns>
public override StackFrame[] GetFrames() => _frames.ToArray();
/// <summary>
/// Builds a readable representation of the stack trace.
/// </summary>
/// <returns>A readable representation of the stack trace.</returns>
public override string ToString()
{
if (_frames == null || _frames.Count == 0) return "";
if (_frames.Count == 0) return "";
var sb = new StringBuilder();
Append(sb);
AppendTo(sb);
return sb.ToString();
}
internal void Append(StringBuilder sb)
public void AppendTo(StringBuilder sb)
{
var frames = _frames;
var count = frames.Count;
var count = _frames.Count;
for (var i = 0; i < count; i++)
{
if (i > 0)
{
sb.Append(Environment.NewLine);
}
var frame = frames[i];
if (i > 0) sb.Append(Environment.NewLine);
var frame = _frames[i];
sb.Append(" at ");
frame.MethodInfo.Append(sb);
frame.MethodInfo.AppendTo(sb);
if (frame.GetFileName() is {} fileName
// IsNullOrEmpty alone wasn't enough to disable the null warning
&& !string.IsNullOrEmpty(fileName))
var fileName = frame.GetFileName();
if (!string.IsNullOrEmpty(fileName))
{
sb.Append(" in ");
sb.Append(TryGetFullPath(fileName));
}
var lineNo = frame.GetFileLineNumber();
@ -120,10 +76,6 @@ namespace Ben.Demystifier
}
}
EnumerableIList<EnhancedStackFrame> GetEnumerator() => EnumerableIList.Create(_frames);
IEnumerator<EnhancedStackFrame> IEnumerable<EnhancedStackFrame>.GetEnumerator() => _frames.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _frames.GetEnumerator();
/// <summary>
/// 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.
@ -131,11 +83,7 @@ namespace Ben.Demystifier
public static string TryGetFullPath(string filePath)
{
if (Uri.TryCreate(filePath, UriKind.Absolute, out var uri) && uri.IsFile)
{
return Uri.UnescapeDataString(uri.AbsolutePath);
}
return filePath;
}
}
}

View File

@ -1,11 +1,14 @@
// 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.
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 static EnumerableIList<T> Create<T>(IList<T> list)
{
return new(list);
}
}
public struct EnumerableIList<T> : IEnumerableIList<T>, IList<T>
@ -14,11 +17,11 @@ namespace Ben.Demystifier.Enumerable
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;
@ -26,7 +29,11 @@ namespace Ben.Demystifier.Enumerable
// IList pass through
/// <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 />
public int Count => _list.Count;
@ -58,8 +65,13 @@ namespace Ben.Demystifier.Enumerable
/// <inheritdoc />
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,8 +1,8 @@
// 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.
namespace Ben.Demystifier.Enumerable
{
namespace Ben.Demystifier.Enumerable;
public struct EnumeratorIList<T> : IEnumerator<T>
{
private readonly IList<T> _list;
@ -23,8 +23,13 @@ namespace Ben.Demystifier.Enumerable
return _index < (_list?.Count ?? 0);
}
public void Dispose() { }
public void Dispose()
{ }
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.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Ben.Demystifier.Enumerable
{
interface IEnumerableIList<T> : IEnumerable<T>
namespace Ben.Demystifier.Enumerable;
internal interface IEnumerableIList<T> : IEnumerable<T>
{
new EnumeratorIList<T> GetEnumerator();
}
}

View File

@ -1,25 +1,31 @@
// 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.
global using System.Diagnostics;
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.Collections.Generic;
using Ben.Demystifier.Enumerable;
using System.Reflection;
using System.Text;
namespace Ben.Demystifier
{
namespace Ben.Demystifier;
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)
=> stackTraceString?.SetValue(exception, value);
{
StackTraceString?.SetValue(exception, value);
}
/// <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>
public static T Demystify<T>(this T exception) where T : Exception
{
@ -27,18 +33,11 @@ namespace Ben.Demystifier
{
var stackTrace = new EnhancedStackTrace(exception);
if (stackTrace.FrameCount > 0)
{
exception.SetStackTracesString(stackTrace.ToString());
}
if (stackTrace.FrameCount > 0) exception.SetStackTracesString(stackTrace.ToString());
if (exception is AggregateException aggEx)
{
foreach (var ex in EnumerableIList.Create(aggEx.InnerExceptions))
{
ex.Demystify();
}
}
exception.InnerException?.Demystify();
}
@ -61,6 +60,7 @@ namespace Ben.Demystifier
/// </remarks>
[Pure]
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;
namespace Ben.Demystifier.Internal
{
namespace Ben.Demystifier.Internal;
internal class ILReader
{
private static OpCode[] singleByteOpCode;
private static OpCode[] doubleByteOpCode;
private static readonly OpCode[] singleByteOpCode;
private static readonly OpCode[] doubleByteOpCode;
private readonly byte[] _cil;
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 int MetadataToken { get; private set; }
@ -26,19 +48,19 @@ namespace Ben.Demystifier.Internal
Operand = ReadOperand(OpCode, methodInfo);
return true;
}
return false;
}
OpCode ReadOpCode()
private OpCode ReadOpCode()
{
var instruction = ReadByte();
if (instruction < 254)
return singleByteOpCode[instruction];
else
return doubleByteOpCode[ReadByte()];
}
MemberInfo? ReadOperand(OpCode code, MethodBase methodInfo)
private MemberInfo? ReadOperand(OpCode code, MethodBase methodInfo)
{
MetadataToken = 0;
int inlineLength;
@ -47,15 +69,11 @@ namespace Ben.Demystifier.Internal
case OperandType.InlineMethod:
MetadataToken = ReadInt();
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();
}
Type[]? typeArgs = null;
if (methodInfo.DeclaringType != null)
{
typeArgs = methodInfo.DeclaringType.GetGenericArguments();
}
if (methodInfo.DeclaringType is not null) typeArgs = methodInfo.DeclaringType.GetGenericArguments();
try
{
return methodInfo.Module.ResolveMember(MetadataToken, typeArgs, methodArgs);
@ -101,45 +119,27 @@ namespace Ben.Demystifier.Internal
return null;
}
for (var i = 0; i < inlineLength; i++)
{
ReadByte();
}
for (var i = 0; i < inlineLength; i++) ReadByte();
return null;
}
byte ReadByte() => _cil[ptr++];
private byte ReadByte()
{
return _cil[ptr++];
}
int ReadInt()
private int ReadInt()
{
var b1 = ReadByte();
var b2 = ReadByte();
var b3 = 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];
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);
return typeof(OpCodes).GetFields(BindingFlags.Public | BindingFlags.Static);
}
}

View File

@ -1,38 +1,37 @@
// 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.
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
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);
private readonly Dictionary<string, MetadataReaderProvider> _cache = new(StringComparer.Ordinal);
public void PopulateStackFrame(StackFrame frameInfo, MethodBase method, int IlOffset, out string fileName, out int row, out int column)
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 = "";
row = 0;
column = 0;
if (method.Module.Assembly.IsDynamic)
{
return;
}
if (method.Module.Assembly.IsDynamic) return;
var metadataReader = GetMetadataReader(method.Module.Assembly.Location);
if (metadataReader == null)
{
return;
}
if (metadataReader is null) return;
var methodToken = MetadataTokens.Handle(method.MetadataToken);
@ -48,15 +47,9 @@ namespace Ben.Demystifier.Internal
foreach (var point in sequencePoints)
{
if (point.Offset > IlOffset)
{
break;
}
if (point.Offset > IlOffset) break;
if (point.StartLine != SequencePoint.HiddenLine)
{
bestPointSoFar = point;
}
if (point.StartLine != SequencePoint.HiddenLine) bestPointSoFar = point;
}
if (bestPointSoFar.HasValue)
@ -88,10 +81,7 @@ namespace Ben.Demystifier.Internal
private static string? GetPdbPath(string assemblyPath)
{
if (string.IsNullOrEmpty(assemblyPath))
{
return null;
}
if (string.IsNullOrEmpty(assemblyPath)) return null;
if (File.Exists(assemblyPath))
{
@ -99,7 +89,6 @@ namespace Ben.Demystifier.Internal
using var peReader = new PEReader(peStream);
foreach (var entry in peReader.ReadDebugDirectory())
{
if (entry.Type == DebugDirectoryEntryType.CodeView)
{
var codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry);
@ -109,7 +98,6 @@ namespace Ben.Demystifier.Internal
: Path.Combine(peDirectory, Path.GetFileName(codeViewData.Path));
}
}
}
return null;
}
@ -122,15 +110,4 @@ namespace Ben.Demystifier.Internal
pdbStream.ReadByte() == 'J' &&
pdbStream.ReadByte() == 'B';
}
public void Dispose()
{
foreach (var entry in _cache)
{
entry.Value?.Dispose();
}
_cache.Clear();
}
}
}

View File

@ -1,12 +1,10 @@
// 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.
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
namespace Ben.Demystifier.Internal
{
namespace Ben.Demystifier.Internal;
/// <summary>
/// A helper class that contains utilities methods for dealing with reflection.
/// </summary>
@ -26,7 +24,8 @@ namespace Ben.Demystifier.Internal
/// Returns true if the given <paramref name="attribute" /> is of type <code>TupleElementNameAttribute</code>.
/// </summary>
/// <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
/// that
/// the given <paramref name="attribute" /> is <code>TupleElementNameAttribute</code>.
/// </remarks>
public static bool IsTupleElementNameAttribute(this Attribute attribute)
@ -48,10 +47,10 @@ namespace Ben.Demystifier.Internal
Debug.Assert(attribute.IsTupleElementNameAttribute());
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
return LazyInitializer.EnsureInitialized(ref transformerNamesLazyPropertyInfo,
@ -59,4 +58,3 @@ namespace Ben.Demystifier.Internal
() => attributeType.GetProperty("TransformNames", BindingFlags.Instance | BindingFlags.Public)!);
}
}
}

View File

@ -2,11 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Ben.Demystifier.Enumerable;
using System.Reflection;
using System.Text;
namespace Ben.Demystifier
{
namespace Ben.Demystifier;
public class ResolvedMethod
{
public MethodBase? MethodBase { get; set; }
@ -48,27 +46,20 @@ namespace Ben.Demystifier
SubMethod == obj.SubMethod;
}
public override string ToString() => Append(new StringBuilder()).ToString();
public override string ToString() => AppendTo(new StringBuilder()).ToString();
public StringBuilder Append(StringBuilder builder)
=> Append(builder, true);
public StringBuilder Append(StringBuilder builder, bool fullName)
public StringBuilder AppendTo(StringBuilder builder, bool fullName = true)
{
if (IsAsync)
{
builder.Append("async ");
}
if (IsAsync) builder.Append("async ");
if (ReturnParameter != null)
if (ReturnParameter is not null)
{
ReturnParameter.Append(builder);
builder.Append(" ");
builder.Append(' ');
}
if (DeclaringType != null)
if (DeclaringType is not null)
{
if (Name == ".ctor")
{
if (string.IsNullOrEmpty(SubMethod) && !IsLambda)
@ -84,65 +75,49 @@ namespace Ben.Demystifier
else
{
AppendDeclaringTypeName(builder, fullName)
.Append(".")
.Append('.')
.Append(Name);
}
}
else
{
builder.Append(Name);
}
else builder.Append(Name);
builder.Append(GenericArguments);
builder.Append("(");
if (MethodBase != null)
builder.Append('(');
if (MethodBase is not null)
{
var isFirst = true;
foreach (var param in Parameters)
{
if (isFirst)
{
isFirst = false;
}
else
{
builder.Append(", ");
}
else builder.Append(", ");
param.Append(builder);
}
}
else
{
builder.Append("?");
}
builder.Append(")");
else builder.Append('?');
builder.Append(')');
if (!string.IsNullOrEmpty(SubMethod) || IsLambda)
{
builder.Append("+");
builder.Append('+');
builder.Append(SubMethod);
builder.Append("(");
if (SubMethodBase != null)
builder.Append('(');
if (SubMethodBase is not null)
{
var isFirst = true;
foreach (var param in SubMethodParameters)
{
if (isFirst)
{
isFirst = false;
}
else
{
builder.Append(", ");
}
else builder.Append(", ");
param.Append(builder);
}
}
else
{
builder.Append("?");
}
builder.Append(")");
else builder.Append('?');
builder.Append(')');
if (IsLambda)
{
builder.Append(" => { }");
@ -151,22 +126,18 @@ namespace Ben.Demystifier
{
builder.Append(" [");
builder.Append(Ordinal);
builder.Append("]");
builder.Append(']');
}
}
}
if (RecurseCount > 0)
{
builder.Append($" x {RecurseCount + 1:0}");
}
if (RecurseCount > 0) builder.Append($" x {RecurseCount + 1:0}");
return builder;
}
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.
// 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 ResolvedParameter(Type resolvedType)
{
ResolvedType = resolvedType;
}
public string? Name { get; set; }
public Type ResolvedType { get; set; }
@ -14,9 +17,10 @@ namespace Ben.Demystifier
public string? Prefix { get; set; }
public bool IsDynamicType { get; set; }
public ResolvedParameter(Type resolvedType) => ResolvedType = resolvedType;
public override string ToString() => Append(new StringBuilder()).ToString();
public override string ToString()
{
return Append(new StringBuilder()).ToString();
}
public StringBuilder Append(StringBuilder sb)
{
@ -24,36 +28,22 @@ namespace Ben.Demystifier
return sb;
if (!string.IsNullOrEmpty(Prefix))
{
sb.Append(Prefix)
.Append(" ");
}
.Append(' ');
if (IsDynamicType)
{
sb.Append("dynamic");
}
else if (ResolvedType != null)
{
AppendTypeName(sb);
}
else
{
sb.Append("?");
}
else AppendTypeName(sb);
if (!string.IsNullOrEmpty(Name))
{
sb.Append(" ")
sb.Append(' ')
.Append(Name);
}
return sb;
}
protected virtual void AppendTypeName(StringBuilder sb)
{
sb.AppendTypeDisplayName(ResolvedType, fullName: false, includeGenericParameterNames: true);
}
sb.AppendTypeDisplayName(ResolvedType, false, true);
}
}

View File

@ -2,10 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Ben.Demystifier.Enumerable;
using System.Text;
namespace Ben.Demystifier
{
namespace Ben.Demystifier;
public static class StringBuilderExtentions
{
public static StringBuilder AppendDemystified(this StringBuilder builder, Exception exception)
@ -15,29 +14,16 @@ namespace Ben.Demystifier
var stackTrace = new EnhancedStackTrace(exception);
builder.Append(exception.GetType());
if (!string.IsNullOrEmpty(exception.Message))
{
builder.Append(": ").Append(exception.Message);
}
if (!string.IsNullOrEmpty(exception.Message)) builder.Append(": ").Append(exception.Message);
builder.Append(Environment.NewLine);
if (stackTrace.FrameCount > 0)
{
stackTrace.Append(builder);
}
if (stackTrace.FrameCount > 0) stackTrace.AppendTo(builder);
if (exception is AggregateException aggEx)
{
foreach (var ex in EnumerableIList.Create(aggEx.InnerExceptions))
{
builder.AppendInnerException(ex);
}
}
if (exception.InnerException != null)
{
builder.AppendInnerException(exception.InnerException);
}
if (exception.InnerException is not null) builder.AppendInnerException(exception.InnerException);
}
catch
{
@ -48,7 +34,8 @@ namespace Ben.Demystifier
}
private static void AppendInnerException(this StringBuilder builder, Exception exception)
=> builder.Append(" ---> ")
{
builder.Append(" ---> ")
.AppendDemystified(exception)
.AppendLine()
.Append(" --- End of inner exception stack trace ---");

View File

@ -1,15 +1,12 @@
// 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.
using System.Collections.Generic;
using System.Text;
namespace Ben.Demystifier;
namespace Ben.Demystifier
{
// Adapted from https://github.com/aspnet/Common/blob/dev/shared/Microsoft.Extensions.TypeNameHelper.Sources/TypeNameHelper.cs
public static class TypeNameHelper
{
public static readonly Dictionary<Type, string> BuiltInTypeNames = new Dictionary<Type, string>
public static readonly Dictionary<Type, string> BuiltInTypeNames = new()
{
{ typeof(void), "void" },
{ typeof(bool), "bool" },
@ -29,7 +26,7 @@ namespace Ben.Demystifier
{ typeof(ushort), "ushort" }
};
public static readonly Dictionary<string, string> FSharpTypeNames = new Dictionary<string, string>
public static readonly Dictionary<string, string> FSharpTypeNames = new()
{
{ "Unit", "void" },
{ "FSharpOption", "Option" },
@ -52,7 +49,8 @@ namespace Ben.Demystifier
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));
return builder;
@ -63,14 +61,11 @@ namespace Ben.Demystifier
/// </summary>
public static string GetTypeNameForGenericType(Type type)
{
if (!type.IsGenericType)
{
throw new ArgumentException("The given type should be generic", nameof(type));
}
if (!type.IsGenericType) throw new ArgumentException("The given type should be generic", nameof(type));
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)
@ -78,7 +73,7 @@ namespace Ben.Demystifier
if (type.IsGenericType)
{
var underlyingType = Nullable.GetUnderlyingType(type);
if (underlyingType != null)
if (underlyingType is not null)
{
ProcessType(builder, underlyingType, options);
builder.Append('?');
@ -108,10 +103,7 @@ namespace Ben.Demystifier
}
else if (type.IsGenericParameter)
{
if (options.IncludeGenericParameterNames)
{
builder.Append(type.Name);
}
if (options.IncludeGenericParameterNames) builder.Append(type.Name);
}
else
{
@ -123,12 +115,8 @@ namespace Ben.Demystifier
{
var innerType = type;
while (innerType.IsArray)
{
if (innerType.GetElementType() is { } inner)
{
innerType = inner;
}
}
ProcessType(builder, innerType, options);
@ -137,21 +125,16 @@ namespace Ben.Demystifier
builder.Append('[');
builder.Append(',', type.GetArrayRank() - 1);
builder.Append(']');
if (type.GetElementType() is not { } elementType)
{
break;
}
if (type.GetElementType() is not { } elementType) break;
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;
if (type.IsNested && type.DeclaringType is not null)
{
offset = type.DeclaringType.GetGenericArguments().Length;
}
if (type.IsNested && type.DeclaringType is not null) offset = type.DeclaringType.GetGenericArguments().Length;
if (options.FullName)
{
@ -176,29 +159,21 @@ namespace Ben.Demystifier
if (type.Assembly.ManifestModule.Name == "FSharp.Core.dll"
&& FSharpTypeNames.TryGetValue(type.Name, out var builtInName))
{
builder.Append(builtInName);
}
else
{
builder.Append(type.Name, 0, genericPartIndex);
}
builder.Append('<');
for (var i = offset; i < length; i++)
{
ProcessType(builder, genericArguments[i], options);
if (i + 1 == length)
{
continue;
}
if (i + 1 == length) continue;
builder.Append(',');
if (options.IncludeGenericParameterNames || !genericArguments[i + 1].IsGenericParameter)
{
builder.Append(' ');
}
}
builder.Append('>');
}
@ -215,4 +190,3 @@ namespace Ben.Demystifier
public bool IncludeGenericParameterNames { get; }
}
}
}

View File

@ -1,19 +1,19 @@
// 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.
using System.Collections.Generic;
using Ben.Demystifier.Internal;
using System.Text;
namespace Ben.Demystifier
{
namespace Ben.Demystifier;
public class ValueTupleResolvedParameter : ResolvedParameter
{
public IList<string> TupleNames { get; }
public ValueTupleResolvedParameter(Type resolvedType, IList<string> tupleNames)
: base(resolvedType)
=> TupleNames = tupleNames;
{
TupleNames = tupleNames;
}
public IList<string> TupleNames { get; }
protected override void AppendTypeName(StringBuilder sb)
{
@ -36,33 +36,23 @@ namespace Ben.Demystifier
private void AppendValueTupleParameterName(StringBuilder sb, Type parameterType)
{
sb.Append("(");
sb.Append('(');
var args = parameterType.GetGenericArguments();
for (var i = 0; i < args.Length; i++)
{
if (i > 0)
{
sb.Append(", ");
}
if (i > 0) sb.Append(", ");
sb.AppendTypeDisplayName(args[i], fullName: false, includeGenericParameterNames: true);
sb.AppendTypeDisplayName(args[i], false, true);
if (i >= TupleNames.Count)
{
continue;
}
if (i >= TupleNames.Count) continue;
var argName = TupleNames[i];
if (argName == null)
{
continue;
}
if (argName is null) continue;
sb.Append(" ");
sb.Append(' ');
sb.Append(argName);
}
sb.Append(")");
}
sb.Append(')');
}
}