| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | // |
| 5 | |
| 6 | // |
| 7 | // --------------------------------------------------------------------------- |
| 8 | // CLREx.h |
| 9 | // --------------------------------------------------------------------------- |
| 10 | |
| 11 | |
| 12 | #ifndef _CLREX_H_ |
| 13 | #define _CLREX_H_ |
| 14 | |
| 15 | #include <ex.h> |
| 16 | |
| 17 | #include "runtimeexceptionkind.h" |
| 18 | #include "interoputil.h" |
| 19 | |
| 20 | class BaseBind; |
| 21 | class AssemblySpec; |
| 22 | class PEFile; |
| 23 | class PEAssembly; |
| 24 | |
| 25 | struct StackTraceElement |
| 26 | { |
| 27 | UINT_PTR ip; |
| 28 | UINT_PTR sp; |
| 29 | PTR_MethodDesc pFunc; |
| 30 | // TRUE if this element represents the last frame of the foreign |
| 31 | // exception stack trace. |
| 32 | BOOL fIsLastFrameFromForeignStackTrace; |
| 33 | |
| 34 | bool operator==(StackTraceElement const & rhs) const |
| 35 | { |
| 36 | return ip == rhs.ip |
| 37 | && sp == rhs.sp |
| 38 | && pFunc == rhs.pFunc; |
| 39 | } |
| 40 | |
| 41 | bool operator!=(StackTraceElement const & rhs) const |
| 42 | { |
| 43 | return !(*this == rhs); |
| 44 | } |
| 45 | }; |
| 46 | |
| 47 | class StackTraceInfo |
| 48 | { |
| 49 | private: |
| 50 | // for building stack trace info |
| 51 | StackTraceElement* m_pStackTrace; // pointer to stack trace storage |
| 52 | unsigned m_cStackTrace; // size of stack trace storage |
| 53 | unsigned m_dFrameCount; // current frame in stack trace |
| 54 | unsigned m_cDynamicMethodItems; // number of items in the Dynamic Method array |
| 55 | unsigned m_dCurrentDynamicIndex; // index of the next location where the resolver object will be stored |
| 56 | |
| 57 | public: |
| 58 | void Init(); |
| 59 | BOOL IsEmpty(); |
| 60 | void AllocateStackTrace(); |
| 61 | void ClearStackTrace(); |
| 62 | void FreeStackTrace(); |
| 63 | void SaveStackTrace(BOOL bAllowAllocMem, OBJECTHANDLE hThrowable, BOOL bReplaceStack, BOOL bSkipLastElement); |
| 64 | BOOL AppendElement(BOOL bAllowAllocMem, UINT_PTR currentIP, UINT_PTR currentSP, MethodDesc* pFunc, CrawlFrame* pCf); |
| 65 | |
| 66 | void GetLeafFrameInfo(StackTraceElement* pStackTraceElement); |
| 67 | }; |
| 68 | |
| 69 | |
| 70 | // --------------------------------------------------------------------------- |
| 71 | // CLRException represents an exception which has a managed representation. |
| 72 | // It adds the generic method GetThrowable(). |
| 73 | // --------------------------------------------------------------------------- |
| 74 | class CLRException : public Exception |
| 75 | { |
| 76 | friend bool DebugIsEECxxExceptionPointer(void* pv); |
| 77 | friend class CLRLastThrownObjectException; |
| 78 | private: |
| 79 | static const int c_type = 0x434c5220; // 'CLR ' |
| 80 | |
| 81 | protected: |
| 82 | OBJECTHANDLE m_throwableHandle; |
| 83 | |
| 84 | void SetThrowableHandle(OBJECTHANDLE throwable); |
| 85 | OBJECTHANDLE GetThrowableHandle() { return m_throwableHandle; } |
| 86 | |
| 87 | |
| 88 | CLRException(); |
| 89 | public: |
| 90 | ~CLRException(); |
| 91 | |
| 92 | OBJECTREF GetThrowable(); |
| 93 | |
| 94 | // Dynamic type query for catchers |
| 95 | static int GetType() {LIMITED_METHOD_CONTRACT; return c_type; } |
| 96 | virtual int GetInstanceType() { LIMITED_METHOD_CONTRACT; return c_type; } |
| 97 | BOOL IsType(int type) { WRAPPER_NO_CONTRACT; return type == c_type || Exception::IsType(type); } |
| 98 | |
| 99 | BOOL IsSameInstanceType(Exception *pException) |
| 100 | { |
| 101 | STATIC_CONTRACT_MODE_COOPERATIVE; |
| 102 | STATIC_CONTRACT_GC_TRIGGERS; |
| 103 | STATIC_CONTRACT_NOTHROW; |
| 104 | |
| 105 | if (pException->GetInstanceType() != GetInstanceType()) |
| 106 | { |
| 107 | return FALSE; |
| 108 | } |
| 109 | OBJECTREF mine = GetThrowable(); |
| 110 | OBJECTREF other = ((CLRException*)pException)->GetThrowable(); |
| 111 | return mine != NULL && other != NULL && |
| 112 | mine->GetMethodTable() == other->GetMethodTable(); |
| 113 | } |
| 114 | |
| 115 | // Overrides |
| 116 | virtual BOOL IsDomainBound() |
| 117 | { |
| 118 | //@TODO special case for preallocated exceptions? |
| 119 | return TRUE; |
| 120 | } |
| 121 | |
| 122 | HRESULT GetHR(); |
| 123 | IErrorInfo *GetErrorInfo(); |
| 124 | HRESULT SetErrorInfo(); |
| 125 | |
| 126 | void GetMessage(SString &result); |
| 127 | |
| 128 | protected: |
| 129 | |
| 130 | virtual OBJECTREF CreateThrowable() { LIMITED_METHOD_CONTRACT; return NULL; } |
| 131 | |
| 132 | public: // These are really private, but are used by the exception macros |
| 133 | |
| 134 | |
| 135 | // Accessors for all the preallocated exception objects. |
| 136 | static OBJECTREF GetPreallocatedBaseException(); |
| 137 | static OBJECTREF GetPreallocatedOutOfMemoryException(); |
| 138 | static OBJECTREF GetPreallocatedRudeThreadAbortException(); |
| 139 | static OBJECTREF GetPreallocatedThreadAbortException(); |
| 140 | static OBJECTREF GetPreallocatedStackOverflowException(); |
| 141 | static OBJECTREF GetPreallocatedExecutionEngineException(); |
| 142 | |
| 143 | // Accessors for all the preallocated exception handles. |
| 144 | static OBJECTHANDLE GetPreallocatedOutOfMemoryExceptionHandle(); |
| 145 | static OBJECTHANDLE GetPreallocatedRudeThreadAbortExceptionHandle(); |
| 146 | static OBJECTHANDLE GetPreallocatedThreadAbortExceptionHandle(); |
| 147 | static OBJECTHANDLE GetPreallocatedStackOverflowExceptionHandle(); |
| 148 | static OBJECTHANDLE GetPreallocatedExecutionEngineExceptionHandle(); |
| 149 | static OBJECTHANDLE GetPreallocatedHandleForObject(OBJECTREF o); |
| 150 | |
| 151 | // Use these to determine if a handle or object ref is one of the preallocated handles or object refs. |
| 152 | static BOOL IsPreallocatedExceptionObject(OBJECTREF o); |
| 153 | static BOOL IsPreallocatedExceptionHandle(OBJECTHANDLE h); |
| 154 | |
| 155 | // Prefer a new OOM exception if we can make one. If we cannot, then give back the pre-allocated |
| 156 | // one. |
| 157 | static OBJECTREF GetBestOutOfMemoryException(); |
| 158 | |
| 159 | static OBJECTREF GetThrowableFromException(Exception *pException); |
| 160 | static OBJECTREF GetThrowableFromExceptionRecord(EXCEPTION_RECORD *pExceptionRecord); |
| 161 | |
| 162 | class HandlerState : public Exception::HandlerState |
| 163 | { |
| 164 | public: |
| 165 | Thread* m_pThread; |
| 166 | Frame* m_pFrame; |
| 167 | BOOL m_fPreemptiveGCDisabled; |
| 168 | |
| 169 | enum NonNullThread |
| 170 | { |
| 171 | ThreadIsNotNull |
| 172 | }; |
| 173 | |
| 174 | HandlerState(Thread * pThread); |
| 175 | HandlerState(Thread * pThread, NonNullThread dummy); |
| 176 | |
| 177 | void CleanupTry(); |
| 178 | void SetupCatch(INDEBUG_COMMA(__in_z const char * szFile) int lineNum); |
| 179 | #ifdef LOGGING // Use parent implementation that inlines into nothing in retail build |
| 180 | void SucceedCatch(); |
| 181 | #endif |
| 182 | void SetupFinally(); |
| 183 | }; |
| 184 | }; |
| 185 | |
| 186 | // prototype for helper function to get exception object from thread's LastThrownObject. |
| 187 | void GetLastThrownObjectExceptionFromThread_Internal(Exception **ppException); |
| 188 | |
| 189 | |
| 190 | // --------------------------------------------------------------------------- |
| 191 | // EEException is a CLR exception subclass which has purely unmanaged representation. |
| 192 | // The standard methods will not do any GC dangerous operations. Thus you |
| 193 | // can throw and catch such an exception without regard to GC mode. |
| 194 | // --------------------------------------------------------------------------- |
| 195 | |
| 196 | class EEException : public CLRException |
| 197 | { |
| 198 | friend bool DebugIsEECxxExceptionPointer(void* pv); |
| 199 | |
| 200 | private: |
| 201 | static const int c_type = 0x45452020; // 'EE ' |
| 202 | |
| 203 | public: |
| 204 | const RuntimeExceptionKind m_kind; |
| 205 | |
| 206 | EEException(RuntimeExceptionKind kind); |
| 207 | EEException(HRESULT hr); |
| 208 | |
| 209 | // Dynamic type query for catchers |
| 210 | static int GetType() {LIMITED_METHOD_CONTRACT; return c_type; } |
| 211 | virtual int GetInstanceType() { LIMITED_METHOD_CONTRACT; return c_type; } |
| 212 | BOOL IsType(int type) { WRAPPER_NO_CONTRACT; return type == c_type || CLRException::IsType(type); } |
| 213 | |
| 214 | BOOL IsSameInstanceType(Exception *pException) |
| 215 | { |
| 216 | WRAPPER_NO_CONTRACT; |
| 217 | return pException->GetInstanceType() == GetType() && ((EEException*)pException)->m_kind == m_kind; |
| 218 | } |
| 219 | |
| 220 | // Virtual overrides |
| 221 | HRESULT GetHR(); |
| 222 | IErrorInfo *GetErrorInfo(); |
| 223 | void GetMessage(SString &result); |
| 224 | OBJECTREF CreateThrowable(); |
| 225 | |
| 226 | // GetThrowableMessage returns a message to be stored in the throwable. |
| 227 | // Returns FALSE if there is no useful value. |
| 228 | virtual BOOL GetThrowableMessage(SString &result); |
| 229 | |
| 230 | static BOOL GetResourceMessage(UINT iResourceID, SString &result, |
| 231 | const SString &arg1 = SString::Empty(), const SString &arg2 = SString::Empty(), |
| 232 | const SString &arg3 = SString::Empty(), const SString &arg4 = SString::Empty(), |
| 233 | const SString &arg5 = SString::Empty(), const SString &arg6 = SString::Empty()); |
| 234 | |
| 235 | // Note: reKind-->hr is a one-to-many relationship. |
| 236 | // |
| 237 | // each reKind is associated with one or more hresults. |
| 238 | // every hresult is associated with exactly one reKind (with kCOMException being the catch-all.) |
| 239 | static RuntimeExceptionKind GetKindFromHR(HRESULT hr, bool fIsWinRtMode = false); |
| 240 | protected: |
| 241 | static HRESULT GetHRFromKind(RuntimeExceptionKind reKind); |
| 242 | |
| 243 | #ifdef _DEBUG |
| 244 | EEException() : m_kind(kException) |
| 245 | { |
| 246 | // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. |
| 247 | // We need a variant which does not allocate memory. |
| 248 | } |
| 249 | #endif // _DEBUG |
| 250 | |
| 251 | virtual Exception *CloneHelper() |
| 252 | { |
| 253 | WRAPPER_NO_CONTRACT; |
| 254 | return new EEException(m_kind); |
| 255 | } |
| 256 | |
| 257 | }; |
| 258 | |
| 259 | // --------------------------------------------------------------------------- |
| 260 | // EEMessageException is an EE exception subclass composed of a type and |
| 261 | // an unmanaged message of some sort. |
| 262 | // --------------------------------------------------------------------------- |
| 263 | |
| 264 | class EEMessageException : public EEException |
| 265 | { |
| 266 | friend bool DebugIsEECxxExceptionPointer(void* pv); |
| 267 | |
| 268 | private: |
| 269 | HRESULT m_hr; |
| 270 | UINT m_resID; |
| 271 | InlineSString<32> m_arg1; |
| 272 | InlineSString<32> m_arg2; |
| 273 | SString m_arg3; |
| 274 | SString m_arg4; |
| 275 | SString m_arg5; |
| 276 | SString m_arg6; |
| 277 | |
| 278 | public: |
| 279 | EEMessageException(RuntimeExceptionKind kind, UINT resID = 0, LPCWSTR szArg1 = NULL, LPCWSTR szArg2 = NULL, |
| 280 | LPCWSTR szArg3 = NULL, LPCWSTR szArg4 = NULL, LPCWSTR szArg5 = NULL, LPCWSTR szArg6 = NULL); |
| 281 | |
| 282 | EEMessageException(HRESULT hr); |
| 283 | |
| 284 | EEMessageException(HRESULT hr, bool fUseCOMException); |
| 285 | |
| 286 | EEMessageException(HRESULT hr, UINT resID, LPCWSTR szArg1 = NULL, LPCWSTR szArg2 = NULL, LPCWSTR szArg3 = NULL, |
| 287 | LPCWSTR szArg4 = NULL, LPCWSTR szArg5 = NULL, LPCWSTR szArg6 = NULL); |
| 288 | |
| 289 | EEMessageException(RuntimeExceptionKind kind, HRESULT hr, UINT resID, LPCWSTR szArg1 = NULL, LPCWSTR szArg2 = NULL, |
| 290 | LPCWSTR szArg3 = NULL, LPCWSTR szArg4 = NULL, LPCWSTR szArg5 = NULL, LPCWSTR szArg6 = NULL); |
| 291 | |
| 292 | // Virtual overrides |
| 293 | HRESULT GetHR(); |
| 294 | |
| 295 | BOOL GetThrowableMessage(SString &result); |
| 296 | |
| 297 | UINT GetResID(void) { LIMITED_METHOD_CONTRACT; return m_resID; } |
| 298 | |
| 299 | static BOOL IsEEMessageException(Exception *pException) |
| 300 | { |
| 301 | return (*(PVOID*)pException == GetEEMessageExceptionVPtr()); |
| 302 | } |
| 303 | |
| 304 | protected: |
| 305 | |
| 306 | virtual Exception *CloneHelper() |
| 307 | { |
| 308 | WRAPPER_NO_CONTRACT; |
| 309 | return new EEMessageException( |
| 310 | m_kind, m_hr, m_resID, m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6); |
| 311 | } |
| 312 | |
| 313 | |
| 314 | private: |
| 315 | |
| 316 | static PVOID GetEEMessageExceptionVPtr() |
| 317 | { |
| 318 | CONTRACT (PVOID) |
| 319 | { |
| 320 | WRAPPER(THROWS); |
| 321 | WRAPPER(GC_TRIGGERS); |
| 322 | MODE_ANY; |
| 323 | POSTCONDITION(CheckPointer(RETVAL)); |
| 324 | } |
| 325 | CONTRACT_END; |
| 326 | |
| 327 | EEMessageException boilerplate(E_FAIL); |
| 328 | RETURN (PVOID&)boilerplate; |
| 329 | } |
| 330 | |
| 331 | BOOL GetResourceMessage(UINT iResourceID, SString &result); |
| 332 | |
| 333 | #ifdef _DEBUG |
| 334 | EEMessageException() |
| 335 | { |
| 336 | // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. |
| 337 | // We need a variant which does not allocate memory. |
| 338 | } |
| 339 | #endif // _DEBUG |
| 340 | }; |
| 341 | |
| 342 | // --------------------------------------------------------------------------- |
| 343 | // EEResourceException is an EE exception subclass composed of a type and |
| 344 | // an message using a managed exception resource. |
| 345 | // --------------------------------------------------------------------------- |
| 346 | |
| 347 | class EEResourceException : public EEException |
| 348 | { |
| 349 | friend bool DebugIsEECxxExceptionPointer(void* pv); |
| 350 | |
| 351 | private: |
| 352 | InlineSString<32> m_resourceName; |
| 353 | |
| 354 | public: |
| 355 | EEResourceException(RuntimeExceptionKind kind, const SString &resourceName); |
| 356 | |
| 357 | // Unmanaged message text containing only the resource name (GC safe) |
| 358 | void GetMessage(SString &result); |
| 359 | |
| 360 | // Throwable message containig the resource contents (not GC safe) |
| 361 | BOOL GetThrowableMessage(SString &result); |
| 362 | |
| 363 | protected: |
| 364 | |
| 365 | virtual Exception *CloneHelper() |
| 366 | { |
| 367 | WRAPPER_NO_CONTRACT; |
| 368 | return new EEResourceException(m_kind, m_resourceName); |
| 369 | } |
| 370 | |
| 371 | private: |
| 372 | #ifdef _DEBUG |
| 373 | EEResourceException() |
| 374 | { |
| 375 | // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. |
| 376 | // We need a variant which does not allocate memory. |
| 377 | } |
| 378 | #endif // _DEBUG |
| 379 | }; |
| 380 | |
| 381 | // --------------------------------------------------------------------------- |
| 382 | // EECOMException is an EE exception subclass composed of COM-generated data. |
| 383 | // Note that you must ensure that the COM data was not derived from a wrapper |
| 384 | // on a managed Exception object. (If so, you must compose the exception from |
| 385 | // the managed object itself.) |
| 386 | // --------------------------------------------------------------------------- |
| 387 | |
| 388 | struct ExceptionData |
| 389 | { |
| 390 | HRESULT hr; |
| 391 | BSTR bstrDescription; |
| 392 | BSTR bstrSource; |
| 393 | BSTR bstrHelpFile; |
| 394 | DWORD dwHelpContext; |
| 395 | GUID guid; |
| 396 | #ifdef FEATURE_COMINTEROP |
| 397 | BSTR bstrRestrictedError; // Returned from IRestrictedErrorInfo::GetErrorDetails |
| 398 | BSTR bstrReference; // Returned from IRestrictedErrorInfo::GetReference |
| 399 | BSTR bstrCapabilitySid; // Returned from IRestrictedErrorInfo::GetErrorDetails |
| 400 | IUnknown *pRestrictedErrorInfo; // AddRef-ed RestrictedErrorInfo pointer |
| 401 | // We need to keep this alive as long as user need the reference |
| 402 | BOOL bHasLanguageRestrictedErrorInfo; |
| 403 | #endif // FEATURE_COMINTEROP |
| 404 | }; |
| 405 | |
| 406 | class EECOMException : public EEException |
| 407 | { |
| 408 | friend bool DebugIsEECxxExceptionPointer(void* pv); |
| 409 | |
| 410 | private: |
| 411 | ExceptionData m_ED; |
| 412 | |
| 413 | public: |
| 414 | |
| 415 | EECOMException(EXCEPINFO *pExcepInfo); |
| 416 | EECOMException(ExceptionData *pED); |
| 417 | EECOMException( |
| 418 | HRESULT hr, |
| 419 | IErrorInfo *pErrInfo, |
| 420 | bool fUseCOMException, |
| 421 | IRestrictedErrorInfo *pRestrictedErrInfo, |
| 422 | BOOL bHasLanguageRestrictedErrorInfo |
| 423 | COMMA_INDEBUG(BOOL bCheckInProcCCWTearOff = TRUE)); |
| 424 | ~EECOMException(); |
| 425 | |
| 426 | // Virtual overrides |
| 427 | HRESULT GetHR(); |
| 428 | |
| 429 | BOOL GetThrowableMessage(SString &result); |
| 430 | OBJECTREF CreateThrowable(); |
| 431 | |
| 432 | protected: |
| 433 | |
| 434 | virtual Exception *CloneHelper() |
| 435 | { |
| 436 | WRAPPER_NO_CONTRACT; |
| 437 | return new EECOMException(&m_ED); |
| 438 | } |
| 439 | |
| 440 | private: |
| 441 | #ifdef _DEBUG |
| 442 | EECOMException() |
| 443 | { |
| 444 | // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. |
| 445 | // We need a variant which does not allocate memory. |
| 446 | ZeroMemory(&m_ED, sizeof(m_ED)); |
| 447 | } |
| 448 | #endif // _DEBUG |
| 449 | }; |
| 450 | |
| 451 | // --------------------------------------------------------------------------- |
| 452 | // EEFieldException is an EE exception subclass composed of a field |
| 453 | // --------------------------------------------------------------------------- |
| 454 | class EEFieldException : public EEException |
| 455 | { |
| 456 | friend bool DebugIsEECxxExceptionPointer(void* pv); |
| 457 | |
| 458 | private: |
| 459 | FieldDesc *m_pFD; |
| 460 | MethodDesc *m_pAccessingMD; |
| 461 | SString m_additionalContext; |
| 462 | UINT m_messageID; |
| 463 | |
| 464 | public: |
| 465 | EEFieldException(FieldDesc *pField); |
| 466 | EEFieldException(FieldDesc *pField, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID); |
| 467 | |
| 468 | BOOL GetThrowableMessage(SString &result); |
| 469 | virtual BOOL IsDomainBound() {return TRUE;}; |
| 470 | protected: |
| 471 | |
| 472 | virtual Exception *CloneHelper() |
| 473 | { |
| 474 | WRAPPER_NO_CONTRACT; |
| 475 | return new EEFieldException(m_pFD, m_pAccessingMD, m_additionalContext, m_messageID); |
| 476 | } |
| 477 | |
| 478 | private: |
| 479 | #ifdef _DEBUG |
| 480 | EEFieldException() |
| 481 | { |
| 482 | // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. |
| 483 | // We need a variant which does not allocate memory. |
| 484 | } |
| 485 | #endif // _DEBUG |
| 486 | }; |
| 487 | |
| 488 | // --------------------------------------------------------------------------- |
| 489 | // EEMethodException is an EE exception subclass composed of a field |
| 490 | // --------------------------------------------------------------------------- |
| 491 | |
| 492 | class EEMethodException : public EEException |
| 493 | { |
| 494 | friend bool DebugIsEECxxExceptionPointer(void* pv); |
| 495 | |
| 496 | private: |
| 497 | MethodDesc *m_pMD; |
| 498 | MethodDesc *m_pAccessingMD; |
| 499 | SString m_additionalContext; |
| 500 | UINT m_messageID; |
| 501 | |
| 502 | public: |
| 503 | EEMethodException(MethodDesc *pMethod); |
| 504 | EEMethodException(MethodDesc *pMethod, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID); |
| 505 | |
| 506 | BOOL GetThrowableMessage(SString &result); |
| 507 | virtual BOOL IsDomainBound() {return TRUE;}; |
| 508 | protected: |
| 509 | |
| 510 | virtual Exception *CloneHelper() |
| 511 | { |
| 512 | WRAPPER_NO_CONTRACT; |
| 513 | return new EEMethodException(m_pMD, m_pAccessingMD, m_additionalContext, m_messageID); |
| 514 | } |
| 515 | |
| 516 | private: |
| 517 | #ifdef _DEBUG |
| 518 | EEMethodException() |
| 519 | { |
| 520 | // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. |
| 521 | // We need a variant which does not allocate memory. |
| 522 | } |
| 523 | #endif // _DEBUG |
| 524 | }; |
| 525 | |
| 526 | // --------------------------------------------------------------------------- |
| 527 | // EETypeAccessException is an EE exception subclass composed of a type being |
| 528 | // illegally accessed and the method doing the access |
| 529 | // --------------------------------------------------------------------------- |
| 530 | |
| 531 | class EETypeAccessException : public EEException |
| 532 | { |
| 533 | friend bool DebugIsEECxxExceptionPointer(void* pv); |
| 534 | |
| 535 | private: |
| 536 | MethodTable *m_pMT; |
| 537 | MethodDesc *m_pAccessingMD; |
| 538 | SString m_additionalContext; |
| 539 | UINT m_messageID; |
| 540 | |
| 541 | public: |
| 542 | EETypeAccessException(MethodTable *pMT); |
| 543 | EETypeAccessException(MethodTable *pMT, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID); |
| 544 | |
| 545 | BOOL GetThrowableMessage(SString &result); |
| 546 | virtual BOOL IsDomainBound() {return TRUE;}; |
| 547 | protected: |
| 548 | |
| 549 | virtual Exception *CloneHelper() |
| 550 | { |
| 551 | WRAPPER_NO_CONTRACT; |
| 552 | return new EETypeAccessException(m_pMT, m_pAccessingMD, m_additionalContext, m_messageID); |
| 553 | } |
| 554 | |
| 555 | private: |
| 556 | #ifdef _DEBUG |
| 557 | EETypeAccessException() |
| 558 | { |
| 559 | // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. |
| 560 | // We need a variant which does not allocate memory. |
| 561 | } |
| 562 | #endif // _DEBUG |
| 563 | }; |
| 564 | |
| 565 | // --------------------------------------------------------------------------- |
| 566 | // EEArgumentException is an EE exception subclass representing a bad argument |
| 567 | // exception |
| 568 | // --------------------------------------------------------------------------- |
| 569 | |
| 570 | class EEArgumentException : public EEException |
| 571 | { |
| 572 | friend bool DebugIsEECxxExceptionPointer(void* pv); |
| 573 | |
| 574 | private: |
| 575 | InlineSString<32> m_argumentName; |
| 576 | InlineSString<32> m_resourceName; |
| 577 | |
| 578 | public: |
| 579 | EEArgumentException(RuntimeExceptionKind reKind, LPCWSTR pArgName, |
| 580 | LPCWSTR wszResourceName); |
| 581 | |
| 582 | // @todo: GetMessage |
| 583 | |
| 584 | OBJECTREF CreateThrowable(); |
| 585 | |
| 586 | protected: |
| 587 | |
| 588 | virtual Exception *CloneHelper() |
| 589 | { |
| 590 | WRAPPER_NO_CONTRACT; |
| 591 | return new EEArgumentException(m_kind, m_argumentName, m_resourceName); |
| 592 | } |
| 593 | |
| 594 | private: |
| 595 | #ifdef _DEBUG |
| 596 | EEArgumentException() |
| 597 | { |
| 598 | // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. |
| 599 | // We need a variant which does not allocate memory. |
| 600 | } |
| 601 | #endif // _DEBUG |
| 602 | }; |
| 603 | |
| 604 | // --------------------------------------------------------------------------- |
| 605 | // EETypeLoadException is an EE exception subclass representing a type loading |
| 606 | // error |
| 607 | // --------------------------------------------------------------------------- |
| 608 | |
| 609 | class EETypeLoadException : public EEException |
| 610 | { |
| 611 | friend bool DebugIsEECxxExceptionPointer(void* pv); |
| 612 | |
| 613 | private: |
| 614 | InlineSString<64> m_fullName; |
| 615 | SString m_pAssemblyName; |
| 616 | SString m_pMessageArg; |
| 617 | UINT m_resIDWhy; |
| 618 | |
| 619 | public: |
| 620 | EETypeLoadException(LPCUTF8 pszNameSpace, LPCUTF8 pTypeName, |
| 621 | LPCWSTR pAssemblyName, LPCUTF8 pMessageArg, UINT resIDWhy); |
| 622 | |
| 623 | EETypeLoadException(LPCWSTR pFullTypeName, |
| 624 | LPCWSTR pAssemblyName, LPCUTF8 pMessageArg, UINT resIDWhy); |
| 625 | |
| 626 | // virtual overrides |
| 627 | void GetMessage(SString &result); |
| 628 | OBJECTREF CreateThrowable(); |
| 629 | |
| 630 | protected: |
| 631 | |
| 632 | virtual Exception *CloneHelper() |
| 633 | { |
| 634 | WRAPPER_NO_CONTRACT; |
| 635 | return new EETypeLoadException(m_fullName, m_pAssemblyName, m_pMessageArg, m_resIDWhy); |
| 636 | } |
| 637 | |
| 638 | private: |
| 639 | EETypeLoadException(const InlineSString<64> &fullName, LPCWSTR pAssemblyName, |
| 640 | const SString &pMessageArg, UINT resIDWhy) |
| 641 | : EEException(kTypeLoadException), |
| 642 | m_fullName(fullName), |
| 643 | m_pAssemblyName(pAssemblyName), |
| 644 | m_pMessageArg(pMessageArg), |
| 645 | m_resIDWhy(resIDWhy) |
| 646 | { |
| 647 | WRAPPER_NO_CONTRACT; |
| 648 | } |
| 649 | |
| 650 | |
| 651 | #ifdef _DEBUG |
| 652 | EETypeLoadException() |
| 653 | { |
| 654 | // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. |
| 655 | // We need a variant which does not allocate memory. |
| 656 | } |
| 657 | #endif // _DEBUG |
| 658 | }; |
| 659 | |
| 660 | // --------------------------------------------------------------------------- |
| 661 | // EEFileLoadException is an EE exception subclass representing a file loading |
| 662 | // error |
| 663 | // --------------------------------------------------------------------------- |
| 664 | |
| 665 | class EEFileLoadException : public EEException |
| 666 | { |
| 667 | friend bool DebugIsEECxxExceptionPointer(void* pv); |
| 668 | |
| 669 | private: |
| 670 | SString m_name; |
| 671 | void *m_pFusionLog; |
| 672 | HRESULT m_hr; |
| 673 | |
| 674 | |
| 675 | public: |
| 676 | |
| 677 | EEFileLoadException(const SString &name, HRESULT hr, void *pFusionLog = NULL, Exception *pInnerException = NULL); |
| 678 | ~EEFileLoadException(); |
| 679 | |
| 680 | // virtual overrides |
| 681 | HRESULT GetHR() |
| 682 | { |
| 683 | LIMITED_METHOD_DAC_CONTRACT; |
| 684 | return m_hr; |
| 685 | } |
| 686 | void GetMessage(SString &result); |
| 687 | void GetName(SString &result); |
| 688 | OBJECTREF CreateThrowable(); |
| 689 | |
| 690 | static RuntimeExceptionKind GetFileLoadKind(HRESULT hr); |
| 691 | static void DECLSPEC_NORETURN Throw(AssemblySpec *pSpec, HRESULT hr, Exception *pInnerException = NULL); |
| 692 | static void DECLSPEC_NORETURN Throw(PEFile *pFile, HRESULT hr, Exception *pInnerException = NULL); |
| 693 | static void DECLSPEC_NORETURN Throw(LPCWSTR path, HRESULT hr, Exception *pInnerException = NULL); |
| 694 | static void DECLSPEC_NORETURN Throw(PEAssembly *parent, const void *memory, COUNT_T size, HRESULT hr, Exception *pInnerException = NULL); |
| 695 | static BOOL CheckType(Exception* ex); // typeof(EEFileLoadException) |
| 696 | |
| 697 | protected: |
| 698 | virtual Exception *CloneHelper() |
| 699 | { |
| 700 | WRAPPER_NO_CONTRACT; |
| 701 | return new EEFileLoadException(m_name, m_hr, m_pFusionLog); |
| 702 | } |
| 703 | |
| 704 | private: |
| 705 | #ifdef _DEBUG |
| 706 | EEFileLoadException() : m_pFusionLog(NULL) |
| 707 | { |
| 708 | // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. |
| 709 | // We need a variant which does not allocate memory. |
| 710 | } |
| 711 | #endif // _DEBUG |
| 712 | |
| 713 | void SetFileName(const SString &fileName, BOOL removePath); |
| 714 | }; |
| 715 | |
| 716 | // ------------------------------------------------------------------------------------------------------- |
| 717 | // Throw/catch macros. These are derived from the generic EXCEPTION macros, |
| 718 | // but add extra functionality for cleaning up thread state on catches |
| 719 | // |
| 720 | // Usage: |
| 721 | // EX_TRY |
| 722 | // { |
| 723 | // EX_THROW(EEMessageException, (kind, L"Failure message")); |
| 724 | // } |
| 725 | // EX_CATCH |
| 726 | // { |
| 727 | // EX_RETHROW() |
| 728 | // } |
| 729 | // EX_END_CATCH(RethrowTerminalExceptions or RethrowCorruptingExceptions) |
| 730 | // -------------------------------------------------------------------------------------------------------- |
| 731 | |
| 732 | // In DAC builds, we don't want to override the normal utilcode exception handling. |
| 733 | // We're not actually running in the CLR, but we may need access to some CLR-exception |
| 734 | // related data structures elsewhere in this header file in order to analyze CLR |
| 735 | // exceptions that occurred in the target. |
| 736 | #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) |
| 737 | |
| 738 | #define GET_THROWABLE() CLRException::GetThrowableFromException(GET_EXCEPTION()) |
| 739 | |
| 740 | #ifdef FEATURE_CORRUPTING_EXCEPTIONS |
| 741 | |
| 742 | // For the VM folder, we redefine SET_CE_RETHROW_FLAG_FOR_EX_CATCH to also check the |
| 743 | // corruption severity when deciding whether to rethrow them or not. |
| 744 | // |
| 745 | // We also check the global override flag incase it has been set to force pre-V4 behaviour. |
| 746 | // |
| 747 | // Doing the checks for "__fCaughtSO" and "__fCaughtNonCxx" will ensure that we check for |
| 748 | // corruption severity only if the last exception was a managed exception that could have been rethrown in the VM. |
| 749 | // When "(__fCaughtSO == FALSE) && (__fCaughtNonCxx == true)" is true, it implies we are dealing with a managed exception |
| 750 | // inside the VM that is represented by the CLRLastThrownObjectException instance (see EX_TRY/EX_CATCH implementation in VM |
| 751 | // folder to see how CLRLastThrownObjectException is used). |
| 752 | // |
| 753 | // This macro also supports the following scenarios: |
| 754 | // |
| 755 | // Scenario 1 |
| 756 | // ---------- |
| 757 | // |
| 758 | // [VM1] -> [VM2] -> <managed code> |
| 759 | // |
| 760 | // If a managed exception is swallowed by an EX_CATCH in native function VM2, which then returns back |
| 761 | // to native function VM1 that throws, for example, a VM C++ exception, an EX_CATCH(RethrowCorruptingExceptions) |
| 762 | // in VM1 that catches the C++ exception will not rethrow since the last exception was not a managed CSE but |
| 763 | // a C++ exception. |
| 764 | // |
| 765 | // A variation of this is for VM2 to return back in VM1, which calls VM3 that throws a VM C++ exception that |
| 766 | // reaches VM1's EX_CATCH(RethrowCorruptingExceptions). VM1 shouldn't be rethrowing the exception in such a case. |
| 767 | // |
| 768 | // Scenario 2 |
| 769 | // ---------- |
| 770 | // |
| 771 | // [VM1 - RethrowCSE] -> [VM2 - RethrowCSE] -> [VM3 - RethrowCSE] -> <managed code> |
| 772 | // |
| 773 | // When managed code throws a CSE (e.g. TargetInvocationException flagged as CSE), [VM3] will rethrow it and we will |
| 774 | // enter EX_CATCH in VM2 which is supposed to rethrow it as well. But if the implementation of EX_CATCH in VM2 throws |
| 775 | // another VM C++ exception (e.g. EEFileLoadException) *before* rethrow policy is applied, control will reach EX_CATCH |
| 776 | // in VM1 that *shouldn't* rethrow (even though it has RethrowCSE as the policy) since the last exception was a VM C++ |
| 777 | // exception. |
| 778 | // |
| 779 | // Scenario 3 |
| 780 | // ---------- |
| 781 | // |
| 782 | // This is about VM throwing a managed exception that gets handled either within the VM, with or without CLR's managed code |
| 783 | // exception handler coming into the picture. |
| 784 | // |
| 785 | // This is explained in detail (alongwith relevant changes) in the implementation of RaiseTheException (in excep.cpp). |
| 786 | |
| 787 | #undef SET_CE_RETHROW_FLAG_FOR_EX_CATCH |
| 788 | #define SET_CE_RETHROW_FLAG_FOR_EX_CATCH(expr) ((expr == TRUE) && \ |
| 789 | (g_pConfig->LegacyCorruptedStateExceptionsPolicy() == false) && \ |
| 790 | (CEHelper::IsProcessCorruptedStateException(GetCurrentExceptionCode(), FALSE) || \ |
| 791 | ((!__state.DidCatchSO()) && (!__state.DidCatchCxx()) && \ |
| 792 | CEHelper::IsLastActiveExceptionCorrupting(TRUE)))) |
| 793 | |
| 794 | #endif // FEATURE_CORRUPTING_EXCEPTIONS |
| 795 | |
| 796 | #undef EX_TRY |
| 797 | #define EX_TRY \ |
| 798 | EX_TRY_CUSTOM(CLRException::HandlerState, (::GetThreadNULLOk()), CLRLastThrownObjectException) |
| 799 | |
| 800 | #undef EX_TRY_CPP_ONLY |
| 801 | #define EX_TRY_CPP_ONLY \ |
| 802 | EX_TRY_CUSTOM_CPP_ONLY(CLRException::HandlerState, (::GetThreadNULLOk()), CLRLastThrownObjectException) |
| 803 | |
| 804 | // Faster version with thread, skipping GetThread call |
| 805 | #define EX_TRY_THREAD(pThread) \ |
| 806 | EX_TRY_CUSTOM(CLRException::HandlerState, (pThread, CLRException::HandlerState::ThreadIsNotNull), CLRLastThrownObjectException) |
| 807 | |
| 808 | #if defined(_DEBUG) |
| 809 | // Redefine GET_EXCEPTION to validate CLRLastThrownObjectException as much as possible. |
| 810 | #undef GET_EXCEPTION |
| 811 | #define GET_EXCEPTION() (__pException == NULL ? __defaultException.Validate() : __pException.GetValue()) |
| 812 | #endif // _DEBUG |
| 813 | |
| 814 | // When we throw an exception, we need stay in SO-intolerant state and |
| 815 | // probe for sufficient stack so that we don't SO during the processing. |
| 816 | #undef HANDLE_SO_TOLERANCE_FOR_THROW |
| 817 | #define HANDLE_SO_TOLERANCE_FOR_THROW STACK_PROBE_FOR_THROW(GetThread()); |
| 818 | |
| 819 | LONG CLRNoCatchHandler(EXCEPTION_POINTERS* pExceptionInfo, PVOID pv); |
| 820 | |
| 821 | // Re-define the macro to add automatic restoration of the guard page to PAL_EXCEPT and PAL_EXCEPT_FILTER and |
| 822 | // friends. Note: RestoreGuardPage will only do work if the guard page is not present. |
| 823 | #undef PAL_SEH_RESTORE_GUARD_PAGE |
| 824 | #define PAL_SEH_RESTORE_GUARD_PAGE \ |
| 825 | if (__exCode == STATUS_STACK_OVERFLOW) \ |
| 826 | { \ |
| 827 | Thread *__pThread = GetThread(); \ |
| 828 | if (__pThread != NULL) \ |
| 829 | { \ |
| 830 | __pThread->RestoreGuardPage(); \ |
| 831 | } \ |
| 832 | } |
| 833 | |
| 834 | #undef EX_TRY_NOCATCH |
| 835 | #define EX_TRY_NOCATCH(ParamType, paramDef, paramRef) \ |
| 836 | PAL_TRY(ParamType, __EXparam, paramRef) \ |
| 837 | { \ |
| 838 | CLRException::HandlerState __state(::GetThreadNULLOk()); \ |
| 839 | PAL_TRY(ParamType, paramDef, __EXparam) \ |
| 840 | { |
| 841 | |
| 842 | #undef EX_END_NOCATCH |
| 843 | #define EX_END_NOCATCH \ |
| 844 | ; \ |
| 845 | } \ |
| 846 | PAL_FINALLY \ |
| 847 | { \ |
| 848 | __state.CleanupTry(); \ |
| 849 | } \ |
| 850 | PAL_ENDTRY \ |
| 851 | } \ |
| 852 | PAL_EXCEPT_FILTER(CLRNoCatchHandler) \ |
| 853 | { \ |
| 854 | } \ |
| 855 | PAL_ENDTRY |
| 856 | |
| 857 | // |
| 858 | // We need a way to identify an exception in managed code that is rethrown from a new exception in managed code |
| 859 | // when we get into our managed EH logic. Currently, we do that by checking the GC mode. If a thread has preemptive |
| 860 | // GC enabled, but the IP of the exception is in mangaed code, then it must be a rethrow from unmanaged code |
| 861 | // (including CLR code.) Therefore, we toggle the GC mode before the rethrow to indicate that. Note: we don't do |
| 862 | // this if we've caught one of our internal C++ Exception objects: by definition, those don't come from managed |
| 863 | // code, and this allows us to continue to use EX_RETHROW in no-trigger regions. |
| 864 | // |
| 865 | #undef EX_RETHROW |
| 866 | #define EX_RETHROW \ |
| 867 | do \ |
| 868 | { \ |
| 869 | /* don't embed file names in retail to save space and avoid IP */ \ |
| 870 | /* a findstr /n will allow you to locate it in a pinch */ \ |
| 871 | STRESS_LOG1(LF_EH, LL_INFO100, \ |
| 872 | "EX_RETHROW " INDEBUG(__FILE__) " line %d\n", __LINE__); \ |
| 873 | __pException.SuppressRelease(); \ |
| 874 | if ((!__state.DidCatchCxx()) && (GetThread() != NULL)) \ |
| 875 | { \ |
| 876 | if (GetThread()->PreemptiveGCDisabled()) \ |
| 877 | { \ |
| 878 | LOG((LF_EH, LL_INFO10, "EX_RETHROW: going preemptive\n")); \ |
| 879 | GetThread()->EnablePreemptiveGC(); \ |
| 880 | } \ |
| 881 | } \ |
| 882 | PAL_CPP_RETHROW; \ |
| 883 | } while (0) |
| 884 | |
| 885 | // |
| 886 | // Note: we only restore the guard page if we did _not_ catch a C++ exception, since a SO exception is a SEH |
| 887 | // exception. |
| 888 | // |
| 889 | // We also need to restore the SO tolerance state, including restoring the cookie for the current stack guard. |
| 890 | // |
| 891 | // For VM code EX_CATCH calls CLREXception::HandleState::SetupCatch(). |
| 892 | // When Stack guards are disabled we will tear down the process in |
| 893 | // CLREXception::HandleState::SetupCatch() if there is a StackOverflow. |
| 894 | // So we should not reach EX_ENDTRY when there is StackOverflow. |
| 895 | // This change cannot be done in ex.h as for all other code |
| 896 | // CLREXception::HandleState::SetupCatch() is not called rather |
| 897 | // EXception::HandleState::SetupCatch() is called which is a nop. |
| 898 | // |
| 899 | #undef EX_ENDTRY |
| 900 | #define EX_ENDTRY \ |
| 901 | PAL_CPP_ENDTRY \ |
| 902 | SO_INFRASTRUCTURE_CODE(if (__state.DidCatch()) { RESTORE_SO_TOLERANCE_STATE; }) \ |
| 903 | SO_INFRASTRUCTURE_CODE(if (__state.DidCatchSO()) { HANDLE_STACKOVERFLOW_AFTER_CATCH; }) \ |
| 904 | NO_SO_INFRASTRUCTURE_CODE_ASSERTE(!__state.DidCatchSO()) \ |
| 905 | |
| 906 | |
| 907 | // CLRException::GetErrorInfo below invokes GetComIPFromObjectRef |
| 908 | // that invokes ObjHeader::GetSyncBlock which has the INJECT_FAULT contract. |
| 909 | // |
| 910 | // This EX_CATCH_HRESULT implementation can be used in functions |
| 911 | // that have FORBID_FAULT contracts. |
| 912 | // |
| 913 | // However, failure due to OOM (or any other potential exception) in GetErrorInfo |
| 914 | // implies that we couldnt get the interface pointer from the objectRef and would be |
| 915 | // returned NULL. |
| 916 | // |
| 917 | // Thus, the scoped use of FAULT_NOT_FATAL macro. |
| 918 | #undef EX_CATCH_HRESULT |
| 919 | #define EX_CATCH_HRESULT(_hr) \ |
| 920 | EX_CATCH \ |
| 921 | { \ |
| 922 | (_hr) = GET_EXCEPTION()->GetHR(); \ |
| 923 | { \ |
| 924 | FAULT_NOT_FATAL(); \ |
| 925 | HRESULT hrErrorInfo = GET_EXCEPTION()->SetErrorInfo(); \ |
| 926 | if (FAILED(hrErrorInfo)) \ |
| 927 | { \ |
| 928 | (_hr) = hrErrorInfo; \ |
| 929 | } \ |
| 930 | } \ |
| 931 | _ASSERTE(FAILED(_hr)); \ |
| 932 | } \ |
| 933 | EX_END_CATCH(SwallowAllExceptions) |
| 934 | |
| 935 | #endif // !DACCESS_COMPILE && !CROSSGEN_COMPILE |
| 936 | |
| 937 | // When collecting dumps, we need to ignore errors unless the user cancels. |
| 938 | #define EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED \ |
| 939 | EX_CATCH \ |
| 940 | { \ |
| 941 | /* Swallow the exception and keep going unless COR_E_OPERATIONCANCELED */ \ |
| 942 | /* was thrown. Used generating dumps, where rethrow will cancel dump. */ \ |
| 943 | } \ |
| 944 | EX_END_CATCH(RethrowCancelExceptions) |
| 945 | |
| 946 | // Only use this version to wrap single source lines, or it makes debugging painful. |
| 947 | #define CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED(sourceCode) \ |
| 948 | EX_TRY \ |
| 949 | { \ |
| 950 | sourceCode \ |
| 951 | } \ |
| 952 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
| 953 | |
| 954 | |
| 955 | //============================================================================== |
| 956 | // High-level macros for common uses of EX_TRY. Try using these rather |
| 957 | // than the raw EX_TRY constructs. |
| 958 | //============================================================================== |
| 959 | |
| 960 | //=================================================================================== |
| 961 | // Macro for defining external entrypoints such as COM interop boundaries. |
| 962 | // The boundary will catch all exceptions (including terminals) and convert |
| 963 | // them into HR/IErrorInfo pairs as appropriate. |
| 964 | // |
| 965 | // Usage: |
| 966 | // |
| 967 | // HRESULT hr; ;; BEGIN will initialize HR |
| 968 | // BEGIN_EXTERNAL_ENTRYPOINT(&hr) |
| 969 | // <do managed stuff> ;; this part will execute in cooperative GC mode |
| 970 | // END_EXTERNAL_ENTRYPOINT |
| 971 | // return hr; |
| 972 | // |
| 973 | // Comments: |
| 974 | // The BEGIN macro will setup a Thread if necessary. It should only be called |
| 975 | // in preemptive mode. If you are calling it from cooperative mode, this implies |
| 976 | // we are executing "external" code in cooperative mode. The Reentrancy MDA |
| 977 | // complains about this. |
| 978 | // |
| 979 | // Only use this macro for actual boundaries between CLR and |
| 980 | // outside unmanaged code. If you want to connect internal pieces |
| 981 | // of CLR code, use EX_TRY instead. |
| 982 | //=================================================================================== |
| 983 | #ifdef MDA_SUPPORTED |
| 984 | NOINLINE BOOL HasIllegalReentrancyRare(); |
| 985 | #define HAS_ILLEGAL_REENTRANCY() (NULL != MDA_GET_ASSISTANT(Reentrancy) && HasIllegalReentrancyRare()) |
| 986 | #else |
| 987 | #define HAS_ILLEGAL_REENTRANCY() false |
| 988 | #endif |
| 989 | |
| 990 | #define BEGIN_EXTERNAL_ENTRYPOINT(phresult) \ |
| 991 | { \ |
| 992 | HRESULT *__phr = (phresult); \ |
| 993 | *__phr = S_OK; \ |
| 994 | _ASSERTE(GetThread() == NULL || \ |
| 995 | !GetThread()->PreemptiveGCDisabled()); \ |
| 996 | if (HAS_ILLEGAL_REENTRANCY()) \ |
| 997 | { \ |
| 998 | *__phr = COR_E_ILLEGAL_REENTRANCY; \ |
| 999 | } \ |
| 1000 | else \ |
| 1001 | if (!CanRunManagedCode()) \ |
| 1002 | { \ |
| 1003 | *__phr = E_PROCESS_SHUTDOWN_REENTRY; \ |
| 1004 | } \ |
| 1005 | else \ |
| 1006 | { \ |
| 1007 | MAKE_CURRENT_THREAD_AVAILABLE_EX(GetThreadNULLOk()); \ |
| 1008 | if (CURRENT_THREAD == NULL) \ |
| 1009 | { \ |
| 1010 | CURRENT_THREAD = SetupThreadNoThrow(__phr); \ |
| 1011 | } \ |
| 1012 | if (CURRENT_THREAD != NULL) \ |
| 1013 | { \ |
| 1014 | BEGIN_SO_INTOLERANT_CODE_NOTHROW(CURRENT_THREAD, *__phr = COR_E_STACKOVERFLOW); \ |
| 1015 | EX_TRY_THREAD(CURRENT_THREAD); \ |
| 1016 | { \ |
| 1017 | |
| 1018 | #define END_EXTERNAL_ENTRYPOINT \ |
| 1019 | } \ |
| 1020 | EX_CATCH_HRESULT(*__phr); \ |
| 1021 | END_SO_INTOLERANT_CODE; \ |
| 1022 | } \ |
| 1023 | } \ |
| 1024 | } \ |
| 1025 | |
| 1026 | // This macro should be used at the entry points (e.g. COM interop boundaries) |
| 1027 | // where CE's are not expected to get swallowed. |
| 1028 | #define END_EXTERNAL_ENTRYPOINT_RETHROW_CORRUPTING_EXCEPTIONS_EX(fCond) \ |
| 1029 | } \ |
| 1030 | EX_CATCH \ |
| 1031 | { \ |
| 1032 | *__phr = GET_EXCEPTION()->GetHR(); \ |
| 1033 | } \ |
| 1034 | EX_END_CATCH(RethrowCorruptingExceptionsEx(fCond)); \ |
| 1035 | END_SO_INTOLERANT_CODE; \ |
| 1036 | } \ |
| 1037 | } \ |
| 1038 | } \ |
| 1039 | |
| 1040 | // This macro should be used at the entry points (e.g. COM interop boundaries) |
| 1041 | // where CE's are not expected to get swallowed. |
| 1042 | #define END_EXTERNAL_ENTRYPOINT_RETHROW_CORRUPTING_EXCEPTIONS \ |
| 1043 | END_EXTERNAL_ENTRYPOINT_RETHROW_CORRUPTING_EXCEPTIONS_EX(TRUE) |
| 1044 | |
| 1045 | |
| 1046 | |
| 1047 | //============================================================================== |
| 1048 | |
| 1049 | // --------------------------------------------------------------------------- |
| 1050 | // Inline implementations. Pay no attention to that man behind the curtain. |
| 1051 | // --------------------------------------------------------------------------- |
| 1052 | |
| 1053 | inline CLRException::CLRException() |
| 1054 | : m_throwableHandle(NULL) |
| 1055 | { |
| 1056 | LIMITED_METHOD_CONTRACT; |
| 1057 | } |
| 1058 | |
| 1059 | inline void CLRException::SetThrowableHandle(OBJECTHANDLE throwable) |
| 1060 | { |
| 1061 | STRESS_LOG1(LF_EH, LL_INFO100, "in CLRException::SetThrowableHandle: obj = %x\n" , throwable); |
| 1062 | m_throwableHandle = throwable; |
| 1063 | } |
| 1064 | |
| 1065 | inline EEException::EEException(RuntimeExceptionKind kind) |
| 1066 | : m_kind(kind) |
| 1067 | { |
| 1068 | LIMITED_METHOD_CONTRACT; |
| 1069 | } |
| 1070 | |
| 1071 | inline EEException::EEException(HRESULT hr) |
| 1072 | : m_kind(GetKindFromHR(hr)) |
| 1073 | { |
| 1074 | LIMITED_METHOD_CONTRACT; |
| 1075 | } |
| 1076 | |
| 1077 | inline EEMessageException::EEMessageException(HRESULT hr) |
| 1078 | : EEException(GetKindFromHR(hr)), |
| 1079 | m_hr(hr), |
| 1080 | m_resID(0) |
| 1081 | { |
| 1082 | WRAPPER_NO_CONTRACT; |
| 1083 | |
| 1084 | m_arg1.Printf("%.8x" , hr); |
| 1085 | } |
| 1086 | |
| 1087 | inline EEMessageException::EEMessageException(HRESULT hr, bool fUseCOMException) |
| 1088 | : EEException(GetKindFromHR(hr, !fUseCOMException)), |
| 1089 | m_hr(hr), |
| 1090 | m_resID(0) |
| 1091 | { |
| 1092 | WRAPPER_NO_CONTRACT; |
| 1093 | |
| 1094 | m_arg1.Printf("%.8x" , hr); |
| 1095 | } |
| 1096 | |
| 1097 | //----------------------------------------------------------------------------- |
| 1098 | // Constructor with lots of defaults (to 0 / null) |
| 1099 | // kind -- "clr kind" of the exception |
| 1100 | // resid -- resource id for message |
| 1101 | // strings -- substitution text for message |
| 1102 | inline EEMessageException::EEMessageException(RuntimeExceptionKind kind, UINT resID, LPCWSTR szArg1, LPCWSTR szArg2, |
| 1103 | LPCWSTR szArg3, LPCWSTR szArg4, LPCWSTR szArg5, LPCWSTR szArg6) |
| 1104 | : EEException(kind), |
| 1105 | m_hr(EEException::GetHRFromKind(kind)), |
| 1106 | m_resID(resID), |
| 1107 | m_arg1(szArg1), |
| 1108 | m_arg2(szArg2), |
| 1109 | m_arg3(szArg3), |
| 1110 | m_arg4(szArg4), |
| 1111 | m_arg5(szArg5), |
| 1112 | m_arg6(szArg6) |
| 1113 | { |
| 1114 | WRAPPER_NO_CONTRACT; |
| 1115 | } |
| 1116 | |
| 1117 | //----------------------------------------------------------------------------- |
| 1118 | // Constructor with lots of defaults (to 0 / null) |
| 1119 | // hr -- hresult that lead to this exception |
| 1120 | // resid -- resource id for message |
| 1121 | // strings -- substitution text for message |
| 1122 | inline EEMessageException::EEMessageException(HRESULT hr, UINT resID, LPCWSTR szArg1, LPCWSTR szArg2, LPCWSTR szArg3, |
| 1123 | LPCWSTR szArg4, LPCWSTR szArg5, LPCWSTR szArg6) |
| 1124 | : EEException(GetKindFromHR(hr)), |
| 1125 | m_hr(hr), |
| 1126 | m_resID(resID), |
| 1127 | m_arg1(szArg1), |
| 1128 | m_arg2(szArg2), |
| 1129 | m_arg3(szArg3), |
| 1130 | m_arg4(szArg4), |
| 1131 | m_arg5(szArg5), |
| 1132 | m_arg6(szArg6) |
| 1133 | { |
| 1134 | } |
| 1135 | |
| 1136 | //----------------------------------------------------------------------------- |
| 1137 | // Constructor with no defaults |
| 1138 | // kind -- "clr kind" of the exception |
| 1139 | // hr -- hresult that lead to this exception |
| 1140 | // resid -- resource id for message |
| 1141 | // strings -- substitution text for message |
| 1142 | inline EEMessageException::EEMessageException(RuntimeExceptionKind kind, HRESULT hr, UINT resID, LPCWSTR szArg1, |
| 1143 | LPCWSTR szArg2, LPCWSTR szArg3, LPCWSTR szArg4, LPCWSTR szArg5, |
| 1144 | LPCWSTR szArg6) |
| 1145 | : EEException(kind), |
| 1146 | m_hr(hr), |
| 1147 | m_resID(resID), |
| 1148 | m_arg1(szArg1), |
| 1149 | m_arg2(szArg2), |
| 1150 | m_arg3(szArg3), |
| 1151 | m_arg4(szArg4), |
| 1152 | m_arg5(szArg5), |
| 1153 | m_arg6(szArg6) |
| 1154 | { |
| 1155 | WRAPPER_NO_CONTRACT; |
| 1156 | } |
| 1157 | |
| 1158 | |
| 1159 | inline EEResourceException::EEResourceException(RuntimeExceptionKind kind, const SString &resourceName) |
| 1160 | : EEException(kind), |
| 1161 | m_resourceName(resourceName) |
| 1162 | { |
| 1163 | WRAPPER_NO_CONTRACT; |
| 1164 | } |
| 1165 | |
| 1166 | |
| 1167 | inline EEFieldException::EEFieldException(FieldDesc *pField) |
| 1168 | : EEException(kFieldAccessException), |
| 1169 | m_pFD(pField), |
| 1170 | m_pAccessingMD(NULL), |
| 1171 | m_messageID(0) |
| 1172 | { |
| 1173 | WRAPPER_NO_CONTRACT; |
| 1174 | } |
| 1175 | |
| 1176 | inline EEFieldException::EEFieldException(FieldDesc *pField, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID) |
| 1177 | : EEException(kFieldAccessException), |
| 1178 | m_pFD(pField), |
| 1179 | m_pAccessingMD(pAccessingMD), |
| 1180 | m_additionalContext(additionalContext), |
| 1181 | m_messageID(messageID) |
| 1182 | { |
| 1183 | } |
| 1184 | |
| 1185 | inline EEMethodException::EEMethodException(MethodDesc *pMethod) |
| 1186 | : EEException(kMethodAccessException), |
| 1187 | m_pMD(pMethod), |
| 1188 | m_pAccessingMD(NULL), |
| 1189 | m_messageID(0) |
| 1190 | { |
| 1191 | WRAPPER_NO_CONTRACT; |
| 1192 | } |
| 1193 | |
| 1194 | inline EEMethodException::EEMethodException(MethodDesc *pMethod, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID) |
| 1195 | : EEException(kMethodAccessException), |
| 1196 | m_pMD(pMethod), |
| 1197 | m_pAccessingMD(pAccessingMD), |
| 1198 | m_additionalContext(additionalContext), |
| 1199 | m_messageID(messageID) |
| 1200 | { |
| 1201 | } |
| 1202 | |
| 1203 | inline EETypeAccessException::EETypeAccessException(MethodTable *pMT) |
| 1204 | : EEException(kTypeAccessException), |
| 1205 | m_pMT(pMT), |
| 1206 | m_pAccessingMD(NULL), |
| 1207 | m_messageID(0) |
| 1208 | { |
| 1209 | } |
| 1210 | |
| 1211 | inline EETypeAccessException::EETypeAccessException(MethodTable *pMT, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID) |
| 1212 | : EEException(kTypeAccessException), |
| 1213 | m_pMT(pMT), |
| 1214 | m_pAccessingMD(pAccessingMD), |
| 1215 | m_additionalContext(additionalContext), |
| 1216 | m_messageID(messageID) |
| 1217 | { |
| 1218 | } |
| 1219 | |
| 1220 | inline EEArgumentException::EEArgumentException(RuntimeExceptionKind reKind, LPCWSTR pArgName, |
| 1221 | LPCWSTR wszResourceName) |
| 1222 | : EEException(reKind), |
| 1223 | m_argumentName(pArgName), |
| 1224 | m_resourceName(wszResourceName) |
| 1225 | { |
| 1226 | WRAPPER_NO_CONTRACT; |
| 1227 | } |
| 1228 | |
| 1229 | |
| 1230 | class ObjrefException : public CLRException |
| 1231 | { |
| 1232 | friend bool DebugIsEECxxExceptionPointer(void* pv); |
| 1233 | |
| 1234 | public: |
| 1235 | |
| 1236 | ObjrefException(); |
| 1237 | ObjrefException(OBJECTREF throwable); |
| 1238 | |
| 1239 | private: |
| 1240 | static const int c_type = 0x4F522020; // 'OR ' |
| 1241 | |
| 1242 | public: |
| 1243 | // Dynamic type query for catchers |
| 1244 | static int GetType() {LIMITED_METHOD_CONTRACT; return c_type; } |
| 1245 | virtual int GetInstanceType() { LIMITED_METHOD_CONTRACT; return c_type; } |
| 1246 | BOOL IsType(int type) { WRAPPER_NO_CONTRACT; return type == c_type || CLRException::IsType(type); } |
| 1247 | |
| 1248 | protected: |
| 1249 | virtual Exception *CloneHelper() |
| 1250 | { |
| 1251 | WRAPPER_NO_CONTRACT; |
| 1252 | return new ObjrefException(); |
| 1253 | } |
| 1254 | |
| 1255 | virtual Exception *DomainBoundCloneHelper(); |
| 1256 | }; |
| 1257 | |
| 1258 | |
| 1259 | class CLRLastThrownObjectException : public CLRException |
| 1260 | { |
| 1261 | friend bool DebugIsEECxxExceptionPointer(void* pv); |
| 1262 | |
| 1263 | public: |
| 1264 | CLRLastThrownObjectException(); |
| 1265 | |
| 1266 | private: |
| 1267 | static const int c_type = 0x4C544F20; // 'LTO ' |
| 1268 | |
| 1269 | public: |
| 1270 | // Dynamic type query for catchers |
| 1271 | static int GetType() {LIMITED_METHOD_CONTRACT; return c_type; } |
| 1272 | virtual int GetInstanceType() { LIMITED_METHOD_CONTRACT; return c_type; } |
| 1273 | BOOL IsType(int type) { WRAPPER_NO_CONTRACT; return type == c_type || CLRException::IsType(type); } |
| 1274 | |
| 1275 | #if defined(_DEBUG) |
| 1276 | CLRLastThrownObjectException* Validate(); |
| 1277 | #endif // _DEBUG |
| 1278 | |
| 1279 | protected: |
| 1280 | virtual Exception *CloneHelper(); |
| 1281 | |
| 1282 | virtual Exception *DomainBoundCloneHelper(); |
| 1283 | |
| 1284 | virtual OBJECTREF CreateThrowable(); |
| 1285 | }; |
| 1286 | |
| 1287 | // Returns true if the HRESULT maps to the RuntimeExceptionKind (hr => kind). |
| 1288 | bool IsHRESULTForExceptionKind(HRESULT hr, RuntimeExceptionKind kind); |
| 1289 | |
| 1290 | #endif // _CLREX_H_ |
| 1291 | |
| 1292 | |