Nullable: System.Runtime.InteropServices.CustomMarshalers/WindowsRuntime (#23930)
[platform/upstream/coreclr.git] / src / System.Private.CoreLib / src / System / Runtime / InteropServices / WindowsRuntime / EnumeratorToIteratorAdapter.cs
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 #nullable enable
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Diagnostics;
9 using Internal.Runtime.CompilerServices;
10
11 namespace System.Runtime.InteropServices.WindowsRuntime
12 {
13     // This is a set of stub methods implementing the support for the IIterable`1 interface on managed
14     // objects that implement IEnumerable`1. Used by the interop mashaling infrastructure.
15     //
16     // The methods on this class must be written VERY carefully to avoid introducing security holes.
17     // That's because they are invoked with special "this"! The "this" object
18     // for all of these methods are not EnumerableToIterableAdapter objects. Rather, they are of type
19     // IEnumerable<T>. No actual EnumerableToIterableAdapter object is ever instantiated. Thus, you will
20     // see a lot of expressions that cast "this" to "IEnumerable<T>". 
21     internal sealed class EnumerableToIterableAdapter
22     {
23         private EnumerableToIterableAdapter()
24         {
25             Debug.Fail("This class is never instantiated");
26         }
27
28         // This method is invoked when First is called on a managed implementation of IIterable<T>.
29         internal IIterator<T> First_Stub<T>()
30         {
31             IEnumerable<T> _this = Unsafe.As<IEnumerable<T>>(this);
32             return new EnumeratorToIteratorAdapter<T>(_this.GetEnumerator());
33         }
34     }
35
36     internal sealed class EnumerableToBindableIterableAdapter
37     {
38         private EnumerableToBindableIterableAdapter()
39         {
40             Debug.Fail("This class is never instantiated");
41         }
42
43         internal sealed class NonGenericToGenericEnumerator : IEnumerator<object?>
44         {
45             private IEnumerator enumerator;
46
47             public NonGenericToGenericEnumerator(IEnumerator enumerator)
48             { this.enumerator = enumerator; }
49
50             public object? Current { get { return enumerator.Current; } }
51             public bool MoveNext() { return enumerator.MoveNext(); }
52             public void Reset() { enumerator.Reset(); }
53             public void Dispose() { }
54         }
55
56         // This method is invoked when First is called on a managed implementation of IBindableIterable.
57         internal IBindableIterator First_Stub()
58         {
59             IEnumerable _this = Unsafe.As<IEnumerable>(this);
60             return new EnumeratorToIteratorAdapter<object?>(new NonGenericToGenericEnumerator(_this.GetEnumerator()));
61         }
62     }
63
64     // Adapter class which holds a managed IEnumerator<T>, exposing it as a Windows Runtime IIterator<T>
65     internal sealed class EnumeratorToIteratorAdapter<T> : IIterator<T>, IBindableIterator
66     {
67         private IEnumerator<T> m_enumerator;
68         private bool m_firstItem = true;
69         private bool m_hasCurrent;
70
71         internal EnumeratorToIteratorAdapter(IEnumerator<T> enumerator)
72         {
73             Debug.Assert(enumerator != null);
74             m_enumerator = enumerator;
75         }
76
77         public T Current
78         {
79             get
80             {
81                 // IEnumerator starts at item -1, while IIterators start at item 0.  Therefore, if this is the
82                 // first access to the iterator we need to advance to the first item.
83                 if (m_firstItem)
84                 {
85                     m_firstItem = false;
86                     MoveNext();
87                 }
88
89                 if (!m_hasCurrent)
90                 {
91                     throw WindowsRuntimeMarshal.GetExceptionForHR(HResults.E_BOUNDS, null);
92                 }
93
94                 return m_enumerator.Current;
95             }
96         }
97
98         object? IBindableIterator.Current
99         {
100             get
101             {
102                 return ((IIterator<T>)this).Current;
103             }
104         }
105
106         public bool HasCurrent
107         {
108             get
109             {
110                 // IEnumerator starts at item -1, while IIterators start at item 0.  Therefore, if this is the
111                 // first access to the iterator we need to advance to the first item.
112                 if (m_firstItem)
113                 {
114                     m_firstItem = false;
115                     MoveNext();
116                 }
117
118                 return m_hasCurrent;
119             }
120         }
121
122         public bool MoveNext()
123         {
124             try
125             {
126                 m_hasCurrent = m_enumerator.MoveNext();
127             }
128             catch (InvalidOperationException e)
129             {
130                 throw WindowsRuntimeMarshal.GetExceptionForHR(HResults.E_CHANGED_STATE, e);
131             }
132
133             return m_hasCurrent;
134         }
135
136         public int GetMany(T[] items)
137         {
138             if (items == null)
139             {
140                 return 0;
141             }
142
143             int index = 0;
144             while (index < items.Length && HasCurrent)
145             {
146                 items[index] = Current;
147                 MoveNext();
148                 ++index;
149             }
150
151             if (typeof(T) == typeof(string))
152             {
153                 string[] stringItems = (items as string[])!;
154
155                 // Fill the rest of the array with string.Empty to avoid marshaling failure
156                 for (int i = index; i < items.Length; ++i)
157                     stringItems[i] = string.Empty;
158             }
159
160             return index;
161         }
162     }
163 }