mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-10-31 17:15:28 +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:
		 Cédric Luthi
					Cédric Luthi
				
			
				
					committed by
					
						 Patrik Svensson
						Patrik Svensson
					
				
			
			
				
	
			
			
			 Patrik Svensson
						Patrik Svensson
					
				
			
						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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user