Skip to content

Commit 4870ac8

Browse files
committed
In JavaScriptEngineSwitcher.Jint fixed a error that occurred during parallel execution of pre-compiled scripts
1 parent 9c144b2 commit 4870ac8

File tree

5 files changed

+195
-16
lines changed

5 files changed

+195
-16
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
The MIT License (MIT)
2+
Copyright (c) 2014 Burtsev Alexey
3+
4+
Permission is hereby granted, free of charge, to any person obtaining a copy
5+
of this software and associated documentation files (the "Software"), to deal
6+
in the Software without restriction, including without limitation the rights
7+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
copies of the Software, and to permit persons to whom the Software is
9+
furnished to do so, subject to the following conditions:
10+
11+
The above copyright notice and this permission notice shall be included in
12+
all copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
SOFTWARE.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/* .NET Object Deep Copy
2+
* https://github.com/Burtsev-Alexey/net-object-deep-copy
3+
*
4+
* Copyright (c) 2014, Burtsev Alexey
5+
* Source code is released under the MIT license.
6+
*/
7+
8+
using System;
9+
10+
namespace JavaScriptEngineSwitcher.Jint.Extensions
11+
{
12+
internal static class ArrayExtensions
13+
{
14+
public static void ForEach(this Array array, Action<Array, int[]> action)
15+
{
16+
if (array.LongLength == 0) return;
17+
ArrayTraverse walker = new ArrayTraverse(array);
18+
do action(array, walker.Position);
19+
while (walker.Step());
20+
}
21+
22+
private class ArrayTraverse
23+
{
24+
public int[] Position;
25+
private int[] maxLengths;
26+
27+
public ArrayTraverse(Array array)
28+
{
29+
maxLengths = new int[array.Rank];
30+
for (int i = 0; i < array.Rank; ++i)
31+
{
32+
maxLengths[i] = array.GetLength(i) - 1;
33+
}
34+
Position = new int[array.Rank];
35+
}
36+
37+
public bool Step()
38+
{
39+
for (int i = 0; i < Position.Length; ++i)
40+
{
41+
if (Position[i] < maxLengths[i])
42+
{
43+
Position[i]++;
44+
for (int j = 0; j < i; j++)
45+
{
46+
Position[j] = 0;
47+
}
48+
return true;
49+
}
50+
}
51+
return false;
52+
}
53+
}
54+
}
55+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/* .NET Object Deep Copy
2+
* https://github.com/Burtsev-Alexey/net-object-deep-copy
3+
*
4+
* Copyright (c) 2014, Burtsev Alexey
5+
* Source code is released under the MIT license.
6+
*/
7+
8+
using System;
9+
using System.Collections.Generic;
10+
using System.Reflection;
11+
12+
namespace JavaScriptEngineSwitcher.Jint.Extensions
13+
{
14+
internal static class ObjectExtensions
15+
{
16+
private static readonly MethodInfo CloneMethod = typeof(Object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance);
17+
18+
public static bool IsPrimitive(this Type type)
19+
{
20+
if (type == typeof(String)) return true;
21+
return (type.IsValueType & type.IsPrimitive);
22+
}
23+
24+
public static Object Copy(this Object originalObject)
25+
{
26+
return InternalCopy(originalObject, new Dictionary<Object, Object>(new ReferenceEqualityComparer()));
27+
}
28+
29+
private static Object InternalCopy(Object originalObject, IDictionary<Object, Object> visited)
30+
{
31+
if (originalObject == null) return null;
32+
var typeToReflect = originalObject.GetType();
33+
if (IsPrimitive(typeToReflect)) return originalObject;
34+
if (visited.ContainsKey(originalObject)) return visited[originalObject];
35+
if (typeof(Delegate).IsAssignableFrom(typeToReflect)) return null;
36+
var cloneObject = CloneMethod.Invoke(originalObject, null);
37+
if (typeToReflect.IsArray)
38+
{
39+
var arrayType = typeToReflect.GetElementType();
40+
if (IsPrimitive(arrayType) == false)
41+
{
42+
Array clonedArray = (Array)cloneObject;
43+
clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), visited), indices));
44+
}
45+
46+
}
47+
visited.Add(originalObject, cloneObject);
48+
CopyFields(originalObject, visited, cloneObject, typeToReflect);
49+
RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect);
50+
return cloneObject;
51+
}
52+
53+
private static void RecursiveCopyBaseTypePrivateFields(object originalObject, IDictionary<object, object> visited, object cloneObject, Type typeToReflect)
54+
{
55+
if (typeToReflect.BaseType != null)
56+
{
57+
RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect.BaseType);
58+
CopyFields(originalObject, visited, cloneObject, typeToReflect.BaseType, BindingFlags.Instance | BindingFlags.NonPublic, info => info.IsPrivate);
59+
}
60+
}
61+
62+
private static void CopyFields(object originalObject, IDictionary<object, object> visited, object cloneObject, Type typeToReflect, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy, Func<FieldInfo, bool> filter = null)
63+
{
64+
foreach (FieldInfo fieldInfo in typeToReflect.GetFields(bindingFlags))
65+
{
66+
if (filter != null && filter(fieldInfo) == false) continue;
67+
if (IsPrimitive(fieldInfo.FieldType)) continue;
68+
var originalFieldValue = fieldInfo.GetValue(originalObject);
69+
var clonedFieldValue = InternalCopy(originalFieldValue, visited);
70+
fieldInfo.SetValue(cloneObject, clonedFieldValue);
71+
}
72+
}
73+
74+
public static T Copy<T>(this T original)
75+
{
76+
return (T)Copy((Object)original);
77+
}
78+
79+
private class ReferenceEqualityComparer : EqualityComparer<Object>
80+
{
81+
public override bool Equals(object x, object y)
82+
{
83+
return ReferenceEquals(x, y);
84+
}
85+
public override int GetHashCode(object obj)
86+
{
87+
if (obj == null) return 0;
88+
return obj.GetHashCode();
89+
}
90+
}
91+
}
92+
}

src/JavaScriptEngineSwitcher.Jint/JavaScriptEngineSwitcher.Jint.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@
4444
<Pack>true</Pack>
4545
<Visible>false</Visible>
4646
</Content>
47+
<Content Include="../../Licenses/net-object-deep-copy-license.txt">
48+
<PackagePath>net-object-deep-copy-license.txt</PackagePath>
49+
<Pack>true</Pack>
50+
<Visible>false</Visible>
51+
</Content>
4752
</ItemGroup>
4853

4954
</Project>

src/JavaScriptEngineSwitcher.Jint/JintJsEngine.cs

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
using WrapperTimeoutException = JavaScriptEngineSwitcher.Core.JsTimeoutException;
3232
using WrapperUsageException = JavaScriptEngineSwitcher.Core.JsUsageException;
3333

34+
using JavaScriptEngineSwitcher.Jint.Extensions;
35+
3436
namespace JavaScriptEngineSwitcher.Jint
3537
{
3638
/// <summary>
@@ -104,9 +106,14 @@ public JintJsEngine(JintSettings settings)
104106
}
105107

106108

107-
private OriginalParserOptions CreateParserOptions(string source)
109+
/// <summary>
110+
/// Creates a Esprima .NET parser options with document name and special settings for the Jint
111+
/// </summary>
112+
/// <param name="documentName">Document name</param>
113+
/// <returns>Esprima .NET parser options with document name and special settings for the Jint</returns>
114+
private static OriginalParserOptions CreateParserOptions(string documentName)
108115
{
109-
var parserOptions = new OriginalParserOptions(source)
116+
var parserOptions = new OriginalParserOptions(documentName)
110117
{
111118
AdaptRegexp = true,
112119
Tolerant = true,
@@ -327,19 +334,16 @@ protected override IPrecompiledScript InnerPrecompile(string code, string docume
327334
{
328335
OriginalProgram program;
329336
string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName);
337+
OriginalParserOptions parserOptions = CreateParserOptions(uniqueDocumentName);
330338

331-
lock (_executionSynchronizer)
339+
try
332340
{
333-
try
334-
{
335-
var parserOptions = CreateParserOptions(uniqueDocumentName);
336-
var parser = new OriginalParser(code, parserOptions);
337-
program = parser.ParseProgram();
338-
}
339-
catch (OriginalParserException e)
340-
{
341-
throw WrapParserException(e);
342-
}
341+
var parser = new OriginalParser(code, parserOptions);
342+
program = parser.ParseProgram();
343+
}
344+
catch (OriginalParserException e)
345+
{
346+
throw WrapParserException(e);
343347
}
344348

345349
return new JintPrecompiledScript(program);
@@ -354,14 +358,14 @@ protected override object InnerEvaluate(string expression, string documentName)
354358
{
355359
object result;
356360
string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName);
361+
OriginalParserOptions parserOptions = CreateParserOptions(uniqueDocumentName);
357362

358363
lock (_executionSynchronizer)
359364
{
360365
OriginalValue resultValue;
361366

362367
try
363368
{
364-
var parserOptions = CreateParserOptions(uniqueDocumentName);
365369
resultValue = _jsEngine.Execute(expression, parserOptions).GetCompletionValue();
366370
}
367371
catch (OriginalParserException e)
@@ -411,12 +415,12 @@ protected override void InnerExecute(string code)
411415
protected override void InnerExecute(string code, string documentName)
412416
{
413417
string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName);
418+
OriginalParserOptions parserOptions = CreateParserOptions(uniqueDocumentName);
414419

415420
lock (_executionSynchronizer)
416421
{
417422
try
418423
{
419-
var parserOptions = CreateParserOptions(uniqueDocumentName);
420424
_jsEngine.Execute(code, parserOptions);
421425
}
422426
catch (OriginalParserException e)
@@ -454,11 +458,14 @@ protected override void InnerExecute(IPrecompiledScript precompiledScript)
454458
);
455459
}
456460

461+
// Create a copy of the instance of `Esprima.Ast.Program` class because the Jint 3 changes its state
462+
OriginalProgram programCopy = jintPrecompiledScript.Program.Copy();
463+
457464
lock (_executionSynchronizer)
458465
{
459466
try
460467
{
461-
_jsEngine.Execute(jintPrecompiledScript.Program);
468+
_jsEngine.Execute(programCopy);
462469
}
463470
catch (OriginalJavaScriptException e)
464471
{

0 commit comments

Comments
 (0)