Explorar el Código

#451 optimize evaluation performance

* make StrictModeScope a struct
* prioritize statement branching
* reuse JsValue array instances
* check string length before equality (can save some 20% if less likely to hit)
Marko Lahma hace 7 años
padre
commit
93bee1d289

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

@@ -12,13 +12,14 @@
     <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
     <GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
     <GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
+    <LangVersion>latest</LangVersion>
   </PropertyGroup>
   <ItemGroup>
     <None Include=".\Scripts\**" CopyToOutputDirectory="PreserveNewest" />
     <None Include="..\Jint.Tests.CommonScripts\Scripts\**" CopyToOutputDirectory="PreserveNewest" LinkBase="SunSpider" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="BenchmarkDotNet" Version="0.10.12" />
+    <PackageReference Include="BenchmarkDotNet" Version="0.10.14" />
     <ProjectReference Include="..\Jint\Jint.csproj" />
     <PackageReference Include="Jurassic" Version="3.0.0-alpha2" />
     <PackageReference Include="NiL.JS.NetCore" Version="2.5.1200" />

+ 53 - 48
Jint/Engine.cs

@@ -157,6 +157,7 @@ namespace Jint
             ReferencePool = new ReferencePool();
             CompletionPool = new CompletionPool();
             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);
@@ -178,38 +179,39 @@ namespace Jint
             DebugHandler = new DebugHandler(this);
         }
 
-        public LexicalEnvironment GlobalEnvironment;
-        public GlobalObject Global { get; private set; }
-        public ObjectConstructor Object { get; private set; }
-        public FunctionConstructor Function { get; private set; }
-        public ArrayConstructor Array { get; private set; }
-        public StringConstructor String { get; private set; }
-        public RegExpConstructor RegExp { get; private set; }
-        public BooleanConstructor Boolean { get; private set; }
-        public NumberConstructor Number { get; private set; }
-        public DateConstructor Date { get; private set; }
-        public MathInstance Math { get; private set; }
-        public JsonInstance Json { get; private set; }
-        public SymbolConstructor Symbol { get; private set; }
-        public EvalFunctionInstance Eval { get; private set; }
-
-        public ErrorConstructor Error { get; private set; }
-        public ErrorConstructor EvalError { get; private set; }
-        public ErrorConstructor SyntaxError { get; private set; }
-        public ErrorConstructor TypeError { get; private set; }
-        public ErrorConstructor RangeError { get; private set; }
-        public ErrorConstructor ReferenceError { get; private set; }
-        public ErrorConstructor UriError { get; private set; }
-
-        public ExecutionContext ExecutionContext { get { return _executionContexts.Peek(); } }
+        public LexicalEnvironment GlobalEnvironment { get; }
+        public GlobalObject Global { get; }
+        public ObjectConstructor Object { get; }
+        public FunctionConstructor Function { get; }
+        public ArrayConstructor Array { get; }
+        public StringConstructor String { get; }
+        public RegExpConstructor RegExp { get; }
+        public BooleanConstructor Boolean { get; }
+        public NumberConstructor Number { get; }
+        public DateConstructor Date { get; }
+        public MathInstance Math { get; }
+        public JsonInstance Json { get; }
+        public SymbolConstructor Symbol { get; }
+        public EvalFunctionInstance Eval { get; }
+
+        public ErrorConstructor Error { get; }
+        public ErrorConstructor EvalError { get; }
+        public ErrorConstructor SyntaxError { get; }
+        public ErrorConstructor TypeError { get; }
+        public ErrorConstructor RangeError { get; }
+        public ErrorConstructor ReferenceError { get; }
+        public ErrorConstructor UriError { get; }
+
+        public ExecutionContext ExecutionContext => _executionContexts.Peek();
 
         public GlobalSymbolRegistry GlobalSymbolRegistry { get; }
 
-        internal Options Options { get; private set; }
+        internal Options Options { get; }
 
         internal ReferencePool ReferencePool { get; }
         internal CompletionPool CompletionPool { get; }
         internal ArgumentsInstancePool ArgumentsInstancePool { get; }
+        internal JsValueArrayPool JsValueArrayPool { get; }
 
         #region Debugger
         public delegate StepMode DebugStepDelegate(object sender, DebugInformation e);
@@ -382,6 +384,12 @@ namespace Jint
                 case Nodes.BlockStatement:
                     return _statements.ExecuteBlockStatement(statement.As<BlockStatement>());
 
+                case Nodes.ReturnStatement:
+                    return _statements.ExecuteReturnStatement(statement.As<ReturnStatement>());
+
+                case Nodes.VariableDeclaration:
+                    return _statements.ExecuteVariableDeclaration(statement.As<VariableDeclaration>());
+
                 case Nodes.BreakStatement:
                     return _statements.ExecuteBreakStatement(statement.As<BreakStatement>());
 
@@ -391,9 +399,6 @@ namespace Jint
                 case Nodes.DoWhileStatement:
                     return _statements.ExecuteDoWhileStatement(statement.As<DoWhileStatement>());
 
-                case Nodes.DebuggerStatement:
-                    return _statements.ExecuteDebuggerStatement(statement.As<DebuggerStatement>());
-
                 case Nodes.EmptyStatement:
                     return _statements.ExecuteEmptyStatement(statement.As<EmptyStatement>());
 
@@ -406,36 +411,33 @@ namespace Jint
                 case Nodes.ForInStatement:
                     return _statements.ExecuteForInStatement(statement.As<ForInStatement>());
 
-                case Nodes.FunctionDeclaration:
-                    return Completion.Empty;
-
                 case Nodes.IfStatement:
                     return _statements.ExecuteIfStatement(statement.As<IfStatement>());
 
                 case Nodes.LabeledStatement:
                     return _statements.ExecuteLabeledStatement(statement.As<LabeledStatement>());
 
-                case Nodes.ReturnStatement:
-                    return _statements.ExecuteReturnStatement(statement.As<ReturnStatement>());
-
                 case Nodes.SwitchStatement:
                     return _statements.ExecuteSwitchStatement(statement.As<SwitchStatement>());
 
+                case Nodes.FunctionDeclaration:
+                    return Completion.Empty;
+
                 case Nodes.ThrowStatement:
                     return _statements.ExecuteThrowStatement(statement.As<ThrowStatement>());
 
                 case Nodes.TryStatement:
                     return _statements.ExecuteTryStatement(statement.As<TryStatement>());
 
-                case Nodes.VariableDeclaration:
-                    return _statements.ExecuteVariableDeclaration(statement.As<VariableDeclaration>());
-
                 case Nodes.WhileStatement:
                     return _statements.ExecuteWhileStatement(statement.As<WhileStatement>());
 
                 case Nodes.WithStatement:
                     return _statements.ExecuteWithStatement(statement.As<WithStatement>());
 
+                case Nodes.DebuggerStatement:
+                    return _statements.ExecuteDebuggerStatement(statement.As<DebuggerStatement>());
+
                 case Nodes.Program:
                     return _statements.ExecuteProgram(statement.As<Program>());
 
@@ -745,12 +747,16 @@ namespace Jint
                 throw new ArgumentException("Can only invoke functions");
             }
 
-            var items = new JsValue[arguments.Length];
+            var items = JsValueArrayPool.RentArray(arguments.Length);
             for (int i = 0; i < arguments.Length; ++i)
             {
                 items[i] = JsValue.FromObject(this, arguments[i]);
             }
-            return callable.Call(JsValue.FromObject(this, thisObj), items);
+
+            var result = callable.Call(JsValue.FromObject(this, thisObj), items);
+            JsValueArrayPool.ReturnArray(items);
+
+            return result;
         }
 
         /// <summary>
@@ -792,8 +798,8 @@ namespace Jint
         //  http://www.ecma-international.org/ecma-262/5.1/#sec-10.5
         internal bool DeclarationBindingInstantiation(
             DeclarationBindingType declarationBindingType,
-            IList<FunctionDeclaration> functionDeclarations,
-            IList<VariableDeclaration> variableDeclarations,
+            List<FunctionDeclaration> functionDeclarations,
+            List<VariableDeclaration> variableDeclarations,
             FunctionInstance functionInstance,
             JsValue[] arguments)
         {
@@ -803,13 +809,11 @@ namespace Jint
 
             if (declarationBindingType == DeclarationBindingType.FunctionCode)
             {
-                var argCount = arguments.Length;
-                var n = 0;
-                for (var i = 0; i < functionInstance.FormalParameters.Length; i++)
+                var parameters = functionInstance.FormalParameters;
+                for (var i = 0; i < parameters.Length; i++)
                 {
-                    var argName = functionInstance.FormalParameters[i];
-                    n++;
-                    var v = n > argCount ? Undefined.Instance : arguments[n - 1];
+                    var argName = parameters[i];
+                    var v = i + 1 > arguments.Length ? Undefined.Instance : arguments[i];
                     var argAlreadyDeclared = env.HasBinding(argName);
                     if (!argAlreadyDeclared)
                     {
@@ -820,7 +824,8 @@ namespace Jint
                 }
             }
 
-            for (var i = 0; i < functionDeclarations.Count; i++)
+            var functionDeclarationsCount = functionDeclarations.Count;
+            for (var i = 0; i < functionDeclarationsCount; i++)
             {
                 var f = functionDeclarations[i];
                 var fn = f.Id.Name;

+ 1 - 0
Jint/Jint.csproj

@@ -4,6 +4,7 @@
     <TargetFramework>netstandard2.0</TargetFramework>
     <AssemblyOriginatorKeyFile>Jint.snk</AssemblyOriginatorKeyFile>
     <SignAssembly>true</SignAssembly>
+    <LangVersion>latest</LangVersion>
   </PropertyGroup>
   <ItemGroup>
     <PackageReference Include="Esprima" Version="1.0.0-beta-1026" />

+ 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 = new JsValue[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);
                     return jsArray;
                 }
             }

+ 7 - 5
Jint/Native/Array/ArrayInstance.cs

@@ -14,6 +14,8 @@ namespace Jint.Native.Array
         private readonly Engine _engine;
 
         private const string PropertyNameLength = "length";
+        private const int PropertyNameLengthLength = 6;
+
         private IPropertyDescriptor _length;
 
         private const int MaxDenseArrayLength = 1024 * 10;
@@ -81,7 +83,7 @@ namespace Jint.Native.Array
             var oldLenDesc = _length;
             var oldLen = (uint) TypeConverter.ToNumber(oldLenDesc.Value);
 
-            if (propertyName == "length")
+            if (propertyName.Length == 6 && propertyName == "length")
             {
                 if (desc.Value == null)
                 {
@@ -290,7 +292,7 @@ namespace Jint.Native.Array
 
         protected override void AddProperty(string propertyName, IPropertyDescriptor descriptor)
         {
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 _length = descriptor;
                 return;
@@ -300,7 +302,7 @@ namespace Jint.Native.Array
 
         protected override bool TryGetProperty(string propertyName, out IPropertyDescriptor descriptor)
         {
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 descriptor = _length;
                 return _length != null;
@@ -351,7 +353,7 @@ namespace Jint.Native.Array
                 return PropertyDescriptor.Undefined;
             }
 
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 return _length ?? PropertyDescriptor.Undefined;
             }
@@ -365,7 +367,7 @@ namespace Jint.Native.Array
             {
                 WriteArrayValue(index, desc);
             }
-            else if (propertyName == PropertyNameLength)
+            else if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 _length = desc;
             }

+ 16 - 6
Jint/Native/Array/ArrayPrototype.cs

@@ -175,7 +175,7 @@ namespace Jint.Native.Array
             var a = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
 
             uint to = 0;
-            var args = new JsValue[3];
+            var args = Engine.JsValueArrayPool.RentArray(3);
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -191,6 +191,7 @@ namespace Jint.Native.Array
                     }
                 }
             }
+            Engine.JsValueArrayPool.ReturnArray(args);
 
             return a;
         }
@@ -205,8 +206,12 @@ namespace Jint.Native.Array
 
             var callable = GetCallable(callbackfn);
 
-            var a = Engine.Array.Construct(new JsValue[] {len}, len);
-            var args = new JsValue[3];
+            var jsValues = Engine.JsValueArrayPool.RentArray(1);
+            jsValues[0] = len;
+            var a = Engine.Array.Construct(jsValues, len);
+            Engine.JsValueArrayPool.ReturnArray(jsValues);
+            
+            var args = Engine.JsValueArrayPool.RentArray(3);
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -219,6 +224,8 @@ namespace Jint.Native.Array
                 }
             }
 
+            Engine.JsValueArrayPool.ReturnArray(args);
+
             return a;
         }
 
@@ -232,7 +239,7 @@ namespace Jint.Native.Array
 
             var callable = GetCallable(callbackfn);
 
-            var args = new JsValue[3];
+            var args = Engine.JsValueArrayPool.RentArray(3);
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -243,6 +250,7 @@ namespace Jint.Native.Array
                     callable.Call(thisArg, args);
                 }
             }
+            Engine.JsValueArrayPool.ReturnArray(args);
 
             return Undefined;
         }
@@ -257,7 +265,7 @@ namespace Jint.Native.Array
 
             var callable = GetCallable(callbackfn);
 
-            var args = new JsValue[3];
+            var args = Engine.JsValueArrayPool.RentArray(3);
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -272,6 +280,7 @@ namespace Jint.Native.Array
                     }
                 }
             }
+            Engine.JsValueArrayPool.ReturnArray(args);
 
             return false;
         }
@@ -286,7 +295,7 @@ namespace Jint.Native.Array
 
             var callable = GetCallable(callbackfn);
 
-            var args = new JsValue[3];
+            var args = Engine.JsValueArrayPool.RentArray(3);
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -301,6 +310,7 @@ namespace Jint.Native.Array
                     }
                 }
             }
+            Engine.JsValueArrayPool.ReturnArray(args);
 
             return JsBoolean.True;
         }

+ 14 - 10
Jint/Native/Function/FunctionInstance.cs

@@ -9,8 +9,11 @@ namespace Jint.Native.Function
     public abstract class FunctionInstance : ObjectInstance, ICallable
     {
         private const string PropertyNamePrototype = "prototype";
+        private const int PropertyNamePrototypeLength = 9;
         private IPropertyDescriptor _prototype;
+
         private const string PropertyNameLength = "length";
+        private const int PropertyNameLengthLength = 6;
         private IPropertyDescriptor _length;
 
         private readonly Engine _engine;
@@ -84,8 +87,9 @@ namespace Jint.Native.Function
         {
             var v = base.Get(propertyName);
 
-            var f = v.As<FunctionInstance>();
-            if (propertyName == "caller" && f != null && f.Strict)
+            if (propertyName.Length == 6
+                && propertyName == "caller"
+                && ((v.As<FunctionInstance>()?.Strict).GetValueOrDefault()))
             {
                 throw new JavaScriptException(_engine.TypeError);
             }
@@ -112,11 +116,11 @@ namespace Jint.Native.Function
 
         public override IPropertyDescriptor GetOwnProperty(string propertyName)
         {
-            if (propertyName == PropertyNamePrototype)
+            if (propertyName.Length == PropertyNamePrototypeLength && propertyName == PropertyNamePrototype)
             {
                 return _prototype ?? PropertyDescriptor.Undefined;
             }
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 return _length ?? PropertyDescriptor.Undefined;
             }
@@ -126,11 +130,11 @@ namespace Jint.Native.Function
 
         protected internal override void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
         {
-            if (propertyName == PropertyNamePrototype)
+            if (propertyName.Length == PropertyNamePrototypeLength && propertyName == PropertyNamePrototype)
             {
                 _prototype = desc;
             }
-            else if (propertyName == PropertyNameLength)
+            else if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 _length = desc;
             }
@@ -142,11 +146,11 @@ namespace Jint.Native.Function
 
         public override bool HasOwnProperty(string propertyName)
         {
-            if (propertyName == PropertyNamePrototype)
+            if (propertyName.Length == PropertyNamePrototypeLength && propertyName == PropertyNamePrototype)
             {
                 return _prototype != null;
             }
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 return _length != null;
             }
@@ -156,11 +160,11 @@ namespace Jint.Native.Function
 
         public override void RemoveOwnProperty(string propertyName)
         {
-            if (propertyName == PropertyNamePrototype)
+            if (propertyName.Length == PropertyNamePrototypeLength && propertyName == PropertyNamePrototype)
             {
                 _prototype = null;
             }
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 _prototype = null;
             }

+ 11 - 3
Jint/Native/Function/FunctionPrototype.cs

@@ -80,7 +80,7 @@ namespace Jint.Native.Function
                 throw new JavaScriptException(Engine.TypeError, "Function object expected.");
             }
 
-            return System.String.Format("function() {{ ... }}");
+            return "function() {{ ... }}";
         }
 
         public JsValue Apply(JsValue thisObject, JsValue[] arguments)
@@ -107,14 +107,22 @@ namespace Jint.Native.Function
 
             var len = argArrayObj.Get("length").AsNumber();
             uint n = TypeConverter.ToUint32(len);
-            var argList = new JsValue[n];
+
+            var argList = n < 10 
+                ? Engine.JsValueArrayPool.RentArray((int) n)
+                : new JsValue[n];
+            
             for (int index = 0; index < n; index++)
             {
                 string indexName = TypeConverter.ToString(index);
                 var nextArg = argArrayObj.Get(indexName);
                 argList[index] = nextArg;
             }
-            return func.Call(thisArg, argList);
+
+            var result = func.Call(thisArg, argList);
+            Engine.JsValueArrayPool.ReturnArray(argList);
+            
+            return result;
         }
 
         public JsValue CallImpl(JsValue thisObject, JsValue[] arguments)

+ 10 - 8
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -16,6 +16,7 @@ namespace Jint.Native.Function
     public sealed class ScriptFunctionInstance : FunctionInstance, IConstructor
     {
         private const string PropertyNameName = "name";
+        private const int PropertyNameNameLength = 4;
 
         private IPropertyDescriptor _name;
 
@@ -74,7 +75,7 @@ namespace Jint.Native.Function
 
         public override IPropertyDescriptor GetOwnProperty(string propertyName)
         {
-            if (propertyName == PropertyNameName)
+            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
             {
                 return _name ?? PropertyDescriptor.Undefined;
             }
@@ -84,7 +85,7 @@ namespace Jint.Native.Function
 
         protected internal override void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
         {
-            if (propertyName == PropertyNameName)
+            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
             {
                 _name = desc;
             }
@@ -96,7 +97,7 @@ namespace Jint.Native.Function
 
         public override bool HasOwnProperty(string propertyName)
         {
-            if (propertyName == PropertyNameName)
+            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
             {
                 return _name != null;
             }
@@ -106,7 +107,7 @@ namespace Jint.Native.Function
 
         public override void RemoveOwnProperty(string propertyName)
         {
-            if (propertyName == PropertyNameName)
+            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
             {
                 _name = null;
             }
@@ -236,6 +237,7 @@ namespace Jint.Native.Function
         private class ObjectInstanceWithConstructor : ObjectInstance
         {
             private const string PropertyNameConstructor = "constructor";
+            private const int PropertyNameConstructorLength = 11;
             private IPropertyDescriptor _constructor;
 
             public ObjectInstanceWithConstructor(Engine engine, ObjectInstance thisObj) : base(engine)
@@ -258,7 +260,7 @@ namespace Jint.Native.Function
 
             public override IPropertyDescriptor GetOwnProperty(string propertyName)
             {
-                if (propertyName == PropertyNameConstructor)
+                if (propertyName.Length == PropertyNameConstructorLength && propertyName == PropertyNameConstructor)
                 {
                     return _constructor ?? PropertyDescriptor.Undefined;
                 }
@@ -268,7 +270,7 @@ namespace Jint.Native.Function
 
             protected internal override void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
             {
-                if (propertyName == PropertyNameConstructor)
+                if (propertyName.Length == PropertyNameConstructorLength && propertyName == PropertyNameConstructor)
                 {
                     _constructor = desc;
                 }
@@ -280,7 +282,7 @@ namespace Jint.Native.Function
 
             public override bool HasOwnProperty(string propertyName)
             {
-                if (propertyName == PropertyNameConstructor)
+                if (propertyName.Length == PropertyNameConstructorLength && propertyName == PropertyNameConstructor)
                 {
                     return _constructor != null;
                 }
@@ -290,7 +292,7 @@ namespace Jint.Native.Function
 
             public override void RemoveOwnProperty(string propertyName)
             {
-                if (propertyName == PropertyNameConstructor)
+                if (propertyName.Length == PropertyNameConstructorLength && propertyName == PropertyNameConstructor)
                 {
                     _constructor = null;
                 }

+ 6 - 1
Jint/Native/Object/ObjectConstructor.cs

@@ -401,7 +401,12 @@ namespace Jint.Native.Object
                 .Where(x => x.Value.Enumerable.HasValue && x.Value.Enumerable.Value)
                 .ToArray();
             var n = enumerableProperties.Length;
-            var array = Engine.Array.Construct(new JsValue[] {n}, (uint) n);
+
+            var args = _engine.JsValueArrayPool.RentArray(1);
+            args[0] = n;
+            var array = Engine.Array.Construct(args, (uint) n);
+            _engine.JsValueArrayPool.ReturnArray(args);
+
             uint index = 0;
             foreach (var prop in enumerableProperties)
             {

+ 7 - 5
Jint/Native/String/StringInstance.cs

@@ -9,6 +9,8 @@ namespace Jint.Native.String
     public class StringInstance : ObjectInstance, IPrimitiveInstance
     {
         private const string PropertyNameLength = "length";
+        private const int PropertyNameLengthLength = 6;
+
         private IPropertyDescriptor _length;
 
         public StringInstance(Engine engine)
@@ -37,12 +39,12 @@ namespace Jint.Native.String
 
         public override IPropertyDescriptor GetOwnProperty(string propertyName)
         {
-            if (propertyName == "Infinity")
+            if (propertyName.Length == 8 && propertyName == "Infinity")
             {
                 return PropertyDescriptor.Undefined;
             }
 
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 return _length ?? PropertyDescriptor.Undefined;
             }
@@ -90,7 +92,7 @@ namespace Jint.Native.String
 
         protected internal override void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
         {
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 _length = desc;
             }
@@ -102,7 +104,7 @@ namespace Jint.Native.String
 
         public override bool HasOwnProperty(string propertyName)
         {
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 return _length != null;
             }
@@ -112,7 +114,7 @@ namespace Jint.Native.String
 
         public override void RemoveOwnProperty(string propertyName)
         {
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 _length = null;
             }

+ 8 - 2
Jint/Native/String/StringPrototype.cs

@@ -268,7 +268,11 @@ namespace Jint.Native.String
             }
             else if (ReferenceEquals(separator, Undefined))
             {
-                return (ArrayInstance)Engine.Array.Construct(Arguments.From(s));
+                var jsValues = Engine.JsValueArrayPool.RentArray(1);
+                jsValues[0] = s;
+                var arrayInstance = (ArrayInstance)Engine.Array.Construct(jsValues);
+                Engine.JsValueArrayPool.ReturnArray(jsValues);
+                return arrayInstance;
             }
             else
             {
@@ -581,12 +585,14 @@ namespace Jint.Native.String
                     return thisString;
                 int end = start + substr.Length;
 
-                var args = new JsValue[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);
 
                 // Replace only the first match.
                 var result = StringExecutionContext.Current.GetStringBuilder(thisString.Length + (substr.Length - substr.Length));

+ 71 - 0
Jint/Pooling/JsValueArrayPool.cs

@@ -0,0 +1,71 @@
+using System;
+using Jint.Native;
+
+namespace Jint.Pooling
+{
+    /// <summary>
+    /// Cache reusable <see cref="JsValue" /> array instances as we allocate them a lot.
+    /// </summary>
+    internal sealed class JsValueArrayPool
+    {
+        private const int PoolSize = 15;
+        private readonly ObjectPool<JsValue[]> _poolArray1;
+        private readonly ObjectPool<JsValue[]> _poolArray2;
+        private readonly ObjectPool<JsValue[]> _poolArray3;
+
+        public JsValueArrayPool()
+        {
+            _poolArray1 = new ObjectPool<JsValue[]>(Factory1, PoolSize);
+            _poolArray2 = new ObjectPool<JsValue[]>(Factory2, PoolSize);
+            _poolArray3 = new ObjectPool<JsValue[]>(Factory3, PoolSize);
+        }
+
+        private static JsValue[] Factory1()
+        {
+            return new JsValue[1];
+        }
+
+        private static JsValue[] Factory2()
+        {
+            return new JsValue[2];
+        }
+
+        private static JsValue[] Factory3()
+        {
+            return new JsValue[3];
+        }
+
+        public JsValue[] RentArray(int size)
+        {
+            switch (size)
+            {
+                case 0:
+                    return Array.Empty<JsValue>();
+                case 1:
+                    return _poolArray1.Allocate();
+                case 2:
+                    return _poolArray2.Allocate();
+                case 3:
+                    return _poolArray3.Allocate();
+            }
+
+            return new JsValue[size];
+        }
+
+        public void ReturnArray(JsValue[] array)
+        {
+            switch (array.Length)
+            {
+                case 1:
+                    _poolArray1.Free(array);
+                    break;
+                case 2:
+                    _poolArray2.Free(array);
+                    break;
+                case 3:
+                    _poolArray3.Free(array);
+                    break;
+            }
+        }
+    }
+}

+ 1 - 1
Jint/Runtime/Arguments.cs

@@ -6,7 +6,7 @@ namespace Jint.Runtime
 {
     public static class Arguments
     {
-        public static JsValue[] Empty = new JsValue[0];
+        public static readonly JsValue[] Empty = Array.Empty<JsValue>();
 
         public static JsValue[] From(params JsValue[] o)
         {

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

@@ -148,7 +148,7 @@ namespace Jint.Runtime.Debugger
         {
             var info = new DebugInformation { CurrentStatement = statement, CallStack = _debugCallStack };
 
-            if (_engine.ExecutionContext != null && _engine.ExecutionContext.LexicalEnvironment != null)
+            if (_engine.ExecutionContext?.LexicalEnvironment != null)
             {
                 var lexicalEnvironment = _engine.ExecutionContext.LexicalEnvironment;
                 info.Locals = GetLocalVariables(lexicalEnvironment);

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

@@ -24,7 +24,7 @@ namespace Jint.Runtime.Environments
 
         public override bool HasBinding(string name)
         {
-            if (name == BindingNameArguments)
+            if (name.Length == 9 && name == BindingNameArguments)
             {
                 return _argumentsBinding != null;
             }

+ 29 - 18
Jint/Runtime/ExpressionIntepreter.cs

@@ -825,16 +825,19 @@ namespace Jint.Runtime
 
             // todo: implement as in http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.4
 
-
-            JsValue[] arguments;
-
+            var arguments = Array.Empty<JsValue>();
             if (callExpression.Cached)
             {
                 arguments = (JsValue[]) callExpression.CachedArguments;
             }
             else
             {
-                arguments = BuildArguments(callExpression.Arguments, out bool allLiteral);
+                var allLiteral = true;
+                if (callExpression.Arguments.Count > 0)
+                {
+                    arguments = _engine.JsValueArrayPool.RentArray(callExpression.Arguments.Count);
+                    BuildArguments(callExpression.Arguments, arguments, out allLiteral);
+                }
 
                 if (callExpression.CanBeCached)
                 {
@@ -927,6 +930,11 @@ namespace Jint.Runtime
                 _engine.CallStack.Pop();
             }
 
+            if (!callExpression.Cached && arguments.Length > 0)
+            {
+                _engine.JsValueArrayPool.ReturnArray(arguments);
+            }
+
             _engine.ReferencePool.Return(r);
             return result;
         }
@@ -998,7 +1006,8 @@ namespace Jint.Runtime
 
         public JsValue EvaluateNewExpression(NewExpression newExpression)
         {
-            var arguments = BuildArguments(newExpression.Arguments, out bool _);
+            var arguments = _engine.JsValueArrayPool.RentArray(newExpression.Arguments.Count);
+            BuildArguments(newExpression.Arguments, arguments, out _);
 
             // todo: optimize by defining a common abstract class or interface
             var callee = _engine.GetValue(EvaluateExpression(newExpression.Callee), true).TryCast<IConstructor>();
@@ -1011,6 +1020,8 @@ namespace Jint.Runtime
             // construct the new instance using the Function's constructor method
             var instance = callee.Construct(arguments);
 
+            _engine.JsValueArrayPool.ReturnArray(arguments);
+
             return instance;
         }
 
@@ -1018,7 +1029,11 @@ namespace Jint.Runtime
         {
             var elements = arrayExpression.Elements;
             var count = elements.Count;
-            var a = _engine.Array.Construct(new JsValue[] {count}, (uint) count);
+            
+            var jsValues = _engine.JsValueArrayPool.RentArray(1);
+            jsValues[0] = count;
+            
+            var a = _engine.Array.Construct(jsValues, (uint) count);
             for (var n = 0; n < count; n++)
             {
                 var expr = elements[n];
@@ -1028,6 +1043,7 @@ namespace Jint.Runtime
                     a.SetIndexValue((uint) n, value, throwOnError: false);
                 }
             }
+            _engine.JsValueArrayPool.ReturnArray(jsValues);
 
             return a;
         }
@@ -1128,25 +1144,20 @@ namespace Jint.Runtime
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private JsValue[] BuildArguments(List<ArgumentListElement> expressionArguments, out bool allLiteral)
+        private void BuildArguments(
+            List<ArgumentListElement> expressionArguments, 
+            JsValue[] targetArray,
+            out bool cacheable)
         {
-            allLiteral = true;
+            cacheable = true;
             var count = expressionArguments.Count;
 
-            if (count == 0)
-            {
-                return Array.Empty<JsValue>();
-            }
-
-            var arguments = new JsValue[count];
             for (var i = 0; i < count; i++)
             {
                 var argument = (Expression) expressionArguments[i];
-                arguments[i] = _engine.GetValue(EvaluateExpression(argument), true);
-                allLiteral &= argument is Literal;
+                targetArray[i] = _engine.GetValue(EvaluateExpression(argument), true);
+                cacheable &= argument is Literal;
             }
-
-            return arguments;
         }
     }
 }

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

@@ -139,7 +139,7 @@ namespace Jint.Runtime.Interop
                 {
                     if (type == typeof(Action))
                     {
-                        return (Action)(() => function(JsValue.Undefined, new JsValue[0]));
+                        return (Action)(() => function(JsValue.Undefined, Array.Empty<JsValue>()));
                     }
                     else if (typeof(MulticastDelegate).IsAssignableFrom(type))
                     {

+ 6 - 18
Jint/StrictModeScope.cs

@@ -2,7 +2,7 @@
 
 namespace Jint
 {
-    public class StrictModeScope : IDisposable
+    public readonly struct StrictModeScope : IDisposable
     {
         private readonly bool _strict;
         private readonly bool _force;
@@ -21,12 +21,15 @@ namespace Jint
                 _forcedRefCount = _refCount;
                 _refCount = 0;
             }
+            else
+            {
+                _forcedRefCount = 0;
+            }
 
             if (_strict)
             {
                 _refCount++;
             }
-
         }
 
         public void Dispose()
@@ -42,21 +45,6 @@ namespace Jint
             } 
         }
 
-        public static bool IsStrictModeCode
-        {
-            get { return _refCount > 0; }
-        }
-
-        public static int RefCount
-        {
-            get
-            {
-                return _refCount;
-            }
-            set
-            {
-                _refCount = value;
-            }
-        }
+        public static bool IsStrictModeCode => _refCount > 0;
     }
 }