@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,56 @@
1
+ module;
2
+ #include <cassert>
3
+ #include <functional>
4
+ #include <thread>
5
+ #include <utility>
6
+ module napi_js;
7
+
8
+ namespace js::napi {
9
+
10
+ auto uv_scheduler::close() -> void {
11
+ // nb: It's not safe to dereference `auto& async` like the functions because `.close()` deletes
12
+ // the underlying memory and would cause a dangling reference. I think `uv_close` is probably
13
+ // always async but I'm not sure of it.
14
+ assert((*async_)->thread_id == std::this_thread::get_id());
15
+ [[maybe_unused]] auto abandoned_tasks =
16
+ std::invoke([ & ]() -> auto {
17
+ assert((*async_)->thread_id == std::this_thread::get_id());
18
+ auto lock = (*async_)->shared.write();
19
+ lock->is_open = false;
20
+ auto tasks = std::exchange(lock->tasks, {});
21
+ async_->close();
22
+ return tasks;
23
+ });
24
+ }
25
+
26
+ auto uv_scheduler::decrement_ref() -> void {
27
+ auto& async = *async_;
28
+ assert(async->thread_id == std::this_thread::get_id());
29
+ if (--async->refs == 0) {
30
+ uv_unref(async.handle());
31
+ }
32
+ }
33
+
34
+ auto uv_scheduler::increment_ref() -> void {
35
+ auto& async = *async_;
36
+ assert(async->thread_id == std::this_thread::get_id());
37
+ if (++async->refs == 1) {
38
+ uv_ref(async.handle());
39
+ }
40
+ }
41
+
42
+ auto uv_scheduler::open(uv_loop_t* loop) -> void {
43
+ auto& async = *async_;
44
+ assert(async->thread_id == std::this_thread::get_id());
45
+ async->shared.write()->is_open = true;
46
+ async.open(uv_async_init, loop, [](uv_async_t* async_handle) -> void {
47
+ auto& async = *handle_type::unwrap(async_handle);
48
+ auto tasks = std::exchange(async->shared.write()->tasks, {});
49
+ for (auto& task : tasks) {
50
+ task();
51
+ }
52
+ });
53
+ uv_unref(async.handle());
54
+ }
55
+
56
+ } // namespace js::napi
@@ -0,0 +1,73 @@
1
+ module;
2
+ #include <memory>
3
+ #include <thread>
4
+ #include <vector>
5
+ export module napi_js:api.uv_scheduler;
6
+ import :api.uv_handle;
7
+ import util;
8
+
9
+ #if _LIBCPP_VERSION
10
+ // clang 22.1.0
11
+ // /usr/lib/llvm-22/bin/../include/c++/v1/__new/allocate.h:39:30: error: no matching function for call to 'operator new'
12
+ // 39 | return static_cast<_Tp*>(__builtin_operator_new(__size, static_cast<align_val_t>(__align)));
13
+ export {
14
+ using ::operator new;
15
+ }
16
+ #endif
17
+
18
+ namespace js::napi {
19
+
20
+ // Shareable libuv scheduler
21
+ export class uv_scheduler {
22
+ private:
23
+ using task_type = util::maybe_move_only_function<auto()->void>;
24
+
25
+ public:
26
+ auto close() -> void;
27
+ auto decrement_ref() -> void;
28
+ auto increment_ref() -> void;
29
+ auto open(uv_loop_t* loop) -> void;
30
+ auto operator()(auto task, auto&&... args) const -> void;
31
+ explicit operator bool() const { return bool{async_}; }
32
+
33
+ private:
34
+ struct locked_storage {
35
+ std::vector<task_type> tasks;
36
+ bool is_open{};
37
+ };
38
+ struct storage {
39
+ storage() = default;
40
+ util::lockable<locked_storage> shared;
41
+ std::thread::id thread_id{std::this_thread::get_id()};
42
+ int refs{};
43
+ };
44
+ using handle_type = uv_handle_of<uv_async_t, storage>;
45
+ std::shared_ptr<handle_type> async_{handle_type::make()};
46
+ };
47
+
48
+ // An environment with an uv loop and scheduler
49
+ export class uv_schedulable {
50
+ public:
51
+ auto scheduler(this auto& self) -> auto& { return self.uv_scheduler_; }
52
+
53
+ private:
54
+ uv_scheduler uv_scheduler_;
55
+ };
56
+
57
+ // ---
58
+
59
+ auto uv_scheduler::operator()(auto task, auto&&... args) const -> void {
60
+ auto& async = *async_;
61
+ auto shared = async->shared.write();
62
+ if (shared->is_open) {
63
+ shared->tasks.push_back(
64
+ [ task = std::move(task),
65
+ ... args = std::forward<decltype(args)>(args) ]() mutable -> void {
66
+ task(std::move(args)...);
67
+ }
68
+ );
69
+ uv_async_send(async.handle());
70
+ }
71
+ }
72
+
73
+ } // namespace js::napi
@@ -0,0 +1,49 @@
1
+ module;
2
+ #include <concepts>
3
+ export module napi_js:bound_value;
4
+ import :handle.types;
5
+ import nodejs;
6
+
7
+ namespace js::napi {
8
+
9
+ // Details applied to each level of the `bound_value<T>` hierarchy.
10
+ template <class Tag>
11
+ class bound_value_next : public bound_value<typename Tag::tag_type> {
12
+ public:
13
+ using bound_value<typename Tag::tag_type>::bound_value;
14
+ bound_value_next() = default;
15
+ bound_value_next(napi_env env, value<Tag> value) :
16
+ bound_value<typename Tag::tag_type>{env, napi_value{value}} {}
17
+
18
+ template <std::constructible_from<Tag> To>
19
+ // NOLINTNEXTLINE(google-explicit-constructor)
20
+ operator value<To>() const { return value<To>::from(napi_value{*this}); }
21
+ };
22
+
23
+ // Member & method implementation for stateful objects. Used internally in visitors. I think it
24
+ // might make sense to have the environment specified by a template parameter. Then you would use
25
+ // `bound_value<T>` or something instead of passing the environment to each `value<T>` method.
26
+ template <class Tag>
27
+ class bound_value : public value_specialization<Tag>::bound_type {
28
+ public:
29
+ using value_specialization<Tag>::bound_type::bound_type;
30
+ };
31
+
32
+ template <class Tag>
33
+ bound_value(auto, value<Tag>) -> bound_value<Tag>;
34
+
35
+ template <>
36
+ class bound_value<void> : public value_handle {
37
+ protected:
38
+ bound_value() = default;
39
+ bound_value(napi_env env, napi_value value) :
40
+ value_handle{value},
41
+ env_{env} {}
42
+
43
+ [[nodiscard]] auto env() const -> napi_env { return env_; }
44
+
45
+ private:
46
+ napi_env env_{};
47
+ };
48
+
49
+ } // namespace js::napi
@@ -0,0 +1,93 @@
1
+ module;
2
+ #include <stdexcept>
3
+ #include <utility>
4
+ export module napi_js:reference;
5
+ import :api;
6
+ import :value_handle;
7
+ import util;
8
+
9
+ namespace js::napi {
10
+
11
+ // Base class of reference & remote values
12
+ class reference_handle : util::non_copyable {
13
+ protected:
14
+ reference_handle() = default;
15
+ reference_handle(napi_env env, napi_value value) :
16
+ env_{env},
17
+ ref_{napi::invoke(napi_create_reference, env, value, 1)} {}
18
+
19
+ [[nodiscard]] auto env() const -> napi_env {
20
+ return env_;
21
+ }
22
+
23
+ [[nodiscard]] auto get_value(napi_env env) const -> napi_value {
24
+ return napi::invoke(napi_get_reference_value, env, ref_);
25
+ }
26
+
27
+ auto reset(napi_env env, napi_value value) -> void {
28
+ if (ref_ != nullptr) {
29
+ if (env != env_) {
30
+ throw std::logic_error{"Environment mismatch"};
31
+ }
32
+ napi::invoke0(napi_delete_reference, env, std::exchange(ref_, nullptr));
33
+ }
34
+ env_ = env;
35
+ ref_ = napi::invoke(napi_create_reference, env, value, 1);
36
+ }
37
+
38
+ public:
39
+ consteval reference_handle(const reference_handle& /*right*/) {
40
+ // consteval no-op!
41
+ };
42
+
43
+ constexpr reference_handle(reference_handle&& right) noexcept :
44
+ env_{right.env_},
45
+ ref_{std::exchange(right.ref_, nullptr)} {}
46
+
47
+ constexpr ~reference_handle() {
48
+ if (ref_ != nullptr) {
49
+ napi::invoke0_noexcept(napi_delete_reference, env_, ref_);
50
+ }
51
+ }
52
+
53
+ auto operator=(const reference_handle&) = delete;
54
+ auto operator=(reference_handle&& right) noexcept -> reference_handle& {
55
+ env_ = right.env_;
56
+ ref_ = std::exchange(right.ref_, nullptr);
57
+ return *this;
58
+ };
59
+
60
+ explicit operator bool() const { return ref_ != nullptr; }
61
+
62
+ private:
63
+ napi_env env_{};
64
+ napi_ref ref_{};
65
+ };
66
+
67
+ // A not-thread safe persistent value reference.
68
+ export template <class Tag>
69
+ class reference : public reference<typename Tag::tag_type> {
70
+ public:
71
+ using value_type = value<Tag>;
72
+ using reference<typename Tag::tag_type>::reference;
73
+
74
+ reference() = default;
75
+ reference(napi_env env, value_type value) :
76
+ reference<typename Tag::tag_type>{env, napi_value{value}} {}
77
+
78
+ explicit operator bool() const { return reference_handle::operator bool(); }
79
+ [[nodiscard]] auto get(napi_env env) const -> value_type { return value_type::from(this->get_value(env)); }
80
+
81
+ auto reset(napi_env env, value_type value) -> void { reference_handle::reset(env, napi_value{value}); }
82
+ };
83
+
84
+ template <class Tag>
85
+ reference(auto, value<Tag>) -> reference<Tag>;
86
+
87
+ // nb: Protected inheritance so that `remote<T> is not comparable to `reference<T>`.
88
+ template <>
89
+ class reference<void> : protected reference_handle {
90
+ using reference_handle::reference_handle;
91
+ };
92
+
93
+ } // namespace js::napi
@@ -0,0 +1,92 @@
1
+ module;
2
+ #include <cassert>
3
+ #include <memory>
4
+ #include <type_traits>
5
+ #include <utility>
6
+ export module napi_js:remote;
7
+ import :api;
8
+ import :environment;
9
+ import :reference;
10
+ import :value_handle;
11
+ import util;
12
+
13
+ namespace js::napi {
14
+
15
+ template <class Type>
16
+ concept remote_handle_environment =
17
+ std::is_base_of_v<environment, Type> &&
18
+ std::is_base_of_v<uv_schedulable, Type>;
19
+
20
+ // Thread safe persistent value reference
21
+ export template <class Tag>
22
+ class remote : protected reference_handle {
23
+ private:
24
+ struct private_constructor {
25
+ explicit private_constructor() = default;
26
+ };
27
+ static auto expire(remote* ptr) -> void;
28
+
29
+ public:
30
+ using unique_remote = std::unique_ptr<remote, util::function_constant<expire>>;
31
+
32
+ remote(private_constructor /*private*/, const environment& env, value<Tag> value, uv_scheduler scheduler) :
33
+ reference_handle{napi_env{env}, napi_value{value}},
34
+ scheduler_{std::move(scheduler)} {}
35
+
36
+ auto deref(const environment& env) const -> value<Tag>;
37
+
38
+ static auto make_shared(remote_handle_environment auto& env, value<Tag> value) -> std::shared_ptr<remote>;
39
+ static auto make_unique(remote_handle_environment auto& env, value<Tag> value) -> unique_remote;
40
+
41
+ private:
42
+ uv_scheduler scheduler_;
43
+ };
44
+
45
+ // Convenience helpers
46
+ export template <class Type>
47
+ using shared_remote = std::shared_ptr<remote<Type>>;
48
+
49
+ export template <class Type>
50
+ using unique_remote = remote<Type>::unique_remote;
51
+
52
+ export template <class Tag>
53
+ auto make_shared_remote(remote_handle_environment auto& env, value<Tag> value) -> shared_remote<Tag> {
54
+ return remote<Tag>::make_shared(env, value);
55
+ }
56
+
57
+ export template <class Tag>
58
+ auto make_unique_remote(remote_handle_environment auto& env, value<Tag> value) -> unique_remote<Tag> {
59
+ return remote<Tag>::make_unique(env, value);
60
+ }
61
+
62
+ // ---
63
+
64
+ template <class Tag>
65
+ auto remote<Tag>::expire(remote* ptr) -> void {
66
+ std::unique_ptr<remote> self{ptr};
67
+ ptr->scheduler_(
68
+ util::make_indirect_moveable_function(
69
+ [ self = std::move(self) ] mutable -> auto {
70
+ const auto scope = handle_scope{self->env()};
71
+ self.reset();
72
+ }
73
+ )
74
+ );
75
+ }
76
+
77
+ template <class Tag>
78
+ auto remote<Tag>::deref(const environment& env) const -> value<Tag> {
79
+ return value<Tag>::from(get_value(napi_env{env}));
80
+ }
81
+
82
+ template <class Tag>
83
+ auto remote<Tag>::make_shared(remote_handle_environment auto& env, value<Tag> value) -> std::shared_ptr<remote> {
84
+ return std::shared_ptr<remote>{new remote{private_constructor{}, env, value, env.scheduler()}, expire};
85
+ }
86
+
87
+ template <class Tag>
88
+ auto remote<Tag>::make_unique(remote_handle_environment auto& env, value<Tag> value) -> unique_remote {
89
+ return unique_remote{new remote{private_constructor{}, env, value, env.scheduler()}};
90
+ }
91
+
92
+ } // namespace js::napi
@@ -0,0 +1,136 @@
1
+ export module napi_js:handle.types;
2
+ import auto_js;
3
+ import nodejs;
4
+
5
+ namespace js::napi {
6
+
7
+ // `value_handle` is the base class of `value<T>`, and `bound_value<T>`.
8
+ class value_handle {
9
+ protected:
10
+ explicit value_handle(napi_value value) :
11
+ value_{value} {}
12
+
13
+ public:
14
+ value_handle() = default;
15
+
16
+ // Implicit cast back to a `napi_value`
17
+ // NOLINTNEXTLINE(google-explicit-constructor)
18
+ operator napi_value() const { return value_; }
19
+
20
+ // Check empty value
21
+ explicit operator bool() const { return value_ != nullptr; }
22
+
23
+ private:
24
+ napi_value value_{};
25
+ };
26
+
27
+ // `value` forward declarations
28
+ export template <class Tag = value_tag>
29
+ class value;
30
+
31
+ template <class Tag>
32
+ class value_next;
33
+
34
+ template <class Type>
35
+ class value_for_class_of;
36
+
37
+ // `bound_value` forward declarations
38
+ export template <class Tag>
39
+ class bound_value;
40
+
41
+ template <class Tag>
42
+ class bound_value_next;
43
+
44
+ // Map of `value<Tag>` / `bound_value<Tag>` to concrete implementation classes.
45
+ template <class Tag>
46
+ struct value_specialization {
47
+ using value_type = value_next<Tag>;
48
+ using bound_type = bound_value_next<Tag>;
49
+ };
50
+
51
+ // Typed specializations
52
+ template <>
53
+ struct value_specialization<undefined_tag> {
54
+ using value_type = class value_for_undefined;
55
+ using bound_type = bound_value_next<undefined_tag>;
56
+ };
57
+
58
+ template <>
59
+ struct value_specialization<null_tag> {
60
+ using value_type = class value_for_null;
61
+ using bound_type = bound_value_next<null_tag>;
62
+ };
63
+
64
+ template <>
65
+ struct value_specialization<boolean_tag> {
66
+ using value_type = class value_for_boolean;
67
+ using bound_type = class bound_value_for_boolean;
68
+ };
69
+
70
+ template <>
71
+ struct value_specialization<number_tag> {
72
+ using value_type = class value_for_number;
73
+ using bound_type = class bound_value_for_number;
74
+ };
75
+
76
+ template <>
77
+ struct value_specialization<bigint_tag> {
78
+ using value_type = class value_for_bigint;
79
+ using bound_type = class bound_value_for_bigint;
80
+ };
81
+
82
+ template <>
83
+ struct value_specialization<string_tag> {
84
+ using value_type = class value_for_string;
85
+ using bound_type = class bound_value_for_string;
86
+ };
87
+
88
+ template <>
89
+ struct value_specialization<date_tag> {
90
+ using value_type = class value_for_date;
91
+ using bound_type = class bound_value_for_date;
92
+ };
93
+
94
+ template <>
95
+ struct value_specialization<object_tag> {
96
+ using value_type = class value_for_object;
97
+ using bound_type = class bound_value_for_object;
98
+ };
99
+
100
+ template <>
101
+ struct value_specialization<function_tag> {
102
+ using value_type = class value_for_function;
103
+ using bound_type = bound_value_next<function_tag>;
104
+ };
105
+
106
+ template <class Type>
107
+ struct value_specialization<class_tag_of<Type>> {
108
+ using value_type = value_for_class_of<Type>;
109
+ using bound_type = bound_value_next<class_tag_of<Type>>;
110
+ };
111
+
112
+ template <>
113
+ struct value_specialization<external_tag> {
114
+ using value_type = class value_for_external;
115
+ using bound_type = class bound_value_for_external;
116
+ };
117
+
118
+ template <>
119
+ struct value_specialization<vector_tag> {
120
+ using value_type = value_next<vector_tag>;
121
+ using bound_type = class bound_value_for_vector;
122
+ };
123
+
124
+ template <>
125
+ struct value_specialization<list_tag> {
126
+ using value_type = value_next<list_tag>;
127
+ using bound_type = class bound_value_for_list;
128
+ };
129
+
130
+ template <>
131
+ struct value_specialization<dictionary_tag> {
132
+ using value_type = value_next<dictionary_tag>;
133
+ using bound_type = class bound_value_for_dictionary;
134
+ };
135
+
136
+ } // namespace js::napi
@@ -0,0 +1,52 @@
1
+ module;
2
+ #include <concepts>
3
+ #include <type_traits>
4
+ export module napi_js:value_handle;
5
+ import :handle.types;
6
+ import auto_js;
7
+ import nodejs;
8
+
9
+ namespace js::napi {
10
+
11
+ // Heirarchy:
12
+ // value<object_tag> ->
13
+ // value_for_object ->
14
+ // value_next<object_tag> ->
15
+ // value<value_tag> -> ...
16
+
17
+ // Details applied to each level of the `value<T>` hierarchy.
18
+ template <class Tag>
19
+ class value_next : public value<typename Tag::tag_type> {
20
+ public:
21
+ using value<typename Tag::tag_type>::value;
22
+
23
+ // "Downcast" to a more specific tag. Potentially unsafe.
24
+ template <std::convertible_to<Tag> To>
25
+ auto cast(To /*tag*/) const -> value<To> { return value<To>::from(*this); }
26
+
27
+ // Construct from any `napi_value`. Potentially unsafe.
28
+ static auto from(napi_value value_) -> value<Tag> { return value<Tag>{value_}; }
29
+ };
30
+
31
+ // Tagged napi_value
32
+ export template <class Tag>
33
+ class value : public value_specialization<Tag>::value_type {
34
+ public:
35
+ using value_specialization<Tag>::value_type::value_type;
36
+ };
37
+
38
+ // Sentinel instantiation
39
+ template <>
40
+ class value<void> : public value_handle {
41
+ public:
42
+ using value_handle::value_handle;
43
+ };
44
+
45
+ } // namespace js::napi
46
+
47
+ // Specialize for `js::forward`. Makes `js::forward{napi::value<Tag>}` infer
48
+ // `js::forward<Tag, ...>`.
49
+ namespace js {
50
+ template <class Tag>
51
+ struct js::forward_tag_for<napi::value<Tag>> : std::type_identity<Tag> {};
52
+ } // namespace js
@@ -0,0 +1,7 @@
1
+ #include <node_api.h>
2
+ import napi_js;
3
+
4
+ NAPI_MODULE_INIT(/*napi_env env, napi_value exports*/) {
5
+ js::napi::detail::initialize_require::initialize(env, exports);
6
+ return exports;
7
+ }
package/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@auto_js/napi",
3
+ "version": "0.0.1",
4
+ "description": "Automatic JavaScript to N-API bindings",
5
+ "author": "https://github.com/laverdet/",
6
+ "homepage": "https://github.com/laverdet/isolated-vm/tree/experimental/packages/auto_js/napi",
7
+ "license": "ISC",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/laverdet/isolated-vm.git"
11
+ },
12
+ "dependencies": {
13
+ "@auto_js/js": "0.0.1",
14
+ "@auto_js/napi_exports": "0.0.1"
15
+ }
16
+ }