@auto_js/napi 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +17 -1
- package/_module.cc +0 -1
- package/api/api.cc +10 -8
- package/api/finalizer.cc +43 -21
- package/api/napi_scheduler.cc +39 -0
- package/api/napi_scheduler.h.cc +59 -0
- package/api/threadsafe_function.cc +156 -0
- package/api/uv_dlib.cc +4 -3
- package/handle/bound_value.cc +1 -0
- package/handle/reference.cc +2 -2
- package/handle/remote.cc +5 -5
- package/handle/types.cc +10 -0
- package/handle/value.cc +5 -0
- package/package.json +4 -4
- package/support/callback.cc +1 -1
- package/support/environment.cc +12 -31
- package/support/environment.h.cc +1 -8
- package/support/host.cc +339 -0
- package/support/host.h.cc +72 -0
- package/support/promise.cc +11 -11
- package/support/utility.cc +10 -85
- package/support/win32_delay_load_hook.cc +12 -0
- package/transfer/accept.cc +1 -22
- package/transfer/accept.h.cc +1 -1
- package/transfer/visit.cc +98 -84
- package/value/array_buffer.cc +10 -25
- package/value/array_buffer.h.cc +1 -23
- package/value/class.cc +1 -1
- package/value/primitive.h.cc +13 -0
- package/support/container.cc +0 -77
package/support/host.cc
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
module napi_js;
|
|
2
|
+
import std;
|
|
3
|
+
import v8;
|
|
4
|
+
|
|
5
|
+
namespace js::napi {
|
|
6
|
+
util::lockable_with<bool, {.shared = true}> host_environment_initialized;
|
|
7
|
+
|
|
8
|
+
// Addressof operations
|
|
9
|
+
constexpr auto direct_handle_address = [](napi_value value) noexcept -> void* { return value; };
|
|
10
|
+
constexpr auto indirect_handle_address = [](napi_value value) noexcept -> void* { return *reinterpret_cast<void**>(value); };
|
|
11
|
+
|
|
12
|
+
// Fast functions which are supported on all platforms
|
|
13
|
+
// nb: Bun exports these v8 functions, but they crash with the `std::bit_cast<v8::Local<T>>` trick.
|
|
14
|
+
auto v8_is_false(napi_env /*env*/, napi_value value) -> bool {
|
|
15
|
+
return std::bit_cast<v8::Local<v8::Value>>(value)->IsFalse();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
auto v8_is_null(napi_env /*env*/, napi_value value) -> bool {
|
|
19
|
+
return std::bit_cast<v8::Local<v8::Value>>(value)->IsNull();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
auto v8_is_true(napi_env /*env*/, napi_value value) -> bool {
|
|
23
|
+
return std::bit_cast<v8::Local<v8::Value>>(value)->IsTrue();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
auto v8_is_undefined(napi_env /*env*/, napi_value value) -> bool {
|
|
27
|
+
return std::bit_cast<v8::Local<v8::Value>>(value)->IsUndefined();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
auto napi_fast_is_false(napi_env env, napi_value value) -> bool {
|
|
31
|
+
return value == napi::invoke(napi_get_boolean, env, false);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
auto napi_fast_is_null(napi_env env, napi_value value) -> bool {
|
|
35
|
+
return value == napi::invoke(napi_get_null, env);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
auto napi_fast_is_true(napi_env env, napi_value value) -> bool {
|
|
39
|
+
return value == napi::invoke(napi_get_boolean, env, true);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
auto napi_fast_is_undefined(napi_env env, napi_value value) -> bool {
|
|
43
|
+
return value == napi::invoke(napi_get_undefined, env);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Extended fast functions
|
|
47
|
+
// nb: Bun exports these v8 functions, but they crash with the `std::bit_cast<v8::Local<T>>` trick.
|
|
48
|
+
auto fast_is_array(napi_value value) -> bool {
|
|
49
|
+
return std::bit_cast<v8::Local<v8::Value>>(value)->IsArray();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
auto fast_is_bigint(napi_value value) -> bool {
|
|
53
|
+
return std::bit_cast<v8::Local<v8::Value>>(value)->IsBigInt();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
auto fast_is_function(napi_value value) -> bool {
|
|
57
|
+
return std::bit_cast<v8::Local<v8::Value>>(value)->IsFunction();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
auto fast_is_number(napi_value value) -> bool {
|
|
61
|
+
return std::bit_cast<v8::Local<v8::Value>>(value)->IsNumber();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
auto fast_is_object(napi_value value) -> bool {
|
|
65
|
+
return std::bit_cast<v8::Local<v8::Value>>(value)->IsObject();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
auto fast_is_string(napi_value value) -> bool {
|
|
69
|
+
return std::bit_cast<v8::Local<v8::Value>>(value)->IsString();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Bug fixes
|
|
73
|
+
auto napi_is_object_array_buffer(napi_env env, value_of<object_tag> value) -> bool {
|
|
74
|
+
return napi::invoke(napi_is_arraybuffer, env, value);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
auto napi_is_data_block_array_buffer(napi_env env, value_of<data_block_tag> value) -> bool {
|
|
78
|
+
return napi_is_object_array_buffer(env, value);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
auto bun_is_data_block_array_buffer(napi_env env, value_of<data_block_tag> value) -> bool {
|
|
82
|
+
// https://github.com/oven-sh/bun/issues/32624
|
|
83
|
+
auto* string = napi::invoke(napi_coerce_to_string, env, value);
|
|
84
|
+
auto length = napi::invoke(napi_get_value_string_latin1, env, string, nullptr, 0);
|
|
85
|
+
// '[object ArrayBuffer]'
|
|
86
|
+
return length == 20;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
auto bun_is_object_array_buffer(napi_env env, value_of<object_tag> value) -> bool {
|
|
90
|
+
if (napi::invoke(napi_is_arraybuffer, env, value)) {
|
|
91
|
+
return bun_is_data_block_array_buffer(env, value_of<data_block_tag>::from(value));
|
|
92
|
+
} else {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Optional value inspectors.
|
|
98
|
+
auto v8_is_number_int32(value_of<number_tag> value) -> std::optional<bool> {
|
|
99
|
+
return std::bit_cast<v8::Local<v8::Number>>(value)->IsInt32();
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
auto v8_is_string_one_byte(value_of<string_tag> value) -> std::optional<bool> {
|
|
103
|
+
return std::bit_cast<v8::Local<v8::String>>(value)->IsOneByte();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
auto node_api_maybe_is_shared_array_buffer(napi_env env, value_of<object_tag> value) -> std::optional<bool> {
|
|
107
|
+
return napi::invoke(node_api_is_sharedarraybuffer, env, value);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
auto bun_maybe_is_shared_array_buffer(napi_env env, value_of<object_tag> value) -> std::optional<bool> {
|
|
111
|
+
if (napi::invoke(napi_is_arraybuffer, env, value)) {
|
|
112
|
+
return !bun_is_data_block_array_buffer(env, value_of<data_block_tag>::from(value));
|
|
113
|
+
} else {
|
|
114
|
+
return std::nullopt;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
constexpr auto unknown_maybe_is = [](auto...) -> std::optional<bool> { return std::nullopt; };
|
|
119
|
+
|
|
120
|
+
// 'SharedArrayBuffer' functions
|
|
121
|
+
auto v8_make_shared_array_buffer(js::shared_array_buffer::shared_pointer_type data, std::size_t byte_length) -> value_of<shared_array_buffer_tag> {
|
|
122
|
+
auto backing_store = [ & ]() -> auto {
|
|
123
|
+
// v8 does not call the deleter `byte_length` is zero. So the heap-allocated shared_ptr trick
|
|
124
|
+
// does not work in that case.
|
|
125
|
+
if (byte_length == 0) {
|
|
126
|
+
return v8::SharedArrayBuffer::NewBackingStore(nullptr, 0, nullptr, nullptr);
|
|
127
|
+
} else {
|
|
128
|
+
auto holder = std::make_unique<js::shared_array_buffer::shared_pointer_type>(std::move(data));
|
|
129
|
+
auto backing_store = v8::SharedArrayBuffer::NewBackingStore(
|
|
130
|
+
holder->get(),
|
|
131
|
+
byte_length,
|
|
132
|
+
[](void* /*data*/, std::size_t /*length*/, void* param) -> void {
|
|
133
|
+
delete static_cast<js::shared_array_buffer::shared_pointer_type*>(param);
|
|
134
|
+
},
|
|
135
|
+
holder.get()
|
|
136
|
+
);
|
|
137
|
+
std::ignore = holder.release();
|
|
138
|
+
return backing_store;
|
|
139
|
+
}
|
|
140
|
+
}();
|
|
141
|
+
auto shared_array_buffer = v8::SharedArrayBuffer::New(v8::Isolate::GetCurrent(), std::move(backing_store));
|
|
142
|
+
return value_of<shared_array_buffer_tag>::from(std::bit_cast<napi_value>(shared_array_buffer));
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
auto v8_shared_array_buffer_get_byte_length(value_of<shared_array_buffer_tag> buffer) -> std::size_t {
|
|
146
|
+
return std::bit_cast<v8::Local<v8::SharedArrayBuffer>>(napi_value{buffer})->ByteLength();
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
auto v8_shared_array_buffer_get_backing_store(value_of<shared_array_buffer_tag> buffer) -> std::shared_ptr<std::byte[]> {
|
|
150
|
+
auto backing_store = std::bit_cast<v8::Local<v8::SharedArrayBuffer>>(buffer)->GetBackingStore();
|
|
151
|
+
auto* data = reinterpret_cast<std::byte*>(backing_store->Data());
|
|
152
|
+
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
|
|
153
|
+
return std::shared_ptr<std::byte[]>{std::move(backing_store), data};
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// 'ArrayBufferView' constructors
|
|
157
|
+
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
|
|
158
|
+
auto v8_make_sab_data_view(value_of<shared_array_buffer_tag> buffer, std::size_t byte_offset, std::size_t length) -> value_of<data_view_tag> {
|
|
159
|
+
auto buffer_local = std::bit_cast<v8::Local<v8::SharedArrayBuffer>>(napi_value{buffer});
|
|
160
|
+
auto view_local = v8::DataView::New(buffer_local, byte_offset, length);
|
|
161
|
+
return value_of<data_view_tag>::from(std::bit_cast<napi_value>(view_local));
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
template <class Type>
|
|
165
|
+
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
|
|
166
|
+
auto v8_make_sab_typed_array_of(value_of<shared_array_buffer_tag> buffer, std::size_t byte_offset, std::size_t length) -> value_of<typed_array_tag_of<Type>> {
|
|
167
|
+
using constructor_type = type_t<util::overloaded{
|
|
168
|
+
[](std::type_identity<double>) -> std::type_identity<v8::Float64Array> { return {}; },
|
|
169
|
+
[](std::type_identity<float>) -> std::type_identity<v8::Float32Array> { return {}; },
|
|
170
|
+
[](std::type_identity<js::float16_t>) -> std::type_identity<v8::Float16Array> { return {}; },
|
|
171
|
+
[](std::type_identity<js::uint8_clamped_t>) -> std::type_identity<v8::Uint8ClampedArray> { return {}; },
|
|
172
|
+
[](std::type_identity<std::int16_t>) -> std::type_identity<v8::Int16Array> { return {}; },
|
|
173
|
+
[](std::type_identity<std::int32_t>) -> std::type_identity<v8::Int32Array> { return {}; },
|
|
174
|
+
[](std::type_identity<std::int64_t>) -> std::type_identity<v8::BigInt64Array> { return {}; },
|
|
175
|
+
[](std::type_identity<std::int8_t>) -> std::type_identity<v8::Int8Array> { return {}; },
|
|
176
|
+
[](std::type_identity<std::uint16_t>) -> std::type_identity<v8::Uint16Array> { return {}; },
|
|
177
|
+
[](std::type_identity<std::uint32_t>) -> std::type_identity<v8::Uint32Array> { return {}; },
|
|
178
|
+
[](std::type_identity<std::uint64_t>) -> std::type_identity<v8::BigUint64Array> { return {}; },
|
|
179
|
+
[](std::type_identity<std::uint8_t>) -> std::type_identity<v8::Uint8Array> { return {}; },
|
|
180
|
+
}(type<Type>)>;
|
|
181
|
+
auto buffer_local = std::bit_cast<v8::Local<v8::SharedArrayBuffer>>(napi_value{buffer});
|
|
182
|
+
auto view_local = constructor_type::New(buffer_local, byte_offset, length);
|
|
183
|
+
return value_of<typed_array_tag_of<Type>>::from(std::bit_cast<napi_value>(view_local));
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
template <class Type>
|
|
187
|
+
// NOLINTNEXTLINE(performance-unnecessary-value-param)
|
|
188
|
+
constexpr auto unknown_sab_throw = [](auto...) -> Type {
|
|
189
|
+
throw js::type_error{u"SharedArrayBuffer is not supported in this environment"};
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// Dynamic libraries
|
|
193
|
+
constexpr auto node_uv_dlerror =
|
|
194
|
+
[](const uv_lib_t* lib) -> const char* { return uv_dlerror(lib); };
|
|
195
|
+
constexpr auto node_uv_dlopen =
|
|
196
|
+
[](const char* filename, uv_lib_t* lib) -> int { return uv_dlopen(filename, lib); };
|
|
197
|
+
constexpr auto node_uv_dlclose =
|
|
198
|
+
[](uv_lib_t* lib) -> void { uv_dlclose(lib); };
|
|
199
|
+
|
|
200
|
+
constexpr auto unknown_uv_dlerror =
|
|
201
|
+
[](const uv_lib_t* /*lib*/) -> const char* { return "Dynamic libraries are not supported in this environment"; };
|
|
202
|
+
constexpr auto unknown_uv_dlopen =
|
|
203
|
+
[](const char* /*filename*/, uv_lib_t* /*lib*/) -> int { return -1; };
|
|
204
|
+
constexpr auto unknown_uv_dlclose =
|
|
205
|
+
[](uv_lib_t* /*lib*/) -> void { std::unreachable(); };
|
|
206
|
+
|
|
207
|
+
auto initialize_host_environment(napi_env env) -> void {
|
|
208
|
+
if (!*host_environment_initialized.read()) {
|
|
209
|
+
if (auto lock = host_environment_initialized.write(); !*lock) {
|
|
210
|
+
// Detect indirect handles (nodejs, v8 [configurable]) or direct handles (bun, jsc)
|
|
211
|
+
// See: `v8_enable_direct_handle`
|
|
212
|
+
auto uses_direct_handles = [ & ] -> bool {
|
|
213
|
+
auto* array = napi::invoke(napi_create_array_with_length, env, 1);
|
|
214
|
+
napi::invoke0(napi_set_element, env, array, 0, array);
|
|
215
|
+
auto* result = napi::invoke(napi_get_element, env, array, 0);
|
|
216
|
+
if (direct_handle_address(array) == direct_handle_address(result)) {
|
|
217
|
+
return true;
|
|
218
|
+
} else if (indirect_handle_address(array) == indirect_handle_address(result)) {
|
|
219
|
+
return false;
|
|
220
|
+
} else {
|
|
221
|
+
throw std::runtime_error{"Exotic napi handle behavior detected"};
|
|
222
|
+
}
|
|
223
|
+
}();
|
|
224
|
+
*lock = true;
|
|
225
|
+
if (uses_direct_handles) {
|
|
226
|
+
handle_addressof = direct_handle_address;
|
|
227
|
+
} else {
|
|
228
|
+
handle_addressof = indirect_handle_address;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Get `globalThis.process`
|
|
232
|
+
auto* process_global = [ & ]() -> napi_value {
|
|
233
|
+
auto* global = napi::invoke(napi_get_global, env);
|
|
234
|
+
constexpr auto process_cw = util::make_consteval_string_view(util::cw<"process">);
|
|
235
|
+
auto* process_str = napi::invoke(node_api_create_property_key_latin1, env, process_cw.data(), process_cw.length());
|
|
236
|
+
auto* process = napi::invoke(napi_get_property, env, global, process_str);
|
|
237
|
+
if (napi::invoke(napi_typeof, env, process) == napi_object) {
|
|
238
|
+
return process;
|
|
239
|
+
} else {
|
|
240
|
+
return nullptr;
|
|
241
|
+
}
|
|
242
|
+
}();
|
|
243
|
+
|
|
244
|
+
// Detect bun via `process.isBun`
|
|
245
|
+
auto is_bun = process_global == nullptr ? false : [ & ]() -> bool {
|
|
246
|
+
constexpr auto is_bun_cw = util::make_consteval_string_view(util::cw<"isBun">);
|
|
247
|
+
auto* is_bun_str = napi::invoke(node_api_create_property_key_latin1, env, is_bun_cw.data(), is_bun_cw.length());
|
|
248
|
+
auto* is_bun = napi::invoke(napi_get_property, env, process_global, is_bun_str);
|
|
249
|
+
auto* is_bun_bool = napi::invoke(napi_coerce_to_bool, env, is_bun);
|
|
250
|
+
return napi::invoke(napi_get_value_bool, env, is_bun_bool);
|
|
251
|
+
}();
|
|
252
|
+
|
|
253
|
+
// Detect deno via `process.versions.deno`
|
|
254
|
+
auto is_deno = process_global == nullptr ? false : [ & ]() -> bool {
|
|
255
|
+
constexpr auto versions_cw = util::make_consteval_string_view(util::cw<"versions">);
|
|
256
|
+
auto* versions_str = napi::invoke(node_api_create_property_key_latin1, env, versions_cw.data(), versions_cw.length());
|
|
257
|
+
auto* versions = napi::invoke(napi_get_property, env, process_global, versions_str);
|
|
258
|
+
if (napi::invoke(napi_typeof, env, versions) != napi_object) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
constexpr auto deno_cw = util::make_consteval_string_view(util::cw<"deno">);
|
|
262
|
+
auto* deno_str = napi::invoke(node_api_create_property_key_latin1, env, deno_cw.data(), deno_cw.length());
|
|
263
|
+
auto* deno = napi::invoke(napi_get_property, env, versions, deno_str);
|
|
264
|
+
auto* deno_bool = napi::invoke(napi_coerce_to_bool, env, deno);
|
|
265
|
+
return napi::invoke(napi_get_value_bool, env, deno_bool);
|
|
266
|
+
}();
|
|
267
|
+
|
|
268
|
+
auto is_nodejs = !is_bun && !is_deno;
|
|
269
|
+
if (is_nodejs) {
|
|
270
|
+
has_extended_fast_is_functions = true;
|
|
271
|
+
fast_is_false = v8_is_false;
|
|
272
|
+
fast_is_null = v8_is_null;
|
|
273
|
+
fast_is_true = v8_is_true;
|
|
274
|
+
fast_is_undefined = v8_is_undefined;
|
|
275
|
+
host_uv_dlclose = node_uv_dlclose;
|
|
276
|
+
host_uv_dlerror = node_uv_dlerror;
|
|
277
|
+
host_uv_dlopen = node_uv_dlopen;
|
|
278
|
+
make_sab_typed_array_of<double> = v8_make_sab_typed_array_of<double>;
|
|
279
|
+
make_sab_typed_array_of<float> = v8_make_sab_typed_array_of<float>;
|
|
280
|
+
make_sab_typed_array_of<js::float16_t> = v8_make_sab_typed_array_of<js::float16_t>;
|
|
281
|
+
make_sab_typed_array_of<js::uint8_clamped_t> = v8_make_sab_typed_array_of<js::uint8_clamped_t>;
|
|
282
|
+
make_sab_typed_array_of<std::int16_t> = v8_make_sab_typed_array_of<std::int16_t>;
|
|
283
|
+
make_sab_typed_array_of<std::int32_t> = v8_make_sab_typed_array_of<std::int32_t>;
|
|
284
|
+
make_sab_typed_array_of<std::int64_t> = v8_make_sab_typed_array_of<std::int64_t>;
|
|
285
|
+
make_sab_typed_array_of<std::int8_t> = v8_make_sab_typed_array_of<std::int8_t>;
|
|
286
|
+
make_sab_typed_array_of<std::uint16_t> = v8_make_sab_typed_array_of<std::uint16_t>;
|
|
287
|
+
make_sab_typed_array_of<std::uint32_t> = v8_make_sab_typed_array_of<std::uint32_t>;
|
|
288
|
+
make_sab_typed_array_of<std::uint64_t> = v8_make_sab_typed_array_of<std::uint64_t>;
|
|
289
|
+
make_sab_typed_array_of<std::uint8_t> = v8_make_sab_typed_array_of<std::uint8_t>;
|
|
290
|
+
make_shared_array_buffer = v8_make_shared_array_buffer;
|
|
291
|
+
maybe_is_number_int32 = v8_is_number_int32;
|
|
292
|
+
maybe_is_shared_array_buffer = node_api_maybe_is_shared_array_buffer;
|
|
293
|
+
maybe_is_string_latin1 = v8_is_string_one_byte;
|
|
294
|
+
shared_array_buffer_get_backing_store = v8_shared_array_buffer_get_backing_store;
|
|
295
|
+
shared_array_buffer_get_byte_length = v8_shared_array_buffer_get_byte_length;
|
|
296
|
+
} else {
|
|
297
|
+
has_extended_fast_is_functions = false;
|
|
298
|
+
fast_is_false = napi_fast_is_false;
|
|
299
|
+
fast_is_null = napi_fast_is_null;
|
|
300
|
+
fast_is_true = napi_fast_is_true;
|
|
301
|
+
fast_is_undefined = napi_fast_is_undefined;
|
|
302
|
+
host_uv_dlclose = unknown_uv_dlclose;
|
|
303
|
+
host_uv_dlerror = unknown_uv_dlerror;
|
|
304
|
+
host_uv_dlopen = unknown_uv_dlopen;
|
|
305
|
+
make_sab_data_view = unknown_sab_throw<value_of<data_view_tag>>;
|
|
306
|
+
make_sab_typed_array_of<double> = unknown_sab_throw<value_of<typed_array_tag_of<double>>>;
|
|
307
|
+
make_sab_typed_array_of<float> = unknown_sab_throw<value_of<typed_array_tag_of<float>>>;
|
|
308
|
+
make_sab_typed_array_of<js::float16_t> = unknown_sab_throw<value_of<typed_array_tag_of<js::float16_t>>>;
|
|
309
|
+
make_sab_typed_array_of<js::uint8_clamped_t> = unknown_sab_throw<value_of<typed_array_tag_of<js::uint8_clamped_t>>>;
|
|
310
|
+
make_sab_typed_array_of<std::int16_t> = unknown_sab_throw<value_of<typed_array_tag_of<std::int16_t>>>;
|
|
311
|
+
make_sab_typed_array_of<std::int32_t> = unknown_sab_throw<value_of<typed_array_tag_of<std::int32_t>>>;
|
|
312
|
+
make_sab_typed_array_of<std::int64_t> = unknown_sab_throw<value_of<typed_array_tag_of<std::int64_t>>>;
|
|
313
|
+
make_sab_typed_array_of<std::int8_t> = unknown_sab_throw<value_of<typed_array_tag_of<std::int8_t>>>;
|
|
314
|
+
make_sab_typed_array_of<std::uint16_t> = unknown_sab_throw<value_of<typed_array_tag_of<std::uint16_t>>>;
|
|
315
|
+
make_sab_typed_array_of<std::uint32_t> = unknown_sab_throw<value_of<typed_array_tag_of<std::uint32_t>>>;
|
|
316
|
+
make_sab_typed_array_of<std::uint64_t> = unknown_sab_throw<value_of<typed_array_tag_of<std::uint64_t>>>;
|
|
317
|
+
make_sab_typed_array_of<std::uint8_t> = unknown_sab_throw<value_of<typed_array_tag_of<std::uint8_t>>>;
|
|
318
|
+
make_shared_array_buffer = unknown_sab_throw<value_of<shared_array_buffer_tag>>;
|
|
319
|
+
maybe_is_number_int32 = unknown_maybe_is;
|
|
320
|
+
maybe_is_shared_array_buffer = unknown_maybe_is;
|
|
321
|
+
maybe_is_string_latin1 = unknown_maybe_is;
|
|
322
|
+
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
|
|
323
|
+
shared_array_buffer_get_backing_store = unknown_sab_throw<std::shared_ptr<std::byte[]>>;
|
|
324
|
+
shared_array_buffer_get_byte_length = unknown_sab_throw<std::size_t>;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Bug fixes
|
|
328
|
+
is_data_block_array_buffer = napi_is_data_block_array_buffer;
|
|
329
|
+
is_object_array_buffer = napi_is_object_array_buffer;
|
|
330
|
+
if (is_bun) {
|
|
331
|
+
is_data_block_array_buffer = bun_is_data_block_array_buffer;
|
|
332
|
+
is_object_array_buffer = bun_is_object_array_buffer;
|
|
333
|
+
maybe_is_shared_array_buffer = bun_maybe_is_shared_array_buffer;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
} // namespace js::napi
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export module napi_js:support.host;
|
|
2
|
+
import :value_handle;
|
|
3
|
+
import std;
|
|
4
|
+
|
|
5
|
+
namespace js::napi {
|
|
6
|
+
|
|
7
|
+
// Addressof operation
|
|
8
|
+
auto (*handle_addressof)(napi_value handle) noexcept -> void* = nullptr;
|
|
9
|
+
|
|
10
|
+
// Fast functions which are supported on all platforms
|
|
11
|
+
auto (*fast_is_false)(napi_env env, napi_value value) -> bool;
|
|
12
|
+
auto (*fast_is_null)(napi_env env, napi_value value) -> bool;
|
|
13
|
+
auto (*fast_is_true)(napi_env env, napi_value value) -> bool;
|
|
14
|
+
auto (*fast_is_undefined)(napi_env env, napi_value value) -> bool;
|
|
15
|
+
|
|
16
|
+
// Extended fast functions
|
|
17
|
+
auto has_extended_fast_is_functions = false;
|
|
18
|
+
auto fast_is_array(napi_value value) -> bool;
|
|
19
|
+
auto fast_is_bigint(napi_value value) -> bool;
|
|
20
|
+
auto fast_is_function(napi_value value) -> bool;
|
|
21
|
+
auto fast_is_number(napi_value value) -> bool;
|
|
22
|
+
auto fast_is_object(napi_value value) -> bool;
|
|
23
|
+
auto fast_is_string(napi_value value) -> bool;
|
|
24
|
+
|
|
25
|
+
// Bug fixes
|
|
26
|
+
auto (*is_object_array_buffer)(napi_env env, value_of<object_tag> value) -> bool = nullptr;
|
|
27
|
+
auto (*is_data_block_array_buffer)(napi_env env, value_of<data_block_tag> value) -> bool = nullptr;
|
|
28
|
+
|
|
29
|
+
// Optional value inspectors. int32 & latin1 will select optimized representations, and
|
|
30
|
+
// shared_array_buffer is required for support of that value type.
|
|
31
|
+
auto (*maybe_is_number_int32)(value_of<number_tag> value) -> std::optional<bool> = nullptr;
|
|
32
|
+
auto (*maybe_is_string_latin1)(value_of<string_tag> value) -> std::optional<bool> = nullptr;
|
|
33
|
+
auto (*maybe_is_shared_array_buffer)(napi_env env, value_of<object_tag> value) -> std::optional<bool> = nullptr;
|
|
34
|
+
|
|
35
|
+
// 'SharedArrayBuffer' functions
|
|
36
|
+
auto (*make_shared_array_buffer)(js::shared_array_buffer::shared_pointer_type data, std::size_t byte_length) -> value_of<shared_array_buffer_tag> = nullptr;
|
|
37
|
+
auto (*shared_array_buffer_get_byte_length)(value_of<shared_array_buffer_tag> buffer) -> std::size_t = nullptr;
|
|
38
|
+
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
|
|
39
|
+
auto (*shared_array_buffer_get_backing_store)(value_of<shared_array_buffer_tag> buffer) -> std::shared_ptr<std::byte[]> = nullptr;
|
|
40
|
+
|
|
41
|
+
// 'ArrayBufferView' constructors
|
|
42
|
+
auto (*make_sab_data_view)(value_of<shared_array_buffer_tag> buffer, std::size_t byte_offset, std::size_t length) -> value_of<data_view_tag> = nullptr;
|
|
43
|
+
|
|
44
|
+
template <class Type>
|
|
45
|
+
using make_sab_typed_array_constructor =
|
|
46
|
+
auto(value_of<shared_array_buffer_tag> buffer, std::size_t byte_offset, std::size_t length) -> value_of<typed_array_tag_of<Type>>;
|
|
47
|
+
|
|
48
|
+
template <class Type>
|
|
49
|
+
make_sab_typed_array_constructor<Type>* make_sab_typed_array_of;
|
|
50
|
+
|
|
51
|
+
template make_sab_typed_array_constructor<double>* make_sab_typed_array_of<double>;
|
|
52
|
+
template make_sab_typed_array_constructor<float>* make_sab_typed_array_of<float>;
|
|
53
|
+
template make_sab_typed_array_constructor<js::float16_t>* make_sab_typed_array_of<js::float16_t>;
|
|
54
|
+
template make_sab_typed_array_constructor<js::uint8_clamped_t>* make_sab_typed_array_of<js::uint8_clamped_t>;
|
|
55
|
+
template make_sab_typed_array_constructor<std::int16_t>* make_sab_typed_array_of<std::int16_t>;
|
|
56
|
+
template make_sab_typed_array_constructor<std::int32_t>* make_sab_typed_array_of<std::int32_t>;
|
|
57
|
+
template make_sab_typed_array_constructor<std::int64_t>* make_sab_typed_array_of<std::int64_t>;
|
|
58
|
+
template make_sab_typed_array_constructor<std::int8_t>* make_sab_typed_array_of<std::int8_t>;
|
|
59
|
+
template make_sab_typed_array_constructor<std::uint16_t>* make_sab_typed_array_of<std::uint16_t>;
|
|
60
|
+
template make_sab_typed_array_constructor<std::uint32_t>* make_sab_typed_array_of<std::uint32_t>;
|
|
61
|
+
template make_sab_typed_array_constructor<std::uint64_t>* make_sab_typed_array_of<std::uint64_t>;
|
|
62
|
+
template make_sab_typed_array_constructor<std::uint8_t>* make_sab_typed_array_of<std::uint8_t>;
|
|
63
|
+
|
|
64
|
+
// uv dlfcn wrappers
|
|
65
|
+
auto (*host_uv_dlerror)(const uv_lib_t* lib) -> const char*;
|
|
66
|
+
auto (*host_uv_dlopen)(const char* filename, uv_lib_t* lib) -> int;
|
|
67
|
+
auto (*host_uv_dlclose)(uv_lib_t* lib) -> void;
|
|
68
|
+
|
|
69
|
+
// Once per process, this detects the host environment (nodejs vs bun)
|
|
70
|
+
auto initialize_host_environment(napi_env env) -> void;
|
|
71
|
+
|
|
72
|
+
} // namespace js::napi
|
package/support/promise.cc
CHANGED
|
@@ -13,7 +13,7 @@ class resolver {
|
|
|
13
13
|
deferred_{deferred},
|
|
14
14
|
scheduler_{env.scheduler()},
|
|
15
15
|
dispatch_{dispatch} {
|
|
16
|
-
scheduler_.increment_ref();
|
|
16
|
+
scheduler_.increment_ref(napi_env{env});
|
|
17
17
|
}
|
|
18
18
|
resolver(const resolver&) = delete;
|
|
19
19
|
resolver(resolver&&) = default;
|
|
@@ -42,7 +42,8 @@ class resolver {
|
|
|
42
42
|
|
|
43
43
|
// Fulfill using direct resolution value. Note it still needs try/catch (in `fulfill`) because
|
|
44
44
|
// the `transfer` operation could fail.
|
|
45
|
-
|
|
45
|
+
template <class Type>
|
|
46
|
+
auto resolve(Type value) noexcept(std::is_nothrow_move_constructible_v<Type>) -> void {
|
|
46
47
|
using expected_type = std::expected<decltype(value), js::error_value>;
|
|
47
48
|
schedule(
|
|
48
49
|
[](Environment& /*env*/, auto value) -> expected_type {
|
|
@@ -53,7 +54,7 @@ class resolver {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
// Fulfill using thrown error.
|
|
56
|
-
auto reject(js::error_value error) -> void {
|
|
57
|
+
auto reject(js::error_value error) noexcept -> void {
|
|
57
58
|
using expected_type = std::expected<std::monostate, js::error_value>;
|
|
58
59
|
schedule(
|
|
59
60
|
[](Environment& /*env*/, js::error_value error) -> expected_type {
|
|
@@ -64,13 +65,13 @@ class resolver {
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
private:
|
|
67
|
-
|
|
68
|
+
template <class Operation, class... Args>
|
|
69
|
+
auto schedule(Operation operation, Args&&... args) noexcept(std::is_nothrow_move_constructible_v<Operation> && (std::is_nothrow_move_constructible_v<Args> && ...)) -> void {
|
|
68
70
|
auto scheduler = std::move(scheduler_);
|
|
69
71
|
scheduler(
|
|
70
|
-
[](napi_env env, napi_deferred deferred, auto operation, auto&&... args) {
|
|
72
|
+
[](napi_env env, napi_value /*nothing*/, napi_deferred deferred, auto operation, auto&&... args) noexcept {
|
|
71
73
|
auto& environment = napi::environment::unsafe_get_environment_as<Environment>(env);
|
|
72
|
-
environment.scheduler().decrement_ref();
|
|
73
|
-
const auto scope = js::napi::handle_scope{env};
|
|
74
|
+
environment.scheduler().decrement_ref(env);
|
|
74
75
|
try {
|
|
75
76
|
auto expected = operation(environment, std::forward<decltype(args)>(args)...);
|
|
76
77
|
if (expected) {
|
|
@@ -82,13 +83,12 @@ class resolver {
|
|
|
82
83
|
}
|
|
83
84
|
} catch (const napi::pending_error& /*error*/) {
|
|
84
85
|
auto* exception = napi::invoke(napi_get_and_clear_last_exception, env);
|
|
85
|
-
napi::
|
|
86
|
+
napi::invoke0_noexcept(napi_reject_deferred, env, deferred, exception);
|
|
86
87
|
} catch (const js::error& error) {
|
|
87
88
|
auto exception = js::transfer_in_strict<napi_value>(error, environment);
|
|
88
|
-
napi::
|
|
89
|
+
napi::invoke0_noexcept(napi_reject_deferred, env, deferred, exception);
|
|
89
90
|
}
|
|
90
91
|
},
|
|
91
|
-
env_,
|
|
92
92
|
deferred_,
|
|
93
93
|
std::move(operation),
|
|
94
94
|
std::forward<decltype(args)>(args)...
|
|
@@ -97,7 +97,7 @@ class resolver {
|
|
|
97
97
|
|
|
98
98
|
napi_env env_;
|
|
99
99
|
napi_deferred deferred_;
|
|
100
|
-
|
|
100
|
+
napi_scheduler scheduler_;
|
|
101
101
|
Dispatch dispatch_;
|
|
102
102
|
};
|
|
103
103
|
|
package/support/utility.cc
CHANGED
|
@@ -1,102 +1,27 @@
|
|
|
1
|
-
module;
|
|
2
|
-
#include "auto_js/no_unique_address.h"
|
|
3
1
|
export module napi_js:utility;
|
|
2
|
+
import :support.host;
|
|
4
3
|
import nodejs;
|
|
5
4
|
import std;
|
|
6
|
-
import util;
|
|
7
5
|
|
|
8
6
|
namespace js::napi {
|
|
9
7
|
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
// Null handle for both direct and indirect handles
|
|
13
|
-
// Note: 1 -> 0x1'0000'0000
|
|
14
|
-
const std::uint64_t null_value_handle_data = 0x7f7f'7f7f'7f7f'7f7f;
|
|
15
|
-
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
|
|
16
|
-
auto* const null_value_handle = reinterpret_cast<napi_value>(const_cast<std::uint64_t*>(&null_value_handle_data));
|
|
17
|
-
|
|
18
|
-
// Unwrap the v8 address of the napi handle. This points into the v8 heap.
|
|
19
|
-
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
|
|
20
|
-
struct virtual_handle_address {
|
|
21
|
-
protected:
|
|
22
|
-
~virtual_handle_address() = default;
|
|
23
|
-
|
|
24
|
-
public:
|
|
25
|
-
virtual auto operator()(napi_value value) const noexcept -> void* = 0;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
struct direct_handle_address final : virtual_handle_address {
|
|
29
|
-
auto operator()(napi_value value) const noexcept -> void* final {
|
|
30
|
-
return value;
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
struct indirect_handle_address final : virtual_handle_address {
|
|
35
|
-
auto operator()(napi_value value) const noexcept -> void* final {
|
|
36
|
-
return *reinterpret_cast<void**>(value);
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// Address equality for underlying napi handles.
|
|
41
|
-
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
|
|
42
|
-
struct virtual_address_equal {
|
|
43
|
-
protected:
|
|
44
|
-
~virtual_address_equal() = default;
|
|
45
|
-
|
|
46
|
-
public:
|
|
47
|
-
virtual auto operator()(napi_value left, napi_value right) const noexcept -> bool = 0;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
template <class Address>
|
|
51
|
-
struct virtual_address_equal_of final : virtual_address_equal {
|
|
52
|
-
public:
|
|
53
|
-
auto operator()(napi_value left, napi_value right) const noexcept -> bool final {
|
|
54
|
-
return address_(left) == address_(right);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
private:
|
|
58
|
-
NO_UNIQUE_ADDRESS Address address_;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
using direct_address_equal = virtual_address_equal_of<direct_handle_address>;
|
|
62
|
-
using indirect_address_equal = virtual_address_equal_of<indirect_handle_address>;
|
|
63
|
-
|
|
64
|
-
// Polymorphic address equality operator
|
|
65
|
-
export class address_equal {
|
|
66
|
-
private:
|
|
67
|
-
using virtual_equal_type = util::covariant_value<virtual_address_equal, direct_address_equal, indirect_address_equal>;
|
|
68
|
-
|
|
69
|
-
public:
|
|
70
|
-
explicit address_equal(bool uses_direct_handles) :
|
|
71
|
-
equal_{
|
|
72
|
-
uses_direct_handles
|
|
73
|
-
? virtual_equal_type{direct_address_equal{}}
|
|
74
|
-
: virtual_equal_type{indirect_address_equal{}}
|
|
75
|
-
} {}
|
|
76
|
-
|
|
8
|
+
// Equality check for underlying handle. False negatives on primitives.
|
|
9
|
+
struct addressof_equal {
|
|
77
10
|
auto operator()(napi_value left, napi_value right) const noexcept -> bool {
|
|
78
|
-
|
|
79
|
-
return (*equal_)(left, right);
|
|
11
|
+
return handle_addressof(left) == handle_addressof(right);
|
|
80
12
|
}
|
|
81
|
-
|
|
82
|
-
private:
|
|
83
|
-
virtual_equal_type equal_;
|
|
84
13
|
};
|
|
85
14
|
|
|
86
15
|
// Address hash for napi value handles.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
public:
|
|
16
|
+
struct addressof_hash : std::hash<void*> {
|
|
17
|
+
using std::hash<void*>::operator();
|
|
90
18
|
auto operator()(napi_value value) const noexcept -> std::size_t {
|
|
91
|
-
return
|
|
19
|
+
return (*this)(handle_addressof(value));
|
|
92
20
|
}
|
|
93
|
-
|
|
94
|
-
private:
|
|
95
|
-
NO_UNIQUE_ADDRESS Address address_;
|
|
96
|
-
NO_UNIQUE_ADDRESS std::hash<void*> hash_;
|
|
97
21
|
};
|
|
98
22
|
|
|
99
|
-
|
|
100
|
-
|
|
23
|
+
// Unordered map for napi values
|
|
24
|
+
template <class Type>
|
|
25
|
+
using unordered_value_map = std::unordered_map<napi_value, Type, addressof_hash, addressof_equal>;
|
|
101
26
|
|
|
102
27
|
} // namespace js::napi
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#define WIN32_LEAN_AND_MEAN
|
|
2
|
+
#include <windows.h>
|
|
3
|
+
#include <delayimp.h>
|
|
4
|
+
import std;
|
|
5
|
+
|
|
6
|
+
const auto __pfnDliNotifyHook2 = PfnDliHook{[](unsigned event, DelayLoadInfo* info) -> FARPROC {
|
|
7
|
+
if (event == dliNotePreLoadLibrary && std::string_view{info->szDll} == "node.exe") {
|
|
8
|
+
return reinterpret_cast<FARPROC>(GetModuleHandle(nullptr));
|
|
9
|
+
} else {
|
|
10
|
+
return nullptr;
|
|
11
|
+
}
|
|
12
|
+
}};
|
package/transfer/accept.cc
CHANGED
|
@@ -121,28 +121,7 @@ auto accept_basic_napi_value::operator()(array_buffer_tag /*tag*/, visit_holder
|
|
|
121
121
|
|
|
122
122
|
auto accept_basic_napi_value::operator()(shared_array_buffer_tag /*tag*/, visit_holder /*visit*/, js::shared_array_buffer&& subject) const
|
|
123
123
|
-> js::referenceable_value<value_of<shared_array_buffer_tag>> {
|
|
124
|
-
auto
|
|
125
|
-
auto byte_length = subject.byte_length();
|
|
126
|
-
// v8 does not call the deleter `byte_length` is zero. So the heap-allocated shared_ptr trick
|
|
127
|
-
// does not work in that case.
|
|
128
|
-
if (byte_length == 0) {
|
|
129
|
-
return v8::SharedArrayBuffer::NewBackingStore(nullptr, 0, nullptr, nullptr);
|
|
130
|
-
} else {
|
|
131
|
-
auto holder = std::make_unique<js::shared_array_buffer::shared_pointer_type>(std::move(subject).acquire_ownership());
|
|
132
|
-
auto backing_store = v8::SharedArrayBuffer::NewBackingStore(
|
|
133
|
-
holder->get(),
|
|
134
|
-
byte_length,
|
|
135
|
-
[](void* /*data*/, std::size_t /*length*/, void* param) -> void {
|
|
136
|
-
delete static_cast<js::shared_array_buffer::shared_pointer_type*>(param);
|
|
137
|
-
},
|
|
138
|
-
holder.get()
|
|
139
|
-
);
|
|
140
|
-
std::ignore = holder.release();
|
|
141
|
-
return backing_store;
|
|
142
|
-
}
|
|
143
|
-
}();
|
|
144
|
-
auto shared_array_buffer = v8::SharedArrayBuffer::New(v8::Isolate::GetCurrent(), std::move(backing_store));
|
|
145
|
-
auto value = value_of<shared_array_buffer_tag>::from(std::bit_cast<napi_value>(shared_array_buffer));
|
|
124
|
+
auto value = make_shared_array_buffer(std::move(subject).acquire_ownership(), subject.byte_length());
|
|
146
125
|
return js::referenceable_value{value};
|
|
147
126
|
}
|
|
148
127
|
|
package/transfer/accept.h.cc
CHANGED