koffi 3.0.1 → 3.1.0

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 (50) hide show
  1. package/CHANGELOG.md +32 -3
  2. package/cnoke.cjs +2 -2
  3. package/doc/benchmarks.md +1 -1
  4. package/doc/callbacks.md +7 -26
  5. package/doc/{input.md → composites.md} +161 -147
  6. package/doc/contribute.md +3 -2
  7. package/doc/index.md +0 -14
  8. package/doc/{functions.md → load.md} +54 -113
  9. package/doc/migration.md +4 -7
  10. package/doc/misc.md +0 -103
  11. package/doc/output.md +5 -11
  12. package/doc/pointers.md +76 -17
  13. package/doc/primitives.md +151 -0
  14. package/doc/start.md +3 -13
  15. package/doc/types.md +88 -0
  16. package/doc/unions.md +0 -186
  17. package/doc/values.md +134 -0
  18. package/index.d.ts +375 -308
  19. package/lib/native/base/base.cc +66 -24
  20. package/lib/native/base/base.hh +55 -153
  21. package/package.json +16 -16
  22. package/src/koffi/CMakeLists.txt +20 -17
  23. package/src/koffi/index.cjs +30 -111
  24. package/src/koffi/index.js +22 -96
  25. package/src/koffi/indirect.cjs +30 -111
  26. package/src/koffi/indirect.js +24 -24
  27. package/src/koffi/src/abi/arm64.cc +48 -62
  28. package/src/koffi/src/abi/riscv64.cc +39 -57
  29. package/src/koffi/src/abi/x64sysv.cc +39 -57
  30. package/src/koffi/src/abi/x64win.cc +48 -65
  31. package/src/koffi/src/abi/x86.cc +47 -59
  32. package/src/koffi/src/call.cc +426 -209
  33. package/src/koffi/src/call.hh +7 -11
  34. package/src/koffi/src/ffi.cc +534 -303
  35. package/src/koffi/src/ffi.hh +71 -15
  36. package/src/koffi/src/parser.cc +5 -3
  37. package/src/koffi/src/parser.hh +2 -2
  38. package/src/koffi/src/static.cjs +122 -0
  39. package/src/koffi/src/static.js +125 -0
  40. package/src/koffi/src/type.cc +725 -0
  41. package/src/koffi/src/type.hh +71 -0
  42. package/src/koffi/src/util.cc +117 -1202
  43. package/src/koffi/src/util.hh +158 -156
  44. package/src/koffi/src/uv.cc +17 -11
  45. package/src/koffi/src/uv.hh +2 -1
  46. package/vendor/node-addon-api/README.md +1 -1
  47. package/vendor/node-addon-api/napi-inl.h +213 -35
  48. package/vendor/node-addon-api/napi.h +118 -7
  49. package/doc/variables.md +0 -102
  50. package/indirect.d.ts +0 -322
@@ -4,6 +4,7 @@
4
4
  #include "lib/native/base/base.hh"
5
5
  #include "call.hh"
6
6
  #include "ffi.hh"
7
+ #include "type.hh"
7
8
  #include "util.hh"
8
9
  #if defined(_WIN32)
9
10
  #include "win32.hh"
@@ -44,6 +45,8 @@ static const Size TrampolineSize = ((const uint8_t *)&TrampolineEnd - Trampoline
44
45
 
45
46
  #endif
46
47
 
48
+ static napi_value (*translate_zero_call)(napi_env env, napi_callback_info info);
49
+
47
50
  #if defined(K_DEBUG)
48
51
  CallData::~CallData()
49
52
  {
@@ -71,21 +74,12 @@ void CallData::Finalize()
71
74
  DecodeElements(instance, value, out.ptr, out.type, len);
72
75
  } break;
73
76
 
74
- case OutArgument::Kind::Buffer: {
75
- Span<uint8_t> buffer;
76
-
77
- bool success = TryBuffer(env, value, &buffer);
78
- K_ASSERT(success);
79
-
80
- DecodeBuffer(buffer, out.ptr, out.type);
81
- } break;
82
-
83
77
  case OutArgument::Kind::String: {
84
78
  K_ASSERT(IsArray(env, value));
85
79
  K_ASSERT(GetArrayLength(env, value) == 1);
86
80
 
87
81
  Size len = strnlen((const char *)out.ptr, out.max_len);
88
- Napi::String str = Napi::String::New(env, (const char *)out.ptr, len);
82
+ napi_value str = NewString(env, (const char *)out.ptr, len);
89
83
 
90
84
  napi_set_element(env, value, 0, str);
91
85
  } break;
@@ -95,7 +89,7 @@ void CallData::Finalize()
95
89
  K_ASSERT(GetArrayLength(env, value) == 1);
96
90
 
97
91
  Size len = NullTerminatedLength((const char16_t *)out.ptr, out.max_len);
98
- Napi::String str = Napi::String::New(env, (const char16_t *)out.ptr, len);
92
+ napi_value str = NewString(env, (const char16_t *)out.ptr, len);
99
93
 
100
94
  napi_set_element(env, value, 0, str);
101
95
  } break;
@@ -105,7 +99,7 @@ void CallData::Finalize()
105
99
  K_ASSERT(GetArrayLength(env, value) == 1);
106
100
 
107
101
  Size len = NullTerminatedLength((const char32_t *)out.ptr, out.max_len);
108
- Napi::String str = MakeStringFromUTF32(env, (const char32_t *)out.ptr, len);
102
+ napi_value str = NewString(env, (const char32_t *)out.ptr, len);
109
103
 
110
104
  napi_set_element(env, value, 0, str);
111
105
  } break;
@@ -113,7 +107,7 @@ void CallData::Finalize()
113
107
  case OutArgument::Kind::Object: {
114
108
  if (CheckValueTag(env, value, &UnionValueMarker)) {
115
109
  UnionValue *u = nullptr;
116
- napi_unwrap(env, value, (void **)&u);
110
+ NAPI_OK(napi_unwrap(env, value, (void **)&u));
117
111
 
118
112
  u->SetRaw(out.ptr);
119
113
  } else {
@@ -125,7 +119,7 @@ void CallData::Finalize()
125
119
  }
126
120
 
127
121
  for (const OutArgument &out: out_arguments) {
128
- napi_delete_reference(env, out.ref);
122
+ node_api_delete_reference(env, out.ref);
129
123
  }
130
124
  }
131
125
 
@@ -140,7 +134,7 @@ void CallData::Finalize()
140
134
  K_ASSERT(trampoline->func);
141
135
 
142
136
  trampoline->state = 0;
143
- napi_delete_reference(env, trampoline->func);
137
+ node_api_delete_reference(env, trampoline->func);
144
138
  trampoline->func = nullptr;
145
139
 
146
140
  shared.available.Append(idx);
@@ -183,7 +177,7 @@ void CallData::RelayAsync(Size idx, uint8_t *sp)
183
177
  .sp = sp
184
178
  };
185
179
 
186
- napi_call_threadsafe_function(instance->broker, &ctx, napi_tsfn_blocking);
180
+ NAPI_OK(napi_call_threadsafe_function(instance->broker, &ctx, napi_tsfn_blocking));
187
181
 
188
182
  // Wait until it executes
189
183
  std::unique_lock<std::mutex> lock(ctx.mutex);
@@ -198,8 +192,8 @@ napi_value CallData::CallCallback(const TrampolineInfo *trampoline, const napi_v
198
192
  napi_value func;
199
193
  napi_value value;
200
194
 
201
- napi_get_undefined(env, &recv);
202
- napi_get_reference_value(env, trampoline->func, &func);
195
+ NAPI_OK(napi_get_undefined(env, &recv));
196
+ NAPI_OK(napi_get_reference_value(env, trampoline->func, &func));
203
197
 
204
198
  napi_status status = napi_call_function(env, recv, func, (size_t)count, args, &value);
205
199
  if (status == napi_pending_exception) [[unlikely]]
@@ -257,16 +251,13 @@ bool CallData::PushString32(napi_value value, int directions, const char32_t **o
257
251
  Size CallData::PushStringValue(napi_value value, const char **out_str)
258
252
  {
259
253
  size_t len;
260
- napi_status status;
261
254
 
262
255
  size_t available = (size_t)(mem->heap.end - mem->heap.ptr);
263
256
  char *ptr = (char *)mem->heap.ptr;
264
257
 
265
258
  // Fast path for small strings
266
259
  if (available >= 4096) [[likely]] {
267
- char *ptr = (char *)mem->heap.ptr;
268
-
269
- status = napi_get_value_string_utf8(env, value, ptr, 4096, &len);
260
+ napi_status status = napi_get_value_string_utf8(env, value, ptr, 4096, &len);
270
261
  if (status == napi_string_expected)
271
262
  return -1;
272
263
  K_ASSERT(status == napi_ok);
@@ -285,14 +276,12 @@ Size CallData::PushStringValue(napi_value value, const char **out_str)
285
276
  }
286
277
  }
287
278
 
288
- status = napi_get_value_string_utf8(env, value, nullptr, 0, &len);
289
- K_ASSERT(status == napi_ok);
279
+ NAPI_OK(napi_get_value_string_utf8(env, value, nullptr, 0, &len));
290
280
 
291
281
  len++;
292
282
 
293
283
  if (len <= available) {
294
- status = napi_get_value_string_utf8(env, value, ptr, len, nullptr);
295
- K_ASSERT(status == napi_ok);
284
+ NAPI_OK(napi_get_value_string_utf8(env, value, ptr, len, nullptr));
296
285
 
297
286
  mem->heap.ptr += (Size)AlignLen(len, 16);
298
287
 
@@ -302,8 +291,7 @@ Size CallData::PushStringValue(napi_value value, const char **out_str)
302
291
  Span<char> buf = AllocateSpan<char>(&mem->allocator, (Size)len);
303
292
  release_alloc |= (prev_stack == mem->stack.end);
304
293
 
305
- status = napi_get_value_string_utf8(env, value, buf.ptr, len, nullptr);
306
- K_ASSERT(status == napi_ok);
294
+ NAPI_OK(napi_get_value_string_utf8(env, value, buf.ptr, len, nullptr));
307
295
 
308
296
  *out_str = buf.ptr;
309
297
  return (Size)len;
@@ -314,9 +302,8 @@ Size CallData::PushString16Value(napi_value value, const char16_t **out_str16)
314
302
  {
315
303
  size_t len = 0;
316
304
  Span<char16_t> buf;
317
- napi_status status;
318
305
 
319
- status = napi_get_value_string_utf16(env, value, nullptr, 0, &len);
306
+ napi_status status = napi_get_value_string_utf16(env, value, nullptr, 0, &len);
320
307
  if (status == napi_string_expected)
321
308
  return -1;
322
309
  K_ASSERT(status == napi_ok);
@@ -327,16 +314,14 @@ Size CallData::PushString16Value(napi_value value, const char16_t **out_str16)
327
314
  buf.len = (mem->heap.end - mem->heap.ptr) / 2;
328
315
 
329
316
  if (len <= (size_t)buf.len) {
330
- status = napi_get_value_string_utf16(env, value, buf.ptr, len, nullptr);
331
- K_ASSERT(status == napi_ok);
317
+ NAPI_OK(napi_get_value_string_utf16(env, value, buf.ptr, len, nullptr));
332
318
 
333
319
  mem->heap.ptr += (Size)AlignLen(len * 2, 16);
334
320
  } else {
335
321
  buf = AllocateSpan<char16_t>(&mem->allocator, (Size)len);
336
322
  release_alloc |= (prev_stack == mem->stack.end);
337
323
 
338
- status = napi_get_value_string_utf16(env, value, buf.ptr, len, nullptr);
339
- K_ASSERT(status == napi_ok);
324
+ NAPI_OK(napi_get_value_string_utf16(env, value, buf.ptr, len, nullptr));
340
325
  }
341
326
 
342
327
  *out_str16 = buf.ptr;
@@ -391,17 +376,16 @@ Size CallData::PushString32Value(napi_value value, const char32_t **out_str32)
391
376
  return j;
392
377
  }
393
378
 
394
- static inline napi_value GetMemberValue(napi_env env, napi_value obj, const RecordMember &member)
379
+ static FORCE_INLINE napi_value GetMemberValue(napi_env env, napi_value obj, const RecordMember &member)
395
380
  {
396
- napi_value value = nullptr;
381
+ napi_value value;
397
382
 
398
383
  if (member.key) {
399
- napi_value key = nullptr;
400
- napi_get_reference_value(env, member.key, &key);
401
-
402
- napi_get_property(env, obj, key, &value);
384
+ napi_value key;
385
+ NAPI_OK(napi_get_reference_value(env, member.key, &key));
386
+ NAPI_OK(napi_get_property(env, obj, key, &value));
403
387
  } else {
404
- napi_get_named_property(env, obj, member.name, &value);
388
+ NAPI_OK(napi_get_named_property(env, obj, member.name, &value));
405
389
  }
406
390
 
407
391
  return value;
@@ -409,10 +393,14 @@ static inline napi_value GetMemberValue(napi_env env, napi_value obj, const Reco
409
393
 
410
394
  bool CallData::PushObject(napi_value obj, const TypeInfo *type, uint8_t *origin)
411
395
  {
412
- K_ASSERT(IsObject(env, obj));
413
396
  K_ASSERT(type->primitive == PrimitiveKind::Record ||
414
397
  type->primitive == PrimitiveKind::Union);
415
398
 
399
+ if (GetKindOf(env, obj) != napi_object) [[unlikely]] {
400
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected object", GetValueType(instance, obj));
401
+ return false;
402
+ }
403
+
416
404
  Span<const RecordMember> members = type->members;
417
405
 
418
406
  if (type->primitive == PrimitiveKind::Union) {
@@ -562,12 +550,7 @@ bool CallData::PushObject(napi_value obj, const TypeInfo *type, uint8_t *origin)
562
550
  } break;
563
551
  case PrimitiveKind::Record:
564
552
  case PrimitiveKind::Union: {
565
- if (!IsObject(env, value)) [[unlikely]] {
566
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected object", GetValueType(instance, value));
567
- return false;
568
- }
569
-
570
- if (!PushObject(value, member.type, dest))
553
+ if (!PushObject(value, member.type, dest)) [[unlikely]]
571
554
  return false;
572
555
  } break;
573
556
  case PrimitiveKind::Array: {
@@ -652,7 +635,7 @@ bool CallData::PushNormalArray(Napi::Array array, const TypeInfo *type, Size siz
652
635
  #define PUSH_ARRAY(SetCode) \
653
636
  do { \
654
637
  for (Size i = 0; i < len; i++) { \
655
- Napi::Value value = array[(uint32_t)i]; \
638
+ napi_value value = array[(uint32_t)i].AsValue(); \
656
639
  \
657
640
  uint8_t *dest = origin + offset; \
658
641
  SetCode \
@@ -744,11 +727,6 @@ bool CallData::PushNormalArray(Napi::Array array, const TypeInfo *type, Size siz
744
727
  } break;
745
728
  case PrimitiveKind::Pointer: {
746
729
  PUSH_ARRAY({
747
- if (!IsObject(env, value)) [[unlikely]] {
748
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected object", GetValueType(instance, value));
749
- return false;
750
- }
751
-
752
730
  void *ptr;
753
731
  if (!PushPointer(value, ref, 1, &ptr)) [[unlikely]]
754
732
  return false;
@@ -759,28 +737,23 @@ bool CallData::PushNormalArray(Napi::Array array, const TypeInfo *type, Size siz
759
737
  case PrimitiveKind::Record:
760
738
  case PrimitiveKind::Union: {
761
739
  PUSH_ARRAY({
762
- if (!IsObject(env, value)) [[unlikely]] {
763
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected object", GetValueType(instance, value));
764
- return false;
765
- }
766
-
767
- if (!PushObject(value, ref, dest))
740
+ if (!PushObject(value, ref, dest)) [[unlikely]]
768
741
  return false;
769
742
  });
770
743
  } break;
771
744
  case PrimitiveKind::Array: {
772
745
  for (Size i = 0; i < len; i++) {
773
- Napi::Value value = array[(uint32_t)i];
746
+ napi_value value = array[(uint32_t)i].AsValue();
774
747
 
775
748
  uint8_t *dest = origin + offset;
776
749
 
777
- if (value.IsArray()) {
778
- Napi::Array array2 = value.As<Napi::Array>();
779
- if (!PushNormalArray(array2, ref, (Size)ref->size, dest))
750
+ if (IsArray(env, value)) {
751
+ Napi::Array array = Napi::Array(env, value);
752
+ if (!PushNormalArray(array, ref, (Size)ref->size, dest))
780
753
  return false;
781
754
  } else if (Span<uint8_t> buffer = {}; TryBuffer(env, value, &buffer)) {
782
755
  PushBuffer(buffer, ref, dest);
783
- } else if (value.IsString()) {
756
+ } else if (GetKindOf(env, value) == napi_string) {
784
757
  if (!PushStringArray(value, ref, dest))
785
758
  return false;
786
759
  } else {
@@ -814,19 +787,13 @@ bool CallData::PushNormalArray(Napi::Array array, const TypeInfo *type, Size siz
814
787
  });
815
788
  } break;
816
789
  case PrimitiveKind::Callback: {
817
- for (Size i = 0; i < len; i++) {
818
- Napi::Value value = array[(uint32_t)i];
819
-
820
- uint8_t *dest = origin + offset;
821
-
790
+ PUSH_ARRAY({
822
791
  void *ptr;
823
792
  if (!PushCallback(value, ref, &ptr))
824
793
  return false;
825
794
 
826
- *(void **)dest = ptr;
827
-
828
- offset += stride;
829
- }
795
+ *(const void **)dest = ptr;
796
+ });
830
797
  } break;
831
798
 
832
799
  case PrimitiveKind::Prototype: { K_UNREACHABLE(); } break;
@@ -893,14 +860,9 @@ bool CallData::PushStringArray(napi_value value, const TypeInfo *type, uint8_t *
893
860
  size_t encoded = 0;
894
861
 
895
862
  switch (type->ref.type->primitive) {
896
- case PrimitiveKind::Int8: {
897
- napi_status status = napi_get_value_string_utf8(env, value, (char *)origin, type->size, &encoded);
898
- K_ASSERT(status == napi_ok);
899
- } break;
863
+ case PrimitiveKind::Int8: { NAPI_OK(napi_get_value_string_utf8(env, value, (char *)origin, type->size, &encoded)); } break;
900
864
  case PrimitiveKind::Int16: {
901
- napi_status status = napi_get_value_string_utf16(env, value, (char16_t *)origin, type->size / 2, &encoded);
902
- K_ASSERT(status == napi_ok);
903
-
865
+ NAPI_OK(napi_get_value_string_utf16(env, value, (char16_t *)origin, type->size / 2, &encoded));
904
866
  encoded *= 2;
905
867
  } break;
906
868
 
@@ -933,7 +895,7 @@ restart:
933
895
  Napi::External<ValueCast> external = Napi::External<ValueCast>(env, value);
934
896
  ValueCast *cast = external.Data();
935
897
 
936
- napi_get_reference_value(env, cast->ref, &value);
898
+ NAPI_OK(napi_get_reference_value(env, cast->ref, &value));
937
899
  type = cast->type;
938
900
 
939
901
  goto restart;
@@ -1029,8 +991,7 @@ bool CallData::PushPointerSlow(napi_value value, napi_valuetype kind, const Type
1029
991
  if (directions & 2) {
1030
992
  OutArgument *out = out_arguments.AppendDefault();
1031
993
 
1032
- napi_status status = napi_create_reference(env, value, 1, &out->ref);
1033
- K_ASSERT(status == napi_ok);
994
+ NAPI_OK(napi_create_reference(env, value, 1, &out->ref));
1034
995
 
1035
996
  out->kind = out_kind;
1036
997
  out->ptr = (const uint8_t *)ptr;
@@ -1042,8 +1003,6 @@ bool CallData::PushPointerSlow(napi_value value, napi_valuetype kind, const Type
1042
1003
  return true;
1043
1004
  } else if (ref->primitive == PrimitiveKind::Record ||
1044
1005
  ref->primitive == PrimitiveKind::Union) [[likely]] {
1045
- K_ASSERT(IsObject(env, value));
1046
-
1047
1006
  ptr = (void *)AllocHeap(ref->size);
1048
1007
 
1049
1008
  if (ref->primitive == PrimitiveKind::Union &&
@@ -1053,17 +1012,19 @@ bool CallData::PushPointerSlow(napi_value value, napi_valuetype kind, const Type
1053
1012
  }
1054
1013
 
1055
1014
  if (directions & 1) {
1056
- if (!PushObject(value, ref, (uint8_t *)ptr))
1015
+ if (!PushObject(value, ref, (uint8_t *)ptr)) [[unlikely]]
1057
1016
  return false;
1058
1017
  } else {
1018
+ if (kind != napi_object) [[unlikely]]
1019
+ return false;
1020
+
1059
1021
  MemSet(ptr, 0, ref->size);
1060
1022
  }
1061
1023
 
1062
1024
  if (directions & 2) {
1063
1025
  OutArgument *out = out_arguments.AppendDefault();
1064
1026
 
1065
- napi_status status = napi_create_reference(env, value, 1, &out->ref);
1066
- K_ASSERT(status == napi_ok);
1027
+ NAPI_OK(napi_create_reference(env, value, 1, &out->ref));
1067
1028
 
1068
1029
  out->kind = OutArgument::Kind::Object;
1069
1030
  out->ptr = (const uint8_t *)ptr;
@@ -1107,7 +1068,7 @@ restart:
1107
1068
  Napi::External<ValueCast> external = Napi::External<ValueCast>(env, value);
1108
1069
  ValueCast *cast = external.Data();
1109
1070
 
1110
- napi_get_reference_value(env, cast->ref, &value);
1071
+ NAPI_OK(napi_get_reference_value(env, cast->ref, &value));
1111
1072
  type = cast->type;
1112
1073
 
1113
1074
  goto restart;
@@ -1132,7 +1093,7 @@ restart:
1132
1093
  Napi::External<ValueCast> external = Napi::External<ValueCast>(env, value);
1133
1094
  ValueCast *cast = external.Data();
1134
1095
 
1135
- napi_get_reference_value(env, cast->ref, &value);
1096
+ NAPI_OK(napi_get_reference_value(env, cast->ref, &value));
1136
1097
  type = cast->type;
1137
1098
 
1138
1099
  goto restart;
@@ -1148,7 +1109,7 @@ Size CallData::PushIndirectString(Napi::Array array, const TypeInfo *ref, void *
1148
1109
  if (array.Length() != 1)
1149
1110
  return -1;
1150
1111
 
1151
- Napi::Value value = array[0u];
1112
+ napi_value value = array[0u].AsValue();
1152
1113
 
1153
1114
  if (ref == instance->void_type) {
1154
1115
  return PushStringValue(value, (const char **)out_ptr);
@@ -1192,7 +1153,7 @@ void *CallData::ReserveTrampoline(const FunctionInfo *proto, Napi::Function func
1192
1153
  trampoline->instance = instance;
1193
1154
  trampoline->stack0 = instance->memories[0]->stack0;
1194
1155
  trampoline->proto = proto;
1195
- napi_create_reference(env, func, 1, &trampoline->func);
1156
+ NAPI_OK(napi_create_reference(env, func, 1, &trampoline->func));
1196
1157
 
1197
1158
  void *ptr = GetTrampoline(idx);
1198
1159
 
@@ -1280,72 +1241,7 @@ void CallData::DebugForward()
1280
1241
 
1281
1242
  #endif
1282
1243
 
1283
- static bool DetectDeno(Napi::Env env)
1284
- {
1285
- Napi::Value ret = env.RunScript("typeof Deno != 'undefined'");
1286
- Napi::Boolean b = ret.ToBoolean();
1287
-
1288
- return b.Value();
1289
- }
1290
-
1291
- static bool DetectBun(Napi::Env env)
1292
- {
1293
- Napi::Value ret = env.RunScript("process.isBun");
1294
- Napi::Boolean b = ret.ToBoolean();
1295
-
1296
- return b.Value();
1297
- }
1298
-
1299
- void InitTranslateZeroCall(Napi::Env env)
1300
- {
1301
- if (DetectDeno(env) || DetectBun(env)) {
1302
- translate_zero_call = TranslateFastCall;
1303
- return;
1304
- }
1305
-
1306
- Napi::Object self = Napi::Object::New(env);
1307
-
1308
- napi_status status;
1309
- napi_value func;
1310
- napi_value ret;
1311
-
1312
- status = napi_create_function(env, nullptr, 0, [](napi_env env, napi_callback_info info) {
1313
- napi_value self;
1314
- napi_get_cb_info(env, info, nullptr, nullptr, &self, nullptr);
1315
-
1316
- napi_value *ptr = (napi_value *)info;
1317
-
1318
- if (ptr[0] != self && ptr[1] != self) {
1319
- translate_zero_call = TranslateZeroCall;
1320
- } else {
1321
- translate_zero_call = TranslateFastCall;
1322
- }
1323
-
1324
- return self;
1325
- }, nullptr, &func);
1326
- K_ASSERT(status == napi_ok);
1327
-
1328
- status = napi_call_function(env, self, func, 0, nullptr, &ret);
1329
- K_ASSERT(status == napi_ok);
1330
- }
1331
-
1332
- bool InitAsyncBroker(Napi::Env env, InstanceData *instance)
1333
- {
1334
- if (!instance->broker) {
1335
- if (napi_create_threadsafe_function(env, nullptr, nullptr,
1336
- Napi::String::New(env, "Koffi Async Callback Broker"),
1337
- 0, 1, nullptr, nullptr, nullptr,
1338
- PerformAsyncRelay, &instance->broker) != napi_ok) {
1339
- LogError("Failed to create async callback broker");
1340
- return false;
1341
- }
1342
- napi_unref_threadsafe_function(env, instance->broker);
1343
- }
1344
-
1345
- return true;
1346
- }
1347
-
1348
- napi_value TranslateZeroCall(napi_env env, napi_callback_info info)
1244
+ static napi_value TranslateZeroCall(napi_env env, napi_callback_info info)
1349
1245
  {
1350
1246
  struct CallbackBundle {
1351
1247
  napi_env env;
@@ -1376,8 +1272,7 @@ napi_value TranslateFastCall(napi_env env, napi_callback_info info)
1376
1272
  size_t count = 6;
1377
1273
  FunctionInfo *func;
1378
1274
 
1379
- napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, (void **)&func);
1380
- K_ASSERT(status == napi_ok);
1275
+ NAPI_OK(napi_get_cb_info(env, info, &count, args, nullptr, (void **)&func));
1381
1276
 
1382
1277
  if (count < (size_t)func->required_parameters) [[unlikely]] {
1383
1278
  ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len, count);
@@ -1421,7 +1316,7 @@ static FORCE_INLINE napi_value TranslateNormalCall(napi_env env, const FunctionI
1421
1316
  return ret;
1422
1317
  }
1423
1318
 
1424
- napi_value TranslateNormalCall(napi_env env, napi_callback_info info)
1319
+ static napi_value TranslateNormalCall(napi_env env, napi_callback_info info)
1425
1320
  {
1426
1321
  static_assert(MaxParameters >= 8);
1427
1322
 
@@ -1429,13 +1324,10 @@ napi_value TranslateNormalCall(napi_env env, napi_callback_info info)
1429
1324
  size_t count = 8;
1430
1325
  FunctionInfo *func;
1431
1326
 
1432
- napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, (void **)&func);
1433
- K_ASSERT(status == napi_ok);
1327
+ NAPI_OK(napi_get_cb_info(env, info, &count, args, nullptr, (void **)&func));
1434
1328
 
1435
1329
  if (count > 8) {
1436
- napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, nullptr);
1437
- K_ASSERT(status == napi_ok);
1438
-
1330
+ NAPI_OK(napi_get_cb_info(env, info, &count, args, nullptr, nullptr));
1439
1331
  count = std::min(count, (size_t)MaxParameters);
1440
1332
  }
1441
1333
 
@@ -1474,7 +1366,7 @@ static FORCE_INLINE napi_value TranslateNormalCallDebugAsync(napi_env env, const
1474
1366
  return ret;
1475
1367
  }
1476
1368
 
1477
- napi_value TranslateNormalCallDebugAsync(napi_env env, napi_callback_info info)
1369
+ static napi_value TranslateNormalCallDebugAsync(napi_env env, napi_callback_info info)
1478
1370
  {
1479
1371
  static_assert(MaxParameters >= 8);
1480
1372
 
@@ -1482,13 +1374,10 @@ napi_value TranslateNormalCallDebugAsync(napi_env env, napi_callback_info info)
1482
1374
  size_t count = 8;
1483
1375
  FunctionInfo *func;
1484
1376
 
1485
- napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, (void **)&func);
1486
- K_ASSERT(status == napi_ok);
1377
+ NAPI_OK(napi_get_cb_info(env, info, &count, args, nullptr, (void **)&func));
1487
1378
 
1488
1379
  if (count > 8) {
1489
- napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, nullptr);
1490
- K_ASSERT(status == napi_ok);
1491
-
1380
+ NAPI_OK(napi_get_cb_info(env, info, &count, args, nullptr, nullptr));
1492
1381
  count = std::min(count, (size_t)MaxParameters);
1493
1382
  }
1494
1383
 
@@ -1517,7 +1406,7 @@ static napi_value TranslateVariadicCall(napi_env env, const FunctionInfo *func,
1517
1406
 
1518
1407
  for (Size i = prev->required_parameters, j = prev->required_parameters; i < (Size)count; i += 2, j++) {
1519
1408
  int directions;
1520
- const TypeInfo *type = ResolveType(Napi::Value(env, args[i]), &directions);
1409
+ const TypeInfo *type = ResolveType(instance, args[i], &directions);
1521
1410
 
1522
1411
  if (type != prev->parameters[j].type || directions != prev->parameters[j].directions) [[unlikely]] {
1523
1412
  match = false;
@@ -1558,7 +1447,7 @@ static napi_value TranslateVariadicCall(napi_env env, const FunctionInfo *func,
1558
1447
  for (Size i = variadic->required_parameters; i < count; i += 2) {
1559
1448
  ParameterInfo param = {};
1560
1449
 
1561
- param.type = ResolveType(Napi::Value(env, args[i]), &param.directions);
1450
+ param.type = ResolveType(instance, args[i], &param.directions);
1562
1451
 
1563
1452
  if (!param.type) [[unlikely]]
1564
1453
  return Napi::Env(env).Null();
@@ -1599,7 +1488,7 @@ static napi_value TranslateVariadicCall(napi_env env, const FunctionInfo *func,
1599
1488
  return ret;
1600
1489
  }
1601
1490
 
1602
- napi_value TranslateVariadicCall(napi_env env, napi_callback_info info)
1491
+ static napi_value TranslateVariadicCall(napi_env env, napi_callback_info info)
1603
1492
  {
1604
1493
  static_assert(MaxParameters >= 8);
1605
1494
 
@@ -1607,13 +1496,10 @@ napi_value TranslateVariadicCall(napi_env env, napi_callback_info info)
1607
1496
  size_t count = 8;
1608
1497
  FunctionInfo *func;
1609
1498
 
1610
- napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, (void **)&func);
1611
- K_ASSERT(status == napi_ok);
1499
+ NAPI_OK(napi_get_cb_info(env, info, &count, args, nullptr, (void **)&func));
1612
1500
 
1613
1501
  if (count > 8) {
1614
- napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, nullptr);
1615
- K_ASSERT(status == napi_ok);
1616
-
1502
+ NAPI_OK(napi_get_cb_info(env, info, &count, args, nullptr, nullptr));
1617
1503
  count = std::min(count, (size_t)MaxParameters);
1618
1504
  }
1619
1505
 
@@ -1695,7 +1581,7 @@ void AsyncCall::OnError(const Napi::Error& err)
1695
1581
  callback.Call(self, K_LEN(args), args);
1696
1582
  }
1697
1583
 
1698
- napi_value TranslateAsyncCall(napi_env env, napi_callback_info info)
1584
+ static napi_value TranslateAsyncCall(napi_env env, napi_callback_info info)
1699
1585
  {
1700
1586
  static_assert(MaxParameters >= 6);
1701
1587
 
@@ -1703,13 +1589,10 @@ napi_value TranslateAsyncCall(napi_env env, napi_callback_info info)
1703
1589
  size_t count = 6;
1704
1590
  FunctionInfo *func;
1705
1591
 
1706
- napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, (void **)&func);
1707
- K_ASSERT(status == napi_ok);
1592
+ NAPI_OK(napi_get_cb_info(env, info, &count, args, nullptr, (void **)&func));
1708
1593
 
1709
1594
  if (count > 6) {
1710
- napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, nullptr);
1711
- K_ASSERT(status == napi_ok);
1712
-
1595
+ NAPI_OK(napi_get_cb_info(env, info, &count, args, nullptr, nullptr));
1713
1596
  count = std::min(count, (size_t)MaxParameters);
1714
1597
  }
1715
1598
 
@@ -1740,6 +1623,215 @@ napi_value TranslateAsyncCall(napi_env env, napi_callback_info info)
1740
1623
  return Napi::Env(env).Undefined();
1741
1624
  }
1742
1625
 
1626
+ static bool DetectDeno(Napi::Env env)
1627
+ {
1628
+ Napi::Value ret = env.RunScript("typeof Deno != 'undefined'");
1629
+ Napi::Boolean b = ret.ToBoolean();
1630
+
1631
+ return b.Value();
1632
+ }
1633
+
1634
+ static bool DetectBun(Napi::Env env)
1635
+ {
1636
+ Napi::Value ret = env.RunScript("process.isBun");
1637
+ Napi::Boolean b = ret.ToBoolean();
1638
+
1639
+ return b.Value();
1640
+ }
1641
+
1642
+ void InitTranslateZeroCall(Napi::Env env)
1643
+ {
1644
+ if (DetectDeno(env) || DetectBun(env)) {
1645
+ translate_zero_call = TranslateFastCall;
1646
+ return;
1647
+ }
1648
+
1649
+ Napi::Object self = Napi::Object::New(env);
1650
+
1651
+ napi_value func;
1652
+ napi_value ret;
1653
+
1654
+ auto cb = [](napi_env env, napi_callback_info info) {
1655
+ napi_value self;
1656
+ NAPI_OK(napi_get_cb_info(env, info, nullptr, nullptr, &self, nullptr));
1657
+
1658
+ napi_value *ptr = (napi_value *)info;
1659
+
1660
+ if (ptr[0] != self && ptr[1] != self) {
1661
+ translate_zero_call = TranslateZeroCall;
1662
+ } else {
1663
+ translate_zero_call = TranslateFastCall;
1664
+ }
1665
+
1666
+ return self;
1667
+ };
1668
+
1669
+ NAPI_OK(napi_create_function(env, nullptr, 0, cb, nullptr, &func));
1670
+ NAPI_OK(napi_call_function(env, self, func, 0, nullptr, &ret));
1671
+ }
1672
+
1673
+ napi_value CallPointer(Napi::Env env, const FunctionInfo *proto, void *native, napi_value *args, Size count)
1674
+ {
1675
+ if (proto->variadic) {
1676
+ return TranslateVariadicCall(env, proto, native, args, count);
1677
+ } else {
1678
+ return TranslateNormalCall(env, proto, native, args, count);
1679
+ }
1680
+ }
1681
+
1682
+ static void PerformAsyncRelay(napi_env, napi_value, void *, void *udata)
1683
+ {
1684
+ RelayContext *ctx = (RelayContext *)udata;
1685
+ CallData *call = ctx->call;
1686
+
1687
+ call->Relay(ctx->idx, ctx->sp);
1688
+ call->Finalize();
1689
+
1690
+ // We're done!
1691
+ std::lock_guard<std::mutex> lock(ctx->mutex);
1692
+ ctx->done = true;
1693
+ ctx->cv.notify_one();
1694
+ }
1695
+
1696
+ bool InitAsyncBroker(Napi::Env env, InstanceData *instance)
1697
+ {
1698
+ if (!instance->broker) {
1699
+ if (napi_create_threadsafe_function(env, nullptr, nullptr,
1700
+ NewString(env, "Koffi Async Callback Broker"),
1701
+ 0, 1, nullptr, nullptr, nullptr,
1702
+ PerformAsyncRelay, &instance->broker) != napi_ok) {
1703
+ LogError("Failed to create async callback broker");
1704
+ return false;
1705
+ }
1706
+
1707
+ NAPI_OK(napi_unref_threadsafe_function(env, instance->broker));
1708
+ }
1709
+
1710
+ return true;
1711
+ }
1712
+
1713
+ void *GetTrampoline(int idx)
1714
+ {
1715
+ return (void *)(TrampolineStart + TrampolineSize * idx);
1716
+ }
1717
+
1718
+ static bool CanTypeAcceptCallbacks(const TypeInfo *type)
1719
+ {
1720
+ if (type->primitive == PrimitiveKind::Pointer)
1721
+ return true;
1722
+ if (type->primitive == PrimitiveKind::Callback)
1723
+ return true;
1724
+
1725
+ if (type->primitive == PrimitiveKind::Record ||
1726
+ type->primitive == PrimitiveKind::Union) {
1727
+ for (const RecordMember &member: type->members) {
1728
+ if (CanTypeAcceptCallbacks(member.type))
1729
+ return false;
1730
+ }
1731
+ }
1732
+
1733
+ return true;
1734
+ }
1735
+
1736
+ static bool CanUseFastCall(const FunctionInfo *func)
1737
+ {
1738
+ if (func->parameters.len > 6)
1739
+ return false;
1740
+
1741
+ // Fast calls basically skip CallData::Finalize(), which handles output arguments
1742
+ // and temporary callback trampolines. If the function does not use any
1743
+ // output argument and cannot accept callbacks (so no pointer or callback arguments),
1744
+ // we can skip finalization!
1745
+
1746
+ for (const ParameterInfo &param: func->parameters) {
1747
+ if (param.directions & 2)
1748
+ return false;
1749
+ if (CanTypeAcceptCallbacks(param.type))
1750
+ return false;
1751
+ }
1752
+
1753
+ return true;
1754
+ }
1755
+
1756
+ napi_value DescribeFunction(InstanceData *instance, const FunctionInfo *func)
1757
+ {
1758
+ static const char *const DirectionNames[] = {
1759
+ nullptr,
1760
+ "Input",
1761
+ "Output",
1762
+ "Input/Output"
1763
+ };
1764
+
1765
+ Napi::Env env = instance->env;
1766
+
1767
+ Napi::Object meta = Napi::Object::New(env);
1768
+ Napi::Array arguments = Napi::Array::New(env, func->parameters.len);
1769
+
1770
+ meta.Set("name", NewString(env, func->name));
1771
+ meta.Set("arguments", arguments);
1772
+ meta.Set("result", WrapType(instance, func->ret.type));
1773
+
1774
+ for (Size i = 0; i < func->parameters.len; i++) {
1775
+ const ParameterInfo &param = func->parameters[i];
1776
+ Napi::Object obj = Napi::Object::New(env);
1777
+
1778
+ obj.Set("type", WrapType(instance, param.type));
1779
+ obj.Set("direction", NewString(env, DirectionNames[param.directions]));
1780
+
1781
+ arguments.Set((uint32_t)i, obj);
1782
+ }
1783
+
1784
+ meta.Freeze();
1785
+
1786
+ return meta;
1787
+ }
1788
+
1789
+ static bool IsDebugAsyncEnabled()
1790
+ {
1791
+ static bool debug = GetDebugFlag("DEBUG_ASYNC");
1792
+ return debug;
1793
+ }
1794
+
1795
+ napi_value WrapFunction(InstanceData *instance, const FunctionInfo *func)
1796
+ {
1797
+ Napi::Env env = instance->env;
1798
+
1799
+ Napi::Function wrapper;
1800
+ {
1801
+ napi_value value;
1802
+
1803
+ if (func->variadic) {
1804
+ NAPI_OK(napi_create_function(env, func->name, NAPI_AUTO_LENGTH, TranslateVariadicCall, (void *)func->Ref(), &value));
1805
+ } else if (IsDebugAsyncEnabled()) {
1806
+ NAPI_OK(napi_create_function(env, func->name, NAPI_AUTO_LENGTH, TranslateNormalCallDebugAsync, (void *)func->Ref(), &value));
1807
+ } else if (!func->parameters.len) {
1808
+ NAPI_OK(napi_create_function(env, func->name, NAPI_AUTO_LENGTH, translate_zero_call, (void *)func->Ref(), &value));
1809
+ } else if (CanUseFastCall(func)) {
1810
+ NAPI_OK(napi_create_function(env, func->name, NAPI_AUTO_LENGTH, TranslateFastCall, (void *)func->Ref(), &value));
1811
+ } else {
1812
+ NAPI_OK(napi_create_function(env, func->name, NAPI_AUTO_LENGTH, TranslateNormalCall, (void *)func->Ref(), &value));
1813
+ }
1814
+
1815
+ wrapper = Napi::Function(env, value);
1816
+ wrapper.AddFinalizer([](Napi::BasicEnv, FunctionInfo *func) { func->Unref(); }, (FunctionInfo *)func);
1817
+ }
1818
+
1819
+ if (!func->variadic) {
1820
+ napi_value value;
1821
+ NAPI_OK(napi_create_function(env, func->name, NAPI_AUTO_LENGTH, TranslateAsyncCall, (void *)func->Ref(), &value));
1822
+
1823
+ Napi::Function async = Napi::Function(env, value);
1824
+ async.AddFinalizer([](Napi::BasicEnv, FunctionInfo *func) { func->Unref(); }, (FunctionInfo *)func);
1825
+
1826
+ wrapper.Set("async", async);
1827
+ }
1828
+
1829
+ napi_value meta = DescribeFunction(instance, func);
1830
+ wrapper.Set("info", meta);
1831
+
1832
+ return wrapper;
1833
+ }
1834
+
1743
1835
  static FORCE_INLINE bool CheckTrampolineStatus(TrampolineInfo *trampoline)
1744
1836
  {
1745
1837
  if (trampoline->state == 1) [[likely]]
@@ -1800,7 +1892,7 @@ extern "C" void RelayCallback(Size idx, uint8_t *sp)
1800
1892
  K_DEFER { call.Finalize(); };
1801
1893
 
1802
1894
  napi_handle_scope scope;
1803
- napi_open_handle_scope(env, &scope);
1895
+ NAPI_OK(napi_open_handle_scope(env, &scope));
1804
1896
  K_DEFER { napi_close_handle_scope(env, scope); };
1805
1897
 
1806
1898
  call.Relay(idx, sp);
@@ -1839,39 +1931,164 @@ extern "C" void RelayDirect(CallData *call, Size idx, uint8_t *sp)
1839
1931
  // Relay the call
1840
1932
  {
1841
1933
  napi_handle_scope scope;
1842
- napi_open_handle_scope(call->env, &scope);
1934
+ NAPI_OK(napi_open_handle_scope(call->env, &scope));
1843
1935
  K_DEFER { napi_close_handle_scope(call->env, scope); };
1844
1936
 
1845
1937
  call->Relay(idx, sp);
1846
1938
  }
1847
1939
  }
1848
1940
 
1849
- napi_value CallPointer(napi_env env, const FunctionInfo *proto, void *native, napi_value *args, Size count)
1941
+ bool Encode(InstanceData *instance, uint8_t *origin, napi_value value, const TypeInfo *type)
1850
1942
  {
1851
- if (proto->variadic) {
1852
- return TranslateVariadicCall(env, proto, native, args, count);
1853
- } else {
1854
- return TranslateNormalCall(env, proto, native, args, count);
1943
+ Napi::Env env = instance->env;
1944
+
1945
+ InstanceMemory mem = {};
1946
+ CallData call(env, instance, &mem, nullptr);
1947
+
1948
+ #define PUSH_INTEGER(CType) \
1949
+ do { \
1950
+ CType v; \
1951
+ if (!TryNumber(env, value, &v)) [[unlikely]] { \
1952
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value)); \
1953
+ return false; \
1954
+ } \
1955
+ \
1956
+ *(CType *)origin = v; \
1957
+ } while (false)
1958
+ #define PUSH_INTEGER_SWAP(CType) \
1959
+ do { \
1960
+ CType v; \
1961
+ if (!TryNumber(env, value, &v)) [[unlikely]] { \
1962
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value)); \
1963
+ return false; \
1964
+ } \
1965
+ \
1966
+ *(CType *)origin = ReverseBytes(v); \
1967
+ } while (false)
1968
+
1969
+ switch (type->primitive) {
1970
+ case PrimitiveKind::Void: { K_UNREACHABLE(); } break;
1971
+
1972
+ case PrimitiveKind::Bool: {
1973
+ bool b;
1974
+ napi_status status = napi_get_value_bool(env, value, &b);
1975
+
1976
+ if (status != napi_ok) [[unlikely]] {
1977
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected boolean", GetValueType(instance, value));
1978
+ return false;
1979
+ }
1980
+
1981
+ *(bool *)origin = b;
1982
+ } break;
1983
+ case PrimitiveKind::Int8: { PUSH_INTEGER(int8_t); } break;
1984
+ case PrimitiveKind::UInt8: { PUSH_INTEGER(uint8_t); } break;
1985
+ case PrimitiveKind::Int16: { PUSH_INTEGER(int16_t); } break;
1986
+ case PrimitiveKind::Int16S: { PUSH_INTEGER_SWAP(int16_t); } break;
1987
+ case PrimitiveKind::UInt16: { PUSH_INTEGER(uint16_t); } break;
1988
+ case PrimitiveKind::UInt16S: { PUSH_INTEGER_SWAP(uint16_t); } break;
1989
+ case PrimitiveKind::Int32: { PUSH_INTEGER(int32_t); } break;
1990
+ case PrimitiveKind::Int32S: { PUSH_INTEGER_SWAP(int32_t); } break;
1991
+ case PrimitiveKind::UInt32: { PUSH_INTEGER(uint32_t); } break;
1992
+ case PrimitiveKind::UInt32S: { PUSH_INTEGER_SWAP(uint32_t); } break;
1993
+ case PrimitiveKind::Int64: { PUSH_INTEGER(int64_t); } break;
1994
+ case PrimitiveKind::Int64S: { PUSH_INTEGER_SWAP(int64_t); } break;
1995
+ case PrimitiveKind::UInt64: { PUSH_INTEGER(uint64_t); } break;
1996
+ case PrimitiveKind::UInt64S: { PUSH_INTEGER_SWAP(uint64_t); } break;
1997
+ case PrimitiveKind::String: {
1998
+ const char *str;
1999
+ if (!call.PushString(value, 1, &str)) [[unlikely]]
2000
+ return false;
2001
+ *(const char **)origin = str;
2002
+ } break;
2003
+ case PrimitiveKind::String16: {
2004
+ const char16_t *str16;
2005
+ if (!call.PushString16(value, 1, &str16)) [[unlikely]]
2006
+ return false;
2007
+ *(const char16_t **)origin = str16;
2008
+ } break;
2009
+ case PrimitiveKind::String32: {
2010
+ const char32_t *str32;
2011
+ if (!call.PushString32(value, 1, &str32)) [[unlikely]]
2012
+ return false;
2013
+ *(const char32_t **)origin = str32;
2014
+ } break;
2015
+ case PrimitiveKind::Pointer: {
2016
+ void *ptr;
2017
+ if (!call.PushPointer(value, type, 1, &ptr)) [[unlikely]]
2018
+ return false;
2019
+ *(void **)origin = ptr;
2020
+ } break;
2021
+ case PrimitiveKind::Record:
2022
+ case PrimitiveKind::Union: {
2023
+ if (!call.PushObject(value, type, origin)) [[unlikely]]
2024
+ return false;
2025
+ } break;
2026
+ case PrimitiveKind::Array: {
2027
+ if (IsArray(env, value)) {
2028
+ Napi::Array array = Napi::Array(env, value);
2029
+ if (!call.PushNormalArray(array, type, type->size, origin))
2030
+ return false;
2031
+ } else if (Span<uint8_t> buffer = {}; TryBuffer(env, value, &buffer)) {
2032
+ call.PushBuffer(buffer, type, origin);
2033
+ } else if (GetKindOf(env, value) == napi_string) {
2034
+ if (!call.PushStringArray(value, type, origin))
2035
+ return false;
2036
+ } else {
2037
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected array", GetValueType(instance, value));
2038
+ return false;
2039
+ }
2040
+ } break;
2041
+ case PrimitiveKind::Float32: {
2042
+ float f;
2043
+ if (!TryNumber(env, value, &f)) [[unlikely]] {
2044
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value));
2045
+ return false;
2046
+ }
2047
+
2048
+ memcpy(origin, &f, 4);
2049
+ } break;
2050
+ case PrimitiveKind::Float64: {
2051
+ double d;
2052
+ if (!TryNumber(env, value, &d)) [[unlikely]] {
2053
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value));
2054
+ return false;
2055
+ }
2056
+
2057
+ memcpy(origin, &d, 8);
2058
+ } break;
2059
+ case PrimitiveKind::Callback: {
2060
+ void *ptr;
2061
+ if (!TryPointer(env, value, &ptr)) [[unlikely]] {
2062
+ if (GetKindOf(env, value) == napi_function) {
2063
+ ThrowError<Napi::Error>(env, "Cannot encode non-registered callback");
2064
+ } else {
2065
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected %2", GetValueType(instance, value), type->name);
2066
+ }
2067
+ return false;
2068
+ }
2069
+
2070
+ *(void **)origin = ptr;
2071
+ } break;
2072
+
2073
+ case PrimitiveKind::Prototype: { K_UNREACHABLE(); } break;
1855
2074
  }
1856
- }
1857
2075
 
1858
- void PerformAsyncRelay(napi_env, napi_value, void *, void *udata)
1859
- {
1860
- RelayContext *ctx = (RelayContext *)udata;
1861
- CallData *call = ctx->call;
2076
+ #undef PUSH_INTEGER_SWAP
2077
+ #undef PUSH_INTEGER
1862
2078
 
1863
- call->Relay(ctx->idx, ctx->sp);
1864
- call->Finalize();
2079
+ // Keep memory around if any was allocated
2080
+ if (mem.allocator.IsUsed()) {
2081
+ LinkedAllocator *copy = instance->encode_map.FindValue(origin, nullptr);
1865
2082
 
1866
- // We're done!
1867
- std::lock_guard<std::mutex> lock(ctx->mutex);
1868
- ctx->done = true;
1869
- ctx->cv.notify_one();
1870
- }
2083
+ if (!copy) {
2084
+ copy = instance->encode_allocators.AppendDefault();
2085
+ instance->encode_map.Set(origin, copy);
2086
+ }
1871
2087
 
1872
- void *GetTrampoline(int idx)
1873
- {
1874
- return (void *)(TrampolineStart + TrampolineSize * idx);
2088
+ std::swap(mem.allocator, *copy);
2089
+ }
2090
+
2091
+ return true;
1875
2092
  }
1876
2093
 
1877
2094
  }