summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs')
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs210
1 files changed, 210 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..f1b799aa84
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs
@@ -0,0 +1,210 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+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()
+ {
+ }
+ }
+}