3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // <OWNER>Microsoft</OWNER>
8 // <OWNER>Microsoft</OWNER>
11 using System.Collections;
12 using System.Collections.Generic;
13 using System.Diagnostics.Contracts;
14 using System.Runtime.InteropServices;
15 using System.Runtime.CompilerServices;
16 using System.Security;
18 namespace System.Runtime.InteropServices.WindowsRuntime
20 internal delegate IEnumerator<T> GetEnumerator_Delegate<out T>();
22 // This is a set of stub methods implementing the support for the IEnumerable`1 interface on WinRT
23 // objects that implement IIterable`1. Used by the interop mashaling infrastructure.
25 // The methods on this class must be written VERY carefully to avoid introducing security holes.
26 // That's because they are invoked with special "this"! The "this" object
27 // for all of these methods are not IterableToEnumerableAdapter objects. Rather, they are of type
28 // IIterable<T>. No actual IterableToEnumerableAdapter object is ever instantiated. Thus, you will
29 // see a lot of expressions that cast "this" to "IIterable<T>".
30 internal sealed class IterableToEnumerableAdapter
32 private IterableToEnumerableAdapter()
34 Contract.Assert(false, "This class is never instantiated");
37 // This method is invoked when GetEnumerator is called on a WinRT-backed implementation of IEnumerable<T>.
39 internal IEnumerator<T> GetEnumerator_Stub<T>()
41 IIterable<T> _this = JitHelpers.UnsafeCast<IIterable<T>>(this);
42 return new IteratorToEnumeratorAdapter<T>(_this.First());
45 // This method is invoked when GetEnumerator is called on a WinRT-backed implementation of IEnumerable<T>
46 // and it is possible that the implementation supports IEnumerable<Type>/IEnumerable<string>/IEnumerable<Exception>/
47 // IEnumerable<array>/IEnumerable<delegate> rather than IEnumerable<T> because T is assignable from Type/string/
48 // Exception/array/delegate via co-variance.
50 internal IEnumerator<T> GetEnumerator_Variance_Stub<T>() where T : class
53 Delegate target = System.StubHelpers.StubHelpers.GetTargetForAmbiguousVariantCall(
55 typeof(IEnumerable<T>).TypeHandle.Value,
60 return (JitHelpers.UnsafeCast<GetEnumerator_Delegate<T>>(target))();
65 return JitHelpers.UnsafeCast<IEnumerator<T>>(GetEnumerator_Stub<string>());
68 return GetEnumerator_Stub<T>();
72 internal sealed class BindableIterableToEnumerableAdapter
74 private BindableIterableToEnumerableAdapter()
76 Contract.Assert(false, "This class is never instantiated");
80 private sealed class NonGenericToGenericIterator : IIterator<object>
82 private IBindableIterator iterator;
84 public NonGenericToGenericIterator(IBindableIterator iterator)
85 { this.iterator = iterator; }
87 public object Current { get { return iterator.Current; } }
88 public bool HasCurrent { get { return iterator.HasCurrent; } }
89 public bool MoveNext() { return iterator.MoveNext(); }
90 public int GetMany(object[] items) { throw new NotSupportedException(); }
93 // This method is invoked when GetEnumerator is called on a WinRT-backed implementation of IEnumerable.
95 internal IEnumerator GetEnumerator_Stub()
97 IBindableIterable _this = JitHelpers.UnsafeCast<IBindableIterable>(this);
98 return new IteratorToEnumeratorAdapter<object>(new NonGenericToGenericIterator(_this.First()));
102 // Adapter class which holds a Windows Runtime IIterator<T>, exposing it as a managed IEnumerator<T>
105 // There are a few implementation differences between the Iterator and IEnumerator which need to be
106 // addressed. Iterator starts at index 0 while IEnumerator starts at index -1 as a result of which
107 // the first call to IEnumerator.Current is correct only after calling MoveNext().
108 // Also IEnumerator throws an exception when we call Current after reaching the end of collection.
109 internal sealed class IteratorToEnumeratorAdapter<T> : IEnumerator<T>
111 private IIterator<T> m_iterator;
112 private bool m_hadCurrent;
114 private bool m_isInitialized;
116 internal IteratorToEnumeratorAdapter(IIterator<T> iterator)
118 Contract.Requires(iterator != null);
119 m_iterator = iterator;
121 m_isInitialized = false;
128 // The enumerator has not been advanced to the first element yet.
129 if (!m_isInitialized)
130 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumNotStarted);
131 // The enumerator has reached the end of the collection
133 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded);
138 object IEnumerator.Current
142 // The enumerator has not been advanced to the first element yet.
143 if (!m_isInitialized)
144 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumNotStarted);
145 // The enumerator has reached the end of the collection
147 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded);
152 [SecuritySafeCritical]
153 public bool MoveNext()
155 // If we've passed the end of the iteration, IEnumerable<T> should return false, while
156 // IIterable will fail the interface call
162 // IIterators start at index 0, rather than -1. If this is the first call, we need to just
163 // check HasCurrent rather than actually moving to the next element
166 if (!m_isInitialized)
168 m_hadCurrent = m_iterator.HasCurrent;
169 m_isInitialized = true;
173 m_hadCurrent = m_iterator.MoveNext();
176 // We want to save away the current value for two reasons:
177 // 1. Accessing .Current is cheap on other iterators, so having it be a property which is a
178 // simple field access preserves the expected performance characteristics (as opposed to
179 // triggering a COM call every time the property is accessed)
181 // 2. This allows us to preserve the same semantics as generic collection iteration when iterating
182 // beyond the end of the collection - namely that Current continues to return the last value
186 m_current = m_iterator.Current;
191 // Translate E_CHANGED_STATE into an InvalidOperationException for an updated enumeration
192 if (Marshal.GetHRForException(e) == __HResults.E_CHANGED_STATE)
194 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
207 throw new NotSupportedException();
210 public void Dispose()