Ver Fonte

Optimize DeclarativeEnvironmentRecord (#518)

Marko Lahma há 7 anos atrás
pai
commit
f9e65d3340
34 ficheiros alterados com 1209 adições e 673 exclusões
  1. 63 57
      Jint/Engine.cs
  2. 1 1
      Jint/Jint.csproj
  3. 12 8
      Jint/Native/Argument/ArgumentsInstance.cs
  4. 2 2
      Jint/Native/Array/ArrayConstructor.cs
  5. 16 15
      Jint/Native/Array/ArrayInstance.cs
  6. 12 12
      Jint/Native/Array/ArrayPrototype.cs
  7. 3 2
      Jint/Native/Function/EvalFunctionInstance.cs
  8. 12 8
      Jint/Native/Function/FunctionInstance.cs
  9. 2 5
      Jint/Native/Function/FunctionPrototype.cs
  10. 3 3
      Jint/Native/Function/ScriptFunctionInstance.cs
  11. 5 4
      Jint/Native/JsNumber.cs
  12. 17 10
      Jint/Native/JsString.cs
  13. 1 1
      Jint/Native/Json/JsonSerializer.cs
  14. 2 2
      Jint/Native/Object/ObjectConstructor.cs
  15. 33 38
      Jint/Native/Object/ObjectInstance.cs
  16. 4 4
      Jint/Native/String/StringPrototype.cs
  17. 5 8
      Jint/Pooling/ArgumentsInstancePool.cs
  18. 1 1
      Jint/Runtime/Descriptors/PropertyDescriptor.cs
  19. 1 1
      Jint/Runtime/Descriptors/Specialized/FieldInfoDescriptor.cs
  20. 1 1
      Jint/Runtime/Descriptors/Specialized/IndexDescriptor.cs
  21. 1 1
      Jint/Runtime/Descriptors/Specialized/PropertyInfoDescriptor.cs
  22. 10 3
      Jint/Runtime/Environments/Binding.cs
  23. 146 111
      Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs
  24. 19 3
      Jint/Runtime/Environments/EnvironmentRecord.cs
  25. 5 5
      Jint/Runtime/Environments/LexicalEnvironment.cs
  26. 1 1
      Jint/Runtime/Environments/ObjectEnvironmentRecord.cs
  27. 21 24
      Jint/Runtime/ExpressionIntepreter.cs
  28. 0 165
      Jint/Runtime/MruPropertyCache.cs
  29. 0 164
      Jint/Runtime/MruPropertyCache2.cs
  30. 2 2
      Jint/Runtime/RefStack.cs
  31. 2 7
      Jint/Runtime/References/Reference.cs
  32. 3 3
      Jint/Runtime/StatementInterpreter.cs
  33. 802 0
      Jint/Runtime/StructDictionary.cs
  34. 1 1
      Jint/Runtime/TypeConverter.cs

+ 63 - 57
Jint/Engine.cs

@@ -53,6 +53,9 @@ namespace Jint
         private readonly long _memoryLimit;
         private readonly bool _runBeforeStatementChecks;
         private readonly IReferenceResolver _referenceResolver;
+        internal readonly ReferencePool _referencePool;
+        internal readonly ArgumentsInstancePool _argumentsInstancePool;
+        internal readonly JsValueArrayPool _jsValueArrayPool;
         
         public ITypeConverter ClrTypeConverter;
 
@@ -184,9 +187,9 @@ namespace Jint
                                         || _memoryLimit > 0 
                                         || _isDebugMode;
 
-            ReferencePool = new ReferencePool();
-            ArgumentsInstancePool = new ArgumentsInstancePool(this);
-            JsValueArrayPool = new JsValueArrayPool();
+            _referencePool = new ReferencePool();
+            _argumentsInstancePool = new ArgumentsInstancePool(this);
+            _jsValueArrayPool = new JsValueArrayPool();
 
             Eval = new EvalFunctionInstance(this, System.Array.Empty<string>(), LexicalEnvironment.NewDeclarativeEnvironment(this, ExecutionContext.LexicalEnvironment), StrictModeScope.IsStrictModeCode);
             Global.FastAddProperty("eval", Eval, true, false, true);
@@ -241,15 +244,6 @@ namespace Jint
 
         internal Options Options { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; }
 
-        internal ReferencePool ReferencePool
-        {
-            [MethodImpl(MethodImplOptions.AggressiveInlining)]
-            get;
-        }
-
-        internal ArgumentsInstancePool ArgumentsInstancePool { get; }
-        internal JsValueArrayPool JsValueArrayPool { get; }
-
         #region Debugger
         public delegate StepMode DebugStepDelegate(object sender, DebugInformation e);
         public delegate StepMode BreakDelegate(object sender, DebugInformation e);
@@ -591,7 +585,6 @@ namespace Jint
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.7.1
         /// </summary>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public JsValue GetValue(object value)
         {
             return GetValue(value, false);
@@ -632,7 +625,7 @@ namespace Jint
                 var referencedName = reference._name;
                 if (returnReferenceToPool)
                 {
-                    ReferencePool.Return(reference);
+                    _referencePool.Return(reference);
                 }
                 if (!(reference._baseValue._type != Types.Object && reference._baseValue._type != Types.None))
                 {
@@ -675,7 +668,7 @@ namespace Jint
 
             if (returnReferenceToPool)
             {
-                ReferencePool.Return(reference);
+                _referencePool.Return(reference);
             }
 
             return bindingValue;
@@ -688,21 +681,21 @@ namespace Jint
         /// <param name="value"></param>
         public void PutValue(Reference reference, JsValue value)
         {
-            if (reference.IsUnresolvableReference())
+            if (reference._baseValue._type == Types.Undefined)
             {
                 if (reference._strict)
                 {
                     ExceptionHelper.ThrowReferenceError(this);
                 }
 
-                Global.Put(reference.GetReferencedName(), value, false);
+                Global.Put(reference._name, value, false);
             }
             else if (reference.IsPropertyReference())
             {
                 var baseValue = reference._baseValue;
-                if (!reference.HasPrimitiveBase())
+                if (reference._baseValue._type == Types.Object || reference._baseValue._type == Types.None)
                 {
-                    baseValue.AsObject().Put(reference._name, value, reference._strict);
+                    ((ObjectInstance) baseValue).Put(reference._name, value, reference._strict);
                 }
                 else
                 {
@@ -712,7 +705,7 @@ namespace Jint
             else
             {
                 var baseValue = reference._baseValue;
-                ((EnvironmentRecord) baseValue).SetMutableBinding(reference.GetReferencedName(), value, reference._strict);
+                ((EnvironmentRecord) baseValue).SetMutableBinding(reference._name, value, reference._strict);
             }
         }
 
@@ -805,14 +798,14 @@ namespace Jint
         {
             var callable = value as ICallable ?? ExceptionHelper.ThrowArgumentException<ICallable>("Can only invoke functions");
 
-            var items = JsValueArrayPool.RentArray(arguments.Length);
+            var items = _jsValueArrayPool.RentArray(arguments.Length);
             for (int i = 0; i < arguments.Length; ++i)
             {
                 items[i] = JsValue.FromObject(this, arguments[i]);
             }
 
             var result = callable.Call(JsValue.FromObject(this, thisObj), items);
-            JsValueArrayPool.ReturnArray(items);
+            _jsValueArrayPool.ReturnArray(items);
 
             return result;
         }
@@ -843,9 +836,9 @@ namespace Jint
         {
             AssertNotNullOrEmpty(nameof(propertyName), propertyName);
 
-            var reference = ReferencePool.Rent(scope, propertyName, _isStrict);
-            var jsValue = GetValue(reference);
-            ReferencePool.Return(reference);
+            var reference = _referencePool.Rent(scope, propertyName, _isStrict);
+            var jsValue = GetValue(reference, false);
+            _referencePool.Return(reference);
             return jsValue;
         }
 
@@ -857,7 +850,7 @@ namespace Jint
             FunctionInstance functionInstance,
             JsValue[] arguments)
         {
-            var env = ExecutionContext.VariableEnvironment.Record;
+            var env = ExecutionContext.VariableEnvironment._record;
             bool configurableBindings = declarationBindingType == DeclarationBindingType.EvalCode;
             var strict = StrictModeScope.IsStrictModeCode;
 
@@ -865,7 +858,7 @@ namespace Jint
             bool canReleaseArgumentsInstance = false;
             if (declarationBindingType == DeclarationBindingType.FunctionCode)
             {
-                var argsObj = ArgumentsInstancePool.Rent(functionInstance, functionInstance.FormalParameters, arguments, env, strict);
+                var argsObj = _argumentsInstancePool.Rent(functionInstance, functionInstance._formalParameters, arguments, env, strict);
                 canReleaseArgumentsInstance = true;
 
                 if (!ReferenceEquals(der, null))
@@ -875,7 +868,7 @@ namespace Jint
                 else
                 {
                     // slow path
-                    var parameters = functionInstance.FormalParameters;
+                    var parameters = functionInstance._formalParameters;
                     for (var i = 0; i < parameters.Length; i++)
                     {
                         var argName = parameters[i];
@@ -892,6 +885,47 @@ namespace Jint
                 }
             }
 
+            if (functionDeclarations.Count > 0)
+            {
+                AddFunctionDeclarations(functionDeclarations, env, configurableBindings, strict);
+            }
+
+            if (variableDeclarations.Count == 0)
+            {
+                return canReleaseArgumentsInstance;
+            }
+
+            // process all variable declarations in the current parser scope
+            if (!ReferenceEquals(der, null))
+            {
+                der.AddVariableDeclarations(variableDeclarations);
+            }
+            else
+            {
+                // slow path
+                var variableDeclarationsCount = variableDeclarations.Count;
+                for (var i = 0; i < variableDeclarationsCount; i++)
+                {
+                    var variableDeclaration = variableDeclarations[i];
+                    var declarationsCount = variableDeclaration.Declarations.Count;
+                    for (var j = 0; j < declarationsCount; j++)
+                    {
+                        var d = variableDeclaration.Declarations[j];
+                        var dn = ((Identifier) d.Id).Name;
+                        var varAlreadyDeclared = env.HasBinding(dn);
+                        if (!varAlreadyDeclared)
+                        {
+                            env.CreateMutableBinding(dn, Undefined.Instance);
+                        }
+                    }
+                }
+            }
+
+            return canReleaseArgumentsInstance;
+        }
+
+        private void AddFunctionDeclarations(List<FunctionDeclaration> functionDeclarations, EnvironmentRecord env, bool configurableBindings, bool strict)
+        {
             var functionDeclarationsCount = functionDeclarations.Count;
             for (var i = 0; i < functionDeclarationsCount; i++)
             {
@@ -905,7 +939,7 @@ namespace Jint
                 }
                 else
                 {
-                    if (ReferenceEquals(env, GlobalEnvironment.Record))
+                    if (ReferenceEquals(env, GlobalEnvironment._record))
                     {
                         var go = Global;
                         var existingProp = go.GetProperty(fn);
@@ -932,34 +966,6 @@ namespace Jint
 
                 env.SetMutableBinding(fn, fo, strict);
             }
-
-            // process all variable declarations in the current parser scope
-            if (!ReferenceEquals(der, null))
-            {
-                der.AddVariableDeclarations(variableDeclarations);
-            }
-            else
-            {
-                // slow path
-                var variableDeclarationsCount = variableDeclarations.Count;
-                for (var i = 0; i < variableDeclarationsCount; i++)
-                {
-                    var variableDeclaration = variableDeclarations[i];
-                    var declarationsCount = variableDeclaration.Declarations.Count;
-                    for (var j = 0; j < declarationsCount; j++)
-                    {
-                        var d = variableDeclaration.Declarations[j];
-                        var dn = ((Identifier) d.Id).Name;
-                        var varAlreadyDeclared = env.HasBinding(dn);
-                        if (!varAlreadyDeclared)
-                        {
-                            env.CreateMutableBinding(dn, Undefined.Instance);
-                        }
-                    }
-                }
-            }
-
-            return canReleaseArgumentsInstance;
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]

+ 1 - 1
Jint/Jint.csproj

@@ -9,4 +9,4 @@
   <ItemGroup>
     <PackageReference Include="Esprima" Version="1.0.0-beta-1036" />
   </ItemGroup>
-</Project>
+</Project>

+ 12 - 8
Jint/Native/Argument/ArgumentsInstance.cs

@@ -29,21 +29,25 @@ namespace Jint.Native.Argument
         {
         }
 
-        internal void Prepare(FunctionInstance func, string[] names, JsValue[] args, EnvironmentRecord env, bool strict)
+        internal void Prepare(
+            FunctionInstance func, 
+            string[] names, 
+            JsValue[] args, 
+            EnvironmentRecord env, 
+            bool strict)
         {
-            Clear();
-
             _func = func;
             _names = names;
             _args = args;
             _env = env;
             _strict = strict;
 
+            _properties?.Clear();
+            _intrinsicProperties?.Clear();
+
             _initialized = false;
         }
 
-        public bool Strict { get; set; }
-
         protected override void EnsureInitialized()
         {
             if (_initialized)
@@ -105,7 +109,7 @@ namespace Jint.Native.Argument
         {
             EnsureInitialized();
 
-            if (!Strict && !ReferenceEquals(ParameterMap, null))
+            if (!_strict && !ReferenceEquals(ParameterMap, null))
             {
                 var desc = base.GetOwnProperty(propertyName);
                 if (desc == PropertyDescriptor.Undefined)
@@ -170,7 +174,7 @@ namespace Jint.Native.Argument
         {
             EnsureInitialized();
 
-            if (!Strict && !ReferenceEquals(ParameterMap, null))
+            if (!_strict && !ReferenceEquals(ParameterMap, null))
             {
                 var map = ParameterMap;
                 var isMapped = map.GetOwnProperty(propertyName);
@@ -214,7 +218,7 @@ namespace Jint.Native.Argument
         {
             EnsureInitialized();
 
-            if (!Strict && !ReferenceEquals(ParameterMap, null))
+            if (!_strict && !ReferenceEquals(ParameterMap, null))
             {
                 var map = ParameterMap;
                 var isMapped = map.GetOwnProperty(propertyName);

+ 2 - 2
Jint/Native/Array/ArrayConstructor.cs

@@ -105,14 +105,14 @@ namespace Jint.Native.Array
                 if (objectWrapper.Target is IEnumerable enumerable)
                 {
                     var jsArray = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
-                    var tempArray = Engine.JsValueArrayPool.RentArray(1);
+                    var tempArray = _engine._jsValueArrayPool.RentArray(1);
                     foreach (var item in enumerable)
                     {
                         var jsItem = FromObject(Engine, item);
                         tempArray[0] = jsItem;
                         Engine.Array.PrototypeObject.Push(jsArray, tempArray);
                     }
-                    Engine.JsValueArrayPool.ReturnArray(tempArray);
+                    _engine._jsValueArrayPool.ReturnArray(tempArray);
                     return jsArray;
                 }
             }

+ 16 - 15
Jint/Native/Array/ArrayInstance.cs

@@ -394,7 +394,7 @@ namespace Jint.Native.Array
             {
                 return index < GetLength()
                        && (_sparse == null || _sparse.ContainsKey(index))
-                       && (_dense == null || (index < _dense.Length && _dense[index] != null));
+                       && (_dense == null || (index < (uint) _dense.Length && _dense[index] != null));
             }
 
             if (p == PropertyNameLength)
@@ -535,7 +535,7 @@ namespace Jint.Native.Array
         {
             if (_dense != null)
             {
-                if (index < _dense.Length)
+                if (index < (uint) _dense.Length)
                 {
                     _dense[index] = null;
                     return true;
@@ -555,7 +555,7 @@ namespace Jint.Native.Array
             if (_dense != null)
             {
                 descriptor = null;
-                if (index < _dense.Length)
+                if (index < (uint) _dense.Length)
                 {
                     descriptor = _dense[index];
                 }
@@ -570,7 +570,7 @@ namespace Jint.Native.Array
         internal void WriteArrayValue(uint index, PropertyDescriptor desc)
         {
             // calculate eagerly so we know if we outgrow
-            var newSize = _dense != null && index >= _dense.Length
+            var newSize = _dense != null && index >= (uint) _dense.Length
                 ? System.Math.Max(index, System.Math.Max(_dense.Length, 2)) * 2
                 : 0;
 
@@ -581,7 +581,8 @@ namespace Jint.Native.Array
 
             if (canUseDense)
             {
-                if (index >= _dense.Length)
+                var temp = _dense;
+                if (index >= (uint) temp.Length)
                 {
                     EnsureCapacity((uint) newSize);
                 }
@@ -602,7 +603,7 @@ namespace Jint.Native.Array
         {
             _sparse = new Dictionary<uint, PropertyDescriptor>(_dense.Length <= 1024 ? _dense.Length : 0);
             // need to move data
-            for (uint i = 0; i < _dense.Length; ++i)
+            for (uint i = 0; i < (uint) _dense.Length; ++i)
             {
                 if (_dense[i] != null)
                 {
@@ -616,7 +617,7 @@ namespace Jint.Native.Array
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal void EnsureCapacity(uint capacity)
         {
-            if (capacity > _dense.Length)
+            if (capacity > (uint) _dense.Length)
             {
                 // need to grow
                 var newArray = new PropertyDescriptor[capacity];
@@ -670,7 +671,7 @@ namespace Jint.Native.Array
                 }
                 else
                 {
-                    DefineOwnProperty(TypeConverter.ToString(n), desc, true);
+                    DefineOwnProperty(TypeConverter.ToString((uint) n), desc, true);
                 }
                 n++;
             }
@@ -697,17 +698,17 @@ namespace Jint.Native.Array
 
             var callable = GetCallable(callbackfn);
             var a = Engine.Array.ConstructFast(len);
-            var args = Engine.JsValueArrayPool.RentArray(3);
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = this;
             for (uint k = 0; k < len; k++)
             {
                 if (TryGetValue(k, out var kvalue))
                 {
                     args[0] = kvalue;
                     args[1] = k;
-                    args[2] = this;
                     var mappedValue = callable.Call(thisArg, args);
                     var desc = new PropertyDescriptor(mappedValue, PropertyFlag.ConfigurableEnumerableWritable);
-                    if (a._dense != null && k < a._dense.Length)
+                    if (a._dense != null && k < (uint) a._dense.Length)
                     {
                         a._dense[k] = desc;
                     }
@@ -718,7 +719,7 @@ namespace Jint.Native.Array
                 }
             }
 
-            Engine.JsValueArrayPool.ReturnArray(args);
+            _engine._jsValueArrayPool.ReturnArray(args);
             return a;
         }
         
@@ -740,14 +741,14 @@ namespace Jint.Native.Array
             var thisArg = arguments.At(1);
             var callable = GetCallable(callbackfn);
 
-            var args = Engine.JsValueArrayPool.RentArray(3);
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = this;
             for (uint k = 0; k < len; k++)
             {
                 if (TryGetValue(k, out var kvalue))
                 {
                     args[0] = kvalue;
                     args[1] = k;
-                    args[2] = this;
                     var testResult = callable.Call(thisArg, args);
                     if (TypeConverter.ToBoolean(testResult))
                     {
@@ -758,7 +759,7 @@ namespace Jint.Native.Array
                 }
             }
 
-            Engine.JsValueArrayPool.ReturnArray(args);
+            _engine._jsValueArrayPool.ReturnArray(args);
 
             index = 0;
             value = Undefined;

+ 12 - 12
Jint/Native/Array/ArrayPrototype.cs

@@ -176,14 +176,14 @@ namespace Jint.Native.Array
             var a = Engine.Array.ConstructFast(0);
 
             uint to = 0;
-            var args = Engine.JsValueArrayPool.RentArray(3);
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = o.Target;
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
                 {
                     args[0] = kvalue;
                     args[1] = k;
-                    args[2] = o.Target;
                     var selected = callable.Call(thisArg, args);
                     if (TypeConverter.ToBoolean(selected))
                     {
@@ -194,7 +194,7 @@ namespace Jint.Native.Array
             }
 
             a.SetLength(to);
-            Engine.JsValueArrayPool.ReturnArray(args);
+            _engine._jsValueArrayPool.ReturnArray(args);
 
             return a;
         }
@@ -214,19 +214,19 @@ namespace Jint.Native.Array
             var callable = GetCallable(callbackfn);
 
             var a = Engine.Array.ConstructFast(len);
-            var args = Engine.JsValueArrayPool.RentArray(3);
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = o.Target;
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
                 {
                     args[0] = kvalue;
                     args[1] = k;
-                    args[2] = o.Target;
                     var mappedValue = callable.Call(thisArg, args);
                     a.SetIndexValue(k, mappedValue, updateLength: false);
                 }
             }
-            Engine.JsValueArrayPool.ReturnArray(args);
+            _engine._jsValueArrayPool.ReturnArray(args);
             return a;
         }
 
@@ -240,18 +240,18 @@ namespace Jint.Native.Array
 
             var callable = GetCallable(callbackfn);
 
-            var args = Engine.JsValueArrayPool.RentArray(3);
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = o.Target;
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
                 {
                     args[0] = kvalue;
                     args[1] = k;
-                    args[2] = o.Target;
                     callable.Call(thisArg, args);
                 }
             }
-            Engine.JsValueArrayPool.ReturnArray(args);
+            _engine._jsValueArrayPool.ReturnArray(args);
 
             return Undefined;
         }
@@ -272,14 +272,14 @@ namespace Jint.Native.Array
 
             var callable = GetCallable(callbackfn);
 
-            var args = Engine.JsValueArrayPool.RentArray(3);
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = o.Target;
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
                 {
                     args[0] = kvalue;
                     args[1] = k;
-                    args[2] = o.Target;
                     var testResult = callable.Call(thisArg, args);
                     if (false == TypeConverter.ToBoolean(testResult))
                     {
@@ -287,7 +287,7 @@ namespace Jint.Native.Array
                     }
                 }
             }
-            Engine.JsValueArrayPool.ReturnArray(args);
+            _engine._jsValueArrayPool.ReturnArray(args);
 
             return JsBoolean.True;
         }

+ 3 - 2
Jint/Native/Function/EvalFunctionInstance.cs

@@ -48,9 +48,10 @@ namespace Jint.Native.Function
                                 Engine.EnterExecutionContext(Engine.GlobalEnvironment, Engine.GlobalEnvironment, Engine.Global);
                             }
 
+                            var lexicalEnvironment = Engine.ExecutionContext.LexicalEnvironment;
                             if (StrictModeScope.IsStrictModeCode)
                             {
-                                strictVarEnv = LexicalEnvironment.NewDeclarativeEnvironment(Engine, Engine.ExecutionContext.LexicalEnvironment);
+                                strictVarEnv = LexicalEnvironment.NewDeclarativeEnvironment(Engine, lexicalEnvironment);
                                 Engine.EnterExecutionContext(strictVarEnv, strictVarEnv, Engine.ExecutionContext.ThisBinding);
                             }
 
@@ -66,7 +67,7 @@ namespace Jint.Native.Function
 
                             // we can safely release arguments if they don't escape the scope
                             if (argumentInstanceRented
-                                && Engine.ExecutionContext.LexicalEnvironment?.Record is DeclarativeEnvironmentRecord der
+                                && lexicalEnvironment?._record is DeclarativeEnvironmentRecord der
                                 && !(result.Value is ArgumentsInstance))
                             {
                                 der.ReleaseArguments();

+ 12 - 8
Jint/Native/Function/FunctionInstance.cs

@@ -15,7 +15,10 @@ namespace Jint.Native.Function
         private const string PropertyNameLength = "length";
         private const int PropertyNameLengthLength = 6;
         protected PropertyDescriptor _length;
-        
+        protected readonly LexicalEnvironment _scope;
+        protected internal readonly string[] _formalParameters;
+        private readonly bool _strict;
+
         protected FunctionInstance(Engine engine, string[] parameters, LexicalEnvironment scope, bool strict)
             : this(engine, parameters, scope, strict, objectClass: "Function")
         {
@@ -29,9 +32,9 @@ namespace Jint.Native.Function
             in string objectClass)
             : base(engine, objectClass)
         {
-            FormalParameters = parameters;
-            Scope = scope;
-            Strict = strict;
+            _formalParameters = parameters;
+            _scope = scope;
+            _strict = strict;
         }
 
         /// <summary>
@@ -42,10 +45,11 @@ namespace Jint.Native.Function
         /// <returns></returns>
         public abstract JsValue Call(JsValue thisObject, JsValue[] arguments);
 
-        public LexicalEnvironment Scope { get; }
+        public LexicalEnvironment Scope => _scope;
+
+        public string[] FormalParameters => _formalParameters;
 
-        public string[] FormalParameters { get; }
-        public bool Strict { get; }
+        public bool Strict => _strict;
 
         public virtual bool HasInstance(JsValue v)
         {
@@ -95,7 +99,7 @@ namespace Jint.Native.Function
 
             if (propertyName.Length == 6
                 && propertyName == "caller"
-                && ((v.As<FunctionInstance>()?.Strict).GetValueOrDefault()))
+                && ((v.As<FunctionInstance>()?._strict).GetValueOrDefault()))
             {
                 ExceptionHelper.ThrowTypeError(_engine);
             }

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

@@ -108,10 +108,7 @@ namespace Jint.Native.Function
             var len = ((JsNumber) argArrayObj.Get("length"))._value;
             uint n = TypeConverter.ToUint32(len);
 
-            var argList = n < 10 
-                ? Engine.JsValueArrayPool.RentArray((int) n)
-                : new JsValue[n];
-            
+            var argList = _engine._jsValueArrayPool.RentArray((int) n);
             for (int index = 0; index < n; index++)
             {
                 string indexName = TypeConverter.ToString(index);
@@ -120,7 +117,7 @@ namespace Jint.Native.Function
             }
 
             var result = func.Call(thisArg, argList);
-            Engine.JsValueArrayPool.ReturnArray(argList);
+            _engine._jsValueArrayPool.ReturnArray(argList);
             
             return result;
         }

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

@@ -36,7 +36,7 @@ namespace Jint.Native.Function
             Extensible = true;
             Prototype = _engine.Function.PrototypeObject;
 
-            _length = new PropertyDescriptor(JsNumber.Create(FormalParameters.Length), PropertyFlag.AllForbidden);
+            _length = new PropertyDescriptor(JsNumber.Create(_formalParameters.Length), PropertyFlag.AllForbidden);
 
             var proto = new ObjectInstanceWithConstructor(engine, this)
             {
@@ -163,7 +163,7 @@ namespace Jint.Native.Function
                     thisBinding = thisArg;
                 }
 
-                var localEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, Scope);
+                var localEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, _scope);
 
                 _engine.EnterExecutionContext(localEnv, localEnv, thisBinding);
 
@@ -182,7 +182,7 @@ namespace Jint.Native.Function
                     
                     // we can safely release arguments if they don't escape the scope
                     if (argumentInstanceRented
-                        && _engine.ExecutionContext.LexicalEnvironment?.Record is DeclarativeEnvironmentRecord der
+                        && _engine.ExecutionContext.LexicalEnvironment?._record is DeclarativeEnvironmentRecord der
                         && !(result.Value is ArgumentsInstance))
                     {
                         der.ReleaseArguments();

+ 5 - 4
Jint/Native/JsNumber.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Globalization;
 using Jint.Runtime;
 
 namespace Jint.Native
@@ -88,7 +89,7 @@ namespace Jint.Native
 
         internal static JsNumber Create(int value)
         {
-            if (value >= 0 && value < _intToJsValue.Length)
+            if ((uint) value < (uint) _intToJsValue.Length)
             {
                 return _intToJsValue[value];
             }
@@ -103,7 +104,7 @@ namespace Jint.Native
 
         internal static JsNumber Create(uint value)
         {
-            if (value >= 0 && value < _intToJsValue.Length)
+            if (value < (uint) _intToJsValue.Length)
             {
                 return _intToJsValue[value];
             }
@@ -113,7 +114,7 @@ namespace Jint.Native
 
         internal static JsNumber Create(ulong value)
         {
-            if (value >= 0 && value < (ulong) _intToJsValue.Length)
+            if (value < (ulong) _intToJsValue.Length)
             {
                 return _intToJsValue[value];
             }
@@ -123,7 +124,7 @@ namespace Jint.Native
 
         public override string ToString()
         {
-            return _value.ToString();
+            return _value.ToString(CultureInfo.InvariantCulture);
         }
 
         public override bool Equals(JsValue obj)

+ 17 - 10
Jint/Native/JsString.cs

@@ -59,22 +59,29 @@ namespace Jint.Native
 
         internal static JsString Create(string value)
         {
-            switch (value.Length)
+            if (value.Length == 0)
             {
-                case 0:
-                    return Empty;
-                case 1 when value[0] >= 0 && value[0] <= AsciiMax:
-                    return _charToStringJsValue[value[0]];
-                case 4 when value == Native.Null.Text:
-                    return NullString;
-                default:
-                    return new JsString(value);
+                return Empty;
             }
+            if (value.Length == 1)
+            {
+                var i = (uint) value[0];
+                if (i < (uint) _charToStringJsValue.Length)
+                {
+                    return _charToStringJsValue[i];
+                }
+            }
+            else if (value.Length == 4 && value == Native.Null.Text)
+            {
+                return NullString;
+            }
+
+            return new JsString(value);
         }
 
         internal static JsString Create(char value)
         {
-            if (value >= 0 && value <= AsciiMax)
+            if (value < (uint) _charToJsValue.Length)
             {
                 return _charToJsValue[value];
             }

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

@@ -49,7 +49,7 @@ namespace Jint.Native.Json
 
                     foreach (var property in replacerObj.GetOwnProperties().Select(x => x.Value))
                     {
-                        JsValue v = _engine.GetValue(property);
+                        JsValue v = _engine.GetValue(property, false);
                         string item = null;
                         if (v.IsString())
                         {

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

@@ -181,11 +181,11 @@ namespace Jint.Native.Object
             var properties = arguments.At(1);
             if (!properties.IsUndefined())
             {
-                var jsValues = _engine.JsValueArrayPool.RentArray(2);
+                var jsValues = _engine._jsValueArrayPool.RentArray(2);
                 jsValues[0] = obj;
                 jsValues[1] = properties;
                 DefineProperties(thisObject, jsValues);
-               _engine.JsValueArrayPool.ReturnArray(jsValues);
+                _engine._jsValueArrayPool.ReturnArray(jsValues);
             }
 
             return obj;

+ 33 - 38
Jint/Native/Object/ObjectInstance.cs

@@ -18,8 +18,8 @@ namespace Jint.Native.Object
 {
     public class ObjectInstance : JsValue, IEquatable<ObjectInstance>
     {
-        private Dictionary<string, PropertyDescriptor> _intrinsicProperties;
-        private Dictionary<string, PropertyDescriptor> _properties;
+        protected Dictionary<string, PropertyDescriptor> _intrinsicProperties;
+        protected Dictionary<string, PropertyDescriptor> _properties;
         
         private readonly string _class;
         protected readonly Engine _engine;
@@ -62,7 +62,7 @@ namespace Jint.Native.Object
         {
             if (_intrinsicProperties == null)
             {
-                _intrinsicProperties = new Dictionary<string, PropertyDescriptor>(StringComparer.Ordinal);
+                _intrinsicProperties = new Dictionary<string, PropertyDescriptor>();
             }
 
             _intrinsicProperties[symbol.AsSymbol()] = new PropertyDescriptor(value, writable, enumerable, configurable);
@@ -102,7 +102,7 @@ namespace Jint.Native.Object
         {
             if (_properties == null)
             {
-                _properties = new Dictionary<string, PropertyDescriptor>(StringComparer.Ordinal);
+                _properties = new Dictionary<string, PropertyDescriptor>();
             }
 
             _properties.Add(propertyName, descriptor);
@@ -147,18 +147,27 @@ namespace Jint.Native.Object
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal JsValue UnwrapJsValue(PropertyDescriptor desc)
+        {
+            return UnwrapJsValue(desc, this);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static JsValue UnwrapJsValue(PropertyDescriptor desc, JsValue thisObject)
         {
             if (desc == PropertyDescriptor.Undefined)
             {
                 return Undefined;
             }
 
+            var value = (desc._flags & PropertyFlag.CustomJsValue) != 0
+                ? desc.CustomValue
+                : desc._value;
+
             // IsDataDescriptor inlined
-            if ((desc._flags & (PropertyFlag.WritableSet | PropertyFlag.Writable)) != 0 
-                || !ReferenceEquals(desc.Value, null))
+            if ((desc._flags & (PropertyFlag.WritableSet | PropertyFlag.Writable)) != 0
+                || !ReferenceEquals(value, null))
             {
-                var val = desc.Value;
-                return val ?? Undefined;
+                return value ?? Undefined;
             }
 
             var getter = desc.Get ?? Undefined;
@@ -169,7 +178,7 @@ namespace Jint.Native.Object
 
             // if getter is not undefined it must be ICallable
             var callable = getter.TryCast<ICallable>();
-            return callable.Call(this, Arguments.Empty);
+            return callable.Call(thisObject, Arguments.Empty);
         }
 
         /// <summary>
@@ -400,15 +409,13 @@ namespace Jint.Native.Object
                 RemoveOwnProperty(propertyName);
                 return true;
             }
-            else
-            {
-                if (throwOnError)
-                {
-                    ExceptionHelper.ThrowTypeError(Engine);
-                }
 
-                return false;
+            if (throwOnError)
+            {
+                ExceptionHelper.ThrowTypeError(Engine);
             }
+
+            return false;
         }
 
         /// <summary>
@@ -423,8 +430,7 @@ namespace Jint.Native.Object
 
             if (hint == Types.String || (hint == Types.None && Class == "Date"))
             {
-                var toString = Get("toString").TryCast<ICallable>();
-                if (toString != null)
+                if (Get("toString") is ICallable toString)
                 {
                     var str = toString.Call(this, Arguments.Empty);
                     if (str.IsPrimitive())
@@ -433,8 +439,7 @@ namespace Jint.Native.Object
                     }
                 }
 
-                var valueOf = Get("valueOf").TryCast<ICallable>();
-                if (valueOf != null)
+                if (Get("valueOf") is ICallable valueOf)
                 {
                     var val = valueOf.Call(this, Arguments.Empty);
                     if (val.IsPrimitive())
@@ -448,8 +453,7 @@ namespace Jint.Native.Object
 
             if (hint == Types.Number || hint == Types.None)
             {
-                var valueOf = Get("valueOf").TryCast<ICallable>();
-                if (valueOf != null)
+                if (Get("valueOf") is ICallable valueOf)
                 {
                     var val = valueOf.Call(this, Arguments.Empty);
                     if (val.IsPrimitive())
@@ -458,8 +462,7 @@ namespace Jint.Native.Object
                     }
                 }
 
-                var toString = Get("toString").TryCast<ICallable>();
-                if (toString != null)
+                if (Get("toString") is ICallable toString)
                 {
                     var str = toString.Call(this, Arguments.Empty);
                     if (str.IsPrimitive())
@@ -509,11 +512,11 @@ namespace Jint.Native.Object
                     if (desc.IsGenericDescriptor() || desc.IsDataDescriptor())
                     {
                         PropertyDescriptor propertyDescriptor;
-                        if (desc.Configurable && desc.Enumerable && desc.Writable)
+                        if ((desc._flags & PropertyFlag.ConfigurableEnumerableWritable) == PropertyFlag.ConfigurableEnumerableWritable)
                         {
                             propertyDescriptor = new PropertyDescriptor(descValue ?? Undefined, PropertyFlag.ConfigurableEnumerableWritable);
                         }
-                        else if (!desc.Configurable && !desc.Enumerable && !desc.Writable)
+                        else if ((desc._flags & PropertyFlag.ConfigurableEnumerableWritable) == 0)
                         {
                             propertyDescriptor = new PropertyDescriptor(descValue ?? Undefined, PropertyFlag.AllForbidden);
                         }
@@ -541,9 +544,7 @@ namespace Jint.Native.Object
             var currentSet = current.Set;
             var currentValue = current.Value;
             
-            if (!current.ConfigurableSet &&
-                !current.EnumerableSet &&
-                !current.WritableSet &&
+            if ((current._flags & PropertyFlag.ConfigurableSet | PropertyFlag.EnumerableSet | PropertyFlag.WritableSet) == 0 &&
                 ReferenceEquals(currentGet, null) &&
                 ReferenceEquals(currentSet, null) &&
                 ReferenceEquals(currentValue, null))
@@ -898,14 +899,14 @@ namespace Jint.Native.Object
             var thisArg = arguments.At(1);
             var callable = GetCallable(callbackfn);
 
-            var args = Engine.JsValueArrayPool.RentArray(3);
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = this;
             for (uint k = 0; k < len; k++)
             {
                 if (TryGetValue(k, out var kvalue))
                 {
                     args[0] = kvalue;
                     args[1] = k;
-                    args[2] = this;
                     var testResult = callable.Call(thisArg, args);
                     if (TypeConverter.ToBoolean(testResult))
                     {
@@ -916,7 +917,7 @@ namespace Jint.Native.Object
                 }
             }
 
-            Engine.JsValueArrayPool.ReturnArray(args);
+            _engine._jsValueArrayPool.ReturnArray(args);
 
             index = 0;
             value = Undefined;
@@ -963,11 +964,5 @@ namespace Jint.Native.Object
 
             return false;
         }
-
-        internal void Clear()
-        {
-            _intrinsicProperties?.Clear();
-            _properties?.Clear();
-        }
     }
 }

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

@@ -278,10 +278,10 @@ namespace Jint.Native.String
             }
             else if (separator.IsUndefined())
             {
-                var jsValues = Engine.JsValueArrayPool.RentArray(1);
+                var jsValues = _engine._jsValueArrayPool.RentArray(1);
                 jsValues[0] = s;
                 var arrayInstance = (ArrayInstance)Engine.Array.Construct(jsValues);
-                Engine.JsValueArrayPool.ReturnArray(jsValues);
+                _engine._jsValueArrayPool.ReturnArray(jsValues);
                 return arrayInstance;
             }
             else
@@ -597,14 +597,14 @@ namespace Jint.Native.String
                     return thisString;
                 int end = start + substr.Length;
 
-                var args = Engine.JsValueArrayPool.RentArray(3);
+                var args = _engine._jsValueArrayPool.RentArray(3);
                 args[0] = substr;
                 args[1] = start;
                 args[2] = thisString;
 
                 var replaceString = TypeConverter.ToString(replaceFunction.Call(Undefined, args));
                 
-                Engine.JsValueArrayPool.ReturnArray(args);
+                _engine._jsValueArrayPool.ReturnArray(args);
 
                 // Replace only the first match.
                 var result = StringExecutionContext.Current.GetStringBuilder(thisString.Length + (substr.Length - substr.Length));

+ 5 - 8
Jint/Pooling/ArgumentsInstancePool.cs

@@ -23,7 +23,11 @@ namespace Jint.Pooling
 
         private ArgumentsInstance Factory()
         {
-            return new ArgumentsInstance(_engine);
+            return new ArgumentsInstance(_engine)
+            {
+                Prototype = _engine.Object.PrototypeObject,
+                Extensible = true
+            };
         }
 
         public ArgumentsInstance Rent(
@@ -35,13 +39,6 @@ namespace Jint.Pooling
         {
             var obj = _pool.Allocate();
             obj.Prepare(func, names, args, env, strict);
-
-            // These properties are pre-initialized as their don't trigger
-            // the EnsureInitialized() event and are cheap
-            obj.Prototype = _engine.Object.PrototypeObject;
-            obj.Extensible = true;
-            obj.Strict = strict;
-
             return obj;
         }
 

+ 1 - 1
Jint/Runtime/Descriptors/PropertyDescriptor.cs

@@ -204,7 +204,7 @@ namespace Jint.Runtime.Descriptors
             }
         }
 
-        protected virtual JsValue CustomValue
+        protected internal virtual JsValue CustomValue
         {
             get => null;
             set => ExceptionHelper.ThrowNotImplementedException();

+ 1 - 1
Jint/Runtime/Descriptors/Specialized/FieldInfoDescriptor.cs

@@ -19,7 +19,7 @@ namespace Jint.Runtime.Descriptors.Specialized
             Writable = !fieldInfo.Attributes.HasFlag(FieldAttributes.InitOnly); // don't write to fields marked as readonly
         }
 
-        protected override JsValue CustomValue
+        protected internal override JsValue CustomValue
         {
             get => JsValue.FromObject(_engine, _fieldInfo.GetValue(_item));
             set

+ 1 - 1
Jint/Runtime/Descriptors/Specialized/IndexDescriptor.cs

@@ -53,7 +53,7 @@ namespace Jint.Runtime.Descriptors.Specialized
         {
         }
 
-        protected override JsValue CustomValue
+        protected internal override JsValue CustomValue
         {
             get
             {

+ 1 - 1
Jint/Runtime/Descriptors/Specialized/PropertyInfoDescriptor.cs

@@ -19,7 +19,7 @@ namespace Jint.Runtime.Descriptors.Specialized
             Writable = propertyInfo.CanWrite;
         }
 
-        protected override JsValue CustomValue
+        protected internal override JsValue CustomValue
         {
             get => JsValue.FromObject(_engine, _propertyInfo.GetValue(_item, null));
             set

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

@@ -2,10 +2,17 @@
 
 namespace Jint.Runtime.Environments
 {
-    public sealed class Binding
+    public struct Binding
     {
+        public Binding(JsValue value, bool canBeDeleted, bool mutable)
+        {
+            Value = value;
+            CanBeDeleted = canBeDeleted;
+            Mutable = mutable;
+        }
+
         public JsValue Value;
-        public bool CanBeDeleted;
-        public bool Mutable;
+        public readonly bool CanBeDeleted;
+        public readonly bool Mutable;
     }
 }

+ 146 - 111
Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs

@@ -1,4 +1,4 @@
-using System;
+using System;
 using System.Collections.Generic;
 using Esprima.Ast;
 using Jint.Native;
@@ -13,59 +13,112 @@ namespace Jint.Runtime.Environments
     /// </summary>
     public sealed class DeclarativeEnvironmentRecord : EnvironmentRecord
     {
+        private StructDictionary<Binding> _dictionary;
+        private bool _set;
+        private string _key;
+        private Binding _value;
+
         private const string BindingNameArguments = "arguments";
         private Binding _argumentsBinding;
 
-        private MruPropertyCache2<Binding> _bindings;
-
         public DeclarativeEnvironmentRecord(Engine engine) : base(engine)
         {
         }
 
-        public override bool HasBinding(string name)
+        private void SetItem(string key, in Binding value)
         {
-            if (name.Length == 9 && name == BindingNameArguments)
+            if (_set && _key != key)
             {
-                return _argumentsBinding != null;
+                if (_dictionary == null)
+                {
+                    _dictionary = new StructDictionary<Binding>();
+                }
+
+                _dictionary.TryInsert(_key, _value, InsertionBehavior.OverwriteExisting);
             }
 
-            return _bindings?.ContainsKey(name) == true;
+            _set = true;
+            _key = key;
+            _value = value;
+
+            _dictionary?.TryInsert(key, value, InsertionBehavior.OverwriteExisting);
         }
 
-        public override void CreateMutableBinding(string name, JsValue value, bool canBeDeleted = false)
+        private ref Binding GetExistingItem(string key)
         {
-            var binding = new Binding
+            if (_set && _key == key)
             {
-                Value = value,
-                CanBeDeleted = canBeDeleted,
-                Mutable = true
-            };
+                return ref _value;
+            }
 
-            if (name.Length == 9 && name == BindingNameArguments)
+            if (key.Length == 9 && key == BindingNameArguments)
             {
-                _argumentsBinding = binding;
+                return ref _argumentsBinding;
             }
-            else
+
+            return ref _dictionary.GetItem(key);
+        }
+
+        private bool ContainsKey(string key)
+        {
+            if (key.Length == 9 && key == BindingNameArguments)
             {
-                if (_bindings == null)
-                {
-                    _bindings = new MruPropertyCache2<Binding>();
-                }
+                return !ReferenceEquals(_argumentsBinding.Value, null);
+            }
 
-                _bindings.Add(name, binding);
+            if (_set && key == _key)
+            {
+                return true;
             }
+
+            return _dictionary?.ContainsKey(key) == true;
         }
 
-        public override void SetMutableBinding(string name, JsValue value, bool strict)
+        private void Remove(string key)
+        {
+            if (_set && key == _key)
+            {
+                _set = false;
+                _key = null;
+                _value = default;
+            }
+            
+            if (key == BindingNameArguments)
+            {
+                _argumentsBinding.Value = null;
+            }
+            else
+            {
+                _dictionary?.Remove(key);
+            }
+
+        }
+
+        private bool TryGetValue(string key, out Binding value)
         {
-            if (_bindings == null)
+            value = default;
+            if (_set && _key == key)
             {
-                _bindings = new MruPropertyCache2<Binding>();
+                value = _value;
+                return true;
             }
 
-            var binding = name.Length == 9 && name == BindingNameArguments
-                ? _argumentsBinding 
-                : _bindings[name];
+            return _dictionary?.TryGetValue(key, out value) == true;
+        }
+
+        public override bool HasBinding(string name)
+        {
+            return ContainsKey(name);
+        }
+
+        public override void CreateMutableBinding(string name, JsValue value, bool canBeDeleted = false)
+        {
+            SetItem(name, new Binding(value, canBeDeleted, mutable: true));
+        }
+
+        public override void SetMutableBinding(string name, JsValue value, bool strict)
+        {
+            ref var binding = ref GetExistingItem(name);
 
             if (binding.Mutable)
             {
@@ -82,11 +135,9 @@ namespace Jint.Runtime.Environments
 
         public override JsValue GetBindingValue(string name, bool strict)
         {
-            var binding = name.Length == 9 && name == BindingNameArguments
-                ? _argumentsBinding 
-                : _bindings[name];
+            ref var binding = ref GetExistingItem(name);
 
-            if (!binding.Mutable && binding.Value.IsUndefined())
+            if (!binding.Mutable && binding.Value._type == Types.Undefined)
             {
                 if (strict)
                 {
@@ -101,17 +152,9 @@ namespace Jint.Runtime.Environments
 
         public override bool DeleteBinding(string name)
         {
-            Binding binding = null;
-            if (name == BindingNameArguments)
-            {
-                binding = _argumentsBinding;
-            }
-            else
-            {
-                _bindings?.TryGetValue(name, out binding);
-            }
+            ref Binding binding = ref GetExistingItem(name);
 
-            if (binding == null)
+            if (ReferenceEquals(binding.Value, null))
             {
                 return true;
             }
@@ -121,14 +164,7 @@ namespace Jint.Runtime.Environments
                 return false;
             }
 
-            if (name == BindingNameArguments)
-            {
-                _argumentsBinding = null;
-            }
-            else
-            {
-                _bindings.Remove(name);
-            }
+            Remove(name);
 
             return true;
         }
@@ -138,90 +174,104 @@ namespace Jint.Runtime.Environments
             return Undefined;
         }
 
-        /// <summary>
-        /// Creates a new immutable binding in an environment record.
-        /// </summary>
-        /// <param name="name">The identifier of the binding.</param>
-        /// <param name="value">The value  of the binding.</param>
-        public void CreateImmutableBinding(string name, JsValue value)
+        /// <inheritdoc />
+        public override string[] GetAllBindingNames()
         {
-            var binding = new Binding
+            int size = _set ? 1 : 0;
+            if (!ReferenceEquals(_argumentsBinding.Value, null))
             {
-                Value = value,
-                Mutable = false,
-                CanBeDeleted = false
-            };
+                size += 1;
+            }
 
-            if (name == BindingNameArguments)
+            if (_dictionary != null)
             {
-                _argumentsBinding = binding;
+                size += _dictionary.Count;
             }
-            else
+
+            var keys = size > 0 ? new string[size] : Array.Empty<string>();
+            int n = 0;
+            if (_set)
+            {
+                keys[n++] = _key;
+            }
+
+            if (!ReferenceEquals(_argumentsBinding.Value, null))
+            {
+                keys[n++] = BindingNameArguments;
+            }
+
+            if (_dictionary != null)
             {
-                if (_bindings == null)
+                foreach (var key in _dictionary.Keys)
                 {
-                    _bindings = new MruPropertyCache2<Binding>();
+                    keys[n++] = key;
                 }
-
-                _bindings.Add(name, binding);
             }
-        }
 
-        /// <summary>
-        /// Returns an array of all the defined binding names
-        /// </summary>
-        /// <returns>The array of all defined bindings</returns>
-        public override string[] GetAllBindingNames()
-        {
-            return _bindings?.GetKeys() ?? Array.Empty<string>();
+            return keys;
         }
 
         internal void ReleaseArguments()
         {
-            _engine.ArgumentsInstancePool.Return(_argumentsBinding?.Value as ArgumentsInstance);
-            _argumentsBinding = null;
+            _engine._argumentsInstancePool.Return(_argumentsBinding.Value as ArgumentsInstance);
+            _argumentsBinding = default;
         }
 
         /// <summary>
         /// Optimized version for function calls.
         /// </summary>
-        internal void AddFunctionParameters(FunctionInstance functionInstance, JsValue[] arguments, ArgumentsInstance strict)
+        internal void AddFunctionParameters(
+            FunctionInstance functionInstance,
+            JsValue[] arguments,
+            ArgumentsInstance argumentsInstance)
         {
-            var parameters = functionInstance.FormalParameters;
-            bool empty = _bindings == null;
-            if (empty)
+            var parameters = functionInstance._formalParameters;
+            bool empty = _dictionary == null && !_set;
+            if (empty && parameters.Length == 1 && parameters[0].Length != BindingNameArguments.Length)
+            {
+                var jsValue = arguments.Length == 0 ? Undefined : arguments[0];
+                var binding = new Binding(jsValue, false, true);
+                _set = true;
+                _key = parameters[0];
+                _value = binding;
+            }
+            else
+            {
+                AddMultipleParameters(arguments, parameters);
+            }
+
+            if (ReferenceEquals(_argumentsBinding.Value, null))
             {
-                _bindings = new MruPropertyCache2<Binding>();
+                _argumentsBinding = new Binding(argumentsInstance, canBeDeleted: false, mutable: true);
             }
+        }
 
+        private void AddMultipleParameters(JsValue[] arguments, string[] parameters)
+        {
+            bool empty = _dictionary == null && !_set;
             for (var i = 0; i < parameters.Length; i++)
             {
                 var argName = parameters[i];
                 var jsValue = i + 1 > arguments.Length ? Undefined : arguments[i];
 
-                if (empty || !_bindings.TryGetValue(argName, out var existing))
+                if (empty || !TryGetValue(argName, out var existing))
                 {
-                    var binding = new Binding
-                    {
-                        Value = jsValue,
-                        CanBeDeleted = false,
-                        Mutable = true
-                    };
-
+                    var binding = new Binding(jsValue, false, true);
                     if (argName.Length == 9 && argName == BindingNameArguments)
                     {
                         _argumentsBinding = binding;
                     }
                     else
                     {
-                        _bindings[argName] = binding;
+                        SetItem(argName, binding);
                     }
                 }
                 else
                 {
                     if (existing.Mutable)
                     {
-                        existing.Value = jsValue;
+                        ref var b = ref GetExistingItem(argName);
+                        b.Value = jsValue;
                     }
                     else
                     {
@@ -229,11 +279,6 @@ namespace Jint.Runtime.Environments
                     }
                 }
             }
-
-            if (_argumentsBinding == null)
-            {
-                _argumentsBinding = new Binding {Value = strict, Mutable = true};
-            }
         }
 
         internal void AddVariableDeclarations(List<VariableDeclaration> variableDeclarations)
@@ -245,26 +290,16 @@ namespace Jint.Runtime.Environments
                 var declarationsCount = variableDeclaration.Declarations.Count;
                 for (var j = 0; j < declarationsCount; j++)
                 {
-                    if (_bindings == null)
-                    {
-                        _bindings = new MruPropertyCache2<Binding>();
-                    }
-                    
                     var d = variableDeclaration.Declarations[j];
                     var dn = ((Identifier) d.Id).Name;
 
-                    if (!_bindings.ContainsKey(dn))
+                    if (!ContainsKey(dn))
                     {
-                        var binding = new Binding
-                        {
-                            Value = Undefined,
-                            CanBeDeleted = false,
-                            Mutable = true
-                        };
-                        _bindings.Add(dn, binding);
+                        var binding = new Binding(Undefined, canBeDeleted: false, mutable: true);
+                        SetItem(dn, binding);
                     }
                 }
             }
         }
     }
-}
+}

+ 19 - 3
Jint/Runtime/Environments/EnvironmentRecord.cs

@@ -1,5 +1,4 @@
 using Jint.Native;
-using Jint.Native.Object;
 
 namespace Jint.Runtime.Environments
 {
@@ -7,10 +6,13 @@ namespace Jint.Runtime.Environments
     /// Base implementation of an Environment Record
     /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.1
     /// </summary>
-    public abstract class EnvironmentRecord : ObjectInstance
+    public abstract class EnvironmentRecord : JsValue
     {
-        protected EnvironmentRecord(Engine engine) : base(engine)
+        protected readonly Engine _engine;
+
+        protected EnvironmentRecord(Engine engine) : base(Types.Object)
         {
+            _engine = engine;
         }
 
         /// <summary>
@@ -62,5 +64,19 @@ namespace Jint.Runtime.Environments
         /// </summary>
         /// <returns>The array of all defined bindings</returns>
         public abstract string[] GetAllBindingNames();
+        
+        public override object ToObject()
+        {
+            ExceptionHelper.ThrowNotSupportedException();
+            return null;
+        }
+
+        public override bool Equals(JsValue other)
+        {
+            ExceptionHelper.ThrowNotSupportedException();
+            return false;
+        }
+
     }
 }
+

+ 5 - 5
Jint/Runtime/Environments/LexicalEnvironment.cs

@@ -13,7 +13,7 @@ namespace Jint.Runtime.Environments
     public sealed class LexicalEnvironment
     {
         private readonly Engine _engine;
-        private readonly EnvironmentRecord _record;
+        internal readonly EnvironmentRecord _record;
         private readonly LexicalEnvironment _outer;
 
         public LexicalEnvironment(Engine engine, EnvironmentRecord record, LexicalEnvironment outer)
@@ -37,7 +37,7 @@ namespace Jint.Runtime.Environments
             // optimize for common case where result is in one of the nearest scopes
             if (lex._record.HasBinding(name))
             {
-                return lex._engine.ReferencePool.Rent(lex._record, name, strict);
+                return lex._engine._referencePool.Rent(lex._record, name, strict);
             }
 
             if (lex._outer == null)
@@ -52,14 +52,14 @@ namespace Jint.Runtime.Environments
         {
             while (true)
             {
-                if (lex.Record.HasBinding(name))
+                if (lex._record.HasBinding(name))
                 {
-                    return lex._engine.ReferencePool.Rent(lex._record, name, strict);
+                    return lex._engine._referencePool.Rent(lex._record, name, strict);
                 }
 
                 if (lex._outer == null)
                 {
-                    return lex._engine.ReferencePool.Rent(Undefined.Instance, name, strict);
+                    return lex._engine._referencePool.Rent(Undefined.Instance, name, strict);
                 }
 
                 lex = lex._outer;

+ 1 - 1
Jint/Runtime/Environments/ObjectEnvironmentRecord.cs

@@ -51,7 +51,7 @@ namespace Jint.Runtime.Environments
                 ExceptionHelper.ThrowReferenceError(_engine);
             }
 
-            return UnwrapJsValue(desc);
+            return ObjectInstance.UnwrapJsValue(desc, this);
         }
 
         public override bool DeleteBinding(string name)

+ 21 - 24
Jint/Runtime/ExpressionIntepreter.cs

@@ -5,7 +5,6 @@ using Esprima.Ast;
 using Jint.Native;
 using Jint.Native.Function;
 using Jint.Native.Number;
-using Jint.Pooling;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Environments;
@@ -20,7 +19,6 @@ namespace Jint.Runtime
         private readonly bool _isDebugMode;
         private readonly int _maxRecursionDepth;
         private readonly IReferenceResolver _referenceResolver;
-        private readonly ReferencePool _referencePool;
 
         public ExpressionInterpreter(Engine engine)
         {
@@ -30,7 +28,6 @@ namespace Jint.Runtime
             _isDebugMode = engine.Options.IsDebugMode;
             _maxRecursionDepth = engine.Options.MaxRecursionDepth;
             _referenceResolver = engine.Options.ReferenceResolver;
-            _referencePool = engine.ReferencePool;
         }
 
         private object EvaluateExpression(Expression expression)
@@ -68,11 +65,11 @@ namespace Jint.Runtime
                 lref.AssertValid(_engine);
 
                 _engine.PutValue(lref, rval);
-                _referencePool.Return(lref);
+                _engine._referencePool.Return(lref);
                 return rval;
             }
 
-            JsValue lval = _engine.GetValue(lref);
+            JsValue lval = _engine.GetValue(lref, false);
 
             switch (assignmentExpression.Operator)
             {
@@ -154,7 +151,7 @@ namespace Jint.Runtime
 
             _engine.PutValue(lref, lval);
 
-            _referencePool.Return(lref);
+            _engine._referencePool.Return(lref);
             return lval;
         }
 
@@ -750,7 +747,7 @@ namespace Jint.Runtime
         public Reference EvaluateMemberExpression(MemberExpression memberExpression)
         {
             var baseReference = _engine.EvaluateExpression(memberExpression.Object);
-            var baseValue = _engine.GetValue(baseReference);
+            var baseValue = _engine.GetValue(baseReference, false);
 
             string propertyNameString;
             if (!memberExpression.Computed) // index accessor ?
@@ -769,15 +766,15 @@ namespace Jint.Runtime
 
             if (baseReference is Reference r)
             {
-                _referencePool.Return(r);
+                _engine._referencePool.Return(r);
             }
-            return _referencePool.Rent(baseValue, propertyNameString, StrictModeScope.IsStrictModeCode);
+            return _engine._referencePool.Rent(baseValue, propertyNameString, StrictModeScope.IsStrictModeCode);
         }
 
         public JsValue EvaluateFunctionExpression(IFunction functionExpression)
         {
             var funcEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, _engine.ExecutionContext.LexicalEnvironment);
-            var envRec = (DeclarativeEnvironmentRecord)funcEnv.Record;
+            var envRec = (DeclarativeEnvironmentRecord) funcEnv._record;
 
             var closure = new ScriptFunctionInstance(
                 _engine,
@@ -815,7 +812,7 @@ namespace Jint.Runtime
                 var allLiteral = true;
                 if (callExpression.Arguments.Count > 0)
                 {
-                    arguments = _engine.JsValueArrayPool.RentArray(callExpression.Arguments.Count);
+                    arguments = _engine._jsValueArrayPool.RentArray(callExpression.Arguments.Count);
                     BuildArguments(callExpression.Arguments, arguments, out allLiteral);
                 }
 
@@ -834,7 +831,7 @@ namespace Jint.Runtime
                 }
             }
 
-            var func = _engine.GetValue(callee);
+            var func = _engine.GetValue(callee, false);
 
             var r = callee as Reference;
             if (_maxRecursionDepth >= 0)
@@ -887,7 +884,7 @@ namespace Jint.Runtime
                 if (r._name == "eval" && callable is EvalFunctionInstance instance)
                 {
                     var value = instance.Call(thisObject, arguments, true);
-                    _referencePool.Return(r);
+                    _engine._referencePool.Return(r);
                     return value;
                 }
             }
@@ -906,10 +903,10 @@ namespace Jint.Runtime
 
             if (!callExpression.Cached && arguments.Length > 0)
             {
-                _engine.JsValueArrayPool.ReturnArray(arguments);
+                _engine._jsValueArrayPool.ReturnArray(arguments);
             }
 
-            _referencePool.Return(r);
+            _engine._referencePool.Return(r);
             return result;
         }
 
@@ -933,7 +930,7 @@ namespace Jint.Runtime
             var r = (Reference) value;
             r.AssertValid(_engine);
 
-            var oldValue = TypeConverter.ToNumber(_engine.GetValue(value));
+            var oldValue = TypeConverter.ToNumber(_engine.GetValue(value, false));
             double newValue = 0;
             if (updateExpression.Operator == UnaryOperator.Increment)
             {
@@ -949,7 +946,7 @@ namespace Jint.Runtime
             }
 
             _engine.PutValue(r, newValue);
-            _referencePool.Return(r);
+            _engine._referencePool.Return(r);
             return updateExpression.Prefix ? newValue : oldValue;
         }
 
@@ -960,7 +957,7 @@ namespace Jint.Runtime
 
         public JsValue EvaluateNewExpression(NewExpression newExpression)
         {
-            var arguments = _engine.JsValueArrayPool.RentArray(newExpression.Arguments.Count);
+            var arguments = _engine._jsValueArrayPool.RentArray(newExpression.Arguments.Count);
             BuildArguments(newExpression.Arguments, arguments, out _);
 
             // todo: optimize by defining a common abstract class or interface
@@ -974,7 +971,7 @@ namespace Jint.Runtime
             // construct the new instance using the Function's constructor method
             var instance = callee.Construct(arguments);
 
-            _engine.JsValueArrayPool.ReturnArray(arguments);
+            _engine._jsValueArrayPool.ReturnArray(arguments);
 
             return instance;
         }
@@ -1029,14 +1026,14 @@ namespace Jint.Runtime
                             ExceptionHelper.ThrowSyntaxError(_engine);
                         }
 
-                        _referencePool.Return(r);
+                        _engine._referencePool.Return(r);
                         return true;
                     }
                     if (r.IsPropertyReference())
                     {
                         var o = TypeConverter.ToObject(_engine, r.GetBase());
                         var jsValue = o.Delete(r._name, r._strict);
-                        _referencePool.Return(r);
+                        _engine._referencePool.Return(r);
                         return jsValue;
                     }
                     if (r._strict)
@@ -1046,12 +1043,12 @@ namespace Jint.Runtime
 
                     var bindings = r.GetBase().TryCast<EnvironmentRecord>();
                     var referencedName = r.GetReferencedName();
-                    _referencePool.Return(r);
+                    _engine._referencePool.Return(r);
 
                     return bindings.DeleteBinding(referencedName);
 
                 case UnaryOperator.Void:
-                    _engine.GetValue(value);
+                    _engine.GetValue(value, true);
                     return Undefined.Instance;
 
                 case UnaryOperator.TypeOf:
@@ -1060,7 +1057,7 @@ namespace Jint.Runtime
                     {
                         if (r.IsUnresolvableReference())
                         {
-                            _referencePool.Return(r);
+                            _engine._referencePool.Return(r);
                             return "undefined";
                         }
                     }

+ 0 - 165
Jint/Runtime/MruPropertyCache.cs

@@ -1,165 +0,0 @@
-using System.Collections;
-using System.Collections.Generic;
-
-namespace Jint.Runtime
-{
-    internal class MruPropertyCache<TKey, TValue> : IDictionary<TKey, TValue>
-    {
-        private IDictionary<TKey, TValue> _dictionary = new Dictionary<TKey, TValue>();
-        private LinkedList<KeyValuePair<TKey, TValue>> _list;
-        private uint _length;
-
-        public MruPropertyCache(uint length) {
-            _length = length;
-            _list = new LinkedList<KeyValuePair<TKey, TValue>>();
-            for(int i=0; i<length; i++) {
-                _list.AddLast(new KeyValuePair<TKey, TValue>(default(TKey), default(TValue)));
-            }
-        }
-
-        private bool Find(TKey key, out LinkedListNode<KeyValuePair<TKey, TValue>> result) {
-            result = _list.First;
-            while(result != null) {
-                if(key.Equals(result.Value.Key)) {
-                    return true;
-                }
-
-                result = result.Next;
-            }
-
-            return false;
-        }
-        public TValue this[TKey key] {
-            get {
-                LinkedListNode<KeyValuePair<TKey, TValue>> node;
-                if(Find(key, out node)) {
-                    return node.Value.Value;
-                }
-                
-                return _dictionary[key];
-            }
-
-            set {
-                LinkedListNode<KeyValuePair<TKey, TValue>> node;
-                if (!Find(key, out node)) {
-                    _list.AddFirst(new KeyValuePair<TKey, TValue>(key, value));
-                    _list.RemoveLast();
-                }
-                else
-                {
-                    node.Value = new KeyValuePair<TKey, TValue>(key, value);
-                }
-
-                _dictionary[key] = value;
-            }
-        }
-
-        public int Count {
-            get {
-                return _dictionary.Count;
-            }
-        }
-
-        public bool IsReadOnly {
-            get {
-                return _dictionary.IsReadOnly;
-            }
-        }
-
-        public ICollection<TKey> Keys {
-            get {
-                return _dictionary.Keys;
-            }
-        }
-
-        public ICollection<TValue> Values {
-            get {
-                return _dictionary.Values;
-            }
-        }
-
-        public void Add(KeyValuePair<TKey, TValue> item) {
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (!Find(item.Key, out node)) {
-                _list.AddFirst(item);
-                _list.RemoveLast();
-            }
-
-            _dictionary.Add(item);
-        }
-
-        public void Add(TKey key, TValue value) {
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (!Find(key, out node)) {
-                _list.AddFirst(new KeyValuePair<TKey, TValue>(key, value));
-                _list.RemoveLast();
-            }
-            _dictionary.Add(key, value);
-        }
-
-        public void Clear() {
-            _list.Clear();
-            _dictionary.Clear();
-        }
-
-        public bool Contains(KeyValuePair<TKey, TValue> item) {
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (Find(item.Key, out node)) {
-                return true;
-            }
-
-            return _dictionary.Contains(item);
-        }
-
-        public bool ContainsKey(TKey key) {
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (Find(key, out node)) {
-                return true;
-            }
-
-            return _dictionary.ContainsKey(key);
-        }
-
-        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
-            _dictionary.CopyTo(array, arrayIndex);
-        }
-
-        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
-            return _dictionary.GetEnumerator();
-        }
-
-        public bool Remove(KeyValuePair<TKey, TValue> item) {
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (Find(item.Key, out node))
-            {
-                _list.Remove(node);
-            }
-
-            return _dictionary.Remove(item);
-        }
-
-        public bool Remove(TKey key) {
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (Find(key, out node))
-            {
-                _list.Remove(node);
-            }
-
-            return _dictionary.Remove(key);
-        }
-
-        public bool TryGetValue(TKey key, out TValue value) {
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (Find(key, out node)) {
-                value = node.Value.Value;
-                return true;
-            }
-
-            return _dictionary.TryGetValue(key, out value);
-        }
-
-        IEnumerator IEnumerable.GetEnumerator() {
-            return _dictionary.GetEnumerator();
-        }
-    }
-}

+ 0 - 164
Jint/Runtime/MruPropertyCache2.cs

@@ -1,164 +0,0 @@
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-
-namespace Jint.Runtime
-{
-    internal class MruPropertyCache2<TValue> where TValue : class
-    {
-        private Dictionary<string, TValue> _dictionary;
-        private bool _set;
-        private string _key;
-        private TValue _value;
-
-        public TValue this[string key]
-        {
-            get
-            {
-                if (_set && _key == key)
-                {
-                    return _value;
-                }
-
-                return _dictionary?[key];
-            }
-
-            set
-            {
-                EnsureInitialized(key);
-                _set = true;
-                _key = key;
-                _value = value;
-
-                if (_dictionary != null)
-                {
-                    _dictionary[key] = value;
-                }
-            }
-        }
-
-        public int Count
-        {
-            get
-            {
-                int count = _set ? 1 : 0;
-                if (_dictionary != null)
-                {
-                    count += _dictionary.Count;
-                }
-
-                return count;
-            }
-        }
-
-        public void Add(string key, TValue value)
-        {
-            EnsureInitialized(key);
-            _set = true;
-            _key = key;
-            _value = value;
-
-            _dictionary?.Add(key, value);
-        }
-
-        public void Clear()
-        {
-            _set = false;
-            _key = default(string);
-            _value = null;
-
-            _dictionary?.Clear();
-        }
-
-        public bool ContainsKey(string key)
-        {
-            if (_set && key == _key)
-            {
-                return true;
-            }
-
-            return _dictionary?.ContainsKey(key) == true;
-        }
-
-        public IEnumerable<KeyValuePair<string, TValue>> GetEnumerator()
-        {
-            if (_dictionary == null)
-            {
-                if (_set)
-                {
-                    yield return new KeyValuePair<string, TValue>(_key, _value);
-                }
-
-                yield break;
-            }
-
-            foreach (var pair in _dictionary)
-            {
-                yield return pair;
-            }
-        }
-
-        public void Remove(string key)
-        {
-            if (_set && key == _key)
-            {
-                _set = false;
-                _key = null;
-                _value = null;
-            }
-
-            _dictionary?.Remove(key);
-        }
-
-        public bool TryGetValue(string key, out TValue value)
-        {
-            value = null;
-            if (_set && _key == key)
-            {
-                value = _value;
-                return true;
-            }
-
-            return _dictionary?.TryGetValue(key, out value) == true;
-        }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private void EnsureInitialized(string key)
-        {
-            if (_set && _key != key)
-            {
-                if (_dictionary == null)
-                {
-                    _dictionary = new Dictionary<string, TValue>();
-                }
-
-                _dictionary[_key] = _value;
-            }
-        }
-
-        public string[] GetKeys()
-        {
-            int size = _set ? 1 : 0;
-            if (_dictionary != null)
-            {
-                size += _dictionary.Count;
-            }
-
-            var keys = new string[size];
-            int n = 0;
-            if (_set)
-            {
-                keys[n++] = _key;
-            }
-
-            if (_dictionary != null)
-            {
-                foreach (var key in _dictionary.Keys)
-                {
-                    keys[n++] = key;
-                }
-            }
-
-            return keys;
-        }
-    }
-}

+ 2 - 2
Jint/Runtime/RefStack.cs

@@ -7,7 +7,7 @@ namespace Jint.Runtime
     internal sealed class ExecutionContextStack
     {
         private ExecutionContext[] _array;
-        private int _size;
+        private uint _size;
 
         private const int DefaultCapacity = 4;
 
@@ -40,7 +40,7 @@ namespace Jint.Runtime
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public void Push(in ExecutionContext item)
         {
-            if (_size == _array.Length)
+            if (_size == (uint) _array.Length)
             {
                 var newSize = 2 * _array.Length;
                 var newArray = new ExecutionContext[newSize];

+ 2 - 7
Jint/Runtime/References/Reference.cs

@@ -55,13 +55,8 @@ namespace Jint.Runtime.References
         public bool IsPropertyReference()
         {
             // http://www.ecma-international.org/ecma-262/5.1/#sec-8.7
-            if (_baseValue._type != Types.Object && _baseValue._type != Types.None)
-            {
-                // primitive
-                return true;
-            }
-            
-            return _baseValue._type == Types.Object && !(_baseValue is EnvironmentRecord);
+            return _baseValue._type != Types.Object && _baseValue._type != Types.None
+                   || _baseValue._type == Types.Object && !(_baseValue is EnvironmentRecord);
         }
 
         internal Reference Reassign(JsValue baseValue, string name, bool strict)

+ 3 - 3
Jint/Runtime/StatementInterpreter.cs

@@ -509,10 +509,10 @@ namespace Jint.Runtime
                 var catchClause = tryStatement.Handler;
                 if (catchClause != null)
                 {
-                    var c = _engine.GetValue(b);
+                    var c = b.Value;
                     var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
                     var catchEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
-                    catchEnv.Record.CreateMutableBinding(((Identifier) catchClause.Param).Name, c);
+                    catchEnv._record.CreateMutableBinding(((Identifier) catchClause.Param).Name, c);
 
                     _engine.UpdateLexicalEnvironment(catchEnv);
                     b = _engine.ExecuteStatement(catchClause.Body);
@@ -552,7 +552,7 @@ namespace Jint.Runtime
 
                     var value = _engine.GetValue(_engine.EvaluateExpression(declaration.Init), true);
                     _engine.PutValue(lhs, value);
-                    _engine.ReferencePool.Return(lhs);
+                    _engine._referencePool.Return(lhs);
                 }
             }
 

+ 802 - 0
Jint/Runtime/StructDictionary.cs

@@ -0,0 +1,802 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+
+namespace Jint.Runtime
+{
+    internal enum InsertionBehavior : byte
+    {
+        /// <summary>
+        /// Specifies that an existing entry with the same key should be overwritten if encountered.
+        /// </summary>
+        OverwriteExisting = 1,
+
+        /// <summary>
+        /// Specifies that if an existing entry with the same key is encountered, an exception should be thrown.
+        /// </summary>
+        ThrowOnExisting = 2,
+        
+        /// <summary>
+        /// Specifies that if existing entry with the same key is encountered, the update will be skipped.
+        /// </summary>
+        SkipIfExists = 3
+    }
+
+    /// <summary>
+    /// Taken from .NET source to create performant specialized dictionary containing structs for Jint.
+    /// </summary>
+    internal sealed class StructDictionary<TValue> where TValue : struct
+    {
+        private static readonly EqualityComparer<string> _comparer; 
+        
+        static StructDictionary()
+        {
+            // we want to use same comparer as default dictionary impl that is hidden from us
+            // .NET Core uses non-randomized hash code generation that is faster than default
+            try
+            {
+                Dictionary<string, TValue> dictionary = new Dictionary<string, TValue>();
+                var field = dictionary.GetType().GetField("_comparer", BindingFlags.Instance | BindingFlags.NonPublic);
+                field = field ?? dictionary.GetType().GetField("comparer", BindingFlags.Instance | BindingFlags.NonPublic);
+                _comparer = field?.GetValue(dictionary) as EqualityComparer<string> ?? EqualityComparer<string>.Default;
+            }
+            catch
+            {
+                _comparer = EqualityComparer<string>.Default;
+            }
+        }
+        
+        private struct Entry
+        {
+            public int hashCode; // Lower 31 bits of hash code, -1 if unused
+            public int next; // Index of next entry, -1 if last
+            public string key; // Key of entry
+            public TValue value; // Value of entry
+        }
+
+        private int[] _buckets;
+        private Entry[] _entries;
+        private int _count;
+        private int _freeList;
+        private int _freeCount;
+        private KeyCollection _keys;
+        private ValueCollection _values;
+
+        public StructDictionary() : this(0)
+        {
+        }
+
+        public StructDictionary(int capacity)
+        {
+            if (capacity > 0) Initialize(capacity);
+        }
+                                
+        public int Count => _count - _freeCount;
+
+        public KeyCollection Keys
+        {
+            get
+            {
+                if (_keys == null) _keys = new KeyCollection(this);
+                return _keys;
+            }
+        }
+
+        public ValueCollection Values
+        {
+            get
+            {
+                if (_values == null) _values = new ValueCollection(this);
+                return _values;
+            }
+        }
+
+        public ref TValue GetItem(string key)
+        {
+            int i = FindEntry(key);
+            if (i >= 0) return ref _entries[i].value;
+            ExceptionHelper.ThrowArgumentException("key " + key + " not part of dictionary");
+            return ref _entries[0].value;
+        }
+
+        public void Clear()
+        {
+            int count = _count;
+            if (count > 0)
+            {
+                Array.Clear(_buckets, 0, _buckets.Length);
+
+                _count = 0;
+                _freeList = -1;
+                _freeCount = 0;
+                Array.Clear(_entries, 0, count);
+            }
+        }
+
+        public bool ContainsKey(string key)
+            => FindEntry(key) >= 0;
+
+        public Enumerator GetEnumerator()
+            => new Enumerator(this, Enumerator.KeyValuePair);
+
+        private int FindEntry(string key)
+        {
+            int i = -1;
+            int[] buckets = _buckets;
+            Entry[] entries = _entries;
+            int collisionCount = 0;
+            if (buckets != null)
+            {
+                int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
+                // Value in _buckets is 1-based
+                i = buckets[hashCode % buckets.Length] - 1;
+                do
+                {
+                    // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
+                    // Test in if to drop range check for following array access
+                    if ((uint) i >= (uint) entries.Length ||
+                        (entries[i].hashCode == hashCode && entries[i].key == key))
+                    {
+                        break;
+                    }
+
+                    i = entries[i].next;
+                    if (collisionCount >= entries.Length)
+                    {
+                        // The chain of entries forms a loop; which means a concurrent update has happened.
+                        // Break out of the loop and throw, rather than looping forever.
+                        ExceptionHelper.ThrowInvalidOperationException();
+                    }
+
+                    collisionCount++;
+                } while (true);
+            }
+
+            return i;
+        }
+
+        private int Initialize(int capacity)
+        {
+            int size = HashHelpers.GetPrime(capacity);
+
+            _freeList = -1;
+            _buckets = new int[size];
+            _entries = new Entry[size];
+
+            return size;
+        }
+
+        public bool TryInsert(string key, in TValue value, InsertionBehavior behavior)
+        {
+            if (_buckets == null)
+            {
+                Initialize(0);
+            }
+
+            Entry[] entries = _entries;
+
+            int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
+
+            int collisionCount = 0;
+            ref int bucket = ref _buckets[hashCode % _buckets.Length];
+            // Value in _buckets is 1-based
+            int i = bucket - 1;
+            do
+            {
+                // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
+                // Test uint in if rather than loop condition to drop range check for following array access
+                if ((uint) i >= (uint) entries.Length)
+                {
+                    break;
+                }
+
+                if (entries[i].hashCode == hashCode && entries[i].key == key)
+                {
+                    if (behavior == InsertionBehavior.OverwriteExisting)
+                    {
+                        entries[i].value = value;
+                        return true;
+                    }
+
+                    if (behavior == InsertionBehavior.SkipIfExists)
+                    {
+                        return true;
+                    }
+
+                    if (behavior == InsertionBehavior.ThrowOnExisting)
+                    {
+                        ExceptionHelper.ThrowArgumentException("key already exists");
+                    }
+
+                    return false;
+                }
+
+                i = entries[i].next;
+                if (collisionCount >= entries.Length)
+                {
+                    // The chain of entries forms a loop; which means a concurrent update has happened.
+                    // Break out of the loop and throw, rather than looping forever.
+                    ExceptionHelper.ThrowInvalidOperationException();
+                }
+
+                collisionCount++;
+            } while (true);
+
+            bool updateFreeList = false;
+            int index;
+            if (_freeCount > 0)
+            {
+                index = _freeList;
+                updateFreeList = true;
+                _freeCount--;
+            }
+            else
+            {
+                int count = _count;
+                if (count == entries.Length)
+                {
+                    Resize();
+                    bucket = ref _buckets[hashCode % _buckets.Length];
+                }
+
+                index = count;
+                _count = count + 1;
+                entries = _entries;
+            }
+
+            ref Entry entry = ref entries[index];
+
+            if (updateFreeList)
+            {
+                _freeList = entry.next;
+            }
+
+            entry.hashCode = hashCode;
+            // Value in _buckets is 1-based
+            entry.next = bucket - 1;
+            entry.key = key;
+            entry.value = value;
+            // Value in _buckets is 1-based
+            bucket = index + 1;
+
+            return true;
+        }
+
+        private void Resize()
+            => Resize(HashHelpers.ExpandPrime(_count), false);
+
+        private void Resize(int newSize, bool forceNewHashCodes)
+        {
+            // Value types never rehash
+            Debug.Assert(!forceNewHashCodes || default(string) == null);
+            Debug.Assert(newSize >= _entries.Length);
+
+            int[] buckets = new int[newSize];
+            Entry[] entries = new Entry[newSize];
+
+            int count = _count;
+            Array.Copy(_entries, 0, entries, 0, count);
+
+            if (forceNewHashCodes)
+            {
+                for (int i = 0; i < count; i++)
+                {
+                    if (entries[i].hashCode >= 0)
+                    {
+                        entries[i].hashCode = (_comparer.GetHashCode(entries[i].key) & 0x7FFFFFFF);
+                    }
+                }
+            }
+
+            for (int i = 0; i < count; i++)
+            {
+                if (entries[i].hashCode >= 0)
+                {
+                    int bucket = entries[i].hashCode % newSize;
+                    // Value in _buckets is 1-based
+                    entries[i].next = buckets[bucket] - 1;
+                    // Value in _buckets is 1-based
+                    buckets[bucket] = i + 1;
+                }
+            }
+
+            _buckets = buckets;
+            _entries = entries;
+        }
+
+        // The overload Remove(string key, out TValue value) is a copy of this method with one additional
+        // statement to copy the value for entry being removed into the output parameter.
+        // Code has been intentionally duplicated for performance reasons.
+        public bool Remove(string key)
+        {
+            int[] buckets = _buckets;
+            Entry[] entries = _entries;
+            int collisionCount = 0;
+            if (buckets != null)
+            {
+                int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
+                int bucket = hashCode % buckets.Length;
+                int last = -1;
+                // Value in buckets is 1-based
+                int i = buckets[bucket] - 1;
+                while (i >= 0)
+                {
+                    ref Entry entry = ref entries[i];
+
+                    if (entry.hashCode == hashCode && entry.key == key)
+                    {
+                        if (last < 0)
+                        {
+                            // Value in buckets is 1-based
+                            buckets[bucket] = entry.next + 1;
+                        }
+                        else
+                        {
+                            entries[last].next = entry.next;
+                        }
+
+                        entry.hashCode = -1;
+                        entry.next = _freeList;
+                        entry.key = null;
+                        entry.value = default;
+
+                        _freeList = i;
+                        _freeCount++;
+                        return true;
+                    }
+
+                    last = i;
+                    i = entry.next;
+                    if (collisionCount >= entries.Length)
+                    {
+                        // The chain of entries forms a loop; which means a concurrent update has happened.
+                        // Break out of the loop and throw, rather than looping forever.
+                        ExceptionHelper.ThrowInvalidOperationException();
+                    }
+
+                    collisionCount++;
+                }
+            }
+
+            return false;
+        }
+
+        // This overload is a copy of the overload Remove(string key) with one additional
+        // statement to copy the value for entry being removed into the output parameter.
+        // Code has been intentionally duplicated for performance reasons.
+        public bool Remove(string key, out TValue value)
+        {
+            int[] buckets = _buckets;
+            Entry[] entries = _entries;
+            int collisionCount = 0;
+            if (buckets != null)
+            {
+                int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
+                int bucket = hashCode % buckets.Length;
+                int last = -1;
+                // Value in buckets is 1-based
+                int i = buckets[bucket] - 1;
+                while (i >= 0)
+                {
+                    ref Entry entry = ref entries[i];
+
+                    if (entry.hashCode == hashCode && entry.key == key)
+                    {
+                        if (last < 0)
+                        {
+                            // Value in buckets is 1-based
+                            buckets[bucket] = entry.next + 1;
+                        }
+                        else
+                        {
+                            entries[last].next = entry.next;
+                        }
+
+                        value = entry.value;
+
+                        entry.hashCode = -1;
+                        entry.next = _freeList;
+                        entry.key = null;
+                        entry.value = default;
+
+                        _freeList = i;
+                        _freeCount++;
+                        return true;
+                    }
+
+                    last = i;
+                    i = entry.next;
+                    if (collisionCount >= entries.Length)
+                    {
+                        // The chain of entries forms a loop; which means a concurrent update has happened.
+                        // Break out of the loop and throw, rather than looping forever.
+                        ExceptionHelper.ThrowInvalidOperationException();
+                    }
+
+                    collisionCount++;
+                }
+            }
+
+            value = default;
+            return false;
+        }
+
+        public bool TryGetValue(string key, out TValue value)
+        {
+            int i = FindEntry(key);
+            if (i >= 0)
+            {
+                value = _entries[i].value;
+                return true;
+            }
+
+            value = default;
+            return false;
+        }
+
+        /// <summary>
+        /// Ensures that the dictionary can hold up to 'capacity' entries without any further expansion of its backing storage
+        /// </summary>
+        public int EnsureCapacity(int capacity)
+        {
+            int currentCapacity = _entries == null ? 0 : _entries.Length;
+            if (currentCapacity >= capacity)
+                return currentCapacity;
+            if (_buckets == null)
+                return Initialize(capacity);
+            int newSize = HashHelpers.GetPrime(capacity);
+            Resize(newSize, forceNewHashCodes: false);
+            return newSize;
+        }
+                                    
+        public struct Enumerator : IEnumerator<KeyValuePair<string, TValue>>,
+            IDictionaryEnumerator
+        {
+            private readonly StructDictionary<TValue> _dictionary;
+            private int _index;
+            private KeyValuePair<string, TValue> _current;
+            private readonly int _getEnumeratorRetType; // What should Enumerator.Current return?
+
+            internal const int DictEntry = 1;
+            internal const int KeyValuePair = 2;
+
+            internal Enumerator(StructDictionary<TValue> dictionary, int getEnumeratorRetType)
+            {
+                _dictionary = dictionary;
+                _index = 0;
+                _getEnumeratorRetType = getEnumeratorRetType;
+                _current = new KeyValuePair<string, TValue>();
+            }
+
+            public bool MoveNext()
+            {
+                // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
+                // dictionary.count+1 could be negative if dictionary.count is int.MaxValue
+                while ((uint) _index < (uint) _dictionary._count)
+                {
+                    ref Entry entry = ref _dictionary._entries[_index++];
+
+                    if (entry.hashCode >= 0)
+                    {
+                        _current = new KeyValuePair<string, TValue>(entry.key, entry.value);
+                        return true;
+                    }
+                }
+
+                _index = _dictionary._count + 1;
+                _current = new KeyValuePair<string, TValue>();
+                return false;
+            }
+
+            public KeyValuePair<string, TValue> Current => _current;
+
+            public void Dispose()
+            {
+            }
+
+            object IEnumerator.Current
+            {
+                get
+                {
+                    if (_index == 0 || (_index == _dictionary._count + 1))
+                    {
+                        ExceptionHelper.ThrowInvalidOperationException();
+                    }
+
+                    if (_getEnumeratorRetType == DictEntry)
+                    {
+                        return new DictionaryEntry(_current.Key, _current.Value);
+                    }
+                    else
+                    {
+                        return new KeyValuePair<string, TValue>(_current.Key, _current.Value);
+                    }
+                }
+            }
+
+            void IEnumerator.Reset()
+            {
+                _index = 0;
+                _current = new KeyValuePair<string, TValue>();
+            }
+
+            DictionaryEntry IDictionaryEnumerator.Entry
+            {
+                get
+                {
+                    if (_index == 0 || (_index == _dictionary._count + 1))
+                    {
+                        ExceptionHelper.ThrowInvalidOperationException();
+                    }
+
+                    return new DictionaryEntry(_current.Key, _current.Value);
+                }
+            }
+
+            object IDictionaryEnumerator.Key
+            {
+                get
+                {
+                    if (_index == 0 || (_index == _dictionary._count + 1))
+                    {
+                        ExceptionHelper.ThrowInvalidOperationException();
+                    }
+
+                    return _current.Key;
+                }
+            }
+
+            object IDictionaryEnumerator.Value
+            {
+                get
+                {
+                    if (_index == 0 || (_index == _dictionary._count + 1))
+                    {
+                        ExceptionHelper.ThrowInvalidOperationException();
+                    }
+
+                    return _current.Value;
+                }
+            }
+        }
+
+        public sealed class KeyCollection
+        {
+            private readonly StructDictionary<TValue> _dictionary;
+
+            public KeyCollection(StructDictionary<TValue> dictionary)
+            {
+                if (dictionary == null)
+                {
+                    ExceptionHelper.ThrowArgumentNullException(nameof(dictionary));
+                }
+
+                _dictionary = dictionary;
+            }
+
+            public Enumerator GetEnumerator()
+                => new Enumerator(_dictionary);
+
+            public int Count => _dictionary.Count;
+
+            public struct Enumerator : IEnumerator<string>, IEnumerator
+            {
+                private readonly StructDictionary<TValue> _dictionary;
+                private int _index;
+                private string _currenstring;
+
+                internal Enumerator(StructDictionary<TValue> dictionary)
+                {
+                    _dictionary = dictionary;
+                    _index = 0;
+                    _currenstring = default;
+                }
+
+                public void Dispose()
+                {
+                }
+
+                public bool MoveNext()
+                {
+                    while ((uint) _index < (uint) _dictionary._count)
+                    {
+                        ref Entry entry = ref _dictionary._entries[_index++];
+
+                        if (entry.hashCode >= 0)
+                        {
+                            _currenstring = entry.key;
+                            return true;
+                        }
+                    }
+
+                    _index = _dictionary._count + 1;
+                    _currenstring = default;
+                    return false;
+                }
+
+                public string Current => _currenstring;
+
+                object IEnumerator.Current
+                {
+                    get
+                    {
+                        if (_index == 0 || (_index == _dictionary._count + 1))
+                        {
+                            ExceptionHelper.ThrowInvalidOperationException();
+                        }
+
+                        return _currenstring;
+                    }
+                }
+
+                void IEnumerator.Reset()
+                {
+                    _index = 0;
+                    _currenstring = default;
+                }
+            }
+        }
+
+        public sealed class ValueCollection
+        {
+            private readonly StructDictionary<TValue> _dictionary;
+
+            public ValueCollection(StructDictionary<TValue> dictionary)
+            {
+                _dictionary = dictionary;
+            }
+
+            public Enumerator GetEnumerator()
+                => new Enumerator(_dictionary);
+
+            public int Count => _dictionary.Count;
+
+            public struct Enumerator : IEnumerator<TValue>
+            {
+                private readonly StructDictionary<TValue> _dictionary;
+                private int _index;
+                private TValue _currentValue;
+
+                internal Enumerator(StructDictionary<TValue> dictionary)
+                {
+                    _dictionary = dictionary;
+                    _index = 0;
+                    _currentValue = default;
+                }
+
+                public void Dispose()
+                {
+                }
+
+                public bool MoveNext()
+                {
+                    while ((uint) _index < (uint) _dictionary._count)
+                    {
+                        ref Entry entry = ref _dictionary._entries[_index++];
+
+                        if (entry.hashCode >= 0)
+                        {
+                            _currentValue = entry.value;
+                            return true;
+                        }
+                    }
+
+                    _index = _dictionary._count + 1;
+                    _currentValue = default;
+                    return false;
+                }
+
+                public TValue Current => _currentValue;
+
+                object IEnumerator.Current
+                {
+                    get
+                    {
+                        if (_index == 0 || (_index == _dictionary._count + 1))
+                        {
+                            ExceptionHelper.ThrowInvalidOperationException();
+                        }
+
+                        return _currentValue;
+                    }
+                }
+
+                void IEnumerator.Reset()
+                {
+                    _index = 0;
+                    _currentValue = default;
+                }
+            }
+        }
+
+        private static class HashHelpers
+        {
+            public const int HashCollisionThreshold = 100;
+
+            // This is the maximum prime smaller than Array.MaxArrayLength
+            public const int MaxPrimeArrayLength = 0x7FEFFFFD;
+
+            public const int HashPrime = 101;
+
+            // Table of prime numbers to use as hash table sizes. 
+            // A typical resize algorithm would pick the smallest prime number in this array
+            // that is larger than twice the previous capacity. 
+            // Suppose our Hashtable currently has capacity x and enough elements are added 
+            // such that a resize needs to occur. Resizing first computes 2x then finds the 
+            // first prime in the table greater than 2x, i.e. if primes are ordered 
+            // p_1, p_2, ..., p_i, ..., it finds p_n such that p_n-1 < 2x < p_n. 
+            // Doubling is important for preserving the asymptotic complexity of the 
+            // hashtable operations such as add.  Having a prime guarantees that double 
+            // hashing does not lead to infinite loops.  IE, your hash function will be 
+            // h1(key) + i*h2(key), 0 <= i < size.  h2 and the size must be relatively prime.
+            // We prefer the low computation costs of higher prime numbers over the increased
+            // memory allocation of a fixed prime number i.e. when right sizing a HashSet.
+            public static readonly int[] primes =
+            {
+                3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
+                1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
+                17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,
+                187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
+                1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369
+            };
+
+            public static bool IsPrime(int candidate)
+            {
+                if ((candidate & 1) != 0)
+                {
+                    int limit = (int) Math.Sqrt(candidate);
+                    for (int divisor = 3; divisor <= limit; divisor += 2)
+                    {
+                        if ((candidate % divisor) == 0)
+                            return false;
+                    }
+
+                    return true;
+                }
+
+                return (candidate == 2);
+            }
+
+            public static int GetPrime(int min)
+            {
+                if (min < 0)
+                    throw new ArgumentException();
+
+                for (int i = 0; i < primes.Length; i++)
+                {
+                    int prime = primes[i];
+                    if (prime >= min)
+                        return prime;
+                }
+
+                //outside of our predefined table. 
+                //compute the hard way. 
+                for (int i = (min | 1); i < int.MaxValue; i += 2)
+                {
+                    if (IsPrime(i) && ((i - 1) % HashPrime != 0))
+                        return i;
+                }
+
+                return min;
+            }
+
+            // Returns size of hashtable to grow to.
+            public static int ExpandPrime(int oldSize)
+            {
+                int newSize = 2 * oldSize;
+
+                // Allow the hashtables to grow to maximum possible size (~2G elements) before encountering capacity overflow.
+                // Note that this check works even when _items.Length overflowed thanks to the (uint) cast
+                if ((uint) newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
+                {
+                    Debug.Assert(MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
+                    return MaxPrimeArrayLength;
+                }
+
+                return GetPrime(newSize);
+            }
+        }
+    }
+}

+ 1 - 1
Jint/Runtime/TypeConverter.cs

@@ -266,7 +266,7 @@ namespace Jint.Runtime
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static string ToString(uint i)
         {
-            return i < intToString.Length
+            return i < (uint) intToString.Length
                 ? intToString[i]
                 : i.ToString();
         }