1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
8 using System.Collections;
9 using System.Collections.Generic;
10 using System.Diagnostics;
11 using System.Runtime.InteropServices;
12 using System.Runtime.CompilerServices;
13 using System.Security;
14 using Internal.Runtime.CompilerServices;
16 namespace System.Runtime.InteropServices.WindowsRuntime
18 internal delegate IEnumerator<T> GetEnumerator_Delegate<out T>();
20 // This is a set of stub methods implementing the support for the IEnumerable`1 interface on WinRT
21 // objects that implement IIterable`1. Used by the interop mashaling infrastructure.
23 // The methods on this class must be written VERY carefully to avoid introducing security holes.
24 // That's because they are invoked with special "this"! The "this" object
25 // for all of these methods are not IterableToEnumerableAdapter objects. Rather, they are of type
26 // IIterable<T>. No actual IterableToEnumerableAdapter object is ever instantiated. Thus, you will
27 // see a lot of expressions that cast "this" to "IIterable<T>".
28 internal sealed class IterableToEnumerableAdapter
30 private IterableToEnumerableAdapter()
32 Debug.Fail("This class is never instantiated");
35 // This method is invoked when GetEnumerator is called on a WinRT-backed implementation of IEnumerable<T>.
36 internal IEnumerator<T> GetEnumerator_Stub<T>()
38 IIterable<T> _this = Unsafe.As<IIterable<T>>(this);
39 return new IteratorToEnumeratorAdapter<T>(_this.First());
42 // This method is invoked when GetEnumerator is called on a WinRT-backed implementation of IEnumerable<T>
43 // and it is possible that the implementation supports IEnumerable<Type>/IEnumerable<string>/IEnumerable<Exception>/
44 // IEnumerable<array>/IEnumerable<delegate> rather than IEnumerable<T> because T is assignable from Type/string/
45 // Exception/array/delegate via co-variance.
46 internal IEnumerator<T> GetEnumerator_Variance_Stub<T>() where T : class
49 Delegate target = System.StubHelpers.StubHelpers.GetTargetForAmbiguousVariantCall(
51 typeof(IEnumerable<T>).TypeHandle.Value,
56 return (Unsafe.As<GetEnumerator_Delegate<T>>(target))();
61 return Unsafe.As<IEnumerator<T>>(GetEnumerator_Stub<string>());
64 return GetEnumerator_Stub<T>();
68 internal sealed class BindableIterableToEnumerableAdapter
70 private BindableIterableToEnumerableAdapter()
72 Debug.Fail("This class is never instantiated");
75 private sealed class NonGenericToGenericIterator : IIterator<object>
77 private IBindableIterator iterator;
79 public NonGenericToGenericIterator(IBindableIterator iterator)
80 { this.iterator = iterator; }
82 public object Current { get { return iterator.Current; } }
83 public bool HasCurrent { get { return iterator.HasCurrent; } }
84 public bool MoveNext() { return iterator.MoveNext(); }
85 public int GetMany(object[] items) { throw new NotSupportedException(); }
88 // This method is invoked when GetEnumerator is called on a WinRT-backed implementation of IEnumerable.
89 internal IEnumerator GetEnumerator_Stub()
91 IBindableIterable _this = Unsafe.As<IBindableIterable>(this);
92 return new IteratorToEnumeratorAdapter<object>(new NonGenericToGenericIterator(_this.First()));
96 // Adapter class which holds a Windows Runtime IIterator<T>, exposing it as a managed IEnumerator<T>
99 // There are a few implementation differences between the Iterator and IEnumerator which need to be
100 // addressed. Iterator starts at index 0 while IEnumerator starts at index -1 as a result of which
101 // the first call to IEnumerator.Current is correct only after calling MoveNext().
102 // Also IEnumerator throws an exception when we call Current after reaching the end of collection.
103 internal sealed class IteratorToEnumeratorAdapter<T> : IEnumerator<T>
105 private IIterator<T> m_iterator;
106 private bool m_hadCurrent;
108 private bool m_isInitialized;
110 internal IteratorToEnumeratorAdapter(IIterator<T> iterator)
112 Debug.Assert(iterator != null);
113 m_iterator = iterator;
115 m_isInitialized = false;
122 // The enumerator has not been advanced to the first element yet.
123 if (!m_isInitialized)
124 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumNotStarted();
125 // The enumerator has reached the end of the collection
127 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumEnded();
132 object IEnumerator.Current
136 // The enumerator has not been advanced to the first element yet.
137 if (!m_isInitialized)
138 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumNotStarted();
139 // The enumerator has reached the end of the collection
141 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumEnded();
146 public bool MoveNext()
148 // If we've passed the end of the iteration, IEnumerable<T> should return false, while
149 // IIterable will fail the interface call
155 // IIterators start at index 0, rather than -1. If this is the first call, we need to just
156 // check HasCurrent rather than actually moving to the next element
159 if (!m_isInitialized)
161 m_hadCurrent = m_iterator.HasCurrent;
162 m_isInitialized = true;
166 m_hadCurrent = m_iterator.MoveNext();
169 // We want to save away the current value for two reasons:
170 // 1. Accessing .Current is cheap on other iterators, so having it be a property which is a
171 // simple field access preserves the expected performance characteristics (as opposed to
172 // triggering a COM call every time the property is accessed)
174 // 2. This allows us to preserve the same semantics as generic collection iteration when iterating
175 // beyond the end of the collection - namely that Current continues to return the last value
179 m_current = m_iterator.Current;
184 // Translate E_CHANGED_STATE into an InvalidOperationException for an updated enumeration
185 if (Marshal.GetHRForException(e) == HResults.E_CHANGED_STATE)
187 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
200 throw new NotSupportedException();
203 public void Dispose()