Skip to content

Commit 431e816

Browse files
rigor789darindbrysemLogikgate
authored
fix: memory leaks & WeakRefs (#111)
* fix: don't allocate persistent empty objects * fix: Blocks memory leak (#100) * feat: drop custom WeakRef implementation & use built-in * fix: free allocated memory in ReferenceWrapper * fix: DictionaryAdapter Hanging References (#114) * Clean up hanging references before deallocating Reduces total dictionary related memory leaks by 36% * fix * chore: more leak fixes * fix: only release enumerator_ when set (#117) * fix: handle methods with pointer type params fixes #109 * fix: referenceWrapper memory leak & CString leak * refactor: ReferenceWrapper dispose to be managed internally * fix: pre-allocate memory even for empty values * fix: undo invalid fix (needs a different way) Co-authored-by: Darin Dimitrov <[email protected]> Co-authored-by: Bryse Meijer <[email protected]> Co-authored-by: logikgate <[email protected]>
1 parent a84ae27 commit 431e816

File tree

12 files changed

+104
-194
lines changed

12 files changed

+104
-194
lines changed

NativeScript/runtime/ArgConverter.mm

+1
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@
267267
id adapter = [[DictionaryAdapter alloc] initWithJSObject:value.As<Object>() isolate:isolate];
268268
memset(retValue, 0, sizeof(id));
269269
*static_cast<id __strong *>(retValue) = adapter;
270+
CFAutorelease(adapter);
270271
return;
271272
}
272273
} else if (type == BinaryTypeEncodingType::StructDeclarationReference) {

NativeScript/runtime/DataWrapper.h

+12-5
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,17 @@ class ReferenceWrapper: public BaseDataWrapper {
259259
disposeData_(false) {
260260
}
261261

262+
~ReferenceWrapper() {
263+
if(this->value_ != nullptr) {
264+
value_->Reset();
265+
delete value_;
266+
}
267+
268+
if (this->data_ != nullptr) {
269+
std::free(this->data_);
270+
}
271+
}
272+
262273
const WrapperType Type() {
263274
return WrapperType::Reference;
264275
}
@@ -291,16 +302,12 @@ class ReferenceWrapper: public BaseDataWrapper {
291302
}
292303

293304
void SetData(void* data, bool disposeData = false) {
294-
if (this->data_ != nullptr && data != nullptr && this->disposeData_) {
305+
if (this->data_ != nullptr && this->disposeData_) {
295306
std::free(this->data_);
296307
}
297308
this->data_ = data;
298309
this->disposeData_ = disposeData;
299310
}
300-
301-
bool ShouldDisposeData() {
302-
return this->disposeData_;
303-
}
304311
private:
305312
BaseDataWrapper* typeWrapper_;
306313
v8::Persistent<v8::Value>* value_;

NativeScript/runtime/DictionaryAdapter.mm

+24-2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ - (id)nextObject {
5555
}
5656

5757
- (void)dealloc {
58+
self->isolate_ = nullptr;
59+
self->map_ = nil;
60+
self->cache_ = nil;
61+
5862
[super dealloc];
5963
}
6064

@@ -138,6 +142,10 @@ - (NSArray*)allObjects {
138142
}
139143

140144
- (void)dealloc {
145+
self->isolate_ = nullptr;
146+
self->dictionary_ = nil;
147+
self->cache_ = nil;
148+
141149
[super dealloc];
142150
}
143151

@@ -147,6 +155,7 @@ @implementation DictionaryAdapter {
147155
Isolate* isolate_;
148156
std::shared_ptr<Persistent<Value>> object_;
149157
std::shared_ptr<Caches> cache_;
158+
NSEnumerator* enumerator_;
150159
}
151160

152161
- (instancetype)initWithJSObject:(Local<Object>)jsObject isolate:(Isolate*)isolate {
@@ -225,10 +234,14 @@ - (NSEnumerator*)keyEnumerator {
225234
Local<Value> obj = self->object_->Get(self->isolate_);
226235

227236
if (obj->IsMap()) {
228-
return [[DictionaryAdapterMapKeysEnumerator alloc] initWithMap:self->object_ isolate:self->isolate_ cache:self->cache_];
237+
self->enumerator_ = [[DictionaryAdapterMapKeysEnumerator alloc] initWithMap:self->object_ isolate:self->isolate_ cache:self->cache_];
238+
239+
return self->enumerator_;
229240
}
230241

231-
return [[DictionaryAdapterObjectKeysEnumerator alloc] initWithProperties:self->object_ isolate:self->isolate_ cache:self->cache_];
242+
self->enumerator_ = [[DictionaryAdapterObjectKeysEnumerator alloc] initWithProperties:self->object_ isolate:self->isolate_ cache:self->cache_];
243+
244+
return self->enumerator_;
232245
}
233246

234247
- (void)dealloc {
@@ -240,6 +253,15 @@ - (void)dealloc {
240253
delete wrapper;
241254
}
242255
self->object_->Reset();
256+
self->isolate_ = nullptr;
257+
self->cache_ = nil;
258+
self->object_ = nil;
259+
260+
if (self->enumerator_ != nullptr) {
261+
CFAutorelease(self->enumerator_);
262+
self->enumerator_ = nullptr;
263+
}
264+
243265
[super dealloc];
244266
}
245267

NativeScript/runtime/Helpers.mm

+1
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@
252252
return Local<Value>();
253253
}
254254

255+
v8::Locker locker(isolate);
255256
Local<Value> result;
256257
if (!obj->GetPrivate(context, privateKey).ToLocal(&result)) {
257258
tns::Assert(false, isolate);

NativeScript/runtime/Interop.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ class Interop {
126126
static id ToObject(v8::Local<v8::Context> context, v8::Local<v8::Value> arg);
127127
static v8::Local<v8::Value> GetPrimitiveReturnType(v8::Local<v8::Context> context, BinaryTypeEncodingType type, BaseCall* call);
128128
private:
129+
static std::pair<IMP, ffi_closure*> CreateMethodInternal(const uint8_t initialParamIndex, const uint8_t argsCount, const TypeEncoding* typeEncoding, FFIMethodCallback callback, void* userData);
129130
static CFTypeRef CreateBlock(const uint8_t initialParamIndex, const uint8_t argsCount, const TypeEncoding* typeEncoding, FFIMethodCallback callback, void* userData);
130131
template <typename T>
131132
static void SetStructValue(v8::Local<v8::Value> value, void* destBuffer, ptrdiff_t position);
@@ -187,7 +188,8 @@ class Interop {
187188
const void* invoke;
188189
JSBlockDescriptor* descriptor;
189190
void* userData;
190-
191+
ffi_closure* ffiClosure;
192+
191193
static JSBlockDescriptor kJSBlockDescriptor;
192194
} JSBlock;
193195
};

NativeScript/runtime/Interop.mm

+25-9
Original file line numberDiff line numberDiff line change
@@ -46,43 +46,53 @@
4646
v8::Locker locker(isolate);
4747
Isolate::Scope isolate_scope(isolate);
4848
HandleScope handle_scope(isolate);
49+
BlockWrapper* blockWrapper = static_cast<BlockWrapper*>(tns::GetValue(isolate, callback));
4950
tns::DeleteValue(isolate, callback);
5051
wrapper->callback_->Reset();
52+
delete blockWrapper;
5153
}
5254
}
5355
delete wrapper;
56+
ffi_closure_free(block->ffiClosure);
5457
block->~JSBlock();
5558
}
5659
}
5760
};
5861

59-
IMP Interop::CreateMethod(const uint8_t initialParamIndex, const uint8_t argsCount, const TypeEncoding* typeEncoding, FFIMethodCallback callback, void* userData) {
62+
std::pair<IMP, ffi_closure*> Interop::CreateMethodInternal(const uint8_t initialParamIndex, const uint8_t argsCount, const TypeEncoding* typeEncoding, FFIMethodCallback callback, void* userData) {
6063
void* functionPointer;
6164
ffi_closure* closure = static_cast<ffi_closure*>(ffi_closure_alloc(sizeof(ffi_closure), &functionPointer));
6265
ParametrizedCall* call = ParametrizedCall::Get(typeEncoding, initialParamIndex, initialParamIndex + argsCount);
6366
ffi_status status = ffi_prep_closure_loc(closure, call->Cif, callback, userData, functionPointer);
6467
tns::Assert(status == FFI_OK);
6568

66-
return (IMP)functionPointer;
69+
return std::make_pair((IMP)functionPointer, closure);
70+
71+
}
72+
73+
IMP Interop::CreateMethod(const uint8_t initialParamIndex, const uint8_t argsCount, const TypeEncoding* typeEncoding, FFIMethodCallback callback, void* userData) {
74+
std::pair<IMP, ffi_closure*> result = Interop::CreateMethodInternal(initialParamIndex, argsCount, typeEncoding, callback, userData);
75+
return result.first;
6776
}
6877

6978
CFTypeRef Interop::CreateBlock(const uint8_t initialParamIndex, const uint8_t argsCount, const TypeEncoding* typeEncoding, FFIMethodCallback callback, void* userData) {
7079
JSBlock* blockPointer = reinterpret_cast<JSBlock*>(malloc(sizeof(JSBlock)));
71-
void* functionPointer = (void*)CreateMethod(initialParamIndex, argsCount, typeEncoding, callback, userData);
80+
81+
std::pair<IMP, ffi_closure*> result = Interop::CreateMethodInternal(initialParamIndex, argsCount, typeEncoding, callback, userData);
7282

7383
*blockPointer = {
7484
.isa = nullptr,
7585
.flags = JSBlock::BLOCK_HAS_COPY_DISPOSE | JSBlock::BLOCK_NEEDS_FREE | (1 /* ref count */ << 1),
7686
.reserved = 0,
77-
.invoke = functionPointer,
87+
.invoke = (void*)result.first,
7888
.descriptor = &JSBlock::kJSBlockDescriptor,
89+
.userData = userData,
90+
.ffiClosure = result.second,
7991
};
8092

81-
blockPointer->userData = userData;
82-
8393
object_setClass((__bridge id)blockPointer, objc_getClass("__NSMallocBlock__"));
8494

85-
return blockPointer;
95+
return CFAutorelease(blockPointer);
8696
}
8797

8898
Local<Value> Interop::CallFunction(CMethodCall& methodCall) {
@@ -295,8 +305,10 @@
295305
Local<Value> value = referenceWrapper->Value() != nullptr ? referenceWrapper->Value()->Get(isolate) : Local<Value>();
296306
ffi_type* ffiType = FFICall::GetArgumentType(innerType);
297307
data = calloc(1, ffiType->size);
298-
referenceWrapper->SetData(data);
308+
309+
referenceWrapper->SetData(data, true);
299310
referenceWrapper->SetEncoding(innerType);
311+
300312
// Initialize the ref/out parameter value before passing it to the function call
301313
if (!value.IsEmpty()) {
302314
Interop::WriteValue(context, innerType, data, value);
@@ -350,7 +362,7 @@
350362
BaseDataWrapper* baseWrapper = tns::GetValue(isolate, arg);
351363
if (baseWrapper != nullptr && baseWrapper->Type() == WrapperType::Block) {
352364
BlockWrapper* wrapper = static_cast<BlockWrapper*>(baseWrapper);
353-
blockPtr = wrapper->Block();
365+
blockPtr = Block_copy(wrapper->Block());
354366
} else {
355367
std::shared_ptr<Persistent<Value>> poCallback = std::make_shared<Persistent<Value>>(isolate, arg);
356368
MethodCallbackWrapper* userData = new MethodCallbackWrapper(isolate, poCallback, 1, argsCount, blockTypeEncoding);
@@ -512,13 +524,16 @@
512524
Local<ArrayBuffer> buffer = arg.As<ArrayBuffer>();
513525
NSDataAdapter* adapter = [[NSDataAdapter alloc] initWithJSObject:buffer isolate:isolate];
514526
Interop::SetValue(dest, adapter);
527+
CFAutorelease(adapter);
515528
} else if (tns::IsArrayOrArrayLike(isolate, obj)) {
516529
Local<v8::Array> array = Interop::ToArray(obj);
517530
ArrayAdapter* adapter = [[ArrayAdapter alloc] initWithJSObject:array isolate:isolate];
518531
Interop::SetValue(dest, adapter);
532+
CFAutorelease(adapter);
519533
} else {
520534
DictionaryAdapter* adapter = [[DictionaryAdapter alloc] initWithJSObject:obj isolate:isolate];
521535
Interop::SetValue(dest, adapter);
536+
CFAutorelease(adapter);
522537
}
523538
} else {
524539
tns::Assert(false, isolate);
@@ -574,6 +589,7 @@
574589
} else {
575590
Local<Object> obj = arg.As<Object>();
576591
DictionaryAdapter* adapter = [[DictionaryAdapter alloc] initWithJSObject:obj isolate:isolate];
592+
CFAutorelease(adapter);
577593
return adapter;
578594
}
579595
}

NativeScript/runtime/ObjectManager.mm

-3
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,6 @@
112112
case WrapperType::Reference: {
113113
ReferenceWrapper* referenceWrapper = static_cast<ReferenceWrapper*>(wrapper);
114114
if (referenceWrapper->Data() != nullptr) {
115-
if (referenceWrapper->ShouldDisposeData()) {
116-
std::free(referenceWrapper->Data());
117-
}
118115
referenceWrapper->SetData(nullptr);
119116
referenceWrapper->SetEncoding(nullptr);
120117
}

NativeScript/runtime/Reference.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Local<v8::Function> Reference::GetInteropReferenceCtorFunc(Local<Context> contex
5858
Local<Object> prototype = prototypeValue.As<Object>();
5959
Reference::RegisterToStringMethod(context, prototype);
6060

61-
cache->InteropReferenceCtorFunc = std::make_unique<Persistent<v8::Function>>(isolate, ctorFunc);
61+
cache->InteropReferenceCtorFunc = std::make_unique<Persistent<v8::Function> >(isolate, ctorFunc);
6262

6363
return ctorFunc;
6464
}
@@ -80,6 +80,10 @@ void Reference::ReferenceConstructorCallback(const FunctionCallbackInfo<Value>&
8080
val = new Persistent<Value>(isolate, info[1]);
8181
}
8282
}
83+
84+
if(val != nullptr) {
85+
val->SetWeak();
86+
}
8387

8488
ReferenceWrapper* wrapper = new ReferenceWrapper(typeWrapper, val);
8589
Local<Object> thiz = info.This();

NativeScript/runtime/Runtime.mm

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@
105105

106106
DefineGlobalObject(context);
107107
DefineCollectFunction(context);
108-
PromiseProxy::Init(context);
108+
// PromiseProxy::Init(context);
109109
Console::Init(context);
110110
WeakRef::Init(context);
111111

0 commit comments

Comments
 (0)