koffi 2.5.9 → 2.5.11

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 (32) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/build/koffi/darwin_arm64/koffi.node +0 -0
  3. package/build/koffi/darwin_x64/koffi.node +0 -0
  4. package/build/koffi/freebsd_arm64/koffi.node +0 -0
  5. package/build/koffi/freebsd_ia32/koffi.node +0 -0
  6. package/build/koffi/freebsd_x64/koffi.node +0 -0
  7. package/build/koffi/linux_arm32hf/koffi.node +0 -0
  8. package/build/koffi/linux_arm64/koffi.node +0 -0
  9. package/build/koffi/linux_ia32/koffi.node +0 -0
  10. package/build/koffi/linux_riscv64hf64/koffi.node +0 -0
  11. package/build/koffi/linux_x64/koffi.node +0 -0
  12. package/build/koffi/openbsd_ia32/koffi.node +0 -0
  13. package/build/koffi/openbsd_x64/koffi.node +0 -0
  14. package/build/koffi/win32_arm64/koffi.node +0 -0
  15. package/build/koffi/win32_ia32/koffi.node +0 -0
  16. package/build/koffi/win32_x64/koffi.node +0 -0
  17. package/doc/packaging.md +30 -19
  18. package/package.json +2 -2
  19. package/src/core/libcc/libcc.cc +125 -9
  20. package/src/core/libcc/libcc.hh +42 -16
  21. package/src/index.js +3 -3
  22. package/src/koffi/CMakeLists.txt +6 -2
  23. package/src/koffi/examples/electron-builder/README.md +0 -12
  24. package/src/koffi/examples/electron-builder/package.json +2 -2
  25. package/src/koffi/examples/electron-forge/package.json +2 -2
  26. package/src/koffi/examples/electron-forge/src/main.js +1 -1
  27. package/src/koffi/examples/node-esbuild/README.md +19 -0
  28. package/src/koffi/examples/node-esbuild/index.js +2 -0
  29. package/src/koffi/examples/node-esbuild/package.json +16 -0
  30. package/src/koffi/examples/nwjs/src/package.json +3 -2
  31. package/src/koffi/src/call.cc +16 -6
  32. package/src/koffi/src/ffi.cc +2 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@
4
4
 
5
5
  ### Koffi 2.5
6
6
 
7
+ #### Koffi 2.5.11
8
+
9
+ - Support casting function pointers with [koffi.as()](parameters.md#input-polymorphism)
10
+ - Build in C++20 mode
11
+
12
+ #### Koffi 2.5.10
13
+
14
+ - Fix CMake regression when client has to build Koffi
15
+
7
16
  #### Koffi 2.5.9
8
17
 
9
18
  **Main changes:**
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/packaging.md CHANGED
@@ -6,42 +6,43 @@
6
6
 
7
7
  Koffi uses native modules to work. The NPM package contains binaries for various platforms and architectures, and the appropriate module is selected at runtime.
8
8
 
9
- In theory, the bundlers should be able to find recorgnize all native modules because they are explictly listed in the Javascript file, as static strings.
9
+ In theory, the **packagers/bundlers should be able to find all native modules** because they are explictly listed in the Javascript file (as static strings) and package them somehow.
10
10
 
11
11
  If that is not the case, you can manually arrange to copy the `node_modules/koffi/build/koffi` directory next to your bundled script.
12
12
 
13
13
  Here is an example that would work:
14
14
 
15
15
  ```
16
- koffi/
17
- win32_x64/
18
- koffi.node
19
- linux_x64/
20
- koffi.node
21
- ...
22
- MyBundle.js
16
+ koffi/
17
+ win32_x64/
18
+ koffi.node
19
+ linux_x64/
20
+ koffi.node
21
+ ...
22
+ MyBundle.js
23
23
  ```
24
24
 
25
25
  When running in Electron, Koffi will also try to find the native module in `process.resourcesPath`. For an Electron app you could do something like this
26
26
 
27
27
  ```
28
- locales/
29
- resources/
30
- koffi/
31
- darwin_arm64/
32
- koffi.node
33
- darwin_x64/
34
- koffi.node
35
- MyApp.exe
28
+ locales/
29
+ resources/
30
+ koffi/
31
+ win32_ia32/
32
+ koffi.node
33
+ win32_x64/
34
+ koffi.node
35
+ ...
36
+ MyApp.exe
36
37
  ```
37
38
 
38
39
  ## Packaging examples
39
40
 
40
41
  ### Electron with electron-builder
41
42
 
42
- Packaging with electron-builder should work as-is:
43
+ Packaging with electron-builder should work as-is.
43
44
 
44
- You can also go take a look at the full [working example in the repository](https://github.com/Koromix/rygel/tree/master/src/koffi/examples/electron-builder).
45
+ Take a look at the full [working example in the repository](https://github.com/Koromix/rygel/tree/master/src/koffi/examples/electron-builder).
45
46
 
46
47
  ### Electron Forge
47
48
 
@@ -51,10 +52,20 @@ Packaging with Electron Force should work as-is, even when using webpack as conf
51
52
  npm init electron-app@latest my-app -- --template=webpack
52
53
  ```
53
54
 
54
- You can also go take a look at the full [working example in the repository](https://github.com/Koromix/rygel/tree/master/src/koffi/examples/electron-forge).
55
+ Take a look at the full [working example in the repository](https://github.com/Koromix/rygel/tree/master/src/koffi/examples/electron-forge).
55
56
 
56
57
  ### NW.js
57
58
 
58
59
  Packagers such as nw-builder should work as-is.
59
60
 
60
61
  You can find a full [working example in the repository](https://github.com/Koromix/rygel/tree/master/src/koffi/examples/nwjs).
62
+
63
+ ### Node.js and esbuild
64
+
65
+ You can easily tell esbuild to copy the native files with the copy loader and things should just work. Use something like:
66
+
67
+ ```sh
68
+ esbuild index.js --platform=node --bundle --loader:.node=copy --outdir=dist/
69
+ ```
70
+
71
+ You can find a full [working example in the repository](https://github.com/Koromix/rygel/tree/master/src/koffi/examples/node-esbuild).
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.5.9",
4
- "stable": "2.5.9",
3
+ "version": "2.5.11",
4
+ "stable": "2.5.11",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
7
7
  "foreign",
@@ -801,6 +801,108 @@ bool ParseBool(Span<const char> str, bool *out_value, unsigned int flags,
801
801
  return false;
802
802
  }
803
803
 
804
+ bool ParseSize(Span<const char> str, int64_t *out_size, unsigned int flags, Span<const char> *out_remaining)
805
+ {
806
+ uint64_t size = 0;
807
+
808
+ if (!ParseInt(str, &size, flags & ~(int)ParseFlag::End, &str))
809
+ return false;
810
+ if (size > INT64_MAX) [[unlikely]]
811
+ goto overflow;
812
+
813
+ if (str.len) {
814
+ uint64_t multiplier = 1;
815
+ int next = 1;
816
+
817
+ switch (str[0]) {
818
+ case 'B': { multiplier = 1; } break;
819
+ case 'k': { multiplier = 1000; } break;
820
+ case 'M': { multiplier = 1000000; } break;
821
+ case 'G': { multiplier = 1000000000; } break;
822
+ case 'T': { multiplier = 1000000000000; } break;
823
+ default: { next = 0; } break;
824
+ }
825
+
826
+ if ((flags & (int)ParseFlag::End) && str.len > next) [[unlikely]] {
827
+ if (flags & (int)ParseFlag::Log) {
828
+ LogError("Unknown size unit '%1'", str[0]);
829
+ }
830
+ return false;
831
+ }
832
+ str = str.Take(next, str.len - next);
833
+
834
+ uint64_t total = size * multiplier;
835
+ if ((size && total / size != multiplier) || total > INT64_MAX) [[unlikely]]
836
+ goto overflow;
837
+ size = total;
838
+ }
839
+
840
+ *out_size = (int64_t)size;
841
+ if (out_remaining) {
842
+ *out_remaining = str;
843
+ }
844
+ return true;
845
+
846
+ overflow:
847
+ if (flags & (int)ParseFlag::Log) {
848
+ LogError("Size value is too high");
849
+ }
850
+ return false;
851
+ }
852
+
853
+ bool ParseDuration(Span<const char> str, int64_t *out_duration, unsigned int flags, Span<const char> *out_remaining)
854
+ {
855
+ uint64_t duration = 0;
856
+
857
+ if (!ParseInt(str, &duration, flags & ~(int)ParseFlag::End, &str))
858
+ return false;
859
+ if (duration > INT64_MAX) [[unlikely]]
860
+ goto overflow;
861
+
862
+ if (str.len) {
863
+ uint64_t multiplier = 1;
864
+ int next = 1;
865
+
866
+ switch (str[0]) {
867
+ case 's': { multiplier = 1000; } break;
868
+ case 'm': { multiplier = 60000; } break;
869
+ case 'h': { multiplier = 3600000; } break;
870
+ case 'd': { multiplier = 86400000; } break;
871
+ default: { next = 0; } break;
872
+ }
873
+
874
+ if ((flags & (int)ParseFlag::End) && str.len > next) [[unlikely]] {
875
+ if (flags & (int)ParseFlag::Log) {
876
+ LogError("Unknown duration unit '%1'", str[0]);
877
+ }
878
+ return false;
879
+ }
880
+ str = str.Take(next, str.len - next);
881
+
882
+ uint64_t total = duration * multiplier;
883
+ if ((duration && total / duration != multiplier) || total > INT64_MAX) [[unlikely]]
884
+ goto overflow;
885
+ duration = total;
886
+ } else {
887
+ uint64_t total = duration * 1000;
888
+ if ((duration && total / duration != 1000) || total > INT64_MAX) [[unlikely]]
889
+ goto overflow;
890
+ duration = total;
891
+ }
892
+
893
+ *out_duration = (int64_t)duration;
894
+ if (out_remaining) {
895
+ *out_remaining = str;
896
+ }
897
+ return true;
898
+
899
+ overflow:
900
+ if (flags & (int)ParseFlag::Log) {
901
+ LogError("Duration value is too high");
902
+ }
903
+ return false;
904
+ }
905
+
804
906
  // ------------------------------------------------------------------------
805
907
  // Format
806
908
  // ------------------------------------------------------------------------
@@ -4832,7 +4934,8 @@ const char *GetTemporaryDirectory()
4832
4934
 
4833
4935
  #endif
4834
4936
 
4835
- const char *FindConfigFile(const char *name, Allocator *alloc, LocalArray<const char *, 4> *out_possibilities)
4937
+ const char *FindConfigFile(Span<const char *const> names, Allocator *alloc,
4938
+ LocalArray<const char *, 4> *out_possibilities)
4836
4939
  {
4837
4940
  decltype(GetUserConfigPath) *funcs[] = {
4838
4941
  [](const char *name, Allocator *alloc) {
@@ -4850,16 +4953,19 @@ const char *FindConfigFile(const char *name, Allocator *alloc, LocalArray<const
4850
4953
  const char *filename = nullptr;
4851
4954
 
4852
4955
  for (const auto &func: funcs) {
4853
- const char *path = func(name, alloc);
4956
+ for (const char *name: names) {
4957
+ const char *path = func(name, alloc);
4854
4958
 
4855
- if (!path)
4856
- continue;
4959
+ if (!path)
4960
+ continue;
4857
4961
 
4858
- if (TestFile(path, FileType::File)) {
4859
- filename = path;
4860
- }
4861
- if (out_possibilities) {
4862
- out_possibilities->Append(path);
4962
+ if (TestFile(path, FileType::File)) {
4963
+ filename = path;
4964
+ }
4965
+ if (out_possibilities) {
4966
+ out_possibilities->Append(path);
4967
+ break;
4968
+ }
4863
4969
  }
4864
4970
  }
4865
4971
 
@@ -6756,6 +6862,16 @@ bool SpliceStream(StreamReader *reader, int64_t max_len, StreamWriter *writer)
6756
6862
  return true;
6757
6863
  }
6758
6864
 
6865
+ bool IsCompressorAvailable(CompressionType compression_type)
6866
+ {
6867
+ return CompressorFunctions[(int)compression_type];
6868
+ }
6869
+
6870
+ bool IsDecompressorAvailable(CompressionType compression_type)
6871
+ {
6872
+ return DecompressorFunctions[(int)compression_type];
6873
+ }
6874
+
6759
6875
  // ------------------------------------------------------------------------
6760
6876
  // INI
6761
6877
  // ------------------------------------------------------------------------
@@ -3315,41 +3315,38 @@ static inline int CmpStr(const char *str1, Span<const char> str2)
3315
3315
  static inline int CmpStr(const char *str1, const char *str2)
3316
3316
  { return strcmp(str1, str2); }
3317
3317
 
3318
- static inline Size StartsWith(Span<const char> str, Span<const char> prefix)
3318
+ static inline bool StartsWith(Span<const char> str, Span<const char> prefix)
3319
3319
  {
3320
3320
  Size i = 0;
3321
3321
  while (i < str.len && i < prefix.len) {
3322
3322
  if (str[i] != prefix[i])
3323
- return 0;
3324
-
3323
+ return false;
3325
3324
  i++;
3326
3325
  }
3327
3326
 
3328
- return (i == prefix.len) ? i : 0;
3327
+ return (i == prefix.len);
3329
3328
  }
3330
- static inline Size StartsWith(Span<const char> str, const char *prefix)
3329
+ static inline bool StartsWith(Span<const char> str, const char *prefix)
3331
3330
  {
3332
3331
  Size i = 0;
3333
3332
  while (i < str.len && prefix[i]) {
3334
3333
  if (str[i] != prefix[i])
3335
- return 0;
3336
-
3334
+ return false;
3337
3335
  i++;
3338
3336
  }
3339
3337
 
3340
- return !prefix[i] ? i : 0;
3338
+ return !prefix[i];
3341
3339
  }
3342
- static inline Size StartsWith(const char *str, const char *prefix)
3340
+ static inline bool StartsWith(const char *str, const char *prefix)
3343
3341
  {
3344
3342
  Size i = 0;
3345
3343
  while (str[i] && prefix[i]) {
3346
3344
  if (str[i] != prefix[i])
3347
- return 0;
3348
-
3345
+ return false;
3349
3346
  i++;
3350
3347
  }
3351
3348
 
3352
- return !prefix[i] ? i : 0;
3349
+ return !prefix[i];
3353
3350
  }
3354
3351
 
3355
3352
  static inline bool EndsWith(Span<const char> str, const char *suffix)
@@ -3703,6 +3700,31 @@ overflow:
3703
3700
  bool ParseBool(Span<const char> str, bool *out_value, unsigned int flags = RG_DEFAULT_PARSE_FLAGS,
3704
3701
  Span<const char> *out_remaining = nullptr);
3705
3702
 
3703
+ bool ParseSize(Span<const char> str, int64_t *out_size, unsigned int flags = RG_DEFAULT_PARSE_FLAGS,
3704
+ Span<const char> *out_remaining = nullptr);
3705
+ #if RG_SIZE_MAX < INT64_MAX
3706
+ static inline bool ParseSize(Span<const char> str, Size *out_size,
3707
+ unsigned int flags = RG_DEFAULT_PARSE_FLAGS, Span<const char> *out_remaining = nullptr)
3708
+ {
3709
+ int64_t size = 0;
3710
+ if (!ParseSize(str, &size, flags, out_remaining))
3711
+ return false;
3712
+
3713
+ if (size > RG_SIZE_MAX) [[unlikely]] {
3714
+ if (flags & (int)ParseFlag::Log) {
3715
+ LogError("Size value is too high");
3716
+ }
3717
+ return false;
3718
+ }
3719
+
3720
+ *out_size = (Size)size;
3721
+ return true;
3722
+ }
3723
+ #endif
3724
+
3725
+ bool ParseDuration(Span<const char> str, int64_t *out_duration, unsigned int flags = RG_DEFAULT_PARSE_FLAGS,
3726
+ Span<const char> *out_remaining = nullptr);
3727
+
3706
3728
  static inline Size EncodeUtf8(int32_t c, char out_buf[4])
3707
3729
  {
3708
3730
  if (c < 0x80) {
@@ -4096,11 +4118,11 @@ bool NotifySystemd();
4096
4118
  })()
4097
4119
  #endif
4098
4120
 
4121
+ void InitRG();
4122
+ int Main(int argc, char **argv);
4123
+
4099
4124
  static inline int RunApp(int argc, char **argv)
4100
4125
  {
4101
- void InitRG();
4102
- int Main(int argc, char **argv);
4103
-
4104
4126
  InitRG();
4105
4127
  return Main(argc, argv);
4106
4128
  }
@@ -4114,7 +4136,8 @@ const char *GetUserCachePath(const char *name, Allocator *alloc); // Can return
4114
4136
  const char *GetSystemConfigPath(const char *name, Allocator *alloc);
4115
4137
  const char *GetTemporaryDirectory();
4116
4138
 
4117
- const char *FindConfigFile(const char *name, Allocator *alloc, LocalArray<const char *, 4> *out_possibilities = nullptr);
4139
+ const char *FindConfigFile(Span<const char *const> names, Allocator *alloc,
4140
+ LocalArray<const char *, 4> *out_possibilities = nullptr);
4118
4141
 
4119
4142
  const char *CreateUniqueFile(Span<const char> directory, const char *prefix, const char *extension,
4120
4143
  Allocator *alloc, FILE **out_fp = nullptr);
@@ -4657,6 +4680,9 @@ public:
4657
4680
 
4658
4681
  bool SpliceStream(StreamReader *reader, int64_t max_len, StreamWriter *writer);
4659
4682
 
4683
+ bool IsCompressorAvailable(CompressionType compression_type);
4684
+ bool IsDecompressorAvailable(CompressionType compression_type);
4685
+
4660
4686
  // For convenience, don't close them
4661
4687
  extern StreamReader stdin_st;
4662
4688
  extern StreamWriter stdout_st;
package/src/index.js CHANGED
@@ -21,14 +21,14 @@
21
21
 
22
22
  'use strict';
23
23
 
24
- const cnoke = require('./cnoke/src/index.js');
25
24
  const util = require('util');
26
25
  const fs = require('fs');
26
+ const { get_napi_version, determine_arch } = require('./cnoke/src/tools.js');
27
27
  const pkg = require('../package.json');
28
28
 
29
29
  if (process.versions.napi == null || process.versions.napi < pkg.cnoke.napi) {
30
30
  let major = parseInt(process.versions.node, 10);
31
- let required = cnoke.get_napi_version(pkg.cnoke.napi, major);
31
+ let required = get_napi_version(pkg.cnoke.napi, major);
32
32
 
33
33
  if (required != null) {
34
34
  throw new Error(`Project ${pkg.name} requires Node >= ${required} in the Node ${major}.x branch (N-API >= ${pkg.cnoke.napi})`);
@@ -37,7 +37,7 @@ if (process.versions.napi == null || process.versions.napi < pkg.cnoke.napi) {
37
37
  }
38
38
  }
39
39
 
40
- let arch = cnoke.determine_arch();
40
+ let arch = determine_arch();
41
41
  let triplet = `${process.platform}_${arch}`;
42
42
 
43
43
  let native = null;
@@ -28,7 +28,7 @@ include(CheckCXXCompilerFlag)
28
28
 
29
29
  find_package(CNoke)
30
30
 
31
- set(CMAKE_CXX_STANDARD 17)
31
+ set(CMAKE_CXX_STANDARD 20)
32
32
  if(MSVC)
33
33
  set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded)
34
34
 
@@ -47,7 +47,11 @@ endif()
47
47
 
48
48
  # ---- Koffi ----
49
49
 
50
- file(READ ${CMAKE_CURRENT_SOURCE_DIR}/package.json PKG)
50
+ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/package.json)
51
+ file(READ ${CMAKE_CURRENT_SOURCE_DIR}/package.json PKG)
52
+ else()
53
+ file(READ ${CMAKE_CURRENT_SOURCE_DIR}/../../package.json PKG)
54
+ endif()
51
55
  string(REGEX MATCH "\"version\": \"([^\"]+)\"" XX "${PKG}")
52
56
  set(KOFFI_VERSION ${CMAKE_MATCH_1})
53
57
 
@@ -3,18 +3,6 @@ This is a simple example shows:
3
3
  - How to communicate with Koffi from a renderer window
4
4
  - How to use electron-builder with an application that uses Koffi
5
5
 
6
- One of the important piece for the packaging is in `package.json`, specifically this part:
7
-
8
- ```json
9
- "build": {
10
- "extraResources": [
11
- { "from": "node_modules/koffi/build", "to": "koffi" }
12
- ]
13
- }
14
- ```
15
-
16
- This instructs electron-builder to copy the native koffi modules to a place where your application will find them.
17
-
18
6
  Use the following commands to package the app for your system:
19
7
 
20
8
  ```sh
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "Koffi Config",
2
+ "name": "KoffiConfig",
3
3
  "version": "1.0.0",
4
4
  "description": "Koffi Electron Example",
5
5
  "main": "src/app.js",
@@ -16,6 +16,6 @@
16
16
  "esbuild": "^0.18.17"
17
17
  },
18
18
  "dependencies": {
19
- "koffi": "^2.5.8"
19
+ "koffi": "^2.5.9"
20
20
  }
21
21
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "Koffi Config",
2
+ "name": "KoffiConfig",
3
3
  "version": "1.0.0",
4
4
  "description": "Koffi Electron Example",
5
5
  "main": ".webpack/main",
@@ -32,6 +32,6 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "electron-squirrel-startup": "^1.0.0",
35
- "koffi": "^2.5.8"
35
+ "koffi": "^2.5.9"
36
36
  }
37
37
  }
@@ -21,7 +21,7 @@ const createWindow = () => {
21
21
  mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
22
22
 
23
23
  // Open the DevTools.
24
- mainWindow.webContents.openDevTools();
24
+ // mainWindow.webContents.openDevTools();
25
25
  };
26
26
 
27
27
  // This method will be called when Electron has finished
@@ -0,0 +1,19 @@
1
+ This is a simple example to bundle a CLI node.js app that uses Koffi, using esbuild.
2
+
3
+ To run the app, execute the following:
4
+
5
+ ```sh
6
+ cd examples/node-esbuild
7
+ npm install
8
+ npm start
9
+ ```
10
+
11
+ You can bundle the script and the native modules with the following command
12
+
13
+ ```sh
14
+ cd examples/node-esbuild
15
+ npm install
16
+ npm run bundle
17
+ ```
18
+
19
+ Internally, this uses the esbuild copy loader to handle the native `.node` modules.
@@ -0,0 +1,2 @@
1
+ const koffi = require('koffi');
2
+ console.log(koffi.config());
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "KoffiConfig",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "start": "node index.js",
8
+ "bundle": "esbuild index.js --platform=node --bundle --loader:.node=copy --outdir=dist/"
9
+ },
10
+ "author": "",
11
+ "license": "ISC",
12
+ "dependencies": {
13
+ "esbuild": "^0.18.17",
14
+ "koffi": "^2.5.9"
15
+ }
16
+ }
@@ -1,9 +1,10 @@
1
1
  {
2
- "name": "Koffi Config",
2
+ "name": "KoffiConfig",
3
+ "version": "1.0.0",
3
4
  "main": "index.html",
4
5
  "author": "Niels Martignène <niels.martignene@protonmail.com>",
5
6
  "license": "MIT",
6
7
  "dependencies": {
7
- "koffi": "^2.5.8"
8
+ "koffi": "^2.5.9"
8
9
  }
9
10
  }
@@ -1043,12 +1043,6 @@ bool CallData::PushPointer(Napi::Value value, const TypeInfo *type, int directio
1043
1043
  out_kind = OutArgument::Kind::Buffer;
1044
1044
  } else if (type->ref.type->primitive == PrimitiveKind::Record ||
1045
1045
  type->ref.type->primitive == PrimitiveKind::Union) [[likely]] {
1046
- if (!type->ref.type->size) [[unlikely]] {
1047
- ThrowError<Napi::TypeError>(env, "Cannot pass %1 value to %2, use koffi.as()",
1048
- type->ref.type != instance->void_type ? "opaque" : "ambiguous", type->name);
1049
- return false;
1050
- }
1051
-
1052
1046
  Napi::Object obj = value.As<Napi::Object>();
1053
1047
  RG_ASSERT(IsObject(value));
1054
1048
 
@@ -1108,6 +1102,22 @@ bool CallData::PushPointer(Napi::Value value, const TypeInfo *type, int directio
1108
1102
  }
1109
1103
  } break;
1110
1104
 
1105
+ case napi_function: {
1106
+ if (type->primitive != PrimitiveKind::Callback) [[unlikely]] {
1107
+ ThrowError<Napi::TypeError>(env, "Cannot pass function to type %1", type->name);
1108
+ return false;
1109
+ }
1110
+
1111
+ Napi::Function func = value.As<Napi::Function>();
1112
+
1113
+ void *ptr = ReserveTrampoline(type->ref.proto, func);
1114
+ if (!ptr) [[unlikely]]
1115
+ return false;
1116
+
1117
+ *out_ptr = (void *)ptr;
1118
+ return true;
1119
+ } break;
1120
+
1111
1121
  case napi_number: {
1112
1122
  Napi::Number number = value.As<Napi::Number>();
1113
1123
  intptr_t ptr = (intptr_t)number.Int32Value();
@@ -1775,8 +1775,9 @@ static Napi::Value CastValue(const Napi::CallbackInfo &info)
1775
1775
  if (!type) [[unlikely]]
1776
1776
  return env.Null();
1777
1777
  if (type->primitive != PrimitiveKind::Pointer &&
1778
+ type->primitive != PrimitiveKind::Callback &&
1778
1779
  type->primitive != PrimitiveKind::String &&
1779
- type->primitive != PrimitiveKind::String16) {
1780
+ type->primitive != PrimitiveKind::String16) [[unlikely]] {
1780
1781
  ThrowError<Napi::TypeError>(env, "Only pointer or string types can be used for casting");
1781
1782
  return env.Null();
1782
1783
  }