koffi 2.15.0 → 2.16.0-beta.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/build/koffi/darwin_arm64/koffi.node +0 -0
  3. package/build/koffi/darwin_x64/koffi.node +0 -0
  4. package/build/koffi/freebsd_arm64/koffi.node +0 -0
  5. package/build/koffi/freebsd_ia32/koffi.node +0 -0
  6. package/build/koffi/freebsd_x64/koffi.node +0 -0
  7. package/build/koffi/linux_arm64/koffi.node +0 -0
  8. package/build/koffi/linux_armhf/koffi.node +0 -0
  9. package/build/koffi/linux_ia32/koffi.node +0 -0
  10. package/build/koffi/linux_loong64/koffi.node +0 -0
  11. package/build/koffi/linux_riscv64d/koffi.node +0 -0
  12. package/build/koffi/linux_x64/koffi.node +0 -0
  13. package/build/koffi/musl_arm64/koffi.node +0 -0
  14. package/build/koffi/musl_x64/koffi.node +0 -0
  15. package/build/koffi/openbsd_ia32/koffi.node +0 -0
  16. package/build/koffi/openbsd_x64/koffi.node +0 -0
  17. package/build/koffi/win32_arm64/koffi.node +0 -0
  18. package/build/koffi/win32_ia32/koffi.node +0 -0
  19. package/build/koffi/win32_x64/koffi.node +0 -0
  20. package/doc/pages/index.md +4 -2
  21. package/doc/pages/misc.md +2 -0
  22. package/doc/templates/code.html +1 -2
  23. package/doc/templates/page.html +1 -2
  24. package/index.d.ts +11 -9
  25. package/index.js +9 -9
  26. package/indirect.js +9 -9
  27. package/lib/native/base/base.cc +79 -44
  28. package/lib/native/base/base.hh +31 -33
  29. package/package.json +2 -2
  30. package/src/cnoke/assets/FindCNoke.cmake +16 -10
  31. package/src/cnoke/assets/win_delay_hook.c +4 -0
  32. package/src/cnoke/src/builder.js +49 -46
  33. package/src/koffi/CMakeLists.txt +18 -8
  34. package/src/koffi/src/abi_arm32.cc +222 -219
  35. package/src/koffi/src/abi_arm32_asm.S +1 -29
  36. package/src/koffi/src/abi_arm64.cc +257 -235
  37. package/src/koffi/src/abi_arm64_asm.S +1 -32
  38. package/src/koffi/src/abi_arm64_asm.asm +1 -23
  39. package/src/koffi/src/abi_loong64_asm.S +1 -25
  40. package/src/koffi/src/abi_riscv64.cc +220 -217
  41. package/src/koffi/src/abi_riscv64_asm.S +1 -25
  42. package/src/koffi/src/abi_x64_sysv.cc +196 -192
  43. package/src/koffi/src/abi_x64_sysv_asm.S +1 -31
  44. package/src/koffi/src/abi_x64_win.cc +188 -172
  45. package/src/koffi/src/abi_x64_win_asm.S +144 -0
  46. package/src/koffi/src/abi_x64_win_asm.asm +1 -21
  47. package/src/koffi/src/abi_x86.cc +224 -189
  48. package/src/koffi/src/abi_x86_asm.S +6 -25
  49. package/src/koffi/src/abi_x86_asm.asm +9 -22
  50. package/src/koffi/src/call.cc +246 -428
  51. package/src/koffi/src/call.hh +9 -8
  52. package/src/koffi/src/ffi.cc +142 -88
  53. package/src/koffi/src/ffi.hh +13 -59
  54. package/src/koffi/src/primitives.inc +39 -0
  55. package/src/koffi/src/trampolines/armasm.inc +0 -32770
  56. package/src/koffi/src/trampolines/gnu.inc +0 -24578
  57. package/src/koffi/src/trampolines/masm32.inc +0 -32770
  58. package/src/koffi/src/trampolines/masm64.inc +0 -32770
  59. package/src/koffi/src/trampolines/prototypes.inc +16385 -16385
  60. package/src/koffi/src/util.cc +155 -112
  61. package/src/koffi/src/util.hh +77 -40
  62. package/vendor/node-api-headers/CHANGELOG.md +22 -0
  63. package/vendor/node-api-headers/README.md +6 -17
  64. package/vendor/node-api-headers/include/js_native_api.h +3 -13
  65. package/vendor/node-api-headers/include/js_native_api_types.h +15 -0
  66. package/vendor/node-api-headers/include/node_api.h +0 -4
  67. package/vendor/node-api-headers/include/node_api_types.h +6 -0
  68. package/vendor/node-api-headers/package.json +1 -1
  69. package/vendor/node-api-headers/scripts/update-headers.js +6 -0
@@ -29,8 +29,6 @@ extern "C" napi_value CallSwitchStack(Napi::Function *func, size_t argc, napi_va
29
29
  uint8_t *old_sp, Span<uint8_t> *new_stack,
30
30
  napi_value (*call)(Napi::Function *func, size_t argc, napi_value *argv));
31
31
 
32
- #include "trampolines/prototypes.inc"
33
-
34
32
  bool AnalyseFunction(Napi::Env, InstanceData *, FunctionInfo *func)
35
33
  {
36
34
  func->ret.regular = IsRegularSize(func->ret.type->size, 8);
@@ -47,154 +45,212 @@ bool AnalyseFunction(Napi::Env, InstanceData *, FunctionInfo *func)
47
45
 
48
46
  bool CallData::Prepare(const FunctionInfo *func, const Napi::CallbackInfo &info)
49
47
  {
50
- uint64_t *args_ptr = nullptr;
48
+ uint64_t *args_ptr = AllocStack<uint64_t>(func->args_size);
51
49
 
52
- // Pass return value in register or through memory
53
- if (!AllocStack(func->args_size, 16, &args_ptr)) [[unlikely]]
50
+ if (!args_ptr) [[unlikely]]
54
51
  return false;
55
52
  if (!func->ret.regular) {
56
53
  return_ptr = AllocHeap(func->ret.type->size, 16);
57
54
  *(uint8_t **)(args_ptr++) = return_ptr;
58
55
  }
59
56
 
57
+ Size i = -1;
58
+
59
+ #if defined(__GNUC__) || defined(__clang__)
60
+ static const void *const DispatchTable[] = {
61
+ #define PRIMITIVE(Name) && Name,
62
+ #include "primitives.inc"
63
+ };
64
+
65
+ #define LOOP
66
+ #define CASE(Primitive) \
67
+ do { \
68
+ PrimitiveKind next = func->primitives[++i]; \
69
+ goto *DispatchTable[(int)next]; \
70
+ } while (false); \
71
+ Primitive:
72
+ #define OR(Primitive) \
73
+ Primitive:
74
+ #else
75
+ #define LOOP \
76
+ while (++i < func->parameters.len) \
77
+ switch (func->primitives[i])
78
+ #define CASE(Primitive) \
79
+ break; \
80
+ case PrimitiveKind::Primitive:
81
+ #define OR(Primitive) \
82
+ case PrimitiveKind::Primitive:
83
+ #endif
84
+
60
85
  #define PUSH_INTEGER(CType) \
61
86
  do { \
62
- if (!value.IsNumber() && !value.IsBigInt()) [[unlikely]] { \
87
+ const ParameterInfo &param = func->parameters[i]; \
88
+ Napi::Value value = info[param.offset]; \
89
+ \
90
+ CType v; \
91
+ if (!TryNumber(value, &v)) [[unlikely]] { \
63
92
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value)); \
64
93
  return false; \
65
94
  } \
66
95
  \
67
- CType v = GetNumber<CType>(value); \
68
96
  *(args_ptr++) = (uint64_t)v; \
69
97
  } while (false)
70
98
  #define PUSH_INTEGER_SWAP(CType) \
71
99
  do { \
72
- if (!value.IsNumber() && !value.IsBigInt()) [[unlikely]] { \
100
+ const ParameterInfo &param = func->parameters[i]; \
101
+ Napi::Value value = info[param.offset]; \
102
+ \
103
+ CType v; \
104
+ if (!TryNumber(value, &v)) [[unlikely]] { \
73
105
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value)); \
74
106
  return false; \
75
107
  } \
76
108
  \
77
- CType v = GetNumber<CType>(value); \
78
109
  *(args_ptr++) = (uint64_t)ReverseBytes(v); \
79
110
  } while (false)
80
111
 
81
- // Push arguments
82
- for (Size i = 0; i < func->parameters.len; i++) {
83
- const ParameterInfo &param = func->parameters[i];
84
- K_ASSERT(param.directions >= 1 && param.directions <= 3);
112
+ LOOP {
113
+ CASE(Void) { K_UNREACHABLE(); };
85
114
 
86
- Napi::Value value = info[param.offset];
115
+ CASE(Bool) {
116
+ const ParameterInfo &param = func->parameters[i];
117
+ Napi::Value value = info[param.offset];
87
118
 
88
- switch (param.type->primitive) {
89
- case PrimitiveKind::Void: { K_UNREACHABLE(); } break;
119
+ bool b;
120
+ if (napi_get_value_bool(env, value, &b) != napi_ok) [[unlikely]] {
121
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected boolean", GetValueType(instance, value));
122
+ return false;
123
+ }
90
124
 
91
- case PrimitiveKind::Bool: {
92
- if (!value.IsBoolean()) [[unlikely]] {
93
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected boolean", GetValueType(instance, value));
94
- return false;
95
- }
125
+ *(bool *)(args_ptr++) = b;
126
+ };
127
+
128
+ CASE(Int8) { PUSH_INTEGER(int8_t); };
129
+ CASE(UInt8) { PUSH_INTEGER(uint8_t); };
130
+ CASE(Int16) { PUSH_INTEGER(int16_t); };
131
+ CASE(Int16S) { PUSH_INTEGER_SWAP(int16_t); };
132
+ CASE(UInt16) { PUSH_INTEGER(uint16_t); };
133
+ CASE(UInt16S) { PUSH_INTEGER_SWAP(uint16_t); };
134
+ CASE(Int32) { PUSH_INTEGER(int32_t); };
135
+ CASE(Int32S) { PUSH_INTEGER_SWAP(int32_t); };
136
+ CASE(UInt32) { PUSH_INTEGER(uint32_t); };
137
+ CASE(UInt32S) { PUSH_INTEGER_SWAP(uint32_t); };
138
+ CASE(Int64) { PUSH_INTEGER(int64_t); };
139
+ CASE(Int64S) { PUSH_INTEGER_SWAP(int64_t); };
140
+ CASE(UInt64) { PUSH_INTEGER(uint64_t); };
141
+ CASE(UInt64S) { PUSH_INTEGER_SWAP(uint64_t); };
142
+
143
+ CASE(String) {
144
+ const ParameterInfo &param = func->parameters[i];
145
+ Napi::Value value = info[param.offset];
96
146
 
97
- bool b = value.As<Napi::Boolean>();
147
+ const char *str;
148
+ if (!PushString(value, param.directions, &str)) [[unlikely]]
149
+ return false;
98
150
 
99
- *(bool *)(args_ptr++) = b;
100
- } break;
101
- case PrimitiveKind::Int8: { PUSH_INTEGER(int8_t); } break;
102
- case PrimitiveKind::UInt8: { PUSH_INTEGER(uint8_t); } break;
103
- case PrimitiveKind::Int16: { PUSH_INTEGER(int16_t); } break;
104
- case PrimitiveKind::Int16S: { PUSH_INTEGER_SWAP(int16_t); } break;
105
- case PrimitiveKind::UInt16: { PUSH_INTEGER(uint16_t); } break;
106
- case PrimitiveKind::UInt16S: { PUSH_INTEGER_SWAP(uint16_t); } break;
107
- case PrimitiveKind::Int32: { PUSH_INTEGER(int32_t); } break;
108
- case PrimitiveKind::Int32S: { PUSH_INTEGER_SWAP(int32_t); } break;
109
- case PrimitiveKind::UInt32: { PUSH_INTEGER(uint32_t); } break;
110
- case PrimitiveKind::UInt32S: { PUSH_INTEGER_SWAP(uint32_t); } break;
111
- case PrimitiveKind::Int64: { PUSH_INTEGER(int64_t); } break;
112
- case PrimitiveKind::Int64S: { PUSH_INTEGER_SWAP(int64_t); } break;
113
- case PrimitiveKind::UInt64: { PUSH_INTEGER(uint64_t); } break;
114
- case PrimitiveKind::UInt64S: { PUSH_INTEGER_SWAP(uint64_t); } break;
115
- case PrimitiveKind::String: {
116
- const char *str;
117
- if (!PushString(value, param.directions, &str)) [[unlikely]]
118
- return false;
151
+ *(const char **)(args_ptr++) = str;
152
+ };
153
+ CASE(String16) {
154
+ const ParameterInfo &param = func->parameters[i];
155
+ Napi::Value value = info[param.offset];
119
156
 
120
- *(const char **)(args_ptr++) = str;
121
- } break;
122
- case PrimitiveKind::String16: {
123
- const char16_t *str16;
124
- if (!PushString16(value, param.directions, &str16)) [[unlikely]]
125
- return false;
157
+ const char16_t *str16;
158
+ if (!PushString16(value, param.directions, &str16)) [[unlikely]]
159
+ return false;
126
160
 
127
- *(const char16_t **)(args_ptr++) = str16;
128
- } break;
129
- case PrimitiveKind::String32: {
130
- const char32_t *str32;
131
- if (!PushString32(value, param.directions, &str32)) [[unlikely]]
132
- return false;
161
+ *(const char16_t **)(args_ptr++) = str16;
162
+ };
163
+ CASE(String32) {
164
+ const ParameterInfo &param = func->parameters[i];
165
+ Napi::Value value = info[param.offset];
133
166
 
134
- *(const char32_t **)(args_ptr++) = str32;
135
- } break;
136
- case PrimitiveKind::Pointer: {
137
- void *ptr;
138
- if (!PushPointer(value, param.type, param.directions, &ptr)) [[unlikely]]
139
- return false;
167
+ const char32_t *str32;
168
+ if (!PushString32(value, param.directions, &str32)) [[unlikely]]
169
+ return false;
140
170
 
141
- *(void **)(args_ptr++) = ptr;
142
- } break;
143
- case PrimitiveKind::Record:
144
- case PrimitiveKind::Union: {
145
- if (!IsObject(value)) [[unlikely]] {
146
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected object", GetValueType(instance, value));
147
- return false;
148
- }
171
+ *(const char32_t **)(args_ptr++) = str32;
172
+ };
149
173
 
150
- uint8_t *ptr;
151
- if (param.regular) {
152
- ptr = (uint8_t *)(args_ptr++);
153
- } else {
154
- ptr = AllocHeap(param.type->size, 16);
155
- *(uint8_t **)(args_ptr++) = ptr;
156
- }
174
+ CASE(Pointer) {
175
+ const ParameterInfo &param = func->parameters[i];
176
+ Napi::Value value = info[param.offset];
157
177
 
158
- Napi::Object obj = value.As<Napi::Object>();
159
- if (!PushObject(obj, param.type, ptr))
160
- return false;
161
- } break;
162
- case PrimitiveKind::Array: { K_UNREACHABLE(); } break;
163
- case PrimitiveKind::Float32: {
164
- if (!value.IsNumber() && !value.IsBigInt()) [[unlikely]] {
165
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value));
166
- return false;
167
- }
178
+ void *ptr;
179
+ if (!PushPointer(value, param.type, param.directions, &ptr)) [[unlikely]]
180
+ return false;
168
181
 
169
- float f = GetNumber<float>(value);
182
+ *(void **)(args_ptr++) = ptr;
183
+ };
170
184
 
171
- memset((uint8_t *)args_ptr + 4, 0, 4);
172
- *(float *)(args_ptr++) = f;
173
- } break;
174
- case PrimitiveKind::Float64: {
175
- if (!value.IsNumber() && !value.IsBigInt()) [[unlikely]] {
176
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value));
177
- return false;
178
- }
185
+ CASE(Record) OR(Union) {
186
+ const ParameterInfo &param = func->parameters[i];
187
+ Napi::Value value = info[param.offset];
179
188
 
180
- double d = GetNumber<double>(value);
181
- *(double *)(args_ptr++) = d;
182
- } break;
183
- case PrimitiveKind::Callback: {
184
- void *ptr;
185
- if (!PushCallback(value, param.type, &ptr)) [[unlikely]]
186
- return false;
189
+ if (!IsObject(value)) [[unlikely]] {
190
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected object", GetValueType(instance, value));
191
+ return false;
192
+ }
187
193
 
188
- *(void **)(args_ptr++) = ptr;
189
- } break;
194
+ uint8_t *ptr;
195
+ if (param.regular) {
196
+ ptr = (uint8_t *)(args_ptr++);
197
+ } else {
198
+ ptr = AllocHeap(param.type->size, 16);
199
+ *(uint8_t **)(args_ptr++) = ptr;
200
+ }
190
201
 
191
- case PrimitiveKind::Prototype: { K_UNREACHABLE(); } break;
192
- }
202
+ Napi::Object obj = value.As<Napi::Object>();
203
+ if (!PushObject(obj, param.type, ptr))
204
+ return false;
205
+ };
206
+ CASE(Array) { K_UNREACHABLE(); };
207
+
208
+ CASE(Float32) {
209
+ const ParameterInfo &param = func->parameters[i];
210
+ Napi::Value value = info[param.offset];
211
+
212
+ float f;
213
+ if (!TryNumber(value, &f)) [[unlikely]] {
214
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value));
215
+ return false;
216
+ }
217
+
218
+ memset((uint8_t *)args_ptr + 4, 0, 4);
219
+ *(float *)(args_ptr++) = f;
220
+ };
221
+ CASE(Float64) {
222
+ const ParameterInfo &param = func->parameters[i];
223
+ Napi::Value value = info[param.offset];
224
+
225
+ double d;
226
+ if (!TryNumber(value, &d)) [[unlikely]] {
227
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value));
228
+ return false;
229
+ }
230
+
231
+ *(double *)(args_ptr++) = d;
232
+ };
233
+ CASE(Callback) {
234
+ const ParameterInfo &param = func->parameters[i];
235
+ Napi::Value value = info[param.offset];
236
+
237
+ void *ptr;
238
+ if (!PushCallback(value, param.type, &ptr)) [[unlikely]]
239
+ return false;
240
+
241
+ *(void **)(args_ptr++) = ptr;
242
+ };
243
+
244
+ CASE(Prototype) { /* End loop */ };
193
245
  }
194
246
 
195
247
  #undef PUSH_INTEGER_SWAP
196
248
  #undef PUSH_INTEGER
197
249
 
250
+ #undef OR
251
+ #undef CASE
252
+ #undef LOOP
253
+
198
254
  new_sp = mem->stack.end();
199
255
 
200
256
  return true;
@@ -302,17 +358,8 @@ Napi::Value CallData::Complete(const FunctionInfo *func)
302
358
  case PrimitiveKind::String: return result.ptr ? Napi::String::New(env, (const char *)result.ptr) : env.Null();
303
359
  case PrimitiveKind::String16: return result.ptr ? Napi::String::New(env, (const char16_t *)result.ptr) : env.Null();
304
360
  case PrimitiveKind::String32: return result.ptr ? MakeStringFromUTF32(env, (const char32_t *)result.ptr) : env.Null();
305
- case PrimitiveKind::Pointer:
306
- case PrimitiveKind::Callback: {
307
- if (result.ptr) {
308
- Napi::External<void> external = Napi::External<void>::New(env, result.ptr);
309
- SetValueTag(external, func->ret.type->ref.marker);
310
-
311
- return external;
312
- } else {
313
- return env.Null();
314
- }
315
- } break;
361
+ case PrimitiveKind::Pointer: return result.ptr ? WrapPointer(env, func->ret.type->ref.type, result.ptr) : env.Null();
362
+ case PrimitiveKind::Callback: return result.ptr ? WrapCallback(env, func->ret.type->ref.type, result.ptr) : env.Null();
316
363
  case PrimitiveKind::Record:
317
364
  case PrimitiveKind::Union: {
318
365
  const uint8_t *ptr = return_ptr ? (const uint8_t *)return_ptr
@@ -520,19 +567,23 @@ void CallData::Relay(Size idx, uint8_t *own_sp, uint8_t *caller_sp, bool switch_
520
567
  Napi::Value arg = str32 ? MakeStringFromUTF32(env, str32) : env.Null();
521
568
  arguments.Append(arg);
522
569
  } break;
523
- case PrimitiveKind::Pointer:
524
- case PrimitiveKind::Callback: {
570
+ case PrimitiveKind::Pointer: {
525
571
  void *ptr2 = *(void **)(j < 4 ? gpr_ptr + j : args_ptr);
526
572
  args_ptr += (j >= 4);
527
573
 
528
- if (ptr2) {
529
- Napi::External<void> external = Napi::External<void>::New(env, ptr2);
530
- SetValueTag(external, param.type->ref.marker);
574
+ Napi::Value p = ptr2 ? WrapPointer(env, param.type->ref.type, ptr2) : env.Null();
575
+ arguments.Append(p);
531
576
 
532
- arguments.Append(external);
533
- } else {
534
- arguments.Append(env.Null());
577
+ if (param.type->dispose) {
578
+ param.type->dispose(env, param.type, ptr2);
535
579
  }
580
+ } break;
581
+ case PrimitiveKind::Callback: {
582
+ void *ptr2 = *(void **)(j < 4 ? gpr_ptr + j : args_ptr);
583
+ args_ptr += (j >= 4);
584
+
585
+ Napi::Value p = ptr2 ? WrapCallback(env, param.type->ref.type, ptr2) : env.Null();
586
+ arguments.Append(p);
536
587
 
537
588
  if (param.type->dispose) {
538
589
  param.type->dispose(env, param.type, ptr2);
@@ -588,34 +639,34 @@ void CallData::Relay(Size idx, uint8_t *own_sp, uint8_t *caller_sp, bool switch_
588
639
 
589
640
  #define RETURN_INTEGER(CType) \
590
641
  do { \
591
- if (!value.IsNumber() && !value.IsBigInt()) [[unlikely]] { \
642
+ CType v; \
643
+ if (!TryNumber(value, &v)) [[unlikely]] { \
592
644
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value)); \
593
645
  return; \
594
646
  } \
595
647
  \
596
- CType v = GetNumber<CType>(value); \
597
648
  out_reg->rax = (uint64_t)v; \
598
649
  } while (false)
599
650
  #define RETURN_INTEGER_SWAP(CType) \
600
651
  do { \
601
- if (!value.IsNumber() && !value.IsBigInt()) [[unlikely]] { \
652
+ CType v; \
653
+ if (!TryNumber(value, &v)) [[unlikely]] { \
602
654
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value)); \
603
655
  return; \
604
656
  } \
605
657
  \
606
- CType v = GetNumber<CType>(value); \
607
658
  out_reg->rax = (uint64_t)ReverseBytes(v); \
608
659
  } while (false)
609
660
 
610
661
  switch (type->primitive) {
611
662
  case PrimitiveKind::Void: {} break;
612
663
  case PrimitiveKind::Bool: {
613
- if (!value.IsBoolean()) [[unlikely]] {
664
+ bool b;
665
+ if (napi_get_value_bool(env, value, &b) != napi_ok) [[unlikely]] {
614
666
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected boolean", GetValueType(instance, value));
615
667
  return;
616
668
  }
617
669
 
618
- bool b = value.As<Napi::Boolean>();
619
670
  out_reg->rax = (uint64_t)b;
620
671
  } break;
621
672
  case PrimitiveKind::Int8: { RETURN_INTEGER(int8_t); } break;
@@ -654,24 +705,9 @@ void CallData::Relay(Size idx, uint8_t *own_sp, uint8_t *caller_sp, bool switch_
654
705
  out_reg->rax = (uint64_t)str32;
655
706
  } break;
656
707
  case PrimitiveKind::Pointer: {
657
- uint8_t *ptr;
658
-
659
- if (CheckValueTag(value, type->ref.marker)) {
660
- ptr = value.As<Napi::External<uint8_t>>().Data();
661
- } else if (IsObject(value) && (type->ref.type->primitive == PrimitiveKind::Record ||
662
- type->ref.type->primitive == PrimitiveKind::Union)) {
663
- Napi::Object obj = value.As<Napi::Object>();
664
-
665
- ptr = AllocHeap(type->ref.type->size, 16);
666
-
667
- if (!PushObject(obj, type->ref.type, ptr))
668
- return;
669
- } else if (IsNullOrUndefined(value)) {
670
- ptr = nullptr;
671
- } else {
672
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected %2", GetValueType(instance, value), type->name);
708
+ void *ptr;
709
+ if (!PushPointer(value, type, 1, &ptr)) [[unlikely]]
673
710
  return;
674
- }
675
711
 
676
712
  out_reg->rax = (uint64_t)ptr;
677
713
  } break;
@@ -694,42 +730,28 @@ void CallData::Relay(Size idx, uint8_t *own_sp, uint8_t *caller_sp, bool switch_
694
730
  } break;
695
731
  case PrimitiveKind::Array: { K_UNREACHABLE(); } break;
696
732
  case PrimitiveKind::Float32: {
697
- if (!value.IsNumber() && !value.IsBigInt()) [[unlikely]] {
733
+ float f;
734
+ if (!TryNumber(value, &f)) [[unlikely]] {
698
735
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value));
699
736
  return;
700
737
  }
701
738
 
702
- float f = GetNumber<float>(value);
703
-
704
739
  memset((uint8_t *)&out_reg->xmm0 + 4, 0, 4);
705
740
  memcpy(&out_reg->xmm0, &f, 4);
706
741
  } break;
707
742
  case PrimitiveKind::Float64: {
708
- if (!value.IsNumber() && !value.IsBigInt()) [[unlikely]] {
743
+ double d;
744
+ if (!TryNumber(value, &d)) [[unlikely]] {
709
745
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value));
710
746
  return;
711
747
  }
712
748
 
713
- double d = GetNumber<double>(value);
714
749
  out_reg->xmm0 = d;
715
750
  } break;
716
751
  case PrimitiveKind::Callback: {
717
752
  void *ptr;
718
-
719
- if (value.IsFunction()) {
720
- Napi::Function func2 = value.As<Napi::Function>();
721
-
722
- ptr = ReserveTrampoline(type->ref.proto, func2);
723
- if (!ptr) [[unlikely]]
724
- return;
725
- } else if (CheckValueTag(value, type->ref.marker)) {
726
- ptr = value.As<Napi::External<void>>().Data();
727
- } else if (IsNullOrUndefined(value)) {
728
- ptr = nullptr;
729
- } else {
730
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected %2", GetValueType(instance, value), type->name);
753
+ if (!PushCallback(value, type, &ptr)) [[unlikely]]
731
754
  return;
732
- }
733
755
 
734
756
  out_reg->rax = (uint64_t)ptr;
735
757
  } break;
@@ -743,12 +765,6 @@ void CallData::Relay(Size idx, uint8_t *own_sp, uint8_t *caller_sp, bool switch_
743
765
  err_guard.Disable();
744
766
  }
745
767
 
746
- void *GetTrampoline(int16_t idx, const FunctionInfo *proto)
747
- {
748
- bool xmm = proto->forward_fp || IsFloat(proto->ret.type);
749
- return Trampolines[idx][xmm];
750
- }
751
-
752
768
  }
753
769
 
754
770
  #endif
@@ -0,0 +1,144 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # SPDX-FileCopyrightText: 2025 Niels Martignène <niels.martignene@protonmail.com>
3
+
4
+ #define SYMBOL(Symbol) Symbol
5
+
6
+ # Forward
7
+ # ----------------------------
8
+
9
+ .global ForwardCallG
10
+ .global ForwardCallF
11
+ .global ForwardCallD
12
+ .global ForwardCallXG
13
+ .global ForwardCallXF
14
+ .global ForwardCallXD
15
+
16
+ # Copy function pointer to RAX, in order to save it through argument forwarding.
17
+ # Also make a copy of the SP to CallData::old_sp because the callback system might need it.
18
+ # Save RSP in RBX (non-volatile), and use carefully assembled stack provided by caller.
19
+ .macro prologue
20
+ endbr64
21
+ movq %rcx, %rax
22
+ push %rbp
23
+ movq %rsp, %rbp
24
+ movq %rsp, (%r8)
25
+ movq %rdx, %rsp
26
+ .endm
27
+
28
+ # Call native function.
29
+ # Once done, restore normal stack pointer and return.
30
+ # The return value is passed untouched through RAX or XMM0.
31
+ .macro epilogue
32
+ call *%rax
33
+ movq %rbp, %rsp
34
+ pop %rbp
35
+ ret
36
+ .endm
37
+
38
+ # Prepare integer argument registers from array passed by caller.
39
+ .macro forward_gpr
40
+ movq 24(%rdx), %r9
41
+ movq 16(%rdx), %r8
42
+ movq 0(%rdx), %rcx
43
+ movq 8(%rdx), %rdx
44
+ .endm
45
+
46
+ # Prepare XMM argument registers from array passed by caller.
47
+ .macro forward_xmm
48
+ movsd 24(%rdx), %xmm3
49
+ movsd 16(%rdx), %xmm2
50
+ movsd 8(%rdx), %xmm1
51
+ movsd 0(%rdx), %xmm0
52
+ .endm
53
+
54
+ ForwardCallG:
55
+ prologue
56
+ forward_gpr
57
+ epilogue
58
+
59
+ ForwardCallF:
60
+ prologue
61
+ forward_gpr
62
+ epilogue
63
+
64
+ ForwardCallD:
65
+ prologue
66
+ forward_gpr
67
+ epilogue
68
+
69
+ ForwardCallXG:
70
+ prologue
71
+ forward_xmm
72
+ forward_gpr
73
+ epilogue
74
+
75
+ ForwardCallXF:
76
+ prologue
77
+ forward_xmm
78
+ forward_gpr
79
+ epilogue
80
+
81
+ ForwardCallXD:
82
+ prologue
83
+ forward_xmm
84
+ forward_gpr
85
+ epilogue
86
+
87
+ # Callbacks
88
+ # ----------------------------
89
+
90
+ .global RelayCallback
91
+ .global CallSwitchStack
92
+
93
+ # First, make a copy of argument registers.
94
+ # Then call the C function RelayCallback with the following arguments:
95
+ # static trampoline ID, a pointer to the saved GPR array, a pointer to the stack
96
+ # arguments of this call, and a pointer to a struct that will contain the result registers.
97
+ # After the call, simply load these registers from the output struct.
98
+ .macro trampoline id
99
+ endbr64
100
+ subq $120, %rsp
101
+ movq %rcx, 32(%rsp)
102
+ movq %rdx, 40(%rsp)
103
+ movq %r8, 48(%rsp)
104
+ movq %r9, 56(%rsp)
105
+ movsd %xmm0, 64(%rsp)
106
+ movsd %xmm1, 72(%rsp)
107
+ movsd %xmm2, 80(%rsp)
108
+ movsd %xmm3, 88(%rsp)
109
+ movq $\id, %rcx
110
+ leaq 32(%rsp), %rdx
111
+ leaq 160(%rsp), %r8
112
+ leaq 96(%rsp), %r9
113
+ call RelayCallback
114
+ movq 96(%rsp), %rax
115
+ movsd 104(%rsp), %xmm0
116
+ addq $120, %rsp
117
+ ret
118
+ .endm
119
+
120
+ # When a callback is relayed, Koffi will call into Node.js and V8 to execute Javascript.
121
+ # The problem is that we're still running on the separate Koffi stack, and V8 will
122
+ # probably misdetect this as a "stack overflow". We have to restore the old
123
+ # stack pointer, call Node.js/V8 and go back to ours.
124
+ # The first three parameters (rcx, rdx, r8) are passed through untouched.
125
+ CallSwitchStack:
126
+ endbr64
127
+ push %rbp
128
+ movq %rsp, %rbp
129
+ movq 56(%rsp), %rax
130
+ movq %rsp, %r10
131
+ movq 48(%rsp), %r11
132
+ subq 0(%r11), %r10
133
+ andq $-16, %r10
134
+ movq %r10, 8(%r11)
135
+ leaq -32(%r9), %rsp
136
+ call *%rax
137
+ movq %rbp, %rsp
138
+ pop %rbp
139
+ ret
140
+
141
+ # Trampolines
142
+ # ----------------------------
143
+
144
+ #include "trampolines/gnu.inc"
@@ -103,32 +103,12 @@ ForwardCallXD endp
103
103
  extern RelayCallback : PROC
104
104
  public CallSwitchStack
105
105
 
106
- ; First, make a copy of the GPR argument registers (rcx, rdx, r8, r9).
106
+ ; First, make a copy of argument registers.
107
107
  ; Then call the C function RelayCallback with the following arguments:
108
108
  ; static trampoline ID, a pointer to the saved GPR array, a pointer to the stack
109
109
  ; arguments of this call, and a pointer to a struct that will contain the result registers.
110
110
  ; After the call, simply load these registers from the output struct.
111
111
  trampoline macro ID
112
- endbr64
113
- sub rsp, 120
114
- .allocstack 120
115
- .endprolog
116
- mov qword ptr [rsp+32], rcx
117
- mov qword ptr [rsp+40], rdx
118
- mov qword ptr [rsp+48], r8
119
- mov qword ptr [rsp+56], r9
120
- mov rcx, ID
121
- lea rdx, qword ptr [rsp+32]
122
- lea r8, qword ptr [rsp+160]
123
- lea r9, qword ptr [rsp+96]
124
- call RelayCallback
125
- mov rax, qword ptr [rsp+96]
126
- add rsp, 120
127
- ret
128
- endm
129
-
130
- ; Same thing, but also forward the XMM argument registers and load the XMM result registers.
131
- trampoline_vec macro ID
132
112
  endbr64
133
113
  sub rsp, 120
134
114
  .allocstack 120