From 52dc07cc8dfb669ad9f7593e67aad8174ee0cc07 Mon Sep 17 00:00:00 2001 From: Dan Zwell Date: Thu, 12 Apr 2018 08:00:40 +0100 Subject: [PATCH] Rethrow exceptions instead of throwing them again When an exception is thrown again ("throw ex"), its stack will be overwritten. Instead, it should be rethrown with its original context. More info here (Figure 2): https://msdn.microsoft.com/en-us/magazine/mt620018.aspx Issue #5 --- .../AsyncAwaitUtil/Source/IEnumeratorAwaitExtensions.cs | 5 +++-- .../Plugins/AsyncAwaitUtil/Source/TaskExtensions.cs | 5 +++-- .../Assets/Plugins/UniRx/Scripts/Notification.cs | 4 +++- UnityProject/Assets/Plugins/UniRx/Scripts/Observer.cs | 9 +++++---- .../Assets/Plugins/UniRx/Scripts/Operators/Wait.cs | 3 ++- .../Plugins/UniRx/Scripts/Subjects/AsyncSubject.cs | 5 +++-- .../Plugins/UniRx/Scripts/Subjects/BehaviorSubject.cs | 3 ++- .../Scripts/UnityEngineBridge/MainThreadDispatcher.cs | 3 ++- .../UniRx/Scripts/UnityEngineBridge/Observable.Unity.cs | 5 +++-- 9 files changed, 26 insertions(+), 16 deletions(-) diff --git a/UnityProject/Assets/Plugins/AsyncAwaitUtil/Source/IEnumeratorAwaitExtensions.cs b/UnityProject/Assets/Plugins/AsyncAwaitUtil/Source/IEnumeratorAwaitExtensions.cs index a8174e4..1c5273f 100644 --- a/UnityProject/Assets/Plugins/AsyncAwaitUtil/Source/IEnumeratorAwaitExtensions.cs +++ b/UnityProject/Assets/Plugins/AsyncAwaitUtil/Source/IEnumeratorAwaitExtensions.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; using System.Text; using System.Threading; using UnityEngine; @@ -154,7 +155,7 @@ public T GetResult() if (_exception != null) { - throw _exception; + ExceptionDispatchInfo.Capture(_exception).Throw(); } return _result; @@ -202,7 +203,7 @@ public void GetResult() if (_exception != null) { - throw _exception; + ExceptionDispatchInfo.Capture(_exception).Throw(); } } diff --git a/UnityProject/Assets/Plugins/AsyncAwaitUtil/Source/TaskExtensions.cs b/UnityProject/Assets/Plugins/AsyncAwaitUtil/Source/TaskExtensions.cs index 14ed79e..ec0b189 100644 --- a/UnityProject/Assets/Plugins/AsyncAwaitUtil/Source/TaskExtensions.cs +++ b/UnityProject/Assets/Plugins/AsyncAwaitUtil/Source/TaskExtensions.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; using System.Threading.Tasks; using UnityEngine; @@ -17,7 +18,7 @@ public static IEnumerator AsIEnumerator(this Task task) if (task.IsFaulted) { - throw task.Exception; + ExceptionDispatchInfo.Capture(task.Exception).Throw(); } } @@ -31,7 +32,7 @@ public static IEnumerator AsIEnumerator(this Task task) if (task.IsFaulted) { - throw task.Exception; + ExceptionDispatchInfo.Capture(task.Exception).Throw(); } yield return task.Result; diff --git a/UnityProject/Assets/Plugins/UniRx/Scripts/Notification.cs b/UnityProject/Assets/Plugins/UniRx/Scripts/Notification.cs index 8ac00ee..30ffb0d 100644 --- a/UnityProject/Assets/Plugins/UniRx/Scripts/Notification.cs +++ b/UnityProject/Assets/Plugins/UniRx/Scripts/Notification.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.Globalization; using System.Collections.Generic; +using System.Runtime.ExceptionServices; using System; #pragma warning disable 0659 @@ -270,7 +271,8 @@ public OnErrorNotification(Exception exception) /// /// Throws the exception. /// - public override T Value { get { throw exception; } } + // Note: the 2nd "throw" is never reached: + public override T Value { get { ExceptionDispatchInfo.Capture(exception).Throw(); throw null; } } /// /// Returns the exception. diff --git a/UnityProject/Assets/Plugins/UniRx/Scripts/Observer.cs b/UnityProject/Assets/Plugins/UniRx/Scripts/Observer.cs index a47bf97..1943d50 100644 --- a/UnityProject/Assets/Plugins/UniRx/Scripts/Observer.cs +++ b/UnityProject/Assets/Plugins/UniRx/Scripts/Observer.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.ExceptionServices; using System.Threading; namespace UniRx @@ -492,7 +493,7 @@ public static IDisposable SubscribeWithState3(this internal static class Stubs { public static readonly Action Nop = () => { }; - public static readonly Action Throw = ex => { throw ex; }; + public static readonly Action Throw = ex => { ExceptionDispatchInfo.Capture(ex).Throw(); }; // marker for CatchIgnore and Catch avoid iOS AOT problem. public static IObservable CatchIgnore(Exception ex) @@ -505,19 +506,19 @@ internal static class Stubs { public static readonly Action Ignore = (T t) => { }; public static readonly Func Identity = (T t) => t; - public static readonly Action Throw = (ex, _) => { throw ex; }; + public static readonly Action Throw = (ex, _) => { ExceptionDispatchInfo.Capture(ex).Throw(); }; } internal static class Stubs { public static readonly Action Ignore = (x, y) => { }; - public static readonly Action Throw = (ex, _, __) => { throw ex; }; + public static readonly Action Throw = (ex, _, __) => { ExceptionDispatchInfo.Capture(ex).Throw(); }; } internal static class Stubs { public static readonly Action Ignore = (x, y, z) => { }; - public static readonly Action Throw = (ex, _, __, ___) => { throw ex; }; + public static readonly Action Throw = (ex, _, __, ___) => { ExceptionDispatchInfo.Capture(ex).Throw(); }; } } \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/UniRx/Scripts/Operators/Wait.cs b/UnityProject/Assets/Plugins/UniRx/Scripts/Operators/Wait.cs index 8942897..62ef2af 100644 --- a/UnityProject/Assets/Plugins/UniRx/Scripts/Operators/Wait.cs +++ b/UnityProject/Assets/Plugins/UniRx/Scripts/Operators/Wait.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.ExceptionServices; namespace UniRx.Operators { @@ -36,7 +37,7 @@ public T Run() } } - if (ex != null) throw ex; + if (ex != null) ExceptionDispatchInfo.Capture(ex).Throw(); if (!seenValue) throw new InvalidOperationException("No Elements."); return value; diff --git a/UnityProject/Assets/Plugins/UniRx/Scripts/Subjects/AsyncSubject.cs b/UnityProject/Assets/Plugins/UniRx/Scripts/Subjects/AsyncSubject.cs index 9da9b48..2a0252e 100644 --- a/UnityProject/Assets/Plugins/UniRx/Scripts/Subjects/AsyncSubject.cs +++ b/UnityProject/Assets/Plugins/UniRx/Scripts/Subjects/AsyncSubject.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.ExceptionServices; using UniRx.InternalUtil; #if (ENABLE_MONO_BLEEDING_EDGE_EDITOR || ENABLE_MONO_BLEEDING_EDGE_STANDALONE) @@ -29,7 +30,7 @@ public T Value { ThrowIfDisposed(); if (!isStopped) throw new InvalidOperationException("AsyncSubject is not completed yet"); - if (lastError != null) throw lastError; + if (lastError != null) ExceptionDispatchInfo.Capture(lastError).Throw(); return lastValue; } } @@ -315,7 +316,7 @@ public T GetResult() if (lastError != null) { - throw lastError; + ExceptionDispatchInfo.Capture(lastError).Throw(); } if (!hasValue) diff --git a/UnityProject/Assets/Plugins/UniRx/Scripts/Subjects/BehaviorSubject.cs b/UnityProject/Assets/Plugins/UniRx/Scripts/Subjects/BehaviorSubject.cs index a737348..507ab0d 100644 --- a/UnityProject/Assets/Plugins/UniRx/Scripts/Subjects/BehaviorSubject.cs +++ b/UnityProject/Assets/Plugins/UniRx/Scripts/Subjects/BehaviorSubject.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.ExceptionServices; using UniRx.InternalUtil; namespace UniRx @@ -23,7 +24,7 @@ public T Value get { ThrowIfDisposed(); - if (lastError != null) throw lastError; + if (lastError != null) ExceptionDispatchInfo.Capture(lastError).Throw(); return lastValue; } } diff --git a/UnityProject/Assets/Plugins/UniRx/Scripts/UnityEngineBridge/MainThreadDispatcher.cs b/UnityProject/Assets/Plugins/UniRx/Scripts/UnityEngineBridge/MainThreadDispatcher.cs index 9ee8484..8283789 100644 --- a/UnityProject/Assets/Plugins/UniRx/Scripts/UnityEngineBridge/MainThreadDispatcher.cs +++ b/UnityProject/Assets/Plugins/UniRx/Scripts/UnityEngineBridge/MainThreadDispatcher.cs @@ -5,6 +5,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Runtime.ExceptionServices; using System.Reflection; using System.Threading; using UniRx.InternalUtil; @@ -444,7 +445,7 @@ public static void Initialize() // Throw exception when calling from a worker thread. var ex = new Exception("UniRx requires a MainThreadDispatcher component created on the main thread. Make sure it is added to the scene before calling UniRx from a worker thread."); UnityEngine.Debug.LogException(ex); - throw ex; + ExceptionDispatchInfo.Capture(ex).Throw(); } if (isQuitting) diff --git a/UnityProject/Assets/Plugins/UniRx/Scripts/UnityEngineBridge/Observable.Unity.cs b/UnityProject/Assets/Plugins/UniRx/Scripts/UnityEngineBridge/Observable.Unity.cs index 488914a..c430325 100644 --- a/UnityProject/Assets/Plugins/UniRx/Scripts/UnityEngineBridge/Observable.Unity.cs +++ b/UnityProject/Assets/Plugins/UniRx/Scripts/UnityEngineBridge/Observable.Unity.cs @@ -5,6 +5,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Runtime.ExceptionServices; using UniRx.Triggers; using UnityEngine; using System.Threading; @@ -156,7 +157,7 @@ bool IEnumerator.MoveNext() { if (reThrowOnError && HasError) { - throw Error; + ExceptionDispatchInfo.Capture(Error).Throw(); } return false; @@ -1147,7 +1148,7 @@ static IEnumerable> RepeatInfinite(IObservable source) internal static class Stubs { public static readonly Action Nop = () => { }; - public static readonly Action Throw = ex => { throw ex; }; + public static readonly Action Throw = ex => { ExceptionDispatchInfo.Capture(ex).Throw(); }; // Stubs.Ignore can't avoid iOS AOT problem. public static void Ignore(T t)