koffi 1.3.5 → 1.3.8

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 (106) hide show
  1. package/CMakeLists.txt +1 -1
  2. package/ChangeLog.md +36 -0
  3. package/benchmark/atoi_koffi.js +3 -4
  4. package/benchmark/atoi_napi.js +2 -3
  5. package/benchmark/atoi_node_ffi.js +3 -4
  6. package/benchmark/raylib_cc.cc +3 -4
  7. package/benchmark/raylib_cc.js +31 -0
  8. package/benchmark/raylib_koffi.js +8 -9
  9. package/benchmark/raylib_node_ffi.js +4 -5
  10. package/benchmark/raylib_node_raylib.js +4 -5
  11. package/build/qemu/1.3.8/koffi_darwin_arm64.tar.gz +0 -0
  12. package/build/qemu/1.3.8/koffi_darwin_x64.tar.gz +0 -0
  13. package/build/qemu/1.3.8/koffi_freebsd_arm64.tar.gz +0 -0
  14. package/build/qemu/1.3.8/koffi_freebsd_ia32.tar.gz +0 -0
  15. package/build/qemu/1.3.8/koffi_freebsd_x64.tar.gz +0 -0
  16. package/build/qemu/1.3.8/koffi_linux_arm32hf.tar.gz +0 -0
  17. package/build/qemu/1.3.8/koffi_linux_arm64.tar.gz +0 -0
  18. package/build/qemu/1.3.8/koffi_linux_ia32.tar.gz +0 -0
  19. package/build/qemu/1.3.8/koffi_linux_riscv64hf64.tar.gz +0 -0
  20. package/build/qemu/1.3.8/koffi_linux_x64.tar.gz +0 -0
  21. package/build/qemu/1.3.8/koffi_openbsd_ia32.tar.gz +0 -0
  22. package/build/qemu/1.3.8/koffi_openbsd_x64.tar.gz +0 -0
  23. package/build/qemu/1.3.8/koffi_win32_arm64.tar.gz +0 -0
  24. package/build/qemu/1.3.8/koffi_win32_ia32.tar.gz +0 -0
  25. package/build/qemu/1.3.8/koffi_win32_x64.tar.gz +0 -0
  26. package/doc/_static/perf_linux_20220627.png +0 -0
  27. package/doc/_static/perf_linux_20220628.png +0 -0
  28. package/doc/_static/perf_windows_20220627.png +0 -0
  29. package/doc/_static/perf_windows_20220628.png +0 -0
  30. package/doc/benchmarks.md +78 -58
  31. package/doc/benchmarks.xlsx +0 -0
  32. package/doc/conf.py +1 -1
  33. package/doc/contribute.md +8 -11
  34. package/doc/dist/html/_sources/benchmarks.md.txt +78 -58
  35. package/doc/dist/html/_sources/contribute.md.txt +8 -11
  36. package/doc/dist/html/_sources/functions.md.txt +9 -8
  37. package/doc/dist/html/_sources/index.rst.txt +3 -0
  38. package/doc/dist/html/_sources/platforms.md.txt +17 -5
  39. package/doc/dist/html/_sources/start.md.txt +14 -3
  40. package/doc/dist/html/_sources/types.md.txt +15 -11
  41. package/doc/dist/html/_static/basic.css +12 -14
  42. package/doc/dist/html/_static/perf_linux_20220627.png +0 -0
  43. package/doc/dist/html/_static/perf_linux_20220628.png +0 -0
  44. package/doc/dist/html/_static/perf_windows_20220627.png +0 -0
  45. package/doc/dist/html/_static/perf_windows_20220628.png +0 -0
  46. package/doc/dist/html/benchmarks.html +148 -159
  47. package/doc/dist/html/changes.html +44 -2
  48. package/doc/dist/html/contribute.html +30 -33
  49. package/doc/dist/html/functions.html +19 -18
  50. package/doc/dist/html/genindex.html +2 -2
  51. package/doc/dist/html/index.html +19 -10
  52. package/doc/dist/html/memory.html +2 -2
  53. package/doc/dist/html/objects.inv +0 -0
  54. package/doc/dist/html/platforms.html +44 -10
  55. package/doc/dist/html/search.html +2 -2
  56. package/doc/dist/html/searchindex.js +1 -1
  57. package/doc/dist/html/start.html +25 -12
  58. package/doc/dist/html/types.html +31 -11
  59. package/doc/functions.md +9 -8
  60. package/doc/index.rst +3 -0
  61. package/doc/platforms.md +17 -5
  62. package/doc/start.md +14 -3
  63. package/doc/types.md +15 -11
  64. package/package.json +7 -4
  65. package/qemu/qemu.js +30 -19
  66. package/qemu/registry/machines.json +19 -19
  67. package/qemu/registry/sha256sum.txt +5 -5
  68. package/src/abi_arm32.cc +9 -2
  69. package/src/abi_arm32_fwd.S +7 -7
  70. package/src/abi_arm64.cc +9 -2
  71. package/src/abi_arm64_fwd.S +11 -7
  72. package/src/abi_arm64_fwd.asm +7 -7
  73. package/src/abi_riscv64.cc +9 -2
  74. package/src/abi_riscv64_fwd.S +11 -11
  75. package/src/abi_x64_sysv.cc +9 -2
  76. package/src/abi_x64_sysv_fwd.S +11 -11
  77. package/src/abi_x64_win.cc +9 -2
  78. package/src/abi_x64_win_fwd.asm +7 -7
  79. package/src/abi_x86.cc +9 -2
  80. package/src/abi_x86_fwd.S +3 -0
  81. package/src/abi_x86_fwd.asm +3 -0
  82. package/src/call.cc +20 -10
  83. package/src/ffi.cc +17 -8
  84. package/src/ffi.hh +4 -3
  85. package/src/util.cc +1 -1
  86. package/test/async.js +1 -1
  87. package/test/callbacks.js +25 -2
  88. package/test/misc.c +57 -2
  89. package/test/raylib.js +4 -4
  90. package/test/sqlite.js +5 -5
  91. package/test/sync.js +22 -7
  92. package/build/qemu/1.3.5/koffi_darwin_arm64.tar.gz +0 -0
  93. package/build/qemu/1.3.5/koffi_darwin_x64.tar.gz +0 -0
  94. package/build/qemu/1.3.5/koffi_freebsd_arm64.tar.gz +0 -0
  95. package/build/qemu/1.3.5/koffi_freebsd_ia32.tar.gz +0 -0
  96. package/build/qemu/1.3.5/koffi_freebsd_x64.tar.gz +0 -0
  97. package/build/qemu/1.3.5/koffi_linux_arm32hf.tar.gz +0 -0
  98. package/build/qemu/1.3.5/koffi_linux_arm64.tar.gz +0 -0
  99. package/build/qemu/1.3.5/koffi_linux_ia32.tar.gz +0 -0
  100. package/build/qemu/1.3.5/koffi_linux_riscv64hf64.tar.gz +0 -0
  101. package/build/qemu/1.3.5/koffi_linux_x64.tar.gz +0 -0
  102. package/build/qemu/1.3.5/koffi_openbsd_ia32.tar.gz +0 -0
  103. package/build/qemu/1.3.5/koffi_openbsd_x64.tar.gz +0 -0
  104. package/build/qemu/1.3.5/koffi_win32_arm64.tar.gz +0 -0
  105. package/build/qemu/1.3.5/koffi_win32_ia32.tar.gz +0 -0
  106. package/build/qemu/1.3.5/koffi_win32_x64.tar.gz +0 -0
@@ -66,7 +66,7 @@
66
66
  .endm
67
67
 
68
68
  # Prepare integer argument registers from array passed by caller.
69
- .macro forward_int
69
+ .macro forward_gpr
70
70
  movq 40(%rsi), %r9
71
71
  movq 32(%rsi), %r8
72
72
  movq 24(%rsi), %rcx
@@ -89,66 +89,66 @@
89
89
 
90
90
  SYMBOL(ForwardCallGG):
91
91
  prologue
92
- forward_int
92
+ forward_gpr
93
93
  movb $0, %al
94
94
  epilogue
95
95
 
96
96
  SYMBOL(ForwardCallF):
97
97
  prologue
98
- forward_int
98
+ forward_gpr
99
99
  movb $0, %al
100
100
  epilogue
101
101
 
102
102
  SYMBOL(ForwardCallDG):
103
103
  prologue
104
- forward_int
104
+ forward_gpr
105
105
  movb $0, %al
106
106
  epilogue
107
107
 
108
108
  SYMBOL(ForwardCallGD):
109
109
  prologue
110
- forward_int
110
+ forward_gpr
111
111
  movb $0, %al
112
112
  epilogue
113
113
 
114
114
  SYMBOL(ForwardCallDD):
115
115
  prologue
116
- forward_int
116
+ forward_gpr
117
117
  movb $0, %al
118
118
  epilogue
119
119
 
120
120
  SYMBOL(ForwardCallXGG):
121
121
  prologue
122
122
  forward_xmm
123
- forward_int
123
+ forward_gpr
124
124
  movb $8, %al
125
125
  epilogue
126
126
 
127
127
  SYMBOL(ForwardCallXF):
128
128
  prologue
129
129
  forward_xmm
130
- forward_int
130
+ forward_gpr
131
131
  movb $8, %al
132
132
  epilogue
133
133
 
134
134
  SYMBOL(ForwardCallXDG):
135
135
  prologue
136
136
  forward_xmm
137
- forward_int
137
+ forward_gpr
138
138
  movb $8, %al
139
139
  epilogue
140
140
 
141
141
  SYMBOL(ForwardCallXGD):
142
142
  prologue
143
143
  forward_xmm
144
- forward_int
144
+ forward_gpr
145
145
  movb $8, %al
146
146
  epilogue
147
147
 
148
148
  SYMBOL(ForwardCallXDD):
149
149
  prologue
150
150
  forward_xmm
151
- forward_int
151
+ forward_gpr
152
152
  movb $8, %al
153
153
  epilogue
154
154
 
@@ -337,8 +337,15 @@ Napi::Value CallData::Complete()
337
337
 
338
338
  void CallData::Relay(Size idx, uint8_t *own_sp, uint8_t *caller_sp, BackRegisters *out_reg)
339
339
  {
340
- const FunctionInfo *proto = instance->trampolines[idx].proto;
341
- Napi::Function func = instance->trampolines[idx].func;
340
+ const TrampolineInfo &trampoline = instance->trampolines[idx];
341
+
342
+ if (RG_UNLIKELY(trampoline.generation != mem->generation)) {
343
+ ThrowError<Napi::Error>(env, "Cannot use non-persistent callback beyond FFI call");
344
+ return;
345
+ }
346
+
347
+ const FunctionInfo *proto = trampoline.proto;
348
+ Napi::Function func = trampoline.func.Value();
342
349
 
343
350
  uint64_t *gpr_ptr = (uint64_t *)own_sp;
344
351
  uint64_t *xmm_ptr = gpr_ptr + 4;
@@ -53,7 +53,7 @@ epilogue macro
53
53
  endm
54
54
 
55
55
  ; Prepare integer argument registers from array passed by caller.
56
- forward_int macro
56
+ forward_gpr macro
57
57
  mov r9, qword ptr [rdx+24]
58
58
  mov r8, qword ptr [rdx+16]
59
59
  mov rcx, qword ptr [rdx+0]
@@ -70,40 +70,40 @@ endm
70
70
 
71
71
  ForwardCallG proc frame
72
72
  prologue
73
- forward_int
73
+ forward_gpr
74
74
  epilogue
75
75
  ForwardCallG endp
76
76
 
77
77
  ForwardCallF proc frame
78
78
  prologue
79
- forward_int
79
+ forward_gpr
80
80
  epilogue
81
81
  ForwardCallF endp
82
82
 
83
83
  ForwardCallD proc frame
84
84
  prologue
85
- forward_int
85
+ forward_gpr
86
86
  epilogue
87
87
  ForwardCallD endp
88
88
 
89
89
  ForwardCallXG proc frame
90
90
  prologue
91
91
  forward_xmm
92
- forward_int
92
+ forward_gpr
93
93
  epilogue
94
94
  ForwardCallXG endp
95
95
 
96
96
  ForwardCallXF proc frame
97
97
  prologue
98
98
  forward_xmm
99
- forward_int
99
+ forward_gpr
100
100
  epilogue
101
101
  ForwardCallXF endp
102
102
 
103
103
  ForwardCallXD proc frame
104
104
  prologue
105
105
  forward_xmm
106
- forward_int
106
+ forward_gpr
107
107
  epilogue
108
108
  ForwardCallXD endp
109
109
 
package/src/abi_x86.cc CHANGED
@@ -411,8 +411,15 @@ Napi::Value CallData::Complete()
411
411
 
412
412
  void CallData::Relay(Size idx, uint8_t *own_sp, uint8_t *caller_sp, BackRegisters *out_reg)
413
413
  {
414
- const FunctionInfo *proto = instance->trampolines[idx].proto;
415
- Napi::Function func = instance->trampolines[idx].func;
414
+ const TrampolineInfo &trampoline = instance->trampolines[idx];
415
+
416
+ if (RG_UNLIKELY(trampoline.generation != mem->generation)) {
417
+ ThrowError<Napi::Error>(env, "Cannot use non-persistent callback beyond FFI call");
418
+ return;
419
+ }
420
+
421
+ const FunctionInfo *proto = trampoline.proto;
422
+ Napi::Function func = trampoline.func.Value();
416
423
 
417
424
  uint32_t *args_ptr = (uint32_t *)caller_sp;
418
425
 
package/src/abi_x86_fwd.S CHANGED
@@ -124,6 +124,9 @@ ForwardCallRD:
124
124
  # static trampoline ID, the current stack pointer, a pointer to the stack arguments of this call,
125
125
  # and a pointer to a struct that will contain the result registers.
126
126
  # After the call, simply load these registers from the output struct.
127
+ # Depending on ABI, call convention and return value size, we need to issue ret <something>. Since ret
128
+ # only takes an immediate value, and I prefer not to branch, the return address is moved instead according
129
+ # to BackRegisters::ret_pop before ret is issued.
127
130
  .macro trampoline id
128
131
  .cfi_startproc
129
132
  .cfi_def_cfa esp, 4
@@ -128,6 +128,9 @@ public CallSwitchStack
128
128
  ; static trampoline ID, the current stack pointer, a pointer to the stack arguments of this call,
129
129
  ; and a pointer to a struct that will contain the result registers.
130
130
  ; After the call, simply load these registers from the output struct.
131
+ ; Depending on ABI, call convention and return value size, we need to issue ret <something>. Since ret
132
+ ; only takes an immediate value, and I prefer not to branch, the return address is moved instead according
133
+ ; to BackRegisters::ret_pop before ret is issued.
131
134
  trampoline macro ID
132
135
  endbr32
133
136
  sub esp, 44
package/src/call.cc CHANGED
@@ -24,6 +24,7 @@ CallData::CallData(Napi::Env env, InstanceData *instance, const FunctionInfo *fu
24
24
  : env(env), instance(instance), func(func),
25
25
  mem(mem), old_stack_mem(mem->stack), old_heap_mem(mem->heap)
26
26
  {
27
+ mem->generation += !mem->depth;
27
28
  mem->depth++;
28
29
 
29
30
  RG_ASSERT(AlignUp(mem->stack.ptr, 16) == mem->stack.ptr);
@@ -226,25 +227,33 @@ bool CallData::PushObject(Napi::Object obj, const TypeInfo *type, uint8_t *origi
226
227
  *(uint64_t *)dest = v;
227
228
  } break;
228
229
  case PrimitiveKind::String: {
229
- if (RG_UNLIKELY(!value.IsString())) {
230
+ const char *str;
231
+ if (RG_LIKELY(value.IsString())) {
232
+ str = PushString(value);
233
+ if (RG_UNLIKELY(!str))
234
+ return false;
235
+ } else if (IsNullOrUndefined(value)) {
236
+ str = nullptr;
237
+ } else {
230
238
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for member '%2', expected string", GetValueType(instance, value), member.name);
231
239
  return false;
232
240
  }
233
241
 
234
- const char *str = PushString(value);
235
- if (RG_UNLIKELY(!str))
236
- return false;
237
242
  *(const char **)dest = str;
238
243
  } break;
239
244
  case PrimitiveKind::String16: {
240
- if (RG_UNLIKELY(!value.IsString())) {
245
+ const char16_t *str16;
246
+ if (RG_LIKELY(value.IsString())) {
247
+ str16 = PushString16(value);
248
+ if (RG_UNLIKELY(!str16))
249
+ return false;
250
+ } else if (IsNullOrUndefined(value)) {
251
+ str16 = nullptr;
252
+ } else {
241
253
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for member '%2', expected string", GetValueType(instance, value), member.name);
242
254
  return false;
243
255
  }
244
256
 
245
- const char16_t *str16 = PushString16(value);
246
- if (RG_UNLIKELY(!str16))
247
- return false;
248
257
  *(const char16_t **)dest = str16;
249
258
  } break;
250
259
  case PrimitiveKind::Pointer: {
@@ -314,7 +323,7 @@ bool CallData::PushObject(Napi::Object obj, const TypeInfo *type, uint8_t *origi
314
323
  if (value.IsFunction()) {
315
324
  Napi::Function func = value.As<Napi::Function>();
316
325
 
317
- ptr = ReserveTrampoline(type->proto, func);
326
+ ptr = ReserveTrampoline(member.type->proto, func);
318
327
  if (RG_UNLIKELY(!ptr))
319
328
  return false;
320
329
  } else if (CheckValueTag(instance, value, member.type)) {
@@ -748,7 +757,8 @@ void *CallData::ReserveTrampoline(const FunctionInfo *proto, Napi::Function func
748
757
  used_trampolines |= 1u << idx;
749
758
 
750
759
  instance->trampolines[idx].proto = proto;
751
- instance->trampolines[idx].func = func;
760
+ instance->trampolines[idx].func.Reset(func, 1);
761
+ instance->trampolines[idx].generation = mem->generation;
752
762
 
753
763
  void *trampoline = GetTrampoline(idx, proto);
754
764
  return trampoline;
package/src/ffi.cc CHANGED
@@ -550,7 +550,7 @@ static Napi::Value CreateCallbackType(const Napi::CallbackInfo &info)
550
550
  if (!ParsePrototype(env, proto.c_str(), func))
551
551
  return env.Null();
552
552
  } else {
553
- ThrowError<Napi::TypeError>(env, "Expected 1 or 3 arguments, not %1", info.Length());
553
+ ThrowError<Napi::TypeError>(env, "Expected 1 or 3 arguments, got %1", info.Length());
554
554
  return env.Null();
555
555
  }
556
556
 
@@ -894,7 +894,7 @@ static Napi::Value FindLibraryFunction(const Napi::CallbackInfo &info, CallConve
894
894
  if (!ParsePrototype(env, proto.c_str(), func))
895
895
  return env.Null();
896
896
  } else {
897
- ThrowError<Napi::TypeError>(env, "Expected 1 or 3 arguments, not %1", info.Length());
897
+ ThrowError<Napi::TypeError>(env, "Expected 1 or 3 arguments, got %1", info.Length());
898
898
  return env.Null();
899
899
  }
900
900
 
@@ -957,7 +957,7 @@ static Napi::Value LoadSharedLibrary(const Napi::CallbackInfo &info)
957
957
  InstanceData *instance = env.GetInstanceData<InstanceData>();
958
958
 
959
959
  if (info.Length() < 1) {
960
- ThrowError<Napi::TypeError>(env, "Expected 2 arguments, not %1", info.Length());
960
+ ThrowError<Napi::TypeError>(env, "Expected 1 or 2 arguments, got %1", info.Length());
961
961
  return env.Null();
962
962
  }
963
963
  if (!info[0].IsString() && !IsNullOrUndefined(info[0])) {
@@ -1075,9 +1075,12 @@ static void RegisterPrimitiveType(InstanceData *instance, const char *name, Prim
1075
1075
  instance->types_map.Set(type);
1076
1076
  }
1077
1077
 
1078
- static inline PrimitiveKind GetLongPrimitive(bool sign)
1078
+ template <typename T>
1079
+ static inline PrimitiveKind GetIntegerPrimitive(bool sign)
1079
1080
  {
1080
- switch (RG_SIZE(long)) {
1081
+ switch (RG_SIZE(T)) {
1082
+ case 1: return sign ? PrimitiveKind::Int8 : PrimitiveKind::UInt8;
1083
+ case 2: return sign ? PrimitiveKind::Int16 : PrimitiveKind::UInt16;
1081
1084
  case 4: return sign ? PrimitiveKind::Int32 : PrimitiveKind::UInt32;
1082
1085
  case 8: return sign ? PrimitiveKind::Int64 : PrimitiveKind::UInt64;
1083
1086
  }
@@ -1121,9 +1124,13 @@ static Napi::Object InitBaseTypes(Napi::Env env)
1121
1124
  RegisterPrimitiveType(instance, "int64_t", PrimitiveKind::Int64, 8, alignof(int64_t));
1122
1125
  RegisterPrimitiveType(instance, "uint64", PrimitiveKind::UInt64, 8, alignof(int64_t));
1123
1126
  RegisterPrimitiveType(instance, "uint64_t", PrimitiveKind::UInt64, 8, alignof(int64_t));
1124
- RegisterPrimitiveType(instance, "long", GetLongPrimitive(true), RG_SIZE(long), alignof(long));
1125
- RegisterPrimitiveType(instance, "ulong", GetLongPrimitive(false), RG_SIZE(long), alignof(long));
1126
- RegisterPrimitiveType(instance, "unsigned long", GetLongPrimitive(false), RG_SIZE(long), alignof(long));
1127
+ RegisterPrimitiveType(instance, "intptr", GetIntegerPrimitive<intptr_t>(true), RG_SIZE(intptr_t), alignof(intptr_t));
1128
+ RegisterPrimitiveType(instance, "intptr_t", GetIntegerPrimitive<intptr_t>(true), RG_SIZE(intptr_t), alignof(intptr_t));
1129
+ RegisterPrimitiveType(instance, "uintptr", GetIntegerPrimitive<intptr_t>(false), RG_SIZE(intptr_t), alignof(intptr_t));
1130
+ RegisterPrimitiveType(instance, "uintptr_t", GetIntegerPrimitive<intptr_t>(false), RG_SIZE(intptr_t), alignof(intptr_t));
1131
+ RegisterPrimitiveType(instance, "long", GetIntegerPrimitive<long>(true), RG_SIZE(long), alignof(long));
1132
+ RegisterPrimitiveType(instance, "ulong", GetIntegerPrimitive<long>(false), RG_SIZE(long), alignof(long));
1133
+ RegisterPrimitiveType(instance, "unsigned long", GetIntegerPrimitive<long>(false), RG_SIZE(long), alignof(long));
1127
1134
  RegisterPrimitiveType(instance, "longlong", PrimitiveKind::Int64, RG_SIZE(int64_t), alignof(int64_t));
1128
1135
  RegisterPrimitiveType(instance, "long long", PrimitiveKind::Int64, RG_SIZE(int64_t), alignof(int64_t));
1129
1136
  RegisterPrimitiveType(instance, "ulonglong", PrimitiveKind::UInt64, RG_SIZE(uint64_t), alignof(uint64_t));
@@ -1134,6 +1141,8 @@ static Napi::Object InitBaseTypes(Napi::Env env)
1134
1141
  RegisterPrimitiveType(instance, "double", PrimitiveKind::Float64, 8, alignof(double));
1135
1142
  RegisterPrimitiveType(instance, "string", PrimitiveKind::String, RG_SIZE(void *), alignof(void *));
1136
1143
  RegisterPrimitiveType(instance, "string16", PrimitiveKind::String16, RG_SIZE(void *), alignof(void *));
1144
+ RegisterPrimitiveType(instance, "str", PrimitiveKind::String, RG_SIZE(void *), alignof(void *));
1145
+ RegisterPrimitiveType(instance, "str16", PrimitiveKind::String16, RG_SIZE(void *), alignof(void *));
1137
1146
 
1138
1147
  Napi::Object types = Napi::Object::New(env);
1139
1148
  for (TypeInfo &type: instance->types) {
package/src/ffi.hh CHANGED
@@ -86,7 +86,6 @@ struct TypeInfo {
86
86
  };
87
87
 
88
88
  const char *name;
89
- napi_type_tag tag;
90
89
 
91
90
  Napi::ObjectReference defn;
92
91
 
@@ -199,13 +198,15 @@ struct InstanceMemory {
199
198
  Span<uint8_t> stack;
200
199
  Span<uint8_t> heap;
201
200
 
202
- int depth;
201
+ uint16_t generation; // Can wrap without risk
202
+ int16_t depth;
203
203
  bool temporary;
204
204
  };
205
205
 
206
206
  struct TrampolineInfo {
207
207
  const FunctionInfo *proto;
208
- Napi::Function func;
208
+ Napi::FunctionReference func;
209
+ uint16_t generation;
209
210
  };
210
211
 
211
212
  struct InstanceData {
package/src/util.cc CHANGED
@@ -28,7 +28,7 @@ const TypeInfo *ResolveType(const InstanceData *instance, Napi::Value value, int
28
28
  const TypeInfo *type = instance->types_map.FindValue(str.c_str(), nullptr);
29
29
 
30
30
  if (!type) {
31
- ThrowError<Napi::TypeError>(value.Env(), "Unknown type string '%1'", str.c_str());
31
+ ThrowError<Napi::TypeError>(value.Env(), "Unknown type name '%1'", str.c_str());
32
32
  return nullptr;
33
33
  }
34
34
 
package/test/async.js CHANGED
@@ -21,7 +21,7 @@ const PackedBFG = koffi.pack('PackedBFG', {
21
21
  a: 'int8_t',
22
22
  b: 'int64_t',
23
23
  c: 'char',
24
- d: 'string',
24
+ d: 'str',
25
25
  e: 'short',
26
26
  inner: koffi.pack({
27
27
  f: 'float',
package/test/callbacks.js CHANGED
@@ -21,7 +21,7 @@ const BFG = koffi.struct('BFG', {
21
21
  a: 'int8_t',
22
22
  b: 'int64_t',
23
23
  c: 'char',
24
- d: 'string',
24
+ d: 'str',
25
25
  e: 'short',
26
26
  inner: koffi.struct({
27
27
  f: 'float',
@@ -30,9 +30,16 @@ const BFG = koffi.struct('BFG', {
30
30
  });
31
31
 
32
32
  const SimpleCallback = koffi.callback('int SimpleCallback(const char *str)');
33
- const RecursiveCallback = koffi.callback('RecursiveCallback', 'float', ['int', 'string', 'double']);
33
+ const RecursiveCallback = koffi.callback('RecursiveCallback', 'float', ['int', 'str', 'double']);
34
34
  const BigCallback = koffi.callback('BFG BigCallback(BFG bfg)');
35
35
  const ApplyCallback = koffi.callback('int __stdcall ApplyCallback(int a, int b, int c)');
36
+ const IntCallback = koffi.callback('int IntCallback(int x)');
37
+
38
+ const StructCallbacks = koffi.struct('StructCallbacks', {
39
+ first: IntCallback,
40
+ second: IntCallback,
41
+ third: IntCallback
42
+ });
36
43
 
37
44
  main();
38
45
 
@@ -54,6 +61,8 @@ async function test() {
54
61
  const CallRecursiveJS = lib.func('float CallRecursiveJS(int i, RecursiveCallback func)');
55
62
  const ModifyBFG = lib.func('BFG ModifyBFG(int x, double y, const char *str, BigCallback func, _Out_ BFG *p)');
56
63
  const ApplyStd = lib.func('int ApplyStd(int a, int b, int c, ApplyCallback func)');
64
+ const ApplyMany = lib.func('int ApplyMany(int x, IntCallback *funcs, int length)');
65
+ const ApplyStruct = lib.func('int ApplyStruct(int x, StructCallbacks callbacks)');
57
66
 
58
67
  // Simple test similar to README example
59
68
  {
@@ -92,4 +101,18 @@ async function test() {
92
101
  let ret = ApplyStd(1, 5, 9, (a, b, c) => a + b * c);
93
102
  assert.equal(ret, 46);
94
103
  }
104
+
105
+ // Array of callbacks
106
+ {
107
+ let callbacks = [x => x * 5, x => x - 42, x => -x];
108
+ let ret = ApplyMany(27, callbacks, callbacks.length);
109
+ assert.equal(ret, -93);
110
+ }
111
+
112
+ // Struct of callbacks
113
+ {
114
+ let callbacks = { first: x => -x, second: x => x * 5, third: x => x - 42 };
115
+ let ret = ApplyStruct(27, callbacks);
116
+ assert.equal(ret, -177);
117
+ }
95
118
  }
package/test/misc.c CHANGED
@@ -129,6 +129,20 @@ typedef struct IntContainer {
129
129
  int len;
130
130
  } IntContainer;
131
131
 
132
+ typedef struct StrStruct {
133
+ const char *str;
134
+ const char16_t *str16;
135
+ } StrStruct;
136
+
137
+ typedef int STDCALL ApplyCallback(int a, int b, int c);
138
+ typedef int IntCallback(int x);
139
+
140
+ typedef struct StructCallbacks {
141
+ IntCallback *first;
142
+ IntCallback *second;
143
+ IntCallback *third;
144
+ } StructCallbacks;
145
+
132
146
  EXPORT int8_t GetMinusOne1(void)
133
147
  {
134
148
  return -1;
@@ -385,7 +399,12 @@ EXPORT PackedBFG FASTCALL MakePackedBFG(int x, double y, PackedBFG *p, const cha
385
399
  return bfg;
386
400
  }
387
401
 
402
+ #ifdef _WIN32
403
+ // Exported by DEF file
404
+ const char *ReturnBigString(const char *str)
405
+ #else
388
406
  EXPORT const char *ReturnBigString(const char *str)
407
+ #endif
389
408
  {
390
409
  static char buf[16 * 1024 * 1024];
391
410
 
@@ -530,8 +549,6 @@ EXPORT BFG ModifyBFG(int x, double y, const char *str, BFG (*func)(BFG bfg), BFG
530
549
  return bfg;
531
550
  }
532
551
 
533
- typedef int STDCALL ApplyCallback(int a, int b, int c);
534
-
535
552
  EXPORT int ApplyStd(int a, int b, int c, ApplyCallback *func)
536
553
  {
537
554
  int ret = func(a, b, c);
@@ -563,3 +580,41 @@ EXPORT void MultiplyIntegers(int multiplier, int *values, int len)
563
580
  values[i] *= multiplier;
564
581
  }
565
582
  }
583
+
584
+ EXPORT const char *ThroughStr(StrStruct s)
585
+ {
586
+ return s.str;
587
+ }
588
+
589
+ EXPORT const char16_t *ThroughStr16(StrStruct s)
590
+ {
591
+ return s.str16;
592
+ }
593
+
594
+ EXPORT const char *ThroughStrStar(StrStruct *s)
595
+ {
596
+ return s->str;
597
+ }
598
+
599
+ EXPORT const char16_t *ThroughStrStar16(StrStruct *s)
600
+ {
601
+ return s->str16;
602
+ }
603
+
604
+ EXPORT int ApplyMany(int x, IntCallback **callbacks, int length)
605
+ {
606
+ for (int i = 0; i < length; i++) {
607
+ x = (callbacks[i])(x);
608
+ }
609
+
610
+ return x;
611
+ }
612
+
613
+ EXPORT int ApplyStruct(int x, StructCallbacks callbacks)
614
+ {
615
+ x = callbacks.first(x);
616
+ x = callbacks.second(x);
617
+ x = callbacks.third(x);
618
+
619
+ return x;
620
+ }
package/test/raylib.js CHANGED
@@ -86,14 +86,14 @@ async function test() {
86
86
  let lib_filename = path.dirname(__filename) + '/build/raylib' + koffi.extension;
87
87
  let lib = koffi.load(lib_filename);
88
88
 
89
- const InitWindow = lib.func('InitWindow', 'void', ['int', 'int', 'string']);
89
+ const InitWindow = lib.func('InitWindow', 'void', ['int', 'int', 'str']);
90
90
  const SetTraceLogLevel = lib.func('SetTraceLogLevel', 'void', ['int']);
91
91
  const SetWindowState = lib.func('SetWindowState', 'void', ['uint']);
92
92
  const GenImageColor = lib.func('GenImageColor', Image, ['int', 'int', Color]);
93
93
  const GetFontDefault = lib.func('GetFontDefault', Font, []);
94
- const MeasureTextEx = lib.func('MeasureTextEx', Vector2, [Font, 'string', 'float', 'float']);
95
- const ImageDrawTextEx = lib.func('ImageDrawTextEx', 'void', [koffi.pointer(Image), Font, 'string', Vector2, 'float', 'float', Color]);
96
- const ExportImage = lib.func('ExportImage', 'bool', [Image, 'string']);
94
+ const MeasureTextEx = lib.func('MeasureTextEx', Vector2, [Font, 'str', 'float', 'float']);
95
+ const ImageDrawTextEx = lib.func('ImageDrawTextEx', 'void', [koffi.pointer(Image), Font, 'str', Vector2, 'float', 'float', Color]);
96
+ const ExportImage = lib.func('ExportImage', 'bool', [Image, 'str']);
97
97
 
98
98
  // We need to call InitWindow before using anything else (such as fonts)
99
99
  SetTraceLogLevel(4); // Warnings
package/test/sqlite.js CHANGED
@@ -39,13 +39,13 @@ async function test() {
39
39
  let lib_filename = path.dirname(__filename) + '/build/sqlite3' + koffi.extension;
40
40
  let lib = koffi.load(lib_filename);
41
41
 
42
- const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['string', koffi.out(koffi.pointer(sqlite3_db)), 'int', 'string']);
43
- const sqlite3_exec = lib.func('sqlite3_exec', 'int', [sqlite3_db, 'string', 'void *', 'void *', 'void *']);
44
- const sqlite3_prepare_v2 = lib.func('sqlite3_prepare_v2', 'int', [sqlite3_db, 'string', 'int', koffi.out(koffi.pointer(sqlite3_stmt)), 'string']);
42
+ const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(koffi.pointer(sqlite3_db)), 'int', 'str']);
43
+ const sqlite3_exec = lib.func('sqlite3_exec', 'int', [sqlite3_db, 'str', 'void *', 'void *', 'void *']);
44
+ const sqlite3_prepare_v2 = lib.func('sqlite3_prepare_v2', 'int', [sqlite3_db, 'str', 'int', koffi.out(koffi.pointer(sqlite3_stmt)), 'string']);
45
45
  const sqlite3_reset = lib.func('sqlite3_reset', 'int', [sqlite3_stmt]);
46
- const sqlite3_bind_text = lib.func('sqlite3_bind_text', 'int', [sqlite3_stmt, 'int', 'string', 'int', 'void *']);
46
+ const sqlite3_bind_text = lib.func('sqlite3_bind_text', 'int', [sqlite3_stmt, 'int', 'str', 'int', 'void *']);
47
47
  const sqlite3_bind_int = lib.func('sqlite3_bind_int', 'int', [sqlite3_stmt, 'int', 'int']);
48
- const sqlite3_column_text = lib.func('sqlite3_column_text', 'string', [sqlite3_stmt, 'int']);
48
+ const sqlite3_column_text = lib.func('sqlite3_column_text', 'str', [sqlite3_stmt, 'int']);
49
49
  const sqlite3_column_int = lib.func('sqlite3_column_int', 'int', [sqlite3_stmt, 'int']);
50
50
  const sqlite3_step = lib.func('sqlite3_step', 'int', [sqlite3_stmt]);
51
51
  const sqlite3_finalize = lib.func('sqlite3_finalize', 'int', [sqlite3_stmt]);
package/test/sync.js CHANGED
@@ -64,7 +64,7 @@ const BFG = koffi.struct('BFG', {
64
64
  a: 'int8_t',
65
65
  b: 'int64_t',
66
66
  c: 'char',
67
- d: 'string',
67
+ d: 'str',
68
68
  e: 'short',
69
69
  inner: koffi.struct({
70
70
  f: 'float',
@@ -75,7 +75,7 @@ const PackedBFG = koffi.pack('PackedBFG', {
75
75
  a: 'int8_t',
76
76
  b: 'int64_t',
77
77
  c: 'char',
78
- d: 'string',
78
+ d: 'str',
79
79
  e: 'short',
80
80
  inner: koffi.pack({
81
81
  f: 'float',
@@ -106,6 +106,11 @@ const IntContainer = koffi.struct('IntContainer', {
106
106
  len: 'int'
107
107
  });
108
108
 
109
+ const StrStruct = koffi.struct('StrStruct', {
110
+ str: 'str',
111
+ str16: 'str16'
112
+ });
113
+
109
114
  main();
110
115
 
111
116
  async function main() {
@@ -146,13 +151,13 @@ async function test() {
146
151
  const ConcatenateToInt1 = lib.func('ConcatenateToInt1', 'int64_t', Array(12).fill('int8_t'));
147
152
  const ConcatenateToInt4 = lib.func('ConcatenateToInt4', 'int64_t', Array(12).fill('int32_t'));
148
153
  const ConcatenateToInt8 = lib.func('ConcatenateToInt8', 'int64_t', Array(12).fill('int64_t'));
149
- const ConcatenateToStr1 = lib.func('ConcatenateToStr1', 'string', [...Array(8).fill('int8_t'), koffi.struct('IJK1', {i: 'int8_t', j: 'int8_t', k: 'int8_t'}), 'int8_t']);
150
- const ConcatenateToStr4 = lib.func('ConcatenateToStr4', 'string', [...Array(8).fill('int32_t'), koffi.pointer(koffi.struct('IJK4', {i: 'int32_t', j: 'int32_t', k: 'int32_t'})), 'int32_t']);
151
- const ConcatenateToStr8 = lib.func('ConcatenateToStr8', 'string', [...Array(8).fill('int64_t'), koffi.struct('IJK8', {i: 'int64_t', j: 'int64_t', k: 'int64_t'}), 'int64_t']);
154
+ const ConcatenateToStr1 = lib.func('ConcatenateToStr1', 'str', [...Array(8).fill('int8_t'), koffi.struct('IJK1', {i: 'int8_t', j: 'int8_t', k: 'int8_t'}), 'int8_t']);
155
+ const ConcatenateToStr4 = lib.func('ConcatenateToStr4', 'str', [...Array(8).fill('int32_t'), koffi.pointer(koffi.struct('IJK4', {i: 'int32_t', j: 'int32_t', k: 'int32_t'})), 'int32_t']);
156
+ const ConcatenateToStr8 = lib.func('ConcatenateToStr8', 'str', [...Array(8).fill('int64_t'), koffi.struct('IJK8', {i: 'int64_t', j: 'int64_t', k: 'int64_t'}), 'int64_t']);
152
157
  const MakeBFG = lib.func('BFG __stdcall MakeBFG(_Out_ BFG *p, int x, double y, const char *str)');
153
158
  const MakePackedBFG = lib.func('PackedBFG __fastcall MakePackedBFG(int x, double y, _Out_ PackedBFG *p, const char *str)');
154
159
  const ReturnBigString = process.platform == 'win32' ?
155
- lib.stdcall(1, 'string', ['string']) :
160
+ lib.stdcall(1, 'str', ['str']) :
156
161
  lib.func('const char * __stdcall ReturnBigString(const char *str)');
157
162
  const PrintFmt = lib.func('const char *PrintFmt(const char *fmt, ...)');
158
163
  const Concat16 = lib.func('const char16_t *Concat16(const char16_t *str1, const char16_t *str2)')
@@ -175,6 +180,8 @@ async function test() {
175
180
  const ArrayToStruct = lib.func('IntContainer ArrayToStruct(int *ptr, int len)');
176
181
  const FillRange = lib.func('void FillRange(int init, int step, _Out_ int *out, int len)');
177
182
  const MultiplyIntegers = lib.func('void MultiplyIntegers(int multiplier, _Inout_ int *values, int len)');
183
+ const ThroughStr = lib.func('str ThroughStr(StrStruct s)');
184
+ const ThroughStr16 = lib.func('str16 ThroughStr16(StrStruct s)');
178
185
 
179
186
  // Simple signed value returns
180
187
  assert.equal(GetMinusOne1(), -1);
@@ -292,7 +299,7 @@ async function test() {
292
299
 
293
300
  // Variadic
294
301
  {
295
- let str = PrintFmt('foo %d %g %s', 'int', 200, 'double', 1.5, 'string', 'BAR');
302
+ let str = PrintFmt('foo %d %g %s', 'int', 200, 'double', 1.5, 'str', 'BAR');
296
303
  assert.equal(str, 'foo 200 1.5 BAR');
297
304
  }
298
305
 
@@ -367,4 +374,12 @@ async function test() {
367
374
  assert.deepEqual(out1, [-2, -9, -16, -23, -30, -37, -44, -51, 58, 65]);
368
375
  assert.deepEqual(out2, new Int32Array([3 * 13, 3 * 16, 3 * 19, 3 * 22, 3 * 25, 3 * 28, 3 * 31, 34, 37, 40]));
369
376
  }
377
+
378
+ // Test struct strings
379
+ {
380
+ assert.equal(ThroughStr({ str: 'Hello', str16: null }), 'Hello');
381
+ assert.equal(ThroughStr({ str: null, str16: 'Hello' }), null);
382
+ assert.equal(ThroughStr16({ str: null, str16: 'World!' }), 'World!');
383
+ assert.equal(ThroughStr16({ str: 'World!', str16: null }), null);
384
+ }
370
385
  }