@auto_js/napi 0.0.11 → 0.0.12

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.
@@ -1,4 +1,5 @@
1
1
  export module napi_js:class_template_storage;
2
+ import :lock;
2
3
  import :reference;
3
4
  import auto_js;
4
5
  import std;
@@ -19,8 +20,8 @@ constexpr auto class_template_references_of = [] consteval -> auto {
19
20
  export template <const auto& Strings>
20
21
  class class_template_references {
21
22
  public:
22
- template <class Type>
23
- auto class_template(this auto& self, std::type_identity<Type> /*type*/, const auto& class_template) -> local_of<class_tag_of<Type>> {
23
+ template <class Environment, class Type>
24
+ auto class_template(this Environment& self, std::type_identity<Type> /*type*/, const environment_lock_witness_of<Environment>& lock, const auto& class_template) -> local_of<class_tag_of<Type>> {
24
25
  constexpr auto name_sv = util::make_consteval_string_view(class_template.constructor.name);
25
26
  constexpr auto index = class_template_references_of<Strings>.lookup(name_sv);
26
27
  if constexpr (!index) {
@@ -35,10 +36,10 @@ class class_template_references {
35
36
  auto& reference = self.class_template_references_.at(index).second;
36
37
  using value_type = js::napi::local_of<class_tag_of<Type>>;
37
38
  if (reference) {
38
- return value_type::from(reference.get(self));
39
+ return value_type::from(reference.get(lock));
39
40
  } else {
40
- auto template_value = value_type::make(self, class_template);
41
- reference.reset(self, template_value);
41
+ auto template_value = value_type::make(lock, class_template);
42
+ reference.reset(lock, template_value);
42
43
  return template_value;
43
44
  }
44
45
  }
@@ -10,6 +10,7 @@ environment::environment(napi_env env) :
10
10
  env_{env} {
11
11
  // Initialize napi_js platform hooks
12
12
  initialize_host_environment(env);
13
+ isolate_ = host_current_isolate();
13
14
 
14
15
  // nb: Closing the schedule needs to be the first thing we do, and deleting it needs to be the
15
16
  // last thing we do. Otherwise we get deadlocks or use-after-free problems.
@@ -1,14 +1,14 @@
1
1
  export module napi_js:environment;
2
2
  export import :environment_fwd;
3
3
  import :api;
4
- import :utility;
5
4
  import std;
6
5
  import util;
6
+ import v8;
7
7
 
8
8
  namespace js::napi {
9
9
 
10
- // A reference to an environment is used as the lock witness. Generally, you should not have an
11
- // `environment&` unless you're in the napi thread and locked.
10
+ // Per-instance environment state. The lifetime of instances of this class is managed by the
11
+ // runtime.
12
12
  export class environment : util::non_moveable, public napi_schedulable {
13
13
  public:
14
14
  explicit environment(napi_env env);
@@ -16,6 +16,10 @@ export class environment : util::non_moveable, public napi_schedulable {
16
16
  // NOLINTNEXTLINE(google-explicit-constructor)
17
17
  [[nodiscard]] operator napi_env() const { return env_; }
18
18
 
19
+ // In the fast (nodejs) case this is the isolate which backs the environment. Otherwise it is
20
+ // `nullptr`.
21
+ [[nodiscard]] auto isolate() const -> v8::Isolate* { return isolate_; }
22
+
19
23
  template <class Type>
20
24
  static auto make_and_set_environment(napi_env env, auto&&... args) -> Type&
21
25
  requires std::constructible_from<Type, napi_env, decltype(args)...> {
@@ -33,6 +37,7 @@ export class environment : util::non_moveable, public napi_schedulable {
33
37
 
34
38
  private:
35
39
  napi_env env_;
40
+ v8::Isolate* isolate_{};
36
41
  napi_async_cleanup_hook_handle cleanup_hook_handle_{};
37
42
  };
38
43
 
@@ -3,12 +3,14 @@ import std;
3
3
 
4
4
  namespace js::napi {
5
5
 
6
- // A reference to an environment is used as the lock witness. Generally, you should not have an
7
- // `environment&` unless you're in the napi thread and locked.
6
+ // Per-instance environment state.
8
7
  export class environment;
9
8
 
10
9
  // Environment constraint
11
10
  export template <class Type>
12
11
  concept auto_environment = std::derived_from<Type, environment>;
13
12
 
13
+ // Environment lock witness, defined in `:lock`
14
+ export class environment_lock_witness;
15
+
14
16
  } // namespace js::napi
@@ -1,18 +1,24 @@
1
1
  export module napi_js:error_scope;
2
2
  import :accept;
3
+ import :lock;
3
4
 
4
5
  namespace js::napi {
5
6
 
6
- // Catch `napi::pending_error` (thrown by `napi::invoke`) or `js::error`, converting to thrown
7
- // runtime error.
8
- [[nodiscard]] auto invoke_internal_error_scope(environment& env, auto implementation) -> napi_value {
7
+ // Catch `napi::pending_error` (thrown by `napi::invoke`), `napi::pending_v8_error` (thrown by
8
+ // `napi::unmaybe`), or `js::error`, converting to thrown runtime error.
9
+ export template <class Environment>
10
+ [[nodiscard]] auto invoke_internal_error_scope(const environment_lock_witness_of<Environment>& lock, auto implementation) -> napi_value {
11
+ auto try_catch = environment_try_catch{lock};
9
12
  try {
10
13
  return implementation();
11
14
  } catch (const napi::pending_error& /*error*/) {
12
15
  return napi_value{};
16
+ } catch (const napi::pending_v8_error& /*error*/) {
17
+ std::ignore = napi::invoke0_maybe(napi_throw, napi_env{lock}, try_catch.take_exception());
18
+ return napi_value{};
13
19
  } catch (const js::error& error) {
14
- auto* exception = js::transfer_in_strict<napi_value>(error, env);
15
- napi::invoke0(napi_throw, napi_env{env}, exception);
20
+ auto* exception = js::transfer_in_strict<napi_value>(error, lock);
21
+ napi::invoke0(napi_throw, napi_env{lock}, exception);
16
22
  return napi_value{};
17
23
  }
18
24
  };
package/support/host.cc CHANGED
@@ -43,6 +43,30 @@ auto napi_fast_is_undefined(napi_env env, napi_value value) -> bool {
43
43
  return value == napi::invoke(napi_get_undefined, env);
44
44
  }
45
45
 
46
+ // Isolate access
47
+ auto v8_current_isolate() -> v8::Isolate* {
48
+ return v8::Isolate::GetCurrent();
49
+ }
50
+
51
+ constexpr auto unknown_current_isolate = [] -> v8::Isolate* { return nullptr; };
52
+
53
+ // Element access
54
+ auto v8_has_element(environment_lock_witness lock, napi_value array, std::uint32_t index) -> bool {
55
+ return napi::unmaybe(std::bit_cast<v8::Local<v8::Object>>(array)->Has(lock.context(), index));
56
+ }
57
+
58
+ auto v8_get_element(environment_lock_witness lock, napi_value array, std::uint32_t index) -> napi_value {
59
+ return std::bit_cast<napi_value>(napi::unmaybe(std::bit_cast<v8::Local<v8::Object>>(array)->Get(lock.context(), index)));
60
+ }
61
+
62
+ auto napi_fast_has_element(environment_lock_witness lock, napi_value array, std::uint32_t index) -> bool {
63
+ return napi::invoke(napi_has_element, napi_env{lock}, array, index);
64
+ }
65
+
66
+ auto napi_fast_get_element(environment_lock_witness lock, napi_value array, std::uint32_t index) -> napi_value {
67
+ return napi::invoke(napi_get_element, napi_env{lock}, array, index);
68
+ }
69
+
46
70
  // Extended fast functions
47
71
  // nb: Bun exports these v8 functions, but they crash with the `std::bit_cast<v8::Local<T>>` trick.
48
72
  auto fast_is_array(napi_value value) -> bool {
@@ -121,6 +145,13 @@ auto bun_maybe_is_shared_array_buffer(napi_env env, local_of<object_tag> value)
121
145
 
122
146
  constexpr auto unknown_maybe_is = [](auto...) -> std::optional<bool> { return std::nullopt; };
123
147
 
148
+ // 'ArrayBuffer' functions
149
+ auto v8_array_buffer_get_backing_store(local_of<array_buffer_tag> buffer) -> std::shared_ptr<data_block::array_type> {
150
+ auto backing_store = std::bit_cast<v8::Local<v8::ArrayBuffer>>(buffer)->GetBackingStore();
151
+ auto* data = reinterpret_cast<std::byte*>(backing_store->Data());
152
+ return std::shared_ptr<data_block::array_type>{std::move(backing_store), data};
153
+ };
154
+
124
155
  // 'SharedArrayBuffer' functions
125
156
  auto v8_make_shared_array_buffer(js::shared_array_buffer::shared_pointer_type data, std::size_t byte_length) -> local_of<shared_array_buffer_tag> {
126
157
  auto backing_store = [ & ] -> auto {
@@ -150,11 +181,10 @@ auto v8_shared_array_buffer_get_byte_length(local_of<shared_array_buffer_tag> bu
150
181
  return std::bit_cast<v8::Local<v8::SharedArrayBuffer>>(napi_value{buffer})->ByteLength();
151
182
  };
152
183
 
153
- auto v8_shared_array_buffer_get_backing_store(local_of<shared_array_buffer_tag> buffer) -> std::shared_ptr<std::byte[]> {
184
+ auto v8_shared_array_buffer_get_backing_store(local_of<shared_array_buffer_tag> buffer) -> std::shared_ptr<data_block::array_type> {
154
185
  auto backing_store = std::bit_cast<v8::Local<v8::SharedArrayBuffer>>(buffer)->GetBackingStore();
155
186
  auto* data = reinterpret_cast<std::byte*>(backing_store->Data());
156
- // NOLINTNEXTLINE(modernize-avoid-c-arrays)
157
- return std::shared_ptr<std::byte[]>{std::move(backing_store), data};
187
+ return std::shared_ptr<data_block::array_type>{std::move(backing_store), data};
158
188
  };
159
189
 
160
190
  // 'ArrayBufferView' constructors
@@ -272,6 +302,9 @@ auto initialize_host_environment(napi_env env) -> void {
272
302
  auto is_nodejs = !is_bun && !is_deno;
273
303
  if (is_nodejs) {
274
304
  has_extended_fast_is_functions = true;
305
+ host_current_isolate = v8_current_isolate;
306
+ fast_get_element = v8_get_element;
307
+ fast_has_element = v8_has_element;
275
308
  fast_is_false = v8_is_false;
276
309
  fast_is_null = v8_is_null;
277
310
  fast_is_true = v8_is_true;
@@ -279,6 +312,7 @@ auto initialize_host_environment(napi_env env) -> void {
279
312
  host_uv_dlclose = node_uv_dlclose;
280
313
  host_uv_dlerror = node_uv_dlerror;
281
314
  host_uv_dlopen = node_uv_dlopen;
315
+ make_sab_data_view = v8_make_sab_data_view;
282
316
  make_sab_typed_array_of<double> = v8_make_sab_typed_array_of<double>;
283
317
  make_sab_typed_array_of<float> = v8_make_sab_typed_array_of<float>;
284
318
  make_sab_typed_array_of<js::float16_t> = v8_make_sab_typed_array_of<js::float16_t>;
@@ -291,6 +325,7 @@ auto initialize_host_environment(napi_env env) -> void {
291
325
  make_sab_typed_array_of<std::uint32_t> = v8_make_sab_typed_array_of<std::uint32_t>;
292
326
  make_sab_typed_array_of<std::uint64_t> = v8_make_sab_typed_array_of<std::uint64_t>;
293
327
  make_sab_typed_array_of<std::uint8_t> = v8_make_sab_typed_array_of<std::uint8_t>;
328
+ array_buffer_get_backing_store = v8_array_buffer_get_backing_store;
294
329
  make_shared_array_buffer = v8_make_shared_array_buffer;
295
330
  maybe_is_number_int32 = v8_is_number_int32;
296
331
  maybe_is_shared_array_buffer = node_api_maybe_is_shared_array_buffer;
@@ -299,6 +334,9 @@ auto initialize_host_environment(napi_env env) -> void {
299
334
  shared_array_buffer_get_byte_length = v8_shared_array_buffer_get_byte_length;
300
335
  } else {
301
336
  has_extended_fast_is_functions = false;
337
+ host_current_isolate = unknown_current_isolate;
338
+ fast_get_element = napi_fast_get_element;
339
+ fast_has_element = napi_fast_has_element;
302
340
  fast_is_false = napi_fast_is_false;
303
341
  fast_is_null = napi_fast_is_null;
304
342
  fast_is_true = napi_fast_is_true;
@@ -319,12 +357,12 @@ auto initialize_host_environment(napi_env env) -> void {
319
357
  make_sab_typed_array_of<std::uint32_t> = unknown_sab_throw<local_of<typed_array_tag_of<std::uint32_t>>>;
320
358
  make_sab_typed_array_of<std::uint64_t> = unknown_sab_throw<local_of<typed_array_tag_of<std::uint64_t>>>;
321
359
  make_sab_typed_array_of<std::uint8_t> = unknown_sab_throw<local_of<typed_array_tag_of<std::uint8_t>>>;
360
+ array_buffer_get_backing_store = nullptr;
322
361
  make_shared_array_buffer = unknown_sab_throw<local_of<shared_array_buffer_tag>>;
323
362
  maybe_is_number_int32 = unknown_maybe_is;
324
363
  maybe_is_shared_array_buffer = unknown_maybe_is;
325
364
  maybe_is_string_latin1 = unknown_maybe_is;
326
- // NOLINTNEXTLINE(modernize-avoid-c-arrays)
327
- shared_array_buffer_get_backing_store = unknown_sab_throw<std::shared_ptr<std::byte[]>>;
365
+ shared_array_buffer_get_backing_store = unknown_sab_throw<std::shared_ptr<data_block::array_type>>;
328
366
  shared_array_buffer_get_byte_length = unknown_sab_throw<std::size_t>;
329
367
  }
330
368
 
package/support/host.h.cc CHANGED
@@ -1,6 +1,8 @@
1
1
  export module napi_js:support.host;
2
+ import :environment_fwd;
2
3
  import :handle.local_of;
3
4
  import std;
5
+ import v8;
4
6
 
5
7
  namespace js::napi {
6
8
 
@@ -13,6 +15,13 @@ auto (*fast_is_null)(napi_env env, napi_value value) -> bool;
13
15
  auto (*fast_is_true)(napi_env env, napi_value value) -> bool;
14
16
  auto (*fast_is_undefined)(napi_env env, napi_value value) -> bool;
15
17
 
18
+ // Isolate access. In the fast case (nodejs) this returns the isolate of the current thread.
19
+ auto (*host_current_isolate)() -> v8::Isolate* = nullptr;
20
+
21
+ // Element access
22
+ auto (*fast_has_element)(environment_lock_witness lock, napi_value array, std::uint32_t index) -> bool = nullptr;
23
+ auto (*fast_get_element)(environment_lock_witness lock, napi_value array, std::uint32_t index) -> napi_value = nullptr;
24
+
16
25
  // Extended fast functions
17
26
  auto has_extended_fast_is_functions = false;
18
27
  auto fast_is_array(napi_value value) -> bool;
@@ -32,11 +41,13 @@ auto (*maybe_is_number_int32)(local_of<number_tag> value) -> std::optional<bool>
32
41
  auto (*maybe_is_string_latin1)(local_of<string_tag> value) -> std::optional<bool> = nullptr;
33
42
  auto (*maybe_is_shared_array_buffer)(napi_env env, local_of<object_tag> value) -> std::optional<bool> = nullptr;
34
43
 
44
+ // 'ArrayBuffer' functions
45
+ auto (*array_buffer_get_backing_store)(local_of<array_buffer_tag> buffer) -> std::shared_ptr<data_block::array_type> = nullptr;
46
+
35
47
  // 'SharedArrayBuffer' functions
36
48
  auto (*make_shared_array_buffer)(js::shared_array_buffer::shared_pointer_type data, std::size_t byte_length) -> local_of<shared_array_buffer_tag> = nullptr;
37
49
  auto (*shared_array_buffer_get_byte_length)(local_of<shared_array_buffer_tag> buffer) -> std::size_t = nullptr;
38
- // NOLINTNEXTLINE(modernize-avoid-c-arrays)
39
- auto (*shared_array_buffer_get_backing_store)(local_of<shared_array_buffer_tag> buffer) -> std::shared_ptr<std::byte[]> = nullptr;
50
+ auto (*shared_array_buffer_get_backing_store)(local_of<shared_array_buffer_tag> buffer) -> std::shared_ptr<data_block::array_type> = nullptr;
40
51
 
41
52
  // 'ArrayBufferView' constructors
42
53
  auto (*make_sab_data_view)(local_of<shared_array_buffer_tag> buffer, std::size_t byte_offset, std::size_t length) -> local_of<data_view_tag> = nullptr;
@@ -1,4 +1,5 @@
1
1
  export module napi_js:initialize;
2
+ import :lock;
2
3
  import :value;
3
4
  import nodejs;
4
5
  import std;
@@ -33,9 +34,10 @@ class napi_js_module final : private detail::initialize_require {
33
34
  // construct environment, set napi instance data w/ finalizer
34
35
  constexpr auto [... indices ] = util::sequence<sizeof...(Args)>;
35
36
  auto& client_environment = environment::make_and_set_environment<Environment>(env, std::get<indices>(std::move(args_))...);
37
+ auto lock = environment_lock_witness_of<Environment>{environment_lock_witness::make_witness(client_environment), client_environment};
36
38
  // assign export descriptor to napi-constructor namespace object
37
39
  auto exports_local = local_of<dictionary_tag>::from(exports);
38
- exports_local->assign(client_environment, std::move(make_exports_)(client_environment));
40
+ exports_local->assign(lock, std::move(make_exports_)(lock));
39
41
  }
40
42
 
41
43
  Make make_exports_;
@@ -0,0 +1,40 @@
1
+ module;
2
+ #include <cassert>
3
+ module napi_js;
4
+ import std;
5
+ import v8;
6
+
7
+ namespace js::napi {
8
+
9
+ // `environment_lock_witness`
10
+ auto environment_lock_witness::make_witness(napi::environment& env) -> environment_lock_witness {
11
+ auto* isolate = env.isolate();
12
+ if (isolate == nullptr) {
13
+ return environment_lock_witness{env, nullptr, {}};
14
+ } else {
15
+ return environment_lock_witness{env, isolate, isolate->GetCurrentContext()};
16
+ }
17
+ }
18
+
19
+ // `environment_try_catch`
20
+ environment_try_catch::environment_try_catch(environment_lock_witness lock) {
21
+ if (auto* isolate = lock.isolate(); isolate != nullptr) {
22
+ try_catch_.emplace(isolate);
23
+ }
24
+ }
25
+
26
+ environment_try_catch::~environment_try_catch() {
27
+ assert(!try_catch_ || !try_catch_->HasCaught());
28
+ }
29
+
30
+ auto environment_try_catch::take_exception() -> napi_value {
31
+ if (try_catch_ && try_catch_->HasCaught()) {
32
+ auto exception = try_catch_->Exception();
33
+ try_catch_->Reset();
34
+ return std::bit_cast<napi_value>(exception);
35
+ } else {
36
+ throw std::logic_error{"no pending v8 exception"};
37
+ }
38
+ }
39
+
40
+ } // namespace js::napi
@@ -0,0 +1,64 @@
1
+ export module napi_js:lock;
2
+ import :environment;
3
+ import nodejs;
4
+ import std;
5
+ import util;
6
+ import v8;
7
+
8
+ namespace js::napi {
9
+
10
+ // Environment lock witness. Similar to `iv8::isolate_lock_witness`, this provides some assurance
11
+ // that the environment is locked in this thread. In the "fast" (nodejs) case it also carries the
12
+ // isolate and the current context.
13
+ export class environment_lock_witness {
14
+ protected:
15
+ environment_lock_witness(napi_env env, v8::Isolate* isolate, v8::Local<v8::Context> context) :
16
+ env_{env},
17
+ isolate_{isolate},
18
+ context_{context} {}
19
+
20
+ public:
21
+ // NOLINTNEXTLINE(google-explicit-constructor)
22
+ operator napi_env() const { return env_; }
23
+ [[nodiscard]] auto isolate() const -> v8::Isolate* { return isolate_; }
24
+ [[nodiscard]] auto context() const -> v8::Local<v8::Context> { return context_; }
25
+ [[nodiscard]] static auto make_witness(napi_env env, v8::Isolate* isolate, v8::Local<v8::Context> context) -> environment_lock_witness {
26
+ return environment_lock_witness{env, isolate, context};
27
+ }
28
+ [[nodiscard]] static auto make_witness(napi::environment& env) -> environment_lock_witness;
29
+
30
+ private:
31
+ napi_env env_;
32
+ v8::Isolate* isolate_;
33
+ v8::Local<v8::Context> context_;
34
+ };
35
+
36
+ // Environment lock witness with backing environment reference
37
+ export template <class Environment>
38
+ class environment_lock_witness_of
39
+ : public util::pointer_facade,
40
+ public environment_lock_witness {
41
+ public:
42
+ environment_lock_witness_of(environment_lock_witness witness, Environment& env) :
43
+ environment_lock_witness{witness},
44
+ env_{env} {}
45
+
46
+ [[nodiscard]] auto operator*() const -> Environment& { return env_; }
47
+
48
+ private:
49
+ std::reference_wrapper<Environment> env_;
50
+ };
51
+
52
+ // In the fast case this installs a `v8::TryCatch` which catches exceptions thrown by direct v8
53
+ // operations.
54
+ export class environment_try_catch : util::non_moveable {
55
+ public:
56
+ explicit environment_try_catch(environment_lock_witness lock);
57
+ ~environment_try_catch();
58
+ [[nodiscard]] auto take_exception() -> napi_value;
59
+
60
+ private:
61
+ std::optional<v8::TryCatch> try_catch_;
62
+ };
63
+
64
+ } // namespace js::napi
@@ -1,4 +1,5 @@
1
1
  export module napi_js:promise;
2
+ import :lock;
2
3
  import :value;
3
4
  import std;
4
5
 
@@ -8,12 +9,14 @@ namespace js::napi {
8
9
  template <class Environment, class Dispatch>
9
10
  class resolver {
10
11
  public:
11
- resolver(Environment& env, napi_deferred deferred, Dispatch dispatch = {}) :
12
- env_{env},
12
+ using lock_type = environment_lock_witness_of<Environment>;
13
+
14
+ resolver(const lock_type& lock, napi_deferred deferred, Dispatch dispatch = {}) :
15
+ env_{lock},
13
16
  deferred_{deferred},
14
- scheduler_{env.scheduler()},
17
+ scheduler_{lock->scheduler()},
15
18
  dispatch_{dispatch} {
16
- scheduler_.increment_ref(napi_env{env});
19
+ scheduler_.increment_ref(napi_env{lock});
17
20
  }
18
21
  resolver(const resolver&) = delete;
19
22
  resolver(resolver&&) = default;
@@ -28,12 +31,12 @@ class resolver {
28
31
 
29
32
  // Fulfill using callback supplied to constructor
30
33
  auto operator()(auto&&... args) -> void
31
- requires std::invocable<Dispatch&, Environment&, decltype(args)...> {
32
- using result_type = std::invoke_result_t<Dispatch&, Environment&, decltype(args)...>;
34
+ requires std::invocable<Dispatch&, const lock_type&, decltype(args)...> {
35
+ using result_type = std::invoke_result_t<Dispatch&, const lock_type&, decltype(args)...>;
33
36
  using expected_type = std::expected<result_type, js::error_value>;
34
37
  schedule(
35
- [](Environment& environment, auto dispatch, auto&&... args) -> expected_type {
36
- return expected_type{std::in_place, std::move(dispatch)(environment, std::forward<decltype(args)>(args)...)};
38
+ [](const lock_type& lock, auto dispatch, auto&&... args) -> expected_type {
39
+ return expected_type{std::in_place, std::move(dispatch)(lock, std::forward<decltype(args)>(args)...)};
37
40
  },
38
41
  std::move(dispatch_),
39
42
  std::forward<decltype(args)>(args)...
@@ -46,7 +49,7 @@ class resolver {
46
49
  auto resolve(Type value) noexcept(std::is_nothrow_move_constructible_v<Type>) -> void {
47
50
  using expected_type = std::expected<decltype(value), js::error_value>;
48
51
  schedule(
49
- [](Environment& /*env*/, auto value) -> expected_type {
52
+ [](const lock_type& /*lock*/, auto value) -> expected_type {
50
53
  return expected_type{std::in_place, std::move(value)};
51
54
  },
52
55
  std::move(value)
@@ -57,7 +60,7 @@ class resolver {
57
60
  auto reject(js::error_value error) noexcept -> void {
58
61
  using expected_type = std::expected<std::monostate, js::error_value>;
59
62
  schedule(
60
- [](Environment& /*env*/, js::error_value error) -> expected_type {
63
+ [](const lock_type& /*lock*/, js::error_value error) -> expected_type {
61
64
  return expected_type{std::unexpect, std::move(error)};
62
65
  },
63
66
  std::move(error)
@@ -72,20 +75,24 @@ class resolver {
72
75
  [](napi_env env, napi_value /*nothing*/, napi_deferred deferred, auto operation, auto&&... args) noexcept {
73
76
  auto& environment = napi::environment::unsafe_get_environment_as<Environment>(env);
74
77
  environment.scheduler().decrement_ref(env);
78
+ auto lock = lock_type{environment_lock_witness::make_witness(environment), environment};
79
+ auto try_catch = environment_try_catch{lock};
75
80
  try {
76
- auto expected = operation(environment, std::forward<decltype(args)>(args)...);
81
+ auto expected = operation(lock, std::forward<decltype(args)>(args)...);
77
82
  if (expected) {
78
- auto value = js::transfer_in_strict<napi_value>(*std::move(expected), environment);
83
+ auto value = js::transfer_in_strict<napi_value>(*std::move(expected), lock);
79
84
  napi::invoke0(napi_resolve_deferred, env, deferred, value);
80
85
  } else {
81
- auto error = js::transfer_in_strict<napi_value>(std::move(expected).error(), environment);
86
+ auto error = js::transfer_in_strict<napi_value>(std::move(expected).error(), lock);
82
87
  napi::invoke0(napi_reject_deferred, env, deferred, error);
83
88
  }
84
89
  } catch (const napi::pending_error& /*error*/) {
85
90
  auto* exception = napi::invoke(napi_get_and_clear_last_exception, env);
86
91
  napi::invoke0_noexcept(napi_reject_deferred, env, deferred, exception);
92
+ } catch (const napi::pending_v8_error& /*error*/) {
93
+ napi::invoke0_noexcept(napi_reject_deferred, env, deferred, try_catch.take_exception());
87
94
  } catch (const js::error& error) {
88
- auto exception = js::transfer_in_strict<napi_value>(error, environment);
95
+ auto exception = js::transfer_in_strict<napi_value>(error, lock);
89
96
  napi::invoke0_noexcept(napi_reject_deferred, env, deferred, exception);
90
97
  }
91
98
  },
@@ -102,19 +109,22 @@ class resolver {
102
109
  };
103
110
 
104
111
  template <class Environment>
105
- resolver(Environment&, napi_deferred) -> resolver<Environment, std::monostate>;
112
+ resolver(const environment_lock_witness_of<Environment>&, napi_deferred) -> resolver<Environment, std::monostate>;
113
+
114
+ template <class Environment, class Dispatch>
115
+ resolver(const environment_lock_witness_of<Environment>&, napi_deferred, Dispatch) -> resolver<Environment, Dispatch>;
106
116
 
107
117
  export template <class Environment>
108
- auto make_promise(Environment& env, auto... dispatch) {
118
+ auto make_promise(const environment_lock_witness_of<Environment>& lock, auto... dispatch) {
109
119
  // Make nodejs promise & future
110
120
  // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
111
121
  napi_deferred deferred;
112
122
  // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
113
123
  napi_value promise;
114
- napi::invoke0(napi_create_promise, napi_env{env}, &deferred, &promise);
124
+ napi::invoke0(napi_create_promise, napi_env{lock}, &deferred, &promise);
115
125
 
116
126
  // Make resolve helper
117
- auto resolve = resolver{env, deferred, std::move(dispatch)...};
127
+ auto resolve = resolver{lock, deferred, std::move(dispatch)...};
118
128
 
119
129
  // `[ promise, resolver ]`
120
130
  return std::tuple{local_of<promise_tag>::from(promise), std::move(resolve)};
@@ -84,7 +84,7 @@ auto accept_basic_napi_value::operator()(date_tag /*tag*/, visit_holder /*visit*
84
84
  // error
85
85
  auto accept_basic_napi_value::operator()(error_tag /*tag*/, visit_holder visit, const js::error_value& subject) const
86
86
  -> js::referenceable_value<local_of<error_tag>> {
87
- auto* message = napi_value{js::transfer_in<local_of<string_tag>>(subject.message(), environment())};
87
+ auto* message = napi_value{js::transfer_in<local_of<string_tag>>(subject.message(), lock())};
88
88
  auto* error = [ & ] -> napi_value {
89
89
  switch (subject.name()) {
90
90
  default:
@@ -1,4 +1,5 @@
1
1
  export module napi_js:accept;
2
+ import :lock;
2
3
  import :utility;
3
4
  import :value;
4
5
  import std;
@@ -23,7 +24,7 @@ struct reaccept_napi_value {
23
24
  // Napi acceptor which does not need specialized environment type
24
25
  struct accept_basic_napi_value {
25
26
  public:
26
- explicit accept_basic_napi_value(auto* /*transfer*/, auto& env) : env_{env} {}
27
+ explicit accept_basic_napi_value(auto* /*transfer*/, environment_lock_witness lock) : lock_{lock} {}
27
28
 
28
29
  // Declare reference provider
29
30
  using accept_reference_type = reaccept_napi_value;
@@ -140,35 +141,36 @@ struct accept_basic_napi_value {
140
141
  // TODO: We accept the nested `buffer` property as a `data_block` which means `make` needs to
141
142
  // invoke `is_arraybuffer` on it again even though we knew what it was a moment ago.
142
143
  auto buffer = local_of<data_block_tag>::from(visit(std::forward<decltype(subject)>(subject).buffer(), self));
143
- return js::referenceable_value{local_of<Tag>::make(self.environment(), buffer, byte_offset, length)};
144
+ return js::referenceable_value{local_of<Tag>::make(self.lock(), buffer, byte_offset, length)};
144
145
  }
145
146
 
146
147
  // extras
147
- [[nodiscard]] auto environment() const -> environment& { return env_; }
148
- explicit operator napi_env() const { return napi_env{env_.get()}; }
148
+ [[nodiscard]] auto lock() const -> environment_lock_witness { return lock_; }
149
+ explicit operator napi_env() const { return napi_env{lock_}; }
149
150
 
150
151
  private:
151
- std::reference_wrapper<napi::environment> env_;
152
+ environment_lock_witness lock_;
152
153
  };
153
154
 
154
155
  // Generic napi acceptor which accepts all value types.
155
- template <class Environment>
156
+ template <class Lock>
156
157
  struct accept_napi_value;
157
158
 
158
159
  template <class Meta>
159
160
  using accept_napi_value_with = accept_napi_value<typename Meta::accept_context_type>;
160
161
 
161
- template <class Environment>
162
+ template <class Lock>
162
163
  struct accept_napi_value : accept_basic_napi_value {
163
164
  public:
164
- explicit accept_napi_value(auto* transfer, auto& env) :
165
- accept_basic_napi_value{transfer, env} {}
165
+ explicit accept_napi_value(auto* transfer, const Lock& lock) :
166
+ accept_basic_napi_value{transfer, lock},
167
+ lock_{lock} {}
166
168
  using accept_basic_napi_value::operator();
167
169
 
168
170
  // function
169
171
  template <class Callback>
170
172
  auto operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, js::free_function<Callback> subject) const -> local_of<function_tag> {
171
- return local_of<function_tag>::make(environment(), std::forward<decltype(subject)>(subject));
173
+ return local_of<function_tag>::make(lock(), std::forward<decltype(subject)>(subject));
172
174
  }
173
175
 
174
176
  // vectors
@@ -298,11 +300,12 @@ struct accept_napi_value : accept_basic_napi_value {
298
300
  }
299
301
 
300
302
  // extras
301
- [[nodiscard]] auto environment() const -> Environment& {
302
- return static_cast<Environment&>(accept_basic_napi_value::environment());
303
- }
303
+ [[nodiscard]] auto lock() const -> const Lock& { return lock_; }
304
304
 
305
305
  consteval static auto types(auto /*recursive*/) { return util::type_pack{}; }
306
+
307
+ private:
308
+ Lock lock_;
306
309
  };
307
310
 
308
311
  // Forward `value_of<T>`
@@ -329,8 +332,8 @@ struct object_assign_delegate {
329
332
  local_of<object_tag> object_;
330
333
  };
331
334
 
332
- auto local_for_object::assign(auto_environment auto& env, auto source) const -> void {
333
- js::transfer_in<object_assign_delegate>(std::move(source), env, object_assign_delegate{local_of{*this}});
335
+ auto local_for_object::assign(const auto& lock, auto source) const -> void {
336
+ js::transfer_in<object_assign_delegate>(std::move(source), lock, object_assign_delegate{local_of{*this}});
334
337
  }
335
338
 
336
339
  } // namespace js::napi
@@ -349,7 +352,7 @@ struct accept<Meta, napi_value> : napi::accept_napi_value_with<Meta> {
349
352
 
350
353
  // Tagged `local_of<T>` acceptor
351
354
  template <>
352
- struct accept_property_subject<napi::local_of<value_tag>> : std::type_identity<napi_value> {};
355
+ struct accept_property_subject<napi::local_of<>> : std::type_identity<napi_value> {};
353
356
 
354
357
  template <class Meta, class Tag>
355
358
  struct accept<Meta, napi::local_of<Tag>> : napi::accept_napi_value_with<Meta> {
@@ -387,8 +390,8 @@ struct accept_property_value<Meta, Key, Type, napi_value> {
387
390
  template <class Meta>
388
391
  struct accept<Meta, napi::object_assign_delegate> {
389
392
  public:
390
- accept(auto* transfer, auto& env, napi::object_assign_delegate object) :
391
- accept_{transfer, env},
393
+ accept(auto* transfer, const auto& lock, napi::object_assign_delegate object) :
394
+ accept_{transfer, lock},
392
395
  object_{object} {}
393
396
 
394
397
  template <std::size_t Size>