Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / mscorlib / system / diagnostics / contracts / contractsbcl.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 /*============================================================
7 **
8 ** Class:  Contract
9 ** 
10 ** <OWNER>maf,mbarnett,Microsoft</OWNER>
11 **
12 ** Implementation details of CLR Contracts.
13 **
14 ===========================================================*/
15 #define DEBUG // The behavior of this contract library should be consistent regardless of build type.
16
17 #if SILVERLIGHT
18 #define FEATURE_UNTRUSTED_CALLERS
19 #elif REDHAWK_RUNTIME
20
21 #elif BARTOK_RUNTIME
22
23 #else // CLR
24 #define FEATURE_UNTRUSTED_CALLERS
25 #define FEATURE_RELIABILITY_CONTRACTS
26 #define FEATURE_SERIALIZATION
27 #endif
28
29 using System;
30 using System.Collections.Generic;
31 using System.Diagnostics;
32 using System.Diagnostics.CodeAnalysis;
33 using System.Diagnostics.Contracts;
34 using System.Reflection;
35
36 #if FEATURE_RELIABILITY_CONTRACTS
37 using System.Runtime.ConstrainedExecution;
38 #endif
39 #if FEATURE_UNTRUSTED_CALLERS
40 using System.Security;
41 using System.Security.Permissions;
42 #endif
43
44 namespace System.Diagnostics.Contracts {
45
46     public static partial class Contract
47     {
48         #region Private Methods
49
50         [ThreadStatic]
51         private static bool _assertingMustUseRewriter;
52
53         /// <summary>
54         /// This method is used internally to trigger a failure indicating to the "programmer" that he is using the interface incorrectly.
55         /// It is NEVER used to indicate failure of actual contracts at runtime.
56         /// </summary>
57         [SecuritySafeCritical]
58         static partial void AssertMustUseRewriter(ContractFailureKind kind, String contractKind)
59         {
60             if (_assertingMustUseRewriter)
61                 System.Diagnostics.Assert.Fail("Asserting that we must use the rewriter went reentrant.", "Didn't rewrite this mscorlib?");
62             _assertingMustUseRewriter = true;
63
64             // For better diagnostics, report which assembly is at fault.  Walk up stack and
65             // find the first non-mscorlib assembly.
66             Assembly thisAssembly = typeof(Contract).Assembly;  // In case we refactor mscorlib, use Contract class instead of Object.
67             StackTrace stack = new StackTrace();
68             Assembly probablyNotRewritten = null;
69             for (int i = 0; i < stack.FrameCount; i++)
70             {
71                 Assembly caller = stack.GetFrame(i).GetMethod().DeclaringType.Assembly;
72                 if (caller != thisAssembly)
73                 {
74                     probablyNotRewritten = caller;
75                     break;
76                 }
77             }
78
79             if (probablyNotRewritten == null)
80                 probablyNotRewritten = thisAssembly;
81             String simpleName = probablyNotRewritten.GetName().Name;
82             System.Runtime.CompilerServices.ContractHelper.TriggerFailure(kind, Environment.GetResourceString("MustUseCCRewrite", contractKind, simpleName), null, null, null);
83
84             _assertingMustUseRewriter = false;
85         }
86
87         #endregion Private Methods
88
89         #region Failure Behavior
90
91         /// <summary>
92         /// Without contract rewriting, failing Assert/Assumes end up calling this method.
93         /// Code going through the contract rewriter never calls this method. Instead, the rewriter produced failures call
94         /// System.Runtime.CompilerServices.ContractHelper.RaiseContractFailedEvent, followed by 
95         /// System.Runtime.CompilerServices.ContractHelper.TriggerFailure.
96         /// </summary>
97         [SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Security.SecuritySafeCriticalAttribute")]
98         [System.Diagnostics.DebuggerNonUserCode]
99 #if FEATURE_RELIABILITY_CONTRACTS
100         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
101 #endif
102         static partial void ReportFailure(ContractFailureKind failureKind, String userMessage, String conditionText, Exception innerException)
103         {
104             if (failureKind < ContractFailureKind.Precondition || failureKind > ContractFailureKind.Assume)
105                 throw new ArgumentException(Environment.GetResourceString("Arg_EnumIllegalVal", failureKind), "failureKind");
106             Contract.EndContractBlock();
107
108             // displayMessage == null means: yes we handled it. Otherwise it is the localized failure message
109             var displayMessage = System.Runtime.CompilerServices.ContractHelper.RaiseContractFailedEvent(failureKind, userMessage, conditionText, innerException);
110
111             if (displayMessage == null) return;
112
113             System.Runtime.CompilerServices.ContractHelper.TriggerFailure(failureKind, displayMessage, userMessage, conditionText, innerException);
114         }
115
116         /// <summary>
117         /// Allows a managed application environment such as an interactive interpreter (IronPython)
118         /// to be notified of contract failures and 
119         /// potentially "handle" them, either by throwing a particular exception type, etc.  If any of the
120         /// event handlers sets the Cancel flag in the ContractFailedEventArgs, then the Contract class will
121         /// not pop up an assert dialog box or trigger escalation policy.  Hooking this event requires 
122         /// full trust, because it will inform you of bugs in the appdomain and because the event handler
123         /// could allow you to continue execution.
124         /// </summary>
125         public static event EventHandler<ContractFailedEventArgs> ContractFailed {
126 #if FEATURE_UNTRUSTED_CALLERS
127             [SecurityCritical]
128 #if FEATURE_LINK_DEMAND
129             [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
130 #endif
131 #endif
132             add {
133                 System.Runtime.CompilerServices.ContractHelper.InternalContractFailed += value;
134             }
135 #if FEATURE_UNTRUSTED_CALLERS
136             [SecurityCritical]
137 #if FEATURE_LINK_DEMAND
138             [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
139 #endif
140 #endif
141             remove {
142                 System.Runtime.CompilerServices.ContractHelper.InternalContractFailed -= value;
143             }
144         }
145         #endregion FailureBehavior
146     }
147
148     public sealed class ContractFailedEventArgs : EventArgs
149     {
150         private ContractFailureKind _failureKind;
151         private String _message;
152         private String _condition;
153         private Exception _originalException;
154         private bool _handled;
155         private bool _unwind;
156
157         internal Exception thrownDuringHandler;
158
159 #if FEATURE_RELIABILITY_CONTRACTS
160         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
161 #endif
162         public ContractFailedEventArgs(ContractFailureKind failureKind, String message, String condition, Exception originalException)
163         {
164             Contract.Requires(originalException == null || failureKind == ContractFailureKind.PostconditionOnException);
165             _failureKind = failureKind;
166             _message = message;
167             _condition = condition;
168             _originalException = originalException;
169         }
170
171         public String Message { get { return _message; } }
172         public String Condition { get { return _condition; } }
173         public ContractFailureKind FailureKind { get { return _failureKind; } }
174         public Exception OriginalException { get { return _originalException; } }
175
176         // Whether the event handler "handles" this contract failure, or to fail via escalation policy.
177         public bool Handled {
178             get { return _handled; }
179         }
180
181 #if FEATURE_UNTRUSTED_CALLERS
182         [SecurityCritical]
183 #if FEATURE_LINK_DEMAND
184         [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
185 #endif
186 #endif
187         public void SetHandled()
188         {
189             _handled = true;
190         }
191
192         public bool Unwind {
193             get { return _unwind; }
194         }
195
196 #if FEATURE_UNTRUSTED_CALLERS
197         [SecurityCritical]
198 #if FEATURE_LINK_DEMAND
199         [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
200 #endif
201 #endif
202         public void SetUnwind()
203         {
204             _unwind = true;
205         }
206     }
207
208 #if FEATURE_SERIALIZATION
209     [Serializable]
210 #else
211     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors")]
212 #endif
213     [SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic")]
214     internal sealed class ContractException : Exception
215     {
216         readonly ContractFailureKind _Kind;
217         readonly string _UserMessage;
218         readonly string _Condition;
219
220         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
221         public ContractFailureKind Kind { get { return _Kind; } }
222         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
223         public string Failure { get { return this.Message; } }
224         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
225         public string UserMessage { get { return _UserMessage; } }
226         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
227         public string Condition { get { return _Condition; } }
228
229         // Called by COM Interop, if we see COR_E_CODECONTRACTFAILED as an HRESULT.
230         private ContractException()
231         {
232             HResult = System.Runtime.CompilerServices.ContractHelper.COR_E_CODECONTRACTFAILED;
233         }
234
235         public ContractException(ContractFailureKind kind, string failure, string userMessage, string condition, Exception innerException)
236             : base(failure, innerException)
237         {
238             HResult = System.Runtime.CompilerServices.ContractHelper.COR_E_CODECONTRACTFAILED;
239             this._Kind = kind;
240             this._UserMessage = userMessage;
241             this._Condition = condition;
242         }
243
244 #if FEATURE_SERIALIZATION
245         private ContractException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
246             : base(info, context)
247         {
248             _Kind = (ContractFailureKind)info.GetInt32("Kind");
249             _UserMessage = info.GetString("UserMessage");
250             _Condition = info.GetString("Condition");
251         }
252 #endif // FEATURE_SERIALIZATION
253
254 #if FEATURE_UNTRUSTED_CALLERS && FEATURE_SERIALIZATION
255         [SecurityCritical]
256 #if FEATURE_LINK_DEMAND && FEATURE_SERIALIZATION
257         [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
258 #endif // FEATURE_LINK_DEMAND
259 #endif // FEATURE_UNTRUSTED_CALLERS
260         public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
261         {
262             base.GetObjectData(info, context);
263
264             info.AddValue("Kind", _Kind);
265             info.AddValue("UserMessage", _UserMessage);
266             info.AddValue("Condition", _Condition);
267         }
268     }
269 }
270
271
272 namespace System.Runtime.CompilerServices
273 {
274     public static partial class ContractHelper
275     {
276         #region Private fields
277
278         private static volatile EventHandler<ContractFailedEventArgs> contractFailedEvent;
279         private static readonly Object lockObject = new Object();
280
281         internal const int COR_E_CODECONTRACTFAILED = unchecked((int)0x80131542);
282
283         #endregion
284
285         /// <summary>
286         /// Allows a managed application environment such as an interactive interpreter (IronPython) or a
287         /// web browser host (Jolt hosting Silverlight in IE) to be notified of contract failures and 
288         /// potentially "handle" them, either by throwing a particular exception type, etc.  If any of the
289         /// event handlers sets the Cancel flag in the ContractFailedEventArgs, then the Contract class will
290         /// not pop up an assert dialog box or trigger escalation policy.  Hooking this event requires 
291         /// full trust.
292         /// </summary>
293         internal static event EventHandler<ContractFailedEventArgs> InternalContractFailed
294         {
295 #if FEATURE_UNTRUSTED_CALLERS
296             [SecurityCritical]
297 #endif
298             add {
299                 // Eagerly prepare each event handler _marked with a reliability contract_, to 
300                 // attempt to reduce out of memory exceptions while reporting contract violations.
301                 // This only works if the new handler obeys the constraints placed on 
302                 // constrained execution regions.  Eagerly preparing non-reliable event handlers
303                 // would be a perf hit and wouldn't significantly improve reliability.
304                 // UE: Please mention reliable event handlers should also be marked with the 
305                 // PrePrepareMethodAttribute to avoid CER eager preparation work when ngen'ed.
306                 System.Runtime.CompilerServices.RuntimeHelpers.PrepareContractedDelegate(value);
307                 lock (lockObject)
308                 {
309                     contractFailedEvent += value;
310                 }
311             }
312 #if FEATURE_UNTRUSTED_CALLERS
313             [SecurityCritical]
314 #endif
315             remove {
316                 lock (lockObject)
317                 {
318                     contractFailedEvent -= value;
319                 }
320             }
321         }
322
323         /// <summary>
324         /// Rewriter will call this method on a contract failure to allow listeners to be notified.
325         /// The method should not perform any failure (assert/throw) itself.
326         /// This method has 3 functions:
327         /// 1. Call any contract hooks (such as listeners to Contract failed events)
328         /// 2. Determine if the listeneres deem the failure as handled (then resultFailureMessage should be set to null)
329         /// 3. Produce a localized resultFailureMessage used in advertising the failure subsequently.
330         /// </summary>
331         /// <param name="resultFailureMessage">Should really be out (or the return value), but partial methods are not flexible enough.
332         /// On exit: null if the event was handled and should not trigger a failure.
333         ///          Otherwise, returns the localized failure message</param>
334         [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
335         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
336         [System.Diagnostics.DebuggerNonUserCode]
337 #if FEATURE_RELIABILITY_CONTRACTS
338         [SecuritySafeCritical]
339 #endif
340         static partial void RaiseContractFailedEventImplementation(ContractFailureKind failureKind, String userMessage, String conditionText, Exception innerException, ref string resultFailureMessage)
341         {
342             if (failureKind < ContractFailureKind.Precondition || failureKind > ContractFailureKind.Assume)
343                 throw new ArgumentException(Environment.GetResourceString("Arg_EnumIllegalVal", failureKind), "failureKind");
344             Contract.EndContractBlock();
345
346             string returnValue;
347             String displayMessage = "contract failed.";  // Incomplete, but in case of OOM during resource lookup...
348             ContractFailedEventArgs eventArgs = null;  // In case of OOM.
349 #if FEATURE_RELIABILITY_CONTRACTS
350             System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
351 #endif
352             try
353             {
354                 displayMessage = GetDisplayMessage(failureKind, userMessage, conditionText);
355                 EventHandler<ContractFailedEventArgs> contractFailedEventLocal = contractFailedEvent;
356                 if (contractFailedEventLocal != null)
357                 {
358                     eventArgs = new ContractFailedEventArgs(failureKind, displayMessage, conditionText, innerException);
359                     foreach (EventHandler<ContractFailedEventArgs> handler in contractFailedEventLocal.GetInvocationList())
360                     {
361                         try
362                         {
363                             handler(null, eventArgs);
364                         }
365                         catch (Exception e)
366                         {
367                             eventArgs.thrownDuringHandler = e;
368                             eventArgs.SetUnwind();
369                         }
370                     }
371                     if (eventArgs.Unwind)
372                     {
373 #if !FEATURE_CORECLR 
374                         if (Environment.IsCLRHosted)
375                             TriggerCodeContractEscalationPolicy(failureKind, displayMessage, conditionText, innerException);
376 #endif
377                         // unwind
378                         if (innerException == null) { innerException = eventArgs.thrownDuringHandler; }
379                         throw new ContractException(failureKind, displayMessage, userMessage, conditionText, innerException);
380                     }
381                 }
382             }
383             finally
384             {
385                 if (eventArgs != null && eventArgs.Handled)
386                 {
387                     returnValue = null; // handled
388                 }
389                 else
390                 {
391                     returnValue = displayMessage;
392                 }
393             }
394             resultFailureMessage = returnValue;
395         }
396
397         /// <summary>
398         /// Rewriter calls this method to get the default failure behavior.
399         /// </summary>
400         [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "conditionText")]
401         [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "userMessage")]
402         [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "kind")]
403         [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "innerException")]
404         [System.Diagnostics.DebuggerNonUserCode]
405 #if FEATURE_UNTRUSTED_CALLERS && !FEATURE_CORECLR
406         [SecuritySafeCritical]
407 #endif
408         static partial void TriggerFailureImplementation(ContractFailureKind kind, String displayMessage, String userMessage, String conditionText, Exception innerException)
409         {
410             // If we're here, our intent is to pop up a dialog box (if we can).  For developers 
411             // interacting live with a debugger, this is a good experience.  For Silverlight 
412             // hosted in Internet Explorer, the assert window is great.  If we cannot
413             // pop up a dialog box, throw an exception (consider a library compiled with 
414             // "Assert On Failure" but used in a process that can't pop up asserts, like an 
415             // NT Service).  For the CLR hosted by server apps like SQL or Exchange, we should 
416             // trigger escalation policy.  
417 #if !FEATURE_CORECLR
418             if (Environment.IsCLRHosted)
419             {
420                 TriggerCodeContractEscalationPolicy(kind, displayMessage, conditionText, innerException);
421                 // Hosts like SQL may choose to abort the thread, so we will not get here in all cases.
422                 // But if the host's chosen action was to throw an exception, we should throw an exception
423                 // here (which is easier to do in managed code with the right parameters).  
424                 throw new ContractException(kind, displayMessage, userMessage, conditionText, innerException);
425             }
426 #endif // !FEATURE_CORECLR
427             if (!Environment.UserInteractive) {
428                 throw new ContractException(kind, displayMessage, userMessage, conditionText, innerException);
429             }
430             // May need to rethink Assert.Fail w/ TaskDialogIndirect as a model.  Window title.  Main instruction.  Content.  Expanded info.
431             // Optional info like string for collapsed text vs. expanded text.
432             String windowTitle = Environment.GetResourceString(GetResourceNameForFailure(kind));
433             const int numStackFramesToSkip = 2;  // To make stack traces easier to read
434             System.Diagnostics.Assert.Fail(conditionText, displayMessage, windowTitle, COR_E_CODECONTRACTFAILED, StackTrace.TraceFormat.Normal, numStackFramesToSkip);
435             // If we got here, the user selected Ignore.  Continue.
436         }
437
438         private static String GetResourceNameForFailure(ContractFailureKind failureKind, bool withCondition = false)
439         {
440             String resourceName = null;
441             switch (failureKind)
442             {
443                 case ContractFailureKind.Assert:
444                     resourceName = withCondition ? "AssertionFailed_Cnd" : "AssertionFailed";
445                     break;
446
447                 case ContractFailureKind.Assume:
448                     resourceName = withCondition ? "AssumptionFailed_Cnd" : "AssumptionFailed";
449                     break;
450
451                 case ContractFailureKind.Precondition:
452                     resourceName = withCondition ? "PreconditionFailed_Cnd" : "PreconditionFailed";
453                     break;
454
455                 case ContractFailureKind.Postcondition:
456                     resourceName = withCondition ? "PostconditionFailed_Cnd" : "PostconditionFailed";
457                     break;
458
459                 case ContractFailureKind.Invariant:
460                     resourceName = withCondition ? "InvariantFailed_Cnd" : "InvariantFailed";
461                     break;
462
463                 case ContractFailureKind.PostconditionOnException:
464                     resourceName = withCondition ? "PostconditionOnExceptionFailed_Cnd" : "PostconditionOnExceptionFailed";
465                     break;
466
467                 default:
468                     Contract.Assume(false, "Unreachable code");
469                     resourceName = "AssumptionFailed";
470                     break;
471             }
472             return resourceName;
473         }
474
475 #if FEATURE_RELIABILITY_CONTRACTS
476         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
477 #endif
478         private static String GetDisplayMessage(ContractFailureKind failureKind, String userMessage, String conditionText)
479         {
480             String resourceName = GetResourceNameForFailure(failureKind, !String.IsNullOrEmpty(conditionText));
481             // Well-formatted English messages will take one of four forms.  A sentence ending in
482             // either a period or a colon, the condition string, then the message tacked 
483             // on to the end with two spaces in front.
484             // Note that both the conditionText and userMessage may be null.  Also, 
485             // on Silverlight we may not be able to look up a friendly string for the
486             // error message.  Let's leverage Silverlight's default error message there.
487             String failureMessage;
488             if (!String.IsNullOrEmpty(conditionText)) {
489                 failureMessage = Environment.GetResourceString(resourceName, conditionText);
490             }
491             else {
492                 failureMessage = Environment.GetResourceString(resourceName);
493             }
494
495             // Now add in the user message, if present.
496             if (!String.IsNullOrEmpty(userMessage))
497             {
498                 return failureMessage + "  " + userMessage;
499             }
500             else
501             {
502                 return failureMessage;
503             }
504         }
505
506 #if !FEATURE_CORECLR
507         // Will trigger escalation policy, if hosted and the host requested us to do something (such as 
508         // abort the thread or exit the process).  Starting in Dev11, for hosted apps the default behavior 
509         // is to throw an exception.  
510         // Implementation notes:
511         // We implement our default behavior of throwing an exception by simply returning from our native 
512         // method inside the runtime and falling through to throw an exception.
513         // We must call through this method before calling the method on the Environment class
514         // because our security team does not yet support SecuritySafeCritical on P/Invoke methods.
515         // Note this can be called in the context of throwing another exception (EnsuresOnThrow).
516         [SecuritySafeCritical]
517         [DebuggerNonUserCode]
518         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
519         private static void TriggerCodeContractEscalationPolicy(ContractFailureKind failureKind, String message, String conditionText, Exception innerException)
520         {
521             String exceptionAsString = null;
522             if (innerException != null)
523                 exceptionAsString = innerException.ToString();
524             Environment.TriggerCodeContractFailure(failureKind, message, conditionText, exceptionAsString);
525         }
526 #endif // !FEATURE_CORECLR
527     }
528 }  // namespace System.Runtime.CompilerServices
529