objc-js 0.0.15 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -288
- package/binding.gyp +2 -1
- package/dist/native.js +2 -1
- package/package.json +12 -6
- package/prebuilds/darwin-arm64/objc-js.node +0 -0
- package/prebuilds/darwin-x64/objc-js.node +0 -0
- package/src/native/ObjcObject.mm +2 -14
- package/src/native/constants.h +42 -0
- package/src/native/ffi-utils.h +103 -1
- package/src/native/forwarding-common.h +87 -0
- package/src/native/forwarding-common.mm +155 -0
- package/src/native/memory-utils.h +197 -0
- package/src/native/method-forwarding.mm +137 -208
- package/src/native/nobjc.mm +7 -31
- package/src/native/pointer-utils.h +63 -0
- package/src/native/protocol-impl.mm +7 -27
- package/src/native/protocol-manager.h +145 -0
- package/src/native/protocol-storage.h +12 -33
- package/src/native/runtime-detection.h +54 -0
- package/src/native/subclass-impl.mm +232 -566
- package/src/native/subclass-manager.h +170 -0
- package/src/native/super-call-helpers.h +361 -0
- package/src/native/type-conversion.h +200 -246
- package/src/native/type-dispatch.h +241 -0
- package/build/Release/nobjc_native.node +0 -0
|
@@ -1,7 +1,33 @@
|
|
|
1
1
|
#ifndef TYPE_CONVERSION_H
|
|
2
2
|
#define TYPE_CONVERSION_H
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* @file type-conversion.h
|
|
6
|
+
* @brief Type conversion utilities for nobjc.
|
|
7
|
+
*
|
|
8
|
+
* This header provides utilities for converting between JavaScript and
|
|
9
|
+
* Objective-C types. It includes:
|
|
10
|
+
*
|
|
11
|
+
* - Type Encoding Utilities:
|
|
12
|
+
* - SimplifiedTypeEncoding: Class to strip type qualifiers from ObjC encodings
|
|
13
|
+
* - SimplifyTypeEncoding(): Legacy function for the same purpose
|
|
14
|
+
*
|
|
15
|
+
* - ObjC to JS Conversion:
|
|
16
|
+
* - ObjCToJS(): Convert ObjC value at pointer to JS value
|
|
17
|
+
* - ExtractInvocationArgumentToJS(): Extract NSInvocation arg to JS
|
|
18
|
+
* - GetInvocationReturnAsJS(): Get NSInvocation return value as JS
|
|
19
|
+
*
|
|
20
|
+
* - JS to ObjC Conversion:
|
|
21
|
+
* - SetInvocationReturnFromJS(): Set NSInvocation return from JS value
|
|
22
|
+
*
|
|
23
|
+
* The conversion functions use a visitor pattern (via type-dispatch.h) to
|
|
24
|
+
* handle different type codes with minimal code duplication.
|
|
25
|
+
*
|
|
26
|
+
* @see type-dispatch.h for the underlying dispatch mechanism
|
|
27
|
+
*/
|
|
28
|
+
|
|
4
29
|
#include "ObjcObject.h"
|
|
30
|
+
#include "type-dispatch.h"
|
|
5
31
|
#include <Foundation/Foundation.h>
|
|
6
32
|
#include <napi.h>
|
|
7
33
|
#include <objc/runtime.h>
|
|
@@ -10,229 +36,165 @@
|
|
|
10
36
|
// MARK: - Type Encoding Utilities
|
|
11
37
|
|
|
12
38
|
// Helper class to manage the lifetime of simplified type encodings
|
|
39
|
+
// Optimized to use pointer offset instead of string::erase()
|
|
13
40
|
class SimplifiedTypeEncoding {
|
|
14
41
|
private:
|
|
15
|
-
|
|
42
|
+
const char* original;
|
|
43
|
+
size_t offset; // Offset past any leading qualifiers
|
|
44
|
+
|
|
45
|
+
// Check if a character is a type qualifier
|
|
46
|
+
static bool IsQualifier(char c) {
|
|
47
|
+
// r=const, n=in, N=inout, o=out, O=bycopy, R=byref, V=oneway
|
|
48
|
+
return c == 'r' || c == 'n' || c == 'N' || c == 'o' ||
|
|
49
|
+
c == 'O' || c == 'R' || c == 'V';
|
|
50
|
+
}
|
|
16
51
|
|
|
17
52
|
public:
|
|
18
|
-
SimplifiedTypeEncoding(const char *typeEncoding)
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
simplified[0] == 'V')) {
|
|
26
|
-
simplified.erase(0, 1);
|
|
53
|
+
SimplifiedTypeEncoding(const char *typeEncoding)
|
|
54
|
+
: original(typeEncoding), offset(0) {
|
|
55
|
+
// Skip leading qualifiers using pointer arithmetic (O(k) where k = qualifier count)
|
|
56
|
+
if (original) {
|
|
57
|
+
while (original[offset] != '\0' && IsQualifier(original[offset])) {
|
|
58
|
+
++offset;
|
|
59
|
+
}
|
|
27
60
|
}
|
|
28
61
|
}
|
|
29
62
|
|
|
30
|
-
const char *c_str() const { return
|
|
31
|
-
char operator[](size_t index) const {
|
|
32
|
-
|
|
63
|
+
const char *c_str() const { return original ? original + offset : ""; }
|
|
64
|
+
char operator[](size_t index) const {
|
|
65
|
+
return original ? original[offset + index] : '\0';
|
|
66
|
+
}
|
|
67
|
+
operator const char *() const { return c_str(); }
|
|
68
|
+
|
|
69
|
+
// Check if empty (after stripping qualifiers)
|
|
70
|
+
bool empty() const { return !original || original[offset] == '\0'; }
|
|
33
71
|
};
|
|
34
72
|
|
|
35
73
|
// Legacy function for compatibility - returns pointer to internal string
|
|
36
|
-
//
|
|
37
|
-
// parameter is valid
|
|
74
|
+
// Optimized to use pointer arithmetic instead of string mutations
|
|
38
75
|
inline const char *SimplifyTypeEncoding(const char *typeEncoding) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// This is a temporary fix - callers should use SimplifiedTypeEncoding class
|
|
49
|
-
static thread_local std::string buffer;
|
|
50
|
-
buffer = typeEncoding;
|
|
51
|
-
while (!buffer.empty() &&
|
|
52
|
-
(buffer[0] == 'r' || buffer[0] == 'n' || buffer[0] == 'N' ||
|
|
53
|
-
buffer[0] == 'o' || buffer[0] == 'O' || buffer[0] == 'R' ||
|
|
54
|
-
buffer[0] == 'V')) {
|
|
55
|
-
buffer.erase(0, 1);
|
|
56
|
-
}
|
|
57
|
-
return buffer.c_str();
|
|
76
|
+
if (!typeEncoding) return "";
|
|
77
|
+
|
|
78
|
+
// Skip leading qualifiers using pointer arithmetic
|
|
79
|
+
const char* ptr = typeEncoding;
|
|
80
|
+
while (*ptr == 'r' || *ptr == 'n' || *ptr == 'N' || *ptr == 'o' ||
|
|
81
|
+
*ptr == 'O' || *ptr == 'R' || *ptr == 'V') {
|
|
82
|
+
++ptr;
|
|
83
|
+
}
|
|
84
|
+
return ptr;
|
|
58
85
|
}
|
|
59
86
|
|
|
60
87
|
// MARK: - ObjC to JS Conversion
|
|
61
88
|
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
case 's': {
|
|
74
|
-
short value = *(short *)valuePtr;
|
|
75
|
-
return Napi::Number::New(env, value);
|
|
76
|
-
}
|
|
77
|
-
case 'l': {
|
|
78
|
-
long value = *(long *)valuePtr;
|
|
79
|
-
return Napi::Number::New(env, value);
|
|
80
|
-
}
|
|
81
|
-
case 'q': {
|
|
82
|
-
long long value = *(long long *)valuePtr;
|
|
83
|
-
return Napi::Number::New(env, value);
|
|
84
|
-
}
|
|
85
|
-
case 'C': {
|
|
86
|
-
unsigned char value = *(unsigned char *)valuePtr;
|
|
87
|
-
return Napi::Number::New(env, value);
|
|
88
|
-
}
|
|
89
|
-
case 'I': {
|
|
90
|
-
unsigned int value = *(unsigned int *)valuePtr;
|
|
91
|
-
return Napi::Number::New(env, value);
|
|
92
|
-
}
|
|
93
|
-
case 'S': {
|
|
94
|
-
unsigned short value = *(unsigned short *)valuePtr;
|
|
95
|
-
return Napi::Number::New(env, value);
|
|
96
|
-
}
|
|
97
|
-
case 'L': {
|
|
98
|
-
unsigned long value = *(unsigned long *)valuePtr;
|
|
99
|
-
return Napi::Number::New(env, value);
|
|
100
|
-
}
|
|
101
|
-
case 'Q': {
|
|
102
|
-
unsigned long long value = *(unsigned long long *)valuePtr;
|
|
103
|
-
return Napi::Number::New(env, value);
|
|
104
|
-
}
|
|
105
|
-
case 'f': {
|
|
106
|
-
float value = *(float *)valuePtr;
|
|
107
|
-
return Napi::Number::New(env, value);
|
|
108
|
-
}
|
|
109
|
-
case 'd': {
|
|
110
|
-
double value = *(double *)valuePtr;
|
|
111
|
-
return Napi::Number::New(env, value);
|
|
89
|
+
// Visitor for converting ObjC values to JS
|
|
90
|
+
struct ObjCToJSVisitor {
|
|
91
|
+
Napi::Env env;
|
|
92
|
+
void* valuePtr;
|
|
93
|
+
|
|
94
|
+
// Numeric types -> Number (or Boolean for bool)
|
|
95
|
+
template <typename T>
|
|
96
|
+
auto operator()(std::type_identity<T>) const
|
|
97
|
+
-> std::enable_if_t<is_numeric_v<T> && !std::is_same_v<T, bool>, Napi::Value> {
|
|
98
|
+
T value = *static_cast<T*>(valuePtr);
|
|
99
|
+
return Napi::Number::New(env, static_cast<double>(value));
|
|
112
100
|
}
|
|
113
|
-
|
|
114
|
-
|
|
101
|
+
|
|
102
|
+
// Bool -> Boolean
|
|
103
|
+
Napi::Value operator()(std::type_identity<bool>) const {
|
|
104
|
+
bool value = *static_cast<bool*>(valuePtr);
|
|
115
105
|
return Napi::Boolean::New(env, value);
|
|
116
106
|
}
|
|
117
|
-
|
|
118
|
-
|
|
107
|
+
|
|
108
|
+
// C string -> String or Null
|
|
109
|
+
Napi::Value operator()(std::type_identity<ObjCCStringTag>) const {
|
|
110
|
+
char* value = *static_cast<char**>(valuePtr);
|
|
119
111
|
if (value == nullptr) {
|
|
120
112
|
return env.Null();
|
|
121
113
|
}
|
|
122
114
|
return Napi::String::New(env, value);
|
|
123
115
|
}
|
|
124
|
-
|
|
125
|
-
|
|
116
|
+
|
|
117
|
+
// id -> ObjcObject or Null
|
|
118
|
+
Napi::Value operator()(std::type_identity<ObjCIdTag>) const {
|
|
119
|
+
id value = *static_cast<__strong id*>(valuePtr);
|
|
126
120
|
if (value == nil) {
|
|
127
121
|
return env.Null();
|
|
128
122
|
}
|
|
129
123
|
return ObjcObject::NewInstance(env, value);
|
|
130
124
|
}
|
|
131
|
-
|
|
132
|
-
|
|
125
|
+
|
|
126
|
+
// Class -> ObjcObject or Null
|
|
127
|
+
Napi::Value operator()(std::type_identity<ObjCClassTag>) const {
|
|
128
|
+
Class value = *static_cast<Class*>(valuePtr);
|
|
133
129
|
if (value == nil) {
|
|
134
130
|
return env.Null();
|
|
135
131
|
}
|
|
136
132
|
return ObjcObject::NewInstance(env, value);
|
|
137
133
|
}
|
|
138
|
-
|
|
139
|
-
|
|
134
|
+
|
|
135
|
+
// SEL -> String or Null
|
|
136
|
+
Napi::Value operator()(std::type_identity<ObjCSELTag>) const {
|
|
137
|
+
SEL value = *static_cast<SEL*>(valuePtr);
|
|
140
138
|
if (value == nullptr) {
|
|
141
139
|
return env.Null();
|
|
142
140
|
}
|
|
143
|
-
NSString
|
|
141
|
+
NSString* selectorString = NSStringFromSelector(value);
|
|
144
142
|
if (selectorString == nil) {
|
|
145
143
|
return env.Null();
|
|
146
144
|
}
|
|
147
145
|
return Napi::String::New(env, [selectorString UTF8String]);
|
|
148
146
|
}
|
|
149
|
-
|
|
147
|
+
|
|
148
|
+
// Pointer -> Undefined (not fully supported)
|
|
149
|
+
Napi::Value operator()(std::type_identity<ObjCPointerTag>) const {
|
|
150
150
|
return env.Undefined();
|
|
151
|
-
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Void -> Undefined
|
|
154
|
+
Napi::Value operator()(std::type_identity<ObjCVoidTag>) const {
|
|
152
155
|
return env.Undefined();
|
|
153
156
|
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// Convert an Objective-C value (from a pointer) to a JavaScript value
|
|
160
|
+
inline Napi::Value ObjCToJS(Napi::Env env, void *valuePtr, char typeCode) {
|
|
161
|
+
return DispatchByTypeCode(typeCode, ObjCToJSVisitor{env, valuePtr});
|
|
154
162
|
}
|
|
155
163
|
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
case 'i': {
|
|
168
|
-
int value;
|
|
169
|
-
[invocation getArgument:&value atIndex:index];
|
|
170
|
-
return Napi::Number::New(env, value);
|
|
171
|
-
}
|
|
172
|
-
case 's': {
|
|
173
|
-
short value;
|
|
174
|
-
[invocation getArgument:&value atIndex:index];
|
|
175
|
-
return Napi::Number::New(env, value);
|
|
176
|
-
}
|
|
177
|
-
case 'l': {
|
|
178
|
-
long value;
|
|
179
|
-
[invocation getArgument:&value atIndex:index];
|
|
180
|
-
return Napi::Number::New(env, value);
|
|
181
|
-
}
|
|
182
|
-
case 'q': {
|
|
183
|
-
long long value;
|
|
184
|
-
[invocation getArgument:&value atIndex:index];
|
|
185
|
-
return Napi::Number::New(env, value);
|
|
186
|
-
}
|
|
187
|
-
case 'C': {
|
|
188
|
-
unsigned char value;
|
|
189
|
-
[invocation getArgument:&value atIndex:index];
|
|
190
|
-
return Napi::Number::New(env, value);
|
|
191
|
-
}
|
|
192
|
-
case 'I': {
|
|
193
|
-
unsigned int value;
|
|
194
|
-
[invocation getArgument:&value atIndex:index];
|
|
195
|
-
return Napi::Number::New(env, value);
|
|
196
|
-
}
|
|
197
|
-
case 'S': {
|
|
198
|
-
unsigned short value;
|
|
199
|
-
[invocation getArgument:&value atIndex:index];
|
|
200
|
-
return Napi::Number::New(env, value);
|
|
201
|
-
}
|
|
202
|
-
case 'L': {
|
|
203
|
-
unsigned long value;
|
|
204
|
-
[invocation getArgument:&value atIndex:index];
|
|
205
|
-
return Napi::Number::New(env, value);
|
|
206
|
-
}
|
|
207
|
-
case 'Q': {
|
|
208
|
-
unsigned long long value;
|
|
209
|
-
[invocation getArgument:&value atIndex:index];
|
|
210
|
-
return Napi::Number::New(env, value);
|
|
211
|
-
}
|
|
212
|
-
case 'f': {
|
|
213
|
-
float value;
|
|
214
|
-
[invocation getArgument:&value atIndex:index];
|
|
215
|
-
return Napi::Number::New(env, value);
|
|
216
|
-
}
|
|
217
|
-
case 'd': {
|
|
218
|
-
double value;
|
|
164
|
+
// Visitor for extracting NSInvocation arguments to JS
|
|
165
|
+
struct ExtractInvocationArgVisitor {
|
|
166
|
+
Napi::Env env;
|
|
167
|
+
NSInvocation* invocation;
|
|
168
|
+
NSUInteger index;
|
|
169
|
+
|
|
170
|
+
// Numeric types -> Number (or Boolean for bool)
|
|
171
|
+
template <typename T>
|
|
172
|
+
auto operator()(std::type_identity<T>) const
|
|
173
|
+
-> std::enable_if_t<is_numeric_v<T> && !std::is_same_v<T, bool>, Napi::Value> {
|
|
174
|
+
T value;
|
|
219
175
|
[invocation getArgument:&value atIndex:index];
|
|
220
|
-
return Napi::Number::New(env, value);
|
|
176
|
+
return Napi::Number::New(env, static_cast<double>(value));
|
|
221
177
|
}
|
|
222
|
-
|
|
178
|
+
|
|
179
|
+
// Bool -> Boolean
|
|
180
|
+
Napi::Value operator()(std::type_identity<bool>) const {
|
|
223
181
|
bool value;
|
|
224
182
|
[invocation getArgument:&value atIndex:index];
|
|
225
183
|
return Napi::Boolean::New(env, value);
|
|
226
184
|
}
|
|
227
|
-
|
|
228
|
-
|
|
185
|
+
|
|
186
|
+
// C string -> String or Null
|
|
187
|
+
Napi::Value operator()(std::type_identity<ObjCCStringTag>) const {
|
|
188
|
+
char* value;
|
|
229
189
|
[invocation getArgument:&value atIndex:index];
|
|
230
190
|
if (value == nullptr) {
|
|
231
191
|
return env.Null();
|
|
232
192
|
}
|
|
233
193
|
return Napi::String::New(env, value);
|
|
234
194
|
}
|
|
235
|
-
|
|
195
|
+
|
|
196
|
+
// id -> ObjcObject or Null
|
|
197
|
+
Napi::Value operator()(std::type_identity<ObjCIdTag>) const {
|
|
236
198
|
__unsafe_unretained id value;
|
|
237
199
|
[invocation getArgument:&value atIndex:index];
|
|
238
200
|
if (value == nil) {
|
|
@@ -240,7 +202,9 @@ inline Napi::Value ExtractInvocationArgumentToJS(Napi::Env env,
|
|
|
240
202
|
}
|
|
241
203
|
return ObjcObject::NewInstance(env, value);
|
|
242
204
|
}
|
|
243
|
-
|
|
205
|
+
|
|
206
|
+
// Class -> ObjcObject or Null
|
|
207
|
+
Napi::Value operator()(std::type_identity<ObjCClassTag>) const {
|
|
244
208
|
Class value;
|
|
245
209
|
[invocation getArgument:&value atIndex:index];
|
|
246
210
|
if (value == nil) {
|
|
@@ -248,29 +212,43 @@ inline Napi::Value ExtractInvocationArgumentToJS(Napi::Env env,
|
|
|
248
212
|
}
|
|
249
213
|
return ObjcObject::NewInstance(env, value);
|
|
250
214
|
}
|
|
251
|
-
|
|
215
|
+
|
|
216
|
+
// SEL -> String or Null
|
|
217
|
+
Napi::Value operator()(std::type_identity<ObjCSELTag>) const {
|
|
252
218
|
SEL value;
|
|
253
219
|
[invocation getArgument:&value atIndex:index];
|
|
254
220
|
if (value == nullptr) {
|
|
255
221
|
return env.Null();
|
|
256
222
|
}
|
|
257
|
-
NSString
|
|
223
|
+
NSString* selString = NSStringFromSelector(value);
|
|
258
224
|
if (selString == nil) {
|
|
259
225
|
return env.Null();
|
|
260
226
|
}
|
|
261
227
|
return Napi::String::New(env, [selString UTF8String]);
|
|
262
228
|
}
|
|
263
|
-
|
|
264
|
-
|
|
229
|
+
|
|
230
|
+
// Pointer -> Undefined (not fully supported)
|
|
231
|
+
Napi::Value operator()(std::type_identity<ObjCPointerTag>) const {
|
|
232
|
+
void* value;
|
|
265
233
|
[invocation getArgument:&value atIndex:index];
|
|
266
234
|
if (value == nullptr) {
|
|
267
235
|
return env.Null();
|
|
268
236
|
}
|
|
269
237
|
return env.Undefined();
|
|
270
238
|
}
|
|
271
|
-
|
|
239
|
+
|
|
240
|
+
// Void -> Undefined
|
|
241
|
+
Napi::Value operator()(std::type_identity<ObjCVoidTag>) const {
|
|
272
242
|
return env.Undefined();
|
|
273
243
|
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// Extract an argument from NSInvocation and convert to JS value
|
|
247
|
+
inline Napi::Value ExtractInvocationArgumentToJS(Napi::Env env,
|
|
248
|
+
NSInvocation *invocation,
|
|
249
|
+
NSUInteger index,
|
|
250
|
+
char typeCode) {
|
|
251
|
+
return DispatchByTypeCode(typeCode, ExtractInvocationArgVisitor{env, invocation, index});
|
|
274
252
|
}
|
|
275
253
|
|
|
276
254
|
// MARK: - JS to ObjC Return Value Conversion
|
|
@@ -491,90 +469,49 @@ inline void SetInvocationReturnFromJS(NSInvocation *invocation,
|
|
|
491
469
|
|
|
492
470
|
// MARK: - Return Value Extraction from NSInvocation
|
|
493
471
|
|
|
494
|
-
//
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
SimplifiedTypeEncoding returnType([methodSignature methodReturnType]);
|
|
472
|
+
// Visitor for getting return values from NSInvocation
|
|
473
|
+
struct GetInvocationReturnVisitor {
|
|
474
|
+
Napi::Env env;
|
|
475
|
+
NSInvocation* invocation;
|
|
499
476
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
}
|
|
506
|
-
case 'i': {
|
|
507
|
-
int result;
|
|
508
|
-
[invocation getReturnValue:&result];
|
|
509
|
-
return Napi::Number::New(env, result);
|
|
510
|
-
}
|
|
511
|
-
case 's': {
|
|
512
|
-
short result;
|
|
513
|
-
[invocation getReturnValue:&result];
|
|
514
|
-
return Napi::Number::New(env, result);
|
|
515
|
-
}
|
|
516
|
-
case 'l': {
|
|
517
|
-
long result;
|
|
518
|
-
[invocation getReturnValue:&result];
|
|
519
|
-
return Napi::Number::New(env, result);
|
|
520
|
-
}
|
|
521
|
-
case 'q': {
|
|
522
|
-
long long result;
|
|
523
|
-
[invocation getReturnValue:&result];
|
|
524
|
-
return Napi::Number::New(env, result);
|
|
525
|
-
}
|
|
526
|
-
case 'C': {
|
|
527
|
-
unsigned char result;
|
|
528
|
-
[invocation getReturnValue:&result];
|
|
529
|
-
return Napi::Number::New(env, result);
|
|
530
|
-
}
|
|
531
|
-
case 'I': {
|
|
532
|
-
unsigned int result;
|
|
533
|
-
[invocation getReturnValue:&result];
|
|
534
|
-
return Napi::Number::New(env, result);
|
|
535
|
-
}
|
|
536
|
-
case 'S': {
|
|
537
|
-
unsigned short result;
|
|
477
|
+
// Numeric types -> Number (or Boolean for bool)
|
|
478
|
+
template <typename T>
|
|
479
|
+
auto operator()(std::type_identity<T>) const
|
|
480
|
+
-> std::enable_if_t<is_numeric_v<T> && !std::is_same_v<T, bool>, Napi::Value> {
|
|
481
|
+
T result;
|
|
538
482
|
[invocation getReturnValue:&result];
|
|
539
|
-
return Napi::Number::New(env, result);
|
|
483
|
+
return Napi::Number::New(env, static_cast<double>(result));
|
|
540
484
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
return Napi::Number::New(env, result);
|
|
545
|
-
}
|
|
546
|
-
case 'Q': {
|
|
547
|
-
unsigned long long result;
|
|
548
|
-
[invocation getReturnValue:&result];
|
|
549
|
-
return Napi::Number::New(env, result);
|
|
550
|
-
}
|
|
551
|
-
case 'f': {
|
|
552
|
-
float result;
|
|
553
|
-
[invocation getReturnValue:&result];
|
|
554
|
-
return Napi::Number::New(env, result);
|
|
555
|
-
}
|
|
556
|
-
case 'd': {
|
|
557
|
-
double result;
|
|
558
|
-
[invocation getReturnValue:&result];
|
|
559
|
-
return Napi::Number::New(env, result);
|
|
560
|
-
}
|
|
561
|
-
case 'B': {
|
|
485
|
+
|
|
486
|
+
// Bool -> Boolean
|
|
487
|
+
Napi::Value operator()(std::type_identity<bool>) const {
|
|
562
488
|
bool result;
|
|
563
489
|
[invocation getReturnValue:&result];
|
|
564
490
|
return Napi::Boolean::New(env, result);
|
|
565
491
|
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
char
|
|
492
|
+
|
|
493
|
+
// C string -> String or Null
|
|
494
|
+
Napi::Value operator()(std::type_identity<ObjCCStringTag>) const {
|
|
495
|
+
char* result = nullptr;
|
|
570
496
|
[invocation getReturnValue:&result];
|
|
571
497
|
if (result == nullptr) {
|
|
572
498
|
return env.Null();
|
|
573
499
|
}
|
|
574
500
|
return Napi::String::New(env, result);
|
|
575
501
|
}
|
|
576
|
-
|
|
577
|
-
|
|
502
|
+
|
|
503
|
+
// id -> ObjcObject or Null
|
|
504
|
+
Napi::Value operator()(std::type_identity<ObjCIdTag>) const {
|
|
505
|
+
id result = nil;
|
|
506
|
+
[invocation getReturnValue:&result];
|
|
507
|
+
if (result == nil) {
|
|
508
|
+
return env.Null();
|
|
509
|
+
}
|
|
510
|
+
return ObjcObject::NewInstance(env, result);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Class -> ObjcObject or Null (same as id)
|
|
514
|
+
Napi::Value operator()(std::type_identity<ObjCClassTag>) const {
|
|
578
515
|
id result = nil;
|
|
579
516
|
[invocation getReturnValue:&result];
|
|
580
517
|
if (result == nil) {
|
|
@@ -582,23 +519,40 @@ inline Napi::Value GetInvocationReturnAsJS(Napi::Env env,
|
|
|
582
519
|
}
|
|
583
520
|
return ObjcObject::NewInstance(env, result);
|
|
584
521
|
}
|
|
585
|
-
|
|
522
|
+
|
|
523
|
+
// SEL -> String or Null
|
|
524
|
+
Napi::Value operator()(std::type_identity<ObjCSELTag>) const {
|
|
586
525
|
SEL result = nullptr;
|
|
587
526
|
[invocation getReturnValue:&result];
|
|
588
527
|
if (result == nullptr) {
|
|
589
528
|
return env.Null();
|
|
590
529
|
}
|
|
591
|
-
NSString
|
|
530
|
+
NSString* selectorString = NSStringFromSelector(result);
|
|
592
531
|
if (selectorString == nil) {
|
|
593
532
|
return env.Null();
|
|
594
533
|
}
|
|
595
534
|
return Napi::String::New(env, [selectorString UTF8String]);
|
|
596
535
|
}
|
|
597
|
-
|
|
598
|
-
|
|
536
|
+
|
|
537
|
+
// Pointer -> Error (unsupported)
|
|
538
|
+
Napi::Value operator()(std::type_identity<ObjCPointerTag>) const {
|
|
539
|
+
Napi::TypeError::New(env, "Unsupported return type (pointer)")
|
|
599
540
|
.ThrowAsJavaScriptException();
|
|
600
541
|
return env.Null();
|
|
601
542
|
}
|
|
543
|
+
|
|
544
|
+
// Void -> Undefined
|
|
545
|
+
Napi::Value operator()(std::type_identity<ObjCVoidTag>) const {
|
|
546
|
+
return env.Undefined();
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
// Get return value from NSInvocation and convert to JS
|
|
551
|
+
inline Napi::Value GetInvocationReturnAsJS(Napi::Env env,
|
|
552
|
+
NSInvocation *invocation,
|
|
553
|
+
NSMethodSignature *methodSignature) {
|
|
554
|
+
SimplifiedTypeEncoding returnType([methodSignature methodReturnType]);
|
|
555
|
+
return DispatchByTypeCode(returnType[0], GetInvocationReturnVisitor{env, invocation});
|
|
602
556
|
}
|
|
603
557
|
|
|
604
558
|
#endif // TYPE_CONVERSION_H
|