koffi 2.6.4 → 2.6.5

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,11 @@
4
4
 
5
5
  ### Koffi 2.6
6
6
 
7
+ #### Koffi 2.6.5 (2023-10-28)
8
+
9
+ - Allow self-referential structs and unions
10
+ - Fix rare Node.js "FATAL ERROR" with some invalid types specifiers
11
+
7
12
  #### Koffi 2.6.4 (2023-10-26)
8
13
 
9
14
  - Fix build issue with recent Visual Studio versions
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/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_20220812.png" target="_blank"><img src="_static/perf_linux_20220812.png" alt="Linux x86_64 performance" style="width: 350px;"/></a></td>
14
- <td><a href="_static/perf_windows_20220812.png" target="_blank"><img src="_static/perf_windows_20220812.png" alt="Windows x86_64 performance" style="width: 350px;"/></a></td>
13
+ <td><a href="_static/perf_linux_20231028.png" target="_blank"><img src="_static/perf_linux_20231028.png" alt="Linux x86_64 performance" style="width: 350px;"/></a></td>
14
+ <td><a href="_static/perf_windows_20231028.png" target="_blank"><img src="_static/perf_windows_20231028.png" alt="Windows x86_64 performance" style="width: 350px;"/></a></td>
15
15
  </tr>
16
16
  </table>
17
17
 
@@ -31,9 +31,9 @@ This test is based around repeated calls to a simple standard C function `rand`,
31
31
 
32
32
  Benchmark | Iteration time | Relative performance | Overhead
33
33
  ------------- | -------------- | -------------------- | --------
34
- rand_napi | 842 ns | x1.00 | (ref)
35
- rand_koffi | 1114 ns | x0.76 | +32%
36
- rand_node_ffi | 44845 ns | x0.02 | +5224%
34
+ rand_napi | 700 ns | x1.00 | (ref)
35
+ rand_koffi | 1152 ns | x0.61 | +64%
36
+ rand_node_ffi | 32750 ns | x0.02 | +4576%
37
37
 
38
38
  Because rand is a pretty small function, the FFI overhead is clearly visible.
39
39
 
@@ -43,9 +43,9 @@ This test is similar to the rand one, but it is based on `atoi`, which takes a s
43
43
 
44
44
  Benchmark | Iteration time | Relative performance | Overhead
45
45
  ------------- | -------------- | -------------------- | --------
46
- atoi_napi | 921 ns | x1.00 | (ref)
47
- atoi_koffi | 1357 ns | x0.68 | +47%
48
- atoi_node_ffi | 152550 ns | x0.006 | +16472%
46
+ atoi_napi | 1028 ns | x1.00 | (ref)
47
+ atoi_koffi | 1730 ns | x0.59 | +68%
48
+ atoi_node_ffi | 121670 ns | x0.008 | +11738%
49
49
 
50
50
  Because atoi is a pretty small function, the FFI overhead is clearly visible.
51
51
 
@@ -58,10 +58,10 @@ This benchmark uses the CPU-based image drawing functions in Raylib. The calls a
58
58
 
59
59
  Benchmark | Iteration time | Relative performance | Overhead
60
60
  ------------------ | -------------- | -------------------- | --------
61
- raylib_cc | 215.7 µs | x1.20 | -17%
62
- raylib_node_raylib | 258.9 µs | x1.00 | (ref)
63
- raylib_koffi | 311.6 µs | x0.83 | +20%
64
- raylib_node_ffi | 928.4 µs | x0.28 | +259%
61
+ raylib_cc | 18.5 µs | x1.42 | -30%
62
+ raylib_node_raylib | 26.3 µs | x1.00 | (ref)
63
+ raylib_koffi | 28.0 µs | x0.94 | +6%
64
+ raylib_node_ffi | 87.0 µs | x0.30 | +230%
65
65
 
66
66
  ## Windows x86_64
67
67
 
@@ -77,9 +77,9 @@ This test is based around repeated calls to a simple standard C function `rand`,
77
77
 
78
78
  Benchmark | Iteration time | Relative performance | Overhead
79
79
  ------------- | -------------- | -------------------- | --------
80
- rand_napi | 964 ns | x1.00 | (ref)
81
- rand_koffi | 1274 ns | x0.76 | +32%
82
- rand_node_ffi | 42300 ns | x0.02 | +4289%
80
+ rand_napi | 859 ns | x1.00 | (ref)
81
+ rand_koffi | 1352 ns | x0.64 | +57%
82
+ rand_node_ffi | 35640 ns | x0.02 | +4048%
83
83
 
84
84
  Because rand is a pretty small function, the FFI overhead is clearly visible.
85
85
 
@@ -91,9 +91,9 @@ The results below were measured on my x86_64 Windows machine (Intel® Core™ i5
91
91
 
92
92
  Benchmark | Iteration time | Relative performance | Overhead
93
93
  ------------- | -------------- | -------------------- | --------
94
- atoi_napi | 1415 ns | x1.00 | (ref)
95
- atoi_koffi | 2193 ns | x0.65 | +55%
96
- atoi_node_ffi | 168300 ns | x0.008 | +11792%
94
+ atoi_napi | 1336 ns | x1.00 | (ref)
95
+ atoi_koffi | 2440 ns | x0.55 | +83%
96
+ atoi_node_ffi | 136890 ns | x0.010 | +10144%
97
97
 
98
98
  Because atoi is a pretty small function, the FFI overhead is clearly visible.
99
99
 
@@ -106,26 +106,19 @@ This benchmark uses the CPU-based image drawing functions in Raylib. The calls a
106
106
 
107
107
  Benchmark | Iteration time | Relative performance | Overhead
108
108
  ------------------ | -------------- | -------------------- | --------
109
- raylib_cc | 211.8 µs | x1.25 | -20%
110
- raylib_node_raylib | 264.4 µs | x1.00 | (ref)
111
- raylib_koffi | 318.9 µs | x0.83 | +21%
112
- raylib_node_ffi | 1146.2 µs | x0.23 | +334%
113
-
114
- Please note that in order to get fair numbers for raylib_node_raylib, it was recompiled with clang-cl before running the benchmark with the following commands:
115
-
116
- ```batch
117
- cd node_modules\raylib
118
- rmdir /S /Q bin build
119
- npx cmake-js compile -t ClangCL
120
- ```
109
+ raylib_cc | 18.2 µs | x1.50 | -33%
110
+ raylib_node_raylib | 27.3 µs | x1.00 | (ref)
111
+ raylib_koffi | 29.8 µs | x0.92 | +9%
112
+ raylib_node_ffi | 96.3 µs | x0.28 | +253%
121
113
 
122
114
  ## Running benchmarks
123
115
 
124
- Open a console, go to `koffi/benchmark` and run `../../cnoke/cnoke.js` (or `node ..\..\cnoke\cnoke.js` on Windows) before doing anything else.
125
-
126
- Please note that all benchmark results are made with Clang-built binaries.
116
+ Please note that all benchmark results on this page are made with Clang-built binaries.
127
117
 
128
118
  ```sh
119
+ cd koffi
120
+ node ../../cnoke/cnoke.js --prefer-clang
121
+
129
122
  cd koffi/benchmark
130
123
  node ../../cnoke/cnoke.js --prefer-clang
131
124
  ```
Binary file
package/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.6.4",
382
- stable: "2.6.4",
381
+ version: "2.6.5",
382
+ stable: "2.6.5",
383
383
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
384
384
  keywords: [
385
385
  "foreign",
@@ -413,10 +413,9 @@ var require_package = __commonJS({
413
413
  chalk: "^4.1.2",
414
414
  esbuild: "^0.19.2",
415
415
  "ffi-napi": "^4.0.3",
416
- "ffi-rs": "^1.0.12",
417
416
  minimatch: "^5.0.1",
418
417
  "node-ssh": "^12.0.3",
419
- raylib: "^0.9.2",
418
+ raylib: "^0.14.0",
420
419
  "ref-napi": "^3.0.3",
421
420
  "ref-struct-di": "^1.1.1",
422
421
  tar: "^6.1.11"
package/indirect.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.6.4",
382
- stable: "2.6.4",
381
+ version: "2.6.5",
382
+ stable: "2.6.5",
383
383
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
384
384
  keywords: [
385
385
  "foreign",
@@ -413,10 +413,9 @@ var require_package = __commonJS({
413
413
  chalk: "^4.1.2",
414
414
  esbuild: "^0.19.2",
415
415
  "ffi-napi": "^4.0.3",
416
- "ffi-rs": "^1.0.12",
417
416
  minimatch: "^5.0.1",
418
417
  "node-ssh": "^12.0.3",
419
- raylib: "^0.9.2",
418
+ raylib: "^0.14.0",
420
419
  "ref-napi": "^3.0.3",
421
420
  "ref-struct-di": "^1.1.1",
422
421
  tar: "^6.1.11"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.6.4",
4
- "stable": "2.6.4",
3
+ "version": "2.6.5",
4
+ "stable": "2.6.5",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
7
7
  "foreign",
@@ -340,9 +340,9 @@ function Builder(config = {}) {
340
340
 
341
341
  function get_cache_directory() {
342
342
  if (process.platform == 'win32') {
343
- let cache_dir = process.env['APPDATA'];
343
+ let cache_dir = process.env['LOCALAPPDATA'] || process.env['APPDATA'];
344
344
  if (cache_dir == null)
345
- throw new Error('Missing APPDATA environment variable');
345
+ throw new Error('Missing LOCALAPPDATA and APPDATA environment variable');
346
346
 
347
347
  cache_dir = path.join(cache_dir, 'cnoke');
348
348
  return cache_dir;
@@ -2428,11 +2428,15 @@ StatResult StatFile(const char *filename, unsigned int flags, FileInfo *out_info
2428
2428
  (int64_t)sb.st_mtim.tv_nsec / 1000000;
2429
2429
  out_info->btime = (int64_t)sb.__st_birthtim.tv_sec * 1000 +
2430
2430
  (int64_t)sb.__st_birthtim.tv_nsec / 1000000;
2431
- #else
2431
+ #elif defined(__FreeBSD__)
2432
2432
  out_info->mtime = (int64_t)sb.st_mtim.tv_sec * 1000 +
2433
2433
  (int64_t)sb.st_mtim.tv_nsec / 1000000;
2434
2434
  out_info->btime = (int64_t)sb.st_birthtim.tv_sec * 1000 +
2435
2435
  (int64_t)sb.st_birthtim.tv_nsec / 1000000;
2436
+ #else
2437
+ out_info->mtime = (int64_t)sb.st_mtim.tv_sec * 1000 +
2438
+ (int64_t)sb.st_mtim.tv_nsec / 1000000;
2439
+ out_info->btime = out_info->mtime;
2436
2440
  #endif
2437
2441
  out_info->mode = (unsigned int)sb.st_mode;
2438
2442
  out_info->uid = (uint32_t)sb.st_uid;
@@ -0,0 +1,98 @@
1
+ # Copyright 2023 Niels Martignène <niels.martignene@protonmail.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ # this software and associated documentation files (the “Software”), to deal in
5
+ # the Software without restriction, including without limitation the rights to use,
6
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
7
+ # Software, and to permit persons to whom the Software is furnished to do so,
8
+ # subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
14
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
15
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
17
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20
+ # OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ add_library(raylib SHARED
23
+ ../../../vendor/raylib/src/rcore.c
24
+ ../../../vendor/raylib/src/rshapes.c
25
+ ../../../vendor/raylib/src/rtextures.c
26
+ ../../../vendor/raylib/src/rtext.c
27
+ ../../../vendor/raylib/src/rmodels.c
28
+ ../../../vendor/raylib/src/utils.c
29
+ ../../../vendor/raylib/src/rglfw.c
30
+ ../../../vendor/raylib/src/raudio.c
31
+ )
32
+ set_target_properties(raylib PROPERTIES PREFIX "")
33
+ target_include_directories(raylib PRIVATE ../../../vendor/raylib/src/external/glfw/include)
34
+ target_compile_definitions(raylib PRIVATE PLATFORM_DESKTOP GRAPHICS_API_OPENGL_21 BUILD_LIBTYPE_SHARED NDEBUG)
35
+
36
+ if(MSVC)
37
+ target_compile_options(raylib PRIVATE /wd4244 /wd4305)
38
+ else()
39
+ target_compile_options(raylib PRIVATE -Wno-sign-compare -Wno-old-style-declaration
40
+ -Wno-unused-function -Wno-missing-field-initializers
41
+ -Wno-unused-value -Wno-implicit-fallthrough -Wno-stringop-overflow
42
+ -Wno-unused-result)
43
+ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
44
+ target_compile_options(raylib PRIVATE -Wno-unknown-warning-option)
45
+ endif()
46
+ endif()
47
+
48
+ if(WIN32)
49
+ target_compile_definitions(raylib PRIVATE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
50
+ target_link_libraries(raylib PRIVATE winmm)
51
+ endif()
52
+
53
+ if(CMAKE_SYSTEM_NAME MATCHES "BSD")
54
+ target_include_directories(raylib PRIVATE /usr/local/include)
55
+ endif()
56
+
57
+ if(APPLE)
58
+ target_compile_options(raylib PRIVATE -Wno-unknown-warning-option -Wno-macro-redefined)
59
+ target_compile_definitions(raylib PRIVATE GL_SILENCE_DEPRECATION)
60
+ set_source_files_properties(../../../vendor/raylib/src/rglfw.c PROPERTIES COMPILE_FLAGS "-x objective-c")
61
+ target_link_libraries(raylib PRIVATE "-framework Cocoa" "-framework IOKit" "-framework CoreFoundation" "-framework OpenGL")
62
+ endif()
63
+
64
+ if(UNIX AND NOT APPLE)
65
+ set(missing_xlib "")
66
+
67
+ find_path(XLIB_INCLUDE_DIRS X11/Xlib.h)
68
+ if(NOT XLIB_INCLUDE_DIRS)
69
+ list(APPEND missing_xlib Xlib)
70
+ endif()
71
+ find_path(XCURSOR_INCLUDE_DIRS X11/Xcursor/Xcursor.h)
72
+ if(NOT XCURSOR_INCLUDE_DIRS)
73
+ list(APPEND missing_xlib Xcursor)
74
+ endif()
75
+ find_path(XRANDR_INCLUDE_DIRS X11/extensions/Xrandr.h)
76
+ if(NOT XRANDR_INCLUDE_DIRS)
77
+ list(APPEND missing_xlib Xrandr)
78
+ endif()
79
+ find_path(XKB_INCLUDE_DIRS X11/XKBlib.h)
80
+ if(NOT XKB_INCLUDE_DIRS)
81
+ list(APPEND missing_xlib Xkbcommon)
82
+ endif()
83
+ find_path(XINERAMA_INCLUDE_DIRS X11/extensions/Xinerama.h)
84
+ if(NOT XINERAMA_INCLUDE_DIRS)
85
+ list(APPEND missing_xlib Xinerama)
86
+ endif()
87
+ find_path(XINPUT_INCLUDE_DIRS X11/extensions/XInput2.h)
88
+ if(NOT XINPUT_INCLUDE_DIRS)
89
+ list(APPEND missing_xlib XInput2)
90
+ endif()
91
+
92
+ if(missing_xlib)
93
+ list(JOIN missing_xlib ", " missing_xlib_str)
94
+ message(FATAL_ERROR "Missing X11 development files: ${missing_xlib_str}")
95
+ endif()
96
+
97
+ target_include_directories(raylib PRIVATE ${XLIB_INCLUDE_DIRS})
98
+ endif()
@@ -0,0 +1,27 @@
1
+ # Copyright 2023 Niels Martignène <niels.martignene@protonmail.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ # this software and associated documentation files (the “Software”), to deal in
5
+ # the Software without restriction, including without limitation the rights to use,
6
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
7
+ # Software, and to permit persons to whom the Software is furnished to do so,
8
+ # subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
14
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
15
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
17
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20
+ # OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ add_library(sqlite3 SHARED ../../../vendor/sqlite3/sqlite3.c)
23
+ set_target_properties(sqlite3 PROPERTIES PREFIX "")
24
+
25
+ if(WIN32)
26
+ target_compile_definitions(sqlite3 PRIVATE SQLITE_API=__declspec\(dllexport\))
27
+ endif()
@@ -225,18 +225,38 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
225
225
  return env.Null();
226
226
  }
227
227
 
228
+ RG_DEFER_NC(err_guard, len = instance->types.len) {
229
+ for (Size i = len + 1; i < instance->types.len; i++) {
230
+ const TypeInfo &type = instance->types[i];
231
+ instance->types_map.Remove(type.name);
232
+ }
233
+
234
+ instance->types.RemoveFrom(len);
235
+ };
236
+
228
237
  TypeInfo *type = instance->types.AppendDefault();
229
- RG_DEFER_N(err_guard) { instance->types.RemoveLast(1); };
230
238
 
231
239
  Napi::String name = info[0].As<Napi::String>();
232
240
  Napi::Object obj = info[named].As<Napi::Object>();
233
241
  Napi::Array keys = obj.GetPropertyNames();
234
242
 
235
- type->name = named ? DuplicateString(name.Utf8Value().c_str(), &instance->str_alloc).ptr
236
- : Fmt(&instance->str_alloc, "<anonymous_%1>", instance->types.len).ptr;
243
+ if (named) {
244
+ type->name = DuplicateString(name.Utf8Value().c_str(), &instance->str_alloc).ptr;
245
+
246
+ bool inserted;
247
+ instance->types_map.TrySet(type->name, type, &inserted);
248
+
249
+ if (!inserted) {
250
+ ThrowError<Napi::Error>(env, "Duplicate type name '%1'", type->name);
251
+ return env.Null();
252
+ }
253
+ } else {
254
+ type->name = Fmt(&instance->str_alloc, "<anonymous_%1>", instance->types.len).ptr;
255
+ }
237
256
 
238
257
  type->primitive = PrimitiveKind::Record;
239
258
  type->align = 1;
259
+ type->flags = (int)TypeFlag::IsIncomplete;
240
260
 
241
261
  HashSet<const char *> members;
242
262
  int64_t size = 0;
@@ -308,16 +328,7 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
308
328
  }
309
329
  type->size = (int32_t)size;
310
330
 
311
- // If the insert succeeds, we cannot fail anymore
312
- if (named) {
313
- bool inserted;
314
- instance->types_map.TrySet(type->name, type, &inserted);
315
-
316
- if (!inserted) {
317
- ThrowError<Napi::Error>(env, "Duplicate type name '%1'", type->name);
318
- return env.Null();
319
- }
320
- }
331
+ type->flags &= ~(int)TypeFlag::IsIncomplete;
321
332
  err_guard.Disable();
322
333
 
323
334
  return WrapType(env, instance, type);
@@ -354,18 +365,38 @@ static Napi::Value CreateUnionType(const Napi::CallbackInfo &info)
354
365
  return env.Null();
355
366
  }
356
367
 
368
+ RG_DEFER_NC(err_guard, len = instance->types.len) {
369
+ for (Size i = len + 1; i < instance->types.len; i++) {
370
+ const TypeInfo &type = instance->types[i];
371
+ instance->types_map.Remove(type.name);
372
+ }
373
+
374
+ instance->types.RemoveFrom(len);
375
+ };
376
+
357
377
  TypeInfo *type = instance->types.AppendDefault();
358
- RG_DEFER_N(err_guard) { instance->types.RemoveLast(1); };
359
378
 
360
379
  Napi::String name = info[0].As<Napi::String>();
361
380
  Napi::Object obj = info[named].As<Napi::Object>();
362
381
  Napi::Array keys = obj.GetPropertyNames();
363
382
 
364
- type->name = named ? DuplicateString(name.Utf8Value().c_str(), &instance->str_alloc).ptr
365
- : Fmt(&instance->str_alloc, "<anonymous_%1>", instance->types.len).ptr;
383
+ if (named) {
384
+ type->name = DuplicateString(name.Utf8Value().c_str(), &instance->str_alloc).ptr;
385
+
386
+ bool inserted;
387
+ instance->types_map.TrySet(type->name, type, &inserted);
388
+
389
+ if (!inserted) {
390
+ ThrowError<Napi::Error>(env, "Duplicate type name '%1'", type->name);
391
+ return env.Null();
392
+ }
393
+ } else {
394
+ type->name = Fmt(&instance->str_alloc, "<anonymous_%1>", instance->types.len).ptr;
395
+ }
366
396
 
367
397
  type->primitive = PrimitiveKind::Union;
368
398
  type->align = 1;
399
+ type->flags = (int)TypeFlag::IsIncomplete;
369
400
 
370
401
  HashSet<const char *> members;
371
402
  int32_t size = 0;
@@ -428,16 +459,7 @@ static Napi::Value CreateUnionType(const Napi::CallbackInfo &info)
428
459
  }
429
460
  type->size = (int32_t)size;
430
461
 
431
- // If the insert succeeds, we cannot fail anymore
432
- if (named) {
433
- bool inserted;
434
- instance->types_map.TrySet(type->name, type, &inserted);
435
-
436
- if (!inserted) {
437
- ThrowError<Napi::Error>(env, "Duplicate type name '%1'", type->name);
438
- return env.Null();
439
- }
440
- }
462
+ type->flags &= ~(int)TypeFlag::IsIncomplete;
441
463
  err_guard.Disable();
442
464
 
443
465
  // Union constructor
@@ -104,8 +104,9 @@ class CallData;
104
104
  typedef void DisposeFunc (Napi::Env env, const TypeInfo *type, const void *ptr);
105
105
 
106
106
  enum class TypeFlag {
107
- HasTypedArray = 1 << 0,
108
- IsCharLike = 1 << 1
107
+ IsIncomplete = 1 << 0,
108
+ HasTypedArray = 1 << 1,
109
+ IsCharLike = 1 << 2
109
110
  };
110
111
 
111
112
  enum class ArrayHint {
@@ -120,7 +120,9 @@ const TypeInfo *ResolveType(Napi::Value value, int *out_directions)
120
120
  const TypeInfo *type = ResolveType(env, str.c_str(), out_directions);
121
121
 
122
122
  if (!type) {
123
- ThrowError<Napi::TypeError>(env, "Unknown or invalid type name '%1'", str.c_str());
123
+ if (!env.IsExceptionPending()) {
124
+ ThrowError<Napi::TypeError>(env, "Unknown or invalid type name '%1'", str.c_str());
125
+ }
124
126
  return nullptr;
125
127
  }
126
128
 
@@ -299,6 +301,11 @@ const TypeInfo *ResolveType(Napi::Env env, Span<const char> str, int *out_direct
299
301
  RG_ASSERT(type);
300
302
  }
301
303
 
304
+ if (type->flags & (int)TypeFlag::IsIncomplete) [[unlikely]] {
305
+ ThrowError<Napi::TypeError>(env, "Cannot directly use incomplete type");
306
+ return nullptr;
307
+ }
308
+
302
309
  return type;
303
310
  }
304
311