Fix failfast stacktrace (#15895)
authorSung Yoon Whang <suwhang@microsoft.com>
Sat, 3 Feb 2018 23:58:22 +0000 (15:58 -0800)
committerGitHub <noreply@github.com>
Sat, 3 Feb 2018 23:58:22 +0000 (15:58 -0800)
* attempt to fix stacktrace getting printed twice

* Fix some default parameter issues, and wrong commit from last commit

* Fix build errors, switch call from Debug.Assert to new FailFast FCall

* Fix signature to allow more types of exception title

* cleanup

* Addressing comments from PR

* More PR comments

* remove useless using

* Address comments on GC hole and few naming changes

15 files changed:
src/classlibnative/bcltype/system.cpp
src/classlibnative/bcltype/system.h
src/inc/vptr_list.h
src/mscorlib/shared/System/Diagnostics/Debug.Unix.cs
src/mscorlib/shared/System/Diagnostics/Debug.cs
src/mscorlib/src/System/Diagnostics/Contracts/ContractsBCL.cs
src/mscorlib/src/System/Diagnostics/Debug.Windows.cs
src/mscorlib/src/System/Environment.cs
src/vm/crossgencompile.cpp
src/vm/ecalllist.h
src/vm/eepolicy.cpp
src/vm/eepolicy.h
src/vm/fcall.h
src/vm/frames.h
src/vm/metasig.h

index bb06cec..fdb0416 100644 (file)
@@ -377,7 +377,7 @@ WCHAR g_szFailFastBuffer[256];
 
 // This is the common code for FailFast processing that is wrapped by the two
 // FailFast FCalls below.
-void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, UINT exitCode)
+void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, UINT exitCode, STRINGREF refErrorSourceString)
 {
     CONTRACTL
     {
@@ -391,6 +391,7 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce
     {
         STRINGREF refMesgString;
         EXCEPTIONREF refExceptionForWatsonBucketing;
+        STRINGREF refErrorSourceString;
     } gc;
     ZeroMemory(&gc, sizeof(gc));
 
@@ -398,6 +399,7 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce
     
     gc.refMesgString = refMesgString;
     gc.refExceptionForWatsonBucketing = refExceptionForWatsonBucketing;
+    gc.refErrorSourceString = refErrorSourceString;
 
     // Managed code injected FailFast maps onto the unmanaged version
     // (EEPolicy::HandleFatalError) in the following manner: the exit code is
@@ -423,6 +425,20 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce
     WCHAR  *pszMessage = NULL;
     DWORD   cchMessage = (gc.refMesgString == NULL) ? 0 : gc.refMesgString->GetStringLength();
 
+    WCHAR * errorSourceString = NULL;
+
+    if (gc.refErrorSourceString != NULL) 
+    {
+        DWORD cchErrorSource = gc.refErrorSourceString->GetStringLength();
+        errorSourceString = new (nothrow) WCHAR[cchErrorSource + 1];
+
+        if (errorSourceString != NULL) 
+        {
+            memcpyNoGCRefs(errorSourceString, gc.refErrorSourceString->GetBuffer(), cchErrorSource * sizeof(WCHAR));
+            errorSourceString[cchErrorSource] = W('\0');
+        }
+    }
+
     if (cchMessage < FAIL_FAST_STATIC_BUFFER_LENGTH)
     {
         pszMessage = g_szFailFastBuffer;
@@ -483,7 +499,7 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce
     if (gc.refExceptionForWatsonBucketing != NULL)
         pThread->SetLastThrownObject(gc.refExceptionForWatsonBucketing);
 
-    EEPolicy::HandleFatalError(exitCode, retAddress, pszMessage);
+    EEPolicy::HandleFatalError(exitCode, retAddress, pszMessage, NULL, errorSourceString);
 
     GCPROTECT_END();
 }
@@ -502,7 +518,7 @@ FCIMPL1(VOID, SystemNative::FailFast, StringObject* refMessageUNSAFE)
     UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
     
     // Call the actual worker to perform failfast
-    GenericFailFast(refMessage, NULL, retaddr, COR_E_FAILFAST);
+    GenericFailFast(refMessage, NULL, retaddr, COR_E_FAILFAST, NULL);
 
     HELPER_METHOD_FRAME_END();
 }
@@ -520,7 +536,7 @@ FCIMPL2(VOID, SystemNative::FailFastWithExitCode, StringObject* refMessageUNSAFE
     UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
     
     // Call the actual worker to perform failfast
-    GenericFailFast(refMessage, NULL, retaddr, exitCode);
+    GenericFailFast(refMessage, NULL, retaddr, exitCode, NULL);
 
     HELPER_METHOD_FRAME_END();
 }
@@ -539,7 +555,27 @@ FCIMPL2(VOID, SystemNative::FailFastWithException, StringObject* refMessageUNSAF
     UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
     
     // Call the actual worker to perform failfast
-    GenericFailFast(refMessage, refException, retaddr, COR_E_FAILFAST);
+    GenericFailFast(refMessage, refException, retaddr, COR_E_FAILFAST, NULL);
+
+    HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+FCIMPL3(VOID, SystemNative::FailFastWithExceptionAndSource, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE, StringObject* errorSourceUNSAFE)
+{
+    FCALL_CONTRACT;
+
+    STRINGREF refMessage = (STRINGREF)refMessageUNSAFE;
+    EXCEPTIONREF refException = (EXCEPTIONREF)refExceptionUNSAFE;
+    STRINGREF errorSource = (STRINGREF)errorSourceUNSAFE;
+
+    HELPER_METHOD_FRAME_BEGIN_3(refMessage, refException, errorSource);
+
+    // The HelperMethodFrame knows how to get the return address.
+    UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
+    
+    // Call the actual worker to perform failfast
+    GenericFailFast(refMessage, refException, retaddr, COR_E_FAILFAST, errorSource);
 
     HELPER_METHOD_FRAME_END();
 }
index 87dde89..6a489ba 100644 (file)
@@ -55,6 +55,7 @@ public:
     static FCDECL1(VOID, FailFast, StringObject* refMessageUNSAFE);
     static FCDECL2(VOID, FailFastWithExitCode, StringObject* refMessageUNSAFE, UINT exitCode);
     static FCDECL2(VOID, FailFastWithException, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE);
+    static FCDECL3(VOID, FailFastWithExceptionAndSource, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE, StringObject* errorSourceUNSAFE);
 
     static FCDECL0(StringObject*, _GetModuleFileName);
     static FCDECL0(StringObject*, GetRuntimeDirectory);
@@ -75,7 +76,7 @@ public:
 
 private:
     // Common processing code for FailFast
-    static void GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, UINT exitCode);
+    static void GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, UINT exitCode, STRINGREF errorSource);
 };
 
 /* static */
index ce38156..7b7b5f0 100644 (file)
@@ -72,6 +72,7 @@ VPTR_CLASS(GCFrame)
 VPTR_CLASS(HelperMethodFrame)
 VPTR_CLASS(HelperMethodFrame_1OBJ)
 VPTR_CLASS(HelperMethodFrame_2OBJ)
+VPTR_CLASS(HelperMethodFrame_3OBJ)
 VPTR_CLASS(HelperMethodFrame_PROTECTOBJ)
 #ifdef FEATURE_HIJACK
 VPTR_CLASS(HijackFrame)
index 495f2f7..0554581 100644 (file)
@@ -10,7 +10,7 @@ namespace System.Diagnostics
     {
         private static readonly bool s_shouldWriteToStdErr = Environment.GetEnvironmentVariable("COMPlus_DebugWriteToStdErr") == "1";
 
-        private static void ShowAssertDialog(string stackTrace, string message, string detailMessage)
+        private static void ShowDialog(string stackTrace, string message, string detailMessage, string errorSource)
         {
             if (Debugger.IsAttached)
             {
@@ -22,7 +22,7 @@ namespace System.Diagnostics
                 // Fail in order to avoid anyone catching an exception and masking
                 // an assert failure.
                 var ex = new DebugAssertException(message, detailMessage, stackTrace);
-                Environment.FailFast(ex.Message, ex);
+                Environment.FailFast(ex.Message, ex, errorSource);
             }
         }
 
index 5178f7f..3a29738 100644 (file)
@@ -4,6 +4,8 @@
 
 // Do not remove this, it is needed to retain calls to these conditional methods in release builds
 #define DEBUG
+using System.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
 
 namespace System.Diagnostics
 {
@@ -91,18 +93,34 @@ namespace System.Diagnostics
             if (!condition)
             {
                 string stackTrace;
-
                 try
                 {
-                    stackTrace = Internal.Runtime.Augments.EnvironmentAugments.StackTrace;
+                    stackTrace = new StackTrace(0, true).ToString(System.Diagnostics.StackTrace.TraceFormat.Normal);
                 }
                 catch
                 {
                     stackTrace = "";
                 }
+                WriteLine(FormatAssert(stackTrace, message, detailMessage));
+                s_ShowDialog(stackTrace, message, detailMessage, "Assertion Failed");
+            }
+        }
 
+        internal static void ContractFailure(bool condition, string message, string detailMessage, string failureKindMessage)
+        {
+            if (!condition)
+            {
+                string stackTrace;
+                try
+                {
+                    stackTrace = new StackTrace(0, true).ToString(System.Diagnostics.StackTrace.TraceFormat.Normal);
+                }
+                catch
+                {
+                    stackTrace = "";
+                }
                 WriteLine(FormatAssert(stackTrace, message, detailMessage));
-                s_ShowAssertDialog(stackTrace, message, detailMessage);
+                s_ShowDialog(stackTrace, message, detailMessage, SR.GetResourceString(failureKindMessage));
             }
         }
 
@@ -315,7 +333,8 @@ namespace System.Diagnostics
         }
 
         // internal and not readonly so that the tests can swap this out.
-        internal static Action<string, string, string> s_ShowAssertDialog = ShowAssertDialog;
+        internal static Action<string, string, string, string> s_ShowDialog = ShowDialog;
+
         internal static Action<string> s_WriteCore = WriteCore;
     }
 }
index 45cb9bb..7b74b01 100644 (file)
@@ -317,8 +317,7 @@ namespace System.Runtime.CompilerServices
                 displayMessage = GetDisplayMessage(kind, userMessage, conditionText);
             }
 
-            // TODO: https://github.com/dotnet/coreclr/issues/14867
-            System.Diagnostics.Debug.Fail(displayMessage);
+            System.Diagnostics.Debug.ContractFailure(false, displayMessage, string.Empty, GetResourceNameForFailure(kind));
         }
 
         private static String GetResourceNameForFailure(ContractFailureKind failureKind)
index 483bf51..e1992b4 100644 (file)
@@ -6,7 +6,7 @@ namespace System.Diagnostics
 {
     public static partial class Debug
     {
-        private static void ShowAssertDialog(string stackTrace, string message, string detailMessage)
+        private static void ShowDialog(string stackTrace, string message, string detailMessage, string errorSource)
         {
             if (Debugger.IsAttached)
             {
@@ -18,7 +18,7 @@ namespace System.Diagnostics
                 // Fail in order to avoid anyone catching an exception and masking
                 // an assert failure.
                 var ex = new DebugAssertException(message, detailMessage, stackTrace);
-                Environment.FailFast(ex.Message, ex);
+                Environment.FailFast(ex.Message, ex, errorSource);
             }
         }
 
index 78ec2a0..a4d5aec 100644 (file)
@@ -115,6 +115,9 @@ namespace System
         [MethodImplAttribute(MethodImplOptions.InternalCall)]
         public static extern void FailFast(String message, Exception exception);
 
+        [MethodImplAttribute(MethodImplOptions.InternalCall)]
+        public static extern void FailFast(String message, Exception exception, String errorMessage);
+
 #if FEATURE_WIN32_REGISTRY
         // This is only used by RegistryKey on Windows.
         public static String ExpandEnvironmentVariables(String name)
index 4cb2b5a..b94800d 100644 (file)
@@ -384,7 +384,7 @@ extern "C" UINT_PTR STDCALL GetCurrentIP()
     return 0;
 }
 
-void EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo)
+void EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo, LPCWSTR errorSource)
 { 
     fprintf(stderr, "Fatal error: %08x\n", exitCode);
     ExitProcess(exitCode);
index b9706bb..51c13ac 100644 (file)
@@ -164,6 +164,7 @@ FCFuncStart(gEnvironmentFuncs)
 #endif // FEATURE_COMINTEROP
     FCFuncElementSig("FailFast", &gsig_SM_Str_RetVoid, SystemNative::FailFast)
     FCFuncElementSig("FailFast", &gsig_SM_Str_Exception_RetVoid, SystemNative::FailFastWithException)
+    FCFuncElementSig("FailFast", &gsig_SM_Str_Exception_Str_RetVoid, SystemNative::FailFastWithExceptionAndSource)
 FCFuncEnd()
 
 FCFuncStart(gRuntimeEnvironmentFuncs)
index db47e3f..06c3741 100644 (file)
@@ -1176,18 +1176,27 @@ inline void LogCallstackForLogWorker()
 // Return Value:
 //    None
 //
-inline void DoLogForFailFastException(LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo)
+inline void DoLogForFailFastException(LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo, LPCWSTR errorSource)
 {
     WRAPPER_NO_CONTRACT;
 
     Thread *pThread = GetThread();
     EX_TRY
     {
-        PrintToStdErrA("FailFast: ");
+        if (errorSource == NULL)
+        {
+            PrintToStdErrA("FailFast:");
+        }
+        else 
+        {
+            PrintToStdErrW((WCHAR*)errorSource);
+        }
+
+        PrintToStdErrA("\n");
         PrintToStdErrW((WCHAR*)pszMessage);
         PrintToStdErrA("\n");
 
-        if (pThread)
+        if (pThread && errorSource == NULL)
         {
             PrintToStdErrA("\n");
             LogCallstackForLogWorker();
@@ -1203,7 +1212,7 @@ inline void DoLogForFailFastException(LPCWSTR pszMessage, PEXCEPTION_POINTERS pE
 // Log an error to the event log if possible, then throw up a dialog box.
 //
 
-void EEPolicy::LogFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo)
+void EEPolicy::LogFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo, LPCWSTR errorSource)
 {
     STATIC_CONTRACT_NOTHROW;
     STATIC_CONTRACT_GC_TRIGGERS;
@@ -1214,7 +1223,7 @@ void EEPolicy::LogFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage
     // Log FailFast exception to StdErr
     if (exitCode == (UINT)COR_E_FAILFAST)
     {
-        DoLogForFailFastException(pszMessage, pExceptionInfo);
+        DoLogForFailFastException(pszMessage, pExceptionInfo, errorSource);
     }
 
     if(ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, FailFast))
@@ -1469,7 +1478,7 @@ void DECLSPEC_NORETURN EEPolicy::HandleFatalStackOverflow(EXCEPTION_POINTERS *pE
     UNREACHABLE();
 }
 
-void DECLSPEC_NORETURN EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage /* = NULL */, PEXCEPTION_POINTERS pExceptionInfo /* = NULL */)
+void DECLSPEC_NORETURN EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage /* = NULL */, PEXCEPTION_POINTERS pExceptionInfo /* = NULL */, LPCWSTR errorSource /* = NULL */)
 {
     WRAPPER_NO_CONTRACT;
 
@@ -1519,11 +1528,11 @@ void DECLSPEC_NORETURN EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR addres
         switch (GetEEPolicy()->GetActionOnFailure(FAIL_FatalRuntime))
         {
         case eRudeExitProcess:
-            LogFatalError(exitCode, address, pszMessage, pExceptionInfo);
+            LogFatalError(exitCode, address, pszMessage, pExceptionInfo, errorSource);
                SafeExitProcess(exitCode, TRUE);
             break;
         case eDisableRuntime:
-            LogFatalError(exitCode, address, pszMessage, pExceptionInfo);
+            LogFatalError(exitCode, address, pszMessage, pExceptionInfo, errorSource);
             DisableRuntime(SCA_ExitProcessWhenShutdownComplete);
             break;
         default:
index 4d61fee..a757956 100644 (file)
@@ -124,7 +124,7 @@ public:
 
     static void HandleExitProcess(ShutdownCompleteAction sca = SCA_ExitProcessWhenShutdownComplete);
 
-    static void DECLSPEC_NORETURN HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pMessage=NULL, PEXCEPTION_POINTERS pExceptionInfo= NULL);
+    static void DECLSPEC_NORETURN HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pMessage=NULL, PEXCEPTION_POINTERS pExceptionInfo= NULL, LPCWSTR errorSource=NULL);
 
     static void DECLSPEC_NORETURN HandleFatalStackOverflow(EXCEPTION_POINTERS *pException, BOOL fSkipDebugger = FALSE);
 
@@ -147,7 +147,7 @@ private:
     BOOL IsValidActionForFailure(EClrFailure failure, EPolicyAction action);
     EPolicyAction GetFinalAction(EPolicyAction action, Thread *pThread);
 
-    static void LogFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pMessage, PEXCEPTION_POINTERS pExceptionInfo);
+    static void LogFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pMessage, PEXCEPTION_POINTERS pExceptionInfo, LPCWSTR errorSource);
 
     // IMPORTANT NOTE: only the following two functions should be calling ExitProcessViaShim.
     // - CorHost2::ExitProcess
@@ -188,4 +188,7 @@ extern ULONGLONG GetObjFinalizeStartTime();
 // FailFast with specific error code and exception details
 #define EEPOLICY_HANDLE_FATAL_ERROR_USING_EXCEPTION_INFO(_exitcode, _pExceptionInfo) EEPolicy::HandleFatalError(_exitcode, GetCurrentIP(), NULL, _pExceptionInfo);
 
+// Failfast with specific error code, exception details, and debug info
+#define EEPOLICY_HANDLE_FATAL_ERROR_USING_EXCEPTION_AND_DEBUG_INFO(_exitcode, _pExceptionInfo, _isDebug) EEPolicy::HandleFatalError(_exitcode, GetCurrentIP(), NULL, _pExceptionInfo, _isDebug);
+
 #endif  // EEPOLICY_H_
index c3f026b..c50c6ed 100644 (file)
@@ -653,6 +653,18 @@ LPVOID __FCThrowArgument(LPVOID me, enum RuntimeExceptionKind reKind, LPCWSTR ar
 
 #define HELPER_METHOD_FRAME_BEGIN_2(arg1, arg2) HELPER_METHOD_FRAME_BEGIN_ATTRIB_2(Frame::FRAME_ATTR_NONE, arg1, arg2)
 
+#define HELPER_METHOD_FRAME_BEGIN_ATTRIB_3(attribs, arg1, arg2, arg3)                         \
+        static_assert(sizeof(arg1) == sizeof(OBJECTREF), "GC protecting structs of multiple OBJECTREFs requires a PROTECT variant of the HELPER METHOD FRAME macro");\
+        static_assert(sizeof(arg2) == sizeof(OBJECTREF), "GC protecting structs of multiple OBJECTREFs requires a PROTECT variant of the HELPER METHOD FRAME macro");\
+        static_assert(sizeof(arg3) == sizeof(OBJECTREF), "GC protecting structs of multiple OBJECTREFs requires a PROTECT variant of the HELPER METHOD FRAME macro");\
+        HELPER_METHOD_FRAME_BEGIN_EX(                                                   \
+            return,                                                                     \
+            HELPER_FRAME_DECL(3)(HELPER_FRAME_ARGS(attribs),                            \
+                (OBJECTREF*) &arg1, (OBJECTREF*) &arg2, (OBJECTREF*) &arg3),                                \
+            HELPER_METHOD_POLL(),TRUE)
+
+#define HELPER_METHOD_FRAME_BEGIN_3(arg1, arg2, arg3) HELPER_METHOD_FRAME_BEGIN_ATTRIB_3(Frame::FRAME_ATTR_NONE, arg1, arg2, arg3)
+
 #define HELPER_METHOD_FRAME_BEGIN_PROTECT(gc)                                           \
         HELPER_METHOD_FRAME_BEGIN_EX(                                                   \
             return,                                                                     \
index 2ee197d..66520a8 100644 (file)
@@ -60,6 +60,8 @@
 //    | |
 //    + +-HelperMethodFrame_2OBJ- reports additional object references
 //    | |
+//    + +-HelperMethodFrame_3OBJ- reports additional object references
+//    | |
 //    + +-HelperMethodFrame_PROTECTOBJ - reports additional object references
 //    |
 //    +-TransitionFrame         - this abstract frame represents a transition from
@@ -215,6 +217,7 @@ FRAME_TYPE_NAME(FuncEvalFrame)
 FRAME_TYPE_NAME(HelperMethodFrame)
 FRAME_TYPE_NAME(HelperMethodFrame_1OBJ)
 FRAME_TYPE_NAME(HelperMethodFrame_2OBJ)
+FRAME_TYPE_NAME(HelperMethodFrame_3OBJ)
 FRAME_TYPE_NAME(HelperMethodFrame_PROTECTOBJ)
 FRAME_ABSTRACT_TYPE_NAME(FramedMethodFrame)
 FRAME_TYPE_NAME(SecureDelegateFrame)
@@ -1549,6 +1552,72 @@ private:
     DEFINE_VTABLE_GETTER_AND_CTOR_AND_DTOR(HelperMethodFrame_2OBJ)
 };
 
+//-----------------------------------------------------------------------------
+// HelperMethodFrame_3OBJ
+//-----------------------------------------------------------------------------
+
+class HelperMethodFrame_3OBJ : public HelperMethodFrame
+{
+    VPTR_VTABLE_CLASS(HelperMethodFrame_3OBJ, HelperMethodFrame)
+
+public:
+#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
+    HelperMethodFrame_3OBJ(
+            void* fCallFtnEntry, 
+            unsigned attribs, 
+            OBJECTREF* aGCPtr1, 
+            OBJECTREF* aGCPtr2,
+            OBJECTREF* aGCPtr3)
+        : HelperMethodFrame(fCallFtnEntry, attribs)
+    {
+        LIMITED_METHOD_CONTRACT;
+        gcPtrs[0] = aGCPtr1;
+        gcPtrs[1] = aGCPtr2;
+        gcPtrs[2] = aGCPtr3;
+        INDEBUG(Thread::ObjectRefProtected(aGCPtr1);)
+        INDEBUG(Thread::ObjectRefProtected(aGCPtr2);)
+        INDEBUG(Thread::ObjectRefProtected(aGCPtr3);)
+        INDEBUG((*aGCPtr1).Validate();)
+        INDEBUG((*aGCPtr2).Validate();)
+        INDEBUG((*aGCPtr3).Validate();)
+    }
+#endif
+
+    virtual void GcScanRoots(promote_func *fn, ScanContext* sc)
+    {
+        WRAPPER_NO_CONTRACT;
+        DoPromote(fn, sc, gcPtrs[0], FALSE);
+        DoPromote(fn, sc, gcPtrs[1], FALSE);
+        DoPromote(fn, sc, gcPtrs[2], FALSE);
+        HelperMethodFrame::GcScanRoots(fn, sc);
+    }
+
+#ifdef _DEBUG
+#ifndef DACCESS_COMPILE
+    void Pop()
+    {
+        WRAPPER_NO_CONTRACT;
+        HelperMethodFrame::Pop();
+        Thread::ObjectRefNew(gcPtrs[0]);
+        Thread::ObjectRefNew(gcPtrs[1]);
+        Thread::ObjectRefNew(gcPtrs[2]);
+    }
+#endif // DACCESS_COMPILE
+
+    BOOL Protects(OBJECTREF *ppORef)
+    {
+        LIMITED_METHOD_CONTRACT;
+        return (ppORef == gcPtrs[0] || ppORef == gcPtrs[1] || ppORef == gcPtrs[2]) ? TRUE : FALSE;
+    }
+#endif
+
+private:
+    PTR_OBJECTREF gcPtrs[3];
+
+    // Keep as last entry in class
+    DEFINE_VTABLE_GETTER_AND_CTOR_AND_DTOR(HelperMethodFrame_3OBJ)
+};
+
 
 //-----------------------------------------------------------------------------
 // HelperMethodFrame_PROTECTOBJ
@@ -3423,6 +3492,10 @@ public:
     FORCEINLINE FrameWithCookie(void* fCallFtnEntry, unsigned attribs, OBJECTREF * aGCPtr1, OBJECTREF * aGCPtr2) :
         m_frame(fCallFtnEntry, attribs, aGCPtr1, aGCPtr2) { WRAPPER_NO_CONTRACT; }
 
+    // HelperMethodFrame_3OBJ
+    FORCEINLINE FrameWithCookie(void* fCallFtnEntry, unsigned attribs, OBJECTREF * aGCPtr1, OBJECTREF * aGCPtr2, OBJECTREF * aGCPtr3) :
+        m_frame(fCallFtnEntry, attribs, aGCPtr1, aGCPtr2, aGCPtr3) { WRAPPER_NO_CONTRACT; }
+
     // HelperMethodFrame_PROTECTOBJ
     FORCEINLINE FrameWithCookie(void* fCallFtnEntry, unsigned attribs, OBJECTREF* pObjRefs, int numObjRefs) :
         m_frame(fCallFtnEntry, attribs, pObjRefs, numObjRefs) { WRAPPER_NO_CONTRACT; }
index 5218eb7..47dd024 100644 (file)
@@ -560,6 +560,7 @@ DEFINE_METASIG(SM(Obj_Bool_RetVoid, j F, v))
 DEFINE_METASIG(SM(Str_RetVoid, s, v))
 DEFINE_METASIG(SM(Str_Uint_RetVoid, s K, v))
 DEFINE_METASIG_T(SM(Str_Exception_RetVoid, s C(EXCEPTION), v))
+DEFINE_METASIG_T(SM(Str_Exception_Str_RetVoid, s C(EXCEPTION) s, v))
 
 // fields - e.g.:
 // DEFINE_METASIG(Fld(PtrVoid, P(v)))