@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.
package/CMakeLists.txt CHANGED
@@ -20,6 +20,7 @@ target_sources(${napi_js}
20
20
  support/environment.cc
21
21
  support/host.cc
22
22
  support/initialize.cc
23
+ support/lock.cc
23
24
  transfer/accept.cc
24
25
  value/array_buffer.cc
25
26
  value/array.cc
@@ -35,6 +36,7 @@ target_sources(${napi_js}
35
36
  api/invoke.cc
36
37
  api/napi_scheduler.h.cc
37
38
  api/threadsafe_function.cc
39
+ api/unmaybe.cc
38
40
  api/uv_dlib.h.cc
39
41
  api/uv_handle.cc
40
42
  api/uv_scheduler.h.cc
@@ -51,10 +53,12 @@ target_sources(${napi_js}
51
53
  support/error_scope.cc
52
54
  support/host.h.cc
53
55
  support/initialize.h.cc
56
+ support/lock.h.cc
54
57
  support/promise.cc
55
58
  support/string_table.cc
56
59
  support/utility.cc
57
60
  transfer/accept.h.cc
61
+ transfer/transfer_list.cc
58
62
  transfer/visit.cc
59
63
  value/array_buffer.h.cc
60
64
  value/array.h.cc
package/_module.cc CHANGED
@@ -11,11 +11,13 @@ export import :handle.local_of;
11
11
  export import :handle.types;
12
12
  export import :handle.value_of;
13
13
  export import :initialize;
14
+ export import :lock;
14
15
  export import :promise;
15
16
  export import :reference;
16
17
  export import :remote;
17
18
  export import :string_table;
18
19
  export import :support.host;
20
+ export import :transfer_list;
19
21
  export import :utility;
20
22
  export import :value;
21
23
  export import :visit;
package/api/api.cc CHANGED
@@ -8,6 +8,7 @@ export import :api.handle_scope;
8
8
  export import :api.invoke;
9
9
  export import :api.napi_scheduler;
10
10
  export import :api.threadsafe_function;
11
+ export import :api.unmaybe;
11
12
  export import :api.uv_dlib;
12
13
  export import :api.uv_handle;
13
14
  export import :api.uv_scheduler;
@@ -35,6 +35,6 @@ export struct callback_info : util::non_copyable {
35
35
 
36
36
  // Common declaration for `napi_type_tag` by type
37
37
  export template <class Type>
38
- constexpr auto type_tag_for = napi_type_tag{.lower = util::type_hash<Type>, .upper = 0};
38
+ constexpr auto type_tag_for = napi_type_tag{.lower = util::type_hash64<Type>, .upper = 0};
39
39
 
40
40
  } // namespace js::napi
@@ -32,8 +32,29 @@ auto napi_scheduler::increment_ref(node_api_basic_env env) const -> void {
32
32
  }
33
33
  }
34
34
 
35
+ auto napi_scheduler::make_ref(node_api_basic_env env) const -> ref {
36
+ return ref{env, *this};
37
+ }
38
+
35
39
  auto napi_scheduler::storage::operator()(napi_env env, napi_value value, task_type& task) noexcept -> void {
36
40
  task(env, value);
37
41
  }
38
42
 
43
+ // napi_scheduler::ref
44
+ napi_scheduler::ref::ref(node_api_basic_env env, napi_scheduler scheduler) :
45
+ scheduler_{std::move(scheduler)} {
46
+ scheduler_.increment_ref(env);
47
+ }
48
+
49
+ napi_scheduler::ref::~ref() {
50
+ if (scheduler_) {
51
+ scheduler_(
52
+ [](napi_env env, napi_value /*nothing*/, const napi_scheduler& scheduler) noexcept -> void {
53
+ scheduler.decrement_ref(env);
54
+ },
55
+ scheduler_
56
+ );
57
+ }
58
+ }
59
+
39
60
  } // namespace js::napi
@@ -14,6 +14,8 @@ export class napi_scheduler {
14
14
  using threadsafe_function_type = threadsafe_function_of<storage, task_type>;
15
15
 
16
16
  public:
17
+ class ref;
18
+
17
19
  napi_scheduler() = default;
18
20
  explicit napi_scheduler(napi_env env);
19
21
  auto operator()(auto task, auto&&... args) const noexcept -> bool;
@@ -21,6 +23,7 @@ export class napi_scheduler {
21
23
  auto close(threadsafe_function_type::close_callback close) noexcept -> void;
22
24
  auto decrement_ref(node_api_basic_env env) const -> void;
23
25
  auto increment_ref(node_api_basic_env env) const -> void;
26
+ auto make_ref(node_api_basic_env env) const -> ref;
24
27
 
25
28
  private:
26
29
  struct storage {
@@ -32,6 +35,19 @@ export class napi_scheduler {
32
35
  threadsafe_function_of<storage, task_type> fn_;
33
36
  };
34
37
 
38
+ // Reference to a scheduler which keeps the napi event loop alive. Can be passed between threads,
39
+ // the decrement_ref operation is scheduled.
40
+ class napi_scheduler::ref {
41
+ public:
42
+ ref(node_api_basic_env env, napi_scheduler scheduler);
43
+ ref(ref&&) noexcept = default;
44
+ ~ref();
45
+ auto operator*() const -> const napi_scheduler& { return scheduler_; }
46
+
47
+ private:
48
+ napi_scheduler scheduler_;
49
+ };
50
+
35
51
  // An environment with an uv loop and scheduler
36
52
  export class napi_schedulable {
37
53
  public:
@@ -22,8 +22,8 @@ class threadsafe_function_of : util::non_copyable {
22
22
  auto operator=(threadsafe_function_of&&) -> threadsafe_function_of& = delete;
23
23
 
24
24
  auto close(close_callback callback) noexcept -> void;
25
- auto ref(node_api_basic_env env) const -> void;
26
- auto unref(node_api_basic_env env) const -> void;
25
+ auto ref(node_api_basic_env env) const noexcept -> void;
26
+ auto unref(node_api_basic_env env) const noexcept -> void;
27
27
  auto operator()(Message message) const noexcept -> bool;
28
28
  auto operator*() const noexcept -> Context& { return get(); }
29
29
  explicit operator bool() const noexcept { return tsfn_ != nullptr; }
@@ -103,13 +103,13 @@ auto threadsafe_function_of<Context, Message>::close(close_callback callback) no
103
103
  }
104
104
 
105
105
  template <class Context, class Message>
106
- auto threadsafe_function_of<Context, Message>::ref(node_api_basic_env env) const -> void {
107
- napi::invoke0(napi_ref_threadsafe_function, env, tsfn_);
106
+ auto threadsafe_function_of<Context, Message>::ref(node_api_basic_env env) const noexcept -> void {
107
+ napi::invoke0_noexcept(napi_ref_threadsafe_function, env, tsfn_);
108
108
  }
109
109
 
110
110
  template <class Context, class Message>
111
- auto threadsafe_function_of<Context, Message>::unref(node_api_basic_env env) const -> void {
112
- napi::invoke0(napi_unref_threadsafe_function, env, tsfn_);
111
+ auto threadsafe_function_of<Context, Message>::unref(node_api_basic_env env) const noexcept -> void {
112
+ napi::invoke0_noexcept(napi_unref_threadsafe_function, env, tsfn_);
113
113
  }
114
114
 
115
115
  template <class Context, class Message>
package/api/unmaybe.cc ADDED
@@ -0,0 +1,36 @@
1
+ export module napi_js:api.unmaybe;
2
+ import std;
3
+ import v8;
4
+
5
+ namespace js::napi {
6
+
7
+ // Thrown from `unmaybe` to indicate that a direct v8 operation failed and there is an exception
8
+ // waiting in the `v8::TryCatch` of the enclosing `environment_try_catch`.
9
+ export class pending_v8_error : public std::exception {
10
+ public:
11
+ [[nodiscard]] auto what() const noexcept -> const char* final { return "[pending v8 error]"; }
12
+ };
13
+
14
+ // Unwrap a `MaybeLocal`, or throw `napi::pending_v8_error` on failure
15
+ export template <class Type>
16
+ auto unmaybe(v8::MaybeLocal<Type> maybe_value) -> v8::Local<Type> {
17
+ v8::Local<Type> value;
18
+ if (maybe_value.ToLocal(&value)) {
19
+ return value;
20
+ } else {
21
+ throw napi::pending_v8_error{};
22
+ }
23
+ }
24
+
25
+ // Unwrap a `Maybe`, or throw `napi::pending_v8_error` on failure
26
+ export template <class Type>
27
+ auto unmaybe(v8::Maybe<Type> maybe) -> Type {
28
+ Type value;
29
+ if (maybe.To(&value)) {
30
+ return value;
31
+ } else {
32
+ throw napi::pending_v8_error{};
33
+ }
34
+ }
35
+
36
+ } // namespace js::napi
package/handle/remote.cc CHANGED
@@ -1,5 +1,6 @@
1
1
  export module napi_js:remote;
2
2
  import :environment;
3
+ import :lock;
3
4
  import :reference;
4
5
  import std;
5
6
  import util;
@@ -24,14 +25,16 @@ class remote : protected reference_handle {
24
25
  using unique_remote = std::unique_ptr<remote, util::function_constant<expire>>;
25
26
 
26
27
  remote() = default;
27
- remote(private_constructor /*private*/, const environment& env, local_of<Tag> value, napi_scheduler scheduler) :
28
- reference_handle{napi_env{env}, napi_value{value}},
28
+ remote(private_constructor /*private*/, environment_lock_witness lock, local_of<Tag> value, napi_scheduler scheduler) :
29
+ reference_handle{napi_env{lock}, napi_value{value}},
29
30
  scheduler_{std::move(scheduler)} {}
30
31
 
31
- auto deref(const environment& env) const -> local_of<Tag>;
32
+ auto deref(environment_lock_witness lock) const -> local_of<Tag>;
32
33
 
33
- static auto make_shared(remote_handle_environment auto& env, local_of<Tag> value) -> std::shared_ptr<remote>;
34
- static auto make_unique(remote_handle_environment auto& env, local_of<Tag> value) -> unique_remote;
34
+ template <remote_handle_environment Environment>
35
+ static auto make_shared(const environment_lock_witness_of<Environment>& lock, local_of<Tag> value) -> std::shared_ptr<remote>;
36
+ template <remote_handle_environment Environment>
37
+ static auto make_unique(const environment_lock_witness_of<Environment>& lock, local_of<Tag> value) -> unique_remote;
35
38
 
36
39
  private:
37
40
  napi_scheduler scheduler_;
@@ -44,14 +47,14 @@ using shared_remote = std::shared_ptr<remote<Type>>;
44
47
  export template <class Type>
45
48
  using unique_remote = remote<Type>::unique_remote;
46
49
 
47
- export template <class Tag>
48
- auto make_shared_remote(remote_handle_environment auto& env, local_of<Tag> value) -> shared_remote<Tag> {
49
- return remote<Tag>::make_shared(env, value);
50
+ export template <remote_handle_environment Environment, class Tag>
51
+ auto make_shared_remote(const environment_lock_witness_of<Environment>& lock, local_of<Tag> value) -> shared_remote<Tag> {
52
+ return remote<Tag>::make_shared(lock, value);
50
53
  }
51
54
 
52
- export template <class Tag>
53
- auto make_unique_remote(remote_handle_environment auto& env, local_of<Tag> value) -> unique_remote<Tag> {
54
- return remote<Tag>::make_unique(env, value);
55
+ export template <remote_handle_environment Environment, class Tag>
56
+ auto make_unique_remote(const environment_lock_witness_of<Environment>& lock, local_of<Tag> value) -> unique_remote<Tag> {
57
+ return remote<Tag>::make_unique(lock, value);
55
58
  }
56
59
 
57
60
  // ---
@@ -68,18 +71,20 @@ auto remote<Tag>::expire(remote* ptr) -> void {
68
71
  }
69
72
 
70
73
  template <class Tag>
71
- auto remote<Tag>::deref(const environment& env) const -> local_of<Tag> {
72
- return local_of<Tag>::from(get_value(napi_env{env}));
74
+ auto remote<Tag>::deref(environment_lock_witness lock) const -> local_of<Tag> {
75
+ return local_of<Tag>::from(get_value(napi_env{lock}));
73
76
  }
74
77
 
75
78
  template <class Tag>
76
- auto remote<Tag>::make_shared(remote_handle_environment auto& env, local_of<Tag> value) -> std::shared_ptr<remote> {
77
- return std::shared_ptr<remote>{new remote{private_constructor{}, env, value, env.scheduler()}, expire};
79
+ template <remote_handle_environment Environment>
80
+ auto remote<Tag>::make_shared(const environment_lock_witness_of<Environment>& lock, local_of<Tag> value) -> std::shared_ptr<remote> {
81
+ return std::shared_ptr<remote>{new remote{private_constructor{}, lock, value, lock->scheduler()}, expire};
78
82
  }
79
83
 
80
84
  template <class Tag>
81
- auto remote<Tag>::make_unique(remote_handle_environment auto& env, local_of<Tag> value) -> unique_remote {
82
- return unique_remote{new remote{private_constructor{}, env, value, env.scheduler()}};
85
+ template <remote_handle_environment Environment>
86
+ auto remote<Tag>::make_unique(const environment_lock_witness_of<Environment>& lock, local_of<Tag> value) -> unique_remote {
87
+ return unique_remote{new remote{private_constructor{}, lock, value, lock->scheduler()}};
83
88
  }
84
89
 
85
90
  } // namespace js::napi
package/handle/value.cc CHANGED
@@ -1,7 +1,9 @@
1
1
  export module napi_js:handle.value_of;
2
2
  import :handle.types;
3
+ import :lock;
3
4
  import nodejs;
4
5
  import std;
6
+ import v8;
5
7
 
6
8
  namespace js::napi {
7
9
 
@@ -33,7 +35,7 @@ class value_next : public value_of<typename Tag::tag_type> {
33
35
  };
34
36
 
35
37
  // Member & method implementation for value semantics objects. It holds the type-erased environment
36
- // and is used for common operations like casting & iteration.
38
+ // lock witness and is used for common operations like casting & iteration.
37
39
  template <class Tag>
38
40
  class value_of : public value_specialization<Tag>::type {
39
41
  public:
@@ -46,14 +48,19 @@ template <>
46
48
  class value_of<void> : public runtime_handle {
47
49
  protected:
48
50
  value_of() = default;
49
- value_of(napi_env env, napi_value value) :
51
+ value_of(environment_lock_witness lock, napi_value value) :
50
52
  runtime_handle{value},
51
- env_{env} {}
53
+ env_{lock},
54
+ isolate_{lock.isolate()},
55
+ context_{lock.context()} {}
52
56
 
53
57
  [[nodiscard]] auto env() const -> napi_env { return env_; }
58
+ [[nodiscard]] auto lock() const -> environment_lock_witness { return environment_lock_witness::make_witness(env_, isolate_, context_); }
54
59
 
55
60
  private:
56
61
  napi_env env_{};
62
+ v8::Isolate* isolate_{};
63
+ v8::Local<v8::Context> context_;
57
64
  };
58
65
 
59
66
  // Deduction guide
@@ -108,6 +115,10 @@ template <>
108
115
  struct value_specialization<record_tag>
109
116
  : std::type_identity<class value_for_record> {};
110
117
 
118
+ template <>
119
+ struct value_specialization<list_tag>
120
+ : std::type_identity<class value_for_list> {};
121
+
111
122
  template <>
112
123
  struct value_specialization<data_block_tag>
113
124
  : std::type_identity<class value_for_data_block> {};
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@auto_js/napi",
3
- "version": "0.0.11",
3
+ "version": "0.0.12",
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.11",
10
- "@auto_js/napi_exports": "^0.0.10",
11
- "@auto_js/v8_exports": "^0.0.8"
9
+ "@auto_js/js": "^0.0.12",
10
+ "@auto_js/napi_exports": "^0.0.11",
11
+ "@auto_js/v8_exports": "^0.0.9"
12
12
  },
13
13
  "repository": {
14
14
  "type": "git",
@@ -1,6 +1,7 @@
1
1
  export module napi_js:callback;
2
2
  export import :callback_storage;
3
3
  import :error_scope;
4
+ import :lock;
4
5
  import std;
5
6
  import util;
6
7
 
@@ -10,15 +11,15 @@ namespace js::napi {
10
11
  // and there is a napi exception pending. `nullptr` will not be returned.
11
12
  template <class Type>
12
13
  // NOLINTNEXTLINE(bugprone-exception-escape)
13
- constexpr auto unwrap_member_this = [](auto_environment auto& env, napi_value this_arg) noexcept -> std::optional<tagged_external<Type>> {
14
- return napi::invoke_maybe(napi_coerce_to_object, napi_env{env}, this_arg)
15
- .and_then([ &env ](napi_value coerced_this_arg) -> std::optional<tagged_external<Type>> {
16
- auto as_object = value_of{env, local_of<object_tag>::from(coerced_this_arg)};
14
+ constexpr auto unwrap_member_this = [](environment_lock_witness lock, napi_value this_arg) noexcept -> std::optional<tagged_external<Type>> {
15
+ return napi::invoke_maybe(napi_coerce_to_object, napi_env{lock}, this_arg)
16
+ .and_then([ & ](napi_value coerced_this_arg) -> std::optional<tagged_external<Type>> {
17
+ auto as_object = value_of{lock, local_of<object_tag>::from(coerced_this_arg)};
17
18
  // Technically `try_cast` can throw but it should only be in catastrophic cases.
18
19
  Type* unwrapped = as_object.try_cast(type<Type>);
19
20
  if (unwrapped == nullptr) {
20
21
  // nb: The failure here is "no tag" which means we need to set the napi exception
21
- napi::invoke0(napi_throw_type_error, napi_env{env}, nullptr, "Invalid object type");
22
+ napi::invoke0(napi_throw_type_error, napi_env{lock}, nullptr, "Invalid object type");
22
23
  return std::nullopt;
23
24
  } else {
24
25
  return tagged_external{*unwrapped};
@@ -32,6 +33,7 @@ using internal_constructor = util::function_ref<auto(napi_value)->napi_value>;
32
33
  // Make callback for plain free function
33
34
  template <auto_environment Environment>
34
35
  constexpr auto make_free_function(auto function) {
36
+ using lock_type = environment_lock_witness_of<Environment>;
35
37
  constexpr auto make_with_try_catch =
36
38
  []<class Env, class... Args, bool Nx, class Result>(
37
39
  std::type_identity<auto(Env, Args...) noexcept(Nx)->Result> /*signature*/,
@@ -39,18 +41,18 @@ constexpr auto make_free_function(auto function) {
39
41
  ) -> auto {
40
42
  using callback_type = decltype(callback);
41
43
  return util::bind{
42
- [](callback_type& callback, Environment& env, const callback_info& info) noexcept(Nx) -> napi_value {
43
- return invoke_internal_error_scope(env, [ & ] -> napi_value {
44
+ [](callback_type& callback, const lock_type& lock, const callback_info& info) noexcept(Nx) -> napi_value {
45
+ return invoke_internal_error_scope(lock, [ & ] -> napi_value {
44
46
  auto run = util::regular_return{[ & ] -> decltype(auto) {
45
47
  return std::apply(
46
48
  callback,
47
49
  std::tuple_cat(
48
- std::forward_as_tuple(env),
49
- js::transfer_out<std::tuple<js::functional::parameter_transfer_as_t<Args>...>>(info.arguments(), env)
50
+ std::forward_as_tuple(lock),
51
+ js::transfer_out<std::tuple<js::functional::parameter_transfer_as_t<Args>...>>(info.arguments(), lock)
50
52
  )
51
53
  );
52
54
  }};
53
- return js::transfer_in_strict<napi_value>(run().value_or(std::monostate{}), env);
55
+ return js::transfer_in_strict<napi_value>(run().value_or(std::monostate{}), lock);
54
56
  });
55
57
  },
56
58
  std::move(callback),
@@ -64,17 +66,17 @@ constexpr auto make_free_function(auto function) {
64
66
  static_assert(false, "untested");
65
67
  using callback_type = decltype(callback);
66
68
  return util::bind{
67
- [](callback_type& callback, Environment& env, const callback_info& /*info*/) noexcept -> napi_value {
69
+ [](callback_type& callback, const lock_type& lock, const callback_info& /*info*/) noexcept -> napi_value {
68
70
  auto run = util::regular_return{[ & ] -> decltype(auto) {
69
- return callback(env);
71
+ return callback(lock);
70
72
  }};
71
- return js::transfer_in_strict<napi_value>(run().value_or(std::monostate{}), env);
73
+ return js::transfer_in_strict<napi_value>(run().value_or(std::monostate{}), lock);
72
74
  },
73
75
  std::move(callback),
74
76
  };
75
77
  };
76
78
 
77
- auto callback = js::functional::thunk_free_function<Environment&>(std::move(function));
79
+ auto callback = js::functional::thunk_free_function<const lock_type&>(std::move(function));
78
80
  constexpr auto make = util::overloaded{make_with_try_catch, make_noexcept};
79
81
  return make(std::type_identity<util::function_signature_t<decltype(callback)>>{}, std::move(callback));
80
82
  }
@@ -82,22 +84,23 @@ constexpr auto make_free_function(auto function) {
82
84
  // Make callback for constructor invocations
83
85
  template <auto_environment Environment, class Type>
84
86
  constexpr auto make_constructor_function(auto constructor) {
87
+ using lock_type = environment_lock_witness_of<Environment>;
85
88
  // First, a lambda is created which will actually invoke the supplied constructor function.
86
89
  constexpr auto make_invoke_runtime_constructor = util::overloaded{
87
90
  [](std::nullptr_t /*constructor*/) -> auto {
88
- return [](Environment& env, const callback_info& /*info*/) -> napi_value {
89
- napi::invoke0(napi_throw_type_error, napi_env{env}, nullptr, "Illegal constructor");
91
+ return [](const lock_type& lock, const callback_info& /*info*/) -> napi_value {
92
+ napi::invoke0(napi_throw_type_error, napi_env{lock}, nullptr, "Illegal constructor");
90
93
  return {};
91
94
  };
92
95
  },
93
96
  [](auto constructor) -> auto {
94
97
  // These are the last steps, common to both branches
95
- constexpr auto wrap = [](auto instance, Environment& env, const callback_info& info) -> napi_value {
98
+ constexpr auto wrap = [](auto instance, const lock_type& lock, const callback_info& info) -> napi_value {
96
99
  // Tag the result
97
- napi::invoke0(napi_type_tag_object, napi_env{env}, info.this_arg(), &type_tag_for<Type>);
100
+ napi::invoke0(napi_type_tag_object, napi_env{lock}, info.this_arg(), &type_tag_for<Type>);
98
101
  // Wrap w/ finalizer
99
102
  return apply_finalizer(std::move(instance), [ & ](Type* instance, node_api_basic_finalize finalize, void* hint) -> napi_value {
100
- napi::invoke0(napi_wrap, napi_env{env}, info.this_arg(), instance, finalize, hint, nullptr);
103
+ napi::invoke0(napi_wrap, napi_env{lock}, info.this_arg(), instance, finalize, hint, nullptr);
101
104
  return info.this_arg();
102
105
  });
103
106
  };
@@ -111,16 +114,16 @@ constexpr auto make_constructor_function(auto constructor) {
111
114
  static_assert(false, "untested");
112
115
  using callback_type = decltype(callback);
113
116
  return util::bind{
114
- [](callback_type& callback, wrap_type& wrap, Environment& env, const callback_info& info) noexcept(Nx) -> napi_value {
115
- return invoke_internal_error_scope(env, [ & ] -> napi_value {
117
+ [](callback_type& callback, wrap_type& wrap, const lock_type& lock, const callback_info& info) noexcept(Nx) -> napi_value {
118
+ return invoke_internal_error_scope(lock, [ & ] -> napi_value {
116
119
  auto instance = std::apply(
117
120
  callback,
118
121
  std::tuple_cat(
119
- std::forward_as_tuple(env, local_of<object_tag>::from(info.this_arg())),
120
- js::transfer_out<std::tuple<js::functional::parameter_transfer_as_t<Args>...>>(info.arguments(), env)
122
+ std::forward_as_tuple(lock, local_of<object_tag>::from(info.this_arg())),
123
+ js::transfer_out<std::tuple<js::functional::parameter_transfer_as_t<Args>...>>(info.arguments(), lock)
121
124
  )
122
125
  );
123
- return wrap(std::move(instance));
126
+ return wrap(std::move(instance), lock, info);
124
127
  });
125
128
  },
126
129
  std::move(callback),
@@ -135,14 +138,14 @@ constexpr auto make_constructor_function(auto constructor) {
135
138
  static_assert(false, "untested");
136
139
  using callback_type = decltype(callback);
137
140
  return util::bind{
138
- [](callback_type& callback, Environment& env, const callback_info& info) noexcept -> napi_value {
139
- return wrap(callback(env, local_of<object_tag>::from(info.this_arg())));
141
+ [](callback_type& callback, const lock_type& lock, const callback_info& info) noexcept -> napi_value {
142
+ return wrap(callback(lock, local_of<object_tag>::from(info.this_arg())));
140
143
  },
141
144
  std::move(callback),
142
145
  };
143
146
  };
144
147
 
145
- auto callback = js::functional::thunk_free_function<Environment&>(std::move(constructor));
148
+ auto callback = js::functional::thunk_free_function<const lock_type&>(std::move(constructor));
146
149
  using signature_type = util::function_signature_t<decltype(callback)>;
147
150
  static_assert(util::signature_result<signature_type>{} != type<void>);
148
151
  constexpr auto make = util::overloaded{make_with_try_catch, make_noexcept};
@@ -156,18 +159,18 @@ constexpr auto make_constructor_function(auto constructor) {
156
159
  // invocation not available to JavaScript. This is done with a tagged external value to a
157
160
  // `util::function_ref`.
158
161
  return util::bind{
159
- [](runtime_constructor_type& constructor, Environment& env, const callback_info& info) -> napi_value {
162
+ [](runtime_constructor_type& constructor, const lock_type& lock, const callback_info& info) -> napi_value {
160
163
  auto arguments = info.arguments();
161
164
  if (!arguments.empty()) {
162
165
  auto maybe_constructor = [ & ] -> std::optional<internal_constructor*> {
163
166
  try {
164
167
  // Check truthiness since null or undefined causes `napi_coerce_to_object` to throw
165
168
  auto* arg0 = util::at(arguments, 0);
166
- if (napi::invoke(napi_coerce_to_bool, napi_env{env}, arg0)) {
167
- auto* as_object = napi::invoke(napi_coerce_to_object, napi_env{env}, arg0);
168
- auto has_tag = napi::invoke(napi_check_object_type_tag, napi_env{env}, as_object, &type_tag_for<internal_constructor>);
169
+ if (napi::invoke(napi_coerce_to_bool, napi_env{lock}, arg0)) {
170
+ auto* as_object = napi::invoke(napi_coerce_to_object, napi_env{lock}, arg0);
171
+ auto has_tag = napi::invoke(napi_check_object_type_tag, napi_env{lock}, as_object, &type_tag_for<internal_constructor>);
169
172
  if (has_tag) {
170
- void* addr = napi::invoke(napi_get_value_external, napi_env{env}, as_object);
173
+ void* addr = napi::invoke(napi_get_value_external, napi_env{lock}, as_object);
171
174
  return static_cast<internal_constructor*>(addr);
172
175
  }
173
176
  }
@@ -181,13 +184,13 @@ constexpr auto make_constructor_function(auto constructor) {
181
184
  }
182
185
  if (*maybe_constructor != nullptr) {
183
186
  const auto& constructor = **maybe_constructor;
184
- return invoke_internal_error_scope(env, [ & ] -> napi_value {
187
+ return invoke_internal_error_scope(lock, [ & ] -> napi_value {
185
188
  return constructor(info.this_arg());
186
189
  });
187
190
  }
188
191
  }
189
192
  // Invoke normal constructor
190
- return constructor(env, info);
193
+ return constructor(lock, info);
191
194
  },
192
195
  std::move(runtime_constructor),
193
196
  };
@@ -196,6 +199,7 @@ constexpr auto make_constructor_function(auto constructor) {
196
199
  // Make callback for method invocations
197
200
  template <auto_environment Environment, class Type, class Method>
198
201
  constexpr auto make_member_function(Method method) {
202
+ using lock_type = environment_lock_witness_of<Environment>;
199
203
  constexpr auto make_with_try_catch =
200
204
  []<class That, class Env, class... Args, bool Nx, class Result>(
201
205
  std::type_identity<auto(That, Env, Args...) noexcept(Nx)->Result> /*signature*/,
@@ -203,22 +207,22 @@ constexpr auto make_member_function(Method method) {
203
207
  ) -> auto {
204
208
  using callback_type = decltype(callback);
205
209
  return util::bind{
206
- [](callback_type& callback, Environment& env, const callback_info& info) noexcept(Nx) -> napi_value {
207
- auto maybe_that = unwrap_member_this<Type>(env, info.this_arg());
208
- if (!maybe_that) {
209
- return nullptr;
210
- }
211
- return invoke_internal_error_scope(env, [ & ] -> napi_value {
210
+ [](callback_type& callback, const lock_type& lock, const callback_info& info) noexcept(Nx) -> napi_value {
211
+ return invoke_internal_error_scope(lock, [ & ] -> napi_value {
212
+ auto maybe_that = unwrap_member_this<Type>(lock, info.this_arg());
213
+ if (!maybe_that) {
214
+ return nullptr;
215
+ }
212
216
  auto run = util::regular_return{[ & ] -> decltype(auto) {
213
217
  return std::apply(
214
218
  callback,
215
219
  std::tuple_cat(
216
- std::forward_as_tuple(**maybe_that, env),
217
- js::transfer_out<std::tuple<js::functional::parameter_transfer_as_t<Args>...>>(info.arguments(), env)
220
+ std::forward_as_tuple(**maybe_that, lock),
221
+ js::transfer_out<std::tuple<js::functional::parameter_transfer_as_t<Args>...>>(info.arguments(), lock)
218
222
  )
219
223
  );
220
224
  }};
221
- return js::transfer_in_strict<napi_value>(run().value_or(std::monostate{}), env);
225
+ return js::transfer_in_strict<napi_value>(run().value_or(std::monostate{}), lock);
222
226
  });
223
227
  },
224
228
  std::move(callback),
@@ -232,21 +236,21 @@ constexpr auto make_member_function(Method method) {
232
236
  static_assert(false, "untested");
233
237
  using callback_type = decltype(callback);
234
238
  return util::bind{
235
- [](callback_type& callback, Environment& env, const callback_info& info) noexcept -> napi_value {
236
- auto maybe_that = unwrap_member_this<Type>(env, info.this_arg());
239
+ [](callback_type& callback, const lock_type& lock, const callback_info& info) noexcept -> napi_value {
240
+ auto maybe_that = unwrap_member_this<Type>(lock, info.this_arg());
237
241
  if (!maybe_that) {
238
242
  return nullptr;
239
243
  }
240
244
  auto run = util::regular_return{[ & ] -> decltype(auto) {
241
- return callback(**maybe_that, env);
245
+ return callback(**maybe_that, lock);
242
246
  }};
243
- return js::transfer_in_strict<napi_value>(run().value_or(std::monostate{}), env);
247
+ return js::transfer_in_strict<napi_value>(run().value_or(std::monostate{}), lock);
244
248
  },
245
249
  std::move(callback),
246
250
  };
247
251
  };
248
252
 
249
- auto callback = js::functional::thunk_member_function<Environment&>(std::move(method));
253
+ auto callback = js::functional::thunk_member_function<const lock_type&>(std::move(method));
250
254
  constexpr auto make = util::overloaded{make_with_try_catch, make_noexcept};
251
255
  return make(std::type_identity<util::function_signature_t<decltype(callback)>>{}, std::move(callback));
252
256
  };
@@ -1,6 +1,7 @@
1
1
  export module napi_js:callback_storage;
2
2
  import :api;
3
3
  import :environment_fwd;
4
+ import :lock;
4
5
  import std;
5
6
 
6
7
  namespace js::napi {
@@ -15,8 +16,10 @@ thread_local Environment* callback_env_local = nullptr;
15
16
  // result is a `std::tuple` with `{ callback_ptr, data_ptr, finalizer }`. Finalizer is either a
16
17
  // `nullptr` or a `std::unique_ptr<T>` which should be disposed of in a finalizer.
17
18
  template <auto_environment Environment>
18
- auto make_callback_storage(Environment& env, std::invocable<Environment&, const callback_info&> auto function) {
19
+ auto make_callback_storage(const environment_lock_witness_of<Environment>& lock, std::invocable<const environment_lock_witness_of<Environment>&, const callback_info&> auto function) {
19
20
  using function_type = decltype(function);
21
+ using lock_type = environment_lock_witness_of<Environment>;
22
+ auto& env = *lock;
20
23
  if constexpr (std::is_trivially_copyable_v<function_type>) {
21
24
  if constexpr (std::is_empty_v<function_type>) {
22
25
  // Constant expression function, expressed entirely in the type. `data` is the environment.
@@ -25,7 +28,8 @@ auto make_callback_storage(Environment& env, std::invocable<Environment&, const
25
28
  if (args) {
26
29
  auto invoke = function_type{};
27
30
  auto& env = *static_cast<Environment*>(args.data());
28
- return invoke(env, args);
31
+ auto lock = lock_type{environment_lock_witness::make_witness(env), env};
32
+ return invoke(lock, args);
29
33
  } else {
30
34
  return {};
31
35
  }
@@ -42,7 +46,8 @@ auto make_callback_storage(Environment& env, std::invocable<Environment&, const
42
46
  auto* data = args.data();
43
47
  auto invoke = util::bit_truncation_cast<function_type>(data);
44
48
  auto& env = *callback_env_local<Environment>;
45
- return invoke(env, args);
49
+ auto lock = lock_type{environment_lock_witness::make_witness(env), env};
50
+ return invoke(lock, args);
46
51
  } else {
47
52
  return {};
48
53
  }
@@ -59,7 +64,9 @@ auto make_callback_storage(Environment& env, std::invocable<Environment&, const
59
64
  const auto args = callback_info{nenv, info};
60
65
  if (args) {
61
66
  auto& state = *static_cast<pair_type*>(args.data());
62
- return state.second(*state.first, args);
67
+ auto& env = *state.first;
68
+ auto lock = lock_type{environment_lock_witness::make_witness(env), env};
69
+ return state.second(lock, args);
63
70
  } else {
64
71
  return {};
65
72
  }