@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 +4 -0
- package/_module.cc +2 -0
- package/api/api.cc +1 -0
- package/api/callback_info.cc +1 -1
- package/api/napi_scheduler.cc +21 -0
- package/api/napi_scheduler.h.cc +16 -0
- package/api/threadsafe_function.cc +6 -6
- package/api/unmaybe.cc +36 -0
- package/handle/remote.cc +22 -17
- package/handle/value.cc +14 -3
- package/package.json +4 -4
- package/support/callback.cc +52 -48
- package/support/callback_storage.cc +11 -4
- package/support/class_template_storage.cc +6 -5
- package/support/environment.cc +1 -0
- package/support/environment.h.cc +8 -3
- package/support/environment_fwd.cc +4 -2
- package/support/error_scope.cc +11 -5
- package/support/host.cc +43 -5
- package/support/host.h.cc +13 -2
- package/support/initialize.h.cc +3 -1
- package/support/lock.cc +40 -0
- package/support/lock.h.cc +64 -0
- package/support/promise.cc +28 -18
- package/transfer/accept.cc +1 -1
- package/transfer/accept.h.cc +21 -18
- package/transfer/transfer_list.cc +132 -0
- package/transfer/visit.cc +183 -123
- package/value/array.cc +6 -1
- package/value/array.h.cc +8 -3
- package/value/array_buffer.cc +19 -15
- package/value/array_buffer.h.cc +18 -16
- package/value/class.cc +37 -16
- package/value/class.h.cc +5 -4
- package/value/external.cc +7 -7
- package/value/function.cc +15 -15
- package/value/function.h.cc +6 -5
- package/value/object.cc +2 -2
- package/value/object.h.cc +2 -2
- package/value/record.cc +8 -2
- package/value/record.h.cc +8 -2
|
@@ -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
|
|
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(
|
|
39
|
+
return value_type::from(reference.get(lock));
|
|
39
40
|
} else {
|
|
40
|
-
auto template_value = value_type::make(
|
|
41
|
-
reference.reset(
|
|
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
|
}
|
package/support/environment.cc
CHANGED
|
@@ -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.
|
package/support/environment.h.cc
CHANGED
|
@@ -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
|
-
//
|
|
11
|
-
//
|
|
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
|
-
//
|
|
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
|
package/support/error_scope.cc
CHANGED
|
@@ -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`)
|
|
7
|
-
// runtime error.
|
|
8
|
-
|
|
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,
|
|
15
|
-
napi::invoke0(napi_throw, napi_env{
|
|
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<
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
package/support/initialize.h.cc
CHANGED
|
@@ -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(
|
|
40
|
+
exports_local->assign(lock, std::move(make_exports_)(lock));
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
Make make_exports_;
|
package/support/lock.cc
ADDED
|
@@ -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
|
package/support/promise.cc
CHANGED
|
@@ -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
|
-
|
|
12
|
-
|
|
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_{
|
|
17
|
+
scheduler_{lock->scheduler()},
|
|
15
18
|
dispatch_{dispatch} {
|
|
16
|
-
scheduler_.increment_ref(napi_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&,
|
|
32
|
-
using result_type = std::invoke_result_t<Dispatch&,
|
|
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
|
-
[](
|
|
36
|
-
return expected_type{std::in_place, std::move(dispatch)(
|
|
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
|
-
[](
|
|
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
|
-
[](
|
|
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(
|
|
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),
|
|
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(),
|
|
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,
|
|
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
|
|
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
|
|
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{
|
|
124
|
+
napi::invoke0(napi_create_promise, napi_env{lock}, &deferred, &promise);
|
|
115
125
|
|
|
116
126
|
// Make resolve helper
|
|
117
|
-
auto resolve = resolver{
|
|
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)};
|
package/transfer/accept.cc
CHANGED
|
@@ -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(),
|
|
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:
|
package/transfer/accept.h.cc
CHANGED
|
@@ -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*/,
|
|
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.
|
|
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
|
|
148
|
-
explicit operator napi_env() const { return napi_env{
|
|
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
|
-
|
|
152
|
+
environment_lock_witness lock_;
|
|
152
153
|
};
|
|
153
154
|
|
|
154
155
|
// Generic napi acceptor which accepts all value types.
|
|
155
|
-
template <class
|
|
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
|
|
162
|
+
template <class Lock>
|
|
162
163
|
struct accept_napi_value : accept_basic_napi_value {
|
|
163
164
|
public:
|
|
164
|
-
explicit accept_napi_value(auto* transfer,
|
|
165
|
-
accept_basic_napi_value{transfer,
|
|
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(
|
|
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
|
|
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(
|
|
333
|
-
js::transfer_in<object_assign_delegate>(std::move(source),
|
|
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
|
|
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&
|
|
391
|
-
accept_{transfer,
|
|
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>
|