diff --git a/src/Ben.Demystifier/Ben.Demystifier.csproj b/src/Ben.Demystifier/Ben.Demystifier.csproj
index c1cd196..d51ad47 100644
--- a/src/Ben.Demystifier/Ben.Demystifier.csproj
+++ b/src/Ben.Demystifier/Ben.Demystifier.csproj
@@ -25,9 +25,6 @@
1.5.0
-
- 4.4.0
-
diff --git a/src/Ben.Demystifier/EnhancedStackTrace.Frames.cs b/src/Ben.Demystifier/EnhancedStackTrace.Frames.cs
index d2acf07..f83b54b 100644
--- a/src/Ben.Demystifier/EnhancedStackTrace.Frames.cs
+++ b/src/Ben.Demystifier/EnhancedStackTrace.Frames.cs
@@ -508,7 +508,8 @@ namespace System.Diagnostics
{
return "out";
}
- else if (parameterType != null && parameterType.IsByRef)
+
+ if (parameterType != null && parameterType.IsByRef)
{
var attribs = parameter.GetCustomAttributes(inherit: false);
if (attribs?.Length > 0)
@@ -549,15 +550,13 @@ namespace System.Diagnostics
{
var customAttribs = parameter.GetCustomAttributes(inherit: false);
- if ((customAttribs?.Length ?? 0) > 0)
- {
- var tupleNames = customAttribs
- .OfType().FirstOrDefault()?.TransformNames;
+ var tupleNameAttribute = customAttribs.OfType().FirstOrDefault(a => a.IsTupleElementNameAttribue());
- if (tupleNames?.Count > 0)
- {
- return GetValueTupleParameter(tupleNames, prefix, parameter.Name, parameterType);
- }
+ var tupleNames = tupleNameAttribute?.GetTransformerNames();
+
+ if (tupleNames?.Count > 0)
+ {
+ return GetValueTupleParameter(tupleNames, prefix, parameter.Name, parameterType);
}
}
diff --git a/src/Ben.Demystifier/Internal/ReflectionHelper.cs b/src/Ben.Demystifier/Internal/ReflectionHelper.cs
index 9306532..7280b05 100644
--- a/src/Ben.Demystifier/Internal/ReflectionHelper.cs
+++ b/src/Ben.Demystifier/Internal/ReflectionHelper.cs
@@ -1,8 +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.
+using System.Collections.Generic;
using System.Reflection;
-using System.Reflection.Emit;
+using System.Threading;
namespace System.Diagnostics.Internal
{
@@ -11,6 +12,8 @@ namespace System.Diagnostics.Internal
///
public static class ReflectionHelper
{
+ private static PropertyInfo tranformerNamesLazyPropertyInfo;
+
///
/// Returns true if is System.Runtime.CompilerServices.IsReadOnlyAttribute.
///
@@ -26,5 +29,40 @@ namespace System.Diagnostics.Internal
{
return type.Namespace == "System" && type.Name.Contains("ValueTuple`");
}
+
+ ///
+ /// Returns true if the given is of type TupleElementNameAttribute.
+ ///
+ ///
+ /// To avoid compile-time depencency hell with System.ValueTuple, this method uses reflection and not checks statically that
+ /// the given is TupleElementNameAttribute.
+ ///
+ public static bool IsTupleElementNameAttribue(this Attribute attribute)
+ {
+ var attributeType = attribute.GetType();
+ return attributeType.Namespace == "System.Runtime.CompilerServices" &&
+ attributeType.Name == "TupleElementNamesAttribute";
+ }
+
+ ///
+ /// Returns 'TransformNames' property value from a given .
+ ///
+ ///
+ /// To avoid compile-time depencency hell with System.ValueTuple, this method uses reflection
+ /// instead of casting the attribute to a specific type.
+ ///
+ public static IList GetTransformerNames(this Attribute attribute)
+ {
+ Debug.Assert(attribute.IsTupleElementNameAttribue());
+
+ var propertyInfo = GetTransformNamesPropertyInfo(attribute.GetType());
+ return (IList)propertyInfo.GetValue(attribute);
+ }
+
+ private static PropertyInfo GetTransformNamesPropertyInfo(Type attributeType)
+ {
+ return LazyInitializer.EnsureInitialized(ref tranformerNamesLazyPropertyInfo,
+ () => attributeType.GetProperty("TransformNames", BindingFlags.Instance | BindingFlags.Public));
+ }
}
}
diff --git a/test/Ben.Demystifier.Benchmarks/Exceptions.cs b/test/Ben.Demystifier.Benchmarks/Exceptions.cs
index 0bc8881..12417a9 100644
--- a/test/Ben.Demystifier.Benchmarks/Exceptions.cs
+++ b/test/Ben.Demystifier.Benchmarks/Exceptions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Jobs;
@@ -14,5 +15,26 @@ namespace Ben.Demystifier.Benchmarks
[Benchmark(Description = "Demystify().ToString()")]
public string Demystify() => new Exception().Demystify().ToString();
+
+ [Benchmark(Description = "(left, right).ToString()")]
+ public string ToStringForTupleBased() => GetException(() => ReturnsTuple()).ToString();
+
+ [Benchmark(Description = "(left, right).Demystify().ToString()")]
+ public string ToDemystifyForTupleBased() => GetException(() => ReturnsTuple()).Demystify().ToString();
+
+ private static Exception GetException(Action action)
+ {
+ try
+ {
+ action();
+ throw new InvalidOperationException("Should not be reachable.");
+ }
+ catch (Exception e)
+ {
+ return e;
+ }
+ }
+
+ private static List<(int left, int right)> ReturnsTuple() => throw new Exception();
}
}
diff --git a/test/Ben.Demystifier.Test/Ben.Demystifier.Test.csproj b/test/Ben.Demystifier.Test/Ben.Demystifier.Test.csproj
index 9400fa6..e75a1bd 100644
--- a/test/Ben.Demystifier.Test/Ben.Demystifier.Test.csproj
+++ b/test/Ben.Demystifier.Test/Ben.Demystifier.Test.csproj
@@ -7,6 +7,7 @@
+
diff --git a/test/Ben.Demystifier.Test/EnhancedStackTraceTests.cs b/test/Ben.Demystifier.Test/EnhancedStackTraceTests.cs
index c00acc9..a045af2 100644
--- a/test/Ben.Demystifier.Test/EnhancedStackTraceTests.cs
+++ b/test/Ben.Demystifier.Test/EnhancedStackTraceTests.cs
@@ -1,7 +1,4 @@
-using System;
-using System.Collections.Generic;
using System.Diagnostics;
-using System.Text;
using Xunit;
namespace Ben.Demystifier.Test
diff --git a/test/Ben.Demystifier.Test/ToDemystifiedStringTests.cs b/test/Ben.Demystifier.Test/ToDemystifiedStringTests.cs
index 47ff7e2..16a2ee4 100644
--- a/test/Ben.Demystifier.Test/ToDemystifiedStringTests.cs
+++ b/test/Ben.Demystifier.Test/ToDemystifiedStringTests.cs
@@ -1,7 +1,5 @@
using System;
-using System.Collections.Generic;
using System.Diagnostics;
-using System.Text;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
@@ -22,7 +20,7 @@ namespace Ben.Demystifier.Test
{
try
{
- SimpleMethodThatThrows().Wait();
+ SimpleMethodThatThrows(null).Wait();
}
catch (Exception e)
{
@@ -39,9 +37,14 @@ namespace Ben.Demystifier.Test
Assert.Equal(original, afterDemystified);
}
- async Task SimpleMethodThatThrows()
+ async Task SimpleMethodThatThrows(string value)
{
- throw new InvalidOperationException("message");
+ if (value == null)
+ {
+ throw new InvalidOperationException("message");
+ }
+
+ await Task.Yield();
}
}
}