diff options
Diffstat (limited to 'src/mscorlib/src/System/Diagnostics/Contracts/ContractsBCL.cs')
-rw-r--r-- | src/mscorlib/src/System/Diagnostics/Contracts/ContractsBCL.cs | 521 |
1 files changed, 521 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Diagnostics/Contracts/ContractsBCL.cs b/src/mscorlib/src/System/Diagnostics/Contracts/ContractsBCL.cs new file mode 100644 index 0000000000..804318e702 --- /dev/null +++ b/src/mscorlib/src/System/Diagnostics/Contracts/ContractsBCL.cs @@ -0,0 +1,521 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** Implementation details of CLR Contracts. +** +===========================================================*/ +#define DEBUG // The behavior of this contract library should be consistent regardless of build type. + +#if SILVERLIGHT +#define FEATURE_UNTRUSTED_CALLERS +#elif REDHAWK_RUNTIME + +#elif BARTOK_RUNTIME + +#else // CLR +#define FEATURE_UNTRUSTED_CALLERS +#define FEATURE_RELIABILITY_CONTRACTS +#define FEATURE_SERIALIZATION +#endif + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Reflection; + +#if FEATURE_RELIABILITY_CONTRACTS +using System.Runtime.ConstrainedExecution; +#endif +#if FEATURE_UNTRUSTED_CALLERS +using System.Security; +using System.Security.Permissions; +#endif + +namespace System.Diagnostics.Contracts { + + public static partial class Contract + { + #region Private Methods + + [ThreadStatic] + private static bool _assertingMustUseRewriter; + + /// <summary> + /// This method is used internally to trigger a failure indicating to the "programmer" that he is using the interface incorrectly. + /// It is NEVER used to indicate failure of actual contracts at runtime. + /// </summary> + [SecuritySafeCritical] + static partial void AssertMustUseRewriter(ContractFailureKind kind, String contractKind) + { + if (_assertingMustUseRewriter) + System.Diagnostics.Assert.Fail("Asserting that we must use the rewriter went reentrant.", "Didn't rewrite this mscorlib?"); + _assertingMustUseRewriter = true; + + // For better diagnostics, report which assembly is at fault. Walk up stack and + // find the first non-mscorlib assembly. + Assembly thisAssembly = typeof(Contract).Assembly; // In case we refactor mscorlib, use Contract class instead of Object. + StackTrace stack = new StackTrace(); + Assembly probablyNotRewritten = null; + for (int i = 0; i < stack.FrameCount; i++) + { + Assembly caller = stack.GetFrame(i).GetMethod().DeclaringType.Assembly; + if (caller != thisAssembly) + { + probablyNotRewritten = caller; + break; + } + } + + if (probablyNotRewritten == null) + probablyNotRewritten = thisAssembly; + String simpleName = probablyNotRewritten.GetName().Name; + System.Runtime.CompilerServices.ContractHelper.TriggerFailure(kind, Environment.GetResourceString("MustUseCCRewrite", contractKind, simpleName), null, null, null); + + _assertingMustUseRewriter = false; + } + + #endregion Private Methods + + #region Failure Behavior + + /// <summary> + /// Without contract rewriting, failing Assert/Assumes end up calling this method. + /// Code going through the contract rewriter never calls this method. Instead, the rewriter produced failures call + /// System.Runtime.CompilerServices.ContractHelper.RaiseContractFailedEvent, followed by + /// System.Runtime.CompilerServices.ContractHelper.TriggerFailure. + /// </summary> + [SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Security.SecuritySafeCriticalAttribute")] + [System.Diagnostics.DebuggerNonUserCode] +#if FEATURE_RELIABILITY_CONTRACTS + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] +#endif + static partial void ReportFailure(ContractFailureKind failureKind, String userMessage, String conditionText, Exception innerException) + { + if (failureKind < ContractFailureKind.Precondition || failureKind > ContractFailureKind.Assume) + throw new ArgumentException(Environment.GetResourceString("Arg_EnumIllegalVal", failureKind), "failureKind"); + Contract.EndContractBlock(); + + // displayMessage == null means: yes we handled it. Otherwise it is the localized failure message + var displayMessage = System.Runtime.CompilerServices.ContractHelper.RaiseContractFailedEvent(failureKind, userMessage, conditionText, innerException); + + if (displayMessage == null) return; + + System.Runtime.CompilerServices.ContractHelper.TriggerFailure(failureKind, displayMessage, userMessage, conditionText, innerException); + } + + /// <summary> + /// Allows a managed application environment such as an interactive interpreter (IronPython) + /// to be notified of contract failures and + /// potentially "handle" them, either by throwing a particular exception type, etc. If any of the + /// event handlers sets the Cancel flag in the ContractFailedEventArgs, then the Contract class will + /// not pop up an assert dialog box or trigger escalation policy. Hooking this event requires + /// full trust, because it will inform you of bugs in the appdomain and because the event handler + /// could allow you to continue execution. + /// </summary> + public static event EventHandler<ContractFailedEventArgs> ContractFailed { +#if FEATURE_UNTRUSTED_CALLERS + [SecurityCritical] +#if FEATURE_LINK_DEMAND + [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] +#endif +#endif + add { + System.Runtime.CompilerServices.ContractHelper.InternalContractFailed += value; + } +#if FEATURE_UNTRUSTED_CALLERS + [SecurityCritical] +#if FEATURE_LINK_DEMAND + [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] +#endif +#endif + remove { + System.Runtime.CompilerServices.ContractHelper.InternalContractFailed -= value; + } + } + #endregion FailureBehavior + } + + public sealed class ContractFailedEventArgs : EventArgs + { + private ContractFailureKind _failureKind; + private String _message; + private String _condition; + private Exception _originalException; + private bool _handled; + private bool _unwind; + + internal Exception thrownDuringHandler; + +#if FEATURE_RELIABILITY_CONTRACTS + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] +#endif + public ContractFailedEventArgs(ContractFailureKind failureKind, String message, String condition, Exception originalException) + { + Contract.Requires(originalException == null || failureKind == ContractFailureKind.PostconditionOnException); + _failureKind = failureKind; + _message = message; + _condition = condition; + _originalException = originalException; + } + + public String Message { get { return _message; } } + public String Condition { get { return _condition; } } + public ContractFailureKind FailureKind { get { return _failureKind; } } + public Exception OriginalException { get { return _originalException; } } + + // Whether the event handler "handles" this contract failure, or to fail via escalation policy. + public bool Handled { + get { return _handled; } + } + +#if FEATURE_UNTRUSTED_CALLERS + [SecurityCritical] +#if FEATURE_LINK_DEMAND + [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] +#endif +#endif + public void SetHandled() + { + _handled = true; + } + + public bool Unwind { + get { return _unwind; } + } + +#if FEATURE_UNTRUSTED_CALLERS + [SecurityCritical] +#if FEATURE_LINK_DEMAND + [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] +#endif +#endif + public void SetUnwind() + { + _unwind = true; + } + } + + [Serializable] + [SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic")] + internal sealed class ContractException : Exception + { + readonly ContractFailureKind _Kind; + readonly string _UserMessage; + readonly string _Condition; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public ContractFailureKind Kind { get { return _Kind; } } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public string Failure { get { return this.Message; } } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public string UserMessage { get { return _UserMessage; } } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public string Condition { get { return _Condition; } } + + // Called by COM Interop, if we see COR_E_CODECONTRACTFAILED as an HRESULT. + private ContractException() + { + HResult = System.Runtime.CompilerServices.ContractHelper.COR_E_CODECONTRACTFAILED; + } + + public ContractException(ContractFailureKind kind, string failure, string userMessage, string condition, Exception innerException) + : base(failure, innerException) + { + HResult = System.Runtime.CompilerServices.ContractHelper.COR_E_CODECONTRACTFAILED; + this._Kind = kind; + this._UserMessage = userMessage; + this._Condition = condition; + } + + private ContractException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) + { + _Kind = (ContractFailureKind)info.GetInt32("Kind"); + _UserMessage = info.GetString("UserMessage"); + _Condition = info.GetString("Condition"); + } + +#if FEATURE_UNTRUSTED_CALLERS && FEATURE_SERIALIZATION + [SecurityCritical] +#if FEATURE_LINK_DEMAND && FEATURE_SERIALIZATION + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] +#endif // FEATURE_LINK_DEMAND +#endif // FEATURE_UNTRUSTED_CALLERS + public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + { + base.GetObjectData(info, context); + + info.AddValue("Kind", _Kind); + info.AddValue("UserMessage", _UserMessage); + info.AddValue("Condition", _Condition); + } + } +} + + +namespace System.Runtime.CompilerServices +{ + public static partial class ContractHelper + { + #region Private fields + + private static volatile EventHandler<ContractFailedEventArgs> contractFailedEvent; + private static readonly Object lockObject = new Object(); + + internal const int COR_E_CODECONTRACTFAILED = unchecked((int)0x80131542); + + #endregion + + /// <summary> + /// Allows a managed application environment such as an interactive interpreter (IronPython) or a + /// web browser host (Jolt hosting Silverlight in IE) to be notified of contract failures and + /// potentially "handle" them, either by throwing a particular exception type, etc. If any of the + /// event handlers sets the Cancel flag in the ContractFailedEventArgs, then the Contract class will + /// not pop up an assert dialog box or trigger escalation policy. Hooking this event requires + /// full trust. + /// </summary> + internal static event EventHandler<ContractFailedEventArgs> InternalContractFailed + { +#if FEATURE_UNTRUSTED_CALLERS + [SecurityCritical] +#endif + add { + // Eagerly prepare each event handler _marked with a reliability contract_, to + // attempt to reduce out of memory exceptions while reporting contract violations. + // This only works if the new handler obeys the constraints placed on + // constrained execution regions. Eagerly preparing non-reliable event handlers + // would be a perf hit and wouldn't significantly improve reliability. + // UE: Please mention reliable event handlers should also be marked with the + // PrePrepareMethodAttribute to avoid CER eager preparation work when ngen'ed. + System.Runtime.CompilerServices.RuntimeHelpers.PrepareContractedDelegate(value); + lock (lockObject) + { + contractFailedEvent += value; + } + } +#if FEATURE_UNTRUSTED_CALLERS + [SecurityCritical] +#endif + remove { + lock (lockObject) + { + contractFailedEvent -= value; + } + } + } + + /// <summary> + /// Rewriter will call this method on a contract failure to allow listeners to be notified. + /// The method should not perform any failure (assert/throw) itself. + /// This method has 3 functions: + /// 1. Call any contract hooks (such as listeners to Contract failed events) + /// 2. Determine if the listeneres deem the failure as handled (then resultFailureMessage should be set to null) + /// 3. Produce a localized resultFailureMessage used in advertising the failure subsequently. + /// </summary> + /// <param name="resultFailureMessage">Should really be out (or the return value), but partial methods are not flexible enough. + /// On exit: null if the event was handled and should not trigger a failure. + /// Otherwise, returns the localized failure message</param> + [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + [System.Diagnostics.DebuggerNonUserCode] +#if FEATURE_RELIABILITY_CONTRACTS + [SecuritySafeCritical] +#endif + static partial void RaiseContractFailedEventImplementation(ContractFailureKind failureKind, String userMessage, String conditionText, Exception innerException, ref string resultFailureMessage) + { + if (failureKind < ContractFailureKind.Precondition || failureKind > ContractFailureKind.Assume) + throw new ArgumentException(Environment.GetResourceString("Arg_EnumIllegalVal", failureKind), "failureKind"); + Contract.EndContractBlock(); + + string returnValue; + String displayMessage = "contract failed."; // Incomplete, but in case of OOM during resource lookup... + ContractFailedEventArgs eventArgs = null; // In case of OOM. +#if FEATURE_RELIABILITY_CONTRACTS + System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions(); +#endif + try + { + displayMessage = GetDisplayMessage(failureKind, userMessage, conditionText); + EventHandler<ContractFailedEventArgs> contractFailedEventLocal = contractFailedEvent; + if (contractFailedEventLocal != null) + { + eventArgs = new ContractFailedEventArgs(failureKind, displayMessage, conditionText, innerException); + foreach (EventHandler<ContractFailedEventArgs> handler in contractFailedEventLocal.GetInvocationList()) + { + try + { + handler(null, eventArgs); + } + catch (Exception e) + { + eventArgs.thrownDuringHandler = e; + eventArgs.SetUnwind(); + } + } + if (eventArgs.Unwind) + { +#if !FEATURE_CORECLR + if (Environment.IsCLRHosted) + TriggerCodeContractEscalationPolicy(failureKind, displayMessage, conditionText, innerException); +#endif + // unwind + if (innerException == null) { innerException = eventArgs.thrownDuringHandler; } + throw new ContractException(failureKind, displayMessage, userMessage, conditionText, innerException); + } + } + } + finally + { + if (eventArgs != null && eventArgs.Handled) + { + returnValue = null; // handled + } + else + { + returnValue = displayMessage; + } + } + resultFailureMessage = returnValue; + } + + /// <summary> + /// Rewriter calls this method to get the default failure behavior. + /// </summary> + [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "conditionText")] + [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "userMessage")] + [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "kind")] + [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "innerException")] + [System.Diagnostics.DebuggerNonUserCode] +#if FEATURE_UNTRUSTED_CALLERS && !FEATURE_CORECLR + [SecuritySafeCritical] +#endif + static partial void TriggerFailureImplementation(ContractFailureKind kind, String displayMessage, String userMessage, String conditionText, Exception innerException) + { + // If we're here, our intent is to pop up a dialog box (if we can). For developers + // interacting live with a debugger, this is a good experience. For Silverlight + // hosted in Internet Explorer, the assert window is great. If we cannot + // pop up a dialog box, throw an exception (consider a library compiled with + // "Assert On Failure" but used in a process that can't pop up asserts, like an + // NT Service). For the CLR hosted by server apps like SQL or Exchange, we should + // trigger escalation policy. +#if !FEATURE_CORECLR + if (Environment.IsCLRHosted) + { + TriggerCodeContractEscalationPolicy(kind, displayMessage, conditionText, innerException); + // Hosts like SQL may choose to abort the thread, so we will not get here in all cases. + // But if the host's chosen action was to throw an exception, we should throw an exception + // here (which is easier to do in managed code with the right parameters). + throw new ContractException(kind, displayMessage, userMessage, conditionText, innerException); + } +#endif // !FEATURE_CORECLR + if (!Environment.UserInteractive) { + throw new ContractException(kind, displayMessage, userMessage, conditionText, innerException); + } + // May need to rethink Assert.Fail w/ TaskDialogIndirect as a model. Window title. Main instruction. Content. Expanded info. + // Optional info like string for collapsed text vs. expanded text. + String windowTitle = Environment.GetResourceString(GetResourceNameForFailure(kind)); + const int numStackFramesToSkip = 2; // To make stack traces easier to read + System.Diagnostics.Assert.Fail(conditionText, displayMessage, windowTitle, COR_E_CODECONTRACTFAILED, StackTrace.TraceFormat.Normal, numStackFramesToSkip); + // If we got here, the user selected Ignore. Continue. + } + + private static String GetResourceNameForFailure(ContractFailureKind failureKind) + { + String resourceName = null; + switch (failureKind) + { + case ContractFailureKind.Assert: + resourceName = "AssertionFailed"; + break; + + case ContractFailureKind.Assume: + resourceName = "AssumptionFailed"; + break; + + case ContractFailureKind.Precondition: + resourceName = "PreconditionFailed"; + break; + + case ContractFailureKind.Postcondition: + resourceName = "PostconditionFailed"; + break; + + case ContractFailureKind.Invariant: + resourceName = "InvariantFailed"; + break; + + case ContractFailureKind.PostconditionOnException: + resourceName = "PostconditionOnExceptionFailed"; + break; + + default: + Contract.Assume(false, "Unreachable code"); + resourceName = "AssumptionFailed"; + break; + } + return resourceName; + } + +#if FEATURE_RELIABILITY_CONTRACTS + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] +#endif + private static String GetDisplayMessage(ContractFailureKind failureKind, String userMessage, String conditionText) + { + String resourceName = GetResourceNameForFailure(failureKind); + // Well-formatted English messages will take one of four forms. A sentence ending in + // either a period or a colon, the condition string, then the message tacked + // on to the end with two spaces in front. + // Note that both the conditionText and userMessage may be null. Also, + // on Silverlight we may not be able to look up a friendly string for the + // error message. Let's leverage Silverlight's default error message there. + String failureMessage; + if (!String.IsNullOrEmpty(conditionText)) { + resourceName += "_Cnd"; + failureMessage = Environment.GetResourceString(resourceName, conditionText); + } + else { + failureMessage = Environment.GetResourceString(resourceName); + } + + // Now add in the user message, if present. + if (!String.IsNullOrEmpty(userMessage)) + { + return failureMessage + " " + userMessage; + } + else + { + return failureMessage; + } + } + +#if !FEATURE_CORECLR + // Will trigger escalation policy, if hosted and the host requested us to do something (such as + // abort the thread or exit the process). Starting in Dev11, for hosted apps the default behavior + // is to throw an exception. + // Implementation notes: + // We implement our default behavior of throwing an exception by simply returning from our native + // method inside the runtime and falling through to throw an exception. + // We must call through this method before calling the method on the Environment class + // because our security team does not yet support SecuritySafeCritical on P/Invoke methods. + // Note this can be called in the context of throwing another exception (EnsuresOnThrow). + [SecuritySafeCritical] + [DebuggerNonUserCode] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + private static void TriggerCodeContractEscalationPolicy(ContractFailureKind failureKind, String message, String conditionText, Exception innerException) + { + String exceptionAsString = null; + if (innerException != null) + exceptionAsString = innerException.ToString(); + Environment.TriggerCodeContractFailure(failureKind, message, conditionText, exceptionAsString); + } +#endif // !FEATURE_CORECLR + } +} // namespace System.Runtime.CompilerServices + |