1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 //
5 // ==--==
6 //
7 // <OWNER>GPaperin</OWNER>
8 // <OWNER>Microsoft</OWNER>
9 
10 using System;
11 using System.Security;
12 using System.Reflection;
13 using System.Collections;
14 using System.Collections.Generic;
15 using System.Collections.ObjectModel;
16 using System.Diagnostics.Contracts;
17 using System.Runtime.InteropServices;
18 using System.Runtime.CompilerServices;
19 
20 namespace System.Runtime.InteropServices.WindowsRuntime
21 {
22     // This is a set of stub methods implementing the support for the IVector`1 interface on managed
23     // objects that implement IList`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 ListToVectorAdapter objects. Rather, they are of type
28     // IList<T>. No actual ListToVectorAdapter object is ever instantiated. Thus, you will
29     // see a lot of expressions that cast "this" to "IList<T>".
30     internal sealed class ListToVectorAdapter
31     {
ListToVectorAdapter()32         private ListToVectorAdapter()
33         {
34             Contract.Assert(false, "This class is never instantiated");
35         }
36 
37         // T GetAt(uint index)
38         [SecurityCritical]
GetAt(uint index)39         internal T GetAt<T>(uint index)
40         {
41             IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this);
42             EnsureIndexInt32(index, _this.Count);
43 
44             try
45             {
46                 return _this[(Int32)index];
47             }
48             catch (ArgumentOutOfRangeException ex)
49             {
50                 throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange");
51             }
52         }
53 
54         // uint Size { get }
55         [SecurityCritical]
Size()56         internal uint Size<T>()
57         {
58             IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this);
59             return (uint)_this.Count;
60         }
61 
62         // IVectorView<T> GetView()
63         [SecurityCritical]
GetView()64         internal IReadOnlyList<T> GetView<T>()
65         {
66             IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this);
67             Contract.Assert(_this != null);
68 
69             // Note: This list is not really read-only - you could QI for a modifiable
70             // list.  We gain some perf by doing this.  We believe this is acceptable.
71             IReadOnlyList<T> roList = _this as IReadOnlyList<T>;
72             if (roList == null)
73             {
74                 roList = new ReadOnlyCollection<T>(_this);
75             }
76             return roList;
77         }
78 
79         // bool IndexOf(T value, out uint index)
80         [SecurityCritical]
IndexOf(T value, out uint index)81         internal bool IndexOf<T>(T value, out uint index)
82         {
83             IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this);
84             int ind = _this.IndexOf(value);
85 
86             if (-1 == ind)
87             {
88                 index = 0;
89                 return false;
90             }
91 
92             index = (uint)ind;
93             return true;
94         }
95 
96         // void SetAt(uint index, T value)
97         [SecurityCritical]
SetAt(uint index, T value)98         internal void SetAt<T>(uint index, T value)
99         {
100             IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this);
101             EnsureIndexInt32(index, _this.Count);
102 
103             try
104             {
105                 _this[(int)index] = value;
106             }
107             catch (ArgumentOutOfRangeException ex)
108             {
109                 throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange");
110             }
111         }
112 
113         // void InsertAt(uint index, T value)
114         [SecurityCritical]
InsertAt(uint index, T value)115         internal void InsertAt<T>(uint index, T value)
116         {
117             IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this);
118 
119             // Inserting at an index one past the end of the list is equivalent to appending
120             // so we need to ensure that we're within (0, count + 1).
121             EnsureIndexInt32(index, _this.Count + 1);
122 
123             try
124             {
125                 _this.Insert((int)index, value);
126             }
127             catch (ArgumentOutOfRangeException ex)
128             {
129                 // Change error code to match what WinRT expects
130                 ex.SetErrorCode(__HResults.E_BOUNDS);
131                 throw;
132             }
133         }
134 
135         // void RemoveAt(uint index)
136         [SecurityCritical]
RemoveAt(uint index)137         internal void RemoveAt<T>(uint index)
138         {
139             IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this);
140             EnsureIndexInt32(index, _this.Count);
141 
142             try
143             {
144                 _this.RemoveAt((Int32)index);
145             }
146             catch (ArgumentOutOfRangeException ex)
147             {
148                 // Change error code to match what WinRT expects
149                 ex.SetErrorCode(__HResults.E_BOUNDS);
150                 throw;
151             }
152         }
153 
154         // void Append(T value)
155         [SecurityCritical]
Append(T value)156         internal void Append<T>(T value)
157         {
158             IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this);
159             _this.Add(value);
160         }
161 
162         // void RemoveAtEnd()
163         [SecurityCritical]
RemoveAtEnd()164         internal void RemoveAtEnd<T>()
165         {
166             IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this);
167             if (_this.Count == 0)
168             {
169                 Exception e = new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotRemoveLastFromEmptyCollection"));
170                 e.SetErrorCode(__HResults.E_BOUNDS);
171                 throw e;
172             }
173 
174             uint size = (uint)_this.Count;
175             RemoveAt<T>(size - 1);
176         }
177 
178         // void Clear()
179         [SecurityCritical]
Clear()180         internal void Clear<T>()
181         {
182             IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this);
183             _this.Clear();
184         }
185 
186         // uint GetMany(uint startIndex, T[] items)
187         [SecurityCritical]
GetMany(uint startIndex, T[] items)188         internal uint GetMany<T>(uint startIndex, T[] items)
189         {
190             IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this);
191             return GetManyHelper<T>(_this, startIndex, items);
192         }
193 
194         // void ReplaceAll(T[] items)
195         [SecurityCritical]
ReplaceAll(T[] items)196         internal void ReplaceAll<T>(T[] items)
197         {
198             IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this);
199             _this.Clear();
200 
201             if (items != null)
202             {
203                 foreach (T item in items)
204                 {
205                     _this.Add(item);
206                 }
207             }
208         }
209 
210         // Helpers:
211 
EnsureIndexInt32(uint index, int listCapacity)212         private static void EnsureIndexInt32(uint index, int listCapacity)
213         {
214             // We use '<=' and not '<' becasue Int32.MaxValue == index would imply
215             // that Size > Int32.MaxValue:
216             if (((uint)Int32.MaxValue) <= index || index >= (uint)listCapacity)
217             {
218                 Exception e = new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_IndexLargerThanMaxValue"));
219                 e.SetErrorCode(__HResults.E_BOUNDS);
220                 throw e;
221             }
222         }
223 
GetManyHelper(IList<T> sourceList, uint startIndex, T[] items)224         private static uint GetManyHelper<T>(IList<T> sourceList, uint startIndex, T[] items)
225         {
226             // Calling GetMany with a start index equal to the size of the list should always
227             // return 0 elements, regardless of the input item size
228             if (startIndex == sourceList.Count)
229             {
230                 return 0;
231             }
232 
233             EnsureIndexInt32(startIndex, sourceList.Count);
234 
235             if (items == null)
236             {
237                 return 0;
238             }
239 
240             uint itemCount = Math.Min((uint)items.Length, (uint)sourceList.Count - startIndex);
241             for (uint i = 0; i < itemCount; ++i)
242             {
243                 items[i] = sourceList[(int)(i + startIndex)];
244             }
245 
246             if (typeof(T) == typeof(string))
247             {
248                 string[] stringItems = items as string[];
249 
250                 // Fill in rest of the array with String.Empty to avoid marshaling failure
251                 for (uint i = itemCount; i < items.Length; ++i)
252                     stringItems[i] = String.Empty;
253             }
254 
255             return itemCount;
256         }
257     }
258 }
259