diff options
Diffstat (limited to 'src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs')
-rw-r--r-- | src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs new file mode 100644 index 0000000000..c1656ce7f8 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs @@ -0,0 +1,209 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// + +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() + { + } + } +} |