koffi 2.16.1 → 3.0.0-alpha.2

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 (84) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/README.md +8 -8
  3. package/build/koffi/darwin_arm64/koffi.node +0 -0
  4. package/build/koffi/darwin_x64/koffi.node +0 -0
  5. package/build/koffi/freebsd_arm64/koffi.node +0 -0
  6. package/build/koffi/freebsd_ia32/koffi.node +0 -0
  7. package/build/koffi/freebsd_x64/koffi.node +0 -0
  8. package/build/koffi/linux_arm64/koffi.node +0 -0
  9. package/build/koffi/linux_ia32/koffi.node +0 -0
  10. package/build/koffi/linux_x64/koffi.node +0 -0
  11. package/build/koffi/musl_arm64/koffi.node +0 -0
  12. package/build/koffi/musl_x64/koffi.node +0 -0
  13. package/build/koffi/openbsd_ia32/koffi.node +0 -0
  14. package/build/koffi/openbsd_x64/koffi.node +0 -0
  15. package/build/koffi/win32_ia32/koffi.node +0 -0
  16. package/build/koffi/win32_x64/koffi.node +0 -0
  17. package/doc/benchmarks.md +33 -33
  18. package/doc/benchmarks.xlsx +0 -0
  19. package/doc/contribute.md +37 -25
  20. package/doc/index.md +8 -8
  21. package/doc/variables.md +1 -1
  22. package/index.d.ts +11 -9
  23. package/index.js +1 -634
  24. package/indirect.js +1 -533
  25. package/lib/native/base/base.cc +63 -85
  26. package/lib/native/base/base.hh +178 -151
  27. package/package.json +3 -4
  28. package/src/build.js +1178 -0
  29. package/src/koffi/CMakeLists.txt +97 -34
  30. package/src/koffi/index.js +359 -0
  31. package/src/koffi/indirect.js +216 -0
  32. package/src/koffi/src/{abi_arm32.cc → abi/arm32.cc} +279 -275
  33. package/src/koffi/src/{abi_arm32_asm.S → abi/arm32_asm.S} +2 -5
  34. package/src/koffi/src/abi/arm64.cc +1511 -0
  35. package/src/koffi/src/{abi_arm64_asm.S → abi/arm64_asm.S} +44 -23
  36. package/src/koffi/src/{abi_arm64_asm.asm → abi/arm64_asm.asm} +32 -25
  37. package/src/koffi/src/{abi_loong64_asm.S → abi/loong64_asm.S} +1 -5
  38. package/src/koffi/src/{abi_riscv64.cc → abi/riscv64.cc} +279 -273
  39. package/src/koffi/src/{abi_riscv64_asm.S → abi/riscv64_asm.S} +1 -4
  40. package/src/koffi/src/abi/x64sysv.cc +1456 -0
  41. package/src/koffi/src/{abi_x64_sysv_asm.S → abi/x64sysv_asm.S} +30 -33
  42. package/src/koffi/src/abi/x64win.cc +1121 -0
  43. package/src/koffi/src/{abi_x64_win_asm.S → abi/x64win_asm.S} +8 -12
  44. package/src/koffi/src/{abi_x64_win_asm.asm → abi/x64win_asm.asm} +11 -14
  45. package/src/koffi/src/abi/x86.cc +1233 -0
  46. package/src/koffi/src/{abi_x86_asm.S → abi/x86_asm.S} +49 -25
  47. package/src/koffi/src/{abi_x86_asm.asm → abi/x86_asm.asm} +11 -14
  48. package/src/koffi/src/call.cc +458 -598
  49. package/src/koffi/src/call.hh +61 -82
  50. package/src/koffi/src/ffi.cc +595 -309
  51. package/src/koffi/src/ffi.hh +103 -89
  52. package/src/koffi/src/parser.cc +0 -4
  53. package/src/koffi/src/primitives.inc +39 -0
  54. package/src/koffi/{tools/write_trampolines.js → src/trampolines.js} +1 -1
  55. package/src/koffi/src/util.cc +503 -446
  56. package/src/koffi/src/util.hh +269 -57
  57. package/src/koffi/src/uv.cc +2 -2
  58. package/src/koffi/src/uv.def +3 -0
  59. package/src/koffi/src/win32.hh +30 -1
  60. package/vendor/node-addon-api/napi-inl.h +4 -0
  61. package/vendor/node-addon-api/napi.h +1 -0
  62. package/build/koffi/linux_armhf/koffi.node +0 -0
  63. package/build/koffi/linux_loong64/koffi.node +0 -0
  64. package/build/koffi/linux_riscv64d/koffi.node +0 -0
  65. package/build/koffi/win32_arm64/koffi.exp +0 -0
  66. package/build/koffi/win32_arm64/koffi.lib +0 -0
  67. package/build/koffi/win32_arm64/koffi.node +0 -0
  68. package/doc/platforms.md +0 -36
  69. package/src/cnoke/LICENSE.txt +0 -22
  70. package/src/cnoke/README.md +0 -99
  71. package/src/cnoke/assets/FindCNoke.cmake +0 -127
  72. package/src/cnoke/assets/toolchains.json +0 -126
  73. package/src/cnoke/assets/win_delay_hook.c +0 -36
  74. package/src/cnoke/cnoke.js +0 -170
  75. package/src/cnoke/package.json +0 -24
  76. package/src/cnoke/src/builder.js +0 -511
  77. package/src/cnoke/src/index.js +0 -10
  78. package/src/cnoke/src/tools.js +0 -407
  79. package/src/koffi/src/abi_arm64.cc +0 -1295
  80. package/src/koffi/src/abi_x64_sysv.cc +0 -939
  81. package/src/koffi/src/abi_x64_win.cc +0 -715
  82. package/src/koffi/src/abi_x86.cc +0 -860
  83. package/src/koffi/src/init.js +0 -105
  84. /package/src/koffi/src/{abi_loong64.cc → abi/loong64.cc} +0 -0
@@ -32,14 +32,18 @@
32
32
  #include <wchar.h>
33
33
 
34
34
  #include <napi.h>
35
+ #include <uv.h>
35
36
 
36
37
  namespace K {
37
38
 
38
- SharedData shared;
39
+ extern "C" napi_value SwitchAndRelay(CallData *call, Size idx, uint8_t *sp, uint8_t *saved_sp, MemoryRange<uint8_t> *new_stack);
39
40
 
40
- static thread_local CallData *exec_call;
41
+ SharedData shared;
41
42
 
42
- extern "C" napi_value SwitchAndRelay(CallData *call, Size idx, uint8_t *sp, uint8_t *saved_sp, Span<uint8_t> *new_stack);
43
+ // Recent N-API functions are loaded dynamically
44
+ napi_status (NAPI_CDECL *node_api_create_property_key_utf8)(napi_env env, const char* str, size_t length, napi_value* result);
45
+ napi_status (NAPI_CDECL *node_api_post_finalizer)(node_api_basic_env env, napi_finalize finalize_cb, void* finalize_data, void* finalize_hint);
46
+ napi_value (*translate_zero_call)(napi_env env, napi_callback_info info);
43
47
 
44
48
  static bool ChangeSize(const char *name, Napi::Value value, Size min_size, Size max_size, Size *out_size)
45
49
  {
@@ -104,7 +108,7 @@ static Napi::Value GetSetConfig(const Napi::CallbackInfo &info)
104
108
  return env.Null();
105
109
  }
106
110
 
107
- if (!info[0].IsObject()) {
111
+ if (!IsObject(env, info[0])) {
108
112
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for config, expected object", GetValueType(instance, info[0]));
109
113
  return env.Null();
110
114
  }
@@ -113,7 +117,7 @@ static Napi::Value GetSetConfig(const Napi::CallbackInfo &info)
113
117
  int max_async_calls = new_config.resident_async_pools + new_config.max_temporaries;
114
118
 
115
119
  Napi::Object obj = info[0].As<Napi::Object>();
116
- Napi::Array keys = GetOwnPropertyNames(obj);
120
+ Napi::Array keys = GetOwnPropertyNames(env, obj);
117
121
 
118
122
  for (uint32_t i = 0; i < keys.Length(); i++) {
119
123
  std::string key = keys.Get(i).As<Napi::String>();
@@ -140,6 +144,20 @@ static Napi::Value GetSetConfig(const Napi::CallbackInfo &info)
140
144
  } else if (key == "max_type_size") {
141
145
  if (!ChangeSize(key.c_str(), value, 32, Mebibytes(512), &new_config.max_type_size))
142
146
  return env.Null();
147
+ } else if (key == "fast_pointers") {
148
+ if (!value.IsBoolean()) {
149
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for '%2', expected boolean", GetValueType(instance, value), key.c_str());
150
+ return env.Null();
151
+ }
152
+
153
+ new_config.fast_pointers = value.As<Napi::Boolean>();
154
+ } else if (key == "fast_callbacks") {
155
+ if (!value.IsBoolean()) {
156
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for '%2', expected boolean", GetValueType(instance, value), key.c_str());
157
+ return env.Null();
158
+ }
159
+
160
+ new_config.fast_callbacks = value.As<Napi::Boolean>();
143
161
  } else {
144
162
  ThrowError<Napi::Error>(env, "Unexpected config member '%1'", key.c_str());
145
163
  return env.Null();
@@ -164,6 +182,8 @@ static Napi::Value GetSetConfig(const Napi::CallbackInfo &info)
164
182
  obj.Set("resident_async_pools", instance->config.resident_async_pools);
165
183
  obj.Set("max_async_calls", instance->config.resident_async_pools + instance->config.max_temporaries);
166
184
  obj.Set("max_type_size", instance->config.max_type_size);
185
+ obj.Set("fast_pointers", instance->config.fast_pointers);
186
+ obj.Set("fast_callbacks", instance->config.fast_callbacks);
167
187
 
168
188
  return obj;
169
189
  }
@@ -217,6 +237,29 @@ static bool MapType(Napi::Env env, InstanceData *instance, const TypeInfo *type,
217
237
  return true;
218
238
  }
219
239
 
240
+ static bool FinalizeCompositeType(Napi::Env env, TypeInfo *type, Size size)
241
+ {
242
+ if (node_api_create_property_key_utf8) {
243
+ for (RecordMember &member: type->members) {
244
+ napi_value key = nullptr;
245
+ node_api_create_property_key_utf8(env, member.name, NAPI_AUTO_LENGTH, &key);
246
+
247
+ napi_create_reference(env, key, 1, &member.key);
248
+ }
249
+ }
250
+
251
+ size = AlignLen(size, type->align);
252
+ if (!size) {
253
+ ThrowError<Napi::Error>(env, "Empty type '%1' is not allowed in C", type->name);
254
+ return false;
255
+ }
256
+ type->size = (int32_t)size;
257
+
258
+ type->flags &= ~(int)TypeFlag::IsIncomplete;
259
+
260
+ return true;
261
+ }
262
+
220
263
  static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
221
264
  {
222
265
  Napi::Env env = info.Env();
@@ -228,21 +271,21 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
228
271
  }
229
272
 
230
273
  bool skip = (info.Length() > 1);
231
- bool named = skip && !IsNullOrUndefined(info[0]);
232
- bool redefine = named && CheckValueTag(info[0], &TypeInfoMarker);
274
+ bool named = skip && !IsNullOrUndefined(env, info[0]);
275
+ bool redefine = named && info[0].IsExternal() && CheckValueTag(env, info[0], &TypeInfoMarker);
233
276
 
234
277
  if (named && !info[0].IsString() && !redefine) {
235
278
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for name, expected string", GetValueType(instance, info[0]));
236
279
  return env.Null();
237
280
  }
238
- if (!IsObject(info[skip])) {
281
+ if (!IsObject(env, info[skip])) {
239
282
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for members, expected object", GetValueType(instance, info[1]));
240
283
  return env.Null();
241
284
  }
242
285
 
243
286
  Napi::String name = info[0].As<Napi::String>();
244
287
  Napi::Object obj = info[skip].As<Napi::Object>();
245
- Napi::Array keys = GetOwnPropertyNames(obj);
288
+ Napi::Array keys = GetOwnPropertyNames(env, obj);
246
289
 
247
290
  K_DEFER_NC(err_guard, count = instance->types.count) {
248
291
  Size start = count + !skip;
@@ -287,7 +330,7 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
287
330
  type->flags = (int)TypeFlag::IsIncomplete;
288
331
 
289
332
  HashSet<const char *> members;
290
- int64_t size = 0;
333
+ Size size = 0;
291
334
 
292
335
  for (uint32_t i = 0; i < keys.Length(); i++) {
293
336
  RecordMember member = {};
@@ -384,14 +427,8 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
384
427
  }
385
428
  }
386
429
 
387
- size = (int32_t)AlignLen(size, type->align);
388
- if (!size) {
389
- ThrowError<Napi::Error>(env, "Empty struct '%1' is not allowed in C", type->name);
430
+ if (!FinalizeCompositeType(env, type, size))
390
431
  return env.Null();
391
- }
392
- type->size = (int32_t)size;
393
-
394
- type->flags &= ~(int)TypeFlag::IsIncomplete;
395
432
  err_guard.Disable();
396
433
 
397
434
  if (replace) {
@@ -423,21 +460,21 @@ static Napi::Value CreateUnionType(const Napi::CallbackInfo &info)
423
460
  }
424
461
 
425
462
  bool skip = (info.Length() > 1);
426
- bool named = skip && !IsNullOrUndefined(info[0]);
427
- bool redefine = named && CheckValueTag(info[0], &TypeInfoMarker);
463
+ bool named = skip && !IsNullOrUndefined(env, info[0]);
464
+ bool redefine = named && info[0].IsExternal() && CheckValueTag(env, info[0], &TypeInfoMarker);
428
465
 
429
466
  if (named && !info[0].IsString() && !redefine) {
430
467
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for name, expected string", GetValueType(instance, info[0]));
431
468
  return env.Null();
432
469
  }
433
- if (!IsObject(info[skip])) {
470
+ if (!IsObject(env, info[skip])) {
434
471
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for members, expected object", GetValueType(instance, info[1]));
435
472
  return env.Null();
436
473
  }
437
474
 
438
475
  Napi::String name = info[0].As<Napi::String>();
439
476
  Napi::Object obj = info[skip].As<Napi::Object>();
440
- Napi::Array keys = GetOwnPropertyNames(obj);
477
+ Napi::Array keys = GetOwnPropertyNames(env, obj);
441
478
 
442
479
  K_DEFER_NC(err_guard, count = instance->types.count) {
443
480
  Size start = count + !skip;
@@ -549,18 +586,12 @@ static Napi::Value CreateUnionType(const Napi::CallbackInfo &info)
549
586
  type->members.Append(member);
550
587
  }
551
588
 
552
- size = (int32_t)AlignLen(size, type->align);
553
- if (!size) {
554
- ThrowError<Napi::Error>(env, "Empty union '%1' is not allowed in C", type->name);
589
+ if (!FinalizeCompositeType(env, type, size))
555
590
  return env.Null();
556
- }
557
- type->size = (int32_t)size;
558
-
559
- type->flags &= ~(int)TypeFlag::IsIncomplete;
560
591
  err_guard.Disable();
561
592
 
562
593
  // Union constructor
563
- Napi::Function constructor = MagicUnion::InitClass(env, type);
594
+ Napi::Function constructor = UnionClass::InitClass(env, type);
564
595
  type->construct.Reset(constructor, 1);
565
596
 
566
597
  if (replace) {
@@ -593,7 +624,7 @@ Napi::Value InstantiateUnion(const Napi::CallbackInfo &info)
593
624
  }
594
625
 
595
626
  Napi::Object wrapper = type->construct.New({}).As<Napi::Object>();
596
- SetValueTag(wrapper, &MagicUnionMarker);
627
+ SetValueTag(env, wrapper, &UnionClassMarker);
597
628
 
598
629
  return wrapper;
599
630
  }
@@ -603,7 +634,7 @@ static Napi::Value CreateOpaqueType(const Napi::CallbackInfo &info)
603
634
  Napi::Env env = info.Env();
604
635
  InstanceData *instance = env.GetInstanceData<InstanceData>();
605
636
 
606
- bool named = (info.Length() >= 1) && !IsNullOrUndefined(info[0]);
637
+ bool named = (info.Length() >= 1) && !IsNullOrUndefined(env, info[0]);
607
638
 
608
639
  if (named && !info[0].IsString()) {
609
640
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for name, expected string", GetValueType(instance, info[0]));
@@ -641,7 +672,7 @@ static Napi::Value CreatePointerType(const Napi::CallbackInfo &info)
641
672
  }
642
673
 
643
674
  bool skip = (info.Length() > 1) && !info[1].IsNumber();
644
- bool named = skip && !IsNullOrUndefined(info[0]);
675
+ bool named = skip && !IsNullOrUndefined(env, info[0]);
645
676
 
646
677
  if (named && !info[0].IsString()) {
647
678
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for name, expected string", GetValueType(instance, info[0]));
@@ -754,7 +785,7 @@ static Napi::Value CreateDisposableType(const Napi::CallbackInfo &info)
754
785
  }
755
786
 
756
787
  bool skip = (info.Length() > 1) && !info[1].IsFunction();
757
- bool named = skip && !IsNullOrUndefined(info[0]);
788
+ bool named = skip && !IsNullOrUndefined(env, info[0]);
758
789
 
759
790
  if (named && !info[0].IsString()) {
760
791
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for name, expected string", GetValueType(instance, info[0]));
@@ -780,7 +811,7 @@ static Napi::Value CreateDisposableType(const Napi::CallbackInfo &info)
780
811
 
781
812
  DisposeFunc *dispose;
782
813
  Napi::Function dispose_func;
783
- if (info.Length() >= 2u + skip && !IsNullOrUndefined(info[1 + skip])) {
814
+ if (info.Length() >= 2u + skip && !IsNullOrUndefined(env, info[1 + skip])) {
784
815
  Napi::Function func = info[1 + skip].As<Napi::Function>();
785
816
 
786
817
  if (!func.IsFunction()) {
@@ -792,13 +823,10 @@ static Napi::Value CreateDisposableType(const Napi::CallbackInfo &info)
792
823
  InstanceData *instance = env.GetInstanceData<InstanceData>();
793
824
  const Napi::FunctionReference &ref = type->dispose_ref;
794
825
 
795
- Napi::External<void> external = Napi::External<void>::New(env, (void *)ptr);
796
- SetValueTag(external, type->ref.marker);
826
+ Napi::Value p = WrapPointer(env, type->ref.type, (void *)ptr);
797
827
 
798
- Napi::Value self = env.Null();
799
- napi_value args[] = {
800
- external
801
- };
828
+ napi_value self = env.Null();
829
+ napi_value args[] = { p };
802
830
 
803
831
  ref.Call(self, K_LEN(args), args);
804
832
  instance->stats.disposed++;
@@ -826,37 +854,30 @@ static Napi::Value CreateDisposableType(const Napi::CallbackInfo &info)
826
854
  type->dispose_ref = Napi::Persistent(dispose_func);
827
855
 
828
856
  // If the insert succeeds, we cannot fail anymore
829
- if (named && !MapType(env, instance, type, type->name))
830
- return env.Null();
857
+ if (named) {
858
+ bool inserted;
859
+ instance->types_map.InsertOrGet(type->name, type, &inserted);
860
+
861
+ if (!inserted) {
862
+ ThrowError<Napi::Error>(env, "Duplicate type name '%1'", type->name);
863
+ return env.Null();
864
+ }
865
+ }
831
866
  err_guard.Disable();
832
867
 
833
868
  return WrapType(env, type);
834
869
  }
835
870
 
836
- static inline bool GetExternalPointer(Napi::Env env, Napi::Value value, void **out_ptr)
871
+ static inline bool GetExternalPointer(napi_env env, napi_value value, void **out_ptr)
837
872
  {
838
- InstanceData *instance = env.GetInstanceData<InstanceData>();
839
-
840
- if (IsNullOrUndefined(value)) {
841
- *out_ptr = 0;
842
- return true;
843
- } else if (IsRawBuffer(value)) {
844
- Span<uint8_t> buffer = GetRawBuffer(value);
873
+ if (!TryPointer(env, value, out_ptr)) {
874
+ InstanceData *instance = Napi::Env(env).GetInstanceData<InstanceData>();
845
875
 
846
- *out_ptr = buffer.ptr;
847
- return true;
848
- } else if (value.IsExternal() && !CheckValueTag(value, &TypeInfoMarker) &&
849
- !CheckValueTag(value, &CastMarker) &&
850
- !CheckValueTag(value, &MagicUnionMarker)) {
851
- Napi::External<void> external = value.As<Napi::External<void>>();
852
- void *ptr = external.Data();
853
-
854
- *out_ptr = ptr;
855
- return true;
856
- } else {
857
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value for ptr, expected external pointer", GetValueType(instance, value));
876
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for ptr, expected pointer", GetValueType(instance, value));
858
877
  return false;
859
878
  }
879
+
880
+ return true;
860
881
  }
861
882
 
862
883
  static Napi::Value CallAlloc(const Napi::CallbackInfo &info)
@@ -902,10 +923,7 @@ static Napi::Value CallAlloc(const Napi::CallbackInfo &info)
902
923
  return env.Null();
903
924
  }
904
925
 
905
- Napi::External<void> external = Napi::External<void>::New(env, ptr);
906
- SetValueTag(external, type);
907
-
908
- return external;
926
+ return WrapPointer(env, type, ptr);
909
927
  }
910
928
 
911
929
  static Napi::Value CallFree(const Napi::CallbackInfo &info)
@@ -942,8 +960,7 @@ static Napi::Value GetOrSetErrNo(const Napi::CallbackInfo &info)
942
960
  errno = value;
943
961
  }
944
962
 
945
- Napi::Number ret = Napi::Number::New(env, errno);
946
- return ret;
963
+ return NewInt(env, (int32_t)errno);
947
964
  }
948
965
 
949
966
  static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
@@ -988,7 +1005,7 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
988
1005
 
989
1006
  TypeInfo *type = nullptr;
990
1007
 
991
- if (info.Length() >= 3u + dynamic && !IsNullOrUndefined(info[2 + dynamic])) {
1008
+ if (info.Length() >= 3u + dynamic && !IsNullOrUndefined(env, info[2 + dynamic])) {
992
1009
  if (!info[2 + dynamic].IsString()) {
993
1010
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for hint, expected string", GetValueType(instance, info[2]));
994
1011
  return env.Null();
@@ -1008,7 +1025,7 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
1008
1025
  hint = ArrayHint::Array;
1009
1026
  } else if (str == "String" || str == "string") {
1010
1027
  if (ref->primitive != PrimitiveKind::Int8 && ref->primitive != PrimitiveKind::Int16) {
1011
- ThrowError<Napi::Error>(env, "Array hint 'String' can only be used with 8 and 16-bit signed integer types");
1028
+ ThrowError<Napi::Error>(env, "Array hint 'String' can only be used with 8, 16 and 32-bit signed integer types");
1012
1029
  return env.Null();
1013
1030
  }
1014
1031
 
@@ -1018,9 +1035,9 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
1018
1035
  return env.Null();
1019
1036
  }
1020
1037
 
1021
- type = MakeArrayType(instance, ref, len, hint);
1038
+ type = MakeArrayType(instance, ref, (Size)len, hint);
1022
1039
  } else {
1023
- type = MakeArrayType(instance, ref, len);
1040
+ type = MakeArrayType(instance, ref, (Size)len);
1024
1041
  }
1025
1042
 
1026
1043
  if (dynamic) {
@@ -1064,7 +1081,7 @@ static bool ParseClassicFunction(const Napi::CallbackInfo &info, bool concrete,
1064
1081
  }
1065
1082
  #endif
1066
1083
  if (!name.IsString()) {
1067
- if (!concrete && IsNullOrUndefined(name)) {
1084
+ if (!concrete && IsNullOrUndefined(env, name)) {
1068
1085
  named = false;
1069
1086
  } else {
1070
1087
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for name, expected string or integer", GetValueType(instance, name));
@@ -1118,10 +1135,6 @@ static bool ParseClassicFunction(const Napi::CallbackInfo &info, bool concrete,
1118
1135
  ThrowError<Napi::TypeError>(env, "Functions cannot have more than %1 parameters", MaxParameters);
1119
1136
  return false;
1120
1137
  }
1121
- if ((param.directions & 2) && ++out_func->out_parameters >= MaxParameters) {
1122
- ThrowError<Napi::TypeError>(env, "Functions cannot have more than %1 output parameters", MaxParameters);
1123
- return false;
1124
- }
1125
1138
 
1126
1139
  param.offset = (int8_t)j;
1127
1140
 
@@ -1139,12 +1152,10 @@ static Napi::Value CreateFunctionType(const Napi::CallbackInfo &info)
1139
1152
  InstanceData *instance = env.GetInstanceData<InstanceData>();
1140
1153
 
1141
1154
  FunctionInfo *func = instance->callbacks.AppendDefault();
1142
- TypeInfo *type = instance->types.AppendDefault();
1155
+ K_DEFER_N(err_guard) { instance->callbacks.RemoveLast(1); };
1143
1156
 
1144
- K_DEFER_N(err_guard) {
1145
- instance->callbacks.RemoveLast(1);
1146
- instance->types.RemoveLast(1);
1147
- };
1157
+ func->env = env;
1158
+ func->instance = instance;
1148
1159
 
1149
1160
  if (info.Length() >= 2) {
1150
1161
  if (!ParseClassicFunction(info, false, func))
@@ -1172,11 +1183,14 @@ static Napi::Value CreateFunctionType(const Napi::CallbackInfo &info)
1172
1183
  if (!AnalyseFunction(env, instance, func))
1173
1184
  return env.Null();
1174
1185
 
1175
- // Adjust parameter offsets for koffi.call()
1176
- for (ParameterInfo &param: func->parameters) {
1177
- param.offset += 2;
1186
+ // We cannot fail after this check
1187
+ if (named && instance->types_map.Find(func->name)) {
1188
+ ThrowError<Napi::Error>(env, "Duplicate type name '%1'", func->name);
1189
+ return env.Null();
1178
1190
  }
1179
- func->required_parameters += 2;
1191
+ err_guard.Disable();
1192
+
1193
+ TypeInfo *type = instance->types.AppendDefault();
1180
1194
 
1181
1195
  type->name = func->name;
1182
1196
 
@@ -1185,10 +1199,7 @@ static Napi::Value CreateFunctionType(const Napi::CallbackInfo &info)
1185
1199
  type->size = K_SIZE(void *);
1186
1200
  type->ref.proto = func;
1187
1201
 
1188
- // If the insert succeeds, we cannot fail anymore
1189
- if (named && !MapType(env, instance, type, type->name))
1190
- return env.Null();
1191
- err_guard.Disable();
1202
+ instance->types_map.Set(type->name, type);
1192
1203
 
1193
1204
  return WrapType(env, type);
1194
1205
  }
@@ -1234,7 +1245,7 @@ static Napi::Value GetTypeSize(const Napi::CallbackInfo &info)
1234
1245
  if (!type)
1235
1246
  return env.Null();
1236
1247
 
1237
- return Napi::Number::New(env, type->size);
1248
+ return NewInt(env, type->size);
1238
1249
  }
1239
1250
 
1240
1251
  static Napi::Value GetTypeAlign(const Napi::CallbackInfo &info)
@@ -1250,7 +1261,7 @@ static Napi::Value GetTypeAlign(const Napi::CallbackInfo &info)
1250
1261
  if (!type)
1251
1262
  return env.Null();
1252
1263
 
1253
- return Napi::Number::New(env, type->align);
1264
+ return NewInt(env, type->align);
1254
1265
  }
1255
1266
 
1256
1267
  static Napi::Value GetMemberOffset(const Napi::CallbackInfo &info)
@@ -1284,7 +1295,7 @@ static Napi::Value GetMemberOffset(const Napi::CallbackInfo &info)
1284
1295
  return env.Null();
1285
1296
  }
1286
1297
 
1287
- return Napi::Number::New(env, member->offset);
1298
+ return NewInt(env, member->offset);
1288
1299
  }
1289
1300
 
1290
1301
  static Napi::Value GetResolvedType(const Napi::CallbackInfo &info)
@@ -1321,8 +1332,8 @@ static Napi::Value GetTypeDefinition(const Napi::CallbackInfo &info)
1321
1332
 
1322
1333
  defn.Set("name", Napi::String::New(env, type->name));
1323
1334
  defn.Set("primitive", PrimitiveKindNames[(int)type->primitive]);
1324
- defn.Set("size", Napi::Number::New(env, (double)type->size));
1325
- defn.Set("alignment", Napi::Number::New(env, (double)type->align));
1335
+ defn.Set("size", NewInt(env, type->size));
1336
+ defn.Set("alignment", NewInt(env, type->align));
1326
1337
  defn.Set("disposable", Napi::Boolean::New(env, !!type->dispose));
1327
1338
 
1328
1339
  switch (type->primitive) {
@@ -1352,7 +1363,7 @@ static Napi::Value GetTypeDefinition(const Napi::CallbackInfo &info)
1352
1363
 
1353
1364
  case PrimitiveKind::Array: {
1354
1365
  uint32_t len = type->size / type->ref.type->size;
1355
- defn.Set("length", Napi::Number::New(env, (double)len));
1366
+ defn.Set("length", NewInt(env, len));
1356
1367
  defn.Set("hint", ArrayHintNames[(int)type->hint]);
1357
1368
  } [[fallthrough]];
1358
1369
  case PrimitiveKind::Pointer: {
@@ -1412,44 +1423,48 @@ InstanceMemory *AllocateMemory(InstanceData *instance, Size stack_size, Size hea
1412
1423
 
1413
1424
  #if defined(_WIN32)
1414
1425
  // Allocate stack memory
1415
- mem->stack.len = stack_size;
1416
- mem->stack.ptr = (uint8_t *)VirtualAlloc(nullptr, mem->stack.len, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
1426
+ mem->stack.ptr = (uint8_t *)VirtualAlloc(nullptr, stack_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
1427
+ mem->stack.end = mem->stack.ptr + stack_size;
1417
1428
 
1418
- K_CRITICAL(mem->stack.ptr, "Failed to allocate %1 of memory", mem->stack.len);
1429
+ K_CRITICAL(mem->stack.ptr, "Failed to allocate %1 of memory", stack_size);
1419
1430
  #else
1420
- mem->stack.len = stack_size;
1421
- mem->stack.ptr = (uint8_t *)mmap(nullptr, mem->stack.len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_STACK, -1, 0);
1431
+ mem->stack.ptr = (uint8_t *)mmap(nullptr, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_STACK, -1, 0);
1432
+ mem->stack.end = mem->stack.ptr + stack_size;
1422
1433
 
1423
- K_CRITICAL(mem->stack.ptr, "Failed to allocate %1 of memory", mem->stack.len);
1434
+ K_CRITICAL(mem->stack.ptr, "Failed to allocate %1 of memory", stack_size);
1424
1435
  #endif
1425
1436
 
1426
1437
  #if defined(__OpenBSD__)
1427
1438
  // Make sure the SP points inside the MAP_STACK area, or (void) functions may crash on OpenBSD i386
1428
- mem->stack.len -= 16;
1439
+ mem->stack.end -= 16;
1429
1440
  #endif
1430
1441
 
1431
1442
  // Keep real stack limits intact, in case we need them
1432
1443
  mem->stack0 = mem->stack;
1433
1444
 
1434
1445
  #if defined(_WIN32) && !defined(_WIN64)
1435
- mem->stack.len -= K_SIZE(SehFrame);
1446
+ mem->stack.end -= K_SIZE(SehFrame);
1436
1447
 
1437
1448
  // Prepare at the top SEH frame record
1438
1449
  {
1439
- SehFrame *seh = (SehFrame *)mem->stack.end();
1450
+ SehFrame *seh = (SehFrame *)mem->stack.end;
1440
1451
 
1441
1452
  seh->Next = (void *)-1;
1442
1453
  seh->Handler = (void *)SehHandler;
1443
1454
  }
1444
1455
  #endif
1445
1456
 
1446
- mem->heap.len = heap_size;
1447
1457
  #if defined(_WIN32)
1448
- mem->heap.ptr = (uint8_t *)VirtualAlloc(nullptr, mem->heap.len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
1458
+ mem->heap.ptr = (uint8_t *)VirtualAlloc(nullptr, heap_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
1459
+ mem->heap.end = mem->heap.ptr + heap_size;
1460
+
1461
+ K_CRITICAL(mem->heap.ptr, "Failed to allocate %1 of memory", heap_size);
1449
1462
  #else
1450
- mem->heap.ptr = (uint8_t *)mmap(nullptr, mem->heap.len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
1463
+ mem->heap.ptr = (uint8_t *)mmap(nullptr, heap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
1464
+ mem->heap.end = mem->heap.ptr + heap_size;
1465
+
1466
+ K_CRITICAL(mem->heap.ptr, "Failed to allocate %1 of memory", heap_size);
1451
1467
  #endif
1452
- K_CRITICAL(mem->heap.ptr, "Failed to allocate %1 of memory", mem->heap.len);
1453
1468
 
1454
1469
  if (temporary) {
1455
1470
  instance->temporaries++;
@@ -1467,13 +1482,6 @@ InstanceMemory *AllocateMemory(InstanceData *instance, Size stack_size, Size hea
1467
1482
 
1468
1483
  void ReleaseMemory(InstanceData *instance, InstanceMemory *mem)
1469
1484
  {
1470
- if (--mem->depth)
1471
- return;
1472
-
1473
- // The first InstanceMemory is used for sync calls, no need to manage the async stuff
1474
- if (mem == instance->memories[0])
1475
- return;
1476
-
1477
1485
  std::lock_guard<std::mutex> lock(instance->mem_mutex);
1478
1486
 
1479
1487
  if (mem->temporary) {
@@ -1484,164 +1492,282 @@ void ReleaseMemory(InstanceData *instance, InstanceMemory *mem)
1484
1492
  }
1485
1493
  }
1486
1494
 
1487
- static Napi::Value TranslateNormalCall(const FunctionInfo *func, void *native,
1488
- const Napi::CallbackInfo &info)
1495
+ static napi_value TranslateZeroCallNode(napi_env env, napi_callback_info info)
1489
1496
  {
1490
- Napi::Env env = info.Env();
1491
- InstanceData *instance = env.GetInstanceData<InstanceData>();
1497
+ struct CallbackBundle {
1498
+ napi_env env;
1499
+ void *data;
1500
+ };
1492
1501
 
1493
- if (info.Length() < (uint32_t)func->required_parameters) [[unlikely]] {
1494
- ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len, info.Length());
1495
- return env.Null();
1496
- }
1502
+ CallbackBundle **ptr = (CallbackBundle **)((uint8_t *)info + K_SIZE(void *));
1503
+ FunctionInfo *func = (FunctionInfo *)(*ptr)->data;
1497
1504
 
1505
+ InstanceData *instance = func->instance;
1498
1506
  InstanceMemory *mem = instance->memories[0];
1499
- CallData call(env, instance, mem);
1507
+ CallData call(env, instance, mem, func->native);
1500
1508
 
1501
- if (!call.Prepare(func, info)) [[unlikely]]
1502
- return env.Null();
1509
+ K_DEFER_C(prev_call = instance->sync_call) { instance->sync_call = prev_call; };
1510
+ instance->sync_call = &call;
1503
1511
 
1504
- if (instance->debug) {
1505
- call.DumpForward(func);
1506
- }
1512
+ napi_value ret = call.Run(func, nullptr);
1513
+ call.FinalizeFast();
1507
1514
 
1508
- // Execute call
1509
- {
1510
- K_DEFER_C(prev_call = exec_call) { exec_call = prev_call; };
1511
- exec_call = &call;
1515
+ return ret;
1516
+ }
1517
+
1518
+ static napi_value TranslateZeroCallBun(napi_env env, napi_callback_info info)
1519
+ {
1520
+ FunctionInfo *func = *(FunctionInfo **)((uint8_t *)info + K_SIZE(void *));
1521
+
1522
+ InstanceData *instance = func->instance;
1523
+ InstanceMemory *mem = instance->memories[0];
1524
+ CallData call(env, instance, mem, func->native);
1525
+
1526
+ K_DEFER_C(prev_call = instance->sync_call) { instance->sync_call = prev_call; };
1527
+ instance->sync_call = &call;
1528
+
1529
+ napi_value ret = call.Run(func, nullptr);
1530
+ call.FinalizeFast();
1531
+
1532
+ return ret;
1533
+ }
1534
+
1535
+ napi_value TranslateFastCall(napi_env env, napi_callback_info info)
1536
+ {
1537
+ napi_value args[6];
1538
+ size_t count = 6;
1539
+ FunctionInfo *func;
1540
+
1541
+ napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, (void **)&func);
1542
+ K_ASSERT(status == napi_ok);
1543
+
1544
+ InstanceData *instance = func->instance;
1512
1545
 
1513
- call.Execute(func, native);
1546
+ if (count < (size_t)func->required_parameters) [[unlikely]] {
1547
+ ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len, count);
1548
+ return Napi::Env(env).Null();
1514
1549
  }
1515
1550
 
1516
- return call.Complete(func);
1551
+ InstanceMemory *mem = instance->memories[0];
1552
+ CallData call(env, instance, mem, func->native);
1553
+
1554
+ K_DEFER_C(prev_call = instance->sync_call) { instance->sync_call = prev_call; };
1555
+ instance->sync_call = &call;
1556
+
1557
+ napi_value ret = call.Run(func, args);
1558
+ call.FinalizeFast();
1559
+
1560
+ return ret;
1517
1561
  }
1518
1562
 
1519
- Napi::Value TranslateNormalCall(const Napi::CallbackInfo &info)
1563
+ static FORCE_INLINE napi_value TranslateNormalCall(napi_env env, const FunctionInfo *func, void *native, napi_value *args, Size count)
1520
1564
  {
1521
- FunctionInfo *func = (FunctionInfo *)info.Data();
1522
- return TranslateNormalCall(func, func->native, info);
1565
+ static_assert(MaxParameters >= 6);
1566
+
1567
+ InstanceData *instance = func->instance;
1568
+
1569
+ if (count < func->required_parameters) [[unlikely]] {
1570
+ ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len, count);
1571
+ return Napi::Env(env).Null();
1572
+ }
1573
+
1574
+ InstanceMemory *mem = instance->memories[0];
1575
+ CallData call(env, instance, mem, native);
1576
+
1577
+ K_DEFER_C(prev_call = instance->sync_call) { instance->sync_call = prev_call; };
1578
+ instance->sync_call = &call;
1579
+
1580
+ napi_value ret = call.Run(func, args);
1581
+ call.Finalize();
1582
+
1583
+ return ret;
1523
1584
  }
1524
1585
 
1525
- static Napi::Value TranslateVariadicCall(const FunctionInfo *func, void *native,
1526
- const Napi::CallbackInfo &info)
1586
+ napi_value TranslateNormalCall(napi_env env, napi_callback_info info)
1527
1587
  {
1528
- Napi::Env env = info.Env();
1529
- InstanceData *instance = env.GetInstanceData<InstanceData>();
1588
+ napi_value args[MaxParameters];
1589
+ size_t count = 6;
1590
+ FunctionInfo *func;
1530
1591
 
1531
- FunctionInfo copy;
1532
- memcpy((void *)&copy, func, K_SIZE(*func));
1533
- copy.lib = nullptr;
1592
+ napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, (void **)&func);
1593
+ K_ASSERT(status == napi_ok);
1534
1594
 
1535
- // This makes variadic calls non-reentrant
1536
- K_DEFER_C(len = copy.parameters.len) {
1537
- copy.parameters.RemoveFrom(len);
1538
- copy.parameters.Leak();
1539
- };
1595
+ if (count > 6) {
1596
+ napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, nullptr);
1597
+ K_ASSERT(status == napi_ok);
1540
1598
 
1541
- if (info.Length() < (uint32_t)copy.required_parameters) [[unlikely]] {
1542
- ThrowError<Napi::TypeError>(env, "Expected %1 arguments or more, got %2", copy.parameters.len, info.Length());
1543
- return env.Null();
1544
- }
1545
- if ((info.Length() - copy.required_parameters) % 2) [[unlikely]] {
1546
- ThrowError<Napi::Error>(env, "Missing value argument for variadic call");
1547
- return env.Null();
1599
+ count = std::min(count, (size_t)MaxParameters);
1548
1600
  }
1549
1601
 
1550
- for (Size i = copy.required_parameters; i < (Size)info.Length(); i += 2) {
1551
- ParameterInfo param = {};
1602
+ return TranslateNormalCall(env, func, func->native, args, (Size)count);
1603
+ }
1552
1604
 
1553
- param.type = ResolveType(info[(uint32_t)i], &param.directions);
1605
+ static napi_value TranslateVariadicCall(napi_env env, const FunctionInfo *func, void *native, napi_value *args, Size count)
1606
+ {
1607
+ static_assert(MaxParameters >= 6);
1554
1608
 
1555
- if (!param.type) [[unlikely]]
1556
- return env.Null();
1557
- if (!CanPassType(param.type, param.directions)) [[unlikely]] {
1558
- ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a parameter", param.type->name);
1559
- return env.Null();
1609
+ InstanceData *instance = func->instance;
1610
+
1611
+ FunctionInfo *variadic = nullptr;
1612
+ K_DEFER_N(err_guard) { delete variadic; };
1613
+
1614
+ // Try cached function
1615
+ {
1616
+ FunctionInfo *prev = instance->variadic_func;
1617
+
1618
+ if (prev && prev->native == native) {
1619
+ Size specified = (count - prev->required_parameters);
1620
+ Size processed = (prev->parameters.len - prev->required_parameters) * 2;
1621
+
1622
+ if (specified == processed) {
1623
+ bool match = true;
1624
+
1625
+ for (Size i = prev->required_parameters, j = prev->required_parameters; i < (Size)count; i += 2, j++) {
1626
+ int directions;
1627
+ const TypeInfo *type = ResolveType(Napi::Value(env, args[i]), &directions);
1628
+
1629
+ if (type != prev->parameters[j].type || directions != prev->parameters[j].directions) [[unlikely]] {
1630
+ match = false;
1631
+ break;
1632
+ }
1633
+ }
1634
+
1635
+ if (match) [[likely]] {
1636
+ variadic = prev;
1637
+
1638
+ // If an error happens it'll get destroyed, so don't keep it around
1639
+ instance->variadic_func = nullptr;
1640
+ }
1641
+ }
1560
1642
  }
1561
- if (copy.parameters.len >= MaxParameters) [[unlikely]] {
1562
- ThrowError<Napi::TypeError>(env, "Functions cannot have more than %1 parameters", MaxParameters);
1563
- return env.Null();
1643
+ }
1644
+
1645
+ if (!variadic) {
1646
+ variadic = new FunctionInfo();
1647
+
1648
+ memcpy((void *)variadic, func, K_SIZE(*func));
1649
+ memset((void *)&variadic->parameters, 0, K_SIZE(variadic->parameters));
1650
+ memset((void *)&variadic->sync, 0, K_SIZE(variadic->sync));
1651
+ memset((void *)&variadic->async, 0, K_SIZE(variadic->async));
1652
+
1653
+ variadic->parameters = func->parameters;
1654
+ variadic->lib = nullptr;
1655
+
1656
+ if (count < variadic->required_parameters) [[unlikely]] {
1657
+ ThrowError<Napi::TypeError>(env, "Expected %1 arguments or more, got %2", variadic->parameters.len, count);
1658
+ return Napi::Env(env).Null();
1564
1659
  }
1565
- if ((param.directions & 2) && ++copy.out_parameters >= MaxParameters) [[unlikely]] {
1566
- ThrowError<Napi::TypeError>(env, "Functions cannot have more than %1 output parameters", MaxParameters);
1567
- return env.Null();
1660
+ if ((count - variadic->required_parameters) % 2) [[unlikely]] {
1661
+ ThrowError<Napi::Error>(env, "Missing value argument for variadic call");
1662
+ return Napi::Env(env).Null();
1568
1663
  }
1569
1664
 
1570
- param.variadic = true;
1571
- param.offset = (int8_t)(i + 1);
1665
+ for (Size i = variadic->required_parameters; i < count; i += 2) {
1666
+ ParameterInfo param = {};
1572
1667
 
1573
- copy.parameters.Append(param);
1574
- }
1668
+ param.type = ResolveType(Napi::Value(env, args[i]), &param.directions);
1575
1669
 
1576
- if (!AnalyseFunction(env, instance, &copy)) [[unlikely]]
1577
- return env.Null();
1670
+ if (!param.type) [[unlikely]]
1671
+ return Napi::Env(env).Null();
1672
+ if (!CanPassType(param.type, param.directions)) [[unlikely]] {
1673
+ ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a parameter", param.type->name);
1674
+ return Napi::Env(env).Null();
1675
+ }
1676
+ if (variadic->parameters.len >= MaxParameters) [[unlikely]] {
1677
+ ThrowError<Napi::TypeError>(env, "Functions cannot have more than %1 parameters", MaxParameters);
1678
+ return Napi::Env(env).Null();
1679
+ }
1578
1680
 
1579
- InstanceMemory *mem = instance->memories[0];
1580
- CallData call(env, instance, mem);
1681
+ param.variadic = true;
1682
+ param.offset = (int8_t)(i + 1);
1581
1683
 
1582
- if (!call.Prepare(&copy, info)) [[unlikely]]
1583
- return env.Null();
1684
+ variadic->parameters.Append(param);
1685
+ }
1584
1686
 
1585
- if (instance->debug) {
1586
- call.DumpForward(&copy);
1687
+ if (!AnalyseFunction(env, instance, variadic)) [[unlikely]]
1688
+ return Napi::Env(env).Null();
1587
1689
  }
1588
1690
 
1589
- // Execute call
1590
- {
1591
- K_DEFER_C(prev_call = exec_call) { exec_call = prev_call; };
1592
- exec_call = &call;
1691
+ InstanceMemory *mem = instance->memories[0];
1692
+ CallData call(env, instance, mem, native);
1693
+
1694
+ K_DEFER_C(prev_call = instance->sync_call) { instance->sync_call = prev_call; };
1695
+ instance->sync_call = &call;
1696
+
1697
+ napi_value ret = call.Run(variadic, args);
1698
+ call.Finalize();
1593
1699
 
1594
- call.Execute(&copy, native);
1700
+ if (variadic != instance->variadic_func) {
1701
+ err_guard.Disable();
1702
+
1703
+ delete instance->variadic_func;
1704
+ instance->variadic_func = variadic;
1595
1705
  }
1596
1706
 
1597
- return call.Complete(&copy);
1707
+ return ret;
1598
1708
  }
1599
1709
 
1600
- Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
1710
+ napi_value TranslateVariadicCall(napi_env env, napi_callback_info info)
1601
1711
  {
1602
- FunctionInfo *func = (FunctionInfo *)info.Data();
1603
- return TranslateVariadicCall(func, func->native, info);
1712
+ napi_value args[MaxParameters];
1713
+ size_t count = 6;
1714
+ FunctionInfo *func;
1715
+
1716
+ napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, (void **)&func);
1717
+ K_ASSERT(status == napi_ok);
1718
+
1719
+ if (count > 6) {
1720
+ napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, nullptr);
1721
+ K_ASSERT(status == napi_ok);
1722
+
1723
+ count = std::min(count, (size_t)MaxParameters);
1724
+ }
1725
+
1726
+ return TranslateVariadicCall(env, func, func->native, args, (Size)count);
1604
1727
  }
1605
1728
 
1606
1729
  class AsyncCall: public Napi::AsyncWorker {
1607
1730
  Napi::Env env;
1608
1731
 
1609
1732
  const FunctionInfo *func;
1610
- void *native;
1733
+ NoDestroy<CallData> call;
1611
1734
 
1612
- CallData call;
1613
1735
  bool prepared = false;
1614
1736
 
1615
1737
  public:
1616
- AsyncCall(Napi::Env env, InstanceData *instance, const FunctionInfo *func,
1617
- void *native, InstanceMemory *mem, Napi::Function &callback)
1618
- : Napi::AsyncWorker(callback), env(env), func(func->Ref()), native(native),
1619
- call(env, instance, mem) {}
1620
- ~AsyncCall() { func->Unref(); }
1738
+ AsyncCall(Napi::Env env, InstanceData *instance, InstanceMemory *mem, const FunctionInfo *func, Napi::Function &callback)
1739
+ : Napi::AsyncWorker(callback), env(env), func(func->Ref()), call(env, instance, mem, func->native) {}
1740
+ ~AsyncCall();
1621
1741
 
1622
- bool Prepare(const Napi::CallbackInfo &info) {
1623
- prepared = call.Prepare(func, info);
1742
+ bool Prepare(napi_value *args) {
1743
+ prepared = call->PrepareAsync(func, args);
1624
1744
 
1625
- if (!prepared) {
1745
+ if (!prepared) [[unlikely]] {
1626
1746
  Napi::Error err = env.GetAndClearPendingException();
1627
1747
  SetError(err.Message());
1628
1748
  }
1629
1749
 
1630
1750
  return prepared;
1631
1751
  }
1632
- void DumpForward() { call.DumpForward(func); }
1633
1752
 
1634
1753
  void Execute() override;
1635
1754
  void OnOK() override;
1755
+ void OnError(const Napi::Error& err) override;
1636
1756
  };
1637
1757
 
1638
- void AsyncCall::Execute()
1758
+ AsyncCall::~AsyncCall()
1639
1759
  {
1640
- if (prepared) {
1641
- K_DEFER_C(prev_call = exec_call) { exec_call = prev_call; };
1642
- exec_call = &call;
1760
+ #if defined(K_DEBUG)
1761
+ call->~CallData();
1762
+ #endif
1763
+
1764
+ ReleaseMemory(call->instance, call->mem);
1765
+ }
1643
1766
 
1644
- call.Execute(func, native);
1767
+ void AsyncCall::Execute()
1768
+ {
1769
+ if (prepared) [[likely]] {
1770
+ call->ExecuteAsync();
1645
1771
  }
1646
1772
  }
1647
1773
 
@@ -1651,90 +1777,138 @@ void AsyncCall::OnOK()
1651
1777
 
1652
1778
  Napi::FunctionReference &callback = Callback();
1653
1779
 
1654
- Napi::Value self = env.Null();
1655
- napi_value args[] = {
1656
- env.Null(),
1657
- call.Complete(func)
1658
- };
1780
+ napi_value ret = call->EndAsync();
1781
+ call->Finalize();
1782
+
1783
+ napi_value self = env.Null();
1784
+ napi_value args[] = { env.Null(), ret };
1659
1785
 
1660
1786
  callback.Call(self, K_LEN(args), args);
1661
1787
  }
1662
1788
 
1663
- static Napi::Value TranslateAsyncCall(const FunctionInfo *func, void *native,
1664
- const Napi::CallbackInfo &info)
1789
+ void AsyncCall::OnError(const Napi::Error& err)
1665
1790
  {
1666
- K_ASSERT(!func->variadic);
1791
+ Napi::FunctionReference &callback = Callback();
1667
1792
 
1668
- Napi::Env env = info.Env();
1669
- InstanceData *instance = env.GetInstanceData<InstanceData>();
1793
+ call->Finalize();
1670
1794
 
1671
- if (info.Length() <= (uint32_t)func->required_parameters) {
1672
- ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len + 1, info.Length());
1673
- return env.Null();
1795
+ napi_value self = env.Null();
1796
+ napi_value args[] = { err.Value(), env.Undefined() };
1797
+
1798
+ callback.Call(self, K_LEN(args), args);
1799
+ }
1800
+
1801
+ napi_value TranslateAsyncCall(napi_env env, napi_callback_info info)
1802
+ {
1803
+ static_assert(MaxParameters >= 6);
1804
+
1805
+ napi_value args[MaxParameters];
1806
+ size_t count = 6;
1807
+ FunctionInfo *func;
1808
+
1809
+ napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, (void **)&func);
1810
+ K_ASSERT(status == napi_ok);
1811
+
1812
+ if (count > 6) {
1813
+ napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, nullptr);
1814
+ K_ASSERT(status == napi_ok);
1815
+
1816
+ count = std::min(count, (size_t)MaxParameters);
1817
+ }
1818
+
1819
+ InstanceData *instance = func->instance;
1820
+
1821
+ if (count <= (size_t)func->required_parameters) {
1822
+ ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->required_parameters + 1, count);
1823
+ return Napi::Env(env).Null();
1674
1824
  }
1675
1825
 
1676
- Napi::Function callback = info[(uint32_t)func->required_parameters].As<Napi::Function>();
1826
+ Napi::Function callback = Napi::Value(env, args[func->required_parameters]).As<Napi::Function>();
1677
1827
 
1678
1828
  if (!callback.IsFunction()) {
1679
1829
  ThrowError<Napi::TypeError>(env, "Expected callback function as last argument, got %1", GetValueType(instance, callback));
1680
- return env.Null();
1830
+ return Napi::Env(env).Null();
1681
1831
  }
1682
1832
 
1683
1833
  InstanceMemory *mem = AllocateMemory(instance, instance->config.async_stack_size, instance->config.async_heap_size);
1684
1834
  if (!mem) [[unlikely]] {
1685
1835
  ThrowError<Napi::Error>(env, "Too many asynchronous calls are running");
1686
- return env.Null();
1836
+ return Napi::Env(env).Null();
1687
1837
  }
1688
- AsyncCall *async = new AsyncCall(env, instance, func, native, mem, callback);
1838
+ AsyncCall *async = new AsyncCall(env, instance, mem, func, callback);
1689
1839
 
1690
- if (async->Prepare(info) && instance->debug) {
1691
- async->DumpForward();
1692
- }
1840
+ async->Prepare(args);
1693
1841
  async->Queue();
1694
1842
 
1695
- return env.Undefined();
1843
+ return Napi::Env(env).Undefined();
1696
1844
  }
1697
1845
 
1698
- Napi::Value TranslateAsyncCall(const Napi::CallbackInfo &info)
1846
+ static FORCE_INLINE bool CheckTrampolineStatus(TrampolineInfo *trampoline)
1699
1847
  {
1700
- FunctionInfo *func = (FunctionInfo *)info.Data();
1701
- return TranslateAsyncCall(func, func->native, info);
1848
+ if (trampoline->state == 1) [[likely]]
1849
+ return true;
1850
+
1851
+ Napi::Env env = trampoline->func.Env();
1852
+
1853
+ if (!trampoline->state) {
1854
+ ThrowError<Napi::Error>(env, "Cannot use non-registered callback beyond FFI call");
1855
+ trampoline->state = -1;
1856
+ }
1857
+
1858
+ // We use trampoline->state < 0 as a signal than an exception has occured, because we want
1859
+ // to avoid calling IsExceptionPending() in the happy path. But trampolines get reused, so
1860
+ // there might not be an exception anymore!
1861
+ if (!env.IsExceptionPending()) {
1862
+ trampoline->state = 1;
1863
+ return true;
1864
+ }
1865
+
1866
+ return false;
1702
1867
  }
1703
1868
 
1704
1869
  extern "C" void RelayCallback(Size idx, uint8_t *sp)
1705
1870
  {
1706
- CallData *call = exec_call;
1871
+ TrampolineInfo *trampoline = &shared.trampolines[idx];
1872
+ InstanceData *instance = trampoline->instance;
1707
1873
 
1708
- // Try the fast path first: we are on the main thread and we are running a native call through Koffi.
1709
- // It should be easy, but in this case we are running on the custom Koffi stack, which will trip up
1710
- // Node and V8. So we need to switch back to the normal/main stack.
1711
- if (call && std::this_thread::get_id() == call->instance->main_thread_id) {
1712
- SwitchAndRelay(call, idx, sp, call->old_sp, &call->mem->stack);
1874
+ // Fast path: main thread and we are running a native call through Koffi.
1875
+ // But this means we are running on the custom Koffi stack, which could trip up
1876
+ // Node and V8, so we need to stwich back to the normal/main stack.
1877
+ if (InstanceMemory *mem0 = instance->memories[0]; sp >= mem0->stack0.ptr && sp <= mem0->stack0.end) {
1878
+ CallData *call = instance->sync_call;
1879
+
1880
+ SwitchAndRelay(call, idx, sp, call->saved_sp, &call->mem->stack);
1713
1881
  return;
1714
1882
  }
1715
1883
 
1884
+ if (!CheckTrampolineStatus(trampoline))
1885
+ return;
1886
+
1716
1887
  // Otherwise, we need to allocate memory to perform the callback.
1717
1888
  // Since the necessary machinery live in CallData, just use a temporary instance.
1718
- // In some cases we would reuse the existing call (exec_call may be not null),
1719
- // but it is rare so let's ignore this for simplicity.
1889
+ // In some cases we would reuse the existing call, but it is rare so let's ignore
1890
+ // this for simplicity.
1720
1891
 
1721
- TrampolineInfo *trampoline = &shared.trampolines[idx];
1722
1892
  Napi::Env env = trampoline->func.Env();
1723
- InstanceData *instance = env.GetInstanceData<InstanceData>();
1724
1893
 
1725
1894
  InstanceMemory *mem = AllocateMemory(instance, instance->config.async_stack_size, instance->config.async_heap_size);
1726
1895
  if (!mem) [[unlikely]] {
1727
1896
  ThrowError<Napi::Error>(env, "Too many asynchronous calls are running");
1728
1897
  return;
1729
1898
  }
1899
+ K_DEFER { ReleaseMemory(instance, mem); };
1730
1900
 
1731
1901
  if (std::this_thread::get_id() == instance->main_thread_id) {
1732
- CallData call(env, instance, mem);
1902
+ CallData call(env, instance, mem, nullptr);
1903
+ K_DEFER { call.Finalize(); };
1904
+
1905
+ napi_handle_scope scope;
1906
+ napi_open_handle_scope(env, &scope);
1907
+ K_DEFER { napi_close_handle_scope(env, scope); };
1733
1908
 
1734
- Napi::HandleScope scope(env);
1735
1909
  call.Relay(idx, sp);
1736
1910
  } else {
1737
- CallData call(env, instance, mem);
1911
+ CallData call(env, instance, mem, nullptr);
1738
1912
  call.RelayAsync(idx, sp);
1739
1913
  }
1740
1914
  }
@@ -1742,7 +1916,6 @@ extern "C" void RelayCallback(Size idx, uint8_t *sp)
1742
1916
  extern "C" void RelayDirect(CallData *call, Size idx, uint8_t *sp)
1743
1917
  {
1744
1918
  TrampolineInfo *trampoline = &shared.trampolines[idx];
1745
- Napi::Env env = trampoline->func.Env();
1746
1919
 
1747
1920
  #if defined(_WIN32)
1748
1921
  TEB *teb = GetTEB();
@@ -1763,15 +1936,17 @@ extern "C" void RelayDirect(CallData *call, Size idx, uint8_t *sp)
1763
1936
  teb->DeallocationStack = instance->main_stack_min;
1764
1937
  #endif
1765
1938
 
1766
- if (env.IsExceptionPending()) [[unlikely]]
1767
- return;
1768
- if (!trampoline->used) [[unlikely]] {
1769
- ThrowError<Napi::Error>(env, "Cannot use non-registered callback beyond FFI call");
1939
+ if (!CheckTrampolineStatus(trampoline))
1770
1940
  return;
1771
- }
1772
1941
 
1773
- Napi::HandleScope scope(call->env);
1774
- call->Relay(idx, sp);
1942
+ // Relay the call
1943
+ {
1944
+ napi_handle_scope scope;
1945
+ napi_open_handle_scope(call->env, &scope);
1946
+ K_DEFER { napi_close_handle_scope(call->env, scope); };
1947
+
1948
+ call->Relay(idx, sp);
1949
+ }
1775
1950
  }
1776
1951
 
1777
1952
  static Napi::Value FindLibraryFunction(const Napi::CallbackInfo &info)
@@ -1783,6 +1958,8 @@ static Napi::Value FindLibraryFunction(const Napi::CallbackInfo &info)
1783
1958
  FunctionInfo *func = new FunctionInfo();
1784
1959
  K_DEFER { func->Unref(); };
1785
1960
 
1961
+ func->env = env;
1962
+ func->instance = instance;
1786
1963
  func->lib = lib->Ref();
1787
1964
 
1788
1965
  if (info.Length() >= 2) {
@@ -1810,10 +1987,6 @@ static Napi::Value FindLibraryFunction(const Napi::CallbackInfo &info)
1810
1987
 
1811
1988
  if (!AnalyseFunction(env, instance, func))
1812
1989
  return env.Null();
1813
- if (func->variadic) {
1814
- // Minimize reallocations
1815
- func->parameters.Grow(32);
1816
- }
1817
1990
 
1818
1991
  #if defined(_WIN32)
1819
1992
  if (func->ordinal_name < 0) {
@@ -1877,10 +2050,7 @@ static Napi::Value FindSymbol(const Napi::CallbackInfo &info)
1877
2050
  return env.Null();
1878
2051
  }
1879
2052
 
1880
- Napi::External<void> external = Napi::External<void>::New(env, ptr);
1881
- SetValueTag(external, &type);
1882
-
1883
- return external;
2053
+ return WrapPointer(env, type, ptr);
1884
2054
  }
1885
2055
 
1886
2056
  static Napi::Value UnloadLibrary(const Napi::CallbackInfo &info)
@@ -1902,11 +2072,11 @@ static Napi::Value LoadSharedLibrary(const Napi::CallbackInfo &info)
1902
2072
  ThrowError<Napi::TypeError>(env, "Expected 1 or 2 arguments, got %1", info.Length());
1903
2073
  return env.Null();
1904
2074
  }
1905
- if (!info[0].IsString() && !IsNullOrUndefined(info[0])) {
2075
+ if (!info[0].IsString() && !IsNullOrUndefined(env, info[0])) {
1906
2076
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for filename, expected string or null", GetValueType(instance, info[0]));
1907
2077
  return env.Null();
1908
2078
  }
1909
- if (info.Length() >= 2 && !IsObject(info[1])) {
2079
+ if (info.Length() >= 2 && !IsObject(env, info[1])) {
1910
2080
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for options, expected object", GetValueType(instance, info[1]));
1911
2081
  return env.Null();
1912
2082
  }
@@ -2037,25 +2207,22 @@ static Napi::Value RegisterCallback(const Napi::CallbackInfo &info)
2037
2207
 
2038
2208
  TrampolineInfo *trampoline = &shared.trampolines[idx];
2039
2209
 
2210
+ trampoline->state = 1;
2040
2211
  trampoline->instance = instance;
2041
2212
  trampoline->proto = type->ref.proto;
2042
2213
  trampoline->func.Reset(func, 1);
2043
- if (!IsNullOrUndefined(recv)) {
2214
+ if (!IsNullOrUndefined(env, recv)) {
2044
2215
  trampoline->recv.Reset(recv, 1);
2045
2216
  } else {
2046
2217
  trampoline->recv.Reset();
2047
2218
  }
2048
- trampoline->used = true;
2049
2219
 
2050
2220
  void *ptr = GetTrampoline(idx);
2051
2221
 
2052
- Napi::External<void> external = Napi::External<void>::New(env, ptr);
2053
- SetValueTag(external, type->ref.marker);
2054
-
2055
2222
  // Cache index for fast unregistration
2056
2223
  instance->trampolines_map.Set(ptr, idx);
2057
2224
 
2058
- return external;
2225
+ return WrapCallback(env, type->ref.type, ptr);
2059
2226
  }
2060
2227
 
2061
2228
  static Napi::Value UnregisterCallback(const Napi::CallbackInfo &info)
@@ -2067,14 +2234,13 @@ static Napi::Value UnregisterCallback(const Napi::CallbackInfo &info)
2067
2234
  ThrowError<Napi::TypeError>(env, "Expected 1 argument, got %1", info.Length());
2068
2235
  return env.Null();
2069
2236
  }
2070
- if (!info[0].IsExternal()) {
2237
+
2238
+ void *ptr;
2239
+ if (!TryPointer(env, info[0], &ptr)) {
2071
2240
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for id, expected registered callback", GetValueType(instance, info[0]));
2072
2241
  return env.Null();
2073
2242
  }
2074
2243
 
2075
- Napi::External<void> external = info[0].As<Napi::External<void>>();
2076
- void *ptr = external.Data();
2077
-
2078
2244
  int16_t idx;
2079
2245
  {
2080
2246
  int16_t *it = instance->trampolines_map.Find(ptr);
@@ -2095,9 +2261,9 @@ static Napi::Value UnregisterCallback(const Napi::CallbackInfo &info)
2095
2261
  TrampolineInfo *trampoline = &shared.trampolines[idx];
2096
2262
  K_ASSERT(!trampoline->func.IsEmpty());
2097
2263
 
2264
+ trampoline->state = 0;
2098
2265
  trampoline->func.Reset();
2099
2266
  trampoline->recv.Reset();
2100
- trampoline->used = false;
2101
2267
 
2102
2268
  shared.available.Append(idx);
2103
2269
  }
@@ -2128,13 +2294,14 @@ static Napi::Value CastValue(const Napi::CallbackInfo &info)
2128
2294
  return env.Null();
2129
2295
  }
2130
2296
 
2131
- ValueCast *cast = new ValueCast;
2297
+ ValueCast *cast = new ValueCast();
2132
2298
 
2133
- cast->ref.Reset(value, 1);
2299
+ cast->env = env;
2300
+ napi_create_reference(env, value, 1, &cast->ref);
2134
2301
  cast->type = type;
2135
2302
 
2136
2303
  Napi::External<ValueCast> external = Napi::External<ValueCast>::New(env, cast, [](Napi::Env, ValueCast *cast) { delete cast; });
2137
- SetValueTag(external, &CastMarker);
2304
+ SetValueTag(env, external, &CastMarker);
2138
2305
 
2139
2306
  return external;
2140
2307
  }
@@ -2161,10 +2328,10 @@ static Napi::Value DecodeValue(const Napi::CallbackInfo &info)
2161
2328
  if (has_len) {
2162
2329
  Size len = info[2 + has_offset].As<Napi::Number>();
2163
2330
 
2164
- Napi::Value ret = Decode(value, offset, type, &len);
2331
+ Napi::Value ret = Decode(value, (Size)offset, type, &len);
2165
2332
  return ret;
2166
2333
  } else {
2167
- Napi::Value ret = Decode(value, offset, type);
2334
+ Napi::Value ret = Decode(value, (Size)offset, type);
2168
2335
  return ret;
2169
2336
  }
2170
2337
  }
@@ -2212,8 +2379,10 @@ static Napi::Value CallPointerSync(const Napi::CallbackInfo &info)
2212
2379
 
2213
2380
  const FunctionInfo *proto = type->ref.proto;
2214
2381
 
2215
- return proto->variadic ? TranslateVariadicCall(proto, ptr, info)
2216
- : TranslateNormalCall(proto, ptr, info);
2382
+ napi_value ret = proto->variadic ? TranslateVariadicCall(env, proto, ptr, info.First() + 2, info.Length() - 2)
2383
+ : TranslateNormalCall(env, proto, ptr, info.First() + 2, info.Length() - 2);
2384
+
2385
+ return Napi::Value(env, ret);
2217
2386
  }
2218
2387
 
2219
2388
  static Napi::Value EncodeValue(const Napi::CallbackInfo &info)
@@ -2239,10 +2408,10 @@ static Napi::Value EncodeValue(const Napi::CallbackInfo &info)
2239
2408
  if (has_len) {
2240
2409
  Size len = info[3 + has_offset].As<Napi::Number>();
2241
2410
 
2242
- if (!Encode(ref, offset, value, type, &len))
2411
+ if (!Encode(ref, (Size)offset, value, type, &len))
2243
2412
  return env.Null();
2244
2413
  } else {
2245
- if (!Encode(ref, offset, value, type))
2414
+ if (!Encode(ref, (Size)offset, value, type))
2246
2415
  return env.Null();
2247
2416
  }
2248
2417
 
@@ -2318,6 +2487,11 @@ static Napi::Value ResetKoffi(const Napi::CallbackInfo &info)
2318
2487
 
2319
2488
  instance->callbacks.Clear();
2320
2489
 
2490
+ for (InstanceMemory *mem: instance->memories) {
2491
+ delete mem;
2492
+ }
2493
+ instance->memories.Clear();
2494
+
2321
2495
  return env.Undefined();
2322
2496
  }
2323
2497
 
@@ -2349,6 +2523,18 @@ void LibraryHolder::Unref() const
2349
2523
  }
2350
2524
  }
2351
2525
 
2526
+ ValueCast::~ValueCast()
2527
+ {
2528
+ if (node_api_post_finalizer) {
2529
+ node_api_post_finalizer(env, [](napi_env env, void *data, void *) {
2530
+ napi_ref ref = (napi_ref)data;
2531
+ napi_delete_reference(env, ref);
2532
+ }, (void *)ref, nullptr);
2533
+ } else {
2534
+ napi_delete_reference(env, ref);
2535
+ }
2536
+ }
2537
+
2352
2538
  FunctionInfo::~FunctionInfo()
2353
2539
  {
2354
2540
  if (lib) {
@@ -2380,10 +2566,10 @@ InstanceMemory::~InstanceMemory()
2380
2566
  }
2381
2567
  #else
2382
2568
  if (stack.ptr) {
2383
- munmap(stack.ptr, stack.len);
2569
+ munmap(stack.ptr, stack.end - stack.ptr);
2384
2570
  }
2385
2571
  if (heap.ptr) {
2386
- munmap(heap.ptr, heap.len);
2572
+ munmap(heap.ptr, heap.end - heap.ptr);
2387
2573
  }
2388
2574
  #endif
2389
2575
  }
@@ -2431,10 +2617,8 @@ static void RegisterPrimitiveType(Napi::Env env, Napi::Object map, std::initiali
2431
2617
  }
2432
2618
 
2433
2619
  if (ref) {
2434
- const TypeInfo *marker = instance->types_map.FindValue(ref, nullptr);
2435
- K_ASSERT(marker);
2436
-
2437
- type->ref.marker = marker;
2620
+ type->ref.type = instance->types_map.FindValue(ref, nullptr);
2621
+ K_ASSERT(type->ref.type);
2438
2622
  }
2439
2623
 
2440
2624
  Napi::Value wrapper = WrapType(env, type);
@@ -2480,33 +2664,124 @@ static inline PrimitiveKind GetBigEndianPrimitive(PrimitiveKind kind)
2480
2664
  #endif
2481
2665
  }
2482
2666
 
2483
- static InstanceData *CreateInstance()
2667
+ static bool DetectBun(Napi::Env env)
2484
2668
  {
2485
- InstanceData *instance = new InstanceData();
2486
- K_DEFER_N(err_guard) { delete instance; };
2669
+ Napi::Value ret = env.RunScript("process.isBun");
2670
+ Napi::Boolean b = ret.ToBoolean();
2487
2671
 
2488
- instance->main_thread_id = std::this_thread::get_id();
2672
+ return b.Value();
2673
+ }
2489
2674
 
2490
- instance->debug = GetDebugFlag("DUMP_CALLS");
2675
+ static void PickTranslateZeroCallVariant(Napi::Env env)
2676
+ {
2677
+ static int BunTrap = 42;
2491
2678
 
2492
- #if defined(_WIN32)
2493
- TEB *teb = GetTEB();
2679
+ Napi::Object self = Napi::Object::New(env);
2494
2680
 
2495
- instance->main_stack_max = teb->StackBase;
2496
- instance->main_stack_min = teb->DeallocationStack;
2497
- #endif
2681
+ napi_status status;
2682
+ napi_value func;
2683
+ napi_value ret;
2498
2684
 
2499
- err_guard.Disable();
2500
- return instance;
2685
+ status = napi_create_function(env, nullptr, 0, [](napi_env env, napi_callback_info info) {
2686
+ napi_value self;
2687
+ napi_get_cb_info(env, info, nullptr, nullptr, &self, nullptr);
2688
+
2689
+ void **ptr1 = (void **)info;
2690
+ napi_value *ptr2 = (napi_value *)info;
2691
+
2692
+ if (ptr1[0] == &BunTrap || ptr1[1] == &BunTrap) {
2693
+ translate_zero_call = TranslateZeroCallBun;
2694
+ } else if (ptr2[0] != self && ptr2[1] != self) {
2695
+ translate_zero_call = TranslateZeroCallNode;
2696
+ } else {
2697
+ translate_zero_call = TranslateFastCall;
2698
+ }
2699
+
2700
+ return self;
2701
+ }, &BunTrap, &func);
2702
+ K_ASSERT(status == napi_ok);
2703
+
2704
+ status = napi_call_function(env, self, func, 0, nullptr, &ret);
2705
+ K_ASSERT(status == napi_ok);
2706
+ }
2707
+
2708
+ static bool CanReferencePrimitiveValues(napi_env env)
2709
+ {
2710
+ uint32_t version = 0;
2711
+ napi_get_version(env, &version);
2712
+
2713
+ return version >= 10;
2714
+ }
2715
+
2716
+ static bool CanDeleteReferenceInFinalizer(napi_env env)
2717
+ {
2718
+ const napi_node_version *version = nullptr;
2719
+ napi_get_node_version(env, &version);
2720
+
2721
+ if (version->major >= 24)
2722
+ return true;
2723
+
2724
+ // Made by looking at the git history of each release branch
2725
+ if (version->major == 23 && version->minor >= 5)
2726
+ return true;
2727
+ if (version->major == 22 && version->minor >= 13)
2728
+ return true;
2729
+ if (version->major == 20 && version->minor >= 19)
2730
+ return true;
2731
+ if (version->major == 20 && version->minor == 18 && version->patch >= 3)
2732
+ return true;
2733
+
2734
+ return false;
2501
2735
  }
2502
2736
 
2503
2737
  static Napi::Object InitModule(Napi::Env env, Napi::Object exports)
2504
2738
  {
2505
- InstanceData *instance = CreateInstance();
2739
+ // Load recent N-API functions (version >= 9) functions dynamically
2740
+ {
2741
+ static std::once_flag flag;
2742
+
2743
+ std::call_once(flag, [&]() {
2744
+ PickTranslateZeroCallVariant(env);
2745
+
2746
+ if (DetectBun(env))
2747
+ return;
2748
+
2749
+ uv_lib_t lib = {};
2750
+
2751
+ #if defined(_WIN32)
2752
+ lib.handle = GetModuleHandle(nullptr);
2753
+ #else
2754
+ uv_dlopen(nullptr, &lib);
2755
+ K_DEFER { uv_dlclose(&lib); };
2756
+ #endif
2757
+
2758
+ if (CanReferencePrimitiveValues(env)) {
2759
+ // We can't use optimized property keys in older versions because we need to create
2760
+ // references to them, but napi_create_reference() was not usable with primitive values.
2761
+ uv_dlsym(&lib, "node_api_create_property_key_utf8", (void **)&node_api_create_property_key_utf8);
2762
+ }
2763
+
2764
+ if (!CanDeleteReferenceInFinalizer(env)) {
2765
+ // napi_delete_reference cannot be safely used in older Node versions because it
2766
+ // errors out (or even asserts) if it gets called in a finalizer. In this case,
2767
+ // use experimental API to try to run it later.
2768
+ uv_dlsym(&lib, "node_api_post_finalizer", (void **)&node_api_post_finalizer);
2769
+ }
2770
+ });
2771
+ }
2772
+
2773
+ InstanceData *instance = new InstanceData();
2506
2774
  K_CRITICAL(instance, "Failed to initialize Koffi");
2507
2775
 
2776
+ instance->env = env;
2508
2777
  env.SetInstanceData(instance);
2509
2778
 
2779
+ #if defined(__clang__)
2780
+ // First call to napi_create_double() does some weird stuff I can't explain
2781
+ // in clang-cl builds. I don't like this... but this fixes it, somehow ><
2782
+ Napi::Number::New(env, 0.0);
2783
+ #endif
2784
+
2510
2785
  exports.Set("config", Napi::Function::New(env, GetSetConfig, "config"));
2511
2786
  exports.Set("stats", Napi::Function::New(env, GetStats, "stats"));
2512
2787
 
@@ -2558,7 +2833,7 @@ static Napi::Object InitModule(Napi::Env env, Napi::Object exports)
2558
2833
  Napi::Object codes = Napi::Object::New(env);
2559
2834
 
2560
2835
  for (const ErrnoCodeInfo &info: ErrnoCodes) {
2561
- codes.Set(info.name, Napi::Number::New(env, info.value));
2836
+ codes.Set(info.name, NewInt(env, (int32_t)info.value));
2562
2837
  }
2563
2838
 
2564
2839
  os.Set("errno", codes);
@@ -2649,10 +2924,7 @@ static Napi::Object InitModule(Napi::Env env, Napi::Object exports)
2649
2924
  Napi::Object node = Napi::Object::New(env);
2650
2925
  exports.Set("node", node);
2651
2926
 
2652
- Napi::External<void> external = Napi::External<void>::New(env, (napi_env)env);
2653
- SetValueTag(external, instance->void_type);
2654
-
2655
- node.Set("env", external);
2927
+ node.Set("env", WrapPointer(env, instance->void_type, (napi_env)env));
2656
2928
 
2657
2929
  node.Set("poll", Napi::Function::New(env, &Poll, "poll"));
2658
2930
  node.Set("PollHandle", PollHandle::Define(env));
@@ -2660,6 +2932,18 @@ static Napi::Object InitModule(Napi::Env env, Napi::Object exports)
2660
2932
 
2661
2933
  exports.Set("version", Napi::String::New(env, K_STRINGIFY(VERSION)));
2662
2934
 
2935
+ #if defined(_WIN32)
2936
+ {
2937
+ TEB *teb = GetTEB();
2938
+
2939
+ instance->main_stack_max = teb->StackBase;
2940
+ instance->main_stack_min = teb->DeallocationStack;
2941
+ }
2942
+ #endif
2943
+
2944
+ instance->translate_zero_call = translate_zero_call;
2945
+ instance->main_thread_id = std::this_thread::get_id();
2946
+
2663
2947
  napi_add_env_cleanup_hook(env, [](void *udata) {
2664
2948
  InstanceData *instance = (InstanceData *)udata;
2665
2949
 
@@ -2676,6 +2960,8 @@ static Napi::Object InitModule(Napi::Env env, Napi::Object exports)
2676
2960
 
2677
2961
  InstanceData::~InstanceData()
2678
2962
  {
2963
+ delete variadic_func;
2964
+
2679
2965
  for (InstanceMemory *mem: memories) {
2680
2966
  delete mem;
2681
2967
  }
@@ -2691,7 +2977,7 @@ InstanceData::~InstanceData()
2691
2977
  trampoline->instance = nullptr;
2692
2978
  trampoline->func.Reset();
2693
2979
  trampoline->recv.Reset();
2694
- trampoline->used = false;
2980
+ trampoline->state = 0;
2695
2981
  }
2696
2982
  }
2697
2983
  }