887 lines
33 KiB
C#
887 lines
33 KiB
C#
// 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.
|
|
// 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.Linq;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.ExceptionServices;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using DTLib.Demystifier.Enumerable;
|
|
using DTLib.Demystifier.Internal;
|
|
|
|
namespace DTLib.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);
|
|
|
|
static EnhancedStackTrace()
|
|
{
|
|
if (AsyncIteratorStateMachineAttributeType is not null) return;
|
|
|
|
Assembly mba;
|
|
try
|
|
{
|
|
mba = Assembly.Load("Microsoft.Bcl.AsyncInterfaces");
|
|
}
|
|
catch
|
|
{
|
|
return;
|
|
}
|
|
|
|
AsyncIteratorStateMachineAttributeType =
|
|
mba.GetType("System.Runtime.CompilerServices.AsyncIteratorStateMachineAttribute", false);
|
|
}
|
|
|
|
public static List<EnhancedStackFrame> GetFrames(StackTrace stackTrace)
|
|
{
|
|
var enhancedFrames = new List<EnhancedStackFrame>();
|
|
var stackFrames = stackTrace.GetFrames();
|
|
|
|
EnhancedStackFrame? lastFrame = null;
|
|
PortablePdbReader? portablePdbReader = null;
|
|
try
|
|
{
|
|
for (var i = 0; i < stackFrames.Length; i++)
|
|
{
|
|
var frame = stackFrames[i];
|
|
var method = frame.GetMethod();
|
|
|
|
// Always show last stackFrame
|
|
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 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);
|
|
}
|
|
|
|
if (method is null)
|
|
{
|
|
// Method can't be null
|
|
continue;
|
|
}
|
|
|
|
var resolvedMethod = GetMethodDisplayString(method);
|
|
if (lastFrame?.IsEquivalent(resolvedMethod, fileName, row, column) ?? false)
|
|
{
|
|
lastFrame.IsRecursive = true;
|
|
}
|
|
else
|
|
{
|
|
var stackFrame = new EnhancedStackFrame(frame, resolvedMethod, fileName, row, column);
|
|
enhancedFrames.Add(stackFrame);
|
|
lastFrame = stackFrame;
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
portablePdbReader?.Dispose();
|
|
}
|
|
|
|
return enhancedFrames;
|
|
}
|
|
|
|
public static ResolvedMethod GetMethodDisplayString(MethodBase originMethod)
|
|
{
|
|
var method = originMethod;
|
|
|
|
var methodDisplayInfo = new ResolvedMethod
|
|
{
|
|
SubMethodBase = method
|
|
};
|
|
|
|
// Type name
|
|
var type = method.DeclaringType;
|
|
|
|
var subMethodName = method.Name;
|
|
var methodName = method.Name;
|
|
|
|
var isAsyncStateMachine = typeof(IAsyncStateMachine).IsAssignableFrom(type);
|
|
if (isAsyncStateMachine || typeof(IEnumerator).IsAssignableFrom(type))
|
|
{
|
|
methodDisplayInfo.IsAsync = isAsyncStateMachine;
|
|
|
|
// Convert StateMachine methods to correct overload +MoveNext()
|
|
if (!TryResolveStateMachineMethod(ref method, out type))
|
|
{
|
|
methodDisplayInfo.SubMethodBase = null;
|
|
subMethodName = null;
|
|
}
|
|
|
|
methodName = method.Name;
|
|
}
|
|
else if (IsFSharpAsync(method))
|
|
{
|
|
methodDisplayInfo.IsAsync = true;
|
|
methodDisplayInfo.SubMethodBase = null;
|
|
subMethodName = null;
|
|
methodName = null;
|
|
}
|
|
|
|
// Method name
|
|
methodDisplayInfo.MethodBase = method;
|
|
methodDisplayInfo.Name = methodName;
|
|
if (method.Name.IndexOf("<") >= 0)
|
|
{
|
|
if (TryResolveGeneratedName(ref method, out type, out methodName, out subMethodName, out var kind,
|
|
out var ordinal))
|
|
{
|
|
methodName = method.Name;
|
|
methodDisplayInfo.MethodBase = method;
|
|
methodDisplayInfo.Name = methodName;
|
|
methodDisplayInfo.Ordinal = ordinal;
|
|
}
|
|
else
|
|
{
|
|
methodDisplayInfo.MethodBase = null;
|
|
}
|
|
|
|
methodDisplayInfo.IsLambda = (kind == GeneratedNameKind.LambdaMethod);
|
|
|
|
if (methodDisplayInfo.IsLambda && type is not null)
|
|
{
|
|
if (methodName == ".cctor")
|
|
{
|
|
if (type.IsGenericTypeDefinition && !type.IsConstructedGenericType)
|
|
{
|
|
// TODO: diagnose type's generic type arguments from frame's "this" or something
|
|
}
|
|
else
|
|
{
|
|
var fields =
|
|
type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
|
foreach (var field in fields)
|
|
{
|
|
var value = field.GetValue(field);
|
|
if (value is Delegate d && d.Target is not null)
|
|
{
|
|
if (ReferenceEquals(d.Method, originMethod) &&
|
|
d.Target.ToString() == originMethod.DeclaringType?.ToString())
|
|
{
|
|
methodDisplayInfo.Name = field.Name;
|
|
methodDisplayInfo.IsLambda = false;
|
|
method = originMethod;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (subMethodName != methodName)
|
|
{
|
|
methodDisplayInfo.SubMethod = subMethodName;
|
|
}
|
|
|
|
// ResolveStateMachineMethod may have set declaringType to null
|
|
if (type is not null)
|
|
{
|
|
methodDisplayInfo.DeclaringType = type;
|
|
}
|
|
|
|
if (method is MethodInfo mi)
|
|
{
|
|
if (mi.ReturnParameter is not null)
|
|
methodDisplayInfo.ReturnParameter = GetParameter(mi.ReturnParameter);
|
|
else if (mi.ReturnType is not null)
|
|
methodDisplayInfo.ReturnParameter = new ResolvedParameter(mi.ReturnType)
|
|
{
|
|
Prefix = "",
|
|
Name = ""
|
|
};
|
|
}
|
|
|
|
if (method.IsGenericMethod)
|
|
{
|
|
var genericArguments = method.GetGenericArguments();
|
|
var genericArgumentsString = string.Join(", ", genericArguments
|
|
.Select(arg =>
|
|
TypeNameHelper.GetTypeDisplayName(arg, fullName: false, includeGenericParameterNames: true)));
|
|
methodDisplayInfo.GenericArguments += "<" + genericArgumentsString + ">";
|
|
methodDisplayInfo.ResolvedGenericArguments = genericArguments;
|
|
}
|
|
|
|
// Method parameters
|
|
var parameters = method.GetParameters();
|
|
if (parameters.Length > 0)
|
|
{
|
|
var parameterList = new List<ResolvedParameter>(parameters.Length);
|
|
foreach (var parameter in parameters)
|
|
{
|
|
parameterList.Add(GetParameter(parameter));
|
|
}
|
|
|
|
methodDisplayInfo.Parameters = parameterList;
|
|
}
|
|
|
|
if (methodDisplayInfo.SubMethodBase == methodDisplayInfo.MethodBase)
|
|
{
|
|
methodDisplayInfo.SubMethodBase = null;
|
|
}
|
|
else if (methodDisplayInfo.SubMethodBase is not null)
|
|
{
|
|
parameters = methodDisplayInfo.SubMethodBase.GetParameters();
|
|
if (parameters.Length > 0)
|
|
{
|
|
var parameterList = new List<ResolvedParameter>(parameters.Length);
|
|
foreach (var parameter in parameters)
|
|
{
|
|
var param = GetParameter(parameter);
|
|
if (param.Name?.StartsWith("<") ?? true) continue;
|
|
|
|
parameterList.Add(param);
|
|
}
|
|
|
|
methodDisplayInfo.SubMethodParameters = parameterList;
|
|
}
|
|
}
|
|
|
|
return methodDisplayInfo;
|
|
}
|
|
|
|
private static bool IsFSharpAsync(MethodBase method)
|
|
{
|
|
if (method is MethodInfo minfo)
|
|
{
|
|
var returnType = minfo.ReturnType;
|
|
if (returnType.Namespace == "Microsoft.FSharp.Control" && returnType.Name == "FSharpAsync`1")
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
subMethodName = null;
|
|
ordinal = null;
|
|
methodName = method.Name;
|
|
|
|
var generatedName = methodName;
|
|
|
|
if (!TryParseGeneratedName(generatedName, out kind, out var openBracketOffset, out var closeBracketOffset))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
methodName = generatedName.Substring(openBracketOffset + 1, closeBracketOffset - openBracketOffset - 1);
|
|
|
|
switch (kind)
|
|
{
|
|
case GeneratedNameKind.LocalFunction:
|
|
{
|
|
var localNameStart = generatedName.IndexOf((char)kind, closeBracketOffset + 1);
|
|
if (localNameStart < 0) break;
|
|
localNameStart += 3;
|
|
|
|
if (localNameStart < generatedName.Length)
|
|
{
|
|
var localNameEnd = generatedName.IndexOf("|", localNameStart);
|
|
if (localNameEnd > 0)
|
|
{
|
|
subMethodName = generatedName.Substring(localNameStart, localNameEnd - localNameStart);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case GeneratedNameKind.LambdaMethod:
|
|
subMethodName = "";
|
|
break;
|
|
}
|
|
|
|
var dt = method.DeclaringType;
|
|
if (dt is null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var matchHint = GetMatchHint(kind, method);
|
|
|
|
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 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;
|
|
|
|
for (var i = 0; i < 10; i++)
|
|
{
|
|
dt = dt.DeclaringType;
|
|
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;
|
|
|
|
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);
|
|
foreach (var cctor in candidateConstructors)
|
|
{
|
|
method = cctor;
|
|
type = dt;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (candidateMethod.GetMethodBody() is not { } methodBody)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (kind == GeneratedNameKind.LambdaMethod)
|
|
{
|
|
foreach (var v in EnumerableIList.Create(methodBody.LocalVariables))
|
|
{
|
|
if (v.LocalType == type)
|
|
{
|
|
GetOrdinal(method, ref ordinal);
|
|
}
|
|
|
|
method = candidateMethod;
|
|
type = method.DeclaringType;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
var rawIl = methodBody.GetILAsByteArray();
|
|
if (rawIl is null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var reader = new ILReader(rawIl);
|
|
while (reader.Read(candidateMethod))
|
|
{
|
|
if (reader.Operand is MethodBase mb)
|
|
{
|
|
if (method == mb || matchHint is not null && method.Name.Contains(matchHint))
|
|
{
|
|
if (kind == GeneratedNameKind.LambdaMethod)
|
|
{
|
|
GetOrdinal(method, ref ordinal);
|
|
}
|
|
|
|
method = candidateMethod;
|
|
type = method.DeclaringType;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// https://github.com/benaadams/Demystifier/issues/32
|
|
// Skip methods where il can't be interpreted
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static void GetOrdinal(MethodBase method, ref int? ordinal)
|
|
{
|
|
var lamdaStart = method.Name.IndexOf((char)GeneratedNameKind.LambdaMethod + "__") + 3;
|
|
if (lamdaStart > 3)
|
|
{
|
|
var secondStart = method.Name.IndexOf("_", lamdaStart) + 1;
|
|
if (secondStart > 0)
|
|
{
|
|
lamdaStart = secondStart;
|
|
}
|
|
|
|
if (!int.TryParse(method.Name.Substring(lamdaStart), out var foundOrdinal))
|
|
{
|
|
ordinal = null;
|
|
return;
|
|
}
|
|
|
|
ordinal = foundOrdinal;
|
|
|
|
var methods = method.DeclaringType?.GetMethods(BindingFlags.Public | BindingFlags.NonPublic |
|
|
BindingFlags.Static | BindingFlags.Instance |
|
|
BindingFlags.DeclaredOnly);
|
|
|
|
var count = 0;
|
|
if (methods is not null)
|
|
{
|
|
var startName = method.Name.Substring(0, lamdaStart);
|
|
foreach (var m in methods)
|
|
{
|
|
if (m.Name.Length > lamdaStart && m.Name.StartsWith(startName))
|
|
{
|
|
count++;
|
|
|
|
if (count > 1)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (count <= 1)
|
|
{
|
|
ordinal = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
static string? GetMatchHint(GeneratedNameKind kind, MethodBase method)
|
|
{
|
|
var methodName = method.Name;
|
|
|
|
switch (kind)
|
|
{
|
|
case GeneratedNameKind.LocalFunction:
|
|
var start = methodName.IndexOf("|");
|
|
if (start < 1) return null;
|
|
var end = methodName.IndexOf("_", start) + 1;
|
|
if (end <= start) return null;
|
|
|
|
return methodName.Substring(start, end - start);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// Parse the generated name. Returns true for names of the form
|
|
// [CS$]<[middle]>c[__[suffix]] where [CS$] is included for certain
|
|
// generated names, where [middle] and [__[suffix]] are optional,
|
|
// and where c is a single character in [1-9a-z]
|
|
// (csharp\LanguageAnalysis\LIB\SpecialName.cpp).
|
|
internal static bool TryParseGeneratedName(
|
|
string name,
|
|
out GeneratedNameKind kind,
|
|
out int openBracketOffset,
|
|
out int closeBracketOffset)
|
|
{
|
|
openBracketOffset = -1;
|
|
if (name.StartsWith("CS$<", StringComparison.Ordinal))
|
|
openBracketOffset = 3;
|
|
else if (name.StartsWith("<", StringComparison.Ordinal)) openBracketOffset = 0;
|
|
|
|
if (openBracketOffset >= 0)
|
|
{
|
|
closeBracketOffset = IndexOfBalancedParenthesis(name, openBracketOffset, '>');
|
|
if (closeBracketOffset >= 0 && closeBracketOffset + 1 < name.Length)
|
|
{
|
|
int c = name[closeBracketOffset + 1];
|
|
if ((c >= '1' && c <= '9') || (c >= 'a' && c <= 'z')) // Note '0' is not special.
|
|
{
|
|
kind = (GeneratedNameKind)c;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
kind = GeneratedNameKind.None;
|
|
openBracketOffset = -1;
|
|
closeBracketOffset = -1;
|
|
return false;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
private static ResolvedParameter GetParameter(ParameterInfo parameter)
|
|
{
|
|
var prefix = GetPrefix(parameter);
|
|
var parameterType = parameter.ParameterType;
|
|
|
|
if (parameterType.IsGenericType)
|
|
{
|
|
var customAttribs = parameter.GetCustomAttributes(inherit: false);
|
|
|
|
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;
|
|
|
|
return new ResolvedParameter(parameterType)
|
|
{
|
|
Prefix = prefix,
|
|
Name = parameter.Name,
|
|
IsDynamicType = parameter.IsDefined(typeof(DynamicAttribute), false)
|
|
};
|
|
}
|
|
|
|
private static ResolvedParameter GetValueTupleParameter(IList<string> tupleNames, string prefix, string? name,
|
|
Type parameterType)
|
|
{
|
|
return new ValueTupleResolvedParameter(parameterType, tupleNames)
|
|
{
|
|
Prefix = prefix,
|
|
Name = name
|
|
};
|
|
}
|
|
|
|
private static bool ShowInStackTrace(MethodBase method)
|
|
{
|
|
// Since .NET 5:
|
|
// https://github.com/dotnet/runtime/blob/7c18d4d6488dab82124d475d1199def01d1d252c/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs#L348-L361
|
|
if ((method.MethodImplementationFlags & MethodImplAttributes.AggressiveInlining) != 0)
|
|
{
|
|
// Aggressive Inlines won't normally show in the StackTrace; however for Tier0 Jit and
|
|
// cross-assembly AoT/R2R these inlines will be blocked until Tier1 Jit re-Jits
|
|
// them when they will inline. We don't show them in the StackTrace to bring consistency
|
|
// between this first-pass asm and fully optimized asm.
|
|
return false;
|
|
}
|
|
|
|
// Since .NET Core 2:
|
|
if (StackTraceHiddenAttributeType is not null)
|
|
{
|
|
// Don't show any methods marked with the StackTraceHiddenAttribute
|
|
// https://github.com/dotnet/coreclr/pull/14652
|
|
if (IsStackTraceHidden(method))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
var type = method.DeclaringType;
|
|
|
|
if (type is null)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Since .NET Core 2:
|
|
if (StackTraceHiddenAttributeType is not null)
|
|
{
|
|
// Don't show any methods marked with the StackTraceHiddenAttribute
|
|
// https://github.com/dotnet/coreclr/pull/14652
|
|
if (IsStackTraceHidden(type))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (type == typeof(Task<>) && method.Name == "InnerInvoke")
|
|
{
|
|
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"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
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"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch (method.Name)
|
|
{
|
|
case "ExecuteWithThreadLocal":
|
|
case "Execute":
|
|
case "ExecutionContextCallback":
|
|
case "ExecuteEntry":
|
|
case "InnerInvoke":
|
|
case "ExecuteEntryUnsafe":
|
|
case "ExecuteFromThreadPool":
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (type == typeof(ExecutionContext))
|
|
{
|
|
if (method.Name.Contains(".cctor"))
|
|
return false;
|
|
|
|
switch (method.Name)
|
|
{
|
|
case "RunInternal":
|
|
case "Run":
|
|
case "RunFromThreadPoolDispatchLoop":
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (type.Namespace == "Microsoft.FSharp.Control")
|
|
{
|
|
switch (type.Name)
|
|
{
|
|
case "AsyncPrimitives":
|
|
case "Trampoline":
|
|
return false;
|
|
case var typeName when type.IsGenericType:
|
|
{
|
|
if (typeName == "AsyncResult`1") return false;
|
|
else break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (type.Namespace == "Ply")
|
|
{
|
|
if (type.DeclaringType?.Name == "TplPrimitives")
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Fallbacks for runtime pre-StackTraceHiddenAttribute
|
|
if (type == typeof(ExceptionDispatchInfo) && method.Name == "Throw")
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (type == typeof(TaskAwaiter) ||
|
|
type == typeof(TaskAwaiter<>) ||
|
|
type == typeof(ValueTaskAwaiter) ||
|
|
type == typeof(ValueTaskAwaiter<>) ||
|
|
type == typeof(ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter) ||
|
|
type == typeof(ConfiguredValueTaskAwaitable<>.ConfiguredValueTaskAwaiter) ||
|
|
type == typeof(ConfiguredTaskAwaitable.ConfiguredTaskAwaiter) ||
|
|
type == typeof(ConfiguredTaskAwaitable<>.ConfiguredTaskAwaiter))
|
|
{
|
|
switch (method.Name)
|
|
{
|
|
case "HandleNonSuccessAndDebuggerNotification":
|
|
case "ThrowForNonSuccess":
|
|
case "ValidateEnd":
|
|
case "GetResult":
|
|
return false;
|
|
}
|
|
}
|
|
else if (type.FullName == "System.ThrowHelper")
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
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
|
|
{
|
|
attributes = EnumerableIList.Create(memberInfo.GetCustomAttributesData());
|
|
}
|
|
catch (NotImplementedException)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (var i = 0; i < attributes.Count; i++)
|
|
{
|
|
var attribute = attributes[i];
|
|
// reflection-only attribute, match on name
|
|
if (attribute.AttributeType.FullName == StackTraceHiddenAttributeType?.FullName)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// https://github.com/dotnet/runtime/blob/c985bdcec2a9190e733bcada413a193d5ff60c0d/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs#L375-L430
|
|
private static bool TryResolveStateMachineMethod(ref MethodBase method, out Type declaringType)
|
|
{
|
|
if (method.DeclaringType is null)
|
|
{
|
|
declaringType = null!;
|
|
return false;
|
|
}
|
|
|
|
declaringType = method.DeclaringType;
|
|
|
|
var parentType = declaringType.DeclaringType;
|
|
if (parentType is null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var methods = parentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static |
|
|
BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
|
if (methods is null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
foreach (var candidateMethod in methods)
|
|
{
|
|
var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>(inherit: false);
|
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalse - Taken from CoreFX
|
|
if (attributes is null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bool foundAttribute = false, foundIteratorAttribute = false;
|
|
foreach (var asma in attributes)
|
|
{
|
|
if (asma.StateMachineType == declaringType)
|
|
{
|
|
foundAttribute = true;
|
|
foundIteratorAttribute |= asma is IteratorStateMachineAttribute
|
|
|| AsyncIteratorStateMachineAttributeType is not null
|
|
&& AsyncIteratorStateMachineAttributeType.IsInstanceOfType(asma);
|
|
}
|
|
}
|
|
|
|
if (foundAttribute)
|
|
{
|
|
// If this is an iterator (sync or async), mark the iterator as changed, so it gets the + annotation
|
|
// of the original method. Non-iterator async state machines resolve directly to their builder methods
|
|
// so aren't marked as changed.
|
|
method = candidateMethod;
|
|
declaringType = candidateMethod.DeclaringType!;
|
|
return foundIteratorAttribute;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal enum GeneratedNameKind
|
|
{
|
|
None = 0,
|
|
|
|
// Used by EE:
|
|
ThisProxyField = '4',
|
|
HoistedLocalField = '5',
|
|
DisplayClassLocalOrField = '8',
|
|
LambdaMethod = 'b',
|
|
LambdaDisplayClass = 'c',
|
|
StateMachineType = 'd',
|
|
|
|
LocalFunction =
|
|
'g', // note collision with Deprecated_InitializerLocal, however this one is only used for method names
|
|
|
|
// Used by EnC:
|
|
AwaiterField = 'u',
|
|
HoistedSynthesizedLocalField = 's',
|
|
|
|
// Currently not parsed:
|
|
StateMachineStateField = '1',
|
|
IteratorCurrentBackingField = '2',
|
|
StateMachineParameterProxyField = '3',
|
|
ReusableHoistedLocalField = '7',
|
|
LambdaCacheField = '9',
|
|
FixedBufferField = 'e',
|
|
AnonymousType = 'f',
|
|
TransparentIdentifier = 'h',
|
|
AnonymousTypeField = 'i',
|
|
AutoPropertyBackingField = 'k',
|
|
IteratorCurrentThreadIdField = 'l',
|
|
IteratorFinallyMethod = 'm',
|
|
BaseMethodWrapper = 'n',
|
|
AsyncBuilderField = 't',
|
|
DynamicCallSiteContainerType = 'o',
|
|
DynamicCallSiteField = 'p'
|
|
}
|
|
}
|
|
} |