koffi 1.3.9 → 1.3.12

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 (92) hide show
  1. package/ChangeLog.md +24 -0
  2. package/build/qemu/1.3.12/koffi_darwin_arm64.tar.gz +0 -0
  3. package/build/qemu/1.3.12/koffi_darwin_x64.tar.gz +0 -0
  4. package/build/qemu/1.3.12/koffi_freebsd_arm64.tar.gz +0 -0
  5. package/build/qemu/1.3.12/koffi_freebsd_ia32.tar.gz +0 -0
  6. package/build/qemu/1.3.12/koffi_freebsd_x64.tar.gz +0 -0
  7. package/build/qemu/1.3.12/koffi_linux_arm32hf.tar.gz +0 -0
  8. package/build/qemu/1.3.12/koffi_linux_arm64.tar.gz +0 -0
  9. package/build/qemu/1.3.12/koffi_linux_ia32.tar.gz +0 -0
  10. package/build/qemu/1.3.12/koffi_linux_riscv64hf64.tar.gz +0 -0
  11. package/build/qemu/1.3.12/koffi_linux_x64.tar.gz +0 -0
  12. package/build/qemu/1.3.12/koffi_openbsd_ia32.tar.gz +0 -0
  13. package/build/qemu/1.3.12/koffi_openbsd_x64.tar.gz +0 -0
  14. package/build/qemu/1.3.12/koffi_win32_arm64.tar.gz +0 -0
  15. package/build/qemu/1.3.12/koffi_win32_ia32.tar.gz +0 -0
  16. package/build/qemu/1.3.12/koffi_win32_x64.tar.gz +0 -0
  17. package/doc/benchmarks.md +2 -2
  18. package/doc/conf.py +1 -1
  19. package/doc/contribute.md +1 -1
  20. package/doc/dist/doctrees/benchmarks.doctree +0 -0
  21. package/doc/dist/doctrees/changes.doctree +0 -0
  22. package/doc/dist/doctrees/contribute.doctree +0 -0
  23. package/doc/dist/doctrees/environment.pickle +0 -0
  24. package/doc/dist/doctrees/functions.doctree +0 -0
  25. package/doc/dist/doctrees/index.doctree +0 -0
  26. package/doc/dist/doctrees/memory.doctree +0 -0
  27. package/doc/dist/doctrees/types.doctree +0 -0
  28. package/doc/dist/html/.buildinfo +1 -1
  29. package/doc/dist/html/_sources/benchmarks.md.txt +2 -2
  30. package/doc/dist/html/_sources/contribute.md.txt +1 -2
  31. package/doc/dist/html/_sources/functions.md.txt +137 -15
  32. package/doc/dist/html/_sources/index.rst.txt +2 -0
  33. package/doc/dist/html/_sources/memory.md.txt +1 -1
  34. package/doc/dist/html/_sources/types.md.txt +27 -11
  35. package/doc/dist/html/_static/basic.css +14 -12
  36. package/doc/dist/html/_static/pygments.css +54 -54
  37. package/doc/dist/html/benchmarks.html +3 -3
  38. package/doc/dist/html/changes.html +1 -1
  39. package/doc/dist/html/contribute.html +2 -3
  40. package/doc/dist/html/functions.html +206 -86
  41. package/doc/dist/html/genindex.html +1 -1
  42. package/doc/dist/html/index.html +6 -7
  43. package/doc/dist/html/memory.html +4 -4
  44. package/doc/dist/html/objects.inv +0 -0
  45. package/doc/dist/html/platforms.html +2 -2
  46. package/doc/dist/html/search.html +1 -1
  47. package/doc/dist/html/searchindex.js +1 -1
  48. package/doc/dist/html/start.html +55 -55
  49. package/doc/dist/html/types.html +172 -159
  50. package/doc/functions.md +9 -7
  51. package/doc/index.rst +2 -0
  52. package/doc/memory.md +1 -1
  53. package/doc/poetry.lock +692 -0
  54. package/doc/pyproject.toml +18 -0
  55. package/doc/{_static → static}/bench_linux.png +0 -0
  56. package/doc/{_static → static}/bench_windows.png +0 -0
  57. package/doc/{_static → static}/custom.css +0 -0
  58. package/doc/{_static → static}/perf_linux_20220623.png +0 -0
  59. package/doc/{_static → static}/perf_linux_20220623_2.png +0 -0
  60. package/doc/{_static → static}/perf_linux_20220627.png +0 -0
  61. package/doc/{_static → static}/perf_linux_20220628.png +0 -0
  62. package/doc/{_static → static}/perf_windows_20220623.png +0 -0
  63. package/doc/{_static → static}/perf_windows_20220623_2.png +0 -0
  64. package/doc/{_static → static}/perf_windows_20220627.png +0 -0
  65. package/doc/{_static → static}/perf_windows_20220628.png +0 -0
  66. package/doc/types.md +1 -1
  67. package/package.json +9 -7
  68. package/qemu/qemu.js +3 -3
  69. package/src/abi_arm32.cc +3 -3
  70. package/src/abi_riscv64.cc +1 -1
  71. package/src/abi_x64_sysv.cc +1 -1
  72. package/src/abi_x64_win.cc +1 -1
  73. package/src/abi_x64_win_fwd.asm +2 -2
  74. package/src/abi_x86.cc +5 -5
  75. package/src/parser.cc +2 -1
  76. package/test/misc.c +1 -1
  77. package/test/sync.js +2 -2
  78. package/build/qemu/1.3.9/koffi_darwin_arm64.tar.gz +0 -0
  79. package/build/qemu/1.3.9/koffi_darwin_x64.tar.gz +0 -0
  80. package/build/qemu/1.3.9/koffi_freebsd_arm64.tar.gz +0 -0
  81. package/build/qemu/1.3.9/koffi_freebsd_ia32.tar.gz +0 -0
  82. package/build/qemu/1.3.9/koffi_freebsd_x64.tar.gz +0 -0
  83. package/build/qemu/1.3.9/koffi_linux_arm32hf.tar.gz +0 -0
  84. package/build/qemu/1.3.9/koffi_linux_arm64.tar.gz +0 -0
  85. package/build/qemu/1.3.9/koffi_linux_ia32.tar.gz +0 -0
  86. package/build/qemu/1.3.9/koffi_linux_riscv64hf64.tar.gz +0 -0
  87. package/build/qemu/1.3.9/koffi_linux_x64.tar.gz +0 -0
  88. package/build/qemu/1.3.9/koffi_openbsd_ia32.tar.gz +0 -0
  89. package/build/qemu/1.3.9/koffi_openbsd_x64.tar.gz +0 -0
  90. package/build/qemu/1.3.9/koffi_win32_arm64.tar.gz +0 -0
  91. package/build/qemu/1.3.9/koffi_win32_ia32.tar.gz +0 -0
  92. package/build/qemu/1.3.9/koffi_win32_x64.tar.gz +0 -0
package/ChangeLog.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## Koffi 1.3.12
4
+
5
+ **Main fixes:**
6
+
7
+ - Fix support for Yarn package manager
8
+
9
+ ## Koffi 1.3.11
10
+
11
+ **Main fixes:**
12
+
13
+ - Fix broken parsing of `void *` when used for first parameter
14
+
15
+ ## Koffi 1.3.10
16
+
17
+ **Main fixes:**
18
+
19
+ - Fix support for callbacks with more than 4 parameters on Windows x64
20
+ - Fix support for callbacks with multiple floating-point arguments on ARM32 platforms
21
+ - Fix possibly incorrect conversion for uint32_t callback parameters
22
+
23
+ **Other changes:**
24
+
25
+ - Various documentation fixes and improvements
26
+
3
27
  ## Koffi 1.3.9
4
28
 
5
29
  **Main fixes:**
package/doc/benchmarks.md CHANGED
@@ -10,8 +10,8 @@ Here is a quick overview of the execution time of Koffi calls on three benchmark
10
10
 
11
11
  <table style="margin: 0 auto;">
12
12
  <tr>
13
- <td><a href="_static/perf_linux_20220628.png" target="_blank"><img src="_static/perf_linux_20220628.png" alt="Linux performance" style="width: 350px;"/></a></td>
14
- <td><a href="_static/perf_windows_20220628.png" target="_blank"><img src="_static/perf_windows_20220628.png" alt="Windows performance" style="width: 350px;"/></a></td>
13
+ <td><a href="_static/perf_linux_20220628.png" target="_blank"><img src="_static/perf_linux_20220628.png" alt="Linux x86_64 performance" style="width: 350px;"/></a></td>
14
+ <td><a href="_static/perf_windows_20220628.png" target="_blank"><img src="_static/perf_windows_20220628.png" alt="Windows x86_64 performance" style="width: 350px;"/></a></td>
15
15
  </tr>
16
16
  </table>
17
17
 
package/doc/conf.py CHANGED
@@ -31,7 +31,7 @@ html_title = project
31
31
 
32
32
  html_theme = 'furo'
33
33
 
34
- html_static_path = ['_static']
34
+ html_static_path = ['static']
35
35
 
36
36
  html_theme_options = {
37
37
  'light_css_variables': {
package/doc/contribute.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  Use the official repository (named Luigi, because this is a monorepo containing multiple projects) for bugs, ideas and features requests.
6
6
 
7
- Go here: https://github.com/Koromix/luigi/issues
7
+ Go here: https://github.com/Koromix/luigi/
8
8
 
9
9
  ## Build from source
10
10
 
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,4 +1,4 @@
1
1
  # Sphinx build info version 1
2
2
  # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
3
- config: 1029792c9b37058f12795e72139e7221
3
+ config: 3958eced2fda3c6c0751ec1a88d416ca
4
4
  tags: 645f666f9bcd5a90fca523b33c5a78b7
@@ -10,8 +10,8 @@ Here is a quick overview of the execution time of Koffi calls on three benchmark
10
10
 
11
11
  <table style="margin: 0 auto;">
12
12
  <tr>
13
- <td><a href="_static/perf_linux_20220628.png" target="_blank"><img src="_static/perf_linux_20220628.png" alt="Linux performance" style="width: 350px;"/></a></td>
14
- <td><a href="_static/perf_windows_20220628.png" target="_blank"><img src="_static/perf_windows_20220628.png" alt="Windows performance" style="width: 350px;"/></a></td>
13
+ <td><a href="static/perf_linux_20220628.png" target="_blank"><img src="static/perf_linux_20220628.png" alt="Linux x86_64 performance" style="width: 350px;"/></a></td>
14
+ <td><a href="static/perf_windows_20220628.png" target="_blank"><img src="static/perf_windows_20220628.png" alt="Windows x86_64 performance" style="width: 350px;"/></a></td>
15
15
  </tr>
16
16
  </table>
17
17
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  Use the official repository (named Luigi, because this is a monorepo containing multiple projects) for bugs, ideas and features requests.
6
6
 
7
- Go here: https://github.com/Koromix/luigi/issues
7
+ Go here: https://github.com/Koromix/luigi/
8
8
 
9
9
  ## Build from source
10
10
 
@@ -111,7 +111,6 @@ node qemu.js info debian_x64
111
111
 
112
112
  The following features and improvements are planned, not necessarily in that order:
113
113
 
114
- - Provide better ways to automatically deal with caller/heap-allocated memory (strings, etc.)
115
114
  - Optimize passing of structs and arrays (avoid setting named properties one by one? separate HFA-specific helper functions?)
116
115
  - Automate Windows/AArch64 (qemu) and macOS/AArch64 (how? ... thanks Apple) tests
117
116
  - Create a real-world example, using several libraries (Raylib, SQLite, libsodium) to illustrate various C API styles
@@ -23,7 +23,7 @@ const printf = lib.func('printf', 'int', ['str', '...']);
23
23
  const atoi = lib.func('atoi', 'int', ['str']);
24
24
  ```
25
25
 
26
- Koffi automatically tries mangled names for non-standard x86 calling conventions. See the section [on standard calls](#synchronous-calls) for more information on this subject.
26
+ Koffi automatically tries mangled names for non-standard x86 calling conventions. See the section on [calling conventions](#calling-conventions) for more information on this subject.
27
27
 
28
28
  ### C-like prototypes
29
29
 
@@ -36,7 +36,9 @@ const atoi = lib.func('int atoi(str)'); // The parameter name is not used by Kof
36
36
 
37
37
  You can use `()` or `(void)` for functions that take no argument.
38
38
 
39
- ## Synchronous calls
39
+ ## Function calls
40
+
41
+ ### Calling conventions
40
42
 
41
43
  By default, calling a C function happens synchronously.
42
44
 
@@ -57,12 +59,12 @@ Below you can find a small example showing how to use a non-default calling conv
57
59
  const koffi = require('koffi');
58
60
  const lib = koffi.load('user32.dll');
59
61
 
60
- // The following two declarations are equivalent, and use Stdcall
62
+ // The following two declarations are equivalent, and use stdcall on x86 (and the default ABI on other platforms)
61
63
  const MessageBoxA_1 = lib.stdcall('MessageBoxA', 'int', ['void *', 'str', 'str', 'uint']);
62
64
  const MessageBoxA_2 = lib.func('int __stdcall MessageBoxA(void *hwnd, str text, str caption, uint type)');
63
65
  ```
64
66
 
65
- ## Asynchronous calls
67
+ ### Asynchronous calls
66
68
 
67
69
  You can issue asynchronous calls by calling the function through its async member. In this case, you need to provide a callback function as the last argument, with `(err, res)` parameters.
68
70
 
@@ -88,7 +90,7 @@ You can easily convert this callback-style async function to a promise-based ver
88
90
 
89
91
  Variadic functions cannot be called asynchronously.
90
92
 
91
- ## Variadic functions
93
+ ### Variadic functions
92
94
 
93
95
  Variadic functions are declared with an ellipsis as the last argument.
94
96
 
@@ -103,7 +105,9 @@ printf('Integer %d, double %g, str %s', 'int', 6, 'double', 8.5, 'str', 'THE END
103
105
 
104
106
  On x86 platforms, only the Cdecl convention can be used for variadic functions.
105
107
 
106
- ## Output parameters
108
+ ## C to JS conversion gotchas
109
+
110
+ ### Output parameters
107
111
 
108
112
  By default, Koffi will only forward arguments from Javascript to C. However, many C functions use pointer arguments for output values, or input/output values.
109
113
 
@@ -122,7 +126,7 @@ The same can be done when declaring a function with a C-like prototype string, w
122
126
  - `_Out_` for output parameters
123
127
  - `_Inout_` for dual input/output parameters
124
128
 
125
- ### Struct example
129
+ #### Struct example
126
130
 
127
131
  This example calls the POSIX function `gettimeofday()`, and uses the prototype-like syntax.
128
132
 
@@ -148,7 +152,7 @@ gettimeofday(tv, null);
148
152
  console.log(tv);
149
153
  ```
150
154
 
151
- ### Opaque handle example
155
+ #### Opaque handle example
152
156
 
153
157
  This example opens an in-memory SQLite database, and uses the node-ffi-style function declaration syntax.
154
158
 
@@ -158,9 +162,9 @@ const lib = koffi.load('sqlite.so');
158
162
 
159
163
  const sqlite3_db = koffi.handle('sqlite3_db');
160
164
 
161
- // Use koffi.out() on a pointer to copy out (from C to JS) after the call
162
- const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(koffi.pointer(sqlite3_db)), 'int', 'str']);
163
- const sqlite3_close_v2 = lib.func('sqlite3_close_v2', 'int', [sqlite3_db]);
165
+ // Use koffi.out() on a double pointer to copy out (from C to JS) after the call
166
+ const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(koffi.pointer(sqlite3_db, 2)), 'int', 'str']);
167
+ const sqlite3_close_v2 = lib.func('sqlite3_close_v2', 'int', [koffi.pointer(sqlite3_db)]);
164
168
 
165
169
  const SQLITE_OPEN_READWRITE = 0x2;
166
170
  const SQLITE_OPEN_CREATE = 0x4;
@@ -171,6 +175,55 @@ if (sqlite3_open_v2(':memory:', db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
171
175
  sqlite3_close_v2(db);
172
176
  ```
173
177
 
178
+ ### Heap-allocated values
179
+
180
+ Some C functions return heap-allocated values directly or through output parameters. While Koffi automatically converts values from C to JS (to a string or an object), it does not know when something needs to be freed, or how.
181
+
182
+ For opaque handles, such as FILE, this does not matter because you will explicitly call `fclose()` on them. But some values (such as strings) get implicitly converted by Koffi, and you lose access to the original pointer. This creates a leak if the string is heap-allocated.
183
+
184
+ To avoid this, you can instruct Koffi to call a function on the original pointer once the conversion is done, by creating a disposable type with `koffi.dispose(name, type, func)`. This creates a type derived from another type, the only difference being that *func* gets called with the original pointer once the value has been converted and is not needed anymore.
185
+
186
+ The *name* can be omitted to create an anonymous disposable type. If *func* is omitted or is null, Koffi will use `koffi.free(ptr)` (which calls the standard C library *free* function under the hood).
187
+
188
+ ```js
189
+ const AnonHeapStr = koffi.disposable('str'); // Anonymous type (cannot be used in function prototypes)
190
+ const NamedHeapStr = koffi.disposable('HeapStr', 'str'); // Same thing, but named so usable in function prototypes
191
+ const ExplicitFree = koffi.disposable('HeapStr16', 'str16', koffi.free); // You can specify any other JS function
192
+ ```
193
+
194
+ The following example illustrates the use of a disposable type derived from *str*.
195
+
196
+ ```js
197
+ const koffi = require('koffi');
198
+ const lib = koffi.load('libc.so.6');
199
+
200
+ // You can also use: const strdup = lib.func('const char *! asprintf(const char *str)')
201
+ const HeapStr = koffi.disposable('str');
202
+ const strdup = lib.cdecl('strdup', HeapStr, ['str']);
203
+
204
+ let copy = strdup('Hello!');
205
+ console.log(copy); // Prints Hello!
206
+ ```
207
+
208
+ When you declare functions with the [prototype-like syntax](#c-like-prototypes), you can either use named disposables types or use the '!' shortcut qualifier with compatibles types, as shown in the example below. This qualifier creates an anonymous disposable type that calls `koffi.free(ptr)`.
209
+
210
+ ```js
211
+ const koffi = require('koffi');
212
+ const lib = koffi.load('libc.so.6');
213
+
214
+ // You can also use: const strdup = lib.func('const char *! strdup(const char *str)')
215
+ const strdup = lib.func('str! strdup(const char *str)');
216
+
217
+ let copy = strdup('World!');
218
+ console.log(copy); // Prints World!
219
+ ```
220
+
221
+ Disposable types can only be created from pointer or string types.
222
+
223
+ ```{warning}
224
+ Be careful on Windows: if your shared library uses a different CRT (such as msvcrt), the memory could have been allocated by a different malloc/free implementation or heap, resulting in undefined behavior if you use `koffi.free()`.
225
+ ```
226
+
174
227
  ## Javascript callbacks
175
228
 
176
229
  In order to pass a JS function to a C function expecting a callback, you must first create a callback type with the expected return type and parameters. The syntax is similar to the one used to load functions from a shared library.
@@ -185,9 +238,28 @@ const ExampleCallback = koffi.callback('ExampleCallback', 'void', ['int']);
185
238
  const AddDoubleFloat = koffi.callback('double AddDoubleFloat(double d, float f)');
186
239
  ```
187
240
 
188
- Once your callback type is declared, you can use it in struct definitions, or as function parameter and/or return type.
241
+ Once your callback type is declared, you can use a pointer to it in struct definitions, or as function parameters and/or return types.
242
+
243
+ ```{note}
244
+ Callbacks **have changed in version 2.0**.
245
+
246
+ In Koffi 1.x, callbacks were defined in a way that made them usable directly as parameter and return types, obscuring the underlying pointer.
247
+
248
+ Now, you must use them through a pointer: `void CallIt(CallbackType func)` in Koffi 1.x becomes `void CallIt(CallbackType *func)` in version 2.0 and newer.
249
+ ```
250
+
251
+ Koffi only uses predefined static trampolines, and does not need to generate code at runtime, which makes it compatible with platforms with hardened W^X migitations (such as PaX mprotect). However, this imposes some restrictions on the maximum number of callbacks, and their duration.
252
+
253
+ Thus, Koffi distinguishes two callback modes:
189
254
 
190
- Here is a small example with the C part and the JS part.
255
+ - [Transient callbacks](#transient-callbacks) can only be called while the C function they are passed to is running, and are invalidated when it returns. If the C function calls the callback later, the behavior is undefined, though Koffi tries to detect such cases. If it does, an exception will be thrown, but this is no guaranteed. However, they are simple to use, and don't require any special handling.
256
+ - [Registered callbacks](#registered-callbacks) can be called at any time, but they must be manually registered and unregistered. A limited number of registered callbacks can exist at the same time.
257
+
258
+ You need to specify the correct [calling convention](#calling-conventions) on x86 platforms, or the behavior is undefined (Node will probably crash). Only *cdecl* and *stdcall* callbacks are supported.
259
+
260
+ ### Transient callbacks
261
+
262
+ Use transient callbacks when the native C function only needs to call them while it runs (e.g. qsort, progress callback, `sqlite3_exec`). Here is a small example with the C part and the JS part.
191
263
 
192
264
  ```c
193
265
  #include <string.h>
@@ -202,10 +274,11 @@ int TransferToJS(const char *name, int age, int (*cb)(const char *str, int age))
202
274
 
203
275
  ```js
204
276
  const koffi = require('koffi');
277
+ const lib = koffi.load('./callbacks.so'); // Fake path
205
278
 
206
279
  const TransferCallback = koffi.callback('int TransferCallback(const char *str, int age)');
207
280
 
208
- const TransferToJS = lib.func('int TransferToJS(const char *str, int age, TransferCallback cb)');
281
+ const TransferToJS = lib.func('TransferToJS', 'int', ['str', 'int', koffi.pointer(TransferCallback)]);
209
282
 
210
283
  let ret = TransferToJS('Niels', 27, (str, age) => {
211
284
  console.log(str);
@@ -220,7 +293,56 @@ console.log(ret);
220
293
  // 42
221
294
  ```
222
295
 
223
- On x86 platforms, only Cdecl and Stdcall callbacks are supported.
296
+ ### Registered callbacks
297
+
298
+ Use registered callbacks when the function needs to be called at a later time (e.g. log handler, event handler, `fopencookie/funopen`). Call `koffi.register(func, type)` to register a callback function, with two arguments: the JS function, and the callback type.
299
+
300
+ When you are done, call `koffi.unregister()` (with the value returned by `koffi.register()`) to release the slot. A maximum of 16 registered callbacks can exist at the same time. Failure to do so will leak the slot, and subsequent registrations may fail (with an exception) once all slots are used.
301
+
302
+ The example below shows how to register and unregister delayed callbacks.
303
+
304
+ ```c
305
+ static const char *(*g_cb1)(const char *name);
306
+ static void (*g_cb2)(const char *str);
307
+
308
+ void RegisterFunctions(const char *(*cb1)(const char *name), void (*cb2)(const char *str))
309
+ {
310
+ g_cb1 = cb1;
311
+ g_cb2 = cb2;
312
+ }
313
+
314
+ void SayIt(const char *name)
315
+ {
316
+ const char *str = g_cb1(name);
317
+ g_cb2(str);
318
+ }
319
+ ```
320
+
321
+ ```js
322
+ const koffi = require('koffi');
323
+ const lib = koffi.load('./callbacks.so'); // Fake path
324
+
325
+ const GetCallback = koffi.callback('const char *GetCallback(const char *name)');
326
+ const PrintCallback = koffi.callback('void PrintCallback(const char *str)');
327
+
328
+ const RegisterFunctions = lib.func('void RegisterFunctions(GetCallback *cb1, PrintCallback *cb2)');
329
+ const SayIt = lib.func('void SayIt(const char *name)');
330
+
331
+ let cb1 = koffi.register(name => 'Hello ' + name + '!', koffi.pointer(GetCallback));
332
+ let cb2 = koffi.register(console.log, 'PrintCallback *');
333
+
334
+ RegisterFunctions(cb1, cb2);
335
+ SayIt('Kyoto'); // Prints Hello Kyoto!
336
+
337
+ koffi.unregister(cb1);
338
+ koffi.unregister(cb2);
339
+ ```
340
+
341
+ ### Handling of exceptions
342
+
343
+ If an exception happens inside the JS callback, the C API will receive 0 or NULL (depending on the return value type).
344
+
345
+ Handle the exception yourself (with try/catch) if you need to handle exceptions differently.
224
346
 
225
347
  ## Thread safety
226
348
 
@@ -14,6 +14,8 @@ Koffi is a **fast and easy-to-use C FFI module for Node.js**, featuring:
14
14
 
15
15
  Koffi requires a recent `Node.js <https://nodejs.org/>`_ version with N-API version 8 support, see :ref:`this page<Node.js>` for more information.
16
16
 
17
+ The source code is available here: https://github.com/Koromix/luigi/ (in the *koffi* subdirectory).
18
+
17
19
  Table of contents
18
20
  -----------------
19
21
 
@@ -7,7 +7,7 @@ For synchronous/normal calls, Koffi uses two preallocated memory blocks:
7
7
  - One to construct the C stack and assign registers, subsequently used by the platform-specific assembly code (1 MiB by default)
8
8
  - One to allocate strings and big objects/structs (2 MiB by default)
9
9
 
10
- Unless very big strings or objects (at least more than one page of memory) are used, no extra allocation ever happens during calls or callbacks.
10
+ Unless very big strings or objects (at least more than one page of memory) are used, Koffi does not directly allocate any extra memory during calls or callbacks. However, please note that the JS engine (V8) might.
11
11
 
12
12
  The size (in bytes) of these preallocated blocks can be changed. Use `koffi.config()` to get an object with the settings, and `koffi.config(obj)` to apply new settings.
13
13
 
@@ -63,7 +63,7 @@ Primitive types can be specified by name (in a string) or through `koffi.types`:
63
63
 
64
64
  ```js
65
65
  // These two lines do the same:
66
- let struct1 = koffi.struct({ dummy: 'int' });
66
+ let struct1 = koffi.struct({ dummy: 'long' });
67
67
  let struct2 = koffi.struct({ dummy: koffi.types.long });
68
68
  ```
69
69
 
@@ -91,7 +91,7 @@ typedef struct A {
91
91
  const A = koffi.struct('A', {
92
92
  a: 'int',
93
93
  b: 'char',
94
- c: 'str',
94
+ c: 'const char *', // Koffi does not care about const, it is ignored
95
95
  d: koffi.struct({
96
96
  d1: 'double',
97
97
  d2: 'double'
@@ -146,7 +146,15 @@ console.log(pos);
146
146
 
147
147
  Many C libraries use some kind of object-oriented API, with a pair of functions dedicated to create and delete objects. An obvious example of this can be found in stdio.h, with the opaque `FILE *` pointer. You can open and close files with `fopen()` and `fclose()`, and manipule the handle with other functions such as `fread()` or `ftell()`.
148
148
 
149
- In Koffi, you can manage this with opaque handles. Declare the handle type with `koffi.handle(name)`, and use this type either as a return type or some kind of [output parameter](functions.md#output-parameters) (with a pointer to the handle).
149
+ In Koffi, you can manage this with opaque handles. Declare the handle type with `koffi.handle(name)`, and use a pointer to this type either as a return type or some kind of [output parameter](functions.md#output-parameters) (with a double pointer).
150
+
151
+ ```{note}
152
+ Opaque handles **have changed in version 2.0**.
153
+
154
+ In Koffi 1.x, opaque handles were defined in a way that made them usable directly as parameter and return types, obscuring the underlying pointer.
155
+
156
+ Now, you must use them through a pointer, and use an array for output parameters. This is shown in the example below (look for the call to `ConcatNewOut` in the JS part), and is described in the section on [output parameters](functions.md#output-parameters).
157
+ ```
150
158
 
151
159
  The full example below implements an iterative string builder (concatenator) in C, and uses it from Javascript to output a mix of Hello World and FizzBuzz. The builder is hidden behind an opaque handle, and is created and destroyed using a pair of C functions: `ConcatNew` (or `ConcatNewOut`) and `ConcatFree`.
152
160
 
@@ -274,17 +282,19 @@ const koffi = require('koffi');
274
282
  const lib = koffi.load('./handles.so');
275
283
 
276
284
  const Concat = koffi.handle('Concat');
277
- const ConcatNew = lib.func('Concat ConcatNew()');
278
- const ConcatFree = lib.func('void ConcatFree(Concat c)');
279
- const ConcatAppend = lib.func('bool ConcatAppend(Concat c, const char *frag)');
280
- const ConcatBuild = lib.func('const char *ConcatBuild(Concat c)');
281
- const ConcatNewOut = lib.func('bool ConcatNewOut(_Out_ Concat out)');
285
+ const ConcatNewOut = lib.func('bool ConcatNewOut(_Out_ Concat **out)');
286
+ const ConcatNew = lib.func('Concat *ConcatNew()');
287
+ const ConcatFree = lib.func('void ConcatFree(Concat *c)');
288
+ const ConcatAppend = lib.func('bool ConcatAppend(Concat *c, const char *frag)');
289
+ const ConcatBuild = lib.func('const char *ConcatBuild(Concat *c)');
282
290
 
283
291
  let c = ConcatNew();
284
292
  if (!c) {
285
293
  // This is stupid, it does the same, but try both versions (return value, output parameter)
286
- if (!ConcatNewOut(c))
294
+ let ptr = [null];
295
+ if (!ConcatNewOut(ptr))
287
296
  throw new Error('Allocation failure');
297
+ c = ptr[0];
288
298
  }
289
299
 
290
300
  try {
@@ -368,7 +378,7 @@ Here is an example based on the Win32 API, listing files in the current director
368
378
  const koffi = require('koffi');
369
379
  const lib = koffi.load('kernel32.dll');
370
380
 
371
- const HANDLE = koffi.handle('HANDLE');
381
+ const HANDLE = koffi.pointer('HANDLE', koffi.handle());
372
382
  const FILETIME = koffi.struct('FILETIME', {
373
383
  dwLowDateTime: 'uint',
374
384
  dwHighDateTime: 'uint'
@@ -509,7 +519,13 @@ Koffi exposes three functions to explore type information:
509
519
 
510
520
  - `koffi.sizeof(type)` to get the size of a type
511
521
  - `koffi.alignof(type)` to get the alignment of a type
512
- - `koffi.introspect(type)` to get the definition of a type (only for structs for now)
522
+ - `koffi.introspect(type)` to get the definition of a type in an object containing: name, primitive, size, alignment, members (structs), reference (array, pointer) and length (array)
523
+
524
+ ```{note}
525
+ The value returned by `introspect()` has **changed in version 2.0**.
526
+
527
+ In Koffi 1.x, it could only be used with struct types and returned the object passed to koffi.struct() with the member names and types.
528
+ ```
513
529
 
514
530
  Just like before, you can refer to primitive types by their name or through `koffi.types`:
515
531
 
@@ -237,6 +237,16 @@ a.headerlink {
237
237
  visibility: hidden;
238
238
  }
239
239
 
240
+ a.brackets:before,
241
+ span.brackets > a:before{
242
+ content: "[";
243
+ }
244
+
245
+ a.brackets:after,
246
+ span.brackets > a:after {
247
+ content: "]";
248
+ }
249
+
240
250
  h1:hover > a.headerlink,
241
251
  h2:hover > a.headerlink,
242
252
  h3:hover > a.headerlink,
@@ -324,18 +334,14 @@ aside.sidebar {
324
334
  p.sidebar-title {
325
335
  font-weight: bold;
326
336
  }
327
- nav.contents,
328
- aside.topic,
329
337
 
330
- div.admonition, div.topic, blockquote {
338
+ div.admonition, div.topic, aside.topic, blockquote {
331
339
  clear: left;
332
340
  }
333
341
 
334
342
  /* -- topics ---------------------------------------------------------------- */
335
- nav.contents,
336
- aside.topic,
337
343
 
338
- div.topic {
344
+ div.topic, aside.topic {
339
345
  border: 1px solid #ccc;
340
346
  padding: 7px;
341
347
  margin: 10px 0 10px 0;
@@ -373,20 +379,16 @@ div.body p.centered {
373
379
 
374
380
  div.sidebar > :last-child,
375
381
  aside.sidebar > :last-child,
376
- nav.contents > :last-child,
377
- aside.topic > :last-child,
378
-
379
382
  div.topic > :last-child,
383
+ aside.topic > :last-child,
380
384
  div.admonition > :last-child {
381
385
  margin-bottom: 0;
382
386
  }
383
387
 
384
388
  div.sidebar::after,
385
389
  aside.sidebar::after,
386
- nav.contents::after,
387
- aside.topic::after,
388
-
389
390
  div.topic::after,
391
+ aside.topic::after,
390
392
  div.admonition::after,
391
393
  blockquote::after {
392
394
  display: block;