Guo Hui пре 6 година
родитељ
комит
a21753a596

+ 11 - 0
src/Chino.Core/Objects/Directory.cs

@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Chino.Objects
+{
+    public sealed class Directory
+    {
+        //private readonly Dictionary<>
+    }
+}

+ 10 - 0
src/Chino.Core/Objects/IObjectManager.cs

@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Chino.Objects
+{
+    public interface IObjectManager
+    {
+    }
+}

+ 10 - 0
src/Chino.Core/Objects/Object.cs

@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Chino.Objects
+{
+    public class Object
+    {
+    }
+}

+ 42 - 8
src/Native/natsu.fcall.cpp

@@ -50,7 +50,18 @@ int32_t Array::GetLowerBound(gc_obj_ref<Array> _this, int32_t dimension)
 
 void Array::_s_Copy(gc_obj_ref<Array> sourceArray, int32_t sourceIndex, gc_obj_ref<Array> destinationArray, int32_t destinationIndex, int32_t length, bool reliable)
 {
-    throw_exception<InvalidOperationException>();
+    check_null_obj_ref(sourceArray);
+    check_null_obj_ref(destinationArray);
+    auto src = sourceArray.cast<RawSzArrayData>();
+    auto dest = sourceArray.cast<RawSzArrayData>();
+    auto element_size = sourceArray.header().vtable_->ElementSize;
+
+    if ((sourceIndex + length) > (intptr_t)src->Count || (destinationIndex + length) > (intptr_t)dest->Count)
+        throw_index_out_of_range_exception();
+
+    if (sourceArray.header().vtable_ != destinationArray.header().vtable_)
+        throw_exception<ArrayTypeMismatchException>();
+    std::memmove(&dest->Data + (size_t)destinationIndex * element_size, &src->Data + (size_t)sourceIndex * element_size, (size_t)length * element_size);
 }
 
 gc_ref<uint8_t> Array::_s_GetRawArrayGeometry(gc_obj_ref<Array> array, gc_ref<uint32_t> numComponents, gc_ref<uint32_t> elementSize, gc_ref<int32_t> lowerBound, ::natsu::gc_ref<bool> containsGCPointers)
@@ -300,41 +311,64 @@ void *get_current_thread_id()
 
 void Monitor::_s_Enter(gc_obj_ref<Object> obj)
 {
-    check_null_obj_ref(obj);
-    auto thread_id = get_current_thread_id();
-    auto &sync_header = obj.header().sync_header_;
+    bool lock_taken = false;
+    _s_ReliableEnter(obj, lock_taken);
 }
 
 void Monitor::_s_ReliableEnter(gc_obj_ref<Object> obj, gc_ref<bool> lockTaken)
 {
-    *lockTaken = true;
+    check_null_obj_ref(obj);
+    auto thread_id = get_current_thread_id();
+    auto &sync_header = obj.header().sync_header_;
+    void *expected = nullptr;
+
+    while (true)
+    {
+        if (sync_header.lock_taken.compare_exchange_strong(expected, thread_id))
+        {
+            Volatile::_s_Write(*lockTaken, true);
+            break;
+        }
+
+        Thread::_s_SleepInternal(1);
+    }
 }
 
 void Monitor::_s_Exit(::natsu::gc_obj_ref<Object> obj)
 {
+    check_null_obj_ref(obj);
+    auto thread_id = get_current_thread_id();
+    auto &sync_header = obj.header().sync_header_;
+    void *expected = thread_id;
+    sync_header.lock_taken.compare_exchange_strong(expected, nullptr);
 }
 
 void Monitor::_s_ReliableEnterTimeout(gc_obj_ref<Object> obj, int32_t timeout, gc_ref<bool> lockTaken)
 {
-    *lockTaken = true;
+    pure_call();
 }
 
 bool Monitor::_s_IsEnteredNative(gc_obj_ref<Object> obj)
 {
-    return true;
+    check_null_obj_ref(obj);
+    auto thread_id = get_current_thread_id();
+    auto &sync_header = obj.header().sync_header_;
+    return sync_header.lock_taken.load() == thread_id;
 }
 
 bool Monitor::_s_ObjWait(bool exitContext, int32_t millisecondsTimeout, gc_obj_ref<Object> obj)
 {
-    return true;
+    pure_call();
 }
 
 void Monitor::_s_ObjPulse(gc_obj_ref<Object> obj)
 {
+    pure_call();
 }
 
 void Monitor::_s_ObjPulseAll(gc_obj_ref<Object> obj)
 {
+    pure_call();
 }
 
 int64_t Monitor::_s_GetLockContentionCount()

+ 75 - 1
src/Native/natsu.runtime.h

@@ -629,6 +629,75 @@ gc_ref<T> unbox_exact(gc_obj_ref<::System_Private_CoreLib::System::Object> value
 
 namespace System_Private_CoreLib
 {
+namespace details
+{
+    template <class T>
+    struct default_comparer_impl
+    {
+        ::natsu::gc_obj_ref<System::Collections::Generic::Comparer_1<T>> operator()() const
+        {
+            using namespace System::Collections::Generic;
+
+            // If T implements IComparable<T> return a GenericComparer<T>
+            if constexpr (natsu::is_convertible_v<T, IComparable_1<T>>)
+                return natsu::make_object<GenericComparer_1<T>>();
+            else
+                return nullptr;
+        }
+    };
+
+    // Nullable does not implement IComparable<T?> directly because that would add an extra interface call per comparison.
+    // Instead, it relies on Comparer<T?>.Default to specialize for nullables and do the lifted comparisons if T implements IComparable.
+    template <class T>
+    struct default_comparer_impl<System::Nullable_1<T>>
+    {
+        ::natsu::gc_obj_ref<System::Collections::Generic::Comparer_1<System::Nullable_1<T>>> operator()() const
+        {
+            using namespace System::Collections::Generic;
+
+            // If T implements IComparable<T> return a GenericComparer<T>
+            if constexpr (natsu::is_convertible_v<T, IComparable_1<T>>)
+                return natsu::make_object<NullableComparer_1<T>>();
+            else
+                return nullptr;
+        }
+    };
+
+    template <class T>
+    struct default_equality_comparer_impl
+    {
+        ::natsu::gc_obj_ref<System::Collections::Generic::EqualityComparer_1<T>> operator()() const
+        {
+            using namespace System::Collections::Generic;
+
+            // Specialize for byte so Array.IndexOf is faster.
+            if constexpr (std::is_same_v<T, uint8_t>)
+                return natsu::make_object<ByteEqualityComparer>();
+            // If T implements IEquatable<T> return a GenericEqualityComparer<T>
+            else if constexpr (natsu::is_convertible_v<T, ::System_Private_CoreLib::System::IEquatable_1<T>>)
+                return natsu::make_object<GenericEqualityComparer_1<T>>();
+            else
+                return nullptr;
+        }
+    };
+
+    // Nullable does not implement IEquatable<T?> directly because that would add an extra interface call per comparison.
+    // Instead, it relies on EqualityComparer<T?>.Default to specialize for nullables and do the lifted comparisons if T implements IEquatable.
+    template <class T>
+    struct default_equality_comparer_impl<System::Nullable_1<T>>
+    {
+        ::natsu::gc_obj_ref<System::Collections::Generic::EqualityComparer_1<System::Nullable_1<T>>> operator()() const
+        {
+            using namespace System::Collections::Generic;
+
+            if constexpr (natsu::is_convertible_v<T, IEquatable_1<T>>)
+                return natsu::make_object<NullableEqualityComparer_1<T>>();
+            else
+                return nullptr;
+        }
+    };
+}
+
 template <class T>
 void System::ByReference_1<T>::_ctor(::natsu::gc_ref<System::ByReference_1<T>> _this, ::natsu::gc_ref<::natsu::variable_type_t<T>> value)
 {
@@ -756,6 +825,11 @@ bool System::Runtime::CompilerServices::RuntimeHelpers::_s_IsBitwiseEquatable()
 template <class T>
 ::natsu::gc_obj_ref<System::Collections::Generic::EqualityComparer_1<T>> System::Collections::Generic::ComparerHelpers::_s_CreateDefaultEqualityComparer()
 {
-    return nullptr;
+    using namespace System::Collections::Generic;
+
+    auto comparer = details::default_equality_comparer_impl<T>()();
+    if (!comparer)
+        comparer = natsu::make_object<ObjectEqualityComparer_1<T>>();
+    return comparer;
 }
 }

+ 8 - 9
src/Native/natsu.typedef.h

@@ -212,8 +212,7 @@ constexpr bool is_value_type_v = to_clr_type_t<T>::TypeInfo::IsValueType;
 
 // clang-format off
 template <class TFrom, class TTo>
-constexpr bool is_convertible_v = std::is_convertible_v<to_clr_type_t<TFrom> *, to_clr_type_t<TTo> *>
-|| std::is_convertible_v<typename to_clr_type_t<TFrom>::VTable *, typename to_clr_type_t<TTo>::VTable *>;
+constexpr bool is_convertible_v = std::is_convertible_v<typename to_clr_type_t<TFrom>::VTable *, typename to_clr_type_t<TTo>::VTable *>;
 // clang-format on
 
 template <class T>
@@ -592,13 +591,13 @@ struct clr_exception
 template <class T, class U>
 constexpr bool operator==(const gc_obj_ref<T> &lhs, const gc_obj_ref<U> &rhs) noexcept
 {
-    return lhs.ptr_ == rhs.ptr_;
+    return reinterpret_cast<uintptr_t>(lhs.ptr_) == reinterpret_cast<uintptr_t>(rhs.ptr_);
 }
 
 template <class T, class U>
 constexpr bool operator==(const gc_obj_ref<T> &lhs, U *rhs) noexcept
 {
-    return lhs.ptr_ == rhs;
+    return reinterpret_cast<uintptr_t>(lhs.ptr_) == reinterpret_cast<uintptr_t>(rhs);
 }
 
 template <class T, class U>
@@ -919,11 +918,11 @@ struct static_object
 
 #define NATSU_OBJECT_IMPL
 
-#define NATSU_VALUETYPE_IMPL                                                                                \
-    template <class T>                                                                                      \
-    static ::natsu::gc_obj_ref<::System_Private_CoreLib::System::String> ToString(::natsu::gc_ref<T> _this) \
-    {                                                                                                       \
-        ::natsu::pure_call();                                                                               \
+#define NATSU_VALUETYPE_IMPL                                                                      \
+    template <class T>                                                                            \
+    static ::natsu::gc_obj_ref<::System_Private_CoreLib::System::String> ToString(const T &_this) \
+    {                                                                                             \
+        ::natsu::pure_call();                                                                     \
     }
 
 #define NATSU_PRIMITIVE_OPERATORS_IMPL

+ 10 - 0
src/Natsu.Compiler/ILImporter.cs

@@ -1096,6 +1096,16 @@ namespace Natsu.Compiler
             {
                 return $"{src.Expression}";
             }
+            else if (destType.ElementType == ElementType.Ptr &&
+                src.Type.Code == StackTypeCode.Ref)
+            {
+                return $"{src.Expression}.get()";
+            }
+            else if (destType.ElementType != ElementType.ByRef &&
+                src.Type.Code == StackTypeCode.Ref)
+            {
+                return $"*{src.Expression}";
+            }
             else
             {
                 if (destType.IsValueType)

+ 3 - 1
src/System.Collections/TypeForwards.cs

@@ -3,7 +3,9 @@ using System;
 using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 
+[assembly: TypeForwardedTo(typeof(Dictionary<,>))]
 [assembly: TypeForwardedTo(typeof(List<>))]
 [assembly: TypeForwardedTo(typeof(Comparer<>))]
 [assembly: TypeForwardedTo(typeof(EqualityComparer<>))]
-[assembly: AssemblyEmbeddedCode("namespace System { namespace Collections { namespace Generic { template <class T0> using List_1_Enumerator = ::System_Private_CoreLib::System::Collections::Generic::List_1_Enumerator<T0>; } } }")]
+[assembly: AssemblyEmbeddedCode("namespace System { namespace Collections { namespace Generic { template <class T0> using List_1_Enumerator = ::System_Private_CoreLib::System::Collections::Generic::List_1_Enumerator<T0>; } } }")]
+[assembly: AssemblyEmbeddedCode("namespace System { namespace Collections { namespace Generic { template <class T0, class T1> using Dictionary_2_Enumerator = ::System_Private_CoreLib::System::Collections::Generic::Dictionary_2_Enumerator<T0, T1>; } } }")]

+ 1223 - 0
src/System.Private.CoreLib/System/Collections/Generic/Dictionary.cs

@@ -0,0 +1,1223 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.Serialization;
+using System.Threading;
+
+namespace System.Collections.Generic
+{
+    /// <summary>
+    /// Used internally to control behavior of insertion into a <see cref="Dictionary{TKey, TValue}"/>.
+    /// </summary>
+    internal enum InsertionBehavior : byte
+    {
+        /// <summary>
+        /// The default insertion behavior.
+        /// </summary>
+        None = 0,
+
+        /// <summary>
+        /// Specifies that an existing entry with the same key should be overwritten if encountered.
+        /// </summary>
+        OverwriteExisting = 1,
+
+        /// <summary>
+        /// Specifies that if an existing entry with the same key is encountered, an exception should be thrown.
+        /// </summary>
+        ThrowOnExisting = 2
+    }
+
+    [DebuggerTypeProxy(typeof(IDictionaryDebugView<,>))]
+    [DebuggerDisplay("Count = {Count}")]
+    [Serializable]
+    [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+    public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue>
+    {
+        private struct Entry
+        {
+            public int hashCode;    // Lower 31 bits of hash code, -1 if unused
+            public int next;        // Index of next entry, -1 if last
+            public TKey key;           // Key of entry
+            public TValue value;         // Value of entry
+        }
+
+        private int[] _buckets;
+        private Entry[] _entries;
+        private int _count;
+        private int _freeList;
+        private int _freeCount;
+        private int _version;
+        private IEqualityComparer<TKey> _comparer;
+        private KeyCollection _keys;
+        private ValueCollection _values;
+        private object _syncRoot;
+
+        // constants for serialization
+        private const string VersionName = "Version"; // Do not rename (binary serialization)
+        private const string HashSizeName = "HashSize"; // Do not rename (binary serialization). Must save buckets.Length
+        private const string KeyValuePairsName = "KeyValuePairs"; // Do not rename (binary serialization)
+        private const string ComparerName = "Comparer"; // Do not rename (binary serialization)
+
+        public Dictionary() : this(0, null) { }
+
+        public Dictionary(int capacity) : this(capacity, null) { }
+
+        public Dictionary(IEqualityComparer<TKey> comparer) : this(0, comparer) { }
+
+        public Dictionary(int capacity, IEqualityComparer<TKey> comparer)
+        {
+            if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
+            if (capacity > 0) Initialize(capacity);
+            if (comparer != EqualityComparer<TKey>.Default)
+            {
+                _comparer = comparer;
+            }
+
+            if (typeof(TKey) == typeof(string) && _comparer == null)
+            {
+                // To start, move off default comparer for string which is randomised
+                _comparer = (IEqualityComparer<TKey>)NonRandomizedStringEqualityComparer.Default;
+            }
+        }
+
+        public Dictionary(IDictionary<TKey, TValue> dictionary) : this(dictionary, null) { }
+
+        public Dictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) :
+            this(dictionary != null ? dictionary.Count : 0, comparer)
+        {
+            if (dictionary == null)
+            {
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
+            }
+
+            // It is likely that the passed-in dictionary is Dictionary<TKey,TValue>. When this is the case,
+            // avoid the enumerator allocation and overhead by looping through the entries array directly.
+            // We only do this when dictionary is Dictionary<TKey,TValue> and not a subclass, to maintain
+            // back-compat with subclasses that may have overridden the enumerator behavior.
+            if (dictionary.GetType() == typeof(Dictionary<TKey, TValue>))
+            {
+                Dictionary<TKey, TValue> d = (Dictionary<TKey, TValue>)dictionary;
+                int count = d._count;
+                Entry[] entries = d._entries;
+                for (int i = 0; i < count; i++)
+                {
+                    if (entries[i].hashCode >= 0)
+                    {
+                        Add(entries[i].key, entries[i].value);
+                    }
+                }
+                return;
+            }
+
+            foreach (KeyValuePair<TKey, TValue> pair in dictionary)
+            {
+                Add(pair.Key, pair.Value);
+            }
+        }
+
+        public Dictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection) : this(collection, null) { }
+
+        public Dictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) :
+            this((collection as ICollection<KeyValuePair<TKey, TValue>>)?.Count ?? 0, comparer)
+        {
+            if (collection == null)
+            {
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
+            }
+
+            foreach (KeyValuePair<TKey, TValue> pair in collection)
+            {
+                Add(pair.Key, pair.Value);
+            }
+        }
+
+        public IEqualityComparer<TKey> Comparer
+        {
+            get
+            {
+                return (_comparer == null || _comparer is NonRandomizedStringEqualityComparer) ? EqualityComparer<TKey>.Default : _comparer;
+            }
+        }
+
+        public int Count
+        {
+            get { return _count - _freeCount; }
+        }
+
+        public KeyCollection Keys
+        {
+            get
+            {
+                if (_keys == null) _keys = new KeyCollection(this);
+                return _keys;
+            }
+        }
+
+        ICollection<TKey> IDictionary<TKey, TValue>.Keys
+        {
+            get
+            {
+                if (_keys == null) _keys = new KeyCollection(this);
+                return _keys;
+            }
+        }
+
+        IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys
+        {
+            get
+            {
+                if (_keys == null) _keys = new KeyCollection(this);
+                return _keys;
+            }
+        }
+
+        public ValueCollection Values
+        {
+            get
+            {
+                if (_values == null) _values = new ValueCollection(this);
+                return _values;
+            }
+        }
+
+        ICollection<TValue> IDictionary<TKey, TValue>.Values
+        {
+            get
+            {
+                if (_values == null) _values = new ValueCollection(this);
+                return _values;
+            }
+        }
+
+        IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values
+        {
+            get
+            {
+                if (_values == null) _values = new ValueCollection(this);
+                return _values;
+            }
+        }
+
+        public TValue this[TKey key]
+        {
+            get
+            {
+                int i = FindEntry(key);
+                if (i >= 0) return _entries[i].value;
+                ThrowHelper.ThrowKeyNotFoundException(key);
+                return default;
+            }
+            set
+            {
+                bool modified = TryInsert(key, value, InsertionBehavior.OverwriteExisting);
+                Debug.Assert(modified);
+            }
+        }
+
+        public void Add(TKey key, TValue value)
+        {
+            bool modified = TryInsert(key, value, InsertionBehavior.ThrowOnExisting);
+            Debug.Assert(modified); // If there was an existing key and the Add failed, an exception will already have been thrown.
+        }
+
+        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> keyValuePair)
+            => Add(keyValuePair.Key, keyValuePair.Value);
+
+        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> keyValuePair)
+        {
+            int i = FindEntry(keyValuePair.Key);
+            if (i >= 0 && EqualityComparer<TValue>.Default.Equals(_entries[i].value, keyValuePair.Value))
+            {
+                return true;
+            }
+            return false;
+        }
+
+        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> keyValuePair)
+        {
+            int i = FindEntry(keyValuePair.Key);
+            if (i >= 0 && EqualityComparer<TValue>.Default.Equals(_entries[i].value, keyValuePair.Value))
+            {
+                Remove(keyValuePair.Key);
+                return true;
+            }
+            return false;
+        }
+
+        public void Clear()
+        {
+            int count = _count;
+            if (count > 0)
+            {
+                Array.Clear(_buckets, 0, _buckets.Length);
+
+                _count = 0;
+                _freeList = -1;
+                _freeCount = 0;
+                Array.Clear(_entries, 0, count);
+            }
+        }
+
+        public bool ContainsKey(TKey key)
+            => FindEntry(key) >= 0;
+
+        public bool ContainsValue(TValue value)
+        {
+            Entry[] entries = _entries;
+            if (value == null)
+            {
+                for (int i = 0; i < _count; i++)
+                {
+                    if (entries[i].hashCode >= 0 && entries[i].value == null) return true;
+                }
+            }
+            else
+            {
+                if (default(TValue) != null)
+                {
+                    // ValueType: Devirtualize with EqualityComparer<TValue>.Default intrinsic
+                    for (int i = 0; i < _count; i++)
+                    {
+                        if (entries[i].hashCode >= 0 && EqualityComparer<TValue>.Default.Equals(entries[i].value, value)) return true;
+                    }
+                }
+                else
+                {
+                    // Object type: Shared Generic, EqualityComparer<TValue>.Default won't devirtualize
+                    // https://github.com/dotnet/coreclr/issues/17273
+                    // So cache in a local rather than get EqualityComparer per loop iteration
+                    EqualityComparer<TValue> defaultComparer = EqualityComparer<TValue>.Default;
+                    for (int i = 0; i < _count; i++)
+                    {
+                        if (entries[i].hashCode >= 0 && defaultComparer.Equals(entries[i].value, value)) return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        private void CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
+        {
+            if (array == null)
+            {
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+            }
+
+            if ((uint)index > (uint)array.Length)
+            {
+                ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
+            }
+
+            if (array.Length - index < Count)
+            {
+                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
+            }
+
+            int count = _count;
+            Entry[] entries = _entries;
+            for (int i = 0; i < count; i++)
+            {
+                if (entries[i].hashCode >= 0)
+                {
+                    array[index++] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value);
+                }
+            }
+        }
+
+        public Enumerator GetEnumerator()
+            => new Enumerator(this, Enumerator.KeyValuePair);
+
+        IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
+            => new Enumerator(this, Enumerator.KeyValuePair);
+
+        private int FindEntry(TKey key)
+        {
+            if (key == null)
+            {
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+            }
+
+            int i = -1;
+            int[] buckets = _buckets;
+            Entry[] entries = _entries;
+            int collisionCount = 0;
+            if (buckets != null)
+            {
+                IEqualityComparer<TKey> comparer = _comparer;
+                if (comparer == null)
+                {
+                    int hashCode = key.GetHashCode() & 0x7FFFFFFF;
+                    // Value in _buckets is 1-based
+                    i = buckets[hashCode % buckets.Length] - 1;
+                    if (default(TKey) != null)
+                    {
+                        // ValueType: Devirtualize with EqualityComparer<TValue>.Default intrinsic
+                        do
+                        {
+                            // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
+                            // Test in if to drop range check for following array access
+                            if ((uint)i >= (uint)entries.Length || (entries[i].hashCode == hashCode && EqualityComparer<TKey>.Default.Equals(entries[i].key, key)))
+                            {
+                                break;
+                            }
+
+                            i = entries[i].next;
+                            if (collisionCount >= entries.Length)
+                            {
+                                // The chain of entries forms a loop; which means a concurrent update has happened.
+                                // Break out of the loop and throw, rather than looping forever.
+                                ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported();
+                            }
+                            collisionCount++;
+                        } while (true);
+                    }
+                    else
+                    {
+                        // Object type: Shared Generic, EqualityComparer<TValue>.Default won't devirtualize
+                        // https://github.com/dotnet/coreclr/issues/17273
+                        // So cache in a local rather than get EqualityComparer per loop iteration
+                        EqualityComparer<TKey> defaultComparer = EqualityComparer<TKey>.Default;
+                        do
+                        {
+                            // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
+                            // Test in if to drop range check for following array access
+                            if ((uint)i >= (uint)entries.Length || (entries[i].hashCode == hashCode && defaultComparer.Equals(entries[i].key, key)))
+                            {
+                                break;
+                            }
+
+                            i = entries[i].next;
+                            if (collisionCount >= entries.Length)
+                            {
+                                // The chain of entries forms a loop; which means a concurrent update has happened.
+                                // Break out of the loop and throw, rather than looping forever.
+                                ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported();
+                            }
+                            collisionCount++;
+                        } while (true);
+                    }
+                }
+                else
+                {
+                    int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
+                    // Value in _buckets is 1-based
+                    i = buckets[hashCode % buckets.Length] - 1;
+                    do
+                    {
+                        // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
+                        // Test in if to drop range check for following array access
+                        if ((uint)i >= (uint)entries.Length ||
+                            (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)))
+                        {
+                            break;
+                        }
+
+                        i = entries[i].next;
+                        if (collisionCount >= entries.Length)
+                        {
+                            // The chain of entries forms a loop; which means a concurrent update has happened.
+                            // Break out of the loop and throw, rather than looping forever.
+                            ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported();
+                        }
+                        collisionCount++;
+                    } while (true);
+                }
+            }
+
+            return i;
+        }
+
+        private int Initialize(int capacity)
+        {
+            int size = HashHelpers.GetPrime(capacity);
+
+            _freeList = -1;
+            _buckets = new int[size];
+            _entries = new Entry[size];
+
+            return size;
+        }
+
+        private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior)
+        {
+            if (key == null)
+            {
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+            }
+
+            if (_buckets == null)
+            {
+                Initialize(0);
+            }
+
+            Entry[] entries = _entries;
+            IEqualityComparer<TKey> comparer = _comparer;
+
+            int hashCode = ((comparer == null) ? key.GetHashCode() : comparer.GetHashCode(key)) & 0x7FFFFFFF;
+
+            int collisionCount = 0;
+            ref int bucket = ref _buckets[hashCode % _buckets.Length];
+            // Value in _buckets is 1-based
+            int i = bucket - 1;
+
+            if (comparer == null)
+            {
+                if (default(TKey) != null)
+                {
+                    // ValueType: Devirtualize with EqualityComparer<TValue>.Default intrinsic
+                    do
+                    {
+                        // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
+                        // Test uint in if rather than loop condition to drop range check for following array access
+                        if ((uint)i >= (uint)entries.Length)
+                        {
+                            break;
+                        }
+
+                        if (entries[i].hashCode == hashCode && EqualityComparer<TKey>.Default.Equals(entries[i].key, key))
+                        {
+                            if (behavior == InsertionBehavior.OverwriteExisting)
+                            {
+                                entries[i].value = value;
+                                _version++;
+                                return true;
+                            }
+
+                            if (behavior == InsertionBehavior.ThrowOnExisting)
+                            {
+                                ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key);
+                            }
+
+                            return false;
+                        }
+
+                        i = entries[i].next;
+                        if (collisionCount >= entries.Length)
+                        {
+                            // The chain of entries forms a loop; which means a concurrent update has happened.
+                            // Break out of the loop and throw, rather than looping forever.
+                            ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported();
+                        }
+                        collisionCount++;
+                    } while (true);
+                }
+                else
+                {
+                    // Object type: Shared Generic, EqualityComparer<TValue>.Default won't devirtualize
+                    // https://github.com/dotnet/coreclr/issues/17273
+                    // So cache in a local rather than get EqualityComparer per loop iteration
+                    EqualityComparer<TKey> defaultComparer = EqualityComparer<TKey>.Default;
+                    do
+                    {
+                        // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
+                        // Test uint in if rather than loop condition to drop range check for following array access
+                        if ((uint)i >= (uint)entries.Length)
+                        {
+                            break;
+                        }
+
+                        if (entries[i].hashCode == hashCode && defaultComparer.Equals(entries[i].key, key))
+                        {
+                            if (behavior == InsertionBehavior.OverwriteExisting)
+                            {
+                                entries[i].value = value;
+                                _version++;
+                                return true;
+                            }
+
+                            if (behavior == InsertionBehavior.ThrowOnExisting)
+                            {
+                                ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key);
+                            }
+
+                            return false;
+                        }
+
+                        i = entries[i].next;
+                        if (collisionCount >= entries.Length)
+                        {
+                            // The chain of entries forms a loop; which means a concurrent update has happened.
+                            // Break out of the loop and throw, rather than looping forever.
+                            ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported();
+                        }
+                        collisionCount++;
+                    } while (true);
+                }
+            }
+            else
+            {
+                do
+                {
+                    // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
+                    // Test uint in if rather than loop condition to drop range check for following array access
+                    if ((uint)i >= (uint)entries.Length)
+                    {
+                        break;
+                    }
+
+                    if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key))
+                    {
+                        if (behavior == InsertionBehavior.OverwriteExisting)
+                        {
+                            entries[i].value = value;
+                            _version++;
+                            return true;
+                        }
+
+                        if (behavior == InsertionBehavior.ThrowOnExisting)
+                        {
+                            ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key);
+                        }
+
+                        return false;
+                    }
+
+                    i = entries[i].next;
+                    if (collisionCount >= entries.Length)
+                    {
+                        // The chain of entries forms a loop; which means a concurrent update has happened.
+                        // Break out of the loop and throw, rather than looping forever.
+                        ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported();
+                    }
+                    collisionCount++;
+                } while (true);
+
+            }
+
+            bool updateFreeList = false;
+            int index;
+            if (_freeCount > 0)
+            {
+                index = _freeList;
+                updateFreeList = true;
+                _freeCount--;
+            }
+            else
+            {
+                int count = _count;
+                if (count == entries.Length)
+                {
+                    Resize();
+                    bucket = ref _buckets[hashCode % _buckets.Length];
+                }
+                index = count;
+                _count = count + 1;
+                entries = _entries;
+            }
+
+            ref Entry entry = ref entries[index];
+
+            if (updateFreeList)
+            {
+                _freeList = entry.next;
+            }
+            entry.hashCode = hashCode;
+            // Value in _buckets is 1-based
+            entry.next = bucket - 1;
+            entry.key = key;
+            entry.value = value;
+            // Value in _buckets is 1-based
+            bucket = index + 1;
+            _version++;
+
+            // Value types never rehash
+            if (default(TKey) == null && collisionCount > HashHelpers.HashCollisionThreshold && comparer is NonRandomizedStringEqualityComparer)
+            {
+                // If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing
+                // i.e. EqualityComparer<string>.Default.
+                _comparer = null;
+                Resize(entries.Length, true);
+            }
+
+            return true;
+        }
+
+        private void Resize()
+            => Resize(HashHelpers.ExpandPrime(_count), false);
+
+        private void Resize(int newSize, bool forceNewHashCodes)
+        {
+            // Value types never rehash
+            Debug.Assert(!forceNewHashCodes || default(TKey) == null);
+            Debug.Assert(newSize >= _entries.Length);
+
+            int[] buckets = new int[newSize];
+            Entry[] entries = new Entry[newSize];
+
+            int count = _count;
+            Array.Copy(_entries, 0, entries, 0, count);
+
+            if (default(TKey) == null && forceNewHashCodes)
+            {
+                for (int i = 0; i < count; i++)
+                {
+                    if (entries[i].hashCode >= 0)
+                    {
+                        Debug.Assert(_comparer == null);
+                        entries[i].hashCode = (entries[i].key.GetHashCode() & 0x7FFFFFFF);
+                    }
+                }
+            }
+
+            for (int i = 0; i < count; i++)
+            {
+                if (entries[i].hashCode >= 0)
+                {
+                    int bucket = entries[i].hashCode % newSize;
+                    // Value in _buckets is 1-based
+                    entries[i].next = buckets[bucket] - 1;
+                    // Value in _buckets is 1-based
+                    buckets[bucket] = i + 1;
+                }
+            }
+
+            _buckets = buckets;
+            _entries = entries;
+        }
+
+        // The overload Remove(TKey key, out TValue value) is a copy of this method with one additional
+        // statement to copy the value for entry being removed into the output parameter.
+        // Code has been intentionally duplicated for performance reasons.
+        public bool Remove(TKey key)
+        {
+            if (key == null)
+            {
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+            }
+
+            int[] buckets = _buckets;
+            Entry[] entries = _entries;
+            int collisionCount = 0;
+            if (buckets != null)
+            {
+                int hashCode = (_comparer?.GetHashCode(key) ?? key.GetHashCode()) & 0x7FFFFFFF;
+                int bucket = hashCode % buckets.Length;
+                int last = -1;
+                // Value in buckets is 1-based
+                int i = buckets[bucket] - 1;
+                while (i >= 0)
+                {
+                    ref Entry entry = ref entries[i];
+
+                    if (entry.hashCode == hashCode && (_comparer?.Equals(entry.key, key) ?? EqualityComparer<TKey>.Default.Equals(entry.key, key)))
+                    {
+                        if (last < 0)
+                        {
+                            // Value in buckets is 1-based
+                            buckets[bucket] = entry.next + 1;
+                        }
+                        else
+                        {
+                            entries[last].next = entry.next;
+                        }
+                        entry.hashCode = -1;
+                        entry.next = _freeList;
+
+                        if (RuntimeHelpers.IsReferenceOrContainsReferences<TKey>())
+                        {
+                            entry.key = default;
+                        }
+                        if (RuntimeHelpers.IsReferenceOrContainsReferences<TValue>())
+                        {
+                            entry.value = default;
+                        }
+                        _freeList = i;
+                        _freeCount++;
+                        return true;
+                    }
+
+                    last = i;
+                    i = entry.next;
+                    if (collisionCount >= entries.Length)
+                    {
+                        // The chain of entries forms a loop; which means a concurrent update has happened.
+                        // Break out of the loop and throw, rather than looping forever.
+                        ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported();
+                    }
+                    collisionCount++;
+                }
+            }
+            return false;
+        }
+
+        // This overload is a copy of the overload Remove(TKey key) with one additional
+        // statement to copy the value for entry being removed into the output parameter.
+        // Code has been intentionally duplicated for performance reasons.
+        public bool Remove(TKey key, out TValue value)
+        {
+            if (key == null)
+            {
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+            }
+
+            int[] buckets = _buckets;
+            Entry[] entries = _entries;
+            int collisionCount = 0;
+            if (buckets != null)
+            {
+                int hashCode = (_comparer?.GetHashCode(key) ?? key.GetHashCode()) & 0x7FFFFFFF;
+                int bucket = hashCode % buckets.Length;
+                int last = -1;
+                // Value in buckets is 1-based
+                int i = buckets[bucket] - 1;
+                while (i >= 0)
+                {
+                    ref Entry entry = ref entries[i];
+
+                    if (entry.hashCode == hashCode && (_comparer?.Equals(entry.key, key) ?? EqualityComparer<TKey>.Default.Equals(entry.key, key)))
+                    {
+                        if (last < 0)
+                        {
+                            // Value in buckets is 1-based
+                            buckets[bucket] = entry.next + 1;
+                        }
+                        else
+                        {
+                            entries[last].next = entry.next;
+                        }
+
+                        value = entry.value;
+
+                        entry.hashCode = -1;
+                        entry.next = _freeList;
+
+                        if (RuntimeHelpers.IsReferenceOrContainsReferences<TKey>())
+                        {
+                            entry.key = default;
+                        }
+                        if (RuntimeHelpers.IsReferenceOrContainsReferences<TValue>())
+                        {
+                            entry.value = default;
+                        }
+                        _freeList = i;
+                        _freeCount++;
+                        return true;
+                    }
+
+                    last = i;
+                    i = entry.next;
+                    if (collisionCount >= entries.Length)
+                    {
+                        // The chain of entries forms a loop; which means a concurrent update has happened.
+                        // Break out of the loop and throw, rather than looping forever.
+                        ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported();
+                    }
+                    collisionCount++;
+                }
+            }
+            value = default;
+            return false;
+        }
+
+        public bool TryGetValue(TKey key, out TValue value)
+        {
+            int i = FindEntry(key);
+            if (i >= 0)
+            {
+                value = _entries[i].value;
+                return true;
+            }
+            value = default;
+            return false;
+        }
+
+        public bool TryAdd(TKey key, TValue value)
+            => TryInsert(key, value, InsertionBehavior.None);
+
+        bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false;
+
+        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
+            => CopyTo(array, index);
+
+        /// <summary>
+        /// Ensures that the dictionary can hold up to 'capacity' entries without any further expansion of its backing storage
+        /// </summary>
+        public int EnsureCapacity(int capacity)
+        {
+            if (capacity < 0)
+                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
+            int currentCapacity = _entries == null ? 0 : _entries.Length;
+            if (currentCapacity >= capacity)
+                return currentCapacity;
+            _version++;
+            if (_buckets == null)
+                return Initialize(capacity);
+            int newSize = HashHelpers.GetPrime(capacity);
+            Resize(newSize, forceNewHashCodes: false);
+            return newSize;
+        }
+
+        /// <summary>
+        /// Sets the capacity of this dictionary to what it would be if it had been originally initialized with all its entries
+        /// 
+        /// This method can be used to minimize the memory overhead 
+        /// once it is known that no new elements will be added. 
+        /// 
+        /// To allocate minimum size storage array, execute the following statements:
+        /// 
+        /// dictionary.Clear();
+        /// dictionary.TrimExcess();
+        /// </summary>
+        public void TrimExcess()
+            => TrimExcess(Count);
+
+        /// <summary>
+        /// Sets the capacity of this dictionary to hold up 'capacity' entries without any further expansion of its backing storage
+        /// 
+        /// This method can be used to minimize the memory overhead 
+        /// once it is known that no new elements will be added. 
+        /// </summary>
+        public void TrimExcess(int capacity)
+        {
+            if (capacity < Count)
+                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
+            int newSize = HashHelpers.GetPrime(capacity);
+
+            Entry[] oldEntries = _entries;
+            int currentCapacity = oldEntries == null ? 0 : oldEntries.Length;
+            if (newSize >= currentCapacity)
+                return;
+
+            int oldCount = _count;
+            _version++;
+            Initialize(newSize);
+            Entry[] entries = _entries;
+            int[] buckets = _buckets;
+            int count = 0;
+            for (int i = 0; i < oldCount; i++)
+            {
+                int hashCode = oldEntries[i].hashCode;
+                if (hashCode >= 0)
+                {
+                    ref Entry entry = ref entries[count];
+                    entry = oldEntries[i];
+                    int bucket = hashCode % newSize;
+                    // Value in _buckets is 1-based
+                    entry.next = buckets[bucket] - 1;
+                    // Value in _buckets is 1-based
+                    buckets[bucket] = count + 1;
+                    count++;
+                }
+            }
+            _count = count;
+            _freeCount = 0;
+        }
+
+        private static bool IsCompatibleKey(object key)
+        {
+            if (key == null)
+            {
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+            }
+            return (key is TKey);
+        }
+
+        public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
+        {
+            private readonly Dictionary<TKey, TValue> _dictionary;
+            private readonly int _version;
+            private int _index;
+            private KeyValuePair<TKey, TValue> _current;
+            private readonly int _getEnumeratorRetType;  // What should Enumerator.Current return?
+
+            internal const int DictEntry = 1;
+            internal const int KeyValuePair = 2;
+
+            internal Enumerator(Dictionary<TKey, TValue> dictionary, int getEnumeratorRetType)
+            {
+                _dictionary = dictionary;
+                _version = dictionary._version;
+                _index = 0;
+                _getEnumeratorRetType = getEnumeratorRetType;
+                _current = new KeyValuePair<TKey, TValue>();
+            }
+
+            public bool MoveNext()
+            {
+                if (_version != _dictionary._version)
+                {
+                    ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
+                }
+
+                // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
+                // dictionary.count+1 could be negative if dictionary.count is int.MaxValue
+                while ((uint)_index < (uint)_dictionary._count)
+                {
+                    ref Entry entry = ref _dictionary._entries[_index++];
+
+                    if (entry.hashCode >= 0)
+                    {
+                        _current = new KeyValuePair<TKey, TValue>(entry.key, entry.value);
+                        return true;
+                    }
+                }
+
+                _index = _dictionary._count + 1;
+                _current = new KeyValuePair<TKey, TValue>();
+                return false;
+            }
+
+            public KeyValuePair<TKey, TValue> Current => _current;
+
+            public void Dispose()
+            {
+            }
+
+            public void Reset()
+            {
+                if (_version != _dictionary._version)
+                {
+                    ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
+                }
+
+                _index = 0;
+                _current = new KeyValuePair<TKey, TValue>();
+            }
+        }
+
+        [DebuggerTypeProxy(typeof(DictionaryKeyCollectionDebugView<,>))]
+        [DebuggerDisplay("Count = {Count}")]
+        public sealed class KeyCollection : ICollection<TKey>, IReadOnlyCollection<TKey>
+        {
+            private Dictionary<TKey, TValue> _dictionary;
+
+            public KeyCollection(Dictionary<TKey, TValue> dictionary)
+            {
+                if (dictionary == null)
+                {
+                    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
+                }
+                _dictionary = dictionary;
+            }
+
+            public Enumerator GetEnumerator()
+                => new Enumerator(_dictionary);
+
+            public void CopyTo(TKey[] array, int index)
+            {
+                if (array == null)
+                {
+                    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+                }
+
+                if (index < 0 || index > array.Length)
+                {
+                    ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
+                }
+
+                if (array.Length - index < _dictionary.Count)
+                {
+                    ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
+                }
+
+                int count = _dictionary._count;
+                Entry[] entries = _dictionary._entries;
+                for (int i = 0; i < count; i++)
+                {
+                    if (entries[i].hashCode >= 0) array[index++] = entries[i].key;
+                }
+            }
+
+            public int Count => _dictionary.Count;
+
+            bool ICollection<TKey>.IsReadOnly => true;
+
+            void ICollection<TKey>.Add(TKey item)
+                => ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet);
+
+            void ICollection<TKey>.Clear()
+                => ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet);
+
+            bool ICollection<TKey>.Contains(TKey item)
+                => _dictionary.ContainsKey(item);
+
+            bool ICollection<TKey>.Remove(TKey item)
+            {
+                ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet);
+                return false;
+            }
+
+            IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator()
+                => new Enumerator(_dictionary);
+
+            public struct Enumerator : IEnumerator<TKey>
+            {
+                private readonly Dictionary<TKey, TValue> _dictionary;
+                private int _index;
+                private readonly int _version;
+                private TKey _currentKey;
+
+                internal Enumerator(Dictionary<TKey, TValue> dictionary)
+                {
+                    _dictionary = dictionary;
+                    _version = dictionary._version;
+                    _index = 0;
+                    _currentKey = default;
+                }
+
+                public void Dispose()
+                {
+                }
+
+                public bool MoveNext()
+                {
+                    if (_version != _dictionary._version)
+                    {
+                        ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
+                    }
+
+                    while ((uint)_index < (uint)_dictionary._count)
+                    {
+                        ref Entry entry = ref _dictionary._entries[_index++];
+
+                        if (entry.hashCode >= 0)
+                        {
+                            _currentKey = entry.key;
+                            return true;
+                        }
+                    }
+
+                    _index = _dictionary._count + 1;
+                    _currentKey = default;
+                    return false;
+                }
+
+                public TKey Current => _currentKey;
+
+                public void Reset()
+                {
+                    if (_version != _dictionary._version)
+                    {
+                        ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
+                    }
+
+                    _index = 0;
+                    _currentKey = default;
+                }
+            }
+        }
+
+        [DebuggerTypeProxy(typeof(DictionaryValueCollectionDebugView<,>))]
+        [DebuggerDisplay("Count = {Count}")]
+        public sealed class ValueCollection : ICollection<TValue>, IReadOnlyCollection<TValue>
+        {
+            private Dictionary<TKey, TValue> _dictionary;
+
+            public ValueCollection(Dictionary<TKey, TValue> dictionary)
+            {
+                if (dictionary == null)
+                {
+                    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
+                }
+                _dictionary = dictionary;
+            }
+
+            public Enumerator GetEnumerator()
+                => new Enumerator(_dictionary);
+
+            public void CopyTo(TValue[] array, int index)
+            {
+                if (array == null)
+                {
+                    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+                }
+
+                if (index < 0 || index > array.Length)
+                {
+                    ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
+                }
+
+                if (array.Length - index < _dictionary.Count)
+                {
+                    ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
+                }
+
+                int count = _dictionary._count;
+                Entry[] entries = _dictionary._entries;
+                for (int i = 0; i < count; i++)
+                {
+                    if (entries[i].hashCode >= 0) array[index++] = entries[i].value;
+                }
+            }
+
+            public int Count => _dictionary.Count;
+
+            bool ICollection<TValue>.IsReadOnly => true;
+
+            void ICollection<TValue>.Add(TValue item)
+                => ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet);
+
+            bool ICollection<TValue>.Remove(TValue item)
+            {
+                ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet);
+                return false;
+            }
+
+            void ICollection<TValue>.Clear()
+                => ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet);
+
+            bool ICollection<TValue>.Contains(TValue item)
+                => _dictionary.ContainsValue(item);
+
+            IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator()
+                => new Enumerator(_dictionary);
+
+            public struct Enumerator : IEnumerator<TValue>
+            {
+                private readonly Dictionary<TKey, TValue> _dictionary;
+                private int _index;
+                private readonly int _version;
+                private TValue _currentValue;
+
+                internal Enumerator(Dictionary<TKey, TValue> dictionary)
+                {
+                    _dictionary = dictionary;
+                    _version = dictionary._version;
+                    _index = 0;
+                    _currentValue = default;
+                }
+
+                public void Dispose()
+                {
+                }
+
+                public bool MoveNext()
+                {
+                    if (_version != _dictionary._version)
+                    {
+                        ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
+                    }
+
+                    while ((uint)_index < (uint)_dictionary._count)
+                    {
+                        ref Entry entry = ref _dictionary._entries[_index++];
+
+                        if (entry.hashCode >= 0)
+                        {
+                            _currentValue = entry.value;
+                            return true;
+                        }
+                    }
+                    _index = _dictionary._count + 1;
+                    _currentValue = default;
+                    return false;
+                }
+
+                public TValue Current => _currentValue;
+
+                public void Reset()
+                {
+                    if (_version != _dictionary._version)
+                    {
+                        ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
+                    }
+                    _index = 0;
+                    _currentValue = default;
+                }
+            }
+        }
+    }
+}

+ 50 - 0
src/System.Private.CoreLib/System/Collections/Generic/IDictionary.cs

@@ -0,0 +1,50 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace System.Collections.Generic
+{
+    // An IDictionary is a possibly unordered set of key-value pairs.
+    // Keys can be any non-null object.  Values can be any object.
+    // You can look up a value in an IDictionary via the default indexed
+    // property, Items.
+    public interface IDictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>>
+    {
+        // Interfaces are not serializable
+        // The Item property provides methods to read and edit entries 
+        // in the Dictionary.
+        TValue this[TKey key]
+        {
+            get;
+            set;
+        }
+
+        // Returns a collections of the keys in this dictionary.
+        ICollection<TKey> Keys
+        {
+            get;
+        }
+
+        // Returns a collections of the values in this dictionary.
+        ICollection<TValue> Values
+        {
+            get;
+        }
+
+        // Returns whether this dictionary contains a particular key.
+        //
+        bool ContainsKey(TKey key);
+
+        // Adds a key-value pair to the dictionary.
+        // 
+        void Add(TKey key, TValue value);
+
+        // Removes a particular key from the dictionary.
+        //
+        bool Remove(TKey key);
+
+        bool TryGetValue(TKey key, out TValue value);
+    }
+}

+ 80 - 0
src/System.Private.CoreLib/System/Collections/Generic/IDictionaryDebugView.cs

@@ -0,0 +1,80 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+
+namespace System.Collections.Generic
+{
+    internal sealed class IDictionaryDebugView<K, V>
+    {
+        private readonly IDictionary<K, V> _dict;
+
+        public IDictionaryDebugView(IDictionary<K, V> dictionary)
+        {
+            if (dictionary == null)
+                throw new ArgumentNullException(nameof(dictionary));
+
+            _dict = dictionary;
+        }
+
+        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+        public KeyValuePair<K, V>[] Items
+        {
+            get
+            {
+                KeyValuePair<K, V>[] items = new KeyValuePair<K, V>[_dict.Count];
+                _dict.CopyTo(items, 0);
+                return items;
+            }
+        }
+    }
+
+    internal sealed class DictionaryKeyCollectionDebugView<TKey, TValue>
+    {
+        private readonly ICollection<TKey> _collection;
+
+        public DictionaryKeyCollectionDebugView(ICollection<TKey> collection)
+        {
+            if (collection == null)
+                throw new ArgumentNullException(nameof(collection));
+
+            _collection = collection;
+        }
+
+        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+        public TKey[] Items
+        {
+            get
+            {
+                TKey[] items = new TKey[_collection.Count];
+                _collection.CopyTo(items, 0);
+                return items;
+            }
+        }
+    }
+
+    internal sealed class DictionaryValueCollectionDebugView<TKey, TValue>
+    {
+        private readonly ICollection<TValue> _collection;
+
+        public DictionaryValueCollectionDebugView(ICollection<TValue> collection)
+        {
+            if (collection == null)
+                throw new ArgumentNullException(nameof(collection));
+
+            _collection = collection;
+        }
+
+        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+        public TValue[] Items
+        {
+            get
+            {
+                TValue[] items = new TValue[_collection.Count];
+                _collection.CopyTo(items, 0);
+                return items;
+            }
+        }
+    }
+}

+ 19 - 0
src/System.Private.CoreLib/System/Collections/Generic/IReadOnlyDictionary.cs

@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace System.Collections.Generic
+{
+    // Provides a read-only view of a generic dictionary.
+    public interface IReadOnlyDictionary<TKey, TValue> : IReadOnlyCollection<KeyValuePair<TKey, TValue>>
+    {
+        bool ContainsKey(TKey key);
+        bool TryGetValue(TKey key, out TValue value);
+
+        TValue this[TKey key] { get; }
+        IEnumerable<TKey> Keys { get; }
+        IEnumerable<TValue> Values { get; }
+    }
+}

+ 83 - 0
src/System.Private.CoreLib/System/Collections/Generic/KeyValuePair.cs

@@ -0,0 +1,83 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.ComponentModel;
+using System.Text;
+
+namespace System.Collections.Generic
+{
+    // Provides the Create factory method for KeyValuePair<TKey, TValue>.
+    public static class KeyValuePair
+    {
+        // Creates a new KeyValuePair<TKey, TValue> from the given values.
+        public static KeyValuePair<TKey, TValue> Create<TKey, TValue>(TKey key, TValue value)
+        {
+            return new KeyValuePair<TKey, TValue>(key, value);
+        }
+
+        /// <summary>
+        /// Used by KeyValuePair.ToString to reduce generic code
+        /// </summary>
+        internal static string PairToString(object key, object value)
+        {
+            StringBuilder s = StringBuilderCache.Acquire();
+            s.Append('[');
+
+            if (key != null)
+            {
+                s.Append(key);
+            }
+
+            s.Append(", ");
+
+            if (value != null)
+            {
+                s.Append(value);
+            }
+
+            s.Append(']');
+
+            return StringBuilderCache.GetStringAndRelease(s);
+        }
+    }
+
+    // A KeyValuePair holds a key and a value from a dictionary.
+    // It is used by the IEnumerable<T> implementation for both IDictionary<TKey, TValue>
+    // and IReadOnlyDictionary<TKey, TValue>.
+    [Serializable]
+    [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+    public readonly struct KeyValuePair<TKey, TValue>
+    {
+        private readonly TKey key; // Do not rename (binary serialization)
+        private readonly TValue value; // Do not rename (binary serialization)
+
+        public KeyValuePair(TKey key, TValue value)
+        {
+            this.key = key;
+            this.value = value;
+        }
+
+        public TKey Key
+        {
+            get { return key; }
+        }
+
+        public TValue Value
+        {
+            get { return value; }
+        }
+
+        public override string ToString()
+        {
+            return KeyValuePair.PairToString(Key, Value);
+        }
+
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Deconstruct(out TKey key, out TValue value)
+        {
+            key = Key;
+            value = Value;
+        }
+    }
+}

+ 25 - 0
src/System.Private.CoreLib/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs

@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.Serialization;
+
+namespace System.Collections.Generic
+{
+    // NonRandomizedStringEqualityComparer is the comparer used by default with the Dictionary<string,...> 
+    // We use NonRandomizedStringEqualityComparer as default comparer as it doesnt use the randomized string hashing which 
+    // keeps the performance not affected till we hit collision threshold and then we switch to the comparer which is using 
+    // randomized string hashing.
+    [Serializable] // Required for compatibility with .NET Core 2.0 as we exposed the NonRandomizedStringEqualityComparer inside the serialization blob
+    // Needs to be public to support binary serialization compatibility
+    public sealed class NonRandomizedStringEqualityComparer : EqualityComparer<string>
+    {
+        internal static new IEqualityComparer<string> Default { get; } = new NonRandomizedStringEqualityComparer();
+
+        private NonRandomizedStringEqualityComparer() { }
+
+        public sealed override bool Equals(string x, string y) => string.Equals(x, y);
+
+        public sealed override int GetHashCode(string obj) => obj?.GetNonRandomizedHashCode() ?? 0;
+    }
+} 

+ 92 - 0
src/System.Private.CoreLib/System/Collections/HashHelpers.cs

@@ -0,0 +1,92 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+
+using System.Diagnostics;
+
+namespace System.Collections
+{
+    internal static partial class HashHelpers
+    {
+        public const int HashCollisionThreshold = 100;
+
+        // This is the maximum prime smaller than Array.MaxArrayLength
+        public const int MaxPrimeArrayLength = 0x7FEFFFFD;
+
+        public const int HashPrime = 101;
+
+        // Table of prime numbers to use as hash table sizes. 
+        // A typical resize algorithm would pick the smallest prime number in this array
+        // that is larger than twice the previous capacity. 
+        // Suppose our Hashtable currently has capacity x and enough elements are added 
+        // such that a resize needs to occur. Resizing first computes 2x then finds the 
+        // first prime in the table greater than 2x, i.e. if primes are ordered 
+        // p_1, p_2, ..., p_i, ..., it finds p_n such that p_n-1 < 2x < p_n. 
+        // Doubling is important for preserving the asymptotic complexity of the 
+        // hashtable operations such as add.  Having a prime guarantees that double 
+        // hashing does not lead to infinite loops.  IE, your hash function will be 
+        // h1(key) + i*h2(key), 0 <= i < size.  h2 and the size must be relatively prime.
+        // We prefer the low computation costs of higher prime numbers over the increased
+        // memory allocation of a fixed prime number i.e. when right sizing a HashSet.
+        public static readonly int[] primes = {
+            3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
+            1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
+            17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,
+            187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
+            1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369 };
+
+        public static bool IsPrime(int candidate)
+        {
+            if ((candidate & 1) != 0)
+            {
+                int limit = (int)Math.Sqrt(candidate);
+                for (int divisor = 3; divisor <= limit; divisor += 2)
+                {
+                    if ((candidate % divisor) == 0)
+                        return false;
+                }
+                return true;
+            }
+            return (candidate == 2);
+        }
+
+        public static int GetPrime(int min)
+        {
+            if (min < 0)
+                throw new ArgumentException(SR.Arg_HTCapacityOverflow);
+
+            for (int i = 0; i < primes.Length; i++)
+            {
+                int prime = primes[i];
+                if (prime >= min)
+                    return prime;
+            }
+
+            //outside of our predefined table. 
+            //compute the hard way. 
+            for (int i = (min | 1); i < int.MaxValue; i += 2)
+            {
+                if (IsPrime(i) && ((i - 1) % HashPrime != 0))
+                    return i;
+            }
+            return min;
+        }
+
+        // Returns size of hashtable to grow to.
+        public static int ExpandPrime(int oldSize)
+        {
+            int newSize = 2 * oldSize;
+
+            // Allow the hashtables to grow to maximum possible size (~2G elements) before encountering capacity overflow.
+            // Note that this check works even when _items.Length overflowed thanks to the (uint) cast
+            if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
+            {
+                Debug.Assert(MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
+                return MaxPrimeArrayLength;
+            }
+
+            return GetPrime(newSize);
+        }
+    }
+}

+ 1 - 1
src/System.Private.CoreLib/System/SZArray.cs

@@ -4,6 +4,6 @@ namespace System
 {
     internal class SZArray<T> : Array
     {
-        public uint Length;
+        public IntPtr Length;
     }
 }

+ 2 - 0
src/System.Runtime/TypeForwards.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 using System.Text;
 using System.Threading;
@@ -60,6 +61,7 @@ using System.Threading;
 [assembly: TypeForwardedTo(typeof(ValueTuple<,,,,,>))]
 [assembly: TypeForwardedTo(typeof(ValueTuple<,,,,,,>))]
 [assembly: TypeForwardedTo(typeof(ValueTuple<,,,,,,,>))]
+[assembly: TypeForwardedTo(typeof(KeyValuePair<,>))]
 [assembly: TypeForwardedTo(typeof(Span<>))]
 [assembly: TypeForwardedTo(typeof(ReadOnlySpan<>))]
 [assembly: TypeForwardedTo(typeof(TimeSpan))]