Bläddra i källkod

xplat: keep uncaught exception object alive

On xplat when throwing a JS exception, the exception object pointer
is not on stack and could be collected by GC prematually (triggered
by destructors).

To ensure the exception object is kept alive before caught, manually
track it in threadContext data. Throw a C++ wrapper object instead,
and the catch handler must retrieve and clear the exception object
from the wrapper.

Changed all throw and catch code related to previous
JavascriptExceptionObject* to use C++ wrapper JavascriptException.
Jianchun Xu 9 år sedan
förälder
incheckning
ab7ee7d37c
31 ändrade filer med 256 tillägg och 193 borttagningar
  1. 1 1
      lib/Common/Common.h
  2. 4 2
      lib/Common/Common/Jobs.cpp
  3. 1 1
      lib/Common/Exceptions/Chakra.Common.Exceptions.vcxproj
  4. 0 15
      lib/Common/Exceptions/InScriptExceptionBase.h
  5. 43 0
      lib/Common/Exceptions/JavascriptException.h
  6. 4 2
      lib/Common/Exceptions/Throw.h
  7. 2 1
      lib/Jsrt/JsrtDebugUtils.cpp
  8. 6 2
      lib/Jsrt/JsrtDebuggerObject.cpp
  9. 13 13
      lib/Jsrt/JsrtInternal.h
  10. 3 3
      lib/Runtime/Base/FunctionBody.cpp
  11. 2 13
      lib/Runtime/Base/LeaveScriptObject.h
  12. 13 0
      lib/Runtime/Base/ThreadContext.h
  13. 3 3
      lib/Runtime/Debug/DiagHelperMethodWrapper.cpp
  14. 2 2
      lib/Runtime/Debug/DiagHelperMethodWrapper.h
  15. 11 12
      lib/Runtime/Debug/DiagObjectModel.cpp
  16. 4 3
      lib/Runtime/Debug/TTActionEvents.cpp
  17. 10 10
      lib/Runtime/Debug/TTEventLog.cpp
  18. 13 14
      lib/Runtime/Language/InterpreterStackFrame.cpp
  19. 1 1
      lib/Runtime/Language/JavascriptExceptionObject.h
  20. 37 21
      lib/Runtime/Language/JavascriptExceptionOperators.cpp
  21. 3 0
      lib/Runtime/Language/JavascriptExceptionOperators.h
  22. 11 8
      lib/Runtime/Language/JavascriptOperators.cpp
  23. 3 2
      lib/Runtime/Language/JavascriptOperators.h
  24. 3 2
      lib/Runtime/Language/JavascriptOperators.inl
  25. 10 10
      lib/Runtime/Language/SourceTextModuleRecord.cpp
  26. 5 5
      lib/Runtime/Library/IntlEngineInterfaceExtensionObject.cpp
  27. 4 5
      lib/Runtime/Library/JavascriptFunction.cpp
  28. 3 2
      lib/Runtime/Library/JavascriptGenerator.cpp
  29. 2 2
      lib/Runtime/Library/JavascriptGeneratorFunction.cpp
  30. 19 18
      lib/Runtime/Library/JavascriptLibrary.cpp
  31. 20 20
      lib/Runtime/Library/JavascriptPromise.cpp

+ 1 - 1
lib/Common/Common.h

@@ -74,8 +74,8 @@ template<> struct IntMath<int64> { using Type = Int64Math; };
 
 // Exceptions
 #include "Exceptions/ExceptionBase.h"
-#include "Exceptions/InScriptExceptionBase.h"
 #include "Exceptions/InternalErrorException.h"
+#include "Exceptions/JavascriptException.h"
 #include "Exceptions/OutOfMemoryException.h"
 #include "Exceptions/OperationAbortedException.h"
 #include "Exceptions/RejitException.h"

+ 4 - 2
lib/Common/Common/Jobs.cpp

@@ -10,7 +10,7 @@
 #include "Core/EtwTraceCore.h"
 
 #include "Exceptions/ExceptionBase.h"
-#include "Exceptions/InScriptExceptionBase.h"
+#include "Exceptions/JavascriptException.h"
 #include "Exceptions/OperationAbortedException.h"
 #include "Exceptions/OutOfMemoryException.h"
 #include "Exceptions/StackOverflowException.h"
@@ -424,8 +424,10 @@ namespace JsUtil
         {
             return job->Manager()->Process(job, 0);
         }
-        catch (Js::InScriptExceptionBase *)
+        catch (const Js::JavascriptException& err)
         {
+            err.GetAndClear(); // discard exception object
+
             // Treat OOM or stack overflow to be a non-terminal failure. The foreground job processor processes jobs when the
             // jobs are prioritized, on the calling thread. The script would be active (at the time of this writing), so a
             // JavascriptExceptionObject would be thrown for OOM or stack overflow.

+ 1 - 1
lib/Common/Exceptions/Chakra.Common.Exceptions.vcxproj

@@ -45,8 +45,8 @@
     <ClInclude Include="EvalDisabledException.h" />
     <ClInclude Include="ExceptionBase.h" />
     <ClInclude Include="ExceptionCheck.h" />
-    <ClInclude Include="InScriptExceptionBase.h" />
     <ClInclude Include="InternalErrorException.h" />
+    <ClInclude Include="JavascriptException.h" />
     <ClInclude Include="JITOperationFailedException.h" />
     <ClInclude Include="NotImplementedException.h" />
     <ClInclude Include="OperationAbortedException.h" />

+ 0 - 15
lib/Common/Exceptions/InScriptExceptionBase.h

@@ -1,15 +0,0 @@
-//-------------------------------------------------------------------------------------------------------
-// Copyright (C) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
-//-------------------------------------------------------------------------------------------------------
-#pragma once
-
-namespace Js {
-
-    // This class serves as base class for runtime in-script exception objects, so that code here
-    // (lib/common) can catch and handle runtime in-script exceptions.
-    class InScriptExceptionBase
-    {
-    };
-
-} // namespace Js

+ 43 - 0
lib/Common/Exceptions/JavascriptException.h

@@ -0,0 +1,43 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+#pragma once
+
+namespace Js {
+    class JavascriptExceptionObject;
+
+    //
+    // JavascriptException wraps a runtime JavascriptExceptionObject. To ensure
+    // a thrown JavascriptExceptionObject is kept alive before being caught by
+    // a handler, we store the JavascriptExceptionObject reference in thread context
+    // data (which GC scans). After the exception is caught, call GetAndClear()
+    // to retrieve the wrapped JavascriptExceptionObject and clear it from thread
+    // context data.
+    //
+    class JavascriptException : public ExceptionBase
+    {
+    private:
+        JavascriptExceptionObject** const addressOfException;
+
+    public:
+        // Caller should have stored the JavascriptExceptionObject reference in
+        // thread context data. addressOfException should be the thread context data
+        // address.
+        JavascriptException(JavascriptExceptionObject** addressOfException)
+            : addressOfException(addressOfException)
+        {
+            Assert(addressOfException && *addressOfException);
+        }
+
+        JavascriptExceptionObject* GetAndClear() const
+        {
+            Assert(*addressOfException);
+
+            JavascriptExceptionObject* exceptionObject = *addressOfException;
+            *addressOfException = nullptr;
+            return exceptionObject;
+        }
+    };
+
+} // namespace Js

+ 4 - 2
lib/Common/Exceptions/Throw.h

@@ -168,8 +168,9 @@ namespace Js {
     CATCH_UNHANDLED_EXCEPTION(hr)
 
 #define END_TRANSLATE_ERROROBJECT_TO_HRESULT_EX(hr, GetRuntimeErrorFunc) \
-    catch(Js::JavascriptExceptionObject *  exceptionObject)  \
+    catch(const Js::JavascriptException& err)  \
     {   \
+        Js::JavascriptExceptionObject* exceptionObject = err.GetAndClear(); \
         GET_RUNTIME_ERROR_IMPL(hr, GetRuntimeErrorFunc, exceptionObject); \
     }
 
@@ -195,8 +196,9 @@ namespace Js {
     END_TRANSLATE_ERROROBJECT_TO_HRESULT_EX(hr, Js::JavascriptError::GetRuntimeErrorWithScriptEnter)
 
 #define END_GET_ERROROBJECT(hr, scriptContext, exceptionObject) \
-    catch (Js::JavascriptExceptionObject *  _exceptionObject)  \
+    catch (const Js::JavascriptException& err)  \
     {   \
+        Js::JavascriptExceptionObject *  _exceptionObject = err.GetAndClear(); \
         BEGIN_TRANSLATE_OOM_TO_HRESULT_NESTED \
             exceptionObject = _exceptionObject; \
             exceptionObject = exceptionObject->CloneIfStaticExceptionObject(scriptContext);  \

+ 2 - 1
lib/Jsrt/JsrtDebugUtils.cpp

@@ -311,8 +311,9 @@ void JsrtDebugUtils::AddPropertyType(Js::DynamicObject * object, Js::IDiagObject
         {
             value = objectDisplayRef->Value(10);
         }
-        catch (Js::JavascriptExceptionObject*)
+        catch (const Js::JavascriptException& err)
         {
+            err.GetAndClear();  // discard exception object
             value = _u("");
         }
 

+ 6 - 2
lib/Jsrt/JsrtDebuggerObject.cpp

@@ -52,7 +52,10 @@ Js::DynamicObject * JsrtDebuggerObjectBase::GetChildren(WeakArenaReference<Js::I
         {
             childrensCount = walker->GetChildrenCount();
         }
-        catch (Js::JavascriptExceptionObject*) {}
+        catch (const Js::JavascriptException& err)
+        {
+            err.GetAndClear();  // discard exception object
+        }
 
         if (fromCount < childrensCount)
         {
@@ -64,8 +67,9 @@ Js::DynamicObject * JsrtDebuggerObjectBase::GetChildren(WeakArenaReference<Js::I
                 {
                     walker->Get(i, &resolvedObject);
                 }
-                catch (Js::JavascriptExceptionObject* exception)
+                catch (const Js::JavascriptException& err)
                 {
+                    Js::JavascriptExceptionObject* exception = err.GetAndClear();
                     Js::Var error = exception->GetThrownObject(scriptContext);
                     resolvedObject.obj = error;
                     resolvedObject.address = nullptr;

+ 13 - 13
lib/Jsrt/JsrtInternal.h

@@ -154,9 +154,9 @@ JsErrorCode ContextAPIWrapper(Fn fn)
     {
       return JsErrorOutOfMemory;
     }
-    catch (Js::JavascriptExceptionObject *  exceptionObject)
+    catch (const Js::JavascriptException& err)
     {
-        scriptContext->GetThreadContext()->SetRecordedException(exceptionObject);
+        scriptContext->GetThreadContext()->SetRecordedException(err.GetAndClear());
         return JsErrorScriptException;
     }
     catch (Js::ScriptAbortException)
@@ -209,10 +209,10 @@ JsErrorCode ContextAPINoScriptWrapper(Fn fn, bool allowInObjectBeforeCollectCall
     }
     CATCH_STATIC_JAVASCRIPT_EXCEPTION_OBJECT
 
-    catch (Js::JavascriptExceptionObject *  exceptionObject)
+    catch (const Js::JavascriptException& err)
     {
         AssertMsg(false, "Should never get JavascriptExceptionObject for ContextAPINoScriptWrapper.");
-        scriptContext->GetThreadContext()->SetRecordedException(exceptionObject);
+        scriptContext->GetThreadContext()->SetRecordedException(err.GetAndClear());
         return JsErrorScriptException;
     }
 
@@ -268,9 +268,9 @@ JsErrorCode SetContextAPIWrapper(JsrtContext* newContext, Fn fn)
     {
         errorCode = JsErrorOutOfMemory;
     }
-    catch (Js::JavascriptExceptionObject *  exceptionObject)
+    catch (const Js::JavascriptException& err)
     {
-        scriptContext->GetThreadContext()->SetRecordedException(exceptionObject);
+        scriptContext->GetThreadContext()->SetRecordedException(err.GetAndClear());
         errorCode = JsErrorScriptException;
     }
     catch (Js::ScriptAbortException)
@@ -334,15 +334,15 @@ void HandleScriptCompileError(Js::ScriptContext * scriptContext, CompileScriptEx
 #else
 #define PERFORM_JSRT_TTD_RECORD_ACTION_CHECK(CTX) false
 
-#define PERFORM_JSRT_TTD_RECORD_ACTION(WRAPPER_TAG, CTX, ACTION_CODE, ...) 
+#define PERFORM_JSRT_TTD_RECORD_ACTION(WRAPPER_TAG, CTX, ACTION_CODE, ...)
 
-#define PERFORM_JSRT_TTD_RECORD_ACTION_STD_GLOBALWRAPPER(ACTION_CODE, ...) 
-#define PERFORM_JSRT_TTD_RECORD_ACTION_STD_CONTEXTWRAPPER(ACTION_CODE, ...) 
-#define PERFORM_JSRT_TTD_RECORD_ACTION_STD_NOSCRIPTWRAPPER(ACTION_CODE, ...) 
+#define PERFORM_JSRT_TTD_RECORD_ACTION_STD_GLOBALWRAPPER(ACTION_CODE, ...)
+#define PERFORM_JSRT_TTD_RECORD_ACTION_STD_CONTEXTWRAPPER(ACTION_CODE, ...)
+#define PERFORM_JSRT_TTD_RECORD_ACTION_STD_NOSCRIPTWRAPPER(ACTION_CODE, ...)
 
-#define PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(RESULT) 
-#define PERFORM_JSRT_TTD_RECORD_ACTION_COMPLETE_NO_RESULT() 
+#define PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(RESULT)
+#define PERFORM_JSRT_TTD_RECORD_ACTION_COMPLETE_NO_RESULT()
 
 //TODO: find and replace all of the occourences of this in jsrt.cpp
-#define PERFORM_JSRT_TTD_RECORD_ACTION_NOT_IMPLEMENTED(CTX) 
+#define PERFORM_JSRT_TTD_RECORD_ACTION_NOT_IMPLEMENTED(CTX)
 #endif

+ 3 - 3
lib/Runtime/Base/FunctionBody.cpp

@@ -1744,9 +1744,9 @@ namespace Js
                         }
                         catch (OutOfMemoryException) {}
                         catch (StackOverflowException) {}
-                        catch (JavascriptExceptionObject* exceptionObject)
+                        catch (const Js::JavascriptException& err)
                         {
-                            pExceptionObject = exceptionObject;
+                            pExceptionObject = err.GetAndClear();
                         }
 
                         // Do not do anything with an OOM or SOE, returning true is fine, it will then be undeferred (or attempted to again when called)
@@ -1755,7 +1755,7 @@ namespace Js
                             if(pExceptionObject != ThreadContext::GetContextForCurrentThread()->GetPendingOOMErrorObject() &&
                                 pExceptionObject != ThreadContext::GetContextForCurrentThread()->GetPendingSOErrorObject())
                             {
-                                throw pExceptionObject;
+                                JavascriptExceptionOperators::DoThrow(pExceptionObject, /*scriptContext*/nullptr);
                             }
                         }
                     }

+ 2 - 13
lib/Runtime/Base/LeaveScriptObject.h

@@ -94,8 +94,9 @@
 
 #define END_TRANSLATE_SO_OOM_JSEXCEPTION(hr) \
         } \
-        catch (Js::JavascriptExceptionObject *) \
+        catch (const JavascriptException& err) \
         { \
+            err.GetAndClear(); \
         } \
         catch (Js::OutOfMemoryException) \
         { \
@@ -122,18 +123,6 @@
 #define ENFORCE_ENTRYEXITRECORD_HASCALLER(scriptContext) \
         scriptContext->EnforceEERHasCaller();
 
-#define BEGIN_JS_RUNTIME_CALL_EX_AND_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT2(scriptContext, doCleanup, hasCaller) \
-        BEGIN_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT \
-        BEGIN_ENTER_SCRIPT(scriptContext, doCleanup, /*isCallRoot*/ false, /*hasCaller*/hasCaller) \
-        { \
-
-// Same as above but allows custom handling of exception object.
-#define END_JS_RUNTIME_CALL_AND_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT2(hr, exceptionObject) \
-        } \
-        END_ENTER_SCRIPT \
-        END_TRANSLATE_KNOWN_EXCEPTION_TO_HRESULT(hr) \
-        catch(Js::JavascriptExceptionObject *  exceptionObject)
-
 namespace Js
 {
     class EnterScriptObject

+ 13 - 0
lib/Runtime/Base/ThreadContext.h

@@ -603,6 +603,9 @@ private:
 
         Js::JavascriptExceptionObject* unhandledExceptionObject;
 
+        // Used to temporarily keep throwing exception object alive (thrown but not yet caught)
+        Js::JavascriptExceptionObject* tempUncaughtException;
+
         // Contains types that have property caches that need to be tracked, as the caches may need to be cleared. Types that
         // contain a property cache for a property that is on a prototype object will be tracked in this map since those caches
         // need to be cleared if for instance, the property is deleted from the prototype object.
@@ -1403,6 +1406,16 @@ public:
     void SetUnhandledExceptionObject(Js::JavascriptExceptionObject* exceptionObject) {recyclableData->unhandledExceptionObject  = exceptionObject; }
     Js::JavascriptExceptionObject* GetUnhandledExceptionObject() const  { return recyclableData->unhandledExceptionObject; };
 
+    // To temporarily keep throwing exception object alive (thrown but not yet caught)
+    Js::JavascriptExceptionObject** SaveTempUncaughtException(Js::JavascriptExceptionObject* exceptionObject)
+    {
+        // Previous save should have been caught and cleared
+        Assert(recyclableData->tempUncaughtException == nullptr);
+
+        recyclableData->tempUncaughtException = exceptionObject;
+        return &recyclableData->tempUncaughtException;
+    }
+
     bool HasCatchHandler() const { return hasCatchHandler; }
     void SetHasCatchHandler(bool hasCatchHandler) { this->hasCatchHandler = hasCatchHandler; }
 

+ 3 - 3
lib/Runtime/Debug/DiagHelperMethodWrapper.cpp

@@ -214,7 +214,7 @@ namespace Js
             exceptionObject == scriptContext->GetThreadContext()->GetPendingOOMErrorObject() ||
             exceptionObject == scriptContext->GetThreadContext()->GetPendingSOErrorObject())
         {
-            throw exceptionObject->CloneIfStaticExceptionObject(scriptContext);
+            JavascriptExceptionOperators::DoThrowCheckClone(exceptionObject, scriptContext);
         }
 
         if (doCheckParentInterpreterFrame)
@@ -231,7 +231,7 @@ namespace Js
             {
                 // If parent frame is interpreter frame, it already has try-catch around all calls,
                 // so that we don't need any special handling here.
-                throw exceptionObject->CloneIfStaticExceptionObject(scriptContext);
+                JavascriptExceptionOperators::DoThrowCheckClone(exceptionObject, scriptContext);
             }
         }
 
@@ -252,7 +252,7 @@ namespace Js
                 exceptionObject->GetFunctionBody(), &reader, exceptionObject->GetByteCodeOffset(), &nextStatementOffset))
             {
                 // Can't advance.
-                throw exceptionObject->CloneIfStaticExceptionObject(scriptContext);
+                JavascriptExceptionOperators::DoThrowCheckClone(exceptionObject, scriptContext);
             }
         }
 

+ 2 - 2
lib/Runtime/Debug/DiagHelperMethodWrapper.h

@@ -52,9 +52,9 @@ namespace Js
         {
             return fn();
         }
-        catch (JavascriptExceptionObject* _exceptionObject)
+        catch (const JavascriptException& err)
         {
-            exceptionObject = _exceptionObject;
+            exceptionObject = err.GetAndClear();
         }
 
         if (exceptionObject != nullptr)

+ 11 - 12
lib/Runtime/Debug/DiagObjectModel.cpp

@@ -2116,10 +2116,10 @@ namespace Js
                         return TRUE;
                     }
                 }
-                catch (Js::JavascriptExceptionObject* exception)
+                catch (const JavascriptException& err)
                 {
                     // The For in enumerator can throw an exception and we will use the error object as a child in that case.
-                    Var error = exception->GetThrownObject(scriptContext);
+                    Var error = err.GetAndClear()->GetThrownObject(scriptContext);
                     if (error != nullptr && Js::JavascriptError::Is(error))
                     {
                         return TRUE;
@@ -2454,9 +2454,9 @@ namespace Js
                                 }
                             }
                         }
-                        catch (JavascriptExceptionObject* exception)
+                        catch (const JavascriptException& err)
                         {
-                            Var error = exception->GetThrownObject(scriptContext);
+                            Var error = err.GetAndClear()->GetThrownObject(scriptContext);
                             if (error != nullptr && Js::JavascriptError::Is(error))
                             {
                                 Js::PropertyId propertyId = scriptContext->GetOrAddPropertyIdTracked(_u("{error}"));
@@ -2747,7 +2747,7 @@ namespace Js
                 DebuggerPropertyDisplayInfo *info = Anew(arena, DebuggerPropertyDisplayInfo, propertyId, itemObj, DebuggerPropertyDisplayInfoFlags_Const);
                 pMembersList->Add(info);
             }
-            else 
+            else
             {
                 EnsureFakeGroupObjectWalkerList();
 
@@ -2786,9 +2786,9 @@ namespace Js
                 return instance->GetScriptContext()->GetMissingPropertyResult();
             }
         }
-        catch(Js::JavascriptExceptionObject * exceptionObject)
+        catch(const JavascriptException& err)
         {
-            Var error = exceptionObject->GetThrownObject(instance->GetScriptContext());
+            Var error = err.GetAndClear()->GetThrownObject(instance->GetScriptContext());
             if (error != nullptr && Js::JavascriptError::Is(error))
             {
                 obj = error;
@@ -4032,10 +4032,9 @@ namespace Js
                     }
                 }
             }
-            catch(Js::JavascriptExceptionObject *exceptionObject)
+            catch(const JavascriptException& err)
             {
-                exceptionObject;
-                // Not doing anything over here.
+                err.GetAndClear();  // discard exception object
             }
 
             return _u("");
@@ -4280,7 +4279,7 @@ namespace Js
         pResolvedObject->objectDisplay = pResolvedObject->CreateDisplay();
         pResolvedObject->objectDisplay->SetDefaultTypeAttribute(DBGPROP_ATTRIB_VALUE_READONLY | DBGPROP_ATTRIB_VALUE_IS_FAKE);
         pResolvedObject->address = nullptr;
-        
+
         return TRUE;
     }
 
@@ -4330,7 +4329,7 @@ namespace Js
         SIMDValue value = simd->GetValue();
 
         char16* stringBuffer = AnewArray(GetArenaFromContext(scriptContext), char16, SIMD_STRING_BUFFER_MAX);
-        
+
         simdType::ToStringBuffer(value, stringBuffer, SIMD_STRING_BUFFER_MAX, scriptContext);
 
         builder->AppendSz(stringBuffer);

+ 4 - 3
lib/Runtime/Debug/TTActionEvents.cpp

@@ -128,7 +128,7 @@ namespace TTD
         {
             Js::Var message = InflateVarInReplay(ctx, errorData->Var1);
 
-            Js::Var res = nullptr; 
+            Js::Var res = nullptr;
             switch(eventKind)
             {
             case EventKind::CreateErrorActionTag:
@@ -564,7 +564,7 @@ namespace TTD
             Js::JavascriptFunction* jsFunction = Js::JavascriptFunction::FromVar(jsFunctionVar);
 
             //remove implicit constructor function as first arg in callInfo and argument loop below
-            Js::CallInfo callInfo(Js::CallFlags::CallFlags_New, (ushort)(ccAction->ArgCount - 1)); 
+            Js::CallInfo callInfo(Js::CallFlags::CallFlags_New, (ushort)(ccAction->ArgCount - 1));
             for(uint32 i = 1; i < ccAction->ArgCount; ++i)
             {
                 ccAction->ExecArgs[i - 1] = InflateVarInReplay(ctx, ccAction->ArgArray[i]);
@@ -937,9 +937,10 @@ namespace TTD
 
                     AssertMsg(EventCompletesScriptContextNormally(evt), "Why did we get a different completion");
                 }
-                catch(Js::JavascriptExceptionObject*)
+                catch(const Js::JavascriptException& err)
                 {
 #if ENABLE_TTD_DEBUGGING
+                    err.GetAndClear();  // discard exception object
                     AssertMsg(EventCompletesScriptContextWithException(evt), "Why did we get a different exception");
 
                     //convert to uncaught debugger exception for host

+ 10 - 10
lib/Runtime/Debug/TTEventLog.cpp

@@ -459,7 +459,7 @@ namespace TTD
 
     const SingleCallCounter* EventLog::GetTopCallCallerCounter(bool justMyCode) const
     {
-        
+
         for(int32 pos = this->m_callStack.Count() - 2; pos >= 0; --pos)
         {
             const SingleCallCounter* csi = &(this->m_callStack.Item(pos));
@@ -700,7 +700,7 @@ namespace TTD
         : m_threadContext(threadContext), m_eventSlabAllocator(TTD_SLAB_BLOCK_ALLOCATION_SIZE_MID), m_miscSlabAllocator(TTD_SLAB_BLOCK_ALLOCATION_SIZE_SMALL),
         m_eventTimeCtr(0), m_timer(), m_runningFunctionTimeCtr(0), m_topLevelCallbackEventTime(-1), m_hostCallbackId(-1),
         m_eventList(&this->m_eventSlabAllocator), m_eventListVTable(nullptr), m_currentReplayEventIterator(),
-        m_callStack(&HeapAllocator::Instance, 32), 
+        m_callStack(&HeapAllocator::Instance, 32),
 #if ENABLE_TTD_DEBUGGING
         m_lastReturnLocation(), m_lastReturnLocationJMC(), m_breakOnFirstUserCode(false), m_pendingTTDBP(), m_activeBPId(-1), m_shouldRemoveWhenDone(false), m_activeTTDBP(), m_breakpointInfoList(&HeapAllocator::Instance), m_bpPreserveList(&HeapAllocator::Instance),
 #endif
@@ -710,7 +710,7 @@ namespace TTD
         m_modeStack(), m_currentMode(TTDMode::Pending),
         m_ttdContext(nullptr),
         m_snapExtractor(), m_elapsedExecutionTimeSinceSnapshot(0.0),
-        m_lastInflateSnapshotTime(-1), m_lastInflateMap(nullptr), m_propertyRecordPinSet(nullptr), m_propertyRecordList(&this->m_miscSlabAllocator), 
+        m_lastInflateSnapshotTime(-1), m_lastInflateMap(nullptr), m_propertyRecordPinSet(nullptr), m_propertyRecordList(&this->m_miscSlabAllocator),
         m_loadedTopLevelScripts(&this->m_miscSlabAllocator), m_newFunctionTopLevelScripts(&this->m_miscSlabAllocator), m_evalTopLevelScripts(&this->m_miscSlabAllocator)
     {
         this->InitializeEventListVTable();
@@ -1572,7 +1572,7 @@ namespace TTD
 
         SetDiagnosticOriginInformation(originInfo, srcLine, cfinfo.EventTime, cfinfo.FunctionTime, cfinfo.LoopTime);
     }
-#endif 
+#endif
 
 #if ENABLE_TTD_DEBUGGING
     bool EventLog::GetPreviousTimeAndPositionForDebugger(TTDebuggerSourceLocation& sourceLocation) const
@@ -1863,7 +1863,7 @@ namespace TTD
             {
                 validSnap = isSnap;
             }
-            
+
             if(validSnap && time <= targetTime)
             {
                 snapTime = time;
@@ -2157,11 +2157,11 @@ namespace TTD
 
                 AssertMsg(NSLogEvents::EventCompletesScriptContextNormally(evt), "All my action events should exit / terminate before return so no need to loop yet but may want to later");
             }
-            catch(Js::JavascriptExceptionObject *  exceptionObject)
+            catch(const Js::JavascriptException& err)
             {
                 AssertMsg(NSLogEvents::EventCompletesScriptContextWithException(evt), "Should see same execption here");
 
-                ctx->GetThreadContext()->SetRecordedException(exceptionObject);
+                ctx->GetThreadContext()->SetRecordedException(err.GetAndClear());
             }
             catch(Js::ScriptAbortException)
             {
@@ -2194,12 +2194,12 @@ namespace TTD
 
                 AssertMsg(NSLogEvents::EventCompletesScriptContextNormally(evt), "All my action events should both exit / terminate before return so no need to loop yet but may want to later");
             }
-            catch(Js::JavascriptExceptionObject *  exceptionObject)
+            catch(const Js::JavascriptException& err)
             {
                 AssertMsg(NSLogEvents::EventCompletesScriptContextWithException(evt), "Should see same execption here");
 
                 AssertMsg(false, "Should never get JavascriptExceptionObject for ContextAPINoScriptWrapper.");
-                ctx->GetThreadContext()->SetRecordedException(exceptionObject);
+                ctx->GetThreadContext()->SetRecordedException(err.GetAndClear());
             }
             catch(Js::ScriptAbortException)
             {
@@ -2901,7 +2901,7 @@ namespace TTD
         writer.AdjustIndent(-1);
         writer.WriteSequenceEnd(NSTokens::Separator::BigSpaceSeparator);
 
-        //we haven't moved the properties to their serialized form them take care of it 
+        //we haven't moved the properties to their serialized form them take care of it
         AssertMsg(this->m_propertyRecordList.Count() == 0, "We only compute this when we are ready to emit.");
 
         for(auto iter = this->m_propertyRecordPinSet->GetIterator(); iter.IsValid(); iter.MoveNext())

+ 13 - 14
lib/Runtime/Language/InterpreterStackFrame.cpp

@@ -2305,8 +2305,9 @@ namespace Js
             {
                 return this->ProcessWithDebugging();
             }
-            catch (JavascriptExceptionObject *exception_)
+            catch (const Js::JavascriptException& err)
             {
+                JavascriptExceptionObject *exception_ = err.GetAndClear();
                 Assert(exception_);
                 exception = exception_;
             }
@@ -2337,8 +2338,7 @@ namespace Js
                     }
                 }
 
-                exception = exception->CloneIfStaticExceptionObject(scriptContext);
-                throw exception;
+                JavascriptExceptionOperators::DoThrowCheckClone(exception, scriptContext);
             }
         }
     }
@@ -6468,11 +6468,11 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
                 this->TrySetRetOffset();
             }
         }
-        catch (Js::JavascriptExceptionObject * exceptionObject)
+        catch (const Js::JavascriptException& err)
         {
             // We are using C++ exception handling which does not unwind the stack in the catch block.
             // For stack overflow and OOM exceptions, we cannot run user code here because the stack is not unwind.
-            exception = exceptionObject;
+            exception = err.GetAndClear();
         }
 
         if (--this->nestedTryDepth == -1)
@@ -6487,7 +6487,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
             if (exception->IsGeneratorReturnException())
             {
                 // Generator return scenario, so no need to go into the catch block and we must rethrow to propagate the exception to down level
-                throw exception;
+                JavascriptExceptionOperators::DoThrow(exception, scriptContext);
             }
 
             exception = exception->CloneIfStaticExceptionObject(scriptContext);
@@ -6604,11 +6604,11 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
                     this->TrySetRetOffset();
                 }
             }
-            catch (Js::JavascriptExceptionObject * exceptionObject)
+            catch (const Js::JavascriptException& err)
             {
                 // We are using C++ exception handling which does not unwind the stack in the catch block.
                 // For stack overflow and OOM exceptions, we cannot run user code here because the stack is not unwind.
-                exception = exceptionObject;
+                exception = err.GetAndClear();
             }
         }
         else
@@ -6643,7 +6643,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
             if (exception->IsGeneratorReturnException())
             {
                 // Generator return scenario, so no need to go into the catch block and we must rethrow to propagate the exception to down level
-                throw exception;
+                JavascriptExceptionOperators::DoThrow(exception, scriptContext);
             }
 
             exception = exception->CloneIfStaticExceptionObject(scriptContext);
@@ -6683,7 +6683,6 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
                 this->m_flags &= ~InterpreterStackFrameFlags_WithinCatchBlock;
             }
         }
-        return;
     }
 
     void InterpreterStackFrame::TrySetRetOffset()
@@ -6779,9 +6778,9 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
                 skipFinallyBlock = true;
             }
         }
-        catch (Js::JavascriptExceptionObject * e)
+        catch (const Js::JavascriptException& err)
         {
-            pExceptionObject = e;
+            pExceptionObject = err.GetAndClear();
         }
 
         if (--this->nestedTryDepth == -1)
@@ -6848,7 +6847,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
 
         if (pExceptionObject && (endOfFinallyBlock || !pExceptionObject->IsGeneratorReturnException()))
         {
-            throw pExceptionObject;
+            JavascriptExceptionOperators::DoThrow(pExceptionObject, scriptContext);
         }
     }
 
@@ -6897,7 +6896,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
         Js::JavascriptExceptionObject* exceptionObj = (Js::JavascriptExceptionObject*)GetNonVarReg(exceptionRegSlot);
         if (exceptionObj && (endOfFinallyBlock || !exceptionObj->IsGeneratorReturnException()))
         {
-            throw exceptionObj;
+            JavascriptExceptionOperators::DoThrow(exceptionObj, scriptContext);
         }
     }
 

+ 1 - 1
lib/Runtime/Language/JavascriptExceptionObject.h

@@ -10,7 +10,7 @@ namespace Js {
 
     class JavascriptExceptionContext;
 
-    class JavascriptExceptionObject: public InScriptExceptionBase
+    class JavascriptExceptionObject
     {
     public:
         typedef Var (__stdcall *HostWrapperCreateFuncType)(Var var, ScriptContext * sourceScriptContext, ScriptContext * destScriptContext);

+ 37 - 21
lib/Runtime/Language/JavascriptExceptionOperators.cpp

@@ -94,9 +94,9 @@ namespace Js
             Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext, true);
             continuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize);
         }
-        catch (JavascriptExceptionObject *caughtException)
+        catch (const Js::JavascriptException& err)
         {
-            exception = caughtException;
+            exception = err.GetAndClear();
         }
 
         if (exception)
@@ -108,7 +108,7 @@ namespace Js
                 // If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
                 // it so happens that this catch was on the stack and caught the exception.
                 // Re-throw!
-                throw exception;
+                JavascriptExceptionOperators::DoThrow(exception, scriptContext);
             }
             Var exceptionObject = exception->GetThrownObject(scriptContext);
             AssertMsg(exceptionObject, "Caught object is null.");
@@ -135,9 +135,9 @@ namespace Js
         {
             tryContinuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize);
         }
-        catch (JavascriptExceptionObject *caughtException)
+        catch (const Js::JavascriptException& err)
         {
-            exception = caughtException;
+            exception = err.GetAndClear();
         }
 
         if (exception)
@@ -154,7 +154,7 @@ namespace Js
 
         if (exception)
         {
-            throw exception;
+            JavascriptExceptionOperators::DoThrow(exception, scriptContext);
         }
 
         return tryContinuation;
@@ -184,9 +184,9 @@ namespace Js
             continuation = arm64_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize);
 #endif
         }
-        catch (JavascriptExceptionObject *caughtException)
+        catch (const Js::JavascriptException& err)
         {
-            exception = caughtException;
+            exception = err.GetAndClear();
         }
 
         if (exception)
@@ -198,7 +198,7 @@ namespace Js
                 // If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
                 // it so happens that this catch was on the stack and caught the exception.
                 // Re-throw!
-                throw exception;
+                JavascriptExceptionOperators::DoThrow(exception, scriptContext);
             }
             Var exceptionObject = exception->GetThrownObject(scriptContext);
             AssertMsg(exceptionObject, "Caught object is null.");
@@ -234,9 +234,9 @@ namespace Js
             tryContinuation = arm64_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize);
 #endif
         }
-        catch (JavascriptExceptionObject *caughtException)
+        catch (const Js::JavascriptException& err)
         {
-            exception = caughtException;
+            exception = err.GetAndClear();
         }
 
         if (exception)
@@ -258,7 +258,7 @@ namespace Js
 
         if (exception)
         {
-            throw exception;
+            JavascriptExceptionOperators::DoThrow(exception, scriptContext);
         }
 
         return tryContinuation;
@@ -334,9 +334,9 @@ namespace Js
             AssertMsg(FALSE, "Unsupported native try-catch handler");
 #endif
         }
-        catch(Js::JavascriptExceptionObject * exceptionObject)
+        catch(const Js::JavascriptException& err)
         {
-            pExceptionObject = exceptionObject;
+            pExceptionObject = err.GetAndClear();
         }
 
         // Let's run user catch handler code only after the stack has been unwound.
@@ -349,7 +349,7 @@ namespace Js
                 // If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
                 // it so happens that this catch was on the stack and caught the exception.
                 // Re-throw!
-                throw pExceptionObject;
+                JavascriptExceptionOperators::DoThrow(pExceptionObject, scriptContext);
             }
             Var catchObject = pExceptionObject->GetThrownObject(scriptContext);
             AssertMsg(catchObject, "Caught object is NULL");
@@ -475,9 +475,9 @@ namespace Js
             AssertMsg(FALSE, "Unsupported native try-finally handler");
 #endif
         }
-        catch(Js::JavascriptExceptionObject* e)
+        catch(const Js::JavascriptException& err)
         {
-            pExceptionObject = e;
+            pExceptionObject = err.GetAndClear();
         }
 
         if (pExceptionObject)
@@ -547,7 +547,7 @@ namespace Js
 
         if (pExceptionObject)
         {
-            throw pExceptionObject;
+            JavascriptExceptionOperators::DoThrow(pExceptionObject, scriptContext);
         }
 
         return continuationAddr;
@@ -859,15 +859,31 @@ namespace Js
             {
                 DispatchExceptionToDebugger(exceptionObject, scriptContext);
             }
-       }
+        }
 
         if (exceptionObject->IsPendingExceptionObject())
         {
             ThreadContext * threadContext = scriptContext? scriptContext->GetThreadContext() : ThreadContext::GetContextForCurrentThread();
             threadContext->SetHasThrownPendingException();
-
         }
-        throw exceptionObject;
+
+        DoThrow(exceptionObject, scriptContext);
+    }
+
+    void JavascriptExceptionOperators::DoThrow(JavascriptExceptionObject* exceptionObject, ScriptContext* scriptContext)
+    {
+        ThreadContext* threadContext = scriptContext? scriptContext->GetThreadContext() : ThreadContext::GetContextForCurrentThread();
+
+        // Temporarily keep throwing exception object alive (thrown but not yet caught)
+        JavascriptExceptionObject** addr = threadContext->SaveTempUncaughtException(exceptionObject);
+
+        // Throw a wrapper JavascriptException. catch handler must GetAndClear() the exception object.
+        throw JavascriptException(addr);
+    }
+
+    void JavascriptExceptionOperators::DoThrowCheckClone(JavascriptExceptionObject* exceptionObject, ScriptContext* scriptContext)
+    {
+        DoThrow(exceptionObject->CloneIfStaticExceptionObject(scriptContext), scriptContext);
     }
 
     void JavascriptExceptionOperators::DispatchExceptionToDebugger(Js::JavascriptExceptionObject * exceptionObject, ScriptContext* scriptContext)

+ 3 - 0
lib/Runtime/Language/JavascriptExceptionOperators.h

@@ -48,6 +48,9 @@ namespace Js
         static void __declspec(noreturn) ThrowExceptionObject(Js::JavascriptExceptionObject* exceptionObject, ScriptContext* scriptContext, bool considerPassingToDebugger = false, PVOID returnAddress = NULL, bool resetStack = false);
         static void __declspec(noreturn) RethrowExceptionObject(Js::JavascriptExceptionObject* exceptionObject, ScriptContext* scriptContext, bool considerPassingToDebugger = false);
 
+        static void __declspec(noreturn) DoThrow(JavascriptExceptionObject* exceptionObject, ScriptContext* scriptContext);
+        static void __declspec(noreturn) DoThrowCheckClone(JavascriptExceptionObject* exceptionObject, ScriptContext* scriptContext);
+
 #ifdef _M_X64
         static void *OP_TryCatch(void *try_, void *catch_, void *frame, size_t spillSize, size_t argsSize, int hasBailedOutOffset, ScriptContext *scriptContext);
         static void *OP_TryFinally(void *try_, void *finally_, void *frame, size_t spillSize, size_t argsSize, ScriptContext *scriptContext);

+ 11 - 8
lib/Runtime/Language/JavascriptOperators.cpp

@@ -367,8 +367,9 @@ namespace Js
                 return JavascriptOperators::Typeof(value, scriptContext);
             }
         }
-        catch(Js::JavascriptExceptionObject * )
+        catch(const JavascriptException& err)
         {
+            err.GetAndClear();  // discard exception object
             return scriptContext->GetLibrary()->GetUndefinedDisplayString();
         }
 
@@ -496,8 +497,9 @@ namespace Js
             threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
             return JavascriptOperators::Typeof(member, scriptContext);
         }
-        catch(Js::JavascriptExceptionObject * )
+        catch(const JavascriptException& err)
         {
+            err.GetAndClear();  // discard exception object
             threadContext->CheckAndResetImplicitCallAccessorFlag();
             threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
             return scriptContext->GetLibrary()->GetUndefinedDisplayString();
@@ -4931,7 +4933,7 @@ CommonNumber:
         if (loadRoot)
         {
             if (moduleID == 0)
-            {                
+            {
                 thisVar = (Js::Var)scriptContext->GetGlobalObjectThisAddr();
             }
             else
@@ -8360,7 +8362,7 @@ CommonNumber:
                 // Else, set it to next element after current nextEvictionVictim index
                 cache->nextEvictionVictim = (cache->nextEvictionVictim + 1) & (EQUIVALENT_TYPE_CACHE_SIZE - 1);
             }
-   
+
             if (PHASE_TRACE1(Js::EquivObjTypeSpecPhase))
             {
                 Output::Print(_u("EquivObjTypeSpec: Saving type in used slot of equiv types cache at index = %d. NextEvictionVictim = %d. \n"), index, cache->nextEvictionVictim);
@@ -9657,7 +9659,7 @@ CommonNumber:
                 }
 
                 // Do not use ThrowExceptionObject for return() API exceptions since these exceptions are not real exceptions
-                throw yieldData->exceptionObj;
+                JavascriptExceptionOperators::DoThrow(yieldData->exceptionObj, scriptContext);
             }
 
             if (!JavascriptConversion::IsCallable(prop))
@@ -9690,7 +9692,7 @@ CommonNumber:
                 Var value = JavascriptOperators::GetProperty(obj, PropertyIds::value, scriptContext);
                 yieldData->exceptionObj->SetThrownObject(value);
                 // Do not use ThrowExceptionObject for return() API exceptions since these exceptions are not real exceptions
-                throw yieldData->exceptionObj;
+                JavascriptExceptionOperators::DoThrow(yieldData->exceptionObj, scriptContext);
             }
             return result;
         }
@@ -9726,7 +9728,7 @@ CommonNumber:
         // have to wrap the call to the generator code in a try catch.
 
         // Do not use ThrowExceptionObject for return() API exceptions since these exceptions are not real exceptions
-        throw yieldData->exceptionObj;
+        JavascriptExceptionOperators::DoThrow(yieldData->exceptionObj, yieldData->exceptionObj->GetScriptContext());
     }
 
     Js::Var
@@ -10494,8 +10496,9 @@ CommonNumber:
                 JavascriptFunction::CallFunction<true>(callable, callable->GetEntryPoint(), Js::Arguments(callInfo, args));
             }
         }
-        catch (JavascriptExceptionObject *)
+        catch (const JavascriptException& err)
         {
+            err.GetAndClear();  // discard exception object
             // We have arrived in this function due to AbruptCompletion (which is an exception), so we don't need to
             // propagate the exception of calling return function
         }

+ 3 - 2
lib/Runtime/Language/JavascriptOperators.h

@@ -24,8 +24,9 @@ namespace Js
 
 #define TYPEOF_ERROR_HANDLER_CATCH(scriptContext, var) \
     } \
-    catch (Js::JavascriptExceptionObject *exceptionObject) \
+    catch (const JavascriptException& err) \
     { \
+        JavascriptExceptionObject* exceptionObject = err.GetAndClear(); \
         Js::Var errorObject = exceptionObject->GetThrownObject(nullptr); \
         if (errorObject != nullptr && Js::JavascriptError::Is(errorObject)) \
         { \
@@ -39,7 +40,7 @@ namespace Js
                 } \
                 else \
                 { \
-                    throw exceptionObject; \
+                    JavascriptExceptionOperators::DoThrow(exceptionObject, scriptContext); \
                 } \
             } \
         } \

+ 3 - 2
lib/Runtime/Language/JavascriptOperators.inl

@@ -67,14 +67,15 @@ namespace Js
                 shouldCallReturn = false;
             }
         }
-        catch (JavascriptExceptionObject * exceptionObj)
+        catch (const JavascriptException& err)
         {
+            JavascriptExceptionObject * exceptionObj = err.GetAndClear();
             if (shouldCallReturn)
             {
                 // Closing the iterator
                 JavascriptOperators::IteratorClose(iterator, scriptContext);
             }
-            throw exceptionObj;
+            JavascriptExceptionOperators::DoThrow(exceptionObj, scriptContext);
         }
     }
 

+ 10 - 10
lib/Runtime/Language/SourceTextModuleRecord.cpp

@@ -56,7 +56,7 @@ namespace Js
         // There is no real reference to lifetime management in ecmascript
         // The life time of a module record should be controlled by the module registry as defined in WHATWG module loader spec
         // in practice the modulerecord lifetime should be the same as the scriptcontext so it could be retrieved for the same
-        // site. Host might hold a reference to the module as well after initializing the module. 
+        // site. Host might hold a reference to the module as well after initializing the module.
         // In our implementation, we'll use the moduleId in bytecode to identify the module.
         childModuleRecord->moduleId = scriptContext->GetLibrary()->EnsureModuleRecordList()->Add(childModuleRecord);
 
@@ -201,7 +201,7 @@ namespace Js
         if (numUnInitializedChildrenModule == 0)
         {
             NotifyParentsAsNeeded();
-        
+
             if (!WasDeclarationInitialized() && isRootModule)
             {
                 // TODO: move this as a promise call? if parser is called from a different thread
@@ -366,7 +366,7 @@ namespace Js
         return *importRecord != nullptr;
     }
 
-    // return false when "ambiguous". 
+    // return false when "ambiguous".
     // otherwise nullptr means "null" where we have circular reference/cannot resolve.
     bool SourceTextModuleRecord::ResolveExport(PropertyId exportName, ResolveSet* resolveSet, ExportModuleRecordList* exportStarSet, ModuleNameRecord** exportRecord)
     {
@@ -426,7 +426,7 @@ namespace Js
                 ModuleRecordBase* sourceModule = this;
                 ModuleNameRecord* importRecord = nullptr;
                 if (this->importRecordList != nullptr
-                    && this->ResolveImport(localNameId, &importRecord) 
+                    && this->ResolveImport(localNameId, &importRecord)
                     && importRecord != nullptr)
                 {
                     sourceModule = importRecord->module;
@@ -602,7 +602,7 @@ namespace Js
         Assert(wasParsed);
         Assert(wasEvaluated);
         Assert(wasDeclarationInitialized);
-        // Debugger can reparse the source and generate the byte code again. Don't cleanup the 
+        // Debugger can reparse the source and generate the byte code again. Don't cleanup the
         // helper information for now.
     }
 
@@ -624,9 +624,9 @@ namespace Js
 
             InitializeIndirectExports();
         }
-        catch (Js::JavascriptExceptionObject * exceptionObject)
+        catch (const JavascriptException& err)
         {
-            this->errorObject = exceptionObject->GetThrownObject(scriptContext);
+            this->errorObject = err.GetAndClear()->GetThrownObject(scriptContext);
         }
 
         if (this->errorObject != nullptr)
@@ -684,7 +684,7 @@ namespace Js
         }
         Assert(!WasEvaluated());
         SetWasEvaluated();
-        // we shouldn't evaluate if there are existing failure. This is defense in depth as the host shouldn't 
+        // we shouldn't evaluate if there are existing failure. This is defense in depth as the host shouldn't
         // call into evaluation if there was previous fialure on the module.
         if (this->errorObject)
         {
@@ -788,7 +788,7 @@ namespace Js
                     // import {foo} from "module1.js"; export {foo};
                     ModuleNameRecord* importRecord = nullptr;
                     if (this->GetImportEntryList() != nullptr
-                        && this->ResolveImport(localNameId, &importRecord) 
+                        && this->ResolveImport(localNameId, &importRecord)
                         && importRecord != nullptr)
                     {
                         return;
@@ -825,7 +825,7 @@ namespace Js
                     }
 
                     localExportMapByExportName->Add(exportNameId, exportSlot);
-                    
+
                 });
             }
             // Namespace object will be added to the end of the array though invisible through namespace object itself.

+ 5 - 5
lib/Runtime/Library/IntlEngineInterfaceExtensionObject.cpp

@@ -384,7 +384,7 @@ namespace Js
             HRESULT hr;
 
             DelayLoadWindowsGlobalization *library = scriptContext->GetThreadContext()->GetWindowsGlobalizationLibrary();
-            
+
             JavascriptString* initType = nullptr;
 
             //Ensure we have initialized all appropriate COM objects for the adapter (we will be using them now)
@@ -441,9 +441,9 @@ namespace Js
                 deletePrototypePropertyHelper(scriptContext, intlObject, Js::PropertyIds::DateTimeFormat, Js::PropertyIds::format);
             }
         }
-        catch (JavascriptExceptionObject* exceptionObject)
+        catch (const JavascriptException& err)
         {
-            pExceptionObject = exceptionObject;
+            pExceptionObject = err.GetAndClear();
         }
 
         if (pExceptionObject)
@@ -477,8 +477,8 @@ namespace Js
                     globAdapter->ResetNumberFormatFactoryObjects();
                     break;
                 }
-                pExceptionObject = pExceptionObject->CloneIfStaticExceptionObject(scriptContext);
-                throw pExceptionObject;
+
+                JavascriptExceptionOperators::DoThrowCheckClone(pExceptionObject, scriptContext);
             }
             JavascriptError::ThrowTypeError(scriptContext, JSERR_IntlNotAvailable);
         }

+ 4 - 5
lib/Runtime/Library/JavascriptFunction.cpp

@@ -755,18 +755,17 @@ namespace Js
                 // A recent compiler bug 150148 can incorrectly eliminate catch block, temporary workaround
                 if (threadContext == NULL)
                 {
-                    throw (JavascriptExceptionObject*)NULL;
+                    throw JavascriptException(nullptr);
                 }
             }
-            catch (JavascriptExceptionObject* exceptionObject)
+            catch (const JavascriptException& err)
             {
-                pExceptionObject = exceptionObject;
+                pExceptionObject = err.GetAndClear();
             }
 
             if (pExceptionObject)
             {
-                pExceptionObject = pExceptionObject->CloneIfStaticExceptionObject(scriptContext);
-                throw pExceptionObject;
+                JavascriptExceptionOperators::DoThrowCheckClone(pExceptionObject, scriptContext);
             }
         }
         END_JS_RUNTIME_CALL(scriptContext);

+ 3 - 2
lib/Runtime/Library/JavascriptGenerator.cpp

@@ -57,11 +57,12 @@ namespace Js
                 result = JavascriptFunction::CallFunction<1>(this->scriptFunction, this->scriptFunction->GetEntryPoint(), arguments);
                 helper.DidNotThrow();
             }
-            catch (Js::JavascriptExceptionObject* exceptionObj)
+            catch (const JavascriptException& err)
             {
+                Js::JavascriptExceptionObject* exceptionObj = err.GetAndClear();
                 if (!exceptionObj->IsGeneratorReturnException())
                 {
-                    throw exceptionObj;
+                    JavascriptExceptionOperators::DoThrow(exceptionObj, scriptContext);
                 }
                 result = exceptionObj->GetThrownObject(nullptr);
             }

+ 2 - 2
lib/Runtime/Library/JavascriptGeneratorFunction.cpp

@@ -160,9 +160,9 @@ namespace Js
         {
             CALL_FUNCTION(executor, CallInfo(CallFlags_Value, 3), library->GetUndefined(), resolve, reject);
         }
-        catch (JavascriptExceptionObject* ex)
+        catch (const JavascriptException& err)
         {
-            e = ex;
+            e = err.GetAndClear();
         }
 
         if (e != nullptr)

+ 19 - 18
lib/Runtime/Library/JavascriptLibrary.cpp

@@ -107,7 +107,7 @@ namespace Js
     {
         Recycler* recycler = this->GetRecycler();
 
-        // Recycler macros below expect that their arguments will not throw when they're evaluated. 
+        // Recycler macros below expect that their arguments will not throw when they're evaluated.
         // We allocate a lot of types for the built-in prototype objects which we need to store temporarily.
         DynamicType* tempDynamicType = nullptr;
         Type* tempType = StaticType::New(scriptContext, TypeIds_Null, nullptr, nullptr);
@@ -256,7 +256,7 @@ namespace Js
             simdFloat32x4Prototype = RecyclerNew(recycler, JavascriptSIMDObject, nullptr, tempDynamicType);
         }
 #endif
-        
+
         if (scriptContext->GetConfig()->IsES6PrototypeChain())
         {
             datePrototype = DynamicObject::New(recycler,
@@ -626,7 +626,7 @@ namespace Js
             simdUint8x16TypeStatic = StaticType::New(scriptContext, TypeIds_SIMDUint8x16, simdUint8x16Prototype, nullptr);
         }
 #endif
-        
+
         // Initialize Object types
         for (int16 i = 0; i < PreInitializedObjectTypeCount; i++)
         {
@@ -1144,7 +1144,7 @@ namespace Js
             simdUint8x16DisplayString = CreateStringFromCppLiteral(_u("uint8x16"));
         }
 #endif
-        
+
         symbolTypeDisplayString = CreateStringFromCppLiteral(_u("symbol"));
 
         symbolHasInstance = CreateSymbol(BuiltInPropertyRecords::_symbolHasInstance);
@@ -2675,7 +2675,7 @@ namespace Js
 
         /*** Float32x4 ***/
         JavascriptFunction* float32x4Function = library->AddFunctionToLibraryObjectWithPrototype(simdObject, PropertyIds::Float32x4, &SIMDFloat32x4Lib::EntryInfo::Float32x4, 5, library->simdFloat32x4Prototype, nullptr);
-        builtinFuncs[BuiltinFunction::SIMDFloat32x4Lib_Float32x4] = float32x4Function;        
+        builtinFuncs[BuiltinFunction::SIMDFloat32x4Lib_Float32x4] = float32x4Function;
         builtinFuncs[BuiltinFunction::SIMDFloat32x4Lib_Check] = library->AddFunctionToLibraryObject(float32x4Function, PropertyIds::check, &SIMDFloat32x4Lib::EntryInfo::Check, 2);
         builtinFuncs[BuiltinFunction::SIMDFloat32x4Lib_Splat] = library->AddFunctionToLibraryObject(float32x4Function, PropertyIds::splat, &SIMDFloat32x4Lib::EntryInfo::Splat, 2);
 
@@ -2688,7 +2688,7 @@ namespace Js
 #if 0
         library->AddFunctionToLibraryObject(float32x4Function, PropertyIds::fromFloat64x2,     &SIMDFloat32x4Lib::EntryInfo::FromFloat64x2,     2);
         library->AddFunctionToLibraryObject(float32x4Function, PropertyIds::fromFloat64x2Bits, &SIMDFloat32x4Lib::EntryInfo::FromFloat64x2Bits, 2);
-#endif   
+#endif
         builtinFuncs[BuiltinFunction::SIMDFloat32x4Lib_FromInt32x4] = library->AddFunctionToLibraryObject(float32x4Function, PropertyIds::fromInt32x4,       &SIMDFloat32x4Lib::EntryInfo::FromInt32x4,       2);
 
         library->AddFunctionToLibraryObject(float32x4Function, PropertyIds::fromUint32x4,      &SIMDFloat32x4Lib::EntryInfo::FromUint32x4,      2);
@@ -2738,7 +2738,7 @@ namespace Js
         /*** Float64x2 ***/
 #if 0
         JavascriptFunction* float64x2Function = library->AddFunctionToLibraryObject(simdObject, PropertyIds::Float64x2, &SIMDFloat64x2Lib::EntryInfo::Float64x2, 3);
-        
+
         library->AddFunctionToLibraryObject(float64x2Function, PropertyIds::check, &SIMDFloat64x2Lib::EntryInfo::Check, 2);
         library->AddFunctionToLibraryObject(float64x2Function, PropertyIds::splat, &SIMDFloat64x2Lib::EntryInfo::Splat, 2);
         // type conversions
@@ -2822,7 +2822,7 @@ namespace Js
         library->AddFunctionToLibraryObject(int32x4Function, PropertyIds::shiftRightByScalar, &SIMDInt32x4Lib::EntryInfo::ShiftRightByScalar, 3);
         // select
         library->AddFunctionToLibraryObject(int32x4Function, PropertyIds::select, &SIMDInt32x4Lib::EntryInfo::Select, 4);
- 
+
         builtinFuncs[BuiltinFunction::SIMDInt32x4Lib_Load] = library->AddFunctionToLibraryObject(int32x4Function, PropertyIds::load, &SIMDInt32x4Lib::EntryInfo::Load, 3);
         builtinFuncs[BuiltinFunction::SIMDInt32x4Lib_Load1] = library->AddFunctionToLibraryObject(int32x4Function, PropertyIds::load1, &SIMDInt32x4Lib::EntryInfo::Load1, 3);
         builtinFuncs[BuiltinFunction::SIMDInt32x4Lib_Load2] = library->AddFunctionToLibraryObject(int32x4Function, PropertyIds::load2, &SIMDInt32x4Lib::EntryInfo::Load2, 3);
@@ -4441,7 +4441,7 @@ namespace Js
     JavascriptExternalFunction* JavascriptLibrary::CreateExternalConstructor(Js::ExternalMethod entryPoint, PropertyId nameId, InitializeMethod method, unsigned short deferredTypeSlots, bool hasAccessors)
     {
         Assert(nameId >= Js::InternalPropertyIds::Count && scriptContext->IsTrackedPropertyId(nameId));
-        
+
         JavascriptExternalFunction* function = nullptr;
         if (entryPoint != nullptr)
         {
@@ -4450,7 +4450,7 @@ namespace Js
         }
         else
         {
-            function = RecyclerNewEnumClass(this->GetRecycler(), EnumFunctionClass, JavascriptExternalFunction, 
+            function = RecyclerNewEnumClass(this->GetRecycler(), EnumFunctionClass, JavascriptExternalFunction,
                 defaultExternalConstructorFunctionWithDeferredPrototypeType, method, deferredTypeSlots, hasAccessors);
         }
         function->SetFunctionNameId(TaggedInt::ToVarUnchecked(nameId));
@@ -4703,7 +4703,8 @@ namespace Js
             try
             {
                 this->nativeHostPromiseContinuationFunction(taskVar, this->nativeHostPromiseContinuationFunctionState);
-            } catch (...)
+            }
+            catch (...)
             {
                 // Hosts are required not to pass exceptions back across the callback boundary. If
                 // this happens, it is a bug in the host, not something that we are expected to
@@ -4754,10 +4755,10 @@ namespace Js
         try
         {
             this->IntlObject->GetTypeHandler()->EnsureObjectReady(this->IntlObject);
-        } 
-        catch (JavascriptExceptionObject *e)
+        }
+        catch (const JavascriptException& err)
         {
-            caughtExceptionObject = e;
+            caughtExceptionObject = err.GetAndClear();
         }
 
         // Propagate the OOM and SOE exceptions only
@@ -4766,7 +4767,7 @@ namespace Js
             caughtExceptionObject == ThreadContext::GetContextForCurrentThread()->GetPendingSOErrorObject()))
         {
             caughtExceptionObject = caughtExceptionObject->CloneIfStaticExceptionObject(scriptContext);
-            throw caughtExceptionObject;
+            JavascriptExceptionOperators::DoThrow(caughtExceptionObject, scriptContext);
         }
     }
 
@@ -5604,7 +5605,7 @@ namespace Js
         Recycler *recycler = this->GetRecycler();
 
         EnsureArrayPrototypeValuesFunction(); //InitializeArrayPrototype can be delay loaded, which could prevent us from access to array.prototype.values
-        
+
         DynamicType * argumentsType = nullptr;
 
         if (isStrictMode)
@@ -6068,7 +6069,7 @@ namespace Js
         Assert(scriptContext->GetConfig()->IsES6PromiseEnabled());
 
         FunctionInfo* functionInfo = &Js::JavascriptPromise::EntryInfo::CapabilitiesExecutorFunction;
-        DynamicType* type = DynamicType::New(scriptContext, TypeIds_Function, functionPrototype, entryPoint, GetDeferredAnonymousFunctionTypeHandler());        
+        DynamicType* type = DynamicType::New(scriptContext, TypeIds_Function, functionPrototype, entryPoint, GetDeferredAnonymousFunctionTypeHandler());
         JavascriptPromiseCapabilitiesExecutorFunction* function = RecyclerNewEnumClass(this->GetRecycler(), EnumFunctionClass, JavascriptPromiseCapabilitiesExecutorFunction, type, functionInfo, capability);
 
         function->SetPropertyWithAttributes(PropertyIds::length, TaggedInt::ToVarUnchecked(2), PropertyConfigurable, nullptr);
@@ -6732,7 +6733,7 @@ namespace Js
 
     void JavascriptLibrary::BindReference(void * addr)
     {
-        // The last void* is the linklist connecting to next block. 
+        // The last void* is the linklist connecting to next block.
         if (bindRefChunkCurrent == bindRefChunkEnd)
         {
             void** tmpBindRefChunk = RecyclerNewArrayZ(recycler, void *, HeapConstants::ObjectGranularity / sizeof(void *));

+ 20 - 20
lib/Runtime/Library/JavascriptPromise.cpp

@@ -69,7 +69,7 @@ namespace Js
         InitializePromise(promise, &resolve, &reject, scriptContext);
 
         JavascriptExceptionObject* exception = nullptr;
-        
+
         // 9. Let completion be Call(executor, undefined, << resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] >>).
         try
         {
@@ -78,9 +78,9 @@ namespace Js
                 resolve,
                 reject);
         }
-        catch (JavascriptExceptionObject* e)
+        catch (const JavascriptException& err)
         {
-            exception = e;
+            exception = err.GetAndClear();
         }
 
         if (exception != nullptr)
@@ -89,8 +89,8 @@ namespace Js
             //    a. Perform ? Call(resolvingFunctions.[[Reject]], undefined, << completion.[[Value]] >>).
             TryRejectWithExceptionObject(exception, reject, scriptContext);
         }
-        
-        // 11. Return promise. 
+
+        // 11. Return promise.
         return promise;
     }
 
@@ -250,9 +250,9 @@ namespace Js
                 index++;
             });
         }
-        catch (JavascriptExceptionObject* e)
+        catch (const JavascriptException& err)
         {
-            exception = e;
+            exception = err.GetAndClear();
         }
 
         if (exception != nullptr)
@@ -406,9 +406,9 @@ namespace Js
 
             });
         }
-        catch (JavascriptExceptionObject* e)
+        catch (const JavascriptException& err)
         {
-            exception = e;
+            exception = err.GetAndClear();
         }
 
         if (exception != nullptr)
@@ -642,9 +642,9 @@ namespace Js
                         return undefinedVar;
                     }
                 }
-                catch (JavascriptExceptionObject* e)
+                catch (const JavascriptException& err)
                 {
-                    resolution = e->GetThrownObject(scriptContext);
+                    resolution = err.GetAndClear()->GetThrownObject(scriptContext);
 
                     if (resolution == nullptr)
                     {
@@ -743,12 +743,12 @@ namespace Js
                     undefinedVar,
                     argument);
             }
-            catch (JavascriptExceptionObject* e)
+            catch (const JavascriptException& err)
             {
-                exception = e;
+                exception = err.GetAndClear();
             }
         }
-        
+
         if (exception != nullptr)
         {
             return TryRejectWithExceptionObject(exception, promiseCapability->GetReject(), scriptContext);
@@ -818,9 +818,9 @@ namespace Js
                     resolve,
                     reject);
             }
-            catch (JavascriptExceptionObject* e)
+            catch (const JavascriptException& err)
             {
-                exception = e;
+                exception = err.GetAndClear();
             }
         }
 
@@ -910,9 +910,9 @@ namespace Js
         {
             values->DirectSetItemAt(index, x);
         }
-        catch (JavascriptExceptionObject* e)
+        catch (const JavascriptException& err)
         {
-            exception = e;
+            exception = err.GetAndClear();
         }
 
         if (exception != nullptr)
@@ -1029,9 +1029,9 @@ namespace Js
         {
             next = RecyclableObject::FromVar(CALL_FUNCTION(nextFunction, CallInfo(CallFlags_Value, 1), undefinedVar));
         }
-        catch (JavascriptExceptionObject* e)
+        catch (const JavascriptException& err)
         {
-            exception = e;
+            exception = err.GetAndClear();
         }
 
         if (exception != nullptr)