koffi 2.4.2 → 2.5.0-beta.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 (48) hide show
  1. package/build/2.5.0-beta.1/koffi_darwin_arm64/koffi.node +0 -0
  2. package/build/2.5.0-beta.1/koffi_darwin_x64/koffi.node +0 -0
  3. package/build/2.5.0-beta.1/koffi_freebsd_arm64/koffi.node +0 -0
  4. package/build/2.5.0-beta.1/koffi_freebsd_ia32/koffi.node +0 -0
  5. package/build/2.5.0-beta.1/koffi_freebsd_x64/koffi.node +0 -0
  6. package/build/2.5.0-beta.1/koffi_linux_arm32hf/koffi.node +0 -0
  7. package/build/2.5.0-beta.1/koffi_linux_arm64/koffi.node +0 -0
  8. package/build/2.5.0-beta.1/koffi_linux_ia32/koffi.node +0 -0
  9. package/build/2.5.0-beta.1/koffi_linux_riscv64hf64/koffi.node +0 -0
  10. package/build/2.5.0-beta.1/koffi_linux_x64/koffi.node +0 -0
  11. package/build/2.5.0-beta.1/koffi_openbsd_ia32/koffi.node +0 -0
  12. package/build/{2.4.2 → 2.5.0-beta.1}/koffi_openbsd_x64/koffi.node +0 -0
  13. package/build/2.5.0-beta.1/koffi_win32_arm64/koffi.node +0 -0
  14. package/build/{2.4.2 → 2.5.0-beta.1}/koffi_win32_ia32/koffi.node +0 -0
  15. package/build/{2.4.2 → 2.5.0-beta.1}/koffi_win32_x64/koffi.node +0 -0
  16. package/build/2.5.0-beta.1/koffi_win32_x64/koffi.pdb +0 -0
  17. package/doc/callbacks.md +1 -1
  18. package/doc/conf.py +1 -1
  19. package/doc/functions.md +1 -1
  20. package/doc/parameters.md +0 -1
  21. package/doc/pointers.md +1 -1
  22. package/package.json +1 -1
  23. package/src/core/libcc/libcc.cc +58 -54
  24. package/src/core/libcc/libcc.hh +33 -19
  25. package/src/koffi/src/abi_x86.cc +31 -4
  26. package/src/koffi/src/call.cc +37 -22
  27. package/src/koffi/src/ffi.cc +251 -303
  28. package/src/koffi/src/ffi.hh +3 -1
  29. package/src/koffi/src/util.cc +20 -13
  30. package/src/koffi/src/util.hh +4 -2
  31. package/build/2.4.2/koffi_darwin_arm64/koffi.node +0 -0
  32. package/build/2.4.2/koffi_darwin_x64/koffi.node +0 -0
  33. package/build/2.4.2/koffi_freebsd_arm64/koffi.node +0 -0
  34. package/build/2.4.2/koffi_freebsd_ia32/koffi.node +0 -0
  35. package/build/2.4.2/koffi_freebsd_x64/koffi.node +0 -0
  36. package/build/2.4.2/koffi_linux_arm32hf/koffi.node +0 -0
  37. package/build/2.4.2/koffi_linux_arm64/koffi.node +0 -0
  38. package/build/2.4.2/koffi_linux_ia32/koffi.node +0 -0
  39. package/build/2.4.2/koffi_linux_riscv64hf64/koffi.node +0 -0
  40. package/build/2.4.2/koffi_linux_x64/koffi.node +0 -0
  41. package/build/2.4.2/koffi_openbsd_ia32/koffi.node +0 -0
  42. package/build/2.4.2/koffi_win32_arm64/koffi.node +0 -0
  43. /package/build/{2.4.2 → 2.5.0-beta.1}/koffi_win32_arm64/koffi.exp +0 -0
  44. /package/build/{2.4.2 → 2.5.0-beta.1}/koffi_win32_arm64/koffi.lib +0 -0
  45. /package/build/{2.4.2 → 2.5.0-beta.1}/koffi_win32_ia32/koffi.exp +0 -0
  46. /package/build/{2.4.2 → 2.5.0-beta.1}/koffi_win32_ia32/koffi.lib +0 -0
  47. /package/build/{2.4.2 → 2.5.0-beta.1}/koffi_win32_x64/koffi.exp +0 -0
  48. /package/build/{2.4.2 → 2.5.0-beta.1}/koffi_win32_x64/koffi.lib +0 -0
package/doc/callbacks.md CHANGED
@@ -20,7 +20,7 @@ const ExampleCallback = koffi.proto('ExampleCallback', 'void', ['int']);
20
20
  const AddDoubleFloat = koffi.proto('double AddDoubleFloat(double d, float f)');
21
21
  ```
22
22
 
23
- Once your callback type is declared, you can use a pointer to it in struct definitions, or as function parameters and/or return types.
23
+ Once your callback type is declared, you can use a pointer to it in struct definitions, as function parameters and/or return types, or to call/decode function pointers.
24
24
 
25
25
  ```{note}
26
26
  Callbacks **have changed in version 2.0**.
package/doc/conf.py CHANGED
@@ -65,7 +65,7 @@ html_theme_options = {
65
65
  }
66
66
  }
67
67
 
68
- # html_link_suffix = ''
68
+ html_link_suffix = ''
69
69
 
70
70
  html_css_files = ['custom.css']
71
71
 
package/doc/functions.md CHANGED
@@ -137,7 +137,7 @@ You can easily convert this callback-style async function to a promise-based ver
137
137
 
138
138
  Variadic functions cannot be called asynchronously.
139
139
 
140
- ```{note}
140
+ ```{warning}
141
141
  Asynchronous functions run on worker threads. You need to deal with thread safety issues if you share data between threads.
142
142
 
143
143
  Callbacks must be called from the main thread, or more precisely from the same thread as the V8 intepreter. Calling a callback from another thread is undefined behavior, and will likely lead to a crash or a big mess. You've been warned!
package/doc/parameters.md CHANGED
@@ -212,7 +212,6 @@ if (filename == null)
212
212
 
213
213
  let hdr = {};
214
214
  {
215
-
216
215
  let fp = fopen(filename, 'rb');
217
216
  if (!fp)
218
217
  throw new Error(`Failed to open '${filename}'`);
package/doc/pointers.md CHANGED
@@ -132,7 +132,7 @@ By default, just like for objects, array arguments are copied from JS to C but n
132
132
 
133
133
  Disposable types allow you to register a function that will automatically called after each C to JS conversion performed by Koffi. This can be used to avoid leaking heap-allocated strings, for example.
134
134
 
135
- Read the documentation for [disposable types](parameters.md#heap-allocated-values) on the page about function calls.
135
+ Read the documentation for [disposable types](parameters.md#heap-allocated-values) on the page about special parameters.
136
136
 
137
137
  ## Unwrap pointers
138
138
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.4.2",
3
+ "version": "2.5.0-beta.1",
4
4
  "stable": "2.4.2",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
@@ -2856,15 +2856,6 @@ const char *GetWorkingDirectory()
2856
2856
  return buf;
2857
2857
  }
2858
2858
 
2859
- #ifdef __OpenBSD__
2860
- RG_INIT(ExePathOpenBSD)
2861
- {
2862
- // This can depend on PATH, which could change during execution
2863
- // so we want to cache the result as soon as possible.
2864
- GetApplicationExecutable();
2865
- }
2866
- #endif
2867
-
2868
2859
  const char *GetApplicationExecutable()
2869
2860
  {
2870
2861
  #if defined(_WIN32)
@@ -4068,26 +4059,6 @@ static void DefaultSignalHandler(int signal)
4068
4059
  }
4069
4060
  }
4070
4061
 
4071
- RG_INIT(SetupDefaultHandlers)
4072
- {
4073
- // Best effort
4074
- setpgid(0, 0);
4075
-
4076
- SetSignalHandler(SIGINT, DefaultSignalHandler);
4077
- SetSignalHandler(SIGTERM, DefaultSignalHandler);
4078
- SetSignalHandler(SIGHUP, DefaultSignalHandler);
4079
- SetSignalHandler(SIGPIPE, [](int) {});
4080
- }
4081
-
4082
- RG_EXIT(TerminateChildren)
4083
- {
4084
- pid_t pid = getpid();
4085
- RG_ASSERT(pid > 1);
4086
-
4087
- SetSignalHandler(SIGTERM, [](int) {});
4088
- kill(-pid, SIGTERM);
4089
- }
4090
-
4091
4062
  bool CreatePipe(int pfd[2])
4092
4063
  {
4093
4064
  #ifdef __APPLE__
@@ -4625,6 +4596,42 @@ bool NotifySystemd()
4625
4596
  }
4626
4597
  #endif
4627
4598
 
4599
+ void InitRG()
4600
+ {
4601
+ #ifdef _WIN32
4602
+ // Use binary standard I/O
4603
+ _setmode(_fileno(stdin), _O_BINARY);
4604
+ _setmode(_fileno(stdout), _O_BINARY);
4605
+ _setmode(_fileno(stderr), _O_BINARY);
4606
+ #else
4607
+ // Best effort
4608
+ setpgid(0, 0);
4609
+
4610
+ // Setup default signal handlers
4611
+ SetSignalHandler(SIGINT, DefaultSignalHandler);
4612
+ SetSignalHandler(SIGTERM, DefaultSignalHandler);
4613
+ SetSignalHandler(SIGHUP, DefaultSignalHandler);
4614
+ SetSignalHandler(SIGPIPE, [](int) {});
4615
+
4616
+ // Kill children on exit
4617
+ atexit([]() {
4618
+ if (interrupt_pfd[1] >= 0) {
4619
+ pid_t pid = getpid();
4620
+ RG_ASSERT(pid > 1);
4621
+
4622
+ SetSignalHandler(SIGTERM, [](int) {});
4623
+ kill(-pid, SIGTERM);
4624
+ }
4625
+ });
4626
+ #endif
4627
+
4628
+ #ifdef __OpenBSD__
4629
+ // This can depend on PATH, which could change during execution
4630
+ // so we want to cache the result as soon as possible.
4631
+ GetApplicationExecutable();
4632
+ #endif
4633
+ }
4634
+
4628
4635
  // ------------------------------------------------------------------------
4629
4636
  // Standard paths
4630
4637
  // ------------------------------------------------------------------------
@@ -4709,16 +4716,16 @@ const char *GetUserConfigPath(const char *name, Allocator *alloc)
4709
4716
  RG_ASSERT(!strchr(RG_PATH_SEPARATORS, name[0]));
4710
4717
 
4711
4718
  const char *xdg = getenv("XDG_CONFIG_HOME");
4719
+ const char *home = getenv("HOME");
4712
4720
 
4713
4721
  if (xdg) {
4714
4722
  const char *path = Fmt(alloc, "%1%/%2", xdg, name).ptr;
4715
4723
  return path;
4716
- } else {
4717
- const char *home = getenv("HOME");
4718
- RG_CRITICAL(home, "Failed to get HOME environment variable: %1", strerror(errno));
4719
-
4724
+ } else if (home) {
4720
4725
  const char *path = Fmt(alloc, "%1%/.config/%2", home, name).ptr;
4721
4726
  return path;
4727
+ } else {
4728
+ return nullptr;
4722
4729
  }
4723
4730
  }
4724
4731
 
@@ -4727,20 +4734,20 @@ const char *GetUserCachePath(const char *name, Allocator *alloc)
4727
4734
  RG_ASSERT(!strchr(RG_PATH_SEPARATORS, name[0]));
4728
4735
 
4729
4736
  const char *xdg = getenv("XDG_CACHE_HOME");
4737
+ const char *home = getenv("HOME");
4730
4738
 
4731
4739
  if (xdg) {
4732
4740
  const char *path = Fmt(alloc, "%1%/%2", xdg, name).ptr;
4733
4741
  return path;
4734
- } else {
4735
- const char *home = getenv("HOME");
4736
- RG_CRITICAL(home, "Failed to get HOME environment variable: %1", strerror(errno));
4737
-
4742
+ } else if (home) {
4738
4743
  const char *path = Fmt(alloc, "%1%/.cache/%2", home, name).ptr;
4739
4744
  return path;
4745
+ } else {
4746
+ return nullptr;
4740
4747
  }
4741
4748
  }
4742
4749
 
4743
- static const char *GetSystemConfigPath(const char *name, Allocator *alloc)
4750
+ const char *GetSystemConfigPath(const char *name, Allocator *alloc)
4744
4751
  {
4745
4752
  RG_ASSERT(!strchr(RG_PATH_SEPARATORS, name[0]));
4746
4753
 
@@ -4792,6 +4799,9 @@ const char *FindConfigFile(const char *name, Allocator *alloc, LocalArray<const
4792
4799
  for (const auto &func: funcs) {
4793
4800
  const char *path = func(name, alloc);
4794
4801
 
4802
+ if (!path)
4803
+ continue;
4804
+
4795
4805
  if (TestFile(path, FileType::File)) {
4796
4806
  filename = path;
4797
4807
  }
@@ -5129,8 +5139,9 @@ int OpenIPSocket(SocketType type, int port, SocketMode mode)
5129
5139
 
5130
5140
  int flags = 0;
5131
5141
  switch (mode) {
5132
- case SocketMode::Stream: { flags = SOCK_STREAM; } break;
5133
- case SocketMode::Messages: { flags = SOCK_DGRAM; } break;
5142
+ case SocketMode::ConnectStream: { flags = SOCK_STREAM; } break;
5143
+ case SocketMode::ConnectDatagrams: { RG_UNREACHABLE(); } break;
5144
+ case SocketMode::FreeDatagrams: { flags = SOCK_DGRAM; } break;
5134
5145
  }
5135
5146
 
5136
5147
  #ifdef _WIN32
@@ -5199,8 +5210,9 @@ int OpenUnixSocket(const char *path, SocketMode mode)
5199
5210
  {
5200
5211
  int flags = 0;
5201
5212
  switch (mode) {
5202
- case SocketMode::Stream: { flags = SOCK_STREAM; } break;
5203
- case SocketMode::Messages: { flags = SOCK_SEQPACKET; } break;
5213
+ case SocketMode::ConnectStream: { flags = SOCK_STREAM; } break;
5214
+ case SocketMode::ConnectDatagrams: { flags = SOCK_SEQPACKET; } break;
5215
+ case SocketMode::FreeDatagrams: { flags = SOCK_DGRAM; } break;
5204
5216
  }
5205
5217
 
5206
5218
  #if defined(_WIN32)
@@ -5252,8 +5264,9 @@ int ConnectToUnixSocket(const char *path, SocketMode mode)
5252
5264
  {
5253
5265
  int flags = 0;
5254
5266
  switch (mode) {
5255
- case SocketMode::Stream: { flags = SOCK_STREAM; } break;
5256
- case SocketMode::Messages: { flags = SOCK_SEQPACKET; } break;
5267
+ case SocketMode::ConnectStream: { flags = SOCK_STREAM; } break;
5268
+ case SocketMode::ConnectDatagrams: { flags = SOCK_SEQPACKET; } break;
5269
+ case SocketMode::FreeDatagrams: { flags = SOCK_DGRAM; } break;
5257
5270
  }
5258
5271
 
5259
5272
  #if defined(_WIN32)
@@ -5867,15 +5880,6 @@ void Fiber::Toggle(int to, std::unique_lock<std::mutex> *lock)
5867
5880
  // Streams
5868
5881
  // ------------------------------------------------------------------------
5869
5882
 
5870
- #ifdef _WIN32
5871
- RG_INIT(BinaryStdIO)
5872
- {
5873
- _setmode(_fileno(stdin), _O_BINARY);
5874
- _setmode(_fileno(stdout), _O_BINARY);
5875
- _setmode(_fileno(stderr), _O_BINARY);
5876
- }
5877
- #endif
5878
-
5879
5883
  StreamReader stdin_st(stdin, "<stdin>");
5880
5884
  StreamWriter stdout_st(stdout, "<stdout>");
5881
5885
  StreamWriter stderr_st(stderr, "<stderr>");
@@ -6416,7 +6420,7 @@ bool StreamWriter::Open(const char *filename, unsigned int flags,
6416
6420
  dest.u.file.tmp_exclusive = true;
6417
6421
  }
6418
6422
 
6419
- dest.u.file.tmp_filename = CreateUniqueFile(directory, "", ".tmp", &str_alloc, &dest.u.file.fp);
6423
+ dest.u.file.tmp_filename = CreateUniqueFile(directory, ".", ".tmp", &str_alloc, &dest.u.file.fp);
6420
6424
  if (!dest.u.file.tmp_filename)
6421
6425
  return false;
6422
6426
  dest.u.file.owned = true;
@@ -2599,11 +2599,11 @@ public:
2599
2599
 
2600
2600
  ValueType *Set(const KeyType &key, const ValueType &value)
2601
2601
  { return &table.Set({ key, value })->value; }
2602
- ValueType *SetDefault(const KeyType &key)
2602
+ Bucket *SetDefault(const KeyType &key)
2603
2603
  {
2604
2604
  Bucket *table_it = table.SetDefault(key);
2605
2605
  table_it->key = key;
2606
- return &table_it->value;
2606
+ return table_it;
2607
2607
  }
2608
2608
 
2609
2609
  ValueType *TrySet(const KeyType &key, const ValueType &value, bool *out_inserted = nullptr)
@@ -2611,7 +2611,7 @@ public:
2611
2611
  Bucket *ptr = table.TrySet({ key, value }, out_inserted);
2612
2612
  return &ptr->value;
2613
2613
  }
2614
- ValueType *TrySetDefault(const KeyType &key, bool *out_inserted = nullptr)
2614
+ Bucket *TrySetDefault(const KeyType &key, bool *out_inserted = nullptr)
2615
2615
  {
2616
2616
  bool inserted;
2617
2617
  Bucket *ptr = table.TrySetDefault(key, &inserted);
@@ -2623,7 +2623,7 @@ public:
2623
2623
  if (out_inserted) {
2624
2624
  *out_inserted = inserted;
2625
2625
  }
2626
- return &ptr->value;
2626
+ return ptr;
2627
2627
  }
2628
2628
 
2629
2629
  void Remove(ValueType *it)
@@ -2632,6 +2632,12 @@ public:
2632
2632
  return;
2633
2633
  table.Remove((Bucket *)((uint8_t *)it - RG_OFFSET_OF(Bucket, value)));
2634
2634
  }
2635
+ void Remove(Bucket *it)
2636
+ {
2637
+ if (!it)
2638
+ return;
2639
+ table.Remove(it);
2640
+ }
2635
2641
  template <typename T = KeyType>
2636
2642
  void Remove(const KeyType &key) { Remove(Find(key)); }
2637
2643
 
@@ -3983,9 +3989,9 @@ bool ExecuteCommandLine(const char *cmd_line, FunctionRef<Span<const uint8_t>()>
3983
3989
  FunctionRef<void(Span<uint8_t> buf)> out_func, int *out_code);
3984
3990
  bool ExecuteCommandLine(const char *cmd_line, Span<const uint8_t> in_buf, Size max_len,
3985
3991
  HeapArray<uint8_t> *out_buf, int *out_code);
3986
- static inline bool ExecuteCommandLine(const char *cmd_line, int *out_code) {
3987
- return ExecuteCommandLine(cmd_line, {}, {}, out_code);
3988
- }
3992
+
3993
+ static inline bool ExecuteCommandLine(const char *cmd_line, int *out_code)
3994
+ { return ExecuteCommandLine(cmd_line, {}, {}, out_code); }
3989
3995
  static inline bool ExecuteCommandLine(const char *cmd_line, Span<const uint8_t> in_buf,
3990
3996
  FunctionRef<void(Span<uint8_t> buf)> out_func, int *out_code)
3991
3997
  {
@@ -4001,15 +4007,12 @@ static inline bool ExecuteCommandLine(const char *cmd_line, Span<const char> in_
4001
4007
  FunctionRef<void(Span<char> buf)> out_func, int *out_code)
4002
4008
  {
4003
4009
  const auto write = [&](Span<uint8_t> buf) { out_func(buf.As<char>()); };
4004
-
4005
4010
  return ExecuteCommandLine(cmd_line, in_buf.As<const uint8_t>(), write, out_code);
4006
4011
  }
4007
4012
  static inline bool ExecuteCommandLine(const char *cmd_line, Span<const char> in_buf, Size max_len,
4008
4013
  HeapArray<char> *out_buf, int *out_code)
4009
- {
4010
- return ExecuteCommandLine(cmd_line, in_buf.As<const uint8_t>(), max_len,
4011
- (HeapArray<uint8_t> *)out_buf, out_code);
4012
- }
4014
+ { return ExecuteCommandLine(cmd_line, in_buf.As<const uint8_t>(), max_len,
4015
+ (HeapArray<uint8_t> *)out_buf, out_code); }
4013
4016
 
4014
4017
  void WaitDelay(int64_t delay);
4015
4018
 
@@ -4043,12 +4046,22 @@ bool NotifySystemd();
4043
4046
  })()
4044
4047
  #endif
4045
4048
 
4049
+ static inline int RunApp(int argc, char **argv)
4050
+ {
4051
+ void InitRG();
4052
+ int Main(int argc, char **argv);
4053
+
4054
+ InitRG();
4055
+ return Main(argc, argv);
4056
+ }
4057
+
4046
4058
  // ------------------------------------------------------------------------
4047
4059
  // Standard paths
4048
4060
  // ------------------------------------------------------------------------
4049
4061
 
4050
- const char *GetUserConfigPath(const char *name, Allocator *alloc);
4051
- const char *GetUserCachePath(const char *name, Allocator *alloc);
4062
+ const char *GetUserConfigPath(const char *name, Allocator *alloc); // Can return NULL
4063
+ const char *GetUserCachePath(const char *name, Allocator *alloc); // Can return NULL
4064
+ const char *GetSystemConfigPath(const char *name, Allocator *alloc);
4052
4065
  const char *GetTemporaryDirectory();
4053
4066
 
4054
4067
  const char *FindConfigFile(const char *name, Allocator *alloc, LocalArray<const char *, 4> *out_possibilities = nullptr);
@@ -4116,14 +4129,15 @@ static const char *const SocketTypeNames[] = {
4116
4129
  };
4117
4130
 
4118
4131
  enum class SocketMode {
4119
- Stream,
4120
- Messages
4132
+ ConnectStream,
4133
+ ConnectDatagrams,
4134
+ FreeDatagrams
4121
4135
  };
4122
4136
 
4123
- int OpenIPSocket(SocketType type, int port, SocketMode mode = SocketMode::Stream);
4137
+ int OpenIPSocket(SocketType type, int port, SocketMode mode = SocketMode::ConnectStream);
4124
4138
 
4125
- int OpenUnixSocket(const char *path, SocketMode mode = SocketMode::Stream);
4126
- int ConnectToUnixSocket(const char *path, SocketMode mode = SocketMode::Stream);
4139
+ int OpenUnixSocket(const char *path, SocketMode mode = SocketMode::ConnectStream);
4140
+ int ConnectToUnixSocket(const char *path, SocketMode mode = SocketMode::ConnectStream);
4127
4141
 
4128
4142
  void CloseSocket(int fd);
4129
4143
 
@@ -75,11 +75,23 @@ bool AnalyseFunction(Napi::Env env, InstanceData *instance, FunctionInfo *func)
75
75
  #if defined(_WIN32) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
76
76
  } else {
77
77
  func->ret.trivial = IsRegularSize(func->ret.type->size, 8);
78
+
79
+ #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
80
+ if (func->ret.type->members.len == 1) {
81
+ const RecordMember &member = func->ret.type->members[0];
82
+
83
+ if (member.type->primitive == PrimitiveKind::Float32) {
84
+ func->ret.fast = 1;
85
+ } else if (member.type->primitive == PrimitiveKind::Float64) {
86
+ func->ret.fast = 2;
87
+ }
88
+ }
89
+ #endif
78
90
  #endif
79
91
  }
80
92
  #ifndef _WIN32
81
93
  if (fast && !func->ret.trivial) {
82
- func->ret.fast = true;
94
+ func->ret.fast = 1;
83
95
  fast--;
84
96
  }
85
97
  #endif
@@ -87,7 +99,7 @@ bool AnalyseFunction(Napi::Env env, InstanceData *instance, FunctionInfo *func)
87
99
  Size params_size = 0;
88
100
  for (ParameterInfo &param: func->parameters) {
89
101
  if (fast && param.type->size <= 4) {
90
- param.fast = true;
102
+ param.fast = 1;
91
103
  fast--;
92
104
  }
93
105
 
@@ -362,9 +374,24 @@ void CallData::Execute(const FunctionInfo *func, void *native)
362
374
  case PrimitiveKind::String:
363
375
  case PrimitiveKind::String16:
364
376
  case PrimitiveKind::Pointer:
365
- case PrimitiveKind::Record:
366
- case PrimitiveKind::Union:
367
377
  case PrimitiveKind::Callback: { result.u64 = PERFORM_CALL(G); } break;
378
+ #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
379
+ case PrimitiveKind::Record:
380
+ case PrimitiveKind::Union: {
381
+ if (!func->ret.fast) {
382
+ result.u64 = PERFORM_CALL(G);
383
+ } else if (func->ret.fast == 1) {
384
+ result.f = PERFORM_CALL(F);
385
+ } else if (func->ret.fast == 2) {
386
+ result.d = PERFORM_CALL(D);
387
+ } else {
388
+ RG_UNREACHABLE();
389
+ }
390
+ } break;
391
+ #else
392
+ case PrimitiveKind::Record:
393
+ case PrimitiveKind::Union: { result.u64 = PERFORM_CALL(G); } break;
394
+ #endif
368
395
  case PrimitiveKind::Array: { RG_UNREACHABLE(); } break;
369
396
  case PrimitiveKind::Float32: { result.f = PERFORM_CALL(F); } break;
370
397
  case PrimitiveKind::Float64: { result.d = PERFORM_CALL(D); } break;
@@ -360,26 +360,41 @@ bool CallData::PushObject(Napi::Object obj, const TypeInfo *type, uint8_t *origi
360
360
  if (type->primitive == PrimitiveKind::Record) {
361
361
  members = type->members;
362
362
  } else if (type->primitive == PrimitiveKind::Union) {
363
- if (RG_UNLIKELY(!CheckValueTag(instance, obj, &MagicUnionMarker))) {
364
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected union value", GetValueType(instance, obj));
365
- return false;
366
- }
363
+ if (CheckValueTag(instance, obj, &MagicUnionMarker)) {
364
+ MagicUnion *u = MagicUnion::Unwrap(obj);
365
+ const uint8_t *raw = u->GetRaw();
367
366
 
368
- MagicUnion *u = MagicUnion::Unwrap(obj);
369
- const uint8_t *raw = u->GetRaw();
367
+ // Fast path: encoded value already exists, just copy!
368
+ if (raw) {
369
+ memcpy(origin, raw, type->size);
370
+ return true;
371
+ }
370
372
 
371
- // Fast path: encoded value already exists, just copy!
372
- if (raw) {
373
- memcpy(origin, raw, type->size);
374
- return true;
375
- }
373
+ members.ptr = u->GetMember();
374
+ members.len = 1;
376
375
 
377
- members.ptr = u->GetMember();
378
- members.len = 1;
376
+ if (RG_UNLIKELY(!members.ptr)) {
377
+ ThrowError<Napi::Error>(env, "Cannot use ambiguous empty union");
378
+ return false;
379
+ }
380
+ } else {
381
+ Napi::Array properties = obj.GetPropertyNames();
379
382
 
380
- if (RG_UNLIKELY(!members.ptr)) {
381
- ThrowError<Napi::Error>(env, "Cannot use ambiguous empty union");
382
- return false;
383
+ if (RG_UNLIKELY(properties.Length() != 1 || !((Napi::Value)properties[0u]).IsString())) {
384
+ ThrowError<Napi::Error>(env, "Expected object with single property name for union");
385
+ return false;
386
+ }
387
+
388
+ std::string property = ((Napi::Value)properties[0u]).As<Napi::String>().Utf8Value();
389
+
390
+ members.ptr = std::find_if(type->members.begin(), type->members.end(),
391
+ [&](const RecordMember &member) { return TestStr(property.c_str(), member.name); });
392
+ members.len = 1;
393
+
394
+ if (RG_UNLIKELY(members.ptr == type->members.end())) {
395
+ ThrowError<Napi::Error>(env, "Unknown member %1 in union type %2", property.c_str(), type->name);
396
+ return false;
397
+ }
383
398
  }
384
399
  } else {
385
400
  RG_UNREACHABLE();
@@ -1041,16 +1056,16 @@ bool CallData::PushPointer(Napi::Value value, const TypeInfo *type, int directio
1041
1056
 
1042
1057
  ptr = AllocHeap(type->ref.type->size, 16);
1043
1058
 
1059
+ if (RG_UNLIKELY(type->ref.type->primitive == PrimitiveKind::Union &&
1060
+ (directions & 2) && !CheckValueTag(instance, obj, &MagicUnionMarker))) {
1061
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected union value", GetValueType(instance, obj));
1062
+ return false;
1063
+ }
1064
+
1044
1065
  if (directions & 1) {
1045
1066
  if (!PushObject(obj, type->ref.type, ptr))
1046
1067
  return false;
1047
1068
  } else {
1048
- if (RG_UNLIKELY(type->ref.type->primitive == PrimitiveKind::Union &&
1049
- !CheckValueTag(instance, obj, &MagicUnionMarker))) {
1050
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected union value", GetValueType(instance, obj));
1051
- return false;
1052
- }
1053
-
1054
1069
  memset_safe(ptr, 0, type->size);
1055
1070
  }
1056
1071