Nullable: System.Runtime.InteropServices.CustomMarshalers/WindowsRuntime (#23930)
[platform/upstream/coreclr.git] / src / System.Private.CoreLib / src / System / Runtime / InteropServices / WindowsRuntime / ListToBindableVectorAdapter.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.Diagnostics;
8 using Internal.Runtime.CompilerServices;
9
10 namespace System.Runtime.InteropServices.WindowsRuntime
11 {
12     // This is a set of stub methods implementing the support for the IBindableVector interface on managed
13     // objects that implement IList. Used by the interop mashaling infrastructure.
14     //
15     // The methods on this class must be written VERY carefully to avoid introducing security holes.
16     // That's because they are invoked with special "this"! The "this" object
17     // for all of these methods are not ListToBindableVectorAdapter objects. Rather, they are of type
18     // IList. No actual ListToVectorBindableAdapter object is ever instantiated. Thus, you will
19     // see a lot of expressions that cast "this" to "IList". 
20     internal sealed class ListToBindableVectorAdapter
21     {
22         private ListToBindableVectorAdapter()
23         {
24             Debug.Fail("This class is never instantiated");
25         }
26
27         // object GetAt(uint index)
28         internal object GetAt(uint index)
29         {
30             IList _this = Unsafe.As<IList>(this);
31             EnsureIndexInt32(index, _this.Count);
32
33             try
34             {
35                 return _this[(int)index];
36             }
37             catch (ArgumentOutOfRangeException ex)
38             {
39                 throw WindowsRuntimeMarshal.GetExceptionForHR(HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange");
40             }
41         }
42
43         // uint Size { get }
44         internal uint Size()
45         {
46             IList _this = Unsafe.As<IList>(this);
47             return (uint)_this.Count;
48         }
49
50         // IBindableVectorView GetView()
51         internal IBindableVectorView GetView()
52         {
53             IList _this = Unsafe.As<IList>(this);
54             return new ListToBindableVectorViewAdapter(_this);
55         }
56
57         // bool IndexOf(object value, out uint index)
58         internal bool IndexOf(object value, out uint index)
59         {
60             IList _this = Unsafe.As<IList>(this);
61             int ind = _this.IndexOf(value);
62
63             if (-1 == ind)
64             {
65                 index = 0;
66                 return false;
67             }
68
69             index = (uint)ind;
70             return true;
71         }
72
73         // void SetAt(uint index, object value)
74         internal void SetAt(uint index, object value)
75         {
76             IList _this = Unsafe.As<IList>(this);
77             EnsureIndexInt32(index, _this.Count);
78
79             try
80             {
81                 _this[(int)index] = value;
82             }
83             catch (ArgumentOutOfRangeException ex)
84             {
85                 throw WindowsRuntimeMarshal.GetExceptionForHR(HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange");
86             }
87         }
88
89         // void InsertAt(uint index, object value)
90         internal void InsertAt(uint index, object value)
91         {
92             IList _this = Unsafe.As<IList>(this);
93
94             // Inserting at an index one past the end of the list is equivalent to appending
95             // so we need to ensure that we're within (0, count + 1).
96             EnsureIndexInt32(index, _this.Count + 1);
97
98             try
99             {
100                 _this.Insert((int)index, value);
101             }
102             catch (ArgumentOutOfRangeException ex)
103             {
104                 // Change error code to match what WinRT expects
105                 ex.HResult = HResults.E_BOUNDS;
106                 throw;
107             }
108         }
109
110         // void RemoveAt(uint index)
111         internal void RemoveAt(uint index)
112         {
113             IList _this = Unsafe.As<IList>(this);
114             EnsureIndexInt32(index, _this.Count);
115
116             try
117             {
118                 _this.RemoveAt((int)index);
119             }
120             catch (ArgumentOutOfRangeException ex)
121             {
122                 // Change error code to match what WinRT expects
123                 ex.HResult = HResults.E_BOUNDS;
124                 throw;
125             }
126         }
127
128         // void Append(object value)
129         internal void Append(object value)
130         {
131             IList _this = Unsafe.As<IList>(this);
132             _this.Add(value);
133         }
134
135         // void RemoveAtEnd()
136         internal void RemoveAtEnd()
137         {
138             IList _this = Unsafe.As<IList>(this);
139             if (_this.Count == 0)
140             {
141                 Exception e = new InvalidOperationException(SR.InvalidOperation_CannotRemoveLastFromEmptyCollection);
142                 e.HResult = HResults.E_BOUNDS;
143                 throw e;
144             }
145
146             uint size = (uint)_this.Count;
147             RemoveAt(size - 1);
148         }
149
150         // void Clear()
151         internal void Clear()
152         {
153             IList _this = Unsafe.As<IList>(this);
154             _this.Clear();
155         }
156
157         // Helpers:
158
159         private static void EnsureIndexInt32(uint index, int listCapacity)
160         {
161             // We use '<=' and not '<' becasue int.MaxValue == index would imply
162             // that Size > int.MaxValue:
163             if (((uint)int.MaxValue) <= index || index >= (uint)listCapacity)
164             {
165                 Exception e = new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexLargerThanMaxValue);
166                 e.HResult = HResults.E_BOUNDS;
167                 throw e;
168             }
169         }
170     }
171 }