@auto_js/napi 0.0.7 → 0.0.8

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/CMakeLists.txt CHANGED
@@ -18,7 +18,6 @@ target_sources(${napi_js}
18
18
  api/uv_dlib.cc
19
19
  api/uv_scheduler.cc
20
20
  support/environment.cc
21
- support/host_shim.cc
22
21
  support/host.cc
23
22
  support/initialize.cc
24
23
  transfer/accept.cc
@@ -69,3 +68,15 @@ target_sources(${napi_js}
69
68
  value/record.h.cc
70
69
  value/value.cc
71
70
  )
71
+
72
+ # Enable "delay load" for win32 otherwise the dll will attempt to load node.exe from PATH as a
73
+ # shared library and then start invoking functions from it.
74
+ if(WIN32)
75
+ set(napi_js_delay_load ${napi_js}_delay_load)
76
+ add_library(${napi_js_delay_load} STATIC)
77
+ set_target_properties(${napi_js_delay_load} PROPERTIES CXX_VISIBILITY_PRESET default)
78
+ target_sources(${napi_js_delay_load} PUBLIC support/win32_delay_load_hook.cc)
79
+ target_link_libraries(${napi_js_delay_load} PUBLIC delayimp.lib)
80
+ target_link_libraries(${napi_js} PUBLIC ${napi_js_delay_load})
81
+ target_link_options(${napi_js_delay_load} PUBLIC "-Wl,/DELAYLOAD:node.exe")
82
+ endif()
package/api/finalizer.cc CHANGED
@@ -7,15 +7,14 @@ namespace js::napi {
7
7
 
8
8
  // Generic finalizer for basic `std::unique_ptr<T>`
9
9
  template <class Env, class Type, class Deleter>
10
- auto make_finalizer(std::type_identity<Env> /*type*/, std::unique_ptr<Type, Deleter>* unique) {
10
+ auto make_finalizer(std::type_identity<Env> /*type*/, std::unique_ptr<Type, Deleter> unique) {
11
11
  if constexpr (std::is_empty_v<Deleter>) {
12
12
  // Finalizer uses a default-constructed deleter
13
13
  auto finalize = [](Env /*env*/, void* data, void* /*hint*/) -> void {
14
14
  Deleter deleter{};
15
15
  deleter(static_cast<Type*>(data));
16
16
  };
17
- auto release = [ unique ] { unique->release(); };
18
- return std::tuple{finalize, nullptr, release};
17
+ return std::tuple{finalize, nullptr, std::move(unique)};
19
18
  } else {
20
19
  static_assert(std::is_trivially_destructible_v<Deleter>);
21
20
  static_assert(sizeof(Deleter) == sizeof(void*));
@@ -23,28 +22,26 @@ auto make_finalizer(std::type_identity<Env> /*type*/, std::unique_ptr<Type, Dele
23
22
  auto finalize = [](Env /*env*/, void* data, void* hint) -> void {
24
23
  std::bit_cast<Deleter>(hint)(static_cast<Type*>(data));
25
24
  };
26
- auto release = [ unique ] { unique->release(); };
27
- return std::tuple{finalize, std::bit_cast<void*>(unique->get_deleter()), release};
25
+ return std::tuple{finalize, std::bit_cast<void*>(unique.get_deleter()), std::move(unique)};
28
26
  }
29
27
  }
30
28
 
31
29
  // Generic finalizer for `std::shared_ptr<T>`
32
30
  template <class Env, class Type>
33
- auto make_finalizer(std::type_identity<Env> /*type*/, std::shared_ptr<Type>* shared) {
31
+ auto make_finalizer(std::type_identity<Env> /*type*/, std::shared_ptr<Type> shared) {
34
32
  // `shared_ptr<T>*` is passed as the finalizer hint
35
33
  using shared_ptr_type = std::shared_ptr<Type>;
36
- auto* ptr = shared->get();
37
- auto ptr_ptr = std::make_unique<shared_ptr_type>(std::move(*shared));
34
+ auto* ptr = shared.get();
35
+ auto ptr_ptr = std::make_unique<shared_ptr_type>(std::move(shared));
38
36
  auto finalize = [](Env /*env*/, void* /*data*/, void* hint) -> void {
39
37
  delete static_cast<shared_ptr_type*>(hint);
40
38
  };
41
- auto release = [ ptr_ptr = std::move(ptr_ptr) ] mutable { ptr_ptr.release(); };
42
- return std::tuple{finalize, ptr_ptr.get(), std::move(release)};
39
+ return std::tuple{finalize, ptr_ptr.get(), std::move(ptr_ptr)};
43
40
  }
44
41
 
45
42
  // Apply a finalizer to the given smart pointer type
46
43
  template <class Pointer, class Apply>
47
- auto apply_finalizer(Pointer unique, Apply apply) -> decltype(auto) {
44
+ auto apply_finalizer(Pointer smart_ptr, Apply apply) -> decltype(auto) {
48
45
  using element_type = Pointer::element_type;
49
46
  constexpr auto napi_env_type = []() consteval -> auto {
50
47
  if constexpr (std::invocable<Apply, element_type*, node_api_basic_finalize, void*>) {
@@ -54,11 +51,12 @@ auto apply_finalizer(Pointer unique, Apply apply) -> decltype(auto) {
54
51
  return type<napi_env>;
55
52
  }
56
53
  }();
57
- auto [ finalize, hint, release ] = make_finalizer(napi_env_type, &unique);
54
+ auto* pointer = smart_ptr.get();
55
+ auto [ finalize, hint, releasable ] = make_finalizer(napi_env_type, std::move(smart_ptr));
58
56
  auto result = util::regular_return{[ & ] -> decltype(auto) {
59
- return apply(unique.get(), finalize, hint);
57
+ return apply(pointer, finalize, hint);
60
58
  }}();
61
- release();
59
+ releasable.release();
62
60
  return *std::move(result);
63
61
  }
64
62
 
@@ -10,11 +10,11 @@ napi_scheduler::napi_scheduler(napi_env env) :
10
10
  fn_.unref(env);
11
11
  }
12
12
 
13
- napi_scheduler::operator bool() const {
13
+ napi_scheduler::operator bool() const noexcept {
14
14
  return bool{fn_};
15
15
  }
16
16
 
17
- auto napi_scheduler::close(threadsafe_function_type::close_callback close) -> void {
17
+ auto napi_scheduler::close(threadsafe_function_type::close_callback close) noexcept -> void {
18
18
  fn_.close(close);
19
19
  }
20
20
 
@@ -32,7 +32,7 @@ auto napi_scheduler::increment_ref(node_api_basic_env env) const -> void {
32
32
  }
33
33
  }
34
34
 
35
- auto napi_scheduler::storage::operator()(napi_env env, napi_value value, task_type& task) -> void {
35
+ auto napi_scheduler::storage::operator()(napi_env env, napi_value value, task_type& task) noexcept -> void {
36
36
  task(env, value);
37
37
  }
38
38
 
@@ -10,21 +10,22 @@ namespace js::napi {
10
10
  export class napi_scheduler {
11
11
  private:
12
12
  struct storage;
13
- using task_type = util::move_only_function<auto(napi_env, napi_value)->void>;
13
+ using task_type = util::move_only_function<auto(napi_env, napi_value) noexcept -> void>;
14
14
  using threadsafe_function_type = threadsafe_function_of<storage, task_type>;
15
15
 
16
16
  public:
17
+ napi_scheduler() = default;
17
18
  explicit napi_scheduler(napi_env env);
18
- auto operator()(auto task, auto&&... args) const -> bool;
19
- explicit operator bool() const;
20
- auto close(threadsafe_function_type::close_callback close) -> void;
19
+ auto operator()(auto task, auto&&... args) const noexcept -> bool;
20
+ explicit operator bool() const noexcept;
21
+ auto close(threadsafe_function_type::close_callback close) noexcept -> void;
21
22
  auto decrement_ref(node_api_basic_env env) const -> void;
22
23
  auto increment_ref(node_api_basic_env env) const -> void;
23
24
 
24
25
  private:
25
26
  struct storage {
26
27
  storage() = default;
27
- auto operator()(napi_env env, napi_value value, task_type& task) -> void;
28
+ auto operator()(napi_env env, napi_value value, task_type& task) noexcept -> void;
28
29
  int refs{};
29
30
  };
30
31
 
@@ -43,7 +44,7 @@ export class napi_schedulable {
43
44
 
44
45
  // ---
45
46
 
46
- auto napi_scheduler::operator()(auto task, auto&&... args) const -> bool {
47
+ auto napi_scheduler::operator()(auto task, auto&&... args) const noexcept -> bool {
47
48
  return fn_(
48
49
  [ task = std::move(task),
49
50
  ... args = std::forward<decltype(args)>(args) ](
@@ -13,6 +13,7 @@ class threadsafe_function_of : util::non_copyable {
13
13
  public:
14
14
  using close_callback = util::function_ref<auto()->void>;
15
15
 
16
+ threadsafe_function_of() = default;
16
17
  explicit threadsafe_function_of(auto& env, napi_value value, auto&&... args);
17
18
  threadsafe_function_of(const threadsafe_function_of&);
18
19
  threadsafe_function_of(threadsafe_function_of&&) noexcept;
@@ -20,12 +21,12 @@ class threadsafe_function_of : util::non_copyable {
20
21
  auto operator=(const threadsafe_function_of&) -> threadsafe_function_of& = delete;
21
22
  auto operator=(threadsafe_function_of&&) -> threadsafe_function_of& = delete;
22
23
 
23
- auto close(close_callback callback) -> void;
24
+ auto close(close_callback callback) noexcept -> void;
24
25
  auto ref(node_api_basic_env env) const -> void;
25
26
  auto unref(node_api_basic_env env) const -> void;
26
- auto operator()(Message message) const -> bool;
27
- auto operator*() const -> Context& { return get(); }
28
- explicit operator bool() const { return tsfn_ != nullptr; }
27
+ auto operator()(Message message) const noexcept -> bool;
28
+ auto operator*() const noexcept -> Context& { return get(); }
29
+ explicit operator bool() const noexcept { return tsfn_ != nullptr; }
29
30
 
30
31
  private:
31
32
  struct storage : Context {
@@ -36,11 +37,11 @@ class threadsafe_function_of : util::non_copyable {
36
37
  private:
37
38
  close_callback close_hook_{[] -> void {}};
38
39
  };
39
- auto get() const -> storage&;
40
+ auto get() const noexcept -> storage&;
40
41
 
41
42
  constexpr static auto trivial_message =
42
43
  std::is_trivially_destructible_v<Message> && sizeof(Message) <= sizeof(void*);
43
- mutable napi_threadsafe_function tsfn_;
44
+ mutable napi_threadsafe_function tsfn_{};
44
45
  };
45
46
 
46
47
  // ---
@@ -98,7 +99,7 @@ threadsafe_function_of<Context, Message>::~threadsafe_function_of() {
98
99
  }
99
100
 
100
101
  template <class Context, class Message>
101
- auto threadsafe_function_of<Context, Message>::close(close_callback callback) -> void {
102
+ auto threadsafe_function_of<Context, Message>::close(close_callback callback) noexcept -> void {
102
103
  get().close_hook_ = callback;
103
104
  napi::invoke0_noexcept(napi_release_threadsafe_function, std::exchange(tsfn_, nullptr), napi_tsfn_abort);
104
105
  }
@@ -114,7 +115,7 @@ auto threadsafe_function_of<Context, Message>::unref(node_api_basic_env env) con
114
115
  }
115
116
 
116
117
  template <class Context, class Message>
117
- auto threadsafe_function_of<Context, Message>::operator()(Message message) const -> bool {
118
+ auto threadsafe_function_of<Context, Message>::operator()(Message message) const noexcept -> bool {
118
119
  auto check = [ & ](napi_status status) {
119
120
  switch (status) {
120
121
  case napi_ok:
@@ -147,7 +148,7 @@ auto threadsafe_function_of<Context, Message>::operator()(Message message) const
147
148
  }
148
149
 
149
150
  template <class Context, class Message>
150
- auto threadsafe_function_of<Context, Message>::get() const -> storage& {
151
+ auto threadsafe_function_of<Context, Message>::get() const noexcept -> storage& {
151
152
  auto* context = napi::invoke_noexcept(napi_get_threadsafe_function_context, tsfn_);
152
153
  return *reinterpret_cast<storage*>(context);
153
154
  }
package/api/uv_dlib.cc CHANGED
@@ -1,13 +1,14 @@
1
1
  module napi_js;
2
2
  import :api.uv_dlib;
3
+ import :support.host;
3
4
  import util;
4
5
 
5
6
  namespace js::napi {
6
7
 
7
8
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
8
9
  uv_dlib::uv_dlib(const std::string& filename) {
9
- if (uv_dlopen(filename.c_str(), &lib_) != 0) {
10
- throw js::runtime_error{util::transcode_string<char16_t>(std::string_view{uv_dlerror(&lib_)})};
10
+ if (host_uv_dlopen(filename.c_str(), &lib_) != 0) {
11
+ throw js::runtime_error{util::transcode_string<char16_t>(std::string_view{host_uv_dlerror(&lib_)})};
11
12
  }
12
13
  }
13
14
 
@@ -20,7 +21,7 @@ auto uv_dlib::operator=(uv_dlib&& right) noexcept -> uv_dlib& {
20
21
 
21
22
  uv_dlib::~uv_dlib() {
22
23
  if (lib_.handle != nullptr) {
23
- uv_dlclose(&lib_);
24
+ host_uv_dlclose(&lib_);
24
25
  }
25
26
  }
26
27
 
@@ -14,7 +14,7 @@ class reference_handle : util::non_copyable {
14
14
  env_{env},
15
15
  ref_{napi::invoke(napi_create_reference, env, value, 1)} {}
16
16
 
17
- [[nodiscard]] auto env() const -> napi_env {
17
+ [[nodiscard]] auto env() const -> node_api_basic_env {
18
18
  return env_;
19
19
  }
20
20
 
@@ -64,7 +64,7 @@ class reference_handle : util::non_copyable {
64
64
  explicit operator bool() const { return ref_ != nullptr; }
65
65
 
66
66
  private:
67
- napi_env env_{};
67
+ node_api_basic_env env_{};
68
68
  napi_ref ref_{};
69
69
  };
70
70
 
package/handle/remote.cc CHANGED
@@ -23,6 +23,7 @@ class remote : protected reference_handle {
23
23
  public:
24
24
  using unique_remote = std::unique_ptr<remote, util::function_constant<expire>>;
25
25
 
26
+ remote() = default;
26
27
  remote(private_constructor /*private*/, const environment& env, value_of<Tag> value, napi_scheduler scheduler) :
27
28
  reference_handle{napi_env{env}, napi_value{value}},
28
29
  scheduler_{std::move(scheduler)} {}
package/handle/types.cc CHANGED
@@ -63,6 +63,16 @@ struct value_specialization<boolean_tag> : value_defaults<boolean_tag> {
63
63
  using bound_type = class bound_value_for_boolean;
64
64
  };
65
65
 
66
+ template <>
67
+ struct value_specialization<false_tag> : value_defaults<false_tag> {
68
+ using bound_type = class bound_value_for_false;
69
+ };
70
+
71
+ template <>
72
+ struct value_specialization<true_tag> : value_defaults<true_tag> {
73
+ using bound_type = class bound_value_for_true;
74
+ };
75
+
66
76
  template <>
67
77
  struct value_specialization<number_tag> : value_defaults<number_tag> {
68
78
  using bound_type = class bound_value_for_number;
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@auto_js/napi",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "author": "https://github.com/laverdet/",
5
5
  "description": "Automatic JavaScript to N-API bindings",
6
6
  "homepage": "https://github.com/laverdet/isolated-vm/tree/experimental/packages/auto_js/napi",
7
7
  "license": "ISC",
8
8
  "dependencies": {
9
- "@auto_js/js": "^0.0.7",
10
- "@auto_js/napi_exports": "^0.0.7",
11
- "@auto_js/v8_exports": "^0.0.5"
9
+ "@auto_js/js": "^0.0.8",
10
+ "@auto_js/napi_exports": "^0.0.8",
11
+ "@auto_js/v8_exports": "^0.0.6"
12
12
  },
13
13
  "repository": {
14
14
  "type": "git",
package/support/host.cc CHANGED
@@ -6,31 +6,45 @@ namespace js::napi {
6
6
  util::lockable_with<bool, {.shared = true}> host_environment_initialized;
7
7
 
8
8
  // Addressof operations
9
- constexpr auto direct_handle_address =
10
- [](napi_value value) noexcept -> void* { return value; };
9
+ constexpr auto direct_handle_address = [](napi_value value) noexcept -> void* { return value; };
10
+ constexpr auto indirect_handle_address = [](napi_value value) noexcept -> void* { return *reinterpret_cast<void**>(value); };
11
11
 
12
- constexpr auto indirect_handle_address =
13
- [](napi_value value) noexcept -> void* { return *reinterpret_cast<void**>(value); };
12
+ // Fast functions which are supported on all platforms
13
+ // nb: Bun exports these v8 functions, but they crash with the `std::bit_cast<v8::Local<T>>` trick.
14
+ auto v8_is_false(napi_env /*env*/, napi_value value) -> bool {
15
+ return std::bit_cast<v8::Local<v8::Value>>(value)->IsFalse();
16
+ }
14
17
 
15
- // Value inspection
16
- constexpr auto v8_maybe_fast_is_number_int32 =
17
- [](value_of<number_tag> value) -> std::optional<bool> { return std::bit_cast<v8::Local<v8::Number>>(value)->IsInt32(); };
18
+ auto v8_is_null(napi_env /*env*/, napi_value value) -> bool {
19
+ return std::bit_cast<v8::Local<v8::Value>>(value)->IsNull();
20
+ }
18
21
 
19
- constexpr auto v8_maybe_fast_is_string_one_byte =
20
- [](value_of<string_tag> value) -> std::optional<bool> { return std::bit_cast<v8::Local<v8::String>>(value)->IsOneByte(); };
22
+ auto v8_is_true(napi_env /*env*/, napi_value value) -> bool {
23
+ return std::bit_cast<v8::Local<v8::Value>>(value)->IsTrue();
24
+ }
25
+
26
+ auto v8_is_undefined(napi_env /*env*/, napi_value value) -> bool {
27
+ return std::bit_cast<v8::Local<v8::Value>>(value)->IsUndefined();
28
+ }
21
29
 
22
- constexpr auto napi_maybe_is_shared_array_buffer =
23
- [](napi_env env, value_of<object_tag> value) -> std::optional<bool> { return napi::invoke(node_api_is_sharedarraybuffer, env, value); };
30
+ auto napi_fast_is_false(napi_env env, napi_value value) -> bool {
31
+ return value == napi::invoke(napi_get_boolean, env, false);
32
+ }
24
33
 
25
- constexpr auto unknown_maybe_fast_is_number_int32 =
26
- [](value_of<number_tag> /*value*/) -> std::optional<bool> { return std::nullopt; };
34
+ auto napi_fast_is_null(napi_env env, napi_value value) -> bool {
35
+ return value == napi::invoke(napi_get_null, env);
36
+ }
27
37
 
28
- constexpr auto unknown_maybe_fast_is_string_one_byte =
29
- [](value_of<string_tag> /*value*/) -> std::optional<bool> { return std::nullopt; };
38
+ auto napi_fast_is_true(napi_env env, napi_value value) -> bool {
39
+ return value == napi::invoke(napi_get_boolean, env, true);
40
+ }
30
41
 
31
- constexpr auto unknown_maybe_is_shared_array_buffer =
32
- [](napi_env /*env*/, value_of<object_tag> /*value*/) -> std::optional<bool> { return std::nullopt; };
42
+ auto napi_fast_is_undefined(napi_env env, napi_value value) -> bool {
43
+ return value == napi::invoke(napi_get_undefined, env);
44
+ }
33
45
 
46
+ // Extended fast functions
47
+ // nb: Bun exports these v8 functions, but they crash with the `std::bit_cast<v8::Local<T>>` trick.
34
48
  auto fast_is_array(napi_value value) -> bool {
35
49
  return std::bit_cast<v8::Local<v8::Value>>(value)->IsArray();
36
50
  }
@@ -39,18 +53,10 @@ auto fast_is_bigint(napi_value value) -> bool {
39
53
  return std::bit_cast<v8::Local<v8::Value>>(value)->IsBigInt();
40
54
  }
41
55
 
42
- auto fast_is_boolean(napi_value value) -> bool {
43
- return std::bit_cast<v8::Local<v8::Value>>(value)->IsBoolean();
44
- }
45
-
46
56
  auto fast_is_function(napi_value value) -> bool {
47
57
  return std::bit_cast<v8::Local<v8::Value>>(value)->IsFunction();
48
58
  }
49
59
 
50
- auto fast_is_null(napi_value value) -> bool {
51
- return std::bit_cast<v8::Local<v8::Value>>(value)->IsNull();
52
- }
53
-
54
60
  auto fast_is_number(napi_value value) -> bool {
55
61
  return std::bit_cast<v8::Local<v8::Value>>(value)->IsNumber();
56
62
  }
@@ -63,13 +69,56 @@ auto fast_is_string(napi_value value) -> bool {
63
69
  return std::bit_cast<v8::Local<v8::Value>>(value)->IsString();
64
70
  }
65
71
 
66
- auto fast_is_undefined(napi_value value) -> bool {
67
- return std::bit_cast<v8::Local<v8::Value>>(value)->IsUndefined();
72
+ // Bug fixes
73
+ auto napi_is_object_array_buffer(napi_env env, value_of<object_tag> value) -> bool {
74
+ return napi::invoke(napi_is_arraybuffer, env, value);
75
+ }
76
+
77
+ auto napi_is_data_block_array_buffer(napi_env env, value_of<data_block_tag> value) -> bool {
78
+ return napi_is_object_array_buffer(env, value);
79
+ }
80
+
81
+ auto bun_is_data_block_array_buffer(napi_env env, value_of<data_block_tag> value) -> bool {
82
+ // https://github.com/oven-sh/bun/issues/32624
83
+ auto* string = napi::invoke(napi_coerce_to_string, env, value);
84
+ auto length = napi::invoke(napi_get_value_string_latin1, env, string, nullptr, 0);
85
+ // '[object ArrayBuffer]'
86
+ return length == 20;
87
+ }
88
+
89
+ auto bun_is_object_array_buffer(napi_env env, value_of<object_tag> value) -> bool {
90
+ if (napi::invoke(napi_is_arraybuffer, env, value)) {
91
+ return bun_is_data_block_array_buffer(env, value_of<data_block_tag>::from(value));
92
+ } else {
93
+ return false;
94
+ }
95
+ }
96
+
97
+ // Optional value inspectors.
98
+ auto v8_is_number_int32(value_of<number_tag> value) -> std::optional<bool> {
99
+ return std::bit_cast<v8::Local<v8::Number>>(value)->IsInt32();
100
+ };
101
+
102
+ auto v8_is_string_one_byte(value_of<string_tag> value) -> std::optional<bool> {
103
+ return std::bit_cast<v8::Local<v8::String>>(value)->IsOneByte();
68
104
  }
69
105
 
70
- // 'SharedArrayBuffer' constructors
71
- constexpr auto v8_make_shared_array_buffer =
72
- [](js::shared_array_buffer::shared_pointer_type data, std::size_t byte_length) -> value_of<shared_array_buffer_tag> {
106
+ auto node_api_maybe_is_shared_array_buffer(napi_env env, value_of<object_tag> value) -> std::optional<bool> {
107
+ return napi::invoke(node_api_is_sharedarraybuffer, env, value);
108
+ }
109
+
110
+ auto bun_maybe_is_shared_array_buffer(napi_env env, value_of<object_tag> value) -> std::optional<bool> {
111
+ if (napi::invoke(napi_is_arraybuffer, env, value)) {
112
+ return !bun_is_data_block_array_buffer(env, value_of<data_block_tag>::from(value));
113
+ } else {
114
+ return std::nullopt;
115
+ }
116
+ }
117
+
118
+ constexpr auto unknown_maybe_is = [](auto...) -> std::optional<bool> { return std::nullopt; };
119
+
120
+ // 'SharedArrayBuffer' functions
121
+ auto v8_make_shared_array_buffer(js::shared_array_buffer::shared_pointer_type data, std::size_t byte_length) -> value_of<shared_array_buffer_tag> {
73
122
  auto backing_store = [ & ]() -> auto {
74
123
  // v8 does not call the deleter `byte_length` is zero. So the heap-allocated shared_ptr trick
75
124
  // does not work in that case.
@@ -93,31 +142,28 @@ constexpr auto v8_make_shared_array_buffer =
93
142
  return value_of<shared_array_buffer_tag>::from(std::bit_cast<napi_value>(shared_array_buffer));
94
143
  };
95
144
 
96
- constexpr auto unknown_make_shared_array_buffer =
97
- // NOLINTNEXTLINE(performance-unnecessary-value-param)
98
- [](js::shared_array_buffer::shared_pointer_type /*data*/, std::size_t /*byte_length*/) -> value_of<shared_array_buffer_tag> {
99
- throw js::type_error{u"SharedArrayBuffer is not supported in this environment"};
145
+ auto v8_shared_array_buffer_get_byte_length(value_of<shared_array_buffer_tag> buffer) -> std::size_t {
146
+ return std::bit_cast<v8::Local<v8::SharedArrayBuffer>>(napi_value{buffer})->ByteLength();
147
+ };
148
+
149
+ auto v8_shared_array_buffer_get_backing_store(value_of<shared_array_buffer_tag> buffer) -> std::shared_ptr<std::byte[]> {
150
+ auto backing_store = std::bit_cast<v8::Local<v8::SharedArrayBuffer>>(buffer)->GetBackingStore();
151
+ auto* data = reinterpret_cast<std::byte*>(backing_store->Data());
152
+ // NOLINTNEXTLINE(modernize-avoid-c-arrays)
153
+ return std::shared_ptr<std::byte[]>{std::move(backing_store), data};
100
154
  };
101
155
 
102
156
  // 'ArrayBufferView' constructors
103
- constexpr auto v8_make_sab_data_view =
104
- // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
105
- [](value_of<shared_array_buffer_tag> buffer, std::size_t byte_offset, std::size_t length) -> value_of<data_view_tag> {
157
+ // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
158
+ auto v8_make_sab_data_view(value_of<shared_array_buffer_tag> buffer, std::size_t byte_offset, std::size_t length) -> value_of<data_view_tag> {
106
159
  auto buffer_local = std::bit_cast<v8::Local<v8::SharedArrayBuffer>>(napi_value{buffer});
107
160
  auto view_local = v8::DataView::New(buffer_local, byte_offset, length);
108
161
  return value_of<data_view_tag>::from(std::bit_cast<napi_value>(view_local));
109
162
  };
110
163
 
111
- constexpr auto unknown_make_sab_data_view =
112
- // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
113
- [](value_of<shared_array_buffer_tag> /*buffer*/, std::size_t /*byte_offset*/, std::size_t /*length*/) -> value_of<data_view_tag> {
114
- throw js::type_error{u"SharedArrayBuffer is not supported in this environment"};
115
- };
116
-
117
164
  template <class Type>
118
- constexpr auto v8_make_sab_typed_array_of =
119
- // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
120
- [](value_of<shared_array_buffer_tag> buffer, std::size_t byte_offset, std::size_t length) -> value_of<typed_array_tag_of<Type>> {
165
+ // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
166
+ auto v8_make_sab_typed_array_of(value_of<shared_array_buffer_tag> buffer, std::size_t byte_offset, std::size_t length) -> value_of<typed_array_tag_of<Type>> {
121
167
  using constructor_type = type_t<util::overloaded{
122
168
  [](std::type_identity<double>) -> std::type_identity<v8::Float64Array> { return {}; },
123
169
  [](std::type_identity<float>) -> std::type_identity<v8::Float32Array> { return {}; },
@@ -138,12 +184,26 @@ constexpr auto v8_make_sab_typed_array_of =
138
184
  };
139
185
 
140
186
  template <class Type>
141
- constexpr auto unknown_make_sab_typed_array_of =
142
- // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
143
- [](value_of<shared_array_buffer_tag> /*buffer*/, std::size_t /*byte_offset*/, std::size_t /*length*/) -> value_of<typed_array_tag_of<Type>> {
187
+ // NOLINTNEXTLINE(performance-unnecessary-value-param)
188
+ constexpr auto unknown_sab_throw = [](auto...) -> Type {
144
189
  throw js::type_error{u"SharedArrayBuffer is not supported in this environment"};
145
190
  };
146
191
 
192
+ // Dynamic libraries
193
+ constexpr auto node_uv_dlerror =
194
+ [](const uv_lib_t* lib) -> const char* { return uv_dlerror(lib); };
195
+ constexpr auto node_uv_dlopen =
196
+ [](const char* filename, uv_lib_t* lib) -> int { return uv_dlopen(filename, lib); };
197
+ constexpr auto node_uv_dlclose =
198
+ [](uv_lib_t* lib) -> void { uv_dlclose(lib); };
199
+
200
+ constexpr auto unknown_uv_dlerror =
201
+ [](const uv_lib_t* /*lib*/) -> const char* { return "Dynamic libraries are not supported in this environment"; };
202
+ constexpr auto unknown_uv_dlopen =
203
+ [](const char* /*filename*/, uv_lib_t* /*lib*/) -> int { return -1; };
204
+ constexpr auto unknown_uv_dlclose =
205
+ [](uv_lib_t* /*lib*/) -> void { std::unreachable(); };
206
+
147
207
  auto initialize_host_environment(napi_env env) -> void {
148
208
  if (!*host_environment_initialized.read()) {
149
209
  if (auto lock = host_environment_initialized.write(); !*lock) {
@@ -168,44 +228,53 @@ auto initialize_host_environment(napi_env env) -> void {
168
228
  handle_addressof = indirect_handle_address;
169
229
  }
170
230
 
171
- // Detect bun via `process.isBun`
172
- auto is_bun = [ & ] -> bool {
231
+ // Get `globalThis.process`
232
+ auto* process_global = [ & ]() -> napi_value {
173
233
  auto* global = napi::invoke(napi_get_global, env);
174
234
  constexpr auto process_cw = util::make_consteval_string_view(util::cw<"process">);
175
235
  auto* process_str = napi::invoke(node_api_create_property_key_latin1, env, process_cw.data(), process_cw.length());
176
236
  auto* process = napi::invoke(napi_get_property, env, global, process_str);
177
237
  if (napi::invoke(napi_typeof, env, process) == napi_object) {
178
- constexpr auto is_bun_cw = util::make_consteval_string_view(util::cw<"isBun">);
179
- auto* is_bun_str = napi::invoke(node_api_create_property_key_latin1, env, is_bun_cw.data(), is_bun_cw.length());
180
- auto* is_bun = napi::invoke(napi_get_property, env, process, is_bun_str);
181
- auto* is_bun_bool = napi::invoke(napi_coerce_to_bool, env, is_bun);
182
- return napi::invoke(napi_get_value_bool, env, is_bun_bool);
238
+ return process;
183
239
  } else {
240
+ return nullptr;
241
+ }
242
+ }();
243
+
244
+ // Detect bun via `process.isBun`
245
+ auto is_bun = process_global == nullptr ? false : [ & ]() -> bool {
246
+ constexpr auto is_bun_cw = util::make_consteval_string_view(util::cw<"isBun">);
247
+ auto* is_bun_str = napi::invoke(node_api_create_property_key_latin1, env, is_bun_cw.data(), is_bun_cw.length());
248
+ auto* is_bun = napi::invoke(napi_get_property, env, process_global, is_bun_str);
249
+ auto* is_bun_bool = napi::invoke(napi_coerce_to_bool, env, is_bun);
250
+ return napi::invoke(napi_get_value_bool, env, is_bun_bool);
251
+ }();
252
+
253
+ // Detect deno via `process.versions.deno`
254
+ auto is_deno = process_global == nullptr ? false : [ & ]() -> bool {
255
+ constexpr auto versions_cw = util::make_consteval_string_view(util::cw<"versions">);
256
+ auto* versions_str = napi::invoke(node_api_create_property_key_latin1, env, versions_cw.data(), versions_cw.length());
257
+ auto* versions = napi::invoke(napi_get_property, env, process_global, versions_str);
258
+ if (napi::invoke(napi_typeof, env, versions) != napi_object) {
184
259
  return false;
185
260
  }
261
+ constexpr auto deno_cw = util::make_consteval_string_view(util::cw<"deno">);
262
+ auto* deno_str = napi::invoke(node_api_create_property_key_latin1, env, deno_cw.data(), deno_cw.length());
263
+ auto* deno = napi::invoke(napi_get_property, env, versions, deno_str);
264
+ auto* deno_bool = napi::invoke(napi_coerce_to_bool, env, deno);
265
+ return napi::invoke(napi_get_value_bool, env, deno_bool);
186
266
  }();
187
- if (is_bun) {
188
- has_fast_is_functions = false;
189
- make_sab_data_view = unknown_make_sab_data_view;
190
- make_sab_typed_array_of<double> = unknown_make_sab_typed_array_of<double>;
191
- make_sab_typed_array_of<float> = unknown_make_sab_typed_array_of<float>;
192
- make_sab_typed_array_of<js::float16_t> = unknown_make_sab_typed_array_of<js::float16_t>;
193
- make_sab_typed_array_of<js::uint8_clamped_t> = unknown_make_sab_typed_array_of<js::uint8_clamped_t>;
194
- make_sab_typed_array_of<std::int16_t> = unknown_make_sab_typed_array_of<std::int16_t>;
195
- make_sab_typed_array_of<std::int32_t> = unknown_make_sab_typed_array_of<std::int32_t>;
196
- make_sab_typed_array_of<std::int64_t> = unknown_make_sab_typed_array_of<std::int64_t>;
197
- make_sab_typed_array_of<std::int8_t> = unknown_make_sab_typed_array_of<std::int8_t>;
198
- make_sab_typed_array_of<std::uint16_t> = unknown_make_sab_typed_array_of<std::uint16_t>;
199
- make_sab_typed_array_of<std::uint32_t> = unknown_make_sab_typed_array_of<std::uint32_t>;
200
- make_sab_typed_array_of<std::uint64_t> = unknown_make_sab_typed_array_of<std::uint64_t>;
201
- make_sab_typed_array_of<std::uint8_t> = unknown_make_sab_typed_array_of<std::uint8_t>;
202
- make_shared_array_buffer = unknown_make_shared_array_buffer;
203
- maybe_fast_is_number_int32 = unknown_maybe_fast_is_number_int32;
204
- maybe_fast_is_string_one_byte = unknown_maybe_fast_is_string_one_byte;
205
- maybe_is_shared_array_buffer = unknown_maybe_is_shared_array_buffer;
206
- } else {
207
- has_fast_is_functions = true;
208
- make_sab_data_view = v8_make_sab_data_view;
267
+
268
+ auto is_nodejs = !is_bun && !is_deno;
269
+ if (is_nodejs) {
270
+ has_extended_fast_is_functions = true;
271
+ fast_is_false = v8_is_false;
272
+ fast_is_null = v8_is_null;
273
+ fast_is_true = v8_is_true;
274
+ fast_is_undefined = v8_is_undefined;
275
+ host_uv_dlclose = node_uv_dlclose;
276
+ host_uv_dlerror = node_uv_dlerror;
277
+ host_uv_dlopen = node_uv_dlopen;
209
278
  make_sab_typed_array_of<double> = v8_make_sab_typed_array_of<double>;
210
279
  make_sab_typed_array_of<float> = v8_make_sab_typed_array_of<float>;
211
280
  make_sab_typed_array_of<js::float16_t> = v8_make_sab_typed_array_of<js::float16_t>;
@@ -219,9 +288,49 @@ auto initialize_host_environment(napi_env env) -> void {
219
288
  make_sab_typed_array_of<std::uint64_t> = v8_make_sab_typed_array_of<std::uint64_t>;
220
289
  make_sab_typed_array_of<std::uint8_t> = v8_make_sab_typed_array_of<std::uint8_t>;
221
290
  make_shared_array_buffer = v8_make_shared_array_buffer;
222
- maybe_fast_is_number_int32 = v8_maybe_fast_is_number_int32;
223
- maybe_fast_is_string_one_byte = v8_maybe_fast_is_string_one_byte;
224
- maybe_is_shared_array_buffer = napi_maybe_is_shared_array_buffer;
291
+ maybe_is_number_int32 = v8_is_number_int32;
292
+ maybe_is_shared_array_buffer = node_api_maybe_is_shared_array_buffer;
293
+ maybe_is_string_latin1 = v8_is_string_one_byte;
294
+ shared_array_buffer_get_backing_store = v8_shared_array_buffer_get_backing_store;
295
+ shared_array_buffer_get_byte_length = v8_shared_array_buffer_get_byte_length;
296
+ } else {
297
+ has_extended_fast_is_functions = false;
298
+ fast_is_false = napi_fast_is_false;
299
+ fast_is_null = napi_fast_is_null;
300
+ fast_is_true = napi_fast_is_true;
301
+ fast_is_undefined = napi_fast_is_undefined;
302
+ host_uv_dlclose = unknown_uv_dlclose;
303
+ host_uv_dlerror = unknown_uv_dlerror;
304
+ host_uv_dlopen = unknown_uv_dlopen;
305
+ make_sab_data_view = unknown_sab_throw<value_of<data_view_tag>>;
306
+ make_sab_typed_array_of<double> = unknown_sab_throw<value_of<typed_array_tag_of<double>>>;
307
+ make_sab_typed_array_of<float> = unknown_sab_throw<value_of<typed_array_tag_of<float>>>;
308
+ make_sab_typed_array_of<js::float16_t> = unknown_sab_throw<value_of<typed_array_tag_of<js::float16_t>>>;
309
+ make_sab_typed_array_of<js::uint8_clamped_t> = unknown_sab_throw<value_of<typed_array_tag_of<js::uint8_clamped_t>>>;
310
+ make_sab_typed_array_of<std::int16_t> = unknown_sab_throw<value_of<typed_array_tag_of<std::int16_t>>>;
311
+ make_sab_typed_array_of<std::int32_t> = unknown_sab_throw<value_of<typed_array_tag_of<std::int32_t>>>;
312
+ make_sab_typed_array_of<std::int64_t> = unknown_sab_throw<value_of<typed_array_tag_of<std::int64_t>>>;
313
+ make_sab_typed_array_of<std::int8_t> = unknown_sab_throw<value_of<typed_array_tag_of<std::int8_t>>>;
314
+ make_sab_typed_array_of<std::uint16_t> = unknown_sab_throw<value_of<typed_array_tag_of<std::uint16_t>>>;
315
+ make_sab_typed_array_of<std::uint32_t> = unknown_sab_throw<value_of<typed_array_tag_of<std::uint32_t>>>;
316
+ make_sab_typed_array_of<std::uint64_t> = unknown_sab_throw<value_of<typed_array_tag_of<std::uint64_t>>>;
317
+ make_sab_typed_array_of<std::uint8_t> = unknown_sab_throw<value_of<typed_array_tag_of<std::uint8_t>>>;
318
+ make_shared_array_buffer = unknown_sab_throw<value_of<shared_array_buffer_tag>>;
319
+ maybe_is_number_int32 = unknown_maybe_is;
320
+ maybe_is_shared_array_buffer = unknown_maybe_is;
321
+ maybe_is_string_latin1 = unknown_maybe_is;
322
+ // NOLINTNEXTLINE(modernize-avoid-c-arrays)
323
+ shared_array_buffer_get_backing_store = unknown_sab_throw<std::shared_ptr<std::byte[]>>;
324
+ shared_array_buffer_get_byte_length = unknown_sab_throw<std::size_t>;
325
+ }
326
+
327
+ // Bug fixes
328
+ is_data_block_array_buffer = napi_is_data_block_array_buffer;
329
+ is_object_array_buffer = napi_is_object_array_buffer;
330
+ if (is_bun) {
331
+ is_data_block_array_buffer = bun_is_data_block_array_buffer;
332
+ is_object_array_buffer = bun_is_object_array_buffer;
333
+ maybe_is_shared_array_buffer = bun_maybe_is_shared_array_buffer;
225
334
  }
226
335
  }
227
336
  }
package/support/host.h.cc CHANGED
@@ -7,25 +7,36 @@ namespace js::napi {
7
7
  // Addressof operation
8
8
  auto (*handle_addressof)(napi_value handle) noexcept -> void* = nullptr;
9
9
 
10
- // Value inspection
11
- auto (*maybe_fast_is_number_int32)(value_of<number_tag> value) -> std::optional<bool> = nullptr;
12
- auto (*maybe_fast_is_string_one_byte)(value_of<string_tag> value) -> std::optional<bool> = nullptr;
13
- auto (*maybe_is_shared_array_buffer)(napi_env env, value_of<object_tag> value) -> std::optional<bool> = nullptr;
10
+ // Fast functions which are supported on all platforms
11
+ auto (*fast_is_false)(napi_env env, napi_value value) -> bool;
12
+ auto (*fast_is_null)(napi_env env, napi_value value) -> bool;
13
+ auto (*fast_is_true)(napi_env env, napi_value value) -> bool;
14
+ auto (*fast_is_undefined)(napi_env env, napi_value value) -> bool;
14
15
 
15
- // nb: These will crash on Bun
16
- auto has_fast_is_functions = false;
16
+ // Extended fast functions
17
+ auto has_extended_fast_is_functions = false;
17
18
  auto fast_is_array(napi_value value) -> bool;
18
19
  auto fast_is_bigint(napi_value value) -> bool;
19
- auto fast_is_boolean(napi_value value) -> bool;
20
20
  auto fast_is_function(napi_value value) -> bool;
21
- auto fast_is_null(napi_value value) -> bool;
22
21
  auto fast_is_number(napi_value value) -> bool;
23
22
  auto fast_is_object(napi_value value) -> bool;
24
23
  auto fast_is_string(napi_value value) -> bool;
25
- auto fast_is_undefined(napi_value value) -> bool;
26
24
 
27
- // 'SharedArrayBuffer' constructor
25
+ // Bug fixes
26
+ auto (*is_object_array_buffer)(napi_env env, value_of<object_tag> value) -> bool = nullptr;
27
+ auto (*is_data_block_array_buffer)(napi_env env, value_of<data_block_tag> value) -> bool = nullptr;
28
+
29
+ // Optional value inspectors. int32 & latin1 will select optimized representations, and
30
+ // shared_array_buffer is required for support of that value type.
31
+ auto (*maybe_is_number_int32)(value_of<number_tag> value) -> std::optional<bool> = nullptr;
32
+ auto (*maybe_is_string_latin1)(value_of<string_tag> value) -> std::optional<bool> = nullptr;
33
+ auto (*maybe_is_shared_array_buffer)(napi_env env, value_of<object_tag> value) -> std::optional<bool> = nullptr;
34
+
35
+ // 'SharedArrayBuffer' functions
28
36
  auto (*make_shared_array_buffer)(js::shared_array_buffer::shared_pointer_type data, std::size_t byte_length) -> value_of<shared_array_buffer_tag> = nullptr;
37
+ auto (*shared_array_buffer_get_byte_length)(value_of<shared_array_buffer_tag> buffer) -> std::size_t = nullptr;
38
+ // NOLINTNEXTLINE(modernize-avoid-c-arrays)
39
+ auto (*shared_array_buffer_get_backing_store)(value_of<shared_array_buffer_tag> buffer) -> std::shared_ptr<std::byte[]> = nullptr;
29
40
 
30
41
  // 'ArrayBufferView' constructors
31
42
  auto (*make_sab_data_view)(value_of<shared_array_buffer_tag> buffer, std::size_t byte_offset, std::size_t length) -> value_of<data_view_tag> = nullptr;
@@ -50,6 +61,11 @@ template make_sab_typed_array_constructor<std::uint32_t>* make_sab_typed_array_o
50
61
  template make_sab_typed_array_constructor<std::uint64_t>* make_sab_typed_array_of<std::uint64_t>;
51
62
  template make_sab_typed_array_constructor<std::uint8_t>* make_sab_typed_array_of<std::uint8_t>;
52
63
 
64
+ // uv dlfcn wrappers
65
+ auto (*host_uv_dlerror)(const uv_lib_t* lib) -> const char*;
66
+ auto (*host_uv_dlopen)(const char* filename, uv_lib_t* lib) -> int;
67
+ auto (*host_uv_dlclose)(uv_lib_t* lib) -> void;
68
+
53
69
  // Once per process, this detects the host environment (nodejs vs bun)
54
70
  auto initialize_host_environment(napi_env env) -> void;
55
71
 
@@ -42,7 +42,8 @@ class resolver {
42
42
 
43
43
  // Fulfill using direct resolution value. Note it still needs try/catch (in `fulfill`) because
44
44
  // the `transfer` operation could fail.
45
- auto resolve(auto value) -> void {
45
+ template <class Type>
46
+ auto resolve(Type value) noexcept(std::is_nothrow_move_constructible_v<Type>) -> void {
46
47
  using expected_type = std::expected<decltype(value), js::error_value>;
47
48
  schedule(
48
49
  [](Environment& /*env*/, auto value) -> expected_type {
@@ -53,7 +54,7 @@ class resolver {
53
54
  }
54
55
 
55
56
  // Fulfill using thrown error.
56
- auto reject(js::error_value error) -> void {
57
+ auto reject(js::error_value error) noexcept -> void {
57
58
  using expected_type = std::expected<std::monostate, js::error_value>;
58
59
  schedule(
59
60
  [](Environment& /*env*/, js::error_value error) -> expected_type {
@@ -64,10 +65,11 @@ class resolver {
64
65
  }
65
66
 
66
67
  private:
67
- auto schedule(auto operation, auto&&... args) -> void {
68
+ template <class Operation, class... Args>
69
+ auto schedule(Operation operation, Args&&... args) noexcept(std::is_nothrow_move_constructible_v<Operation> && (std::is_nothrow_move_constructible_v<Args> && ...)) -> void {
68
70
  auto scheduler = std::move(scheduler_);
69
71
  scheduler(
70
- [](napi_env env, napi_value /*nothing*/, napi_deferred deferred, auto operation, auto&&... args) {
72
+ [](napi_env env, napi_value /*nothing*/, napi_deferred deferred, auto operation, auto&&... args) noexcept {
71
73
  auto& environment = napi::environment::unsafe_get_environment_as<Environment>(env);
72
74
  environment.scheduler().decrement_ref(env);
73
75
  try {
@@ -81,10 +83,10 @@ class resolver {
81
83
  }
82
84
  } catch (const napi::pending_error& /*error*/) {
83
85
  auto* exception = napi::invoke(napi_get_and_clear_last_exception, env);
84
- napi::invoke0(napi_reject_deferred, env, deferred, exception);
86
+ napi::invoke0_noexcept(napi_reject_deferred, env, deferred, exception);
85
87
  } catch (const js::error& error) {
86
88
  auto exception = js::transfer_in_strict<napi_value>(error, environment);
87
- napi::invoke0(napi_reject_deferred, env, deferred, exception);
89
+ napi::invoke0_noexcept(napi_reject_deferred, env, deferred, exception);
88
90
  }
89
91
  },
90
92
  deferred_,
@@ -0,0 +1,12 @@
1
+ #define WIN32_LEAN_AND_MEAN
2
+ #include <windows.h>
3
+ #include <delayimp.h>
4
+ import std;
5
+
6
+ const auto __pfnDliNotifyHook2 = PfnDliHook{[](unsigned event, DelayLoadInfo* info) -> FARPROC {
7
+ if (event == dliNotePreLoadLibrary && std::string_view{info->szDll} == "node.exe") {
8
+ return reinterpret_cast<FARPROC>(GetModuleHandle(nullptr));
9
+ } else {
10
+ return nullptr;
11
+ }
12
+ }};
package/transfer/visit.cc CHANGED
@@ -134,7 +134,7 @@ struct visit_value : reference_map_t<Reference, reference_map_type> {
134
134
 
135
135
  template <class Accept>
136
136
  auto operator()(value_of<number_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
137
- if (maybe_fast_is_number_int32(subject).value_or(false)) {
137
+ if (maybe_is_number_int32(subject).value_or(false)) {
138
138
  return immediate(value_of<number_tag_of<std::int32_t>>::from(subject), accept);
139
139
  } else {
140
140
  return immediate(value_of<number_tag_of<double>>::from(subject), accept);
@@ -153,85 +153,94 @@ struct visit_value : reference_map_t<Reference, reference_map_type> {
153
153
  // Check the reference map, and lookup type via napi
154
154
  return lookup_or_visit(subject, [ & ]() -> accept_target_t<Accept> {
155
155
  // This is the pure-napi implementation
156
- auto slow_path = [ & ]() -> accept_target_t<Accept> {
157
- auto type_of = napi::invoke(napi_typeof, napi_env{*this}, subject);
158
- switch (type_of) {
159
- case napi_undefined:
160
- return (*this)(value_of<undefined_tag>::from(subject), accept);
161
- case napi_null:
162
- return (*this)(value_of<null_tag>::from(subject), accept);
163
- case napi_boolean:
164
- return (*this)(value_of<boolean_tag>::from(subject), accept);
165
- case napi_number:
166
- return (*this)(value_of<number_tag>::from(subject), accept);
167
- case napi_string:
168
- return immediate(value_of<string_tag>::from(subject), accept);
169
- case napi_symbol:
170
- return immediate(value_of<symbol_tag>::from(subject), accept);
171
- case napi_object:
172
- return immediate(value_of<object_tag>::from(subject), accept);
173
- case napi_function:
174
- return immediate(value_of<function_tag>::from(subject), accept);
175
- case napi_external:
176
- return immediate(value_of<external_tag>::from(subject), accept);
177
- case napi_bigint:
178
- return immediate(value_of<bigint_tag>::from(subject), accept);
179
- }
180
- std::unreachable();
156
+ auto slow_path = util::overloaded{
157
+ [ & ]() -> accept_target_t<Accept> {
158
+ auto type_of = napi::invoke(napi_typeof, napi_env{*this}, subject);
159
+ switch (type_of) {
160
+ case napi_undefined:
161
+ return (*this)(value_of<undefined_tag>::from(subject), accept);
162
+ case napi_null:
163
+ return (*this)(value_of<null_tag>::from(subject), accept);
164
+ case napi_boolean:
165
+ return (*this)(value_of<boolean_tag>::from(subject), accept);
166
+ case napi_number:
167
+ return (*this)(value_of<number_tag>::from(subject), accept);
168
+ case napi_string:
169
+ return immediate(value_of<string_tag>::from(subject), accept);
170
+ case napi_symbol:
171
+ return immediate(value_of<symbol_tag>::from(subject), accept);
172
+ case napi_object:
173
+ return immediate(value_of<object_tag>::from(subject), accept);
174
+ case napi_function:
175
+ return immediate(value_of<function_tag>::from(subject), accept);
176
+ case napi_external:
177
+ return immediate(value_of<external_tag>::from(subject), accept);
178
+ case napi_bigint:
179
+ return immediate(value_of<bigint_tag>::from(subject), accept);
180
+ }
181
+ std::unreachable();
182
+ },
183
+
184
+ [](auto /*tag*/, auto next) -> accept_target_t<Accept> { return next(); },
181
185
  };
182
186
 
183
- if (has_fast_is_functions) {
184
- return util::template_traverse(
185
- accept_tags_of_v<Accept>,
186
- util::overloaded{
187
- // Fast paths
188
- [ & ](undefined_tag /*tag*/, auto next) -> accept_target_t<Accept> {
189
- return fast_is_undefined(subject) ? (*this)(value_of<undefined_tag>::from(subject), accept) : next();
190
- },
191
- [ & ](null_tag /*tag*/, auto next) -> accept_target_t<Accept> {
192
- return fast_is_null(subject) ? (*this)(value_of<null_tag>::from(subject), accept) : next();
193
- },
194
- [ & ](boolean_tag /*tag*/, auto next) -> accept_target_t<Accept> {
195
- return fast_is_boolean(subject) ? (*this)(value_of<boolean_tag>::from(subject), accept) : next();
196
- },
197
- [ & ](number_tag /*tag*/, auto next) -> accept_target_t<Accept> {
198
- return fast_is_number(subject) ? (*this)(value_of<number_tag>::from(subject), accept) : next();
199
- },
200
- [ & ](string_tag /*tag*/, auto next) -> accept_target_t<Accept> {
201
- return fast_is_string(subject) ? immediate(value_of<string_tag>::from(subject), accept) : next();
202
- },
203
- [ & ](bigint_tag /*tag*/, auto next) -> accept_target_t<Accept> {
204
- return fast_is_bigint(subject) ? immediate(value_of<bigint_tag>::from(subject), accept) : next();
205
- },
206
- [ & ](date_tag /*tag*/, auto next) -> accept_target_t<Accept> {
207
- return napi::invoke(napi_is_date, napi_env{*this}, subject) ? immediate(value_of<date_tag>::from(subject), accept) : next();
208
- },
209
- [ & ](list_tag /*tag*/, auto next) -> accept_target_t<Accept> {
210
- return fast_is_array(subject) ? immediate(value_of<list_tag>::from(subject), accept) : next();
211
- },
212
- [ & ](object_tag /*tag*/, auto next) -> accept_target_t<Accept> {
213
- // nb: You can't really skip the subsequent is promise, is date, is arraybuffer, etc
214
- // checks. We don't really want to accept those types here, I don't think..
215
- return fast_is_object(subject) ? immediate(value_of<object_tag>::from(subject), accept) : next();
216
- },
217
- [ & ](function_tag /*tag*/, auto next) -> accept_target_t<Accept> {
218
- return fast_is_function(subject) ? immediate(value_of<function_tag>::from(subject), accept) : next();
219
- },
220
-
221
- // Skip these, otherwise `number_tag` and `string_tag` are invoked twice, which we don't
222
- // differentiate between.
223
- []<class Type>(number_tag_of<Type> /*tag*/, auto next) -> accept_target_t<Accept> { return next(); },
224
- []<class Type>(string_tag_of<Type> /*tag*/, auto next) -> accept_target_t<Accept> { return next(); },
225
-
226
- // Unknown tag
227
- [](auto /*tag*/, auto next) -> accept_target_t<Accept> { return next(); },
228
-
229
- // Slow path
230
- slow_path,
187
+ // Universal fast checks
188
+ auto fast_path = util::overloaded{
189
+ [ & ](undefined_tag /*tag*/, auto next) -> accept_target_t<Accept> {
190
+ return fast_is_undefined(napi_env{*this}, subject) ? (*this)(value_of<undefined_tag>::from(subject), accept) : next();
191
+ },
192
+ [ & ](null_tag /*tag*/, auto next) -> accept_target_t<Accept> {
193
+ return fast_is_null(napi_env{*this}, subject) ? (*this)(value_of<null_tag>::from(subject), accept) : next();
194
+ },
195
+ [ & ](boolean_tag /*tag*/, auto next) -> accept_target_t<Accept> {
196
+ if (fast_is_false(napi_env{*this}, subject)) {
197
+ return (*this)(value_of<false_tag>::from(subject), accept);
198
+ } else if (fast_is_true(napi_env{*this}, subject)) {
199
+ return (*this)(value_of<true_tag>::from(subject), accept);
200
+ } else {
201
+ return next();
231
202
  }
232
- );
203
+ },
204
+ };
205
+
206
+ // Extended fast checks
207
+ auto extended_fast_path = util::overloaded{
208
+ [ & ](number_tag /*tag*/, auto next) -> accept_target_t<Accept> {
209
+ return fast_is_number(subject) ? (*this)(value_of<number_tag>::from(subject), accept) : next();
210
+ },
211
+ [ & ](string_tag /*tag*/, auto next) -> accept_target_t<Accept> {
212
+ return fast_is_string(subject) ? immediate(value_of<string_tag>::from(subject), accept) : next();
213
+ },
214
+ [ & ](bigint_tag /*tag*/, auto next) -> accept_target_t<Accept> {
215
+ return fast_is_bigint(subject) ? immediate(value_of<bigint_tag>::from(subject), accept) : next();
216
+ },
217
+ [ & ](date_tag /*tag*/, auto next) -> accept_target_t<Accept> {
218
+ return napi::invoke(napi_is_date, napi_env{*this}, subject) ? immediate(value_of<date_tag>::from(subject), accept) : next();
219
+ },
220
+ [ & ](list_tag /*tag*/, auto next) -> accept_target_t<Accept> {
221
+ return fast_is_array(subject) ? immediate(value_of<list_tag>::from(subject), accept) : next();
222
+ },
223
+ [ & ](object_tag /*tag*/, auto next) -> accept_target_t<Accept> {
224
+ // nb: You can't really skip the subsequent is promise, is date, is arraybuffer, etc
225
+ // checks. We don't really want to accept those types here, I don't think..
226
+ return fast_is_object(subject) ? immediate(value_of<object_tag>::from(subject), accept) : next();
227
+ },
228
+ [ & ](function_tag /*tag*/, auto next) -> accept_target_t<Accept> {
229
+ return fast_is_function(subject) ? immediate(value_of<function_tag>::from(subject), accept) : next();
230
+ },
231
+
232
+ // Skip these, otherwise `number_tag` and `string_tag` are invoked twice, which we don't
233
+ // differentiate between.
234
+ []<class Type>(number_tag_of<Type> /*tag*/, auto next) -> accept_target_t<Accept> { return next(); },
235
+ []<class Type>(string_tag_of<Type> /*tag*/, auto next) -> accept_target_t<Accept> { return next(); },
236
+ };
237
+
238
+ if (has_extended_fast_is_functions) {
239
+ auto traverse = util::overloaded{fast_path, extended_fast_path, slow_path};
240
+ return util::template_traverse(accept_tags_of_v<Accept>, traverse);
233
241
  } else {
234
- return slow_path();
242
+ auto traverse = util::overloaded{fast_path, slow_path};
243
+ return util::template_traverse(accept_tags_of_v<Accept>, traverse);
235
244
  }
236
245
  });
237
246
  }
@@ -272,7 +281,7 @@ struct visit_value : reference_map_t<Reference, reference_map_type> {
272
281
  // strings
273
282
  template <class Accept>
274
283
  auto immediate(value_of<string_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
275
- if (maybe_fast_is_string_one_byte(subject).value_or(false)) {
284
+ if (maybe_is_string_latin1(subject).value_or(false)) {
276
285
  return accept_tagged(value_of<string_tag_of<char>>::from(subject), accept);
277
286
  } else {
278
287
  return accept_tagged(value_of<string_tag_of<char16_t>>::from(subject), accept);
@@ -298,7 +307,7 @@ struct visit_value : reference_map_t<Reference, reference_map_type> {
298
307
  return immediate(value_of<data_view_tag>::from(subject), accept);
299
308
  } else if (maybe_is_shared_array_buffer(env_.get(), subject).value_or(false)) {
300
309
  return immediate(value_of<shared_array_buffer_tag>::from(subject), accept);
301
- } else if (napi::invoke(napi_is_arraybuffer, napi_env{*this}, subject)) {
310
+ } else if (is_object_array_buffer(env_.get(), subject)) {
302
311
  return immediate(value_of<array_buffer_tag>::from(subject), accept);
303
312
  } else if (napi::invoke(napi_is_promise, napi_env{*this}, subject)) {
304
313
  return immediate(value_of<promise_tag>::from(subject), accept);
@@ -316,7 +325,7 @@ struct visit_value : reference_map_t<Reference, reference_map_type> {
316
325
  // data blocks
317
326
  template <class Accept>
318
327
  auto immediate(value_of<data_block_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
319
- if (napi::invoke(napi_is_arraybuffer, napi_env{*this}, subject)) {
328
+ if (is_data_block_array_buffer(env_.get(), subject)) {
320
329
  return immediate(value_of<array_buffer_tag>::from(subject), accept);
321
330
  } else {
322
331
  return immediate(value_of<shared_array_buffer_tag>::from(subject), accept);
@@ -24,11 +24,9 @@ bound_value_for_array_buffer::operator js::array_buffer() const {
24
24
 
25
25
  // `bound_value_for_shared_array_buffer`
26
26
  bound_value_for_shared_array_buffer::operator js::shared_array_buffer() const {
27
- auto local = std::bit_cast<v8::Local<v8::SharedArrayBuffer>>(napi_value{*this});
28
- auto backing_store = local->GetBackingStore();
29
- auto* data = reinterpret_cast<std::byte*>(backing_store->Data());
30
- auto data_shared_ptr = std::shared_ptr<js::shared_array_buffer::array_type>{std::move(backing_store), data};
31
- return js::shared_array_buffer{local->ByteLength(), std::move(data_shared_ptr)};
27
+ auto byte_length = shared_array_buffer_get_byte_length(value_of{*this});
28
+ auto backing_store = shared_array_buffer_get_backing_store(value_of{*this});
29
+ return js::shared_array_buffer{byte_length, std::move(backing_store)};
32
30
  }
33
31
 
34
32
  // `value_for_typed_array`
@@ -13,6 +13,19 @@ class bound_value_for_boolean : public bound_value_next<boolean_tag> {
13
13
  using bound_value_next<boolean_tag>::bound_value_next;
14
14
  [[nodiscard]] explicit operator bool() const;
15
15
  };
16
+
17
+ class bound_value_for_false : public bound_value_next<false_tag> {
18
+ public:
19
+ using bound_value_next<false_tag>::bound_value_next;
20
+ [[nodiscard]] constexpr explicit operator bool() const { return false; }
21
+ };
22
+
23
+ class bound_value_for_true : public bound_value_next<true_tag> {
24
+ public:
25
+ using bound_value_next<true_tag>::bound_value_next;
26
+ [[nodiscard]] constexpr explicit operator bool() const { return true; }
27
+ };
28
+
16
29
  // number
17
30
  class bound_value_for_number : public bound_value_next<number_tag> {
18
31
  public:
@@ -1,42 +0,0 @@
1
- import std;
2
-
3
- // NOLINTBEGIN(bugprone-macro-parentheses)
4
- #if _WIN64
5
- // I couldn't get this to work.
6
- // https://stackoverflow.com/questions/2290587/gcc-style-weak-linking-in-visual-studio
7
- #define SHIM(NAME)
8
- #else
9
- #define SHIM(NAME) \
10
- __attribute__((weak)) __attribute__((visibility("default"))) auto NAME = nullptr;
11
- #endif
12
- // NOLINTEND(bugprone-macro-parentheses)
13
-
14
- extern "C" {
15
- const void* shim_nullptr = nullptr;
16
- // NOLINTBEGIN(bugprone-reserved-identifier)
17
- SHIM(_ZN2v810Int16Array3NewENS_5LocalINS_17SharedArrayBufferEEEmm)
18
- SHIM(_ZN2v810Int32Array3NewENS_5LocalINS_17SharedArrayBufferEEEmm)
19
- SHIM(_ZN2v810Uint8Array3NewENS_5LocalINS_17SharedArrayBufferEEEmm)
20
- SHIM(_ZN2v811Uint16Array3NewENS_5LocalINS_17SharedArrayBufferEEEmm)
21
- SHIM(_ZN2v811Uint32Array3NewENS_5LocalINS_17SharedArrayBufferEEEmm)
22
- SHIM(_ZN2v812BackingStoreD1Ev)
23
- SHIM(_ZN2v812Float16Array3NewENS_5LocalINS_17SharedArrayBufferEEEmm)
24
- SHIM(_ZN2v812Float32Array3NewENS_5LocalINS_17SharedArrayBufferEEEmm)
25
- SHIM(_ZN2v812Float64Array3NewENS_5LocalINS_17SharedArrayBufferEEEmm)
26
- SHIM(_ZN2v813BigInt64Array3NewENS_5LocalINS_17SharedArrayBufferEEEmm)
27
- SHIM(_ZN2v814BigUint64Array3NewENS_5LocalINS_17SharedArrayBufferEEEmm)
28
- SHIM(_ZN2v817SharedArrayBuffer15GetBackingStoreEv)
29
- SHIM(_ZN2v817SharedArrayBuffer15NewBackingStoreEPvmPFvS1_mS1_ES1_)
30
- SHIM(_ZN2v817SharedArrayBuffer3NewEPNS_7IsolateENSt3__110shared_ptrINS_12BackingStoreEEE)
31
- SHIM(_ZN2v817Uint8ClampedArray3NewENS_5LocalINS_17SharedArrayBufferEEEmm)
32
- SHIM(_ZN2v87Isolate10GetCurrentEv)
33
- SHIM(_ZN2v88DataView3NewENS_5LocalINS_17SharedArrayBufferEEEmm)
34
- SHIM(_ZN2v89Int8Array3NewENS_5LocalINS_17SharedArrayBufferEEEmm)
35
- SHIM(_ZNK2v812BackingStore4DataEv)
36
- SHIM(_ZNK2v817SharedArrayBuffer10ByteLengthEv)
37
- SHIM(_ZNK2v85Value7IsInt32Ev)
38
- SHIM(_ZNK2v85Value8IsNumberEv)
39
- SHIM(_ZNK2v86String9IsOneByteEv)
40
- SHIM(node_api_is_sharedarraybuffer)
41
- // NOLINTEND(bugprone-reserved-identifier)
42
- }