@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.
Files changed (49) hide show
  1. package/CMakeLists.txt +55 -0
  2. package/LICENSE +13 -0
  3. package/README.md +255 -0
  4. package/_module.cc +19 -0
  5. package/api/api.cc +10 -0
  6. package/api/callback_info.cc +41 -0
  7. package/api/finalizer.cc +44 -0
  8. package/api/handle_scope.cc +23 -0
  9. package/api/invoke.cc +99 -0
  10. package/api/uv_handle.cc +113 -0
  11. package/api/uv_scheduler.cc +56 -0
  12. package/api/uv_scheduler.h.cc +73 -0
  13. package/handle/bound_value.cc +49 -0
  14. package/handle/reference.cc +93 -0
  15. package/handle/remote.cc +92 -0
  16. package/handle/types.cc +136 -0
  17. package/handle/value.cc +52 -0
  18. package/include/napi_js_initialize.h +7 -0
  19. package/package.json +16 -0
  20. package/support/callback.cc +266 -0
  21. package/support/callback_storage.cc +64 -0
  22. package/support/class_template_storage.cc +62 -0
  23. package/support/container.cc +78 -0
  24. package/support/environment.cc +33 -0
  25. package/support/environment.h.cc +56 -0
  26. package/support/environment_fwd.cc +15 -0
  27. package/support/error_scope.cc +22 -0
  28. package/support/initialize.cc +25 -0
  29. package/support/initialize.h.cc +48 -0
  30. package/support/promise.cc +130 -0
  31. package/support/string_table.cc +45 -0
  32. package/support/utility.cc +76 -0
  33. package/transfer/accept.cc +349 -0
  34. package/transfer/visit.cc +268 -0
  35. package/upload/macro.jpg +0 -0
  36. package/value/array.cc +32 -0
  37. package/value/array.h.cc +55 -0
  38. package/value/class.cc +147 -0
  39. package/value/class.h.cc +32 -0
  40. package/value/dictionary.cc +33 -0
  41. package/value/dictionary.h.cc +56 -0
  42. package/value/external.cc +54 -0
  43. package/value/function.cc +52 -0
  44. package/value/function.h.cc +30 -0
  45. package/value/object.cc +30 -0
  46. package/value/object.h.cc +59 -0
  47. package/value/primitive.cc +160 -0
  48. package/value/primitive.h.cc +101 -0
  49. package/value/value.cc +10 -0
@@ -0,0 +1,268 @@
1
+ module;
2
+ #include <functional>
3
+ #include <utility>
4
+ export module napi_js:visit;
5
+ import :api;
6
+ import :container;
7
+ import :environment_fwd;
8
+ import :utility;
9
+ import :value;
10
+ import util;
11
+
12
+ namespace js {
13
+ using namespace napi;
14
+
15
+ // Instantiated in the acceptor that corresponds to a napi visitor
16
+ struct napi_reference_map_type {
17
+ template <class Type>
18
+ using type = napi::value_map<Type>;
19
+ };
20
+
21
+ // Visitor which may visit only property names. `symbol` should probably be rethought since they are
22
+ // not cloneable.
23
+ template <class Visit>
24
+ struct visit_napi_property_name {
25
+ public:
26
+ explicit visit_napi_property_name(Visit& visit) : visit_{visit} {}
27
+
28
+ template <class Accept>
29
+ auto operator()(napi_value subject, const Accept& accept) -> accept_target_t<Accept> {
30
+ return visit_.get().lookup_or_visit(accept, subject, [ & ]() -> accept_target_t<Accept> {
31
+ switch (napi::invoke(napi_typeof, napi_env{visit_.get()}, subject)) {
32
+ case napi_number:
33
+ return visit_(napi::value<number_tag>::from(subject), accept);
34
+ case napi_string:
35
+ // TODO: This looks up in the reference map twice. This should actually use
36
+ // `make_property_name`.
37
+ return visit_(napi::value<string_tag>::from(subject), accept);
38
+ case napi_symbol:
39
+ return visit_(napi::value<symbol_tag>::from(subject), accept);
40
+ default:
41
+ std::unreachable();
42
+ }
43
+ });
44
+ }
45
+
46
+ [[nodiscard]] auto environment() const -> auto& { return visit_.get().environment(); }
47
+
48
+ private:
49
+ std::reference_wrapper<Visit> visit_;
50
+ };
51
+
52
+ // Base napi visitor implementing all functionality. Napi doesn't give us granular information like
53
+ // "is this a latin1 string" and all checks must be made at once. So it's structured it great deal
54
+ // differently than the v8 visitor.
55
+ template <auto_environment Environment, class Target>
56
+ struct visit_napi_value;
57
+
58
+ template <class Meta>
59
+ using visit_napi_value_with = visit_napi_value<
60
+ typename Meta::visit_context_type,
61
+ typename Meta::accept_reference_type>;
62
+
63
+ template <auto_environment Environment, class Target>
64
+ struct visit_napi_value
65
+ : napi::environment_scope<Environment>,
66
+ reference_map_t<Target, napi_reference_map_type> {
67
+ public:
68
+ using reference_map_t<Target, napi_reference_map_type>::lookup_or_visit;
69
+
70
+ visit_napi_value(auto* /*transfer*/, Environment& env) :
71
+ napi::environment_scope<Environment>{env},
72
+ reference_map_t<Target, napi_reference_map_type>{env},
73
+ equal_{env} {}
74
+
75
+ // If the private `immediate` operation is defined: this public operation will first
76
+ // perform a reference map lookup, then delegate to the private operation if not found.
77
+ template <auto_tag Tag, class Accept>
78
+ auto operator()(value<Tag> subject, const Accept& accept) -> accept_target_t<Accept>
79
+ requires requires { immediate(subject, accept); } {
80
+ return lookup_or_visit(accept, subject, [ & ]() -> accept_target_t<Accept> {
81
+ return immediate(subject, accept);
82
+ });
83
+ }
84
+
85
+ // Visit operations for non-refable types.
86
+ template <class Accept>
87
+ auto operator()(value<null_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
88
+ null_ = subject;
89
+ return accept_tagged(subject, accept);
90
+ }
91
+
92
+ template <class Accept>
93
+ auto operator()(value<undefined_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
94
+ undefined_ = subject;
95
+ return accept_tagged(subject, accept);
96
+ }
97
+
98
+ template <class Accept>
99
+ auto operator()(value<boolean_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
100
+ return accept_tagged(subject, accept);
101
+ }
102
+
103
+ template <class Accept>
104
+ auto operator()(value<number_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
105
+ return accept_tagged(napi::value<number_tag_of<double>>::from(subject), accept);
106
+ }
107
+
108
+ template <class Accept>
109
+ auto operator()(value<symbol_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
110
+ return accept_tagged(subject, accept);
111
+ }
112
+
113
+ // General purpose visit operation which actually performs the type check
114
+ template <class Accept>
115
+ auto operator()(napi_value subject, const Accept& accept) -> accept_target_t<Accept> {
116
+ // Check known address values before the map lookup
117
+ // TODO: Fix it
118
+ // if (equal_(subject, undefined_)) {
119
+ // return (*this)(napi::value<undefined_tag>::from(subject), accept);
120
+ // } else if (equal_(subject, null_)) {
121
+ // return (*this)(napi::value<null_tag>::from(subject), accept);
122
+ // }
123
+
124
+ // Check the reference map, and lookup type via napi
125
+ return this->lookup_or_visit(accept, subject, [ & ]() -> accept_target_t<Accept> {
126
+ auto typeof = napi::invoke(napi_typeof, napi_env{*this}, subject);
127
+ switch (typeof) {
128
+ case napi_undefined:
129
+ return (*this)(napi::value<undefined_tag>::from(subject), accept);
130
+ case napi_null:
131
+ return (*this)(napi::value<null_tag>::from(subject), accept);
132
+ case napi_boolean:
133
+ return (*this)(napi::value<boolean_tag>::from(subject), accept);
134
+ case napi_number:
135
+ return (*this)(napi::value<number_tag>::from(subject), accept);
136
+ case napi_string:
137
+ return immediate(napi::value<string_tag>::from(subject), accept);
138
+ case napi_symbol:
139
+ return (*this)(napi::value<symbol_tag>::from(subject), accept);
140
+ case napi_object:
141
+ {
142
+ if (napi::invoke(napi_is_array, napi_env{*this}, subject)) {
143
+ return immediate(napi::value<list_tag>::from(subject), accept);
144
+ } else if (napi::invoke(napi_is_date, napi_env{*this}, subject)) {
145
+ return immediate(napi::value<date_tag>::from(subject), accept);
146
+ } else if (napi::invoke(napi_is_promise, napi_env{*this}, subject)) {
147
+ return immediate(napi::value<promise_tag>::from(subject), accept);
148
+ }
149
+ return immediate(napi::value<object_tag>::from(subject), accept);
150
+ }
151
+ case napi_function:
152
+ return immediate(napi::value<function_tag>::from(subject), accept);
153
+ case napi_external:
154
+ return immediate(napi::value<external_tag>::from(subject), accept);
155
+ case napi_bigint:
156
+ return immediate(napi::value<bigint_tag>::from(subject), accept);
157
+ }
158
+ });
159
+ }
160
+
161
+ // no required types
162
+ consteval static auto types(auto /*recursive*/) { return util::type_pack{}; }
163
+
164
+ private:
165
+ // Private visit operations for refable types
166
+ template <class Accept>
167
+ auto immediate(value<string_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
168
+ return accept_tagged(napi::value<string_tag_of<char16_t>>::from(subject), accept);
169
+ }
170
+
171
+ template <class Accept>
172
+ auto immediate(value<date_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
173
+ return accept_tagged(subject, accept);
174
+ }
175
+
176
+ template <class Accept>
177
+ auto immediate(value<promise_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
178
+ return accept_tagged(subject, accept);
179
+ }
180
+
181
+ template <class Accept>
182
+ auto immediate(value<external_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
183
+ return accept_tagged(subject, accept);
184
+ }
185
+
186
+ template <class Accept>
187
+ auto immediate(value<function_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
188
+ return accept_tagged(subject, accept);
189
+ }
190
+
191
+ template <class Accept>
192
+ auto immediate(value<bigint_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
193
+ return accept_tagged(napi::value<bigint_tag_of<bigint>>::from(subject), accept);
194
+ }
195
+
196
+ template <class Accept>
197
+ auto immediate(value<list_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
198
+ // nb: It is intentional that `dictionary_tag` is bound. It handles sparse arrays.
199
+ auto target = napi::bound_value{napi_env{*this}, napi::value<dictionary_tag>::from(subject)};
200
+ auto visit_entry = visit_entry_pair<visit_napi_property_name<visit_napi_value>, visit_napi_value&>{*this};
201
+ return accept(list_tag{}, visit_entry, target);
202
+ }
203
+
204
+ template <class Accept>
205
+ auto immediate(value<object_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
206
+ auto target = napi::bound_value{napi_env{*this}, napi::value<dictionary_tag>::from(subject)};
207
+ auto visit_entry = visit_entry_pair<visit_napi_property_name<visit_napi_value>, visit_napi_value&>{*this};
208
+ return accept(dictionary_tag{}, visit_entry, target);
209
+ }
210
+
211
+ // Convenience function which wraps in `napi::bound_value` and invokes `accept`.
212
+ template <auto_tag Tag, class Accept>
213
+ [[nodiscard]] auto accept_tagged(value<Tag> subject, const Accept& accept) -> accept_target_t<Accept> {
214
+ return accept(Tag{}, *this, napi::bound_value{napi_env{*this}, subject});
215
+ }
216
+
217
+ napi::address_equal equal_;
218
+ napi_value undefined_{napi::null_value_handle};
219
+ napi_value null_{napi::null_value_handle};
220
+ };
221
+
222
+ // Napi value visitor entry
223
+ template <class Meta>
224
+ struct visit<Meta, napi_value> : visit_napi_value_with<Meta> {
225
+ using visit_napi_value_with<Meta>::visit_napi_value_with;
226
+ };
227
+
228
+ template <class Meta>
229
+ struct visit<Meta, value<value_tag>> : visit_napi_value_with<Meta> {
230
+ using visit_napi_value_with<Meta>::visit_napi_value_with;
231
+ };
232
+
233
+ template <class Meta>
234
+ struct visit<Meta, value<object_tag>> : visit_napi_value_with<Meta> {
235
+ using visit_napi_value_with<Meta>::visit_napi_value_with;
236
+ };
237
+
238
+ // Object key maker via napi
239
+ template <auto Key>
240
+ struct visit_key_literal<Key, napi_value> : util::non_moveable {
241
+ public:
242
+ explicit constexpr visit_key_literal(auto* /*transfer*/) {}
243
+
244
+ auto operator()(const auto& /*could_be_literally_anything*/, const auto& accept_or_visit) -> napi_value {
245
+ if (local_key_ == napi_value{}) {
246
+ auto& environment = accept_or_visit.environment();
247
+ auto storage = environment.string_table_storage(Key);
248
+ if (storage) {
249
+ auto& reference = *storage;
250
+ if (reference) {
251
+ local_key_ = reference.get(environment);
252
+ } else {
253
+ auto value = napi::value<string_tag>::make_property_name(environment, util::consteval_string_view{Key});
254
+ reference.reset(environment, value);
255
+ local_key_ = napi_value{value};
256
+ }
257
+ } else {
258
+ return napi::value<string_tag>::make_property_name(environment, util::consteval_string_view{Key});
259
+ }
260
+ }
261
+ return local_key_;
262
+ }
263
+
264
+ private:
265
+ napi_value local_key_{};
266
+ };
267
+
268
+ } // namespace js
Binary file
package/value/array.cc ADDED
@@ -0,0 +1,32 @@
1
+ module;
2
+ #include <cstdint>
3
+ module napi_js;
4
+ import :api;
5
+ import :bound_value;
6
+
7
+ namespace js::napi {
8
+
9
+ auto bound_value_for_vector::begin() const -> iterator {
10
+ return {*this, 0};
11
+ }
12
+
13
+ auto bound_value_for_vector::end() const -> iterator {
14
+ return {*this, size()};
15
+ }
16
+
17
+ auto bound_value_for_vector::size() const -> uint32_t {
18
+ if (size_ == 0) {
19
+ size_ = napi::invoke(napi_get_array_length, env(), napi_value{*this}) + 1;
20
+ }
21
+ return size_ - 1;
22
+ }
23
+
24
+ bound_value_for_vector::iterator::iterator(bound_value_for_vector subject, size_type index) :
25
+ subject_{subject},
26
+ index_{index} {}
27
+
28
+ auto bound_value_for_vector::iterator::operator*() const -> value_type {
29
+ return value_type::from(napi::invoke(napi_get_element, subject_.env(), napi_value{subject_}, index_));
30
+ }
31
+
32
+ } // namespace js::napi
@@ -0,0 +1,55 @@
1
+ module;
2
+ #include <compare>
3
+ #include <cstdint>
4
+ export module napi_js:array;
5
+ import :bound_value;
6
+ import :object;
7
+ import :value_handle;
8
+ import auto_js;
9
+ import nodejs;
10
+ import util;
11
+
12
+ namespace js::napi {
13
+
14
+ class bound_value_for_vector : public bound_value_next<vector_tag> {
15
+ public:
16
+ class iterator;
17
+ using bound_value_next<vector_tag>::bound_value_next;
18
+ using value_type = value<value_tag>;
19
+
20
+ [[nodiscard]] auto begin() const -> iterator;
21
+ [[nodiscard]] auto end() const -> iterator;
22
+ [[nodiscard]] auto size() const -> uint32_t;
23
+
24
+ private:
25
+ mutable uint32_t size_{};
26
+ };
27
+
28
+ class bound_value_for_vector::iterator : public util::random_access_iterator_facade<int32_t, int64_t> {
29
+ public:
30
+ using arithmetic_facade::operator+;
31
+ using difference_type = arithmetic_facade::difference_type;
32
+ using size_type = uint32_t;
33
+ using value_type = bound_value_for_vector::value_type;
34
+
35
+ iterator() = default;
36
+ iterator(bound_value_for_vector subject, size_type index);
37
+
38
+ auto operator*() const -> value_type;
39
+
40
+ auto operator+=(difference_type offset) -> iterator& {
41
+ index_ += offset;
42
+ return *this;
43
+ }
44
+
45
+ auto operator==(const iterator& right) const -> bool { return index_ == right.index_; }
46
+ auto operator<=>(const iterator& right) const -> std::strong_ordering { return index_ <=> right.index_; }
47
+
48
+ private:
49
+ auto operator+() const -> size_type { return index_; }
50
+
51
+ bound_value_for_vector subject_;
52
+ size_type index_{};
53
+ };
54
+
55
+ } // namespace js::napi
package/value/class.cc ADDED
@@ -0,0 +1,147 @@
1
+ module;
2
+ #include <algorithm>
3
+ #include <array>
4
+ #include <concepts>
5
+ #include <memory>
6
+ #include <tuple>
7
+ #include <type_traits>
8
+ #include <utility>
9
+ export module napi_js:class_definitions;
10
+ import :api;
11
+ import :callback;
12
+ import :class_;
13
+ import :external;
14
+ import util;
15
+
16
+ namespace js::napi {
17
+
18
+ template <class Type>
19
+ template <class... Args>
20
+ auto value_for_class_of<Type>::construct(auto& env, Args&&... args) const -> value<object_tag>
21
+ requires std::constructible_from<Type, Args...> {
22
+ // NOLINTNEXTLINE(readability-simplify-boolean-expr)
23
+ if (false) {
24
+ // nb: Fixes invalid `performance-unnecessary-value-param` lint errors when invoking this function
25
+ [[maybe_unused]] auto nothing = std::tuple<std::remove_cvref_t<Args>...>{std::forward<Args>(args)...};
26
+ }
27
+ // NOLINTNEXTLINE(bugprone-use-after-move)
28
+ return runtime_construct(env, std::forward_as_tuple(std::forward<Args>(args)...), std::tuple{});
29
+ }
30
+
31
+ template <class Type>
32
+ template <class... HostArgs, class... RuntimeArgs>
33
+ auto value_for_class_of<Type>::runtime_construct(
34
+ auto& env,
35
+ std::tuple<HostArgs...> host_args,
36
+ std::tuple<RuntimeArgs...> runtime_args
37
+ ) const -> value<object_tag>
38
+ requires std::constructible_from<Type, HostArgs...> {
39
+ const auto [... indices ] = util::sequence<sizeof...(HostArgs)>;
40
+ // NOLINTNEXTLINE(bugprone-use-after-move)
41
+ auto instance = std::make_unique<Type>(std::get<indices>(std::move(host_args))...);
42
+ return transfer_construct(env, std::move(instance), std::move(runtime_args));
43
+ }
44
+
45
+ template <class Type>
46
+ template <class... Args>
47
+ auto value_for_class_of<Type>::transfer_construct(auto& env, auto instance, std::tuple<Args...> runtime_args) const -> value<object_tag> {
48
+ using element_type = decltype(instance)::element_type;
49
+ auto construct = [ & ](napi_value this_arg) mutable -> napi_value {
50
+ // Tag `this_arg`
51
+ napi::invoke0(napi_type_tag_object, napi_env{env}, this_arg, &type_tag_for<element_type>);
52
+
53
+ // Wrap w/ finalizer
54
+ return apply_finalizer(std::move(instance), [ & ](element_type* instance, napi_finalize finalize, void* hint) -> napi_value {
55
+ napi::invoke0(napi_wrap, napi_env{env}, this_arg, instance, finalize, hint, nullptr);
56
+ return this_arg;
57
+ });
58
+ };
59
+
60
+ // Now an external (of `internal_constructor`) gets passed to JavaScript for a moment and
61
+ // hopefully it jumps into `construct`
62
+ auto construct_ref = internal_constructor{construct};
63
+ auto* construct_external = napi_value{value<external_tag>::make(env, &construct_ref)};
64
+ auto [... runtime_arg_values ] = js::transfer_in_strict<std::array<napi_value, sizeof...(Args)>>(std::move(runtime_args), env);
65
+ auto arg_vector = std::array{construct_external, runtime_arg_values...};
66
+ auto* this_arg = napi::invoke(napi_new_instance, napi_env{env}, napi_value{*this}, arg_vector.size(), arg_vector.data());
67
+ return value<object_tag>::from(this_arg);
68
+ }
69
+
70
+ template <class Type>
71
+ template <class Environment>
72
+ auto value_for_class_of<Type>::make(Environment& env, const auto& class_template) -> value<class_tag_of<Type>> {
73
+ // Make constructor callback
74
+ auto [ construct_ptr, constructor_data ] =
75
+ make_callback_storage(env, make_constructor_function<Environment, Type>(class_template.constructor.function));
76
+ static_assert(std::remove_cvref_t<decltype(class_template.constructor)>::disposition == property_disposition::function);
77
+ static_assert(!requires { typename decltype(constructor_data)::element_type; });
78
+
79
+ // Member function property descriptor
80
+ const auto make_member_function_descriptor = [ & ]<class Property>(const Property& property) -> napi_property_descriptor
81
+ requires(Property::scope == class_property_scope::prototype) {
82
+ constexpr auto u8_name = util::consteval_string_view{util::interpolate_string<char>(property.name)};
83
+ auto [ callback, data ] = make_callback_storage(env, make_member_function<Environment, Type>(property.function));
84
+ static_assert(!requires { typename decltype(data)::element_type; });
85
+ return {
86
+ .utf8name = u8_name.data(),
87
+ .name{},
88
+
89
+ .method = callback,
90
+ .getter{},
91
+ .setter{},
92
+ .value{},
93
+
94
+ // NOLINTNEXTLINE(hicpp-signed-bitwise)
95
+ .attributes = static_cast<napi_property_attributes>(napi_writable | napi_configurable),
96
+ .data = data,
97
+ };
98
+ };
99
+
100
+ // Static function property descriptor
101
+ const auto make_static_function_descriptor = [ & ]<class Property>(const Property& property) -> napi_property_descriptor
102
+ requires(Property::scope == class_property_scope::constructor) {
103
+ constexpr auto u8_name = util::consteval_string_view{util::interpolate_string<char>(property.name)};
104
+ auto [ callback, data ] = make_callback_storage(env, make_free_function<Environment>(property.function));
105
+ static_assert(!requires { typename decltype(data)::element_type; });
106
+ return {
107
+ .utf8name = u8_name.data(),
108
+ .name{},
109
+
110
+ .method = callback,
111
+ .getter{},
112
+ .setter{},
113
+ .value{},
114
+
115
+ // NOLINTNEXTLINE(hicpp-signed-bitwise)
116
+ .attributes = static_cast<napi_property_attributes>(napi_static | napi_writable | napi_configurable),
117
+ .data = data,
118
+ };
119
+ };
120
+
121
+ // Make class property descriptors
122
+ const auto make_property_descriptor = util::overloaded{
123
+ make_member_function_descriptor,
124
+ make_static_function_descriptor,
125
+ };
126
+ const auto [... properties ] = class_template.properties;
127
+ const auto property_descriptors = std::array<napi_property_descriptor, sizeof...(properties)>{
128
+ // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage)
129
+ make_property_descriptor(properties)...
130
+ };
131
+
132
+ // Finally, define the class
133
+ constexpr auto u8_name = util::consteval_string_view{util::interpolate_string<char>(class_template.constructor.name)};
134
+ auto result = napi::invoke(
135
+ napi_define_class,
136
+ napi_env{env},
137
+ u8_name.data(),
138
+ u8_name.length(),
139
+ construct_ptr,
140
+ constructor_data,
141
+ property_descriptors.size(),
142
+ property_descriptors.data()
143
+ );
144
+ return js::napi::value<class_tag_of<Type>>::from(result);
145
+ }
146
+
147
+ } // namespace js::napi
@@ -0,0 +1,32 @@
1
+ module;
2
+ #include <concepts>
3
+ #include <tuple>
4
+ export module napi_js:class_;
5
+ import :value_handle;
6
+ import :object;
7
+
8
+ namespace js::napi {
9
+
10
+ template <class Type>
11
+ class value_for_class_of : public value_next<class_tag_of<Type>> {
12
+ public:
13
+ using value_next<class_tag_of<Type>>::value_next;
14
+
15
+ // Construct a new C++ instance & JavaScript value
16
+ template <class... Args>
17
+ auto construct(auto& env, Args&&... args) const -> value<object_tag>
18
+ requires std::constructible_from<Type, Args...>;
19
+
20
+ template <class... HostArgs, class... RuntimeArgs>
21
+ auto runtime_construct(auto& env, std::tuple<HostArgs...> host_args, std::tuple<RuntimeArgs...> runtime_args) const -> value<object_tag>
22
+ requires std::constructible_from<Type, HostArgs...>;
23
+
24
+ // Create a JavaScript value from an already-created instance smart pointer
25
+ template <class... Args>
26
+ auto transfer_construct(auto& env, auto instance, std::tuple<Args...> runtime_args) const -> value<object_tag>;
27
+
28
+ template <class Environment>
29
+ static auto make(Environment& env, const auto& class_template) -> value<class_tag_of<Type>>;
30
+ };
31
+
32
+ } // namespace js::napi
@@ -0,0 +1,33 @@
1
+ module;
2
+ #include <ranges>
3
+ #include <utility>
4
+ module napi_js;
5
+ import :api;
6
+ import :array;
7
+
8
+ namespace js::napi {
9
+
10
+ auto dictionary_like::into_range() const -> range_type {
11
+ return keys() | std::views::transform(iterator_transform{*this});
12
+ }
13
+
14
+ auto dictionary_like::size() const -> std::size_t {
15
+ return keys().size();
16
+ }
17
+
18
+ auto dictionary_like::keys() const -> const keys_type& {
19
+ if (napi_value{keys_} == nullptr) {
20
+ auto* property_names = napi::invoke(napi_get_property_names, env(), *this);
21
+ keys_ = keys_type{env(), js::napi::value<vector_tag>::from(property_names)};
22
+ }
23
+ return keys_;
24
+ }
25
+
26
+ dictionary_like::iterator_transform::iterator_transform(const dictionary_like& subject) :
27
+ subject_{&subject} {}
28
+
29
+ auto dictionary_like::iterator_transform::operator()(value<value_tag> key) const -> value_type {
30
+ return std::pair{key_type::from(key), subject_->get(key)};
31
+ }
32
+
33
+ } // namespace js::napi
@@ -0,0 +1,56 @@
1
+ module;
2
+ #include <ranges>
3
+ export module napi_js:dictionary;
4
+ import :array;
5
+ import :bound_value;
6
+ import :object;
7
+ import :value_handle;
8
+ import auto_js;
9
+
10
+ namespace js::napi {
11
+
12
+ // Weird naming here because an object `{}` is a typeof "object", but lots of other things are
13
+ // objects. Also, the object iterator is implemented as transformation on a property names vector
14
+ // (which is also an object). So this needs to be its own class or it is a circular inheritance.
15
+ class dictionary_like : public bound_value<object_tag> {
16
+ public:
17
+ using bound_value<object_tag>::bound_value;
18
+ using keys_type = bound_value<vector_tag>;
19
+ using key_type = value<primitive_tag>;
20
+ using mapped_type = value<value_tag>;
21
+ using value_type = std::pair<key_type, mapped_type>;
22
+
23
+ private:
24
+ class iterator_transform {
25
+ public:
26
+ explicit iterator_transform(const dictionary_like& subject_);
27
+ auto operator()(value<value_tag> key) const -> value_type;
28
+
29
+ private:
30
+ const dictionary_like* subject_;
31
+ };
32
+
33
+ public:
34
+ using range_type = std::ranges::transform_view<std::views::all_t<const keys_type&>, iterator_transform>;
35
+ using iterator = std::ranges::iterator_t<range_type>;
36
+
37
+ [[nodiscard]] auto into_range() const -> range_type;
38
+ [[nodiscard]] auto size() const -> std::size_t;
39
+
40
+ private:
41
+ [[nodiscard]] auto keys() const -> const keys_type&;
42
+
43
+ mutable keys_type keys_;
44
+ };
45
+
46
+ class bound_value_for_list : public dictionary_like {
47
+ public:
48
+ using dictionary_like::dictionary_like;
49
+ };
50
+
51
+ class bound_value_for_dictionary : public dictionary_like {
52
+ public:
53
+ using dictionary_like::dictionary_like;
54
+ };
55
+
56
+ } // namespace js::napi
@@ -0,0 +1,54 @@
1
+ module;
2
+ #include <type_traits>
3
+ export module napi_js:external;
4
+ import :api;
5
+ import :bound_value;
6
+ import :object;
7
+ import :value_handle;
8
+
9
+ namespace js::napi {
10
+
11
+ class value_for_external : public value_next<external_tag> {
12
+ public:
13
+ using value_next<external_tag>::value_next;
14
+
15
+ // untagged
16
+ static auto make(auto& env, void* pointer) -> value<external_tag>;
17
+
18
+ // tagged
19
+ template <class Type>
20
+ static auto make(auto& env, Type* pointer) -> value<external_tag>;
21
+ };
22
+
23
+ class bound_value_for_external : public bound_value_next<external_tag> {
24
+ public:
25
+ using bound_value_next<external_tag>::bound_value_next;
26
+
27
+ template <class Type>
28
+ [[nodiscard]] auto try_cast(std::type_identity<Type> /*type*/) const -> Type*;
29
+ };
30
+
31
+ // ---
32
+
33
+ auto value_for_external::make(auto& env, void* pointer) -> value<external_tag> {
34
+ auto* external = napi::invoke(napi_create_external, napi_env{env}, pointer, nullptr, nullptr);
35
+ return value<external_tag>::from(external);
36
+ }
37
+
38
+ template <class Type>
39
+ auto value_for_external::make(auto& env, Type* pointer) -> value<external_tag> {
40
+ auto external = make(env, static_cast<void*>(pointer));
41
+ napi::invoke0(napi_type_tag_object, napi_env{env}, external, &type_tag_for<Type>);
42
+ return external;
43
+ };
44
+
45
+ template <class Type>
46
+ auto bound_value_for_external::try_cast(std::type_identity<Type> /*type*/) const -> Type* {
47
+ if (napi::invoke(napi_check_object_type_tag, env(), napi_value{*this}, &type_tag_for<Type>)) {
48
+ return static_cast<Type*>(napi::invoke(napi_get_value_external, env(), napi_value{*this}));
49
+ } else {
50
+ return nullptr;
51
+ }
52
+ }
53
+
54
+ } // namespace js::napi