// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
// <OWNER>[....]</OWNER>
// 

namespace System.Security.Policy
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Configuration.Assemblies;
    using System.Diagnostics.Contracts;
    using System.IO;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Runtime.Remoting;
#if FEATURE_SERIALIZATION
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary;
#endif // FEATURE_SERIALIZATION
    using System.Security.Permissions;
    using System.Security.Util;
    using System.Threading;
    using Microsoft.Win32.SafeHandles;

    /// <summary>
    ///     The Evidence class keeps track of information that can be used to make security decisions about
    ///     an assembly or an AppDomain.  There are two types of evidence, one is supplied by the CLR or a
    ///     host, the other supplied by the assembly itself.
    ///     
    ///     We keep a dictionary that maps each type of possbile evidence to an EvidenceTypeDescriptor which
    ///     contains the evidence objects themselves if they exist as well as some extra metadata about that
    ///     type of evidence.  This dictionary is fully populated with keys for host evidence at all times and
    ///     for assembly evidence the first time the application evidence is touched.  This means that if a
    ///     Type key does not exist in the dictionary, then that particular type of evidence will never be
    ///     given to the assembly or AppDomain in question as host evidence.  The only exception is if the
    ///     user later manually adds host evidence via the AddHostEvidence API.
    ///     
    ///     Assembly supplied evidence is created up front, however host supplied evidence may be lazily
    ///     created.  In the lazy creation case, the Type will map to either an EvidenceTypeDescriptor that does
    ///     not contain any evidence data or null.  As requests come in for that evidence, we'll populate the
    ///     EvidenceTypeDescriptor appropriately.
    /// </summary>
    [Serializable]
    [ComVisible(true)]
    public sealed class Evidence
#if FEATURE_CAS_POLICY
 : ICollection
#endif // FEATURE_CAS_POLICY
    {
#if !FEATURE_CORECLR && FEATURE_RWLOCK
#if FEATURE_SERIALIZATION
        [OptionalField(VersionAdded = 4)]
        private Dictionary<Type, EvidenceTypeDescriptor> m_evidence;

        [OptionalField(VersionAdded = 4)]
        private bool m_deserializedTargetEvidence;

        // These fields are only used to deserialize v2.0 serialized versions of Evidence. It will be null
        // after the seriailzation process is complete, and should not be used.
#pragma warning disable 414
        private volatile ArrayList m_hostList;
        private volatile ArrayList m_assemblyList;
#pragma warning restore 414
#else // !FEATURE_SERIALIZATION
        private Dictionary<Type, EvidenceTypeDescriptor> m_evidence;
#endif // FEATURE_SERIALIZATION

        [NonSerialized]
        private ReaderWriterLock m_evidenceLock;

        [NonSerialized]
        private uint m_version;

        [NonSerialized]
        private IRuntimeEvidenceFactory m_target;

        private bool m_locked;

        // If this evidence collection is a clone where we may need to backpatch to the original, this will
        // reference the collection it was cloned from.  See
        // code:System.Security.Policy.Evidence#BackpatchGeneratedEvidence 
        [NonSerialized]
        private WeakReference m_cloneOrigin;

        private static volatile Type[] s_runtimeEvidenceTypes;

        /// <summary>
        ///     Set of actions that we could perform if we detect that we are attempting to add evidence
        ///     when we already have evidence of that type stored.
        /// </summary>
        private enum DuplicateEvidenceAction
        {
            Throw,                  // Throw an exception
            Merge,                  // Create a list of all the evidence objects
            SelectNewObject         // The newly added object wins
        }

#if FEATURE_CAS_POLICY
        public Evidence()
        {
            m_evidence = new Dictionary<Type, EvidenceTypeDescriptor>();
            m_evidenceLock = new ReaderWriterLock();
        }
#endif // FEATURE_CAS_POLICY

        /// <summary>
        ///     Create a deep copy of an evidence object
        /// </summary>
        public Evidence(Evidence evidence)
        {
            m_evidence = new Dictionary<Type, EvidenceTypeDescriptor>();

            if (evidence != null)
            {
                using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(evidence, EvidenceLockHolder.LockType.Reader))
                {
                    foreach (KeyValuePair<Type, EvidenceTypeDescriptor> evidenceType in evidence.m_evidence)
                    {
                        EvidenceTypeDescriptor cloneDescriptor = evidenceType.Value;
                        if (cloneDescriptor != null)
                        {
                            cloneDescriptor = cloneDescriptor.Clone();
                        }

                        m_evidence[evidenceType.Key] = cloneDescriptor;
                    }

                    m_target = evidence.m_target;
                    m_locked = evidence.m_locked;
#if FEATURE_SERIALIZATION
                    m_deserializedTargetEvidence = evidence.m_deserializedTargetEvidence;
#endif // FEATURE_SERIALIZATION

                    // see code:System.Security.Policy.Evidence#BackpatchGeneratedEvidence
                    if (evidence.Target != null)
                    {
                        m_cloneOrigin = new WeakReference(evidence);
                    }
                }
            }

            // see code:System.Security.Policy.Evidence#EvidenceLock
            m_evidenceLock = new ReaderWriterLock();
        }

        [Obsolete("This constructor is obsolete. Please use the constructor which takes arrays of EvidenceBase instead.")]
        public Evidence(object[] hostEvidence, object[] assemblyEvidence)
        {
            m_evidence = new Dictionary<Type, EvidenceTypeDescriptor>();

            // This is a legacy evidence entry point, so we add through the legacy add APIs in order to get
            // proper legacy wrapping and merge behavior.
#pragma warning disable 618
            if (hostEvidence != null)
            {
                foreach (object hostEvidenceObject in hostEvidence)
                {
                    AddHost(hostEvidenceObject);
                }
            }

            if (assemblyEvidence != null)
            {
                foreach (object assemblyEvidenceObject in assemblyEvidence)
                {
                    AddAssembly(assemblyEvidenceObject);
                }
            }
#pragma warning restore 618

            // see code:System.Security.Policy.Evidence#EvidenceLock
            m_evidenceLock = new ReaderWriterLock();
        }

        public Evidence(EvidenceBase[] hostEvidence, EvidenceBase[] assemblyEvidence)
        {
            m_evidence = new Dictionary<Type, EvidenceTypeDescriptor>();

            if (hostEvidence != null)
            {
                foreach (EvidenceBase hostEvidenceObject in hostEvidence)
                {
                    AddHostEvidence(hostEvidenceObject, GetEvidenceIndexType(hostEvidenceObject), DuplicateEvidenceAction.Throw);
                }
            }

            if (assemblyEvidence != null)
            {
                foreach (EvidenceBase assemblyEvidenceObject in assemblyEvidence)
                {
                    AddAssemblyEvidence(assemblyEvidenceObject, GetEvidenceIndexType(assemblyEvidenceObject), DuplicateEvidenceAction.Throw);
                }
            }

            // see code:System.Security.Policy.Evidence#EvidenceLock
            m_evidenceLock = new ReaderWriterLock();
        }

        /// <summary>
        ///     Create an empty evidence collection which will contain evidence for a specific assembly or
        ///     AppDomain
        /// </summary>
        [SecuritySafeCritical]
        internal Evidence(IRuntimeEvidenceFactory target)
        {
            Contract.Assert(target != null);

            m_evidence = new Dictionary<Type, EvidenceTypeDescriptor>();
            m_target = target;

            // Setup the types of evidence that the CLR can generate for a target as keys in the dictionary
            foreach (Type runtimeEvidenceType in RuntimeEvidenceTypes)
            {
                BCLDebug.Assert(typeof(EvidenceBase).IsAssignableFrom(runtimeEvidenceType), "All runtime evidence types should be EvidenceBases");
                m_evidence[runtimeEvidenceType] = null;
            }

            QueryHostForPossibleEvidenceTypes();

            // see code:System.Security.Policy.Evidence#EvidenceLock
            m_evidenceLock = new ReaderWriterLock();
        }

        internal static Type[] RuntimeEvidenceTypes
        {
            get
            {
                if (s_runtimeEvidenceTypes == null)
                {
                    Type[] runtimeEvidenceTypes = new Type[]
                    {
#if FEATURE_CLICKONCE
                        typeof(System.Runtime.Hosting.ActivationArguments),
#endif // FEATURE_CLICKONCE
#if FEATURE_CAS_POLICY
                        typeof(ApplicationDirectory),
#endif // FEATURE_CAS_POLICY
                        typeof(ApplicationTrust),
#if FEATURE_CAS_POLICY
                        typeof(GacInstalled),
                        typeof(Hash),
                        typeof(Publisher),
#endif // FEATURE_CAS_POLICY
                        typeof(Site),
                        typeof(StrongName),
                        typeof(Url),
                        typeof(Zone)
                    };

#if FEATURE_CAS_POLICY
                    // We only supply permission request evidence in legacy CAS mode
                    if (AppDomain.CurrentDomain.IsLegacyCasPolicyEnabled)
                    {
#pragma warning disable 618 // We need to generate PermissionRequestEvidence in compatibility mode
                        int l = runtimeEvidenceTypes.Length;
                        Array.Resize(ref runtimeEvidenceTypes, l+1);
                        runtimeEvidenceTypes[l] = typeof(PermissionRequestEvidence);
#pragma warning restore 618
                    }
#endif // FEATURE_CAS_POLICY

                    s_runtimeEvidenceTypes = runtimeEvidenceTypes;
                }

                return s_runtimeEvidenceTypes;
            }
        }

        //
        // #EvidenceLock
        // 
        // Evidence synchronization locking wrappers. In the case where the lock has not yet been created,
        // we know that we're in the process of constructing the evidence collection and therefore we can
        // act as though the evidence is locked.  If there is a lock in place, then just delegate back to it.
        //
        // The nested EvidenceLockHolder and EvidenceUpgradeLockHolder utility classes can be used to wrap
        // these methods when acquiring and releasing the evidence lock.
        //

        // Millisecond timeout when waiting to acquire the evidence lock
        private const int LockTimeout = 5000;

        private bool IsReaderLockHeld
        {
            get { return m_evidenceLock == null || m_evidenceLock.IsReaderLockHeld; }
        }

        private bool IsWriterLockHeld
        {
            get { return m_evidenceLock == null || m_evidenceLock.IsWriterLockHeld; }
        }

        private void AcquireReaderLock()
        {
            Contract.Assert(m_evidenceLock == null || !IsReaderLockHeld);

            if (m_evidenceLock != null)
            {
                m_evidenceLock.AcquireReaderLock(LockTimeout);
            }
        }

        private void AcquireWriterlock()
        {
            Contract.Assert(m_evidenceLock == null || !IsWriterLockHeld);

            if (m_evidenceLock != null)
            {
                m_evidenceLock.AcquireWriterLock(LockTimeout);
            }
        }

        private void DowngradeFromWriterLock(ref LockCookie lockCookie)
        {
            Contract.Assert(IsWriterLockHeld);
            if (m_evidenceLock != null)
            {
                m_evidenceLock.DowngradeFromWriterLock(ref lockCookie);
            }
        }

        private LockCookie UpgradeToWriterLock()
        {
            Contract.Assert(IsReaderLockHeld);
            return m_evidenceLock != null ? m_evidenceLock.UpgradeToWriterLock(LockTimeout) : new LockCookie();
        }

        private void ReleaseReaderLock()
        {
            Contract.Assert(IsReaderLockHeld);

            if (m_evidenceLock != null)
            {
                m_evidenceLock.ReleaseReaderLock();
            }
        }

        private void ReleaseWriterLock()
        {
            Contract.Assert(IsWriterLockHeld);

            if (m_evidenceLock != null)
            {
                m_evidenceLock.ReleaseWriterLock();
            }
        }

        [Obsolete("This method is obsolete. Please use AddHostEvidence instead.")]
        [SecuritySafeCritical]
        public void AddHost(object id)
        {
            if (id == null)
                throw new ArgumentNullException("id");
            if (!id.GetType().IsSerializable)
                throw new ArgumentException(Environment.GetResourceString("Policy_EvidenceMustBeSerializable"), "id");
            Contract.EndContractBlock();

            if (m_locked)
            {
                new SecurityPermission(SecurityPermissionFlag.ControlEvidence).Demand();
            }

            EvidenceBase evidence = WrapLegacyEvidence(id);
            Type evidenceIndex = GetEvidenceIndexType(evidence);

            // Whidbey allowed for multiple types of the same evidence, so if we're being called via the Whidbey
            // APIs, then allow the evidences to merge together.
            AddHostEvidence(evidence, evidenceIndex, DuplicateEvidenceAction.Merge);
        }

        [Obsolete("This method is obsolete. Please use AddAssemblyEvidence instead.")]
        public void AddAssembly(object id)
        {
            if (id == null)
                throw new ArgumentNullException("id");
            if (!id.GetType().IsSerializable)
                throw new ArgumentException(Environment.GetResourceString("Policy_EvidenceMustBeSerializable"), "id");
            Contract.EndContractBlock();

            EvidenceBase evidence = WrapLegacyEvidence(id);
            Type evidenceIndex = GetEvidenceIndexType(evidence);

            // Whidbey allowed for multiple types of the same evidence, so if we're being called via the Whidbey
            // APIs, then allow the evidences to merge together.
            AddAssemblyEvidence(evidence, evidenceIndex, DuplicateEvidenceAction.Merge);
        }

        /// <summary>
        ///     Add a piece of evidence to the assembly supplied evidence list. This method will disallow adding
        ///     evidence if there is already evidence of that type in the assembly list.
        /// </summary>
        [ComVisible(false)]
        public void AddAssemblyEvidence<T>(T evidence) where T : EvidenceBase
        {
            if (evidence == null)
                throw new ArgumentNullException("evidence");
            Contract.EndContractBlock();

            // Index the evidence under the type that the Add function was called with, unless we were given
            // a plain EvidenceBase or a wrapped legacy evidence.  In that case, we need to index under a
            // more specific type.
            Type evidenceType = typeof(T);
            if (typeof(T) == typeof(EvidenceBase) || evidence is ILegacyEvidenceAdapter)
            {
                evidenceType = GetEvidenceIndexType(evidence);
            }

            AddAssemblyEvidence(evidence, evidenceType, DuplicateEvidenceAction.Throw);
        }

        private void AddAssemblyEvidence(EvidenceBase evidence, Type evidenceType, DuplicateEvidenceAction duplicateAction)
        {
            using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Writer))
            {
                AddAssemblyEvidenceNoLock(evidence, evidenceType, duplicateAction);
            }
        }

        private void AddAssemblyEvidenceNoLock(EvidenceBase evidence, Type evidenceType, DuplicateEvidenceAction duplicateAction)
        {
            Contract.Assert(IsWriterLockHeld);
            Contract.Assert(evidence != null);
            Contract.Assert(evidenceType != null);

            // We need to make sure that any target supplied evidence is deserialized before adding to the
            // Assembly collection in order to preserve the semantics that the evidence objects supplied by
            // the target are the original versions and evidence objects added via the APIs are the duplicates.
            DeserializeTargetEvidence();

            EvidenceTypeDescriptor descriptor = GetEvidenceTypeDescriptor(evidenceType, true);

            ++m_version;
            if (descriptor.AssemblyEvidence == null)
            {
                descriptor.AssemblyEvidence = evidence;
            }
            else
            {
                descriptor.AssemblyEvidence = HandleDuplicateEvidence(descriptor.AssemblyEvidence,
                                                                      evidence,
                                                                      duplicateAction);
            }
        }

        /// <summary>
        ///     Add a piece of evidence to the host supplied evidence list. This method will disallow adding
        ///     evidence if there is already evidence of that type in the host list.
        /// </summary>
        [ComVisible(false)]
        public void AddHostEvidence<T>(T evidence) where T : EvidenceBase
        {
            if (evidence == null)
                throw new ArgumentNullException("evidence");
            Contract.EndContractBlock();

            // Index the evidence under the type that the Add function was called with, unless we were given
            // a plain EvidenceBase or a wrapped legacy evidence.  In that case, we need to index under a
            // more specific type.
            Type evidenceType = typeof(T);
            if (typeof(T) == typeof(EvidenceBase) || evidence is ILegacyEvidenceAdapter)
            {
                evidenceType = GetEvidenceIndexType(evidence);
            }

            AddHostEvidence(evidence, evidenceType, DuplicateEvidenceAction.Throw);
        }

        [SecuritySafeCritical]
        private void AddHostEvidence(EvidenceBase evidence, Type evidenceType, DuplicateEvidenceAction duplicateAction)
        {
            Contract.Assert(evidence != null);
            Contract.Assert(evidenceType != null);

            if (Locked)
            {
                new SecurityPermission(SecurityPermissionFlag.ControlEvidence).Demand();
            }

            using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Writer))
            {
                AddHostEvidenceNoLock(evidence, evidenceType, duplicateAction);
            }
        }

        /// <summary>
        ///     Add evidence to the host supplied evidence collection without acquiring the evidence lock or
        ///     checking to make sure that the caller has permission to bypass locked evidence.
        /// </summary>
        private void AddHostEvidenceNoLock(EvidenceBase evidence, Type evidenceType, DuplicateEvidenceAction duplicateAction)
        {
            Contract.Assert(IsWriterLockHeld);
            Contract.Assert(evidence != null);
            Contract.Assert(evidenceType != null);

            EvidenceTypeDescriptor descriptor = GetEvidenceTypeDescriptor(evidenceType, true);

            ++m_version;
            if (descriptor.HostEvidence == null)
            {
                descriptor.HostEvidence = evidence;
            }
            else
            {
                descriptor.HostEvidence = HandleDuplicateEvidence(descriptor.HostEvidence,
                                                                  evidence,
                                                                  duplicateAction);
            }
        }

        /// <summary>
        ///     Ask the host for the types of evidence that it might provide if it is asked.
        ///     
        ///     This should only be called when setting up the Evidence collection to interact with the
        ///     host, and should not be used once that connection is established and the evidence has been
        ///     made available to user code.
        /// </summary>
        [SecurityCritical]
        private void QueryHostForPossibleEvidenceTypes()
        {
#if FEATURE_CAS_POLICY
            Contract.Assert(IsWriterLockHeld);

            // First check to see if we have a HostSecurityManager
            if (AppDomain.CurrentDomain.DomainManager != null)
            {
                HostSecurityManager hsm = AppDomain.CurrentDomain.DomainManager.HostSecurityManager;
                if (hsm != null)
                {
                    Type[] hostSuppliedTypes = null;

                    AppDomain targetDomain = m_target.Target as AppDomain;
                    Assembly targetAssembly = m_target.Target as Assembly;

                    //
                    // If the HostSecurityManager wants to supply evidence for the type of target that we have,
                    // then ask it what types of evidence it might supply.
                    //

                    if (targetAssembly != null &&
                        (hsm.Flags & HostSecurityManagerOptions.HostAssemblyEvidence) == HostSecurityManagerOptions.HostAssemblyEvidence)
                    {
                        hostSuppliedTypes = hsm.GetHostSuppliedAssemblyEvidenceTypes(targetAssembly);
                    }
                    else if (targetDomain != null &&
                             (hsm.Flags & HostSecurityManagerOptions.HostAppDomainEvidence) == HostSecurityManagerOptions.HostAppDomainEvidence)
                    {
                        hostSuppliedTypes = hsm.GetHostSuppliedAppDomainEvidenceTypes();
                    }

                    //
                    // Finally, mark the descriptor for each of the types that the host can supply to indicate
                    // we should ask the host to generate them if we're asked.
                    // 

                    if (hostSuppliedTypes != null)
                    {
                        foreach (Type hostEvidenceType in hostSuppliedTypes)
                        {
                            EvidenceTypeDescriptor evidenceDescriptor = GetEvidenceTypeDescriptor(hostEvidenceType, true);
                            evidenceDescriptor.HostCanGenerate = true;
                        }
                    }
                }
            }
#endif // FEATURE_CAS_POLICY
        }

        internal bool IsUnmodified
        {
            get { return m_version == 0; }
        }

        /// <summary>
        ///     Set or check to see if the evidence is locked.  Locked evidence cannot have its host supplied
        ///     evidence list be modified without a successful demand for ControlEvidence.  Any code can lock
        ///     evidence, but only code with ControlEvidence may unlock it.
        ///     
        ///     This lock is not the same as the synchronization lock that gates access to the evidence collection.
        /// </summary>
        public bool Locked
        {
            get
            {
                return m_locked;
            }

            [SecuritySafeCritical]
            set
            {
                if (!value)
                {
                    new SecurityPermission(SecurityPermissionFlag.ControlEvidence).Demand();

                    m_locked = false;
                }
                else
                {
                    m_locked = true;
                }
            }
        }

        /// <summary>
        ///     Target of any delay generated evidence objects
        /// </summary>
        internal IRuntimeEvidenceFactory Target
        {
            get { return m_target; }

            //
            // There are two retargeting scenarios supported:
            // 
            //   1. A PEFileEvidenceFactory is being upgraded to an AssemblyEvidenceFactory and we don't want
            //      to throw away any already generated evidence.
            //   2. A detached evidence collection is being applied to an AppDomain and that domain has a
            //      HostSecurityManager. In that case, we want to attach the target to the AppDomain to
            //      allow the HostSecurityManager to get callbacks for delay generated evidence.
            // 

            [SecurityCritical]
            set
            {
#if FEATURE_CAS_POLICY
                Contract.Assert((m_target != null && m_target is PEFileEvidenceFactory && value != null && value is AssemblyEvidenceFactory) ||
                                (m_target == null && value != null && value is AppDomainEvidenceFactory),
                                "Evidence retargeting should only be from PEFile -> Assembly or detached -> AppDomain.");
#endif // FEATURE_CAS_POLICY

                using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Writer))
                {
                    m_target = value;

                    // Since we've updated what we're pointing at, we need to query the host to determine what
                    // types of evidence that it can generate for this new target.
                    QueryHostForPossibleEvidenceTypes();
                }
            }
        }

        /// <summary>
        ///     Get the type that would be used to index into the evidence dictionary for this object
        /// </summary>
        private static Type GetEvidenceIndexType(EvidenceBase evidence)
        {
            Contract.Assert(evidence != null);

            //
            // Legacy wrapper evidence types should be indexed via the type of evidence that they're wrapping
            // so check to see if we have one of those; otherwise just return the type itself.
            //

            ILegacyEvidenceAdapter adapter = evidence as ILegacyEvidenceAdapter;
            return adapter == null ? evidence.GetType() : adapter.EvidenceType;
        }

        /// <summary>
        ///     Get the type descriptor for a specific type of evidence.  This method should be used instead
        ///     of accessing the dictionary directly as it will handle the case where a new descriptor needs
        ///     to be created.
        /// </summary>
        internal EvidenceTypeDescriptor GetEvidenceTypeDescriptor(Type evidenceType)
        {
            return GetEvidenceTypeDescriptor(evidenceType, false);
        }

        /// <summary>
        ///     Get the type descriptor for a specific type of evidence, optionally creating a descriptor if
        ///     we did not yet know about this type of evidence.  This method should be used instead of
        ///     accessing the dictionary directly as it will handle the case where a new descriptor needs
        ///     to be created.
        /// </summary>
        private EvidenceTypeDescriptor GetEvidenceTypeDescriptor(Type evidenceType, bool addIfNotExist)
        {
            Contract.Assert(IsReaderLockHeld || IsWriterLockHeld);
            Contract.Assert(evidenceType != null);

            // If we don't know about the type being indexed and we don't want to add it then exit out
            EvidenceTypeDescriptor descriptor = null;
            if (!m_evidence.TryGetValue(evidenceType, out descriptor) && !addIfNotExist)
            {
                return null;
            }

            // If we haven't yet created a descriptor for this type then create one now
            if (descriptor == null)
            {
                descriptor = new EvidenceTypeDescriptor();
#if _DEBUG
                descriptor.SetEvidenceType(evidenceType);
#endif // _DEBUG

                bool upgradedLock = false;
                LockCookie upgradeCookie = new LockCookie();
                try
                {
                    if (!IsWriterLockHeld)
                    {
                        upgradeCookie = UpgradeToWriterLock();
                        upgradedLock = true;
                    }

                    m_evidence[evidenceType] = descriptor;
                }
                finally
                {
                    if (upgradedLock)
                        DowngradeFromWriterLock(ref upgradeCookie);
                }
            }

            return descriptor;
        }

        /// <summary>
        ///     This method is called if a piece of evidence is added but another piece of evidence of the same
        ///     type already existed.  We have different strategies depending on compatibility concerns of the
        ///     calling code.
        /// </summary>
        private static EvidenceBase HandleDuplicateEvidence(EvidenceBase original,
                                                            EvidenceBase duplicate,
                                                            DuplicateEvidenceAction action)
        {
            Contract.Assert(original != null);
            Contract.Assert(duplicate != null);
            Contract.Assert(original.GetType() == duplicate.GetType() || original.GetType() == typeof(LegacyEvidenceList));

            switch (action)
            {
                // Throw - duplicate evidence is not allowed (Arrowhead behavior), so throw an exception
                case DuplicateEvidenceAction.Throw:
                    throw new InvalidOperationException(Environment.GetResourceString("Policy_DuplicateEvidence", duplicate.GetType().FullName));

                // SelectNewObject - MergeWithNoDuplicates behavior - the duplicate object wins
                case DuplicateEvidenceAction.SelectNewObject:
                    return duplicate;

                // Merge - compat behavior. Merge the old and new evidence into a list so that both may exist
                case DuplicateEvidenceAction.Merge:

                    LegacyEvidenceList list = original as LegacyEvidenceList;
                    if (list == null)
                    {
                        list = new LegacyEvidenceList();
                        list.Add(original);
                    }

                    list.Add(duplicate);
                    return list;

                default:
                    BCLDebug.Assert(false, "Uknown DuplicateEvidenceAction");
                    return null;
            }
        }

        /// <summary>
        ///     Wrap evidence we recieved through a legacy API to ensure that it is stored in an EvidenceBase
        /// </summary>
        private static EvidenceBase WrapLegacyEvidence(object evidence)
        {
            Contract.Assert(evidence != null);

            EvidenceBase wrappedEvidence = evidence as EvidenceBase;
            if (wrappedEvidence == null)
            {
                wrappedEvidence = new LegacyEvidenceWrapper(evidence);
            }

            return wrappedEvidence;
        }

        /// <summary>
        ///     Upwrap evidence stored in a legacy adapter.
        ///     
        ///     This is only necessary for the case where multiple objects derived from EvidenceBase is
        ///     are added via the legacy APIs and are then retrieved via GetHostEvidence. This may occur if
        ///     a legacy application adds CLR supplied evidence types via the old APIs and a new application
        ///     consumes the resulting evidence.
        /// </summary>
        private static object UnwrapEvidence(EvidenceBase evidence)
        {
            ILegacyEvidenceAdapter adapter = evidence as ILegacyEvidenceAdapter;
            return adapter == null ? evidence : adapter.EvidenceObject;
        }

        /// <summary>
        ///     Merge two evidence collections together.  Note that this will cause all of the lazily
        ///     generated evidence for the input collection to be generated, as well as causing any lazily
        ///     generated evidence that both collections share to be generated in the target.
        /// </summary>
        [SecuritySafeCritical]
        public void Merge(Evidence evidence)
        {
            if (evidence == null)
            {
                return;
            }

            using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Writer))
            {
                bool checkedLock = false;
                IEnumerator hostEnumerator = evidence.GetHostEnumerator();
                while (hostEnumerator.MoveNext())
                {
                    if (Locked && !checkedLock)
                    {
                        new SecurityPermission(SecurityPermissionFlag.ControlEvidence).Demand();
                        checkedLock = true;
                    }

                    // If we could potentially have evidence of the type about to be merged into our host list,
                    // then make sure that we generate that evidence before merging.  This will prevent the
                    // newly merged evidence from masking the value that we would have generated on our own.
                    Type hostEvidenceType = hostEnumerator.Current.GetType();
                    if (m_evidence.ContainsKey(hostEvidenceType))
                    {
                        GetHostEvidenceNoLock(hostEvidenceType);
                    }

                    EvidenceBase hostEvidence = WrapLegacyEvidence(hostEnumerator.Current);
                    AddHostEvidenceNoLock(hostEvidence,
                                          GetEvidenceIndexType(hostEvidence),
                                          DuplicateEvidenceAction.Merge);
                }

                // Add each piece of assembly evidence. We don't need to deserialize our copy of the
                // evidence because AddAssemblyEvidenceNoLock will do this for us.
                IEnumerator assemblyEnumerator = evidence.GetAssemblyEnumerator();
                while (assemblyEnumerator.MoveNext())
                {
                    EvidenceBase assemblyEvidence = WrapLegacyEvidence(assemblyEnumerator.Current);
                    AddAssemblyEvidenceNoLock(assemblyEvidence,
                                              GetEvidenceIndexType(assemblyEvidence),
                                              DuplicateEvidenceAction.Merge);
                }
            }
        }

        /// <summary>
        ///     Same as merge, except only one instance of any one evidence type is allowed. When duplicates
        ///     are found, the evidence in the input argument will have priority. Note this will force the
        ///     entire input evidence to be generated, and does not check for locked evidence
        /// </summary>
        internal void MergeWithNoDuplicates(Evidence evidence)
        {
            if (evidence == null)
            {
                return;
            }

            using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Writer))
            {
                IEnumerator hostEnumerator = evidence.GetHostEnumerator();
                while (hostEnumerator.MoveNext())
                {
                    EvidenceBase hostEvidence = WrapLegacyEvidence(hostEnumerator.Current);
                    AddHostEvidenceNoLock(hostEvidence,
                                          GetEvidenceIndexType(hostEvidence),
                                          DuplicateEvidenceAction.SelectNewObject);
                }

                IEnumerator assemblyEnumerator = evidence.GetAssemblyEnumerator();
                while (assemblyEnumerator.MoveNext())
                {
                    EvidenceBase assemblyEvidence = WrapLegacyEvidence(assemblyEnumerator.Current);
                    AddAssemblyEvidenceNoLock(assemblyEvidence,
                                              GetEvidenceIndexType(assemblyEvidence),
                                              DuplicateEvidenceAction.SelectNewObject);
                }
            }
        }

#if FEATURE_SERIALIZATION
        /// <summary>
        ///     Do a full serialization of the evidence, which requires that we generate all of the evidence
        ///     we can and disconnect ourselves from the host and source assembly.
        /// </summary>
        [ComVisible(false)]
        [OnSerializing]
        [SecurityCritical]
        [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
        private void OnSerializing(StreamingContext context)
        {
            using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader))
            {
                // First, force all of the host evidence that might be lazily generated to be created
                foreach (Type evidenceType in new List<Type>(m_evidence.Keys))
                {
                    GetHostEvidenceNoLock(evidenceType);
                }

                // Also ensure that all serialized assembly evidence has been created
                DeserializeTargetEvidence();
            }

            // Fill in legacy evidence lists. We can't guarantee thread-safety here using locks
            // because we can't put a lock in the serialization code that will read the lists.
            // The best we can do is prevent another thread from seeing a half-populated list.
            // Therefore, we assign the lists after we've populated them fully (and declare them volatile.)
            ArrayList hostList = new ArrayList();
            IEnumerator hostEnumerator = GetHostEnumerator();
            while (hostEnumerator.MoveNext())
            {
                hostList.Add(hostEnumerator.Current);
            }
            m_hostList = hostList;

            ArrayList assemblyList = new ArrayList();
            IEnumerator assemblyEnumerator = GetAssemblyEnumerator();
            while (assemblyEnumerator.MoveNext())
            {
                assemblyList.Add(assemblyEnumerator.Current);
            }
            m_assemblyList = assemblyList;
        }

        /// <summary>
        ///     Finish deserializing legacy evidence
        /// </summary>
        [ComVisible(false)]
        [OnDeserialized]
        [SecurityCritical]
        private void OnDeserialized(StreamingContext context)
        {
            // Look at host and assembly evidence lists only if we serialized using Whidbey.
            if (m_evidence == null)
            {
                m_evidence = new Dictionary<Type, EvidenceTypeDescriptor>();

                // Whidbey evidence may need to be wrapped or added to a LegacyEvidenceList, so we go
                // through the legacy APIs to add them.
#pragma warning disable 618
                if (m_hostList != null)
                {
                    foreach (object evidenceObject in m_hostList)
                    {
                        if (evidenceObject != null)
                        {
                            AddHost(evidenceObject);
                        }
                    }

                    m_hostList = null;
                }

                if (m_assemblyList != null)
                {
                    foreach (object evidenceObject in m_assemblyList)
                    {
                        if (evidenceObject != null)
                        {
                            AddAssembly(evidenceObject);
                        }
                    }

                    m_assemblyList = null;
                }
#pragma warning restore 618
            }

            // see code:System.Security.Policy.Evidence#EvidenceLock
            m_evidenceLock = new ReaderWriterLock();
        }
#endif // FEATURE_SERIALIZATION

        /// <summary>
        ///     Load any serialized evidence out of the target assembly into our evidence collection.
        ///     
        ///     We allow entry to this method with only a reader lock held, since most of the time we will
        ///     not need to write to the evidence dictionary. If we haven't yet deserialized the target
        ///     evidence, then we will upgrade to a writer lock at that point.
        /// </summary>
        private void DeserializeTargetEvidence()
        {
#if FEATURE_SERIALIZATION
            Contract.Assert(IsReaderLockHeld || IsWriterLockHeld);

            if (m_target != null && !m_deserializedTargetEvidence)
            {
                bool upgradedLock = false;
                LockCookie lockCookie = new LockCookie();
                try
                {
                    if (!IsWriterLockHeld)
                    {
                        lockCookie = UpgradeToWriterLock();
                        upgradedLock = true;
                    }

                    // Set this to true here because AddAssemblyEvidenceNoLock will attempt to reenter this
                    // method creating possible infinite recursion.
                    m_deserializedTargetEvidence = true;

                    foreach (EvidenceBase targetEvidence in m_target.GetFactorySuppliedEvidence())
                    {
                        AddAssemblyEvidenceNoLock(targetEvidence, GetEvidenceIndexType(targetEvidence), DuplicateEvidenceAction.Throw);
                    }
                }
                finally
                {
                    if (upgradedLock)
                        DowngradeFromWriterLock(ref lockCookie);
                }
            }
#endif // FEATURE_SERIALIZATION
        }

#if FEATURE_SERIALIZATION
        /// <summary>
        ///     Serialize out raw evidence objects which have already been generated, ignoring any evidence
        ///     which might be present but has not yet been created for this assembly.
        ///     
        ///     This is used for indexing into the security policy cache, since we know that once policy is
        ///     resolved, the relevent membership conditions will have checked for any applicable evidence
        ///     and therefore after poliyc resolution this evidence collection will contain any evidence
        ///     objects necessary to arrive at its grant set.
        /// </summary>
        [SecurityCritical]
        internal byte[] RawSerialize()
        {
            try
            {
                using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader))
                {
                    // Filter out any evidence which is not yet generated
                    Dictionary<Type, EvidenceBase> generatedEvidence = new Dictionary<Type, EvidenceBase>();
                    foreach (KeyValuePair<Type, EvidenceTypeDescriptor> evidenceType in m_evidence)
                    {
                        if (evidenceType.Value != null && evidenceType.Value.HostEvidence != null)
                        {
                            generatedEvidence[evidenceType.Key] = evidenceType.Value.HostEvidence;
                        }
                    }

                    using (MemoryStream serializationStream = new MemoryStream())
                    {
                        BinaryFormatter formatter = new BinaryFormatter();
                        formatter.Serialize(serializationStream, generatedEvidence);
                        return serializationStream.ToArray();
                    }
                }
            }
            catch (SecurityException)
            {
                // We're running in a context where it's not safe to serialize the evidence out.  In this case
                // Simply decline to cache the result of the policy evaluation
                return null;
            }
        }
#endif // FEATURE_SERIALIZATION

        //
        // ICollection implementation.  All ICollection interface members are potentially much more
        // expensive in Arrowhead then they were downlevel.  They should not be used if the standard Get and
        // Add methods will work instead.
        // 

        [Obsolete("Evidence should not be treated as an ICollection. Please use the GetHostEnumerator and GetAssemblyEnumerator methods rather than using CopyTo.")]
        public void CopyTo(Array array, int index)
        {
            if (array == null)
                throw new ArgumentNullException("array");
            if (index < 0 || index > array.Length - Count)
                throw new ArgumentOutOfRangeException("index");
            Contract.EndContractBlock();

            int currentIndex = index;

            IEnumerator hostEnumerator = GetHostEnumerator();
            while (hostEnumerator.MoveNext())
            {
                array.SetValue(hostEnumerator.Current, currentIndex);
                ++currentIndex;
            }

            IEnumerator assemblyEnumerator = GetAssemblyEnumerator();
            while (assemblyEnumerator.MoveNext())
            {
                array.SetValue(assemblyEnumerator.Current, currentIndex);
                ++currentIndex;
            }
        }

        public IEnumerator GetHostEnumerator()
        {
            using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader))
            {
                return new EvidenceEnumerator(this, EvidenceEnumerator.Category.Host);
            }
        }

        public IEnumerator GetAssemblyEnumerator()
        {
            using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader))
            {
                DeserializeTargetEvidence();
                return new EvidenceEnumerator(this, EvidenceEnumerator.Category.Assembly);
            }
        }

        /// <summary>
        ///     Get an enumerator that can iterate over the raw evidence objects stored for the assembly
        /// </summary>
        internal RawEvidenceEnumerator GetRawAssemblyEvidenceEnumerator()
        {
            Contract.Assert(IsReaderLockHeld);
            DeserializeTargetEvidence();
            return new RawEvidenceEnumerator(this, new List<Type>(m_evidence.Keys), false);
        }

        /// <summary>
        ///     Get an enumerator that can iterate over the raw evidence objects stored for the host
        /// </summary>
        /// <returns></returns>
        internal RawEvidenceEnumerator GetRawHostEvidenceEnumerator()
        {
            Contract.Assert(IsReaderLockHeld);
            return new RawEvidenceEnumerator(this, new List<Type>(m_evidence.Keys), true);
        }

        [Obsolete("GetEnumerator is obsolete. Please use GetAssemblyEnumerator and GetHostEnumerator instead.")]
        public IEnumerator GetEnumerator()
        {
            using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader))
            {
                return new EvidenceEnumerator(this, EvidenceEnumerator.Category.Host | EvidenceEnumerator.Category.Assembly);
            }
        }

        /// <summary>
        ///     Get a specific type of assembly supplied evidence
        /// </summary>
        [ComVisible(false)]
        public T GetAssemblyEvidence<T>() where T : EvidenceBase
        {
            return UnwrapEvidence(GetAssemblyEvidence(typeof(T))) as T;
        }

        internal EvidenceBase GetAssemblyEvidence(Type type)
        {
            Contract.Assert(type != null);

            using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader))
            {
                return GetAssemblyEvidenceNoLock(type);
            }
        }

        private EvidenceBase GetAssemblyEvidenceNoLock(Type type)
        {
            Contract.Assert(IsReaderLockHeld || IsWriterLockHeld);
            Contract.Assert(type != null);

            DeserializeTargetEvidence();
            EvidenceTypeDescriptor descriptor = GetEvidenceTypeDescriptor(type);
            if (descriptor != null)
            {
                return descriptor.AssemblyEvidence;
            }

            return null;
        }

        /// <summary>
        ///     Get a specific type of host supplied evidence
        /// </summary>
        [ComVisible(false)]
        public T GetHostEvidence<T>() where T : EvidenceBase
        {
            return UnwrapEvidence(GetHostEvidence(typeof(T))) as T;
        }

        /// <summary>
        ///     Get a specific type of evidence from the host which may not have been verified yet.  If the
        ///     evidence was not verified, then don't mark it as being used yet.
        /// </summary>
        internal T GetDelayEvaluatedHostEvidence<T>() where T : EvidenceBase, IDelayEvaluatedEvidence
        {
            return UnwrapEvidence(GetHostEvidence(typeof(T), false)) as T;
        }

        internal EvidenceBase GetHostEvidence(Type type)
        {
            Contract.Assert(type != null);

            return GetHostEvidence(type, true);
        }

        [SecuritySafeCritical]
        private EvidenceBase GetHostEvidence(Type type, bool markDelayEvaluatedEvidenceUsed)
        {
            Contract.Assert(type != null);

            using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader))
            {
                EvidenceBase evidence = GetHostEvidenceNoLock(type);

                if (markDelayEvaluatedEvidenceUsed)
                {
                    IDelayEvaluatedEvidence delayEvidence = evidence as IDelayEvaluatedEvidence;
                    if (delayEvidence != null)
                    {
                        delayEvidence.MarkUsed();
                    }
                }

                return evidence;
            }
        }

        /// <summary>
        ///     Get host supplied evidence from the collection
        ///
        ///     We attempt to find host evdience in the following order:
        ///     
        ///       1. Already generated or explicitly supplied evidence
        ///       2. Evidence supplied by the CLR host
        ///       3. Evidence supplied by the CLR itself
        /// </summary>
        [SecurityCritical]
        private EvidenceBase GetHostEvidenceNoLock(Type type)
        {
            Contract.Assert(IsReaderLockHeld || IsWriterLockHeld);
            Contract.Assert(type != null);

            EvidenceTypeDescriptor descriptor = GetEvidenceTypeDescriptor(type);

            // If the evidence descriptor doesn't exist for the host evidence type than the evidence doesn't
            // exist and neither the host nor the runtime can produce it.
            if (descriptor == null)
            {
                return null;
            }

            // If the evidence has already been generated or if it was explicitly provided then return that
            if (descriptor.HostEvidence != null)
            {
                return descriptor.HostEvidence;
            }

            // If we have a target, then the host or the runtime might be able to generate this type of
            // evidence on demand.
            if (m_target != null && !descriptor.Generated)
            {
                using (EvidenceUpgradeLockHolder lockHolder = new EvidenceUpgradeLockHolder(this))
                {
                    // Make sure that we don't attempt to generate this type of evidencea again if we fail to
                    // generate it now.
                    descriptor.Generated = true;

                    EvidenceBase generatedEvidence = GenerateHostEvidence(type, descriptor.HostCanGenerate);
                    if (generatedEvidence != null)
                    {
                        descriptor.HostEvidence = generatedEvidence;

                        //
                        // #BackpatchGeneratedEvidence
                        // 
                        // If we were cloned from another evidence collection propigate any generated evidence
                        // back to the original collection.  Since Assembly and AppDomain both clone their
                        // evidence before giving it to users, this prevents us from having to regenerate
                        // evidence types on each clone that gets created.  Note that we do not want to do this
                        // backpatching if the origin already has evidence of this type or if it has had
                        // this type of evidence removed from its collection.
                        //

                        Evidence cloneOrigin = m_cloneOrigin != null ? m_cloneOrigin.Target as Evidence : null;
                        if (cloneOrigin != null)
                        {
                            BCLDebug.Assert(cloneOrigin.Target != null && cloneOrigin.Target == Target,
                                            "Attempt to backpatch evidence to a collection with a different target.");

                            using (EvidenceLockHolder cloneLockHolder = new EvidenceLockHolder(cloneOrigin, EvidenceLockHolder.LockType.Writer))
                            {
                                EvidenceTypeDescriptor cloneDescriptor = cloneOrigin.GetEvidenceTypeDescriptor(type);
                                if (cloneDescriptor != null && cloneDescriptor.HostEvidence == null)
                                {
                                    cloneDescriptor.HostEvidence = generatedEvidence.Clone() as EvidenceBase;
                                }
                            }
                        }

                    }

                    return generatedEvidence;
                }
            }

            // The evidence could not be generated and was not found
            return null;
        }

        /// <summary>
        ///     Attempt to generate host evidence on demand via calls to the runtime host or the evidence facotry
        /// </summary>
        [SecurityCritical]
        private EvidenceBase GenerateHostEvidence(Type type, bool hostCanGenerate)
        {
            Contract.Assert(type != null);
            Contract.Assert(IsWriterLockHeld);

#if FEATURE_CAS_POLICY
            // First let the host generate the evidence if it can.
            if (hostCanGenerate)
            {
                AppDomain targetDomain = m_target.Target as AppDomain;
                Assembly targetAssembly = m_target.Target as Assembly;

                EvidenceBase hostEvidence = null;
                if (targetDomain != null)
                {
                    hostEvidence = AppDomain.CurrentDomain.HostSecurityManager.GenerateAppDomainEvidence(type);
                }
                else if (targetAssembly != null)
                {
                    hostEvidence = AppDomain.CurrentDomain.HostSecurityManager.GenerateAssemblyEvidence(type, targetAssembly);
                }

                // If the host generated the evidence, verify that it generated the evidence we expected
                // and use that.
                if (hostEvidence != null)
                {
                    if (!type.IsAssignableFrom(hostEvidence.GetType()))
                    {
                        string hostType = AppDomain.CurrentDomain.HostSecurityManager.GetType().FullName;
                        string recievedType = hostEvidence.GetType().FullName;
                        string requestedType = type.FullName;

                        throw new InvalidOperationException(Environment.GetResourceString("Policy_IncorrectHostEvidence", hostType, recievedType, requestedType));
                    }

                    return hostEvidence;
                }
            }
#endif // FEATURE_CAS_POLICY

            // Finally, check to see if the CLR can generate the evidence
            return m_target.GenerateEvidence(type);
        }

        [Obsolete("Evidence should not be treated as an ICollection. Please use GetHostEnumerator and GetAssemblyEnumerator to iterate over the evidence to collect a count.")]
        public int Count
        {
            get
            {
                int count = 0;

                IEnumerator hostEvidence = GetHostEnumerator();
                while (hostEvidence.MoveNext())
                {
                    ++count;
                }

                IEnumerator assemblyEvidence = GetAssemblyEnumerator();
                while (assemblyEvidence.MoveNext())
                {
                    ++count;
                }

                return count;
            }
        }

        /// <summary>
        ///     Get the number of pieces of evidence which are currently generated, without causing any
        ///     lazily generated evidence to be created.
        /// </summary>
        [ComVisible(false)]
        internal int RawCount
        {
            get
            {
                int count = 0;

                using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader))
                {
                    foreach (Type evidenceType in new List<Type>(m_evidence.Keys))
                    {
                        EvidenceTypeDescriptor descriptor = GetEvidenceTypeDescriptor(evidenceType);

                        if (descriptor != null)
                        {
                            if (descriptor.AssemblyEvidence != null)
                            {
                                ++count;
                            }
                            if (descriptor.HostEvidence != null)
                            {
                                ++count;
                            }
                        }
                    }
                }

                return count;
            }
        }

        public Object SyncRoot
        {
            get { return this; }
        }

        public bool IsSynchronized
        {
            get { return true; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

#if FEATURE_CAS_POLICY
        [ComVisible(false)]
        public Evidence Clone()
        {
            return new Evidence(this);
        }
#endif // FEATURE_CAS_POLICY

        [ComVisible(false)]
        [SecuritySafeCritical]
        public void Clear()
        {
            if (Locked)
            {
                new SecurityPermission(SecurityPermissionFlag.ControlEvidence).Demand();
            }

            using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Writer))
            {
                ++m_version;
                m_evidence.Clear();
            }
        }

        [ComVisible(false)]
        [SecuritySafeCritical]
        public void RemoveType(Type t)
        {
            if (t == null)
                throw new ArgumentNullException("t");
            Contract.EndContractBlock();

            using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Writer))
            {
                EvidenceTypeDescriptor descriptor = GetEvidenceTypeDescriptor(t);
                if (descriptor != null)
                {
                    ++m_version;

                    // If we've locked this evidence collection, we need to do the lock check in the case that 
                    // either we have host evidence, or that the host might generate it, since removing the
                    // evidence will cause us to bypass the host's ability to ever generate the evidence.
                    if (Locked && (descriptor.HostEvidence != null || descriptor.HostCanGenerate))
                    {
                        new SecurityPermission(SecurityPermissionFlag.ControlEvidence).Demand();
                    }

                    m_evidence.Remove(t);
                }
            }
        }

        /// <summary>
        ///     Mark all of the already generated evidence in the collection as having been used during a
        ///     policy evaluation.
        /// </summary>
        internal void MarkAllEvidenceAsUsed()
        {
            using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader))
            {
                foreach (KeyValuePair<Type, EvidenceTypeDescriptor> evidenceType in m_evidence)
                {
                    if (evidenceType.Value != null)
                    {
                        IDelayEvaluatedEvidence hostEvidence = evidenceType.Value.HostEvidence as IDelayEvaluatedEvidence;
                        if (hostEvidence != null)
                        {
                            hostEvidence.MarkUsed();
                        }

                        IDelayEvaluatedEvidence assemblyEvidence = evidenceType.Value.AssemblyEvidence as IDelayEvaluatedEvidence;
                        if (assemblyEvidence != null)
                        {
                            assemblyEvidence.MarkUsed();
                        }
                    }
                }
            }
        }

#if FEATURE_CAS_POLICY
        /// <summary>
        ///     Determine if delay evaluated strong name evidence is contained in this collection, and if so
        ///     if it was used during policy evaluation.
        ///     
        ///     This method is called from the VM in SecurityPolicy::WasStrongNameEvidenceUsed
        ///     This class should be used as an adapter layer to allow the public facing EvidenceEnumerator to
        ///     be able to get the evidence values out of an Evidence class.  It is tightly coupled with the
        ///     internal data structures holding the evidence objects in the Evidence class.
        /// </summary>
        private bool WasStrongNameEvidenceUsed()
        {
            using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader))
            {
                EvidenceTypeDescriptor snTypeDescriptor = GetEvidenceTypeDescriptor(typeof(StrongName));
                if (snTypeDescriptor != null)
                {
                    IDelayEvaluatedEvidence snEvidence = snTypeDescriptor.HostEvidence as IDelayEvaluatedEvidence;
                    return snEvidence != null && snEvidence.WasUsed;
                }

                return false;
            }
        }
#endif // FEATURE_CAS_POLICY

        /// <summary>
        ///     Utility class to wrap acquiring a lock onto the evidence collection
        /// </summary>
        private class EvidenceLockHolder : IDisposable
        {
            private Evidence m_target;
            private LockType m_lockType;

            public enum LockType
            {
                Reader,
                Writer
            }

            public EvidenceLockHolder(Evidence target, LockType lockType)
            {
                Contract.Assert(target != null);
                Contract.Assert(lockType == LockType.Reader || lockType == LockType.Writer);

                m_target = target;
                m_lockType = lockType;

                if (m_lockType == LockType.Reader)
                {
                    m_target.AcquireReaderLock();
                }
                else
                {
                    m_target.AcquireWriterlock();
                }
            }

            public void Dispose()
            {
                if (m_lockType == LockType.Reader && m_target.IsReaderLockHeld)
                {
                    m_target.ReleaseReaderLock();
                }
                else if (m_lockType == LockType.Writer && m_target.IsWriterLockHeld)
                {
                    m_target.ReleaseWriterLock();
                }
            }
        }

        /// <summary>
        ///     Utility class to wrap upgrading an acquired reader lock to a writer lock and then
        ///     downgrading it back to a reader lock.
        /// </summary>
        private class EvidenceUpgradeLockHolder : IDisposable
        {
            private Evidence m_target;
            private LockCookie m_cookie;

            public EvidenceUpgradeLockHolder(Evidence target)
            {
                Contract.Assert(target != null);

                m_target = target;
                m_cookie = m_target.UpgradeToWriterLock();
            }

            public void Dispose()
            {
                if (m_target.IsWriterLockHeld)
                {
                    m_target.DowngradeFromWriterLock(ref m_cookie);
                }
            }
        }

        /// <summary>
        ///     Enumerator that iterates directly over the evidence type map, returning back the evidence objects
        ///     that are contained in it.  This enumerator will generate any lazy evaluated evidence it finds,
        ///     but it does not attempt to deal with legacy evidence adapters.
        ///     
        ///     This class should be used as an adapter layer to allow the public facing EvidenceEnumerator to
        ///     be able to get the evidence values out of an Evidence class.  It is tightly coupled with the
        ///     internal data structures holding the evidence objects in the Evidence class.
        /// </summary>
        internal sealed class RawEvidenceEnumerator : IEnumerator<EvidenceBase>
        {
            private Evidence m_evidence;
            private bool m_hostEnumerator;      // true to enumerate host evidence, false to enumerate assembly evidence
            private uint m_evidenceVersion;

            private Type[] m_evidenceTypes;
            private int m_typeIndex;
            private EvidenceBase m_currentEvidence;

            private static volatile List<Type> s_expensiveEvidence;

            public RawEvidenceEnumerator(Evidence evidence, IEnumerable<Type> evidenceTypes, bool hostEnumerator)
            {
                Contract.Assert(evidence != null);
                Contract.Assert(evidenceTypes != null);

                m_evidence = evidence;
                m_hostEnumerator = hostEnumerator;
                m_evidenceTypes = GenerateEvidenceTypes(evidence, evidenceTypes, hostEnumerator);
                m_evidenceVersion = evidence.m_version;

                Reset();
            }

            public EvidenceBase Current
            {
                get
                {
                    if (m_evidence.m_version != m_evidenceVersion)
                        throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));

                    return m_currentEvidence;
                }
            }

            object IEnumerator.Current
            {
                get
                {
                    if (m_evidence.m_version != m_evidenceVersion)
                        throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));

                    return m_currentEvidence;
                }
            }

            /// <summary>
            ///     List of types of evidence that we would like to avoid generating if possible
            /// </summary>
            private static List<Type> ExpensiveEvidence
            {
                get
                {
                    if (s_expensiveEvidence == null)
                    {
                        List<Type> expensiveEvidence = new List<Type>();
#if FEATURE_CAS_POLICY
                        expensiveEvidence.Add(typeof(Hash));
                        expensiveEvidence.Add(typeof(Publisher));
#endif // FEATURE_CAS_POLICY
                        s_expensiveEvidence = expensiveEvidence;

#if _DEBUG
                        List<Type> runtimeTypes = new List<Type>(Evidence.RuntimeEvidenceTypes);
                        foreach (Type expensiveType in s_expensiveEvidence)
                        {
                            BCLDebug.Assert(runtimeTypes.Contains(expensiveType),
                                            "Evidence type not generated by the runtime found in expensive evidence type list");
                        }
#endif // _DEBUG
                    }

                    return s_expensiveEvidence;
                }
            }

            public void Dispose()
            {
                return;
            }

            /// <summary>
            ///     Generate the array of types of evidence that could have values for
            /// </summary>
            private static Type[] GenerateEvidenceTypes(Evidence evidence,
                                                        IEnumerable<Type> evidenceTypes,
                                                        bool hostEvidence)
            {
                Contract.Assert(evidence != null);
                Contract.Assert(evidenceTypes != null);

                //
                // Sort the evidence being generated into three categories, which we enumerate in order:
                //   1. Evidence which has already been generated
                //   2. Evidence which is relatively inexpensive to generate
                //   3. Evidence which is expensive to generate.
                //   
                // This allows us to be as efficient as possible in case the user of the enumerator stops the
                // enumeration before we step up to the next more expensive category.
                //

                List<Type> alreadyGeneratedList = new List<Type>();
                List<Type> inexpensiveList = new List<Type>();
                List<Type> expensiveList = new List<Type>(ExpensiveEvidence.Count);

                // Iterate over the evidence types classifying into the three groups.  We need to copy the list
                // here since GetEvidenceTypeDescriptor will potentially update the evidence dictionary, which
                // evidenceTypes iterates over.
                foreach (Type evidenceType in evidenceTypes)
                {
                    EvidenceTypeDescriptor descriptor = evidence.GetEvidenceTypeDescriptor(evidenceType);
                    BCLDebug.Assert(descriptor != null, "descriptor != null");

                    bool alreadyGenerated = (hostEvidence && descriptor.HostEvidence != null) ||
                                            (!hostEvidence && descriptor.AssemblyEvidence != null);

                    if (alreadyGenerated)
                    {
                        alreadyGeneratedList.Add(evidenceType);
                    }
                    else if (ExpensiveEvidence.Contains(evidenceType))
                    {
                        expensiveList.Add(evidenceType);
                    }
                    else
                    {
                        inexpensiveList.Add(evidenceType);
                    }
                }

                Type[] enumerationTypes = new Type[alreadyGeneratedList.Count + inexpensiveList.Count + expensiveList.Count];
                alreadyGeneratedList.CopyTo(enumerationTypes, 0);
                inexpensiveList.CopyTo(enumerationTypes, alreadyGeneratedList.Count);
                expensiveList.CopyTo(enumerationTypes, alreadyGeneratedList.Count + inexpensiveList.Count);

                return enumerationTypes;
            }

            [SecuritySafeCritical]
            public bool MoveNext()
            {
                using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(m_evidence, EvidenceLockHolder.LockType.Reader))
                {
                    if (m_evidence.m_version != m_evidenceVersion)
                        throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));

                    m_currentEvidence = null;

                    // Iterate over the possible types of evidence that we could have until we find one that
                    // really exists, or we run out of posibilities.
                    do
                    {
                        ++m_typeIndex;

                        if (m_typeIndex < m_evidenceTypes.Length)
                        {
                            if (m_hostEnumerator)
                            {
                                m_currentEvidence = m_evidence.GetHostEvidenceNoLock(m_evidenceTypes[m_typeIndex]);
                            }
                            else
                            {
                                m_currentEvidence = m_evidence.GetAssemblyEvidenceNoLock(m_evidenceTypes[m_typeIndex]);
                            }
                        }
                    }
                    while (m_typeIndex < m_evidenceTypes.Length && m_currentEvidence == null);
                }

                return m_currentEvidence != null;
            }

            public void Reset()
            {
                if (m_evidence.m_version != m_evidenceVersion)
                    throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));

                m_typeIndex = -1;
                m_currentEvidence = null;
            }
        }

        private sealed class EvidenceEnumerator : IEnumerator
        {
            private Evidence m_evidence;
            private Category m_category;
            private Stack m_enumerators;

            private object m_currentEvidence;

            [Flags]
            internal enum Category
            {
                Host = 0x1,     // Enumerate only host supplied evidence
                Assembly = 0x2      // Enumerate only assembly supplied evidence
            }

            internal EvidenceEnumerator(Evidence evidence, Category category)
            {
                Contract.Assert(evidence != null);
                Contract.Assert(evidence.IsReaderLockHeld);

                m_evidence = evidence;
                m_category = category;
                ResetNoLock();
            }

            public bool MoveNext()
            {
                IEnumerator currentEnumerator = CurrentEnumerator;

                // No more enumerators means we can't go any further
                if (currentEnumerator == null)
                {
                    m_currentEvidence = null;
                    return false;
                }

                // See if the current enumerator can continue
                if (currentEnumerator.MoveNext())
                {
                    //
                    // If we've found an adapter for legacy evidence, we need to unwrap it for it to be the
                    // current enumerator's value.  For wrapped evidence, this is a simple unwrap, for a list of
                    // evidence, we need to make that the current enumerator and get its first value.
                    // 

                    LegacyEvidenceWrapper legacyWrapper = currentEnumerator.Current as LegacyEvidenceWrapper;
                    LegacyEvidenceList legacyList = currentEnumerator.Current as LegacyEvidenceList;

                    if (legacyWrapper != null)
                    {
                        m_currentEvidence = legacyWrapper.EvidenceObject;
                    }
                    else if (legacyList != null)
                    {
                        IEnumerator legacyListEnumerator = legacyList.GetEnumerator();
                        m_enumerators.Push(legacyListEnumerator);
                        MoveNext();
                    }
                    else
                    {
                        m_currentEvidence = currentEnumerator.Current;
                    }

                    BCLDebug.Assert(m_currentEvidence != null, "m_currentEvidence != null");
                    return true;
                }
                else
                {
                    // If we've reached the end of the current enumerator, move to the next one and try again
                    m_enumerators.Pop();
                    return MoveNext();
                }
            }

            public object Current
            {
                get { return m_currentEvidence; }
            }

            private IEnumerator CurrentEnumerator
            {
                get
                {
                    return m_enumerators.Count > 0 ? m_enumerators.Peek() as IEnumerator : null;
                }
            }

            public void Reset()
            {
                using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(m_evidence, EvidenceLockHolder.LockType.Reader))
                {
                    ResetNoLock();
                }
            }

            private void ResetNoLock()
            {
                Contract.Assert(m_evidence != null);
                Contract.Assert(m_evidence.IsReaderLockHeld);

                m_currentEvidence = null;
                m_enumerators = new Stack();

                if ((m_category & Category.Host) == Category.Host)
                {
                    m_enumerators.Push(m_evidence.GetRawHostEvidenceEnumerator());
                }
                if ((m_category & Category.Assembly) == Category.Assembly)
                {
                    m_enumerators.Push(m_evidence.GetRawAssemblyEvidenceEnumerator());
                }
            }
        }
#endif //!FEATURE_CORECLR && FEATURE_RWLOCK
    }
}
