@auto_js/napi 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +17 -1
- package/_module.cc +0 -1
- package/api/api.cc +10 -8
- package/api/finalizer.cc +43 -21
- package/api/napi_scheduler.cc +39 -0
- package/api/napi_scheduler.h.cc +59 -0
- package/api/threadsafe_function.cc +156 -0
- package/api/uv_dlib.cc +4 -3
- package/handle/bound_value.cc +1 -0
- package/handle/reference.cc +2 -2
- package/handle/remote.cc +5 -5
- package/handle/types.cc +10 -0
- package/handle/value.cc +5 -0
- package/package.json +4 -4
- package/support/callback.cc +1 -1
- package/support/environment.cc +12 -31
- package/support/environment.h.cc +1 -8
- package/support/host.cc +339 -0
- package/support/host.h.cc +72 -0
- package/support/promise.cc +11 -11
- package/support/utility.cc +10 -85
- package/support/win32_delay_load_hook.cc +12 -0
- package/transfer/accept.cc +1 -22
- package/transfer/accept.h.cc +1 -1
- package/transfer/visit.cc +98 -84
- package/value/array_buffer.cc +10 -25
- package/value/array_buffer.h.cc +1 -23
- package/value/class.cc +1 -1
- package/value/primitive.h.cc +13 -0
- package/support/container.cc +0 -77
package/CMakeLists.txt
CHANGED
|
@@ -14,9 +14,11 @@ target_link_libraries(${napi_js} PUBLIC nodejs)
|
|
|
14
14
|
|
|
15
15
|
target_sources(${napi_js}
|
|
16
16
|
PRIVATE
|
|
17
|
+
api/napi_scheduler.cc
|
|
17
18
|
api/uv_dlib.cc
|
|
18
19
|
api/uv_scheduler.cc
|
|
19
20
|
support/environment.cc
|
|
21
|
+
support/host.cc
|
|
20
22
|
support/initialize.cc
|
|
21
23
|
transfer/accept.cc
|
|
22
24
|
value/array_buffer.cc
|
|
@@ -31,6 +33,8 @@ target_sources(${napi_js}
|
|
|
31
33
|
api/finalizer.cc
|
|
32
34
|
api/handle_scope.cc
|
|
33
35
|
api/invoke.cc
|
|
36
|
+
api/napi_scheduler.h.cc
|
|
37
|
+
api/threadsafe_function.cc
|
|
34
38
|
api/uv_dlib.h.cc
|
|
35
39
|
api/uv_handle.cc
|
|
36
40
|
api/uv_scheduler.h.cc
|
|
@@ -42,10 +46,10 @@ target_sources(${napi_js}
|
|
|
42
46
|
support/callback_storage.cc
|
|
43
47
|
support/callback.cc
|
|
44
48
|
support/class_template_storage.cc
|
|
45
|
-
support/container.cc
|
|
46
49
|
support/environment_fwd.cc
|
|
47
50
|
support/environment.h.cc
|
|
48
51
|
support/error_scope.cc
|
|
52
|
+
support/host.h.cc
|
|
49
53
|
support/initialize.h.cc
|
|
50
54
|
support/promise.cc
|
|
51
55
|
support/string_table.cc
|
|
@@ -64,3 +68,15 @@ target_sources(${napi_js}
|
|
|
64
68
|
value/record.h.cc
|
|
65
69
|
value/value.cc
|
|
66
70
|
)
|
|
71
|
+
|
|
72
|
+
# Enable "delay load" for win32 otherwise the dll will attempt to load node.exe from PATH as a
|
|
73
|
+
# shared library and then start invoking functions from it.
|
|
74
|
+
if(WIN32)
|
|
75
|
+
set(napi_js_delay_load ${napi_js}_delay_load)
|
|
76
|
+
add_library(${napi_js_delay_load} STATIC)
|
|
77
|
+
set_target_properties(${napi_js_delay_load} PROPERTIES CXX_VISIBILITY_PRESET default)
|
|
78
|
+
target_sources(${napi_js_delay_load} PUBLIC support/win32_delay_load_hook.cc)
|
|
79
|
+
target_link_libraries(${napi_js_delay_load} PUBLIC delayimp.lib)
|
|
80
|
+
target_link_libraries(${napi_js} PUBLIC ${napi_js_delay_load})
|
|
81
|
+
target_link_options(${napi_js_delay_load} PUBLIC "-Wl,/DELAYLOAD:node.exe")
|
|
82
|
+
endif()
|
package/_module.cc
CHANGED
package/api/api.cc
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
export module napi_js:api;
|
|
2
|
-
// Internal nodejs API utilities which have no dependencies on anything else in `napi_js`
|
|
3
|
-
export import nodejs;
|
|
4
2
|
export import auto_js;
|
|
5
|
-
export import
|
|
6
|
-
|
|
7
|
-
export import :api.uv_dlib;
|
|
8
|
-
export import :api.invoke;
|
|
9
|
-
export import :api.handle_scope;
|
|
10
|
-
export import :api.finalizer;
|
|
3
|
+
export import nodejs;
|
|
4
|
+
// Internal nodejs API utilities which have no dependencies on anything else in `napi_js`
|
|
11
5
|
export import :api.callback_info;
|
|
6
|
+
export import :api.finalizer;
|
|
7
|
+
export import :api.handle_scope;
|
|
8
|
+
export import :api.invoke;
|
|
9
|
+
export import :api.napi_scheduler;
|
|
10
|
+
export import :api.threadsafe_function;
|
|
11
|
+
export import :api.uv_dlib;
|
|
12
|
+
export import :api.uv_handle;
|
|
13
|
+
export import :api.uv_scheduler;
|
package/api/finalizer.cc
CHANGED
|
@@ -5,36 +5,58 @@ import util;
|
|
|
5
5
|
|
|
6
6
|
namespace js::napi {
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
auto
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
8
|
+
// Generic finalizer for basic `std::unique_ptr<T>`
|
|
9
|
+
template <class Env, class Type, class Deleter>
|
|
10
|
+
auto make_finalizer(std::type_identity<Env> /*type*/, std::unique_ptr<Type, Deleter> unique) {
|
|
11
|
+
if constexpr (std::is_empty_v<Deleter>) {
|
|
12
|
+
// Finalizer uses a default-constructed deleter
|
|
13
|
+
auto finalize = [](Env /*env*/, void* data, void* /*hint*/) -> void {
|
|
14
|
+
Deleter deleter{};
|
|
15
|
+
deleter(static_cast<Type*>(data));
|
|
16
|
+
};
|
|
17
|
+
return std::tuple{finalize, nullptr, std::move(unique)};
|
|
18
|
+
} else {
|
|
19
|
+
static_assert(std::is_trivially_destructible_v<Deleter>);
|
|
20
|
+
static_assert(sizeof(Deleter) == sizeof(void*));
|
|
21
|
+
// Hint is the deleter object
|
|
22
|
+
auto finalize = [](Env /*env*/, void* data, void* hint) -> void {
|
|
23
|
+
std::bit_cast<Deleter>(hint)(static_cast<Type*>(data));
|
|
24
|
+
};
|
|
25
|
+
return std::tuple{finalize, std::bit_cast<void*>(unique.get_deleter()), std::move(unique)};
|
|
26
|
+
}
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
auto
|
|
29
|
+
// Generic finalizer for `std::shared_ptr<T>`
|
|
30
|
+
template <class Env, class Type>
|
|
31
|
+
auto make_finalizer(std::type_identity<Env> /*type*/, std::shared_ptr<Type> shared) {
|
|
27
32
|
// `shared_ptr<T>*` is passed as the finalizer hint
|
|
28
33
|
using shared_ptr_type = std::shared_ptr<Type>;
|
|
29
34
|
auto* ptr = shared.get();
|
|
30
35
|
auto ptr_ptr = std::make_unique<shared_ptr_type>(std::move(shared));
|
|
31
|
-
|
|
36
|
+
auto finalize = [](Env /*env*/, void* /*data*/, void* hint) -> void {
|
|
32
37
|
delete static_cast<shared_ptr_type*>(hint);
|
|
33
38
|
};
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
return std::tuple{finalize, ptr_ptr.get(), std::move(ptr_ptr)};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Apply a finalizer to the given smart pointer type
|
|
43
|
+
template <class Pointer, class Apply>
|
|
44
|
+
auto apply_finalizer(Pointer smart_ptr, Apply apply) -> decltype(auto) {
|
|
45
|
+
using element_type = Pointer::element_type;
|
|
46
|
+
constexpr auto napi_env_type = []() consteval -> auto {
|
|
47
|
+
if constexpr (std::invocable<Apply, element_type*, node_api_basic_finalize, void*>) {
|
|
48
|
+
return type<node_api_basic_env>;
|
|
49
|
+
} else {
|
|
50
|
+
static_assert(std::invocable<Apply, element_type*, napi_finalize, void*>);
|
|
51
|
+
return type<napi_env>;
|
|
52
|
+
}
|
|
53
|
+
}();
|
|
54
|
+
auto* pointer = smart_ptr.get();
|
|
55
|
+
auto [ finalize, hint, releasable ] = make_finalizer(napi_env_type, std::move(smart_ptr));
|
|
56
|
+
auto result = util::regular_return{[ & ] -> decltype(auto) {
|
|
57
|
+
return apply(pointer, finalize, hint);
|
|
36
58
|
}}();
|
|
37
|
-
|
|
59
|
+
releasable.release();
|
|
38
60
|
return *std::move(result);
|
|
39
61
|
}
|
|
40
62
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module napi_js;
|
|
2
|
+
import std;
|
|
3
|
+
|
|
4
|
+
namespace js::napi {
|
|
5
|
+
|
|
6
|
+
// napi_scheduler
|
|
7
|
+
napi_scheduler::napi_scheduler(napi_env env) :
|
|
8
|
+
fn_{env, nullptr} {
|
|
9
|
+
// It starts ref'd, so we need to unref it
|
|
10
|
+
fn_.unref(env);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
napi_scheduler::operator bool() const noexcept {
|
|
14
|
+
return bool{fn_};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
auto napi_scheduler::close(threadsafe_function_type::close_callback close) noexcept -> void {
|
|
18
|
+
fn_.close(close);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
auto napi_scheduler::decrement_ref(node_api_basic_env env) const -> void {
|
|
22
|
+
auto& storage = *fn_;
|
|
23
|
+
if (--storage.refs == 0) {
|
|
24
|
+
fn_.unref(env);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
auto napi_scheduler::increment_ref(node_api_basic_env env) const -> void {
|
|
29
|
+
auto& storage = *fn_;
|
|
30
|
+
if (++storage.refs == 1) {
|
|
31
|
+
fn_.ref(env);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
auto napi_scheduler::storage::operator()(napi_env env, napi_value value, task_type& task) noexcept -> void {
|
|
36
|
+
task(env, value);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
} // namespace js::napi
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export module napi_js:api.napi_scheduler;
|
|
2
|
+
import :api.threadsafe_function;
|
|
3
|
+
import nodejs;
|
|
4
|
+
import std;
|
|
5
|
+
import util;
|
|
6
|
+
|
|
7
|
+
namespace js::napi {
|
|
8
|
+
|
|
9
|
+
// Shareable napi scheduler via napi_threadsafe_function
|
|
10
|
+
export class napi_scheduler {
|
|
11
|
+
private:
|
|
12
|
+
struct storage;
|
|
13
|
+
using task_type = util::move_only_function<auto(napi_env, napi_value) noexcept -> void>;
|
|
14
|
+
using threadsafe_function_type = threadsafe_function_of<storage, task_type>;
|
|
15
|
+
|
|
16
|
+
public:
|
|
17
|
+
napi_scheduler() = default;
|
|
18
|
+
explicit napi_scheduler(napi_env env);
|
|
19
|
+
auto operator()(auto task, auto&&... args) const noexcept -> bool;
|
|
20
|
+
explicit operator bool() const noexcept;
|
|
21
|
+
auto close(threadsafe_function_type::close_callback close) noexcept -> void;
|
|
22
|
+
auto decrement_ref(node_api_basic_env env) const -> void;
|
|
23
|
+
auto increment_ref(node_api_basic_env env) const -> void;
|
|
24
|
+
|
|
25
|
+
private:
|
|
26
|
+
struct storage {
|
|
27
|
+
storage() = default;
|
|
28
|
+
auto operator()(napi_env env, napi_value value, task_type& task) noexcept -> void;
|
|
29
|
+
int refs{};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
threadsafe_function_of<storage, task_type> fn_;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// An environment with an uv loop and scheduler
|
|
36
|
+
export class napi_schedulable {
|
|
37
|
+
public:
|
|
38
|
+
explicit napi_schedulable(napi_env env) : scheduler_{env} {}
|
|
39
|
+
[[nodiscard]] auto scheduler(this auto& self) -> auto& { return self.scheduler_; }
|
|
40
|
+
|
|
41
|
+
private:
|
|
42
|
+
napi_scheduler scheduler_;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// ---
|
|
46
|
+
|
|
47
|
+
auto napi_scheduler::operator()(auto task, auto&&... args) const noexcept -> bool {
|
|
48
|
+
return fn_(
|
|
49
|
+
[ task = std::move(task),
|
|
50
|
+
... args = std::forward<decltype(args)>(args) ](
|
|
51
|
+
napi_env env,
|
|
52
|
+
napi_value value
|
|
53
|
+
) mutable -> void {
|
|
54
|
+
task(env, value, std::move(args)...);
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
} // namespace js::napi
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
export module napi_js:api.threadsafe_function;
|
|
2
|
+
import :api.invoke;
|
|
3
|
+
import nodejs;
|
|
4
|
+
import std;
|
|
5
|
+
import util;
|
|
6
|
+
|
|
7
|
+
namespace js::napi {
|
|
8
|
+
|
|
9
|
+
// Maintain a "threadsafe function" via napi. `Context` must be invocable with
|
|
10
|
+
// `(napi_env, napi_value, Message&)`.
|
|
11
|
+
export template <class Context, class Message>
|
|
12
|
+
class threadsafe_function_of : util::non_copyable {
|
|
13
|
+
public:
|
|
14
|
+
using close_callback = util::function_ref<auto()->void>;
|
|
15
|
+
|
|
16
|
+
threadsafe_function_of() = default;
|
|
17
|
+
explicit threadsafe_function_of(auto& env, napi_value value, auto&&... args);
|
|
18
|
+
threadsafe_function_of(const threadsafe_function_of&);
|
|
19
|
+
threadsafe_function_of(threadsafe_function_of&&) noexcept;
|
|
20
|
+
~threadsafe_function_of();
|
|
21
|
+
auto operator=(const threadsafe_function_of&) -> threadsafe_function_of& = delete;
|
|
22
|
+
auto operator=(threadsafe_function_of&&) -> threadsafe_function_of& = delete;
|
|
23
|
+
|
|
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;
|
|
27
|
+
auto operator()(Message message) const noexcept -> bool;
|
|
28
|
+
auto operator*() const noexcept -> Context& { return get(); }
|
|
29
|
+
explicit operator bool() const noexcept { return tsfn_ != nullptr; }
|
|
30
|
+
|
|
31
|
+
private:
|
|
32
|
+
struct storage : Context {
|
|
33
|
+
public:
|
|
34
|
+
friend threadsafe_function_of;
|
|
35
|
+
using Context::Context;
|
|
36
|
+
|
|
37
|
+
private:
|
|
38
|
+
close_callback close_hook_{[] -> void {}};
|
|
39
|
+
};
|
|
40
|
+
auto get() const noexcept -> storage&;
|
|
41
|
+
|
|
42
|
+
constexpr static auto trivial_message =
|
|
43
|
+
std::is_trivially_destructible_v<Message> && sizeof(Message) <= sizeof(void*);
|
|
44
|
+
mutable napi_threadsafe_function tsfn_{};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// ---
|
|
48
|
+
|
|
49
|
+
template <class Context, class Message>
|
|
50
|
+
threadsafe_function_of<Context, Message>::threadsafe_function_of(auto& env, napi_value value, auto&&... args) :
|
|
51
|
+
tsfn_{[ & ]() -> auto {
|
|
52
|
+
constexpr auto name = util::make_consteval_string_view(util::cw<"threadsafe_function_of">);
|
|
53
|
+
auto resource_name = napi::invoke(napi_create_string_latin1, env, name.data(), name.size());
|
|
54
|
+
auto context = std::make_unique<storage>(std::forward<decltype(args)>(args)...);
|
|
55
|
+
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
|
|
56
|
+
auto callback = napi_threadsafe_function_call_js{[](napi_env env, napi_value value, void* context_ptr, void* data) -> void {
|
|
57
|
+
auto& context = *static_cast<storage*>(context_ptr);
|
|
58
|
+
if constexpr (trivial_message) {
|
|
59
|
+
if (env != nullptr) {
|
|
60
|
+
auto message = Message{};
|
|
61
|
+
std::memcpy(&message, &data, sizeof(Message));
|
|
62
|
+
context(env, value, message);
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
auto message = std::unique_ptr<Message>{static_cast<Message*>(data)};
|
|
66
|
+
if (env != nullptr) {
|
|
67
|
+
context(env, value, *message);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}};
|
|
71
|
+
auto finalize = napi_finalize{[](napi_env /*env*/, void* /*data*/, void* hint) -> void {
|
|
72
|
+
auto* storage_ptr = static_cast<storage*>(hint);
|
|
73
|
+
storage_ptr->close_hook_();
|
|
74
|
+
delete storage_ptr;
|
|
75
|
+
}};
|
|
76
|
+
auto* tsfn = napi::invoke(napi_create_threadsafe_function, env, value, nullptr, resource_name, 0, 1, nullptr, finalize, context.get(), callback);
|
|
77
|
+
context.release();
|
|
78
|
+
return tsfn;
|
|
79
|
+
}()} {
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
template <class Context, class Message>
|
|
83
|
+
threadsafe_function_of<Context, Message>::threadsafe_function_of(const threadsafe_function_of& that) :
|
|
84
|
+
tsfn_{that.tsfn_} {
|
|
85
|
+
if (that) {
|
|
86
|
+
napi::invoke0_noexcept(napi_acquire_threadsafe_function, tsfn_);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
template <class Context, class Message>
|
|
91
|
+
threadsafe_function_of<Context, Message>::threadsafe_function_of(threadsafe_function_of&& that) noexcept :
|
|
92
|
+
tsfn_{std::exchange(that.tsfn_, nullptr)} {}
|
|
93
|
+
|
|
94
|
+
template <class Context, class Message>
|
|
95
|
+
threadsafe_function_of<Context, Message>::~threadsafe_function_of() {
|
|
96
|
+
if (*this) {
|
|
97
|
+
napi::invoke0_noexcept(napi_release_threadsafe_function, tsfn_, napi_tsfn_release);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
template <class Context, class Message>
|
|
102
|
+
auto threadsafe_function_of<Context, Message>::close(close_callback callback) noexcept -> void {
|
|
103
|
+
get().close_hook_ = callback;
|
|
104
|
+
napi::invoke0_noexcept(napi_release_threadsafe_function, std::exchange(tsfn_, nullptr), napi_tsfn_abort);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
template <class Context, class Message>
|
|
108
|
+
auto threadsafe_function_of<Context, Message>::ref(node_api_basic_env env) const -> void {
|
|
109
|
+
napi::invoke0(napi_ref_threadsafe_function, env, tsfn_);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
template <class Context, class Message>
|
|
113
|
+
auto threadsafe_function_of<Context, Message>::unref(node_api_basic_env env) const -> void {
|
|
114
|
+
napi::invoke0(napi_unref_threadsafe_function, env, tsfn_);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
template <class Context, class Message>
|
|
118
|
+
auto threadsafe_function_of<Context, Message>::operator()(Message message) const noexcept -> bool {
|
|
119
|
+
auto check = [ & ](napi_status status) {
|
|
120
|
+
switch (status) {
|
|
121
|
+
case napi_ok:
|
|
122
|
+
return true;
|
|
123
|
+
case napi_queue_full:
|
|
124
|
+
return false;
|
|
125
|
+
case napi_closing:
|
|
126
|
+
// nb: This is undocumented, but if you got this return it means the internal ref count was
|
|
127
|
+
// decremented.
|
|
128
|
+
this->tsfn_ = nullptr;
|
|
129
|
+
return false;
|
|
130
|
+
case napi_invalid_arg:
|
|
131
|
+
std::terminate();
|
|
132
|
+
default:
|
|
133
|
+
std::unreachable();
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
if constexpr (trivial_message) {
|
|
137
|
+
void* data{};
|
|
138
|
+
std::memcpy(&data, &message, sizeof(Message));
|
|
139
|
+
return check(napi_call_threadsafe_function(tsfn_, data, napi_tsfn_nonblocking));
|
|
140
|
+
} else {
|
|
141
|
+
auto message_ptr = std::make_unique<Message>(std::move(message));
|
|
142
|
+
auto result = check(napi_call_threadsafe_function(tsfn_, message_ptr.get(), napi_tsfn_nonblocking));
|
|
143
|
+
if (result) {
|
|
144
|
+
message_ptr.release();
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
template <class Context, class Message>
|
|
151
|
+
auto threadsafe_function_of<Context, Message>::get() const noexcept -> storage& {
|
|
152
|
+
auto* context = napi::invoke_noexcept(napi_get_threadsafe_function_context, tsfn_);
|
|
153
|
+
return *reinterpret_cast<storage*>(context);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
} // namespace js::napi
|
package/api/uv_dlib.cc
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
module napi_js;
|
|
2
2
|
import :api.uv_dlib;
|
|
3
|
+
import :support.host;
|
|
3
4
|
import util;
|
|
4
5
|
|
|
5
6
|
namespace js::napi {
|
|
6
7
|
|
|
7
8
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
|
8
9
|
uv_dlib::uv_dlib(const std::string& filename) {
|
|
9
|
-
if (
|
|
10
|
-
throw js::runtime_error{util::transcode_string<char16_t>(std::string_view{
|
|
10
|
+
if (host_uv_dlopen(filename.c_str(), &lib_) != 0) {
|
|
11
|
+
throw js::runtime_error{util::transcode_string<char16_t>(std::string_view{host_uv_dlerror(&lib_)})};
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -20,7 +21,7 @@ auto uv_dlib::operator=(uv_dlib&& right) noexcept -> uv_dlib& {
|
|
|
20
21
|
|
|
21
22
|
uv_dlib::~uv_dlib() {
|
|
22
23
|
if (lib_.handle != nullptr) {
|
|
23
|
-
|
|
24
|
+
host_uv_dlclose(&lib_);
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
|
package/handle/bound_value.cc
CHANGED
|
@@ -8,6 +8,7 @@ namespace js::napi {
|
|
|
8
8
|
template <class Tag>
|
|
9
9
|
class bound_value_next : public bound_value<typename Tag::tag_type> {
|
|
10
10
|
public:
|
|
11
|
+
using tag_type = Tag;
|
|
11
12
|
using bound_value<typename Tag::tag_type>::bound_value;
|
|
12
13
|
bound_value_next() = default;
|
|
13
14
|
bound_value_next(napi_env env, value_of<Tag> value) :
|
package/handle/reference.cc
CHANGED
|
@@ -14,7 +14,7 @@ class reference_handle : util::non_copyable {
|
|
|
14
14
|
env_{env},
|
|
15
15
|
ref_{napi::invoke(napi_create_reference, env, value, 1)} {}
|
|
16
16
|
|
|
17
|
-
[[nodiscard]] auto env() const ->
|
|
17
|
+
[[nodiscard]] auto env() const -> node_api_basic_env {
|
|
18
18
|
return env_;
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -64,7 +64,7 @@ class reference_handle : util::non_copyable {
|
|
|
64
64
|
explicit operator bool() const { return ref_ != nullptr; }
|
|
65
65
|
|
|
66
66
|
private:
|
|
67
|
-
|
|
67
|
+
node_api_basic_env env_{};
|
|
68
68
|
napi_ref ref_{};
|
|
69
69
|
};
|
|
70
70
|
|
package/handle/remote.cc
CHANGED
|
@@ -9,7 +9,7 @@ namespace js::napi {
|
|
|
9
9
|
template <class Type>
|
|
10
10
|
concept remote_handle_environment =
|
|
11
11
|
std::is_base_of_v<environment, Type> &&
|
|
12
|
-
std::is_base_of_v<
|
|
12
|
+
std::is_base_of_v<napi_schedulable, Type>;
|
|
13
13
|
|
|
14
14
|
// Thread safe persistent value reference
|
|
15
15
|
export template <class Tag>
|
|
@@ -23,7 +23,8 @@ class remote : protected reference_handle {
|
|
|
23
23
|
public:
|
|
24
24
|
using unique_remote = std::unique_ptr<remote, util::function_constant<expire>>;
|
|
25
25
|
|
|
26
|
-
remote(
|
|
26
|
+
remote() = default;
|
|
27
|
+
remote(private_constructor /*private*/, const environment& env, value_of<Tag> value, napi_scheduler scheduler) :
|
|
27
28
|
reference_handle{napi_env{env}, napi_value{value}},
|
|
28
29
|
scheduler_{std::move(scheduler)} {}
|
|
29
30
|
|
|
@@ -33,7 +34,7 @@ class remote : protected reference_handle {
|
|
|
33
34
|
static auto make_unique(remote_handle_environment auto& env, value_of<Tag> value) -> unique_remote;
|
|
34
35
|
|
|
35
36
|
private:
|
|
36
|
-
|
|
37
|
+
napi_scheduler scheduler_;
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
// Convenience helpers
|
|
@@ -59,8 +60,7 @@ template <class Tag>
|
|
|
59
60
|
auto remote<Tag>::expire(remote* ptr) -> void {
|
|
60
61
|
std::unique_ptr<remote> self{ptr};
|
|
61
62
|
ptr->scheduler_(
|
|
62
|
-
[](auto self) -> void {
|
|
63
|
-
const auto scope = handle_scope{self->env()};
|
|
63
|
+
[](napi_env /*env*/, napi_value /*nothing*/, auto self) -> void {
|
|
64
64
|
self.reset();
|
|
65
65
|
},
|
|
66
66
|
std::move(self)
|
package/handle/types.cc
CHANGED
|
@@ -63,6 +63,16 @@ struct value_specialization<boolean_tag> : value_defaults<boolean_tag> {
|
|
|
63
63
|
using bound_type = class bound_value_for_boolean;
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
+
template <>
|
|
67
|
+
struct value_specialization<false_tag> : value_defaults<false_tag> {
|
|
68
|
+
using bound_type = class bound_value_for_false;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
template <>
|
|
72
|
+
struct value_specialization<true_tag> : value_defaults<true_tag> {
|
|
73
|
+
using bound_type = class bound_value_for_true;
|
|
74
|
+
};
|
|
75
|
+
|
|
66
76
|
template <>
|
|
67
77
|
struct value_specialization<number_tag> : value_defaults<number_tag> {
|
|
68
78
|
using bound_type = class bound_value_for_number;
|
package/handle/value.cc
CHANGED
|
@@ -26,6 +26,11 @@ class value_next : public value_of<typename Tag::tag_type> {
|
|
|
26
26
|
static auto from(napi_value value_) -> value_of<Tag> { return std::bit_cast<value_of<Tag>>(value_); }
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
// Deduction guide for `value_of{bound_value}`
|
|
30
|
+
template <class Type>
|
|
31
|
+
requires requires { typename Type::tag_type; }
|
|
32
|
+
value_of(Type value) -> value_of<typename Type::tag_type>;
|
|
33
|
+
|
|
29
34
|
// Tagged napi_value
|
|
30
35
|
export template <class Tag>
|
|
31
36
|
class value_of : public value_specialization<Tag>::value_type {
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@auto_js/napi",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"author": "https://github.com/laverdet/",
|
|
5
5
|
"description": "Automatic JavaScript to N-API bindings",
|
|
6
6
|
"homepage": "https://github.com/laverdet/isolated-vm/tree/experimental/packages/auto_js/napi",
|
|
7
7
|
"license": "ISC",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@auto_js/js": "^0.0.
|
|
10
|
-
"@auto_js/napi_exports": "^0.0.
|
|
11
|
-
"@auto_js/v8_exports": "^0.0.
|
|
9
|
+
"@auto_js/js": "^0.0.8",
|
|
10
|
+
"@auto_js/napi_exports": "^0.0.8",
|
|
11
|
+
"@auto_js/v8_exports": "^0.0.6"
|
|
12
12
|
},
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
package/support/callback.cc
CHANGED
|
@@ -96,7 +96,7 @@ constexpr auto make_constructor_function(auto constructor) {
|
|
|
96
96
|
// Tag the result
|
|
97
97
|
napi::invoke0(napi_type_tag_object, napi_env{env}, info.this_arg(), &type_tag_for<Type>);
|
|
98
98
|
// Wrap w/ finalizer
|
|
99
|
-
return apply_finalizer(std::move(instance), [ & ](Type* instance,
|
|
99
|
+
return apply_finalizer(std::move(instance), [ & ](Type* instance, node_api_basic_finalize finalize, void* hint) -> napi_value {
|
|
100
100
|
napi::invoke0(napi_wrap, napi_env{env}, info.this_arg(), instance, finalize, hint, nullptr);
|
|
101
101
|
return info.this_arg();
|
|
102
102
|
});
|
package/support/environment.cc
CHANGED
|
@@ -1,45 +1,26 @@
|
|
|
1
1
|
module napi_js;
|
|
2
2
|
import :api;
|
|
3
|
-
import :
|
|
3
|
+
import :support.host;
|
|
4
4
|
import util;
|
|
5
5
|
|
|
6
6
|
namespace js::napi {
|
|
7
7
|
|
|
8
|
-
environment::environment(napi_env env
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
auto
|
|
17
|
-
auto& env = *static_cast<environment*>(data);
|
|
8
|
+
environment::environment(napi_env env) :
|
|
9
|
+
napi_schedulable{env},
|
|
10
|
+
env_{env} {
|
|
11
|
+
// Initialize napi_js platform hooks
|
|
12
|
+
initialize_host_environment(env);
|
|
13
|
+
|
|
14
|
+
// nb: Closing the schedule needs to be the first thing we do, and deleting it needs to be the
|
|
15
|
+
// last thing we do. Otherwise we get deadlocks or use-after-free problems.
|
|
16
|
+
auto release_scheduler = [](napi_async_cleanup_hook_handle handle, void* data) -> void {
|
|
18
17
|
auto finalizer = util::fn<[](napi_async_cleanup_hook_handle handle) -> void {
|
|
19
18
|
napi::invoke0_noexcept(napi_remove_async_cleanup_hook, handle);
|
|
20
19
|
}>;
|
|
20
|
+
auto& env = *static_cast<environment*>(data);
|
|
21
21
|
env.scheduler().close(util::function_ref{finalizer, handle});
|
|
22
22
|
};
|
|
23
|
-
napi::invoke0_noexcept(napi_add_async_cleanup_hook, env,
|
|
23
|
+
napi::invoke0_noexcept(napi_add_async_cleanup_hook, env, release_scheduler, this, &cleanup_hook_handle_);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
environment::environment(napi_env env) :
|
|
27
|
-
environment{
|
|
28
|
-
env,
|
|
29
|
-
[ & ]() -> bool {
|
|
30
|
-
const auto direct_equal = direct_address_equal{};
|
|
31
|
-
const auto indirect_equal = indirect_address_equal{};
|
|
32
|
-
auto* array = napi::invoke(napi_create_array_with_length, env, 1);
|
|
33
|
-
napi::invoke0(napi_set_element, env, array, 0, array);
|
|
34
|
-
auto* result = napi::invoke(napi_get_element, env, array, 0);
|
|
35
|
-
if (direct_equal(array, result)) {
|
|
36
|
-
return true;
|
|
37
|
-
} else if (indirect_equal(array, result)) {
|
|
38
|
-
return false;
|
|
39
|
-
} else {
|
|
40
|
-
throw std::runtime_error{"Exotic napi handle behavior detected"};
|
|
41
|
-
}
|
|
42
|
-
}()
|
|
43
|
-
} {}
|
|
44
|
-
|
|
45
26
|
} // namespace js::napi
|
package/support/environment.h.cc
CHANGED
|
@@ -9,17 +9,12 @@ namespace js::napi {
|
|
|
9
9
|
|
|
10
10
|
// A reference to an environment is used as the lock witness. Generally, you should not have an
|
|
11
11
|
// `environment&` unless you're in the napi thread and locked.
|
|
12
|
-
export class environment : util::non_moveable, public
|
|
13
|
-
private:
|
|
14
|
-
environment(napi_env env, bool uses_direct_handles);
|
|
15
|
-
|
|
12
|
+
export class environment : util::non_moveable, public napi_schedulable {
|
|
16
13
|
public:
|
|
17
14
|
explicit environment(napi_env env);
|
|
18
15
|
|
|
19
16
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
20
17
|
[[nodiscard]] operator napi_env() const { return env_; }
|
|
21
|
-
[[nodiscard]] auto address_equal() const -> const auto& { return address_equal_; }
|
|
22
|
-
[[nodiscard]] auto uses_direct_handles() const -> bool { return uses_direct_handles_; }
|
|
23
18
|
|
|
24
19
|
template <class Type>
|
|
25
20
|
static auto make_and_set_environment(napi_env env, auto&&... args) -> Type&
|
|
@@ -39,8 +34,6 @@ export class environment : util::non_moveable, public uv_schedulable {
|
|
|
39
34
|
private:
|
|
40
35
|
napi_env env_;
|
|
41
36
|
napi_async_cleanup_hook_handle cleanup_hook_handle_{};
|
|
42
|
-
napi::address_equal address_equal_;
|
|
43
|
-
bool uses_direct_handles_;
|
|
44
37
|
};
|
|
45
38
|
|
|
46
39
|
} // namespace js::napi
|