Skip to content

Commit 7455e22

Browse files
committed
fix: release blocks properly
1 parent b8a1313 commit 7455e22

File tree

3 files changed

+75
-30
lines changed

3 files changed

+75
-30
lines changed

NativeScript/ffi/Block.mm

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "js_native_api_types.h"
77
#include "node_api_util.h"
88
#include "objc/runtime.h"
9+
#include <cstring>
910

1011
struct Block_descriptor_1 {
1112
unsigned long int reserved; // NULL
@@ -27,49 +28,79 @@
2728
nativescript::Closure* closure;
2829
};
2930

30-
void block_copy(void* dest, void* src) {}
31-
void block_release(void* src) {}
31+
namespace {
32+
33+
constexpr int kBlockNeedsFree = (1 << 24);
34+
constexpr int kBlockHasCopyDispose = (1 << 25);
35+
constexpr int kBlockRefCountOne = (1 << 1);
36+
37+
void block_copy(void* dest, void* src) {
38+
auto dst = static_cast<Block_literal_1*>(dest);
39+
auto source = static_cast<Block_literal_1*>(src);
40+
dst->closure = source->closure;
41+
}
42+
43+
void block_release(void* src) {
44+
auto block = static_cast<Block_literal_1*>(src);
45+
if (block == nullptr) {
46+
return;
47+
}
48+
49+
if (block->closure != nullptr) {
50+
delete block->closure;
51+
block->closure = nullptr;
52+
}
53+
}
54+
55+
Block_descriptor_1 kBlockDescriptor = {
56+
.reserved = 0,
57+
.size = sizeof(Block_literal_1),
58+
.copy_helper = block_copy,
59+
.dispose_helper = block_release,
60+
.signature = nullptr,
61+
};
62+
63+
} // namespace
3264

3365
void block_finalize(napi_env env, void* data, void* hint) {
34-
auto block = (Block_literal_1*)data;
35-
delete block->closure;
36-
delete block;
66+
auto block = static_cast<Block_literal_1*>(data);
67+
if (block == nullptr) {
68+
return;
69+
}
70+
71+
if (block->closure != nullptr) {
72+
delete block->closure;
73+
block->closure = nullptr;
74+
}
75+
76+
free(block);
3777
}
3878

3979
namespace nativescript {
4080

81+
void* mallocBlockISA = nullptr;
4182
void* stackBlockISA = nullptr;
4283

4384
id registerBlock(napi_env env, Closure* closure, napi_value callback) {
44-
auto block = new Block_literal_1();
85+
auto block = static_cast<Block_literal_1*>(malloc(sizeof(Block_literal_1)));
86+
memset(block, 0, sizeof(Block_literal_1));
87+
88+
if (mallocBlockISA == nullptr) {
89+
mallocBlockISA = dlsym(RTLD_DEFAULT, "_NSConcreteMallocBlock");
90+
}
91+
4592
if (stackBlockISA == nullptr) {
4693
stackBlockISA = dlsym(RTLD_DEFAULT, "_NSConcreteStackBlock");
4794
}
48-
block->isa = stackBlockISA;
49-
block->flags = (1 << 29) | (1 << 25);
95+
96+
block->isa = mallocBlockISA != nullptr ? mallocBlockISA : stackBlockISA;
97+
block->flags = kBlockNeedsFree | kBlockHasCopyDispose | kBlockRefCountOne;
5098
block->reserved = 0;
5199
block->invoke = closure->fnptr;
52-
block->descriptor = new Block_descriptor_1();
100+
block->descriptor = &kBlockDescriptor;
53101
block->closure = closure;
54102

55-
block->descriptor->reserved = 0;
56-
block->descriptor->size = sizeof(Block_literal_1);
57-
block->descriptor->copy_helper = block_copy;
58-
block->descriptor->dispose_helper = block_release;
59-
block->descriptor->signature = nullptr;
60-
61-
napi_remove_wrap(env, callback, nullptr);
62-
napi_ref ref = nullptr;
63-
// TODO: fix memory management of objc blocks here
64-
// napi_wrap(env, callback, block, block_finalize, nullptr, &ref);
65-
// if (ref == nullptr) {
66-
// Deno doesn't handle napi_wrap properly.
67-
ref = make_ref(env, callback, 1);
68-
// } else {
69-
// uint32_t refCount;
70-
// napi_reference_ref(env, ref, &refCount);
71-
// }
72-
closure->func = ref;
103+
closure->func = make_ref(env, callback, 1);
73104

74105
auto bridgeState = ObjCBridgeState::InstanceData(env);
75106

NativeScript/ffi/Closure.mm

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,10 @@ void JSBlockCallback(ffi_cif* cif, void* ret, void* args[], void* data) {
260260

261261
Closure::~Closure() {
262262
if (func != nullptr) {
263-
napi_reference_unref(env, func, nullptr);
263+
if (env != nullptr) {
264+
napi_delete_reference(env, func);
265+
}
266+
func = nullptr;
264267
}
265268
#ifndef ENABLE_JS_RUNTIME
266269
if (tsfn != nullptr) {

NativeScript/ffi/TypeConv.mm

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,7 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree,
841841
*res = nullptr;
842842
return;
843843
}
844+
return;
844845
}
845846

846847
case napi_function: {
@@ -856,6 +857,8 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree,
856857
closure->env = env;
857858
id block = registerBlock(env, closure, value);
858859
*res = (void*)block;
860+
*shouldFree = true;
861+
*shouldFreeAny = true;
859862
return;
860863
}
861864

@@ -866,12 +869,20 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree,
866869
}
867870
}
868871

872+
void free(napi_env env, void* value) override {
873+
if (value != nullptr) {
874+
[(id)value release];
875+
}
876+
}
877+
869878
void encode(std::string* encoding) override { *encoding += "^v"; }
870879
};
871880

872881
void function_pointer_finalize(napi_env env, void* finalize_data, void* finalize_hint) {
873-
Closure* closure = (Closure*)finalize_hint;
874-
delete closure;
882+
Closure* closure = static_cast<Closure*>(finalize_hint);
883+
if (closure != nullptr) {
884+
delete closure;
885+
}
875886
}
876887

877888
class FunctionPointerTypeConv : public TypeConv {

0 commit comments

Comments
 (0)