Skip to content

Commit bc3fe61

Browse files
committed
If errors occur while loading a JS file, don't throw an exception until GetEngine is called.
This ensures that errors are not thrown on a background thread if the engines are being initialised on a background thread (ie. when the engines are being recycled due to files being modified). References #189
1 parent b118156 commit bc3fe61

File tree

6 files changed

+64
-18
lines changed

6 files changed

+64
-18
lines changed

src/React.AspNet/project.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
}
1616
},
1717
"dependencies": {
18-
"JsPool": "0.3.0",
18+
"JsPool": "0.3.1",
1919
"Microsoft.Framework.DependencyInjection": "1.0.0-beta8",
2020
"Microsoft.AspNet.Mvc.Core": "6.0.0-beta8",
2121
"Microsoft.AspNet.StaticFiles": "1.0.0-beta8",

src/React.Core/JavaScriptEngineFactory.cs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ protected readonly ConcurrentDictionary<int, IJsEngine> _engines
4242
/// Whether this class has been disposed.
4343
/// </summary>
4444
protected bool _disposed;
45+
/// <summary>
46+
/// The exception that was thrown during the most recent recycle of the pool.
47+
/// </summary>
48+
protected Exception _scriptLoadException;
4549

4650
/// <summary>
4751
/// Initializes a new instance of the <see cref="JavaScriptEngineFactory"/> class.
@@ -86,7 +90,11 @@ protected virtual IJsPool CreatePool()
8690
poolConfig.StartEngines = _config.StartEngines.Value;
8791
}
8892

89-
return new JsPool(poolConfig);
93+
var pool = new JsPool(poolConfig);
94+
// Reset the recycle exception on recycle. If there *are* errors loading the scripts
95+
// during recycle, the errors will be caught in the initializer.
96+
pool.Recycled += (sender, args) => _scriptLoadException = null;
97+
return pool;
9098
}
9199

92100
/// <summary>
@@ -105,7 +113,7 @@ protected virtual void InitialiseEngine(IJsEngine engine)
105113
LoadUserScripts(engine);
106114
if (!_config.LoadReact)
107115
{
108-
// We expect to user to have loaded their own versino of React in the scripts that
116+
// We expect to user to have loaded their own version of React in the scripts that
109117
// were loaded above, let's ensure that's the case.
110118
EnsureReactLoaded(engine);
111119
}
@@ -127,7 +135,11 @@ private void LoadUserScripts(IJsEngine engine)
127135
}
128136
catch (JsRuntimeException ex)
129137
{
130-
throw new ReactScriptLoadException(string.Format(
138+
// We can't simply rethrow the exception here, as it's possible this is running
139+
// on a background thread (ie. as a response to a file changing). If we did
140+
// throw the exception here, it would terminate the entire process. Instead,
141+
// save the exception, and then just rethrow it later when getting the engine.
142+
_scriptLoadException = new ReactScriptLoadException(string.Format(
131143
"Error while loading \"{0}\": {1}\r\nLine: {2}\r\nColumn: {3}",
132144
file,
133145
ex.Message,
@@ -162,11 +174,12 @@ private static void EnsureReactLoaded(IJsEngine engine)
162174
/// <returns>The JavaScript engine</returns>
163175
public virtual IJsEngine GetEngineForCurrentThread()
164176
{
165-
EnsureNotDisposed();
177+
EnsureValidState();
166178
return _engines.GetOrAdd(Thread.CurrentThread.ManagedThreadId, id =>
167179
{
168180
var engine = _factory();
169181
InitialiseEngine(engine);
182+
EnsureValidState();
170183
return engine;
171184
});
172185
}
@@ -192,7 +205,7 @@ public virtual void DisposeEngineForCurrentThread()
192205
/// <returns>The JavaScript engine</returns>
193206
public virtual IJsEngine GetEngine()
194207
{
195-
EnsureNotDisposed();
208+
EnsureValidState();
196209
return _pool.GetEngine();
197210
}
198211

@@ -298,14 +311,20 @@ public virtual void Dispose()
298311
}
299312

300313
/// <summary>
301-
/// Ensures that this object has not been disposed.
314+
/// Ensures that this object has not been disposed, and that no error was thrown while
315+
/// loading the scripts.
302316
/// </summary>
303-
public void EnsureNotDisposed()
317+
public void EnsureValidState()
304318
{
305319
if (_disposed)
306320
{
307321
throw new ObjectDisposedException(GetType().Name);
308322
}
323+
if (_scriptLoadException != null)
324+
{
325+
// This means an exception occurred while loading the script (eg. syntax error in the file)
326+
throw _scriptLoadException;
327+
}
309328
}
310329

311330
/// <summary>

src/React.Core/React.Core.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@
6161
<HintPath>..\packages\JavaScriptEngineSwitcher.V8.1.2.4\lib\net40\JavaScriptEngineSwitcher.V8.dll</HintPath>
6262
</Reference>
6363
<Reference Include="JSPool, Version=0.3.0.0, Culture=neutral, PublicKeyToken=2fc7775f73072640, processorArchitecture=MSIL">
64-
<SpecificVersion>False</SpecificVersion>
65-
<HintPath>..\packages\JSPool.0.3.0\lib\net40-Client\JSPool.dll</HintPath>
64+
<HintPath>..\packages\JSPool.0.3.1\lib\net40-Client\JSPool.dll</HintPath>
65+
<Private>True</Private>
6666
</Reference>
6767
<Reference Include="MsieJavaScriptEngine, Version=1.5.1.0, Culture=neutral, PublicKeyToken=a3a2846a37ac0d3e, processorArchitecture=MSIL">
6868
<SpecificVersion>False</SpecificVersion>

src/React.Core/packages.config

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
3-
<package id="JavaScriptEngineSwitcher.Core" version="1.2.4" targetFramework="net40" />
4-
<package id="JavaScriptEngineSwitcher.Msie" version="1.2.4" targetFramework="net40" />
5-
<package id="JavaScriptEngineSwitcher.V8" version="1.2.4" targetFramework="net40" />
6-
<package id="JSPool" version="0.3.0" targetFramework="net40" />
7-
<package id="MsieJavaScriptEngine" version="1.5.1" targetFramework="net40" />
8-
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="net40" />
3+
<package id="JavaScriptEngineSwitcher.Core" version="1.2.4" targetFramework="net4" />
4+
<package id="JavaScriptEngineSwitcher.Msie" version="1.2.4" targetFramework="net4" />
5+
<package id="JavaScriptEngineSwitcher.V8" version="1.2.4" targetFramework="net4" />
6+
<package id="JSPool" version="0.3.1" targetFramework="net4" />
7+
<package id="MsieJavaScriptEngine" version="1.5.1" targetFramework="net4" />
8+
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="net4" />
99
</packages>

src/React.Tests/Core/JavaScriptEngineFactoryTest.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,5 +156,32 @@ public void ShouldThrowIfReactVersionNotLoaded()
156156
factory.GetEngineForCurrentThread();
157157
});
158158
}
159+
160+
[Test]
161+
public void ShouldCatchErrorsWhileLoadingScripts()
162+
{
163+
var config = new Mock<IReactSiteConfiguration>();
164+
config.Setup(x => x.ScriptsWithoutTransform).Returns(new List<string> {"foo.js"});
165+
config.Setup(x => x.LoadReact).Returns(true);
166+
var fileSystem = new Mock<IFileSystem>();
167+
fileSystem.Setup(x => x.ReadAsString("foo.js")).Returns("FAIL PLZ");
168+
169+
var jsEngine = new Mock<IJsEngine>();
170+
jsEngine.Setup(x => x.Evaluate<int>("1 + 1")).Returns(2);
171+
jsEngine.Setup(x => x.Execute("FAIL PLZ")).Throws(new JsRuntimeException("Fail")
172+
{
173+
LineNumber = 42,
174+
ColumnNumber = 911,
175+
});
176+
var registration = new JavaScriptEngineFactory.Registration
177+
{
178+
Factory = () => jsEngine.Object,
179+
Priority = 1
180+
};
181+
var factory = new JavaScriptEngineFactory(new[] { registration }, config.Object, fileSystem.Object);
182+
183+
var ex = Assert.Throws<ReactScriptLoadException>(() => factory.GetEngineForCurrentThread());
184+
Assert.AreEqual("Error while loading \"foo.js\": Fail\r\nLine: 42\r\nColumn: 911", ex.Message);
185+
}
159186
}
160187
}

src/wrap/React.Core/project.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
"JavaScriptEngineSwitcher.Core": "1.2.4",
1212
"JavaScriptEngineSwitcher.Msie": "1.2.4",
1313
"JavaScriptEngineSwitcher.V8": "1.2.4",
14-
"JSPool": "0.3.0",
14+
"JSPool": "0.3.1",
1515
"MsieJavaScriptEngine": "1.5.1",
1616
"Newtonsoft.Json": "6.0.8",
17-
"VroomJs": "2.0.0-*"
17+
"VroomJs": "2.0.2-*"
1818
}
1919
}
2020
}

0 commit comments

Comments
 (0)