Skip to content

Commit 6e7e806

Browse files
committed
fix: correctly convert function and block pointers in arguments to callable functions from JS
1 parent 64bb8b2 commit 6e7e806

File tree

16 files changed

+251
-88
lines changed

16 files changed

+251
-88
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ set(LIB_SOURCE_FILES
9090
src/Class.mm
9191
src/Closure.mm
9292
src/ClassMember.mm
93-
src/MethodCif.mm
93+
src/Cif.mm
9494
src/TypeConv.mm
9595
src/Util.mm
9696
src/Struct.mm

examples/webview.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,10 @@ export class ApplicationDelegate extends NSObject {
5151

5252
/**
5353
* @implements {WKUIDelegate}
54+
* @implements {WKNavigationDelegate}
5455
*/
5556
export class ViewController extends NSViewController {
56-
static ObjCProtocols = [WKUIDelegate];
57+
static ObjCProtocols = [WKUIDelegate, WKNavigationDelegate];
5758

5859
static {
5960
NativeClass(this);
@@ -74,6 +75,7 @@ export class ViewController extends NSViewController {
7475
config,
7576
);
7677
view.UIDelegate = this;
78+
view.navigationDelegate = this;
7779
this.view = view;
7880
this.webview = view;
7981
}
@@ -93,6 +95,19 @@ export class ViewController extends NSViewController {
9395
const request = NSURLRequest.requestWithURL(url);
9496
if (this.webview) this.webview.loadRequest(request);
9597
}
98+
99+
/**
100+
* @param {WKWebView} _webView
101+
* @param {WKNavigationAction} _navigationAction
102+
* @param {(p1: interop.Enum<typeof WKNavigationActionPolicy>) => void} decisionHandler
103+
*/
104+
webViewDecidePolicyForNavigationActionDecisionHandler(
105+
_webView,
106+
_navigationAction,
107+
decisionHandler,
108+
) {
109+
decisionHandler(WKNavigationActionPolicy.Allow);
110+
}
96111
}
97112

98113
const NSApp = NSApplication.sharedApplication;

include/Block.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
11
#ifndef BLOCK_H
22
#define BLOCK_H
33

4+
#include "Cif.h"
45
#include "Closure.h"
56
#include "node_api_util.h"
67
#include <cstdlib>
78

89
namespace objc_bridge {
910

11+
class FunctionPointer {
12+
public:
13+
void *function;
14+
metagen::MDSectionOffset offset;
15+
Cif *cif;
16+
17+
static napi_value wrap(napi_env env, void *function, metagen::MDSectionOffset offset, bool isBlock);
18+
static void finalize(napi_env env, void *finalize_data, void *finalize_hint);
19+
20+
static napi_value jsCallAsCFunction(napi_env env, napi_callback_info cbinfo);
21+
static napi_value jsCallAsBlock(napi_env env, napi_callback_info cbinfo);
22+
};
23+
1024
id registerBlock(napi_env env, Closure *closure, napi_value callback);
1125

1226
NAPI_FUNCTION(registerBlock);

include/CFunction.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
#ifndef C_FUNCTION_H
22
#define C_FUNCTION_H
33

4-
#include "MethodCif.h"
4+
#include "Cif.h"
55

66
namespace objc_bridge {
77

88
class CFunction {
99
public:
10-
static napi_value JSCall(napi_env env, napi_callback_info cbinfo);
10+
static napi_value jsCall(napi_env env, napi_callback_info cbinfo);
1111

1212
CFunction(void *fnptr) : fnptr(fnptr) {}
1313
~CFunction();
1414

1515
void *fnptr;
16-
MethodCif *cif = nullptr;
16+
Cif *cif = nullptr;
1717
};
1818

1919
} // namespace objc_bridge

include/MethodCif.h renamed to include/Cif.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
namespace objc_bridge {
1212

13-
class MethodCif {
13+
class Cif {
1414
public:
1515
ffi_cif cif;
1616
unsigned int argc;
@@ -28,9 +28,9 @@ class MethodCif {
2828
bool shouldFreeAny;
2929
bool *shouldFree;
3030

31-
MethodCif(napi_env env, std::string typeEncoding);
32-
MethodCif(napi_env env, MDMetadataReader *reader, MDSectionOffset offset,
33-
bool isMethod = false);
31+
Cif(napi_env env, std::string typeEncoding);
32+
Cif(napi_env env, MDMetadataReader *reader, MDSectionOffset offset,
33+
bool isMethod = false, bool isBlock = false);
3434
};
3535

3636
} // namespace objc_bridge

include/ClassMember.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#ifndef BRIDGED_METHOD_H
22
#define BRIDGED_METHOD_H
33

4-
#include "MethodCif.h"
4+
#include "Cif.h"
55
#include "objc/runtime.h"
66
#include <iostream>
77

@@ -50,10 +50,10 @@ class ObjCClassMember {
5050
static void defineMembers(napi_env env, ObjCClassMemberMap &memberMap,
5151
MDSectionOffset offset, napi_value constructor);
5252

53-
static napi_value JSCall(napi_env env, napi_callback_info cbinfo);
54-
static napi_value JSCallInit(napi_env env, napi_callback_info cbinfo);
55-
static napi_value JSGetter(napi_env env, napi_callback_info cbinfo);
56-
static napi_value JSSetter(napi_env env, napi_callback_info cbinfo);
53+
static napi_value jsCall(napi_env env, napi_callback_info cbinfo);
54+
static napi_value jsCallInit(napi_env env, napi_callback_info cbinfo);
55+
static napi_value jsGetter(napi_env env, napi_callback_info cbinfo);
56+
static napi_value jsSetter(napi_env env, napi_callback_info cbinfo);
5757

5858
ObjCClassMember(ObjCBridgeState *bridgeState, SEL selector,
5959
MDSectionOffset offset, MDMemberFlag flags)
@@ -77,8 +77,8 @@ class ObjCClassMember {
7777
ObjCBridgeState *bridgeState;
7878
MethodDescriptor methodOrGetter;
7979
MethodDescriptor setter;
80-
MethodCif *methodCif = nullptr;
81-
MethodCif *setterMethodCif = nullptr;
80+
Cif *cif = nullptr;
81+
Cif *setterCif = nullptr;
8282
bool returnOwned;
8383
bool classMethod;
8484
};

include/ObjCBridge.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include "CFunction.h"
66
#include "Class.h"
77
#include "MetadataReader.h"
8-
#include "MethodCif.h"
8+
#include "Cif.h"
99
#include "Protocol.h"
1010
#include "Struct.h"
1111
#include "TypeConv.h"
@@ -66,8 +66,10 @@ class ObjCBridgeState {
6666

6767
ObjCProtocol *getProtocol(napi_env env, MDSectionOffset offset);
6868

69-
MethodCif *getMethodCif(napi_env env, Method method);
70-
MethodCif *getMethodCif(napi_env env, MDSectionOffset offset);
69+
Cif *getMethodCif(napi_env env, Method method);
70+
Cif *getMethodCif(napi_env env, MDSectionOffset offset);
71+
Cif *getBlockCif(napi_env env, MDSectionOffset offset);
72+
Cif *getCFunctionCif(napi_env env, MDSectionOffset offset);
7173

7274
napi_value proxyNativeObject(napi_env env, napi_value object,
7375
id nativeObject);
@@ -123,11 +125,12 @@ class ObjCBridgeState {
123125
std::unordered_map<Protocol *, MDSectionOffset> mdProtocolsByPointer;
124126
std::unordered_map<Class, napi_ref> constructorsByPointer;
125127

126-
std::unordered_map<std::string, MethodCif *> methodCifs;
128+
std::unordered_map<std::string, Cif *> cifs;
127129
std::unordered_map<MDSectionOffset, napi_ref> mdValueCache;
128130
std::unordered_map<MDSectionOffset, CFunction *> cFunctionCache;
129-
std::unordered_map<MDSectionOffset, MethodCif *> mdFunctionSignatureCache;
130-
std::unordered_map<MDSectionOffset, MethodCif *> mdMethodSignatureCache;
131+
std::unordered_map<MDSectionOffset, Cif *> mdFunctionSignatureCache;
132+
std::unordered_map<MDSectionOffset, Cif *> mdMethodSignatureCache;
133+
std::unordered_map<MDSectionOffset, Cif *> mdBlockSignatureCache;
131134
std::unordered_map<std::string, MDSectionOffset> structOffsets;
132135
std::unordered_map<std::string, MDSectionOffset> unionOffsets;
133136
// std::unordered_map<std::string, MDSectionOffset> protocolOffsets;

include/Protocol.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ NAPI_FUNCTION(protocolGetter);
1515

1616
class ObjCProtocol {
1717
public:
18-
static napi_value JSConstructor(napi_env env, napi_callback_info cbinfo);
18+
static napi_value jsConstructor(napi_env env, napi_callback_info cbinfo);
1919

2020
ObjCProtocol(napi_env env, MDSectionOffset offset);
2121
~ObjCProtocol();

src/Block.mm

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "Block.h"
2+
#include "Interop.h"
23
#include "ObjCBridge.h"
4+
#include "js_native_api.h"
35
#include "node_api_util.h"
46
#include "objc/runtime.h"
57
#import <Foundation/Foundation.h>
@@ -96,4 +98,118 @@ id registerBlock(napi_env env, Closure *closure, napi_value callback) {
9698
return callback;
9799
}
98100

101+
napi_value FunctionPointer::wrap(napi_env env, void *function,
102+
metagen::MDSectionOffset offset,
103+
bool isBlock) {
104+
FunctionPointer *ref = new FunctionPointer();
105+
ref->function = function;
106+
ref->offset = offset;
107+
108+
ObjCBridgeState *bridgeState = ObjCBridgeState::InstanceData(env);
109+
110+
if (isBlock) {
111+
ref->cif = bridgeState->getBlockCif(env, offset);
112+
} else {
113+
ref->cif = bridgeState->getCFunctionCif(env, offset);
114+
}
115+
116+
napi_value result;
117+
napi_create_function(
118+
env, isBlock ? "objcBlockWrapper" : "cFunctionWrapper", NAPI_AUTO_LENGTH,
119+
isBlock ? jsCallAsBlock : jsCallAsCFunction, ref, &result);
120+
121+
napi_ref jsRef;
122+
napi_add_finalizer(env, result, ref, FunctionPointer::finalize, nullptr,
123+
&jsRef);
124+
125+
return result;
126+
}
127+
128+
void FunctionPointer::finalize(napi_env env, void *finalize_data,
129+
void *finalize_hint) {
130+
auto ref = (FunctionPointer *)finalize_data;
131+
delete ref;
132+
}
133+
134+
napi_value FunctionPointer::jsCallAsCFunction(napi_env env,
135+
napi_callback_info cbinfo) {
136+
FunctionPointer *ref;
137+
138+
napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, (void **)&ref);
139+
140+
auto cif = ref->cif;
141+
142+
size_t argc = cif->argc;
143+
napi_get_cb_info(env, cbinfo, &argc, cif->argv, nullptr, nullptr);
144+
145+
void *avalues[cif->argc];
146+
void *rvalue = cif->rvalue;
147+
148+
bool shouldFreeAny = false;
149+
bool shouldFree[cif->argc];
150+
151+
if (cif->argc > 0) {
152+
for (unsigned int i = 0; i < cif->argc; i++) {
153+
shouldFree[i] = false;
154+
avalues[i] = cif->avalues[i];
155+
cif->argTypes[i]->toNative(env, cif->argv[i], avalues[i], &shouldFree[i],
156+
&shouldFreeAny);
157+
}
158+
}
159+
160+
ffi_call(&cif->cif, FFI_FN(ref->function), rvalue, avalues);
161+
162+
if (shouldFreeAny) {
163+
for (unsigned int i = 0; i < cif->argc; i++) {
164+
if (shouldFree[i]) {
165+
cif->argTypes[i]->free(env, *((void **)avalues[i]));
166+
}
167+
}
168+
}
169+
170+
return cif->returnType->toJS(env, rvalue);
171+
}
172+
173+
napi_value FunctionPointer::jsCallAsBlock(napi_env env,
174+
napi_callback_info cbinfo) {
175+
FunctionPointer *ref;
176+
177+
napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, (void **)&ref);
178+
179+
Block_literal_1 *block = (Block_literal_1 *)ref->function;
180+
auto cif = ref->cif;
181+
182+
size_t argc = cif->argc;
183+
napi_get_cb_info(env, cbinfo, &argc, cif->argv, nullptr, nullptr);
184+
185+
void *avalues[cif->cif.nargs];
186+
void *rvalue = cif->rvalue;
187+
188+
bool shouldFreeAny = false;
189+
bool shouldFree[cif->argc];
190+
191+
avalues[0] = &block;
192+
193+
if (cif->argc > 0) {
194+
for (unsigned int i = 0; i < cif->argc; i++) {
195+
shouldFree[i] = false;
196+
avalues[i + 1] = cif->avalues[i];
197+
cif->argTypes[i]->toNative(env, cif->argv[i], avalues[i + 1],
198+
&shouldFree[i], &shouldFreeAny);
199+
}
200+
}
201+
202+
ffi_call(&cif->cif, FFI_FN(block->invoke), rvalue, avalues);
203+
204+
if (shouldFreeAny) {
205+
for (unsigned int i = 0; i < cif->argc; i++) {
206+
if (shouldFree[i]) {
207+
cif->argTypes[i]->free(env, *((void **)avalues[i + 1]));
208+
}
209+
}
210+
}
211+
212+
return cif->returnType->toJS(env, rvalue);
213+
}
214+
99215
} // namespace objc_bridge

src/CFunction.mm

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
.setter = nullptr,
2222
.value = nullptr,
2323
.data = (void *)((size_t)originalOffset),
24-
.method = CFunction::JSCall,
24+
.method = CFunction::jsCall,
2525
};
2626

2727
napi_define_properties(env, global, 1, &prop);
@@ -36,23 +36,15 @@
3636

3737
auto sigOffset = metadata->signaturesOffset +
3838
metadata->getOffset(offset + sizeof(MDSectionOffset));
39-
auto cachedCif = mdFunctionSignatureCache.find(sigOffset);
4039

4140
auto cFunction = new CFunction(dlsym(self_dl, metadata->getString(offset)));
41+
cFunction->cif = getCFunctionCif(env, sigOffset);
4242
cFunctionCache[offset] = cFunction;
4343

44-
if (cachedCif != mdFunctionSignatureCache.end()) {
45-
cFunction->cif = cachedCif->second;
46-
} else {
47-
auto cif = new MethodCif(env, metadata, sigOffset);
48-
cFunction->cif = cif;
49-
mdFunctionSignatureCache[sigOffset] = cif;
50-
}
51-
5244
return cFunction;
5345
}
5446

55-
napi_value CFunction::JSCall(napi_env env, napi_callback_info cbinfo) {
47+
napi_value CFunction::jsCall(napi_env env, napi_callback_info cbinfo) {
5648
void *_offset;
5749

5850
napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, &_offset);

0 commit comments

Comments
 (0)