@auto_js/napi 0.0.1 → 0.0.2

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 (51) hide show
  1. package/CMakeLists.txt +10 -4
  2. package/README.md +30 -3
  3. package/_module.cc +12 -10
  4. package/api/api.cc +8 -7
  5. package/api/callback_info.cc +1 -7
  6. package/api/finalizer.cc +1 -4
  7. package/api/invoke.cc +4 -9
  8. package/api/uv_dlib.cc +27 -0
  9. package/api/uv_dlib.h.cc +21 -0
  10. package/api/uv_handle.cc +9 -9
  11. package/api/uv_scheduler.cc +11 -17
  12. package/api/uv_scheduler.h.cc +3 -6
  13. package/handle/bound_value.cc +4 -7
  14. package/handle/reference.cc +9 -5
  15. package/handle/remote.cc +11 -17
  16. package/handle/types.cc +61 -46
  17. package/handle/value.cc +12 -14
  18. package/package.json +7 -6
  19. package/support/callback.cc +7 -19
  20. package/support/callback_storage.cc +4 -9
  21. package/support/class_template_storage.cc +4 -6
  22. package/support/container.cc +9 -10
  23. package/support/environment.cc +32 -20
  24. package/support/environment.h.cc +8 -18
  25. package/support/environment_fwd.cc +1 -2
  26. package/support/error_scope.cc +0 -2
  27. package/support/initialize.h.cc +2 -5
  28. package/support/promise.cc +3 -10
  29. package/support/string_table.cc +3 -5
  30. package/support/utility.cc +54 -28
  31. package/transfer/accept.cc +131 -327
  32. package/transfer/accept.h.cc +390 -0
  33. package/transfer/visit.cc +201 -132
  34. package/value/array.cc +1 -3
  35. package/value/array.h.cc +5 -11
  36. package/value/array_buffer.cc +109 -0
  37. package/value/array_buffer.h.cc +150 -0
  38. package/value/class.cc +12 -21
  39. package/value/class.h.cc +5 -10
  40. package/value/external.cc +6 -10
  41. package/value/function.cc +5 -12
  42. package/value/function.h.cc +2 -9
  43. package/value/object.cc +2 -6
  44. package/value/object.h.cc +7 -15
  45. package/value/primitive.cc +12 -81
  46. package/value/primitive.h.cc +6 -64
  47. package/value/record.cc +38 -0
  48. package/value/record.h.cc +39 -0
  49. package/value/value.cc +8 -8
  50. package/value/dictionary.cc +0 -33
  51. package/value/dictionary.h.cc +0 -56
package/handle/types.cc CHANGED
@@ -4,7 +4,7 @@ import nodejs;
4
4
 
5
5
  namespace js::napi {
6
6
 
7
- // `value_handle` is the base class of `value<T>`, and `bound_value<T>`.
7
+ // `value_handle` is the base class of `value_of<T>`, and `bound_value<T>`.
8
8
  class value_handle {
9
9
  protected:
10
10
  explicit value_handle(napi_value value) :
@@ -24,9 +24,9 @@ class value_handle {
24
24
  napi_value value_{};
25
25
  };
26
26
 
27
- // `value` forward declarations
27
+ // `value_of` forward declarations
28
28
  export template <class Tag = value_tag>
29
- class value;
29
+ class value_of;
30
30
 
31
31
  template <class Tag>
32
32
  class value_next;
@@ -34,60 +34,52 @@ class value_next;
34
34
  template <class Type>
35
35
  class value_for_class_of;
36
36
 
37
+ template <class Type>
38
+ class value_for_typed_array_of;
39
+
37
40
  // `bound_value` forward declarations
38
- export template <class Tag>
41
+ template <class Tag>
39
42
  class bound_value;
40
43
 
41
44
  template <class Tag>
42
45
  class bound_value_next;
43
46
 
44
- // Map of `value<Tag>` / `bound_value<Tag>` to concrete implementation classes.
47
+ template <class Type>
48
+ class bound_value_for_typed_array_of;
49
+
50
+ // Map of `value_of<Tag>` / `bound_value<Tag>` to concrete implementation classes.
45
51
  template <class Tag>
46
- struct value_specialization {
52
+ struct value_defaults {
47
53
  using value_type = value_next<Tag>;
48
54
  using bound_type = bound_value_next<Tag>;
49
55
  };
50
56
 
51
- // Typed specializations
52
- template <>
53
- struct value_specialization<undefined_tag> {
54
- using value_type = class value_for_undefined;
55
- using bound_type = bound_value_next<undefined_tag>;
56
- };
57
-
58
- template <>
59
- struct value_specialization<null_tag> {
60
- using value_type = class value_for_null;
61
- using bound_type = bound_value_next<null_tag>;
62
- };
57
+ template <class Tag>
58
+ struct value_specialization : value_defaults<Tag> {};
63
59
 
60
+ // Typed specializations
64
61
  template <>
65
- struct value_specialization<boolean_tag> {
66
- using value_type = class value_for_boolean;
62
+ struct value_specialization<boolean_tag> : value_defaults<boolean_tag> {
67
63
  using bound_type = class bound_value_for_boolean;
68
64
  };
69
65
 
70
66
  template <>
71
- struct value_specialization<number_tag> {
72
- using value_type = class value_for_number;
67
+ struct value_specialization<number_tag> : value_defaults<number_tag> {
73
68
  using bound_type = class bound_value_for_number;
74
69
  };
75
70
 
76
71
  template <>
77
- struct value_specialization<bigint_tag> {
78
- using value_type = class value_for_bigint;
72
+ struct value_specialization<bigint_tag> : value_defaults<bigint_tag> {
79
73
  using bound_type = class bound_value_for_bigint;
80
74
  };
81
75
 
82
76
  template <>
83
- struct value_specialization<string_tag> {
84
- using value_type = class value_for_string;
77
+ struct value_specialization<string_tag> : value_defaults<string_tag> {
85
78
  using bound_type = class bound_value_for_string;
86
79
  };
87
80
 
88
81
  template <>
89
- struct value_specialization<date_tag> {
90
- using value_type = class value_for_date;
82
+ struct value_specialization<date_tag> : value_defaults<date_tag> {
91
83
  using bound_type = class bound_value_for_date;
92
84
  };
93
85
 
@@ -98,39 +90,62 @@ struct value_specialization<object_tag> {
98
90
  };
99
91
 
100
92
  template <>
101
- struct value_specialization<function_tag> {
93
+ struct value_specialization<record_tag> : value_defaults<record_tag> {
94
+ using bound_type = class bound_value_for_record;
95
+ };
96
+
97
+ template <>
98
+ struct value_specialization<function_tag> : value_defaults<function_tag> {
102
99
  using value_type = class value_for_function;
103
- using bound_type = bound_value_next<function_tag>;
104
100
  };
105
101
 
106
- template <class Type>
107
- struct value_specialization<class_tag_of<Type>> {
108
- using value_type = value_for_class_of<Type>;
109
- using bound_type = bound_value_next<class_tag_of<Type>>;
102
+ template <>
103
+ struct value_specialization<array_buffer_tag> : value_defaults<array_buffer_tag> {
104
+ using bound_type = class bound_value_for_array_buffer;
110
105
  };
111
106
 
112
107
  template <>
113
- struct value_specialization<external_tag> {
114
- using value_type = class value_for_external;
115
- using bound_type = class bound_value_for_external;
108
+ struct value_specialization<shared_array_buffer_tag> : value_defaults<shared_array_buffer_tag> {
109
+ using bound_type = class bound_value_for_shared_array_buffer;
116
110
  };
117
111
 
118
112
  template <>
119
- struct value_specialization<vector_tag> {
120
- using value_type = value_next<vector_tag>;
121
- using bound_type = class bound_value_for_vector;
113
+ struct value_specialization<array_buffer_view_tag> : value_defaults<array_buffer_view_tag> {
114
+ using bound_type = class bound_value_for_array_buffer_view;
115
+ };
116
+
117
+ template <>
118
+ struct value_specialization<typed_array_tag> {
119
+ using value_type = class value_for_typed_array;
120
+ using bound_type = class bound_value_for_typed_array;
121
+ };
122
+
123
+ template <class Type>
124
+ struct value_specialization<typed_array_tag_of<Type>> {
125
+ using value_type = value_for_typed_array_of<Type>;
126
+ using bound_type = bound_value_for_typed_array_of<Type>;
127
+ };
128
+
129
+ template <>
130
+ struct value_specialization<data_view_tag> {
131
+ using value_type = class value_for_data_view;
132
+ using bound_type = class bound_value_for_data_view;
133
+ };
134
+
135
+ template <class Type>
136
+ struct value_specialization<class_tag_of<Type>> : value_defaults<class_tag_of<Type>> {
137
+ using value_type = value_for_class_of<Type>;
122
138
  };
123
139
 
124
140
  template <>
125
- struct value_specialization<list_tag> {
126
- using value_type = value_next<list_tag>;
127
- using bound_type = class bound_value_for_list;
141
+ struct value_specialization<external_tag> {
142
+ using value_type = class value_for_external;
143
+ using bound_type = class bound_value_for_external;
128
144
  };
129
145
 
130
146
  template <>
131
- struct value_specialization<dictionary_tag> {
132
- using value_type = value_next<dictionary_tag>;
133
- using bound_type = class bound_value_for_dictionary;
147
+ struct value_specialization<vector_tag> : value_defaults<vector_tag> {
148
+ using bound_type = class bound_value_for_vector;
134
149
  };
135
150
 
136
151
  } // namespace js::napi
package/handle/value.cc CHANGED
@@ -1,52 +1,50 @@
1
- module;
2
- #include <concepts>
3
- #include <type_traits>
4
1
  export module napi_js:value_handle;
5
2
  import :handle.types;
6
3
  import auto_js;
7
4
  import nodejs;
5
+ import std;
8
6
 
9
7
  namespace js::napi {
10
8
 
11
9
  // Heirarchy:
12
- // value<object_tag> ->
10
+ // value_of<object_tag> ->
13
11
  // value_for_object ->
14
12
  // value_next<object_tag> ->
15
- // value<value_tag> -> ...
13
+ // value_of<value_tag> -> ...
16
14
 
17
- // Details applied to each level of the `value<T>` hierarchy.
15
+ // Details applied to each level of the `value_of<T>` hierarchy.
18
16
  template <class Tag>
19
- class value_next : public value<typename Tag::tag_type> {
17
+ class value_next : public value_of<typename Tag::tag_type> {
20
18
  public:
21
- using value<typename Tag::tag_type>::value;
19
+ using value_of<typename Tag::tag_type>::value_of;
22
20
 
23
21
  // "Downcast" to a more specific tag. Potentially unsafe.
24
22
  template <std::convertible_to<Tag> To>
25
- auto cast(To /*tag*/) const -> value<To> { return value<To>::from(*this); }
23
+ auto cast(To /*tag*/) const -> value_of<To> { return value_of<To>::from(*this); }
26
24
 
27
25
  // Construct from any `napi_value`. Potentially unsafe.
28
- static auto from(napi_value value_) -> value<Tag> { return value<Tag>{value_}; }
26
+ static auto from(napi_value value_) -> value_of<Tag> { return std::bit_cast<value_of<Tag>>(value_); }
29
27
  };
30
28
 
31
29
  // Tagged napi_value
32
30
  export template <class Tag>
33
- class value : public value_specialization<Tag>::value_type {
31
+ class value_of : public value_specialization<Tag>::value_type {
34
32
  public:
35
33
  using value_specialization<Tag>::value_type::value_type;
36
34
  };
37
35
 
38
36
  // Sentinel instantiation
39
37
  template <>
40
- class value<void> : public value_handle {
38
+ class value_of<void> : public value_handle {
41
39
  public:
42
40
  using value_handle::value_handle;
43
41
  };
44
42
 
45
43
  } // namespace js::napi
46
44
 
47
- // Specialize for `js::forward`. Makes `js::forward{napi::value<Tag>}` infer
45
+ // Specialize for `js::forward`. Makes `js::forward{value_of<Tag>}` infer
48
46
  // `js::forward<Tag, ...>`.
49
47
  namespace js {
50
48
  template <class Tag>
51
- struct js::forward_tag_for<napi::value<Tag>> : std::type_identity<Tag> {};
49
+ struct forward_tag_for<napi::value_of<Tag>> : std::type_identity<Tag> {};
52
50
  } // namespace js
package/package.json CHANGED
@@ -1,16 +1,17 @@
1
1
  {
2
2
  "name": "@auto_js/napi",
3
- "version": "0.0.1",
4
- "description": "Automatic JavaScript to N-API bindings",
3
+ "version": "0.0.2",
5
4
  "author": "https://github.com/laverdet/",
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
+ "dependencies": {
9
+ "@auto_js/js": "^0.0.2",
10
+ "@auto_js/v8_exports": "^0.0.2",
11
+ "@auto_js/napi_exports": "^0.0.2"
12
+ },
8
13
  "repository": {
9
14
  "type": "git",
10
15
  "url": "git+https://github.com/laverdet/isolated-vm.git"
11
- },
12
- "dependencies": {
13
- "@auto_js/js": "0.0.1",
14
- "@auto_js/napi_exports": "0.0.1"
15
16
  }
16
17
  }
@@ -1,20 +1,10 @@
1
- module;
2
- #include <optional>
3
- #include <tuple>
4
- #include <type_traits>
5
- #include <utility>
6
1
  export module napi_js:callback;
7
2
  export import :callback_storage;
8
- import :api;
9
- import :bound_value;
10
- import :environment_fwd;
11
3
  import :error_scope;
12
- import :object;
13
- import :value_handle;
4
+ import std;
14
5
  import util;
15
6
 
16
7
  namespace js::napi {
17
- namespace {
18
8
 
19
9
  // Unwrap `this` into the correct tagged external. Returns `std::nullopt` if there was a type error,
20
10
  // and there is a napi exception pending. `nullptr` will not be returned.
@@ -23,7 +13,7 @@ template <class Type>
23
13
  constexpr auto unwrap_member_this = [](auto_environment auto& env, napi_value this_arg) noexcept -> std::optional<tagged_external<Type>> {
24
14
  return napi::invoke_maybe(napi_coerce_to_object, napi_env{env}, this_arg)
25
15
  .and_then([ &env ](napi_value coerced_this_arg) -> std::optional<tagged_external<Type>> {
26
- auto as_object = bound_value{env, value<object_tag>::from(coerced_this_arg)};
16
+ auto as_object = bound_value{env, value_of<object_tag>::from(coerced_this_arg)};
27
17
  // Technically `try_cast` can throw but it should only be in catastrophic cases.
28
18
  Type* unwrapped = as_object.try_cast(type<Type>);
29
19
  if (unwrapped == nullptr) {
@@ -36,13 +26,11 @@ constexpr auto unwrap_member_this = [](auto_environment auto& env, napi_value th
36
26
  });
37
27
  };
38
28
 
39
- } // namespace
40
-
41
29
  // Function type for internal host object construction
42
30
  using internal_constructor = util::function_ref<auto(napi_value)->napi_value>;
43
31
 
44
32
  // Make callback for plain free function
45
- export template <auto_environment Environment>
33
+ template <auto_environment Environment>
46
34
  constexpr auto make_free_function(auto function) {
47
35
  constexpr auto make_with_try_catch =
48
36
  []<class Env, class... Args, bool Nx, class Result>(
@@ -92,7 +80,7 @@ constexpr auto make_free_function(auto function) {
92
80
  }
93
81
 
94
82
  // Make callback for constructor invocations
95
- export template <auto_environment Environment, class Type>
83
+ template <auto_environment Environment, class Type>
96
84
  constexpr auto make_constructor_function(auto constructor) {
97
85
  // First, a lambda is created which will actually invoke the supplied constructor function.
98
86
  constexpr auto make_invoke_runtime_constructor = util::overloaded{
@@ -128,7 +116,7 @@ constexpr auto make_constructor_function(auto constructor) {
128
116
  auto instance = std::apply(
129
117
  callback,
130
118
  std::tuple_cat(
131
- std::forward_as_tuple(env, value<object_tag>::from(info.this_arg())),
119
+ std::forward_as_tuple(env, value_of<object_tag>::from(info.this_arg())),
132
120
  js::transfer_out<std::tuple<Args...>>(info.arguments(), env)
133
121
  )
134
122
  );
@@ -148,7 +136,7 @@ constexpr auto make_constructor_function(auto constructor) {
148
136
  using callback_type = decltype(callback);
149
137
  return util::bind{
150
138
  [](callback_type& callback, Environment& env, const callback_info& info) noexcept -> napi_value {
151
- return wrap(callback(env, value<object_tag>::from(info.this_arg())));
139
+ return wrap(callback(env, value_of<object_tag>::from(info.this_arg())));
152
140
  },
153
141
  std::move(callback),
154
142
  };
@@ -206,7 +194,7 @@ constexpr auto make_constructor_function(auto constructor) {
206
194
  };
207
195
 
208
196
  // Make callback for method invocations
209
- export template <auto_environment Environment, class Type, class Method>
197
+ template <auto_environment Environment, class Type, class Method>
210
198
  constexpr auto make_member_function(Method method) {
211
199
  constexpr auto make_with_try_catch =
212
200
  []<class That, class Env, class... Args, bool Nx, class Result>(
@@ -1,20 +1,15 @@
1
- module;
2
- #include <memory>
3
- #include <type_traits>
4
- #include <utility>
5
1
  export module napi_js:callback_storage;
6
2
  import :api;
7
3
  import :environment_fwd;
4
+ import std;
8
5
 
9
6
  namespace js::napi {
10
7
 
11
- namespace {
12
8
  // Since napi runs each environment in its own thread we can store the environment data pointer
13
9
  // here. We could also go through `napi_get_instance_data` but there is no assurance that the
14
10
  // pointer type is the same. We assume the environment pointer will outlive the callback.
15
11
  template <auto_environment Environment>
16
- thread_local Environment* env_local = nullptr;
17
- } // namespace
12
+ thread_local Environment* callback_env_local = nullptr;
18
13
 
19
14
  // Converts any invocable into a `napi_callback` and data pointer for use in napi API calls. The
20
15
  // result is a `std::tuple` with `{ callback_ptr, data_ptr, finalizer }`. Finalizer is either a
@@ -35,13 +30,13 @@ auto make_callback_storage(Environment& env, std::invocable<Environment&, const
35
30
  } else if constexpr (sizeof(function_type) <= sizeof(void*)) {
36
31
  // Trivial function type containing less than or equal to a pointer size. `data` is the
37
32
  // function and environment is stored in a thread local.
38
- env_local<Environment> = &env;
33
+ callback_env_local<Environment> = &env;
39
34
  auto* data = reinterpret_cast<void*&>(function);
40
35
  const auto callback = napi_callback{[](napi_env nenv, napi_callback_info info) -> napi_value {
41
36
  const auto args = callback_info{nenv, info};
42
37
  auto* data = args.data();
43
38
  auto& invoke = reinterpret_cast<function_type&>(data);
44
- auto& env = *env_local<Environment>;
39
+ auto& env = *callback_env_local<Environment>;
45
40
  return invoke(env, args);
46
41
  }};
47
42
  return std::tuple{callback, data};
@@ -1,9 +1,7 @@
1
- module;
2
- #include <string> // IWYU pragma: keep
3
- #include <type_traits>
4
1
  export module napi_js:class_template_storage;
5
2
  import :reference;
6
3
  import auto_js;
4
+ import std;
7
5
  import util;
8
6
  using namespace std::string_literals;
9
7
 
@@ -32,8 +30,8 @@ export template <const auto& Strings>
32
30
  class class_template_references {
33
31
  public:
34
32
  template <class Type>
35
- auto class_template(this auto& self, std::type_identity<Type> /*type*/, const auto& class_template) -> napi::value<class_tag_of<Type>> {
36
- constexpr auto name_sv = util::consteval_string_view{class_template.constructor.name};
33
+ auto class_template(this auto& self, std::type_identity<Type> /*type*/, const auto& class_template) -> value_of<class_tag_of<Type>> {
34
+ constexpr auto name_sv = util::make_consteval_string_view(class_template.constructor.name);
37
35
  constexpr auto index = class_template_references_of<Strings>.lookup(name_sv);
38
36
  if constexpr (!index) {
39
37
  // nb: This fails while linking against STL for some reason. So the `constexpr` if works
@@ -45,7 +43,7 @@ class class_template_references {
45
43
  static_assert(index, "Class template '"s + name_sv + "' is missing in storage"s);
46
44
  }
47
45
  auto& reference = self.class_template_references_.at(index).second;
48
- using value_type = js::napi::value<class_tag_of<Type>>;
46
+ using value_type = js::napi::value_of<class_tag_of<Type>>;
49
47
  if (reference) {
50
48
  return value_type::from(reference.get(self));
51
49
  } else {
@@ -1,10 +1,7 @@
1
- module;
2
- #include <bit>
3
- #include <unordered_map>
4
1
  export module napi_js:container;
5
2
  import :environment;
6
- import :utility;
7
3
  import nodejs;
4
+ import std;
8
5
  import util;
9
6
 
10
7
  namespace js::napi {
@@ -15,6 +12,7 @@ struct virtual_value_map {
15
12
  public:
16
13
  using container_type = std::unordered_map<napi_value, Type>;
17
14
  using iterator = container_type::iterator;
15
+ using key_type = container_type::key_type;
18
16
  using mapped_type = container_type::mapped_type;
19
17
 
20
18
  virtual auto end() const -> iterator = 0;
@@ -26,16 +24,16 @@ template <class Map>
26
24
  struct concrete_value_map final : virtual_value_map<typename Map::mapped_type> {
27
25
  public:
28
26
  using container_type = Map;
29
- // nb: The iterator type between `std::unordered_map<int, int, left>` and
30
- // `std::unordered_map<int, int, right>` are not technically compatible.
31
- using iterator = virtual_value_map<typename Map::mapped_type>::iterator;
32
- using mapped_type = container_type::mapped_type;
27
+ using typename virtual_value_map<typename Map::mapped_type>::mapped_type;
28
+ using typename virtual_value_map<typename Map::mapped_type>::iterator;
33
29
 
34
30
  virtual auto end() const -> iterator {
35
31
  return std::bit_cast<iterator>(map_.end());
36
32
  }
37
33
 
38
34
  virtual auto find(napi_value key) const -> iterator {
35
+ // nb: The iterator type between `std::unordered_map<int, int, left>` and
36
+ // `std::unordered_map<int, int, right>` are not technically compatible.
39
37
  return std::bit_cast<iterator>(map_.find(key));
40
38
  }
41
39
 
@@ -58,6 +56,7 @@ class value_map {
58
56
  public:
59
57
  using container_type = virtual_value_map<Type>;
60
58
  using iterator = container_type::iterator;
59
+ using key_type = container_type::key_type;
61
60
  using mapped_type = container_type::mapped_type;
62
61
 
63
62
  explicit value_map(const environment& env) :
@@ -68,8 +67,8 @@ class value_map {
68
67
  } {}
69
68
 
70
69
  auto end() const -> iterator { return map_->end(); }
71
- auto find(napi_value key) const -> iterator { return map_->find(key); }
72
- auto try_emplace(napi_value key, mapped_type value) -> std::pair<iterator, bool> { return map_->try_emplace(key, std::move(value)); }
70
+ auto find(key_type key) const -> iterator { return map_->find(key); }
71
+ auto try_emplace(key_type key, mapped_type value) -> std::pair<iterator, bool> { return map_->try_emplace(key, std::move(value)); }
73
72
 
74
73
  private:
75
74
  virtual_map_type map_;
@@ -1,5 +1,3 @@
1
- module;
2
- #include <stdexcept>
3
1
  module napi_js;
4
2
  import :api;
5
3
  import :utility;
@@ -7,27 +5,41 @@ import util;
7
5
 
8
6
  namespace js::napi {
9
7
 
10
- environment::environment(napi_env env) :
8
+ environment::environment(napi_env env, bool uses_direct_handles) :
11
9
  env_{env},
12
- uses_direct_handles_{[ & ]() -> bool {
13
- const auto direct_equal = direct_address_equal{};
14
- const auto indirect_equal = indirect_address_equal{};
15
- auto* array = napi::invoke(napi_create_array_with_length, env, 1);
16
- napi::invoke0(napi_set_element, env, array, 0, array);
17
- auto* result = napi::invoke(napi_get_element, env, array, 0);
18
- if (direct_equal(array, result)) {
19
- return true;
20
- } else if (indirect_equal(array, result)) {
21
- return false;
22
- } else {
23
- throw std::runtime_error{"Exotic napi handle behavior detected"};
24
- }
25
- }()} {
10
+ address_equal_{uses_direct_handles},
11
+ uses_direct_handles_{uses_direct_handles} {
26
12
  scheduler().open(napi::invoke(napi_get_uv_event_loop, env));
13
+ // nb: `uv_async_t` is closed with an async hook, and not as the finalizer of
14
+ // `napi_set_instance_data`. `uv_close` is async and since nothing is keeping this shared library
15
+ // alive, the finalizer code can be unloaded.
16
+ auto finalizer = [](napi_async_cleanup_hook_handle handle, void* data) -> void {
17
+ auto& env = *static_cast<environment*>(data);
18
+ auto finalizer = util::fn<[](napi_async_cleanup_hook_handle handle) -> void {
19
+ napi::invoke0_noexcept(napi_remove_async_cleanup_hook, handle);
20
+ }>;
21
+ env.scheduler().close(util::function_ref{finalizer, handle});
22
+ };
23
+ napi::invoke0_noexcept(napi_add_async_cleanup_hook, env, finalizer, this, &cleanup_hook_handle_);
27
24
  }
28
25
 
29
- environment::~environment() {
30
- scheduler().close();
31
- }
26
+ environment::environment(napi_env env) :
27
+ environment{
28
+ env,
29
+ [ & ]() -> bool {
30
+ const auto direct_equal = direct_address_equal{};
31
+ const auto indirect_equal = indirect_address_equal{};
32
+ auto* array = napi::invoke(napi_create_array_with_length, env, 1);
33
+ napi::invoke0(napi_set_element, env, array, 0, array);
34
+ auto* result = napi::invoke(napi_get_element, env, array, 0);
35
+ if (direct_equal(array, result)) {
36
+ return true;
37
+ } else if (indirect_equal(array, result)) {
38
+ return false;
39
+ } else {
40
+ throw std::runtime_error{"Exotic napi handle behavior detected"};
41
+ }
42
+ }()
43
+ } {}
32
44
 
33
45
  } // namespace js::napi
@@ -1,36 +1,24 @@
1
- module;
2
- #include <functional>
3
- #include <memory>
4
1
  export module napi_js:environment;
5
2
  export import :environment_fwd;
6
3
  import :api;
4
+ import :utility;
5
+ import std;
7
6
  import util;
8
7
 
9
8
  namespace js::napi {
10
9
 
11
- // Internal holder for an environment pointer. Used as a common base class for `visit` & `accept`.
12
- // This could maybe become something similar to `realm_scope` if the need for it is more common.
13
- export template <auto_environment Type>
14
- class environment_scope {
15
- public:
16
- explicit environment_scope(Type& env) : env_{env} {}
17
-
18
- explicit operator napi_env() const { return napi_env{env_.get()}; }
19
- [[nodiscard]] auto environment() const -> Type& { return env_; }
20
-
21
- private:
22
- std::reference_wrapper<Type> env_;
23
- };
24
-
25
10
  // A reference to an environment is used as the lock witness. Generally, you should not have an
26
11
  // `environment&` unless you're in the napi thread and locked.
27
12
  export class environment : util::non_moveable, public uv_schedulable {
13
+ private:
14
+ environment(napi_env env, bool uses_direct_handles);
15
+
28
16
  public:
29
17
  explicit environment(napi_env env);
30
- ~environment();
31
18
 
32
19
  // NOLINTNEXTLINE(google-explicit-constructor)
33
20
  [[nodiscard]] operator napi_env() const { return env_; }
21
+ [[nodiscard]] auto address_equal() const -> const auto& { return address_equal_; }
34
22
  [[nodiscard]] auto uses_direct_handles() const -> bool { return uses_direct_handles_; }
35
23
 
36
24
  template <class Type>
@@ -50,6 +38,8 @@ export class environment : util::non_moveable, public uv_schedulable {
50
38
 
51
39
  private:
52
40
  napi_env env_;
41
+ napi_async_cleanup_hook_handle cleanup_hook_handle_{};
42
+ napi::address_equal address_equal_;
53
43
  bool uses_direct_handles_;
54
44
  };
55
45
 
@@ -1,6 +1,5 @@
1
- module;
2
- #include <concepts>
3
1
  export module napi_js:environment_fwd;
2
+ import std;
4
3
 
5
4
  namespace js::napi {
6
5
 
@@ -1,7 +1,5 @@
1
1
  export module napi_js:error_scope;
2
2
  import :accept;
3
- import :api;
4
- import :environment;
5
3
 
6
4
  namespace js::napi {
7
5
 
@@ -1,10 +1,7 @@
1
- module;
2
- #include <cassert>
3
- #include <tuple>
4
1
  export module napi_js:initialize;
5
- import :environment;
6
2
  import :value;
7
3
  import nodejs;
4
+ import std;
8
5
  import util;
9
6
 
10
7
  namespace js::napi {
@@ -37,7 +34,7 @@ class napi_js_module final : private detail::initialize_require {
37
34
  const auto [... indices ] = util::sequence<sizeof...(Args)>;
38
35
  auto& client_environment = environment::make_and_set_environment<Environment>(env, std::get<indices>(std::move(args_))...);
39
36
  // assign export descriptor to napi-constructor namespace object
40
- auto exports_local = napi::value<dictionary_tag>::from(exports);
37
+ auto exports_local = value_of<dictionary_tag>::from(exports);
41
38
  exports_local.assign(client_environment, std::move(make_exports_)(client_environment));
42
39
  }
43
40
 
@@ -1,18 +1,11 @@
1
- module;
2
- #include <concepts>
3
- #include <expected>
4
- #include <tuple>
5
- #include <utility>
6
1
  export module napi_js:promise;
7
- import :api;
8
- import :environment;
9
2
  import :value;
10
- import util;
3
+ import std;
11
4
 
12
5
  namespace js::napi {
13
6
 
14
7
  // Resolver for `make_promise`
15
- export template <class Environment, class Dispatch>
8
+ template <class Environment, class Dispatch>
16
9
  class resolver {
17
10
  public:
18
11
  resolver(Environment& env, napi_deferred deferred, Dispatch dispatch = {}) :
@@ -124,7 +117,7 @@ auto make_promise(Environment& env, auto... dispatch) {
124
117
  auto resolve = resolver{env, deferred, std::move(dispatch)...};
125
118
 
126
119
  // `[ promise, resolver ]`
127
- return std::tuple{value<promise_tag>::from(promise), std::move(resolve)};
120
+ return std::tuple{value_of<promise_tag>::from(promise), std::move(resolve)};
128
121
  }
129
122
 
130
123
  } // namespace js::napi