Skip to content

Commit

Permalink
Merge pull request #57 from JSkimming/retain-exception-stack
Browse files Browse the repository at this point in the history
Preserve the stack trace of re-thrown exceptions
  • Loading branch information
JSkimming committed Sep 18, 2019
2 parents 172b157 + aefdca6 commit c7c5367
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 29 deletions.
2 changes: 1 addition & 1 deletion coverage.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
@SET test_assemblies=%~dp0test\Castle.Core.AsyncInterceptor.Tests\bin\%config%\net472\Castle.Core.AsyncInterceptor.Tests.dll
::@SET test_assemblies=%test_assemblies% %~dp0test\More.Tests\bin\%config%\net472\More.Tests.dll
@SET xunit_results=%results_path%\Xunit.Tests.html
@SET coverage_filter=+[Castle.Core.AsyncInterceptor*]* -[*.Tests]*
@SET coverage_filter=+[Castle.Core.AsyncInterceptor*]* -[Castle.Core.AsyncInterceptor*]*.NoCoverage.* -[*.Tests]*
@SET coverage_results=%results_path%\Test.Coverage.xml

@IF NOT EXIST "%results_path%" MD "%results_path%"
Expand Down
2 changes: 1 addition & 1 deletion coverage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ framework="${1-netcoreapp2.1}"
config="${2-Debug}"

include="[Castle.Core.AsyncInterceptor]*"
exclude="[*.Tests]*"
exclude="\"[Castle.Core.AsyncInterceptor]*.NoCoverage.*,[*.Tests]*\""

# Cannot use a bash solution in alpine builds https://stackoverflow.com/a/246128
#rootDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
Expand Down
11 changes: 3 additions & 8 deletions src/Castle.Core.AsyncInterceptor/AsyncInterceptorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace Castle.DynamicProxy
using System.Collections.Concurrent;
using System.Reflection;
using System.Threading.Tasks;
using Castle.DynamicProxy.NoCoverage;

/// <summary>
/// A base type for an <see cref="IAsyncInterceptor"/> to provided a simplified solution of method
Expand Down Expand Up @@ -109,10 +110,7 @@ private static void InterceptSynchronousVoid(AsyncInterceptorBase me, IInvocatio
Task.Run(() => task).GetAwaiter().GetResult();
}

if (task.IsFaulted)
{
throw task.Exception.InnerException;
}
task.RethrowIfFaulted();
}

private static void InterceptSynchronousResult<TResult>(AsyncInterceptorBase me, IInvocation invocation)
Expand All @@ -128,10 +126,7 @@ private static void InterceptSynchronousResult<TResult>(AsyncInterceptorBase me,
Task.Run(() => task).GetAwaiter().GetResult();
}

if (task.IsFaulted)
{
throw task.Exception.InnerException;
}
task.RethrowIfFaulted();
}

private static Task ProceedSynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo)
Expand Down
67 changes: 67 additions & 0 deletions src/Castle.Core.AsyncInterceptor/NoCoverage/RethrowHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) 2016 James Skimming. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

namespace Castle.DynamicProxy.NoCoverage
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;

/// <summary>
/// A helper class to re-throw exceptions and retain the stack trace.
/// </summary>
internal static class RethrowHelper
{
/// <summary>
/// Re-throws the supplied exception without losing its stack trace.
/// Prefer <c>throw;</c> where possible, this method is useful for re-throwing
/// <see cref="Exception.InnerException" /> which cannot be done with the <c>throw;</c> semantics.
/// </summary>
/// <param name="exception">The exception.</param>
public static void Rethrow(this Exception exception)
{
if (exception == null)
throw new ArgumentNullException(nameof(exception));

ExceptionDispatchInfo.Capture(exception).Throw();
}

/// <summary>
/// If the <paramref name="exception"/> is an <see cref="AggregateException"/> the
/// <paramref name="exception"/>.<see cref="Exception.InnerException"/> is re-thrown; otherwise the
/// <paramref name="exception"/> is re-thrown.
/// </summary>
/// <param name="exception">The exception.</param>
public static void RethrowInnerIfAggregate(this Exception exception)
{
if (exception == null)
throw new ArgumentNullException(nameof(exception));

switch (exception)
{
case AggregateException aggregate:
Rethrow(aggregate.InnerException);
break;
default:
Rethrow(exception);
break;
}
}

/// <summary>
/// If the <paramref name="task"/> <see cref="Task.IsFaulted"/> the inner exception is re-thrown; otherwise the
/// method is a no-op.
/// </summary>
/// <param name="task">The task.</param>
public static void RethrowIfFaulted(this Task task)
{
if (task == null)
throw new ArgumentNullException(nameof(task));

if (task.IsFaulted)
RethrowInnerIfAggregate(task.Exception);
}
}
}
Loading

0 comments on commit c7c5367

Please sign in to comment.