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

Make ObjectInstance MruPropertyCache2 dictionary lazy (#454)

Marko Lahma 7 жил өмнө
parent
commit
189af9f321

+ 1 - 1
.gitignore

@@ -160,4 +160,4 @@ project.lock.json
 .build
 
 .idea
-BenchmarkDotNet.Artifacts
+BenchmarkDotNet.Artifacts*

+ 34 - 18
Jint/Engine.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using Esprima;
 using Esprima.Ast;
 using Jint.Native;
@@ -408,7 +407,7 @@ namespace Jint
                     return _statements.ExecuteForInStatement(statement.As<ForInStatement>());
 
                 case Nodes.FunctionDeclaration:
-                    return new Completion(Completion.Normal, null, null);
+                    return Completion.Empty;
 
                 case Nodes.IfStatement:
                     return _statements.ExecuteIfStatement(statement.As<IfStatement>());
@@ -728,7 +727,12 @@ namespace Jint
                 throw new ArgumentException("Can only invoke functions");
             }
 
-            return callable.Call(JsValue.FromObject(this, thisObj), arguments.Select(x => JsValue.FromObject(this, x)).ToArray());
+            var items = new JsValue[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);
         }
 
         /// <summary>
@@ -766,7 +770,12 @@ namespace Jint
         }
 
         //  http://www.ecma-international.org/ecma-262/5.1/#sec-10.5
-        public void DeclarationBindingInstantiation(DeclarationBindingType declarationBindingType, IEnumerable<FunctionDeclaration> functionDeclarations, IEnumerable<VariableDeclaration> variableDeclarations, FunctionInstance functionInstance, JsValue[] arguments)
+        public void DeclarationBindingInstantiation(
+            DeclarationBindingType declarationBindingType,
+            IList<FunctionDeclaration> functionDeclarations,
+            IList<VariableDeclaration> variableDeclarations,
+            FunctionInstance functionInstance,
+            JsValue[] arguments)
         {
             var env = ExecutionContext.VariableEnvironment.Record;
             bool configurableBindings = declarationBindingType == DeclarationBindingType.EvalCode;
@@ -776,8 +785,9 @@ namespace Jint
             {
                 var argCount = arguments.Length;
                 var n = 0;
-                foreach (var argName in functionInstance.FormalParameters)
+                for (var i = 0; i < functionInstance.FormalParameters.Length; i++)
                 {
+                    var argName = functionInstance.FormalParameters[i];
                     n++;
                     var v = n > argCount ? Undefined.Instance : arguments[n - 1];
                     var argAlreadyDeclared = env.HasBinding(argName);
@@ -790,8 +800,9 @@ namespace Jint
                 }
             }
 
-            foreach (var f in functionDeclarations)
+            for (var i = 0; i < functionDeclarations.Count; i++)
             {
+                var f = functionDeclarations[i];
                 var fn = f.Id.Name;
                 var fo = Function.CreateFunctionObject(f);
                 var funcAlreadyDeclared = env.HasBinding(fn);
@@ -808,12 +819,12 @@ namespace Jint
                         if (existingProp.Configurable.Value)
                         {
                             go.DefineOwnProperty(fn,
-                                                 new PropertyDescriptor(
-                                                     value: Undefined.Instance,
-                                                     writable: true,
-                                                     enumerable: true,
-                                                     configurable: configurableBindings
-                                                     ), true);
+                                new PropertyDescriptor(
+                                    value: Undefined.Instance,
+                                    writable: true,
+                                    enumerable: true,
+                                    configurable: configurableBindings
+                                ), true);
                         }
                         else
                         {
@@ -854,14 +865,19 @@ namespace Jint
             }
 
             // process all variable declarations in the current parser scope
-            foreach (var d in variableDeclarations.SelectMany(x => x.Declarations))
+            for (var i = 0; i < variableDeclarations.Count; i++)
             {
-                var dn = d.Id.As<Identifier>().Name;
-                var varAlreadyDeclared = env.HasBinding(dn);
-                if (!varAlreadyDeclared)
+                var variableDeclaration = variableDeclarations[i];
+                var declarations  = (List<VariableDeclarator>) variableDeclaration.Declarations;
+                foreach (var d in declarations)
                 {
-                    env.CreateMutableBinding(dn, configurableBindings);
-                    env.SetMutableBinding(dn, Undefined.Instance, strict);
+                    var dn = d.Id.As<Identifier>().Name;
+                    var varAlreadyDeclared = env.HasBinding(dn);
+                    if (!varAlreadyDeclared)
+                    {
+                        env.CreateMutableBinding(dn, configurableBindings);
+                        env.SetMutableBinding(dn, Undefined.Instance, strict);
+                    }
                 }
             }
         }

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

@@ -1,6 +1,4 @@
-using System.Collections.Generic;
-using System.Linq;
-using Jint.Native.Object;
+using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
@@ -49,7 +47,7 @@ namespace Jint.Native.Function
             var f = new BindFunctionInstance(Engine) {Extensible = true};
             f.TargetFunction = thisObj;
             f.BoundThis = thisArg;
-            f.BoundArgs = arguments.Skip(1).ToArray();
+            f.BoundArgs = arguments.Skip(1);
             f.Prototype = Engine.Function.PrototypeObject;
 
             var o = target as FunctionInstance;
@@ -126,7 +124,7 @@ namespace Jint.Native.Function
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            return func.Call(arguments.At(0), arguments.Length == 0 ? arguments : arguments.Skip(1).ToArray());
+            return func.Call(arguments.At(0), arguments.Length == 0 ? arguments : arguments.Skip(1));
         }
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)

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

@@ -1,4 +1,5 @@
-using System.Linq;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
 using Esprima.Ast;
 using Jint.Native.Object;
 using Jint.Runtime;
@@ -22,7 +23,7 @@ namespace Jint.Native.Function
         /// <param name="scope"></param>
         /// <param name="strict"></param>
         public ScriptFunctionInstance(Engine engine, IFunction functionDeclaration, LexicalEnvironment scope, bool strict)
-            : base(engine, functionDeclaration.Params.Select(x => x.As<Identifier>().Name).ToArray(), scope, strict)
+            : base(engine, GetParameterNames(functionDeclaration), scope, strict)
         {
             _functionDeclaration = functionDeclaration;
 
@@ -48,6 +49,20 @@ namespace Jint.Native.Function
             }
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static string[] GetParameterNames(IFunction functionDeclaration)
+        {
+            var list = (List<INode>) functionDeclaration.Params;
+            var count = list.Count;
+            var names = new string[count];
+            for (var i = 0; i < count; ++i)
+            {
+                names[i] = ((Identifier) list[i]).Name;
+            }
+
+            return names;
+        }
+
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.1
         /// </summary>

+ 3 - 1
Jint/Native/Number/NumberPrototype.cs

@@ -12,6 +12,8 @@ namespace Jint.Native.Number
     /// </summary>
     public sealed class NumberPrototype : NumberInstance
     {
+        private static readonly char[] _numberSeparators = {'.', 'e'};
+
         private NumberPrototype(Engine engine)
             : base(engine)
         {
@@ -154,7 +156,7 @@ namespace Jint.Native.Number
 
             // Get the number of decimals
             string str = x.ToString("e23", CultureInfo.InvariantCulture);
-            int decimals = str.IndexOfAny(new [] { '.', 'e' });
+            int decimals = str.IndexOfAny(_numberSeparators);
             decimals = decimals == -1 ? str.Length : decimals;
 
             p -= decimals;

+ 156 - 31
Jint/Native/Object/ObjectInstance.cs

@@ -1,26 +1,31 @@
 using System.Collections.Generic;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
-using System;
-using System.Collections.Specialized;
 
 namespace Jint.Native.Object
 {
     public class ObjectInstance
     {
+        private const string PropertyNamePrototype = "prototype";
+        private const string PropertyNameConstructor = "constructor";
+        private const string PropertyNameLength = "length";
+
         private JsValue _jsValue;
+
         private Dictionary<string, PropertyDescriptor> _intrinsicProperties;
+        private MruPropertyCache2<string, PropertyDescriptor> _properties;
+
+        private PropertyDescriptor _prototype;
+        private PropertyDescriptor _constructor;
+        private PropertyDescriptor _length;
 
         public ObjectInstance(Engine engine)
         {
             Engine = engine;
-            Properties = new MruPropertyCache2<string, PropertyDescriptor>();
         }
 
         public Engine Engine { get; set; }
 
-        protected IDictionary<string, PropertyDescriptor> Properties { get; private set; }
-
         /// <summary>
         /// Caches the constructed JS.
         /// </summary>
@@ -78,27 +83,125 @@ namespace Jint.Native.Object
         /// A String value indicating a specification defined
         /// classification of objects.
         /// </summary>
-        public virtual string Class
-        {
-            get { return "Object"; }
-        }
+        public virtual string Class => "Object";
 
         public virtual IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
         {
             EnsureInitialized();
-            return Properties;
+
+            if (_prototype != null)
+            {
+                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNamePrototype, _prototype);
+            }
+            if (_constructor != null)
+            {
+                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameConstructor, _constructor);
+            }
+            if (_length != null)
+            {
+                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameLength, _length);
+            }
+
+            if (_properties != null)
+            {
+                foreach (var pair in _properties.GetEnumerator())
+                {
+                    yield return pair;
+                }
+            }
         }
 
-        public virtual bool HasOwnProperty(string p)
+        protected void AddProperty(string propertyName, PropertyDescriptor descriptor)
+        {
+            if (propertyName == PropertyNamePrototype)
+            {
+                _prototype = descriptor;
+                return;
+            }
+            if (propertyName == PropertyNameConstructor)
+            {
+                _constructor = descriptor;
+                return;
+            }
+            if (propertyName == PropertyNameLength)
+            {
+                _length = descriptor;
+                return;
+            }
+
+            if (_properties == null)
+            {
+                _properties = new MruPropertyCache2<string, PropertyDescriptor>();
+            }
+
+            _properties.Add(propertyName, descriptor);
+        }
+
+        protected bool TryGetProperty(string propertyName, out PropertyDescriptor descriptor)
+        {
+            if (propertyName == PropertyNamePrototype)
+            {
+                descriptor = _prototype;
+                return _prototype != null;
+            }
+            if (propertyName == PropertyNameConstructor)
+            {
+                descriptor = _constructor;
+                return _constructor != null;
+            }
+            if (propertyName == PropertyNameLength)
+            {
+                descriptor = _length;
+                return _length != null;
+            }
+
+            if (_properties == null)
+            {
+                descriptor = null;
+                return false;
+            }
+
+            return _properties.TryGetValue(propertyName, out descriptor);
+        }
+
+        public virtual bool HasOwnProperty(string propertyName)
         {
             EnsureInitialized();
-            return Properties.ContainsKey(p);
+
+            if (propertyName == PropertyNamePrototype)
+            {
+                return _prototype != null;
+            }
+            if (propertyName == PropertyNameConstructor)
+            {
+                return _constructor != null;
+            }
+            if (propertyName == PropertyNameLength)
+            {
+                return _length != null;
+            }
+
+            return _properties?.ContainsKey(propertyName) ?? false;
         }
 
-        public virtual void RemoveOwnProperty(string p)
+        public virtual void RemoveOwnProperty(string propertyName)
         {
             EnsureInitialized();
-            Properties.Remove(p);
+
+            if (propertyName == PropertyNamePrototype)
+            {
+                _prototype = null;
+            }
+            if (propertyName == PropertyNameConstructor)
+            {
+                _constructor = null;
+            }
+            if (propertyName == PropertyNameLength)
+            {
+                _length = null;
+            }
+
+            _properties?.Remove(propertyName);
         }
 
         /// <summary>
@@ -146,24 +249,22 @@ namespace Jint.Native.Object
         {
             EnsureInitialized();
 
-            PropertyDescriptor x;
-            if (Properties.TryGetValue(propertyName, out x))
+            if (propertyName == PropertyNamePrototype)
             {
-                /* Spec implementation
-                PropertyDescriptor d;
-                if (x.IsDataDescriptor())
-                {
-                    d = new PropertyDescriptor(x.As<DataDescriptor>());
-                }
-                else
-                {
-                    d = new PropertyDescriptor(x.As<AccessorDescriptor>());
-                }
-
-                return d;
-                */
+                return _prototype ?? PropertyDescriptor.Undefined;
+            }
+            if (propertyName == PropertyNameConstructor)
+            {
+                return _constructor ?? PropertyDescriptor.Undefined;
+            }
+            if (propertyName == PropertyNameLength)
+            {
+                return _length ?? PropertyDescriptor.Undefined;
+            }
 
-                // optimmized implementation
+            PropertyDescriptor x;
+            if (_properties != null && _properties.TryGetValue(propertyName, out x))
+            {
                 return x;
             }
 
@@ -173,7 +274,29 @@ namespace Jint.Native.Object
         protected virtual void SetOwnProperty(string propertyName, PropertyDescriptor desc)
         {
             EnsureInitialized();
-            Properties[propertyName] = desc;
+
+            if (propertyName == PropertyNamePrototype)
+            {
+                _prototype = desc;
+                return;
+            }
+            if (propertyName == PropertyNameConstructor)
+            {
+                _constructor = desc;
+                return;
+            }
+            if (propertyName == PropertyNameLength)
+            {
+                _length = desc;
+                return;
+            }
+
+            if (_properties == null)
+            {
+                _properties = new MruPropertyCache2<string, PropertyDescriptor>();
+            }
+
+            _properties[propertyName] = desc;
         }
 
         /// <summary>
@@ -662,5 +785,7 @@ namespace Jint.Native.Object
         {
             return TypeConverter.ToString(this);
         }
+
+        protected uint GetLengthValue() => TypeConverter.ToUint32(_length.Value);
     }
 }

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

@@ -320,19 +320,20 @@ namespace Jint.Native.String
             }
             else
             {
-                var segments = new List<string>();
+                List<string> segments;
                 var sep = TypeConverter.ToString(separator);
 
                 if (sep == string.Empty || (rx != null && rx.Source == regExpForMatchingAllCharactere)) // for s.split(new RegExp)
                 {
+                    segments = new List<string>(s.Length);
                     foreach (var c in s)
                     {
-                        segments.Add(c.ToString());
+                        segments.Add(TypeConverter.ToString(c));
                     }
                 }
                 else
                 {
-                    segments = s.Split(new[] {sep}, StringSplitOptions.None).ToList();
+                    segments = new List<string>(s.Split(new[] {sep}, StringSplitOptions.None));
                 }
 
                 for (int i = 0; i < segments.Count && i < limit; i++)
@@ -731,7 +732,7 @@ namespace Jint.Native.String
             {
                 return "";
             }
-            return s[(int) position].ToString();
+            return TypeConverter.ToString(s[(int) position]);
 
         }
 

+ 20 - 2
Jint/Runtime/Arguments.cs

@@ -1,4 +1,6 @@
-using Jint.Native;
+using System;
+using System.Runtime.CompilerServices;
+using Jint.Native;
 
 namespace Jint.Runtime
 {
@@ -18,14 +20,30 @@ namespace Jint.Runtime
         /// <param name="index">The index of the parameter to return</param>
         /// <param name="undefinedValue">The value to return is the parameter is not provided</param>
         /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static JsValue At(this JsValue[] args, int index, JsValue undefinedValue)
         {
             return args.Length > index ? args[index] : undefinedValue;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static JsValue At(this JsValue[] args, int index)
         {
             return At(args, index, Undefined.Instance);
         }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static JsValue[] Skip(this JsValue[] args, int count)
+        {
+            var newLength = args.Length - count;
+            if (newLength <= 0)
+            {
+                return Array.Empty<JsValue>();
+            }
+
+            var array = new JsValue[newLength];
+            Array.Copy(args, count, array, 0, newLength);
+            return array;
+        }
     }
-}
+}

+ 11 - 8
Jint/Runtime/Completion.cs

@@ -8,11 +8,14 @@ namespace Jint.Runtime
     /// </summary>
     public class Completion
     {
-        public static string Normal = "normal";
-        public static string Break = "break";
-        public static string Continue = "continue";
-        public static string Return = "return";
-        public static string Throw = "throw";
+        public const string Normal = "normal";
+        public const string Break = "break";
+        public const string Continue = "continue";
+        public const string Return = "return";
+        public const string Throw = "throw";
+
+        public static readonly Completion Empty = new Completion(Normal, null, null);
+        public static readonly Completion EmptyUndefined = new Completion(Normal, Undefined.Instance, null);
 
         public Completion(string type, JsValue value, string identifier)
         {
@@ -21,9 +24,9 @@ namespace Jint.Runtime
             Identifier = identifier;
         }
 
-        public string Type { get; private set; }
-        public JsValue Value { get; private set; }
-        public string Identifier { get; private set; }
+        public string Type { get; }
+        public JsValue Value { get; }
+        public string Identifier { get; }
 
         public JsValue GetValueOrDefault()
         {

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

@@ -11,7 +11,7 @@ namespace Jint.Runtime.Environments
     public sealed class DeclarativeEnvironmentRecord : EnvironmentRecord
     {
         private readonly Engine _engine;
-        private readonly IDictionary<string, Binding> _bindings = new Dictionary<string, Binding>();
+        private readonly Dictionary<string, Binding> _bindings = new Dictionary<string, Binding>();
 
         public DeclarativeEnvironmentRecord(Engine engine) : base(engine)
         {
@@ -27,7 +27,7 @@ namespace Jint.Runtime.Environments
         {
             _bindings.Add(name, new Binding
                 {
-                    Value = Undefined.Instance, 
+                    Value = Undefined.Instance,
                     CanBeDeleted =  canBeDeleted,
                     Mutable = true
                 });
@@ -80,7 +80,7 @@ namespace Jint.Runtime.Environments
             }
 
             _bindings.Remove(name);
-            
+
             return true;
         }
 

+ 28 - 10
Jint/Runtime/ExpressionIntepreter.cs

@@ -1,5 +1,6 @@
 using System;
-using System.Linq;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
 using Esprima;
 using Esprima.Ast;
 using Jint.Native;
@@ -810,12 +811,12 @@ namespace Jint.Runtime
             }
             else
             {
-                arguments = callExpression.Arguments.Select(x => EvaluateExpression(x.As<Expression>())).Select(_engine.GetValue).ToArray();
+                arguments = BuildArguments(callExpression.Arguments, out bool allLiteral);
 
                 if (callExpression.CanBeCached)
                 {
                     // The arguments array can be cached if they are all literals
-                    if (callExpression.Arguments.All(x => x is Literal))
+                    if (allLiteral)
                     {
                         callExpression.CachedArguments = arguments;
                         callExpression.Cached = true;
@@ -927,7 +928,7 @@ namespace Jint.Runtime
                     if (r != null
                         && r.IsStrict()
                         && (r.GetBase().TryCast<EnvironmentRecord>() != null)
-                        && (Array.IndexOf(new[] { "eval", "arguments" }, r.GetReferencedName()) != -1))
+                        && ("eval" == r.GetReferencedName() || "arguments" == r.GetReferencedName()))
                     {
                         throw new JavaScriptException(_engine.SyntaxError);
                     }
@@ -943,7 +944,7 @@ namespace Jint.Runtime
                     if (r != null
                         && r.IsStrict()
                         && (r.GetBase().TryCast<EnvironmentRecord>() != null)
-                        && (Array.IndexOf(new[] { "eval", "arguments" }, r.GetReferencedName()) != -1))
+                        && ("eval" == r.GetReferencedName() || "arguments" == r.GetReferencedName()))
                     {
                         throw new JavaScriptException(_engine.SyntaxError);
                     }
@@ -966,7 +967,7 @@ namespace Jint.Runtime
 
         public JsValue EvaluateNewExpression(NewExpression newExpression)
         {
-            var arguments = newExpression.Arguments.Select(x => EvaluateExpression(x.As<Expression>())).Select(_engine.GetValue).ToArray();
+            var arguments = BuildArguments(newExpression.Arguments, out bool _);
 
             // todo: optimize by defining a common abstract class or interface
             var callee = _engine.GetValue(EvaluateExpression(newExpression.Callee)).TryCast<IConstructor>();
@@ -984,17 +985,18 @@ namespace Jint.Runtime
 
         public JsValue EvaluateArrayExpression(ArrayExpression arrayExpression)
         {
-            var a = _engine.Array.Construct(new JsValue[] { arrayExpression.Elements.Count() });
-            var n = 0;
-            foreach (var expr in arrayExpression.Elements)
+            var elements = (List<ArrayExpressionElement>) arrayExpression.Elements;
+            var count = elements.Count;
+            var a = _engine.Array.Construct(new JsValue[] { count });
+            for (var n = 0; n < count; n++)
             {
+                var expr = elements[n];
                 if (expr != null)
                 {
                     var value = _engine.GetValue(EvaluateExpression(expr.As<Expression>()));
                     a.DefineOwnProperty(TypeConverter.ToString(n),
                         new PropertyDescriptor(value, true, true, true), false);
                 }
-                n++;
             }
 
             return a;
@@ -1085,5 +1087,21 @@ namespace Jint.Runtime
                     throw new ArgumentException();
             }
         }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private JsValue[] BuildArguments(List<ArgumentListElement> expressionArguments, out bool allLiteral)
+        {
+            allLiteral = true;
+            var count = expressionArguments.Count;
+            var arguments = new JsValue[count];
+            for (var i = 0; i < count; i++)
+            {
+                var argument = (Expression) expressionArguments[i];
+                arguments[i] = _engine.GetValue(EvaluateExpression(argument));
+                allLiteral &= argument is Literal;
+            }
+
+            return arguments;
+        }
     }
 }

+ 27 - 15
Jint/Runtime/Interop/MethodInfoFunctionInstance.cs

@@ -1,11 +1,8 @@
 using System;
-using System.Collections.Generic;
 using System.Globalization;
-using System.Linq;
 using System.Linq.Expressions;
 using System.Reflection;
 using Jint.Native;
-using Jint.Native.Array;
 using Jint.Native.Function;
 
 namespace Jint.Runtime.Interop
@@ -29,10 +26,9 @@ namespace Jint.Runtime.Interop
         public JsValue Invoke(MethodInfo[] methodInfos, JsValue thisObject, JsValue[] jsArguments)
         {
             var arguments = ProcessParamsArrays(jsArguments, methodInfos);
-            var methods = TypeConverter.FindBestMatch(Engine, methodInfos, arguments).ToList();
             var converter = Engine.ClrTypeConverter;
 
-            foreach (var method in methods)
+            foreach (var method in TypeConverter.FindBestMatch(Engine, methodInfos, arguments))
             {
                 var parameters = new object[arguments.Length];
                 var argumentsMatch = true;
@@ -86,7 +82,7 @@ namespace Jint.Runtime.Interop
                 // todo: cache method info
                 try
                 {
-                    return JsValue.FromObject(Engine, method.Invoke(thisObject.ToObject(), parameters.ToArray()));
+                    return JsValue.FromObject(Engine, method.Invoke(thisObject.ToObject(), parameters));
                 }
                 catch (TargetInvocationException exception)
                 {
@@ -108,29 +104,45 @@ namespace Jint.Runtime.Interop
         /// <summary>
         /// Reduces a flat list of parameters to a params array
         /// </summary>
-        private JsValue[] ProcessParamsArrays(JsValue[] jsArguments, IEnumerable<MethodInfo> methodInfos)
+        private JsValue[] ProcessParamsArrays(JsValue[] jsArguments, MethodInfo[] methodInfos)
         {
-            foreach (var methodInfo in methodInfos)
+            for (var i = 0; i < methodInfos.Length; i++)
             {
+                var methodInfo = methodInfos[i];
                 var parameters = methodInfo.GetParameters();
-                if (!parameters.Any(p => p.HasAttribute<ParamArrayAttribute>()))
+
+                bool hasParamArrayAttribute = false;
+                for (int j = 0; j < parameters.Length; ++j)
+                {
+                    if (parameters[j].HasAttribute<ParamArrayAttribute>())
+                    {
+                        hasParamArrayAttribute = true;
+                        break;
+                    }
+                }
+                if (!hasParamArrayAttribute)
                     continue;
 
                 var nonParamsArgumentsCount = parameters.Length - 1;
                 if (jsArguments.Length < nonParamsArgumentsCount)
                     continue;
 
-                var newArgumentsCollection = jsArguments.Take(nonParamsArgumentsCount).ToList();
-                var argsToTransform = jsArguments.Skip(nonParamsArgumentsCount).ToList();
+                var argsToTransform = jsArguments.Skip(nonParamsArgumentsCount);
 
-                if (argsToTransform.Count == 1 && argsToTransform.FirstOrDefault().IsArray())
+                if (argsToTransform.Length == 1 && argsToTransform[0].IsArray())
                     continue;
 
                 var jsArray = Engine.Array.Construct(Arguments.Empty);
-                Engine.Array.PrototypeObject.Push(jsArray, argsToTransform.ToArray());
+                Engine.Array.PrototypeObject.Push(jsArray, argsToTransform);
+
+                var newArgumentsCollection = new JsValue[nonParamsArgumentsCount + 1];
+                for (var j = 0; j < nonParamsArgumentsCount; ++j)
+                {
+                    newArgumentsCollection[j] = jsArguments[j];
+                }
 
-                newArgumentsCollection.Add(jsArray.JsValue);
-                return newArgumentsCollection.ToArray();
+                newArgumentsCollection[nonParamsArgumentsCount] = new JsValue(jsArray);
+                return newArgumentsCollection;
             }
 
             return jsArguments;

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

@@ -53,7 +53,7 @@ namespace Jint.Runtime.Interop
         public override PropertyDescriptor GetOwnProperty(string propertyName)
         {
             PropertyDescriptor x;
-            if (Properties.TryGetValue(propertyName, out x))
+            if (TryGetProperty(propertyName, out x))
                 return x;
 
             var type = Target.GetType();
@@ -65,7 +65,7 @@ namespace Jint.Runtime.Interop
             if (property != null)
             {
                 var descriptor = new PropertyInfoDescriptor(Engine, property, Target);
-                Properties.Add(propertyName, descriptor);
+                AddProperty(propertyName, descriptor);
                 return descriptor;
             }
 
@@ -76,11 +76,11 @@ namespace Jint.Runtime.Interop
             if (field != null)
             {
                 var descriptor = new FieldInfoDescriptor(Engine, field, Target);
-                Properties.Add(propertyName, descriptor);
+                AddProperty(propertyName, descriptor);
                 return descriptor;
             }
 
-            // if no properties were found then look for a method 
+            // if no properties were found then look for a method
             var methods = type.GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public)
                 .Where(m => EqualsIgnoreCasing(m.Name, propertyName))
                 .ToArray();
@@ -88,7 +88,7 @@ namespace Jint.Runtime.Interop
             if (methods.Any())
             {
                 var descriptor = new PropertyDescriptor(new MethodInfoFunctionInstance(Engine, methods), false, true, false);
-                Properties.Add(propertyName, descriptor);
+                AddProperty(propertyName, descriptor);
                 return descriptor;
             }
 
@@ -109,7 +109,7 @@ namespace Jint.Runtime.Interop
             if (explicitProperties.Length == 1)
             {
                 var descriptor = new PropertyInfoDescriptor(Engine, explicitProperties[0], Target);
-                Properties.Add(propertyName, descriptor);
+                AddProperty(propertyName, descriptor);
                 return descriptor;
             }
 
@@ -122,7 +122,7 @@ namespace Jint.Runtime.Interop
             if (explicitMethods.Length > 0)
             {
                 var descriptor = new PropertyDescriptor(new MethodInfoFunctionInstance(Engine, explicitMethods), false, true, false);
-                Properties.Add(propertyName, descriptor);
+                AddProperty(propertyName, descriptor);
                 return descriptor;
             }
 
@@ -146,11 +146,11 @@ namespace Jint.Runtime.Interop
             bool equals = false;
             if (s1.Length == s2.Length)
             {
-                if (s1.Length > 0 && s2.Length > 0) 
+                if (s1.Length > 0 && s2.Length > 0)
                 {
                     equals = (s1.ToLower()[0] == s2.ToLower()[0]);
                 }
-                if (s1.Length > 1 && s2.Length > 1) 
+                if (s1.Length > 1 && s2.Length > 1)
                 {
                     equals = equals && (s1.Substring(1) == s2.Substring(1));
                 }

+ 77 - 76
Jint/Runtime/MruPropertyCache2.cs

@@ -1,146 +1,147 @@
-using System.Collections;
-using System.Collections.Generic;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
 
 namespace Jint.Runtime
 {
-    public class MruPropertyCache2<TKey, TValue> : IDictionary<TKey, TValue> 
-        where TValue:class
+    internal class MruPropertyCache2<TKey, TValue> where TValue : class
     {
-        private IDictionary<TKey, TValue> _dictionary = new Dictionary<TKey, TValue>();
+        private Dictionary<TKey, TValue> _dictionary;
         private bool _set;
         private TKey _key;
         private TValue _value;
 
-        public MruPropertyCache2() {
-        }
-
-        public TValue this[TKey key] {
-            get {
+        public TValue this[TKey key]
+        {
+            get
+            {
                 if (_set && key.Equals(_key))
                 {
                     return _value;
                 }
 
-                return _dictionary[key];
+                return _dictionary != null ? _dictionary[key] : null;
             }
 
-            set {
+            set
+            {
+                EnsureInitialized(key);
                 _set = true;
                 _key = key;
                 _value = value;
 
-                _dictionary[key] = value;
-            }
-        }
-
-        public int Count {
-            get {
-                return _dictionary.Count;
-            }
-        }
-
-        public bool IsReadOnly {
-            get {
-                return _dictionary.IsReadOnly;
+                if (_dictionary != null)
+                {
+                    _dictionary[key] = value;
+                }
             }
         }
 
-        public ICollection<TKey> Keys {
-            get {
-                return _dictionary.Keys;
-            }
-        }
+        public int Count
+        {
+            get
+            {
+                if (_dictionary != null)
+                {
+                    return _dictionary.Count;
+                }
 
-        public ICollection<TValue> Values {
-            get {
-                return _dictionary.Values;
+                return _set ? 1 : 0;
             }
         }
 
-        public void Add(KeyValuePair<TKey, TValue> item) {
-            _set = true;
-            _key = item.Key;
-            _value = item.Value;
-
-            _dictionary.Add(item);
-        }
-
-        public void Add(TKey key, TValue value) {
+        public void Add(TKey key, TValue value)
+        {
+            EnsureInitialized(key);
             _set = true;
             _key = key;
             _value = value;
 
-            _dictionary.Add(key, value);
+            if (_dictionary != null)
+            {
+                _dictionary.Add(key, value);
+            }
         }
 
-        public void Clear() {
+        public void Clear()
+        {
             _set = false;
             _key = default(TKey);
             _value = null;
 
-            _dictionary.Clear();
+            _dictionary?.Clear();
         }
 
-        public bool Contains(KeyValuePair<TKey, TValue> item) {
-            if(_set && item.Key.Equals(_key))
-            {
-                return true;
-            }
-
-            return _dictionary.Contains(item);
-        }
-
-        public bool ContainsKey(TKey key) {
+        public bool ContainsKey(TKey key)
+        {
             if (_set && key.Equals(_key))
             {
                 return true;
             }
 
-            return _dictionary.ContainsKey(key);
+            return _dictionary != null && _dictionary.ContainsKey(key);
         }
 
-        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
-            _dictionary.CopyTo(array, arrayIndex);
-        }
+        public IEnumerable<KeyValuePair<TKey, TValue>> GetEnumerator()
+        {
+            if (_dictionary == null)
+            {
+                if (_set)
+                {
+                    yield return new KeyValuePair<TKey, TValue>(_key, _value);
+                }
 
-        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
-            return _dictionary.GetEnumerator();
-        }
+                yield break;
+            }
 
-        public bool Remove(KeyValuePair<TKey, TValue> item) {
-            if(_set && item.Key.Equals(_key))
+            foreach (var pair in _dictionary)
             {
-                _set = false;
-                _key = default(TKey);
-                _value = null;
+                yield return pair;
             }
-
-            return _dictionary.Remove(item);
         }
 
-        public bool Remove(TKey key) {
+        public bool Remove(TKey key)
+        {
+            bool removed = false;
             if (_set && key.Equals(_key))
             {
                 _set = false;
                 _key = default(TKey);
                 _value = null;
+                removed = true;
             }
 
-            return _dictionary.Remove(key);
+            _dictionary?.Remove(key);
+            return removed;
         }
 
-        public bool TryGetValue(TKey key, out TValue value) {
-            if (_set && key.Equals(_key))
+        public bool TryGetValue(TKey key, out TValue value)
+        {
+            if (_set && _key.Equals(key))
             {
                 value = _value;
                 return true;
             }
 
+            if (_dictionary == null)
+            {
+                value = null;
+                return false;
+            }
+
             return _dictionary.TryGetValue(key, out value);
         }
 
-        IEnumerator IEnumerable.GetEnumerator() {
-            return _dictionary.GetEnumerator();
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private void EnsureInitialized(TKey key)
+        {
+            if (_set && !Equals(_key, key))
+            {
+                if (_dictionary == null)
+                {
+                    _dictionary = new Dictionary<TKey, TValue>();
+                }
+                _dictionary[_key] = _value;
+            }
         }
     }
-}
+}

+ 9 - 7
Jint/Runtime/StatementInterpreter.cs

@@ -25,7 +25,7 @@ namespace Jint.Runtime
 
         public Completion ExecuteEmptyStatement(EmptyStatement emptyStatement)
         {
-            return new Completion(Completion.Normal, null, null);
+            return Completion.Empty;
         }
 
         public Completion ExecuteExpressionStatement(ExpressionStatement expressionStatement)
@@ -49,7 +49,7 @@ namespace Jint.Runtime
             }
             else
             {
-                return new Completion(Completion.Normal, null, null);
+                return Completion.Empty;
             }
 
             return result;
@@ -217,7 +217,7 @@ namespace Jint.Runtime
             var experValue = _engine.GetValue(exprRef);
             if (experValue == Undefined.Instance || experValue == Null.Instance)
             {
-                return new Completion(Completion.Normal, null, null);
+                return Completion.Empty;
             }
 
 
@@ -415,13 +415,15 @@ namespace Jint.Runtime
 
         public Completion ExecuteStatementList(IEnumerable<StatementListItem> statementList)
         {
-            var c = new Completion(Completion.Normal, null, null);
+            var c = Completion.Empty;
             Completion sl = c;
             Statement s = null;
 
+            var list = (List<StatementListItem>) statementList;
+
             try
             {
-                foreach (var statement in statementList)
+                foreach (var statement in list)
                 {
                     s = statement.As<Statement>();
                     c = ExecuteStatement(s);
@@ -528,7 +530,7 @@ namespace Jint.Runtime
                 }
             }
 
-            return new Completion(Completion.Normal, Undefined.Instance, null);
+            return Completion.EmptyUndefined;
         }
 
         public Completion ExecuteBlockStatement(BlockStatement blockStatement)
@@ -548,7 +550,7 @@ namespace Jint.Runtime
                 System.Diagnostics.Debugger.Break();
             }
 
-            return new Completion(Completion.Normal, null, null);
+            return Completion.Empty;
         }
     }
 }

+ 1 - 1
Jint/Runtime/TypeConverter.cs

@@ -440,7 +440,7 @@ namespace Jint.Runtime
         public static IEnumerable<MethodBase> FindBestMatch(Engine engine, MethodBase[] methods, JsValue[] arguments)
         {
             methods = methods
-                .Where(m => m.GetParameters().Count() == arguments.Length)
+                .Where(m => m.GetParameters().Length == arguments.Length)
                 .ToArray();
 
             if (methods.Length == 1 && !methods[0].GetParameters().Any())