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.
Files changed (60) hide show
  1. package/package.json +1 -1
  2. package/src/call.hh +2 -1
  3. package/src/call_arm32.cc +27 -15
  4. package/src/call_arm64.cc +4 -9
  5. package/src/call_x64_sysv.cc +4 -9
  6. package/src/call_x64_sysv_fwd.S +13 -3
  7. package/src/call_x64_win.cc +4 -9
  8. package/src/call_x86.cc +18 -16
  9. package/src/ffi.cc +93 -46
  10. package/src/ffi.hh +11 -6
  11. package/src/util.cc +35 -1
  12. package/src/util.hh +2 -0
  13. package/test/tests/misc.c +62 -1
  14. package/test/tests/misc.js +49 -1
  15. package/vendor/node-addon-api/CHANGELOG.md +57 -0
  16. package/vendor/node-addon-api/README.md +3 -3
  17. package/vendor/node-addon-api/doc/array_buffer.md +2 -2
  18. package/vendor/node-addon-api/doc/buffer.md +2 -2
  19. package/vendor/node-addon-api/doc/class_property_descriptor.md +13 -5
  20. package/vendor/node-addon-api/doc/env.md +5 -5
  21. package/vendor/node-addon-api/doc/object.md +12 -33
  22. package/vendor/node-addon-api/doc/object_reference.md +1 -1
  23. package/vendor/node-addon-api/doc/object_wrap.md +27 -0
  24. package/vendor/node-addon-api/doc/reference.md +2 -2
  25. package/vendor/node-addon-api/doc/threadsafe_function.md +3 -3
  26. package/vendor/node-addon-api/doc/typed_threadsafe_function.md +2 -2
  27. package/vendor/node-addon-api/napi-inl.h +111 -61
  28. package/vendor/node-addon-api/napi.h +79 -59
  29. package/vendor/node-addon-api/package.json +22 -6
  30. package/vendor/node-addon-api/test/async_context.cc +15 -0
  31. package/vendor/node-addon-api/test/async_context.js +69 -33
  32. package/vendor/node-addon-api/test/binding.cc +2 -0
  33. package/vendor/node-addon-api/test/binding.gyp +7 -0
  34. package/vendor/node-addon-api/test/common/index.js +23 -22
  35. package/vendor/node-addon-api/test/common/test_helper.h +10 -0
  36. package/vendor/node-addon-api/test/error_terminating_environment.js +1 -0
  37. package/vendor/node-addon-api/test/function.cc +29 -0
  38. package/vendor/node-addon-api/test/function.js +35 -23
  39. package/vendor/node-addon-api/test/index.js +40 -17
  40. package/vendor/node-addon-api/test/object/object.cc +2 -0
  41. package/vendor/node-addon-api/test/object/set_property.cc +8 -0
  42. package/vendor/node-addon-api/test/object/set_property.js +6 -5
  43. package/vendor/node-addon-api/test/object/subscript_operator.cc +19 -3
  44. package/vendor/node-addon-api/test/objectwrap_function.cc +45 -0
  45. package/vendor/node-addon-api/test/objectwrap_function.js +22 -0
  46. package/vendor/node-addon-api/test/threadsafe_function/threadsafe_function_sum.cc +1 -1
  47. package/vendor/node-addon-api/test/typed_threadsafe_function/typed_threadsafe_function_ctx.js +2 -2
  48. package/vendor/node-addon-api/test/typed_threadsafe_function/typed_threadsafe_function_sum.cc +1 -1
  49. package/vendor/node-addon-api/tools/clang-format.js +4 -1
  50. package/vendor/node-addon-api/unit-test/README.md +28 -0
  51. package/vendor/node-addon-api/unit-test/binding-file-template.js +39 -0
  52. package/vendor/node-addon-api/unit-test/binding.gyp +72 -0
  53. package/vendor/node-addon-api/unit-test/exceptions.js +32 -0
  54. package/vendor/node-addon-api/unit-test/generate-binding-cc.js +61 -0
  55. package/vendor/node-addon-api/unit-test/injectTestParams.js +101 -0
  56. package/vendor/node-addon-api/unit-test/listOfTestModules.js +88 -0
  57. package/vendor/node-addon-api/unit-test/matchModules.js +65 -0
  58. package/vendor/node-addon-api/unit-test/setup.js +13 -0
  59. package/vendor/node-addon-api/unit-test/spawnTask.js +26 -0
  60. package/vendor/node-addon-api/unit-test/test.js +30 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "0.9.30",
3
+ "version": "0.9.33",
4
4
  "description": "Fast and simple FFI (foreign function interface) for Node.js",
5
5
  "keywords": [
6
6
  "foreign",
package/src/call.hh CHANGED
@@ -22,6 +22,7 @@ namespace RG {
22
22
  struct InstanceData;
23
23
  struct FunctionInfo;
24
24
 
25
- Napi::Function::Callback AnalyseFunction(InstanceData *instance, FunctionInfo *func);
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
- Napi::Function::Callback AnalyseFunction(InstanceData *, FunctionInfo *func)
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
- func->ret.gpr_count = (func->ret.type->size >= 4) ? 2 : 1;
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 (need <= vec_avail) {
112
- param.vec_count = need;
113
- vec_avail -= need;
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
- started_stack = true;
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 TranslateCall;
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
- static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
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 &param = func->parameters[i];
254
259
  RG_ASSERT(param.directions >= 1 && param.directions <= 3);
255
260
 
256
- Napi::Value value = info[i];
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
- Napi::Function::Callback AnalyseFunction(InstanceData *, FunctionInfo *func)
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 TranslateCall;
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
- static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
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 &param = func->parameters[i];
240
235
  RG_ASSERT(param.directions >= 1 && param.directions <= 3);
241
236
 
242
- Napi::Value value = info[i];
237
+ Napi::Value value = info[param.offset];
243
238
 
244
239
  switch (param.type->primitive) {
245
240
  case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
@@ -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
- Napi::Function::Callback AnalyseFunction(InstanceData *, FunctionInfo *func)
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 TranslateCall;
180
+ return true;
183
181
  }
184
182
 
185
- static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
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 &param = func->parameters[i];
222
217
  RG_ASSERT(param.directions >= 1 && param.directions <= 3);
223
218
 
224
- Napi::Value value = info[i];
219
+ Napi::Value value = info[param.offset];
225
220
 
226
221
  switch (param.type->primitive) {
227
222
  case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
@@ -32,13 +32,13 @@
32
32
  .global SYMBOL(ForwardCallXGD)
33
33
  .global SYMBOL(ForwardCallXDD)
34
34
 
35
- // Copy function pointer to RAX, in order to save it through argument forwarding.
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, %rax
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 *%rax
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
@@ -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
- Napi::Function::Callback AnalyseFunction(InstanceData *, FunctionInfo *func)
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 TranslateCall;
51
+ return true;
54
52
  }
55
53
 
56
- static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
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 &param = func->parameters[i];
87
82
  RG_ASSERT(param.directions >= 1 && param.directions <= 3);
88
83
 
89
- Napi::Value value = info[i];
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 Napi::Value TranslateCall(const Napi::CallbackInfo &info);
32
+ static inline bool IsRegular(Size size)
33
+ {
34
+ bool regular = (size <= 8 && !(size & (size - 1)));
35
+ return regular;
36
+ }
33
37
 
34
- Napi::Function::Callback AnalyseFunction(InstanceData *instance, FunctionInfo *func)
38
+ bool AnalyseFunction(InstanceData *instance, FunctionInfo *func)
35
39
  {
36
40
  int fast = (func->convention == CallConvention::Fastcall) ? 2 : 0;
37
41
 
38
- if (IsIntegral(func->ret.type->primitive)) {
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: {} break;
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 TranslateCall;
82
+ return true;
78
83
  }
79
84
 
80
- static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
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 &param = func->parameters[i];
116
118
  RG_ASSERT(param.directions >= 1 && param.directions <= 3);
117
119
 
118
- Napi::Value value = info[i];
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
- static const int TypeInfoMarker = 0xDEADBEEF;
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 FindLibraryFunction(const Napi::CallbackInfo &info, CallConvention convention)
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], &param.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 out_counter = 0;
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
- for (uint32_t j = 0; j < parameters.Length(); j++) {
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], &param.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) && ++out_counter >= MaxOutParameters) {
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
- Napi::Function::Callback call = AnalyseFunction(instance, func);
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