Skip to content

Commit f9ffa44

Browse files
committed
Defended JavascriptFunction against use after a context has been disposed.
1 parent 5bc4e57 commit f9ffa44

File tree

3 files changed

+38
-8
lines changed

3 files changed

+38
-8
lines changed

Source/Noesis.Javascript/JavascriptFunction.cpp

+16-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@ JavascriptFunction::~JavascriptFunction()
4141

4242
System::Object^ JavascriptFunction::Call(... cli::array<System::Object^>^ args)
4343
{
44-
JavascriptScope scope(mContext);
44+
if (mFuncHandle == nullptr)
45+
throw gcnew JavascriptException(L"This function's owning JavascriptContext has been disposed");
46+
if (!args)
47+
throw gcnew System::ArgumentNullException("args");
48+
49+
JavascriptScope scope(mContext);
4550
v8::Isolate* isolate = mContext->GetCurrentIsolate();
4651
HandleScope handleScope(isolate);
4752

@@ -65,7 +70,12 @@ System::Object^ JavascriptFunction::Call(... cli::array<System::Object^>^ args)
6570

6671
bool JavascriptFunction::operator==(JavascriptFunction^ func1, JavascriptFunction^ func2)
6772
{
68-
if(ReferenceEquals(func2, nullptr)) {
73+
if (func1->mFuncHandle == nullptr)
74+
throw gcnew JavascriptException(L"'func1's owning JavascriptContext has been disposed");
75+
if (func2->mFuncHandle == nullptr)
76+
throw gcnew JavascriptException(L"'func2's owning JavascriptContext has been disposed");
77+
78+
if(ReferenceEquals(func2, nullptr)) {
6979
return false;
7080
}
7181
Handle<Function> jsFuncPtr1 = func1->mFuncHandle->Get(func1->mContext->GetCurrentIsolate());
@@ -81,7 +91,10 @@ bool JavascriptFunction::Equals(JavascriptFunction^ other)
8191

8292
bool JavascriptFunction::Equals(Object^ other)
8393
{
84-
JavascriptFunction^ otherFunc = dynamic_cast<JavascriptFunction^>(other);
94+
if (mFuncHandle == nullptr)
95+
throw gcnew JavascriptException(L"This function's owning JavascriptContext has been disposed");
96+
97+
JavascriptFunction^ otherFunc = dynamic_cast<JavascriptFunction^>(other);
8598
return (otherFunc && this->Equals(otherFunc));
8699
}
87100

Source/Noesis.Javascript/JavascriptFunction.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ namespace Noesis { namespace Javascript {
1717
//////////////////////////////////////////////////////////////////////////
1818
// JavascriptFunction
1919
//
20-
// Wraps around JS function object and allow calling it in later time
20+
// Wraps around a JS function when passed back to C#, allowing it to be
21+
// called from C#. Callers must not dispose of their JavascriptContext
22+
// while they still have references to JavascriptFunctions.
2123
//////////////////////////////////////////////////////////////////////////
2224
public ref class JavascriptFunction
2325
{

Tests/Noesis.Javascript.Tests/JavascriptFunctionTests.cs

+19-4
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public void TearDown()
2727
public void GetFunctionExpressionFromJsContext()
2828
{
2929
_context.Run("a = function(a, b) { return a + b; }");
30-
30+
3131
JavascriptFunction funcObj = _context.GetParameter("a") as JavascriptFunction;
3232
funcObj.Should().NotBeNull();
3333
funcObj.Call(1, 2).Should().BeOfType<int>().Which.Should().Be(3);
@@ -37,7 +37,7 @@ public void GetFunctionExpressionFromJsContext()
3737
public void GetNamedFunctionFromJsContext()
3838
{
3939
_context.Run("function test(a, b) { return a + b; }");
40-
40+
4141
JavascriptFunction funcObj = _context.GetParameter("test") as JavascriptFunction;
4242
funcObj.Should().NotBeNull();
4343
funcObj.Call(1, 2).Should().BeOfType<int>().Which.Should().Be(3);
@@ -47,7 +47,7 @@ public void GetNamedFunctionFromJsContext()
4747
public void GetArrowFunctionExpressionFromJsContext()
4848
{
4949
_context.Run("a = (a, b) => a + b");
50-
50+
5151
JavascriptFunction funcObj = _context.GetParameter("a") as JavascriptFunction;
5252
funcObj.Should().NotBeNull();
5353
funcObj.Call(1, 2).Should().BeOfType<int>().Which.Should().Be(3);
@@ -57,7 +57,7 @@ public void GetArrowFunctionExpressionFromJsContext()
5757
public void PassFunctionToMethodInManagedObjectAndUseItToFilterAList()
5858
{
5959
_context.SetParameter("collection", new CollectionWrapper());
60-
60+
6161
var result = _context.Run("collection.Filter(x => x % 2 === 0)") as IEnumerable<int>;
6262
result.Should().NotBeNull();
6363
result.Should().BeEquivalentTo(2, 4);
@@ -73,6 +73,21 @@ public void ExceptionsAreHandledAndWrappedInAJavascriptExceptionObject()
7373
}
7474
}
7575

76+
[TestClass]
77+
public class JavascriptFunctionTestsWithoutAutomaticContext
78+
{
79+
[TestMethod]
80+
public void CannotUseAFunctionWhenItsContextIsDisposed()
81+
{
82+
JavascriptFunction function;
83+
using (var context = new JavascriptContext()) {
84+
function = context.Run("() => { throw new Error('test'); }") as JavascriptFunction;
85+
}
86+
Action action = () => function.Call();
87+
action.ShouldThrowExactly<JavascriptException>().WithMessage("This function's owning JavascriptContext has been disposed");
88+
}
89+
}
90+
7691
class CollectionWrapper
7792
{
7893
private IEnumerable<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

0 commit comments

Comments
 (0)