koffi 2.1.5 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/ChangeLog.md +21 -1
  2. package/doc/Makefile +1 -1
  3. package/doc/callbacks.md +175 -0
  4. package/doc/changes.md +2 -3
  5. package/doc/conf.py +29 -6
  6. package/doc/functions.md +39 -124
  7. package/doc/index.rst +1 -0
  8. package/doc/make.bat +1 -1
  9. package/doc/types.md +36 -9
  10. package/package.json +2 -2
  11. package/src/core/libcc/libcc.cc +89 -27
  12. package/src/core/libcc/libcc.hh +74 -39
  13. package/src/koffi/build/2.2.1/koffi_darwin_arm64.tar.gz +0 -0
  14. package/src/koffi/build/2.2.1/koffi_darwin_x64.tar.gz +0 -0
  15. package/src/koffi/build/2.2.1/koffi_freebsd_arm64.tar.gz +0 -0
  16. package/src/koffi/build/2.2.1/koffi_freebsd_ia32.tar.gz +0 -0
  17. package/src/koffi/build/2.2.1/koffi_freebsd_x64.tar.gz +0 -0
  18. package/src/koffi/build/2.2.1/koffi_linux_arm32hf.tar.gz +0 -0
  19. package/src/koffi/build/2.2.1/koffi_linux_arm64.tar.gz +0 -0
  20. package/src/koffi/build/2.2.1/koffi_linux_ia32.tar.gz +0 -0
  21. package/src/koffi/build/2.2.1/koffi_linux_riscv64hf64.tar.gz +0 -0
  22. package/src/koffi/build/2.2.1/koffi_linux_x64.tar.gz +0 -0
  23. package/src/koffi/build/2.2.1/koffi_openbsd_ia32.tar.gz +0 -0
  24. package/src/koffi/build/2.2.1/koffi_openbsd_x64.tar.gz +0 -0
  25. package/src/koffi/build/2.2.1/koffi_win32_arm64.tar.gz +0 -0
  26. package/src/koffi/build/2.2.1/koffi_win32_ia32.tar.gz +0 -0
  27. package/src/koffi/build/2.2.1/koffi_win32_x64.tar.gz +0 -0
  28. package/src/koffi/qemu/qemu.js +3 -1
  29. package/src/koffi/src/abi_arm32.cc +26 -23
  30. package/src/koffi/src/abi_arm64.cc +25 -22
  31. package/src/koffi/src/abi_riscv64.cc +21 -18
  32. package/src/koffi/src/abi_x64_sysv.cc +20 -17
  33. package/src/koffi/src/abi_x64_win.cc +19 -16
  34. package/src/koffi/src/abi_x86.cc +23 -20
  35. package/src/koffi/src/call.cc +222 -607
  36. package/src/koffi/src/call.hh +7 -11
  37. package/src/koffi/src/ffi.cc +229 -29
  38. package/src/koffi/src/ffi.hh +6 -2
  39. package/src/koffi/src/parser.cc +3 -9
  40. package/src/koffi/src/util.cc +546 -8
  41. package/src/koffi/src/util.hh +8 -2
  42. package/src/koffi/test/CMakeLists.txt +3 -3
  43. package/src/koffi/test/callbacks.js +89 -0
  44. package/src/koffi/test/misc.c +78 -0
  45. package/src/koffi/test/raylib.js +2 -2
  46. package/src/koffi/test/sqlite.js +1 -1
  47. package/src/koffi/test/sync.js +28 -6
  48. package/vendor/brotli/c/common/platform.h +2 -0
  49. package/vendor/sqlite3mc/sqlite3.c +243532 -0
  50. package/vendor/sqlite3mc/sqlite3.h +12887 -0
  51. package/src/koffi/build/2.1.5/koffi_darwin_arm64.tar.gz +0 -0
  52. package/src/koffi/build/2.1.5/koffi_darwin_x64.tar.gz +0 -0
  53. package/src/koffi/build/2.1.5/koffi_freebsd_arm64.tar.gz +0 -0
  54. package/src/koffi/build/2.1.5/koffi_freebsd_ia32.tar.gz +0 -0
  55. package/src/koffi/build/2.1.5/koffi_freebsd_x64.tar.gz +0 -0
  56. package/src/koffi/build/2.1.5/koffi_linux_arm32hf.tar.gz +0 -0
  57. package/src/koffi/build/2.1.5/koffi_linux_arm64.tar.gz +0 -0
  58. package/src/koffi/build/2.1.5/koffi_linux_ia32.tar.gz +0 -0
  59. package/src/koffi/build/2.1.5/koffi_linux_riscv64hf64.tar.gz +0 -0
  60. package/src/koffi/build/2.1.5/koffi_linux_x64.tar.gz +0 -0
  61. package/src/koffi/build/2.1.5/koffi_openbsd_ia32.tar.gz +0 -0
  62. package/src/koffi/build/2.1.5/koffi_openbsd_x64.tar.gz +0 -0
  63. package/src/koffi/build/2.1.5/koffi_win32_arm64.tar.gz +0 -0
  64. package/src/koffi/build/2.1.5/koffi_win32_ia32.tar.gz +0 -0
  65. package/src/koffi/build/2.1.5/koffi_win32_x64.tar.gz +0 -0
package/ChangeLog.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  ## History
4
4
 
5
+ ### Koffi 2.2.1
6
+
7
+ **Main fixes:**
8
+
9
+ - Fix crash when [calling callback again after FFI call inside previous callback](https://github.com/Koromix/rygel/issues/15)
10
+
11
+ ### Koffi 2.2.0
12
+
13
+ **New features:**
14
+
15
+ - Add [koffi.decode()](callbacks.md#pointer-arguments) for callback pointer arguments
16
+ - Support transparent [output string parameters](functions.md#output-parameters)
17
+ - Add `koffi.offsetof()` utility function
18
+ - Support optional *this* binding in `koffi.register()`
19
+
20
+ **Other fixes:**
21
+
22
+ - Correctly validate output parameter types
23
+ - Fix assertion with `void *` parameters
24
+
5
25
  ### Koffi 2.1.5
6
26
 
7
27
  **Main fixes:**
@@ -66,7 +86,7 @@
66
86
  **Major new features:**
67
87
 
68
88
  - Add [disposable types](functions.md#heap-allocated-values) for automatic disposal of C values (such as heap-allocated strings)
69
- - Add support for [registered callbacks](functions.md#registered-callbacks), that can be called after the initial FFI call
89
+ - Add support for [registered callbacks](callbacks.md#registered-callbacks), that can be called after the initial FFI call
70
90
  - Support named pointer types
71
91
  - Support complex type specifications outside of prototype parser
72
92
 
package/doc/Makefile CHANGED
@@ -6,7 +6,7 @@
6
6
  SPHINXOPTS ?=
7
7
  SPHINXBUILD ?= sphinx-build
8
8
  SOURCEDIR = .
9
- BUILDDIR = ../build/doc
9
+ BUILDDIR = dist
10
10
 
11
11
  # Put it first so that "make" without argument is like "make help".
12
12
  help:
@@ -0,0 +1,175 @@
1
+ # Callbacks
2
+
3
+ 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.
4
+
5
+ ```js
6
+ const koffi = require('koffi');
7
+
8
+ // With the classic syntax, this callback expects an integer and returns nothing
9
+ const ExampleCallback = koffi.callback('ExampleCallback', 'void', ['int']);
10
+
11
+ // With the prototype parser, this callback expects a double and float, and returns the sum as a double
12
+ const AddDoubleFloat = koffi.callback('double AddDoubleFloat(double d, float f)');
13
+ ```
14
+
15
+ Once your callback type is declared, you can use a pointer to it in struct definitions, or as function parameters and/or return types.
16
+
17
+ ```{note}
18
+ Callbacks **have changed in version 2.0**.
19
+
20
+ In Koffi 1.x, callbacks were defined in a way that made them usable directly as parameter and return types, obscuring the underlying pointer.
21
+
22
+ 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.
23
+
24
+ Consult the [migration guide](changes.md) for more information.
25
+ ```
26
+
27
+ ## Callback types
28
+
29
+ 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.
30
+
31
+ Thus, Koffi distinguishes two callback modes:
32
+
33
+ - [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.
34
+ - [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.
35
+
36
+ You need to specify the correct [calling convention](functions.md#calling-conventions) on x86 platforms, or the behavior is undefined (Node will probably crash). Only *cdecl* and *stdcall* callbacks are supported.
37
+
38
+ ### Transient callbacks
39
+
40
+ 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.
41
+
42
+ ```c
43
+ #include <string.h>
44
+
45
+ int TransferToJS(const char *name, int age, int (*cb)(const char *str, int age))
46
+ {
47
+ char buf[64];
48
+ snprintf(buf, sizeof(buf), "Hello %s!", str);
49
+ return cb(buf, age);
50
+ }
51
+ ```
52
+
53
+ ```js
54
+ const koffi = require('koffi');
55
+ const lib = koffi.load('./callbacks.so'); // Fake path
56
+
57
+ const TransferCallback = koffi.callback('int TransferCallback(const char *str, int age)');
58
+
59
+ const TransferToJS = lib.func('TransferToJS', 'int', ['str', 'int', koffi.pointer(TransferCallback)]);
60
+
61
+ let ret = TransferToJS('Niels', 27, (str, age) => {
62
+ console.log(str);
63
+ console.log('Your age is:', age);
64
+ return 42;
65
+ });
66
+ console.log(ret);
67
+
68
+ // This example prints:
69
+ // Hello Niels!
70
+ // Your age is: 27
71
+ // 42
72
+ ```
73
+
74
+ ### Registered callbacks
75
+
76
+ *New in Koffi 2.0 (explicit this binding in Koffi 2.2)*
77
+
78
+ 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.
79
+
80
+ 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.
81
+
82
+ The example below shows how to register and unregister delayed callbacks.
83
+
84
+ ```c
85
+ static const char *(*g_cb1)(const char *name);
86
+ static void (*g_cb2)(const char *str);
87
+
88
+ void RegisterFunctions(const char *(*cb1)(const char *name), void (*cb2)(const char *str))
89
+ {
90
+ g_cb1 = cb1;
91
+ g_cb2 = cb2;
92
+ }
93
+
94
+ void SayIt(const char *name)
95
+ {
96
+ const char *str = g_cb1(name);
97
+ g_cb2(str);
98
+ }
99
+ ```
100
+
101
+ ```js
102
+ const koffi = require('koffi');
103
+ const lib = koffi.load('./callbacks.so'); // Fake path
104
+
105
+ const GetCallback = koffi.callback('const char *GetCallback(const char *name)');
106
+ const PrintCallback = koffi.callback('void PrintCallback(const char *str)');
107
+
108
+ const RegisterFunctions = lib.func('void RegisterFunctions(GetCallback *cb1, PrintCallback *cb2)');
109
+ const SayIt = lib.func('void SayIt(const char *name)');
110
+
111
+ let cb1 = koffi.register(name => 'Hello ' + name + '!', koffi.pointer(GetCallback));
112
+ let cb2 = koffi.register(console.log, 'PrintCallback *');
113
+
114
+ RegisterFunctions(cb1, cb2);
115
+ SayIt('Kyoto'); // Prints Hello Kyoto!
116
+
117
+ koffi.unregister(cb1);
118
+ koffi.unregister(cb2);
119
+ ```
120
+
121
+ Starting *with Koffi 2.2*, you can optionally specify the `this` value for the function as the first argument.
122
+
123
+ ```js
124
+ class ValueStore {
125
+ constructor(value) { this.value = value; }
126
+ get() { return this.value; }
127
+ }
128
+
129
+ let store = new ValueStore(42);
130
+
131
+ let cb1 = koffi.register(store.get, 'IntCallback *'); // If a C function calls cb1 it will fail because this will be undefined
132
+ let cb2 = koffi.register(store, store.get, 'IntCallback *'); // However in this case, this will match the store object
133
+ ```
134
+
135
+ ## Pointer arguments
136
+
137
+ *New in Koffi 2.2*
138
+
139
+ Koffi does not have enough information to convert callback pointer arguments to an appropriate JS value. In this case, your JS function will receive an opaque *External* object.
140
+
141
+ You can pass this value through to another C function that expects a pointer of the same type, or you can use `koffi.decode(value, offset, type, len)` to decode it into something you can use in Javascript.
142
+
143
+ Some arguments are optional and this function can be called in several ways:
144
+
145
+ - `koffi.decode(value, offset, type, len)`: this is the full signature, value is the JS external object, offset (optional, defaults to 0) is specified in bytes, type is the value type, and length (optional) can be used for arrays and strings.
146
+ - `koffi.decode(value, type)`: no offset, expect NUL-terminated strings
147
+ - `koffi.decode(value, type, len)`: use specified length when decoding strings and arrays
148
+ - `koffi.decode(value, offset, type)`
149
+
150
+ The following example sorts an array of strings (in-place) with `qsort()`:
151
+
152
+ ```js
153
+ const koffi = require('koffi');
154
+ const lib = koffi.load('libc.so.6');
155
+
156
+ const SortCallback = koffi.callback('int SortCallback(const void *first, const void *second)');
157
+ const qsort = lib.func('void qsort(_Inout_ void *array, size_t count, size_t size, SortCallback *cb)');
158
+
159
+ let array = ['foo', 'bar', '123', 'foobar'];
160
+
161
+ qsort(koffi.as(array, 'char **'), array.length, koffi.sizeof('void *'), (ptr1, ptr2) => {
162
+ let str1 = koffi.decode(ptr1, 'char *');
163
+ let str2 = koffi.decode(ptr2, 'char *');
164
+
165
+ return str1.localeCompare(str2);
166
+ });
167
+
168
+ console.log(array); // Prints ['123', 'bar', 'foo', 'foobar']
169
+ ```
170
+
171
+ ## Handling of exceptions
172
+
173
+ If an exception happens inside the JS callback, the C API will receive 0 or NULL (depending on the return value type).
174
+
175
+ Handle the exception yourself (with try/catch) if you need to handle exceptions differently.
package/doc/changes.md CHANGED
@@ -1,5 +1,4 @@
1
- ```{include} ../ChangeLog.md
2
- ```
1
+ {{ "```{include} " ~ root ~ "/ChangeLog.md" ~ "\n```" }}
3
2
 
4
3
  ## Migration guide
5
4
 
@@ -64,7 +63,7 @@ let ret = TransferToJS('Niels', 27, (str, age) => {
64
63
  console.log(ret);
65
64
  ```
66
65
 
67
- Koffi 1.x only supported [transient callbacks](functions.md#javascript-callbacks), you must use Koffi 2.x for registered callbacks.
66
+ Koffi 1.x only supported [transient callbacks](callbacks.md#callbacks), you must use Koffi 2.x for registered callbacks.
68
67
 
69
68
  #### Opaque types
70
69
 
package/doc/conf.py CHANGED
@@ -7,12 +7,31 @@ project = 'Koffi'
7
7
  copyright = '2022, Niels Martignène'
8
8
  author = 'Niels Martignène'
9
9
 
10
- with open(os.path.dirname(__file__) + '/../package.json') as f:
11
- config = json.load(f)
10
+ root = None
11
+ version = None
12
+ revision = None
13
+ stable = None
12
14
 
13
- version = config['version']
14
- revision = config['version']
15
- stable = config['stable']
15
+ names = ['../../src/koffi/package.json', '../package.json']
16
+
17
+ for name in names:
18
+ filename = os.path.dirname(__file__) + '/' + name
19
+
20
+ if not os.path.exists(filename):
21
+ continue;
22
+
23
+ with open(filename) as f:
24
+ config = json.load(f)
25
+
26
+ root = os.path.dirname(name)
27
+ version = config['version']
28
+ revision = config['version']
29
+ stable = config['stable']
30
+
31
+ break
32
+
33
+ if root is None:
34
+ raise FileNotFoundError('Cannot find Koffi package.json')
16
35
 
17
36
  # -- General configuration ---------------------------------------------------
18
37
 
@@ -63,15 +82,19 @@ html_sidebars = {
63
82
  }
64
83
 
65
84
  html_context = {
85
+ "root": root,
66
86
  "stable": stable
67
87
  }
68
88
 
69
89
  # -- MyST parser options -------------------------------------------------
70
90
 
71
91
  myst_enable_extensions = [
72
- 'linkify'
92
+ 'linkify',
93
+ 'substitution'
73
94
  ]
74
95
 
96
+ myst_substitutions = html_context
97
+
75
98
  myst_heading_anchors = 3
76
99
 
77
100
  myst_linkify_fuzzy_links = False
package/doc/functions.md CHANGED
@@ -115,6 +115,7 @@ For simplicity, and because Javascript only has value semantics for primitive ty
115
115
 
116
116
  - [Structs](types.md#struct-types) (to/from JS objects)
117
117
  - [Opaque types](types.md#opaque-types)
118
+ - String buffers
118
119
 
119
120
  In order to change an argument from input-only to output or input/output, use the following functions:
120
121
 
@@ -177,6 +178,44 @@ let db = out[0];
177
178
  sqlite3_close_v2(db);
178
179
  ```
179
180
 
181
+ #### String buffer example
182
+
183
+ *New in Koffi 2.2*
184
+
185
+ This example calls a C function to concatenate two strings to a pre-allocated string buffer. Since JS strings are immutable, you must pass an array with a single string instead.
186
+
187
+ ```c
188
+ void ConcatToBuffer(const char *str1, const char *str2, char *out)
189
+ {
190
+ size_t len = 0;
191
+
192
+ for (size_t i = 0; str1[i]; i++) {
193
+ out[len++] = str1[i];
194
+ }
195
+ for (size_t i = 0; str2[i]; i++) {
196
+ out[len++] = str2[i];
197
+ }
198
+
199
+ out[len] = 0;
200
+ }
201
+ ```
202
+
203
+ ```js
204
+ const ConcatToBuffer = lib.func('void ConcatToBuffer(const char *str1, const char *str2, _Out_ char *out)');
205
+
206
+ let str1 = 'Hello ';
207
+ let str2 = 'Friends!';
208
+
209
+ // We need to reserve space for the output buffer! Including the NUL terminator
210
+ // because ConcatToBuffer() expects so, but Koffi can convert back to a JS string
211
+ // without it (if we reserve the right size).
212
+ let out = ['\0'.repeat(str1.length + str2.length + 1)];
213
+
214
+ ConcatToBuffer(str1, str2, out);
215
+
216
+ console.log(out[0]);
217
+ ```
218
+
180
219
  ### Polymorphic parameters
181
220
 
182
221
  *New in Koffi 2.1*
@@ -290,130 +329,6 @@ Disposable types can only be created from pointer or string types.
290
329
  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()`.
291
330
  ```
292
331
 
293
- ## Javascript callbacks
294
-
295
- 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.
296
-
297
- ```js
298
- const koffi = require('koffi');
299
-
300
- // With the classic syntax, this callback expects an integer and returns nothing
301
- const ExampleCallback = koffi.callback('ExampleCallback', 'void', ['int']);
302
-
303
- // With the prototype parser, this callback expects a double and float, and returns the sum as a double
304
- const AddDoubleFloat = koffi.callback('double AddDoubleFloat(double d, float f)');
305
- ```
306
-
307
- Once your callback type is declared, you can use a pointer to it in struct definitions, or as function parameters and/or return types.
308
-
309
- ```{note}
310
- Callbacks **have changed in version 2.0**.
311
-
312
- In Koffi 1.x, callbacks were defined in a way that made them usable directly as parameter and return types, obscuring the underlying pointer.
313
-
314
- 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.
315
-
316
- Consult the [migration guide](changes.md) for more information.
317
- ```
318
-
319
- 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.
320
-
321
- Thus, Koffi distinguishes two callback modes:
322
-
323
- - [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.
324
- - [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.
325
-
326
- 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.
327
-
328
- ### Transient callbacks
329
-
330
- 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.
331
-
332
- ```c
333
- #include <string.h>
334
-
335
- int TransferToJS(const char *name, int age, int (*cb)(const char *str, int age))
336
- {
337
- char buf[64];
338
- snprintf(buf, sizeof(buf), "Hello %s!", str);
339
- return cb(buf, age);
340
- }
341
- ```
342
-
343
- ```js
344
- const koffi = require('koffi');
345
- const lib = koffi.load('./callbacks.so'); // Fake path
346
-
347
- const TransferCallback = koffi.callback('int TransferCallback(const char *str, int age)');
348
-
349
- const TransferToJS = lib.func('TransferToJS', 'int', ['str', 'int', koffi.pointer(TransferCallback)]);
350
-
351
- let ret = TransferToJS('Niels', 27, (str, age) => {
352
- console.log(str);
353
- console.log('Your age is:', age);
354
- return 42;
355
- });
356
- console.log(ret);
357
-
358
- // This example prints:
359
- // Hello Niels!
360
- // Your age is: 27
361
- // 42
362
- ```
363
-
364
- ### Registered callbacks
365
-
366
- *New in Koffi 2.0*
367
-
368
- 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.
369
-
370
- 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.
371
-
372
- The example below shows how to register and unregister delayed callbacks.
373
-
374
- ```c
375
- static const char *(*g_cb1)(const char *name);
376
- static void (*g_cb2)(const char *str);
377
-
378
- void RegisterFunctions(const char *(*cb1)(const char *name), void (*cb2)(const char *str))
379
- {
380
- g_cb1 = cb1;
381
- g_cb2 = cb2;
382
- }
383
-
384
- void SayIt(const char *name)
385
- {
386
- const char *str = g_cb1(name);
387
- g_cb2(str);
388
- }
389
- ```
390
-
391
- ```js
392
- const koffi = require('koffi');
393
- const lib = koffi.load('./callbacks.so'); // Fake path
394
-
395
- const GetCallback = koffi.callback('const char *GetCallback(const char *name)');
396
- const PrintCallback = koffi.callback('void PrintCallback(const char *str)');
397
-
398
- const RegisterFunctions = lib.func('void RegisterFunctions(GetCallback *cb1, PrintCallback *cb2)');
399
- const SayIt = lib.func('void SayIt(const char *name)');
400
-
401
- let cb1 = koffi.register(name => 'Hello ' + name + '!', koffi.pointer(GetCallback));
402
- let cb2 = koffi.register(console.log, 'PrintCallback *');
403
-
404
- RegisterFunctions(cb1, cb2);
405
- SayIt('Kyoto'); // Prints Hello Kyoto!
406
-
407
- koffi.unregister(cb1);
408
- koffi.unregister(cb2);
409
- ```
410
-
411
- ### Handling of exceptions
412
-
413
- If an exception happens inside the JS callback, the C API will receive 0 or NULL (depending on the return value type).
414
-
415
- Handle the exception yourself (with try/catch) if you need to handle exceptions differently.
416
-
417
332
  ## Thread safety
418
333
 
419
334
  Asynchronous functions run on worker threads. You need to deal with thread safety issues if you share data between threads.
package/doc/index.rst CHANGED
@@ -26,6 +26,7 @@ Table of contents
26
26
  start
27
27
  types
28
28
  functions
29
+ callbacks
29
30
  memory
30
31
  benchmarks
31
32
  contribute
package/doc/make.bat CHANGED
@@ -8,7 +8,7 @@ if "%SPHINXBUILD%" == "" (
8
8
  set SPHINXBUILD=sphinx-build
9
9
  )
10
10
  set SOURCEDIR=.
11
- set BUILDDIR=../build/doc
11
+ set BUILDDIR=dist
12
12
 
13
13
  %SPHINXBUILD% >NUL 2>NUL
14
14
  if errorlevel 9009 (
package/doc/types.md CHANGED
@@ -509,23 +509,50 @@ Read the documentation for [disposable types](functions.md#heap-allocated-values
509
509
 
510
510
  ### Type introspection
511
511
 
512
- *New in Koffi 2.0: `koffi.resolve()`*
513
-
514
- Koffi exposes three functions to explore type information:
515
-
516
- - `koffi.sizeof(type)` to get the size of a type
517
- - `koffi.alignof(type)` to get the alignment of a type
518
- - `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)
519
- - `koffi.resolve(type)` to get the resolved type object from a type string
512
+ *New in Koffi 2.0: `koffi.resolve()`, new in Koffi 2.2: `koffi.offsetof()`*
520
513
 
521
514
  ```{note}
522
- The value returned by `introspect()` has **changed in version 2.0**.
515
+ The value returned by `introspect()` has **changed in version 2.0 and in version 2.2**.
523
516
 
524
517
  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.
525
518
 
519
+ Starting in Koffi 2.2, each record member is exposed as an object containing the name, the type and the offset within the record.
520
+
526
521
  Consult the [migration guide](changes.md) for more information.
527
522
  ```
528
523
 
524
+ Use `koffi.introspect(type)` to get detailed information about a type: name, primitive, size, alignment, members (record types), reference type (array, pointer) and length (array).
525
+
526
+ ```js
527
+ const FoobarType = koffi.struct('FoobarType', {
528
+ a: 'int',
529
+ b: 'char *',
530
+ c: 'double'
531
+ });
532
+
533
+ console.log(koffi.introspect(FoobarType));
534
+
535
+ // Expected result on 64-bit machines:
536
+ // {
537
+ // name: 'FoobarType',
538
+ // primitive: 'Record',
539
+ // size: 24,
540
+ // alignment: 8,
541
+ // members: {
542
+ // a: { name: 'a', type: [External: 4b28a60], offset: 0 },
543
+ // b: { name: 'b', type: [External: 4b292e0], offset: 8 },
544
+ // c: { name: 'c', type: [External: 4b29260], offset: 16 }
545
+ // }
546
+ // }
547
+ ```
548
+
549
+ Koffi also exposes a few more utility functions to get a subset of this information:
550
+
551
+ - `koffi.sizeof(type)` to get the size of a type
552
+ - `koffi.alignof(type)` to get the alignment of a type
553
+ - `koffi.offsetof(type, member_name)` to get the offset of a record member
554
+ - `koffi.resolve(type)` to get the resolved type object from a type string
555
+
529
556
  Just like before, you can refer to primitive types by their name or through `koffi.types`:
530
557
 
531
558
  ```js
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.1.5",
4
- "stable": "2.1.5",
3
+ "version": "2.2.1",
4
+ "stable": "2.2.1",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
7
7
  "foreign",