网上演练
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

188 lines
8.5 KiB

#if NET20 || NET30 || NET35 || NET40 || !NET_4_6
// 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.
**
===========================================================*/
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Runtime.ConstrainedExecution;
using System.Security;
namespace System.Runtime.CompilerServices
{
public static class ContractHelper
{
[DebuggerNonUserCode]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static string RaiseContractFailedEvent(ContractFailureKind failureKind, string userMessage, string conditionText, Exception innerException)
{
var resultFailureMessage = "Contract failed"; // default in case implementation does not assign anything.
RaiseContractFailedEventImplementation(failureKind, userMessage, conditionText, innerException, ref resultFailureMessage);
return resultFailureMessage;
}
[DebuggerNonUserCode]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public static void TriggerFailure(ContractFailureKind kind, string displayMessage, string userMessage, string conditionText, Exception innerException)
{
TriggerFailureImplementation(kind, displayMessage, userMessage, conditionText, innerException);
}
internal const int Cor_E_Codecontractfailed = unchecked((int)0x80131542);
private static readonly object _lockObject = new object();
private static volatile EventHandler<ContractFailedEventArgs> _contractFailedEvent;
/// <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
{
[SecurityCritical]
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); // TODO? I'm afraid I can't do that.
lock (_lockObject)
{
_contractFailedEvent += value;
}
}
[SecurityCritical]
remove
{
lock (_lockObject)
{
_contractFailedEvent -= value;
}
}
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
private static string GetDisplayMessage(ContractFailureKind failureKind, string userMessage,
string conditionText)
{
// 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.
var failureMessage = ContractHelperEx.GetFailureMessage(failureKind, conditionText);
// Now add in the user message, if present.
if (!string.IsNullOrEmpty(userMessage))
{
return failureMessage + " " + userMessage;
}
return failureMessage;
}
[DebuggerNonUserCode]
[SecuritySafeCritical]
private static void RaiseContractFailedEventImplementation(ContractFailureKind failureKind, string userMessage,
string conditionText, Exception innerException, ref string resultFailureMessage)
{
if (failureKind < ContractFailureKind.Precondition || failureKind > ContractFailureKind.Assume)
{
throw new ArgumentException(string.Format("Invalid enum value: {0}", failureKind), "failureKind");
}
Contract.EndContractBlock();
string returnValue;
var displayMessage = "contract failed."; // Incomplete, but in case of OOM during resource lookup...
ContractFailedEventArgs eventArgs = null; // In case of OOM.
RuntimeHelpers.PrepareConstrainedRegions();
try
{
displayMessage = GetDisplayMessage(failureKind, userMessage, conditionText);
var contractFailedEventLocal = _contractFailedEvent;
if (contractFailedEventLocal != null)
{
eventArgs = new ContractFailedEventArgs(failureKind, displayMessage, conditionText, innerException);
foreach (var @delegate in contractFailedEventLocal.GetInvocationList())
{
var handler = (EventHandler<ContractFailedEventArgs>)@delegate;
try
{
handler(null, eventArgs);
}
catch (Exception e)
{
#if NET20 || NET30 || NET35
eventArgs.ThrownDuringHandler = e;
#else
GC.KeepAlive(e);
#endif
eventArgs.SetUnwind();
}
}
if (eventArgs.Unwind)
{
#if NET20 || NET30 || NET35
// unwind
if (innerException == null)
{
innerException = eventArgs.ThrownDuringHandler;
}
#endif
throw new ContractException(failureKind, displayMessage, userMessage, conditionText, innerException);
}
}
}
finally
{
if (eventArgs != null && eventArgs.Handled)
{
returnValue = null; // handled
}
else
{
returnValue = displayMessage;
}
}
resultFailureMessage = returnValue;
}
[DebuggerNonUserCode]
[SecuritySafeCritical]
private static 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 (!Environment.UserInteractive)
{
throw new ContractException(kind, displayMessage, userMessage, conditionText, innerException);
}
ContractHelperEx.Fail(displayMessage);
}
}
}
#endif