mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 00:42:51 +08:00
Simplify stack frame parsing
This is the natural extension of #513 which already removed a lot of regex parsing. The `ExceptionParser` class is renamed into `ExceptionConverter` because there is no more string parsing performed at all. `Exception` is converted into `ExceptionInfo` and `StackFrame` is converted into `StackFrameInfo`.
This commit is contained in:
parent
37f661a963
commit
8e762e4618
65
src/Spectre.Console/Widgets/Exceptions/ExceptionConverter.cs
Normal file
65
src/Spectre.Console/Widgets/Exceptions/ExceptionConverter.cs
Normal file
@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Spectre.Console
|
||||
{
|
||||
internal static class ExceptionConverter
|
||||
{
|
||||
public static ExceptionInfo Convert(Exception exception)
|
||||
{
|
||||
if (exception is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
var exceptionType = exception.GetType();
|
||||
var stackTrace = new StackTrace(exception, true);
|
||||
var frames = stackTrace.GetFrames().Where(f => f != null).Cast<StackFrame>().Select(Convert).ToList();
|
||||
var inner = exception.InnerException is null ? null : Convert(exception.InnerException);
|
||||
return new ExceptionInfo(exceptionType.FullName ?? exceptionType.Name, exception.Message, frames, inner);
|
||||
}
|
||||
|
||||
private static StackFrameInfo Convert(StackFrame frame)
|
||||
{
|
||||
var method = frame.GetMethod();
|
||||
if (method is null)
|
||||
{
|
||||
return new StackFrameInfo("<unknown method>", new List<(string Type, string Name)>(), null, null);
|
||||
}
|
||||
|
||||
var methodName = GetMethodName(method);
|
||||
var parameters = method.GetParameters().Select(e => (e.ParameterType.Name, e.Name ?? string.Empty)).ToList();
|
||||
var path = frame.GetFileName();
|
||||
var lineNumber = frame.GetFileLineNumber();
|
||||
return new StackFrameInfo(methodName, parameters, path, lineNumber == 0 ? null : lineNumber);
|
||||
}
|
||||
|
||||
private static string GetMethodName(MethodBase method)
|
||||
{
|
||||
var builder = new StringBuilder(256);
|
||||
|
||||
var fullName = method.DeclaringType?.FullName;
|
||||
if (fullName != null)
|
||||
{
|
||||
// See https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs#L247-L253
|
||||
builder.Append(fullName.Replace('+', '.'));
|
||||
builder.Append('.');
|
||||
}
|
||||
|
||||
builder.Append(method.Name);
|
||||
|
||||
if (method.IsGenericMethod)
|
||||
{
|
||||
builder.Append('[');
|
||||
builder.Append(string.Join(",", method.GetGenericArguments().Select(t => t.Name)));
|
||||
builder.Append(']');
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ namespace Spectre.Console
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
var info = ExceptionParser.Parse(exception);
|
||||
var info = ExceptionConverter.Convert(exception);
|
||||
|
||||
return GetException(info, settings);
|
||||
}
|
||||
|
@ -1,79 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Spectre.Console
|
||||
{
|
||||
internal static class ExceptionParser
|
||||
{
|
||||
private static readonly Regex _stackFrameRegex = new Regex(@"^\s*\w*\s(?'method'.*)\((?'params'.*)\)");
|
||||
private static readonly Regex _fullStackFrameRegex = new Regex(@"^\s*(?'at'\w*)\s(?'method'.*)\((?'params'.*)\)\s(?'in'\w*)\s(?'path'.*)\:(?'line'\w*)\s(?'linenumber'\d*)$");
|
||||
|
||||
public static ExceptionInfo Parse(Exception exception)
|
||||
{
|
||||
if (exception is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
var exceptionType = exception.GetType();
|
||||
var frames = exception.StackTrace?.SplitLines().Select(ParseStackFrame).Where(e => e != null).Cast<StackFrameInfo>().ToList() ?? new List<StackFrameInfo>();
|
||||
var inner = exception.InnerException is null ? null : Parse(exception.InnerException);
|
||||
return new ExceptionInfo(exceptionType.FullName ?? exceptionType.Name, exception.Message, frames, inner);
|
||||
}
|
||||
|
||||
private static StackFrameInfo? ParseStackFrame(string frame)
|
||||
{
|
||||
var match = _fullStackFrameRegex.Match(frame);
|
||||
if (match?.Success != true)
|
||||
{
|
||||
match = _stackFrameRegex.Match(frame);
|
||||
if (match?.Success != true)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var parameters = ParseMethodParameters(match.Groups["params"].Value);
|
||||
if (parameters == null)
|
||||
{
|
||||
// Error: Could not parse parameters
|
||||
return null;
|
||||
}
|
||||
|
||||
var method = match.Groups["method"].Value;
|
||||
var path = match.Groups["path"].Success ? match.Groups["path"].Value : null;
|
||||
|
||||
var lineNumber = (int?)null;
|
||||
if (!string.IsNullOrWhiteSpace(match.Groups["linenumber"].Value))
|
||||
{
|
||||
lineNumber = int.Parse(match.Groups["linenumber"].Value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
return new StackFrameInfo(method, parameters, path, lineNumber);
|
||||
}
|
||||
|
||||
private static List<(string Type, string Name)>? ParseMethodParameters(string parameters)
|
||||
{
|
||||
var result = new List<(string Type, string Name)>();
|
||||
foreach (var parameterPart in parameters.Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
var parameterNameIndex = parameterPart.LastIndexOf(' ');
|
||||
if (parameterNameIndex == -1)
|
||||
{
|
||||
// Error: Could not parse parameter
|
||||
return null;
|
||||
}
|
||||
|
||||
var type = parameterPart.Substring(0, parameterNameIndex);
|
||||
var name = parameterPart.Substring(parameterNameIndex + 1, parameterPart.Length - parameterNameIndex - 1);
|
||||
|
||||
result.Add((type, name));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user