koffi 0.9.30 → 0.9.33
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/package.json +1 -1
- package/src/call.hh +2 -1
- package/src/call_arm32.cc +27 -15
- package/src/call_arm64.cc +4 -9
- package/src/call_x64_sysv.cc +4 -9
- package/src/call_x64_sysv_fwd.S +13 -3
- package/src/call_x64_win.cc +4 -9
- package/src/call_x86.cc +18 -16
- package/src/ffi.cc +93 -46
- package/src/ffi.hh +11 -6
- package/src/util.cc +35 -1
- package/src/util.hh +2 -0
- package/test/tests/misc.c +62 -1
- package/test/tests/misc.js +49 -1
- package/vendor/node-addon-api/CHANGELOG.md +57 -0
- package/vendor/node-addon-api/README.md +3 -3
- package/vendor/node-addon-api/doc/array_buffer.md +2 -2
- package/vendor/node-addon-api/doc/buffer.md +2 -2
- package/vendor/node-addon-api/doc/class_property_descriptor.md +13 -5
- package/vendor/node-addon-api/doc/env.md +5 -5
- package/vendor/node-addon-api/doc/object.md +12 -33
- package/vendor/node-addon-api/doc/object_reference.md +1 -1
- package/vendor/node-addon-api/doc/object_wrap.md +27 -0
- package/vendor/node-addon-api/doc/reference.md +2 -2
- package/vendor/node-addon-api/doc/threadsafe_function.md +3 -3
- package/vendor/node-addon-api/doc/typed_threadsafe_function.md +2 -2
- package/vendor/node-addon-api/napi-inl.h +111 -61
- package/vendor/node-addon-api/napi.h +79 -59
- package/vendor/node-addon-api/package.json +22 -6
- package/vendor/node-addon-api/test/async_context.cc +15 -0
- package/vendor/node-addon-api/test/async_context.js +69 -33
- package/vendor/node-addon-api/test/binding.cc +2 -0
- package/vendor/node-addon-api/test/binding.gyp +7 -0
- package/vendor/node-addon-api/test/common/index.js +23 -22
- package/vendor/node-addon-api/test/common/test_helper.h +10 -0
- package/vendor/node-addon-api/test/error_terminating_environment.js +1 -0
- package/vendor/node-addon-api/test/function.cc +29 -0
- package/vendor/node-addon-api/test/function.js +35 -23
- package/vendor/node-addon-api/test/index.js +40 -17
- package/vendor/node-addon-api/test/object/object.cc +2 -0
- package/vendor/node-addon-api/test/object/set_property.cc +8 -0
- package/vendor/node-addon-api/test/object/set_property.js +6 -5
- package/vendor/node-addon-api/test/object/subscript_operator.cc +19 -3
- package/vendor/node-addon-api/test/objectwrap_function.cc +45 -0
- package/vendor/node-addon-api/test/objectwrap_function.js +22 -0
- package/vendor/node-addon-api/test/threadsafe_function/threadsafe_function_sum.cc +1 -1
- package/vendor/node-addon-api/test/typed_threadsafe_function/typed_threadsafe_function_ctx.js +2 -2
- package/vendor/node-addon-api/test/typed_threadsafe_function/typed_threadsafe_function_sum.cc +1 -1
- package/vendor/node-addon-api/tools/clang-format.js +4 -1
- package/vendor/node-addon-api/unit-test/README.md +28 -0
- package/vendor/node-addon-api/unit-test/binding-file-template.js +39 -0
- package/vendor/node-addon-api/unit-test/binding.gyp +72 -0
- package/vendor/node-addon-api/unit-test/exceptions.js +32 -0
- package/vendor/node-addon-api/unit-test/generate-binding-cc.js +61 -0
- package/vendor/node-addon-api/unit-test/injectTestParams.js +101 -0
- package/vendor/node-addon-api/unit-test/listOfTestModules.js +88 -0
- package/vendor/node-addon-api/unit-test/matchModules.js +65 -0
- package/vendor/node-addon-api/unit-test/setup.js +13 -0
- package/vendor/node-addon-api/unit-test/spawnTask.js +26 -0
- package/vendor/node-addon-api/unit-test/test.js +30 -0
package/package.json
CHANGED
package/src/call.hh
CHANGED
|
@@ -22,6 +22,7 @@ namespace RG {
|
|
|
22
22
|
struct InstanceData;
|
|
23
23
|
struct FunctionInfo;
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
bool AnalyseFunction(InstanceData *instance, FunctionInfo *func);
|
|
26
|
+
Napi::Value TranslateCall(InstanceData *instance, const FunctionInfo *func, const Napi::CallbackInfo &info);
|
|
26
27
|
|
|
27
28
|
}
|
package/src/call_arm32.cc
CHANGED
|
@@ -37,8 +37,6 @@ extern "C" uint64_t ForwardCallXGG(const void *func, uint8_t *sp);
|
|
|
37
37
|
extern "C" float ForwardCallXF(const void *func, uint8_t *sp);
|
|
38
38
|
extern "C" HfaRet ForwardCallXDDDD(const void *func, uint8_t *sp);
|
|
39
39
|
|
|
40
|
-
static Napi::Value TranslateCall(const Napi::CallbackInfo &info);
|
|
41
|
-
|
|
42
40
|
static bool IsHFA(const TypeInfo *type)
|
|
43
41
|
{
|
|
44
42
|
if (type->primitive != PrimitiveKind::Record)
|
|
@@ -58,13 +56,14 @@ static bool IsHFA(const TypeInfo *type)
|
|
|
58
56
|
return true;
|
|
59
57
|
}
|
|
60
58
|
|
|
61
|
-
|
|
59
|
+
bool AnalyseFunction(InstanceData *, FunctionInfo *func)
|
|
62
60
|
{
|
|
63
61
|
if (IsHFA(func->ret.type)) {
|
|
64
62
|
func->ret.vec_count = func->ret.type->members.len *
|
|
65
63
|
(func->ret.type->members[0].type->size / 4);
|
|
66
|
-
} else if (func->ret.type->primitive != PrimitiveKind::Record
|
|
67
|
-
|
|
64
|
+
} else if (func->ret.type->primitive != PrimitiveKind::Record ||
|
|
65
|
+
func->ret.type->size <= 4) {
|
|
66
|
+
func->ret.gpr_count = (func->ret.type->size > 4) ? 2 : 1;
|
|
68
67
|
} else {
|
|
69
68
|
func->ret.use_memory = true;
|
|
70
69
|
}
|
|
@@ -108,11 +107,20 @@ Napi::Function::Callback AnalyseFunction(InstanceData *, FunctionInfo *func)
|
|
|
108
107
|
case PrimitiveKind::Float64: {
|
|
109
108
|
Size need = param.type->size / 4;
|
|
110
109
|
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
if (param.variadic) {
|
|
111
|
+
if (need <= gpr_avail) {
|
|
112
|
+
param.gpr_count = need;
|
|
113
|
+
gpr_avail -= need;
|
|
114
|
+
} else {
|
|
115
|
+
started_stack = true;
|
|
116
|
+
}
|
|
114
117
|
} else {
|
|
115
|
-
|
|
118
|
+
if (need <= vec_avail) {
|
|
119
|
+
param.vec_count = need;
|
|
120
|
+
vec_avail -= need;
|
|
121
|
+
} else {
|
|
122
|
+
started_stack = true;
|
|
123
|
+
}
|
|
116
124
|
}
|
|
117
125
|
} break;
|
|
118
126
|
|
|
@@ -149,7 +157,7 @@ Napi::Function::Callback AnalyseFunction(InstanceData *, FunctionInfo *func)
|
|
|
149
157
|
|
|
150
158
|
func->forward_fp = (vec_avail < 16);
|
|
151
159
|
|
|
152
|
-
return
|
|
160
|
+
return true;
|
|
153
161
|
}
|
|
154
162
|
|
|
155
163
|
static bool PushHFA(const Napi::Object &obj, const TypeInfo *type, uint8_t *dest)
|
|
@@ -211,12 +219,9 @@ static Napi::Object PopHFA(napi_env env, const uint8_t *ptr, const TypeInfo *typ
|
|
|
211
219
|
return obj;
|
|
212
220
|
}
|
|
213
221
|
|
|
214
|
-
|
|
222
|
+
Napi::Value TranslateCall(InstanceData *instance, const FunctionInfo *func, const Napi::CallbackInfo &info)
|
|
215
223
|
{
|
|
216
224
|
Napi::Env env = info.Env();
|
|
217
|
-
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
218
|
-
FunctionInfo *func = (FunctionInfo *)info.Data();
|
|
219
|
-
|
|
220
225
|
CallData call(env, instance, func);
|
|
221
226
|
|
|
222
227
|
// Sanity checks
|
|
@@ -253,7 +258,7 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
253
258
|
const ParameterInfo ¶m = func->parameters[i];
|
|
254
259
|
RG_ASSERT(param.directions >= 1 && param.directions <= 3);
|
|
255
260
|
|
|
256
|
-
Napi::Value value = info[
|
|
261
|
+
Napi::Value value = info[param.offset];
|
|
257
262
|
|
|
258
263
|
switch (param.type->primitive) {
|
|
259
264
|
case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
|
|
@@ -321,6 +326,8 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
321
326
|
|
|
322
327
|
if (RG_LIKELY(param.vec_count)) {
|
|
323
328
|
memcpy(vec_ptr++, &f, 4);
|
|
329
|
+
} else if (param.gpr_count) {
|
|
330
|
+
memcpy(gpr_ptr++, &f, 4);
|
|
324
331
|
} else {
|
|
325
332
|
memcpy(args_ptr, &f, 4);
|
|
326
333
|
args_ptr += 4;
|
|
@@ -337,6 +344,9 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
337
344
|
if (RG_LIKELY(param.vec_count)) {
|
|
338
345
|
memcpy(vec_ptr, &d, 8);
|
|
339
346
|
vec_ptr += 2;
|
|
347
|
+
} else if (param.gpr_count) {
|
|
348
|
+
memcpy(gpr_ptr, &d, 8);
|
|
349
|
+
gpr_ptr += 2;
|
|
340
350
|
} else {
|
|
341
351
|
args_ptr = AlignUp(args_ptr, 8);
|
|
342
352
|
memcpy(args_ptr, &d, 8);
|
|
@@ -457,6 +467,8 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
457
467
|
|
|
458
468
|
case PrimitiveKind::Record: {
|
|
459
469
|
if (func->ret.gpr_count) {
|
|
470
|
+
RG_ASSERT(func->ret.gpr_count <= 1);
|
|
471
|
+
|
|
460
472
|
uint64_t ret = PERFORM_CALL(GG);
|
|
461
473
|
uint32_t r0 = (uint32_t)ret;
|
|
462
474
|
|
package/src/call_arm64.cc
CHANGED
|
@@ -41,8 +41,6 @@ extern "C" X0X1Ret ForwardCallXGG(const void *func, uint8_t *sp);
|
|
|
41
41
|
extern "C" float ForwardCallXF(const void *func, uint8_t *sp);
|
|
42
42
|
extern "C" HfaRet ForwardCallXDDDD(const void *func, uint8_t *sp);
|
|
43
43
|
|
|
44
|
-
static Napi::Value TranslateCall(const Napi::CallbackInfo &info);
|
|
45
|
-
|
|
46
44
|
static bool IsHFA(const TypeInfo *type)
|
|
47
45
|
{
|
|
48
46
|
if (type->primitive != PrimitiveKind::Record)
|
|
@@ -62,7 +60,7 @@ static bool IsHFA(const TypeInfo *type)
|
|
|
62
60
|
return true;
|
|
63
61
|
}
|
|
64
62
|
|
|
65
|
-
|
|
63
|
+
bool AnalyseFunction(InstanceData *, FunctionInfo *func)
|
|
66
64
|
{
|
|
67
65
|
if (IsHFA(func->ret.type)) {
|
|
68
66
|
func->ret.vec_count = func->ret.type->members.len;
|
|
@@ -138,7 +136,7 @@ Napi::Function::Callback AnalyseFunction(InstanceData *, FunctionInfo *func)
|
|
|
138
136
|
func->args_size = 16 * func->parameters.len;
|
|
139
137
|
func->forward_fp = (vec_avail < 8);
|
|
140
138
|
|
|
141
|
-
return
|
|
139
|
+
return true;
|
|
142
140
|
}
|
|
143
141
|
|
|
144
142
|
static bool PushHFA(const Napi::Object &obj, const TypeInfo *type, uint8_t *dest)
|
|
@@ -200,12 +198,9 @@ static Napi::Object PopHFA(napi_env env, const uint8_t *ptr, const TypeInfo *typ
|
|
|
200
198
|
return obj;
|
|
201
199
|
}
|
|
202
200
|
|
|
203
|
-
|
|
201
|
+
Napi::Value TranslateCall(InstanceData *instance, const FunctionInfo *func, const Napi::CallbackInfo &info)
|
|
204
202
|
{
|
|
205
203
|
Napi::Env env = info.Env();
|
|
206
|
-
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
207
|
-
FunctionInfo *func = (FunctionInfo *)info.Data();
|
|
208
|
-
|
|
209
204
|
CallData call(env, instance, func);
|
|
210
205
|
|
|
211
206
|
// Sanity checks
|
|
@@ -239,7 +234,7 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
239
234
|
const ParameterInfo ¶m = func->parameters[i];
|
|
240
235
|
RG_ASSERT(param.directions >= 1 && param.directions <= 3);
|
|
241
236
|
|
|
242
|
-
Napi::Value value = info[
|
|
237
|
+
Napi::Value value = info[param.offset];
|
|
243
238
|
|
|
244
239
|
switch (param.type->primitive) {
|
|
245
240
|
case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
|
package/src/call_x64_sysv.cc
CHANGED
|
@@ -58,8 +58,6 @@ extern "C" Xmm0RaxRet ForwardCallXDG(const void *func, uint8_t *sp);
|
|
|
58
58
|
extern "C" RaxXmm0Ret ForwardCallXGD(const void *func, uint8_t *sp);
|
|
59
59
|
extern "C" Xmm0Xmm1Ret ForwardCallXDD(const void *func, uint8_t *sp);
|
|
60
60
|
|
|
61
|
-
static Napi::Value TranslateCall(const Napi::CallbackInfo &info);
|
|
62
|
-
|
|
63
61
|
static inline RegisterClass MergeClasses(RegisterClass cls1, RegisterClass cls2)
|
|
64
62
|
{
|
|
65
63
|
if (cls1 == cls2)
|
|
@@ -161,7 +159,7 @@ static void AnalyseParameter(ParameterInfo *param, int gpr_avail, int xmm_avail)
|
|
|
161
159
|
}
|
|
162
160
|
}
|
|
163
161
|
|
|
164
|
-
|
|
162
|
+
bool AnalyseFunction(InstanceData *, FunctionInfo *func)
|
|
165
163
|
{
|
|
166
164
|
AnalyseParameter(&func->ret, 2, 2);
|
|
167
165
|
|
|
@@ -179,15 +177,12 @@ Napi::Function::Callback AnalyseFunction(InstanceData *, FunctionInfo *func)
|
|
|
179
177
|
|
|
180
178
|
func->forward_fp = (xmm_avail < 8);
|
|
181
179
|
|
|
182
|
-
return
|
|
180
|
+
return true;
|
|
183
181
|
}
|
|
184
182
|
|
|
185
|
-
|
|
183
|
+
Napi::Value TranslateCall(InstanceData *instance, const FunctionInfo *func, const Napi::CallbackInfo &info)
|
|
186
184
|
{
|
|
187
185
|
Napi::Env env = info.Env();
|
|
188
|
-
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
189
|
-
FunctionInfo *func = (FunctionInfo *)info.Data();
|
|
190
|
-
|
|
191
186
|
CallData call(env, instance, func);
|
|
192
187
|
|
|
193
188
|
// Sanity checks
|
|
@@ -221,7 +216,7 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
221
216
|
const ParameterInfo ¶m = func->parameters[i];
|
|
222
217
|
RG_ASSERT(param.directions >= 1 && param.directions <= 3);
|
|
223
218
|
|
|
224
|
-
Napi::Value value = info[
|
|
219
|
+
Napi::Value value = info[param.offset];
|
|
225
220
|
|
|
226
221
|
switch (param.type->primitive) {
|
|
227
222
|
case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
|
package/src/call_x64_sysv_fwd.S
CHANGED
|
@@ -32,13 +32,13 @@
|
|
|
32
32
|
.global SYMBOL(ForwardCallXGD)
|
|
33
33
|
.global SYMBOL(ForwardCallXDD)
|
|
34
34
|
|
|
35
|
-
// Copy function pointer to
|
|
35
|
+
// Copy function pointer to R11, in order to save it through argument forwarding.
|
|
36
36
|
// Save RSP in RBX (non-volatile), and use carefully assembled stack provided by caller.
|
|
37
37
|
.macro prologue
|
|
38
38
|
.cfi_startproc
|
|
39
39
|
.cfi_def_cfa rsp, 8
|
|
40
40
|
endbr64
|
|
41
|
-
movq %rdi, %
|
|
41
|
+
movq %rdi, %r11
|
|
42
42
|
pushq %rbx
|
|
43
43
|
.cfi_def_cfa rsp, 16
|
|
44
44
|
movq %rsp, %rbx
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
// Once done, restore normal stack pointer and return.
|
|
51
51
|
// The return value is passed untouched through RAX or XMM0.
|
|
52
52
|
.macro epilogue
|
|
53
|
-
call *%
|
|
53
|
+
call *%r11
|
|
54
54
|
movq %rbx, %rsp
|
|
55
55
|
popq %rbx
|
|
56
56
|
.cfi_def_cfa rsp, 8
|
|
@@ -83,54 +83,64 @@
|
|
|
83
83
|
SYMBOL(ForwardCallGG):
|
|
84
84
|
prologue
|
|
85
85
|
forward_int
|
|
86
|
+
movb $0, %al
|
|
86
87
|
epilogue
|
|
87
88
|
|
|
88
89
|
SYMBOL(ForwardCallF):
|
|
89
90
|
prologue
|
|
90
91
|
forward_int
|
|
92
|
+
movb $0, %al
|
|
91
93
|
epilogue
|
|
92
94
|
|
|
93
95
|
SYMBOL(ForwardCallDG):
|
|
94
96
|
prologue
|
|
95
97
|
forward_int
|
|
98
|
+
movb $0, %al
|
|
96
99
|
epilogue
|
|
97
100
|
|
|
98
101
|
SYMBOL(ForwardCallGD):
|
|
99
102
|
prologue
|
|
100
103
|
forward_int
|
|
104
|
+
movb $0, %al
|
|
101
105
|
epilogue
|
|
102
106
|
|
|
103
107
|
SYMBOL(ForwardCallDD):
|
|
104
108
|
prologue
|
|
105
109
|
forward_int
|
|
110
|
+
movb $0, %al
|
|
106
111
|
epilogue
|
|
107
112
|
|
|
108
113
|
SYMBOL(ForwardCallXGG):
|
|
109
114
|
prologue
|
|
110
115
|
forward_xmm
|
|
111
116
|
forward_int
|
|
117
|
+
movb $8, %al
|
|
112
118
|
epilogue
|
|
113
119
|
|
|
114
120
|
SYMBOL(ForwardCallXF):
|
|
115
121
|
prologue
|
|
116
122
|
forward_xmm
|
|
117
123
|
forward_int
|
|
124
|
+
movb $8, %al
|
|
118
125
|
epilogue
|
|
119
126
|
|
|
120
127
|
SYMBOL(ForwardCallXDG):
|
|
121
128
|
prologue
|
|
122
129
|
forward_xmm
|
|
123
130
|
forward_int
|
|
131
|
+
movb $8, %al
|
|
124
132
|
epilogue
|
|
125
133
|
|
|
126
134
|
SYMBOL(ForwardCallXGD):
|
|
127
135
|
prologue
|
|
128
136
|
forward_xmm
|
|
129
137
|
forward_int
|
|
138
|
+
movb $8, %al
|
|
130
139
|
epilogue
|
|
131
140
|
|
|
132
141
|
SYMBOL(ForwardCallXDD):
|
|
133
142
|
prologue
|
|
134
143
|
forward_xmm
|
|
135
144
|
forward_int
|
|
145
|
+
movb $8, %al
|
|
136
146
|
epilogue
|
package/src/call_x64_win.cc
CHANGED
|
@@ -29,15 +29,13 @@ extern "C" uint64_t ForwardCallXG(const void *func, uint8_t *sp);
|
|
|
29
29
|
extern "C" float ForwardCallXF(const void *func, uint8_t *sp);
|
|
30
30
|
extern "C" double ForwardCallXD(const void *func, uint8_t *sp);
|
|
31
31
|
|
|
32
|
-
static Napi::Value TranslateCall(const Napi::CallbackInfo &info);
|
|
33
|
-
|
|
34
32
|
static inline bool IsRegular(Size size)
|
|
35
33
|
{
|
|
36
34
|
bool regular = (size <= 8 && !(size & (size - 1)));
|
|
37
35
|
return regular;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
|
|
38
|
+
bool AnalyseFunction(InstanceData *, FunctionInfo *func)
|
|
41
39
|
{
|
|
42
40
|
func->ret.regular = IsRegular(func->ret.type->size);
|
|
43
41
|
|
|
@@ -50,15 +48,12 @@ Napi::Function::Callback AnalyseFunction(InstanceData *, FunctionInfo *func)
|
|
|
50
48
|
|
|
51
49
|
func->args_size = AlignLen(8 * std::max((Size)4, func->parameters.len + !func->ret.regular), 16);
|
|
52
50
|
|
|
53
|
-
return
|
|
51
|
+
return true;
|
|
54
52
|
}
|
|
55
53
|
|
|
56
|
-
|
|
54
|
+
Napi::Value TranslateCall(InstanceData *instance, const FunctionInfo *func, const Napi::CallbackInfo &info)
|
|
57
55
|
{
|
|
58
56
|
Napi::Env env = info.Env();
|
|
59
|
-
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
60
|
-
FunctionInfo *func = (FunctionInfo *)info.Data();
|
|
61
|
-
|
|
62
57
|
CallData call(env, instance, func);
|
|
63
58
|
|
|
64
59
|
// Sanity checks
|
|
@@ -86,7 +81,7 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
86
81
|
const ParameterInfo ¶m = func->parameters[i];
|
|
87
82
|
RG_ASSERT(param.directions >= 1 && param.directions <= 3);
|
|
88
83
|
|
|
89
|
-
Napi::Value value = info[
|
|
84
|
+
Napi::Value value = info[param.offset];
|
|
90
85
|
|
|
91
86
|
switch (param.type->primitive) {
|
|
92
87
|
case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
|
package/src/call_x86.cc
CHANGED
|
@@ -29,20 +29,21 @@ extern "C" uint64_t ForwardCallRG(const void *func, uint8_t *sp);
|
|
|
29
29
|
extern "C" float ForwardCallRF(const void *func, uint8_t *sp);
|
|
30
30
|
extern "C" double ForwardCallRD(const void *func, uint8_t *sp);
|
|
31
31
|
|
|
32
|
-
static
|
|
32
|
+
static inline bool IsRegular(Size size)
|
|
33
|
+
{
|
|
34
|
+
bool regular = (size <= 8 && !(size & (size - 1)));
|
|
35
|
+
return regular;
|
|
36
|
+
}
|
|
33
37
|
|
|
34
|
-
|
|
38
|
+
bool AnalyseFunction(InstanceData *instance, FunctionInfo *func)
|
|
35
39
|
{
|
|
36
40
|
int fast = (func->convention == CallConvention::Fastcall) ? 2 : 0;
|
|
37
41
|
|
|
38
|
-
if (
|
|
39
|
-
func->ret.trivial = true;
|
|
40
|
-
} else if (func->ret.type->members.len == 1 && IsIntegral(func->ret.type->members[0].type->primitive)) {
|
|
41
|
-
func->ret.trivial = true;
|
|
42
|
-
#ifdef _WIN32
|
|
43
|
-
} else if (func->ret.type->members.len == 2 && IsIntegral(func->ret.type->members[0].type->primitive)
|
|
44
|
-
&& IsIntegral(func->ret.type->members[1].type->primitive)) {
|
|
42
|
+
if (func->ret.type->primitive != PrimitiveKind::Record) {
|
|
45
43
|
func->ret.trivial = true;
|
|
44
|
+
#if defined(_WIN32) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
|
45
|
+
} else {
|
|
46
|
+
func->ret.trivial = IsRegular(func->ret.type->size);
|
|
46
47
|
#endif
|
|
47
48
|
}
|
|
48
49
|
#ifndef _WIN32
|
|
@@ -64,25 +65,26 @@ Napi::Function::Callback AnalyseFunction(InstanceData *instance, FunctionInfo *f
|
|
|
64
65
|
func->args_size = params_size + 4 * !func->ret.trivial;
|
|
65
66
|
|
|
66
67
|
switch (func->convention) {
|
|
67
|
-
case CallConvention::Default: {
|
|
68
|
+
case CallConvention::Default: {
|
|
69
|
+
func->decorated_name = Fmt(&instance->str_alloc, "_%1", func->name).ptr;
|
|
70
|
+
} break;
|
|
68
71
|
case CallConvention::Stdcall: {
|
|
72
|
+
RG_ASSERT(!func->variadic);
|
|
69
73
|
func->decorated_name = Fmt(&instance->str_alloc, "_%1@%2", func->name, params_size).ptr;
|
|
70
74
|
} break;
|
|
71
75
|
case CallConvention::Fastcall: {
|
|
76
|
+
RG_ASSERT(!func->variadic);
|
|
72
77
|
func->decorated_name = Fmt(&instance->str_alloc, "@%1@%2", func->name, params_size).ptr;
|
|
73
78
|
func->args_size += 16;
|
|
74
79
|
} break;
|
|
75
80
|
}
|
|
76
81
|
|
|
77
|
-
return
|
|
82
|
+
return true;
|
|
78
83
|
}
|
|
79
84
|
|
|
80
|
-
|
|
85
|
+
Napi::Value TranslateCall(InstanceData *instance, const FunctionInfo *func, const Napi::CallbackInfo &info)
|
|
81
86
|
{
|
|
82
87
|
Napi::Env env = info.Env();
|
|
83
|
-
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
84
|
-
FunctionInfo *func = (FunctionInfo *)info.Data();
|
|
85
|
-
|
|
86
88
|
CallData call(env, instance, func);
|
|
87
89
|
|
|
88
90
|
// Sanity checks
|
|
@@ -115,7 +117,7 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
115
117
|
const ParameterInfo ¶m = func->parameters[i];
|
|
116
118
|
RG_ASSERT(param.directions >= 1 && param.directions <= 3);
|
|
117
119
|
|
|
118
|
-
Napi::Value value = info[
|
|
120
|
+
Napi::Value value = info[param.offset];
|
|
119
121
|
|
|
120
122
|
switch (param.type->primitive) {
|
|
121
123
|
case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
|
package/src/ffi.cc
CHANGED
|
@@ -39,41 +39,7 @@
|
|
|
39
39
|
namespace RG {
|
|
40
40
|
|
|
41
41
|
// Value does not matter, the tag system uses memory addresses
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
static const TypeInfo *ResolveType(const InstanceData *instance, Napi::Value value, int *out_directions = nullptr)
|
|
45
|
-
{
|
|
46
|
-
if (value.IsString()) {
|
|
47
|
-
std::string str = value.As<Napi::String>();
|
|
48
|
-
|
|
49
|
-
const TypeInfo *type = instance->types_map.FindValue(str.c_str(), nullptr);
|
|
50
|
-
|
|
51
|
-
if (!type) {
|
|
52
|
-
ThrowError<Napi::TypeError>(value.Env(), "Unknown type string '%1'", str.c_str());
|
|
53
|
-
return nullptr;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (out_directions) {
|
|
57
|
-
*out_directions = 1;
|
|
58
|
-
}
|
|
59
|
-
return type;
|
|
60
|
-
} else if (CheckValueTag(instance, value, &TypeInfoMarker)) {
|
|
61
|
-
Napi::External<TypeInfo> external = value.As<Napi::External<TypeInfo>>();
|
|
62
|
-
|
|
63
|
-
const TypeInfo *raw = external.Data();
|
|
64
|
-
const TypeInfo *type = AlignDown(raw, 4);
|
|
65
|
-
RG_ASSERT(type);
|
|
66
|
-
|
|
67
|
-
if (out_directions) {
|
|
68
|
-
Size delta = (uint8_t *)raw - (uint8_t *)type;
|
|
69
|
-
*out_directions = 1 + (int)delta;
|
|
70
|
-
}
|
|
71
|
-
return type;
|
|
72
|
-
} else {
|
|
73
|
-
ThrowError<Napi::TypeError>(value.Env(), "Unexpected %1 value as type specifier, expected string or type", GetValueType(instance, value));
|
|
74
|
-
return nullptr;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
42
|
+
const int TypeInfoMarker = 0xDEADBEEF;
|
|
77
43
|
|
|
78
44
|
static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
|
|
79
45
|
{
|
|
@@ -289,7 +255,68 @@ static Napi::Value MarkInOut(const Napi::CallbackInfo &info)
|
|
|
289
255
|
return EncodePointerDirection(info, 3);
|
|
290
256
|
}
|
|
291
257
|
|
|
292
|
-
static Napi::Value
|
|
258
|
+
static Napi::Value TranslateNormalCall(const Napi::CallbackInfo &info)
|
|
259
|
+
{
|
|
260
|
+
Napi::Env env = info.Env();
|
|
261
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
262
|
+
FunctionInfo *func = (FunctionInfo *)info.Data();
|
|
263
|
+
|
|
264
|
+
return TranslateCall(instance, func, info);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
|
|
268
|
+
{
|
|
269
|
+
Napi::Env env = info.Env();
|
|
270
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
271
|
+
|
|
272
|
+
FunctionInfo func;
|
|
273
|
+
memcpy(&func, info.Data(), RG_SIZE(FunctionInfo));
|
|
274
|
+
func.lib = nullptr;
|
|
275
|
+
|
|
276
|
+
// This makes variadic calls non-reentrant
|
|
277
|
+
RG_DEFER_C(len = func.parameters.len) {
|
|
278
|
+
func.parameters.RemoveFrom(len);
|
|
279
|
+
func.parameters.Leak();
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
if ((info.Length() - func.parameters.len) % 2) {
|
|
283
|
+
ThrowError<Napi::Error>(env, "Missing value argument for variadic call");
|
|
284
|
+
return env.Null();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
for (Size i = func.parameters.len; i < info.Length(); i += 2) {
|
|
288
|
+
ParameterInfo param = {};
|
|
289
|
+
|
|
290
|
+
param.type = ResolveType(instance, info[i], ¶m.directions);
|
|
291
|
+
if (!param.type)
|
|
292
|
+
return env.Null();
|
|
293
|
+
if (param.type->primitive == PrimitiveKind::Void) {
|
|
294
|
+
ThrowError<Napi::TypeError>(env, "Type void cannot be used as a parameter");
|
|
295
|
+
return env.Null();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (func.parameters.len >= MaxParameters) {
|
|
299
|
+
ThrowError<Napi::TypeError>(env, "Functions cannot have more than %1 parameters", MaxParameters);
|
|
300
|
+
return env.Null();
|
|
301
|
+
}
|
|
302
|
+
if ((param.directions & 2) && ++func.out_parameters >= MaxOutParameters) {
|
|
303
|
+
ThrowError<Napi::TypeError>(env, "Functions cannot have more than out %1 parameters", MaxOutParameters);
|
|
304
|
+
return env.Null();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
param.variadic = true;
|
|
308
|
+
param.offset = i + 1;
|
|
309
|
+
|
|
310
|
+
func.parameters.Append(param);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (!AnalyseFunction(instance, &func))
|
|
314
|
+
return env.Null();
|
|
315
|
+
|
|
316
|
+
return TranslateCall(instance, &func, info);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
static Napi::Value FindLibraryFunction(const Napi::CallbackInfo &info, CallConvention convention, bool variadic)
|
|
293
320
|
{
|
|
294
321
|
Napi::Env env = info.Env();
|
|
295
322
|
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
@@ -321,9 +348,23 @@ static Napi::Value FindLibraryFunction(const Napi::CallbackInfo &info, CallConve
|
|
|
321
348
|
}
|
|
322
349
|
|
|
323
350
|
Napi::Array parameters = ((Napi::Value)info[2u]).As<Napi::Array>();
|
|
324
|
-
Size
|
|
351
|
+
Size parameters_len = parameters.Length();
|
|
352
|
+
|
|
353
|
+
if (parameters_len) {
|
|
354
|
+
Napi::String str = ((Napi::Value)parameters[(uint32_t)(parameters_len - 1)]).As<Napi::String>();
|
|
325
355
|
|
|
326
|
-
|
|
356
|
+
if (str.IsString() && str.Utf8Value() == "...") {
|
|
357
|
+
func->variadic = true;
|
|
358
|
+
parameters_len--;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (!variadic && func->variadic) {
|
|
362
|
+
LogError("Call convention '%1' does not support variadic functions, ignoring");
|
|
363
|
+
func->convention = CallConvention::Default;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
for (uint32_t j = 0; j < parameters_len; j++) {
|
|
327
368
|
ParameterInfo param = {};
|
|
328
369
|
|
|
329
370
|
param.type = ResolveType(instance, parameters[j], ¶m.directions);
|
|
@@ -338,17 +379,22 @@ static Napi::Value FindLibraryFunction(const Napi::CallbackInfo &info, CallConve
|
|
|
338
379
|
ThrowError<Napi::TypeError>(env, "Functions cannot have more than %1 parameters", MaxParameters);
|
|
339
380
|
return env.Null();
|
|
340
381
|
}
|
|
341
|
-
if ((param.directions & 2) && ++
|
|
382
|
+
if ((param.directions & 2) && ++func->out_parameters >= MaxOutParameters) {
|
|
342
383
|
ThrowError<Napi::TypeError>(env, "Functions cannot have more than out %1 parameters", MaxOutParameters);
|
|
343
384
|
return env.Null();
|
|
344
385
|
}
|
|
345
386
|
|
|
387
|
+
param.offset = j;
|
|
388
|
+
|
|
346
389
|
func->parameters.Append(param);
|
|
347
390
|
}
|
|
348
391
|
|
|
349
|
-
|
|
350
|
-
if (!call)
|
|
392
|
+
if (!AnalyseFunction(instance, func))
|
|
351
393
|
return env.Null();
|
|
394
|
+
if (func->variadic) {
|
|
395
|
+
// Minimize reallocations
|
|
396
|
+
func->parameters.Grow(32);
|
|
397
|
+
}
|
|
352
398
|
|
|
353
399
|
#ifdef _WIN32
|
|
354
400
|
if (func->decorated_name) {
|
|
@@ -370,6 +416,7 @@ static Napi::Value FindLibraryFunction(const Napi::CallbackInfo &info, CallConve
|
|
|
370
416
|
return env.Null();
|
|
371
417
|
}
|
|
372
418
|
|
|
419
|
+
Napi::Function::Callback call = func->variadic ? TranslateVariadicCall : TranslateNormalCall;
|
|
373
420
|
Napi::Function wrapper = Napi::Function::New(env, call, name.c_str(), (void *)func);
|
|
374
421
|
wrapper.AddFinalizer([](Napi::Env, FunctionInfo *func) { delete func; }, func);
|
|
375
422
|
func_guard.Disable();
|
|
@@ -434,17 +481,17 @@ static Napi::Value LoadSharedLibrary(const Napi::CallbackInfo &info)
|
|
|
434
481
|
|
|
435
482
|
Napi::Object obj = Napi::Object::New(env);
|
|
436
483
|
|
|
437
|
-
#define ADD_CONVENTION(Name, Value) \
|
|
484
|
+
#define ADD_CONVENTION(Name, Value, Variadic) \
|
|
438
485
|
do { \
|
|
439
|
-
const auto wrapper = [](const Napi::CallbackInfo &info) { return FindLibraryFunction(info, (Value)); }; \
|
|
486
|
+
const auto wrapper = [](const Napi::CallbackInfo &info) { return FindLibraryFunction(info, (Value), (Variadic)); }; \
|
|
440
487
|
Napi::Function func = Napi::Function::New(env, wrapper, (Name), (void *)lib->Ref()); \
|
|
441
488
|
func.AddFinalizer([](Napi::Env, LibraryHolder *lib) { lib->Unref(); }, lib); \
|
|
442
489
|
obj.Set((Name), func); \
|
|
443
490
|
} while (false)
|
|
444
491
|
|
|
445
|
-
ADD_CONVENTION("cdecl", CallConvention::Default);
|
|
446
|
-
ADD_CONVENTION("stdcall", CallConvention::Stdcall);
|
|
447
|
-
ADD_CONVENTION("fastcall", CallConvention::Fastcall);
|
|
492
|
+
ADD_CONVENTION("cdecl", CallConvention::Default, true);
|
|
493
|
+
ADD_CONVENTION("stdcall", CallConvention::Stdcall, false);
|
|
494
|
+
ADD_CONVENTION("fastcall", CallConvention::Fastcall, false);
|
|
448
495
|
|
|
449
496
|
#undef ADD_CONVENTION
|
|
450
497
|
|
package/src/ffi.hh
CHANGED
|
@@ -22,6 +22,8 @@ namespace RG {
|
|
|
22
22
|
static const Size MaxParameters = 32;
|
|
23
23
|
static const Size MaxOutParameters = 8;
|
|
24
24
|
|
|
25
|
+
extern const int TypeInfoMarker;
|
|
26
|
+
|
|
25
27
|
enum class PrimitiveKind {
|
|
26
28
|
Void,
|
|
27
29
|
|
|
@@ -63,12 +65,6 @@ static const char *const PrimitiveKindNames[] = {
|
|
|
63
65
|
"Record"
|
|
64
66
|
};
|
|
65
67
|
|
|
66
|
-
static inline bool IsIntegral(PrimitiveKind primitive)
|
|
67
|
-
{
|
|
68
|
-
bool integral = ((int)primitive <= (int)PrimitiveKind::Pointer);
|
|
69
|
-
return integral;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
68
|
struct TypeInfo;
|
|
73
69
|
struct RecordMember;
|
|
74
70
|
|
|
@@ -108,10 +104,17 @@ enum class CallConvention {
|
|
|
108
104
|
Stdcall,
|
|
109
105
|
Fastcall
|
|
110
106
|
};
|
|
107
|
+
static const char *const CallConventionNames[] = {
|
|
108
|
+
"Default",
|
|
109
|
+
"Stdcall",
|
|
110
|
+
"Fastcall"
|
|
111
|
+
};
|
|
111
112
|
|
|
112
113
|
struct ParameterInfo {
|
|
113
114
|
const TypeInfo *type;
|
|
114
115
|
int directions;
|
|
116
|
+
bool variadic;
|
|
117
|
+
Size offset;
|
|
115
118
|
|
|
116
119
|
// ABI-specific part
|
|
117
120
|
|
|
@@ -144,6 +147,8 @@ struct FunctionInfo {
|
|
|
144
147
|
|
|
145
148
|
ParameterInfo ret;
|
|
146
149
|
HeapArray<ParameterInfo> parameters;
|
|
150
|
+
Size out_parameters;
|
|
151
|
+
bool variadic;
|
|
147
152
|
|
|
148
153
|
// ABI-specific part
|
|
149
154
|
|