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.
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Diagnostics;
9 using Internal.Runtime.CompilerServices;
11 namespace System.Runtime.InteropServices.WindowsRuntime
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.
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
23 private EnumerableToIterableAdapter()
25 Debug.Fail("This class is never instantiated");
28 // This method is invoked when First is called on a managed implementation of IIterable<T>.
29 internal IIterator<T> First_Stub<T>()
31 IEnumerable<T> _this = Unsafe.As<IEnumerable<T>>(this);
32 return new EnumeratorToIteratorAdapter<T>(_this.GetEnumerator());
36 internal sealed class EnumerableToBindableIterableAdapter
38 private EnumerableToBindableIterableAdapter()
40 Debug.Fail("This class is never instantiated");
43 internal sealed class NonGenericToGenericEnumerator : IEnumerator<object?>
45 private IEnumerator enumerator;
47 public NonGenericToGenericEnumerator(IEnumerator enumerator)
48 { this.enumerator = enumerator; }
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() { }
56 // This method is invoked when First is called on a managed implementation of IBindableIterable.
57 internal IBindableIterator First_Stub()
59 IEnumerable _this = Unsafe.As<IEnumerable>(this);
60 return new EnumeratorToIteratorAdapter<object?>(new NonGenericToGenericEnumerator(_this.GetEnumerator()));
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
67 private IEnumerator<T> m_enumerator;
68 private bool m_firstItem = true;
69 private bool m_hasCurrent;
71 internal EnumeratorToIteratorAdapter(IEnumerator<T> enumerator)
73 Debug.Assert(enumerator != null);
74 m_enumerator = enumerator;
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.
91 throw WindowsRuntimeMarshal.GetExceptionForHR(HResults.E_BOUNDS, null);
94 return m_enumerator.Current;
98 object? IBindableIterator.Current
102 return ((IIterator<T>)this).Current;
106 public bool HasCurrent
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.
122 public bool MoveNext()
126 m_hasCurrent = m_enumerator.MoveNext();
128 catch (InvalidOperationException e)
130 throw WindowsRuntimeMarshal.GetExceptionForHR(HResults.E_CHANGED_STATE, e);
136 public int GetMany(T[] items)
144 while (index < items.Length && HasCurrent)
146 items[index] = Current;
151 if (typeof(T) == typeof(string))
153 string[] stringItems = (items as string[])!;
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;