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// Contract.inl
6//
7
8// ! I am the owner for issues in the contract *infrastructure*, not for every
9// ! CONTRACT_VIOLATION dialog that comes up. If you interrupt my work for a routine
10// ! CONTRACT_VIOLATION, you will become the new owner of this file.
11// ---------------------------------------------------------------------------
12
13#ifndef CONTRACT_INL_
14#define CONTRACT_INL_
15
16#include "contract.h"
17#include <string.h>
18
19#ifndef _countof
20#define _countof(x) (sizeof(x)/sizeof(x[0]))
21#endif
22
23#ifdef ENABLE_CONTRACTS_IMPL
24
25#ifdef FEATURE_STACK_PROBE
26/* FLAG to turn on/off dynamic SO Contract checking */
27extern BOOL g_EnableDefaultRWValidation;
28
29/* Used to report any code with SO_NOT_MAINLINE being run in a test environment
30 * with COMPLUS_NO_SO_NOT_MAINLINE enabled
31 */
32void SONotMainlineViolation(const char *szFunction, const char *szFile, int lineNum);
33
34/* Wrapper over SOTolerantViolation(). Used to report SO_Intolerant functions being called
35 * from SO_tolerant funcs without stack probing.
36 */
37void SoTolerantViolationHelper(const char *szFunction,
38 const char *szFile,
39 int lineNum);
40#endif
41
42
43inline void BaseContract::DoChecks(UINT testmask, __in_z const char *szFunction, __in_z const char *szFile, int lineNum)
44{
45 STATIC_CONTRACT_DEBUG_ONLY;
46 STATIC_CONTRACT_NOTHROW;
47 STATIC_CONTRACT_GC_NOTRIGGER;
48
49 // Cache the pointer to our ClrDebugState if it's not already cached.
50 // Derived types could set up this ptr before calling BaseContract::DoChecks if they have access to the Thread ptr
51 if (m_pClrDebugState == NULL)
52 {
53 m_pClrDebugState = GetClrDebugState();
54 }
55
56 // Save the incoming contents for restoration in the destructor
57 m_IncomingClrDebugState = *m_pClrDebugState;
58
59 m_testmask = testmask; // Save the testmask for destructor
60
61 // Setup the new stack record.
62 m_contractStackRecord.m_szFunction = szFunction;
63 m_contractStackRecord.m_szFile = szFile;
64 m_contractStackRecord.m_lineNum = lineNum;
65 m_contractStackRecord.m_testmask = testmask;
66 m_contractStackRecord.m_construct = "CONTRACT";
67
68 // Link the new ContractStackRecord into the chain for this thread.
69 m_pClrDebugState->LinkContractStackTrace( &m_contractStackRecord );
70
71 if (testmask & DEBUG_ONLY_Yes)
72 {
73 m_pClrDebugState->SetDebugOnly();
74 }
75
76#ifdef FEATURE_STACK_PROBE //Dynamic SO contract checks only required when SO infrastructure is present.
77
78 if (testmask & SO_MAINLINE_No)
79 {
80 static DWORD dwCheckNotMainline = -1;
81
82 // Some tests should never hit an SO_NOT_MAINLINE contract
83 if (dwCheckNotMainline == -1)
84 dwCheckNotMainline = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_NO_SO_NOT_MAINLINE);
85
86
87 if (dwCheckNotMainline)
88 {
89 SONotMainlineViolation(m_contractStackRecord.m_szFunction,
90 m_contractStackRecord.m_szFile,
91 m_contractStackRecord.m_lineNum);
92 }
93
94 m_pClrDebugState->SetSONotMainline();
95 }
96
97#endif // FEATURE_STACK_PROBE
98
99 switch (testmask & FAULT_Mask)
100 {
101 case FAULT_Forbid:
102 m_pClrDebugState->ViolationMaskReset( FaultViolation|FaultNotFatal );
103 m_pClrDebugState->SetFaultForbid();
104 break;
105
106 case FAULT_Inject:
107 if (m_pClrDebugState->IsFaultForbid() &&
108 !(m_pClrDebugState->ViolationMask() & (FaultViolation|FaultNotFatal|BadDebugState)))
109 {
110 CONTRACT_ASSERT("INJECT_FAULT called in a FAULTFORBID region.",
111 BaseContract::FAULT_Forbid,
112 BaseContract::FAULT_Mask,
113 m_contractStackRecord.m_szFunction,
114 m_contractStackRecord.m_szFile,
115 m_contractStackRecord.m_lineNum);
116 }
117 break;
118
119 case FAULT_Disabled:
120 // Nothing
121 break;
122
123 default:
124 UNREACHABLE();
125 }
126
127 switch (testmask & THROWS_Mask)
128 {
129 case THROWS_Yes:
130 m_pClrDebugState->CheckOkayToThrow(m_contractStackRecord.m_szFunction,
131 m_contractStackRecord.m_szFile,
132 m_contractStackRecord.m_lineNum);
133 break;
134
135 case THROWS_No:
136 m_pClrDebugState->ViolationMaskReset( ThrowsViolation );
137 m_pClrDebugState->ResetOkToThrow();
138 break;
139
140 case THROWS_Disabled:
141 // Nothing
142 break;
143
144 default:
145 UNREACHABLE();
146 }
147
148 // LOADS_TYPE check
149 switch (testmask & LOADS_TYPE_Mask)
150 {
151 case LOADS_TYPE_Disabled:
152 // Nothing
153 break;
154
155 default:
156 {
157 UINT newTypeLoadLevel = ((testmask & LOADS_TYPE_Mask) >> LOADS_TYPE_Shift) - 1;
158 if (newTypeLoadLevel > m_pClrDebugState->GetMaxLoadTypeLevel())
159 {
160 if (!((LoadsTypeViolation|BadDebugState) & m_pClrDebugState->ViolationMask()))
161 {
162 CONTRACT_ASSERT("A function tried to load a type past the current level limit.",
163 (m_pClrDebugState->GetMaxLoadTypeLevel() + 1) << LOADS_TYPE_Shift,
164 Contract::LOADS_TYPE_Mask,
165 m_contractStackRecord.m_szFunction,
166 m_contractStackRecord.m_szFile,
167 m_contractStackRecord.m_lineNum
168 );
169 }
170 }
171 m_pClrDebugState->SetMaxLoadTypeLevel(newTypeLoadLevel);
172 m_pClrDebugState->ViolationMaskReset(LoadsTypeViolation);
173
174 }
175 break;
176 }
177
178#ifdef FEATURE_STACK_PROBE
179
180 switch (testmask & SO_TOLERANCE_Mask)
181 {
182 case SO_TOLERANT_No:
183 if (g_EnableDefaultRWValidation)
184 {
185 m_pClrDebugState->CheckIfSOIntolerantOK(m_contractStackRecord.m_szFunction,
186 m_contractStackRecord.m_szFile,
187 m_contractStackRecord.m_lineNum);
188 }
189 break;
190
191 case SO_TOLERANT_Yes:
192 case SO_TOLERANCE_Disabled:
193 // Nothing
194 break;
195
196 default:
197 UNREACHABLE();
198 }
199
200#endif // FEATURE_STACK_PROBE
201
202 if (testmask & CAN_RETAKE_LOCK_No)
203 {
204 m_pClrDebugState->OnEnterCannotRetakeLockFunction();
205 m_pClrDebugState->ResetOkToRetakeLock();
206 }
207
208 switch (testmask & CAN_TAKE_LOCK_Mask)
209 {
210 case CAN_TAKE_LOCK_Yes:
211 m_pClrDebugState->CheckOkayToLock(m_contractStackRecord.m_szFunction,
212 m_contractStackRecord.m_szFile,
213 m_contractStackRecord.m_lineNum);
214 break;
215
216 case CAN_TAKE_LOCK_No:
217 m_pClrDebugState->ViolationMaskReset(TakesLockViolation);
218 m_pClrDebugState->ResetOkToLock();
219 break;
220
221 case CAN_TAKE_LOCK_Disabled:
222 // Nothing
223 break;
224
225 default:
226 UNREACHABLE();
227 }
228
229}
230
231FORCEINLINE BOOL BaseContract::CheckFaultInjection()
232{
233 // ??? use m_tag to see if we should trigger an injection
234 return FALSE;
235}
236
237inline BOOL ClrDebugState::CheckOkayToThrowNoAssert()
238{
239 if (!IsOkToThrow() && !(m_violationmask & (ThrowsViolation|BadDebugState)))
240 {
241 return FALSE;
242 }
243 return TRUE;
244}
245
246inline void ClrDebugState::CheckOkayToThrow(__in_z const char *szFunction, __in_z const char *szFile, int lineNum)
247{
248 STATIC_CONTRACT_DEBUG_ONLY;
249 STATIC_CONTRACT_NOTHROW;
250 STATIC_CONTRACT_GC_NOTRIGGER;
251
252 if (!CheckOkayToThrowNoAssert())
253 {
254 CONTRACT_ASSERT("THROWS called in a NOTHROW region.",
255 BaseContract::THROWS_No,
256 BaseContract::THROWS_Mask,
257 szFunction,
258 szFile,
259 lineNum);
260 }
261}
262
263inline BOOL ClrDebugState::CheckOkayToLockNoAssert()
264{
265 STATIC_CONTRACT_DEBUG_ONLY;
266 STATIC_CONTRACT_NOTHROW;
267 STATIC_CONTRACT_GC_NOTRIGGER;
268
269 if (!IsOkToLock() && !(m_violationmask & (TakesLockViolation|BadDebugState)))
270 {
271 return FALSE;
272 }
273 return TRUE;
274}
275
276inline void ClrDebugState::CheckOkayToLock(__in_z const char *szFunction, __in_z const char *szFile, int lineNum)
277{
278 STATIC_CONTRACT_DEBUG_ONLY;
279 STATIC_CONTRACT_NOTHROW;
280 STATIC_CONTRACT_GC_NOTRIGGER;
281
282 if (!CheckOkayToLockNoAssert())
283 {
284
285 CONTRACT_ASSERT("CAN_TAKE_LOCK called in a CANNOT_TAKE_LOCK region.",
286 BaseContract::CAN_TAKE_LOCK_No,
287 BaseContract::CAN_TAKE_LOCK_Mask,
288 szFunction,
289 szFile,
290 lineNum);
291
292 }
293}
294
295
296inline void ClrDebugState::LockTaken(DbgStateLockType dbgStateLockType,
297 UINT cTakes,
298 void * pvLock,
299 __in_z const char * szFunction,
300 __in_z const char * szFile,
301 int lineNum)
302{
303 STATIC_CONTRACT_DEBUG_ONLY;
304 STATIC_CONTRACT_NOTHROW;
305 STATIC_CONTRACT_GC_NOTRIGGER;
306
307 if ((m_violationmask & BadDebugState) != 0)
308 {
309 return;
310 }
311
312 // Assert if we're taking a lock in a CANNOT_TAKE_LOCK scope. Even if this asserts, we'll
313 // continue to the following lines to track the lock
314 CheckOkayToLock(szFunction, szFile, lineNum);
315
316 _ASSERTE(GetDbgStateLockData() != NULL);
317
318 if (!IsOkToRetakeLock())
319 {
320 if (m_LockState.IsLockRetaken(pvLock))
321 {
322 CONTRACT_ASSERT("You cannot take a lock which is already being held in a CANNOT_RETAKE_LOCK scope.",
323 BaseContract::CAN_RETAKE_LOCK_No,
324 BaseContract::CAN_RETAKE_LOCK_No,
325 szFunction,
326 szFile,
327 lineNum);
328 }
329 }
330
331 GetDbgStateLockData()->LockTaken(dbgStateLockType, cTakes, pvLock, szFunction, szFile, lineNum);
332}
333
334inline void ClrDebugState::LockReleased(DbgStateLockType dbgStateLockType, UINT cReleases, void * pvLock)
335{
336 STATIC_CONTRACT_DEBUG_ONLY;
337 STATIC_CONTRACT_NOTHROW;
338 STATIC_CONTRACT_GC_NOTRIGGER;
339
340 if ((m_violationmask & BadDebugState) != 0)
341 {
342 return;
343 }
344
345 _ASSERTE(GetDbgStateLockData() != NULL);
346
347 if (!IsOkToRetakeLock())
348 {
349 // It is very suspicious to release any locks being hold at the time this function was
350 // called in a CANNOT_RETAKE_LOCK scope
351 _ASSERTE(m_LockState.IsSafeToRelease(cReleases));
352 }
353
354 GetDbgStateLockData()->LockReleased(dbgStateLockType, cReleases, pvLock);
355}
356
357inline UINT ClrDebugState::GetLockCount(DbgStateLockType dbgStateLockType)
358{
359 STATIC_CONTRACT_DEBUG_ONLY;
360 STATIC_CONTRACT_NOTHROW;
361 STATIC_CONTRACT_GC_NOTRIGGER;
362
363 if ((m_violationmask & BadDebugState) != 0)
364 {
365 return 0;
366 }
367
368 _ASSERTE(GetDbgStateLockData() != NULL);
369 return GetDbgStateLockData()->GetLockCount(dbgStateLockType);
370}
371
372inline UINT ClrDebugState::GetCombinedLockCount()
373{
374 STATIC_CONTRACT_DEBUG_ONLY;
375 STATIC_CONTRACT_NOTHROW;
376 STATIC_CONTRACT_GC_NOTRIGGER;
377
378 if ((m_violationmask & BadDebugState) != 0)
379 {
380 return 0;
381 }
382
383 _ASSERTE(GetDbgStateLockData() != NULL);
384 return GetDbgStateLockData()->GetCombinedLockCount();
385}
386
387inline void DbgStateLockData::LockTaken(DbgStateLockType dbgStateLockType,
388 UINT cTakes, // # times we're taking this lock (usually 1)
389 void * pvLock,
390 __in_z const char * szFunction,
391 __in_z const char * szFile,
392 int lineNum)
393{
394 STATIC_CONTRACT_NOTHROW;
395 STATIC_CONTRACT_GC_NOTRIGGER;
396
397 // Technically the lock's already been taken before we're called, but it's
398 // handy to have this contract here at the leaf end of the call chain, as it
399 // ensures SCAN will enforce that no use of the LOCK_TAKEN macros occurs
400 // in a CANNOT_TAKE_LOCK scope (as LOCK_TAKEN macros just call this function).
401 STATIC_CONTRACT_CAN_TAKE_LOCK;
402
403 // Valid enum?
404 _ASSERTE(UINT(dbgStateLockType) < kDbgStateLockType_Count);
405
406 UINT cCombinedLocks = GetCombinedLockCount();
407
408 // Are we exceeding the threshold for what we can store in m_rgTakenLockInfos?
409 // If so, assert a warning, but we'll deal with it.
410 if ((cCombinedLocks <= _countof(m_rgTakenLockInfos)) &&
411 (cCombinedLocks + cTakes > _countof(m_rgTakenLockInfos)))
412 {
413 // Actually, for now we are NOT asserting until I can dedicate more time
414 // to this. Some class loader code paths legally hold many simultaneous
415 // locks (>10). Need to do further analysis on reasonable value to set
416 // for kMaxAllowedSimultaneousLocks. Since lock order checking is turned
417 // off for the moment anyway, exceeding kMaxAllowedSimultaneousLocks
418 // has no consequences for now anyway.
419 }
420
421 m_rgcLocksTaken[dbgStateLockType] += cTakes;
422
423 // Remember as many of these new entrances in m_rgTakenLockInfos as we can
424 for (UINT i = cCombinedLocks;
425 i < min (_countof(m_rgTakenLockInfos), cCombinedLocks + cTakes);
426 i++)
427 {
428 m_rgTakenLockInfos[i].m_pvLock = pvLock;
429 m_rgTakenLockInfos[i].m_szFile = szFile;
430 m_rgTakenLockInfos[i].m_lineNum = lineNum;
431 }
432}
433
434inline void DbgStateLockData::LockReleased(DbgStateLockType dbgStateLockType, UINT cReleases, void * pvLock)
435{
436 // Valid enum?
437 _ASSERTE(UINT(dbgStateLockType) < kDbgStateLockType_Count);
438
439 if (cReleases > m_rgcLocksTaken[dbgStateLockType])
440 {
441 _ASSERTE(!"Releasing lock(s) that were never taken");
442 cReleases = m_rgcLocksTaken[dbgStateLockType];
443 }
444
445 UINT cCombinedLocks = GetCombinedLockCount();
446
447 // If lock count is within range of our m_rgTakenLockInfos buffer size, then
448 // make sure we're releasing locks in reverse order of how we took them
449 for (UINT i = cCombinedLocks - cReleases;
450 i < min (_countof(m_rgTakenLockInfos), cCombinedLocks);
451 i++)
452 {
453 if (m_rgTakenLockInfos[i].m_pvLock != pvLock)
454 {
455 // Ok, I lied. We're not really checking that we're releasing locks in reverse
456 // order, because sometimes we legally release them out of order. (The loader
457 // does this intentionally in a few places.) We should consider whether those
458 // places can be changed, or whether we can add some kind of macro to declare
459 // that we're releasing out of order, and that it's ok & intentional. At that
460 // point, we can place a nice ASSERTE right here. Until then, do nothing.
461 }
462
463 // We may be clearing out the wrong entry in m_rgTakenLockInfos here, if the locks
464 // were released out of order. However, it will eventually correct itself once all
465 // the out-of-order locks have been released. And our count
466 // (i.e., m_rgcLocksTaken[dbgStateLockType]) will always be accurate
467 memset(&(m_rgTakenLockInfos[i]),
468 0,
469 sizeof(m_rgTakenLockInfos[i]));
470 }
471
472 m_rgcLocksTaken[dbgStateLockType] -= cReleases;
473}
474
475inline void DbgStateLockData::SetStartingValues()
476{
477 memset(this, 0, sizeof(*this));
478}
479
480inline UINT DbgStateLockData::GetLockCount(DbgStateLockType dbgStateLockType)
481{
482 _ASSERTE(UINT(dbgStateLockType) < kDbgStateLockType_Count);
483 return m_rgcLocksTaken[dbgStateLockType];
484}
485
486inline UINT DbgStateLockData::GetCombinedLockCount()
487{
488 // If this fires, the set of lock types must have changed. You'll need to
489 // fix the sum below to include all lock types
490 _ASSERTE(kDbgStateLockType_Count == 3);
491
492 return m_rgcLocksTaken[0] + m_rgcLocksTaken[1] + m_rgcLocksTaken[2];
493}
494
495inline void DbgStateLockState::SetStartingValues()
496{
497 m_cLocksEnteringCannotRetakeLock = 0;
498 m_pLockData = NULL; // Will get filled in by CLRInitDebugState()
499}
500
501// We set a marker to record the number of locks that have been taken when
502// CANNOT_RETAKE_LOCK contract is constructed.
503inline void DbgStateLockState::OnEnterCannotRetakeLockFunction()
504{
505 m_cLocksEnteringCannotRetakeLock = m_pLockData->GetCombinedLockCount();
506}
507
508inline BOOL DbgStateLockState::IsLockRetaken(void * pvLock)
509{
510 // m_cLocksEnteringCannotRetakeLock must be in valid range
511 _ASSERTE(m_cLocksEnteringCannotRetakeLock <= m_pLockData->GetCombinedLockCount());
512
513 // m_cLocksEnteringCannotRetakeLock records the number of locks that were taken
514 // when CANNOT_RETAKE_LOCK contract was constructed.
515 for (UINT i = 0;
516 i < min(_countof(m_pLockData->m_rgTakenLockInfos), m_cLocksEnteringCannotRetakeLock);
517 ++i)
518 {
519 if (m_pLockData->m_rgTakenLockInfos[i].m_pvLock == pvLock)
520 {
521 return TRUE;
522 }
523 }
524 return FALSE;
525}
526
527inline BOOL DbgStateLockState::IsSafeToRelease(UINT cReleases)
528{
529 return m_cLocksEnteringCannotRetakeLock <= (m_pLockData->GetCombinedLockCount() - cReleases);
530}
531
532inline void DbgStateLockState::SetDbgStateLockData(DbgStateLockData * pDbgStateLockData)
533{
534 m_pLockData = pDbgStateLockData;
535}
536
537inline DbgStateLockData * DbgStateLockState::GetDbgStateLockData()
538{
539 return m_pLockData;
540}
541
542#ifdef FEATURE_STACK_PROBE
543// We don't want to allow functions that use holders to EX_TRY to be intolerant
544// code... if an exception were to occur, the holders and EX_CATCH/FINALLY would
545// have less than 1/4 clean up.
546inline void EnsureSOIntolerantOK(const char *szFunction,
547 const char *szFile,
548 int lineNum)
549{
550 // We don't want to use a holder here, because a holder will
551 // call EnsureSOIntolerantOK
552
553 DWORD error = GetLastError();
554 if (! g_EnableDefaultRWValidation)
555 {
556 SetLastError(error);
557 return;
558 }
559 ClrDebugState *pClrDebugState = CheckClrDebugState();
560 if (! pClrDebugState)
561 {
562 SetLastError(error);
563 return;
564 }
565 pClrDebugState->CheckIfSOIntolerantOK(szFunction, szFile, lineNum);
566 SetLastError(error);
567}
568
569inline void ClrDebugState::CheckIfSOIntolerantOK(const char *szFunction,
570 const char *szFile,
571 int lineNum)
572
573{
574 // If we are an RW function on a managed thread, we must be in SO-intolerant mode. Ie. we must be behind a probe.
575 if (IsSOIntolerant() || IsDebugOnly() || IsSONotMainline() || (m_violationmask & SOToleranceViolation) ||
576 !g_fpShouldValidateSOToleranceOnThisThread || !g_fpShouldValidateSOToleranceOnThisThread())
577 {
578 return;
579 }
580 SoTolerantViolationHelper(szFunction, szFile, lineNum);
581}
582
583#endif
584
585inline
586void CONTRACT_ASSERT(const char *szElaboration,
587 UINT whichTest,
588 UINT whichTestMask,
589 const char *szFunction,
590 const char *szFile,
591 int lineNum)
592{
593 if (CheckClrDebugState() && ( CheckClrDebugState()->ViolationMask() & BadDebugState))
594 {
595 _ASSERTE(!"Someone tried to assert a contract violation although the contracts were disabled in this thread due to"
596 " an OOM or a shim/mscorwks mismatch. You can probably safely ignore this assert - however, whoever"
597 " called CONTRACT_ASSERT was supposed to checked if the current violationmask had the BadDebugState set."
598 " Look up the stack, see who called CONTRACT_ASSERT and file a bug against the owner.");
599 return;
600 }
601
602 // prevent recursion - we use the same mechanism as CHECK, so this will
603 // also prevent mutual recursion involving ASSERT_CHECKs
604 CHECK _check;
605 if (_check.EnterAssert())
606 {
607 char Buf[512*20 + 2048 + 1024];
608
609 sprintf_s(Buf,_countof(Buf), "CONTRACT VIOLATION by %s at \"%s\" @ %d\n\n%s\n", szFunction, szFile, lineNum, szElaboration);
610
611 int count = 20;
612 ContractStackRecord *pRec = CheckClrDebugState() ? CheckClrDebugState()->GetContractStackTrace() : NULL;
613 BOOL foundconflict = FALSE;
614 BOOL exceptionBuildingStack = FALSE;
615
616 PAL_TRY_NAKED
617 {
618 while (pRec != NULL)
619 {
620 char tmpbuf[512];
621 BOOL fshowconflict = FALSE;
622
623 if (!foundconflict)
624 {
625 if (whichTest == (pRec->m_testmask & whichTestMask))
626 {
627 foundconflict = TRUE;
628 fshowconflict = TRUE;
629 }
630 }
631
632 if (count != 0 || fshowconflict)
633 {
634 if (count != 0)
635 {
636 count--;
637 }
638 else
639 {
640 // Show that some lines have been skipped
641 strcat_s(Buf, _countof(Buf), "\n ...");
642
643 }
644
645 sprintf_s(tmpbuf,_countof(tmpbuf),
646 "\n%s %s in %s at \"%s\" @ %d",
647 fshowconflict ? "VIOLATED-->" : " ",
648 pRec->m_construct,
649 pRec->m_szFunction,
650 pRec->m_szFile,
651 pRec->m_lineNum
652 );
653
654 strcat_s(Buf, _countof(Buf), tmpbuf);
655 }
656
657 pRec = pRec->m_pNext;
658 }
659 }
660 PAL_EXCEPT_NAKED(EXCEPTION_EXECUTE_HANDLER)
661 {
662 // We're done trying to walk the stack of contracts. We faulted trying to form the contract stack trace,
663 // and that usually means that its corrupted. A common cause of this is having CONTRACTs in functions that
664 // never return, but instead do a non-local goto.
665 count = 0;
666 exceptionBuildingStack = TRUE;
667 }
668 PAL_ENDTRY_NAKED;
669
670 if (count == 0)
671 {
672 strcat_s(Buf,_countof(Buf), "\n ...");
673 }
674
675 if (exceptionBuildingStack)
676 {
677 strcat_s(Buf,_countof(Buf),
678 "\n"
679 "\nError forming contract stack. Any contract stack displayed above is correct,"
680 "\nbut it's most probably truncated. This is probably due to a CONTRACT in a"
681 "\nfunction that does a non-local goto. There are two bugs here:"
682 "\n"
683 "\n 1) the CONTRACT violation, and"
684 "\n 2) the CONTRACT in the function with the non-local goto."
685 "\n"
686 "\nPlease fix both bugs!"
687 "\n"
688 );
689 }
690
691 strcat_s(Buf,_countof(Buf), "\n\n");
692
693 if (!foundconflict && count != 0)
694 {
695 if (whichTest == BaseContract::THROWS_No)
696 {
697 strcat_s(Buf,_countof(Buf), "You can't throw here because there is no handler on the stack.\n");
698 }
699 else
700 {
701 strcat_s(Buf,_countof(Buf), "We can't find the violated contract. Look for an old-style non-holder-based contract.\n");
702 }
703 }
704
705 DbgAssertDialog((char *)szFile, lineNum, Buf);
706 _check.LeaveAssert();
707 }
708}
709
710
711FORCEINLINE BOOL BaseContract::EnforceContract()
712{
713 if (s_alwaysEnforceContracts)
714 return TRUE;
715 else
716 return CHECK::EnforceAssert();
717}
718
719inline void BaseContract::SetUnconditionalContractEnforcement(BOOL value)
720{
721 s_alwaysEnforceContracts = value;
722}
723
724inline UINT GetDbgStateCombinedLockCount()
725{
726 return GetClrDebugState()->GetCombinedLockCount();
727}
728inline UINT GetDbgStateLockCount(DbgStateLockType dbgStateLockType)
729{
730 return GetClrDebugState()->GetLockCount(dbgStateLockType);
731}
732
733#define ASSERT_NO_USER_LOCKS_HELD() \
734 _ASSERTE(GetDbgStateLockCount(kDbgStateLockType_User) == 0)
735#define ASSERT_NO_HOST_BREAKABLE_CRSTS_HELD() \
736 _ASSERTE(GetDbgStateLockCount(kDbgStateLockType_HostBreakableCrst) == 0)
737#define ASSERT_NO_EE_LOCKS_HELD() \
738 _ASSERTE(GetDbgStateLockCount(kDbgStateLockType_EE) == 0)
739
740#else // ENABLE_CONTRACTS_IMPL
741
742#define ASSERT_NO_USER_LOCKS_HELD()
743#define ASSERT_NO_HOST_BREAKABLE_CRSTS_HELD()
744#define ASSERT_NO_EE_LOCKS_HELD()
745
746#endif // ENABLE_CONTRACTS_IMPL
747
748#endif // CONTRACT_INL_
749