File: system\runtime\interopservices\windowsruntime\iteratortoenumeratoradapter.cs
Project: ndp\clr\src\bcl\mscorlib.csproj (mscorlib)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
//
// <OWNER>Microsoft</OWNER>
// <OWNER>Microsoft</OWNER>
 
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Security;
 
namespace System.Runtime.InteropServices.WindowsRuntime
{
    internal delegate IEnumerator<T> GetEnumerator_Delegate<out T>();
 
    // This is a set of stub methods implementing the support for the IEnumerable`1 interface on WinRT
    // objects that implement IIterable`1. Used by the interop mashaling infrastructure.
    //
    // The methods on this class must be written VERY carefully to avoid introducing security holes.
    // That's because they are invoked with special "this"! The "this" object
    // for all of these methods are not IterableToEnumerableAdapter objects. Rather, they are of type
    // IIterable<T>. No actual IterableToEnumerableAdapter object is ever instantiated. Thus, you will
    // see a lot of expressions that cast "this" to "IIterable<T>". 
    internal sealed class IterableToEnumerableAdapter
    {
        private IterableToEnumerableAdapter()
        {
            Contract.Assert(false, "This class is never instantiated");
        }
        
        // This method is invoked when GetEnumerator is called on a WinRT-backed implementation of IEnumerable<T>.
        [SecurityCritical]
        internal IEnumerator<T> GetEnumerator_Stub<T>()
        {
            IIterable<T> _this = JitHelpers.UnsafeCast<IIterable<T>>(this);
            return new IteratorToEnumeratorAdapter<T>(_this.First());
        }
 
        // This method is invoked when GetEnumerator is called on a WinRT-backed implementation of IEnumerable<T>
        // and it is possible that the implementation supports IEnumerable<Type>/IEnumerable<string>/IEnumerable<Exception>/
        // IEnumerable<array>/IEnumerable<delegate> rather than IEnumerable<T> because T is assignable from Type/string/
        // Exception/array/delegate via co-variance.
        [SecurityCritical]
        internal IEnumerator<T> GetEnumerator_Variance_Stub<T>() where T : class
        {
            bool fUseString;
            Delegate target = System.StubHelpers.StubHelpers.GetTargetForAmbiguousVariantCall(
                this,
                typeof(IEnumerable<T>).TypeHandle.Value,
                out fUseString);
 
            if (target != null)
            {
                return (JitHelpers.UnsafeCast<GetEnumerator_Delegate<T>>(target))();
            }
            
            if (fUseString)
            {
                return JitHelpers.UnsafeCast<IEnumerator<T>>(GetEnumerator_Stub<string>());
            }
 
            return GetEnumerator_Stub<T>();
        }
    }
 
    internal sealed class BindableIterableToEnumerableAdapter
    {
        private BindableIterableToEnumerableAdapter()
        {
            Contract.Assert(false, "This class is never instantiated");
        }
 
        // @
        private sealed class NonGenericToGenericIterator : IIterator<object>
        {
            private IBindableIterator iterator;
 
            public NonGenericToGenericIterator(IBindableIterator iterator)
            { this.iterator = iterator; }
 
            public object Current  { get { return iterator.Current; } }
            public bool HasCurrent { get { return iterator.HasCurrent; } }
            public bool MoveNext() { return iterator.MoveNext(); }
            public int GetMany(object[] items) { throw new NotSupportedException(); }
        }
 
        // This method is invoked when GetEnumerator is called on a WinRT-backed implementation of IEnumerable.
        [SecurityCritical]
        internal IEnumerator GetEnumerator_Stub()
        {
            IBindableIterable _this = JitHelpers.UnsafeCast<IBindableIterable>(this);
            return new IteratorToEnumeratorAdapter<object>(new NonGenericToGenericIterator(_this.First()));
        }
    }
 
    // Adapter class which holds a Windows Runtime IIterator<T>, exposing it as a managed IEnumerator<T>
 
 
    // There are a few implementation differences between the Iterator and IEnumerator which need to be 
    // addressed. Iterator starts at index 0 while IEnumerator starts at index -1 as a result of which 
    // the first call to IEnumerator.Current is correct only after calling MoveNext(). 
    // Also IEnumerator throws an exception when we call Current after reaching the end of collection.
    internal sealed class IteratorToEnumeratorAdapter<T> : IEnumerator<T>
    {
        private IIterator<T> m_iterator;
        private bool m_hadCurrent;
        private T m_current;
        private bool m_isInitialized;
 
        internal IteratorToEnumeratorAdapter(IIterator<T> iterator)
        {
            Contract.Requires(iterator != null);
            m_iterator = iterator;
            m_hadCurrent = true;
            m_isInitialized = false;
        }
 
        public T Current
        {
            get
            {
                // The enumerator has not been advanced to the first element yet.
                if (!m_isInitialized)
                    ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumNotStarted);
                // The enumerator has reached the end of the collection
                if (!m_hadCurrent)
                    ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded);
                return m_current;
            }
        }
 
        object IEnumerator.Current
        {
            get
            {
                // The enumerator has not been advanced to the first element yet.
                if (!m_isInitialized)
                    ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumNotStarted);
                // The enumerator has reached the end of the collection
                if (!m_hadCurrent)
                    ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded);
                return m_current;
            }
        }
 
        [SecuritySafeCritical]
        public bool MoveNext()
        {
            // If we've passed the end of the iteration, IEnumerable<T> should return false, while
            // IIterable will fail the interface call
            if (!m_hadCurrent)
            {
                return false;
            }
 
            // IIterators start at index 0, rather than -1.  If this is the first call, we need to just
            // check HasCurrent rather than actually moving to the next element
            try
            {
                if (!m_isInitialized)
                {
                    m_hadCurrent = m_iterator.HasCurrent;
                    m_isInitialized = true;
                }
                else
                {
                    m_hadCurrent = m_iterator.MoveNext();
                }
 
                // We want to save away the current value for two reasons:
                //  1. Accessing .Current is cheap on other iterators, so having it be a property which is a
                //     simple field access preserves the expected performance characteristics (as opposed to
                //     triggering a COM call every time the property is accessed)
                //
                //  2. This allows us to preserve the same semantics as generic collection iteration when iterating
                //     beyond the end of the collection - namely that Current continues to return the last value
                //     of the collection
                if (m_hadCurrent)
                {
                    m_current = m_iterator.Current;
                }
            }
            catch (Exception e)
            {
                // Translate E_CHANGED_STATE into an InvalidOperationException for an updated enumeration
                if (Marshal.GetHRForException(e) == __HResults.E_CHANGED_STATE)
                {
                    ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
                }
                else
                {
                    throw;
                }
            }
 
            return m_hadCurrent;
        }
 
        public void Reset()
        {
            throw new NotSupportedException();
        }
 
        public void Dispose()
        {
        }
    }
}