koffi 2.5.18 → 2.5.20

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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@
4
4
 
5
5
  ### Koffi 2.5
6
6
 
7
+ #### Koffi 2.5.20 (2023-08-31)
8
+
9
+ - Fix possible crash with async registered callbacks introduced in Koffi 2.5.19
10
+ - Various documentation fixes and improvements
11
+
12
+ #### Koffi 2.5.19 (2023-08-29)
13
+
14
+ - Create thread-safe function broker lazily
15
+ - Add [koffi.reset()](misc.md#reset-internal-state) for type names and async broker
16
+
7
17
  #### Koffi 2.5.18 (2023-08-27)
8
18
 
9
19
  - Fix compatibility with Electron
@@ -327,7 +337,7 @@ Pre-built binaries don't work correctly in Koffi 2.5.13 to 2.5.15, skip those ve
327
337
  **Main changes:**
328
338
 
329
339
  - Add [koffi.as()](polymorphism.md#input-polymorphism) to support polymorphic APIs based on `void *` parameters
330
- - Add [endian-sensitive integer types](input.md#endian-sensitive-types): `intX_le_t`, `intX_be_t`, `uintX_le_t`, `uintX_be_t`
340
+ - Add [endian-sensitive integer types](input.md#endian-sensitive-integers): `intX_le_t`, `intX_be_t`, `uintX_le_t`, `uintX_be_t`
331
341
  - Accept typed arrays for `void *` parameters
332
342
  - Introduce `koffi.opaque()` to replace `koffi.handle()` (which remains supported until Koffi 3.0)
333
343
  - Support JS Array and TypedArray to fill struct and array pointer members
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/doc/callbacks.md CHANGED
@@ -88,7 +88,7 @@ console.log(ret);
88
88
 
89
89
  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.
90
90
 
91
- When you are done, call `koffi.unregister()` (with the value returned by `koffi.register()`) to release the slot. A maximum of 1024 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.
91
+ When you are done, call `koffi.unregister()` (with the value returned by `koffi.register()`) to release the slot. A maximum of 8192 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.
92
92
 
93
93
  The example below shows how to register and unregister delayed callbacks.
94
94
 
package/doc/contribute.md CHANGED
@@ -14,7 +14,7 @@ Start by cloning the repository with [Git](https://git-scm.com/):
14
14
 
15
15
  ```sh
16
16
  git clone https://github.com/Koromix/rygel
17
- cd rygel/src/koffi
17
+ cd rygel
18
18
  ```
19
19
 
20
20
  As said before, this is a monorepository containg multiple projects, hence the name.
@@ -30,8 +30,8 @@ First, make sure the following dependencies are met:
30
30
  Once this is done, run this command _from the test or the benchmark directory_ (depending on what you want to build):
31
31
 
32
32
  ```sh
33
- cd koffi/test # or cd koffi/benchmark
34
- node ../../cnoke/cnoke.js
33
+ cd src/koffi
34
+ node ../cnoke/cnoke.js
35
35
  ```
36
36
 
37
37
  ### Other platforms
@@ -46,11 +46,31 @@ Make sure the following dependencies are met:
46
46
  Once this is done, run this command _from the test or the benchmark directory_ (depending on what you want to build):
47
47
 
48
48
  ```sh
49
- cd koffi/test # or cd koffi/benchmark
49
+ cd src/koffi
50
+ node ../cnoke/cnoke.js
51
+ ```
52
+
53
+ ## Run tests
54
+
55
+ ### On your machine
56
+
57
+ Once Koffi is built, you can build the tests and run them with the following commands:
58
+
59
+ ```sh
60
+ cd src/koffi/test
50
61
  node ../../cnoke/cnoke.js
62
+
63
+ node sync.js # Run synchronous unit tests
64
+ node async.js # Run asynchronous unit tests
65
+ node callbacks.js # Run callback unit tests
66
+ node union.js # Run union unit tests
67
+ node win32.js # Run Windows-specific unit tests (only on Windows)
68
+
69
+ node sqlite.js # Run SQLite integration tests
70
+ node raylib.js # Run Raylib integration tests
51
71
  ```
52
72
 
53
- ## Running tests
73
+ ### On virtual machines
54
74
 
55
75
  Koffi is tested on multiple architectures using emulated (accelerated when possible) QEMU machines. First, you need to install qemu packages, such as `qemu-system` (or even `qemu-system-gui`) on Ubuntu.
56
76
 
@@ -59,7 +79,7 @@ These machines are not included directly in this repository (for license and siz
59
79
  For example, if you want to run the tests on Debian ARM64, run the following commands:
60
80
 
61
81
  ```sh
62
- cd rygel/src/koffi/tools/
82
+ cd src/koffi/tools/
63
83
  wget -q -O- https://koromix.dev/files/machines/qemu_debian_arm64.tar.zst | zstd -d | tar xv
64
84
  sha256sum -c --ignore-missing registry/sha256sum.txt
65
85
  ```
package/doc/input.md CHANGED
@@ -56,26 +56,26 @@ let struct1 = koffi.struct({ dummy: 'long' });
56
56
  let struct2 = koffi.struct({ dummy: koffi.types.long });
57
57
  ```
58
58
 
59
- ### Endian-sensitive types
59
+ ### Endian-sensitive integers
60
60
 
61
61
  *New in Koffi 2.1*
62
62
 
63
63
  Koffi defines a bunch of endian-sensitive types, which can be used when dealing with binary data (network payloads, binary file formats, etc.).
64
64
 
65
- JS type | C type | Bytes | Signedness | Endianness
66
- ---------------- | ---------------------- | ----- | ---------- | -------------
67
- Number (integer) | int16_le, int16_le_t | 2 | Signed | Little Endian
68
- Number (integer) | int16_be, int16_be_t | 2 | Signed | Big Endian
69
- Number (integer) | uint16_le, uint16_le_t | 2 | Unsigned | Little Endian
70
- Number (integer) | uint16_be, uint16_be_t | 2 | Unsigned | Big Endian
71
- Number (integer) | int32_le, int32_le_t | 4 | Signed | Little Endian
72
- Number (integer) | int32_be, int32_be_t | 4 | Signed | Big Endian
73
- Number (integer) | uint32_le, uint32_le_t | 4 | Unsigned | Little Endian
74
- Number (integer) | uint32_be, uint32_be_t | 4 | Unsigned | Big Endian
75
- Number (integer) | int64_le, int64_le_t | 8 | Signed | Little Endian
76
- Number (integer) | int64_be, int64_be_t | 8 | Signed | Big Endian
77
- Number (integer) | uint64_le, uint64_le_t | 8 | Unsigned | Little Endian
78
- Number (integer) | uint64_be, uint64_be_t | 8 | Unsigned | Big Endian
65
+ C type | Bytes | Signedness | Endianness
66
+ ---------------------- | ----- | ---------- | -------------
67
+ int16_le, int16_le_t | 2 | Signed | Little Endian
68
+ int16_be, int16_be_t | 2 | Signed | Big Endian
69
+ uint16_le, uint16_le_t | 2 | Unsigned | Little Endian
70
+ uint16_be, uint16_be_t | 2 | Unsigned | Big Endian
71
+ int32_le, int32_le_t | 4 | Signed | Little Endian
72
+ int32_be, int32_be_t | 4 | Signed | Big Endian
73
+ uint32_le, uint32_le_t | 4 | Unsigned | Little Endian
74
+ uint32_be, uint32_be_t | 4 | Unsigned | Big Endian
75
+ int64_le, int64_le_t | 8 | Signed | Little Endian
76
+ int64_be, int64_be_t | 8 | Signed | Big Endian
77
+ uint64_le, uint64_le_t | 8 | Unsigned | Little Endian
78
+ uint64_be, uint64_be_t | 8 | Unsigned | Big Endian
79
79
 
80
80
  ## Struct types
81
81
 
package/doc/misc.md CHANGED
@@ -106,9 +106,9 @@ You can use `koffi.stats()` to get a few statistics related to Koffi.
106
106
 
107
107
  *New in Koffi 2.3.14*
108
108
 
109
- You can use `koffi.errno()` to the current errno value, and `koffi.errno(value)` to change it.
109
+ You can use `koffi.errno()` to get the current errno value, and `koffi.errno(value)` to change it.
110
110
 
111
- The standard POSIX error codes are available in `koffi.os.errno`, as in the example below:
111
+ The standard POSIX error codes are available in `koffi.os.errno`, as shown below:
112
112
 
113
113
  ```js
114
114
  const assert = require('assert');
@@ -125,3 +125,18 @@ assert.equal(koffi.errno(), koffi.os.errno.EBADF);
125
125
 
126
126
  console.log('close() with invalid FD is POSIX compliant!');
127
127
  ```
128
+
129
+ ## Reset internal state
130
+
131
+ *New in Koffi 2.5.19*
132
+
133
+ You can use `koffi.reset()` to clear some Koffi internal state such as:
134
+
135
+ - Parser type names
136
+ - Asynchronous function broker (useful to avoid false positive with `jest --detectOpenHandles`)
137
+
138
+ This function is mainly intended for test code, when you execute the same code over and over and you need to reuse type names.
139
+
140
+ ```{warning}
141
+ Trying to use a function or a type that was initially defined before the reset is undefined behavior and will likely lead to a crash!
142
+ ```
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.5.18",
4
- "stable": "2.5.18",
3
+ "version": "2.5.20",
4
+ "stable": "2.5.20",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
7
7
  "foreign",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cnoke",
3
- "version": "4.0.1",
3
+ "version": "4.0.2",
4
4
  "description": "Build native Node addons based on CMake, without extra dependency",
5
5
  "keywords": [
6
6
  "native",
package/src/index.d.ts CHANGED
@@ -145,6 +145,8 @@ declare module 'koffi' {
145
145
  export function errno(): number;
146
146
  export function errno(value: number): number;
147
147
 
148
+ export function reset(): void;
149
+
148
150
  export const internal: Boolean;
149
151
  export const extension: String;
150
152
 
package/src/index.js CHANGED
@@ -378,8 +378,8 @@ var require_package = __commonJS({
378
378
  "build/dist/src/koffi/package.json"(exports2, module2) {
379
379
  module2.exports = {
380
380
  name: "koffi",
381
- version: "2.5.18",
382
- stable: "2.5.18",
381
+ version: "2.5.20",
382
+ stable: "2.5.20",
383
383
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
384
384
  keywords: [
385
385
  "foreign",
@@ -47,6 +47,10 @@ endif()
47
47
 
48
48
  # ---- Koffi ----
49
49
 
50
+ # Recompute the version string after each commit
51
+ if(EXISTS "${CMAKE_SOURCE_DIR}/../../.git/logs/HEAD")
52
+ configure_file("${CMAKE_SOURCE_DIR}/../../.git/logs/HEAD" git_logs_HEAD COPYONLY)
53
+ endif()
50
54
  if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/package.json)
51
55
  file(READ ${CMAKE_CURRENT_SOURCE_DIR}/package.json PKG)
52
56
  else()
@@ -1236,6 +1236,9 @@ void CallData::PopOutArguments()
1236
1236
 
1237
1237
  void *CallData::ReserveTrampoline(const FunctionInfo *proto, Napi::Function func)
1238
1238
  {
1239
+ if (!InitAsyncBroker(env, instance)) [[unlikely]]
1240
+ return nullptr;
1241
+
1239
1242
  int16_t idx;
1240
1243
  {
1241
1244
  std::lock_guard<std::mutex> lock(shared.mutex);
@@ -1656,6 +1656,9 @@ static Napi::Value RegisterCallback(const Napi::CallbackInfo &info)
1656
1656
  Napi::Env env = info.Env();
1657
1657
  InstanceData *instance = env.GetInstanceData<InstanceData>();
1658
1658
 
1659
+ if (!InitAsyncBroker(env, instance)) [[unlikely]]
1660
+ return env.Null();
1661
+
1659
1662
  bool has_recv = (info.Length() >= 3 && info[1].IsFunction());
1660
1663
 
1661
1664
  if (info.Length() < 2u + has_recv) {
@@ -1937,6 +1940,22 @@ InstanceMemory::~InstanceMemory()
1937
1940
  #endif
1938
1941
  }
1939
1942
 
1943
+ bool InitAsyncBroker(Napi::Env env, InstanceData *instance)
1944
+ {
1945
+ if (!instance->broker) {
1946
+ if (napi_create_threadsafe_function(env, nullptr, nullptr,
1947
+ Napi::String::New(env, "Koffi Async Callback Broker"),
1948
+ 0, 1, nullptr, nullptr, nullptr,
1949
+ CallData::RelayAsync, &instance->broker) != napi_ok) {
1950
+ LogError("Failed to create async callback broker");
1951
+ return false;
1952
+ }
1953
+ napi_unref_threadsafe_function(env, instance->broker);
1954
+ }
1955
+
1956
+ return true;
1957
+ }
1958
+
1940
1959
  static void RegisterPrimitiveType(Napi::Env env, Napi::Object map, std::initializer_list<const char *> names,
1941
1960
  PrimitiveKind primitive, int32_t size, int16_t align, const char *ref = nullptr)
1942
1961
  {
@@ -2068,6 +2087,25 @@ static Napi::Object InitBaseTypes(Napi::Env env)
2068
2087
  return types;
2069
2088
  }
2070
2089
 
2090
+ static Napi::Value ResetKoffi(const Napi::CallbackInfo &info)
2091
+ {
2092
+ Napi::Env env = info.Env();
2093
+ InstanceData *instance = env.GetInstanceData<InstanceData>();
2094
+
2095
+ if (instance->broker) {
2096
+ napi_release_threadsafe_function(instance->broker, napi_tsfn_abort);
2097
+ instance->broker = nullptr;
2098
+ }
2099
+
2100
+ instance->types.Clear();
2101
+ instance->types_map.Clear();
2102
+ instance->callbacks.Clear();
2103
+
2104
+ InitBaseTypes(env);
2105
+
2106
+ return env.Undefined();
2107
+ }
2108
+
2071
2109
  static InstanceData *CreateInstance(Napi::Env env)
2072
2110
  {
2073
2111
  InstanceData *instance = new InstanceData();
@@ -2078,15 +2116,6 @@ static InstanceData *CreateInstance(Napi::Env env)
2078
2116
  instance->debug = GetDebugFlag("DUMP_CALLS");
2079
2117
  FillRandomSafe(&instance->tag_lower, RG_SIZE(instance->tag_lower));
2080
2118
 
2081
- if (napi_create_threadsafe_function(env, nullptr, nullptr,
2082
- Napi::String::New(env, "Koffi Async Callback Broker"),
2083
- 0, 1, nullptr, nullptr, nullptr,
2084
- CallData::RelayAsync, &instance->broker) != napi_ok) {
2085
- LogError("Failed to create async callback broker");
2086
- return nullptr;
2087
- }
2088
- napi_unref_threadsafe_function(env, instance->broker);
2089
-
2090
2119
  #ifdef _WIN32
2091
2120
  TEB *teb = GetTEB();
2092
2121
 
@@ -2141,6 +2170,8 @@ static Napi::Object InitModule(Napi::Env env, Napi::Object exports)
2141
2170
  exports.Set("address", Napi::Function::New(env, GetPointerAddress));
2142
2171
  exports.Set("call", Napi::Function::New(env, CallPointerSync));
2143
2172
 
2173
+ exports.Set("reset", Napi::Function::New(env, ResetKoffi));
2174
+
2144
2175
  exports.Set("errno", Napi::Function::New(env, GetOrSetErrNo));
2145
2176
 
2146
2177
  Napi::Object os = Napi::Object::New(env);
@@ -341,4 +341,6 @@ Napi::Value TranslateNormalCall(const Napi::CallbackInfo &info);
341
341
  Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info);
342
342
  Napi::Value TranslateAsyncCall(const Napi::CallbackInfo &info);
343
343
 
344
+ bool InitAsyncBroker(Napi::Env env, InstanceData *instance);
345
+
344
346
  }