@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,266 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <optional>
|
|
3
|
+
#include <tuple>
|
|
4
|
+
#include <type_traits>
|
|
5
|
+
#include <utility>
|
|
6
|
+
export module napi_js:callback;
|
|
7
|
+
export import :callback_storage;
|
|
8
|
+
import :api;
|
|
9
|
+
import :bound_value;
|
|
10
|
+
import :environment_fwd;
|
|
11
|
+
import :error_scope;
|
|
12
|
+
import :object;
|
|
13
|
+
import :value_handle;
|
|
14
|
+
import util;
|
|
15
|
+
|
|
16
|
+
namespace js::napi {
|
|
17
|
+
namespace {
|
|
18
|
+
|
|
19
|
+
// Unwrap `this` into the correct tagged external. Returns `std::nullopt` if there was a type error,
|
|
20
|
+
// and there is a napi exception pending. `nullptr` will not be returned.
|
|
21
|
+
template <class Type>
|
|
22
|
+
// NOLINTNEXTLINE(bugprone-exception-escape)
|
|
23
|
+
constexpr auto unwrap_member_this = [](auto_environment auto& env, napi_value this_arg) noexcept -> std::optional<tagged_external<Type>> {
|
|
24
|
+
return napi::invoke_maybe(napi_coerce_to_object, napi_env{env}, this_arg)
|
|
25
|
+
.and_then([ &env ](napi_value coerced_this_arg) -> std::optional<tagged_external<Type>> {
|
|
26
|
+
auto as_object = bound_value{env, value<object_tag>::from(coerced_this_arg)};
|
|
27
|
+
// Technically `try_cast` can throw but it should only be in catastrophic cases.
|
|
28
|
+
Type* unwrapped = as_object.try_cast(type<Type>);
|
|
29
|
+
if (unwrapped == nullptr) {
|
|
30
|
+
// nb: The failure here is "no tag" which means we need to set the napi exception
|
|
31
|
+
napi::invoke0(napi_throw_type_error, napi_env{env}, nullptr, "Invalid object type");
|
|
32
|
+
return std::nullopt;
|
|
33
|
+
} else {
|
|
34
|
+
return tagged_external{*unwrapped};
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
} // namespace
|
|
40
|
+
|
|
41
|
+
// Function type for internal host object construction
|
|
42
|
+
using internal_constructor = util::function_ref<auto(napi_value)->napi_value>;
|
|
43
|
+
|
|
44
|
+
// Make callback for plain free function
|
|
45
|
+
export template <auto_environment Environment>
|
|
46
|
+
constexpr auto make_free_function(auto function) {
|
|
47
|
+
constexpr auto make_with_try_catch =
|
|
48
|
+
[]<class Env, class... Args, bool Nx, class Result>(
|
|
49
|
+
std::type_identity<auto(Env, Args...) noexcept(Nx)->Result> /*signature*/,
|
|
50
|
+
auto callback
|
|
51
|
+
) -> auto {
|
|
52
|
+
using callback_type = decltype(callback);
|
|
53
|
+
return util::bind{
|
|
54
|
+
[](callback_type& callback, Environment& env, const callback_info& info) noexcept(Nx) -> napi_value {
|
|
55
|
+
return invoke_internal_error_scope(env, [ & ]() -> napi_value {
|
|
56
|
+
auto run = util::regular_return{[ & ]() -> decltype(auto) {
|
|
57
|
+
return std::apply(
|
|
58
|
+
callback,
|
|
59
|
+
std::tuple_cat(
|
|
60
|
+
std::forward_as_tuple(env),
|
|
61
|
+
js::transfer_out<std::tuple<Args...>>(info.arguments(), env)
|
|
62
|
+
)
|
|
63
|
+
);
|
|
64
|
+
}};
|
|
65
|
+
return js::transfer_in_strict<napi_value>(run().value_or(std::monostate{}), env);
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
std::move(callback),
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
constexpr auto make_noexcept =
|
|
72
|
+
[]<class Env, class Result>(
|
|
73
|
+
std::type_identity<auto(Env) noexcept -> Result> /*signature*/,
|
|
74
|
+
auto callback
|
|
75
|
+
) -> auto {
|
|
76
|
+
static_assert(false, "untested");
|
|
77
|
+
using callback_type = decltype(callback);
|
|
78
|
+
return util::bind{
|
|
79
|
+
[](callback_type& callback, Environment& env, const callback_info& /*info*/) noexcept -> napi_value {
|
|
80
|
+
auto run = util::regular_return{[ & ]() -> decltype(auto) {
|
|
81
|
+
return callback(env);
|
|
82
|
+
}};
|
|
83
|
+
return js::transfer_in_strict<napi_value>(run().value_or(std::monostate{}), env);
|
|
84
|
+
},
|
|
85
|
+
std::move(callback),
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
auto callback = js::functional::thunk_free_function<Environment&>(std::move(function));
|
|
90
|
+
constexpr auto make = util::overloaded{make_with_try_catch, make_noexcept};
|
|
91
|
+
return make(std::type_identity<util::function_signature_t<decltype(callback)>>{}, std::move(callback));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Make callback for constructor invocations
|
|
95
|
+
export template <auto_environment Environment, class Type>
|
|
96
|
+
constexpr auto make_constructor_function(auto constructor) {
|
|
97
|
+
// First, a lambda is created which will actually invoke the supplied constructor function.
|
|
98
|
+
constexpr auto make_invoke_runtime_constructor = util::overloaded{
|
|
99
|
+
[](std::nullptr_t /*constructor*/) -> auto {
|
|
100
|
+
return [](Environment& env, const callback_info& /*info*/) -> napi_value {
|
|
101
|
+
napi::invoke0(napi_throw_type_error, napi_env{env}, nullptr, "Illegal constructor");
|
|
102
|
+
return {};
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
[](auto constructor) -> auto {
|
|
106
|
+
// These are the last steps, common to both branches
|
|
107
|
+
constexpr auto wrap = [](auto instance, Environment& env, const callback_info& info) -> napi_value {
|
|
108
|
+
// Tag the result
|
|
109
|
+
napi::invoke0(napi_type_tag_object, napi_env{env}, info.this_arg(), &type_tag_for<Type>);
|
|
110
|
+
// Wrap w/ finalizer
|
|
111
|
+
return apply_finalizer(std::move(instance), [ & ](Type* instance, napi_finalize finalize, void* hint) -> napi_value {
|
|
112
|
+
napi::invoke0(napi_wrap, napi_env{env}, info.this_arg(), instance, finalize, hint, nullptr);
|
|
113
|
+
return info.this_arg();
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
using wrap_type = decltype(wrap);
|
|
117
|
+
|
|
118
|
+
constexpr auto make_with_try_catch =
|
|
119
|
+
[]<class Env, class... Args, bool Nx, class Result>(
|
|
120
|
+
std::type_identity<auto(Env, Args...) noexcept(Nx)->Result> /*signature*/,
|
|
121
|
+
auto callback
|
|
122
|
+
) -> auto {
|
|
123
|
+
static_assert(false, "untested");
|
|
124
|
+
using callback_type = decltype(callback);
|
|
125
|
+
return util::bind{
|
|
126
|
+
[](callback_type& callback, wrap_type& wrap, Environment& env, const callback_info& info) noexcept(Nx) -> napi_value {
|
|
127
|
+
return invoke_internal_error_scope(env, [ & ]() -> napi_value {
|
|
128
|
+
auto instance = std::apply(
|
|
129
|
+
callback,
|
|
130
|
+
std::tuple_cat(
|
|
131
|
+
std::forward_as_tuple(env, value<object_tag>::from(info.this_arg())),
|
|
132
|
+
js::transfer_out<std::tuple<Args...>>(info.arguments(), env)
|
|
133
|
+
)
|
|
134
|
+
);
|
|
135
|
+
return wrap(std::move(instance));
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
std::move(callback),
|
|
139
|
+
wrap,
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
constexpr auto make_noexcept =
|
|
143
|
+
[]<class Env, class Result>(
|
|
144
|
+
std::type_identity<auto(Env) noexcept -> Result> /*signature*/,
|
|
145
|
+
auto callback
|
|
146
|
+
) -> auto {
|
|
147
|
+
static_assert(false, "untested");
|
|
148
|
+
using callback_type = decltype(callback);
|
|
149
|
+
return util::bind{
|
|
150
|
+
[](callback_type& callback, Environment& env, const callback_info& info) noexcept -> napi_value {
|
|
151
|
+
return wrap(callback(env, value<object_tag>::from(info.this_arg())));
|
|
152
|
+
},
|
|
153
|
+
std::move(callback),
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
auto callback = js::functional::thunk_free_function<Environment&>(std::move(constructor));
|
|
158
|
+
using signature_type = util::function_signature_t<decltype(callback)>;
|
|
159
|
+
static_assert(util::signature_result<signature_type>{} != type<void>);
|
|
160
|
+
constexpr auto make = util::overloaded{make_with_try_catch, make_noexcept};
|
|
161
|
+
return make(std::type_identity<signature_type>{}, std::move(callback));
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
auto runtime_constructor = make_invoke_runtime_constructor(std::move(constructor));
|
|
165
|
+
using runtime_constructor_type = decltype(runtime_constructor);
|
|
166
|
+
|
|
167
|
+
// This part is really silly. We want the ability to create runtime host values through direct C++
|
|
168
|
+
// invocation not available to JavaScript. This is done with a tagged external value to a
|
|
169
|
+
// `util::function_ref`.
|
|
170
|
+
return util::bind{
|
|
171
|
+
[](runtime_constructor_type& constructor, Environment& env, const callback_info& info) -> napi_value {
|
|
172
|
+
auto arguments = info.arguments();
|
|
173
|
+
if (!arguments.empty()) {
|
|
174
|
+
auto maybe_constructor = [ & ]() -> std::optional<internal_constructor*> {
|
|
175
|
+
try {
|
|
176
|
+
// Check truthiness since null or undefined causes `napi_coerce_to_object` to throw
|
|
177
|
+
auto* arg0 = util::at(arguments, 0);
|
|
178
|
+
if (napi::invoke(napi_coerce_to_bool, napi_env{env}, arg0)) {
|
|
179
|
+
auto* as_object = napi::invoke(napi_coerce_to_object, napi_env{env}, arg0);
|
|
180
|
+
auto has_tag = napi::invoke(napi_check_object_type_tag, napi_env{env}, as_object, &type_tag_for<internal_constructor>);
|
|
181
|
+
if (has_tag) {
|
|
182
|
+
void* addr = napi::invoke(napi_get_value_external, napi_env{env}, as_object);
|
|
183
|
+
return static_cast<internal_constructor*>(addr);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return nullptr;
|
|
187
|
+
} catch (const napi::pending_error& /*error*/) {
|
|
188
|
+
return std::nullopt;
|
|
189
|
+
}
|
|
190
|
+
}();
|
|
191
|
+
if (!maybe_constructor) {
|
|
192
|
+
return nullptr;
|
|
193
|
+
}
|
|
194
|
+
if (*maybe_constructor != nullptr) {
|
|
195
|
+
const auto& constructor = **maybe_constructor;
|
|
196
|
+
return invoke_internal_error_scope(env, [ & ]() -> napi_value {
|
|
197
|
+
return constructor(info.this_arg());
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Invoke normal constructor
|
|
202
|
+
return constructor(env, info);
|
|
203
|
+
},
|
|
204
|
+
std::move(runtime_constructor),
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// Make callback for method invocations
|
|
209
|
+
export template <auto_environment Environment, class Type, class Method>
|
|
210
|
+
constexpr auto make_member_function(Method method) {
|
|
211
|
+
constexpr auto make_with_try_catch =
|
|
212
|
+
[]<class That, class Env, class... Args, bool Nx, class Result>(
|
|
213
|
+
std::type_identity<auto(That, Env, Args...) noexcept(Nx)->Result> /*signature*/,
|
|
214
|
+
auto callback
|
|
215
|
+
) -> auto {
|
|
216
|
+
using callback_type = decltype(callback);
|
|
217
|
+
return util::bind{
|
|
218
|
+
[](callback_type& callback, Environment& env, const callback_info& info) noexcept(Nx) -> napi_value {
|
|
219
|
+
auto maybe_that = unwrap_member_this<Type>(env, info.this_arg());
|
|
220
|
+
if (!maybe_that) {
|
|
221
|
+
return nullptr;
|
|
222
|
+
}
|
|
223
|
+
return invoke_internal_error_scope(env, [ & ]() -> napi_value {
|
|
224
|
+
auto run = util::regular_return{[ & ]() -> decltype(auto) {
|
|
225
|
+
return std::apply(
|
|
226
|
+
callback,
|
|
227
|
+
std::tuple_cat(
|
|
228
|
+
std::forward_as_tuple(**maybe_that, env),
|
|
229
|
+
js::transfer_out<std::tuple<Args...>>(info.arguments(), env)
|
|
230
|
+
)
|
|
231
|
+
);
|
|
232
|
+
}};
|
|
233
|
+
return js::transfer_in_strict<napi_value>(run().value_or(std::monostate{}), env);
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
std::move(callback),
|
|
237
|
+
};
|
|
238
|
+
};
|
|
239
|
+
constexpr auto make_noexcept =
|
|
240
|
+
[]<class That, class Env, class Result>(
|
|
241
|
+
std::type_identity<auto(That, Env) noexcept -> Result> /*signature*/,
|
|
242
|
+
auto callback
|
|
243
|
+
) -> auto {
|
|
244
|
+
static_assert(false, "untested");
|
|
245
|
+
using callback_type = decltype(callback);
|
|
246
|
+
return util::bind{
|
|
247
|
+
[](callback_type& callback, Environment& env, const callback_info& info) noexcept -> napi_value {
|
|
248
|
+
auto maybe_that = unwrap_member_this<Type>(env, info.this_arg());
|
|
249
|
+
if (!maybe_that) {
|
|
250
|
+
return nullptr;
|
|
251
|
+
}
|
|
252
|
+
auto run = util::regular_return{[ & ]() -> decltype(auto) {
|
|
253
|
+
return callback(**maybe_that, env);
|
|
254
|
+
}};
|
|
255
|
+
return js::transfer_in_strict<napi_value>(run().value_or(std::monostate{}), env);
|
|
256
|
+
},
|
|
257
|
+
std::move(callback),
|
|
258
|
+
};
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
auto callback = js::functional::thunk_member_function<Environment&>(std::move(method));
|
|
262
|
+
constexpr auto make = util::overloaded{make_with_try_catch, make_noexcept};
|
|
263
|
+
return make(std::type_identity<util::function_signature_t<decltype(callback)>>{}, std::move(callback));
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
} // namespace js::napi
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <memory>
|
|
3
|
+
#include <type_traits>
|
|
4
|
+
#include <utility>
|
|
5
|
+
export module napi_js:callback_storage;
|
|
6
|
+
import :api;
|
|
7
|
+
import :environment_fwd;
|
|
8
|
+
|
|
9
|
+
namespace js::napi {
|
|
10
|
+
|
|
11
|
+
namespace {
|
|
12
|
+
// Since napi runs each environment in its own thread we can store the environment data pointer
|
|
13
|
+
// here. We could also go through `napi_get_instance_data` but there is no assurance that the
|
|
14
|
+
// pointer type is the same. We assume the environment pointer will outlive the callback.
|
|
15
|
+
template <auto_environment Environment>
|
|
16
|
+
thread_local Environment* env_local = nullptr;
|
|
17
|
+
} // namespace
|
|
18
|
+
|
|
19
|
+
// Converts any invocable into a `napi_callback` and data pointer for use in napi API calls. The
|
|
20
|
+
// result is a `std::tuple` with `{ callback_ptr, data_ptr, finalizer }`. Finalizer is either a
|
|
21
|
+
// `nullptr` or a `std::unique_ptr<T>` which should be disposed of in a finalizer.
|
|
22
|
+
template <auto_environment Environment>
|
|
23
|
+
auto make_callback_storage(Environment& env, std::invocable<Environment&, const callback_info&> auto function) {
|
|
24
|
+
using function_type = decltype(function);
|
|
25
|
+
if constexpr (std::is_trivially_copyable_v<function_type>) {
|
|
26
|
+
if constexpr (std::is_empty_v<function_type>) {
|
|
27
|
+
// Constant expression function, expressed entirely in the type. `data` is the environment.
|
|
28
|
+
const auto callback = napi_callback{[](napi_env nenv, napi_callback_info info) -> napi_value {
|
|
29
|
+
const auto args = callback_info{nenv, info};
|
|
30
|
+
auto invoke = function_type{};
|
|
31
|
+
auto& env = *static_cast<Environment*>(args.data());
|
|
32
|
+
return invoke(env, args);
|
|
33
|
+
}};
|
|
34
|
+
return std::tuple{callback, &env};
|
|
35
|
+
} else if constexpr (sizeof(function_type) <= sizeof(void*)) {
|
|
36
|
+
// Trivial function type containing less than or equal to a pointer size. `data` is the
|
|
37
|
+
// function and environment is stored in a thread local.
|
|
38
|
+
env_local<Environment> = &env;
|
|
39
|
+
auto* data = reinterpret_cast<void*&>(function);
|
|
40
|
+
const auto callback = napi_callback{[](napi_env nenv, napi_callback_info info) -> napi_value {
|
|
41
|
+
const auto args = callback_info{nenv, info};
|
|
42
|
+
auto* data = args.data();
|
|
43
|
+
auto& invoke = reinterpret_cast<function_type&>(data);
|
|
44
|
+
auto& env = *env_local<Environment>;
|
|
45
|
+
return invoke(env, args);
|
|
46
|
+
}};
|
|
47
|
+
return std::tuple{callback, data};
|
|
48
|
+
} else {
|
|
49
|
+
static_assert(false, "Large trivial function?");
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
// Otherwise the function requires bound state w/ a destructor
|
|
53
|
+
using pair_type = std::pair<Environment*, function_type>;
|
|
54
|
+
auto external_data = std::make_unique<pair_type>(&env, std::move(function));
|
|
55
|
+
const auto callback = napi_callback{[](napi_env nenv, napi_callback_info info) -> napi_value {
|
|
56
|
+
const auto args = callback_info{nenv, info};
|
|
57
|
+
auto& state = *static_cast<pair_type*>(args.data());
|
|
58
|
+
return state.second(*state.first, args);
|
|
59
|
+
}};
|
|
60
|
+
return std::tuple{callback, std::move(external_data)};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
} // namespace js::napi
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <string> // IWYU pragma: keep
|
|
3
|
+
#include <type_traits>
|
|
4
|
+
export module napi_js:class_template_storage;
|
|
5
|
+
import :reference;
|
|
6
|
+
import auto_js;
|
|
7
|
+
import util;
|
|
8
|
+
using namespace std::string_literals;
|
|
9
|
+
|
|
10
|
+
#if _LIBCPP_VERSION || _MSVC_STL_UPDATE
|
|
11
|
+
// clang 22.1.0 w/ -stdlib=libc++ (and also MS STL)
|
|
12
|
+
// /workspace/packages/auto_js/napi/support/class_template_storage.cc:28:45: error: invalid operands
|
|
13
|
+
// to binary expression ('basic_string<char>' and 'const std::basic_string_view<char>')
|
|
14
|
+
// 28 | static_assert(index, "Class template '"s + name_sv + "' is missing in storage"s);
|
|
15
|
+
export {
|
|
16
|
+
using std::operator+;
|
|
17
|
+
}
|
|
18
|
+
#endif
|
|
19
|
+
|
|
20
|
+
namespace js::napi {
|
|
21
|
+
|
|
22
|
+
// Given a pack of strings it returns a static reference to a `util::sealed_map` of napi object
|
|
23
|
+
// references.
|
|
24
|
+
template <const auto& Strings>
|
|
25
|
+
constexpr auto class_template_references_of = []() -> auto {
|
|
26
|
+
const auto [... strings ] = Strings;
|
|
27
|
+
return util::sealed_map{std::type_identity<napi::reference<class_tag>>{}, strings...};
|
|
28
|
+
}();
|
|
29
|
+
|
|
30
|
+
// Environment storage for class template references
|
|
31
|
+
export template <const auto& Strings>
|
|
32
|
+
class class_template_references {
|
|
33
|
+
public:
|
|
34
|
+
template <class Type>
|
|
35
|
+
auto class_template(this auto& self, std::type_identity<Type> /*type*/, const auto& class_template) -> napi::value<class_tag_of<Type>> {
|
|
36
|
+
constexpr auto name_sv = util::consteval_string_view{class_template.constructor.name};
|
|
37
|
+
constexpr auto index = class_template_references_of<Strings>.lookup(name_sv);
|
|
38
|
+
if constexpr (!index) {
|
|
39
|
+
// nb: This fails while linking against STL for some reason. So the `constexpr` if works
|
|
40
|
+
// around the issue.
|
|
41
|
+
// D:\a\isolated-vm\isolated-vm\packages\auto_js\napi\support\class_template_storage.cc:38:45:
|
|
42
|
+
// error: invalid operands to binary expression ('string' (aka 'basic_string<char,
|
|
43
|
+
// char_traits<char>, allocator<char>>') and 'const std::basic_string_view<char>')
|
|
44
|
+
// 38 | static_assert(index, "Class template '"s + name_sv + "' is missing in storage"s);
|
|
45
|
+
static_assert(index, "Class template '"s + name_sv + "' is missing in storage"s);
|
|
46
|
+
}
|
|
47
|
+
auto& reference = self.class_template_references_.at(index).second;
|
|
48
|
+
using value_type = js::napi::value<class_tag_of<Type>>;
|
|
49
|
+
if (reference) {
|
|
50
|
+
return value_type::from(reference.get(self));
|
|
51
|
+
} else {
|
|
52
|
+
auto template_value = value_type::make(self, class_template);
|
|
53
|
+
reference.reset(self, template_value);
|
|
54
|
+
return template_value;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private:
|
|
59
|
+
util::copy_of<class_template_references_of<Strings>> class_template_references_;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
} // namespace js::napi
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <bit>
|
|
3
|
+
#include <unordered_map>
|
|
4
|
+
export module napi_js:container;
|
|
5
|
+
import :environment;
|
|
6
|
+
import :utility;
|
|
7
|
+
import nodejs;
|
|
8
|
+
import util;
|
|
9
|
+
|
|
10
|
+
namespace js::napi {
|
|
11
|
+
|
|
12
|
+
// Map container for napi values
|
|
13
|
+
template <class Type>
|
|
14
|
+
struct virtual_value_map {
|
|
15
|
+
public:
|
|
16
|
+
using container_type = std::unordered_map<napi_value, Type>;
|
|
17
|
+
using iterator = container_type::iterator;
|
|
18
|
+
using mapped_type = container_type::mapped_type;
|
|
19
|
+
|
|
20
|
+
virtual auto end() const -> iterator = 0;
|
|
21
|
+
virtual auto find(napi_value key) const -> iterator = 0;
|
|
22
|
+
virtual auto try_emplace(napi_value key, Type value) -> std::pair<iterator, bool> = 0;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
template <class Map>
|
|
26
|
+
struct concrete_value_map final : virtual_value_map<typename Map::mapped_type> {
|
|
27
|
+
public:
|
|
28
|
+
using container_type = Map;
|
|
29
|
+
// nb: The iterator type between `std::unordered_map<int, int, left>` and
|
|
30
|
+
// `std::unordered_map<int, int, right>` are not technically compatible.
|
|
31
|
+
using iterator = virtual_value_map<typename Map::mapped_type>::iterator;
|
|
32
|
+
using mapped_type = container_type::mapped_type;
|
|
33
|
+
|
|
34
|
+
virtual auto end() const -> iterator {
|
|
35
|
+
return std::bit_cast<iterator>(map_.end());
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
virtual auto find(napi_value key) const -> iterator {
|
|
39
|
+
return std::bit_cast<iterator>(map_.find(key));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
virtual auto try_emplace(napi_value key, mapped_type value) -> std::pair<iterator, bool> {
|
|
43
|
+
auto [ it, inserted ] = map_.try_emplace(key, std::move(value));
|
|
44
|
+
return std::pair{std::bit_cast<iterator>(it), inserted};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private:
|
|
48
|
+
container_type map_;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export template <class Type>
|
|
52
|
+
class value_map {
|
|
53
|
+
private:
|
|
54
|
+
using direct_map_type = concrete_value_map<std::unordered_map<napi_value, Type, direct_address_hash, direct_address_equal>>;
|
|
55
|
+
using indirect_map_type = concrete_value_map<std::unordered_map<napi_value, Type, indirect_address_hash, indirect_address_equal>>;
|
|
56
|
+
using virtual_map_type = util::covariant_value<virtual_value_map<Type>, direct_map_type, indirect_map_type>;
|
|
57
|
+
|
|
58
|
+
public:
|
|
59
|
+
using container_type = virtual_value_map<Type>;
|
|
60
|
+
using iterator = container_type::iterator;
|
|
61
|
+
using mapped_type = container_type::mapped_type;
|
|
62
|
+
|
|
63
|
+
explicit value_map(const environment& env) :
|
|
64
|
+
map_{
|
|
65
|
+
env.uses_direct_handles()
|
|
66
|
+
? virtual_map_type{direct_map_type{}}
|
|
67
|
+
: virtual_map_type{indirect_map_type{}}
|
|
68
|
+
} {}
|
|
69
|
+
|
|
70
|
+
auto end() const -> iterator { return map_->end(); }
|
|
71
|
+
auto find(napi_value key) const -> iterator { return map_->find(key); }
|
|
72
|
+
auto try_emplace(napi_value key, mapped_type value) -> std::pair<iterator, bool> { return map_->try_emplace(key, std::move(value)); }
|
|
73
|
+
|
|
74
|
+
private:
|
|
75
|
+
virtual_map_type map_;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
} // namespace js::napi
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <stdexcept>
|
|
3
|
+
module napi_js;
|
|
4
|
+
import :api;
|
|
5
|
+
import :utility;
|
|
6
|
+
import util;
|
|
7
|
+
|
|
8
|
+
namespace js::napi {
|
|
9
|
+
|
|
10
|
+
environment::environment(napi_env env) :
|
|
11
|
+
env_{env},
|
|
12
|
+
uses_direct_handles_{[ & ]() -> bool {
|
|
13
|
+
const auto direct_equal = direct_address_equal{};
|
|
14
|
+
const auto indirect_equal = indirect_address_equal{};
|
|
15
|
+
auto* array = napi::invoke(napi_create_array_with_length, env, 1);
|
|
16
|
+
napi::invoke0(napi_set_element, env, array, 0, array);
|
|
17
|
+
auto* result = napi::invoke(napi_get_element, env, array, 0);
|
|
18
|
+
if (direct_equal(array, result)) {
|
|
19
|
+
return true;
|
|
20
|
+
} else if (indirect_equal(array, result)) {
|
|
21
|
+
return false;
|
|
22
|
+
} else {
|
|
23
|
+
throw std::runtime_error{"Exotic napi handle behavior detected"};
|
|
24
|
+
}
|
|
25
|
+
}()} {
|
|
26
|
+
scheduler().open(napi::invoke(napi_get_uv_event_loop, env));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
environment::~environment() {
|
|
30
|
+
scheduler().close();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
} // namespace js::napi
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <functional>
|
|
3
|
+
#include <memory>
|
|
4
|
+
export module napi_js:environment;
|
|
5
|
+
export import :environment_fwd;
|
|
6
|
+
import :api;
|
|
7
|
+
import util;
|
|
8
|
+
|
|
9
|
+
namespace js::napi {
|
|
10
|
+
|
|
11
|
+
// Internal holder for an environment pointer. Used as a common base class for `visit` & `accept`.
|
|
12
|
+
// This could maybe become something similar to `realm_scope` if the need for it is more common.
|
|
13
|
+
export template <auto_environment Type>
|
|
14
|
+
class environment_scope {
|
|
15
|
+
public:
|
|
16
|
+
explicit environment_scope(Type& env) : env_{env} {}
|
|
17
|
+
|
|
18
|
+
explicit operator napi_env() const { return napi_env{env_.get()}; }
|
|
19
|
+
[[nodiscard]] auto environment() const -> Type& { return env_; }
|
|
20
|
+
|
|
21
|
+
private:
|
|
22
|
+
std::reference_wrapper<Type> env_;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// A reference to an environment is used as the lock witness. Generally, you should not have an
|
|
26
|
+
// `environment&` unless you're in the napi thread and locked.
|
|
27
|
+
export class environment : util::non_moveable, public uv_schedulable {
|
|
28
|
+
public:
|
|
29
|
+
explicit environment(napi_env env);
|
|
30
|
+
~environment();
|
|
31
|
+
|
|
32
|
+
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
33
|
+
[[nodiscard]] operator napi_env() const { return env_; }
|
|
34
|
+
[[nodiscard]] auto uses_direct_handles() const -> bool { return uses_direct_handles_; }
|
|
35
|
+
|
|
36
|
+
template <class Type>
|
|
37
|
+
static auto make_and_set_environment(napi_env env, auto&&... args) -> Type&
|
|
38
|
+
requires std::constructible_from<Type, napi_env, decltype(args)...> {
|
|
39
|
+
auto instance = std::make_unique<Type>(env, std::forward<decltype(args)>(args)...);
|
|
40
|
+
return *apply_finalizer(std::move(instance), [ & ](Type* instance, napi_finalize finalize, void* hint) -> Type* {
|
|
41
|
+
napi::invoke0(napi_set_instance_data, env, instance, finalize, hint);
|
|
42
|
+
return instance;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
template <class Type>
|
|
47
|
+
static auto unsafe_get_environment_as(napi_env env) -> Type& {
|
|
48
|
+
return *static_cast<Type*>(napi::invoke(napi_get_instance_data, env));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private:
|
|
52
|
+
napi_env env_;
|
|
53
|
+
bool uses_direct_handles_;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
} // namespace js::napi
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <concepts>
|
|
3
|
+
export module napi_js:environment_fwd;
|
|
4
|
+
|
|
5
|
+
namespace js::napi {
|
|
6
|
+
|
|
7
|
+
// A reference to an environment is used as the lock witness. Generally, you should not have an
|
|
8
|
+
// `environment&` unless you're in the napi thread and locked.
|
|
9
|
+
export class environment;
|
|
10
|
+
|
|
11
|
+
// Environment constraint
|
|
12
|
+
export template <class Type>
|
|
13
|
+
concept auto_environment = std::derived_from<Type, environment>;
|
|
14
|
+
|
|
15
|
+
} // namespace js::napi
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export module napi_js:error_scope;
|
|
2
|
+
import :accept;
|
|
3
|
+
import :api;
|
|
4
|
+
import :environment;
|
|
5
|
+
|
|
6
|
+
namespace js::napi {
|
|
7
|
+
|
|
8
|
+
// Catch `napi::pending_error` (thrown by `napi::invoke`) or `js::error`, converting to thrown
|
|
9
|
+
// runtime error.
|
|
10
|
+
[[nodiscard]] auto invoke_internal_error_scope(environment& env, auto implementation) -> napi_value {
|
|
11
|
+
try {
|
|
12
|
+
return implementation();
|
|
13
|
+
} catch (const napi::pending_error& /*error*/) {
|
|
14
|
+
return napi_value{};
|
|
15
|
+
} catch (const js::error& error) {
|
|
16
|
+
auto* exception = js::transfer_in_strict<napi_value>(error, env);
|
|
17
|
+
napi::invoke0(napi_throw, napi_env{env}, exception);
|
|
18
|
+
return napi_value{};
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
} // namespace js::napi
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <cassert>
|
|
3
|
+
module napi_js;
|
|
4
|
+
import :initialize;
|
|
5
|
+
|
|
6
|
+
namespace js::napi::detail {
|
|
7
|
+
|
|
8
|
+
initialize_require* module_to_initialize = nullptr;
|
|
9
|
+
|
|
10
|
+
initialize_require::initialize_require(int /*nothing*/, initialize_require& instance) {
|
|
11
|
+
assert(module_to_initialize == nullptr);
|
|
12
|
+
module_to_initialize = &instance;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
initialize_require::~initialize_require() {
|
|
16
|
+
assert(module_to_initialize == this);
|
|
17
|
+
module_to_initialize = nullptr;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
auto initialize_require::initialize(napi_env env, napi_value exports) -> void {
|
|
21
|
+
assert(module_to_initialize != nullptr);
|
|
22
|
+
(*module_to_initialize)(env, exports);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
} // namespace js::napi::detail
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <cassert>
|
|
3
|
+
#include <tuple>
|
|
4
|
+
export module napi_js:initialize;
|
|
5
|
+
import :environment;
|
|
6
|
+
import :value;
|
|
7
|
+
import nodejs;
|
|
8
|
+
import util;
|
|
9
|
+
|
|
10
|
+
namespace js::napi {
|
|
11
|
+
|
|
12
|
+
namespace detail {
|
|
13
|
+
|
|
14
|
+
export class initialize_require : util::non_copyable {
|
|
15
|
+
protected:
|
|
16
|
+
~initialize_require();
|
|
17
|
+
|
|
18
|
+
public:
|
|
19
|
+
explicit initialize_require(int /*nothing*/, initialize_require& instance);
|
|
20
|
+
virtual auto operator()(napi_env env, napi_value exports) -> void = 0;
|
|
21
|
+
static auto initialize(napi_env env, napi_value exports) -> void;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
} // namespace detail
|
|
25
|
+
|
|
26
|
+
export template <class Environment, class Make, class... Args>
|
|
27
|
+
class napi_js_module final : private detail::initialize_require {
|
|
28
|
+
public:
|
|
29
|
+
explicit napi_js_module(std::type_identity<Environment> /*env*/, Make make_exports, Args... args) :
|
|
30
|
+
detail::initialize_require{0, *this},
|
|
31
|
+
make_exports_{std::move(make_exports)},
|
|
32
|
+
args_{std::move(args)...} {}
|
|
33
|
+
|
|
34
|
+
private:
|
|
35
|
+
auto operator()(napi_env env, napi_value exports) -> void override {
|
|
36
|
+
// construct environment, set napi instance data w/ finalizer
|
|
37
|
+
const auto [... indices ] = util::sequence<sizeof...(Args)>;
|
|
38
|
+
auto& client_environment = environment::make_and_set_environment<Environment>(env, std::get<indices>(std::move(args_))...);
|
|
39
|
+
// assign export descriptor to napi-constructor namespace object
|
|
40
|
+
auto exports_local = napi::value<dictionary_tag>::from(exports);
|
|
41
|
+
exports_local.assign(client_environment, std::move(make_exports_)(client_environment));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
Make make_exports_;
|
|
45
|
+
std::tuple<Args...> args_;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
} // namespace js::napi
|