Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / mscorlib / system / runtime / interopservices / windowsruntime / iteratortoenumeratoradapter.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 //
7 // <OWNER>Microsoft</OWNER>
8 // <OWNER>Microsoft</OWNER>
9
10 using System;
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;
17
18 namespace System.Runtime.InteropServices.WindowsRuntime
19 {
20     internal delegate IEnumerator<T> GetEnumerator_Delegate<out T>();
21
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.
24     //
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
31     {
32         private IterableToEnumerableAdapter()
33         {
34             Contract.Assert(false, "This class is never instantiated");
35         }
36         
37         // This method is invoked when GetEnumerator is called on a WinRT-backed implementation of IEnumerable<T>.
38         [SecurityCritical]
39         internal IEnumerator<T> GetEnumerator_Stub<T>()
40         {
41             IIterable<T> _this = JitHelpers.UnsafeCast<IIterable<T>>(this);
42             return new IteratorToEnumeratorAdapter<T>(_this.First());
43         }
44
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.
49         [SecurityCritical]
50         internal IEnumerator<T> GetEnumerator_Variance_Stub<T>() where T : class
51         {
52             bool fUseString;
53             Delegate target = System.StubHelpers.StubHelpers.GetTargetForAmbiguousVariantCall(
54                 this,
55                 typeof(IEnumerable<T>).TypeHandle.Value,
56                 out fUseString);
57
58             if (target != null)
59             {
60                 return (JitHelpers.UnsafeCast<GetEnumerator_Delegate<T>>(target))();
61             }
62             
63             if (fUseString)
64             {
65                 return JitHelpers.UnsafeCast<IEnumerator<T>>(GetEnumerator_Stub<string>());
66             }
67
68             return GetEnumerator_Stub<T>();
69         }
70     }
71
72     internal sealed class BindableIterableToEnumerableAdapter
73     {
74         private BindableIterableToEnumerableAdapter()
75         {
76             Contract.Assert(false, "This class is never instantiated");
77         }
78
79         // @
80         private sealed class NonGenericToGenericIterator : IIterator<object>
81         {
82             private IBindableIterator iterator;
83
84             public NonGenericToGenericIterator(IBindableIterator iterator)
85             { this.iterator = iterator; }
86
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(); }
91         }
92
93         // This method is invoked when GetEnumerator is called on a WinRT-backed implementation of IEnumerable.
94         [SecurityCritical]
95         internal IEnumerator GetEnumerator_Stub()
96         {
97             IBindableIterable _this = JitHelpers.UnsafeCast<IBindableIterable>(this);
98             return new IteratorToEnumeratorAdapter<object>(new NonGenericToGenericIterator(_this.First()));
99         }
100     }
101
102     // Adapter class which holds a Windows Runtime IIterator<T>, exposing it as a managed IEnumerator<T>
103
104
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>
110     {
111         private IIterator<T> m_iterator;
112         private bool m_hadCurrent;
113         private T m_current;
114         private bool m_isInitialized;
115
116         internal IteratorToEnumeratorAdapter(IIterator<T> iterator)
117         {
118             Contract.Requires(iterator != null);
119             m_iterator = iterator;
120             m_hadCurrent = true;
121             m_isInitialized = false;
122         }
123
124         public T Current
125         {
126             get
127             {
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
132                 if (!m_hadCurrent)
133                     ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded);
134                 return m_current;
135             }
136         }
137
138         object IEnumerator.Current
139         {
140             get
141             {
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
146                 if (!m_hadCurrent)
147                     ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded);
148                 return m_current;
149             }
150         }
151
152         [SecuritySafeCritical]
153         public bool MoveNext()
154         {
155             // If we've passed the end of the iteration, IEnumerable<T> should return false, while
156             // IIterable will fail the interface call
157             if (!m_hadCurrent)
158             {
159                 return false;
160             }
161
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
164             try
165             {
166                 if (!m_isInitialized)
167                 {
168                     m_hadCurrent = m_iterator.HasCurrent;
169                     m_isInitialized = true;
170                 }
171                 else
172                 {
173                     m_hadCurrent = m_iterator.MoveNext();
174                 }
175
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)
180                 //
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
183                 //     of the collection
184                 if (m_hadCurrent)
185                 {
186                     m_current = m_iterator.Current;
187                 }
188             }
189             catch (Exception e)
190             {
191                 // Translate E_CHANGED_STATE into an InvalidOperationException for an updated enumeration
192                 if (Marshal.GetHRForException(e) == __HResults.E_CHANGED_STATE)
193                 {
194                     ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
195                 }
196                 else
197                 {
198                     throw;
199                 }
200             }
201
202             return m_hadCurrent;
203         }
204
205         public void Reset()
206         {
207             throw new NotSupportedException();
208         }
209
210         public void Dispose()
211         {
212         }
213     }
214 }