Nullable: System.Runtime.InteropServices.CustomMarshalers/WindowsRuntime (#23930)
[platform/upstream/coreclr.git] / src / System.Private.CoreLib / src / System / Runtime / InteropServices / WindowsRuntime / MapToDictionaryAdapter.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.Generic;
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 IDictionary`2 interface on WinRT
13     // objects that support IMap`2. 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 MapToDictionaryAdapter objects. Rather, they are of type
18     // IMap<K, V>. No actual MapToDictionaryAdapter object is ever instantiated. Thus, you will see
19     // a lot of expressions that cast "this" to "IMap<K, V>".
20     internal sealed class MapToDictionaryAdapter
21     {
22         private MapToDictionaryAdapter()
23         {
24             Debug.Fail("This class is never instantiated");
25         }
26
27         // V this[K key] { get }
28         internal V Indexer_Get<K, V>(K key)
29         {
30             if (key == null)
31                 throw new ArgumentNullException(nameof(key));
32
33
34             IMap<K, V> _this = Unsafe.As<IMap<K, V>>(this);
35             return Lookup(_this, key);
36         }
37
38         // V this[K key] { set }
39         internal void Indexer_Set<K, V>(K key, V value)
40         {
41             if (key == null)
42                 throw new ArgumentNullException(nameof(key));
43
44
45             IMap<K, V> _this = Unsafe.As<IMap<K, V>>(this);
46             Insert(_this, key, value);
47         }
48
49         // ICollection<K> Keys { get }
50         internal ICollection<K> Keys<K, V>()
51         {
52             IMap<K, V> _this = Unsafe.As<IMap<K, V>>(this);
53             IDictionary<K, V> dictionary = (IDictionary<K, V>)_this;
54             return new DictionaryKeyCollection<K, V>(dictionary);
55         }
56
57         // ICollection<V> Values { get }
58         internal ICollection<V> Values<K, V>()
59         {
60             IMap<K, V> _this = Unsafe.As<IMap<K, V>>(this);
61             IDictionary<K, V> dictionary = (IDictionary<K, V>)_this;
62             return new DictionaryValueCollection<K, V>(dictionary);
63         }
64
65         // bool ContainsKey(K key)
66         internal bool ContainsKey<K, V>(K key)
67         {
68             if (key == null)
69                 throw new ArgumentNullException(nameof(key));
70
71             IMap<K, V> _this = Unsafe.As<IMap<K, V>>(this);
72             return _this.HasKey(key);
73         }
74
75         // void Add(K key, V value)
76         internal void Add<K, V>(K key, V value)
77         {
78             if (key == null)
79                 throw new ArgumentNullException(nameof(key));
80
81             if (ContainsKey<K, V>(key))
82                 throw new ArgumentException(SR.Argument_AddingDuplicate);
83
84
85             IMap<K, V> _this = Unsafe.As<IMap<K, V>>(this);
86             Insert(_this, key, value);
87         }
88
89         // bool Remove(TKey key)
90         internal bool Remove<K, V>(K key)
91         {
92             if (key == null)
93                 throw new ArgumentNullException(nameof(key));
94
95             IMap<K, V> _this = Unsafe.As<IMap<K, V>>(this);
96             if (!_this.HasKey(key))
97                 return false;
98
99             try
100             {
101                 _this.Remove(key);
102                 return true;
103             }
104             catch (Exception ex)
105             {
106                 if (HResults.E_BOUNDS == ex.HResult)
107                     return false;
108
109                 throw;
110             }
111         }
112
113         // bool TryGetValue(TKey key, out TValue value)
114         internal bool TryGetValue<K, V>(K key, out V value)
115         {
116             if (key == null)
117                 throw new ArgumentNullException(nameof(key));
118
119             IMap<K, V> _this = Unsafe.As<IMap<K, V>>(this);
120             if (!_this.HasKey(key))
121             {
122                 value = default!; // TODO-NULLABLE-GENERIC
123                 return false;
124             }
125
126             try
127             {
128                 value = Lookup(_this, key);
129                 return true;
130             }
131             catch (KeyNotFoundException)
132             {
133                 value = default!; // TODO-NULLABLE-GENERIC
134                 return false;
135             }
136         }
137
138         // Helpers:
139
140         private static V Lookup<K, V>(IMap<K, V> _this, K key)
141         {
142             Debug.Assert(null != key);
143
144             try
145             {
146                 return _this.Lookup(key);
147             }
148             catch (Exception ex)
149             {
150                 if (HResults.E_BOUNDS == ex.HResult)
151                     throw new KeyNotFoundException(SR.Arg_KeyNotFound);
152                 throw;
153             }
154         }
155
156         private static bool Insert<K, V>(IMap<K, V> _this, K key, V value)
157         {
158             Debug.Assert(null != key);
159
160             bool replaced = _this.Insert(key, value);
161             return replaced;
162         }
163     }
164 }