@auto_js/v8 0.0.11 → 0.0.12
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 +2 -0
- package/_module.cc +1 -0
- package/container/hash.cc +5 -1
- package/evaluation/module.cc +2 -0
- package/handle/collected_handle.cc +1 -29
- package/handle/value_of.cc +4 -1
- package/package.json +3 -3
- package/support/callback.cc +4 -0
- package/support/callback_storage.cc +23 -33
- package/transfer/accept.cc +18 -8
- package/transfer/transfer_list.cc +141 -0
- package/transfer/visit.cc +153 -96
- package/value/array.cc +9 -4
- package/value/array.h.cc +11 -3
- package/value/external.h.cc +136 -0
- package/value/object.cc +1 -1
- package/value/primitive.cc +0 -12
- package/value/primitive.h.cc +1 -0
- package/value/value.cc +1 -0
package/CMakeLists.txt
CHANGED
|
@@ -39,10 +39,12 @@ target_sources(${v8_js}
|
|
|
39
39
|
support/lock.h.cc
|
|
40
40
|
support/unmaybe.cc
|
|
41
41
|
transfer/accept.h.cc
|
|
42
|
+
transfer/transfer_list.cc
|
|
42
43
|
transfer/visit.cc
|
|
43
44
|
value/array_buffer.h.cc
|
|
44
45
|
value/array.h.cc
|
|
45
46
|
value/callback_info.cc
|
|
47
|
+
value/external.h.cc
|
|
46
48
|
value/fixed_array.h.cc
|
|
47
49
|
value/function.cc
|
|
48
50
|
value/function.h.cc
|
package/_module.cc
CHANGED
package/container/hash.cc
CHANGED
|
@@ -4,13 +4,17 @@ import v8;
|
|
|
4
4
|
|
|
5
5
|
namespace js::iv8 {
|
|
6
6
|
|
|
7
|
+
// Physical location of the local handle's value, comparable between unrelated handle types
|
|
8
|
+
export constexpr auto handle_addressof =
|
|
9
|
+
[]<class Type>(v8::Local<Type> local) -> void* { return *local; };
|
|
10
|
+
|
|
7
11
|
// Hash based on the physical location of the local handle's value
|
|
8
12
|
export struct address_hash : std::hash<void*> {
|
|
9
13
|
using std::hash<void*>::operator();
|
|
10
14
|
|
|
11
15
|
template <class Type>
|
|
12
16
|
auto operator()(v8::Local<Type> local) const -> std::size_t {
|
|
13
|
-
return (*this)(
|
|
17
|
+
return (*this)(handle_addressof(local));
|
|
14
18
|
}
|
|
15
19
|
};
|
|
16
20
|
|
package/evaluation/module.cc
CHANGED
|
@@ -132,7 +132,9 @@ auto module_record::link(context_lock_witness lock, module_link_record link_reco
|
|
|
132
132
|
}
|
|
133
133
|
return link_record.modules.at(link_record.payload.at(it->second + index));
|
|
134
134
|
};
|
|
135
|
+
// nb: The initializer only runs on the first pass through the declaration.
|
|
135
136
|
thread_local auto* linker_ptr = &linker;
|
|
137
|
+
linker_ptr = &linker;
|
|
136
138
|
|
|
137
139
|
// v8 linker callback
|
|
138
140
|
auto v8_callback = v8::Module::ResolveModuleByIndexCallback{
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
module;
|
|
2
|
-
#include <v8_js/version.h>
|
|
3
1
|
export module v8_js:collected_handle;
|
|
4
2
|
import :lock;
|
|
5
3
|
import std;
|
|
@@ -34,6 +32,7 @@ class collected_handle
|
|
|
34
32
|
pool_{pool},
|
|
35
33
|
value_{std::forward<decltype(args)>(args)...} {}
|
|
36
34
|
|
|
35
|
+
auto get() -> Type* { return &value_; }
|
|
37
36
|
auto operator*() -> Type& { return value_; }
|
|
38
37
|
static auto make(const collected_handle_lock& lock, auto&&... args) -> unique_ptr
|
|
39
38
|
requires std::constructible_from<Type, decltype(args)...>;
|
|
@@ -45,10 +44,6 @@ class collected_handle
|
|
|
45
44
|
Type value_;
|
|
46
45
|
};
|
|
47
46
|
|
|
48
|
-
// Untagged v8-managed host object
|
|
49
|
-
export template <class Type>
|
|
50
|
-
using collected_external_of = collected_handle<v8::External, Type>;
|
|
51
|
-
|
|
52
47
|
// ---
|
|
53
48
|
|
|
54
49
|
template <class Value, class Type>
|
|
@@ -73,27 +68,4 @@ auto collected_handle<Value, Type>::reset(const collected_handle_lock& lock, uni
|
|
|
73
68
|
});
|
|
74
69
|
}
|
|
75
70
|
|
|
76
|
-
export template <class Type>
|
|
77
|
-
auto make_collected_external(const collected_handle_lock& lock, auto&&... args) -> v8::Local<v8::External> {
|
|
78
|
-
auto unique = collected_external_of<Type>::make(lock, std::forward<decltype(args)>(args)...);
|
|
79
|
-
#if V8_HAS_TAGGED_EXTERNAL
|
|
80
|
-
auto value = v8::External::New(lock.isolate(), unique.get(), 0);
|
|
81
|
-
#else
|
|
82
|
-
auto value = v8::External::New(lock.isolate(), unique.get());
|
|
83
|
-
#endif
|
|
84
|
-
collected_handle<v8::External, Type>::reset(lock, std::move(unique), value);
|
|
85
|
-
return value;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export template <class Type>
|
|
89
|
-
auto unwrap_collected_external(v8::Local<v8::External> external) -> Type& {
|
|
90
|
-
using handle_type = collected_external_of<Type>;
|
|
91
|
-
#if V8_HAS_TAGGED_EXTERNAL
|
|
92
|
-
// TODO: Use this feature
|
|
93
|
-
return **static_cast<handle_type*>(external->Value(0));
|
|
94
|
-
#else
|
|
95
|
-
return **static_cast<handle_type*>(external->Value());
|
|
96
|
-
#endif
|
|
97
|
-
}
|
|
98
|
-
|
|
99
71
|
} // namespace js::iv8
|
package/handle/value_of.cc
CHANGED
|
@@ -16,8 +16,11 @@ class value_of : public value_specialization<Tag>::type {
|
|
|
16
16
|
public:
|
|
17
17
|
using value_specialization<Tag>::type::type;
|
|
18
18
|
|
|
19
|
+
template <std::convertible_to<tag_to_v8<Tag>> Type>
|
|
19
20
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
20
|
-
operator v8::Local<
|
|
21
|
+
operator v8::Local<Type>() const {
|
|
22
|
+
return std::bit_cast<v8::Local<Type>>(v8::Local<v8::Data>{*this});
|
|
23
|
+
}
|
|
21
24
|
};
|
|
22
25
|
|
|
23
26
|
template <class Type>
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@auto_js/v8",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"author": "https://github.com/laverdet/",
|
|
5
5
|
"homepage": "https://github.com/laverdet/isolated-vm/tree/experimental/packages/auto_js/v8",
|
|
6
6
|
"license": "ISC",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@auto_js/js": "^0.0.
|
|
9
|
-
"@auto_js/v8_exports": "^0.0.
|
|
8
|
+
"@auto_js/js": "^0.0.12",
|
|
9
|
+
"@auto_js/v8_exports": "^0.0.9"
|
|
10
10
|
},
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
package/support/callback.cc
CHANGED
|
@@ -28,6 +28,8 @@ constexpr auto make_free_function_with_try_catch =
|
|
|
28
28
|
using callback_type = decltype(callback);
|
|
29
29
|
auto bound_function = util::bind{
|
|
30
30
|
[](callback_type& callback, Lock lock, const v8::FunctionCallbackInfo<v8::Value>& info) noexcept(Nx) -> void {
|
|
31
|
+
auto disallow_js =
|
|
32
|
+
v8::Isolate::DisallowJavascriptExecutionScope{lock.isolate(), v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE};
|
|
31
33
|
// NOLINTNEXTLINE(cppcoreguidelines-slicing)
|
|
32
34
|
auto result = invoke_internal_error_scope(lock, [ & ] -> auto {
|
|
33
35
|
auto run = util::regular_return{[ & ] -> decltype(auto) {
|
|
@@ -61,6 +63,8 @@ constexpr auto make_free_function_noexcept =
|
|
|
61
63
|
using callback_type = decltype(callback);
|
|
62
64
|
auto bound_function = util::bind{
|
|
63
65
|
[](callback_type& callback, Lock lock, const v8::FunctionCallbackInfo<v8::Value>& /*info*/) noexcept -> void {
|
|
66
|
+
auto disallow_js =
|
|
67
|
+
v8::Isolate::DisallowJavascriptExecutionScope{lock.isolate(), v8::Isolate::DisallowJavascriptExecutionScope::CRASH_ON_FAILURE};
|
|
64
68
|
auto run = util::regular_return{[ & ] -> decltype(auto) {
|
|
65
69
|
return callback(lock);
|
|
66
70
|
}};
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
module;
|
|
2
|
-
#include <v8_js/version.h>
|
|
3
1
|
export module v8_js:callback_storage;
|
|
4
|
-
import :
|
|
2
|
+
import :external;
|
|
5
3
|
import :unmaybe;
|
|
6
4
|
import std;
|
|
7
5
|
import v8;
|
|
@@ -70,41 +68,32 @@ auto make_callback_storage(const auto& lock, auto function) {
|
|
|
70
68
|
auto bound = make_bound_callback(std::type_identity<signature_type>{}, lock, std::move(function));
|
|
71
69
|
using bound_type = decltype(bound);
|
|
72
70
|
if constexpr (std::is_trivially_copyable_v<bound_type>) {
|
|
73
|
-
static_assert(std::is_trivially_destructible_v<bound_type>);
|
|
74
71
|
if constexpr (std::is_empty_v<bound_type>) {
|
|
75
72
|
// Constant expression function of 0 size. No data needed at all!
|
|
76
73
|
auto data = v8::Local<v8::Value>{};
|
|
77
|
-
|
|
74
|
+
constexpr auto callback = v8::FunctionCallback{[](const v8::FunctionCallbackInfo<v8::Value>& info) -> void {
|
|
78
75
|
auto invoke = bound_type{};
|
|
79
76
|
invoke(info);
|
|
80
77
|
}};
|
|
81
78
|
return std::tuple{callback, data};
|
|
82
|
-
} else if constexpr (sizeof(bound_type) == sizeof(void*)) {
|
|
83
|
-
// Trivial function of pointer type. Data() is the function data.
|
|
84
|
-
auto data = [ & ] -> auto {
|
|
85
|
-
#if V8_HAS_TAGGED_EXTERNAL
|
|
86
|
-
return v8::External::New(lock.isolate(), std::bit_cast<void*>(bound), 0);
|
|
87
|
-
#else
|
|
88
|
-
return v8::External::New(lock.isolate(), std::bit_cast<void*>(bound));
|
|
89
|
-
#endif
|
|
90
|
-
}();
|
|
91
|
-
const auto callback = v8::FunctionCallback{[](const v8::FunctionCallbackInfo<v8::Value>& info) -> void {
|
|
92
|
-
auto invoke = [ & ] -> auto {
|
|
93
|
-
#if V8_HAS_TAGGED_EXTERNAL
|
|
94
|
-
// TODO: Use this feature
|
|
95
|
-
return std::bit_cast<bound_type>(info.Data().As<v8::External>()->Value(0));
|
|
96
|
-
#else
|
|
97
|
-
return std::bit_cast<bound_type>(info.Data().As<v8::External>()->Value());
|
|
98
|
-
#endif
|
|
99
|
-
}();
|
|
100
|
-
invoke(info);
|
|
101
|
-
}};
|
|
102
|
-
return std::tuple{callback, data};
|
|
103
79
|
} else {
|
|
104
|
-
|
|
80
|
+
if constexpr (sizeof(bound_type) <= sizeof(void*)) {
|
|
81
|
+
// Trivial function which can fit inside a pointer. There is a v8 DCHECK on nullptr, so in
|
|
82
|
+
// that case it falls through to the latin1 representation.
|
|
83
|
+
if (auto* pointer = util::bit_padding_cast<void*>(bound); pointer != nullptr) {
|
|
84
|
+
using external_type = untagged_external<void>;
|
|
85
|
+
auto data = v8::Local<v8::Value>{external_type::make(lock, pointer)};
|
|
86
|
+
constexpr auto callback = v8::FunctionCallback{[](const v8::FunctionCallbackInfo<v8::Value>& info) -> void {
|
|
87
|
+
auto invoke = util::bit_truncation_cast<bound_type>(info.Data().As<external_type>()->value());
|
|
88
|
+
invoke(info);
|
|
89
|
+
}};
|
|
90
|
+
return std::tuple{callback, data};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Trivial data-only function type. `Data()` is a latin1 string containing the function data.
|
|
105
94
|
auto data =
|
|
106
95
|
unmaybe(v8::String::NewFromOneByte(lock.isolate(), reinterpret_cast<const std::uint8_t*>(&bound), v8::NewStringType::kNormal, sizeof(bound)));
|
|
107
|
-
|
|
96
|
+
constexpr auto callback = v8::FunctionCallback{[](const v8::FunctionCallbackInfo<v8::Value>& info) -> void {
|
|
108
97
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
|
109
98
|
std::array<std::byte, sizeof(bound_type)> data;
|
|
110
99
|
auto* isolate = info.GetIsolate();
|
|
@@ -112,13 +101,14 @@ auto make_callback_storage(const auto& lock, auto function) {
|
|
|
112
101
|
auto invoke = std::bit_cast<bound_type>(data);
|
|
113
102
|
invoke(info);
|
|
114
103
|
}};
|
|
115
|
-
return std::tuple{callback, data};
|
|
104
|
+
return std::tuple{callback, v8::Local<v8::Value>{data}};
|
|
116
105
|
}
|
|
117
106
|
} else {
|
|
118
|
-
// Otherwise
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
107
|
+
// Otherwise `Data()` is a collected external.
|
|
108
|
+
using external_type = untagged_external<bound_type>;
|
|
109
|
+
auto data = external_type::make_collected(lock, std::move(bound));
|
|
110
|
+
constexpr auto callback = v8::FunctionCallback{[](const v8::FunctionCallbackInfo<v8::Value>& info) -> void {
|
|
111
|
+
auto& invoke = *info.Data().As<external_type>()->value();
|
|
122
112
|
invoke(info);
|
|
123
113
|
}};
|
|
124
114
|
return std::tuple{callback, data};
|
package/transfer/accept.cc
CHANGED
|
@@ -116,14 +116,24 @@ auto accept_v8_value::operator()(function_prototype_tag /*tag*/, visit_holder /*
|
|
|
116
116
|
auto accept_v8_value::operator()(array_buffer_tag /*tag*/, visit_holder /*visit*/, js::array_buffer subject) const
|
|
117
117
|
-> js::referenceable_value<v8::Local<v8::ArrayBuffer>> {
|
|
118
118
|
auto byte_length = subject.byte_length();
|
|
119
|
-
auto backing_store =
|
|
120
|
-
|
|
121
|
-
byte_length
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
119
|
+
auto backing_store = [ & ] -> auto {
|
|
120
|
+
// nb: v8 does not call the deleter when `byte_length` is zero.
|
|
121
|
+
if (byte_length == 0) {
|
|
122
|
+
return v8::ArrayBuffer::NewBackingStore(nullptr, 0, nullptr, nullptr);
|
|
123
|
+
} else {
|
|
124
|
+
auto holder = std::make_unique<js::array_buffer>(std::move(subject));
|
|
125
|
+
auto backing_store = v8::ArrayBuffer::NewBackingStore(
|
|
126
|
+
holder->data(),
|
|
127
|
+
byte_length,
|
|
128
|
+
[](void* /*data*/, std::size_t /*length*/, void* param) -> void {
|
|
129
|
+
delete static_cast<js::array_buffer*>(param);
|
|
130
|
+
},
|
|
131
|
+
holder.get()
|
|
132
|
+
);
|
|
133
|
+
std::ignore = holder.release();
|
|
134
|
+
return backing_store;
|
|
135
|
+
}
|
|
136
|
+
}();
|
|
127
137
|
auto value = v8::ArrayBuffer::New(isolate(), std::move(backing_store));
|
|
128
138
|
return js::referenceable_value{value};
|
|
129
139
|
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
export module v8_js:transfer_list;
|
|
2
|
+
import :array;
|
|
3
|
+
import :handle.value;
|
|
4
|
+
import :object;
|
|
5
|
+
import :lock;
|
|
6
|
+
import :unmaybe;
|
|
7
|
+
import :value.tag;
|
|
8
|
+
import auto_js;
|
|
9
|
+
import std;
|
|
10
|
+
import util;
|
|
11
|
+
import v8;
|
|
12
|
+
|
|
13
|
+
namespace js::iv8 {
|
|
14
|
+
|
|
15
|
+
// Transfer delegate for `ArrayBuffer`
|
|
16
|
+
export class array_buffer_transfer {
|
|
17
|
+
public:
|
|
18
|
+
array_buffer_transfer(context_lock_witness /*lock*/, std::vector<v8::Local<v8::Value>>& entries) :
|
|
19
|
+
buffers_{[ & ] -> auto {
|
|
20
|
+
constexpr auto maybe_claim_in = [](v8::Local<v8::Value> value) -> std::optional<v8::Local<v8::ArrayBuffer>> {
|
|
21
|
+
if (value->IsArrayBuffer()) {
|
|
22
|
+
auto buffer = value.As<v8::ArrayBuffer>();
|
|
23
|
+
if (!buffer->IsDetachable()) {
|
|
24
|
+
throw js::type_error{u"ArrayBuffer is not detachable"};
|
|
25
|
+
}
|
|
26
|
+
return buffer;
|
|
27
|
+
} else {
|
|
28
|
+
return std::nullopt;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
return js::extract_transferees(util::cw<u"ArrayBuffer">, maybe_claim_in, std::equal_to{}, entries);
|
|
32
|
+
}()} {}
|
|
33
|
+
|
|
34
|
+
template <class Visit, class Accept>
|
|
35
|
+
auto operator()(v8::Local<v8::ArrayBuffer> subject, Visit& visit, const Accept& accept) {
|
|
36
|
+
return maybe_claim_out(subject, visit, accept);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
template <class Visit, class Accept>
|
|
40
|
+
auto operator()(v8::Local<iv8::DataBlock> subject, Visit& visit, const Accept& accept) {
|
|
41
|
+
return maybe_claim_out(subject, visit, accept);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
auto finalize() -> void {
|
|
45
|
+
for (auto entry : std::exchange(buffers_, {})) {
|
|
46
|
+
unmaybe(entry->Detach(v8::Local<v8::Value>{}));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private:
|
|
51
|
+
template <class Visit, class Accept>
|
|
52
|
+
auto maybe_claim_out(auto subject, Visit& visit, const Accept& accept) {
|
|
53
|
+
auto claim_out = [ & ](v8::Local<v8::ArrayBuffer> handle) -> accept_target_t<Accept> {
|
|
54
|
+
auto buffer = [ & ] -> js::array_buffer {
|
|
55
|
+
if (handle->ByteLength() == 0) {
|
|
56
|
+
return js::array_buffer{std::span<std::byte>{}};
|
|
57
|
+
} else {
|
|
58
|
+
// Steal the backing store, avoiding a copy of the contents
|
|
59
|
+
auto backing_store = std::make_unique<std::shared_ptr<v8::BackingStore>>(handle->GetBackingStore());
|
|
60
|
+
auto data = std::span{static_cast<std::byte*>((*backing_store)->Data()), (*backing_store)->ByteLength()};
|
|
61
|
+
return js::array_buffer{
|
|
62
|
+
data,
|
|
63
|
+
js::array_buffer::deleter{
|
|
64
|
+
[](std::shared_ptr<v8::BackingStore>* backing_store, std::byte* /*data*/) -> void {
|
|
65
|
+
delete backing_store;
|
|
66
|
+
},
|
|
67
|
+
backing_store.release()
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}();
|
|
72
|
+
unmaybe(handle->Detach(v8::Local<v8::Value>{}));
|
|
73
|
+
return accept(array_buffer_tag{}, visit, js::transferred_value{v8::Local<v8::Data>{handle}, buffer});
|
|
74
|
+
};
|
|
75
|
+
return js::claim_transferee(buffers_, subject, std::equal_to{}, claim_out);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
std::vector<v8::Local<v8::ArrayBuffer>> buffers_;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// v8 'transferList' delegate
|
|
82
|
+
export template <class... Delegates>
|
|
83
|
+
class transfer_list {
|
|
84
|
+
public:
|
|
85
|
+
template <class Type>
|
|
86
|
+
transfer_list(context_lock_witness lock, std::optional<Type> list) :
|
|
87
|
+
transfer_list{lock, list ? entries_of(lock, list->template As<v8::Array>()) : std::vector<v8::Local<v8::Value>>{}} {}
|
|
88
|
+
|
|
89
|
+
transfer_list(context_lock_witness lock, value_of<list_tag> list) :
|
|
90
|
+
transfer_list{lock, entries_of(lock, list.As<v8::Array>())} {}
|
|
91
|
+
|
|
92
|
+
// Constructs a list in place and invokes the given operation with it, finalizing afterwards
|
|
93
|
+
static auto with(context_lock_witness lock, auto list, auto operation) {
|
|
94
|
+
auto self = transfer_list{lock, std::move(list)};
|
|
95
|
+
auto result = operation(self);
|
|
96
|
+
self.finalize();
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Offers the subject to each delegate in order, claiming with the first which engages
|
|
101
|
+
template <class Visit, class Accept>
|
|
102
|
+
auto operator()(auto subject, Visit& visit, const Accept& accept) -> std::optional<accept_target_t<Accept>> {
|
|
103
|
+
return util::template_traverse(
|
|
104
|
+
util::sequence_cw<sizeof...(Delegates)>,
|
|
105
|
+
util::overloaded{
|
|
106
|
+
[ & ](auto ii, auto next) -> std::optional<accept_target_t<Accept>> {
|
|
107
|
+
auto& delegate = std::get<ii>(delegates_);
|
|
108
|
+
if constexpr (std::invocable<decltype(delegate), decltype(subject), Visit&, const Accept&>) {
|
|
109
|
+
if (auto claim = delegate(subject, visit, accept)) {
|
|
110
|
+
return std::optional{(*std::move(claim))()};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return next();
|
|
114
|
+
},
|
|
115
|
+
[] -> std::optional<accept_target_t<Accept>> { return std::nullopt; },
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
auto finalize() -> void {
|
|
121
|
+
constexpr auto [... ii ] = util::sequence<sizeof...(Delegates)>;
|
|
122
|
+
(..., std::get<ii>(delegates_).finalize());
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private:
|
|
126
|
+
// Each delegate claims its entries from the vector. Anything left over is unknown.
|
|
127
|
+
transfer_list(context_lock_witness lock, std::vector<v8::Local<v8::Value>> entries) :
|
|
128
|
+
delegates_{Delegates{lock, entries}...} {
|
|
129
|
+
if (!entries.empty()) {
|
|
130
|
+
throw js::type_error{u"Transfer list contains unknown value"};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
static auto entries_of(context_lock_witness lock, v8::Local<v8::Array> list) -> std::vector<v8::Local<v8::Value>> {
|
|
135
|
+
return std::vector{std::from_range, value_for_array{lock, list}};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
std::tuple<Delegates...> delegates_;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
} // namespace js::iv8
|
package/transfer/visit.cc
CHANGED
|
@@ -77,11 +77,11 @@ struct visit_cached_immediate : Visit {
|
|
|
77
77
|
using Visit::Visit;
|
|
78
78
|
|
|
79
79
|
template <class Accept>
|
|
80
|
-
auto operator()(auto subject, const Accept& accept) -> accept_target_t<Accept>
|
|
80
|
+
auto operator()(this auto& self, auto subject, const Accept& accept) -> accept_target_t<Accept>
|
|
81
81
|
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124954
|
|
82
|
-
requires requires {
|
|
83
|
-
return lookup_or_visit(subject, [ & ] -> accept_target_t<Accept> {
|
|
84
|
-
return immediate(subject, accept);
|
|
82
|
+
requires requires { self.immediate(subject, accept); } {
|
|
83
|
+
return self.lookup_or_visit(subject, [ & ] -> accept_target_t<Accept> {
|
|
84
|
+
return self.immediate(subject, accept);
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
};
|
|
@@ -94,10 +94,10 @@ struct visit_uncached_immediate : Visit {
|
|
|
94
94
|
using Visit::Visit;
|
|
95
95
|
|
|
96
96
|
template <class Accept>
|
|
97
|
-
auto operator()(auto subject, const Accept& accept) -> accept_target_t<Accept>
|
|
97
|
+
auto operator()(this auto& self, auto subject, const Accept& accept) -> accept_target_t<Accept>
|
|
98
98
|
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124954
|
|
99
|
-
requires requires {
|
|
100
|
-
return immediate(subject, accept);
|
|
99
|
+
requires requires { self.immediate(subject, accept); } {
|
|
100
|
+
return self.immediate(subject, accept);
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
103
|
|
|
@@ -121,18 +121,18 @@ struct visit_flat_value : reference_map_t<Reference, visit_reference_map_type> {
|
|
|
121
121
|
|
|
122
122
|
// numbers
|
|
123
123
|
template <class Accept>
|
|
124
|
-
auto operator()(v8::Local<v8::Number> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
124
|
+
auto operator()(this auto& self, v8::Local<v8::Number> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
125
125
|
if (subject->IsInt32()) {
|
|
126
|
-
return immediate(subject.As<v8::Int32>(), accept);
|
|
126
|
+
return self.immediate(subject.As<v8::Int32>(), accept);
|
|
127
127
|
} else {
|
|
128
|
-
return immediate(subject.As<iv8::Double>(), accept);
|
|
128
|
+
return self.immediate(subject.As<iv8::Double>(), accept);
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
// boolean
|
|
133
133
|
template <class Accept>
|
|
134
|
-
auto operator()(v8::Local<v8::Boolean> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
135
|
-
return immediate(subject, accept);
|
|
134
|
+
auto operator()(this auto& self, v8::Local<v8::Boolean> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
135
|
+
return self.immediate(subject, accept);
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
// extras
|
|
@@ -142,19 +142,19 @@ struct visit_flat_value : reference_map_t<Reference, visit_reference_map_type> {
|
|
|
142
142
|
protected:
|
|
143
143
|
// primitives
|
|
144
144
|
template <class Accept>
|
|
145
|
-
auto immediate(v8::Local<v8::Primitive> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
145
|
+
auto immediate(this auto& self, v8::Local<v8::Primitive> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
146
146
|
if (subject->IsUndefined()) {
|
|
147
|
-
return accept(undefined_tag{},
|
|
147
|
+
return accept(undefined_tag{}, self, subject);
|
|
148
148
|
} else if (subject->IsNull()) {
|
|
149
|
-
return accept(null_tag{},
|
|
149
|
+
return accept(null_tag{}, self, subject);
|
|
150
150
|
} else if (subject->IsNumber()) {
|
|
151
|
-
return (
|
|
151
|
+
return self(subject.As<v8::Number>(), accept);
|
|
152
152
|
} else if (subject->IsName()) {
|
|
153
|
-
return immediate(subject.As<v8::Name>(), accept);
|
|
153
|
+
return self.immediate(subject.As<v8::Name>(), accept);
|
|
154
154
|
} else if (subject->IsBoolean()) {
|
|
155
|
-
return (
|
|
155
|
+
return self(subject.As<v8::Boolean>(), accept);
|
|
156
156
|
} else if (subject->IsBigInt()) {
|
|
157
|
-
return immediate(subject.As<v8::BigInt>(), accept);
|
|
157
|
+
return self.immediate(subject.As<v8::BigInt>(), accept);
|
|
158
158
|
} else {
|
|
159
159
|
std::unreachable();
|
|
160
160
|
}
|
|
@@ -162,86 +162,86 @@ struct visit_flat_value : reference_map_t<Reference, visit_reference_map_type> {
|
|
|
162
162
|
|
|
163
163
|
template <class Accept, class Type>
|
|
164
164
|
requires std::is_convertible_v<Type, v8::Primitive>
|
|
165
|
-
auto immediate(v8::Local<Type> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
166
|
-
return accept_tagged(subject, accept);
|
|
165
|
+
auto immediate(this auto& self, v8::Local<Type> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
166
|
+
return self.accept_tagged(subject, accept);
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
// names
|
|
170
170
|
template <class Accept>
|
|
171
|
-
auto immediate(v8::Local<v8::Name> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
171
|
+
auto immediate(this auto& self, v8::Local<v8::Name> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
172
172
|
if (subject->IsString()) {
|
|
173
|
-
return immediate(subject.As<v8::String>(), accept);
|
|
173
|
+
return self.immediate(subject.As<v8::String>(), accept);
|
|
174
174
|
} else {
|
|
175
|
-
return immediate(subject.As<v8::Symbol>(), accept);
|
|
175
|
+
return self.immediate(subject.As<v8::Symbol>(), accept);
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
template <class Accept>
|
|
180
|
-
auto immediate(v8::Local<v8::String> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
180
|
+
auto immediate(this auto& self, v8::Local<v8::String> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
181
181
|
if (subject->IsOneByte()) {
|
|
182
|
-
return immediate(subject.As<iv8::StringOneByte>(), accept);
|
|
182
|
+
return self.immediate(subject.As<iv8::StringOneByte>(), accept);
|
|
183
183
|
} else {
|
|
184
|
-
return immediate(subject.As<iv8::StringTwoByte>(), accept);
|
|
184
|
+
return self.immediate(subject.As<iv8::StringTwoByte>(), accept);
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
// bigint
|
|
189
189
|
template <class Accept>
|
|
190
|
-
auto immediate(v8::Local<v8::BigInt> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
190
|
+
auto immediate(this auto& self, v8::Local<v8::BigInt> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
191
191
|
bool lossless{};
|
|
192
192
|
auto i64 = subject->Int64Value(&lossless);
|
|
193
193
|
if (lossless) {
|
|
194
|
-
return accept(bigint_tag_of<std::int64_t>{},
|
|
194
|
+
return accept(bigint_tag_of<std::int64_t>{}, self, value_of{self.witness(), subject.As<iv8::BigInt64>(), i64});
|
|
195
195
|
} else {
|
|
196
|
-
return immediate(subject.As<iv8::BigIntWords>(), accept);
|
|
196
|
+
return self.immediate(subject.As<iv8::BigIntWords>(), accept);
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
// date
|
|
201
201
|
template <class Accept>
|
|
202
|
-
auto immediate(v8::Local<v8::Date> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
203
|
-
return accept_tagged(subject, accept);
|
|
202
|
+
auto immediate(this auto& self, v8::Local<v8::Date> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
203
|
+
return self.accept_tagged(subject, accept);
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
// data blocks
|
|
207
207
|
template <class Accept>
|
|
208
|
-
auto immediate(v8::Local<iv8::DataBlock> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
208
|
+
auto immediate(this auto& self, v8::Local<iv8::DataBlock> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
209
209
|
if (subject->IsSharedArrayBuffer()) {
|
|
210
|
-
return immediate(subject.As<v8::SharedArrayBuffer>(), accept);
|
|
210
|
+
return self.immediate(subject.As<v8::SharedArrayBuffer>(), accept);
|
|
211
211
|
} else {
|
|
212
|
-
return immediate(subject.As<v8::ArrayBuffer>(), accept);
|
|
212
|
+
return self.immediate(subject.As<v8::ArrayBuffer>(), accept);
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
template <class Accept, class Type>
|
|
217
217
|
requires std::is_convertible_v<iv8::v8_to_tag<Type>, data_block_tag>
|
|
218
|
-
auto immediate(v8::Local<Type> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
219
|
-
return accept_tagged(subject, accept);
|
|
218
|
+
auto immediate(this auto& self, v8::Local<Type> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
219
|
+
return self.accept_tagged(subject, accept);
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
// external
|
|
223
223
|
template <class Accept>
|
|
224
|
-
auto immediate(v8::Local<v8::External> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
225
|
-
return accept_tagged(subject, accept);
|
|
224
|
+
auto immediate(this auto& self, v8::Local<v8::External> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
225
|
+
return self.accept_tagged(subject, accept);
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
// promise (maybe could be forwarded)
|
|
229
229
|
template <class Accept>
|
|
230
|
-
auto immediate(v8::Local<v8::Promise> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
231
|
-
return accept(promise_tag{},
|
|
230
|
+
auto immediate(this auto& self, v8::Local<v8::Promise> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
231
|
+
return accept(promise_tag{}, self, subject);
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
// function (can be forwarded)
|
|
235
235
|
template <class Type, class Accept>
|
|
236
236
|
requires std::is_convertible_v<Type, v8::Function>
|
|
237
|
-
auto immediate(v8::Local<Type> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
238
|
-
return accept(function_tag{},
|
|
237
|
+
auto immediate(this auto& self, v8::Local<Type> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
238
|
+
return accept(function_tag{}, self, subject);
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
// Convenience function which wraps in `iv8::value_of` and invokes `accept`.
|
|
242
242
|
template <class Type, class Accept>
|
|
243
|
-
[[nodiscard]] auto accept_tagged(v8::Local<Type> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
244
|
-
return accept(iv8::v8_to_tag<Type>{},
|
|
243
|
+
[[nodiscard]] auto accept_tagged(this auto& self, v8::Local<Type> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
244
|
+
return accept(iv8::v8_to_tag<Type>{}, self, value_of{self.witness(), subject});
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
private:
|
|
@@ -273,40 +273,40 @@ struct visit_value : visit_flat_value<Target> {
|
|
|
273
273
|
visit_value{transfer, context_lock_witness{util::slice(lock)}} {}
|
|
274
274
|
|
|
275
275
|
template <class Accept>
|
|
276
|
-
auto operator()(v8::Local<v8::Value> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
276
|
+
auto operator()(this auto& self, v8::Local<v8::Value> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
277
277
|
|
|
278
278
|
// Check the reference map, and check type
|
|
279
|
-
return lookup_or_visit(subject, [ & ] -> accept_target_t<Accept> {
|
|
279
|
+
return self.lookup_or_visit(subject, [ & ] -> accept_target_t<Accept> {
|
|
280
280
|
return util::template_traverse(
|
|
281
281
|
accept_tags_of_v<Accept>,
|
|
282
282
|
util::overloaded{
|
|
283
283
|
// Fast paths
|
|
284
284
|
[ & ](undefined_tag /*tag*/, auto next) -> accept_target_t<Accept> {
|
|
285
|
-
return subject->IsUndefined() ? accept(undefined_tag{},
|
|
285
|
+
return subject->IsUndefined() ? accept(undefined_tag{}, self, subject.As<iv8::Undefined>()) : next();
|
|
286
286
|
},
|
|
287
287
|
[ & ](null_tag /*tag*/, auto next) -> accept_target_t<Accept> {
|
|
288
|
-
return subject->IsNull() ? accept(null_tag{},
|
|
288
|
+
return subject->IsNull() ? accept(null_tag{}, self, subject.As<iv8::Null>()) : next();
|
|
289
289
|
},
|
|
290
290
|
[ & ](boolean_tag /*tag*/, auto next) -> accept_target_t<Accept> {
|
|
291
|
-
return subject->IsBoolean() ? (
|
|
291
|
+
return subject->IsBoolean() ? self(subject.As<v8::Boolean>(), accept) : next();
|
|
292
292
|
},
|
|
293
293
|
[ & ](number_tag /*tag*/, auto next) -> accept_target_t<Accept> {
|
|
294
|
-
return subject->IsNumber() ? (
|
|
294
|
+
return subject->IsNumber() ? self(subject.As<v8::Number>(), accept) : next();
|
|
295
295
|
},
|
|
296
296
|
[ & ](string_tag /*tag*/, auto next) -> accept_target_t<Accept> {
|
|
297
|
-
return subject->IsString() ? immediate(subject.As<v8::String>(), accept) : next();
|
|
297
|
+
return subject->IsString() ? self.immediate(subject.As<v8::String>(), accept) : next();
|
|
298
298
|
},
|
|
299
299
|
[ & ](bigint_tag /*tag*/, auto next) -> accept_target_t<Accept> {
|
|
300
|
-
return subject->IsBigInt() ? immediate(subject.As<v8::BigInt>(), accept) : next();
|
|
300
|
+
return subject->IsBigInt() ? self.immediate(subject.As<v8::BigInt>(), accept) : next();
|
|
301
301
|
},
|
|
302
302
|
[ & ](date_tag /*tag*/, auto next) -> accept_target_t<Accept> {
|
|
303
|
-
return subject->IsDate() ? immediate(subject.As<v8::Date>(), accept) : next();
|
|
303
|
+
return subject->IsDate() ? self.immediate(subject.As<v8::Date>(), accept) : next();
|
|
304
304
|
},
|
|
305
305
|
[ & ](list_tag /*tag*/, auto next) -> accept_target_t<Accept> {
|
|
306
|
-
return subject->IsArray() ? immediate(subject.As<v8::Array>(), accept) : next();
|
|
306
|
+
return subject->IsArray() ? self.immediate(subject.As<v8::Array>(), accept) : next();
|
|
307
307
|
},
|
|
308
308
|
[ & ](function_tag /*tag*/, auto next) -> accept_target_t<Accept> {
|
|
309
|
-
return subject->IsFunction() ? immediate(subject.As<iv8::Function>(), accept) : next();
|
|
309
|
+
return subject->IsFunction() ? self.immediate(subject.As<iv8::Function>(), accept) : next();
|
|
310
310
|
},
|
|
311
311
|
|
|
312
312
|
// Unknown tag
|
|
@@ -315,9 +315,9 @@ struct visit_value : visit_flat_value<Target> {
|
|
|
315
315
|
// Slow path
|
|
316
316
|
[ & ] -> accept_target_t<Accept> {
|
|
317
317
|
if (subject->IsObject()) {
|
|
318
|
-
return immediate(subject.As<v8::Object>(), accept);
|
|
318
|
+
return self.immediate(subject.As<v8::Object>(), accept);
|
|
319
319
|
} else {
|
|
320
|
-
return immediate(subject.As<v8::Primitive>(), accept);
|
|
320
|
+
return self.immediate(subject.As<v8::Primitive>(), accept);
|
|
321
321
|
}
|
|
322
322
|
},
|
|
323
323
|
}
|
|
@@ -326,19 +326,19 @@ struct visit_value : visit_flat_value<Target> {
|
|
|
326
326
|
}
|
|
327
327
|
|
|
328
328
|
template <class Accept>
|
|
329
|
-
auto operator()(v8::Local<iv8::DataBlock> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
330
|
-
return lookup_or_visit(subject, [ & ] -> accept_target_t<Accept> {
|
|
329
|
+
auto operator()(this auto& self, v8::Local<iv8::DataBlock> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
330
|
+
return self.lookup_or_visit(subject, [ & ] -> accept_target_t<Accept> {
|
|
331
331
|
return util::template_traverse(
|
|
332
332
|
accept_tags_of_v<Accept>,
|
|
333
333
|
util::overloaded{
|
|
334
334
|
// Fast paths
|
|
335
|
-
[ & ](data_block_tag /*tag*/, auto /*next*/) -> accept_target_t<Accept> { return accept_tagged(subject, accept); },
|
|
335
|
+
[ & ](data_block_tag /*tag*/, auto /*next*/) -> accept_target_t<Accept> { return self.accept_tagged(subject, accept); },
|
|
336
336
|
[ & ](array_buffer_tag /*tag*/, auto next) -> accept_target_t<Accept> { return next(); },
|
|
337
337
|
[ & ](shared_array_buffer_tag /*tag*/, auto next) -> accept_target_t<Accept> { return next(); },
|
|
338
338
|
|
|
339
339
|
// Slow path
|
|
340
340
|
[ & ] -> accept_target_t<Accept> {
|
|
341
|
-
return immediate(subject, accept);
|
|
341
|
+
return self.immediate(subject, accept);
|
|
342
342
|
}
|
|
343
343
|
}
|
|
344
344
|
);
|
|
@@ -352,72 +352,72 @@ struct visit_value : visit_flat_value<Target> {
|
|
|
352
352
|
|
|
353
353
|
protected:
|
|
354
354
|
// object
|
|
355
|
-
template <class Accept>
|
|
356
|
-
auto immediate(v8::Local<v8::Object> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
355
|
+
template <class Visit, class Accept>
|
|
356
|
+
auto immediate(this Visit& self, v8::Local<v8::Object> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
357
357
|
if (subject->IsArray()) {
|
|
358
|
-
return immediate(subject.As<v8::Array>(), accept);
|
|
358
|
+
return self.immediate(subject.As<v8::Array>(), accept);
|
|
359
359
|
} else if (subject->IsExternal()) {
|
|
360
|
-
return immediate(subject.As<v8::External>(), accept);
|
|
360
|
+
return self.immediate(subject.As<v8::External>(), accept);
|
|
361
361
|
} else if (subject->IsDate()) {
|
|
362
|
-
return immediate(subject.As<v8::Date>(), accept);
|
|
362
|
+
return self.immediate(subject.As<v8::Date>(), accept);
|
|
363
363
|
} else if (subject->IsArrayBuffer()) {
|
|
364
|
-
return immediate(subject.As<v8::ArrayBuffer>(), accept);
|
|
364
|
+
return self.immediate(subject.As<v8::ArrayBuffer>(), accept);
|
|
365
365
|
} else if (subject->IsSharedArrayBuffer()) {
|
|
366
|
-
return immediate(subject.As<v8::SharedArrayBuffer>(), accept);
|
|
366
|
+
return self.immediate(subject.As<v8::SharedArrayBuffer>(), accept);
|
|
367
367
|
} else if (subject->IsArrayBufferView()) {
|
|
368
|
-
return immediate(subject.As<v8::ArrayBufferView>(), accept);
|
|
368
|
+
return self.immediate(subject.As<v8::ArrayBufferView>(), accept);
|
|
369
369
|
} else if (subject->IsPromise()) {
|
|
370
|
-
return immediate(subject.As<v8::Promise>(), accept);
|
|
370
|
+
return self.immediate(subject.As<v8::Promise>(), accept);
|
|
371
371
|
} else if (subject->IsFunction()) {
|
|
372
|
-
return immediate(subject.As<iv8::Function>(), accept);
|
|
372
|
+
return self.immediate(subject.As<iv8::Function>(), accept);
|
|
373
373
|
} else {
|
|
374
|
-
auto visit_entry = visit_entry_pair<visit_property_name<
|
|
375
|
-
return accept(dictionary_tag{}, visit_entry, value_of{witness(), subject.As<v8::Object>()});
|
|
374
|
+
auto visit_entry = visit_entry_pair<visit_property_name<Visit>, Visit&>{self};
|
|
375
|
+
return accept(dictionary_tag{}, visit_entry, value_of{self.witness(), subject.As<v8::Object>()});
|
|
376
376
|
}
|
|
377
377
|
}
|
|
378
378
|
|
|
379
379
|
// array
|
|
380
|
-
template <class Accept>
|
|
381
|
-
auto immediate(v8::Local<v8::Array> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
382
|
-
auto visit_entry = visit_entry_pair<visit_property_name<
|
|
383
|
-
return accept(list_tag{}, visit_entry, value_of{witness(), subject.As<v8::Array>()});
|
|
380
|
+
template <class Visit, class Accept>
|
|
381
|
+
auto immediate(this Visit& self, v8::Local<v8::Array> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
382
|
+
auto visit_entry = visit_entry_pair<visit_property_name<Visit>, Visit&>{self};
|
|
383
|
+
return accept(list_tag{}, visit_entry, value_of{self.witness(), subject.As<v8::Array>()});
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
// function template
|
|
387
387
|
template <class Accept>
|
|
388
|
-
auto immediate(v8::Local<v8::FunctionTemplate> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
389
|
-
return accept(function_tag{},
|
|
388
|
+
auto immediate(this auto& self, v8::Local<v8::FunctionTemplate> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
389
|
+
return accept(function_tag{}, self, unmaybe(subject->GetFunction(self.witness().context())));
|
|
390
390
|
}
|
|
391
391
|
|
|
392
392
|
// array buffer views (typed arrays, data view)
|
|
393
393
|
template <class Accept>
|
|
394
|
-
auto immediate(v8::Local<v8::ArrayBufferView> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
394
|
+
auto immediate(this auto& self, v8::Local<v8::ArrayBufferView> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
395
395
|
if (subject->IsUint8Array()) {
|
|
396
|
-
return immediate(subject.As<v8::Uint8Array>(), accept);
|
|
396
|
+
return self.immediate(subject.As<v8::Uint8Array>(), accept);
|
|
397
397
|
} else if (subject->IsDataView()) {
|
|
398
|
-
return immediate(subject.As<v8::DataView>(), accept);
|
|
398
|
+
return self.immediate(subject.As<v8::DataView>(), accept);
|
|
399
399
|
} else if (subject->IsUint8ClampedArray()) {
|
|
400
|
-
return immediate(subject.As<v8::Uint8ClampedArray>(), accept);
|
|
400
|
+
return self.immediate(subject.As<v8::Uint8ClampedArray>(), accept);
|
|
401
401
|
} else if (subject->IsInt8Array()) {
|
|
402
|
-
return immediate(subject.As<v8::Int8Array>(), accept);
|
|
402
|
+
return self.immediate(subject.As<v8::Int8Array>(), accept);
|
|
403
403
|
} else if (subject->IsUint16Array()) {
|
|
404
|
-
return immediate(subject.As<v8::Uint16Array>(), accept);
|
|
404
|
+
return self.immediate(subject.As<v8::Uint16Array>(), accept);
|
|
405
405
|
} else if (subject->IsInt16Array()) {
|
|
406
|
-
return immediate(subject.As<v8::Int16Array>(), accept);
|
|
406
|
+
return self.immediate(subject.As<v8::Int16Array>(), accept);
|
|
407
407
|
} else if (subject->IsUint32Array()) {
|
|
408
|
-
return immediate(subject.As<v8::Uint32Array>(), accept);
|
|
408
|
+
return self.immediate(subject.As<v8::Uint32Array>(), accept);
|
|
409
409
|
} else if (subject->IsInt32Array()) {
|
|
410
|
-
return immediate(subject.As<v8::Int32Array>(), accept);
|
|
410
|
+
return self.immediate(subject.As<v8::Int32Array>(), accept);
|
|
411
411
|
} else if (subject->IsFloat16Array()) {
|
|
412
|
-
return immediate(subject.As<v8::Float16Array>(), accept);
|
|
412
|
+
return self.immediate(subject.As<v8::Float16Array>(), accept);
|
|
413
413
|
} else if (subject->IsFloat32Array()) {
|
|
414
|
-
return immediate(subject.As<v8::Float32Array>(), accept);
|
|
414
|
+
return self.immediate(subject.As<v8::Float32Array>(), accept);
|
|
415
415
|
} else if (subject->IsFloat64Array()) {
|
|
416
|
-
return immediate(subject.As<v8::Float64Array>(), accept);
|
|
416
|
+
return self.immediate(subject.As<v8::Float64Array>(), accept);
|
|
417
417
|
} else if (subject->IsBigInt64Array()) {
|
|
418
|
-
return immediate(subject.As<v8::BigInt64Array>(), accept);
|
|
418
|
+
return self.immediate(subject.As<v8::BigInt64Array>(), accept);
|
|
419
419
|
} else if (subject->IsBigUint64Array()) {
|
|
420
|
-
return immediate(subject.As<v8::BigUint64Array>(), accept);
|
|
420
|
+
return self.immediate(subject.As<v8::BigUint64Array>(), accept);
|
|
421
421
|
} else {
|
|
422
422
|
throw js::type_error{u"Received exotic v8 'ArrayBufferView'"};
|
|
423
423
|
}
|
|
@@ -425,14 +425,56 @@ struct visit_value : visit_flat_value<Target> {
|
|
|
425
425
|
|
|
426
426
|
template <class Accept, class Type>
|
|
427
427
|
requires std::is_convertible_v<Type, v8::ArrayBufferView>
|
|
428
|
-
auto immediate(v8::Local<Type> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
429
|
-
return accept(v8_to_tag<Type>{},
|
|
428
|
+
auto immediate(this auto& self, v8::Local<Type> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
429
|
+
return accept(v8_to_tag<Type>{}, self, value_of{self.witness(), subject});
|
|
430
430
|
}
|
|
431
431
|
|
|
432
432
|
private:
|
|
433
433
|
v8::Local<v8::Context> context_;
|
|
434
434
|
};
|
|
435
435
|
|
|
436
|
+
// Visitor with transfer delegate. The delegate is offered transferable subjects, claiming the ones
|
|
437
|
+
// listed in its `transferList` before the underlying visitor gets a chance to copy them.
|
|
438
|
+
template <class Target, class Delegate>
|
|
439
|
+
struct visit_value_delegate : visit_value<Target> {
|
|
440
|
+
private:
|
|
441
|
+
using visit_type = visit_value<Target>;
|
|
442
|
+
|
|
443
|
+
public:
|
|
444
|
+
visit_value_delegate(auto* transfer, context_lock_witness lock, Delegate& delegate) :
|
|
445
|
+
visit_type{transfer, lock},
|
|
446
|
+
delegate_{delegate} {}
|
|
447
|
+
visit_value_delegate(auto* transfer, const auto& lock, Delegate& delegate) :
|
|
448
|
+
visit_value_delegate{transfer, context_lock_witness{util::slice(lock)}, delegate} {}
|
|
449
|
+
|
|
450
|
+
using visit_type::operator();
|
|
451
|
+
using visit_type::immediate;
|
|
452
|
+
|
|
453
|
+
template <class Accept>
|
|
454
|
+
auto immediate(this auto& self, v8::Local<v8::ArrayBuffer> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
455
|
+
return self.claim_or_immediate(subject, accept);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
template <class Accept>
|
|
459
|
+
auto immediate(this auto& self, v8::Local<iv8::DataBlock> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
460
|
+
return self.claim_or_immediate(subject, accept);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
private:
|
|
464
|
+
template <class Accept>
|
|
465
|
+
auto claim_or_immediate(this auto& self, auto subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
466
|
+
if (auto claimed = self.delegate_.get()(subject, self, accept)) {
|
|
467
|
+
return *std::move(claimed);
|
|
468
|
+
}
|
|
469
|
+
return self.visit_value<Target>::immediate(subject, accept);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
std::reference_wrapper<Delegate> delegate_;
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
template <class Meta, class Delegate>
|
|
476
|
+
using visit_value_delegate_with = visit_cached_immediate<visit_value_delegate<typename Meta::accept_reference_type, Delegate>>;
|
|
477
|
+
|
|
436
478
|
// Forward `value_of<T>` back to acceptor
|
|
437
479
|
template <class Tag>
|
|
438
480
|
struct visit_v8_value_of {
|
|
@@ -512,6 +554,21 @@ struct visit<Meta, v8::Local<Type>> : iv8::visit_value_with<Meta> {
|
|
|
512
554
|
using iv8::visit_value_with<Meta>::visit_value_with;
|
|
513
555
|
};
|
|
514
556
|
|
|
557
|
+
// `transferee_visit_subject` subject visitor
|
|
558
|
+
template <class Meta, class Type, class Delegate>
|
|
559
|
+
struct visit<Meta, transferee_visit_subject<v8::Local<Type>, Delegate>> : iv8::visit_value_delegate_with<Meta, Delegate> {
|
|
560
|
+
private:
|
|
561
|
+
using visit_type = iv8::visit_value_delegate_with<Meta, Delegate>;
|
|
562
|
+
|
|
563
|
+
public:
|
|
564
|
+
using visit_type::visit_type;
|
|
565
|
+
|
|
566
|
+
template <class Accept>
|
|
567
|
+
auto operator()(const transferee_visit_subject<v8::Local<Type>, Delegate>& subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
568
|
+
return util::invoke_as<visit_type>(*this, *subject, accept);
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
|
|
515
572
|
// value_of<T> visitor
|
|
516
573
|
template <class Meta, class Tag>
|
|
517
574
|
struct visit<Meta, iv8::value_of<Tag>> : iv8::visit_v8_value_of<Tag> {
|
package/value/array.cc
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
module v8_js;
|
|
2
|
+
import auto_js;
|
|
2
3
|
import util;
|
|
3
4
|
import v8;
|
|
4
5
|
|
|
5
6
|
namespace js::iv8 {
|
|
6
7
|
|
|
7
8
|
auto value_for_array::begin() const -> iterator {
|
|
8
|
-
return iterator{util::slice(*this), context(), 0};
|
|
9
|
+
return iterator{util::slice(*this), context(), 0, maybe_sparse_};
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
auto value_for_array::end() const -> iterator {
|
|
12
|
-
return iterator{util::slice(*this), context(), size()};
|
|
13
|
+
return iterator{util::slice(*this), context(), size(), maybe_sparse_};
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
auto value_for_array::size() const -> std::uint32_t {
|
|
@@ -20,12 +21,16 @@ auto value_for_array::size() const -> std::uint32_t {
|
|
|
20
21
|
return length_ - 1;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
value_for_array::iterator::iterator(v8::Local<v8::Array> array, v8::Local<v8::Context> context, std::uint32_t index) :
|
|
24
|
+
value_for_array::iterator::iterator(v8::Local<v8::Array> array, v8::Local<v8::Context> context, std::uint32_t index, bool maybe_sparse) :
|
|
24
25
|
array_{array},
|
|
25
26
|
context_{context},
|
|
26
|
-
index_{index}
|
|
27
|
+
index_{index},
|
|
28
|
+
maybe_sparse_{maybe_sparse} {}
|
|
27
29
|
|
|
28
30
|
auto value_for_array::iterator::operator*() const -> value_type {
|
|
31
|
+
if (maybe_sparse_ && !iv8::unmaybe(array_->Has(context_, index_))) {
|
|
32
|
+
throw js::type_error{u"Sparse arrays are not supported"};
|
|
33
|
+
}
|
|
29
34
|
return iv8::unmaybe(array_->Get(context_, index_));
|
|
30
35
|
}
|
|
31
36
|
|
package/value/array.h.cc
CHANGED
|
@@ -12,14 +12,21 @@ class value_for_array : public handle_with_context<v8::Array> {
|
|
|
12
12
|
class iterator;
|
|
13
13
|
struct handle_data;
|
|
14
14
|
using value_type = v8::Local<v8::Value>;
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
value_for_array() = default;
|
|
17
|
+
explicit value_for_array(v8::Local<v8::Context> context, v8::Local<v8::Array> array, bool maybe_sparse = true) :
|
|
18
|
+
handle_with_context{context, array},
|
|
19
|
+
maybe_sparse_{maybe_sparse} {}
|
|
20
|
+
explicit value_for_array(context_lock_witness lock, v8::Local<v8::Array> array, bool maybe_sparse = true) :
|
|
21
|
+
value_for_array{lock.context(), array, maybe_sparse} {}
|
|
16
22
|
|
|
17
23
|
[[nodiscard]] auto begin() const -> iterator;
|
|
18
24
|
[[nodiscard]] auto end() const -> iterator;
|
|
19
25
|
[[nodiscard]] auto size() const -> std::uint32_t;
|
|
20
26
|
|
|
21
27
|
private:
|
|
22
|
-
|
|
28
|
+
bool maybe_sparse_ : 1 = true;
|
|
29
|
+
mutable std::uint32_t length_ : 31 {};
|
|
23
30
|
};
|
|
24
31
|
|
|
25
32
|
class value_for_array::iterator : public util::random_access_iterator_facade<std::int32_t, std::int64_t> {
|
|
@@ -30,7 +37,7 @@ class value_for_array::iterator : public util::random_access_iterator_facade<std
|
|
|
30
37
|
using value_type = value_for_array::value_type;
|
|
31
38
|
|
|
32
39
|
iterator() = default;
|
|
33
|
-
iterator(v8::Local<v8::Array> array, v8::Local<v8::Context> context, std::uint32_t index);
|
|
40
|
+
iterator(v8::Local<v8::Array> array, v8::Local<v8::Context> context, std::uint32_t index, bool maybe_sparse);
|
|
34
41
|
|
|
35
42
|
auto operator*() const -> value_type;
|
|
36
43
|
auto operator+=(difference_type offset) -> iterator&;
|
|
@@ -43,6 +50,7 @@ class value_for_array::iterator : public util::random_access_iterator_facade<std
|
|
|
43
50
|
v8::Local<v8::Array> array_;
|
|
44
51
|
v8::Local<v8::Context> context_;
|
|
45
52
|
std::uint32_t index_{};
|
|
53
|
+
bool maybe_sparse_{};
|
|
46
54
|
};
|
|
47
55
|
|
|
48
56
|
static_assert(std::ranges::range<value_for_array>);
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
module;
|
|
2
|
+
#include <v8_js/version.h>
|
|
3
|
+
export module v8_js:external;
|
|
4
|
+
import :collected_handle;
|
|
5
|
+
import :lock;
|
|
6
|
+
import std;
|
|
7
|
+
import v8;
|
|
8
|
+
|
|
9
|
+
namespace js::iv8 {
|
|
10
|
+
|
|
11
|
+
// Untagged `v8::External` for pointer types.
|
|
12
|
+
export template <class Type>
|
|
13
|
+
class untagged_external : public v8::External {
|
|
14
|
+
public:
|
|
15
|
+
[[nodiscard]] auto value() const -> Type*;
|
|
16
|
+
|
|
17
|
+
static auto make(isolate_lock_witness lock, Type* value) -> v8::Local<untagged_external>;
|
|
18
|
+
static auto make_collected(const collected_handle_lock& lock, auto&&... args) -> v8::Local<untagged_external>
|
|
19
|
+
requires std::constructible_from<Type, decltype(args)...>;
|
|
20
|
+
|
|
21
|
+
private:
|
|
22
|
+
constexpr static auto type_tag = v8::ExternalPointerTypeTag{1};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Tagged `v8::External` which holds a v8-managed host object.
|
|
26
|
+
export template <class Type>
|
|
27
|
+
class tagged_external : public v8::External {
|
|
28
|
+
private:
|
|
29
|
+
class holder;
|
|
30
|
+
|
|
31
|
+
public:
|
|
32
|
+
[[nodiscard]] auto get() const -> Type&;
|
|
33
|
+
[[nodiscard]] auto try_cast() const -> Type*;
|
|
34
|
+
static auto make_collected(const collected_handle_lock& lock, auto&&... args) -> v8::Local<tagged_external>
|
|
35
|
+
requires std::constructible_from<Type, decltype(args)...>;
|
|
36
|
+
|
|
37
|
+
private:
|
|
38
|
+
constexpr static auto type_tag = v8::ExternalPointerTypeTag{2};
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Inherited by `tagged_external<Type>::holder` instances
|
|
42
|
+
struct external_type_tag {
|
|
43
|
+
std::reference_wrapper<const std::type_info> type_tag;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Host object with type tag
|
|
47
|
+
template <class Type>
|
|
48
|
+
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
|
49
|
+
class tagged_external<Type>::holder
|
|
50
|
+
: public external_type_tag,
|
|
51
|
+
public Type {
|
|
52
|
+
public:
|
|
53
|
+
explicit holder(auto&&... args)
|
|
54
|
+
requires std::constructible_from<Type, decltype(args)...> :
|
|
55
|
+
external_type_tag{typeid(Type)},
|
|
56
|
+
Type{std::forward<decltype(args)>(args)...} {}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// ---
|
|
60
|
+
|
|
61
|
+
// untagged_external
|
|
62
|
+
template <class Type>
|
|
63
|
+
auto untagged_external<Type>::value() const -> Type* {
|
|
64
|
+
#if V8_HAS_TAGGED_EXTERNAL
|
|
65
|
+
return static_cast<Type*>(Value(type_tag));
|
|
66
|
+
#else
|
|
67
|
+
return static_cast<Type*>(Value());
|
|
68
|
+
#endif
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
template <class Type>
|
|
72
|
+
auto untagged_external<Type>::make(isolate_lock_witness lock, Type* value) -> v8::Local<untagged_external> {
|
|
73
|
+
#if V8_HAS_TAGGED_EXTERNAL
|
|
74
|
+
return v8::External::New(lock.isolate(), value, type_tag).template As<untagged_external>();
|
|
75
|
+
#else
|
|
76
|
+
return v8::External::New(lock.isolate(), value).template As<untagged_external>();
|
|
77
|
+
#endif
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
template <class Type>
|
|
81
|
+
auto untagged_external<Type>::make_collected(const collected_handle_lock& lock, auto&&... args) -> v8::Local<untagged_external>
|
|
82
|
+
requires std::constructible_from<Type, decltype(args)...> {
|
|
83
|
+
using collected_handle_type = collected_handle<untagged_external, Type>;
|
|
84
|
+
auto collected_handle = collected_handle_type::make(lock, std::forward<decltype(args)>(args)...);
|
|
85
|
+
auto value =
|
|
86
|
+
#if V8_HAS_TAGGED_EXTERNAL
|
|
87
|
+
v8::External::New(lock.isolate(), collected_handle->get(), type_tag).template As<untagged_external>();
|
|
88
|
+
#else
|
|
89
|
+
v8::External::New(lock.isolate(), collected_handle->get()).template As<untagged_external>();
|
|
90
|
+
#endif
|
|
91
|
+
collected_handle_type::reset(lock, std::move(collected_handle), value);
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// tagged_external
|
|
96
|
+
template <class Type>
|
|
97
|
+
auto tagged_external<Type>::get() const -> Type& {
|
|
98
|
+
auto* pointer = try_cast();
|
|
99
|
+
if (pointer == nullptr) {
|
|
100
|
+
throw std::logic_error{"external tag mismatch"};
|
|
101
|
+
} else {
|
|
102
|
+
return *pointer;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
template <class Type>
|
|
107
|
+
auto tagged_external<Type>::try_cast() const -> Type* {
|
|
108
|
+
auto& object =
|
|
109
|
+
#if V8_HAS_TAGGED_EXTERNAL
|
|
110
|
+
*static_cast<holder*>(Value(type_tag));
|
|
111
|
+
#else
|
|
112
|
+
*static_cast<holder*>(Value());
|
|
113
|
+
#endif
|
|
114
|
+
if (object.type_tag.get() == typeid(Type)) {
|
|
115
|
+
return &object;
|
|
116
|
+
} else {
|
|
117
|
+
return nullptr;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
template <class Type>
|
|
122
|
+
auto tagged_external<Type>::make_collected(const collected_handle_lock& lock, auto&&... args) -> v8::Local<tagged_external>
|
|
123
|
+
requires std::constructible_from<Type, decltype(args)...> {
|
|
124
|
+
using collected_handle_type = collected_handle<tagged_external, holder>;
|
|
125
|
+
auto collected_handle = collected_handle_type::make(lock, std::forward<decltype(args)>(args)...);
|
|
126
|
+
auto value =
|
|
127
|
+
#if V8_HAS_TAGGED_EXTERNAL
|
|
128
|
+
v8::External::New(lock.isolate(), collected_handle->get(), type_tag).template As<tagged_external>();
|
|
129
|
+
#else
|
|
130
|
+
v8::External::New(lock.isolate(), collected_handle->get()).template As<tagged_external>();
|
|
131
|
+
#endif
|
|
132
|
+
collected_handle_type::reset(lock, std::move(collected_handle), value);
|
|
133
|
+
return value;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
} // namespace js::iv8
|
package/value/object.cc
CHANGED
|
@@ -52,7 +52,7 @@ auto value_for_object::keys() const -> const value_for_array& {
|
|
|
52
52
|
v8::IndexFilter::kIncludeIndices,
|
|
53
53
|
v8::KeyConversionMode::kKeepNumbers
|
|
54
54
|
);
|
|
55
|
-
keys_ = value_for_array{context(), unmaybe(property_names)};
|
|
55
|
+
keys_ = value_for_array{context(), unmaybe(property_names), false};
|
|
56
56
|
}
|
|
57
57
|
return keys_;
|
|
58
58
|
}
|
package/value/primitive.cc
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
module;
|
|
2
|
-
#include <v8_js/version.h>
|
|
3
1
|
module v8_js;
|
|
4
2
|
import :primitive;
|
|
5
3
|
import std;
|
|
@@ -68,14 +66,4 @@ value_for_date::operator js_clock::time_point() const {
|
|
|
68
66
|
return js_clock::time_point{js_clock::duration{(*this)->ValueOf()}};
|
|
69
67
|
}
|
|
70
68
|
|
|
71
|
-
// `value_for_external`
|
|
72
|
-
value_for_external::operator void*() const {
|
|
73
|
-
#if V8_HAS_TAGGED_EXTERNAL
|
|
74
|
-
// TODO: Use this feature
|
|
75
|
-
return (*this)->Value(0);
|
|
76
|
-
#else
|
|
77
|
-
return (*this)->Value();
|
|
78
|
-
#endif
|
|
79
|
-
}
|
|
80
|
-
|
|
81
69
|
} // namespace js::iv8
|
package/value/primitive.h.cc
CHANGED
|
@@ -62,6 +62,7 @@ class value_for_external : public v8::Local<v8::External> {
|
|
|
62
62
|
explicit value_for_external(null_lock_witness /*witness*/, v8::Local<v8::External> handle) :
|
|
63
63
|
v8::Local<v8::External>{handle} {}
|
|
64
64
|
|
|
65
|
+
// nb: Unused at this time.
|
|
65
66
|
[[nodiscard]] explicit operator void*() const;
|
|
66
67
|
};
|
|
67
68
|
|