Browse Source

Merge pull request #477 from sebastienros/dev

Merge dev to rel/3.0-beta
Sébastien Ros 7 years ago
parent
commit
3cb0455d8e

+ 3 - 3
Jint/Native/Array/ArrayExecutionContext.cs

@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Text;
 using System.Threading;
 
 namespace Jint.Native.Array
@@ -11,13 +11,13 @@ namespace Jint.Native.Array
         // cache key container for array iteration for less allocations
         private static readonly ThreadLocal<ArrayExecutionContext> _executionContext = new ThreadLocal<ArrayExecutionContext>(() => new ArrayExecutionContext());
 
-        private List<uint> _keyCache;
+        private StringBuilder _stringBuilder;
 
         private ArrayExecutionContext()
         {
         }
 
-        public List<uint> KeyCache => _keyCache = _keyCache ?? new List<uint>();
+        public StringBuilder StringBuilder => _stringBuilder = _stringBuilder ?? new StringBuilder();
 
         public static ArrayExecutionContext Current => _executionContext.Value;
     }

+ 1 - 4
Jint/Native/Array/ArrayInstance.cs

@@ -183,10 +183,7 @@ namespace Jint.Native.Array
                     {
                         // in the case of sparse arrays, treat each concrete element instead of
                         // iterating over all indexes
-
-                        var keys = ArrayExecutionContext.Current.KeyCache;
-                        keys.Clear();
-                        keys.AddRange(_sparse.Keys);
+                        var keys = new List<uint>(_sparse.Keys);
                         foreach (var keyIndex in keys)
                         {
                             // is it the index of the array

+ 34 - 46
Jint/Native/Array/ArrayPrototype.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Text;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
@@ -14,12 +13,6 @@ namespace Jint.Native.Array
     /// </summary>
     public sealed class ArrayPrototype : ArrayInstance
     {
-        private readonly StringBuilder _arrayJoinBuilder = new StringBuilder();
-        private JsValue[] _callArray1;
-        private JsValue[] _callArray2;
-        private JsValue[] _callArray3;
-        private JsValue[] _callArray4;
-
         private ArrayPrototype(Engine engine) : base(engine)
         {
         }
@@ -150,8 +143,7 @@ namespace Jint.Native.Array
                 }
             }
 
-
-            var args = _callArray4 = _callArray4 ?? new JsValue[4];
+            var args = new JsValue[4];
             while (k < len)
             {
                 var i = (uint) k;
@@ -183,15 +175,15 @@ namespace Jint.Native.Array
             var a = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
 
             uint to = 0;
-            var jsValues = _callArray3 = _callArray3 ?? new JsValue[3];
+            var args = new JsValue[3];
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
                 {
-                    jsValues[0] = kvalue;
-                    jsValues[1] = k;
-                    jsValues[2] = o.Target;
-                    var selected = callable.Call(thisArg, jsValues);
+                    args[0] = kvalue;
+                    args[1] = k;
+                    args[2] = o.Target;
+                    var selected = callable.Call(thisArg, args);
                     if (TypeConverter.ToBoolean(selected))
                     {
                         a.SetIndexValue(to, kvalue, throwOnError: false);
@@ -213,18 +205,16 @@ namespace Jint.Native.Array
 
             var callable = callbackfn.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "Argument must be callable"));
 
-            var args = _callArray1 = _callArray1 ?? new JsValue[1];
-            args[0] = len;
-            var a = Engine.Array.Construct(args, len);
-            var jsValues = _callArray3 = _callArray3 ?? new JsValue[3];
+            var a = Engine.Array.Construct(new JsValue[] {len}, len);
+            var args = new JsValue[3];
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
                 {
-                    jsValues[0] = kvalue;
-                    jsValues[1] = k;
-                    jsValues[2] = o.Target;
-                    var mappedValue = callable.Call(thisArg, jsValues);
+                    args[0] = kvalue;
+                    args[1] = k;
+                    args[2] = o.Target;
+                    var mappedValue = callable.Call(thisArg, args);
                     a.SetIndexValue(k, mappedValue, throwOnError: false);
                 }
             }
@@ -242,15 +232,15 @@ namespace Jint.Native.Array
 
             var callable = callbackfn.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "Argument must be callable"));
 
-            var jsValues = _callArray3 = _callArray3 ?? new JsValue[3];
+            var args = new JsValue[3];
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
                 {
-                    jsValues[0] = kvalue;
-                    jsValues[1] = k;
-                    jsValues[2] = o.Target;
-                    callable.Call(thisArg, jsValues);
+                    args[0] = kvalue;
+                    args[1] = k;
+                    args[2] = o.Target;
+                    callable.Call(thisArg, args);
                 }
             }
 
@@ -267,15 +257,15 @@ namespace Jint.Native.Array
 
             var callable = callbackfn.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "Argument must be callable"));
 
-            var jsValues = _callArray3 = _callArray3 ?? new JsValue[3];
+            var args = new JsValue[3];
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
                 {
-                    jsValues[0] = kvalue;
-                    jsValues[1] = k;
-                    jsValues[2] = o.Target;
-                    var testResult = callable.Call(thisArg, jsValues);
+                    args[0] = kvalue;
+                    args[1] = k;
+                    args[2] = o.Target;
+                    var testResult = callable.Call(thisArg, args);
                     if (TypeConverter.ToBoolean(testResult))
                     {
                         return true;
@@ -296,15 +286,15 @@ namespace Jint.Native.Array
 
             var callable = callbackfn.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "Argument must be callable"));
 
-            var jsValues = _callArray3 = _callArray3 ?? new JsValue[3];
+            var args = new JsValue[3];
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
                 {
-                    jsValues[0] = kvalue;
-                    jsValues[1] = k;
-                    jsValues[2] = o.Target;
-                    var testResult = callable.Call(thisArg, jsValues);
+                    args[0] = kvalue;
+                    args[1] = k;
+                    args[2] = o.Target;
+                    var testResult = callable.Call(thisArg, args);
                     if (false == TypeConverter.ToBoolean(testResult))
                     {
                         return JsBoolean.False;
@@ -531,10 +521,7 @@ namespace Jint.Native.Array
 
                 if (compareFn != null)
                 {
-                    var args = _callArray2 = _callArray2 ?? new JsValue[2];
-                    args[0] = x;
-                    args[1] = y;
-                    var s = TypeConverter.ToNumber(compareFn.Call(Undefined, args));
+                    var s = TypeConverter.ToNumber(compareFn.Call(Undefined, new[] {x, y}));
                     if (s < 0)
                     {
                         return -1;
@@ -727,15 +714,16 @@ namespace Jint.Native.Array
                 return s;
             }
 
-            _arrayJoinBuilder.Clear();
-            _arrayJoinBuilder.Append(s);
+            var sb = ArrayExecutionContext.Current.StringBuilder;
+            sb.Clear();
+            sb.Append(s);
             for (uint k = 1; k < len; k++)
             {
-                _arrayJoinBuilder.Append(sep);
-                _arrayJoinBuilder.Append(StringFromJsValue(o.Get(k)));
+                sb.Append(sep);
+                sb.Append(StringFromJsValue(o.Get(k)));
             }
 
-            return _arrayJoinBuilder.ToString();
+            return sb.ToString();
         }
 
         private JsValue ToLocaleString(JsValue thisObj, JsValue[] arguments)

+ 10 - 3
Jint/Native/Object/ObjectInstance.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Diagnostics.Contracts;
 using System.Dynamic;
+using System.Runtime.CompilerServices;
 using Jint.Native.Array;
 using Jint.Native.Boolean;
 using Jint.Native.Date;
@@ -136,7 +137,12 @@ namespace Jint.Native.Object
         public virtual JsValue Get(string propertyName)
         {
             var desc = GetProperty(propertyName);
+            return UnwrapJsValue(desc);
+        }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal JsValue UnwrapJsValue(IPropertyDescriptor desc)
+        {
             if (desc == PropertyDescriptor.Undefined)
             {
                 return Undefined;
@@ -145,11 +151,10 @@ namespace Jint.Native.Object
             if (desc.IsDataDescriptor())
             {
                 var val = desc.Value;
-                return val != null ? val : Undefined;
+                return !ReferenceEquals(val, null) ? val : Undefined;
             }
 
-            var getter = desc.Get != null ? desc.Get : Undefined;
-
+            var getter = !ReferenceEquals(desc.Get, null) ? desc.Get : Undefined;
             if (getter.IsUndefined())
             {
                 return Undefined;
@@ -197,6 +202,7 @@ namespace Jint.Native.Object
         /// </summary>
         /// <param name="propertyName"></param>
         /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public IPropertyDescriptor GetProperty(string propertyName)
         {
             var prop = GetOwnProperty(propertyName);
@@ -367,6 +373,7 @@ namespace Jint.Native.Object
         /// </summary>
         /// <param name="propertyName"></param>
         /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool HasProperty(string propertyName)
         {
             return GetProperty(propertyName) != PropertyDescriptor.Undefined;

+ 41 - 0
Jint/Native/String/StringExecutionContext.cs

@@ -0,0 +1,41 @@
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+
+namespace Jint.Native.String
+{
+    /// <summary>
+    /// Helper to cache common data structures when manipulating strings.
+    /// </summary>
+    internal class StringExecutionContext
+    {
+        private static readonly ThreadLocal<StringExecutionContext> _executionContext = new ThreadLocal<StringExecutionContext>(() => new StringExecutionContext());
+
+        private StringBuilder _stringBuilder;
+        private List<string> _splitSegmentList;
+        private string[] _splitArray1;
+
+        private StringExecutionContext()
+        {
+        }
+
+        public StringBuilder GetStringBuilder(int capacity)
+        {
+            if (_stringBuilder == null)
+            {
+                _stringBuilder = new StringBuilder(capacity);
+            }
+            else
+            {
+                _stringBuilder.EnsureCapacity(capacity);
+            }
+
+            return _stringBuilder;
+        }
+
+        public List<string> SplitSegmentList => _splitSegmentList = _splitSegmentList ?? new List<string>();
+        public string[] SplitArray1 => _splitArray1 = _splitArray1 ?? new string[1];
+
+        public static StringExecutionContext Current => _executionContext.Value;
+    }
+}

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

@@ -1,8 +1,6 @@
 using System;
-using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.CompilerServices;
-using System.Text;
 using Jint.Native.Array;
 using Jint.Native.Function;
 using Jint.Native.Object;
@@ -18,11 +16,6 @@ namespace Jint.Native.String
     /// </summary>
     public sealed class StringPrototype : StringInstance
     {
-        private StringBuilder _stringBuilder;
-        private List<string> _splitSegmentList;
-        private JsValue[] _callArray3;
-        private string[] _splitArray1;
-
         private StringPrototype(Engine engine)
             : base(engine)
         {
@@ -67,20 +60,6 @@ namespace Jint.Native.String
             FastAddProperty("padEnd", new ClrFunctionInstance(Engine, PadEnd), true, false, true);
         }
 
-        private StringBuilder GetStringBuilder(int capacity)
-        {
-            if (_stringBuilder == null)
-            {
-                _stringBuilder = new StringBuilder(capacity);
-            }
-            else
-            {
-                _stringBuilder.EnsureCapacity(capacity);
-            }
-
-            return _stringBuilder;
-        }
-
         private JsValue ToStringString(JsValue thisObj, JsValue[] arguments)
         {
             var s = TypeConverter.ToObject(Engine, thisObj) as StringInstance;
@@ -363,7 +342,7 @@ namespace Jint.Native.String
             }
             else
             {
-                var segments = _splitSegmentList = _splitSegmentList ?? new List<string>();
+                var segments = StringExecutionContext.Current.SplitSegmentList;
                 segments.Clear();
                 var sep = TypeConverter.ToString(separator);
 
@@ -380,7 +359,7 @@ namespace Jint.Native.String
                 }
                 else
                 {
-                    var array = _splitArray1 = _splitArray1 ?? new string[1];
+                    var array = StringExecutionContext.Current.SplitArray1;
                     array[0] = sep;
                     segments.AddRange(s.Split(array, StringSplitOptions.None));
                 }
@@ -493,7 +472,7 @@ namespace Jint.Native.String
                     // $`	Inserts the portion of the string that precedes the matched substring.
                     // $'	Inserts the portion of the string that follows the matched substring.
                     // $n or $nn	Where n or nn are decimal digits, inserts the nth parenthesized submatch string, provided the first argument was a RegExp object.
-                    var replacementBuilder = GetStringBuilder(0);
+                    var replacementBuilder = StringExecutionContext.Current.GetStringBuilder(0);
                     replacementBuilder.Clear();
                     for (int i = 0; i < replaceString.Length; i++)
                     {
@@ -601,7 +580,7 @@ namespace Jint.Native.String
                     return thisString;
                 int end = start + substr.Length;
 
-                var args = _callArray3 = _callArray3 ?? new JsValue[3];
+                var args = new JsValue[3];
                 args[0] = substr;
                 args[1] = start;
                 args[2] = thisString;
@@ -609,7 +588,7 @@ namespace Jint.Native.String
                 var replaceString = TypeConverter.ToString(replaceFunction.Call(Undefined, args));
 
                 // Replace only the first match.
-                var result = GetStringBuilder(thisString.Length + (substr.Length - substr.Length));
+                var result = StringExecutionContext.Current.GetStringBuilder(thisString.Length + (substr.Length - substr.Length));
                 result.Clear();
                 result.Append(thisString, 0, start);
                 result.Append(replaceString);

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

@@ -1,4 +1,5 @@
-using Jint.Native;
+using System;
+using Jint.Native;
 
 namespace Jint.Runtime.Environments
 {
@@ -13,7 +14,7 @@ namespace Jint.Runtime.Environments
         private const string BindingNameArguments = "arguments";
         private Binding _argumentsBinding;
 
-        private readonly MruPropertyCache2<Binding> _bindings = new MruPropertyCache2<Binding>();
+        private MruPropertyCache2<Binding> _bindings;
 
         public DeclarativeEnvironmentRecord(Engine engine) : base(engine)
         {
@@ -27,7 +28,7 @@ namespace Jint.Runtime.Environments
                 return _argumentsBinding != null;
             }
 
-            return _bindings.ContainsKey(name);
+            return _bindings?.ContainsKey(name) == true;
         }
 
         public override void CreateMutableBinding(string name, bool canBeDeleted = false)
@@ -45,12 +46,21 @@ namespace Jint.Runtime.Environments
             }
             else
             {
+                if (_bindings == null)
+                {
+                    _bindings = new MruPropertyCache2<Binding>();
+                }
                 _bindings.Add(name, binding);
             }
         }
 
         public override void SetMutableBinding(string name, JsValue value, bool strict)
         {
+            if (_bindings == null)
+            {
+                _bindings = new MruPropertyCache2<Binding>();
+            }
+
             var binding = name == BindingNameArguments ? _argumentsBinding : _bindings[name];
             if (binding.Mutable)
             {
@@ -84,14 +94,14 @@ namespace Jint.Runtime.Environments
 
         public override bool DeleteBinding(string name)
         {
-            Binding binding;
+            Binding binding = null;
             if (name == BindingNameArguments)
             {
                 binding = _argumentsBinding;
             }
             else
             {
-                _bindings.TryGetValue(name, out binding);
+                _bindings?.TryGetValue(name, out binding);
             }
 
             if (binding == null)
@@ -140,6 +150,10 @@ namespace Jint.Runtime.Environments
             }
             else
             {
+                if (_bindings == null)
+                {
+                    _bindings = new MruPropertyCache2<Binding>();
+                }
                 _bindings.Add(name, binding);
             }
         }
@@ -161,7 +175,7 @@ namespace Jint.Runtime.Environments
         /// <returns>The array of all defined bindings</returns>
         public override string[] GetAllBindingNames()
         {
-            return _bindings.GetKeys();
+            return _bindings?.GetKeys() ?? Array.Empty<string>();
         }
     }
 }

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

@@ -32,22 +32,25 @@ namespace Jint.Runtime.Environments
 
         public static Reference GetIdentifierReference(LexicalEnvironment lex, string name, bool strict)
         {
-            if (lex == null)
+            while (true)
             {
-                return new Reference(Undefined.Instance, name, strict);
-            }
+                if (lex == null)
+                {
+                    return new Reference(Undefined.Instance, name, strict);
+                }
 
-            if (lex.Record.HasBinding(name))
-            {
-                return new Reference(lex.Record, name, strict);
-            }
+                if (lex.Record.HasBinding(name))
+                {
+                    return new Reference(lex.Record, name, strict);
+                }
 
-            if (lex.Outer == null)
-            {
-                return new Reference(Undefined.Instance, name, strict);    
-            }
+                if (lex.Outer == null)
+                {
+                    return new Reference(Undefined.Instance, name, strict);
+                }
 
-            return GetIdentifierReference(lex.Outer, name, strict);
+                lex = lex.Outer;
+            }
         }
 
         public static LexicalEnvironment NewDeclarativeEnvironment(Engine engine, LexicalEnvironment outer = null)

+ 3 - 9
Jint/Runtime/Environments/ObjectEnvironmentRecord.cs

@@ -50,19 +50,13 @@ namespace Jint.Runtime.Environments
 
         public override JsValue GetBindingValue(string name, bool strict)
         {
-            // todo: can be optimized
-
-            if (!_bindingObject.HasProperty(name))
+            var desc = _bindingObject.GetProperty(name);
+            if (strict && desc == PropertyDescriptor.Undefined)
             {
-                if(!strict)
-                {
-                    return Undefined;
-                }
-
                 throw new JavaScriptException(_engine.ReferenceError);
             }
 
-            return _bindingObject.Get(name);
+            return UnwrapJsValue(desc);
         }
 
         public override bool DeleteBinding(string name)

+ 20 - 23
Jint/Runtime/MruPropertyCache2.cs

@@ -6,6 +6,7 @@ namespace Jint.Runtime
     internal class MruPropertyCache2<TValue> where TValue : class
     {
         private Dictionary<string, TValue> _dictionary;
+        private bool _set;
         private string _key;
         private TValue _value;
 
@@ -13,17 +14,18 @@ namespace Jint.Runtime
         {
             get
             {
-                if (_key != null && _key == key)
+                if (_set && _key == key)
                 {
                     return _value;
                 }
 
-                return _dictionary != null ? _dictionary[key] : null;
+                return _dictionary?[key];
             }
 
             set
             {
                 EnsureInitialized(key);
+                _set = true;
                 _key = key;
                 _value = value;
 
@@ -38,7 +40,7 @@ namespace Jint.Runtime
         {
             get
             {
-                int count = _key != null ? 1 : 0;
+                int count = _set ? 1 : 0;
                 if (_dictionary != null)
                 {
                     count += _dictionary.Count;
@@ -51,17 +53,16 @@ namespace Jint.Runtime
         public void Add(string key, TValue value)
         {
             EnsureInitialized(key);
+            _set = true;
             _key = key;
             _value = value;
 
-            if (_dictionary != null)
-            {
-                _dictionary.Add(key, value);
-            }
+            _dictionary?.Add(key, value);
         }
 
         public void Clear()
         {
+            _set = false;
             _key = default(string);
             _value = null;
 
@@ -70,7 +71,7 @@ namespace Jint.Runtime
 
         public bool ContainsKey(string key)
         {
-            if (_key != null && _key == key)
+            if (_set && key.Equals(_key))
             {
                 return true;
             }
@@ -82,7 +83,7 @@ namespace Jint.Runtime
         {
             if (_dictionary == null)
             {
-                if (_key != null)
+                if (_set)
                 {
                     yield return new KeyValuePair<string, TValue>(_key, _value);
                 }
@@ -99,20 +100,21 @@ namespace Jint.Runtime
         public bool Remove(string key)
         {
             bool removed = false;
-            if (_key != null && _key == key)
+            if (_set && key.Equals(_key))
             {
+                _set = false;
                 _key = null;
                 _value = null;
                 removed = true;
             }
 
-            removed |= _dictionary?.Remove(key) ?? false;
+            _dictionary?.Remove(key);
             return removed;
         }
 
         public bool TryGetValue(string key, out TValue value)
         {
-            if (_key != null && _key == key)
+            if (_set && _key.Equals(key))
             {
                 value = _value;
                 return true;
@@ -124,32 +126,26 @@ namespace Jint.Runtime
                 return false;
             }
 
-            if (_dictionary.TryGetValue(key, out value))
-            {
-                _key = key;
-                _value = value;
-                return true;
-            }
-
-            return false;
+            return _dictionary.TryGetValue(key, out value);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private void EnsureInitialized(string key)
         {
-            if (_key != null && _key != key)
+            if (_set && _key != key)
             {
                 if (_dictionary == null)
                 {
                     _dictionary = new Dictionary<string, TValue>();
                 }
+
                 _dictionary[_key] = _value;
             }
         }
 
         public string[] GetKeys()
         {
-            int size = _key != null ? 1 : 0;
+            int size = _set ? 1 : 0;
             if (_dictionary != null)
             {
                 size += _dictionary.Count;
@@ -157,10 +153,11 @@ namespace Jint.Runtime
 
             var keys = new string[size];
             int n = 0;
-            if (_key != null)
+            if (_set)
             {
                 keys[n++] = _key;
             }
+
             if (_dictionary != null)
             {
                 foreach (var key in _dictionary.Keys)