Skip to content

Commit 612f08e

Browse files
committed
Clean up external object references when they are garbage collected, rather than when the context is destroyed.
1 parent 90dc8e7 commit 612f08e

File tree

5 files changed

+42
-34
lines changed

5 files changed

+42
-34
lines changed

Source/Noesis.Javascript/JavascriptContext.cpp

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,9 @@ JavascriptContext::JavascriptContext()
171171

172172
isolate->SetFatalErrorHandler(FatalErrorCallback);
173173

174-
mExternals = gcnew System::Collections::Generic::Dictionary<System::Object ^, WrappedJavascriptExternal>();
175174
mTypeToConstructorMapping = gcnew System::Collections::Generic::Dictionary<System::Type ^, System::IntPtr>();
175+
mTypeToMethods = gcnew System::Collections::Generic::Dictionary<System::Type^, System::Collections::Generic::Dictionary<System::String^, WrappedMethod>^>();
176+
176177
mFunctions = gcnew System::Collections::Generic::List<System::WeakReference ^>();
177178
HandleScope scope(isolate);
178179
mContext = new Persistent<Context>(isolate, Context::New(isolate));
@@ -186,8 +187,6 @@ JavascriptContext::~JavascriptContext()
186187
{
187188
v8::Locker v8ThreadLock(isolate);
188189
v8::Isolate::Scope isolate_scope(isolate);
189-
for each (WrappedJavascriptExternal wrapped in mExternals->Values)
190-
delete wrapped.Pointer;
191190
for each (System::WeakReference^ f in mFunctions) {
192191
JavascriptFunction ^function = safe_cast<JavascriptFunction ^>(f->Target);
193192
if (function != nullptr)
@@ -197,8 +196,8 @@ JavascriptContext::~JavascriptContext()
197196
delete (void *)p;
198197
}
199198
delete mContext;
200-
delete mExternals;
201199
delete mTypeToConstructorMapping;
200+
delete mTypeToMethods;
202201
delete mFunctions;
203202
}
204203
if (isolate != NULL)
@@ -298,6 +297,7 @@ void JavascriptContext::SetConstructor(System::String^ name, System::Type^ assoc
298297
functionTemplate->SetClassName(className);
299298
JavascriptInterop::InitObjectWrapperTemplate(functionTemplate->InstanceTemplate());
300299
mTypeToConstructorMapping[associatedType] = System::IntPtr(new Persistent<FunctionTemplate>(isolate, functionTemplate));
300+
mTypeToMethods[associatedType] = gcnew System::Collections::Generic::Dictionary<System::String^, WrappedMethod>();
301301
Local<Context>::New(isolate, *mContext)->Global()->Set(context, className, functionTemplate->GetFunction(context).ToLocalChecked());
302302
}
303303

@@ -505,18 +505,7 @@ JavascriptContext::Collect()
505505
JavascriptExternal*
506506
JavascriptContext::WrapObject(System::Object^ iObject)
507507
{
508-
WrappedJavascriptExternal external_wrapped;
509-
if (mExternals->TryGetValue(iObject, external_wrapped))
510-
{
511-
// We've wrapped this guy before.
512-
return external_wrapped.Pointer;
513-
}
514-
else
515-
{
516-
JavascriptExternal* external = new JavascriptExternal(iObject);
517-
mExternals[iObject] = WrappedJavascriptExternal(external);
518-
return external;
519-
}
508+
return new JavascriptExternal(iObject);
520509
}
521510

522511
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -529,6 +518,7 @@ JavascriptContext::GetObjectWrapperConstructorTemplate(System::Type ^type)
529518
Local<FunctionTemplate> constructor = FunctionTemplate::New(GetCurrentIsolate());
530519
JavascriptInterop::InitObjectWrapperTemplate(constructor->InstanceTemplate());
531520
mTypeToConstructorMapping[type] = System::IntPtr(new Persistent<FunctionTemplate>(isolate, constructor));
521+
mTypeToMethods[type] = gcnew System::Collections::Generic::Dictionary<System::String^, WrappedMethod>();
532522
return constructor;
533523
}
534524
Persistent<FunctionTemplate> *constructor = (Persistent<FunctionTemplate> *)(void *)ptrToConstructor;
@@ -537,6 +527,14 @@ JavascriptContext::GetObjectWrapperConstructorTemplate(System::Type ^type)
537527

538528
////////////////////////////////////////////////////////////////////////////////////////////////////
539529

530+
System::Collections::Generic::Dictionary<System::String^, WrappedMethod>^
531+
JavascriptContext::MethodsForType(System::Type^ type)
532+
{
533+
return mTypeToMethods[type];
534+
}
535+
536+
////////////////////////////////////////////////////////////////////////////////////////////////////
537+
540538
void
541539
JavascriptContext::RegisterFunction(System::Object^ f)
542540
{

Source/Noesis.Javascript/JavascriptContext.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ public value struct WrappedJavascriptExternal
9494
{
9595
private:
9696
System::IntPtr pointer;
97+
System::Type^ type;
9798

9899
internal:
99100
WrappedJavascriptExternal(JavascriptExternal *value)
@@ -196,6 +197,8 @@ public ref class JavascriptContext: public System::IDisposable
196197

197198
Local<FunctionTemplate> GetObjectWrapperConstructorTemplate(System::Type ^type);
198199

200+
System::Collections::Generic::Dictionary<System::String^, WrappedMethod>^ MethodsForType(System::Type^ type);
201+
199202
void RegisterFunction(System::Object^ f);
200203

201204
static void FatalErrorCallbackMember(const char* location, const char* message);
@@ -212,18 +215,16 @@ public ref class JavascriptContext: public System::IDisposable
212215
// v8 context required to be active for all v8 operations.
213216
Persistent<Context>* mContext;
214217

215-
// Stores every JavascriptExternal we create. This saves time if the same
216-
// objects are recreated frequently, and stops us building up a huge
217-
// collection of JavascriptExternal objects that won't be freed until
218-
// the context is destroyed.
219-
System::Collections::Generic::Dictionary<System::Object ^, WrappedJavascriptExternal> ^mExternals;
220-
221218
// Maps types to their constructor function templates
222219
// The mapping will either be defined by the user calling `SetConstructor` or autogenerated if no
223220
// mapping was provided.
224221
// The `IntPtr` points to a `Persistent<FunctionTemplate>`.
225222
System::Collections::Generic::Dictionary<System::Type ^, System::IntPtr> ^mTypeToConstructorMapping;
226223

224+
// For each distinct CLR type we've wrapped, this accumulates functions
225+
// constructed to access its methods/properties.
226+
System::Collections::Generic::Dictionary<System::Type ^, System::Collections::Generic::Dictionary<System::String^, WrappedMethod>^>^ mTypeToMethods;
227+
227228
// Stores every JavascriptFunction we create. Ensures we dispose of them
228229
// all.
229230
System::Collections::Generic::List<System::WeakReference ^> ^mFunctions;

Source/Noesis.Javascript/JavascriptExternal.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ JavascriptExternal::JavascriptExternal(System::Object^ iObject)
5252
{
5353
mObjectHandle = System::Runtime::InteropServices::GCHandle::Alloc(iObject);
5454
mOptions = SetParameterOptions::None;
55-
mMethods = gcnew System::Collections::Generic::Dictionary<System::String ^, WrappedMethod>();
5655
}
5756

5857
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -76,15 +75,16 @@ Local<Function>
7675
JavascriptExternal::GetMethod(wstring iName)
7776
{
7877
v8::Isolate *isolate = JavascriptContext::GetCurrentIsolate();
79-
System::Collections::Generic::Dictionary<System::String ^, WrappedMethod> ^methods = mMethods;
78+
JavascriptContext^ context = JavascriptContext::GetCurrent();
79+
System::Object^ self = mObjectHandle.Target;
80+
System::Type^ type = self->GetType();
81+
System::Collections::Generic::Dictionary<System::String ^, WrappedMethod> ^methods = context->MethodsForType(type);
8082
System::String^ memberName = gcnew System::String(iName.c_str());
8183
WrappedMethod method;
8284
if (methods->TryGetValue(memberName, method))
8385
return Local<Function>::New(isolate, *(method.Pointer));
8486
else
8587
{
86-
System::Object^ self = mObjectHandle.Target;
87-
System::Type^ type = self->GetType();
8888
System::String^ memberName = gcnew System::String(iName.c_str());
8989
cli::array<System::Object^>^ objectInfo = gcnew cli::array<System::Object^>(2);
9090
objectInfo->SetValue(self,0);
@@ -94,7 +94,6 @@ JavascriptExternal::GetMethod(wstring iName)
9494
cli::array<MemberInfo^>^ members = type->GetMember(memberName);
9595
if (members->Length > 0 && members[0]->MemberType == MemberTypes::Method)
9696
{
97-
JavascriptContext^ context = JavascriptContext::GetCurrent();
9897
Local<External> external = External::New(isolate, context->WrapObject(objectInfo));
9998
Local<FunctionTemplate> functionTemplate = FunctionTemplate::New(isolate, JavascriptInterop::Invoker, external);
10099
Local<Function> function = functionTemplate->GetFunction(isolate->GetCurrentContext()).ToLocalChecked();

Source/Noesis.Javascript/JavascriptExternal.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,6 @@ class JavascriptExternal
9898

9999
SetParameterOptions mOptions;
100100

101-
// Owned by JavascriptContext.
102-
gcroot<System::Collections::Generic::Dictionary<System::String ^, WrappedMethod> ^> mMethods;
103-
104101
std::unique_ptr<Persistent<Function>> mIterator;
105102
static void IteratorCallback(const v8::FunctionCallbackInfo<Value>& iArgs);
106103
static void IteratorNextCallback(const v8::FunctionCallbackInfo<Value>& iArgs);

Source/Noesis.Javascript/JavascriptInterop.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -259,19 +259,32 @@ JavascriptInterop::ConvertToV8(System::Object^ iObject)
259259

260260
////////////////////////////////////////////////////////////////////////////////////////////////////
261261

262-
// TODO: should return Local<External>
262+
// a sort-of destructor in v8 land
263+
void JavascriptExternalMakeWeak(const v8::WeakCallbackInfo<JavascriptExternal>& data) {
264+
v8::Isolate* isolate = JavascriptContext::GetCurrentIsolate();
265+
JavascriptExternal *external = data.GetParameter();
266+
delete external;
267+
}
268+
269+
// TO DO: should return Local<External>
263270
Local<Object>
264271
JavascriptInterop::WrapObject(System::Object^ iObject)
265272
{
266273
JavascriptContext^ context = JavascriptContext::GetCurrent();
267274

268275
if (context != nullptr)
269276
{
270-
Local<FunctionTemplate> templ = context->GetObjectWrapperConstructorTemplate(iObject->GetType());
277+
System::Type ^object_type = iObject->GetType();
278+
Local<FunctionTemplate> templ = context->GetObjectWrapperConstructorTemplate(object_type);
271279
v8::Isolate *isolate = JavascriptContext::GetCurrentIsolate();
272280
Local<ObjectTemplate> instanceTemplate = templ->InstanceTemplate();
273281
Local<Object> object = instanceTemplate->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
274-
object->SetInternalField(0, External::New(isolate, context->WrapObject(iObject)));
282+
JavascriptExternal* external = context->WrapObject(iObject);
283+
object->SetInternalField(0, External::New(isolate, external));
284+
285+
// So we're notified when this object is no longer needed.
286+
v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object>> pobj(isolate, object);
287+
pobj.SetWeak<JavascriptExternal>(external, JavascriptExternalMakeWeak, v8::WeakCallbackType::kParameter);
275288

276289
return object;
277290
}
@@ -281,7 +294,7 @@ JavascriptInterop::WrapObject(System::Object^ iObject)
281294

282295
////////////////////////////////////////////////////////////////////////////////////////////////////
283296

284-
// TODO: should use Local<External> iExternal
297+
// TO DO: should use Local<External> iExternal
285298
System::Object^
286299
JavascriptInterop::UnwrapObject(Local<Value> iValue)
287300
{

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy