Skip to content

Commit caf1c60

Browse files
authored
refactor: custom classes -> ClassBuilder, improve memory model (#23)
* refactor: custom classes -> ClassBuilder, improve memory model * complete ClassBuilder rework, classes now built lazily * use replace method instead of add method * fix minor bug * implement __extends natively and other helpers using JS
1 parent 019d263 commit caf1c60

18 files changed

+665
-658
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,10 @@ set(LIB_SOURCE_FILES
9999
src/Enum.mm
100100
src/Variable.mm
101101
src/Object.mm
102-
src/CustomClass.mm
103102
src/CFunction.mm
104103
src/Interop.mm
105104
src/InlineFunctions.mm
105+
src/ClassBuilder.mm
106106
)
107107

108108
add_library(

include/CFunction.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@
55

66
namespace objc_bridge {
77

8-
typedef struct CFunction {
9-
void *fnptr;
10-
MethodCif *cif;
11-
} CFunction;
8+
class CFunction {
9+
public:
10+
static napi_value JSCall(napi_env env, napi_callback_info cbinfo);
11+
12+
CFunction(void *fnptr) : fnptr(fnptr) {}
13+
~CFunction();
1214

13-
napi_value JS_CFunction(napi_env env, napi_callback_info cbinfo);
15+
void *fnptr;
16+
MethodCif *cif = nullptr;
17+
};
1418

1519
} // namespace objc_bridge
1620

include/Class.h

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,23 @@ NAPI_FUNCTION(registerClass);
1717
NAPI_FUNCTION(import);
1818
NAPI_FUNCTION(classGetter);
1919

20+
class ObjCBridgeState;
21+
2022
class ObjCClass {
2123
public:
22-
MDSectionOffset metadataOffset;
23-
std::string name;
24-
Class nativeClass;
25-
26-
ObjCClass *superclass;
27-
std::vector<std::shared_ptr<ObjCClassMember>> members;
24+
ObjCClass() {}
25+
ObjCClass(napi_env env, MDSectionOffset offset);
26+
~ObjCClass();
2827

28+
ObjCBridgeState *bridgeState;
2929
napi_env env;
3030
napi_ref constructor;
3131
napi_ref prototype;
32-
33-
ObjCClass() {}
34-
ObjCClass(napi_env env, MDSectionOffset offset);
35-
~ObjCClass();
32+
MDSectionOffset metadataOffset;
33+
std::string name;
34+
Class nativeClass;
35+
ObjCClass *superclass;
36+
ObjCClassMemberMap members;
3637
};
3738

3839
} // namespace objc_bridge

include/ClassBuilder.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#ifndef CLASS_BUILDER_H
2+
#define CLASS_BUILDER_H
3+
4+
#include "Class.h"
5+
#include "ClassMember.h"
6+
#include "objc/runtime.h"
7+
8+
@protocol ObjCBridgeClassBuilderProtocol
9+
10+
@end
11+
12+
namespace objc_bridge {
13+
14+
class ObjCProtocol;
15+
16+
class ClassBuilder : public ObjCClass {
17+
public:
18+
ClassBuilder(napi_env env, napi_value constructor);
19+
~ClassBuilder();
20+
21+
void addProtocol(ObjCProtocol *protocol);
22+
MethodDescriptor *lookupMethodDescriptor(std::string &name);
23+
void addMethod(std::string &name, MethodDescriptor *desc, napi_value key);
24+
25+
void build();
26+
27+
MethodMap exposedMethods;
28+
std::unordered_set<ObjCProtocol *> protocols;
29+
bool isFinal = false;
30+
};
31+
32+
} // namespace objc_bridge
33+
34+
#endif /* CLASS_BUILDER_H */

include/ClassMember.h

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,75 @@
33

44
#include "MethodCif.h"
55
#include "objc/runtime.h"
6+
#include <iostream>
67

78
namespace objc_bridge {
89

9-
napi_value JS_BridgedMethod(napi_env env, napi_callback_info cbinfo);
10-
napi_value JS_BridgedGetter(napi_env env, napi_callback_info cbinfo);
11-
napi_value JS_BridgedSetter(napi_env env, napi_callback_info cbinfo);
12-
1310
class ObjCBridgeState;
1411

12+
enum MethodDescriptorKind : uint8_t {
13+
kMethodDescSignatureOffset,
14+
kMethodDescEncoding,
15+
};
16+
17+
class MethodDescriptor {
18+
public:
19+
SEL selector;
20+
21+
MethodDescriptorKind kind;
22+
23+
MDSectionOffset signatureOffset;
24+
std::string encoding;
25+
26+
MethodDescriptor() {}
27+
28+
MethodDescriptor(SEL selector, MDSectionOffset offset)
29+
: selector(selector), kind(kMethodDescSignatureOffset),
30+
signatureOffset(offset) {}
31+
32+
MethodDescriptor(SEL selector, char *encoding)
33+
: selector(selector), kind(kMethodDescEncoding), encoding(encoding) {}
34+
35+
MethodDescriptor(SEL selector, std::string encoding)
36+
: kind(kMethodDescEncoding), encoding(encoding) {
37+
this->selector = selector;
38+
}
39+
};
40+
41+
typedef std::unordered_map<std::string, MethodDescriptor> MethodMap;
42+
43+
class ObjCClassMember;
44+
45+
typedef std::unordered_map<std::string, ObjCClassMember> ObjCClassMemberMap;
46+
1547
class ObjCClassMember {
1648
public:
49+
static void defineMembers(napi_env env, ObjCClassMemberMap &memberMap,
50+
MDSectionOffset offset, napi_value constructor);
51+
52+
static napi_value JSCall(napi_env env, napi_callback_info cbinfo);
53+
static napi_value JSGetter(napi_env env, napi_callback_info cbinfo);
54+
static napi_value JSSetter(napi_env env, napi_callback_info cbinfo);
55+
56+
ObjCClassMember(ObjCBridgeState *bridgeState, SEL selector,
57+
MDSectionOffset offset, MDMemberFlag flags)
58+
: bridgeState(bridgeState),
59+
methodOrGetter(MethodDescriptor(selector, offset)),
60+
returnOwned((flags & metagen::mdMemberReturnOwned) != 0),
61+
classMethod((flags & metagen::mdMemberStatic) != 0) {}
62+
63+
ObjCClassMember(ObjCBridgeState *bridgeState, SEL getterSelector,
64+
SEL setterSelector, MDSectionOffset getterOffset,
65+
MDSectionOffset setterOffset, MDMemberFlag flags)
66+
: bridgeState(bridgeState),
67+
methodOrGetter(MethodDescriptor(getterSelector, getterOffset)),
68+
setter(MethodDescriptor(setterSelector, setterOffset)),
69+
returnOwned((flags & metagen::mdMemberReturnOwned) != 0),
70+
classMethod((flags & metagen::mdMemberStatic) != 0) {}
71+
1772
ObjCBridgeState *bridgeState;
18-
// Can be either method selector or property getter selector
19-
SEL selector;
20-
SEL setterSelector;
21-
MDSectionOffset signature;
22-
MDSectionOffset setterSignature;
73+
MethodDescriptor methodOrGetter;
74+
MethodDescriptor setter;
2375
MethodCif *methodCif = nullptr;
2476
MethodCif *setterMethodCif = nullptr;
2577
bool returnOwned;

include/Closure.h

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,18 @@
1111

1212
namespace objc_bridge {
1313

14-
void callJSBlockFromMainThread(napi_env env, napi_value js_cb, void *context,
15-
void *data);
16-
1714
class Closure {
1815
public:
16+
static void callBlockFromMainThread(napi_env env, napi_value js_cb,
17+
void *context, void *data);
18+
19+
Closure(std::string typeEncoding, bool isBlock);
20+
Closure(MDMetadataReader *reader, MDSectionOffset offset,
21+
bool isBlock = false, std::string *encoding = nullptr,
22+
bool isMethod = false);
23+
24+
~Closure();
25+
1926
napi_env env;
2027
napi_ref thisConstructor;
2128
napi_ref func = nullptr;
@@ -30,21 +37,6 @@ class Closure {
3037

3138
std::shared_ptr<TypeConv> returnType;
3239
std::vector<std::shared_ptr<TypeConv>> argTypes;
33-
34-
Closure(std::string typeEncoding, bool isBlock);
35-
Closure(MDMetadataReader *reader, MDSectionOffset offset,
36-
bool isBlock = false, std::string *encoding = nullptr,
37-
bool isMethod = false);
38-
39-
~Closure() {
40-
if (func != nullptr) {
41-
napi_delete_reference(env, func);
42-
}
43-
if (tsfn != nullptr) {
44-
napi_release_threadsafe_function(tsfn, napi_tsfn_abort);
45-
}
46-
ffi_closure_free(closure);
47-
}
4840
};
4941

5042
} // namespace objc_bridge

include/ObjCBridge.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class ObjCBridgeState {
6666

6767
ObjCClass *getClass(napi_env env, MDSectionOffset offset);
6868

69-
BridgedProtocol *getProtocol(napi_env env, MDSectionOffset offset);
69+
ObjCProtocol *getProtocol(napi_env env, MDSectionOffset offset);
7070

7171
MethodCif *getMethodCif(napi_env env, Method method);
7272
MethodCif *getMethodCif(napi_env env, MDSectionOffset offset);
@@ -78,8 +78,6 @@ class ObjCBridgeState {
7878
MDSectionOffset classOffset = 0,
7979
std::vector<MDSectionOffset> *protocolOffsets = nullptr);
8080

81-
void registerClass(napi_env env, napi_value constructor);
82-
8381
void unregisterObject(id object) noexcept;
8482

8583
CFunction *getCFunction(napi_env env, MDSectionOffset offset);
@@ -115,7 +113,7 @@ class ObjCBridgeState {
115113
napi_ref referenceClass;
116114

117115
std::unordered_map<MDSectionOffset, ObjCClass *> classes;
118-
std::unordered_map<MDSectionOffset, BridgedProtocol *> protocols;
116+
std::unordered_map<MDSectionOffset, ObjCProtocol *> protocols;
119117
std::unordered_map<Class, ObjCClass *> classesByPointer;
120118
std::unordered_map<Class, MDSectionOffset> mdClassesByPointer;
121119
std::unordered_map<Protocol *, MDSectionOffset> mdProtocolsByPointer;

include/Protocol.h

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

4+
#include "ClassMember.h"
45
#include "MetadataReader.h"
56
#include "node_api_util.h"
67
#include <string>
8+
#include <unordered_set>
79

810
using namespace metagen;
911

1012
namespace objc_bridge {
1113

1214
NAPI_FUNCTION(protocolGetter);
13-
NAPI_FUNCTION(ProtocolConstructor);
1415

15-
void defineProtocolMembers(napi_env env, MDSectionOffset offset,
16-
napi_value constructor);
17-
18-
class BridgedProtocol {
16+
class ObjCProtocol {
1917
public:
18+
static napi_value JSConstructor(napi_env env, napi_callback_info cbinfo);
19+
20+
ObjCProtocol(napi_env env, MDSectionOffset offset);
21+
~ObjCProtocol();
22+
23+
napi_env env;
2024
MDSectionOffset metadataOffset;
2125
std::string name;
2226
napi_ref constructor;
2327
MDSectionOffset membersOffset;
24-
25-
BridgedProtocol(napi_env env, MDSectionOffset offset);
28+
ObjCClassMemberMap members;
29+
std::unordered_set<ObjCProtocol *> protocols;
2630
};
2731

2832
} // namespace objc_bridge

src/Block.mm

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ id registerBlock(napi_env env, Closure *closure, napi_value callback) {
6868
if (napiSupportsThreadsafeFunctions(bridgeState->self_dl)) {
6969
napi_value workName;
7070
napi_create_string_utf8(env, "Block", NAPI_AUTO_LENGTH, &workName);
71-
napi_create_threadsafe_function(env, callback, nullptr, workName, 0, 1,
72-
nullptr, nullptr, closure,
73-
callJSBlockFromMainThread, &closure->tsfn);
71+
napi_create_threadsafe_function(
72+
env, callback, nullptr, workName, 0, 1, nullptr, nullptr, closure,
73+
Closure::callBlockFromMainThread, &closure->tsfn);
7474
if (closure->tsfn)
7575
napi_unref_threadsafe_function(env, closure->tsfn);
7676
}

src/CFunction.mm

Lines changed: 9 additions & 5 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 = JS_CFunction,
24+
.method = CFunction::JSCall,
2525
};
2626

2727
napi_define_properties(env, global, 1, &prop);
@@ -38,9 +38,7 @@
3838
metadata->getOffset(offset + sizeof(MDSectionOffset));
3939
auto cachedCif = mdFunctionSignatureCache.find(sigOffset);
4040

41-
auto cFunction = new CFunction();
42-
cFunction->fnptr = dlsym(self_dl, metadata->getString(offset));
43-
cFunction->cif = nullptr;
41+
auto cFunction = new CFunction(dlsym(self_dl, metadata->getString(offset)));
4442
cFunctionCache[offset] = cFunction;
4543

4644
if (cachedCif != mdFunctionSignatureCache.end()) {
@@ -54,7 +52,7 @@
5452
return cFunction;
5553
}
5654

57-
NAPI_FUNCTION(CFunction) {
55+
napi_value CFunction::JSCall(napi_env env, napi_callback_info cbinfo) {
5856
void *_offset;
5957

6058
napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, &_offset);
@@ -96,4 +94,10 @@
9694
return cif->returnType->toJS(env, rvalue);
9795
}
9896

97+
CFunction::~CFunction() {
98+
if (cif != nullptr) {
99+
delete cif;
100+
}
101+
}
102+
99103
} // namespace objc_bridge

0 commit comments

Comments
 (0)