2
0
Эх сурвалжийг харах

Use Key to refer to properties and cache hash code (#634)

Marko Lahma 6 жил өмнө
parent
commit
00d91f6ce1
54 өөрчлөгдсөн 748 нэмэгдсэн , 775 устгасан
  1. 1 1
      Jint.Benchmark/Jint.Benchmark.csproj
  2. 101 250
      Jint/Collections/StringDictionarySlim.cs
  3. 33 31
      Jint/Engine.cs
  4. 1 1
      Jint/EsprimaExtensions.cs
  5. 1 1
      Jint/JsValueExtensions.cs
  6. 68 0
      Jint/Key.cs
  7. 9 9
      Jint/Native/Argument/ArgumentsInstance.cs
  8. 80 76
      Jint/Native/Array/ArrayInstance.cs
  9. 41 40
      Jint/Native/Array/ArrayPrototype.cs
  10. 6 4
      Jint/Native/Function/ArrowFunctionInstance.cs
  11. 24 31
      Jint/Native/Function/FunctionInstance.cs
  12. 5 13
      Jint/Native/Function/FunctionPrototype.cs
  13. 12 14
      Jint/Native/Function/ScriptFunctionInstance.cs
  14. 9 4
      Jint/Native/Global/GlobalObject.cs
  15. 15 11
      Jint/Native/JsNumber.cs
  16. 1 1
      Jint/Native/JsValue.cs
  17. 5 5
      Jint/Native/Map/MapInstance.cs
  18. 15 21
      Jint/Native/Object/ObjectInstance.cs
  19. 1 1
      Jint/Native/RegExp/RegExpPrototype.cs
  20. 6 13
      Jint/Native/Set/SetInstance.cs
  21. 1 3
      Jint/Native/String/StringConstructor.cs
  22. 10 13
      Jint/Native/String/StringInstance.cs
  23. 1 1
      Jint/Pooling/ReferencePool.cs
  24. 1 1
      Jint/Runtime/Arguments.cs
  25. 2 2
      Jint/Runtime/Debugger/DebugHandler.cs
  26. 15 11
      Jint/Runtime/Descriptors/PropertyDescriptor.cs
  27. 3 3
      Jint/Runtime/Descriptors/Specialized/ClrAccessDescriptor.cs
  28. 52 42
      Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs
  29. 14 12
      Jint/Runtime/Environments/EnvironmentRecord.cs
  30. 7 30
      Jint/Runtime/Environments/LexicalEnvironment.cs
  31. 17 15
      Jint/Runtime/Environments/ObjectEnvironmentRecord.cs
  32. 1 1
      Jint/Runtime/ExceptionHelper.cs
  33. 4 4
      Jint/Runtime/Interop/NamespaceReference.cs
  34. 2 2
      Jint/Runtime/Interop/ObjectWrapper.cs
  35. 8 8
      Jint/Runtime/Interop/TypeReference.cs
  36. 6 6
      Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs
  37. 5 4
      Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs
  38. 4 3
      Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs
  39. 4 3
      Jint/Runtime/Interpreter/Expressions/JintExpression.cs
  40. 5 4
      Jint/Runtime/Interpreter/Expressions/JintIdentifierExpression.cs
  41. 10 8
      Jint/Runtime/Interpreter/Expressions/JintMemberExpression.cs
  42. 19 15
      Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs
  43. 1 1
      Jint/Runtime/Interpreter/Expressions/JintSpreadExpression.cs
  44. 1 1
      Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs
  45. 3 2
      Jint/Runtime/Interpreter/Expressions/JintUpdateExpression.cs
  46. 4 4
      Jint/Runtime/Interpreter/JintFunctionDefinition.cs
  47. 10 18
      Jint/Runtime/Interpreter/JintStatementList.cs
  48. 2 2
      Jint/Runtime/Interpreter/Statements/JintForInStatement.cs
  49. 1 1
      Jint/Runtime/Interpreter/Statements/JintTryStatement.cs
  50. 3 4
      Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs
  51. 27 0
      Jint/Runtime/KnownKeys.cs
  52. 43 10
      Jint/Runtime/RefStack.cs
  53. 6 6
      Jint/Runtime/References/Reference.cs
  54. 22 8
      Jint/Runtime/TypeConverter.cs

+ 1 - 1
Jint.Benchmark/Jint.Benchmark.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netcoreapp2.0</TargetFramework>
+    <TargetFramework>netcoreapp2.2</TargetFramework>
     <AssemblyName>Jint.Benchmark</AssemblyName>
     <OutputType>Exe</OutputType>
     <PackageId>Jint.Benchmark</PackageId>

+ 101 - 250
Jint/Collections/StringDictionarySlim.cs

@@ -6,20 +6,20 @@ using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Linq;
 using System.Runtime.CompilerServices;
-using Jint.Runtime;
 
 namespace Jint.Collections
 {
     /// <summary>
-    /// DictionarySlim<TKey, TValue> is similar to Dictionary<TKey, TValue> but optimized in three ways:
+    /// 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>
     [DebuggerTypeProxy(typeof(DictionarySlimDebugView<>))]
     [DebuggerDisplay("Count = {Count}")]
-    internal sealed class StringDictionarySlim<TValue>
+    internal sealed class StringDictionarySlim<TValue> : IReadOnlyCollection<KeyValuePair<string, TValue>>
     {
         // 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.
@@ -27,6 +27,8 @@ namespace Jint.Collections
         // 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;
@@ -34,21 +36,24 @@ namespace Jint.Collections
         [DebuggerDisplay("({key}, {value})->{next}")]
         private struct Entry
         {
-            public string key;
+            public Key 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 StringDictionarySlim()
         {
-            _buckets = HashHelpers.DictionarySlimSizeOneIntArray;
+            _buckets = HashHelpers.SizeOneIntArray;
             _entries = InitialEntries;
         }
 
         public StringDictionarySlim(int capacity)
         {
-            if (capacity < 2) ExceptionHelper.ThrowArgumentOutOfRangeException();
+            if (capacity < 2)
+                capacity = 2; // 1 would indicate the dummy array
             capacity = HashHelpers.PowerOf2(capacity);
             _buckets = new int[capacity];
             _entries = new Entry[capacity];
@@ -56,13 +61,22 @@ namespace Jint.Collections
 
         public int Count => _count;
 
-        public int Capacity => _entries.Length;
+        /// <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(string key)
+        public bool ContainsKey(in Key key)
         {
             Entry[] entries = _entries;
-            for (int i = _buckets[key.GetHashCode() & (_buckets.Length - 1)] - 1;
-                    (uint)i < (uint)entries.Length; i = entries[i].next)
+            for (int i = _buckets[key.HashCode & (_buckets.Length-1)] - 1;
+                (uint)i < (uint)entries.Length; i = entries[i].next)
             {
                 if (key == entries[i].key)
                     return true;
@@ -71,17 +85,11 @@ namespace Jint.Collections
             return false;
         }
 
-        public TValue GetValueOrDefault(string key)
-        {
-            bool result = TryGetValue(key, out TValue value);
-            return value;
-        }
-
-        public bool TryGetValue(string key, out TValue value)
+        public bool TryGetValue(in Key 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)
+            for (int i = _buckets[key.HashCode & (_buckets.Length - 1)] - 1;
+                (uint)i < (uint)entries.Length; i = entries[i].next)
             {
                 if (key == entries[i].key)
                 {
@@ -94,10 +102,10 @@ namespace Jint.Collections
             return false;
         }
 
-        public bool Remove(string key)
+        public bool Remove(in Key key)
         {
             Entry[] entries = _entries;
-            int bucketIndex = key.GetHashCode() & (_buckets.Length - 1);
+            int bucketIndex = key.HashCode & (_buckets.Length - 1);
             int entryIndex = _buckets[bucketIndex] - 1;
 
             int lastIndex = -1;
@@ -106,39 +114,21 @@ namespace Jint.Collections
                 Entry candidate = entries[entryIndex];
                 if (candidate.key == key)
                 {
-                    if (lastIndex == -1)
-                    {
-                        // Fixup bucket to new head (if any)
-                        _buckets[bucketIndex] = candidate.next + 1;
+                    if (lastIndex != -1)
+                    {   // Fixup preceding element in chain to point to next (if any)
+                        entries[lastIndex].next = candidate.next;
                     }
                     else
-                    {
-                        // Fixup preceding element in chain to point to next (if any)
-                        entries[lastIndex].next = candidate.next;
+                    {   // Fixup bucket to new head (if any)
+                        _buckets[bucketIndex] = candidate.next + 1;
                     }
 
-                    // move last item to this index and fix link to it
-                    if (entryIndex != --_count)
-                    {
-                        entries[entryIndex] = entries[_count];
-
-                        bucketIndex = entries[entryIndex].key.GetHashCode() & (_buckets.Length - 1);
-                        lastIndex = _buckets[bucketIndex] - 1;
-
-                        if (lastIndex == _count)
-                        {
-                            // Fixup bucket to this index
-                            _buckets[bucketIndex] = entryIndex + 1;
-                        }
-                        else
-                        {
-                            // Find preceding element in chain and point to this index
-                            while (entries[lastIndex].next != _count)
-                                lastIndex = entries[lastIndex].next;
-                            entries[lastIndex].next = entryIndex;
-                        }
-                    }
-                    entries[_count] = default;
+                    entries[entryIndex] = default;
+
+                    entries[entryIndex].next = -3 - _freeList; // New head of free list
+                    _freeList = entryIndex;
+
+                    _count--;
                     return true;
                 }
                 lastIndex = entryIndex;
@@ -148,63 +138,78 @@ namespace Jint.Collections
             return false;
         }
 
-        public void Clear()
+       // 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(in Key key)
         {
-            int count = _count;
-            if (count > 0)
+            Entry[] entries = _entries;
+            int bucketIndex = key.HashCode & (_buckets.Length - 1);
+            for (int i = _buckets[bucketIndex] - 1;
+                    (uint)i < (uint)entries.Length; i = entries[i].next)
             {
-                Array.Clear(_buckets, 0, _buckets.Length);
-                _count = 0;
-                Array.Clear(_entries, 0, count);
+                if (key == entries[i].key)
+                    return ref entries[i].value;
             }
+
+            return ref AddKey(key, bucketIndex);
         }
 
-        public ref TValue this[string key]
+        public ref TValue this[in Key key]
         {
-            get
-            {
-                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 == entries[i].key)
-                        return ref entries[i].value;
-                }
-
-                return ref AddKey(key, bucketIndex);
-            }
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => ref GetOrAddValueRef(key);
         }
 
         [MethodImpl(MethodImplOptions.NoInlining)]
-        private ref TValue AddKey(string key, int bucketIndex)
+        private ref TValue AddKey(in Key key, int bucketIndex)
         {
             Entry[] entries = _entries;
-
-            if (_count == entries.Length || entries.Length == 1)
+            int entryIndex;
+            if (_freeList != -1)
             {
-                entries = Resize();
-                bucketIndex = key.GetHashCode() & (_buckets.Length - 1);
-                // entry indexes were not changed by Resize
+                entryIndex = _freeList;
+                _freeList = -3 - entries[_freeList].next;
+            }
+            else
+            {
+                if (_count == entries.Length || entries.Length == 1)
+                {
+                    entries = Resize();
+                    bucketIndex = key.HashCode & (_buckets.Length - 1);
+                    // entry indexes were not changed by Resize
+                }
+                entryIndex = _count;
             }
 
-            int 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;
-            var entries = new Entry[_entries.Length * 2];
+            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);
+                int bucketIndex = entries[count].key.HashCode & (newBuckets.Length - 1);
                 entries[count].next = newBuckets[bucketIndex] - 1;
                 newBuckets[bucketIndex] = count + 1;
             }
@@ -214,23 +219,22 @@ namespace Jint.Collections
 
             return entries;
         }
+        
+        /// <summary>
+        /// Gets an enumerator over the dictionary
+        /// </summary>
+        public Enumerator GetEnumerator() => new Enumerator(this); // avoid boxing
 
-        public KeyCollection Keys => new KeyCollection(this);
+        /// <summary>
+        /// Gets an enumerator over the dictionary
+        /// </summary>
+        IEnumerator<KeyValuePair<string, TValue>> IEnumerable<KeyValuePair<string, TValue>>.GetEnumerator() =>
+            new Enumerator(this);
 
-        public ValueCollection Values => new ValueCollection(this);
-
-        public void CopyTo(KeyValuePair<string, TValue>[] array, int index)
-        {
-            Entry[] entries = _entries;
-            for (int i = 0; i < _count; i++)
-            {
-                array[index++] = new KeyValuePair<string, TValue>(
-                    entries[i].key,
-                    entries[i].value);
-            }
-        }
-
-        public Enumerator GetEnumerator() => new Enumerator(this); // avoid boxing
+        /// <summary>
+        /// Gets an enumerator over the dictionary
+        /// </summary>
+        IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
 
         public struct Enumerator : IEnumerator<KeyValuePair<string, TValue>>
         {
@@ -270,161 +274,10 @@ namespace Jint.Collections
 
             public void Dispose() { }
         }
-
-        public struct KeyCollection : ICollection<string>, IReadOnlyCollection<string>
-        {
-            private readonly StringDictionarySlim<TValue> _dictionary;
-
-            internal KeyCollection(StringDictionarySlim<TValue> dictionary)
-            {
-                _dictionary = dictionary;
-            }
-
-            public int Count => _dictionary._count;
-
-            bool ICollection<string>.IsReadOnly => true;
-
-            void ICollection<string>.Add(string item) =>
-                ExceptionHelper.ThrowNotSupportedException();
-
-            void ICollection<string>.Clear() =>
-                ExceptionHelper.ThrowNotSupportedException();
-
-            public bool Contains(string item) => _dictionary.ContainsKey(item);
-
-            bool ICollection<string>.Remove(string item) =>
-                ExceptionHelper.ThrowNotSupportedException<bool>();
-
-            public void CopyTo(string[] array, int index)
-            {
-                Entry[] entries = _dictionary._entries;
-                for (int i = 0; i < _dictionary._count; i++)
-                {
-                    array[index++] = entries[i].key;
-                }
-            }
-
-            public Enumerator GetEnumerator() => new Enumerator(_dictionary); // avoid boxing
-            IEnumerator<string> IEnumerable<string>.GetEnumerator() => new Enumerator(_dictionary);
-            IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_dictionary);
-
-            public struct Enumerator : IEnumerator<string>
-            {
-                private readonly StringDictionarySlim<TValue> _dictionary;
-                private int _index;
-                private string _current;
-
-                internal Enumerator(StringDictionarySlim<TValue> dictionary)
-                {
-                    _dictionary = dictionary;
-                    _index = 0;
-                    _current = default;
-                }
-
-                public string Current => _current;
-
-                object IEnumerator.Current => _current;
-
-                public void Dispose() { }
-
-                public bool MoveNext()
-                {
-                    if (_index == _dictionary._count)
-                    {
-                        _current = default;
-                        return false;
-                    }
-
-                    _current = _dictionary._entries[_index++].key;
-                    return true;
-                }
-
-                public void Reset()
-                {
-                    _index = 0;
-                }
-            }
-        }
-
-        public struct ValueCollection : ICollection<TValue>, IReadOnlyCollection<TValue>
-        {
-            private readonly StringDictionarySlim<TValue> _dictionary;
-
-            internal ValueCollection(StringDictionarySlim<TValue> dictionary)
-            {
-                _dictionary = dictionary;
-            }
-
-            public int Count => _dictionary._count;
-
-            bool ICollection<TValue>.IsReadOnly => true;
-
-            void ICollection<TValue>.Add(TValue item) =>
-                ExceptionHelper.ThrowNotSupportedException();
-
-            void ICollection<TValue>.Clear() =>
-                ExceptionHelper.ThrowNotSupportedException();
-
-            bool ICollection<TValue>.Contains(TValue item) =>
-                ExceptionHelper.ThrowNotSupportedException<bool>(); // performance antipattern
-
-            bool ICollection<TValue>.Remove(TValue item) =>
-                ExceptionHelper.ThrowNotSupportedException<bool>();
-
-            public void CopyTo(TValue[] array, int index)
-            {
-                Entry[] entries = _dictionary._entries;
-                for (int i = 0; i < _dictionary._count; i++)
-                {
-                    array[index++] = entries[i].value;
-                }
-            }
-
-            public Enumerator GetEnumerator() => new Enumerator(_dictionary); // avoid boxing
-            IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator() => new Enumerator(_dictionary);
-            IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_dictionary);
-
-            public struct Enumerator : IEnumerator<TValue>
-            {
-                private readonly StringDictionarySlim<TValue> _dictionary;
-                private int _index;
-                private TValue _current;
-
-                internal Enumerator(StringDictionarySlim<TValue> dictionary)
-                {
-                    _dictionary = dictionary;
-                    _index = 0;
-                    _current = default;
-                }
-
-                public TValue Current => _current;
-
-                object IEnumerator.Current => _current;
-
-                public void Dispose() { }
-
-                public bool MoveNext()
-                {
-                    if (_index == _dictionary._count)
-                    {
-                        _current = default;
-                        return false;
-                    }
-
-                    _current = _dictionary._entries[_index++].value;
-                    return true;
-                }
-
-                public void Reset()
-                {
-                    _index = 0;
-                }
-            }
-        }
-
+        
         internal static class HashHelpers
         {
-            internal static readonly int[] DictionarySlimSizeOneIntArray = new int[1];
+            internal static readonly int[] SizeOneIntArray = new int[1];
 
             internal static int PowerOf2(int v)
             {
@@ -449,9 +302,7 @@ namespace Jint.Collections
             {
                 get
                 {
-                    var array = new KeyValuePair<string, V>[_dictionary.Count];
-                    _dictionary.CopyTo(array, 0);
-                    return array;
+                    return _dictionary.ToArray();
                 }
             }
         }

+ 33 - 31
Jint/Engine.cs

@@ -109,16 +109,16 @@ namespace Jint
         // shared frozen version
         internal readonly PropertyDescriptor _getSetThrower;
 
-        internal struct ClrPropertyDescriptorFactoriesKey : IEquatable<ClrPropertyDescriptorFactoriesKey>
+        internal readonly struct ClrPropertyDescriptorFactoriesKey : IEquatable<ClrPropertyDescriptorFactoriesKey>
         {
-            public ClrPropertyDescriptorFactoriesKey(Type type, string propertyName)
+            public ClrPropertyDescriptorFactoriesKey(Type type, in Key propertyName)
             {
                 Type = type;
                 PropertyName = propertyName;
             }
 
             private readonly Type Type;
-            private readonly string PropertyName;
+            private readonly Key PropertyName;
 
             public bool Equals(ClrPropertyDescriptorFactoriesKey other)
             {
@@ -164,7 +164,7 @@ namespace Jint
 
         public Engine(Action<Options> options)
         {
-            _executionContexts = new ExecutionContextStack();
+            _executionContexts = new ExecutionContextStack(2);
 
             Global = GlobalObject.CreateGlobalObject(this);
 
@@ -220,7 +220,7 @@ namespace Jint
             _jsValueArrayPool = new JsValueArrayPool();
 
             Eval = new EvalFunctionInstance(this, System.ArrayExt.Empty<string>(), LexicalEnvironment.NewDeclarativeEnvironment(this, ExecutionContext.LexicalEnvironment), StrictModeScope.IsStrictModeCode);
-            Global._properties["eval"] = new PropertyDescriptor(Eval, true, false, true);
+            Global._properties[KnownKeys.Eval] = new PropertyDescriptor(Eval, true, false, true);
 
             if (Options._IsClrAllowed)
             {
@@ -306,39 +306,39 @@ namespace Jint
             _executionContexts.Push(context);
         }
 
-        public Engine SetValue(string name, Delegate value)
+        public Engine SetValue(in Key name, Delegate value)
         {
             Global.FastAddProperty(name, new DelegateWrapper(this, value), true, false, true);
             return this;
         }
 
-        public Engine SetValue(string name, string value)
+        public Engine SetValue(in Key name, string value)
         {
             return SetValue(name, (JsValue) value);
         }
 
-        public Engine SetValue(string name, double value)
+        public Engine SetValue(in Key name, double value)
         {
             return SetValue(name, JsNumber.Create(value));
         }
 
-        public Engine SetValue(string name, int value)
+        public Engine SetValue(in Key name, int value)
         {
             return SetValue(name, JsNumber.Create(value));
         }
 
-        public Engine SetValue(string name, bool value)
+        public Engine SetValue(in Key name, bool value)
         {
             return SetValue(name, value ? JsBoolean.True : JsBoolean.False);
         }
 
-        public Engine SetValue(string name, JsValue value)
+        public Engine SetValue(in Key name, JsValue value)
         {
             Global.Put(name, value, false);
             return this;
         }
 
-        public Engine SetValue(string name, object obj)
+        public Engine SetValue(in Key name, object obj)
         {
             return SetValue(name, JsValue.FromObject(this, obj));
         }
@@ -517,7 +517,7 @@ namespace Jint
                     return baseValue;
                 }
 
-                var referencedName = reference._name;
+                ref readonly var referencedName = ref reference.GetReferencedName();
                 if (returnReferenceToPool)
                 {
                     _referencePool.Return(reference);
@@ -559,7 +559,7 @@ namespace Jint
                 return ExceptionHelper.ThrowArgumentException<JsValue>();
             }
 
-            var bindingValue = record.GetBindingValue(reference._name, reference._strict);
+            var bindingValue = record.GetBindingValue(reference.GetReferencedName(), reference._strict);
 
             if (returnReferenceToPool)
             {
@@ -574,6 +574,7 @@ namespace Jint
         /// </summary>
         public void PutValue(Reference reference, JsValue value)
         {
+            ref readonly var referencedName = ref reference.GetReferencedName();
             if (reference._baseValue._type == Types.Undefined)
             {
                 if (reference._strict)
@@ -581,31 +582,31 @@ namespace Jint
                     ExceptionHelper.ThrowReferenceError(this, reference);
                 }
 
-                Global.Put(reference._name, value, false);
+                Global.Put(referencedName, value, false);
             }
             else if (reference.IsPropertyReference())
             {
                 var baseValue = reference._baseValue;
                 if (reference._baseValue._type == Types.Object || reference._baseValue._type == Types.None)
                 {
-                    ((ObjectInstance) baseValue).Put(reference._name, value, reference._strict);
+                    ((ObjectInstance) baseValue).Put(referencedName, value, reference._strict);
                 }
                 else
                 {
-                    PutPrimitiveBase(baseValue, reference._name, value, reference._strict);
+                    PutPrimitiveBase(baseValue, referencedName, value, reference._strict);
                 }
             }
             else
             {
                 var baseValue = reference._baseValue;
-                ((EnvironmentRecord) baseValue).SetMutableBinding(reference._name, value, reference._strict);
+                ((EnvironmentRecord) baseValue).SetMutableBinding(referencedName, value, reference._strict);
             }
         }
 
         /// <summary>
         /// Used by PutValue when the reference has a primitive base value
         /// </summary>
-        public void PutPrimitiveBase(JsValue b, string name, JsValue value, bool throwOnError)
+        public void PutPrimitiveBase(JsValue b, in Key name, JsValue value, bool throwOnError)
         {
             var o = TypeConverter.ToObject(this, b);
             if (!o.CanPut(name))
@@ -725,7 +726,7 @@ namespace Jint
         /// </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, string propertyName)
+        public JsValue GetValue(JsValue scope, in Key propertyName)
         {
             AssertNotNullOrEmpty(nameof(propertyName), propertyName);
 
@@ -753,7 +754,6 @@ namespace Jint
                 var argsObj = _argumentsInstancePool.Rent(functionInstance, functionInstance._formalParameters, arguments, env, strict);
                 canReleaseArgumentsInstance = true;
 
-
                 var functionDeclaration = (functionInstance as ScriptFunctionInstance)?.FunctionDeclaration ??
                     (functionInstance as ArrowFunctionInstance)?.FunctionDeclaration;
 
@@ -768,7 +768,7 @@ namespace Jint
                     var parameters = functionInstance._formalParameters;
                     for (var i = 0; i < parameters.Length; i++)
                     {
-                        var argName = parameters[i];
+                        Key argName = parameters[i];
                         var v = i + 1 > arguments.Length ? Undefined.Instance : arguments[i];
                         v = DeclarativeEnvironmentRecord.HandleAssignmentPatternIfNeeded(functionDeclaration, v, i);
 
@@ -780,7 +780,7 @@ namespace Jint
 
                         env.SetMutableBinding(argName, v, strict);
                     }
-                    env.CreateMutableBinding("arguments", argsObj);
+                    env.CreateMutableBinding(KnownKeys.Arguments, argsObj);
                 }
             }
 
@@ -808,16 +808,18 @@ namespace Jint
                 for (var i = 0; i < variableDeclarationsCount; i++)
                 {
                     var variableDeclaration = variableDeclarations[i];
-                    var declarationsCount = variableDeclaration.Declarations.Count;
+                    var declarations = variableDeclaration.Declarations;
+                    var declarationsCount = declarations.Count;
                     for (var j = 0; j < declarationsCount; j++)
                     {
-                        var d = variableDeclaration.Declarations[j];
-                        if (d.Id is Identifier id1)
+                        var d = declarations[j];
+                        if (d.Id is Esprima.Ast.Identifier id1)
                         {
-                            var varAlreadyDeclared = env.HasBinding(id1.Name);
+                            Key name = id1.Name;
+                            var varAlreadyDeclared = env.HasBinding(name);
                             if (!varAlreadyDeclared)
                             {
-                                env.CreateMutableBinding(id1.Name, Undefined.Instance);
+                                env.CreateMutableBinding(name, Undefined.Instance);
                             }
                         }
                     }
@@ -837,7 +839,7 @@ namespace Jint
             for (var i = 0; i < functionDeclarationsCount; i++)
             {
                 var f = functionDeclarations[i];
-                var fn = f.Id.Name;
+                Key fn = f.Id.Name;
                 var fo = Function.CreateFunctionObject(f);
                 var funcAlreadyDeclared = env.HasBinding(fn);
                 if (!funcAlreadyDeclared)
@@ -881,11 +883,11 @@ namespace Jint
             _executionContexts.ReplaceTopLexicalEnvironment(newEnv);
         }
 
-        private static void AssertNotNullOrEmpty(string propertyname, string propertyValue)
+        private static void AssertNotNullOrEmpty(string propertyName, string propertyValue)
         {
             if (string.IsNullOrEmpty(propertyValue))
             {
-                ExceptionHelper.ThrowArgumentException(propertyname);
+                ExceptionHelper.ThrowArgumentException(propertyName);
             }
         }
     }

+ 1 - 1
Jint/EsprimaExtensions.cs

@@ -16,7 +16,7 @@ namespace Jint
                 return literal.Value as string ?? Convert.ToString(literal.Value, provider: null);
             }
 
-            if (expression is Identifier identifier)
+            if (expression is Esprima.Ast.Identifier identifier)
             {
                 return identifier.Name;
             }

+ 1 - 1
Jint/JsValueExtensions.cs

@@ -38,7 +38,7 @@ namespace Jint
 
             return AsStringWithoutTypeCheck(value);
         }
-        
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static string AsStringWithoutTypeCheck(this JsValue value)
         {

+ 68 - 0
Jint/Key.cs

@@ -0,0 +1,68 @@
+using System;
+using System.Diagnostics;
+using Jint.Runtime;
+
+namespace Jint
+{
+    /// <summary>
+    /// Represents a key that Jint uses with pre-calculated hash code
+    /// as runtime does a lot of repetitive dictionary lookups.
+    /// </summary>
+    [DebuggerDisplay("{" + nameof(Name) + "}")]
+    public readonly struct Key : IEquatable<Key>
+    {
+        public Key(string name)
+        {
+            if (name == null)
+            {
+                ExceptionHelper.ThrowArgumentException("name cannot be null");
+            }
+            Name = name;
+            HashCode = name.GetHashCode();
+        }
+
+        public readonly string Name;
+        internal readonly int HashCode;
+
+        public static implicit operator Key(string name)
+        {
+            return new Key(name);
+        }
+
+        public static implicit operator string(Key key) => key.Name;
+
+        public static bool operator ==(in Key a, Key b)
+        {
+            return a.HashCode == b.HashCode && a.Name == b.Name;
+        }
+
+        public static bool operator !=(in Key a, Key b)
+        {
+            return a.HashCode != b.HashCode || a.Name != b.Name;
+        }
+
+        public static bool operator ==(in Key a, string b)
+        {
+            return a.Name == b;
+        }
+
+        public static bool operator !=(in Key a, string b)
+        {
+            return a.Name != b;
+        }
+
+        public bool Equals(Key other)
+        {
+            return HashCode == other.HashCode && Name == other.Name;
+        }
+
+        public override bool Equals(object obj)
+        {
+            return obj is Key other && Equals(other);
+        }
+
+        public override int GetHashCode() => HashCode;
+
+        public override string ToString() => Name;
+    }
+}

+ 9 - 9
Jint/Native/Argument/ArgumentsInstance.cs

@@ -50,7 +50,7 @@ namespace Jint.Native.Argument
         protected override void Initialize()
         {
             var args = _args;
-            SetOwnProperty("length", new PropertyDescriptor(args.Length, PropertyFlag.NonEnumerable));
+            SetOwnProperty(KnownKeys.Length, new PropertyDescriptor(args.Length, PropertyFlag.NonEnumerable));
 
             ObjectInstance map = null;
             if (args.Length > 0)
@@ -59,7 +59,7 @@ namespace Jint.Native.Argument
                 mappedNamed.Clear();
                 for (var i = 0; i < (uint) args.Length; i++)
                 {
-                    var indxStr = TypeConverter.ToString(i);
+                    var indxStr = (Key) TypeConverter.ToString(i);
                     var val = args[i];
                     SetOwnProperty(indxStr, new PropertyDescriptor(val, PropertyFlag.ConfigurableEnumerableWritable));
                     if (i < _names.Length)
@@ -80,19 +80,19 @@ namespace Jint.Native.Argument
             // step 13
             if (!_strict)
             {
-                SetOwnProperty("callee", new PropertyDescriptor(_func, PropertyFlag.NonEnumerable));
+                SetOwnProperty(KnownKeys.Callee, new PropertyDescriptor(_func, PropertyFlag.NonEnumerable));
             }
             // step 14
             else
             {
-                DefineOwnProperty("caller", _engine._getSetThrower, false);
-                DefineOwnProperty("callee", _engine._getSetThrower, false);
+                DefineOwnProperty(KnownKeys.Caller, _engine._getSetThrower, false);
+                DefineOwnProperty(KnownKeys.Callee, _engine._getSetThrower, false);
             }
         }
 
         public ObjectInstance ParameterMap { get; set; }
 
-        public override PropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
         {
             EnsureInitialized();
 
@@ -118,7 +118,7 @@ namespace Jint.Native.Argument
         /// Implementation from ObjectInstance official specs as the one
         /// in ObjectInstance is optimized for the general case and wouldn't work
         /// for arrays
-        public override void Put(string propertyName, JsValue value, bool throwOnError)
+        public override void Put(in Key propertyName, JsValue value, bool throwOnError)
         {
             EnsureInitialized();
 
@@ -156,7 +156,7 @@ namespace Jint.Native.Argument
             }
         }
 
-        public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
+        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc, bool throwOnError)
         {
             if (_func is ScriptFunctionInstance scriptFunctionInstance && scriptFunctionInstance._function._hasRestParameter)
             {
@@ -206,7 +206,7 @@ namespace Jint.Native.Argument
             return base.DefineOwnProperty(propertyName, desc, throwOnError);
         }
 
-        public override bool Delete(string propertyName, bool throwOnError)
+        public override bool Delete(in Key propertyName, bool throwOnError)
         {
             EnsureInitialized();
 

+ 80 - 76
Jint/Native/Array/ArrayInstance.cs

@@ -8,15 +8,12 @@ namespace Jint.Native.Array
 {
     public class ArrayInstance : ObjectInstance
     {
-        private const string PropertyNameLength = "length";
-        private const int PropertyNameLengthLength = 6;
-
         internal PropertyDescriptor _length;
 
         private const int MaxDenseArrayLength = 1024 * 10;
 
         // we have dense and sparse, we usually can start with dense and fall back to sparse when necessary
-        private PropertyDescriptor[] _dense;
+        internal PropertyDescriptor[] _dense;
         private Dictionary<uint, PropertyDescriptor> _sparse;
 
         public ArrayInstance(Engine engine, uint capacity = 0) : base(engine, objectClass: "Array")
@@ -65,7 +62,7 @@ namespace Jint.Native.Array
         /// Implementation from ObjectInstance official specs as the one
         /// in ObjectInstance is optimized for the general case and wouldn't work
         /// for arrays
-        public override void Put(string propertyName, JsValue value, bool throwOnError)
+        public override void Put(in Key propertyName, JsValue value, bool throwOnError)
         {
             if (!CanPut(propertyName))
             {
@@ -101,12 +98,12 @@ namespace Jint.Native.Array
             }
         }
 
-        public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
+        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc, bool throwOnError)
         {
             var oldLenDesc = _length;
             var oldLen = (uint) TypeConverter.ToNumber(oldLenDesc.Value);
 
-            if (propertyName.Length == 6 && propertyName == "length")
+            if (propertyName == KnownKeys.Length)
             {
                 var value = desc.Value;
                 if (ReferenceEquals(value, null))
@@ -300,9 +297,9 @@ namespace Jint.Native.Array
             return (uint) ((JsNumber) _length._value)._value;
         }
 
-        protected override void AddProperty(string propertyName, PropertyDescriptor descriptor)
+        protected override void AddProperty(in Key propertyName, PropertyDescriptor descriptor)
         {
-            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
+            if (propertyName == KnownKeys.Length)
             {
                 _length = descriptor;
                 return;
@@ -311,9 +308,9 @@ namespace Jint.Native.Array
             base.AddProperty(propertyName, descriptor);
         }
 
-        protected override bool TryGetProperty(string propertyName, out PropertyDescriptor descriptor)
+        protected override bool TryGetProperty(in Key propertyName, out PropertyDescriptor descriptor)
         {
-            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
+            if (propertyName == KnownKeys.Length)
             {
                 descriptor = _length;
                 return _length != null;
@@ -326,7 +323,7 @@ namespace Jint.Native.Array
         {
             if (_length != null)
             {
-                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameLength, _length);
+                yield return new KeyValuePair<string, PropertyDescriptor>(KnownKeys.Length, _length);
             }
 
             if (_dense != null)
@@ -354,8 +351,13 @@ namespace Jint.Native.Array
             }
         }
 
-        public override PropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
         {
+            if (propertyName == KnownKeys.Length)
+            {
+                return _length ?? PropertyDescriptor.Undefined;
+            }
+
             if (IsArrayIndex(propertyName, out var index))
             {
                 if (TryGetDescriptor(index, out var result))
@@ -366,31 +368,30 @@ namespace Jint.Native.Array
                 return PropertyDescriptor.Undefined;
             }
 
-            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
-            {
-                return _length ?? PropertyDescriptor.Undefined;
-            }
-
             return base.GetOwnProperty(propertyName);
         }
 
-        internal PropertyDescriptor GetOwnProperty(uint index)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private PropertyDescriptor GetOwnProperty(uint index)
         {
-            if (TryGetDescriptor(index, out var result))
-            {
-                return result;
-            }
-
-            return PropertyDescriptor.Undefined;
+            return TryGetDescriptor(index, out var result)
+                ? result
+                : PropertyDescriptor.Undefined;
         }
 
         internal JsValue Get(uint index)
         {
-            var desc = GetProperty(index);
-            return UnwrapJsValue(desc);
+            var prop = GetOwnProperty(index);
+            if (prop == PropertyDescriptor.Undefined)
+            {
+                prop = Prototype?.GetProperty(TypeConverter.ToString(index)) ?? PropertyDescriptor.Undefined;
+            }
+
+            return UnwrapJsValue(prop);
         }
 
-        internal PropertyDescriptor GetProperty(uint index)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private PropertyDescriptor GetProperty(uint index)
         {
             var prop = GetOwnProperty(index);
             if (prop != PropertyDescriptor.Undefined)
@@ -400,13 +401,13 @@ namespace Jint.Native.Array
             return Prototype?.GetProperty(TypeConverter.ToString(index)) ?? PropertyDescriptor.Undefined;
         }
 
-        protected internal override void SetOwnProperty(string propertyName, PropertyDescriptor desc)
+        protected internal override void SetOwnProperty(in Key propertyName, PropertyDescriptor desc)
         {
             if (IsArrayIndex(propertyName, out var index))
             {
                 WriteArrayValue(index, desc);
             }
-            else if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
+            else if (propertyName == KnownKeys.Length)
             {
                 _length = desc;
             }
@@ -416,7 +417,7 @@ namespace Jint.Native.Array
             }
         }
 
-        public override bool HasOwnProperty(string p)
+        public override bool HasOwnProperty(in Key p)
         {
             if (IsArrayIndex(p, out var index))
             {
@@ -425,7 +426,7 @@ namespace Jint.Native.Array
                        && (_dense == null || (index < (uint) _dense.Length && _dense[index] != null));
             }
 
-            if (p == PropertyNameLength)
+            if (p == KnownKeys.Length)
             {
                 return _length != null;
             }
@@ -433,14 +434,14 @@ namespace Jint.Native.Array
             return base.HasOwnProperty(p);
         }
 
-        public override void RemoveOwnProperty(string p)
+        public override void RemoveOwnProperty(in Key p)
         {
             if (IsArrayIndex(p, out var index))
             {
                 DeleteAt(index);
             }
 
-            if (p == PropertyNameLength)
+            if (p == KnownKeys.Length)
             {
                 _length = null;
             }
@@ -550,27 +551,6 @@ namespace Jint.Native.Array
             return smallest;
         }
 
-        internal uint GetLargestIndex()
-        {
-            if (_dense != null)
-            {
-                return (uint) (_dense.Length - 1);
-            }
-
-            uint largest = uint.MaxValue;
-            // only try to help if collection reasonable small
-            if (_sparse.Count > 0 && _sparse.Count < 100)
-            {
-                largest = 0;
-                foreach (var key in _sparse.Keys)
-                {
-                    largest = System.Math.Max(key, largest);
-                }
-            }
-
-            return largest;
-        }
-
         public bool TryGetValue(uint index, out JsValue value)
         {
             value = Undefined;
@@ -601,14 +581,14 @@ namespace Jint.Native.Array
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private bool TryGetDescriptor(uint index, out PropertyDescriptor descriptor)
         {
-            if (_dense != null)
+            var temp = _dense;
+            if (temp != null)
             {
                 descriptor = null;
-                if (index < (uint) _dense.Length)
+                if (index < (uint) temp.Length)
                 {
-                    descriptor = _dense[index];
+                    descriptor = temp[index];
                 }
-
                 return descriptor != null;
             }
 
@@ -662,7 +642,6 @@ namespace Jint.Native.Array
             _dense = null;
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal void EnsureCapacity(uint capacity)
         {
             if (capacity <= MaxDenseArrayLength && capacity > (uint) _dense.Length)
@@ -700,38 +679,60 @@ namespace Jint.Native.Array
                 EnsureCapacity((uint) newLength);
             }
 
+            var canUseDirectIndexSet = _dense != null && newLength <= _dense.Length;
+
             double n = initialLength;
-            for (var i = 0; i < arguments.Length; i++)
+            foreach (var argument in arguments)
             {
-                var desc = new PropertyDescriptor(arguments[i], PropertyFlag.ConfigurableEnumerableWritable);
-                if (_dense != null && n < _dense.Length)
+                var desc = new PropertyDescriptor(argument, PropertyFlag.ConfigurableEnumerableWritable);
+                if (canUseDirectIndexSet)
                 {
                     _dense[(uint) n] = desc;
                 }
-                else if (n < uint.MaxValue)
-                {
-                    WriteArrayValue((uint) n, desc);
-                }
                 else
                 {
-                    DefineOwnProperty(TypeConverter.ToString((uint) n), desc, true);
+                    WriteValueSlow(n, desc);
                 }
+
                 n++;
             }
 
             // check if we can set length fast without breaking ECMA specification
-            if (n < uint.MaxValue && CanPut(PropertyNameLength))
+            if (n < uint.MaxValue && CanSetLength())
             {
                 _length.Value = (uint) n;
             }
             else
             {
-                Put(PropertyNameLength, newLength, true);
+                Put(KnownKeys.Length, newLength, true);
             }
 
             return (uint) n;
         }
 
+        private bool CanSetLength()
+        {
+            if (!_length.IsAccessorDescriptor())
+            {
+                return _length.Writable;
+            }
+            var set = _length.Set;
+            return !(set is null) && !set.IsUndefined();
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private void WriteValueSlow(double n, PropertyDescriptor desc)
+        {
+            if (n < uint.MaxValue)
+            {
+                WriteArrayValue((uint) n, desc);
+            }
+            else
+            {
+                DefineOwnProperty(TypeConverter.ToString((uint) n), desc, true);
+            }
+        }
+
         internal ArrayInstance Map(JsValue[] arguments)
         {
             var callbackfn = arguments.At(0);
@@ -845,18 +846,21 @@ namespace Jint.Native.Array
                 return;
             }
 
-            if (_dense != null && source._dense != null
-                               && _dense.Length >= targetStartIndex + length
-                               && ReferenceEquals(_dense[targetStartIndex], null))
+            var dense = _dense;
+            var sourceDense = source._dense;
+
+            if (dense != null && sourceDense != null
+                               && (uint) dense.Length >= targetStartIndex + length
+                               && dense[targetStartIndex] is null)
             {
                 uint j = 0;
                 for (uint i = sourceStartIndex; i < sourceStartIndex + length; ++i, j++)
                 {
-                    var sourcePropertyDescriptor = i < source._dense.Length && source._dense[i] != null
-                        ? source._dense[i]
+                    var sourcePropertyDescriptor = i < (uint) sourceDense.Length && sourceDense[i] != null
+                        ? sourceDense[i]
                         : source.GetProperty(i.ToString());
 
-                    _dense[targetStartIndex + j] = sourcePropertyDescriptor?._value != null
+                    dense[targetStartIndex + j] = sourcePropertyDescriptor?._value != null
                         ? new PropertyDescriptor(sourcePropertyDescriptor._value, PropertyFlag.ConfigurableEnumerableWritable)
                         : null;
                 }

+ 41 - 40
Jint/Native/Array/ArrayPrototype.cs

@@ -1266,8 +1266,6 @@ namespace Jint.Native.Array
 
             public abstract ulong GetSmallestIndex(ulong length);
 
-            public abstract ulong GetLargestIndex();
-
             public abstract uint GetLength();
 
             public abstract ulong GetLongLength();
@@ -1278,6 +1276,18 @@ namespace Jint.Native.Array
 
             public abstract JsValue Get(ulong index);
 
+            public virtual JsValue[] GetAll()
+            {
+                var n = (int) GetLength();
+                var jsValues = new JsValue[n];
+                for (uint i = 0; i < (uint) jsValues.Length; i++)
+                {
+                    jsValues[i] = Get(i);
+                }
+
+                return jsValues;
+            }
+
             public abstract bool TryGetValue(ulong index, out JsValue value);
 
             public abstract void Put(ulong index, JsValue value, bool throwOnError);
@@ -1341,9 +1351,9 @@ namespace Jint.Native.Array
                     }
 
                     ulong min = length;
-                    foreach (var key in _instance._properties.Keys)
+                    foreach (var entry in _instance._properties)
                     {
-                        if (ulong.TryParse(key, out var index))
+                        if (ulong.TryParse(entry.Key, out var index))
                         {
                             min = System.Math.Min(index, min);
                         }
@@ -1351,9 +1361,9 @@ namespace Jint.Native.Array
 
                     if (_instance.Prototype?._properties != null)
                     {
-                        foreach (var key in _instance.Prototype._properties.Keys)
+                        foreach (var entry  in _instance.Prototype._properties)
                         {
-                            if (ulong.TryParse(key, out var index))
+                            if (ulong.TryParse(entry.Key, out var index))
                             {
                                 min = System.Math.Min(index, min);
                             }
@@ -1363,38 +1373,6 @@ namespace Jint.Native.Array
                     return min;
                 }
 
-                public override ulong GetLargestIndex()
-                {
-                    if (_instance._properties == null)
-                    {
-                        return 0;
-                    }
-
-                    long? max = null;
-                    foreach (var key in _instance._properties.Keys)
-                    {
-                        max = max ?? long.MinValue;
-                        if (ulong.TryParse(key, out var index))
-                        {
-                            max = System.Math.Max((long) index, max.Value);
-                        }
-                    }
-
-                    if (_instance.Prototype?._properties != null)
-                    {
-                        max = max ?? long.MinValue;
-                        foreach (var key in _instance.Prototype._properties.Keys)
-                        {
-                            if (ulong.TryParse(key, out var index))
-                            {
-                                max = System.Math.Max((long) index, max.Value);
-                            }
-                        }
-                    }
-
-                    return (ulong) (max ?? 0);
-                }
-
                 public override uint GetLength()
                 {
                     var integerLength = GetIntegerLength();
@@ -1445,8 +1423,6 @@ namespace Jint.Native.Array
 
                 public override ulong GetSmallestIndex(ulong length) => _array.GetSmallestIndex();
 
-                public override ulong GetLargestIndex() => _array.GetLargestIndex();
-
                 public override uint GetLength()
                 {
                     return (uint) ((JsNumber) _array._length._value)._value;
@@ -1472,6 +1448,31 @@ namespace Jint.Native.Array
 
                 public override JsValue Get(ulong index) => _array.Get((uint) index);
 
+                public override JsValue[] GetAll()
+                {
+                    var n = _array.Length;
+
+                    if (_array._dense == null || _array._dense.Length < n)
+                    {
+                        return base.GetAll();
+                    }
+
+                    // optimized
+                    var jsValues = new JsValue[n];
+                    for (uint i = 0; i < (uint) jsValues.Length; i++)
+                    {
+                        var prop = _array._dense[i] ?? PropertyDescriptor.Undefined;
+                        if (prop == PropertyDescriptor.Undefined)
+                        {
+                            prop = _array.Prototype?.GetProperty(TypeConverter.ToString(i)) ?? PropertyDescriptor.Undefined;
+                        }
+
+                        jsValues[i] = _array.UnwrapJsValue(prop);
+                    }
+
+                    return jsValues;
+                }
+
                 public override void DeleteAt(ulong index) => _array.DeleteAt((uint) index);
 
                 public override void Put(ulong index, JsValue value, bool throwOnError) => _array.SetIndexValue((uint) index, value, throwOnError);

+ 6 - 4
Jint/Native/Function/ArrowFunctionInstance.cs

@@ -98,22 +98,24 @@ namespace Jint.Native.Function
             }
         }
 
-        public override void Put(string propertyName, JsValue value, bool throwOnError)
+        public override void Put(in Key propertyName, JsValue value, bool throwOnError)
         {
             AssertValidPropertyName(propertyName);
             base.Put(propertyName, value, throwOnError);
         }
 
-        public override JsValue Get(string propertyName)
+        public override JsValue Get(in Key propertyName)
         {
             AssertValidPropertyName(propertyName);
             return base.Get(propertyName);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private void AssertValidPropertyName(string propertyName)
+        private void AssertValidPropertyName(in Key propertyName)
         {
-            if (propertyName == "caller" || propertyName ==  "callee" || propertyName == "arguments")
+            if (propertyName == KnownKeys.Caller
+                || propertyName ==  KnownKeys.Callee
+                || propertyName == KnownKeys.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");
             }

+ 24 - 31
Jint/Native/Function/FunctionInstance.cs

@@ -8,16 +8,10 @@ namespace Jint.Native.Function
 {
     public abstract class FunctionInstance : ObjectInstance, ICallable
     {
-        private const string PropertyNamePrototype = "prototype";
-        private const int PropertyNamePrototypeLength = 9;
         protected internal PropertyDescriptor _prototype;
 
-        private const string PropertyNameLength = "length";
-        private const int PropertyNameLengthLength = 6;
         protected PropertyDescriptor _length;
 
-        private const string PropertyNameName = "name";
-        private const int PropertyNameNameLength = 4;
         private JsValue _name;
         private PropertyDescriptor _nameDescriptor;
 
@@ -82,7 +76,7 @@ namespace Jint.Native.Function
                 return false;
             }
 
-            var po = Get("prototype");
+            var po = Get(KnownKeys.Prototype);
             if (!po.IsObject())
             {
                 ExceptionHelper.ThrowTypeError(_engine, $"Function has non-object prototype '{TypeConverter.ToString(po)}' in instanceof check");
@@ -116,12 +110,11 @@ namespace Jint.Native.Function
         /// </summary>
         /// <param name="propertyName"></param>
         /// <returns></returns>
-        public override JsValue Get(string propertyName)
+        public override JsValue Get(in Key propertyName)
         {
             var v = base.Get(propertyName);
 
-            if (propertyName.Length == 6
-                && propertyName == "caller"
+            if (propertyName == KnownKeys.Caller
                 && ((v.As<FunctionInstance>()?._strict).GetValueOrDefault()))
             {
                 ExceptionHelper.ThrowTypeError(_engine);
@@ -134,15 +127,15 @@ namespace Jint.Native.Function
         {
             if (_prototype != null)
             {
-                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNamePrototype, _prototype);
+                yield return new KeyValuePair<string, PropertyDescriptor>(KnownKeys.Prototype, _prototype);
             }
             if (_length != null)
             {
-                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameLength, _length);
+                yield return new KeyValuePair<string, PropertyDescriptor>(KnownKeys.Length, _length);
             }
             if (!(_name is null))
             {
-                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameName, GetOwnProperty(PropertyNameName));
+                yield return new KeyValuePair<string, PropertyDescriptor>(KnownKeys.Name, GetOwnProperty(KnownKeys.Name));
             }
 
             foreach (var entry in base.GetOwnProperties())
@@ -151,17 +144,17 @@ namespace Jint.Native.Function
             }
         }
 
-        public override PropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
         {
-            if (propertyName.Length == PropertyNamePrototypeLength && propertyName == PropertyNamePrototype)
+            if (propertyName == KnownKeys.Prototype)
             {
                 return _prototype ?? PropertyDescriptor.Undefined;
             }
-            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
+            if (propertyName == KnownKeys.Length)
             {
                 return _length ?? PropertyDescriptor.Undefined;
             }
-            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
+            if (propertyName == KnownKeys.Name)
             {
                 return !(_name is null)
                     ? _nameDescriptor ?? (_nameDescriptor = new PropertyDescriptor(_name, PropertyFlag.Configurable))
@@ -171,17 +164,17 @@ namespace Jint.Native.Function
             return base.GetOwnProperty(propertyName);
         }
 
-        protected internal override void SetOwnProperty(string propertyName, PropertyDescriptor desc)
+        protected internal override void SetOwnProperty(in Key propertyName, PropertyDescriptor desc)
         {
-            if (propertyName.Length == PropertyNamePrototypeLength && propertyName == PropertyNamePrototype)
+            if (propertyName == KnownKeys.Prototype)
             {
                 _prototype = desc;
             }
-            else if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
+            else if (propertyName == KnownKeys.Length)
             {
                 _length = desc;
             }
-            else if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
+            else if (propertyName == KnownKeys.Name)
             {
                 _name = desc._value;
                 _nameDescriptor = desc;
@@ -192,17 +185,17 @@ namespace Jint.Native.Function
             }
         }
 
-        public override bool HasOwnProperty(string propertyName)
+        public override bool HasOwnProperty(in Key propertyName)
         {
-            if (propertyName.Length == PropertyNamePrototypeLength && propertyName == PropertyNamePrototype)
+            if (propertyName == KnownKeys.Prototype)
             {
                 return _prototype != null;
             }
-            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
+            if (propertyName == KnownKeys.Length)
             {
                 return _length != null;
             }
-            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
+            if (propertyName == KnownKeys.Name)
             {
                 return !(_name is null);
             }
@@ -210,17 +203,17 @@ namespace Jint.Native.Function
             return base.HasOwnProperty(propertyName);
         }
 
-        public override void RemoveOwnProperty(string propertyName)
+        public override void RemoveOwnProperty(in Key propertyName)
         {
-            if (propertyName.Length == PropertyNamePrototypeLength && propertyName == PropertyNamePrototype)
+            if (propertyName == KnownKeys.Prototype)
             {
                 _prototype = null;
             }
-            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
+            if (propertyName == KnownKeys.Length)
             {
                 _length = null;
             }
-            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
+            if (propertyName == KnownKeys.Name)
             {
                 _name = null;
                 _nameDescriptor = null;
@@ -229,11 +222,11 @@ namespace Jint.Native.Function
             base.RemoveOwnProperty(propertyName);
         }
 
-        internal void SetFunctionName(string name, bool throwIfExists = false)
+        internal void SetFunctionName(in Key key, bool throwIfExists = false)
         {
             if (_name is null)
             {
-                _name = name;
+                _name = key.Name;
             }
             else if (throwIfExists)
             {

+ 5 - 13
Jint/Native/Function/FunctionPrototype.cs

@@ -66,15 +66,15 @@ namespace Jint.Native.Function
             if (target is FunctionInstance functionInstance)
             {
                 var l = TypeConverter.ToNumber(functionInstance.Get("length")) - (arguments.Length - 1);
-                f.SetOwnProperty("length", new PropertyDescriptor(System.Math.Max(l, 0), PropertyFlag.AllForbidden));
+                f.SetOwnProperty(KnownKeys.Length, new PropertyDescriptor(System.Math.Max(l, 0), PropertyFlag.AllForbidden));
             }
             else
             {
-                f.SetOwnProperty("length", PropertyDescriptor.AllForbiddenDescriptor.NumberZero);
+                f.SetOwnProperty(KnownKeys.Length, PropertyDescriptor.AllForbiddenDescriptor.NumberZero);
             }
 
-            f.DefineOwnProperty("caller", _engine._getSetThrower, false);
-            f.DefineOwnProperty("arguments", _engine._getSetThrower, false);
+            f.DefineOwnProperty(KnownKeys.Caller, _engine._getSetThrower, false);
+            f.DefineOwnProperty(KnownKeys.Arguments, _engine._getSetThrower, false);
 
             return f;
         }
@@ -102,17 +102,9 @@ namespace Jint.Native.Function
 
             var argArrayObj = argArray as ObjectInstance ?? ExceptionHelper.ThrowTypeError<ObjectInstance>(Engine);
             var operations = ArrayPrototype.ArrayOperations.For(argArrayObj);
-
-            uint n = operations.GetLength();
-            var argList = _engine._jsValueArrayPool.RentArray((int) n);
-            for (uint i = 0; i < n; i++)
-            {
-                var nextArg = operations.Get(i);
-                argList[i] = nextArg;
-            }
+            var argList = operations.GetAll();
 
             var result = func.Call(thisArg, argList);
-            _engine._jsValueArrayPool.ReturnArray(argList);
 
             return result;
         }

+ 12 - 14
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -48,8 +48,8 @@ namespace Jint.Native.Function
 
             if (strict)
             {
-                DefineOwnProperty("caller", engine._getSetThrower, false);
-                DefineOwnProperty("arguments", engine._getSetThrower, false);
+                DefineOwnProperty(KnownKeys.Caller, engine._getSetThrower, false);
+                DefineOwnProperty(KnownKeys.Arguments, engine._getSetThrower, false);
             }
         }
 
@@ -134,7 +134,7 @@ namespace Jint.Native.Function
         /// <returns></returns>
         public ObjectInstance Construct(JsValue[] arguments)
         {
-            var proto = Get("prototype").TryCast<ObjectInstance>();
+            var proto = Get(KnownKeys.Prototype).TryCast<ObjectInstance>();
 
             var obj = new ObjectInstance(_engine)
             {
@@ -153,8 +153,6 @@ namespace Jint.Native.Function
 
         private class ObjectInstanceWithConstructor : ObjectInstance
         {
-            private const string PropertyNameConstructor = "constructor";
-            private const int PropertyNameConstructorLength = 11;
             private PropertyDescriptor _constructor;
 
             public ObjectInstanceWithConstructor(Engine engine, ObjectInstance thisObj) : base(engine)
@@ -166,7 +164,7 @@ namespace Jint.Native.Function
             {
                 if (_constructor != null)
                 {
-                    yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameConstructor, _constructor);
+                    yield return new KeyValuePair<string, PropertyDescriptor>(KnownKeys.Constructor, _constructor);
                 }
 
                 foreach (var entry in base.GetOwnProperties())
@@ -175,9 +173,9 @@ namespace Jint.Native.Function
                 }
             }
 
-            public override PropertyDescriptor GetOwnProperty(string propertyName)
+            public override PropertyDescriptor GetOwnProperty(in Key propertyName)
             {
-                if (propertyName.Length == PropertyNameConstructorLength && propertyName == PropertyNameConstructor)
+                if (propertyName == KnownKeys.Constructor)
                 {
                     return _constructor ?? PropertyDescriptor.Undefined;
                 }
@@ -185,9 +183,9 @@ namespace Jint.Native.Function
                 return base.GetOwnProperty(propertyName);
             }
 
-            protected internal override void SetOwnProperty(string propertyName, PropertyDescriptor desc)
+            protected internal override void SetOwnProperty(in Key propertyName, PropertyDescriptor desc)
             {
-                if (propertyName.Length == PropertyNameConstructorLength && propertyName == PropertyNameConstructor)
+                if (propertyName == KnownKeys.Constructor)
                 {
                     _constructor = desc;
                 }
@@ -197,9 +195,9 @@ namespace Jint.Native.Function
                 }
             }
 
-            public override bool HasOwnProperty(string propertyName)
+            public override bool HasOwnProperty(in Key propertyName)
             {
-                if (propertyName.Length == PropertyNameConstructorLength && propertyName == PropertyNameConstructor)
+                if (propertyName == KnownKeys.Constructor)
                 {
                     return _constructor != null;
                 }
@@ -207,9 +205,9 @@ namespace Jint.Native.Function
                 return base.HasOwnProperty(propertyName);
             }
 
-            public override void RemoveOwnProperty(string propertyName)
+            public override void RemoveOwnProperty(in Key propertyName)
             {
-                if (propertyName.Length == PropertyNameConstructorLength && propertyName == PropertyNameConstructor)
+                if (propertyName == KnownKeys.Constructor)
                 {
                     _constructor = null;
                 }

+ 9 - 4
Jint/Native/Global/GlobalObject.cs

@@ -530,6 +530,8 @@ namespace Jint.Native.Global
             _stringBuilder.EnsureCapacity(strLen);
             _stringBuilder.Clear();
 
+            var octets = ArrayExt.Empty<byte>();
+
             for (var k = 0; k < strLen; k++)
             {
                 var C = uriString[k];
@@ -575,8 +577,11 @@ namespace Jint.Native.Global
                             ExceptionHelper.ThrowUriError(_engine);
                         }
 
-                        var Octets = new byte[n];
-                        Octets[0] = B;
+                        octets = octets.Length == n
+                            ? octets
+                            : new byte[n];
+
+                        octets[0] = B;
 
                         if (k + (3 * (n - 1)) >= strLen)
                         {
@@ -606,10 +611,10 @@ namespace Jint.Native.Global
 
                             k += 2;
 
-                            Octets[j] = B;
+                            octets[j] = B;
                         }
 
-                        _stringBuilder.Append(Encoding.UTF8.GetString(Octets, 0, Octets.Length));
+                        _stringBuilder.Append(Encoding.UTF8.GetString(octets, 0, octets.Length));
                     }
                 }
             }

+ 15 - 11
Jint/Native/JsNumber.cs

@@ -62,14 +62,16 @@ namespace Jint.Native
             return _value;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static JsNumber Create(double value)
         {
             // we can cache positive double zero, but not negative, -0 == 0 in C# but in JS it's a different story
+            var temp = _doubleToJsValue;
             if ((value == 0 && BitConverter.DoubleToInt64Bits(value) != NegativeZeroBits || value >= 1)
-                && value < _doubleToJsValue.Length
+                && value < temp.Length
                 && System.Math.Abs(value % 1) <= DoubleIsIntegerTolerance)
             {
-                return _doubleToJsValue[(int) value];
+                return temp[(uint) value];
             }
 
             if (value == -1)
@@ -77,17 +79,17 @@ namespace Jint.Native
                 return DoubleNegativeOne;
             }
 
-            if (value <= double.MaxValue && value >= double.MinValue)
-            {
-                return new JsNumber(value);
-            }
-
             return CreateNumberUnlikely(value);
         }
 
         [MethodImpl(MethodImplOptions.NoInlining)]
         private static JsNumber CreateNumberUnlikely(double value)
         {
+            if (value <= double.MaxValue && value >= double.MinValue)
+            {
+                return new JsNumber(value);
+            }
+
             if (value == double.NegativeInfinity)
             {
                 return DoubleNegativeInfinity;
@@ -108,9 +110,10 @@ namespace Jint.Native
 
         internal static JsNumber Create(int value)
         {
-            if ((uint) value < (uint) _intToJsValue.Length)
+            var temp = _intToJsValue;
+            if ((uint) value < (uint) temp.Length)
             {
-                return _intToJsValue[value];
+                return temp[value];
             }
 
             if (value == -1)
@@ -123,9 +126,10 @@ namespace Jint.Native
 
         internal static JsNumber Create(uint value)
         {
-            if (value < (uint) _intToJsValue.Length)
+            var temp = _intToJsValue;
+            if (value < (uint) temp.Length)
             {
-                return _intToJsValue[value];
+                return temp[value];
             }
 
             return new JsNumber(value);

+ 1 - 1
Jint/Native/JsValue.cs

@@ -367,7 +367,7 @@ namespace Jint.Native
                 jsArray.WriteArrayValue(i, new PropertyDescriptor(jsItem, PropertyFlag.ConfigurableEnumerableWritable));
             }
 
-            jsArray.SetOwnProperty("length", new PropertyDescriptor(arrayLength, PropertyFlag.OnlyWritable));
+            jsArray.SetOwnProperty(KnownKeys.Length, new PropertyDescriptor(arrayLength, PropertyFlag.OnlyWritable));
 
             return jsArray;
         }

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

@@ -18,7 +18,7 @@ namespace Jint.Native.Map
         /// Implementation from ObjectInstance official specs as the one
         /// in ObjectInstance is optimized for the general case and wouldn't work
         /// for arrays
-        public override void Put(string propertyName, JsValue value, bool throwOnError)
+        public override void Put(in Key propertyName, JsValue value, bool throwOnError)
         {
             if (!CanPut(propertyName))
             {
@@ -54,9 +54,9 @@ namespace Jint.Native.Map
             }
         }
 
-        public override PropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
         {
-            if (propertyName.Length == 4 && propertyName == "size")
+            if (propertyName == KnownKeys.Size)
             {
                 return new PropertyDescriptor(_map.Count, PropertyFlag.None);
             }
@@ -64,9 +64,9 @@ namespace Jint.Native.Map
             return base.GetOwnProperty(propertyName);
         }
 
-        protected override bool TryGetProperty(string propertyName, out PropertyDescriptor descriptor)
+        protected override bool TryGetProperty(in Key propertyName, out PropertyDescriptor descriptor)
         {
-            if (propertyName.Length == 4 && propertyName == "size")
+            if (propertyName == KnownKeys.Size)
             {
                 descriptor = new PropertyDescriptor(_map.Count, PropertyFlag.None);
                 return true;

+ 15 - 21
Jint/Native/Object/ObjectInstance.cs

@@ -71,7 +71,7 @@ namespace Jint.Native.Object
             }
         }
 
-        protected virtual void AddProperty(string propertyName, PropertyDescriptor descriptor)
+        protected virtual void AddProperty(in Key propertyName, PropertyDescriptor descriptor)
         {
             if (_properties == null)
             {
@@ -81,7 +81,7 @@ namespace Jint.Native.Object
             _properties[propertyName] = descriptor;
         }
 
-        protected virtual bool TryGetProperty(string propertyName, out PropertyDescriptor descriptor)
+        protected virtual bool TryGetProperty(in Key propertyName, out PropertyDescriptor descriptor)
         {
             if (_properties == null)
             {
@@ -92,14 +92,14 @@ namespace Jint.Native.Object
             return _properties.TryGetValue(propertyName, out descriptor);
         }
 
-        public virtual bool HasOwnProperty(string propertyName)
+        public virtual bool HasOwnProperty(in Key propertyName)
         {
             EnsureInitialized();
 
             return _properties?.ContainsKey(propertyName) == true;
         }
 
-        public virtual void RemoveOwnProperty(string propertyName)
+        public virtual void RemoveOwnProperty(in Key propertyName)
         {
             EnsureInitialized();
 
@@ -112,7 +112,7 @@ namespace Jint.Native.Object
         /// </summary>
         /// <param name="propertyName"></param>
         /// <returns></returns>
-        public virtual JsValue Get(string propertyName)
+        public virtual JsValue Get(in Key propertyName)
         {
             var desc = GetProperty(propertyName);
             return UnwrapJsValue(desc);
@@ -124,7 +124,6 @@ namespace Jint.Native.Object
             return UnwrapJsValue(desc, this);
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static JsValue UnwrapJsValue(PropertyDescriptor desc, JsValue thisObject)
         {
             if (desc == PropertyDescriptor.Undefined)
@@ -162,7 +161,7 @@ namespace Jint.Native.Object
         /// </summary>
         /// <param name="propertyName"></param>
         /// <returns></returns>
-        public virtual PropertyDescriptor GetOwnProperty(string propertyName)
+        public virtual PropertyDescriptor GetOwnProperty(in Key propertyName)
         {
             EnsureInitialized();
 
@@ -171,7 +170,7 @@ namespace Jint.Native.Object
             return descriptor ?? PropertyDescriptor.Undefined;
         }
 
-        protected internal virtual void SetOwnProperty(string propertyName, PropertyDescriptor desc)
+        protected internal virtual void SetOwnProperty(in Key propertyName, PropertyDescriptor desc)
         {
             EnsureInitialized();
 
@@ -189,7 +188,7 @@ namespace Jint.Native.Object
         /// <param name="propertyName"></param>
         /// <returns></returns>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public PropertyDescriptor GetProperty(string propertyName)
+        public PropertyDescriptor GetProperty(in Key propertyName)
         {
             var prop = GetOwnProperty(propertyName);
 
@@ -201,7 +200,7 @@ namespace Jint.Native.Object
             return Prototype?.GetProperty(propertyName) ?? PropertyDescriptor.Undefined;
         }
 
-        public bool TryGetValue(string propertyName, out JsValue value)
+        public bool TryGetValue(in Key propertyName, out JsValue value)
         {
             value = Undefined;
             var desc = GetOwnProperty(propertyName);
@@ -248,7 +247,7 @@ namespace Jint.Native.Object
         /// <param name="propertyName"></param>
         /// <param name="value"></param>
         /// <param name="throwOnError"></param>
-        public virtual void Put(string propertyName, JsValue value, bool throwOnError)
+        public virtual void Put(in Key propertyName, JsValue value, bool throwOnError)
         {
             if (!CanPut(propertyName))
             {
@@ -296,7 +295,7 @@ namespace Jint.Native.Object
         /// </summary>
         /// <param name="propertyName"></param>
         /// <returns></returns>
-        public bool CanPut(string propertyName)
+        public bool CanPut(in Key propertyName)
         {
             var desc = GetOwnProperty(propertyName);
 
@@ -355,7 +354,7 @@ namespace Jint.Native.Object
         /// <param name="propertyName"></param>
         /// <returns></returns>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public bool HasProperty(string propertyName)
+        public bool HasProperty(in Key propertyName)
         {
             return GetProperty(propertyName) != PropertyDescriptor.Undefined;
         }
@@ -368,7 +367,7 @@ namespace Jint.Native.Object
         /// <param name="propertyName"></param>
         /// <param name="throwOnError"></param>
         /// <returns></returns>
-        public virtual bool Delete(string propertyName, bool throwOnError)
+        public virtual bool Delete(in Key propertyName, bool throwOnError)
         {
             var desc = GetOwnProperty(propertyName);
 
@@ -497,7 +496,7 @@ namespace Jint.Native.Object
         /// <param name="desc"></param>
         /// <param name="throwOnError"></param>
         /// <returns></returns>
-        public virtual bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
+        public virtual bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc, bool throwOnError)
         {
             var current = GetOwnProperty(propertyName);
 
@@ -735,17 +734,12 @@ namespace Jint.Native.Object
             SetOwnProperty(name, new PropertyDescriptor(value, writable, enumerable, configurable));
         }
 
-        internal void FastAddProperty(string name, JsValue value, PropertyFlag flags)
-        {
-            SetOwnProperty(name, new PropertyDescriptor(value, flags));
-        }
-
         /// <summary>
         /// Optimized version of [[Put]] when the property is known to be already declared
         /// </summary>
         /// <param name="name"></param>
         /// <param name="value"></param>
-        public void FastSetProperty(string name, PropertyDescriptor value)
+        public void FastSetProperty(in Key name, PropertyDescriptor value)
         {
             SetOwnProperty(name, value);
         }

+ 1 - 1
Jint/Native/RegExp/RegExpPrototype.cs

@@ -139,7 +139,7 @@ namespace Jint.Native.RegExp
         {
             array.SetOwnProperty("index", new PropertyDescriptor(indexValue, PropertyFlag.ConfigurableEnumerableWritable));
             array.SetOwnProperty("input", new PropertyDescriptor(inputValue, PropertyFlag.ConfigurableEnumerableWritable));
-            array.SetOwnProperty("length", new PropertyDescriptor(lengthValue, PropertyFlag.OnlyWritable));
+            array.SetOwnProperty(KnownKeys.Length, new PropertyDescriptor(lengthValue, PropertyFlag.OnlyWritable));
             return array;
         }
     }

+ 6 - 13
Jint/Native/Set/SetInstance.cs

@@ -1,5 +1,4 @@
-using System.Runtime.CompilerServices;
-using Jint.Native.Object;
+using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 
@@ -18,7 +17,7 @@ namespace Jint.Native.Set
         /// Implementation from ObjectInstance official specs as the one
         /// in ObjectInstance is optimized for the general case and wouldn't work
         /// for arrays
-        public override void Put(string propertyName, JsValue value, bool throwOnError)
+        public override void Put(in Key propertyName, JsValue value, bool throwOnError)
         {
             if (!CanPut(propertyName))
             {
@@ -54,9 +53,9 @@ namespace Jint.Native.Set
             }
         }
 
-        public override PropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
         {
-            if (propertyName.Length == 4 && propertyName == "size")
+            if (propertyName == KnownKeys.Size)
             {
                 return new PropertyDescriptor(_set.Count, PropertyFlag.None);
             }
@@ -64,9 +63,9 @@ namespace Jint.Native.Set
             return base.GetOwnProperty(propertyName);
         }
 
-        protected override bool TryGetProperty(string propertyName, out PropertyDescriptor descriptor)
+        protected override bool TryGetProperty(in Key propertyName, out PropertyDescriptor descriptor)
         {
-            if (propertyName.Length == 4 && propertyName == "size")
+            if (propertyName == KnownKeys.Size)
             {
                 descriptor = new PropertyDescriptor(_set.Count, PropertyFlag.None);
                 return true;
@@ -120,11 +119,5 @@ namespace Jint.Native.Set
         {
             return _engine.Iterator.Construct(_set._list);
         }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal uint GetSize()
-        {
-            return (uint) _set.Count;
-        }
     }
 }

+ 1 - 3
Jint/Native/String/StringConstructor.cs

@@ -178,9 +178,7 @@ namespace Jint.Native.String
                 Prototype = PrototypeObject,
                 PrimitiveValue = value,
                 Extensible = true,
-                _length = value.Length == 0
-                    ? PropertyDescriptor.AllForbiddenDescriptor.NumberZero
-                    : new PropertyDescriptor(value.Length, PropertyFlag.AllForbidden)
+                _length = PropertyDescriptor.AllForbiddenDescriptor.ForNumber(value.Length)
             };
 
             return instance;

+ 10 - 13
Jint/Native/String/StringInstance.cs

@@ -7,9 +7,6 @@ namespace Jint.Native.String
 {
     public class StringInstance : ObjectInstance, IPrimitiveInstance
     {
-        private const string PropertyNameLength = "length";
-        private const int PropertyNameLengthLength = 6;
-
         internal PropertyDescriptor _length;
 
         public StringInstance(Engine engine)
@@ -34,14 +31,14 @@ namespace Jint.Native.String
             return false;
         }
 
-        public override PropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
         {
-            if (propertyName.Length == 8 && propertyName == "Infinity")
+            if (propertyName == KnownKeys.Infinity)
             {
                 return PropertyDescriptor.Undefined;
             }
 
-            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
+            if (propertyName == KnownKeys.Length)
             {
                 return _length ?? PropertyDescriptor.Undefined;
             }
@@ -78,7 +75,7 @@ namespace Jint.Native.String
         {
             if (_length != null)
             {
-                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameLength, _length);
+                yield return new KeyValuePair<string, PropertyDescriptor>(KnownKeys.Length, _length);
             }
 
             foreach (var entry in base.GetOwnProperties())
@@ -87,9 +84,9 @@ namespace Jint.Native.String
             }
         }
 
-        protected internal override void SetOwnProperty(string propertyName, PropertyDescriptor desc)
+        protected internal override void SetOwnProperty(in Key propertyName, PropertyDescriptor desc)
         {
-            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
+            if (propertyName == KnownKeys.Length)
             {
                 _length = desc;
             }
@@ -99,9 +96,9 @@ namespace Jint.Native.String
             }
         }
 
-        public override bool HasOwnProperty(string propertyName)
+        public override bool HasOwnProperty(in Key propertyName)
         {
-            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
+            if (propertyName == KnownKeys.Length)
             {
                 return _length != null;
             }
@@ -109,9 +106,9 @@ namespace Jint.Native.String
             return base.HasOwnProperty(propertyName);
         }
 
-        public override void RemoveOwnProperty(string propertyName)
+        public override void RemoveOwnProperty(in Key propertyName)
         {
-            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
+            if (propertyName == KnownKeys.Length)
             {
                 _length = null;
             }

+ 1 - 1
Jint/Pooling/ReferencePool.cs

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

+ 1 - 1
Jint/Runtime/Arguments.cs

@@ -23,7 +23,7 @@ namespace Jint.Runtime
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static JsValue At(this JsValue[] args, int index, JsValue undefinedValue)
         {
-            return index < args.Length ? args[index] : undefinedValue;
+            return (uint) index < (uint) args.Length ? args[index] : undefinedValue;
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]

+ 2 - 2
Jint/Runtime/Debugger/DebugHandler.cs

@@ -40,7 +40,7 @@ namespace Jint.Runtime.Debugger
 
         internal void AddToDebugCallStack(CallExpression callExpression)
         {
-            var identifier = callExpression.Callee as Identifier;
+            var identifier = callExpression.Callee as Esprima.Ast.Identifier;
             if (identifier != null)
             {
                 var stack = identifier.Name + "(";
@@ -50,7 +50,7 @@ namespace Jint.Runtime.Debugger
                 {
                     if (argument != null)
                     {
-                        var argIdentifier = argument as Identifier;
+                        var argIdentifier = argument as Esprima.Ast.Identifier;
                         paramStrings.Add(argIdentifier != null ? argIdentifier.Name : "null");
                     }
                     else

+ 15 - 11
Jint/Runtime/Descriptors/PropertyDescriptor.cs

@@ -400,6 +400,8 @@ namespace Jint.Runtime.Descriptors
 
         internal sealed class AllForbiddenDescriptor : PropertyDescriptor
         {
+            private static readonly PropertyDescriptor[] _cache;
+
             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));
@@ -407,6 +409,15 @@ namespace Jint.Runtime.Descriptors
             public static readonly AllForbiddenDescriptor BooleanFalse = new AllForbiddenDescriptor(JsBoolean.False);
             public static readonly AllForbiddenDescriptor BooleanTrue = new AllForbiddenDescriptor(JsBoolean.True);
 
+            static AllForbiddenDescriptor()
+            {
+                _cache = new PropertyDescriptor[10];
+                for (int i = 0; i < _cache.Length; ++i)
+                {
+                    _cache[i] = new AllForbiddenDescriptor(JsNumber.Create(i));
+                }
+            }
+
             private AllForbiddenDescriptor(JsValue value)
                 : base(PropertyFlag.AllForbidden)
             {
@@ -415,17 +426,10 @@ namespace Jint.Runtime.Descriptors
 
             public static PropertyDescriptor ForNumber(int number)
             {
-                switch (number)
-                {
-                    case 2:
-                        return NumberTwo;
-                    case 1:
-                        return NumberOne;
-                    case 0:
-                        return NumberZero;
-                    default:
-                        return new PropertyDescriptor(number, PropertyFlag.AllForbidden);
-                }
+                var temp = _cache;
+                return (uint) number < temp.Length
+                    ? temp[number]
+                    : new PropertyDescriptor(number, PropertyFlag.AllForbidden);
             }
         }
     }

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

@@ -8,7 +8,7 @@ namespace Jint.Runtime.Descriptors.Specialized
     {
         private readonly EnvironmentRecord _env;
         private readonly Engine _engine;
-        private readonly string _name;
+        private readonly Key _name;
 
         private GetterFunctionInstance _get;
         private SetterFunctionInstance _set;
@@ -21,7 +21,7 @@ namespace Jint.Runtime.Descriptors.Specialized
         {
             _env = env;
             _engine = engine;
-            _name = name;
+            _name = new Key(name);
         }
 
         public override JsValue Get => _get = _get ?? new GetterFunctionInstance(_engine, DoGet);
@@ -29,7 +29,7 @@ namespace Jint.Runtime.Descriptors.Specialized
 
         private JsValue DoGet(JsValue n)
         {
-            return _env.TryGetBinding(_name, false, out var binding)
+            return _env.TryGetBinding(_name, false, out var binding, out _)
                 ? binding.Value
                 : JsValue.Undefined;
         }

+ 52 - 42
Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs

@@ -19,10 +19,9 @@ namespace Jint.Runtime.Environments
     {
         private StringDictionarySlim<Binding> _dictionary;
         private bool _set;
-        private string _key;
+        private Key _key;
         private Binding _value;
 
-        private const string BindingNameArguments = "arguments";
         private Binding _argumentsBinding;
 
         // false = not accessed, true = accessed, null = values copied
@@ -32,7 +31,7 @@ namespace Jint.Runtime.Environments
         {
         }
 
-        private void SetItem(string key, in Binding value)
+        private void SetItem(in Key key, in Binding value)
         {
             if (_set && _key != key)
             {
@@ -54,25 +53,25 @@ namespace Jint.Runtime.Environments
             }
         }
 
-        private ref Binding GetExistingItem(string key)
+        private ref Binding GetExistingItem(in Key key)
         {
             if (_set && _key == key)
             {
                 return ref _value;
             }
 
-            if (key.Length == 9 && key == BindingNameArguments)
+            if (key == KnownKeys.Arguments)
             {
                 _argumentsBindingWasAccessed = true;
                 return ref _argumentsBinding;
             }
 
-            return ref _dictionary[key];
+            return ref _dictionary.GetOrAddValueRef(key);
         }
 
-        private bool ContainsKey(string key)
+        private bool ContainsKey(in Key key)
         {
-            if (key.Length == 9 && key == BindingNameArguments)
+            if (key == KnownKeys.Arguments)
             {
                 return !ReferenceEquals(_argumentsBinding.Value, null);
             }
@@ -85,16 +84,16 @@ namespace Jint.Runtime.Environments
             return _dictionary?.ContainsKey(key) == true;
         }
 
-        private void Remove(string key)
+        private void Remove(in Key key)
         {
             if (_set && key == _key)
             {
                 _set = false;
-                _key = null;
+                _key = default;
                 _value = default;
             }
-            
-            if (key == BindingNameArguments)
+
+            if (key == KnownKeys.Arguments)
             {
                 _argumentsBinding.Value = null;
             }
@@ -104,7 +103,7 @@ namespace Jint.Runtime.Environments
             }
         }
 
-        private bool TryGetValue(string key, out Binding value)
+        private bool TryGetValue(in Key key, out Binding value)
         {
             value = default;
             if (_set && _key == key)
@@ -116,43 +115,51 @@ namespace Jint.Runtime.Environments
             return _dictionary != null && _dictionary.TryGetValue(key, out value);
         }
 
-        public override bool HasBinding(string name)
+        public override bool HasBinding(in Key name)
         {
             return ContainsKey(name);
         }
 
-        internal override bool TryGetBinding(string name, bool strict, out Binding binding)
+        internal override bool TryGetBinding(
+            in Key name,
+            bool strict,
+            out Binding binding,
+            out JsValue value)
         {
             if (_set && _key == name)
             {
                 binding = _value;
+                value = UnwrapBindingValue(strict, _value);
                 return true;
             }
 
-            if (name.Length == 9
-                && name == BindingNameArguments
+            if (name == KnownKeys.Arguments
                 && !ReferenceEquals(_argumentsBinding.Value, null))
             {
                 _argumentsBindingWasAccessed = true;
                 binding = _argumentsBinding;
+                value = UnwrapBindingValue(strict, _argumentsBinding);
                 return true;
             }
 
             if (_dictionary != null)
             {
-                return _dictionary.TryGetValue(name, out binding);
+                var success = _dictionary.TryGetValue(name, out binding);
+                value = success ? UnwrapBindingValue(strict, binding) : default;
+                return success;
             }
 
             binding = default;
+            value = default;
             return false;
         }
 
-        public override void CreateMutableBinding(string name, JsValue value, bool canBeDeleted = false)
+        public override void CreateMutableBinding(in Key name, JsValue value, bool canBeDeleted = false)
         {
             SetItem(name, new Binding(value, canBeDeleted, mutable: true));
         }
 
-        public override void SetMutableBinding(string name, JsValue value, bool strict)
+        public override void SetMutableBinding(in Key name, JsValue value, bool strict)
         {
             ref var binding = ref GetExistingItem(name);
 
@@ -169,19 +176,14 @@ namespace Jint.Runtime.Environments
             }
         }
 
-        public override JsValue GetBindingValue(string name, bool strict)
+        public override JsValue GetBindingValue(in Key name, bool strict)
         {
             ref var binding = ref GetExistingItem(name);
-            return UnwrapBindingValue(name, strict, binding);
-        }
-
-        internal override JsValue UnwrapBindingValue(string name, bool strict, in Binding binding)
-        {
-            return UnwrapBindingValueInternal(strict, binding);
+            return UnwrapBindingValue(strict, binding);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private JsValue UnwrapBindingValueInternal(bool strict, in Binding binding)
+        private JsValue UnwrapBindingValue(bool strict, in Binding binding)
         {
             if (!binding.Mutable && binding.Value._type == Types.Undefined)
             {
@@ -201,7 +203,7 @@ namespace Jint.Runtime.Environments
             throw new JavaScriptException(_engine.ReferenceError, "Can't access an uninitialized immutable binding.");
         }
 
-        public override bool DeleteBinding(string name)
+        public override bool DeleteBinding(in Key name)
         {
             ref Binding binding = ref GetExistingItem(name);
 
@@ -248,10 +250,18 @@ namespace Jint.Runtime.Environments
 
             if (!ReferenceEquals(_argumentsBinding.Value, null))
             {
-                keys[n++] = BindingNameArguments;
+                keys[n++] = KnownKeys.Arguments;
             }
 
-            _dictionary?.Keys.CopyTo(keys, n);
+            if (_dictionary == null)
+            {
+                return keys;
+            }
+
+            foreach (var entry in _dictionary)
+            {
+                keys[n++] = entry.Key;
+            }
 
             return keys;
         }
@@ -287,7 +297,7 @@ namespace Jint.Runtime.Environments
         {
             var argument = arguments.Length > index ? arguments[index] : Undefined;
 
-            if (parameter is Identifier identifier)
+            if (parameter is Esprima.Ast.Identifier identifier)
             {
                 SetItemSafely(identifier.Name, argument, initiallyEmpty);
             }
@@ -307,7 +317,7 @@ namespace Jint.Runtime.Environments
 
                 argument = rest;
 
-                if (restElement.Argument is Identifier restIdentifier)
+                if (restElement.Argument is Esprima.Ast.Identifier restIdentifier)
                 {
                     SetItemSafely(restIdentifier.Name, argument, initiallyEmpty);
                 }
@@ -344,9 +354,9 @@ namespace Jint.Runtime.Environments
                 {
                     arrayContents = new JsValue[array.Length];
 
-                    for (uint contentsIndex = 0; contentsIndex < array.Length; contentsIndex++)
+                    for (uint i = 0; i < (uint) arrayContents.Length; i++)
                     {
-                        arrayContents[contentsIndex] = array.Get(contentsIndex);
+                        arrayContents[i] = array.Get(i);
                     }
                 }
 
@@ -372,7 +382,7 @@ namespace Jint.Runtime.Environments
                 var jsValues = _engine._jsValueArrayPool.RentArray(1);
                 foreach (var property in objectPattern.Properties)
                 {
-                    if (property.Key is Identifier propertyIdentifier)
+                    if (property.Key is Esprima.Ast.Identifier propertyIdentifier)
                     {
                         argument = argumentObject.Get(propertyIdentifier.Name);
                     }
@@ -393,9 +403,9 @@ namespace Jint.Runtime.Environments
             }
             else if (parameter is AssignmentPattern assignmentPattern)
             {
-                var idLeft = assignmentPattern.Left as Identifier;
+                var idLeft = assignmentPattern.Left as Esprima.Ast.Identifier;
                 if (idLeft != null
-                    && assignmentPattern.Right is Identifier idRight
+                    && assignmentPattern.Right is Esprima.Ast.Identifier idRight
                     && idLeft.Name == idRight.Name)
                 {
                     ExceptionHelper.ThrowReferenceError(_engine, idRight.Name);
@@ -432,12 +442,12 @@ namespace Jint.Runtime.Environments
             }
         }
 
-        private void SetItemSafely(string name, JsValue argument, bool initiallyEmpty)
+        private void SetItemSafely(in Key name, JsValue argument, bool initiallyEmpty)
         {
             if (initiallyEmpty || !TryGetValue(name, out var existing))
             {
                 var binding = new Binding(argument, false, true);
-                if (name.Length == 9 && name == BindingNameArguments)
+                if (name == KnownKeys.Arguments)
                 {
                     _argumentsBinding = binding;
                 }
@@ -470,9 +480,9 @@ namespace Jint.Runtime.Environments
                 for (var j = 0; j < declarationsCount; j++)
                 {
                     var d = variableDeclaration.Declarations[j];
-                    if (d.Id is Identifier id)
+                    if (d.Id is Esprima.Ast.Identifier id)
                     {
-                        var dn = id.Name;
+                        Key dn = id.Name;
                         if (!ContainsKey(dn))
                         {
                             var binding = new Binding(Undefined, canBeDeleted: false, mutable: true);

+ 14 - 12
Jint/Runtime/Environments/EnvironmentRecord.cs

@@ -16,15 +16,17 @@ namespace Jint.Runtime.Environments
         }
 
         /// <summary>
-        /// Determines if an environment record has a binding for an identifier. 
+        /// Determines if an environment record has a binding for an identifier.
         /// </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(string name);
+        public abstract bool HasBinding(in Key name);
 
-        internal abstract bool TryGetBinding(string name, bool strict, out Binding binding);
-
-        internal abstract JsValue UnwrapBindingValue(string name, bool strict, in Binding binding);
+        internal abstract bool TryGetBinding(
+            in Key name,
+            bool strict,
+            out Binding binding,
+            out JsValue value);
 
         /// <summary>
         /// Creates a new mutable binding in an environment record.
@@ -32,30 +34,30 @@ 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(string name, JsValue value, bool canBeDeleted = false);
+        public abstract void CreateMutableBinding(in Key name, JsValue value, bool canBeDeleted = false);
 
         /// <summary>
-        /// Sets the value of an already existing mutable binding in an environment record. 
+        /// Sets the value of an already existing mutable binding in an environment record.
         /// </summary>
         /// <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(string name, JsValue value, bool strict);
-        
+        public abstract void SetMutableBinding(in Key name, JsValue value, bool strict);
+
         /// <summary>
-        /// Returns the value of an already existing binding from an environment record. 
+        /// Returns the value of an already existing binding from an environment record.
         /// </summary>
         /// <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(string name, bool strict);
+        public abstract JsValue GetBindingValue(in Key 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(string name);
+        public abstract bool DeleteBinding(in Key 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.

+ 7 - 30
Jint/Runtime/Environments/LexicalEnvironment.cs

@@ -28,7 +28,7 @@ namespace Jint.Runtime.Environments
         public LexicalEnvironment Outer => _outer;
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static Reference GetIdentifierReference(LexicalEnvironment lex, string name, bool strict)
+        public static Reference GetIdentifierReference(LexicalEnvironment lex, in Key name, bool strict)
         {
             var identifierEnvironment = TryGetIdentifierEnvironmentWithBindingValue(lex, name, strict, out var temp, out _)
                 ? temp
@@ -37,45 +37,22 @@ namespace Jint.Runtime.Environments
             return lex._engine._referencePool.Rent(identifierEnvironment, name, strict);
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static bool TryGetIdentifierEnvironmentWithBindingValue(
             LexicalEnvironment lex,
-            string name,
-            bool strict,
-            out EnvironmentRecord record,
-            out JsValue value)
-        {
-            // optimize for common case where result is in one of the nearest scopes
-            if (lex._record.TryGetBinding(name, strict, out var binding))
-            {
-                record = lex._record;
-                value = lex._record.UnwrapBindingValue(name, strict, binding);
-                return true;
-            }
-
-            if (lex._outer == null)
-            {
-                record = default;
-                value = default;
-                return false;
-            }
-
-            return TryGetIdentifierReferenceLooping(lex._outer, name, strict, out record, out value);
-        }
-
-        private static bool TryGetIdentifierReferenceLooping(
-            LexicalEnvironment lex,
-            string name,
+            in Key name,
             bool strict,
             out EnvironmentRecord record,
             out JsValue value)
         {
             while (true)
             {
-                if (lex._record.TryGetBinding(name, strict, out var binding))
+                if (lex._record.TryGetBinding(
+                    name,
+                    strict,
+                    out _,
+                    out value))
                 {
                     record = lex._record;
-                    value = lex._record.UnwrapBindingValue(name, strict, binding);
                     return true;
                 }
 

+ 17 - 15
Jint/Runtime/Environments/ObjectEnvironmentRecord.cs

@@ -21,33 +21,35 @@ namespace Jint.Runtime.Environments
             _provideThis = provideThis;
         }
 
-        public override bool HasBinding(string name)
+        public override bool HasBinding(in Key name)
         {
             return _bindingObject.HasProperty(name);
         }
 
-        internal override bool TryGetBinding(string name, bool strict, out Binding binding)
+        internal override bool TryGetBinding(
+            in Key name,
+            bool strict,
+            out Binding binding,
+            out JsValue value)
         {
-            if (!_bindingObject.HasProperty(name))
+            // we unwrap by name
+            binding = default;
+
+            var desc = _bindingObject.GetProperty(name);
+            if (desc == PropertyDescriptor.Undefined)
             {
-                binding = default;
+                value = default;
                 return false;
             }
 
-            // we unwrap by name
-            binding = default;
+            value = ObjectInstance.UnwrapJsValue(desc, this);
             return true;
         }
 
-        internal override JsValue UnwrapBindingValue(string name, bool strict, in Binding binding)
-        {
-            return GetBindingValue(name, strict);
-        }
-
         /// <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 = true)
+        public override void CreateMutableBinding(in Key name, JsValue value, bool configurable = true)
         {
             var propertyDescriptor = configurable
                 ? new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable)
@@ -56,12 +58,12 @@ namespace Jint.Runtime.Environments
             _bindingObject.SetOwnProperty(name, propertyDescriptor);
         }
 
-        public override void SetMutableBinding(string name, JsValue value, bool strict)
+        public override void SetMutableBinding(in Key name, JsValue value, bool strict)
         {
             _bindingObject.Put(name, value, strict);
         }
 
-        public override JsValue GetBindingValue(string name, bool strict)
+        public override JsValue GetBindingValue(in Key name, bool strict)
         {
             var desc = _bindingObject.GetProperty(name);
             if (strict && desc == PropertyDescriptor.Undefined)
@@ -72,7 +74,7 @@ namespace Jint.Runtime.Environments
             return ObjectInstance.UnwrapJsValue(desc, this);
         }
 
-        public override bool DeleteBinding(string name)
+        public override bool DeleteBinding(in Key name)
         {
             return _bindingObject.Delete(name, false);
         }

+ 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());
+            ThrowReferenceError(engine, reference?.GetReferencedName().Name);
         }
 
         public static void ThrowReferenceError(Engine engine, string name)

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

@@ -23,7 +23,7 @@ namespace Jint.Runtime.Interop
             _path = path;
         }
 
-        public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
+        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc, bool throwOnError)
         {
             if (throwOnError)
             {
@@ -33,7 +33,7 @@ namespace Jint.Runtime.Interop
             return false;
         }
 
-        public override bool Delete(string propertyName, bool throwOnError)
+        public override bool Delete(in Key propertyName, bool throwOnError)
         {
             if (throwOnError)
             {
@@ -80,7 +80,7 @@ namespace Jint.Runtime.Interop
             }
         }
 
-        public override JsValue Get(string propertyName)
+        public override JsValue Get(in Key propertyName)
         {
             var newPath = _path + "." + propertyName;
 
@@ -196,7 +196,7 @@ namespace Jint.Runtime.Interop
             }
         }
 
-        public override PropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
         {
             return PropertyDescriptor.Undefined;
         }

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

@@ -32,7 +32,7 @@ namespace Jint.Runtime.Interop
 
         internal override bool IsArrayLike { get; }
 
-        public override void Put(string propertyName, JsValue value, bool throwOnError)
+        public override void Put(in Key propertyName, JsValue value, bool throwOnError)
         {
             if (!CanPut(propertyName))
             {
@@ -59,7 +59,7 @@ namespace Jint.Runtime.Interop
             }
         }
 
-        public override PropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
         {
             if (TryGetProperty(propertyName, out var x))
             {

+ 8 - 8
Jint/Runtime/Interop/TypeReference.cs

@@ -108,7 +108,7 @@ namespace Jint.Runtime.Interop
             return base.HasInstance(v);
         }
 
-        public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
+        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc, bool throwOnError)
         {
             if (throwOnError)
             {
@@ -118,7 +118,7 @@ namespace Jint.Runtime.Interop
             return false;
         }
 
-        public override bool Delete(string propertyName, bool throwOnError)
+        public override bool Delete(in Key propertyName, bool throwOnError)
         {
             if (throwOnError)
             {
@@ -128,7 +128,7 @@ namespace Jint.Runtime.Interop
             return false;
         }
 
-        public override void Put(string propertyName, JsValue value, bool throwOnError)
+        public override void Put(in Key propertyName, JsValue value, bool throwOnError)
         {
             if (!CanPut(propertyName))
             {
@@ -157,7 +157,7 @@ namespace Jint.Runtime.Interop
             ownDesc.Value = value;
         }
 
-        public override PropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(in Key key)
         {
             // todo: cache members locally
 
@@ -168,7 +168,7 @@ namespace Jint.Runtime.Interop
 
                 for (int i = 0; i < enumValues.Length; i++)
                 {
-                    if (enumNames.GetValue(i) as string == propertyName)
+                    if (enumNames.GetValue(i) as string == key.Name)
                     {
                         return new PropertyDescriptor((int) enumValues.GetValue(i), PropertyFlag.AllForbidden);
                     }
@@ -176,13 +176,13 @@ namespace Jint.Runtime.Interop
                 return PropertyDescriptor.Undefined;
             }
 
-            var propertyInfo = ReferenceType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Static);
+            var propertyInfo = ReferenceType.GetProperty(key.Name, BindingFlags.Public | BindingFlags.Static);
             if (propertyInfo != null)
             {
                 return new PropertyInfoDescriptor(Engine, propertyInfo, Type);
             }
 
-            var fieldInfo = ReferenceType.GetField(propertyName, BindingFlags.Public | BindingFlags.Static);
+            var fieldInfo = ReferenceType.GetField(key.Name, BindingFlags.Public | BindingFlags.Static);
             if (fieldInfo != null)
             {
                 return new FieldInfoDescriptor(Engine, fieldInfo, Type);
@@ -191,7 +191,7 @@ namespace Jint.Runtime.Interop
             List<MethodInfo> methodInfo = null;
             foreach (var mi in ReferenceType.GetMethods(BindingFlags.Public | BindingFlags.Static))
             {
-                if (mi.Name == propertyName)
+                if (mi.Name == key.Name)
                 {
                     methodInfo = methodInfo ?? new List<MethodInfo>();
                     methodInfo.Add(mi);

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

@@ -82,7 +82,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             for (uint i = 0; i < pattern.Elements.Count; i++)
             {
                 var left = pattern.Elements[(int) i];
-                if (left is Identifier identifier)
+                if (left is Esprima.Ast.Identifier identifier)
                 {
                     JsValue value;
                     if (arrayOperations != null)
@@ -133,7 +133,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                             protocol.Execute();
                         }
 
-                        if (restElement.Argument is Identifier leftIdentifier)
+                        if (restElement.Argument is Esprima.Ast.Identifier leftIdentifier)
                         {
                             AssignToIdentifier(engine, leftIdentifier.Name, array);
                         }
@@ -162,7 +162,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                             value = jintExpression.GetValue();
                         }
 
-                        if (assignmentPattern.Left is Identifier leftIdentifier)
+                        if (assignmentPattern.Left is Esprima.Ast.Identifier leftIdentifier)
                         {
                             if (assignmentPattern.Right.IsFunctionWithName())
                             {
@@ -196,7 +196,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
                 var left = pattern.Properties[(int) i];
                 string sourceKey;
-                Identifier identifier = left.Key as Identifier;
+                Esprima.Ast.Identifier identifier = left.Key as Esprima.Ast.Identifier;
                 if (identifier == null)
                 {
                     var keyExpression = Build(engine, left.Key);
@@ -223,7 +223,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                         continue;
                     }
                     
-                    var target = assignmentPattern.Left as Identifier ?? identifier;
+                    var target = assignmentPattern.Left as Esprima.Ast.Identifier ?? identifier;
 
                     if (assignmentPattern.Right.IsFunctionWithName())
                     {
@@ -238,7 +238,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 }
                 else
                 {
-                    var target = left.Value as Identifier ?? identifier;
+                    var target = left.Value as Esprima.Ast.Identifier ?? identifier;
                     AssignToIdentifier(engine, target.Name, value);
                 }
             }

+ 5 - 4
Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs

@@ -150,7 +150,8 @@ namespace Jint.Runtime.Interpreter.Expressions
                 {
                     _left = Build(engine, (Expression) expression.Left);
                     _leftIdentifier = _left as JintIdentifierExpression;
-                    _evalOrArguments = _leftIdentifier?._expressionName == "eval" || _leftIdentifier?._expressionName == "arguments";
+                    _evalOrArguments = _leftIdentifier?.ExpressionName == KnownKeys.Eval
+                                       || _leftIdentifier?.ExpressionName == KnownKeys.Arguments;
                 }
 
                 _right = Build(engine, expression.Right);
@@ -202,7 +203,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var strict = StrictModeScope.IsStrictModeCode;
                 if (LexicalEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
                     env,
-                    left._expressionName,
+                    left.ExpressionName,
                     strict,
                     out var environmentRecord,
                     out _))
@@ -216,10 +217,10 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                     if (right._expression.IsFunctionWithName())
                     {
-                        ((FunctionInstance) rval).SetFunctionName(left._expressionName);
+                        ((FunctionInstance) rval).SetFunctionName(left.ExpressionName);
                     }
 
-                    environmentRecord.SetMutableBinding(left._expressionName, rval, strict);
+                    environmentRecord.SetMutableBinding(left.ExpressionName, rval, strict);
                     return rval;
                 }
 

+ 4 - 3
Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs

@@ -103,12 +103,13 @@ namespace Jint.Runtime.Interpreter.Expressions
                 }
             }
 
-            var func = _engine.GetValue(callee, false);
 
+            var func = _engine.GetValue(callee, false);
             var r = callee as Reference;
+
             if (_maxRecursionDepth >= 0)
             {
-                var stackItem = new CallStackElement(expression, func, r?._name ?? "anonymous function");
+                var stackItem = new CallStackElement(expression, func, r?.GetReferencedName() ?? "anonymous function");
 
                 var recursionDepth = _engine.CallStack.Push(stackItem);
 
@@ -153,7 +154,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 }
 
                 // is it a direct call to eval ? http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.1.1
-                if (r._name == "eval" && callable is EvalFunctionInstance instance)
+                if (r.GetReferencedName() == KnownKeys.Eval && callable is EvalFunctionInstance instance)
                 {
                     var value = instance.Call(thisObject, arguments, true);
                     _engine._referencePool.Return(r);

+ 4 - 3
Jint/Runtime/Interpreter/Expressions/JintExpression.cs

@@ -79,7 +79,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     return new JintFunctionExpression(engine, (IFunction) expression);
 
                 case Nodes.Identifier:
-                    return new JintIdentifierExpression(engine, (Identifier) expression);
+                    return new JintIdentifierExpression(engine, (Esprima.Ast.Identifier) expression);
 
                 case Nodes.Literal:
                     return new JintLiteralExpression(engine, (Literal) expression);
@@ -337,6 +337,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         protected static void BuildArguments(JintExpression[] jintExpressions, JsValue[] targetArray)
         {
             for (var i = 0; i < jintExpressions.Length; i++)
@@ -402,7 +403,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         protected bool TryGetIdentifierEnvironmentWithBindingValue(
-            string expressionName,
+            in Key expressionName,
             out EnvironmentRecord record,
             out JsValue value)
         {
@@ -419,7 +420,7 @@ namespace Jint.Runtime.Interpreter.Expressions
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         protected bool TryGetIdentifierEnvironmentWithBindingValue(
             bool strict,
-            string expressionName,
+            in Key expressionName,
             out EnvironmentRecord record,
             out JsValue value)
         {

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

@@ -1,4 +1,3 @@
-using Esprima.Ast;
 using Jint.Native;
 using Jint.Runtime.Environments;
 using Jint.Runtime.References;
@@ -7,18 +6,20 @@ namespace Jint.Runtime.Interpreter.Expressions
 {
     internal sealed class JintIdentifierExpression : JintExpression
     {
-        internal readonly string _expressionName;
+        private readonly Key _expressionName;
         private readonly JsValue _calculatedValue;
 
-        public JintIdentifierExpression(Engine engine, Identifier expression) : base(engine, expression)
+        public JintIdentifierExpression(Engine engine, Esprima.Ast.Identifier expression) : base(engine, expression)
         {
             _expressionName = expression.Name;
-            if (_expressionName == "undefined")
+            if (expression.Name == "undefined")
             {
                 _calculatedValue = JsValue.Undefined;
             }
         }
 
+        public ref readonly Key ExpressionName => ref _expressionName;
+
         protected override object EvaluateInternal()
         {
             var env = _engine.ExecutionContext.LexicalEnvironment;

+ 10 - 8
Jint/Runtime/Interpreter/Expressions/JintMemberExpression.cs

@@ -14,7 +14,7 @@ namespace Jint.Runtime.Interpreter.Expressions
         private readonly JintThisExpression _objectThisExpression;
 
         private readonly JintExpression _propertyExpression;
-        private readonly string _determinedPropertyNameString;
+        private readonly Key _determinedPropertyName;
 
         public JintMemberExpression(Engine engine, MemberExpression expression) : base(engine, expression)
         {
@@ -24,11 +24,11 @@ namespace Jint.Runtime.Interpreter.Expressions
 
             if (!expression.Computed)
             {
-                _determinedPropertyNameString = ((Identifier) expression.Property).Name;
+                _determinedPropertyName = ((Esprima.Ast.Identifier) expression.Property).Name;
             }
             else
             {
-                _determinedPropertyNameString = null;
+                _determinedPropertyName = "";
                 _propertyExpression = Build(engine, expression.Property);
             }
         }
@@ -41,11 +41,11 @@ namespace Jint.Runtime.Interpreter.Expressions
 
             if (_objectIdentifierExpression != null)
             {
-                baseReferenceName = _objectIdentifierExpression._expressionName;
+                baseReferenceName = _objectIdentifierExpression.ExpressionName;
                 var strict = isStrictModeCode;
                 TryGetIdentifierEnvironmentWithBindingValue(
                     strict,
-                    _objectIdentifierExpression._expressionName,
+                    _objectIdentifierExpression.ExpressionName,
                     out _,
                     out baseValue);
             }
@@ -60,7 +60,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var baseReference = _objectExpression.Evaluate();
                 if (baseReference is Reference reference)
                 {
-                    baseReferenceName = reference._name;
+                    baseReferenceName = reference.GetReferencedName();
                     baseValue = _engine.GetValue(reference, false);
                     _engine._referencePool.Return(reference);
                 }
@@ -70,11 +70,13 @@ namespace Jint.Runtime.Interpreter.Expressions
                 }
             }
 
-            var propertyNameString = _determinedPropertyNameString ?? TypeConverter.ToPropertyKey(_propertyExpression.GetValue());
+            var propertyName = !string.IsNullOrEmpty(_determinedPropertyName.Name)
+                ? _determinedPropertyName
+                : (Key) TypeConverter.ToPropertyKey(_propertyExpression.GetValue());
 
             TypeConverter.CheckObjectCoercible(_engine, baseValue, (MemberExpression) _expression, baseReferenceName);
 
-            return _engine._referencePool.Rent(baseValue, propertyNameString, isStrictModeCode);
+            return _engine._referencePool.Rent(baseValue, propertyName, isStrictModeCode);
         }
     }
 }

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

@@ -26,8 +26,16 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         private class ObjectProperty
         {
-            internal string _name;
-            internal Property _value;
+            private readonly Key _name;
+            internal readonly Property _value;
+
+            public ObjectProperty(in Key propName, Property property)
+            {
+                _name = propName;
+                _value = property;
+            }
+
+            public ref readonly Key Name => ref _name;
         }
 
         public JintObjectExpression(Engine engine, ObjectExpression expression) : base(engine, expression)
@@ -55,16 +63,12 @@ namespace Jint.Runtime.Interpreter.Expressions
                     propName = literal.Value as string ?? Convert.ToString(literal.Value, provider: null);
                 }
 
-                if (property.Key is Identifier identifier)
+                if (property.Key is Esprima.Ast.Identifier identifier)
                 {
                     propName = identifier.Name;
                 }
 
-                _properties[i] = new ObjectProperty
-                {
-                    _name = propName,
-                    _value = property
-                };
+                _properties[i] = new ObjectProperty(propName ?? "", property);
 
                 if (property.Kind == PropertyKind.Init || property.Kind == PropertyKind.Data)
                 {
@@ -95,16 +99,14 @@ namespace Jint.Runtime.Interpreter.Expressions
         private object BuildObjectFast()
         {
             var obj = _engine.Object.Construct(0);
-            var properties = _properties.Length > 1
-                ? new StringDictionarySlim<PropertyDescriptor>(_properties.Length)
-                : new StringDictionarySlim<PropertyDescriptor>();
+            var properties = new StringDictionarySlim<PropertyDescriptor>(_properties.Length);
 
             for (var i = 0; i < _properties.Length; i++)
             {
                 var objectProperty = _properties[i];
                 var valueExpression = _valueExpressions[i];
                 var propValue = valueExpression.GetValue();
-                properties[objectProperty._name] = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
+                properties[objectProperty.Name] = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
             }
 
             obj._properties = properties;
@@ -120,7 +122,9 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
                 var objectProperty = _properties[i];
                 var property = objectProperty._value;
-                var propName = objectProperty._name ?? objectProperty._value.Key.GetKey(_engine);
+                var propName = !string.IsNullOrEmpty(objectProperty.Name)
+                    ? objectProperty.Name
+                    : (Key) objectProperty._value.Key.GetKey(_engine);
 
                 PropertyDescriptor propDesc;
 
@@ -131,7 +135,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     if (expr._expression.IsFunctionWithName())
                     {
                         var functionInstance = (FunctionInstance) propValue;
-                        functionInstance.SetFunctionName(objectProperty._name);
+                        functionInstance.SetFunctionName(objectProperty.Name);
                     }
                     propDesc = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
                 }
@@ -145,7 +149,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                         _engine.ExecutionContext.LexicalEnvironment,
                         isStrictModeCode
                     );
-                    functionInstance.SetFunctionName(objectProperty._name);
+                    functionInstance.SetFunctionName(objectProperty.Name);
                     functionInstance._prototype = null;
 
                     propDesc = new GetSetPropertyDescriptor(

+ 1 - 1
Jint/Runtime/Interpreter/Expressions/JintSpreadExpression.cs

@@ -12,7 +12,7 @@ namespace Jint.Runtime.Interpreter.Expressions
         public JintSpreadExpression(Engine engine, SpreadElement expression) : base(engine, expression)
         {
             _argument = Build(engine, expression.Argument);
-            _argumentName = (expression.Argument as Identifier)?.Name;
+            _argumentName = (expression.Argument as Esprima.Ast.Identifier)?.Name;
         }
 
         protected override object EvaluateInternal()

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

@@ -54,7 +54,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     if (r.IsPropertyReference())
                     {
                         var o = TypeConverter.ToObject(_engine, r.GetBase());
-                        var jsValue = o.Delete(r._name, r._strict);
+                        var jsValue = o.Delete(r.GetReferencedName(), r._strict);
                         _engine._referencePool.Return(r);
                         return jsValue ? JsBoolean.True : JsBoolean.False;
                     }

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

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

+ 4 - 4
Jint/Runtime/Interpreter/JintFunctionDefinition.cs

@@ -49,9 +49,9 @@ namespace Jint.Runtime.Interpreter
             _body = JintStatement.Build(engine, bodyStatement);
         }
 
-        private IEnumerable<Identifier> GetParameterIdentifiers(INode parameter)
+        private IEnumerable<Esprima.Ast.Identifier> GetParameterIdentifiers(INode parameter)
         {
-            if (parameter is Identifier identifier)
+            if (parameter is Esprima.Ast.Identifier identifier)
             {
                 return new [] { identifier };
             }
@@ -73,7 +73,7 @@ namespace Jint.Runtime.Interpreter
                 return GetParameterIdentifiers(assignmentPattern.Left);
             }
 
-            return Enumerable.Empty<Identifier>();
+            return Enumerable.Empty<Esprima.Ast.Identifier>();
         }
 
         private string[] GetParameterNames(IFunction functionDeclaration)
@@ -85,7 +85,7 @@ namespace Jint.Runtime.Interpreter
             for (var i = 0; i < count; i++)
             {
                 var parameter = functionDeclarationParams[i];
-                if (parameter is Identifier id)
+                if (parameter is Esprima.Ast.Identifier id)
                 {
                     parameterNames.Add(id.Name);
                     if (onlyIdentifiers)

+ 10 - 18
Jint/Runtime/Interpreter/JintStatementList.cs

@@ -28,16 +28,17 @@ namespace Jint.Runtime.Interpreter
 
         private void Initialize()
         {
-            _jintStatements = new Pair[_statements.Count];
-            for (var i = 0; i < _jintStatements.Length; i++)
+            var jintStatements = new Pair[_statements.Count];
+            for (var i = 0; i < jintStatements.Length; i++)
             {
-                var statement = JintStatement.Build(_engine, (Statement) _statements[i]);
-                _jintStatements[i] = new Pair
+                var esprimaStatement = (Statement) _statements[i];
+                jintStatements[i] = new Pair
                 {
-                    Statement = statement,
-                    Value = JintStatement.FastResolve(_statements[i])
+                    Statement = JintStatement.Build(_engine, esprimaStatement),
+                    Value = JintStatement.FastResolve(esprimaStatement)
                 };
             }
+            _jintStatements = jintStatements;
         }
 
         public Completion Execute()
@@ -62,21 +63,17 @@ namespace Jint.Runtime.Interpreter
             Completion sl = c;
             try
             {
-                var statements = _jintStatements;
-                for (var i = 0; i < (uint) statements.Length; i++)
+                foreach (var pair in _jintStatements)
                 {
-                    var pair = statements[i];
                     s = pair.Statement;
                     c = pair.Value ?? s.Execute();
                     if (c.Type != CompletionType.Normal)
                     {
-                        var executeStatementList = new Completion(
+                        return new Completion(
                             c.Type,
                             c.Value ?? sl.Value,
                             c.Identifier,
                             c.Location);
-
-                        return executeStatementList;
                     }
 
                     sl = c;
@@ -105,11 +102,6 @@ namespace Jint.Runtime.Interpreter
                 c = new Completion(CompletionType.Throw, error, null, s.Location);
             }
             return new Completion(c.Type, c.GetValueOrDefault(), c.Identifier, c.Location);
-        }
-
-        internal Completion? FastResolve()
-        {
-            return _statements.Count == 1 ? JintStatement.FastResolve(_statements[0]) : null;
-        }
+        } 
     }
 }

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

@@ -20,7 +20,7 @@ namespace Jint.Runtime.Interpreter.Statements
         {
             if (_statement.Left.Type == Nodes.VariableDeclaration)
             {
-                _identifier = JintExpression.Build(engine, (Identifier) ((VariableDeclaration) _statement.Left).Declarations[0].Id);
+                _identifier = JintExpression.Build(engine, (Esprima.Ast.Identifier) ((VariableDeclaration) _statement.Left).Declarations[0].Id);
             }
             else if (_statement.Left.Type == Nodes.MemberExpression)
             {
@@ -28,7 +28,7 @@ namespace Jint.Runtime.Interpreter.Statements
             }
             else
             {
-                _identifier = JintExpression.Build(engine, (Identifier) _statement.Left);
+                _identifier = JintExpression.Build(engine, (Esprima.Ast.Identifier) _statement.Left);
             }
 
             _body = Build(engine, _statement.Body);

+ 1 - 1
Jint/Runtime/Interpreter/Statements/JintTryStatement.cs

@@ -19,7 +19,7 @@ namespace Jint.Runtime.Interpreter.Statements
             if (_statement.Handler != null)
             {
                 _catch = Build(engine, _statement.Handler.Body);
-                _catchParamName = ((Identifier) _statement.Handler.Param).Name;
+                _catchParamName = ((Esprima.Ast.Identifier) _statement.Handler.Param).Name;
             }
 
             if (statement.Finalizer != null)

+ 3 - 4
Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs

@@ -56,7 +56,8 @@ namespace Jint.Runtime.Interpreter.Statements
                     Left = left,
                     LeftPattern = bindingPattern,
                     LeftIdentifier = leftIdentifier,
-                    EvalOrArguments = leftIdentifier?._expressionName == "eval" || leftIdentifier?._expressionName == "arguments",
+                    EvalOrArguments = leftIdentifier?.ExpressionName == KnownKeys.Eval
+                                      || leftIdentifier?.ExpressionName == KnownKeys.Arguments,
                     Init = init
                 };
             }
@@ -64,10 +65,8 @@ namespace Jint.Runtime.Interpreter.Statements
 
         protected override Completion ExecuteInternal()
         {
-            var declarations = _declarations;
-            for (var i = 0; i < (uint) declarations.Length; i++)
+            foreach (var declaration in _declarations)
             {
-                var declaration = declarations[i];
                 if (declaration.Init != null)
                 {
                     if (declaration.LeftPattern != null)

+ 27 - 0
Jint/Runtime/KnownKeys.cs

@@ -0,0 +1,27 @@
+namespace Jint.Runtime
+{
+    internal static class KnownKeys
+    {
+        private static readonly Key _arguments = "arguments";
+        private static readonly Key _caller = "caller";
+        private static readonly Key _callee = "callee";
+        private static readonly Key _constructor = "constructor";
+        private static readonly Key _eval = "eval";
+        private static readonly Key _infinity = "Infinity";
+        private static readonly Key _length = "length";
+        private static readonly Key _name = "name";
+        private static readonly Key _prototype = "prototype";
+        private static readonly Key _size = "size";
+
+        internal static ref readonly Key Arguments => ref _arguments;
+        internal static ref readonly Key Caller => ref _caller;
+        internal static ref readonly Key Callee => ref _callee;
+        internal static ref readonly Key Constructor => ref _constructor;
+        internal static ref readonly Key Eval => ref _eval;
+        internal static ref readonly Key Infinity => ref _infinity;
+        internal static ref readonly Key Length => ref _length;
+        internal static ref readonly Key Name => ref _name;
+        internal static ref readonly Key Prototype => ref _prototype;
+        internal static ref readonly Key Size => ref _size;
+    }
+}

+ 43 - 10
Jint/Runtime/RefStack.cs

@@ -7,13 +7,13 @@ namespace Jint.Runtime
     internal sealed class ExecutionContextStack
     {
         private ExecutionContext[] _array;
-        private uint _size;
+        private int _size;
 
-        private const int DefaultCapacity = 4;
+        private const int DefaultCapacity = 2;
 
-        public ExecutionContextStack()
+        public ExecutionContextStack(int capacity)
         {
-            _array = new ExecutionContext[4];
+            _array = new ExecutionContext[capacity];
             _size = 0;
         }
 
@@ -40,17 +40,50 @@ namespace Jint.Runtime
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public void Push(in ExecutionContext item)
         {
-            if (_size == (uint) _array.Length)
+            if (_size == _array.Length)
             {
-                var newSize = 2 * _array.Length;
-                var newArray = new ExecutionContext[newSize];
-                Array.Copy(_array, 0, newArray, 0, _size);
-                _array = newArray;
+                EnsureCapacity(_size + 1);
             }
-
             _array[_size++] = item;
         }
 
+        private void EnsureCapacity(int min)
+        {
+            if (_array.Length < min)
+            {
+                int newCapacity = _array.Length == 0
+                    ? DefaultCapacity
+                    : _array.Length * 2;
+
+                if (newCapacity < min)
+                {
+                    newCapacity = min;
+                }
+                Resize(newCapacity);
+            }
+        }
+
+        private void Resize(int value)
+        {
+            if (value != _array.Length)
+            {
+                if (value > 0)
+                {
+                    var newItems = new ExecutionContext[value];
+                    if (_size > 0)
+                    {
+                        Array.Copy(_array, 0, newItems, 0, _size);
+                    }
+
+                    _array = newItems;
+                }
+                else
+                {
+                    _array = ArrayExt.Empty<ExecutionContext>();
+                }
+            }
+        }
+
         public void ReplaceTopLexicalEnvironment(LexicalEnvironment newEnv)
         {
             _array[_size - 1] = _array[_size - 1].UpdateLexicalEnvironment(newEnv);

+ 6 - 6
Jint/Runtime/References/Reference.cs

@@ -12,10 +12,10 @@ namespace Jint.Runtime.References
     public sealed class Reference
     {
         internal JsValue _baseValue;
-        internal string _name;
+        private Key _name;
         internal bool _strict;
 
-        public Reference(JsValue baseValue, string name, bool strict)
+        public Reference(JsValue baseValue, in Key name, bool strict)
         {
             _baseValue = baseValue;
             _name = name;
@@ -28,9 +28,9 @@ namespace Jint.Runtime.References
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public string GetReferencedName()
+        public ref readonly Key GetReferencedName()
         {
-            return _name;
+            return ref _name;
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -59,7 +59,7 @@ namespace Jint.Runtime.References
                    || _baseValue._type == Types.Object && !(_baseValue is EnvironmentRecord);
         }
 
-        internal Reference Reassign(JsValue baseValue, string name, bool strict)
+        internal Reference Reassign(JsValue baseValue, in Key name, bool strict)
         {
             _baseValue = baseValue;
             _name = name;
@@ -70,7 +70,7 @@ namespace Jint.Runtime.References
 
         internal void AssertValid(Engine engine)
         {
-            if (_strict && (_name == "eval" || _name == "arguments") && _baseValue is EnvironmentRecord)
+            if (_strict && (_name == KnownKeys.Eval || _name == KnownKeys.Arguments) && _baseValue is EnvironmentRecord)
             {
                 ExceptionHelper.ThrowSyntaxError(engine);
             }

+ 22 - 8
Jint/Runtime/TypeConverter.cs

@@ -55,7 +55,7 @@ namespace Jint.Runtime
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static JsValue ToPrimitive(JsValue input, Types preferredType = Types.None)
         {
-            if (input._type > Types.None && input._type < Types.Object) 
+            if (input._type > Types.None && input._type < Types.Object)
             {
                 return input;
             }
@@ -89,13 +89,18 @@ namespace Jint.Runtime
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.3
         /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static double ToNumber(JsValue o)
+        {
+            return o._type == Types.Number
+                ? ((JsNumber) o)._value
+                : ToNumberUnlikely(o);
+        }
+
+        private static double ToNumberUnlikely(JsValue o)
         {
             switch (o._type)
             {
-                // check number first as this is what is usually expected
-                case Types.Number:
-                    return ((JsNumber) o)._value;
                 case Types.Undefined:
                     return double.NaN;
                 case Types.Null:
@@ -367,12 +372,21 @@ namespace Jint.Runtime
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/6.0/#sec-tostring
         /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static string ToString(JsValue o)
+        {
+            if (o._type == Types.String)
+            {
+                return o.AsStringWithoutTypeCheck();
+            }
+
+            return ToStringUnlikely(o);
+        }
+
+        private static string ToStringUnlikely(JsValue o)
         {
             switch (o._type)
             {
-                case Types.String:
-                    return o.AsStringWithoutTypeCheck();
                 case Types.Boolean:
                     return ((JsBoolean) o)._value ? "true" : "false";
                 case Types.Number:
@@ -467,9 +481,9 @@ namespace Jint.Runtime
         }
 
         private static void ThrowTypeError(
-            Engine engine, 
+            Engine engine,
             JsValue o,
-            MemberExpression expression, 
+            MemberExpression expression,
             object baseReference)
         {
             ThrowTypeError(engine, o, expression, (baseReference as Reference)?.GetReferencedName());