ソースを参照

Use JsValue for property access (#701)

* specialize and optimize global environment record
* optimize RegExp
* object class should be internal concept and an enum
* try to skip virtual method call for Clone when possible
* withEnvironment is implicitly false for GlobalEnvironmentRecord and true for ObjectEnvironmentRecord
Marko Lahma 5 年 前
コミット
5dbdb5967c
100 ファイル変更2373 行追加1548 行削除
  1. 1 1
      Jint.Benchmark/DictionaryBenchmark.cs
  2. 15 0
      Jint.Tests.Test262/GlobalTests.cs
  3. 7 7
      Jint.Tests/Runtime/EngineTests.cs
  4. 1 1
      Jint.Tests/Runtime/ObjectInstanceTests.cs
  5. 298 0
      Jint/Collections/DictionarySlim.cs
  6. 42 36
      Jint/Collections/HybridDictionary.cs
  7. 67 63
      Jint/Collections/ListDictionary.cs
  8. 15 0
      Jint/Collections/PropertyDictionary.cs
  9. 9 11
      Jint/Collections/StringDictionarySlim.cs
  10. 16 0
      Jint/Collections/SymbolDictionary.cs
  11. 70 43
      Jint/Engine.cs
  12. 7 6
      Jint/EsprimaExtensions.cs
  13. 1 12
      Jint/JsValueExtensions.cs
  14. 12 74
      Jint/Key.cs
  15. 35 38
      Jint/Native/Argument/ArgumentsInstance.cs
  16. 14 9
      Jint/Native/Array/ArrayConstructor.cs
  17. 90 53
      Jint/Native/Array/ArrayInstance.cs
  18. 11 11
      Jint/Native/Array/ArrayOperations.cs
  19. 30 26
      Jint/Native/Array/ArrayPrototype.cs
  20. 1 1
      Jint/Native/Boolean/BooleanInstance.cs
  21. 4 4
      Jint/Native/Boolean/BooleanPrototype.cs
  22. 4 3
      Jint/Native/Date/DateConstructor.cs
  23. 1 1
      Jint/Native/Date/DateInstance.cs
  24. 13 8
      Jint/Native/Date/DatePrototype.cs
  25. 4 4
      Jint/Native/Error/ErrorInstance.cs
  26. 4 4
      Jint/Native/Error/ErrorPrototype.cs
  27. 10 10
      Jint/Native/Function/ArrowFunctionInstance.cs
  28. 2 2
      Jint/Native/Function/FunctionConstructor.cs
  29. 59 62
      Jint/Native/Function/FunctionInstance.cs
  30. 8 8
      Jint/Native/Function/FunctionPrototype.cs
  31. 17 17
      Jint/Native/Function/ScriptFunctionInstance.cs
  32. 140 44
      Jint/Native/Global/GlobalObject.cs
  33. 12 14
      Jint/Native/Iterator/IteratorInstance.cs
  34. 3 2
      Jint/Native/Iterator/IteratorProtocol.cs
  35. 8 3
      Jint/Native/Iterator/IteratorPrototype.cs
  36. 1 1
      Jint/Native/JsBoolean.cs
  37. 5 3
      Jint/Native/JsNumber.cs
  38. 136 19
      Jint/Native/JsString.cs
  39. 6 11
      Jint/Native/JsSymbol.cs
  40. 37 50
      Jint/Native/JsValue.cs
  41. 12 17
      Jint/Native/Json/JsonInstance.cs
  42. 1 1
      Jint/Native/Json/JsonParser.cs
  43. 19 19
      Jint/Native/Json/JsonSerializer.cs
  44. 5 5
      Jint/Native/Map/MapConstructor.cs
  45. 16 16
      Jint/Native/Map/MapInstance.cs
  46. 14 10
      Jint/Native/Map/MapPrototype.cs
  47. 10 6
      Jint/Native/Math/MathInstance.cs
  48. 5 5
      Jint/Native/Number/NumberConstructor.cs
  49. 1 1
      Jint/Native/Number/NumberInstance.cs
  50. 5 6
      Jint/Native/Number/NumberPrototype.cs
  51. 24 0
      Jint/Native/Object/ObjectClass.cs
  52. 20 20
      Jint/Native/Object/ObjectConstructor.cs
  53. 211 163
      Jint/Native/Object/ObjectInstance.cs
  54. 4 5
      Jint/Native/Object/ObjectPrototype.cs
  55. 7 6
      Jint/Native/Proxy/ProxyConstructor.cs
  56. 57 58
      Jint/Native/Proxy/ProxyInstance.cs
  57. 3 4
      Jint/Native/Reflect/ReflectInstance.cs
  58. 6 17
      Jint/Native/RegExp/RegExpConstructor.cs
  59. 2 1
      Jint/Native/RegExp/RegExpExtensions.cs
  60. 23 19
      Jint/Native/RegExp/RegExpInstance.cs
  61. 172 49
      Jint/Native/RegExp/RegExpPrototype.cs
  62. 3 3
      Jint/Native/Set/SetConstructor.cs
  63. 14 14
      Jint/Native/Set/SetInstance.cs
  64. 12 8
      Jint/Native/Set/SetPrototype.cs
  65. 4 4
      Jint/Native/String/StringConstructor.cs
  66. 28 27
      Jint/Native/String/StringInstance.cs
  67. 59 107
      Jint/Native/String/StringPrototype.cs
  68. 20 51
      Jint/Native/Symbol/GlobalSymbolRegistry.cs
  69. 6 7
      Jint/Native/Symbol/SymbolConstructor.cs
  70. 1 1
      Jint/Native/Symbol/SymbolInstance.cs
  71. 12 8
      Jint/Native/Symbol/SymbolPrototype.cs
  72. 2 2
      Jint/Pooling/ReferencePool.cs
  73. 1 1
      Jint/Runtime/CallStack/CallStackElementComparer.cs
  74. 27 0
      Jint/Runtime/CommonProperties.cs
  75. 9 10
      Jint/Runtime/Descriptors/PropertyDescriptor.cs
  76. 5 2
      Jint/Runtime/Descriptors/PropertyFlag.cs
  77. 3 3
      Jint/Runtime/Descriptors/Specialized/ClrAccessDescriptor.cs
  78. 8 3
      Jint/Runtime/Environments/Binding.cs
  79. 27 20
      Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs
  80. 6 6
      Jint/Runtime/Environments/EnvironmentRecord.cs
  81. 95 0
      Jint/Runtime/Environments/GlobalEnvironmentRecord.cs
  82. 15 27
      Jint/Runtime/Environments/LexicalEnvironment.cs
  83. 24 25
      Jint/Runtime/Environments/ObjectEnvironmentRecord.cs
  84. 1 1
      Jint/Runtime/ExceptionHelper.cs
  85. 1 1
      Jint/Runtime/Interop/MethodInfoFunctionInstance.cs
  86. 6 6
      Jint/Runtime/Interop/NamespaceReference.cs
  87. 9 9
      Jint/Runtime/Interop/ObjectWrapper.cs
  88. 14 12
      Jint/Runtime/Interop/TypeReference.cs
  89. 6 5
      Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs
  90. 1 2
      Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs
  91. 38 25
      Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs
  92. 7 6
      Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs
  93. 12 13
      Jint/Runtime/Interpreter/Expressions/JintExpression.cs
  94. 13 4
      Jint/Runtime/Interpreter/Expressions/JintIdentifierExpression.cs
  95. 23 15
      Jint/Runtime/Interpreter/Expressions/JintMemberExpression.cs
  96. 12 15
      Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs
  97. 7 5
      Jint/Runtime/Interpreter/Expressions/JintTaggedTemplateExpression.cs
  98. 5 5
      Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs
  99. 2 3
      Jint/Runtime/Interpreter/Expressions/JintUpdateExpression.cs
  100. 2 2
      Jint/Runtime/Interpreter/Statements/JintForInStatement.cs

+ 1 - 1
Jint.Benchmark/DictionaryBenchmark.cs

@@ -29,7 +29,7 @@ namespace Jint.Benchmark
         [Benchmark]
         public void HybridDictionary()
         {
-            var hybridDictionary = new HybridDictionary<string, object>();
+            var hybridDictionary = new HybridDictionary<object>();
             for (var i = 0; i < N; i++)
             {
                 hybridDictionary.Add(_keys[i], _keys);

+ 15 - 0
Jint.Tests.Test262/GlobalTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262
+{
+    public class GlobalTests : Test262Test
+    {
+        [Theory(DisplayName = "built-ins\\global")]
+        [MemberData(nameof(SourceFiles), "built-ins\\global", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\global", true, Skip = "Skipped")]
+        protected void RunTest(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 7 - 7
Jint.Tests/Runtime/EngineTests.cs

@@ -379,8 +379,8 @@ namespace Jint.Tests.Runtime
                 var n = 8;
                 var o = Array(n);
                 for (var i = 0; i < n; i++) o[i] = i;
-                assert(o[0] == 0);
-                assert(o[7] == 7);
+                equal(0, o[0]);
+                equal(7, o[7]);
             ");
         }
 
@@ -391,8 +391,8 @@ namespace Jint.Tests.Runtime
                 var n = 1024*10+2;
                 var o = Array(n);
                 for (var i = 0; i < n; i++) o[i] = i;
-                assert(o[0] == 0);
-                assert(o[n - 1] == n -1);
+                equal(0, o[0]);
+                equal(n -1, o[n - 1]);
             ");
         }
 
@@ -1834,9 +1834,9 @@ var prep = function (fn) { fn(); };
                   , key
                   , i = 0;
                 target[key = keys[i++]] = source[key];
-                assert(i == 1);
-                assert(key == 'a');
-                assert(target[key] == 3);
+                equal(1, i);
+                equal('a', key);
+                equal(3, target[key]);
             ");
         }
 

+ 1 - 1
Jint.Tests/Runtime/ObjectInstanceTests.cs

@@ -16,7 +16,7 @@ namespace Jint.Tests.Runtime
             instance.FastAddProperty("scope", JsValue.Null, true, true, true);
             instance.RemoveOwnProperty("bare");
             var propertyNames = instance.GetOwnProperties().Select(x => x.Key).ToList();
-            Assert.Equal(new Key[] { "scope" }, propertyNames);
+            Assert.Equal(new JsValue[] { "scope" }, propertyNames);
         }
     }
 }

+ 298 - 0
Jint/Collections/DictionarySlim.cs

@@ -0,0 +1,298 @@
+// 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;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace Jint.Collections
+{
+    /// <summary>
+    /// DictionarySlim<string, TValue> is similar to Dictionary<TKey, TValue> but optimized in three ways:
+    /// 1) It allows access to the value by ref replacing the common TryGetValue and Add pattern.
+    /// 2) It does not store the hash code (assumes it is cheap to equate values).
+    /// 3) It does not accept an equality comparer (assumes Object.GetHashCode() and Object.Equals() or overridden implementation are cheap and sufficient).
+    /// </summary>
+    [DebuggerDisplay("Count = {Count}")]
+    internal class DictionarySlim<TKey, TValue> : IReadOnlyCollection<KeyValuePair<TKey, TValue>> where TKey : IEquatable<TKey>
+    {
+        // We want to initialize without allocating arrays. We also want to avoid null checks.
+        // Array.Empty would give divide by zero in modulo operation. So we use static one element arrays.
+        // The first add will cause a resize replacing these with real arrays of three elements.
+        // Arrays are wrapped in a class to avoid being duplicated for each <TKey, TValue>
+        private static readonly Entry[] InitialEntries = new Entry[1];
+        private int _count;
+        // 0-based index into _entries of head of free chain: -1 means empty
+        private int _freeList = -1;
+        // 1-based index into _entries; 0 means empty
+        private int[] _buckets;
+        private Entry[] _entries;
+
+        [DebuggerDisplay("({key}, {value})->{next}")]
+        private struct Entry
+        {
+            public TKey key;
+            public TValue value;
+            // 0-based index of next entry in chain: -1 means end of chain
+            // also encodes whether this entry _itself_ is part of the free list by changing sign and subtracting 3,
+            // so -2 means end of free list, -3 means index 0 but on free list, -4 means index 1 but on free list, etc.
+            public int next;
+        }
+
+        public DictionarySlim()
+        {
+            _buckets = HashHelpers.SizeOneIntArray;
+            _entries = InitialEntries;
+        }
+
+        public DictionarySlim(int capacity)
+        {
+            if (capacity < 2)
+                capacity = 2; // 1 would indicate the dummy array
+            capacity = HashHelpers.PowerOf2(capacity);
+            _buckets = new int[capacity];
+            _entries = new Entry[capacity];
+        }
+
+        public int Count => _count;
+
+        /// <summary>
+        /// Clears the dictionary. Note that this invalidates any active enumerators.
+        /// </summary>
+        public void Clear()
+        {
+            _count = 0;
+            _freeList = -1;
+            _buckets = HashHelpers.SizeOneIntArray;
+            _entries = InitialEntries;
+        }
+
+        public bool ContainsKey(TKey key)
+        {
+            Entry[] entries = _entries;
+            for (int i = _buckets[key.GetHashCode() & (_buckets.Length-1)] - 1;
+                (uint)i < (uint)entries.Length; i = entries[i].next)
+            {
+                if (key.Equals(entries[i].key))
+                    return true;
+            }
+
+            return false;
+        }
+
+        public bool TryGetValue(TKey key, out TValue value)
+        {
+            Entry[] entries = _entries;
+            for (int i = _buckets[key.GetHashCode() & (_buckets.Length - 1)] - 1;
+                (uint)i < (uint)entries.Length; i = entries[i].next)
+            {
+                if (key.Equals(entries[i].key))
+                {
+                    value = entries[i].value;
+                    return true;
+                }
+            }
+
+            value = default;
+            return false;
+        }
+
+        public bool Remove(TKey key)
+        {
+            Entry[] entries = _entries;
+            int bucketIndex = key.GetHashCode() & (_buckets.Length - 1);
+            int entryIndex = _buckets[bucketIndex] - 1;
+
+            int lastIndex = -1;
+            while (entryIndex != -1)
+            {
+                Entry candidate = entries[entryIndex];
+                if (candidate.key.Equals(key))
+                {
+                    if (lastIndex != -1)
+                    {   // Fixup preceding element in chain to point to next (if any)
+                        entries[lastIndex].next = candidate.next;
+                    }
+                    else
+                    {   // Fixup bucket to new head (if any)
+                        _buckets[bucketIndex] = candidate.next + 1;
+                    }
+
+                    entries[entryIndex] = default;
+
+                    entries[entryIndex].next = -3 - _freeList; // New head of free list
+                    _freeList = entryIndex;
+
+                    _count--;
+                    return true;
+                }
+                lastIndex = entryIndex;
+                entryIndex = candidate.next;
+            }
+
+            return false;
+        }
+
+       // Not safe for concurrent _reads_ (at least, if either of them add)
+        // For concurrent reads, prefer TryGetValue(key, out value)
+        /// <summary>
+        /// Gets the value for the specified key, or, if the key is not present,
+        /// adds an entry and returns the value by ref. This makes it possible to
+        /// add or update a value in a single look up operation.
+        /// </summary>
+        /// <param name="key">Key to look for</param>
+        /// <returns>Reference to the new or existing value</returns>
+        public ref TValue GetOrAddValueRef(TKey key)
+        {
+            Entry[] entries = _entries;
+            int bucketIndex = key.GetHashCode() & (_buckets.Length - 1);
+            for (int i = _buckets[bucketIndex] - 1;
+                    (uint)i < (uint)entries.Length; i = entries[i].next)
+            {
+                if (key.Equals(entries[i].key))
+                    return ref entries[i].value;
+            }
+
+            return ref AddKey(key, bucketIndex);
+        }
+
+        public ref TValue this[TKey key]
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => ref GetOrAddValueRef(key);
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private ref TValue AddKey(TKey key, int bucketIndex)
+        {
+            Entry[] entries = _entries;
+            int entryIndex;
+            if (_freeList != -1)
+            {
+                entryIndex = _freeList;
+                _freeList = -3 - entries[_freeList].next;
+            }
+            else
+            {
+                if (_count == entries.Length || entries.Length == 1)
+                {
+                    entries = Resize();
+                    bucketIndex = key.GetHashCode() & (_buckets.Length - 1);
+                    // entry indexes were not changed by Resize
+                }
+                entryIndex = _count;
+            }
+
+            entries[entryIndex].key = key;
+            entries[entryIndex].next = _buckets[bucketIndex] - 1;
+            _buckets[bucketIndex] = entryIndex + 1;
+            _count++;
+            return ref entries[entryIndex].value;
+        }
+
+        private Entry[] Resize()
+        {
+            Debug.Assert(_entries.Length == _count || _entries.Length == 1); // We only copy _count, so if it's longer we will miss some
+            int count = _count;
+            int newSize = _entries.Length * 2;
+            if ((uint)newSize > (uint)int.MaxValue) // uint cast handles overflow
+                throw new InvalidOperationException("Capacity Overflow");
+
+            var entries = new Entry[newSize];
+            Array.Copy(_entries, 0, entries, 0, count);
+
+            var newBuckets = new int[entries.Length];
+            while (count-- > 0)
+            {
+                int bucketIndex = entries[count].key.GetHashCode() & (newBuckets.Length - 1);
+                entries[count].next = newBuckets[bucketIndex] - 1;
+                newBuckets[bucketIndex] = count + 1;
+            }
+
+            _buckets = newBuckets;
+            _entries = entries;
+
+            return entries;
+        }
+
+        /// <summary>
+        /// Gets an enumerator over the dictionary
+        /// </summary>
+        public Enumerator GetEnumerator() => new Enumerator(this); // avoid boxing
+
+        /// <summary>
+        /// Gets an enumerator over the dictionary
+        /// </summary>
+        IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() =>
+            new Enumerator(this);
+
+        /// <summary>
+        /// Gets an enumerator over the dictionary
+        /// </summary>
+        IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
+
+        public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
+        {
+            private readonly DictionarySlim<TKey, TValue> _dictionary;
+            private int _index;
+            private int _count;
+            private KeyValuePair<TKey, TValue> _current;
+
+            internal Enumerator(DictionarySlim<TKey, TValue> dictionary)
+            {
+                _dictionary = dictionary;
+                _index = 0;
+                _count = _dictionary._count;
+                _current = default;
+            }
+
+            public bool MoveNext()
+            {
+                if (_count == 0)
+                {
+                    _current = default;
+                    return false;
+                }
+
+                _count--;
+
+                while (_dictionary._entries[_index].next < -1)
+                    _index++;
+
+                _current = new KeyValuePair<TKey, TValue>(
+                    _dictionary._entries[_index].key,
+                    _dictionary._entries[_index++].value);
+                return true;
+            }
+
+            public KeyValuePair<TKey, TValue> Current => _current;
+
+            object IEnumerator.Current => _current;
+
+            void IEnumerator.Reset()
+            {
+                _index = 0;
+                _count = _dictionary._count;
+                _current = default;
+            }
+
+            public void Dispose() { }
+        }
+
+        internal static class HashHelpers
+        {
+            internal static readonly int[] SizeOneIntArray = new int[1];
+
+            internal static int PowerOf2(int v)
+            {
+                if ((v & (v - 1)) == 0) return v;
+                int i = 2;
+                while (i < v) i <<= 1;
+                return i;
+            }
+        }
+    }
+}

+ 42 - 36
Jint/Collections/HybridDictionary.cs

@@ -1,33 +1,34 @@
-using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
+using System.Runtime.CompilerServices;
 
 namespace Jint.Collections
 {
-    internal sealed class HybridDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>> where TKey : IEquatable<TKey>
+    internal class HybridDictionary<TValue> : IEnumerable<KeyValuePair<Key, TValue>>
     {
         private const int CutoverPoint = 9;
         private const int InitialDictionarySize = 13;
         private const int FixedSizeCutoverPoint = 6;
 
-        // Instance variables. This keeps the HybridDictionary very light-weight when empty
-        private ListDictionary<TKey, TValue> _list;
-        private Dictionary<TKey, TValue> _dictionary;
+        private readonly bool _checkExistingKeys;
+        private ListDictionary<TValue> _list;
+        private StringDictionarySlim<TValue> _dictionary;
 
-        public HybridDictionary()
+        public HybridDictionary() : this(0, checkExistingKeys: true)
         {
         }
 
-        public HybridDictionary(int initialSize)
+        public HybridDictionary(int initialSize, bool checkExistingKeys)
         {
+            _checkExistingKeys = checkExistingKeys;
             if (initialSize >= FixedSizeCutoverPoint)
             {
-                _dictionary = new Dictionary<TKey, TValue>(initialSize);
+                _dictionary = new StringDictionarySlim<TValue>(initialSize);
             }
         }
 
-        public TValue this[TKey key]
+        public TValue this[in Key key]
         {
             get
             {
@@ -44,8 +45,7 @@ namespace Jint.Collections
                 {
                     if (_list.Count >= CutoverPoint - 1)
                     {
-                        SwitchToDictionary();
-                        _dictionary[key] = value;
+                        SwitchToDictionary(key, value);
                     }
                     else
                     {
@@ -54,13 +54,12 @@ namespace Jint.Collections
                 }
                 else
                 {
-                    _list = new ListDictionary<TKey, TValue>();
-                    _list[key] = value;
+                    _list = new ListDictionary<TValue>(key, value, _checkExistingKeys);
                 }
             }
         }
 
-        public bool TryGetValue(TKey key, out TValue value)
+        public bool TryGetValue(in Key key, out TValue value)
         {
             if (_dictionary != null)
             {
@@ -76,49 +75,42 @@ namespace Jint.Collections
             return false;
         }
 
-        private void SwitchToDictionary()
+        private void SwitchToDictionary(in Key key, TValue value)
         {
-            var dictionary = new Dictionary<TKey, TValue>(InitialDictionarySize);
+            var dictionary = new StringDictionarySlim<TValue>(InitialDictionarySize);
             foreach (var pair in _list)
             {
                 dictionary[pair.Key] = pair.Value;
             }
+
+            dictionary[key] = value;
             _dictionary = dictionary;
             _list = null;
         }
 
         public int Count
         {
-            get
-            {
-                if (_dictionary != null)
-                {
-                    return _dictionary.Count;
-                }
-
-                return _list?.Count ?? 0;
-            }
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => _dictionary?.Count ?? _list?.Count ?? 0;
         }
 
-        public void Add(TKey key, TValue value)
+        public void Add(in Key key, TValue value)
         {
             if (_dictionary != null)
             {
-                _dictionary.Add(key, value);
+                _dictionary.GetOrAddValueRef(key) = value;
             }
             else
             {
                 if (_list == null)
                 {
-                    _list = new ListDictionary<TKey, TValue>();
-                    _list.Add(key, value);
+                    _list = new ListDictionary<TValue>(key, value, _checkExistingKeys);
                 }
                 else
                 {
                     if (_list.Count + 1 >= CutoverPoint)
                     {
-                        SwitchToDictionary();
-                        _dictionary.Add(key, value);
+                        SwitchToDictionary(key, value);
                     }
                     else
                     {
@@ -145,7 +137,7 @@ namespace Jint.Collections
             }
         }
 
-        public bool ContainsKey(TKey key)
+        public bool ContainsKey(in Key key)
         {
             if (_dictionary != null)
             {
@@ -161,7 +153,7 @@ namespace Jint.Collections
             return false;
         }
 
-        IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
+        IEnumerator<KeyValuePair<Key, TValue>> IEnumerable<KeyValuePair<Key, TValue>>.GetEnumerator()
         {
             if (_dictionary != null)
             {
@@ -173,7 +165,7 @@ namespace Jint.Collections
                 return _list.GetEnumerator();
             }
 
-            return Enumerable.Empty<KeyValuePair<TKey, TValue>>().GetEnumerator();
+            return Enumerable.Empty<KeyValuePair<Key, TValue>>().GetEnumerator();
 
         }
 
@@ -189,10 +181,10 @@ namespace Jint.Collections
                 return _list.GetEnumerator();
             }
 
-            return Enumerable.Empty<KeyValuePair<TKey, TValue>>().GetEnumerator();
+            return Enumerable.Empty<KeyValuePair<Key, TValue>>().GetEnumerator();
         }
 
-        public bool Remove(TKey key)
+        public bool Remove(in Key key)
         {
             if (_dictionary != null)
             {
@@ -201,5 +193,19 @@ namespace Jint.Collections
 
             return _list != null && _list.Remove(key);
         }
+        
+        /// <summary>
+        /// Optimization when no need to check for existing items.
+        /// </summary>
+        public bool CheckExistingKeys
+        {
+            set
+            {
+                if (_list != null)
+                {
+                    _list.CheckExistingKeys = value;
+                }
+            }
+        }
     }
 }

+ 67 - 63
Jint/Collections/ListDictionary.cs

@@ -1,4 +1,3 @@
-using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Runtime.CompilerServices;
@@ -6,16 +5,24 @@ using Jint.Runtime;
 
 namespace Jint.Collections
 {
-    internal sealed class ListDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>> where TKey : IEquatable<TKey>
+    internal sealed class ListDictionary<TValue> : IEnumerable<KeyValuePair<Key, TValue>>
     {
-        private DictionaryNode head;
-        private int count;
+        private DictionaryNode _head;
+        private int _count;
+        private bool _checkExistingKeys;
 
-        public ListDictionary()
+        public ListDictionary(in Key key, TValue value, bool checkExistingKeys)
         {
+            _checkExistingKeys = checkExistingKeys;
+            _head = new DictionaryNode
+            {
+                Key = key, 
+                Value = value
+            };
+            _count = 1;
         }
 
-        public TValue this[TKey key]
+        public TValue this[in Key key]
         {
             get
             {
@@ -26,10 +33,11 @@ namespace Jint.Collections
             {
                 DictionaryNode last = null;
                 DictionaryNode node;
-                for (node = head; node != null; node = node.Next)
+                var checkExistingKeys = _checkExistingKeys;
+                for (node = _head; node != null; node = node.Next)
                 {
                     var oldKey = node.Key;
-                    if (oldKey.Equals(key))
+                    if (checkExistingKeys && oldKey == key)
                     {
                         break;
                     }
@@ -44,29 +52,16 @@ namespace Jint.Collections
                     return;
                 }
 
-                // Not found, so add a new one
-                DictionaryNode newNode = new DictionaryNode();
-                newNode.Key = key;
-                newNode.Value = value;
-                if (last != null)
-                {
-                    last.Next = newNode;
-                }
-                else
-                {
-                    head = newNode;
-                }
-
-                count++;
+                AddNode(key, value, last);
             }
         }
 
-        public bool TryGetValue(TKey key, out TValue value)
+        public bool TryGetValue(in Key key, out TValue value)
         {
-            var node = head;
+            var node = _head;
             while (node != null)
             {
-                if (node.Key.Equals(key))
+                if (node.Key == key)
                 {
                     value = node.Value;
                     return true;
@@ -79,16 +74,21 @@ namespace Jint.Collections
             return false;
         }
 
-        public int Count => count;
+        public int Count
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => _count;
+        }
 
-        public void Add(TKey key, TValue value)
+        public void Add(in Key key, TValue value)
         {
             DictionaryNode last = null;
             DictionaryNode node;
-            for (node = head; node != null; node = node.Next)
+            var checkExistingKeys = _checkExistingKeys;
+            for (node = _head; node != null; node = node.Next)
             {
                 var oldKey = node.Key;
-                if (oldKey.Equals(key))
+                if (checkExistingKeys && oldKey == key)
                 {
                     ExceptionHelper.ThrowArgumentException();
                 }
@@ -96,34 +96,39 @@ namespace Jint.Collections
                 last = node;
             }
 
-            // Not found, so add a new one
-            DictionaryNode newNode = new DictionaryNode();
-            newNode.Key = key;
-            newNode.Value = value;
-            if (last != null)
+            AddNode(key, value, last);
+        }
+
+        private void AddNode(in Key key, TValue value, DictionaryNode last)
+        {
+            var newNode = new DictionaryNode
             {
-                last.Next = newNode;
+                Key = key,
+                Value = value
+            };
+            if (_head is null)
+            {
+                _head = newNode;
             }
             else
             {
-                head = newNode;
+                last.Next = newNode;
             }
-
-            count++;
+            _count++;
         }
 
         public void Clear()
         {
-            count = 0;
-            head = null;
+            _count = 0;
+            _head = null;
         }
 
-        public bool ContainsKey(TKey key)
+        public bool ContainsKey(in Key key)
         {
-            for (var node = head; node != null; node = node.Next)
+            for (var node = _head; node != null; node = node.Next)
             {
                 var oldKey = node.Key;
-                if (oldKey.Equals(key))
+                if (oldKey == key)
                 {
                     return true;
                 }
@@ -132,12 +137,17 @@ namespace Jint.Collections
             return false;
         }
 
+        internal bool CheckExistingKeys
+        {
+            set => _checkExistingKeys = value;
+        }
+
         public NodeEnumerator GetEnumerator()
         {
             return new NodeEnumerator(this);
         }
 
-        IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
+        IEnumerator<KeyValuePair<Key, TValue>> IEnumerable<KeyValuePair<Key, TValue>>.GetEnumerator()
         {
             return new NodeEnumerator(this);
         }
@@ -147,14 +157,14 @@ namespace Jint.Collections
             return new NodeEnumerator(this);
         }
 
-        public bool Remove(TKey key)
+        public bool Remove(in Key key)
         {
             DictionaryNode last = null;
             DictionaryNode node;
-            for (node = head; node != null; node = node.Next)
+            for (node = _head; node != null; node = node.Next)
             {
                 var oldKey = node.Key;
-                if (oldKey.Equals(key))
+                if (oldKey == key)
                 {
                     break;
                 }
@@ -167,45 +177,39 @@ namespace Jint.Collections
                 return false;
             }
 
-            if (node == head)
+            if (node == _head)
             {
-                head = node.Next;
+                _head = node.Next;
             }
             else
             {
                 last.Next = node.Next;
             }
 
-            count--;
+            _count--;
             return true;
         }
 
-        internal struct NodeEnumerator : IEnumerator<KeyValuePair<TKey, TValue>>
+        internal struct NodeEnumerator : IEnumerator<KeyValuePair<Key, TValue>>
         {
-            private readonly ListDictionary<TKey, TValue> _list;
+            private readonly ListDictionary<TValue> _list;
             private DictionaryNode _current;
             private bool _start;
 
-            public NodeEnumerator(ListDictionary<TKey, TValue> list)
+            public NodeEnumerator(ListDictionary<TValue> list)
             {
                 _list = list;
                 _start = true;
                 _current = null;
             }
 
-            public KeyValuePair<TKey, TValue> Current
-            {
-                get
-                {
-                    return new KeyValuePair<TKey, TValue>(_current.Key, _current.Value);
-                }
-            }
+            public KeyValuePair<Key, TValue> Current => new KeyValuePair<Key, TValue>(_current.Key, _current.Value);
 
             public bool MoveNext()
             {
                 if (_start)
                 {
-                    _current = _list.head;
+                    _current = _list._head;
                     _start = false;
                 }
                 else if (_current != null)
@@ -213,7 +217,7 @@ namespace Jint.Collections
                     _current = _current.Next;
                 }
 
-                return (_current != null);
+                return _current != null;
             }
 
             void IEnumerator.Reset()
@@ -231,7 +235,7 @@ namespace Jint.Collections
 
         internal class DictionaryNode
         {
-            public TKey Key;
+            public Key Key;
             public TValue Value;
             public DictionaryNode Next;
         }

+ 15 - 0
Jint/Collections/PropertyDictionary.cs

@@ -0,0 +1,15 @@
+using Jint.Runtime.Descriptors;
+
+namespace Jint.Collections
+{
+    internal sealed class PropertyDictionary : HybridDictionary<PropertyDescriptor>
+    {
+        public PropertyDictionary()
+        {
+        }
+
+        public PropertyDictionary(int capacity, bool checkExistingKeys) : base(capacity, checkExistingKeys)
+        {
+        }
+    }
+}

+ 9 - 11
Jint/Collections/StringDictionarySlim.cs

@@ -59,7 +59,11 @@ namespace Jint.Collections
             _entries = new Entry[capacity];
         }
 
-        public int Count => _count;
+        public int Count
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => _count;
+        }
 
         /// <summary>
         /// Clears the dictionary. Note that this invalidates any active enumerators.
@@ -78,7 +82,7 @@ namespace Jint.Collections
             for (int i = _buckets[key.HashCode & (_buckets.Length-1)] - 1;
                 (uint)i < (uint)entries.Length; i = entries[i].next)
             {
-                if (key == entries[i].key)
+                if (key.Name == entries[i].key.Name)
                     return true;
             }
 
@@ -91,7 +95,7 @@ namespace Jint.Collections
             for (int i = _buckets[key.HashCode & (_buckets.Length - 1)] - 1;
                 (uint)i < (uint)entries.Length; i = entries[i].next)
             {
-                if (key == entries[i].key)
+                if (key.Name == entries[i].key.Name)
                 {
                     value = entries[i].value;
                     return true;
@@ -154,7 +158,7 @@ namespace Jint.Collections
             for (int i = _buckets[bucketIndex] - 1;
                     (uint)i < (uint)entries.Length; i = entries[i].next)
             {
-                if (key == entries[i].key)
+                if (key.Name == entries[i].key.Name)
                     return ref entries[i].value;
             }
 
@@ -307,13 +311,7 @@ namespace Jint.Collections
             }
 
             [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
-            public KeyValuePair<Key, V>[] Items
-            {
-                get
-                {
-                    return _dictionary.ToArray();
-                }
-            }
+            public KeyValuePair<Key, V>[] Items => _dictionary.ToArray();
         }
     }
 }

+ 16 - 0
Jint/Collections/SymbolDictionary.cs

@@ -0,0 +1,16 @@
+using Jint.Native;
+using Jint.Runtime.Descriptors;
+
+namespace Jint.Collections
+{
+    internal sealed class SymbolDictionary : DictionarySlim<JsSymbol, PropertyDescriptor>
+    {
+        public SymbolDictionary()
+        {
+        }
+
+        public SymbolDictionary(int capacity) : base(capacity)
+        {
+        }
+    }
+}

+ 70 - 43
Jint/Engine.cs

@@ -103,7 +103,7 @@ namespace Jint
             { typeof(Int64), (engine, v) => (JsValue)(Int64)v },
             { typeof(SByte), (engine, v) => JsNumber.Create((SByte)v) },
             { typeof(Single), (engine, v) => (JsValue)(Single)v },
-            { typeof(string), (engine, v) => (JsValue) (string)v },
+            { typeof(string), (engine, v) => JsString.Create((string) v) },
             { typeof(UInt16), (engine, v) => JsNumber.Create((UInt16)v) },
             { typeof(UInt32), (engine, v) => JsNumber.Create((UInt32)v) },
             { typeof(UInt64), (engine, v) => JsNumber.Create((UInt64)v) },
@@ -196,12 +196,15 @@ namespace Jint
             // Because the properties might need some of the built-in object
             // their configuration is delayed to a later step
 
+            // trigger initialization
+            Global.GetProperty(JsString.Empty);
+
             // this is implementation dependent, and only to pass some unit tests
             Global._prototype = Object.PrototypeObject;
             Object._prototype = Function.PrototypeObject;
 
             // create the global environment http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.3
-            GlobalEnvironment = LexicalEnvironment.NewObjectEnvironment(this, Global, null, false);
+            GlobalEnvironment = LexicalEnvironment.NewGlobalEnvironment(this, Global);
 
             // create the global execution context http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.1.1
             EnterExecutionContext(GlobalEnvironment, GlobalEnvironment, Global);
@@ -226,7 +229,7 @@ namespace Jint
             _jsValueArrayPool = new JsValueArrayPool();
 
             Eval = new EvalFunctionInstance(this, ArrayExt.Empty<string>(), LexicalEnvironment.NewDeclarativeEnvironment(this, ExecutionContext.LexicalEnvironment), StrictModeScope.IsStrictModeCode);
-            Global.SetProperty(KnownKeys.Eval, new PropertyDescriptor(Eval, PropertyFlag.Configurable | PropertyFlag.Writable));
+            Global.SetProperty(CommonProperties.Eval, new PropertyDescriptor(Eval, PropertyFlag.Configurable | PropertyFlag.Writable));
 
             if (Options._IsClrAllowed)
             {
@@ -240,7 +243,7 @@ namespace Jint
             ClrTypeConverter = new DefaultTypeConverter(this);
         }
 
-        public LexicalEnvironment GlobalEnvironment { get; }
+        internal LexicalEnvironment GlobalEnvironment { get; }
         public GlobalObject Global { get; }
         public ObjectConstructor Object { get; }
         public FunctionConstructor Function { get; }
@@ -314,39 +317,39 @@ namespace Jint
             _executionContexts.Push(context);
         }
 
-        public Engine SetValue(in Key name, Delegate value)
+        public Engine SetValue(JsValue name, Delegate value)
         {
             Global.FastAddProperty(name, new DelegateWrapper(this, value), true, false, true);
             return this;
         }
 
-        public Engine SetValue(in Key name, string value)
+        public Engine SetValue(JsValue name, string value)
         {
-            return SetValue(name, (JsValue) value);
+            return SetValue(name, new JsString(value));
         }
 
-        public Engine SetValue(in Key name, double value)
+        public Engine SetValue(JsValue name, double value)
         {
             return SetValue(name, JsNumber.Create(value));
         }
 
-        public Engine SetValue(in Key name, int value)
+        public Engine SetValue(JsValue name, int value)
         {
             return SetValue(name, JsNumber.Create(value));
         }
 
-        public Engine SetValue(in Key name, bool value)
+        public Engine SetValue(JsValue name, bool value)
         {
             return SetValue(name, value ? JsBoolean.True : JsBoolean.False);
         }
 
-        public Engine SetValue(in Key name, JsValue value)
+        public Engine SetValue(JsValue name, JsValue value)
         {
             Global.Set(name, value, Global);
             return this;
         }
 
-        public Engine SetValue(in Key name, object obj)
+        public Engine SetValue(JsValue name, object obj)
         {
             return SetValue(name, JsValue.FromObject(this, obj));
         }
@@ -504,7 +507,9 @@ namespace Jint
 
         internal JsValue GetValue(Reference reference, bool returnReferenceToPool)
         {
-            if (reference._baseValue._type == InternalTypes.Undefined)
+            var baseValue = reference.GetBase();
+
+            if (baseValue._type == InternalTypes.Undefined)
             {
                 if (_referenceResolver != null &&
                     _referenceResolver.TryUnresolvableReference(this, reference, out JsValue val))
@@ -515,10 +520,8 @@ namespace Jint
                 ExceptionHelper.ThrowReferenceError(this, reference);
             }
 
-            var baseValue = reference._baseValue;
-
             if (_referenceResolver != null
-                && reference._baseValue._type < InternalTypes.ObjectEnvironmentRecord
+                && (baseValue._type & InternalTypes.ObjectEnvironmentRecord) == 0
                 && _referenceResolver.TryPropertyReference(this, reference, ref baseValue))
             {
                 return baseValue;
@@ -526,22 +529,47 @@ namespace Jint
             
             if (reference.IsPropertyReference())
             {
-                ref readonly var referencedName = ref reference.GetReferencedName();
+                var property = reference.GetReferencedName();
                 if (returnReferenceToPool)
                 {
                     _referencePool.Return(reference);
                 }
 
-                if (reference._baseValue._type == InternalTypes.Object)
+                if (baseValue.IsObject())
                 {
                     var o = TypeConverter.ToObject(this, baseValue);
-                    var v = o.Get(referencedName);
+                    var v = o.Get(property);
                     return v;
                 }
                 else
                 {
-                    var o = TypeConverter.ToObject(this, baseValue);
-                    var desc = o.GetProperty(referencedName);
+                    // check if we are accessing a string, boxing operation can be costly to do index access
+                    ObjectInstance o;
+                    if (baseValue is JsString s)
+                    {
+                        if (property == CommonProperties.Length)
+                        {
+                            return s.Length;
+                        }
+
+                        if (property is JsNumber number && number.IsInteger())
+                        {
+                            var index = number.AsInteger();
+                            if (index < 0 || index >= s.Length)
+                            {
+                                return JsValue.Undefined;
+                            }
+                            return s[index];
+                        }
+
+                        o = String.PrototypeObject;
+                    }
+                    else
+                    {
+                        o = TypeConverter.ToObject(this, baseValue);
+                    }
+
+                    var desc = o.GetProperty(property);
                     if (desc == PropertyDescriptor.Undefined)
                     {
                         return JsValue.Undefined;
@@ -568,7 +596,7 @@ namespace Jint
                 return ExceptionHelper.ThrowArgumentException<JsValue>();
             }
 
-            var bindingValue = record.GetBindingValue(reference.GetReferencedName(), reference._strict);
+            var bindingValue = record.GetBindingValue(reference.GetReferencedName().ToString(), reference.IsStrictReference());
 
             if (returnReferenceToPool)
             {
@@ -581,35 +609,36 @@ namespace Jint
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/#sec-putvalue
         /// </summary>
-        public void PutValue(Reference reference, JsValue value)
+        internal void PutValue(Reference reference, JsValue value)
         {
-            ref readonly var referencedName = ref reference.GetReferencedName();
-            var baseValue = reference._baseValue;
+            var property = reference.GetReferencedName();
+            var baseValue = reference.GetBase();
             if (reference.IsUnresolvableReference())
             {
-                if (reference._strict)
+                if (reference.IsStrictReference())
                 {
                     ExceptionHelper.ThrowReferenceError(this, reference);
                 }
 
-                Global.Set(referencedName, value);
+                Global.Set(property, value);
             }
             else if (reference.IsPropertyReference())
             {
                 if (reference.HasPrimitiveBase())
                 {
-                    baseValue = TypeConverter.ToObject(this, reference._baseValue);
+                    baseValue = TypeConverter.ToObject(this, baseValue);
                 }
+
                 var thisValue = GetThisValue(reference);
-                var succeeded = baseValue.Set(referencedName, value, thisValue);
-                if (!succeeded && reference._strict)
+                var succeeded = baseValue.Set(property, value, thisValue);
+                if (!succeeded && reference.IsStrictReference())
                 {
                     ExceptionHelper.ThrowTypeError(this);
                 }
             }
             else
             {
-                ((EnvironmentRecord) baseValue).SetMutableBinding(referencedName, value, reference._strict);
+                ((EnvironmentRecord) baseValue).SetMutableBinding(property.ToString(), value, reference.IsStrictReference());
             }
         }
 
@@ -620,7 +649,7 @@ namespace Jint
                 return ExceptionHelper.ThrowNotImplementedException<JsValue>();
             }
 
-            return reference._baseValue;
+            return reference.GetBase();
         }
 
         /// <summary>
@@ -688,7 +717,7 @@ namespace Jint
         /// <param name="propertyName">The name of the property to return.</param>
         public JsValue GetValue(string propertyName)
         {
-            return GetValue(Global, propertyName);
+            return GetValue(Global, new JsString(propertyName));
         }
 
         /// <summary>
@@ -703,12 +732,10 @@ namespace Jint
         /// Gets a named value from the specified scope.
         /// </summary>
         /// <param name="scope">The scope to get the property from.</param>
-        /// <param name="propertyName">The name of the property to return.</param>
-        public JsValue GetValue(JsValue scope, in Key propertyName)
+        /// <param name="property">The name of the property to return.</param>
+        public JsValue GetValue(JsValue scope, JsValue property)
         {
-            AssertNotNullOrEmpty(nameof(propertyName), propertyName);
-
-            var reference = _referencePool.Rent(scope, propertyName, _isStrict);
+            var reference = _referencePool.Rent(scope, property, _isStrict);
             var jsValue = GetValue(reference, false);
             _referencePool.Return(reference);
             return jsValue;
@@ -749,7 +776,7 @@ namespace Jint
                     var parameters = functionInstance._formalParameters;
                     for (uint i = 0; i < (uint) parameters.Length; i++)
                     {
-                        Key argName = parameters[i];
+                        var argName = parameters[i];
                         var v = i + 1 > arguments.Length ? Undefined.Instance : arguments[i];
                         v = DeclarativeEnvironmentRecord.HandleAssignmentPatternIfNeeded(functionDeclaration, v, i);
 
@@ -761,7 +788,7 @@ namespace Jint
 
                         env.SetMutableBinding(argName, v, strict);
                     }
-                    env.CreateMutableBinding(KnownKeys.Arguments, argsObj);
+                    env.CreateMutableBinding("arguments", argsObj);
                 }
             }
 
@@ -796,7 +823,7 @@ namespace Jint
                         var d = declarations[j];
                         if (d.Id is Identifier id1)
                         {
-                            Key name = id1.Name;
+                            var name = id1.Name;
                             var varAlreadyDeclared = env.HasBinding(name);
                             if (!varAlreadyDeclared)
                             {
@@ -820,9 +847,9 @@ namespace Jint
             for (var i = 0; i < functionDeclarationsCount; i++)
             {
                 var f = functionDeclarations[i];
-                Key fn = f.Id.Name;
+                var fn = f.Id.Name;
                 var fo = Function.CreateFunctionObject(f);
-                var funcAlreadyDeclared = env.HasBinding(fn);
+                var funcAlreadyDeclared = env.HasBinding(f.Id.Name);
                 if (!funcAlreadyDeclared)
                 {
                     env.CreateMutableBinding(fn, configurableBindings);

+ 7 - 6
Jint/EsprimaExtensions.cs

@@ -2,6 +2,7 @@ using System;
 using System.Globalization;
 using System.Runtime.CompilerServices;
 using Esprima.Ast;
+using Jint.Native;
 using Jint.Runtime;
 using Jint.Runtime.Interpreter.Expressions;
 
@@ -9,9 +10,9 @@ namespace Jint
 {
     public static class EsprimaExtensions
     {
-        public static Key GetKey(this Property property, Engine engine) => GetKey(property.Key, engine, property.Computed);
+        public static JsValue GetKey(this Property property, Engine engine) => GetKey(property.Key, engine, property.Computed);
 
-        internal static Key GetKey<T>(this T expression, Engine engine, bool computed) where T : class, Expression
+        internal static JsValue GetKey<T>(this T expression, Engine engine, bool computed) where T : class, Expression
         {
             if (expression is Literal literal)
             {
@@ -31,7 +32,7 @@ namespace Jint
             return propertyKey;
         }
 
-        private static bool TryGetComputedPropertyKey<T>(T expression, Engine engine, out Key propertyKey)
+        private static bool TryGetComputedPropertyKey<T>(T expression, Engine engine, out JsValue propertyKey)
             where T : class, Expression
         {
             if (expression.Type == Nodes.Identifier
@@ -40,11 +41,11 @@ namespace Jint
                 || expression.Type == Nodes.UpdateExpression
                 || expression is StaticMemberExpression)
             {
-                propertyKey = JintExpression.Build(engine, expression).GetValue().ToPropertyKey();
+                propertyKey = TypeConverter.ToPropertyKey(JintExpression.Build(engine, expression).GetValue());
                 return true;
             }
 
-            propertyKey = "";
+            propertyKey = string.Empty;
             return false;
         }
 
@@ -56,7 +57,7 @@ namespace Jint
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal static Key LiteralKeyToString(Literal literal)
+        internal static string LiteralKeyToString(Literal literal)
         {
             // prevent conversion to scientific notation
             if (literal.Value is double d)

+ 1 - 12
Jint/JsValueExtensions.cs

@@ -37,7 +37,7 @@ namespace Jint
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static string AsString(this JsValue value)
         {
-            if (value._type != InternalTypes.String)
+            if (!value.IsString())
             {
                 ThrowWrongTypeException(value, "string");
             }
@@ -51,17 +51,6 @@ namespace Jint
             return value.ToString();
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static string AsSymbol(this JsValue value)
-        {
-            if (value._type != InternalTypes.Symbol)
-            {
-                ThrowWrongTypeException(value, "symbol");
-            }
-
-            return ((JsSymbol) value).ToPropertyKey();
-        }
-
         private static void ThrowWrongTypeException(JsValue value, string expectedType)
         {
             ExceptionHelper.ThrowArgumentException($"Expected {expectedType} but got {value._type}");

+ 12 - 74
Jint/Key.cs

@@ -1,7 +1,6 @@
 using System;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
-using Jint.Native;
 using Jint.Runtime;
 
 namespace Jint
@@ -11,110 +10,49 @@ namespace Jint
     /// as runtime does a lot of repetitive dictionary lookups.
     /// </summary>
     [DebuggerDisplay("{" + nameof(Name) + "}")]
-    public readonly struct Key : IEquatable<Key>
+    internal readonly struct Key : IEquatable<Key>
     {
-        // lookup for indexer keys
-        internal static readonly Key[] indexKeys = new Key[TypeConverter.intToString.Length];
-
-        static Key()
-        {
-            for (uint i = 0; i < indexKeys.Length; ++i)
-            {
-                indexKeys[i] = new Key(i.ToString());
-            }
-        }
-
-        public Key(string name) : this(name, symbolIdentity: 0)
-        {
-        }
-
-        internal Key(string name, int symbolIdentity)
+        private Key(string name)
         {
-            if (name == null)
-            {
-                ExceptionHelper.ThrowArgumentException("name cannot be null");
-            }
-            Name = name;
-            HashCode = name.GetHashCode() * 397 ^ symbolIdentity.GetHashCode();
-            _symbolIdentity = symbolIdentity;
+            Name = name ?? ExceptionHelper.ThrowArgumentException<string>("name cannot be null");
+            HashCode = name.GetHashCode();
         }
 
-        public readonly string Name;
+        internal readonly string Name;
         internal readonly int HashCode;
-        internal readonly int _symbolIdentity;
-
-        public Types Type => _symbolIdentity == 0 ? Types.String : Types.Symbol;
-
-        public bool IsSymbol => _symbolIdentity != 0;
 
         public static implicit operator Key(string name)
         {
             return new Key(name);
         }
 
-        public static implicit operator Key(JsSymbol name)
-        {
-            return name.ToPropertyKey();
-        }
-
-        public static implicit operator JsValue(in Key key)
-        {
-            return !key.IsSymbol 
-                ? (JsValue) JsString.Create(key.Name)
-                : new JsSymbol(new JsString(key.Name), key._symbolIdentity);
-        }
-
-        public static implicit operator Key(int value)
-        {
-            var keys = indexKeys;
-            return (uint) value < keys.Length ? keys[value] : BuildKey(value);
-        }
-
-        public static implicit operator Key(uint value)
-        {
-            var keys = indexKeys;
-            return value < keys.Length ? keys[value] : BuildKey(value);
-        }
-
-        public static implicit operator Key(ulong value)
-        {
-            var keys = indexKeys;
-            return value < (ulong) keys.Length ? keys[value] : new Key(value.ToString());
-        }
-
-        [MethodImpl(MethodImplOptions.NoInlining)]
-        private static Key BuildKey(long value)
-        {
-            return new Key(value.ToString());
-        }
-
         public static implicit operator string(Key key) => key.Name;
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static bool operator ==(in Key a, Key b)
+        public static bool operator ==(in Key a, in Key b)
         {
-            return a.HashCode == b.HashCode && a.Name == b.Name && a._symbolIdentity == b._symbolIdentity;
+            return a.HashCode == b.HashCode && a.Name == b.Name;
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static bool operator !=(in Key a, Key b)
+        public static bool operator !=(in Key a, in Key b)
         {
-            return a.HashCode != b.HashCode || a.Name != b.Name || a._symbolIdentity != b._symbolIdentity;
+            return a.HashCode != b.HashCode || a.Name != b.Name;
         }
 
         public static bool operator ==(in Key a, string b)
         {
-            return a.Name == b && a._symbolIdentity == 0;
+            return a.Name == b;
         }
 
         public static bool operator !=(in Key a, string b)
         {
-            return a.Name != b || a._symbolIdentity > 0;
+            return a.Name != b;
         }
 
         public bool Equals(Key other)
         {
-            return Name == other.Name && _symbolIdentity == other._symbolIdentity;
+            return HashCode == other.HashCode && Name == other.Name;
         }
 
         public override bool Equals(object obj)

+ 35 - 38
Jint/Native/Argument/ArgumentsInstance.cs

@@ -14,7 +14,7 @@ namespace Jint.Native.Argument
     /// </summary>
     public sealed class ArgumentsInstance : ObjectInstance
     {
-        // cache key container for array iteration for less allocations
+        // cache property container for array iteration for less allocations
         private static readonly ThreadLocal<HashSet<string>> _mappedNamed = new ThreadLocal<HashSet<string>>(() => new HashSet<string>());
 
         private FunctionInstance _func;
@@ -24,7 +24,8 @@ namespace Jint.Native.Argument
         private bool _strict;
         private bool _canReturnToPool;
 
-        internal ArgumentsInstance(Engine engine) : base(engine, objectClass: "Arguments")
+        internal ArgumentsInstance(Engine engine)
+            : base(engine, ObjectClass.Arguments, InternalTypes.Object | InternalTypes.RequiresCloning)
         {
         }
 
@@ -50,9 +51,9 @@ namespace Jint.Native.Argument
         {
             _canReturnToPool = false;
 
-            var args = _args;
-            SetOwnProperty(KnownKeys.Length, new PropertyDescriptor(args.Length, PropertyFlag.NonEnumerable));
+            SetOwnProperty(CommonProperties.Length, new PropertyDescriptor(_args.Length, PropertyFlag.NonEnumerable));
 
+            var args = _args;
             ObjectInstance map = null;
             if (args.Length > 0)
             {
@@ -62,14 +63,10 @@ namespace Jint.Native.Argument
                     mappedNamed = _mappedNamed.Value;
                     mappedNamed.Clear();
                 }
-                for (var i = 0; i < (uint) args.Length; i++)
-                {
-                    var indexKey = i < Key.indexKeys.Length
-                        ? Key.indexKeys[i]
-                        : (Key) TypeConverter.ToString(i);
 
-                    var val = args[i];
-                    SetOwnProperty(indexKey, new PropertyDescriptor(val, PropertyFlag.ConfigurableEnumerableWritable));
+                for (uint i = 0; i < (uint) args.Length; i++)
+                {
+                    SetOwnProperty(i, new PropertyDescriptor(args[i], PropertyFlag.ConfigurableEnumerableWritable));
                     if (i < _names.Length)
                     {
                         var name = _names[i];
@@ -77,7 +74,7 @@ namespace Jint.Native.Argument
                         {
                             map ??= Engine.Object.Construct(Arguments.Empty);
                             mappedNamed.Add(name);
-                            map.SetOwnProperty(indexKey, new ClrAccessDescriptor(_env, Engine, name));
+                            map.SetOwnProperty(i, new ClrAccessDescriptor(_env, Engine, name));
                         }
                     }
                 }
@@ -88,31 +85,31 @@ namespace Jint.Native.Argument
             // step 13
             if (!_strict)
             {
-                SetOwnProperty(KnownKeys.Callee, new PropertyDescriptor(_func, PropertyFlag.NonEnumerable));
+                SetOwnProperty(CommonProperties.Callee, new PropertyDescriptor(_func, PropertyFlag.NonEnumerable));
             }
             // step 14
             else
             {
-                DefineOwnProperty(KnownKeys.Caller, _engine._getSetThrower);
-                DefineOwnProperty(KnownKeys.Callee, _engine._getSetThrower);
+                DefineOwnProperty(CommonProperties.Caller, _engine._getSetThrower);
+                DefineOwnProperty(CommonProperties.Callee, _engine._getSetThrower);
             }
         }
 
         public ObjectInstance ParameterMap { get; set; }
 
-        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
+        public override PropertyDescriptor GetOwnProperty(JsValue property)
         {
             EnsureInitialized();
 
             if (!_strict && !ReferenceEquals(ParameterMap, null))
             {
-                var desc = base.GetOwnProperty(propertyName);
+                var desc = base.GetOwnProperty(property);
                 if (desc == PropertyDescriptor.Undefined)
                 {
                     return desc;
                 }
 
-                if (ParameterMap.TryGetValue(propertyName, out var jsValue) && !jsValue.IsUndefined())
+                if (ParameterMap.TryGetValue(property, out var jsValue) && !jsValue.IsUndefined())
                 {
                     desc.Value = jsValue;
                 }
@@ -120,31 +117,31 @@ namespace Jint.Native.Argument
                 return desc;
             }
 
-            return base.GetOwnProperty(propertyName);
+            return base.GetOwnProperty(property);
         }
 
         /// Implementation from ObjectInstance official specs as the one
         /// in ObjectInstance is optimized for the general case and wouldn't work
         /// for arrays
-        public override bool Set(in Key propertyName, JsValue value, JsValue receiver)
+        public override bool Set(JsValue property, JsValue value, JsValue receiver)
         {
             EnsureInitialized();
 
-            if (!CanPut(propertyName))
+            if (!CanPut(property))
             {
                 return false;
             }
 
-            var ownDesc = GetOwnProperty(propertyName);
+            var ownDesc = GetOwnProperty(property);
 
             if (ownDesc.IsDataDescriptor())
             {
                 var valueDesc = new PropertyDescriptor(value, PropertyFlag.None);
-                return DefineOwnProperty(propertyName, valueDesc);
+                return DefineOwnProperty(property, valueDesc);
             }
 
             // property is an accessor or inherited
-            var desc = GetProperty(propertyName);
+            var desc = GetProperty(property);
 
             if (desc.IsAccessorDescriptor())
             {
@@ -157,13 +154,13 @@ namespace Jint.Native.Argument
             else
             {
                 var newDesc = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
-                return DefineOwnProperty(propertyName, newDesc);
+                return DefineOwnProperty(property, newDesc);
             }
 
             return true;
         }
 
-        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc)
+        public override bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
         {
             if (_func is ScriptFunctionInstance scriptFunctionInstance && scriptFunctionInstance._function._hasRestParameter)
             {
@@ -176,8 +173,8 @@ namespace Jint.Native.Argument
             if (!_strict && !ReferenceEquals(ParameterMap, null))
             {
                 var map = ParameterMap;
-                var isMapped = map.GetOwnProperty(propertyName);
-                var allowed = base.DefineOwnProperty(propertyName, desc);
+                var isMapped = map.GetOwnProperty(property);
+                var allowed = base.DefineOwnProperty(property, desc);
                 if (!allowed)
                 {
                     return false;
@@ -187,19 +184,19 @@ namespace Jint.Native.Argument
                 {
                     if (desc.IsAccessorDescriptor())
                     {
-                        map.Delete(propertyName);
+                        map.Delete(property);
                     }
                     else
                     {
                         var descValue = desc.Value;
                         if (!ReferenceEquals(descValue, null) && !descValue.IsUndefined())
                         {
-                            map.Set(propertyName, descValue, false);
+                            map.Set(property, descValue, false);
                         }
 
                         if (desc.WritableSet && !desc.Writable)
                         {
-                            map.Delete(propertyName);
+                            map.Delete(property);
                         }
                     }
                 }
@@ -207,30 +204,30 @@ namespace Jint.Native.Argument
                 return true;
             }
 
-            return base.DefineOwnProperty(propertyName, desc);
+            return base.DefineOwnProperty(property, desc);
         }
 
-        public override bool Delete(in Key propertyName)
+        public override bool Delete(JsValue property)
         {
             EnsureInitialized();
 
             if (!_strict && !ReferenceEquals(ParameterMap, null))
             {
                 var map = ParameterMap;
-                var isMapped = map.GetOwnProperty(propertyName);
-                var result = base.Delete(propertyName);
+                var isMapped = map.GetOwnProperty(property);
+                var result = base.Delete(property);
                 if (result && isMapped != PropertyDescriptor.Undefined)
                 {
-                    map.Delete(propertyName);
+                    map.Delete(property);
                 }
 
                 return result;
             }
 
-            return base.Delete(propertyName);
+            return base.Delete(property);
         }
 
-        internal override JsValue Clone()
+        internal override JsValue DoClone()
         {
             // there's an assignment or return value of function, need to create persistent state
 

+ 14 - 9
Jint/Native/Array/ArrayConstructor.cs

@@ -45,15 +45,19 @@ namespace Jint.Native.Array
 
         protected override void Initialize()
         {
-            var properties = new StringDictionarySlim<PropertyDescriptor>(5)
+            var properties = new PropertyDictionary(3, checkExistingKeys: false)
             {
-                [GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(Engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable), set: Undefined,PropertyFlag.Configurable),
                 ["from"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "from", From, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable)),
                 ["isArray"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "isArray", IsArray, 1), PropertyFlag.NonEnumerable)),
                 ["of"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "of", Of, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable))
             };
-            
-            SetProperties(properties, hasSymbols: true);
+            SetProperties(properties);
+
+            var symbols = new SymbolDictionary(1)
+            {
+                [GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(Engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable), set: Undefined,PropertyFlag.Configurable),
+            };
+            SetSymbols(symbols);
         }
 
         private JsValue From(JsValue thisObj, JsValue[] arguments)
@@ -227,11 +231,11 @@ namespace Jint.Native.Array
                 for (uint k = 0; k < arguments.Length; k++)
                 {
                     var kValue = arguments[k];
-                    var key = TypeConverter.ToString(k);
+                    var key = JsString.Create(k);
                     a.CreateDataPropertyOrThrow(key, kValue);
                 }
 
-                a.Set(KnownKeys.Length, len, true);
+                a.Set(CommonProperties.Length, len, true);
             }
             else
             {
@@ -270,12 +274,13 @@ namespace Jint.Native.Array
                 return JsBoolean.False;
             }
 
-            if (o.AsObject().Class == "Array")
+            var objectClass = o.AsObject().Class;
+            if (objectClass == ObjectClass.Array)
             {
                 return JsBoolean.True;
             }
 
-            if (o.AsObject().Class == "Proxy")
+            if (objectClass == ObjectClass.Proxy)
             {
                 var proxyInstance = (ProxyInstance) o;
                 proxyInstance.AssertNotRevoked("isArray");
@@ -388,7 +393,7 @@ namespace Jint.Native.Array
                 return ConstructFast(length);
             }
 
-            var c = originalArray.Get(KnownKeys.Constructor);
+            var c = originalArray.Get(CommonProperties.Constructor);
 
             // If IsConstructor(C) is true, then
             // Let thisRealm be the current Realm Record.

+ 90 - 53
Jint/Native/Array/ArrayInstance.cs

@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Runtime.CompilerServices;
+
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
@@ -17,7 +18,7 @@ namespace Jint.Native.Array
         internal PropertyDescriptor[] _dense;
         private Dictionary<uint, PropertyDescriptor> _sparse;
 
-        public ArrayInstance(Engine engine, uint capacity = 0) : base(engine, objectClass: "Array")
+        public ArrayInstance(Engine engine, uint capacity = 0) : base(engine, ObjectClass.Array)
         {
             if (capacity < MaxDenseArrayLength)
             {
@@ -32,7 +33,7 @@ namespace Jint.Native.Array
         /// <summary>
         /// Possibility to construct valid array fast, requires that supplied array does not have holes.
         /// </summary>
-        public ArrayInstance(Engine engine, PropertyDescriptor[] items) : base(engine, objectClass: "Array")
+        public ArrayInstance(Engine engine, PropertyDescriptor[] items) : base(engine, ObjectClass.Array)
         {
             int length = 0;
             if (items == null || items.Length == 0)
@@ -49,7 +50,7 @@ namespace Jint.Native.Array
             _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
         }
 
-        public ArrayInstance(Engine engine, Dictionary<uint, PropertyDescriptor> items) : base(engine, objectClass: "Array")
+        public ArrayInstance(Engine engine, Dictionary<uint, PropertyDescriptor> items) : base(engine, ObjectClass.Array)
         {
             _sparse = items;
             var length = items?.Count ?? 0;
@@ -58,17 +59,17 @@ namespace Jint.Native.Array
 
         internal override bool IsArrayLike => true;
 
-        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc)
+        public override bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
         {
             var oldLenDesc = _length;
             var oldLen = (uint) TypeConverter.ToNumber(oldLenDesc.Value);
 
-            if (propertyName == KnownKeys.Length)
+            if (property == CommonProperties.Length)
             {
                 var value = desc.Value;
                 if (ReferenceEquals(value, null))
                 {
-                    return base.DefineOwnProperty("length", desc);
+                    return base.DefineOwnProperty(CommonProperties.Length, desc);
                 }
 
                 var newLenDesc = new PropertyDescriptor(desc);
@@ -81,7 +82,7 @@ namespace Jint.Native.Array
                 newLenDesc.Value = newLen;
                 if (newLen >= oldLen)
                 {
-                    return base.DefineOwnProperty("length", newLenDesc);
+                    return base.DefineOwnProperty(CommonProperties.Length, newLenDesc);
                 }
 
                 if (!oldLenDesc.Writable)
@@ -100,7 +101,7 @@ namespace Jint.Native.Array
                     newLenDesc.Writable = true;
                 }
 
-                var succeeded = base.DefineOwnProperty("length", newLenDesc);
+                var succeeded = base.DefineOwnProperty(CommonProperties.Length, newLenDesc);
                 if (!succeeded)
                 {
                     return false;
@@ -121,7 +122,7 @@ namespace Jint.Native.Array
                             // is it the index of the array
                             if (keyIndex >= newLen && keyIndex < oldLen)
                             {
-                                var deleteSucceeded = DeleteAt(keyIndex);
+                                var deleteSucceeded = Delete(keyIndex);
                                 if (!deleteSucceeded)
                                 {
                                     newLenDesc.Value = keyIndex + 1;
@@ -130,7 +131,7 @@ namespace Jint.Native.Array
                                         newLenDesc.Writable = false;
                                     }
 
-                                    base.DefineOwnProperty("length", newLenDesc);
+                                    base.DefineOwnProperty(CommonProperties.Length, newLenDesc);
                                     return false;
                                 }
                             }
@@ -158,7 +159,7 @@ namespace Jint.Native.Array
                                         newLenDesc.Writable = false;
                                     }
 
-                                    base.DefineOwnProperty("length", newLenDesc);
+                                    base.DefineOwnProperty(CommonProperties.Length, newLenDesc);
                                     return false;
                                 }
                             }
@@ -171,7 +172,7 @@ namespace Jint.Native.Array
                     {
                         // algorithm as per the spec
                         oldLen--;
-                        var deleteSucceeded = Delete(TypeConverter.ToString(oldLen));
+                        var deleteSucceeded = Delete(oldLen);
                         if (!deleteSucceeded)
                         {
                             newLenDesc.Value = oldLen + 1;
@@ -180,7 +181,7 @@ namespace Jint.Native.Array
                                 newLenDesc.Writable = false;
                             }
 
-                            base.DefineOwnProperty("length", newLenDesc);
+                            base.DefineOwnProperty(CommonProperties.Length, newLenDesc);
                             return false;
                         }
                     }
@@ -188,19 +189,19 @@ namespace Jint.Native.Array
 
                 if (!newWritable)
                 {
-                    base.DefineOwnProperty("length", new PropertyDescriptor(value: null, PropertyFlag.WritableSet));
+                    base.DefineOwnProperty(CommonProperties.Length, new PropertyDescriptor(value: null, PropertyFlag.WritableSet));
                 }
 
                 return true;
             }
-            else if (IsArrayIndex(propertyName, out var index))
+            else if (IsArrayIndex(property, out var index))
             {
                 if (index >= oldLen && !oldLenDesc.Writable)
                 {
                     return false;
                 }
 
-                var succeeded = base.DefineOwnProperty(propertyName, desc);
+                var succeeded = base.DefineOwnProperty(property, desc);
                 if (!succeeded)
                 {
                     return false;
@@ -209,13 +210,13 @@ namespace Jint.Native.Array
                 if (index >= oldLen)
                 {
                     oldLenDesc.Value = index + 1;
-                    base.DefineOwnProperty("length", oldLenDesc);
+                    base.DefineOwnProperty(CommonProperties.Length, oldLenDesc);
                 }
 
                 return true;
             }
 
-            return base.DefineOwnProperty(propertyName, desc);
+            return base.DefineOwnProperty(property, desc);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -224,26 +225,26 @@ namespace Jint.Native.Array
             return (uint) ((JsNumber) _length._value)._value;
         }
 
-        protected override void AddProperty(in Key propertyName, PropertyDescriptor descriptor)
+        protected override void AddProperty(JsValue property, PropertyDescriptor descriptor)
         {
-            if (propertyName == KnownKeys.Length)
+            if (property == CommonProperties.Length)
             {
                 _length = descriptor;
                 return;
             }
 
-            base.AddProperty(propertyName, descriptor);
+            base.AddProperty(property, descriptor);
         }
 
-        protected override bool TryGetProperty(in Key propertyName, out PropertyDescriptor descriptor)
+        protected override bool TryGetProperty(JsValue property, out PropertyDescriptor descriptor)
         {
-            if (propertyName == KnownKeys.Length)
+            if (property == CommonProperties.Length)
             {
                 descriptor = _length;
                 return _length != null;
             }
 
-            return base.TryGetProperty(propertyName, out descriptor);
+            return base.TryGetProperty(property, out descriptor);
         }
 
         public override List<JsValue> GetOwnPropertyKeys(Types types)
@@ -256,7 +257,7 @@ namespace Jint.Native.Array
                 {
                     if (_dense[i] != null)
                     {
-                        properties.Add(TypeConverter.ToString(i));
+                        properties.Add(JsString.Create(i));
                     }
                 }
             }
@@ -264,13 +265,13 @@ namespace Jint.Native.Array
             {
                 foreach (var entry in _sparse)
                 {
-                    properties.Add(TypeConverter.ToString(entry.Key));
+                    properties.Add(JsString.Create(entry.Key));
                 }
             }
 
             if (_length != null)
             {
-                properties.Add(KnownKeys.Length);
+                properties.Add(CommonProperties.Length);
             }
 
             properties.AddRange(base.GetOwnPropertyKeys(types));
@@ -278,7 +279,7 @@ namespace Jint.Native.Array
             return properties;
         }
 
-        public override IEnumerable<KeyValuePair<Key, PropertyDescriptor>> GetOwnProperties()
+        public override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
         {
             if (_dense != null)
             {
@@ -287,7 +288,7 @@ namespace Jint.Native.Array
                 {
                     if (_dense[i] != null)
                     {
-                        yield return new KeyValuePair<Key, PropertyDescriptor>(TypeConverter.ToString(i), _dense[i]);
+                        yield return new KeyValuePair<JsValue, PropertyDescriptor>(TypeConverter.ToString(i), _dense[i]);
                     }
                 }
             }
@@ -295,13 +296,13 @@ namespace Jint.Native.Array
             {
                 foreach (var entry in _sparse)
                 {
-                    yield return new KeyValuePair<Key, PropertyDescriptor>(TypeConverter.ToString(entry.Key), entry.Value);
+                    yield return new KeyValuePair<JsValue, PropertyDescriptor>(TypeConverter.ToString(entry.Key), entry.Value);
                 }
             }
 
             if (_length != null)
             {
-                yield return new KeyValuePair<Key, PropertyDescriptor>(KnownKeys.Length, _length);
+                yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Length, _length);
             }
 
             foreach (var entry in base.GetOwnProperties())
@@ -310,14 +311,14 @@ namespace Jint.Native.Array
             }
         }
 
-        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
+        public override PropertyDescriptor GetOwnProperty(JsValue property)
         {
-            if (propertyName == KnownKeys.Length)
+            if (property == CommonProperties.Length)
             {
                 return _length ?? PropertyDescriptor.Undefined;
             }
 
-            if (IsArrayIndex(propertyName, out var index))
+            if (IsArrayIndex(property, out var index))
             {
                 if (TryGetDescriptor(index, out var result))
                 {
@@ -327,7 +328,7 @@ namespace Jint.Native.Array
                 return PropertyDescriptor.Undefined;
             }
 
-            return base.GetOwnProperty(propertyName);
+            return base.GetOwnProperty(property);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -360,23 +361,23 @@ namespace Jint.Native.Array
             return Prototype?.GetProperty(index) ?? PropertyDescriptor.Undefined;
         }
 
-        protected internal override void SetOwnProperty(in Key propertyName, PropertyDescriptor desc)
+        protected internal override void SetOwnProperty(JsValue property, PropertyDescriptor desc)
         {
-            if (IsArrayIndex(propertyName, out var index))
+            if (IsArrayIndex(property, out var index))
             {
                 WriteArrayValue(index, desc);
             }
-            else if (propertyName == KnownKeys.Length)
+            else if (property == CommonProperties.Length)
             {
                 _length = desc;
             }
             else
             {
-                base.SetOwnProperty(propertyName, desc);
+                base.SetOwnProperty(property, desc);
             }
         }
 
-        public override bool HasOwnProperty(in Key p)
+        public override bool HasOwnProperty(JsValue p)
         {
             if (IsArrayIndex(p, out var index))
             {
@@ -385,7 +386,7 @@ namespace Jint.Native.Array
                        && (_dense == null || (index < (uint) _dense.Length && _dense[index] != null));
             }
 
-            if (p == KnownKeys.Length)
+            if (p == CommonProperties.Length)
             {
                 return _length != null;
             }
@@ -393,14 +394,14 @@ namespace Jint.Native.Array
             return base.HasOwnProperty(p);
         }
 
-        public override void RemoveOwnProperty(in Key p)
+        public override void RemoveOwnProperty(JsValue p)
         {
             if (IsArrayIndex(p, out var index))
             {
-                DeleteAt(index);
+                Delete(index);
             }
 
-            if (p == KnownKeys.Length)
+            if (p == CommonProperties.Length)
             {
                 _length = null;
             }
@@ -409,17 +410,24 @@ namespace Jint.Native.Array
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private static bool IsArrayIndex(string p, out uint index)
+        private static bool IsArrayIndex(JsValue p, out uint index)
         {
-            index = ParseArrayIndex(p);
+            if (p is JsNumber number)
+            {
+                var value = number._value;
+                var intValue = (uint) value;
+                index = intValue;
+                return value == intValue && intValue != uint.MaxValue;
+            }
+
+            index = ParseArrayIndex(p.ToString());
             return index != uint.MaxValue;
 
             // 15.4 - Use an optimized version of the specification
             // return TypeConverter.ToString(index) == TypeConverter.ToString(p) && index != uint.MaxValue;
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal static uint ParseArrayIndex(string p)
+        private static uint ParseArrayIndex(string p)
         {
             if (p.Length == 0)
             {
@@ -527,13 +535,41 @@ namespace Jint.Native.Array
             return desc.TryGetValue(this, out value);
         }
 
+        internal bool DeletePropertyOrThrow(uint index)
+        {
+            if (!Delete(index))
+            {
+                ExceptionHelper.ThrowTypeError(Engine);
+            }
+            return true;
+        }
+
+        internal bool Delete(uint index)
+        {
+            var desc = GetOwnProperty(index);
+
+            if (desc == PropertyDescriptor.Undefined)
+            {
+                return true;
+            }
+
+            if (desc.Configurable)
+            {
+                DeleteAt(index);
+                return true;
+            }
+
+            return false;
+        }
+        
         internal bool DeleteAt(uint index)
         {
-            if (_dense != null)
+            var temp = _dense;
+            if (temp != null)
             {
-                if (index < (uint) _dense.Length)
+                if (index < (uint) temp.Length)
                 {
-                    _dense[index] = null;
+                    temp[index] = null;
                     return true;
                 }
             }
@@ -544,6 +580,7 @@ namespace Jint.Native.Array
 
             return false;
         }
+        
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private bool TryGetDescriptor(uint index, out PropertyDescriptor descriptor)
@@ -671,7 +708,7 @@ namespace Jint.Native.Array
             }
             else
             {
-                if (!Set(KnownKeys.Length, newLength, this))
+                if (!Set(CommonProperties.Length, newLength, this))
                 {
                     ExceptionHelper.ThrowTypeError(_engine);
                 }
@@ -828,7 +865,7 @@ namespace Jint.Native.Array
                 {
                     var sourcePropertyDescriptor = i < (uint) sourceDense.Length && sourceDense[i] != null
                         ? sourceDense[i]
-                        : source.GetProperty(i.ToString());
+                        : source.GetProperty(i);
 
                     dense[targetStartIndex + j] = sourcePropertyDescriptor?._value != null
                         ? new PropertyDescriptor(sourcePropertyDescriptor._value, PropertyFlag.ConfigurableEnumerableWritable)

+ 11 - 11
Jint/Native/Array/ArrayOperations.cs

@@ -76,7 +76,7 @@ namespace Jint.Native.Array
 
             private double GetIntegerLength()
             {
-                var descValue = _target.Get("length", _target);
+                var descValue = _target.Get(CommonProperties.Length, _target);
                 if (!ReferenceEquals(descValue, null))
                 {
                     return TypeConverter.ToInteger(descValue);
@@ -96,7 +96,7 @@ namespace Jint.Native.Array
                 var min = length;
                 foreach (var entry in _target.Properties)
                 {
-                    if (ulong.TryParse(entry.Key, out var index))
+                    if (ulong.TryParse(entry.Key.ToString(), out var index))
                     {
                         min = System.Math.Min(index, min);
                     }
@@ -106,7 +106,7 @@ namespace Jint.Native.Array
                 {
                     foreach (var entry in _target.Prototype.Properties)
                     {
-                        if (ulong.TryParse(entry.Key, out var index))
+                        if (ulong.TryParse(entry.Key.ToString(), out var index))
                         {
                             min = System.Math.Min(index, min);
                         }
@@ -135,7 +135,7 @@ namespace Jint.Native.Array
 
             public override void SetLength(ulong length)
             {
-                _target.Set("length", length, true);
+                _target.Set(CommonProperties.Length, length, true);
             }
 
             public override void EnsureCapacity(ulong capacity)
@@ -144,12 +144,12 @@ namespace Jint.Native.Array
 
             public override JsValue Get(ulong index)
             {
-                return _target.Get(TypeConverter.ToString(index), _target);
+                return _target.Get(JsString.Create(index), _target);
             }
 
             public override bool TryGetValue(ulong index, out JsValue value)
             {
-                var propertyName = TypeConverter.ToString(index);
+                var propertyName = JsString.Create(index);
                 var property = _target.GetProperty(propertyName);
                 var kPresent = property != PropertyDescriptor.Undefined;
                 value = kPresent ? _target.UnwrapJsValue(property) : JsValue.Undefined;
@@ -158,17 +158,17 @@ namespace Jint.Native.Array
 
             public override void CreateDataPropertyOrThrow(ulong index, JsValue value)
             {
-                _target.CreateDataPropertyOrThrow(TypeConverter.ToString(index), value);
+                _target.CreateDataPropertyOrThrow(JsString.Create(index), value);
             }
 
             public override void Set(ulong index, JsValue value, bool updateLength, bool throwOnError)
             {
-                _target.Set(TypeConverter.ToString(index), value, throwOnError);
+                _target.Set(JsString.Create(index), value, throwOnError);
             }
 
             public override void DeletePropertyOrThrow(ulong index)
             {
-                _target.DeletePropertyOrThrow(TypeConverter.ToString(index));
+                _target.DeletePropertyOrThrow(JsString.Create(index));
             }
         }
 
@@ -195,7 +195,7 @@ namespace Jint.Native.Array
 
             public override void SetLength(ulong length)
             {
-                _target.Set(KnownKeys.Length, length, true);
+                _target.Set(CommonProperties.Length, length, true);
             }
 
             public override void EnsureCapacity(ulong capacity)
@@ -247,7 +247,7 @@ namespace Jint.Native.Array
 
             public override void DeletePropertyOrThrow(ulong index)
             {
-                _target.DeletePropertyOrThrow(TypeConverter.ToString(index));
+                _target.DeletePropertyOrThrow((uint) index);
             }
 
             public override void CreateDataPropertyOrThrow(ulong index, JsValue value)

+ 30 - 26
Jint/Native/Array/ArrayPrototype.cs

@@ -42,21 +42,21 @@ namespace Jint.Native.Array
                 _prototype = null
             };
 
-            unscopables.CreateDataProperty("copyWithin", JsBoolean.True);
-            unscopables.CreateDataProperty("entries", JsBoolean.True);
-            unscopables.CreateDataProperty("fill", JsBoolean.True);
-            unscopables.CreateDataProperty("find", JsBoolean.True);
-            unscopables.CreateDataProperty("findIndex", JsBoolean.True);
-            unscopables.CreateDataProperty("flat", JsBoolean.True);
-            unscopables.CreateDataProperty("flatMap", JsBoolean.True);
-            unscopables.CreateDataProperty("includes", JsBoolean.True);
-            unscopables.CreateDataProperty("keys", JsBoolean.True);
-            unscopables.CreateDataProperty("values", JsBoolean.True);
+            unscopables.SetDataProperty("copyWithin", JsBoolean.True);
+            unscopables.SetDataProperty("entries", JsBoolean.True);
+            unscopables.SetDataProperty("fill", JsBoolean.True);
+            unscopables.SetDataProperty("find", JsBoolean.True);
+            unscopables.SetDataProperty("findIndex", JsBoolean.True);
+            unscopables.SetDataProperty("flat", JsBoolean.True);
+            unscopables.SetDataProperty("flatMap", JsBoolean.True);
+            unscopables.SetDataProperty("includes", JsBoolean.True);
+            unscopables.SetDataProperty("keys", JsBoolean.True);
+            unscopables.SetDataProperty("values", JsBoolean.True);
             
             const PropertyFlag propertyFlags = PropertyFlag.Writable | PropertyFlag.Configurable;
-            var properties = new StringDictionarySlim<PropertyDescriptor>(35)
+            var properties = new PropertyDictionary(30, checkExistingKeys: false)
             {
-                [KnownKeys.Constructor] = new PropertyDescriptor(_arrayConstructor, PropertyFlag.NonEnumerable),
+                ["constructor"] = new PropertyDescriptor(_arrayConstructor, PropertyFlag.NonEnumerable),
                 ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToString, 0, PropertyFlag.Configurable), propertyFlags),
                 ["toLocaleString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toLocaleString", ToLocaleString, 0, PropertyFlag.Configurable), propertyFlags),
                 ["concat"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "concat", Concat, 1, PropertyFlag.Configurable), propertyFlags),
@@ -85,11 +85,16 @@ namespace Jint.Native.Array
                 ["find"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "find", Find, 1, PropertyFlag.Configurable), propertyFlags),
                 ["findIndex"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "findIndex", FindIndex, 1, PropertyFlag.Configurable), propertyFlags),
                 ["keys"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "keys", Keys, 0, PropertyFlag.Configurable), propertyFlags),
-                ["values"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "values", Values, 0, PropertyFlag.Configurable), propertyFlags),
+                ["values"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "values", Values, 0, PropertyFlag.Configurable), propertyFlags)
+            };
+            SetProperties(properties);
+
+            var symbols = new SymbolDictionary(2)
+            {
                 [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "iterator", Values, 1), propertyFlags),
                 [GlobalSymbolRegistry.Unscopables] = new PropertyDescriptor(unscopables, PropertyFlag.Configurable)
             };
-            SetProperties(properties, hasSymbols: true);
+            SetSymbols(symbols);
         }
         
         private ObjectInstance Keys(JsValue thisObj, JsValue[] arguments)
@@ -398,7 +403,7 @@ namespace Jint.Native.Array
 
         private JsValue Map(JsValue thisObj, JsValue[] arguments)
         {
-            if (thisObj is ArrayInstance arrayInstance && !arrayInstance.HasOwnProperty("constructor"))
+            if (thisObj is ArrayInstance arrayInstance && !arrayInstance.HasOwnProperty(CommonProperties.Constructor))
             {
                 return arrayInstance.Map(arguments);
             }
@@ -481,9 +486,9 @@ namespace Jint.Native.Array
                     ? n
                     : len - System.Math.Abs(n), 0);
 
-            static bool SameValueZero(JsValue x, JsValue y) {
-                return x == y 
-                             || (x is JsNumber xNum && y is JsNumber yNum && double.IsNaN(xNum._value) && double.IsNaN(yNum._value));
+            static bool SameValueZero(JsValue x, JsValue y)
+            {
+                return x == y || (x is JsNumber xNum && y is JsNumber yNum && double.IsNaN(xNum._value) && double.IsNaN(yNum._value));
             }
 
             while (k < len)
@@ -944,7 +949,7 @@ namespace Jint.Native.Array
                     }
                 }
             }
-            a.DefineOwnProperty("length", new PropertyDescriptor(length, PropertyFlag.None));
+            a.DefineOwnProperty(CommonProperties.Length, new PropertyDescriptor(length, PropertyFlag.None));
 
             return a;
         }
@@ -1034,7 +1039,7 @@ namespace Jint.Native.Array
             // as per the spec, this has to be called after ToString(separator)
             if (len == 0)
             {
-                return "";
+                return JsString.Empty;
             }
 
             string StringFromJsValue(JsValue value)
@@ -1070,13 +1075,13 @@ namespace Jint.Native.Array
             const string separator = ",";
             if (len == 0)
             {
-                return "";
+                return JsString.Empty;
             }
 
             JsValue r;
             if (!array.TryGetValue(0, out var firstElement) || firstElement.IsNull() || firstElement.IsUndefined())
             {
-                r = "";
+                r = JsString.Empty;
             }
             else
             {
@@ -1091,7 +1096,7 @@ namespace Jint.Native.Array
                 string s = r + separator;
                 if (!array.TryGetValue(k, out var nextElement) || nextElement.IsNull())
                 {
-                    r = "";
+                    r = JsString.Empty;
                 }
                 else
                 {
@@ -1118,8 +1123,7 @@ namespace Jint.Native.Array
             for (var i = 0; i < items.Count; i++)
             {
                 uint increment;
-                var objectInstance = items[i] as ObjectInstance;
-                if (objectInstance == null)
+                if (!(items[i] is ObjectInstance objectInstance))
                 {
                     increment = 1;
                 }
@@ -1167,7 +1171,7 @@ namespace Jint.Native.Array
 
             // this is not in the specs, but is necessary in case the last element of the last
             // array doesn't exist, and thus the length would not be incremented
-            a.DefineOwnProperty("length", new PropertyDescriptor(n, PropertyFlag.None));
+            a.DefineOwnProperty(CommonProperties.Length, new PropertyDescriptor(n, PropertyFlag.None));
 
             return a;
         }

+ 1 - 1
Jint/Native/Boolean/BooleanInstance.cs

@@ -6,7 +6,7 @@ namespace Jint.Native.Boolean
     public class BooleanInstance : ObjectInstance, IPrimitiveInstance
     {
         public BooleanInstance(Engine engine)
-            : base(engine, objectClass: "Boolean")
+            : base(engine, ObjectClass.Boolean)
         {
         }
 

+ 4 - 4
Jint/Native/Boolean/BooleanPrototype.cs

@@ -30,13 +30,13 @@ namespace Jint.Native.Boolean
 
         protected override void Initialize()
         {
-            var properties = new StringDictionarySlim<PropertyDescriptor>(3)
+            var properties = new PropertyDictionary(3, checkExistingKeys: false)
             {
-                [KnownKeys.Constructor] = new PropertyDescriptor(_booleanConstructor, PropertyFlag.NonEnumerable),
+                ["constructor"] = new PropertyDescriptor(_booleanConstructor, PropertyFlag.NonEnumerable),
                 ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToBooleanString, 0, PropertyFlag.Configurable), true, false, true),
                 ["valueOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "valueOf", ValueOf, 0, PropertyFlag.Configurable), true, false, true)
             };
-            SetProperties(properties, hasSymbols: false);
+            SetProperties(properties);
         }
 
         private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)
@@ -57,7 +57,7 @@ namespace Jint.Native.Boolean
         private JsValue ToBooleanString(JsValue thisObj, JsValue[] arguments)
         {
             var b = ValueOf(thisObj, Arguments.Empty);
-            return ((JsBoolean) b)._value ? "true" : "false";
+            return ((JsBoolean) b)._value ? JsString.TrueString : JsString.FalseString;
         }
     }
 }

+ 4 - 3
Jint/Native/Date/DateConstructor.cs

@@ -80,13 +80,14 @@ namespace Jint.Native.Date
         {
             const PropertyFlag lengthFlags = PropertyFlag.Configurable;
             const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
-            
-            SetProperties(new StringDictionarySlim<PropertyDescriptor>(3)
+
+            var properties = new PropertyDictionary(3, checkExistingKeys: false)
             {
                 ["parse"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "parse", Parse, 1, lengthFlags), propertyFlags),
                 ["UTC"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "UTC", Utc, 7, lengthFlags), propertyFlags),
                 ["now"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "now", Now, 0, lengthFlags), propertyFlags)
-            }, false);
+            };
+            SetProperties(properties);
         }
 
         private JsValue Parse(JsValue thisObj, JsValue[] arguments)

+ 1 - 1
Jint/Native/Date/DateInstance.cs

@@ -14,7 +14,7 @@ namespace Jint.Native.Date
         private static readonly double Min = -(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) - DateTime.MinValue).TotalMilliseconds;
 
         public DateInstance(Engine engine)
-            : base(engine, objectClass: "Date")
+            : base(engine, ObjectClass.Date)
         {
             PrimitiveValue = double.NaN;
         }

+ 13 - 8
Jint/Native/Date/DatePrototype.cs

@@ -44,9 +44,9 @@ namespace Jint.Native.Date
             const PropertyFlag lengthFlags = PropertyFlag.Configurable;
             const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
 
-            var properties = new StringDictionarySlim<PropertyDescriptor>(50)
+            var properties = new PropertyDictionary(50, checkExistingKeys: false)
             {
-                [KnownKeys.Constructor] = new PropertyDescriptor(_dateConstructor, PropertyFlag.NonEnumerable),
+                ["constructor"] = new PropertyDescriptor(_dateConstructor, PropertyFlag.NonEnumerable),
                 ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToString, 0, lengthFlags), propertyFlags),
                 ["toDateString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toDateString", ToDateString, 0, lengthFlags), propertyFlags),
                 ["toTimeString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toTimeString", ToTimeString, 0, lengthFlags), propertyFlags),
@@ -91,11 +91,15 @@ namespace Jint.Native.Date
                 ["setUTCFullYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCFullYear", SetUTCFullYear, 3, lengthFlags), propertyFlags),
                 ["toUTCString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toUTCString", ToUtcString, 0, lengthFlags), propertyFlags),
                 ["toISOString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toISOString", ToISOString, 0, lengthFlags), propertyFlags),
-                ["toJSON"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toJSON", ToJSON, 1, lengthFlags), propertyFlags),
+                ["toJSON"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toJSON", ToJSON, 1, lengthFlags), propertyFlags)
+            };
+            SetProperties(properties);
+
+            var symbols = new SymbolDictionary(1)
+            {
                 [GlobalSymbolRegistry.ToPrimitive] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "[Symbol.toPrimitive]", ToPrimitive, 1, PropertyFlag.Configurable), PropertyFlag.Configurable),
             };
-            
-            SetProperties(properties, hasSymbols: true);
+            SetSymbols(symbols);
         }
 
         private JsValue ToPrimitive(JsValue thisObject, JsValue[] arguments)
@@ -111,12 +115,13 @@ namespace Jint.Native.Date
                 return ExceptionHelper.ThrowTypeError<JsValue>(_engine);
             }
 
-            Types tryFirst = Types.None;
-            if (hint == "default" || hint == "string")
+            var hintString = hint.ToString();
+            var tryFirst = Types.None;
+            if (hintString == "default" || hintString == "string")
             {
                 tryFirst = Types.String;
             }
-            else  if (hint == "number")
+            else  if (hintString == "number")
             {
                 tryFirst = Types.Number;
             }

+ 4 - 4
Jint/Native/Error/ErrorInstance.cs

@@ -10,18 +10,18 @@ namespace Jint.Native.Error
         private PropertyDescriptor _descriptor;
 
         public ErrorInstance(Engine engine, JsString name)
-            : base(engine, objectClass: "Error")
+            : base(engine, ObjectClass.Error)
         {
             _name = name;
         }
 
-        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
+        public override PropertyDescriptor GetOwnProperty(JsValue property)
         {
-            if (propertyName.Name == "name")
+            if (property == CommonProperties.Name)
             {
                 return _descriptor ??= new PropertyDescriptor(_name, PropertyFlag.Configurable | PropertyFlag.Writable);
             };
-            return base.GetOwnProperty(in propertyName);
+            return base.GetOwnProperty(property);
         }
 
         public override string ToString()

+ 4 - 4
Jint/Native/Error/ErrorPrototype.cs

@@ -25,7 +25,7 @@ namespace Jint.Native.Error
                 _errorConstructor = errorConstructor,
             };
 
-            if (name != "Error")
+            if (name.ToString() != "Error")
             {
                 obj._prototype = engine.Error.PrototypeObject;
             }
@@ -39,13 +39,13 @@ namespace Jint.Native.Error
 
         protected override void Initialize()
         {
-            var properties = new StringDictionarySlim<PropertyDescriptor>(3)
+            var properties = new PropertyDictionary(3, checkExistingKeys: false)
             {
-                [KnownKeys.Constructor] = new PropertyDescriptor(_errorConstructor, PropertyFlag.NonEnumerable),
+                ["constructor"] = new PropertyDescriptor(_errorConstructor, PropertyFlag.NonEnumerable),
                 ["message"] = new PropertyDescriptor("", PropertyFlag.Configurable | PropertyFlag.Writable),
                 ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToString), PropertyFlag.Configurable | PropertyFlag.Writable)
             };
-            SetProperties(properties, hasSymbols: false);
+            SetProperties(properties);
         }
 
         public JsValue ToString(JsValue thisObject, JsValue[] arguments)

+ 10 - 10
Jint/Native/Function/ArrowFunctionInstance.cs

@@ -92,24 +92,24 @@ namespace Jint.Native.Function
             }
         }
 
-        public override bool Set(in Key propertyName, JsValue value, JsValue receiver)
+        public override bool Set(JsValue property, JsValue value, JsValue receiver)
         {
-            AssertValidPropertyName(propertyName);
-            return base.Set(propertyName, value, receiver);
+            AssertValidPropertyName(property);
+            return base.Set(property, value, receiver);
         }
 
-        public override JsValue Get(in Key propertyName, JsValue receiver)
+        public override JsValue Get(JsValue property, JsValue receiver)
         {
-            AssertValidPropertyName(propertyName);
-            return base.Get(propertyName, receiver);
+            AssertValidPropertyName(property);
+            return base.Get(property, receiver);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private void AssertValidPropertyName(in Key propertyName)
+        private void AssertValidPropertyName(JsValue property)
         {
-            if (propertyName == KnownKeys.Caller
-                || propertyName ==  KnownKeys.Callee
-                || propertyName == KnownKeys.Arguments)
+            if (property == CommonProperties.Caller
+                || property ==  CommonProperties.Callee
+                || property == CommonProperties.Arguments)
             {
                 ExceptionHelper.ThrowTypeError(_engine, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them");
             }

+ 2 - 2
Jint/Native/Function/FunctionConstructor.cs

@@ -147,13 +147,13 @@ namespace Jint.Native.Function
                 ExceptionHelper.ThrowTypeError(Engine);
             }
 
-            var len = argArrayObj.Get("length", argArrayObj);
+            var len = argArrayObj.Get(CommonProperties.Length, argArrayObj);
             var n = TypeConverter.ToUint32(len);
             var argList = new JsValue[n];
             for (var index = 0; index < n; index++)
             {
                 var indexName = TypeConverter.ToString(index);
-                var nextArg = argArrayObj.Get(indexName, argArrayObj);
+                var nextArg = argArrayObj.Get(JsString.Create(indexName), argArrayObj);
                 argList[index] = nextArg;
             }
             return func.Call(thisArg, argList);

+ 59 - 62
Jint/Native/Function/FunctionInstance.cs

@@ -25,9 +25,8 @@ namespace Jint.Native.Function
             string name,
             string[] parameters,
             LexicalEnvironment scope,
-            bool strict,
-            string objectClass = "Function")
-            : this(engine, !string.IsNullOrWhiteSpace(name) ? new JsString(name) : null, parameters, scope, strict, objectClass)
+            bool strict)
+            : this(engine, !string.IsNullOrWhiteSpace(name) ? new JsString(name) : null, parameters, scope, strict)
         {
         }
 
@@ -36,9 +35,8 @@ namespace Jint.Native.Function
             JsString name,
             string[] parameters,
             LexicalEnvironment scope,
-            bool strict,
-            string objectClass = "Function")
-            : this(engine, name, strict, objectClass)
+            bool strict)
+            : this(engine, name, strict)
         {
             _formalParameters = parameters;
             _scope = scope;
@@ -48,7 +46,7 @@ namespace Jint.Native.Function
             Engine engine,
             JsString name,
             bool strict,
-            string objectClass = "Function")
+            ObjectClass objectClass = ObjectClass.Function)
             : base(engine, objectClass)
         {
             _name = name;
@@ -63,43 +61,35 @@ namespace Jint.Native.Function
         /// <returns></returns>
         public abstract JsValue Call(JsValue thisObject, JsValue[] arguments);
 
-        public LexicalEnvironment Scope => _scope;
+        internal LexicalEnvironment Scope => _scope;
 
-        public string[] FormalParameters => _formalParameters;
+        internal string[] FormalParameters => _formalParameters;
 
         public bool Strict => _strict;
 
         public virtual bool HasInstance(JsValue v)
         {
-            var vObj = v.TryCast<ObjectInstance>();
-            if (ReferenceEquals(vObj, null))
+            if (!(v is ObjectInstance o))
             {
                 return false;
             }
 
-            var po = Get(KnownKeys.Prototype, this);
-            if (!po.IsObject())
+            var p = Get(CommonProperties.Prototype, this);
+            if (!(p is ObjectInstance prototype))
             {
-                ExceptionHelper.ThrowTypeError(_engine, $"Function has non-object prototype '{TypeConverter.ToString(po)}' in instanceof check");
-            }
-
-            var o = po.AsObject();
-
-            if (ReferenceEquals(o, null))
-            {
-                ExceptionHelper.ThrowTypeError(_engine);
+                ExceptionHelper.ThrowTypeError(_engine, $"Function has non-object prototype '{TypeConverter.ToString(p)}' in instanceof check");
             }
 
             while (true)
             {
-                vObj = vObj.Prototype;
+                o = o.Prototype;
 
-                if (ReferenceEquals(vObj, null))
+                if (o is null)
                 {
                     return false;
                 }
 
-                if (vObj == o)
+                if (SameValue(p, o))
                 {
                     return true;
                 }
@@ -109,14 +99,11 @@ namespace Jint.Native.Function
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5.4
         /// </summary>
-        /// <param name="propertyName"></param>
-        /// <param name="receiver"></param>
-        /// <returns></returns>
-        public override JsValue Get(in Key propertyName, JsValue receiver)
+        public override JsValue Get(JsValue property, JsValue receiver)
         {
-            var v = base.Get(propertyName, receiver);
+            var v = base.Get(property, receiver);
 
-            if (propertyName == KnownKeys.Caller
+            if (property == CommonProperties.Caller
                 && ((v.As<FunctionInstance>()?._strict).GetValueOrDefault()))
             {
                 ExceptionHelper.ThrowTypeError(_engine);
@@ -125,19 +112,19 @@ namespace Jint.Native.Function
             return v;
         }
 
-        public override IEnumerable<KeyValuePair<Key, PropertyDescriptor>> GetOwnProperties()
+        public override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
         {
             if (_prototypeDescriptor != null)
             {
-                yield return new KeyValuePair<Key, PropertyDescriptor>(KnownKeys.Prototype, _prototypeDescriptor);
+                yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Prototype, _prototypeDescriptor);
             }
             if (_length != null)
             {
-                yield return new KeyValuePair<Key, PropertyDescriptor>(KnownKeys.Length, _length);
+                yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Length, _length);
             }
             if (!(_name is null))
             {
-                yield return new KeyValuePair<Key, PropertyDescriptor>(KnownKeys.Name, GetOwnProperty(KnownKeys.Name));
+                yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Name, GetOwnProperty(CommonProperties.Name));
             }
 
             foreach (var entry in base.GetOwnProperties())
@@ -151,15 +138,15 @@ namespace Jint.Native.Function
             var keys = new List<JsValue>();
             if (_prototypeDescriptor != null)
             {
-                keys.Add(KnownKeys.Prototype);
+                keys.Add(CommonProperties.Prototype);
             }
             if (_length != null)
             {
-                keys.Add(KnownKeys.Length);
+                keys.Add(CommonProperties.Length);
             }
             if (!(_name is null))
             {
-                keys.Add(KnownKeys.Name);
+                keys.Add(CommonProperties.Name);
             }
 
             keys.AddRange(base.GetOwnPropertyKeys(types));
@@ -167,97 +154,107 @@ namespace Jint.Native.Function
             return keys;
         }
 
-        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
+        public override PropertyDescriptor GetOwnProperty(JsValue property)
         {
-            if (propertyName == KnownKeys.Prototype)
+            if (property == CommonProperties.Prototype)
             {
                 return _prototypeDescriptor ?? PropertyDescriptor.Undefined;
             }
-            if (propertyName == KnownKeys.Length)
+            if (property == CommonProperties.Length)
             {
                 return _length ?? PropertyDescriptor.Undefined;
             }
-            if (propertyName == KnownKeys.Name)
+            if (property == CommonProperties.Name)
             {
                 return !(_name is null)
                     ? _nameDescriptor ?? (_nameDescriptor = new PropertyDescriptor(_name, PropertyFlag.Configurable))
                     :  PropertyDescriptor.Undefined;
             }
 
-            return base.GetOwnProperty(propertyName);
+            return base.GetOwnProperty(property);
         }
 
-        protected internal override void SetOwnProperty(in Key propertyName, PropertyDescriptor desc)
+        protected internal override void SetOwnProperty(JsValue property, PropertyDescriptor desc)
         {
-            if (propertyName == KnownKeys.Prototype)
+            if (property == CommonProperties.Prototype)
             {
                 _prototypeDescriptor = desc;
             }
-            else if (propertyName == KnownKeys.Length)
+            else if (property == CommonProperties.Length)
             {
                 _length = desc;
             }
-            else if (propertyName == KnownKeys.Name)
+            else if (property == CommonProperties.Name)
             {
                 _name = desc._value;
                 _nameDescriptor = desc;
             }
             else
             {
-                base.SetOwnProperty(propertyName, desc);
+                base.SetOwnProperty(property, desc);
             }
         }
 
-        public override bool HasOwnProperty(in Key propertyName)
+        public override bool HasOwnProperty(JsValue property)
         {
-            if (propertyName == KnownKeys.Prototype)
+            if (property == CommonProperties.Prototype)
             {
                 return _prototypeDescriptor != null;
             }
-            if (propertyName == KnownKeys.Length)
+            if (property == CommonProperties.Length)
             {
                 return _length != null;
             }
-            if (propertyName == KnownKeys.Name)
+            if (property == CommonProperties.Name)
             {
                 return !(_name is null);
             }
 
-            return base.HasOwnProperty(propertyName);
+            return base.HasOwnProperty(property);
         }
 
-        public override void RemoveOwnProperty(in Key propertyName)
+        public override void RemoveOwnProperty(JsValue property)
         {
-            if (propertyName == KnownKeys.Prototype)
+            if (property == CommonProperties.Prototype)
             {
                 _prototypeDescriptor = null;
             }
-            if (propertyName == KnownKeys.Length)
+            if (property == CommonProperties.Length)
             {
                 _length = null;
             }
-            if (propertyName == KnownKeys.Name)
+            if (property == CommonProperties.Name)
             {
                 _name = null;
                 _nameDescriptor = null;
             }
 
-            base.RemoveOwnProperty(propertyName);
+            base.RemoveOwnProperty(property);
         }
 
-        internal void SetFunctionName(in Key key, bool throwIfExists = false)
+        internal void SetFunctionName(JsValue name, bool throwIfExists = false)
         {
             if (_name is null)
             {
-                _name = key.IsSymbol && key.Name.Length > 0 ? "[" + key.Name + "]" : key.Name;
+                JsString value;
+                if (name is JsSymbol symbol)
+                {
+                    value = new JsString(symbol._value.IsUndefined()
+                        ? ""
+                        : "[" + symbol._value + "]");
+                }
+                else
+                {
+                    value = name as JsString ?? new JsString(name.ToString());
+                }
+                _name = value;
             }
             else if (throwIfExists)
             {
                 ExceptionHelper.ThrowError(_engine, "cannot set name");
             }
         }
-        
-        
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal ObjectInstance OrdinaryCreateFromConstructor(JsValue constructor, ObjectInstance intrinsicDefaultProto)
         {
@@ -273,7 +270,7 @@ namespace Jint.Native.Function
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private static ObjectInstance GetPrototypeFromConstructor(JsValue constructor, ObjectInstance intrinsicDefaultProto)
         {
-            var proto = constructor.Get(KnownKeys.Prototype, constructor) as ObjectInstance;
+            var proto = constructor.Get(CommonProperties.Prototype, constructor) as ObjectInstance;
             // If Type(proto) is not Object, then
             //    Let realm be ? GetFunctionRealm(constructor).
             //    Set proto to realm's intrinsic object named intrinsicDefaultProto.

+ 8 - 8
Jint/Native/Function/FunctionPrototype.cs

@@ -34,15 +34,15 @@ namespace Jint.Native.Function
 
         protected override void Initialize()
         {
-            var properties = new StringDictionarySlim<PropertyDescriptor>(5)
+            var properties = new PropertyDictionary(5, checkExistingKeys: false)
             {
-                [KnownKeys.Constructor] = new PropertyDescriptor(Engine.Function, PropertyFlag.NonEnumerable),
+                ["constructor"] = new PropertyDescriptor(Engine.Function, PropertyFlag.NonEnumerable),
                 ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToString), true, false, true),
                 ["apply"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "apply", Apply, 2), true, false, true),
                 ["call"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "call", CallImpl, 1), true, false, true),
                 ["bind"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "bind", Bind, 1), true, false, true)
             };
-            SetProperties(properties, hasSymbols: false);
+            SetProperties(properties);
         }
 
         private JsValue Bind(JsValue thisObj, JsValue[] arguments)
@@ -65,16 +65,16 @@ namespace Jint.Native.Function
 
             if (target is FunctionInstance functionInstance)
             {
-                var l = TypeConverter.ToNumber(functionInstance.Get("length", functionInstance)) - (arguments.Length - 1);
-                f.SetOwnProperty(KnownKeys.Length, new PropertyDescriptor(System.Math.Max(l, 0), PropertyFlag.AllForbidden));
+                var l = TypeConverter.ToNumber(functionInstance.Get(CommonProperties.Length, functionInstance)) - (arguments.Length - 1);
+                f.SetOwnProperty(CommonProperties.Length, new PropertyDescriptor(System.Math.Max(l, 0), PropertyFlag.AllForbidden));
             }
             else
             {
-                f.SetOwnProperty(KnownKeys.Length, PropertyDescriptor.AllForbiddenDescriptor.NumberZero);
+                f.SetOwnProperty(CommonProperties.Length, PropertyDescriptor.AllForbiddenDescriptor.NumberZero);
             }
 
-            f.DefineOwnProperty(KnownKeys.Caller, _engine._getSetThrower);
-            f.DefineOwnProperty(KnownKeys.Arguments, _engine._getSetThrower);
+            f.DefineOwnProperty(CommonProperties.Caller, _engine._getSetThrower);
+            f.DefineOwnProperty(CommonProperties.Arguments, _engine._getSetThrower);
 
             return f;
         }

+ 17 - 17
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -46,8 +46,8 @@ namespace Jint.Native.Function
 
             if (strict)
             {
-                DefineOwnProperty(KnownKeys.Caller, engine._getSetThrower);
-                DefineOwnProperty(KnownKeys.Arguments, engine._getSetThrower);
+                DefineOwnProperty(CommonProperties.Caller, engine._getSetThrower);
+                DefineOwnProperty(CommonProperties.Arguments, engine._getSetThrower);
             }
         }
 
@@ -75,7 +75,7 @@ namespace Jint.Native.Function
                 {
                     thisBinding = _engine.Global;
                 }
-                else if (thisArg._type != InternalTypes.Object)
+                else if (!thisArg.IsObject())
                 {
                     thisBinding = TypeConverter.ToObject(_engine, thisArg);
                 }
@@ -146,11 +146,11 @@ namespace Jint.Native.Function
                 _constructor = new PropertyDescriptor(thisObj, PropertyFlag.NonEnumerable);
             }
 
-            public override IEnumerable<KeyValuePair<Key, PropertyDescriptor>> GetOwnProperties()
+            public override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
             {
                 if (_constructor != null)
                 {
-                    yield return new KeyValuePair<Key, PropertyDescriptor>(KnownKeys.Constructor, _constructor);
+                    yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Constructor, _constructor);
                 }
 
                 foreach (var entry in base.GetOwnProperties())
@@ -159,47 +159,47 @@ namespace Jint.Native.Function
                 }
             }
 
-            public override PropertyDescriptor GetOwnProperty(in Key propertyName)
+            public override PropertyDescriptor GetOwnProperty(JsValue property)
             {
-                if (propertyName == KnownKeys.Constructor)
+                if (property == CommonProperties.Constructor)
                 {
                     return _constructor ?? PropertyDescriptor.Undefined;
                 }
 
-                return base.GetOwnProperty(propertyName);
+                return base.GetOwnProperty(property);
             }
 
-            protected internal override void SetOwnProperty(in Key propertyName, PropertyDescriptor desc)
+            protected internal override void SetOwnProperty(JsValue property, PropertyDescriptor desc)
             {
-                if (propertyName == KnownKeys.Constructor)
+                if (property == CommonProperties.Constructor)
                 {
                     _constructor = desc;
                 }
                 else
                 {
-                    base.SetOwnProperty(propertyName, desc);
+                    base.SetOwnProperty(property, desc);
                 }
             }
 
-            public override bool HasOwnProperty(in Key propertyName)
+            public override bool HasOwnProperty(JsValue property)
             {
-                if (propertyName == KnownKeys.Constructor)
+                if (property == CommonProperties.Constructor)
                 {
                     return _constructor != null;
                 }
 
-                return base.HasOwnProperty(propertyName);
+                return base.HasOwnProperty(property);
             }
 
-            public override void RemoveOwnProperty(in Key propertyName)
+            public override void RemoveOwnProperty(JsValue property)
             {
-                if (propertyName == KnownKeys.Constructor)
+                if (property == CommonProperties.Constructor)
                 {
                     _constructor = null;
                 }
                 else
                 {
-                    base.RemoveOwnProperty(propertyName);
+                    base.RemoveOwnProperty(property);
                 }
             }
         }

+ 140 - 44
Jint/Native/Global/GlobalObject.cs

@@ -24,60 +24,67 @@ namespace Jint.Native.Global
 
         public static GlobalObject CreateGlobalObject(Engine engine)
         {
-            var global = new GlobalObject(engine)
+            return new GlobalObject(engine);
+        }
+
+        protected override void Initialize()
+        {
+            const PropertyFlag defaultFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
+            var properties = new PropertyDictionary(40, checkExistingKeys: false)
             {
-                _prototype = null,
+                ["Object"] = new PropertyDescriptor(Engine.Object, defaultFlags),
+                ["Function"] = new PropertyDescriptor(Engine.Function, defaultFlags),
+                ["Symbol"] = new PropertyDescriptor(Engine.Symbol, defaultFlags),
+                ["Array"] = new PropertyDescriptor(Engine.Array, defaultFlags),
+                ["Map"] = new PropertyDescriptor(Engine.Map, defaultFlags),
+                ["Set"] = new PropertyDescriptor(Engine.Set, defaultFlags),
+                ["String"] = new PropertyDescriptor(Engine.String, defaultFlags),
+                ["RegExp"] = new PropertyDescriptor(Engine.RegExp, defaultFlags),
+                ["Number"] = new PropertyDescriptor(Engine.Number, defaultFlags),
+                ["Boolean"] = new PropertyDescriptor(Engine.Boolean, defaultFlags),
+                ["Date"] = new PropertyDescriptor(Engine.Date, defaultFlags),
+                ["Math"] = new PropertyDescriptor(Engine.Math, defaultFlags),
+                ["JSON"] = new PropertyDescriptor(Engine.Json, defaultFlags),
+                ["Error"] = new LazyPropertyDescriptor(() => Engine.Error, defaultFlags),
+                ["EvalError"] = new LazyPropertyDescriptor(() => Engine.EvalError, defaultFlags),
+                ["Proxy"] = new LazyPropertyDescriptor(() => Engine.Proxy, defaultFlags),
+                ["RangeError"] = new LazyPropertyDescriptor(() => Engine.RangeError, defaultFlags),
+                ["ReferenceError"] = new LazyPropertyDescriptor(() => Engine.ReferenceError, defaultFlags),
+                ["Reflect"] = new LazyPropertyDescriptor(() => Engine.Reflect, defaultFlags),
+                ["SyntaxError"] = new LazyPropertyDescriptor(() => Engine.SyntaxError, defaultFlags),
+                ["TypeError"] = new LazyPropertyDescriptor(() => Engine.TypeError, defaultFlags),
+                ["URIError"] = new LazyPropertyDescriptor(() => Engine.UriError, defaultFlags),
+                ["NaN"] = new PropertyDescriptor(double.NaN, PropertyFlag.None),
+                ["Infinity"] = new PropertyDescriptor(double.PositiveInfinity, PropertyFlag.None),
+                ["undefined"] = new PropertyDescriptor(Undefined, PropertyFlag.None),
+                ["parseInt"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "parseInt", ParseInt, 2, PropertyFlag.Configurable), defaultFlags),
+                ["parseFloat"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "parseFloat", ParseFloat, 1, PropertyFlag.Configurable), defaultFlags),
+                ["isNaN"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "isNaN", IsNaN, 1), defaultFlags),
+                ["isFinite"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "isFinite", IsFinite, 1), defaultFlags),
+                ["decodeURI"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "decodeURI", DecodeUri, 1, PropertyFlag.Configurable), defaultFlags),
+                ["decodeURIComponent"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "decodeURIComponent", DecodeUriComponent, 1, PropertyFlag.Configurable), defaultFlags),
+                ["encodeURI"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "encodeURI", EncodeUri, 1, PropertyFlag.Configurable), defaultFlags),
+                ["encodeURIComponent"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "encodeURIComponent", EncodeUriComponent, 1, PropertyFlag.Configurable), defaultFlags),
+                ["escape"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "escape", Escape, 1), defaultFlags),
+                ["unescape"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "unescape", Unescape, 1), defaultFlags),
+                ["globalThis"] = new PropertyDescriptor(this, defaultFlags),
+
+                // toString is not mentioned or actually required in spec, but some tests rely on it
+                ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToStringString, 1), defaultFlags)
             };
-            global.SetProperties(new StringDictionarySlim<PropertyDescriptor>(35), hasSymbols: false);
 
-            return global;
+            SetProperties(properties);
         }
 
-        protected override void Initialize()
+        private JsValue ToStringString(JsValue thisObj, JsValue[] arguments)
         {
-            // Global object properties
-            const PropertyFlag defaultFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
-            SetProperty("Object", new PropertyDescriptor(Engine.Object, defaultFlags));
-            SetProperty("Function", new PropertyDescriptor(Engine.Function, defaultFlags));
-            SetProperty("Symbol", new PropertyDescriptor(Engine.Symbol, defaultFlags));
-            SetProperty("Array", new PropertyDescriptor(Engine.Array, defaultFlags));
-            SetProperty("Map", new PropertyDescriptor(Engine.Map, defaultFlags));
-            SetProperty("Set", new PropertyDescriptor(Engine.Set, defaultFlags));
-            SetProperty("String", new PropertyDescriptor(Engine.String, defaultFlags));
-            SetProperty("RegExp", new PropertyDescriptor(Engine.RegExp, defaultFlags));
-            SetProperty("Number", new PropertyDescriptor(Engine.Number, defaultFlags));
-            SetProperty("Boolean", new PropertyDescriptor(Engine.Boolean, defaultFlags));
-            SetProperty("Date", new PropertyDescriptor(Engine.Date, defaultFlags));
-            SetProperty("Math", new PropertyDescriptor(Engine.Math, defaultFlags));
-            SetProperty("JSON", new PropertyDescriptor(Engine.Json, defaultFlags));
-            SetProperty("Error", new LazyPropertyDescriptor(() => Engine.Error, defaultFlags));
-            SetProperty("EvalError", new LazyPropertyDescriptor(() => Engine.EvalError, defaultFlags));
-            SetProperty("Proxy", new LazyPropertyDescriptor(() => Engine.Proxy, defaultFlags));
-            SetProperty("RangeError", new LazyPropertyDescriptor(() => Engine.RangeError, defaultFlags));
-            SetProperty("ReferenceError", new LazyPropertyDescriptor(() => Engine.ReferenceError, defaultFlags));
-            SetProperty("Reflect", new LazyPropertyDescriptor(() => Engine.Reflect, defaultFlags));
-            SetProperty("SyntaxError", new LazyPropertyDescriptor(() => Engine.SyntaxError, defaultFlags));
-            SetProperty("TypeError", new LazyPropertyDescriptor(() => Engine.TypeError, defaultFlags));
-            SetProperty("URIError", new LazyPropertyDescriptor(() => Engine.UriError, defaultFlags));
-            SetProperty("NaN", new PropertyDescriptor(double.NaN, PropertyFlag.None));
-            SetProperty("Infinity", new PropertyDescriptor(double.PositiveInfinity, PropertyFlag.None));
-            SetProperty("undefined", new PropertyDescriptor(Undefined, PropertyFlag.None));
-            SetProperty("parseInt", new PropertyDescriptor(new ClrFunctionInstance(Engine, "parseInt", ParseInt, 2, PropertyFlag.Configurable), defaultFlags));
-            SetProperty("parseFloat", new PropertyDescriptor(new ClrFunctionInstance(Engine, "parseFloat", ParseFloat, 1, PropertyFlag.Configurable), defaultFlags));
-            SetProperty("isNaN", new PropertyDescriptor(new ClrFunctionInstance(Engine, "isNaN", IsNaN, 1), defaultFlags));
-            SetProperty("isFinite", new PropertyDescriptor(new ClrFunctionInstance(Engine, "isFinite", IsFinite, 1), defaultFlags));
-            SetProperty("decodeURI", new PropertyDescriptor(new ClrFunctionInstance(Engine, "decodeURI", DecodeUri, 1, PropertyFlag.Configurable), defaultFlags));
-            SetProperty("decodeURIComponent", new PropertyDescriptor(new ClrFunctionInstance(Engine, "decodeURIComponent", DecodeUriComponent, 1, PropertyFlag.Configurable), defaultFlags));
-            SetProperty("encodeURI", new PropertyDescriptor(new ClrFunctionInstance(Engine, "encodeURI", EncodeUri, 1, PropertyFlag.Configurable), defaultFlags));
-            SetProperty("encodeURIComponent", new PropertyDescriptor(new ClrFunctionInstance(Engine, "encodeURIComponent", EncodeUriComponent, 1, PropertyFlag.Configurable), defaultFlags));
-            SetProperty("escape", new PropertyDescriptor(new ClrFunctionInstance(Engine, "escape", Escape, 1), defaultFlags));
-            SetProperty("unescape", new PropertyDescriptor(new ClrFunctionInstance(Engine, "unescape", Unescape, 1), defaultFlags));
+            return _engine.Object.PrototypeObject.ToObjectString(thisObj, Arguments.Empty);
         }
 
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.2
         /// </summary>
-        public JsValue ParseInt(JsValue thisObject, JsValue[] arguments)
+        public static JsValue ParseInt(JsValue thisObject, JsValue[] arguments)
         {
             string inputString = TypeConverter.ToString(arguments.At(0));
             var s = StringPrototype.TrimEx(inputString);
@@ -178,7 +185,7 @@ namespace Jint.Native.Global
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.3
         /// </summary>
-        public JsValue ParseFloat(JsValue thisObject, JsValue[] arguments)
+        public static JsValue ParseFloat(JsValue thisObject, JsValue[] arguments)
         {
             var inputString = TypeConverter.ToString(arguments.At(0));
             var trimmedString = StringPrototype.TrimStartEx(inputString);
@@ -696,5 +703,94 @@ namespace Jint.Native.Global
 
             return _stringBuilder.ToString();
         }
+        
+        // optimized versions with string parameter and without virtual dispatch for global environment usage
+
+        internal bool HasProperty(in Key property)
+        {
+            return GetOwnProperty(property) != PropertyDescriptor.Undefined;
+        }
+
+        internal PropertyDescriptor GetProperty(in Key property) => GetOwnProperty(property);
+
+        internal bool DefinePropertyOrThrow(in Key property, PropertyDescriptor desc)
+        {
+            if (!DefineOwnProperty(property, desc))
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+
+            return true;
+        }
+
+        internal bool DefineOwnProperty(in Key property, PropertyDescriptor desc)
+        {
+            var current = GetOwnProperty(property);
+            if (current == desc)
+            {
+                return true;
+            }
+            
+            // check fast path
+            if ((current._flags & PropertyFlag.MutableBinding) != 0)
+            {
+                current._value = desc.Value;
+                return true;
+            }
+
+            return ValidateAndApplyPropertyDescriptor(this, new JsString(property), true, desc, current);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal PropertyDescriptor GetOwnProperty(in Key property)
+        {
+            Properties.TryGetValue(property, out var descriptor);
+            return descriptor ?? PropertyDescriptor.Undefined;
+        }
+        
+        internal bool Set(in Key property, JsValue value)
+        {
+            // here we are called only from global environment record context
+            // we can take some shortcuts to be faster
+
+            if (!Properties.TryGetValue(property, out var existingDescriptor))
+            {
+                Properties[property] = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
+                return true;
+            }
+
+            if (existingDescriptor.IsDataDescriptor())
+            {
+                if (!existingDescriptor.Writable || existingDescriptor.IsAccessorDescriptor())
+                {
+                    return false;
+                }
+
+                // check fast path
+                if ((existingDescriptor._flags & PropertyFlag.MutableBinding) != 0)
+                {
+                    existingDescriptor._value = value;
+                    return true;
+                }
+
+                // slow path
+                return DefineOwnProperty(property, new PropertyDescriptor(value, PropertyFlag.None));
+            }
+
+            if (!(existingDescriptor.Set is ICallable setter))
+            {
+                return false;
+            }
+
+            setter.Call(this, new[] {value});
+
+            return true;
+        }
+        
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal void SetOwnProperty(in Key property, PropertyDescriptor desc)
+        {
+            SetProperty(in property, desc);
+        }
     }
 }

+ 12 - 14
Jint/Native/Iterator/IteratorInstance.cs

@@ -21,7 +21,7 @@ namespace Jint.Native.Iterator
 
         public IteratorInstance(
             Engine engine,
-            IEnumerable<JsValue> enumerable) : base(engine, "Iterator")
+            IEnumerable<JsValue> enumerable) : base(engine, ObjectClass.Iterator)
         {
             _enumerable = enumerable.GetEnumerator();
         }
@@ -53,8 +53,8 @@ namespace Jint.Native.Iterator
         private ObjectInstance CreateIterResultObject(JsValue value, bool done)
         {
             var obj = _engine.Object.Construct(2);
-            obj.CreateDataPropertyOrThrow("value", value);
-            obj.CreateDataPropertyOrThrow("done", done);
+            obj.SetDataProperty("value", value);
+            obj.SetDataProperty("done", done);
             return obj;
         }
 
@@ -70,11 +70,9 @@ namespace Jint.Native.Iterator
                     var arrayInstance = engine.Array.ConstructFast(2);
                     arrayInstance.SetIndexValue(0, key, false);
                     arrayInstance.SetIndexValue(1, value, false);
-                    SetOwnProperty("value", new PropertyDescriptor(arrayInstance, PropertyFlag.AllForbidden));
+                    SetProperty("value", new PropertyDescriptor(arrayInstance, PropertyFlag.AllForbidden));
                 }
-                SetOwnProperty(
-                    "done",
-                    done ? PropertyDescriptor.AllForbiddenDescriptor.BooleanTrue : PropertyDescriptor.AllForbiddenDescriptor.BooleanFalse);
+                SetProperty("done", done ? PropertyDescriptor.AllForbiddenDescriptor.BooleanTrue : PropertyDescriptor.AllForbiddenDescriptor.BooleanFalse);
             }
         }
 
@@ -87,9 +85,9 @@ namespace Jint.Native.Iterator
                 var done = ReferenceEquals(null, value);
                 if (!done)
                 {
-                    SetOwnProperty("value", new PropertyDescriptor(value, PropertyFlag.AllForbidden));
+                    SetProperty("value", new PropertyDescriptor(value, PropertyFlag.AllForbidden));
                 }
-                SetOwnProperty("done", new PropertyDescriptor(done, PropertyFlag.AllForbidden));
+                SetProperty("done", new PropertyDescriptor(done, PropertyFlag.AllForbidden));
             }
         }
 
@@ -288,7 +286,7 @@ namespace Jint.Native.Iterator
             public ObjectWrapper(ObjectInstance target)
             {
                 _target = target;
-                _callable = (ICallable) target.Get("next", target);
+                _callable = (ICallable) target.Get(CommonProperties.Next, target);
             }
 
             public ObjectInstance Next()
@@ -298,7 +296,7 @@ namespace Jint.Native.Iterator
 
             public void Return()
             {
-                if (_target.TryGetValue("return", out var func))
+                if (_target.TryGetValue(CommonProperties.Return, out var func))
                 {
                     ((ICallable) func).Call(_target, Arguments.Empty);
                 }
@@ -369,12 +367,12 @@ namespace Jint.Native.Iterator
 
                 if (_global)
                 {
-                    var macthStr = TypeConverter.ToString(match.Get("0"));
+                    var macthStr = TypeConverter.ToString(match.Get(JsString.NumberZeroString));
                     if (macthStr == "")
                     {
-                        var thisIndex = TypeConverter.ToLength(_iteratingRegExp.Get(RegExpInstance.KeyLastIndex));
+                        var thisIndex = TypeConverter.ToLength(_iteratingRegExp.Get(RegExpInstance.PropertyLastIndex));
                         var nextIndex = thisIndex + 1;
-                        _iteratingRegExp.Set(RegExpInstance.KeyLastIndex, nextIndex, true);
+                        _iteratingRegExp.Set(RegExpInstance.PropertyLastIndex, nextIndex, true);
                     }
                 }
                 else

+ 3 - 2
Jint/Native/Iterator/IteratorProtocol.cs

@@ -1,4 +1,5 @@
 using Jint.Native.Array;
+using Jint.Runtime;
 
 namespace Jint.Native.Iterator
 {
@@ -29,12 +30,12 @@ namespace Jint.Native.Iterator
                 do
                 {
                     var item = _iterator.Next();
-                    if (item.TryGetValue("done", out var done) && done.AsBoolean())
+                    if (item.TryGetValue(CommonProperties.Done, out var done) && done.AsBoolean())
                     {
                         break;
                     }
 
-                    if (!item.TryGetValue("value", out var currentValue))
+                    if (!item.TryGetValue(CommonProperties.Value, out var currentValue))
                     {
                         currentValue = JsValue.Undefined;
                     }

+ 8 - 3
Jint/Native/Iterator/IteratorPrototype.cs

@@ -27,16 +27,21 @@ namespace Jint.Native.Iterator
 
         protected override void Initialize()
         {
-            var properties = new StringDictionarySlim<PropertyDescriptor>(3)
+            var properties = new PropertyDictionary(2, checkExistingKeys: false)
             {
                 ["name"] = new PropertyDescriptor("Map", PropertyFlag.Configurable),
                 ["next"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "next", Next, 0, PropertyFlag.Configurable), true, false, true)
             };
+            SetProperties(properties);
+
             if (_name != null)
             {
-                properties[GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor(_name, PropertyFlag.Configurable);
+                var symbols = new SymbolDictionary(1)
+                {
+                    [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor(_name, PropertyFlag.Configurable)
+                };
+                SetSymbols(symbols);
             }
-            SetProperties(properties, hasSymbols: true);
         }
 
         private JsValue Next(JsValue thisObj, JsValue[] arguments)

+ 1 - 1
Jint/Native/JsBoolean.cs

@@ -25,7 +25,7 @@ namespace Jint.Native
 
         public override string ToString()
         {
-            return _value ? bool.TrueString : bool.FalseString;
+            return _value ? "true" : "false";
         }
 
         public override bool Equals(JsValue obj)

+ 5 - 3
Jint/Native/JsNumber.cs

@@ -19,7 +19,7 @@ namespace Jint.Native
         // we can cache most common values, doubles are used in indexing too at times so we also cache
         // integer values converted to doubles
         private const int NumbersMax = 1024 * 10;
-        private static readonly JsNumber[] _intToJsValue = new JsNumber[NumbersMax];
+        private static readonly JsNumber[] _intToJsValue;
 
         internal static readonly JsNumber DoubleNaN = new JsNumber(double.NaN);
         internal static readonly JsNumber DoubleNegativeOne = new JsNumber((double) -1);
@@ -34,10 +34,12 @@ namespace Jint.Native
 
         static JsNumber()
         {
-            for (int i = 0; i < NumbersMax; i++)
+            var integers = new JsNumber[NumbersMax];
+            for (uint i = 0; i < (uint) integers.Length; i++)
             {
-                _intToJsValue[i] = new JsNumber(i);
+                integers[i] = new JsNumber(i);
             }
+            _intToJsValue = integers;
         }
 
         public JsNumber(double value) : base(Types.Number)

+ 136 - 19
Jint/Native/JsString.cs

@@ -10,6 +10,7 @@ namespace Jint.Native
         private const int AsciiMax = 126;
         private static readonly JsString[] _charToJsValue;
         private static readonly JsString[] _charToStringJsValue;
+        private static readonly JsString[] _intToStringJsValue;
 
         public static readonly JsString Empty = new JsString("");
         private static readonly JsString NullString = new JsString("null");
@@ -20,8 +21,11 @@ namespace Jint.Native
         internal static readonly JsString StringString = new JsString("string");
         internal static readonly JsString NumberString = new JsString("number");
         internal static readonly JsString SymbolString = new JsString("symbol");
-        internal static readonly JsString LengthString = new JsString("length");
         internal static readonly JsString DefaultString = new JsString("default");
+        internal static readonly JsString NumberZeroString = new JsString("0");
+        internal static readonly JsString NumberOneString = new JsString("1");
+        internal static readonly JsString TrueString = new JsString("true");
+        internal static readonly JsString FalseString = new JsString("false");
 
         internal string _value;
 
@@ -30,14 +34,24 @@ namespace Jint.Native
             _charToJsValue = new JsString[AsciiMax + 1];
             _charToStringJsValue = new JsString[AsciiMax + 1];
 
-            for (int i = 0; i <= AsciiMax; i++)
+            for (var i = 0; i <= AsciiMax; i++)
             {
                 _charToJsValue[i] = new JsString((char) i);
                 _charToStringJsValue[i] = new JsString(((char) i).ToString());
             }
+
+            _intToStringJsValue = new JsString[1024];
+            for (var i = 0; i < _intToStringJsValue.Length; ++i)
+            {
+                _intToStringJsValue[i] = new JsString(TypeConverter.ToString(i));
+            }
+        }
+
+        public JsString(string value) : this(value, InternalTypes.String)
+        {
         }
 
-        public JsString(string value) : base(Types.String)
+        private JsString(string value, InternalTypes type) : base(type)
         {
             _value = value;
         }
@@ -52,6 +66,48 @@ namespace Jint.Native
             _value = value.ToString();
         }
 
+        public static bool operator ==(JsValue a, JsString b)
+        {
+            if (a is JsString s && b is object)
+            {
+                return s.ToString() == b.ToString();
+            }
+
+            if ((object) a == null)
+            {
+                return (object) b == null;
+            }
+
+            return (object) b != null && a.Equals(b);
+        }
+
+        public static bool operator ==(JsString a, JsValue b)
+        {
+            if (a is object && b is JsString s)
+            {
+                return s.ToString() == b.ToString();
+            }
+
+            if ((object) a == null)
+            {
+                return (object) b == null;
+            }
+
+            return (object) b != null && a.Equals(b);
+        }
+
+        public static bool operator !=(JsString a, JsValue b)
+        {
+            return !(a == b);
+        }
+
+        public static bool operator !=(JsValue a, JsString b)
+        {
+            return !(a == b);
+        }
+
+        public virtual char this[int index] => _value[index];
+
         public virtual JsString Append(JsValue jsValue)
         {
             return new ConcatenatedString(string.Concat(_value, TypeConverter.ToString(jsValue)));
@@ -71,23 +127,21 @@ namespace Jint.Native
 
         internal static JsString Create(string value)
         {
-            if (value.Length == 0)
+            if (value.Length > 1)
             {
-                return Empty;
+                return new JsString(value);
             }
-            if (value.Length == 1)
+
+            if (value.Length == 0)
             {
-                var i = (uint) value[0];
-                if (i < (uint) _charToStringJsValue.Length)
-                {
-                    return _charToStringJsValue[i];
-                }
+                return Empty;
             }
-            else if (value.Length == 4 && value == Native.Null.Text)
+
+            var i = (uint) value[0];
+            if (i < (uint) _charToStringJsValue.Length)
             {
-                return NullString;
+                return _charToStringJsValue[i];
             }
-
             return new JsString(value);
         }
 
@@ -101,9 +155,34 @@ namespace Jint.Native
             return new JsString(value);
         }
 
-        internal override Key ToPropertyKey()
+        internal static JsString Create(int value)
+        {
+            if (value < (uint) _intToStringJsValue.Length)
+            {
+                return _intToStringJsValue[value];
+            }
+
+            return new JsString(TypeConverter.ToString(value));
+        }
+
+        internal static JsValue Create(uint value)
         {
-            return new Key(_value);
+            if (value < (uint) _intToStringJsValue.Length)
+            {
+                return _intToStringJsValue[value];
+            }
+
+            return new JsString(TypeConverter.ToString(value));
+        }
+
+        internal static JsValue Create(ulong value)
+        {
+            if (value < (uint) _intToStringJsValue.Length)
+            {
+                return _intToStringJsValue[value];
+            }
+
+            return new JsString(TypeConverter.ToString(value));
         }
 
         public override string ToString()
@@ -122,6 +201,26 @@ namespace Jint.Native
             return array;
         }
 
+        internal int IndexOf(string value, StringComparison comparisonType)
+        {
+            return ToString().IndexOf(value, comparisonType);
+        }
+
+        internal int IndexOf(char value)
+        {
+            return ToString().IndexOf(value);
+        }
+
+        internal string Substring(int startIndex, int length)
+        {
+            return ToString().Substring(startIndex, length);
+        }
+
+        internal string Substring(int startIndex)
+        {
+            return ToString().Substring(startIndex);
+        }
+
         public override bool Equals(JsValue obj)
         {
             if (ReferenceEquals(null, obj))
@@ -152,12 +251,23 @@ namespace Jint.Native
             return _value == other.ToString();
         }
 
+        public override bool Equals(object obj)
+        {
+            return ReferenceEquals(this, obj) || obj is JsString other && Equals(other);
+        }
+
+        public override int GetHashCode()
+        {
+            return _value.GetHashCode();
+        }
+
         internal sealed class ConcatenatedString : JsString
         {
             private StringBuilder _stringBuilder;
             private bool _dirty;
 
-            internal ConcatenatedString(string value, int capacity = 0) : base(value)
+            internal ConcatenatedString(string value, int capacity = 0)
+                : base(value, InternalTypes.String | InternalTypes.RequiresCloning)
             {
                 if (capacity > 0)
                 {
@@ -180,6 +290,8 @@ namespace Jint.Native
                 return _value;
             }
 
+            public override char this[int index] => _stringBuilder?[index] ?? _value[index];
+
             public override JsString Append(JsValue jsValue)
             {
                 var value = TypeConverter.ToString(jsValue);
@@ -235,9 +347,14 @@ namespace Jint.Native
                 return base.Equals(other);
             }
 
-            internal override JsValue Clone()
+            public override int GetHashCode()
+            {
+                return _stringBuilder?.GetHashCode() ?? _value.GetHashCode();
+            }
+
+            internal override JsValue DoClone()
             {
-                return ToString();
+                return new JsString(ToString());
             }
         }
     }

+ 6 - 11
Jint/Native/JsSymbol.cs

@@ -10,24 +10,19 @@ namespace Jint.Native
     public sealed class JsSymbol : JsValue, IEquatable<JsSymbol>
     {
         internal readonly JsValue _value;
-        private readonly Key _key;
 
-        internal JsSymbol(JsValue value, int identity) : base(Types.Symbol)
+        internal JsSymbol(string value) : this(new JsString(value))
         {
-            _value = value;
-            _key = new Key(value.IsUndefined() 
-                ? ""
-                : TypeConverter.ToString(value), identity);
         }
 
-        public override object ToObject()
+        internal JsSymbol(JsValue value) : base(Types.Symbol)
         {
-            return _value;
+            _value = value;
         }
 
-        internal override Key ToPropertyKey()
+        public override object ToObject()
         {
-            return _key;
+            return _value;
         }
 
         public override string ToString()
@@ -43,7 +38,7 @@ namespace Jint.Native
 
         public bool Equals(JsSymbol other)
         {
-            return other != null && other._key == _key;
+            return ReferenceEquals(this, other);
         }
 
         public override int GetHashCode()

+ 37 - 50
Jint/Native/JsValue.cs

@@ -38,7 +38,7 @@ namespace Jint.Native
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsPrimitive()
         {
-            return _type < InternalTypes.Object;
+            return (_type & (InternalTypes.Primitive | InternalTypes.Undefined | InternalTypes.Null)) != 0;
         }
 
         [Pure]
@@ -90,21 +90,21 @@ namespace Jint.Native
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsObject()
         {
-            return _type == InternalTypes.Object;
+            return (_type & InternalTypes.Object) != 0;
         }
 
         [Pure]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsString()
         {
-            return _type == InternalTypes.String;
+            return (_type & InternalTypes.String) != 0;
         }
 
         [Pure]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsNumber()
         {
-            return _type == InternalTypes.Number || _type == InternalTypes.Integer;
+            return (_type & (InternalTypes.Number | InternalTypes.Integer)) != 0;
         }
 
         [Pure]
@@ -271,7 +271,9 @@ namespace Jint.Native
         public Types Type
         {
             [MethodImpl(MethodImplOptions.AggressiveInlining)]
-            get => _type == InternalTypes.Integer ? Types.Number : (Types) _type;
+            get => _type == InternalTypes.Integer
+                ? Types.Number
+                : (Types) (_type & ~InternalTypes.InternalFlags);
         }
 
         internal virtual bool IsConstructor => this is IConstructor;
@@ -371,7 +373,7 @@ namespace Jint.Native
                 jsArray.WriteArrayValue(i, new PropertyDescriptor(jsItem, PropertyFlag.ConfigurableEnumerableWritable));
             }
 
-            jsArray.SetOwnProperty(KnownKeys.Length, new PropertyDescriptor(arrayLength, PropertyFlag.OnlyWritable));
+            jsArray.SetOwnProperty(CommonProperties.Length, new PropertyDescriptor(arrayLength, PropertyFlag.OnlyWritable));
 
             return jsArray;
         }
@@ -411,7 +413,7 @@ namespace Jint.Native
         /// <param name="propertyName">Property that should be ICallable</param>
         /// <param name="arguments">The arguments of the function call.</param>
         /// <returns>The value returned by the function call.</returns>
-        internal static JsValue Invoke(JsValue v, in Key propertyName, JsValue[] arguments)
+        internal static JsValue Invoke(JsValue v, JsValue propertyName, JsValue[] arguments)
         {
             var func = v.Get(propertyName);
             var callable = func as ICallable ?? ExceptionHelper.ThrowTypeErrorNoEngine<ICallable>("Can only invoke functions");
@@ -419,15 +421,15 @@ namespace Jint.Native
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public JsValue Get(in Key propertyName)
+        public JsValue Get(JsValue property)
         {
-            return Get(propertyName, this);
+            return Get(property, this);
         }
 
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.3
         /// </summary>
-        public virtual JsValue Get(in Key propertyName, JsValue receiver)
+        public virtual JsValue Get(JsValue property, JsValue receiver)
         {
             return Undefined;
         }
@@ -435,16 +437,11 @@ namespace Jint.Native
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver
         /// </summary>
-        public virtual bool Set(in Key propertyName, JsValue value, JsValue receiver)
+        public virtual bool Set(JsValue property, JsValue value, JsValue receiver)
         {
             return ExceptionHelper.ThrowNotSupportedException<bool>();
         }
 
-        internal virtual Key ToPropertyKey()
-        {
-            return new Key(ToString());
-        }
-
         public override string ToString()
         {
             return "None";
@@ -452,22 +449,12 @@ namespace Jint.Native
 
         public static bool operator ==(JsValue a, JsValue b)
         {
-            if ((object)a == null)
-            {
-                if ((object)b == null)
-                {
-                    return true;
-                }
-
-                return false;
-            }
-
-            if ((object)b == null)
+            if ((object) a == null)
             {
-                return false;
+                return (object) b == null;
             }
 
-            return a.Equals(b);
+            return (object) b != null && a.Equals(b);
         }
 
         public static bool operator !=(JsValue a, JsValue b)
@@ -588,7 +575,8 @@ namespace Jint.Native
                         Value = value.AsObject().GetType().Name;
                         break;
                     case Types.Symbol:
-                        Value = value.AsSymbol() + " (symbol)";
+                        var jsValue = ((JsSymbol) value)._value;
+                        Value = (jsValue.IsUndefined() ? "" : jsValue.ToString()) + " (symbol)";
                         break;
                     default:
                         Value = "Unknown";
@@ -600,11 +588,20 @@ namespace Jint.Native
         /// <summary>
         /// Some values need to be cloned in order to be assigned, like ConcatenatedString.
         /// </summary>
-        internal virtual JsValue Clone()
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal JsValue Clone()
+        {
+            // concatenated string and arguments currently may require cloning
+            return (_type & InternalTypes.RequiresCloning) == 0
+                ? this
+                : DoClone();
+        }
+
+        internal virtual JsValue DoClone()
         {
             return this;
         }
-        
+
         internal static bool SameValue(JsValue x, JsValue y)
         {
             var typea = TypeConverter.GetInternalPrimitiveType(x);
@@ -612,27 +609,17 @@ namespace Jint.Native
 
             if (typea != typeb)
             {
-                if (typea == InternalTypes.Integer)
-                {
-                    typea = InternalTypes.Number;
-                }
-                if (typeb == InternalTypes.Integer)
-                {
-                    typeb = InternalTypes.Number;
-                }
-
-                if (typea != typeb)
-                {
-                    return false;
-                }
+                return false;
             }
 
             switch (typea)
             {
-                case InternalTypes.Integer:
-                    return x.AsInteger() == y.AsInteger();
+                case Types.Number:
+                    if (x._type == y._type && x._type == InternalTypes.Integer)
+                    {
+                        return x.AsInteger() == y.AsInteger();
+                    }
 
-                case InternalTypes.Number:
                     var nx = TypeConverter.ToNumber(x);
                     var ny = TypeConverter.ToNumber(y);
 
@@ -653,9 +640,9 @@ namespace Jint.Native
                     }
 
                     return false;
-                case InternalTypes.String:
+                case Types.String:
                     return TypeConverter.ToString(x) == TypeConverter.ToString(y);
-                case InternalTypes.Boolean:
+                case Types.Boolean:
                     return TypeConverter.ToBoolean(x) == TypeConverter.ToBoolean(y);
                 default:
                     return x == y;

+ 12 - 17
Jint/Native/Json/JsonInstance.cs

@@ -11,7 +11,7 @@ namespace Jint.Native.Json
         private JsValue _reviver;
 
         private JsonInstance(Engine engine)
-            : base(engine, objectClass: "JSON")
+            : base(engine, objectClass: ObjectClass.JSON)
         {
         }
 
@@ -26,37 +26,37 @@ namespace Jint.Native.Json
 
         protected override void Initialize()
         {
-            var properties = new StringDictionarySlim<PropertyDescriptor>(2)
+            var properties = new PropertyDictionary(2, checkExistingKeys: false)
             {
                 ["parse"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "parse", Parse, 2), true, false, true),
                 ["stringify"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "stringify", Stringify, 3), true, false, true)
             };
-            SetProperties(properties, hasSymbols: false);
+            SetProperties(properties);
         }
 
-        private JsValue AbstractWalkOperation(ObjectInstance thisObject, string prop)
+        private JsValue AbstractWalkOperation(ObjectInstance thisObject, JsValue prop)
         {
             JsValue value = thisObject.Get(prop, thisObject);
             if (value.IsObject())
             {
                 var valueAsObject = value.AsObject();
-                if (valueAsObject.Class == "Array")
+                if (valueAsObject.Class == ObjectClass.Array)
                 {
                     var valAsArray = value.AsArray();
                     var i = 0;
                     var arrLen = valAsArray.GetLength();
                     while (i < arrLen)
                     {
-                        var newValue = AbstractWalkOperation(valAsArray, TypeConverter.ToString(i));
+                        var newValue = AbstractWalkOperation(valAsArray, JsString.Create(i));
                         if (newValue.IsUndefined())
                         {
-                            valAsArray.Delete(TypeConverter.ToString(i));
+                            valAsArray.Delete(JsString.Create(i));
                         }
                         else
                         {
                             valAsArray.DefineOwnProperty
                             (
-                                TypeConverter.ToString(i),
+                                JsString.Create(i),
                                 new PropertyDescriptor
                                 (
                                     value: newValue,
@@ -98,15 +98,10 @@ namespace Jint.Native.Json
             var res = parser.Parse(TypeConverter.ToString(arguments[0]));
             if (arguments.Length > 1)
             {
-                this._reviver = arguments[1];
-                ObjectInstance revRes = ObjectConstructor.CreateObjectConstructor(_engine).Construct(Arguments.Empty);
-                revRes.DefineOwnProperty(
-                    "",
-                    new PropertyDescriptor(
-                        value: res,
-                        PropertyFlag.ConfigurableEnumerableWritable
-                    ));
-                return AbstractWalkOperation(revRes, "");
+                _reviver = arguments[1];
+                ObjectInstance revRes = _engine.Object.Construct(Arguments.Empty);
+                revRes.SetProperty("", new PropertyDescriptor(value: res, PropertyFlag.ConfigurableEnumerableWritable));
+                return AbstractWalkOperation(revRes, JsString.Empty);
             }
             return res;
         }

+ 1 - 1
Jint/Native/Json/JsonParser.cs

@@ -767,7 +767,7 @@ namespace Jint.Native.Json
                     return (bool) Lex().Value ? JsBoolean.True : JsBoolean.False;
                 case Tokens.String:
                     // implicit conversion operator goes through caching
-                    return (string) Lex().Value;
+                    return new JsString((string) Lex().Value);
                 case Tokens.Number:
                     return (double) Lex().Value;
             }

+ 19 - 19
Jint/Native/Json/JsonSerializer.cs

@@ -19,7 +19,7 @@ namespace Jint.Native.Json
 
         Stack<object> _stack;
         string _indent, _gap;
-        List<Key> _propertyList;
+        List<JsValue> _propertyList;
         JsValue _replacerFunction = Undefined.Instance;
 
         public JsValue Serialize(JsValue value, JsValue replacer, JsValue space)
@@ -42,9 +42,9 @@ namespace Jint.Native.Json
                 else
                 {
                     var replacerObj = replacer.AsObject();
-                    if (replacerObj.Class == "Array")
+                    if (replacerObj.Class == ObjectClass.Array)
                     {
-                        _propertyList = new List<Key>();
+                        _propertyList = new List<JsValue>();
                     }
 
                     foreach (var property in replacerObj.GetOwnProperties().Select(x => x.Value))
@@ -62,7 +62,7 @@ namespace Jint.Native.Json
                         else if (v.IsObject())
                         {
                             var propertyObj = v.AsObject();
-                            if (propertyObj.Class == "String" || propertyObj.Class == "Number")
+                            if (propertyObj.Class == ObjectClass.String || propertyObj.Class == ObjectClass.Number)
                             {
                                 item = TypeConverter.ToString(v);
                             }
@@ -80,13 +80,13 @@ namespace Jint.Native.Json
             if (space.IsObject())
             {
                 var spaceObj = space.AsObject();
-                if (spaceObj.Class == "Number")
+                if (spaceObj.Class == ObjectClass.Number)
                 {
                     space = TypeConverter.ToNumber(spaceObj);
                 }
-                else if (spaceObj.Class == "String")
+                else if (spaceObj.Class == ObjectClass.String)
                 {
-                    space = TypeConverter.ToString(spaceObj);
+                    space = TypeConverter.ToJsString(spaceObj);
                 }
             }
 
@@ -114,12 +114,12 @@ namespace Jint.Native.Json
             }
 
             var wrapper = _engine.Object.Construct(Arguments.Empty);
-            wrapper.DefineOwnProperty("", new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable));
+            wrapper.DefineOwnProperty(JsString.Empty, new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable));
 
-            return Str("", wrapper);
+            return Str(JsString.Empty, wrapper);
         }
 
-        private JsValue Str(string key, ObjectInstance holder)
+        private JsValue Str(JsValue key, ObjectInstance holder)
         {
 
             var value = holder.Get(key, holder);
@@ -147,19 +147,19 @@ namespace Jint.Native.Json
                 var valueObj = value.AsObject();
                 switch (valueObj.Class)
                 {
-                    case "Number":
+                    case ObjectClass.Number:
                         value = TypeConverter.ToNumber(value);
                         break;
-                    case "String":
+                    case ObjectClass.String:
                         value = TypeConverter.ToString(value);
                         break;
-                    case "Boolean":
+                    case ObjectClass.Boolean:
                         value = TypeConverter.ToPrimitive(value);
                         break;
-                    case "Array":
+                    case ObjectClass.Array:
                         value = SerializeArray(value.As<ArrayInstance>());
                         return value;
-                    case "Object":
+                    case ObjectClass.Object:
                         value = SerializeObject(value.AsObject());
                         return value;
                 }
@@ -185,7 +185,7 @@ namespace Jint.Native.Json
                 var isFinite = GlobalObject.IsFinite(Undefined.Instance, Arguments.From(value));
                 if (((JsBoolean) isFinite)._value)
                 {
-                    return TypeConverter.ToString(((JsNumber) value)._value);
+                    return TypeConverter.ToJsString(value);
                 }
 
                 return "null";
@@ -195,7 +195,7 @@ namespace Jint.Native.Json
 
             if (value.IsObject() && isCallable == false)
             {
-                if (value.AsObject().Class == "Array")
+                if (value.AsObject().Class == ObjectClass.Array)
                 {
                     return SerializeArray(value.As<ArrayInstance>());
                 }
@@ -258,7 +258,7 @@ namespace Jint.Native.Json
             var stepback = _indent;
             _indent = _indent + _gap;
             var partial = new List<string>();
-            var len = TypeConverter.ToUint32(value.Get("length", value));
+            var len = TypeConverter.ToUint32(value.Get(CommonProperties.Length, value));
             for (int i = 0; i < len; i++)
             {
                 var strP = Str(TypeConverter.ToString(i), value);
@@ -324,7 +324,7 @@ namespace Jint.Native.Json
                 var strP = Str(p, value);
                 if (!strP.IsUndefined())
                 {
-                    var member = Quote(p) + ":";
+                    var member = Quote(p.ToString()) + ":";
                     if (_gap != "")
                     {
                         member += " ";

+ 5 - 5
Jint/Native/Map/MapConstructor.cs

@@ -41,11 +41,11 @@ namespace Jint.Native.Map
 
         protected override void Initialize()
         {
-            var properties = new StringDictionarySlim<PropertyDescriptor>(2)
+            var symbols = new SymbolDictionary(1)
             {
                 [GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(_engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable), set: Undefined, PropertyFlag.Configurable)
             };
-            SetProperties(properties, hasSymbols: true);
+            SetSymbols(symbols);
         }
 
         private static JsValue Species(JsValue thisObject, JsValue[] arguments)
@@ -91,7 +91,7 @@ namespace Jint.Native.Map
                 IIterator iterator) : base(engine, iterator, 2)
             {
                 _instance = instance;
-                var setterProperty = instance.GetProperty("set");
+                var setterProperty = instance.GetProperty(CommonProperties.Set);
 
                 if (setterProperty is null
                     || !setterProperty.TryGetValue(instance, out var setterValue)
@@ -109,8 +109,8 @@ namespace Jint.Native.Map
                     return;
                 }
 
-                oi.TryGetValue("0", out var key);
-                oi.TryGetValue("1", out var value);
+                oi.TryGetValue(JsString.NumberZeroString, out var key);
+                oi.TryGetValue(JsString.NumberOneString, out var value);
 
                 args[0] = key;
                 args[1] = value;

+ 16 - 16
Jint/Native/Map/MapInstance.cs

@@ -10,53 +10,53 @@ namespace Jint.Native.Map
         internal readonly OrderedDictionary<JsValue, JsValue> _map;
 
         public MapInstance(Engine engine)
-            : base(engine, objectClass: "Map")
+            : base(engine, objectClass: ObjectClass.Map)
         {
             _map = new OrderedDictionary<JsValue, JsValue>();
         }
 
-        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
+        public override PropertyDescriptor GetOwnProperty(JsValue property)
         {
-            if (propertyName == KnownKeys.Size)
+            if (property == CommonProperties.Size)
             {
                 return new PropertyDescriptor(_map.Count, PropertyFlag.None);
             }
 
-            return base.GetOwnProperty(propertyName);
+            return base.GetOwnProperty(property);
         }
 
-        protected override bool TryGetProperty(in Key propertyName, out PropertyDescriptor descriptor)
+        protected override bool TryGetProperty(JsValue property, out PropertyDescriptor descriptor)
         {
-            if (propertyName == KnownKeys.Size)
+            if (property == CommonProperties.Size)
             {
                 descriptor = new PropertyDescriptor(_map.Count, PropertyFlag.None);
                 return true;
             }
 
-            return base.TryGetProperty(propertyName, out descriptor);
+            return base.TryGetProperty(property, out descriptor);
         }
 
-        public void Clear()
+        internal void Clear()
         {
             _map.Clear();
         }
 
-        public bool Has(JsValue key)
+        internal bool Has(JsValue key)
         {
             return _map.ContainsKey(key);
         }
 
-        public bool Delete(JsValue key)
+        internal bool MapDelete(JsValue key)
         {
             return _map.Remove(key);
         }
 
-        public void Set(JsValue key, JsValue value)
+        internal void MapSet(JsValue key, JsValue value)
         {
             _map[key] = value;
         }
 
-        public void ForEach(ICallable callable, JsValue thisArg)
+        internal void ForEach(ICallable callable, JsValue thisArg)
         {
             var args = _engine._jsValueArrayPool.RentArray(3);
             args[2] = this;
@@ -71,7 +71,7 @@ namespace Jint.Native.Map
             _engine._jsValueArrayPool.ReturnArray(args);
         }
 
-        public JsValue Get(JsValue key)
+        internal JsValue MapGet(JsValue key)
         {
             if (!_map.TryGetValue(key, out var value))
             {
@@ -81,17 +81,17 @@ namespace Jint.Native.Map
             return value;
         }
 
-        public ObjectInstance Iterator()
+        internal ObjectInstance Iterator()
         {
             return _engine.Iterator.Construct(this);
         }
 
-        public ObjectInstance Keys()
+        internal ObjectInstance Keys()
         {
             return _engine.Iterator.Construct(_map.Keys);
         }
 
-        public ObjectInstance Values()
+        internal ObjectInstance Values()
         {
             return _engine.Iterator.Construct(_map.Values);
         }

+ 14 - 10
Jint/Native/Map/MapPrototype.cs

@@ -33,12 +33,10 @@ namespace Jint.Native.Map
         protected override void Initialize()
         {
             const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
-            var properties = new StringDictionarySlim<PropertyDescriptor>(15)
+            var properties = new PropertyDictionary(12, checkExistingKeys: false)
             {
-                [KnownKeys.Length] = new PropertyDescriptor(0, PropertyFlag.Configurable),
-                [KnownKeys.Constructor] = new PropertyDescriptor(_mapConstructor, PropertyFlag.NonEnumerable),
-                [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "iterator", Iterator, 1, PropertyFlag.Configurable), propertyFlags),
-                [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("Map", false, false, true),
+                ["length"] = new PropertyDescriptor(0, PropertyFlag.Configurable),
+                ["constructor"] = new PropertyDescriptor(_mapConstructor, PropertyFlag.NonEnumerable),
                 ["clear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "clear", Clear, 0, PropertyFlag.Configurable), propertyFlags),
                 ["delete"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "delete", Delete, 1, PropertyFlag.Configurable), propertyFlags),
                 ["entries"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "entries", Iterator, 0, PropertyFlag.Configurable), propertyFlags),
@@ -50,8 +48,14 @@ namespace Jint.Native.Map
                 ["values"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "values", Values, 0, PropertyFlag.Configurable), propertyFlags),
                 ["size"] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(Engine, "get size", Size, 0, PropertyFlag.Configurable), set: null, PropertyFlag.Configurable)
             };
-            
-            SetProperties(properties, hasSymbols: true);
+            SetProperties(properties);
+
+            var symbols = new SymbolDictionary(2)
+            {
+                [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "iterator", Iterator, 1, PropertyFlag.Configurable), propertyFlags),
+                [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("Map", false, false, true),
+            };
+            SetSymbols(symbols);
         }
 
         private JsValue Size(JsValue thisObj, JsValue[] arguments)
@@ -63,7 +67,7 @@ namespace Jint.Native.Map
         private JsValue Get(JsValue thisObj, JsValue[] arguments)
         {
             var map = AssertMapInstance(thisObj);
-            return map.Get(arguments.At(0));
+            return map.MapGet(arguments.At(0));
         }
 
         private JsValue Clear(JsValue thisObj, JsValue[] arguments)
@@ -76,7 +80,7 @@ namespace Jint.Native.Map
         private JsValue Delete(JsValue thisObj, JsValue[] arguments)
         {
             var map = AssertMapInstance(thisObj);
-            return map.Delete(arguments[0])
+            return map.MapDelete(arguments[0])
                 ? JsBoolean.True
                 : JsBoolean.False;
         }
@@ -84,7 +88,7 @@ namespace Jint.Native.Map
         private JsValue Set(JsValue thisObj, JsValue[] arguments)
         {
             var map = AssertMapInstance(thisObj);
-            map.Set(arguments[0], arguments[1]);
+            map.MapSet(arguments[0], arguments[1]);
             return thisObj;
         }
 

+ 10 - 6
Jint/Native/Math/MathInstance.cs

@@ -13,7 +13,7 @@ namespace Jint.Native.Math
     {
         private Random _random;
 
-        private MathInstance(Engine engine) : base(engine, "Math")
+        private MathInstance(Engine engine) : base(engine, ObjectClass.Math)
         {
         }
 
@@ -29,7 +29,7 @@ namespace Jint.Native.Math
 
         protected override void Initialize()
         {
-            var properties = new StringDictionarySlim<PropertyDescriptor>(45)
+            var properties = new PropertyDictionary(45, checkExistingKeys: false)
             {
                 ["abs"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "abs", Abs, 1, PropertyFlag.Configurable), true, false, true),
                 ["acos"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "acos", Acos, 1, PropertyFlag.Configurable), true, false, true),
@@ -73,11 +73,15 @@ namespace Jint.Native.Math
                 ["LOG10E"] = new PropertyDescriptor(System.Math.Log(System.Math.E, 10), false, false, false),
                 ["PI"] = new PropertyDescriptor(System.Math.PI, false, false, false),
                 ["SQRT1_2"] = new PropertyDescriptor(System.Math.Sqrt(0.5), false, false, false),
-                ["SQRT2"] = new PropertyDescriptor(System.Math.Sqrt(2), false, false, false),
-                [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("Math", PropertyFlag.Configurable)
+                ["SQRT2"] = new PropertyDescriptor(System.Math.Sqrt(2), false, false, false)
             };
-            
-            SetProperties(properties, hasSymbols: true);
+            SetProperties(properties);
+
+            var symbols = new SymbolDictionary(1)
+            {
+                [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor(new JsString("Math"), PropertyFlag.Configurable)
+            };
+            SetSymbols(symbols);
         }
 
         private static JsValue Abs(JsValue thisObject, JsValue[] arguments)

+ 5 - 5
Jint/Native/Number/NumberConstructor.cs

@@ -1,5 +1,6 @@
 using Jint.Collections;
 using Jint.Native.Function;
+using Jint.Native.Global;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
@@ -40,7 +41,7 @@ namespace Jint.Native.Number
 
         protected override void Initialize()
         {
-            var properties = new StringDictionarySlim<PropertyDescriptor>(15)
+            var properties = new PropertyDictionary(15, checkExistingKeys: false)
             {
                 ["MAX_VALUE"] = new PropertyDescriptor(new PropertyDescriptor(double.MaxValue, PropertyFlag.AllForbidden)),
                 ["MIN_VALUE"] = new PropertyDescriptor(new PropertyDescriptor(double.Epsilon, PropertyFlag.AllForbidden)),
@@ -54,11 +55,10 @@ namespace Jint.Native.Number
                 ["isInteger"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "isInteger", IsInteger, 1, PropertyFlag.Configurable), true, false, true),
                 ["isNaN"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "isNaN", IsNaN, 1, PropertyFlag.Configurable), true, false, true),
                 ["isSafeInteger"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "isSafeInteger", IsSafeInteger, 1, PropertyFlag.Configurable), true, false, true),
-                ["parseFloat"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "parseFloat", _engine.Global.ParseFloat, 0, PropertyFlag.Configurable), true, false, true),
-                ["parseInt"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "parseInt", _engine.Global.ParseInt, 0, PropertyFlag.Configurable), true, false, true)
+                ["parseFloat"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "parseFloat", GlobalObject.ParseFloat, 0, PropertyFlag.Configurable), true, false, true),
+                ["parseInt"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "parseInt", GlobalObject.ParseInt, 0, PropertyFlag.Configurable), true, false, true)
             };
-            
-            SetProperties(properties, hasSymbols: false);
+            SetProperties(properties);
         }
 
         private static JsValue IsFinite(JsValue thisObj, JsValue[] arguments)

+ 1 - 1
Jint/Native/Number/NumberInstance.cs

@@ -10,7 +10,7 @@ namespace Jint.Native.Number
         private static readonly long NegativeZeroBits = BitConverter.DoubleToInt64Bits(-0.0);
 
         public NumberInstance(Engine engine)
-            : base(engine, "Number")
+            : base(engine, ObjectClass.Number)
         {
         }
 

+ 5 - 6
Jint/Native/Number/NumberPrototype.cs

@@ -37,9 +37,9 @@ namespace Jint.Native.Number
 
         protected override void Initialize()
         {
-            var properties = new StringDictionarySlim<PropertyDescriptor>(8)
+            var properties = new PropertyDictionary(8, checkExistingKeys: false)
             {
-                [KnownKeys.Constructor] = new PropertyDescriptor(_numberConstructor, true, false, true),
+                ["constructor"] = new PropertyDescriptor(_numberConstructor, true, false, true),
                 ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToNumberString, 1, PropertyFlag.Configurable), true, false, true),
                 ["toLocaleString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toLocaleString", ToLocaleString, 0, PropertyFlag.Configurable), true, false, true),
                 ["valueOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "valueOf", ValueOf, 0, PropertyFlag.Configurable), true, false, true),
@@ -47,8 +47,7 @@ namespace Jint.Native.Number
                 ["toExponential"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toExponential", ToExponential, 1, PropertyFlag.Configurable), true, false, true),
                 ["toPrecision"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toPrecision", ToPrecision, 1, PropertyFlag.Configurable), true, false, true)
             };
-            
-            SetProperties(properties, hasSymbols: false);
+            SetProperties(properties);
         }
 
         private JsValue ToLocaleString(JsValue thisObject, JsValue[] arguments)
@@ -67,7 +66,7 @@ namespace Jint.Native.Number
 
             if (m == 0)
             {
-                return "0";
+                return JsString.NumberZeroString;
             }
 
             if (m < 0)
@@ -367,7 +366,7 @@ namespace Jint.Native.Number
 
             if (x == 0)
             {
-                return "0";
+                return JsString.NumberZeroString;
             }
 
             if (double.IsPositiveInfinity(x) || x >= double.MaxValue)

+ 24 - 0
Jint/Native/Object/ObjectClass.cs

@@ -0,0 +1,24 @@
+namespace Jint.Native.Object
+{
+    internal enum ObjectClass : byte
+    {
+        Arguments,
+        Array,
+        Boolean,
+        Date,
+        Error,
+        Function,
+        Iterator,
+        JSON,
+        Map,
+        Math,
+        Number,
+        Object,
+        Proxy,
+        Reflect,
+        RegExp,
+        String,
+        Symbol,
+        TypeReference
+    }
+}

+ 20 - 20
Jint/Native/Object/ObjectConstructor.cs

@@ -32,7 +32,7 @@ namespace Jint.Native.Object
         {
             _prototype = Engine.Function.PrototypeObject;
 
-            var properties = new StringDictionarySlim<PropertyDescriptor>(15)
+            var properties = new PropertyDictionary(15, checkExistingKeys: false)
             {
                 ["getPrototypeOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getPrototypeOf", GetPrototypeOf, 1), true, false, true),
                 ["getOwnPropertyDescriptor"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertyDescriptor", GetOwnPropertyDescriptor, 2), true, false, true),
@@ -50,8 +50,7 @@ namespace Jint.Native.Object
                 ["keys"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "keys", Keys, 1), true, false, true),
                 ["setPrototypeOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setPrototypeOf", SetPrototypeOf, 2), true, false, true)
             };
-            
-            SetProperties(properties, hasSymbols: false);
+            SetProperties(properties);
         }
 
         public ObjectPrototype PrototypeObject { get; private set; }
@@ -117,12 +116,7 @@ namespace Jint.Native.Object
                 _prototype = Engine.Object.PrototypeObject,
             };
 
-            obj.SetProperties(
-                propertyCount > 1
-                    ? new StringDictionarySlim<PropertyDescriptor>(propertyCount)
-                    : null,
-                hasSymbols: false
-            );
+            obj.SetProperties(propertyCount > 0  ? new PropertyDictionary(propertyCount, checkExistingKeys: true) : null);
 
             return obj;
         }
@@ -184,7 +178,7 @@ namespace Jint.Native.Object
                     array.SetIndexValue(n++, TypeConverter.ToString(i), updateLength: false);
                 }
 
-                array.SetIndexValue(n++, JsString.LengthString, updateLength: false);
+                array.SetIndexValue(n++, CommonProperties.Length, updateLength: false);
             }
 
             array = array ?? Engine.Array.ConstructFast((uint) ownProperties.Count);
@@ -247,7 +241,7 @@ namespace Jint.Native.Object
             var o = arguments.As<ObjectInstance>(0, _engine);
             var properties = arguments.At(1);
             var props = TypeConverter.ToObject(Engine, properties);
-            var descriptors = new List<KeyValuePair<string, PropertyDescriptor>>();
+            var descriptors = new List<KeyValuePair<JsValue, PropertyDescriptor>>();
             foreach (var p in props.GetOwnProperties())
             {
                 if (!p.Value.Enumerable)
@@ -257,7 +251,7 @@ namespace Jint.Native.Object
 
                 var descObj = props.Get(p.Key, props);
                 var desc = PropertyDescriptor.ToPropertyDescriptor(Engine, descObj);
-                descriptors.Add(new KeyValuePair<string, PropertyDescriptor>(p.Key, desc));
+                descriptors.Add(new KeyValuePair<JsValue, PropertyDescriptor>(p.Key, desc));
             }
             foreach (var pair in descriptors)
             {
@@ -270,7 +264,7 @@ namespace Jint.Native.Object
         public JsValue Seal(JsValue thisObject, JsValue[] arguments)
         {
             var o = arguments.As<ObjectInstance>(0, _engine);
-            var properties = new List<KeyValuePair<Key, PropertyDescriptor>>(o.GetOwnProperties());
+            var properties = new List<KeyValuePair<JsValue, PropertyDescriptor>>(o.GetOwnProperties());
             foreach (var prop in properties)
             {
                 var propertyDescriptor = prop.Value;
@@ -291,7 +285,7 @@ namespace Jint.Native.Object
         public JsValue Freeze(JsValue thisObject, JsValue[] arguments)
         {
             var o = arguments.As<ObjectInstance>(0, _engine);
-            var properties = new List<KeyValuePair<Key, PropertyDescriptor>>(o.GetOwnProperties());
+            var properties = new List<KeyValuePair<JsValue, PropertyDescriptor>>(o.GetOwnProperties());
             foreach (var p in properties)
             {
                 var desc = o.GetOwnProperty(p.Key);
@@ -389,28 +383,34 @@ namespace Jint.Native.Object
 
             var array = Engine.Array.ConstructFast((uint) ownKeys.Count);
             uint index = 0;
-            foreach (var key in ownKeys)
+
+            for (var i = 0; i < ownKeys.Count; i++)
             {
-                var propertyName = key.ToPropertyKey();
-                var desc = o.GetOwnProperty(propertyName);
+                var property = ownKeys[i];
+                var desc = o.GetOwnProperty(property);
                 if (desc != PropertyDescriptor.Undefined && desc.Enumerable)
                 {
                     if (kind == EnumerableOwnPropertyNamesKind.Key)
                     {
-                        array.SetIndexValue(index, key, updateLength: false);
+                        array.SetIndexValue(index, property, updateLength: false);
                     }
                     else
                     {
-                        var value = o.Get(propertyName, o);
+                        var value = o.Get(property, o);
                         if (kind == EnumerableOwnPropertyNamesKind.Value)
                         {
                             array.SetIndexValue(index, value, updateLength: false);
                         }
                         else
                         {
-                            array.SetIndexValue(index, _engine.Array.Construct(new [] { key, value }), updateLength: false);
+                            array.SetIndexValue(index, _engine.Array.Construct(new[]
+                            {
+                                property,
+                                value
+                            }), updateLength: false);
                         }
                     }
+
                     index++;
                 }
             }

+ 211 - 163
Jint/Native/Object/ObjectInstance.cs

@@ -22,20 +22,21 @@ namespace Jint.Native.Object
 {
     public class ObjectInstance : JsValue, IEquatable<ObjectInstance>
     {
-        private StringDictionarySlim<PropertyDescriptor> _properties;
-
         private bool _initialized;
-        internal bool _hasSymbols;
+        private readonly ObjectClass _class;
+
+        private PropertyDictionary _properties;
+        internal SymbolDictionary _symbols;
 
         internal ObjectInstance _prototype;
-        private readonly string _class;
         protected readonly Engine _engine;
 
-        public ObjectInstance(Engine engine) : this(engine, "Object")
+        public ObjectInstance(Engine engine) : this(engine, ObjectClass.Object)
         {
         }
 
-        protected ObjectInstance(Engine engine, string objectClass) : base(Types.Object)
+        internal ObjectInstance(Engine engine, ObjectClass objectClass, InternalTypes type = InternalTypes.Object)
+            : base(type)
         {
             _engine = engine;
             _class = objectClass;
@@ -63,25 +64,20 @@ namespace Jint.Native.Object
         /// </summary>
         public virtual bool Extensible { get; private set; }
 
-        internal bool Initialized
-        {
-            [DebuggerStepThrough]
-            get => _initialized;
-        }
-
-        internal StringDictionarySlim<PropertyDescriptor> Properties
+        internal PropertyDictionary Properties
         {
             [DebuggerStepThrough]            
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
             get => _properties;
         }
 
         /// <summary>
-        /// A String value indicating a specification defined
-        /// classification of objects.
+        /// A value indicating a specification defined classification of objects.
         /// </summary>
-        public string Class
+        internal ObjectClass Class
         {
             [DebuggerStepThrough]
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
             get => _class;
         }
 
@@ -100,7 +96,7 @@ namespace Jint.Native.Object
         /// </summary>
         internal static IConstructor SpeciesConstructor(ObjectInstance o, IConstructor defaultConstructor)
         {
-            var c = o.Get(KnownKeys.Constructor);
+            var c = o.Get(CommonProperties.Constructor);
             if (c.IsUndefined())
             {
                 return defaultConstructor;
@@ -126,27 +122,77 @@ namespace Jint.Native.Object
         }
 
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal void SetProperties(StringDictionarySlim<PropertyDescriptor> properties, bool hasSymbols)
+        internal void SetProperties(PropertyDictionary properties)
         {
+            if (properties != null)
+            {
+                properties.CheckExistingKeys = true;
+            }
             _properties = properties;
-            _hasSymbols = hasSymbols;
+        }
+
+        internal void SetSymbols(SymbolDictionary symbols)
+        {
+            _symbols = symbols;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal void SetProperty(JsValue property, PropertyDescriptor value)
+        {
+            if (property is JsString jsString)
+            {
+                SetProperty(jsString.ToString(), value);
+            }
+            else
+            {
+                SetPropertyUnlikely(property, value);
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal void SetProperty(string property, PropertyDescriptor value)
+        {
+            Key key = property;
+            SetProperty(in key, value);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal void SetProperty(in Key property, PropertyDescriptor value)
+        {
+            _properties ??= new PropertyDictionary();
+            _properties[property] = value;
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal void SetProperty(in Key propertyName, PropertyDescriptor value)
+        internal void SetDataProperty(string property, JsValue value)
         {
-            _properties[propertyName] = value;
-            _hasSymbols |= propertyName.IsSymbol;
+            _properties ??= new PropertyDictionary();
+            _properties[property] = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private void SetPropertyUnlikely(JsValue property, PropertyDescriptor value)
+        {
+            var propertyKey = TypeConverter.ToPropertyKey(property);
+            if (!property.IsSymbol())
+            {
+                _properties ??= new PropertyDictionary();
+                _properties[TypeConverter.ToString(propertyKey)] = value;
+            }
+            else
+            {
+                _symbols ??= new SymbolDictionary();
+                _symbols[(JsSymbol) propertyKey] = value;
+            }
         }
 
         internal void ClearProperties()
         {
             _properties?.Clear();
-            _hasSymbols = false;
+            _symbols?.Clear();
         }
 
-        public virtual IEnumerable<KeyValuePair<Key, PropertyDescriptor>> GetOwnProperties()
+        public virtual IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
         {
             EnsureInitialized();
 
@@ -154,7 +200,15 @@ namespace Jint.Native.Object
             {
                 foreach (var pair in _properties)
                 {
-                    yield return pair;
+                    yield return new KeyValuePair<JsValue, PropertyDescriptor>(new JsString(pair.Key), pair.Value);
+                }
+            }
+
+            if (_symbols != null)
+            {
+                foreach (var pair in _symbols)
+                {
+                    yield return new KeyValuePair<JsValue, PropertyDescriptor>(pair.Key, pair.Value);
                 }
             }
         }
@@ -163,44 +217,38 @@ namespace Jint.Native.Object
         {
             EnsureInitialized();
 
-            if (_properties == null)
-            {
-                return new List<JsValue>();
-            }
-            
-            var keys = new List<JsValue>(_properties.Count);
+            var keys = new List<JsValue>(_properties?.Count ?? 0 + _symbols?.Count ?? 0);
             var propertyKeys = new List<JsValue>();
             List<JsValue> symbolKeys = null;
-            
-            foreach (var pair in _properties)
+
+            if ((types & Types.String) != 0 && _properties != null)
             {
-                if ((pair.Key.Type & types) == 0)
-                {
-                    continue;
-                }
-                
-                var isArrayIndex = ulong.TryParse(pair.Key, out var index);
-                if (pair.Key.Type != Types.Symbol)
+                foreach (var pair in _properties)
                 {
+                    var isArrayIndex = ulong.TryParse(pair.Key, out var index);
                     if (isArrayIndex)
                     {
-                        keys.Add(pair.Key);
+                        keys.Add(JsString.Create(index));
                     }
                     else
                     {
-                        propertyKeys.Add(pair.Key);
+                        propertyKeys.Add(new JsString(pair.Key));
                     }
                 }
-                else
+            }
+
+            keys.Sort((v1, v2) => TypeConverter.ToNumber(v1).CompareTo(TypeConverter.ToNumber(v2)));
+            keys.AddRange(propertyKeys);
+
+            if ((types & Types.Symbol) != 0 && _symbols != null)
+            {
+                foreach (var pair in _symbols)
                 {
                     symbolKeys ??= new List<JsValue>();
                     symbolKeys.Add(pair.Key);
                 }
             }
-            
-            keys.Sort((v1, v2) => TypeConverter.ToNumber(v1).CompareTo(TypeConverter.ToNumber(v2)));
 
-            keys.AddRange(propertyKeys);
             if (symbolKeys != null)
             {
                 keys.AddRange(symbolKeys);
@@ -210,44 +258,54 @@ namespace Jint.Native.Object
         }
 
 
-        protected virtual void AddProperty(in Key propertyName, PropertyDescriptor descriptor)
+        protected virtual void AddProperty(JsValue property, PropertyDescriptor descriptor)
         {
-            if (_properties == null)
-            {
-                _properties = new StringDictionarySlim<PropertyDescriptor>();
-            }
-
-            SetProperty(propertyName, descriptor);
+            SetProperty(property, descriptor);
         }
 
-        protected virtual bool TryGetProperty(in Key propertyName, out PropertyDescriptor descriptor)
+        protected virtual bool TryGetProperty(JsValue property, out PropertyDescriptor descriptor)
         {
-            if (_properties == null)
+            descriptor = null;
+
+            var key = TypeConverter.ToPropertyKey(property);
+            if (!key.IsSymbol())
             {
-                descriptor = null;
-                return false;
+                return _properties?.TryGetValue(TypeConverter.ToString(key), out descriptor) == true;
             }
 
-            return _properties.TryGetValue(propertyName, out descriptor);
+            return _symbols?.TryGetValue((JsSymbol) key, out descriptor) == true;
         }
 
-        public virtual bool HasOwnProperty(in Key propertyName)
+        public virtual bool HasOwnProperty(JsValue property)
         {
             EnsureInitialized();
 
-            return _properties?.ContainsKey(propertyName) == true;
+            var key = TypeConverter.ToPropertyKey(property);
+            if (!key.IsSymbol())
+            {
+                return _properties?.ContainsKey(TypeConverter.ToString(key)) == true;
+            }
+
+            return _symbols?.ContainsKey((JsSymbol) key) == true;
         }
 
-        public virtual void RemoveOwnProperty(in Key propertyName)
+        public virtual void RemoveOwnProperty(JsValue property)
         {
             EnsureInitialized();
 
-            _properties?.Remove(propertyName);
+            var key = TypeConverter.ToPropertyKey(property);
+            if (!key.IsSymbol())
+            {
+                _properties?.Remove(TypeConverter.ToString(key));
+                return;
+            }
+
+            _symbols?.Remove((JsSymbol) key);
         }
 
-        public override JsValue Get(in Key propertyName, JsValue receiver)
+        public override JsValue Get(JsValue property, JsValue receiver)
         {
-            var desc = GetProperty(propertyName);
+            var desc = GetProperty(property);
             return UnwrapJsValue(desc, receiver);
         }
 
@@ -292,54 +350,50 @@ namespace Jint.Native.Object
         /// absent.
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.1
         /// </summary>
-        /// <param name="propertyName"></param>
-        /// <returns></returns>
-        public virtual PropertyDescriptor GetOwnProperty(in Key propertyName)
+        public virtual PropertyDescriptor GetOwnProperty(JsValue property)
         {
             EnsureInitialized();
 
             PropertyDescriptor descriptor = null;
-            if (_properties != null && (!propertyName.IsSymbol || _hasSymbols))
+            var key = TypeConverter.ToPropertyKey(property);
+            if (!key.IsSymbol())
+            {
+                _properties?.TryGetValue(TypeConverter.ToString(key), out descriptor);
+            }
+            else
             {
-                _properties.TryGetValue(propertyName, out descriptor);
+                _symbols?.TryGetValue((JsSymbol) key, out descriptor);
             }
+
             return descriptor ?? PropertyDescriptor.Undefined;
         }
 
-        protected internal virtual void SetOwnProperty(in Key propertyName, PropertyDescriptor desc)
+        protected internal virtual void SetOwnProperty(JsValue property, PropertyDescriptor desc)
         {
             EnsureInitialized();
-
-            if (_properties == null)
-            {
-                _properties = new StringDictionarySlim<PropertyDescriptor>();
-            }
-
-            SetProperty(propertyName, desc);
+            SetProperty(property, desc);
         }
 
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.2
         /// </summary>
-        /// <param name="propertyName"></param>
-        /// <returns></returns>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public PropertyDescriptor GetProperty(in Key propertyName)
+        public PropertyDescriptor GetProperty(JsValue property)
         {
-            var prop = GetOwnProperty(propertyName);
+            var prop = GetOwnProperty(property);
 
             if (prop != PropertyDescriptor.Undefined)
             {
                 return prop;
             }
 
-            return Prototype?.GetProperty(propertyName) ?? PropertyDescriptor.Undefined;
+            return Prototype?.GetProperty(property) ?? PropertyDescriptor.Undefined;
         }
 
-        public bool TryGetValue(in Key propertyName, out JsValue value)
+        public bool TryGetValue(JsValue property, out JsValue value)
         {
             value = Undefined;
-            var desc = GetOwnProperty(propertyName);
+            var desc = GetOwnProperty(property);
             if (desc != null && desc != PropertyDescriptor.Undefined)
             {
                 if (desc == PropertyDescriptor.Undefined)
@@ -372,11 +426,11 @@ namespace Jint.Native.Object
                 return false;
             }
 
-            return Prototype.TryGetValue(propertyName, out value);
+            return Prototype.TryGetValue(property, out value);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public bool Set(Key p, JsValue v, bool throwOnError)
+        public bool Set(JsValue p, JsValue v, bool throwOnError)
         {
             if (!Set(p, v, this) && throwOnError)
             {
@@ -387,21 +441,21 @@ namespace Jint.Native.Object
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public bool Set(in Key propertyName, JsValue value)
+        public bool Set(JsValue property, JsValue value)
         {
-            return Set(propertyName, value, this);
+            return Set(property, value, this);
         }
 
-        public override bool Set(in Key propertyName, JsValue value, JsValue receiver)
+        public override bool Set(JsValue property, JsValue value, JsValue receiver)
         {
-            var ownDesc = GetOwnProperty(propertyName);
+            var ownDesc = GetOwnProperty(property);
 
             if (ownDesc == PropertyDescriptor.Undefined)
             {
                 var parent = GetPrototypeOf();
                 if (!(parent is null))
                 {
-                    return parent.Set(propertyName, value, receiver);
+                    return parent.Set(property, value, receiver);
                 }
                 else
                 {
@@ -421,7 +475,7 @@ namespace Jint.Native.Object
                     return false;
                 }
 
-                var existingDescriptor = oi.GetOwnProperty(propertyName);
+                var existingDescriptor = oi.GetOwnProperty(property);
                 if (existingDescriptor != PropertyDescriptor.Undefined)
                 {
                     if (existingDescriptor.IsAccessorDescriptor())
@@ -435,11 +489,11 @@ namespace Jint.Native.Object
                     }
 
                     var valueDesc = new PropertyDescriptor(value, PropertyFlag.None);
-                    return oi.DefineOwnProperty(propertyName, valueDesc);
+                    return oi.DefineOwnProperty(property, valueDesc);
                 }
                 else
                 {
-                    return oi.CreateDataProperty(propertyName, value);
+                    return oi.CreateDataProperty(property, value);
                 }
             }
 
@@ -452,16 +506,16 @@ namespace Jint.Native.Object
 
             return true;
         }
-        
+
         /// <summary>
         /// Returns a Boolean value indicating whether a
         /// [[Put]] operation with PropertyName can be
         /// performed.
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.4
         /// </summary>
-        public bool CanPut(in Key propertyName)
+        public bool CanPut(JsValue property)
         {
-            var desc = GetOwnProperty(propertyName);
+            var desc = GetOwnProperty(property);
 
             if (desc != PropertyDescriptor.Undefined)
             {
@@ -484,7 +538,7 @@ namespace Jint.Native.Object
                 return Extensible;
             }
 
-            var inherited = Prototype.GetProperty(propertyName);
+            var inherited = Prototype.GetProperty(property);
 
             if (inherited == PropertyDescriptor.Undefined)
             {
@@ -513,9 +567,9 @@ namespace Jint.Native.Object
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
         /// </summary>
-        public virtual bool HasProperty(in Key propertyName)
+        public virtual bool HasProperty(JsValue property)
         {
-            var hasOwn = GetOwnProperty(propertyName);
+            var hasOwn = GetOwnProperty(property);
             if (hasOwn != PropertyDescriptor.Undefined)
             {
                 return true;
@@ -524,7 +578,7 @@ namespace Jint.Native.Object
             var parent = GetPrototypeOf();
             if (parent != null)
             {
-                return parent.HasProperty(propertyName);
+                return parent.HasProperty(property);
             }
 
             return false;
@@ -533,23 +587,23 @@ namespace Jint.Native.Object
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/#sec-deletepropertyorthrow
         /// </summary>
-        public bool DeletePropertyOrThrow(in Key propertyName)
+        public bool DeletePropertyOrThrow(JsValue property)
         {
-            if (!Delete(propertyName))
+            if (!Delete(property))
             {
                 ExceptionHelper.ThrowTypeError(Engine);
             }
             return true;
         }
-        
+
         /// <summary>
         /// Removes the specified named own property
         /// from the object. The flag controls failure
         /// handling.
         /// </summary>
-        public virtual bool Delete(in Key propertyName)
+        public virtual bool Delete(JsValue property)
         {
-            var desc = GetOwnProperty(propertyName);
+            var desc = GetOwnProperty(property);
 
             if (desc == PropertyDescriptor.Undefined)
             {
@@ -558,31 +612,31 @@ namespace Jint.Native.Object
 
             if (desc.Configurable)
             {
-                RemoveOwnProperty(propertyName);
+                RemoveOwnProperty(property);
                 return true;
             }
 
             return false;
         }
 
-        public bool DefinePropertyOrThrow(in Key propertyName, PropertyDescriptor desc)
+        public bool DefinePropertyOrThrow(JsValue property, PropertyDescriptor desc)
         {
-            if (!DefineOwnProperty(propertyName, desc))
+            if (!DefineOwnProperty(property, desc))
             {
                 ExceptionHelper.ThrowTypeError(_engine);
             }
 
             return true;
-        } 
-        
+        }
+
         /// <summary>
         /// Creates or alters the named own property to
         /// have the state described by a Property
         /// Descriptor. The flag controls failure handling.
         /// </summary>
-        public virtual bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc)
+        public virtual bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
         {
-            var current = GetOwnProperty(propertyName);
+            var current = GetOwnProperty(property);
             var extensible = Extensible;
 
             if (current == desc)
@@ -590,13 +644,13 @@ namespace Jint.Native.Object
                 return true;
             }
 
-            return ValidateAndApplyPropertyDescriptor(this, propertyName, extensible, desc, current);
+            return ValidateAndApplyPropertyDescriptor(this, property, extensible, desc, current);
         }
 
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/#sec-validateandapplypropertydescriptor
         /// </summary>
-        protected static bool ValidateAndApplyPropertyDescriptor(ObjectInstance o, in Key propertyName, bool extensible, PropertyDescriptor desc, PropertyDescriptor current)
+        protected static bool ValidateAndApplyPropertyDescriptor(ObjectInstance o, JsValue property, bool extensible, PropertyDescriptor desc, PropertyDescriptor current)
         {
             var descValue = desc.Value;
             if (current == PropertyDescriptor.Undefined)
@@ -627,11 +681,11 @@ namespace Jint.Native.Object
                             };
                         }
 
-                        o.SetOwnProperty(propertyName, propertyDescriptor);
+                        o.SetOwnProperty(property, propertyDescriptor);
                     }
                     else
                     {
-                        o.SetOwnProperty(propertyName, new GetSetPropertyDescriptor(desc));
+                        o.SetOwnProperty(property, new GetSetPropertyDescriptor(desc));
                     }
                 }
 
@@ -694,7 +748,7 @@ namespace Jint.Native.Object
                         var flags = current.Flags & ~(PropertyFlag.Writable | PropertyFlag.WritableSet);
                         if (current.IsDataDescriptor())
                         {
-                            o.SetOwnProperty(propertyName, current = new GetSetPropertyDescriptor(
+                            o.SetOwnProperty(property, current = new GetSetPropertyDescriptor(
                                 get: Undefined,
                                 set: Undefined,
                                 flags
@@ -702,7 +756,7 @@ namespace Jint.Native.Object
                         }
                         else
                         {
-                            o.SetOwnProperty(propertyName, current = new PropertyDescriptor(
+                            o.SetOwnProperty(property, current = new PropertyDescriptor(
                                 value: Undefined,
                                 flags
                             ));
@@ -780,7 +834,7 @@ namespace Jint.Native.Object
                 if (mutable != null)
                 {
                     // replace old with new type that supports get and set
-                    o.FastSetProperty(propertyName, mutable);
+                    o.FastSetProperty(property, mutable);
                 }
             }
 
@@ -790,12 +844,7 @@ namespace Jint.Native.Object
         /// <summary>
         /// Optimized version of [[Put]] when the property is known to be undeclared already
         /// </summary>
-        /// <param name="name"></param>
-        /// <param name="value"></param>
-        /// <param name="writable"></param>
-        /// <param name="configurable"></param>
-        /// <param name="enumerable"></param>
-        public void FastAddProperty(string name, JsValue value, bool writable, bool enumerable, bool configurable)
+        public void FastAddProperty(JsValue name, JsValue value, bool writable, bool enumerable, bool configurable)
         {
             SetOwnProperty(name, new PropertyDescriptor(value, writable, enumerable, configurable));
         }
@@ -805,19 +854,22 @@ namespace Jint.Native.Object
         /// </summary>
         /// <param name="name"></param>
         /// <param name="value"></param>
-        public void FastSetProperty(in Key name, PropertyDescriptor value)
+        public void FastSetProperty(JsValue name, PropertyDescriptor value)
         {
             SetOwnProperty(name, value);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         protected void EnsureInitialized()
         {
-            if (!_initialized)
+            if (_initialized)
             {
-                // we need to set flag eagerly to prevent wrong recursion
-                _initialized = true;
-                Initialize();
+                return;
             }
+
+            // we need to set flag eagerly to prevent wrong recursion
+            _initialized = true;
+            Initialize();
         }
 
         protected virtual void Initialize()
@@ -838,14 +890,14 @@ namespace Jint.Native.Object
 
             switch (Class)
             {
-                case "Array":
+                case ObjectClass.Array:
                     if (this is ArrayInstance arrayInstance)
                     {
-                        var len = TypeConverter.ToInt32(arrayInstance.Get("length", arrayInstance));
+                        var len = TypeConverter.ToInt32(arrayInstance.Get(CommonProperties.Length, arrayInstance));
                         var result = new object[len];
                         for (var k = 0; k < len; k++)
                         {
-                            var pk = TypeConverter.ToString(k);
+                            var pk = TypeConverter.ToJsString(k);
                             var kpresent = arrayInstance.HasProperty(pk);
                             if (kpresent)
                             {
@@ -863,7 +915,7 @@ namespace Jint.Native.Object
 
                     break;
 
-                case "String":
+                case ObjectClass.String:
                     if (this is StringInstance stringInstance)
                     {
                         return stringInstance.PrimitiveValue.ToString();
@@ -871,7 +923,7 @@ namespace Jint.Native.Object
 
                     break;
 
-                case "Date":
+                case ObjectClass.Date:
                     if (this is DateInstance dateInstance)
                     {
                         return dateInstance.ToDateTime();
@@ -879,7 +931,7 @@ namespace Jint.Native.Object
 
                     break;
 
-                case "Boolean":
+                case ObjectClass.Boolean:
                     if (this is BooleanInstance booleanInstance)
                     {
                         return ((JsBoolean) booleanInstance.PrimitiveValue)._value
@@ -889,7 +941,7 @@ namespace Jint.Native.Object
 
                     break;
 
-                case "Function":
+                case ObjectClass.Function:
                     if (this is FunctionInstance function)
                     {
                         return (Func<JsValue, JsValue[], JsValue>) function.Call;
@@ -897,7 +949,7 @@ namespace Jint.Native.Object
 
                     break;
 
-                case "Number":
+                case ObjectClass.Number:
                     if (this is NumberInstance numberInstance)
                     {
                         return numberInstance.NumberData._value;
@@ -905,7 +957,7 @@ namespace Jint.Native.Object
 
                     break;
 
-                case "RegExp":
+                case ObjectClass.RegExp:
                     if (this is RegExpInstance regeExpInstance)
                     {
                         return regeExpInstance.Value;
@@ -913,14 +965,9 @@ namespace Jint.Native.Object
 
                     break;
 
-                case "Arguments":
-                case "Object":
-#if __IOS__
-                                IDictionary<string, object> o = new DictionarySlim<string, object>();
-#else
+                case ObjectClass.Arguments:
+                case ObjectClass.Object:
                     IDictionary<string, object> o = new ExpandoObject();
-#endif
-
                     foreach (var p in GetOwnProperties())
                     {
                         if (!p.Value.Enumerable)
@@ -928,7 +975,7 @@ namespace Jint.Native.Object
                             continue;
                         }
 
-                        o.Add(p.Key, Get(p.Key, this).ToObject());
+                        o.Add(p.Key.ToString(), Get(p.Key, this).ToObject());
                     }
 
                     return o;
@@ -949,7 +996,7 @@ namespace Jint.Native.Object
         {
             long GetLength()
             {
-                var desc = GetProperty("length");
+                var desc = GetProperty(CommonProperties.Length);
                 var descValue = desc.Value;
                 double len;
                 if (desc.IsDataDescriptor() && !ReferenceEquals(descValue, null))
@@ -977,7 +1024,7 @@ namespace Jint.Native.Object
 
             bool TryGetValue(uint idx, out JsValue jsValue)
             {
-                var property = TypeConverter.ToString(idx);
+                var property = JsString.Create(idx);
                 var kPresent = HasProperty(property);
                 jsValue = kPresent ? Get(property, this) : Undefined;
                 return kPresent;
@@ -1043,11 +1090,12 @@ namespace Jint.Native.Object
             }
         }
 
-        internal virtual bool IsArrayLike => TryGetValue(KnownKeys.Length, out var lengthValue)
+        internal virtual bool IsArrayLike => TryGetValue(CommonProperties.Length, out var lengthValue)
                                              && lengthValue.IsNumber()
                                              && ((JsNumber) lengthValue)._value >= 0;
 
-        public virtual uint Length => (uint) TypeConverter.ToLength(Get(KnownKeys.Length));
+
+        public virtual uint Length => (uint) TypeConverter.ToLength(Get(CommonProperties.Length));
 
         public virtual JsValue PreventExtensions()
         {
@@ -1109,20 +1157,20 @@ namespace Jint.Native.Object
             _prototype = value as ObjectInstance;
             return true;
         }
-        
+
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/#sec-createdatapropertyorthrow
         /// </summary>
-        internal bool CreateDataProperty(Key p, JsValue v)
+        internal bool CreateDataProperty(JsValue p, JsValue v)
         {
             var newDesc = new PropertyDescriptor(v, PropertyFlag.ConfigurableEnumerableWritable);
             return DefineOwnProperty(p, newDesc);
-        }   
-        
+        }
+
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/#sec-createdataproperty
         /// </summary>
-        internal bool CreateDataPropertyOrThrow( Key p, JsValue v)
+        internal bool CreateDataPropertyOrThrow(JsValue p, JsValue v)
         {
             if (!CreateDataProperty(p, v))
             {
@@ -1133,12 +1181,12 @@ namespace Jint.Native.Object
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal ICallable GetMethod(in Key propertyName)
+        internal ICallable GetMethod(JsValue property)
         {
-            return GetMethod(_engine, this, propertyName);
+            return GetMethod(_engine, this, property);
         }
 
-        internal static ICallable GetMethod(Engine engine, JsValue v, in Key p)
+        internal static ICallable GetMethod(Engine engine, JsValue v, JsValue p)
         {
             var jsValue = v.Get(p);
             if (jsValue.IsNullOrUndefined())
@@ -1146,9 +1194,9 @@ namespace Jint.Native.Object
                 return null;
             }
 
-            return jsValue as ICallable ?? ExceptionHelper.ThrowTypeError<ICallable>(engine, "Value returned for property '" + p.Name + "' of object is not a function");
+            return jsValue as ICallable ?? ExceptionHelper.ThrowTypeError<ICallable>(engine, "Value returned for property '" + p + "' of object is not a function");
         }
-        
+
         internal ObjectInstance AssertThisIsObjectInstance(JsValue value, string methodName)
         {
             return value as ObjectInstance ?? ThrowIncompatibleReceiver<ObjectInstance>(value, methodName);

+ 4 - 5
Jint/Native/Object/ObjectPrototype.cs

@@ -26,9 +26,9 @@ namespace Jint.Native.Object
         protected override void Initialize()
         {
             const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
-            var properties = new StringDictionarySlim<PropertyDescriptor>(8)
+            var properties = new PropertyDictionary(8, checkExistingKeys: false)
             {
-                [KnownKeys.Constructor] = new PropertyDescriptor(_objectConstructor, propertyFlags),
+                ["constructor"] = new PropertyDescriptor(_objectConstructor, propertyFlags),
                 ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToObjectString), propertyFlags),
                 ["toLocaleString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toLocaleString", ToLocaleString), propertyFlags),
                 ["valueOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "valueOF", ValueOf), propertyFlags),
@@ -36,8 +36,7 @@ namespace Jint.Native.Object
                 ["isPrototypeOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "isPrototypeOf", IsPrototypeOf, 1), propertyFlags),
                 ["propertyIsEnumerable"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "propertyIsEnumerable", PropertyIsEnumerable, 1), propertyFlags)
             };
-            
-            SetProperties(properties, hasSymbols: false);
+            SetProperties(properties);
         }
 
         private JsValue PropertyIsEnumerable(JsValue thisObject, JsValue[] arguments)
@@ -78,7 +77,7 @@ namespace Jint.Native.Object
                     return false;
                 }
 
-                if (o == v)
+                if (ReferenceEquals(o, v))
                 {
                     return true;
                 }

+ 7 - 6
Jint/Native/Proxy/ProxyConstructor.cs

@@ -11,6 +11,8 @@ namespace Jint.Native.Proxy
     public sealed class ProxyConstructor : FunctionInstance, IConstructor
     {
         private static readonly JsString _name = new JsString("Proxy");
+        private static readonly JsString PropertyProxy = new JsString("proxy");
+        private static readonly JsString PropertyRevoke = new JsString("revoke");
 
         private ProxyConstructor(Engine engine)
             : base(engine, _name, strict: false)
@@ -46,12 +48,11 @@ namespace Jint.Native.Proxy
 
         protected override void Initialize()
         {
-            var properties = new StringDictionarySlim<PropertyDescriptor>(3)
+            var properties = new PropertyDictionary(1, checkExistingKeys: false)
             {
                 ["revocable"] = new PropertyDescriptor(new ClrFunctionInstance(_engine, "revocable", Revocable, 2, PropertyFlag.Configurable), true, true, true)
             };
-            
-            SetProperties(properties, hasSymbols: false);
+            SetProperties(properties);
         }
 
         protected override ObjectInstance GetPrototypeOf()
@@ -77,15 +78,15 @@ namespace Jint.Native.Proxy
         {
             var p = _engine.Proxy.Construct(arguments, Undefined);
             var result = _engine.Object.Construct(ArrayExt.Empty<JsValue>());
-            result.DefineOwnProperty("revoke", new PropertyDescriptor(new ClrFunctionInstance(_engine, name: null, Revoke, 0, PropertyFlag.Configurable), PropertyFlag.ConfigurableEnumerableWritable));
-            result.DefineOwnProperty("proxy", new PropertyDescriptor(Construct(arguments, thisObject), PropertyFlag.ConfigurableEnumerableWritable));
+            result.DefineOwnProperty(PropertyRevoke, new PropertyDescriptor(new ClrFunctionInstance(_engine, name: null, Revoke, 0, PropertyFlag.Configurable), PropertyFlag.ConfigurableEnumerableWritable));
+            result.DefineOwnProperty(PropertyProxy, new PropertyDescriptor(Construct(arguments, thisObject), PropertyFlag.ConfigurableEnumerableWritable));
             return result;
         }
 
         private JsValue Revoke(JsValue thisObject, JsValue[] arguments)
         {
             var o = thisObject.AsObject();
-            var proxy = (ProxyInstance) o.Get("proxy");
+            var proxy = (ProxyInstance) o.Get(PropertyProxy);
             proxy._handler = null;
             proxy._target = null;
             return Undefined;

+ 57 - 58
Jint/Native/Proxy/ProxyInstance.cs

@@ -11,27 +11,27 @@ namespace Jint.Native.Proxy
         internal ObjectInstance _target;
         internal ObjectInstance _handler;
 
-        private static readonly Key TrapApply = "apply";
-        private static readonly Key TrapGet = "get";
-        private static readonly Key TrapSet = "set";
-        private static readonly Key TrapPreventExtensions = "preventExtensions";
-        private static readonly Key TrapIsExtensible = "isExtensible";
-        private static readonly Key TrapDefineProperty = "defineProperty";
-        private static readonly Key TrapDeleteProperty = "deleteProperty";
-        private static readonly Key TrapGetOwnPropertyDescriptor = "getOwnPropertyDescriptor";
-        private static readonly Key TrapHas = "has";
-        private static readonly Key TrapGetProtoTypeOf = "getPrototypeOf";
-        private static readonly Key TrapSetProtoTypeOf = "setPrototypeOf";
-        private static readonly Key TrapOwnKeys = "ownKeys";
-        private static readonly Key TrapConstruct = "construct";
-
-        private static readonly Key KeyFunctionRevoke = "revoke";
+        private static readonly JsString TrapApply = new JsString("apply");
+        private static readonly JsString TrapGet = new JsString("get");
+        private static readonly JsString TrapSet = new JsString("set");
+        private static readonly JsString TrapPreventExtensions = new JsString("preventExtensions");
+        private static readonly JsString TrapIsExtensible = new JsString("isExtensible");
+        private static readonly JsString TrapDefineProperty = new JsString("defineProperty");
+        private static readonly JsString TrapDeleteProperty = new JsString("deleteProperty");
+        private static readonly JsString TrapGetOwnPropertyDescriptor = new JsString("getOwnPropertyDescriptor");
+        private static readonly JsString TrapHas = new JsString("has");
+        private static readonly JsString TrapGetProtoTypeOf = new JsString("getPrototypeOf");
+        private static readonly JsString TrapSetProtoTypeOf = new JsString("setPrototypeOf");
+        private static readonly JsString TrapOwnKeys = new JsString("ownKeys");
+        private static readonly JsString TrapConstruct = new JsString("construct");
+
+        private static readonly JsString KeyFunctionRevoke = new JsString("revoke");
 
         public ProxyInstance(
             Engine engine,
             ObjectInstance target,
             ObjectInstance handler)
-            : base(engine, JsString.Empty, false, "Proxy")
+            : base(engine, JsString.Empty, false, ObjectClass.Proxy)
         {
             _target = target;
             _handler = handler;
@@ -81,16 +81,16 @@ namespace Jint.Native.Proxy
         internal override bool IsConstructor => 
             _handler.TryGetValue(TrapConstruct, out var handlerFunction) && handlerFunction is IConstructor;
 
-        public override JsValue Get(in Key propertyName, JsValue receiver)
+        public override JsValue Get(JsValue property, JsValue receiver)
         {
-            if (propertyName == KeyFunctionRevoke || !TryCallHandler(TrapGet, new JsValue[] {_target, propertyName, this}, out var result))
+            if (property == KeyFunctionRevoke || !TryCallHandler(TrapGet, new JsValue[] {_target, property, this}, out var result))
             {
-                AssertTargetNotRevoked(propertyName);
-                return _target.Get(in propertyName, receiver);
+                AssertTargetNotRevoked(property);
+                return _target.Get(property, receiver);
             }
 
-            AssertTargetNotRevoked(propertyName);
-            var targetDesc = _target.GetOwnProperty(propertyName);
+            AssertTargetNotRevoked(property);
+            var targetDesc = _target.GetOwnProperty(property);
             if (targetDesc != PropertyDescriptor.Undefined)
             {
                 if (targetDesc.IsDataDescriptor() && !targetDesc.Configurable && !targetDesc.Writable && !ReferenceEquals(result, targetDesc._value))
@@ -99,7 +99,7 @@ namespace Jint.Native.Proxy
                 }
                 if (targetDesc.IsAccessorDescriptor() && !targetDesc.Configurable && targetDesc.Get.IsUndefined() && !result.IsUndefined())
                 {
-                   ExceptionHelper.ThrowTypeError(_engine, $"'get' on proxy: property '{propertyName}' is a non-configurable accessor property on the proxy target and does not have a getter function, but the trap did not return 'undefined' (got '{result}')");
+                   ExceptionHelper.ThrowTypeError(_engine, $"'get' on proxy: property '{property}' is a non-configurable accessor property on the proxy target and does not have a getter function, but the trap did not return 'undefined' (got '{result}')");
                 }
             }
 
@@ -122,20 +122,19 @@ namespace Jint.Native.Proxy
 
             var extensibleTarget = _target.Extensible;
             var targetKeys = _target.GetOwnPropertyKeys(types);
-            var targetConfigurableKeys = new List<Key>();
-            var targetNonconfigurableKeys = new List<Key>();
+            var targetConfigurableKeys = new List<JsValue>();
+            var targetNonconfigurableKeys = new List<JsValue>();
 
-            foreach (var jsValue in targetKeys)
+            foreach (var property in targetKeys)
             {
-                var key = jsValue.ToPropertyKey();
-                var desc = _target.GetOwnProperty(key);
+                var desc = _target.GetOwnProperty(property);
                 if (desc != PropertyDescriptor.Undefined && !desc.Configurable)
                 {
-                    targetNonconfigurableKeys.Add(key);
+                    targetNonconfigurableKeys.Add(property);
                 }
                 else
                 {
-                    targetConfigurableKeys.Add(key);
+                    targetConfigurableKeys.Add(property);
                 }
                 
             }
@@ -145,10 +144,10 @@ namespace Jint.Native.Proxy
                 return trapResult;
             }
             
-            var uncheckedResultKeys = new HashSet<Key>();
+            var uncheckedResultKeys = new HashSet<JsValue>();
             foreach (var jsValue in trapResult)
             {
-                uncheckedResultKeys.Add(jsValue.ToPropertyKey());
+                uncheckedResultKeys.Add(jsValue);
             }
 
             for (var i = 0; i < targetNonconfigurableKeys.Count; i++)
@@ -182,11 +181,11 @@ namespace Jint.Native.Proxy
             return trapResult;
         }
 
-        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
+        public override PropertyDescriptor GetOwnProperty(JsValue property)
         {
-            if (!TryCallHandler(TrapGetOwnPropertyDescriptor, new JsValue[] {_target, propertyName, this}, out var result))
+            if (!TryCallHandler(TrapGetOwnPropertyDescriptor, new JsValue[] {_target, property, this}, out var result))
             {
-                return _target.GetOwnProperty(propertyName);
+                return _target.GetOwnProperty(property);
             }
 
             if (!result.IsObject() && !result.IsUndefined())
@@ -194,7 +193,7 @@ namespace Jint.Native.Proxy
                 ExceptionHelper.ThrowTypeError(_engine);
             }
 
-            var targetDesc = _target.GetOwnProperty(propertyName);
+            var targetDesc = _target.GetOwnProperty(property);
 
             if (result.IsUndefined())
             {
@@ -228,11 +227,11 @@ namespace Jint.Native.Proxy
             return resultDesc;
         }
 
-        public override bool Set(in Key propertyName, JsValue value, JsValue receiver)
+        public override bool Set(JsValue property, JsValue value, JsValue receiver)
         {
-            if (!TryCallHandler(TrapSet, new[] { _target, propertyName, value, this }, out var trapResult))
+            if (!TryCallHandler(TrapSet, new[] { _target, property, value, this }, out var trapResult))
             {
-                return _target.Set(propertyName, value, receiver);
+                return _target.Set(property, value, receiver);
             }
 
             var result = TypeConverter.ToBoolean(trapResult);
@@ -241,7 +240,7 @@ namespace Jint.Native.Proxy
                 return false;
             }
 
-            var targetDesc  = _target.GetOwnProperty(propertyName);
+            var targetDesc  = _target.GetOwnProperty(property);
             if (targetDesc != PropertyDescriptor.Undefined)
             {
                 if (targetDesc.IsDataDescriptor() && !targetDesc.Configurable && !targetDesc.Writable)
@@ -264,12 +263,12 @@ namespace Jint.Native.Proxy
             return true;
         }
 
-        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc)
+        public override bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
         {
-            var arguments = new[] { _target, propertyName, PropertyDescriptor.FromPropertyDescriptor(_engine, desc) };
+            var arguments = new[] { _target, property, PropertyDescriptor.FromPropertyDescriptor(_engine, desc) };
             if (!TryCallHandler(TrapDefineProperty, arguments, out var result))
             {
-                return _target.DefineOwnProperty(propertyName, desc);
+                return _target.DefineOwnProperty(property, desc);
             }
 
             var success = TypeConverter.ToBoolean(result);
@@ -278,7 +277,7 @@ namespace Jint.Native.Proxy
                 return false;
             }
 
-            var targetDesc = _target.GetOwnProperty(propertyName);
+            var targetDesc = _target.GetOwnProperty(property);
             var extensibleTarget = _target.Extensible;
             var settingConfigFalse = !desc.Configurable;
 
@@ -306,21 +305,21 @@ namespace Jint.Native.Proxy
 
         private static bool IsCompatiblePropertyDescriptor(bool extensible, PropertyDescriptor desc, PropertyDescriptor current)
         {
-            return ValidateAndApplyPropertyDescriptor(null, "", extensible, desc, current);
+            return ValidateAndApplyPropertyDescriptor(null, JsString.Empty, extensible, desc, current);
         }
 
-        public override bool HasProperty(in Key propertyName)
+        public override bool HasProperty(JsValue property)
         {
-            if (!TryCallHandler(TrapHas, new JsValue[] { _target, propertyName }, out var jsValue))
+            if (!TryCallHandler(TrapHas, new [] { _target, property }, out var jsValue))
             {
-                return _target.HasProperty(propertyName);
+                return _target.HasProperty(property);
             }
 
             var trapResult = TypeConverter.ToBoolean(jsValue);
 
             if (!trapResult)
             {
-                var targetDesc = _target.GetOwnProperty(propertyName);
+                var targetDesc = _target.GetOwnProperty(property);
                 if (targetDesc != PropertyDescriptor.Undefined)
                 {
                     if (!targetDesc.Configurable)
@@ -338,21 +337,21 @@ namespace Jint.Native.Proxy
             return trapResult;
         }
 
-        public override bool Delete(in Key propertyName)
+        public override bool Delete(JsValue property)
         {
-            if (!TryCallHandler(TrapDeleteProperty, new JsValue[] { _target, propertyName }, out var result))
+            if (!TryCallHandler(TrapDeleteProperty, new JsValue[] { _target, property }, out var result))
             {
-                return _target.Delete(propertyName);
+                return _target.Delete(property);
             }
 
             var success = TypeConverter.ToBoolean(result);
 
             if (success)
             {
-                var targetDesc = _target.GetOwnProperty(propertyName);
+                var targetDesc = _target.GetOwnProperty(property);
                 if (targetDesc != PropertyDescriptor.Undefined && !targetDesc.Configurable)
                 {
-                    ExceptionHelper.ThrowTypeError(_engine, $"'deleteProperty' on proxy: trap returned truish for property '{propertyName}' which is non-configurable in the proxy target");
+                    ExceptionHelper.ThrowTypeError(_engine, $"'deleteProperty' on proxy: trap returned truish for property '{property}' which is non-configurable in the proxy target");
                 }
             }
 
@@ -447,10 +446,10 @@ namespace Jint.Native.Proxy
             return true;
         }
 
-        private bool TryCallHandler(in Key propertyName, JsValue[] arguments, out JsValue result)
+        private bool TryCallHandler(JsValue propertyName, JsValue[] arguments, out JsValue result)
         {
             AssertNotRevoked(propertyName);
-            
+
             result = Undefined;
             var handlerFunction = _handler.Get(propertyName);
             if (!handlerFunction.IsNullOrUndefined())
@@ -467,7 +466,7 @@ namespace Jint.Native.Proxy
             return false;
         }
 
-        internal void AssertNotRevoked(in Key key)
+        internal void AssertNotRevoked(JsValue key)
         {
             if (_handler is null)
             {
@@ -475,7 +474,7 @@ namespace Jint.Native.Proxy
             }
         }
 
-        internal void AssertTargetNotRevoked(in Key key)
+        internal void AssertTargetNotRevoked(JsValue key)
         {
             if (_target is null)
             {

+ 3 - 4
Jint/Native/Reflect/ReflectInstance.cs

@@ -11,7 +11,7 @@ namespace Jint.Native.Reflect
     /// </summary>
     public sealed class ReflectInstance : ObjectInstance
     {
-        private ReflectInstance(Engine engine) : base(engine, "Reflect")
+        private ReflectInstance(Engine engine) : base(engine, ObjectClass.Reflect)
         {
         }
 
@@ -27,7 +27,7 @@ namespace Jint.Native.Reflect
 
         protected override void Initialize()
         {
-            var properties = new StringDictionarySlim<PropertyDescriptor>(14)
+            var properties = new PropertyDictionary(14, checkExistingKeys: false)
             {
                 ["apply"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "apply", Apply, 3, PropertyFlag.Configurable), true, false, true),
                 ["construct"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "construct", Construct, 2, PropertyFlag.Configurable), true, false, true),
@@ -43,8 +43,7 @@ namespace Jint.Native.Reflect
                 ["set"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "set", Set, 3, PropertyFlag.Configurable), true, false, true),
                 ["setPrototypeOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setPrototypeOf", SetPrototypeOf, 2, PropertyFlag.Configurable), true, false, true),
             };
-            
-            SetProperties(properties, hasSymbols: false);
+            SetProperties(properties);
         }
 
         private JsValue Apply(JsValue thisObject, JsValue[] arguments)

+ 6 - 17
Jint/Native/RegExp/RegExpConstructor.cs

@@ -41,26 +41,15 @@ namespace Jint.Native.RegExp
 
         protected override void Initialize()
         {
-            var properties = new StringDictionarySlim<PropertyDescriptor>(1)
+            var symbols = new SymbolDictionary(1)
             {
                 [GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(_engine, "get [Symbol.species]", (thisObj, _) => thisObj, 0, PropertyFlag.Configurable), set: Undefined, PropertyFlag.Configurable)
             };
-           
-            SetProperties(properties, hasSymbols: true);
+            SetSymbols(symbols);
         }
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
-            var pattern = arguments.At(0);
-            var flags = arguments.At(1);
-
-            if (!pattern.IsUndefined()
-                && flags.IsUndefined()
-                && TypeConverter.ToObject(Engine, pattern).Class == "Regex")
-            {
-                return pattern;
-            }
-
             return Construct(arguments, thisObject);
         }
 
@@ -83,7 +72,7 @@ namespace Jint.Native.RegExp
                 newTarget = this;
                 if (patternIsRegExp && flags.IsUndefined())
                 {
-                    var patternConstructor = pattern.Get(KnownKeys.Constructor);
+                    var patternConstructor = pattern.Get(CommonProperties.Constructor);
                     if (ReferenceEquals(newTarget, patternConstructor))
                     {
                         return (ObjectInstance) pattern;
@@ -100,8 +89,8 @@ namespace Jint.Native.RegExp
             }
             else if (patternIsRegExp)
             {
-                p = pattern.Get("source");
-                f = flags.IsUndefined() ? pattern.Get("flags") : flags;
+                p = pattern.Get(RegExpPrototype.PropertySource);
+                f = flags.IsUndefined() ? pattern.Get(RegExpPrototype.PropertyFlags) : flags;
             }
             else
             {
@@ -207,7 +196,7 @@ namespace Jint.Native.RegExp
         
         private static void RegExpInitialize(RegExpInstance r)
         {
-            r.SetOwnProperty("lastIndex", new PropertyDescriptor(0, PropertyFlag.OnlyWritable));
+            r.SetOwnProperty(RegExpInstance.PropertyLastIndex, new PropertyDescriptor(0, PropertyFlag.OnlyWritable));
         }
         
         public RegExpPrototype PrototypeObject { get; private set; }

+ 2 - 1
Jint/Native/RegExp/RegExpExtensions.cs

@@ -15,7 +15,8 @@ namespace Jint.Native.RegExp
             if (o is RegExpInstance instance)
             {
                 exec = default;
-                return instance.Properties == null && TryGetDefaultRegExpExec(instance.Prototype, out exec);
+                return instance.Properties == null
+                       && TryGetDefaultRegExpExec(instance.Prototype, out exec);
             }
 
             exec = default;

+ 23 - 19
Jint/Native/RegExp/RegExpInstance.cs

@@ -8,16 +8,17 @@ namespace Jint.Native.RegExp
 {
     public class RegExpInstance : ObjectInstance
     {
-        internal static readonly Key KeyLastIndex = "lastIndex";
-        
+        internal const string regExpForMatchingAllCharacters = "(?:)";
+        internal static readonly JsString PropertyLastIndex = new JsString("lastIndex");
+
         private string _flags;
 
         private PropertyDescriptor _prototypeDescriptor;
 
         public RegExpInstance(Engine engine)
-            : base(engine, objectClass: "RegExp")
+            : base(engine, ObjectClass.RegExp)
         {
-            Source = "(?:)";
+            Source = regExpForMatchingAllCharacters;
         }
 
         public Regex Value { get; set; }
@@ -63,32 +64,34 @@ namespace Jint.Native.RegExp
         public bool Sticky { get; private set; }
         public bool FullUnicode { get; private set; }
 
-        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
+        public override PropertyDescriptor GetOwnProperty(JsValue property)
         {
-            if (propertyName == KeyLastIndex)
+            if (property == PropertyLastIndex)
             {
                 return _prototypeDescriptor ?? PropertyDescriptor.Undefined;
             }
-            return base.GetOwnProperty(propertyName);
+
+            return base.GetOwnProperty(property);
         }
 
-        protected internal override void SetOwnProperty(in Key propertyName, PropertyDescriptor desc)
+        protected internal override void SetOwnProperty(JsValue property, PropertyDescriptor desc)
         {
-            if (propertyName == KeyLastIndex)
+            if (property == PropertyLastIndex)
             {
                 _prototypeDescriptor = desc;
                 return;
             }
 
-            base.SetOwnProperty(propertyName, desc);
+            base.SetOwnProperty(property, desc);
         }
 
-        public override IEnumerable<KeyValuePair<Key, PropertyDescriptor>> GetOwnProperties()
+        public override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
         {
             if (_prototypeDescriptor != null)
             {
-                yield return new KeyValuePair<Key, PropertyDescriptor>(KeyLastIndex, _prototypeDescriptor);
+                yield return new KeyValuePair<JsValue, PropertyDescriptor>(PropertyLastIndex, _prototypeDescriptor);
             }
+
             foreach (var entry in base.GetOwnProperties())
             {
                 yield return entry;
@@ -100,20 +103,21 @@ namespace Jint.Native.RegExp
             var keys = new List<JsValue>();
             if (_prototypeDescriptor != null)
             {
-                keys.Add(KeyLastIndex);
+                keys.Add(PropertyLastIndex);
             }
+
             keys.AddRange(base.GetOwnPropertyKeys(types));
             return keys;
         }
-        public override bool HasOwnProperty(in Key propertyName)
+
+        public override bool HasOwnProperty(JsValue property)
         {
-            if (propertyName == KeyLastIndex)
+            if (property == PropertyLastIndex)
             {
                 return _prototypeDescriptor != null;
             }
-            return base.HasOwnProperty(propertyName);
-        }
-
 
+            return base.HasOwnProperty(property);
+        }
     }
-}
+}

+ 172 - 49
Jint/Native/RegExp/RegExpPrototype.cs

@@ -6,6 +6,7 @@ using Jint.Collections;
 using Jint.Native.Array;
 using Jint.Native.Number;
 using Jint.Native.Object;
+using Jint.Native.String;
 using Jint.Native.Symbol;
 using Jint.Pooling;
 using Jint.Runtime;
@@ -17,6 +18,15 @@ namespace Jint.Native.RegExp
 {
     public sealed class RegExpPrototype : ObjectInstance
     {
+        private static readonly JsString PropertyExec = new JsString("exec");
+        private static readonly JsString PropertyIndex = new JsString("index");
+        private static readonly JsString PropertyInput = new JsString("input");
+        private static readonly JsString PropertySticky = new JsString("sticky");
+        private static readonly JsString PropertyGlobal = new JsString("global");
+        internal static readonly JsString PropertySource = new JsString("source");
+        private static readonly JsValue DefaultSource = new JsString("(?:)");
+        internal static readonly JsString PropertyFlags = new JsString("flags");
+
         private RegExpConstructor _regExpConstructor;
         private readonly Func<JsValue, JsValue[], JsValue> _defaultExec;
 
@@ -61,9 +71,9 @@ namespace Jint.Native.RegExp
             }
 
             const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
-            var properties = new StringDictionarySlim<PropertyDescriptor>(10)
+            var properties = new PropertyDictionary(12, checkExistingKeys: false)
             {
-                [KnownKeys.Constructor] = new PropertyDescriptor(_regExpConstructor, propertyFlags),
+                ["constructor"] = new PropertyDescriptor(_regExpConstructor, propertyFlags),
                 ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToRegExpString, 0, lengthFlags), propertyFlags),
                 ["exec"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "exec", _defaultExec, 1, lengthFlags), propertyFlags),
                 ["test"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "test", Test, 1, lengthFlags), propertyFlags),
@@ -74,22 +84,26 @@ namespace Jint.Native.RegExp
                 ["multiline"] = CreateGetAccessorDescriptor("get multiline", r => r.Multiline),
                 ["source"] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(Engine, "get source", Source, 0, lengthFlags), set: Undefined, flags: PropertyFlag.Configurable),
                 ["sticky"] = CreateGetAccessorDescriptor("get sticky", r => r.Sticky),
-                ["unicode"] = CreateGetAccessorDescriptor("get unicode", r => r.FullUnicode),
+                ["unicode"] = CreateGetAccessorDescriptor("get unicode", r => r.FullUnicode)
+            };
+            SetProperties(properties);
+
+            var symbols = new SymbolDictionary(5)
+            {
                 [GlobalSymbolRegistry.Match] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "[Symbol.match]", Match, 1, lengthFlags), propertyFlags),
                 [GlobalSymbolRegistry.MatchAll] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "[Symbol.matchAll]", MatchAll, 1, lengthFlags), propertyFlags),
                 [GlobalSymbolRegistry.Replace] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "[Symbol.replace]", Replace, 2, lengthFlags), propertyFlags),
                 [GlobalSymbolRegistry.Search] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "[Symbol.search]", Search, 1, lengthFlags), propertyFlags),
                 [GlobalSymbolRegistry.Split] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "[Symbol.split]", Split, 2, lengthFlags), propertyFlags)
             };
-
-            SetProperties(properties, hasSymbols: true);
+            SetSymbols(symbols);
         }
 
         private JsValue Source(JsValue thisObj, JsValue[] arguments)
         {
             if (ReferenceEquals(thisObj, this))
             {
-                return "(?:)";
+                return DefaultSource;
             }
 
             if (!(thisObj is RegExpInstance r))
@@ -121,18 +135,18 @@ namespace Jint.Native.RegExp
             }
 
             var fullUnicode = false;
-            var global = TypeConverter.ToBoolean(rx.Get("global"));
+            var global = TypeConverter.ToBoolean(rx.Get(PropertyGlobal));
 
             if (global)
             {
                 fullUnicode = TypeConverter.ToBoolean(rx.Get("unicode"));
-                rx.Set(RegExpInstance.KeyLastIndex, 0, true);
+                rx.Set(RegExpInstance.PropertyLastIndex, 0, true);
             }
 
             // check if we can access fast path
             if (!fullUnicode
                 && !mayHaveNamedCaptures
-                && !TypeConverter.ToBoolean(rx.Get("sticky"))
+                && !TypeConverter.ToBoolean(rx.Get(PropertySticky))
                 && rx is RegExpInstance rei && rei.TryGetDefaultRegExpExec(out _))
             {
                 var count = global ? int.MaxValue : 1;
@@ -165,7 +179,7 @@ namespace Jint.Native.RegExp
                     result = rei.Value.Replace(s, TypeConverter.ToString(replaceValue), count);
                 }
 
-                rx.Set(RegExpInstance.KeyLastIndex, 0);
+                rx.Set(RegExpInstance.PropertyLastIndex, JsNumber.PositiveZero);
                 return result;
             }
 
@@ -188,9 +202,9 @@ namespace Jint.Native.RegExp
                 var matchStr = TypeConverter.ToString(result.Get(0));
                 if (matchStr == "")
                 {
-                    var thisIndex = (int) TypeConverter.ToLength(rx.Get(RegExpInstance.KeyLastIndex));
+                    var thisIndex = (int) TypeConverter.ToLength(rx.Get(RegExpInstance.PropertyLastIndex));
                     var nextIndex = AdvanceStringIndex(s, thisIndex, fullUnicode);
-                    rx.Set(RegExpInstance.KeyLastIndex, nextIndex);
+                    rx.Set(RegExpInstance.PropertyLastIndex, nextIndex);
                 }
             }
 
@@ -204,7 +218,7 @@ namespace Jint.Native.RegExp
                 nCaptures = System.Math.Max(nCaptures - 1, 0);
                 var matched = TypeConverter.ToString(result.Get(0));
                 var matchLength = matched.Length;
-                var position = (int) TypeConverter.ToInteger(result.Get("index"));
+                var position = (int) TypeConverter.ToInteger(result.Get(PropertyIndex));
                 position = System.Math.Max(System.Math.Min(position, lengthS), 0);
                 uint n = 1;
 
@@ -374,25 +388,25 @@ namespace Jint.Native.RegExp
             var s = TypeConverter.ToString(arguments.At(0));
             var limit = arguments.At(1);
             var c = SpeciesConstructor(rx, _engine.RegExp);
-            var flags = TypeConverter.ToString(rx.Get("flags"));
+            var flags = TypeConverter.ToJsString(rx.Get(PropertyFlags));
             var unicodeMatching = flags.IndexOf('u') > -1;
-            var newFlags = flags.IndexOf('y') > -1 ? flags : flags + 'y';
+            var newFlags = flags.IndexOf('y') > -1 ? flags : new JsString(flags.ToString() + 'y');
             var splitter = Construct(c, new JsValue[]
             {
                 rx,
                 newFlags
             });
-            var a = _engine.Array.ConstructFast(0);
             uint lengthA = 0;
             var lim = limit.IsUndefined() ? NumberConstructor.MaxSafeInteger : TypeConverter.ToUint32(limit);
 
             if (lim == 0)
             {
-                return a;
+                return _engine.Array.ConstructFast(0);
             }
 
             if (s.Length == 0)
             {
+                var a = _engine.Array.ConstructFast(0);
                 var z = RegExpExec(splitter, s);
                 if (!z.IsNull())
                 {
@@ -403,11 +417,82 @@ namespace Jint.Native.RegExp
                 return a;
             }
 
+            if (!unicodeMatching && rx is RegExpInstance R && R.TryGetDefaultRegExpExec(out _))
+            {
+                // we can take faster path
+
+                if (R.Source == RegExpInstance.regExpForMatchingAllCharacters)
+                {
+                    // if empty string, just a string split
+                    return StringPrototype.SplitWithStringSeparator(_engine, "", s, (uint) s.Length);                    
+                }
+                
+                var a = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
+                var match = R.Value.Match(s, 0);
+
+                if (!match.Success) // No match at all return the string in an array
+                {
+                    a.SetIndexValue(0, s, updateLength: true);
+                    return a;
+                }
+
+                int lastIndex = 0;
+                uint index = 0;
+                while (match.Success && index < lim)
+                {
+                    if (match.Length == 0 && (match.Index == 0 || match.Index == s.Length || match.Index == lastIndex))
+                    {
+                        match = match.NextMatch();
+                        continue;
+                    }
+
+                    // Add the match results to the array.
+                    a.SetIndexValue(index++, s.Substring(lastIndex, match.Index - lastIndex), updateLength: true);
+
+                    if (index >= lim)
+                    {
+                        return a;
+                    }
+
+                    lastIndex = match.Index + match.Length;
+                    for (int i = 1; i < match.Groups.Count; i++)
+                    {
+                        var group = match.Groups[i];
+                        var item = Undefined;
+                        if (group.Captures.Count > 0)
+                        {
+                            item = match.Groups[i].Value;
+                        }
+
+                        a.SetIndexValue(index++, item, updateLength: true);
+
+                        if (index >= lim)
+                        {
+                            return a;
+                        }
+                    }
+
+                    match = match.NextMatch();
+                    if (!match.Success) // Add the last part of the split
+                    {
+                        a.SetIndexValue(index++, s.Substring(lastIndex), updateLength: true);
+                    }
+                }
+
+                return a;
+            }
+
+            return SplitSlow(s, splitter, unicodeMatching, lengthA, lim);
+        }
+
+        private JsValue SplitSlow(string s, ObjectInstance splitter, bool unicodeMatching, uint lengthA, long lim)
+        {
+            var a = _engine.Array.ConstructFast(0);
             var previousStringIndex = 0;
             var currentIndex = 0;
             while (currentIndex < s.Length)
             {
-                splitter.Set(RegExpInstance.KeyLastIndex, currentIndex, true);
+                splitter.Set(RegExpInstance.PropertyLastIndex, currentIndex, true);
                 var z = RegExpExec(splitter, s);
                 if (z.IsNull())
                 {
@@ -415,7 +500,7 @@ namespace Jint.Native.RegExp
                     continue;
                 }
 
-                var endIndex = (int) TypeConverter.ToLength(splitter.Get(RegExpInstance.KeyLastIndex));
+                var endIndex = (int) TypeConverter.ToLength(splitter.Get(RegExpInstance.PropertyLastIndex));
                 endIndex = System.Math.Min(endIndex, s.Length);
                 if (endIndex == previousStringIndex)
                 {
@@ -432,7 +517,7 @@ namespace Jint.Native.RegExp
                 }
 
                 previousStringIndex = endIndex;
-                var numberOfCaptures = (int) TypeConverter.ToLength(z.Get(KnownKeys.Length));
+                var numberOfCaptures = (int) TypeConverter.ToLength(z.Get(CommonProperties.Length));
                 numberOfCaptures = System.Math.Max(numberOfCaptures - 1, 0);
                 var i = 1;
                 while (i <= numberOfCaptures)
@@ -458,17 +543,17 @@ namespace Jint.Native.RegExp
         {
             var r = AssertThisIsObjectInstance(thisObj, "RegExp.prototype.flags");
 
-            static string AddFlagIfPresent(JsValue o, in Key propertyName, char flag, string s)
+            static string AddFlagIfPresent(JsValue o, JsValue p, char flag, string s)
             {
-                return TypeConverter.ToBoolean(o.Get(propertyName)) ? s + flag : s;
+                return TypeConverter.ToBoolean(o.Get(p)) ? s + flag : s;
             }
 
-            var result = AddFlagIfPresent(r, "global", 'g', "");
+            var result = AddFlagIfPresent(r, PropertyGlobal, 'g', "");
             result = AddFlagIfPresent(r, "ignoreCase", 'i', result);
             result = AddFlagIfPresent(r, "multiline", 'm', result);
             result = AddFlagIfPresent(r, "dotAll", 's', result);
             result = AddFlagIfPresent(r, "unicode", 'u', result);
-            result = AddFlagIfPresent(r, "sticky", 'y', result);
+            result = AddFlagIfPresent(r, PropertySticky, 'y', result);
 
             return result;
         }
@@ -477,8 +562,8 @@ namespace Jint.Native.RegExp
         {
             var r = AssertThisIsObjectInstance(thisObj, "RegExp.prototype.toString");
 
-            var pattern = TypeConverter.ToString(r.Get("source"));
-            var flags = TypeConverter.ToString(r.Get("flags"));
+            var pattern = TypeConverter.ToString(r.Get(PropertySource));
+            var flags = TypeConverter.ToString(r.Get(PropertyFlags));
 
             return "/" + pattern + "/" + flags;
         }
@@ -486,9 +571,33 @@ namespace Jint.Native.RegExp
         private JsValue Test(JsValue thisObj, JsValue[] arguments)
         {
             var r = AssertThisIsObjectInstance(thisObj, "RegExp.prototype.test");
-
             var s = TypeConverter.ToString(arguments.At(0));
 
+            // check couple fast paths
+            if (r is RegExpInstance R && !R.FullUnicode)
+            {
+                if (!R.Sticky && !R.Global)
+                {
+                    R.Set(RegExpInstance.PropertyLastIndex, 0, throwOnError: true); 
+                    return R.Value.IsMatch(s);
+                }
+
+                var lastIndex = (int) TypeConverter.ToLength(R.Get(RegExpInstance.PropertyLastIndex));
+                if (lastIndex >= s.Length && s.Length > 0)
+                {
+                    return JsBoolean.False;
+                }
+
+                var m = R.Value.Match(s, lastIndex);
+                if (!m.Success || (R.Sticky && m.Index != lastIndex))
+                {
+                    R.Set(RegExpInstance.PropertyLastIndex, 0, throwOnError: true); 
+                    return JsBoolean.False;
+                }
+                R.Set(RegExpInstance.PropertyLastIndex, m.Index + m.Length, throwOnError: true);
+                return JsBoolean.True;
+            }
+            
             var match = RegExpExec(r, s);
             return !match.IsNull();
         }
@@ -498,17 +607,17 @@ namespace Jint.Native.RegExp
             var rx = AssertThisIsObjectInstance(thisObj, "RegExp.prototype.search");
 
             var s = TypeConverter.ToString(arguments.At(0));
-            var previousLastIndex = rx.Get(RegExpInstance.KeyLastIndex);
+            var previousLastIndex = rx.Get(RegExpInstance.PropertyLastIndex);
             if (!SameValue(previousLastIndex, 0))
             {
-                rx.Set(RegExpInstance.KeyLastIndex, 0, true);
+                rx.Set(RegExpInstance.PropertyLastIndex, 0, true);
             }
 
             var result = RegExpExec(rx, s);
-            var currentLastIndex = rx.Get(RegExpInstance.KeyLastIndex);
+            var currentLastIndex = rx.Get(RegExpInstance.PropertyLastIndex);
             if (!SameValue(currentLastIndex, previousLastIndex))
             {
-                rx.Set(RegExpInstance.KeyLastIndex, previousLastIndex, true);
+                rx.Set(RegExpInstance.PropertyLastIndex, previousLastIndex, true);
             }
 
             if (result.IsNull())
@@ -516,7 +625,7 @@ namespace Jint.Native.RegExp
                 return -1;
             }
 
-            return result.Get("index");
+            return result.Get(PropertyIndex);
         }
 
         private JsValue Match(JsValue thisObj, JsValue[] arguments)
@@ -524,14 +633,14 @@ namespace Jint.Native.RegExp
             var rx = AssertThisIsObjectInstance(thisObj, "RegExp.prototype.match");
 
             var s = TypeConverter.ToString(arguments.At(0));
-            var global = TypeConverter.ToBoolean(rx.Get("global"));
+            var global = TypeConverter.ToBoolean(rx.Get(PropertyGlobal));
             if (!global)
             {
                 return RegExpExec(rx, s);
             }
 
             var fullUnicode = TypeConverter.ToBoolean(rx.Get("unicode"));
-            rx.Set(RegExpInstance.KeyLastIndex, JsNumber.PositiveZero, true);
+            rx.Set(RegExpInstance.PropertyLastIndex, JsNumber.PositiveZero, true);
 
             if (!fullUnicode
                 && rx is RegExpInstance rei
@@ -594,14 +703,13 @@ namespace Jint.Native.RegExp
                     return n == 0 ? Null : a;
                 }
 
-                Key keyZero = 0;
-                var matchStr = TypeConverter.ToString(result.Get(keyZero));
+                var matchStr = TypeConverter.ToString(result.Get(JsString.NumberZeroString));
                 a.SetIndexValue(n, matchStr, updateLength: false);
                 if (matchStr == "")
                 {
-                    var thisIndex = (int) TypeConverter.ToLength(rx.Get(RegExpInstance.KeyLastIndex));
+                    var thisIndex = (int) TypeConverter.ToLength(rx.Get(RegExpInstance.PropertyLastIndex));
                     var nextIndex = AdvanceStringIndex(s, thisIndex, fullUnicode);
-                    rx.Set(RegExpInstance.KeyLastIndex, nextIndex, true);
+                    rx.Set(RegExpInstance.PropertyLastIndex, nextIndex, true);
                 }
 
                 n++;
@@ -618,15 +726,15 @@ namespace Jint.Native.RegExp
             var s = TypeConverter.ToString(arguments.At(0));
             var c = SpeciesConstructor(r, _engine.RegExp);
 
-            var flags = TypeConverter.ToString(r.Get("flags"));
+            var flags = TypeConverter.ToJsString(r.Get(PropertyFlags));
             var matcher = Construct(c, new JsValue[]
             {
                 r,
                 flags
             });
 
-            var lastIndex = TypeConverter.ToLength(r.Get(RegExpInstance.KeyLastIndex));
-            matcher.Set(RegExpInstance.KeyLastIndex, lastIndex, true);
+            var lastIndex = TypeConverter.ToLength(r.Get(RegExpInstance.PropertyLastIndex));
+            matcher.Set(RegExpInstance.PropertyLastIndex, lastIndex, true);
 
             var global = flags.IndexOf('g') != -1;
             var fullUnicode = flags.IndexOf('u') != -1;
@@ -658,7 +766,7 @@ namespace Jint.Native.RegExp
 
         internal static JsValue RegExpExec(ObjectInstance r, string s)
         {
-            var exec = r.Get("exec");
+            var exec = r.Get(PropertyExec);
             if (exec is ICallable callable)
             {
                 var result = callable.Call(r, new JsValue[]  { s });
@@ -680,7 +788,7 @@ namespace Jint.Native.RegExp
 
         internal bool TryGetDefaultExec(ObjectInstance o, out Func<JsValue, JsValue[], JsValue> exec)
         {
-            if (o.Get("exec") is ClrFunctionInstance functionInstance && functionInstance._func == _defaultExec)
+            if (o.Get(PropertyExec) is ClrFunctionInstance functionInstance && functionInstance._func == _defaultExec)
             {
                 exec = _defaultExec;
                 return true;
@@ -693,7 +801,7 @@ namespace Jint.Native.RegExp
         private static JsValue RegExpBuiltinExec(RegExpInstance R, string s)
         {
             var length = s.Length;
-            var lastIndex = (int) TypeConverter.ToLength(R.Get(RegExpInstance.KeyLastIndex));
+            var lastIndex = (int) TypeConverter.ToLength(R.Get(RegExpInstance.PropertyLastIndex));
 
             var global = R.Global;
             var sticky = R.Sticky;
@@ -702,6 +810,21 @@ namespace Jint.Native.RegExp
                 lastIndex = 0;
             }
 
+            if (R.Source == RegExpInstance.regExpForMatchingAllCharacters)  // Reg Exp is really ""
+            {
+                if (lastIndex > s.Length)
+                {
+                    return Null;
+                }
+
+                // "aaa".match() => [ '', index: 0, input: 'aaa' ]
+                var array = R.Engine.Array.ConstructFast(1);
+                array.FastAddProperty(PropertyIndex, lastIndex, true, true, true);
+                array.FastAddProperty(PropertyInput, s, true, true, true);
+                array.SetIndexValue(0, JsString.Empty, updateLength: false);
+                return array;
+            }
+
             var matcher = R.Value;
             var fullUnicode = R.FullUnicode;
 
@@ -723,7 +846,7 @@ namespace Jint.Native.RegExp
             {
                 if (lastIndex > length)
                 {
-                    R.Set(RegExpInstance.KeyLastIndex, JsNumber.PositiveZero, true);
+                    R.Set(RegExpInstance.PropertyLastIndex, JsNumber.PositiveZero, true);
                     return Null;
                 }
 
@@ -733,7 +856,7 @@ namespace Jint.Native.RegExp
                 {
                     if (sticky)
                     {
-                        R.Set(RegExpInstance.KeyLastIndex, JsNumber.PositiveZero, true);
+                        R.Set(RegExpInstance.PropertyLastIndex, JsNumber.PositiveZero, true);
                         return Null;
                     }
 
@@ -760,7 +883,7 @@ namespace Jint.Native.RegExp
                 }
             }
 
-            R.Set("lastIndex", e, true);
+            R.Set(RegExpInstance.PropertyLastIndex, e, true);
 
             return CreateReturnValueArray(R.Engine, match, s, fullUnicode);
         }
@@ -768,8 +891,8 @@ namespace Jint.Native.RegExp
         private static ArrayInstance CreateReturnValueArray(Engine engine, Match match, string inputValue, bool fullUnicode)
         {
             var array = engine.Array.ConstructFast((ulong) match.Groups.Count);
-            array.CreateDataProperty("index", match.Index);
-            array.CreateDataProperty("input", inputValue);
+            array.CreateDataProperty(PropertyIndex, match.Index);
+            array.CreateDataProperty(PropertyInput, inputValue);
 
             ObjectInstance groups = null;
             for (uint i = 0; i < match.Groups.Count; i++)

+ 3 - 3
Jint/Native/Set/SetConstructor.cs

@@ -41,12 +41,12 @@ namespace Jint.Native.Set
 
         protected override void Initialize()
         {
-            var properties = new StringDictionarySlim<PropertyDescriptor>(2)
+            var symbols = new SymbolDictionary(1)
             {
                 [GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(_engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable), set: Undefined, PropertyFlag.Configurable)
             };
-            
-            SetProperties(properties, hasSymbols: true);
+
+            SetSymbols(symbols);
         }
 
         private static JsValue Species(JsValue thisObject, JsValue[] arguments)

+ 14 - 14
Jint/Native/Set/SetInstance.cs

@@ -9,53 +9,53 @@ namespace Jint.Native.Set
         internal readonly OrderedSet<JsValue> _set;
 
         public SetInstance(Engine engine)
-            : base(engine, objectClass: "Map")
+            : base(engine, ObjectClass.Map)
         {
             _set = new OrderedSet<JsValue>();
         }
 
-        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
+        public override PropertyDescriptor GetOwnProperty(JsValue property)
         {
-            if (propertyName == KnownKeys.Size)
+            if (property == CommonProperties.Size)
             {
                 return new PropertyDescriptor(_set.Count, PropertyFlag.None);
             }
 
-            return base.GetOwnProperty(propertyName);
+            return base.GetOwnProperty(property);
         }
 
-        protected override bool TryGetProperty(in Key propertyName, out PropertyDescriptor descriptor)
+        protected override bool TryGetProperty(JsValue property, out PropertyDescriptor descriptor)
         {
-            if (propertyName == KnownKeys.Size)
+            if (property == CommonProperties.Size)
             {
                 descriptor = new PropertyDescriptor(_set.Count, PropertyFlag.None);
                 return true;
             }
 
-            return base.TryGetProperty(propertyName, out descriptor);
+            return base.TryGetProperty(property, out descriptor);
         }
 
-        public void Add(JsValue value)
+        internal void Add(JsValue value)
         {
             _set.Add(value);
         }
 
-        public void Clear()
+        internal void Clear()
         {
             _set.Clear();
         }
 
-        public bool Has(JsValue key)
+        internal bool Has(JsValue key)
         {
             return _set.Contains(key);
         }
 
-        public bool Delete(JsValue key)
+        internal bool SetDelete(JsValue key)
         {
             return _set.Remove(key);
         }
 
-        public void ForEach(ICallable callable, JsValue thisArg)
+        internal void ForEach(ICallable callable, JsValue thisArg)
         {
             var args = _engine._jsValueArrayPool.RentArray(3);
             args[2] = this;
@@ -71,12 +71,12 @@ namespace Jint.Native.Set
             _engine._jsValueArrayPool.ReturnArray(args);
         }
 
-        public ObjectInstance Entries()
+        internal ObjectInstance Entries()
         {
             return _engine.Iterator.ConstructEntryIterator(this);
         }
 
-        public ObjectInstance Values()
+        internal ObjectInstance Values()
         {
             return _engine.Iterator.Construct(_set._list);
         }

+ 12 - 8
Jint/Native/Set/SetPrototype.cs

@@ -32,12 +32,10 @@ namespace Jint.Native.Set
 
         protected override void Initialize()
         {
-            var properties = new StringDictionarySlim<PropertyDescriptor>(15)
+            var properties = new PropertyDictionary(12, checkExistingKeys: false)
             {
-                [KnownKeys.Length] = new PropertyDescriptor(0, PropertyFlag.Configurable),
-                [KnownKeys.Constructor] = new PropertyDescriptor(_mapConstructor, PropertyFlag.NonEnumerable),
-                [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "iterator", Values, 1, PropertyFlag.Configurable), true, false, true),
-                [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("Set", false, false, true),
+                ["length"] = new PropertyDescriptor(0, PropertyFlag.Configurable),
+                ["constructor"] = new PropertyDescriptor(_mapConstructor, PropertyFlag.NonEnumerable),
                 ["add"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "add", Add, 1, PropertyFlag.Configurable), true, false, true),
                 ["clear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "clear", Clear, 0, PropertyFlag.Configurable), true, false, true),
                 ["delete"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "delete", Delete, 1, PropertyFlag.Configurable), true, false, true),
@@ -48,8 +46,14 @@ namespace Jint.Native.Set
                 ["values"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "values", Values, 0, PropertyFlag.Configurable), true, false, true),
                 ["size"] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(Engine, "get size", Size, 0, PropertyFlag.Configurable), set: null, PropertyFlag.Configurable)
             };
-            
-            SetProperties(properties, hasSymbols: true);
+            SetProperties(properties);
+
+            var symbols = new SymbolDictionary(2)
+            {
+                [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "iterator", Values, 1, PropertyFlag.Configurable), true, false, true),
+                [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("Set", false, false, true)
+            };
+            SetSymbols(symbols);
         }
         
         private JsValue Size(JsValue thisObj, JsValue[] arguments)
@@ -75,7 +79,7 @@ namespace Jint.Native.Set
         private JsValue Delete(JsValue thisObj, JsValue[] arguments)
         {
             var set = AssertSetInstance(thisObj);
-            return set.Delete(arguments[0])
+            return set.SetDelete(arguments[0])
                 ? JsBoolean.True
                 : JsBoolean.False;
         }

+ 4 - 4
Jint/Native/String/StringConstructor.cs

@@ -7,6 +7,7 @@ using Jint.Pooling;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
+using Jint.Runtime.Interpreter.Expressions;
 
 namespace Jint.Native.String
 {
@@ -39,14 +40,13 @@ namespace Jint.Native.String
 
         protected override void Initialize()
         {
-            var properties = new StringDictionarySlim<PropertyDescriptor>(3)
+            var properties = new PropertyDictionary(3, checkExistingKeys: false)
             {
                 ["fromCharCode"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "fromCharCode", FromCharCode, 1), PropertyFlag.NonEnumerable)),
                 ["fromCodePoint"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "fromCodePoint", FromCodePoint, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable)),
                 ["raw"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "raw", Raw, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable))
             };
-            
-            SetProperties(properties, hasSymbols: false);
+            SetProperties(properties);
         }
 
         private static JsValue FromCharCode(JsValue thisObj, JsValue[] arguments)
@@ -106,7 +106,7 @@ namespace Jint.Native.String
         private JsValue Raw(JsValue thisObj, JsValue[] arguments)
         {
             var cooked = TypeConverter.ToObject(_engine, arguments.At(0));
-            var raw = TypeConverter.ToObject(_engine, cooked.Get("raw", cooked));
+            var raw = TypeConverter.ToObject(_engine, cooked.Get(JintTaggedTemplateExpression.PropertyRaw, cooked));
 
             var operations = ArrayOperations.For(raw);
             var length = operations.GetLength();

+ 28 - 27
Jint/Native/String/StringInstance.cs

@@ -10,7 +10,7 @@ namespace Jint.Native.String
         internal PropertyDescriptor _length;
 
         public StringInstance(Engine engine)
-            : base(engine, objectClass: "String")
+            : base(engine, ObjectClass.String)
         {
         }
 
@@ -20,43 +20,44 @@ namespace Jint.Native.String
 
         public JsString PrimitiveValue { get; set; }
 
-        private static bool IsInt32(double d)
+        private static bool IsInt32(double d, out int intValue)
         {
-            return d >= int.MinValue && d <= int.MaxValue && ((int) d) == d;
+            if (d >= int.MinValue && d <= int.MaxValue)
+            {
+                intValue = (int) d;
+                return intValue == d;
+            }
+
+            intValue = 0;
+            return false;
         }
 
-        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
+        public override PropertyDescriptor GetOwnProperty(JsValue property)
         {
-            if (propertyName == KnownKeys.Infinity)
+            if (property == CommonProperties.Infinity)
             {
                 return PropertyDescriptor.Undefined;
             }
 
-            if (propertyName == KnownKeys.Length)
+            if (property == CommonProperties.Length)
             {
                 return _length ?? PropertyDescriptor.Undefined;
             }
 
-            var desc = base.GetOwnProperty(propertyName);
+            var desc = base.GetOwnProperty(property);
             if (desc != PropertyDescriptor.Undefined)
             {
                 return desc;
             }
 
-            if (!TypeConverter.CanBeIndex(propertyName))
-            {
-                return PropertyDescriptor.Undefined;
-            }
-
-            var number = TypeConverter.ToNumber(propertyName.Name);
-            if (!IsInt32(number))
+            if ((property._type & (InternalTypes.Number | InternalTypes.Integer | InternalTypes.String)) == 0)
             {
                 return PropertyDescriptor.Undefined;
             }
 
-            var index = (int) number;
             var str = PrimitiveValue.AsStringWithoutTypeCheck();
-            if (index < 0 || str.Length <= index)
+            var number = TypeConverter.ToNumber(property);
+            if (!IsInt32(number, out var index) || index < 0 || index >= str.Length)
             {
                 return PropertyDescriptor.Undefined;
             }
@@ -64,11 +65,11 @@ namespace Jint.Native.String
             return new PropertyDescriptor(str[index], PropertyFlag.OnlyEnumerable);
         }
 
-        public override IEnumerable<KeyValuePair<Key, PropertyDescriptor>> GetOwnProperties()
+        public override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
         {
             if (_length != null)
             {
-                yield return new KeyValuePair<Key, PropertyDescriptor>(KnownKeys.Length, _length);
+                yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Length, _length);
             }
 
             foreach (var entry in base.GetOwnProperties())
@@ -77,36 +78,36 @@ namespace Jint.Native.String
             }
         }
 
-        protected internal override void SetOwnProperty(in Key propertyName, PropertyDescriptor desc)
+        protected internal override void SetOwnProperty(JsValue property, PropertyDescriptor desc)
         {
-            if (propertyName == KnownKeys.Length)
+            if (property == CommonProperties.Length)
             {
                 _length = desc;
             }
             else
             {
-                base.SetOwnProperty(propertyName, desc);
+                base.SetOwnProperty(property, desc);
             }
         }
 
-        public override bool HasOwnProperty(in Key propertyName)
+        public override bool HasOwnProperty(JsValue property)
         {
-            if (propertyName == KnownKeys.Length)
+            if (property == CommonProperties.Length)
             {
                 return _length != null;
             }
 
-            return base.HasOwnProperty(propertyName);
+            return base.HasOwnProperty(property);
         }
 
-        public override void RemoveOwnProperty(in Key propertyName)
+        public override void RemoveOwnProperty(JsValue property)
         {
-            if (propertyName == KnownKeys.Length)
+            if (property == CommonProperties.Length)
             {
                 _length = null;
             }
 
-            base.RemoveOwnProperty(propertyName);
+            base.RemoveOwnProperty(property);
         }
     }
 }

+ 59 - 107
Jint/Native/String/StringPrototype.cs

@@ -43,9 +43,9 @@ namespace Jint.Native.String
         {
             const PropertyFlag lengthFlags = PropertyFlag.Configurable;
             const PropertyFlag propertyFlags = lengthFlags | PropertyFlag.Writable;
-            var properties = new StringDictionarySlim<PropertyDescriptor>(40)
+            var properties = new PropertyDictionary(35, checkExistingKeys: false)
             {
-                [KnownKeys.Constructor] = new PropertyDescriptor(_stringConstructor, PropertyFlag.NonEnumerable),
+                ["constructor"] = new PropertyDescriptor(_stringConstructor, PropertyFlag.NonEnumerable),
                 ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToStringString, 0, lengthFlags), propertyFlags),
                 ["valueOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "valueOf", ValueOf, 0, lengthFlags), propertyFlags),
                 ["charAt"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "charAt", CharAt, 1, lengthFlags), propertyFlags),
@@ -76,10 +76,15 @@ namespace Jint.Native.String
                 ["padEnd"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "padEnd", PadEnd, 1, lengthFlags), propertyFlags),
                 ["includes"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "includes", Includes, 1, lengthFlags), propertyFlags),
                 ["normalize"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "normalize", Normalize, 0, lengthFlags), propertyFlags),
-                ["repeat"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "repeat", Repeat, 1, lengthFlags), propertyFlags),
+                ["repeat"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "repeat", Repeat, 1, lengthFlags), propertyFlags)
+            };
+            SetProperties(properties);
+
+            var symbols = new SymbolDictionary(1)
+            {
                 [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "[Symbol.iterator]", Iterator, 0, lengthFlags), propertyFlags)
             };
-            SetProperties(properties, hasSymbols: true);
+            SetSymbols(symbols);
         }
 
         private ObjectInstance Iterator(JsValue thisObj, JsValue[] arguments)
@@ -194,21 +199,21 @@ namespace Jint.Native.String
         {
             TypeConverter.CheckObjectCoercible(_engine, thisObj);
             var s = TypeConverter.ToString(thisObj);
-            return s.ToUpper();
+            return new JsString(s.ToUpper());
         }
 
         private JsValue ToUpperCase(JsValue thisObj, JsValue[] arguments)
         {
             TypeConverter.CheckObjectCoercible(_engine, thisObj);
             var s = TypeConverter.ToString(thisObj);
-            return s.ToUpperInvariant();
+            return new JsString(s.ToUpperInvariant());
         }
 
         private JsValue ToLocaleLowerCase(JsValue thisObj, JsValue[] arguments)
         {
             TypeConverter.CheckObjectCoercible(_engine, thisObj);
             var s = TypeConverter.ToString(thisObj);
-            return s.ToLower();
+            return new JsString(s.ToLower());
         }
 
         private JsValue ToLowerCase(JsValue thisObj, JsValue[] arguments)
@@ -270,15 +275,15 @@ namespace Jint.Native.String
 
             if (length == 0)
             {
-                return string.Empty;
+                return JsString.Empty;
             }
 
             if (length == 1)
             {
-                return TypeConverter.ToString(s[from]);
+                return JsString.Create(s[from]);
             }
 
-            return s.Substring(from, length);
+            return new JsString(s.Substring(from, length));
         }
 
         private JsValue Substr(JsValue thisObj, JsValue[] arguments)
@@ -293,7 +298,7 @@ namespace Jint.Native.String
             length = System.Math.Min(System.Math.Max(length, 0), s.Length - start);
             if (length <= 0)
             {
-                return "";
+                return JsString.Empty;
             }
 
             var startIndex = TypeConverter.ToInt32(start);
@@ -312,6 +317,12 @@ namespace Jint.Native.String
 
             var separator = arguments.At(0);
             var limit = arguments.At(1);
+
+            // fast path for empty regexp
+            if (separator is RegExpInstance R && R.Source == RegExpInstance.regExpForMatchingAllCharacters)
+            {
+                separator = JsString.Empty;
+            }
             
             if (separator is ObjectInstance oi)
             {
@@ -347,106 +358,47 @@ namespace Jint.Native.String
             {
                 if (!separator.IsRegExp())
                 {
-                    separator = TypeConverter.ToString(separator); // Coerce into a string, for an object call toString()
+                    separator = TypeConverter.ToJsString(separator); // Coerce into a string, for an object call toString()
                 }
             }
 
-            var rx = TypeConverter.ToObject(Engine, separator) as RegExpInstance;
+            return SplitWithStringSeparator(_engine, separator, s, lim);
+        }
 
-            const string regExpForMatchingAllCharactere = "(?:)";
+        internal static JsValue SplitWithStringSeparator(Engine engine, JsValue separator, string s, uint lim)
+        {
+            var segments = StringExecutionContext.Current.SplitSegmentList;
+            segments.Clear();
+            var sep = TypeConverter.ToString(separator);
 
-            if (!ReferenceEquals(rx, null) &&
-                rx.Source != regExpForMatchingAllCharactere // We need pattern to be defined -> for s.split(new RegExp)
-                )
+            if (sep == string.Empty)
             {
-                var a = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
-                var match = rx.Value.Match(s, 0);
-
-                if (!match.Success) // No match at all return the string in an array
+                if (s.Length > segments.Capacity)
                 {
-                    a.SetIndexValue(0, s, updateLength: true);
-                    return a;
+                    segments.Capacity = s.Length;
                 }
 
-                int lastIndex = 0;
-                uint index = 0;
-                while (match.Success && index < lim)
+                for (var i = 0; i < s.Length; i++)
                 {
-                    if (match.Length == 0 && (match.Index == 0 || match.Index == len || match.Index == lastIndex))
-                    {
-                        match = match.NextMatch();
-                        continue;
-                    }
-
-                    // Add the match results to the array.
-                    a.SetIndexValue(index++, s.Substring(lastIndex, match.Index - lastIndex), updateLength: true);
-
-                    if (index >= lim)
-                    {
-                        return a;
-                    }
-
-                    lastIndex = match.Index + match.Length;
-                    for (int i = 1; i < match.Groups.Count; i++)
-                    {
-                        var group = match.Groups[i];
-                        var item = Undefined;
-                        if (group.Captures.Count > 0)
-                        {
-                            item = match.Groups[i].Value;
-                        }
-
-                        a.SetIndexValue(index++, item, updateLength: true);
-
-                        if (index >= lim)
-                        {
-                            return a;
-                        }
-                    }
-
-                    match = match.NextMatch();
-                    if (!match.Success) // Add the last part of the split
-                    {
-                        a.SetIndexValue(index++, s.Substring(lastIndex), updateLength: true);
-                    }
+                    segments.Add(TypeConverter.ToString(s[i]));
                 }
-
-                return a;
             }
             else
             {
-                var segments = StringExecutionContext.Current.SplitSegmentList;
-                segments.Clear();
-                var sep = TypeConverter.ToString(separator);
-
-                if (sep == string.Empty || (!ReferenceEquals(rx, null) && rx.Source == regExpForMatchingAllCharactere)) // for s.split(new RegExp)
-                {
-                    if (s.Length > segments.Capacity)
-                    {
-                        segments.Capacity = s.Length;
-                    }
-
-                    for (var i = 0; i < s.Length; i++)
-                    {
-                        segments.Add(TypeConverter.ToString(s[i]));
-                    }
-                }
-                else
-                {
-                    var array = StringExecutionContext.Current.SplitArray1;
-                    array[0] = sep;
-                    segments.AddRange(s.Split(array, StringSplitOptions.None));
-                }
+                var array = StringExecutionContext.Current.SplitArray1;
+                array[0] = sep;
+                segments.AddRange(s.Split(array, StringSplitOptions.None));
+            }
 
-                var length = (uint) System.Math.Min(segments.Count, lim);
-                var a = Engine.Array.ConstructFast(length);
-                for (int i = 0; i < length; i++)
-                {
-                    a.SetIndexValue((uint) i, segments[i], updateLength: false);
-                }
-                a.SetLength(length);
-                return a;
+            var length = (uint) System.Math.Min(segments.Count, lim);
+            var a = engine.Array.ConstructFast(length);
+            for (int i = 0; i < length; i++)
+            {
+                a.SetIndexValue((uint) i, segments[i], updateLength: false);
             }
+
+            a.SetLength(length);
+            return a;
         }
 
         private JsValue Slice(JsValue thisObj, JsValue[] arguments)
@@ -460,7 +412,7 @@ namespace Jint.Native.String
             }
             if (double.IsPositiveInfinity(start))
             {
-                return string.Empty;
+                return JsString.Empty;
             }
 
             var s = TypeConverter.ToString(thisObj);
@@ -479,15 +431,15 @@ namespace Jint.Native.String
 
             if (span == 0)
             {
-                return string.Empty;
+                return JsString.Empty;
             }
 
             if (span == 1)
             {
-                return TypeConverter.ToString(s[from]);
+                return JsString.Create(s[from]);
             }
 
-            return s.Substring(from, span);
+            return new JsString(s.Substring(from, span));
         }
 
         private JsValue Search(JsValue thisObj, JsValue[] arguments)
@@ -506,7 +458,7 @@ namespace Jint.Native.String
 
             var rx = (RegExpInstance) Engine.RegExp.Construct(new[] {regex});
             var s = TypeConverter.ToString(thisObj);
-            return Invoke(rx, GlobalSymbolRegistry.Search.ToPropertyKey(), new JsValue[] { s });
+            return Invoke(rx, GlobalSymbolRegistry.Search, new JsValue[] { s });
         }
 
         private JsValue Replace(JsValue thisObj, JsValue[] arguments)
@@ -525,13 +477,13 @@ namespace Jint.Native.String
                 }
             }
             
-            var thisString = TypeConverter.ToString(thisObj);
+            var thisString = TypeConverter.ToJsString(thisObj);
             var searchString = TypeConverter.ToString(searchValue);
             var functionalReplace = replaceValue is ICallable;
 
             if (!functionalReplace)
             {
-                replaceValue = TypeConverter.ToString(replaceValue);
+                replaceValue = TypeConverter.ToJsString(replaceValue);
             }
 
             var pos = thisString.IndexOf(searchString, StringComparison.Ordinal);
@@ -550,7 +502,7 @@ namespace Jint.Native.String
             else
             {
                 var captures = ArrayExt.Empty<string>();
-                replStr =  RegExpPrototype.GetSubstitution(matched, thisString, pos, captures, Undefined, TypeConverter.ToString(replaceValue));
+                replStr =  RegExpPrototype.GetSubstitution(matched, thisString.ToString(), pos, captures, Undefined, TypeConverter.ToString(replaceValue));
             }
 
             var tailPos = pos + matched.Length;
@@ -588,7 +540,7 @@ namespace Jint.Native.String
             {
                 if (regex.IsRegExp())
                 {
-                    var flags = regex.Get("flags");
+                    var flags = regex.Get(RegExpPrototype.PropertyFlags);
                     TypeConverter.CheckObjectCoercible(_engine, flags);
                     if (TypeConverter.ToString(flags).IndexOf('g') < 0)
                     {
@@ -769,9 +721,9 @@ namespace Jint.Native.String
             var size = s.Length;
             if (position >= size || position < 0)
             {
-                return "";
+                return JsString.Empty;
             }
-            return TypeConverter.ToString(s[(int) position]);
+            return JsString.Create(s[(int) position]);
         }
 
         private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)
@@ -827,7 +779,7 @@ namespace Jint.Native.String
                 ? " "
                 : TypeConverter.ToString(padStringValue);
 
-            var s = TypeConverter.ToString(thisObj);
+            var s = TypeConverter.ToJsString(thisObj);
             if (s.Length > targetLength || padString.Length == 0)
             {
                 return s;

+ 20 - 51
Jint/Native/Symbol/GlobalSymbolRegistry.cs

@@ -1,72 +1,41 @@
-using System.Threading;
-using Jint.Collections;
+using System.Collections.Generic;
 
 namespace Jint.Native.Symbol
 {
     public class GlobalSymbolRegistry
     {
-        public static readonly JsSymbol HasInstance = new JsSymbol("Symbol.hasInstance", 1);
-        public static readonly JsSymbol IsConcatSpreadable = new JsSymbol("Symbol.isConcatSpreadable", 2);
-        public static readonly JsSymbol Iterator = new JsSymbol("Symbol.iterator", 3);
-        public static readonly JsSymbol Match = new JsSymbol("Symbol.match", 4);
-        public static readonly JsSymbol MatchAll = new JsSymbol("Symbol.matchAll", 5);
-        public static readonly JsSymbol Replace = new JsSymbol("Symbol.replace", 6);
-        public static readonly JsSymbol Search = new JsSymbol("Symbol.search", 7);
-        public static readonly JsSymbol Species = new JsSymbol("Symbol.species", 8);
-        public static readonly JsSymbol Split = new JsSymbol("Symbol.split", 9);
-        public static readonly JsSymbol ToPrimitive = new JsSymbol("Symbol.toPrimitive", 10);
-        public static readonly JsSymbol ToStringTag = new JsSymbol("Symbol.toStringTag", 11);
-        public static readonly JsSymbol Unscopables = new JsSymbol("Symbol.unscopables", 12);
-
-        // well-known globals
-        private static readonly StringDictionarySlim<JsSymbol> _globalLookup;
+        public static readonly JsSymbol HasInstance = new JsSymbol("Symbol.hasInstance");
+        public static readonly JsSymbol IsConcatSpreadable = new JsSymbol("Symbol.isConcatSpreadable");
+        public static readonly JsSymbol Iterator = new JsSymbol("Symbol.iterator");
+        public static readonly JsSymbol Match = new JsSymbol("Symbol.match");
+        public static readonly JsSymbol MatchAll = new JsSymbol("Symbol.matchAll");
+        public static readonly JsSymbol Replace = new JsSymbol("Symbol.replace");
+        public static readonly JsSymbol Search = new JsSymbol("Symbol.search");
+        public static readonly JsSymbol Species = new JsSymbol("Symbol.species");
+        public static readonly JsSymbol Split = new JsSymbol("Symbol.split");
+        public static readonly JsSymbol ToPrimitive = new JsSymbol("Symbol.toPrimitive");
+        public static readonly JsSymbol ToStringTag = new JsSymbol("Symbol.toStringTag");
+        public static readonly JsSymbol Unscopables = new JsSymbol("Symbol.unscopables");
 
         // engine-specific created by scripts
-        private StringDictionarySlim<JsSymbol> _customSymbolLookup;
-
-        // custom symbol identity is based on running counter 
-        private int _symbolCounter = 100;
-
-        static GlobalSymbolRegistry()
-        {
-            _globalLookup = new StringDictionarySlim<JsSymbol>
-            {
-                [HasInstance.ToPropertyKey().Name] = HasInstance,
-                [IsConcatSpreadable.ToPropertyKey().Name] = IsConcatSpreadable,
-                [Iterator.ToPropertyKey().Name] = Iterator,
-                [Match.ToPropertyKey().Name] = Match,
-                [Replace.ToPropertyKey().Name] = Replace,
-                [Search.ToPropertyKey().Name] = Search,
-                [Species.ToPropertyKey().Name] = Species,
-                [Split.ToPropertyKey().Name] = Split,
-                [ToPrimitive.ToPropertyKey().Name] = ToPrimitive,
-                [ToStringTag.ToPropertyKey().Name] = ToStringTag,
-                [Unscopables.ToPropertyKey().Name] = Unscopables
-            };
-        }
+        private Dictionary<JsValue, JsSymbol> _customSymbolLookup;
 
-        internal bool TryGetSymbol(Key key, out JsSymbol symbol)
+        internal bool TryGetSymbol(JsValue key, out JsSymbol symbol)
         {
             symbol = null;
-            return _customSymbolLookup != null && _customSymbolLookup.TryGetValue(key.Name, out symbol);
+            return _customSymbolLookup != null
+                   && _customSymbolLookup.TryGetValue(key, out symbol);
         }
 
         internal void Add(JsSymbol symbol)
         {
-            _customSymbolLookup ??= new StringDictionarySlim<JsSymbol>();
-            _customSymbolLookup[symbol.ToPropertyKey().Name] = symbol;
+            _customSymbolLookup ??= new Dictionary<JsValue, JsSymbol>();
+            _customSymbolLookup[symbol._value] = symbol;
         }
 
         internal JsSymbol CreateSymbol(JsValue description)
         {
-            var identity = Interlocked.Increment(ref _symbolCounter);
-            return new JsSymbol(description, identity);
-        }
-
-        internal JsSymbol CreateSymbol(string description)
-        {
-            var identity = Interlocked.Increment(ref _symbolCounter);
-            return new JsSymbol(description, identity);
+            return new JsSymbol(description);
         }
     }
 }

+ 6 - 7
Jint/Native/Symbol/SymbolConstructor.cs

@@ -43,7 +43,7 @@ namespace Jint.Native.Symbol
             const PropertyFlag lengthFlags = PropertyFlag.Configurable;
             const PropertyFlag propertyFlags = PropertyFlag.AllForbidden;
 
-            var properties = new StringDictionarySlim<PropertyDescriptor>(15)
+            var properties = new PropertyDictionary(15, checkExistingKeys: false)
             {
                 ["for"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "for", For, 1, lengthFlags), PropertyFlag.Writable | PropertyFlag.Configurable),
                 ["keyFor"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "keyFor", KeyFor, 1, lengthFlags), PropertyFlag.Writable | PropertyFlag.Configurable),
@@ -60,7 +60,7 @@ namespace Jint.Native.Symbol
                 ["toStringTag"] = new PropertyDescriptor(GlobalSymbolRegistry.ToStringTag, propertyFlags),
                 ["unscopables"] = new PropertyDescriptor(GlobalSymbolRegistry.Unscopables, propertyFlags)
             };
-            SetProperties(properties, hasSymbols: false);
+            SetProperties(properties);
         }
 
         /// <summary>
@@ -71,7 +71,7 @@ namespace Jint.Native.Symbol
             var description = arguments.At(0);
             var descString = description.IsUndefined()
                 ? Undefined
-                : TypeConverter.ToString(description);
+                : TypeConverter.ToJsString(description);
 
             var value = _engine.GlobalSymbolRegistry.CreateSymbol(descString);
             return value;
@@ -79,7 +79,7 @@ namespace Jint.Native.Symbol
 
         public JsValue For(JsValue thisObj, JsValue[] arguments)
         {
-            var stringKey = TypeConverter.ToString(arguments.At(0));
+            var stringKey = TypeConverter.ToJsString(arguments.At(0));
 
             // 2. ReturnIfAbrupt(stringKey).
 
@@ -99,10 +99,9 @@ namespace Jint.Native.Symbol
                 return ExceptionHelper.ThrowTypeError<JsValue>(Engine);
             }
 
-            var key = sym.ToPropertyKey();
-            if (_engine.GlobalSymbolRegistry.TryGetSymbol(key, out var e))
+            if (_engine.GlobalSymbolRegistry.TryGetSymbol(sym._value, out var e))
             {
-                return e.ToPropertyKey().Name;
+                return e._value;
             }
 
             return Undefined;

+ 1 - 1
Jint/Native/Symbol/SymbolInstance.cs

@@ -6,7 +6,7 @@ namespace Jint.Native.Symbol
     public class SymbolInstance : ObjectInstance, IPrimitiveInstance
     {
         public SymbolInstance(Engine engine)
-            : base(engine, objectClass: "Symbol")
+            : base(engine, ObjectClass.Symbol)
         {
         }
 

+ 12 - 8
Jint/Native/Symbol/SymbolPrototype.cs

@@ -34,16 +34,20 @@ namespace Jint.Native.Symbol
         {
             const PropertyFlag lengthFlags = PropertyFlag.Configurable;
             const PropertyFlag propertyFlags = PropertyFlag.Configurable;
-            SetProperties(new StringDictionarySlim<PropertyDescriptor>(8)
+            SetProperties(new PropertyDictionary(5, checkExistingKeys: false)
             {
-                [KnownKeys.Length] = new PropertyDescriptor(JsNumber.PositiveZero, propertyFlags),
-                [KnownKeys.Constructor] = new PropertyDescriptor(_symbolConstructor, PropertyFlag.Configurable | PropertyFlag.Writable),
+                ["length"] = new PropertyDescriptor(JsNumber.PositiveZero, propertyFlags),
+                ["constructor"] = new PropertyDescriptor(_symbolConstructor, PropertyFlag.Configurable | PropertyFlag.Writable),
                 ["description"] = new GetSetPropertyDescriptor(new ClrFunctionInstance(Engine, "description", Description, 0, lengthFlags), Undefined, propertyFlags),
                 ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToSymbolString, 0, lengthFlags), PropertyFlag.Configurable | PropertyFlag.Writable),
-                ["valueOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "valueOf", ValueOf, 0, lengthFlags), PropertyFlag.Configurable | PropertyFlag.Writable),
-                [GlobalSymbolRegistry.ToPrimitive] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "[Symbol.toPrimitive]", ToPrimitive, 1, lengthFlags), propertyFlags),
-                [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor(new JsString("Symbol"), propertyFlags)
-            }, true);
+                ["valueOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "valueOf", ValueOf, 0, lengthFlags), PropertyFlag.Configurable | PropertyFlag.Writable)
+            });
+
+            SetSymbols(new SymbolDictionary(1)
+                {
+                    [GlobalSymbolRegistry.ToPrimitive] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "[Symbol.toPrimitive]", ToPrimitive, 1, lengthFlags), propertyFlags), [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor(new JsString("Symbol"), propertyFlags)
+                }
+            );
         }
 
         private JsValue Description(JsValue thisObject, JsValue[] arguments)
@@ -55,7 +59,7 @@ namespace Jint.Native.Symbol
         private JsValue ToSymbolString(JsValue thisObject, JsValue[] arguments)
         {
             var sym = ThisSymbolValue(thisObject);
-            return SymbolDescriptiveString(sym);
+            return new JsString(SymbolDescriptiveString(sym));
         }
 
         private JsValue ValueOf(JsValue thisObject, JsValue[] arguments)

+ 2 - 2
Jint/Pooling/ReferencePool.cs

@@ -18,10 +18,10 @@ namespace Jint.Pooling
 
         private static Reference Factory()
         {
-            return new Reference(JsValue.Undefined, string.Empty, false);
+            return new Reference(JsValue.Undefined, JsString.Empty, false);
         }
 
-        public Reference Rent(JsValue baseValue, in Key name, bool strict)
+        public Reference Rent(JsValue baseValue, JsValue name, bool strict)
         {
             return _pool.Allocate().Reassign(baseValue, name, strict);
         }

+ 1 - 1
Jint/Runtime/CallStack/CallStackElementComparer.cs

@@ -6,7 +6,7 @@
     {
         public bool Equals(CallStackElement x, CallStackElement y)
         {
-            return x.Function == y.Function;
+            return ReferenceEquals(x?.Function, y?.Function);
         }
 
         public int GetHashCode(CallStackElement obj)

+ 27 - 0
Jint/Runtime/CommonProperties.cs

@@ -0,0 +1,27 @@
+using Jint.Native;
+
+namespace Jint.Runtime
+{
+    internal static class CommonProperties
+    {
+        internal static readonly JsString Arguments = new JsString("arguments");
+        internal static readonly JsString Caller = new JsString("caller");
+        internal static readonly JsString Callee = new JsString("callee");
+        internal static readonly JsString Constructor = new JsString("constructor");
+        internal static readonly JsString Eval = new JsString("eval");
+        internal static readonly JsString Infinity = new JsString("Infinity");
+        internal static readonly JsString Length = new JsString("length");
+        internal static readonly JsString Name = new JsString("name");
+        internal static readonly JsString Prototype = new JsString("prototype");
+        internal static readonly JsString Size = new JsString("size");
+        internal static readonly JsString Next = new JsString("next");
+        internal static readonly JsString Done = new JsString("done");
+        internal static readonly JsString Value = new JsString("value");
+        internal static readonly JsString Return = new JsString("return");
+        internal static readonly JsString Set = new JsString("set");
+        internal static readonly JsString Get = new JsString("get");
+        internal static readonly JsString Writable = new JsString("writable");
+        internal static readonly JsString Enumerable = new JsString("enumerable");
+        internal static readonly JsString Configurable = new JsString("configurable");
+    }
+}

+ 9 - 10
Jint/Runtime/Descriptors/PropertyDescriptor.cs

@@ -227,12 +227,12 @@ namespace Jint.Runtime.Descriptors
                 ExceptionHelper.ThrowTypeError(engine);
             }
 
-            var getProperty = obj.GetProperty("get");
+            var getProperty = obj.GetProperty(CommonProperties.Get);
             var hasGetProperty = getProperty != Undefined;
-            var setProperty = obj.GetProperty("set");
+            var setProperty = obj.GetProperty(CommonProperties.Set);
             var hasSetProperty = setProperty != Undefined;
 
-            if ((obj.HasProperty("value") || obj.HasProperty("writable")) &&
+            if ((obj.HasProperty(CommonProperties.Value) || obj.HasProperty(CommonProperties.Writable)) &&
                 (hasGetProperty || hasSetProperty))
             {
                 ExceptionHelper.ThrowTypeError(engine);
@@ -242,27 +242,27 @@ namespace Jint.Runtime.Descriptors
                 ? new GetSetPropertyDescriptor(null, null, PropertyFlag.None)
                 : new PropertyDescriptor(PropertyFlag.None);
 
-            var enumerableProperty = obj.GetProperty("enumerable");
+            var enumerableProperty = obj.GetProperty(CommonProperties.Enumerable);
             if (enumerableProperty != Undefined)
             {
                 desc.Enumerable = TypeConverter.ToBoolean(obj.UnwrapJsValue(enumerableProperty));
                 desc.EnumerableSet = true;
             }
 
-            var configurableProperty = obj.GetProperty("configurable");
+            var configurableProperty = obj.GetProperty(CommonProperties.Configurable);
             if (configurableProperty != Undefined)
             {
                 desc.Configurable = TypeConverter.ToBoolean(obj.UnwrapJsValue(configurableProperty));
                 desc.ConfigurableSet = true;
             }
 
-            var valueProperty = obj.GetProperty("value");
+            var valueProperty = obj.GetProperty(CommonProperties.Value);
             if (valueProperty != Undefined)
             {
                 desc.Value = obj.UnwrapJsValue(valueProperty);
             }
 
-            var writableProperty = obj.GetProperty("writable");
+            var writableProperty = obj.GetProperty(CommonProperties.Writable);
             if (writableProperty != Undefined)
             {
                 desc.Writable = TypeConverter.ToBoolean(obj.UnwrapJsValue(writableProperty));
@@ -310,7 +310,7 @@ namespace Jint.Runtime.Descriptors
             }
 
             var obj = engine.Object.Construct(Arguments.Empty);
-            var properties = new StringDictionarySlim<PropertyDescriptor>(4);
+            var properties = new PropertyDictionary(4, checkExistingKeys: false);
 
             if (desc.IsDataDescriptor())
             {
@@ -326,7 +326,7 @@ namespace Jint.Runtime.Descriptors
             properties["enumerable"] = new PropertyDescriptor(desc.Enumerable, PropertyFlag.ConfigurableEnumerableWritable);
             properties["configurable"] = new PropertyDescriptor(desc.Configurable, PropertyFlag.ConfigurableEnumerableWritable);
 
-            obj.SetProperties(properties, hasSymbols: false);
+            obj.SetProperties(properties);
             return obj;
         }
 
@@ -407,7 +407,6 @@ namespace Jint.Runtime.Descriptors
 
             public static readonly AllForbiddenDescriptor NumberZero = new AllForbiddenDescriptor(JsNumber.Create(0));
             public static readonly AllForbiddenDescriptor NumberOne = new AllForbiddenDescriptor(JsNumber.Create(1));
-            public static readonly AllForbiddenDescriptor NumberTwo = new AllForbiddenDescriptor(JsNumber.Create(2));
 
             public static readonly AllForbiddenDescriptor BooleanFalse = new AllForbiddenDescriptor(JsBoolean.False);
             public static readonly AllForbiddenDescriptor BooleanTrue = new AllForbiddenDescriptor(JsBoolean.True);

+ 5 - 2
Jint/Runtime/Descriptors/PropertyFlag.cs

@@ -12,9 +12,12 @@ namespace Jint.Runtime.Descriptors
         WritableSet = 8,
         Configurable = 16,
         ConfigurableSet = 32,
-        
+
         CustomJsValue = 256,
-        
+
+        // we can check for mutable binding and do some fast assignments
+        MutableBinding = 512,
+
         // common helpers
         AllForbidden = ConfigurableSet | EnumerableSet | WritableSet,
         ConfigurableEnumerableWritable = Configurable | Enumerable | Writable,

+ 3 - 3
Jint/Runtime/Descriptors/Specialized/ClrAccessDescriptor.cs

@@ -21,11 +21,11 @@ namespace Jint.Runtime.Descriptors.Specialized
         {
             _env = env;
             _engine = engine;
-            _name = new Key(name);
+            _name = name;
         }
 
-        public override JsValue Get => _get = _get ?? new GetterFunctionInstance(_engine, DoGet);
-        public override JsValue Set => _set = _set ?? new SetterFunctionInstance(_engine, DoSet);
+        public override JsValue Get => _get ??= new GetterFunctionInstance(_engine, DoGet);
+        public override JsValue Set => _set ??= new SetterFunctionInstance(_engine, DoSet);
 
         private JsValue DoGet(JsValue n)
         {

+ 8 - 3
Jint/Runtime/Environments/Binding.cs

@@ -2,7 +2,7 @@
 
 namespace Jint.Runtime.Environments
 {
-    public struct Binding
+    public readonly struct Binding
     {
         public Binding(JsValue value, bool canBeDeleted, bool mutable)
         {
@@ -11,10 +11,15 @@ namespace Jint.Runtime.Environments
             Mutable = mutable;
         }
 
-        public JsValue Value;
+        public readonly JsValue Value;
         public readonly bool CanBeDeleted;
         public readonly bool Mutable;
 
-        public bool IsInitialized => !ReferenceEquals(Value, null);
+        public Binding ChangeValue(JsValue argument)
+        {
+            return new Binding(argument, CanBeDeleted, Mutable);
+        }
+
+        public bool IsInitialized() => !(Value is null);
     }
 }

+ 27 - 20
Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs

@@ -15,9 +15,9 @@ namespace Jint.Runtime.Environments
     /// Represents a declarative environment record
     /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.1.1
     /// </summary>
-    public sealed class DeclarativeEnvironmentRecord : EnvironmentRecord
+    internal sealed class DeclarativeEnvironmentRecord : EnvironmentRecord
     {
-        private readonly HybridDictionary<Key, Binding> _dictionary = new HybridDictionary<Key, Binding>();
+        private readonly HybridDictionary<Binding> _dictionary = new HybridDictionary<Binding>();
 
         public DeclarativeEnvironmentRecord(Engine engine) : base(engine)
         {
@@ -25,15 +25,16 @@ namespace Jint.Runtime.Environments
 
         private bool ContainsKey(in Key key)
         {
-            return _dictionary?.ContainsKey(key) == true;
+            return _dictionary.ContainsKey(key);
         }
 
         private bool TryGetValue(in Key key, out Binding value)
         {
+            value = default;
             return _dictionary.TryGetValue(key, out value);
         }
 
-        public override bool HasBinding(in Key name)
+        public override bool HasBinding(string name)
         {
             return ContainsKey(name);
         }
@@ -44,23 +45,24 @@ namespace Jint.Runtime.Environments
             out Binding binding,
             out JsValue value)
         {
+            binding = default;
             var success = _dictionary.TryGetValue(name, out binding);
             value = success ? UnwrapBindingValue(strict, binding) : default;
             return success;
         }
 
-        public override void CreateMutableBinding(in Key name, JsValue value, bool canBeDeleted = false)
+        public override void CreateMutableBinding(string name, JsValue value, bool canBeDeleted = false)
         {
             _dictionary[name] = new Binding(value, canBeDeleted, mutable: true);
         }
 
-        public override void SetMutableBinding(in Key name, JsValue value, bool strict)
+        public override void SetMutableBinding(string name, JsValue value, bool strict)
         {
-            _dictionary.TryGetValue(name, out var binding);
+            var key = name;
+            _dictionary.TryGetValue(key, out var binding);
             if (binding.Mutable)
             {
-                binding.Value = value;
-                _dictionary[name] = binding;
+                _dictionary[key] = binding.ChangeValue(value);
             }
             else
             {
@@ -71,7 +73,7 @@ namespace Jint.Runtime.Environments
             }
         }
 
-        public override JsValue GetBindingValue(in Key name, bool strict)
+        public override JsValue GetBindingValue(string name, bool strict)
         {
             _dictionary.TryGetValue(name, out var binding);
             return UnwrapBindingValue(strict, binding);
@@ -80,7 +82,7 @@ namespace Jint.Runtime.Environments
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private JsValue UnwrapBindingValue(bool strict, in Binding binding)
         {
-            if (!binding.Mutable && !binding.IsInitialized)
+            if (!binding.Mutable && !binding.IsInitialized())
             {
                 if (strict)
                 {
@@ -98,7 +100,7 @@ namespace Jint.Runtime.Environments
             throw new JavaScriptException(_engine.ReferenceError, "Can't access an uninitialized immutable binding.");
         }
 
-        public override bool DeleteBinding(in Key name)
+        public override bool DeleteBinding(string name)
         {
             if (!_dictionary.TryGetValue(name, out var binding))
             {
@@ -121,9 +123,14 @@ namespace Jint.Runtime.Environments
         }
 
         /// <inheritdoc />
-        public override Key[] GetAllBindingNames()
+        internal override string[] GetAllBindingNames()
         {
-            var keys = new Key[_dictionary.Count];
+            if (_dictionary is null)
+            {
+                return ArrayExt.Empty<string>();
+            }
+
+            var keys = new string[_dictionary.Count];
             var n = 0;
             foreach (var entry in _dictionary)
             {
@@ -144,8 +151,9 @@ namespace Jint.Runtime.Environments
                 _dictionary[KnownKeys.Arguments] = new Binding(argumentsInstance, canBeDeleted: false, mutable: true);
             }
 
-            var parameters = functionDeclaration.Params;
-            for (var i = 0; i < parameters.Count; i++)
+            ref readonly var parameters = ref functionDeclaration.Params;
+            var count = parameters.Count;
+            for (var i = 0; i < count; i++)
             {
                 SetFunctionParameter(parameters[i], arguments, i, empty);
             }
@@ -160,7 +168,7 @@ namespace Jint.Runtime.Environments
         {
             if (parameter is Identifier identifier)
             {
-                var argument = arguments.Length > index ? arguments[index] : Undefined;
+                var argument = (uint) index < (uint) arguments.Length ? arguments[index] : Undefined;
                 SetItemSafely(identifier.Name, argument, initiallyEmpty);
             }
             else
@@ -294,7 +302,7 @@ namespace Jint.Runtime.Environments
                         var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
                         var paramVarEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
 
-                        _engine.EnterExecutionContext(paramVarEnv, paramVarEnv, _engine.ExecutionContext.ThisBinding);;
+                        _engine.EnterExecutionContext(paramVarEnv, paramVarEnv, _engine.ExecutionContext.ThisBinding);
                         var result = exp.GetValue();
                         _engine.LeaveExecutionContext();
 
@@ -327,8 +335,7 @@ namespace Jint.Runtime.Environments
             {
                 if (existing.Mutable)
                 {
-                    existing.Value = argument;
-                    _dictionary[name] = existing;
+                    _dictionary[name] = existing.ChangeValue(argument);
                 }
                 else
                 {

+ 6 - 6
Jint/Runtime/Environments/EnvironmentRecord.cs

@@ -20,7 +20,7 @@ namespace Jint.Runtime.Environments
         /// </summary>
         /// <param name="name">The identifier of the binding</param>
         /// <returns><c>true</c> if it does and <c>false</c> if it does not.</returns>
-        public abstract bool HasBinding(in Key name);
+        public abstract bool HasBinding(string name);
 
         internal abstract bool TryGetBinding(
             in Key name,
@@ -34,7 +34,7 @@ namespace Jint.Runtime.Environments
         /// <param name="name">The identifier of the binding.</param>
         /// <param name="value">The value of the binding.</param>
         /// <param name="canBeDeleted"><c>true</c> if the binding may be subsequently deleted.</param>
-        public abstract void CreateMutableBinding(in Key name, JsValue value, bool canBeDeleted = false);
+        public abstract void CreateMutableBinding(string name, JsValue value, bool canBeDeleted = false);
 
         /// <summary>
         /// Sets the value of an already existing mutable binding in an environment record.
@@ -42,7 +42,7 @@ namespace Jint.Runtime.Environments
         /// <param name="name">The identifier of the binding</param>
         /// <param name="value">The value of the binding.</param>
         /// <param name="strict">The identify strict mode references.</param>
-        public abstract void SetMutableBinding(in Key name, JsValue value, bool strict);
+        public abstract void SetMutableBinding(string name, JsValue value, bool strict);
 
         /// <summary>
         /// Returns the value of an already existing binding from an environment record.
@@ -50,14 +50,14 @@ namespace Jint.Runtime.Environments
         /// <param name="name">The identifier of the binding</param>
         /// <param name="strict">The identify strict mode references.</param>
         /// <return>The value of an already existing binding from an environment record.</return>
-        public abstract JsValue GetBindingValue(in Key name, bool strict);
+        public abstract JsValue GetBindingValue(string name, bool strict);
 
         /// <summary>
         /// Delete a binding from an environment record. The String value N is the text of the bound name If a binding for N exists, remove the binding and return true. If the binding exists but cannot be removed return false. If the binding does not exist return true.
         /// </summary>
         /// <param name="name">The identifier of the binding</param>
         /// <returns><true>true</true> if the deletion is successfull.</returns>
-        public abstract bool DeleteBinding(in Key name);
+        public abstract bool DeleteBinding(string name);
 
         /// <summary>
         /// Returns the value to use as the <c>this</c> value on calls to function objects that are obtained as binding values from this environment record.
@@ -69,7 +69,7 @@ namespace Jint.Runtime.Environments
         /// Returns an array of all the defined binding names
         /// </summary>
         /// <returns>The array of all defined bindings</returns>
-        public abstract Key[] GetAllBindingNames();
+        internal abstract string[] GetAllBindingNames();
         
         public override object ToObject()
         {

+ 95 - 0
Jint/Runtime/Environments/GlobalEnvironmentRecord.cs

@@ -0,0 +1,95 @@
+using System.Linq;
+using Jint.Native;
+using Jint.Native.Global;
+using Jint.Native.Object;
+using Jint.Runtime.Descriptors;
+
+namespace Jint.Runtime.Environments
+{
+    /// <summary>
+    /// Optimized for GlobalObject, which we know of and can skip some virtual calls.
+    /// http://www.ecma-international.org/ecma-262/6.0/#sec-global-environment-records
+    /// </summary>
+    internal sealed class GlobalEnvironmentRecord : EnvironmentRecord
+    {
+        private readonly GlobalObject _global;
+
+        public GlobalEnvironmentRecord(Engine engine, GlobalObject global) : base(engine)
+        {
+            _global = global;
+        }
+
+        public override bool HasBinding(string name) => _global.Properties.ContainsKey(name);
+
+        internal override bool TryGetBinding(
+            in Key name,
+            bool strict,
+            out Binding binding,
+            out JsValue value)
+        {
+            // we unwrap by name
+            binding = default;
+
+            Key key = name;
+            if (!_global.Properties.TryGetValue(key, out var desc))
+            {
+                value = default;
+                return false;
+            }
+
+            value = ObjectInstance.UnwrapJsValue(desc, _global);
+            return true;
+        }
+
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.1.2.2
+        /// </summary>
+        public override void CreateMutableBinding(string name, JsValue value, bool configurable = false)
+        {
+            var propertyDescriptor = configurable
+                ? new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable | PropertyFlag.MutableBinding)
+                : new PropertyDescriptor(value, PropertyFlag.NonConfigurable | PropertyFlag.MutableBinding);
+
+            _global.DefinePropertyOrThrow(name, propertyDescriptor);
+        }
+
+        public override void SetMutableBinding(string name, JsValue value, bool strict)
+        {
+            if (!_global.Set(name, value) && strict)
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+        }
+
+        public override JsValue GetBindingValue(string name, bool strict)
+        {
+            var desc = _global.GetProperty(name);
+            if (strict && desc == PropertyDescriptor.Undefined)
+            {
+                ExceptionHelper.ThrowReferenceError(_engine, name.ToString());
+            }
+
+            return ObjectInstance.UnwrapJsValue(desc, this);
+        }
+
+        public override bool DeleteBinding(string name)
+        {
+            return _global.Delete(name);
+        }
+
+        public override JsValue ImplicitThisValue()
+        {
+            return Undefined;
+        }
+
+        internal override string[] GetAllBindingNames()
+        {
+            return _global.GetOwnProperties().Select( x=> x.Key.ToString()).ToArray();
+        }
+
+        public override bool Equals(JsValue other)
+        {
+            return ReferenceEquals(_global, other);
+        }
+    }
+}

+ 15 - 27
Jint/Runtime/Environments/LexicalEnvironment.cs

@@ -1,7 +1,6 @@
-using System.Runtime.CompilerServices;
-using Jint.Native;
+using Jint.Native;
+using Jint.Native.Global;
 using Jint.Native.Object;
-using Jint.Runtime.References;
 
 namespace Jint.Runtime.Environments
 {
@@ -23,20 +22,6 @@ namespace Jint.Runtime.Environments
             _outer = outer;
         }
 
-        public EnvironmentRecord Record => _record;
-
-        public LexicalEnvironment Outer => _outer;
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static Reference GetIdentifierReference(LexicalEnvironment lex, in Key name, bool strict)
-        {
-            var identifierEnvironment = TryGetIdentifierEnvironmentWithBindingValue(lex, name, strict, out var temp, out _)
-                ? temp
-                : JsValue.Undefined;
-
-            return lex._engine._referencePool.Rent(identifierEnvironment, name, strict);
-        }
-
         internal static bool TryGetIdentifierEnvironmentWithBindingValue(
             LexicalEnvironment lex,
             in Key name,
@@ -44,7 +29,10 @@ namespace Jint.Runtime.Environments
             out EnvironmentRecord record,
             out JsValue value)
         {
-            while (true)
+            record = default;
+            value = default;
+
+            while (lex != null)
             {
                 if (lex._record.TryGetBinding(
                     name,
@@ -56,15 +44,10 @@ namespace Jint.Runtime.Environments
                     return true;
                 }
 
-                if (lex._outer == null)
-                {
-                    record = default;
-                    value = default;
-                    return false;
-                }
-
                 lex = lex._outer;
             }
+
+            return false;
         }
 
         public static LexicalEnvironment NewDeclarativeEnvironment(Engine engine, LexicalEnvironment outer = null)
@@ -76,9 +59,14 @@ namespace Jint.Runtime.Environments
             return environment;
         }
 
-        public static LexicalEnvironment NewObjectEnvironment(Engine engine, ObjectInstance objectInstance, LexicalEnvironment outer, bool provideThis)
+        internal static LexicalEnvironment NewGlobalEnvironment(Engine engine, GlobalObject objectInstance)
         {
-            return new LexicalEnvironment(engine, new ObjectEnvironmentRecord(engine, objectInstance, provideThis), outer);
+            return new LexicalEnvironment(engine, new GlobalEnvironmentRecord(engine, objectInstance), null);
         }
+
+        internal static LexicalEnvironment NewObjectEnvironment(Engine engine, ObjectInstance objectInstance, LexicalEnvironment outer, bool provideThis)
+        {
+            return new LexicalEnvironment(engine, new ObjectEnvironmentRecord(engine, objectInstance, provideThis), outer);
+        } 
     }
 }

+ 24 - 25
Jint/Runtime/Environments/ObjectEnvironmentRecord.cs

@@ -11,7 +11,7 @@ namespace Jint.Runtime.Environments
     /// Represents an object environment record
     /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.1.2
     /// </summary>
-    public sealed class ObjectEnvironmentRecord : EnvironmentRecord
+    internal sealed class ObjectEnvironmentRecord : EnvironmentRecord
     {
         private readonly ObjectInstance _bindingObject;
         private readonly bool _provideThis;
@@ -22,23 +22,22 @@ namespace Jint.Runtime.Environments
             _provideThis = provideThis;
         }
 
-        public override bool HasBinding(in Key name)
+        public override bool HasBinding(string name)
         {
-            var foundBinding = _bindingObject.HasProperty(name);
+            var property = new JsString(name);
+            var foundBinding = HasProperty(property);
 
             if (!foundBinding)
             {
                 return false;
             }
-            
-            // TODO If the withEnvironment flag of envRec is false, return true.
 
-            if (IsBlocked(name))
-            {
-                return false;
-            }
+            return !IsBlocked(name);
+        }
 
-            return true;
+        private bool HasProperty(JsValue property)
+        {
+            return _bindingObject.HasProperty(property);
         }
 
         internal override bool TryGetBinding(
@@ -50,23 +49,23 @@ namespace Jint.Runtime.Environments
             // we unwrap by name
             binding = default;
 
-            if (!_bindingObject.HasProperty(name) || IsBlocked(name))
+            if (!HasProperty(name.Name) || IsBlocked(name))
             {
                 value = default;
                 return false;
             }
 
-            var desc = _bindingObject.GetProperty(name);
+            var desc = _bindingObject.GetProperty(name.Name);
             value = ObjectInstance.UnwrapJsValue(desc, _bindingObject);
             return true;
         }
 
-        private bool IsBlocked(in Key name)
+        private bool IsBlocked(string property)
         {
-            var unscopables = _bindingObject._hasSymbols ? _bindingObject.Get(GlobalSymbolRegistry.Unscopables) : null;
+            var unscopables = _bindingObject.Get(GlobalSymbolRegistry.Unscopables);
             if (unscopables is ObjectInstance oi)
             {
-                var blocked = TypeConverter.ToBoolean(oi.Get(name));
+                var blocked = TypeConverter.ToBoolean(oi.Get(new JsString(property)));
                 if (blocked)
                 {
                     return true;
@@ -79,16 +78,16 @@ namespace Jint.Runtime.Environments
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.1.2.2
         /// </summary>
-        public override void CreateMutableBinding(in Key name, JsValue value, bool configurable = true)
+        public override void CreateMutableBinding(string name, JsValue value, bool configurable = false)
         {
             var propertyDescriptor = configurable
-                ? new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable)
-                : new PropertyDescriptor(value, PropertyFlag.NonConfigurable);
+                ? new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable | PropertyFlag.MutableBinding)
+                : new PropertyDescriptor(value, PropertyFlag.NonConfigurable | PropertyFlag.MutableBinding);
 
             _bindingObject.DefinePropertyOrThrow(name, propertyDescriptor);
         }
 
-        public override void SetMutableBinding(in Key name, JsValue value, bool strict)
+        public override void SetMutableBinding(string name, JsValue value, bool strict)
         {
             if (!_bindingObject.Set(name, value) && strict)
             {
@@ -96,18 +95,18 @@ namespace Jint.Runtime.Environments
             }
         }
 
-        public override JsValue GetBindingValue(in Key name, bool strict)
+        public override JsValue GetBindingValue(string name, bool strict)
         {
             var desc = _bindingObject.GetProperty(name);
             if (strict && desc == PropertyDescriptor.Undefined)
             {
-                ExceptionHelper.ThrowReferenceError(_engine, name);
+                ExceptionHelper.ThrowReferenceError(_engine, name.ToString());
             }
 
             return ObjectInstance.UnwrapJsValue(desc, this);
         }
 
-        public override bool DeleteBinding(in Key name)
+        public override bool DeleteBinding(string name)
         {
             return _bindingObject.Delete(name);
         }
@@ -122,14 +121,14 @@ namespace Jint.Runtime.Environments
             return Undefined;
         }
 
-        public override Key[] GetAllBindingNames()
+        internal override string[] GetAllBindingNames()
         {
             if (!ReferenceEquals(_bindingObject, null))
             {
-                return _bindingObject.GetOwnProperties().Select( x=> x.Key).ToArray();
+                return _bindingObject.GetOwnProperties().Select( x=> x.Key.ToString()).ToArray();
             }
 
-            return ArrayExt.Empty<Key>();
+            return ArrayExt.Empty<string>();
         }
 
         public override bool Equals(JsValue other)

+ 1 - 1
Jint/Runtime/ExceptionHelper.cs

@@ -37,7 +37,7 @@ namespace Jint.Runtime
 
         public static void ThrowReferenceError(Engine engine, Reference reference)
         {
-            ThrowReferenceError(engine, reference?.GetReferencedName().Name);
+            ThrowReferenceError(engine, (reference?.GetReferencedName()).ToString());
         }
 
         public static void ThrowReferenceError(Engine engine, string name)

+ 1 - 1
Jint/Runtime/Interop/MethodInfoFunctionInstance.cs

@@ -53,7 +53,7 @@ namespace Jint.Runtime.Interop
                         // Handle specific case of F(params JsValue[])
 
                         var arrayInstance = arguments[i].AsArray();
-                        var len = TypeConverter.ToInt32(arrayInstance.Get("length", this));
+                        var len = TypeConverter.ToInt32(arrayInstance.Get(CommonProperties.Length, this));
                         var result = new JsValue[len];
                         for (uint k = 0; k < len; k++)
                         {

+ 6 - 6
Jint/Runtime/Interop/NamespaceReference.cs

@@ -23,12 +23,12 @@ namespace Jint.Runtime.Interop
             _path = path;
         }
 
-        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc)
+        public override bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
         {
             return false;
         }
 
-        public override bool Delete(in Key propertyName)
+        public override bool Delete(JsValue property)
         {
             return false;
         }
@@ -42,7 +42,7 @@ namespace Jint.Runtime.Interop
                 var genericTypeReference = arguments[i];
                 if (genericTypeReference.IsUndefined()
                     || !genericTypeReference.IsObject() 
-                    || genericTypeReference.AsObject().Class != "TypeReference")
+                    || genericTypeReference.AsObject().Class != ObjectClass.TypeReference)
                 {
                     ExceptionHelper.ThrowTypeError(_engine, "Invalid generic type parameter on " + _path + ", if this is not a generic type / method, are you missing a lookup assembly?");
                 }
@@ -69,9 +69,9 @@ namespace Jint.Runtime.Interop
             }
         }
 
-        public override JsValue Get(in Key propertyName, JsValue receiver)
+        public override JsValue Get(JsValue property, JsValue receiver)
         {
-            var newPath = _path + "." + propertyName;
+            var newPath = _path + "." + property;
 
             return GetPath(newPath);
         }
@@ -185,7 +185,7 @@ namespace Jint.Runtime.Interop
             }
         }
 
-        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
+        public override PropertyDescriptor GetOwnProperty(JsValue property)
         {
             return PropertyDescriptor.Undefined;
         }

+ 9 - 9
Jint/Runtime/Interop/ObjectWrapper.cs

@@ -24,7 +24,7 @@ namespace Jint.Runtime.Interop
                 // create a forwarder to produce length from Count
                 var functionInstance = new ClrFunctionInstance(engine, "length", (thisObj, arguments) => collection.Count);
                 var descriptor = new GetSetPropertyDescriptor(functionInstance, Undefined, PropertyFlag.Configurable);
-                AddProperty("length", descriptor);
+                AddProperty(CommonProperties.Length, descriptor);
             }
         }
 
@@ -32,14 +32,14 @@ namespace Jint.Runtime.Interop
 
         internal override bool IsArrayLike { get; }
 
-        public override bool Set(in Key propertyName, JsValue value, JsValue receiver)
+        public override bool Set(JsValue property, JsValue value, JsValue receiver)
         {
-            if (!CanPut(propertyName))
+            if (!CanPut(property))
             {
                 return false;
             }
 
-            var ownDesc = GetOwnProperty(propertyName);
+            var ownDesc = GetOwnProperty(property);
 
             if (ownDesc == null)
             {
@@ -50,24 +50,24 @@ namespace Jint.Runtime.Interop
             return true;
         }
 
-        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
+        public override PropertyDescriptor GetOwnProperty(JsValue property)
         {
-            if (TryGetProperty(propertyName, out var x))
+            if (TryGetProperty(property, out var x))
             {
                 return x;
             }
 
             var type = Target.GetType();
-            var key = new Engine.ClrPropertyDescriptorFactoriesKey(type, propertyName);
+            var key = new Engine.ClrPropertyDescriptorFactoriesKey(type, property.ToString());
 
             if (!_engine.ClrPropertyDescriptorFactories.TryGetValue(key, out var factory))
             {
-                factory = ResolveProperty(type, propertyName);
+                factory = ResolveProperty(type, property.ToString());
                 _engine.ClrPropertyDescriptorFactories[key] = factory;
             }
 
             var descriptor = factory(_engine, Target);
-            AddProperty(propertyName, descriptor);
+            AddProperty(property, descriptor);
             return descriptor;
         }
         

+ 14 - 12
Jint/Runtime/Interop/TypeReference.cs

@@ -12,8 +12,10 @@ namespace Jint.Runtime.Interop
 {
     public sealed class TypeReference : FunctionInstance, IConstructor, IObjectWrapper
     {
+        private static readonly JsString _name = new JsString("typereference");
+
         private TypeReference(Engine engine)
-            : base(engine, "typereference", null, null, false, "TypeReference")
+            : base(engine, _name, strict: false, ObjectClass.TypeReference)
         {
         }
 
@@ -107,24 +109,24 @@ namespace Jint.Runtime.Interop
             return base.HasInstance(v);
         }
 
-        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc)
+        public override bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
         {
             return false;
         }
 
-        public override bool Delete(in Key propertyName)
+        public override bool Delete(JsValue property)
         {
             return false;
         }
 
-        public override bool Set(in Key propertyName, JsValue value, JsValue receiver)
+        public override bool Set(JsValue property, JsValue value, JsValue receiver)
         {
-            if (!CanPut(propertyName))
+            if (!CanPut(property))
             {
                 return false;
             }
 
-            var ownDesc = GetOwnProperty(propertyName);
+            var ownDesc = GetOwnProperty(property);
 
             if (ownDesc == null)
             {
@@ -135,10 +137,10 @@ namespace Jint.Runtime.Interop
             return true;
         }
 
-        public override PropertyDescriptor GetOwnProperty(in Key key)
+        public override PropertyDescriptor GetOwnProperty(JsValue property)
         {
             // todo: cache members locally
-
+            var name = property.ToString();
             if (ReferenceType.IsEnum)
             {
                 Array enumValues = Enum.GetValues(ReferenceType);
@@ -146,7 +148,7 @@ namespace Jint.Runtime.Interop
 
                 for (int i = 0; i < enumValues.Length; i++)
                 {
-                    if (enumNames.GetValue(i) as string == key.Name)
+                    if (enumNames.GetValue(i) as string == name)
                     {
                         return new PropertyDescriptor((int) enumValues.GetValue(i), PropertyFlag.AllForbidden);
                     }
@@ -154,13 +156,13 @@ namespace Jint.Runtime.Interop
                 return PropertyDescriptor.Undefined;
             }
 
-            var propertyInfo = ReferenceType.GetProperty(key.Name, BindingFlags.Public | BindingFlags.Static);
+            var propertyInfo = ReferenceType.GetProperty(name, BindingFlags.Public | BindingFlags.Static);
             if (propertyInfo != null)
             {
                 return new PropertyInfoDescriptor(Engine, propertyInfo, Type);
             }
 
-            var fieldInfo = ReferenceType.GetField(key.Name, BindingFlags.Public | BindingFlags.Static);
+            var fieldInfo = ReferenceType.GetField(name, BindingFlags.Public | BindingFlags.Static);
             if (fieldInfo != null)
             {
                 return new FieldInfoDescriptor(Engine, fieldInfo, Type);
@@ -169,7 +171,7 @@ namespace Jint.Runtime.Interop
             List<MethodInfo> methodInfo = null;
             foreach (var mi in ReferenceType.GetMethods(BindingFlags.Public | BindingFlags.Static))
             {
-                if (mi.Name == key.Name)
+                if (mi.Name == name)
                 {
                     methodInfo = methodInfo ?? new List<MethodInfo>();
                     methodInfo.Add(mi);

+ 6 - 5
Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs

@@ -47,13 +47,13 @@ namespace Jint.Runtime.Interpreter.Expressions
                 value = JsValue.Undefined;
                 done = false;
 
-                if (item.TryGetValue("done", out var d) && d.AsBoolean())
+                if (item.TryGetValue(CommonProperties.Done, out var d) && d.AsBoolean())
                 {
                     done = true;
                     return false;
                 }
 
-                if (!item.TryGetValue("value", out value))
+                if (!item.TryGetValue(CommonProperties.Value, out value))
                 {
                     return false;
                 }
@@ -166,7 +166,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                         {
                             if (assignmentPattern.Right.IsFunctionWithName())
                             {
-                                ((FunctionInstance) value).SetFunctionName(leftIdentifier.Name);
+                                ((FunctionInstance) value).SetFunctionName(new JsString(leftIdentifier.Name));
                             }
                             AssignToIdentifier(engine, leftIdentifier.Name, value);
                         }
@@ -195,7 +195,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             for (uint i = 0; i < pattern.Properties.Count; i++)
             {
                 var left = pattern.Properties[(int) i];
-                string sourceKey;
+                JsValue sourceKey;
                 var identifier = left.Key as Identifier;
                 if (identifier == null)
                 {
@@ -244,7 +244,8 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
         }
 
-        private static void AssignToIdentifier(Engine engine,
+        private static void AssignToIdentifier(
+            Engine engine,
             string name,
             JsValue rval)
         {

+ 1 - 2
Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs

@@ -164,8 +164,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 {
                     _left = Build(engine, (Expression) expression.Left);
                     _leftIdentifier = _left as JintIdentifierExpression;
-                    _evalOrArguments = _leftIdentifier?.ExpressionName == KnownKeys.Eval
-                                       || _leftIdentifier?.ExpressionName == KnownKeys.Arguments;
+                    _evalOrArguments = _leftIdentifier?.HasEvalOrArguments == true;
                 }
 
                 _right = Build(engine, expression.Right);

+ 38 - 25
Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs

@@ -115,8 +115,8 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         public static bool StrictlyEqual(JsValue x, JsValue y)
         {
-            var typeX = x._type;
-            var typeY = y._type;
+            var typeX = x._type & ~InternalTypes.InternalFlags;
+            var typeY = y._type & ~InternalTypes.InternalFlags;
 
             if (typeX != typeY)
             {
@@ -136,29 +136,42 @@ namespace Jint.Runtime.Interpreter.Expressions
                 }
             }
 
-            switch (typeX)
-            {
-                case InternalTypes.Undefined:
-                case InternalTypes.Null:
-                    return true;
-                case InternalTypes.Integer:
-                    return x.AsInteger() == y.AsInteger();
-                case InternalTypes.Number:
-                    var nx = ((JsNumber) x)._value;
-                    var ny = ((JsNumber) y)._value;
-                    return !double.IsNaN(nx) && !double.IsNaN(ny) && nx == ny;
-                case InternalTypes.String:
-                    return x.AsStringWithoutTypeCheck() == y.AsStringWithoutTypeCheck();
-                case InternalTypes.Boolean:
-                    return ((JsBoolean) x)._value == ((JsBoolean) y)._value;
-                case InternalTypes.Object when x.AsObject() is IObjectWrapper xw:
-                    var yw = y.AsObject() as IObjectWrapper;
-                    if (yw == null)
-                        return false;
-                    return Equals(xw.Target, yw.Target);
-                default:
-                    return x == y;
+            if (typeX == InternalTypes.Undefined || typeX == InternalTypes.Null)
+            {
+                return true;
+            }
+
+            if (typeX == InternalTypes.Integer)
+            {
+                return x.AsInteger() == y.AsInteger();
             }
+
+            if (typeX == InternalTypes.Number)
+            {
+                var nx = ((JsNumber) x)._value;
+                var ny = ((JsNumber) y)._value;
+                return !double.IsNaN(nx) && !double.IsNaN(ny) && nx == ny;
+            }
+
+            if ((typeX & InternalTypes.String) != 0)
+            {
+                return x.AsStringWithoutTypeCheck() == y.AsStringWithoutTypeCheck();
+            }
+
+            if (typeX == InternalTypes.Boolean)
+            {
+                return ((JsBoolean) x)._value == ((JsBoolean) y)._value;
+            }
+
+            if ((typeX & InternalTypes.Object) != 0 && x.AsObject() is IObjectWrapper xw)
+            {
+                var yw = y.AsObject() as IObjectWrapper;
+                if (yw == null)
+                    return false;
+                return Equals(xw.Target, yw.Target);
+            }
+
+            return x == y;
         }
 
         private sealed class StrictlyEqualBinaryExpression : JintBinaryExpression
@@ -402,7 +415,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "in can only be used with an object");
                 }
 
-                return oi.HasProperty(TypeConverter.ToString(left)) ? JsBoolean.True : JsBoolean.False;
+                return oi.HasProperty(TypeConverter.ToJsString(left)) ? JsBoolean.True : JsBoolean.False;
             }
         }
 

+ 7 - 6
Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs

@@ -112,7 +112,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
             if (_maxRecursionDepth >= 0)
             {
-                var stackItem = new CallStackElement(expression, func, r?.GetReferencedName() ?? "anonymous function");
+                var stackItem = new CallStackElement(expression, func, (r?.GetReferencedName()).ToString() ?? "anonymous function");
 
                 var recursionDepth = _engine.CallStack.Push(stackItem);
 
@@ -128,7 +128,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 ExceptionHelper.ThrowTypeError(_engine, r == null ? "" : $"Object has no method '{r.GetReferencedName()}'");
             }
 
-            if (func._type != InternalTypes.Object)
+            if (!func.IsObject())
             {
                 if (_engine._referenceResolver == null || !_engine._referenceResolver.TryGetCallable(_engine, callee, out func))
                 {
@@ -146,18 +146,19 @@ namespace Jint.Runtime.Interpreter.Expressions
             var thisObject = Undefined.Instance;
             if (r != null)
             {
-                if (r._baseValue._type < InternalTypes.ObjectEnvironmentRecord)
+                var baseValue = r.GetBase();
+                if ((baseValue._type & InternalTypes.ObjectEnvironmentRecord) == 0)
                 {
-                    thisObject = r._baseValue;
+                    thisObject = baseValue;
                 }
                 else
                 {
-                    var env = (EnvironmentRecord) r._baseValue;
+                    var env = (EnvironmentRecord) baseValue;
                     thisObject = env.ImplicitThisValue();
                 }
 
                 // is it a direct call to eval ? http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.1.1
-                if (r.GetReferencedName() == KnownKeys.Eval && callable is EvalFunctionInstance instance)
+                if (r.GetReferencedName() == CommonProperties.Eval && callable is EvalFunctionInstance instance)
                 {
                     var value = instance.Call(thisObject, arguments, true);
                     _engine._referencePool.Return(r);

+ 12 - 13
Jint/Runtime/Interpreter/Expressions/JintExpression.cs

@@ -215,12 +215,9 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         protected static bool Equal(JsValue x, JsValue y)
         {
-            if (x._type == y._type || x.IsNumber() && y.IsNumber())
-            {
-                return JintBinaryExpression.StrictlyEqual(x, y);
-            }
-
-            return EqualUnlikely(x, y);
+            return x.Type == y.Type
+                ? JintBinaryExpression.StrictlyEqual(x, y)
+                : EqualUnlikely(x, y);
         }
 
         private static bool EqualUnlikely(JsValue x, JsValue y)
@@ -235,32 +232,34 @@ namespace Jint.Runtime.Interpreter.Expressions
                 return true;
             }
 
-            if (x.IsNumber() && y._type == InternalTypes.String)
+            if (x.IsNumber() && y.IsString())
             {
                 return Equal(x, TypeConverter.ToNumber(y));
             }
 
-            if (x._type == InternalTypes.String && y.IsNumber())
+            if (x.IsString() && y.IsNumber())
             {
                 return Equal(TypeConverter.ToNumber(x), y);
             }
 
-            if (x._type == InternalTypes.Boolean)
+            if (x.IsBoolean())
             {
                 return Equal(TypeConverter.ToNumber(x), y);
             }
 
-            if (y._type == InternalTypes.Boolean)
+            if (y.IsBoolean())
             {
                 return Equal(x, TypeConverter.ToNumber(y));
             }
 
-            if (y._type == InternalTypes.Object && (x._type == InternalTypes.String || x.IsNumber()))
+            const InternalTypes stringOrNumber = InternalTypes.String | InternalTypes.Integer | InternalTypes.Number;
+
+            if (y.IsObject() && (x._type & stringOrNumber) != 0)
             {
                 return Equal(x, TypeConverter.ToPrimitive(y));
             }
 
-            if (x._type == InternalTypes.Object && (y._type == InternalTypes.String || y.IsNumber()))
+            if (x.IsObject() && ((y._type & stringOrNumber) != 0))
             {
                 return Equal(TypeConverter.ToPrimitive(x), y);
             }
@@ -415,7 +414,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         protected bool TryGetIdentifierEnvironmentWithBindingValue(
-            in Key expressionName,
+            string expressionName,
             out EnvironmentRecord record,
             out JsValue value)
         {

+ 13 - 4
Jint/Runtime/Interpreter/Expressions/JintIdentifierExpression.cs

@@ -1,12 +1,12 @@
 using Jint.Native;
 using Jint.Runtime.Environments;
-using Jint.Runtime.References;
 
 namespace Jint.Runtime.Interpreter.Expressions
 {
     internal sealed class JintIdentifierExpression : JintExpression
     {
         private readonly Key _expressionName;
+        private JsString _expressionNameJsValue; 
         private readonly JsValue _calculatedValue;
 
         public JintIdentifierExpression(Engine engine, Esprima.Ast.Identifier expression) : base(engine, expression)
@@ -18,13 +18,21 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
         }
 
-        public ref readonly Key ExpressionName => ref _expressionName;
+        public string ExpressionName => _expressionName.Name;
+
+        public bool HasEvalOrArguments
+            => ExpressionName == CommonProperties.Eval || ExpressionName == CommonProperties.Arguments;
 
         protected override object EvaluateInternal()
         {
             var env = _engine.ExecutionContext.LexicalEnvironment;
             var strict = StrictModeScope.IsStrictModeCode;
-            return LexicalEnvironment.GetIdentifierReference(env, _expressionName, strict);
+            var identifierEnvironment = LexicalEnvironment.TryGetIdentifierEnvironmentWithBindingValue(env, _expressionName, strict, out var temp, out _)
+                ? temp
+                : JsValue.Undefined;
+
+            var property = _expressionNameJsValue ??= new JsString(_expressionName.Name);
+            return _engine._referencePool.Rent(identifierEnvironment, property, strict);
         }
 
         public override JsValue GetValue()
@@ -38,9 +46,10 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
 
             var strict = StrictModeScope.IsStrictModeCode;
+            var property = _expressionNameJsValue ??= new JsString(_expressionName);
             return TryGetIdentifierEnvironmentWithBindingValue(strict, _expressionName, out _, out var value)
                 ? value
-                : _engine.GetValue(new Reference(JsValue.Undefined, _expressionName, strict), true);
+                : _engine.GetValue(_engine._referencePool.Rent(JsValue.Undefined, property, strict), true);
         }
     }
 }

+ 23 - 15
Jint/Runtime/Interpreter/Expressions/JintMemberExpression.cs

@@ -9,27 +9,37 @@ namespace Jint.Runtime.Interpreter.Expressions
     /// </summary>
     internal sealed class JintMemberExpression : JintExpression
     {
-        private readonly JintExpression _objectExpression;
-        private readonly JintIdentifierExpression _objectIdentifierExpression;
-        private readonly JintThisExpression _objectThisExpression;
+        private JintExpression _objectExpression;
+        private JintIdentifierExpression _objectIdentifierExpression;
+        private JintThisExpression _objectThisExpression;
 
-        private readonly JintExpression _propertyExpression;
-        private readonly Key _determinedPropertyName;
+        private JintExpression _propertyExpression;
+        private JsValue _determinedProperty;
 
         public JintMemberExpression(Engine engine, MemberExpression expression) : base(engine, expression)
         {
-            _objectExpression = Build(engine, expression.Object);
+            _initialized = false;
+        }
+
+        protected override void Initialize()
+        {
+            var expression = (MemberExpression) _expression;
+            _objectExpression = Build(_engine, expression.Object);
             _objectIdentifierExpression = _objectExpression as JintIdentifierExpression;
             _objectThisExpression = _objectExpression as JintThisExpression;
 
             if (!expression.Computed)
             {
-                _determinedPropertyName = ((Identifier) expression.Property).Name;
+                _determinedProperty = ((Identifier) expression.Property).Name;
             }
-            else
+            else if (expression.Property.Type == Nodes.Literal)
+            {
+                _determinedProperty = JintLiteralExpression.ConvertToJsValue((Literal) expression.Property);
+            }
+
+            if (_determinedProperty is null)
             {
-                _determinedPropertyName = "";
-                _propertyExpression = Build(engine, expression.Property);
+                _propertyExpression = Build(_engine, expression.Property);
             }
         }
 
@@ -60,7 +70,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var baseReference = _objectExpression.Evaluate();
                 if (baseReference is Reference reference)
                 {
-                    baseReferenceName = reference.GetReferencedName();
+                    baseReferenceName = reference.GetReferencedName().ToString();
                     baseValue = _engine.GetValue(reference, false);
                     _engine._referencePool.Return(reference);
                 }
@@ -70,13 +80,11 @@ namespace Jint.Runtime.Interpreter.Expressions
                 }
             }
 
-            var propertyName = _determinedPropertyName.Name.Length > 0
-                ? _determinedPropertyName
-                : TypeConverter.ToPropertyKey(_propertyExpression.GetValue());
+            var property = _determinedProperty ?? _propertyExpression.GetValue();
 
             TypeConverter.CheckObjectCoercible(_engine, baseValue, (MemberExpression) _expression, baseReferenceName);
 
-            return _engine._referencePool.Rent(baseValue, propertyName, isStrictModeCode);
+            return _engine._referencePool.Rent(baseValue, property, isStrictModeCode);
         }
     }
 }

+ 12 - 15
Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs

@@ -1,6 +1,7 @@
 using System;
 using Esprima.Ast;
 using Jint.Collections;
+using Jint.Native;
 using Jint.Native.Function;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors.Specialized;
@@ -21,16 +22,17 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         private class ObjectProperty
         {
-            private readonly Key _key;
+            internal readonly string _key;
+            private JsString _keyJsString;
             internal readonly Property _value;
 
-            public ObjectProperty(in Key key, Property property)
+            public ObjectProperty(string key, Property property)
             {
                 _key = key;
                 _value = property;
             }
 
-            public ref readonly Key Key => ref _key;
+            public JsString KeyJsString => _keyJsString ??= _key != null ? JsString.Create(_key) : null;
         }
 
         public JintObjectExpression(Engine engine, ObjectExpression expression) : base(engine, expression)
@@ -66,7 +68,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     propName = identifier.Name;
                 }
 
-                _properties[i] = new ObjectProperty(propName ?? "", property);
+                _properties[i] = new ObjectProperty(propName, property);
 
                 if (property.Kind == PropertyKind.Init || property.Kind == PropertyKind.Data)
                 {
@@ -101,33 +103,28 @@ namespace Jint.Runtime.Interpreter.Expressions
                 return obj;
             }
 
-            var properties = new StringDictionarySlim<PropertyDescriptor>(_properties.Length);
-            var hasSymbols = false;
+            var properties = new PropertyDictionary(_properties.Length, checkExistingKeys: true);
             for (var i = 0; i < _properties.Length; i++)
             {
                 var objectProperty = _properties[i];
                 var valueExpression = _valueExpressions[i];
                 var propValue = valueExpression.GetValue().Clone();
-                hasSymbols |= objectProperty.Key.IsSymbol;
-                properties[objectProperty.Key] = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
+                properties[objectProperty._key] = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
             }
-
-            obj.SetProperties(properties, hasSymbols);
+            obj.SetProperties(properties);
             return obj;
         }
-                                                
+
         private object BuildObjectNormal()
         {
-            var obj = _engine.Object.Construct(Math.Max(2, _properties.Length));
+            var obj = _engine.Object.Construct(_properties.Length);
             bool isStrictModeCode = StrictModeScope.IsStrictModeCode;
 
             for (var i = 0; i < _properties.Length; i++)
             {
                 var objectProperty = _properties[i];
                 var property = objectProperty._value;
-                var propName = objectProperty.Key.Name.Length > 0
-                    ? objectProperty.Key
-                    : property.GetKey(_engine);
+                var propName = objectProperty.KeyJsString ?? property.GetKey(_engine);
 
                 PropertyDescriptor propDesc;
 

+ 7 - 5
Jint/Runtime/Interpreter/Expressions/JintTaggedTemplateExpression.cs

@@ -7,6 +7,8 @@ namespace Jint.Runtime.Interpreter.Expressions
 {
     internal sealed class JintTaggedTemplateExpression : JintExpression
     {
+        internal static  readonly JsString PropertyRaw = new JsString("raw");
+        
         private readonly TaggedTemplateExpression _taggedTemplateExpression;
         private JintExpression _tagIdentifier;
         private JintTemplateLiteralExpression _quasi;
@@ -55,14 +57,14 @@ namespace Jint.Runtime.Interpreter.Expressions
             var count = (uint) _quasi._templateLiteralExpression.Quasis.Count;
             var template = _engine.Array.ConstructFast(count);
             var rawObj = _engine.Array.ConstructFast(count);
-            for (int i = 0; i < _quasi._templateLiteralExpression.Quasis.Count; ++i)
+            for (uint i = 0; i < _quasi._templateLiteralExpression.Quasis.Count; ++i)
             {
-                var templateElementValue = _quasi._templateLiteralExpression.Quasis[i].Value;
-                template.SetIndexValue((uint) i, templateElementValue.Cooked, updateLength: false);
-                rawObj.SetIndexValue((uint) i, templateElementValue.Raw, updateLength: false);
+                var templateElementValue = _quasi._templateLiteralExpression.Quasis[(int) i].Value;
+                template.SetIndexValue(i, templateElementValue.Cooked, updateLength: false);
+                rawObj.SetIndexValue(i, templateElementValue.Raw, updateLength: false);
             }
 
-            template.DefineOwnProperty("raw", new PropertyDescriptor(rawObj, PropertyFlag.AllForbidden));
+            template.DefineOwnProperty(PropertyRaw, new PropertyDescriptor(rawObj, PropertyFlag.AllForbidden));
             return template;
         }
     }

+ 5 - 5
Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs

@@ -69,7 +69,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                     if (r.IsUnresolvableReference())
                     {
-                        if (r._strict)
+                        if (r.IsStrictReference())
                         {
                             ExceptionHelper.ThrowSyntaxError(_engine);
                         }
@@ -82,7 +82,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     {
                         var o = TypeConverter.ToObject(_engine, r.GetBase());
                         var deleteStatus  = o.Delete(r.GetReferencedName());
-                        if (!deleteStatus && r._strict)
+                        if (!deleteStatus && r.IsStrictReference())
                         {
                             ExceptionHelper.ThrowTypeError(_engine);
                         }
@@ -91,16 +91,16 @@ namespace Jint.Runtime.Interpreter.Expressions
                         return deleteStatus ? JsBoolean.True : JsBoolean.False;
                     }
 
-                    if (r._strict)
+                    if (r.IsStrictReference())
                     {
                         ExceptionHelper.ThrowSyntaxError(_engine);
                     }
 
                     var bindings = r.GetBase().TryCast<EnvironmentRecord>();
-                    var referencedName = r.GetReferencedName();
+                    var property = r.GetReferencedName();
                     _engine._referencePool.Return(r);
 
-                    return bindings.DeleteBinding(referencedName) ? JsBoolean.True : JsBoolean.False;
+                    return bindings.DeleteBinding(property.ToString()) ? JsBoolean.True : JsBoolean.False;
 
                 case UnaryOperator.Void:
                     _argument.GetValue();

+ 2 - 3
Jint/Runtime/Interpreter/Expressions/JintUpdateExpression.cs

@@ -31,8 +31,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
 
             _leftIdentifier = _argument as JintIdentifierExpression;
-            _evalOrArguments = _leftIdentifier?.ExpressionName == KnownKeys.Eval
-                               || _leftIdentifier?.ExpressionName == KnownKeys.Arguments;
+            _evalOrArguments = _leftIdentifier?.HasEvalOrArguments == true;
         }
 
         protected override object EvaluateInternal()
@@ -70,7 +69,7 @@ namespace Jint.Runtime.Interpreter.Expressions
         private JsValue UpdateIdentifier()
         {
             var strict = StrictModeScope.IsStrictModeCode;
-            ref readonly var name = ref _leftIdentifier.ExpressionName;
+            var name = _leftIdentifier.ExpressionName;
             if (TryGetIdentifierEnvironmentWithBindingValue(
                 name,
                 out var environmentRecord,

+ 2 - 2
Jint/Runtime/Interpreter/Statements/JintForInStatement.cs

@@ -49,7 +49,7 @@ namespace Jint.Runtime.Interpreter.Statements
 
             // keys are constructed using the prototype chain
             var cursor = obj;
-            var processedKeys = new HashSet<string>();
+            var processedKeys = new HashSet<JsString>();
 
             while (!ReferenceEquals(cursor, null))
             {
@@ -58,7 +58,7 @@ namespace Jint.Runtime.Interpreter.Statements
                 var length = keys.GetLength();
                 for (uint i = 0; i < length; i++)
                 {
-                    var p = keys.GetOwnProperty(i).Value.AsStringWithoutTypeCheck();
+                    var p = (JsString) keys.GetOwnProperty(i).Value;
 
                     if (processedKeys.Contains(p))
                     {

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません