Skip to content

Commit da8cc41

Browse files
committed
Prefer a weak reference to the JavascriptContext in JavascriptFunction to a list of JavascriptFunction handles in JavascriptContext
- makes it safe to call Dispose (~JavascriptFunction) explicitly and to let the GC remove references of undisposed references (!JavascriptFunction) - simplifies code in JavascriptContext which now does not need to know about JavascriptFunction handles that the GC takes care of anyway - the Reset method on the Persistent<Function> handle is irrelevant after the v8::Context is disposed because the underlying memory has been freed by V8
1 parent 6f50436 commit da8cc41

File tree

4 files changed

+27
-45
lines changed

4 files changed

+27
-45
lines changed

Source/Noesis.Javascript/JavascriptContext.cpp

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,6 @@ JavascriptContext::JavascriptContext()
161161
mExternals = gcnew System::Collections::Generic::Dictionary<System::Object ^, WrappedJavascriptExternal>();
162162
mMethods = gcnew System::Collections::Generic::Dictionary<System::String ^, WrappedMethod>();
163163
mTypeToConstructorMapping = gcnew System::Collections::Generic::Dictionary<System::Type ^, System::IntPtr>();
164-
mFunctions = gcnew System::Collections::Generic::List<System::WeakReference ^>();
165164
HandleScope scope(isolate);
166165
mContext = new Persistent<Context>(isolate, Context::New(isolate));
167166
terminateRuns = false;
@@ -181,19 +180,14 @@ JavascriptContext::~JavascriptContext()
181180
wrapped.Pointer->Reset();
182181
delete wrapped.Pointer;
183182
}
184-
for each (System::WeakReference^ f in mFunctions) {
185-
JavascriptFunction ^function = safe_cast<JavascriptFunction ^>(f->Target);
186-
if (function != nullptr)
187-
delete function;
188-
}
189183
for each (System::IntPtr p in mTypeToConstructorMapping->Values) {
190184
delete (void *)p;
191185
}
192186
delete mContext;
187+
mContext = nullptr;
193188
delete mExternals;
194189
delete mMethods;
195190
delete mTypeToConstructorMapping;
196-
delete mFunctions;
197191
}
198192
if (isolate != NULL)
199193
isolate->Dispose();
@@ -532,16 +526,6 @@ JavascriptContext::GetObjectWrapperConstructorTemplate(System::Type ^type)
532526

533527
////////////////////////////////////////////////////////////////////////////////////////////////////
534528

535-
void
536-
JavascriptContext::RegisterFunction(System::Object^ f)
537-
{
538-
// Note that while we do store WeakReferences, we never clean up this hashtable,
539-
// so it will just grow and grow.
540-
mFunctions->Add(gcnew System::WeakReference(f));
541-
}
542-
543-
////////////////////////////////////////////////////////////////////////////////////////////////////
544-
545529
System::String^ JavascriptContext::V8Version::get()
546530
{
547531
return gcnew System::String(v8::V8::GetVersion());

Source/Noesis.Javascript/JavascriptContext.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,10 @@ public ref class JavascriptContext: public System::IDisposable
196196

197197
Local<FunctionTemplate> GetObjectWrapperConstructorTemplate(System::Type ^type);
198198

199-
void RegisterFunction(System::Object^ f);
200-
201199
static void FatalErrorCallbackMember(const char* location, const char* message);
202200

201+
inline bool IsDisposed() { return mContext == nullptr; }
202+
203203
////////////////////////////////////////////////////////////
204204
// Data members
205205
////////////////////////////////////////////////////////////
@@ -224,10 +224,6 @@ public ref class JavascriptContext: public System::IDisposable
224224
// The `IntPtr` points to a `Persistent<FunctionTemplate>`.
225225
System::Collections::Generic::Dictionary<System::Type ^, System::IntPtr> ^mTypeToConstructorMapping;
226226

227-
// Stores every JavascriptFunction we create. Ensures we dispose of them
228-
// all.
229-
System::Collections::Generic::List<System::WeakReference ^> ^mFunctions;
230-
231227
// See comment for TerminateExecution().
232228
bool terminateRuns;
233229

Source/Noesis.Javascript/JavascriptFunction.cpp

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,16 @@ JavascriptFunction::JavascriptFunction(v8::Local<v8::Object> iFunction, Javascri
2020
throw gcnew System::ArgumentException("Must provide a JavascriptContext");
2121

2222
mFuncHandle = new Persistent<Function>(context->GetCurrentIsolate(), Local<Function>::Cast(iFunction));
23-
mContext = context;
24-
25-
mContext->RegisterFunction(this);
23+
mContextHandle = gcnew System::WeakReference(context);
2624
}
2725

2826
JavascriptFunction::~JavascriptFunction()
2927
{
3028
if(mFuncHandle)
3129
{
32-
if (mContext)
30+
if (IsAlive())
3331
{
34-
JavascriptScope scope(mContext);
32+
JavascriptScope scope(GetContext());
3533
mFuncHandle->Reset();
3634
}
3735
delete mFuncHandle;
@@ -46,16 +44,17 @@ JavascriptFunction::!JavascriptFunction()
4644

4745
System::Object^ JavascriptFunction::Call(... cli::array<System::Object^>^ args)
4846
{
49-
if (mFuncHandle == nullptr)
47+
if (!IsAlive())
5048
throw gcnew JavascriptException(L"This function's owning JavascriptContext has been disposed");
5149
if (!args)
5250
throw gcnew System::ArgumentNullException("args");
5351

54-
JavascriptScope scope(mContext);
55-
v8::Isolate* isolate = mContext->GetCurrentIsolate();
52+
auto context = GetContext();
53+
JavascriptScope scope(context);
54+
v8::Isolate* isolate = context->GetCurrentIsolate();
5655
HandleScope handleScope(isolate);
5756

58-
Local<v8::Object> global = mContext->GetGlobal();
57+
Local<v8::Object> global = context->GetGlobal();
5958

6059
int argc = args->Length;
6160
Local<v8::Value> *argv = new Local<v8::Value>[argc];
@@ -81,13 +80,13 @@ bool JavascriptFunction::operator==(JavascriptFunction^ func1, JavascriptFunctio
8180
return false;
8281
if (func1_null && func2_null)
8382
return true;
84-
if (func1->mFuncHandle == nullptr)
83+
if (!func1->IsAlive())
8584
throw gcnew JavascriptException(L"'func1's owning JavascriptContext has been disposed");
86-
if (func2->mFuncHandle == nullptr)
85+
if (!func2->IsAlive())
8786
throw gcnew JavascriptException(L"'func2's owning JavascriptContext has been disposed");
8887

89-
Local<Function> jsFuncPtr1 = func1->mFuncHandle->Get(func1->mContext->GetCurrentIsolate());
90-
Local<Function> jsFuncPtr2 = func2->mFuncHandle->Get(func2->mContext->GetCurrentIsolate());
88+
Local<Function> jsFuncPtr1 = func1->mFuncHandle->Get(func1->GetContext()->GetCurrentIsolate());
89+
Local<Function> jsFuncPtr2 = func2->mFuncHandle->Get(func2->GetContext()->GetCurrentIsolate());
9190

9291
return jsFuncPtr1->Equals(JavascriptContext::GetCurrentIsolate()->GetCurrentContext(), jsFuncPtr2).ToChecked();
9392
}
@@ -99,22 +98,23 @@ bool JavascriptFunction::Equals(JavascriptFunction^ other)
9998

10099
bool JavascriptFunction::Equals(Object^ other)
101100
{
102-
if (mFuncHandle == nullptr)
101+
if (!IsAlive())
103102
throw gcnew JavascriptException(L"This function's owning JavascriptContext has been disposed");
104103
JavascriptFunction^ otherFunc = dynamic_cast<JavascriptFunction^>(other);
105-
if (otherFunc != nullptr && otherFunc->mFuncHandle == nullptr)
106-
throw gcnew JavascriptException(L"This function's owning JavascriptContext has been disposed");
104+
if (otherFunc != nullptr && !otherFunc->IsAlive())
105+
throw gcnew JavascriptException(L"The other function's owning JavascriptContext has been disposed");
107106

108107
return (otherFunc && this->Equals(otherFunc));
109108
}
110109

111110
System::String^ JavascriptFunction::ToString()
112111
{
113-
if (mFuncHandle == nullptr)
112+
if (!IsAlive())
114113
throw gcnew JavascriptException(L"This function's owning JavascriptContext has been disposed");
115114

116-
JavascriptScope scope(mContext);
117-
auto isolate = mContext->GetCurrentIsolate();
115+
auto context = GetContext();
116+
JavascriptScope scope(context);
117+
auto isolate = context->GetCurrentIsolate();
118118
HandleScope handleScope(isolate);
119119
auto asString = mFuncHandle->Get(isolate)->ToString(isolate->GetCurrentContext());
120120
return safe_cast<System::String^>(JavascriptInterop::ConvertFromV8(asString.ToLocalChecked()));

Source/Noesis.Javascript/JavascriptFunction.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@ public ref class JavascriptFunction
3535
virtual bool Equals(Object^ other) override;
3636

3737
virtual System::String^ ToString() override;
38-
38+
internal:
39+
v8::Persistent<v8::Function>* mFuncHandle;
3940
private:
40-
v8::Persistent<v8::Function>* mFuncHandle;
41-
JavascriptContext^ mContext;
41+
System::WeakReference^ mContextHandle;
42+
inline JavascriptContext^ GetContext() { return mContextHandle->IsAlive ? safe_cast<JavascriptContext^>(mContextHandle->Target) : nullptr; }
43+
inline bool IsAlive() { auto context = GetContext(); return context != nullptr && !context->IsDisposed() && mFuncHandle != nullptr; }
4244
};
4345

4446
//////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)