@auto_js/napi 0.0.1
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 +55 -0
- package/LICENSE +13 -0
- package/README.md +255 -0
- package/_module.cc +19 -0
- package/api/api.cc +10 -0
- package/api/callback_info.cc +41 -0
- package/api/finalizer.cc +44 -0
- package/api/handle_scope.cc +23 -0
- package/api/invoke.cc +99 -0
- package/api/uv_handle.cc +113 -0
- package/api/uv_scheduler.cc +56 -0
- package/api/uv_scheduler.h.cc +73 -0
- package/handle/bound_value.cc +49 -0
- package/handle/reference.cc +93 -0
- package/handle/remote.cc +92 -0
- package/handle/types.cc +136 -0
- package/handle/value.cc +52 -0
- package/include/napi_js_initialize.h +7 -0
- package/package.json +16 -0
- package/support/callback.cc +266 -0
- package/support/callback_storage.cc +64 -0
- package/support/class_template_storage.cc +62 -0
- package/support/container.cc +78 -0
- package/support/environment.cc +33 -0
- package/support/environment.h.cc +56 -0
- package/support/environment_fwd.cc +15 -0
- package/support/error_scope.cc +22 -0
- package/support/initialize.cc +25 -0
- package/support/initialize.h.cc +48 -0
- package/support/promise.cc +130 -0
- package/support/string_table.cc +45 -0
- package/support/utility.cc +76 -0
- package/transfer/accept.cc +349 -0
- package/transfer/visit.cc +268 -0
- package/upload/macro.jpg +0 -0
- package/value/array.cc +32 -0
- package/value/array.h.cc +55 -0
- package/value/class.cc +147 -0
- package/value/class.h.cc +32 -0
- package/value/dictionary.cc +33 -0
- package/value/dictionary.h.cc +56 -0
- package/value/external.cc +54 -0
- package/value/function.cc +52 -0
- package/value/function.h.cc +30 -0
- package/value/object.cc +30 -0
- package/value/object.h.cc +59 -0
- package/value/primitive.cc +160 -0
- package/value/primitive.h.cc +101 -0
- package/value/value.cc +10 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <concepts>
|
|
3
|
+
#include <expected>
|
|
4
|
+
#include <tuple>
|
|
5
|
+
#include <utility>
|
|
6
|
+
export module napi_js:promise;
|
|
7
|
+
import :api;
|
|
8
|
+
import :environment;
|
|
9
|
+
import :value;
|
|
10
|
+
import util;
|
|
11
|
+
|
|
12
|
+
namespace js::napi {
|
|
13
|
+
|
|
14
|
+
// Resolver for `make_promise`
|
|
15
|
+
export template <class Environment, class Dispatch>
|
|
16
|
+
class resolver {
|
|
17
|
+
public:
|
|
18
|
+
resolver(Environment& env, napi_deferred deferred, Dispatch dispatch = {}) :
|
|
19
|
+
env_{env},
|
|
20
|
+
deferred_{deferred},
|
|
21
|
+
scheduler_{env.scheduler()},
|
|
22
|
+
dispatch_{dispatch} {
|
|
23
|
+
scheduler_.increment_ref();
|
|
24
|
+
}
|
|
25
|
+
resolver(const resolver&) = delete;
|
|
26
|
+
resolver(resolver&&) = default;
|
|
27
|
+
~resolver() {
|
|
28
|
+
if (scheduler_) {
|
|
29
|
+
resolve(std::monostate{});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
auto operator=(const resolver&) -> resolver& = delete;
|
|
34
|
+
auto operator=(resolver&&) -> resolver& = default;
|
|
35
|
+
|
|
36
|
+
// Fulfill using callback supplied to constructor
|
|
37
|
+
auto operator()(auto&&... args) -> void
|
|
38
|
+
requires std::invocable<Dispatch&, Environment&, decltype(args)...> {
|
|
39
|
+
using result_type = std::invoke_result_t<Dispatch&, Environment&, decltype(args)...>;
|
|
40
|
+
using expected_type = std::expected<result_type, js::error_value>;
|
|
41
|
+
schedule(
|
|
42
|
+
[](Environment& environment, auto dispatch, auto&&... args) -> expected_type {
|
|
43
|
+
return expected_type{std::in_place, std::move(dispatch)(environment, std::forward<decltype(args)>(args)...)};
|
|
44
|
+
},
|
|
45
|
+
std::move(dispatch_),
|
|
46
|
+
std::forward<decltype(args)>(args)...
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Fulfill using direct resolution value. Note it still needs try/catch (in `fulfill`) because
|
|
51
|
+
// the `transfer` operation could fail.
|
|
52
|
+
auto resolve(auto value) -> void {
|
|
53
|
+
using expected_type = std::expected<decltype(value), js::error_value>;
|
|
54
|
+
schedule(
|
|
55
|
+
[](Environment& /*env*/, auto value) -> expected_type {
|
|
56
|
+
return expected_type{std::in_place, std::move(value)};
|
|
57
|
+
},
|
|
58
|
+
std::move(value)
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Fulfill using thrown error.
|
|
63
|
+
auto reject(js::error_value error) -> void {
|
|
64
|
+
using expected_type = std::expected<std::monostate, js::error_value>;
|
|
65
|
+
schedule(
|
|
66
|
+
[](Environment& /*env*/, js::error_value error) -> expected_type {
|
|
67
|
+
return expected_type{std::unexpect, std::move(error)};
|
|
68
|
+
},
|
|
69
|
+
std::move(error)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private:
|
|
74
|
+
auto schedule(auto operation, auto&&... args) -> void {
|
|
75
|
+
auto scheduler = std::move(scheduler_);
|
|
76
|
+
scheduler(
|
|
77
|
+
[](napi_env env, napi_deferred deferred, auto operation, auto&&... args) {
|
|
78
|
+
auto& environment = napi::environment::unsafe_get_environment_as<Environment>(env);
|
|
79
|
+
environment.scheduler().decrement_ref();
|
|
80
|
+
const auto scope = js::napi::handle_scope{env};
|
|
81
|
+
try {
|
|
82
|
+
auto expected = operation(environment, std::forward<decltype(args)>(args)...);
|
|
83
|
+
if (expected) {
|
|
84
|
+
auto value = js::transfer_in_strict<napi_value>(*std::move(expected), environment);
|
|
85
|
+
napi::invoke0(napi_resolve_deferred, env, deferred, value);
|
|
86
|
+
} else {
|
|
87
|
+
auto error = js::transfer_in_strict<napi_value>(std::move(expected).error(), environment);
|
|
88
|
+
napi::invoke0(napi_reject_deferred, env, deferred, error);
|
|
89
|
+
}
|
|
90
|
+
} catch (const napi::pending_error& /*error*/) {
|
|
91
|
+
auto* exception = napi::invoke(napi_get_and_clear_last_exception, env);
|
|
92
|
+
napi::invoke0(napi_reject_deferred, env, deferred, exception);
|
|
93
|
+
} catch (const js::error& error) {
|
|
94
|
+
auto exception = js::transfer_in_strict<napi_value>(error, environment);
|
|
95
|
+
napi::invoke0(napi_reject_deferred, env, deferred, exception);
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
env_,
|
|
99
|
+
deferred_,
|
|
100
|
+
std::move(operation),
|
|
101
|
+
std::forward<decltype(args)>(args)...
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
napi_env env_;
|
|
106
|
+
napi_deferred deferred_;
|
|
107
|
+
uv_scheduler scheduler_;
|
|
108
|
+
Dispatch dispatch_;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
template <class Environment>
|
|
112
|
+
resolver(Environment&, napi_deferred) -> resolver<Environment, std::monostate>;
|
|
113
|
+
|
|
114
|
+
export template <class Environment>
|
|
115
|
+
auto make_promise(Environment& env, auto... dispatch) {
|
|
116
|
+
// Make nodejs promise & future
|
|
117
|
+
// NOLINTNEXTLINE(cppcoreguidelines-init-variables)
|
|
118
|
+
napi_deferred deferred;
|
|
119
|
+
// NOLINTNEXTLINE(cppcoreguidelines-init-variables)
|
|
120
|
+
napi_value promise;
|
|
121
|
+
napi::invoke0(napi_create_promise, napi_env{env}, &deferred, &promise);
|
|
122
|
+
|
|
123
|
+
// Make resolve helper
|
|
124
|
+
auto resolve = resolver{env, deferred, std::move(dispatch)...};
|
|
125
|
+
|
|
126
|
+
// `[ promise, resolver ]`
|
|
127
|
+
return std::tuple{value<promise_tag>::from(promise), std::move(resolve)};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
} // namespace js::napi
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <string> // IWYU pragma: keep
|
|
3
|
+
#include <type_traits>
|
|
4
|
+
export module napi_js:string_table;
|
|
5
|
+
import :reference;
|
|
6
|
+
import auto_js;
|
|
7
|
+
import util;
|
|
8
|
+
using namespace std::string_literals;
|
|
9
|
+
|
|
10
|
+
namespace js::napi {
|
|
11
|
+
|
|
12
|
+
// Given a pack of strings it returns a static reference to a `util::sealed_map` of napi string
|
|
13
|
+
// references.
|
|
14
|
+
template <const auto& Strings>
|
|
15
|
+
constexpr auto string_table_of = []() -> auto {
|
|
16
|
+
const auto [... strings ] = Strings;
|
|
17
|
+
return util::sealed_map{std::type_identity<napi::reference<string_tag>>{}, strings...};
|
|
18
|
+
}();
|
|
19
|
+
|
|
20
|
+
// You can turn off `strict` if you don't care about a string table
|
|
21
|
+
struct string_table_options {
|
|
22
|
+
bool strict = true;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Environment storage for string literals
|
|
26
|
+
export template <const auto& Strings, string_table_options Options = {}>
|
|
27
|
+
class string_table {
|
|
28
|
+
public:
|
|
29
|
+
auto string_table_storage(const auto& string_value) {
|
|
30
|
+
constexpr auto string_sv = util::consteval_string_view{string_value};
|
|
31
|
+
constexpr auto index = string_table_of<Strings>.lookup(string_sv);
|
|
32
|
+
if constexpr (index) {
|
|
33
|
+
return util::just<napi::reference<string_tag>&>{string_literal_storage_.at(index).second};
|
|
34
|
+
} else {
|
|
35
|
+
// static_assert(!Options.strict, std::format("String literal '{}' is missing in storage", string_sv));
|
|
36
|
+
static_assert(!Options.strict, "String literal '"s + string_sv + "' is missing in storage"s);
|
|
37
|
+
return util::nothing<napi::reference<string_tag>&>{};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private:
|
|
42
|
+
util::copy_of<string_table_of<Strings>> string_literal_storage_;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
} // namespace js::napi
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <functional>
|
|
3
|
+
export module napi_js:utility;
|
|
4
|
+
import :environment;
|
|
5
|
+
import nodejs;
|
|
6
|
+
import util;
|
|
7
|
+
|
|
8
|
+
namespace js::napi {
|
|
9
|
+
|
|
10
|
+
// See: `v8_enable_direct_handle`.
|
|
11
|
+
|
|
12
|
+
// Null handle for both direct and indirect handles
|
|
13
|
+
// Note: 1 -> 0x1'0000'0000
|
|
14
|
+
const void* null_value_handle_ = nullptr;
|
|
15
|
+
export auto* const null_value_handle = reinterpret_cast<napi_value>(&null_value_handle_);
|
|
16
|
+
|
|
17
|
+
// Address equality for underlying napi handles.
|
|
18
|
+
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
|
|
19
|
+
export struct virtual_address_equal {
|
|
20
|
+
protected:
|
|
21
|
+
~virtual_address_equal() = default;
|
|
22
|
+
|
|
23
|
+
public:
|
|
24
|
+
virtual auto operator()(napi_value left, napi_value right) const -> bool = 0;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export struct direct_address_equal final : virtual_address_equal {
|
|
28
|
+
auto operator()(napi_value left, napi_value right) const -> bool final {
|
|
29
|
+
return left == right;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export struct indirect_address_equal final : virtual_address_equal {
|
|
34
|
+
auto operator()(napi_value left, napi_value right) const -> bool final {
|
|
35
|
+
auto* indirect_left = reinterpret_cast<void**>(left);
|
|
36
|
+
auto* indirect_right = reinterpret_cast<void**>(right);
|
|
37
|
+
return *indirect_left == *indirect_right;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Equality comparator which respects napi's handle types
|
|
42
|
+
export class address_equal {
|
|
43
|
+
private:
|
|
44
|
+
using virtual_equal_type = util::covariant_value<virtual_address_equal, direct_address_equal, indirect_address_equal>;
|
|
45
|
+
|
|
46
|
+
public:
|
|
47
|
+
explicit address_equal(const environment& env) :
|
|
48
|
+
equal_{
|
|
49
|
+
env.uses_direct_handles()
|
|
50
|
+
? virtual_equal_type{direct_address_equal{}}
|
|
51
|
+
: virtual_equal_type{indirect_address_equal{}}
|
|
52
|
+
} {}
|
|
53
|
+
|
|
54
|
+
auto operator()(napi_value left, napi_value right) const -> bool {
|
|
55
|
+
return (*equal_)(left, right);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private:
|
|
59
|
+
virtual_equal_type equal_;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Address hash for napi value handles.
|
|
63
|
+
export struct direct_address_hash : std::hash<void*> {};
|
|
64
|
+
|
|
65
|
+
export struct indirect_address_hash : std::hash<void*> {
|
|
66
|
+
private:
|
|
67
|
+
using std::hash<void*>::operator();
|
|
68
|
+
|
|
69
|
+
public:
|
|
70
|
+
auto operator()(napi_value value) const noexcept -> std::size_t {
|
|
71
|
+
auto* indirect_handle = reinterpret_cast<void**>(value);
|
|
72
|
+
return (*this)(*indirect_handle);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
} // namespace js::napi
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <concepts>
|
|
3
|
+
#include <string_view>
|
|
4
|
+
#include <tuple>
|
|
5
|
+
#include <utility>
|
|
6
|
+
#include <variant>
|
|
7
|
+
#include <vector>
|
|
8
|
+
export module napi_js:accept;
|
|
9
|
+
import :api;
|
|
10
|
+
import :container;
|
|
11
|
+
import :environment_fwd;
|
|
12
|
+
import :value;
|
|
13
|
+
import util;
|
|
14
|
+
|
|
15
|
+
namespace js {
|
|
16
|
+
using namespace napi;
|
|
17
|
+
|
|
18
|
+
// Generic napi acceptor which accepts all value types.
|
|
19
|
+
template <class Environment>
|
|
20
|
+
struct accept_napi_value;
|
|
21
|
+
|
|
22
|
+
template <class Meta>
|
|
23
|
+
using accept_napi_value_with = accept_napi_value<typename Meta::accept_context_type>;
|
|
24
|
+
|
|
25
|
+
template <class Environment>
|
|
26
|
+
struct accept_napi_value : napi::environment_scope<Environment> {
|
|
27
|
+
public:
|
|
28
|
+
// nb: This marks this acceptor as referential!
|
|
29
|
+
using accept_reference_type = napi_value;
|
|
30
|
+
|
|
31
|
+
using napi::environment_scope<Environment>::environment;
|
|
32
|
+
|
|
33
|
+
explicit accept_napi_value(auto* /*transfer*/, auto& env) :
|
|
34
|
+
napi::environment_scope<Environment>{env} {}
|
|
35
|
+
|
|
36
|
+
// reference provider
|
|
37
|
+
constexpr auto operator()(std::type_identity<napi_value> /*type*/, napi_value value) const -> napi_value { return value; }
|
|
38
|
+
|
|
39
|
+
template <class Tag>
|
|
40
|
+
constexpr auto operator()(std::type_identity<napi::value<Tag>> /*type*/, napi_value value) const -> napi::value<Tag> {
|
|
41
|
+
return napi::value<Tag>::from(value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// undefined & null
|
|
45
|
+
auto operator()(undefined_tag /*tag*/, visit_holder /*visit*/, const auto& /*undefined*/) const -> napi::value<undefined_tag> {
|
|
46
|
+
return napi::value<undefined_tag>::make(environment(), std::monostate{});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
auto operator()(null_tag /*tag*/, visit_holder /*visit*/, const auto& /*null*/) const -> napi::value<null_tag> {
|
|
50
|
+
return napi::value<null_tag>::make(environment(), nullptr);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// boolean
|
|
54
|
+
auto operator()(boolean_tag /*tag*/, visit_holder /*visit*/, auto&& subject) const -> napi::value<boolean_tag> {
|
|
55
|
+
return napi::value<boolean_tag>::make(environment(), bool{std::forward<decltype(subject)>(subject)});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// number
|
|
59
|
+
auto operator()(number_tag /*tag*/, visit_holder visit, auto&& subject) const -> napi::value<number_tag> {
|
|
60
|
+
return (*this)(number_tag_of<double>{}, visit, std::forward<decltype(subject)>(subject));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
template <class Numeric>
|
|
64
|
+
auto operator()(number_tag_of<Numeric> /*tag*/, visit_holder /*visit*/, auto&& subject) const -> napi::value<number_tag> {
|
|
65
|
+
return napi::value<number_tag>::make(environment(), Numeric{std::forward<decltype(subject)>(subject)});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// bigint
|
|
69
|
+
auto operator()(bigint_tag /*tag*/, visit_holder visit, auto&& subject) const
|
|
70
|
+
-> js::referenceable_value<napi::value<bigint_tag>> {
|
|
71
|
+
return (*this)(bigint_tag_of<bigint>{}, visit, std::forward<decltype(subject)>(subject));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
auto operator()(bigint_tag_of<bigint> /*tag*/, visit_holder /*visit*/, auto&& subject) const
|
|
75
|
+
-> js::referenceable_value<napi::value<bigint_tag>> {
|
|
76
|
+
return js::referenceable_value{napi::value<bigint_tag>::make(environment(), js::bigint{std::forward<decltype(subject)>(subject)})};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
auto operator()(bigint_tag_of<bigint> /*tag*/, visit_holder /*visit*/, const js::bigint& subject) const
|
|
80
|
+
-> js::referenceable_value<napi::value<bigint_tag>> {
|
|
81
|
+
return js::referenceable_value{napi::value<bigint_tag>::make(environment(), subject)};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
template <class Numeric>
|
|
85
|
+
auto operator()(bigint_tag_of<Numeric> /*tag*/, visit_holder /*visit*/, auto&& subject) const
|
|
86
|
+
-> js::referenceable_value<napi::value<bigint_tag>> {
|
|
87
|
+
return js::referenceable_value{napi::value<bigint_tag>::make(environment(), Numeric{std::forward<decltype(subject)>(subject)})};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// string
|
|
91
|
+
auto operator()(string_tag /*tag*/, visit_holder visit, auto&& subject) const
|
|
92
|
+
-> js::referenceable_value<napi::value<string_tag>> {
|
|
93
|
+
return (*this)(string_tag_of<char16_t>{}, visit, std::forward<decltype(subject)>(subject));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
template <class Char>
|
|
97
|
+
auto operator()(string_tag_of<Char> /*tag*/, visit_holder /*visit*/, std::convertible_to<std::basic_string_view<Char>> auto&& subject) const
|
|
98
|
+
-> js::referenceable_value<napi::value<string_tag>> {
|
|
99
|
+
return js::referenceable_value{napi::value<string_tag>::make(environment(), std::basic_string_view<Char>{std::forward<decltype(subject)>(subject)})};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
template <class Char>
|
|
103
|
+
auto operator()(string_tag_of<Char> tag, visit_holder visit, auto&& subject) const
|
|
104
|
+
-> js::referenceable_value<napi::value<string_tag>> {
|
|
105
|
+
return (*this)(tag, visit, std::basic_string<Char>{std::forward<decltype(subject)>(subject)});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// date
|
|
109
|
+
auto operator()(date_tag /*tag*/, visit_holder /*visit*/, auto&& subject) const
|
|
110
|
+
-> js::referenceable_value<napi::value<date_tag>> {
|
|
111
|
+
return js::referenceable_value{napi::value<date_tag>::make(environment(), js_clock::time_point{std::forward<decltype(subject)>(subject)})};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// function
|
|
115
|
+
template <class Callback>
|
|
116
|
+
auto operator()(function_tag /*tag*/, visit_holder /*visit*/, js::free_function<Callback> subject) const -> napi::value<function_tag> {
|
|
117
|
+
return napi::value<function_tag>::make(environment(), std::forward<decltype(subject)>(subject));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// error
|
|
121
|
+
auto operator()(error_tag /*tag*/, visit_holder /*visit*/, const auto& subject) const
|
|
122
|
+
-> js::referenceable_value<napi::value<error_tag>> {
|
|
123
|
+
auto* message = napi_value{napi::value<string_tag>::make(environment(), subject.message())};
|
|
124
|
+
auto error = [ & ]() -> napi_value {
|
|
125
|
+
switch (subject.name()) {
|
|
126
|
+
default:
|
|
127
|
+
return napi::invoke(napi_create_error, napi_env{*this}, napi_value{}, message);
|
|
128
|
+
case js::error::name_type::range_error:
|
|
129
|
+
return napi::invoke(napi_create_range_error, napi_env{*this}, napi_value{}, message);
|
|
130
|
+
case js::error::name_type::syntax_error:
|
|
131
|
+
return napi::invoke(node_api_create_syntax_error, napi_env{*this}, napi_value{}, message);
|
|
132
|
+
case js::error::name_type::type_error:
|
|
133
|
+
return napi::invoke(napi_create_type_error, napi_env{*this}, napi_value{}, message);
|
|
134
|
+
}
|
|
135
|
+
}();
|
|
136
|
+
return js::referenceable_value{napi::value<error_tag>::from(error)};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// vectors
|
|
140
|
+
auto operator()(this const auto& self, vector_tag /*tag*/, auto& visit, auto&& subject)
|
|
141
|
+
-> js::deferred_receiver<napi::value<vector_tag>, decltype(self), decltype(visit), decltype(subject)> {
|
|
142
|
+
return {
|
|
143
|
+
napi::value<vector_tag>::from(napi::invoke(napi_create_array_with_length, napi_env{self}, subject.size())),
|
|
144
|
+
std::forward_as_tuple(self, visit, std::forward<decltype(subject)>(subject)),
|
|
145
|
+
[](napi::value<vector_tag> array, auto& self, auto& visit, auto /*&&*/ subject) -> void {
|
|
146
|
+
int ii = 0;
|
|
147
|
+
auto&& range = util::into_range(std::forward<decltype(subject)>(subject));
|
|
148
|
+
for (auto&& subject : util::forward_range(std::forward<decltype(range)>(range))) {
|
|
149
|
+
auto* element = napi_value{visit(std::forward<decltype(subject)>(subject), self)};
|
|
150
|
+
napi::invoke0(napi_set_element, napi_env{self}, napi_value{array}, ii++, element);
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
template <std::size_t Size>
|
|
157
|
+
auto operator()(this const auto& self, tuple_tag<Size> /*tag*/, auto& visit, auto&& subject)
|
|
158
|
+
-> js::deferred_receiver<napi::value<vector_tag>, decltype(self), decltype(visit), decltype(subject)> {
|
|
159
|
+
return {
|
|
160
|
+
napi::value<vector_tag>::from(napi::invoke(napi_create_array_with_length, napi_env{self}, Size)),
|
|
161
|
+
std::forward_as_tuple(self, visit, std::forward<decltype(subject)>(subject)),
|
|
162
|
+
[](napi::value<vector_tag> array, auto& self, auto& visit, auto /*&&*/ tuple) -> void {
|
|
163
|
+
const auto [... indices ] = util::sequence<Size>;
|
|
164
|
+
(..., [ & ]() -> void {
|
|
165
|
+
// nb: This is forwarded to *each* visitor. The visitor should be aware and only lvalue
|
|
166
|
+
// reference members one at a time.
|
|
167
|
+
auto* element = napi_value{visit(indices, std::forward<decltype(tuple)>(tuple), self)};
|
|
168
|
+
napi::invoke0(napi_set_element, napi_env{self}, napi_value{array}, indices, element);
|
|
169
|
+
}());
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// arrays & dictionaries
|
|
175
|
+
auto operator()(this const auto& self, list_tag /*tag*/, auto& visit, auto&& subject)
|
|
176
|
+
-> js::deferred_receiver<napi::value<list_tag>, decltype(self), decltype(visit), decltype(subject)> {
|
|
177
|
+
return {
|
|
178
|
+
napi::value<list_tag>::from(napi::invoke(napi_create_array, napi_env{self})),
|
|
179
|
+
std::forward_as_tuple(self, visit, std::forward<decltype(subject)>(subject)),
|
|
180
|
+
[](napi::value<list_tag> array, auto& self, auto& visit, auto /*&&*/ subject) -> void {
|
|
181
|
+
self.accept_entry_pair_vector(visit, array, std::forward<decltype(subject)>(subject));
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
auto operator()(this const auto& self, dictionary_tag /*tag*/, auto& visit, auto&& subject)
|
|
187
|
+
-> js::deferred_receiver<napi::value<dictionary_tag>, decltype(self), decltype(visit), decltype(subject)> {
|
|
188
|
+
return {
|
|
189
|
+
napi::value<dictionary_tag>::from(napi::invoke(napi_create_object, napi_env{self})),
|
|
190
|
+
std::forward_as_tuple(self, visit, std::forward<decltype(subject)>(subject)),
|
|
191
|
+
[](napi::value<dictionary_tag> object, auto& self, auto& visit, auto /*&&*/ subject) -> void {
|
|
192
|
+
self.accept_entry_pair_vector(visit, object, std::forward<decltype(subject)>(subject));
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
auto accept_entry_pair_vector(this const auto& self, auto& visit, value<object_tag> target, auto&& subject) -> void {
|
|
198
|
+
std::vector<napi_property_descriptor> properties;
|
|
199
|
+
auto&& range = util::into_range(std::forward<decltype(subject)>(subject));
|
|
200
|
+
properties.reserve(std::size(range));
|
|
201
|
+
for (auto&& [ key, value ] : util::forward_range(std::forward<decltype(range)>(range))) {
|
|
202
|
+
properties.emplace_back(napi_property_descriptor{
|
|
203
|
+
.utf8name{},
|
|
204
|
+
.name = napi_value{visit.first(std::forward<decltype(key)>(key), self)},
|
|
205
|
+
|
|
206
|
+
.method{},
|
|
207
|
+
.getter{},
|
|
208
|
+
.setter{},
|
|
209
|
+
.value = napi_value{visit.second(std::forward<decltype(value)>(value), self)},
|
|
210
|
+
|
|
211
|
+
// NOLINTNEXTLINE(hicpp-signed-bitwise)
|
|
212
|
+
.attributes = static_cast<napi_property_attributes>(napi_writable | napi_enumerable | napi_configurable),
|
|
213
|
+
.data{},
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
napi::invoke0(napi_define_properties, napi_env{self}, napi_value{target}, properties.size(), properties.data());
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// structs
|
|
220
|
+
template <std::size_t Size>
|
|
221
|
+
auto operator()(this const auto& self, struct_tag<Size> /*tag*/, auto& visit, auto&& subject)
|
|
222
|
+
-> js::deferred_receiver<napi::value<dictionary_tag>, decltype(self), decltype(visit), decltype(subject)> {
|
|
223
|
+
return {
|
|
224
|
+
napi::value<dictionary_tag>::from(napi::invoke(napi_create_object, napi_env{self})),
|
|
225
|
+
std::forward_as_tuple(self, visit, std::forward<decltype(subject)>(subject)),
|
|
226
|
+
[](napi::value<dictionary_tag> object, auto& self, auto& visit, auto /*&&*/ subject) -> void {
|
|
227
|
+
self.template accept_entry_pair_struct<Size>(visit, object, std::forward<decltype(subject)>(subject));
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
template <std::size_t Size>
|
|
233
|
+
auto accept_entry_pair_struct(this const auto& self, auto& visit, value<object_tag> target, auto&& subject) -> void {
|
|
234
|
+
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
|
235
|
+
std::array<napi_property_descriptor, Size> properties;
|
|
236
|
+
const auto [... indices ] = util::sequence<Size>;
|
|
237
|
+
(..., [ & ]() -> void {
|
|
238
|
+
// nb: This is forwarded to *each* visitor. The visitor should be aware and only lvalue
|
|
239
|
+
// reference members one at a time.
|
|
240
|
+
auto accept_entry = accept_entry_pair<decltype(self), decltype(self)>{self};
|
|
241
|
+
auto entry = visit(std::integral_constant<std::size_t, indices>{}, std::forward<decltype(subject)>(subject), accept_entry);
|
|
242
|
+
// NOLINTNEXTLINE(modernize-type-traits)
|
|
243
|
+
properties.at(indices) = {
|
|
244
|
+
.utf8name{},
|
|
245
|
+
.name = entry.first,
|
|
246
|
+
|
|
247
|
+
.method{},
|
|
248
|
+
.getter{},
|
|
249
|
+
.setter{},
|
|
250
|
+
.value = entry.second,
|
|
251
|
+
|
|
252
|
+
// NOLINTNEXTLINE(hicpp-signed-bitwise)
|
|
253
|
+
.attributes = static_cast<napi_property_attributes>(napi_writable | napi_enumerable | napi_configurable),
|
|
254
|
+
.data{},
|
|
255
|
+
};
|
|
256
|
+
}());
|
|
257
|
+
napi::invoke0(napi_define_properties, napi_env{self}, napi_value{target}, properties.size(), properties.data());
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// no required types
|
|
261
|
+
consteval static auto types(auto /*recursive*/) { return util::type_pack{}; }
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// Plain napi_value acceptor
|
|
265
|
+
template <>
|
|
266
|
+
struct accept_property_subject<napi_value> : std::type_identity<napi_value> {};
|
|
267
|
+
|
|
268
|
+
template <class Meta>
|
|
269
|
+
struct accept<Meta, napi_value> : accept_napi_value_with<Meta> {
|
|
270
|
+
using accept_type = accept_napi_value_with<Meta>;
|
|
271
|
+
using accept_type::accept_type;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// Tagged `napi::value<T>` acceptor
|
|
275
|
+
template <>
|
|
276
|
+
struct accept_property_subject<napi::value<value_tag>> : std::type_identity<napi_value> {};
|
|
277
|
+
|
|
278
|
+
template <class Meta>
|
|
279
|
+
struct accept<Meta, napi::value<value_tag>> : accept_napi_value_with<Meta> {
|
|
280
|
+
using accept_type = accept_napi_value_with<Meta>;
|
|
281
|
+
using accept_type::accept_type;
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// Forwarded `napi::value<T>` acceptor
|
|
285
|
+
template <class Tag>
|
|
286
|
+
struct accept<void, js::forward<napi::value<Tag>, Tag>> {
|
|
287
|
+
auto operator()(Tag /*tag*/, visit_holder /*visit*/, napi::value<Tag> value) const -> js::forward<napi::value<Tag>, Tag> {
|
|
288
|
+
return js::forward{napi::value<Tag>{value}, Tag{}};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
consteval static auto types(auto /*recursive*/) { return util::type_pack{}; }
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// Object key lookup via napi
|
|
295
|
+
template <class Meta, auto Key, class Type>
|
|
296
|
+
struct accept_property_value<Meta, Key, Type, napi_value> {
|
|
297
|
+
public:
|
|
298
|
+
explicit constexpr accept_property_value(auto* transfer) :
|
|
299
|
+
first{transfer},
|
|
300
|
+
second{transfer} {}
|
|
301
|
+
|
|
302
|
+
auto operator()(dictionary_tag /*tag*/, auto& visit, const auto& object) const -> Type {
|
|
303
|
+
if (auto local = first(std::type_identity<void>{}, visit.first); object.has(local)) {
|
|
304
|
+
return visit.second(object.get(local), second);
|
|
305
|
+
} else {
|
|
306
|
+
return second(undefined_in_tag{}, visit.second, std::monostate{});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
private:
|
|
311
|
+
mutable visit_key_literal<Key, napi_value> first;
|
|
312
|
+
accept_value<Meta, Type> second;
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// `value<object_tag>{}.assign(...)` implementation
|
|
316
|
+
struct object_assign_delegate {
|
|
317
|
+
public:
|
|
318
|
+
explicit object_assign_delegate(value<object_tag> object) : object_{object} {}
|
|
319
|
+
auto operator*() const { return object_; }
|
|
320
|
+
|
|
321
|
+
private:
|
|
322
|
+
value<object_tag> object_;
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
template <class Meta>
|
|
326
|
+
struct accept<Meta, object_assign_delegate> {
|
|
327
|
+
public:
|
|
328
|
+
accept(auto* transfer, auto& env, object_assign_delegate object) :
|
|
329
|
+
accept_{transfer, env},
|
|
330
|
+
object_{object} {}
|
|
331
|
+
|
|
332
|
+
template <std::size_t Size>
|
|
333
|
+
auto operator()(this const auto& self, tuple_tag<Size> /*tag*/, auto& visit, auto&& subject) -> object_assign_delegate {
|
|
334
|
+
self.accept_.template accept_entry_pair_struct<Size>(visit, *self.object_, std::forward<decltype(subject)>(subject));
|
|
335
|
+
return self.object_;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
consteval static auto types(auto recursive) { return accept<Meta, napi_value>::types(recursive); }
|
|
339
|
+
|
|
340
|
+
private:
|
|
341
|
+
accept_value<Meta, napi_value> accept_;
|
|
342
|
+
object_assign_delegate object_;
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
auto value_for_object::assign(this value<object_tag> self, auto_environment auto& env, auto source) -> void {
|
|
346
|
+
js::transfer_in<object_assign_delegate>(std::move(source), env, object_assign_delegate{self});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
} // namespace js
|