Browse Source

Fix cached data structures to be thread-local (#476)

Marko Lahma 7 năm trước cách đây
mục cha
commit
8259c62213

+ 13 - 0
Jint/Native/Array/ArrayExecutionContext.cs

@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+using System.Text;
 using System.Threading;
 
 namespace Jint.Native.Array
@@ -12,6 +13,11 @@ namespace Jint.Native.Array
         private static readonly ThreadLocal<ArrayExecutionContext> _executionContext = new ThreadLocal<ArrayExecutionContext>(() => new ArrayExecutionContext());
 
         private List<uint> _keyCache;
+        private JsValue[] _callArray1;
+        private JsValue[] _callArray2;
+        private JsValue[] _callArray3;
+        private JsValue[] _callArray4;
+        private StringBuilder _stringBuilder;
 
         private ArrayExecutionContext()
         {
@@ -19,6 +25,13 @@ namespace Jint.Native.Array
 
         public List<uint> KeyCache => _keyCache = _keyCache ?? new List<uint>();
 
+        public JsValue[] CallArray1 => _callArray1 = _callArray1 ?? new JsValue[1];
+        public JsValue[] CallArray2 => _callArray2 = _callArray2 ?? new JsValue[2];
+        public JsValue[] CallArray3 => _callArray3 = _callArray3 ?? new JsValue[3];
+        public JsValue[] CallArray4 => _callArray4 = _callArray4 ?? new JsValue[4];
+
+        public StringBuilder StringBuilder => _stringBuilder = _stringBuilder ?? new StringBuilder();
+
         public static ArrayExecutionContext Current => _executionContext.Value;
     }
 }

+ 14 - 20
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)
         {
         }
@@ -151,7 +144,7 @@ namespace Jint.Native.Array
             }
 
 
-            var args = _callArray4 = _callArray4 ?? new JsValue[4];
+            var args = ArrayExecutionContext.Current.CallArray4;
             while (k < len)
             {
                 var i = (uint) k;
@@ -183,7 +176,7 @@ namespace Jint.Native.Array
             var a = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
 
             uint to = 0;
-            var jsValues = _callArray3 = _callArray3 ?? new JsValue[3];
+            var jsValues = ArrayExecutionContext.Current.CallArray3;
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -213,10 +206,10 @@ 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];
+            var args = ArrayExecutionContext.Current.CallArray1;
             args[0] = len;
             var a = Engine.Array.Construct(args, len);
-            var jsValues = _callArray3 = _callArray3 ?? new JsValue[3];
+            var jsValues =ArrayExecutionContext.Current.CallArray3;
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -242,7 +235,7 @@ 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 jsValues = ArrayExecutionContext.Current.CallArray3;
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -267,7 +260,7 @@ 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 jsValues = ArrayExecutionContext.Current.CallArray3;
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -296,7 +289,7 @@ 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 jsValues = ArrayExecutionContext.Current.CallArray3;
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -531,7 +524,7 @@ namespace Jint.Native.Array
 
                 if (compareFn != null)
                 {
-                    var args = _callArray2 = _callArray2 ?? new JsValue[2];
+                    var args = ArrayExecutionContext.Current.CallArray2;
                     args[0] = x;
                     args[1] = y;
                     var s = TypeConverter.ToNumber(compareFn.Call(Undefined, args));
@@ -727,15 +720,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)

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

@@ -0,0 +1,43 @@
+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 JsValue[] _callArray3;
+
+        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 JsValue[] CallArray3 => _callArray3 = _callArray3 ?? new JsValue[3];
+
+        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 = StringExecutionContext.Current.CallArray3;
                 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);