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// StaticContract.h
6// ---------------------------------------------------------------------------
7
8
9#ifndef __STATIC_CONTRACT_H_
10#define __STATIC_CONTRACT_H_
11
12// Make sure we have the WCHAR defines available.
13#include "palclr.h"
14
15#define SCAN_WIDEN2(x) L ## x
16#define SCAN_WIDEN(x) SCAN_WIDEN2(x)
17
18//
19// PDB annotations for the static contract analysis tool. These are seperated
20// from Contract.h to allow their inclusion in any part of the system.
21//
22
23#if defined(_DEBUG) && defined(_TARGET_X86_)
24#define METHOD_CANNOT_BE_FOLDED_DEBUG \
25 static int _noFold = 0; \
26 _noFold++;
27#else
28#define METHOD_CANNOT_BE_FOLDED_DEBUG
29#endif
30
31#ifdef _TARGET_X86_
32
33//
34// currently, only x86 has a static contract analysis tool, so let's not
35// bloat the PDBs of all the other architectures too..
36//
37#define ANNOTATION_TRY_BEGIN __annotation(W("TRY_BEGIN"))
38#define ANNOTATION_TRY_END __annotation(W("TRY_END"))
39#define ANNOTATION_HANDLER_BEGIN __annotation(W("HANDLER_BEGIN"))
40#define ANNOTATION_HANDLER_END __annotation(W("HANDLER_END"))
41#define ANNOTATION_NOTHROW __annotation(W("NOTHROW"))
42#define ANNOTATION_CANNOT_TAKE_LOCK __annotation(W("CANNOT_TAKE_LOCK"))
43#define ANNOTATION_WRAPPER __annotation(W("WRAPPER"))
44#define ANNOTATION_FAULT __annotation(W("FAULT"))
45#define ANNOTATION_FORBID_FAULT __annotation(W("FORBID_FAULT"))
46#define ANNOTATION_COOPERATIVE __annotation(W("MODE_COOPERATIVE"))
47#define ANNOTATION_MODE_COOPERATIVE __annotation(W("MODE_PREEMPTIVE"))
48#define ANNOTATION_MODE_ANY __annotation(W("MODE_ANY"))
49#define ANNOTATION_GC_TRIGGERS __annotation(W("GC_TRIGGERS"))
50#define ANNOTATION_IGNORE_THROW __annotation(W("THROWS"), W("NOTHROW"), W("CONDITIONAL_EXEMPT"))
51#define ANNOTATION_IGNORE_LOCK __annotation(W("CAN_TAKE_LOCK"), W("CANNOT_TAKE_LOCK"), W("CONDITIONAL_EXEMPT"))
52#define ANNOTATION_IGNORE_FAULT __annotation(W("FAULT"), W("FORBID_FAULT"), W("CONDITIONAL_EXEMPT"))
53#define ANNOTATION_IGNORE_TRIGGER __annotation(W("GC_TRIGGERS"), W("GC_NOTRIGGER"), W("CONDITIONAL_EXEMPT"))
54#define ANNOTATION_IGNORE_SO __annotation(W("SO_TOLERANT"), W("SO_INTOLERANT"), W("CONDITIONAL_EXEMPT"))
55#define ANNOTATION_VIOLATION(violationmask) __annotation(W("VIOLATION(") L#violationmask W(")"))
56#define ANNOTATION_UNCHECKED(thecheck) __annotation(W("UNCHECKED(") L#thecheck W(")"))
57
58#define ANNOTATION_MARK_BLOCK_ANNOTATION __annotation(W("MARK"))
59#define ANNOTATION_USE_BLOCK_ANNOTATION __annotation(W("USE"))
60#define ANNOTATION_END_USE_BLOCK_ANNOTATION __annotation(W("END_USE"))
61
62// here is the plan:
63//
64// a special holder which implements a violation
65//
66
67#define ANNOTATION_FN_SPECIAL_HOLDER_BEGIN __annotation(W("SPECIAL_HOLDER_BEGIN ") SCAN_WIDEN(__FUNCTION__))
68#define ANNOTATION_SPECIAL_HOLDER_END __annotation(W("SPECIAL_HOLDER_END"))
69#define ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT __annotation(W("SPECIAL_HOLDER_DYNAMIC"))
70
71#define ANNOTATION_SO_PROBE_BEGIN(probeAmount) __annotation(W("SO_PROBE_BEGIN(") L#probeAmount W(")"))
72#define ANNOTATION_SO_PROBE_END __annotation(W("SO_PROBE_END"))
73
74//
75// these annotations are all function-name qualified
76//
77#define ANNOTATION_FN_LEAF __annotation(W("LEAF ") SCAN_WIDEN(__FUNCTION__))
78#define ANNOTATION_FN_WRAPPER __annotation(W("WRAPPER ") SCAN_WIDEN(__FUNCTION__))
79#define ANNOTATION_FN_THROWS __annotation(W("THROWS ") SCAN_WIDEN(__FUNCTION__))
80#define ANNOTATION_FN_NOTHROW __annotation(W("NOTHROW ") SCAN_WIDEN(__FUNCTION__))
81#define ANNOTATION_FN_CAN_TAKE_LOCK __annotation(W("CAN_TAKE_LOCK ") SCAN_WIDEN(__FUNCTION__))
82#define ANNOTATION_FN_CANNOT_TAKE_LOCK __annotation(W("CANNOT_TAKE_LOCK ") SCAN_WIDEN(__FUNCTION__))
83#define ANNOTATION_FN_FAULT __annotation(W("FAULT ") SCAN_WIDEN(__FUNCTION__))
84#define ANNOTATION_FN_FORBID_FAULT __annotation(W("FORBID_FAULT ") SCAN_WIDEN(__FUNCTION__))
85#define ANNOTATION_FN_GC_TRIGGERS __annotation(W("GC_TRIGGERS ") SCAN_WIDEN(__FUNCTION__))
86#define ANNOTATION_FN_GC_NOTRIGGER __annotation(W("GC_NOTRIGGER ") SCAN_WIDEN(__FUNCTION__))
87#define ANNOTATION_FN_SO_TOLERANT __annotation(W("SO_TOLERANT ") SCAN_WIDEN(__FUNCTION__))
88#define ANNOTATION_FN_SO_INTOLERANT __annotation(W("SO_INTOLERANT ") SCAN_WIDEN(__FUNCTION__))
89#define ANNOTATION_FN_SO_NOT_MAINLINE __annotation(W("SO_NOT_MAINLINE ") SCAN_WIDEN(__FUNCTION__))
90#define ANNOTATION_FN_MODE_COOPERATIVE __annotation(W("MODE_COOPERATIVE ") SCAN_WIDEN(__FUNCTION__))
91#define ANNOTATION_FN_MODE_PREEMPTIVE __annotation(W("MODE_PREEMPTIVE ") SCAN_WIDEN(__FUNCTION__))
92#define ANNOTATION_FN_MODE_ANY __annotation(W("MODE_ANY ") SCAN_WIDEN(__FUNCTION__))
93#define ANNOTATION_FN_HOST_NOCALLS __annotation(W("HOST_NOCALLS ") SCAN_WIDEN(__FUNCTION__))
94#define ANNOTATION_FN_HOST_CALLS __annotation(W("HOST_CALLS ") SCAN_WIDEN(__FUNCTION__))
95
96#define ANNOTATION_ENTRY_POINT __annotation(W("SO_EP ") SCAN_WIDEN(__FUNCTION__))
97
98
99// for DacCop
100#define ANNOTATION_SUPPORTS_DAC __annotation(W("SUPPORTS_DAC"))
101#define ANNOTATION_SUPPORTS_DAC_HOST_ONLY __annotation(W("SUPPORTS_DAC_HOST_ONLY"))
102
103#ifdef _DEBUG
104// @todo : put correct annotation in and fixup the static analysis tool
105// This is used to flag debug-only functions that we want to ignore in our static analysis
106#define ANNOTATION_DEBUG_ONLY __annotation(W("DBG_ONLY"))
107
108#endif
109
110#else // _TARGET_X86_
111
112#define ANNOTATION_TRY_BEGIN { }
113#define ANNOTATION_TRY_END { }
114#define ANNOTATION_HANDLER_BEGIN { }
115#define ANNOTATION_HANDLER_END { }
116#define ANNOTATION_NOTHROW { }
117#define ANNOTATION_CANNOT_TAKE_LOCK { }
118#define ANNOTATION_WRAPPER { }
119#define ANNOTATION_FAULT { }
120#define ANNOTATION_FORBID_FAULT { }
121#define ANNOTATION_COOPERATIVE { }
122#define ANNOTATION_MODE_COOPERATIVE { }
123#define ANNOTATION_MODE_ANY { }
124#define ANNOTATION_GC_TRIGGERS { }
125#define ANNOTATION_IGNORE_THROW { }
126#define ANNOTATION_IGNORE_LOCK { }
127#define ANNOTATION_IGNORE_FAULT { }
128#define ANNOTATION_IGNORE_TRIGGER { }
129#define ANNOTATION_IGNORE_SO { }
130#define ANNOTATION_VIOLATION(violationmask) { }
131#define ANNOTATION_UNCHECKED(thecheck) { }
132
133#define ANNOTATION_TRY_MARKER { }
134#define ANNOTATION_CATCH_MARKER { }
135
136#define ANNOTATION_FN_HOST_NOCALLS { }
137#define ANNOTATION_FN_HOST_CALLS { }
138
139#define ANNOTATION_FN_SPECIAL_HOLDER_BEGIN { }
140#define ANNOTATION_SPECIAL_HOLDER_END { }
141#define ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT { }
142
143#define ANNOTATION_FN_LEAF { }
144#define ANNOTATION_FN_WRAPPER { }
145#define ANNOTATION_FN_THROWS { }
146#define ANNOTATION_FN_NOTHROW { }
147#define ANNOTATION_FN_CAN_TAKE_LOCK { }
148#define ANNOTATION_FN_CANNOT_TAKE_LOCK { }
149#define ANNOTATION_FN_FAULT { }
150#define ANNOTATION_FN_FORBID_FAULT { }
151#define ANNOTATION_FN_GC_TRIGGERS { }
152#define ANNOTATION_FN_GC_NOTRIGGER { }
153#define ANNOTATION_FN_SO_TOLERANT { }
154#define ANNOTATION_FN_SO_INTOLERANT { }
155#define ANNOTATION_FN_SO_NOT_MAINLINE { }
156#define ANNOTATION_FN_MODE_COOPERATIVE { }
157#define ANNOTATION_FN_MODE_PREEMPTIVE { }
158#define ANNOTATION_FN_MODE_ANY { }
159#define ANNOTATION_FN_HOST_NOCALLS { }
160#define ANNOTATION_FN_HOST_CALLS { }
161
162#define ANNOTATION_SUPPORTS_DAC { }
163#define ANNOTATION_SUPPORTS_DAC_HOST_ONLY { }
164
165#define ANNOTATION_SO_PROBE_BEGIN(probeAmount) { }
166#define ANNOTATION_SO_PROBE_END { }
167
168#define ANNOTATION_SO_TOLERANT { }
169#define ANNOTATION_SO_INTOLERANT { }
170#define ANNOTATION_SO_NOT_MAINLINE { }
171#define ANNOTATION_SO_NOT_MAINLINE_BEGIN { }
172#define ANNOTATION_SO_NOT_MAINLINE_END { }
173#define ANNOTATION_ENTRY_POINT { }
174#ifdef _DEBUG
175#define ANNOTATION_DEBUG_ONLY { }
176#endif
177
178#endif // _TARGET_X86_
179
180#define STATIC_CONTRACT_THROWS ANNOTATION_FN_THROWS
181#define STATIC_CONTRACT_NOTHROW ANNOTATION_FN_NOTHROW
182#define STATIC_CONTRACT_CAN_TAKE_LOCK ANNOTATION_FN_CAN_TAKE_LOCK
183#define STATIC_CONTRACT_CANNOT_TAKE_LOCK ANNOTATION_FN_CANNOT_TAKE_LOCK
184#define STATIC_CONTRACT_FAULT ANNOTATION_FN_FAULT
185#define STATIC_CONTRACT_FORBID_FAULT ANNOTATION_FN_FORBID_FAULT
186#define STATIC_CONTRACT_GC_TRIGGERS ANNOTATION_FN_GC_TRIGGERS
187#define STATIC_CONTRACT_GC_NOTRIGGER ANNOTATION_FN_GC_NOTRIGGER
188#define STATIC_CONTRACT_HOST_NOCALLS ANNOTATION_FN_HOST_NOCALLS
189#define STATIC_CONTRACT_HOST_CALLS ANNOTATION_FN_HOST_CALLS
190
191#define STATIC_CONTRACT_SUPPORTS_DAC ANNOTATION_SUPPORTS_DAC
192#define STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY ANNOTATION_SUPPORTS_DAC_HOST_ONLY
193
194#define STATIC_CONTRACT_MODE_COOPERATIVE ANNOTATION_FN_MODE_COOPERATIVE
195#define STATIC_CONTRACT_MODE_PREEMPTIVE ANNOTATION_FN_MODE_PREEMPTIVE
196#define STATIC_CONTRACT_MODE_ANY ANNOTATION_FN_MODE_ANY
197#define STATIC_CONTRACT_LEAF ANNOTATION_FN_LEAF
198#define STATIC_CONTRACT_LIMITED_METHOD ANNOTATION_FN_LEAF
199#define STATIC_CONTRACT_WRAPPER ANNOTATION_FN_WRAPPER
200
201#ifdef FEATURE_STACK_PROBE // Static SO contracts only required when SO Infrastructure code is present
202#define STATIC_CONTRACT_SO_INTOLERANT ANNOTATION_FN_SO_INTOLERANT
203#define STATIC_CONTRACT_SO_TOLERANT ANNOTATION_FN_SO_TOLERANT
204#define STATIC_CONTRACT_SO_NOT_MAINLINE ANNOTATION_FN_SO_NOT_MAINLINE
205
206#define STATIC_CONTRACT_ENTRY_POINT ANNOTATION_ENTRY_POINT; ANNOTATION_FN_SO_TOLERANT
207#else // FEATURE_STACK_PROBE
208#define STATIC_CONTRACT_SO_INTOLERANT
209#define STATIC_CONTRACT_SO_TOLERANT
210#define STATIC_CONTRACT_SO_NOT_MAINLINE
211#define STATIC_CONTRACT_ENTRY_POINT
212#endif // FEATURE_STACK_PROBE
213
214#ifdef _DEBUG
215#define STATIC_CONTRACT_DEBUG_ONLY \
216 ANNOTATION_DEBUG_ONLY; \
217 STATIC_CONTRACT_CANNOT_TAKE_LOCK; \
218 ANNOTATION_VIOLATION(TakesLockViolation); \
219 ANNOTATION_FN_SO_NOT_MAINLINE;
220#else
221#define STATIC_CONTRACT_DEBUG_ONLY
222#endif
223
224#define STATIC_CONTRACT_VIOLATION(mask) \
225 ANNOTATION_VIOLATION(mask)
226
227#define SCAN_SCOPE_BEGIN \
228 METHOD_CANNOT_BE_FOLDED_DEBUG; \
229 ANNOTATION_FN_SPECIAL_HOLDER_BEGIN;
230
231#define SCAN_SCOPE_END \
232 METHOD_CANNOT_BE_FOLDED_DEBUG; \
233 ANNOTATION_SPECIAL_HOLDER_END;
234
235namespace StaticContract
236{
237 struct ScanThrowMarkerStandard
238 {
239 __declspec(noinline) ScanThrowMarkerStandard()
240 {
241 METHOD_CANNOT_BE_FOLDED_DEBUG;
242 STATIC_CONTRACT_THROWS;
243 STATIC_CONTRACT_GC_NOTRIGGER;
244 STATIC_CONTRACT_SO_TOLERANT;
245 }
246
247 static void used()
248 {
249 }
250 };
251
252 struct ScanThrowMarkerTerminal
253 {
254 __declspec(noinline) ScanThrowMarkerTerminal()
255 {
256 METHOD_CANNOT_BE_FOLDED_DEBUG;
257 }
258
259 static void used()
260 {
261 }
262 };
263
264 struct ScanThrowMarkerIgnore
265 {
266 __declspec(noinline) ScanThrowMarkerIgnore()
267 {
268 METHOD_CANNOT_BE_FOLDED_DEBUG;
269 }
270
271 static void used()
272 {
273 }
274 };
275}
276typedef StaticContract::ScanThrowMarkerStandard ScanThrowMarker;
277
278// This is used to annotate code as throwing a terminal exception, and should
279// be used immediately before the throw so that infer that it can be inferred
280// that the block in which this annotation appears throws unconditionally.
281#define SCAN_THROW_MARKER do { ScanThrowMarker __throw_marker; } while (0)
282
283#define SCAN_IGNORE_THROW_MARKER \
284 typedef StaticContract::ScanThrowMarkerIgnore ScanThrowMarker; if (0) ScanThrowMarker::used();
285
286// Terminal exceptions are asynchronous and cannot be included in THROWS contract
287// analysis. As such, this uses typedef to reassign the ScanThrowMarker to a
288// non-annotating struct so that SCAN does not see the block as throwing.
289#define STATIC_CONTRACT_THROWS_TERMINAL \
290 typedef StaticContract::ScanThrowMarkerTerminal ScanThrowMarker; if (0) ScanThrowMarker::used();
291
292#if defined(_DEBUG) && !defined(DACCESS_COMPILE) && defined(FEATURE_STACK_PROBE) && !defined(_TARGET_ARM_) // @ARMTODO
293extern void EnsureSOIntolerantOK(const char *szFunction, const char *szFile, int lineNum);
294
295extern BOOL (*g_fpShouldValidateSOToleranceOnThisThread)();
296
297// @todo Is there any checks we can do here?
298#define ENSURE_SHOULD_NOT_PROBE_FOR_SO
299
300#define CHECK_IF_SO_INTOLERANT_OK \
301 EnsureSOIntolerantOK(__FUNCTION__, __FILE__, __LINE__);
302
303// Even if we can't have a full-blown contract, we can at least check
304// if its ok to run an SO-Intolerant function.
305#undef STATIC_CONTRACT_SO_INTOLERANT
306#define STATIC_CONTRACT_SO_INTOLERANT \
307 ANNOTATION_FN_SO_INTOLERANT; \
308 CHECK_IF_SO_INTOLERANT_OK;
309
310#undef STATIC_CONTRACT_SO_NOT_MAINLINE
311#define STATIC_CONTRACT_SO_NOT_MAINLINE \
312 ENSURE_SHOULD_NOT_PROBE_FOR_SO \
313 ANNOTATION_FN_SO_NOT_MAINLINE
314
315#else
316#define EnsureSOIntolerantOK(x,y,z)
317
318#endif
319
320
321#ifdef _MSC_VER
322#define SCAN_IGNORE_THROW typedef StaticContract::ScanThrowMarkerIgnore ScanThrowMarker; ANNOTATION_IGNORE_THROW
323#define SCAN_IGNORE_LOCK ANNOTATION_IGNORE_LOCK
324#define SCAN_IGNORE_FAULT ANNOTATION_IGNORE_FAULT
325#define SCAN_IGNORE_TRIGGER ANNOTATION_IGNORE_TRIGGER
326#define SCAN_IGNORE_SO ANNOTATION_IGNORE_SO
327#else
328#define SCAN_IGNORE_THROW
329#define SCAN_IGNORE_LOCK
330#define SCAN_IGNORE_FAULT
331#define SCAN_IGNORE_TRIGGER
332#define SCAN_IGNORE_SO
333#endif
334
335
336// we use BlockMarker's only for SCAN
337#if defined(_DEBUG) && defined(_TARGET_X86_) && !defined(DACCESS_COMPILE)
338
339template <UINT COUNT>
340class BlockMarker
341{
342public:
343 __declspec(noinline) void MarkBlock()
344 {
345 ANNOTATION_MARK_BLOCK_ANNOTATION;
346 METHOD_CANNOT_BE_FOLDED_DEBUG;
347 return;
348 }
349
350 __declspec(noinline) void UseMarkedBlockAnnotation()
351 {
352 ANNOTATION_USE_BLOCK_ANNOTATION;
353 METHOD_CANNOT_BE_FOLDED_DEBUG;
354 return;
355 }
356
357 __declspec(noinline) void EndUseMarkedBlockAnnotation()
358 {
359 ANNOTATION_END_USE_BLOCK_ANNOTATION;
360 METHOD_CANNOT_BE_FOLDED_DEBUG;
361 return;
362 }
363};
364
365#define SCAN_BLOCKMARKER() BlockMarker<__COUNTER__> __blockMarker_onlyOneAllowedPerScope
366#define SCAN_BLOCKMARKER_MARK() __blockMarker_onlyOneAllowedPerScope.MarkBlock()
367#define SCAN_BLOCKMARKER_USE() __blockMarker_onlyOneAllowedPerScope.UseMarkedBlockAnnotation()
368#define SCAN_BLOCKMARKER_END_USE() __blockMarker_onlyOneAllowedPerScope.EndUseMarkedBlockAnnotation()
369
370#define SCAN_BLOCKMARKER_N(num) BlockMarker<__COUNTER__> __blockMarker_onlyOneAllowedPerScope##num
371#define SCAN_BLOCKMARKER_MARK_N(num) __blockMarker_onlyOneAllowedPerScope##num.MarkBlock()
372#define SCAN_BLOCKMARKER_USE_N(num) __blockMarker_onlyOneAllowedPerScope##num.UseMarkedBlockAnnotation()
373#define SCAN_BLOCKMARKER_END_USE_N(num) __blockMarker_onlyOneAllowedPerScope##num.EndUseMarkedBlockAnnotation()
374
375#define SCAN_EHMARKER() BlockMarker<__COUNTER__> __marker_onlyOneAllowedPerScope
376#define SCAN_EHMARKER_TRY() __annotation(W("SCOPE(BLOCK);SCAN_TRY_BEGIN")); __marker_onlyOneAllowedPerScope.MarkBlock()
377#define SCAN_EHMARKER_END_TRY() __annotation(W("SCOPE(BLOCK);SCAN_TRY_END"))
378#define SCAN_EHMARKER_CATCH() __marker_onlyOneAllowedPerScope.UseMarkedBlockAnnotation()
379#define SCAN_EHMARKER_END_CATCH() __marker_onlyOneAllowedPerScope.EndUseMarkedBlockAnnotation()
380
381#else
382
383#define SCAN_BLOCKMARKER()
384#define SCAN_BLOCKMARKER_MARK()
385#define SCAN_BLOCKMARKER_USE()
386#define SCAN_BLOCKMARKER_END_USE()
387
388#define SCAN_BLOCKMARKER_N(num)
389#define SCAN_BLOCKMARKER_MARK_N(num)
390#define SCAN_BLOCKMARKER_USE_N(num)
391#define SCAN_BLOCKMARKER_END_USE_N(num)
392
393#define SCAN_EHMARKER()
394#define SCAN_EHMARKER_TRY()
395#define SCAN_EHMARKER_END_TRY()
396#define SCAN_EHMARKER_CATCH()
397#define SCAN_EHMARKER_END_CATCH()
398
399#endif
400
401
402//
403// @todo remove this... if there really are cases where a function just shouldn't have a contract, then perhaps
404// we can add a more descriptive name for it...
405//
406#define CANNOT_HAVE_CONTRACT __annotation(W("NO_CONTRACT"))
407
408#endif // __STATIC_CONTRACT_H_
409