d49836c46a244a6a227c614d420125d68efa675c
[platform/upstream/dotnet/runtime.git] /
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.
4
5 //
6
7 using System;
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;
15
16 namespace System.Runtime.InteropServices.WindowsRuntime
17 {
18     internal delegate IEnumerator<T> GetEnumerator_Delegate<out T>();
19
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.
22     //
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
29     {
30         private IterableToEnumerableAdapter()
31         {
32             Debug.Fail("This class is never instantiated");
33         }
34
35         // This method is invoked when GetEnumerator is called on a WinRT-backed implementation of IEnumerable<T>.
36         internal IEnumerator<T> GetEnumerator_Stub<T>()
37         {
38             IIterable<T> _this = Unsafe.As<IIterable<T>>(this);
39             return new IteratorToEnumeratorAdapter<T>(_this.First());
40         }
41
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
47         {
48             bool fUseString;
49             Delegate target = System.StubHelpers.StubHelpers.GetTargetForAmbiguousVariantCall(
50                 this,
51                 typeof(IEnumerable<T>).TypeHandle.Value,
52                 out fUseString);
53
54             if (target != null)
55             {
56                 return (Unsafe.As<GetEnumerator_Delegate<T>>(target))();
57             }
58
59             if (fUseString)
60             {
61                 return Unsafe.As<IEnumerator<T>>(GetEnumerator_Stub<string>());
62             }
63
64             return GetEnumerator_Stub<T>();
65         }
66     }
67
68     internal sealed class BindableIterableToEnumerableAdapter
69     {
70         private BindableIterableToEnumerableAdapter()
71         {
72             Debug.Fail("This class is never instantiated");
73         }
74
75         private sealed class NonGenericToGenericIterator : IIterator<object>
76         {
77             private IBindableIterator iterator;
78
79             public NonGenericToGenericIterator(IBindableIterator iterator)
80             { this.iterator = iterator; }
81
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(); }
86         }
87
88         // This method is invoked when GetEnumerator is called on a WinRT-backed implementation of IEnumerable.
89         internal IEnumerator GetEnumerator_Stub()
90         {
91             IBindableIterable _this = Unsafe.As<IBindableIterable>(this);
92             return new IteratorToEnumeratorAdapter<object>(new NonGenericToGenericIterator(_this.First()));
93         }
94     }
95
96     // Adapter class which holds a Windows Runtime IIterator<T>, exposing it as a managed IEnumerator<T>
97
98
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>
104     {
105         private IIterator<T> m_iterator;
106         private bool m_hadCurrent;
107         private T m_current;
108         private bool m_isInitialized;
109
110         internal IteratorToEnumeratorAdapter(IIterator<T> iterator)
111         {
112             Debug.Assert(iterator != null);
113             m_iterator = iterator;
114             m_hadCurrent = true;
115             m_isInitialized = false;
116         }
117
118         public T Current
119         {
120             get
121             {
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
126                 if (!m_hadCurrent)
127                     ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumEnded();
128                 return m_current;
129             }
130         }
131
132         object IEnumerator.Current
133         {
134             get
135             {
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
140                 if (!m_hadCurrent)
141                     ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumEnded();
142                 return m_current;
143             }
144         }
145
146         public bool MoveNext()
147         {
148             // If we've passed the end of the iteration, IEnumerable<T> should return false, while
149             // IIterable will fail the interface call
150             if (!m_hadCurrent)
151             {
152                 return false;
153             }
154
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
157             try
158             {
159                 if (!m_isInitialized)
160                 {
161                     m_hadCurrent = m_iterator.HasCurrent;
162                     m_isInitialized = true;
163                 }
164                 else
165                 {
166                     m_hadCurrent = m_iterator.MoveNext();
167                 }
168
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)
173                 //
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
176                 //     of the collection
177                 if (m_hadCurrent)
178                 {
179                     m_current = m_iterator.Current;
180                 }
181             }
182             catch (Exception e)
183             {
184                 // Translate E_CHANGED_STATE into an InvalidOperationException for an updated enumeration
185                 if (Marshal.GetHRForException(e) == HResults.E_CHANGED_STATE)
186                 {
187                     ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
188                 }
189                 else
190                 {
191                     throw;
192                 }
193             }
194
195             return m_hadCurrent;
196         }
197
198         public void Reset()
199         {
200             throw new NotSupportedException();
201         }
202
203         public void Dispose()
204         {
205         }
206     }
207 }